Skip to content

Commit

Permalink
Merge pull request #643 from bandada-infra/feat/add-member-to-groups
Browse files Browse the repository at this point in the history
feat: add getGroupsByGroupIds and addMemberToGroups
  • Loading branch information
vplasencia authored Feb 27, 2025
2 parents 6eaa7d0 + 6a36eaf commit 6562303
Show file tree
Hide file tree
Showing 9 changed files with 688 additions and 72 deletions.
9 changes: 9 additions & 0 deletions apps/api/src/app/groups/dto/add-member-to-groups.dto.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
import { ApiProperty } from "@nestjs/swagger"
import { ArrayNotEmpty, IsArray } from "class-validator"

export class AddMemberToGroupsDto {
@IsArray()
@ArrayNotEmpty()
@ApiProperty()
readonly groupIds: string[]
}
52 changes: 48 additions & 4 deletions apps/api/src/app/groups/groups.controller.ts
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ import { UpdateGroupsDto } from "./dto/update-groups.dto"
import { GroupsService } from "./groups.service"
import { mapGroupToResponseDTO } from "./groups.utils"
import { RemoveGroupsDto } from "./dto/remove-groups.dto"
import { AddMemberToGroupsDto } from "./dto/add-member-to-groups.dto"

@ApiTags("groups")
@Controller("groups")
Expand All @@ -40,15 +41,26 @@ export class GroupsController {
@Get()
@ApiQuery({ name: "adminId", required: false, type: String })
@ApiQuery({ name: "memberId", required: false, type: String })
@ApiQuery({
name: "groupIds",
required: false,
type: String,
isArray: true
})
@ApiOperation({ description: "Returns the list of groups." })
@ApiCreatedResponse({ type: Group, isArray: true })
async getGroups(
@Query("adminId") adminId: string,
@Query("memberId") memberId: string
@Query("memberId") memberId: string,
@Query("groupIds") groupIds: string[]
) {
const groups = await this.groupsService.getGroups({ adminId, memberId })
const groupIds = groups.map((group) => group.id)
const fingerprints = await this.groupsService.getFingerprints(groupIds)
const groups = await this.groupsService.getGroups({
adminId,
memberId,
groupIds
})
const groupsIds = groups.map((group) => group.id)
const fingerprints = await this.groupsService.getFingerprints(groupsIds)

return groups.map((group, index) =>
mapGroupToResponseDTO(group, fingerprints[index])
Expand Down Expand Up @@ -365,6 +377,38 @@ export class GroupsController {
throw new NotImplementedException()
}

@Post("/members/:member")
@ApiBody({ type: AddMemberToGroupsDto })
@ApiHeader({ name: "x-api-key", required: true })
@ApiOperation({
description:
"Adds a member to multiple groups. Requires an API Key in the headers or a valid session."
})
async addMemberToGroups(
@Param("member") memberId: string,
@Body() dto: AddMemberToGroupsDto,
@Headers() headers: Headers,
@Req() req: Request
): Promise<void | any> {
const apiKey = headers["x-api-key"] as string

if (apiKey) {
await this.groupsService.addMemberToGroupsWithAPIKey(
dto.groupIds,
memberId,
apiKey
)
} else if (req.session.adminId) {
await this.groupsService.addMemberToGroupsManually(
dto.groupIds,
memberId,
req.session.adminId
)
} else {
throw new NotImplementedException()
}
}

@Delete(":group/members/:member")
@ApiHeader({ name: "x-api-key", required: true })
@ApiOperation({
Expand Down
234 changes: 234 additions & 0 deletions apps/api/src/app/groups/groups.service.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -266,6 +266,34 @@ describe("GroupsService", () => {

expect(result).toHaveLength(1)
})

it("Should return a list of groups by group ids", async () => {
const group1 = await groupsService.createGroup(
{
name: "Group Id 1",
description: "This is a description",
treeDepth: 16,
fingerprintDuration: 3600
},
"admin"
)

const group2 = await groupsService.createGroup(
{
name: "Group Id 2",
description: "This is a description",
treeDepth: 16,
fingerprintDuration: 3600
},
"admin"
)

const result = await groupsService.getGroups({
groupIds: [group1.id, group2.id]
})

expect(result).toHaveLength(2)
})
})

describe("# getGroup", () => {
Expand Down Expand Up @@ -1487,6 +1515,212 @@ describe("GroupsService", () => {
})
})

describe("# addMemberToGroupsManually", () => {
let groups: Array<Group>
let admin: Admin

beforeAll(async () => {
admin = await adminsService.create({
id: "admin",
address: "0x"
})

groups = await groupsService.createGroupsManually(
[
{
name: "Multiple Group 1",
description: "This is a new group",
treeDepth: 16,
fingerprintDuration: 3600
},
{
name: "Multiple Group 2",
description: "This is a new group",
treeDepth: 16,
fingerprintDuration: 3600
}
],
admin.id
)
})

it("Should add a member to multiple groups manually", async () => {
const multipleGroups =
await groupsService.addMemberToGroupsManually(
groups.map((g) => g.id),
"123123",
admin.id
)

expect(multipleGroups).toHaveLength(2)
})

it("Should not add a member to the groups if the group is a credential group", async () => {
const _group = await groupsService.createGroup(
{
name: "Multiple Group Credential Group",
description: "This is a new group",
treeDepth: 16,
fingerprintDuration: 3600,
credentials: {
id: "GITHUB_FOLLOWERS",
criteria: {
minFollowers: 5
}
}
},
admin.id
)

const fun = groupsService.addMemberToGroupsManually(
[groups[0].id, _group.id],
"456456",
admin.id
)

await expect(fun).rejects.toThrow(
`The group '${_group.name}' is a credential group. You cannot manually add members to a credential group.`
)
})

it("Should not add a member to multiple groups if the member already exists", async () => {
const fun = groupsService.addMemberToGroupsManually(
groups.map((g) => g.id),
"123123",
admin.id
)

await expect(fun).rejects.toThrow(
`Member '123123' already exists in the group '${groups[0].id}'`
)
})

it("Should not add a member to multiple groups if the admin is the wrong admin", async () => {
const fun = groupsService.addMemberToGroupsManually(
groups.map((g) => g.id),
"123123",
"wrong-admin"
)

await expect(fun).rejects.toThrow(
`You are not the admin of the group '${groups[0].id}'`
)
})
})

describe("# addMembersToGroupsWithAPIKey", () => {
let groups: Array<Group>
let admin: Admin
let apiKey: string

beforeAll(async () => {
admin = await adminsService.create({
id: "admin",
address: "0x"
})

apiKey = await adminsService.updateApiKey(
admin.id,
ApiKeyActions.Generate
)

groups = await groupsService.createGroupsManually(
[
{
name: "Multiple Group API 1",
description: "This is a new group",
treeDepth: 16,
fingerprintDuration: 3600
},
{
name: "Multiple Group API 2",
description: "This is a new group",
treeDepth: 16,
fingerprintDuration: 3600
}
],
admin.id
)
})

it("Should add a member to multiple groups via API", async () => {
const multipleGroups =
await groupsService.addMemberToGroupsWithAPIKey(
groups.map((g) => g.id),
"123123",
apiKey
)

expect(multipleGroups).toHaveLength(2)
})

it("Should not add a member to the groups if the group is a credential group", async () => {
const _group = await groupsService.createGroup(
{
name: "Multiple Group Credential Group API",
description: "This is a new group",
treeDepth: 16,
fingerprintDuration: 3600,
credentials: {
id: "GITHUB_FOLLOWERS",
criteria: {
minFollowers: 5
}
}
},
admin.id
)

const fun = groupsService.addMemberToGroupsWithAPIKey(
[groups[0].id, _group.id],
"456456",
apiKey
)

await expect(fun).rejects.toThrow(
`The group '${_group.name}' is a credential group. You cannot add members to a credential group using an API Key.`
)
})

it("Should not add a member to multiple groups if the member already exists", async () => {
const fun = groupsService.addMemberToGroupsWithAPIKey(
groups.map((g) => g.id),
"123123",
apiKey
)

await expect(fun).rejects.toThrow(
`Member '123123' already exists in the group '${groups[0].id}'`
)
})

it("Should not add a member to multiple groups if the API key is invalid", async () => {
const fun = groupsService.addMemberToGroupsWithAPIKey(
groups.map((g) => g.id),
"123123",
"invalid-apikey"
)

await expect(fun).rejects.toThrow(
`Invalid API key or API access not enabled for admin '${admin.id}'`
)
})

it("Should not add a member to multiple groups if the API key is disabled", async () => {
await adminsService.updateApiKey(admin.id, ApiKeyActions.Disable)

const fun = groupsService.addMemberToGroupsWithAPIKey(
groups.map((g) => g.id),
"123123",
apiKey
)

await expect(fun).rejects.toThrow(
`Invalid API key or API access not enabled for admin '${admin.id}'`
)
})
})

describe("# createGroupManually", () => {
const groupDto: CreateGroupDto = {
id: "1",
Expand Down
Loading

0 comments on commit 6562303

Please sign in to comment.