Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
49 commits
Select commit Hold shift + click to select a range
8a6ce60
Automation refactoring
gaspergrom Jul 20, 2023
9d1333a
Split to configuration
gaspergrom Jul 21, 2023
b754036
Add common trigger component
gaspergrom Jul 24, 2023
77b2e76
Hubspot automation form
gaspergrom Jul 25, 2023
381b84b
Merge branch 'main' into feature/hubspot-automations
gaspergrom Jul 25, 2023
4826a24
Hubspot automations testing & fixes
gaspergrom Jul 25, 2023
d55deed
Bug fixes
gaspergrom Jul 25, 2023
23c5acf
Merge branch 'main' into feature/hubspot-automations
gaspergrom Jul 26, 2023
d5a019d
Bug fixing and improvements
gaspergrom Jul 26, 2023
35779c1
Fix limits on scale plan
gaspergrom Jul 26, 2023
05c06c2
Fix limits on scale plan
gaspergrom Jul 26, 2023
b9453e2
bug fixes and improvements
gaspergrom Jul 26, 2023
b5d07ea
Fetch integrations
gaspergrom Jul 26, 2023
63c1952
Bug fixes
gaspergrom Jul 26, 2023
6df22c1
Fix settings drawer
gaspergrom Jul 26, 2023
21f748d
Merge branch 'main' into feature/hubspot-automations
epipav Jul 31, 2023
946fc25
get mappable fields also returns readonly information
epipav Aug 1, 2023
634aef0
Add icon for read-only attributes and introduce alert popover (#1209)
skwowet Aug 3, 2023
5efa396
Fix organization sync
gaspergrom Aug 3, 2023
bb67151
Merge branch 'feature/hubspot-automations' of github.com:CrowdDotDev/…
gaspergrom Aug 3, 2023
4628192
automations for hubspot members
epipav Aug 3, 2023
e474d1f
Merge branch 'feature/hubspot-automations' of github.com:CrowdDotDev/…
epipav Aug 3, 2023
95ea4a3
switch to new endpoint for mappable fields
epipav Aug 3, 2023
df82857
removed old typemap mapper function
epipav Aug 3, 2023
2039c0b
Merge branch 'main' into feature/hubspot-automations
epipav Aug 3, 2023
abdf0ab
Fix headcount filter render
gaspergrom Aug 3, 2023
5f16d9e
Merge branch 'feature/hubspot-automations' of github.com:CrowdDotDev/…
gaspergrom Aug 3, 2023
2ba164e
Add prevention of duplicate filters & property filter fix
gaspergrom Aug 3, 2023
e12a9f8
Fix organization contact list validation
gaspergrom Aug 3, 2023
59aebd0
automations with new maintanence tables
epipav Aug 4, 2023
78ba20b
mappable fields identities problem fixed
epipav Aug 4, 2023
3743b7b
linting, added automationExecution rows for hubspot
epipav Aug 4, 2023
0a5c028
index migrations for syncRemote tables
epipav Aug 4, 2023
d906d6f
linting for automation executions
epipav Aug 4, 2023
0fa3c2a
stop manual sync endpoints update remoteSync tables
epipav Aug 4, 2023
2bb41c9
review points, manual sync endpoints fix
epipav Aug 4, 2023
d56f90e
syntax error fixed on syncing sqls
epipav Aug 4, 2023
a53260f
fix returning id on conflict
epipav Aug 4, 2023
156bcaa
on conflict using excluded to return the ids
epipav Aug 4, 2023
ebc4ec4
removed on conflict query with proper checks
epipav Aug 4, 2023
483926b
on conflict check also removed from members sync remote repo
epipav Aug 4, 2023
cdf030a
null sourceIds handled on syncRemote repo
epipav Aug 4, 2023
322d801
transaction management with hubspot sync endpoints
epipav Aug 5, 2023
cda06b8
core review points, automation deletion fixes,, bug fixes
epipav Aug 5, 2023
fee1737
409 conflict resolve filtering wrong field fixed
epipav Aug 6, 2023
69a85e0
case insensitive conflict handle
epipav Aug 6, 2023
c2c4368
empty filter fix for organization filters
epipav Aug 6, 2023
1bfee55
disconnecting integration also deletes related sync automations, exec…
epipav Aug 7, 2023
5f0667c
handling re-connecting outgoing integrations, few bugfixes and cleaning
epipav Aug 7, 2023
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion backend/src/api/integration/helpers/hubspotConnect.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import IntegrationService from '../../../services/integrationService'
import PermissionChecker from '../../../services/user/permissionChecker'

export default async (req, res) => {
new PermissionChecker(req).validateHas(Permissions.values.tenantEdit)
new PermissionChecker(req).validateHas(Permissions.values.integrationEdit)
const payload = await new IntegrationService(req).hubspotConnect()
await req.responseHandler.success(req, res, payload)
}
9 changes: 9 additions & 0 deletions backend/src/api/integration/helpers/hubspotGetLists.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
import Permissions from '../../../security/permissions'
import IntegrationService from '../../../services/integrationService'
import PermissionChecker from '../../../services/user/permissionChecker'

export default async (req, res) => {
new PermissionChecker(req).validateHas(Permissions.values.integrationEdit)
const payload = await new IntegrationService(req).hubspotGetLists()
await req.responseHandler.success(req, res, payload)
}
6 changes: 6 additions & 0 deletions backend/src/api/integration/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -102,6 +102,12 @@ export default (app) => {
safeWrap(require('./helpers/hubspotGetMappableFields').default),
)

app.get(
'/tenant/:tenantId/hubspot-get-lists',
featureFlagMiddleware(FeatureFlag.HUBSPOT, 'hubspot.errors.notInPlan'),
safeWrap(require('./helpers/hubspotGetLists').default),
)

app.post(
'/tenant/:tenantId/hubspot-sync-member',
featureFlagMiddleware(FeatureFlag.HUBSPOT, 'hubspot.errors.notInPlan'),
Expand Down
Empty file.
30 changes: 30 additions & 0 deletions backend/src/database/migrations/V1691074653__syncRemoteTables.sql
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
create table "membersSyncRemote" (
id uuid not null,
"memberId" uuid not null,
"sourceId" text,
"integrationId" uuid not null,
"syncFrom" text not null,
"metaData" text,
"lastSyncedAt" timestamptz,
"status" text not null,
constraint "membersSyncRemote_pkey" primary key (id),
foreign key ("memberId") references members (id) on delete cascade,
foreign key ("integrationId") references integrations (id) on delete cascade,
unique("memberId", "integrationId", "syncFrom")
);


create table "organizationsSyncRemote" (
id uuid not null,
"organizationId" uuid not null,
"sourceId" text,
"integrationId" uuid not null,
"syncFrom" text not null,
"metaData" text,
"lastSyncedAt" timestamptz,
"status" text not null,
constraint "organizationsSyncRemote_pkey" primary key (id),
foreign key ("organizationId") references organizations (id) on delete cascade,
foreign key ("integrationId") references integrations (id) on delete cascade,
unique("organizationId", "integrationId", "syncFrom")
);
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
create index if not exists "ix_membersSyncRemote_memberId" on "membersSyncRemote" ("memberId");
create index if not exists "ix_membersSyncRemote_integrationId" on "membersSyncRemote" ("integrationId");
create index if not exists "ix_membersSyncRemote_syncFrom" on "membersSyncRemote" ("syncFrom");
create index if not exists "ix_membersSyncRemote_sourceId" on "membersSyncRemote" ("sourceId");
create index if not exists "ix_membersSyncRemote_status" on "membersSyncRemote" ("status");

create index if not exists "ix_organizationsSyncRemote_organizationId" on "organizationsSyncRemote" ("organizationId");
create index if not exists "ix_organizationsSyncRemote_integrationId" on "organizationsSyncRemote" ("integrationId");
create index if not exists "ix_organizationsSyncRemote_syncFrom" on "organizationsSyncRemote" ("syncFrom");
create index if not exists "ix_organizationsSyncRemote_sourceId" on "organizationsSyncRemote" ("sourceId");
create index if not exists "ix_organizationsSyncRemote_status" on "organizationsSyncRemote" ("status");
23 changes: 23 additions & 0 deletions backend/src/database/repositories/automationExecutionRepository.ts
Original file line number Diff line number Diff line change
Expand Up @@ -137,6 +137,29 @@ export default class AutomationExecutionRepository extends RepositoryBase<
throw new Error('Method not implemented.')
}

async destroyAllAutomation(automationIds: string[]): Promise<void> {
const transaction = this.transaction

const seq = this.seq

const currentTenant = this.currentTenant

const query = `
delete
from "automationExecutions"
where "automationId" in (:automationIds)
and "tenantId" = :tenantId;`

await seq.query(query, {
replacements: {
automationIds,
tenantId: currentTenant.id,
},
type: QueryTypes.DELETE,
transaction,
})
}

override async destroyAll(ids: string[]): Promise<void> {
throw new Error('Method not implemented.')
}
Expand Down
43 changes: 43 additions & 0 deletions backend/src/database/repositories/automationRepository.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import Sequelize, { QueryTypes } from 'sequelize'
import { AutomationSyncTrigger, IAutomation } from '@crowd/types'
import AuditLogRepository from './auditLogRepository'
import { IRepositoryOptions } from './IRepositoryOptions'
import Error404 from '../../errors/Error404'
Expand Down Expand Up @@ -246,4 +247,46 @@ export default class AutomationRepository extends RepositoryBase<

return automationCount
}

