diff --git a/README.md b/README.md index a7e5a8dc..5dc851ba 100644 --- a/README.md +++ b/README.md @@ -24,6 +24,7 @@ A _Good Enough_ offline emulator for [Amazon Cognito](https://aws.amazon.com/cog - [ForgotPassword](https://docs.aws.amazon.com/cognito-user-identity-pools/latest/APIReference/API_ForgotPassword.html) - [GetUser](https://docs.aws.amazon.com/cognito-user-identity-pools/latest/APIReference/API_GetUser.html) - [InitiateAuth](https://docs.aws.amazon.com/cognito-user-identity-pools/latest/APIReference/API_InitiateAuth.html) +- [ListGroups](https://docs.aws.amazon.com/cognito-user-identity-pools/latest/APIReference/API_ListGroups.html) - [ListUsers](https://docs.aws.amazon.com/cognito-user-identity-pools/latest/APIReference/API_ListUsers.html) - [RespondToAuthChallenge](https://docs.aws.amazon.com/cognito-user-identity-pools/latest/APIReference/API_RespondToAuthChallenge.html) - [SignUp](https://docs.aws.amazon.com/cognito-user-identity-pools/latest/APIReference/API_SignUp.html) diff --git a/integration-tests/aws-sdk/listGroups.test.ts b/integration-tests/aws-sdk/listGroups.test.ts new file mode 100644 index 00000000..dfa37116 --- /dev/null +++ b/integration-tests/aws-sdk/listGroups.test.ts @@ -0,0 +1,98 @@ +import { ClockFake } from "../../src/__tests__/clockFake"; +import { withCognitoSdk } from "./setup"; + +const originalDate = new Date(); +const roundedDate = new Date(originalDate.getTime()); +roundedDate.setMilliseconds(0); + +const clock = new ClockFake(originalDate); + +describe( + "CognitoIdentityServiceProvider.listGroups", + withCognitoSdk( + (Cognito) => { + it("lists groups", async () => { + const client = Cognito(); + + await client + .createGroup({ + GroupName: "abc", + UserPoolId: "test1", + }) + .promise(); + await client + .createGroup({ + GroupName: "def", + UserPoolId: "test1", + }) + .promise(); + await client + .createGroup({ + GroupName: "ghi", + UserPoolId: "test2", + }) + .promise(); + + const result1 = await client + .listGroups({ + UserPoolId: "test1", + }) + .promise(); + + expect(result1).toEqual({ + Groups: [ + { + CreationDate: roundedDate, + GroupName: "abc", + LastModifiedDate: roundedDate, + UserPoolId: "test1", + }, + { + CreationDate: roundedDate, + GroupName: "def", + LastModifiedDate: roundedDate, + UserPoolId: "test1", + }, + ], + }); + + const result2 = await client + .listGroups({ + UserPoolId: "test2", + }) + .promise(); + + expect(result2).toEqual({ + Groups: [ + { + CreationDate: roundedDate, + GroupName: "ghi", + LastModifiedDate: roundedDate, + UserPoolId: "test2", + }, + ], + }); + }); + + it("returns an empty collection when there are no groups", async () => { + const client = Cognito(); + + const result = await client + .listGroups({ + UserPoolId: "test1", + }) + .promise(); + + expect(result).toEqual({ + Groups: [], + }); + }); + + // TODO: getUserPool lazily creates a pool right now, so we can't handle invalid user pools + it.todo("handles invalid user pool"); + }, + { + clock, + } + ) +); diff --git a/src/targets/listGroups.test.ts b/src/targets/listGroups.test.ts new file mode 100644 index 00000000..daba1f6c --- /dev/null +++ b/src/targets/listGroups.test.ts @@ -0,0 +1,67 @@ +import { MockUserPoolClient } from "../__tests__/mockUserPoolClient"; +import { CognitoClient } from "../services"; +import { ListGroups, ListGroupsTarget } from "./listGroups"; + +describe("ListGroups target", () => { + let listGroups: ListGroupsTarget; + let mockCognitoClient: jest.Mocked; + let now: Date; + + beforeEach(() => { + now = new Date(2020, 1, 2, 3, 4, 5); + + mockCognitoClient = { + getAppClient: jest.fn(), + getUserPool: jest.fn().mockResolvedValue(MockUserPoolClient), + getUserPoolForClientId: jest.fn().mockResolvedValue(MockUserPoolClient), + }; + + listGroups = ListGroups({ + cognitoClient: mockCognitoClient, + }); + }); + + it("lists groups", async () => { + MockUserPoolClient.listGroups.mockResolvedValue([ + { + CreationDate: now.getTime(), + Description: "Description", + GroupName: "abc", + LastModifiedDate: now.getTime(), + Precedence: 1, + RoleArn: "ARN", + }, + { + CreationDate: now.getTime(), + GroupName: "def", + LastModifiedDate: now.getTime(), + }, + ]); + + const output = await listGroups({ + UserPoolId: "userPoolId", + }); + + expect(output).toBeDefined(); + expect(output.Groups).toEqual([ + { + CreationDate: now, + Description: "Description", + GroupName: "abc", + LastModifiedDate: now, + Precedence: 1, + RoleArn: "ARN", + UserPoolId: "userPoolId", + }, + { + CreationDate: now, + GroupName: "def", + LastModifiedDate: now, + UserPoolId: "userPoolId", + }, + ]); + }); + + it.todo("supports Limit to specify the number of groups to return"); + it.todo("supports PaginationToken to paginate results"); +}); diff --git a/src/targets/listGroups.ts b/src/targets/listGroups.ts new file mode 100644 index 00000000..4dbfbf2d --- /dev/null +++ b/src/targets/listGroups.ts @@ -0,0 +1,33 @@ +import { + ListGroupsRequest, + ListGroupsResponse, +} from "aws-sdk/clients/cognitoidentityserviceprovider"; +import { Services } from "../services"; + +export type ListGroupsTarget = ( + req: ListGroupsRequest +) => Promise; + +type ListGroupServices = Pick; + +export const ListGroups = ({ + cognitoClient, +}: ListGroupServices): ListGroupsTarget => async (req) => { + // TODO: Limit support + // TODO: PaginationToken support + + const userPool = await cognitoClient.getUserPool(req.UserPoolId); + const groups = await userPool.listGroups(); + + return { + Groups: groups.map((group) => ({ + CreationDate: new Date(group.CreationDate), + Description: group.Description, + GroupName: group.GroupName, + LastModifiedDate: new Date(group.LastModifiedDate), + Precedence: group.Precedence, + RoleArn: group.RoleArn, + UserPoolId: req.UserPoolId, + })), + }; +}; diff --git a/src/targets/router.ts b/src/targets/router.ts index ed25520f..7ba326f5 100644 --- a/src/targets/router.ts +++ b/src/targets/router.ts @@ -9,6 +9,7 @@ import { DescribeUserPoolClient } from "./describeUserPoolClient"; import { ForgotPassword } from "./forgotPassword"; import { ChangePassword } from "./changePassword"; import { InitiateAuth } from "./initiateAuth"; +import { ListGroups } from "./listGroups"; import { ListUsers } from "./listUsers"; import { RespondToAuthChallenge } from "./respondToAuthChallenge"; import { SignUp } from "./signUp"; @@ -34,6 +35,7 @@ export const Targets = { ForgotPassword, GetUser, InitiateAuth, + ListGroups, ListUsers, RespondToAuthChallenge, SignUp,