Fix zapier (#2735)

* Fix zapier tests

* Handle nested fields

* Code review returns
This commit is contained in:
martmull
2023-11-27 18:09:21 +01:00
committed by GitHub
parent e2e871ca32
commit a413b29dd4
10 changed files with 137 additions and 80 deletions

View File

@ -4,8 +4,8 @@ import requestDb from '../utils/requestDb';
const perform = async (z: ZObject, bundle: Bundle) => { const perform = async (z: ZObject, bundle: Bundle) => {
const query = ` const query = `
mutation CreateCompany { mutation createCompany {
createOneCompany( createCompany(
data:{${handleQueryParams(bundle.inputData)}} data:{${handleQueryParams(bundle.inputData)}}
) )
{id} {id}
@ -47,29 +47,53 @@ export default {
altersDynamicFields: false, altersDynamicFields: false,
}, },
{ {
key: 'linkedinUrl', key: 'linkedinLink__url',
label: 'Linkedin', label: 'Linkedin Link Url',
type: 'string', type: 'string',
required: false, required: false,
list: false, list: false,
altersDynamicFields: false, altersDynamicFields: false,
}, },
{ {
key: 'xUrl', key: 'linkedinLink__label',
label: 'Twitter', label: 'Linkedin Link Label',
type: 'string', type: 'string',
required: false, required: false,
list: false, list: false,
altersDynamicFields: false, altersDynamicFields: false,
}, },
{ {
key: 'annualRecurringRevenue', key: 'xLink__url',
label: 'ARR (Annual Recurring Revenue)', 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', type: 'number',
required: false, required: false,
list: false, list: false,
altersDynamicFields: false, altersDynamicFields: false,
}, },
{
key: 'annualRecurringRevenue__currencyCode',
label: 'ARR (Annual Recurring Revenue) currency Code',
type: 'string',
required: false,
list: false,
altersDynamicFields: false,
},
{ {
key: 'idealCustomerProfile', key: 'idealCustomerProfile',
label: 'ICP (Ideal Customer Profile)', label: 'ICP (Ideal Customer Profile)',

View File

@ -4,8 +4,8 @@ import requestDb from '../utils/requestDb';
const perform = async (z: ZObject, bundle: Bundle) => { const perform = async (z: ZObject, bundle: Bundle) => {
const query = ` const query = `
mutation CreatePerson { mutation createPerson {
createOnePerson( createPerson(
data:{${handleQueryParams(bundle.inputData)}} data:{${handleQueryParams(bundle.inputData)}}
) )
{id} {id}
@ -23,15 +23,15 @@ export default {
operation: { operation: {
inputFields: [ inputFields: [
{ {
key: 'firstName', key: 'name__firstName',
label: 'First Name', label: 'First Name',
type: 'string', type: 'string',
required: true, required: false,
list: false, list: false,
altersDynamicFields: false, altersDynamicFields: false,
}, },
{ {
key: 'lastName', key: 'name__lastName',
label: 'Last Name', label: 'Last Name',
type: 'string', type: 'string',
required: false, required: false,
@ -64,8 +64,8 @@ export default {
}, },
], ],
sample: { sample: {
firstName: 'John', name__firstName: 'John',
lastName: 'Doe', name__lastName: 'Doe',
email: 'johndoe@gmail.com', email: 'johndoe@gmail.com',
}, },
perform, perform,

View File

@ -12,15 +12,26 @@ import requestDb from '../utils/requestDb';
const appTester = createAppTester(App); const appTester = createAppTester(App);
tools.env.inject(); tools.env.inject();
const generateKey = async (z: ZObject, bundle: Bundle) => { const createApiKey = async (z: ZObject, bundle: Bundle) => {
const query = ` const query = `
mutation CreateApiKey { mutation createApiKey {
createOneApiKey( createApiKey(
data:{${handleQueryParams(bundle.inputData)}} 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} {token}
}`; }`;
return (await requestDb(z, bundle, query)).data.createOneApiKey.token; return (await requestDb(z, bundle, query)).data.generateApiKeyToken.token;
}; };
describe('custom auth', () => { describe('custom auth', () => {
@ -37,18 +48,24 @@ describe('custom auth', () => {
try { try {
await appTester(App.authentication.test, bundle); await appTester(App.authentication.test, bundle);
} catch (error: any) { } catch (error: any) {
expect(error.message).toContain('The API Key you supplied is incorrect'); expect(error.message).toContain('UNAUTHENTICATED');
return; return;
} }
throw new Error('appTester should have thrown'); throw new Error('appTester should have thrown');
}); });
it('fails on invalid auth token', async () => { it('fails on invalid auth token', async () => {
const bundle = getBundle({ const expiresAt = '2020-01-01 10:10:10.000'
const apiKeyBundle = getBundle({
name: 'Test', 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 = { const bundleWithExpiredApiKey = {
authData: { apiKey: expiredToken }, authData: { apiKey: expiredToken },
}; };
@ -56,7 +73,7 @@ describe('custom auth', () => {
try { try {
await appTester(App.authentication.test, bundleWithExpiredApiKey); await appTester(App.authentication.test, bundleWithExpiredApiKey);
} catch (error: any) { } catch (error: any) {
expect(error.message).toContain('The API Key you supplied is incorrect'); expect(error.message).toContain('UNAUTHENTICATED');
return; return;
} }
throw new Error('appTester should have thrown'); throw new Error('appTester should have thrown');

View File

@ -11,9 +11,9 @@ describe('creates.create_company', () => {
name: 'Company Name', name: 'Company Name',
address: 'Company Address', address: 'Company Address',
domainName: 'Company Domain Name', domainName: 'Company Domain Name',
linkedinUrl: 'Test linkedinUrl', linkedinLink: {url: '/linkedin_url', label: "Test linkedinUrl"},
xUrl: 'Test xUrl', xLink: {url: '/x_url', label: "Test xUrl"},
annualRecurringRevenue: 100000, annualRecurringRevenue: {amountMicros:100000000000,currencyCode: 'USD'},
idealCustomerProfile: true, idealCustomerProfile: true,
employees: 25, employees: 25,
}); });
@ -22,18 +22,18 @@ describe('creates.create_company', () => {
bundle, bundle,
); );
expect(result).toBeDefined(); expect(result).toBeDefined();
expect(result.data?.createOneCompany?.id).toBeDefined(); expect(result.data?.createCompany?.id).toBeDefined();
const checkDbResult = await appTester( const checkDbResult = await appTester(
(z: ZObject, bundle: Bundle) => (z: ZObject, bundle: Bundle) =>
requestDb( requestDb(
z, z,
bundle, 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, bundle,
); );
expect(checkDbResult.data.findUniqueCompany.annualRecurringRevenue).toEqual( expect(checkDbResult.data.company.annualRecurringRevenue.amountMicros).toEqual(
100000, 100000000000,
); );
}); });
test('should run with not required missing params', async () => { test('should run with not required missing params', async () => {
@ -41,8 +41,8 @@ describe('creates.create_company', () => {
name: 'Company Name', name: 'Company Name',
address: 'Company Address', address: 'Company Address',
domainName: 'Company Domain Name', domainName: 'Company Domain Name',
linkedinUrl: 'Test linkedinUrl', linkedinLink: {url: '/linkedin_url', label: "Test linkedinUrl"},
xUrl: 'Test xUrl', xLink: {url: '/x_url', label: "Test xUrl"},
idealCustomerProfile: true, idealCustomerProfile: true,
employees: 25, employees: 25,
}); });
@ -51,17 +51,17 @@ describe('creates.create_company', () => {
bundle, bundle,
); );
expect(result).toBeDefined(); expect(result).toBeDefined();
expect(result.data?.createOneCompany?.id).toBeDefined(); expect(result.data?.createCompany?.id).toBeDefined();
const checkDbResult = await appTester( const checkDbResult = await appTester(
(z: ZObject, bundle: Bundle) => (z: ZObject, bundle: Bundle) =>
requestDb( requestDb(
z, z,
bundle, 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, bundle,
); );
expect(checkDbResult.data.findUniqueCompany.annualRecurringRevenue).toEqual( expect(checkDbResult.data.company.annualRecurringRevenue.amountMicros).toEqual(
null, null,
); );
}); });

View File

@ -8,8 +8,7 @@ tools.env.inject();
describe('creates.create_person', () => { describe('creates.create_person', () => {
test('should run', async () => { test('should run', async () => {
const bundle = getBundle({ const bundle = getBundle({
firstName: 'John', name: {firstName: 'John', lastName: 'Doe'},
lastName: 'Doe',
email: 'johndoe@gmail.com', email: 'johndoe@gmail.com',
phone: '+33610203040', phone: '+33610203040',
city: 'Paris', city: 'Paris',
@ -19,23 +18,22 @@ describe('creates.create_person', () => {
bundle, bundle,
); );
expect(results).toBeDefined(); expect(results).toBeDefined();
expect(results.data?.createOnePerson?.id).toBeDefined(); expect(results.data?.createPerson?.id).toBeDefined();
const checkDbResult = await appTester( const checkDbResult = await appTester(
(z: ZObject, bundle: Bundle) => (z: ZObject, bundle: Bundle) =>
requestDb( requestDb(
z, z,
bundle, bundle,
`query findPerson {findUniquePerson(id: "${results.data.createOnePerson.id}"){id, phone}}`, `query findPerson {person(filter: {id: {eq: "${results.data.createPerson.id}"}}){phone}}`,
), ),
bundle, bundle,
); );
expect(checkDbResult.data.findUniquePerson.phone).toEqual('+33610203040'); expect(checkDbResult.data.person.phone).toEqual('+33610203040');
}); });
test('should run with not required missing params', async () => { test('should run with not required missing params', async () => {
const bundle = getBundle({ const bundle = getBundle({
firstName: 'John', name: {firstName: 'John', lastName: 'Doe'},
lastName: 'Doe',
email: 'johndoe@gmail.com', email: 'johndoe@gmail.com',
city: 'Paris', city: 'Paris',
}); });
@ -44,16 +42,16 @@ describe('creates.create_person', () => {
bundle, bundle,
); );
expect(results).toBeDefined(); expect(results).toBeDefined();
expect(results.data?.createOnePerson?.id).toBeDefined(); expect(results.data?.createPerson?.id).toBeDefined();
const checkDbResult = await appTester( const checkDbResult = await appTester(
(z: ZObject, bundle: Bundle) => (z: ZObject, bundle: Bundle) =>
requestDb( requestDb(
z, z,
bundle, bundle,
`query findPerson {findUniquePerson(id: "${results.data.createOnePerson.id}"){id, phone}}`, `query findPerson {person(filter: {id: {eq: "${results.data.createPerson.id}"}}){phone}}`,
), ),
bundle, bundle,
); );
expect(checkDbResult.data.findUniquePerson.phone).toEqual(null); expect(checkDbResult.data.person.phone).toEqual("");
}); });
}); });

View File

@ -19,13 +19,12 @@ describe('triggers.company', () => {
requestDb( requestDb(
z, z,
bundle, bundle,
`query findManyWebHook {findManyWebHook(where: {id: {equals: "${result.id}"}}){id operation}}`, `query webhook {webhook(filter: {id: {eq: "${result.id}"}}){id operation}}`,
), ),
bundle, bundle,
); );
expect(checkDbResult.data.findManyWebHook.length).toEqual(1); expect(checkDbResult.data.webhook.operation).toEqual(
expect(checkDbResult.data.findManyWebHook[0].operation).toEqual( 'company',
'createOneCompany',
); );
}); });
test('should succeed to unsubscribe', async () => { test('should succeed to unsubscribe', async () => {
@ -48,13 +47,13 @@ describe('triggers.company', () => {
requestDb( requestDb(
z, z,
bundle, bundle,
`query findManyWebHook {findManyWebHook(where: {id: {equals: "${result.id}"}}){id}}`, `query webhook {webhook(filter: {id: {eq: "${result.id}"}}){id}}`,
), ),
bundle, 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 = { const bundle = {
cleanedRequest: { cleanedRequest: {
id: 'd6ccb1d1-a90b-4822-a992-a0dd946592c9', id: 'd6ccb1d1-a90b-4822-a992-a0dd946592c9',
@ -85,6 +84,6 @@ describe('triggers.company', () => {
); );
expect(results.length).toBeGreaterThan(1); expect(results.length).toBeGreaterThan(1);
const firstCompany = results[0]; const firstCompany = results[0];
expect(firstCompany.id).toBeDefined(); expect(firstCompany.node.id).toBeDefined();
}); });
}); });

View File

@ -12,8 +12,10 @@ describe('utils.handleQueryParams', () => {
name: 'Company Name', name: 'Company Name',
address: 'Company Address', address: 'Company Address',
domainName: 'Company Domain Name', domainName: 'Company Domain Name',
linkedinUrl: 'Test linkedinUrl', linkedinUrl__url: '/linkedin_url',
xUrl: 'Test xUrl', linkedinUrl__label: "Test linkedinUrl",
xUrl__url: '/x_url',
xUrl__label: "Test xUrl",
annualRecurringRevenue: 100000, annualRecurringRevenue: 100000,
idealCustomerProfile: true, idealCustomerProfile: true,
employees: 25, employees: 25,
@ -23,8 +25,8 @@ describe('utils.handleQueryParams', () => {
'name: "Company Name", ' + 'name: "Company Name", ' +
'address: "Company Address", ' + 'address: "Company Address", ' +
'domainName: "Company Domain Name", ' + 'domainName: "Company Domain Name", ' +
'linkedinUrl: "Test linkedinUrl", ' + 'linkedinUrl: {url: "/linkedin_url", label: "Test linkedinUrl"}, ' +
'xUrl: "Test xUrl", ' + 'xUrl: {url: "/x_url", label: "Test xUrl"}, ' +
'annualRecurringRevenue: 100000, ' + 'annualRecurringRevenue: 100000, ' +
'idealCustomerProfile: true, ' + 'idealCustomerProfile: true, ' +
'employees: 25'; 'employees: 25';

View File

@ -3,26 +3,26 @@ import requestDb from '../utils/requestDb';
import handleQueryParams from '../utils/handleQueryParams'; import handleQueryParams from '../utils/handleQueryParams';
const performSubscribe = async (z: ZObject, bundle: Bundle) => { 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( const result = await requestDb(
z, z,
bundle, bundle,
`mutation createOneWebHook {createOneWebHook(data:{${handleQueryParams( `mutation createWebhook {createWebhook(data:{${handleQueryParams(
data, data,
)}}) {id}}`, )}}) {id}}`,
); );
return result.data.createOneWebHook; return result.data.createWebhook;
}; };
const performUnsubscribe = async (z: ZObject, bundle: Bundle) => { const performUnsubscribe = async (z: ZObject, bundle: Bundle) => {
const data = { id: bundle.subscribeData?.id }; const data = { id: bundle.subscribeData?.id };
const result = await requestDb( const result = await requestDb(
z, z,
bundle, bundle,
`mutation deleteOneWebHook {deleteOneWebHook(where:{${handleQueryParams( `mutation deleteWebhook {deleteWebhook(${handleQueryParams(
data, data,
)}}) {id}}`, )}) {id}}`,
); );
return result.data.deleteOneWebHook; return result.data.deleteWebhook;
}; };
const perform = (z: ZObject, bundle: Bundle) => { const perform = (z: ZObject, bundle: Bundle) => {
return [bundle.cleanedRequest]; return [bundle.cleanedRequest];
@ -31,20 +31,20 @@ const performList = async (z: ZObject, bundle: Bundle) => {
const results = await requestDb( const results = await requestDb(
z, z,
bundle, bundle,
`query FindManyCompany {findManyCompany { `query company {companies {edges {node {
id id
name name
domainName domainName
createdAt createdAt
address address
employees employees
linkedinUrl linkedinLink{label url}
xUrl xLink{label url}
annualRecurringRevenue annualRecurringRevenue{amountMicros currencyCode}
idealCustomerProfile idealCustomerProfile
}}`, }}}}`,
); );
return results.data.findManyCompany; return results.data.companies.edges;
}; };
export default { export default {
key: 'company', key: 'company',
@ -55,7 +55,7 @@ export default {
}, },
operation: { operation: {
inputFields: [], inputFields: [],
type: 'web-hook', type: 'hook',
performSubscribe, performSubscribe,
performUnsubscribe, performUnsubscribe,
perform, perform,

View File

@ -1,9 +1,26 @@
const handleQueryParams = (inputData: { [x: string]: any }): string => { const handleQueryParams = (inputData: { [x: string]: any }): string => {
let result = ''; const formattedInputData: {[x:string]: any} = {};
Object.keys(inputData).forEach((key) => { 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 = ''; let quote = '';
if (typeof inputData[key] === 'string') quote = '"'; if (typeof formattedInputData[key]==='object') {
result = result.concat(`${key}: ${quote}${inputData[key]}${quote}, `); 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 ', ' if (result.length) result = result.slice(0, -2); // Remove the last ', '
return result; return result;

View File

@ -20,9 +20,9 @@ const requestDb = async (z: ZObject, bundle: Bundle, query: string) => {
const results = response.json; const results = response.json;
if (results.errors) { if (results.errors) {
throw new z.errors.Error( throw new z.errors.Error(
'The API Key you supplied is incorrect', `query: ${query}, error: ${JSON.stringify(results.errors)}`,
'AuthenticationError', 'ApiError',
results.errors, response.status
); );
} }
response.throwForStatus(); response.throwForStatus();
@ -30,9 +30,9 @@ const requestDb = async (z: ZObject, bundle: Bundle, query: string) => {
}) })
.catch((err) => { .catch((err) => {
throw new z.errors.Error( throw new z.errors.Error(
'The API Key you supplied is incorrect', `query: ${query}, error: ${err.message}`,
'AuthenticationError', 'Error',
err.message, err.status
); );
}); });
}; };