From 5647a5a9591eb001ccf23ece199fdb216bdfefa3 Mon Sep 17 00:00:00 2001 From: Florian THIERRY Date: Thu, 26 Sep 2024 10:21:20 +0200 Subject: [PATCH] Implementation of jpa saving for traffic traces. --- .../security/model/CustomUserDetails.java | 4 ++ .../traffic/TrafficTraceUseCases.java | 7 ++- .../codiki/application/user/UserUseCases.java | 4 +- .../domain/traffic/model/TrafficTrace.java | 9 ++- .../security/JwtAuthenticationFilter.java | 4 -- .../traffic/ApiCallsLoggerAspect.java | 41 +++++++++----- ...apter.java => TrafficTraceJpaAdapter.java} | 14 +++-- .../traffic/model/HttpMethodEntity.java | 25 +++++++++ .../traffic/model/TrafficTraceEntity.java | 56 +++++++++++++++++++ .../TrafficTraceEntityJpaRepository.java | 12 ++++ .../resources/sql/002-traffic-trace-table.sql | 11 ++++ 11 files changed, 161 insertions(+), 26 deletions(-) rename backend/codiki-infrastructure/src/main/java/org/codiki/infrastructure/traffic/{TrafficTraceInMemoryAdapter.java => TrafficTraceJpaAdapter.java} (62%) create mode 100644 backend/codiki-infrastructure/src/main/java/org/codiki/infrastructure/traffic/model/HttpMethodEntity.java create mode 100644 backend/codiki-infrastructure/src/main/java/org/codiki/infrastructure/traffic/model/TrafficTraceEntity.java create mode 100644 backend/codiki-infrastructure/src/main/java/org/codiki/infrastructure/traffic/repository/TrafficTraceEntityJpaRepository.java create mode 100644 backend/codiki-infrastructure/src/main/resources/sql/002-traffic-trace-table.sql diff --git a/backend/codiki-application/src/main/java/org/codiki/application/security/model/CustomUserDetails.java b/backend/codiki-application/src/main/java/org/codiki/application/security/model/CustomUserDetails.java index ecf7871..6d4dab1 100644 --- a/backend/codiki-application/src/main/java/org/codiki/application/security/model/CustomUserDetails.java +++ b/backend/codiki-application/src/main/java/org/codiki/application/security/model/CustomUserDetails.java @@ -25,6 +25,10 @@ public class CustomUserDetails implements UserDetails { .toList(); } + public User getUser() { + return user; + } + @Override public String getUsername() { return user.id().toString(); diff --git a/backend/codiki-application/src/main/java/org/codiki/application/traffic/TrafficTraceUseCases.java b/backend/codiki-application/src/main/java/org/codiki/application/traffic/TrafficTraceUseCases.java index 3d2a612..1dc965f 100644 --- a/backend/codiki-application/src/main/java/org/codiki/application/traffic/TrafficTraceUseCases.java +++ b/backend/codiki-application/src/main/java/org/codiki/application/traffic/TrafficTraceUseCases.java @@ -26,7 +26,11 @@ public class TrafficTraceUseCases { } @Async - public void saveNewTrace(TrafficEndpoint trafficEndpoint, @Nullable String correlationId) { + public void saveNewTrace( + TrafficEndpoint trafficEndpoint, + @Nullable UUID userId, + @Nullable String correlationId + ) { if (isNull(trafficEndpoint)) { throw new TrafficTraceCreationException("Traffic endpoint should not be null."); } @@ -35,6 +39,7 @@ public class TrafficTraceUseCases { .withId(UUID.randomUUID()) .withDateTime(ZonedDateTime.now(clock)) .withEndpoint(trafficEndpoint) + .withUserId(userId) .withCorrelationId(correlationId) .build(); trafficTracePort.save(newTrace); diff --git a/backend/codiki-application/src/main/java/org/codiki/application/user/UserUseCases.java b/backend/codiki-application/src/main/java/org/codiki/application/user/UserUseCases.java index 6a4b1de..f55746c 100644 --- a/backend/codiki-application/src/main/java/org/codiki/application/user/UserUseCases.java +++ b/backend/codiki-application/src/main/java/org/codiki/application/user/UserUseCases.java @@ -87,9 +87,7 @@ public class UserUseCases { .map(Authentication::getPrincipal) .filter(CustomUserDetails.class::isInstance) .map(CustomUserDetails.class::cast) - .map(CustomUserDetails::getUsername) - .map(UUID::fromString) - .flatMap(userPort::findById); + .map(CustomUserDetails::getUser); } private UserAuthenticationData generateAuthenticationData(User user) { diff --git a/backend/codiki-domain/src/main/java/org/codiki/domain/traffic/model/TrafficTrace.java b/backend/codiki-domain/src/main/java/org/codiki/domain/traffic/model/TrafficTrace.java index aecd9df..7d33d90 100644 --- a/backend/codiki-domain/src/main/java/org/codiki/domain/traffic/model/TrafficTrace.java +++ b/backend/codiki-domain/src/main/java/org/codiki/domain/traffic/model/TrafficTrace.java @@ -7,6 +7,7 @@ public record TrafficTrace( UUID id, ZonedDateTime dateTime, TrafficEndpoint endpoint, + UUID userId, String correlationId ) { public static Builder aTrafficTrace() { @@ -17,6 +18,7 @@ public record TrafficTrace( private UUID id; private ZonedDateTime dateTime; private TrafficEndpoint endpoint; + private UUID userId; private String correlationId; private Builder() {} @@ -36,13 +38,18 @@ public record TrafficTrace( return this; } + public Builder withUserId(UUID userId) { + this.userId = userId; + return this; + } + public Builder withCorrelationId(String correlationId) { this.correlationId = correlationId; return this; } public TrafficTrace build() { - return new TrafficTrace(id, dateTime, endpoint, correlationId); + return new TrafficTrace(id, dateTime, endpoint, userId, correlationId); } } } diff --git a/backend/codiki-exposition/src/main/java/org/codiki/exposition/configuration/security/JwtAuthenticationFilter.java b/backend/codiki-exposition/src/main/java/org/codiki/exposition/configuration/security/JwtAuthenticationFilter.java index 6305ed1..c4c6a01 100644 --- a/backend/codiki-exposition/src/main/java/org/codiki/exposition/configuration/security/JwtAuthenticationFilter.java +++ b/backend/codiki-exposition/src/main/java/org/codiki/exposition/configuration/security/JwtAuthenticationFilter.java @@ -38,10 +38,6 @@ public class JwtAuthenticationFilter extends OncePerRequestFilter { .filter(authorizationHeader -> !isEmpty(authorizationHeader)) .filter(authorizationHeader -> authorizationHeader.startsWith(BEARER_PREFIX)) .map(authorizationHeader -> authorizationHeader.substring(BEARER_PREFIX.length())) - .filter(token -> { - String authorizationHeader = request.getHeader(AUTHORIZATION); - return !isEmpty(authorizationHeader) && authorizationHeader.startsWith(BEARER_PREFIX); - }) .filter(jwtService::isValid) .flatMap(jwtService::extractUser) .map(CustomUserDetails::new) diff --git a/backend/codiki-exposition/src/main/java/org/codiki/exposition/traffic/ApiCallsLoggerAspect.java b/backend/codiki-exposition/src/main/java/org/codiki/exposition/traffic/ApiCallsLoggerAspect.java index ec85b37..c0ec975 100644 --- a/backend/codiki-exposition/src/main/java/org/codiki/exposition/traffic/ApiCallsLoggerAspect.java +++ b/backend/codiki-exposition/src/main/java/org/codiki/exposition/traffic/ApiCallsLoggerAspect.java @@ -1,16 +1,20 @@ package org.codiki.exposition.traffic; +import jakarta.servlet.http.HttpServletRequest; import org.aspectj.lang.JoinPoint; import org.aspectj.lang.annotation.Aspect; import org.aspectj.lang.annotation.Before; import org.codiki.application.traffic.TrafficTraceUseCases; +import org.codiki.application.user.UserUseCases; import org.codiki.domain.traffic.model.HttpMethod; import org.codiki.domain.traffic.model.TrafficEndpoint; +import org.codiki.domain.user.model.User; import org.springframework.stereotype.Component; import org.springframework.web.context.request.RequestContextHolder; import org.springframework.web.context.request.ServletRequestAttributes; import java.util.Optional; +import java.util.UUID; @Component @Aspect @@ -18,9 +22,14 @@ public class ApiCallsLoggerAspect { private static final String HTTP_HEADER_CORRELATION_ID = "x-correlation-id"; private final TrafficTraceUseCases trafficTraceUseCases; + private final UserUseCases userUseCases; - public ApiCallsLoggerAspect(TrafficTraceUseCases trafficTraceUseCases) { + public ApiCallsLoggerAspect( + TrafficTraceUseCases trafficTraceUseCases, + UserUseCases userUseCases + ) { this.trafficTraceUseCases = trafficTraceUseCases; + this.userUseCases = userUseCases; } @Before("@annotation(org.springframework.web.bind.annotation.GetMapping)") @@ -42,19 +51,25 @@ public class ApiCallsLoggerAspect { } private void logApiCall() { - Optional.ofNullable(RequestContextHolder.getRequestAttributes()) + getHttpServletRequest().ifPresent(request -> + Optional.of(request.getMethod()) + .flatMap(HttpMethod::fromString) + .ifPresent(queryHttpMethod -> { + String queryUriPath = request.getRequestURI(); + TrafficEndpoint endpoint = new TrafficEndpoint(queryHttpMethod, queryUriPath); + UUID userId = userUseCases.getAuthenticatedUser() + .map(User::id) + .orElse(null); + String correlationId = request.getHeader(HTTP_HEADER_CORRELATION_ID); + trafficTraceUseCases.saveNewTrace(endpoint, userId, correlationId); + }) + ); + } + + private static Optional getHttpServletRequest() { + return Optional.ofNullable(RequestContextHolder.getRequestAttributes()) .filter(ServletRequestAttributes.class::isInstance) .map(ServletRequestAttributes.class::cast) - .map(ServletRequestAttributes::getRequest) - .ifPresent(request -> - Optional.of(request.getMethod()) - .flatMap(HttpMethod::fromString) - .ifPresent(queryHttpMethod -> { - String queryUriPath = request.getRequestURI(); - String correlationId = request.getHeader(HTTP_HEADER_CORRELATION_ID); - TrafficEndpoint endpoint = new TrafficEndpoint(queryHttpMethod, queryUriPath); - trafficTraceUseCases.saveNewTrace(endpoint, correlationId); - }) - ); + .map(ServletRequestAttributes::getRequest); } } diff --git a/backend/codiki-infrastructure/src/main/java/org/codiki/infrastructure/traffic/TrafficTraceInMemoryAdapter.java b/backend/codiki-infrastructure/src/main/java/org/codiki/infrastructure/traffic/TrafficTraceJpaAdapter.java similarity index 62% rename from backend/codiki-infrastructure/src/main/java/org/codiki/infrastructure/traffic/TrafficTraceInMemoryAdapter.java rename to backend/codiki-infrastructure/src/main/java/org/codiki/infrastructure/traffic/TrafficTraceJpaAdapter.java index 84ba866..8c86e7a 100644 --- a/backend/codiki-infrastructure/src/main/java/org/codiki/infrastructure/traffic/TrafficTraceInMemoryAdapter.java +++ b/backend/codiki-infrastructure/src/main/java/org/codiki/infrastructure/traffic/TrafficTraceJpaAdapter.java @@ -2,19 +2,25 @@ package org.codiki.infrastructure.traffic; import org.codiki.domain.traffic.model.TrafficTrace; import org.codiki.domain.traffic.port.TrafficTracePort; +import org.codiki.infrastructure.traffic.model.TrafficTraceEntity; +import org.codiki.infrastructure.traffic.repository.TrafficTraceEntityJpaRepository; import org.springframework.stereotype.Component; import java.time.ZonedDateTime; -import java.util.ArrayList; import java.util.List; @Component -public class TrafficTraceInMemoryAdapter implements TrafficTracePort { - private final List traces = new ArrayList<>(); +public class TrafficTraceJpaAdapter implements TrafficTracePort { + private final TrafficTraceEntityJpaRepository repository; + + public TrafficTraceJpaAdapter(TrafficTraceEntityJpaRepository repository) { + this.repository = repository; + } @Override public void save(TrafficTrace trace) { - traces.add(trace); + TrafficTraceEntity entity = new TrafficTraceEntity(trace); + repository.save(entity); } @Override diff --git a/backend/codiki-infrastructure/src/main/java/org/codiki/infrastructure/traffic/model/HttpMethodEntity.java b/backend/codiki-infrastructure/src/main/java/org/codiki/infrastructure/traffic/model/HttpMethodEntity.java new file mode 100644 index 0000000..39f4b97 --- /dev/null +++ b/backend/codiki-infrastructure/src/main/java/org/codiki/infrastructure/traffic/model/HttpMethodEntity.java @@ -0,0 +1,25 @@ +package org.codiki.infrastructure.traffic.model; + +import org.codiki.domain.traffic.model.HttpMethod; + +public enum HttpMethodEntity { + GET, POST, PUT, DELETE; + + public HttpMethod toDomain() { + return switch (this) { + case GET -> HttpMethod.GET; + case POST -> HttpMethod.POST; + case PUT -> HttpMethod.PUT; + case DELETE -> HttpMethod.DELETE; + }; + } + + public static HttpMethodEntity fromDomain(HttpMethod method) { + return switch (method) { + case HttpMethod.GET -> GET; + case HttpMethod.POST -> POST; + case HttpMethod.PUT -> PUT; + case HttpMethod.DELETE -> DELETE; + }; + } +} diff --git a/backend/codiki-infrastructure/src/main/java/org/codiki/infrastructure/traffic/model/TrafficTraceEntity.java b/backend/codiki-infrastructure/src/main/java/org/codiki/infrastructure/traffic/model/TrafficTraceEntity.java new file mode 100644 index 0000000..beafd10 --- /dev/null +++ b/backend/codiki-infrastructure/src/main/java/org/codiki/infrastructure/traffic/model/TrafficTraceEntity.java @@ -0,0 +1,56 @@ +package org.codiki.infrastructure.traffic.model; + +import jakarta.persistence.*; +import lombok.AllArgsConstructor; +import lombok.Getter; +import lombok.NoArgsConstructor; +import lombok.Setter; +import org.codiki.domain.traffic.model.TrafficEndpoint; +import org.codiki.domain.traffic.model.TrafficTrace; + +import java.time.ZonedDateTime; +import java.util.UUID; + +import static org.codiki.domain.traffic.model.TrafficTrace.aTrafficTrace; + +@NoArgsConstructor +@AllArgsConstructor +@Getter +@Setter +@Entity +@Table(name = "traffic") +public class TrafficTraceEntity { + @Id + private UUID id; + @Column(nullable = false) + private ZonedDateTime dateTime; + @Column(nullable = false) + @Enumerated + private HttpMethodEntity endpointMethod; + @Column(nullable = false) + private String endpointPath; + private UUID userId; + private String correlationId; + + public TrafficTraceEntity(TrafficTrace trace) { + id = trace.id(); + dateTime = trace.dateTime(); + endpointMethod = HttpMethodEntity.fromDomain(trace.endpoint().method()); + endpointPath = trace.endpoint().path(); + userId = trace.userId(); + correlationId = trace.correlationId(); + } + + public TrafficTrace toDomain() { + return aTrafficTrace() + .withId(id) + .withDateTime(dateTime) + .withEndpoint(new TrafficEndpoint( + endpointMethod.toDomain(), + endpointPath + )) + .withUserId(userId) + .withCorrelationId(correlationId) + .build(); + } +} diff --git a/backend/codiki-infrastructure/src/main/java/org/codiki/infrastructure/traffic/repository/TrafficTraceEntityJpaRepository.java b/backend/codiki-infrastructure/src/main/java/org/codiki/infrastructure/traffic/repository/TrafficTraceEntityJpaRepository.java new file mode 100644 index 0000000..3e6e51d --- /dev/null +++ b/backend/codiki-infrastructure/src/main/java/org/codiki/infrastructure/traffic/repository/TrafficTraceEntityJpaRepository.java @@ -0,0 +1,12 @@ +package org.codiki.infrastructure.traffic.repository; + +import org.codiki.infrastructure.traffic.model.TrafficTraceEntity; +import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.stereotype.Repository; + +import java.util.UUID; + +@Repository +public interface TrafficTraceEntityJpaRepository extends JpaRepository { + +} diff --git a/backend/codiki-infrastructure/src/main/resources/sql/002-traffic-trace-table.sql b/backend/codiki-infrastructure/src/main/resources/sql/002-traffic-trace-table.sql new file mode 100644 index 0000000..83d684d --- /dev/null +++ b/backend/codiki-infrastructure/src/main/resources/sql/002-traffic-trace-table.sql @@ -0,0 +1,11 @@ +CREATE TABLE IF NOT EXISTS traffic ( + id UUID NOT NULL, + date_time TIMESTAMP WITH TIME ZONE NOT NULL, + endpoint_method SMALLINT NOT NULL, + endpoint_path VARCHAR NOT NULL, + user_id UUID, + correlation_id VARCHAR, + CONSTRAINT traffic_pk PRIMARY KEY (id), + CONSTRAINT traffic_user_id_fk FOREIGN KEY (user_id) REFERENCES "user" (id) +); +CREATE INDEX traffic_user_id_idx ON traffic (user_id); \ No newline at end of file