@pipedream/linkedin 0.1.0 → 0.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (28) hide show
  1. package/README.md +12 -0
  2. package/actions/create-comment/create-comment.mjs +1 -1
  3. package/actions/create-image-post-organization/create-image-post-organization.mjs +1 -1
  4. package/actions/create-image-post-user/create-image-post-user.mjs +1 -1
  5. package/actions/create-like-on-share/create-like-on-share.mjs +1 -1
  6. package/actions/create-text-post-organization/create-text-post-organization.mjs +1 -1
  7. package/actions/create-text-post-user/create-text-post-user.mjs +1 -1
  8. package/actions/delete-post/delete-post.mjs +1 -1
  9. package/actions/fetch-ad-account/fetch-ad-account.mjs +1 -1
  10. package/actions/get-current-member-profile/get-current-member-profile.mjs +1 -1
  11. package/actions/get-member-organization-access-control/get-member-organization-access-control.mjs +1 -1
  12. package/actions/get-member-profile/get-member-profile.mjs +1 -1
  13. package/actions/get-multiple-member-profiles/get-multiple-member-profiles.mjs +1 -1
  14. package/actions/get-organization-access-control/get-organization-access-control.mjs +7 -11
  15. package/actions/get-organization-administrators/get-organization-administrators.mjs +7 -13
  16. package/actions/retrieve-comments-on-comments/retrieve-comments-on-comments.mjs +1 -1
  17. package/actions/retrieve-comments-shares/retrieve-comments-shares.mjs +1 -1
  18. package/actions/search-organization/search-organization.mjs +1 -1
  19. package/common/constants.mjs +14 -1
  20. package/common/utils.mjs +38 -0
  21. package/linkedin.app.mjs +79 -9
  22. package/package.json +2 -2
  23. package/sources/common/polling.mjs +122 -0
  24. package/sources/new-organization-post-created/new-organization-post-created.mjs +82 -0
  25. package/sources/new-organization-post-created/test-event.mjs +18 -0
  26. package/actions/analytics-finder-account-sample/analytics-finder-account-sample.mjs +0 -44
  27. package/actions/analytics-finder-campaign-sample/analytics-finder-campaign-sample.mjs +0 -44
  28. package/actions/query-analytics-finder/query-analytics-finder.mjs +0 -126
