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
diff --git a/.github/workflows/group-ms-workflow.yaml b/.github/workflows/group-ms-workflow.yaml
index ab61520..974032f 100644
--- a/.github/workflows/group-ms-workflow.yaml
+++ b/.github/workflows/group-ms-workflow.yaml
@@ -5,6 +5,9 @@ on:
paths:
- 'GroupMicroservice/**'
- 'GroupMicroservice.Tests/**'
+ - '.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 7f2ae48..28565aa 100644
--- a/.github/workflows/user-ms-workflow.yaml
+++ b/.github/workflows/user-ms-workflow.yaml
@@ -5,6 +5,9 @@ on:
paths:
- 'UserMicroservice/**'
- 'UserMicroservice.Tests/**'
+ - '.github/workflows/user-ms-workflow.yaml'
+ - '.github/actions/check-schema/**'
+ - '.github/actions/verify-pact-contract/**'
workflow_call:
secrets:
PACT_BROKER:
@@ -14,16 +17,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 +41,22 @@ 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
+ 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
+ echo "The branch name in Group Microservice is: $GROUP_MS_BRANCH"
+
- 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 }}
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 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
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 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
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 2bb0a16..48ff635 100644
--- a/UserMicroservice/Presentation/Apis/UserApi.cs
+++ b/UserMicroservice/Presentation/Apis/UserApi.cs
@@ -25,7 +25,10 @@ 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": {