Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 11 additions & 0 deletions nodes/LinkedApi/LinkedApi.node.ts
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,8 @@ import {
CommentOnPost,
CreatePost,
CreateRepost,
EndorsePersonSkill,
EndorsePersonSkills,
FetchCompany,
FetchPerson,
FetchPost,
Expand Down Expand Up @@ -84,6 +86,8 @@ const operations: Record<string, LinkedApiOperation> = {
commentOnPost: new CommentOnPost(),
createPost: new CreatePost(),
createRepost: new CreateRepost(),
endorsePersonSkill: new EndorsePersonSkill(),
endorsePersonSkills: new EndorsePersonSkills(),
fetchPerson: new FetchPerson(),
fetchCompany: new FetchCompany(),
fetchPost: new FetchPost(),
Expand Down Expand Up @@ -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,
Expand All @@ -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,
Expand All @@ -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,
Expand All @@ -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
Expand Down
2 changes: 1 addition & 1 deletion nodes/LinkedApi/operations/admin/AdminDeleteLimits.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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,
),
Expand Down
2 changes: 1 addition & 1 deletion nodes/LinkedApi/operations/admin/AdminSetLimits.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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,
),
Expand Down
33 changes: 33 additions & 0 deletions nodes/LinkedApi/operations/webhook/EndorsePersonSkill.ts
Original file line number Diff line number Diff line change
@@ -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<string, any> {
return {
personUrl: this.stringParameter(context, 'personUrl'),
skillName: this.stringParameter(context, 'skillName'),
};
}
}
89 changes: 89 additions & 0 deletions nodes/LinkedApi/operations/webhook/EndorsePersonSkills.ts
Original file line number Diff line number Diff line change
@@ -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<string, any> {
const body: Record<string, any> = {
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;
}
}
2 changes: 2 additions & 0 deletions nodes/LinkedApi/operations/webhook/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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';
Expand Down
15 changes: 15 additions & 0 deletions nodes/LinkedApi/shared/AvailableActions.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,8 @@ export const AVAILABLE_ACTION = {
replyToComment: 'replyToComment',
createPost: 'createPost',
createRepost: 'createRepost',
endorsePersonSkill: 'endorsePersonSkill',
endorsePersonSkills: 'endorsePersonSkills',
fetchCompany: 'fetchCompany',
fetchPerson: 'fetchPerson',
fetchPost: 'fetchPost',
Expand Down Expand Up @@ -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,
Expand Down
4 changes: 2 additions & 2 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -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",
Expand Down
Loading