diff --git a/packages/twenty-server/src/workspace/workspace-query-runner/workspace-query-runner.service.ts b/packages/twenty-server/src/workspace/workspace-query-runner/workspace-query-runner.service.ts index 450cea667..415f7a038 100644 --- a/packages/twenty-server/src/workspace/workspace-query-runner/workspace-query-runner.service.ts +++ b/packages/twenty-server/src/workspace/workspace-query-runner/workspace-query-runner.service.ts @@ -156,12 +156,6 @@ export class WorkspaceQueryRunnerService { ): Promise { const results = await this.createMany({ data: [args.data] }, options); - await this.triggerWebhooks( - results, - CallWebhookJobsJobOperation.create, - options, - ); - return results?.[0]; } diff --git a/packages/twenty-zapier/src/creates/create_record.ts b/packages/twenty-zapier/src/creates/create_record.ts index ee9ea7285..01d6333aa 100644 --- a/packages/twenty-zapier/src/creates/create_record.ts +++ b/packages/twenty-zapier/src/creates/create_record.ts @@ -1,16 +1,10 @@ import { Bundle, ZObject } from 'zapier-platform-core'; -import requestDb, { requestSchema } from '../utils/requestDb'; -import handleQueryParams from '../utils/handleQueryParams'; -import { capitalize } from '../utils/capitalize'; -import { computeInputFields } from '../utils/computeInputFields'; + import { findObjectNamesSingularKey } from '../triggers/find_object_names_singular'; - -const recordInputFields = async (z: ZObject, bundle: Bundle) => { - const schema = await requestSchema(z, bundle); - const infos = schema.components.schemas[bundle.inputData.nameSingular]; - - return computeInputFields(infos); -}; +import { capitalize } from '../utils/capitalize'; +import { recordInputFields } from '../utils/creates/creates.utils'; +import handleQueryParams from '../utils/handleQueryParams'; +import requestDb from '../utils/requestDb'; const perform = async (z: ZObject, bundle: Bundle) => { const data = bundle.inputData; @@ -30,9 +24,9 @@ export const createRecordKey = 'create_record'; export default { display: { - description: 'Creates a new Record in Twenty', + description: 'Create a Record in Twenty.', hidden: false, - label: 'Create New Record', + label: 'Create Record', }, key: createRecordKey, noun: 'Record', diff --git a/packages/twenty-zapier/src/creates/delete_record.ts b/packages/twenty-zapier/src/creates/delete_record.ts new file mode 100644 index 000000000..274d12960 --- /dev/null +++ b/packages/twenty-zapier/src/creates/delete_record.ts @@ -0,0 +1,56 @@ +import { Bundle, ZObject } from 'zapier-platform-core'; + +import { findObjectNamesPluralKey } from '../triggers/find_object_names_plural'; +import { listRecordIdsKey } from '../triggers/list_record_ids'; +import { capitalize } from '../utils/capitalize'; +import requestDb from '../utils/requestDb'; + +export const deleteRecordKey = 'delete_record'; + +const perform = async (z: ZObject, bundle: Bundle) => { + const data = bundle.inputData; + const nameSingular = data.nameSingular; + const id = data.id; + delete data.nameSingular; + delete data.id; + const query = ` + mutation delete${capitalize(nameSingular)} { + delete${capitalize(nameSingular)}( + id: "${id}" + ) + {id} + }`; + return await requestDb(z, bundle, query); +}; + +export default { + display: { + description: 'Delete a Record in Twenty.', + hidden: false, + label: 'Delete Record', + }, + key: deleteRecordKey, + noun: 'Record', + operation: { + inputFields: [ + { + key: 'namePlural', + label: 'Record Name', + dynamic: `${findObjectNamesPluralKey}.namePlural`, + required: true, + altersDynamicFields: true, + }, + { + key: 'id', + label: 'Id', + type: 'string', + dynamic: `${listRecordIdsKey}.id`, + required: true, + }, + ], + sample: { + id: '179ed459-79cf-41d9-ab85-96397fa8e936', + }, + perform, + }, +}; diff --git a/packages/twenty-zapier/src/creates/update_record.ts b/packages/twenty-zapier/src/creates/update_record.ts new file mode 100644 index 000000000..7e80aa5cf --- /dev/null +++ b/packages/twenty-zapier/src/creates/update_record.ts @@ -0,0 +1,56 @@ +import { Bundle, ZObject } from 'zapier-platform-core'; + +import { findObjectNamesSingularKey } from '../triggers/find_object_names_singular'; +import { capitalize } from '../utils/capitalize'; +import { recordInputFields } from '../utils/creates/creates.utils'; +import handleQueryParams from '../utils/handleQueryParams'; +import requestDb from '../utils/requestDb'; + +export const updateRecordKey = 'update_record'; + +const perform = async (z: ZObject, bundle: Bundle) => { + const data = bundle.inputData; + const nameSingular = data.nameSingular; + const id = data.id; + delete data.nameSingular; + delete data.id; + const query = ` + mutation update${capitalize(nameSingular)} { + update${capitalize(nameSingular)}( + data:{${handleQueryParams(data)}}, + id: "${id}" + ) + {id} + }`; + return await requestDb(z, bundle, query); +}; + +const updateRecordInputFields = async (z: ZObject, bundle: Bundle) => { + return recordInputFields(z, bundle, true); +}; + +export default { + display: { + description: 'Update a Record in Twenty.', + hidden: false, + label: 'Update Record', + }, + key: updateRecordKey, + noun: 'Record', + operation: { + inputFields: [ + { + key: 'nameSingular', + required: true, + label: 'Record Name', + dynamic: `${findObjectNamesSingularKey}.nameSingular`, + altersDynamicFields: true, + }, + updateRecordInputFields, + ], + sample: { + id: '179ed459-79cf-41d9-ab85-96397fa8e936', + }, + perform, + }, +}; diff --git a/packages/twenty-zapier/src/index.ts b/packages/twenty-zapier/src/index.ts index 11d247590..c11d76535 100644 --- a/packages/twenty-zapier/src/index.ts +++ b/packages/twenty-zapier/src/index.ts @@ -1,13 +1,19 @@ +import { version as platformVersion } from 'zapier-platform-core'; + +import 'dotenv/config'; + +const { version } = require('../package.json'); + +import createRecord, { createRecordKey } from './creates/create_record'; +import deleteRecord, { deleteRecordKey } from './creates/delete_record'; +import updateRecord, { updateRecordKey } from './creates/update_record'; import findObjectNamesPlural, { findObjectNamesPluralKey, } from './triggers/find_object_names_plural'; - -const { version } = require('../package.json'); -import { version as platformVersion } from 'zapier-platform-core'; -import createRecord, { createRecordKey } from './creates/create_record'; import findObjectNamesSingular, { findObjectNamesSingularKey, } from './triggers/find_object_names_singular'; +import listRecordIds, { listRecordIdsKey } from './triggers/list_record_ids'; import triggerRecordCreated, { triggerRecordCreatedKey, } from './triggers/trigger_record_created'; @@ -18,7 +24,6 @@ import triggerRecordUpdated, { triggerRecordUpdatedKey, } from './triggers/trigger_record_updated'; import authentication from './authentication'; -import 'dotenv/config'; export default { version, @@ -27,11 +32,14 @@ export default { triggers: { [findObjectNamesSingularKey]: findObjectNamesSingular, [findObjectNamesPluralKey]: findObjectNamesPlural, + [listRecordIdsKey]: listRecordIds, [triggerRecordCreatedKey]: triggerRecordCreated, [triggerRecordUpdatedKey]: triggerRecordUpdated, [triggerRecordDeletedKey]: triggerRecordDeleted, }, creates: { [createRecordKey]: createRecord, + [updateRecordKey]: updateRecord, + [deleteRecordKey]: deleteRecord, }, }; diff --git a/packages/twenty-zapier/src/test/creates/create_record.test.ts b/packages/twenty-zapier/src/test/creates/create_record.test.ts index 4ed0e214c..1a922281c 100644 --- a/packages/twenty-zapier/src/test/creates/create_record.test.ts +++ b/packages/twenty-zapier/src/test/creates/create_record.test.ts @@ -1,12 +1,13 @@ +import { Bundle, createAppTester, tools, ZObject } from 'zapier-platform-core'; + +import { createRecordKey } from '../../creates/create_record'; import App from '../../index'; import getBundle from '../../utils/getBundle'; -import { Bundle, createAppTester, tools, ZObject } from 'zapier-platform-core'; import requestDb from '../../utils/requestDb'; -import { createRecordKey } from '../../creates/create_record'; const appTester = createAppTester(App); tools.env.inject(); -describe('creates.[createRecordKey]', () => { +describe('creates.create_company', () => { test('should run to create a Company Record', async () => { const bundle = getBundle({ nameSingular: 'Company', diff --git a/packages/twenty-zapier/src/test/creates/delete_record.test.ts b/packages/twenty-zapier/src/test/creates/delete_record.test.ts new file mode 100644 index 000000000..902e7643e --- /dev/null +++ b/packages/twenty-zapier/src/test/creates/delete_record.test.ts @@ -0,0 +1,49 @@ +import { Bundle, createAppTester, tools, ZObject } from 'zapier-platform-core'; + +import { createRecordKey } from '../../creates/create_record'; +import { deleteRecordKey } from '../../creates/delete_record'; +import App from '../../index'; +import getBundle from '../../utils/getBundle'; +import requestDb from '../../utils/requestDb'; +const appTester = createAppTester(App); + +tools.env.inject(); +describe('creates.delete_company', () => { + test('should run to delete a Company record', async () => { + const createBundle = getBundle({ + nameSingular: 'Company', + name: 'Delete Company Name', + employees: 25, + }); + + const createResult = await appTester( + App.creates[createRecordKey].operation.perform, + createBundle, + ); + + const companyId = createResult.data?.createCompany?.id; + + const deleteBundle = getBundle({ + nameSingular: 'Company', + id: companyId, + }); + + const deleteResult = await appTester( + App.creates[deleteRecordKey].operation.perform, + deleteBundle, + ); + + expect(deleteResult).toBeDefined(); + expect(deleteResult.data?.deleteCompany?.id).toBeDefined(); + const checkDbResult = await appTester( + (z: ZObject, bundle: Bundle) => + requestDb( + z, + bundle, + `query findCompanies {companies(filter: {id: {eq: "${companyId}"}}){edges{node{id}}}}`, + ), + deleteBundle, + ); + expect(checkDbResult.data.companies.edges.length).toEqual(0); + }); +}); diff --git a/packages/twenty-zapier/src/test/creates/update_record.test.ts b/packages/twenty-zapier/src/test/creates/update_record.test.ts new file mode 100644 index 000000000..fafab1b06 --- /dev/null +++ b/packages/twenty-zapier/src/test/creates/update_record.test.ts @@ -0,0 +1,50 @@ +import { Bundle, createAppTester, tools, ZObject } from 'zapier-platform-core'; + +import { createRecordKey } from '../../creates/create_record'; +import { updateRecordKey } from '../../creates/update_record'; +import App from '../../index'; +import getBundle from '../../utils/getBundle'; +import requestDb from '../../utils/requestDb'; +const appTester = createAppTester(App); + +tools.env.inject(); +describe('creates.update_company', () => { + test('should run to update a Company record', async () => { + const createBundle = getBundle({ + nameSingular: 'Company', + name: 'Company Name', + employees: 25, + }); + + const createResult = await appTester( + App.creates[createRecordKey].operation.perform, + createBundle, + ); + + const companyId = createResult.data?.createCompany?.id; + + const updateBundle = getBundle({ + nameSingular: 'Company', + id: companyId, + name: 'Updated Company Name', + }); + + const updateResult = await appTester( + App.creates[updateRecordKey].operation.perform, + updateBundle, + ); + + expect(updateResult).toBeDefined(); + expect(updateResult.data?.updateCompany?.id).toBeDefined(); + const checkDbResult = await appTester( + (z: ZObject, bundle: Bundle) => + requestDb( + z, + bundle, + `query findCompany {company(filter: {id: {eq: "${companyId}"}}){id name}}`, + ), + updateBundle, + ); + expect(checkDbResult.data.company.name).toEqual('Updated Company Name'); + }); +}); diff --git a/packages/twenty-zapier/src/test/triggers/list_record_ids.test.ts b/packages/twenty-zapier/src/test/triggers/list_record_ids.test.ts new file mode 100644 index 000000000..d9e4f1b3d --- /dev/null +++ b/packages/twenty-zapier/src/test/triggers/list_record_ids.test.ts @@ -0,0 +1,20 @@ +import { createAppTester, tools } from 'zapier-platform-core'; + +import App from '../../index'; +import { listRecordIdsKey } from '../../triggers/list_record_ids'; +import getBundle from '../../utils/getBundle'; +tools.env.inject(); + +const appTester = createAppTester(App); +describe('triggers.list_record_ids', () => { + test('should run', async () => { + const bundle = getBundle({ namePlural: 'companies' }); + const result = await appTester( + App.triggers[listRecordIdsKey].operation.perform, + bundle, + ); + expect(result).toBeDefined(); + expect(result.length).toBeGreaterThan(1); + expect(result[0].id).toBeDefined(); + }); +}); diff --git a/packages/twenty-zapier/src/test/utils/computeInputFields.test.ts b/packages/twenty-zapier/src/test/utils/computeInputFields.test.ts index ce46cd332..7795ef0ae 100644 --- a/packages/twenty-zapier/src/test/utils/computeInputFields.test.ts +++ b/packages/twenty-zapier/src/test/utils/computeInputFields.test.ts @@ -5,6 +5,9 @@ describe('computeInputFields', () => { const personInfos = { type: 'object', properties: { + id: { + type: 'string', + }, email: { type: 'string', }, @@ -33,6 +36,7 @@ describe('computeInputFields', () => { required: ['avatarUrl'], }; expect(computeInputFields(personInfos)).toEqual([ + { key: 'id', label: 'Id', required: false, type: 'string' }, { key: 'email', label: 'Email', required: false, type: 'string' }, { key: 'xLink__url', @@ -48,5 +52,27 @@ describe('computeInputFields', () => { }, { key: 'avatarUrl', label: 'Avatar Url', required: true, type: 'string' }, ]); + expect(computeInputFields(personInfos, true)).toEqual([ + { key: 'id', label: 'Id', required: true, type: 'string' }, + { key: 'email', label: 'Email', required: false, type: 'string' }, + { + key: 'xLink__url', + label: 'X Link: Url', + required: false, + type: 'string', + }, + { + key: 'xLink__label', + label: 'X Link: Label', + required: false, + type: 'string', + }, + { + key: 'avatarUrl', + label: 'Avatar Url', + required: false, + type: 'string', + }, + ]); }); }); diff --git a/packages/twenty-zapier/src/triggers/list_record_ids.ts b/packages/twenty-zapier/src/triggers/list_record_ids.ts new file mode 100644 index 000000000..17a120a8c --- /dev/null +++ b/packages/twenty-zapier/src/triggers/list_record_ids.ts @@ -0,0 +1,37 @@ +import { Bundle, ZObject } from 'zapier-platform-core'; + +import { capitalize } from '../utils/capitalize'; +import requestDb from '../utils/requestDb'; + +const listRecordIdsRequest = async ( + z: ZObject, + bundle: Bundle, +): Promise<{ id: string }[]> => { + const data = bundle.inputData; + const namePlural = data.namePlural; + const query = ` + query List${capitalize(namePlural)}Ids { + ${namePlural}{edges{node{id}}} + }`; + const result = await requestDb(z, bundle, query); + return result.data[namePlural]['edges'].map((edge: any) => { + return { + id: edge.node.id, + }; + }); +}; + +export const listRecordIdsKey = 'list_record_ids'; + +export default { + display: { + description: 'List Record Ids of an object.', + label: 'List Record Ids.', + hidden: true, + }, + key: listRecordIdsKey, + noun: 'Object', + operation: { + perform: listRecordIdsRequest, + }, +}; diff --git a/packages/twenty-zapier/src/triggers/trigger_record_created.ts b/packages/twenty-zapier/src/triggers/trigger_record_created.ts index 99cdffa7f..145c9fb57 100644 --- a/packages/twenty-zapier/src/triggers/trigger_record_created.ts +++ b/packages/twenty-zapier/src/triggers/trigger_record_created.ts @@ -1,3 +1,5 @@ +import { Bundle, ZObject } from 'zapier-platform-core'; + import { findObjectNamesPluralKey } from '../triggers/find_object_names_plural'; import { listSample, @@ -5,8 +7,7 @@ import { perform, performUnsubscribe, subscribe, -} from '../utils/triggers.utils'; -import { Bundle, ZObject } from 'zapier-platform-core'; +} from '../utils/triggers/triggers.utils'; export const triggerRecordCreatedKey = 'trigger_record_created'; diff --git a/packages/twenty-zapier/src/triggers/trigger_record_deleted.ts b/packages/twenty-zapier/src/triggers/trigger_record_deleted.ts index 387ab3c5a..dfb4218b7 100644 --- a/packages/twenty-zapier/src/triggers/trigger_record_deleted.ts +++ b/packages/twenty-zapier/src/triggers/trigger_record_deleted.ts @@ -1,12 +1,13 @@ +import { Bundle, ZObject } from 'zapier-platform-core'; + import { findObjectNamesPluralKey } from '../triggers/find_object_names_plural'; import { - perform, listSample, - subscribe, - performUnsubscribe, Operation, -} from '../utils/triggers.utils'; -import { Bundle, ZObject } from 'zapier-platform-core'; + perform, + performUnsubscribe, + subscribe, +} from '../utils/triggers/triggers.utils'; export const triggerRecordDeletedKey = 'trigger_record_deleted'; diff --git a/packages/twenty-zapier/src/triggers/trigger_record_updated.ts b/packages/twenty-zapier/src/triggers/trigger_record_updated.ts index 798d69cda..1577f711c 100644 --- a/packages/twenty-zapier/src/triggers/trigger_record_updated.ts +++ b/packages/twenty-zapier/src/triggers/trigger_record_updated.ts @@ -1,3 +1,5 @@ +import { Bundle, ZObject } from 'zapier-platform-core'; + import { findObjectNamesPluralKey } from '../triggers/find_object_names_plural'; import { listSample, @@ -5,8 +7,7 @@ import { perform, performUnsubscribe, subscribe, -} from '../utils/triggers.utils'; -import { Bundle, ZObject } from 'zapier-platform-core'; +} from '../utils/triggers/triggers.utils'; export const triggerRecordUpdatedKey = 'trigger_record_updated'; diff --git a/packages/twenty-zapier/src/utils/computeInputFields.ts b/packages/twenty-zapier/src/utils/computeInputFields.ts index 381880c39..05025301f 100644 --- a/packages/twenty-zapier/src/utils/computeInputFields.ts +++ b/packages/twenty-zapier/src/utils/computeInputFields.ts @@ -12,7 +12,10 @@ type Infos = { required: string[]; }; -export const computeInputFields = (infos: Infos): object[] => { +export const computeInputFields = ( + infos: Infos, + idRequired = false, +): object[] => { const result = []; for (const fieldName of Object.keys(infos.properties)) { @@ -38,17 +41,21 @@ export const computeInputFields = (infos: Infos): object[] => { result.push(field); } break; - default: + default: { const field = { key: fieldName, label: labelling(fieldName), type: infos.properties[fieldName].type, required: false, }; - if (infos.required?.includes(fieldName)) { + if ( + (idRequired && fieldName === 'id') || + (!idRequired && infos.required?.includes(fieldName)) + ) { field.required = true; } result.push(field); + } } } diff --git a/packages/twenty-zapier/src/utils/creates/creates.utils.ts b/packages/twenty-zapier/src/utils/creates/creates.utils.ts new file mode 100644 index 000000000..7df8925a3 --- /dev/null +++ b/packages/twenty-zapier/src/utils/creates/creates.utils.ts @@ -0,0 +1,15 @@ +import { Bundle, ZObject } from 'zapier-platform-core'; + +import { computeInputFields } from '../../utils/computeInputFields'; +import { requestSchema } from '../../utils/requestDb'; + +export const recordInputFields = async ( + z: ZObject, + bundle: Bundle, + idRequired = false, +) => { + const schema = await requestSchema(z, bundle); + const infos = schema.components.schemas[bundle.inputData.nameSingular]; + + return computeInputFields(infos, idRequired); +}; diff --git a/packages/twenty-zapier/src/utils/triggers.utils.ts b/packages/twenty-zapier/src/utils/triggers/triggers.utils.ts similarity index 90% rename from packages/twenty-zapier/src/utils/triggers.utils.ts rename to packages/twenty-zapier/src/utils/triggers/triggers.utils.ts index 19e7c2546..e1a4567d2 100644 --- a/packages/twenty-zapier/src/utils/triggers.utils.ts +++ b/packages/twenty-zapier/src/utils/triggers/triggers.utils.ts @@ -1,6 +1,7 @@ import { Bundle, ZObject } from 'zapier-platform-core'; -import requestDb, { requestDbViaRestApi } from '../utils/requestDb'; -import handleQueryParams from '../utils/handleQueryParams'; + +import handleQueryParams from '../../utils/handleQueryParams'; +import requestDb, { requestDbViaRestApi } from '../../utils/requestDb'; export enum Operation { create = 'create',