6071 return only updated fields of records in zapier update trigger (#8193)
- move webhook triggers into `entity-events-to-db.listener.ts` - refactor event management - add a `@OnDatabaseEvent` decorator to manage database events - add updatedFields in updated events - update openApi webhooks docs - update zapier integration
This commit is contained in:
@ -7,7 +7,7 @@ import { computeInputFields } from '../utils/computeInputFields';
|
||||
import { InputData } from '../utils/data.types';
|
||||
import handleQueryParams from '../utils/handleQueryParams';
|
||||
import requestDb, { requestSchema } from '../utils/requestDb';
|
||||
import { Operation } from '../utils/triggers/triggers.utils';
|
||||
import { DatabaseEventAction } from '../utils/triggers/triggers.utils';
|
||||
|
||||
export const recordInputFields = async (
|
||||
z: ZObject,
|
||||
@ -24,7 +24,7 @@ export const recordInputFields = async (
|
||||
const computeFields = async (z: ZObject, bundle: Bundle) => {
|
||||
const operation = bundle.inputData.crudZapierOperation;
|
||||
switch (operation) {
|
||||
case Operation.delete:
|
||||
case DatabaseEventAction.DELETED:
|
||||
return [
|
||||
{
|
||||
key: 'id',
|
||||
@ -34,9 +34,9 @@ const computeFields = async (z: ZObject, bundle: Bundle) => {
|
||||
required: true,
|
||||
},
|
||||
];
|
||||
case Operation.update:
|
||||
case DatabaseEventAction.UPDATED:
|
||||
return recordInputFields(z, bundle, true);
|
||||
case Operation.create:
|
||||
case DatabaseEventAction.CREATED:
|
||||
return recordInputFields(z, bundle, false);
|
||||
default:
|
||||
return [];
|
||||
@ -44,18 +44,18 @@ const computeFields = async (z: ZObject, bundle: Bundle) => {
|
||||
};
|
||||
|
||||
const computeQueryParameters = (
|
||||
operation: Operation,
|
||||
operation: DatabaseEventAction,
|
||||
data: InputData,
|
||||
): string => {
|
||||
switch (operation) {
|
||||
case Operation.create:
|
||||
case DatabaseEventAction.CREATED:
|
||||
return `data:{${handleQueryParams(data)}}`;
|
||||
case Operation.update:
|
||||
case DatabaseEventAction.UPDATED:
|
||||
return `
|
||||
data:{${handleQueryParams(data)}},
|
||||
id: "${data.id}"
|
||||
`;
|
||||
case Operation.delete:
|
||||
case DatabaseEventAction.DELETED:
|
||||
return `
|
||||
id: "${data.id}"
|
||||
`;
|
||||
@ -104,9 +104,9 @@ export default {
|
||||
required: true,
|
||||
label: 'Operation',
|
||||
choices: {
|
||||
[Operation.create]: Operation.create,
|
||||
[Operation.update]: Operation.update,
|
||||
[Operation.delete]: Operation.delete,
|
||||
[DatabaseEventAction.CREATED]: DatabaseEventAction.CREATED,
|
||||
[DatabaseEventAction.UPDATED]: DatabaseEventAction.UPDATED,
|
||||
[DatabaseEventAction.DELETED]: DatabaseEventAction.DELETED,
|
||||
},
|
||||
altersDynamicFields: true,
|
||||
},
|
||||
|
||||
@ -4,7 +4,7 @@ import { crudRecordKey } from '../../creates/crud_record';
|
||||
import App from '../../index';
|
||||
import getBundle from '../../utils/getBundle';
|
||||
import requestDb from '../../utils/requestDb';
|
||||
import { Operation } from '../../utils/triggers/triggers.utils';
|
||||
import { DatabaseEventAction } from '../../utils/triggers/triggers.utils';
|
||||
const appTester = createAppTester(App);
|
||||
tools.env.inject();
|
||||
|
||||
@ -12,7 +12,7 @@ describe('creates.create_company', () => {
|
||||
test('should run to create a Company Record', async () => {
|
||||
const bundle = getBundle({
|
||||
nameSingular: 'Company',
|
||||
crudZapierOperation: Operation.create,
|
||||
crudZapierOperation: DatabaseEventAction.CREATED,
|
||||
name: 'Company Name',
|
||||
address: { addressCity: 'Paris' },
|
||||
linkedinLink: {
|
||||
@ -56,7 +56,7 @@ describe('creates.create_company', () => {
|
||||
test('should run to create a Person Record', async () => {
|
||||
const bundle = getBundle({
|
||||
nameSingular: 'Person',
|
||||
crudZapierOperation: Operation.create,
|
||||
crudZapierOperation: DatabaseEventAction.CREATED,
|
||||
name: { firstName: 'John', lastName: 'Doe' },
|
||||
phones: {
|
||||
primaryPhoneNumber: '610203040',
|
||||
@ -90,7 +90,7 @@ describe('creates.update_company', () => {
|
||||
test('should run to update a Company record', async () => {
|
||||
const createBundle = getBundle({
|
||||
nameSingular: 'Company',
|
||||
crudZapierOperation: Operation.create,
|
||||
crudZapierOperation: DatabaseEventAction.CREATED,
|
||||
name: 'Company Name',
|
||||
employees: 25,
|
||||
});
|
||||
@ -104,7 +104,7 @@ describe('creates.update_company', () => {
|
||||
|
||||
const updateBundle = getBundle({
|
||||
nameSingular: 'Company',
|
||||
crudZapierOperation: Operation.update,
|
||||
crudZapierOperation: DatabaseEventAction.UPDATED,
|
||||
id: companyId,
|
||||
name: 'Updated Company Name',
|
||||
});
|
||||
@ -133,7 +133,7 @@ describe('creates.delete_company', () => {
|
||||
test('should run to delete a Company record', async () => {
|
||||
const createBundle = getBundle({
|
||||
nameSingular: 'Company',
|
||||
crudZapierOperation: Operation.create,
|
||||
crudZapierOperation: DatabaseEventAction.CREATED,
|
||||
name: 'Delete Company Name',
|
||||
employees: 25,
|
||||
});
|
||||
@ -147,7 +147,7 @@ describe('creates.delete_company', () => {
|
||||
|
||||
const deleteBundle = getBundle({
|
||||
nameSingular: 'Company',
|
||||
crudZapierOperation: Operation.delete,
|
||||
crudZapierOperation: DatabaseEventAction.DELETED,
|
||||
id: companyId,
|
||||
});
|
||||
|
||||
|
||||
@ -4,13 +4,14 @@ import App from '../../index';
|
||||
import { triggerRecordKey } from '../../triggers/trigger_record';
|
||||
import getBundle from '../../utils/getBundle';
|
||||
import requestDb from '../../utils/requestDb';
|
||||
import { DatabaseEventAction } from '../../utils/triggers/triggers.utils';
|
||||
const appTester = createAppTester(App);
|
||||
|
||||
describe('triggers.trigger_record.created', () => {
|
||||
test('should succeed to subscribe', async () => {
|
||||
const bundle = getBundle({});
|
||||
bundle.inputData.nameSingular = 'company';
|
||||
bundle.inputData.operation = 'create';
|
||||
bundle.inputData.operation = DatabaseEventAction.CREATED;
|
||||
bundle.targetUrl = 'https://test.com';
|
||||
const result = await appTester(
|
||||
App.triggers[triggerRecordKey].operation.performSubscribe,
|
||||
|
||||
@ -1,20 +1,21 @@
|
||||
import { Bundle, ZObject } from 'zapier-platform-core';
|
||||
|
||||
import { findObjectNamesSingularKey } from '../triggers/find_object_names_singular';
|
||||
import {
|
||||
listSample,
|
||||
Operation,
|
||||
perform,
|
||||
performSubscribe,
|
||||
performUnsubscribe,
|
||||
subscribe,
|
||||
perform,
|
||||
performList,
|
||||
DatabaseEventAction,
|
||||
} from '../utils/triggers/triggers.utils';
|
||||
|
||||
export const triggerRecordKey = 'trigger_record';
|
||||
|
||||
const performSubscribe = (z: ZObject, bundle: Bundle) =>
|
||||
subscribe(z, bundle, bundle.inputData.operation);
|
||||
const performList = (z: ZObject, bundle: Bundle) =>
|
||||
listSample(z, bundle, bundle.inputData.operation === Operation.delete);
|
||||
const choices = Object.values(DatabaseEventAction).reduce(
|
||||
(acc, action) => {
|
||||
acc[action] = action;
|
||||
return acc;
|
||||
},
|
||||
{} as Record<DatabaseEventAction, DatabaseEventAction>,
|
||||
);
|
||||
|
||||
export default {
|
||||
key: triggerRecordKey,
|
||||
@ -37,12 +38,7 @@ export default {
|
||||
key: 'operation',
|
||||
required: true,
|
||||
label: 'Operation',
|
||||
choices: {
|
||||
[Operation.create]: Operation.create,
|
||||
[Operation.update]: Operation.update,
|
||||
[Operation.delete]: Operation.delete,
|
||||
[Operation.destroy]: Operation.destroy,
|
||||
},
|
||||
choices,
|
||||
altersDynamicFields: true,
|
||||
},
|
||||
],
|
||||
|
||||
@ -79,7 +79,7 @@ export const requestDbViaRestApi = (
|
||||
z: ZObject,
|
||||
bundle: Bundle,
|
||||
objectNamePlural: string,
|
||||
) => {
|
||||
): Promise<Record<string, any>[]> => {
|
||||
const options = {
|
||||
url: `${
|
||||
bundle.authData.apiUrl || process.env.SERVER_BASE_URL
|
||||
|
||||
@ -7,48 +7,28 @@ import requestDb, {
|
||||
requestSchema,
|
||||
} from '../../utils/requestDb';
|
||||
|
||||
export enum Operation {
|
||||
create = 'create',
|
||||
update = 'update',
|
||||
delete = 'delete',
|
||||
destroy = 'destroy',
|
||||
export enum DatabaseEventAction {
|
||||
CREATED = 'created',
|
||||
UPDATED = 'updated',
|
||||
DELETED = 'deleted',
|
||||
DESTROYED = 'destroyed',
|
||||
}
|
||||
|
||||
export const subscribe = async (
|
||||
z: ZObject,
|
||||
bundle: Bundle,
|
||||
operation: Operation,
|
||||
) => {
|
||||
try {
|
||||
const data = {
|
||||
targetUrl: bundle.targetUrl,
|
||||
operations: [`${bundle.inputData.nameSingular}.${operation}`],
|
||||
};
|
||||
const result = await requestDb(
|
||||
z,
|
||||
bundle,
|
||||
`mutation createWebhook {createWebhook(data:{${handleQueryParams(
|
||||
data,
|
||||
)}}) {id}}`,
|
||||
);
|
||||
return result.data.createWebhook;
|
||||
} catch (e) {
|
||||
// Remove that catch code when VERSION 0.32 is deployed
|
||||
// probably removable after 01/11/2024
|
||||
// (ie: when operations column exists in all active workspace schemas)
|
||||
const data = {
|
||||
targetUrl: bundle.targetUrl,
|
||||
operation: `${bundle.inputData.nameSingular}.${operation}`,
|
||||
};
|
||||
const result = await requestDb(
|
||||
z,
|
||||
bundle,
|
||||
`mutation createWebhook {createWebhook(data:{${handleQueryParams(
|
||||
data,
|
||||
)}}) {id}}`,
|
||||
);
|
||||
return result.data.createWebhook;
|
||||
}
|
||||
export const performSubscribe = async (z: ZObject, bundle: Bundle) => {
|
||||
const data = {
|
||||
targetUrl: bundle.targetUrl,
|
||||
operations: [
|
||||
`${bundle.inputData.nameSingular}.${bundle.inputData.operation}`,
|
||||
],
|
||||
};
|
||||
const result = await requestDb(
|
||||
z,
|
||||
bundle,
|
||||
`mutation createWebhook {createWebhook(data:{${handleQueryParams(
|
||||
data,
|
||||
)}}) {id}}`,
|
||||
);
|
||||
return result.data.createWebhook;
|
||||
};
|
||||
|
||||
export const performUnsubscribe = async (z: ZObject, bundle: Bundle) => {
|
||||
@ -62,20 +42,26 @@ export const performUnsubscribe = async (z: ZObject, bundle: Bundle) => {
|
||||
};
|
||||
|
||||
export const perform = (z: ZObject, bundle: Bundle) => {
|
||||
const record = bundle.cleanedRequest.record;
|
||||
if (record.createdAt) {
|
||||
record.createdAt = record.createdAt + 'Z';
|
||||
const data = {
|
||||
record: bundle.cleanedRequest.record,
|
||||
...(bundle.cleanedRequest.updatedFields && {
|
||||
updatedFields: bundle.cleanedRequest.updatedFields,
|
||||
}),
|
||||
};
|
||||
if (data.record.createdAt) {
|
||||
data.record.createdAt = data.record.createdAt + 'Z';
|
||||
}
|
||||
if (record.updatedAt) {
|
||||
record.updatedAt = record.updatedAt + 'Z';
|
||||
if (data.record.updatedAt) {
|
||||
data.record.updatedAt = data.record.updatedAt + 'Z';
|
||||
}
|
||||
if (record.revokedAt) {
|
||||
record.revokedAt = record.revokedAt + 'Z';
|
||||
if (data.record.revokedAt) {
|
||||
data.record.revokedAt = data.record.revokedAt + 'Z';
|
||||
}
|
||||
if (record.expiresAt) {
|
||||
record.expiresAt = record.expiresAt + 'Z';
|
||||
if (data.record.expiresAt) {
|
||||
data.record.expiresAt = data.record.expiresAt + 'Z';
|
||||
}
|
||||
return [record];
|
||||
|
||||
return [data];
|
||||
};
|
||||
|
||||
const getNamePluralFromNameSingular = async (
|
||||
@ -92,10 +78,9 @@ const getNamePluralFromNameSingular = async (
|
||||
throw new Error(`Unknown Object Name Singular ${nameSingular}`);
|
||||
};
|
||||
|
||||
export const listSample = async (
|
||||
export const performList = async (
|
||||
z: ZObject,
|
||||
bundle: Bundle,
|
||||
onlyIds = false,
|
||||
): Promise<ObjectData[]> => {
|
||||
const nameSingular = bundle.inputData.nameSingular;
|
||||
const namePlural = await getNamePluralFromNameSingular(
|
||||
@ -103,19 +88,13 @@ export const listSample = async (
|
||||
bundle,
|
||||
nameSingular,
|
||||
);
|
||||
const result: { [key: string]: string }[] = await requestDbViaRestApi(
|
||||
z,
|
||||
bundle,
|
||||
namePlural,
|
||||
);
|
||||
|
||||
if (onlyIds) {
|
||||
return result.map((res) => {
|
||||
return {
|
||||
id: res.id,
|
||||
};
|
||||
});
|
||||
}
|
||||
|
||||
return result;
|
||||
const results = await requestDbViaRestApi(z, bundle, namePlural);
|
||||
return results.map((result) => ({
|
||||
record: result,
|
||||
...(bundle.inputData.operation === DatabaseEventAction.UPDATED && {
|
||||
updatedFields: Object.keys(result).filter((key) => key !== 'id')?.[0] || [
|
||||
'updatedField',
|
||||
],
|
||||
}),
|
||||
}));
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user