Skip to content

Commit

Permalink
feat(api): support for DeleteUserPoolClient
Browse files Browse the repository at this point in the history
  • Loading branch information
jagregory committed Feb 16, 2022
1 parent 6e546ce commit f5bca87
Show file tree
Hide file tree
Showing 8 changed files with 183 additions and 1 deletion.
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -74,7 +74,7 @@ A _Good Enough_ offline emulator for [Amazon Cognito](https://aws.amazon.com/cog
| DeleteUser | ✅² |
| DeleteUserAttributes ||
| DeleteUserPool ||
| DeleteUserPoolClient | |
| DeleteUserPoolClient | ✅² |
| DeleteUserPoolDomain ||
| DescribeIdentityProvider ||
| DescribeResourceServer ||
Expand Down
45 changes: 45 additions & 0 deletions integration-tests/aws-sdk/deleteUserPoolClient.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
import { withCognitoSdk } from "./setup";

describe(
"CognitoIdentityServiceProvider.deleteUserPoolClient",
withCognitoSdk((Cognito) => {
it("deletes a group", async () => {
const client = Cognito();

// create the user pool client
const upc = await client
.createUserPoolClient({
UserPoolId: "test",
ClientName: "test",
})
.promise();

const describeResponse = await client
.describeUserPoolClient({
ClientId: upc.UserPoolClient?.ClientId!,
UserPoolId: "test",
})
.promise();

expect(describeResponse.UserPoolClient).toBeDefined();

await client
.deleteUserPoolClient({
ClientId: upc.UserPoolClient?.ClientId!,
UserPoolId: "test",
})
.promise();

await expect(
client
.describeUserPoolClient({
ClientId: upc.UserPoolClient?.ClientId!,
UserPoolId: "test",
})
.promise()
).rejects.toMatchObject({
code: "ResourceNotFoundException",
});
});
})
);
1 change: 1 addition & 0 deletions src/__tests__/mockUserPoolService.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ export const newMockUserPoolService = (
}
): jest.Mocked<UserPoolService> => ({
config,
deleteAppClient: jest.fn(),
deleteGroup: jest.fn(),
deleteUser: jest.fn(),
getGroupByGroupName: jest.fn(),
Expand Down
25 changes: 25 additions & 0 deletions src/services/userPoolService.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -146,6 +146,31 @@ describe("User Pool Service", () => {
});
});

describe("deleteAppClient", () => {
const appClient = TDB.appClient();

it("deletes the user pool client", async () => {
const clientsDs = newMockDataStore();

const userPool = new UserPoolServiceImpl(
clientsDs,
clock,
newMockDataStore(),
{
Id: "local",
UsernameAttributes: [],
}
);

await userPool.deleteAppClient(TestContext, appClient);

expect(clientsDs.delete).toHaveBeenCalledWith(TestContext, [
"Clients",
appClient.ClientId,
]);
});
});

