Skip to content

Commit

Permalink
fix(LinkedIn Node): Update the version of the API (n8n-io#5720)
Browse files Browse the repository at this point in the history
* 🐛 Change request to follow new API version

* Extract urn from response header

* Change body params for image and media request

* Fix body for Image and Article posts

* remove console log

---------

Co-authored-by: Marcus <[email protected]>
  • Loading branch information
2 people authored and believe-Mahesh committed Apr 4, 2023
1 parent 166ab18 commit f613655
Show file tree
Hide file tree
Showing 2 changed files with 54 additions and 93 deletions.
24 changes: 18 additions & 6 deletions packages/nodes-base/nodes/LinkedIn/GenericFunctions.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand All @@ -18,17 +25,20 @@ export async function linkedInApiRequest(
binary?: boolean,
_headers?: object,
): Promise<any> {
const options: OptionsWithUrl = {
let options: OptionsWithUrl = {
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,
};

options = Object.assign({}, options, {
resolveWithFullResponse: true,
});
// If uploading binary data
if (binary) {
delete options.json;
Expand All @@ -40,9 +50,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);
}
Expand Down
123 changes: 36 additions & 87 deletions packages/nodes-base/nodes/LinkedIn/LinkedIn.node.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -111,66 +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;

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);

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,
},
},
],
const { uploadUrl, image } = registerObject.value;
await linkedInApiRequest.call(this, 'POST', uploadUrl as string, buffer, true);

const imageBody = {
content: {
media: {
title,
id: image,
description,
},
},
visibility: {
'com.linkedin.ugc.MemberNetworkVisibility': visibility,
},
commentary: text,
};
Object.assign(body, imageBody);
} else if (shareMediaCategory === 'ARTICLE') {
if (additionalFields.description) {
description = additionalFields.description as string;
Expand All @@ -182,60 +163,28 @@ export class LinkedIn implements INodeType {
originalUrl = additionalFields.originalUrl as string;
}

body = {
author: `${authorUrn}`,
lifecycleState: 'PUBLISHED',
specificContent: {
'com.linkedin.ugc.ShareContent': {
shareCommentary: {
text,
},
shareMediaCategory,
media: [
{
status: 'READY',
description: {
text: description,
},
originalUrl,
title: {
text: title,
},
},
],
const articleBody = {
content: {
article: {
title,
description,
source: originalUrl,
},
},
visibility: {
'com.linkedin.ugc.MemberNetworkVisibility': visibility,
},
commentary: text,
};

Object.assign(body, articleBody);
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',
specificContent: {
'com.linkedin.ugc.ShareContent': {
shareCommentary: {
text,
},
shareMediaCategory,
},
},
visibility: {
'com.linkedin.ugc.MemberNetworkVisibility': visibility,
},
};
Object.assign(body, { commentary: text });
}

const endpoint = '/ugcPosts';
const endpoint = '/posts';
responseData = await linkedInApiRequest.call(this, 'POST', endpoint, body);
}
}
Expand Down

0 comments on commit f613655

Please sign in to comment.