Merge branch 'n8n-io:master' into Add-schema-registry-into-kafka
551
packages/nodes-base/nodes/ActionNetwork/ActionNetwork.node.ts
Normal file
@@ -0,0 +1,551 @@
|
||||
import {
|
||||
IExecuteFunctions,
|
||||
} from 'n8n-core';
|
||||
|
||||
import {
|
||||
IDataObject,
|
||||
INodeExecutionData,
|
||||
INodeType,
|
||||
INodeTypeDescription,
|
||||
NodeOperationError,
|
||||
} from 'n8n-workflow';
|
||||
|
||||
import {
|
||||
actionNetworkApiRequest,
|
||||
adjustEventPayload,
|
||||
adjustPersonPayload,
|
||||
adjustPetitionPayload,
|
||||
handleListing,
|
||||
makeOsdiLink,
|
||||
resourceLoaders,
|
||||
simplifyResponse,
|
||||
} from './GenericFunctions';
|
||||
|
||||
import {
|
||||
attendanceFields,
|
||||
attendanceOperations,
|
||||
eventFields,
|
||||
eventOperations,
|
||||
personFields,
|
||||
personOperations,
|
||||
personTagFields,
|
||||
personTagOperations,
|
||||
petitionFields,
|
||||
petitionOperations,
|
||||
signatureFields,
|
||||
signatureOperations,
|
||||
tagFields,
|
||||
tagOperations,
|
||||
} from './descriptions';
|
||||
|
||||
import {
|
||||
AllFieldsUi,
|
||||
EmailAddressUi,
|
||||
Operation,
|
||||
PersonResponse,
|
||||
Resource,
|
||||
Response,
|
||||
} from './types';
|
||||
|
||||
export class ActionNetwork implements INodeType {
|
||||
description: INodeTypeDescription = {
|
||||
displayName: 'Action Network',
|
||||
name: 'actionNetwork',
|
||||
icon: 'file:actionNetwork.svg',
|
||||
group: ['transform'],
|
||||
version: 1,
|
||||
subtitle: '={{$parameter["resource"] + ": " + $parameter["operation"]}}',
|
||||
description: 'Consume the Action Network API',
|
||||
defaults: {
|
||||
name: 'Action Network',
|
||||
color: '#9dd3ed',
|
||||
},
|
||||
inputs: ['main'],
|
||||
outputs: ['main'],
|
||||
credentials: [
|
||||
{
|
||||
name: 'actionNetworkApi',
|
||||
required: true,
|
||||
},
|
||||
],
|
||||
properties: [
|
||||
{
|
||||
displayName: 'Resource',
|
||||
name: 'resource',
|
||||
type: 'options',
|
||||
options: [
|
||||
{
|
||||
name: 'Attendance',
|
||||
value: 'attendance',
|
||||
},
|
||||
{
|
||||
name: 'Event',
|
||||
value: 'event',
|
||||
},
|
||||
{
|
||||
name: 'Person',
|
||||
value: 'person',
|
||||
},
|
||||
{
|
||||
name: 'Person Tag',
|
||||
value: 'personTag',
|
||||
},
|
||||
{
|
||||
name: 'Petition',
|
||||
value: 'petition',
|
||||
},
|
||||
{
|
||||
name: 'Signature',
|
||||
value: 'signature',
|
||||
},
|
||||
{
|
||||
name: 'Tag',
|
||||
value: 'tag',
|
||||
},
|
||||
],
|
||||
default: 'attendance',
|
||||
description: 'Resource to consume',
|
||||
},
|
||||
...attendanceOperations,
|
||||
...attendanceFields,
|
||||
...eventOperations,
|
||||
...eventFields,
|
||||
...personOperations,
|
||||
...personFields,
|
||||
...petitionOperations,
|
||||
...petitionFields,
|
||||
...signatureOperations,
|
||||
...signatureFields,
|
||||
...tagOperations,
|
||||
...tagFields,
|
||||
...personTagOperations,
|
||||
...personTagFields,
|
||||
],
|
||||
};
|
||||
|
||||
methods = {
|
||||
loadOptions: resourceLoaders,
|
||||
};
|
||||
|
||||
async execute(this: IExecuteFunctions): Promise<INodeExecutionData[][]> {
|
||||
const items = this.getInputData();
|
||||
const returnData: IDataObject[] = [];
|
||||
|
||||
const resource = this.getNodeParameter('resource', 0) as Resource;
|
||||
const operation = this.getNodeParameter('operation', 0) as Operation;
|
||||
|
||||
let response;
|
||||
|
||||
for (let i = 0; i < items.length; i++) {
|
||||
try {
|
||||
if (resource === 'attendance') {
|
||||
|
||||
// **********************************************************************
|
||||
// attendance
|
||||
// **********************************************************************
|
||||
|
||||
// https://actionnetwork.org/docs/v2/attendances
|
||||
|
||||
if (operation === 'create') {
|
||||
|
||||
// ----------------------------------------
|
||||
// attendance: create
|
||||
// ----------------------------------------
|
||||
|
||||
const personId = this.getNodeParameter('personId', i) as string;
|
||||
const eventId = this.getNodeParameter('eventId', i);
|
||||
|
||||
const body = makeOsdiLink(personId) as IDataObject;
|
||||
|
||||
const endpoint = `/events/${eventId}/attendances`;
|
||||
response = await actionNetworkApiRequest.call(this, 'POST', endpoint, body);
|
||||
|
||||
} else if (operation === 'get') {
|
||||
|
||||
// ----------------------------------------
|
||||
// attendance: get
|
||||
// ----------------------------------------
|
||||
|
||||
const eventId = this.getNodeParameter('eventId', i);
|
||||
const attendanceId = this.getNodeParameter('attendanceId', i);
|
||||
|
||||
const endpoint = `/events/${eventId}/attendances/${attendanceId}`;
|
||||
response = await actionNetworkApiRequest.call(this, 'GET', endpoint);
|
||||
|
||||
} else if (operation === 'getAll') {
|
||||
|
||||
// ----------------------------------------
|
||||
// attendance: getAll
|
||||
// ----------------------------------------
|
||||
|
||||
const eventId = this.getNodeParameter('eventId', i);
|
||||
|
||||
const endpoint = `/events/${eventId}/attendances`;
|
||||
response = await handleListing.call(this, 'GET', endpoint);
|
||||
|
||||
}
|
||||
|
||||
} else if (resource === 'event') {
|
||||
|
||||
// **********************************************************************
|
||||
// event
|
||||
// **********************************************************************
|
||||
|
||||
// https://actionnetwork.org/docs/v2/events
|
||||
|
||||
if (operation === 'create') {
|
||||
|
||||
// ----------------------------------------
|
||||
// event: create
|
||||
// ----------------------------------------
|
||||
|
||||
const body = {
|
||||
origin_system: this.getNodeParameter('originSystem', i),
|
||||
title: this.getNodeParameter('title', i),
|
||||
} as IDataObject;
|
||||
|
||||
const additionalFields = this.getNodeParameter('additionalFields', i) as AllFieldsUi;
|
||||
|
||||
if (Object.keys(additionalFields).length) {
|
||||
Object.assign(body, adjustEventPayload(additionalFields));
|
||||
}
|
||||
|
||||
response = await actionNetworkApiRequest.call(this, 'POST', '/events', body);
|
||||
|
||||
} else if (operation === 'get') {
|
||||
|
||||
// ----------------------------------------
|
||||
// event: get
|
||||
// ----------------------------------------
|
||||
|
||||
const eventId = this.getNodeParameter('eventId', i);
|
||||
|
||||
response = await actionNetworkApiRequest.call(this, 'GET', `/events/${eventId}`);
|
||||
|
||||
} else if (operation === 'getAll') {
|
||||
|
||||
// ----------------------------------------
|
||||
// event: getAll
|
||||
// ----------------------------------------
|
||||
|
||||
response = await handleListing.call(this, 'GET', '/events');
|
||||
|
||||
}
|
||||
|
||||
} else if (resource === 'person') {
|
||||
|
||||
// **********************************************************************
|
||||
// person
|
||||
// **********************************************************************
|
||||
|
||||
// https://actionnetwork.org/docs/v2/people
|
||||
|
||||
if (operation === 'create') {
|
||||
|
||||
// ----------------------------------------
|
||||
// person: create
|
||||
// ----------------------------------------
|
||||
|
||||
const emailAddresses = this.getNodeParameter('email_addresses', i) as EmailAddressUi;
|
||||
|
||||
const body = {
|
||||
person: {
|
||||
email_addresses: [emailAddresses.email_addresses_fields], // only one accepted by API
|
||||
},
|
||||
} as IDataObject;
|
||||
|
||||
const additionalFields = this.getNodeParameter('additionalFields', i) as IDataObject;
|
||||
|
||||
if (Object.keys(additionalFields).length) {
|
||||
Object.assign(body.person, adjustPersonPayload(additionalFields));
|
||||
}
|
||||
|
||||
response = await actionNetworkApiRequest.call(this, 'POST', '/people', body);
|
||||
|
||||
} else if (operation === 'get') {
|
||||
|
||||
// ----------------------------------------
|
||||
// person: get
|
||||
// ----------------------------------------
|
||||
|
||||
const personId = this.getNodeParameter('personId', i);
|
||||
|
||||
response = await actionNetworkApiRequest.call(this, 'GET', `/people/${personId}`) as PersonResponse;
|
||||
|
||||
} else if (operation === 'getAll') {
|
||||
|
||||
// ----------------------------------------
|
||||
// person: getAll
|
||||
// ----------------------------------------
|
||||
|
||||
response = await handleListing.call(this, 'GET', '/people') as PersonResponse[];
|
||||
|
||||
} else if (operation === 'update') {
|
||||
|
||||
// ----------------------------------------
|
||||
// person: update
|
||||
// ----------------------------------------
|
||||
|
||||
const personId = this.getNodeParameter('personId', i);
|
||||
const body = {} as IDataObject;
|
||||
const updateFields = this.getNodeParameter('updateFields', i) as IDataObject;
|
||||
|
||||
if (Object.keys(updateFields).length) {
|
||||
Object.assign(body, adjustPersonPayload(updateFields));
|
||||
} else {
|
||||
throw new NodeOperationError(
|
||||
this.getNode(),
|
||||
`Please enter at least one field to update for the ${resource}.`,
|
||||
);
|
||||
}
|
||||
|
||||
response = await actionNetworkApiRequest.call(this, 'PUT', `/people/${personId}`, body);
|
||||
|
||||
}
|
||||
|
||||
} else if (resource === 'petition') {
|
||||
|
||||
// **********************************************************************
|
||||
// petition
|
||||
// **********************************************************************
|
||||
|
||||
// https://actionnetwork.org/docs/v2/petitions
|
||||
|
||||
if (operation === 'create') {
|
||||
|
||||
// ----------------------------------------
|
||||
// petition: create
|
||||
// ----------------------------------------
|
||||
|
||||
const body = {
|
||||
origin_system: this.getNodeParameter('originSystem', i),
|
||||
title: this.getNodeParameter('title', i),
|
||||
} as IDataObject;
|
||||
|
||||
const additionalFields = this.getNodeParameter('additionalFields', i) as AllFieldsUi;
|
||||
|
||||
if (Object.keys(additionalFields).length) {
|
||||
Object.assign(body, adjustPetitionPayload(additionalFields));
|
||||
}
|
||||
|
||||
response = await actionNetworkApiRequest.call(this, 'POST', '/petitions', body);
|
||||
|
||||
} else if (operation === 'get') {
|
||||
|
||||
// ----------------------------------------
|
||||
// petition: get
|
||||
// ----------------------------------------
|
||||
|
||||
const petitionId = this.getNodeParameter('petitionId', i);
|
||||
|
||||
const endpoint = `/petitions/${petitionId}`;
|
||||
response = await actionNetworkApiRequest.call(this, 'GET', endpoint);
|
||||
|
||||
} else if (operation === 'getAll') {
|
||||
|
||||
// ----------------------------------------
|
||||
// petition: getAll
|
||||
// ----------------------------------------
|
||||
|
||||
response = await handleListing.call(this, 'GET', '/petitions');
|
||||
|
||||
} else if (operation === 'update') {
|
||||
|
||||
// ----------------------------------------
|
||||
// petition: update
|
||||
// ----------------------------------------
|
||||
|
||||
const petitionId = this.getNodeParameter('petitionId', i);
|
||||
const body = {} as IDataObject;
|
||||
const updateFields = this.getNodeParameter('updateFields', i) as AllFieldsUi;
|
||||
|
||||
if (Object.keys(updateFields).length) {
|
||||
Object.assign(body, adjustPetitionPayload(updateFields));
|
||||
} else {
|
||||
throw new NodeOperationError(
|
||||
this.getNode(),
|
||||
`Please enter at least one field to update for the ${resource}.`,
|
||||
);
|
||||
}
|
||||
|
||||
response = await actionNetworkApiRequest.call(this, 'PUT', `/petitions/${petitionId}`, body);
|
||||
|
||||
}
|
||||
|
||||
} else if (resource === 'signature') {
|
||||
|
||||
// **********************************************************************
|
||||
// signature
|
||||
// **********************************************************************
|
||||
|
||||
// https://actionnetwork.org/docs/v2/signatures
|
||||
|
||||
if (operation === 'create') {
|
||||
|
||||
// ----------------------------------------
|
||||
// signature: create
|
||||
// ----------------------------------------
|
||||
|
||||
const personId = this.getNodeParameter('personId', i) as string;
|
||||
const petitionId = this.getNodeParameter('petitionId', i);
|
||||
|
||||
const body = makeOsdiLink(personId) as IDataObject;
|
||||
|
||||
const additionalFields = this.getNodeParameter('additionalFields', i) as IDataObject;
|
||||
|
||||
if (Object.keys(additionalFields).length) {
|
||||
Object.assign(body, additionalFields);
|
||||
}
|
||||
|
||||
const endpoint = `/petitions/${petitionId}/signatures`;
|
||||
response = await actionNetworkApiRequest.call(this, 'POST', endpoint, body);
|
||||
|
||||
} else if (operation === 'get') {
|
||||
|
||||
// ----------------------------------------
|
||||
// signature: get
|
||||
// ----------------------------------------
|
||||
|
||||
const petitionId = this.getNodeParameter('petitionId', i);
|
||||
const signatureId = this.getNodeParameter('signatureId', i);
|
||||
|
||||
const endpoint = `/petitions/${petitionId}/signatures/${signatureId}`;
|
||||
response = await actionNetworkApiRequest.call(this, 'GET', endpoint);
|
||||
|
||||
} else if (operation === 'getAll') {
|
||||
|
||||
// ----------------------------------------
|
||||
// signature: getAll
|
||||
// ----------------------------------------
|
||||
|
||||
const petitionId = this.getNodeParameter('petitionId', i);
|
||||
|
||||
const endpoint = `/petitions/${petitionId}/signatures`;
|
||||
response = await handleListing.call(this, 'GET', endpoint);
|
||||
|
||||
} else if (operation === 'update') {
|
||||
|
||||
// ----------------------------------------
|
||||
// signature: update
|
||||
// ----------------------------------------
|
||||
|
||||
const petitionId = this.getNodeParameter('petitionId', i);
|
||||
const signatureId = this.getNodeParameter('signatureId', i);
|
||||
const body = {};
|
||||
const updateFields = this.getNodeParameter('updateFields', i) as AllFieldsUi;
|
||||
|
||||
if (Object.keys(updateFields).length) {
|
||||
Object.assign(body, updateFields);
|
||||
} else {
|
||||
throw new NodeOperationError(
|
||||
this.getNode(),
|
||||
`Please enter at least one field to update for the ${resource}.`,
|
||||
);
|
||||
}
|
||||
|
||||
const endpoint = `/petitions/${petitionId}/signatures/${signatureId}`;
|
||||
response = await actionNetworkApiRequest.call(this, 'PUT', endpoint, body);
|
||||
|
||||
}
|
||||
|
||||
} else if (resource === 'tag') {
|
||||
|
||||
// **********************************************************************
|
||||
// tag
|
||||
// **********************************************************************
|
||||
|
||||
// https://actionnetwork.org/docs/v2/tags
|
||||
|
||||
if (operation === 'create') {
|
||||
|
||||
// ----------------------------------------
|
||||
// tag: create
|
||||
// ----------------------------------------
|
||||
|
||||
const body = {
|
||||
name: this.getNodeParameter('name', i),
|
||||
} as IDataObject;
|
||||
|
||||
response = await actionNetworkApiRequest.call(this, 'POST', '/tags', body);
|
||||
|
||||
} else if (operation === 'get') {
|
||||
|
||||
// ----------------------------------------
|
||||
// tag: get
|
||||
// ----------------------------------------
|
||||
|
||||
const tagId = this.getNodeParameter('tagId', i);
|
||||
|
||||
response = await actionNetworkApiRequest.call(this, 'GET', `/tags/${tagId}`);
|
||||
|
||||
} else if (operation === 'getAll') {
|
||||
|
||||
// ----------------------------------------
|
||||
// tag: getAll
|
||||
// ----------------------------------------
|
||||
|
||||
response = await handleListing.call(this, 'GET', '/tags');
|
||||
|
||||
}
|
||||
|
||||
} else if (resource === 'personTag') {
|
||||
|
||||
// **********************************************************************
|
||||
// personTag
|
||||
// **********************************************************************
|
||||
|
||||
// https://actionnetwork.org/docs/v2/taggings
|
||||
|
||||
if (operation === 'add') {
|
||||
|
||||
// ----------------------------------------
|
||||
// personTag: add
|
||||
// ----------------------------------------
|
||||
|
||||
const personId = this.getNodeParameter('personId', i) as string;
|
||||
const tagId = this.getNodeParameter('tagId', i);
|
||||
|
||||
const body = makeOsdiLink(personId) as IDataObject;
|
||||
|
||||
const endpoint = `/tags/${tagId}/taggings`;
|
||||
response = await actionNetworkApiRequest.call(this, 'POST', endpoint, body);
|
||||
|
||||
} else if (operation === 'remove') {
|
||||
|
||||
// ----------------------------------------
|
||||
// personTag: remove
|
||||
// ----------------------------------------
|
||||
|
||||
const tagId = this.getNodeParameter('tagId', i);
|
||||
const taggingId = this.getNodeParameter('taggingId', i);
|
||||
|
||||
const endpoint = `/tags/${tagId}/taggings/${taggingId}`;
|
||||
response = await actionNetworkApiRequest.call(this, 'DELETE', endpoint);
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
const simplify = this.getNodeParameter('simple', i, false) as boolean;
|
||||
|
||||
if (simplify) {
|
||||
response = operation === 'getAll'
|
||||
? response.map((i: Response) => simplifyResponse(i, resource))
|
||||
: simplifyResponse(response, resource);
|
||||
}
|
||||
|
||||
Array.isArray(response)
|
||||
? returnData.push(...response)
|
||||
: returnData.push(response);
|
||||
|
||||
} catch (error) {
|
||||
if (this.continueOnFail()) {
|
||||
returnData.push({ error: error.message });
|
||||
continue;
|
||||
}
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
return [this.helpers.returnJsonArray(returnData)];
|
||||
}
|
||||
}
|
||||
348
packages/nodes-base/nodes/ActionNetwork/GenericFunctions.ts
Normal file
@@ -0,0 +1,348 @@
|
||||
import {
|
||||
IExecuteFunctions,
|
||||
} from 'n8n-core';
|
||||
|
||||
import {
|
||||
IDataObject,
|
||||
ILoadOptionsFunctions,
|
||||
NodeApiError,
|
||||
NodeOperationError,
|
||||
} from 'n8n-workflow';
|
||||
|
||||
import {
|
||||
OptionsWithUri,
|
||||
} from 'request';
|
||||
|
||||
import {
|
||||
flow,
|
||||
omit,
|
||||
} from 'lodash';
|
||||
|
||||
import {
|
||||
AllFieldsUi,
|
||||
FieldWithPrimaryField,
|
||||
LinksFieldContainer,
|
||||
PersonResponse,
|
||||
PetitionResponse,
|
||||
Resource,
|
||||
Response,
|
||||
} from './types';
|
||||
|
||||
export async function actionNetworkApiRequest(
|
||||
this: IExecuteFunctions | ILoadOptionsFunctions,
|
||||
method: string,
|
||||
endpoint: string,
|
||||
body: IDataObject = {},
|
||||
qs: IDataObject = {},
|
||||
) {
|
||||
const credentials = this.getCredentials('actionNetworkApi') as { apiKey: string } | undefined;
|
||||
|
||||
if (credentials === undefined) {
|
||||
throw new NodeOperationError(this.getNode(), 'No credentials got returned!');
|
||||
}
|
||||
|
||||
const options: OptionsWithUri = {
|
||||
headers: {
|
||||
'OSDI-API-Token': credentials.apiKey,
|
||||
},
|
||||
method,
|
||||
body,
|
||||
qs,
|
||||
uri: `https://actionnetwork.org/api/v2${endpoint}`,
|
||||
json: true,
|
||||
};
|
||||
|
||||
if (!Object.keys(body).length) {
|
||||
delete options.body;
|
||||
}
|
||||
|
||||
if (!Object.keys(qs).length) {
|
||||
delete options.qs;
|
||||
}
|
||||
|
||||
try {
|
||||
return await this.helpers.request!(options);
|
||||
} catch (error) {
|
||||
throw new NodeApiError(this.getNode(), error);
|
||||
}
|
||||
}
|
||||
|
||||
export async function handleListing(
|
||||
this: IExecuteFunctions | ILoadOptionsFunctions,
|
||||
method: string,
|
||||
endpoint: string,
|
||||
body: IDataObject = {},
|
||||
qs: IDataObject = {},
|
||||
options?: { returnAll: true },
|
||||
) {
|
||||
const returnData: IDataObject[] = [];
|
||||
let responseData;
|
||||
|
||||
qs.perPage = 25; // max
|
||||
qs.page = 1;
|
||||
|
||||
const returnAll = options?.returnAll ?? this.getNodeParameter('returnAll', 0, false) as boolean;
|
||||
const limit = this.getNodeParameter('limit', 0, 0) as number;
|
||||
|
||||
const itemsKey = toItemsKey(endpoint);
|
||||
|
||||
do {
|
||||
responseData = await actionNetworkApiRequest.call(this, method, endpoint, body, qs);
|
||||
const items = responseData._embedded[itemsKey];
|
||||
returnData.push(...items);
|
||||
|
||||
if (!returnAll && returnData.length >= limit) {
|
||||
return returnData.slice(0, limit);
|
||||
}
|
||||
|
||||
qs.page = responseData.page as number;
|
||||
} while (responseData.total_pages && qs.page < responseData.total_pages);
|
||||
|
||||
return returnData;
|
||||
}
|
||||
|
||||
|
||||
// ----------------------------------------
|
||||
// helpers
|
||||
// ----------------------------------------
|
||||
|
||||
/**
|
||||
* Convert an endpoint to the key needed to access data in the response.
|
||||
*/
|
||||
const toItemsKey = (endpoint: string) => {
|
||||
|
||||
// handle two-resource endpoint
|
||||
if (
|
||||
endpoint.includes('/signatures') ||
|
||||
endpoint.includes('/attendances') ||
|
||||
endpoint.includes('/taggings')
|
||||
) {
|
||||
endpoint = endpoint.split('/').pop()!;
|
||||
}
|
||||
|
||||
return `osdi:${endpoint.replace(/\//g, '')}`;
|
||||
};
|
||||
|
||||
export const extractId = (response: LinksFieldContainer) => {
|
||||
return response._links.self.href.split('/').pop() ?? 'No ID';
|
||||
};
|
||||
|
||||
export const makeOsdiLink = (personId: string) => {
|
||||
return {
|
||||
_links: {
|
||||
'osdi:person': {
|
||||
href: `https://actionnetwork.org/api/v2/people/${personId}`,
|
||||
},
|
||||
},
|
||||
};
|
||||
};
|
||||
|
||||
export const isPrimary = (field: FieldWithPrimaryField) => field.primary;
|
||||
|
||||
|
||||
// ----------------------------------------
|
||||
// field adjusters
|
||||
// ----------------------------------------
|
||||
|
||||
function adjustLanguagesSpoken(allFields: AllFieldsUi) {
|
||||
if (!allFields.languages_spoken) return allFields;
|
||||
|
||||
return {
|
||||
...omit(allFields, ['languages_spoken']),
|
||||
languages_spoken: [allFields.languages_spoken],
|
||||
};
|
||||
}
|
||||
|
||||
function adjustPhoneNumbers(allFields: AllFieldsUi) {
|
||||
if (!allFields.phone_numbers) return allFields;
|
||||
|
||||
return {
|
||||
...omit(allFields, ['phone_numbers']),
|
||||
phone_numbers: [
|
||||
allFields.phone_numbers.phone_numbers_fields,
|
||||
],
|
||||
};
|
||||
}
|
||||
|
||||
function adjustPostalAddresses(allFields: AllFieldsUi) {
|
||||
if (!allFields.postal_addresses) return allFields;
|
||||
|
||||
if (allFields.postal_addresses.postal_addresses_fields.length) {
|
||||
const adjusted = allFields.postal_addresses.postal_addresses_fields.map((field) => {
|
||||
const copy: IDataObject = {
|
||||
...omit(field, ['address_lines', 'location']),
|
||||
};
|
||||
|
||||
if (field.address_lines) {
|
||||
copy.address_lines = [field.address_lines];
|
||||
}
|
||||
|
||||
if (field.location) {
|
||||
copy.location = field.location.location_fields;
|
||||
}
|
||||
|
||||
return copy;
|
||||
});
|
||||
|
||||
return {
|
||||
...omit(allFields, ['postal_addresses']),
|
||||
postal_addresses: adjusted,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
function adjustLocation(allFields: AllFieldsUi) {
|
||||
if (!allFields.location) return allFields;
|
||||
|
||||
const locationFields = allFields.location.postal_addresses_fields;
|
||||
|
||||
const adjusted: IDataObject = {
|
||||
...omit(locationFields, ['address_lines', 'location']),
|
||||
};
|
||||
|
||||
if (locationFields.address_lines) {
|
||||
adjusted.address_lines = [locationFields.address_lines];
|
||||
}
|
||||
|
||||
if (locationFields.location) {
|
||||
adjusted.location = locationFields.location.location_fields;
|
||||
}
|
||||
|
||||
return {
|
||||
...omit(allFields, ['location']),
|
||||
location: adjusted,
|
||||
};
|
||||
}
|
||||
|
||||
function adjustTargets(allFields: AllFieldsUi) {
|
||||
if (!allFields.target) return allFields;
|
||||
|
||||
const adjusted = allFields.target.split(',').map(value => ({ name: value }));
|
||||
|
||||
return {
|
||||
...omit(allFields, ['target']),
|
||||
target: adjusted,
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
// ----------------------------------------
|
||||
// payload adjusters
|
||||
// ----------------------------------------
|
||||
|
||||
export const adjustPersonPayload = flow(
|
||||
adjustLanguagesSpoken,
|
||||
adjustPhoneNumbers,
|
||||
adjustPostalAddresses,
|
||||
);
|
||||
|
||||
export const adjustPetitionPayload = adjustTargets;
|
||||
|
||||
export const adjustEventPayload = adjustLocation;
|
||||
|
||||
|
||||
// ----------------------------------------
|
||||
// resource loaders
|
||||
// ----------------------------------------
|
||||
|
||||
async function loadResource(this: ILoadOptionsFunctions, resource: string) {
|
||||
return await handleListing.call(this, 'GET', `/${resource}`, {}, {}, { returnAll: true });
|
||||
}
|
||||
|
||||
export const resourceLoaders = {
|
||||
|
||||
async getTags(this: ILoadOptionsFunctions) {
|
||||
const tags = await loadResource.call(this, 'tags') as Array<{ name: string } & LinksFieldContainer>;
|
||||
|
||||
return tags.map((tag) => ({ name: tag.name, value: extractId(tag) }));
|
||||
},
|
||||
|
||||
async getTaggings(this: ILoadOptionsFunctions) {
|
||||
const tagId = this.getNodeParameter('tagId', 0);
|
||||
const endpoint = `/tags/${tagId}/taggings`;
|
||||
|
||||
// two-resource endpoint, so direct call
|
||||
const taggings = await handleListing.call(
|
||||
this, 'GET', endpoint, {}, {}, { returnAll: true },
|
||||
) as LinksFieldContainer[];
|
||||
|
||||
return taggings.map((tagging) => {
|
||||
const taggingId = extractId(tagging);
|
||||
|
||||
return {
|
||||
name: taggingId,
|
||||
value: taggingId,
|
||||
};
|
||||
});
|
||||
},
|
||||
};
|
||||
|
||||
|
||||
// ----------------------------------------
|
||||
// response simplifiers
|
||||
// ----------------------------------------
|
||||
|
||||
export const simplifyResponse = (response: Response, resource: Resource) => {
|
||||
if (resource === 'person') {
|
||||
return simplifyPersonResponse(response as PersonResponse);
|
||||
} else if (resource === 'petition') {
|
||||
return simplifyPetitionResponse(response as PetitionResponse);
|
||||
}
|
||||
|
||||
const fieldsToSimplify = [
|
||||
'identifiers',
|
||||
'_links',
|
||||
'action_network:sponsor',
|
||||
'reminders',
|
||||
];
|
||||
|
||||
return {
|
||||
id: extractId(response),
|
||||
...omit(response, fieldsToSimplify),
|
||||
};
|
||||
};
|
||||
|
||||
|
||||
const simplifyPetitionResponse = (response: PetitionResponse) => {
|
||||
const fieldsToSimplify = [
|
||||
'identifiers',
|
||||
'_links',
|
||||
'action_network:hidden',
|
||||
'_embedded',
|
||||
];
|
||||
|
||||
return {
|
||||
id: extractId(response),
|
||||
...omit(response, fieldsToSimplify),
|
||||
creator: simplifyPersonResponse(response._embedded['osdi:creator']),
|
||||
};
|
||||
};
|
||||
|
||||
const simplifyPersonResponse = (response: PersonResponse) => {
|
||||
const emailAddress = response.email_addresses.filter(isPrimary);
|
||||
const phoneNumber = response.phone_numbers.filter(isPrimary);
|
||||
const postalAddress = response.postal_addresses.filter(isPrimary);
|
||||
|
||||
const fieldsToSimplify = [
|
||||
'identifiers',
|
||||
'email_addresses',
|
||||
'phone_numbers',
|
||||
'postal_addresses',
|
||||
'languages_spoken',
|
||||
'_links',
|
||||
];
|
||||
|
||||
return {
|
||||
id: extractId(response),
|
||||
...omit(response, fieldsToSimplify),
|
||||
...{ email_address: emailAddress[0].address || '' },
|
||||
...{ phone_number: phoneNumber[0].number || '' },
|
||||
...{
|
||||
postal_address: {
|
||||
...postalAddress && omit(postalAddress[0], 'address_lines'),
|
||||
address_lines: postalAddress[0].address_lines ?? '',
|
||||
},
|
||||
},
|
||||
language_spoken: response.languages_spoken[0],
|
||||
};
|
||||
};
|
||||
@@ -0,0 +1 @@
|
||||
<svg id="Layer_1" data-name="Layer 1" xmlns="http://www.w3.org/2000/svg" viewBox="0 1 79.89 75.96"><defs><style>.cls-1{fill:#9dd3ed;}</style></defs><title>actionnetwork</title><circle class="cls-1" cx="40.42" cy="5.43" r="5.43"/><path class="cls-1" d="M26.32,15.92c9.94-2.11,15.06,5.25,16.52,11.47.92,3.93,4.28,3.87,6.58,2.73S56,28.48,53,21.23c-1.82-4.41-4.14-8.44-8.26-11.08A6.42,6.42,0,0,1,34.66,8.3a6.88,6.88,0,0,1-.44-1.14c-8.85.1-14.7,8-16.45,13.26a18.85,18.85,0,0,1,8.55-4.5"/><circle class="cls-1" cx="5.43" cy="29.87" r="5.43"/><path class="cls-1" d="M11.23,46.45c1-10.11,9.51-12.8,15.88-12.33,4,.29,5-2.93,4.57-5.47s.4-6.78-7.41-6.1C19.52,23,15,24,11.23,27.12a6.37,6.37,0,0,1,.34,4.67,6.62,6.62,0,0,1-.93,1.86,6.39,6.39,0,0,1-4.19,2.57,6.31,6.31,0,0,1-1.22.08C2.68,44.78,8.52,52.73,13,56a18.92,18.92,0,0,1-1.75-9.51"/><circle class="cls-1" cx="18.08" cy="70.13" r="5.43"/><path class="cls-1" d="M35.65,69.73c-9.33-4-9.25-13-6.83-18.91,1.52-3.74-1.25-5.64-3.79-6S18.7,42.31,16.94,50c-1.07,4.64-1.51,9.27.32,13.81a6.44,6.44,0,0,1,4.55,1.12,6.47,6.47,0,0,1,2.63,6.24,7.55,7.55,0,0,1-.3,1.18c7.27,5,16.64,2,21.09-1.3a18.8,18.8,0,0,1-9.58-1.27"/><circle class="cls-1" cx="60.58" cy="70.53" r="5.43"/><path class="cls-1" d="M65.83,53.76c-6.81,7.55-15.28,4.6-20.11.42-3-2.64-5.74-.62-6.93,1.66S34.42,61,41.1,65.16c4.06,2.5,8.3,4.4,13.19,4.11a6.43,6.43,0,0,1,2.51-4,6.76,6.76,0,0,1,1.86-.93,6.38,6.38,0,0,1,4.9.44,5.94,5.94,0,0,1,1,.66c7.1-5.28,7.17-15.14,5.52-20.4a18.91,18.91,0,0,1-4.27,8.67"/><circle class="cls-1" cx="74.46" cy="30.68" r="5.43"/><path class="cls-1" d="M60.13,20.51c5.08,8.81-.34,16-5.8,19.25C50.87,41.85,52,45,53.76,46.87s3.6,5.76,9.57.68c3.64-3.08,6.75-6.54,8-11.27A6.42,6.42,0,0,1,70,26.09a6.21,6.21,0,0,1,.94-.77c-2.83-8.39-12.19-11.49-17.69-11.55a18.94,18.94,0,0,1,6.92,6.74"/></svg>
|
||||
|
After Width: | Height: | Size: 1.8 KiB |
@@ -0,0 +1,185 @@
|
||||
import {
|
||||
INodeProperties,
|
||||
} from 'n8n-workflow';
|
||||
|
||||
import {
|
||||
makeSimpleField,
|
||||
} from './SharedFields';
|
||||
|
||||
export const attendanceOperations = [
|
||||
{
|
||||
displayName: 'Operation',
|
||||
name: 'operation',
|
||||
type: 'options',
|
||||
displayOptions: {
|
||||
show: {
|
||||
resource: [
|
||||
'attendance',
|
||||
],
|
||||
},
|
||||
},
|
||||
options: [
|
||||
{
|
||||
name: 'Create',
|
||||
value: 'create',
|
||||
},
|
||||
{
|
||||
name: 'Get',
|
||||
value: 'get',
|
||||
},
|
||||
{
|
||||
name: 'Get All',
|
||||
value: 'getAll',
|
||||
},
|
||||
],
|
||||
default: 'create',
|
||||
description: 'Operation to perform',
|
||||
},
|
||||
] as INodeProperties[];
|
||||
|
||||
export const attendanceFields = [
|
||||
// ----------------------------------------
|
||||
// attendance: create
|
||||
// ----------------------------------------
|
||||
{
|
||||
displayName: 'Person ID',
|
||||
name: 'personId',
|
||||
description: 'ID of the person to create an attendance for.',
|
||||
type: 'string',
|
||||
default: '',
|
||||
required: true,
|
||||
displayOptions: {
|
||||
show: {
|
||||
resource: [
|
||||
'attendance',
|
||||
],
|
||||
operation: [
|
||||
'create',
|
||||
],
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
displayName: 'Event ID',
|
||||
name: 'eventId',
|
||||
description: 'ID of the event to create an attendance for.',
|
||||
type: 'string',
|
||||
default: '',
|
||||
required: true,
|
||||
displayOptions: {
|
||||
show: {
|
||||
resource: [
|
||||
'attendance',
|
||||
],
|
||||
operation: [
|
||||
'create',
|
||||
],
|
||||
},
|
||||
},
|
||||
},
|
||||
makeSimpleField('attendance', 'create'),
|
||||
|
||||
// ----------------------------------------
|
||||
// attendance: get
|
||||
// ----------------------------------------
|
||||
{
|
||||
displayName: 'Event ID',
|
||||
name: 'eventId',
|
||||
description: 'ID of the event whose attendance to retrieve.',
|
||||
type: 'string',
|
||||
default: '',
|
||||
required: true,
|
||||
displayOptions: {
|
||||
show: {
|
||||
resource: [
|
||||
'attendance',
|
||||
],
|
||||
operation: [
|
||||
'get',
|
||||
],
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
displayName: 'Attendance ID',
|
||||
name: 'attendanceId',
|
||||
description: 'ID of the attendance to retrieve.',
|
||||
type: 'string',
|
||||
default: '',
|
||||
required: true,
|
||||
displayOptions: {
|
||||
show: {
|
||||
resource: [
|
||||
'attendance',
|
||||
],
|
||||
operation: [
|
||||
'get',
|
||||
],
|
||||
},
|
||||
},
|
||||
},
|
||||
makeSimpleField('attendance', 'get'),
|
||||
|
||||
// ----------------------------------------
|
||||
// attendance: getAll
|
||||
// ----------------------------------------
|
||||
{
|
||||
displayName: 'Event ID',
|
||||
name: 'eventId',
|
||||
description: 'ID of the event to create an attendance for.',
|
||||
type: 'string',
|
||||
default: '',
|
||||
required: true,
|
||||
displayOptions: {
|
||||
show: {
|
||||
resource: [
|
||||
'attendance',
|
||||
],
|
||||
operation: [
|
||||
'getAll',
|
||||
],
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
displayName: 'Return All',
|
||||
name: 'returnAll',
|
||||
type: 'boolean',
|
||||
default: false,
|
||||
description: 'Return all results.',
|
||||
displayOptions: {
|
||||
show: {
|
||||
resource: [
|
||||
'attendance',
|
||||
],
|
||||
operation: [
|
||||
'getAll',
|
||||
],
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
displayName: 'Limit',
|
||||
name: 'limit',
|
||||
type: 'number',
|
||||
default: 50,
|
||||
description: 'The number of results to return.',
|
||||
typeOptions: {
|
||||
minValue: 1,
|
||||
},
|
||||
displayOptions: {
|
||||
show: {
|
||||
resource: [
|
||||
'attendance',
|
||||
],
|
||||
operation: [
|
||||
'getAll',
|
||||
],
|
||||
returnAll: [
|
||||
false,
|
||||
],
|
||||
},
|
||||
},
|
||||
},
|
||||
makeSimpleField('attendance', 'getAll'),
|
||||
] as INodeProperties[];
|
||||
@@ -0,0 +1,168 @@
|
||||
import {
|
||||
INodeProperties,
|
||||
} from 'n8n-workflow';
|
||||
|
||||
import {
|
||||
eventAdditionalFieldsOptions,
|
||||
makeSimpleField,
|
||||
} from './SharedFields';
|
||||
|
||||
export const eventOperations = [
|
||||
{
|
||||
displayName: 'Operation',
|
||||
name: 'operation',
|
||||
type: 'options',
|
||||
displayOptions: {
|
||||
show: {
|
||||
resource: [
|
||||
'event',
|
||||
],
|
||||
},
|
||||
},
|
||||
options: [
|
||||
{
|
||||
name: 'Create',
|
||||
value: 'create',
|
||||
},
|
||||
{
|
||||
name: 'Get',
|
||||
value: 'get',
|
||||
},
|
||||
{
|
||||
name: 'Get All',
|
||||
value: 'getAll',
|
||||
},
|
||||
],
|
||||
default: 'create',
|
||||
description: 'Operation to perform',
|
||||
},
|
||||
] as INodeProperties[];
|
||||
|
||||
export const eventFields = [
|
||||
// ----------------------------------------
|
||||
// event: create
|
||||
// ----------------------------------------
|
||||
{
|
||||
displayName: 'Origin System',
|
||||
name: 'originSystem',
|
||||
description: 'Source where the event originated.',
|
||||
type: 'string',
|
||||
required: true,
|
||||
default: '',
|
||||
displayOptions: {
|
||||
show: {
|
||||
resource: [
|
||||
'event',
|
||||
],
|
||||
operation: [
|
||||
'create',
|
||||
],
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
displayName: 'Title',
|
||||
name: 'title',
|
||||
description: 'Title of the event to create.',
|
||||
type: 'string',
|
||||
required: true,
|
||||
default: '',
|
||||
displayOptions: {
|
||||
show: {
|
||||
resource: [
|
||||
'event',
|
||||
],
|
||||
operation: [
|
||||
'create',
|
||||
],
|
||||
},
|
||||
},
|
||||
},
|
||||
makeSimpleField('event', 'create'),
|
||||
{
|
||||
displayName: 'Additional Fields',
|
||||
name: 'additionalFields',
|
||||
type: 'collection',
|
||||
placeholder: 'Add Field',
|
||||
default: {},
|
||||
displayOptions: {
|
||||
show: {
|
||||
resource: [
|
||||
'event',
|
||||
],
|
||||
operation: [
|
||||
'create',
|
||||
],
|
||||
},
|
||||
},
|
||||
options: eventAdditionalFieldsOptions,
|
||||
},
|
||||
|
||||
// ----------------------------------------
|
||||
// event: get
|
||||
// ----------------------------------------
|
||||
{
|
||||
displayName: 'Event ID',
|
||||
name: 'eventId',
|
||||
description: 'ID of the event to retrieve.',
|
||||
type: 'string',
|
||||
default: '',
|
||||
required: true,
|
||||
displayOptions: {
|
||||
show: {
|
||||
resource: [
|
||||
'event',
|
||||
],
|
||||
operation: [
|
||||
'get',
|
||||
],
|
||||
},
|
||||
},
|
||||
},
|
||||
makeSimpleField('event', 'get'),
|
||||
|
||||
// ----------------------------------------
|
||||
// event: getAll
|
||||
// ----------------------------------------
|
||||
{
|
||||
displayName: 'Return All',
|
||||
name: 'returnAll',
|
||||
type: 'boolean',
|
||||
default: false,
|
||||
description: 'Return all results.',
|
||||
displayOptions: {
|
||||
show: {
|
||||
resource: [
|
||||
'event',
|
||||
],
|
||||
operation: [
|
||||
'getAll',
|
||||
],
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
displayName: 'Limit',
|
||||
name: 'limit',
|
||||
type: 'number',
|
||||
default: 50,
|
||||
description: 'The number of results to return.',
|
||||
typeOptions: {
|
||||
minValue: 1,
|
||||
},
|
||||
displayOptions: {
|
||||
show: {
|
||||
resource: [
|
||||
'event',
|
||||
],
|
||||
operation: [
|
||||
'getAll',
|
||||
],
|
||||
returnAll: [
|
||||
false,
|
||||
],
|
||||
},
|
||||
},
|
||||
},
|
||||
makeSimpleField('event', 'getAll'),
|
||||
] as INodeProperties[];
|
||||
@@ -0,0 +1,250 @@
|
||||
import {
|
||||
INodeProperties,
|
||||
} from 'n8n-workflow';
|
||||
|
||||
import {
|
||||
makeSimpleField,
|
||||
personAdditionalFieldsOptions,
|
||||
} from './SharedFields';
|
||||
|
||||
export const personOperations = [
|
||||
{
|
||||
displayName: 'Operation',
|
||||
name: 'operation',
|
||||
type: 'options',
|
||||
displayOptions: {
|
||||
show: {
|
||||
resource: [
|
||||
'person',
|
||||
],
|
||||
},
|
||||
},
|
||||
options: [
|
||||
{
|
||||
name: 'Create',
|
||||
value: 'create',
|
||||
},
|
||||
{
|
||||
name: 'Get',
|
||||
value: 'get',
|
||||
},
|
||||
{
|
||||
name: 'Get All',
|
||||
value: 'getAll',
|
||||
},
|
||||
{
|
||||
name: 'Update',
|
||||
value: 'update',
|
||||
},
|
||||
],
|
||||
default: 'create',
|
||||
description: 'Operation to perform',
|
||||
},
|
||||
] as INodeProperties[];
|
||||
|
||||
export const personFields = [
|
||||
// ----------------------------------------
|
||||
// person: create
|
||||
// ----------------------------------------
|
||||
makeSimpleField('person', 'create'),
|
||||
{
|
||||
displayName: 'Email Address', // on create, only _one_ must be passed in
|
||||
name: 'email_addresses',
|
||||
type: 'fixedCollection',
|
||||
default: {},
|
||||
placeholder: 'Add Email Address Field',
|
||||
description: 'Person’s email addresses.',
|
||||
displayOptions: {
|
||||
show: {
|
||||
resource: [
|
||||
'person',
|
||||
],
|
||||
operation: [
|
||||
'create',
|
||||
],
|
||||
},
|
||||
},
|
||||
options: [
|
||||
{
|
||||
displayName: 'Email Addresses Fields',
|
||||
name: 'email_addresses_fields',
|
||||
values: [
|
||||
{
|
||||
displayName: 'Address',
|
||||
name: 'address',
|
||||
type: 'string',
|
||||
default: '',
|
||||
description: 'Person\'s email address.',
|
||||
},
|
||||
{
|
||||
displayName: 'Primary',
|
||||
name: 'primary',
|
||||
type: 'hidden',
|
||||
default: true,
|
||||
description: 'Whether this is the person\'s primary email address.',
|
||||
},
|
||||
{
|
||||
displayName: 'Status',
|
||||
name: 'status',
|
||||
type: 'options',
|
||||
default: 'subscribed',
|
||||
description: 'Subscription status of this email address.',
|
||||
options: [
|
||||
{
|
||||
name: 'Bouncing',
|
||||
value: 'bouncing',
|
||||
},
|
||||
{
|
||||
name: 'Previous Bounce',
|
||||
value: 'previous bounce',
|
||||
},
|
||||
{
|
||||
name: 'Previous Spam Complaint',
|
||||
value: 'previous spam complaint',
|
||||
},
|
||||
{
|
||||
name: 'Spam Complaint',
|
||||
value: 'spam complaint',
|
||||
},
|
||||
{
|
||||
name: 'Subscribed',
|
||||
value: 'subscribed',
|
||||
},
|
||||
{
|
||||
name: 'Unsubscribed',
|
||||
value: 'unsubscribed',
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
displayName: 'Additional Fields',
|
||||
name: 'additionalFields',
|
||||
type: 'collection',
|
||||
placeholder: 'Add Field',
|
||||
default: {},
|
||||
displayOptions: {
|
||||
show: {
|
||||
resource: [
|
||||
'person',
|
||||
],
|
||||
operation: [
|
||||
'create',
|
||||
],
|
||||
},
|
||||
},
|
||||
options: personAdditionalFieldsOptions,
|
||||
},
|
||||
|
||||
// ----------------------------------------
|
||||
// person: get
|
||||
// ----------------------------------------
|
||||
{
|
||||
displayName: 'Person ID',
|
||||
name: 'personId',
|
||||
description: 'ID of the person to retrieve.',
|
||||
type: 'string',
|
||||
required: true,
|
||||
default: '',
|
||||
displayOptions: {
|
||||
show: {
|
||||
resource: [
|
||||
'person',
|
||||
],
|
||||
operation: [
|
||||
'get',
|
||||
],
|
||||
},
|
||||
},
|
||||
},
|
||||
makeSimpleField('person', 'get'),
|
||||
|
||||
// ----------------------------------------
|
||||
// person: getAll
|
||||
// ----------------------------------------
|
||||
{
|
||||
displayName: 'Return All',
|
||||
name: 'returnAll',
|
||||
type: 'boolean',
|
||||
default: false,
|
||||
description: 'Return all results.',
|
||||
displayOptions: {
|
||||
show: {
|
||||
resource: [
|
||||
'person',
|
||||
],
|
||||
operation: [
|
||||
'getAll',
|
||||
],
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
displayName: 'Limit',
|
||||
name: 'limit',
|
||||
type: 'number',
|
||||
default: 50,
|
||||
description: 'The number of results to return.',
|
||||
typeOptions: {
|
||||
minValue: 1,
|
||||
},
|
||||
displayOptions: {
|
||||
show: {
|
||||
resource: [
|
||||
'person',
|
||||
],
|
||||
operation: [
|
||||
'getAll',
|
||||
],
|
||||
returnAll: [
|
||||
false,
|
||||
],
|
||||
},
|
||||
},
|
||||
},
|
||||
makeSimpleField('person', 'getAll'),
|
||||
|
||||
// ----------------------------------------
|
||||
// person: update
|
||||
// ----------------------------------------
|
||||
{
|
||||
displayName: 'Person ID',
|
||||
name: 'personId',
|
||||
description: 'ID of the person to update.',
|
||||
type: 'string',
|
||||
required: true,
|
||||
default: '',
|
||||
displayOptions: {
|
||||
show: {
|
||||
resource: [
|
||||
'person',
|
||||
],
|
||||
operation: [
|
||||
'update',
|
||||
],
|
||||
},
|
||||
},
|
||||
},
|
||||
makeSimpleField('person', 'update'),
|
||||
{
|
||||
displayName: 'Update Fields',
|
||||
name: 'updateFields',
|
||||
type: 'collection',
|
||||
placeholder: 'Add Field',
|
||||
default: {},
|
||||
displayOptions: {
|
||||
show: {
|
||||
resource: [
|
||||
'person',
|
||||
],
|
||||
operation: [
|
||||
'update',
|
||||
],
|
||||
},
|
||||
},
|
||||
options: personAdditionalFieldsOptions,
|
||||
},
|
||||
] as INodeProperties[];
|
||||
@@ -0,0 +1,122 @@
|
||||
import {
|
||||
INodeProperties,
|
||||
} from 'n8n-workflow';
|
||||
|
||||
export const personTagOperations = [
|
||||
{
|
||||
displayName: 'Operation',
|
||||
name: 'operation',
|
||||
type: 'options',
|
||||
displayOptions: {
|
||||
show: {
|
||||
resource: [
|
||||
'personTag',
|
||||
],
|
||||
},
|
||||
},
|
||||
options: [
|
||||
{
|
||||
name: 'Add',
|
||||
value: 'add',
|
||||
},
|
||||
{
|
||||
name: 'Remove',
|
||||
value: 'remove',
|
||||
},
|
||||
],
|
||||
default: 'add',
|
||||
description: 'Operation to perform',
|
||||
},
|
||||
] as INodeProperties[];
|
||||
|
||||
export const personTagFields = [
|
||||
// ----------------------------------------
|
||||
// personTag: add
|
||||
// ----------------------------------------
|
||||
{
|
||||
displayName: 'Tag ID',
|
||||
name: 'tagId',
|
||||
description: 'ID of the tag to add.',
|
||||
type: 'options',
|
||||
typeOptions: {
|
||||
loadOptionsMethod: 'getTags',
|
||||
},
|
||||
required: true,
|
||||
default: [],
|
||||
displayOptions: {
|
||||
show: {
|
||||
resource: [
|
||||
'personTag',
|
||||
],
|
||||
operation: [
|
||||
'add',
|
||||
],
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
displayName: 'Person ID',
|
||||
name: 'personId',
|
||||
description: 'ID of the person to add the tag to.',
|
||||
type: 'string',
|
||||
default: '',
|
||||
required: true,
|
||||
displayOptions: {
|
||||
show: {
|
||||
resource: [
|
||||
'personTag',
|
||||
],
|
||||
operation: [
|
||||
'add',
|
||||
],
|
||||
},
|
||||
},
|
||||
},
|
||||
|
||||
// ----------------------------------------
|
||||
// personTag: remove
|
||||
// ----------------------------------------
|
||||
{
|
||||
displayName: 'Tag ID',
|
||||
name: 'tagId',
|
||||
description: 'ID of the tag whose tagging to delete.',
|
||||
type: 'options',
|
||||
typeOptions: {
|
||||
loadOptionsMethod: 'getTags',
|
||||
},
|
||||
default: [],
|
||||
required: true,
|
||||
displayOptions: {
|
||||
show: {
|
||||
resource: [
|
||||
'personTag',
|
||||
],
|
||||
operation: [
|
||||
'remove',
|
||||
],
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
displayName: 'Tagging ID',
|
||||
name: 'taggingId',
|
||||
description: 'ID of the tagging to remove.',
|
||||
type: 'options',
|
||||
typeOptions: {
|
||||
loadOptionsDependsOn: 'tagId',
|
||||
loadOptionsMethod: 'getTaggings',
|
||||
},
|
||||
required: true,
|
||||
default: [],
|
||||
displayOptions: {
|
||||
show: {
|
||||
resource: [
|
||||
'personTag',
|
||||
],
|
||||
operation: [
|
||||
'remove',
|
||||
],
|
||||
},
|
||||
},
|
||||
},
|
||||
] as INodeProperties[];
|
||||
@@ -0,0 +1,213 @@
|
||||
import {
|
||||
INodeProperties,
|
||||
} from 'n8n-workflow';
|
||||
|
||||
import {
|
||||
makeSimpleField,
|
||||
petitionAdditionalFieldsOptions,
|
||||
} from './SharedFields';
|
||||
|
||||
export const petitionOperations = [
|
||||
{
|
||||
displayName: 'Operation',
|
||||
name: 'operation',
|
||||
type: 'options',
|
||||
displayOptions: {
|
||||
show: {
|
||||
resource: [
|
||||
'petition',
|
||||
],
|
||||
},
|
||||
},
|
||||
options: [
|
||||
{
|
||||
name: 'Create',
|
||||
value: 'create',
|
||||
},
|
||||
{
|
||||
name: 'Get',
|
||||
value: 'get',
|
||||
},
|
||||
{
|
||||
name: 'Get All',
|
||||
value: 'getAll',
|
||||
},
|
||||
{
|
||||
name: 'Update',
|
||||
value: 'update',
|
||||
},
|
||||
],
|
||||
default: 'create',
|
||||
description: 'Operation to perform',
|
||||
},
|
||||
] as INodeProperties[];
|
||||
|
||||
export const petitionFields = [
|
||||
// ----------------------------------------
|
||||
// petition: create
|
||||
// ----------------------------------------
|
||||
{
|
||||
displayName: 'Origin System',
|
||||
name: 'originSystem',
|
||||
description: 'Source where the petition originated.',
|
||||
type: 'string',
|
||||
required: true,
|
||||
default: '',
|
||||
displayOptions: {
|
||||
show: {
|
||||
resource: [
|
||||
'petition',
|
||||
],
|
||||
operation: [
|
||||
'create',
|
||||
],
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
displayName: 'Title',
|
||||
name: 'title',
|
||||
description: 'Title of the petition to create.',
|
||||
type: 'string',
|
||||
required: true,
|
||||
default: '',
|
||||
displayOptions: {
|
||||
show: {
|
||||
resource: [
|
||||
'petition',
|
||||
],
|
||||
operation: [
|
||||
'create',
|
||||
],
|
||||
},
|
||||
},
|
||||
},
|
||||
makeSimpleField('petition', 'create'),
|
||||
{
|
||||
displayName: 'Additional Fields',
|
||||
name: 'additionalFields',
|
||||
type: 'collection',
|
||||
placeholder: 'Add Field',
|
||||
default: {},
|
||||
displayOptions: {
|
||||
show: {
|
||||
resource: [
|
||||
'petition',
|
||||
],
|
||||
operation: [
|
||||
'create',
|
||||
],
|
||||
},
|
||||
},
|
||||
options: petitionAdditionalFieldsOptions,
|
||||
},
|
||||
|
||||
// ----------------------------------------
|
||||
// petition: get
|
||||
// ----------------------------------------
|
||||
{
|
||||
displayName: 'Petition ID',
|
||||
name: 'petitionId',
|
||||
description: 'ID of the petition to retrieve.',
|
||||
type: 'string',
|
||||
default: '',
|
||||
required: true,
|
||||
displayOptions: {
|
||||
show: {
|
||||
resource: [
|
||||
'petition',
|
||||
],
|
||||
operation: [
|
||||
'get',
|
||||
],
|
||||
},
|
||||
},
|
||||
},
|
||||
makeSimpleField('petition', 'get'),
|
||||
|
||||
// ----------------------------------------
|
||||
// petition: getAll
|
||||
// ----------------------------------------
|
||||
{
|
||||
displayName: 'Return All',
|
||||
name: 'returnAll',
|
||||
type: 'boolean',
|
||||
default: false,
|
||||
description: 'Return all results.',
|
||||
displayOptions: {
|
||||
show: {
|
||||
resource: [
|
||||
'petition',
|
||||
],
|
||||
operation: [
|
||||
'getAll',
|
||||
],
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
displayName: 'Limit',
|
||||
name: 'limit',
|
||||
type: 'number',
|
||||
default: 50,
|
||||
description: 'The number of results to return.',
|
||||
typeOptions: {
|
||||
minValue: 1,
|
||||
},
|
||||
displayOptions: {
|
||||
show: {
|
||||
resource: [
|
||||
'petition',
|
||||
],
|
||||
operation: [
|
||||
'getAll',
|
||||
],
|
||||
returnAll: [
|
||||
false,
|
||||
],
|
||||
},
|
||||
},
|
||||
},
|
||||
makeSimpleField('petition', 'getAll'),
|
||||
|
||||
// ----------------------------------------
|
||||
// petition: update
|
||||
// ----------------------------------------
|
||||
{
|
||||
displayName: 'Petition ID',
|
||||
name: 'petitionId',
|
||||
description: 'ID of the petition to update.',
|
||||
type: 'string',
|
||||
default: '',
|
||||
required: true,
|
||||
displayOptions: {
|
||||
show: {
|
||||
resource: [
|
||||
'petition',
|
||||
],
|
||||
operation: [
|
||||
'update',
|
||||
],
|
||||
},
|
||||
},
|
||||
},
|
||||
makeSimpleField('petition', 'update'),
|
||||
{
|
||||
displayName: 'Update Fields',
|
||||
name: 'updateFields',
|
||||
type: 'collection',
|
||||
placeholder: 'Add Field',
|
||||
default: {},
|
||||
displayOptions: {
|
||||
show: {
|
||||
resource: [
|
||||
'petition',
|
||||
],
|
||||
operation: [
|
||||
'update',
|
||||
],
|
||||
},
|
||||
},
|
||||
options: petitionAdditionalFieldsOptions,
|
||||
},
|
||||
] as INodeProperties[];
|
||||
@@ -0,0 +1,376 @@
|
||||
import {
|
||||
Operation,
|
||||
Resource,
|
||||
} from '../types';
|
||||
|
||||
export const languageOptions = [
|
||||
{
|
||||
name: 'Danish',
|
||||
value: 'da',
|
||||
},
|
||||
{
|
||||
name: 'Dutch',
|
||||
value: 'nl',
|
||||
},
|
||||
{
|
||||
name: 'English',
|
||||
value: 'en',
|
||||
},
|
||||
{
|
||||
name: 'Finnish',
|
||||
value: 'fi',
|
||||
},
|
||||
{
|
||||
name: 'French',
|
||||
value: 'fr',
|
||||
},
|
||||
{
|
||||
name: 'German',
|
||||
value: 'de',
|
||||
},
|
||||
{
|
||||
name: 'Hungarian',
|
||||
value: 'hu',
|
||||
},
|
||||
{
|
||||
name: 'Indonesian',
|
||||
value: 'id',
|
||||
},
|
||||
{
|
||||
name: 'Japanese',
|
||||
value: 'ja',
|
||||
},
|
||||
{
|
||||
name: 'Portuguese - Portugal',
|
||||
value: 'pt',
|
||||
},
|
||||
{
|
||||
name: 'Portuguese - Brazil',
|
||||
value: 'br',
|
||||
},
|
||||
{
|
||||
name: 'Rumanian',
|
||||
value: 'ru',
|
||||
},
|
||||
{
|
||||
name: 'Spanish',
|
||||
value: 'es',
|
||||
},
|
||||
{
|
||||
name: 'Swedish',
|
||||
value: 'sv',
|
||||
},
|
||||
{
|
||||
name: 'Turkish',
|
||||
value: 'tr',
|
||||
},
|
||||
{
|
||||
name: 'Welsh',
|
||||
value: 'cy',
|
||||
},
|
||||
] as const;
|
||||
|
||||
const postalAddressesFields = [
|
||||
{
|
||||
displayName: 'Primary',
|
||||
name: 'primary',
|
||||
type: 'boolean',
|
||||
default: false,
|
||||
description: 'Whether this is the person\'s primary address.',
|
||||
},
|
||||
{
|
||||
displayName: 'Address Line',
|
||||
name: 'address_lines',
|
||||
type: 'string', // The Action Network API expects a string array but ignores any string beyond the first, so this input field is simplified to string.
|
||||
default: '',
|
||||
description: 'Line for a person\'s address.',
|
||||
},
|
||||
{
|
||||
displayName: 'Locality',
|
||||
name: 'locality',
|
||||
type: 'string',
|
||||
default: '',
|
||||
description: 'City or other local administrative area. If blank, this will be filled in based on Action Network\'s geocoding.',
|
||||
},
|
||||
{
|
||||
displayName: 'Region',
|
||||
name: 'region',
|
||||
type: 'string',
|
||||
default: '',
|
||||
description: 'State or subdivision code per ISO 3166-2.',
|
||||
},
|
||||
{
|
||||
displayName: 'Postal Code',
|
||||
name: 'postal_code',
|
||||
type: 'string',
|
||||
default: '',
|
||||
description: 'Region specific postal code, such as ZIP code.',
|
||||
},
|
||||
{
|
||||
displayName: 'Country',
|
||||
name: 'country',
|
||||
type: 'string',
|
||||
default: '',
|
||||
description: 'Country code according to ISO 3166-1 Alpha-2. Defaults to US.',
|
||||
},
|
||||
{
|
||||
displayName: 'Language',
|
||||
name: 'language',
|
||||
type: 'string',
|
||||
default: '',
|
||||
description: 'Language in which the address is recorded, per ISO 639.',
|
||||
},
|
||||
{
|
||||
displayName: 'Location',
|
||||
name: 'location',
|
||||
type: 'fixedCollection',
|
||||
default: '',
|
||||
options: [
|
||||
{
|
||||
displayName: 'Location Fields',
|
||||
name: 'location_fields',
|
||||
values: [
|
||||
{
|
||||
displayName: 'Latitude',
|
||||
name: 'latitude',
|
||||
type: 'string',
|
||||
default: '',
|
||||
description: 'Latitude of the location of the address.',
|
||||
},
|
||||
{
|
||||
displayName: 'Longitude',
|
||||
name: 'longitude',
|
||||
type: 'string',
|
||||
default: '',
|
||||
description: 'Longitude of the location of the address.',
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
},
|
||||
];
|
||||
|
||||
export const eventAdditionalFieldsOptions = [
|
||||
{
|
||||
displayName: 'Browser URL',
|
||||
name: 'browser_url',
|
||||
type: 'string',
|
||||
default: '',
|
||||
description: 'URL to this event’s page on the Action Network or a third party.',
|
||||
},
|
||||
{
|
||||
displayName: 'Description',
|
||||
name: 'description',
|
||||
type: 'string',
|
||||
default: '',
|
||||
description: 'Description of the event. HTML supported.',
|
||||
},
|
||||
{
|
||||
displayName: 'End Date',
|
||||
name: 'end_date',
|
||||
type: 'dateTime',
|
||||
default: '',
|
||||
description: 'End date and time of the event.',
|
||||
},
|
||||
{
|
||||
displayName: 'Featured Image URL',
|
||||
name: 'featured_image_url',
|
||||
type: 'string',
|
||||
default: '',
|
||||
description: 'URL to this event’s featured image on the Action Network.',
|
||||
},
|
||||
{
|
||||
displayName: 'Instructions',
|
||||
name: 'instructions',
|
||||
type: 'string',
|
||||
default: '',
|
||||
description: 'Event\'s instructions for activists, visible after they RSVP. HTML supported.',
|
||||
},
|
||||
{
|
||||
displayName: 'Location',
|
||||
name: 'location',
|
||||
type: 'fixedCollection',
|
||||
default: {},
|
||||
placeholder: 'Add Location Field',
|
||||
typeOptions: {
|
||||
multipleValues: false,
|
||||
},
|
||||
options: [
|
||||
// different name, identical structure
|
||||
{
|
||||
displayName: 'Postal Addresses Fields',
|
||||
name: 'postal_addresses_fields',
|
||||
placeholder: 'Add Postal Address Field',
|
||||
values: postalAddressesFields,
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
displayName: 'Name',
|
||||
name: 'name',
|
||||
type: 'string',
|
||||
default: '',
|
||||
description: 'Internal (not public) title of the event.',
|
||||
},
|
||||
{
|
||||
displayName: 'Start Date',
|
||||
name: 'start_date',
|
||||
type: 'dateTime',
|
||||
default: '',
|
||||
description: 'Start date and time of the event.',
|
||||
},
|
||||
];
|
||||
|
||||
export const personAdditionalFieldsOptions = [
|
||||
{
|
||||
displayName: 'Family Name',
|
||||
name: 'family_name',
|
||||
type: 'string',
|
||||
default: '',
|
||||
description: 'Person’s last name.',
|
||||
},
|
||||
{
|
||||
displayName: 'Given Name',
|
||||
name: 'given_name',
|
||||
type: 'string',
|
||||
default: '',
|
||||
description: 'Person’s first name.',
|
||||
},
|
||||
{
|
||||
displayName: 'Language Spoken',
|
||||
name: 'languages_spoken',
|
||||
type: 'options', // Action Network accepts a `string[]` of language codes, but supports only one language per person - sending an array of 2+ languages will result in the first valid language being set as the preferred language for the person. Therefore, the user may select only one option in the n8n UI.
|
||||
default: [],
|
||||
description: 'Language spoken by the person',
|
||||
options: languageOptions,
|
||||
},
|
||||
{
|
||||
displayName: 'Phone Number', // on create, only _one_ must be passed in
|
||||
name: 'phone_numbers',
|
||||
type: 'fixedCollection',
|
||||
default: {},
|
||||
placeholder: 'Add Phone Numbers Field',
|
||||
options: [
|
||||
{
|
||||
displayName: 'Phone Numbers Fields',
|
||||
name: 'phone_numbers_fields',
|
||||
placeholder: 'Add Phone Number Field',
|
||||
values: [
|
||||
{
|
||||
displayName: 'Number',
|
||||
name: 'number',
|
||||
type: 'string',
|
||||
default: '',
|
||||
description: 'Person\'s mobile number, in international format without the plus sign.',
|
||||
},
|
||||
{
|
||||
displayName: 'Primary',
|
||||
name: 'primary',
|
||||
type: 'hidden',
|
||||
default: true,
|
||||
description: 'Whether this is the person\'s primary phone number.',
|
||||
},
|
||||
{
|
||||
displayName: 'Status',
|
||||
name: 'status',
|
||||
type: 'options',
|
||||
default: 'subscribed',
|
||||
description: 'Subscription status of this number.',
|
||||
options: [
|
||||
{
|
||||
name: 'Bouncing',
|
||||
value: 'bouncing',
|
||||
},
|
||||
{
|
||||
name: 'Previous Bounce',
|
||||
value: 'previous bounce',
|
||||
},
|
||||
{
|
||||
name: 'Subscribed',
|
||||
value: 'subscribed',
|
||||
},
|
||||
{
|
||||
name: 'Unsubscribed',
|
||||
value: 'unsubscribed',
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
displayName: 'Postal Addresses',
|
||||
name: 'postal_addresses',
|
||||
type: 'fixedCollection',
|
||||
default: {},
|
||||
placeholder: 'Add Postal Addresses Field',
|
||||
typeOptions: {
|
||||
multipleValues: true,
|
||||
},
|
||||
options: [
|
||||
{
|
||||
displayName: 'Postal Addresses Fields',
|
||||
name: 'postal_addresses_fields',
|
||||
placeholder: 'Add Postal Address Field',
|
||||
values: postalAddressesFields,
|
||||
},
|
||||
],
|
||||
},
|
||||
];
|
||||
|
||||
export const petitionAdditionalFieldsOptions = [
|
||||
{
|
||||
displayName: 'Browser URL',
|
||||
name: 'browser_url',
|
||||
type: 'string',
|
||||
default: '',
|
||||
description: 'URL to this petition’s page on the Action Network or a third party.',
|
||||
},
|
||||
{
|
||||
displayName: 'Featured Image URL',
|
||||
name: 'featured_image_url',
|
||||
type: 'string',
|
||||
default: '',
|
||||
description: 'URL to this action’s featured image on the Action Network.',
|
||||
},
|
||||
{
|
||||
displayName: 'Name',
|
||||
name: 'name',
|
||||
type: 'string',
|
||||
default: '',
|
||||
description: 'Internal (not public) title of the petition.',
|
||||
},
|
||||
{
|
||||
displayName: 'Petition Text',
|
||||
name: 'petition_text',
|
||||
type: 'string',
|
||||
default: '',
|
||||
description: 'Text of the letter to the petition’s target.',
|
||||
},
|
||||
{
|
||||
displayName: 'Targets',
|
||||
name: 'target',
|
||||
type: 'string',
|
||||
default: '',
|
||||
description: 'Comma-separated names of targets for this petition.',
|
||||
},
|
||||
];
|
||||
|
||||
export const makeSimpleField = (resource: Resource, operation: Operation) => ({
|
||||
displayName: 'Simple',
|
||||
name: 'simple',
|
||||
type: 'boolean',
|
||||
displayOptions: {
|
||||
show: {
|
||||
resource: [
|
||||
resource,
|
||||
],
|
||||
operation: [
|
||||
operation,
|
||||
],
|
||||
},
|
||||
},
|
||||
default: true,
|
||||
description: 'Return a simplified version of the response instead of the raw data.',
|
||||
});
|
||||
@@ -0,0 +1,282 @@
|
||||
import {
|
||||
INodeProperties,
|
||||
} from 'n8n-workflow';
|
||||
|
||||
import {
|
||||
makeSimpleField,
|
||||
} from './SharedFields';
|
||||
|
||||
export const signatureOperations = [
|
||||
{
|
||||
displayName: 'Operation',
|
||||
name: 'operation',
|
||||
type: 'options',
|
||||
displayOptions: {
|
||||
show: {
|
||||
resource: [
|
||||
'signature',
|
||||
],
|
||||
},
|
||||
},
|
||||
options: [
|
||||
{
|
||||
name: 'Create',
|
||||
value: 'create',
|
||||
},
|
||||
{
|
||||
name: 'Get',
|
||||
value: 'get',
|
||||
},
|
||||
{
|
||||
name: 'Get All',
|
||||
value: 'getAll',
|
||||
},
|
||||
{
|
||||
name: 'Update',
|
||||
value: 'update',
|
||||
},
|
||||
],
|
||||
default: 'create',
|
||||
description: 'Operation to perform',
|
||||
},
|
||||
] as INodeProperties[];
|
||||
|
||||
export const signatureFields = [
|
||||
// ----------------------------------------
|
||||
// signature: create
|
||||
// ----------------------------------------
|
||||
{
|
||||
displayName: 'Petition ID',
|
||||
name: 'petitionId',
|
||||
description: 'ID of the petition to sign.',
|
||||
type: 'string',
|
||||
default: '',
|
||||
required: true,
|
||||
displayOptions: {
|
||||
show: {
|
||||
resource: [
|
||||
'signature',
|
||||
],
|
||||
operation: [
|
||||
'create',
|
||||
],
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
displayName: 'Person ID',
|
||||
name: 'personId',
|
||||
description: 'ID of the person whose signature to create.',
|
||||
type: 'string',
|
||||
default: '',
|
||||
required: true,
|
||||
displayOptions: {
|
||||
show: {
|
||||
resource: [
|
||||
'signature',
|
||||
],
|
||||
operation: [
|
||||
'create',
|
||||
],
|
||||
},
|
||||
},
|
||||
},
|
||||
makeSimpleField('signature', 'create'),
|
||||
{
|
||||
displayName: 'Additional Fields',
|
||||
name: 'additionalFields',
|
||||
type: 'collection',
|
||||
placeholder: 'Add Field',
|
||||
default: {},
|
||||
displayOptions: {
|
||||
show: {
|
||||
resource: [
|
||||
'signature',
|
||||
],
|
||||
operation: [
|
||||
'create',
|
||||
],
|
||||
},
|
||||
},
|
||||
options: [
|
||||
{
|
||||
displayName: 'Comments',
|
||||
name: 'comments',
|
||||
type: 'string',
|
||||
default: '',
|
||||
description: 'Comments to leave when signing this petition.',
|
||||
},
|
||||
],
|
||||
},
|
||||
|
||||
// ----------------------------------------
|
||||
// signature: get
|
||||
// ----------------------------------------
|
||||
{
|
||||
displayName: 'Petition ID',
|
||||
name: 'petitionId',
|
||||
description: 'ID of the petition whose signature to retrieve.',
|
||||
type: 'string',
|
||||
default: '',
|
||||
required: true,
|
||||
displayOptions: {
|
||||
show: {
|
||||
resource: [
|
||||
'signature',
|
||||
],
|
||||
operation: [
|
||||
'get',
|
||||
],
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
displayName: 'Signature ID',
|
||||
name: 'signatureId',
|
||||
description: 'ID of the signature to retrieve.',
|
||||
type: 'string',
|
||||
default: '',
|
||||
required: true,
|
||||
displayOptions: {
|
||||
show: {
|
||||
resource: [
|
||||
'signature',
|
||||
],
|
||||
operation: [
|
||||
'get',
|
||||
],
|
||||
},
|
||||
},
|
||||
},
|
||||
makeSimpleField('signature', 'get'),
|
||||
|
||||
// ----------------------------------------
|
||||
// signature: getAll
|
||||
// ----------------------------------------
|
||||
{
|
||||
displayName: 'Petition ID',
|
||||
name: 'petitionId',
|
||||
description: 'ID of the petition whose signatures to retrieve.',
|
||||
type: 'string',
|
||||
default: '',
|
||||
required: true,
|
||||
displayOptions: {
|
||||
show: {
|
||||
resource: [
|
||||
'signature',
|
||||
],
|
||||
operation: [
|
||||
'getAll',
|
||||
],
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
displayName: 'Return All',
|
||||
name: 'returnAll',
|
||||
type: 'boolean',
|
||||
default: false,
|
||||
description: 'Return all results.',
|
||||
displayOptions: {
|
||||
show: {
|
||||
resource: [
|
||||
'signature',
|
||||
],
|
||||
operation: [
|
||||
'getAll',
|
||||
],
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
displayName: 'Limit',
|
||||
name: 'limit',
|
||||
type: 'number',
|
||||
default: 50,
|
||||
description: 'The number of results to return.',
|
||||
typeOptions: {
|
||||
minValue: 1,
|
||||
},
|
||||
displayOptions: {
|
||||
show: {
|
||||
resource: [
|
||||
'signature',
|
||||
],
|
||||
operation: [
|
||||
'getAll',
|
||||
],
|
||||
returnAll: [
|
||||
false,
|
||||
],
|
||||
},
|
||||
},
|
||||
},
|
||||
makeSimpleField('signature', 'getAll'),
|
||||
|
||||
// ----------------------------------------
|
||||
// signature: update
|
||||
// ----------------------------------------
|
||||
{
|
||||
displayName: 'Petition ID',
|
||||
name: 'petitionId',
|
||||
description: 'ID of the petition whose signature to update.',
|
||||
type: 'string',
|
||||
default: '',
|
||||
required: true,
|
||||
displayOptions: {
|
||||
show: {
|
||||
resource: [
|
||||
'signature',
|
||||
],
|
||||
operation: [
|
||||
'update',
|
||||
],
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
displayName: 'Signature ID',
|
||||
name: 'signatureId',
|
||||
description: 'ID of the signature to update.',
|
||||
type: 'string',
|
||||
default: '',
|
||||
required: true,
|
||||
displayOptions: {
|
||||
show: {
|
||||
resource: [
|
||||
'signature',
|
||||
],
|
||||
operation: [
|
||||
'update',
|
||||
],
|
||||
},
|
||||
},
|
||||
},
|
||||
makeSimpleField('signature', 'update'),
|
||||
{
|
||||
displayName: 'Update Fields',
|
||||
name: 'updateFields',
|
||||
type: 'collection',
|
||||
placeholder: 'Add Field',
|
||||
default: {},
|
||||
displayOptions: {
|
||||
show: {
|
||||
resource: [
|
||||
'signature',
|
||||
],
|
||||
operation: [
|
||||
'update',
|
||||
],
|
||||
},
|
||||
},
|
||||
options: [
|
||||
{
|
||||
displayName: 'Comments',
|
||||
name: 'comments',
|
||||
type: 'string',
|
||||
default: '',
|
||||
description: 'Comments to leave when signing this petition.',
|
||||
},
|
||||
],
|
||||
},
|
||||
] as INodeProperties[];
|
||||
@@ -0,0 +1,131 @@
|
||||
import {
|
||||
INodeProperties,
|
||||
} from 'n8n-workflow';
|
||||
|
||||
import {
|
||||
makeSimpleField,
|
||||
} from './SharedFields';
|
||||
|
||||
export const tagOperations = [
|
||||
{
|
||||
displayName: 'Operation',
|
||||
name: 'operation',
|
||||
type: 'options',
|
||||
displayOptions: {
|
||||
show: {
|
||||
resource: [
|
||||
'tag',
|
||||
],
|
||||
},
|
||||
},
|
||||
options: [
|
||||
{
|
||||
name: 'Create',
|
||||
value: 'create',
|
||||
},
|
||||
{
|
||||
name: 'Get',
|
||||
value: 'get',
|
||||
},
|
||||
{
|
||||
name: 'Get All',
|
||||
value: 'getAll',
|
||||
},
|
||||
],
|
||||
default: 'create',
|
||||
description: 'Operation to perform',
|
||||
},
|
||||
] as INodeProperties[];
|
||||
|
||||
export const tagFields = [
|
||||
// ----------------------------------------
|
||||
// tag: create
|
||||
// ----------------------------------------
|
||||
{
|
||||
displayName: 'Name',
|
||||
name: 'name',
|
||||
description: 'Name of the tag to create.',
|
||||
type: 'string',
|
||||
required: true,
|
||||
default: '',
|
||||
displayOptions: {
|
||||
show: {
|
||||
resource: [
|
||||
'tag',
|
||||
],
|
||||
operation: [
|
||||
'create',
|
||||
],
|
||||
},
|
||||
},
|
||||
},
|
||||
makeSimpleField('tag', 'create'),
|
||||
|
||||
// ----------------------------------------
|
||||
// tag: get
|
||||
// ----------------------------------------
|
||||
{
|
||||
displayName: 'Tag ID',
|
||||
name: 'tagId',
|
||||
description: 'ID of the tag to retrieve.',
|
||||
type: 'string',
|
||||
default: '',
|
||||
required: true,
|
||||
displayOptions: {
|
||||
show: {
|
||||
resource: [
|
||||
'tag',
|
||||
],
|
||||
operation: [
|
||||
'get',
|
||||
],
|
||||
},
|
||||
},
|
||||
},
|
||||
makeSimpleField('tag', 'get'),
|
||||
|
||||
// ----------------------------------------
|
||||
// tag: getAll
|
||||
// ----------------------------------------
|
||||
{
|
||||
displayName: 'Return All',
|
||||
name: 'returnAll',
|
||||
type: 'boolean',
|
||||
default: false,
|
||||
description: 'Return all results.',
|
||||
displayOptions: {
|
||||
show: {
|
||||
resource: [
|
||||
'tag',
|
||||
],
|
||||
operation: [
|
||||
'getAll',
|
||||
],
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
displayName: 'Limit',
|
||||
name: 'limit',
|
||||
type: 'number',
|
||||
default: 50,
|
||||
description: 'The number of results to return.',
|
||||
typeOptions: {
|
||||
minValue: 1,
|
||||
},
|
||||
displayOptions: {
|
||||
show: {
|
||||
resource: [
|
||||
'tag',
|
||||
],
|
||||
operation: [
|
||||
'getAll',
|
||||
],
|
||||
returnAll: [
|
||||
false,
|
||||
],
|
||||
},
|
||||
},
|
||||
},
|
||||
makeSimpleField('tag', 'getAll'),
|
||||
] as INodeProperties[];
|
||||
@@ -0,0 +1,7 @@
|
||||
export * from './AttendanceDescription';
|
||||
export * from './EventDescription';
|
||||
export * from './PersonDescription';
|
||||
export * from './PersonTagDescription';
|
||||
export * from './PetitionDescription';
|
||||
export * from './SignatureDescription';
|
||||
export * from './TagDescription';
|
||||
99
packages/nodes-base/nodes/ActionNetwork/types.d.ts
vendored
Normal file
@@ -0,0 +1,99 @@
|
||||
import { languageOptions } from './descriptions/SharedFields';
|
||||
|
||||
export type Resource = 'attendance' | 'event' | 'person' | 'personTag' | 'petition' | 'signature' | 'tag';
|
||||
|
||||
export type Operation = 'create' | 'delete' | 'get' | 'getAll' | 'update' | 'add' | 'remove';
|
||||
|
||||
export type LanguageCodes = typeof languageOptions[number]['value']
|
||||
|
||||
|
||||
// ----------------------------------------
|
||||
// UI fields
|
||||
// ----------------------------------------
|
||||
|
||||
export type AllFieldsUi = {
|
||||
email_addresses: EmailAddressUi;
|
||||
postal_addresses: PostalAddressesUi;
|
||||
phone_numbers: PhoneNumberUi;
|
||||
languages_spoken: LanguageCodes;
|
||||
target: string;
|
||||
location: LocationUi;
|
||||
}
|
||||
|
||||
export type EmailAddressUi = {
|
||||
email_addresses_fields: EmailAddressField,
|
||||
}
|
||||
|
||||
export type EmailAddressField = {
|
||||
primary: boolean;
|
||||
address: string;
|
||||
status: EmailStatus;
|
||||
}
|
||||
|
||||
type BaseStatus = 'subscribed' | 'unsubscribed' | 'bouncing' | 'previous bounce';
|
||||
|
||||
type EmailStatus = BaseStatus | 'spam complaint' | 'previous spam complaint';
|
||||
|
||||
type PhoneNumberUi = {
|
||||
phone_numbers_fields: PhoneNumberField[],
|
||||
}
|
||||
|
||||
export type PhoneNumberField = {
|
||||
primary: boolean;
|
||||
number: string;
|
||||
status: BaseStatus;
|
||||
};
|
||||
|
||||
type PostalAddressesUi = {
|
||||
postal_addresses_fields: PostalAddressField[],
|
||||
}
|
||||
|
||||
type LocationUi = {
|
||||
postal_addresses_fields: PostalAddressField,
|
||||
}
|
||||
|
||||
export type PostalAddressField = {
|
||||
primary: boolean;
|
||||
address_lines: string;
|
||||
locality: string;
|
||||
region: string;
|
||||
postal_code: string;
|
||||
country: string;
|
||||
language: LanguageCodes;
|
||||
location: { location_fields: LatitudeLongitude }
|
||||
}
|
||||
|
||||
type LatitudeLongitude = {
|
||||
latitude: string;
|
||||
longitude: string;
|
||||
}
|
||||
|
||||
export type FieldWithPrimaryField = EmailAddressField | PhoneNumberField | PostalAddressField;
|
||||
|
||||
|
||||
// ----------------------------------------
|
||||
// responses
|
||||
// ----------------------------------------
|
||||
|
||||
export type LinksFieldContainer = { _links: { self: { href: string } } };
|
||||
|
||||
export type Response = JsonObject & LinksFieldContainer;
|
||||
|
||||
export type PersonResponse = Response & {
|
||||
identifiers: string[];
|
||||
email_addresses: EmailAddressField[];
|
||||
phone_numbers: PhoneNumberField[];
|
||||
postal_addresses: PostalAddressField[];
|
||||
languages_spoken: LanguageCodes[];
|
||||
};
|
||||
|
||||
export type PetitionResponse = Response & { _embedded: { 'osdi:creator': PersonResponse } };
|
||||
|
||||
|
||||
// ----------------------------------------
|
||||
// utils
|
||||
// ----------------------------------------
|
||||
|
||||
export type JsonValue = string | number | boolean | null | JsonObject | JsonValue[];
|
||||
|
||||
export type JsonObject = { [key: string]: JsonValue };
|
||||
@@ -27,6 +27,11 @@
|
||||
"icon": "☀️",
|
||||
"url": "https://n8n.io/blog/2021-the-year-to-automate-the-new-you-with-n8n/"
|
||||
},
|
||||
{
|
||||
"label": "How to build a low-code, self-hosted URL shortener in 3 steps",
|
||||
"icon": "🔗",
|
||||
"url": "https://n8n.io/blog/how-to-build-a-low-code-self-hosted-url-shortener/"
|
||||
},
|
||||
{
|
||||
"label": "15 Google apps you can combine and automate to increase productivity",
|
||||
"icon": "💡",
|
||||
|
||||
@@ -16,7 +16,7 @@ export class AwsLambda implements INodeType {
|
||||
description: INodeTypeDescription = {
|
||||
displayName: 'AWS Lambda',
|
||||
name: 'awsLambda',
|
||||
icon: 'file:lambda.png',
|
||||
icon: 'file:lambda.svg',
|
||||
group: ['output'],
|
||||
version: 1,
|
||||
subtitle: '={{$parameter["function"]}}',
|
||||
|
||||
@@ -16,7 +16,7 @@ export class AwsSns implements INodeType {
|
||||
description: INodeTypeDescription = {
|
||||
displayName: 'AWS SNS',
|
||||
name: 'awsSns',
|
||||
icon: 'file:sns.png',
|
||||
icon: 'file:sns.svg',
|
||||
group: ['output'],
|
||||
version: 1,
|
||||
subtitle: '={{$parameter["topic"]}}',
|
||||
|
||||
@@ -26,7 +26,7 @@ export class AwsSnsTrigger implements INodeType {
|
||||
displayName: 'AWS SNS Trigger',
|
||||
subtitle: `={{$parameter["topic"].split(':')[5]}}`,
|
||||
name: 'awsSnsTrigger',
|
||||
icon: 'file:sns.png',
|
||||
icon: 'file:sns.svg',
|
||||
group: ['trigger'],
|
||||
version: 1,
|
||||
description: 'Handle AWS SNS events via webhooks',
|
||||
|
||||
@@ -51,7 +51,7 @@ export class AwsS3 implements INodeType {
|
||||
description: INodeTypeDescription = {
|
||||
displayName: 'AWS S3',
|
||||
name: 'awsS3',
|
||||
icon: 'file:s3.png',
|
||||
icon: 'file:s3.svg',
|
||||
group: ['output'],
|
||||
version: 1,
|
||||
subtitle: '={{$parameter["operation"] + ": " + $parameter["resource"]}}',
|
||||
@@ -163,6 +163,15 @@ export class AwsS3 implements INodeType {
|
||||
|
||||
returnData.push({ success: true });
|
||||
}
|
||||
|
||||
// https://docs.aws.amazon.com/AmazonS3/latest/API/API_DeleteBucket.html
|
||||
if (operation === 'delete') {
|
||||
const name = this.getNodeParameter('name', i) as string;
|
||||
|
||||
responseData = await awsApiRequestSOAP.call(this, `${name}.s3`, 'DELETE', '', '', {}, headers);
|
||||
returnData.push({ success: true });
|
||||
}
|
||||
|
||||
//https://docs.aws.amazon.com/AmazonS3/latest/API/API_ListBuckets.html
|
||||
if (operation === 'getAll') {
|
||||
const returnAll = this.getNodeParameter('returnAll', 0) as boolean;
|
||||
|
||||
@@ -20,6 +20,11 @@ export const bucketOperations = [
|
||||
value: 'create',
|
||||
description: 'Create a bucket',
|
||||
},
|
||||
{
|
||||
name: 'Delete',
|
||||
value: 'delete',
|
||||
description: 'Delete a bucket',
|
||||
},
|
||||
{
|
||||
name: 'Get All',
|
||||
value: 'getAll',
|
||||
@@ -152,6 +157,29 @@ export const bucketFields = [
|
||||
},
|
||||
],
|
||||
},
|
||||
|
||||
/* -------------------------------------------------------------------------- */
|
||||
/* bucket:delete */
|
||||
/* -------------------------------------------------------------------------- */
|
||||
{
|
||||
displayName: 'Name',
|
||||
name: 'name',
|
||||
type: 'string',
|
||||
required: true,
|
||||
default: '',
|
||||
displayOptions: {
|
||||
show: {
|
||||
resource: [
|
||||
'bucket',
|
||||
],
|
||||
operation: [
|
||||
'delete',
|
||||
],
|
||||
},
|
||||
},
|
||||
description: 'Name of the AWS S3 bucket to delete.',
|
||||
},
|
||||
|
||||
/* -------------------------------------------------------------------------- */
|
||||
/* bucket:getAll */
|
||||
/* -------------------------------------------------------------------------- */
|
||||
|
||||
@@ -57,7 +57,7 @@ export async function awsApiRequest(this: IHookFunctions | IExecuteFunctions | I
|
||||
try {
|
||||
return await this.helpers.request!(options);
|
||||
} catch (error) {
|
||||
throw new NodeApiError(this.getNode(), error, { parseXml: true });
|
||||
throw new NodeApiError(this.getNode(), error);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
Before Width: | Height: | Size: 1.7 KiB |
1
packages/nodes-base/nodes/Aws/S3/s3.svg
Normal file
@@ -0,0 +1 @@
|
||||
<svg width="2065" height="2500" viewBox="0 0 256 310" xmlns="http://www.w3.org/2000/svg" preserveAspectRatio="xMidYMid"><path d="M20.624 53.686L0 64v181.02l20.624 10.254.124-.149V53.828l-.124-.142" fill="#8C3123"/><path d="M131 229L20.624 255.274V53.686L131 79.387V229" fill="#E05243"/><path d="M81.178 187.866l46.818 5.96.294-.678.263-76.77-.557-.6-46.818 5.874v66.214" fill="#8C3123"/><path d="M127.996 229.295l107.371 26.035.169-.269-.003-201.195-.17-.18-107.367 25.996v149.613" fill="#8C3123"/><path d="M174.827 187.866l-46.831 5.96v-78.048l46.831 5.874v66.214" fill="#E05243"/><path d="M174.827 89.631l-46.831 8.535-46.818-8.535 46.759-12.256 46.89 12.256" fill="#5E1F18"/><path d="M174.827 219.801l-46.831-8.591-46.818 8.591 46.761 13.053 46.888-13.053" fill="#F2B0A9"/><path d="M81.178 89.631l46.818-11.586.379-.117V.313L127.996 0 81.178 23.413v66.218" fill="#8C3123"/><path d="M174.827 89.631l-46.831-11.586V0l46.831 23.413v66.218" fill="#E05243"/><path d="M127.996 309.428l-46.823-23.405v-66.217l46.823 11.582.689.783-.187 75.906-.502 1.351" fill="#8C3123"/><path d="M127.996 309.428l46.827-23.405v-66.217l-46.827 11.582v78.04M235.367 53.686L256 64v181.02l-20.633 10.31V53.686" fill="#E05243"/></svg>
|
||||
|
After Width: | Height: | Size: 1.2 KiB |
@@ -426,6 +426,7 @@ export class AwsTranscribe implements INodeType {
|
||||
Media: {
|
||||
MediaFileUri: mediaFileUri,
|
||||
},
|
||||
Settings: {},
|
||||
};
|
||||
|
||||
if (detectLang) {
|
||||
@@ -438,16 +439,16 @@ export class AwsTranscribe implements INodeType {
|
||||
Object.assign(body.Settings, { ChannelIdentification: options.channelIdentification });
|
||||
}
|
||||
|
||||
if (options.MaxAlternatives) {
|
||||
if (options.maxAlternatives) {
|
||||
Object.assign(body.Settings, {
|
||||
ShowAlternatives: options.maxAlternatives,
|
||||
ShowAlternatives: true,
|
||||
MaxAlternatives: options.maxAlternatives,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
if (options.showSpeakerLabels) {
|
||||
if (options.maxSpeakerLabels) {
|
||||
Object.assign(body.Settings, {
|
||||
ShowSpeakerLabels: options.showSpeakerLabels,
|
||||
ShowSpeakerLabels: true,
|
||||
MaxSpeakerLabels: options.maxSpeakerLabels,
|
||||
});
|
||||
}
|
||||
|
||||
|
Before Width: | Height: | Size: 1.3 KiB |
1
packages/nodes-base/nodes/Aws/lambda.svg
Normal file
@@ -0,0 +1 @@
|
||||
<svg height="2500" viewBox="-3.023 -0.22 420.923 433.54" width="2443" xmlns="http://www.w3.org/2000/svg"><path d="M208.45 227.89c-1.59 2.26-2.93 4.12-4.22 6q-30.86 45.42-61.7 90.83-28.69 42.24-57.44 84.43a3.88 3.88 0 01-2.73 1.59q-40.59-.35-81.16-.88c-.3 0-.61-.09-1.2-.18a14.44 14.44 0 01.76-1.65q28.31-43.89 56.62-87.76 25.11-38.88 50.25-77.74 27.86-43.18 55.69-86.42c2.74-4.25 5.59-8.42 8.19-12.75a5.26 5.26 0 00.56-3.83c-5-15.94-10.1-31.84-15.19-47.74-2.18-6.81-4.46-13.58-6.5-20.43-.66-2.2-1.75-2.87-4-2.86-17 .07-33.9.05-50.85.05-3.22 0-3.23 0-3.23-3.18 0-20.84 0-41.68-.06-62.52 0-2.32.76-2.84 2.94-2.84q51.19.09 102.4 0a3.29 3.29 0 013.6 2.43q27 67.91 54 135.77 31.5 79.14 63 158.3c6.52 16.38 13.09 32.75 19.54 49.17.77 2 1.57 2.38 3.59 1.76 17.89-5.53 35.82-10.91 53.7-16.45 2.25-.7 3.07-.23 3.77 2 6.1 19.17 12.32 38.3 18.5 57.45.21.66.37 1.33.62 2.25-1.28.47-2.48 1-3.71 1.34q-61 19.33-121.93 38.68c-1.94.61-2.52-.05-3.17-1.68q-18.61-47.16-37.31-94.28-18.29-46.14-36.6-92.28c-1.83-4.62-3.63-9.26-5.46-13.88-.29-.79-.69-1.48-1.27-2.7z" fill="#fa7e14"/></svg>
|
||||
|
After Width: | Height: | Size: 1.0 KiB |
|
Before Width: | Height: | Size: 601 B |
1
packages/nodes-base/nodes/Aws/sns.svg
Normal file
@@ -0,0 +1 @@
|
||||
<svg width="2490" height="2500" viewBox="0 0 256 257" xmlns="http://www.w3.org/2000/svg" preserveAspectRatio="xMidYMid"><path d="M98.875 232.033l-26.433-7.408-25.001-28.508 31.272-.863 20.162 36.779m-61.125-18.8l-14.875-4.166-14.058-16.034 17.082-2.809 11.851 23.009" fill="#995B80"/><path d="M0 191.017l15.204 3.091 2.207-3.882V58.503l-2.207-2.561L0 64.6v126.417" fill="#7B3F65"/><path d="M73.933 69.708L15.208 55.942v138.166l8.798-.818 13.744 19.943 10.6-22.205 25.583-2.378V69.708" fill="#C17B9D"/><path d="M33.958 198.133l26.063 5.25 1.716-4.045V37.44l-1.716-3.665-26.063 13.208v151.15" fill="#7B3F65"/><path d="M208.734 81.516L60.021 33.775v169.612l17.221-2.216 21.633 30.862 17.126-35.85 92.733-11.933V81.516" fill="#C17B9D"/><path d="M181.833 256.492l-37.566-10.525-35.509-40.5 46.033-.468 27.042 51.493" fill="#995B80"/><path d="M89.591 208.95l38.33 7.417 2.977-2.566V4.117L127.921 0l-38.33 19.158V208.95" fill="#7B3F65"/><path d="M256 64.033L127.925 0v216.367l22.597-4.528 31.311 44.653 26.901-56.309-.017-.002L256 190.708V64.033" fill="#C17B9D"/></svg>
|
||||
|
After Width: | Height: | Size: 1.0 KiB |
@@ -21,7 +21,7 @@ export class BitbucketTrigger implements INodeType {
|
||||
description: INodeTypeDescription = {
|
||||
displayName: 'Bitbucket Trigger',
|
||||
name: 'bitbucketTrigger',
|
||||
icon: 'file:bitbucket.png',
|
||||
icon: 'file:bitbucket.svg',
|
||||
group: ['trigger'],
|
||||
version: 1,
|
||||
description: 'Handle Bitbucket events via webhooks',
|
||||
|
||||
|
Before Width: | Height: | Size: 1002 B |
1
packages/nodes-base/nodes/Bitbucket/bitbucket.svg
Normal file
@@ -0,0 +1 @@
|
||||
<svg width="48" height="48" xmlns="http://www.w3.org/2000/svg" fill="none"><defs><linearGradient y2=".656" x2=".33" y1="-.17" x1=".762" id="a"><stop stop-color="#0052CC" offset=".18"/><stop stop-color="#2684FF" offset="1"/></linearGradient></defs><path stroke="null" fill="#2684FF" d="M1.721 3.873a1.465 1.465 0 011.129-.509l42.772.007a1.466 1.466 0 011.466 1.7l-6.222 38.193a1.466 1.466 0 01-1.465 1.231H9.555a1.993 1.993 0 01-1.95-1.663L1.386 5.064c-.07-.427.053-.863.336-1.191zm17.8 26.787h9.526l2.308-13.475H16.941l2.58 13.475z" clip-rule="evenodd" fill-rule="evenodd"/><path stroke="null" fill="url(#a)" d="M44.418 18.437h-12.64l-2.12 12.383h-8.755L10.567 43.09c.327.283.745.44 1.178.444H39.18a1.347 1.347 0 001.347-1.132l3.892-23.965z"/></svg>
|
||||
|
After Width: | Height: | Size: 749 B |
@@ -25,7 +25,7 @@ export class Bitly implements INodeType {
|
||||
description: INodeTypeDescription = {
|
||||
displayName: 'Bitly',
|
||||
name: 'bitly',
|
||||
icon: 'file:bitly.png',
|
||||
icon: 'file:bitly.svg',
|
||||
group: ['output'],
|
||||
version: 1,
|
||||
subtitle: '={{$parameter["operation"] + ": " + $parameter["resource"]}}',
|
||||
|
||||
|
Before Width: | Height: | Size: 1.7 KiB |
1
packages/nodes-base/nodes/Bitly/bitly.svg
Normal file
@@ -0,0 +1 @@
|
||||
<svg viewBox="0 0 128 128" xmlns="http://www.w3.org/2000/svg"><g clip-rule="evenodd" fill-rule="evenodd"><path fill="none" d="M0 0h128v128H0z"/><path d="M63.708 0C28.443 0 0 28.306 0 65.322c0 19.193 10.266 37.736 24.703 48.877 2.78 2.145 6.091 1.949 7.989.109 1.599-1.55 1.467-5.282-1.507-7.897-11.541-10.149-19.52-25.514-19.52-40.861 0-27.564 24.474-50.756 52.044-50.756 33.558 0 51.503 27.252 51.503 50.351 0 14.114-6.902 31.155-19.361 42.025.019-.049 2.588-5.101 2.588-14.94 0-16.748-10.612-25.821-22.926-25.821-8.914 0-14.251 3.187-17.883 6.158 0-6.822.228-19.563.228-19.563 0-8.409-2.946-15.14-13.213-15.287-5.943-.084-10.353 2.641-13.103 8.803-.991 2.311-.626 4.822 1.333 5.96 1.621.941 4.287.243 5.606-1.517.881-1.1 1.375-1.335 2.141-1.252 1.261.136 1.309 2.168 1.359 3.468.038.998 1.008 15.494.477 52.746 0 10.275 8.059 22.077 27.355 22.077 8.307 0 14.686-2.322 23.953-7.587C107.871 112.398 128 94.62 128 64.55 128 26.977 97.572 0 63.708 0zm6.052 113.602c-7.183.126-12.422-2.183-12.743-8.461-.12-2.356-.082-4.865.033-6.378.695-9.174 7.11-15.774 13.443-17.041 7.876-1.576 13.123 2.026 13.123 12.337-.001 6.968-1.935 19.334-13.856 19.543z" fill="#DD5A2B"/></g></svg>
|
||||
|
After Width: | Height: | Size: 1.1 KiB |
@@ -16,6 +16,13 @@
|
||||
{
|
||||
"url": "https://docs.n8n.io/nodes/n8n-nodes-base.calendlyTrigger/"
|
||||
}
|
||||
],
|
||||
"generic": [
|
||||
{
|
||||
"label": "5 tasks you can automate with the new Notion API ",
|
||||
"icon": "⚡️",
|
||||
"url": "https://n8n.io/blog/5-tasks-you-can-automate-with-notion-api/"
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
@@ -17,7 +17,7 @@ export class CalendlyTrigger implements INodeType {
|
||||
description: INodeTypeDescription = {
|
||||
displayName: 'Calendly Trigger',
|
||||
name: 'calendlyTrigger',
|
||||
icon: 'file:calendly.png',
|
||||
icon: 'file:calendly.svg',
|
||||
group: ['trigger'],
|
||||
version: 1,
|
||||
description: 'Starts the workflow when Calendly events occur.',
|
||||
|
||||
|
Before Width: | Height: | Size: 924 B |
1
packages/nodes-base/nodes/Calendly/calendly.svg
Normal file
@@ -0,0 +1 @@
|
||||
<svg height="2500" viewBox="7.4 0 344.6 360" width="2342" xmlns="http://www.w3.org/2000/svg"><g fill="#676b74"><path d="M313.8 360H45.5c-21 0-38.1-17.1-38.1-38.1V53.5c0-21 17.1-38.1 38.1-38.1h268.3c21 0 38.1 17.1 38.1 38.1v268.3c.1 21.1-17 38.2-38.1 38.2zM45.5 36.5c-9.4 0-17 7.6-17 17v268.3c0 9.4 7.6 17 17 17h268.3c9.4 0 17-7.6 17-17V53.5c0-9.4-7.6-17-17-17z"/><path d="M256.6 72.4c-4.5 0-8.1-3.6-8.1-8.1V8.1c0-4.5 3.6-8.1 8.1-8.1s8.1 3.6 8.1 8.1v56.1c0 4.5-3.6 8.2-8.1 8.2zm-154.7 0c-4.5 0-8.1-3.6-8.1-8.1V8.1c0-4.5 3.6-8.1 8.1-8.1s8.1 3.6 8.1 8.1v56.1c.1 4.5-3.6 8.2-8.1 8.2zm87.5 181.4c-33.6 0-60.9-27.3-60.9-60.9s27.3-60.9 60.9-60.9c15.2 0 29.7 5.6 40.9 15.8 1.4 1.2 1.5 3.4.2 4.7-1.2 1.4-3.4 1.5-4.7.2-10-9.1-22.9-14.1-36.4-14.1-29.9 0-54.2 24.3-54.2 54.2s24.3 54.2 54.2 54.2c13.5 0 26.4-5 36.4-14.1 1.4-1.2 3.5-1.1 4.7.2 1.2 1.4 1.1 3.5-.2 4.7-11.2 10.4-25.7 16-40.9 16z"/></g></svg>
|
||||
|
After Width: | Height: | Size: 891 B |
@@ -1 +1 @@
|
||||
<svg viewBox="-10 0 155 155" xmlns="http://www.w3.org/2000/svg"><defs><linearGradient x1="0%" y1="68.01%" y2="68.01%" id="a"><stop stop-color="#8930FD" offset="0%"/><stop stop-color="#49CCF9" offset="100%"/></linearGradient><linearGradient x1="0%" y1="68.01%" y2="68.01%" id="b"><stop stop-color="#FF02F0" offset="0%"/><stop stop-color="#FFC800" offset="100%"/></linearGradient></defs><g fill="none"><path d="M.4 119.12l23.81-18.24C36.86 117.39 50.3 125 65.26 125c14.88 0 27.94-7.52 40.02-23.9l24.15 17.8C112 142.52 90.34 155 65.26 155c-25 0-46.87-12.4-64.86-35.88z" fill="url(#a)"/><path fill="url(#b)" d="M65.18 39.84L22.8 76.36 3.21 53.64 65.27.16l61.57 53.52-19.68 22.64z"/></g></svg>
|
||||
<svg viewBox="-10 0 155 155" xmlns="http://www.w3.org/2000/svg"><defs><linearGradient x1="0%" y1="68.01%" y2="68.01%" id="a"><stop stop-color="#8930FD" offset="0%"/><stop stop-color="#49CCF9" offset="100%"/></linearGradient><linearGradient x1="0%" y1="68.01%" y2="68.01%" id="b"><stop stop-color="#FF02F0" offset="0%"/><stop stop-color="#FFC800" offset="100%"/></linearGradient></defs><g fill="none"><path d="M.4 119.12l23.81-18.24C36.86 117.39 50.3 125 65.26 125c14.88 0 27.94-7.52 40.02-23.9l24.15 17.8C112 142.52 90.34 155 65.26 155c-25 0-46.87-12.4-64.86-35.88z" fill="url(#a)"/><path fill="url(#b)" d="M65.18 39.84L22.8 76.36 3.21 53.64 65.27.16l61.57 53.52-19.68 22.64z"/></g></svg>
|
||||
|
Before Width: | Height: | Size: 689 B After Width: | Height: | Size: 688 B |
@@ -50,7 +50,7 @@ export class Clockify implements INodeType {
|
||||
description: INodeTypeDescription = {
|
||||
displayName: 'Clockify',
|
||||
name: 'clockify',
|
||||
icon: 'file:clockify.png',
|
||||
icon: 'file:clockify.svg',
|
||||
group: ['transform'],
|
||||
version: 1,
|
||||
subtitle: '={{$parameter["operation"] + ": " + $parameter["resource"]}}',
|
||||
|
||||
@@ -22,7 +22,7 @@ import { IWorkspaceDto } from './WorkpaceInterfaces';
|
||||
export class ClockifyTrigger implements INodeType {
|
||||
description: INodeTypeDescription = {
|
||||
displayName: 'Clockify Trigger',
|
||||
icon: 'file:clockify.png',
|
||||
icon: 'file:clockify.svg',
|
||||
name: 'clockifyTrigger',
|
||||
group: [ 'trigger' ],
|
||||
version: 1,
|
||||
|
||||
|
Before Width: | Height: | Size: 2.3 KiB |
1
packages/nodes-base/nodes/Clockify/clockify.svg
Normal file
@@ -0,0 +1 @@
|
||||
<svg width="256" height="256" fill="none" xmlns="http://www.w3.org/2000/svg"><path fill="none" d="M14 0h227v256H14z"/><path fill-rule="evenodd" clip-rule="evenodd" d="M165.124 87.99l58.777-58.814 16.825 16.823-58.778 58.815-16.824-16.823zm-23.817 60.105c-11.503 0-20.822-9.36-20.822-20.918 0-11.546 9.319-20.918 20.822-20.918 11.503 0 20.822 9.372 20.822 20.918 0 11.558-9.319 20.918-20.822 20.918zM241 208.845l-16.824 16.835-58.778-58.816 16.825-16.835L241 208.845z" fill="#222"/><path fill-rule="evenodd" clip-rule="evenodd" d="M141.913 217.637c11.848 0 23.136-2.364 33.482-6.567l28.708 28.74C185.692 250.089 164.5 256 141.913 256 71.274 256 14 198.689 14 128.006 14 57.311 71.275 0 141.913 0c22.361 0 43.362 5.767 61.641 15.856l-28.231 28.261c-10.333-4.179-21.585-6.543-33.41-6.543-49.47 0-89.575 40.309-89.575 90.038 0 49.716 40.104 90.025 89.575 90.025z" fill="#03A9F4"/></svg>
|
||||
|
After Width: | Height: | Size: 882 B |
@@ -36,7 +36,7 @@ export class Coda implements INodeType {
|
||||
description: INodeTypeDescription = {
|
||||
displayName: 'Coda',
|
||||
name: 'coda',
|
||||
icon: 'file:coda.png',
|
||||
icon: 'file:coda.svg',
|
||||
group: ['output'],
|
||||
version: 1,
|
||||
subtitle: '={{$parameter["operation"] + ": " + $parameter["resource"]}}',
|
||||
|
||||
|
Before Width: | Height: | Size: 1.8 KiB |
1
packages/nodes-base/nodes/Coda/coda.svg
Normal file
@@ -0,0 +1 @@
|
||||
<svg width="34" height="54" xmlns="http://www.w3.org/2000/svg"><path d="M.69 48.887c0 2.489 1.557 4.184 3.704 4.184 1.896 0 3.095-1.082 3.328-2.687h-1.79c-.142.721-.733 1.118-1.52 1.118-1.199 0-1.932-1.1-1.932-2.615 0-1.516.733-2.616 1.932-2.616.787 0 1.378.397 1.52 1.118h1.79c-.233-1.605-1.432-2.687-3.328-2.687-2.147 0-3.703 1.695-3.703 4.185zm15.623 0c0-2.454-1.539-4.185-3.775-4.185-2.237 0-3.775 1.731-3.775 4.185 0 2.453 1.538 4.184 3.775 4.184 2.236 0 3.775-1.731 3.775-4.184zm-1.79 0c0 1.515-.697 2.633-1.985 2.633-1.289 0-1.986-1.118-1.986-2.633 0-1.516.697-2.634 1.986-2.634 1.288 0 1.986 1.118 1.986 2.634zm8.665 4.003h1.79V41.167h-1.79v4.402c-.358-.451-1.145-.866-2.021-.866-2.326 0-3.686 1.894-3.686 4.185 0 2.308 1.36 4.184 3.685 4.184.877 0 1.664-.415 2.022-.865v.685zm0-2.488a1.927 1.927 0 01-1.717 1.064c-1.378 0-2.2-1.1-2.2-2.58 0-1.479.822-2.579 2.2-2.579.733 0 1.413.433 1.717 1.064v3.03zM33.31 52.89v-5.248c0-1.822-1.181-2.94-3.185-2.94-1.628 0-2.88 1.028-3.113 2.399h1.718c.196-.56.644-.866 1.36-.866 1.019 0 1.52.631 1.52 1.443v.65c-.322-.235-1.127-.488-1.843-.488-1.753 0-3.095 1.065-3.095 2.58 0 1.641 1.342 2.615 2.97 2.615.895 0 1.7-.325 1.968-.595v.45h1.7zm-1.7-2.128c-.197.505-.912.812-1.628.812-.805 0-1.646-.343-1.646-1.154 0-.794.84-1.137 1.646-1.137.716 0 1.431.307 1.628.812v.667zM30.682.928H3.318A2.99 2.99 0 00.333 3.905v29.762a2.99 2.99 0 002.985 2.976h27.364a2.99 2.99 0 002.985-2.976v-1.39c-.05-1.785-.1-5.505-.1-7.192 0-.942-.696-1.736-1.592-1.736-.995 0-1.642.595-2.14 1.091-1.492 1.34-3.73 1.588-5.67 1.24-.896-.198-1.742-.446-2.439-.892-2.139-1.24-3.532-3.572-3.532-6.052 0-2.48 1.393-4.762 3.532-6.052.747-.446 1.592-.694 2.438-.892 1.89-.348 4.18-.1 5.672 1.24.547.496 1.194 1.09 2.14 1.09.895 0 1.591-.793 1.591-1.735 0-1.637.05-5.407.1-7.193v-1.29A2.99 2.99 0 0030.682.929z" fill="#F46A54"/></svg>
|
||||
|
After Width: | Height: | Size: 1.8 KiB |
@@ -32,7 +32,7 @@ export class CoinGecko implements INodeType {
|
||||
description: INodeTypeDescription = {
|
||||
displayName: 'CoinGecko',
|
||||
name: 'coinGecko',
|
||||
icon: 'file:coinGecko.png',
|
||||
icon: 'file:coinGecko.svg',
|
||||
group: ['output'],
|
||||
version: 1,
|
||||
description: 'Consume CoinGecko API',
|
||||
|
||||
|
Before Width: | Height: | Size: 1.8 KiB |
1
packages/nodes-base/nodes/CoinGecko/coinGecko.svg
Normal file
@@ -0,0 +1 @@
|
||||
<svg id="Layer_1" data-name="Layer 1" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 276 276"><defs><style>.cls-3{fill:#fff}.cls-4{fill:#8bc53f}.cls-6{fill:#58595b}</style></defs><g id="Coin_Gecko_AI" data-name="Coin Gecko AI"><path d="M276 137.39A138 138 0 11137.39 0 138 138 0 01276 137.39z" fill="#8dc63f"/><path d="M265.65 137.44a127.63 127.63 0 11-128.21-127 127.65 127.65 0 01128.21 127z" fill="#f9e988"/><path class="cls-3" d="M140.35 18.66a70.18 70.18 0 0124.53 0 74.75 74.75 0 0123.43 7.85c7.28 4 13.57 9.43 19.83 14.52s12.49 10.3 18.42 16a93.32 93.32 0 0115.71 19 108.28 108.28 0 0111 22.17c5.33 15.66 7.18 32.53 4.52 48.62H257c-2.67-15.95-6.29-31.15-12-45.61A177.51 177.51 0 00235.56 80a209.1 209.1 0 00-12.42-20 72.31 72.31 0 00-16.64-16.8c-6.48-4.62-13.93-7.61-21.14-10.45S171 27 163.48 24.84s-15.16-3.78-23.14-5.35z"/><path class="cls-4" d="M202.74 92.39c-9.26-2.68-18.86-6.48-28.58-10.32-.56-2.44-2.72-5.48-7.09-9.19-6.35-5.51-18.28-5.37-28.59-2.93-11.38-2.68-22.62-3.63-33.41-1-88.25 24.31-38.21 83.62-70.61 143.24 4.61 9.78 54.3 66.84 126.2 51.53 0 0-24.59-59.09 30.9-87.45 45.01-23.09 77.53-65.81 11.18-83.88z"/><path class="cls-3" d="M213.64 131.2a5.35 5.35 0 11-5.38-5.32 5.36 5.36 0 015.38 5.32z"/><path d="M138.48 69.91c6.43.46 29.68 8 35.68 12.12-5-14.5-21.83-16.43-35.68-12.12z" fill="#009345"/><path class="cls-3" d="M144.6 106.58a24.68 24.68 0 11-24.69-24.67 24.68 24.68 0 0124.68 24.66z"/><path class="cls-6" d="M137.28 106.8a17.36 17.36 0 11-17.36-17.36 17.36 17.36 0 0117.36 17.36z"/><path class="cls-4" d="M233.63 142.08c-20 14.09-42.74 24.78-75 24.78-15.1 0-18.16-16-28.14-8.18-5.15 4.06-23.31 13.14-37.72 12.45S55 162 48.49 131.23C45.91 162 44.59 184.65 33 210.62c23 36.83 77.84 65.24 127.62 53-5.31-37.35 27.38-73.93 45.72-92.62 7-7.09 20.3-18.66 27.29-28.91z"/><path class="cls-6" d="M232.85 143c-6.21 5.66-13.6 9.85-21.12 13.55a134.9 134.9 0 01-23.7 8.63c-8.16 2.11-16.67 3.7-25.29 2.92s-17.43-3.71-23.14-10.17l.27-.31c7 4.54 15.08 6.14 23.12 6.37a108.27 108.27 0 0024.3-2 132.71 132.71 0 0023.61-7.3c7.63-3.15 15.18-6.8 21.68-12z"/></g></svg>
|
||||
|
After Width: | Height: | Size: 2.0 KiB |
@@ -44,7 +44,7 @@ export class ConvertKit implements INodeType {
|
||||
description: INodeTypeDescription = {
|
||||
displayName: 'ConvertKit',
|
||||
name: 'convertKit',
|
||||
icon: 'file:convertKit.png',
|
||||
icon: 'file:convertKit.svg',
|
||||
group: ['input'],
|
||||
version: 1,
|
||||
subtitle: '={{$parameter["operation"] + ": " + $parameter["resource"]}}',
|
||||
|
||||
@@ -24,7 +24,7 @@ export class ConvertKitTrigger implements INodeType {
|
||||
description: INodeTypeDescription = {
|
||||
displayName: 'ConvertKit Trigger',
|
||||
name: 'convertKitTrigger',
|
||||
icon: 'file:convertKit.png',
|
||||
icon: 'file:convertKit.svg',
|
||||
subtitle: '={{$parameter["event"]}}',
|
||||
group: ['trigger'],
|
||||
version: 1,
|
||||
|
||||
|
Before Width: | Height: | Size: 1.3 KiB |
1
packages/nodes-base/nodes/ConvertKit/convertKit.svg
Normal file
@@ -0,0 +1 @@
|
||||
<svg width="172" height="160" fill="none" xmlns="http://www.w3.org/2000/svg"><path d="M82.72 126.316c29.77 0 52.78-22.622 52.78-50.526 0-26.143-21.617-42.106-35.935-42.106-19.945 0-35.93 14.084-38.198 34.988-.418 3.856-3.476 7.09-7.355 7.061-6.423-.046-15.746-.1-21.658-.08-2.555.008-4.669-2.065-4.543-4.618.89-18.123 6.914-35.07 18.402-48.087C58.976 8.488 77.561 0 99.565 0c36.969 0 71.869 33.786 71.869 75.79 0 46.508-38.312 84.21-87.927 84.21-35.384 0-71.021-23.258-83.464-55.775a.702.702 0 01-.03-.377c.165-.962.494-1.841.818-2.707.471-1.258.931-2.488.864-3.906l-.215-4.529a5.523 5.523 0 013.18-5.263l1.798-.842a6.982 6.982 0 003.912-5.075 6.993 6.993 0 016.887-5.736c5.282 0 9.875 3.515 11.59 8.512 8.307 24.212 21.511 42.014 53.873 42.014z" fill="#FB6970"/></svg>
|
||||
|
After Width: | Height: | Size: 769 B |
@@ -58,6 +58,11 @@
|
||||
"icon": "⚙️",
|
||||
"url": "https://n8n.io/blog/automate-your-data-processing-pipeline-in-9-steps-with-n8n/"
|
||||
},
|
||||
{
|
||||
"label": "5 tasks you can automate with the new Notion API ",
|
||||
"icon": "⚡️",
|
||||
"url": "https://n8n.io/blog/5-tasks-you-can-automate-with-notion-api/"
|
||||
},
|
||||
{
|
||||
"label": "Celebrating World Poetry Day with a daily poem in Telegram",
|
||||
"icon": "📜",
|
||||
@@ -123,7 +128,7 @@
|
||||
"alias": [
|
||||
"Time",
|
||||
"Scheduler",
|
||||
"Poll"
|
||||
"Polling"
|
||||
],
|
||||
"subcategories": {
|
||||
"Core Nodes": [
|
||||
|
||||
@@ -12,6 +12,13 @@
|
||||
{
|
||||
"url": "https://docs.n8n.io/nodes/n8n-nodes-base.crypto/"
|
||||
}
|
||||
],
|
||||
"generic": [
|
||||
{
|
||||
"label": "How to build a low-code, self-hosted URL shortener in 3 steps",
|
||||
"icon": "🔗",
|
||||
"url": "https://n8n.io/blog/how-to-build-a-low-code-self-hosted-url-shortener/"
|
||||
}
|
||||
]
|
||||
},
|
||||
"alias": [
|
||||
|
||||
@@ -34,7 +34,7 @@ export class CustomerIo implements INodeType {
|
||||
description: INodeTypeDescription = {
|
||||
displayName: 'Customer.io',
|
||||
name: 'customerIo',
|
||||
icon: 'file:customerio.png',
|
||||
icon: 'file:customerio.svg',
|
||||
group: ['output'],
|
||||
version: 1,
|
||||
subtitle: '={{$parameter["operation"] + ": " + $parameter["resource"]}}',
|
||||
|
||||
@@ -29,7 +29,7 @@ export class CustomerIoTrigger implements INodeType {
|
||||
displayName: 'Customer.io Trigger',
|
||||
name: 'customerIoTrigger',
|
||||
group: ['trigger'],
|
||||
icon: 'file:customerio.png',
|
||||
icon: 'file:customerio.svg',
|
||||
version: 1,
|
||||
description: 'Starts the workflow on a Customer.io update. (Beta)',
|
||||
defaults: {
|
||||
|
||||
|
Before Width: | Height: | Size: 1.4 KiB |
1
packages/nodes-base/nodes/CustomerIo/customerio.svg
Normal file
@@ -0,0 +1 @@
|
||||
<svg width="256" height="180" xmlns="http://www.w3.org/2000/svg" preserveAspectRatio="xMidYMid"><path d="M127.843 102.423a51.21 51.21 0 10-51.21-51.186 51.186 51.186 0 0051.21 51.186z" fill="#FFCD00"/><path d="M128.161 128.016h-.171c-35.127-.009-65.777-23.834-74.453-57.872-2.571-10.238-10.898-18.907-21.454-18.907H0c0 70.686 57.303 127.99 127.99 127.99h.171v-51.211z" fill="#00ECBB"/><path d="M127.843 128.016h.147c35.13 0 65.785-23.829 74.452-57.872 2.597-10.238 10.923-18.907 21.48-18.907h32.082c-.013 70.69-57.323 127.99-128.014 127.99h-.147v-51.211z" fill="#AF64FF"/><path d="M218.509 141.73a127.99 127.99 0 01-181.013 0l36.197-36.197c30 29.98 78.618 29.98 108.618 0l36.198 36.198z" fill="#7131FF"/></svg>
|
||||
|
After Width: | Height: | Size: 710 B |
@@ -5,6 +5,7 @@ import {
|
||||
INodeExecutionData,
|
||||
INodeType,
|
||||
INodeTypeDescription,
|
||||
JsonObject,
|
||||
NodeApiError,
|
||||
NodeOperationError,
|
||||
} from 'n8n-workflow';
|
||||
@@ -95,7 +96,7 @@ export class Discord implements INodeType {
|
||||
} while (--maxTries);
|
||||
|
||||
if (maxTries <= 0) {
|
||||
throw new NodeApiError(this.getNode(), { request: options }, { message: 'Could not send message. Max. amount of rate-limit retries got reached.' });
|
||||
throw new NodeApiError(this.getNode(), { request: options } as JsonObject, { message: 'Could not send message. Max. amount of rate-limit retries got reached.' });
|
||||
}
|
||||
|
||||
returnData.push({success: true});
|
||||
|
||||
@@ -21,6 +21,12 @@
|
||||
}
|
||||
]
|
||||
},
|
||||
"alias": [
|
||||
"Shell",
|
||||
"Command",
|
||||
"OS",
|
||||
"Bash"
|
||||
],
|
||||
"subcategories": {
|
||||
"Core Nodes": [
|
||||
"Helpers"
|
||||
|
||||
@@ -13,6 +13,9 @@
|
||||
}
|
||||
]
|
||||
},
|
||||
"alias": [
|
||||
"n8n"
|
||||
],
|
||||
"subcategories": {
|
||||
"Core Nodes": [
|
||||
"Helpers"
|
||||
|
||||
@@ -11,7 +11,7 @@ import {
|
||||
NodeApiError,
|
||||
} from 'n8n-workflow';
|
||||
|
||||
import * as uuid from 'uuid/v4';
|
||||
import { v4 as uuid } from 'uuid';
|
||||
|
||||
import {
|
||||
snakeCase,
|
||||
|
||||
@@ -64,6 +64,11 @@
|
||||
"icon": "⏲",
|
||||
"url": "https://n8n.io/blog/creating-triggers-for-n8n-workflows-using-polling/"
|
||||
},
|
||||
{
|
||||
"label": "How to build a low-code, self-hosted URL shortener in 3 steps",
|
||||
"icon": "🔗",
|
||||
"url": "https://n8n.io/blog/how-to-build-a-low-code-self-hosted-url-shortener/"
|
||||
},
|
||||
{
|
||||
"label": "Build your own virtual assistant with n8n: A step by step guide",
|
||||
"icon": "👦",
|
||||
@@ -101,6 +106,13 @@
|
||||
}
|
||||
]
|
||||
},
|
||||
"alias": [
|
||||
"Code",
|
||||
"Javascript",
|
||||
"Custom Code",
|
||||
"Script",
|
||||
"cpde"
|
||||
],
|
||||
"subcategories": {
|
||||
"Core Nodes": [
|
||||
"Data Transformation"
|
||||
|
||||
@@ -70,16 +70,15 @@ export async function ghostApiRequestAllItems(this: IHookFunctions | IExecuteFun
|
||||
|
||||
let responseData;
|
||||
|
||||
query.limit = 20;
|
||||
|
||||
let uri: string | undefined;
|
||||
query.limit = 50;
|
||||
query.page = 1;
|
||||
|
||||
do {
|
||||
responseData = await ghostApiRequest.call(this, method, endpoint, body, query, uri);
|
||||
uri = responseData.meta.pagination.next;
|
||||
responseData = await ghostApiRequest.call(this, method, endpoint, body, query);
|
||||
query.page = responseData.meta.pagination.next;
|
||||
returnData.push.apply(returnData, responseData[propertyName]);
|
||||
} while (
|
||||
responseData.meta.pagination.next !== null
|
||||
query.page !== null
|
||||
);
|
||||
return returnData;
|
||||
}
|
||||
|
||||
24
packages/nodes-base/nodes/Git/Git.node.json
Normal file
@@ -0,0 +1,24 @@
|
||||
{
|
||||
"node": "n8n-nodes-base.git",
|
||||
"nodeVersion": "1.0",
|
||||
"codexVersion": "1.0",
|
||||
"categories": [
|
||||
"Core Nodes",
|
||||
"Development"
|
||||
],
|
||||
"resources": {
|
||||
"credentialDocumentation": [
|
||||
{
|
||||
"url": "https://docs.n8n.io/credentials/git"
|
||||
}
|
||||
],
|
||||
"primaryDocumentation": [
|
||||
{
|
||||
"url": "https://docs.n8n.io/nodes/n8n-nodes-base.git/"
|
||||
}
|
||||
]
|
||||
},
|
||||
"subcategories": [
|
||||
"Helpers"
|
||||
]
|
||||
}
|
||||
437
packages/nodes-base/nodes/Git/Git.node.ts
Normal file
@@ -0,0 +1,437 @@
|
||||
import { IExecuteFunctions } from 'n8n-core';
|
||||
import {
|
||||
IDataObject,
|
||||
INodeExecutionData,
|
||||
INodeType,
|
||||
INodeTypeDescription,
|
||||
} from 'n8n-workflow';
|
||||
|
||||
import {
|
||||
addConfigFields,
|
||||
addFields,
|
||||
cloneFields,
|
||||
commitFields,
|
||||
logFields,
|
||||
pushFields,
|
||||
tagFields,
|
||||
} from './descriptions';
|
||||
|
||||
import simpleGit, {
|
||||
LogOptions,
|
||||
SimpleGit,
|
||||
SimpleGitOptions,
|
||||
} from 'simple-git';
|
||||
|
||||
import {
|
||||
access,
|
||||
mkdir,
|
||||
} from 'fs/promises';
|
||||
|
||||
import { URL } from 'url';
|
||||
|
||||
export class Git implements INodeType {
|
||||
description: INodeTypeDescription = {
|
||||
displayName: 'Git',
|
||||
name: 'git',
|
||||
icon: 'file:git.svg',
|
||||
group: ['transform'],
|
||||
version: 1,
|
||||
description: 'Control git.',
|
||||
defaults: {
|
||||
name: 'Git',
|
||||
color: '#808080',
|
||||
},
|
||||
inputs: ['main'],
|
||||
outputs: ['main'],
|
||||
credentials: [
|
||||
{
|
||||
name: 'gitPassword',
|
||||
required: true,
|
||||
displayOptions: {
|
||||
show: {
|
||||
authentication: [
|
||||
'gitPassword',
|
||||
],
|
||||
},
|
||||
},
|
||||
},
|
||||
],
|
||||
properties: [
|
||||
{
|
||||
displayName: 'Authentication',
|
||||
name: 'authentication',
|
||||
type: 'options',
|
||||
options: [
|
||||
{
|
||||
name: 'Authenticate',
|
||||
value: 'gitPassword',
|
||||
},
|
||||
{
|
||||
name: 'None',
|
||||
value: 'none',
|
||||
},
|
||||
],
|
||||
displayOptions: {
|
||||
show: {
|
||||
operation: [
|
||||
'clone',
|
||||
'push',
|
||||
],
|
||||
},
|
||||
},
|
||||
default: 'none',
|
||||
description: 'The way to authenticate.',
|
||||
},
|
||||
{
|
||||
displayName: 'Operation',
|
||||
name: 'operation',
|
||||
type: 'options',
|
||||
default: 'log',
|
||||
description: 'Operation to perform',
|
||||
options: [
|
||||
{
|
||||
name: 'Add',
|
||||
value: 'add',
|
||||
description: 'Add a file or folder to commit',
|
||||
},
|
||||
{
|
||||
name: 'Add Config',
|
||||
value: 'addConfig',
|
||||
description: 'Add configuration property',
|
||||
},
|
||||
{
|
||||
name: 'Clone',
|
||||
value: 'clone',
|
||||
description: 'Clone a repository',
|
||||
},
|
||||
{
|
||||
name: 'Commit',
|
||||
value: 'commit',
|
||||
description: 'Commit files or folders to git',
|
||||
},
|
||||
{
|
||||
name: 'Fetch',
|
||||
value: 'fetch',
|
||||
description: 'Fetch from remote repository',
|
||||
},
|
||||
{
|
||||
name: 'List Config',
|
||||
value: 'listConfig',
|
||||
description: 'Return current configuration',
|
||||
},
|
||||
{
|
||||
name: 'Log',
|
||||
value: 'log',
|
||||
description: 'Return git commit history',
|
||||
},
|
||||
{
|
||||
name: 'Pull',
|
||||
value: 'pull',
|
||||
description: 'Pull from remote repository',
|
||||
},
|
||||
{
|
||||
name: 'Push',
|
||||
value: 'push',
|
||||
description: 'Push to remote repository',
|
||||
},
|
||||
{
|
||||
name: 'Push Tags',
|
||||
value: 'pushTags',
|
||||
description: 'Push Tags to remote repository',
|
||||
},
|
||||
{
|
||||
name: 'Status',
|
||||
value: 'status',
|
||||
description: 'Return status of current repository',
|
||||
},
|
||||
{
|
||||
name: 'Tag',
|
||||
value: 'tag',
|
||||
description: 'Create a new tag',
|
||||
},
|
||||
{
|
||||
name: 'User Setup',
|
||||
value: 'userSetup',
|
||||
description: 'Set the user',
|
||||
},
|
||||
],
|
||||
},
|
||||
|
||||
{
|
||||
displayName: 'Repository Path',
|
||||
name: 'repositoryPath',
|
||||
type: 'string',
|
||||
displayOptions: {
|
||||
hide: {
|
||||
operation: [
|
||||
'clone',
|
||||
],
|
||||
},
|
||||
},
|
||||
default: '',
|
||||
placeholder: '/tmp/repository',
|
||||
required: true,
|
||||
description: 'Local path of the git repository to operate on.',
|
||||
},
|
||||
{
|
||||
displayName: 'New Repository Path',
|
||||
name: 'repositoryPath',
|
||||
type: 'string',
|
||||
displayOptions: {
|
||||
show: {
|
||||
operation: [
|
||||
'clone',
|
||||
],
|
||||
},
|
||||
},
|
||||
default: '',
|
||||
placeholder: '/tmp/repository',
|
||||
required: true,
|
||||
description: 'Local path to which the git repository should be cloned into.',
|
||||
},
|
||||
|
||||
...addFields,
|
||||
...addConfigFields,
|
||||
...cloneFields,
|
||||
...commitFields,
|
||||
...logFields,
|
||||
...pushFields,
|
||||
...tagFields,
|
||||
// ...userSetupFields,
|
||||
],
|
||||
};
|
||||
|
||||
|
||||
async execute(this: IExecuteFunctions): Promise<INodeExecutionData[][]> {
|
||||
const items = this.getInputData();
|
||||
|
||||
|
||||
const prepareRepository = (repositoryPath: string): string => {
|
||||
const authentication = this.getNodeParameter('authentication', 0) as string;
|
||||
|
||||
if (authentication === 'gitPassword') {
|
||||
const gitCredentials = this.getCredentials('gitPassword') as IDataObject;
|
||||
|
||||
const url = new URL(repositoryPath);
|
||||
url.username = gitCredentials.username as string;
|
||||
url.password = gitCredentials.password as string;
|
||||
|
||||
return url.toString();
|
||||
}
|
||||
|
||||
return repositoryPath;
|
||||
};
|
||||
|
||||
const operation = this.getNodeParameter('operation', 0) as string;
|
||||
let item: INodeExecutionData;
|
||||
const returnItems: INodeExecutionData[] = [];
|
||||
for (let itemIndex = 0; itemIndex < items.length; itemIndex++) {
|
||||
try {
|
||||
item = items[itemIndex];
|
||||
|
||||
const repositoryPath = this.getNodeParameter('repositoryPath', itemIndex, '') as string;
|
||||
const options = this.getNodeParameter('options', itemIndex, {}) as IDataObject;
|
||||
|
||||
if (operation === 'clone') {
|
||||
// Create repository folder if it does not exist
|
||||
try {
|
||||
await access(repositoryPath);
|
||||
} catch (error) {
|
||||
await mkdir(repositoryPath);
|
||||
}
|
||||
}
|
||||
|
||||
const gitOptions: Partial<SimpleGitOptions> = {
|
||||
baseDir: repositoryPath,
|
||||
};
|
||||
|
||||
const git: SimpleGit = simpleGit(gitOptions)
|
||||
// Tell git not to ask for any information via the terminal like for
|
||||
// example the username. As nobody will be able to answer it would
|
||||
// n8n keep on waiting forever.
|
||||
.env('GIT_TERMINAL_PROMPT', '0');
|
||||
|
||||
if (operation === 'add') {
|
||||
// ----------------------------------
|
||||
// add
|
||||
// ----------------------------------
|
||||
|
||||
const pathsToAdd = this.getNodeParameter('pathsToAdd', itemIndex, '') as string;
|
||||
|
||||
await git.add(pathsToAdd.split(','));
|
||||
|
||||
returnItems.push({ json: { success: true } });
|
||||
|
||||
} else if (operation === 'addConfig') {
|
||||
// ----------------------------------
|
||||
// addConfig
|
||||
// ----------------------------------
|
||||
|
||||
const key = this.getNodeParameter('key', itemIndex, '') as string;
|
||||
const value = this.getNodeParameter('value', itemIndex, '') as string;
|
||||
let append = false;
|
||||
|
||||
if (options.mode === 'append') {
|
||||
append = true;
|
||||
}
|
||||
|
||||
await git.addConfig(key, value, append);
|
||||
returnItems.push({ json: { success: true } });
|
||||
|
||||
} else if (operation === 'clone') {
|
||||
// ----------------------------------
|
||||
// clone
|
||||
// ----------------------------------
|
||||
|
||||
let sourceRepository = this.getNodeParameter('sourceRepository', itemIndex, '') as string;
|
||||
sourceRepository = prepareRepository(sourceRepository);
|
||||
|
||||
await git.clone(sourceRepository, '.');
|
||||
|
||||
returnItems.push({ json: { success: true } });
|
||||
|
||||
} else if (operation === 'commit') {
|
||||
// ----------------------------------
|
||||
// commit
|
||||
// ----------------------------------
|
||||
|
||||
const message = this.getNodeParameter('message', itemIndex, '') as string;
|
||||
|
||||
let pathsToAdd: string[] | undefined = undefined;
|
||||
if (options.files !== undefined) {
|
||||
pathsToAdd = (options.pathsToAdd as string).split(',');
|
||||
}
|
||||
|
||||
await git.commit(message, pathsToAdd);
|
||||
|
||||
returnItems.push({ json: { success: true } });
|
||||
|
||||
} else if (operation === 'fetch') {
|
||||
// ----------------------------------
|
||||
// fetch
|
||||
// ----------------------------------
|
||||
|
||||
await git.fetch();
|
||||
returnItems.push({ json: { success: true } });
|
||||
|
||||
} else if (operation === 'log') {
|
||||
// ----------------------------------
|
||||
// log
|
||||
// ----------------------------------
|
||||
|
||||
const logOptions: LogOptions = {};
|
||||
|
||||
const returnAll = this.getNodeParameter('returnAll', itemIndex, false) as boolean;
|
||||
if (returnAll === false) {
|
||||
logOptions.maxCount = this.getNodeParameter('limit', itemIndex, 100) as number;
|
||||
}
|
||||
if (options.file) {
|
||||
logOptions.file = options.file as string;
|
||||
}
|
||||
|
||||
const log = await git.log(logOptions);
|
||||
|
||||
// @ts-ignore
|
||||
returnItems.push(...this.helpers.returnJsonArray(log.all));
|
||||
|
||||
} else if (operation === 'pull') {
|
||||
// ----------------------------------
|
||||
// pull
|
||||
// ----------------------------------
|
||||
|
||||
await git.pull();
|
||||
returnItems.push({ json: { success: true } });
|
||||
|
||||
} else if (operation === 'push') {
|
||||
// ----------------------------------
|
||||
// push
|
||||
// ----------------------------------
|
||||
|
||||
if (options.repository) {
|
||||
const targetRepository = prepareRepository(options.targetRepository as string);
|
||||
await git.push(targetRepository);
|
||||
} else {
|
||||
const authentication = this.getNodeParameter('authentication', 0) as string;
|
||||
if (authentication === 'gitPassword') {
|
||||
// Try to get remote repository path from git repository itself to add
|
||||
// authentication data
|
||||
const config = await git.listConfig();
|
||||
let targetRepository;
|
||||
for (const fileName of Object.keys(config.values)) {
|
||||
if (config.values[fileName]['remote.origin.url']) {
|
||||
targetRepository = config.values[fileName]['remote.origin.url'];
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
targetRepository = prepareRepository(targetRepository as string);
|
||||
await git.push(targetRepository);
|
||||
} else {
|
||||
await git.push();
|
||||
}
|
||||
}
|
||||
|
||||
returnItems.push({ json: { success: true } });
|
||||
|
||||
} else if (operation === 'pushTags') {
|
||||
// ----------------------------------
|
||||
// pushTags
|
||||
// ----------------------------------
|
||||
|
||||
await git.pushTags();
|
||||
returnItems.push({ json: { success: true } });
|
||||
|
||||
} else if (operation === 'listConfig') {
|
||||
// ----------------------------------
|
||||
// listConfig
|
||||
// ----------------------------------
|
||||
|
||||
const config = await git.listConfig();
|
||||
|
||||
const data = [];
|
||||
for (const fileName of Object.keys(config.values)) {
|
||||
data.push({
|
||||
_file: fileName,
|
||||
...config.values[fileName],
|
||||
});
|
||||
}
|
||||
|
||||
// @ts-ignore
|
||||
returnItems.push(...this.helpers.returnJsonArray(data));
|
||||
|
||||
} else if (operation === 'status') {
|
||||
// ----------------------------------
|
||||
// status
|
||||
// ----------------------------------
|
||||
|
||||
const status = await git.status();
|
||||
|
||||
// @ts-ignore
|
||||
returnItems.push(...this.helpers.returnJsonArray([status]));
|
||||
|
||||
} else if (operation === 'tag') {
|
||||
// ----------------------------------
|
||||
// tag
|
||||
// ----------------------------------
|
||||
|
||||
const name = this.getNodeParameter('name', itemIndex, '') as string;
|
||||
|
||||
await git.addTag(name);
|
||||
returnItems.push({ json: { success: true } });
|
||||
|
||||
}
|
||||
|
||||
} catch (error) {
|
||||
|
||||
if (this.continueOnFail()) {
|
||||
returnItems.push({ json: { error: error.toString() } });
|
||||
continue;
|
||||
}
|
||||
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
|
||||
return this.prepareOutputData(returnItems);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,71 @@
|
||||
import {
|
||||
INodeProperties,
|
||||
} from 'n8n-workflow';
|
||||
|
||||
export const addConfigFields = [
|
||||
{
|
||||
displayName: 'Key',
|
||||
name: 'key',
|
||||
type: 'string',
|
||||
displayOptions: {
|
||||
show: {
|
||||
operation: [
|
||||
'addConfig',
|
||||
],
|
||||
},
|
||||
},
|
||||
default: '',
|
||||
placeholder: 'user.email',
|
||||
description: 'Name of the key to set.',
|
||||
required: true,
|
||||
},
|
||||
{
|
||||
displayName: 'Value',
|
||||
name: 'value',
|
||||
type: 'string',
|
||||
displayOptions: {
|
||||
show: {
|
||||
operation: [
|
||||
'addConfig',
|
||||
],
|
||||
},
|
||||
},
|
||||
default: '',
|
||||
placeholder: 'name@example.com',
|
||||
description: 'Value of the key to set.',
|
||||
required: true,
|
||||
},
|
||||
{
|
||||
displayName: 'Options',
|
||||
name: 'options',
|
||||
type: 'collection',
|
||||
displayOptions: {
|
||||
show: {
|
||||
operation: [
|
||||
'addConfig',
|
||||
],
|
||||
},
|
||||
},
|
||||
placeholder: 'Add Option',
|
||||
default: {},
|
||||
options: [
|
||||
{
|
||||
displayName: 'Mode',
|
||||
name: 'mode',
|
||||
type: 'options',
|
||||
options: [
|
||||
{
|
||||
name: 'Append',
|
||||
value: 'append',
|
||||
},
|
||||
{
|
||||
name: 'Set',
|
||||
value: 'set',
|
||||
},
|
||||
],
|
||||
default: 'set',
|
||||
description: 'Append setting rather than set it in the local config.',
|
||||
},
|
||||
],
|
||||
},
|
||||
] as INodeProperties[];
|
||||
22
packages/nodes-base/nodes/Git/descriptions/AddDescription.ts
Normal file
@@ -0,0 +1,22 @@
|
||||
import {
|
||||
INodeProperties,
|
||||
} from 'n8n-workflow';
|
||||
|
||||
export const addFields = [
|
||||
{
|
||||
displayName: 'Paths to Add',
|
||||
name: 'pathsToAdd',
|
||||
type: 'string',
|
||||
displayOptions: {
|
||||
show: {
|
||||
operation: [
|
||||
'add',
|
||||
],
|
||||
},
|
||||
},
|
||||
default: '',
|
||||
placeholder: 'README.md',
|
||||
description: 'Comma separated list of paths (absolute or relative to Repository Path) of files or folders to add.',
|
||||
required: true,
|
||||
},
|
||||
] as INodeProperties[];
|
||||
@@ -0,0 +1,22 @@
|
||||
import {
|
||||
INodeProperties,
|
||||
} from 'n8n-workflow';
|
||||
|
||||
export const cloneFields = [
|
||||
{
|
||||
displayName: 'Source Repository',
|
||||
name: 'sourceRepository',
|
||||
type: 'string',
|
||||
displayOptions: {
|
||||
show: {
|
||||
operation: [
|
||||
'clone',
|
||||
],
|
||||
},
|
||||
},
|
||||
default: '',
|
||||
placeholder: 'https://github.com/n8n-io/n8n',
|
||||
description: 'The URL or path of the repository to clone.',
|
||||
required: true,
|
||||
},
|
||||
] as INodeProperties[];
|
||||
@@ -0,0 +1,45 @@
|
||||
import {
|
||||
INodeProperties,
|
||||
} from 'n8n-workflow';
|
||||
|
||||
export const commitFields = [
|
||||
{
|
||||
displayName: 'Message',
|
||||
name: 'message',
|
||||
type: 'string',
|
||||
displayOptions: {
|
||||
show: {
|
||||
operation: [
|
||||
'commit',
|
||||
],
|
||||
},
|
||||
},
|
||||
default: '',
|
||||
description: 'The commit message to use.',
|
||||
},
|
||||
{
|
||||
displayName: 'Options',
|
||||
name: 'options',
|
||||
type: 'collection',
|
||||
displayOptions: {
|
||||
show: {
|
||||
operation: [
|
||||
'commit',
|
||||
],
|
||||
},
|
||||
},
|
||||
placeholder: 'Add Option',
|
||||
default: {},
|
||||
options: [
|
||||
{
|
||||
displayName: 'Paths to Add',
|
||||
name: 'pathsToAdd',
|
||||
type: 'string',
|
||||
default: '',
|
||||
placeholder: '/data/file1.json',
|
||||
description: `Comma separated list of paths (absolute or relative to Repository Path) of<br />
|
||||
files or folders to commit. If not set will all "added" files and folders be committed.`,
|
||||
},
|
||||
],
|
||||
},
|
||||
] as INodeProperties[];
|
||||
64
packages/nodes-base/nodes/Git/descriptions/LogDescription.ts
Normal file
@@ -0,0 +1,64 @@
|
||||
import {
|
||||
INodeProperties,
|
||||
} from 'n8n-workflow';
|
||||
|
||||
export const logFields = [
|
||||
{
|
||||
displayName: 'Return All',
|
||||
name: 'returnAll',
|
||||
type: 'boolean',
|
||||
displayOptions: {
|
||||
show: {
|
||||
operation: [
|
||||
'log',
|
||||
],
|
||||
},
|
||||
},
|
||||
default: false,
|
||||
description: 'If all results should be returned or only up to a given limit.',
|
||||
},
|
||||
{
|
||||
displayName: 'Limit',
|
||||
name: 'limit',
|
||||
type: 'number',
|
||||
displayOptions: {
|
||||
show: {
|
||||
operation: [
|
||||
'log',
|
||||
],
|
||||
returnAll: [
|
||||
false,
|
||||
],
|
||||
},
|
||||
},
|
||||
typeOptions: {
|
||||
minValue: 1,
|
||||
maxValue: 100,
|
||||
},
|
||||
default: 100,
|
||||
description: 'How many results to return.',
|
||||
},
|
||||
{
|
||||
displayName: 'Options',
|
||||
name: 'options',
|
||||
type: 'collection',
|
||||
displayOptions: {
|
||||
show: {
|
||||
operation: [
|
||||
'log',
|
||||
],
|
||||
},
|
||||
},
|
||||
placeholder: 'Add Option',
|
||||
default: {},
|
||||
options: [
|
||||
{
|
||||
displayName: 'File',
|
||||
name: 'file',
|
||||
type: 'string',
|
||||
default: 'README.md',
|
||||
description: 'The path (absolute or relative to Repository Path) of file or folder to get the history of.',
|
||||
},
|
||||
],
|
||||
},
|
||||
] as INodeProperties[];
|
||||
@@ -0,0 +1,30 @@
|
||||
import {
|
||||
INodeProperties,
|
||||
} from 'n8n-workflow';
|
||||
|
||||
export const pushFields = [
|
||||
{
|
||||
displayName: 'Options',
|
||||
name: 'options',
|
||||
type: 'collection',
|
||||
displayOptions: {
|
||||
show: {
|
||||
operation: [
|
||||
'push',
|
||||
],
|
||||
},
|
||||
},
|
||||
placeholder: 'Add Option',
|
||||
default: {},
|
||||
options: [
|
||||
{
|
||||
displayName: 'Target Repository',
|
||||
name: 'targetRepository',
|
||||
type: 'string',
|
||||
default: '',
|
||||
placeholder: 'https://github.com/n8n-io/n8n',
|
||||
description: 'The URL or path of the repository to push to.',
|
||||
},
|
||||
],
|
||||
},
|
||||
] as INodeProperties[];
|
||||
21
packages/nodes-base/nodes/Git/descriptions/TagDescription.ts
Normal file
@@ -0,0 +1,21 @@
|
||||
import {
|
||||
INodeProperties,
|
||||
} from 'n8n-workflow';
|
||||
|
||||
export const tagFields = [
|
||||
{
|
||||
displayName: 'Name',
|
||||
name: 'name',
|
||||
type: 'string',
|
||||
displayOptions: {
|
||||
show: {
|
||||
operation: [
|
||||
'tag',
|
||||
],
|
||||
},
|
||||
},
|
||||
default: '',
|
||||
description: 'The name of the tag to create.',
|
||||
required: true,
|
||||
},
|
||||
] as INodeProperties[];
|
||||
7
packages/nodes-base/nodes/Git/descriptions/index.ts
Normal file
@@ -0,0 +1,7 @@
|
||||
export * from './AddDescription';
|
||||
export * from './AddConfigDescription';
|
||||
export * from './CloneDescription';
|
||||
export * from './CommitDescription';
|
||||
export * from './LogDescription';
|
||||
export * from './PushDescription';
|
||||
export * from './TagDescription';
|
||||
1
packages/nodes-base/nodes/Git/git.svg
Normal file
@@ -0,0 +1 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="97" height="97"><path fill="#F05133" d="M92.71 44.408L52.591 4.291a5.918 5.918 0 00-8.369 0l-8.33 8.332L46.459 23.19a7.022 7.022 0 017.229 1.685 7.03 7.03 0 011.67 7.275l10.186 10.185a7.028 7.028 0 017.275 1.671 7.043 7.043 0 01-9.961 9.958 7.043 7.043 0 01-1.531-7.658l-9.5-9.499v24.997a7.042 7.042 0 11-8.096 11.291 7.042 7.042 0 012.307-11.496v-25.23a7.041 7.041 0 01-3.823-9.235L31.798 16.715 4.288 44.222a5.92 5.92 0 000 8.371l40.121 40.118a5.918 5.918 0 008.369 0L92.71 52.779a5.92 5.92 0 000-8.371z"/></svg>
|
||||
|
After Width: | Height: | Size: 561 B |
@@ -70,6 +70,10 @@ export async function googleApiRequestAllItems(this: IExecuteFunctions | ILoadOp
|
||||
export function simplify(responseData: any | [any]) { // tslint:disable-line:no-any
|
||||
const response = [];
|
||||
for (const { columnHeader: { dimensions }, data: { rows } } of responseData) {
|
||||
if (rows === undefined) {
|
||||
// Do not error if there is no data
|
||||
continue;
|
||||
}
|
||||
for (const row of rows) {
|
||||
const data: IDataObject = {};
|
||||
if (dimensions) {
|
||||
|
||||
@@ -112,6 +112,15 @@ export class GoogleAnalytics implements INodeType {
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
returnData.sort((a, b) => {
|
||||
const aName= a.name.toLowerCase();
|
||||
const bName= b.name.toLowerCase();
|
||||
if (aName < bName) { return -1; }
|
||||
if (aName > bName) { return 1; }
|
||||
return 0;
|
||||
});
|
||||
|
||||
return returnData;
|
||||
},
|
||||
// Get all the views to display them to user so that he can
|
||||
@@ -203,6 +212,14 @@ export class GoogleAnalytics implements INodeType {
|
||||
body.dimensions = dimensions;
|
||||
}
|
||||
}
|
||||
if (additionalFields.dimensionFiltersUi) {
|
||||
const dimensionFilters = (additionalFields.dimensionFiltersUi as IDataObject).filterValues as IDataObject[];
|
||||
if (dimensionFilters) {
|
||||
dimensionFilters.forEach(filter => filter.expressions = [filter.expressions]);
|
||||
body.dimensionFilterClauses = { filters: dimensionFilters };
|
||||
}
|
||||
}
|
||||
|
||||
if (additionalFields.includeEmptyRows) {
|
||||
Object.assign(body, { includeEmptyRows: additionalFields.includeEmptyRows });
|
||||
}
|
||||
|
||||
@@ -1,6 +1,9 @@
|
||||
export interface IData {
|
||||
viewId: string;
|
||||
dimensions?: IDimension[];
|
||||
dimensionFilterClauses?: {
|
||||
filters: IDimensionFilter[];
|
||||
};
|
||||
pageSize?: number;
|
||||
metrics?: IMetric[];
|
||||
}
|
||||
@@ -10,6 +13,11 @@ export interface IDimension {
|
||||
histogramBuckets?: string[];
|
||||
}
|
||||
|
||||
export interface IDimensionFilter {
|
||||
dimensionName?: string;
|
||||
operator?: string;
|
||||
expressions?: string[];
|
||||
}
|
||||
export interface IMetric {
|
||||
expression?: string;
|
||||
alias?: string;
|
||||
|
||||
@@ -183,6 +183,85 @@ export const reportFields = [
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
displayName: 'Dimension Filters',
|
||||
name: 'dimensionFiltersUi',
|
||||
type: 'fixedCollection',
|
||||
default: {},
|
||||
typeOptions: {
|
||||
multipleValues: true,
|
||||
},
|
||||
placeholder: 'Add Dimension Filter',
|
||||
description: 'Dimension Filters in the request',
|
||||
options: [
|
||||
{
|
||||
displayName: 'Filters',
|
||||
name: 'filterValues',
|
||||
values: [
|
||||
{
|
||||
displayName: 'Dimension Name',
|
||||
name: 'dimensionName',
|
||||
type: 'options',
|
||||
typeOptions: {
|
||||
loadOptionsMethod: 'getDimensions',
|
||||
},
|
||||
default: '',
|
||||
description: 'Name of the dimension to filter by.',
|
||||
},
|
||||
// https://developers.google.com/analytics/devguides/reporting/core/v4/rest/v4/reports/batchGet#Operator
|
||||
{
|
||||
displayName: 'Operator',
|
||||
name: 'operator',
|
||||
type: 'options',
|
||||
default: 'EXACT',
|
||||
description: 'Operator to use in combination with value.',
|
||||
options: [
|
||||
{
|
||||
name: 'Begins With',
|
||||
value: 'BEGINS_WITH',
|
||||
},
|
||||
{
|
||||
name: 'Ends With',
|
||||
value: 'ENDS_WITH',
|
||||
},
|
||||
{
|
||||
name: 'Exact',
|
||||
value: 'EXACT',
|
||||
},
|
||||
{
|
||||
name: 'Greater Than (number)',
|
||||
value: 'NUMERIC_GREATER_THAN',
|
||||
},
|
||||
{
|
||||
name: 'Partial',
|
||||
value: 'PARTIAL',
|
||||
},
|
||||
{
|
||||
name: 'Regular Expression',
|
||||
value: 'REGEXP',
|
||||
},
|
||||
{
|
||||
name: 'Equal (number)',
|
||||
value: 'NUMERIC_EQUAL',
|
||||
},
|
||||
{
|
||||
name: 'Less Than (number)',
|
||||
value: 'NUMERIC_LESS_THAN',
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
displayName: 'Value',
|
||||
name: 'expressions',
|
||||
type: 'string',
|
||||
default: '',
|
||||
placeholder: 'ga:newUsers',
|
||||
description: `String or <a href="https://support.google.com/analytics/answer/1034324?hl=en" target="_blank">regular expression</a> to match against.`,
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
displayName: 'Hide Totals',
|
||||
name: 'hideTotals',
|
||||
|
||||
@@ -22,7 +22,7 @@ import {
|
||||
recordOperations,
|
||||
} from './RecordDescription';
|
||||
|
||||
import * as uuid from 'uuid';
|
||||
import { v4 as uuid } from 'uuid';
|
||||
|
||||
export class GoogleBigQuery implements INodeType {
|
||||
description: INodeTypeDescription = {
|
||||
|
||||
@@ -53,6 +53,10 @@ export async function googleApiRequest(this: IExecuteFunctions | IExecuteSingleF
|
||||
return await this.helpers.requestOAuth2.call(this, 'googleBooksOAuth2Api', options);
|
||||
}
|
||||
} catch (error) {
|
||||
if (error.code === 'ERR_OSSL_PEM_NO_START_LINE') {
|
||||
error.statusCode = '401';
|
||||
}
|
||||
|
||||
throw new NodeApiError(this.getNode(), error);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -34,7 +34,7 @@ import {
|
||||
|
||||
import * as moment from 'moment-timezone';
|
||||
|
||||
import * as uuid from 'uuid/v4';
|
||||
import { v4 as uuid } from 'uuid';
|
||||
|
||||
export class GoogleCalendar implements INodeType {
|
||||
description: INodeTypeDescription = {
|
||||
|
||||
@@ -22,6 +22,11 @@
|
||||
"icon": "⚙️",
|
||||
"url": "https://n8n.io/blog/automate-your-data-processing-pipeline-in-9-steps-with-n8n/"
|
||||
},
|
||||
{
|
||||
"label": "5 tasks you can automate with the new Notion API ",
|
||||
"icon": "⚡️",
|
||||
"url": "https://n8n.io/blog/5-tasks-you-can-automate-with-notion-api/"
|
||||
},
|
||||
{
|
||||
"label": "15 Google apps you can combine and automate to increase productivity",
|
||||
"icon": "💡",
|
||||
|
||||
1212
packages/nodes-base/nodes/Google/Docs/DocumentDescription.ts
Normal file
142
packages/nodes-base/nodes/Google/Docs/GenericFunctions.ts
Normal file
@@ -0,0 +1,142 @@
|
||||
import {
|
||||
OptionsWithUri,
|
||||
} from 'request';
|
||||
|
||||
import {
|
||||
IExecuteFunctions,
|
||||
ILoadOptionsFunctions,
|
||||
} from 'n8n-core';
|
||||
|
||||
import {
|
||||
IDataObject,
|
||||
NodeApiError,
|
||||
NodeOperationError,
|
||||
} from 'n8n-workflow';
|
||||
|
||||
import * as moment from 'moment-timezone';
|
||||
|
||||
import * as jwt from 'jsonwebtoken';
|
||||
|
||||
export async function googleApiRequest(
|
||||
this: IExecuteFunctions | ILoadOptionsFunctions,
|
||||
method: string,
|
||||
endpoint: string,
|
||||
body: IDataObject = {},
|
||||
qs?: IDataObject,
|
||||
uri?: string,
|
||||
) {
|
||||
const authenticationMethod = this.getNodeParameter('authentication', 0, 'serviceAccount') as string;
|
||||
|
||||
const options: OptionsWithUri = {
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
},
|
||||
method,
|
||||
body,
|
||||
qs,
|
||||
uri: uri || `https://docs.googleapis.com/v1${endpoint}`,
|
||||
json: true,
|
||||
};
|
||||
|
||||
if (!Object.keys(body).length) {
|
||||
delete options.body;
|
||||
}
|
||||
try {
|
||||
|
||||
if (authenticationMethod === 'serviceAccount') {
|
||||
const credentials = this.getCredentials('googleApi');
|
||||
|
||||
if (credentials === undefined) {
|
||||
throw new NodeOperationError(this.getNode(), 'No credentials got returned!');
|
||||
}
|
||||
|
||||
const { access_token } = await getAccessToken.call(this, credentials as IDataObject);
|
||||
|
||||
options.headers!.Authorization = `Bearer ${access_token}`;
|
||||
return await this.helpers.request!(options);
|
||||
} else {
|
||||
//@ts-ignore
|
||||
return await this.helpers.requestOAuth2.call(this, 'googleDocsOAuth2Api', options);
|
||||
}
|
||||
} catch (error) {
|
||||
throw new NodeApiError(this.getNode(), error);
|
||||
}
|
||||
}
|
||||
|
||||
export async function googleApiRequestAllItems(this: IExecuteFunctions | ILoadOptionsFunctions, propertyName: string, method: string, endpoint: string, body: IDataObject = {}, qs?: IDataObject, uri?: string): Promise<any> { // tslint:disable-line:no-any
|
||||
|
||||
const returnData: IDataObject[] = [];
|
||||
|
||||
let responseData;
|
||||
const query: IDataObject = { ...qs };
|
||||
query.maxResults = 100;
|
||||
query.pageSize = 100;
|
||||
|
||||
do {
|
||||
responseData = await googleApiRequest.call(this, method, endpoint, body, query, uri);
|
||||
query.pageToken = responseData['nextPageToken'];
|
||||
returnData.push.apply(returnData, responseData[propertyName]);
|
||||
} while (
|
||||
responseData['nextPageToken'] !== undefined &&
|
||||
responseData['nextPageToken'] !== ''
|
||||
);
|
||||
|
||||
return returnData;
|
||||
}
|
||||
|
||||
function getAccessToken(this: IExecuteFunctions | ILoadOptionsFunctions, credentials: IDataObject): Promise<IDataObject> {
|
||||
//https://developers.google.com/identity/protocols/oauth2/service-account#httprest
|
||||
|
||||
const scopes = [
|
||||
'https://www.googleapis.com/auth/documents',
|
||||
'https://www.googleapis.com/auth/drive',
|
||||
'https://www.googleapis.com/auth/drive.file',
|
||||
];
|
||||
|
||||
const now = moment().unix();
|
||||
|
||||
const signature = jwt.sign(
|
||||
{
|
||||
'iss': credentials.email as string,
|
||||
'sub': credentials.delegatedEmail || credentials.email as string,
|
||||
'scope': scopes.join(' '),
|
||||
'aud': `https://oauth2.googleapis.com/token`,
|
||||
'iat': now,
|
||||
'exp': now + 3600,
|
||||
},
|
||||
credentials.privateKey as string,
|
||||
{
|
||||
algorithm: 'RS256',
|
||||
header: {
|
||||
'kid': credentials.privateKey as string,
|
||||
'typ': 'JWT',
|
||||
'alg': 'RS256',
|
||||
},
|
||||
},
|
||||
);
|
||||
|
||||
const options: OptionsWithUri = {
|
||||
headers: {
|
||||
'Content-Type': 'application/x-www-form-urlencoded',
|
||||
},
|
||||
method: 'POST',
|
||||
form: {
|
||||
grant_type: 'urn:ietf:params:oauth:grant-type:jwt-bearer',
|
||||
assertion: signature,
|
||||
},
|
||||
uri: 'https://oauth2.googleapis.com/token',
|
||||
json: true,
|
||||
};
|
||||
|
||||
return this.helpers.request!(options);
|
||||
}
|
||||
|
||||
export const hasKeys = (obj = {}) => Object.keys(obj).length > 0;
|
||||
export const extractID = (url: string) => {
|
||||
const regex = new RegExp('https://docs.google.com/document/d/([a-zA-Z0-9-_]+)/');
|
||||
const results = regex.exec(url);
|
||||
return results ? results[1] : undefined;
|
||||
};
|
||||
export const upperFirst = (str: string) => {
|
||||
return str[0].toUpperCase() + str.substr(1);
|
||||
};
|
||||
20
packages/nodes-base/nodes/Google/Docs/GoogleDocs.node.json
Normal file
@@ -0,0 +1,20 @@
|
||||
{
|
||||
"node": "n8n-nodes-base.googleDocs",
|
||||
"nodeVersion": "1.0",
|
||||
"codexVersion": "1.0",
|
||||
"categories": [
|
||||
"Miscellaneous"
|
||||
],
|
||||
"resources": {
|
||||
"credentialDocumentation": [
|
||||
{
|
||||
"url": "https://docs.n8n.io/credentials/google"
|
||||
}
|
||||
],
|
||||
"primaryDocumentation": [
|
||||
{
|
||||
"url": "https://docs.n8n.io/nodes/n8n-nodes-base.googleDocs/"
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
461
packages/nodes-base/nodes/Google/Docs/GoogleDocs.node.ts
Normal file
@@ -0,0 +1,461 @@
|
||||
import {
|
||||
IExecuteFunctions,
|
||||
ILoadOptionsFunctions,
|
||||
} from 'n8n-core';
|
||||
|
||||
import {
|
||||
IDataObject,
|
||||
INodeExecutionData,
|
||||
INodePropertyOptions,
|
||||
INodeType,
|
||||
INodeTypeDescription,
|
||||
NodeApiError,
|
||||
} from 'n8n-workflow';
|
||||
|
||||
import {
|
||||
extractID,
|
||||
googleApiRequest,
|
||||
googleApiRequestAllItems,
|
||||
hasKeys,
|
||||
upperFirst,
|
||||
} from './GenericFunctions';
|
||||
|
||||
import {
|
||||
documentFields,
|
||||
documentOperations,
|
||||
} from './DocumentDescription';
|
||||
|
||||
import {
|
||||
IUpdateBody,
|
||||
IUpdateFields,
|
||||
} from './interfaces';
|
||||
|
||||
export class GoogleDocs implements INodeType {
|
||||
description: INodeTypeDescription = {
|
||||
displayName: 'Google Docs',
|
||||
name: 'googleDocs',
|
||||
icon: 'file:googleDocs.svg',
|
||||
group: ['input'],
|
||||
version: 1,
|
||||
subtitle: '={{$parameter["operation"] + ": " + $parameter["resource"]}}',
|
||||
description: 'Consume Google Docs API.',
|
||||
defaults: {
|
||||
name: 'Google Docs',
|
||||
color: '#1a73e8',
|
||||
},
|
||||
inputs: ['main'],
|
||||
outputs: ['main'],
|
||||
credentials: [
|
||||
{
|
||||
name: 'googleApi',
|
||||
required: true,
|
||||
displayOptions: {
|
||||
show: {
|
||||
authentication: [
|
||||
'serviceAccount',
|
||||
],
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: 'googleDocsOAuth2Api',
|
||||
required: true,
|
||||
displayOptions: {
|
||||
show: {
|
||||
authentication: [
|
||||
'oAuth2',
|
||||
],
|
||||
},
|
||||
},
|
||||
},
|
||||
],
|
||||
properties: [
|
||||
{
|
||||
displayName: 'Authentication',
|
||||
name: 'authentication',
|
||||
type: 'options',
|
||||
options: [
|
||||
{
|
||||
name: 'Service Account',
|
||||
value: 'serviceAccount',
|
||||
},
|
||||
{
|
||||
name: 'OAuth2',
|
||||
value: 'oAuth2',
|
||||
},
|
||||
],
|
||||
default: 'serviceAccount',
|
||||
},
|
||||
{
|
||||
displayName: 'Resource',
|
||||
name: 'resource',
|
||||
type: 'options',
|
||||
options: [
|
||||
{
|
||||
name: 'Document',
|
||||
value: 'document',
|
||||
},
|
||||
],
|
||||
default: 'document',
|
||||
description: 'The resource to operate on.',
|
||||
},
|
||||
...documentOperations,
|
||||
...documentFields,
|
||||
],
|
||||
};
|
||||
methods = {
|
||||
loadOptions: {
|
||||
// Get all the drives to display them to user so that he can
|
||||
// select them easily
|
||||
async getDrives(this: ILoadOptionsFunctions): Promise<INodePropertyOptions[]> {
|
||||
const returnData: INodePropertyOptions[] = [
|
||||
{
|
||||
name: 'My Drive',
|
||||
value: 'myDrive',
|
||||
},
|
||||
{
|
||||
name: 'Shared with me',
|
||||
value: 'sharedWithMe',
|
||||
},
|
||||
];
|
||||
let drives;
|
||||
try {
|
||||
drives = await googleApiRequestAllItems.call(this, 'drives', 'GET', '', {}, {}, 'https://www.googleapis.com/drive/v3/drives');
|
||||
} catch (error) {
|
||||
throw new NodeApiError(this.getNode(), error, { message: 'Error in loading Drives' });
|
||||
}
|
||||
|
||||
for (const drive of drives) {
|
||||
returnData.push({
|
||||
name: drive.name as string,
|
||||
value: drive.id as string,
|
||||
});
|
||||
}
|
||||
return returnData;
|
||||
},
|
||||
async getFolders(this: ILoadOptionsFunctions): Promise<INodePropertyOptions[]> {
|
||||
const returnData: INodePropertyOptions[] = [
|
||||
{
|
||||
name: '/',
|
||||
value: 'default',
|
||||
},
|
||||
];
|
||||
const driveId = this.getNodeParameter('driveId');
|
||||
|
||||
const qs = {
|
||||
q: `mimeType = \'application/vnd.google-apps.folder\' ${driveId === 'sharedWithMe' ? 'and sharedWithMe = true' : ' and \'root\' in parents'}`,
|
||||
...(driveId && driveId !== 'myDrive' && driveId !== 'sharedWithMe') ? { driveId } : {},
|
||||
};
|
||||
let folders;
|
||||
|
||||
try {
|
||||
folders = await googleApiRequestAllItems.call(this, 'files', 'GET', '', {}, qs, 'https://www.googleapis.com/drive/v3/files');
|
||||
} catch (error) {
|
||||
throw new NodeApiError(this.getNode(), error, { message: 'Error in loading Folders' });
|
||||
}
|
||||
|
||||
for (const folder of folders) {
|
||||
returnData.push({
|
||||
name: folder.name as string,
|
||||
value: folder.id as string,
|
||||
});
|
||||
}
|
||||
return returnData;
|
||||
},
|
||||
},
|
||||
};
|
||||
async execute(this: IExecuteFunctions): Promise<INodeExecutionData[][]> {
|
||||
const items = this.getInputData();
|
||||
const returnData: IDataObject[] = [];
|
||||
const length = items.length;
|
||||
|
||||
let responseData;
|
||||
|
||||
const resource = this.getNodeParameter('resource', 0) as string;
|
||||
const operation = this.getNodeParameter('operation', 0) as string;
|
||||
|
||||
for (let i = 0; i < length; i++) {
|
||||
|
||||
try {
|
||||
|
||||
if (resource === 'document') {
|
||||
|
||||
if (operation === 'create') {
|
||||
|
||||
// https://developers.google.com/docs/api/reference/rest/v1/documents/create
|
||||
|
||||
const folderId = this.getNodeParameter('folderId', i) as string;
|
||||
|
||||
const body: IDataObject = {
|
||||
name: this.getNodeParameter('title', i) as string,
|
||||
mimeType: 'application/vnd.google-apps.document',
|
||||
...(folderId && folderId !== 'default') ? { parents: [folderId] } : {},
|
||||
};
|
||||
|
||||
responseData = await googleApiRequest.call(this, 'POST', '', body, {}, 'https://www.googleapis.com/drive/v3/files');
|
||||
|
||||
} else if (operation === 'get') {
|
||||
|
||||
// https://developers.google.com/docs/api/reference/rest/v1/documents/get
|
||||
|
||||
const documentURL = this.getNodeParameter('documentURL', i) as string;
|
||||
const simple = this.getNodeParameter('simple', i) as boolean;
|
||||
let documentId = extractID(documentURL);
|
||||
|
||||
if (!documentId) {
|
||||
documentId = documentURL;
|
||||
}
|
||||
responseData = await googleApiRequest.call(this, 'GET', `/documents/${documentId}`);
|
||||
if (simple) {
|
||||
|
||||
const content = (responseData.body.content as IDataObject[])
|
||||
.reduce((arr: string[], contentItem) => {
|
||||
if (contentItem && contentItem.paragraph) {
|
||||
const texts = ((contentItem.paragraph as IDataObject).elements as IDataObject[])
|
||||
.map(element => {
|
||||
if (element && element.textRun) {
|
||||
return (element.textRun as IDataObject).content as string;
|
||||
}
|
||||
}) as string[];
|
||||
arr = [...arr, ...texts];
|
||||
}
|
||||
return arr;
|
||||
}, [])
|
||||
.join('');
|
||||
|
||||
responseData = {
|
||||
documentId,
|
||||
content,
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
} else if (operation === 'update') {
|
||||
|
||||
// https://developers.google.com/docs/api/reference/rest/v1/documents/batchUpdate
|
||||
|
||||
const documentURL = this.getNodeParameter('documentURL', i) as string;
|
||||
let documentId = extractID(documentURL);
|
||||
const simple = this.getNodeParameter('simple', i) as boolean;
|
||||
const actionsUi = this.getNodeParameter('actionsUi', i) as {
|
||||
actionFields: IDataObject[]
|
||||
};
|
||||
const { writeControlObject } = this.getNodeParameter('updateFields', i) as IUpdateFields;
|
||||
|
||||
if (!documentId) {
|
||||
documentId = documentURL;
|
||||
}
|
||||
|
||||
const body = {
|
||||
requests: [],
|
||||
} as IUpdateBody;
|
||||
|
||||
if (hasKeys(writeControlObject)) {
|
||||
const { control, value } = writeControlObject;
|
||||
body.writeControl = {
|
||||
[control]: value,
|
||||
};
|
||||
}
|
||||
|
||||
if (actionsUi) {
|
||||
|
||||
let requestBody: IDataObject;
|
||||
actionsUi.actionFields.forEach(actionField => {
|
||||
const { action, object } = actionField;
|
||||
if (object === 'positionedObject') {
|
||||
if (action === 'delete') {
|
||||
requestBody = {
|
||||
objectId: actionField.objectId,
|
||||
};
|
||||
}
|
||||
|
||||
} else if (object === 'pageBreak') {
|
||||
|
||||
if (action === 'insert') {
|
||||
const { insertSegment, segmentId, locationChoice, index } = actionField;
|
||||
requestBody = {
|
||||
[locationChoice as string]: {
|
||||
segmentId: (insertSegment !== 'body') ? segmentId : '',
|
||||
...(locationChoice === 'location') ? { index } : {},
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
} else if (object === 'table') {
|
||||
|
||||
if (action === 'insert') {
|
||||
const { rows, columns, insertSegment, locationChoice, segmentId, index } = actionField;
|
||||
requestBody = {
|
||||
rows,
|
||||
columns,
|
||||
[locationChoice as string]: {
|
||||
segmentId: (insertSegment !== 'body') ? segmentId : '',
|
||||
...(locationChoice === 'location') ? { index } : {},
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
} else if (object === 'footer') {
|
||||
|
||||
if (action === 'create') {
|
||||
const { insertSegment, locationChoice, segmentId, index } = actionField;
|
||||
requestBody = {
|
||||
type: 'DEFAULT',
|
||||
sectionBreakLocation: {
|
||||
segmentId: (insertSegment !== 'body') ? segmentId : '',
|
||||
...(locationChoice === 'location') ? { index } : {},
|
||||
},
|
||||
};
|
||||
} else if (action === 'delete') {
|
||||
requestBody = {
|
||||
footerId: actionField.footerId,
|
||||
};
|
||||
}
|
||||
|
||||
} else if (object === 'header') {
|
||||
|
||||
if (action === 'create') {
|
||||
const { insertSegment, locationChoice, segmentId, index } = actionField;
|
||||
requestBody = {
|
||||
type: 'DEFAULT',
|
||||
sectionBreakLocation: {
|
||||
segmentId: (insertSegment !== 'body') ? segmentId : '',
|
||||
...(locationChoice === 'location') ? { index } : {},
|
||||
},
|
||||
};
|
||||
} else if (action === 'delete') {
|
||||
requestBody = {
|
||||
headerId: actionField.headerId,
|
||||
};
|
||||
}
|
||||
|
||||
} else if (object === 'tableColumn') {
|
||||
|
||||
if (action === 'insert') {
|
||||
const { insertPosition, rowIndex, columnIndex, insertSegment, segmentId, index } = actionField;
|
||||
requestBody = {
|
||||
insertRight: insertPosition,
|
||||
tableCellLocation: {
|
||||
rowIndex,
|
||||
columnIndex,
|
||||
tableStartLocation: { segmentId: (insertSegment !== 'body') ? segmentId : '', index, },
|
||||
},
|
||||
};
|
||||
} else if (action === 'delete') {
|
||||
const { rowIndex, columnIndex, insertSegment, segmentId, index } = actionField;
|
||||
requestBody = {
|
||||
tableCellLocation: {
|
||||
rowIndex,
|
||||
columnIndex,
|
||||
tableStartLocation: { segmentId: (insertSegment !== 'body') ? segmentId : '', index, },
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
} else if (object === 'tableRow') {
|
||||
|
||||
if (action === 'insert') {
|
||||
const { insertPosition, rowIndex, columnIndex, insertSegment, segmentId, index } = actionField;
|
||||
requestBody = {
|
||||
insertBelow: insertPosition,
|
||||
tableCellLocation: {
|
||||
rowIndex,
|
||||
columnIndex,
|
||||
tableStartLocation: { segmentId: (insertSegment !== 'body') ? segmentId : '', index, },
|
||||
},
|
||||
};
|
||||
} else if (action === 'delete') {
|
||||
const { rowIndex, columnIndex, insertSegment, segmentId, index } = actionField;
|
||||
requestBody = {
|
||||
tableCellLocation: {
|
||||
rowIndex,
|
||||
columnIndex,
|
||||
tableStartLocation: { segmentId: (insertSegment !== 'body') ? segmentId : '', index, },
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
} else if (object === 'text') {
|
||||
|
||||
if (action === 'insert') {
|
||||
const { text, locationChoice, insertSegment, segmentId, index } = actionField;
|
||||
requestBody = {
|
||||
text,
|
||||
[locationChoice as string]: {
|
||||
segmentId: (insertSegment !== 'body') ? segmentId : '',
|
||||
...(locationChoice === 'location') ? { index } : {},
|
||||
},
|
||||
};
|
||||
} else if (action === 'replaceAll') {
|
||||
const { text, replaceText, matchCase } = actionField;
|
||||
requestBody = {
|
||||
replaceText,
|
||||
containsText: { text, matchCase },
|
||||
};
|
||||
}
|
||||
|
||||
} else if (object === 'paragraphBullets') {
|
||||
if (action === 'create') {
|
||||
const { bulletPreset, startIndex, insertSegment, segmentId, endIndex } = actionField;
|
||||
requestBody = {
|
||||
bulletPreset,
|
||||
range: { segmentId: (insertSegment !== 'body') ? segmentId : '', startIndex, endIndex },
|
||||
};
|
||||
} else if (action === 'delete') {
|
||||
const { startIndex, insertSegment, segmentId, endIndex } = actionField;
|
||||
requestBody = {
|
||||
range: { segmentId: (insertSegment !== 'body') ? segmentId : '', startIndex, endIndex },
|
||||
};
|
||||
}
|
||||
} else if (object === 'namedRange') {
|
||||
if (action === 'create') {
|
||||
const { name, insertSegment, segmentId, startIndex, endIndex } = actionField;
|
||||
requestBody = {
|
||||
name,
|
||||
range: { segmentId: (insertSegment !== 'body') ? segmentId : '', startIndex, endIndex },
|
||||
};
|
||||
} else if (action === 'delete') {
|
||||
const { namedRangeReference, value } = actionField;
|
||||
requestBody = {
|
||||
[namedRangeReference as string]: value,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
body.requests.push({
|
||||
[`${action}${upperFirst(object as string)}`]: requestBody,
|
||||
});
|
||||
|
||||
});
|
||||
}
|
||||
|
||||
responseData = await googleApiRequest.call(this, 'POST', `/documents/${documentId}:batchUpdate`, body);
|
||||
|
||||
if (simple === true) {
|
||||
if (Object.keys(responseData.replies[0]).length !== 0) {
|
||||
const key = Object.keys(responseData.replies[0])[0];
|
||||
responseData = responseData.replies[0][key];
|
||||
} else {
|
||||
responseData = {};
|
||||
}
|
||||
}
|
||||
responseData.documentId = documentId;
|
||||
}
|
||||
}
|
||||
|
||||
} catch (error) {
|
||||
if (this.continueOnFail()) {
|
||||
returnData.push({ error: error.message });
|
||||
continue;
|
||||
}
|
||||
throw error;
|
||||
}
|
||||
|
||||
Array.isArray(responseData)
|
||||
? returnData.push(...responseData)
|
||||
: returnData.push(responseData);
|
||||
}
|
||||
|
||||
return [this.helpers.returnJsonArray(returnData)];
|
||||
}
|
||||
}
|
||||
1
packages/nodes-base/nodes/Google/Docs/googleDocs.svg
Normal file
@@ -0,0 +1 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" viewBox="-18 0 90 80" fill="#fff" fill-rule="evenodd" stroke="#000" stroke-linecap="round" stroke-linejoin="round"><use xlink:href="#A" x=".5" y=".5"/><symbol id="A" overflow="visible"><g stroke="none"><path fill="#548df6" d="M36 0l22 22v53a4.99 4.99 0 0 1-5 5H5a4.99 4.99 0 0 1-5-5V5a4.99 4.99 0 0 1 5-5z"/><path d="M14 40h30v3H14zm0 7h30v3H14zm0 8h30v3H14zm0 7h21v3H14z"/><path fill="#abd0fb" d="M36 0l22 22H41c-2.77 0-5-2.48-5-5.25z"/><path fill="#3e5bb9" d="M40.75 22L58 29.125V22z"/></g></symbol></svg>
|
||||
|
After Width: | Height: | Size: 591 B |
13
packages/nodes-base/nodes/Google/Docs/interfaces.d.ts
vendored
Normal file
@@ -0,0 +1,13 @@
|
||||
import { IDataObject } from 'n8n-workflow';
|
||||
|
||||
export interface IUpdateBody extends IDataObject {
|
||||
requests: IDataObject[];
|
||||
writeControl?: { [key: string]: string };
|
||||
}
|
||||
|
||||
export interface IUpdateFields {
|
||||
writeControlObject: {
|
||||
control: string,
|
||||
value: string,
|
||||
};
|
||||
}
|
||||
@@ -51,6 +51,10 @@ export async function googleApiRequest(this: IExecuteFunctions | IExecuteSingleF
|
||||
return await this.helpers.requestOAuth2.call(this, 'googleDriveOAuth2Api', options);
|
||||
}
|
||||
} catch (error) {
|
||||
if (error.code === 'ERR_OSSL_PEM_NO_START_LINE') {
|
||||
error.statusCode = '401';
|
||||
}
|
||||
|
||||
throw new NodeApiError(this.getNode(), error);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -16,7 +16,7 @@ import {
|
||||
googleApiRequestAllItems,
|
||||
} from './GenericFunctions';
|
||||
|
||||
import uuid = require('uuid');
|
||||
import { v4 as uuid } from 'uuid';
|
||||
|
||||
export class GoogleDrive implements INodeType {
|
||||
description: INodeTypeDescription = {
|
||||
@@ -741,6 +741,7 @@ export class GoogleDrive implements INodeType {
|
||||
placeholder: '',
|
||||
description: 'Name of the binary property which contains<br />the data for the file to be uploaded.',
|
||||
},
|
||||
|
||||
// ----------------------------------
|
||||
// file:update
|
||||
// ----------------------------------
|
||||
@@ -1898,6 +1899,91 @@ export class GoogleDrive implements INodeType {
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
displayName: 'Options',
|
||||
name: 'options',
|
||||
type: 'collection',
|
||||
placeholder: 'Add Option',
|
||||
default: {},
|
||||
displayOptions: {
|
||||
show: {
|
||||
operation: [
|
||||
'upload',
|
||||
],
|
||||
resource: [
|
||||
'file',
|
||||
],
|
||||
},
|
||||
},
|
||||
options: [
|
||||
{
|
||||
displayName: 'APP Properties',
|
||||
name: 'appPropertiesUi',
|
||||
placeholder: 'Add Property',
|
||||
type: 'fixedCollection',
|
||||
default: '',
|
||||
typeOptions: {
|
||||
multipleValues: true,
|
||||
},
|
||||
description: 'A collection of arbitrary key-value pairs which are private to the requesting app',
|
||||
options: [
|
||||
{
|
||||
name: 'appPropertyValues',
|
||||
displayName: 'APP Property',
|
||||
values: [
|
||||
{
|
||||
displayName: 'Key',
|
||||
name: 'key',
|
||||
type: 'string',
|
||||
default: '',
|
||||
description: 'Name of the key to add.',
|
||||
},
|
||||
{
|
||||
displayName: 'Value',
|
||||
name: 'value',
|
||||
type: 'string',
|
||||
default: '',
|
||||
description: 'Value to set for the key.',
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
displayName: 'Properties',
|
||||
name: 'propertiesUi',
|
||||
placeholder: 'Add Property',
|
||||
type: 'fixedCollection',
|
||||
default: '',
|
||||
typeOptions: {
|
||||
multipleValues: true,
|
||||
},
|
||||
description: 'A collection of arbitrary key-value pairs which are visible to all apps.',
|
||||
options: [
|
||||
{
|
||||
name: 'propertyValues',
|
||||
displayName: 'Property',
|
||||
values: [
|
||||
{
|
||||
displayName: 'Key',
|
||||
name: 'key',
|
||||
type: 'string',
|
||||
default: '',
|
||||
description: 'Name of the key to add.',
|
||||
},
|
||||
{
|
||||
displayName: 'Value',
|
||||
name: 'value',
|
||||
type: 'string',
|
||||
default: '',
|
||||
description: 'Value to set for the key.',
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
};
|
||||
|
||||
@@ -2228,6 +2314,18 @@ export class GoogleDrive implements INodeType {
|
||||
originalFilename,
|
||||
};
|
||||
|
||||
const properties = this.getNodeParameter('options.propertiesUi.propertyValues', i, []) as IDataObject[];
|
||||
|
||||
if (properties.length) {
|
||||
Object.assign(body, { properties: properties.reduce((obj, value) => Object.assign(obj, { [`${value.key}`]: value.value }), {}) } );
|
||||
}
|
||||
|
||||
const appProperties = this.getNodeParameter('options.appPropertiesUi.appPropertyValues', i, []) as IDataObject[];
|
||||
|
||||
if (properties.length) {
|
||||
Object.assign(body, { appProperties: appProperties.reduce((obj, value) => Object.assign(obj, { [`${value.key}`]: value.value }), {}) });
|
||||
}
|
||||
|
||||
qs = {
|
||||
addParents: parents.join(','),
|
||||
// When set to true shared drives can be used.
|
||||
|
||||
@@ -23,5 +23,8 @@
|
||||
"url": "https://n8n.io/blog/automate-google-apps-for-productivity/"
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
"alias": [
|
||||
"Workspaces"
|
||||
]
|
||||
}
|
||||
@@ -74,6 +74,10 @@ export async function googleApiRequest(this: IExecuteFunctions | IExecuteSingleF
|
||||
}
|
||||
|
||||
} catch (error) {
|
||||
if (error.code === 'ERR_OSSL_PEM_NO_START_LINE') {
|
||||
error.statusCode = '401';
|
||||
}
|
||||
|
||||
throw new NodeApiError(this.getNode(), error);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -53,6 +53,10 @@ export async function googleApiRequest(this: IExecuteFunctions | IExecuteSingleF
|
||||
return await this.helpers.requestOAuth2.call(this, 'googleSheetsOAuth2Api', options);
|
||||
}
|
||||
} catch (error) {
|
||||
if (error.code === 'ERR_OSSL_PEM_NO_START_LINE') {
|
||||
error.statusCode = '401';
|
||||
}
|
||||
|
||||
throw new NodeApiError(this.getNode(), error);
|
||||
}
|
||||
}
|
||||
@@ -138,4 +142,4 @@ export function hexToRgb(hex: string) {
|
||||
green: parseInt(result[2], 16),
|
||||
blue: parseInt(result[3], 16),
|
||||
} : null;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -79,5 +79,10 @@
|
||||
"url": "https://n8n.io/blog/sending-automated-congratulations-with-google-sheets-twilio-and-n8n/"
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
"alias": [
|
||||
"CSV",
|
||||
"Spreadsheet",
|
||||
"GS"
|
||||
]
|
||||
}
|
||||
@@ -54,6 +54,10 @@ export async function googleApiRequest(
|
||||
return await this.helpers.requestOAuth2!.call(this, 'googleSlidesOAuth2Api', options);
|
||||
}
|
||||
} catch (error) {
|
||||
if (error.code === 'ERR_OSSL_PEM_NO_START_LINE') {
|
||||
error.statusCode = '401';
|
||||
}
|
||||
|
||||
throw new NodeApiError(this.getNode(), error);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -13,6 +13,11 @@
|
||||
}
|
||||
],
|
||||
"generic": [
|
||||
{
|
||||
"label": "What are APIs and how to use them with no code",
|
||||
"icon": " 🪢",
|
||||
"url": "https://n8n.io/blog/what-are-apis-how-to-use-them-with-no-code/"
|
||||
},
|
||||
{
|
||||
"label": "How to automatically give kudos to contributors with GitHub, Slack, and n8n",
|
||||
"icon": "👏",
|
||||
|
||||
@@ -17,6 +17,11 @@
|
||||
"icon": "🧬",
|
||||
"url": "https://n8n.io/blog/why-business-process-automation-with-n8n-can-change-your-daily-life/"
|
||||
},
|
||||
{
|
||||
"label": "5 tasks you can automate with the new Notion API ",
|
||||
"icon": "⚡️",
|
||||
"url": "https://n8n.io/blog/5-tasks-you-can-automate-with-notion-api/"
|
||||
},
|
||||
{
|
||||
"label": "How to use the HTTP Request Node - The Swiss Army Knife for Workflow Automation",
|
||||
"icon": "🧰",
|
||||
|
||||
@@ -43,6 +43,16 @@
|
||||
"icon": "🛳",
|
||||
"url": "https://n8n.io/blog/running-n8n-on-ships-an-interview-with-maranics/"
|
||||
},
|
||||
{
|
||||
"label": "What are APIs and how to use them with no code",
|
||||
"icon": " 🪢",
|
||||
"url": "https://n8n.io/blog/what-are-apis-how-to-use-them-with-no-code/"
|
||||
},
|
||||
{
|
||||
"label": "5 tasks you can automate with the new Notion API ",
|
||||
"icon": "⚡️",
|
||||
"url": "https://n8n.io/blog/5-tasks-you-can-automate-with-notion-api/"
|
||||
},
|
||||
{
|
||||
"label": "Celebrating World Poetry Day with a daily poem in Telegram",
|
||||
"icon": "📜",
|
||||
@@ -83,6 +93,11 @@
|
||||
"icon": "📈",
|
||||
"url": "https://n8n.io/blog/a-low-code-bitcoin-ticker-built-with-questdb-and-n8n-io/"
|
||||
},
|
||||
{
|
||||
"label": "How Common Knowledge use workflow automation for activism",
|
||||
"icon": "✨",
|
||||
"url": "https://n8n.io/blog/automations-for-activists/"
|
||||
},
|
||||
{
|
||||
"label": "Creating scheduled text affirmations with n8n",
|
||||
"icon": "🤟",
|
||||
@@ -95,6 +110,12 @@
|
||||
}
|
||||
]
|
||||
},
|
||||
"alias": [
|
||||
"API",
|
||||
"Request",
|
||||
"URL",
|
||||
"Build"
|
||||
],
|
||||
"subcategories": {
|
||||
"Core Nodes": [
|
||||
"Helpers"
|
||||
|
||||