Skip to content

Commit caa438a

Browse files
authored
Member emails as arrays (#672)
1 parent ef91741 commit caa438a

14 files changed

Lines changed: 129 additions & 96 deletions

File tree

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
alter table members
2+
alter column emails type text using coalesce(emails[1],'');
3+
4+
ALTER TABLE members
5+
RENAME COLUMN emails TO email;
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
alter table members
2+
alter column email type text[] using case when email is null then array[]::text[] else array[email] end;
3+
4+
ALTER TABLE members
5+
RENAME COLUMN email TO emails;
6+
7+
UPDATE members
8+
SET emails = array(
9+
select distinct a.c
10+
from unnest(members.emails || ARRAY( SELECT jsonb_array_elements_text( cache.data->'emails' ) )) WITH ORDINALITY a(c,idx)
11+
where nullif(trim(c), '') is not null
12+
)
13+
FROM "memberEnrichmentCache" cache
14+
WHERE members.id = cache."memberId"

backend/src/database/models/member.ts

Lines changed: 3 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -27,8 +27,9 @@ export default (sequelize) => {
2727
notEmpty: true,
2828
},
2929
},
30-
email: {
31-
type: DataTypes.TEXT,
30+
emails: {
31+
type: DataTypes.ARRAY(DataTypes.TEXT),
32+
defaultValue: [],
3233
},
3334
score: {
3435
type: DataTypes.INTEGER,
@@ -73,13 +74,6 @@ export default (sequelize) => {
7374
deletedAt: null,
7475
},
7576
},
76-
{
77-
unique: false,
78-
fields: ['email', 'tenantId'],
79-
where: {
80-
deletedAt: null,
81-
},
82-
},
8377
// Using GIN index so we can index every single platform
8478
// in the JSONB field
8579
{

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

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -135,11 +135,11 @@ describe('MemberAttributeSettings tests', () => {
135135
// no type
136136
await expect(() =>
137137
MemberAttributeSettingsRepository.create(
138-
{ type: AttributeType.STRING, label: 'Some Email', name: 'email' },
138+
{ type: AttributeType.STRING, label: 'Some Email', name: 'emails' },
139139
mockIRepositoryOptions,
140140
),
141141
).rejects.toThrowError(
142-
new Error400('en', 'settings.memberAttributes.errors.reservedField', 'email'),
142+
new Error400('en', 'settings.memberAttributes.errors.reservedField', 'emails'),
143143
)
144144
})
145145
})

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