package/README.md ADDED
@@ -0,0 +1,12 @@
1
+ # Overview
2
+
3
+ The LinkedIn API on Pipedream allows you to automate interactions with your LinkedIn account, such as posting updates, managing your profile, and engaging with your network. With Pipedream's serverless platform, you can create workflows that trigger on various events, process data, and connect with countless other apps to extend your LinkedIn presence, analyze your network's demographics, automate content sharing, and more.
4
+
5
+ # Example Use Cases
6
+
7
+ - **Automated Content Sharing**: Share blog posts or articles from your company website to LinkedIn as soon as they're published by connecting the RSS feed to LinkedIn using Pipedream's built-in RSS trigger and LinkedIn actions.
8
+
9
+ - **Profile Update Notifications**: Set up a workflow that monitors changes to your LinkedIn profile and sends you or your team an email alert whenever updates are made. This can be useful for maintaining a record of changes or for team transparency about profile edits.
10
+
11
+ - **Scheduled Posts and Engagement Analysis**: Create a workflow that schedules and posts content to LinkedIn at optimal times. Include steps that analyze post engagement using Pipedream's Data Store to track likes, comments, and shares over time, which helps in understanding content performance.
12
+ .
@@ -4,7 +4,7 @@ export default {
4
4
  key: "linkedin-create-comment",
5
5
  name: "Create Comment",
6
6
  description: "Create a comment on a share or user generated content post. [See the docs here](https://docs.microsoft.com/en-us/linkedin/marketing/integrations/community-management/shares/network-update-social-actions#create-comment)",
7
- version: "0.1.4",
7
+ version: "0.1.6",
8
8
  type: "action",
9
9
  props: {
10
10
  linkedin,
@@ -6,7 +6,7 @@ export default {
6
6
  key: "linkedin-create-image-post-organization",
7
7
  name: "Create Image Post (Organization)",
8
8
  description: "Create an image post on LinkedIn. [See the docs here](https://learn.microsoft.com/en-us/linkedin/marketing/integrations/community-management/shares/images-api?view=li-lms-2023-09&tabs=http#uploading-an-image)",
9
- version: "0.0.1",
9
+ version: "0.0.3",
10
10
  type: "action",
11
11
  props: {
12
12
  linkedin,
@@ -6,7 +6,7 @@ export default {
6
6
  key: "linkedin-create-image-post-user",
7
7
  name: "Create Image Post (User)",
8
8
  description: "Create an image post on LinkedIn. [See the docs here](https://learn.microsoft.com/en-us/linkedin/marketing/integrations/community-management/shares/images-api?view=li-lms-2023-09&tabs=http#uploading-an-image)",
9
- version: "0.0.1",
9
+ version: "0.0.3",
10
10
  type: "action",
11
11
  props: {
12
12
  linkedin,
@@ -4,7 +4,7 @@ export default {
4
4
  key: "linkedin-create-like-on-share",
5
5
  name: "Create Like On Share",
6
6
  description: "Creates a like on a share. [See the docs here](https://docs.microsoft.com/en-us/linkedin/marketing/integrations/community-management/shares/network-update-social-actions#create-a-like-on-a-share)",
7
- version: "0.1.4",
7
+ version: "0.1.6",
8
8
  type: "action",
9
9
  props: {
10
10
  linkedin,
@@ -4,7 +4,7 @@ export default {
4
4
  key: "linkedin-create-text-post-organization",
5
5
  name: "Create a Simple Post (Organization)",
6
6
  description: "Create post on LinkedIn using text, URL or article. [See the docs](https://learn.microsoft.com/en-us/linkedin/marketing/integrations/community-management/shares/posts-api?view=li-lms-2022-11&tabs=http#create-organic-posts) for more information",
7
- version: "0.0.4",
7
+ version: "0.0.6",
8
8
  type: "action",
9
9
  props: {
10
10
  linkedin,
@@ -4,7 +4,7 @@ export default {
4
4
  key: "linkedin-create-text-post-user",
5
5
  name: "Create a Simple Post (User)",
6
6
  description: "Create post on LinkedIn using text, URL or article. [See the docs](https://learn.microsoft.com/en-us/linkedin/marketing/integrations/community-management/shares/posts-api?view=li-lms-2022-11&tabs=http#create-organic-posts) for more information",
7
- version: "0.0.4",
7
+ version: "0.0.6",
8
8
  type: "action",
9
9
  props: {
10
10
  linkedin,
@@ -4,7 +4,7 @@ export default {
4
4
  key: "linkedin-delete-post",
5
5
  name: "Delete Post",
6
6
  description: "Removes a post from user's wall. [See the docs](https://docs.microsoft.com/en-us/linkedin/marketing/integrations/community-management/shares/share-api?tabs=http#delete-shares) for more information",
7
- version: "0.0.4",
7
+ version: "0.0.6",
8
8
  type: "action",
9
9
  props: {
10
10
  linkedin,
@@ -4,7 +4,7 @@ export default {
4
4
  key: "linkedin-fetch-ad-account",
5
5
  name: "Fetch Ad Account",
6
6
  description: "Fetches an individual adAccount given its id. [See the docs here](https://docs.microsoft.com/en-us/linkedin/marketing/integrations/ads/account-structure/create-and-manage-accounts#fetch-ad-account)",
7
- version: "0.1.4",
7
+ version: "0.1.6",
8
8
  type: "action",
9
9
  props: {
10
10
  linkedin,
@@ -4,7 +4,7 @@ export default {
4
4
  key: "linkedin-get-current-member-profile",
5
5
  name: "Get Current Member Profile",
6
6
  description: "Gets the profile of the current authenticated member. [See the docs here](https://docs.microsoft.com/en-us/linkedin/shared/integrations/people/profile-api#retrieve-current-members-profile)",
7
- version: "0.1.4",
7
+ version: "0.1.6",
8
8
  type: "action",
9
9
  props: {
10
10
  linkedin,
@@ -4,7 +4,7 @@ export default {
4
4
  key: "linkedin-get-member-organization-access-control",
5
5
  name: "Get Member's Organization Access Control Information",
6
6
  description: "Gets the organization access control information of the current authenticated member. [See the docs here](https://docs.microsoft.com/en-us/linkedin/marketing/integrations/community-management/organizations/organization-access-control?context=linkedin/compliance/context#find-a-members-organization-access-control-information)",
7
- version: "0.1.4",
7
+ version: "0.1.6",
8
8
  type: "action",
9
9
  props: {
10
10
  linkedin,
@@ -4,7 +4,7 @@ export default {
4
4
  key: "linkedin-get-member-profile",
5
5
  name: "Get Member Profile",
6
6
  description: "Gets another member's profile, given its person id. [See the docs here](https://docs.microsoft.com/en-us/linkedin/shared/integrations/people/profile-api#retrieve-other-members-profile)",
7
- version: "0.1.4",
7
+ version: "0.1.6",
8
8
  type: "action",
9
9
  props: {
10
10
  linkedin,
@@ -4,7 +4,7 @@ export default {
4
4
  key: "linkedin-get-multiple-member-profiles",
5
5
  name: "Get Multiple Member Profiles",
6
6
  description: "Gets multiple member profiles at once. [See the docs here](https://docs.microsoft.com/en-us/linkedin/shared/integrations/people/profile-api#retrieve-other-members-profile)",
7
- version: "0.1.4",
7
+ version: "0.1.6",
8
8
  type: "action",
9
9
  props: {
10
10
  linkedin,
@@ -4,7 +4,7 @@ export default {
4
4
  key: "linkedin-get-organization-access-control",
5
5
  name: "Gets Organization Access Control",
6
6
  description: "Gets an organization's access control information, given the organization urn. [See the docs here](https://docs.microsoft.com/en-us/linkedin/marketing/integrations/community-management/organizations/organization-access-control?context=linkedin/compliance/context#find-access-control-information)",
7
- version: "0.1.4",
7
+ version: "0.1.6",
8
8
  type: "action",
9
9
  props: {
10
10
  linkedin,
@@ -21,24 +21,20 @@ export default {
21
21
  },
22
22
  },
23
23
  async run({ $ }) {
24
+ let start = 0;
24
25
  const count = 50;
25
26
  const results = [];
26
27
 
27
- const params = {
28
- q: "organization",
29
- organization: encodeURI(this.organizationUrn),
30
- start: 0,
31
- count,
32
- };
28
+ const params = `q=organization&organization=${this.organizationUrn.replace(/:/g, "%3A")}&count=${count}`;
33
29
 
34
30
  let done = false;
35
31
  do {
36
- const { elements } = await this.linkedin.getAccessControl({
37
- $,
38
- params,
32
+ const { data: { elements } } = await this.linkedin.getAccessControl({
33
+ params: params + `&start=${start}`,
39
34
  });
35
+
40
36
  results.push(...elements);
41
- params.start += count;
37
+ start += count;
42
38
  if (elements?.length < count) {
43
39
  done = true;
44
40
  }
@@ -4,7 +4,7 @@ export default {
4
4
  key: "linkedin-get-organization-administrators",
5
5
  name: "Get Organization Administrators",
6
6
  description: "Gets the administator members of an organization, given the organization urn. [See the docs here](https://docs.microsoft.com/en-us/linkedin/marketing/integrations/community-management/organizations/organization-access-control?context=linkedin/compliance/context#find-organization-administrators)",
7
- version: "0.2.4",
7
+ version: "0.2.6",
8
8
  type: "action",
9
9
  props: {
10
10
  linkedin,
@@ -21,26 +21,20 @@ export default {
21
21
  },
22
22
  },
23
23
  async run({ $ }) {
24
+ let start = 0;
24
25
  const count = 50;
25
26
  const results = [];
26
27
 
27
- const params = {
28
- q: "organization",
29
- organization: encodeURI(this.organizationUrn),
30
- role: "ADMINISTRATOR",
31
- state: "APPROVED",
32
- start: 0,
33
- count,
34
- };
28
+ const params = `q=organization&organization=${this.organizationUrn.replace(/:/g, "%3A")}&role=ADMINISTRATOR&state=APPROVED&count=${count}`;
35
29
 
36
30
  let done = false;
37
31
  do {
38
- const { elements } = await this.linkedin.getAccessControl({
39
- $,
40
- params,
32
+ const { data: { elements } } = await this.linkedin.getAccessControl({
33
+ params: params + `&start=${start}`,
41
34
  });
35
+
42
36
  results.push(...elements);
43
- params.start += count;
37
+ start += count;
44
38
  if (elements?.length < count) {
45
39
  done = true;
46
40
  }
@@ -4,7 +4,7 @@ export default {
4
4
  key: "linkedin-retrieve-comments-on-comments",
5
5
  name: "Retrieves Comments on Comments",
6
6
  description: "Retrieves comments on comments, given the parent comment urn. [See the docs here](https://docs.microsoft.com/en-us/linkedin/marketing/integrations/community-management/shares/network-update-social-actions#retrieve-comments-on-comments)",
7
- version: "0.1.4",
7
+ version: "0.1.6",
8
8
  type: "action",
9
9
  props: {
10
10
  linkedin,
@@ -4,7 +4,7 @@ export default {
4
4
  key: "linkedin-retrieve-comments-shares",
5
5
  name: "Retrieve Comments On Shares",
6
6
  description: "Retrieve comments on shares given the share urn. [See the docs here](https://docs.microsoft.com/en-us/linkedin/marketing/integrations/community-management/shares/network-update-social-actions#retrieve-comments-on-shares)",
7
- version: "0.1.4",
7
+ version: "0.1.6",
8
8
  type: "action",
9
9
  props: {
10
10
  linkedin,
@@ -4,7 +4,7 @@ export default {
4
4
  key: "linkedin-search-organization",
5
5
  name: "Search Organization",
6
6
  description: "Searches for an organization by vanity name or email domain. [See the docs here](https://docs.microsoft.com/en-us/linkedin/marketing/integrations/community-management/organizations/organization-lookup-api)",
7
- version: "0.1.4",
7
+ version: "0.1.6",
8
8
  type: "action",
9
9
  props: {
10
10
  linkedin,
@@ -1,6 +1,6 @@
1
1
  const VERSION_PATH = "rest";
2
2
  const BASE_URL = "https://api.linkedin.com/";
3
- const VERSION_HEADER = "202309";
3
+ const VERSION_HEADER = "202409";
4
4
 
5
5
  const VISIBILITIES = [
6
6
  {
@@ -75,6 +75,13 @@ const ROLE_STATES = [
75
75
  "REVOKED",
76
76
  ];
77
77
 
78
+ const DEFAULT_LIMIT = 20;
79
+ const DEFAULT_MAX = 100;
80
+ const IS_FIRST_RUN = "isFirstRun";
81
+ const LAST_DATE_AT = "lastDateAt";
82
+ const ORG_ID = "orgId";
83
+ const PERSON_ID = "personId";
84
+
78
85
  export default {
79
86
  VERSION_PATH,
80
87
  BASE_URL,
@@ -85,4 +92,10 @@ export default {
85
92
  PIVOT_OPTIONS,
86
93
  ORGANIZATION_ROLES,
87
94
  ROLE_STATES,
95
+ DEFAULT_LIMIT,
96
+ DEFAULT_MAX,
97
+ IS_FIRST_RUN,
98
+ LAST_DATE_AT,
99
+ ORG_ID,
100
+ PERSON_ID,
88
101
  };
@@ -0,0 +1,38 @@
1
+ async function iterate(iterations) {
2
+ const items = [];
3
+ for await (const item of iterations) {
4
+ items.push(item);
5
+ }
6
+ return items;
7
+ }
8
+
9
+ function getNestedProperty(obj, propertyString) {
10
+ const properties = propertyString.split(".");
11
+ return properties.reduce((prev, curr) => prev?.[curr], obj);
12
+ }
13
+
14
+ function defaultEncodeFn(key, value) {
15
+ return `${key}=${value}`;
16
+ }
17
+
18
+ function encodeFn(key, value) {
19
+ return `${key}=${encodeURIComponent(value)}`;
20
+ }
21
+
22
+ function getParamsSerializer(encodeFn = defaultEncodeFn) {
23
+ return (params) => {
24
+ return Object.entries(params)
25
+ .map(([
26
+ key,
27
+ value,
28
+ ]) => encodeFn(key, value))
29
+ .join("&");
30
+ };
31
+ }
32
+
33
+ export default {
34
+ iterate,
35
+ getNestedProperty,
36
+ encodeFn,
37
+ getParamsSerializer,
38
+ };
package/linkedin.app.mjs CHANGED
@@ -1,5 +1,7 @@
1
- import { axios } from "@pipedream/platform";
1
+ import { axios as axiosPD } from "@pipedream/platform";
2
+ import axios from "axios";
2
3
  import constants from "./common/constants.mjs";
4
+ import utils from "./common/utils.mjs";
3
5
 
4
6
  export default {
5
7
  type: "app",
@@ -45,7 +47,7 @@ export default {
45
47
  });
46
48
  return elements?.map((element) => ({
47
49
  label: element.name,
48
- id: element.id,
50
+ value: element.id,
49
51
  }));
50
52
  },
51
53
  },
@@ -138,13 +140,22 @@ export default {
138
140
  }) {
139
141
  const BASE_URL = constants.BASE_URL;
140
142
 
141
- const config = {
143
+ return axiosPD($ || this, {
142
144
  url: url || `${BASE_URL}${constants.VERSION_PATH}${path}`,
143
145
  headers: this._getHeaders(),
144
146
  ...otherConfig,
145
- };
147
+ });
148
+ },
149
+ async _makeRequestAxios({
150
+ url, path, ...otherConfig
151
+ }) {
152
+ const BASE_URL = constants.BASE_URL;
146
153
 
147
- return axios($ || this, config);
154
+ return axios({
155
+ url: url || `${BASE_URL}${constants.VERSION_PATH}${path}`,
156
+ headers: this._getHeaders(),
157
+ ...otherConfig,
158
+ });
148
159
  },
149
160
  async createPost({
150
161
  data, ...args
@@ -223,10 +234,9 @@ export default {
223
234
  ...args,
224
235
  });
225
236
  },
226
- async getAccessControl(args = {}) {
227
- return this._makeRequest({
228
- path: "/organizationAcls",
229
- ...args,
237
+ async getAccessControl({ params }) {
238
+ return this._makeRequestAxios({
239
+ path: `/organizationAcls?${params}`,
230
240
  });
231
241
  },
232
242
  async queryAnaltyics(query, args = {} ) {
@@ -259,5 +269,65 @@ export default {
259
269
  ...args,
260
270
  });
261
271
  },
272
+ listPosts(args = {}) {
273
+ return this._makeRequest({
274
+ path: "/posts",
275
+ paramsSerializer: utils.getParamsSerializer(utils.encodeFn),
276
+ ...args,
277
+ });
278
+ },
279
+ async *getIterations({
280
+ resourcesFn, resourcesFnArgs, resourceName,
281
+ lastDateAt, dateField,
282
+ max = constants.DEFAULT_MAX,
283
+ }) {
284
+ let page = 0;
285
+ let resourcesCount = 0;
286
+
287
+ while (true) {
288
+ const response =
289
+ await resourcesFn({
290
+ ...resourcesFnArgs,
291
+ params: {
292
+ ...resourcesFnArgs?.params,
293
+ count: constants.DEFAULT_LIMIT,
294
+ start: page * constants.DEFAULT_LIMIT,
295
+ },
296
+ });
297
+
298
+ const nextResources = utils.getNestedProperty(response, resourceName);
299
+
300
+ if (!nextResources?.length) {
301
+ console.log("No more resources found");
302
+ return;
303
+ }
304
+
305
+ for (const resource of nextResources) {
306
+ const isDateGreater =
307
+ lastDateAt
308
+ && resource[dateField] > lastDateAt;
309
+
310
+ if (!lastDateAt || isDateGreater) {
311
+ yield resource;
312
+ resourcesCount += 1;
313
+ }
314
+
315
+ if (resourcesCount >= max) {
316
+ console.log("Reached max resources");
317
+ return;
318
+ }
319
+ }
320
+
321
+ if (nextResources.length < constants.DEFAULT_LIMIT) {
322
+ console.log("No next page found");
323
+ return;
324
+ }
325
+
326
+ page += 1;
327
+ }
328
+ },
329
+ paginate(args = {}) {
330
+ return utils.iterate(this.getIterations(args));
331
+ },
262
332
  },
263
333
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@pipedream/linkedin",
3
- "version": "0.1.0",
3
+ "version": "0.2.0",
4
4
  "description": "Pipedream Linkedin Components",
5
5
  "main": "linkedin.app.mjs",
6
6
  "keywords": [
@@ -13,7 +13,7 @@
13
13
  "access": "public"
14
14
  },
15
15
  "dependencies": {
16
- "@pipedream/platform": "^1.2.1",
16
+ "@pipedream/platform": "^3.0.3",
17
17
  "axios": "^1.2.3",
18
18
  "form-data": "^4.0.0"
19
19
  }
@@ -0,0 +1,122 @@
1
+ import {
2
+ ConfigurationError,
3
+ DEFAULT_POLLING_SOURCE_TIMER_INTERVAL,
4
+ } from "@pipedream/platform";
5
+ import app from "../../linkedin.app.mjs";
6
+ import constants from "../../common/constants.mjs";
7
+
8
+ export default {
9
+ props: {
10
+ app,
11
+ db: "$.service.db",
12
+ timer: {
13
+ type: "$.interface.timer",
14
+ label: "Polling Schedule",
15
+ description: "How often to poll the API",
16
+ default: {
17
+ intervalSeconds: DEFAULT_POLLING_SOURCE_TIMER_INTERVAL,
18
+ },
19
+ },
20
+ },
21
+ hooks: {
22
+ deploy() {
23
+ this.setIsFirstRun(true);
24
+ },
25
+ },
26
+ methods: {
27
+ generateMeta() {
28
+ throw new ConfigurationError("generateMeta is not implemented");
29
+ },
30
+ setPersonId(value) {
31
+ this.db.set(constants.PERSON_ID, value);
32
+ },
33
+ getPersonId() {
34
+ return this.db.get(constants.PERSON_ID);
35
+ },
36
+ setOrganizationId(value) {
37
+ this.db.set(constants.ORG_ID, value);
38
+ },
39
+ getOrganizationId() {
40
+ return this.db.get(constants.ORG_ID);
41
+ },
42
+ setIsFirstRun(value) {
43
+ this.db.set(constants.IS_FIRST_RUN, value);
44
+ },
45
+ getIsFirstRun() {
46
+ return this.db.get(constants.IS_FIRST_RUN);
47
+ },
48
+ setLastDateAt(value) {
49
+ this.db.set(constants.LAST_DATE_AT, value);
50
+ },
51
+ getLastDateAt() {
52
+ return this.db.get(constants.LAST_DATE_AT);
53
+ },
54
+ getDateField() {
55
+ throw new ConfigurationError("getDateField is not implemented");
56
+ },
57
+ getResourceName() {
58
+ throw new ConfigurationError("getResourceName is not implemented");
59
+ },
60
+ getResourcesFn() {
61
+ throw new ConfigurationError("getResourcesFn is not implemented");
62
+ },
63
+ getResourcesFnArgs() {
64
+ throw new ConfigurationError("getResourcesFnArgs is not implemented");
65
+ },
66
+ processResource(resource) {
67
+ const meta = this.generateMeta(resource);
68
+ this.$emit(resource, meta);
69
+ },
70
+ },
71
+ async run() {
72
+ const {
73
+ app,
74
+ getDateField,
75
+ getLastDateAt,
76
+ getResourcesFn,
77
+ getResourcesFnArgs,
78
+ getResourceName,
79
+ getIsFirstRun,
80
+ setIsFirstRun,
81
+ setLastDateAt,
82
+ processResource,
83
+ } = this;
84
+
85
+ const isFirstRun = getIsFirstRun();
86
+ const dateField = getDateField();
87
+ const lastDateAt = getLastDateAt();
88
+
89
+ const otherArgs = isFirstRun
90
+ ? {
91
+ max: constants.DEFAULT_LIMIT,
92
+ }
93
+ : {
94
+ dateField,
95
+ lastDateAt,
96
+ };
97
+
98
+ const resources = await app.paginate({
99
+ resourcesFn: getResourcesFn(),
100
+ resourcesFnArgs: getResourcesFnArgs(),
101
+ resourceName: getResourceName(),
102
+ ...otherArgs,
103
+ });
104
+
105
+ if (resources.length) {
106
+ const [
107
+ firstResource,
108
+ ] = Array.from(resources);
109
+ if (firstResource) {
110
+ setLastDateAt(firstResource[dateField]);
111
+ }
112
+ }
113
+
114
+ Array.from(resources)
115
+ .reverse()
116
+ .forEach(processResource);
117
+
118
+ if (isFirstRun) {
119
+ setIsFirstRun(false);
120
+ }
121
+ },
122
+ };
@@ -0,0 +1,82 @@
1
+ import { ConfigurationError } from "@pipedream/platform";
2
+ import common from "../common/polling.mjs";
3
+ import utils from "../../common/utils.mjs";
4
+ import sampleEmit from "./test-event.mjs";
5
+
6
+ export default {
7
+ ...common,
8
+ key: "linkedin-new-organization-post-created",
9
+ name: "New Organization Post Created",
10
+ description: "Emit new event when a new post is created by the organization. [See the documentation](https://learn.microsoft.com/en-us/linkedin/marketing/community-management/shares/posts-api?view=li-lms-2024-09&tabs=curl#find-posts-by-authors).",
11
+ type: "source",
12
+ version: "0.0.1",
13
+ dedupe: "unique",
14
+ props: {
15
+ ...common.props,
16
+ organizationVanityName: {
17
+ type: "string",
18
+ label: "Organization Vanity Name",
19
+ description: "You can get the Orgainization Vanity Name from the company LinkedIN URL, for example, if the company LinkedIn URL is `https://www.linkedin.com/company/confluent`, then the Organization Vanity Name is `confluent`.",
20
+ },
21
+ },
22
+ hooks: {
23
+ ...common.hooks,
24
+ async deploy() {
25
+ const {
26
+ app,
27
+ organizationVanityName,
28
+ setOrganizationId,
29
+ setIsFirstRun,
30
+ } = this;
31
+
32
+ const { elements } = await app.searchOrganizations("vanityName", {
33
+ debug: true,
34
+ params: {
35
+ vanityName: organizationVanityName,
36
+ },
37
+ paramsSerializer: utils.getParamsSerializer(utils.encodeFn),
38
+ });
39
+
40
+ const organizationFound =
41
+ elements?.find(({ vanityName }) => vanityName === organizationVanityName);
42
+
43
+ if (!organizationFound) {
44
+ throw new ConfigurationError(`There's no Organization matched for Organization Vanity Name \`${organizationVanityName}\`.`);
45
+ }
46
+
47
+ setOrganizationId(organizationFound.id);
48
+ setIsFirstRun(true);
49
+ },
50
+ },
51
+ methods: {
52
+ ...common.methods,
53
+ getDateField() {
54
+ return "createdAt";
55
+ },
56
+ getResourceName() {
57
+ return "elements";
58
+ },
59
+ getResourcesFn() {
60
+ return this.app.listPosts;
61
+ },
62
+ getResourcesFnArgs() {
63
+ const author = `urn:li:organization:${this.getOrganizationId()}`;
64
+ return {
65
+ debug: true,
66
+ params: {
67
+ author,
68
+ q: "author",
69
+ sortBy: "CREATED",
70
+ },
71
+ };
72
+ },
73
+ generateMeta(resource) {
74
+ return {
75
+ id: resource.id,
76
+ summary: `New Org Post: ${resource.id}`,
77
+ ts: resource.createdAt,
78
+ };
79
+ },
80
+ },
81
+ sampleEmit,
82
+ };
@@ -0,0 +1,18 @@
1
+ export default {
2
+ "isReshareDisabledByAuthor": false,
3
+ "lifecycleState": "PUBLISHED",
4
+ "createdAt": 1741038207195,
5
+ "lastModifiedAt": 1741038237226,
6
+ "visibility": "PUBLIC",
7
+ "publishedAt": 1741038207195,
8
+ "author": "urn:li:organization:100680185",
9
+ "id": "urn:li:share:7302443516318699520",
10
+ "distribution": {
11
+ "feedDistribution": "MAIN_FEED",
12
+ "thirdPartyDistributionChannels": []
13
+ },
14
+ "commentary": "test 1",
15
+ "lifecycleStateInfo": {
16
+ "isEditedByAuthor": false
17
+ }
18
+ };
@@ -1,44 +0,0 @@
1
- import linkedin from "../../linkedin.app.mjs";
2
-
3
- export default {
4
- key: "linkedin-analytics-finder-account-sample",
5
- name: "Query Analytics Finder Account Sample",
6
- description: "Sample query using analytics finder that gets analytics for a particular account for date range starting in a given year. [See the docs here](https://docs.microsoft.com/en-us/linkedin/marketing/integrations/ads-reporting/ads-reporting#sample-request)",
7
- version: "0.1.4",
8
- type: "action",
9
- props: {
10
- linkedin,
11
- startYear: {
12
- propDefinition: [
13
- linkedin,
14
- "startYear",
15
- ],
16
- },
17
- timeGranularity: {
18
- propDefinition: [
19
- linkedin,
20
- "timeGranularity",
21
- ],
22
- },
23
- adAccountId: {
24
- propDefinition: [
25
- linkedin,
26
- "adAccountId",
27
- ],
28
- },
29
- },
30
- async run({ $ }) {
31
- // Note: This action is based on the LinkedIn Analytics Finder sample request.
32
- // It was modified to use an sponsored account to match results by.
33
-
34
- const querystring = `&pivot=ACCOUNT&dateRange.start.day=1&dateRange.start.month=1&dateRange.start.year=${this.startYear}&timeGranularity=${this.timeGranularity}&accounts[0]=urn:li:sponsoredAccount:${this.adAccountId}`;
35
-
36
- const response = await this.linkedin.queryAnalytics(querystring, {
37
- $,
38
- });
39
-
40
- $.export("$summary", "Successfully retrieved analytics");
41
-
42
- return response;
43
- },
44
- };
@@ -1,44 +0,0 @@
1
- import linkedin from "../../linkedin.app.mjs";
2
-
3
- export default {
4
- key: "linkedin-analytics-finder-campaign-sample",
5
- name: "Query Analytics Finder Campaign Sample",
6
- description: "Sample query using analytics finder that gets analytics for a particular campaign in a date range starting in a given year. [See the docs here](https://docs.microsoft.com/en-us/linkedin/marketing/integrations/ads-reporting/ads-reporting#analytics-finder)",
7
- version: "0.2.4",
8
- type: "action",
9
- props: {
10
- linkedin,
11
- startYear: {
12
- propDefinition: [
13
- linkedin,
14
- "startYear",
15
- ],
16
- },
17
- timeGranularity: {
18
- propDefinition: [
19
- linkedin,
20
- "timeGranularity",
21
- ],
22
- },
23
- campaignId: {
24
- propDefinition: [
25
- linkedin,
26
- "campaignId",
27
- ],
28
- },
29
- },
30
- async run({ $ }) {
31
- // Note: This action is based on the LinkedIn Analytics Finder sample request.
32
- // As such, it uses an sponsored campaign to match results by.
33
-
34
- const querystring = `&pivot=CAMPAIGN&dateRange.start.day=1&dateRange.start.month=1&dateRange.start.year=${this.startYear}&timeGranularity=${this.timeGranularity}&campaigns[0]=urn:li:sponsoredCampaign:${this.campaignId}`;
35
-
36
- const response = await this.linkedin.queryAnalytics(querystring, {
37
- $,
38
- });
39
-
40
- $.export("$summary", "Successfully retrieved analytics");
41
-
42
- return response;
43
- },
44
- };
@@ -1,126 +0,0 @@
1
- import linkedin from "../../linkedin.app.mjs";
2
- import axios from "axios"; // @pipedream/platform axios sends a bad request
3
- import constants from "../../common/constants.mjs";
4
-
5
- const FACETS = [
6
- "campaignType",
7
- "shares",
8
- "campaigns",
9
- "creatives",
10
- "campaignGroups",
11
- "accounts",
12
- "companies",
13
- ];
14
-
15
- export default {
16
- key: "linkedin-query-analytics-finder",
17
- name: "Query Analytics Finder",
18
- description: "Queries the Analytics Finder to get analytics for the specified entity i.e company, account, campaign. [See the docs here](https://docs.microsoft.com/en-us/linkedin/marketing/integrations/ads-reporting/ads-reporting#analytics-finder)",
19
- version: "0.2.2",
20
- type: "action",
21
- props: {
22
- linkedin,
23
- pivot: {
24
- propDefinition: [
25
- linkedin,
26
- "pivot",
27
- ],
28
- },
29
- timeGranularity: {
30
- propDefinition: [
31
- linkedin,
32
- "timeGranularity",
33
- ],
34
- },
35
- dateRangeStart: {
36
- type: "string",
37
- label: "Date Range Start",
38
- description: "Represents the inclusive start time range of the analytics. If unset, it indicates an open range up to the end time. Should be in the [ISO 8601](https://en.wikipedia.org/wiki/ISO_8601) format, e.g. 2022-12-27.",
39
- },
40
- dateRangeEnd: {
41
- type: "string",
42
- label: "Date Range End",
43
- description: "Represents the inclusive end time range of the analytics. Must be after start time if it's present. If unset, it indicates an open range from start time to everything after. Should be in the [ISO 8601](https://en.wikipedia.org/wiki/ISO_8601) format, e.g. 2022-12-27.",
44
- optional: true,
45
- },
46
- campaignType: {
47
- type: "string[]",
48
- label: "Campaign Type",
49
- description: "An [Array of Campaign Type Values](https://docs.microsoft.com/en-us/linkedin/shared/references/v2/ads/adaccounts?context=linkedin/marketing/context). Required unless another facet is provided. Supported types are [TEXT_AD, SPONSORED_UPDATES, SPONSORED_INMAILS, DYNAMIC]. Requires at least one other facet.",
50
- optional: true,
51
- },
52
- shares: {
53
- type: "string[]",
54
- label: "Shares",
55
- description: "An [Array of Share URN](https://docs.microsoft.com/en-us/linkedin/marketing/integrations/community-management/shares/share-api). Required unless another facet is provided.",
56
- optional: true,
57
- },
58
- campaigns: {
59
- type: "string[]",
60
- label: "Campaigns",
61
- description: "An [Array of Sponsored Campaign URN](https://docs.microsoft.com/en-us/linkedin/shared/references/v2/ads/adcampaigns?context=linkedin/marketing/context). Required unless another facet is provided.",
62
- optional: true,
63
- },
64
- creatives: {
65
- type: "string[]",
66
- label: "Creatives",
67
- description: "An [Array of Sponsored Creative URN](https://docs.microsoft.com/en-us/linkedin/shared/references/v2/ads/adcreatives?context=linkedin/marketing/context). Required unless another facet is provided.",
68
- optional: true,
69
- },
70
- campaignGroups: {
71
- type: "string[]",
72
- label: "Campaign Groups",
73
- description: "An [Array of Campaign Group URN](https://docs.microsoft.com/en-us/linkedin/shared/references/v2/ads/adcampaigngroups?context=linkedin/marketing/context). Required unless another facet is provided.",
74
- optional: true,
75
- },
76
- accounts: {
77
- type: "string[]",
78
- label: "Accounts",
79
- description: "An [Array of Account URN](https://docs.microsoft.com/en-us/linkedin/shared/references/v2/ads/adaccounts?context=linkedin/marketing/context). Required unless another facet is provided.",
80
- optional: true,
81
- },
82
- companies: {
83
- type: "string[]",
84
- label: "Companies",
85
- description: "An [Array of Organization URN](https://docs.microsoft.com/en-us/linkedin/marketing/integrations/community-management/organizations/organization-lookup-api). Required unless another facet is provided.",
86
- optional: true,
87
- },
88
- },
89
- methods: {
90
- createDateRangeQuery() {
91
- const start = new Date(this.dateRangeStart);
92
- if (!this.dateRangeEnd) {
93
- return `dateRange=(start:(day:${start.getDate() + 1},month:${start.getMonth() + 1},year:${start.getFullYear()}))`;
94
- }
95
- const end = new Date(this.dateRangeEnd);
96
- return `dateRange=(start:(day:${start.getDate() + 1},month:${start.getMonth() + 1},year:${start.getFullYear()}),end:(day:${end.getDate() + 1},month:${end.getMonth() + 1},year:${end.getFullYear()}))`;
97
- },
98
- createListQuery(name, value) {
99
- return `${name}=List(${value.map(encodeURIComponent).join(",")})`;
100
- },
101
- },
102
- async run({ $ }) {
103
- const PATH = "/adAnalytics";
104
- let query = `?q=analytics&pivot=${this.pivot}&timeGranularity=${this.timeGranularity}&${this.createDateRangeQuery()}`;
105
-
106
- for (const facet of FACETS) {
107
- if (this[facet]) {
108
- query += `&${this.createListQuery(facet, this[facet])}`;
109
- }
110
- }
111
-
112
- const url = `${constants.BASE_URL}${constants.VERSION_PATH}${PATH}${query}`;
113
- const headers = this.linkedin._getHeaders();
114
-
115
- try {
116
- const { data } = await axios({
117
- url,
118
- headers,
119
- });
120
- $.export("$summary", "Successfully retrieved analytics information");
121
- return data;
122
- } catch (e) {
123
- throw new Error(JSON.stringify(e.response.data));
124
- }
125
- },
126
- };