Add Onfleet Node & Trigger (#2845)

* feat: added Onfleet nodes

Added Onfleet nodes for working with different endpoints like:
organizations, administrators, workers, hubs, teams, destinations, recipients,
containers and webhooks.

* style: fixed typos, arrays uniformity, unnecesary files

* refactor: changed add to create in comments and labels

* feat: added name field to onfleet trigger node

* feat: added team endpoints to onfleet node

Added team auto-dispatch and driver time estimate endpoints to Onfleet
node

* style: remove dots in descriptions and fixed some typos

* feat: added fixes according to comments made on the n8n PR

added new fixed collections, refactored the code according to comments
made on the n8n pr

* fix: fixed recipient and destination cretion

* docs: added docstrings for format some functions

added docstrings for new functions addded for formatting the destination
and recipient objects

* style: formatting the code according to n8n nodelinter

* fix: typos and better descriptions

* [INT-510] n8n: Address additional problems from n8n code review (#5)

* Fixed some error creating a worker, moving some fields under additional fields collection

* Fixed returned values for delete operations, making some changes for style code

* Added operational error since required property is not working for dateTime fields

*  Improvements to #2593

*  Improvements

* 🐛 Fix issue with wrong interface

*  Improvements

*  Improvements

*  Minor improvement

Co-authored-by: Santiago Botero Ruiz <santiago.botero@devsavant.ai>
Co-authored-by: ilsemaj <james.li.upenn@gmail.com>
Co-authored-by: Santiago Botero Ruiz <39206812+YokySantiago@users.noreply.github.com>
Co-authored-by: Jan Oberhauser <jan.oberhauser@gmail.com>
This commit is contained in:
Ricardo Espinoza 2022-02-28 03:48:17 -05:00 committed by GitHub
parent 2ec4ed6592
commit 401e626a64
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
21 changed files with 5593 additions and 0 deletions

View file

@ -0,0 +1,18 @@
import {
ICredentialType,
NodePropertyTypes,
} from 'n8n-workflow';
export class OnfleetApi implements ICredentialType {
name = 'onfleetApi';
displayName = 'Onfleet API';
documentationUrl = 'onfleet';
properties = [
{
displayName: 'API Key',
name: 'apiKey',
type: 'string' as NodePropertyTypes,
default: '',
},
];
}

View file

@ -0,0 +1,123 @@
import {
ICredentialDataDecryptedObject,
IDataObject,
IExecuteFunctions,
IHookFunctions,
ILoadOptionsFunctions,
INodePropertyOptions,
IWebhookFunctions,
JsonObject,
NodeApiError
} from 'n8n-workflow';
import {
OptionsWithUri,
} from 'request';
import * as moment from 'moment-timezone';
export async function onfleetApiRequest(
this: IWebhookFunctions | IHookFunctions | IExecuteFunctions | ILoadOptionsFunctions,
method: string,
resource: string,
body: any = {}, // tslint:disable-line:no-any
qs?: any, // tslint:disable-line:no-any
uri?: string): Promise<any> { // tslint:disable-line:no-any
const credentials = await this.getCredentials('onfleetApi') as ICredentialDataDecryptedObject;
const options: OptionsWithUri = {
headers: {
'Content-Type': 'application/json',
'User-Agent': 'n8n-onfleet',
},
auth: {
user: credentials.apiKey as string,
pass: '',
},
method,
body,
qs,
uri: uri || `https://onfleet.com/api/v2/${resource}`,
json: true,
};
try {
//@ts-ignore
return await this.helpers.request(options);
} catch (error) {
throw new NodeApiError(this.getNode(), error as JsonObject);
}
}
export async function onfleetApiRequestAllItems(
this: IWebhookFunctions | IHookFunctions | IExecuteFunctions | ILoadOptionsFunctions,
propertyName: string,
method: string,
endpoint: string,
// tslint:disable-next-line: no-any
body: any = {},
query: IDataObject = {},
): Promise<any> { // tslint:disable-line:no-any
const returnData: IDataObject[] = [];
let responseData;
do {
responseData = await onfleetApiRequest.call(this, method, endpoint, body, query);
query.lastId = responseData['lastId'];
returnData.push.apply(returnData, responseData[propertyName]);
} while (
responseData['lastId'] !== undefined
);
return returnData;
}
export const resourceLoaders = {
async getTeams(this: ILoadOptionsFunctions): Promise<INodePropertyOptions[]> {
try {
const teams = await onfleetApiRequest.call(this, 'GET', 'teams') as IDataObject[];
return teams.map(({ name = '', id: value = '' }) => ({ name, value })) as INodePropertyOptions[];
} catch (error) {
return [];
}
},
async getWorkers(this: ILoadOptionsFunctions): Promise<INodePropertyOptions[]> {
try {
const workers = await onfleetApiRequest.call(this, 'GET', 'workers') as IDataObject[];
return workers.map(({ name = '', id: value = '' }) => ({ name, value })) as INodePropertyOptions[];
} catch (error) {
return [];
}
},
async getAdmins(this: ILoadOptionsFunctions): Promise<INodePropertyOptions[]> {
try {
const admins = await onfleetApiRequest.call(this, 'GET', 'admins') as IDataObject[];
return admins.map(({ name = '', id: value = '' }) => ({ name, value })) as INodePropertyOptions[];
} catch (error) {
return [];
}
},
async getHubs(this: ILoadOptionsFunctions): Promise<INodePropertyOptions[]> {
try {
const hubs = await onfleetApiRequest.call(this, 'GET', 'hubs') as IDataObject[];
return hubs.map(({ name = '', id: value = '' }) => ({ name, value })) as INodePropertyOptions[];
} catch (error) {
return [];
}
},
async getTimezones(this: ILoadOptionsFunctions): Promise<INodePropertyOptions[]> {
const returnData = [] as INodePropertyOptions[];
for (const timezone of moment.tz.names()) {
returnData.push({
name: timezone,
value: timezone,
});
}
return returnData;
},
};

View file

@ -0,0 +1,230 @@
import {
ICredentialsDecrypted,
ICredentialTestFunctions,
IDataObject,
INodeCredentialTestResult,
INodeExecutionData,
INodeType,
INodeTypeDescription,
} from 'n8n-workflow';
import {
taskFields,
taskOperations,
} from './descriptions/TaskDescription';
import {
IExecuteFunctions,
} from 'n8n-core';
import {
destinationFields,
destinationOperations,
} from './descriptions/DestinationDescription';
import {
resourceLoaders,
} from './GenericFunctions';
import {
recipientFields,
recipientOperations,
} from './descriptions/RecipientDescription';
import {
organizationFields,
organizationOperations,
} from './descriptions/OrganizationDescription';
import {
adminFields,
adminOperations,
} from './descriptions/AdministratorDescription';
import {
hubFields,
hubOperations,
} from './descriptions/HubDescription';
import {
workerFields,
workerOperations,
} from './descriptions/WorkerDescription';
// import {
// webhookFields,
// webhookOperations,
// } from './descriptions/WebhookDescription';
import {
containerFields,
containerOperations,
} from './descriptions/ContainerDescription';
import {
teamFields,
teamOperations,
} from './descriptions/TeamDescription';
import {
OptionsWithUri,
} from 'request';
import { Onfleet as OnfleetMethods } from './Onfleet';
export class Onfleet implements INodeType {
description: INodeTypeDescription = {
displayName: 'Onfleet',
name: 'onfleet',
icon: 'file:Onfleet.svg',
group: ['input'],
version: 1,
subtitle: '={{$parameter["operation"] + ": " + $parameter["resource"]}}',
description: 'Consume Onfleet API',
defaults: {
color: '#AA81F3',
name: 'Onfleet',
},
inputs: ['main'],
outputs: ['main'],
credentials: [
{
name: 'onfleetApi',
required: true,
testedBy: 'onfleetApiTest',
},
],
properties: [
// List of option resources
{
displayName: 'Resource',
name: 'resource',
type: 'options',
options: [
{
name: 'Admin',
value: 'admin',
},
{
name: 'Container',
value: 'container',
},
{
name: 'Destination',
value: 'destination',
},
{
name: 'Hub',
value: 'hub',
},
{
name: 'Organization',
value: 'organization',
},
{
name: 'Recipient',
value: 'recipient',
},
{
name: 'Task',
value: 'task',
},
{
name: 'Team',
value: 'team',
},
// {
// name: 'Webhook',
// value: 'webhook',
// },
{
name: 'Worker',
value: 'worker',
},
],
default: 'task',
description: 'The resource to perform operations on',
},
// Operations & fields
...adminOperations,
...adminFields,
...containerOperations,
...containerFields,
...destinationOperations,
...destinationFields,
...hubOperations,
...hubFields,
...organizationOperations,
...organizationFields,
...recipientOperations,
...recipientFields,
...taskOperations,
...taskFields,
...teamOperations,
...teamFields,
// ...webhookOperations,
// ...webhookFields,
...workerOperations,
...workerFields,
],
};
methods = {
credentialTest: {
async onfleetApiTest(this: ICredentialTestFunctions, credential: ICredentialsDecrypted): Promise<INodeCredentialTestResult> {
const credentials = credential.data as IDataObject;
const options: OptionsWithUri = {
headers: {
'Content-Type': 'application/json',
'User-Agent': 'n8n-onfleet',
},
auth: {
user: credentials.apiKey as string,
pass: '',
},
method: 'GET',
uri: 'https://onfleet.com/api/v2/auth/test',
json: true,
};
try {
await this.helpers.request(options);
return {
status: 'OK',
message: 'Authentication successful',
};
} catch (error) {
return {
status: 'Error',
message: `Auth settings are not valid: ${error}`,
};
}
},
},
loadOptions: resourceLoaders,
};
async execute(this: IExecuteFunctions): Promise<INodeExecutionData[][]> {
const resource = this.getNodeParameter('resource', 0) as string;
const operation = this.getNodeParameter('operation', 0) as string;
const items = this.getInputData();
const operations: { [key: string]: Function } = {
task: OnfleetMethods.executeTaskOperations,
destination: OnfleetMethods.executeDestinationOperations,
organization: OnfleetMethods.executeOrganizationOperations,
admin: OnfleetMethods.executeAdministratorOperations,
recipient: OnfleetMethods.executeRecipientOperations,
hub: OnfleetMethods.executeHubOperations,
worker: OnfleetMethods.executeWorkerOperations,
webhook: OnfleetMethods.executeWebhookOperations,
container: OnfleetMethods.executeContainerOperations,
team: OnfleetMethods.executeTeamOperations,
};
const responseData = await operations[resource].call(this, `${resource}s`, operation, items);
// Map data to n8n data
return [this.helpers.returnJsonArray(responseData)];
}
}

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 5.6 KiB

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,164 @@
import {
IDataObject,
INodeType,
INodeTypeDescription,
IWebhookResponseData,
NodeApiError,
NodeOperationError
} from 'n8n-workflow';
import {
IHookFunctions,
IWebhookFunctions,
} from 'n8n-core';
import {
eventDisplay,
eventNameField,
} from './descriptions/OnfleetWebhookDescription';
import {
onfleetApiRequest,
} from './GenericFunctions';
import {
webhookMapping,
} from './WebhookMapping';
export class OnfleetTrigger implements INodeType {
description: INodeTypeDescription = {
displayName: 'Onfleet Trigger',
name: 'onfleetTrigger',
icon: 'file:Onfleet.svg',
group: ['trigger'],
version: 1,
subtitle: '={{$parameter["triggerOn"]}}',
description: 'Starts the workflow when Onfleet events occur',
defaults: {
name: 'Onfleet Trigger',
color: '#AA81F3',
},
inputs: [],
outputs: ['main'],
credentials: [
{
name: 'onfleetApi',
required: true,
testedBy: 'onfleetApiTest',
},
],
webhooks: [
{
name: 'setup',
httpMethod: 'GET',
responseMode: 'onReceived',
path: 'webhook',
},
{
name: 'default',
httpMethod: 'POST',
responseMode: 'onReceived',
path: 'webhook',
},
],
properties: [
eventDisplay,
eventNameField,
],
};
// @ts-ignore (because of request)
webhookMethods = {
default: {
async checkExists(this: IHookFunctions): Promise<boolean> {
const webhookData = this.getWorkflowStaticData('node') as IDataObject;
const webhookUrl = this.getNodeWebhookUrl('default') as string;
// Webhook got created before so check if it still exists
const endpoint = '/webhooks';
const webhooks = await onfleetApiRequest.call(this, 'GET', endpoint);
for (const webhook of webhooks) {
if (webhook.url === webhookUrl && webhook.trigger === event) {
webhookData.webhookId = webhook.id;
return true;
}
}
return false;
},
async create(this: IHookFunctions): Promise<boolean> {
const { name = '' } = this.getNodeParameter('additionalFields') as IDataObject;
const triggerOn = this.getNodeParameter('triggerOn') as string;
const webhookData = this.getWorkflowStaticData('node') as IDataObject;
const webhookUrl = this.getNodeWebhookUrl('default') as string;
if (webhookUrl.includes('//localhost')) {
throw new NodeOperationError(this.getNode(), 'The Webhook can not work on "localhost". Please, either setup n8n on a custom domain or start with "--tunnel"!');
}
// Webhook name according to the field
let newWebhookName = `n8n-webhook:${webhookUrl}`;
if (name) {
newWebhookName = `n8n-webhook:${name}`;
}
const path = `/webhooks`;
const body = {
name: newWebhookName,
url: webhookUrl,
trigger: webhookMapping[triggerOn].key,
};
try {
const webhook = await onfleetApiRequest.call(this, 'POST', path, body);
if (webhook.id === undefined) {
throw new NodeApiError(this.getNode(), webhook, { message: 'Onfleet webhook creation response did not contain the expected data' });
}
webhookData.id = webhook.id as string;
} catch (error) {
const { httpCode = '' } = error as { httpCode: string };
if (httpCode === '422') {
throw new NodeOperationError(this.getNode(), 'A webhook with the identical URL probably exists already. Please delete it manually in Onfleet!');
}
throw error;
}
return true;
},
async delete(this: IHookFunctions): Promise<boolean> {
const webhookData = this.getWorkflowStaticData('node') as IDataObject;
// Get the data of the already registered webhook
const endpoint = `/webhooks/${webhookData.id}`;
await onfleetApiRequest.call(this, 'DELETE', endpoint);
return true;
},
},
};
/**
* Triggered function when an Onfleet webhook is executed
* @returns {Promise<IWebhookResponseData>} Response data
*/
async webhook(this: IWebhookFunctions): Promise<IWebhookResponseData> {
const req = this.getRequestObject();
if (this.getWebhookName() === 'setup') {
/* -------------------------------------------------------------------------- */
/* Validation request */
/* -------------------------------------------------------------------------- */
const res = this.getResponseObject();
res.status(200).send(req.query.check);
return { noWebhookResponse: true };
}
const returnData: IDataObject = this.getBodyData();
return {
workflowData: [
this.helpers.returnJsonArray(returnData),
],
};
}
}

View file

@ -0,0 +1,91 @@
import {
OnfleetWebhooksMapping,
} from './interfaces';
export const webhookMapping: OnfleetWebhooksMapping = {
taskStarted: {
name: 'Task Started',
value: 'taskStarted',
key: 0,
},
taskEta: {
name: 'Task ETA',
value: 'taskEta',
key: 1,
},
taskArrival: {
name: 'Task Arrival',
value: 'taskArrival',
key: 2,
},
taskCompleted: {
name: 'Task Completed',
value: 'taskCompleted',
key: 3,
},
taskFailed: {
name: 'Task Failed',
value: 'taskFailed',
key: 4,
},
workerDuty: {
name: 'Worker Duty',
value: 'workerDuty',
key: 5,
},
taskCreated: {
name: 'Task Created',
value: 'taskCreated',
key: 6,
},
taskUpdated: {
name: 'Task Updated',
value: 'taskUpdated',
key: 7,
},
taskDeleted: {
name: 'Task Deleted',
value: 'taskDeleted',
key: 8,
},
taskAssigned: {
name: 'Task Assigned',
value: 'taskAssigned',
key: 9,
},
taskUnassigned: {
name: 'Task Unassigned',
value: 'taskUnassigned',
key: 10,
},
taskDelayed: {
name: 'Task Delayed',
value: 'taskDelayed',
key: 12,
},
taskCloned: {
name: 'Task Cloned',
value: 'taskCloned',
key: 13,
},
smsRecipientResponseMissed: {
name: 'SMS Recipient Response Missed',
value: 'smsRecipientResponseMissed',
key: 14,
},
workerCreated: {
name: 'Worker Created',
value: 'workerCreated',
key: 15,
},
workerDeleted: {
name: 'Worker Deleted',
value: 'workerDeleted',
key: 16,
},
SMSRecipientOptOut: {
name: 'SMS Recipient Opt Out',
value: 'SMSRecipientOptOut',
key: 17,
},
};

View file

@ -0,0 +1,209 @@
import {
INodeProperties
} from 'n8n-workflow';
export const adminOperations: INodeProperties[] = [
{
displayName: 'Operation',
name: 'operation',
type: 'options',
displayOptions: {
show: {
resource: [
'admin',
],
},
},
options: [
{
name: 'Create',
value: 'create',
description: 'Create a new Onfleet admin',
},
{
name: 'Delete',
value: 'delete',
description: 'Delete an Onfleet admin',
},
{
name: 'Get All',
value: 'getAll',
description: 'Get all Onfleet admins',
},
{
name: 'Update',
value: 'update',
description: 'Update an Onfleet admin',
},
],
default: 'getAll',
},
];
const adminNameField = {
displayName: 'Name',
name: 'name',
type: 'string',
default: '',
description: 'The administrator\'s name',
} as INodeProperties;
const adminEmailField = {
displayName: 'Email',
name: 'email',
type: 'string',
default: '',
description: 'The administrator\'s email address',
} as INodeProperties;
const adminPhoneField = {
displayName: 'Phone',
name: 'phone',
type: 'string',
default: '',
description: 'The administrator\'s phone number',
} as INodeProperties;
const adminReadOnlyField = {
displayName: 'Read Only',
name: 'isReadOnly',
type: 'boolean',
default: false,
description: 'Whether this administrator can perform write operations',
} as INodeProperties;
export const adminFields: INodeProperties[] = [
{
displayName: 'Admin ID',
name: 'id',
type: 'string',
displayOptions: {
show: {
resource: [
'admin',
],
},
hide: {
operation: [
'create',
'getAll',
],
},
},
default: '',
required: true,
description: 'The ID of the admin object for lookup',
},
{
displayName: 'Return All',
name: 'returnAll',
type: 'boolean',
displayOptions: {
show: {
resource: [
'admin',
],
operation: [
'getAll',
],
},
},
default: false,
description: 'If all results should be returned or only up to a given limit',
},
{
displayName: 'Limit',
name: 'limit',
type: 'number',
displayOptions: {
show: {
resource: [
'admin',
],
operation: [
'getAll',
],
returnAll: [
false,
],
},
},
typeOptions: {
minValue: 1,
maxValue: 64,
},
default: 64,
description: 'How many results to return',
},
{
displayOptions: {
show: {
resource: [
'admin',
],
operation: [
'create',
],
},
},
required: true,
...adminNameField,
},
{
displayOptions: {
show: {
resource: [
'admin',
],
operation: [
'create',
],
},
},
required: true,
...adminEmailField,
},
{
displayName: 'Additional Fields',
name: 'additionalFields',
type: 'collection',
placeholder: 'Add Field',
default: {},
displayOptions: {
show: {
resource: [
'admin',
],
operation: [
'create',
],
},
},
options: [
adminPhoneField,
adminReadOnlyField,
],
},
{
displayName: 'Update Fields',
name: 'updateFields',
type: 'collection',
placeholder: 'Add Field',
default: {},
displayOptions: {
show: {
resource: [
'admin',
],
operation: [
'update',
],
},
},
options: [
adminNameField,
adminPhoneField,
adminReadOnlyField,
],
},
];

View file

@ -0,0 +1,219 @@
import {
INodeProperties
} from 'n8n-workflow';
export const containerOperations: INodeProperties[] = [
{
displayName: 'Operation',
name: 'operation',
type: 'options',
displayOptions: {
show: {
resource: [
'container',
],
},
},
options: [
{
name: 'Add Tasks',
value: 'addTask',
description: 'Add task at index (or append)',
},
{
name: 'Get',
value: 'get',
description: 'Get container information',
},
{
name: 'Update Tasks',
value: 'updateTask',
description: 'Fully replace a container\'s tasks',
},
],
default: 'get',
},
];
const containerTypeField = {
displayName: 'Container Type',
name: 'containerType',
type: 'options',
options: [
{
name: 'Organizations',
value: 'organizations',
},
{
name: 'Teams',
value: 'teams',
},
{
name: 'Workers',
value: 'workers',
},
],
default: '',
description: 'Container type',
} as INodeProperties;
const containerIdField = {
displayName: 'Container ID',
name: 'containerId',
type: 'string',
default: '',
description: 'The object ID according to the container chosen',
} as INodeProperties;
const insertTypeField = {
displayName: 'Insert Type',
name: 'type',
type: 'options',
options: [
{
name: 'Append',
value: -1,
},
{
name: 'Prepend',
value: 0,
},
{
name: 'At Specific Index',
value: 1,
},
],
default: '',
} as INodeProperties;
const indexField = {
displayName: 'Index',
name: 'index',
type: 'number',
default: 0,
description: 'The index given indicates the position where the tasks are going to be inserted',
} as INodeProperties;
const tasksField = {
displayName: 'Task IDs',
name: 'tasks',
type: 'string',
typeOptions: {
multipleValues: true,
multipleValueButtonText: 'Add Task',
},
default: [],
description: 'Task\'s ID that are going to be used',
} as INodeProperties;
const considerDependenciesField = {
displayName: 'Consider Dependencies',
name: 'considerDependencies',
type: 'boolean',
default: false,
description: 'Whether to include the target task\'s dependency family (parent and child tasks) in the resulting assignment operation',
} as INodeProperties;
export const containerFields: INodeProperties[] = [
{
...containerTypeField,
displayOptions: {
show: {
resource: [
'container',
],
operation: [
'get',
'addTask',
],
},
},
required: true,
},
{
...containerIdField,
displayOptions: {
show: {
resource: [
'container',
],
operation: [
'get',
'addTask',
'updateTask',
],
},
},
required: true,
},
{
...insertTypeField,
displayOptions: {
show: {
resource: [
'container',
],
operation: [
'addTask',
],
},
},
required: true,
},
{
...indexField,
displayOptions: {
show: {
resource: [
'container',
],
operation: [
'addTask',
],
type: [
1,
],
},
},
required: true,
},
{
...tasksField,
displayOptions: {
show: {
resource: [
'container',
],
operation: [
'addTask',
'updateTask',
],
},
},
required: true,
},
{
displayName: 'Options',
name: 'options',
type: 'collection',
placeholder: 'Add Option',
default: {},
displayOptions: {
show: {
resource: [
'container',
],
operation: [
'addTask',
'updateTask',
],
},
},
options: [
{
...considerDependenciesField,
required: false,
},
],
},
];

View file

@ -0,0 +1,402 @@
import {
INodeProperties
} from 'n8n-workflow';
export const destinationOperations: INodeProperties[] = [
{
displayName: 'Operation',
name: 'operation',
type: 'options',
displayOptions: {
show: {
resource: [
'destination',
],
},
},
options: [
{
name: 'Create',
value: 'create',
description: 'Create a new destination',
},
{
name: 'Get',
value: 'get',
description: 'Get a specific destination',
},
],
default: 'get',
},
];
const unparsedField = {
displayName: 'Unparsed Address',
name: 'unparsed',
type: 'boolean',
description: 'Whether or not the address is specified in a single unparsed string',
default: false,
} as INodeProperties;
const unparsedAddressField = {
displayName: 'Destination Address',
name: 'address',
type: 'string',
description: 'The destination\'s street address details',
default: '',
} as INodeProperties;
const unparsedAddressNumberField = {
displayName: 'Number',
name: 'addressNumber',
type: 'string',
description: 'The number component of this address, it may also contain letters',
default: '',
} as INodeProperties;
const unparsedAddressStreetField = {
displayName: 'Street',
name: 'addressStreet',
type: 'string',
description: 'The name of the street',
default: '',
} as INodeProperties;
const unparsedAddressCityField = {
displayName: 'City',
name: 'addressCity',
type: 'string',
description: 'The name of the municipality',
default: '',
} as INodeProperties;
const unparsedAddressCountryField = {
displayName: 'Country',
name: 'addressCountry',
type: 'string',
description: 'The name of the country',
default: '',
} as INodeProperties;
const unparsedAddressStateField = {
displayName: 'State',
name: 'addressState',
type: 'string',
default: '',
} as INodeProperties;
const addressNameField = {
displayName: 'Address Name',
name: 'addressName',
type: 'string',
default: '',
description: 'A name associated with this address',
} as INodeProperties;
const addressApartmentField = {
displayName: 'Apartment',
name: 'addressApartment',
type: 'string',
default: '',
description: 'The suite or apartment number, or any additional relevant information',
} as INodeProperties;
const addressNoteField = {
displayName: 'Address Notes',
name: 'addressNotes',
type: 'string',
default: '',
description: 'Notes about the destination',
} as INodeProperties;
const addressPostalCodeField = {
displayName: 'Postal Code',
name: 'addressPostalCode',
type: 'string',
default: '',
description: 'The postal or zip code',
} as INodeProperties;
export const destinationExternalField = {
displayName: 'Destination',
name: 'destination',
type: 'fixedCollection',
placeholder: 'Add Destination',
default: {},
options: [
{
displayName: 'Destination Properties',
name: 'destinationProperties',
default: {},
values: [
{
...unparsedField,
required: false,
},
{
...unparsedAddressField,
displayOptions: {
show: {
unparsed: [
true,
],
},
},
required: true,
},
{
...unparsedAddressNumberField,
displayOptions: {
show: {
unparsed: [
false,
],
},
},
required: true,
},
{
...unparsedAddressStreetField,
displayOptions: {
show: {
unparsed: [
false,
],
},
},
required: true,
},
{
...unparsedAddressCityField,
displayOptions: {
show: {
unparsed: [
false,
],
},
},
required: true,
},
{
...unparsedAddressStateField,
displayOptions: {
show: {
unparsed: [
false,
],
},
},
required: true,
},
{
...unparsedAddressCountryField,
displayOptions: {
show: {
unparsed: [
false,
],
},
},
required: true,
},
{
displayOptions: {
show: {
unparsed: [
false,
],
},
},
...addressPostalCodeField,
required: false,
},
{
...addressNameField,
required: false,
},
{
...addressApartmentField,
required: false,
},
{
...addressNoteField,
required: false,
},
],
},
],
} as INodeProperties;
export const destinationFields: INodeProperties[] = [
{
displayName: 'Destination ID',
name: 'id',
type: 'string',
displayOptions: {
show: {
resource: [
'destination',
],
},
hide: {
operation: [
'create',
],
},
},
default: '',
required: true,
description: 'The ID of the destination object for lookup',
},
{
...unparsedField,
displayOptions: {
show: {
resource: [
'destination',
],
operation: [
'create',
],
},
},
required: true,
},
{
...unparsedAddressField,
displayOptions: {
show: {
resource: [
'destination',
],
operation: [
'create',
],
unparsed: [
true,
],
},
},
required: true,
},
{
...unparsedAddressNumberField,
displayOptions: {
show: {
resource: [
'destination',
],
operation: [
'create',
],
unparsed: [
false,
],
},
},
required: true,
},
{
...unparsedAddressStreetField,
displayOptions: {
show: {
resource: [
'destination',
],
operation: [
'create',
],
unparsed: [
false,
],
},
},
required: true,
},
{
...unparsedAddressCityField,
displayOptions: {
show: {
resource: [
'destination',
],
operation: [
'create',
],
unparsed: [
false,
],
},
},
required: true,
},
{
...unparsedAddressCountryField,
displayOptions: {
show: {
resource: [
'destination',
],
operation: [
'create',
],
unparsed: [
false,
],
},
},
required: true,
},
{
displayName: 'Additional Fields',
name: 'additionalFields',
type: 'collection',
placeholder: 'Add Field',
default: {},
displayOptions: {
show: {
resource: [
'destination',
],
operation: [
'create',
],
unparsed: [
true,
],
},
},
options: [
addressApartmentField,
addressNameField,
addressNoteField,
],
},
{
displayName: 'Additional Fields',
name: 'additionalFields',
type: 'collection',
placeholder: 'Add Field',
default: {},
displayOptions: {
show: {
resource: [
'destination',
],
operation: [
'create',
],
unparsed: [
false,
],
},
},
options: [
addressApartmentField,
addressNameField,
addressNoteField,
addressPostalCodeField,
],
},
];

View file

@ -0,0 +1,199 @@
import {
INodeProperties
} from 'n8n-workflow';
import {
destinationExternalField,
} from './DestinationDescription';
export const hubOperations: INodeProperties[] = [
{
displayName: 'Operation',
name: 'operation',
type: 'options',
displayOptions: {
show: {
resource: [
'hub',
],
},
},
options: [
{
name: 'Create',
value: 'create',
description: 'Create a new Onfleet hub',
},
{
name: 'Get All',
value: 'getAll',
description: 'Get all Onfleet hubs',
},
{
name: 'Update',
value: 'update',
description: 'Update an Onfleet hub',
},
],
default: 'getAll',
},
];
const nameField = {
displayName: 'Name',
name: 'name',
type: 'string',
default: '',
description: 'A name to identify the hub',
} as INodeProperties;
const teamsField = {
displayName: 'Teams Names/IDs',
name: 'teams',
type: 'multiOptions',
typeOptions: {
loadOptionsMethod: 'getTeams',
},
default: [],
description: 'These are the teams that this Hub will be assigned to',
} as INodeProperties;
export const hubFields: INodeProperties[] = [
{
displayName: 'Hub ID',
name: 'id',
type: 'string',
displayOptions: {
show: {
resource: [
'hub',
],
operation: [
'update',
],
},
},
default: '',
required: true,
description: 'The ID of the hub object for lookup',
},
{
displayName: 'Return All',
name: 'returnAll',
type: 'boolean',
displayOptions: {
show: {
resource: [
'hub',
],
operation: [
'getAll',
],
},
},
default: false,
description: 'If all results should be returned or only up to a given limit',
},
{
displayName: 'Limit',
name: 'limit',
type: 'number',
displayOptions: {
show: {
resource: [
'hub',
],
operation: [
'getAll',
],
returnAll: [
false,
],
},
},
typeOptions: {
minValue: 1,
maxValue: 64,
},
default: 64,
description: 'How many results to return',
},
{
...nameField,
displayOptions: {
show: {
resource: [
'hub',
],
operation: [
'create',
],
},
},
required: true,
},
{
...destinationExternalField,
displayOptions: {
show: {
resource: [
'hub',
],
operation: [
'create',
],
},
},
},
{
displayName: 'Additional Fields',
name: 'additionalFields',
type: 'collection',
placeholder: 'Add Field',
default: {},
displayOptions: {
show: {
resource: [
'hub',
],
operation: [
'create',
],
},
},
options: [
{
...teamsField,
required: false,
},
],
},
{
displayName: 'Update Fields',
name: 'updateFields',
type: 'collection',
placeholder: 'Add Field',
default: {},
displayOptions: {
show: {
resource: [
'hub',
],
operation: [
'update',
],
},
},
options: [
{
...destinationExternalField,
required: false,
},
nameField,
{
...teamsField,
required: false,
},
],
},
];

View file

@ -0,0 +1,43 @@
import {
INodeProperties
} from 'n8n-workflow';
import {
webhookMapping,
} from '../WebhookMapping';
const sort = (a: { name: string }, b: { name: string }) => {
if (a.name < b.name) { return -1; }
if (a.name > b.name) { return 1; }
return 0;
};
export const eventDisplay: INodeProperties = {
displayName: 'Trigger On',
name: 'triggerOn',
type: 'options',
options: Object.keys(webhookMapping).map((webhook) => {
const { name, value } = webhookMapping[webhook];
return { name, value };
}).sort(sort),
required: true,
default: [],
};
export const eventNameField = {
displayName: 'Additional Fields',
name: 'additionalFields',
type: 'collection',
placeholder: 'Add Field',
default: {},
options: [
{
displayName: 'Name',
name: 'name',
type: 'string',
required: false,
default: '',
description: 'A name for the webhook for identification',
},
],
} as INodeProperties;

View file

@ -0,0 +1,53 @@
import {
INodeProperties
} from 'n8n-workflow';
export const organizationOperations: INodeProperties[] = [
{
displayName: 'Operation',
name: 'operation',
type: 'options',
displayOptions: {
show: {
resource: [
'organization',
],
},
},
options: [
{
name: 'Get My Organization',
value: 'get',
description: 'Retrieve your own organization\'s details',
},
{
name: 'Get Delegatee Details',
value: 'getDelegatee',
description: 'Retrieve the details of an organization with which you are connected',
},
],
default: 'get',
},
];
export const organizationFields: INodeProperties[] = [
{
displayName: 'Organization ID',
name: 'id',
type: 'string',
displayOptions: {
show: {
resource: [
'organization',
],
operation: [
'getDelegatee',
],
},
},
default: '',
required: true,
description: 'The ID of the delegatees for lookup',
},
];

View file

@ -0,0 +1,329 @@
import {
INodeProperties
} from 'n8n-workflow';
export const recipientOperations: INodeProperties[] = [
{
displayName: 'Operation',
name: 'operation',
type: 'options',
displayOptions: {
show: {
resource: [
'recipient',
],
},
},
options: [
{
name: 'Create',
value: 'create',
description: 'Create a new Onfleet recipient',
},
{
name: 'Get',
value: 'get',
description: 'Get a specific Onfleet recipient',
},
{
name: 'Update',
value: 'update',
description: 'Update an Onfleet recipient',
},
],
default: 'get',
},
];
const additionalRecipientFields: INodeProperties[] = [
{
displayName: 'Recipient Notes',
name: 'recipientNotes',
type: 'string',
default: '',
description: 'Notes for this recipient: these are global notes that should not be task- or destination-specific',
required: false,
},
{
displayName: 'Skip Recipient SMS Notifications',
name: 'recipientSkipSMSNotifications',
type: 'boolean',
default: false,
description: 'Whether this recipient has requested to skip SMS notifications',
required: false,
},
];
const recipientName = {
displayName: 'Recipient Name',
name: 'recipientName',
type: 'string',
description: 'The recipient\'s complete name',
default: '',
} as INodeProperties;
const recipientPhone = {
displayName: 'Recipient Phone',
name: 'recipientPhone',
type: 'string',
description: 'A unique, valid phone number as per the organization\'s country if there\'s no leading + sign. If a phone number has a leading + sign, it will disregard the organization\'s country setting.',
default: '',
} as INodeProperties;
const updateFields: INodeProperties[] = [
{
...recipientName,
required: false,
},
{
displayName: 'Recipient Notes',
name: 'notes',
type: 'string',
default: '',
description: 'Notes for this recipient: these are global notes that should not be task- or destination-specific',
},
{
...recipientPhone,
required: false,
},
{
displayName: 'Skip Recipient SMS Notifications',
name: 'skipSMSNotifications',
type: 'boolean',
default: false,
description: 'Whether this recipient has requested to skip SMS notifications',
},
];
export const recipientExternalField = {
displayName: 'Recipient',
name: 'recipient',
type: 'fixedCollection',
placeholder: 'Add Recipient',
default: {},
options: [
{
displayName: 'Recipient Properties',
name: 'recipientProperties',
default: {},
values: [
{
...recipientName,
required: true,
},
{
...recipientPhone,
required: true,
},
...additionalRecipientFields,
],
},
],
} as INodeProperties;
export const recipientFields: INodeProperties[] = [
{
displayName: 'Get By',
name: 'getBy',
type: 'options',
displayOptions: {
show: {
resource: [
'recipient',
],
operation: [
'get',
],
},
},
options: [
{
name: 'ID',
value: 'id',
},
{
name: 'Phone',
value: 'phone',
},
{
name: 'Name',
value: 'name',
},
],
description: 'The variable that is used for looking up a recipient',
required: true,
default: 'id',
},
{
displayName: 'Recipient ID',
name: 'id',
type: 'string',
displayOptions: {
show: {
resource: [
'recipient',
],
operation: [
'get',
],
getBy: [
'id',
],
},
},
default: '',
required: true,
description: 'The ID of the recipient object for lookup',
},
{
displayName: 'Recipient ID',
name: 'id',
type: 'string',
displayOptions: {
show: {
resource: [
'recipient',
],
operation: [
'update',
],
},
},
default: '',
required: true,
description: 'The ID of the recipient object for lookup',
},
{
displayName: 'Name',
name: 'name',
type: 'string',
displayOptions: {
show: {
resource: [
'recipient',
],
operation: [
'get',
],
getBy: [
'name',
],
},
},
default: '',
required: true,
description: 'The name of the recipient for lookup',
},
{
displayName: 'Phone',
name: 'phone',
type: 'string',
displayOptions: {
show: {
resource: [
'recipient',
],
operation: [
'get',
],
getBy: [
'phone',
],
},
},
default: '',
required: true,
description: 'The phone of the recipient for lookup',
},
{
displayOptions: {
show: {
resource: [
'recipient',
],
operation: [
'create',
],
},
},
...recipientName,
required: true,
},
{
displayOptions: {
show: {
resource: [
'recipient',
],
operation: [
'create',
],
},
},
...recipientPhone,
required: true,
},
{
displayName: 'Additional Fields',
name: 'additionalFields',
type: 'collection',
placeholder: 'Add Field',
default: {},
displayOptions: {
show: {
resource: [
'recipient',
],
operation: [
'create',
],
},
},
options: additionalRecipientFields,
},
{
displayName: 'Update Fields',
name: 'updateFields',
type: 'collection',
placeholder: 'Update Fields',
default: {},
displayOptions: {
show: {
resource: [
'recipient',
],
operation: [
'update',
],
},
},
options: updateFields,
},
{
displayName: 'Options',
name: 'options',
type: 'collection',
placeholder: 'Add Field',
default: {},
displayOptions: {
show: {
resource: [
'recipient',
],
operation: [
'create',
],
},
},
options: [
{
displayName: 'Skip Recipient Phone Number Validation',
name: 'recipientSkipPhoneNumberValidation',
type: 'boolean',
default: false,
description: 'Whether to skip validation for this recipient\'s phone number',
required: false,
},
],
},
];

View file

@ -0,0 +1,453 @@
import {
INodeProperties
} from 'n8n-workflow';
import {
destinationExternalField,
} from './DestinationDescription';
import {
recipientExternalField,
} from './RecipientDescription';
export const taskOperations: INodeProperties[] = [
{
displayName: 'Operation',
name: 'operation',
type: 'options',
displayOptions: {
show: {
resource: [
'task',
],
},
},
options: [
{
name: 'Create',
value: 'create',
description: 'Create a new Onfleet task',
},
{
name: 'Clone',
value: 'clone',
description: 'Clone an Onfleet task',
},
{
name: 'Complete',
value: 'complete',
description: 'Force-complete a started Onfleet task',
},
{
name: 'Delete',
value: 'delete',
description: 'Delete an Onfleet task',
},
{
name: 'Get All',
value: 'getAll',
description: 'Get all Onfleet tasks',
},
{
name: 'Get',
value: 'get',
description: 'Get a specific Onfleet task',
},
{
name: 'Update',
value: 'update',
description: 'Update an Onfleet task',
},
],
default: 'get',
},
];
const merchantIdField = {
displayName: 'Merchant ID',
name: 'merchant',
type: 'string',
default: '',
description: 'The ID of the organization that will be displayed to the recipient of the task',
} as INodeProperties;
const executorIdField = {
displayName: 'Executor ID',
name: 'executor',
type: 'string',
default: '',
description: 'The ID of the organization that will be responsible for fulfilling the task',
} as INodeProperties;
const completeAfterField = {
displayName: 'Complete After',
name: 'completeAfter',
type: 'dateTime',
default: null,
description: 'The earliest time the task should be completed',
} as INodeProperties;
const completeBeforeField = {
displayName: 'Complete Before',
name: 'completeBefore',
type: 'dateTime',
default: null,
description: 'The latest time the task should be completed',
} as INodeProperties;
const pickupTaskField = {
displayName: 'Pick Up Task',
name: 'pickupTask',
type: 'boolean',
default: false,
description: 'Whether the task is a pickup task',
} as INodeProperties;
const notesField = {
displayName: 'Notes',
name: 'notes',
type: 'string',
default: '',
description: 'Notes for the task',
} as INodeProperties;
const quantityField = {
displayName: 'Quantity',
name: 'quantity',
type: 'number',
default: 0,
description: 'The number of units to be dropped off while completing this task, for route optimization purposes',
} as INodeProperties;
const serviceTimeField = {
displayName: 'Service Time',
name: 'serviceTime',
type: 'number',
default: 0,
description: 'The number of minutes to be spent by the worker on arrival at this task\'s destination, for route optimization purposes',
} as INodeProperties;
export const taskFields: INodeProperties[] = [
{
displayName: 'Task ID',
name: 'id',
type: 'string',
displayOptions: {
show: {
resource: [
'task',
],
},
hide: {
operation: [
'create',
'getAll',
],
},
},
default: '',
required: true,
description: 'The ID of the task object for lookup',
},
{
displayName: 'Return All',
name: 'returnAll',
type: 'boolean',
displayOptions: {
show: {
resource: [
'task',
],
operation: [
'getAll',
],
},
},
default: false,
description: 'If all results should be returned or only up to a given limit',
},
{
displayName: 'Limit',
name: 'limit',
type: 'number',
displayOptions: {
show: {
resource: [
'task',
],
operation: [
'getAll',
],
returnAll: [
false,
],
},
},
typeOptions: {
minValue: 1,
maxValue: 64,
},
default: 64,
description: 'How many results to return',
},
{
...destinationExternalField,
displayOptions: {
show: {
resource: [
'task',
],
operation: [
'create',
],
},
},
default: {},
required: true,
},
{
displayName: 'Complete as a Success',
name: 'success',
type: 'boolean',
displayOptions: {
show: {
resource: [
'task',
],
operation: [
'complete',
],
},
},
description: 'Whether the task\'s completion was successful',
required: true,
default: true,
},
{
displayName: 'Filters',
name: 'filters',
type: 'collection',
placeholder: 'Add Filter',
default: {},
displayOptions: {
show: {
resource: [
'task',
],
operation: [
'getAll',
],
},
},
options: [
{
displayName: 'From',
name: 'from',
type: 'dateTime',
default: '',
description: 'The starting time of the range. Tasks created or completed at or after this time will be included.',
},
{
displayName: 'State',
name: 'state',
type: 'multiOptions',
options: [
{
name: '[All]',
value: 'all',
},
{
name: 'Active',
value: 2,
},
{
name: 'Assigned',
value: 1,
},
{
name: 'Completed',
value: 3,
},
{
name: 'Unassigned',
value: 0,
},
],
default: ['all'],
description: 'The state of the tasks',
},
{
displayName: 'To',
name: 'to',
type: 'dateTime',
default: '',
description: 'The ending time of the range. Defaults to current time if not specified.',
},
],
},
{
displayName: 'Override Fields',
name: 'overrideFields',
type: 'collection',
placeholder: 'Add Field',
default: {},
displayOptions: {
show: {
resource: [
'task',
],
operation: [
'clone',
],
},
},
options: [
{
...completeAfterField,
},
{
...completeBeforeField,
},
{
displayName: 'Include Barcodes',
name: 'includeBarcodes',
type: 'boolean',
default: false,
},
{
displayName: 'Include Dependencies',
name: 'includeDependencies',
type: 'boolean',
default: false,
},
{
displayName: 'Include Metadata',
name: 'includeMetadata',
type: 'boolean',
default: false,
},
{
...notesField,
required: false,
},
{
...pickupTaskField,
required: false,
},
{
...serviceTimeField,
required: false,
},
],
},
{
displayName: 'Update Fields',
name: 'updateFields',
type: 'collection',
placeholder: 'Add Field',
default: {},
displayOptions: {
show: {
resource: [
'task',
],
operation: [
'update',
],
},
},
options: [
completeAfterField,
completeBeforeField,
executorIdField,
merchantIdField,
notesField,
pickupTaskField,
quantityField,
serviceTimeField,
],
},
{
displayName: 'Additional Fields',
name: 'additionalFields',
type: 'collection',
placeholder: 'Add Field',
default: {},
displayOptions: {
show: {
resource: [
'task',
],
operation: [
'complete',
],
},
},
options: [
{
displayName: 'Notes',
name: 'notes',
type: 'string',
default: '',
description: 'Completion Notes',
},
],
},
{
displayName: 'Additional Fields',
name: 'additionalFields',
type: 'collection',
placeholder: 'Add Field',
default: {},
displayOptions: {
show: {
resource: [
'task',
],
operation: [
'create',
],
},
},
options: [
completeAfterField,
completeBeforeField,
executorIdField,
merchantIdField,
notesField,
pickupTaskField,
quantityField,
recipientExternalField,
{
displayName: 'Recipient Name Override',
name: 'recipientName',
type: 'string',
default: '',
description: 'Override the recipient name for this task only',
},
{
displayName: 'Recipient Notes Override',
name: 'recipientNotes',
type: 'string',
default: '',
description: 'Override the recipient notes for this task only',
},
{
displayName: 'Recipient Skip SMS Notifications Override',
name: 'recipientSkipSMSNotifications',
type: 'boolean',
default: false,
description: 'Whether to override the recipient notification settings for this task',
},
serviceTimeField,
{
displayName: 'Use Merchant For Proxy Override',
name: 'useMerchantForProxy',
type: 'boolean',
default: false,
description: 'Whether to override the organization ID with the merchant\'s org ID for this task',
},
],
},
];

View file

@ -0,0 +1,600 @@
import {
INodeProperties
} from 'n8n-workflow';
export const teamOperations: INodeProperties[] = [
{
displayName: 'Operation',
name: 'operation',
type: 'options',
displayOptions: {
show: {
resource: [
'team',
],
},
},
options: [
{
name: 'Auto-Dispatch',
value: 'autoDispatch',
description: 'Automatically dispatch tasks assigned to a team to on-duty drivers',
},
{
name: 'Create',
value: 'create',
description: 'Create a new Onfleet team',
},
{
name: 'Delete',
value: 'delete',
description: 'Delete an Onfleet team',
},
{
name: 'Get',
value: 'get',
description: 'Get a specific Onfleet team',
},
{
name: 'Get All',
value: 'getAll',
description: 'Get all Onfleet teams',
},
{
name: 'Get Time Estimates',
value: 'getTimeEstimates',
description: 'Get estimated times for upcoming tasks for a team, returns a selected driver',
},
{
name: 'Update',
value: 'update',
description: 'Update an Onfleet team',
},
],
default: 'getAll',
},
];
const nameField = {
displayName: 'Name',
name: 'name',
type: 'string',
default: '',
description: 'A unique name for the team',
} as INodeProperties;
const workersField = {
displayName: 'Workers Names/IDs',
name: 'workers',
type: 'multiOptions',
typeOptions: {
loadOptionsMethod: 'getWorkers',
},
default: [],
description: 'A list of workers',
} as INodeProperties;
const managersField = {
displayName: 'Administrators Names/IDs',
name: 'managers',
type: 'multiOptions',
typeOptions: {
loadOptionsMethod: 'getAdmins',
},
default: [],
description: 'A list of managing administrators',
} as INodeProperties;
const hubField = {
displayName: 'Hub Name/ID',
name: 'hub',
type: 'options',
typeOptions: {
loadOptionsMethod: 'getHubs',
},
default: '',
description: 'The team\'s hub',
} as INodeProperties;
const enableSelfAssignmentField = {
displayName: 'Self Assignment',
name: 'enableSelfAssignment',
type: 'boolean',
default: false,
description: 'Whether or not to allow drivers to self-assign tasks that are in the Team\'s unassigned container',
} as INodeProperties;
const maxTasksPerRouteField = {
displayName: 'Max Number Of Tasks Per Route',
name: 'maxTasksPerRoute',
type: 'number',
default: 100,
typeOptions: {
maxValue: 200,
minValue: 1,
},
description: 'Total number of tasks allowed on a route',
} as INodeProperties;
const serviceTimeField = {
displayName: 'Service Time',
name: 'serviceTime',
type: 'number',
default: 2,
typeOptions: {
minValue: 0,
},
description: 'The default service time to apply in Minutes to the tasks when no task service time exists',
} as INodeProperties;
const routeEndField = {
displayName: 'Route End',
name: 'routeEnd',
type: 'options',
options: [
{
name: 'Teams Hub',
value: 'team_hub',
},
{
name: 'Worker Routing Address',
value: 'worker_routing_address',
},
{
name: 'Hub',
value: 'hub',
},
{
name: 'End Anywhere',
value: 'anywhere',
},
],
default: '',
description: 'Where the route will end',
} as INodeProperties;
const maxAllowedDelayField = {
displayName: 'Max Allowed Delay',
name: 'maxAllowedDelay',
type: 'number',
default: 10,
description: 'Max allowed time in minutes that a task can be late',
typeOptions: {
minValue: 1,
},
} as INodeProperties;
const longitudeDropOffField = {
displayName: 'Drop Off Longitude',
name: 'dropOffLongitude',
type: 'number',
typeOptions: {
numberPrecision: 14,
},
default: 0,
description: 'The longitude for drop off location',
} as INodeProperties;
const latitudeDropOffField = {
displayName: 'Drop Off Latitude',
name: 'dropOffLatitude',
type: 'number',
typeOptions: {
numberPrecision: 14,
},
default: 0,
description: 'The latitude for drop off location',
} as INodeProperties;
const longitudePickupField = {
displayName: 'Pick Up Longitude',
name: 'pickupLongitude',
type: 'number',
typeOptions: {
numberPrecision: 14,
},
default: 0,
description: 'The longitude for pickup location',
} as INodeProperties;
const latitudePickupField = {
displayName: 'Pick Up Latitude',
name: 'pickupLatitude',
type: 'number',
typeOptions: {
numberPrecision: 14,
},
default: 0,
description: 'The latitude for pickup location',
} as INodeProperties;
const pickupTimeField = {
displayName: 'Pick Up Time',
name: 'pickupTime',
type: 'dateTime',
default: '',
description: 'If the request includes pickupLocation, pickupTime must be present if the time is fewer than 3 hours in the future',
} as INodeProperties;
const restrictedVehicleTypesField = {
displayName: 'Restricted Vehicle Types',
name: 'restrictedVehicleTypes',
type: 'options',
options: [
{
name: 'Car',
value: 'CAR',
},
{
name: 'Motorcycle',
value: 'MOTORCYCLE',
},
{
name: 'Bicycle',
value: 'BICYCLE',
},
{
name: 'Truck',
value: 'TRUCK',
},
],
default: 'CAR',
description: 'Vehicle types to ignore in the query',
} as INodeProperties;
const serviceTimeEstimateField = {
displayName: 'Service Time',
name: 'serviceTime',
type: 'number',
default: 120,
typeOptions: {
minValue: 0,
},
description: 'The expected time a worker will take at the pickupLocation, dropoffLocation, or both (as applicable) Unit: seconds',
} as INodeProperties;
export const teamFields: INodeProperties[] = [
{
displayName: 'Team ID',
name: 'id',
type: 'string',
displayOptions: {
show: {
resource: [
'team',
],
operation: [
'get',
'update',
'delete',
'getTimeEstimates',
'autoDispatch',
],
},
},
default: '',
required: true,
description: 'The ID of the team object for lookup',
},
{
...nameField,
displayOptions: {
show: {
resource: [
'team',
],
operation: [
'create',
],
},
},
required: true,
},
{
...workersField,
displayOptions: {
show: {
resource: [
'team',
],
operation: [
'create',
],
},
},
required: true,
},
{
...managersField,
displayOptions: {
show: {
resource: [
'team',
],
operation: [
'create',
],
},
},
required: true,
},
{
displayName: 'Additional Fields',
name: 'additionalFields',
type: 'collection',
placeholder: 'Add Field',
default: {},
displayOptions: {
show: {
resource: [
'team',
],
operation: [
'create',
],
},
},
options: [
hubField,
enableSelfAssignmentField,
],
},
{
displayName: 'Return All',
name: 'returnAll',
type: 'boolean',
displayOptions: {
show: {
resource: [
'team',
],
operation: [
'getAll',
],
},
},
default: false,
description: 'If all results should be returned or only up to a given limit',
},
{
displayName: 'Limit',
name: 'limit',
type: 'number',
displayOptions: {
show: {
resource: [
'team',
],
operation: [
'getAll',
],
returnAll: [
false,
],
},
},
typeOptions: {
minValue: 1,
maxValue: 64,
},
default: 64,
description: 'How many results to return',
},
{
displayName: 'Update Fields',
name: 'updateFields',
type: 'collection',
placeholder: 'Add Field',
default: {},
displayOptions: {
show: {
resource: [
'team',
],
operation: [
'update',
],
},
},
options: [
managersField,
hubField,
nameField,
enableSelfAssignmentField,
workersField,
],
},
{
displayName: 'Additional Fields',
name: 'additionalFields',
type: 'collection',
placeholder: 'Add Field',
default: {},
displayOptions: {
show: {
resource: [
'team',
],
operation: [
'autoDispatch',
],
},
},
options: [
{
displayName: 'Ending Route',
name: 'endingRoute',
type: 'fixedCollection',
placeholder: 'Add Route',
default: {},
options: [
{
displayName: 'Ending Route Properties',
name: 'endingRouteProperties',
type: 'fixedCollection',
default: {},
values: [
{
...routeEndField,
required: true,
},
{
...hubField,
displayOptions: {
show: {
routeEnd: [
'hub',
],
},
},
required: false,
},
],
},
],
},
maxAllowedDelayField,
maxTasksPerRouteField,
{
displayName: 'Schedule Time Window',
name: 'scheduleTimeWindow',
type: 'fixedCollection',
placeholder: 'Add Time Window',
default: {},
options: [
{
displayName: 'Schedule Time Window Properties',
name: 'scheduleTimeWindowProperties',
type: 'fixedCollection',
default: {},
values: [
{
displayName: 'Start Time',
name: 'startTime',
type: 'dateTime',
default: '',
},
{
displayName: 'End Time',
name: 'endTime',
type: 'dateTime',
default: '',
},
],
},
],
},
serviceTimeField,
{
displayName: 'Task Time Window',
name: 'taskTimeWindow',
type: 'fixedCollection',
placeholder: 'Add Time Window',
default: {},
options: [
{
displayName: 'Task Time Window Properties',
name: 'taskTimeWindowProperties',
type: 'fixedCollection',
default: {},
values: [
{
displayName: 'Start Time',
name: 'startTime',
type: 'dateTime',
default: '',
},
{
displayName: 'End Time',
name: 'endTime',
type: 'dateTime',
default: '',
},
],
},
],
},
],
},
{
displayName: 'Filters',
name: 'filters',
type: 'collection',
placeholder: 'Add Field',
default: {},
displayOptions: {
show: {
resource: [
'team',
],
operation: [
'getTimeEstimates',
],
},
},
options: [
{
displayName: 'Drop Off',
name: 'dropOff',
type: 'fixedCollection',
placeholder: 'Add Drop Off',
default: {},
options: [
{
displayName: 'DropOff Properties',
name: 'dropOffProperties',
type: 'fixedCollection',
default: {},
values: [
{
...longitudeDropOffField,
required: true,
},
{
...latitudeDropOffField,
required: true,
},
],
},
],
},
{
displayName: 'Pick Up',
name: 'pickUp',
type: 'fixedCollection',
default: {},
placeholder: 'Add Pick Up',
options: [
{
displayName: 'Pick Up Properties',
name: 'pickUpProperties',
type: 'fixedCollection',
default: {},
values: [
{
...longitudePickupField,
required: true,
},
{
...latitudePickupField,
required: true,
},
{
...pickupTimeField,
required: false,
},
],
},
],
},
{
...restrictedVehicleTypesField,
required: false,
},
{
...serviceTimeEstimateField,
required: false,
},
],
},
];

View file

@ -0,0 +1,161 @@
import {
INodeProperties
} from 'n8n-workflow';
import {
webhookMapping,
} from '../WebhookMapping';
export const webhookOperations: INodeProperties[] = [
{
displayName: 'Operation',
name: 'operation',
type: 'options',
displayOptions: {
show: {
resource: [
'webhook',
],
},
},
options: [
{
name: 'Create',
value: 'create',
description: 'Create a new Onfleet webhook',
},
{
name: 'Delete',
value: 'delete',
description: 'Delete an Onfleet webhook',
},
{
name: 'Get All',
value: 'getAll',
description: 'Get all Onfleet webhooks',
},
],
default: 'getAll',
},
];
const urlField = {
displayName: 'URL',
name: 'url',
type: 'string',
default: '',
description: 'The URL that Onfleet should issue a request against as soon as the trigger condition is met. It must be HTTPS and have a valid certificate.',
} as INodeProperties;
const nameField = {
displayName: 'Name',
name: 'name',
type: 'string',
default: '',
description: 'A name for the webhook for identification',
} as INodeProperties;
const triggerField = {
displayName: 'Trigger',
name: 'trigger',
type: 'options',
options: Object.entries(webhookMapping).map(([key, value]) => {
return {
name: value.name,
value: value.key,
};
}),
default: '',
description: 'The number corresponding to the trigger condition on which the webhook should fire',
} as INodeProperties;
const thresholdField = {
displayName: 'Threshold',
name: 'threshold',
type: 'number',
default: 0,
description: 'For trigger Task Eta, the time threshold in seconds; for trigger Task Arrival, the distance threshold in meters',
} as INodeProperties;
export const webhookFields: INodeProperties[] = [
{
displayName: 'Webhook ID',
name: 'id',
type: 'string',
displayOptions: {
show: {
resource: [
'webhook',
],
operation: [
'delete',
],
},
},
default: '',
required: true,
description: 'The ID of the webhook object for lookup',
},
{
...urlField,
displayOptions: {
show: {
resource: [
'webhook',
],
operation: [
'create',
],
},
},
required: true,
},
{
...nameField,
displayOptions: {
show: {
resource: [
'webhook',
],
operation: [
'create',
],
},
},
required: true,
},
{
...triggerField,
displayOptions: {
show: {
resource: [
'webhook',
],
operation: [
'create',
],
},
},
required: true,
},
{
displayName: 'Additional Fields',
name: 'additionalFields',
type: 'collection',
placeholder: 'Add Field',
default: {},
displayOptions: {
show: {
resource: [
'webhook',
],
operation: [
'create',
],
},
},
options: [
thresholdField,
],
},
];

View file

@ -0,0 +1,754 @@
import {
INodeProperties
} from 'n8n-workflow';
export const workerOperations: INodeProperties[] = [
{
displayName: 'Operation',
name: 'operation',
type: 'options',
displayOptions: {
show: {
resource: ['worker'],
},
},
options: [
{
name: 'Create',
value: 'create',
description: 'Create a new Onfleet worker',
},
{
name: 'Delete',
value: 'delete',
description: 'Delete an Onfleet worker',
},
{
name: 'Get',
value: 'get',
description: 'Get a specific Onfleet worker',
},
{
name: 'Get All',
value: 'getAll',
description: 'Get all Onfleet workers',
},
{
name: 'Get Schedule',
value: 'getSchedule',
description: 'Get a specific Onfleet worker schedule',
},
// {
// name: 'Set Worker\'s Schedule',
// value: 'setSchedule',
// description: 'Set the worker\'s schedule',
// },
{
name: 'Update',
value: 'update',
description: 'Update an Onfleet worker',
},
],
default: 'get',
},
];
const byLocationField = {
displayName: 'Search by Location',
name: 'byLocation',
type: 'boolean',
default: false,
description: 'Whether to search for only those workers who are currently within a certain target area',
} as INodeProperties;
const nameField = {
displayName: 'Name',
name: 'name',
type: 'string',
default: '',
description: 'The worker\'s name',
} as INodeProperties;
const phoneField = {
displayName: 'Phone',
name: 'phone',
type: 'string',
default: '',
description: 'A list of workers phone numbers',
} as INodeProperties;
const capacityField = {
displayName: 'Capacity',
name: 'capacity',
type: 'number',
default: 0,
description: 'The maximum number of units this worker can carry, for route optimization purposes',
} as INodeProperties;
const displayNameField = {
displayName: 'Display Name',
name: 'displayName',
type: 'string',
default: '',
description: 'This value is used in place of the worker\'s actual name within sms notifications, delivery tracking pages, and across organization boundaries',
} as INodeProperties;
const vehicleTypeField = {
displayName: 'Type',
name: 'type',
type: 'options',
options: [
{
name: 'Bicycle',
value: 'BICYCLE',
},
{
name: 'Car',
value: 'CAR',
},
{
name: 'Motorcycle',
value: 'MOTORCYCLE',
},
{
name: 'Truck',
value: 'TRUCK',
},
],
default: '',
description: 'Whether the worker has vehicle or not. If it\'s not provided, this worker will be treated as if on foot.',
} as INodeProperties;
const vehicleDescriptionField = {
displayName: 'Description',
name: 'description',
type: 'string',
default: '',
description: 'The vehicle\'s make, model, year, or any other relevant identifying details',
} as INodeProperties;
const vehicleLicensePlateField = {
displayName: 'License Plate',
name: 'licensePlate',
type: 'string',
default: '',
description: 'The vehicle\'s license plate number',
} as INodeProperties;
const vehicleColorField = {
displayName: 'Color',
name: 'color',
type: 'string',
default: '',
description: 'The vehicle\'s color',
} as INodeProperties;
const teamsField = {
displayName: 'Teams Names/IDs',
name: 'teams',
type: 'multiOptions',
typeOptions: {
loadOptionsMethod: 'getTeams',
},
default: [],
description: 'One or more teams of which the worker is a member',
} as INodeProperties;
const teamsFilterField = {
displayName: 'Teams ID/Name',
name: 'teams',
type: 'multiOptions',
typeOptions: {
loadOptionsMethod: 'getTeams',
},
default: [],
description: 'A list of the teams that workers must be part of',
} as INodeProperties;
const statesFilterField = {
displayName: 'States',
name: 'states',
type: 'multiOptions',
options: [
{
name: 'Active (On-Duty, Active Task)',
value: 2,
},
{
name: 'Idle (On-Duty, No Active Task)',
value: 1,
},
{
name: 'Off-Duty',
value: 0,
},
],
default: [],
description: 'List of worker states',
} as INodeProperties;
const phonesFilterField = {
displayName: 'Phones',
name: 'phones',
type: 'string',
typeOptions: {
multipleValues: true,
multipleValueButtonText: 'Add Phone',
},
default: [],
description: 'A list of workers\' phone numbers',
} as INodeProperties;
const filterField = {
displayName: 'Fields to Return',
name: 'filter',
type: 'multiOptions',
options: [
{
name: 'Account Status',
value: 'accountStatus',
},
{
name: 'Active Task',
value: 'activeTask',
},
{
name: 'Capacity',
value: 'capacity',
},
{
name: 'Delay Time',
value: 'delayTime',
},
{
name: 'Display Name',
value: 'displayName',
},
{
name: 'Image Url',
value: 'imageUrl',
},
{
name: 'Location',
value: 'location',
},
{
name: 'Metadata',
value: 'metadata',
},
{
name: 'Name',
value: 'name',
},
{
name: 'On Duty',
value: 'onDuty',
},
{
name: 'Organization',
value: 'organization',
},
{
name: 'Phone',
value: 'phone',
},
{
name: 'Tasks',
value: 'tasks',
},
{
name: 'Teams',
value: 'teams',
},
{
name: 'Time Created',
value: 'timeCreated',
},
{
name: 'Time Last Modified',
value: 'timeLastModified',
},
{
name: 'Time Last Seen',
value: 'timeLastSeen',
},
{
name: 'User Data',
value: 'userData',
},
{
name: 'Vehicle',
value: 'vehicle',
},
{
name: 'Worker ID',
value: 'id',
},
],
default: [],
description: 'A list of fields to show in the response, if all are not desired',
} as INodeProperties;
const longitudeFilterField = {
displayName: 'Longitude',
name: 'longitude',
type: 'number',
typeOptions: {
numberPrecision: 14,
},
default: 0,
description: 'The longitude component of the coordinate pair',
} as INodeProperties;
const latitudeFilterField = {
displayName: 'Latitude',
name: 'latitude',
type: 'number',
typeOptions: {
numberPrecision: 14,
},
default: 0,
description: 'The latitude component of the coordinate pair',
} as INodeProperties;
const radiusFilterField = {
displayName: 'Radius',
name: 'radius',
type: 'number',
typeOptions: {
maxValue: 10000,
minValue: 0,
},
default: 1000,
description: 'The length in meters of the radius of the spherical area in which to look for workers. Defaults to 1000 if missing. Maximum value is 10000.',
} as INodeProperties;
const scheduleDateField = {
displayName: 'Date',
name: 'date',
type: 'dateTime',
default: '',
description: 'Schedule\'s date',
} as INodeProperties;
const scheduleTimezoneField = {
displayName: 'Timezone',
name: 'timezone',
type: 'options',
typeOptions: {
loadOptionsMethod: 'getTimezones',
},
default: '',
description: 'A valid timezone',
} as INodeProperties;
const scheduleStartField = {
displayName: 'Start',
name: 'start',
type: 'dateTime',
default: '',
description: 'Start time',
} as INodeProperties;
const scheduleEndField = {
displayName: 'End',
name: 'end',
type: 'dateTime',
default: '',
description: 'End time',
} as INodeProperties;
export const workerFields: INodeProperties[] = [
{
...byLocationField,
required: true,
displayOptions: {
show: {
resource: [
'worker',
],
operation: [
'getAll',
],
},
},
},
{
displayName: 'Worker ID',
name: 'id',
type: 'string',
displayOptions: {
show: {
resource: [
'worker',
],
operation: [
'get',
'getSchedule',
'setSchedule',
'update',
'delete',
],
},
},
default: '',
required: true,
description: 'The ID of the worker object for lookup',
},
{
...nameField,
displayOptions: {
show: {
resource: [
'worker',
],
operation: [
'create',
],
},
},
required: true,
},
{
...phoneField,
displayOptions: {
show: {
resource: [
'worker',
],
operation: [
'create',
],
},
},
required: true,
},
{
...teamsField,
displayOptions: {
show: {
resource: [
'worker',
],
operation: [
'create',
],
},
},
required: true,
},
{
...longitudeFilterField,
displayOptions: {
show: {
resource: [
'worker',
],
operation: [
'getAll',
],
byLocation: [
true,
],
},
},
required: true,
},
{
...latitudeFilterField,
displayOptions: {
show: {
resource: [
'worker',
],
operation: [
'getAll',
],
byLocation: [
true,
],
},
},
required: true,
},
{
displayName: 'Return All',
name: 'returnAll',
type: 'boolean',
displayOptions: {
show: {
resource: [
'worker',
],
operation: [
'getAll',
],
},
},
default: false,
description: 'If all results should be returned or only up to a given limit',
},
{
displayName: 'Limit',
name: 'limit',
type: 'number',
displayOptions: {
show: {
resource: [
'worker',
],
operation: [
'getAll',
],
returnAll: [
false,
],
},
},
typeOptions: {
minValue: 1,
maxValue: 64,
},
default: 64,
description: 'How many results to return',
},
{
displayName: 'Additional Fields',
name: 'additionalFields',
type: 'collection',
placeholder: 'Add Field',
default: {},
displayOptions: {
show: {
resource: [
'worker',
],
operation: [
'create',
],
},
},
options: [
capacityField,
displayNameField,
{
displayName: 'Vehicle',
name: 'vehicle',
type: 'fixedCollection',
placeholder: 'Add Vehicle',
default: {},
options: [
{
displayName: 'Vehicle Properties',
name: 'vehicleProperties',
values: [
{
...vehicleTypeField,
required: true,
},
{
...vehicleDescriptionField,
required: false,
},
{
...vehicleLicensePlateField,
required: false,
},
{
...vehicleColorField,
required: false,
},
],
},
],
},
],
},
{
displayName: 'Update Fields',
name: 'updateFields',
type: 'collection',
placeholder: 'Add Field',
default: {},
displayOptions: {
show: {
resource: [
'worker',
],
operation: [
'update',
],
},
},
options: [
capacityField,
displayNameField,
nameField,
teamsField,
],
},
{
displayName: 'Filters',
name: 'filters',
type: 'collection',
placeholder: 'Add Field',
default: {},
displayOptions: {
show: {
resource: [
'worker',
],
operation: [
'getAll',
],
byLocation: [
true,
],
},
},
options: [
radiusFilterField,
],
},
{
displayName: 'Filters',
name: 'filters',
type: 'collection',
placeholder: 'Add Field',
default: {},
displayOptions: {
show: {
resource: [
'worker',
],
operation: [
'getAll',
],
byLocation: [
false,
],
},
},
options: [
statesFilterField,
teamsFilterField,
phonesFilterField,
],
},
{
displayName: 'Options',
name: 'options',
type: 'collection',
placeholder: 'Add Field',
default: {},
displayOptions: {
show: {
resource: [
'worker',
],
operation: [
'getAll',
],
},
},
options: [
filterField,
],
},
{
displayName: 'Options',
name: 'options',
type: 'collection',
placeholder: 'Add Field',
default: {},
displayOptions: {
show: {
resource: [
'worker',
],
operation: [
'get',
],
},
},
options: [
{
displayName: 'Analytics',
name: 'analytics',
type: 'boolean',
default: true,
required: false,
description: 'Whether a more detailed response is needed, includes basic worker duty event, traveled distance (meters) and time analytics',
},
{
...filterField,
required: false,
},
],
},
{
displayName: 'Schedule',
name: 'schedule',
type: 'fixedCollection',
placeholder: 'Add Schedule',
displayOptions: {
show: {
resource: [
'worker',
],
operation: [
'setSchedule',
],
},
},
default: {},
typeOptions: {
multipleValues: true,
multipleValueButtonText: 'Add Schedule',
},
options: [
{
displayName: 'Schedule Properties',
name: 'scheduleProperties',
default: {},
values: [
{
...scheduleDateField,
required: true,
},
{
...scheduleTimezoneField,
required: true,
},
{
displayName: 'Shifts',
name: 'shifts',
type: 'fixedCollection',
default: {},
placeholder: 'Add Shift',
typeOptions: {
multipleValues: true,
},
options: [
{
displayName: 'Shifts Properties',
name: 'shiftsProperties',
default: {},
values: [
{
...scheduleStartField,
required: true,
},
{
...scheduleEndField,
required: true,
},
],
},
],
},
],
},
],
},
];

View file

@ -0,0 +1,184 @@
export interface OnfleetRecipient {
name?: string;
phone?: string;
notes?: string;
skipSMSNotifications?: boolean;
skipPhoneNumberValidation?: boolean;
}
export interface OnfleetDestinationAddress {
name?: string;
number?: string;
street?: string;
apartment?: string;
city?: string;
state?: string;
postalCode?: string;
country?: string;
unparsed?: string;
}
export interface OnfleetDestinationOptions {
language?: string;
}
export interface OnfleetDestination {
address: OnfleetDestinationAddress;
location?: [number, number];
notes?: string;
options?: OnfleetDestinationOptions;
}
export interface OnfleetTask {
merchant?: string;
executor?: string;
destination: OnfleetDestination;
recipients: OnfleetRecipient[];
completeAfter?: number;
completeBefore?: number;
pickupTask?: boolean;
notes?: string;
quantity?: number;
serviceTime?: number;
}
export interface OnfleetTaskUpdate {
merchant?: string;
executor?: string;
completeAfter?: number;
completeBefore?: number;
pickupTask?: boolean;
notes?: string;
quantity?: number;
serviceTime?: number;
}
export interface OnfleetListTaskFilters {
from?: number;
to?: number;
lastId?: string;
state?: string;
worker?: string;
completeBeforeBefore?: number;
completeAfterAfter?: number;
dependencies?: string;
}
export interface OnfleetCloneOverrideTaskOptions {
completeAfter?: number;
completeBefore?: number;
destination?: OnfleetDestination;
notes?: string;
pickupTask?: boolean;
recipients?: OnfleetRecipient[];
serviceTime?: number;
}
export interface OnfleetCloneTaskOptions {
includeMetadata?: boolean;
includeBarcodes?: boolean;
includeDependencies?: boolean;
overrides?: OnfleetCloneOverrideTaskOptions;
}
export interface OnfleetCloneTask {
options?: OnfleetCloneTaskOptions;
}
export interface OnfleetTaskCompletionDetails {
success: boolean;
notes?: string;
}
export interface OnfleetTaskComplete {
completionDetails: OnfleetTaskCompletionDetails;
}
export interface OnfleetAdmins {
name?: string;
email?: string;
phone?: string;
isReadOnly?: boolean;
}
export interface OnfleetHubs extends OnfleetDestination {
name?: string;
teams?: string[];
}
export interface OnfleetVehicle {
type?: string;
description?: string;
licensePlate?: string;
color?: string;
}
export interface OnfleetWorker {
name?: string;
phone?: string;
vehicle?: OnfleetVehicle;
teams?: string[];
capacity?: number;
displayName?: string;
}
export interface OnfleetWorkerFilter {
[key: string]: string | undefined;
filter?: string;
teams?: string;
states?: string;
phones?: string;
analytics?: string;
}
export interface OnfleetWorkerScheduleEntry {
date?: string;
timezone?: string;
shifts?: [[number, number]];
}
export interface OnfleetWebhook {
url?: string;
name?: string;
trigger?: number;
threshold?: number;
}
export interface OnfleetTeams {
name?: string;
workers?: string[];
managers?: string[];
hub?: string;
enableSelfAssignment?: boolean;
}
export interface OnfleetWorkerSchedule {
entries: OnfleetWorkerScheduleEntry[];
}
export interface OnfleetWebhookMapping {
key: number;
name: string;
value: string;
}
export interface OnfleetWebhooksMapping {
[key: string]: OnfleetWebhookMapping;
}
export interface OnfleetWorkerEstimates {
dropoffLocation?: string;
pickupLocation?: string;
pickupTime?: number;
restrictedVehicleTypes?: string;
serviceTime?: number;
}
export interface OnfleetTeamAutoDispatch {
maxTasksPerRoute?: number;
taskTimeWindow?: [number, number];
scheduleTimeWindow?: [number, number];
serviceTime?: number;
routeEnd?: string;
maxAllowedDelay?: number;
}

View file

@ -210,6 +210,7 @@
"dist/credentials/OAuth1Api.credentials.js",
"dist/credentials/OAuth2Api.credentials.js",
"dist/credentials/OneSimpleApi.credentials.js",
"dist/credentials/OnfleetApi.credentials.js",
"dist/credentials/OpenWeatherMapApi.credentials.js",
"dist/credentials/OrbitApi.credentials.js",
"dist/credentials/OuraApi.credentials.js",
@ -543,6 +544,8 @@
"dist/nodes/NextCloud/NextCloud.node.js",
"dist/nodes/NocoDB/NocoDB.node.js",
"dist/nodes/NoOp/NoOp.node.js",
"dist/nodes/Onfleet/Onfleet.node.js",
"dist/nodes/Onfleet/OnfleetTrigger.node.js",
"dist/nodes/Notion/Notion.node.js",
"dist/nodes/Notion/NotionTrigger.node.js",
"dist/nodes/OneSimpleApi/OneSimpleApi.node.js",

View file

@ -13,6 +13,7 @@ import { INode, IStatusCodeMessages, JsonObject } from '.';
* Top-level properties where an error message can be found in an API response.
*/
const ERROR_MESSAGE_PROPERTIES = [
'cause',
'error',
'message',
'Message',