From 45abd2a57ff8a11b900a9f99c671eb1e2aa3bcb4 Mon Sep 17 00:00:00 2001 From: Bryan Frimin Date: Tue, 2 Dec 2025 15:42:46 +0100 Subject: [PATCH] Add meeting operations Signed-off-by: Bryan Frimin --- packages/n8n-node/nodes/Probo/Probo.node.ts | 5 + .../n8n-node/nodes/Probo/actions/index.ts | 2 + .../Probo/actions/meeting/create.operation.ts | 174 ++++++++++++++++++ .../Probo/actions/meeting/delete.operation.ts | 42 +++++ .../Probo/actions/meeting/get.operation.ts | 102 ++++++++++ .../Probo/actions/meeting/getAll.operation.ts | 150 +++++++++++++++ .../nodes/Probo/actions/meeting/index.ts | 61 ++++++ .../Probo/actions/meeting/update.operation.ts | 168 +++++++++++++++++ 8 files changed, 704 insertions(+) create mode 100644 packages/n8n-node/nodes/Probo/actions/meeting/create.operation.ts create mode 100644 packages/n8n-node/nodes/Probo/actions/meeting/delete.operation.ts create mode 100644 packages/n8n-node/nodes/Probo/actions/meeting/get.operation.ts create mode 100644 packages/n8n-node/nodes/Probo/actions/meeting/getAll.operation.ts create mode 100644 packages/n8n-node/nodes/Probo/actions/meeting/index.ts create mode 100644 packages/n8n-node/nodes/Probo/actions/meeting/update.operation.ts diff --git a/packages/n8n-node/nodes/Probo/Probo.node.ts b/packages/n8n-node/nodes/Probo/Probo.node.ts index 928e90525..5c5b7fac5 100644 --- a/packages/n8n-node/nodes/Probo/Probo.node.ts +++ b/packages/n8n-node/nodes/Probo/Probo.node.ts @@ -92,6 +92,11 @@ export class Probo implements INodeType { value: 'measure', description: 'Manage measures', }, + { + name: 'Meeting', + value: 'meeting', + description: 'Manage meetings', + }, { name: 'Organization', value: 'organization', diff --git a/packages/n8n-node/nodes/Probo/actions/index.ts b/packages/n8n-node/nodes/Probo/actions/index.ts index 1dffceec7..7bb1527e8 100644 --- a/packages/n8n-node/nodes/Probo/actions/index.ts +++ b/packages/n8n-node/nodes/Probo/actions/index.ts @@ -5,6 +5,7 @@ import * as datum from './datum'; import * as execute from './execute'; import * as framework from './framework'; import * as measure from './measure'; +import * as meeting from './meeting'; import * as organization from './organization'; export interface ResourceModule { @@ -24,6 +25,7 @@ export const resources: Record = { execute: execute as ResourceModule, framework: framework as ResourceModule, measure: measure as ResourceModule, + meeting: meeting as ResourceModule, organization: organization as ResourceModule, }; diff --git a/packages/n8n-node/nodes/Probo/actions/meeting/create.operation.ts b/packages/n8n-node/nodes/Probo/actions/meeting/create.operation.ts new file mode 100644 index 000000000..a2a01e4bb --- /dev/null +++ b/packages/n8n-node/nodes/Probo/actions/meeting/create.operation.ts @@ -0,0 +1,174 @@ +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: ['meeting'], + operation: ['create'], + }, + }, + default: '', + description: 'The ID of the organization', + required: true, + }, + { + displayName: 'Name', + name: 'name', + type: 'string', + displayOptions: { + show: { + resource: ['meeting'], + operation: ['create'], + }, + }, + default: '', + description: 'The name of the meeting', + required: true, + }, + { + displayName: 'Date', + name: 'date', + type: 'dateTime', + displayOptions: { + show: { + resource: ['meeting'], + operation: ['create'], + }, + }, + default: '', + description: 'The date and time of the meeting', + required: true, + }, + { + displayName: 'Attendee IDs', + name: 'attendeeIds', + type: 'string', + displayOptions: { + show: { + resource: ['meeting'], + operation: ['create'], + }, + }, + default: '', + description: 'Comma-separated list of attendee IDs (People IDs)', + }, + { + displayName: 'Minutes', + name: 'minutes', + type: 'string', + typeOptions: { + rows: 4, + }, + displayOptions: { + show: { + resource: ['meeting'], + operation: ['create'], + }, + }, + default: '', + description: 'The minutes of the meeting', + }, + { + displayName: 'Options', + name: 'options', + type: 'collection', + placeholder: 'Add Option', + default: {}, + displayOptions: { + show: { + resource: ['meeting'], + operation: ['create'], + }, + }, + options: [ + { + displayName: 'Include Attendees', + name: 'includeAttendees', + type: 'boolean', + default: false, + description: 'Whether to include attendees in the response', + }, + { + displayName: 'Include Organization', + name: 'includeOrganization', + type: 'boolean', + default: false, + description: 'Whether to include organization in the response', + }, + ], + }, +]; + +export async function execute( + this: IExecuteFunctions, + itemIndex: number, +): Promise { + const organizationId = this.getNodeParameter('organizationId', itemIndex) as string; + const name = this.getNodeParameter('name', itemIndex) as string; + const date = this.getNodeParameter('date', itemIndex) as string; + const attendeeIdsStr = this.getNodeParameter('attendeeIds', itemIndex, '') as string; + const minutes = this.getNodeParameter('minutes', itemIndex, '') as string; + const options = this.getNodeParameter('options', itemIndex, {}) as { + includeAttendees?: boolean; + includeOrganization?: boolean; + }; + + const attendeesFragment = options.includeAttendees + ? `attendees { + id + fullName + }` + : ''; + + const organizationFragment = options.includeOrganization + ? `organization { + id + name + }` + : ''; + + const query = ` + mutation CreateMeeting($input: CreateMeetingInput!) { + createMeeting(input: $input) { + meetingEdge { + node { + id + name + date + minutes + ${attendeesFragment} + ${organizationFragment} + createdAt + updatedAt + } + } + } + } + `; + + const attendeeIds = attendeeIdsStr ? attendeeIdsStr.split(',').map((id) => id.trim()).filter(Boolean) : undefined; + + const input: Record = { + organizationId, + name, + date, + }; + if (attendeeIds && attendeeIds.length > 0) { + input.attendeeIds = attendeeIds; + } + if (minutes) { + input.minutes = minutes; + } + + const responseData = await proboApiRequest.call(this, query, { input }); + + return { + json: responseData, + pairedItem: { item: itemIndex }, + }; +} diff --git a/packages/n8n-node/nodes/Probo/actions/meeting/delete.operation.ts b/packages/n8n-node/nodes/Probo/actions/meeting/delete.operation.ts new file mode 100644 index 000000000..935e3a059 --- /dev/null +++ b/packages/n8n-node/nodes/Probo/actions/meeting/delete.operation.ts @@ -0,0 +1,42 @@ +import type { INodeProperties, IExecuteFunctions, INodeExecutionData } from 'n8n-workflow'; +import { proboApiRequest } from '../../GenericFunctions'; + +export const description: INodeProperties[] = [ + { + displayName: 'Meeting ID', + name: 'meetingId', + type: 'string', + displayOptions: { + show: { + resource: ['meeting'], + operation: ['delete'], + }, + }, + default: '', + description: 'The ID of the meeting to delete', + required: true, + }, +]; + +export async function execute( + this: IExecuteFunctions, + itemIndex: number, +): Promise { + const meetingId = this.getNodeParameter('meetingId', itemIndex) as string; + + const query = ` + mutation DeleteMeeting($input: DeleteMeetingInput!) { + deleteMeeting(input: $input) { + deletedMeetingId + } + } + `; + + const responseData = await proboApiRequest.call(this, query, { input: { meetingId } }); + + return { + json: responseData, + pairedItem: { item: itemIndex }, + }; +} + diff --git a/packages/n8n-node/nodes/Probo/actions/meeting/get.operation.ts b/packages/n8n-node/nodes/Probo/actions/meeting/get.operation.ts new file mode 100644 index 000000000..2911d2f6a --- /dev/null +++ b/packages/n8n-node/nodes/Probo/actions/meeting/get.operation.ts @@ -0,0 +1,102 @@ +import type { INodeProperties, IExecuteFunctions, INodeExecutionData } from 'n8n-workflow'; +import { proboApiRequest } from '../../GenericFunctions'; + +export const description: INodeProperties[] = [ + { + displayName: 'Meeting ID', + name: 'meetingId', + type: 'string', + displayOptions: { + show: { + resource: ['meeting'], + operation: ['get'], + }, + }, + default: '', + description: 'The ID of the meeting', + required: true, + }, + { + displayName: 'Options', + name: 'options', + type: 'collection', + placeholder: 'Add Option', + default: {}, + displayOptions: { + show: { + resource: ['meeting'], + operation: ['get'], + }, + }, + options: [ + { + displayName: 'Include Attendees', + name: 'includeAttendees', + type: 'boolean', + default: false, + description: 'Whether to include attendees in the response', + }, + { + displayName: 'Include Organization', + name: 'includeOrganization', + type: 'boolean', + default: false, + description: 'Whether to include organization in the response', + }, + ], + }, +]; + +export async function execute( + this: IExecuteFunctions, + itemIndex: number, +): Promise { + const meetingId = this.getNodeParameter('meetingId', itemIndex) as string; + const options = this.getNodeParameter('options', itemIndex, {}) as { + includeAttendees?: boolean; + includeOrganization?: boolean; + }; + + const attendeesFragment = options.includeAttendees + ? `attendees { + id + fullName + }` + : ''; + + const organizationFragment = options.includeOrganization + ? `organization { + id + name + }` + : ''; + + const query = ` + query GetMeeting($meetingId: ID!) { + node(id: $meetingId) { + ... on Meeting { + id + name + date + minutes + ${attendeesFragment} + ${organizationFragment} + createdAt + updatedAt + } + } + } + `; + + const variables = { + meetingId, + }; + + const responseData = await proboApiRequest.call(this, query, variables); + + return { + json: responseData, + pairedItem: { item: itemIndex }, + }; +} + diff --git a/packages/n8n-node/nodes/Probo/actions/meeting/getAll.operation.ts b/packages/n8n-node/nodes/Probo/actions/meeting/getAll.operation.ts new file mode 100644 index 000000000..dca187c9e --- /dev/null +++ b/packages/n8n-node/nodes/Probo/actions/meeting/getAll.operation.ts @@ -0,0 +1,150 @@ +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: ['meeting'], + operation: ['getAll'], + }, + }, + default: '', + description: 'The ID of the organization', + required: true, + }, + { + displayName: 'Return All', + name: 'returnAll', + type: 'boolean', + displayOptions: { + show: { + resource: ['meeting'], + 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: ['meeting'], + operation: ['getAll'], + returnAll: [false], + }, + }, + typeOptions: { + minValue: 1, + }, + default: 50, + description: 'Max number of results to return', + }, + { + displayName: 'Options', + name: 'options', + type: 'collection', + placeholder: 'Add Option', + default: {}, + displayOptions: { + show: { + resource: ['meeting'], + operation: ['getAll'], + }, + }, + options: [ + { + displayName: 'Include Attendees', + name: 'includeAttendees', + type: 'boolean', + default: false, + description: 'Whether to include attendees in the response', + }, + { + displayName: 'Include Organization', + name: 'includeOrganization', + type: 'boolean', + default: false, + description: 'Whether to include organization in the response', + }, + ], + }, +]; + +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 options = this.getNodeParameter('options', itemIndex, {}) as { + includeAttendees?: boolean; + includeOrganization?: boolean; + }; + + const attendeesFragment = options.includeAttendees + ? `attendees { + id + fullName + }` + : ''; + + const organizationFragment = options.includeOrganization + ? `organization { + id + name + }` + : ''; + + const query = ` + query GetMeetings($organizationId: ID!, $first: Int, $after: CursorKey) { + node(id: $organizationId) { + ... on Organization { + meetings(first: $first, after: $after) { + edges { + node { + id + name + date + minutes + ${attendeesFragment} + ${organizationFragment} + createdAt + updatedAt + } + } + pageInfo { + hasNextPage + endCursor + } + } + } + } + } + `; + + const meetings = await proboApiRequestAllItems.call( + this, + query, + { organizationId }, + (response) => { + const data = response?.data as IDataObject | undefined; + const node = data?.node as IDataObject | undefined; + return node?.meetings as IDataObject | undefined; + }, + returnAll, + limit, + ); + + return { + json: { meetings }, + pairedItem: { item: itemIndex }, + }; +} diff --git a/packages/n8n-node/nodes/Probo/actions/meeting/index.ts b/packages/n8n-node/nodes/Probo/actions/meeting/index.ts new file mode 100644 index 000000000..c8e582508 --- /dev/null +++ b/packages/n8n-node/nodes/Probo/actions/meeting/index.ts @@ -0,0 +1,61 @@ +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: ['meeting'], + }, + }, + options: [ + { + name: 'Create', + value: 'create', + description: 'Create a new meeting', + action: 'Create a meeting', + }, + { + name: 'Delete', + value: 'delete', + description: 'Delete a meeting', + action: 'Delete a meeting', + }, + { + name: 'Get', + value: 'get', + description: 'Get a meeting', + action: 'Get a meeting', + }, + { + name: 'Get Many', + value: 'getAll', + description: 'Get many meetings', + action: 'Get many meetings', + }, + { + name: 'Update', + value: 'update', + description: 'Update an existing meeting', + action: 'Update a meeting', + }, + ], + 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/meeting/update.operation.ts b/packages/n8n-node/nodes/Probo/actions/meeting/update.operation.ts new file mode 100644 index 000000000..629780105 --- /dev/null +++ b/packages/n8n-node/nodes/Probo/actions/meeting/update.operation.ts @@ -0,0 +1,168 @@ +import type { INodeProperties, IExecuteFunctions, INodeExecutionData } from 'n8n-workflow'; +import { proboApiRequest } from '../../GenericFunctions'; + +export const description: INodeProperties[] = [ + { + displayName: 'Meeting ID', + name: 'meetingId', + type: 'string', + displayOptions: { + show: { + resource: ['meeting'], + operation: ['update'], + }, + }, + default: '', + description: 'The ID of the meeting to update', + required: true, + }, + { + displayName: 'Name', + name: 'name', + type: 'string', + displayOptions: { + show: { + resource: ['meeting'], + operation: ['update'], + }, + }, + default: '', + description: 'The name of the meeting', + }, + { + displayName: 'Date', + name: 'date', + type: 'dateTime', + displayOptions: { + show: { + resource: ['meeting'], + operation: ['update'], + }, + }, + default: '', + description: 'The date and time of the meeting', + }, + { + displayName: 'Attendee IDs', + name: 'attendeeIds', + type: 'string', + displayOptions: { + show: { + resource: ['meeting'], + operation: ['update'], + }, + }, + default: '', + description: 'Comma-separated list of attendee IDs (People IDs)', + }, + { + displayName: 'Minutes', + name: 'minutes', + type: 'string', + typeOptions: { + rows: 4, + }, + displayOptions: { + show: { + resource: ['meeting'], + operation: ['update'], + }, + }, + default: '', + description: 'The minutes of the meeting', + }, + { + displayName: 'Options', + name: 'options', + type: 'collection', + placeholder: 'Add Option', + default: {}, + displayOptions: { + show: { + resource: ['meeting'], + operation: ['update'], + }, + }, + options: [ + { + displayName: 'Include Attendees', + name: 'includeAttendees', + type: 'boolean', + default: false, + description: 'Whether to include attendees in the response', + }, + { + displayName: 'Include Organization', + name: 'includeOrganization', + type: 'boolean', + default: false, + description: 'Whether to include organization in the response', + }, + ], + }, +]; + +export async function execute( + this: IExecuteFunctions, + itemIndex: number, +): Promise { + const meetingId = this.getNodeParameter('meetingId', itemIndex) as string; + const name = this.getNodeParameter('name', itemIndex, '') as string; + const date = this.getNodeParameter('date', itemIndex, '') as string; + const attendeeIdsStr = this.getNodeParameter('attendeeIds', itemIndex, '') as string; + const minutes = this.getNodeParameter('minutes', itemIndex, '') as string; + const options = this.getNodeParameter('options', itemIndex, {}) as { + includeAttendees?: boolean; + includeOrganization?: boolean; + }; + + const attendeesFragment = options.includeAttendees + ? `attendees { + id + fullName + }` + : ''; + + const organizationFragment = options.includeOrganization + ? `organization { + id + name + }` + : ''; + + const query = ` + mutation UpdateMeeting($input: UpdateMeetingInput!) { + updateMeeting(input: $input) { + meeting { + id + name + date + minutes + ${attendeesFragment} + ${organizationFragment} + createdAt + updatedAt + } + } + } + `; + + const attendeeIds = attendeeIdsStr ? attendeeIdsStr.split(',').map((id) => id.trim()).filter(Boolean) : undefined; + + const input: Record = { meetingId }; + if (name) input.name = name; + if (date) input.date = date; + if (attendeeIds && attendeeIds.length > 0) { + input.attendeeIds = attendeeIds; + } + if (minutes !== undefined && minutes !== null) { + input.minutes = minutes; + } + + const responseData = await proboApiRequest.call(this, query, { input }); + + return { + json: responseData, + pairedItem: { item: itemIndex }, + }; +}