Remove position input from zapier (#16616)

As title
Fixes zapier tests
This commit is contained in:
martmull
2025-12-17 14:39:51 +01:00
committed by GitHub
parent a60f750ed7
commit 967298fe09
18 changed files with 138 additions and 102 deletions
-4
View File
@@ -75,10 +75,6 @@ export default [
sourceTag: 'scope:frontend',
onlyDependOnLibsWithTags: ['scope:shared', 'scope:frontend'],
},
{
sourceTag: 'scope:zapier',
onlyDependOnLibsWithTags: ['scope:shared'],
},
],
},
],
+13
View File
@@ -0,0 +1,13 @@
## 2.1.1
* Add changelog
## 2.1.0
* Fix some authentication issues
* Remove position from input fields
* Fix required boolean fields that should not be
## 2.0.0
* First release
## 1.0.0
Initial release to public.
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "twenty-zapier",
"version": "2.0.2",
"version": "2.1.1",
"description": "Effortlessly sync Twenty with 3000+ apps. Automate tasks, boost productivity, and supercharge your customer relationships!",
"main": "src/index.ts",
"engines": {
+22 -7
View File
@@ -8,9 +8,9 @@
"executor": "nx:run-commands",
"options": {
"cwd": "{projectRoot}",
"commands": ["nx run twenty-zapier:clean && tsc"]
"commands": ["tsc"]
},
"dependsOn": ["^build"]
"dependsOn": ["clean","^build"]
},
"format": {
"executor": "nx:run-commands",
@@ -24,14 +24,17 @@
"options": {
"cwd": "{projectRoot}",
"commands": ["NODE_ENV=test && nx run twenty-zapier:build && jest --testTimeout 10000 --rootDir ./lib/test"]
}
},
"dependsOn": ["build"]
},
"typecheck": {},
"validate": {
"executor": "nx:run-commands",
"options": {
"cwd": "{projectRoot}",
"commands": ["nx run twenty-zapier:build && zapier validate"]
}
},
"dependsOn": ["build"]
},
"versions": {
"executor": "nx:run-commands",
@@ -45,7 +48,8 @@
"options": {
"cwd": "{projectRoot}",
"commands": ["nx run twenty-zapier:clean && npx tsc --watch"]
}
},
"dependsOn": ["build"]
},
"clean": {
"executor": "nx:run-commands",
@@ -59,9 +63,20 @@
"options": {
"cwd": "{projectRoot}",
"commands": [
"nx run twenty-zapier:build && cp -r ../twenty-shared/ node_modules/twenty-shared && zapier push --skip-npm-install"
"nx run twenty-zapier:build && zapier push --disable-dependency-detection"
]
}
},
"dependsOn": ["build"]
},
"promote": {
"executor": "nx:run-commands",
"options": {
"cwd": "{projectRoot}",
"commands": [
"zapier promote"
]
},
"dependsOn": ["build"]
}
}
}
+1 -1
View File
@@ -1,4 +1,4 @@
import { type Bundle, type ZObject } from 'zapier-platform-core';
import type { Bundle, ZObject } from 'zapier-platform-core';
import requestDb from './utils/requestDb';
@@ -1,12 +1,18 @@
import { Bundle, ZObject } from 'zapier-platform-core';
import type { Bundle, ZObject } from 'zapier-platform-core';
import { findObjectNamesSingularKey } from '../triggers/find_object_names_singular';
import { listRecordIdsKey } from '../triggers/list_record_ids';
import { computeInputFields } from '../utils/computeInputFields';
import { InputData } from '../utils/data.types';
import { type InputData } from '../utils/data.types';
import handleQueryParams from '../utils/handleQueryParams';
import requestDb, { requestSchema } from '../utils/requestDb';
import { DatabaseEventAction } from '../utils/triggers/triggers.utils';
import { capitalize } from 'twenty-shared/utils';
import { isNonEmptyString } from '@sniptt/guards';
const capitalize = (stringToCapitalize: string) => {
if (!isNonEmptyString(stringToCapitalize)) return '';
return stringToCapitalize[0].toUpperCase() + stringToCapitalize.slice(1);
};
export const recordInputFields = async (
z: ZObject,
@@ -1,34 +1,10 @@
import { Bundle, createAppTester, tools, ZObject } from 'zapier-platform-core';
import { createAppTester, tools } from 'zapier-platform-core';
import App from '../index';
import getBundle from '../utils/getBundle';
import handleQueryParams from '../utils/handleQueryParams';
import requestDb from '../utils/requestDb';
const appTester = createAppTester(App);
tools.env.inject();
const createApiKey = async (z: ZObject, bundle: Bundle) => {
const query = `
mutation createApiKey {
createApiKey(
data:{${handleQueryParams(bundle.inputData)}}
)
{id}
}`;
return (await requestDb(z, bundle, query)).data.createApiKey.id;
};
const generateApiKeyToken = async (z: ZObject, bundle: Bundle) => {
const query = `
mutation generateApiKeyToken {
generateApiKeyToken(
${handleQueryParams(bundle.inputData)}
)
{token}
}`;
return (await requestDb(z, bundle, query)).data.generateApiKeyToken.token;
};
describe('custom auth', () => {
it('passes authentication and returns json', async () => {
const bundle = getBundle();
@@ -78,31 +54,4 @@ describe('custom auth', () => {
}
throw new Error('appTester should have thrown');
});
it('fails on invalid auth token', async () => {
const expiresAt = '2020-01-01 10:10:10.000';
const apiKeyBundle = getBundle({
name: 'Test',
expiresAt,
});
const apiKeyId = await appTester(createApiKey, apiKeyBundle);
const generateTokenBundle = getBundle({
apiKeyId: apiKeyId,
expiresAt,
});
const expiredToken = await appTester(
generateApiKeyToken,
generateTokenBundle,
);
const bundleWithExpiredApiKey = getBundle({});
bundleWithExpiredApiKey.authData.apiKey = expiredToken;
try {
await appTester(App.authentication.test, bundleWithExpiredApiKey);
} catch (error: any) {
expect(error.message).toContain('UNAUTHENTICATED');
return;
}
throw new Error('appTester should have thrown');
});
});
@@ -1,4 +1,9 @@
import { Bundle, createAppTester, tools, ZObject } from 'zapier-platform-core';
import {
type Bundle,
createAppTester,
tools,
type ZObject,
} from 'zapier-platform-core';
import { crudRecordKey } from '../../creates/crud_record';
import App from '../../index';
@@ -16,16 +21,18 @@ describe('creates.create_company', () => {
name: 'Company Name',
address: { addressCity: 'Paris' },
linkedinLink: {
primaryLinkUrl: '/linkedin_url',
primaryLinkUrl: 'http://test.com/linkedin_url',
primaryLinkLabel: 'Test linkedinUrl',
secondaryLinks: [
'{ url: "/linkedin_url2", label: "Test linkedinUrl2" }',
'{ url: "http://test.com/linkedin_url2", label: "Test linkedinUrl2" }',
],
},
xLink: {
primaryLinkUrl: '/x_url',
primaryLinkUrl: 'http://test.com/x_url',
primaryLinkLabel: 'Test xUrl',
secondaryLinks: ['{ url: "/x_url2", label: "Test xUrl2" }'],
secondaryLinks: [
'{ url: "http://test.com/x_url2", label: "Test xUrl2" }',
],
},
annualRecurringRevenue: {
amountMicros: 100000000000,
@@ -1,4 +1,8 @@
import { Bundle, createAppTester, ZObject } from 'zapier-platform-core';
import {
type Bundle,
createAppTester,
type ZObject,
} from 'zapier-platform-core';
import App from '../../index';
import { triggerRecordKey } from '../../triggers/trigger_record';
@@ -57,8 +61,7 @@ describe('triggers.trigger_record.created', () => {
unsubscribeBundle,
);
expect(unsubscribeResult).toBeDefined();
expect(unsubscribeResult.id).toEqual(result.id);
expect(unsubscribeResult).toBeTruthy();
const checkDbResult = await appTester(
(z: ZObject, bundle: Bundle) =>
@@ -146,9 +149,7 @@ describe('triggers.trigger_record.update', () => {
bundle,
);
expect(checkDbResult.data.webhooks.operations[0]).toEqual(
'company.updated',
);
expect(checkDbResult.data.webhook.operations[0]).toEqual('company.updated');
});
test('should succeed to unsubscribe', async () => {
const bundle = getBundle({});
@@ -171,8 +172,7 @@ describe('triggers.trigger_record.update', () => {
unsubscribeBundle,
);
expect(unsubscribeResult).toBeDefined();
expect(unsubscribeResult.id).toEqual(result.id);
expect(unsubscribeResult).toBeTruthy();
const checkDbResult = await appTester(
(z: ZObject, bundle: Bundle) =>
@@ -231,9 +231,7 @@ describe('triggers.trigger_record.delete', () => {
bundle,
);
expect(checkDbResult.data.webhooks.operations[0]).toEqual(
'company.deleted',
);
expect(checkDbResult.data.webhook.operations[0]).toEqual('company.deleted');
});
test('should succeed to unsubscribe', async () => {
const bundle = getBundle({});
@@ -256,8 +254,7 @@ describe('triggers.trigger_record.delete', () => {
unsubscribeBundle,
);
expect(unsubscribeResult).toBeDefined();
expect(unsubscribeResult.id).toEqual(result.id);
expect(unsubscribeResult).toBeTruthy();
const checkDbResult = await appTester(
(z: ZObject, bundle: Bundle) =>
@@ -1,6 +1,6 @@
import { FieldMetadataType } from '../../types/FieldMetadataType';
import { computeInputFields } from '../../utils/computeInputFields';
import { InputField } from '../../utils/data.types';
import { FieldMetadataType } from 'twenty-shared/types';
import { type InputField } from '../../utils/data.types';
describe('computeInputFields', () => {
test('should create Person input fields properly', () => {
@@ -110,6 +110,16 @@ describe('computeInputFields', () => {
defaultValue: null,
},
},
{
node: {
type: FieldMetadataType.BOOLEAN,
name: 'ICP',
label: 'ICP',
description: 'ICP',
isNullable: false,
defaultValue: false,
},
},
],
},
};
@@ -215,6 +225,15 @@ describe('computeInputFields', () => {
list: false,
placeholder: undefined,
},
{
key: 'ICP',
label: 'ICP',
type: 'boolean',
helpText: 'ICP',
required: false,
list: false,
placeholder: undefined,
},
];
const idInputField: InputField = {
key: 'id',
@@ -1,4 +1,4 @@
import { Bundle, ZObject } from 'zapier-platform-core';
import type { Bundle, ZObject } from 'zapier-platform-core';
import { requestSchema } from '../utils/requestDb';
@@ -0,0 +1,27 @@
export enum FieldMetadataType {
UUID = 'UUID',
TEXT = 'TEXT',
PHONES = 'PHONES',
EMAILS = 'EMAILS',
DATE_TIME = 'DATE_TIME',
DATE = 'DATE',
BOOLEAN = 'BOOLEAN',
NUMBER = 'NUMBER',
NUMERIC = 'NUMERIC',
LINKS = 'LINKS',
CURRENCY = 'CURRENCY',
FULL_NAME = 'FULL_NAME',
RATING = 'RATING',
SELECT = 'SELECT',
MULTI_SELECT = 'MULTI_SELECT',
RELATION = 'RELATION',
MORPH_RELATION = 'MORPH_RELATION',
POSITION = 'POSITION',
ADDRESS = 'ADDRESS',
RAW_JSON = 'RAW_JSON',
RICH_TEXT = 'RICH_TEXT',
RICH_TEXT_V2 = 'RICH_TEXT_V2',
ACTOR = 'ACTOR',
ARRAY = 'ARRAY',
TS_VECTOR = 'TS_VECTOR',
}
@@ -1,5 +1,9 @@
import { InputField, Node, NodeField } from '../utils/data.types';
import { FieldMetadataType } from 'twenty-shared/types';
import { FieldMetadataType } from '../types/FieldMetadataType';
import {
type InputField,
type Node,
type NodeField,
} from '../utils/data.types';
const getListFromFieldMetadataType = (fieldMetadataType: FieldMetadataType) => {
return fieldMetadataType === FieldMetadataType.ARRAY;
@@ -206,7 +210,7 @@ const get_subfieldsFromField = (nodeField: NodeField): NodeField[] => {
};
const isFieldRequired = (nodeField: NodeField): boolean => {
return !nodeField.isNullable && !nodeField.defaultValue;
return !nodeField.isNullable && nodeField.defaultValue === null;
};
export const computeInputFields = (
@@ -244,7 +248,6 @@ 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);
@@ -1,4 +1,5 @@
import { FieldMetadataType } from 'twenty-shared/types';
import { type FieldMetadataType } from '../types/FieldMetadataType';
export type InputData = { [x: string]: any };
export type NodeField = {
@@ -7,7 +8,7 @@ export type NodeField = {
label: string;
description: string | null;
isNullable: boolean;
defaultValue: object | null;
defaultValue: boolean | object | null;
list?: boolean;
placeholder?: string;
};
@@ -1,6 +1,6 @@
import { Bundle } from 'zapier-platform-core';
import { type Bundle } from 'zapier-platform-core';
import { InputData } from '../utils/data.types';
import { type InputData } from '../utils/data.types';
const getBundle = (inputData?: InputData): Bundle => {
return {
@@ -1,4 +1,4 @@
import { InputData } from '../utils/data.types';
import { type InputData } from '../utils/data.types';
const OBJECT_SUBFIELD_NAMES = ['secondaryLinks', 'additionalPhones'];
@@ -1,6 +1,6 @@
import { Bundle, HttpRequestOptions, ZObject } from 'zapier-platform-core';
import type { Bundle, HttpRequestOptions, ZObject } from 'zapier-platform-core';
import { Schema } from '../utils/data.types';
import { type Schema } from '../utils/data.types';
export const requestSchema = async (
z: ZObject,
@@ -31,12 +31,15 @@ export const performSubscribe = async (z: ZObject, bundle: Bundle) => {
return result.data.createWebhook;
};
export const performUnsubscribe = async (z: ZObject, bundle: Bundle) => {
export const performUnsubscribe = async (
z: ZObject,
bundle: Bundle,
): Promise<boolean> => {
const data = { id: bundle.subscribeData?.id };
const result = await requestDb(
z,
bundle,
`mutation deleteWebhook {deleteWebhook(${handleQueryParams(data)})}`,
`mutation deleteWebhook {deleteWebhook(input: {${handleQueryParams(data)}})}`,
'metadata',
);
return result.data.deleteWebhook;