From a413b29dd40aad7398e32ec14178ced605ec0d66 Mon Sep 17 00:00:00 2001 From: martmull Date: Mon, 27 Nov 2023 18:09:21 +0100 Subject: [PATCH] Fix zapier (#2735) * Fix zapier tests * Handle nested fields * Code review returns --- .../src/creates/create_company.ts | 40 +++++++++++++++---- .../src/creates/create_person.ts | 14 +++---- .../src/test/authentication.test.ts | 35 +++++++++++----- .../src/test/creates/create_company.test.ts | 24 +++++------ .../src/test/creates/create_person.test.ts | 18 ++++----- .../src/test/triggers/company.test.ts | 15 ++++--- .../src/test/utils/handleQueryParams.test.ts | 10 +++-- .../twenty-zapier/src/triggers/company.ts | 26 ++++++------ .../src/utils/handleQueryParams.ts | 23 +++++++++-- packages/twenty-zapier/src/utils/requestDb.ts | 12 +++--- 10 files changed, 137 insertions(+), 80 deletions(-) diff --git a/packages/twenty-zapier/src/creates/create_company.ts b/packages/twenty-zapier/src/creates/create_company.ts index 1b36dc091..60c8056e1 100644 --- a/packages/twenty-zapier/src/creates/create_company.ts +++ b/packages/twenty-zapier/src/creates/create_company.ts @@ -4,8 +4,8 @@ import requestDb from '../utils/requestDb'; const perform = async (z: ZObject, bundle: Bundle) => { const query = ` - mutation CreateCompany { - createOneCompany( + mutation createCompany { + createCompany( data:{${handleQueryParams(bundle.inputData)}} ) {id} @@ -47,29 +47,53 @@ export default { altersDynamicFields: false, }, { - key: 'linkedinUrl', - label: 'Linkedin', + key: 'linkedinLink__url', + label: 'Linkedin Link Url', type: 'string', required: false, list: false, altersDynamicFields: false, }, { - key: 'xUrl', - label: 'Twitter', + key: 'linkedinLink__label', + label: 'Linkedin Link Label', type: 'string', required: false, list: false, altersDynamicFields: false, }, { - key: 'annualRecurringRevenue', - label: 'ARR (Annual Recurring Revenue)', + key: 'xLink__url', + label: 'Twitter Link Url', + type: 'string', + required: false, + list: false, + altersDynamicFields: false, + }, + { + key: 'xLink__label', + label: 'Twitter Link Label', + type: 'string', + required: false, + list: false, + altersDynamicFields: false, + }, + { + key: 'annualRecurringRevenue__amountMicros', + label: 'ARR (Annual Recurring Revenue) amount micros', type: 'number', required: false, list: false, altersDynamicFields: false, }, + { + key: 'annualRecurringRevenue__currencyCode', + label: 'ARR (Annual Recurring Revenue) currency Code', + type: 'string', + required: false, + list: false, + altersDynamicFields: false, + }, { key: 'idealCustomerProfile', label: 'ICP (Ideal Customer Profile)', diff --git a/packages/twenty-zapier/src/creates/create_person.ts b/packages/twenty-zapier/src/creates/create_person.ts index 1b0a4102d..caea99860 100644 --- a/packages/twenty-zapier/src/creates/create_person.ts +++ b/packages/twenty-zapier/src/creates/create_person.ts @@ -4,8 +4,8 @@ import requestDb from '../utils/requestDb'; const perform = async (z: ZObject, bundle: Bundle) => { const query = ` - mutation CreatePerson { - createOnePerson( + mutation createPerson { + createPerson( data:{${handleQueryParams(bundle.inputData)}} ) {id} @@ -23,15 +23,15 @@ export default { operation: { inputFields: [ { - key: 'firstName', + key: 'name__firstName', label: 'First Name', type: 'string', - required: true, + required: false, list: false, altersDynamicFields: false, }, { - key: 'lastName', + key: 'name__lastName', label: 'Last Name', type: 'string', required: false, @@ -64,8 +64,8 @@ export default { }, ], sample: { - firstName: 'John', - lastName: 'Doe', + name__firstName: 'John', + name__lastName: 'Doe', email: 'johndoe@gmail.com', }, perform, diff --git a/packages/twenty-zapier/src/test/authentication.test.ts b/packages/twenty-zapier/src/test/authentication.test.ts index 54c499119..7750ce8ef 100644 --- a/packages/twenty-zapier/src/test/authentication.test.ts +++ b/packages/twenty-zapier/src/test/authentication.test.ts @@ -12,15 +12,26 @@ import requestDb from '../utils/requestDb'; const appTester = createAppTester(App); tools.env.inject(); -const generateKey = async (z: ZObject, bundle: Bundle) => { +const createApiKey = async (z: ZObject, bundle: Bundle) => { const query = ` - mutation CreateApiKey { - createOneApiKey( + 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.createOneApiKey.token; + return (await requestDb(z, bundle, query)).data.generateApiKeyToken.token; }; describe('custom auth', () => { @@ -37,18 +48,24 @@ describe('custom auth', () => { try { await appTester(App.authentication.test, bundle); } catch (error: any) { - expect(error.message).toContain('The API Key you supplied is incorrect'); + expect(error.message).toContain('UNAUTHENTICATED'); return; } throw new Error('appTester should have thrown'); }); it('fails on invalid auth token', async () => { - const bundle = getBundle({ + const expiresAt = '2020-01-01 10:10:10.000' + const apiKeyBundle = getBundle({ name: 'Test', - expiresAt: '2020-01-01 10:10:10.000', + expiresAt, }); - const expiredToken = await appTester(generateKey, bundle); + const apiKeyId = await appTester(createApiKey, apiKeyBundle); + const generateTokenBundle = getBundle({ + apiKeyId: apiKeyId, + expiresAt, + }); + const expiredToken = await appTester(generateApiKeyToken, generateTokenBundle); const bundleWithExpiredApiKey = { authData: { apiKey: expiredToken }, }; @@ -56,7 +73,7 @@ describe('custom auth', () => { try { await appTester(App.authentication.test, bundleWithExpiredApiKey); } catch (error: any) { - expect(error.message).toContain('The API Key you supplied is incorrect'); + expect(error.message).toContain('UNAUTHENTICATED'); return; } throw new Error('appTester should have thrown'); diff --git a/packages/twenty-zapier/src/test/creates/create_company.test.ts b/packages/twenty-zapier/src/test/creates/create_company.test.ts index 624948277..81799a5d6 100644 --- a/packages/twenty-zapier/src/test/creates/create_company.test.ts +++ b/packages/twenty-zapier/src/test/creates/create_company.test.ts @@ -11,9 +11,9 @@ describe('creates.create_company', () => { name: 'Company Name', address: 'Company Address', domainName: 'Company Domain Name', - linkedinUrl: 'Test linkedinUrl', - xUrl: 'Test xUrl', - annualRecurringRevenue: 100000, + linkedinLink: {url: '/linkedin_url', label: "Test linkedinUrl"}, + xLink: {url: '/x_url', label: "Test xUrl"}, + annualRecurringRevenue: {amountMicros:100000000000,currencyCode: 'USD'}, idealCustomerProfile: true, employees: 25, }); @@ -22,18 +22,18 @@ describe('creates.create_company', () => { bundle, ); expect(result).toBeDefined(); - expect(result.data?.createOneCompany?.id).toBeDefined(); + expect(result.data?.createCompany?.id).toBeDefined(); const checkDbResult = await appTester( (z: ZObject, bundle: Bundle) => requestDb( z, bundle, - `query findCompany {findUniqueCompany(where: {id: "${result.data.createOneCompany.id}"}){id, annualRecurringRevenue}}`, + `query findCompany {company(filter: {id: {eq: "${result.data.createCompany.id}"}}){id annualRecurringRevenue{amountMicros currencyCode}}}`, ), bundle, ); - expect(checkDbResult.data.findUniqueCompany.annualRecurringRevenue).toEqual( - 100000, + expect(checkDbResult.data.company.annualRecurringRevenue.amountMicros).toEqual( + 100000000000, ); }); test('should run with not required missing params', async () => { @@ -41,8 +41,8 @@ describe('creates.create_company', () => { name: 'Company Name', address: 'Company Address', domainName: 'Company Domain Name', - linkedinUrl: 'Test linkedinUrl', - xUrl: 'Test xUrl', + linkedinLink: {url: '/linkedin_url', label: "Test linkedinUrl"}, + xLink: {url: '/x_url', label: "Test xUrl"}, idealCustomerProfile: true, employees: 25, }); @@ -51,17 +51,17 @@ describe('creates.create_company', () => { bundle, ); expect(result).toBeDefined(); - expect(result.data?.createOneCompany?.id).toBeDefined(); + expect(result.data?.createCompany?.id).toBeDefined(); const checkDbResult = await appTester( (z: ZObject, bundle: Bundle) => requestDb( z, bundle, - `query findCompany {findUniqueCompany(where: {id: "${result.data.createOneCompany.id}"}){id, annualRecurringRevenue}}`, + `query findCompany {company(filter: {id: {eq: "${result.data.createCompany.id}"}}){id annualRecurringRevenue{amountMicros currencyCode}}}`, ), bundle, ); - expect(checkDbResult.data.findUniqueCompany.annualRecurringRevenue).toEqual( + expect(checkDbResult.data.company.annualRecurringRevenue.amountMicros).toEqual( null, ); }); diff --git a/packages/twenty-zapier/src/test/creates/create_person.test.ts b/packages/twenty-zapier/src/test/creates/create_person.test.ts index 7003cd055..a50c30d3e 100644 --- a/packages/twenty-zapier/src/test/creates/create_person.test.ts +++ b/packages/twenty-zapier/src/test/creates/create_person.test.ts @@ -8,8 +8,7 @@ tools.env.inject(); describe('creates.create_person', () => { test('should run', async () => { const bundle = getBundle({ - firstName: 'John', - lastName: 'Doe', + name: {firstName: 'John', lastName: 'Doe'}, email: 'johndoe@gmail.com', phone: '+33610203040', city: 'Paris', @@ -19,23 +18,22 @@ describe('creates.create_person', () => { bundle, ); expect(results).toBeDefined(); - expect(results.data?.createOnePerson?.id).toBeDefined(); + expect(results.data?.createPerson?.id).toBeDefined(); const checkDbResult = await appTester( (z: ZObject, bundle: Bundle) => requestDb( z, bundle, - `query findPerson {findUniquePerson(id: "${results.data.createOnePerson.id}"){id, phone}}`, + `query findPerson {person(filter: {id: {eq: "${results.data.createPerson.id}"}}){phone}}`, ), bundle, ); - expect(checkDbResult.data.findUniquePerson.phone).toEqual('+33610203040'); + expect(checkDbResult.data.person.phone).toEqual('+33610203040'); }); test('should run with not required missing params', async () => { const bundle = getBundle({ - firstName: 'John', - lastName: 'Doe', + name: {firstName: 'John', lastName: 'Doe'}, email: 'johndoe@gmail.com', city: 'Paris', }); @@ -44,16 +42,16 @@ describe('creates.create_person', () => { bundle, ); expect(results).toBeDefined(); - expect(results.data?.createOnePerson?.id).toBeDefined(); + expect(results.data?.createPerson?.id).toBeDefined(); const checkDbResult = await appTester( (z: ZObject, bundle: Bundle) => requestDb( z, bundle, - `query findPerson {findUniquePerson(id: "${results.data.createOnePerson.id}"){id, phone}}`, + `query findPerson {person(filter: {id: {eq: "${results.data.createPerson.id}"}}){phone}}`, ), bundle, ); - expect(checkDbResult.data.findUniquePerson.phone).toEqual(null); + expect(checkDbResult.data.person.phone).toEqual(""); }); }); diff --git a/packages/twenty-zapier/src/test/triggers/company.test.ts b/packages/twenty-zapier/src/test/triggers/company.test.ts index 54d22da8b..15ac248cb 100644 --- a/packages/twenty-zapier/src/test/triggers/company.test.ts +++ b/packages/twenty-zapier/src/test/triggers/company.test.ts @@ -19,13 +19,12 @@ describe('triggers.company', () => { requestDb( z, bundle, - `query findManyWebHook {findManyWebHook(where: {id: {equals: "${result.id}"}}){id operation}}`, + `query webhook {webhook(filter: {id: {eq: "${result.id}"}}){id operation}}`, ), bundle, ); - expect(checkDbResult.data.findManyWebHook.length).toEqual(1); - expect(checkDbResult.data.findManyWebHook[0].operation).toEqual( - 'createOneCompany', + expect(checkDbResult.data.webhook.operation).toEqual( + 'company', ); }); test('should succeed to unsubscribe', async () => { @@ -48,13 +47,13 @@ describe('triggers.company', () => { requestDb( z, bundle, - `query findManyWebHook {findManyWebHook(where: {id: {equals: "${result.id}"}}){id}}`, + `query webhook {webhook(filter: {id: {eq: "${result.id}"}}){id}}`, ), bundle, ); - expect(checkDbResult.data.findManyWebHook.length).toEqual(0); + expect(checkDbResult.data.webhook).toEqual(null); }); - test('should load company from web-hook', async () => { + test('should load company from webhook', async () => { const bundle = { cleanedRequest: { id: 'd6ccb1d1-a90b-4822-a992-a0dd946592c9', @@ -85,6 +84,6 @@ describe('triggers.company', () => { ); expect(results.length).toBeGreaterThan(1); const firstCompany = results[0]; - expect(firstCompany.id).toBeDefined(); + expect(firstCompany.node.id).toBeDefined(); }); }); diff --git a/packages/twenty-zapier/src/test/utils/handleQueryParams.test.ts b/packages/twenty-zapier/src/test/utils/handleQueryParams.test.ts index bdf4cae2d..174591f8c 100644 --- a/packages/twenty-zapier/src/test/utils/handleQueryParams.test.ts +++ b/packages/twenty-zapier/src/test/utils/handleQueryParams.test.ts @@ -12,8 +12,10 @@ describe('utils.handleQueryParams', () => { name: 'Company Name', address: 'Company Address', domainName: 'Company Domain Name', - linkedinUrl: 'Test linkedinUrl', - xUrl: 'Test xUrl', + linkedinUrl__url: '/linkedin_url', + linkedinUrl__label: "Test linkedinUrl", + xUrl__url: '/x_url', + xUrl__label: "Test xUrl", annualRecurringRevenue: 100000, idealCustomerProfile: true, employees: 25, @@ -23,8 +25,8 @@ describe('utils.handleQueryParams', () => { 'name: "Company Name", ' + 'address: "Company Address", ' + 'domainName: "Company Domain Name", ' + - 'linkedinUrl: "Test linkedinUrl", ' + - 'xUrl: "Test xUrl", ' + + 'linkedinUrl: {url: "/linkedin_url", label: "Test linkedinUrl"}, ' + + 'xUrl: {url: "/x_url", label: "Test xUrl"}, ' + 'annualRecurringRevenue: 100000, ' + 'idealCustomerProfile: true, ' + 'employees: 25'; diff --git a/packages/twenty-zapier/src/triggers/company.ts b/packages/twenty-zapier/src/triggers/company.ts index df6a710e0..f09b689e6 100644 --- a/packages/twenty-zapier/src/triggers/company.ts +++ b/packages/twenty-zapier/src/triggers/company.ts @@ -3,26 +3,26 @@ import requestDb from '../utils/requestDb'; import handleQueryParams from '../utils/handleQueryParams'; const performSubscribe = async (z: ZObject, bundle: Bundle) => { - const data = { targetUrl: bundle.targetUrl, operation: 'createOneCompany' }; + const data = { targetUrl: bundle.targetUrl, operation: 'company' }; const result = await requestDb( z, bundle, - `mutation createOneWebHook {createOneWebHook(data:{${handleQueryParams( + `mutation createWebhook {createWebhook(data:{${handleQueryParams( data, )}}) {id}}`, ); - return result.data.createOneWebHook; + return result.data.createWebhook; }; const performUnsubscribe = async (z: ZObject, bundle: Bundle) => { const data = { id: bundle.subscribeData?.id }; const result = await requestDb( z, bundle, - `mutation deleteOneWebHook {deleteOneWebHook(where:{${handleQueryParams( + `mutation deleteWebhook {deleteWebhook(${handleQueryParams( data, - )}}) {id}}`, + )}) {id}}`, ); - return result.data.deleteOneWebHook; + return result.data.deleteWebhook; }; const perform = (z: ZObject, bundle: Bundle) => { return [bundle.cleanedRequest]; @@ -31,20 +31,20 @@ const performList = async (z: ZObject, bundle: Bundle) => { const results = await requestDb( z, bundle, - `query FindManyCompany {findManyCompany { + `query company {companies {edges {node { id name domainName createdAt address employees - linkedinUrl - xUrl - annualRecurringRevenue + linkedinLink{label url} + xLink{label url} + annualRecurringRevenue{amountMicros currencyCode} idealCustomerProfile - }}`, + }}}}`, ); - return results.data.findManyCompany; + return results.data.companies.edges; }; export default { key: 'company', @@ -55,7 +55,7 @@ export default { }, operation: { inputFields: [], - type: 'web-hook', + type: 'hook', performSubscribe, performUnsubscribe, perform, diff --git a/packages/twenty-zapier/src/utils/handleQueryParams.ts b/packages/twenty-zapier/src/utils/handleQueryParams.ts index ff751342e..9b2eeda63 100644 --- a/packages/twenty-zapier/src/utils/handleQueryParams.ts +++ b/packages/twenty-zapier/src/utils/handleQueryParams.ts @@ -1,9 +1,26 @@ const handleQueryParams = (inputData: { [x: string]: any }): string => { - let result = ''; + const formattedInputData: {[x:string]: any} = {}; Object.keys(inputData).forEach((key) => { + if(key.includes('__')) { + const [objectKey, nestedObjectKey] = key.split('__') + if (formattedInputData[objectKey]) { + formattedInputData[objectKey][nestedObjectKey] = inputData[key] + } else { + formattedInputData[objectKey] = {[nestedObjectKey]: inputData[key]} + } + } else { + formattedInputData[key]=inputData[key] + } + }) + let result = ''; + Object.keys(formattedInputData).forEach((key) => { let quote = ''; - if (typeof inputData[key] === 'string') quote = '"'; - result = result.concat(`${key}: ${quote}${inputData[key]}${quote}, `); + if (typeof formattedInputData[key]==='object') { + result=result.concat(`${key}: {${handleQueryParams(formattedInputData[key])}}, `) + } else { + if (typeof formattedInputData[key] === 'string') quote = '"'; + result = result.concat(`${key}: ${quote}${formattedInputData[key]}${quote}, `); + } }); if (result.length) result = result.slice(0, -2); // Remove the last ', ' return result; diff --git a/packages/twenty-zapier/src/utils/requestDb.ts b/packages/twenty-zapier/src/utils/requestDb.ts index cc0b21e04..5d7bf37fe 100644 --- a/packages/twenty-zapier/src/utils/requestDb.ts +++ b/packages/twenty-zapier/src/utils/requestDb.ts @@ -20,9 +20,9 @@ const requestDb = async (z: ZObject, bundle: Bundle, query: string) => { const results = response.json; if (results.errors) { throw new z.errors.Error( - 'The API Key you supplied is incorrect', - 'AuthenticationError', - results.errors, + `query: ${query}, error: ${JSON.stringify(results.errors)}`, + 'ApiError', + response.status ); } response.throwForStatus(); @@ -30,9 +30,9 @@ const requestDb = async (z: ZObject, bundle: Bundle, query: string) => { }) .catch((err) => { throw new z.errors.Error( - 'The API Key you supplied is incorrect', - 'AuthenticationError', - err.message, + `query: ${query}, error: ${err.message}`, + 'Error', + err.status ); }); };