Seed company workflow for email upserts (#18909)

@thomtrp

---------

Co-authored-by: Charles Bochet <charles@twenty.com>
This commit is contained in:
Thomas des Francs
2026-03-27 13:47:07 +01:00
committed by GitHub
parent 56056b885d
commit 50ea560e57
20 changed files with 1350 additions and 467 deletions
@@ -0,0 +1,136 @@
import { type EntityManager } from 'typeorm';
import { FieldActorSource } from 'twenty-shared/types';
export const AIRBNB_ID = 'c776ee49-f608-4a77-8cc8-6fe96ae1e43f';
export const ANTHROPIC_ID = 'f45ee421-8a3e-4aa5-a1cf-7207cc6754e1';
export const STRIPE_ID = '1f70157c-4ea5-4d81-bc49-e1401abfbb94';
export const FIGMA_ID = '9d5bcf43-7d38-4e88-82cb-d6d4ce638bf0';
export const NOTION_ID = '06290608-8bf0-4806-99ae-a715a6a93fad';
export const prefillCompanies = async (
entityManager: EntityManager,
schemaName: string,
) => {
await entityManager
.createQueryBuilder()
.insert()
.into(`${schemaName}.company`, [
'id',
'name',
'domainNamePrimaryLinkUrl',
'addressAddressStreet1',
'addressAddressStreet2',
'addressAddressCity',
'addressAddressState',
'addressAddressPostcode',
'addressAddressCountry',
'employees',
'position',
'createdBySource',
'createdByWorkspaceMemberId',
'createdByName',
'updatedBySource',
'updatedByWorkspaceMemberId',
'updatedByName',
])
.orIgnore()
.values([
{
id: AIRBNB_ID,
name: 'Airbnb',
domainNamePrimaryLinkUrl: 'https://airbnb.com',
addressAddressStreet1: '888 Brannan St',
addressAddressStreet2: null,
addressAddressCity: 'San Francisco',
addressAddressState: 'CA',
addressAddressPostcode: '94103',
addressAddressCountry: 'United States',
employees: 5000,
position: 1,
createdBySource: FieldActorSource.SYSTEM,
createdByWorkspaceMemberId: null,
createdByName: 'System',
updatedBySource: FieldActorSource.SYSTEM,
updatedByWorkspaceMemberId: null,
updatedByName: 'System',
},
{
id: ANTHROPIC_ID,
name: 'Anthropic',
domainNamePrimaryLinkUrl: 'https://anthropic.com',
addressAddressStreet1: '548 Market Street',
addressAddressStreet2: null,
addressAddressCity: 'San Francisco',
addressAddressState: 'CA',
addressAddressPostcode: '94104',
addressAddressCountry: 'United States',
employees: 1100,
position: 2,
createdBySource: FieldActorSource.SYSTEM,
createdByWorkspaceMemberId: null,
createdByName: 'System',
updatedBySource: FieldActorSource.SYSTEM,
updatedByWorkspaceMemberId: null,
updatedByName: 'System',
},
{
id: STRIPE_ID,
name: 'Stripe',
domainNamePrimaryLinkUrl: 'https://stripe.com',
addressAddressStreet1: 'Eutaw Street',
addressAddressStreet2: null,
addressAddressCity: 'Dublin',
addressAddressState: null,
addressAddressPostcode: null,
addressAddressCountry: 'Ireland',
employees: 8000,
position: 3,
createdBySource: FieldActorSource.SYSTEM,
createdByWorkspaceMemberId: null,
createdByName: 'System',
updatedBySource: FieldActorSource.SYSTEM,
updatedByWorkspaceMemberId: null,
updatedByName: 'System',
},
{
id: FIGMA_ID,
name: 'Figma',
domainNamePrimaryLinkUrl: 'https://figma.com',
addressAddressStreet1: '760 Market St',
addressAddressStreet2: 'Floor 10',
addressAddressCity: 'San Francisco',
addressAddressState: null,
addressAddressPostcode: '94102',
addressAddressCountry: 'United States',
employees: 800,
position: 4,
createdBySource: FieldActorSource.SYSTEM,
createdByWorkspaceMemberId: null,
createdByName: 'System',
updatedBySource: FieldActorSource.SYSTEM,
updatedByWorkspaceMemberId: null,
updatedByName: 'System',
},
{
id: NOTION_ID,
name: 'Notion',
domainNamePrimaryLinkUrl: 'https://notion.com',
addressAddressStreet1: '2300 Harrison St',
addressAddressStreet2: null,
addressAddressCity: 'San Francisco',
addressAddressState: 'CA',
addressAddressPostcode: '94110',
addressAddressCountry: 'United States',
employees: 400,
position: 5,
createdBySource: FieldActorSource.SYSTEM,
createdByWorkspaceMemberId: null,
createdByName: 'System',
updatedBySource: FieldActorSource.SYSTEM,
updatedByWorkspaceMemberId: null,
updatedByName: 'System',
},
])
.returning('*')
.execute();
};
@@ -0,0 +1,65 @@
import { FieldActorSource } from 'twenty-shared/types';
import { isDefined } from 'twenty-shared/utils';
import { type EntityManager } from 'typeorm';
import { type FlatEntityMaps } from 'src/engine/metadata-modules/flat-entity/types/flat-entity-maps.type';
import { findFlatEntityByUniversalIdentifier } from 'src/engine/metadata-modules/flat-entity/utils/find-flat-entity-by-universal-identifier.util';
import { type FlatPageLayout } from 'src/engine/metadata-modules/flat-page-layout/types/flat-page-layout.type';
import { STANDARD_PAGE_LAYOUTS } from 'src/engine/workspace-manager/twenty-standard-application/constants/standard-page-layout.constant';
export const MY_FIRST_DASHBOARD_ID = 'f31ecf3b-87d3-4e8a-a84b-b6f0f3f8c7e2';
export const prefillDashboards = async (
entityManager: EntityManager,
schemaName: string,
flatPageLayoutMaps: FlatEntityMaps<FlatPageLayout>,
) => {
const myFirstDashboardPageLayout = findFlatEntityByUniversalIdentifier({
flatEntityMaps: flatPageLayoutMaps,
universalIdentifier:
STANDARD_PAGE_LAYOUTS.myFirstDashboard.universalIdentifier,
});
if (!isDefined(myFirstDashboardPageLayout)) {
throw new Error(
`Page layout with universalIdentifier '${STANDARD_PAGE_LAYOUTS.myFirstDashboard.universalIdentifier}' not found`,
);
}
await entityManager
.createQueryBuilder()
.insert()
.into(`${schemaName}.dashboard`, [
'id',
'title',
'pageLayoutId',
'position',
'createdBySource',
'createdByWorkspaceMemberId',
'createdByName',
'createdByContext',
'updatedBySource',
'updatedByWorkspaceMemberId',
'updatedByName',
'updatedByContext',
])
.orIgnore()
.values([
{
id: MY_FIRST_DASHBOARD_ID,
title: 'My First Dashboard',
pageLayoutId: myFirstDashboardPageLayout.id,
position: 0,
createdBySource: FieldActorSource.SYSTEM,
createdByWorkspaceMemberId: null,
createdByName: 'System',
createdByContext: {},
updatedBySource: FieldActorSource.SYSTEM,
updatedByWorkspaceMemberId: null,
updatedByName: 'System',
updatedByContext: {},
},
])
.returning('*')
.execute();
};
@@ -0,0 +1,179 @@
import { FieldActorSource } from 'twenty-shared/types';
import { type EntityManager } from 'typeorm';
import {
AIRBNB_ID,
ANTHROPIC_ID,
FIGMA_ID,
NOTION_ID,
STRIPE_ID,
} from 'src/engine/workspace-manager/standard-objects-prefill-data/utils/prefill-companies.util';
import {
BRIAN_CHESKY_ID,
DARIO_AMODEI_ID,
DYLAN_FIELD_ID,
IVAN_ZHAO_ID,
PATRICK_COLLISON_ID,
} from 'src/engine/workspace-manager/standard-objects-prefill-data/utils/prefill-people.util';
export const OPPORTUNITY_STRIPE_PLATFORM_MIGRATION_ID =
'822639e5-9bf7-40f1-8882-a11140362339';
export const OPPORTUNITY_ANTHROPIC_AI_MODEL_ID =
'fc747edc-cb00-4078-8d6b-1fab2611dae4';
export const OPPORTUNITY_NOTION_WORKSPACE_ID =
'75de302f-1044-4957-8da4-1f67ebefd52b';
export const OPPORTUNITY_STRIPE_API_INTEGRATION_ID =
'2beb07b0-340c-41d7-be33-5aa91757f329';
export const OPPORTUNITY_AIRBNB_ENTERPRISE_ID =
'9543adcf-ec03-44e2-9233-3c2d3ebae98a';
export const OPPORTUNITY_FIGMA_DESIGN_ID =
'9457f8e9-16ae-43b9-92ee-cbd21f3dded5';
export const prefillOpportunities = async (
entityManager: EntityManager,
schemaName: string,
) => {
const workspaceMember = await entityManager
.createQueryBuilder()
.select('id')
.from(`${schemaName}.workspaceMember`, 'workspaceMember')
.limit(1)
.getRawOne();
const ownerId = workspaceMember?.id ?? null;
await entityManager
.createQueryBuilder()
.insert()
.into(`${schemaName}.opportunity`, [
'id',
'name',
'amountAmountMicros',
'amountCurrencyCode',
'closeDate',
'stage',
'position',
'companyId',
'pointOfContactId',
'ownerId',
'createdBySource',
'createdByWorkspaceMemberId',
'createdByName',
'updatedBySource',
'updatedByWorkspaceMemberId',
'updatedByName',
])
.orIgnore()
.values([
{
id: OPPORTUNITY_STRIPE_PLATFORM_MIGRATION_ID,
name: 'Platform Migration',
amountAmountMicros: 60000000000,
amountCurrencyCode: 'USD',
closeDate: new Date('2026-01-31T16:25:00.000Z'),
stage: 'PROPOSAL',
position: 1,
companyId: STRIPE_ID,
pointOfContactId: PATRICK_COLLISON_ID,
ownerId,
createdBySource: FieldActorSource.SYSTEM,
createdByWorkspaceMemberId: null,
createdByName: 'System',
updatedBySource: FieldActorSource.SYSTEM,
updatedByWorkspaceMemberId: null,
updatedByName: 'System',
},
{
id: OPPORTUNITY_ANTHROPIC_AI_MODEL_ID,
name: 'AI Model Training',
amountAmountMicros: 100000000000,
amountCurrencyCode: 'USD',
closeDate: new Date('2026-02-15T16:25:00.000Z'),
stage: 'CUSTOMER',
position: 2,
companyId: ANTHROPIC_ID,
pointOfContactId: DARIO_AMODEI_ID,
ownerId,
createdBySource: FieldActorSource.SYSTEM,
createdByWorkspaceMemberId: null,
createdByName: 'System',
updatedBySource: FieldActorSource.SYSTEM,
updatedByWorkspaceMemberId: null,
updatedByName: 'System',
},
{
id: OPPORTUNITY_NOTION_WORKSPACE_ID,
name: 'Workspace Expansion',
amountAmountMicros: 45000000000,
amountCurrencyCode: 'USD',
closeDate: new Date('2026-01-20T16:26:00.000Z'),
stage: 'MEETING',
position: 3,
companyId: NOTION_ID,
pointOfContactId: IVAN_ZHAO_ID,
ownerId,
createdBySource: FieldActorSource.SYSTEM,
createdByWorkspaceMemberId: null,
createdByName: 'System',
updatedBySource: FieldActorSource.SYSTEM,
updatedByWorkspaceMemberId: null,
updatedByName: 'System',
},
{
id: OPPORTUNITY_STRIPE_API_INTEGRATION_ID,
name: 'API Integration Deal',
amountAmountMicros: 75000000000,
amountCurrencyCode: 'USD',
closeDate: new Date('2026-01-25T16:26:00.000Z'),
stage: 'SCREENING',
position: 4,
companyId: STRIPE_ID,
pointOfContactId: PATRICK_COLLISON_ID,
ownerId,
createdBySource: FieldActorSource.SYSTEM,
createdByWorkspaceMemberId: null,
createdByName: 'System',
updatedBySource: FieldActorSource.SYSTEM,
updatedByWorkspaceMemberId: null,
updatedByName: 'System',
},
{
id: OPPORTUNITY_AIRBNB_ENTERPRISE_ID,
name: 'Enterprise Plan Upgrade',
amountAmountMicros: 50000000000,
amountCurrencyCode: 'USD',
closeDate: new Date('2026-03-10T16:26:00.000Z'),
stage: 'NEW',
position: 5,
companyId: AIRBNB_ID,
pointOfContactId: BRIAN_CHESKY_ID,
ownerId,
createdBySource: FieldActorSource.SYSTEM,
createdByWorkspaceMemberId: null,
createdByName: 'System',
updatedBySource: FieldActorSource.SYSTEM,
updatedByWorkspaceMemberId: null,
updatedByName: 'System',
},
{
id: OPPORTUNITY_FIGMA_DESIGN_ID,
name: 'Design Partnership',
amountAmountMicros: 30000000000,
amountCurrencyCode: 'USD',
closeDate: new Date('2026-01-15T16:27:00.000Z'),
stage: 'NEW',
position: 6,
companyId: FIGMA_ID,
pointOfContactId: DYLAN_FIELD_ID,
ownerId,
createdBySource: FieldActorSource.SYSTEM,
createdByWorkspaceMemberId: null,
createdByName: 'System',
updatedBySource: FieldActorSource.SYSTEM,
updatedByWorkspaceMemberId: null,
updatedByName: 'System',
},
])
.returning('*')
.execute();
};
@@ -0,0 +1,143 @@
import { FieldActorSource } from 'twenty-shared/types';
import { type EntityManager } from 'typeorm';
import {
AIRBNB_ID,
ANTHROPIC_ID,
FIGMA_ID,
NOTION_ID,
STRIPE_ID,
} from 'src/engine/workspace-manager/standard-objects-prefill-data/utils/prefill-companies.util';
export const BRIAN_CHESKY_ID = 'a2e78a5e-338b-46df-8811-fa08c7d19d35'; // Airbnb
export const DARIO_AMODEI_ID = '93c72d2e-e65c-44c4-99ad-f87f50349dcf'; // Anthropic
export const PATRICK_COLLISON_ID = 'edf6d445-13a7-4373-9a47-8f89e8c0a877'; // Stripe
export const DYLAN_FIELD_ID = 'b1e26fa6-c757-4c88-abfa-4b11f5cf3acf'; // Figma
export const IVAN_ZHAO_ID = '7a93d1e5-3f74-4945-8a65-d7f996083f72'; // Notion
export const prefillPeople = async (
entityManager: EntityManager,
schemaName: string,
) => {
await entityManager
.createQueryBuilder()
.insert()
.into(`${schemaName}.person`, [
'id',
'nameFirstName',
'nameLastName',
'city',
'emailsPrimaryEmail',
'avatarUrl',
'position',
'createdBySource',
'createdByWorkspaceMemberId',
'createdByName',
'updatedBySource',
'updatedByWorkspaceMemberId',
'updatedByName',
'phonesPrimaryPhoneNumber',
'phonesPrimaryPhoneCallingCode',
'companyId',
])
.orIgnore()
.values([
{
id: BRIAN_CHESKY_ID,
nameFirstName: 'Brian',
nameLastName: 'Chesky',
city: 'San Francisco',
emailsPrimaryEmail: 'chesky@airbnb.com',
avatarUrl:
'https://twentyhq.github.io/placeholder-images/founders/brian-chesky.jpg',
position: 1,
createdBySource: FieldActorSource.SYSTEM,
createdByWorkspaceMemberId: null,
createdByName: 'System',
updatedBySource: FieldActorSource.SYSTEM,
updatedByWorkspaceMemberId: null,
updatedByName: 'System',
phonesPrimaryPhoneNumber: '123456789',
phonesPrimaryPhoneCallingCode: '+1',
companyId: AIRBNB_ID,
},
{
id: DARIO_AMODEI_ID,
nameFirstName: 'Dario',
nameLastName: 'Amodei',
city: 'San Francisco',
emailsPrimaryEmail: 'amodei@anthropic.com',
avatarUrl:
'https://twentyhq.github.io/placeholder-images/founders/dario-amodei.jpg',
position: 2,
createdBySource: FieldActorSource.SYSTEM,
createdByWorkspaceMemberId: null,
createdByName: 'System',
updatedBySource: FieldActorSource.SYSTEM,
updatedByWorkspaceMemberId: null,
updatedByName: 'System',
phonesPrimaryPhoneNumber: '555123456',
phonesPrimaryPhoneCallingCode: '+1',
companyId: ANTHROPIC_ID,
},
{
id: PATRICK_COLLISON_ID,
nameFirstName: 'Patrick',
nameLastName: 'Collison',
city: 'San Francisco',
emailsPrimaryEmail: 'collison@stripe.com',
avatarUrl:
'https://twentyhq.github.io/placeholder-images/founders/patrick-collison.jpg',
position: 3,
createdBySource: FieldActorSource.SYSTEM,
createdByWorkspaceMemberId: null,
createdByName: 'System',
updatedBySource: FieldActorSource.SYSTEM,
updatedByWorkspaceMemberId: null,
updatedByName: 'System',
phonesPrimaryPhoneNumber: '987625341',
phonesPrimaryPhoneCallingCode: '+1',
companyId: STRIPE_ID,
},
{
id: DYLAN_FIELD_ID,
nameFirstName: 'Dylan',
nameLastName: 'Field',
city: 'San Francisco',
emailsPrimaryEmail: 'field@figma.com',
avatarUrl:
'https://twentyhq.github.io/placeholder-images/founders/dylan-field.jpg',
position: 4,
createdBySource: FieldActorSource.SYSTEM,
createdByWorkspaceMemberId: null,
createdByName: 'System',
updatedBySource: FieldActorSource.SYSTEM,
updatedByWorkspaceMemberId: null,
updatedByName: 'System',
phonesPrimaryPhoneNumber: '098822619',
phonesPrimaryPhoneCallingCode: '+1',
companyId: FIGMA_ID,
},
{
id: IVAN_ZHAO_ID,
nameFirstName: 'Ivan',
nameLastName: 'Zhao',
city: 'San Francisco',
emailsPrimaryEmail: 'zhao@notion.com',
avatarUrl:
'https://twentyhq.github.io/placeholder-images/founders/ivan-zhao.jpg',
position: 5,
createdBySource: FieldActorSource.SYSTEM,
createdByWorkspaceMemberId: null,
createdByName: 'System',
updatedBySource: FieldActorSource.SYSTEM,
updatedByWorkspaceMemberId: null,
updatedByName: 'System',
phonesPrimaryPhoneNumber: '882261739',
phonesPrimaryPhoneCallingCode: '+1',
companyId: NOTION_ID,
},
])
.returning('*')
.execute();
};
@@ -0,0 +1,336 @@
import { v5 as uuidv5 } from 'uuid';
const CREATE_COMPANY_WHEN_ADDING_NEW_PERSON_LOGIC_FUNCTION_ID_NAMESPACE =
'd41b0a2d-aa97-44dc-ad5d-89774164af27';
export const getCreateCompanyWhenAddingNewPersonCodeStepLogicFunctionIds = (
workspaceId: string,
) => ({
extractDomainLogicFunctionId: uuidv5(
`${workspaceId}:create-company-when-adding-new-person:extract-domain`,
CREATE_COMPANY_WHEN_ADDING_NEW_PERSON_LOGIC_FUNCTION_ID_NAMESPACE,
),
findMatchingCompanyByDomainLogicFunctionId: uuidv5(
`${workspaceId}:create-company-when-adding-new-person:find-matching-company-by-domain`,
CREATE_COMPANY_WHEN_ADDING_NEW_PERSON_LOGIC_FUNCTION_ID_NAMESPACE,
),
isPersonalEmailLogicFunctionId: uuidv5(
`${workspaceId}:create-company-when-adding-new-person:is-personal-email`,
CREATE_COMPANY_WHEN_ADDING_NEW_PERSON_LOGIC_FUNCTION_ID_NAMESPACE,
),
});
const EXTRACT_DOMAIN_TOOL_INPUT_SCHEMA = {
type: 'object',
properties: {
email: {
type: 'string',
},
},
required: ['email'],
};
const IS_PERSONAL_EMAIL_TOOL_INPUT_SCHEMA = {
type: 'object',
properties: {
primaryEmail: {
type: 'string',
},
},
required: ['primaryEmail'],
};
const FIND_MATCHING_COMPANY_BY_DOMAIN_TOOL_INPUT_SCHEMA = {
type: 'object',
properties: {
companies: {
type: 'array',
items: {
type: 'object',
},
},
domain: {
type: 'string',
},
},
required: ['companies', 'domain'],
};
const EXTRACT_DOMAIN_LOGIC_FUNCTION_SOURCE = `const MULTI_PART_SUFFIXES = new Set([
'ac.uk',
'co.in',
'co.jp',
'co.kr',
'co.nz',
'co.uk',
'co.za',
'com.ar',
'com.au',
'com.br',
'com.cn',
'com.hk',
'com.mx',
'com.sg',
'com.tr',
'com.tw',
'com.ua',
'gov.uk',
'ne.jp',
'net.au',
'org.au',
'org.uk',
]);
const getRegistrableDomain = (host) => {
const normalizedHost = host.toLowerCase().replace(/^www\\./, '');
const parts = normalizedHost.split('.').filter(Boolean);
if (parts.length <= 2) {
return normalizedHost;
}
const lastTwo = parts.slice(-2).join('.');
if (MULTI_PART_SUFFIXES.has(lastTwo) && parts.length >= 3) {
return parts.slice(-3).join('.');
}
return lastTwo;
};
export const main = async (params) => {
const email =
typeof params?.email === 'string' ? params.email.trim().toLowerCase() : '';
const domainFromEmail = email.split('@')[1] ?? '';
const domain = domainFromEmail ? getRegistrableDomain(domainFromEmail) : '';
return {
url: domain ? \`https://\${domain}\` : '',
domain,
};
};`;
const IS_PERSONAL_EMAIL_LOGIC_FUNCTION_SOURCE = `const MULTI_PART_SUFFIXES = new Set([
'ac.uk',
'co.in',
'co.jp',
'co.kr',
'co.nz',
'co.uk',
'co.za',
'com.ar',
'com.au',
'com.br',
'com.cn',
'com.hk',
'com.mx',
'com.sg',
'com.tr',
'com.tw',
'com.ua',
'gov.uk',
'ne.jp',
'net.au',
'org.au',
'org.uk',
]);
const PERSONAL_EMAIL_DOMAINS = new Set([
'aol.com',
'att.net',
'btinternet.com',
'comcast.net',
'fastmail.com',
'free.fr',
'gmx.com',
'gmx.de',
'googlemail.com',
'hotmail.com',
'hotmail.fr',
'icloud.com',
'laposte.net',
'live.com',
'mac.com',
'mail.com',
'me.com',
'msn.com',
'orange.fr',
'outlook.com',
'outlook.fr',
'pm.me',
'proton.me',
'protonmail.com',
'sfr.fr',
'verizon.net',
'wanadoo.fr',
'yahoo.co.jp',
'yahoo.co.uk',
'yahoo.com',
'yandex.com',
'yandex.ru',
'zoho.com',
'gmail.com',
]);
const getRegistrableDomain = (host) => {
const normalizedHost = host.toLowerCase().replace(/^www\\./, '');
const parts = normalizedHost.split('.').filter(Boolean);
if (parts.length <= 2) {
return normalizedHost;
}
const lastTwo = parts.slice(-2).join('.');
if (MULTI_PART_SUFFIXES.has(lastTwo) && parts.length >= 3) {
return parts.slice(-3).join('.');
}
return lastTwo;
};
export const main = async (params) => {
const email =
typeof params?.primaryEmail === 'string'
? params.primaryEmail.trim().toLowerCase()
: '';
if (!email || !email.includes('@')) {
return { isPersonal: true };
}
const host = email.split('@')[1] ?? '';
const registrableDomain = host ? getRegistrableDomain(host) : '';
return {
isPersonal:
!registrableDomain ||
PERSONAL_EMAIL_DOMAINS.has(host) ||
PERSONAL_EMAIL_DOMAINS.has(registrableDomain),
};
};`;
const FIND_MATCHING_COMPANY_BY_DOMAIN_LOGIC_FUNCTION_SOURCE = `const MULTI_PART_SUFFIXES = new Set([
'ac.uk',
'co.in',
'co.jp',
'co.kr',
'co.nz',
'co.uk',
'co.za',
'com.ar',
'com.au',
'com.br',
'com.cn',
'com.hk',
'com.mx',
'com.sg',
'com.tr',
'com.tw',
'com.ua',
'gov.uk',
'ne.jp',
'net.au',
'org.au',
'org.uk',
]);
const normalizeHost = (value) => {
if (typeof value !== 'string') {
return '';
}
return value
.trim()
.toLowerCase()
.replace(/^https?:\\/\\//, '')
.replace(/^www\\./, '')
.split('/')[0]
.split('?')[0]
.split('#')[0]
.split(':')[0];
};
const getRegistrableDomain = (value) => {
const normalizedHost = normalizeHost(value);
const parts = normalizedHost.split('.').filter(Boolean);
if (parts.length <= 2) {
return normalizedHost;
}
const lastTwo = parts.slice(-2).join('.');
if (MULTI_PART_SUFFIXES.has(lastTwo) && parts.length >= 3) {
return parts.slice(-3).join('.');
}
return lastTwo;
};
export const main = async (params) => {
const domain = getRegistrableDomain(params?.domain);
const companies = Array.isArray(params?.companies) ? params.companies : [];
const matchingCompany = companies.find((company) => {
const companyDomain = getRegistrableDomain(
company?.domainName?.primaryLinkUrl,
);
return companyDomain !== '' && companyDomain === domain;
});
const companyId =
typeof matchingCompany?.id === 'string' ? matchingCompany.id : '';
return {
companyId,
hasMatch: companyId !== '',
};
};`;
export type PrefilledWorkflowCodeStepLogicFunctionDefinition = {
id: string;
name: string;
description: string;
sourceHandlerCode: string;
toolInputSchema: object;
};
export const getCreateCompanyWhenAddingNewPersonCodeStepLogicFunctionDefinitions =
(workspaceId: string): PrefilledWorkflowCodeStepLogicFunctionDefinition[] => {
const {
extractDomainLogicFunctionId,
findMatchingCompanyByDomainLogicFunctionId,
isPersonalEmailLogicFunctionId,
} =
getCreateCompanyWhenAddingNewPersonCodeStepLogicFunctionIds(workspaceId);
return [
{
id: extractDomainLogicFunctionId,
name: 'Extract domain from email',
description:
'Extracts a normalized company domain and URL from a person email address.',
sourceHandlerCode: EXTRACT_DOMAIN_LOGIC_FUNCTION_SOURCE,
toolInputSchema: EXTRACT_DOMAIN_TOOL_INPUT_SCHEMA,
},
{
id: findMatchingCompanyByDomainLogicFunctionId,
name: 'Find matching company by domain',
description:
'Finds an existing company whose website matches a normalized registrable domain.',
sourceHandlerCode:
FIND_MATCHING_COMPANY_BY_DOMAIN_LOGIC_FUNCTION_SOURCE,
toolInputSchema: FIND_MATCHING_COMPANY_BY_DOMAIN_TOOL_INPUT_SCHEMA,
},
{
id: isPersonalEmailLogicFunctionId,
name: 'Is this a personal email?',
description:
'Detects whether an email address belongs to a common personal email provider.',
sourceHandlerCode: IS_PERSONAL_EMAIL_LOGIC_FUNCTION_SOURCE,
toolInputSchema: IS_PERSONAL_EMAIL_TOOL_INPUT_SCHEMA,
},
];
};
@@ -0,0 +1,70 @@
import { type EntityManager } from 'typeorm';
import { CommandMenuItemAvailabilityType } from 'src/engine/metadata-modules/command-menu-item/enums/command-menu-item-availability-type.enum';
import { EngineComponentKey } from 'src/engine/metadata-modules/command-menu-item/enums/engine-component-key.enum';
import { QUICK_LEAD_WORKFLOW_VERSION_ID } from 'src/engine/workspace-manager/standard-objects-prefill-data/utils/prefill-workflows.util';
import { TWENTY_STANDARD_APPLICATION } from 'src/engine/workspace-manager/twenty-standard-application/constants/twenty-standard-applications';
const QUICK_LEAD_COMMAND_MENU_ITEM_UNIVERSAL_IDENTIFIER =
'a1b2c3d4-e5f6-7890-abcd-1234567890ab';
export const prefillWorkflowCommandMenuItems = async (
entityManager: EntityManager,
workspaceId: string,
) => {
const applicationRow = await entityManager
.createQueryBuilder()
.select('app.id')
.from('core.application', 'app')
.where('app.universalIdentifier = :universalIdentifier', {
universalIdentifier: TWENTY_STANDARD_APPLICATION.universalIdentifier,
})
.andWhere('app.workspaceId = :workspaceId', { workspaceId })
.getRawOne();
if (!applicationRow) {
return;
}
await entityManager
.createQueryBuilder()
.insert()
.into('core.commandMenuItem', [
'workspaceId',
'universalIdentifier',
'applicationId',
'workflowVersionId',
'frontComponentId',
'engineComponentKey',
'label',
'icon',
'shortLabel',
'position',
'isPinned',
'availabilityType',
'conditionalAvailabilityExpression',
'availabilityObjectMetadataId',
'hotKeys',
])
.values([
{
workspaceId,
universalIdentifier: QUICK_LEAD_COMMAND_MENU_ITEM_UNIVERSAL_IDENTIFIER,
applicationId: applicationRow.app_id,
workflowVersionId: QUICK_LEAD_WORKFLOW_VERSION_ID,
frontComponentId: null,
engineComponentKey: EngineComponentKey.TRIGGER_WORKFLOW_VERSION,
label: 'Quick Lead',
icon: 'IconUserPlus',
shortLabel: 'Quick Lead',
position: 100,
isPinned: false,
availabilityType: CommandMenuItemAvailabilityType.GLOBAL,
conditionalAvailabilityExpression: null,
availabilityObjectMetadataId: null,
hotKeys: null,
},
])
.orIgnore()
.execute();
};
@@ -0,0 +1,762 @@
import { FieldActorSource } from 'twenty-shared/types';
import { isDefined } from 'twenty-shared/utils';
import { type EntityManager } from 'typeorm';
import { DatabaseEventAction } from 'src/engine/api/graphql/graphql-query-runner/enums/database-event-action';
import { type FlatEntityMaps } from 'src/engine/metadata-modules/flat-entity/types/flat-entity-maps.type';
import { findFlatEntityByIdInFlatEntityMaps } from 'src/engine/metadata-modules/flat-entity/utils/find-flat-entity-by-id-in-flat-entity-maps.util';
import { type FlatFieldMetadata } from 'src/engine/metadata-modules/flat-field-metadata/types/flat-field-metadata.type';
import { type FlatObjectMetadata } from 'src/engine/metadata-modules/flat-object-metadata/types/flat-object-metadata.type';
import { buildObjectIdByNameMaps } from 'src/engine/metadata-modules/flat-object-metadata/utils/build-object-id-by-name-maps.util';
import { generateFakeObjectRecordEvent } from 'src/modules/workflow/workflow-builder/workflow-schema/utils/generate-fake-object-record-event';
import { generateObjectRecordFields } from 'src/modules/workflow/workflow-builder/workflow-schema/utils/generate-object-record-fields';
import { getCreateCompanyWhenAddingNewPersonCodeStepLogicFunctionIds } from 'src/engine/workspace-manager/standard-objects-prefill-data/utils/prefill-workflow-code-step-logic-functions.util';
export const QUICK_LEAD_WORKFLOW_ID = '8b213cac-a68b-4ffe-817a-3ec994e9932d';
export const QUICK_LEAD_WORKFLOW_VERSION_ID =
'ac67974f-c524-4288-9d88-af8515400b68';
export const CREATE_COMPANY_WHEN_ADDING_NEW_PERSON_WORKFLOW_ID =
'887c6c06-fbc5-4b45-8d6b-f7b6b0f40b12';
export const CREATE_COMPANY_WHEN_ADDING_NEW_PERSON_WORKFLOW_VERSION_ID =
'0f276d7e-a950-41ab-ad98-35e80753dc58';
export const CREATE_COMPANY_WHEN_ADDING_NEW_PERSON_AUTOMATED_TRIGGER_ID =
'c54f5990-13a3-4c3b-b75d-df09e7843036';
export const prefillWorkflows = async (
entityManager: EntityManager,
workspaceId: string,
schemaName: string,
flatObjectMetadataMaps: FlatEntityMaps<FlatObjectMetadata>,
flatFieldMetadataMaps: FlatEntityMaps<FlatFieldMetadata>,
) => {
const {
extractDomainLogicFunctionId,
findMatchingCompanyByDomainLogicFunctionId,
isPersonalEmailLogicFunctionId,
} = getCreateCompanyWhenAddingNewPersonCodeStepLogicFunctionIds(workspaceId);
const { idByNameSingular: objectIdByNameSingular } = buildObjectIdByNameMaps(
flatObjectMetadataMaps,
);
const companyObjectMetadataId = objectIdByNameSingular['company'];
const personObjectMetadataId = objectIdByNameSingular['person'];
if (
!isDefined(companyObjectMetadataId) ||
!isDefined(personObjectMetadataId)
) {
throw new Error('Company or person object metadata not found');
}
const companyObjectMetadata = findFlatEntityByIdInFlatEntityMaps({
flatEntityId: companyObjectMetadataId,
flatEntityMaps: flatObjectMetadataMaps,
});
const personObjectMetadata = findFlatEntityByIdInFlatEntityMaps({
flatEntityId: personObjectMetadataId,
flatEntityMaps: flatObjectMetadataMaps,
});
if (!isDefined(companyObjectMetadata) || !isDefined(personObjectMetadata)) {
throw new Error('Company or person object metadata not found');
}
const companyDomainNameFieldMetadata = Object.values(
flatFieldMetadataMaps.byUniversalIdentifier,
).find(
(fieldMetadata) =>
fieldMetadata?.objectMetadataId === companyObjectMetadataId &&
fieldMetadata?.name === 'domainName',
);
if (!isDefined(companyDomainNameFieldMetadata)) {
throw new Error('Company domainName field metadata not found');
}
await entityManager
.createQueryBuilder()
.insert()
.into(`${schemaName}.workflow`, [
'id',
'name',
'lastPublishedVersionId',
'statuses',
'position',
'createdBySource',
'createdByWorkspaceMemberId',
'createdByName',
'createdByContext',
'updatedBySource',
'updatedByWorkspaceMemberId',
'updatedByName',
])
.orIgnore()
.values([
{
id: QUICK_LEAD_WORKFLOW_ID,
name: 'Quick Lead',
lastPublishedVersionId: QUICK_LEAD_WORKFLOW_VERSION_ID,
statuses: ['ACTIVE'],
position: 1,
createdBySource: FieldActorSource.SYSTEM,
createdByWorkspaceMemberId: null,
createdByName: 'System',
createdByContext: {},
updatedBySource: FieldActorSource.SYSTEM,
updatedByWorkspaceMemberId: null,
updatedByName: 'System',
},
{
id: CREATE_COMPANY_WHEN_ADDING_NEW_PERSON_WORKFLOW_ID,
name: 'Create company when adding a new person',
lastPublishedVersionId:
CREATE_COMPANY_WHEN_ADDING_NEW_PERSON_WORKFLOW_VERSION_ID,
statuses: ['ACTIVE'],
position: 2,
createdBySource: FieldActorSource.SYSTEM,
createdByWorkspaceMemberId: null,
createdByName: 'System',
createdByContext: {},
updatedBySource: FieldActorSource.SYSTEM,
updatedByWorkspaceMemberId: null,
updatedByName: 'System',
},
])
.returning('*')
.execute();
await entityManager
.createQueryBuilder()
.insert()
.into(`${schemaName}.workflowVersion`, [
'id',
'name',
'trigger',
'steps',
'status',
'position',
'workflowId',
])
.orIgnore()
.values([
{
id: QUICK_LEAD_WORKFLOW_VERSION_ID,
name: 'v1',
trigger: JSON.stringify({
name: 'Launch manually',
type: 'MANUAL',
settings: {
outputSchema: {},
icon: 'IconUserPlus',
availability: { type: 'GLOBAL', locations: undefined },
},
nextStepIds: ['6e089bc9-aabd-435f-865f-f31c01c8f4a7'],
}),
steps: JSON.stringify([
{
id: '6e089bc9-aabd-435f-865f-f31c01c8f4a7',
name: 'Quick Lead Form',
type: 'FORM',
valid: false,
settings: {
input: [
{
id: '14d669f0-5249-4fa4-b0bb-f8bd408328d5',
name: 'firstName',
type: 'TEXT',
label: 'First name',
placeholder: 'Tim',
},
{
id: '4eb6ce85-d231-4aef-9837-744490c026d0',
name: 'lastName',
type: 'TEXT',
label: 'Last Name',
placeholder: 'Apple',
},
{
id: 'adbf0e9f-1427-49be-b4fb-092b34d97350',
name: 'email',
type: 'TEXT',
label: 'Email',
placeholder: 'timapple@apple.com',
},
{
id: '4ffc7992-9e65-4a4d-9baf-b52e62f2c273',
name: 'jobTitle',
type: 'TEXT',
label: 'Job title',
placeholder: 'CEO',
},
{
id: '42f11926-04ea-4924-94a4-2293cc748362',
name: 'companyName',
type: 'TEXT',
label: 'Company name',
placeholder: 'Apple',
},
{
id: 'd6ca80ee-26cd-466d-91bf-984d7205451c',
name: 'companyDomain',
type: 'TEXT',
label: 'Company domain',
placeholder: 'https://www.apple.com',
},
],
outputSchema: {
email: {
type: 'TEXT',
label: 'Email',
value: 'My text',
isLeaf: true,
},
jobTitle: {
type: 'TEXT',
label: 'Job title',
value: 'My text',
isLeaf: true,
},
lastName: {
type: 'TEXT',
label: 'Last Name',
value: 'My text',
isLeaf: true,
},
firstName: {
type: 'TEXT',
label: 'First name',
value: 'My text',
isLeaf: true,
},
companyName: {
type: 'TEXT',
label: 'Company name',
value: 'My text',
isLeaf: true,
},
companyDomain: {
type: 'TEXT',
label: 'Company domain',
value: 'My text',
isLeaf: true,
},
},
errorHandlingOptions: {
retryOnFailure: { value: false },
continueOnFailure: { value: false },
},
},
__typename: 'WorkflowAction',
nextStepIds: ['0715b6cd-7cc1-4b98-971b-00f54dfe643b'],
},
{
id: '0715b6cd-7cc1-4b98-971b-00f54dfe643b',
name: 'Create Company',
type: 'CREATE_RECORD',
valid: false,
settings: {
input: {
objectName: 'company',
objectRecord: {
name: '{{6e089bc9-aabd-435f-865f-f31c01c8f4a7.companyName}}',
domainName: {
primaryLinkUrl:
'{{6e089bc9-aabd-435f-865f-f31c01c8f4a7.companyDomain}}',
primaryLinkLabel: '',
},
},
},
outputSchema: {
object: {
icon: 'IconBuildingSkyscraper',
label: 'Company',
value: 'A company',
isLeaf: true,
fieldIdName: 'id',
nameSingular: 'company',
},
_outputSchemaType: 'RECORD',
fields: generateObjectRecordFields({
objectMetadataInfo: {
flatObjectMetadata: companyObjectMetadata,
flatObjectMetadataMaps,
flatFieldMetadataMaps,
},
}),
},
errorHandlingOptions: {
retryOnFailure: { value: false },
continueOnFailure: { value: false },
},
},
__typename: 'WorkflowAction',
nextStepIds: ['6f553ea7-b00e-4371-9d88-d8298568a246'],
},
{
id: '6f553ea7-b00e-4371-9d88-d8298568a246',
name: 'Create Person',
type: 'CREATE_RECORD',
valid: false,
settings: {
input: {
objectName: 'person',
objectRecord: {
name: {
lastName:
'{{6e089bc9-aabd-435f-865f-f31c01c8f4a7.lastName}}',
firstName:
'{{6e089bc9-aabd-435f-865f-f31c01c8f4a7.firstName}}',
},
emails: {
primaryEmail:
'{{6e089bc9-aabd-435f-865f-f31c01c8f4a7.email}}',
additionalEmails: [],
},
companyId: '{{0715b6cd-7cc1-4b98-971b-00f54dfe643b.id}}',
},
},
outputSchema: {
fields: generateObjectRecordFields({
objectMetadataInfo: {
flatObjectMetadata: personObjectMetadata,
flatObjectMetadataMaps,
flatFieldMetadataMaps,
},
}),
},
errorHandlingOptions: {
retryOnFailure: { value: false },
continueOnFailure: { value: false },
},
},
__typename: 'WorkflowAction',
nextStepIds: null,
},
]),
status: 'ACTIVE',
position: 1,
workflowId: QUICK_LEAD_WORKFLOW_ID,
},
{
id: CREATE_COMPANY_WHEN_ADDING_NEW_PERSON_WORKFLOW_VERSION_ID,
name: 'v1',
trigger: JSON.stringify({
name: 'Record is created or updated',
type: 'DATABASE_EVENT',
settings: {
eventName: 'person.upserted',
fields: ['emails'],
outputSchema: generateFakeObjectRecordEvent(
{
flatObjectMetadata: personObjectMetadata,
flatObjectMetadataMaps,
flatFieldMetadataMaps,
},
DatabaseEventAction.UPSERTED,
),
},
nextStepIds: ['c30d7cbe-00e0-4966-bc1a-99b0a11a2cca'],
}),
steps: JSON.stringify([
{
id: 'c30d7cbe-00e0-4966-bc1a-99b0a11a2cca',
name: 'Is this a personal email?',
type: 'CODE',
valid: false,
position: {
x: 227.25,
y: 130,
},
settings: {
input: {
logicFunctionId: isPersonalEmailLogicFunctionId,
logicFunctionInput: {
primaryEmail:
'{{trigger.properties.after.emails.primaryEmail}}',
},
},
outputSchema: {
isPersonal: {
type: 'boolean',
label: 'isPersonal',
value: true,
isLeaf: true,
},
},
errorHandlingOptions: {
retryOnFailure: {
value: false,
},
continueOnFailure: {
value: false,
},
},
},
__typename: 'WorkflowAction',
nextStepIds: ['01f3db05-aae5-4e4b-b361-96684f09c704'],
},
{
id: '01f3db05-aae5-4e4b-b361-96684f09c704',
name: 'If business email',
type: 'FILTER',
valid: false,
position: {
x: 249.25,
y: 260,
},
settings: {
input: {
stepFilters: [
{
id: '0e595385-9e18-4869-abfd-cf72952b124c',
type: 'boolean',
value: 'false',
operand: 'IS',
isFullRecord: false,
stepOutputKey:
'{{c30d7cbe-00e0-4966-bc1a-99b0a11a2cca.isPersonal}}',
stepFilterGroupId: '5204d5f5-7b23-428c-9f84-c37971d497d3',
positionInStepFilterGroup: 0,
},
],
stepFilterGroups: [
{
id: '5204d5f5-7b23-428c-9f84-c37971d497d3',
logicalOperator: 'AND',
},
],
},
outputSchema: {},
errorHandlingOptions: {
retryOnFailure: {
value: false,
},
continueOnFailure: {
value: false,
},
},
},
__typename: 'WorkflowAction',
nextStepIds: ['1b01193b-8300-4d79-940b-44464bf45505'],
},
{
id: '1b01193b-8300-4d79-940b-44464bf45505',
name: 'Extract domain from email',
type: 'CODE',
valid: false,
position: {
x: 219.75,
y: 390,
},
settings: {
input: {
logicFunctionId: extractDomainLogicFunctionId,
logicFunctionInput: {
email: '{{trigger.properties.after.emails.primaryEmail}}',
},
},
outputSchema: {
url: {
icon: 'IconVariable',
type: 'string',
label: 'url',
value: 'https://twenty.com',
isLeaf: true,
},
domain: {
icon: 'IconVariable',
type: 'string',
label: 'domain',
value: 'twenty.com',
isLeaf: true,
},
},
errorHandlingOptions: {
retryOnFailure: {
value: false,
},
continueOnFailure: {
value: false,
},
},
},
__typename: 'WorkflowAction',
nextStepIds: ['becb3acf-79bb-4672-8a42-3696e94957b5'],
},
{
id: 'becb3acf-79bb-4672-8a42-3696e94957b5',
name: 'Search Company',
type: 'FIND_RECORDS',
valid: false,
position: {
x: 247.75,
y: 520,
},
settings: {
input: {
limit: 25,
filter: {
recordFilters: [
{
id: 'a9b917a0-5c4c-4e8f-bf91-160d0b888693',
type: 'LINKS',
label: 'Domain Name',
value: '{{1b01193b-8300-4d79-940b-44464bf45505.domain}}',
operand: 'CONTAINS',
displayValue:
'{{1b01193b-8300-4d79-940b-44464bf45505.domain}}',
subFieldName: 'primaryLinkUrl',
fieldMetadataId: companyDomainNameFieldMetadata.id,
recordFilterGroupId:
'194e151c-cf46-4e8f-a48b-649c36082dfa',
},
],
recordFilterGroups: [
{
id: '194e151c-cf46-4e8f-a48b-649c36082dfa',
logicalOperator: 'AND',
},
],
},
objectName: 'company',
},
outputSchema: {},
errorHandlingOptions: {
retryOnFailure: {
value: false,
},
continueOnFailure: {
value: false,
},
},
},
__typename: 'WorkflowAction',
nextStepIds: ['9d0b6ef2-aad2-4853-92e1-95f2abf10d5b'],
},
{
id: '9d0b6ef2-aad2-4853-92e1-95f2abf10d5b',
name: 'Find exact company match',
type: 'CODE',
valid: false,
position: {
x: 247.75,
y: 650,
},
settings: {
input: {
logicFunctionId: findMatchingCompanyByDomainLogicFunctionId,
logicFunctionInput: {
companies: '{{becb3acf-79bb-4672-8a42-3696e94957b5.all}}',
domain: '{{1b01193b-8300-4d79-940b-44464bf45505.domain}}',
},
},
outputSchema: {
companyId: {
type: 'string',
label: 'companyId',
value: '00000000-0000-0000-0000-000000000000',
isLeaf: true,
},
hasMatch: {
type: 'boolean',
label: 'hasMatch',
value: true,
isLeaf: true,
},
},
errorHandlingOptions: {
retryOnFailure: {
value: false,
},
continueOnFailure: {
value: false,
},
},
},
__typename: 'WorkflowAction',
nextStepIds: ['0c99a900-656a-40e8-977e-5a7357be33b9'],
},
{
id: '0c99a900-656a-40e8-977e-5a7357be33b9',
name: 'If a company already exists',
type: 'IF_ELSE',
valid: false,
position: {
x: 216.25,
y: 780,
},
settings: {
input: {
branches: [
{
id: '1344c151-15ff-40e2-a1a3-925fabaf5b1c',
nextStepIds: ['ffdd4271-75d4-4805-b1f8-2167a113c3b2'],
filterGroupId: 'f5c41047-2a6e-49fb-968a-fa7789a90ee5',
},
{
id: 'fe6dd152-1103-4324-af51-3ff994d1f8a7',
nextStepIds: ['ddafb9db-a94f-40b9-a5c9-becce857edf7'],
},
],
stepFilters: [
{
id: '290cc6a3-08fd-4be5-b42e-966d0bb90ff7',
type: 'boolean',
value: 'true',
operand: 'IS',
isFullRecord: false,
stepOutputKey:
'{{9d0b6ef2-aad2-4853-92e1-95f2abf10d5b.hasMatch}}',
stepFilterGroupId: 'f5c41047-2a6e-49fb-968a-fa7789a90ee5',
positionInStepFilterGroup: 0,
},
],
stepFilterGroups: [
{
id: 'f5c41047-2a6e-49fb-968a-fa7789a90ee5',
logicalOperator: 'AND',
},
],
},
outputSchema: {},
errorHandlingOptions: {
retryOnFailure: {
value: false,
},
continueOnFailure: {
value: false,
},
},
},
__typename: 'WorkflowAction',
},
{
id: 'ffdd4271-75d4-4805-b1f8-2167a113c3b2',
name: 'Attach person to existing company',
type: 'UPDATE_RECORD',
valid: false,
position: {
x: 0,
y: 910,
},
settings: {
input: {
objectName: 'person',
objectRecord: {
companyId:
'{{9d0b6ef2-aad2-4853-92e1-95f2abf10d5b.companyId}}',
},
fieldsToUpdate: ['companyId'],
objectRecordId: '{{trigger.properties.after.id}}',
},
outputSchema: {},
errorHandlingOptions: {
retryOnFailure: {
value: false,
},
continueOnFailure: {
value: false,
},
},
},
__typename: 'WorkflowAction',
},
{
id: 'ddafb9db-a94f-40b9-a5c9-becce857edf7',
name: 'Create a new company',
type: 'CREATE_RECORD',
valid: false,
position: {
x: 440,
y: 910,
},
settings: {
input: {
objectName: 'company',
objectRecord: {
name: '{{1b01193b-8300-4d79-940b-44464bf45505.domain}}',
domainName: {
primaryLinkUrl:
'{{1b01193b-8300-4d79-940b-44464bf45505.url}}',
primaryLinkLabel:
'{{1b01193b-8300-4d79-940b-44464bf45505.domain}}',
},
},
},
outputSchema: {},
errorHandlingOptions: {
retryOnFailure: {
value: false,
},
continueOnFailure: {
value: false,
},
},
},
__typename: 'WorkflowAction',
nextStepIds: ['d5d5d6e1-391f-4142-83c1-670f7087f079'],
},
{
id: 'd5d5d6e1-391f-4142-83c1-670f7087f079',
name: 'Attach person to this company',
type: 'UPDATE_RECORD',
valid: false,
position: {
x: 420.5,
y: 1040,
},
settings: {
input: {
objectName: 'person',
objectRecord: {
companyId: '{{ddafb9db-a94f-40b9-a5c9-becce857edf7.id}}',
},
fieldsToUpdate: ['companyId'],
objectRecordId: '{{trigger.properties.after.id}}',
},
outputSchema: {},
errorHandlingOptions: {
retryOnFailure: {
value: false,
},
continueOnFailure: {
value: false,
},
},
},
__typename: 'WorkflowAction',
},
]),
status: 'ACTIVE',
position: 2,
workflowId: CREATE_COMPANY_WHEN_ADDING_NEW_PERSON_WORKFLOW_ID,
},
])
.returning('*')
.execute();
await entityManager
.createQueryBuilder()
.insert()
.into(`${schemaName}.workflowAutomatedTrigger`, [
'id',
'workflowId',
'type',
'settings',
])
.orIgnore()
.values([
{
id: CREATE_COMPANY_WHEN_ADDING_NEW_PERSON_AUTOMATED_TRIGGER_ID,
workflowId: CREATE_COMPANY_WHEN_ADDING_NEW_PERSON_WORKFLOW_ID,
type: 'DATABASE_EVENT',
settings: {
eventName: 'person.upserted',
fields: ['emails'],
},
},
])
.execute();
};