Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Feature: Reactive Soy (v2) #34

Open
wants to merge 5 commits into
base: 6.0.x
Choose a base branch
from

Conversation

sgammon
Copy link
Contributor

@sgammon sgammon commented Feb 26, 2020

This changeset amends the Soy view renderer, to support a new interface called ReactiveViewRenderer. The new interface generates a Flowable instead of a Writable, so that reactive chunked responses can be generated when the renderer is waiting on Future values to resolve.

Caveat: see discussion on #26 (v1 of Reactive Soy), in which @graemerocher and I discuss the ability to dynamically switch to chunked responses, which hasn't landed yet. Until that time, this PR will block forever/fail on unresolved futures in the render context. Workarounds for this might include:

  1. always sending a chunked response, for now (i.e. opting out of dynamic switching, at the cost of some client efficiency)
  2. always sending a synchronous response, for now, i.e. resolving futures before rendering (at the cost of some server efficiency)
  3. working upstream to enable dynamic decision making for chunked responses (this is more invasive, of course, but also higher impact, and probably exceeds the scope of my abilities with Micronaut)

@sgammon sgammon force-pushed the soy/async-chunked-v2 branch 2 times, most recently from 887c4aa to 74f4178 Compare February 26, 2020 19:54
Copy link
Contributor Author

@sgammon sgammon left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

user facing changes to the Soy engine are described below. there should be zero impact to non-Soy users of the Views module.

@@ -10,7 +10,7 @@ micronautVersion=1.2.5
micronautTestVersion=1.1.0
protocVersion=3.5.1
groovyVersion=2.5.6
soyVersion=2019-09-03
soyVersion=2019-10-08
Copy link
Contributor Author

@sgammon sgammon Feb 26, 2020

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

upgrades soy to latest version

* @author Sam Gammon ([email protected])
* @since 1.3.2
*/
public interface BaseViewsRenderer {
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

introduces new BaseViewsRenderer, under which there is ViewRenderer and ReactiveViewRenderer

@Nonnull String viewName,
@Nullable Object data,
@Nonnull HttpRequest<?> request,
@Nonnull MutableHttpResponse<?> response);
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

new interface for rendering views via Flowable (i.e. ReactiveViewRenderer)

