From c8935a8488f0508f07c534517ffd0f1def388300 Mon Sep 17 00:00:00 2001 From: solufa Date: Mon, 30 Dec 2024 00:50:13 +0900 Subject: [PATCH] feat: implement DeleteUserPoolCommand and DeleteUserPoolClientCommand --- IMPLEMENTATION_COVERAGE.md | 8 +-- server/api/controller.ts | 3 + server/common/types/auth.ts | 19 +++++++ .../userPool/repository/userPoolCommand.ts | 12 ++++ .../userPool/repository/userPoolQuery.ts | 8 +++ .../userPool/useCase/userPoolUseCase.ts | 38 +++++++++++++ server/tests/sdk/pool.test.ts | 56 +++++++++++++++++++ 7 files changed, 140 insertions(+), 4 deletions(-) diff --git a/IMPLEMENTATION_COVERAGE.md b/IMPLEMENTATION_COVERAGE.md index 99aedc1..56c816d 100644 --- a/IMPLEMENTATION_COVERAGE.md +++ b/IMPLEMENTATION_COVERAGE.md @@ -3,7 +3,7 @@ ## cognito-idp
- 26% implemented + 29% implemented - [ ] AddCustomAttributes - [ ] AdminAddUserToGroup @@ -49,8 +49,8 @@ - [ ] DeleteResourceServer - [ ] DeleteUser - [x] DeleteUserAttributes -- [ ] DeleteUserPool -- [ ] DeleteUserPoolClient +- [x] DeleteUserPool +- [x] DeleteUserPoolClient - [ ] DeleteUserPoolDomain - [ ] DescribeIdentityProvider - [ ] DescribeResourceServer @@ -79,7 +79,7 @@ - [ ] ListResourceServers - [ ] ListTagsForResource - [ ] ListUserImportJobs -- [ ] ListUserPoolClients +- [x] ListUserPoolClients - [x] ListUserPools - [x] ListUsers - [ ] ListUsersInGroup diff --git a/server/api/controller.ts b/server/api/controller.ts index ca01f8a..a8e2735 100644 --- a/server/api/controller.ts +++ b/server/api/controller.ts @@ -25,8 +25,11 @@ const useCases: { 'AWSCognitoIdentityProviderService.RevokeToken': authUseCase.revokeToken, 'AWSCognitoIdentityProviderService.ResendConfirmationCode': signUpUseCase.resendConfirmationCode, 'AWSCognitoIdentityProviderService.ListUserPools': userPoolUseCase.listUserPools, + 'AWSCognitoIdentityProviderService.ListUserPoolClients': userPoolUseCase.listUserPoolClients, 'AWSCognitoIdentityProviderService.CreateUserPool': userPoolUseCase.createUserPool, 'AWSCognitoIdentityProviderService.CreateUserPoolClient': userPoolUseCase.createUserPoolClient, + 'AWSCognitoIdentityProviderService.DeleteUserPool': userPoolUseCase.deleteUserPool, + 'AWSCognitoIdentityProviderService.DeleteUserPoolClient': userPoolUseCase.deleteUserPoolClient, 'AWSCognitoIdentityProviderService.ListUsers': authUseCase.listUsers, 'AWSCognitoIdentityProviderService.AdminGetUser': adminUseCase.getUser, 'AWSCognitoIdentityProviderService.AdminCreateUser': adminUseCase.createUser, diff --git a/server/common/types/auth.ts b/server/common/types/auth.ts index 3cfb5b0..4cee8d4 100644 --- a/server/common/types/auth.ts +++ b/server/common/types/auth.ts @@ -21,7 +21,11 @@ import type { CreateUserPoolResponse, DeleteUserAttributesRequest, DeleteUserAttributesResponse, + DeleteUserPoolClientRequest, + DeleteUserPoolRequest, GetUserResponse, + ListUserPoolClientsRequest, + ListUserPoolClientsResponse, ListUserPoolsRequest, ListUserPoolsResponse, ListUsersRequest, @@ -65,6 +69,11 @@ export type ListUsersTarget = TargetBody; export type ListUserPoolsTarget = TargetBody; +export type ListUserPoolClientsTarget = TargetBody< + ListUserPoolClientsRequest, + ListUserPoolClientsResponse +>; + export type CreateUserPoolTarget = TargetBody; export type CreateUserPoolClientTarget = TargetBody< @@ -72,6 +81,13 @@ export type CreateUserPoolClientTarget = TargetBody< CreateUserPoolClientResponse >; +export type DeleteUserPoolTarget = TargetBody>; + +export type DeleteUserPoolClientTarget = TargetBody< + DeleteUserPoolClientRequest, + Record +>; + export type AdminGetUserTarget = TargetBody; export type AdminCreateUserTarget = TargetBody; @@ -158,8 +174,11 @@ export type AmzTargets = { 'AWSCognitoIdentityProviderService.ResendConfirmationCode': ResendConfirmationCodeTarget; 'AWSCognitoIdentityProviderService.ListUsers': ListUsersTarget; 'AWSCognitoIdentityProviderService.ListUserPools': ListUserPoolsTarget; + 'AWSCognitoIdentityProviderService.ListUserPoolClients': ListUserPoolClientsTarget; 'AWSCognitoIdentityProviderService.CreateUserPool': CreateUserPoolTarget; 'AWSCognitoIdentityProviderService.CreateUserPoolClient': CreateUserPoolClientTarget; + 'AWSCognitoIdentityProviderService.DeleteUserPool': DeleteUserPoolTarget; + 'AWSCognitoIdentityProviderService.DeleteUserPoolClient': DeleteUserPoolClientTarget; 'AWSCognitoIdentityProviderService.AdminGetUser': AdminGetUserTarget; 'AWSCognitoIdentityProviderService.AdminCreateUser': AdminCreateUserTarget; 'AWSCognitoIdentityProviderService.AdminDeleteUser': AdminDeleteUserTarget; diff --git a/server/domain/userPool/repository/userPoolCommand.ts b/server/domain/userPool/repository/userPoolCommand.ts index b55c9a3..088a8b7 100644 --- a/server/domain/userPool/repository/userPoolCommand.ts +++ b/server/domain/userPool/repository/userPoolCommand.ts @@ -29,4 +29,16 @@ export const userPoolCommand = { }, }); }, + delete: async (tx: Prisma.TransactionClient, pool: UserPoolEntity): Promise => { + await tx.userAttribute.deleteMany({ where: { User: { userPoolId: pool.id } } }); + await tx.user.deleteMany({ where: { userPoolId: pool.id } }); + await tx.userPoolClient.deleteMany({ where: { userPoolId: pool.id } }); + await tx.userPool.delete({ where: { id: pool.id } }); + }, + deleteClient: async ( + tx: Prisma.TransactionClient, + poolClient: UserPoolClientEntity, + ): Promise => { + await tx.userPoolClient.delete({ where: { id: poolClient.id } }); + }, }; diff --git a/server/domain/userPool/repository/userPoolQuery.ts b/server/domain/userPool/repository/userPoolQuery.ts index 2795164..a1905be 100644 --- a/server/domain/userPool/repository/userPoolQuery.ts +++ b/server/domain/userPool/repository/userPoolQuery.ts @@ -6,6 +6,14 @@ import { toUserPoolClientEntity, toUserPoolEntity } from './toUserPoolEntity'; export const userPoolQuery = { listAll: (tx: Prisma.TransactionClient, limit?: number): Promise => tx.userPool.findMany({ take: limit }).then((pools) => pools.map(toUserPoolEntity)), + listClientAll: ( + tx: Prisma.TransactionClient, + userPoolId: string, + limit?: number, + ): Promise => + tx.userPoolClient + .findMany({ where: { userPoolId }, take: limit }) + .then((clients) => clients.map(toUserPoolClientEntity)), findById: (tx: Prisma.TransactionClient, userPoolId: string): Promise => tx.userPool.findUniqueOrThrow({ where: { id: userPoolId } }).then(toUserPoolEntity), findJwks: (tx: Prisma.TransactionClient, userPoolId: string): Promise => diff --git a/server/domain/userPool/useCase/userPoolUseCase.ts b/server/domain/userPool/useCase/userPoolUseCase.ts index f472876..b64e2f2 100644 --- a/server/domain/userPool/useCase/userPoolUseCase.ts +++ b/server/domain/userPool/useCase/userPoolUseCase.ts @@ -2,6 +2,9 @@ import assert from 'assert'; import type { CreateUserPoolClientTarget, CreateUserPoolTarget, + DeleteUserPoolClientTarget, + DeleteUserPoolTarget, + ListUserPoolClientsTarget, ListUserPoolsTarget, } from 'common/types/auth'; import { DEFAULT_USER_POOL_CLIENT_ID, DEFAULT_USER_POOL_ID } from 'service/envValues'; @@ -40,6 +43,15 @@ export const userPoolUseCase = { return { UserPools: pools.map((p) => ({ Id: p.id, Name: p.name })) }; }, + listUserPoolClients: async ( + req: ListUserPoolClientsTarget['reqBody'], + ): Promise => { + assert(req.UserPoolId); + + const clients = await userPoolQuery.listClientAll(prismaClient, req.UserPoolId, req.MaxResults); + + return { UserPoolClients: clients.map((c) => ({ ClientId: c.id, ClientName: c.name })) }; + }, createUserPool: ( req: CreateUserPoolTarget['reqBody'], ): Promise => @@ -66,4 +78,30 @@ export const userPoolUseCase = { UserPoolClient: { ClientId: client.id, UserPoolId: pool.id, ClientName: client.name }, }; }), + deleteUserPool: ( + req: DeleteUserPoolTarget['reqBody'], + ): Promise => + transaction(async (tx) => { + assert(req.UserPoolId); + + const pool = await userPoolQuery.findById(tx, req.UserPoolId); + + await userPoolCommand.delete(tx, pool); + + return {}; + }), + deleteUserPoolClient: ( + req: DeleteUserPoolClientTarget['reqBody'], + ): Promise => + transaction(async (tx) => { + assert(req.ClientId); + + const client = await userPoolQuery.findClientById(tx, req.ClientId); + + assert(client.userPoolId === req.UserPoolId); + + await userPoolCommand.deleteClient(tx, client); + + return {}; + }), }; diff --git a/server/tests/sdk/pool.test.ts b/server/tests/sdk/pool.test.ts index b1bc956..61a95eb 100644 --- a/server/tests/sdk/pool.test.ts +++ b/server/tests/sdk/pool.test.ts @@ -1,6 +1,11 @@ import { + AdminCreateUserCommand, CreateUserPoolClientCommand, CreateUserPoolCommand, + DeleteUserPoolClientCommand, + DeleteUserPoolCommand, + ListUserPoolClientsCommand, + ListUserPoolsCommand, } from '@aws-sdk/client-cognito-identity-provider'; import { cognitoClient } from 'service/cognito'; import { expect, test } from 'vitest'; @@ -14,3 +19,54 @@ test(CreateUserPoolCommand.name, async () => { expect(pool.UserPool?.Name === 'testPool').toBeTruthy(); expect(client.UserPoolClient?.ClientName === 'testClient').toBeTruthy(); }); + +// eslint-disable-next-line complexity +test(DeleteUserPoolCommand.name, async () => { + const pool = await cognitoClient.send(new CreateUserPoolCommand({ PoolName: 'testPool' })); + + await cognitoClient.send( + new CreateUserPoolClientCommand({ ClientName: 'testClient', UserPoolId: pool.UserPool?.Id }), + ); + await cognitoClient.send( + new AdminCreateUserCommand({ + UserPoolId: pool.UserPool?.Id, + Username: 'test', + UserAttributes: [{ Name: 'email', Value: 'test@example.com' }], + }), + ); + + const res1 = await cognitoClient.send(new ListUserPoolsCommand({ MaxResults: 100 })); + + await cognitoClient.send(new DeleteUserPoolCommand({ UserPoolId: pool.UserPool?.Id })); + + const res2 = await cognitoClient.send(new ListUserPoolsCommand({ MaxResults: 100 })); + + expect(res1.UserPools?.length).toBe(2); + expect(res2.UserPools?.length).toBe(1); +}); + +// eslint-disable-next-line complexity +test(DeleteUserPoolClientCommand.name, async () => { + const pool = await cognitoClient.send(new CreateUserPoolCommand({ PoolName: 'testPool' })); + const client = await cognitoClient.send( + new CreateUserPoolClientCommand({ ClientName: 'testClient', UserPoolId: pool.UserPool?.Id }), + ); + + const res1 = await cognitoClient.send( + new ListUserPoolClientsCommand({ UserPoolId: pool.UserPool?.Id, MaxResults: 100 }), + ); + + await cognitoClient.send( + new DeleteUserPoolClientCommand({ + ClientId: client.UserPoolClient?.ClientId, + UserPoolId: pool.UserPool?.Id, + }), + ); + + const res2 = await cognitoClient.send( + new ListUserPoolClientsCommand({ UserPoolId: pool.UserPool?.Id, MaxResults: 100 }), + ); + + expect(res1.UserPoolClients?.length).toBe(1); + expect(res2.UserPoolClients?.length).toBe(0); +});