From 243001a3d52d86434cf7db1eda80db670716df5a Mon Sep 17 00:00:00 2001 From: Saccilotto Date: Mon, 3 Mar 2025 21:15:18 -0300 Subject: [PATCH 01/53] feat: added github actions based on build test push image and trigger steps alongside dockerfile --- .github/workflows/build-trigger.yml | 44 +++++++++++++++++++++++++++++ Dockerfile | 11 ++++++++ 2 files changed, 55 insertions(+) create mode 100644 .github/workflows/build-trigger.yml create mode 100644 Dockerfile diff --git a/.github/workflows/build-trigger.yml b/.github/workflows/build-trigger.yml new file mode 100644 index 00000000..44d401d3 --- /dev/null +++ b/.github/workflows/build-trigger.yml @@ -0,0 +1,44 @@ +name: Backend CI/CD + +on: + push: + branches: [ main, master ] + workflow_dispatch: + +jobs: + build-and-test: + name: Build and Test Backend + runs-on: ubuntu-latest + steps: + - name: Checkout code + uses: actions/checkout@v4 + + - name: Set up JDK 11 + uses: actions/setup-java@v2 + with: + distribution: 'adopt' + java-version: '11' + + - name: Build with Gradle + run: ./gradlew build + + - name: Run tests + run: ./gradlew test + + - name: Build and push Docker image + env: + DOCKER_USERNAME: ${{ secrets.DOCKER_USERNAME }} + DOCKER_PASSWORD: ${{ secrets.DOCKER_PASSWORD }} + run: | + echo $DOCKER_PASSWORD | docker login -u $DOCKER_USERNAME --password-stdin + docker build -t your-org/backend:latest . + docker push your-org/backend:latest + + # Trigger infrastructure deployment + - name: Trigger infrastructure deployment + uses: peter-evans/repository-dispatch@v2 + with: + token: ${{ secrets.REPO_ACCESS_TOKEN }} + repository: your-org/infrastructure + event-type: backend-updated + client-payload: '{"ref": "${{ github.ref }}", "sha": "${{ github.sha }}"}' \ No newline at end of file diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 00000000..f6289ff0 --- /dev/null +++ b/Dockerfile @@ -0,0 +1,11 @@ +# Use an official OpenJDK runtime as a parent image +FROM openjdk:11-jre-slim + +# Set the working directory in the container +WORKDIR /app + +# Copy the built jar file from the host to the container +COPY build/libs/*.jar app.jar + +# Run the jar file +ENTRYPOINT ["java", "-jar", "app.jar"] \ No newline at end of file From 18370ccbeefff12c1277380fdecb8a21e688fd95 Mon Sep 17 00:00:00 2001 From: Matheus Pigatto <1304matheus@gmail.com> Date: Tue, 4 Mar 2025 17:44:40 -0300 Subject: [PATCH 02/53] feat: add owner to folder class in domain and add method to retrieve owner on Folder class --- .../com/callv2/drive/domain/folder/Folder.java | 16 +++++++++++++++- 1 file changed, 15 insertions(+), 1 deletion(-) diff --git a/domain/src/main/java/com/callv2/drive/domain/folder/Folder.java b/domain/src/main/java/com/callv2/drive/domain/folder/Folder.java index 4d52b76f..17e00ee5 100644 --- a/domain/src/main/java/com/callv2/drive/domain/folder/Folder.java +++ b/domain/src/main/java/com/callv2/drive/domain/folder/Folder.java @@ -6,6 +6,7 @@ import com.callv2.drive.domain.AggregateRoot; import com.callv2.drive.domain.exception.ValidationException; +import com.callv2.drive.domain.member.MemberID; import com.callv2.drive.domain.validation.Error; import com.callv2.drive.domain.validation.ValidationHandler; import com.callv2.drive.domain.validation.handler.Notification; @@ -14,6 +15,8 @@ public class Folder extends AggregateRoot { private boolean rootFolder; + private MemberID owner; + private FolderName name; private FolderID parentFolder; private Set subFolders; @@ -24,6 +27,7 @@ public class Folder extends AggregateRoot { private Folder( final FolderID id, + final MemberID owner, final FolderName name, final FolderID parentFolder, final Set subFolders, @@ -32,6 +36,8 @@ private Folder( final Instant deletedAt, final boolean rootFolder) { super(id); + + this.owner = owner; this.name = name; this.parentFolder = parentFolder; this.subFolders = subFolders == null ? new HashSet<>() : new HashSet<>(subFolders); @@ -45,6 +51,7 @@ private Folder( public static Folder with( final FolderID id, + final MemberID owner, final FolderName name, final FolderID parentFolder, final Set subFolders, @@ -52,7 +59,7 @@ public static Folder with( final Instant updatedAt, final Instant deletedAt, final boolean rootFolder) { - return new Folder(id, name, parentFolder, subFolders, createdAt, updatedAt, deletedAt, rootFolder); + return new Folder(id, owner, name, parentFolder, subFolders, createdAt, updatedAt, deletedAt, rootFolder); } public static Folder createRoot() { @@ -60,6 +67,7 @@ public static Folder createRoot() { return Folder.with( FolderID.unique(), + null, // When root folder is created, there is no owner FolderName.of("Root"), null, new HashSet<>(), @@ -70,6 +78,7 @@ public static Folder createRoot() { } public static Folder create( + final MemberID owner, final FolderName name, final Folder parentFolder) { @@ -77,6 +86,7 @@ public static Folder create( final var folder = Folder.with( FolderID.unique(), + owner, name, parentFolder.getId(), new HashSet<>(), @@ -162,6 +172,10 @@ public boolean isRootFolder() { return rootFolder; } + public MemberID getOwner() { + return owner; + } + public FolderID getParentFolder() { return parentFolder; } From ca1f67ba43acd8fdaa360c6df1fc335535220da0 Mon Sep 17 00:00:00 2001 From: Matheus Pigatto <1304matheus@gmail.com> Date: Tue, 4 Mar 2025 17:46:40 -0300 Subject: [PATCH 03/53] feat: adds new ownerId to create method params on create folder use case on application, adds member gateway import and member id validation --- .../folder/create/CreateFolderInput.java | 6 +++--- .../create/DefaultCreateFolderUseCase.java | 19 +++++++++++++++---- 2 files changed, 18 insertions(+), 7 deletions(-) diff --git a/application/src/main/java/com/callv2/drive/application/folder/create/CreateFolderInput.java b/application/src/main/java/com/callv2/drive/application/folder/create/CreateFolderInput.java index 2297bc3d..62946e9d 100644 --- a/application/src/main/java/com/callv2/drive/application/folder/create/CreateFolderInput.java +++ b/application/src/main/java/com/callv2/drive/application/folder/create/CreateFolderInput.java @@ -2,10 +2,10 @@ import java.util.UUID; -public record CreateFolderInput(String name, UUID parentFolderId) { +public record CreateFolderInput(String ownerId, String name, UUID parentFolderId) { - public static CreateFolderInput from(String name, UUID parentFolderId) { - return new CreateFolderInput(name, parentFolderId); + public static CreateFolderInput from(String ownerdId, String name, UUID parentFolderId) { + return new CreateFolderInput(ownerdId, name, parentFolderId); } } diff --git a/application/src/main/java/com/callv2/drive/application/folder/create/DefaultCreateFolderUseCase.java b/application/src/main/java/com/callv2/drive/application/folder/create/DefaultCreateFolderUseCase.java index 0db208f5..c1618157 100644 --- a/application/src/main/java/com/callv2/drive/application/folder/create/DefaultCreateFolderUseCase.java +++ b/application/src/main/java/com/callv2/drive/application/folder/create/DefaultCreateFolderUseCase.java @@ -6,34 +6,45 @@ import com.callv2.drive.domain.folder.FolderGateway; import com.callv2.drive.domain.folder.FolderID; import com.callv2.drive.domain.folder.FolderName; +import com.callv2.drive.domain.member.Member; +import com.callv2.drive.domain.member.MemberGateway; +import com.callv2.drive.domain.member.MemberID; import com.callv2.drive.domain.validation.Error; import com.callv2.drive.domain.validation.handler.Notification; public class DefaultCreateFolderUseCase extends CreateFolderUseCase { + private final MemberGateway memberGateway; private final FolderGateway folderGateway; - public DefaultCreateFolderUseCase(final FolderGateway folderGateway) { + public DefaultCreateFolderUseCase(final MemberGateway memberGateway, final FolderGateway folderGateway) { + this.memberGateway = memberGateway; this.folderGateway = folderGateway; } @Override public CreateFolderOutput execute(final CreateFolderInput input) { + final MemberID ownerId = MemberID.of(input.ownerId()); + + final Member owner = memberGateway + .findById(ownerId) + .orElseThrow(() -> NotFoundException.with(Member.class, input.ownerId().toString())); + final Folder parentFolder = folderGateway .findById(FolderID.of(input.parentFolderId())) .orElseThrow(() -> NotFoundException.with( Folder.class, "Parent folder with id %s not found".formatted(input.parentFolderId().toString()))); - return CreateFolderOutput.from(createFolder(FolderName.of(input.name()), parentFolder)); + return CreateFolderOutput.from(createFolder(ownerId, FolderName.of(input.name()), parentFolder)); } - private Folder createFolder(final FolderName name, final Folder parentFolder) { + private Folder createFolder(final MemberID ownerId, FolderName name, final Folder parentFolder) { final Notification notification = Notification.create(); if (parentFolder.getSubFolders().stream().anyMatch(subFolder -> subFolder.name().equals(name))) notification.append(Error.with("Folder with the same name already exists")); - final Folder folder = notification.valdiate(() -> Folder.create(name, parentFolder)); + final Folder folder = notification.valdiate(() -> Folder.create(ownerId, name, parentFolder)); if (notification.hasError()) throw ValidationException.with("Could not create Aggregate Folder", notification); From f9f3d0ac29c9b44d137b70c90b9f6a572b528cf0 Mon Sep 17 00:00:00 2001 From: Matheus Pigatto <1304matheus@gmail.com> Date: Mon, 10 Mar 2025 20:09:38 -0300 Subject: [PATCH 04/53] fix: update createFolder method to use owner object for ID retrieval --- .../application/folder/create/DefaultCreateFolderUseCase.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/application/src/main/java/com/callv2/drive/application/folder/create/DefaultCreateFolderUseCase.java b/application/src/main/java/com/callv2/drive/application/folder/create/DefaultCreateFolderUseCase.java index c1618157..ee4d2598 100644 --- a/application/src/main/java/com/callv2/drive/application/folder/create/DefaultCreateFolderUseCase.java +++ b/application/src/main/java/com/callv2/drive/application/folder/create/DefaultCreateFolderUseCase.java @@ -36,7 +36,7 @@ public CreateFolderOutput execute(final CreateFolderInput input) { Folder.class, "Parent folder with id %s not found".formatted(input.parentFolderId().toString()))); - return CreateFolderOutput.from(createFolder(ownerId, FolderName.of(input.name()), parentFolder)); + return CreateFolderOutput.from(createFolder(owner.getId(), FolderName.of(input.name()), parentFolder)); } private Folder createFolder(final MemberID ownerId, FolderName name, final Folder parentFolder) { From aef39c8fb431808e8792e65bf3c3e804364899c6 Mon Sep 17 00:00:00 2001 From: Matheus Pigatto <1304matheus@gmail.com> Date: Mon, 10 Mar 2025 20:10:21 -0300 Subject: [PATCH 05/53] feat: integrate owner ID retrieval in create folder method for enhanced security --- .../infrastructure/api/controller/FolderController.java | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/infrastructure/src/main/java/com/callv2/drive/infrastructure/api/controller/FolderController.java b/infrastructure/src/main/java/com/callv2/drive/infrastructure/api/controller/FolderController.java index f2c4e21a..856c1aa6 100644 --- a/infrastructure/src/main/java/com/callv2/drive/infrastructure/api/controller/FolderController.java +++ b/infrastructure/src/main/java/com/callv2/drive/infrastructure/api/controller/FolderController.java @@ -27,6 +27,7 @@ import com.callv2.drive.infrastructure.folder.model.GetRootFolderResponse; import com.callv2.drive.infrastructure.folder.model.MoveFolderRequest; import com.callv2.drive.infrastructure.folder.presenter.FolderPresenter; +import com.callv2.drive.infrastructure.security.SecurityContext; @RestController public class FolderController implements FolderAPI { @@ -57,8 +58,9 @@ public ResponseEntity getRoot() { @Override public ResponseEntity create(final CreateFolderRequest request) { - final var response = FolderPresenter.present(createFolderUseCase.execute(FolderAdapter.adapt(request))); - + final String ownerId = SecurityContext.getAuthenticatedUser(); + final var response = FolderPresenter.present(createFolderUseCase.execute(FolderAdapter.adapt(request, ownerId))); + return ResponseEntity .created(URI.create("/folders/" + response.id())) .body(response); From 2f14a337edcab64587e57fac5e5aa18d3f3fe518 Mon Sep 17 00:00:00 2001 From: Matheus Pigatto <1304matheus@gmail.com> Date: Mon, 10 Mar 2025 20:10:28 -0300 Subject: [PATCH 06/53] feat: add member gateway to create folder use case for improved functionality --- .../configuration/usecase/FolderUseCaseConfig.java | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/infrastructure/src/main/java/com/callv2/drive/infrastructure/configuration/usecase/FolderUseCaseConfig.java b/infrastructure/src/main/java/com/callv2/drive/infrastructure/configuration/usecase/FolderUseCaseConfig.java index 79365121..4814f14d 100644 --- a/infrastructure/src/main/java/com/callv2/drive/infrastructure/configuration/usecase/FolderUseCaseConfig.java +++ b/infrastructure/src/main/java/com/callv2/drive/infrastructure/configuration/usecase/FolderUseCaseConfig.java @@ -15,18 +15,22 @@ import com.callv2.drive.application.folder.retrieve.list.ListFoldersUseCase; import com.callv2.drive.domain.file.FileGateway; import com.callv2.drive.domain.folder.FolderGateway; +import com.callv2.drive.domain.member.MemberGateway; @Configuration public class FolderUseCaseConfig { private final FolderGateway folderGateway; private final FileGateway fileGateway; + private final MemberGateway memberGateway; public FolderUseCaseConfig( final FolderGateway folderGateway, - final FileGateway fileGateway) { + final FileGateway fileGateway, + final MemberGateway memberGateway) { this.folderGateway = folderGateway; this.fileGateway = fileGateway; + this.memberGateway = memberGateway; } @Bean @@ -36,7 +40,7 @@ GetRootFolderUseCase getRootFolderUseCase() { @Bean CreateFolderUseCase createFolderUseCase() { - return new DefaultCreateFolderUseCase(folderGateway); + return new DefaultCreateFolderUseCase(memberGateway, folderGateway); } @Bean From f0c348705edf0498a0ec265cc4f63405ecaba866 Mon Sep 17 00:00:00 2001 From: Matheus Pigatto <1304matheus@gmail.com> Date: Mon, 10 Mar 2025 20:10:38 -0300 Subject: [PATCH 07/53] feat: update FolderAdapter to include owner ID in folder creation input --- .../drive/infrastructure/folder/adapter/FolderAdapter.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/infrastructure/src/main/java/com/callv2/drive/infrastructure/folder/adapter/FolderAdapter.java b/infrastructure/src/main/java/com/callv2/drive/infrastructure/folder/adapter/FolderAdapter.java index 0e0322f8..8093410f 100644 --- a/infrastructure/src/main/java/com/callv2/drive/infrastructure/folder/adapter/FolderAdapter.java +++ b/infrastructure/src/main/java/com/callv2/drive/infrastructure/folder/adapter/FolderAdapter.java @@ -8,8 +8,8 @@ public interface FolderAdapter { - static CreateFolderInput adapt(CreateFolderRequest request) { - return CreateFolderInput.from(request.name(), request.parentFolderId()); + static CreateFolderInput adapt(CreateFolderRequest request, String ownerId) { + return CreateFolderInput.from(ownerId, request.name(), request.parentFolderId()); } static GetFolderInput adapt(UUID id) { From eb9b9b3c173de8e69d2691ad7650d60d1a2526c3 Mon Sep 17 00:00:00 2001 From: Matheus Pigatto <1304matheus@gmail.com> Date: Mon, 10 Mar 2025 20:10:44 -0300 Subject: [PATCH 08/53] feat: add owner ID field to FolderJpaEntity for improved folder ownership management --- .../folder/persistence/FolderJpaEntity.java | 22 +++++++++++++++---- 1 file changed, 18 insertions(+), 4 deletions(-) diff --git a/infrastructure/src/main/java/com/callv2/drive/infrastructure/folder/persistence/FolderJpaEntity.java b/infrastructure/src/main/java/com/callv2/drive/infrastructure/folder/persistence/FolderJpaEntity.java index 285f12b7..4fa71477 100644 --- a/infrastructure/src/main/java/com/callv2/drive/infrastructure/folder/persistence/FolderJpaEntity.java +++ b/infrastructure/src/main/java/com/callv2/drive/infrastructure/folder/persistence/FolderJpaEntity.java @@ -10,6 +10,7 @@ import com.callv2.drive.domain.folder.FolderID; import com.callv2.drive.domain.folder.FolderName; import com.callv2.drive.domain.folder.SubFolder; +import com.callv2.drive.domain.member.MemberID; import jakarta.persistence.CascadeType; import jakarta.persistence.Column; @@ -31,6 +32,9 @@ public class FolderJpaEntity { @Column(name = "name", nullable = false) private String name; + @Column(name = "owner_id") + private String ownerId; + @Column(name = "parent_folder_id") private UUID parentFolderId; @@ -50,6 +54,7 @@ private FolderJpaEntity( final UUID id, final Boolean rootFolder, final String name, + final String ownerId, final UUID parentFolderId, final Instant createdAt, final Instant updatedAt, @@ -57,6 +62,7 @@ private FolderJpaEntity( this.id = id; this.rootFolder = rootFolder; this.name = name; + this.ownerId = ownerId; this.parentFolderId = parentFolderId; this.createdAt = createdAt; this.updatedAt = updatedAt; @@ -69,13 +75,14 @@ public FolderJpaEntity() { } public static FolderJpaEntity fromDomain(final Folder folder) { - final UUID parentFolderId = folder.getParentFolder() == null ? null : folder.getParentFolder().getValue(); + final String ownerId = folder.getOwner() == null ? null : folder.getOwner().getValue(); final var entity = new FolderJpaEntity( folder.getId().getValue(), folder.isRootFolder(), folder.getName().value(), + ownerId, parentFolderId, folder.getCreatedAt(), folder.getUpdatedAt(), @@ -90,6 +97,7 @@ public static FolderJpaEntity fromDomain(final Folder folder) { public Folder toDomain() { return Folder.with( FolderID.of(id), + ownerId != null ? MemberID.of(ownerId) : null, FolderName.of(name), FolderID.of(parentFolderId), subFolders.stream() @@ -99,7 +107,6 @@ public Folder toDomain() { updatedAt, deletedAt, rootFolder); - } public void addSubFolder(final SubFolder anId) { @@ -130,6 +137,14 @@ public void setName(String name) { this.name = name; } + public String getOwnerId() { + return ownerId; + } + + public void setOwnerId(String ownerId) { + this.ownerId = ownerId; + } + public UUID getParentFolderId() { return parentFolderId; } @@ -169,5 +184,4 @@ public Instant getDeletedAt() { public void setDeletedAt(Instant deletedAt) { this.deletedAt = deletedAt; } - -} +} \ No newline at end of file From c3188cf79a0c3bde94c5244cd1cb6d5d43bc437c Mon Sep 17 00:00:00 2001 From: Matheus Pigatto <1304matheus@gmail.com> Date: Fri, 14 Mar 2025 13:23:09 -0300 Subject: [PATCH 09/53] feat: implement existsById method in MemberGateway for member existence check --- .../folder/create/DefaultCreateFolderUseCase.java | 7 +++---- .../java/com/callv2/drive/domain/member/MemberGateway.java | 2 ++ .../drive/infrastructure/member/DefaultMemberGateway.java | 5 +++++ 3 files changed, 10 insertions(+), 4 deletions(-) diff --git a/application/src/main/java/com/callv2/drive/application/folder/create/DefaultCreateFolderUseCase.java b/application/src/main/java/com/callv2/drive/application/folder/create/DefaultCreateFolderUseCase.java index ee4d2598..8d67ff3c 100644 --- a/application/src/main/java/com/callv2/drive/application/folder/create/DefaultCreateFolderUseCase.java +++ b/application/src/main/java/com/callv2/drive/application/folder/create/DefaultCreateFolderUseCase.java @@ -26,9 +26,8 @@ public DefaultCreateFolderUseCase(final MemberGateway memberGateway, final Folde public CreateFolderOutput execute(final CreateFolderInput input) { final MemberID ownerId = MemberID.of(input.ownerId()); - final Member owner = memberGateway - .findById(ownerId) - .orElseThrow(() -> NotFoundException.with(Member.class, input.ownerId().toString())); + if (!memberGateway.existsById(ownerId)) + throw NotFoundException.with(Member.class, input.ownerId().toString()); final Folder parentFolder = folderGateway .findById(FolderID.of(input.parentFolderId())) @@ -36,7 +35,7 @@ public CreateFolderOutput execute(final CreateFolderInput input) { Folder.class, "Parent folder with id %s not found".formatted(input.parentFolderId().toString()))); - return CreateFolderOutput.from(createFolder(owner.getId(), FolderName.of(input.name()), parentFolder)); + return CreateFolderOutput.from(createFolder(ownerId, FolderName.of(input.name()), parentFolder)); } private Folder createFolder(final MemberID ownerId, FolderName name, final Folder parentFolder) { diff --git a/domain/src/main/java/com/callv2/drive/domain/member/MemberGateway.java b/domain/src/main/java/com/callv2/drive/domain/member/MemberGateway.java index ac18ec44..394345e3 100644 --- a/domain/src/main/java/com/callv2/drive/domain/member/MemberGateway.java +++ b/domain/src/main/java/com/callv2/drive/domain/member/MemberGateway.java @@ -15,4 +15,6 @@ public interface MemberGateway { Page findAllQuotaRequests(final SearchQuery searchQuery); + Boolean existsById(MemberID id); + } diff --git a/infrastructure/src/main/java/com/callv2/drive/infrastructure/member/DefaultMemberGateway.java b/infrastructure/src/main/java/com/callv2/drive/infrastructure/member/DefaultMemberGateway.java index 2ce444ff..12a0000f 100644 --- a/infrastructure/src/main/java/com/callv2/drive/infrastructure/member/DefaultMemberGateway.java +++ b/infrastructure/src/main/java/com/callv2/drive/infrastructure/member/DefaultMemberGateway.java @@ -60,4 +60,9 @@ public Page findAllQuotaRequests(SearchQuery searchQuery) { pageResult.toList()); } + @Override + public Boolean existsById(MemberID id) { + return this.memberJpaRepository.existsById(id.getValue()); + } + } From 6ea2d474509be832824f4f64e1fed5b53bb39fd6 Mon Sep 17 00:00:00 2001 From: Matheus Pigatto <1304matheus@gmail.com> Date: Tue, 18 Mar 2025 21:26:43 -0300 Subject: [PATCH 10/53] feat: enhance folder use cases to include owner ID for improved ownership management --- .../get/root/DefaultGetRootFolderUseCase.java | 17 +- .../create/DefaultCreateFileUseCaseTest.java | 582 +++++++++--------- .../get/DefaultGetFileUseCaseTest.java | 5 +- .../move/DefaultMoveFolderUseCaseTest.java | 9 +- .../get/DefaultGetFolderUseCaseTest.java | 9 +- 5 files changed, 323 insertions(+), 299 deletions(-) diff --git a/application/src/main/java/com/callv2/drive/application/folder/retrieve/get/root/DefaultGetRootFolderUseCase.java b/application/src/main/java/com/callv2/drive/application/folder/retrieve/get/root/DefaultGetRootFolderUseCase.java index de352d7e..29af302b 100644 --- a/application/src/main/java/com/callv2/drive/application/folder/retrieve/get/root/DefaultGetRootFolderUseCase.java +++ b/application/src/main/java/com/callv2/drive/application/folder/retrieve/get/root/DefaultGetRootFolderUseCase.java @@ -3,26 +3,39 @@ import java.util.Objects; import java.util.Optional; +import com.callv2.drive.domain.exception.NotFoundException; import com.callv2.drive.domain.file.FileGateway; import com.callv2.drive.domain.folder.Folder; import com.callv2.drive.domain.folder.FolderGateway; +import com.callv2.drive.domain.member.Member; +import com.callv2.drive.domain.member.MemberGateway; +import com.callv2.drive.domain.member.MemberID; public class DefaultGetRootFolderUseCase extends GetRootFolderUseCase { + private final MemberGateway memberGateway; private final FolderGateway folderGateway; private final FileGateway fileGateway; public DefaultGetRootFolderUseCase( + final MemberGateway memberGateway, final FolderGateway folderGateway, final FileGateway fileGateway) { + this.memberGateway = Objects.requireNonNull(memberGateway); this.folderGateway = Objects.requireNonNull(folderGateway); this.fileGateway = Objects.requireNonNull(fileGateway); } @Override - public GetRootFolderOutput execute() { + public GetRootFolderOutput execute(final GetRootFolderInput input) { + + final MemberID owner = MemberID.of(input.ownerId()); + + if (!memberGateway.existsById(owner)) + throw NotFoundException.with(Member.class, owner.getValue().toString()); + final Optional root = folderGateway.findRoot(); - final Folder folder = root.isPresent() ? root.get() : folderGateway.create(Folder.createRoot()); + final Folder folder = root.isPresent() ? root.get() : folderGateway.create(Folder.createRoot(owner)); return GetRootFolderOutput.from(folder, fileGateway.findByFolder(folder.getId())); } diff --git a/application/src/test/java/com/callv2/drive/application/file/create/DefaultCreateFileUseCaseTest.java b/application/src/test/java/com/callv2/drive/application/file/create/DefaultCreateFileUseCaseTest.java index 2d1f7792..95b9f380 100644 --- a/application/src/test/java/com/callv2/drive/application/file/create/DefaultCreateFileUseCaseTest.java +++ b/application/src/test/java/com/callv2/drive/application/file/create/DefaultCreateFileUseCaseTest.java @@ -42,406 +42,406 @@ @ExtendWith(MockitoExtension.class) public class DefaultCreateFileUseCaseTest { - @InjectMocks - DefaultCreateFileUseCase useCase; + @InjectMocks + DefaultCreateFileUseCase useCase; - @Mock - MemberGateway memberGateway; + @Mock + MemberGateway memberGateway; - @Mock - FolderGateway folderGateway; + @Mock + FolderGateway folderGateway; - @Mock - StorageService storageService; + @Mock + StorageService storageService; - @Mock - FileGateway fileGateway; + @Mock + FileGateway fileGateway; - @Test - void givenAValidParams_whenCallsExecute_thenShouldCreateFile() { + @Test + void givenAValidParams_whenCallsExecute_thenShouldCreateFile() { - final var owner = Member.create(MemberID.of("owner")) - .requestQuota(Quota.of(1, QuotaUnit.GIGABYTE)) - .approveQuotaRequest(); + final var owner = Member.create(MemberID.of("owner")) + .requestQuota(Quota.of(1, QuotaUnit.GIGABYTE)) + .approveQuotaRequest(); - final var ownerId = owner.getId(); + final var ownerId = owner.getId(); - final var folder = Folder.createRoot(); - final var expectedFolderId = folder.getId(); + final var folder = Folder.createRoot(ownerId); + final var expectedFolderId = folder.getId(); - final var expectedFileName = FileName.of("file"); - final var expectedContentType = "image/jpeg"; - final var contentBytes = "content".getBytes(); + final var expectedFileName = FileName.of("file"); + final var expectedContentType = "image/jpeg"; + final var contentBytes = "content".getBytes(); - final var expectedContent = new ByteArrayInputStream(contentBytes); - final var expectedContentSize = (long) contentBytes.length; + final var expectedContent = new ByteArrayInputStream(contentBytes); + final var expectedContentSize = (long) contentBytes.length; - when(memberGateway.findById(any())) - .thenReturn(Optional.of(owner)); + when(memberGateway.findById(any())) + .thenReturn(Optional.of(owner)); - when(fileGateway.findByFolder(any())) - .thenReturn(List.of()); + when(fileGateway.findByFolder(any())) + .thenReturn(List.of()); - when(folderGateway.findById(any())) - .thenReturn(Optional.of(folder)); + when(folderGateway.findById(any())) + .thenReturn(Optional.of(folder)); - when(storageService.store(any(), any())) - .then(returnsFirstArg()); + when(storageService.store(any(), any())) + .then(returnsFirstArg()); - when(fileGateway.create(any())) - .thenAnswer(returnsFirstArg()); + when(fileGateway.create(any())) + .thenAnswer(returnsFirstArg()); - final var input = CreateFileInput.of( - ownerId.getValue(), - expectedFolderId.getValue(), - expectedFileName.value(), - expectedContentType, - expectedContent, - expectedContentSize); + final var input = CreateFileInput.of( + ownerId.getValue(), + expectedFolderId.getValue(), + expectedFileName.value(), + expectedContentType, + expectedContent, + expectedContentSize); - final var actualOuptut = useCase.execute(input); + final var actualOuptut = useCase.execute(input); - assertNotNull(actualOuptut.id()); + assertNotNull(actualOuptut.id()); - verify(folderGateway, times(1)).findById(any()); - verify(folderGateway, times(1)).findById(eq(expectedFolderId)); - verify(storageService, times(1)).store(any(), any()); - verify(storageService, times(1)).store(any(), eq(expectedContent)); - verify(storageService, times(0)).delete(any()); - verify(fileGateway, times(1)).findByFolder(any()); - verify(fileGateway, times(1)).findByFolder(eq(folder.getId())); - verify(fileGateway, times(1)).create(any()); - verify(fileGateway, times(1)).create(argThat(file -> { + verify(folderGateway, times(1)).findById(any()); + verify(folderGateway, times(1)).findById(eq(expectedFolderId)); + verify(storageService, times(1)).store(any(), any()); + verify(storageService, times(1)).store(any(), eq(expectedContent)); + verify(storageService, times(0)).delete(any()); + verify(fileGateway, times(1)).findByFolder(any()); + verify(fileGateway, times(1)).findByFolder(eq(folder.getId())); + verify(fileGateway, times(1)).create(any()); + verify(fileGateway, times(1)).create(argThat(file -> { - assertEquals(actualOuptut.id().getValue(), file.getId().getValue()); - assertEquals(expectedFileName, file.getName()); - assertEquals(expectedContentType, file.getContent().type()); - assertNotNull(file.getCreatedAt()); - assertNotNull(file.getUpdatedAt()); - assertNotNull(file.getContent().location()); - assertEquals(file.getCreatedAt(), file.getUpdatedAt()); + assertEquals(actualOuptut.id().getValue(), file.getId().getValue()); + assertEquals(expectedFileName, file.getName()); + assertEquals(expectedContentType, file.getContent().type()); + assertNotNull(file.getCreatedAt()); + assertNotNull(file.getUpdatedAt()); + assertNotNull(file.getContent().location()); + assertEquals(file.getCreatedAt(), file.getUpdatedAt()); - return true; - })); + return true; + })); - } + } - @Test - void givenAnInvalidId_whenCallsExecute_thenShouldThrowNotFoundException() { + @Test + void givenAnInvalidId_whenCallsExecute_thenShouldThrowNotFoundException() { - final var owner = Member.create(MemberID.of("owner")) - .requestQuota(Quota.of(1, QuotaUnit.GIGABYTE)) - .approveQuotaRequest(); + final var owner = Member.create(MemberID.of("owner")) + .requestQuota(Quota.of(1, QuotaUnit.GIGABYTE)) + .approveQuotaRequest(); - final var ownerId = owner.getId(); + final var ownerId = owner.getId(); - final var folder = Folder.createRoot(); + final var folder = Folder.createRoot(ownerId); - final var expectedFolderId = folder.getId(); + final var expectedFolderId = folder.getId(); - final var expectedFileName = FileName.of("file"); - final var expectedContentType = "image/jpeg"; - final var contentBytes = "content".getBytes(); + final var expectedFileName = FileName.of("file"); + final var expectedContentType = "image/jpeg"; + final var contentBytes = "content".getBytes(); - final var expectedContent = new ByteArrayInputStream(contentBytes); - final var expectedContentSize = (long) contentBytes.length; + final var expectedContent = new ByteArrayInputStream(contentBytes); + final var expectedContentSize = (long) contentBytes.length; - final var expectedExceptionMessage = "Folder with id '%s' not found" - .formatted(expectedFolderId.getValue()); + final var expectedExceptionMessage = "Folder with id '%s' not found" + .formatted(expectedFolderId.getValue()); - when(memberGateway.findById(any())) - .thenReturn(Optional.of(owner)); + when(memberGateway.findById(any())) + .thenReturn(Optional.of(owner)); - when(folderGateway.findById(any())) - .thenReturn(Optional.empty()); + when(folderGateway.findById(any())) + .thenReturn(Optional.empty()); - final var input = CreateFileInput.of( - ownerId.getValue(), - expectedFolderId.getValue(), - expectedFileName.value(), - expectedContentType, - expectedContent, - expectedContentSize); + final var input = CreateFileInput.of( + ownerId.getValue(), + expectedFolderId.getValue(), + expectedFileName.value(), + expectedContentType, + expectedContent, + expectedContentSize); - final var actualException = assertThrows(NotFoundException.class, () -> useCase.execute(input)); + final var actualException = assertThrows(NotFoundException.class, () -> useCase.execute(input)); - assertEquals(expectedExceptionMessage, actualException.getMessage()); + assertEquals(expectedExceptionMessage, actualException.getMessage()); - verify(folderGateway, times(1)).findById(any()); - verify(folderGateway, times(1)).findById(eq(expectedFolderId)); - verify(storageService, times(0)).store(any(), any()); - verify(storageService, times(0)).store(any(), eq(expectedContent)); - verify(storageService, times(0)).delete(any()); - verify(fileGateway, times(0)).findByFolder(any()); - verify(fileGateway, times(0)).create(any()); + verify(folderGateway, times(1)).findById(any()); + verify(folderGateway, times(1)).findById(eq(expectedFolderId)); + verify(storageService, times(0)).store(any(), any()); + verify(storageService, times(0)).store(any(), eq(expectedContent)); + verify(storageService, times(0)).delete(any()); + verify(fileGateway, times(0)).findByFolder(any()); + verify(fileGateway, times(0)).create(any()); - } + } - @Test - void givenAValidParamsWithAlreadyExistingFileNameOnSameFolder_whenCallsExecute_thenShouldThrowValidationException() { + @Test + void givenAValidParamsWithAlreadyExistingFileNameOnSameFolder_whenCallsExecute_thenShouldThrowValidationException() { - final var owner = Member.create(MemberID.of("owner")) - .requestQuota(Quota.of(1, QuotaUnit.GIGABYTE)) - .approveQuotaRequest(); + final var owner = Member.create(MemberID.of("owner")) + .requestQuota(Quota.of(1, QuotaUnit.GIGABYTE)) + .approveQuotaRequest(); - final var ownerId = owner.getId(); + final var ownerId = owner.getId(); - final var folder = Folder.createRoot(); + final var folder = Folder.createRoot(ownerId); - final var expectedFolderId = folder.getId(); + final var expectedFolderId = folder.getId(); - final var expectedFileName = FileName.of("file"); - final var expectedContentType = "image/jpeg"; - final var contentBytes = "content".getBytes(); + final var expectedFileName = FileName.of("file"); + final var expectedContentType = "image/jpeg"; + final var contentBytes = "content".getBytes(); - final var expectedContent = new ByteArrayInputStream(contentBytes); - final var expectedContentSize = (long) contentBytes.length; + final var expectedContent = new ByteArrayInputStream(contentBytes); + final var expectedContentSize = (long) contentBytes.length; - final var fileWithSameName = File.create(ownerId, folder.getId(), expectedFileName, - Content.of("location", "text", 10)); + final var fileWithSameName = File.create(ownerId, folder.getId(), expectedFileName, + Content.of("location", "text", 10)); - final var expectedExceptionMessage = "Could not create Aggregate File"; - final var expectedErrorMessage = "File with same name already exists on this folder"; + final var expectedExceptionMessage = "Could not create Aggregate File"; + final var expectedErrorMessage = "File with same name already exists on this folder"; - when(memberGateway.findById(ownerId)) - .thenReturn(Optional.of(owner)); + when(memberGateway.findById(ownerId)) + .thenReturn(Optional.of(owner)); - when(fileGateway.findByFolder(any())) - .thenReturn(List.of(fileWithSameName)); + when(fileGateway.findByFolder(any())) + .thenReturn(List.of(fileWithSameName)); - when(folderGateway.findById(any())) - .thenReturn(Optional.of(folder)); + when(folderGateway.findById(any())) + .thenReturn(Optional.of(folder)); - when(storageService.store(any(), any())) - .then(returnsFirstArg()); + when(storageService.store(any(), any())) + .then(returnsFirstArg()); - final var input = CreateFileInput.of( - ownerId.getValue(), - expectedFolderId.getValue(), - expectedFileName.value(), - expectedContentType, - expectedContent, - expectedContentSize); + final var input = CreateFileInput.of( + ownerId.getValue(), + expectedFolderId.getValue(), + expectedFileName.value(), + expectedContentType, + expectedContent, + expectedContentSize); - final var actualException = assertThrows(ValidationException.class, () -> useCase.execute(input)); + final var actualException = assertThrows(ValidationException.class, () -> useCase.execute(input)); - assertEquals(expectedExceptionMessage, actualException.getMessage()); - assertEquals(expectedErrorMessage, actualException.getErrors().get(0).message()); + assertEquals(expectedExceptionMessage, actualException.getMessage()); + assertEquals(expectedErrorMessage, actualException.getErrors().get(0).message()); - verify(folderGateway, times(1)).findById(any()); - verify(folderGateway, times(1)).findById(eq(expectedFolderId)); - verify(storageService, times(1)).store(any(), any()); - verify(storageService, times(1)).store(any(), eq(expectedContent)); - verify(storageService, times(0)).delete(any()); - verify(fileGateway, times(1)).findByFolder(any()); - verify(fileGateway, times(1)).findByFolder(eq(folder.getId())); - verify(fileGateway, times(0)).create(any()); + verify(folderGateway, times(1)).findById(any()); + verify(folderGateway, times(1)).findById(eq(expectedFolderId)); + verify(storageService, times(1)).store(any(), any()); + verify(storageService, times(1)).store(any(), eq(expectedContent)); + verify(storageService, times(0)).delete(any()); + verify(fileGateway, times(1)).findByFolder(any()); + verify(fileGateway, times(1)).findByFolder(eq(folder.getId())); + verify(fileGateway, times(0)).create(any()); - } + } - @Test - void givenAValidParams_whenCallsExecuteAndFileGatewayCreateThrowsRandomException_thenShouldThrowInternalErrorException() { + @Test + void givenAValidParams_whenCallsExecuteAndFileGatewayCreateThrowsRandomException_thenShouldThrowInternalErrorException() { - final var owner = Member.create(MemberID.of("owner")) - .requestQuota(Quota.of(1, QuotaUnit.GIGABYTE)) - .approveQuotaRequest(); + final var owner = Member.create(MemberID.of("owner")) + .requestQuota(Quota.of(1, QuotaUnit.GIGABYTE)) + .approveQuotaRequest(); - final var ownerId = owner.getId(); + final var ownerId = owner.getId(); - final var folder = Folder.createRoot(); - final var expectedFolderId = folder.getId(); + final var folder = Folder.createRoot(ownerId); + final var expectedFolderId = folder.getId(); - final var expectedFileName = FileName.of("file"); - final var expectedContentType = "image/jpeg"; - final var contentBytes = "content".getBytes(); + final var expectedFileName = FileName.of("file"); + final var expectedContentType = "image/jpeg"; + final var contentBytes = "content".getBytes(); - final var expectedContent = new ByteArrayInputStream(contentBytes); - final var expectedContentSize = (long) contentBytes.length; + final var expectedContent = new ByteArrayInputStream(contentBytes); + final var expectedContentSize = (long) contentBytes.length; - final var expectedExceptionMessage = "Could not store File"; + final var expectedExceptionMessage = "Could not store File"; - when(memberGateway.findById(ownerId)) - .thenReturn(Optional.of(owner)); + when(memberGateway.findById(ownerId)) + .thenReturn(Optional.of(owner)); - when(fileGateway.findByFolder(any())) - .thenReturn(List.of()); + when(fileGateway.findByFolder(any())) + .thenReturn(List.of()); - when(folderGateway.findById(any())) - .thenReturn(Optional.of(folder)); + when(folderGateway.findById(any())) + .thenReturn(Optional.of(folder)); - when(storageService.store(any(), any())) - .then(returnsFirstArg()); + when(storageService.store(any(), any())) + .then(returnsFirstArg()); - when(fileGateway.create(any())) - .thenThrow(new IllegalStateException("FileGateway Exception")); + when(fileGateway.create(any())) + .thenThrow(new IllegalStateException("FileGateway Exception")); - doNothing() - .when(storageService) - .delete(any()); + doNothing() + .when(storageService) + .delete(any()); - final var input = CreateFileInput.of( - ownerId.getValue(), - expectedFolderId.getValue(), - expectedFileName.value(), - expectedContentType, - expectedContent, - expectedContentSize); + final var input = CreateFileInput.of( + ownerId.getValue(), + expectedFolderId.getValue(), + expectedFileName.value(), + expectedContentType, + expectedContent, + expectedContentSize); - final var actualException = assertThrows(InternalErrorException.class, () -> useCase.execute(input)); + final var actualException = assertThrows(InternalErrorException.class, () -> useCase.execute(input)); - assertEquals(expectedExceptionMessage, actualException.getMessage()); - assertEquals("FileGateway Exception", actualException.getCause().getMessage()); + assertEquals(expectedExceptionMessage, actualException.getMessage()); + assertEquals("FileGateway Exception", actualException.getCause().getMessage()); - verify(folderGateway, times(1)).findById(any()); - verify(folderGateway, times(1)).findById(eq(expectedFolderId)); - verify(storageService, times(1)).store(any(), any()); - verify(storageService, times(1)).store(any(), eq(expectedContent)); - verify(storageService, times(1)).delete(any()); - verify(fileGateway, times(1)).findByFolder(any()); - verify(fileGateway, times(1)).findByFolder(eq(folder.getId())); - verify(fileGateway, times(1)).create(any()); - verify(fileGateway, times(1)).create(argThat(file -> { + verify(folderGateway, times(1)).findById(any()); + verify(folderGateway, times(1)).findById(eq(expectedFolderId)); + verify(storageService, times(1)).store(any(), any()); + verify(storageService, times(1)).store(any(), eq(expectedContent)); + verify(storageService, times(1)).delete(any()); + verify(fileGateway, times(1)).findByFolder(any()); + verify(fileGateway, times(1)).findByFolder(eq(folder.getId())); + verify(fileGateway, times(1)).create(any()); + verify(fileGateway, times(1)).create(argThat(file -> { - assertNotNull(file.getId().getValue()); - assertEquals(expectedFileName, file.getName()); - assertEquals(expectedContentType, file.getContent().type()); - assertNotNull(file.getCreatedAt()); - assertNotNull(file.getUpdatedAt()); - assertNotNull(file.getContent().location()); - assertEquals(file.getCreatedAt(), file.getUpdatedAt()); + assertNotNull(file.getId().getValue()); + assertEquals(expectedFileName, file.getName()); + assertEquals(expectedContentType, file.getContent().type()); + assertNotNull(file.getCreatedAt()); + assertNotNull(file.getUpdatedAt()); + assertNotNull(file.getContent().location()); + assertEquals(file.getCreatedAt(), file.getUpdatedAt()); - return true; - })); + return true; + })); - } + } - @Test - void givenAValidParams_whenCallsExecuteAndFileGatewayCreateAndContentGatewayDeleteThrowsRandomException_thenShouldThrowInternalErrorException() { + @Test + void givenAValidParams_whenCallsExecuteAndFileGatewayCreateAndContentGatewayDeleteThrowsRandomException_thenShouldThrowInternalErrorException() { - final var owner = Member.create(MemberID.of("owner")) - .requestQuota(Quota.of(1, QuotaUnit.GIGABYTE)) - .approveQuotaRequest(); + final var owner = Member.create(MemberID.of("owner")) + .requestQuota(Quota.of(1, QuotaUnit.GIGABYTE)) + .approveQuotaRequest(); - final var ownerId = owner.getId(); + final var ownerId = owner.getId(); - final var folder = Folder.createRoot(); - final var expectedFolderId = folder.getId(); + final var folder = Folder.createRoot(ownerId); + final var expectedFolderId = folder.getId(); - final var expectedFileName = FileName.of("file"); - final var expectedContentType = "image/jpeg"; - final var contentBytes = "content".getBytes(); + final var expectedFileName = FileName.of("file"); + final var expectedContentType = "image/jpeg"; + final var contentBytes = "content".getBytes(); - final var expectedContent = new ByteArrayInputStream(contentBytes); - final var expectedContentSize = (long) contentBytes.length; + final var expectedContent = new ByteArrayInputStream(contentBytes); + final var expectedContentSize = (long) contentBytes.length; - final var expectedExceptionMessage = "Could not delete BinaryContent"; + final var expectedExceptionMessage = "Could not delete BinaryContent"; - when(memberGateway.findById(ownerId)) - .thenReturn(Optional.of(owner)); + when(memberGateway.findById(ownerId)) + .thenReturn(Optional.of(owner)); - when(fileGateway.findByFolder(any())) - .thenReturn(List.of()); + when(fileGateway.findByFolder(any())) + .thenReturn(List.of()); - when(folderGateway.findById(any())) - .thenReturn(Optional.of(folder)); + when(folderGateway.findById(any())) + .thenReturn(Optional.of(folder)); - when(storageService.store(any(), any())) - .then(returnsFirstArg()); + when(storageService.store(any(), any())) + .then(returnsFirstArg()); - when(fileGateway.create(any())) - .thenThrow(new IllegalStateException("FileGateway Exception")); + when(fileGateway.create(any())) + .thenThrow(new IllegalStateException("FileGateway Exception")); - doThrow(new IllegalStateException("ContentGateway Exception")) - .when(storageService) - .delete(any()); + doThrow(new IllegalStateException("ContentGateway Exception")) + .when(storageService) + .delete(any()); - final var input = CreateFileInput.of( - ownerId.getValue(), - expectedFolderId.getValue(), - expectedFileName.value(), - expectedContentType, - expectedContent, - expectedContentSize); + final var input = CreateFileInput.of( + ownerId.getValue(), + expectedFolderId.getValue(), + expectedFileName.value(), + expectedContentType, + expectedContent, + expectedContentSize); - final var actualException = assertThrows(InternalErrorException.class, () -> useCase.execute(input)); + final var actualException = assertThrows(InternalErrorException.class, () -> useCase.execute(input)); - assertEquals(expectedExceptionMessage, actualException.getMessage()); - assertEquals("ContentGateway Exception", actualException.getCause().getMessage()); + assertEquals(expectedExceptionMessage, actualException.getMessage()); + assertEquals("ContentGateway Exception", actualException.getCause().getMessage()); - verify(folderGateway, times(1)).findById(any()); - verify(folderGateway, times(1)).findById(eq(expectedFolderId)); - verify(storageService, times(1)).store(any(), any()); - verify(storageService, times(1)).store(any(), eq(expectedContent)); - verify(storageService, times(1)).delete(any()); - verify(fileGateway, times(1)).findByFolder(any()); - verify(fileGateway, times(1)).findByFolder(eq(folder.getId())); - verify(fileGateway, times(1)).create(any()); - verify(fileGateway, times(1)).create(argThat(file -> { + verify(folderGateway, times(1)).findById(any()); + verify(folderGateway, times(1)).findById(eq(expectedFolderId)); + verify(storageService, times(1)).store(any(), any()); + verify(storageService, times(1)).store(any(), eq(expectedContent)); + verify(storageService, times(1)).delete(any()); + verify(fileGateway, times(1)).findByFolder(any()); + verify(fileGateway, times(1)).findByFolder(eq(folder.getId())); + verify(fileGateway, times(1)).create(any()); + verify(fileGateway, times(1)).create(argThat(file -> { - assertNotNull(file.getId().getValue()); - assertEquals(expectedFileName, file.getName()); - assertEquals(expectedContentType, file.getContent().type()); - assertNotNull(file.getCreatedAt()); - assertNotNull(file.getUpdatedAt()); - assertNotNull(file.getContent().location()); - assertEquals(file.getCreatedAt(), file.getUpdatedAt()); + assertNotNull(file.getId().getValue()); + assertEquals(expectedFileName, file.getName()); + assertEquals(expectedContentType, file.getContent().type()); + assertNotNull(file.getCreatedAt()); + assertNotNull(file.getUpdatedAt()); + assertNotNull(file.getContent().location()); + assertEquals(file.getCreatedAt(), file.getUpdatedAt()); - return true; - })); + return true; + })); - } + } - @Test - void givenAValidParams_whenCallsExecuteAndContentGatewayStoreThrowsRandomException_thenShouldThrowInternalErrorException() { + @Test + void givenAValidParams_whenCallsExecuteAndContentGatewayStoreThrowsRandomException_thenShouldThrowInternalErrorException() { - final var owner = Member.create(MemberID.of("owner")) - .requestQuota(Quota.of(1, QuotaUnit.GIGABYTE)) - .approveQuotaRequest(); + final var owner = Member.create(MemberID.of("owner")) + .requestQuota(Quota.of(1, QuotaUnit.GIGABYTE)) + .approveQuotaRequest(); - final var ownerId = owner.getId(); + final var ownerId = owner.getId(); - final var folder = Folder.createRoot(); - final var expectedFolderId = folder.getId(); + final var folder = Folder.createRoot(ownerId); + final var expectedFolderId = folder.getId(); - final var expectedFileName = FileName.of("file"); - final var expectedContentType = "image/jpeg"; - final var contentBytes = "content".getBytes(); + final var expectedFileName = FileName.of("file"); + final var expectedContentType = "image/jpeg"; + final var contentBytes = "content".getBytes(); - final var expectedContent = new ByteArrayInputStream(contentBytes); - final var expectedContentSize = (long) contentBytes.length; + final var expectedContent = new ByteArrayInputStream(contentBytes); + final var expectedContentSize = (long) contentBytes.length; - final var expectedExceptionMessage = "Could not store BinaryContent"; + final var expectedExceptionMessage = "Could not store BinaryContent"; - when(memberGateway.findById(ownerId)) - .thenReturn(Optional.of(owner)); + when(memberGateway.findById(ownerId)) + .thenReturn(Optional.of(owner)); - when(folderGateway.findById(any())) - .thenReturn(Optional.of(folder)); + when(folderGateway.findById(any())) + .thenReturn(Optional.of(folder)); - when(storageService.store(any(), any())) - .thenThrow(new IllegalStateException("ContentGateway Exception")); + when(storageService.store(any(), any())) + .thenThrow(new IllegalStateException("ContentGateway Exception")); - final var input = CreateFileInput.of( - ownerId.getValue(), - expectedFolderId.getValue(), - expectedFileName.value(), - expectedContentType, - expectedContent, - expectedContentSize); + final var input = CreateFileInput.of( + ownerId.getValue(), + expectedFolderId.getValue(), + expectedFileName.value(), + expectedContentType, + expectedContent, + expectedContentSize); - final var actualException = assertThrows(InternalErrorException.class, () -> useCase.execute(input)); + final var actualException = assertThrows(InternalErrorException.class, () -> useCase.execute(input)); - assertEquals(expectedExceptionMessage, actualException.getMessage()); - assertEquals("ContentGateway Exception", actualException.getCause().getMessage()); + assertEquals(expectedExceptionMessage, actualException.getMessage()); + assertEquals("ContentGateway Exception", actualException.getCause().getMessage()); - verify(folderGateway, times(1)).findById(any()); - verify(folderGateway, times(1)).findById(eq(expectedFolderId)); - verify(storageService, times(1)).store(any(), any()); - verify(storageService, times(1)).store(any(), eq(expectedContent)); - verify(storageService, times(0)).delete(any()); - verify(fileGateway, times(0)).findByFolder(any()); - verify(fileGateway, times(0)).create(any()); + verify(folderGateway, times(1)).findById(any()); + verify(folderGateway, times(1)).findById(eq(expectedFolderId)); + verify(storageService, times(1)).store(any(), any()); + verify(storageService, times(1)).store(any(), eq(expectedContent)); + verify(storageService, times(0)).delete(any()); + verify(fileGateway, times(0)).findByFolder(any()); + verify(fileGateway, times(0)).create(any()); - } + } } diff --git a/application/src/test/java/com/callv2/drive/application/file/retrieve/get/DefaultGetFileUseCaseTest.java b/application/src/test/java/com/callv2/drive/application/file/retrieve/get/DefaultGetFileUseCaseTest.java index de3a5171..6f93cbd9 100644 --- a/application/src/test/java/com/callv2/drive/application/file/retrieve/get/DefaultGetFileUseCaseTest.java +++ b/application/src/test/java/com/callv2/drive/application/file/retrieve/get/DefaultGetFileUseCaseTest.java @@ -40,7 +40,7 @@ void givenAValidId_whenCallsExecute_thenShouldReturnFile() { final var ownerId = MemberID.of("owner"); - final var expectedFolder = Folder.createRoot(); + final var expectedFolder = Folder.createRoot(ownerId); final var expectedName = "file.jpeg"; @@ -50,7 +50,8 @@ void givenAValidId_whenCallsExecute_thenShouldReturnFile() { final var expectedContent = Content.of(expectedContentLocation, expectedContentType, expectedContentSize); - final var expectedFile = File.create(ownerId, expectedFolder.getId(), FileName.of(expectedName), expectedContent); + final var expectedFile = File.create(ownerId, expectedFolder.getId(), FileName.of(expectedName), + expectedContent); final var expectedId = expectedFile.getId(); diff --git a/application/src/test/java/com/callv2/drive/application/folder/move/DefaultMoveFolderUseCaseTest.java b/application/src/test/java/com/callv2/drive/application/folder/move/DefaultMoveFolderUseCaseTest.java index 802d719f..e079f46e 100644 --- a/application/src/test/java/com/callv2/drive/application/folder/move/DefaultMoveFolderUseCaseTest.java +++ b/application/src/test/java/com/callv2/drive/application/folder/move/DefaultMoveFolderUseCaseTest.java @@ -17,6 +17,7 @@ import com.callv2.drive.domain.folder.Folder; import com.callv2.drive.domain.folder.FolderGateway; import com.callv2.drive.domain.folder.FolderName; +import com.callv2.drive.domain.member.MemberID; @ExtendWith(MockitoExtension.class) public class DefaultMoveFolderUseCaseTest { @@ -30,10 +31,12 @@ public class DefaultMoveFolderUseCaseTest { @Test void givenVAlidInput_whenCallsExecute_thenMoveFolder() { - final var expectedRootFolder = Folder.createRoot(); + final var ownerId = MemberID.of("owner"); - final var expectedFolderToMove = Folder.create(FolderName.of("folder1"), expectedRootFolder); - final var expectedFolderTarget = Folder.create(FolderName.of("folder2"), expectedRootFolder); + final var expectedRootFolder = Folder.createRoot(ownerId); + + final var expectedFolderToMove = Folder.create(ownerId, FolderName.of("folder1"), expectedRootFolder); + final var expectedFolderTarget = Folder.create(ownerId, FolderName.of("folder2"), expectedRootFolder); when(folderGateway.findById(expectedFolderToMove.getId())) .thenReturn(Optional.of(expectedFolderToMove)); diff --git a/application/src/test/java/com/callv2/drive/application/folder/retrieve/get/DefaultGetFolderUseCaseTest.java b/application/src/test/java/com/callv2/drive/application/folder/retrieve/get/DefaultGetFolderUseCaseTest.java index 768418d7..68e524fc 100644 --- a/application/src/test/java/com/callv2/drive/application/folder/retrieve/get/DefaultGetFolderUseCaseTest.java +++ b/application/src/test/java/com/callv2/drive/application/folder/retrieve/get/DefaultGetFolderUseCaseTest.java @@ -23,6 +23,7 @@ import com.callv2.drive.domain.folder.FolderGateway; import com.callv2.drive.domain.folder.FolderID; import com.callv2.drive.domain.folder.FolderName; +import com.callv2.drive.domain.member.MemberID; @ExtendWith(MockitoExtension.class) public class DefaultGetFolderUseCaseTest { @@ -39,8 +40,14 @@ public class DefaultGetFolderUseCaseTest { @Test void givenAValidFolderId_whenCallsExecute_thenShouldReturnFolder() { + final var ownerId = MemberID.of("owner"); + final var expectedFolderName = "folder"; - final var expectedFolder = Folder.create(FolderName.of(expectedFolderName), Folder.createRoot()); + final var expectedFolder = Folder.create( + ownerId, + FolderName.of(expectedFolderName), + Folder.createRoot(ownerId)); + final var expectedFolderId = expectedFolder.getId(); final var expectedSubFolders = expectedFolder.getSubFolders(); final var expectedCreatedAt = expectedFolder.getCreatedAt(); From 97ce7ce06fb08d86112be410b243812a34f7f382 Mon Sep 17 00:00:00 2001 From: Matheus Pigatto <1304matheus@gmail.com> Date: Tue, 18 Mar 2025 21:27:16 -0300 Subject: [PATCH 11/53] feat: implement GetRootFolder use case with owner ID for improved folder retrieval --- .../retrieve/get/root/GetRootFolderInput.java | 8 +++++++ .../get/root/GetRootFolderUseCase.java | 4 ++-- .../callv2/drive/domain/folder/Folder.java | 6 ++--- .../callv2/drive/domain/file/FileTest.java | 24 ++++++++++--------- .../api/controller/FolderController.java | 9 ++++--- .../usecase/FolderUseCaseConfig.java | 2 +- .../folder/persistence/FolderJpaEntity.java | 7 +++--- 7 files changed, 36 insertions(+), 24 deletions(-) create mode 100644 application/src/main/java/com/callv2/drive/application/folder/retrieve/get/root/GetRootFolderInput.java diff --git a/application/src/main/java/com/callv2/drive/application/folder/retrieve/get/root/GetRootFolderInput.java b/application/src/main/java/com/callv2/drive/application/folder/retrieve/get/root/GetRootFolderInput.java new file mode 100644 index 00000000..28e9bf78 --- /dev/null +++ b/application/src/main/java/com/callv2/drive/application/folder/retrieve/get/root/GetRootFolderInput.java @@ -0,0 +1,8 @@ +package com.callv2.drive.application.folder.retrieve.get.root; +public record GetRootFolderInput(String ownerId) { + + public static GetRootFolderInput from(final String ownerId) { + return new GetRootFolderInput(ownerId); + } + +} diff --git a/application/src/main/java/com/callv2/drive/application/folder/retrieve/get/root/GetRootFolderUseCase.java b/application/src/main/java/com/callv2/drive/application/folder/retrieve/get/root/GetRootFolderUseCase.java index dcefcdb4..33e76f12 100644 --- a/application/src/main/java/com/callv2/drive/application/folder/retrieve/get/root/GetRootFolderUseCase.java +++ b/application/src/main/java/com/callv2/drive/application/folder/retrieve/get/root/GetRootFolderUseCase.java @@ -1,7 +1,7 @@ package com.callv2.drive.application.folder.retrieve.get.root; -import com.callv2.drive.application.NullaryUseCase; +import com.callv2.drive.application.UseCase; -public abstract class GetRootFolderUseCase extends NullaryUseCase { +public abstract class GetRootFolderUseCase extends UseCase { } diff --git a/domain/src/main/java/com/callv2/drive/domain/folder/Folder.java b/domain/src/main/java/com/callv2/drive/domain/folder/Folder.java index 17e00ee5..39a42fda 100644 --- a/domain/src/main/java/com/callv2/drive/domain/folder/Folder.java +++ b/domain/src/main/java/com/callv2/drive/domain/folder/Folder.java @@ -62,12 +62,12 @@ public static Folder with( return new Folder(id, owner, name, parentFolder, subFolders, createdAt, updatedAt, deletedAt, rootFolder); } - public static Folder createRoot() { + public static Folder createRoot(final MemberID owner) { Instant now = Instant.now(); return Folder.with( FolderID.unique(), - null, // When root folder is created, there is no owner + owner, FolderName.of("Root"), null, new HashSet<>(), @@ -78,7 +78,7 @@ public static Folder createRoot() { } public static Folder create( - final MemberID owner, + final MemberID owner, final FolderName name, final Folder parentFolder) { diff --git a/domain/src/test/java/com/callv2/drive/domain/file/FileTest.java b/domain/src/test/java/com/callv2/drive/domain/file/FileTest.java index 7c89ee2e..ae1f9414 100644 --- a/domain/src/test/java/com/callv2/drive/domain/file/FileTest.java +++ b/domain/src/test/java/com/callv2/drive/domain/file/FileTest.java @@ -17,8 +17,10 @@ public class FileTest { @Test void givenAValidParams_whenCallsCreate_thenShouldCreateFile() { + final var ownerId = MemberID.of("owner"); + final var expectedOwner = MemberID.of("owner"); - final var expectedFolder = Folder.createRoot().getId(); + final var expectedFolder = Folder.createRoot(ownerId).getId(); final var expectedName = "file"; @@ -44,7 +46,7 @@ void givenAValidParams_whenCallsCreate_thenShouldCreateFile() { void givenEmptyName_whenCallsCreate_thenShouldThrowsValidationException() { final var expectedOwner = MemberID.of("owner"); - final var expectedFolder = Folder.createRoot().getId(); + final var expectedFolder = Folder.createRoot(expectedOwner).getId(); final var expectedName = ""; @@ -70,7 +72,7 @@ void givenEmptyName_whenCallsCreate_thenShouldThrowsValidationException() { void givenNullName_whenCallsCreate_thenShouldThrowsValidationException() { final var expectedOwner = MemberID.of("owner"); - final var expectedFolder = Folder.createRoot().getId(); + final var expectedFolder = Folder.createRoot(expectedOwner).getId(); final String expectedName = null; @@ -100,7 +102,7 @@ void givenNullName_whenCallsCreate_thenShouldThrowsValidationException() { void givenNameWithMoreThan64Chars_whenCallsCreate_thenShouldThrowsValidationException() { final var expectedOwner = MemberID.of("owner"); - final var expectedFolder = Folder.createRoot().getId(); + final var expectedFolder = Folder.createRoot(expectedOwner).getId(); final var expectedName = """ filefilefilefilefilefilefilefilefilefilefilefilefilefilefilefilefilefile @@ -133,7 +135,7 @@ void givenNameWithMoreThan64Chars_whenCallsCreate_thenShouldThrowsValidationExce void givenReservedName_whenCallsCreate_thenShouldThrowsValidationException() { final var expectedOwner = MemberID.of("owner"); - final var expectedFolder = Folder.createRoot().getId(); + final var expectedFolder = Folder.createRoot(expectedOwner).getId(); final var expectedName = "nul"; @@ -164,7 +166,7 @@ void givenReservedName_whenCallsCreate_thenShouldThrowsValidationException() { void givenMultipleInvalidParams_whenCallsCreate_thenShouldThrowsValidationExceptionWithMultipleErrors() { final var expectedOwner = MemberID.of("owner"); - final var expectedFolder = Folder.createRoot().getId(); + final var expectedFolder = Folder.createRoot(expectedOwner).getId(); final String expectedName = "nul"; @@ -196,7 +198,7 @@ void givenMultipleInvalidParams_whenCallsCreate_thenShouldThrowsValidationExcept void givenAValidParams_whenCallsUpdate_thenShouldCreateFile() { final var expectedOwner = MemberID.of("owner"); - final var expectedFolder = Folder.createRoot().getId(); + final var expectedFolder = Folder.createRoot(expectedOwner).getId(); final var expectedName = FileName.of("File"); @@ -227,7 +229,7 @@ void givenAValidParams_whenCallsUpdate_thenShouldCreateFile() { void givenEmptyName_whenCallsUpdate_thenShouldThrowsValidationException() { final var expectedOwner = MemberID.of("owner"); - final var expectedFolder = Folder.createRoot().getId(); + final var expectedFolder = Folder.createRoot(expectedOwner).getId(); final var expectedName = ""; @@ -254,7 +256,7 @@ void givenEmptyName_whenCallsUpdate_thenShouldThrowsValidationException() { void givenNullName_whenCallsUpdate_thenShouldThrowsValidationException() { final var expectedOwner = MemberID.of("owner"); - final var expectedFolder = Folder.createRoot().getId(); + final var expectedFolder = Folder.createRoot(expectedOwner).getId(); final String expectedName = null; @@ -282,7 +284,7 @@ void givenNullName_whenCallsUpdate_thenShouldThrowsValidationException() { void givenNameWithMoreThan64Chars_whenCallsUpdate_thenShouldThrowsValidationException() { final var expectedOwner = MemberID.of("owner"); - final var expectedFolder = Folder.createRoot().getId(); + final var expectedFolder = Folder.createRoot(expectedOwner).getId(); final var expectedName = """ filefilefilefilefilefilefilefilefilefilefilefilefilefilefilefilefilefile @@ -312,7 +314,7 @@ void givenNameWithMoreThan64Chars_whenCallsUpdate_thenShouldThrowsValidationExce void givenReservedName_whenCallsUpdate_thenShouldThrowsValidationException() { final var expectedOwner = MemberID.of("owner"); - final var expectedFolder = Folder.createRoot().getId(); + final var expectedFolder = Folder.createRoot(expectedOwner).getId(); final var expectedName = "nul"; diff --git a/infrastructure/src/main/java/com/callv2/drive/infrastructure/api/controller/FolderController.java b/infrastructure/src/main/java/com/callv2/drive/infrastructure/api/controller/FolderController.java index 856c1aa6..1a13f32e 100644 --- a/infrastructure/src/main/java/com/callv2/drive/infrastructure/api/controller/FolderController.java +++ b/infrastructure/src/main/java/com/callv2/drive/infrastructure/api/controller/FolderController.java @@ -11,6 +11,7 @@ import com.callv2.drive.application.folder.move.MoveFolderInput; import com.callv2.drive.application.folder.move.MoveFolderUseCase; import com.callv2.drive.application.folder.retrieve.get.GetFolderUseCase; +import com.callv2.drive.application.folder.retrieve.get.root.GetRootFolderInput; import com.callv2.drive.application.folder.retrieve.get.root.GetRootFolderUseCase; import com.callv2.drive.application.folder.retrieve.list.ListFoldersUseCase; import com.callv2.drive.domain.pagination.Filter; @@ -53,14 +54,16 @@ public FolderController( @Override public ResponseEntity getRoot() { - return ResponseEntity.ok(FolderPresenter.present(getRootFolderUseCase.execute())); + return ResponseEntity.ok(FolderPresenter.present( + getRootFolderUseCase.execute(GetRootFolderInput.from(SecurityContext.getAuthenticatedUser())))); } @Override public ResponseEntity create(final CreateFolderRequest request) { final String ownerId = SecurityContext.getAuthenticatedUser(); - final var response = FolderPresenter.present(createFolderUseCase.execute(FolderAdapter.adapt(request, ownerId))); - + final var response = FolderPresenter + .present(createFolderUseCase.execute(FolderAdapter.adapt(request, ownerId))); + return ResponseEntity .created(URI.create("/folders/" + response.id())) .body(response); diff --git a/infrastructure/src/main/java/com/callv2/drive/infrastructure/configuration/usecase/FolderUseCaseConfig.java b/infrastructure/src/main/java/com/callv2/drive/infrastructure/configuration/usecase/FolderUseCaseConfig.java index 4814f14d..960229c1 100644 --- a/infrastructure/src/main/java/com/callv2/drive/infrastructure/configuration/usecase/FolderUseCaseConfig.java +++ b/infrastructure/src/main/java/com/callv2/drive/infrastructure/configuration/usecase/FolderUseCaseConfig.java @@ -35,7 +35,7 @@ public FolderUseCaseConfig( @Bean GetRootFolderUseCase getRootFolderUseCase() { - return new DefaultGetRootFolderUseCase(folderGateway, fileGateway); + return new DefaultGetRootFolderUseCase(memberGateway, folderGateway, fileGateway); } @Bean diff --git a/infrastructure/src/main/java/com/callv2/drive/infrastructure/folder/persistence/FolderJpaEntity.java b/infrastructure/src/main/java/com/callv2/drive/infrastructure/folder/persistence/FolderJpaEntity.java index 4fa71477..53eb4d6d 100644 --- a/infrastructure/src/main/java/com/callv2/drive/infrastructure/folder/persistence/FolderJpaEntity.java +++ b/infrastructure/src/main/java/com/callv2/drive/infrastructure/folder/persistence/FolderJpaEntity.java @@ -32,7 +32,7 @@ public class FolderJpaEntity { @Column(name = "name", nullable = false) private String name; - @Column(name = "owner_id") + @Column(name = "owner_id", nullable = false) private String ownerId; @Column(name = "parent_folder_id") @@ -76,13 +76,12 @@ public FolderJpaEntity() { public static FolderJpaEntity fromDomain(final Folder folder) { final UUID parentFolderId = folder.getParentFolder() == null ? null : folder.getParentFolder().getValue(); - final String ownerId = folder.getOwner() == null ? null : folder.getOwner().getValue(); final var entity = new FolderJpaEntity( folder.getId().getValue(), folder.isRootFolder(), folder.getName().value(), - ownerId, + folder.getOwner().getValue(), parentFolderId, folder.getCreatedAt(), folder.getUpdatedAt(), @@ -97,7 +96,7 @@ public static FolderJpaEntity fromDomain(final Folder folder) { public Folder toDomain() { return Folder.with( FolderID.of(id), - ownerId != null ? MemberID.of(ownerId) : null, + MemberID.of(ownerId), FolderName.of(name), FolderID.of(parentFolderId), subFolders.stream() From 86e59487a1b5f50fdac0f0a0662419c638d26224 Mon Sep 17 00:00:00 2001 From: jhonatapers Date: Sun, 23 Mar 2025 17:24:23 -0300 Subject: [PATCH 12/53] chore: remove version declaration from build.gradle Co-authored-by: Matheus Martins Pigatto --- infrastructure/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/infrastructure/build.gradle b/infrastructure/build.gradle index 4db40c4e..a2ed8f6a 100644 --- a/infrastructure/build.gradle +++ b/infrastructure/build.gradle @@ -6,7 +6,7 @@ plugins { } group = 'com.callv2' -version = '1.0-SNAPSHOT' + repositories { mavenCentral() From 7eacd27022adfbe3382d57d596be9ef174e91d7c Mon Sep 17 00:00:00 2001 From: jhonatapers Date: Sun, 23 Mar 2025 17:24:42 -0300 Subject: [PATCH 13/53] chore: set destination directory for build artifacts in build.gradle Co-authored-by: Matheus Martins Pigatto --- infrastructure/build.gradle | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/infrastructure/build.gradle b/infrastructure/build.gradle index a2ed8f6a..94e67b65 100644 --- a/infrastructure/build.gradle +++ b/infrastructure/build.gradle @@ -7,6 +7,10 @@ plugins { group = 'com.callv2' +bootJar { + archiveFileName = 'application.jar' + destinationDirectory = file("${rootProject.buildDir}/libs") +} repositories { mavenCentral() From 14f04ee03913999b926de0df6825b6e3d01b7de3 Mon Sep 17 00:00:00 2001 From: jhonatapers Date: Sun, 23 Mar 2025 17:24:59 -0300 Subject: [PATCH 14/53] chore: remove client configuration for user-api from application-local.yml Co-authored-by: Matheus Martins Pigatto --- infrastructure/src/main/resources/application-local.yml | 4 ---- 1 file changed, 4 deletions(-) diff --git a/infrastructure/src/main/resources/application-local.yml b/infrastructure/src/main/resources/application-local.yml index 144ca9e0..a3d66db9 100644 --- a/infrastructure/src/main/resources/application-local.yml +++ b/infrastructure/src/main/resources/application-local.yml @@ -1,10 +1,6 @@ keycloak: realm: callv2 host: http://localhost:8090 - client: - user-api: - client-id: user-api - client-secret: local-client-secret postgres: host: localhost From 3e3ed8f8f81b6b595e0df7560362b8c72a610e94 Mon Sep 17 00:00:00 2001 From: jhonatapers Date: Sun, 23 Mar 2025 17:25:07 -0300 Subject: [PATCH 15/53] feat: add application-hmg.yml for environment-specific configuration Co-authored-by: Matheus Martins Pigatto --- .../src/main/resources/application-hmg.yml | 14 ++++++++++++++ 1 file changed, 14 insertions(+) create mode 100644 infrastructure/src/main/resources/application-hmg.yml diff --git a/infrastructure/src/main/resources/application-hmg.yml b/infrastructure/src/main/resources/application-hmg.yml new file mode 100644 index 00000000..902c7b9c --- /dev/null +++ b/infrastructure/src/main/resources/application-hmg.yml @@ -0,0 +1,14 @@ +keycloak: + realm: ${KEYCLOAK_REALM} + host: ${KEYCLOAK_HOST} + +postgres: + host: ${POSTGRES_HOST} + port: ${POSTGRES_PORT} + database: ${POSTGRES_DATABASE} + username: ${POSTGRES_USERNAME} + password: ${POSTGRES_PASSWORD} + +storage: + file-system: + location: ${STORAGE_LOCATION} \ No newline at end of file From a0cef09db959edd3bd9672e745f7929598c09559 Mon Sep 17 00:00:00 2001 From: jhonatapers Date: Sun, 23 Mar 2025 17:26:10 -0300 Subject: [PATCH 16/53] feat: add Dockerfile and .dockerignore for containerization Co-authored-by: Matheus Martins Pigatto --- .dockerignore | 12 ++++++++++++ Dockerfile | 29 +++++++++++++++++++++++++++++ 2 files changed, 41 insertions(+) create mode 100644 .dockerignore create mode 100644 Dockerfile diff --git a/.dockerignore b/.dockerignore new file mode 100644 index 00000000..8d639cf3 --- /dev/null +++ b/.dockerignore @@ -0,0 +1,12 @@ +.git +.github +.gradle +**/build +**/out +**/target +.idea +.vscode +**/*.iml +**/*.ipr +**/*.iws +**/.DS_Store \ No newline at end of file diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 00000000..cd3dfb94 --- /dev/null +++ b/Dockerfile @@ -0,0 +1,29 @@ +FROM amazoncorretto:21-alpine AS builder + +WORKDIR /app + +COPY . . + +RUN ./gradlew clean :infrastructure:bootJar --no-daemon + +FROM amazoncorretto:21-alpine + +WORKDIR /app + +RUN mkdir -p /app/storage && chmod -R 777 /app/storage + +COPY --from=builder /app/build/libs/application.jar app.jar + +ENV SPRING_PROFILES_ACTIVE=local +ENV KEYCLOAK_REALM=callv2 +ENV KEYCLOAK_HOST=http://localhost:8090 +ENV POSTGRES_HOST=localhost +ENV POSTGRES_PORT=5432 +ENV POSTGRES_DATABASE=callv2 +ENV POSTGRES_USERNAME=callv2 +ENV POSTGRES_PASSWORD=callv2 +ENV STORAGE_LOCATION=/app/storage + +EXPOSE 8080 + +ENTRYPOINT ["sh", "-c", "java -jar /app/app.jar --spring.profiles.active=${SPRING_PROFILES_ACTIVE}"] \ No newline at end of file From 1d1e2a3dc6017e6a6de52a268f338b349f1b88b6 Mon Sep 17 00:00:00 2001 From: jhonatapers Date: Tue, 13 May 2025 20:39:56 -0300 Subject: [PATCH 17/53] fix: update gradlew file permissions to executable --- gradlew | 0 1 file changed, 0 insertions(+), 0 deletions(-) mode change 100644 => 100755 gradlew diff --git a/gradlew b/gradlew old mode 100644 new mode 100755 From 143dbd19d7eea4bfe16a8ba62a10a33edf7d3e33 Mon Sep 17 00:00:00 2001 From: jhonatapers Date: Tue, 13 May 2025 20:48:58 -0300 Subject: [PATCH 18/53] feat: add GitHub Actions workflows for Docker image build and push; update application configuration files --- .../workflows/build-and-push-docker-image.yml | 42 ++++++++++++++++++ .github/workflows/build-trigger.yml | 44 ------------------- .github/workflows/latest.yml | 16 +++++++ .github/workflows/nightly.yaml | 16 +++++++ .github/workflows/release.yml | 15 +++++++ .../src/main/resources/application-dev.yml | 21 +++++++++ .../src/main/resources/application-hmg.yml | 14 ------ .../src/main/resources/application-local.yml | 3 ++ .../src/main/resources/application.yml | 2 +- 9 files changed, 114 insertions(+), 59 deletions(-) create mode 100644 .github/workflows/build-and-push-docker-image.yml delete mode 100644 .github/workflows/build-trigger.yml create mode 100644 .github/workflows/latest.yml create mode 100644 .github/workflows/nightly.yaml create mode 100644 .github/workflows/release.yml create mode 100644 infrastructure/src/main/resources/application-dev.yml delete mode 100644 infrastructure/src/main/resources/application-hmg.yml diff --git a/.github/workflows/build-and-push-docker-image.yml b/.github/workflows/build-and-push-docker-image.yml new file mode 100644 index 00000000..8fb0ef57 --- /dev/null +++ b/.github/workflows/build-and-push-docker-image.yml @@ -0,0 +1,42 @@ +name: build-and-push-docker-image + +on: + workflow_call: + inputs: + imageName: + required: true + type: string + imageTag: + required: true + type: string + secrets: + DOCKERHUB_USERNAME: + required: true + DOCKERHUB_TOKEN: + required: true + +jobs: + docker: + runs-on: [self-hosted, Linux, X64] + steps: + + - name: Checkout + uses: actions/checkout@v4 + + - name: Login to Docker Hub + uses: docker/login-action@v3 + with: + username: ${{ secrets.DOCKERHUB_USERNAME }} + password: ${{ secrets.DOCKERHUB_TOKEN }} + + - name: Set up QEMU + uses: docker/setup-qemu-action@v3 + + - name: Set up Docker Buildx + uses: docker/setup-buildx-action@v3 + + - name: Build and push + uses: docker/build-push-action@v6 + with: + push: true + tags: ${{ secrets.DOCKERHUB_USERNAME }}/${{ inputs.imageName }}:${{ inputs.imageTag }} \ No newline at end of file diff --git a/.github/workflows/build-trigger.yml b/.github/workflows/build-trigger.yml deleted file mode 100644 index 44d401d3..00000000 --- a/.github/workflows/build-trigger.yml +++ /dev/null @@ -1,44 +0,0 @@ -name: Backend CI/CD - -on: - push: - branches: [ main, master ] - workflow_dispatch: - -jobs: - build-and-test: - name: Build and Test Backend - runs-on: ubuntu-latest - steps: - - name: Checkout code - uses: actions/checkout@v4 - - - name: Set up JDK 11 - uses: actions/setup-java@v2 - with: - distribution: 'adopt' - java-version: '11' - - - name: Build with Gradle - run: ./gradlew build - - - name: Run tests - run: ./gradlew test - - - name: Build and push Docker image - env: - DOCKER_USERNAME: ${{ secrets.DOCKER_USERNAME }} - DOCKER_PASSWORD: ${{ secrets.DOCKER_PASSWORD }} - run: | - echo $DOCKER_PASSWORD | docker login -u $DOCKER_USERNAME --password-stdin - docker build -t your-org/backend:latest . - docker push your-org/backend:latest - - # Trigger infrastructure deployment - - name: Trigger infrastructure deployment - uses: peter-evans/repository-dispatch@v2 - with: - token: ${{ secrets.REPO_ACCESS_TOKEN }} - repository: your-org/infrastructure - event-type: backend-updated - client-payload: '{"ref": "${{ github.ref }}", "sha": "${{ github.sha }}"}' \ No newline at end of file diff --git a/.github/workflows/latest.yml b/.github/workflows/latest.yml new file mode 100644 index 00000000..7298decc --- /dev/null +++ b/.github/workflows/latest.yml @@ -0,0 +1,16 @@ +name: Release build & push to Docker Hub + +on: + push: + branches: + - main + +jobs: + release-build-push: + uses: ./.github/workflows/build-and-push-docker-image.yml + with: + imageName: drive-api + imageTag: latest + secrets: + DOCKERHUB_USERNAME: ${{ secrets.DOCKERHUB_USERNAME }} + DOCKERHUB_TOKEN: ${{ secrets.DOCKERHUB_TOKEN }} \ No newline at end of file diff --git a/.github/workflows/nightly.yaml b/.github/workflows/nightly.yaml new file mode 100644 index 00000000..d9cf34e5 --- /dev/null +++ b/.github/workflows/nightly.yaml @@ -0,0 +1,16 @@ +name: Develop nightly build & push to Docker Hub + +on: + push: + branches: + - develop + +jobs: + nightly-build-push: + uses: ./.github/workflows/build-and-push-docker-image.yml + with: + imageName: drive-api + imageTag: nightly + secrets: + DOCKERHUB_USERNAME: ${{ secrets.DOCKERHUB_USERNAME }} + DOCKERHUB_TOKEN: ${{ secrets.DOCKERHUB_TOKEN }} \ No newline at end of file diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml new file mode 100644 index 00000000..ff7b3d05 --- /dev/null +++ b/.github/workflows/release.yml @@ -0,0 +1,15 @@ +name: Release build & push to Docker Hub + +on: + release: + types: [published] + +jobs: + release-build-push: + uses: ./.github/workflows/build-and-push-docker-image.yml + with: + imageName: drive-api + imageTag: ${{ github.event.release.tag_name }} + secrets: + DOCKERHUB_USERNAME: ${{ secrets.DOCKERHUB_USERNAME }} + DOCKERHUB_TOKEN: ${{ secrets.DOCKERHUB_TOKEN }} \ No newline at end of file diff --git a/infrastructure/src/main/resources/application-dev.yml b/infrastructure/src/main/resources/application-dev.yml new file mode 100644 index 00000000..615c1b85 --- /dev/null +++ b/infrastructure/src/main/resources/application-dev.yml @@ -0,0 +1,21 @@ +server: + port: ${SERVER_PORT} + +keycloak: + realm: callv2 + host: ${KEYCLOAK_HOST} + client: + user-api: + client-id: ${KEYCLOAK_CLIENT_ID} + client-secret: ${KEYCLOAK_CLIENT_SECRET} + +postgres: + host: ${POSTGRES_HOST} + port: ${POSTGRES_PORT} + database: drive + username: ${POSTGRES_USER} + password: ${POSTGRES_PASSWORD} + +storage: + file-system: + location: ${STORAGE_LOCATION} \ No newline at end of file diff --git a/infrastructure/src/main/resources/application-hmg.yml b/infrastructure/src/main/resources/application-hmg.yml deleted file mode 100644 index 902c7b9c..00000000 --- a/infrastructure/src/main/resources/application-hmg.yml +++ /dev/null @@ -1,14 +0,0 @@ -keycloak: - realm: ${KEYCLOAK_REALM} - host: ${KEYCLOAK_HOST} - -postgres: - host: ${POSTGRES_HOST} - port: ${POSTGRES_PORT} - database: ${POSTGRES_DATABASE} - username: ${POSTGRES_USERNAME} - password: ${POSTGRES_PASSWORD} - -storage: - file-system: - location: ${STORAGE_LOCATION} \ No newline at end of file diff --git a/infrastructure/src/main/resources/application-local.yml b/infrastructure/src/main/resources/application-local.yml index a3d66db9..db006c14 100644 --- a/infrastructure/src/main/resources/application-local.yml +++ b/infrastructure/src/main/resources/application-local.yml @@ -1,3 +1,6 @@ +server: + port: 8080 + keycloak: realm: callv2 host: http://localhost:8090 diff --git a/infrastructure/src/main/resources/application.yml b/infrastructure/src/main/resources/application.yml index ef1e152b..eff8f608 100644 --- a/infrastructure/src/main/resources/application.yml +++ b/infrastructure/src/main/resources/application.yml @@ -1,5 +1,5 @@ server: - port: 8080 + port: 80 servlet: context-path: /api compression: From cb363a051563483f6ac79e4f476d3041348c217e Mon Sep 17 00:00:00 2001 From: jhonatapers Date: Tue, 13 May 2025 22:06:19 -0300 Subject: [PATCH 19/53] fix: fix application configuration files for production and local environments --- .../src/main/resources/application-dev.yml | 7 +++++ .../src/main/resources/application-local.yml | 13 ++++++++- .../src/main/resources/application-prd.yml | 28 +++++++++++++++++++ .../src/main/resources/application.yml | 10 ++++--- 4 files changed, 53 insertions(+), 5 deletions(-) create mode 100644 infrastructure/src/main/resources/application-prd.yml diff --git a/infrastructure/src/main/resources/application-dev.yml b/infrastructure/src/main/resources/application-dev.yml index 615c1b85..40ceab16 100644 --- a/infrastructure/src/main/resources/application-dev.yml +++ b/infrastructure/src/main/resources/application-dev.yml @@ -16,6 +16,13 @@ postgres: username: ${POSTGRES_USER} password: ${POSTGRES_PASSWORD} +db: + ddl-auto: ${DB_DDL_AUTO} + +reuqest-timeout: ${REQUEST_TIMEOUT} + storage: + max-file-size: ${MAX_FILE_SIZE} + max-request-size: ${MAX_REQUEST_SIZE} file-system: location: ${STORAGE_LOCATION} \ No newline at end of file diff --git a/infrastructure/src/main/resources/application-local.yml b/infrastructure/src/main/resources/application-local.yml index db006c14..3382e121 100644 --- a/infrastructure/src/main/resources/application-local.yml +++ b/infrastructure/src/main/resources/application-local.yml @@ -4,6 +4,10 @@ server: keycloak: realm: callv2 host: http://localhost:8090 + client: + user-api: + client-id: user-api + client-secret: local-client-secret postgres: host: localhost @@ -12,6 +16,13 @@ postgres: username: postgres password: postgres +db: + ddl-auto: none + +reuqest-timeout: 600000 + storage: + max-file-size: 1024MB + max-request-size: 1024MB file-system: - location: /home/jhonatapers/callv2/storage \ No newline at end of file + location: / \ No newline at end of file diff --git a/infrastructure/src/main/resources/application-prd.yml b/infrastructure/src/main/resources/application-prd.yml new file mode 100644 index 00000000..03447535 --- /dev/null +++ b/infrastructure/src/main/resources/application-prd.yml @@ -0,0 +1,28 @@ +server: + port: ${SERVER_PORT} + +keycloak: + realm: ${KEYCLOAK_REALM} + host: ${KEYCLOAK_HOST} + client: + user-api: + client-id: ${KEYCLOAK_CLIENT_ID} + client-secret: ${KEYCLOAK_CLIENT_SECRET} + +postgres: + host: ${POSTGRES_HOST} + port: ${POSTGRES_PORT} + database: ${POSTGRES_DATABASE} + username: ${POSTGRES_USER} + password: ${POSTGRES_PASSWORD} + +db: + ddl-auto: none + +reuqest-timeout: ${REQUEST_TIMEOUT} + +storage: + max-file-size: ${MAX_FILE_SIZE} + max-request-size: ${MAX_REQUEST_SIZE} + file-system: + location: ${STORAGE_LOCATION} \ No newline at end of file diff --git a/infrastructure/src/main/resources/application.yml b/infrastructure/src/main/resources/application.yml index eff8f608..b1b22075 100644 --- a/infrastructure/src/main/resources/application.yml +++ b/infrastructure/src/main/resources/application.yml @@ -8,14 +8,16 @@ server: min-response-size: 1024 spring: + profiles: + active: ${SPRING_PROFILES_ACTIVE:prd} mvc: async: - request-timeout: 600000 + request-timeout: ${reuqest-timeout:30000} servlet: multipart: enabled: true - max-file-size: 10240MB - max-request-size: 10240MB + max-file-size: ${storage.max-file-size:10240MB} + max-request-size: ${storage.max-request-size:10240MB} security: oauth2: resourceserver: @@ -29,5 +31,5 @@ spring: url: jdbc:postgresql://${postgres.host}:${postgres.port}/${postgres.database} jpa: hibernate: - ddl-auto: none + ddl-auto: ${db.ddl-auto} show-sql: true \ No newline at end of file From bf27a5e427503e52d7556bfd051069c597dc9f44 Mon Sep 17 00:00:00 2001 From: jhonatapers Date: Thu, 15 May 2025 15:57:17 -0300 Subject: [PATCH 20/53] feat: implement execution and error context records; add logging aspects for error and execution handling Co-authored-by: Artur S --- infrastructure/build.gradle | 9 +++ .../aop/context/ExecutionContext.java | 23 +++++++ .../aop/context/ExecutionErrorContext.java | 19 ++++++ .../aspects/chain/error/AspectErrorChain.java | 65 ++++++++++++++++++ .../chain/execution/AspectExecutionChain.java | 68 +++++++++++++++++++ .../context/aspects/error/LogErrorAspect.java | 41 +++++++++++ .../aspects/execution/LogExecutionAspect.java | 42 ++++++++++++ 7 files changed, 267 insertions(+) create mode 100644 infrastructure/src/main/java/com/callv2/drive/infrastructure/aop/context/ExecutionContext.java create mode 100644 infrastructure/src/main/java/com/callv2/drive/infrastructure/aop/context/ExecutionErrorContext.java create mode 100644 infrastructure/src/main/java/com/callv2/drive/infrastructure/aop/context/aspects/chain/error/AspectErrorChain.java create mode 100644 infrastructure/src/main/java/com/callv2/drive/infrastructure/aop/context/aspects/chain/execution/AspectExecutionChain.java create mode 100644 infrastructure/src/main/java/com/callv2/drive/infrastructure/aop/context/aspects/error/LogErrorAspect.java create mode 100644 infrastructure/src/main/java/com/callv2/drive/infrastructure/aop/context/aspects/execution/LogExecutionAspect.java diff --git a/infrastructure/build.gradle b/infrastructure/build.gradle index 94e67b65..dfa4109d 100644 --- a/infrastructure/build.gradle +++ b/infrastructure/build.gradle @@ -43,6 +43,15 @@ dependencies { implementation 'com.h2database:h2:2.3.232' implementation libs.guava + + implementation 'org.springframework.boot:spring-boot-starter-log4j2' + +} + +configurations { + all { + exclude group: 'org.springframework.boot', module: 'spring-boot-starter-logging' + } } java { diff --git a/infrastructure/src/main/java/com/callv2/drive/infrastructure/aop/context/ExecutionContext.java b/infrastructure/src/main/java/com/callv2/drive/infrastructure/aop/context/ExecutionContext.java new file mode 100644 index 00000000..3df96a0d --- /dev/null +++ b/infrastructure/src/main/java/com/callv2/drive/infrastructure/aop/context/ExecutionContext.java @@ -0,0 +1,23 @@ +package com.callv2.drive.infrastructure.aop.context; + +import java.time.Instant; + +import org.aspectj.lang.JoinPoint; + +public record ExecutionContext(JoinPoint joinPoint, Instant startTime) { + + public ExecutionContext { + + if (joinPoint == null) + throw new IllegalArgumentException("JoinPoint cannot be null"); + + if (startTime == null) + throw new IllegalArgumentException("Start time cannot be null"); + + } + + public static ExecutionContext of(JoinPoint joinPoint) { + return new ExecutionContext(joinPoint, Instant.now()); + } + +} diff --git a/infrastructure/src/main/java/com/callv2/drive/infrastructure/aop/context/ExecutionErrorContext.java b/infrastructure/src/main/java/com/callv2/drive/infrastructure/aop/context/ExecutionErrorContext.java new file mode 100644 index 00000000..950d51a3 --- /dev/null +++ b/infrastructure/src/main/java/com/callv2/drive/infrastructure/aop/context/ExecutionErrorContext.java @@ -0,0 +1,19 @@ +package com.callv2.drive.infrastructure.aop.context; + +import java.time.Instant; + +public record ExecutionErrorContext(ExecutionContext executionContext, Throwable error, Instant occuredAt) { + + public ExecutionErrorContext { + if (executionContext == null) + throw new IllegalArgumentException("ExecutionContext cannot be null"); + + if (error == null) + throw new IllegalArgumentException("Error cannot be null"); + } + + public static ExecutionErrorContext of(ExecutionContext executionContext, Throwable error) { + return new ExecutionErrorContext(executionContext, error, Instant.now()); + } + +} diff --git a/infrastructure/src/main/java/com/callv2/drive/infrastructure/aop/context/aspects/chain/error/AspectErrorChain.java b/infrastructure/src/main/java/com/callv2/drive/infrastructure/aop/context/aspects/chain/error/AspectErrorChain.java new file mode 100644 index 00000000..60ec236c --- /dev/null +++ b/infrastructure/src/main/java/com/callv2/drive/infrastructure/aop/context/aspects/chain/error/AspectErrorChain.java @@ -0,0 +1,65 @@ +package com.callv2.drive.infrastructure.aop.context.aspects.chain.error; + +import java.util.ArrayDeque; +import java.util.Queue; + +import com.callv2.drive.infrastructure.aop.context.ExecutionErrorContext; + +public abstract class AspectErrorChain { + + private AspectErrorChain next; + + protected AspectErrorChain() { + } + + public final Throwable proceed(final ExecutionErrorContext context) throws Throwable { + + execute(context); + + if (next != null) + return next.proceed(context); + + return context.error(); + + } + + protected abstract void execute(final ExecutionErrorContext context) throws Throwable; + + private AspectErrorChain setNext(final AspectErrorChain next) { + return this.next = next; + } + + public static final class Builder { + + private final Queue chains; + + private Builder() { + this.chains = new ArrayDeque<>(); + } + + public static Builder create() { + return new Builder(); + } + + public Builder add(final AspectErrorChain chain) { + this.chains.add(chain); + return this; + } + + public AspectErrorChain build() { + if (chains.isEmpty()) + return null; + + final var firstChain = chains.poll(); + var chain = firstChain; + + do { + chain = chain.setNext(chains.poll()); + } while (!chains.isEmpty()); + + return firstChain; + } + + } + +} diff --git a/infrastructure/src/main/java/com/callv2/drive/infrastructure/aop/context/aspects/chain/execution/AspectExecutionChain.java b/infrastructure/src/main/java/com/callv2/drive/infrastructure/aop/context/aspects/chain/execution/AspectExecutionChain.java new file mode 100644 index 00000000..189012dd --- /dev/null +++ b/infrastructure/src/main/java/com/callv2/drive/infrastructure/aop/context/aspects/chain/execution/AspectExecutionChain.java @@ -0,0 +1,68 @@ +package com.callv2.drive.infrastructure.aop.context.aspects.chain.execution; + +import java.util.ArrayDeque; +import java.util.Queue; + +import org.aspectj.lang.ProceedingJoinPoint; + +import com.callv2.drive.infrastructure.aop.context.ExecutionContext; + +public abstract class AspectExecutionChain { + + private AspectExecutionChain next; + + protected AspectExecutionChain() { + } + + public final Object proceed(final ExecutionContext context) throws Throwable { + execute(context); + + if (next != null) + return next.proceed(context); + + if (context.joinPoint() instanceof ProceedingJoinPoint) + return ((ProceedingJoinPoint) context.joinPoint()).proceed(); + + return null; + } + + protected abstract void execute(final ExecutionContext context) throws Throwable; + + private AspectExecutionChain setNext(final AspectExecutionChain next) { + return this.next = next; + } + + public static final class Builder { + + private final Queue chains; + + private Builder() { + this.chains = new ArrayDeque<>(); + } + + public static Builder create() { + return new Builder(); + } + + public Builder add(final AspectExecutionChain chain) { + this.chains.add(chain); + return this; + } + + public AspectExecutionChain build() { + if (chains.isEmpty()) + return null; + + final var firstChain = chains.poll(); + var chain = firstChain; + + do { + chain = chain.setNext(chains.poll()); + } while (!chains.isEmpty()); + + return firstChain; + } + + } + +} diff --git a/infrastructure/src/main/java/com/callv2/drive/infrastructure/aop/context/aspects/error/LogErrorAspect.java b/infrastructure/src/main/java/com/callv2/drive/infrastructure/aop/context/aspects/error/LogErrorAspect.java new file mode 100644 index 00000000..92c4fd0b --- /dev/null +++ b/infrastructure/src/main/java/com/callv2/drive/infrastructure/aop/context/aspects/error/LogErrorAspect.java @@ -0,0 +1,41 @@ +package com.callv2.drive.infrastructure.aop.context.aspects.error; + +import org.apache.logging.log4j.Level; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; +import org.apache.logging.log4j.message.Message; + +import com.callv2.drive.infrastructure.aop.context.aspects.chain.error.AspectErrorChain; + +public abstract class LogErrorAspect extends AspectErrorChain { + + private final Logger logger; + + private final Level logLevel; + + public LogErrorAspect(final Level logLevel, Class clazz) { + super(); + this.logLevel = logLevel; + this.logger = LogManager.getLogger(clazz); + } + + protected Level level() { + return logLevel; + } + + public Logger logger() { + return logger; + } + + protected Message createMessage(final String message) { + return logger.getMessageFactory().newMessage(message); + } + + protected Message createMessage(final Object message) { + return logger.getMessageFactory().newMessage(message); + } + + protected Message createMessage(final String message, final Object... params) { + return logger.getMessageFactory().newMessage(message, params); + } +} diff --git a/infrastructure/src/main/java/com/callv2/drive/infrastructure/aop/context/aspects/execution/LogExecutionAspect.java b/infrastructure/src/main/java/com/callv2/drive/infrastructure/aop/context/aspects/execution/LogExecutionAspect.java new file mode 100644 index 00000000..752b19a5 --- /dev/null +++ b/infrastructure/src/main/java/com/callv2/drive/infrastructure/aop/context/aspects/execution/LogExecutionAspect.java @@ -0,0 +1,42 @@ +package com.callv2.drive.infrastructure.aop.context.aspects.execution; + +import org.apache.logging.log4j.Level; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; +import org.apache.logging.log4j.message.Message; + +import com.callv2.drive.infrastructure.aop.context.aspects.chain.execution.AspectExecutionChain; + +public abstract class LogExecutionAspect extends AspectExecutionChain { + + private final Logger logger; + + private final Level logLevel; + + public LogExecutionAspect(final Level logLevel, Class clazz) { + super(); + this.logLevel = logLevel; + this.logger = LogManager.getLogger(clazz); + } + + protected Level level() { + return logLevel; + } + + public Logger logger() { + return logger; + } + + protected Message createMessage(final String message) { + return logger.getMessageFactory().newMessage(message); + } + + protected Message createMessage(final Object message) { + return logger.getMessageFactory().newMessage(message); + } + + protected Message createMessage(final String message, final Object... params) { + return logger.getMessageFactory().newMessage(message, params); + } + +} From a3f16b1d182537d888d278e899cc65d85cde283d Mon Sep 17 00:00:00 2001 From: jhonatapers Date: Thu, 15 May 2025 15:59:14 -0300 Subject: [PATCH 21/53] feat: add ArgsLogAspect and SignatureLogAspect for enhanced logging of method arguments and signatures Co-authored-by: Artur S --- .../aspects/execution/ArgsLogAspect.java | 19 +++++++++++++++++++ .../aspects/execution/SignatureLogAspect.java | 19 +++++++++++++++++++ 2 files changed, 38 insertions(+) create mode 100644 infrastructure/src/main/java/com/callv2/drive/infrastructure/aop/context/aspects/execution/ArgsLogAspect.java create mode 100644 infrastructure/src/main/java/com/callv2/drive/infrastructure/aop/context/aspects/execution/SignatureLogAspect.java diff --git a/infrastructure/src/main/java/com/callv2/drive/infrastructure/aop/context/aspects/execution/ArgsLogAspect.java b/infrastructure/src/main/java/com/callv2/drive/infrastructure/aop/context/aspects/execution/ArgsLogAspect.java new file mode 100644 index 00000000..5e19b58d --- /dev/null +++ b/infrastructure/src/main/java/com/callv2/drive/infrastructure/aop/context/aspects/execution/ArgsLogAspect.java @@ -0,0 +1,19 @@ +package com.callv2.drive.infrastructure.aop.context.aspects.execution; + +import org.apache.logging.log4j.Level; + +import com.callv2.drive.infrastructure.aop.context.ExecutionContext; + +public class ArgsLogAspect extends LogExecutionAspect { + + public ArgsLogAspect(final Level logLevel) { + super(logLevel, ArgsLogAspect.class); + } + + @Override + protected void execute(final ExecutionContext context) throws Throwable { + for (Object arg : context.joinPoint().getArgs()) + logger().log(level(), createMessage("Arg: " + (arg != null ? arg.toString() : "null"))); + } + +} diff --git a/infrastructure/src/main/java/com/callv2/drive/infrastructure/aop/context/aspects/execution/SignatureLogAspect.java b/infrastructure/src/main/java/com/callv2/drive/infrastructure/aop/context/aspects/execution/SignatureLogAspect.java new file mode 100644 index 00000000..98ece0e1 --- /dev/null +++ b/infrastructure/src/main/java/com/callv2/drive/infrastructure/aop/context/aspects/execution/SignatureLogAspect.java @@ -0,0 +1,19 @@ +package com.callv2.drive.infrastructure.aop.context.aspects.execution; + +import org.apache.logging.log4j.Level; + +import com.callv2.drive.infrastructure.aop.context.ExecutionContext; + +public class SignatureLogAspect extends LogExecutionAspect { + + public SignatureLogAspect(final Level logLevel) { + super(logLevel, SignatureLogAspect.class); + } + + @Override + protected void execute(final ExecutionContext context) throws Throwable { + logger().log(level(), + createMessage("SignatureLogAspect: " + context.joinPoint().getSignature().toLongString())); + } + +} From ecd42b66cf3748248cec8e344b655102a4a9746d Mon Sep 17 00:00:00 2001 From: jhonatapers Date: Thu, 15 May 2025 15:59:57 -0300 Subject: [PATCH 22/53] feat: add StackTraceLogAspect for enhanced error logging Co-authored-by: Artur S --- .../aspects/error/StackTraceLogAspect.java | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) create mode 100644 infrastructure/src/main/java/com/callv2/drive/infrastructure/aop/context/aspects/error/StackTraceLogAspect.java diff --git a/infrastructure/src/main/java/com/callv2/drive/infrastructure/aop/context/aspects/error/StackTraceLogAspect.java b/infrastructure/src/main/java/com/callv2/drive/infrastructure/aop/context/aspects/error/StackTraceLogAspect.java new file mode 100644 index 00000000..6e5d4c21 --- /dev/null +++ b/infrastructure/src/main/java/com/callv2/drive/infrastructure/aop/context/aspects/error/StackTraceLogAspect.java @@ -0,0 +1,18 @@ +package com.callv2.drive.infrastructure.aop.context.aspects.error; + +import org.apache.logging.log4j.Level; + +import com.callv2.drive.infrastructure.aop.context.ExecutionErrorContext; + +public class StackTraceLogAspect extends LogErrorAspect { + + public StackTraceLogAspect(final Level logLevel) { + super(logLevel, StackTraceLogAspect.class); + } + + @Override + protected void execute(final ExecutionErrorContext context) throws Throwable { + logger().error(createMessage("StackTraceLogAspect: " + context.error().getMessage()), context.error()); + } + +} From 8bd1420029c161e8d73f16a7261145142a240970 Mon Sep 17 00:00:00 2001 From: jhonatapers Date: Thu, 15 May 2025 16:46:37 -0300 Subject: [PATCH 23/53] feat: implement logging aspects for execution and error handling; add context records for before and after execution --- .../chain/AspectAfterExecutionChain.java | 63 +++++++++++++++++++ .../chain/AspectBeforeExecutionChain.java} | 22 +++---- .../chain/AspectExecutionErrorChain.java} | 18 +++--- .../context/AfterExecutionContext.java | 26 ++++++++ .../context/BeforeExecutionContext.java | 23 +++++++ .../context/ExecutionErrorContext.java | 6 +- .../after/LogAfterExecutionAspect.java} | 8 +-- .../after/TelemetryAfterLogAspect.java | 27 ++++++++ .../execution/before}/ArgsLogAspect.java | 8 +-- .../before/LogBeforeExecutionAspect.java | 42 +++++++++++++ .../execution/before}/SignatureLogAspect.java | 8 +-- .../before/TelemetryBeforeLogAspect.java | 25 ++++++++ .../error/LogExecutionErrorAspect.java} | 8 +-- .../execution}/error/StackTraceLogAspect.java | 6 +- .../aop/context/ExecutionContext.java | 23 ------- 15 files changed, 248 insertions(+), 65 deletions(-) create mode 100644 infrastructure/src/main/java/com/callv2/drive/infrastructure/aop/aspects/chain/AspectAfterExecutionChain.java rename infrastructure/src/main/java/com/callv2/drive/infrastructure/aop/{context/aspects/chain/execution/AspectExecutionChain.java => aspects/chain/AspectBeforeExecutionChain.java} (57%) rename infrastructure/src/main/java/com/callv2/drive/infrastructure/aop/{context/aspects/chain/error/AspectErrorChain.java => aspects/chain/AspectExecutionErrorChain.java} (65%) create mode 100644 infrastructure/src/main/java/com/callv2/drive/infrastructure/aop/aspects/context/AfterExecutionContext.java create mode 100644 infrastructure/src/main/java/com/callv2/drive/infrastructure/aop/aspects/context/BeforeExecutionContext.java rename infrastructure/src/main/java/com/callv2/drive/infrastructure/aop/{ => aspects}/context/ExecutionErrorContext.java (57%) rename infrastructure/src/main/java/com/callv2/drive/infrastructure/aop/{context/aspects/execution/LogExecutionAspect.java => aspects/execution/after/LogAfterExecutionAspect.java} (74%) create mode 100644 infrastructure/src/main/java/com/callv2/drive/infrastructure/aop/aspects/execution/after/TelemetryAfterLogAspect.java rename infrastructure/src/main/java/com/callv2/drive/infrastructure/aop/{context/aspects/execution => aspects/execution/before}/ArgsLogAspect.java (52%) create mode 100644 infrastructure/src/main/java/com/callv2/drive/infrastructure/aop/aspects/execution/before/LogBeforeExecutionAspect.java rename infrastructure/src/main/java/com/callv2/drive/infrastructure/aop/{context/aspects/execution => aspects/execution/before}/SignatureLogAspect.java (50%) create mode 100644 infrastructure/src/main/java/com/callv2/drive/infrastructure/aop/aspects/execution/before/TelemetryBeforeLogAspect.java rename infrastructure/src/main/java/com/callv2/drive/infrastructure/aop/{context/aspects/error/LogErrorAspect.java => aspects/execution/error/LogExecutionErrorAspect.java} (74%) rename infrastructure/src/main/java/com/callv2/drive/infrastructure/aop/{context/aspects => aspects/execution}/error/StackTraceLogAspect.java (63%) delete mode 100644 infrastructure/src/main/java/com/callv2/drive/infrastructure/aop/context/ExecutionContext.java diff --git a/infrastructure/src/main/java/com/callv2/drive/infrastructure/aop/aspects/chain/AspectAfterExecutionChain.java b/infrastructure/src/main/java/com/callv2/drive/infrastructure/aop/aspects/chain/AspectAfterExecutionChain.java new file mode 100644 index 00000000..e0198830 --- /dev/null +++ b/infrastructure/src/main/java/com/callv2/drive/infrastructure/aop/aspects/chain/AspectAfterExecutionChain.java @@ -0,0 +1,63 @@ +package com.callv2.drive.infrastructure.aop.aspects.chain; + +import java.util.ArrayDeque; +import java.util.Queue; + +import com.callv2.drive.infrastructure.aop.aspects.context.AfterExecutionContext; + +public abstract class AspectAfterExecutionChain { + + private AspectAfterExecutionChain next; + + protected AspectAfterExecutionChain() { + } + + public final Object proceed(final AfterExecutionContext context) throws Throwable { + execute(context); + + if (next != null) + return next.proceed(context); + + return context.result(); + } + + protected abstract void execute(final AfterExecutionContext context) throws Throwable; + + private AspectAfterExecutionChain setNext(final AspectAfterExecutionChain next) { + return this.next = next; + } + + public static final class Builder { + + private final Queue chains; + + private Builder() { + this.chains = new ArrayDeque<>(); + } + + public static Builder create() { + return new Builder(); + } + + public Builder add(final AspectAfterExecutionChain chain) { + this.chains.add(chain); + return this; + } + + public AspectAfterExecutionChain build() { + if (chains.isEmpty()) + return null; + + final var firstChain = chains.poll(); + var chain = firstChain; + + do { + chain = chain.setNext(chains.poll()); + } while (!chains.isEmpty()); + + return firstChain; + } + + } + +} diff --git a/infrastructure/src/main/java/com/callv2/drive/infrastructure/aop/context/aspects/chain/execution/AspectExecutionChain.java b/infrastructure/src/main/java/com/callv2/drive/infrastructure/aop/aspects/chain/AspectBeforeExecutionChain.java similarity index 57% rename from infrastructure/src/main/java/com/callv2/drive/infrastructure/aop/context/aspects/chain/execution/AspectExecutionChain.java rename to infrastructure/src/main/java/com/callv2/drive/infrastructure/aop/aspects/chain/AspectBeforeExecutionChain.java index 189012dd..624dcd11 100644 --- a/infrastructure/src/main/java/com/callv2/drive/infrastructure/aop/context/aspects/chain/execution/AspectExecutionChain.java +++ b/infrastructure/src/main/java/com/callv2/drive/infrastructure/aop/aspects/chain/AspectBeforeExecutionChain.java @@ -1,20 +1,20 @@ -package com.callv2.drive.infrastructure.aop.context.aspects.chain.execution; +package com.callv2.drive.infrastructure.aop.aspects.chain; import java.util.ArrayDeque; import java.util.Queue; import org.aspectj.lang.ProceedingJoinPoint; -import com.callv2.drive.infrastructure.aop.context.ExecutionContext; +import com.callv2.drive.infrastructure.aop.aspects.context.BeforeExecutionContext; -public abstract class AspectExecutionChain { +public abstract class AspectBeforeExecutionChain { - private AspectExecutionChain next; + private AspectBeforeExecutionChain next; - protected AspectExecutionChain() { + protected AspectBeforeExecutionChain() { } - public final Object proceed(final ExecutionContext context) throws Throwable { + public final Object proceed(final BeforeExecutionContext context) throws Throwable { execute(context); if (next != null) @@ -26,15 +26,15 @@ public final Object proceed(final ExecutionContext context) throws Throwable { return null; } - protected abstract void execute(final ExecutionContext context) throws Throwable; + protected abstract void execute(final BeforeExecutionContext context) throws Throwable; - private AspectExecutionChain setNext(final AspectExecutionChain next) { + private AspectBeforeExecutionChain setNext(final AspectBeforeExecutionChain next) { return this.next = next; } public static final class Builder { - private final Queue chains; + private final Queue chains; private Builder() { this.chains = new ArrayDeque<>(); @@ -44,12 +44,12 @@ public static Builder create() { return new Builder(); } - public Builder add(final AspectExecutionChain chain) { + public Builder add(final AspectBeforeExecutionChain chain) { this.chains.add(chain); return this; } - public AspectExecutionChain build() { + public AspectBeforeExecutionChain build() { if (chains.isEmpty()) return null; diff --git a/infrastructure/src/main/java/com/callv2/drive/infrastructure/aop/context/aspects/chain/error/AspectErrorChain.java b/infrastructure/src/main/java/com/callv2/drive/infrastructure/aop/aspects/chain/AspectExecutionErrorChain.java similarity index 65% rename from infrastructure/src/main/java/com/callv2/drive/infrastructure/aop/context/aspects/chain/error/AspectErrorChain.java rename to infrastructure/src/main/java/com/callv2/drive/infrastructure/aop/aspects/chain/AspectExecutionErrorChain.java index 60ec236c..977afb5c 100644 --- a/infrastructure/src/main/java/com/callv2/drive/infrastructure/aop/context/aspects/chain/error/AspectErrorChain.java +++ b/infrastructure/src/main/java/com/callv2/drive/infrastructure/aop/aspects/chain/AspectExecutionErrorChain.java @@ -1,15 +1,15 @@ -package com.callv2.drive.infrastructure.aop.context.aspects.chain.error; +package com.callv2.drive.infrastructure.aop.aspects.chain; import java.util.ArrayDeque; import java.util.Queue; -import com.callv2.drive.infrastructure.aop.context.ExecutionErrorContext; +import com.callv2.drive.infrastructure.aop.aspects.context.ExecutionErrorContext; -public abstract class AspectErrorChain { +public abstract class AspectExecutionErrorChain { - private AspectErrorChain next; + private AspectExecutionErrorChain next; - protected AspectErrorChain() { + protected AspectExecutionErrorChain() { } public final Throwable proceed(final ExecutionErrorContext context) throws Throwable { @@ -25,13 +25,13 @@ public final Throwable proceed(final ExecutionErrorContext context) throws Throw protected abstract void execute(final ExecutionErrorContext context) throws Throwable; - private AspectErrorChain setNext(final AspectErrorChain next) { + private AspectExecutionErrorChain setNext(final AspectExecutionErrorChain next) { return this.next = next; } public static final class Builder { - private final Queue chains; + private final Queue chains; private Builder() { this.chains = new ArrayDeque<>(); @@ -41,12 +41,12 @@ public static Builder create() { return new Builder(); } - public Builder add(final AspectErrorChain chain) { + public Builder add(final AspectExecutionErrorChain chain) { this.chains.add(chain); return this; } - public AspectErrorChain build() { + public AspectExecutionErrorChain build() { if (chains.isEmpty()) return null; diff --git a/infrastructure/src/main/java/com/callv2/drive/infrastructure/aop/aspects/context/AfterExecutionContext.java b/infrastructure/src/main/java/com/callv2/drive/infrastructure/aop/aspects/context/AfterExecutionContext.java new file mode 100644 index 00000000..5a87bf4f --- /dev/null +++ b/infrastructure/src/main/java/com/callv2/drive/infrastructure/aop/aspects/context/AfterExecutionContext.java @@ -0,0 +1,26 @@ +package com.callv2.drive.infrastructure.aop.aspects.context; + +import java.time.Instant; + +import org.aspectj.lang.JoinPoint; + +public record AfterExecutionContext(Object result, JoinPoint joinPoint, Instant startedAt, Instant finishedAt) { + + public AfterExecutionContext { + + if (joinPoint == null) + throw new IllegalArgumentException("JoinPoint cannot be null"); + + if (startedAt == null) + throw new IllegalArgumentException("StartedAt time cannot be null"); + + if (finishedAt == null) + throw new IllegalArgumentException("FinishedAt time cannot be null"); + + } + + public static AfterExecutionContext of(final Object result, final BeforeExecutionContext beforeContext) { + return new AfterExecutionContext(result, beforeContext.joinPoint(), beforeContext.startTime(), Instant.now()); + } + +} diff --git a/infrastructure/src/main/java/com/callv2/drive/infrastructure/aop/aspects/context/BeforeExecutionContext.java b/infrastructure/src/main/java/com/callv2/drive/infrastructure/aop/aspects/context/BeforeExecutionContext.java new file mode 100644 index 00000000..3d31218e --- /dev/null +++ b/infrastructure/src/main/java/com/callv2/drive/infrastructure/aop/aspects/context/BeforeExecutionContext.java @@ -0,0 +1,23 @@ +package com.callv2.drive.infrastructure.aop.aspects.context; + +import java.time.Instant; + +import org.aspectj.lang.JoinPoint; + +public record BeforeExecutionContext(JoinPoint joinPoint, Instant startTime) { + + public BeforeExecutionContext { + + if (joinPoint == null) + throw new IllegalArgumentException("JoinPoint cannot be null"); + + if (startTime == null) + throw new IllegalArgumentException("Start time cannot be null"); + + } + + public static BeforeExecutionContext of(JoinPoint joinPoint) { + return new BeforeExecutionContext(joinPoint, Instant.now()); + } + +} diff --git a/infrastructure/src/main/java/com/callv2/drive/infrastructure/aop/context/ExecutionErrorContext.java b/infrastructure/src/main/java/com/callv2/drive/infrastructure/aop/aspects/context/ExecutionErrorContext.java similarity index 57% rename from infrastructure/src/main/java/com/callv2/drive/infrastructure/aop/context/ExecutionErrorContext.java rename to infrastructure/src/main/java/com/callv2/drive/infrastructure/aop/aspects/context/ExecutionErrorContext.java index 950d51a3..6e14ba74 100644 --- a/infrastructure/src/main/java/com/callv2/drive/infrastructure/aop/context/ExecutionErrorContext.java +++ b/infrastructure/src/main/java/com/callv2/drive/infrastructure/aop/aspects/context/ExecutionErrorContext.java @@ -1,8 +1,8 @@ -package com.callv2.drive.infrastructure.aop.context; +package com.callv2.drive.infrastructure.aop.aspects.context; import java.time.Instant; -public record ExecutionErrorContext(ExecutionContext executionContext, Throwable error, Instant occuredAt) { +public record ExecutionErrorContext(BeforeExecutionContext executionContext, Throwable error, Instant occuredAt) { public ExecutionErrorContext { if (executionContext == null) @@ -12,7 +12,7 @@ public record ExecutionErrorContext(ExecutionContext executionContext, Throwable throw new IllegalArgumentException("Error cannot be null"); } - public static ExecutionErrorContext of(ExecutionContext executionContext, Throwable error) { + public static ExecutionErrorContext of(BeforeExecutionContext executionContext, Throwable error) { return new ExecutionErrorContext(executionContext, error, Instant.now()); } diff --git a/infrastructure/src/main/java/com/callv2/drive/infrastructure/aop/context/aspects/execution/LogExecutionAspect.java b/infrastructure/src/main/java/com/callv2/drive/infrastructure/aop/aspects/execution/after/LogAfterExecutionAspect.java similarity index 74% rename from infrastructure/src/main/java/com/callv2/drive/infrastructure/aop/context/aspects/execution/LogExecutionAspect.java rename to infrastructure/src/main/java/com/callv2/drive/infrastructure/aop/aspects/execution/after/LogAfterExecutionAspect.java index 752b19a5..6b078542 100644 --- a/infrastructure/src/main/java/com/callv2/drive/infrastructure/aop/context/aspects/execution/LogExecutionAspect.java +++ b/infrastructure/src/main/java/com/callv2/drive/infrastructure/aop/aspects/execution/after/LogAfterExecutionAspect.java @@ -1,19 +1,19 @@ -package com.callv2.drive.infrastructure.aop.context.aspects.execution; +package com.callv2.drive.infrastructure.aop.aspects.execution.after; import org.apache.logging.log4j.Level; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; import org.apache.logging.log4j.message.Message; -import com.callv2.drive.infrastructure.aop.context.aspects.chain.execution.AspectExecutionChain; +import com.callv2.drive.infrastructure.aop.aspects.chain.AspectAfterExecutionChain; -public abstract class LogExecutionAspect extends AspectExecutionChain { +public abstract class LogAfterExecutionAspect extends AspectAfterExecutionChain { private final Logger logger; private final Level logLevel; - public LogExecutionAspect(final Level logLevel, Class clazz) { + public LogAfterExecutionAspect(final Level logLevel, Class clazz) { super(); this.logLevel = logLevel; this.logger = LogManager.getLogger(clazz); diff --git a/infrastructure/src/main/java/com/callv2/drive/infrastructure/aop/aspects/execution/after/TelemetryAfterLogAspect.java b/infrastructure/src/main/java/com/callv2/drive/infrastructure/aop/aspects/execution/after/TelemetryAfterLogAspect.java new file mode 100644 index 00000000..b6efb6f7 --- /dev/null +++ b/infrastructure/src/main/java/com/callv2/drive/infrastructure/aop/aspects/execution/after/TelemetryAfterLogAspect.java @@ -0,0 +1,27 @@ +package com.callv2.drive.infrastructure.aop.aspects.execution.after; + +import java.time.Duration; + +import org.apache.logging.log4j.Level; + +import com.callv2.drive.infrastructure.aop.aspects.context.AfterExecutionContext; + +public class TelemetryAfterLogAspect extends LogAfterExecutionAspect { + + public TelemetryAfterLogAspect(final Level logLevel) { + super(logLevel, TelemetryAfterLogAspect.class); + } + + @Override + protected void execute(final AfterExecutionContext context) throws Throwable { + + final String methodSignature = context.joinPoint().getSignature().toShortString(); + final long elapsedMillis = Duration.between(context.startedAt(), context.finishedAt()).toMillis(); + final String message = String.format( + "Method [%s] executed in %d ms.", + methodSignature, + elapsedMillis); + + logger().log(level(), createMessage(message)); + } +} diff --git a/infrastructure/src/main/java/com/callv2/drive/infrastructure/aop/context/aspects/execution/ArgsLogAspect.java b/infrastructure/src/main/java/com/callv2/drive/infrastructure/aop/aspects/execution/before/ArgsLogAspect.java similarity index 52% rename from infrastructure/src/main/java/com/callv2/drive/infrastructure/aop/context/aspects/execution/ArgsLogAspect.java rename to infrastructure/src/main/java/com/callv2/drive/infrastructure/aop/aspects/execution/before/ArgsLogAspect.java index 5e19b58d..a6583856 100644 --- a/infrastructure/src/main/java/com/callv2/drive/infrastructure/aop/context/aspects/execution/ArgsLogAspect.java +++ b/infrastructure/src/main/java/com/callv2/drive/infrastructure/aop/aspects/execution/before/ArgsLogAspect.java @@ -1,17 +1,17 @@ -package com.callv2.drive.infrastructure.aop.context.aspects.execution; +package com.callv2.drive.infrastructure.aop.aspects.execution.before; import org.apache.logging.log4j.Level; -import com.callv2.drive.infrastructure.aop.context.ExecutionContext; +import com.callv2.drive.infrastructure.aop.aspects.context.BeforeExecutionContext; -public class ArgsLogAspect extends LogExecutionAspect { +public class ArgsLogAspect extends LogBeforeExecutionAspect { public ArgsLogAspect(final Level logLevel) { super(logLevel, ArgsLogAspect.class); } @Override - protected void execute(final ExecutionContext context) throws Throwable { + protected void execute(final BeforeExecutionContext context) throws Throwable { for (Object arg : context.joinPoint().getArgs()) logger().log(level(), createMessage("Arg: " + (arg != null ? arg.toString() : "null"))); } diff --git a/infrastructure/src/main/java/com/callv2/drive/infrastructure/aop/aspects/execution/before/LogBeforeExecutionAspect.java b/infrastructure/src/main/java/com/callv2/drive/infrastructure/aop/aspects/execution/before/LogBeforeExecutionAspect.java new file mode 100644 index 00000000..f8c9da06 --- /dev/null +++ b/infrastructure/src/main/java/com/callv2/drive/infrastructure/aop/aspects/execution/before/LogBeforeExecutionAspect.java @@ -0,0 +1,42 @@ +package com.callv2.drive.infrastructure.aop.aspects.execution.before; + +import org.apache.logging.log4j.Level; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; +import org.apache.logging.log4j.message.Message; + +import com.callv2.drive.infrastructure.aop.aspects.chain.AspectBeforeExecutionChain; + +public abstract class LogBeforeExecutionAspect extends AspectBeforeExecutionChain { + + private final Logger logger; + + private final Level logLevel; + + public LogBeforeExecutionAspect(final Level logLevel, Class clazz) { + super(); + this.logLevel = logLevel; + this.logger = LogManager.getLogger(clazz); + } + + protected Level level() { + return logLevel; + } + + public Logger logger() { + return logger; + } + + protected Message createMessage(final String message) { + return logger.getMessageFactory().newMessage(message); + } + + protected Message createMessage(final Object message) { + return logger.getMessageFactory().newMessage(message); + } + + protected Message createMessage(final String message, final Object... params) { + return logger.getMessageFactory().newMessage(message, params); + } + +} diff --git a/infrastructure/src/main/java/com/callv2/drive/infrastructure/aop/context/aspects/execution/SignatureLogAspect.java b/infrastructure/src/main/java/com/callv2/drive/infrastructure/aop/aspects/execution/before/SignatureLogAspect.java similarity index 50% rename from infrastructure/src/main/java/com/callv2/drive/infrastructure/aop/context/aspects/execution/SignatureLogAspect.java rename to infrastructure/src/main/java/com/callv2/drive/infrastructure/aop/aspects/execution/before/SignatureLogAspect.java index 98ece0e1..0e74a123 100644 --- a/infrastructure/src/main/java/com/callv2/drive/infrastructure/aop/context/aspects/execution/SignatureLogAspect.java +++ b/infrastructure/src/main/java/com/callv2/drive/infrastructure/aop/aspects/execution/before/SignatureLogAspect.java @@ -1,17 +1,17 @@ -package com.callv2.drive.infrastructure.aop.context.aspects.execution; +package com.callv2.drive.infrastructure.aop.aspects.execution.before; import org.apache.logging.log4j.Level; -import com.callv2.drive.infrastructure.aop.context.ExecutionContext; +import com.callv2.drive.infrastructure.aop.aspects.context.BeforeExecutionContext; -public class SignatureLogAspect extends LogExecutionAspect { +public class SignatureLogAspect extends LogBeforeExecutionAspect { public SignatureLogAspect(final Level logLevel) { super(logLevel, SignatureLogAspect.class); } @Override - protected void execute(final ExecutionContext context) throws Throwable { + protected void execute(final BeforeExecutionContext context) throws Throwable { logger().log(level(), createMessage("SignatureLogAspect: " + context.joinPoint().getSignature().toLongString())); } diff --git a/infrastructure/src/main/java/com/callv2/drive/infrastructure/aop/aspects/execution/before/TelemetryBeforeLogAspect.java b/infrastructure/src/main/java/com/callv2/drive/infrastructure/aop/aspects/execution/before/TelemetryBeforeLogAspect.java new file mode 100644 index 00000000..87ac3b45 --- /dev/null +++ b/infrastructure/src/main/java/com/callv2/drive/infrastructure/aop/aspects/execution/before/TelemetryBeforeLogAspect.java @@ -0,0 +1,25 @@ +package com.callv2.drive.infrastructure.aop.aspects.execution.before; + +import java.time.Duration; +import java.time.Instant; + +import org.apache.logging.log4j.Level; + +import com.callv2.drive.infrastructure.aop.aspects.context.BeforeExecutionContext; + +public class TelemetryBeforeLogAspect extends LogBeforeExecutionAspect { + + public TelemetryBeforeLogAspect(final Level logLevel) { + super(logLevel, TelemetryBeforeLogAspect.class); + } + + @Override + protected void execute(final BeforeExecutionContext context) throws Throwable { + logger() + .log( + level(), + createMessage( + "ElapsedTimeProcessingAspectChain: " + + Duration.between(context.startTime(), Instant.now()).toNanos())); + } +} diff --git a/infrastructure/src/main/java/com/callv2/drive/infrastructure/aop/context/aspects/error/LogErrorAspect.java b/infrastructure/src/main/java/com/callv2/drive/infrastructure/aop/aspects/execution/error/LogExecutionErrorAspect.java similarity index 74% rename from infrastructure/src/main/java/com/callv2/drive/infrastructure/aop/context/aspects/error/LogErrorAspect.java rename to infrastructure/src/main/java/com/callv2/drive/infrastructure/aop/aspects/execution/error/LogExecutionErrorAspect.java index 92c4fd0b..4372998c 100644 --- a/infrastructure/src/main/java/com/callv2/drive/infrastructure/aop/context/aspects/error/LogErrorAspect.java +++ b/infrastructure/src/main/java/com/callv2/drive/infrastructure/aop/aspects/execution/error/LogExecutionErrorAspect.java @@ -1,19 +1,19 @@ -package com.callv2.drive.infrastructure.aop.context.aspects.error; +package com.callv2.drive.infrastructure.aop.aspects.execution.error; import org.apache.logging.log4j.Level; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; import org.apache.logging.log4j.message.Message; -import com.callv2.drive.infrastructure.aop.context.aspects.chain.error.AspectErrorChain; +import com.callv2.drive.infrastructure.aop.aspects.chain.AspectExecutionErrorChain; -public abstract class LogErrorAspect extends AspectErrorChain { +public abstract class LogExecutionErrorAspect extends AspectExecutionErrorChain { private final Logger logger; private final Level logLevel; - public LogErrorAspect(final Level logLevel, Class clazz) { + public LogExecutionErrorAspect(final Level logLevel, Class clazz) { super(); this.logLevel = logLevel; this.logger = LogManager.getLogger(clazz); diff --git a/infrastructure/src/main/java/com/callv2/drive/infrastructure/aop/context/aspects/error/StackTraceLogAspect.java b/infrastructure/src/main/java/com/callv2/drive/infrastructure/aop/aspects/execution/error/StackTraceLogAspect.java similarity index 63% rename from infrastructure/src/main/java/com/callv2/drive/infrastructure/aop/context/aspects/error/StackTraceLogAspect.java rename to infrastructure/src/main/java/com/callv2/drive/infrastructure/aop/aspects/execution/error/StackTraceLogAspect.java index 6e5d4c21..0048ae68 100644 --- a/infrastructure/src/main/java/com/callv2/drive/infrastructure/aop/context/aspects/error/StackTraceLogAspect.java +++ b/infrastructure/src/main/java/com/callv2/drive/infrastructure/aop/aspects/execution/error/StackTraceLogAspect.java @@ -1,10 +1,10 @@ -package com.callv2.drive.infrastructure.aop.context.aspects.error; +package com.callv2.drive.infrastructure.aop.aspects.execution.error; import org.apache.logging.log4j.Level; -import com.callv2.drive.infrastructure.aop.context.ExecutionErrorContext; +import com.callv2.drive.infrastructure.aop.aspects.context.ExecutionErrorContext; -public class StackTraceLogAspect extends LogErrorAspect { +public class StackTraceLogAspect extends LogExecutionErrorAspect { public StackTraceLogAspect(final Level logLevel) { super(logLevel, StackTraceLogAspect.class); diff --git a/infrastructure/src/main/java/com/callv2/drive/infrastructure/aop/context/ExecutionContext.java b/infrastructure/src/main/java/com/callv2/drive/infrastructure/aop/context/ExecutionContext.java deleted file mode 100644 index 3df96a0d..00000000 --- a/infrastructure/src/main/java/com/callv2/drive/infrastructure/aop/context/ExecutionContext.java +++ /dev/null @@ -1,23 +0,0 @@ -package com.callv2.drive.infrastructure.aop.context; - -import java.time.Instant; - -import org.aspectj.lang.JoinPoint; - -public record ExecutionContext(JoinPoint joinPoint, Instant startTime) { - - public ExecutionContext { - - if (joinPoint == null) - throw new IllegalArgumentException("JoinPoint cannot be null"); - - if (startTime == null) - throw new IllegalArgumentException("Start time cannot be null"); - - } - - public static ExecutionContext of(JoinPoint joinPoint) { - return new ExecutionContext(joinPoint, Instant.now()); - } - -} From 60a71372d4943833efd5e7698a7365d7f6583bd7 Mon Sep 17 00:00:00 2001 From: jhonatapers Date: Thu, 15 May 2025 18:01:26 -0300 Subject: [PATCH 24/53] feat: add logging configuration and implement aspect handlers for execution and error logging --- .../aspects/AbstractAroundAspectHandler.java | 40 +++++++++++++++++++ .../aop/aspects/AspectHandler.java | 9 +++++ .../drive/infrastructure/api/FileAPI.java | 2 +- .../logging/DomainLogConfig.java | 37 +++++++++++++++++ .../logging/domain/FolderLog.java | 26 ++++++++++++ .../src/main/resources/application.yml | 14 ++++++- 6 files changed, 126 insertions(+), 2 deletions(-) create mode 100644 infrastructure/src/main/java/com/callv2/drive/infrastructure/aop/aspects/AbstractAroundAspectHandler.java create mode 100644 infrastructure/src/main/java/com/callv2/drive/infrastructure/aop/aspects/AspectHandler.java create mode 100644 infrastructure/src/main/java/com/callv2/drive/infrastructure/configuration/logging/DomainLogConfig.java create mode 100644 infrastructure/src/main/java/com/callv2/drive/infrastructure/logging/domain/FolderLog.java diff --git a/infrastructure/src/main/java/com/callv2/drive/infrastructure/aop/aspects/AbstractAroundAspectHandler.java b/infrastructure/src/main/java/com/callv2/drive/infrastructure/aop/aspects/AbstractAroundAspectHandler.java new file mode 100644 index 00000000..f5069e71 --- /dev/null +++ b/infrastructure/src/main/java/com/callv2/drive/infrastructure/aop/aspects/AbstractAroundAspectHandler.java @@ -0,0 +1,40 @@ +package com.callv2.drive.infrastructure.aop.aspects; + +import java.util.Objects; + +import org.aspectj.lang.JoinPoint; + +import com.callv2.drive.infrastructure.aop.aspects.chain.AspectAfterExecutionChain; +import com.callv2.drive.infrastructure.aop.aspects.chain.AspectBeforeExecutionChain; +import com.callv2.drive.infrastructure.aop.aspects.chain.AspectExecutionErrorChain; +import com.callv2.drive.infrastructure.aop.aspects.context.AfterExecutionContext; +import com.callv2.drive.infrastructure.aop.aspects.context.BeforeExecutionContext; +import com.callv2.drive.infrastructure.aop.aspects.context.ExecutionErrorContext; + +public abstract class AbstractAroundAspectHandler implements AspectHandler { + + private final AspectBeforeExecutionChain beforeChain; + private final AspectAfterExecutionChain afterChain; + private final AspectExecutionErrorChain errorChain; + + protected AbstractAroundAspectHandler( + final AspectBeforeExecutionChain beforeChain, + final AspectAfterExecutionChain afterChain, + final AspectExecutionErrorChain errorChain) { + this.beforeChain = Objects.requireNonNull(beforeChain); + this.afterChain = Objects.requireNonNull(afterChain); + this.errorChain = Objects.requireNonNull(errorChain); + } + + protected Object defaultHandle(final JoinPoint joinPoint) throws Throwable { + final BeforeExecutionContext executionContext = BeforeExecutionContext.of(joinPoint); + try { + return afterChain + .proceed(AfterExecutionContext + .of(beforeChain.proceed(executionContext), executionContext)); + } catch (Throwable ex) { + throw errorChain.proceed(ExecutionErrorContext.of(executionContext, ex)); + } + } + +} diff --git a/infrastructure/src/main/java/com/callv2/drive/infrastructure/aop/aspects/AspectHandler.java b/infrastructure/src/main/java/com/callv2/drive/infrastructure/aop/aspects/AspectHandler.java new file mode 100644 index 00000000..ec4153d3 --- /dev/null +++ b/infrastructure/src/main/java/com/callv2/drive/infrastructure/aop/aspects/AspectHandler.java @@ -0,0 +1,9 @@ +package com.callv2.drive.infrastructure.aop.aspects; + +import org.aspectj.lang.JoinPoint; + +public interface AspectHandler { + + Object handle(JoinPoint joinPoint) throws Throwable; + +} \ No newline at end of file diff --git a/infrastructure/src/main/java/com/callv2/drive/infrastructure/api/FileAPI.java b/infrastructure/src/main/java/com/callv2/drive/infrastructure/api/FileAPI.java index 401394b1..bd0cd9ab 100644 --- a/infrastructure/src/main/java/com/callv2/drive/infrastructure/api/FileAPI.java +++ b/infrastructure/src/main/java/com/callv2/drive/infrastructure/api/FileAPI.java @@ -44,7 +44,7 @@ public interface FileAPI { @ApiResponse(responseCode = "500", description = "Internal Server Error", content = @Content(schema = @Schema(implementation = ApiError.class))) }) ResponseEntity create( - @PathVariable UUID folderId, + @PathVariable(required = true, name = "folderId") UUID folderId, @RequestPart("file") MultipartFile file); @GetMapping(value = "{id}", produces = { MediaType.APPLICATION_JSON_VALUE }) diff --git a/infrastructure/src/main/java/com/callv2/drive/infrastructure/configuration/logging/DomainLogConfig.java b/infrastructure/src/main/java/com/callv2/drive/infrastructure/configuration/logging/DomainLogConfig.java new file mode 100644 index 00000000..5cc4f93c --- /dev/null +++ b/infrastructure/src/main/java/com/callv2/drive/infrastructure/configuration/logging/DomainLogConfig.java @@ -0,0 +1,37 @@ +package com.callv2.drive.infrastructure.configuration.logging; + +import org.apache.logging.log4j.Level; +import org.springframework.context.annotation.Configuration; + +import com.callv2.drive.infrastructure.aop.aspects.chain.AspectAfterExecutionChain; +import com.callv2.drive.infrastructure.aop.aspects.chain.AspectBeforeExecutionChain; +import com.callv2.drive.infrastructure.aop.aspects.chain.AspectExecutionErrorChain; +import com.callv2.drive.infrastructure.aop.aspects.execution.after.TelemetryAfterLogAspect; +import com.callv2.drive.infrastructure.aop.aspects.execution.before.ArgsLogAspect; +import com.callv2.drive.infrastructure.aop.aspects.execution.before.SignatureLogAspect; +import com.callv2.drive.infrastructure.aop.aspects.execution.before.TelemetryBeforeLogAspect; +import com.callv2.drive.infrastructure.aop.aspects.execution.error.StackTraceLogAspect; +import com.callv2.drive.infrastructure.logging.domain.FolderLog; + +@Configuration +public class DomainLogConfig { + + FolderLog folderLog() { + final var beforeChain = AspectBeforeExecutionChain.Builder.create() + .add(new SignatureLogAspect(Level.INFO)) + .add(new ArgsLogAspect(Level.INFO)) + .add(new TelemetryBeforeLogAspect(Level.INFO)) + .build(); + + final var afterChain = AspectAfterExecutionChain.Builder.create() + .add(new TelemetryAfterLogAspect(Level.INFO)) + .build(); + + final var errorChain = AspectExecutionErrorChain.Builder.create() + .add(new StackTraceLogAspect(Level.INFO)) + .build(); + + return new FolderLog(beforeChain, afterChain, errorChain); + } + +} diff --git a/infrastructure/src/main/java/com/callv2/drive/infrastructure/logging/domain/FolderLog.java b/infrastructure/src/main/java/com/callv2/drive/infrastructure/logging/domain/FolderLog.java new file mode 100644 index 00000000..481cc890 --- /dev/null +++ b/infrastructure/src/main/java/com/callv2/drive/infrastructure/logging/domain/FolderLog.java @@ -0,0 +1,26 @@ +package com.callv2.drive.infrastructure.logging.domain; + +import org.aspectj.lang.JoinPoint; +import org.aspectj.lang.annotation.Around; + +import com.callv2.drive.infrastructure.aop.aspects.AbstractAroundAspectHandler; +import com.callv2.drive.infrastructure.aop.aspects.chain.AspectAfterExecutionChain; +import com.callv2.drive.infrastructure.aop.aspects.chain.AspectBeforeExecutionChain; +import com.callv2.drive.infrastructure.aop.aspects.chain.AspectExecutionErrorChain; + +public class FolderLog extends AbstractAroundAspectHandler { + + public FolderLog( + final AspectBeforeExecutionChain beforeChain, + final AspectAfterExecutionChain afterChain, + final AspectExecutionErrorChain errorChain) { + super(beforeChain, afterChain, errorChain); + } + + @Override + @Around("execution(* com.callv2.drive.domain.folder..*(..))") + public Object handle(JoinPoint joinPoint) throws Throwable { + return this.defaultHandle(joinPoint); + } + +} diff --git a/infrastructure/src/main/resources/application.yml b/infrastructure/src/main/resources/application.yml index b1b22075..0b8b8006 100644 --- a/infrastructure/src/main/resources/application.yml +++ b/infrastructure/src/main/resources/application.yml @@ -32,4 +32,16 @@ spring: jpa: hibernate: ddl-auto: ${db.ddl-auto} - show-sql: true \ No newline at end of file + show-sql: false + +logging: + level: + root: INFO + com.callv2: DEBUG + org.springframework.web: WARN + + pattern: + console: "%d{yyyy-MM-dd HH:mm:ss} - %highlight(%-5level) [%thread] %logger{36} - %msg%n" + + file: + name: logs/app.log \ No newline at end of file From 5c31d41376ef49063234af496c879ec75341f1fe Mon Sep 17 00:00:00 2001 From: jhonatapers Date: Thu, 15 May 2025 18:02:38 -0300 Subject: [PATCH 25/53] chore: update .gitignore to include additional environment and log files --- .gitignore | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/.gitignore b/.gitignore index 17391b5c..f6cde2b0 100644 --- a/.gitignore +++ b/.gitignore @@ -12,3 +12,8 @@ bin *.db + + +.env + +*.log \ No newline at end of file From 83473255b7f2d7d0c3622d7bc0880b8893e545e3 Mon Sep 17 00:00:00 2001 From: jhonatapers Date: Fri, 16 May 2025 18:55:19 -0300 Subject: [PATCH 26/53] Refactor AOP structure and logging aspects - Added Spring AOP dependency to build.gradle. - Removed obsolete AOP classes: AbstractAroundAspectHandler, AspectHandler, AspectAfterExecutionChain, AspectBeforeExecutionChain, AspectExecutionErrorChain. - Introduced new chains for method invocation and post-invocation aspects: MethodInvocationAspectExecutorChain, PostInvocationAspectExecutorChain. - Created context classes for method invocation and post-invocation: MethodInvocationContext, PostInvocationContext, and their implementations. - Implemented logging executors for post-invocation context: Log4jPostExecutor and ThrowableLogExecutor. - Updated DomainLogConfig to prepare for dynamic aspect configuration. - Removed deprecated logging aspects: LogAfterExecutionAspect, TelemetryAfterLogAspect, ArgsLogAspect, SignatureLogAspect, TelemetryBeforeLogAspect, LogBeforeExecutionAspect, LogExecutionErrorAspect, StackTraceLogAspect. - Cleaned up FolderLog class, removing its dependency on AOP chains. --- infrastructure/build.gradle | 1 + .../aspects/AbstractAroundAspectHandler.java | 40 ------- .../aop/aspects/AspectHandler.java | 9 -- .../chain/AspectAfterExecutionChain.java | 63 ----------- .../chain/AspectBeforeExecutionChain.java | 68 ------------ .../chain/AspectExecutionErrorChain.java | 65 ----------- .../MethodInvocationAspectExecutorChain.java | 67 ++++++++++++ .../PostInvocationAspectExecutorChain.java | 68 ++++++++++++ .../AbstractPostInvocationContext.java | 44 ++++++++ .../context/AfterExecutionContext.java | 26 ----- .../context/BeforeExecutionContext.java | 23 ---- .../context/ExecutionErrorContext.java | 19 ---- .../context/MethodInvocationContext.java | 20 ++++ .../context/PostInvocationContext.java | 22 ++++ .../context/SimplePostInvocationContext.java | 102 ++++++++++++++++++ .../after/LogAfterExecutionAspect.java | 42 -------- .../after/TelemetryAfterLogAspect.java | 27 ----- .../execution/before/ArgsLogAspect.java | 19 ---- .../before/LogBeforeExecutionAspect.java | 42 -------- .../execution/before/SignatureLogAspect.java | 19 ---- .../before/TelemetryBeforeLogAspect.java | 25 ----- .../error/LogExecutionErrorAspect.java | 41 ------- .../execution/error/StackTraceLogAspect.java | 18 ---- .../MethodInvocationAspectExecutor.java | 10 ++ .../PostInvocationContextAspectExecutor.java | 10 ++ .../aop/aspect/DynamicAspectConfig.java | 47 ++++++++ .../logging/DomainLogConfig.java | 78 +++++++++----- .../aspect/executor/Log4jExecutor.java | 43 ++++++++ .../aspect/executor/Log4jPostExecutor.java | 13 +++ .../aspect/executor/ThrowableLogExecutor.java | 26 +++++ .../logging/domain/FolderLog.java | 26 ----- 31 files changed, 526 insertions(+), 597 deletions(-) delete mode 100644 infrastructure/src/main/java/com/callv2/drive/infrastructure/aop/aspects/AbstractAroundAspectHandler.java delete mode 100644 infrastructure/src/main/java/com/callv2/drive/infrastructure/aop/aspects/AspectHandler.java delete mode 100644 infrastructure/src/main/java/com/callv2/drive/infrastructure/aop/aspects/chain/AspectAfterExecutionChain.java delete mode 100644 infrastructure/src/main/java/com/callv2/drive/infrastructure/aop/aspects/chain/AspectBeforeExecutionChain.java delete mode 100644 infrastructure/src/main/java/com/callv2/drive/infrastructure/aop/aspects/chain/AspectExecutionErrorChain.java create mode 100644 infrastructure/src/main/java/com/callv2/drive/infrastructure/aop/aspects/chain/MethodInvocationAspectExecutorChain.java create mode 100644 infrastructure/src/main/java/com/callv2/drive/infrastructure/aop/aspects/chain/PostInvocationAspectExecutorChain.java create mode 100644 infrastructure/src/main/java/com/callv2/drive/infrastructure/aop/aspects/context/AbstractPostInvocationContext.java delete mode 100644 infrastructure/src/main/java/com/callv2/drive/infrastructure/aop/aspects/context/AfterExecutionContext.java delete mode 100644 infrastructure/src/main/java/com/callv2/drive/infrastructure/aop/aspects/context/BeforeExecutionContext.java delete mode 100644 infrastructure/src/main/java/com/callv2/drive/infrastructure/aop/aspects/context/ExecutionErrorContext.java create mode 100644 infrastructure/src/main/java/com/callv2/drive/infrastructure/aop/aspects/context/MethodInvocationContext.java create mode 100644 infrastructure/src/main/java/com/callv2/drive/infrastructure/aop/aspects/context/PostInvocationContext.java create mode 100644 infrastructure/src/main/java/com/callv2/drive/infrastructure/aop/aspects/context/SimplePostInvocationContext.java delete mode 100644 infrastructure/src/main/java/com/callv2/drive/infrastructure/aop/aspects/execution/after/LogAfterExecutionAspect.java delete mode 100644 infrastructure/src/main/java/com/callv2/drive/infrastructure/aop/aspects/execution/after/TelemetryAfterLogAspect.java delete mode 100644 infrastructure/src/main/java/com/callv2/drive/infrastructure/aop/aspects/execution/before/ArgsLogAspect.java delete mode 100644 infrastructure/src/main/java/com/callv2/drive/infrastructure/aop/aspects/execution/before/LogBeforeExecutionAspect.java delete mode 100644 infrastructure/src/main/java/com/callv2/drive/infrastructure/aop/aspects/execution/before/SignatureLogAspect.java delete mode 100644 infrastructure/src/main/java/com/callv2/drive/infrastructure/aop/aspects/execution/before/TelemetryBeforeLogAspect.java delete mode 100644 infrastructure/src/main/java/com/callv2/drive/infrastructure/aop/aspects/execution/error/LogExecutionErrorAspect.java delete mode 100644 infrastructure/src/main/java/com/callv2/drive/infrastructure/aop/aspects/execution/error/StackTraceLogAspect.java create mode 100644 infrastructure/src/main/java/com/callv2/drive/infrastructure/aop/aspects/executor/MethodInvocationAspectExecutor.java create mode 100644 infrastructure/src/main/java/com/callv2/drive/infrastructure/aop/aspects/executor/PostInvocationContextAspectExecutor.java create mode 100644 infrastructure/src/main/java/com/callv2/drive/infrastructure/configuration/aop/aspect/DynamicAspectConfig.java create mode 100644 infrastructure/src/main/java/com/callv2/drive/infrastructure/logging/aspect/executor/Log4jExecutor.java create mode 100644 infrastructure/src/main/java/com/callv2/drive/infrastructure/logging/aspect/executor/Log4jPostExecutor.java create mode 100644 infrastructure/src/main/java/com/callv2/drive/infrastructure/logging/aspect/executor/ThrowableLogExecutor.java delete mode 100644 infrastructure/src/main/java/com/callv2/drive/infrastructure/logging/domain/FolderLog.java diff --git a/infrastructure/build.gradle b/infrastructure/build.gradle index dfa4109d..769eaf18 100644 --- a/infrastructure/build.gradle +++ b/infrastructure/build.gradle @@ -44,6 +44,7 @@ dependencies { implementation libs.guava + implementation 'org.springframework:spring-aop' implementation 'org.springframework.boot:spring-boot-starter-log4j2' } diff --git a/infrastructure/src/main/java/com/callv2/drive/infrastructure/aop/aspects/AbstractAroundAspectHandler.java b/infrastructure/src/main/java/com/callv2/drive/infrastructure/aop/aspects/AbstractAroundAspectHandler.java deleted file mode 100644 index f5069e71..00000000 --- a/infrastructure/src/main/java/com/callv2/drive/infrastructure/aop/aspects/AbstractAroundAspectHandler.java +++ /dev/null @@ -1,40 +0,0 @@ -package com.callv2.drive.infrastructure.aop.aspects; - -import java.util.Objects; - -import org.aspectj.lang.JoinPoint; - -import com.callv2.drive.infrastructure.aop.aspects.chain.AspectAfterExecutionChain; -import com.callv2.drive.infrastructure.aop.aspects.chain.AspectBeforeExecutionChain; -import com.callv2.drive.infrastructure.aop.aspects.chain.AspectExecutionErrorChain; -import com.callv2.drive.infrastructure.aop.aspects.context.AfterExecutionContext; -import com.callv2.drive.infrastructure.aop.aspects.context.BeforeExecutionContext; -import com.callv2.drive.infrastructure.aop.aspects.context.ExecutionErrorContext; - -public abstract class AbstractAroundAspectHandler implements AspectHandler { - - private final AspectBeforeExecutionChain beforeChain; - private final AspectAfterExecutionChain afterChain; - private final AspectExecutionErrorChain errorChain; - - protected AbstractAroundAspectHandler( - final AspectBeforeExecutionChain beforeChain, - final AspectAfterExecutionChain afterChain, - final AspectExecutionErrorChain errorChain) { - this.beforeChain = Objects.requireNonNull(beforeChain); - this.afterChain = Objects.requireNonNull(afterChain); - this.errorChain = Objects.requireNonNull(errorChain); - } - - protected Object defaultHandle(final JoinPoint joinPoint) throws Throwable { - final BeforeExecutionContext executionContext = BeforeExecutionContext.of(joinPoint); - try { - return afterChain - .proceed(AfterExecutionContext - .of(beforeChain.proceed(executionContext), executionContext)); - } catch (Throwable ex) { - throw errorChain.proceed(ExecutionErrorContext.of(executionContext, ex)); - } - } - -} diff --git a/infrastructure/src/main/java/com/callv2/drive/infrastructure/aop/aspects/AspectHandler.java b/infrastructure/src/main/java/com/callv2/drive/infrastructure/aop/aspects/AspectHandler.java deleted file mode 100644 index ec4153d3..00000000 --- a/infrastructure/src/main/java/com/callv2/drive/infrastructure/aop/aspects/AspectHandler.java +++ /dev/null @@ -1,9 +0,0 @@ -package com.callv2.drive.infrastructure.aop.aspects; - -import org.aspectj.lang.JoinPoint; - -public interface AspectHandler { - - Object handle(JoinPoint joinPoint) throws Throwable; - -} \ No newline at end of file diff --git a/infrastructure/src/main/java/com/callv2/drive/infrastructure/aop/aspects/chain/AspectAfterExecutionChain.java b/infrastructure/src/main/java/com/callv2/drive/infrastructure/aop/aspects/chain/AspectAfterExecutionChain.java deleted file mode 100644 index e0198830..00000000 --- a/infrastructure/src/main/java/com/callv2/drive/infrastructure/aop/aspects/chain/AspectAfterExecutionChain.java +++ /dev/null @@ -1,63 +0,0 @@ -package com.callv2.drive.infrastructure.aop.aspects.chain; - -import java.util.ArrayDeque; -import java.util.Queue; - -import com.callv2.drive.infrastructure.aop.aspects.context.AfterExecutionContext; - -public abstract class AspectAfterExecutionChain { - - private AspectAfterExecutionChain next; - - protected AspectAfterExecutionChain() { - } - - public final Object proceed(final AfterExecutionContext context) throws Throwable { - execute(context); - - if (next != null) - return next.proceed(context); - - return context.result(); - } - - protected abstract void execute(final AfterExecutionContext context) throws Throwable; - - private AspectAfterExecutionChain setNext(final AspectAfterExecutionChain next) { - return this.next = next; - } - - public static final class Builder { - - private final Queue chains; - - private Builder() { - this.chains = new ArrayDeque<>(); - } - - public static Builder create() { - return new Builder(); - } - - public Builder add(final AspectAfterExecutionChain chain) { - this.chains.add(chain); - return this; - } - - public AspectAfterExecutionChain build() { - if (chains.isEmpty()) - return null; - - final var firstChain = chains.poll(); - var chain = firstChain; - - do { - chain = chain.setNext(chains.poll()); - } while (!chains.isEmpty()); - - return firstChain; - } - - } - -} diff --git a/infrastructure/src/main/java/com/callv2/drive/infrastructure/aop/aspects/chain/AspectBeforeExecutionChain.java b/infrastructure/src/main/java/com/callv2/drive/infrastructure/aop/aspects/chain/AspectBeforeExecutionChain.java deleted file mode 100644 index 624dcd11..00000000 --- a/infrastructure/src/main/java/com/callv2/drive/infrastructure/aop/aspects/chain/AspectBeforeExecutionChain.java +++ /dev/null @@ -1,68 +0,0 @@ -package com.callv2.drive.infrastructure.aop.aspects.chain; - -import java.util.ArrayDeque; -import java.util.Queue; - -import org.aspectj.lang.ProceedingJoinPoint; - -import com.callv2.drive.infrastructure.aop.aspects.context.BeforeExecutionContext; - -public abstract class AspectBeforeExecutionChain { - - private AspectBeforeExecutionChain next; - - protected AspectBeforeExecutionChain() { - } - - public final Object proceed(final BeforeExecutionContext context) throws Throwable { - execute(context); - - if (next != null) - return next.proceed(context); - - if (context.joinPoint() instanceof ProceedingJoinPoint) - return ((ProceedingJoinPoint) context.joinPoint()).proceed(); - - return null; - } - - protected abstract void execute(final BeforeExecutionContext context) throws Throwable; - - private AspectBeforeExecutionChain setNext(final AspectBeforeExecutionChain next) { - return this.next = next; - } - - public static final class Builder { - - private final Queue chains; - - private Builder() { - this.chains = new ArrayDeque<>(); - } - - public static Builder create() { - return new Builder(); - } - - public Builder add(final AspectBeforeExecutionChain chain) { - this.chains.add(chain); - return this; - } - - public AspectBeforeExecutionChain build() { - if (chains.isEmpty()) - return null; - - final var firstChain = chains.poll(); - var chain = firstChain; - - do { - chain = chain.setNext(chains.poll()); - } while (!chains.isEmpty()); - - return firstChain; - } - - } - -} diff --git a/infrastructure/src/main/java/com/callv2/drive/infrastructure/aop/aspects/chain/AspectExecutionErrorChain.java b/infrastructure/src/main/java/com/callv2/drive/infrastructure/aop/aspects/chain/AspectExecutionErrorChain.java deleted file mode 100644 index 977afb5c..00000000 --- a/infrastructure/src/main/java/com/callv2/drive/infrastructure/aop/aspects/chain/AspectExecutionErrorChain.java +++ /dev/null @@ -1,65 +0,0 @@ -package com.callv2.drive.infrastructure.aop.aspects.chain; - -import java.util.ArrayDeque; -import java.util.Queue; - -import com.callv2.drive.infrastructure.aop.aspects.context.ExecutionErrorContext; - -public abstract class AspectExecutionErrorChain { - - private AspectExecutionErrorChain next; - - protected AspectExecutionErrorChain() { - } - - public final Throwable proceed(final ExecutionErrorContext context) throws Throwable { - - execute(context); - - if (next != null) - return next.proceed(context); - - return context.error(); - - } - - protected abstract void execute(final ExecutionErrorContext context) throws Throwable; - - private AspectExecutionErrorChain setNext(final AspectExecutionErrorChain next) { - return this.next = next; - } - - public static final class Builder { - - private final Queue chains; - - private Builder() { - this.chains = new ArrayDeque<>(); - } - - public static Builder create() { - return new Builder(); - } - - public Builder add(final AspectExecutionErrorChain chain) { - this.chains.add(chain); - return this; - } - - public AspectExecutionErrorChain build() { - if (chains.isEmpty()) - return null; - - final var firstChain = chains.poll(); - var chain = firstChain; - - do { - chain = chain.setNext(chains.poll()); - } while (!chains.isEmpty()); - - return firstChain; - } - - } - -} diff --git a/infrastructure/src/main/java/com/callv2/drive/infrastructure/aop/aspects/chain/MethodInvocationAspectExecutorChain.java b/infrastructure/src/main/java/com/callv2/drive/infrastructure/aop/aspects/chain/MethodInvocationAspectExecutorChain.java new file mode 100644 index 00000000..be623da5 --- /dev/null +++ b/infrastructure/src/main/java/com/callv2/drive/infrastructure/aop/aspects/chain/MethodInvocationAspectExecutorChain.java @@ -0,0 +1,67 @@ +package com.callv2.drive.infrastructure.aop.aspects.chain; + +import java.util.ArrayDeque; +import java.util.Objects; +import java.util.Queue; + +import com.callv2.drive.infrastructure.aop.aspects.context.MethodInvocationContext; +import com.callv2.drive.infrastructure.aop.aspects.context.PostInvocationContext; +import com.callv2.drive.infrastructure.aop.aspects.executor.MethodInvocationAspectExecutor; + +public final class MethodInvocationAspectExecutorChain { + + private MethodInvocationAspectExecutorChain next; + private final MethodInvocationAspectExecutor executor; + + private MethodInvocationAspectExecutorChain(final MethodInvocationAspectExecutor executor) { + this.executor = Objects.requireNonNull(executor, "'executor' cannot be null"); + } + + public PostInvocationContext execute(final MethodInvocationContext context) { + + executor.execute(context); + + if (next != null) + next.execute(context); + + return context.proceedWithContext(); + } + + private MethodInvocationAspectExecutorChain setNext(final MethodInvocationAspectExecutorChain next) { + return this.next = next; + } + + public static final class Builder { + + private final Queue chains; + + private Builder() { + this.chains = new ArrayDeque<>(); + } + + public static Builder create() { + return new Builder(); + } + + public Builder add(final MethodInvocationAspectExecutorChain chain) { + this.chains.add(chain); + return this; + } + + public MethodInvocationAspectExecutorChain build() { + if (chains.isEmpty()) + return null; + + final var firstChain = chains.poll(); + var chain = firstChain; + + do { + chain = chain.setNext(chains.poll()); + } while (!chains.isEmpty()); + + return firstChain; + } + + } + +} diff --git a/infrastructure/src/main/java/com/callv2/drive/infrastructure/aop/aspects/chain/PostInvocationAspectExecutorChain.java b/infrastructure/src/main/java/com/callv2/drive/infrastructure/aop/aspects/chain/PostInvocationAspectExecutorChain.java new file mode 100644 index 00000000..e8bb64e6 --- /dev/null +++ b/infrastructure/src/main/java/com/callv2/drive/infrastructure/aop/aspects/chain/PostInvocationAspectExecutorChain.java @@ -0,0 +1,68 @@ +package com.callv2.drive.infrastructure.aop.aspects.chain; + +import java.util.ArrayDeque; +import java.util.Objects; +import java.util.Queue; + +import com.callv2.drive.infrastructure.aop.aspects.context.PostInvocationContext; + +public final class PostInvocationAspectExecutorChain { + + private PostInvocationAspectExecutorChain next; + private final PostInvocationAspectExecutorChain executor; + + private PostInvocationAspectExecutorChain(final PostInvocationAspectExecutorChain executor) { + this.executor = Objects.requireNonNull(executor, "'executor' cannot be null"); + } + + public Object execute(final PostInvocationContext context) throws Throwable { + + executor.execute(context); + + if (next != null) + next.execute(context); + + if (context.wasSuccessful()) + return context.getResult(); + else + throw context.getThrowable(); + } + + private PostInvocationAspectExecutorChain setNext(final PostInvocationAspectExecutorChain next) { + return this.next = next; + } + + public static final class Builder { + + private final Queue chains; + + private Builder() { + this.chains = new ArrayDeque<>(); + } + + public static Builder create() { + return new Builder(); + } + + public Builder add(final PostInvocationAspectExecutorChain chain) { + this.chains.add(chain); + return this; + } + + public PostInvocationAspectExecutorChain build() { + if (chains.isEmpty()) + return null; + + final var firstChain = chains.poll(); + var chain = firstChain; + + do { + chain = chain.setNext(chains.poll()); + } while (!chains.isEmpty()); + + return firstChain; + } + + } + +} diff --git a/infrastructure/src/main/java/com/callv2/drive/infrastructure/aop/aspects/context/AbstractPostInvocationContext.java b/infrastructure/src/main/java/com/callv2/drive/infrastructure/aop/aspects/context/AbstractPostInvocationContext.java new file mode 100644 index 00000000..bdeae37f --- /dev/null +++ b/infrastructure/src/main/java/com/callv2/drive/infrastructure/aop/aspects/context/AbstractPostInvocationContext.java @@ -0,0 +1,44 @@ +package com.callv2.drive.infrastructure.aop.aspects.context; + +import java.time.Instant; +import java.util.concurrent.atomic.AtomicBoolean; + +public abstract class AbstractPostInvocationContext implements PostInvocationContext { + + private final Object result; + private final Throwable throwable; + private final Instant proceededAt; + private final AtomicBoolean successful; + + protected AbstractPostInvocationContext( + final Object result, + final Throwable throwable, + final Instant proceededAt, + final boolean successful) { + this.result = result; + this.throwable = throwable; + this.proceededAt = proceededAt; + this.successful = new AtomicBoolean(successful); + } + + @Override + public Instant getProceededAt() { + return proceededAt; + } + + @Override + public Object getResult() { + return result; + } + + @Override + public Throwable getThrowable() { + return throwable; + } + + @Override + public boolean wasSuccessful() { + return successful.get(); + } + +} diff --git a/infrastructure/src/main/java/com/callv2/drive/infrastructure/aop/aspects/context/AfterExecutionContext.java b/infrastructure/src/main/java/com/callv2/drive/infrastructure/aop/aspects/context/AfterExecutionContext.java deleted file mode 100644 index 5a87bf4f..00000000 --- a/infrastructure/src/main/java/com/callv2/drive/infrastructure/aop/aspects/context/AfterExecutionContext.java +++ /dev/null @@ -1,26 +0,0 @@ -package com.callv2.drive.infrastructure.aop.aspects.context; - -import java.time.Instant; - -import org.aspectj.lang.JoinPoint; - -public record AfterExecutionContext(Object result, JoinPoint joinPoint, Instant startedAt, Instant finishedAt) { - - public AfterExecutionContext { - - if (joinPoint == null) - throw new IllegalArgumentException("JoinPoint cannot be null"); - - if (startedAt == null) - throw new IllegalArgumentException("StartedAt time cannot be null"); - - if (finishedAt == null) - throw new IllegalArgumentException("FinishedAt time cannot be null"); - - } - - public static AfterExecutionContext of(final Object result, final BeforeExecutionContext beforeContext) { - return new AfterExecutionContext(result, beforeContext.joinPoint(), beforeContext.startTime(), Instant.now()); - } - -} diff --git a/infrastructure/src/main/java/com/callv2/drive/infrastructure/aop/aspects/context/BeforeExecutionContext.java b/infrastructure/src/main/java/com/callv2/drive/infrastructure/aop/aspects/context/BeforeExecutionContext.java deleted file mode 100644 index 3d31218e..00000000 --- a/infrastructure/src/main/java/com/callv2/drive/infrastructure/aop/aspects/context/BeforeExecutionContext.java +++ /dev/null @@ -1,23 +0,0 @@ -package com.callv2.drive.infrastructure.aop.aspects.context; - -import java.time.Instant; - -import org.aspectj.lang.JoinPoint; - -public record BeforeExecutionContext(JoinPoint joinPoint, Instant startTime) { - - public BeforeExecutionContext { - - if (joinPoint == null) - throw new IllegalArgumentException("JoinPoint cannot be null"); - - if (startTime == null) - throw new IllegalArgumentException("Start time cannot be null"); - - } - - public static BeforeExecutionContext of(JoinPoint joinPoint) { - return new BeforeExecutionContext(joinPoint, Instant.now()); - } - -} diff --git a/infrastructure/src/main/java/com/callv2/drive/infrastructure/aop/aspects/context/ExecutionErrorContext.java b/infrastructure/src/main/java/com/callv2/drive/infrastructure/aop/aspects/context/ExecutionErrorContext.java deleted file mode 100644 index 6e14ba74..00000000 --- a/infrastructure/src/main/java/com/callv2/drive/infrastructure/aop/aspects/context/ExecutionErrorContext.java +++ /dev/null @@ -1,19 +0,0 @@ -package com.callv2.drive.infrastructure.aop.aspects.context; - -import java.time.Instant; - -public record ExecutionErrorContext(BeforeExecutionContext executionContext, Throwable error, Instant occuredAt) { - - public ExecutionErrorContext { - if (executionContext == null) - throw new IllegalArgumentException("ExecutionContext cannot be null"); - - if (error == null) - throw new IllegalArgumentException("Error cannot be null"); - } - - public static ExecutionErrorContext of(BeforeExecutionContext executionContext, Throwable error) { - return new ExecutionErrorContext(executionContext, error, Instant.now()); - } - -} diff --git a/infrastructure/src/main/java/com/callv2/drive/infrastructure/aop/aspects/context/MethodInvocationContext.java b/infrastructure/src/main/java/com/callv2/drive/infrastructure/aop/aspects/context/MethodInvocationContext.java new file mode 100644 index 00000000..9ccaafb6 --- /dev/null +++ b/infrastructure/src/main/java/com/callv2/drive/infrastructure/aop/aspects/context/MethodInvocationContext.java @@ -0,0 +1,20 @@ +package com.callv2.drive.infrastructure.aop.aspects.context; + +import java.time.Instant; + +import jakarta.annotation.Nonnull; + +import org.aopalliance.intercept.MethodInvocation; + +public interface MethodInvocationContext extends MethodInvocation { + + @Nonnull + Instant getContextedAt(); + + @Nonnull + boolean proceeded(); + + @Nonnull + PostInvocationContext proceedWithContext(); + +} diff --git a/infrastructure/src/main/java/com/callv2/drive/infrastructure/aop/aspects/context/PostInvocationContext.java b/infrastructure/src/main/java/com/callv2/drive/infrastructure/aop/aspects/context/PostInvocationContext.java new file mode 100644 index 00000000..720ca2fc --- /dev/null +++ b/infrastructure/src/main/java/com/callv2/drive/infrastructure/aop/aspects/context/PostInvocationContext.java @@ -0,0 +1,22 @@ +package com.callv2.drive.infrastructure.aop.aspects.context; + +import java.time.Instant; + +import jakarta.annotation.Nonnull; +import jakarta.annotation.Nullable; + +public interface PostInvocationContext extends MethodInvocationContext { + + @Nullable + Instant getProceededAt(); + + @Nullable + Object getResult(); + + @Nullable + Throwable getThrowable(); + + @Nonnull + boolean wasSuccessful(); + +} diff --git a/infrastructure/src/main/java/com/callv2/drive/infrastructure/aop/aspects/context/SimplePostInvocationContext.java b/infrastructure/src/main/java/com/callv2/drive/infrastructure/aop/aspects/context/SimplePostInvocationContext.java new file mode 100644 index 00000000..7171fedb --- /dev/null +++ b/infrastructure/src/main/java/com/callv2/drive/infrastructure/aop/aspects/context/SimplePostInvocationContext.java @@ -0,0 +1,102 @@ +package com.callv2.drive.infrastructure.aop.aspects.context; + +import java.lang.reflect.AccessibleObject; +import java.lang.reflect.Method; +import java.time.Instant; +import java.util.Objects; + +import javax.annotation.Nonnull; +import javax.annotation.Nullable; + +public final class SimplePostInvocationContext extends AbstractPostInvocationContext { + + private final MethodInvocationContext methodInvocationContext; + + private SimplePostInvocationContext( + final Object result, + final Throwable throwable, + final Instant proceededAt, + final boolean successful, + final MethodInvocationContext methodInvocationContext) { + super(result, throwable, proceededAt, successful); + this.methodInvocationContext = Objects.requireNonNull(methodInvocationContext, + "'methodInvocationContext' must not be null"); + } + + public static final PostInvocationContext captureFromExecution( + final MethodInvocationContext methodInvocationContext) { + + Objects.requireNonNull(methodInvocationContext, "'methodInvocationContext' must not be null"); + + Object result; + Throwable throwable; + boolean successful; + final Instant proceededAt; + + try { + result = methodInvocationContext.proceed(); + throwable = null; + successful = true; + } catch (Throwable e) { + result = null; + throwable = e; + successful = true; + } finally { + proceededAt = Instant.now(); + } + + return new SimplePostInvocationContext( + result, + throwable, + proceededAt, + successful, + methodInvocationContext); + + } + + @Override + public Instant getContextedAt() { + return methodInvocationContext.getContextedAt(); + } + + @Override + public boolean proceeded() { + return true; + } + + @Override + public PostInvocationContext proceedWithContext() { + return this; + } + + @Override + @Nonnull + public Method getMethod() { + return methodInvocationContext.getMethod(); + } + + @Override + @Nonnull + public Object[] getArguments() { + return methodInvocationContext.getArguments(); + } + + @Override + @Nullable + public Object proceed() throws Throwable { + return methodInvocationContext.proceed(); + } + + @Override + @Nullable + public Object getThis() { + return methodInvocationContext.getThis(); + } + + @Override + @Nonnull + public AccessibleObject getStaticPart() { + return methodInvocationContext.getStaticPart(); + } + +} diff --git a/infrastructure/src/main/java/com/callv2/drive/infrastructure/aop/aspects/execution/after/LogAfterExecutionAspect.java b/infrastructure/src/main/java/com/callv2/drive/infrastructure/aop/aspects/execution/after/LogAfterExecutionAspect.java deleted file mode 100644 index 6b078542..00000000 --- a/infrastructure/src/main/java/com/callv2/drive/infrastructure/aop/aspects/execution/after/LogAfterExecutionAspect.java +++ /dev/null @@ -1,42 +0,0 @@ -package com.callv2.drive.infrastructure.aop.aspects.execution.after; - -import org.apache.logging.log4j.Level; -import org.apache.logging.log4j.LogManager; -import org.apache.logging.log4j.Logger; -import org.apache.logging.log4j.message.Message; - -import com.callv2.drive.infrastructure.aop.aspects.chain.AspectAfterExecutionChain; - -public abstract class LogAfterExecutionAspect extends AspectAfterExecutionChain { - - private final Logger logger; - - private final Level logLevel; - - public LogAfterExecutionAspect(final Level logLevel, Class clazz) { - super(); - this.logLevel = logLevel; - this.logger = LogManager.getLogger(clazz); - } - - protected Level level() { - return logLevel; - } - - public Logger logger() { - return logger; - } - - protected Message createMessage(final String message) { - return logger.getMessageFactory().newMessage(message); - } - - protected Message createMessage(final Object message) { - return logger.getMessageFactory().newMessage(message); - } - - protected Message createMessage(final String message, final Object... params) { - return logger.getMessageFactory().newMessage(message, params); - } - -} diff --git a/infrastructure/src/main/java/com/callv2/drive/infrastructure/aop/aspects/execution/after/TelemetryAfterLogAspect.java b/infrastructure/src/main/java/com/callv2/drive/infrastructure/aop/aspects/execution/after/TelemetryAfterLogAspect.java deleted file mode 100644 index b6efb6f7..00000000 --- a/infrastructure/src/main/java/com/callv2/drive/infrastructure/aop/aspects/execution/after/TelemetryAfterLogAspect.java +++ /dev/null @@ -1,27 +0,0 @@ -package com.callv2.drive.infrastructure.aop.aspects.execution.after; - -import java.time.Duration; - -import org.apache.logging.log4j.Level; - -import com.callv2.drive.infrastructure.aop.aspects.context.AfterExecutionContext; - -public class TelemetryAfterLogAspect extends LogAfterExecutionAspect { - - public TelemetryAfterLogAspect(final Level logLevel) { - super(logLevel, TelemetryAfterLogAspect.class); - } - - @Override - protected void execute(final AfterExecutionContext context) throws Throwable { - - final String methodSignature = context.joinPoint().getSignature().toShortString(); - final long elapsedMillis = Duration.between(context.startedAt(), context.finishedAt()).toMillis(); - final String message = String.format( - "Method [%s] executed in %d ms.", - methodSignature, - elapsedMillis); - - logger().log(level(), createMessage(message)); - } -} diff --git a/infrastructure/src/main/java/com/callv2/drive/infrastructure/aop/aspects/execution/before/ArgsLogAspect.java b/infrastructure/src/main/java/com/callv2/drive/infrastructure/aop/aspects/execution/before/ArgsLogAspect.java deleted file mode 100644 index a6583856..00000000 --- a/infrastructure/src/main/java/com/callv2/drive/infrastructure/aop/aspects/execution/before/ArgsLogAspect.java +++ /dev/null @@ -1,19 +0,0 @@ -package com.callv2.drive.infrastructure.aop.aspects.execution.before; - -import org.apache.logging.log4j.Level; - -import com.callv2.drive.infrastructure.aop.aspects.context.BeforeExecutionContext; - -public class ArgsLogAspect extends LogBeforeExecutionAspect { - - public ArgsLogAspect(final Level logLevel) { - super(logLevel, ArgsLogAspect.class); - } - - @Override - protected void execute(final BeforeExecutionContext context) throws Throwable { - for (Object arg : context.joinPoint().getArgs()) - logger().log(level(), createMessage("Arg: " + (arg != null ? arg.toString() : "null"))); - } - -} diff --git a/infrastructure/src/main/java/com/callv2/drive/infrastructure/aop/aspects/execution/before/LogBeforeExecutionAspect.java b/infrastructure/src/main/java/com/callv2/drive/infrastructure/aop/aspects/execution/before/LogBeforeExecutionAspect.java deleted file mode 100644 index f8c9da06..00000000 --- a/infrastructure/src/main/java/com/callv2/drive/infrastructure/aop/aspects/execution/before/LogBeforeExecutionAspect.java +++ /dev/null @@ -1,42 +0,0 @@ -package com.callv2.drive.infrastructure.aop.aspects.execution.before; - -import org.apache.logging.log4j.Level; -import org.apache.logging.log4j.LogManager; -import org.apache.logging.log4j.Logger; -import org.apache.logging.log4j.message.Message; - -import com.callv2.drive.infrastructure.aop.aspects.chain.AspectBeforeExecutionChain; - -public abstract class LogBeforeExecutionAspect extends AspectBeforeExecutionChain { - - private final Logger logger; - - private final Level logLevel; - - public LogBeforeExecutionAspect(final Level logLevel, Class clazz) { - super(); - this.logLevel = logLevel; - this.logger = LogManager.getLogger(clazz); - } - - protected Level level() { - return logLevel; - } - - public Logger logger() { - return logger; - } - - protected Message createMessage(final String message) { - return logger.getMessageFactory().newMessage(message); - } - - protected Message createMessage(final Object message) { - return logger.getMessageFactory().newMessage(message); - } - - protected Message createMessage(final String message, final Object... params) { - return logger.getMessageFactory().newMessage(message, params); - } - -} diff --git a/infrastructure/src/main/java/com/callv2/drive/infrastructure/aop/aspects/execution/before/SignatureLogAspect.java b/infrastructure/src/main/java/com/callv2/drive/infrastructure/aop/aspects/execution/before/SignatureLogAspect.java deleted file mode 100644 index 0e74a123..00000000 --- a/infrastructure/src/main/java/com/callv2/drive/infrastructure/aop/aspects/execution/before/SignatureLogAspect.java +++ /dev/null @@ -1,19 +0,0 @@ -package com.callv2.drive.infrastructure.aop.aspects.execution.before; - -import org.apache.logging.log4j.Level; - -import com.callv2.drive.infrastructure.aop.aspects.context.BeforeExecutionContext; - -public class SignatureLogAspect extends LogBeforeExecutionAspect { - - public SignatureLogAspect(final Level logLevel) { - super(logLevel, SignatureLogAspect.class); - } - - @Override - protected void execute(final BeforeExecutionContext context) throws Throwable { - logger().log(level(), - createMessage("SignatureLogAspect: " + context.joinPoint().getSignature().toLongString())); - } - -} diff --git a/infrastructure/src/main/java/com/callv2/drive/infrastructure/aop/aspects/execution/before/TelemetryBeforeLogAspect.java b/infrastructure/src/main/java/com/callv2/drive/infrastructure/aop/aspects/execution/before/TelemetryBeforeLogAspect.java deleted file mode 100644 index 87ac3b45..00000000 --- a/infrastructure/src/main/java/com/callv2/drive/infrastructure/aop/aspects/execution/before/TelemetryBeforeLogAspect.java +++ /dev/null @@ -1,25 +0,0 @@ -package com.callv2.drive.infrastructure.aop.aspects.execution.before; - -import java.time.Duration; -import java.time.Instant; - -import org.apache.logging.log4j.Level; - -import com.callv2.drive.infrastructure.aop.aspects.context.BeforeExecutionContext; - -public class TelemetryBeforeLogAspect extends LogBeforeExecutionAspect { - - public TelemetryBeforeLogAspect(final Level logLevel) { - super(logLevel, TelemetryBeforeLogAspect.class); - } - - @Override - protected void execute(final BeforeExecutionContext context) throws Throwable { - logger() - .log( - level(), - createMessage( - "ElapsedTimeProcessingAspectChain: " - + Duration.between(context.startTime(), Instant.now()).toNanos())); - } -} diff --git a/infrastructure/src/main/java/com/callv2/drive/infrastructure/aop/aspects/execution/error/LogExecutionErrorAspect.java b/infrastructure/src/main/java/com/callv2/drive/infrastructure/aop/aspects/execution/error/LogExecutionErrorAspect.java deleted file mode 100644 index 4372998c..00000000 --- a/infrastructure/src/main/java/com/callv2/drive/infrastructure/aop/aspects/execution/error/LogExecutionErrorAspect.java +++ /dev/null @@ -1,41 +0,0 @@ -package com.callv2.drive.infrastructure.aop.aspects.execution.error; - -import org.apache.logging.log4j.Level; -import org.apache.logging.log4j.LogManager; -import org.apache.logging.log4j.Logger; -import org.apache.logging.log4j.message.Message; - -import com.callv2.drive.infrastructure.aop.aspects.chain.AspectExecutionErrorChain; - -public abstract class LogExecutionErrorAspect extends AspectExecutionErrorChain { - - private final Logger logger; - - private final Level logLevel; - - public LogExecutionErrorAspect(final Level logLevel, Class clazz) { - super(); - this.logLevel = logLevel; - this.logger = LogManager.getLogger(clazz); - } - - protected Level level() { - return logLevel; - } - - public Logger logger() { - return logger; - } - - protected Message createMessage(final String message) { - return logger.getMessageFactory().newMessage(message); - } - - protected Message createMessage(final Object message) { - return logger.getMessageFactory().newMessage(message); - } - - protected Message createMessage(final String message, final Object... params) { - return logger.getMessageFactory().newMessage(message, params); - } -} diff --git a/infrastructure/src/main/java/com/callv2/drive/infrastructure/aop/aspects/execution/error/StackTraceLogAspect.java b/infrastructure/src/main/java/com/callv2/drive/infrastructure/aop/aspects/execution/error/StackTraceLogAspect.java deleted file mode 100644 index 0048ae68..00000000 --- a/infrastructure/src/main/java/com/callv2/drive/infrastructure/aop/aspects/execution/error/StackTraceLogAspect.java +++ /dev/null @@ -1,18 +0,0 @@ -package com.callv2.drive.infrastructure.aop.aspects.execution.error; - -import org.apache.logging.log4j.Level; - -import com.callv2.drive.infrastructure.aop.aspects.context.ExecutionErrorContext; - -public class StackTraceLogAspect extends LogExecutionErrorAspect { - - public StackTraceLogAspect(final Level logLevel) { - super(logLevel, StackTraceLogAspect.class); - } - - @Override - protected void execute(final ExecutionErrorContext context) throws Throwable { - logger().error(createMessage("StackTraceLogAspect: " + context.error().getMessage()), context.error()); - } - -} diff --git a/infrastructure/src/main/java/com/callv2/drive/infrastructure/aop/aspects/executor/MethodInvocationAspectExecutor.java b/infrastructure/src/main/java/com/callv2/drive/infrastructure/aop/aspects/executor/MethodInvocationAspectExecutor.java new file mode 100644 index 00000000..a4f2d895 --- /dev/null +++ b/infrastructure/src/main/java/com/callv2/drive/infrastructure/aop/aspects/executor/MethodInvocationAspectExecutor.java @@ -0,0 +1,10 @@ +package com.callv2.drive.infrastructure.aop.aspects.executor; + +import com.callv2.drive.infrastructure.aop.aspects.context.MethodInvocationContext; + +@FunctionalInterface +public interface MethodInvocationAspectExecutor { + + void execute(MethodInvocationContext context); + +} diff --git a/infrastructure/src/main/java/com/callv2/drive/infrastructure/aop/aspects/executor/PostInvocationContextAspectExecutor.java b/infrastructure/src/main/java/com/callv2/drive/infrastructure/aop/aspects/executor/PostInvocationContextAspectExecutor.java new file mode 100644 index 00000000..1f05749c --- /dev/null +++ b/infrastructure/src/main/java/com/callv2/drive/infrastructure/aop/aspects/executor/PostInvocationContextAspectExecutor.java @@ -0,0 +1,10 @@ +package com.callv2.drive.infrastructure.aop.aspects.executor; + +import com.callv2.drive.infrastructure.aop.aspects.context.PostInvocationContext; + +@FunctionalInterface +public interface PostInvocationContextAspectExecutor { + + void execute(PostInvocationContext context); + +} diff --git a/infrastructure/src/main/java/com/callv2/drive/infrastructure/configuration/aop/aspect/DynamicAspectConfig.java b/infrastructure/src/main/java/com/callv2/drive/infrastructure/configuration/aop/aspect/DynamicAspectConfig.java new file mode 100644 index 00000000..e0f0df60 --- /dev/null +++ b/infrastructure/src/main/java/com/callv2/drive/infrastructure/configuration/aop/aspect/DynamicAspectConfig.java @@ -0,0 +1,47 @@ +package com.callv2.drive.infrastructure.configuration.aop.aspect; + +import java.util.List; + +import org.aopalliance.intercept.MethodInterceptor; +import org.springframework.aop.Advisor; +import org.springframework.aop.aspectj.AspectJExpressionPointcut; +import org.springframework.aop.support.DefaultPointcutAdvisor; +import org.springframework.context.annotation.Bean; + +import com.callv2.drive.infrastructure.aop.aspects.chain.MethodInvocationAspectExecutorChain; +import com.callv2.drive.infrastructure.aop.aspects.chain.PostInvocationAspectExecutorChain; + +public class DynamicAspectConfig { + + @Bean + public List dynamicAdvisors( + MethodInvocationAspectExecutorChain beforeChain, + PostInvocationAspectExecutorChain afterChain, + PostInvocationAspectExecutorChain errorChain) { + + return List.of(); + // return properties.getDynamicLoggers().stream() + // .map(cfg -> buildAdvisor("cfg.getExpression()", beforeChain, afterChain, + // errorChain)) + // .toList(); + } + + private static Advisor buildAdvisor(final String expression, final MethodInterceptor interceptor) { + final AspectJExpressionPointcut pointcut = new AspectJExpressionPointcut(); + pointcut.setExpression(expression); + + // MethodInterceptor interceptor = invocation -> { + + // return invocation.proceed(); + + // return invocation.proceed(); + // // new MethodInvocationProceedingJoinPoint(invocation); + // // JoinPoint joinPoint = new MethodInvocationJoinPoint(invocation); + // // AbstractAroundAspectHandler handler = new + // // AbstractAroundAspectHandler(beforeChain, afterChain, errorChain); + // // return handler.defaultHandle(joinPoint); + // }; + + return new DefaultPointcutAdvisor(pointcut, interceptor); + } +} diff --git a/infrastructure/src/main/java/com/callv2/drive/infrastructure/configuration/logging/DomainLogConfig.java b/infrastructure/src/main/java/com/callv2/drive/infrastructure/configuration/logging/DomainLogConfig.java index 5cc4f93c..2826d2f7 100644 --- a/infrastructure/src/main/java/com/callv2/drive/infrastructure/configuration/logging/DomainLogConfig.java +++ b/infrastructure/src/main/java/com/callv2/drive/infrastructure/configuration/logging/DomainLogConfig.java @@ -1,37 +1,65 @@ package com.callv2.drive.infrastructure.configuration.logging; -import org.apache.logging.log4j.Level; import org.springframework.context.annotation.Configuration; -import com.callv2.drive.infrastructure.aop.aspects.chain.AspectAfterExecutionChain; -import com.callv2.drive.infrastructure.aop.aspects.chain.AspectBeforeExecutionChain; -import com.callv2.drive.infrastructure.aop.aspects.chain.AspectExecutionErrorChain; -import com.callv2.drive.infrastructure.aop.aspects.execution.after.TelemetryAfterLogAspect; -import com.callv2.drive.infrastructure.aop.aspects.execution.before.ArgsLogAspect; -import com.callv2.drive.infrastructure.aop.aspects.execution.before.SignatureLogAspect; -import com.callv2.drive.infrastructure.aop.aspects.execution.before.TelemetryBeforeLogAspect; -import com.callv2.drive.infrastructure.aop.aspects.execution.error.StackTraceLogAspect; -import com.callv2.drive.infrastructure.logging.domain.FolderLog; - @Configuration public class DomainLogConfig { - FolderLog folderLog() { - final var beforeChain = AspectBeforeExecutionChain.Builder.create() - .add(new SignatureLogAspect(Level.INFO)) - .add(new ArgsLogAspect(Level.INFO)) - .add(new TelemetryBeforeLogAspect(Level.INFO)) - .build(); +// @Bean +// FolderLog folderLog() { +// final var beforeChain = AspectBeforeExecutionChain.Builder.create() +// .add(new SignatureLogAspect(Level.INFO)) +// .add(new ArgsLogAspect(Level.INFO)) +// .add(new TelemetryBeforeLogAspect(Level.DEBUG)) +// .build(); + +// final var afterChain = AspectAfterExecutionChain.Builder.create() +// .add(new TelemetryAfterLogAspect(Level.INFO)) +// .build(); + +// final var errorChain = AspectExecutionErrorChain.Builder.create() +// .add(new StackTraceLogAspect(Level.INFO)) +// .build(); + +// return new FolderLog(beforeChain, afterChain, errorChain); +// } + +// @Bean +// FileLog fileLog() { +// final var beforeChain = AspectBeforeExecutionChain.Builder.create() +// .add(new SignatureLogAspect(Level.INFO)) +// .add(new ArgsLogAspect(Level.INFO)) +// // .add(new TelemetryBeforeLogAspect(Level.INFO)) +// .build(); + +// final var afterChain = AspectAfterExecutionChain.Builder.create() +// .add(new TelemetryAfterLogAspect(Level.INFO)) +// .build(); + +// final var errorChain = AspectExecutionErrorChain.Builder.create() +// .add(new StackTraceLogAspect(Level.INFO)) +// .build(); + +// return new FileLog(beforeChain, afterChain, errorChain); +// } + +// @Bean +// StorageLog storageLog() { +// final var beforeChain = AspectBeforeExecutionChain.Builder.create() +// .add(new SignatureLogAspect(Level.INFO)) +// .add(new ArgsLogAspect(Level.INFO)) +// // .add(new TelemetryBeforeLogAspect(Level.INFO)) +// .build(); - final var afterChain = AspectAfterExecutionChain.Builder.create() - .add(new TelemetryAfterLogAspect(Level.INFO)) - .build(); +// final var afterChain = AspectAfterExecutionChain.Builder.create() +// .add(new TelemetryAfterLogAspect(Level.INFO)) +// .build(); - final var errorChain = AspectExecutionErrorChain.Builder.create() - .add(new StackTraceLogAspect(Level.INFO)) - .build(); +// final var errorChain = AspectExecutionErrorChain.Builder.create() +// .add(new StackTraceLogAspect(Level.INFO)) +// .build(); - return new FolderLog(beforeChain, afterChain, errorChain); - } +// return new StorageLog(beforeChain, afterChain, errorChain); +// } } diff --git a/infrastructure/src/main/java/com/callv2/drive/infrastructure/logging/aspect/executor/Log4jExecutor.java b/infrastructure/src/main/java/com/callv2/drive/infrastructure/logging/aspect/executor/Log4jExecutor.java new file mode 100644 index 00000000..0acd5597 --- /dev/null +++ b/infrastructure/src/main/java/com/callv2/drive/infrastructure/logging/aspect/executor/Log4jExecutor.java @@ -0,0 +1,43 @@ +package com.callv2.drive.infrastructure.logging.aspect.executor; + +import org.apache.logging.log4j.Level; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; +import org.apache.logging.log4j.message.Message; + +public abstract class Log4jExecutor { + + private final Logger logger; + private final Level logLevel; + + public Log4jExecutor(final Level logLevel, Class clazz) { + super(); + this.logLevel = logLevel; + this.logger = LogManager.getLogger(clazz); + } + + public void log(final String message) { + logger.log(logLevel, createMessage(message)); + } + + public void log(final Object message) { + logger.log(logLevel, createMessage(message)); + } + + public void log(final String message, final Object... params) { + logger.log(logLevel, createMessage(message, params)); + } + + private Message createMessage(final String message) { + return logger.getMessageFactory().newMessage(message); + } + + private Message createMessage(final Object message) { + return logger.getMessageFactory().newMessage(message); + } + + private Message createMessage(final String message, final Object... params) { + return logger.getMessageFactory().newMessage(message, params); + } + +} diff --git a/infrastructure/src/main/java/com/callv2/drive/infrastructure/logging/aspect/executor/Log4jPostExecutor.java b/infrastructure/src/main/java/com/callv2/drive/infrastructure/logging/aspect/executor/Log4jPostExecutor.java new file mode 100644 index 00000000..3fc4497e --- /dev/null +++ b/infrastructure/src/main/java/com/callv2/drive/infrastructure/logging/aspect/executor/Log4jPostExecutor.java @@ -0,0 +1,13 @@ +package com.callv2.drive.infrastructure.logging.aspect.executor; + +import org.apache.logging.log4j.Level; + +import com.callv2.drive.infrastructure.aop.aspects.executor.PostInvocationContextAspectExecutor; + +public abstract class Log4jPostExecutor extends Log4jExecutor implements PostInvocationContextAspectExecutor { + + public Log4jPostExecutor(final Level logLevel, final Class clazz) { + super(logLevel, clazz); + } + +} diff --git a/infrastructure/src/main/java/com/callv2/drive/infrastructure/logging/aspect/executor/ThrowableLogExecutor.java b/infrastructure/src/main/java/com/callv2/drive/infrastructure/logging/aspect/executor/ThrowableLogExecutor.java new file mode 100644 index 00000000..256f0842 --- /dev/null +++ b/infrastructure/src/main/java/com/callv2/drive/infrastructure/logging/aspect/executor/ThrowableLogExecutor.java @@ -0,0 +1,26 @@ +package com.callv2.drive.infrastructure.logging.aspect.executor; + +import com.callv2.drive.infrastructure.aop.aspects.context.PostInvocationContext; +import org.apache.logging.log4j.Level; + +public class ThrowableLogExecutor extends Log4jPostExecutor { + + private ThrowableLogExecutor(final Level level, final Class clazz) { + super(level, clazz); + } + + public static ThrowableLogExecutor defaultExecutor(final Class clazz) { + return new ThrowableLogExecutor(Level.ERROR, clazz); + } + + public static ThrowableLogExecutor create(final Level level, final Class clazz) { + return new ThrowableLogExecutor(level, clazz); + } + + @Override + public void execute(final PostInvocationContext context) { + if (context.getThrowable() != null) + log(context.getThrowable()); + } + +} diff --git a/infrastructure/src/main/java/com/callv2/drive/infrastructure/logging/domain/FolderLog.java b/infrastructure/src/main/java/com/callv2/drive/infrastructure/logging/domain/FolderLog.java deleted file mode 100644 index 481cc890..00000000 --- a/infrastructure/src/main/java/com/callv2/drive/infrastructure/logging/domain/FolderLog.java +++ /dev/null @@ -1,26 +0,0 @@ -package com.callv2.drive.infrastructure.logging.domain; - -import org.aspectj.lang.JoinPoint; -import org.aspectj.lang.annotation.Around; - -import com.callv2.drive.infrastructure.aop.aspects.AbstractAroundAspectHandler; -import com.callv2.drive.infrastructure.aop.aspects.chain.AspectAfterExecutionChain; -import com.callv2.drive.infrastructure.aop.aspects.chain.AspectBeforeExecutionChain; -import com.callv2.drive.infrastructure.aop.aspects.chain.AspectExecutionErrorChain; - -public class FolderLog extends AbstractAroundAspectHandler { - - public FolderLog( - final AspectBeforeExecutionChain beforeChain, - final AspectAfterExecutionChain afterChain, - final AspectExecutionErrorChain errorChain) { - super(beforeChain, afterChain, errorChain); - } - - @Override - @Around("execution(* com.callv2.drive.domain.folder..*(..))") - public Object handle(JoinPoint joinPoint) throws Throwable { - return this.defaultHandle(joinPoint); - } - -} From 2574d6f857d0b0f2a84a36e8099f22c331eddd2b Mon Sep 17 00:00:00 2001 From: jhonatapers Date: Fri, 16 May 2025 23:34:20 -0300 Subject: [PATCH 27/53] feat: implement AspectExecutorChain and refactor method invocation chains for improved AOP structure --- .../aspects/chain/AspectExecutorChain.java | 68 +++++++++++++++++++ .../MethodInvocationAspectExecutorChain.java | 64 +++-------------- .../PostInvocationAspectExecutorChain.java | 67 +++--------------- .../aop/aspects/executor/AspectExecutor.java | 10 +++ .../executor/IdentifiableAspectExecutor.java | 11 +++ .../MethodInvocationAspectExecutor.java | 10 --- .../PostInvocationContextAspectExecutor.java | 10 --- .../aop/aspect/DynamicAspectConfig.java | 2 +- .../logging/application/TesteLog.java | 34 ++++++++++ .../logging/application/TesteLogOutro.java | 34 ++++++++++ .../aspect/executor/Log4jExecutor.java | 5 +- .../aspect/executor/Log4jPostExecutor.java | 4 +- .../aspect/executor/ThrowableLogExecutor.java | 2 +- 13 files changed, 183 insertions(+), 138 deletions(-) create mode 100644 infrastructure/src/main/java/com/callv2/drive/infrastructure/aop/aspects/chain/AspectExecutorChain.java create mode 100644 infrastructure/src/main/java/com/callv2/drive/infrastructure/aop/aspects/executor/AspectExecutor.java create mode 100644 infrastructure/src/main/java/com/callv2/drive/infrastructure/aop/aspects/executor/IdentifiableAspectExecutor.java delete mode 100644 infrastructure/src/main/java/com/callv2/drive/infrastructure/aop/aspects/executor/MethodInvocationAspectExecutor.java delete mode 100644 infrastructure/src/main/java/com/callv2/drive/infrastructure/aop/aspects/executor/PostInvocationContextAspectExecutor.java create mode 100644 infrastructure/src/main/java/com/callv2/drive/infrastructure/logging/application/TesteLog.java create mode 100644 infrastructure/src/main/java/com/callv2/drive/infrastructure/logging/application/TesteLogOutro.java diff --git a/infrastructure/src/main/java/com/callv2/drive/infrastructure/aop/aspects/chain/AspectExecutorChain.java b/infrastructure/src/main/java/com/callv2/drive/infrastructure/aop/aspects/chain/AspectExecutorChain.java new file mode 100644 index 00000000..f581deb6 --- /dev/null +++ b/infrastructure/src/main/java/com/callv2/drive/infrastructure/aop/aspects/chain/AspectExecutorChain.java @@ -0,0 +1,68 @@ +package com.callv2.drive.infrastructure.aop.aspects.chain; + +import java.util.ArrayDeque; +import java.util.Queue; + +import org.aopalliance.intercept.Joinpoint; + +import com.callv2.drive.infrastructure.aop.aspects.executor.AspectExecutor; + +public abstract class AspectExecutorChain> { + + private AspectExecutorChain next; + private final E executor; + + protected AspectExecutorChain(final E executor) { + this.executor = executor; + } + + public final O execute(J joinpoint) throws Throwable { + + executor.execute(joinpoint); + + if (next != null) + next.execute(joinpoint); + + return callsProceed(joinpoint); + } + + protected abstract O callsProceed(J joinpoint) throws Throwable; + + private AspectExecutorChain setNext(AspectExecutorChain next) { + return this.next = next; + } + + public static final class Builder> { + + private final Queue> chains; + + private Builder() { + this.chains = new ArrayDeque<>(); + } + + public static > Builder create() { + return new Builder(); + } + + public Builder add(final AspectExecutorChain chain) { + this.chains.add(chain); + return this; + } + + public AspectExecutorChain build() { + if (chains.isEmpty()) + return null; + + final var firstChain = chains.poll(); + var chain = firstChain; + + do { + chain = chain.setNext(chains.poll()); + } while (!chains.isEmpty()); + + return firstChain; + } + + } + +} diff --git a/infrastructure/src/main/java/com/callv2/drive/infrastructure/aop/aspects/chain/MethodInvocationAspectExecutorChain.java b/infrastructure/src/main/java/com/callv2/drive/infrastructure/aop/aspects/chain/MethodInvocationAspectExecutorChain.java index be623da5..06dcf5eb 100644 --- a/infrastructure/src/main/java/com/callv2/drive/infrastructure/aop/aspects/chain/MethodInvocationAspectExecutorChain.java +++ b/infrastructure/src/main/java/com/callv2/drive/infrastructure/aop/aspects/chain/MethodInvocationAspectExecutorChain.java @@ -1,67 +1,19 @@ package com.callv2.drive.infrastructure.aop.aspects.chain; -import java.util.ArrayDeque; -import java.util.Objects; -import java.util.Queue; - import com.callv2.drive.infrastructure.aop.aspects.context.MethodInvocationContext; import com.callv2.drive.infrastructure.aop.aspects.context.PostInvocationContext; -import com.callv2.drive.infrastructure.aop.aspects.executor.MethodInvocationAspectExecutor; - -public final class MethodInvocationAspectExecutorChain { - - private MethodInvocationAspectExecutorChain next; - private final MethodInvocationAspectExecutor executor; - - private MethodInvocationAspectExecutorChain(final MethodInvocationAspectExecutor executor) { - this.executor = Objects.requireNonNull(executor, "'executor' cannot be null"); - } +import com.callv2.drive.infrastructure.aop.aspects.executor.AspectExecutor; - public PostInvocationContext execute(final MethodInvocationContext context) { - - executor.execute(context); - - if (next != null) - next.execute(context); - - return context.proceedWithContext(); - } +public final class MethodInvocationAspectExecutorChain extends + AspectExecutorChain> { - private MethodInvocationAspectExecutorChain setNext(final MethodInvocationAspectExecutorChain next) { - return this.next = next; + protected MethodInvocationAspectExecutorChain(final AspectExecutor executor) { + super(executor); } - public static final class Builder { - - private final Queue chains; - - private Builder() { - this.chains = new ArrayDeque<>(); - } - - public static Builder create() { - return new Builder(); - } - - public Builder add(final MethodInvocationAspectExecutorChain chain) { - this.chains.add(chain); - return this; - } - - public MethodInvocationAspectExecutorChain build() { - if (chains.isEmpty()) - return null; - - final var firstChain = chains.poll(); - var chain = firstChain; - - do { - chain = chain.setNext(chains.poll()); - } while (!chains.isEmpty()); - - return firstChain; - } - + @Override + protected PostInvocationContext callsProceed(final MethodInvocationContext joinpoint) throws Throwable { + return joinpoint.proceedWithContext(); } } diff --git a/infrastructure/src/main/java/com/callv2/drive/infrastructure/aop/aspects/chain/PostInvocationAspectExecutorChain.java b/infrastructure/src/main/java/com/callv2/drive/infrastructure/aop/aspects/chain/PostInvocationAspectExecutorChain.java index e8bb64e6..8f77e619 100644 --- a/infrastructure/src/main/java/com/callv2/drive/infrastructure/aop/aspects/chain/PostInvocationAspectExecutorChain.java +++ b/infrastructure/src/main/java/com/callv2/drive/infrastructure/aop/aspects/chain/PostInvocationAspectExecutorChain.java @@ -1,68 +1,21 @@ package com.callv2.drive.infrastructure.aop.aspects.chain; -import java.util.ArrayDeque; -import java.util.Objects; -import java.util.Queue; - import com.callv2.drive.infrastructure.aop.aspects.context.PostInvocationContext; +import com.callv2.drive.infrastructure.aop.aspects.executor.AspectExecutor; -public final class PostInvocationAspectExecutorChain { - - private PostInvocationAspectExecutorChain next; - private final PostInvocationAspectExecutorChain executor; +public final class PostInvocationAspectExecutorChain extends + AspectExecutorChain> { - private PostInvocationAspectExecutorChain(final PostInvocationAspectExecutorChain executor) { - this.executor = Objects.requireNonNull(executor, "'executor' cannot be null"); + protected PostInvocationAspectExecutorChain(final AspectExecutor executor) { + super(executor); } - public Object execute(final PostInvocationContext context) throws Throwable { - - executor.execute(context); - - if (next != null) - next.execute(context); - - if (context.wasSuccessful()) - return context.getResult(); + @Override + protected Object callsProceed(final PostInvocationContext joinpoint) throws Throwable { + if (joinpoint.wasSuccessful()) + return joinpoint.getResult(); else - throw context.getThrowable(); - } - - private PostInvocationAspectExecutorChain setNext(final PostInvocationAspectExecutorChain next) { - return this.next = next; - } - - public static final class Builder { - - private final Queue chains; - - private Builder() { - this.chains = new ArrayDeque<>(); - } - - public static Builder create() { - return new Builder(); - } - - public Builder add(final PostInvocationAspectExecutorChain chain) { - this.chains.add(chain); - return this; - } - - public PostInvocationAspectExecutorChain build() { - if (chains.isEmpty()) - return null; - - final var firstChain = chains.poll(); - var chain = firstChain; - - do { - chain = chain.setNext(chains.poll()); - } while (!chains.isEmpty()); - - return firstChain; - } - + throw joinpoint.getThrowable(); } } diff --git a/infrastructure/src/main/java/com/callv2/drive/infrastructure/aop/aspects/executor/AspectExecutor.java b/infrastructure/src/main/java/com/callv2/drive/infrastructure/aop/aspects/executor/AspectExecutor.java new file mode 100644 index 00000000..9b7cf5da --- /dev/null +++ b/infrastructure/src/main/java/com/callv2/drive/infrastructure/aop/aspects/executor/AspectExecutor.java @@ -0,0 +1,10 @@ +package com.callv2.drive.infrastructure.aop.aspects.executor; + +import org.aopalliance.intercept.Joinpoint; + +@FunctionalInterface +public interface AspectExecutor { + + void execute(J joinPoint); + +} diff --git a/infrastructure/src/main/java/com/callv2/drive/infrastructure/aop/aspects/executor/IdentifiableAspectExecutor.java b/infrastructure/src/main/java/com/callv2/drive/infrastructure/aop/aspects/executor/IdentifiableAspectExecutor.java new file mode 100644 index 00000000..8a96e709 --- /dev/null +++ b/infrastructure/src/main/java/com/callv2/drive/infrastructure/aop/aspects/executor/IdentifiableAspectExecutor.java @@ -0,0 +1,11 @@ +package com.callv2.drive.infrastructure.aop.aspects.executor; + +import org.aopalliance.intercept.Joinpoint; + +public interface IdentifiableAspectExecutor extends AspectExecutor { + + default String getId() { + return getClass().getSimpleName(); + } + +} diff --git a/infrastructure/src/main/java/com/callv2/drive/infrastructure/aop/aspects/executor/MethodInvocationAspectExecutor.java b/infrastructure/src/main/java/com/callv2/drive/infrastructure/aop/aspects/executor/MethodInvocationAspectExecutor.java deleted file mode 100644 index a4f2d895..00000000 --- a/infrastructure/src/main/java/com/callv2/drive/infrastructure/aop/aspects/executor/MethodInvocationAspectExecutor.java +++ /dev/null @@ -1,10 +0,0 @@ -package com.callv2.drive.infrastructure.aop.aspects.executor; - -import com.callv2.drive.infrastructure.aop.aspects.context.MethodInvocationContext; - -@FunctionalInterface -public interface MethodInvocationAspectExecutor { - - void execute(MethodInvocationContext context); - -} diff --git a/infrastructure/src/main/java/com/callv2/drive/infrastructure/aop/aspects/executor/PostInvocationContextAspectExecutor.java b/infrastructure/src/main/java/com/callv2/drive/infrastructure/aop/aspects/executor/PostInvocationContextAspectExecutor.java deleted file mode 100644 index 1f05749c..00000000 --- a/infrastructure/src/main/java/com/callv2/drive/infrastructure/aop/aspects/executor/PostInvocationContextAspectExecutor.java +++ /dev/null @@ -1,10 +0,0 @@ -package com.callv2.drive.infrastructure.aop.aspects.executor; - -import com.callv2.drive.infrastructure.aop.aspects.context.PostInvocationContext; - -@FunctionalInterface -public interface PostInvocationContextAspectExecutor { - - void execute(PostInvocationContext context); - -} diff --git a/infrastructure/src/main/java/com/callv2/drive/infrastructure/configuration/aop/aspect/DynamicAspectConfig.java b/infrastructure/src/main/java/com/callv2/drive/infrastructure/configuration/aop/aspect/DynamicAspectConfig.java index e0f0df60..c3c2c8f0 100644 --- a/infrastructure/src/main/java/com/callv2/drive/infrastructure/configuration/aop/aspect/DynamicAspectConfig.java +++ b/infrastructure/src/main/java/com/callv2/drive/infrastructure/configuration/aop/aspect/DynamicAspectConfig.java @@ -13,7 +13,7 @@ public class DynamicAspectConfig { - @Bean + // @Bean public List dynamicAdvisors( MethodInvocationAspectExecutorChain beforeChain, PostInvocationAspectExecutorChain afterChain, diff --git a/infrastructure/src/main/java/com/callv2/drive/infrastructure/logging/application/TesteLog.java b/infrastructure/src/main/java/com/callv2/drive/infrastructure/logging/application/TesteLog.java new file mode 100644 index 00000000..a94abfac --- /dev/null +++ b/infrastructure/src/main/java/com/callv2/drive/infrastructure/logging/application/TesteLog.java @@ -0,0 +1,34 @@ +package com.callv2.drive.infrastructure.logging.application; + +import org.aspectj.lang.annotation.Aspect; +import org.springframework.stereotype.Component; + +// @Aspect +// @Component +public class TesteLog {//extends AbstractAroundAspectHandler { + +// protected TesteLog() { +// super( +// AspectBeforeExecutionChain.Builder +// .create() +// .add(new SignatureLogAspect(Level.INFO)) +// .add(new ArgsLogAspect(Level.INFO)) +// .add(new TelemetryBeforeLogAspect(Level.INFO)) +// .build(), +// AspectAfterExecutionChain.Builder +// .create() +// .add(new TelemetryAfterLogAspect(Level.INFO)) +// .build(), +// AspectExecutionErrorChain.Builder +// .create() +// .add(new StackTraceLogAspect(Level.INFO)) +// .build()); +// } + +// @Override +// @Around("execution(* com.callv2.drive.application..*(..))") +// public Object handle(final JoinPoint joinPoint) throws Throwable { +// return this.defaultHandle(joinPoint); +// } + +} diff --git a/infrastructure/src/main/java/com/callv2/drive/infrastructure/logging/application/TesteLogOutro.java b/infrastructure/src/main/java/com/callv2/drive/infrastructure/logging/application/TesteLogOutro.java new file mode 100644 index 00000000..92e54fce --- /dev/null +++ b/infrastructure/src/main/java/com/callv2/drive/infrastructure/logging/application/TesteLogOutro.java @@ -0,0 +1,34 @@ +package com.callv2.drive.infrastructure.logging.application; + +import org.aspectj.lang.annotation.Aspect; +import org.springframework.stereotype.Component; + +// @Aspect +// @Component +public class TesteLogOutro {//extends AbstractAroundAspectHandler { + +// protected TesteLogOutro() { +// super( +// AspectBeforeExecutionChain.Builder +// .create() +// .add(new SignatureLogAspect(Level.INFO)) +// .add(new ArgsLogAspect(Level.INFO)) +// .add(new TelemetryBeforeLogAspect(Level.INFO)) +// .build(), +// AspectAfterExecutionChain.Builder +// .create() +// .add(new TelemetryAfterLogAspect(Level.INFO)) +// .build(), +// AspectExecutionErrorChain.Builder +// .create() +// .add(new StackTraceLogAspect(Level.INFO)) +// .build()); +// } + +// @Override +// @Around("execution(* com.callv2.drive.application..*(..))") +// public Object handle(final JoinPoint joinPoint) throws Throwable { +// return this.defaultHandle(joinPoint); +// } + +} diff --git a/infrastructure/src/main/java/com/callv2/drive/infrastructure/logging/aspect/executor/Log4jExecutor.java b/infrastructure/src/main/java/com/callv2/drive/infrastructure/logging/aspect/executor/Log4jExecutor.java index 0acd5597..8f5102a6 100644 --- a/infrastructure/src/main/java/com/callv2/drive/infrastructure/logging/aspect/executor/Log4jExecutor.java +++ b/infrastructure/src/main/java/com/callv2/drive/infrastructure/logging/aspect/executor/Log4jExecutor.java @@ -1,11 +1,14 @@ package com.callv2.drive.infrastructure.logging.aspect.executor; +import org.aopalliance.intercept.Joinpoint; import org.apache.logging.log4j.Level; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; import org.apache.logging.log4j.message.Message; -public abstract class Log4jExecutor { +import com.callv2.drive.infrastructure.aop.aspects.executor.IdentifiableAspectExecutor; + +public abstract class Log4jExecutor implements IdentifiableAspectExecutor { private final Logger logger; private final Level logLevel; diff --git a/infrastructure/src/main/java/com/callv2/drive/infrastructure/logging/aspect/executor/Log4jPostExecutor.java b/infrastructure/src/main/java/com/callv2/drive/infrastructure/logging/aspect/executor/Log4jPostExecutor.java index 3fc4497e..9589a0e2 100644 --- a/infrastructure/src/main/java/com/callv2/drive/infrastructure/logging/aspect/executor/Log4jPostExecutor.java +++ b/infrastructure/src/main/java/com/callv2/drive/infrastructure/logging/aspect/executor/Log4jPostExecutor.java @@ -2,9 +2,9 @@ import org.apache.logging.log4j.Level; -import com.callv2.drive.infrastructure.aop.aspects.executor.PostInvocationContextAspectExecutor; +import com.callv2.drive.infrastructure.aop.aspects.context.PostInvocationContext; -public abstract class Log4jPostExecutor extends Log4jExecutor implements PostInvocationContextAspectExecutor { +public abstract class Log4jPostExecutor extends Log4jExecutor { public Log4jPostExecutor(final Level logLevel, final Class clazz) { super(logLevel, clazz); diff --git a/infrastructure/src/main/java/com/callv2/drive/infrastructure/logging/aspect/executor/ThrowableLogExecutor.java b/infrastructure/src/main/java/com/callv2/drive/infrastructure/logging/aspect/executor/ThrowableLogExecutor.java index 256f0842..d1a263b3 100644 --- a/infrastructure/src/main/java/com/callv2/drive/infrastructure/logging/aspect/executor/ThrowableLogExecutor.java +++ b/infrastructure/src/main/java/com/callv2/drive/infrastructure/logging/aspect/executor/ThrowableLogExecutor.java @@ -9,7 +9,7 @@ private ThrowableLogExecutor(final Level level, final Class clazz) { super(level, clazz); } - public static ThrowableLogExecutor defaultExecutor(final Class clazz) { + public static ThrowableLogExecutor defaultCreate(final Class clazz) { return new ThrowableLogExecutor(Level.ERROR, clazz); } From 8836f70023a2c12deb81da9cff3249f1f4de83bd Mon Sep 17 00:00:00 2001 From: jhonatapers Date: Sat, 17 May 2025 11:46:26 -0300 Subject: [PATCH 28/53] feat: enhance AOP structure with new context handling and logging executors; remove deprecated dynamic aspect configuration --- .../aspects/chain/AspectExecutorChain.java | 30 +++++---- .../MethodInvocationAspectExecutorChain.java | 6 +- .../PostInvocationAspectExecutorChain.java | 6 +- .../aop/aspects/chain/Proceeder.java | 10 +++ .../AbstractMethodInvocationContext.java | 23 +++++++ .../SimpleMethodInvocationContext.java | 64 +++++++++++++++++++ .../context/SimplePostInvocationContext.java | 4 +- ...leMethodInterceptorWithContextHandler.java | 46 +++++++++++++ .../aop/aspect/AspectConfig.java | 62 ++++++++++++++++++ .../aop/aspect/DynamicAspectConfig.java | 47 -------------- .../aspect/PointcutAdvisorProperties.java | 48 ++++++++++++++ .../logging/application/TesteLog.java | 34 ---------- .../logging/application/TesteLogOutro.java | 34 ---------- .../aspect/executor/Log4jPostExecutor.java | 13 ---- .../executor/MethodSignatureLogExecutor.java | 18 ++++++ .../executor/PostTelemetryLogExecutor.java | 22 +++++++ .../aspect/executor/ThrowableLogExecutor.java | 18 +++++- 17 files changed, 339 insertions(+), 146 deletions(-) create mode 100644 infrastructure/src/main/java/com/callv2/drive/infrastructure/aop/aspects/chain/Proceeder.java create mode 100644 infrastructure/src/main/java/com/callv2/drive/infrastructure/aop/aspects/context/AbstractMethodInvocationContext.java create mode 100644 infrastructure/src/main/java/com/callv2/drive/infrastructure/aop/aspects/context/SimpleMethodInvocationContext.java create mode 100644 infrastructure/src/main/java/com/callv2/drive/infrastructure/aop/aspects/handler/SimpleMethodInterceptorWithContextHandler.java create mode 100644 infrastructure/src/main/java/com/callv2/drive/infrastructure/configuration/aop/aspect/AspectConfig.java delete mode 100644 infrastructure/src/main/java/com/callv2/drive/infrastructure/configuration/aop/aspect/DynamicAspectConfig.java create mode 100644 infrastructure/src/main/java/com/callv2/drive/infrastructure/configuration/properties/aspect/PointcutAdvisorProperties.java delete mode 100644 infrastructure/src/main/java/com/callv2/drive/infrastructure/logging/application/TesteLog.java delete mode 100644 infrastructure/src/main/java/com/callv2/drive/infrastructure/logging/application/TesteLogOutro.java delete mode 100644 infrastructure/src/main/java/com/callv2/drive/infrastructure/logging/aspect/executor/Log4jPostExecutor.java create mode 100644 infrastructure/src/main/java/com/callv2/drive/infrastructure/logging/aspect/executor/MethodSignatureLogExecutor.java create mode 100644 infrastructure/src/main/java/com/callv2/drive/infrastructure/logging/aspect/executor/PostTelemetryLogExecutor.java diff --git a/infrastructure/src/main/java/com/callv2/drive/infrastructure/aop/aspects/chain/AspectExecutorChain.java b/infrastructure/src/main/java/com/callv2/drive/infrastructure/aop/aspects/chain/AspectExecutorChain.java index f581deb6..bbde5387 100644 --- a/infrastructure/src/main/java/com/callv2/drive/infrastructure/aop/aspects/chain/AspectExecutorChain.java +++ b/infrastructure/src/main/java/com/callv2/drive/infrastructure/aop/aspects/chain/AspectExecutorChain.java @@ -16,7 +16,7 @@ protected AspectExecutorChain(final E executor) { this.executor = executor; } - public final O execute(J joinpoint) throws Throwable { + public final O execute(final J joinpoint) throws Throwable { executor.execute(joinpoint); @@ -28,28 +28,36 @@ public final O execute(J joinpoint) throws Throwable { protected abstract O callsProceed(J joinpoint) throws Throwable; - private AspectExecutorChain setNext(AspectExecutorChain next) { - return this.next = next; + @SuppressWarnings("unchecked") + protected AspectExecutorChain setNext(final AspectExecutorChain next) { + return this.next = (AspectExecutorChain) next; } - public static final class Builder> { + public static final class Builder> { - private final Queue> chains; + private final Class clazz; + private final Queue chains; - private Builder() { + private Builder(final Class clazz) { + this.clazz = clazz; this.chains = new ArrayDeque<>(); } - public static > Builder create() { - return new Builder(); + public static > Builder create(final Class clazz) { + return new Builder(clazz); } - public Builder add(final AspectExecutorChain chain) { + public Builder add(final C chain) { + + if (chain.getClass() != clazz) + throw new IllegalArgumentException("Chain must be exactly of type " + clazz.getName()); + this.chains.add(chain); return this; } - public AspectExecutorChain build() { + @SuppressWarnings("unchecked") + public C build() { if (chains.isEmpty()) return null; @@ -57,7 +65,7 @@ public AspectExecutorChain build() { var chain = firstChain; do { - chain = chain.setNext(chains.poll()); + chain = (C) chain.setNext(chains.poll()); } while (!chains.isEmpty()); return firstChain; diff --git a/infrastructure/src/main/java/com/callv2/drive/infrastructure/aop/aspects/chain/MethodInvocationAspectExecutorChain.java b/infrastructure/src/main/java/com/callv2/drive/infrastructure/aop/aspects/chain/MethodInvocationAspectExecutorChain.java index 06dcf5eb..2f0e954d 100644 --- a/infrastructure/src/main/java/com/callv2/drive/infrastructure/aop/aspects/chain/MethodInvocationAspectExecutorChain.java +++ b/infrastructure/src/main/java/com/callv2/drive/infrastructure/aop/aspects/chain/MethodInvocationAspectExecutorChain.java @@ -7,10 +7,14 @@ public final class MethodInvocationAspectExecutorChain extends AspectExecutorChain> { - protected MethodInvocationAspectExecutorChain(final AspectExecutor executor) { + private MethodInvocationAspectExecutorChain(final AspectExecutor executor) { super(executor); } + public static MethodInvocationAspectExecutorChain with(final AspectExecutor executor) { + return new MethodInvocationAspectExecutorChain(executor); + } + @Override protected PostInvocationContext callsProceed(final MethodInvocationContext joinpoint) throws Throwable { return joinpoint.proceedWithContext(); diff --git a/infrastructure/src/main/java/com/callv2/drive/infrastructure/aop/aspects/chain/PostInvocationAspectExecutorChain.java b/infrastructure/src/main/java/com/callv2/drive/infrastructure/aop/aspects/chain/PostInvocationAspectExecutorChain.java index 8f77e619..8f4a953b 100644 --- a/infrastructure/src/main/java/com/callv2/drive/infrastructure/aop/aspects/chain/PostInvocationAspectExecutorChain.java +++ b/infrastructure/src/main/java/com/callv2/drive/infrastructure/aop/aspects/chain/PostInvocationAspectExecutorChain.java @@ -6,10 +6,14 @@ public final class PostInvocationAspectExecutorChain extends AspectExecutorChain> { - protected PostInvocationAspectExecutorChain(final AspectExecutor executor) { + private PostInvocationAspectExecutorChain(final AspectExecutor executor) { super(executor); } + public static PostInvocationAspectExecutorChain with(final AspectExecutor executor) { + return new PostInvocationAspectExecutorChain(executor); + } + @Override protected Object callsProceed(final PostInvocationContext joinpoint) throws Throwable { if (joinpoint.wasSuccessful()) diff --git a/infrastructure/src/main/java/com/callv2/drive/infrastructure/aop/aspects/chain/Proceeder.java b/infrastructure/src/main/java/com/callv2/drive/infrastructure/aop/aspects/chain/Proceeder.java new file mode 100644 index 00000000..1dad486c --- /dev/null +++ b/infrastructure/src/main/java/com/callv2/drive/infrastructure/aop/aspects/chain/Proceeder.java @@ -0,0 +1,10 @@ +package com.callv2.drive.infrastructure.aop.aspects.chain; + +import org.aopalliance.intercept.Joinpoint; + +@FunctionalInterface +public interface Proceeder { + + O proceed(J joinpoint) throws Throwable; + +} \ No newline at end of file diff --git a/infrastructure/src/main/java/com/callv2/drive/infrastructure/aop/aspects/context/AbstractMethodInvocationContext.java b/infrastructure/src/main/java/com/callv2/drive/infrastructure/aop/aspects/context/AbstractMethodInvocationContext.java new file mode 100644 index 00000000..4ed024ca --- /dev/null +++ b/infrastructure/src/main/java/com/callv2/drive/infrastructure/aop/aspects/context/AbstractMethodInvocationContext.java @@ -0,0 +1,23 @@ +package com.callv2.drive.infrastructure.aop.aspects.context; + +import java.time.Instant; + +public abstract class AbstractMethodInvocationContext implements MethodInvocationContext { + + private final Instant contextedAt; + + protected AbstractMethodInvocationContext() { + this.contextedAt = Instant.now(); + } + + @Override + public Instant getContextedAt() { + return contextedAt; + } + + @Override + public PostInvocationContext proceedWithContext() { + return SimplePostInvocationContext.captureFromExecution(this); + } + +} diff --git a/infrastructure/src/main/java/com/callv2/drive/infrastructure/aop/aspects/context/SimpleMethodInvocationContext.java b/infrastructure/src/main/java/com/callv2/drive/infrastructure/aop/aspects/context/SimpleMethodInvocationContext.java new file mode 100644 index 00000000..b5e5d4ea --- /dev/null +++ b/infrastructure/src/main/java/com/callv2/drive/infrastructure/aop/aspects/context/SimpleMethodInvocationContext.java @@ -0,0 +1,64 @@ +package com.callv2.drive.infrastructure.aop.aspects.context; + +import java.lang.reflect.AccessibleObject; +import java.lang.reflect.Method; +import java.util.concurrent.atomic.AtomicBoolean; + +import javax.annotation.Nonnull; +import javax.annotation.Nullable; + +import org.aopalliance.intercept.MethodInvocation; + +public final class SimpleMethodInvocationContext extends AbstractMethodInvocationContext { + + private final AtomicBoolean proceeded; + private final MethodInvocation methodInvocation; + + private SimpleMethodInvocationContext(final MethodInvocation methodInvocation) { + super(); + this.proceeded = new AtomicBoolean(false); + this.methodInvocation = methodInvocation; + } + + public static SimpleMethodInvocationContext of(final MethodInvocation methodInvocation) { + return new SimpleMethodInvocationContext(methodInvocation); + } + + @Override + public boolean proceeded() { + return proceeded.get(); + } + + @Override + @Nonnull + public Method getMethod() { + return methodInvocation.getMethod(); + } + + @Override + @Nonnull + public Object[] getArguments() { + return methodInvocation.getArguments(); + } + + @Override + @Nullable + public Object proceed() throws Throwable { + if (proceeded.getAndSet(true)) + throw new IllegalStateException("Method already proceeded"); + return methodInvocation.proceed(); + } + + @Override + @Nullable + public Object getThis() { + return methodInvocation.getThis(); + } + + @Override + @Nonnull + public AccessibleObject getStaticPart() { + return methodInvocation.getStaticPart(); + } + +} diff --git a/infrastructure/src/main/java/com/callv2/drive/infrastructure/aop/aspects/context/SimplePostInvocationContext.java b/infrastructure/src/main/java/com/callv2/drive/infrastructure/aop/aspects/context/SimplePostInvocationContext.java index 7171fedb..083c45e2 100644 --- a/infrastructure/src/main/java/com/callv2/drive/infrastructure/aop/aspects/context/SimplePostInvocationContext.java +++ b/infrastructure/src/main/java/com/callv2/drive/infrastructure/aop/aspects/context/SimplePostInvocationContext.java @@ -40,7 +40,7 @@ public static final PostInvocationContext captureFromExecution( } catch (Throwable e) { result = null; throwable = e; - successful = true; + successful = false; } finally { proceededAt = Instant.now(); } @@ -61,7 +61,7 @@ public Instant getContextedAt() { @Override public boolean proceeded() { - return true; + return methodInvocationContext.proceeded(); } @Override diff --git a/infrastructure/src/main/java/com/callv2/drive/infrastructure/aop/aspects/handler/SimpleMethodInterceptorWithContextHandler.java b/infrastructure/src/main/java/com/callv2/drive/infrastructure/aop/aspects/handler/SimpleMethodInterceptorWithContextHandler.java new file mode 100644 index 00000000..67cc23c8 --- /dev/null +++ b/infrastructure/src/main/java/com/callv2/drive/infrastructure/aop/aspects/handler/SimpleMethodInterceptorWithContextHandler.java @@ -0,0 +1,46 @@ +package com.callv2.drive.infrastructure.aop.aspects.handler; + +import javax.annotation.Nonnull; +import javax.annotation.Nullable; + +import org.aopalliance.intercept.MethodInterceptor; +import org.aopalliance.intercept.MethodInvocation; + +import com.callv2.drive.infrastructure.aop.aspects.chain.MethodInvocationAspectExecutorChain; +import com.callv2.drive.infrastructure.aop.aspects.chain.PostInvocationAspectExecutorChain; +import com.callv2.drive.infrastructure.aop.aspects.context.MethodInvocationContext; +import com.callv2.drive.infrastructure.aop.aspects.context.PostInvocationContext; +import com.callv2.drive.infrastructure.aop.aspects.context.SimpleMethodInvocationContext; + +public final class SimpleMethodInterceptorWithContextHandler implements MethodInterceptor { + + private final MethodInvocationAspectExecutorChain beforeChain; + private final PostInvocationAspectExecutorChain afterChain; + private final PostInvocationAspectExecutorChain errorChain; + + public SimpleMethodInterceptorWithContextHandler( + final MethodInvocationAspectExecutorChain beforeChain, + final PostInvocationAspectExecutorChain afterChain, + final PostInvocationAspectExecutorChain errorChain) { + this.beforeChain = beforeChain; + this.afterChain = afterChain; + this.errorChain = errorChain; + } + + @Override + @Nullable + public Object invoke(@Nonnull MethodInvocation invocation) throws Throwable { + + final MethodInvocationContext context = SimpleMethodInvocationContext.of(invocation); + + final PostInvocationContext postInvaotionResult = beforeChain.execute(context); + + if (postInvaotionResult.wasSuccessful()) + return afterChain.execute(postInvaotionResult); + else + errorChain.execute(postInvaotionResult); + + throw postInvaotionResult.getThrowable(); + } + +} diff --git a/infrastructure/src/main/java/com/callv2/drive/infrastructure/configuration/aop/aspect/AspectConfig.java b/infrastructure/src/main/java/com/callv2/drive/infrastructure/configuration/aop/aspect/AspectConfig.java new file mode 100644 index 00000000..720249b6 --- /dev/null +++ b/infrastructure/src/main/java/com/callv2/drive/infrastructure/configuration/aop/aspect/AspectConfig.java @@ -0,0 +1,62 @@ +package com.callv2.drive.infrastructure.configuration.aop.aspect; + +import org.aopalliance.intercept.MethodInterceptor; +import org.apache.logging.log4j.Level; +import org.springframework.aop.Advisor; +import org.springframework.aop.aspectj.AspectJExpressionPointcut; +import org.springframework.aop.support.DefaultPointcutAdvisor; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; + +import com.callv2.drive.infrastructure.aop.aspects.chain.AspectExecutorChain; +import com.callv2.drive.infrastructure.aop.aspects.chain.MethodInvocationAspectExecutorChain; +import com.callv2.drive.infrastructure.aop.aspects.chain.PostInvocationAspectExecutorChain; +import com.callv2.drive.infrastructure.aop.aspects.handler.SimpleMethodInterceptorWithContextHandler; +import com.callv2.drive.infrastructure.logging.aspect.executor.MethodSignatureLogExecutor; +import com.callv2.drive.infrastructure.logging.aspect.executor.PostTelemetryLogExecutor; +import com.callv2.drive.infrastructure.logging.aspect.executor.ThrowableLogExecutor; + +@Configuration +public class AspectConfig { + + @Bean + Advisor applicationAdvisor() { + + final MethodInvocationAspectExecutorChain beforeChain = AspectExecutorChain.Builder + .create(MethodInvocationAspectExecutorChain.class) + .add(MethodInvocationAspectExecutorChain + .with(new MethodSignatureLogExecutor(Level.INFO, MethodSignatureLogExecutor.class))) + .build(); + + final PostInvocationAspectExecutorChain afterChain = AspectExecutorChain.Builder + .create(PostInvocationAspectExecutorChain.class) + .add(PostInvocationAspectExecutorChain + .with(new PostTelemetryLogExecutor(Level.INFO, PostTelemetryLogExecutor.class))) + .build(); + + final PostInvocationAspectExecutorChain errorChain = AspectExecutorChain.Builder + .create(PostInvocationAspectExecutorChain.class) + .add(PostInvocationAspectExecutorChain + .with(ThrowableLogExecutor.defaultCreate(ThrowableLogExecutor.class))) + .build(); + + final var ahamm = new SimpleMethodInterceptorWithContextHandler(beforeChain, afterChain, errorChain); + + return applicationAdvisor("execution(* com.callv2.drive.application..*.*(..))", ahamm); + } + + private static Advisor applicationAdvisor( + final String expression, + final MethodInterceptor interceptor) { + + // final String expression = "execution(* com.callv2.drive..*.*(..))"; + + final AspectJExpressionPointcut pointcut = new AspectJExpressionPointcut(); + pointcut.setExpression(expression); + + return new DefaultPointcutAdvisor( + pointcut, + interceptor); + } + +} diff --git a/infrastructure/src/main/java/com/callv2/drive/infrastructure/configuration/aop/aspect/DynamicAspectConfig.java b/infrastructure/src/main/java/com/callv2/drive/infrastructure/configuration/aop/aspect/DynamicAspectConfig.java deleted file mode 100644 index c3c2c8f0..00000000 --- a/infrastructure/src/main/java/com/callv2/drive/infrastructure/configuration/aop/aspect/DynamicAspectConfig.java +++ /dev/null @@ -1,47 +0,0 @@ -package com.callv2.drive.infrastructure.configuration.aop.aspect; - -import java.util.List; - -import org.aopalliance.intercept.MethodInterceptor; -import org.springframework.aop.Advisor; -import org.springframework.aop.aspectj.AspectJExpressionPointcut; -import org.springframework.aop.support.DefaultPointcutAdvisor; -import org.springframework.context.annotation.Bean; - -import com.callv2.drive.infrastructure.aop.aspects.chain.MethodInvocationAspectExecutorChain; -import com.callv2.drive.infrastructure.aop.aspects.chain.PostInvocationAspectExecutorChain; - -public class DynamicAspectConfig { - - // @Bean - public List dynamicAdvisors( - MethodInvocationAspectExecutorChain beforeChain, - PostInvocationAspectExecutorChain afterChain, - PostInvocationAspectExecutorChain errorChain) { - - return List.of(); - // return properties.getDynamicLoggers().stream() - // .map(cfg -> buildAdvisor("cfg.getExpression()", beforeChain, afterChain, - // errorChain)) - // .toList(); - } - - private static Advisor buildAdvisor(final String expression, final MethodInterceptor interceptor) { - final AspectJExpressionPointcut pointcut = new AspectJExpressionPointcut(); - pointcut.setExpression(expression); - - // MethodInterceptor interceptor = invocation -> { - - // return invocation.proceed(); - - // return invocation.proceed(); - // // new MethodInvocationProceedingJoinPoint(invocation); - // // JoinPoint joinPoint = new MethodInvocationJoinPoint(invocation); - // // AbstractAroundAspectHandler handler = new - // // AbstractAroundAspectHandler(beforeChain, afterChain, errorChain); - // // return handler.defaultHandle(joinPoint); - // }; - - return new DefaultPointcutAdvisor(pointcut, interceptor); - } -} diff --git a/infrastructure/src/main/java/com/callv2/drive/infrastructure/configuration/properties/aspect/PointcutAdvisorProperties.java b/infrastructure/src/main/java/com/callv2/drive/infrastructure/configuration/properties/aspect/PointcutAdvisorProperties.java new file mode 100644 index 00000000..c930113e --- /dev/null +++ b/infrastructure/src/main/java/com/callv2/drive/infrastructure/configuration/properties/aspect/PointcutAdvisorProperties.java @@ -0,0 +1,48 @@ +package com.callv2.drive.infrastructure.configuration.properties.aspect; + +import java.util.List; + +public class PointcutAdvisorProperties { + + private String expression; + + private List before; + private List after; + private List error; + + public PointcutAdvisorProperties() { + } + + public String getExpression() { + return expression; + } + + public void setExpression(String expression) { + this.expression = expression; + } + + public List getBefore() { + return before; + } + + public void setBefore(List before) { + this.before = before; + } + + public List getAfter() { + return after; + } + + public void setAfter(List after) { + this.after = after; + } + + public List getError() { + return error; + } + + public void setError(List error) { + this.error = error; + } + +} diff --git a/infrastructure/src/main/java/com/callv2/drive/infrastructure/logging/application/TesteLog.java b/infrastructure/src/main/java/com/callv2/drive/infrastructure/logging/application/TesteLog.java deleted file mode 100644 index a94abfac..00000000 --- a/infrastructure/src/main/java/com/callv2/drive/infrastructure/logging/application/TesteLog.java +++ /dev/null @@ -1,34 +0,0 @@ -package com.callv2.drive.infrastructure.logging.application; - -import org.aspectj.lang.annotation.Aspect; -import org.springframework.stereotype.Component; - -// @Aspect -// @Component -public class TesteLog {//extends AbstractAroundAspectHandler { - -// protected TesteLog() { -// super( -// AspectBeforeExecutionChain.Builder -// .create() -// .add(new SignatureLogAspect(Level.INFO)) -// .add(new ArgsLogAspect(Level.INFO)) -// .add(new TelemetryBeforeLogAspect(Level.INFO)) -// .build(), -// AspectAfterExecutionChain.Builder -// .create() -// .add(new TelemetryAfterLogAspect(Level.INFO)) -// .build(), -// AspectExecutionErrorChain.Builder -// .create() -// .add(new StackTraceLogAspect(Level.INFO)) -// .build()); -// } - -// @Override -// @Around("execution(* com.callv2.drive.application..*(..))") -// public Object handle(final JoinPoint joinPoint) throws Throwable { -// return this.defaultHandle(joinPoint); -// } - -} diff --git a/infrastructure/src/main/java/com/callv2/drive/infrastructure/logging/application/TesteLogOutro.java b/infrastructure/src/main/java/com/callv2/drive/infrastructure/logging/application/TesteLogOutro.java deleted file mode 100644 index 92e54fce..00000000 --- a/infrastructure/src/main/java/com/callv2/drive/infrastructure/logging/application/TesteLogOutro.java +++ /dev/null @@ -1,34 +0,0 @@ -package com.callv2.drive.infrastructure.logging.application; - -import org.aspectj.lang.annotation.Aspect; -import org.springframework.stereotype.Component; - -// @Aspect -// @Component -public class TesteLogOutro {//extends AbstractAroundAspectHandler { - -// protected TesteLogOutro() { -// super( -// AspectBeforeExecutionChain.Builder -// .create() -// .add(new SignatureLogAspect(Level.INFO)) -// .add(new ArgsLogAspect(Level.INFO)) -// .add(new TelemetryBeforeLogAspect(Level.INFO)) -// .build(), -// AspectAfterExecutionChain.Builder -// .create() -// .add(new TelemetryAfterLogAspect(Level.INFO)) -// .build(), -// AspectExecutionErrorChain.Builder -// .create() -// .add(new StackTraceLogAspect(Level.INFO)) -// .build()); -// } - -// @Override -// @Around("execution(* com.callv2.drive.application..*(..))") -// public Object handle(final JoinPoint joinPoint) throws Throwable { -// return this.defaultHandle(joinPoint); -// } - -} diff --git a/infrastructure/src/main/java/com/callv2/drive/infrastructure/logging/aspect/executor/Log4jPostExecutor.java b/infrastructure/src/main/java/com/callv2/drive/infrastructure/logging/aspect/executor/Log4jPostExecutor.java deleted file mode 100644 index 9589a0e2..00000000 --- a/infrastructure/src/main/java/com/callv2/drive/infrastructure/logging/aspect/executor/Log4jPostExecutor.java +++ /dev/null @@ -1,13 +0,0 @@ -package com.callv2.drive.infrastructure.logging.aspect.executor; - -import org.apache.logging.log4j.Level; - -import com.callv2.drive.infrastructure.aop.aspects.context.PostInvocationContext; - -public abstract class Log4jPostExecutor extends Log4jExecutor { - - public Log4jPostExecutor(final Level logLevel, final Class clazz) { - super(logLevel, clazz); - } - -} diff --git a/infrastructure/src/main/java/com/callv2/drive/infrastructure/logging/aspect/executor/MethodSignatureLogExecutor.java b/infrastructure/src/main/java/com/callv2/drive/infrastructure/logging/aspect/executor/MethodSignatureLogExecutor.java new file mode 100644 index 00000000..d3b4ff66 --- /dev/null +++ b/infrastructure/src/main/java/com/callv2/drive/infrastructure/logging/aspect/executor/MethodSignatureLogExecutor.java @@ -0,0 +1,18 @@ +package com.callv2.drive.infrastructure.logging.aspect.executor; + +import org.apache.logging.log4j.Level; + +import com.callv2.drive.infrastructure.aop.aspects.context.MethodInvocationContext; + +public class MethodSignatureLogExecutor extends Log4jExecutor { + + public MethodSignatureLogExecutor(final Level level, final Class clazz) { + super(level, clazz); + } + + @Override + public void execute(final MethodInvocationContext context) { + log("<>: [" + context.getMethod().toString() + "]"); + } + +} diff --git a/infrastructure/src/main/java/com/callv2/drive/infrastructure/logging/aspect/executor/PostTelemetryLogExecutor.java b/infrastructure/src/main/java/com/callv2/drive/infrastructure/logging/aspect/executor/PostTelemetryLogExecutor.java new file mode 100644 index 00000000..8d6df7f6 --- /dev/null +++ b/infrastructure/src/main/java/com/callv2/drive/infrastructure/logging/aspect/executor/PostTelemetryLogExecutor.java @@ -0,0 +1,22 @@ +package com.callv2.drive.infrastructure.logging.aspect.executor; + +import java.time.Duration; + +import org.apache.logging.log4j.Level; + +import com.callv2.drive.infrastructure.aop.aspects.context.PostInvocationContext; + +public class PostTelemetryLogExecutor extends Log4jExecutor { + + public PostTelemetryLogExecutor(final Level logLevel, final Class clazz) { + super(logLevel, clazz); + } + + @Override + public void execute(final PostInvocationContext context) { + log("<> " + + Duration.between(context.getContextedAt(), context.getProceededAt()).toMillis() + " ms " + + "<> " + "[" + context.getMethod().toString() + "]"); + } + +} diff --git a/infrastructure/src/main/java/com/callv2/drive/infrastructure/logging/aspect/executor/ThrowableLogExecutor.java b/infrastructure/src/main/java/com/callv2/drive/infrastructure/logging/aspect/executor/ThrowableLogExecutor.java index d1a263b3..0122a9f6 100644 --- a/infrastructure/src/main/java/com/callv2/drive/infrastructure/logging/aspect/executor/ThrowableLogExecutor.java +++ b/infrastructure/src/main/java/com/callv2/drive/infrastructure/logging/aspect/executor/ThrowableLogExecutor.java @@ -3,7 +3,7 @@ import com.callv2.drive.infrastructure.aop.aspects.context.PostInvocationContext; import org.apache.logging.log4j.Level; -public class ThrowableLogExecutor extends Log4jPostExecutor { +public class ThrowableLogExecutor extends Log4jExecutor { private ThrowableLogExecutor(final Level level, final Class clazz) { super(level, clazz); @@ -19,8 +19,20 @@ public static ThrowableLogExecutor create(final Level level, final Class claz @Override public void execute(final PostInvocationContext context) { - if (context.getThrowable() != null) - log(context.getThrowable()); + if (context.getThrowable() == null) + return; + + final String message = """ + <> [%s] + <> [%s] + <> [%s] + """ + .formatted( + context.getThrowable().getClass().getName(), + context.getThrowable().getMessage(), + context.getMethod().toString()); + + log(message, context.getThrowable()); } } From 2c5817daf5a18110d97688a8059c32fd0e6cbed8 Mon Sep 17 00:00:00 2001 From: jhonatapers Date: Sat, 17 May 2025 11:52:19 -0300 Subject: [PATCH 29/53] feat: change package --- .../aspects}/executor/Log4jExecutor.java | 4 +--- .../executor/MethodSignatureLogExecutor.java | 2 +- .../executor/PostTelemetryLogExecutor.java | 2 +- .../executor/ThrowableLogExecutor.java | 2 +- .../aop/aspect/AspectConfig.java | 20 +++++++++---------- 5 files changed, 13 insertions(+), 17 deletions(-) rename infrastructure/src/main/java/com/callv2/drive/infrastructure/{logging/aspect => aop/aspects}/executor/Log4jExecutor.java (89%) rename infrastructure/src/main/java/com/callv2/drive/infrastructure/{logging/aspect => aop/aspects}/executor/MethodSignatureLogExecutor.java (88%) rename infrastructure/src/main/java/com/callv2/drive/infrastructure/{logging/aspect => aop/aspects}/executor/PostTelemetryLogExecutor.java (91%) rename infrastructure/src/main/java/com/callv2/drive/infrastructure/{logging/aspect => aop/aspects}/executor/ThrowableLogExecutor.java (94%) diff --git a/infrastructure/src/main/java/com/callv2/drive/infrastructure/logging/aspect/executor/Log4jExecutor.java b/infrastructure/src/main/java/com/callv2/drive/infrastructure/aop/aspects/executor/Log4jExecutor.java similarity index 89% rename from infrastructure/src/main/java/com/callv2/drive/infrastructure/logging/aspect/executor/Log4jExecutor.java rename to infrastructure/src/main/java/com/callv2/drive/infrastructure/aop/aspects/executor/Log4jExecutor.java index 8f5102a6..16195a1f 100644 --- a/infrastructure/src/main/java/com/callv2/drive/infrastructure/logging/aspect/executor/Log4jExecutor.java +++ b/infrastructure/src/main/java/com/callv2/drive/infrastructure/aop/aspects/executor/Log4jExecutor.java @@ -1,4 +1,4 @@ -package com.callv2.drive.infrastructure.logging.aspect.executor; +package com.callv2.drive.infrastructure.aop.aspects.executor; import org.aopalliance.intercept.Joinpoint; import org.apache.logging.log4j.Level; @@ -6,8 +6,6 @@ import org.apache.logging.log4j.Logger; import org.apache.logging.log4j.message.Message; -import com.callv2.drive.infrastructure.aop.aspects.executor.IdentifiableAspectExecutor; - public abstract class Log4jExecutor implements IdentifiableAspectExecutor { private final Logger logger; diff --git a/infrastructure/src/main/java/com/callv2/drive/infrastructure/logging/aspect/executor/MethodSignatureLogExecutor.java b/infrastructure/src/main/java/com/callv2/drive/infrastructure/aop/aspects/executor/MethodSignatureLogExecutor.java similarity index 88% rename from infrastructure/src/main/java/com/callv2/drive/infrastructure/logging/aspect/executor/MethodSignatureLogExecutor.java rename to infrastructure/src/main/java/com/callv2/drive/infrastructure/aop/aspects/executor/MethodSignatureLogExecutor.java index d3b4ff66..e51f611c 100644 --- a/infrastructure/src/main/java/com/callv2/drive/infrastructure/logging/aspect/executor/MethodSignatureLogExecutor.java +++ b/infrastructure/src/main/java/com/callv2/drive/infrastructure/aop/aspects/executor/MethodSignatureLogExecutor.java @@ -1,4 +1,4 @@ -package com.callv2.drive.infrastructure.logging.aspect.executor; +package com.callv2.drive.infrastructure.aop.aspects.executor; import org.apache.logging.log4j.Level; diff --git a/infrastructure/src/main/java/com/callv2/drive/infrastructure/logging/aspect/executor/PostTelemetryLogExecutor.java b/infrastructure/src/main/java/com/callv2/drive/infrastructure/aop/aspects/executor/PostTelemetryLogExecutor.java similarity index 91% rename from infrastructure/src/main/java/com/callv2/drive/infrastructure/logging/aspect/executor/PostTelemetryLogExecutor.java rename to infrastructure/src/main/java/com/callv2/drive/infrastructure/aop/aspects/executor/PostTelemetryLogExecutor.java index 8d6df7f6..6f1d6c3d 100644 --- a/infrastructure/src/main/java/com/callv2/drive/infrastructure/logging/aspect/executor/PostTelemetryLogExecutor.java +++ b/infrastructure/src/main/java/com/callv2/drive/infrastructure/aop/aspects/executor/PostTelemetryLogExecutor.java @@ -1,4 +1,4 @@ -package com.callv2.drive.infrastructure.logging.aspect.executor; +package com.callv2.drive.infrastructure.aop.aspects.executor; import java.time.Duration; diff --git a/infrastructure/src/main/java/com/callv2/drive/infrastructure/logging/aspect/executor/ThrowableLogExecutor.java b/infrastructure/src/main/java/com/callv2/drive/infrastructure/aop/aspects/executor/ThrowableLogExecutor.java similarity index 94% rename from infrastructure/src/main/java/com/callv2/drive/infrastructure/logging/aspect/executor/ThrowableLogExecutor.java rename to infrastructure/src/main/java/com/callv2/drive/infrastructure/aop/aspects/executor/ThrowableLogExecutor.java index 0122a9f6..f1734e09 100644 --- a/infrastructure/src/main/java/com/callv2/drive/infrastructure/logging/aspect/executor/ThrowableLogExecutor.java +++ b/infrastructure/src/main/java/com/callv2/drive/infrastructure/aop/aspects/executor/ThrowableLogExecutor.java @@ -1,4 +1,4 @@ -package com.callv2.drive.infrastructure.logging.aspect.executor; +package com.callv2.drive.infrastructure.aop.aspects.executor; import com.callv2.drive.infrastructure.aop.aspects.context.PostInvocationContext; import org.apache.logging.log4j.Level; diff --git a/infrastructure/src/main/java/com/callv2/drive/infrastructure/configuration/aop/aspect/AspectConfig.java b/infrastructure/src/main/java/com/callv2/drive/infrastructure/configuration/aop/aspect/AspectConfig.java index 720249b6..b9d91557 100644 --- a/infrastructure/src/main/java/com/callv2/drive/infrastructure/configuration/aop/aspect/AspectConfig.java +++ b/infrastructure/src/main/java/com/callv2/drive/infrastructure/configuration/aop/aspect/AspectConfig.java @@ -11,10 +11,10 @@ import com.callv2.drive.infrastructure.aop.aspects.chain.AspectExecutorChain; import com.callv2.drive.infrastructure.aop.aspects.chain.MethodInvocationAspectExecutorChain; import com.callv2.drive.infrastructure.aop.aspects.chain.PostInvocationAspectExecutorChain; +import com.callv2.drive.infrastructure.aop.aspects.executor.MethodSignatureLogExecutor; +import com.callv2.drive.infrastructure.aop.aspects.executor.PostTelemetryLogExecutor; +import com.callv2.drive.infrastructure.aop.aspects.executor.ThrowableLogExecutor; import com.callv2.drive.infrastructure.aop.aspects.handler.SimpleMethodInterceptorWithContextHandler; -import com.callv2.drive.infrastructure.logging.aspect.executor.MethodSignatureLogExecutor; -import com.callv2.drive.infrastructure.logging.aspect.executor.PostTelemetryLogExecutor; -import com.callv2.drive.infrastructure.logging.aspect.executor.ThrowableLogExecutor; @Configuration public class AspectConfig { @@ -22,35 +22,33 @@ public class AspectConfig { @Bean Advisor applicationAdvisor() { - final MethodInvocationAspectExecutorChain beforeChain = AspectExecutorChain.Builder + final var beforeChain = AspectExecutorChain.Builder .create(MethodInvocationAspectExecutorChain.class) .add(MethodInvocationAspectExecutorChain .with(new MethodSignatureLogExecutor(Level.INFO, MethodSignatureLogExecutor.class))) .build(); - final PostInvocationAspectExecutorChain afterChain = AspectExecutorChain.Builder + final var afterChain = AspectExecutorChain.Builder .create(PostInvocationAspectExecutorChain.class) .add(PostInvocationAspectExecutorChain .with(new PostTelemetryLogExecutor(Level.INFO, PostTelemetryLogExecutor.class))) .build(); - final PostInvocationAspectExecutorChain errorChain = AspectExecutorChain.Builder + final var errorChain = AspectExecutorChain.Builder .create(PostInvocationAspectExecutorChain.class) .add(PostInvocationAspectExecutorChain .with(ThrowableLogExecutor.defaultCreate(ThrowableLogExecutor.class))) .build(); - final var ahamm = new SimpleMethodInterceptorWithContextHandler(beforeChain, afterChain, errorChain); - - return applicationAdvisor("execution(* com.callv2.drive.application..*.*(..))", ahamm); + return applicationAdvisor( + "execution(* com.callv2.drive.application..*.*(..))", + new SimpleMethodInterceptorWithContextHandler(beforeChain, afterChain, errorChain)); } private static Advisor applicationAdvisor( final String expression, final MethodInterceptor interceptor) { - // final String expression = "execution(* com.callv2.drive..*.*(..))"; - final AspectJExpressionPointcut pointcut = new AspectJExpressionPointcut(); pointcut.setExpression(expression); From 2d8c56aaada6b4ba562a90208ae0ba3f57aeea10 Mon Sep 17 00:00:00 2001 From: jhonatapers Date: Sat, 17 May 2025 11:53:08 -0300 Subject: [PATCH 30/53] fix: correct logging level configuration in application.yml and ensure proper formatting in application-dev.yml --- infrastructure/src/main/resources/application-dev.yml | 4 +++- infrastructure/src/main/resources/application.yml | 4 ++-- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/infrastructure/src/main/resources/application-dev.yml b/infrastructure/src/main/resources/application-dev.yml index 40ceab16..03224211 100644 --- a/infrastructure/src/main/resources/application-dev.yml +++ b/infrastructure/src/main/resources/application-dev.yml @@ -25,4 +25,6 @@ storage: max-file-size: ${MAX_FILE_SIZE} max-request-size: ${MAX_REQUEST_SIZE} file-system: - location: ${STORAGE_LOCATION} \ No newline at end of file + location: ${STORAGE_LOCATION} + +log.level: ${LOG_LEVEL:INFO} \ No newline at end of file diff --git a/infrastructure/src/main/resources/application.yml b/infrastructure/src/main/resources/application.yml index 0b8b8006..95068769 100644 --- a/infrastructure/src/main/resources/application.yml +++ b/infrastructure/src/main/resources/application.yml @@ -37,8 +37,8 @@ spring: logging: level: root: INFO - com.callv2: DEBUG - org.springframework.web: WARN + '[com.callv2.drive]': DEBUG + '[org.springframework.web]': WARN pattern: console: "%d{yyyy-MM-dd HH:mm:ss} - %highlight(%-5level) [%thread] %logger{36} - %msg%n" From 3db5e1b56bfe6971842e652929dde62f17af27df Mon Sep 17 00:00:00 2001 From: jhonatapers Date: Sat, 17 May 2025 12:00:36 -0300 Subject: [PATCH 31/53] feat: update logging levels in AspectConfig for improved debugging and error handling --- .../configuration/aop/aspect/AspectConfig.java | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/infrastructure/src/main/java/com/callv2/drive/infrastructure/configuration/aop/aspect/AspectConfig.java b/infrastructure/src/main/java/com/callv2/drive/infrastructure/configuration/aop/aspect/AspectConfig.java index b9d91557..6b21ce69 100644 --- a/infrastructure/src/main/java/com/callv2/drive/infrastructure/configuration/aop/aspect/AspectConfig.java +++ b/infrastructure/src/main/java/com/callv2/drive/infrastructure/configuration/aop/aspect/AspectConfig.java @@ -25,17 +25,19 @@ Advisor applicationAdvisor() { final var beforeChain = AspectExecutorChain.Builder .create(MethodInvocationAspectExecutorChain.class) .add(MethodInvocationAspectExecutorChain - .with(new MethodSignatureLogExecutor(Level.INFO, MethodSignatureLogExecutor.class))) + .with(new MethodSignatureLogExecutor(Level.DEBUG, MethodSignatureLogExecutor.class))) .build(); final var afterChain = AspectExecutorChain.Builder .create(PostInvocationAspectExecutorChain.class) .add(PostInvocationAspectExecutorChain - .with(new PostTelemetryLogExecutor(Level.INFO, PostTelemetryLogExecutor.class))) + .with(new PostTelemetryLogExecutor(Level.DEBUG, PostTelemetryLogExecutor.class))) .build(); final var errorChain = AspectExecutorChain.Builder .create(PostInvocationAspectExecutorChain.class) + .add(PostInvocationAspectExecutorChain + .with(new PostTelemetryLogExecutor(Level.ERROR, PostTelemetryLogExecutor.class))) .add(PostInvocationAspectExecutorChain .with(ThrowableLogExecutor.defaultCreate(ThrowableLogExecutor.class))) .build(); From 13d891cd3afcde847aac5f724277ecfcc89f6625 Mon Sep 17 00:00:00 2001 From: jhonatapers Date: Sat, 17 May 2025 12:22:14 -0300 Subject: [PATCH 32/53] feat: add storage location configuration in Dockerfile for improved data management --- Dockerfile | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/Dockerfile b/Dockerfile index 87ade5db..def838cc 100644 --- a/Dockerfile +++ b/Dockerfile @@ -13,6 +13,11 @@ COPY --from=builder /usr/app/build/libs/application.jar /opt/app/application.jar RUN addgroup -S app && adduser -S app -G app USER app:app +ARG STORAGE_LOCATION=/srv/drive/storage +ENV STORAGE_LOCATION=$STORAGE_LOCATION + +RUN mkdir -p $STORAGE_LOCATION && chown -R app:app $STORAGE_LOCATION + EXPOSE 80 CMD ["sh", "-c", "java -jar /opt/app/application.jar"] \ No newline at end of file From 06208b56ac4d7bd9b9efa6fd5f89de0f2cdcf01b Mon Sep 17 00:00:00 2001 From: jhonatapers Date: Sat, 17 May 2025 12:45:26 -0300 Subject: [PATCH 33/53] fix: Dokerfile dir's --- Dockerfile | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/Dockerfile b/Dockerfile index def838cc..6aaad901 100644 --- a/Dockerfile +++ b/Dockerfile @@ -10,13 +10,14 @@ FROM eclipse-temurin:21.0.7_6-jdk-alpine COPY --from=builder /usr/app/build/libs/application.jar /opt/app/application.jar +ENV STORAGE_LOCATION=/srv/drive/storage + +RUN mkdir -p $STORAGE_LOCATION RUN addgroup -S app && adduser -S app -G app +RUN chown -R app:app /srv/drive/storage/ USER app:app -ARG STORAGE_LOCATION=/srv/drive/storage -ENV STORAGE_LOCATION=$STORAGE_LOCATION -RUN mkdir -p $STORAGE_LOCATION && chown -R app:app $STORAGE_LOCATION EXPOSE 80 From 1ed30e8f52f5ad0302d414fd3f1cd52fdf4efb80 Mon Sep 17 00:00:00 2001 From: jhonatapers Date: Sat, 17 May 2025 14:40:41 -0300 Subject: [PATCH 34/53] fix: update Spring Boot version to 3.4.5 in build.gradle --- infrastructure/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/infrastructure/build.gradle b/infrastructure/build.gradle index 769eaf18..4305c30a 100644 --- a/infrastructure/build.gradle +++ b/infrastructure/build.gradle @@ -1,7 +1,7 @@ plugins { id 'java' id 'application' - id 'org.springframework.boot' version '3.4.1' + id 'org.springframework.boot' version '3.4.5' id 'io.spring.dependency-management' version '1.1.7' } From 4ff3f440814246fdc99750e6ace6133eabf4b819 Mon Sep 17 00:00:00 2001 From: jhonatapers Date: Sun, 18 May 2025 12:41:00 -0300 Subject: [PATCH 35/53] refactor: replace application-dev.yml and application-local.yml with application-env.yml for improved configuration management --- .../src/main/resources/application-dev.yml | 30 ------------------- ...pplication-prd.yml => application-env.yml} | 10 ++----- .../src/main/resources/application-local.yml | 28 ----------------- .../src/main/resources/application.yml | 16 ++-------- 4 files changed, 5 insertions(+), 79 deletions(-) delete mode 100644 infrastructure/src/main/resources/application-dev.yml rename infrastructure/src/main/resources/{application-prd.yml => application-env.yml} (65%) delete mode 100644 infrastructure/src/main/resources/application-local.yml diff --git a/infrastructure/src/main/resources/application-dev.yml b/infrastructure/src/main/resources/application-dev.yml deleted file mode 100644 index 03224211..00000000 --- a/infrastructure/src/main/resources/application-dev.yml +++ /dev/null @@ -1,30 +0,0 @@ -server: - port: ${SERVER_PORT} - -keycloak: - realm: callv2 - host: ${KEYCLOAK_HOST} - client: - user-api: - client-id: ${KEYCLOAK_CLIENT_ID} - client-secret: ${KEYCLOAK_CLIENT_SECRET} - -postgres: - host: ${POSTGRES_HOST} - port: ${POSTGRES_PORT} - database: drive - username: ${POSTGRES_USER} - password: ${POSTGRES_PASSWORD} - -db: - ddl-auto: ${DB_DDL_AUTO} - -reuqest-timeout: ${REQUEST_TIMEOUT} - -storage: - max-file-size: ${MAX_FILE_SIZE} - max-request-size: ${MAX_REQUEST_SIZE} - file-system: - location: ${STORAGE_LOCATION} - -log.level: ${LOG_LEVEL:INFO} \ No newline at end of file diff --git a/infrastructure/src/main/resources/application-prd.yml b/infrastructure/src/main/resources/application-env.yml similarity index 65% rename from infrastructure/src/main/resources/application-prd.yml rename to infrastructure/src/main/resources/application-env.yml index 03447535..8290abb2 100644 --- a/infrastructure/src/main/resources/application-prd.yml +++ b/infrastructure/src/main/resources/application-env.yml @@ -4,20 +4,16 @@ server: keycloak: realm: ${KEYCLOAK_REALM} host: ${KEYCLOAK_HOST} - client: - user-api: - client-id: ${KEYCLOAK_CLIENT_ID} - client-secret: ${KEYCLOAK_CLIENT_SECRET} postgres: host: ${POSTGRES_HOST} port: ${POSTGRES_PORT} database: ${POSTGRES_DATABASE} - username: ${POSTGRES_USER} + username: ${POSTGRES_USERNAME} password: ${POSTGRES_PASSWORD} db: - ddl-auto: none + ddl-auto: ${DB_DDL_AUTO} reuqest-timeout: ${REQUEST_TIMEOUT} @@ -25,4 +21,4 @@ storage: max-file-size: ${MAX_FILE_SIZE} max-request-size: ${MAX_REQUEST_SIZE} file-system: - location: ${STORAGE_LOCATION} \ No newline at end of file + location: ${STORAGE_LOCATION} diff --git a/infrastructure/src/main/resources/application-local.yml b/infrastructure/src/main/resources/application-local.yml deleted file mode 100644 index 3382e121..00000000 --- a/infrastructure/src/main/resources/application-local.yml +++ /dev/null @@ -1,28 +0,0 @@ -server: - port: 8080 - -keycloak: - realm: callv2 - host: http://localhost:8090 - client: - user-api: - client-id: user-api - client-secret: local-client-secret - -postgres: - host: localhost - port: 5433 - database: drive - username: postgres - password: postgres - -db: - ddl-auto: none - -reuqest-timeout: 600000 - -storage: - max-file-size: 1024MB - max-request-size: 1024MB - file-system: - location: / \ No newline at end of file diff --git a/infrastructure/src/main/resources/application.yml b/infrastructure/src/main/resources/application.yml index 95068769..dcb18d80 100644 --- a/infrastructure/src/main/resources/application.yml +++ b/infrastructure/src/main/resources/application.yml @@ -16,8 +16,8 @@ spring: servlet: multipart: enabled: true - max-file-size: ${storage.max-file-size:10240MB} - max-request-size: ${storage.max-request-size:10240MB} + max-file-size: ${storage.max-file-size} + max-request-size: ${storage.max-request-size} security: oauth2: resourceserver: @@ -33,15 +33,3 @@ spring: hibernate: ddl-auto: ${db.ddl-auto} show-sql: false - -logging: - level: - root: INFO - '[com.callv2.drive]': DEBUG - '[org.springframework.web]': WARN - - pattern: - console: "%d{yyyy-MM-dd HH:mm:ss} - %highlight(%-5level) [%thread] %logger{36} - %msg%n" - - file: - name: logs/app.log \ No newline at end of file From b8c11a0bf761ffaceaec71bd8a22848a0d2c4a24 Mon Sep 17 00:00:00 2001 From: jhonatapers Date: Sun, 18 May 2025 12:41:23 -0300 Subject: [PATCH 36/53] feat: add .env.example and log4j2-spring.xml for environment configuration and logging setup --- .env.example | 29 +++++++ .../src/main/resources/log4j2-spring.xml | 87 +++++++++++++++++++ 2 files changed, 116 insertions(+) create mode 100644 .env.example create mode 100644 infrastructure/src/main/resources/log4j2-spring.xml diff --git a/.env.example b/.env.example new file mode 100644 index 00000000..c4d8598e --- /dev/null +++ b/.env.example @@ -0,0 +1,29 @@ +SPRING_PROFILES_ACTIVE=env + +SERVER_PORT=8080 +REQUEST_TIMEOUT=60000 + +KEYCLOAK_HOST=http://localhost:8090 +KEYCLOAK_REALM=heroes + +POSTGRES_HOST=localhost +POSTGRES_PORT=5432 +POSTGRES_DATABASE=postgres +POSTGRES_USER=postgres +POSTGRES_PASSWORD=postgres + +DB_DDL_AUTO=none + +STORAGE_LOCATION=/srv/drive/storage/ +MAX_FILE_SIZE=2048MB +MAX_REQUEST_SIZE=100MB + +LOG_PATH=log +ROOT_LOG_LEVEL=trace +ROOT_LOG_LEVEL_CONSOLE=info +ROOT_LOG_LEVEL_FILE=info +ROOT_LOG_LEVEL_JSON=info +CALLV2_LOG_LEVEL=trace +CALLV2_LOG_LEVEL_CONSOLE=debug +CALLV2_LOG_LEVEL_FILE=debug +CALLV2_LOG_LEVEL_JSON=debug \ No newline at end of file diff --git a/infrastructure/src/main/resources/log4j2-spring.xml b/infrastructure/src/main/resources/log4j2-spring.xml new file mode 100644 index 00000000..b5e856b8 --- /dev/null +++ b/infrastructure/src/main/resources/log4j2-spring.xml @@ -0,0 +1,87 @@ + + + + ${env:ROOT_LOG_LEVEL:-info} + ${env:ROOT_LOG_LEVEL_CONSOLE:-info} + ${env:ROOT_LOG_LEVEL_FILE:-info} + ${env:ROOT_LOG_LEVEL_JSON:-info} + + ${env:CALLV2_LOG_LEVEL:-info} + ${env:CALLV2_LOG_LEVEL_CONSOLE:-info} + ${env:CALLV2_LOG_LEVEL_FILE:-info} + ${env:CALLV2_LOG_LEVEL_JSON:-info} + + ${env:LOG_PATH:-log} + %d{yyyy-MM-dd HH:mm:ss.SSS} [%t] %X{traceId} %-5level %logger{36}.%M(%F:%L) - %msg%n + {"timestamp":"%d{yyyy-MM-dd'T'HH:mm:ss.SSSZ}","level":"%p","thread":"%t","logger":"%c{1}","message":"%enc{%m}{JSON}"}%n + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file From 433adc6a6ccbad15948ef7e337951c494ee79ebe Mon Sep 17 00:00:00 2001 From: jhonatapers Date: Sun, 18 May 2025 12:42:05 -0300 Subject: [PATCH 37/53] fix: update .gitignore to include *.log.json for log file exclusion --- .gitignore | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/.gitignore b/.gitignore index f6cde2b0..e8fb0dbb 100644 --- a/.gitignore +++ b/.gitignore @@ -16,4 +16,5 @@ bin .env -*.log \ No newline at end of file +*.log +*.log.json \ No newline at end of file From dfc4831cea382a6434fbdca6d4d9d49653933fb6 Mon Sep 17 00:00:00 2001 From: jhonatapers Date: Sun, 18 May 2025 12:44:02 -0300 Subject: [PATCH 38/53] refactor: remove DomainLogConfig class --- .../logging/DomainLogConfig.java | 65 ------------------- 1 file changed, 65 deletions(-) delete mode 100644 infrastructure/src/main/java/com/callv2/drive/infrastructure/configuration/logging/DomainLogConfig.java diff --git a/infrastructure/src/main/java/com/callv2/drive/infrastructure/configuration/logging/DomainLogConfig.java b/infrastructure/src/main/java/com/callv2/drive/infrastructure/configuration/logging/DomainLogConfig.java deleted file mode 100644 index 2826d2f7..00000000 --- a/infrastructure/src/main/java/com/callv2/drive/infrastructure/configuration/logging/DomainLogConfig.java +++ /dev/null @@ -1,65 +0,0 @@ -package com.callv2.drive.infrastructure.configuration.logging; - -import org.springframework.context.annotation.Configuration; - -@Configuration -public class DomainLogConfig { - -// @Bean -// FolderLog folderLog() { -// final var beforeChain = AspectBeforeExecutionChain.Builder.create() -// .add(new SignatureLogAspect(Level.INFO)) -// .add(new ArgsLogAspect(Level.INFO)) -// .add(new TelemetryBeforeLogAspect(Level.DEBUG)) -// .build(); - -// final var afterChain = AspectAfterExecutionChain.Builder.create() -// .add(new TelemetryAfterLogAspect(Level.INFO)) -// .build(); - -// final var errorChain = AspectExecutionErrorChain.Builder.create() -// .add(new StackTraceLogAspect(Level.INFO)) -// .build(); - -// return new FolderLog(beforeChain, afterChain, errorChain); -// } - -// @Bean -// FileLog fileLog() { -// final var beforeChain = AspectBeforeExecutionChain.Builder.create() -// .add(new SignatureLogAspect(Level.INFO)) -// .add(new ArgsLogAspect(Level.INFO)) -// // .add(new TelemetryBeforeLogAspect(Level.INFO)) -// .build(); - -// final var afterChain = AspectAfterExecutionChain.Builder.create() -// .add(new TelemetryAfterLogAspect(Level.INFO)) -// .build(); - -// final var errorChain = AspectExecutionErrorChain.Builder.create() -// .add(new StackTraceLogAspect(Level.INFO)) -// .build(); - -// return new FileLog(beforeChain, afterChain, errorChain); -// } - -// @Bean -// StorageLog storageLog() { -// final var beforeChain = AspectBeforeExecutionChain.Builder.create() -// .add(new SignatureLogAspect(Level.INFO)) -// .add(new ArgsLogAspect(Level.INFO)) -// // .add(new TelemetryBeforeLogAspect(Level.INFO)) -// .build(); - -// final var afterChain = AspectAfterExecutionChain.Builder.create() -// .add(new TelemetryAfterLogAspect(Level.INFO)) -// .build(); - -// final var errorChain = AspectExecutionErrorChain.Builder.create() -// .add(new StackTraceLogAspect(Level.INFO)) -// .build(); - -// return new StorageLog(beforeChain, afterChain, errorChain); -// } - -} From 88efce146346be88fcd134494662b5222400a4e5 Mon Sep 17 00:00:00 2001 From: jhonatapers Date: Sun, 18 May 2025 13:57:14 -0300 Subject: [PATCH 39/53] fix: enhance JsonLog configuration to include stacktrace as string --- infrastructure/src/main/resources/log4j2-spring.xml | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/infrastructure/src/main/resources/log4j2-spring.xml b/infrastructure/src/main/resources/log4j2-spring.xml index b5e856b8..2257b802 100644 --- a/infrastructure/src/main/resources/log4j2-spring.xml +++ b/infrastructure/src/main/resources/log4j2-spring.xml @@ -33,8 +33,11 @@ name="JsonLog" fileName="${LOG_PATH}/drive-api.log.json" filePattern="${LOG_PATH}/drive-api-%d{yyyy-MM-dd}.log.json.gz"> - - + From 368036add0f9d16040a6c3abb30a09d62853d8b5 Mon Sep 17 00:00:00 2001 From: jhonatapers Date: Sun, 18 May 2025 14:02:20 -0300 Subject: [PATCH 40/53] refactor: remove LOG_JSON_PATTERN property from log4j2-spring.xml --- infrastructure/src/main/resources/log4j2-spring.xml | 1 - 1 file changed, 1 deletion(-) diff --git a/infrastructure/src/main/resources/log4j2-spring.xml b/infrastructure/src/main/resources/log4j2-spring.xml index 2257b802..eecde3e7 100644 --- a/infrastructure/src/main/resources/log4j2-spring.xml +++ b/infrastructure/src/main/resources/log4j2-spring.xml @@ -13,7 +13,6 @@ ${env:LOG_PATH:-log} %d{yyyy-MM-dd HH:mm:ss.SSS} [%t] %X{traceId} %-5level %logger{36}.%M(%F:%L) - %msg%n - {"timestamp":"%d{yyyy-MM-dd'T'HH:mm:ss.SSSZ}","level":"%p","thread":"%t","logger":"%c{1}","message":"%enc{%m}{JSON}"}%n From faf9b490d2a4eed2bb29be73f54832feb2f73f43 Mon Sep 17 00:00:00 2001 From: jhonatapers Date: Sun, 18 May 2025 16:20:03 -0300 Subject: [PATCH 41/53] fix: correct typo in variable name for postInvocationResult in SimpleMethodInterceptorWithContextHandler --- .../SimpleMethodInterceptorWithContextHandler.java | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/infrastructure/src/main/java/com/callv2/drive/infrastructure/aop/aspects/handler/SimpleMethodInterceptorWithContextHandler.java b/infrastructure/src/main/java/com/callv2/drive/infrastructure/aop/aspects/handler/SimpleMethodInterceptorWithContextHandler.java index 67cc23c8..91bd254a 100644 --- a/infrastructure/src/main/java/com/callv2/drive/infrastructure/aop/aspects/handler/SimpleMethodInterceptorWithContextHandler.java +++ b/infrastructure/src/main/java/com/callv2/drive/infrastructure/aop/aspects/handler/SimpleMethodInterceptorWithContextHandler.java @@ -33,14 +33,14 @@ public Object invoke(@Nonnull MethodInvocation invocation) throws Throwable { final MethodInvocationContext context = SimpleMethodInvocationContext.of(invocation); - final PostInvocationContext postInvaotionResult = beforeChain.execute(context); + final PostInvocationContext postInvocationResult = beforeChain.execute(context); - if (postInvaotionResult.wasSuccessful()) - return afterChain.execute(postInvaotionResult); + if (postInvocationResult.wasSuccessful()) + return afterChain.execute(postInvocationResult); else - errorChain.execute(postInvaotionResult); + errorChain.execute(postInvocationResult); - throw postInvaotionResult.getThrowable(); + throw postInvocationResult.getThrowable(); } } From 93963d8b8bed601ca4ea2118d658bc078349bea0 Mon Sep 17 00:00:00 2001 From: jhonatapers Date: Sun, 18 May 2025 16:27:04 -0300 Subject: [PATCH 42/53] fix: replace jakarta annotations with javax annotations in MethodInvocationContext and PostInvocationContext --- .../aop/aspects/context/MethodInvocationContext.java | 3 +-- .../aop/aspects/context/PostInvocationContext.java | 4 +--- 2 files changed, 2 insertions(+), 5 deletions(-) diff --git a/infrastructure/src/main/java/com/callv2/drive/infrastructure/aop/aspects/context/MethodInvocationContext.java b/infrastructure/src/main/java/com/callv2/drive/infrastructure/aop/aspects/context/MethodInvocationContext.java index 9ccaafb6..151fb732 100644 --- a/infrastructure/src/main/java/com/callv2/drive/infrastructure/aop/aspects/context/MethodInvocationContext.java +++ b/infrastructure/src/main/java/com/callv2/drive/infrastructure/aop/aspects/context/MethodInvocationContext.java @@ -2,7 +2,7 @@ import java.time.Instant; -import jakarta.annotation.Nonnull; +import javax.annotation.Nonnull; import org.aopalliance.intercept.MethodInvocation; @@ -11,7 +11,6 @@ public interface MethodInvocationContext extends MethodInvocation { @Nonnull Instant getContextedAt(); - @Nonnull boolean proceeded(); @Nonnull diff --git a/infrastructure/src/main/java/com/callv2/drive/infrastructure/aop/aspects/context/PostInvocationContext.java b/infrastructure/src/main/java/com/callv2/drive/infrastructure/aop/aspects/context/PostInvocationContext.java index 720ca2fc..41a206b8 100644 --- a/infrastructure/src/main/java/com/callv2/drive/infrastructure/aop/aspects/context/PostInvocationContext.java +++ b/infrastructure/src/main/java/com/callv2/drive/infrastructure/aop/aspects/context/PostInvocationContext.java @@ -2,8 +2,7 @@ import java.time.Instant; -import jakarta.annotation.Nonnull; -import jakarta.annotation.Nullable; +import javax.annotation.Nullable; public interface PostInvocationContext extends MethodInvocationContext { @@ -16,7 +15,6 @@ public interface PostInvocationContext extends MethodInvocationContext { @Nullable Throwable getThrowable(); - @Nonnull boolean wasSuccessful(); } From f70d5f1de449f1bf03055c77703a93fcfc59297e Mon Sep 17 00:00:00 2001 From: jhonatapers Date: Sun, 18 May 2025 16:59:17 -0300 Subject: [PATCH 43/53] fix: return result from next executor in AspectExecutorChain --- .../infrastructure/aop/aspects/chain/AspectExecutorChain.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/infrastructure/src/main/java/com/callv2/drive/infrastructure/aop/aspects/chain/AspectExecutorChain.java b/infrastructure/src/main/java/com/callv2/drive/infrastructure/aop/aspects/chain/AspectExecutorChain.java index bbde5387..b7aa2c67 100644 --- a/infrastructure/src/main/java/com/callv2/drive/infrastructure/aop/aspects/chain/AspectExecutorChain.java +++ b/infrastructure/src/main/java/com/callv2/drive/infrastructure/aop/aspects/chain/AspectExecutorChain.java @@ -21,7 +21,7 @@ public final O execute(final J joinpoint) throws Throwable { executor.execute(joinpoint); if (next != null) - next.execute(joinpoint); + return next.execute(joinpoint); return callsProceed(joinpoint); } From 4cad21ee6082004d2cb4ffb16ab7c72b470cb563 Mon Sep 17 00:00:00 2001 From: jhonatapers Date: Sun, 18 May 2025 17:24:23 -0300 Subject: [PATCH 44/53] fix: improve error handling in SimpleMethodInterceptorWithContextHandler --- .../handler/SimpleMethodInterceptorWithContextHandler.java | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/infrastructure/src/main/java/com/callv2/drive/infrastructure/aop/aspects/handler/SimpleMethodInterceptorWithContextHandler.java b/infrastructure/src/main/java/com/callv2/drive/infrastructure/aop/aspects/handler/SimpleMethodInterceptorWithContextHandler.java index 91bd254a..4ae41e75 100644 --- a/infrastructure/src/main/java/com/callv2/drive/infrastructure/aop/aspects/handler/SimpleMethodInterceptorWithContextHandler.java +++ b/infrastructure/src/main/java/com/callv2/drive/infrastructure/aop/aspects/handler/SimpleMethodInterceptorWithContextHandler.java @@ -40,7 +40,11 @@ public Object invoke(@Nonnull MethodInvocation invocation) throws Throwable { else errorChain.execute(postInvocationResult); - throw postInvocationResult.getThrowable(); + final Throwable throwable = postInvocationResult.getThrowable(); + if (throwable != null) + throw throwable; + + throw new IllegalStateException("Invocation failed but no throwable was provided."); } } From 1b05cf44edb431b39372be6dba4dfa88390fe017 Mon Sep 17 00:00:00 2001 From: jhonatapers Date: Sun, 18 May 2025 17:24:52 -0300 Subject: [PATCH 45/53] fix: improve logging format in MethodSignatureLogExecutor, PostTelemetryLogExecutor, and ThrowableLogExecutor --- .../executor/MethodSignatureLogExecutor.java | 2 +- .../executor/PostTelemetryLogExecutor.java | 6 +++--- .../executor/ThrowableLogExecutor.java | 21 +++++++------------ 3 files changed, 11 insertions(+), 18 deletions(-) diff --git a/infrastructure/src/main/java/com/callv2/drive/infrastructure/aop/aspects/executor/MethodSignatureLogExecutor.java b/infrastructure/src/main/java/com/callv2/drive/infrastructure/aop/aspects/executor/MethodSignatureLogExecutor.java index e51f611c..4c8ec16b 100644 --- a/infrastructure/src/main/java/com/callv2/drive/infrastructure/aop/aspects/executor/MethodSignatureLogExecutor.java +++ b/infrastructure/src/main/java/com/callv2/drive/infrastructure/aop/aspects/executor/MethodSignatureLogExecutor.java @@ -12,7 +12,7 @@ public MethodSignatureLogExecutor(final Level level, final Class clazz) { @Override public void execute(final MethodInvocationContext context) { - log("<>: [" + context.getMethod().toString() + "]"); + log("<>: [{}]", context.getMethod().toString()); } } diff --git a/infrastructure/src/main/java/com/callv2/drive/infrastructure/aop/aspects/executor/PostTelemetryLogExecutor.java b/infrastructure/src/main/java/com/callv2/drive/infrastructure/aop/aspects/executor/PostTelemetryLogExecutor.java index 6f1d6c3d..48675064 100644 --- a/infrastructure/src/main/java/com/callv2/drive/infrastructure/aop/aspects/executor/PostTelemetryLogExecutor.java +++ b/infrastructure/src/main/java/com/callv2/drive/infrastructure/aop/aspects/executor/PostTelemetryLogExecutor.java @@ -14,9 +14,9 @@ public PostTelemetryLogExecutor(final Level logLevel, final Class clazz) { @Override public void execute(final PostInvocationContext context) { - log("<> " + - Duration.between(context.getContextedAt(), context.getProceededAt()).toMillis() + " ms " + - "<> " + "[" + context.getMethod().toString() + "]"); + log("<> [{}] ms <> [{}]", + Duration.between(context.getContextedAt(), context.getProceededAt()).toMillis(), + context.getMethod().toString()); } } diff --git a/infrastructure/src/main/java/com/callv2/drive/infrastructure/aop/aspects/executor/ThrowableLogExecutor.java b/infrastructure/src/main/java/com/callv2/drive/infrastructure/aop/aspects/executor/ThrowableLogExecutor.java index f1734e09..6bef77d9 100644 --- a/infrastructure/src/main/java/com/callv2/drive/infrastructure/aop/aspects/executor/ThrowableLogExecutor.java +++ b/infrastructure/src/main/java/com/callv2/drive/infrastructure/aop/aspects/executor/ThrowableLogExecutor.java @@ -17,22 +17,15 @@ public static ThrowableLogExecutor create(final Level level, final Class claz return new ThrowableLogExecutor(level, clazz); } + @SuppressWarnings("null") @Override public void execute(final PostInvocationContext context) { - if (context.getThrowable() == null) - return; - - final String message = """ - <> [%s] - <> [%s] - <> [%s] - """ - .formatted( - context.getThrowable().getClass().getName(), - context.getThrowable().getMessage(), - context.getMethod().toString()); - - log(message, context.getThrowable()); + if (context.getThrowable() != null) + log("<> [{}] <> [{}] <> [{}]", + context.getThrowable().getClass().getName(), + context.getThrowable().getMessage(), + context.getMethod().toString(), + context.getThrowable()); } } From caca72463e21376f78c1fb0feaa31d62a5c5aa8e Mon Sep 17 00:00:00 2001 From: jhonatapers Date: Sun, 18 May 2025 17:25:18 -0300 Subject: [PATCH 46/53] feat: add ArgsLogExecutor for logging method calls and arguments --- .../aop/aspects/executor/ArgsLogExecutor.java | 24 +++++++++++++++++++ 1 file changed, 24 insertions(+) create mode 100644 infrastructure/src/main/java/com/callv2/drive/infrastructure/aop/aspects/executor/ArgsLogExecutor.java diff --git a/infrastructure/src/main/java/com/callv2/drive/infrastructure/aop/aspects/executor/ArgsLogExecutor.java b/infrastructure/src/main/java/com/callv2/drive/infrastructure/aop/aspects/executor/ArgsLogExecutor.java new file mode 100644 index 00000000..8519ed3e --- /dev/null +++ b/infrastructure/src/main/java/com/callv2/drive/infrastructure/aop/aspects/executor/ArgsLogExecutor.java @@ -0,0 +1,24 @@ +package com.callv2.drive.infrastructure.aop.aspects.executor; + +import java.util.Arrays; + +import org.apache.logging.log4j.Level; + +import com.callv2.drive.infrastructure.aop.aspects.context.MethodInvocationContext; + +public class ArgsLogExecutor extends Log4jExecutor { + + public ArgsLogExecutor(final Level level, final Class clazz) { + super(level, clazz); + } + + @Override + public void execute(final MethodInvocationContext context) { + final var args = context.getArguments(); + log("<> [{}] <> count:[{}] args: [{}]", + context.getMethod(), + args.length, + Arrays.toString(args)); + } + +} From 64db9df880b95d5941e87f3b660e1e2dc57c1ea2 Mon Sep 17 00:00:00 2001 From: jhonatapers Date: Sun, 18 May 2025 17:26:47 -0300 Subject: [PATCH 47/53] feat: add ArgsLogExecutor to MethodInvocationAspectExecutorChain for enhanced logging --- .../infrastructure/configuration/aop/aspect/AspectConfig.java | 3 +++ 1 file changed, 3 insertions(+) diff --git a/infrastructure/src/main/java/com/callv2/drive/infrastructure/configuration/aop/aspect/AspectConfig.java b/infrastructure/src/main/java/com/callv2/drive/infrastructure/configuration/aop/aspect/AspectConfig.java index 6b21ce69..e76fca6c 100644 --- a/infrastructure/src/main/java/com/callv2/drive/infrastructure/configuration/aop/aspect/AspectConfig.java +++ b/infrastructure/src/main/java/com/callv2/drive/infrastructure/configuration/aop/aspect/AspectConfig.java @@ -11,6 +11,7 @@ import com.callv2.drive.infrastructure.aop.aspects.chain.AspectExecutorChain; import com.callv2.drive.infrastructure.aop.aspects.chain.MethodInvocationAspectExecutorChain; import com.callv2.drive.infrastructure.aop.aspects.chain.PostInvocationAspectExecutorChain; +import com.callv2.drive.infrastructure.aop.aspects.executor.ArgsLogExecutor; import com.callv2.drive.infrastructure.aop.aspects.executor.MethodSignatureLogExecutor; import com.callv2.drive.infrastructure.aop.aspects.executor.PostTelemetryLogExecutor; import com.callv2.drive.infrastructure.aop.aspects.executor.ThrowableLogExecutor; @@ -26,6 +27,8 @@ Advisor applicationAdvisor() { .create(MethodInvocationAspectExecutorChain.class) .add(MethodInvocationAspectExecutorChain .with(new MethodSignatureLogExecutor(Level.DEBUG, MethodSignatureLogExecutor.class))) + .add(MethodInvocationAspectExecutorChain + .with(new ArgsLogExecutor(Level.DEBUG, ArgsLogExecutor.class))) .build(); final var afterChain = AspectExecutorChain.Builder From c7faab6de8f5b67de002234bae82d71596c4b163 Mon Sep 17 00:00:00 2001 From: jhonatapers Date: Sun, 18 May 2025 17:28:33 -0300 Subject: [PATCH 48/53] fix: add @Nonnull annotations to context retrieval methods for improved null safety --- .../aop/aspects/context/AbstractMethodInvocationContext.java | 4 ++++ .../aop/aspects/context/SimplePostInvocationContext.java | 2 ++ 2 files changed, 6 insertions(+) diff --git a/infrastructure/src/main/java/com/callv2/drive/infrastructure/aop/aspects/context/AbstractMethodInvocationContext.java b/infrastructure/src/main/java/com/callv2/drive/infrastructure/aop/aspects/context/AbstractMethodInvocationContext.java index 4ed024ca..1ba24ab1 100644 --- a/infrastructure/src/main/java/com/callv2/drive/infrastructure/aop/aspects/context/AbstractMethodInvocationContext.java +++ b/infrastructure/src/main/java/com/callv2/drive/infrastructure/aop/aspects/context/AbstractMethodInvocationContext.java @@ -2,6 +2,8 @@ import java.time.Instant; +import javax.annotation.Nonnull; + public abstract class AbstractMethodInvocationContext implements MethodInvocationContext { private final Instant contextedAt; @@ -11,11 +13,13 @@ protected AbstractMethodInvocationContext() { } @Override + @Nonnull public Instant getContextedAt() { return contextedAt; } @Override + @Nonnull public PostInvocationContext proceedWithContext() { return SimplePostInvocationContext.captureFromExecution(this); } diff --git a/infrastructure/src/main/java/com/callv2/drive/infrastructure/aop/aspects/context/SimplePostInvocationContext.java b/infrastructure/src/main/java/com/callv2/drive/infrastructure/aop/aspects/context/SimplePostInvocationContext.java index 083c45e2..30b55dfc 100644 --- a/infrastructure/src/main/java/com/callv2/drive/infrastructure/aop/aspects/context/SimplePostInvocationContext.java +++ b/infrastructure/src/main/java/com/callv2/drive/infrastructure/aop/aspects/context/SimplePostInvocationContext.java @@ -55,6 +55,7 @@ public static final PostInvocationContext captureFromExecution( } @Override + @Nonnull public Instant getContextedAt() { return methodInvocationContext.getContextedAt(); } @@ -65,6 +66,7 @@ public boolean proceeded() { } @Override + @Nonnull public PostInvocationContext proceedWithContext() { return this; } From d7e6c6ca7217ae714f6858dfa75501ad0e4cd6d5 Mon Sep 17 00:00:00 2001 From: jhonatapers Date: Thu, 22 May 2025 12:58:35 -0300 Subject: [PATCH 49/53] feat: add forward-headers-strategy configuration to environment files for proxy support --- .env.example | 1 + infrastructure/src/main/resources/application-env.yml | 1 + infrastructure/src/main/resources/application.yml | 1 + 3 files changed, 3 insertions(+) diff --git a/.env.example b/.env.example index c4d8598e..82c9763c 100644 --- a/.env.example +++ b/.env.example @@ -1,6 +1,7 @@ SPRING_PROFILES_ACTIVE=env SERVER_PORT=8080 +FORWARD_HEADERS_STRATEGY=framework #none for no proxy, framework for Spring Cloud Gateway, native for native proxy REQUEST_TIMEOUT=60000 KEYCLOAK_HOST=http://localhost:8090 diff --git a/infrastructure/src/main/resources/application-env.yml b/infrastructure/src/main/resources/application-env.yml index 8290abb2..6a0f0baf 100644 --- a/infrastructure/src/main/resources/application-env.yml +++ b/infrastructure/src/main/resources/application-env.yml @@ -1,5 +1,6 @@ server: port: ${SERVER_PORT} + forward-headers-strategy: ${FORWARD_HEADERS_STRATEGY:none} keycloak: realm: ${KEYCLOAK_REALM} diff --git a/infrastructure/src/main/resources/application.yml b/infrastructure/src/main/resources/application.yml index dcb18d80..e933fa4f 100644 --- a/infrastructure/src/main/resources/application.yml +++ b/infrastructure/src/main/resources/application.yml @@ -1,5 +1,6 @@ server: port: 80 + forward-headers-strategy: none servlet: context-path: /api compression: From 9bc2a84d924d0c1d1fb14b126c1327f30d45ea81 Mon Sep 17 00:00:00 2001 From: jhonatapers Date: Tue, 27 May 2025 20:20:33 -0300 Subject: [PATCH 50/53] feat: add CORS configuration properties to support cross-origin requests --- .env.example | 6 + .../cors/CorsConfigurationProperties.java | 53 ++++++++ .../KeycloakAuthoritiesConverter.java | 67 ++++++++++ .../security/KeycloakJwtConverter.java | 32 +++++ .../security/SecurityConfig.java | 122 ++++++------------ .../src/main/resources/application-env.yml | 8 ++ 6 files changed, 204 insertions(+), 84 deletions(-) create mode 100644 infrastructure/src/main/java/com/callv2/drive/infrastructure/configuration/properties/cors/CorsConfigurationProperties.java create mode 100644 infrastructure/src/main/java/com/callv2/drive/infrastructure/configuration/security/KeycloakAuthoritiesConverter.java create mode 100644 infrastructure/src/main/java/com/callv2/drive/infrastructure/configuration/security/KeycloakJwtConverter.java diff --git a/.env.example b/.env.example index 82c9763c..0be60ad2 100644 --- a/.env.example +++ b/.env.example @@ -4,6 +4,12 @@ SERVER_PORT=8080 FORWARD_HEADERS_STRATEGY=framework #none for no proxy, framework for Spring Cloud Gateway, native for native proxy REQUEST_TIMEOUT=60000 +CORS_PATTERN=/** +CORS_ALLOWED_ORIGINS=* +CORS_ALLOWED_METHODS=GET,POST,PUT,PATCH,DELETE,OPTIONS +CORS_ALLOWED_HEADERS=* +CORS_ALLOW_CREDENTIALS=true + KEYCLOAK_HOST=http://localhost:8090 KEYCLOAK_REALM=heroes diff --git a/infrastructure/src/main/java/com/callv2/drive/infrastructure/configuration/properties/cors/CorsConfigurationProperties.java b/infrastructure/src/main/java/com/callv2/drive/infrastructure/configuration/properties/cors/CorsConfigurationProperties.java new file mode 100644 index 00000000..6ea40d80 --- /dev/null +++ b/infrastructure/src/main/java/com/callv2/drive/infrastructure/configuration/properties/cors/CorsConfigurationProperties.java @@ -0,0 +1,53 @@ +package com.callv2.drive.infrastructure.configuration.properties.cors; + +import java.util.List; + +public class CorsConfigurationProperties { + + private String pattern; + private List allowedOriginsPatterns; + private List allowedMethods; + private List allowedHeaders; + private boolean allowCredentials; + + public String getPattern() { + return pattern; + } + + public void setPattern(String pattern) { + this.pattern = pattern; + } + + public List getAllowedOriginsPatterns() { + return allowedOriginsPatterns; + } + + public void setAllowedOriginsPatterns(List allowedOriginsPatterns) { + this.allowedOriginsPatterns = allowedOriginsPatterns; + } + + public List getAllowedMethods() { + return allowedMethods; + } + + public void setAllowedMethods(List allowedMethods) { + this.allowedMethods = allowedMethods; + } + + public List getAllowedHeaders() { + return allowedHeaders; + } + + public void setAllowedHeaders(List allowedHeaders) { + this.allowedHeaders = allowedHeaders; + } + + public boolean isAllowCredentials() { + return allowCredentials; + } + + public void setAllowCredentials(boolean allowCredentials) { + this.allowCredentials = allowCredentials; + } + +} diff --git a/infrastructure/src/main/java/com/callv2/drive/infrastructure/configuration/security/KeycloakAuthoritiesConverter.java b/infrastructure/src/main/java/com/callv2/drive/infrastructure/configuration/security/KeycloakAuthoritiesConverter.java new file mode 100644 index 00000000..c77d950c --- /dev/null +++ b/infrastructure/src/main/java/com/callv2/drive/infrastructure/configuration/security/KeycloakAuthoritiesConverter.java @@ -0,0 +1,67 @@ +package com.callv2.drive.infrastructure.configuration.security; + +import java.util.Collection; +import java.util.Collections; +import java.util.Map; +import java.util.Optional; +import java.util.Set; +import java.util.function.Function; +import java.util.stream.Collectors; +import java.util.stream.Stream; + +import org.springframework.core.convert.converter.Converter; +import org.springframework.security.core.GrantedAuthority; +import org.springframework.security.core.authority.SimpleGrantedAuthority; +import org.springframework.security.oauth2.jwt.Jwt; + +import com.nimbusds.jose.shaded.gson.internal.LinkedTreeMap; + +public class KeycloakAuthoritiesConverter implements Converter> { + + private static final String REALM_ACCESS = "realm_access"; + private static final String ROLES = "roles"; + private static final String RESOURCE_ACCESS = "resource_access"; + private static final String SEPARATOR = "_"; + private static final String ROLE_PREFIX = "ROLE_"; + + @Override + public Collection convert(final Jwt jwt) { + final var realmRoles = extractRealmRoles(jwt); + final var resourceRoles = extractResourceRoles(jwt); + + return Stream.concat(realmRoles, resourceRoles) + .map(role -> new SimpleGrantedAuthority(ROLE_PREFIX + role.toUpperCase())) + .collect(Collectors.toSet()); + } + + private Stream extractResourceRoles(final Jwt jwt) { + + final Function, Stream> mapResource = resource -> { + final var key = resource.getKey(); + @SuppressWarnings("rawtypes") + final LinkedTreeMap value = (LinkedTreeMap) resource.getValue(); + @SuppressWarnings("unchecked") + final var roles = (Collection) value.get(ROLES); + return roles.stream().map(role -> key.concat(SEPARATOR).concat(role)); + }; + + final Function>, Collection> mapResources = resources -> resources + .stream() + .flatMap(mapResource) + .toList(); + + return Optional.ofNullable(jwt.getClaimAsMap(RESOURCE_ACCESS)) + .map(resources -> resources.entrySet()) + .map(mapResources) + .orElse(Collections.emptyList()) + .stream(); + } + + @SuppressWarnings("unchecked") + private Stream extractRealmRoles(final Jwt jwt) { + return Optional.ofNullable(jwt.getClaimAsMap(REALM_ACCESS)) + .map(resource -> (Collection) resource.get(ROLES)) + .orElse(Collections.emptyList()) + .stream(); + } +} diff --git a/infrastructure/src/main/java/com/callv2/drive/infrastructure/configuration/security/KeycloakJwtConverter.java b/infrastructure/src/main/java/com/callv2/drive/infrastructure/configuration/security/KeycloakJwtConverter.java new file mode 100644 index 00000000..2bf950b1 --- /dev/null +++ b/infrastructure/src/main/java/com/callv2/drive/infrastructure/configuration/security/KeycloakJwtConverter.java @@ -0,0 +1,32 @@ +package com.callv2.drive.infrastructure.configuration.security; + +import java.util.Collection; + +import org.springframework.core.convert.converter.Converter; +import org.springframework.security.authentication.AbstractAuthenticationToken; +import org.springframework.security.core.GrantedAuthority; +import org.springframework.security.oauth2.jwt.Jwt; +import org.springframework.security.oauth2.jwt.JwtClaimNames; +import org.springframework.security.oauth2.server.resource.authentication.JwtAuthenticationToken; + +public class KeycloakJwtConverter implements Converter { + + private final KeycloakAuthoritiesConverter authoritiesConverter; + + public KeycloakJwtConverter() { + this.authoritiesConverter = new KeycloakAuthoritiesConverter(); + } + + @Override + public AbstractAuthenticationToken convert(final Jwt jwt) { + return new JwtAuthenticationToken(jwt, extractAuthorities(jwt), extractPrincipal(jwt)); + } + + private String extractPrincipal(final Jwt jwt) { + return jwt.getClaimAsString(JwtClaimNames.SUB); + } + + private Collection extractAuthorities(final Jwt jwt) { + return this.authoritiesConverter.convert(jwt); + } +} diff --git a/infrastructure/src/main/java/com/callv2/drive/infrastructure/configuration/security/SecurityConfig.java b/infrastructure/src/main/java/com/callv2/drive/infrastructure/configuration/security/SecurityConfig.java index b8b94fdb..b12ca7be 100644 --- a/infrastructure/src/main/java/com/callv2/drive/infrastructure/configuration/security/SecurityConfig.java +++ b/infrastructure/src/main/java/com/callv2/drive/infrastructure/configuration/security/SecurityConfig.java @@ -1,31 +1,21 @@ package com.callv2.drive.infrastructure.configuration.security; -import java.util.Collection; -import java.util.Collections; -import java.util.Map; -import java.util.Optional; -import java.util.Set; -import java.util.function.Function; -import java.util.stream.Collectors; -import java.util.stream.Stream; +import java.util.List; +import org.springframework.boot.context.properties.ConfigurationProperties; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; -import org.springframework.core.convert.converter.Converter; import org.springframework.http.HttpMethod; -import org.springframework.security.authentication.AbstractAuthenticationToken; 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.http.SessionCreationPolicy; -import org.springframework.security.core.GrantedAuthority; -import org.springframework.security.core.authority.SimpleGrantedAuthority; -import org.springframework.security.oauth2.jwt.Jwt; -import org.springframework.security.oauth2.jwt.JwtClaimNames; -import org.springframework.security.oauth2.server.resource.authentication.JwtAuthenticationToken; import org.springframework.security.web.SecurityFilterChain; +import org.springframework.web.cors.CorsConfiguration; +import org.springframework.web.cors.CorsConfigurationSource; +import org.springframework.web.cors.UrlBasedCorsConfigurationSource; -import com.nimbusds.jose.shaded.gson.internal.LinkedTreeMap; +import com.callv2.drive.infrastructure.configuration.properties.cors.CorsConfigurationProperties; @Configuration @EnableWebSecurity @@ -35,11 +25,12 @@ public class SecurityConfig { private static final String ROLE_ADMIN = "ADMINISTRADOR"; @Bean - public SecurityFilterChain securityFilterChain(final HttpSecurity http) throws Exception { + SecurityFilterChain securityFilterChain( + final HttpSecurity http, + final CorsConfigurationSource corsConfigurationSource) throws Exception { return http - .csrf(csrf -> { - csrf.disable(); - }) + .cors(cors -> cors.configurationSource(corsConfigurationSource)) + .csrf(csrf -> csrf.disable()) .authorizeHttpRequests(authorize -> { authorize .requestMatchers("admin/**") @@ -63,76 +54,39 @@ public SecurityFilterChain securityFilterChain(final HttpSecurity http) throws E .build(); } - static class KeycloakJwtConverter implements Converter { - - private final KeycloakAuthoritiesConverter authoritiesConverter; - - public KeycloakJwtConverter() { - this.authoritiesConverter = new KeycloakAuthoritiesConverter(); - } + @Bean + CorsConfigurationSource corsConfigurationSource(final CorsConfigurationProperties corsProperties) { - @Override - public AbstractAuthenticationToken convert(final Jwt jwt) { - return new JwtAuthenticationToken(jwt, extractAuthorities(jwt), extractPrincipal(jwt)); - } + final UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource(); - private String extractPrincipal(final Jwt jwt) { - return jwt.getClaimAsString(JwtClaimNames.SUB); - } + source.registerCorsConfiguration( + corsProperties.getPattern(), + corsConfiguration( + corsProperties.getAllowedOriginsPatterns(), + corsProperties.getAllowedMethods(), + corsProperties.getAllowedHeaders(), + corsProperties.isAllowCredentials())); - private Collection extractAuthorities(final Jwt jwt) { - return this.authoritiesConverter.convert(jwt); - } + return source; } - static class KeycloakAuthoritiesConverter implements Converter> { - - private static final String REALM_ACCESS = "realm_access"; - private static final String ROLES = "roles"; - private static final String RESOURCE_ACCESS = "resource_access"; - private static final String SEPARATOR = "_"; - private static final String ROLE_PREFIX = "ROLE_"; - - @Override - public Collection convert(final Jwt jwt) { - final var realmRoles = extractRealmRoles(jwt); - final var resourceRoles = extractResourceRoles(jwt); - - return Stream.concat(realmRoles, resourceRoles) - .map(role -> new SimpleGrantedAuthority(ROLE_PREFIX + role.toUpperCase())) - .collect(Collectors.toSet()); - } - - private Stream extractResourceRoles(final Jwt jwt) { - - final Function, Stream> mapResource = resource -> { - final var key = resource.getKey(); - @SuppressWarnings("rawtypes") - final LinkedTreeMap value = (LinkedTreeMap) resource.getValue(); - @SuppressWarnings("unchecked") - final var roles = (Collection) value.get(ROLES); - return roles.stream().map(role -> key.concat(SEPARATOR).concat(role)); - }; - - final Function>, Collection> mapResources = resources -> resources - .stream() - .flatMap(mapResource) - .toList(); - - return Optional.ofNullable(jwt.getClaimAsMap(RESOURCE_ACCESS)) - .map(resources -> resources.entrySet()) - .map(mapResources) - .orElse(Collections.emptyList()) - .stream(); - } + static CorsConfiguration corsConfiguration( + final List allowedOriginsPatterns, + final List allowedMethods, + final List allowedHeaders, + final boolean allowCredentials) { + CorsConfiguration configuration = new CorsConfiguration(); + configuration.setAllowedOriginPatterns(allowedOriginsPatterns); + configuration.setAllowedMethods(allowedMethods); + configuration.setAllowedHeaders(allowedHeaders); + configuration.setAllowCredentials(allowCredentials); + return configuration; + } - @SuppressWarnings("unchecked") - private Stream extractRealmRoles(final Jwt jwt) { - return Optional.ofNullable(jwt.getClaimAsMap(REALM_ACCESS)) - .map(resource -> (Collection) resource.get(ROLES)) - .orElse(Collections.emptyList()) - .stream(); - } + @Bean + @ConfigurationProperties("security.cors") + CorsConfigurationProperties corsConfigurationProperties() { + return new CorsConfigurationProperties(); } } diff --git a/infrastructure/src/main/resources/application-env.yml b/infrastructure/src/main/resources/application-env.yml index 6a0f0baf..1ad58a86 100644 --- a/infrastructure/src/main/resources/application-env.yml +++ b/infrastructure/src/main/resources/application-env.yml @@ -2,6 +2,14 @@ server: port: ${SERVER_PORT} forward-headers-strategy: ${FORWARD_HEADERS_STRATEGY:none} +security: + cors: + pattern: ${CORS_PATTERN} + allowed-origins: ${CORS_ALLOWED_ORIGINS} + allowed-methods: ${CORS_ALLOWED_METHODS} + allowed-headers: ${CORS_ALLOWED_HEADERS} + allow-credentials: ${CORS_ALLOW_CREDENTIALS} + keycloak: realm: ${KEYCLOAK_REALM} host: ${KEYCLOAK_HOST} From 97905244b2e1515ece884540e83c2241cbbe47b2 Mon Sep 17 00:00:00 2001 From: jhonatapers Date: Tue, 27 May 2025 21:30:31 -0300 Subject: [PATCH 51/53] feat: allow OPTIONS requests for CORS and streamline session management configuration --- .../configuration/security/SecurityConfig.java | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/infrastructure/src/main/java/com/callv2/drive/infrastructure/configuration/security/SecurityConfig.java b/infrastructure/src/main/java/com/callv2/drive/infrastructure/configuration/security/SecurityConfig.java index b12ca7be..97a84cd5 100644 --- a/infrastructure/src/main/java/com/callv2/drive/infrastructure/configuration/security/SecurityConfig.java +++ b/infrastructure/src/main/java/com/callv2/drive/infrastructure/configuration/security/SecurityConfig.java @@ -33,6 +33,10 @@ SecurityFilterChain securityFilterChain( .csrf(csrf -> csrf.disable()) .authorizeHttpRequests(authorize -> { authorize + + .requestMatchers(HttpMethod.OPTIONS) + .permitAll() + .requestMatchers("admin/**") .hasAnyRole(ROLE_ADMIN) @@ -48,9 +52,7 @@ SecurityFilterChain securityFilterChain( }) .oauth2ResourceServer(oauth2ResourceServer -> oauth2ResourceServer .jwt(jwt -> jwt.jwtAuthenticationConverter(new KeycloakJwtConverter()))) - .sessionManagement(session -> { - session.sessionCreationPolicy(SessionCreationPolicy.STATELESS); - }) + .sessionManagement(session -> session.sessionCreationPolicy(SessionCreationPolicy.STATELESS)) .build(); } From f05f302befa7c62aa75b782c1f0310e54718d282 Mon Sep 17 00:00:00 2001 From: jhonatapers Date: Tue, 27 May 2025 21:40:22 -0300 Subject: [PATCH 52/53] feat: allow OPTIONS requests for all endpoints in security configuration --- .../infrastructure/configuration/security/SecurityConfig.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/infrastructure/src/main/java/com/callv2/drive/infrastructure/configuration/security/SecurityConfig.java b/infrastructure/src/main/java/com/callv2/drive/infrastructure/configuration/security/SecurityConfig.java index 97a84cd5..0ca1d9c1 100644 --- a/infrastructure/src/main/java/com/callv2/drive/infrastructure/configuration/security/SecurityConfig.java +++ b/infrastructure/src/main/java/com/callv2/drive/infrastructure/configuration/security/SecurityConfig.java @@ -34,7 +34,7 @@ SecurityFilterChain securityFilterChain( .authorizeHttpRequests(authorize -> { authorize - .requestMatchers(HttpMethod.OPTIONS) + .requestMatchers(HttpMethod.OPTIONS, "/**") .permitAll() .requestMatchers("admin/**") From 25f49540574a2501f4be179a21db49cb31657a08 Mon Sep 17 00:00:00 2001 From: jhonatapers Date: Wed, 28 May 2025 10:36:49 -0300 Subject: [PATCH 53/53] fix: rename allowedOriginsPatterns to allowedOrigins in CORS configuration --- .../properties/cors/CorsConfigurationProperties.java | 10 +++++----- .../configuration/security/SecurityConfig.java | 2 +- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/infrastructure/src/main/java/com/callv2/drive/infrastructure/configuration/properties/cors/CorsConfigurationProperties.java b/infrastructure/src/main/java/com/callv2/drive/infrastructure/configuration/properties/cors/CorsConfigurationProperties.java index 6ea40d80..f369194d 100644 --- a/infrastructure/src/main/java/com/callv2/drive/infrastructure/configuration/properties/cors/CorsConfigurationProperties.java +++ b/infrastructure/src/main/java/com/callv2/drive/infrastructure/configuration/properties/cors/CorsConfigurationProperties.java @@ -5,7 +5,7 @@ public class CorsConfigurationProperties { private String pattern; - private List allowedOriginsPatterns; + private List allowedOrigins; private List allowedMethods; private List allowedHeaders; private boolean allowCredentials; @@ -18,12 +18,12 @@ public void setPattern(String pattern) { this.pattern = pattern; } - public List getAllowedOriginsPatterns() { - return allowedOriginsPatterns; + public List getAllowedOrigins() { + return allowedOrigins; } - public void setAllowedOriginsPatterns(List allowedOriginsPatterns) { - this.allowedOriginsPatterns = allowedOriginsPatterns; + public void setAllowedOrigins(List allowedOriginsPatterns) { + this.allowedOrigins = allowedOriginsPatterns; } public List getAllowedMethods() { diff --git a/infrastructure/src/main/java/com/callv2/drive/infrastructure/configuration/security/SecurityConfig.java b/infrastructure/src/main/java/com/callv2/drive/infrastructure/configuration/security/SecurityConfig.java index 0ca1d9c1..6995bff3 100644 --- a/infrastructure/src/main/java/com/callv2/drive/infrastructure/configuration/security/SecurityConfig.java +++ b/infrastructure/src/main/java/com/callv2/drive/infrastructure/configuration/security/SecurityConfig.java @@ -64,7 +64,7 @@ CorsConfigurationSource corsConfigurationSource(final CorsConfigurationPropertie source.registerCorsConfiguration( corsProperties.getPattern(), corsConfiguration( - corsProperties.getAllowedOriginsPatterns(), + corsProperties.getAllowedOrigins(), corsProperties.getAllowedMethods(), corsProperties.getAllowedHeaders(), corsProperties.isAllowCredentials()));