Skip to content

Commit

Permalink
refactor: user groups into user pool service
Browse files Browse the repository at this point in the history
  • Loading branch information
jagregory committed May 30, 2022
1 parent fe0e149 commit 4863d54
Show file tree
Hide file tree
Showing 5 changed files with 113 additions and 53 deletions.
3 changes: 2 additions & 1 deletion src/__tests__/mockUserPoolService.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ export const newMockUserPoolService = (
Id: "test",
}
): jest.Mocked<UserPoolService> => ({
options: config,
addUserToGroup: jest.fn(),
deleteAppClient: jest.fn(),
deleteGroup: jest.fn(),
deleteUser: jest.fn(),
Expand All @@ -15,6 +15,7 @@ export const newMockUserPoolService = (
getUserByUsername: jest.fn(),
listGroups: jest.fn(),
listUsers: jest.fn(),
options: config,
removeUserFromGroup: jest.fn(),
saveAppClient: jest.fn(),
saveGroup: jest.fn(),
Expand Down
81 changes: 81 additions & 0 deletions src/services/userPoolService.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -693,4 +693,85 @@ describe("User Pool Service", () => {
expect(userPool.options.MfaConfiguration).toEqual("ON");
});
});

describe("addUserToGroup", () => {
it("updates the group's members", async () => {
const ds = newMockDataStore();
const userPool = new UserPoolServiceImpl(
mockClientsDataStore,
clock,
ds,
{
Id: "test",
}
);

const user = TDB.user();
const group = TDB.group();

await userPool.addUserToGroup(TestContext, group, user);

expect(ds.set).toHaveBeenCalledWith(
TestContext,
["Groups", group.GroupName],
{
...group,
LastModifiedDate: clock.get(),
members: [user.Username],
}
);
});

it("only adds the user once", async () => {
const ds = newMockDataStore();
const userPool = new UserPoolServiceImpl(
mockClientsDataStore,
clock,
ds,
{
Id: "test",
}
);

const user = TDB.user();
const group = TDB.group({
members: [user.Username],
});

await userPool.addUserToGroup(TestContext, group, user);

expect(ds.set).not.toHaveBeenCalled();
});
});

describe("removeUserFromGroup", () => {
it("updates the group's members", async () => {
const ds = newMockDataStore();
const userPool = new UserPoolServiceImpl(
mockClientsDataStore,
clock,
ds,
{
Id: "test",
}
);

const user = TDB.user();
const group = TDB.group({
members: [user.Username],
});

await userPool.removeUserFromGroup(TestContext, group, user);

expect(ds.set).toHaveBeenCalledWith(
TestContext,
["Groups", group.GroupName],
{
...group,
LastModifiedDate: clock.get(),
members: [],
}
);
});
});
});
22 changes: 22 additions & 0 deletions src/services/userPoolService.ts
Original file line number Diff line number Diff line change
Expand Up @@ -138,6 +138,7 @@ export type UserPool = UserPoolType & {
export interface UserPoolService {
readonly options: UserPool;

addUserToGroup(ctx: Context, group: Group, user: User): Promise<void>;
saveAppClient(ctx: Context, appClient: AppClient): Promise<void>;
deleteAppClient(ctx: Context, appClient: AppClient): Promise<void>;
deleteGroup(ctx: Context, group: Group): Promise<void>;
Expand Down Expand Up @@ -346,6 +347,27 @@ export class UserPoolServiceImpl implements UserPoolService {
return Object.values(groups);
}

public async addUserToGroup(
ctx: Context,
group: Group,
user: User
): Promise<void> {
ctx.logger.debug(
{ username: user.Username, groupName: group.GroupName },
"UserPoolServiceImpl.addUserToFromGroup"
);

const groupMembers = new Set(group.members ?? []);
if (!groupMembers.has(user.Username)) {
groupMembers.add(user.Username);
await this.saveGroup(ctx, {
...group,
LastModifiedDate: this.clock.get(),
members: Array.from(groupMembers),
});
}
}

public async removeUserFromGroup(
ctx: Context,
group: Group,
Expand Down
41 changes: 5 additions & 36 deletions src/targets/adminAddUserToGroup.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,6 @@ describe("AdminAddUserToGroup target", () => {
clock = new ClockFake(originalDate);

adminAddUserToGroup = AdminAddUserToGroup({
clock,
cognito: newMockCognitoService(mockUserPoolService),
});
});
Expand All @@ -43,41 +42,11 @@ describe("AdminAddUserToGroup target", () => {
UserPoolId: "test",
});

expect(mockUserPoolService.saveGroup).toHaveBeenCalledWith(TestContext, {
...existingGroup,
LastModifiedDate: newDate,
members: [existingUser.Username],
});
});

it("adds the user to a group only once", async () => {
const existingGroup = TDB.group();
const existingUser = TDB.user();

mockUserPoolService.getGroupByGroupName.mockResolvedValue(existingGroup);
mockUserPoolService.getUserByUsername.mockResolvedValue(existingUser);

const newDate = new Date();
clock.advanceTo(newDate);

await adminAddUserToGroup(TestContext, {
GroupName: existingGroup.GroupName,
Username: existingUser.Username,
UserPoolId: "test",
});

// try add a second time
await adminAddUserToGroup(TestContext, {
GroupName: existingGroup.GroupName,
Username: existingUser.Username,
UserPoolId: "test",
});

expect(mockUserPoolService.saveGroup).toHaveBeenCalledWith(TestContext, {
...existingGroup,
LastModifiedDate: newDate,
members: [existingUser.Username],
});
expect(mockUserPoolService.addUserToGroup).toHaveBeenCalledWith(
TestContext,
existingGroup,
existingUser
);
});

it("throws if the group doesn't exist", async () => {
Expand Down
19 changes: 3 additions & 16 deletions src/targets/adminAddUserToGroup.ts
Original file line number Diff line number Diff line change
@@ -1,18 +1,14 @@
import { AdminAddUserToGroupRequest } from "aws-sdk/clients/cognitoidentityserviceprovider";
import { GroupNotFoundError, UserNotFoundError } from "../errors";
import { Services } from "../services";
import { Group } from "../services/userPoolService";
import { Target } from "./Target";

export type AdminAddUserToGroupTarget = Target<AdminAddUserToGroupRequest, {}>;

type AdminAddUserToGroupServices = Pick<Services, "clock" | "cognito">;
type AdminAddUserToGroupServices = Pick<Services, "cognito">;

export const AdminAddUserToGroup =
({
clock,
cognito,
}: AdminAddUserToGroupServices): AdminAddUserToGroupTarget =>
({ cognito }: AdminAddUserToGroupServices): AdminAddUserToGroupTarget =>
async (ctx, req) => {
const userPool = await cognito.getUserPool(ctx, req.UserPoolId);

Expand All @@ -26,16 +22,7 @@ export const AdminAddUserToGroup =
throw new UserNotFoundError();
}

const groupUsers = new Set(group.members ?? []);
groupUsers.add(user.Username);

const updatedGroup: Group = {
...group,
LastModifiedDate: clock.get(),
members: Array.from(groupUsers.values()),
};

await userPool.saveGroup(ctx, updatedGroup);
await userPool.addUserToGroup(ctx, group, user);

return {};
};

0 comments on commit 4863d54

Please sign in to comment.