Add Action Network node (#1897)

*  Create Action Network node

* 🔥 Remove comments

* 🔥 Remove status in attendance

* 🔥 Remove loaders per feedback

Loaders removed for person, event, signature and petition

* 🚚 Rename tagging to person tag

* 🔨 Convert address_lines param to string

*  Simplify responses for person resource

*  Add simplify to all operations

* ✏️ Add documentation links

*  Improvements

* ✏️ Fix positioning of doc links

* 🔨 Refactor updateFields in signature:update

*  Address minor comments

*  Improvements

*  Add continue on fail

*  Minor improvements

Co-authored-by: ricardo <ricardoespinoza105@gmail.com>
Co-authored-by: Jan Oberhauser <jan.oberhauser@gmail.com>
This commit is contained in:
Iván Ovejero 2021-06-27 13:07:25 +02:00 committed by GitHub
parent 2ec52cf207
commit 224a26c922
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
15 changed files with 2752 additions and 0 deletions

View file

@ -0,0 +1,17 @@
import {
ICredentialType,
NodePropertyTypes,
} from 'n8n-workflow';
export class ActionNetworkApi implements ICredentialType {
name = 'actionNetworkApi';
displayName = 'Action Network API';
properties = [
{
displayName: 'API Key',
name: 'apiKey',
type: 'string' as NodePropertyTypes,
default: '',
},
];
}

View 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)];
}
}

View 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],
};
};

View file

@ -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

View file

@ -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[];

View file

@ -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[];

View file

@ -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: 'Persons 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[];

View file

@ -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[];

View file

@ -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[];

View file

@ -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 events 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 events 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: 'Persons last name.',
},
{
displayName: 'Given Name',
name: 'given_name',
type: 'string',
default: '',
description: 'Persons 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 petitions page on the Action Network or a third party.',
},
{
displayName: 'Featured Image URL',
name: 'featured_image_url',
type: 'string',
default: '',
description: 'URL to this actions 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 petitions 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.',
});

View file

@ -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[];

View file

@ -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[];

View file

@ -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';

View 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 };

View file

@ -27,6 +27,7 @@
], ],
"n8n": { "n8n": {
"credentials": [ "credentials": [
"dist/credentials/ActionNetworkApi.credentials.js",
"dist/credentials/ActiveCampaignApi.credentials.js", "dist/credentials/ActiveCampaignApi.credentials.js",
"dist/credentials/AgileCrmApi.credentials.js", "dist/credentials/AgileCrmApi.credentials.js",
"dist/credentials/AcuitySchedulingApi.credentials.js", "dist/credentials/AcuitySchedulingApi.credentials.js",
@ -282,6 +283,7 @@
"dist/credentials/ZulipApi.credentials.js" "dist/credentials/ZulipApi.credentials.js"
], ],
"nodes": [ "nodes": [
"dist/nodes/ActionNetwork/ActionNetwork.node.js",
"dist/nodes/ActiveCampaign/ActiveCampaign.node.js", "dist/nodes/ActiveCampaign/ActiveCampaign.node.js",
"dist/nodes/ActiveCampaign/ActiveCampaignTrigger.node.js", "dist/nodes/ActiveCampaign/ActiveCampaignTrigger.node.js",
"dist/nodes/AgileCrm/AgileCrm.node.js", "dist/nodes/AgileCrm/AgileCrm.node.js",