From d681cfcb7ab13bf3e8055ec69c9c1f9361c36023 Mon Sep 17 00:00:00 2001 From: Julia Ilasova <1julka1il@gmail.com> Date: Sat, 17 May 2025 14:32:33 +0200 Subject: [PATCH 1/8] try to correlate with Group MS feature branch --- .github/workflows/user-ms-workflow.yaml | 24 +++++++++++++----------- 1 file changed, 13 insertions(+), 11 deletions(-) diff --git a/.github/workflows/user-ms-workflow.yaml b/.github/workflows/user-ms-workflow.yaml index 7f2ae48..212c634 100644 --- a/.github/workflows/user-ms-workflow.yaml +++ b/.github/workflows/user-ms-workflow.yaml @@ -14,16 +14,11 @@ on: PACT_BROKER_PASSWORD: required: true workflow_dispatch: - -# inputs: -# consumer: -# required: true -# consumerVersion: -# required: true -# consumerVersionTags: -# required: false -# pactUrl: -# required: true + inputs: + group-ms-branch: + description: 'Branch name in Group Microservice' + required: true + default: 'main' jobs: check-openapi-schema: @@ -43,10 +38,17 @@ jobs: name: Verify consumer contract needs: check-openapi-schema steps: + - name: Extract Group MS branch from PR description + id: extract-branch + run: | + echo "PR_BODY=${{ github.event.pull_request.body }}" >> $GITHUB_ENV + GROUP_MS_BRANCH=$(echo "${{ github.event.pull_request.body }}" | grep -oP '(?<=GroupMS: )[^\s]+') + echo "group-ms-branch=${GROUP_MS_BRANCH:-main}" >> $GITHUB_ENV + - name: Verify consumer contract uses: ./.github/actions/verify-pact-contract with: pact_broker: ${{ secrets.PACT_BROKER }} pact_broker_username: ${{ secrets.PACT_BROKER_USERNAME }} pact_broker_password: ${{ secrets.PACT_BROKER_PASSWORD }} - \ No newline at end of file + consumer_tags: ${{ env.group-ms-branch || inputs.group-ms-branch }} From 1af301fb62b05dcf0bf73ca49d335454b7e68b4f Mon Sep 17 00:00:00 2001 From: Julia Ilasova <1julka1il@gmail.com> Date: Sat, 17 May 2025 14:36:08 +0200 Subject: [PATCH 2/8] skip extracting group ms branch if not a pr --- .github/workflows/user-ms-workflow.yaml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/user-ms-workflow.yaml b/.github/workflows/user-ms-workflow.yaml index 212c634..49f753c 100644 --- a/.github/workflows/user-ms-workflow.yaml +++ b/.github/workflows/user-ms-workflow.yaml @@ -39,6 +39,7 @@ jobs: needs: check-openapi-schema steps: - name: Extract Group MS branch from PR description + if: ${{ github.event_name == 'pull_request' }} id: extract-branch run: | echo "PR_BODY=${{ github.event.pull_request.body }}" >> $GITHUB_ENV From cde1b7d9f4410fc1862dbf4acf48d72695eb7325 Mon Sep 17 00:00:00 2001 From: Julia Ilasova <1julka1il@gmail.com> Date: Sat, 17 May 2025 14:39:48 +0200 Subject: [PATCH 3/8] explicit code checkout --- Docs/workflow-diagram.drawio | 155 +++++++++++++++++++++++++++++++++++ 1 file changed, 155 insertions(+) create mode 100644 Docs/workflow-diagram.drawio diff --git a/Docs/workflow-diagram.drawio b/Docs/workflow-diagram.drawio new file mode 100644 index 0000000..890c340 --- /dev/null +++ b/Docs/workflow-diagram.drawio @@ -0,0 +1,155 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + From 8f4001be38f5456b5436bca53e654b2ecda1c0b5 Mon Sep 17 00:00:00 2001 From: Julia Ilasova <1julka1il@gmail.com> Date: Sat, 17 May 2025 14:40:29 +0200 Subject: [PATCH 4/8] oops wrong files committed --- .github/workflows/user-ms-workflow.yaml | 3 + Docs/check-schema-diagram.drawio | 73 +++++++++++++++++++++++++ 2 files changed, 76 insertions(+) create mode 100644 Docs/check-schema-diagram.drawio diff --git a/.github/workflows/user-ms-workflow.yaml b/.github/workflows/user-ms-workflow.yaml index 49f753c..1e32123 100644 --- a/.github/workflows/user-ms-workflow.yaml +++ b/.github/workflows/user-ms-workflow.yaml @@ -38,6 +38,9 @@ jobs: name: Verify consumer contract needs: check-openapi-schema steps: + - name: Checkout code + uses: actions/checkout@v4 + - name: Extract Group MS branch from PR description if: ${{ github.event_name == 'pull_request' }} id: extract-branch diff --git a/Docs/check-schema-diagram.drawio b/Docs/check-schema-diagram.drawio new file mode 100644 index 0000000..54f14aa --- /dev/null +++ b/Docs/check-schema-diagram.drawio @@ -0,0 +1,73 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + From 063b53770f5c2981ee7ea24d92c1b27995644b17 Mon Sep 17 00:00:00 2001 From: Julia Ilasova <1julka1il@gmail.com> Date: Sat, 17 May 2025 14:43:32 +0200 Subject: [PATCH 5/8] . --- .github/workflows/group-ms-workflow.yaml | 2 ++ .github/workflows/user-ms-workflow.yaml | 2 ++ UserMicroservice/Presentation/Apis/UserApi.cs | 2 +- 3 files changed, 5 insertions(+), 1 deletion(-) diff --git a/.github/workflows/group-ms-workflow.yaml b/.github/workflows/group-ms-workflow.yaml index ab61520..68fde6f 100644 --- a/.github/workflows/group-ms-workflow.yaml +++ b/.github/workflows/group-ms-workflow.yaml @@ -5,6 +5,8 @@ on: paths: - 'GroupMicroservice/**' - 'GroupMicroservice.Tests/**' + - '.github/workflows/**' + - '.github/actions/**' workflow_call: secrets: PACT_BROKER: diff --git a/.github/workflows/user-ms-workflow.yaml b/.github/workflows/user-ms-workflow.yaml index 1e32123..ca8b10e 100644 --- a/.github/workflows/user-ms-workflow.yaml +++ b/.github/workflows/user-ms-workflow.yaml @@ -5,6 +5,8 @@ on: paths: - 'UserMicroservice/**' - 'UserMicroservice.Tests/**' + - '.github/workflows/**' + - '.github/actions/**' workflow_call: secrets: PACT_BROKER: diff --git a/UserMicroservice/Presentation/Apis/UserApi.cs b/UserMicroservice/Presentation/Apis/UserApi.cs index 2bb0a16..f9fcfac 100644 --- a/UserMicroservice/Presentation/Apis/UserApi.cs +++ b/UserMicroservice/Presentation/Apis/UserApi.cs @@ -25,7 +25,7 @@ public static RouteGroupBuilder AddUserApi(this IEndpointRouteBuilder app) api.MapGet("/{userId:guid}", GetUserByIdAsync) .WithName("GetUserById"); - + return api; } From d0abc75b59f97e88eb76e468633d982b0e63aa0b Mon Sep 17 00:00:00 2001 From: Julia Ilasova <1julka1il@gmail.com> Date: Sat, 17 May 2025 14:44:32 +0200 Subject: [PATCH 6/8] . --- .github/workflows/group-ms-workflow.yaml | 5 +++-- .github/workflows/user-ms-workflow.yaml | 5 +++-- 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/.github/workflows/group-ms-workflow.yaml b/.github/workflows/group-ms-workflow.yaml index 68fde6f..974032f 100644 --- a/.github/workflows/group-ms-workflow.yaml +++ b/.github/workflows/group-ms-workflow.yaml @@ -5,8 +5,9 @@ on: paths: - 'GroupMicroservice/**' - 'GroupMicroservice.Tests/**' - - '.github/workflows/**' - - '.github/actions/**' + - '.github/workflows/group-ms-workflow.yaml' + - '.github/actions/check-schema/**' + - '.github/actions/check-consumer-contract/**' workflow_call: secrets: PACT_BROKER: diff --git a/.github/workflows/user-ms-workflow.yaml b/.github/workflows/user-ms-workflow.yaml index ca8b10e..b5b020e 100644 --- a/.github/workflows/user-ms-workflow.yaml +++ b/.github/workflows/user-ms-workflow.yaml @@ -5,8 +5,9 @@ on: paths: - 'UserMicroservice/**' - 'UserMicroservice.Tests/**' - - '.github/workflows/**' - - '.github/actions/**' + - '.github/workflows/user-ms-workflow.yaml' + - '.github/actions/check-schema/**' + - '.github/actions/verify-pact-contract/**' workflow_call: secrets: PACT_BROKER: From 2be332f388add57ad41abb39b2e66780ed5d964b Mon Sep 17 00:00:00 2001 From: Julia Ilasova <1julka1il@gmail.com> Date: Sat, 17 May 2025 14:45:54 +0200 Subject: [PATCH 7/8] update composite action with correct syntax --- .github/actions/verify-pact-contract/action.yaml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/.github/actions/verify-pact-contract/action.yaml b/.github/actions/verify-pact-contract/action.yaml index de66113..8ad264a 100644 --- a/.github/actions/verify-pact-contract/action.yaml +++ b/.github/actions/verify-pact-contract/action.yaml @@ -10,7 +10,7 @@ inputs: consumer_tags: required: false -verify-contract: +runs: using: "composite" steps: - name: Checkout code @@ -22,6 +22,7 @@ verify-contract: dotnet-version: '9.0.x' - name: Verify consumer contract against provider + shell: bash run: | echo "Verifying pact..." cd UserMicroservice.Tests From e0f950c0fb65fb9f00ead24a5ea543d2418dc3c6 Mon Sep 17 00:00:00 2001 From: Julia Ilasova <1julka1il@gmail.com> Date: Sat, 17 May 2025 15:37:49 +0200 Subject: [PATCH 8/8] add batch user retrieval functionality --- .github/workflows/user-ms-workflow.yaml | 1 + .../Setup/MockUserRepository.cs | 6 ++ .../Setup/ProviderStateMiddleware.cs | 71 ++++++++++++++----- UserMicroservice/Application/IUserService.cs | 1 + UserMicroservice/Application/UserService.cs | 11 +++ .../Infrastructure/IUserRepository.cs | 1 + .../Infrastructure/UserRepository.cs | 7 ++ UserMicroservice/Presentation/Apis/UserApi.cs | 11 +++ UserMicroservice/openapi-schema.json | 37 ++++++++++ 9 files changed, 127 insertions(+), 19 deletions(-) diff --git a/.github/workflows/user-ms-workflow.yaml b/.github/workflows/user-ms-workflow.yaml index b5b020e..28565aa 100644 --- a/.github/workflows/user-ms-workflow.yaml +++ b/.github/workflows/user-ms-workflow.yaml @@ -51,6 +51,7 @@ jobs: echo "PR_BODY=${{ github.event.pull_request.body }}" >> $GITHUB_ENV GROUP_MS_BRANCH=$(echo "${{ github.event.pull_request.body }}" | grep -oP '(?<=GroupMS: )[^\s]+') echo "group-ms-branch=${GROUP_MS_BRANCH:-main}" >> $GITHUB_ENV + echo "The branch name in Group Microservice is: $GROUP_MS_BRANCH" - name: Verify consumer contract uses: ./.github/actions/verify-pact-contract diff --git a/UserMicroservice.Tests/Setup/MockUserRepository.cs b/UserMicroservice.Tests/Setup/MockUserRepository.cs index f6cbd41..18ca4a7 100644 --- a/UserMicroservice.Tests/Setup/MockUserRepository.cs +++ b/UserMicroservice.Tests/Setup/MockUserRepository.cs @@ -48,4 +48,10 @@ public Task DeleteUserAsync(UserEntity user) } return Task.CompletedTask; } + + public Task> GetUsersByIdsAsync(List userIds) + { + var users = _users.Where(u => userIds.Contains(u.Id)).ToList(); + return Task.FromResult(users); + } } \ No newline at end of file diff --git a/UserMicroservice.Tests/Setup/ProviderStateMiddleware.cs b/UserMicroservice.Tests/Setup/ProviderStateMiddleware.cs index 6535de3..888aeb3 100644 --- a/UserMicroservice.Tests/Setup/ProviderStateMiddleware.cs +++ b/UserMicroservice.Tests/Setup/ProviderStateMiddleware.cs @@ -24,6 +24,7 @@ public class ProviderStateMiddleware /// Initialises a new instance of the class. /// /// Next request delegate + /// public ProviderStateMiddleware(RequestDelegate next, IUserRepository userRepository) { _next = next; @@ -31,28 +32,14 @@ public ProviderStateMiddleware(RequestDelegate next, IUserRepository userReposit _providerStates = new Dictionary, HttpContext, Task>> { - ["a user with id {id} exists"] = EnsureEventExistsAsync, - ["a user with id {id} does not exist"] = async (parameters, httpContext) => - { - var id = parameters["id"].ToString(); - var user = await userRepository.GetUserByIdAsync(Guid.Parse(id)); - if (user == null) - { - return; - } - - await userRepository.DeleteUserAsync(user); - } + ["a user with id {id} exists"] = EnsureUserExistsAsync, + ["a user with id {id} does not exist"] = EnsureUserDoesNotExistAsync, + ["users with the specified IDs exist"] = EnsureValidBatchRequestAsync, + ["users with the specified IDs do not exist"] = EnsureInvalidBatchRequestAsync }; } - /// - /// Ensure an event exists - /// - /// Event parameters - /// - /// Awaitable - private async Task EnsureEventExistsAsync(IDictionary parameters, HttpContext httpContext) + private async Task EnsureUserExistsAsync(IDictionary parameters, HttpContext httpContext) { var id = Guid.Parse(parameters["id"].ToString()); var existingUser = await _userRepository.GetUserByIdAsync(id); @@ -68,6 +55,52 @@ await _userRepository.CreateUserAsync(new UserEntity }); } } + + private async Task EnsureUserDoesNotExistAsync(IDictionary parameters, HttpContext httpContext) + { + var id = Guid.Parse(parameters["id"].ToString()); + var existingUser = await _userRepository.GetUserByIdAsync(id); + if (existingUser != null) + { + await _userRepository.DeleteUserAsync(existingUser); + } + } + + private async Task EnsureValidBatchRequestAsync(IDictionary arg1, HttpContext arg2) + { + var userIds = arg1["ids"].ToString().Split(","); + foreach (var user in userIds) + { + var id = Guid.Parse(user); + var existingUser = await _userRepository.GetUserByIdAsync(id); + if (existingUser == null) + { + await _userRepository.CreateUserAsync(new UserEntity + { + Id = id, + Email = $"{id.ToString()}@app.com", + Nickname = id.ToString(), + CreatedAt = new DateTime(2009, 7, 27, 0, 0, 0), + Password = "password" + }); + } + } + } + + private async Task EnsureInvalidBatchRequestAsync(IDictionary arg1, HttpContext arg2) + { + var userIds = arg1["ids"].ToString().Split(","); + + foreach (var user in userIds) + { + var id = Guid.Parse(user); + var existingUser = await _userRepository.GetUserByIdAsync(id); + if (existingUser != null) + { + await _userRepository.DeleteUserAsync(existingUser); + } + } + } /// /// Handle the request diff --git a/UserMicroservice/Application/IUserService.cs b/UserMicroservice/Application/IUserService.cs index 2493c8e..e6ec2f9 100644 --- a/UserMicroservice/Application/IUserService.cs +++ b/UserMicroservice/Application/IUserService.cs @@ -9,4 +9,5 @@ public interface IUserService Task CreateUserAsync(CreateUserDto user); Task UpdateUserAsync(UpdateUserDto user); Task DeleteUserAsync(Guid userId); + Task> GetUsersAsync(List userIds); } \ No newline at end of file diff --git a/UserMicroservice/Application/UserService.cs b/UserMicroservice/Application/UserService.cs index 282cfc0..495aa27 100644 --- a/UserMicroservice/Application/UserService.cs +++ b/UserMicroservice/Application/UserService.cs @@ -56,6 +56,17 @@ public async Task DeleteUserAsync(Guid userId) await userRepository.DeleteUserAsync(existingUser); } + public async Task> GetUsersAsync(List userIds) + { + var users = await userRepository.GetUsersByIdsAsync(userIds); + if (users == null || users.Count == 0) + { + throw new NotFoundException("No users found for the provided IDs."); + } + + return users.Select(user => user.ToGetUserDto()).ToList(); + } + private async Task GetUserEntityByIdAsync(Guid userId) { var user = await userRepository.GetUserByIdAsync(userId); diff --git a/UserMicroservice/Infrastructure/IUserRepository.cs b/UserMicroservice/Infrastructure/IUserRepository.cs index 49a3225..830588d 100644 --- a/UserMicroservice/Infrastructure/IUserRepository.cs +++ b/UserMicroservice/Infrastructure/IUserRepository.cs @@ -9,4 +9,5 @@ public interface IUserRepository Task CreateUserAsync(UserEntity user); Task UpdateUserAsync(UserEntity user); Task DeleteUserAsync(UserEntity user); + Task> GetUsersByIdsAsync(List userIds); } \ No newline at end of file diff --git a/UserMicroservice/Infrastructure/UserRepository.cs b/UserMicroservice/Infrastructure/UserRepository.cs index cc44e8b..25b2ced 100644 --- a/UserMicroservice/Infrastructure/UserRepository.cs +++ b/UserMicroservice/Infrastructure/UserRepository.cs @@ -34,4 +34,11 @@ public async Task DeleteUserAsync(UserEntity user) context.Users.Remove(user); await context.SaveChangesAsync(); } + + public async Task> GetUsersByIdsAsync(List userIds) + { + return await context.Users + .Where(u => userIds.Contains(u.Id)) + .ToListAsync(); + } } \ No newline at end of file diff --git a/UserMicroservice/Presentation/Apis/UserApi.cs b/UserMicroservice/Presentation/Apis/UserApi.cs index f9fcfac..48ff635 100644 --- a/UserMicroservice/Presentation/Apis/UserApi.cs +++ b/UserMicroservice/Presentation/Apis/UserApi.cs @@ -26,6 +26,9 @@ public static RouteGroupBuilder AddUserApi(this IEndpointRouteBuilder app) api.MapGet("/{userId:guid}", GetUserByIdAsync) .WithName("GetUserById"); + api.MapPost("/batch", GetUsersAsync) + .WithName("GetUsers"); + return api; } @@ -60,4 +63,12 @@ private static async Task, ProblemHttpResult>> GetUserByI var user = await userService.GetUserByIdAsync(userId); return TypedResults.Ok(user); } + + private static async Task>, ProblemHttpResult>> GetUsersAsync( + [FromBody] List userIds, + [FromServices] IUserService userService) + { + var users = await userService.GetUsersAsync(userIds); + return TypedResults.Ok(users); + } } \ No newline at end of file diff --git a/UserMicroservice/openapi-schema.json b/UserMicroservice/openapi-schema.json index a1ac03c..1975cfb 100644 --- a/UserMicroservice/openapi-schema.json +++ b/UserMicroservice/openapi-schema.json @@ -115,6 +115,43 @@ } } } + }, + "/api/v1/user/batch": { + "post": { + "tags": [ + "User" + ], + "operationId": "GetUsers", + "requestBody": { + "content": { + "application/json": { + "schema": { + "type": "array", + "items": { + "type": "string", + "format": "uuid" + } + } + } + }, + "required": true + }, + "responses": { + "200": { + "description": "OK", + "content": { + "application/json": { + "schema": { + "type": "array", + "items": { + "$ref": "#/components/schemas/GetUserDto" + } + } + } + } + } + } + } } }, "components": {