This is a very interesting work. Facing a similar challenge (I need to discontinue Tapestry-based UI but I still want to retain Tapestry-based middle tier), I ended up with coupling Tapestry & Nuxt (they share the same idea of what the 'page' is) and implemented a basic abstraction for page classes, basically allowing to leverage Nuxt asyncData/fetch idiom to load the page data using JSON formatted response.
What I have now is basically a mix of pure REST (tapestry-resteasy based) endpoints and Tapestry event handlers, where the former is utilized by the public API and the latter by Nuxt application pages internally. Sincerely, Ilya On Fri, Oct 8, 2021 at 3:04 PM Thiago H. de Paula Figueiredo < thiag...@gmail.com> wrote: > Hello, Tapestry community! > > I've been working on something I really wanted Tapestry to have but haven't > had a chance to implement myself until some time ago: proper REST support. > And not do it by integrating with some other framework like Jersey or CXF > or by being a JAX-RS implementation, but by doing it in a very Tapestry > way: event handler methods, TypeCoercer for type conversions, everything > being customizable by contributions to Tapestry-IoC services, return value > of event handler methods handled by ComponentEventResultProcessor > implementations, etc. > > This is a work in progress, done in the "rest" branch, so I decided to > publish a snapshot (5.8.0-SNAPSHOT) so you can use it yourself and tell me > what you feel about it. All feedback is welcome! > > Major points: > > - New page-level events are triggered after the "activate" event is > triggered (i.e. onActivate()) so you can write REST endpoints as > regular, > live-class-reloaded event handler methods. > - One for each supported HTTP method: GET, POST, DELETE, PUT, HEAD, > PATCH. > - Event name is onHttp[method name]. For example, onHttpPut. > - New constants in EventConstants for each supported HTTP method. > - Activation context for REST event handler methods exactly in the > same way as in onActivate(). > - Just like onActivate(), these new events are not triggered in > components. > - New service in tapestry-http (which is included by tapestry-core), > RestSupport, with utility methods and <T> Optional<T> > getRequestBodyAs(Class<T> type), which returns the request body > converted > to a given type. > - getRequestAsBody(Class) uses HttpRequestBodyConverter. > - Another new service in tapestry-http, HttpRequestBodyConverter, which > uses an ordered configuration of itself, which provides conversions of > request body to given types. > - Only one method, <T> T convert(HttpServletRequest request, Class<T> > type); > - tapestry-http contributes one implementation, which is added as the > last one in the ordered configuration, which uses TypeCoercer. > - Out of the box, it supports conversions to String, primitive types, > JSONArray, JSONObject plus any other type that has a String -> type > conversion available in TypeCoercer, directly or not. > - New annotation for page event handler method (onActivate(), onHttp*) > parameters: @RequestBody, which receives the request body and converts > it > to the parameter type using RestSupport.getRequestAsBody(). > - Think of it as @RequestParameter but for the request body instead of a > query parameter. > - Example below. > - New annotation for page event handler method (onActivate(), onHttp*) > parameters: @StaticActivationContextValue("something"), for defining > that > method will only be called if that page activation context value > matches a > given string. > - Example below. > - Parameters with that annotation still get their values set as > usual. > - This was built mostly for REST support, but it can useful for > non-REST situations too when a give activation context has a number > of > possible values and there's some logic tied to it. > - Automatic generation of Swagger/OpenAPI 3.0 REST API definition files. > - OpenApiDescriptionGenerator service, which is an ordered > configuration of itself. > - An internal implementation generates a base JSON object definition > that can be customized by contributing more OpenApiDescription > implementations. > - Titles, names, summaries, etc can be provided by configuration > symbols or message files (app.properties). > - Messages files are live class reloaded too, so all changes done > to the REST endpoint event handler methods and the > corresponding messages > are live reloaded. > - Formats for message keys and described below. > - Parameter descriptions not implemented yet. > - It will support query parameters and path parameters. > - [Not implemented yet] Embedded Swagger/OpenAPI viewer (i.e. the > right pane of https://editor.swagger.io) hooked directly to the > definition file generated by OpenApiDescriptionGenerator > - It's going to be a separate subproject/JAR to avoid bloating > projects not using the viewer > > > @StaticActivationContextValue example: > > final private String COMPLETED = "completed"; > final private String CLOSED = "closed"; > // Only called if first page activation context value is "completed" > @OnEvent(EventConstants.ACTIVATE) > void completed(@StaticActivationContextValue(COMPLETED) String state, > int id) { > ... > } > > // Only called if first page activation context value is "closed" > @OnEvent(EventConstants.ACTIVATE) > void closed(@StaticActivationContextValue(CLOSED) String state, int id) > { > ... > } > > REST endpoint event handler method example: > > Object onHttpPatch( > @StaticActivationContextValue("subpath") String subpath, > String parameter, > @RequestBody String body) > { > ... > } > > @OnEvent(value = EventConstants.HTTP_DELETE) > Object delete(@StaticActivationContextValue("subpath") String subpath, > String parameter) > { > return createResponse(EventConstants.HTTP_DELETE, null, parameter); > } > > Happy coding! > > -- > Thiago > -- Ilya Obshadko