diff --git a/backend/src/database/repositories/__tests__/memberRepository.test.ts b/backend/src/database/repositories/__tests__/memberRepository.test.ts index a6e5bef629..5a05c10281 100644 --- a/backend/src/database/repositories/__tests__/memberRepository.test.ts +++ b/backend/src/database/repositories/__tests__/memberRepository.test.ts @@ -3217,7 +3217,7 @@ describe('MemberRepository tests', () => { // member1 is expected to have [org1,org2] after update member1 = await MemberRepository.update( member1.id, - { organizations: [org1.id, org2.id] }, + { organizations: [org1.id, org2.id], organizationsReplace: true }, mockIRepositoryOptions, ) diff --git a/backend/src/database/repositories/memberRepository.ts b/backend/src/database/repositories/memberRepository.ts index 5fe85cbde3..371a0a0a34 100644 --- a/backend/src/database/repositories/memberRepository.ts +++ b/backend/src/database/repositories/memberRepository.ts @@ -8,6 +8,7 @@ import { OrganizationSource, } from '@crowd/types' import lodash, { chunk } from 'lodash' +import moment from 'moment' import Sequelize, { QueryTypes } from 'sequelize' import { FieldTranslatorFactory, OpensearchQueryParser } from '@crowd/opensearch' @@ -137,6 +138,7 @@ class MemberRepository { await MemberRepository.updateMemberOrganizations( record, data.organizations, + true, transaction, options, ) @@ -565,7 +567,8 @@ class MemberRepository { 'dateEnd', "dateEnd", 'createdAt', "createdAt", 'updatedAt', "updatedAt", - 'title', title + 'title', title, + 'source', source ) ) ) AS orgs @@ -688,6 +691,7 @@ class MemberRepository { await MemberRepository.updateMemberOrganizations( record, data.organizations, + data.organizationsReplace, transaction, options, ) @@ -963,7 +967,7 @@ class MemberRepository { as: 'organizations', order: [['createdAt', 'ASC']], through: { - attributes: ['memberId', 'organizationId', 'dateStart', 'dateEnd', 'title'], + attributes: ['memberId', 'organizationId', 'dateStart', 'dateEnd', 'title', 'source'], where: { deletedAt: null, }, @@ -3199,7 +3203,7 @@ class MemberRepository { output.organizations = await record.getOrganizations({ transaction, order: [['createdAt', 'ASC']], - joinTableAttributes: ['dateStart', 'dateEnd', 'title'], + joinTableAttributes: ['dateStart', 'dateEnd', 'title', 'source'], through: { where: { deletedAt: null, @@ -3268,6 +3272,7 @@ class MemberRepository { static async updateMemberOrganizations( record, organizations, + replace, transaction, options: IRepositoryOptions, ) { @@ -3275,26 +3280,30 @@ class MemberRepository { return } - const sourceUi = !!organizations.find((org) => org.source === OrganizationSource.UI) + function iso(v) { + return moment(v).toISOString() + } - const originalOrgs = await MemberRepository.fetchWorkExperiences(record.id, sourceUi, options) + if (replace) { + const originalOrgs = await MemberRepository.fetchWorkExperiences(record.id, options) - const toDelete = originalOrgs.filter( - (originalOrg: any) => - !organizations.find( - (newOrg) => - originalOrg.organizationId === newOrg.id && - originalOrg.title === (newOrg.title || null) && - originalOrg.dateStart === (newOrg.startDate || null) && - originalOrg.dateEnd === (newOrg.endDate || null), - ), - ) + const toDelete = originalOrgs.filter( + (originalOrg: any) => + !organizations.find( + (newOrg) => + originalOrg.organizationId === newOrg.id && + originalOrg.title === (newOrg.title || null) && + iso(originalOrg.dateStart) === iso(newOrg.startDate || null) && + iso(originalOrg.dateEnd) === iso(newOrg.endDate || null), + ), + ) - for (const item of toDelete) { - await MemberRepository.deleteWorkExperience((item as any).id, { - transaction, - ...options, - }) + for (const item of toDelete) { + await MemberRepository.deleteWorkExperience((item as any).id, { + transaction, + ...options, + }) + } } for (const item of organizations) { @@ -3438,18 +3447,13 @@ class MemberRepository { ) } - static async fetchWorkExperiences( - memberId: string, - sourceUi: boolean, - options: IRepositoryOptions, - ) { + static async fetchWorkExperiences(memberId: string, options: IRepositoryOptions) { const seq = SequelizeRepository.getSequelize(options) const transaction = SequelizeRepository.getTransaction(options) const query = ` SELECT * FROM "memberOrganizations" WHERE "memberId" = :memberId - ${sourceUi ? '' : "AND (source IS NULL OR source != 'ui')"} AND "deletedAt" IS NULL ` diff --git a/backend/src/serverless/integrations/types/messageTypes.ts b/backend/src/serverless/integrations/types/messageTypes.ts index 5d636faeee..fb4915a419 100644 --- a/backend/src/serverless/integrations/types/messageTypes.ts +++ b/backend/src/serverless/integrations/types/messageTypes.ts @@ -62,7 +62,7 @@ export type Member = { displayName?: string attributes?: any emails?: string[] - organizations?: [any] + organizations?: any[] bio?: string reach?: number | any location?: string diff --git a/backend/src/services/__tests__/memberService.test.ts b/backend/src/services/__tests__/memberService.test.ts index 74550995cd..8c97245d03 100644 --- a/backend/src/services/__tests__/memberService.test.ts +++ b/backend/src/services/__tests__/memberService.test.ts @@ -486,6 +486,7 @@ describe('MemberService tests', () => { dateEnd: null, dateStart: null, title: null, + source: null, }, tags: null, twitter: null, @@ -580,6 +581,7 @@ describe('MemberService tests', () => { dateEnd: null, dateStart: null, title: null, + source: null, }, tags: null, twitter: null, @@ -678,6 +680,7 @@ describe('MemberService tests', () => { dateEnd: null, dateStart: null, title: null, + source: null, }, tags: null, twitter: null, @@ -776,6 +779,7 @@ describe('MemberService tests', () => { dateEnd: null, dateStart: null, title: null, + source: null, }, tags: [], twitter: { diff --git a/backend/src/services/memberService.ts b/backend/src/services/memberService.ts index 77f97cc5cf..6bce70a1d1 100644 --- a/backend/src/services/memberService.ts +++ b/backend/src/services/memberService.ts @@ -698,32 +698,6 @@ export default class MemberService extends LoggerBase { return toKeep }, - organizations: (oldOrganizations, newOrganizations) => { - const convertOrgs = (orgs) => - orgs - ? orgs - .map((o) => (o.dataValues ? o.get({ plain: true }) : o)) - .map((o) => { - if (typeof o === 'string') { - return { - id: o, - } - } - const memberOrg = o.memberOrganizations - return { - id: o.id, - title: memberOrg?.title, - startDate: memberOrg?.dateStart, - endDate: memberOrg?.dateEnd, - } - }) - : [] - - oldOrganizations = convertOrgs(oldOrganizations) - newOrganizations = convertOrgs(newOrganizations) - - return lodash.uniqWith([...oldOrganizations, ...newOrganizations], lodash.isEqual) - }, }) } diff --git a/backend/src/services/premium/enrichment/memberEnrichmentService.ts b/backend/src/services/premium/enrichment/memberEnrichmentService.ts index b2102caad6..3e0a1e8338 100644 --- a/backend/src/services/premium/enrichment/memberEnrichmentService.ts +++ b/backend/src/services/premium/enrichment/memberEnrichmentService.ts @@ -10,7 +10,6 @@ import { MemberEnrichmentAttributeName, MemberEnrichmentAttributes, PlatformType, - IOrganization, OrganizationSource, } from '@crowd/types' import { ENRICHMENT_CONFIG, REDIS_CONFIG } from '../../../conf' @@ -354,39 +353,6 @@ export default class MemberEnrichmentService extends LoggerBase { member.emails = Array.from(emailSet) } - if (enrichmentData.company) { - const organization: IOrganization = { - name: enrichmentData.company, - } - - // check for more info about the company in work experiences - if (enrichmentData.work_experiences && enrichmentData.work_experiences.length > 0) { - const organizationsByWorkExperience = enrichmentData.work_experiences.filter( - (w) => w.company === enrichmentData.company && w.current, - ) - if (organizationsByWorkExperience.length > 0) { - organization.location = organizationsByWorkExperience[0].location - const linkedinUrl = organizationsByWorkExperience[0].companyLinkedInUrl - if (linkedinUrl) { - organization.linkedin = { - handle: linkedinUrl.split('/').pop(), - // remove https/http if exists - url: linkedinUrl.replace(/(^\w+:|^)\/\//, ''), - } - } - organization.url = organizationsByWorkExperience[0].companyUrl - - // fetch jobTitle from most recent work experience - member.attributes.jobTitle = { - custom: organizationsByWorkExperience[0].title, - default: organizationsByWorkExperience[0].title, - } - } - } - - member.organizations = [organization] - } - member.contributions = enrichmentData.oss_contributions?.map( (contribution: EnrichmentAPIContribution) => ({ id: contribution.id, diff --git a/frontend/src/modules/member/components/form/member-form-organizations-drawer.vue b/frontend/src/modules/member/components/form/member-form-organizations-drawer.vue index 56554578ba..30524ac81e 100644 --- a/frontend/src/modules/member/components/form/member-form-organizations-drawer.vue +++ b/frontend/src/modules/member/components/form/member-form-organizations-drawer.vue @@ -104,6 +104,7 @@ const save = () => { doUpdate({ id: props.member.id, values: { + organizationsReplace: true, organizations: organizations.value.map( (o) => ({ id: o.id, diff --git a/frontend/src/modules/member/pages/member-form-page.vue b/frontend/src/modules/member/pages/member-form-page.vue index c75418a19a..7fe19aff61 100644 --- a/frontend/src/modules/member/pages/member-form-page.vue +++ b/frontend/src/modules/member/pages/member-form-page.vue @@ -352,6 +352,7 @@ async function onSubmit() { tags: formModel.value.tags.map((t) => t.id), }, ...formModel.value.organizations.length && { + organizationsReplace: true, organizations: formModel.value.organizations.map( (o) => ({ id: o.id,