Zapier add description to labels (#3787)
* Use object metadata graphql api to fetch input fields * Clean code * Clean code * Remove targetColumnMap * Remove duplicated testing * Fix labels
This commit is contained in:
@ -3,12 +3,24 @@ import { Bundle, ZObject } from 'zapier-platform-core';
|
|||||||
import { findObjectNamesSingularKey } from '../triggers/find_object_names_singular';
|
import { findObjectNamesSingularKey } from '../triggers/find_object_names_singular';
|
||||||
import { listRecordIdsKey } from '../triggers/list_record_ids';
|
import { listRecordIdsKey } from '../triggers/list_record_ids';
|
||||||
import { capitalize } from '../utils/capitalize';
|
import { capitalize } from '../utils/capitalize';
|
||||||
import { recordInputFields } from '../utils/creates/creates.utils';
|
import { computeInputFields } from '../utils/computeInputFields';
|
||||||
import { InputData } from '../utils/data.types';
|
import { InputData } from '../utils/data.types';
|
||||||
import handleQueryParams from '../utils/handleQueryParams';
|
import handleQueryParams from '../utils/handleQueryParams';
|
||||||
import requestDb from '../utils/requestDb';
|
import requestDb, { requestSchema } from '../utils/requestDb';
|
||||||
import { Operation } from '../utils/triggers/triggers.utils';
|
import { Operation } from '../utils/triggers/triggers.utils';
|
||||||
|
|
||||||
|
export const recordInputFields = async (
|
||||||
|
z: ZObject,
|
||||||
|
bundle: Bundle,
|
||||||
|
idRequired = false,
|
||||||
|
) => {
|
||||||
|
const schema = await requestSchema(z, bundle);
|
||||||
|
const node = schema.data.objects.edges.filter(
|
||||||
|
(edge) => edge.node.nameSingular === bundle.inputData.nameSingular,
|
||||||
|
)[0].node;
|
||||||
|
return computeInputFields(node, idRequired);
|
||||||
|
};
|
||||||
|
|
||||||
const computeFields = async (z: ZObject, bundle: Bundle) => {
|
const computeFields = async (z: ZObject, bundle: Bundle) => {
|
||||||
const operation = bundle.inputData.crudZapierOperation;
|
const operation = bundle.inputData.crudZapierOperation;
|
||||||
switch (operation) {
|
switch (operation) {
|
||||||
@ -84,7 +96,7 @@ export default {
|
|||||||
key: 'nameSingular',
|
key: 'nameSingular',
|
||||||
required: true,
|
required: true,
|
||||||
label: 'Record Name',
|
label: 'Record Name',
|
||||||
dynamic: `${findObjectNamesSingularKey}.nameSingular`,
|
dynamic: `${findObjectNamesSingularKey}.nameSingular.labelSingular`,
|
||||||
altersDynamicFields: true,
|
altersDynamicFields: true,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
|||||||
@ -1,78 +1,223 @@
|
|||||||
import { computeInputFields } from '../../utils/computeInputFields';
|
import { computeInputFields } from '../../utils/computeInputFields';
|
||||||
|
import { InputField } from '../../utils/data.types';
|
||||||
|
|
||||||
describe('computeInputFields', () => {
|
describe('computeInputFields', () => {
|
||||||
test('should create Person input fields properly', () => {
|
test('should create Person input fields properly', () => {
|
||||||
const personInfos = {
|
const personNode = {
|
||||||
type: 'object',
|
nameSingular: 'person',
|
||||||
properties: {
|
namePlural: 'people',
|
||||||
id: {
|
labelSingular: 'Person',
|
||||||
type: 'string',
|
fields: {
|
||||||
},
|
edges: [
|
||||||
email: {
|
{
|
||||||
type: 'string',
|
node: {
|
||||||
},
|
type: 'RELATION',
|
||||||
xLink: {
|
name: 'favorites',
|
||||||
type: 'object',
|
label: 'Favorites',
|
||||||
properties: {
|
description: 'Favorites linked to the contact',
|
||||||
url: {
|
isNullable: true,
|
||||||
type: 'string',
|
defaultValue: null,
|
||||||
},
|
|
||||||
label: {
|
|
||||||
type: 'string',
|
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
{
|
||||||
avatarUrl: {
|
node: {
|
||||||
type: 'string',
|
type: 'CURRENCY',
|
||||||
},
|
name: 'annualSalary',
|
||||||
favorites: {
|
label: 'Annual Salary',
|
||||||
type: 'array',
|
description: 'Annual Salary of the Person',
|
||||||
items: {
|
isNullable: true,
|
||||||
$ref: '#/components/schemas/Favorite',
|
defaultValue: null,
|
||||||
|
},
|
||||||
},
|
},
|
||||||
},
|
{
|
||||||
|
node: {
|
||||||
|
type: 'TEXT',
|
||||||
|
name: 'jobTitle',
|
||||||
|
label: 'Job Title',
|
||||||
|
description: 'Contact’s job title',
|
||||||
|
isNullable: false,
|
||||||
|
defaultValue: {
|
||||||
|
value: '',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
node: {
|
||||||
|
type: 'DATE_TIME',
|
||||||
|
name: 'updatedAt',
|
||||||
|
label: 'Update date',
|
||||||
|
description: null,
|
||||||
|
isNullable: false,
|
||||||
|
defaultValue: {
|
||||||
|
type: 'now',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
node: {
|
||||||
|
type: 'FULL_NAME',
|
||||||
|
name: 'name',
|
||||||
|
label: 'Name',
|
||||||
|
description: 'Contact’s name',
|
||||||
|
isNullable: true,
|
||||||
|
defaultValue: {
|
||||||
|
lastName: '',
|
||||||
|
firstName: '',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
node: {
|
||||||
|
type: 'UUID',
|
||||||
|
name: 'id',
|
||||||
|
label: 'Id',
|
||||||
|
description: null,
|
||||||
|
icon: null,
|
||||||
|
isNullable: false,
|
||||||
|
defaultValue: {
|
||||||
|
type: 'uuid',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
node: {
|
||||||
|
type: 'NUMBER',
|
||||||
|
name: 'recordPosition',
|
||||||
|
label: 'RecordPosition',
|
||||||
|
description: 'Record Position',
|
||||||
|
isNullable: true,
|
||||||
|
defaultValue: null,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
node: {
|
||||||
|
type: 'LINK',
|
||||||
|
name: 'xLink',
|
||||||
|
label: 'X',
|
||||||
|
description: 'Contact’s X/Twitter account',
|
||||||
|
isNullable: true,
|
||||||
|
defaultValue: null,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
node: {
|
||||||
|
type: 'EMAIL',
|
||||||
|
name: 'email',
|
||||||
|
label: 'Email',
|
||||||
|
description: 'Contact’s Email',
|
||||||
|
isNullable: false,
|
||||||
|
defaultValue: {
|
||||||
|
value: '',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
node: {
|
||||||
|
type: 'UUID',
|
||||||
|
name: 'companyId',
|
||||||
|
label: 'Company id (foreign key)',
|
||||||
|
description: 'Contact’s company id foreign key',
|
||||||
|
isNullable: true,
|
||||||
|
defaultValue: null,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
],
|
||||||
},
|
},
|
||||||
example: {},
|
|
||||||
required: ['avatarUrl'],
|
|
||||||
};
|
};
|
||||||
expect(computeInputFields(personInfos)).toEqual([
|
const baseExpectedResult: InputField[] = [
|
||||||
{ key: 'id', label: 'Id', required: false, type: 'string' },
|
{
|
||||||
{ key: 'email', label: 'Email', required: false, type: 'string' },
|
key: 'annualSalary__amountMicros',
|
||||||
|
label: 'Annual Salary: Amount Micros',
|
||||||
|
type: 'integer',
|
||||||
|
helpText:
|
||||||
|
'Annual Salary of the Person: Amount Micros. eg: set 3210000 for 3.21$',
|
||||||
|
required: false,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: 'annualSalary__currencyCode',
|
||||||
|
label: 'Annual Salary: Currency Code',
|
||||||
|
type: 'string',
|
||||||
|
helpText:
|
||||||
|
'Annual Salary of the Person: Currency Code. eg: USD, EUR, etc...',
|
||||||
|
required: false,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: 'jobTitle',
|
||||||
|
label: 'Job Title',
|
||||||
|
type: 'string',
|
||||||
|
helpText: 'Contact’s job title',
|
||||||
|
required: false,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: 'updatedAt',
|
||||||
|
label: 'Update date',
|
||||||
|
type: 'datetime',
|
||||||
|
helpText: null,
|
||||||
|
required: false,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: 'name__firstName',
|
||||||
|
label: 'Name: First Name',
|
||||||
|
type: 'string',
|
||||||
|
helpText: 'Contact’s name: First Name',
|
||||||
|
required: false,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: 'name__lastName',
|
||||||
|
label: 'Name: Last Name',
|
||||||
|
type: 'string',
|
||||||
|
helpText: 'Contact’s name: Last Name',
|
||||||
|
required: false,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: 'recordPosition',
|
||||||
|
label: 'RecordPosition',
|
||||||
|
type: 'integer',
|
||||||
|
helpText: 'Record Position',
|
||||||
|
required: false,
|
||||||
|
},
|
||||||
{
|
{
|
||||||
key: 'xLink__url',
|
key: 'xLink__url',
|
||||||
label: 'X Link: Url',
|
label: 'X: Url',
|
||||||
required: false,
|
|
||||||
type: 'string',
|
type: 'string',
|
||||||
|
helpText: 'Contact’s X/Twitter account: Link Url',
|
||||||
|
required: false,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
key: 'xLink__label',
|
key: 'xLink__label',
|
||||||
label: 'X Link: Label',
|
label: 'X: Label',
|
||||||
required: false,
|
|
||||||
type: 'string',
|
type: 'string',
|
||||||
},
|
helpText: 'Contact’s X/Twitter account: Link Label',
|
||||||
{ 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,
|
required: false,
|
||||||
type: 'string',
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
key: 'xLink__label',
|
key: 'email',
|
||||||
label: 'X Link: Label',
|
label: 'Email',
|
||||||
required: false,
|
|
||||||
type: 'string',
|
type: 'string',
|
||||||
|
helpText: 'Contact’s Email',
|
||||||
|
required: false,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
key: 'avatarUrl',
|
key: 'companyId',
|
||||||
label: 'Avatar Url',
|
label: 'Company id (foreign key)',
|
||||||
required: false,
|
|
||||||
type: 'string',
|
type: 'string',
|
||||||
|
helpText: 'Contact’s company id foreign key',
|
||||||
|
required: false,
|
||||||
},
|
},
|
||||||
]);
|
];
|
||||||
|
const idInputField: InputField = {
|
||||||
|
key: 'id',
|
||||||
|
label: 'Id',
|
||||||
|
type: 'string',
|
||||||
|
helpText: null,
|
||||||
|
required: false,
|
||||||
|
};
|
||||||
|
const expectedResult = [idInputField].concat(baseExpectedResult);
|
||||||
|
expect(computeInputFields(personNode)).toEqual(expectedResult);
|
||||||
|
idInputField.required = true;
|
||||||
|
const idRequiredExpectedResult = [idInputField].concat(baseExpectedResult);
|
||||||
|
expect(computeInputFields(personNode, true)).toEqual(
|
||||||
|
idRequiredExpectedResult,
|
||||||
|
);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
@ -1,7 +0,0 @@
|
|||||||
import { labelling } from '../../utils/labelling';
|
|
||||||
|
|
||||||
describe('labelling', () => {
|
|
||||||
test('should label properly', () => {
|
|
||||||
expect(labelling('createdAt')).toEqual('Created At');
|
|
||||||
});
|
|
||||||
});
|
|
||||||
@ -4,8 +4,13 @@ import { requestSchema } from '../utils/requestDb';
|
|||||||
|
|
||||||
const objectListRequest = async (z: ZObject, bundle: Bundle) => {
|
const objectListRequest = async (z: ZObject, bundle: Bundle) => {
|
||||||
const schema = await requestSchema(z, bundle);
|
const schema = await requestSchema(z, bundle);
|
||||||
return Object.keys(schema.components.schemas).map((schema) => {
|
return schema.data.objects.edges.map((edge: any) => {
|
||||||
return { id: schema, nameSingular: schema.toLowerCase() };
|
const object = edge.node;
|
||||||
|
return {
|
||||||
|
id: object.nameSingular,
|
||||||
|
nameSingular: object.nameSingular,
|
||||||
|
labelSingular: object.labelSingular,
|
||||||
|
};
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@ -29,7 +29,7 @@ export default {
|
|||||||
key: 'nameSingular',
|
key: 'nameSingular',
|
||||||
required: true,
|
required: true,
|
||||||
label: 'Record Name',
|
label: 'Record Name',
|
||||||
dynamic: `${findObjectNamesSingularKey}.nameSingular`,
|
dynamic: `${findObjectNamesSingularKey}.nameSingular.labelSingular`,
|
||||||
altersDynamicFields: true,
|
altersDynamicFields: true,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
|||||||
@ -1,63 +1,156 @@
|
|||||||
import { labelling } from '../utils/labelling';
|
import {
|
||||||
|
FieldMetadataType,
|
||||||
|
InputField,
|
||||||
|
Node,
|
||||||
|
NodeField,
|
||||||
|
} from '../utils/data.types';
|
||||||
|
|
||||||
type Infos = {
|
const getTypeFromFieldMetadataType = (
|
||||||
properties: {
|
fieldMetadataType: string,
|
||||||
[field: string]: {
|
): string | undefined => {
|
||||||
type: string;
|
switch (fieldMetadataType) {
|
||||||
properties?: { [field: string]: { type: string } };
|
case FieldMetadataType.UUID:
|
||||||
items?: { [$ref: string]: string };
|
case FieldMetadataType.TEXT:
|
||||||
};
|
case FieldMetadataType.PHONE:
|
||||||
};
|
case FieldMetadataType.EMAIL:
|
||||||
example: object;
|
case FieldMetadataType.LINK:
|
||||||
required: string[];
|
case FieldMetadataType.RATING:
|
||||||
|
return 'string';
|
||||||
|
case FieldMetadataType.DATE_TIME:
|
||||||
|
return 'datetime';
|
||||||
|
case FieldMetadataType.BOOLEAN:
|
||||||
|
return 'boolean';
|
||||||
|
case FieldMetadataType.NUMBER:
|
||||||
|
return 'integer';
|
||||||
|
case FieldMetadataType.NUMERIC:
|
||||||
|
case FieldMetadataType.PROBABILITY:
|
||||||
|
return 'number';
|
||||||
|
default:
|
||||||
|
return undefined;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const get_subfieldsFromField = (nodeField: NodeField): NodeField[] => {
|
||||||
|
switch (nodeField.type) {
|
||||||
|
case FieldMetadataType.FULL_NAME: {
|
||||||
|
const firstName: NodeField = {
|
||||||
|
type: 'TEXT',
|
||||||
|
name: 'firstName',
|
||||||
|
label: 'First Name',
|
||||||
|
description: 'First Name',
|
||||||
|
isNullable: true,
|
||||||
|
defaultValue: null,
|
||||||
|
};
|
||||||
|
const lastName: NodeField = {
|
||||||
|
type: 'TEXT',
|
||||||
|
name: 'lastName',
|
||||||
|
label: 'Last Name',
|
||||||
|
description: 'Last Name',
|
||||||
|
isNullable: true,
|
||||||
|
defaultValue: null,
|
||||||
|
};
|
||||||
|
return [firstName, lastName];
|
||||||
|
}
|
||||||
|
case FieldMetadataType.LINK: {
|
||||||
|
const url: NodeField = {
|
||||||
|
type: 'TEXT',
|
||||||
|
name: 'url',
|
||||||
|
label: 'Url',
|
||||||
|
description: 'Link Url',
|
||||||
|
isNullable: true,
|
||||||
|
defaultValue: null,
|
||||||
|
};
|
||||||
|
const label: NodeField = {
|
||||||
|
type: 'TEXT',
|
||||||
|
name: 'label',
|
||||||
|
label: 'Label',
|
||||||
|
description: 'Link Label',
|
||||||
|
isNullable: true,
|
||||||
|
defaultValue: null,
|
||||||
|
};
|
||||||
|
return [url, label];
|
||||||
|
}
|
||||||
|
case FieldMetadataType.CURRENCY: {
|
||||||
|
const amountMicros: NodeField = {
|
||||||
|
type: 'NUMBER',
|
||||||
|
name: 'amountMicros',
|
||||||
|
label: 'Amount Micros',
|
||||||
|
description: 'Amount Micros. eg: set 3210000 for 3.21$',
|
||||||
|
isNullable: true,
|
||||||
|
defaultValue: null,
|
||||||
|
};
|
||||||
|
const currencyCode: NodeField = {
|
||||||
|
type: 'TEXT',
|
||||||
|
name: 'currencyCode',
|
||||||
|
label: 'Currency Code',
|
||||||
|
description: 'Currency Code. eg: USD, EUR, etc...',
|
||||||
|
isNullable: true,
|
||||||
|
defaultValue: null,
|
||||||
|
};
|
||||||
|
return [amountMicros, currencyCode];
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
throw new Error(`Unknown nodeField type: ${nodeField.type}`);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const isFieldRequired = (nodeField: NodeField): boolean => {
|
||||||
|
return !nodeField.isNullable && !nodeField.defaultValue;
|
||||||
};
|
};
|
||||||
|
|
||||||
export const computeInputFields = (
|
export const computeInputFields = (
|
||||||
infos: Infos,
|
node: Node,
|
||||||
idRequired = false,
|
isRequired = false,
|
||||||
): object[] => {
|
): InputField[] => {
|
||||||
const result = [];
|
const result = [];
|
||||||
|
for (const field of node.fields.edges) {
|
||||||
for (const fieldName of Object.keys(infos.properties)) {
|
const nodeField = field.node;
|
||||||
switch (infos.properties[fieldName].type) {
|
switch (nodeField.type) {
|
||||||
case 'array':
|
case FieldMetadataType.FULL_NAME:
|
||||||
break;
|
case FieldMetadataType.LINK:
|
||||||
case 'object':
|
case FieldMetadataType.CURRENCY:
|
||||||
if (!infos.properties[fieldName].properties) {
|
for (const subNodeField of get_subfieldsFromField(nodeField)) {
|
||||||
break;
|
|
||||||
}
|
|
||||||
for (const subFieldName of Object.keys(
|
|
||||||
infos.properties[fieldName].properties || {},
|
|
||||||
)) {
|
|
||||||
const field = {
|
const field = {
|
||||||
key: `${fieldName}__${subFieldName}`,
|
key: `${nodeField.name}__${subNodeField.name}`,
|
||||||
label: `${labelling(fieldName)}: ${labelling(subFieldName)}`,
|
label: `${nodeField.label}: ${subNodeField.label}`,
|
||||||
type: infos.properties[fieldName].properties?.[subFieldName].type,
|
type: getTypeFromFieldMetadataType(subNodeField.type),
|
||||||
required: false,
|
helpText: `${nodeField.description}: ${subNodeField.description}`,
|
||||||
};
|
required: isFieldRequired(subNodeField),
|
||||||
if (infos.required?.includes(fieldName)) {
|
} as InputField;
|
||||||
field.required = true;
|
|
||||||
}
|
|
||||||
result.push(field);
|
result.push(field);
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
default: {
|
case FieldMetadataType.UUID:
|
||||||
const field = {
|
case FieldMetadataType.TEXT:
|
||||||
key: fieldName,
|
case FieldMetadataType.PHONE:
|
||||||
label: labelling(fieldName),
|
case FieldMetadataType.EMAIL:
|
||||||
type: infos.properties[fieldName].type,
|
case FieldMetadataType.DATE_TIME:
|
||||||
required: false,
|
case FieldMetadataType.BOOLEAN:
|
||||||
};
|
case FieldMetadataType.NUMBER:
|
||||||
if (
|
case FieldMetadataType.NUMERIC:
|
||||||
(idRequired && fieldName === 'id') ||
|
case FieldMetadataType.PROBABILITY:
|
||||||
(!idRequired && infos.required?.includes(fieldName))
|
case FieldMetadataType.RATING: {
|
||||||
) {
|
const nodeFieldType = getTypeFromFieldMetadataType(nodeField.type);
|
||||||
field.required = true;
|
if (!nodeFieldType) {
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
|
const required =
|
||||||
|
(isRequired && nodeField.name === 'id') ||
|
||||||
|
(!isRequired && isFieldRequired(nodeField));
|
||||||
|
const field = {
|
||||||
|
key: nodeField.name,
|
||||||
|
label: nodeField.label,
|
||||||
|
type: nodeFieldType,
|
||||||
|
helpText: nodeField.description,
|
||||||
|
required,
|
||||||
|
};
|
||||||
result.push(field);
|
result.push(field);
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
|
default:
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return result;
|
return result.sort((a, _) => (a.key === 'id' ? -1 : 1));
|
||||||
};
|
};
|
||||||
|
|||||||
@ -1,17 +0,0 @@
|
|||||||
import { Bundle, ZObject } from 'zapier-platform-core';
|
|
||||||
|
|
||||||
import { computeInputFields } from '../../utils/computeInputFields';
|
|
||||||
import { requestSchema } from '../../utils/requestDb';
|
|
||||||
import { capitalize } from '../capitalize';
|
|
||||||
|
|
||||||
export const recordInputFields = async (
|
|
||||||
z: ZObject,
|
|
||||||
bundle: Bundle,
|
|
||||||
idRequired = false,
|
|
||||||
) => {
|
|
||||||
const schema = await requestSchema(z, bundle);
|
|
||||||
const infos =
|
|
||||||
schema.components.schemas[capitalize(bundle.inputData.nameSingular)];
|
|
||||||
|
|
||||||
return computeInputFields(infos, idRequired);
|
|
||||||
};
|
|
||||||
@ -1,2 +1,58 @@
|
|||||||
export type InputData = { [x: string]: any };
|
export type InputData = { [x: string]: any };
|
||||||
|
|
||||||
export type ObjectData = { id: string } | { [x: string]: any };
|
export type ObjectData = { id: string } | { [x: string]: any };
|
||||||
|
|
||||||
|
export type NodeField = {
|
||||||
|
type: string;
|
||||||
|
name: string;
|
||||||
|
label: string;
|
||||||
|
description: string | null;
|
||||||
|
isNullable: boolean;
|
||||||
|
defaultValue: object | null;
|
||||||
|
};
|
||||||
|
|
||||||
|
export type Node = {
|
||||||
|
nameSingular: string;
|
||||||
|
namePlural: string;
|
||||||
|
labelSingular: string;
|
||||||
|
fields: {
|
||||||
|
edges: {
|
||||||
|
node: NodeField;
|
||||||
|
}[];
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
export type InputField = {
|
||||||
|
key: string;
|
||||||
|
label: string;
|
||||||
|
type: string;
|
||||||
|
helpText: string | null;
|
||||||
|
required: boolean;
|
||||||
|
};
|
||||||
|
|
||||||
|
export enum FieldMetadataType {
|
||||||
|
UUID = 'UUID',
|
||||||
|
TEXT = 'TEXT',
|
||||||
|
PHONE = 'PHONE',
|
||||||
|
EMAIL = 'EMAIL',
|
||||||
|
DATE_TIME = 'DATE_TIME',
|
||||||
|
BOOLEAN = 'BOOLEAN',
|
||||||
|
NUMBER = 'NUMBER',
|
||||||
|
NUMERIC = 'NUMERIC',
|
||||||
|
PROBABILITY = 'PROBABILITY',
|
||||||
|
LINK = 'LINK',
|
||||||
|
CURRENCY = 'CURRENCY',
|
||||||
|
FULL_NAME = 'FULL_NAME',
|
||||||
|
RATING = 'RATING',
|
||||||
|
SELECT = 'SELECT',
|
||||||
|
MULTI_SELECT = 'MULTI_SELECT',
|
||||||
|
RELATION = 'RELATION',
|
||||||
|
}
|
||||||
|
|
||||||
|
export type Schema = {
|
||||||
|
data: {
|
||||||
|
objects: {
|
||||||
|
edges: { node: Node }[];
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|||||||
@ -1,9 +0,0 @@
|
|||||||
import { capitalize } from '../utils/capitalize';
|
|
||||||
|
|
||||||
export const labelling = (str: string): string => {
|
|
||||||
return str
|
|
||||||
.replace(/[A-Z]/g, (letter) => ` ${letter.toLowerCase()}`)
|
|
||||||
.split(' ')
|
|
||||||
.map((word) => capitalize(word))
|
|
||||||
.join(' ');
|
|
||||||
};
|
|
||||||
@ -1,17 +1,36 @@
|
|||||||
import { Bundle, HttpRequestOptions, ZObject } from 'zapier-platform-core';
|
import { Bundle, HttpRequestOptions, ZObject } from 'zapier-platform-core';
|
||||||
|
|
||||||
export const requestSchema = async (z: ZObject, bundle: Bundle) => {
|
import { Schema } from '../utils/data.types';
|
||||||
const options = {
|
|
||||||
url: `${process.env.SERVER_BASE_URL}/open-api`,
|
|
||||||
method: 'GET',
|
|
||||||
headers: {
|
|
||||||
'Content-Type': 'application/json',
|
|
||||||
Accept: 'application/json',
|
|
||||||
Authorization: `Bearer ${bundle.authData.apiKey}`,
|
|
||||||
},
|
|
||||||
} satisfies HttpRequestOptions;
|
|
||||||
|
|
||||||
return z.request(options).then((response) => response.json);
|
export const requestSchema = async (
|
||||||
|
z: ZObject,
|
||||||
|
bundle: Bundle,
|
||||||
|
): Promise<Schema> => {
|
||||||
|
const query = `query GetObjects {
|
||||||
|
objects(paging: {first: 1000}, filter: {isActive: {is:true}}) {
|
||||||
|
edges {
|
||||||
|
node {
|
||||||
|
nameSingular
|
||||||
|
namePlural
|
||||||
|
labelSingular
|
||||||
|
fields(paging: {first: 1000}, filter: {isActive: {is:true}}) {
|
||||||
|
edges {
|
||||||
|
node {
|
||||||
|
type
|
||||||
|
name
|
||||||
|
label
|
||||||
|
description
|
||||||
|
isNullable
|
||||||
|
defaultValue
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}`;
|
||||||
|
const endpoint = 'metadata';
|
||||||
|
return await requestDb(z, bundle, query, endpoint);
|
||||||
};
|
};
|
||||||
|
|
||||||
const requestDb = async (
|
const requestDb = async (
|
||||||
|
|||||||
@ -2,7 +2,10 @@ import { Bundle, ZObject } from 'zapier-platform-core';
|
|||||||
|
|
||||||
import { ObjectData } from '../../utils/data.types';
|
import { ObjectData } from '../../utils/data.types';
|
||||||
import handleQueryParams from '../../utils/handleQueryParams';
|
import handleQueryParams from '../../utils/handleQueryParams';
|
||||||
import requestDb, { requestDbViaRestApi } from '../../utils/requestDb';
|
import requestDb, {
|
||||||
|
requestDbViaRestApi,
|
||||||
|
requestSchema,
|
||||||
|
} from '../../utils/requestDb';
|
||||||
|
|
||||||
export enum Operation {
|
export enum Operation {
|
||||||
create = 'create',
|
create = 'create',
|
||||||
@ -61,21 +64,7 @@ const getNamePluralFromNameSingular = async (
|
|||||||
bundle: Bundle,
|
bundle: Bundle,
|
||||||
nameSingular: string,
|
nameSingular: string,
|
||||||
): Promise<string> => {
|
): Promise<string> => {
|
||||||
const result = await requestDb(
|
const result = await requestSchema(z, bundle);
|
||||||
z,
|
|
||||||
bundle,
|
|
||||||
`query GetObjects {
|
|
||||||
objects(paging: {first: 1000}) {
|
|
||||||
edges {
|
|
||||||
node {
|
|
||||||
nameSingular
|
|
||||||
namePlural
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}`,
|
|
||||||
'metadata',
|
|
||||||
);
|
|
||||||
for (const object of result.data.objects.edges) {
|
for (const object of result.data.objects.edges) {
|
||||||
if (object.node.nameSingular === nameSingular) {
|
if (object.node.nameSingular === nameSingular) {
|
||||||
return object.node.namePlural;
|
return object.node.namePlural;
|
||||||
|
|||||||
Reference in New Issue
Block a user