@pipedream/linear_app 0.5.6 → 0.5.7

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.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@pipedream/linear_app",
3
- "version": "0.5.6",
3
+ "version": "0.5.7",
4
4
  "description": "Pipedream Linear_app Components",
5
5
  "main": "linear_app.app.mjs",
6
6
  "keywords": [
@@ -5,9 +5,9 @@ export default {
5
5
  ...common,
6
6
  key: "linear_app-comment-created-instant",
7
7
  name: "New Created Comment (Instant)",
8
- description: "Emit new event when a new comment is created. See the docs [here](https://developers.linear.app/docs/graphql/webhooks)",
8
+ description: "Emit new event when a new comment is created. [See the documentation](https://developers.linear.app/docs/graphql/webhooks)",
9
9
  type: "source",
10
- version: "0.1.6",
10
+ version: "0.1.7",
11
11
  dedupe: "unique",
12
12
  methods: {
13
13
  ...common.methods,
@@ -50,6 +50,11 @@ export default {
50
50
  },
51
51
  };
52
52
  },
53
+ getResource(comment) {
54
+ return this.linearApp.getComment({
55
+ commentId: comment.id,
56
+ });
57
+ },
53
58
  getMetadata(resource) {
54
59
  const {
55
60
  delivery,
@@ -1,7 +1,7 @@
1
1
  import linearApp from "../../linear_app.app.mjs";
2
2
  import constants from "../../common/constants.mjs";
3
3
  import utils from "../../common/utils.mjs";
4
- import { ConfigurationError } from "@pipedream/platform";
4
+ import { DEFAULT_POLLING_SOURCE_TIMER_INTERVAL } from "@pipedream/platform";
5
5
 
6
6
  export default {
7
7
  props: {
@@ -21,18 +21,28 @@ export default {
21
21
  "projectId",
22
22
  ],
23
23
  },
24
- http: "$.interface.http",
25
24
  db: "$.service.db",
26
25
  },
27
26
  async additionalProps() {
28
27
  const props = {};
29
- if (!(await this.isAdmin())) {
30
- props.alert = {
31
- type: "alert",
32
- alertType: "error",
33
- content: "You must have an admin role to create or manage webhooks. See the Linear [documentation](https://linear.app/docs/api-and-webhooks#webhooks) for details.",
28
+ let msg;
29
+ if (await this.isAdmin()) {
30
+ msg = "Admin role detected. Trigger will be set up as a webhook.";
31
+ props.http = "$.interface.http";
32
+ } else {
33
+ msg = "No admin role detected. Trigger will set up to use polling.";
34
+ props.timer = {
35
+ type: "$.interface.timer",
36
+ default: {
37
+ intervalSeconds: DEFAULT_POLLING_SOURCE_TIMER_INTERVAL,
38
+ },
34
39
  };
35
40
  }
41
+ props.alert = {
42
+ type: "alert",
43
+ alertType: "info",
44
+ content: `${msg} See the Linear [documentation](https://linear.app/docs/api-and-webhooks#webhooks) for details.`,
45
+ };
36
46
  return props;
37
47
  },
38
48
  methods: {
@@ -54,6 +64,9 @@ export default {
54
64
  useGraphQl() {
55
65
  return true;
56
66
  },
67
+ getResource() {
68
+ throw new Error("getResource is not implemented");
69
+ },
57
70
  getResourceTypes() {
58
71
  throw new Error("getResourceTypes is not implemented");
59
72
  },
@@ -85,15 +98,7 @@ export default {
85
98
  });
86
99
  return data?.user?.admin;
87
100
  },
88
- },
89
- hooks: {
90
- async deploy() {
91
- if (!(await this.isAdmin())) {
92
- throw new ConfigurationError("You must have an admin role to create or manage webhooks. See the Linear [documentation](https://linear.app/docs/api-and-webhooks#webhooks) for details.");
93
- }
94
-
95
- // Retrieve historical events
96
- console.log("Retrieving historical events...");
101
+ async emitPolledResources() {
97
102
  const stream = this.linearApp.paginateResources({
98
103
  resourcesFn: this.getResourcesFn(),
99
104
  resourcesFnArgs: this.getResourcesFnArgs(),
@@ -107,73 +112,89 @@ export default {
107
112
  this.$emit(resource, this.getMetadata(resource));
108
113
  });
109
114
  },
115
+ },
116
+ hooks: {
117
+ async deploy() {
118
+ // Retrieve historical events
119
+ console.log("Retrieving historical events...");
120
+ await this.emitPolledResources();
121
+ },
110
122
  async activate() {
111
- const args = {
112
- resourceTypes: this.getResourceTypes(),
113
- url: this.http.endpoint,
114
- label: this.getWebhookLabel(),
115
- };
116
- if (!this.teamIds && !this.teamId) {
117
- args.allPublicTeams = true;
118
- const { _webhook: webhook } = await this.linearApp.createWebhook(args);
119
- this.setWebhookId("1", webhook.id);
120
- return;
121
- }
122
- const teamIds = this.teamIds || [
123
- this.teamId,
124
- ];
125
- for (const teamId of teamIds) {
126
- const { _webhook: webhook } =
127
- await this.linearApp.createWebhook({
128
- teamId,
129
- ...args,
130
- });
131
- this.setWebhookId(teamId, webhook.id);
123
+ if (await this.isAdmin()) {
124
+ const args = {
125
+ resourceTypes: this.getResourceTypes(),
126
+ url: this.http.endpoint,
127
+ label: this.getWebhookLabel(),
128
+ };
129
+ if (!this.teamIds && !this.teamId) {
130
+ args.allPublicTeams = true;
131
+ const { _webhook: webhook } = await this.linearApp.createWebhook(args);
132
+ this.setWebhookId("1", webhook.id);
133
+ return;
134
+ }
135
+ const teamIds = this.teamIds || [
136
+ this.teamId,
137
+ ];
138
+ for (const teamId of teamIds) {
139
+ const { _webhook: webhook } =
140
+ await this.linearApp.createWebhook({
141
+ teamId,
142
+ ...args,
143
+ });
144
+ this.setWebhookId(teamId, webhook.id);
145
+ }
132
146
  }
133
147
  },
134
148
  async deactivate() {
135
- if (!this.teamIds && !this.teamId) {
136
- const webhookId = this.getWebhookId("1");
137
- if (webhookId) {
138
- await this.linearApp.deleteWebhook(webhookId);
149
+ if (await this.isAdmin()) {
150
+ if (!this.teamIds && !this.teamId) {
151
+ const webhookId = this.getWebhookId("1");
152
+ if (webhookId) {
153
+ await this.linearApp.deleteWebhook(webhookId);
154
+ }
155
+ return;
139
156
  }
140
- return;
141
- }
142
- const teamIds = this.teamIds || [
143
- this.teamId,
144
- ];
145
- for (const teamId of teamIds) {
146
- const webhookId = this.getWebhookId(teamId);
147
- if (webhookId) {
148
- await this.linearApp.deleteWebhook(webhookId);
157
+ const teamIds = this.teamIds || [
158
+ this.teamId,
159
+ ];
160
+ for (const teamId of teamIds) {
161
+ const webhookId = this.getWebhookId(teamId);
162
+ if (webhookId) {
163
+ await this.linearApp.deleteWebhook(webhookId);
164
+ }
149
165
  }
150
166
  }
151
167
  },
152
168
  },
153
169
  async run(event) {
154
- const {
155
- client_ip: clientIp,
156
- body,
157
- headers,
158
- } = event;
170
+ if (!(await this.isAdmin())) {
171
+ await this.emitPolledResources();
172
+ } else {
173
+ const {
174
+ client_ip: clientIp,
175
+ body,
176
+ headers,
177
+ } = event;
159
178
 
160
- const { [constants.LINEAR_DELIVERY_HEADER]: delivery } = headers;
179
+ const { [constants.LINEAR_DELIVERY_HEADER]: delivery } = headers;
161
180
 
162
- const resource = {
163
- ...body,
164
- delivery,
165
- };
181
+ const resource = {
182
+ ...body,
183
+ delivery,
184
+ };
166
185
 
167
- if (!this.isWebhookValid(clientIp)) {
168
- console.log("Webhook is not valid");
169
- return;
170
- }
186
+ if (!this.isWebhookValid(clientIp)) {
187
+ console.log("Webhook is not valid");
188
+ return;
189
+ }
171
190
 
172
- if (!(await this.isFromProject(body)) || !this.isRelevant(body)) {
173
- return;
174
- }
191
+ if (!(await this.isFromProject(body)) || !this.isRelevant(body)) {
192
+ return;
193
+ }
175
194
 
176
- const meta = this.getMetadata(resource);
177
- this.$emit(body, meta);
195
+ const meta = this.getMetadata(resource);
196
+ const item = await this.getResource(body.data);
197
+ this.$emit(item, meta);
198
+ }
178
199
  },
179
200
  };
@@ -5,9 +5,9 @@ export default {
5
5
  ...common,
6
6
  key: "linear_app-issue-created-instant",
7
7
  name: "New Created Issue (Instant)",
8
- description: "Emit new event when a new issue is created. See the docs [here](https://developers.linear.app/docs/graphql/webhooks)",
8
+ description: "Emit new event when a new issue is created. [See the documentation](https://developers.linear.app/docs/graphql/webhooks)",
9
9
  type: "source",
10
- version: "0.3.6",
10
+ version: "0.3.7",
11
11
  dedupe: "unique",
12
12
  methods: {
13
13
  ...common.methods,
@@ -42,6 +42,11 @@ export default {
42
42
  isRelevant(body) {
43
43
  return body?.action === "create";
44
44
  },
45
+ getResource(issue) {
46
+ return this.linearApp.getIssue({
47
+ issueId: issue.id,
48
+ });
49
+ },
45
50
  getMetadata(resource) {
46
51
  const {
47
52
  delivery,
@@ -5,9 +5,9 @@ export default {
5
5
  ...common,
6
6
  key: "linear_app-issue-updated-instant",
7
7
  name: "New Updated Issue (Instant)",
8
- description: "Emit new event when an issue is updated. See the docs [here](https://developers.linear.app/docs/graphql/webhooks)",
8
+ description: "Emit new event when an issue is updated. [See the documentation](https://developers.linear.app/docs/graphql/webhooks)",
9
9
  type: "source",
10
- version: "0.3.6",
10
+ version: "0.3.7",
11
11
  dedupe: "unique",
12
12
  methods: {
13
13
  ...common.methods,
@@ -39,17 +39,22 @@ export default {
39
39
  },
40
40
  };
41
41
  },
42
+ getResource(issue) {
43
+ return this.linearApp.getIssue({
44
+ issueId: issue.id,
45
+ });
46
+ },
42
47
  getMetadata(resource) {
43
48
  const {
44
- delivery,
45
49
  title,
46
50
  data,
47
51
  updatedAt,
48
52
  } = resource;
53
+ const ts = Date.parse(data?.updatedAt || updatedAt);
49
54
  return {
50
- id: delivery || resource.id,
55
+ id: `${resource.id}-${ts}`,
51
56
  summary: `Issue Updated: ${data?.title || title}`,
52
- ts: Date.parse(updatedAt),
57
+ ts,
53
58
  };
54
59
  },
55
60
  },
@@ -1,18 +1,18 @@
1
1
  import common from "../common/webhook.mjs";
2
2
  import constants from "../../common/constants.mjs";
3
+ import utils from "../../common/utils.mjs";
3
4
 
4
5
  export default {
5
6
  ...common,
6
7
  key: "linear_app-new-issue-status-updated",
7
8
  name: "New Issue Status Updated (Instant)",
8
- description: "Emit new event when the status of an issue is updated. See the docs [here](https://developers.linear.app/docs/graphql/webhooks)",
9
+ description: "Emit new event when the status of an issue is updated. [See the documentation](https://developers.linear.app/docs/graphql/webhooks)",
9
10
  type: "source",
10
- version: "0.1.6",
11
+ version: "0.1.7",
11
12
  dedupe: "unique",
12
13
  props: {
13
14
  linearApp: common.props.linearApp,
14
- http: common.props.http,
15
- db: common.props.db,
15
+ db: "$.service.db",
16
16
  teamId: {
17
17
  label: "Team ID",
18
18
  type: "string",
@@ -20,7 +20,7 @@ export default {
20
20
  common.props.linearApp,
21
21
  "teamId",
22
22
  ],
23
- optional: true,
23
+ reloadProps: true,
24
24
  },
25
25
  projectId: {
26
26
  propDefinition: [
@@ -41,6 +41,12 @@ export default {
41
41
  },
42
42
  methods: {
43
43
  ...common.methods,
44
+ _getPreviousStatuses() {
45
+ return this.db.get("previousStatuses") || {};
46
+ },
47
+ _setPreviousStatuses(previousStatuses) {
48
+ this.db.set("previousStatuses", previousStatuses);
49
+ },
44
50
  getResourceTypes() {
45
51
  return [
46
52
  constants.RESOURCE_TYPE.ISSUE,
@@ -79,6 +85,11 @@ export default {
79
85
  isRelevant(body) {
80
86
  return body?.updatedFrom?.stateId && (!this.stateId || body.data.stateId === this.stateId);
81
87
  },
88
+ getResource(issue) {
89
+ return this.linearApp.getIssue({
90
+ issueId: issue.id,
91
+ });
92
+ },
82
93
  getMetadata(resource) {
83
94
  const {
84
95
  delivery,
@@ -86,11 +97,43 @@ export default {
86
97
  data,
87
98
  updatedAt,
88
99
  } = resource;
100
+ const ts = Date.parse(updatedAt);
89
101
  return {
90
- id: delivery || resource.id,
102
+ id: delivery || `${resource.id}-${ts}`,
91
103
  summary: `Issue status updated: ${data?.title || title}`,
92
- ts: Date.parse(updatedAt),
104
+ ts,
93
105
  };
94
106
  },
107
+ async emitPolledResources() {
108
+ const previousStatuses = this._getPreviousStatuses();
109
+ const newStatuses = {};
110
+
111
+ const stream = this.linearApp.paginateResources({
112
+ resourcesFn: this.getResourcesFn(),
113
+ resourcesFnArgs: this.getResourcesFnArgs(),
114
+ useGraphQl: this.useGraphQl(),
115
+ max: 1000,
116
+ });
117
+ const resources = await utils.streamIterator(stream);
118
+
119
+ const updatedResources = [];
120
+ for (const issue of resources) {
121
+ newStatuses[issue.id] = issue.state.id;
122
+ if (issue.createdAt === issue.updatedAt) {
123
+ continue;
124
+ }
125
+ if (previousStatuses[issue.id] !== issue.state.id) {
126
+ updatedResources.push(issue);
127
+ }
128
+ }
129
+
130
+ this._setPreviousStatuses(newStatuses);
131
+
132
+ updatedResources
133
+ .reverse()
134
+ .forEach((resource) => {
135
+ this.$emit(resource, this.getMetadata(resource));
136
+ });
137
+ },
95
138
  },
96
139
  };