7203 support emails links phones in zapier inputs 2 (#7562)

## Done
- add `EMAILS`, `PHONES`, `LINKS`, `RICH_TEXT`, `POSITION`, and `ARRAY`
field support in Twenty zapier integration
- fix `twenty-zapier` package tests and requirements

## Emails
<img width="791" alt="image"
src="https://github.com/user-attachments/assets/7987a1a2-6076-4715-9221-d4a1898b7634">

## Links
<img width="797" alt="image"
src="https://github.com/user-attachments/assets/b94ce972-fae2-4953-b9e8-79c0478f5f60">

## Phones
<img width="789" alt="image"
src="https://github.com/user-attachments/assets/7234eaaf-40b8-4772-8880-c58ba47618c5">

## Array
<img width="834" alt="image"
src="https://github.com/user-attachments/assets/99cb6795-e428-40ea-9c3a-d52561c2c6e1">
This commit is contained in:
martmull
2024-10-10 15:32:06 +02:00
committed by GitHub
parent f4bc0c687e
commit 29bd74feea
9 changed files with 221 additions and 30 deletions

View File

@ -5,15 +5,21 @@ import {
NodeField,
} from '../utils/data.types';
const getListFromFieldMetadataType = (fieldMetadataType: FieldMetadataType) => {
return fieldMetadataType === FieldMetadataType.ARRAY;
};
const getTypeFromFieldMetadataType = (
fieldMetadataType: string,
fieldMetadataType: FieldMetadataType,
): string | undefined => {
switch (fieldMetadataType) {
case FieldMetadataType.UUID:
case FieldMetadataType.TEXT:
case FieldMetadataType.RICH_TEXT:
case FieldMetadataType.PHONE:
case FieldMetadataType.EMAIL:
case FieldMetadataType.LINK:
case FieldMetadataType.ARRAY:
case FieldMetadataType.RATING:
return 'string';
case FieldMetadataType.DATE_TIME:
@ -23,6 +29,7 @@ const getTypeFromFieldMetadataType = (
case FieldMetadataType.BOOLEAN:
return 'boolean';
case FieldMetadataType.NUMBER:
case FieldMetadataType.POSITION:
return 'integer';
case FieldMetadataType.NUMERIC:
return 'number';
@ -35,7 +42,7 @@ const get_subfieldsFromField = (nodeField: NodeField): NodeField[] => {
switch (nodeField.type) {
case FieldMetadataType.FULL_NAME: {
const firstName: NodeField = {
type: 'TEXT',
type: FieldMetadataType.TEXT,
name: 'firstName',
label: 'First Name',
description: 'First Name',
@ -43,7 +50,7 @@ const get_subfieldsFromField = (nodeField: NodeField): NodeField[] => {
defaultValue: null,
};
const lastName: NodeField = {
type: 'TEXT',
type: FieldMetadataType.TEXT,
name: 'lastName',
label: 'Last Name',
description: 'Last Name',
@ -54,7 +61,7 @@ const get_subfieldsFromField = (nodeField: NodeField): NodeField[] => {
}
case FieldMetadataType.LINK: {
const url: NodeField = {
type: 'TEXT',
type: FieldMetadataType.TEXT,
name: 'url',
label: 'Url',
description: 'Link Url',
@ -62,7 +69,7 @@ const get_subfieldsFromField = (nodeField: NodeField): NodeField[] => {
defaultValue: null,
};
const label: NodeField = {
type: 'TEXT',
type: FieldMetadataType.TEXT,
name: 'label',
label: 'Label',
description: 'Link Label',
@ -73,7 +80,7 @@ const get_subfieldsFromField = (nodeField: NodeField): NodeField[] => {
}
case FieldMetadataType.CURRENCY: {
const amountMicros: NodeField = {
type: 'NUMBER',
type: FieldMetadataType.NUMBER,
name: 'amountMicros',
label: 'Amount Micros',
description: 'Amount Micros. eg: set 3210000 for 3.21$',
@ -81,7 +88,7 @@ const get_subfieldsFromField = (nodeField: NodeField): NodeField[] => {
defaultValue: null,
};
const currencyCode: NodeField = {
type: 'TEXT',
type: FieldMetadataType.TEXT,
name: 'currencyCode',
label: 'Currency Code',
description: 'Currency Code. eg: USD, EUR, etc...',
@ -92,7 +99,7 @@ const get_subfieldsFromField = (nodeField: NodeField): NodeField[] => {
}
case FieldMetadataType.ADDRESS: {
const address1: NodeField = {
type: 'TEXT',
type: FieldMetadataType.TEXT,
name: 'addressStreet1',
label: 'Address',
description: 'Address',
@ -100,7 +107,7 @@ const get_subfieldsFromField = (nodeField: NodeField): NodeField[] => {
defaultValue: null,
};
const address2: NodeField = {
type: 'TEXT',
type: FieldMetadataType.TEXT,
name: 'addressStreet2',
label: 'Address 2',
description: 'Address 2',
@ -108,7 +115,7 @@ const get_subfieldsFromField = (nodeField: NodeField): NodeField[] => {
defaultValue: null,
};
const city: NodeField = {
type: 'TEXT',
type: FieldMetadataType.TEXT,
name: 'addressCity',
label: 'City',
description: 'City',
@ -116,7 +123,7 @@ const get_subfieldsFromField = (nodeField: NodeField): NodeField[] => {
defaultValue: null,
};
const state: NodeField = {
type: 'TEXT',
type: FieldMetadataType.TEXT,
name: 'addressState',
label: 'State',
description: 'State',
@ -124,7 +131,7 @@ const get_subfieldsFromField = (nodeField: NodeField): NodeField[] => {
defaultValue: null,
};
const postalCode: NodeField = {
type: 'TEXT',
type: FieldMetadataType.TEXT,
name: 'addressPostalCode',
label: 'Postal Code',
description: 'Postal Code',
@ -132,7 +139,7 @@ const get_subfieldsFromField = (nodeField: NodeField): NodeField[] => {
defaultValue: null,
};
const country: NodeField = {
type: 'TEXT',
type: FieldMetadataType.TEXT,
name: 'addressCountry',
label: 'Country',
description: 'Country',
@ -141,6 +148,84 @@ const get_subfieldsFromField = (nodeField: NodeField): NodeField[] => {
};
return [address1, address2, city, state, postalCode, country];
}
case FieldMetadataType.PHONES: {
const primaryPhoneNumber: NodeField = {
type: FieldMetadataType.TEXT,
name: 'primaryPhoneNumber',
label: 'Primary Phone Number',
description: 'Primary Phone Number. 600112233',
isNullable: true,
defaultValue: null,
};
const primaryPhoneCountryCode: NodeField = {
type: FieldMetadataType.TEXT,
name: 'primaryPhoneCountryCode',
label: 'Primary Phone Country Code',
description: 'Primary Phone Country Code. eg: +33',
isNullable: true,
defaultValue: null,
};
const additionalPhones: NodeField = {
type: FieldMetadataType.TEXT,
name: 'additionalPhones',
label: 'Additional Phones',
description: 'Additional Phones',
isNullable: true,
defaultValue: null,
placeholder: '{ number: "", countryCode: "" }',
list: true,
};
return [primaryPhoneNumber, primaryPhoneCountryCode, additionalPhones];
}
case FieldMetadataType.EMAILS: {
const primaryEmail: NodeField = {
type: FieldMetadataType.TEXT,
name: 'primaryEmail',
label: 'Primary Email',
description: 'Primary Email',
isNullable: true,
defaultValue: null,
};
const additionalEmails: NodeField = {
type: FieldMetadataType.TEXT,
name: 'additionalEmails',
label: 'Additional Emails',
description: 'Additional Emails',
list: true,
isNullable: true,
defaultValue: null,
};
return [primaryEmail, additionalEmails];
}
case FieldMetadataType.LINKS: {
const primaryLinkLabel: NodeField = {
type: FieldMetadataType.TEXT,
name: 'primaryLinkLabel',
label: 'Primary Link Label',
description: 'Primary Link Label',
isNullable: true,
defaultValue: null,
};
const primaryLinkUrl: NodeField = {
type: FieldMetadataType.TEXT,
name: 'primaryLinkUrl',
label: 'Primary Link Url',
description: 'Primary Link Url',
isNullable: true,
defaultValue: null,
};
const secondaryLinks: NodeField = {
type: FieldMetadataType.TEXT,
name: 'secondaryLinks',
label: 'Secondary Links',
description: 'Secondary Links',
isNullable: true,
defaultValue: null,
placeholder: '{ url: "", label: "" }',
list: true,
};
return [primaryLinkLabel, primaryLinkUrl, secondaryLinks];
}
default:
throw new Error(`Unknown nodeField type: ${nodeField.type}`);
}
@ -161,6 +246,9 @@ export const computeInputFields = (
case FieldMetadataType.FULL_NAME:
case FieldMetadataType.LINK:
case FieldMetadataType.CURRENCY:
case FieldMetadataType.PHONES:
case FieldMetadataType.EMAILS:
case FieldMetadataType.LINKS:
case FieldMetadataType.ADDRESS:
for (const subNodeField of get_subfieldsFromField(nodeField)) {
const field = {
@ -169,12 +257,15 @@ export const computeInputFields = (
type: getTypeFromFieldMetadataType(subNodeField.type),
helpText: `${nodeField.description}: ${subNodeField.description}`,
required: isFieldRequired(subNodeField),
list: !!subNodeField.list,
placeholder: subNodeField.placeholder,
} as InputField;
result.push(field);
}
break;
case FieldMetadataType.UUID:
case FieldMetadataType.TEXT:
case FieldMetadataType.RICH_TEXT:
case FieldMetadataType.PHONE:
case FieldMetadataType.EMAIL:
case FieldMetadataType.DATE_TIME:
@ -182,6 +273,8 @@ export const computeInputFields = (
case FieldMetadataType.BOOLEAN:
case FieldMetadataType.NUMBER:
case FieldMetadataType.NUMERIC:
case FieldMetadataType.POSITION:
case FieldMetadataType.ARRAY:
case FieldMetadataType.RATING: {
const nodeFieldType = getTypeFromFieldMetadataType(nodeField.type);
if (!nodeFieldType) {
@ -196,6 +289,7 @@ export const computeInputFields = (
type: nodeFieldType,
helpText: nodeField.description,
required,
list: getListFromFieldMetadataType(nodeField.type),
};
result.push(field);
break;

View File

@ -3,12 +3,14 @@ export type InputData = { [x: string]: any };
export type ObjectData = { id: string } | { [x: string]: any };
export type NodeField = {
type: string;
type: FieldMetadataType;
name: string;
label: string;
description: string | null;
isNullable: boolean;
defaultValue: object | null;
list?: boolean;
placeholder?: string;
};
export type Node = {
@ -28,26 +30,39 @@ export type InputField = {
type: string;
helpText: string | null;
required: boolean;
list?: boolean;
placeholder?: string;
};
export enum FieldMetadataType {
UUID = 'UUID',
TEXT = 'TEXT',
PHONE = 'PHONE',
PHONES = 'PHONES',
EMAIL = 'EMAIL',
EMAILS = 'EMAILS',
DATE_TIME = 'DATE_TIME',
DATE = 'DATE',
BOOLEAN = 'BOOLEAN',
NUMBER = 'NUMBER',
NUMERIC = 'NUMERIC',
LINK = 'LINK',
LINKS = 'LINKS',
CURRENCY = 'CURRENCY',
FULL_NAME = 'FULL_NAME',
RATING = 'RATING',
SELECT = 'SELECT',
MULTI_SELECT = 'MULTI_SELECT',
RELATION = 'RELATION',
POSITION = 'POSITION',
ADDRESS = 'ADDRESS',
RICH_TEXT = 'RICH_TEXT',
ARRAY = 'ARRAY',
// Ignored fieldTypes
RELATION = 'RELATION',
RAW_JSON = 'RAW_JSON',
ACTOR = 'ACTOR',
TS_VECTOR = 'TS_VECTOR',
}
export type Schema = {

View File

@ -1,5 +1,17 @@
import { InputData } from '../utils/data.types';
const OBJECT_SUBFIELD_NAMES = ['secondaryLinks', 'additionalPhones'];
const formatArrayInputData = (
key: string,
arrayInputData: InputData,
): string => {
if (OBJECT_SUBFIELD_NAMES.includes(key)) {
return `${arrayInputData[key].join('","')}`;
}
return `"${arrayInputData[key].join('","')}"`;
};
const handleQueryParams = (inputData: InputData): string => {
const formattedInputData: InputData = {};
Object.keys(inputData).forEach((key) => {
@ -17,7 +29,11 @@ const handleQueryParams = (inputData: InputData): string => {
let result = '';
Object.keys(formattedInputData).forEach((key) => {
let quote = '';
if (typeof formattedInputData[key] === 'object') {
if (Array.isArray(formattedInputData[key])) {
result = result.concat(
`${key}: [${formatArrayInputData(key, formattedInputData)}], `,
);
} else if (typeof formattedInputData[key] === 'object') {
result = result.concat(
`${key}: {${handleQueryParams(formattedInputData[key])}}, `,
);