From dc9024129eb4b698dd5edf3102be8d2df3d07ae2 Mon Sep 17 00:00:00 2001 From: Otavio Santana Date: Mon, 11 Nov 2024 14:31:43 +0000 Subject: [PATCH] chore: update structure to code Signed-off-by: Otavio Santana --- .dockerignore | 1 + .gitignore | 34 ++-- .helidon | 6 + Dockerfile | 39 +++++ Dockerfile.jlink | 35 ++++ Dockerfile.native | 39 +++++ README.md | 160 +++++++++++++++++- app.yaml | 37 ++++ pom.xml | 96 +++++++++++ .../expert/os/demos/movies/GreetResource.java | 115 +++++++++++++ .../os/demos/movies/GreetingProvider.java | 35 ++++ .../java/expert/os/demos/movies/Message.java | 31 ++++ .../os/demos/movies/SimpleGreetResource.java | 69 ++++++++ .../expert/os/demos/movies/package-info.java | 2 + src/main/resources/META-INF/beans.xml | 8 + .../META-INF/microprofile-config.properties | 11 ++ .../os/demos/movies/native-image.properties | 1 + src/main/resources/logging.properties | 20 +++ .../java/expert/os/demos/movies/MainTest.java | 97 +++++++++++ .../META-INF/microprofile-config.properties | 0 src/test/resources/application-test.yaml | 2 + 21 files changed, 825 insertions(+), 13 deletions(-) create mode 100644 .dockerignore create mode 100644 .helidon create mode 100644 Dockerfile create mode 100644 Dockerfile.jlink create mode 100644 Dockerfile.native create mode 100644 app.yaml create mode 100644 pom.xml create mode 100644 src/main/java/expert/os/demos/movies/GreetResource.java create mode 100644 src/main/java/expert/os/demos/movies/GreetingProvider.java create mode 100644 src/main/java/expert/os/demos/movies/Message.java create mode 100644 src/main/java/expert/os/demos/movies/SimpleGreetResource.java create mode 100644 src/main/java/expert/os/demos/movies/package-info.java create mode 100644 src/main/resources/META-INF/beans.xml create mode 100644 src/main/resources/META-INF/microprofile-config.properties create mode 100644 src/main/resources/META-INF/native-image/expert/os/demos/movies/native-image.properties create mode 100644 src/main/resources/logging.properties create mode 100644 src/test/java/expert/os/demos/movies/MainTest.java create mode 100644 src/test/resources/META-INF/microprofile-config.properties create mode 100644 src/test/resources/application-test.yaml diff --git a/.dockerignore b/.dockerignore new file mode 100644 index 0000000..c8b241f --- /dev/null +++ b/.dockerignore @@ -0,0 +1 @@ +target/* \ No newline at end of file diff --git a/.gitignore b/.gitignore index 524f096..626464d 100644 --- a/.gitignore +++ b/.gitignore @@ -1,14 +1,9 @@ # Compiled class file *.class -# Log file -*.log - -# BlueJ files -*.ctxt - -# Mobile Tools for Java (J2ME) -.mtj.tmp/ +# Maven +target/ +.m2/ # Package Files # *.jar @@ -19,6 +14,23 @@ *.tar.gz *.rar -# virtual machine crash logs, see http://www.java.com/en/download/help/error_hotspot.xml -hs_err_pid* -replay_pid* +# IntelliJ Idea +.idea/* +!.idea/runConfigurations +*.iws +*.ipr +*.iml +*.releaseBackup +atlassian-ide-plugin.xml + +# Netbeans +nbactions.xml +nb-configuration.xml + +# Eclipse +.settings +.settings/ +.project +.classpath +.factorypath + diff --git a/.helidon b/.helidon new file mode 100644 index 0000000..b8dbc7e --- /dev/null +++ b/.helidon @@ -0,0 +1,6 @@ +#Helidon Project Configuration +#Mon Nov 11 14:29:25 UTC 2024 +schema.version=1.1.0 +helidon.version=4.1.3 +project.flavor=mp +project.archetype=quickstart diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 0000000..51f5303 --- /dev/null +++ b/Dockerfile @@ -0,0 +1,39 @@ + +# 1st stage, build the app +FROM container-registry.oracle.com/java/jdk-no-fee-term:21 as build + +# Install maven +WORKDIR /usr/share +RUN set -x && \ + curl -O https://archive.apache.org/dist/maven/maven-3/3.8.4/binaries/apache-maven-3.8.4-bin.tar.gz && \ + tar -xvf apache-maven-*-bin.tar.gz && \ + rm apache-maven-*-bin.tar.gz && \ + mv apache-maven-* maven && \ + ln -s /usr/share/maven/bin/mvn /bin/ + +WORKDIR /helidon + +# Create a first layer to cache the "Maven World" in the local repository. +# Incremental docker builds will always resume after that, unless you update +# the pom +ADD pom.xml . +RUN mvn package -Dmaven.test.skip -Declipselink.weave.skip -Declipselink.weave.skip -DskipOpenApiGenerate + +# Do the Maven build! +# Incremental docker builds will resume here when you change sources +ADD src src +RUN mvn package -DskipTests + +RUN echo "done!" + +# 2nd stage, build the runtime image +FROM container-registry.oracle.com/java/jdk-no-fee-term:21 +WORKDIR /helidon + +# Copy the binary built in the 1st stage +COPY --from=build /helidon/target/movies.jar ./ +COPY --from=build /helidon/target/libs ./libs + +CMD ["java", "-jar", "movies.jar"] + +EXPOSE 8080 diff --git a/Dockerfile.jlink b/Dockerfile.jlink new file mode 100644 index 0000000..6b0c5e7 --- /dev/null +++ b/Dockerfile.jlink @@ -0,0 +1,35 @@ + +# 1st stage, build the app +FROM container-registry.oracle.com/java/jdk-no-fee-term:21 as build + +WORKDIR /usr/share + +# Install maven +RUN set -x && \ + curl -O https://archive.apache.org/dist/maven/maven-3/3.8.4/binaries/apache-maven-3.8.4-bin.tar.gz && \ + tar -xvf apache-maven-*-bin.tar.gz && \ + rm apache-maven-*-bin.tar.gz && \ + mv apache-maven-* maven && \ + ln -s /usr/share/maven/bin/mvn /bin/ + +WORKDIR /helidon + +# Create a first layer to cache the "Maven World" in the local repository. +# Incremental docker builds will always resume after that, unless you update +# the pom +ADD pom.xml . +RUN mvn package -Dmaven.test.skip -Declipselink.weave.skip + +# Do the Maven build to create the custom Java Runtime Image +# Incremental docker builds will resume here when you change sources +ADD src src +RUN mvn package -Pjlink-image -DskipTests +RUN echo "done!" + +# 2nd stage, build the final image with the JRI built in the 1st stage + +FROM debian:stretch-slim +WORKDIR /helidon +COPY --from=build /helidon/target/movies-jri ./ +ENTRYPOINT ["/bin/bash", "/helidon/bin/start"] +EXPOSE 8080 diff --git a/Dockerfile.native b/Dockerfile.native new file mode 100644 index 0000000..f124eb7 --- /dev/null +++ b/Dockerfile.native @@ -0,0 +1,39 @@ + +# 1st stage, build the app +FROM ghcr.io/graalvm/graalvm-community:21.0.0-ol9 as build + +WORKDIR /usr/share + +# Install maven +RUN set -x && \ + curl -O https://archive.apache.org/dist/maven/maven-3/3.8.4/binaries/apache-maven-3.8.4-bin.tar.gz && \ + tar -xvf apache-maven-*-bin.tar.gz && \ + rm apache-maven-*-bin.tar.gz && \ + mv apache-maven-* maven && \ + ln -s /usr/share/maven/bin/mvn /bin/ + +WORKDIR /helidon + +# Create a first layer to cache the "Maven World" in the local repository. +# Incremental docker builds will always resume after that, unless you update +# the pom +ADD pom.xml . +RUN mvn package -Pnative-image -Dnative.image.skip -Dmaven.test.skip -Declipselink.weave.skip + +# Do the Maven build! +# Incremental docker builds will resume here when you change sources +ADD src src +RUN mvn package -Pnative-image -Dnative.image.buildStatic -DskipTests + +RUN echo "done!" + +# 2nd stage, build the runtime image +FROM scratch +WORKDIR /helidon + +# Copy the binary built in the 1st stage +COPY --from=build /helidon/target/movies . + +ENTRYPOINT ["./movies"] + +EXPOSE 8080 diff --git a/README.md b/README.md index 9d6e0e2..9ba653a 100644 --- a/README.md +++ b/README.md @@ -1,2 +1,158 @@ -# movies-management -Sample of REST API using Helidon and Oracle NoSQL +# movies + +Sample Helidon MP project that includes multiple REST operations. + +## Build and run + + +With JDK21 +```bash +mvn package +java -jar target/movies.jar +``` + +## Exercise the application + +Basic: +``` +curl -X GET http://localhost:8080/simple-greet +Hello World! +``` + + +JSON: +``` +curl -X GET http://localhost:8080/greet +{"message":"Hello World!"} + +curl -X GET http://localhost:8080/greet/Joe +{"message":"Hello Joe!"} + +curl -X PUT -H "Content-Type: application/json" -d '{"greeting" : "Hola"}' http://localhost:8080/greet/greeting + +curl -X GET http://localhost:8080/greet/Jose +{"message":"Hola Jose!"} +``` + + + +## Try health + +``` +curl -s -X GET http://localhost:8080/health +{"outcome":"UP",... + +``` + + +## Building a Native Image + +The generation of native binaries requires an installation of GraalVM 22.1.0+. + +You can build a native binary using Maven as follows: + +``` +mvn -Pnative-image install -DskipTests +``` + +The generation of the executable binary may take a few minutes to complete depending on +your hardware and operating system. When completed, the executable file will be available +under the `target` directory and be named after the artifact ID you have chosen during the +project generation phase. + + + +## Try metrics + +``` +# Prometheus Format +curl -s -X GET http://localhost:8080/metrics +# TYPE base:gc_g1_young_generation_count gauge +. . . + +# JSON Format +curl -H 'Accept: application/json' -X GET http://localhost:8080/metrics +{"base":... +. . . +``` + + + +## Building the Docker Image + +``` +docker build -t movies . +``` + +## Running the Docker Image + +``` +docker run --rm -p 8080:8080 movies:latest +``` + +Exercise the application as described above. + + +## Run the application in Kubernetes + +If you don’t have access to a Kubernetes cluster, you can [install one](https://helidon.io/docs/latest/#/about/kubernetes) on your desktop. + +### Verify connectivity to cluster + +``` +kubectl cluster-info # Verify which cluster +kubectl get pods # Verify connectivity to cluster +``` + +### Deploy the application to Kubernetes + +``` +kubectl create -f app.yaml # Deploy application +kubectl get pods # Wait for quickstart pod to be RUNNING +kubectl get service movies # Get service info +kubectl port-forward service/movies 8081:8080 # Forward service port to 8081 +``` + +You can now exercise the application as you did before but use the port number 8081. + +After you’re done, cleanup. + +``` +kubectl delete -f app.yaml +``` + + +## Building a Custom Runtime Image + +Build the custom runtime image using the jlink image profile: + +``` +mvn package -Pjlink-image +``` + +This uses the helidon-maven-plugin to perform the custom image generation. +After the build completes it will report some statistics about the build including the reduction in image size. + +The target/movies-jri directory is a self contained custom image of your application. It contains your application, +its runtime dependencies and the JDK modules it depends on. You can start your application using the provide start script: + +``` +./target/movies-jri/bin/start +``` + +Class Data Sharing (CDS) Archive +Also included in the custom image is a Class Data Sharing (CDS) archive that improves your application’s startup +performance and in-memory footprint. You can learn more about Class Data Sharing in the JDK documentation. + +The CDS archive increases your image size to get these performance optimizations. It can be of significant size (tens of MB). +The size of the CDS archive is reported at the end of the build output. + +If you’d rather have a smaller image size (with a slightly increased startup time) you can skip the creation of the CDS +archive by executing your build like this: + +``` +mvn package -Pjlink-image -Djlink.image.addClassDataSharingArchive=false +``` + +For more information on available configuration options see the helidon-maven-plugin documentation. + diff --git a/app.yaml b/app.yaml new file mode 100644 index 0000000..205399b --- /dev/null +++ b/app.yaml @@ -0,0 +1,37 @@ +kind: Service +apiVersion: v1 +metadata: + name: movies + labels: + app: movies +spec: + type: ClusterIP + selector: + app: movies + ports: + - name: tcp + port: 8080 + protocol: TCP + targetPort: 8080 +--- +kind: Deployment +apiVersion: apps/v1 +metadata: + name: movies +spec: + replicas: 1 + selector: + matchLabels: + app: movies + template: + metadata: + labels: + app: movies + version: v1 + spec: + containers: + - name: movies + image: movies + imagePullPolicy: IfNotPresent + ports: + - containerPort: 8080 diff --git a/pom.xml b/pom.xml new file mode 100644 index 0000000..45d1928 --- /dev/null +++ b/pom.xml @@ -0,0 +1,96 @@ + + + 4.0.0 + + io.helidon.applications + helidon-mp + 4.1.3 + + + expert.os.demos + movies + 1.0-SNAPSHOT + + + + + io.helidon.microprofile.bundles + helidon-microprofile-core + + + io.helidon.microprofile.openapi + helidon-microprofile-openapi + + + io.helidon.microprofile.health + helidon-microprofile-health + + + jakarta.json.bind + jakarta.json.bind-api + + + org.glassfish.jersey.media + jersey-media-json-binding + runtime + + + io.helidon.logging + helidon-logging-jul + runtime + + + io.smallrye + jandex + runtime + + + org.eclipse.microprofile.metrics + microprofile-metrics-api + + + io.helidon.microprofile.metrics + helidon-microprofile-metrics + + + org.junit.jupiter + junit-jupiter-api + test + + + io.helidon.microprofile.testing + helidon-microprofile-testing-junit5 + test + + + org.hamcrest + hamcrest-all + test + + + + + + + org.apache.maven.plugins + maven-dependency-plugin + + + copy-libs + + + + + io.smallrye + jandex-maven-plugin + + + make-index + + + + + + diff --git a/src/main/java/expert/os/demos/movies/GreetResource.java b/src/main/java/expert/os/demos/movies/GreetResource.java new file mode 100644 index 0000000..3ac3aa5 --- /dev/null +++ b/src/main/java/expert/os/demos/movies/GreetResource.java @@ -0,0 +1,115 @@ + +package expert.os.demos.movies; + +import jakarta.enterprise.context.RequestScoped; +import jakarta.inject.Inject; +import jakarta.ws.rs.Consumes; +import jakarta.ws.rs.GET; +import jakarta.ws.rs.PUT; +import jakarta.ws.rs.Path; +import jakarta.ws.rs.PathParam; +import jakarta.ws.rs.Produces; +import jakarta.ws.rs.core.MediaType; +import jakarta.ws.rs.core.Response; + +import org.eclipse.microprofile.openapi.annotations.enums.SchemaType; +import org.eclipse.microprofile.openapi.annotations.media.Content; +import org.eclipse.microprofile.openapi.annotations.media.Schema; +import org.eclipse.microprofile.openapi.annotations.parameters.RequestBody; +import org.eclipse.microprofile.openapi.annotations.responses.APIResponse; +import org.eclipse.microprofile.openapi.annotations.responses.APIResponses; + +/** + * A simple JAX-RS resource to greet you. Examples: + * + * Get default greeting message: + * curl -X GET http://localhost:8080/greet + * + * Get greeting message for Joe: + * curl -X GET http://localhost:8080/greet/Joe + * + * Change greeting + * curl -X PUT -H "Content-Type: application/json" -d '{"greeting" : "Howdy"}' http://localhost:8080/greet/greeting + * + * The message is returned as a JSON object. + */ +@Path("/greet") +@RequestScoped +public class GreetResource { + + /** + * The greeting message provider. + */ + private final GreetingProvider greetingProvider; + + /** + * Using constructor injection to get a configuration property. + * By default this gets the value from META-INF/microprofile-config + * + * @param greetingConfig the configured greeting message + */ + @Inject + public GreetResource(GreetingProvider greetingConfig) { + this.greetingProvider = greetingConfig; + } + + /** + * Return a worldly greeting message. + * + * @return {@link Message} + */ + @GET + @Produces(MediaType.APPLICATION_JSON) + public Message getDefaultMessage() { + return createResponse("World"); + } + + /** + * Return a greeting message using the name that was provided. + * + * @param name the name to greet + * @return {@link Message} + */ + @Path("/{name}") + @GET + @Produces(MediaType.APPLICATION_JSON) + public Message getMessage(@PathParam("name") String name) { + return createResponse(name); + } + + /** + * Set the greeting to use in future messages. + * + * @param message Message containing the new greeting + * @return {@link Response} + */ + @Path("/greeting") + @PUT + @Consumes(MediaType.APPLICATION_JSON) + @Produces(MediaType.APPLICATION_JSON) + @RequestBody(name = "greeting", + required = true, + content = @Content(mediaType = "application/json", + schema = @Schema(type = SchemaType.OBJECT, requiredProperties = { "greeting" }))) + @APIResponses({ + @APIResponse(name = "normal", responseCode = "204", description = "Greeting updated"), + @APIResponse(name = "missing 'greeting'", responseCode = "400", + description = "JSON did not contain setting for 'greeting'")}) + public Response updateGreeting(Message message) { + + if (message.getGreeting() == null || message.getGreeting().isEmpty()) { + Message error = new Message(); + error.setMessage("No greeting provided"); + return Response.status(Response.Status.BAD_REQUEST).entity(error).build(); + } + + greetingProvider.setMessage(message.getGreeting()); + return Response.status(Response.Status.NO_CONTENT).build(); + } + + private Message createResponse(String who) { + String msg = String.format("%s %s!", greetingProvider.getMessage(), who); + + return new Message(msg); + } +} diff --git a/src/main/java/expert/os/demos/movies/GreetingProvider.java b/src/main/java/expert/os/demos/movies/GreetingProvider.java new file mode 100644 index 0000000..2312116 --- /dev/null +++ b/src/main/java/expert/os/demos/movies/GreetingProvider.java @@ -0,0 +1,35 @@ + +package expert.os.demos.movies; + +import java.util.concurrent.atomic.AtomicReference; + +import jakarta.enterprise.context.ApplicationScoped; +import jakarta.inject.Inject; + +import org.eclipse.microprofile.config.inject.ConfigProperty; + +/** + * Provider for greeting message. + */ +@ApplicationScoped +public class GreetingProvider { + private final AtomicReference message = new AtomicReference<>(); + + /** + * Create a new greeting provider, reading the message from configuration. + * + * @param message greeting to use + */ + @Inject + public GreetingProvider(@ConfigProperty(name = "app.greeting") String message) { + this.message.set(message); + } + + String getMessage() { + return message.get(); + } + + void setMessage(String message) { + this.message.set(message); + } +} diff --git a/src/main/java/expert/os/demos/movies/Message.java b/src/main/java/expert/os/demos/movies/Message.java new file mode 100644 index 0000000..4033810 --- /dev/null +++ b/src/main/java/expert/os/demos/movies/Message.java @@ -0,0 +1,31 @@ +package expert.os.demos.movies; + +public class Message { + + private String message; + + private String greeting; + + public Message() { + } + + public Message(String message) { + this.message = message; + } + + public void setMessage(String message) { + this.message = message; + } + + public String getMessage() { + return this.message; + } + + public void setGreeting(String greeting) { + this.greeting = greeting; + } + + public String getGreeting() { + return this.greeting; + } +} diff --git a/src/main/java/expert/os/demos/movies/SimpleGreetResource.java b/src/main/java/expert/os/demos/movies/SimpleGreetResource.java new file mode 100644 index 0000000..9ca6c6a --- /dev/null +++ b/src/main/java/expert/os/demos/movies/SimpleGreetResource.java @@ -0,0 +1,69 @@ + +package expert.os.demos.movies; + +import org.eclipse.microprofile.metrics.MetricUnits; +import org.eclipse.microprofile.metrics.annotation.Counted; +import org.eclipse.microprofile.metrics.annotation.Timed; +import jakarta.ws.rs.PathParam; + +import jakarta.inject.Inject; +import jakarta.ws.rs.GET; +import jakarta.ws.rs.Path; +import jakarta.ws.rs.Produces; +import jakarta.ws.rs.core.MediaType; + +import org.eclipse.microprofile.config.inject.ConfigProperty; + +/** + * A simple JAX-RS resource to greet you. Examples: + * + * Get default greeting message: + * curl -X GET http://localhost:8080/simple-greet + * + * The message is returned as a JSON object. + */ +@Path("/simple-greet") +public class SimpleGreetResource { + + private static final String PERSONALIZED_GETS_COUNTER_NAME = "personalizedGets"; + private static final String PERSONALIZED_GETS_COUNTER_DESCRIPTION = "Counts personalized GET operations"; + private static final String GETS_TIMER_NAME = "allGets"; + private static final String GETS_TIMER_DESCRIPTION = "Tracks all GET operations"; + private final String message; + + @Inject + public SimpleGreetResource(@ConfigProperty(name = "app.greeting") String message) { + this.message = message; + } + + /** + * Return a worldly greeting message. + * + * @return {@link Message} + */ + @GET + @Produces(MediaType.APPLICATION_JSON) + public Message getDefaultMessage() { + String msg = String.format("%s %s!", message, "World"); + Message message = new Message(); + message.setMessage(msg); + return message; + } + + + @Path("/{name}") + @GET + @Produces(MediaType.APPLICATION_JSON) + @Counted(name = PERSONALIZED_GETS_COUNTER_NAME, + absolute = true, + description = PERSONALIZED_GETS_COUNTER_DESCRIPTION) + @Timed(name = GETS_TIMER_NAME, + description = GETS_TIMER_DESCRIPTION, + unit = MetricUnits.SECONDS, + absolute = true) + public Message getMessage(@PathParam("name") String name) { + String message = String.format("Hello %s", name); + return new Message(message); + } + +} diff --git a/src/main/java/expert/os/demos/movies/package-info.java b/src/main/java/expert/os/demos/movies/package-info.java new file mode 100644 index 0000000..6a35e6d --- /dev/null +++ b/src/main/java/expert/os/demos/movies/package-info.java @@ -0,0 +1,2 @@ + +package expert.os.demos.movies; diff --git a/src/main/resources/META-INF/beans.xml b/src/main/resources/META-INF/beans.xml new file mode 100644 index 0000000..b099340 --- /dev/null +++ b/src/main/resources/META-INF/beans.xml @@ -0,0 +1,8 @@ + + + diff --git a/src/main/resources/META-INF/microprofile-config.properties b/src/main/resources/META-INF/microprofile-config.properties new file mode 100644 index 0000000..e3b22ac --- /dev/null +++ b/src/main/resources/META-INF/microprofile-config.properties @@ -0,0 +1,11 @@ +# Microprofile server properties +server.port=8080 +server.host=0.0.0.0 + +# Change the following to true to enable the optional MicroProfile Metrics REST.request metrics +metrics.rest-request.enabled=false + +# Application properties. This is the default greeting +app.greeting=Hello + + diff --git a/src/main/resources/META-INF/native-image/expert/os/demos/movies/native-image.properties b/src/main/resources/META-INF/native-image/expert/os/demos/movies/native-image.properties new file mode 100644 index 0000000..5eef9a3 --- /dev/null +++ b/src/main/resources/META-INF/native-image/expert/os/demos/movies/native-image.properties @@ -0,0 +1 @@ +Args=--initialize-at-build-time=expert.os.demos.movies \ No newline at end of file diff --git a/src/main/resources/logging.properties b/src/main/resources/logging.properties new file mode 100644 index 0000000..ffad43d --- /dev/null +++ b/src/main/resources/logging.properties @@ -0,0 +1,20 @@ + +# Example Logging Configuration File +# For more information see $JAVA_HOME/jre/lib/logging.properties + +# Send messages to the console +handlers=io.helidon.logging.jul.HelidonConsoleHandler + +# HelidonConsoleHandler uses a SimpleFormatter subclass that replaces "!thread!" with the current thread +java.util.logging.SimpleFormatter.format=%1$tY.%1$tm.%1$td %1$tH:%1$tM:%1$tS %4$s %3$s !thread!: %5$s%6$s%n + +# Global logging level. Can be overridden by specific loggers +.level=INFO + +# Quiet Weld +org.jboss.level=WARNING + +# Component specific log levels +#io.helidon.config.level=INFO +#io.helidon.security.level=INFO +#io.helidon.common.level=INFO diff --git a/src/test/java/expert/os/demos/movies/MainTest.java b/src/test/java/expert/os/demos/movies/MainTest.java new file mode 100644 index 0000000..5be4d03 --- /dev/null +++ b/src/test/java/expert/os/demos/movies/MainTest.java @@ -0,0 +1,97 @@ + +package expert.os.demos.movies; + +import jakarta.inject.Inject; +import jakarta.ws.rs.client.WebTarget; +import jakarta.ws.rs.core.Response; +import org.eclipse.microprofile.metrics.Counter; +import org.eclipse.microprofile.metrics.MetricRegistry; +import jakarta.ws.rs.client.Entity; +import jakarta.ws.rs.core.MediaType; + +import io.helidon.microprofile.testing.junit5.HelidonTest; +import io.helidon.metrics.api.MetricsFactory; + +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.AfterAll; + +import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.Matchers.is; +import static org.junit.jupiter.api.Assertions.assertEquals; + +@HelidonTest +class MainTest { + + @Inject + private MetricRegistry registry; + + @Inject + private WebTarget target; + + + @Test + void testHealth() { + Response response = target + .path("health") + .request() + .get(); + assertThat(response.getStatus(), is(200)); + } + + @Test + void testMicroprofileMetrics() { + Message message = target.path("simple-greet/Joe") + .request() + .get(Message.class); + + assertThat(message.getMessage(), is("Hello Joe")); + Counter counter = registry.counter("personalizedGets"); + double before = counter.getCount(); + + message = target.path("simple-greet/Eric") + .request() + .get(Message.class); + + assertThat(message.getMessage(), is("Hello Eric")); + double after = counter.getCount(); + assertEquals(1d, after - before, "Difference in personalized greeting counter between successive calls"); + } + + @AfterAll + static void clear() { + MetricsFactory.closeAll(); + } + + + @Test + void testGreet() { + Message message = target + .path("simple-greet") + .request() + .get(Message.class); + assertThat(message.getMessage(), is("Hello World!")); + } + + @Test + void testGreetings() { + Message jsonMessage = target + .path("greet/Joe") + .request() + .get(Message.class); + assertThat(jsonMessage.getMessage(), is("Hello Joe!")); + + try (Response r = target + .path("greet/greeting") + .request() + .put(Entity.entity("{\"greeting\" : \"Hola\"}", MediaType.APPLICATION_JSON))) { + assertThat(r.getStatus(), is(204)); + } + + jsonMessage = target + .path("greet/Jose") + .request() + .get(Message.class); + assertThat(jsonMessage.getMessage(), is("Hola Jose!")); + } + +} diff --git a/src/test/resources/META-INF/microprofile-config.properties b/src/test/resources/META-INF/microprofile-config.properties new file mode 100644 index 0000000..e69de29 diff --git a/src/test/resources/application-test.yaml b/src/test/resources/application-test.yaml new file mode 100644 index 0000000..94f3451 --- /dev/null +++ b/src/test/resources/application-test.yaml @@ -0,0 +1,2 @@ +security: + enabled: false \ No newline at end of file