Skip to content

Commit

Permalink
✨ Add Onfleet Node & Trigger (n8n-io#2845)
Browse files Browse the repository at this point in the history
* 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 n8n-io#2593

* ⚡ Improvements

* 🐛 Fix issue with wrong interface

* ⚡ Improvements

* ⚡ Improvements

* ⚡ Minor improvement

Co-authored-by: Santiago Botero Ruiz <[email protected]>
Co-authored-by: ilsemaj <[email protected]>
Co-authored-by: Santiago Botero Ruiz <[email protected]>
Co-authored-by: Jan Oberhauser <[email protected]>
  • Loading branch information
5 people authored Feb 28, 2022
1 parent 2ec4ed6 commit 401e626
Show file tree
Hide file tree
Showing 21 changed files with 5,593 additions and 0 deletions.
18 changes: 18 additions & 0 deletions packages/nodes-base/credentials/OnfleetApi.credentials.ts
Original file line number Diff line number Diff line change
@@ -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: '',
},
];
}
123 changes: 123 additions & 0 deletions packages/nodes-base/nodes/Onfleet/GenericFunctions.ts
Original file line number Diff line number Diff line change
@@ -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;
},
};
230 changes: 230 additions & 0 deletions packages/nodes-base/nodes/Onfleet/Onfleet.node.ts
Original file line number Diff line number Diff line change
@@ -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)];
}
}
14 changes: 14 additions & 0 deletions packages/nodes-base/nodes/Onfleet/Onfleet.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading

0 comments on commit 401e626

Please sign in to comment.