if (viewsRenderer.exists(view)) {
response.contentType(type);
try {
if (viewsRenderer instanceof ReactiveViewRenderer) {
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

in ViewsFilter, if a view renderer is an instance of ReactiveViewRenderer, rendering is fully delegated to it rather than synchronously filling a Writable.

@@ -8,6 +8,7 @@ dependencies {
compile "io.micronaut:micronaut-runtime:$micronautVersion"
compile "io.micronaut:micronaut-http:$micronautVersion"
compile "io.micronaut:micronaut-http-server:$micronautVersion"
compile "io.micronaut:micronaut-buffer-netty:$micronautVersion"
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

added direct dependency from views-soy > micronaut-buffer-netty, rather than relying on transitive dependency presence in views-core

*/
@Immutable
@SuppressWarnings("WeakerAccess")
public final class SoyContext implements SoyContextMediator {
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

new SoyContext class and SoyContextMediator interface, which allows the controller to override render context (parameters, injected values, renaming map), by enabling a return value that complies with SoyContextMediator. this is an opt-in feature, the controller may still return a Map or SoyValue directly if it so wishes. any other value is still skipped and handed down the line for further processing.

for example, before, it was impossible to call setIj on the render context, to supply your own custom @inject variables. now, you can do this:

package some.package;

@Controller
class MyController {
    @View("some.soy.path.here")
    fun index(): SoyContextMediator {
      return SoyContext.fromMap(
        mapOf("hi", 5, "regular", "context"),   // < regular context
        mapOf("injected", true),           // < injected context (i.e. via `@inject`)
        Optional.empty())           // < overridden naming map
    }
}

previously, the naming map override and IJ parameters would not have been available, because returning a simple SoyValue or Map only specifies regular context.

* @see SoyContext Default implementation of this interface
* @since 1.3.2
*/
public interface SoyContextMediator {
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

new SoyContextMediator, which allows users to specify their own custom logic for supplying render context, injected render context, and/or naming maps

// If it's done, do nothing.
case DONE: break;
// prime the initial render
return Flowable.<ByteBuffer, SoyRender>generate(SoyRender::create, (buffer, emitter) -> {
Copy link
Contributor Author

@sgammon sgammon Feb 26, 2020

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

core of the new render flow happens here, where we start with Flowable.<>generate and proceed until we either switch out of execution, or finish rendering.

if we switch out of execution, the engine hands us a future, which we wait on reactively. once the future completes, we continue rendering until finished, or until we are limited again by a future, causing the process to repeat. if at any time during rendering the buffer hits the "soft limit" (80% of the buffer maximum, by default), backpressure is triggered and a response chunk emitted.

in the simplest case, a render context has no future values, and proceeds regularly in a de-facto synchronous manner, populating the response in one go and sending it back. in this case, there is no chunked response, it's just a regular HTTP cycle.

in the most complex case, a render context has multiple future values that have not yet resolved, while hitting the soft limit of the buffer in between. it renders until it must wait, then switches out. once the future completes, it switches back in, continues rendering, and let's say it hits the buffer soft limit. so, the buffer indicates back pressure, causing a chunked response (and the initial chunk), then continues processing, encounters the 2nd future, switches out. when the second future completes, it switches back in, completes the render, emits the final chunk, and finishes.

see caveats in PR description for chunked response switching.

* @author Sam Gammon ([email protected])
* @since 1.3.2
*/
public class SoyViewException extends ViewRenderingException {
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

SoyViewException now extends ViewRenderingException, for consistency

@@ -95,7 +110,7 @@ class SoySauceViewRendererSpec extends Specification {
rsp.body().contains("<h1>username: <span>sgammon</span></h1>")
}

def "invoking /soy/missing should produce a 404 exception describing a missing view template"() {
def "invoking /soy/missing should produce a 500 exception describing a missing view template"() {
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

soy now accurately returns ISE 500 for a missing explicitly-named template, rather than 404. this makes it consistent with the Freemarker modules and common sense (being that i am the author of the soy view engine, i am being salty at myself for this)

@sgammon
Copy link
Contributor Author

sgammon commented Feb 26, 2020

@graemerocher / @jameskleeh your feedback on the above would be very welcome. particularly, the ideas in the description about workarounds for the chunking issue.

i'm hoping to get this PR in even if the chunking issue isn't quite ready yet, if you are open to it (we are increasingly using micronaut in prod and this would help on a number of fronts)

all of the above respectfully, of course, and thank you again for your time

also, this PR supersedes the last one, it's based off the latest master and cleans up my previous approach in a few ways. the SoyContext feature was not in yet, that one is new to this PR.

@sgammon sgammon mentioned this pull request Feb 26, 2020
4 tasks
sgammon added a commit to sgammon/elide-archive that referenced this pull request Feb 27, 2020
- Gather proto and other page context into `SoyContextMediator`
- Override Micronaut Views/Views Soy deps
  (see micronaut-projects/micronaut-views#34)
- Update Soy lib and jssrc deps
- Fix CSP error specified by default in `application.yml`
- Serve HTML from controller
@sgammon sgammon marked this pull request as ready for review March 1, 2020 03:43
@graemerocher
Copy link
Contributor

@sgammon Sorry for the late reply. I debugged this and it is essentially writing the view as a single chunk to the response which is not ideal. I'm not sure that is desirable?

I think we should prioritize fixing this problem as it won't work as designed. Could you raise an issue in Micronaut core to fix the handling of stream response detection?

@sgammon
Copy link
Contributor Author

sgammon commented Mar 9, 2020

@graemerocher sure thing, i'll raise a bug on core (thank you for your response!)

update: filed as micronaut-projects/micronaut-core#2902

sgammon pushed a commit to sgammon/elide-archive that referenced this pull request Mar 10, 2020
* Sample App: Todolist

This changeset adds a new sample app, which ties together all the
features thus far and adds some new ones to facilitate easy app
development. I'm using this sample to help define the underlying
framework structure, when used with Micronaut/Soy.

The sample is built with unified modelling via Protobuf, and RPC
dispatch via gRPC. The UI is built with Soy, with SSR support via
Soy/Java, and re-hydrated CSR support via Soy/JS (and `idom`).

The app's data backend is powered by Firestore, with objects
mediated for serialization by the framework. This process is also
driven by the protos. Auth is also powered by Firebase, which
enables easy data permissions.

Via the central API definition, the app can automatically generate
an API console, docs, and Open API/Swagger configs. It is also
dispatchable from nearly any platform (via either gRPC or REST).
Endpoints served in this manner support either Protobuf on the wire,
or JSON (when using REST).

Styles are defined in SASS but processed by both GSS and PostCSS,
which enables both (1) tight optimization on a per-browser basis,
and (2) style modularity when serving. The server can also rewrite
styles using a Soy/GSS rewrite map.

The sample additionally demonstrates testing, with coverage (where
supported by the framework, so far).

* Add simple tests for Soy renaming maps

* Cleanup cloudbuild targets

* Switch tests off in GCB

* Cleanup and testing for existing core.backend logic

* Fix definitions for JS interop

* Early `PageContext` logic and example

- Gather proto and other page context into `SoyContextMediator`
- Override Micronaut Views/Views Soy deps
  (see micronaut-projects/micronaut-views#34)
- Update Soy lib and jssrc deps
- Fix CSP error specified by default in `application.yml`
- Serve HTML from controller

* Refactor Java dependencies and rules

- Add better exclusions for Google coordinates
- Add source dependencies for Protobuf and gRPC
- Upgrade/conform dependency tree to Protobuf/gRPC latest

* Seal Java dependencies

* Further gRPC support in Todolist

- Add support for reflection service
- Make service / interceptor simple passthroughs

* New todolist port arrangement

- Run app on :8081
- Run API on :8082

* Add Todolist sample to cloudbuild config

* Eliminate remote build in GCB

* Adjust build stamp to fallback to COMMIT_SHA variable

* Apply fix for image tag in GCB

* Add K8S rules dependency

* Further adjustments to GCB config

* Fix misplaced deploy jar target

* Pull and tag latest todolist image on build

* Add initial ambassador config

* Remove failing build artifact upload in GCB

* Add initial test for PageContext

* Improvements to page context integration

- Add extended `SoyProtoContextMediator` interface
- Add efficient empty context use
- Cover `PageContext` with tests

* Adjust coverage reporting

* Refactor `HomeController` to use new `AppController`

- Add `PageContextManager` and factory
- Init `AppController` and `BaseController`
- Adjust Bazel flags to be normalized

* Cleanup and simplify PageContext/PageContextManager

- Remove embedded PageContextManager.Factory
- Clean up DI flow
- Fix issue injecting HTTP request

* Initial Todolist infrastructure configs

- Add full suite of K8S configs, scoped to `todolist` namespace
- Add customized Envoy configuration and image

* Toolchain support for Docker, Kubernetes and packaging

- Add macros for generic Docker images
- Add macros for Kubernetes configs
- Add macros for packaging

* Pull in Envoy base image, setup K9 defaults

* Add rules_pkg dependency

* Add yarn run alias for gRPC CLI

* Converge bazelrc and WORKSPACE for Todolist

* Major work on Envoy proxy

* Use manually-built Envoy image, for now

* Fix spacing issue in bazelrc

* Add external LB for testing

* Use fully-qualified service DNS

* Setup devops flow for Envoy and Todolist (including deploy)

- Add deploy routine to GCB (CI will likely fail)
- Enable reflection service (+add logging)
- Tweaks to Envoy config, including healthcheck logging

* Fixes for ingress configuration

* Transition to independent NEGs

* [skip ci] Change to Bazel 2.1.0 in GCB

* Prep version bump -> 1.0.0-alpha2

* Fix incorrect Bazel version in GCB

* Fix prefix for Bazel images in GCB (late steps)

* Pin and build K8S toolchain from source

* Attempt fix for K8S transitive load

* Upgrade K8S > 1.15.9

* Update K8S hash, add dep on infrastructure tools

* Switchup alias for K8S repo infra

* Apply incompatible trip flags to fix K8S in GCB

* Alias K8S repo

* Clean up K8S tools reference

* Add custom builder image for deploy, use it

* Switch up GCB image for 2nd-to-last build step

* Upgrade GCB image -> v1b

* Shift build steps to GCB image

* Add Alpine version of builder image

* Prep CI image for use

* Back to Ubuntu builder, install OpenJDK

* Add git to GCB environment

* Auto-remove packages in builder image

* Upgrade to builder v1d

* Add unzip to build image :eyeroll:

* Version bump for build image -> v1e

* Add regular Python package to GCB

* Version bump for GCB -> v1f

* Upgrade GCB image -> v1g (add symlink to gcloud)

* Fix Python rules in CI

* Add todolist internal gateway

* Fix pip issues in CI

* Remove external LB for Todolist

* Always use latest GCB builder

* Cleanup Python deps, add pypandoc to build env

* Support for ibazel-based development flow

- Call ibazel from Makefile when `m dev` is used
- Tweak flags and invocation to work
- Flush out issue with Soy render context

* Update Micronaut/Soy packages with context fix

* Fix access to ADC

* Add ibazel_live_reload to Java app targets

* Support Java-side live reload

* Re-enable test reporting, add Travis env vars

* Stop upgrading deps in CI image

* Add Makefile task to build builder image

* Major README and licensing cleanup/improvements

* Refactor app boot logic

* Cleanup after refactor, fix various issues reported by Codacy

* Cleanup startup exception flow

* Add GraphQL to readme

* Root folder cleanup

* Add new badges to README

* Tweaks to README

* Remove .develop aliases

* Adjust coverage config to be lenient until it settles

* Remove Makefile aliases

* Fix Makefile command list in README

* Small nits

* Fix spaces-in-tags in Soy templates

* Add interface to set page title

* Remove old page root

* Install logback as the underlying log implementation

* Add sugar for page context, title, assets

- Add ability to change page title, inject stylesheets/scripts
- Add ability to easily generate trusted URI protos
- Rename SoyProtoContextMediator > PageRender
- Make PageContextManager usable as a PageRender
- Use new fluent interface in HomeController

* 🚀 Fix Soy render with injected context

- Working injected context. Yay!
- Logback is working too
- Cleaned up page context manager with improved log messages

* Fix render bugs on homepage

* Remove empty table in README

* Feature: Model Layer (#65)

* Feature: Model Adapters

This changeset introduces backend logic to deal with business data
(i.e. *schema-driven models*). As part of this work, I'm also
building out underlying stuff needed for that, including managed
transport, generic model adapters/codecs, and so on.

Related issues:
- App Framework (#27)
- MVP Functionality Matrix (#43)

In this PR:
- [x] **Backend logic: Models**
  - [x] Interfaces for persistence managers, drivers, codecs
  - [x] Serializer and deserializer interfaces
  - [x] Cache-related manager interfaces
  - [x] Container for encoded models
  - [x] Codec for encoding models in proto formats
  - [x] Encoded (generic) model
    - [x] Serialization to `CollapsedModel`/`OutputStream`/`bytes`
    - [x] Deserialization from `InputStream`/`bytes`
  - [x] Interface support for simple CRUD operations
    - [x] `retrieve`: Fetch a structured record from storage.
    - [x] `persist`: Write a structured record to storage.
    - [x] `delete`: Erase a structured record from storage.
  - [x] Cache driver support for basic operations
    - [x] `put`: Write a model to a cache
    - [x] `fetch`: Retrieve a model from the cache
    - [x] `evict`: Evict one or more cached models, by key
    - [x] `flush`: Drop all keys from the cache
- [x] **Backend logic: Transport**
  - [x] Simple `TransportManager` interface
  - [x] Managed gRPC channels for Google APIs
    - [x] `GoogleTransportManager`: channel management
    - [x] Ability to refresh channels?
  - [x] Service support list
    - [x] Firestore
- [x] **Backend logic: Built-in Drivers**
  - [x] In-memory Adapter (reference)
    - [x] Initial `InmemoryManager` implementation
    - [x] Initial `InmemoryDriver` implementation
    - [x] Initial `InmemoryCache` implementation
- [x] Test suite
  - [x] Simple model interactions
  - [x] Update/create methods
  - [x] Delete methods
  - [x] Cache operations
sgammon pushed a commit to sgammon/elide-archive that referenced this pull request Mar 10, 2020
* Feature: Testsuite

This changeset adds a substantive and useful testsuite, with tests
split based on coverage support and backend/frontend disposition.
Coverage support is included in this changeset (at least for tests
in languages that support it, which, for now, is Java).

* Additional coverage reporting tools

- Report Java coverage (properly tagged) to Codecov
- Run coverage reporter in CI
- Small tweaks to VERBOSE and QUIET in Makefile

* Update rules_graal with support for GraalVM 20.0.0

* Apply new GraalVM version

* Initial work/structure on backend Node support

* Yarn dependency upgrades

* Fix frontend tests

* Update J2CL, rules_closure

* Fixes for upcoming dep updates

* Update all deps

* Remove H5BP

* Update Maven deps

* Fix import paths in tests/style and test/soy

* Standardize on io_bazel_rules_sass for rules_sass

* Fix path to karma_config.js

* Fix import path for dom test

* Fix sample app target build

* Remove superfluous wait in CI

* Basic ability to build and run a NodeJS app

* Refactor build of samples

* Replace superfluous sample image build

* Change TS sample URL to root

* Working NodeJS testsuite (using Jasmine)

- Ability to execute backend JS tests
- No support for coverage yet
- Support for test reporting via ReportCI

* Add logic to merge and report test results

- Merge test results via custom Python script
- Report via ReportCI

* Add report generator (HTML) to tool requirements

* Generate HTML test report during test transmit

* Enable ReportCI

* Fix reporting path for CI

* Switch to latest upload script

* Simple JS (frontend) coverage reporter

* Fix platform-agnostic outpath

* Additional outpath fix

* Disable JS coverage report (for now)

* Fix Makefile syntax error

* Add coverage/test reports as build artifacts

* Serve just the index

* Export coverage tarball

* Update protobuf -> 3.11.4

* Upgrade Bazel > 2.1.1

* Add api-common submodule and dependency

* Enable ability to declare internal protos

* Refactor/cleanup datamodel core

* Small nit fixes

* Add BZL overlay for proto_common

* Sample App: Todolist (#46)

* Sample App: Todolist

This changeset adds a new sample app, which ties together all the
features thus far and adds some new ones to facilitate easy app
development. I'm using this sample to help define the underlying
framework structure, when used with Micronaut/Soy.

The sample is built with unified modelling via Protobuf, and RPC
dispatch via gRPC. The UI is built with Soy, with SSR support via
Soy/Java, and re-hydrated CSR support via Soy/JS (and `idom`).

The app's data backend is powered by Firestore, with objects
mediated for serialization by the framework. This process is also
driven by the protos. Auth is also powered by Firebase, which
enables easy data permissions.

Via the central API definition, the app can automatically generate
an API console, docs, and Open API/Swagger configs. It is also
dispatchable from nearly any platform (via either gRPC or REST).
Endpoints served in this manner support either Protobuf on the wire,
or JSON (when using REST).

Styles are defined in SASS but processed by both GSS and PostCSS,
which enables both (1) tight optimization on a per-browser basis,
and (2) style modularity when serving. The server can also rewrite
styles using a Soy/GSS rewrite map.

The sample additionally demonstrates testing, with coverage (where
supported by the framework, so far).

* Add simple tests for Soy renaming maps

* Cleanup cloudbuild targets

* Switch tests off in GCB

* Cleanup and testing for existing core.backend logic

* Fix definitions for JS interop

* Early `PageContext` logic and example

- Gather proto and other page context into `SoyContextMediator`
- Override Micronaut Views/Views Soy deps
  (see micronaut-projects/micronaut-views#34)
- Update Soy lib and jssrc deps
- Fix CSP error specified by default in `application.yml`
- Serve HTML from controller

* Refactor Java dependencies and rules

- Add better exclusions for Google coordinates
- Add source dependencies for Protobuf and gRPC
- Upgrade/conform dependency tree to Protobuf/gRPC latest

* Seal Java dependencies

* Further gRPC support in Todolist

- Add support for reflection service
- Make service / interceptor simple passthroughs

* New todolist port arrangement

- Run app on :8081
- Run API on :8082

* Add Todolist sample to cloudbuild config

* Eliminate remote build in GCB

* Adjust build stamp to fallback to COMMIT_SHA variable

* Apply fix for image tag in GCB

* Add K8S rules dependency

* Further adjustments to GCB config

* Fix misplaced deploy jar target

* Pull and tag latest todolist image on build

* Add initial ambassador config

* Remove failing build artifact upload in GCB

* Add initial test for PageContext

* Improvements to page context integration

- Add extended `SoyProtoContextMediator` interface
- Add efficient empty context use
- Cover `PageContext` with tests

* Adjust coverage reporting

* Refactor `HomeController` to use new `AppController`

- Add `PageContextManager` and factory
- Init `AppController` and `BaseController`
- Adjust Bazel flags to be normalized

* Cleanup and simplify PageContext/PageContextManager

- Remove embedded PageContextManager.Factory
- Clean up DI flow
- Fix issue injecting HTTP request

* Initial Todolist infrastructure configs

- Add full suite of K8S configs, scoped to `todolist` namespace
- Add customized Envoy configuration and image

* Toolchain support for Docker, Kubernetes and packaging

- Add macros for generic Docker images
- Add macros for Kubernetes configs
- Add macros for packaging

* Pull in Envoy base image, setup K9 defaults

* Add rules_pkg dependency

* Add yarn run alias for gRPC CLI

* Converge bazelrc and WORKSPACE for Todolist

* Major work on Envoy proxy

* Use manually-built Envoy image, for now

* Fix spacing issue in bazelrc

* Add external LB for testing

* Use fully-qualified service DNS

* Setup devops flow for Envoy and Todolist (including deploy)

- Add deploy routine to GCB (CI will likely fail)
- Enable reflection service (+add logging)
- Tweaks to Envoy config, including healthcheck logging

* Fixes for ingress configuration

* Transition to independent NEGs

* [skip ci] Change to Bazel 2.1.0 in GCB

* Prep version bump -> 1.0.0-alpha2

* Fix incorrect Bazel version in GCB

* Fix prefix for Bazel images in GCB (late steps)

* Pin and build K8S toolchain from source

* Attempt fix for K8S transitive load

* Upgrade K8S > 1.15.9

* Update K8S hash, add dep on infrastructure tools

* Switchup alias for K8S repo infra

* Apply incompatible trip flags to fix K8S in GCB

* Alias K8S repo

* Clean up K8S tools reference

* Add custom builder image for deploy, use it

* Switch up GCB image for 2nd-to-last build step

* Upgrade GCB image -> v1b

* Shift build steps to GCB image

* Add Alpine version of builder image

* Prep CI image for use

* Back to Ubuntu builder, install OpenJDK

* Add git to GCB environment

* Auto-remove packages in builder image

* Upgrade to builder v1d

* Add unzip to build image :eyeroll:

* Version bump for build image -> v1e

* Add regular Python package to GCB

* Version bump for GCB -> v1f

* Upgrade GCB image -> v1g (add symlink to gcloud)

* Fix Python rules in CI

* Add todolist internal gateway

* Fix pip issues in CI

* Remove external LB for Todolist

* Always use latest GCB builder

* Cleanup Python deps, add pypandoc to build env

* Support for ibazel-based development flow

- Call ibazel from Makefile when `m dev` is used
- Tweak flags and invocation to work
- Flush out issue with Soy render context

* Update Micronaut/Soy packages with context fix

* Fix access to ADC

* Add ibazel_live_reload to Java app targets

* Support Java-side live reload

* Re-enable test reporting, add Travis env vars

* Stop upgrading deps in CI image

* Add Makefile task to build builder image

* Major README and licensing cleanup/improvements

* Refactor app boot logic

* Cleanup after refactor, fix various issues reported by Codacy

* Cleanup startup exception flow

* Add GraphQL to readme

* Root folder cleanup

* Add new badges to README

* Tweaks to README

* Remove .develop aliases

* Adjust coverage config to be lenient until it settles

* Remove Makefile aliases

* Fix Makefile command list in README

* Small nits

* Fix spaces-in-tags in Soy templates

* Add interface to set page title

* Remove old page root

* Install logback as the underlying log implementation

* Add sugar for page context, title, assets

- Add ability to change page title, inject stylesheets/scripts
- Add ability to easily generate trusted URI protos
- Rename SoyProtoContextMediator > PageRender
- Make PageContextManager usable as a PageRender
- Use new fluent interface in HomeController

* 🚀 Fix Soy render with injected context

- Working injected context. Yay!
- Logback is working too
- Cleaned up page context manager with improved log messages

* Fix render bugs on homepage

* Remove empty table in README

* Feature: Model Layer (#65)

* Feature: Model Adapters

This changeset introduces backend logic to deal with business data
(i.e. *schema-driven models*). As part of this work, I'm also
building out underlying stuff needed for that, including managed
transport, generic model adapters/codecs, and so on.

Related issues:
- App Framework (#27)
- MVP Functionality Matrix (#43)

In this PR:
- [x] **Backend logic: Models**
  - [x] Interfaces for persistence managers, drivers, codecs
  - [x] Serializer and deserializer interfaces
  - [x] Cache-related manager interfaces
  - [x] Container for encoded models
  - [x] Codec for encoding models in proto formats
  - [x] Encoded (generic) model
    - [x] Serialization to `CollapsedModel`/`OutputStream`/`bytes`
    - [x] Deserialization from `InputStream`/`bytes`
  - [x] Interface support for simple CRUD operations
    - [x] `retrieve`: Fetch a structured record from storage.
    - [x] `persist`: Write a structured record to storage.
    - [x] `delete`: Erase a structured record from storage.
  - [x] Cache driver support for basic operations
    - [x] `put`: Write a model to a cache
    - [x] `fetch`: Retrieve a model from the cache
    - [x] `evict`: Evict one or more cached models, by key
    - [x] `flush`: Drop all keys from the cache
- [x] **Backend logic: Transport**
  - [x] Simple `TransportManager` interface
  - [x] Managed gRPC channels for Google APIs
    - [x] `GoogleTransportManager`: channel management
    - [x] Ability to refresh channels?
  - [x] Service support list
    - [x] Firestore
- [x] **Backend logic: Built-in Drivers**
  - [x] In-memory Adapter (reference)
    - [x] Initial `InmemoryManager` implementation
    - [x] Initial `InmemoryDriver` implementation
    - [x] Initial `InmemoryCache` implementation
- [x] Test suite
  - [x] Simple model interactions
  - [x] Update/create methods
  - [x] Delete methods
  - [x] Cache operations

* Warning fixes all around
This changeset amends the Soy view renderer, to support a new
interface called `ReactiveViewRenderer`. The new interface
generates a Flowable instead of a Writable, so that reactive
chunked responses can be generated when the renderer is
waiting on Future values to resolve.
@graemerocher
Copy link
Contributor

Depends on #37

@ilopmar
Copy link
Contributor

ilopmar commented Jul 8, 2020

@sgammon can you please rebase master branch and fix the conflicts, please?

@sgammon
Copy link
Contributor Author

sgammon commented Aug 10, 2020

@ilopmar afaik we are waiting on upstream changes here still (specifically, the ability to switch the chunked serving on or off depending on chunk count)

@graemerocher can you advise?

@graemerocher
Copy link
Contributor

@sgammon the upstream changes went into Micronaut 2.0 so the PR needs rebasing on Micronaut 2.0 and it should be good to go

@sgammon
Copy link
Contributor Author

sgammon commented Aug 17, 2020

@graemerocher understood. i'll do that this week or over the weekend, that's great news!

to use the switched chunk response, do i just return the proper response type based on what needs to happen?

@lpellegr
Copy link
Contributor

lpellegr commented Dec 22, 2020

This PR looks very promising.

@sgammon @graemerocher Any updates?

@sgammon
Copy link
Contributor Author

sgammon commented Dec 22, 2020

@lpellegr this needs to be cleaned up and converged with our internal forks. it's working in production and over the past few months has successfully served well over 1m requests. with precompiled templates, it's insanely fast.

if you are interested in adopting, let me know and i can move that up on my plate. we use those forks from @sgammon/elide, which is a meta-framework that wraps Micronaut.

@lpellegr
Copy link
Contributor

@sgammon Thanks for your answer. I would really love to adopt this. Precompiled templates are also something I started to look at but the current micronaut-views documentation looks quite obscure regarding this point.

@sgammon
Copy link
Contributor Author

sgammon commented Dec 28, 2020

@lpellegr i hope it would be okay to start with a code sample in Bazel, that would be much easier. i haven't yet approached build tooling for precompiling soy templates in, say, Gradle or Maven, but it's theoretically the same as any other route (i.e. the Micronaut/Soy layer just expects templates to exist on the classpath - they are compiled Java classes at a computable package path based on the template namespace)

the micronaut/soy layer is in charge of converting a @View-annotated controller method into a Soy render routine. it captures the return type of the method, and provides it as Soy render context. so, in your method, you return a map of data, which is converted into Soy properties and provided to the template mentioned in your @View annotation.

as an example, in Kotlin (this would be an Elide controller, but in this case it's functionally the same as Micronaut):

some_page.soy

{namespace some.soy.path}

{template templateName}
  {@param name: string /}  // Name for the salutation.

  <b>Hello, {$name}!</b>
{/template}

SomeController.kt

@Controller
class SomeController {
    @View("some.soy.path.templateName")
    @Get("/", produces = ["text/html;charset=UTF-8"])
    fun home(request: HttpRequest<*>): Map<String, String> {
        // do something to calculate context
        return mapOf("name" to "World")
    }
}
curl http://localhost:8080/

<b>Hello, World!</b>

I do see some Soy plugins for Gradle and Maven, or, if you are using Bazel (or willing to learn it, it's awesome), you can use the compiler directly via rules_closure.

@lpellegr
Copy link
Contributor

lpellegr commented Jan 2, 2021

@sgammon Thanks for your help (and happy new year!). I was able to generate compiled templates using Gradle and to use them. However, using compiled templates requires to override SoyFileSetProvider#provideCompiledTemplates with something like:

@Override
public SoySauce provideCompiledTemplates() {
    return new SoySauceBuilder().build();
}

Adding classes in the classpath is not enough. Also, as soon as the previous method is overridden, if classes for compiled templates are not found in the classpath, then rendering always fails, and live compilation is not triggered. I don't know if the current behavior is normal but, personally, I found the documentation misleading:

If compiled templates can’t be located by the SoyFileSetProvider, templates are pre-compiled into bytecode at server startup

Another thing I struggled with and required me to use a customized version of micronaut-views is Soy ij data. A dedicated issue has been opened. I would love to have your thought.

@sgammon
Copy link
Contributor Author

sgammon commented Jan 4, 2021

@lpellegr ah drat, it looks like there are some crossed wires here - on our internal fork, we have a fix for the IJ data and I can also dig up a sample for how we load templates. let me get back to you on this, and happy new year back! :)

@lpellegr
Copy link
Contributor

@sgammon Any news?

@sdelamo sdelamo added this to the 2.3.0 milestone Mar 24, 2021
@sdelamo sdelamo added the type: enhancement New feature or request label Mar 24, 2021
@sdelamo sdelamo self-assigned this Mar 24, 2021
@sdelamo sdelamo added the status: next major version The issue will be considered for the next major version label Mar 25, 2021
@sdelamo
Copy link
Contributor

sdelamo commented Mar 25, 2021

This has breaking changes. Thus, it will need to wait until the next mayor version of Micronaut Views module.

@sdelamo sdelamo removed their assignment Mar 25, 2021
@lpellegr
Copy link
Contributor

@sdelamo I just noticed the first milestone of Micronaut 3.0. Any chance to get this PR sorted?

@sgammon
Copy link
Contributor Author

sgammon commented Jun 3, 2021

@lpellegr this was only ever waiting on the ability to dynamically switch between chunked responses and unary (buffered) responses, based on encountering a Future (or descendent) during the render flow.

if there's a way to trigger that now, we can amend this code to do that and merge -- otherwise, we could simply decide to treat it as an enhancement later. IIRC, right now, it will simply block rendering and wait for the future to complete, which is sub-optimal in many circumstances

@sgammon
Copy link
Contributor Author

sgammon commented Jun 3, 2021

we have been using this in production for some time now, as i know you are as well. so i think the code is very stable, it just needs a rebase, quick cleanup, and the mechanism to return chunked bytes instead of a single response.

@sdelamo sdelamo self-assigned this Aug 9, 2021
@CLAassistant
Copy link

CLA assistant check
Thank you for your submission! We really appreciate it. Like many open source projects, we ask that you sign our Contributor License Agreement before we can accept your contribution.
You have signed the CLA already but the status is still pending? Let us recheck it.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
status: awaiting feedback status: next major version The issue will be considered for the next major version type: enhancement New feature or request
Projects
None yet
Development

Successfully merging this pull request may close these issues.

6 participants