Skip to content

Commit 4534171

Browse files
authored
Mark as team organization (#622)
1 parent bcdb84f commit 4534171

2 files changed

Lines changed: 219 additions & 21 deletions

File tree

backend/src/database/repositories/__tests__/organizationRepository.test.ts

Lines changed: 179 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -65,6 +65,27 @@ async function createMembers(options) {
6565
]
6666
}
6767

68+
async function createOrganization(organization: any, options, members = []) {
69+
const memberIds = []
70+
for (const member of members) {
71+
const memberCreated = await MemberRepository.create(
72+
SequelizeTestUtils.objectWithoutKey(member, 'activities'),
73+
options,
74+
)
75+
76+
if (member.activities) {
77+
for (const activity of member.activities) {
78+
await ActivityRepository.create({ ...activity, member: memberCreated.id }, options)
79+
}
80+
}
81+
82+
memberIds.push(memberCreated.id)
83+
}
84+
organization.members = memberIds
85+
const organizationCreated = await OrganizationRepository.create(organization, options)
86+
return { ...organizationCreated, members: memberIds }
87+
}
88+
6889
describe('OrganizationRepository tests', () => {
6990
beforeEach(async () => {
7091
await SequelizeTestUtils.wipeDatabase(db)
@@ -576,26 +597,6 @@ describe('OrganizationRepository tests', () => {
576597
},
577598
}
578599

579-
async function createOrganization(organization: any, options, members = []) {
580-
const memberIds = []
581-
for (const member of members) {
582-
const memberCreated = await MemberRepository.create(
583-
SequelizeTestUtils.objectWithoutKey(member, 'activities'),
584-
options,
585-
)
586-
587-
if (member.activities) {
588-
for (const activity of member.activities) {
589-
await ActivityRepository.create({ ...activity, member: memberCreated.id }, options)
590-
}
591-
}
592-
593-
memberIds.push(memberCreated.id)
594-
}
595-
organization.members = memberIds
596-
return OrganizationRepository.create(organization, options)
597-
}
598-
599600
it('Should filter by name', async () => {
600601
const mockIRepositoryOptions = await SequelizeTestUtils.getTestIRepositoryOptions(db)
601602
await createOrganization(crowddev, mockIRepositoryOptions)
@@ -894,6 +895,8 @@ describe('OrganizationRepository tests', () => {
894895
mockIRepositoryOptions,
895896
)
896897

898+
delete org1.members
899+
897900
expect(found.count).toBe(1)
898901
expect(found.rows).toStrictEqual([org1])
899902
})
@@ -942,6 +945,9 @@ describe('OrganizationRepository tests', () => {
942945
mockIRepositoryOptions,
943946
)
944947

948+
delete org1.members
949+
delete org2.members
950+
945951
expect(found.count).toBe(2)
946952
expect(found.rows.sort((a, b) => (a.createdAt > b.createdAt ? 1 : -1))).toStrictEqual([
947953
org1,
@@ -1033,6 +1039,8 @@ describe('OrganizationRepository tests', () => {
10331039
mockIRepositoryOptions,
10341040
)
10351041

1042+
delete org2.members
1043+
10361044
expect(found.count).toBe(1)
10371045
expect(found.rows.sort((a, b) => (a.createdAt > b.createdAt ? 1 : -1))).toStrictEqual([org2])
10381046
})
@@ -1216,6 +1224,157 @@ describe('OrganizationRepository tests', () => {
12161224
})
12171225
})
12181226

1227+
describe('setOrganizationIsTeam method', () => {
1228+
const member1 = {
1229+
username: {
1230+
devto: 'iambarker',
1231+
github: 'barker',
1232+
},
1233+
displayName: 'Jack Barker',
1234+
attributes: {
1235+
bio: {
1236+
github: 'Head of development at Hooli',
1237+
twitter: 'Head of development at Hooli | ex CEO at Pied Piper',
1238+
},
1239+
sample: { crowd: true, default: true },
1240+
jobTitle: { custom: 'Head of development', default: 'Head of development' },
1241+
location: { github: 'Silicon Valley', default: 'Silicon Valley' },
1242+
avatarUrl: {
1243+
custom:
1244+
'https://s3.eu-central-1.amazonaws.com/crowd.dev-sample-data/jack-barker-best.jpg',
1245+
default:
1246+
'https://s3.eu-central-1.amazonaws.com/crowd.dev-sample-data/jack-barker-best.jpg',
1247+
},
1248+
},
1249+
joinedAt: moment().toDate(),
1250+
activities: [
1251+
{
1252+
type: 'star',
1253+
timestamp: '2020-05-27T15:13:30Z',
1254+
platform: PlatformType.GITHUB,
1255+
sourceId: '#sourceId1',
1256+
},
1257+
],
1258+
}
1259+
1260+
const member2 = {
1261+
username: {
1262+
devto: 'thebelson',
1263+
github: 'gavinbelson',
1264+
discord: 'gavinbelson',
1265+
twitter: 'gavin',
1266+
linkedin: 'gavinbelson',
1267+
},
1268+
displayName: 'Gavin Belson',
1269+
attributes: {
1270+
bio: {
1271+
custom: 'CEO at Hooli',
1272+
github: 'CEO at Hooli',
1273+
default: 'CEO at Hooli',
1274+
twitter: 'CEO at Hooli',
1275+
},
1276+
sample: { crowd: true, default: true },
1277+
jobTitle: { custom: 'CEO', default: 'CEO' },
1278+
location: { github: 'Silicon Valley', default: 'Silicon Valley' },
1279+
avatarUrl: {
1280+
custom: 'https://s3.eu-central-1.amazonaws.com/crowd.dev-sample-data/gavin.jpg',
1281+
default: 'https://s3.eu-central-1.amazonaws.com/crowd.dev-sample-data/gavin.jpg',
1282+
},
1283+
},
1284+
joinedAt: moment().toDate(),
1285+
activities: [
1286+
{
1287+
type: 'star',
1288+
timestamp: '2020-05-28T15:13:30Z',
1289+
platform: PlatformType.GITHUB,
1290+
sourceId: '#sourceId2',
1291+
},
1292+
],
1293+
}
1294+
1295+
const member3 = {
1296+
username: {
1297+
devto: 'bigheader',
1298+
github: 'bighead',
1299+
},
1300+
displayName: 'Big Head',
1301+
attributes: {
1302+
bio: {
1303+
custom: 'Executive at the Hooli XYZ project',
1304+
github: 'Co-head Dreamer of the Hooli XYZ project',
1305+
default: 'Executive at the Hooli XYZ project',
1306+
twitter: 'Co-head Dreamer of the Hooli XYZ project',
1307+
},
1308+
sample: { crowd: true, default: true },
1309+
jobTitle: { custom: 'Co-head Dreamer', default: 'Co-head Dreamer' },
1310+
location: { github: 'Silicon Valley', default: 'Silicon Valley' },
1311+
avatarUrl: {
1312+
custom: 'https://s3.eu-central-1.amazonaws.com/crowd.dev-sample-data/big-head-small.jpg',
1313+
default: 'https://s3.eu-central-1.amazonaws.com/crowd.dev-sample-data/big-head-small.jpg',
1314+
},
1315+
},
1316+
joinedAt: moment().toDate(),
1317+
activities: [
1318+
{
1319+
type: 'star',
1320+
timestamp: '2020-05-29T15:13:30Z',
1321+
platform: PlatformType.GITHUB,
1322+
sourceId: '#sourceId3',
1323+
},
1324+
],
1325+
}
1326+
it('Should succesfully set/unset organization members as team members', async () => {
1327+
const mockIRepositoryOptions = await SequelizeTestUtils.getTestIRepositoryOptions(db)
1328+
const org = await createOrganization(toCreate, mockIRepositoryOptions, [
1329+
member1,
1330+
member2,
1331+
member3,
1332+
])
1333+
1334+
// mark organization members as team members
1335+
await OrganizationRepository.setOrganizationIsTeam(org.id, true, mockIRepositoryOptions)
1336+
1337+
let m1 = await MemberRepository.findById(org.members[0], mockIRepositoryOptions)
1338+
let m2 = await MemberRepository.findById(org.members[1], mockIRepositoryOptions)
1339+
let m3 = await MemberRepository.findById(org.members[2], mockIRepositoryOptions)
1340+
1341+
expect(m1.attributes.isTeamMember.default).toEqual(true)
1342+
expect(m2.attributes.isTeamMember.default).toEqual(true)
1343+
expect(m3.attributes.isTeamMember.default).toEqual(true)
1344+
1345+
// expect other attributes intact
1346+
delete m1.attributes.isTeamMember
1347+
expect(m1.attributes).toStrictEqual(member1.attributes)
1348+
1349+
delete m2.attributes.isTeamMember
1350+
expect(m2.attributes).toStrictEqual(member2.attributes)
1351+
1352+
delete m3.attributes.isTeamMember
1353+
expect(m3.attributes).toStrictEqual(member3.attributes)
1354+
1355+
// now unmark
1356+
await OrganizationRepository.setOrganizationIsTeam(org.id, false, mockIRepositoryOptions)
1357+
1358+
m1 = await MemberRepository.findById(org.members[0], mockIRepositoryOptions)
1359+
m2 = await MemberRepository.findById(org.members[1], mockIRepositoryOptions)
1360+
m3 = await MemberRepository.findById(org.members[2], mockIRepositoryOptions)
1361+
1362+
expect(m1.attributes.isTeamMember.default).toEqual(false)
1363+
expect(m2.attributes.isTeamMember.default).toEqual(false)
1364+
expect(m3.attributes.isTeamMember.default).toEqual(false)
1365+
1366+
// expect other attributes intact
1367+
delete m1.attributes.isTeamMember
1368+
expect(m1.attributes).toStrictEqual(member1.attributes)
1369+
1370+
delete m2.attributes.isTeamMember
1371+
expect(m2.attributes).toStrictEqual(member2.attributes)
1372+
1373+
delete m3.attributes.isTeamMember
1374+
expect(m3.attributes).toStrictEqual(member3.attributes)
1375+
})
1376+
})
1377+
12191378
describe('destroy method', () => {
12201379
it('Should succesfully destroy previously created organization', async () => {
12211380
const mockIRepositoryOptions = await SequelizeTestUtils.getTestIRepositoryOptions(db)

backend/src/database/repositories/organizationRepository.ts

Lines changed: 40 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import lodash from 'lodash'
2-
import Sequelize from 'sequelize'
2+
import Sequelize, { QueryTypes } from 'sequelize'
33
import SequelizeRepository from './sequelizeRepository'
44
import AuditLogRepository from './auditLogRepository'
55
import SequelizeFilterUtils from '../utils/sequelizeFilterUtils'
@@ -110,11 +110,50 @@ class OrganizationRepository {
110110
})
111111
}
112112

113+
if (
114+
data.isTeamOrganization === true ||
115+
data.isTeamOrganization === 'true' ||
116+
data.isTeamOrganization === false ||
117+
data.isTeamOrganization === 'false'
118+
) {
119+
await this.setOrganizationIsTeam(record.id, data.isTeamOrganization, options)
120+
}
121+
113122
await this._createAuditLog(AuditLogRepository.UPDATE, record, data, options)
114123

115124
return this.findById(record.id, options)
116125
}
117126

127+
/**
128+
* Marks/unmarks an organization's members as team members
129+
* @param organizationId
130+
* @param isTeam
131+
* @param options
132+
*/
133+
static async setOrganizationIsTeam(
134+
organizationId: string,
135+
isTeam: boolean,
136+
options: IRepositoryOptions,
137+
): Promise<void> {
138+
await options.database.sequelize.query(
139+
`update members as m
140+
set attributes = jsonb_set("attributes", '{isTeamMember}', '{"default": ${isTeam}}'::jsonb)
141+
from "memberOrganizations" as mo
142+
where mo."memberId" = m.id
143+
and mo."organizationId" = :organizationId
144+
and m."tenantId" = :tenantId;
145+
`,
146+
{
147+
replacements: {
148+
isTeam,
149+
organizationId,
150+
tenantId: options.currentTenant.id,
151+
},
152+
type: QueryTypes.UPDATE,
153+
},
154+
)
155+
}
156+
118157
static async destroy(id, options: IRepositoryOptions, force = false) {
119158
const transaction = SequelizeRepository.getTransaction(options)
120159

0 commit comments

Comments
 (0)