diff --git a/nodes/LinkedApi/LinkedApi.node.ts b/nodes/LinkedApi/LinkedApi.node.ts index 5000f4b..d152bcc 100644 --- a/nodes/LinkedApi/LinkedApi.node.ts +++ b/nodes/LinkedApi/LinkedApi.node.ts @@ -27,6 +27,8 @@ import { CommentOnPost, CreatePost, CreateRepost, + EndorsePersonSkill, + EndorsePersonSkills, FetchCompany, FetchPerson, FetchPost, @@ -84,6 +86,8 @@ const operations: Record = { commentOnPost: new CommentOnPost(), createPost: new CreatePost(), createRepost: new CreateRepost(), + endorsePersonSkill: new EndorsePersonSkill(), + endorsePersonSkills: new EndorsePersonSkills(), fetchPerson: new FetchPerson(), fetchCompany: new FetchCompany(), fetchPost: new FetchPost(), @@ -193,6 +197,9 @@ export class LinkedApi implements INodeType { ...operations.checkConnectionStatus.operationFields, ...operations.commentOnPost.operationFields, ...operations.createPost.operationFields, + ...operations.createRepost.operationFields, + ...operations.endorsePersonSkill.operationFields, + ...operations.endorsePersonSkills.operationFields, ...operations.fetchPerson.operationFields, ...operations.fetchCompany.operationFields, ...operations.fetchPost.operationFields, @@ -203,6 +210,7 @@ export class LinkedApi implements INodeType { ...operations.removeConnection.operationFields, ...operations.retrieveConnections.operationFields, ...operations.retrieveFeed.operationFields, + ...operations.retrieveProfileViewers.operationFields, ...operations.retrieveInvitations.operationFields, ...operations.retrievePendingRequests.operationFields, ...operations.retrievePerformance.operationFields, @@ -215,6 +223,7 @@ export class LinkedApi implements INodeType { ...operations.ignoreInvitation.operationFields, ...operations.sendMessage.operationFields, ...operations.syncConversation.operationFields, + ...operations.syncInbox.operationFields, ...operations.syncNetwork.operationFields, ...operations.manageConversation.operationFields, ...operations.withdrawConnectionRequest.operationFields, @@ -226,12 +235,14 @@ export class LinkedApi implements INodeType { ...operations.nvSearchPeople.operationFields, ...operations.nvSendMessage.operationFields, ...operations.nvSyncConversation.operationFields, + ...operations.nvSyncInbox.operationFields, ...operations.nvManageConversation.operationFields, // Other actions ...operations.customWorkflow.operationFields, ...operations.getWorkflowResult.operationFields, ...operations.cancelWorkflow.operationFields, ...operations.pollConversations.operationFields, + ...operations.pollInbox.operationFields, ...operations.pollNetwork.operationFields, ...operations.actionsUsageStatistics.operationFields, // Admin actions diff --git a/nodes/LinkedApi/operations/admin/AdminDeleteLimits.ts b/nodes/LinkedApi/operations/admin/AdminDeleteLimits.ts index 41f2572..e7e1179 100644 --- a/nodes/LinkedApi/operations/admin/AdminDeleteLimits.ts +++ b/nodes/LinkedApi/operations/admin/AdminDeleteLimits.ts @@ -26,7 +26,7 @@ export class AdminDeleteLimits extends AdminLinkedApiOperation { required: true, default: '[{"category": "stMessages", "period": "daily"}]', description: - 'JSON array of limit keys to delete. Each entry: { category, period (daily/weekly/monthly) }.', + 'JSON array of limit keys to delete. Each entry: { category, period (daily/weekly/monthly) }. Categories: stPersonProfileViews, stCompanyPageViews, stConnectionRequests, stMessages, stSearchQueries, stReactions, stEndorsements, stComments, stPosts, nvPersonProfileViews, nvCompanyPageViews, nvMessages.', }, this.show, ), diff --git a/nodes/LinkedApi/operations/admin/AdminSetLimits.ts b/nodes/LinkedApi/operations/admin/AdminSetLimits.ts index 59628b4..7dafe82 100644 --- a/nodes/LinkedApi/operations/admin/AdminSetLimits.ts +++ b/nodes/LinkedApi/operations/admin/AdminSetLimits.ts @@ -27,7 +27,7 @@ export class AdminSetLimits extends AdminLinkedApiOperation { default: '[{"category": "stMessages", "period": "daily", "maxValue": 25, "isEnabled": true}]', description: - 'JSON array of limit configurations. Each entry: { category, period (daily/weekly/monthly), maxValue, isEnabled? }. Categories: stPersonProfileViews, stCompanyPageViews, stConnectionRequests, stMessages, stSearchQueries, stReactions, stComments, stPosts, nvPersonProfileViews, nvCompanyPageViews, nvMessages.', + 'JSON array of limit configurations. Each entry: { category, period (daily/weekly/monthly), maxValue, isEnabled? }. Categories: stPersonProfileViews, stCompanyPageViews, stConnectionRequests, stMessages, stSearchQueries, stReactions, stEndorsements, stComments, stPosts, nvPersonProfileViews, nvCompanyPageViews, nvMessages.', }, this.show, ), diff --git a/nodes/LinkedApi/operations/webhook/EndorsePersonSkill.ts b/nodes/LinkedApi/operations/webhook/EndorsePersonSkill.ts new file mode 100644 index 0000000..4470347 --- /dev/null +++ b/nodes/LinkedApi/operations/webhook/EndorsePersonSkill.ts @@ -0,0 +1,33 @@ +import type { IExecuteFunctions, INodeProperties } from 'n8n-workflow'; +import { + createParameterWithDisplayOptions, + personUrlParameter, +} from '../../shared/SharedParameters'; +import { StandardLinkedApiOperation } from '../../shared/LinkedApiOperation'; +import { AVAILABLE_ACTION } from '../../shared/AvailableActions'; + +export class EndorsePersonSkill extends StandardLinkedApiOperation { + operationName = AVAILABLE_ACTION.endorsePersonSkill; + + fields: INodeProperties[] = [ + createParameterWithDisplayOptions(personUrlParameter, this.show), + { + displayName: 'Skill Name', + name: 'skillName', + type: 'string', + required: true, + default: '', + displayOptions: { show: this.show }, + placeholder: 'Project Management', + description: + "Name of the skill to endorse, up to 200 characters. Works only for your 1st-degree connections. It must equal the skill's name on the person's profile; letter case and extra spaces are ignored. It is not a pattern. Skill names are shown in the profile owner's language, whatever your own LinkedIn language: a German profile lists Projektmanagement, not Project Management. A skill you have already endorsed is left as it is and counts as a success. Every successful endorsement counts toward the stEndorsements limit.", + }, + ]; + + public body(context: IExecuteFunctions): Record { + return { + personUrl: this.stringParameter(context, 'personUrl'), + skillName: this.stringParameter(context, 'skillName'), + }; + } +} diff --git a/nodes/LinkedApi/operations/webhook/EndorsePersonSkills.ts b/nodes/LinkedApi/operations/webhook/EndorsePersonSkills.ts new file mode 100644 index 0000000..3811f01 --- /dev/null +++ b/nodes/LinkedApi/operations/webhook/EndorsePersonSkills.ts @@ -0,0 +1,89 @@ +import { IExecuteFunctions, INodeProperties, NodeOperationError } from 'n8n-workflow'; +import { + createParameterWithDisplayOptions, + personUrlParameter, +} from '../../shared/SharedParameters'; +import { StandardLinkedApiOperation } from '../../shared/LinkedApiOperation'; +import { AVAILABLE_ACTION } from '../../shared/AvailableActions'; + +export class EndorsePersonSkills extends StandardLinkedApiOperation { + operationName = AVAILABLE_ACTION.endorsePersonSkills; + + fields: INodeProperties[] = [ + createParameterWithDisplayOptions(personUrlParameter, this.show), + { + displayName: 'Endorse All Skills', + name: 'endorseAllSkills', + type: 'boolean', + default: false, + displayOptions: { show: this.show }, + description: + 'Whether to endorse every skill of the person instead of only the skills matching your patterns', + }, + { + displayName: 'Skill Name Patterns', + name: 'skillNamePatterns', + type: 'string', + typeOptions: { + multipleValues: true, + multipleValueButtonText: 'Add Pattern', + }, + required: true, + default: [], + displayOptions: { show: { ...this.show, endorseAllSkills: [false] } }, + placeholder: 'project\\s*manage', + description: + "Regular expressions that select skills by name; a skill is selected when any of them matches. At least one is required: to endorse every skill, turn on Endorse All Skills instead. The syntax is RE2: lookaround and backreferences are not supported. Matching is case-insensitive and searches anywhere in the name: manage matches Project Management. Anchor a pattern with ^…$ to require the whole name: ^scrum$ matches Scrum but not Scrum Master. Up to 20 patterns of up to 200 characters each; leading and trailing spaces are ignored. Skill names are shown in the profile owner's language, whatever your own LinkedIn language, so write patterns in the language of the profiles you target. Works only for your 1st-degree connections. A skill you have already endorsed counts as a success, and every successful endorsement counts toward the stEndorsements limit.", + }, + ]; + + public body(context: IExecuteFunctions): Record { + const body: Record = { + personUrl: this.stringParameter(context, 'personUrl'), + }; + + // Only a real true endorses everything: an expression resolving to the text "FALSE" is truthy. + if (context.getNodeParameter('endorseAllSkills', this.itemIndex) === true) { + return body; + } + + const rows = context.getNodeParameter('skillNamePatterns', this.itemIndex, []) as unknown[]; + const skillNamePatterns: string[] = []; + + for (const [index, row] of rows.entries()) { + if (row === null || row === undefined) { + continue; + } + + // String() would turn a mapped object into "[object Object]", a valid RE2 character class + // that selects most skills. + if (typeof row !== 'string') { + throw new NodeOperationError( + context.getNode(), + `Skill name pattern ${index + 1} is not text. Map the skill name text, not a whole item or list.`, + { itemIndex: this.itemIndex }, + ); + } + + const pattern = row.trim(); + + if (pattern !== '') { + skillNamePatterns.push(pattern); + } + } + + // An omitted skillNamePatterns endorses every skill, which cannot be undone; a blank row or an + // expression that resolved to nothing must fail instead of silently widening to all skills. + if (skillNamePatterns.length === 0) { + throw new NodeOperationError( + context.getNode(), + 'Add at least one skill name pattern, or turn on "Endorse All Skills" to endorse every skill', + { itemIndex: this.itemIndex }, + ); + } + + body.skillNamePatterns = skillNamePatterns; + + return body; + } +} diff --git a/nodes/LinkedApi/operations/webhook/index.ts b/nodes/LinkedApi/operations/webhook/index.ts index f817764..73aa5fb 100644 --- a/nodes/LinkedApi/operations/webhook/index.ts +++ b/nodes/LinkedApi/operations/webhook/index.ts @@ -3,6 +3,8 @@ export * from './CommentOnPost'; export * from './CreatePost'; export * from './CreateRepost'; export * from './CustomWorkflow'; +export * from './EndorsePersonSkill'; +export * from './EndorsePersonSkills'; export * from './FetchCompany'; export * from './FetchPerson'; export * from './FetchPost'; diff --git a/nodes/LinkedApi/shared/AvailableActions.ts b/nodes/LinkedApi/shared/AvailableActions.ts index 56ab360..9a3c081 100644 --- a/nodes/LinkedApi/shared/AvailableActions.ts +++ b/nodes/LinkedApi/shared/AvailableActions.ts @@ -9,6 +9,8 @@ export const AVAILABLE_ACTION = { replyToComment: 'replyToComment', createPost: 'createPost', createRepost: 'createRepost', + endorsePersonSkill: 'endorsePersonSkill', + endorsePersonSkills: 'endorsePersonSkills', fetchCompany: 'fetchCompany', fetchPerson: 'fetchPerson', fetchPost: 'fetchPost', @@ -106,6 +108,19 @@ export const availableStandardOperations: INodeProperties = { description: 'Repost a LinkedIn post, as is or with your own commentary', action: 'Create repost', }, + { + name: 'Endorse Person Skill', + value: AVAILABLE_ACTION.endorsePersonSkill, + description: 'Endorse one skill, by its exact name, of a 1st-degree connection', + action: 'Endorse person skill', + }, + { + name: 'Endorse Person Skills', + value: AVAILABLE_ACTION.endorsePersonSkills, + description: + 'Endorse every skill of a 1st-degree connection whose name matches your patterns, or all of their skills', + action: 'Endorse person skills', + }, { name: 'Fetch Company', value: AVAILABLE_ACTION.fetchCompany, diff --git a/package-lock.json b/package-lock.json index 3a17ead..4c8f391 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "n8n-nodes-linked-api", - "version": "1.0.12", + "version": "1.0.15", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "n8n-nodes-linked-api", - "version": "1.0.12", + "version": "1.0.15", "license": "MIT", "devDependencies": { "@types/node": "^20.19.11", diff --git a/package.json b/package.json index b6537bb..3c01dca 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "n8n-nodes-linked-api", - "version": "1.0.14", + "version": "1.0.15", "description": "Connect your LinkedIn accounts to n8n's automation platform.", "keywords": [ "n8n-community-node-package",