-
Notifications
You must be signed in to change notification settings - Fork 1
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat(linkedIn): LinkedIn post video action in public template (#104)
## Describe your changes ## Issue ticket number and link ## Checklist before requesting a review (skip if just adding/editing APIs & templates) - [ ] I added tests, otherwise the reason is: - [ ] External API requests have `retries` - [ ] Pagination is used where appropriate - [ ] The built in `nango.paginate` call is used instead of a `while (true)` loop - [ ] Third party requests are NOT parallelized (this can cause issues with rate limits) - [ ] If a sync requires metadata the `nango.yaml` has `auto_start: false` - [ ] If the sync is a `full` sync then `track_deletes: true` is set - [ ] I followed the best practices and guidelines from the [Writing Integration Scripts](/NangoHQ/integration-templates/blob/main/WRITING_INTEGRATION_SCRIPTS.md) doc --------- Co-authored-by: Khaliq <khaliqgant@gmail.com>
- Loading branch information
1 parent
290a958
commit 95ceb42
Showing
11 changed files
with
425 additions
and
46 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,53 +1,52 @@ | ||
import { vi, expect, it, describe } from "vitest"; | ||
import { vi, expect, it, describe } from 'vitest'; | ||
|
||
import fetchData from "../syncs/tickets.js"; | ||
import fetchData from '../syncs/tickets.js'; | ||
|
||
describe("gorgias tickets tests", () => { | ||
const nangoMock = new global.vitest.NangoSyncMock({ | ||
dirname: __dirname, | ||
name: "tickets", | ||
Model: "Ticket" | ||
}); | ||
describe('gorgias tickets tests', () => { | ||
const nangoMock = new global.vitest.NangoSyncMock({ | ||
dirname: __dirname, | ||
name: 'tickets', | ||
Model: 'Ticket' | ||
}); | ||
|
||
const models = "Ticket".split(','); | ||
const batchSaveSpy = vi.spyOn(nangoMock, 'batchSave'); | ||
const models = 'Ticket'.split(','); | ||
const batchSaveSpy = vi.spyOn(nangoMock, 'batchSave'); | ||
|
||
it("should get, map correctly the data and batchSave the result", async () => { | ||
await fetchData(nangoMock); | ||
it('should get, map correctly the data and batchSave the result', async () => { | ||
await fetchData(nangoMock); | ||
|
||
for (const model of models) { | ||
const batchSaveData = await nangoMock.getBatchSaveData(model); | ||
for (const model of models) { | ||
const batchSaveData = await nangoMock.getBatchSaveData(model); | ||
|
||
const totalCalls = batchSaveSpy.mock.calls.length; | ||
const totalCalls = batchSaveSpy.mock.calls.length; | ||
|
||
if (totalCalls > models.length) { | ||
const splitSize = Math.ceil(batchSaveData.length / totalCalls); | ||
if (totalCalls > models.length) { | ||
const splitSize = Math.ceil(batchSaveData.length / totalCalls); | ||
|
||
const splitBatchSaveData = []; | ||
for (let i = 0; i < totalCalls; i++) { | ||
const chunk = batchSaveData.slice(i * splitSize, (i + 1) * splitSize); | ||
splitBatchSaveData.push(chunk); | ||
const splitBatchSaveData = []; | ||
for (let i = 0; i < totalCalls; i++) { | ||
const chunk = batchSaveData.slice(i * splitSize, (i + 1) * splitSize); | ||
splitBatchSaveData.push(chunk); | ||
} | ||
|
||
splitBatchSaveData.forEach((data, index) => { | ||
// @ts-ignore | ||
expect(batchSaveSpy?.mock.calls[index][0]).toEqual(data); | ||
}); | ||
} else { | ||
expect(nangoMock.batchSave).toHaveBeenCalledWith(batchSaveData, model); | ||
} | ||
} | ||
}); | ||
|
||
splitBatchSaveData.forEach((data, index) => { | ||
// @ts-ignore | ||
expect(batchSaveSpy?.mock.calls[index][0]).toEqual(data); | ||
}); | ||
it('should get, map correctly the data and batchDelete the result', async () => { | ||
await fetchData(nangoMock); | ||
|
||
} else { | ||
expect(nangoMock.batchSave).toHaveBeenCalledWith(batchSaveData, model); | ||
for (const model of models) { | ||
const batchDeleteData = await nangoMock.getBatchDeleteData(model); | ||
if (batchDeleteData && batchDeleteData.length > 0) { | ||
expect(nangoMock.batchDelete).toHaveBeenCalledWith(batchDeleteData, model); | ||
} | ||
} | ||
} | ||
}); | ||
|
||
it('should get, map correctly the data and batchDelete the result', async () => { | ||
await fetchData(nangoMock); | ||
|
||
for (const model of models) { | ||
const batchDeleteData = await nangoMock.getBatchDeleteData(model); | ||
if (batchDeleteData && batchDeleteData.length > 0) { | ||
expect(nangoMock.batchDelete).toHaveBeenCalledWith(batchDeleteData, model); | ||
} | ||
} | ||
}); | ||
}); | ||
}); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,23 @@ | ||
import type { CreateLinkedInPostWithVideoResponse, NangoAction, LinkedinVideoPost } from '../../models'; | ||
import { createPostWithVideo } from '../helpers/post-video.js'; | ||
import { userInfo } from '../helpers/user-info.js'; | ||
|
||
export default async function runAction(nango: NangoAction, input: LinkedinVideoPost): Promise<CreateLinkedInPostWithVideoResponse> { | ||
const videoURN = input?.videoURN; | ||
let ownerId = input?.ownerId; | ||
|
||
if (!ownerId) { | ||
const me = await userInfo(nango); | ||
ownerId = me.sub; | ||
} | ||
|
||
if (videoURN && !videoURN.startsWith('urn')) { | ||
throw new nango.ActionError({ | ||
message: `invalid video urn` | ||
}); | ||
} | ||
|
||
const resp = await createPostWithVideo(nango, ownerId, input.text, input.videoTitle, videoURN); | ||
|
||
return resp; | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,3 @@ | ||
{ | ||
"text": "test post 123" | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,55 @@ | ||
import type { NangoAction, CreateLinkedInPostWithVideoResponse, ProxyConfiguration } from '../../models'; | ||
import type { LinkedinCreatePost } from '../types.js'; | ||
|
||
export async function createPostWithVideo( | ||
nango: NangoAction, | ||
author: string, | ||
postText: string, | ||
videoTitle: string, | ||
videoURN?: string | ||
): Promise<CreateLinkedInPostWithVideoResponse> { | ||
const postData: LinkedinCreatePost = { | ||
author: `urn:li:person:${author}`, | ||
commentary: postText, | ||
visibility: 'PUBLIC', | ||
distribution: { | ||
feedDistribution: 'MAIN_FEED', | ||
targetEntities: [], | ||
thirdPartyDistributionChannels: [] | ||
}, | ||
lifecycleState: 'PUBLISHED', | ||
isReshareDisabledByAuthor: false | ||
}; | ||
|
||
if (videoURN) { | ||
postData.content = { | ||
media: { | ||
title: videoTitle, | ||
// video that is already uploaded to linkedin api. this id can be video, image or document urn. | ||
id: videoURN | ||
} | ||
}; | ||
} | ||
|
||
const config: ProxyConfiguration = { | ||
// https://learn.microsoft.com/en-us/linkedin/marketing/community-management/shares/posts-api?view=li-lms-2024-10&tabs=http | ||
endpoint: `/rest/posts`, | ||
retries: 10, | ||
data: postData, | ||
headers: { | ||
'LinkedIn-Version': '202405' | ||
} | ||
}; | ||
|
||
const response = await nango.post(config); | ||
|
||
if (response.status !== 200) { | ||
throw new nango.ActionError({ | ||
message: `failed to create post with video urn ${videoURN}` | ||
}); | ||
} | ||
|
||
return { | ||
succcess: response.status == 200 ? true : false | ||
}; | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,19 @@ | ||
import type { NangoAction, ProxyConfiguration } from '../../models'; | ||
import type { LinkedInUserInfo } from '../types.js'; | ||
|
||
export async function userInfo(nango: NangoAction): Promise<LinkedInUserInfo> { | ||
const config: ProxyConfiguration = { | ||
// https://learn.microsoft.com/en-us/linkedin/shared/integrations/people/profile-api | ||
endpoint: '/v2/userinfo', | ||
retries: 10, | ||
headers: { | ||
'LinkedIn-Version': '202405' | ||
} | ||
}; | ||
|
||
const response = await nango.get(config); | ||
|
||
const { data } = response; | ||
|
||
return data; | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,26 @@ | ||
integrations: | ||
linkedin: | ||
actions: | ||
post: | ||
description: Create a linkedin post with an optional video | ||
input: LinkedinVideoPost | ||
output: CreateLinkedInPostWithVideoResponse | ||
endpoint: POST /videos | ||
scopes: | ||
- openid | ||
- profile | ||
- r_basicprofile | ||
- w_member_social | ||
- w_organization_social | ||
- r_organization_social | ||
|
||
models: | ||
LinkedinVideoPost: | ||
text: string | ||
videoURN: string | ||
videoTitle: string | ||
ownerId: string | ||
|
||
CreateLinkedInPostWithVideoResponse: | ||
succcess: boolean |
Oops, something went wrong.