Skip to content

Commit

Permalink
fix: Turbo Frame processing should use decoration (#796)
Browse files Browse the repository at this point in the history
As with TurboStream done here #778

Fixes #792
  • Loading branch information
timyates authored Apr 30, 2024
1 parent f7ad444 commit fa533ae
Show file tree
Hide file tree
Showing 3 changed files with 84 additions and 17 deletions.
18 changes: 18 additions & 0 deletions test-suite/src/test/java/io/micronaut/views/ModelAndViewTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
import io.micronaut.http.HttpRequest;
import io.micronaut.http.HttpResponse;
import io.micronaut.http.HttpStatus;
import io.micronaut.http.MediaType;
import io.micronaut.http.annotation.Controller;
import io.micronaut.http.annotation.Get;
import io.micronaut.http.annotation.Produces;
Expand All @@ -21,6 +22,7 @@
import io.micronaut.views.model.ConfigViewModelProcessor;
import io.micronaut.views.model.FruitsController;
import io.micronaut.views.model.ViewModelProcessor;
import io.micronaut.views.turbo.TurboFrame;
import io.micronaut.views.turbo.TurboStream;
import io.micronaut.views.turbo.TurboStreamAction;
import io.micronaut.views.turbo.http.TurboMediaType;
Expand All @@ -41,6 +43,7 @@
@Property(name = "micronaut.security.enabled", value = StringUtils.FALSE)
@MicronautTest
class ModelAndViewTest {

@Inject
@Client("/")
HttpClient httpClient;
Expand Down Expand Up @@ -175,6 +178,13 @@ void viewModelProcessorsWorkWithControllersReturningPOJOs() {
request = HttpRequest.GET("/turboStreamBuilderWithProcessor").accept(TurboMediaType.TURBO_STREAM);
html = client.retrieve(request, String.class);
assertTrue(html.contains("<h1>config: test</h1>"));

//when:
request = HttpRequest.GET("/turboFrameBuilderWithProcessor").accept(MediaType.TEXT_HTML);
html = client.retrieve(request, String.class);
assertTrue(html.startsWith("<turbo-frame>"));
assertTrue(html.endsWith("</turbo-frame>"));
assertTrue(html.contains("<h1>config: test</h1>"));
}

@Requires(property = "spec.name", value = "ModelAndViewSpec")
Expand All @@ -193,6 +203,14 @@ public TurboStream.Builder turboStreamBuilder() {
.action(TurboStreamAction.REPLACE)
.template("fruits-processor", new Fruit("orange", "orange"));
}

@Produces(MediaType.TEXT_HTML)
@Get("/turboFrameBuilderWithProcessor")
public TurboFrame.Builder turboFrameBuilder() {
return (TurboFrame.Builder) TurboFrame.builder()
.templateView("fruits-processor")
.templateModel(new Fruit("orange", "orange"));
}
}

@Introspected
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* Copyright 2017-2022 original authors
* Copyright 2017-2024 original authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
Expand All @@ -15,37 +15,61 @@
*/
package io.micronaut.views.turbo;

import io.micronaut.core.annotation.NextMajorVersion;
import io.micronaut.core.annotation.NonNull;
import io.micronaut.core.annotation.Nullable;
import io.micronaut.core.io.Writable;
import io.micronaut.http.HttpRequest;
import io.micronaut.views.ModelAndView;
import io.micronaut.views.TemplatedBuilder;
import io.micronaut.views.ViewsModelDecorator;
import io.micronaut.views.ViewsRendererLocator;
import io.micronaut.views.turbo.http.TurboMediaType;
import jakarta.inject.Inject;

import java.util.Optional;

/**
* @param <T> The class to be built
* @author Sergio del Amo
* @since 3.4.0
* @param <T> The class to be built
*/
public abstract class AbstractTurboRenderer<T extends TemplatedBuilder<?>> {

private final ViewsRendererLocator viewsRendererLocator;
private final String mediaType;

@Nullable
@NextMajorVersion("remove the nullability annotation")
private final ViewsModelDecorator viewsModelDecorator;

/**
*
* @param viewsRendererLocator Views renderer Locator
* @param mediaType Media Type
* @param viewsModelDecorator Views Model Decorator
* @param mediaType Media Type
*/
protected AbstractTurboRenderer(ViewsRendererLocator viewsRendererLocator,
String mediaType) {
@Inject
protected AbstractTurboRenderer(
ViewsRendererLocator viewsRendererLocator,
ViewsModelDecorator viewsModelDecorator,
String mediaType
) {
this.viewsRendererLocator = viewsRendererLocator;
this.viewsModelDecorator = viewsModelDecorator;
this.mediaType = mediaType;
}

/**
*
* @param viewsRendererLocator Views renderer Locator
* @param mediaType Media Type
*/
@Deprecated(since = "5.2.1", forRemoval = true)
protected AbstractTurboRenderer(ViewsRendererLocator viewsRendererLocator,
String mediaType) {
this(viewsRendererLocator, null, mediaType);
}

/**
* @param builder Builder
* @param request The Request
* @return An Optional Writable with the builder rendered
Expand All @@ -54,13 +78,17 @@ protected AbstractTurboRenderer(ViewsRendererLocator viewsRendererLocator,
public Optional<Writable> render(@NonNull T builder,
@Nullable HttpRequest<?> request) {
return builder.getTemplateView()
.map(viewName -> {
Object model = builder.getTemplateModel().orElse(null);
return viewsRendererLocator.resolveViewsRenderer(viewName, mediaType, model)
.flatMap(renderer -> builder.template(renderer.render(viewName, model, request))
.build()
.render());
})
.orElseGet(() -> builder.build().render());
.map(viewName -> {
Object model = builder.getTemplateModel().orElse(null);
ModelAndView<Object> modelAndView = new ModelAndView<>(viewName, model);
if (request != null && viewsModelDecorator != null) {
viewsModelDecorator.decorate(request, modelAndView);
}
Object decoratedModel = modelAndView.getModel().orElse(null);
return viewsRendererLocator.resolveViewsRenderer(viewName, TurboMediaType.TURBO_STREAM, decoratedModel)
.flatMap(renderer -> builder.template(renderer.render(viewName, decoratedModel, request)).build()
.render());
})
.orElseGet(() -> builder.build().render());
}
}
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* Copyright 2017-2022 original authors
* Copyright 2017-2024 original authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
Expand All @@ -15,7 +15,11 @@
*/
package io.micronaut.views.turbo;

import io.micronaut.context.annotation.Requires;
import io.micronaut.http.HttpRequest;
import io.micronaut.views.ViewsModelDecorator;
import io.micronaut.views.ViewsRendererLocator;
import jakarta.inject.Inject;
import jakarta.inject.Singleton;

/**
Expand All @@ -24,12 +28,29 @@
* @since 3.4.0
*/
@Singleton
@Requires(classes = HttpRequest.class)
public class DefaultTurboFrameRenderer extends AbstractTurboRenderer<TurboFrame.Builder> implements TurboFrameRenderer {

/**
* Constructor.
* @param viewsRendererLocator Views Renderer Locator.
* @param viewsModelDecorator Views Model Decorator
*/
@Inject
public DefaultTurboFrameRenderer(
ViewsRendererLocator viewsRendererLocator,
ViewsModelDecorator viewsModelDecorator
) {
super(viewsRendererLocator, viewsModelDecorator, "text/html");
}

/**
*
* @param viewsRendererLocator View Renderer Locator
* @deprecated Use {@link #DefaultTurboFrameRenderer(ViewsRendererLocator, ViewsModelDecorator)} instead.
*/
@Deprecated(since = "5.2.1", forRemoval = true)
public DefaultTurboFrameRenderer(ViewsRendererLocator viewsRendererLocator) {
super(viewsRendererLocator, "text/html");
this(viewsRendererLocator, null);
}
}

0 comments on commit fa533ae

Please sign in to comment.