public async findSyncAutomations(
tenantId: string,
platform: string,
): Promise<IAutomation[] | null> {
const seq = this.seq

const transaction = this.transaction

const pageSize = 10
const syncAutomations: IAutomation[] = []

let results
let offset

do {
offset = results ? pageSize + offset : 0

results = await seq.query(
`select * from automations
where type = :platform and "tenantId" = :tenantId and trigger in (:syncAutomationTriggers)
limit :limit offset :offset`,
{
replacements: {
tenantId,
platform,
syncAutomationTriggers: [
AutomationSyncTrigger.MEMBER_ATTRIBUTES_MATCH,
AutomationSyncTrigger.ORGANIZATION_ATTRIBUTES_MATCH,
],
limit: pageSize,
offset,
},
type: QueryTypes.SELECT,
transaction,
},
)
syncAutomations.push(...results)
} while (results.length > 0)

return syncAutomations
}
}
28 changes: 28 additions & 0 deletions backend/src/database/repositories/integrationRepository.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,10 @@ import Error404 from '../../errors/Error404'
import { IRepositoryOptions } from './IRepositoryOptions'
import QueryParser from './filters/queryParser'
import { QueryOutput } from './filters/queryTypes'
import AutomationRepository from './automationRepository'
import AutomationExecutionRepository from './automationExecutionRepository'
import MemberSyncRemoteRepository from './memberSyncRemoteRepository'
import OrganizationSyncRemoteRepository from './organizationSyncRemoteRepository'

