Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions .dockerignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@

*
!target/*.jar
!src/test/resources/jwt
!src/test/java/httpServer.java
14 changes: 14 additions & 0 deletions .gitattributes
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@

# Declare files that will always have LF line endings on checkout
* text eol=lf

# Windows CMD does not like LF in batch files
*.bat text eol=crlf
*.cmd text eol=crlf

# Files that are truly binary and should not be modified
*.png binary
*.jpg binary
*.jpeg binary
*.jar binary
*.p12 binary
2 changes: 1 addition & 1 deletion .gitlab-ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ variables:
DOCKER_HOST: tcp://docker:2375
DOCKER_TLS_CERTDIR: ''
DOCKER_DRIVER: overlay2
MAVEN_OPTS: -Dmaven.repo.local=$CI_PROJECT_DIR/.m2/repository
MAVEN_OPTS: -D maven.repo.local=$CI_PROJECT_DIR/.m2/repository

build:
stage: build
Expand Down
50 changes: 32 additions & 18 deletions .mvn/parent.xml → .mvn/spring.xml
Original file line number Diff line number Diff line change
Expand Up @@ -14,20 +14,28 @@
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>3.4.3</version>
<version>3.4.5</version>
<relativePath/>
</parent>

<groupId>com.github.jaguililla</groupId>
<artifactId>parent</artifactId>
<artifactId>spring</artifactId>
<version>0.1.0</version>
<packaging>pom</packaging>

<name>Parent</name>
<description>Common Maven settings (excluding DB and messaging)</description>
<name>Spring base POM</name>
<description>
Common Spring settings (excluding DB and messaging):

* Default configurations (i.e.: encoding)
* Set fix stable versions (Java, Maven, etc.)
* OpenAPI code generation (server and client)
* Profiles for Gatling load tests, mutation tests, Docker image generation, etc.
</description>

<properties>
<maven>3.9.9</maven>

<openApiNullable>false</openApiNullable>
<openapi.package>${project.groupId}.${project.artifactId}.http</openapi.package>
<controllers.package>${openapi.package}.controllers</controllers.package>
Expand All @@ -41,8 +49,14 @@
<springdoc.openapi.version>2.8.4</springdoc.openapi.version>
<openapi.generator.version>7.11.0</openapi.generator.version>

<jacoco.version>0.8.12</jacoco.version>
<archunit.version>1.4.0</archunit.version>
<jackson-databind-nullable.version>0.2.6</jackson-databind-nullable.version>
<pitest-maven.version>1.17.3</pitest-maven.version>
<pitest-junit5-plugin.version>1.2.1</pitest-junit5-plugin.version>
<maven-checkstyle-plugin.version>3.6.0</maven-checkstyle-plugin.version>
<exec-maven-plugin.version>3.5.0</exec-maven-plugin.version>

<jacoco.version>0.8.13</jacoco.version>
<archunit.version>1.4.1</archunit.version>
<gatling.version>3.13.4</gatling.version>
<gatling.maven.version>4.14.0</gatling.maven.version>
</properties>
Expand Down Expand Up @@ -104,6 +118,12 @@
<artifactId>kafka</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>com.auth0</groupId>
<artifactId>java-jwt</artifactId>
<version>4.4.0</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>io.gatling.highcharts</groupId>
<artifactId>gatling-charts-highcharts</artifactId>
Expand Down Expand Up @@ -238,8 +258,7 @@
<dependency>
<groupId>org.openapitools</groupId>
<artifactId>jackson-databind-nullable</artifactId>
<!-- TODO -->
<version>0.2.6</version>
<version>${jackson-databind-nullable.version}</version>
</dependency>
</dependencies>
</profile>
Expand All @@ -256,14 +275,12 @@
<plugin>
<groupId>org.pitest</groupId>
<artifactId>pitest-maven</artifactId>
<!-- TODO -->
<version>1.17.3</version>
<version>${pitest-maven.version}</version>
<dependencies>
<dependency>
<groupId>org.pitest</groupId>
<artifactId>pitest-junit5-plugin</artifactId>
<!-- TODO -->
<version>1.2.1</version>
<version>${pitest-junit5-plugin.version}</version>
</dependency>
</dependencies>
<configuration>
Expand Down Expand Up @@ -301,8 +318,7 @@
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-checkstyle-plugin</artifactId>
<!-- TODO -->
<version>3.6.0</version>
<version>${maven-checkstyle-plugin.version}</version>
<configuration>
<failsOnError>true</failsOnError>
</configuration>
Expand All @@ -324,8 +340,7 @@
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-checkstyle-plugin</artifactId>
<!-- TODO -->
<version>3.6.0</version>
<version>${maven-checkstyle-plugin.version}</version>
<reportSets>
<reportSet>
<reports>
Expand All @@ -348,8 +363,7 @@
<plugin>
<groupId>org.codehaus.mojo</groupId>
<artifactId>exec-maven-plugin</artifactId>
<!-- TODO -->
<version>3.5.0</version>
<version>${exec-maven-plugin.version}</version>
<executions>
<execution>
<id>docker-up</id>
Expand Down
2 changes: 1 addition & 1 deletion .sdkmanrc
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
# Enable auto-env through the sdkman_auto_env config
# Add key=value pairs of SDKs to use below
java=21.0.6-librca
java=21.0.7-librca
24 changes: 24 additions & 0 deletions Dockerfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@

FROM docker.io/bellsoft/liberica-runtime-container:jre-21-slim-musl

ARG PROJECT="appointments"
ARG OTEL_BASE="github.com/open-telemetry/opentelemetry-java-instrumentation/releases/download"
ARG OTEL_VERSION="v1.32.1"

WORKDIR /opt/$PROJECT

COPY target/$PROJECT-*.jar application.jar
ADD https://$OTEL_BASE/$OTEL_VERSION/opentelemetry-javaagent.jar opentelemetry.jar

USER 1000

ENV OTEL_SERVICE_NAME "$PROJECT"
ENV PERFORMANCE_OPTIONS "-XX:+AlwaysPreTouch -XX:+UseParallelGC -XX:+UseNUMA"
ENV TELEMETRY_OPTIONS "-javaagent:./opentelemetry.jar"
ENV JAVA_TOOL_OPTIONS "$PERFORMANCE_OPTIONS $TELEMETRY_OPTIONS"

HEALTHCHECK --interval=10s --start-period=10s CMD \
wget -O/dev/stdout --tries=1 \
http://localhost:8080/actuator/health 2>/dev/null | grep UP || exit 1

ENTRYPOINT [ "java", "-jar", "application.jar" ]
5 changes: 5 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -137,3 +137,8 @@ To run the Gatling test, execute `./mvnw -P gatling` at the shell.

[Gatling settings]: https://docs.gatling.io/reference/script/core/configuration
[gatlingDefaults]: https://github.com/gatling/gatling/blob/main/gatling-core/src/main/resources/gatling-defaults.conf

## Release
* Merges to `main` create and publish a release
* A release is a tag, a Maven package for the application, and another for the client
* Publishing is uploading the release to GitHub packages
27 changes: 15 additions & 12 deletions docker-compose.yml
Original file line number Diff line number Diff line change
Expand Up @@ -4,11 +4,13 @@ name: appointments
services:

postgres:
image: docker.io/postgres:16-alpine
image: docker.io/postgres:17-alpine
platform: linux/amd64
environment:
POSTGRES_USER: root
POSTGRES_PASSWORD: root
POSTGRES_DB: local
PGDATA: /var/lib/postgresql/data/pgdata
volumes:
- data:/var/lib/postgresql/data
- ./src/test/resources/db:/docker-entrypoint-initdb.d
Expand All @@ -17,6 +19,7 @@ services:

kafka:
image: docker.io/apache/kafka-native:3.8.0
platform: linux/amd64
environment:
CLUSTER_ID: 4L6g3nShT-eMCtK--X86sw
KAFKA_NODE_ID: 1
Expand All @@ -35,22 +38,14 @@ services:
ports:
- "127.0.0.1:9092:9092"

# openobserve:
# image: docker.io/openobserve/openobserve:v0.14.0
# environment:
# ZO_ROOT_USER_EMAIL: "root@example.com"
# ZO_ROOT_USER_PASSWORD: "Complex-pass#123"
# volumes:
# - data:/data
# ports:
# - "5080:5080"

appointments:
profiles: [ local ]
depends_on:
- postgres
- kafka
image: docker.io/com.github.jaguililla/appointments
platform: linux/amd64
build:
context: .
environment:
GLOBAL_LOG_LEVEL: warn
APPLICATION_LOG_LEVEL: info
Expand All @@ -61,6 +56,14 @@ services:
ports:
- "127.0.0.1:18080:8080"

openid.mock:
image: docker.io/bellsoft/liberica-runtime-container:jdk-21-slim-musl
platform: linux/amd64
security_opt:
- no-new-privileges:true
ports:
- "127.0.0.1:12345:12345"

volumes:
data:
driver: local
30 changes: 22 additions & 8 deletions pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -10,13 +10,13 @@

<parent>
<groupId>com.github.jaguililla</groupId>
<artifactId>parent</artifactId>
<artifactId>spring</artifactId>
<version>0.1.0</version>
<relativePath>.mvn/parent.xml</relativePath>
<relativePath>.mvn/spring.xml</relativePath>
</parent>

<artifactId>appointments</artifactId>
<version>0.3.10</version>
<version>0.3.11</version>

<name>Appointments</name>
<description>Application to create appointments (REST API)</description>
Expand Down Expand Up @@ -45,11 +45,25 @@
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-jdbc</artifactId>
</dependency>
<!-- <dependency>-->
<!-- <groupId>io.micrometer</groupId>-->
<!-- <artifactId>micrometer-registry-otlp</artifactId>-->
<!-- <scope>runtime</scope>-->
<!-- </dependency>-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-oauth2-resource-server</artifactId>
</dependency>
<dependency>
<groupId>io.micrometer</groupId>
<artifactId>micrometer-registry-otlp</artifactId>
<scope>runtime</scope>
<exclusions>
<exclusion>
<groupId>com.google.protobuf</groupId>
<artifactId>protobuf-java</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>io.micrometer</groupId>
<artifactId>micrometer-tracing-bridge-brave</artifactId>
</dependency>
<dependency>
<groupId>org.flywaydb</groupId>
<artifactId>flyway-database-postgresql</artifactId>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,9 +25,13 @@ class ApplicationConfiguration {
private String deleteMessage;

@Bean
AppointmentsNotifier appointmentsNotifier(final KafkaTemplate<String, String> kafkaTemplate) {
AppointmentsNotifier appointmentsNotifier(
final KafkaTemplate<String, String> kafkaTemplate,
final ConsumerFactory<String, String> consumerFactory
) {
final var type = KafkaTemplateAppointmentsNotifier.class.getSimpleName();
LOGGER.info("Creating Appointments Notifier: {}", type);
kafkaTemplate.setConsumerFactory(consumerFactory);
return new KafkaTemplateAppointmentsNotifier(
kafkaTemplate, notifierTopic, createMessage, deleteMessage
);
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
package com.github.jaguililla.appointments.controllers;

import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.method.configuration.EnableMethodSecurity;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configurers.AbstractHttpConfigurer;
import org.springframework.security.config.annotation.web.configurers.HeadersConfigurer.FrameOptionsConfig;
import org.springframework.security.web.SecurityFilterChain;

import static org.springframework.security.oauth2.jwt.JwtDecoders.fromIssuerLocation;

@Configuration
@EnableWebSecurity
@EnableMethodSecurity(securedEnabled = true, jsr250Enabled = true)
public class SecurityConfiguration {

private static final String[] PERMITTED_ENDPOINTS = {
"/actuator/info",
"/actuator/info/**",
"/actuator/health",
"/actuator/health/**",
"/actuator/metrics",
"/actuator/metrics/**",
"/v*/api-docs/**",
"/v*/api-docs*",
"/swagger-ui.html",
"/swagger-ui/**"
};

@Bean
public SecurityFilterChain securityFilterChain(
HttpSecurity http,
// TODO There should be a way to inject the JwtDecoder (this is a Spring property)
@Value("${spring.security.oauth2.resourceserver.jwk.issuer-uri}") String issuer
) throws Exception {

var jwtDecoder = fromIssuerLocation(issuer);

return http
.csrf(AbstractHttpConfigurer::disable)
.headers(headers -> headers.frameOptions(FrameOptionsConfig::sameOrigin))
.authorizeHttpRequests(requests -> requests
.requestMatchers(PERMITTED_ENDPOINTS).permitAll()
.anyRequest().authenticated()
)
.oauth2ResourceServer(oauth2 -> oauth2.jwt(jwt -> jwt.decoder(jwtDecoder)))
.build();
}
}
3 changes: 3 additions & 0 deletions src/main/resources/application.yml
Original file line number Diff line number Diff line change
Expand Up @@ -30,3 +30,6 @@ spring:
value-deserializer: org.apache.kafka.common.serialization.StringDeserializer
group-id: tests
auto-offset-reset: earliest

security.oauth2.resourceserver.jwk:
issuer-uri: ${OPENID:http://localhost:9876/realms/appointments}
Loading