From ba1c884f4afc5a3fad018916c931a47c4b8cfa25 Mon Sep 17 00:00:00 2001 From: Sacha Al Himdani Date: Tue, 9 Dec 2025 10:49:09 +0100 Subject: [PATCH] Add people to n8n Signed-off-by: Sacha Al Himdani --- packages/n8n-node/nodes/Probo/Probo.node.ts | 7 +- .../n8n-node/nodes/Probo/actions/index.ts | 2 + .../Probo/actions/people/create.operation.ts | 173 ++++++++++++++++++ .../Probo/actions/people/delete.operation.ts | 41 +++++ .../Probo/actions/people/get.operation.ts | 56 ++++++ .../Probo/actions/people/getAll.operation.ts | 105 +++++++++++ .../nodes/Probo/actions/people/index.ts | 60 ++++++ .../Probo/actions/people/update.operation.ts | 170 +++++++++++++++++ 8 files changed, 613 insertions(+), 1 deletion(-) create mode 100644 packages/n8n-node/nodes/Probo/actions/people/create.operation.ts create mode 100644 packages/n8n-node/nodes/Probo/actions/people/delete.operation.ts create mode 100644 packages/n8n-node/nodes/Probo/actions/people/get.operation.ts create mode 100644 packages/n8n-node/nodes/Probo/actions/people/getAll.operation.ts create mode 100644 packages/n8n-node/nodes/Probo/actions/people/index.ts create mode 100644 packages/n8n-node/nodes/Probo/actions/people/update.operation.ts diff --git a/packages/n8n-node/nodes/Probo/Probo.node.ts b/packages/n8n-node/nodes/Probo/Probo.node.ts index 439dfc216..ce5882af7 100644 --- a/packages/n8n-node/nodes/Probo/Probo.node.ts +++ b/packages/n8n-node/nodes/Probo/Probo.node.ts @@ -102,6 +102,11 @@ export class Probo implements INodeType { value: 'organization', description: 'Manage organizations', }, + { + name: 'People', + value: 'people', + description: 'Manage people', + }, { name: 'Vendor', value: 'vendor', @@ -134,4 +139,4 @@ export class Probo implements INodeType { methods = { listSearch: {}, }; -} \ No newline at end of file +} diff --git a/packages/n8n-node/nodes/Probo/actions/index.ts b/packages/n8n-node/nodes/Probo/actions/index.ts index 14278a4f2..14aaf0a7d 100644 --- a/packages/n8n-node/nodes/Probo/actions/index.ts +++ b/packages/n8n-node/nodes/Probo/actions/index.ts @@ -7,6 +7,7 @@ import * as framework from './framework'; import * as measure from './measure'; import * as meeting from './meeting'; import * as organization from './organization'; +import * as people from './people'; import * as vendor from './vendor'; export interface ResourceModule { @@ -28,6 +29,7 @@ export const resources: Record = { measure: measure as ResourceModule, meeting: meeting as ResourceModule, organization: organization as ResourceModule, + people: people as ResourceModule, vendor: vendor as ResourceModule, }; diff --git a/packages/n8n-node/nodes/Probo/actions/people/create.operation.ts b/packages/n8n-node/nodes/Probo/actions/people/create.operation.ts new file mode 100644 index 000000000..c8509673b --- /dev/null +++ b/packages/n8n-node/nodes/Probo/actions/people/create.operation.ts @@ -0,0 +1,173 @@ +import type { INodeProperties, IExecuteFunctions, INodeExecutionData } from 'n8n-workflow'; +import { proboApiRequest } from '../../GenericFunctions'; + +export const description: INodeProperties[] = [ + { + displayName: 'Organization ID', + name: 'organizationId', + type: 'string', + displayOptions: { + show: { + resource: ['people'], + operation: ['create'], + }, + }, + default: '', + description: 'The ID of the organization', + required: true, + }, + { + displayName: 'Full Name', + name: 'fullName', + type: 'string', + displayOptions: { + show: { + resource: ['people'], + operation: ['create'], + }, + }, + default: '', + description: 'The full name of the person', + required: true, + }, + { + displayName: 'Primary Email Address', + name: 'primaryEmailAddress', + type: 'string', + displayOptions: { + show: { + resource: ['people'], + operation: ['create'], + }, + }, + default: '', + description: 'The primary email address of the person', + required: true, + }, + { + displayName: 'Kind', + name: 'kind', + type: 'options', + displayOptions: { + show: { + resource: ['people'], + operation: ['create'], + }, + }, + options: [ + { + name: 'Employee', + value: 'EMPLOYEE', + }, + { + name: 'Contractor', + value: 'CONTRACTOR', + }, + { + name: 'Service Account', + value: 'SERVICE_ACCOUNT', + }, + ], + default: 'EMPLOYEE', + description: 'The kind of person', + required: true, + }, + { + displayName: 'Additional Fields', + name: 'additionalFields', + type: 'collection', + placeholder: 'Add Field', + default: {}, + displayOptions: { + show: { + resource: ['people'], + operation: ['create'], + }, + }, + options: [ + { + displayName: 'Additional Email Addresses', + name: 'additionalEmailAddresses', + type: 'string', + default: '', + description: 'Comma-separated list of additional email addresses', + }, + { + displayName: 'Position', + name: 'position', + type: 'string', + default: '', + description: 'The position of the person', + }, + { + displayName: 'Contract Start Date', + name: 'contractStartDate', + type: 'dateTime', + default: '', + }, + { + displayName: 'Contract End Date', + name: 'contractEndDate', + type: 'dateTime', + default: '', + }, + ], + }, +]; + +export async function execute( + this: IExecuteFunctions, + itemIndex: number, +): Promise { + const organizationId = this.getNodeParameter('organizationId', itemIndex) as string; + const fullName = this.getNodeParameter('fullName', itemIndex) as string; + const primaryEmailAddress = this.getNodeParameter('primaryEmailAddress', itemIndex) as string; + const kind = this.getNodeParameter('kind', itemIndex) as string; + const additionalFields = this.getNodeParameter('additionalFields', itemIndex, {}) as { + additionalEmailAddresses?: string; + position?: string; + contractStartDate?: string; + contractEndDate?: string; + }; + + const query = ` + mutation CreatePeople($input: CreatePeopleInput!) { + createPeople(input: $input) { + peopleEdge { + node { + id + fullName + primaryEmailAddress + additionalEmailAddresses + kind + position + contractStartDate + contractEndDate + createdAt + updatedAt + } + } + } + } + `; + + const input: Record = { + organizationId, + fullName, + primaryEmailAddress, + kind, + }; + if (additionalFields.additionalEmailAddresses) { + input.additionalEmailAddresses = additionalFields.additionalEmailAddresses.split(',').map((e) => e.trim()).filter(Boolean); + } + if (additionalFields.position) input.position = additionalFields.position; + if (additionalFields.contractStartDate) input.contractStartDate = additionalFields.contractStartDate; + if (additionalFields.contractEndDate) input.contractEndDate = additionalFields.contractEndDate; + + const responseData = await proboApiRequest.call(this, query, { input }); + + return { + json: responseData, + pairedItem: { item: itemIndex }, + }; +} diff --git a/packages/n8n-node/nodes/Probo/actions/people/delete.operation.ts b/packages/n8n-node/nodes/Probo/actions/people/delete.operation.ts new file mode 100644 index 000000000..9929ac769 --- /dev/null +++ b/packages/n8n-node/nodes/Probo/actions/people/delete.operation.ts @@ -0,0 +1,41 @@ +import type { INodeProperties, IExecuteFunctions, INodeExecutionData } from 'n8n-workflow'; +import { proboApiRequest } from '../../GenericFunctions'; + +export const description: INodeProperties[] = [ + { + displayName: 'People ID', + name: 'peopleId', + type: 'string', + displayOptions: { + show: { + resource: ['people'], + operation: ['delete'], + }, + }, + default: '', + description: 'The ID of the person to delete', + required: true, + }, +]; + +export async function execute( + this: IExecuteFunctions, + itemIndex: number, +): Promise { + const peopleId = this.getNodeParameter('peopleId', itemIndex) as string; + + const query = ` + mutation DeletePeople($input: DeletePeopleInput!) { + deletePeople(input: $input) { + deletedPeopleId + } + } + `; + + const responseData = await proboApiRequest.call(this, query, { input: { peopleId } }); + + return { + json: responseData, + pairedItem: { item: itemIndex }, + }; +} diff --git a/packages/n8n-node/nodes/Probo/actions/people/get.operation.ts b/packages/n8n-node/nodes/Probo/actions/people/get.operation.ts new file mode 100644 index 000000000..cffda812e --- /dev/null +++ b/packages/n8n-node/nodes/Probo/actions/people/get.operation.ts @@ -0,0 +1,56 @@ +import type { INodeProperties, IExecuteFunctions, INodeExecutionData } from 'n8n-workflow'; +import { proboApiRequest } from '../../GenericFunctions'; + +export const description: INodeProperties[] = [ + { + displayName: 'People ID', + name: 'peopleId', + type: 'string', + displayOptions: { + show: { + resource: ['people'], + operation: ['get'], + }, + }, + default: '', + description: 'The ID of the person', + required: true, + }, +]; + +export async function execute( + this: IExecuteFunctions, + itemIndex: number, +): Promise { + const peopleId = this.getNodeParameter('peopleId', itemIndex) as string; + + const query = ` + query GetPeople($peopleId: ID!) { + node(id: $peopleId) { + ... on People { + id + fullName + primaryEmailAddress + additionalEmailAddresses + kind + position + contractStartDate + contractEndDate + createdAt + updatedAt + } + } + } + `; + + const variables = { + peopleId, + }; + + const responseData = await proboApiRequest.call(this, query, variables); + + return { + json: responseData, + pairedItem: { item: itemIndex }, + }; +} diff --git a/packages/n8n-node/nodes/Probo/actions/people/getAll.operation.ts b/packages/n8n-node/nodes/Probo/actions/people/getAll.operation.ts new file mode 100644 index 000000000..d59f884ff --- /dev/null +++ b/packages/n8n-node/nodes/Probo/actions/people/getAll.operation.ts @@ -0,0 +1,105 @@ +import type { INodeProperties, IExecuteFunctions, INodeExecutionData, IDataObject } from 'n8n-workflow'; +import { proboApiRequestAllItems } from '../../GenericFunctions'; + +export const description: INodeProperties[] = [ + { + displayName: 'Organization ID', + name: 'organizationId', + type: 'string', + displayOptions: { + show: { + resource: ['people'], + operation: ['getAll'], + }, + }, + default: '', + description: 'The ID of the organization', + required: true, + }, + { + displayName: 'Return All', + name: 'returnAll', + type: 'boolean', + displayOptions: { + show: { + resource: ['people'], + operation: ['getAll'], + }, + }, + default: false, + description: 'Whether to return all results or only up to a given limit', + }, + { + displayName: 'Limit', + name: 'limit', + type: 'number', + displayOptions: { + show: { + resource: ['people'], + operation: ['getAll'], + returnAll: [false], + }, + }, + typeOptions: { + minValue: 1, + }, + default: 50, + description: 'Max number of results to return', + }, +]; + +export async function execute( + this: IExecuteFunctions, + itemIndex: number, +): Promise { + const organizationId = this.getNodeParameter('organizationId', itemIndex) as string; + const returnAll = this.getNodeParameter('returnAll', itemIndex) as boolean; + const limit = this.getNodeParameter('limit', itemIndex, 50) as number; + + const query = ` + query GetPeoples($organizationId: ID!, $first: Int, $after: CursorKey) { + node(id: $organizationId) { + ... on Organization { + peoples(first: $first, after: $after) { + edges { + node { + id + fullName + primaryEmailAddress + additionalEmailAddresses + kind + position + contractStartDate + contractEndDate + createdAt + updatedAt + } + } + pageInfo { + hasNextPage + endCursor + } + } + } + } + } + `; + + const peoples = await proboApiRequestAllItems.call( + this, + query, + { organizationId }, + (response) => { + const data = response?.data as IDataObject | undefined; + const node = data?.node as IDataObject | undefined; + return node?.peoples as IDataObject | undefined; + }, + returnAll, + limit, + ); + + return { + json: { peoples }, + pairedItem: { item: itemIndex }, + }; +} diff --git a/packages/n8n-node/nodes/Probo/actions/people/index.ts b/packages/n8n-node/nodes/Probo/actions/people/index.ts new file mode 100644 index 000000000..bdfa8240c --- /dev/null +++ b/packages/n8n-node/nodes/Probo/actions/people/index.ts @@ -0,0 +1,60 @@ +import type { INodeProperties } from 'n8n-workflow'; +import * as createOp from './create.operation'; +import * as updateOp from './update.operation'; +import * as deleteOp from './delete.operation'; +import * as getOp from './get.operation'; +import * as getAllOp from './getAll.operation'; + +export const description: INodeProperties[] = [ + { + displayName: 'Operation', + name: 'operation', + type: 'options', + noDataExpression: true, + displayOptions: { + show: { + resource: ['people'], + }, + }, + options: [ + { + name: 'Create', + value: 'create', + description: 'Create a new person', + action: 'Create a person', + }, + { + name: 'Delete', + value: 'delete', + description: 'Delete a person', + action: 'Delete a person', + }, + { + name: 'Get', + value: 'get', + description: 'Get a person', + action: 'Get a person', + }, + { + name: 'Get Many', + value: 'getAll', + description: 'Get many people', + action: 'Get many people', + }, + { + name: 'Update', + value: 'update', + description: 'Update an existing person', + action: 'Update a person', + }, + ], + default: 'create', + }, + ...createOp.description, + ...updateOp.description, + ...deleteOp.description, + ...getOp.description, + ...getAllOp.description, +]; + +export { createOp as create, updateOp as update, deleteOp as delete, getOp as get, getAllOp as getAll }; diff --git a/packages/n8n-node/nodes/Probo/actions/people/update.operation.ts b/packages/n8n-node/nodes/Probo/actions/people/update.operation.ts new file mode 100644 index 000000000..a9d4c642f --- /dev/null +++ b/packages/n8n-node/nodes/Probo/actions/people/update.operation.ts @@ -0,0 +1,170 @@ +import type { INodeProperties, IExecuteFunctions, INodeExecutionData } from 'n8n-workflow'; +import { proboApiRequest } from '../../GenericFunctions'; + +export const description: INodeProperties[] = [ + { + displayName: 'People ID', + name: 'peopleId', + type: 'string', + displayOptions: { + show: { + resource: ['people'], + operation: ['update'], + }, + }, + default: '', + description: 'The ID of the person to update', + required: true, + }, + { + displayName: 'Full Name', + name: 'fullName', + type: 'string', + displayOptions: { + show: { + resource: ['people'], + operation: ['update'], + }, + }, + default: '', + description: 'The full name of the person', + }, + { + displayName: 'Primary Email Address', + name: 'primaryEmailAddress', + type: 'string', + displayOptions: { + show: { + resource: ['people'], + operation: ['update'], + }, + }, + default: '', + description: 'The primary email address of the person', + }, + { + displayName: 'Kind', + name: 'kind', + type: 'options', + displayOptions: { + show: { + resource: ['people'], + operation: ['update'], + }, + }, + options: [ + { + name: 'Employee', + value: 'EMPLOYEE', + }, + { + name: 'Contractor', + value: 'CONTRACTOR', + }, + { + name: 'Service Account', + value: 'SERVICE_ACCOUNT', + }, + ], + default: 'EMPLOYEE', + description: 'The kind of person', + }, + { + displayName: 'Additional Fields', + name: 'additionalFields', + type: 'collection', + placeholder: 'Add Field', + default: {}, + displayOptions: { + show: { + resource: ['people'], + operation: ['update'], + }, + }, + options: [ + { + displayName: 'Additional Email Addresses', + name: 'additionalEmailAddresses', + type: 'string', + default: '', + description: 'Comma-separated list of additional email addresses', + }, + { + displayName: 'Position', + name: 'position', + type: 'string', + default: '', + description: 'The position of the person', + }, + { + displayName: 'Contract Start Date', + name: 'contractStartDate', + type: 'dateTime', + default: '', + }, + { + displayName: 'Contract End Date', + name: 'contractEndDate', + type: 'dateTime', + default: '', + }, + ], + }, +]; + +export async function execute( + this: IExecuteFunctions, + itemIndex: number, +): Promise { + const peopleId = this.getNodeParameter('peopleId', itemIndex) as string; + const fullName = this.getNodeParameter('fullName', itemIndex, '') as string; + const primaryEmailAddress = this.getNodeParameter('primaryEmailAddress', itemIndex, '') as string; + const kind = this.getNodeParameter('kind', itemIndex, '') as string; + const additionalFields = this.getNodeParameter('additionalFields', itemIndex, {}) as { + additionalEmailAddresses?: string; + position?: string; + contractStartDate?: string; + contractEndDate?: string; + }; + + const query = ` + mutation UpdatePeople($input: UpdatePeopleInput!) { + updatePeople(input: $input) { + people { + id + fullName + primaryEmailAddress + additionalEmailAddresses + kind + position + contractStartDate + contractEndDate + createdAt + updatedAt + } + } + } + `; + + const input: Record = { id: peopleId }; + if (fullName) input.fullName = fullName; + if (primaryEmailAddress) input.primaryEmailAddress = primaryEmailAddress; + if (kind) input.kind = kind; + if (additionalFields.additionalEmailAddresses !== undefined) { + if (additionalFields.additionalEmailAddresses === '') { + input.additionalEmailAddresses = []; + } else { + input.additionalEmailAddresses = additionalFields.additionalEmailAddresses.split(',').map((e) => e.trim()).filter(Boolean); + } + } + if (additionalFields.position !== undefined) input.position = additionalFields.position === '' ? null : additionalFields.position; + if (additionalFields.contractStartDate !== undefined) input.contractStartDate = additionalFields.contractStartDate === '' ? null : additionalFields.contractStartDate; + if (additionalFields.contractEndDate !== undefined) input.contractEndDate = additionalFields.contractEndDate === '' ? null : additionalFields.contractEndDate; + + const responseData = await proboApiRequest.call(this, query, { input }); + + return { + json: responseData, + pairedItem: { item: itemIndex }, + }; +}