describe("deleteGroup", () => {
const group = TDB.group();

Expand Down
12 changes: 12 additions & 0 deletions src/services/userPoolService.ts
Original file line number Diff line number Diff line change
Expand Up @@ -139,6 +139,7 @@ export interface UserPoolService {
readonly config: UserPool;

saveAppClient(ctx: Context, appClient: AppClient): Promise<void>;
deleteAppClient(ctx: Context, appClient: AppClient): Promise<void>;
deleteGroup(ctx: Context, group: Group): Promise<void>;
deleteUser(ctx: Context, user: User): Promise<void>;
getGroupByGroupName(ctx: Context, groupName: string): Promise<Group | null>;
Expand Down Expand Up @@ -198,6 +199,17 @@ export class UserPoolServiceImpl implements UserPoolService {
);
}

public async deleteAppClient(
ctx: Context,
appClient: AppClient
): Promise<void> {
ctx.logger.debug(
{ clientId: appClient.ClientId },
"UserPoolServiceImpl.deleteAppClient"
);
await this.clientsDataStore.delete(ctx, ["Clients", appClient.ClientId]);
}

public async deleteGroup(ctx: Context, group: Group): Promise<void> {
ctx.logger.debug(
{ groupName: group.GroupName },
Expand Down
70 changes: 70 additions & 0 deletions src/targets/deleteUserPoolClient.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
import { newMockCognitoService } from "../__tests__/mockCognitoService";
import { newMockUserPoolService } from "../__tests__/mockUserPoolService";
import { TestContext } from "../__tests__/testContext";
import * as TDB from "../__tests__/testDataBuilder";
import { ResourceNotFoundError } from "../errors";
import { CognitoService, UserPoolService } from "../services";
import {
DeleteUserPoolClient,
DeleteUserPoolClientTarget,
} from "./deleteUserPoolClient";

describe("DeleteUserPoolClient target", () => {
let deleteUserPoolClient: DeleteUserPoolClientTarget;
let mockUserPoolService: jest.Mocked<UserPoolService>;
let mockCognitoService: jest.Mocked<CognitoService>;

beforeEach(() => {
mockUserPoolService = newMockUserPoolService();
mockCognitoService = newMockCognitoService(mockUserPoolService);

deleteUserPoolClient = DeleteUserPoolClient({
cognito: mockCognitoService,
});
});

it("deletes a user pool client", async () => {
const existingAppClient = TDB.appClient({
UserPoolId: "test",
});

mockCognitoService.getAppClient.mockResolvedValue(existingAppClient);

await deleteUserPoolClient(TestContext, {
ClientId: existingAppClient.ClientId,
UserPoolId: "test",
});

expect(mockUserPoolService.deleteAppClient).toHaveBeenCalledWith(
TestContext,
existingAppClient
);
});

it("throws if the user pool client doesn't exist", async () => {
mockCognitoService.getAppClient.mockResolvedValue(null);

await expect(
deleteUserPoolClient(TestContext, {
ClientId: "clientId",
UserPoolId: "test",
})
).rejects.toEqual(new ResourceNotFoundError());
});

it("throws if the user pool client UserPoolId doesn't match the request", async () => {
const existingAppClient = TDB.appClient({
ClientId: "clientId",
UserPoolId: "pool-one",
});

mockCognitoService.getAppClient.mockResolvedValue(existingAppClient);

await expect(
deleteUserPoolClient(TestContext, {
ClientId: "clientId",
UserPoolId: "pool-two",
})
).rejects.toEqual(new ResourceNotFoundError());
});
});
27 changes: 27 additions & 0 deletions src/targets/deleteUserPoolClient.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
import { DeleteUserPoolClientRequest } from "aws-sdk/clients/cognitoidentityserviceprovider";
import { ResourceNotFoundError } from "../errors";
import { Services } from "../services";
import { Target } from "./Target";

export type DeleteUserPoolClientTarget = Target<
DeleteUserPoolClientRequest,
{}
>;

type DeleteUserPoolClientServices = Pick<Services, "cognito">;

export const DeleteUserPoolClient =
({ cognito }: DeleteUserPoolClientServices): DeleteUserPoolClientTarget =>
async (ctx, req) => {
// TODO: from the docs "Calling this action requires developer credentials.", can we enforce this?

const userPool = await cognito.getUserPool(ctx, req.UserPoolId);
const appClient = await cognito.getAppClient(ctx, req.ClientId);
if (!appClient || appClient.UserPoolId !== req.UserPoolId) {
throw new ResourceNotFoundError();
}

await userPool.deleteAppClient(ctx, appClient);

return {};
};
2 changes: 2 additions & 0 deletions src/targets/targets.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ import { CreateUserPoolClient } from "./createUserPoolClient";
import { DeleteGroup } from "./deleteGroup";
import { DeleteUser } from "./deleteUser";
import { DeleteUserAttributes } from "./deleteUserAttributes";
import { DeleteUserPoolClient } from "./deleteUserPoolClient";
import { DescribeUserPoolClient } from "./describeUserPoolClient";
import { ForgotPassword } from "./forgotPassword";
import { GetGroup } from "./getGroup";
Expand Down Expand Up @@ -58,6 +59,7 @@ export const Targets = {
DeleteGroup,
DeleteUser,
DeleteUserAttributes,
DeleteUserPoolClient,
DescribeUserPoolClient,
ForgotPassword,
GetGroup,
Expand Down

0 comments on commit f5bca87

Please sign in to comment.