Skip to content

Commit

Permalink
Merge pull request #332 from privacy-scaling-explorations/feat/many-t…
Browse files Browse the repository at this point in the history
…o-many-group-member

Add many to many relationship between group and member
  • Loading branch information
vplasencia authored Nov 11, 2023
2 parents f5889ce + befa1cc commit e2e1697
Show file tree
Hide file tree
Showing 4 changed files with 196 additions and 71 deletions.
100 changes: 85 additions & 15 deletions apps/api/src/app/credentials/credentials.service.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -93,7 +93,7 @@ describe("CredentialsService", () => {
const fun = credentialsService.setOAuthState({
groupId: _groupId,
memberId: "123",
providerName: "twitter"
providerName: "github"
})

await expect(fun).rejects.toThrow(
Expand All @@ -117,7 +117,7 @@ describe("CredentialsService", () => {
stateId = await credentialsService.setOAuthState({
groupId,
memberId: "123",
providerName: "twitter"
providerName: "github"
})

expect(stateId).toHaveLength(36)
Expand All @@ -129,23 +129,23 @@ describe("CredentialsService", () => {
({
getAccessToken: jest.fn(() => "123"),
getProfile: jest.fn(() => ({
id: "id2"
id: "id1"
}))
} as any)
)

const _stateId = await credentialsService.setOAuthState({
groupId,
memberId: "123",
providerName: "twitter"
providerName: "github"
})

await credentialsService.addMember("code", _stateId)

const fun = credentialsService.setOAuthState({
groupId,
memberId: "123",
providerName: "twitter"
providerName: "github"
})

await expect(fun).rejects.toThrow(
Expand All @@ -162,26 +162,72 @@ describe("CredentialsService", () => {
})

it("Should add a member to a credential group", async () => {
const _stateId = await credentialsService.setOAuthState({
groupId,
memberId: "124",
providerName: "github"
})

const clientRedirectUri = await credentialsService.addMember(
"code",
stateId
_stateId
)

expect(clientRedirectUri).toBeUndefined()
})

it("Should throw an error if the same OAuth account tries to join the same group", async () => {
const _stateId = await credentialsService.setOAuthState({
it("Should add the same credential with different identities in different groups", async () => {
const { id: _groupId } = await groupsService.createGroup(
{
name: "Group2",
description: "This is a description",
treeDepth: 16,
fingerprintDuration: 3600,
credentials: JSON.stringify({
id: "GITHUB_FOLLOWERS",
criteria: {
minFollowers: 12
}
})
},
"admin"
)

const _stateId1 = await credentialsService.setOAuthState({
groupId,
memberId: "124",
providerName: "twitter"
memberId: "125",
providerName: "github"
})

const fun = credentialsService.addMember("code", _stateId)
const _stateId2 = await credentialsService.setOAuthState({
groupId: _groupId,
memberId: "126",
providerName: "github"
})

await expect(fun).rejects.toThrow(
`OAuth account has already joined the group`
;(getProvider as any).mockImplementationOnce(
() =>
({
getAccessToken: jest.fn(() => "123"),
getProfile: jest.fn(() => ({
id: "id2" // same OAuth account.
}))
} as any)
)

const clientRedirectUri1 = await credentialsService.addMember(
"code",
_stateId1
)

expect(clientRedirectUri1).toBeUndefined()

const clientRedirectUri2 = await credentialsService.addMember(
"code",
_stateId2
)

expect(clientRedirectUri2).toBeUndefined()
})

it("Should throw an error if the OAuth account does not have enough credential", async () => {
Expand All @@ -200,8 +246,8 @@ describe("CredentialsService", () => {

const _stateId = await credentialsService.setOAuthState({
groupId,
memberId: "124",
providerName: "twitter"
memberId: "127",
providerName: "github"
})

const fun = credentialsService.addMember("code", _stateId)
Expand All @@ -210,5 +256,29 @@ describe("CredentialsService", () => {
`OAuth account does not match criteria`
)
})

it("Should throw an error if the same OAuth account tries to join the same group", async () => {
;(getProvider as any).mockImplementationOnce(
() =>
({
getAccessToken: jest.fn(() => "123"),
getProfile: jest.fn(() => ({
id: "id2" // OAuth account already used to join the group.
}))
} as any)
)

const _stateId = await credentialsService.setOAuthState({
groupId,
memberId: "128",
providerName: "github"
})

const fun = credentialsService.addMember("code", _stateId)

await expect(fun).rejects.toThrow(
`OAuth account has already joined the group`
)
})
})
})
15 changes: 13 additions & 2 deletions apps/api/src/app/groups/entities/group.entity.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@ import {
CreateDateColumn,
Entity,
Index,
JoinTable,
ManyToMany,
OneToMany,
PrimaryColumn,
UpdateDateColumn
Expand Down Expand Up @@ -31,8 +33,17 @@ export class Group {
@Column({ name: "fingerprint_duration" })
fingerprintDuration: number

@OneToMany(() => Member, (member) => member.group, {
cascade: ["insert"]
@ManyToMany(() => Member)
@JoinTable({
name: "memberships",
joinColumn: {
name: "group",
referencedColumnName: "id"
},
inverseJoinColumn: {
name: "member",
referencedColumnName: "id"
}
})
members: Member[]

Expand Down
27 changes: 3 additions & 24 deletions apps/api/src/app/groups/entities/member.entity.ts
Original file line number Diff line number Diff line change
@@ -1,32 +1,11 @@
import {
CreateDateColumn,
Entity,
ManyToOne,
Column,
Index,
Unique,
JoinColumn
} from "typeorm"

import { Group } from "./group.entity"
import { CreateDateColumn, Entity, Index, PrimaryColumn } from "typeorm"

@Entity("members")
@Unique(["id", "group"])
@Index(["id", "group"])
export class Member {
@Column({ primary: true, unique: false })
@PrimaryColumn()
@Index({ unique: true })
id: string

// In reality the relation group -> members is many-to-many.
// i.e we allow same member id to be part of many groups.
// But since this property is not used in any feature at the moment,
// it is treated as many-to-one in the code for simplicity.
@ManyToOne(() => Group, (group) => group.members, {
onDelete: "CASCADE"
})
@JoinColumn({ name: "group_id" })
group: Group

@CreateDateColumn({ name: "created_at" })
createdAt: Date
}
Loading

0 comments on commit e2e1697

Please sign in to comment.