const { Op } = Sequelize
const log: boolean = false
Expand Down Expand Up @@ -152,6 +156,30 @@ class IntegrationRepository {
},
)

// delete syncRemote rows coming from integration
await new MemberSyncRemoteRepository({ ...options, transaction }).destroyAllIntegration([
record.id,
])
await new OrganizationSyncRemoteRepository({ ...options, transaction }).destroyAllIntegration([
record.id,
])

// destroy existing automations for outgoing integrations
const syncAutomationIds = (
await new AutomationRepository({ ...options, transaction }).findSyncAutomations(
currentTenant.id,
record.platform,
)
).map((a) => a.id)

if (syncAutomationIds.length > 0) {
await new AutomationExecutionRepository({ ...options, transaction }).destroyAllAutomation(
syncAutomationIds,
)
}

await new AutomationRepository({ ...options, transaction }).destroyAll(syncAutomationIds)

await this._createAuditLog(AuditLogRepository.DELETE, record, record, options)
}

Expand Down
22 changes: 22 additions & 0 deletions backend/src/database/repositories/memberRepository.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import {
MemberAttributeType,
OpenSearchIndex,
PlatformType,
SyncStatus,
} from '@crowd/types'
import lodash, { chunk } from 'lodash'
import Sequelize, { QueryTypes } from 'sequelize'
Expand Down Expand Up @@ -45,6 +46,7 @@ import {
} from './types/memberTypes'
import Error400 from '../../errors/Error400'
import OrganizationRepository from './organizationRepository'
import MemberSyncRemoteRepository from './memberSyncRemoteRepository'

const { Op } = Sequelize

Expand Down Expand Up @@ -628,6 +630,11 @@ class MemberRepository {
throw new Error404()
}

// exclude syncRemote attributes, since these are populated from memberSyncRemote table
if (data.attributes?.syncRemote) {
delete data.attributes.syncRemote
}

record = await record.update(
{
...lodash.pick(data, [
Expand Down Expand Up @@ -3215,6 +3222,21 @@ class MemberRepository {

output.affiliations = await this.getAffiliations(record.id, options)

const manualSyncRemote = await new MemberSyncRemoteRepository({
...options,
transaction,
}).findMemberManualSync(record.id)

for (const syncRemote of manualSyncRemote) {
if (output.attributes?.syncRemote) {
output.attributes.syncRemote[syncRemote.platform] = syncRemote.status === SyncStatus.ACTIVE
} else {
output.attributes.syncRemote = {
[syncRemote.platform]: syncRemote.status === SyncStatus.ACTIVE,
}
}
}

return output
}

Expand Down
Loading