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