Skip to content

Commit

Permalink
feat: implement DeleteUserCommand
Browse files Browse the repository at this point in the history
  • Loading branch information
solufa committed Dec 29, 2024
1 parent c8935a8 commit ad1accd
Show file tree
Hide file tree
Showing 9 changed files with 67 additions and 11 deletions.
4 changes: 2 additions & 2 deletions IMPLEMENTATION_COVERAGE.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
## cognito-idp

<details>
<summary> 29% implemented </summary>
<summary> 30% implemented </summary>

- [ ] AddCustomAttributes
- [ ] AdminAddUserToGroup
Expand Down Expand Up @@ -47,7 +47,7 @@
- [ ] DeleteGroup
- [ ] DeleteIdentityProvider
- [ ] DeleteResourceServer
- [ ] DeleteUser
- [x] DeleteUser
- [x] DeleteUserAttributes
- [x] DeleteUserPool
- [x] DeleteUserPoolClient
Expand Down
2 changes: 2 additions & 0 deletions server/api/controller.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import { authUseCase } from 'domain/user/useCase/authUseCase';
import { mfaUseCase } from 'domain/user/useCase/mfaUseCase';
import { signInUseCase } from 'domain/user/useCase/signInUseCase';
import { signUpUseCase } from 'domain/user/useCase/signUpUseCase';
import { userUseCase } from 'domain/user/useCase/userUseCase';
import { userPoolUseCase } from 'domain/userPool/useCase/userPoolUseCase';
import { returnPostError } from 'service/returnStatus';
import type { AmzTargets } from '../common/types/auth';
Expand All @@ -28,6 +29,7 @@ const useCases: {
'AWSCognitoIdentityProviderService.ListUserPoolClients': userPoolUseCase.listUserPoolClients,
'AWSCognitoIdentityProviderService.CreateUserPool': userPoolUseCase.createUserPool,
'AWSCognitoIdentityProviderService.CreateUserPoolClient': userPoolUseCase.createUserPoolClient,
'AWSCognitoIdentityProviderService.DeleteUser': userUseCase.deleteUser,
'AWSCognitoIdentityProviderService.DeleteUserPool': userPoolUseCase.deleteUserPool,
'AWSCognitoIdentityProviderService.DeleteUserPoolClient': userPoolUseCase.deleteUserPoolClient,
'AWSCognitoIdentityProviderService.ListUsers': authUseCase.listUsers,
Expand Down
5 changes: 5 additions & 0 deletions server/common/types/auth.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
/* eslint-disable max-lines */
import type {
AdminCreateUserRequest,
AdminCreateUserResponse,
Expand All @@ -23,6 +24,7 @@ import type {
DeleteUserAttributesResponse,
DeleteUserPoolClientRequest,
DeleteUserPoolRequest,
DeleteUserRequest,
GetUserResponse,
ListUserPoolClientsRequest,
ListUserPoolClientsResponse,
Expand Down Expand Up @@ -81,6 +83,8 @@ export type CreateUserPoolClientTarget = TargetBody<
CreateUserPoolClientResponse
>;

export type DeleteUserTarget = TargetBody<DeleteUserRequest, Record<string, never>>;

export type DeleteUserPoolTarget = TargetBody<DeleteUserPoolRequest, Record<string, never>>;

export type DeleteUserPoolClientTarget = TargetBody<
Expand Down Expand Up @@ -177,6 +181,7 @@ export type AmzTargets = {
'AWSCognitoIdentityProviderService.ListUserPoolClients': ListUserPoolClientsTarget;
'AWSCognitoIdentityProviderService.CreateUserPool': CreateUserPoolTarget;
'AWSCognitoIdentityProviderService.CreateUserPoolClient': CreateUserPoolClientTarget;
'AWSCognitoIdentityProviderService.DeleteUser': DeleteUserTarget;
'AWSCognitoIdentityProviderService.DeleteUserPool': DeleteUserPoolTarget;
'AWSCognitoIdentityProviderService.DeleteUserPoolClient': DeleteUserPoolClientTarget;
'AWSCognitoIdentityProviderService.AdminGetUser': AdminGetUserTarget;
Expand Down
9 changes: 9 additions & 0 deletions server/domain/user/model/userMethod.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
import type { EntityId } from 'common/types/brandedId';
import type { UserEntity } from 'common/types/user';
import { brandedId } from 'service/brandedId';

export const userMethod = {
delete: (user: UserEntity): EntityId['deletableUser'] => {
return brandedId.deletableUser.entity.parse(user.id);
},
};
5 changes: 2 additions & 3 deletions server/domain/user/repository/userCommand.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import type { Prisma } from '@prisma/client';
import type { EntityId } from 'common/types/brandedId';
import type { UserAttributeEntity, UserEntity } from 'common/types/user';
import type { UserEntity } from 'common/types/user';

export const userCommand = {
// eslint-disable-next-line complexity
Expand Down Expand Up @@ -61,9 +61,8 @@ export const userCommand = {
delete: async (
tx: Prisma.TransactionClient,
deletableUserId: EntityId['deletableUser'],
attributes: UserAttributeEntity[],
): Promise<void> => {
await tx.userAttribute.deleteMany({ where: { id: { in: attributes.map((attr) => attr.id) } } });
await tx.userAttribute.deleteMany({ where: { userId: deletableUserId } });
await tx.user.delete({ where: { id: deletableUserId } });
},
};
2 changes: 1 addition & 1 deletion server/domain/user/useCase/adminUseCase.ts
Original file line number Diff line number Diff line change
Expand Up @@ -73,7 +73,7 @@ export const adminUseCase = {
const user = await userQuery.findByName(tx, req.Username);
const deletableId = adminMethod.deleteUser(user, req.UserPoolId);

await userCommand.delete(tx, deletableId, user.attributes);
await userCommand.delete(tx, deletableId);

return {};
}),
Expand Down
26 changes: 26 additions & 0 deletions server/domain/user/useCase/userUseCase.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
import assert from 'assert';
import type { DeleteUserTarget } from 'common/types/auth';
import { jwtDecode } from 'jwt-decode';
import { transaction } from 'service/prismaClient';
import type { AccessTokenJwt } from 'service/types';
import { userMethod } from '../model/userMethod';
import { userCommand } from '../repository/userCommand';
import { userQuery } from '../repository/userQuery';

export const userUseCase = {
deleteUser: (req: DeleteUserTarget['reqBody']): Promise<DeleteUserTarget['resBody']> =>
transaction(async (tx) => {
assert(req.AccessToken);

const decoded = jwtDecode<AccessTokenJwt>(req.AccessToken);
const user = await userQuery.findById(tx, decoded.sub);

assert(user.kind === 'cognito');

const deletableId = userMethod.delete(user);

await userCommand.delete(tx, deletableId);

return {};
}),
};
9 changes: 4 additions & 5 deletions server/tests/sdk/pool.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,6 @@ test(CreateUserPoolCommand.name, async () => {
expect(client.UserPoolClient?.ClientName === 'testClient').toBeTruthy();
});

// eslint-disable-next-line complexity
test(DeleteUserPoolCommand.name, async () => {
const pool = await cognitoClient.send(new CreateUserPoolCommand({ PoolName: 'testPool' }));

Expand All @@ -41,8 +40,8 @@ test(DeleteUserPoolCommand.name, async () => {

const res2 = await cognitoClient.send(new ListUserPoolsCommand({ MaxResults: 100 }));

expect(res1.UserPools?.length).toBe(2);
expect(res2.UserPools?.length).toBe(1);
expect(res1.UserPools).toHaveLength(2);
expect(res2.UserPools).toHaveLength(1);
});

// eslint-disable-next-line complexity
Expand All @@ -67,6 +66,6 @@ test(DeleteUserPoolClientCommand.name, async () => {
new ListUserPoolClientsCommand({ UserPoolId: pool.UserPool?.Id, MaxResults: 100 }),
);

expect(res1.UserPoolClients?.length).toBe(1);
expect(res2.UserPoolClients?.length).toBe(0);
expect(res1.UserPoolClients).toHaveLength(1);
expect(res2.UserPoolClients).toHaveLength(0);
});
16 changes: 16 additions & 0 deletions server/tests/sdk/user.test.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,13 @@
import {
DeleteUserAttributesCommand,
DeleteUserCommand,
GetUserCommand,
ListUsersCommand,
UpdateUserAttributesCommand,
VerifyUserAttributeCommand,
} from '@aws-sdk/client-cognito-identity-provider';
import { cognitoClient } from 'service/cognito';
import { DEFAULT_USER_POOL_ID } from 'service/envValues';
import {
createCognitoUserAndToken,
createSocialUserAndToken,
Expand Down Expand Up @@ -106,6 +109,19 @@ test(VerifyUserAttributeCommand.name, async () => {
expect(emailAttr?.Value).toBe(newEmail);
});

test(DeleteUserCommand.name, async () => {
const token = await createCognitoUserAndToken();

const res1 = await cognitoClient.send(new ListUsersCommand({ UserPoolId: DEFAULT_USER_POOL_ID }));

await cognitoClient.send(new DeleteUserCommand(token));

const res2 = await cognitoClient.send(new ListUsersCommand({ UserPoolId: DEFAULT_USER_POOL_ID }));

expect(res1.Users).toHaveLength(1);
expect(res2.Users).toHaveLength(0);
});

test(DeleteUserAttributesCommand.name, async () => {
const token = await createCognitoUserAndToken();
const attrName1 = 'custom:test1';
Expand Down

0 comments on commit ad1accd

Please sign in to comment.