From 91654f7499d05823274405d7c8edd130fcbea75b Mon Sep 17 00:00:00 2001 From: agobrech Date: Fri, 17 Mar 2023 17:24:34 +0100 Subject: [PATCH 1/5] =?UTF-8?q?=F0=9F=90=9B=20Change=20request=20to=20foll?= =?UTF-8?q?ow=20new=20API=20version?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../nodes/LinkedIn/GenericFunctions.ts | 3 ++- .../nodes/LinkedIn/LinkedIn.node.ts | 19 ++++++++----------- 2 files changed, 10 insertions(+), 12 deletions(-) diff --git a/packages/nodes-base/nodes/LinkedIn/GenericFunctions.ts b/packages/nodes-base/nodes/LinkedIn/GenericFunctions.ts index 7dd7ea2f08adc..b688a9e6ca7dd 100644 --- a/packages/nodes-base/nodes/LinkedIn/GenericFunctions.ts +++ b/packages/nodes-base/nodes/LinkedIn/GenericFunctions.ts @@ -22,10 +22,11 @@ export async function linkedInApiRequest( headers: { Accept: 'application/json', 'X-Restli-Protocol-Version': '2.0.0', + 'LinkedIn-Version': '202301', }, method, body, - url: binary ? endpoint : `https://api.linkedin.com/v2${endpoint}`, + url: binary ? endpoint : `https://api.linkedin.com/rest${endpoint}`, json: true, }; diff --git a/packages/nodes-base/nodes/LinkedIn/LinkedIn.node.ts b/packages/nodes-base/nodes/LinkedIn/LinkedIn.node.ts index 72ca14795bf55..c27d18961a298 100644 --- a/packages/nodes-base/nodes/LinkedIn/LinkedIn.node.ts +++ b/packages/nodes-base/nodes/LinkedIn/LinkedIn.node.ts @@ -221,21 +221,18 @@ export class LinkedIn implements INodeType { body = { author: authorUrn, lifecycleState: 'PUBLISHED', - specificContent: { - 'com.linkedin.ugc.ShareContent': { - shareCommentary: { - text, - }, - shareMediaCategory, - }, - }, - visibility: { - 'com.linkedin.ugc.MemberNetworkVisibility': visibility, + commentary: text, + distribution: { + feedDistribution: 'MAIN_FEED', + targetEnties: [], + thirdPartyDistributionChannels: [], }, + visibility, }; } - const endpoint = '/ugcPosts'; + const endpoint = '/posts'; + console.log(body); responseData = await linkedInApiRequest.call(this, 'POST', endpoint, body); } } From c49b9c85315dc827c3171cf52997099696ea591a Mon Sep 17 00:00:00 2001 From: agobrech Date: Thu, 30 Mar 2023 12:01:42 +0200 Subject: [PATCH 2/5] Extract urn from response header --- .../nodes/LinkedIn/GenericFunctions.ts | 20 +++++++++++++++---- .../nodes/LinkedIn/LinkedIn.node.ts | 2 +- 2 files changed, 17 insertions(+), 5 deletions(-) diff --git a/packages/nodes-base/nodes/LinkedIn/GenericFunctions.ts b/packages/nodes-base/nodes/LinkedIn/GenericFunctions.ts index b688a9e6ca7dd..10025a6a97591 100644 --- a/packages/nodes-base/nodes/LinkedIn/GenericFunctions.ts +++ b/packages/nodes-base/nodes/LinkedIn/GenericFunctions.ts @@ -8,6 +8,13 @@ import type { JsonObject, } from 'n8n-workflow'; import { NodeApiError } from 'n8n-workflow'; +function resolveHeaderData(fullResponse: any) { + if (fullResponse.statusCode === 201) { + return { urn: fullResponse.headers['x-restli-id'] }; + } else { + return fullResponse.body; + } +} export async function linkedInApiRequest( this: IHookFunctions | IExecuteFunctions | ILoadOptionsFunctions, @@ -18,7 +25,7 @@ export async function linkedInApiRequest( binary?: boolean, _headers?: object, ): Promise { - const options: OptionsWithUrl = { + let options: OptionsWithUrl = { headers: { Accept: 'application/json', 'X-Restli-Protocol-Version': '2.0.0', @@ -30,6 +37,9 @@ export async function linkedInApiRequest( json: true, }; + options = Object.assign({}, options, { + resolveWithFullResponse: true, + }); // If uploading binary data if (binary) { delete options.json; @@ -41,9 +51,11 @@ export async function linkedInApiRequest( } try { - return await this.helpers.requestOAuth2.call(this, 'linkedInOAuth2Api', options, { - tokenType: 'Bearer', - }); + return resolveHeaderData( + await this.helpers.requestOAuth2.call(this, 'linkedInOAuth2Api', options, { + tokenType: 'Bearer', + }), + ); } catch (error) { throw new NodeApiError(this.getNode(), error as JsonObject); } diff --git a/packages/nodes-base/nodes/LinkedIn/LinkedIn.node.ts b/packages/nodes-base/nodes/LinkedIn/LinkedIn.node.ts index c27d18961a298..69ef5c460469e 100644 --- a/packages/nodes-base/nodes/LinkedIn/LinkedIn.node.ts +++ b/packages/nodes-base/nodes/LinkedIn/LinkedIn.node.ts @@ -232,8 +232,8 @@ export class LinkedIn implements INodeType { } const endpoint = '/posts'; - console.log(body); responseData = await linkedInApiRequest.call(this, 'POST', endpoint, body); + console.log(responseData); } } const executionData = this.helpers.constructExecutionMetaData( From 11cf150ddd058524e2ced3d74bfe672ddb0c1a9d Mon Sep 17 00:00:00 2001 From: agobrech Date: Thu, 30 Mar 2023 12:33:21 +0200 Subject: [PATCH 3/5] Change body params for image and media request --- .../nodes/LinkedIn/LinkedIn.node.ts | 85 +++++-------------- 1 file changed, 23 insertions(+), 62 deletions(-) diff --git a/packages/nodes-base/nodes/LinkedIn/LinkedIn.node.ts b/packages/nodes-base/nodes/LinkedIn/LinkedIn.node.ts index 69ef5c460469e..97306035b6a57 100644 --- a/packages/nodes-base/nodes/LinkedIn/LinkedIn.node.ts +++ b/packages/nodes-base/nodes/LinkedIn/LinkedIn.node.ts @@ -102,6 +102,17 @@ export class LinkedIn implements INodeType { let title = ''; let originalUrl = ''; + body = { + author: authorUrn, + lifecycleState: 'PUBLISHED', + distribution: { + feedDistribution: 'MAIN_FEED', + targetEnties: [], + thirdPartyDistributionChannels: [], + }, + visibility, + }; + if (shareMediaCategory === 'IMAGE') { if (additionalFields.description) { description = additionalFields.description as string; @@ -145,31 +156,13 @@ export class LinkedIn implements INodeType { await linkedInApiRequest.call(this, 'POST', uploadUrl, buffer, true); body = { - author: authorUrn, - lifecycleState: 'PUBLISHED', - specificContent: { - 'com.linkedin.ugc.ShareContent': { - shareCommentary: { - text, - }, - shareMediaCategory: 'IMAGE', - media: [ - { - status: 'READY', - description: { - text: description, - }, - media: asset, - title: { - text: title, - }, - }, - ], + content: { + media: { + title, + id: asset, + description, }, }, - visibility: { - 'com.linkedin.ugc.MemberNetworkVisibility': visibility, - }, }; } else if (shareMediaCategory === 'ARTICLE') { if (additionalFields.description) { @@ -183,57 +176,25 @@ export class LinkedIn implements INodeType { } body = { - author: `${authorUrn}`, - lifecycleState: 'PUBLISHED', - specificContent: { - 'com.linkedin.ugc.ShareContent': { - shareCommentary: { - text, - }, - shareMediaCategory, - media: [ - { - status: 'READY', - description: { - text: description, - }, - originalUrl, - title: { - text: title, - }, - }, - ], - }, - }, - visibility: { - 'com.linkedin.ugc.MemberNetworkVisibility': visibility, + content: { + title, + description, + source: originalUrl, }, }; if (description === '') { - delete body.specificContent['com.linkedin.ugc.ShareContent'].media[0].description; + delete body.description; } if (title === '') { - delete body.specificContent['com.linkedin.ugc.ShareContent'].media[0].title; + delete body.title; } } else { - body = { - author: authorUrn, - lifecycleState: 'PUBLISHED', - commentary: text, - distribution: { - feedDistribution: 'MAIN_FEED', - targetEnties: [], - thirdPartyDistributionChannels: [], - }, - visibility, - }; + Object.assign(body, { commentary: text }); } - const endpoint = '/posts'; responseData = await linkedInApiRequest.call(this, 'POST', endpoint, body); - console.log(responseData); } } const executionData = this.helpers.constructExecutionMetaData( From 60d48224ea6dbf3b9d1031c047649006edc81de6 Mon Sep 17 00:00:00 2001 From: agobrech Date: Thu, 30 Mar 2023 17:45:55 +0200 Subject: [PATCH 4/5] Fix body for Image and Article posts --- .../nodes/LinkedIn/GenericFunctions.ts | 1 - .../nodes/LinkedIn/LinkedIn.node.ts | 43 ++++++++----------- 2 files changed, 17 insertions(+), 27 deletions(-) diff --git a/packages/nodes-base/nodes/LinkedIn/GenericFunctions.ts b/packages/nodes-base/nodes/LinkedIn/GenericFunctions.ts index 10025a6a97591..1c1caf9047b9e 100644 --- a/packages/nodes-base/nodes/LinkedIn/GenericFunctions.ts +++ b/packages/nodes-base/nodes/LinkedIn/GenericFunctions.ts @@ -36,7 +36,6 @@ export async function linkedInApiRequest( url: binary ? endpoint : `https://api.linkedin.com/rest${endpoint}`, json: true, }; - options = Object.assign({}, options, { resolveWithFullResponse: true, }); diff --git a/packages/nodes-base/nodes/LinkedIn/LinkedIn.node.ts b/packages/nodes-base/nodes/LinkedIn/LinkedIn.node.ts index 97306035b6a57..4771927e004ca 100644 --- a/packages/nodes-base/nodes/LinkedIn/LinkedIn.node.ts +++ b/packages/nodes-base/nodes/LinkedIn/LinkedIn.node.ts @@ -122,48 +122,36 @@ export class LinkedIn implements INodeType { } // Send a REQUEST to prepare a register of a media image file const registerRequest = { - registerUploadRequest: { - recipes: ['urn:li:digitalmediaRecipe:feedshare-image'], + initializeUploadRequest: { owner: authorUrn, - serviceRelationships: [ - { - relationshipType: 'OWNER', - identifier: 'urn:li:userGeneratedContent', - }, - ], }, }; const registerObject = await linkedInApiRequest.call( this, 'POST', - '/assets?action=registerUpload', + '/images?action=initializeUpload', registerRequest, ); - - // Response provides a specific upload URL that is used to upload the binary image file - const uploadUrl = registerObject.value.uploadMechanism[ - 'com.linkedin.digitalmedia.uploading.MediaUploadHttpRequest' - ].uploadUrl as string; - const asset = registerObject.value.asset as string; - + console.log(registerObject); const binaryPropertyName = this.getNodeParameter('binaryPropertyName', i); this.helpers.assertBinaryData(i, binaryPropertyName); - // Buffer binary data const buffer = await this.helpers.getBinaryDataBuffer(i, binaryPropertyName); - // Upload image - await linkedInApiRequest.call(this, 'POST', uploadUrl, buffer, true); + const { uploadUrl, image } = registerObject.value; + await linkedInApiRequest.call(this, 'POST', uploadUrl as string, buffer, true); - body = { + const imageBody = { content: { media: { title, - id: asset, + id: image, description, }, }, + commentary: text, }; + Object.assign(body, imageBody); } else if (shareMediaCategory === 'ARTICLE') { if (additionalFields.description) { description = additionalFields.description as string; @@ -175,14 +163,17 @@ export class LinkedIn implements INodeType { originalUrl = additionalFields.originalUrl as string; } - body = { + const articleBody = { content: { - title, - description, - source: originalUrl, + article: { + title, + description, + source: originalUrl, + }, }, + commentary: text, }; - + Object.assign(body, articleBody); if (description === '') { delete body.description; } From 98c8dae57145655d2cc84c3f4b8a039a1b1ad1c5 Mon Sep 17 00:00:00 2001 From: Marcus Date: Fri, 31 Mar 2023 14:23:01 +0200 Subject: [PATCH 5/5] remove console log --- packages/nodes-base/nodes/LinkedIn/LinkedIn.node.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/nodes-base/nodes/LinkedIn/LinkedIn.node.ts b/packages/nodes-base/nodes/LinkedIn/LinkedIn.node.ts index 4771927e004ca..434fa2cc8c8f9 100644 --- a/packages/nodes-base/nodes/LinkedIn/LinkedIn.node.ts +++ b/packages/nodes-base/nodes/LinkedIn/LinkedIn.node.ts @@ -133,7 +133,7 @@ export class LinkedIn implements INodeType { '/images?action=initializeUpload', registerRequest, ); - console.log(registerObject); + const binaryPropertyName = this.getNodeParameter('binaryPropertyName', i); this.helpers.assertBinaryData(i, binaryPropertyName);