Lines changed: 34 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
import { Op } from 'sequelize'
12
import MemberRepository from '../memberRepository'
23
import SequelizeTestUtils from '../../utils/sequelizeTestUtils'
34
import Error404 from '../../../errors/Error404'
@@ -29,7 +30,7 @@ describe('MemberRepository tests', () => {
2930
[PlatformType.GITHUB]: 'anil_github',
3031
},
3132
displayName: 'Member 1',
32-
email: 'lala@l.com',
33+
emails: ['lala@l.com'],
3334
score: 10,
3435
attributes: {
3536
[PlatformType.GITHUB]: {
@@ -65,7 +66,7 @@ describe('MemberRepository tests', () => {
6566
username: member2add.username,
6667
attributes: member2add.attributes,
6768
displayName: member2add.displayName,
68-
email: member2add.email,
69+
emails: member2add.emails,
6970
score: member2add.score,
7071
identities: ['github'],
7172
lastEnriched: null,
@@ -106,7 +107,7 @@ describe('MemberRepository tests', () => {
106107
[PlatformType.GITHUB]: 'anil_github',
107108
},
108109
displayName: 'Member 1',
109-
email: 'lala@l.com',
110+
emails: ['lala@l.com'],
110111
score: 10,
111112
attributes: {
112113
[PlatformType.GITHUB]: {
@@ -142,7 +143,7 @@ describe('MemberRepository tests', () => {
142143
username: member2add.username,
143144
displayName: member2add.displayName,
144145
attributes: member2add.attributes,
145-
email: member2add.email,
146+
emails: member2add.emails,
146147
lastEnriched: null,
147148
enrichedBy: [],
148149
contributions: null,
@@ -182,7 +183,7 @@ describe('MemberRepository tests', () => {
182183
organizations: [],
183184
attributes: {},
184185
identities: ['github'],
185-
email: null,
186+
emails: [],
186187
lastEnriched: null,
187188
enrichedBy: [],
188189
contributions: null,
@@ -221,7 +222,7 @@ describe('MemberRepository tests', () => {
221222
// sequelize unique constraint
222223
const member2add = {
223224
joinedAt: '2020-05-27T15:13:30Z',
224-
email: 'test@crowd.dev',
225+
emails: ['test@crowd.dev'],
225226
}
226227

227228
await expect(() =>
@@ -236,7 +237,7 @@ describe('MemberRepository tests', () => {
236237
// sequelize unique constraint
237238
const member2add = {
238239
username: { [PlatformType.GITHUB]: 'anil' },
239-
email: 'test@crowd.dev',
240+
emails: ['test@crowd.dev'],
240241
}
241242

242243
await expect(() =>
@@ -323,7 +324,7 @@ describe('MemberRepository tests', () => {
323324
displayName: member2add.displayName,
324325
identities: ['github'],
325326
attributes: {},
326-
email: null,
327+
emails: [],
327328
lastEnriched: null,
328329
enrichedBy: [],
329330
contributions: null,
@@ -381,7 +382,7 @@ describe('MemberRepository tests', () => {
381382
enrichedBy: [],
382383
contributions: null,
383384
attributes: {},
384-
email: null,
385+
emails: [],
385386
score: -1,
386387
importHash: null,
387388
createdAt: SequelizeTestUtils.getNowWithoutTime(),
@@ -494,12 +495,16 @@ describe('MemberRepository tests', () => {
494495
username: { [PlatformType.GITHUB]: 'test1' },
495496
displayName: 'Member 1',
496497
joinedAt: '2020-05-27T15:13:30Z',
497-
email: 'joan@crowd.dev',
498+
emails: ['joan@crowd.dev'],
498499
}
499500
const member1Returned = await MemberRepository.create(member1, mockIRepositoryOptions)
500501

501502
const found = await MemberRepository.findOne(
502-
{ email: 'joan@crowd.dev' },
503+
{
504+
emails: {
505+
[Op.contains]: ['joan@crowd.dev'],
506+
},
507+
},
503508
mockIRepositoryOptions,
504509
)
505510

@@ -512,7 +517,7 @@ describe('MemberRepository tests', () => {
512517
username: { [PlatformType.GITHUB]: 'test1' },
513518
displayName: 'Member 1',
514519
joinedAt: '2020-05-27T15:13:30Z',
515-
email: 'joan@crowd.dev',
520+
emails: ['joan@crowd.dev'],
516521
}
517522
const member1Returned = await MemberRepository.create(member1, mockIRepositoryOptions)
518523
delete member1Returned.toMerge
@@ -532,7 +537,11 @@ describe('MemberRepository tests', () => {
532537
delete member1Returned.activeDaysCount
533538

534539
const found = await MemberRepository.findOne(
535-
{ email: 'joan@crowd.dev' },
540+
{
541+
emails: {
542+
[Op.contains]: ['joan@crowd.dev'],
543+
},
544+
},
536545
mockIRepositoryOptions,
537546
false,
538547
)
@@ -547,7 +556,7 @@ describe('MemberRepository tests', () => {
547556
username: { [PlatformType.DEVTO]: 'test1' },
548557
displayName: 'Member 1',
549558
joinedAt: '2020-05-27T15:13:30Z',
550-
email: 'joan@crowd.dev',
559+
emails: ['joan@crowd.dev'],
551560
}
552561
const member1Returned = await MemberRepository.create(member1, mockIRepositoryOptions)
553562

@@ -568,7 +577,7 @@ describe('MemberRepository tests', () => {
568577
username: { [PlatformType.DEVTO]: 'test1' },
569578
displayName: 'Member 1',
570579
joinedAt: '2020-05-27T15:13:30Z',
571-
email: 'joan@crowd.dev',
580+
emails: ['joan@crowd.dev'],
572581
}
573582
await MemberRepository.create(member1, mockIRepositoryOptions)
574583

@@ -587,7 +596,7 @@ describe('MemberRepository tests', () => {
587596
username: { [PlatformType.TWITTER]: 'test1' },
588597
displayName: 'Member 1',
589598
joinedAt: '2020-05-27T15:13:30Z',
590-
email: 'joan@crowd.dev',
599+
emails: ['joan@crowd.dev'],
591600
}
592601
const member1Returned = await MemberRepository.create(member1, mockIRepositoryOptions)
593602

@@ -606,7 +615,7 @@ describe('MemberRepository tests', () => {
606615
username: { [PlatformType.TWITTER]: 'test1' },
607616
displayName: 'Member 1',
608617
joinedAt: '2020-05-27T15:13:30Z',
609-
email: 'joan@crowd.dev',
618+
emails: ['joan@crowd.dev'],
610619
}
611620
const member1Returned = await MemberRepository.create(member1, mockIRepositoryOptions)
612621
delete member1Returned.toMerge
@@ -642,7 +651,7 @@ describe('MemberRepository tests', () => {
642651
username: { [PlatformType.TWITTER]: 'test1' },
643652
displayName: 'Member 1',
644653
joinedAt: '2020-05-27T15:13:30Z',
645-
email: 'joan@crowd.dev',
654+
emails: ['joan@crowd.dev'],
646655
}
647656
await MemberRepository.create(member1, mockIRepositoryOptions)
648657

@@ -658,7 +667,7 @@ describe('MemberRepository tests', () => {
658667
username: { [PlatformType.TWITTER]: 'test1' },
659668
displayName: 'Member 1',
660669
joinedAt: '2020-05-27T15:13:30Z',
661-
email: 'joan@crowd.dev',
670+
emails: ['joan@crowd.dev'],
662671
}
663672
await MemberRepository.create(member1, mockIRepositoryOptions)
664673

@@ -1267,7 +1276,7 @@ describe('MemberRepository tests', () => {
12671276
username: {
12681277
[PlatformType.GITHUB]: 'anil_github',
12691278
},
1270-
email: 'lala@l.com',
1279+
emails: ['lala@l.com'],
12711280
score: 10,
12721281
attributes: {
12731282
[PlatformType.GITHUB]: {
@@ -1311,7 +1320,7 @@ describe('MemberRepository tests', () => {
13111320
identities: ['github'],
13121321
displayName: returnedMember.displayName,
13131322
attributes: updateFields.attributes,
1314-
email: updateFields.email,
1323+
emails: updateFields.emails,
13151324
score: updateFields.score,
13161325
lastEnriched: null,
13171326
enrichedBy: [],
@@ -1359,7 +1368,7 @@ describe('MemberRepository tests', () => {
13591368
username: {
13601369
[PlatformType.GITHUB]: 'anil_github',
13611370
},
1362-
email: 'lala@l.com',
1371+
emails: ['lala@l.com'],
13631372
score: 10,
13641373
attributes: {
13651374
[PlatformType.GITHUB]: {
@@ -1406,7 +1415,7 @@ describe('MemberRepository tests', () => {
14061415
lastEnriched: null,
14071416
enrichedBy: [],
14081417
contributions: null,
1409-
email: updateFields.email,
1418+
emails: updateFields.emails,
14101419
score: updateFields.score,
14111420
importHash: null,
14121421
createdAt: SequelizeTestUtils.getNowWithoutTime(),
@@ -1465,7 +1474,7 @@ describe('MemberRepository tests', () => {
14651474
displayName: member1.displayName,
14661475
identities: ['discord'],
14671476
attributes: {},
1468-
email: member1.email,
1477+
emails: member1.emails,
14691478
score: member1.score,
14701479
organizations: [],
14711480
lastEnriched: null,
@@ -1549,7 +1558,7 @@ describe('MemberRepository tests', () => {
15491558
displayName: member1.displayName,
15501559
identities: ['discord'],
15511560
attributes: {},
1552-
email: member1.email,
1561+
emails: member1.emails,
15531562
score: member1.score,
15541563
tags: [],
15551564
lastEnriched: null,

backend/src/database/repositories/filters/__tests__/rawQueryParser.test.ts

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -146,7 +146,7 @@ describe('RawQueryParser', () => {
146146
const filter = {
147147
and: [
148148
{
149-
email: {
149+
emails: {
150150
in: ['crash@crowd.dev', 'burn@crowd.dev'],
151151
},
152152
},
@@ -163,9 +163,9 @@ describe('RawQueryParser', () => {
163163
params,
164164
)
165165

166-
expect(result).toEqual(`((m.email in (:email_1, :email_2)) and (1=1))`)
167-
expect(params.email_1).toEqual('crash@crowd.dev')
168-
expect(params.email_2).toEqual('burn@crowd.dev')
166+
expect(result).toEqual(`((m.emails in (:emails_1, :emails_2)) and (1=1))`)
167+
expect(params.emails_1).toEqual('crash@crowd.dev')
168+
expect(params.emails_2).toEqual('burn@crowd.dev')
169169
})
170170

171171
it('Should parse filter with attribute column multiselect filter', () => {

backend/src/database/repositories/memberRepository.ts

Lines changed: 10 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,7 @@ class MemberRepository {
3535
'username',
3636
'displayName',
3737
'attributes',
38-
'email',
38+
'emails',
3939
'lastEnriched',
4040
'enrichedBy',
4141
'contributions',
@@ -243,7 +243,7 @@ class MemberRepository {
243243
const currentTenant = SequelizeRepository.getCurrentTenant(options)
244244

245245
const query =
246-
'SELECT "id", "username", "displayName", "attributes", "email", "score", "lastEnriched", "enrichedBy", "contributions", "reach", "joinedAt", "importHash", "createdAt", "updatedAt", "deletedAt", "tenantId", "createdById", "updatedById" FROM "members" AS "member" WHERE ("member"."deletedAt" IS NULL AND ("member"."tenantId" = $tenantId AND ("member"."username"->>$platform) = $username)) LIMIT 1;'
246+
'SELECT "id", "username", "displayName", "attributes", "emails", "score", "lastEnriched", "enrichedBy", "contributions", "reach", "joinedAt", "importHash", "createdAt", "updatedAt", "deletedAt", "tenantId", "createdById", "updatedById" FROM "members" AS "member" WHERE ("member"."deletedAt" IS NULL AND ("member"."tenantId" = $tenantId AND ("member"."username"->>$platform) = $username)) LIMIT 1;'
247247

248248
const records = await options.database.sequelize.query(query, {
249249
type: Sequelize.QueryTypes.SELECT,
@@ -290,7 +290,7 @@ class MemberRepository {
290290
'username',
291291
'displayName',
292292
'attributes',
293-
'email',
293+
'emails',
294294
'lastEnriched',
295295
'enrichedBy',
296296
'contributions',
@@ -624,7 +624,7 @@ class MemberRepository {
624624
['importHash', 'm."importHash"'],
625625
['createdAt', 'm."createdAt"'],
626626
['updatedAt', 'm."updatedAt"'],
627-
['email', 'm.email'],
627+
['emails', 'm.emails'],
628628
])
629629

630630
static async findAndCountAllv2(
@@ -765,7 +765,7 @@ select m.id,
765765
m.username,
766766
m."displayName",
767767
m.attributes,
768-
m.email,
768+
m.emails,
769769
m."tenantId",
770770
m.score,
771771
m."lastEnriched",
@@ -996,10 +996,10 @@ where m."deletedAt" is null
996996
})
997997
}
998998

999-
if (filter.email) {
999+
if (filter.emails) {
10001000
advancedFilter.and.push({
1001-
email: {
1002-
textContains: filter.email,
1001+
emails: {
1002+
contains: filter.emails,
10031003
},
10041004
})
10051005
}
@@ -1207,7 +1207,7 @@ where m."deletedAt" is null
12071207
'username',
12081208
'attributes',
12091209
'displayName',
1210-
'email',
1210+
'emails',
12111211
'score',
12121212
'lastEnriched',
12131213
'enrichedBy',
@@ -1285,7 +1285,7 @@ where m."deletedAt" is null
12851285
'username',
12861286
'attributes',
12871287
'displayName',
1288-
'email',
1288+
'emails',
12891289
'tenantId',
12901290
'score',
12911291
'lastEnriched',

0 commit comments

Comments
 (0)