@pipedream/linear_app 0.5.4 → 0.5.6

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/README.md CHANGED
@@ -1,8 +1,11 @@
1
1
  # Overview
2
2
 
3
- With the Linear API, you can manage your workflow, track issues, and automate
4
- your development process. Here are some examples of what you can build:
3
+ Linear helps streamline software project management, bug tracking, and task coordination. By using the Linear (API key) API with Pipedream, you can automate routine tasks, sync issues across platforms, and trigger custom workflows. Think auto-assignment of tasks based on specific triggers or pushing updates to a Slack channel when an issue's status changes. These automations save time and ensure that your development team stays focused on coding rather than on administrative overhead.
5
4
 
6
- - A custom workflow management tool
7
- - A tool to track issues and feature requests
8
- - A development process automation tool
5
+ # Example Use Cases
6
+
7
+ - **Sync Issues with Google Sheets**: Use Pipedream to monitor new issues in Linear and automatically add them to a Google Sheets spreadsheet. This can help with reporting, auditing, and providing a high-level overview of tasks without needing to access Linear directly.
8
+
9
+ - **Automate Task Assignment Based on Labels**: When a new issue is created in Linear with a specific label (e.g., "urgent"), you can set up a Pipedream workflow to automatically assign it to a designated team member or escalate it by creating a high-priority notification in your team's messaging app, like Slack or Microsoft Teams.
10
+
11
+ - **Create GitHub Issues from Linear Tasks**: For development teams using both Linear and GitHub, a workflow can be set up to create a new GitHub issue whenever a Linear task reaches a certain stage or is tagged for development. This ensures that your code repository is always in sync with your project management tool.
@@ -5,7 +5,7 @@ export default {
5
5
  key: "linear_app-create-issue",
6
6
  name: "Create Issue",
7
7
  description: "Create an issue (API Key). See the docs [here](https://developers.linear.app/docs/graphql/working-with-the-graphql-api#creating-and-editing-issues)",
8
- version: "0.4.4",
8
+ version: "0.4.5",
9
9
  props: {
10
10
  linearApp,
11
11
  teamId: {
@@ -14,6 +14,15 @@ export default {
14
14
  "teamId",
15
15
  ],
16
16
  },
17
+ projectId: {
18
+ propDefinition: [
19
+ linearApp,
20
+ "projectId",
21
+ ({ teamId }) => ({
22
+ teamId,
23
+ }),
24
+ ],
25
+ },
17
26
  title: {
18
27
  propDefinition: [
19
28
  linearApp,
@@ -44,6 +53,8 @@ export default {
44
53
  },
45
54
  async run({ $ }) {
46
55
  const {
56
+ linearApp,
57
+ projectId,
47
58
  title,
48
59
  description,
49
60
  teamId,
@@ -52,8 +63,9 @@ export default {
52
63
  } = this;
53
64
 
54
65
  const response =
55
- await this.linearApp.createIssue({
66
+ await linearApp.createIssue({
56
67
  teamId,
68
+ projectId,
57
69
  title,
58
70
  description,
59
71
  assigneeId,
@@ -4,7 +4,7 @@ export default {
4
4
  key: "linear_app-get-issue",
5
5
  name: "Get Issue",
6
6
  description: "Get an issue by ID (API Key). See the docs [here](https://developers.linear.app/docs/graphql/working-with-the-graphql-api)",
7
- version: "0.1.4",
7
+ version: "0.1.5",
8
8
  type: "action",
9
9
  props: {
10
10
  linearApp,
@@ -17,8 +17,15 @@ export default {
17
17
  },
18
18
  },
19
19
  async run({ $ }) {
20
- const issue = await this.linearApp.getIssue(this.issueId);
21
- $.export("$summary", `Found issue with ID ${this.issueId}`);
20
+ const {
21
+ linearApp,
22
+ issueId,
23
+ } = this;
24
+
25
+ const issue = await linearApp.getIssue({
26
+ issueId,
27
+ });
28
+ $.export("$summary", `Found issue with ID ${issueId}`);
22
29
  return issue;
23
30
  },
24
31
  };
@@ -4,7 +4,7 @@ export default {
4
4
  key: "linear_app-get-teams",
5
5
  name: "Get Teams",
6
6
  description: "Get all the teams (API Key). See the docs [here](https://developers.linear.app/docs/graphql/working-with-the-graphql-api)",
7
- version: "0.2.4",
7
+ version: "0.2.5",
8
8
  type: "action",
9
9
  props: {
10
10
  linearApp,
@@ -6,7 +6,7 @@ export default {
6
6
  name: "Search Issues",
7
7
  description: "Search issues (API Key). See the docs [here](https://developers.linear.app/docs/graphql/working-with-the-graphql-api)",
8
8
  type: "action",
9
- version: "0.2.4",
9
+ version: "0.2.5",
10
10
  props: {
11
11
  linearApp,
12
12
  query: {
@@ -5,7 +5,7 @@ export default {
5
5
  name: "Update Issue",
6
6
  description: "Update an issue (API Key). See the docs [here](https://developers.linear.app/docs/graphql/working-with-the-graphql-api#creating-and-editing-issues)",
7
7
  type: "action",
8
- version: "0.1.4",
8
+ version: "0.1.5",
9
9
  props: {
10
10
  linearApp,
11
11
  teamId: {
@@ -43,65 +43,6 @@ const ORDER_BY_OPTIONS = [
43
43
  },
44
44
  ];
45
45
 
46
- const ISSUE_NODES = `
47
- id
48
- title
49
- description
50
- boardOrder
51
- branchName
52
- createdAt
53
- customerTicketCount
54
- identifier
55
- number
56
- priority
57
- priorityLabel
58
- sortOrder
59
- updatedAt
60
- url
61
- assignee {
62
- id
63
- name
64
- email
65
- }
66
- creator {
67
- id
68
- name
69
- email
70
- }
71
- project {
72
- id
73
- name
74
- }
75
- state {
76
- id
77
- name
78
- }
79
- team {
80
- id
81
- name
82
- }
83
- `;
84
-
85
- const COMMENT_NODES = `
86
- id
87
- body
88
- createdAt
89
- reactionData
90
- updatedAt
91
- issue {
92
- id
93
- title
94
- project {
95
- id
96
- name
97
- }
98
- }
99
- user {
100
- id
101
- name
102
- }
103
- `;
104
-
105
46
  export default {
106
47
  WEBHOOK_ID,
107
48
  LINEAR_DELIVERY_HEADER,
@@ -113,6 +54,4 @@ export default {
113
54
  CLIENT_IPS,
114
55
  ORDER_BY_OPTIONS,
115
56
  FIELD,
116
- ISSUE_NODES,
117
- COMMENT_NODES,
118
57
  };
@@ -0,0 +1,104 @@
1
+ export default {
2
+ pageInfo: `
3
+ fragment PageInfo on PageInfo {
4
+ startCursor
5
+ endCursor
6
+ hasPreviousPage
7
+ hasNextPage
8
+ }
9
+ `,
10
+ issue: `
11
+ fragment Issue on Issue {
12
+ trashed
13
+ labelIds
14
+ url
15
+ identifier
16
+ priorityLabel
17
+ previousIdentifiers
18
+ customerTicketCount
19
+ branchName
20
+ botActor {
21
+ avatarUrl
22
+ name
23
+ userDisplayName
24
+ subType
25
+ type
26
+ id
27
+ }
28
+ cycle {
29
+ id
30
+ }
31
+ dueDate
32
+ estimate
33
+ description
34
+ title
35
+ number
36
+ lastAppliedTemplate {
37
+ id
38
+ }
39
+ updatedAt
40
+ sortOrder
41
+ subIssueSortOrder
42
+ parent {
43
+ id
44
+ }
45
+ priority
46
+ project {
47
+ id
48
+ }
49
+ projectMilestone {
50
+ id
51
+ }
52
+ team {
53
+ id
54
+ }
55
+ archivedAt
56
+ createdAt
57
+ startedTriageAt
58
+ triagedAt
59
+ autoArchivedAt
60
+ autoClosedAt
61
+ canceledAt
62
+ completedAt
63
+ startedAt
64
+ snoozedUntilAt
65
+ id
66
+ assignee {
67
+ id
68
+ }
69
+ creator {
70
+ id
71
+ }
72
+ snoozedBy {
73
+ id
74
+ }
75
+ favorite {
76
+ id
77
+ }
78
+ state {
79
+ id
80
+ }
81
+ }
82
+ `,
83
+ comment: `
84
+ fragment Comment on Comment {
85
+ id
86
+ body
87
+ createdAt
88
+ reactionData
89
+ updatedAt
90
+ issue {
91
+ id
92
+ title
93
+ project {
94
+ id
95
+ name
96
+ }
97
+ }
98
+ user {
99
+ id
100
+ name
101
+ }
102
+ }
103
+ `,
104
+ };
@@ -0,0 +1,60 @@
1
+ import fragments from "./fragments.mjs";
2
+
3
+ export default {
4
+ getIssue: `
5
+ query GetIssue($issueId: String!) {
6
+ issue(id: $issueId) {
7
+ ...Issue
8
+ }
9
+ }
10
+ ${fragments.issue}
11
+ `,
12
+ listIssues: `
13
+ query ListIssues(
14
+ $filter: IssueFilter,
15
+ $before: String,
16
+ $after: String,
17
+ $first: Int,
18
+ $last: Int,
19
+ $includeArchived: Boolean,
20
+ $orderBy: PaginationOrderBy
21
+ ) {
22
+ issues(
23
+ filter: $filter,
24
+ before: $before,
25
+ after: $after,
26
+ first: $first,
27
+ last: $last,
28
+ includeArchived: $includeArchived,
29
+ orderBy: $orderBy
30
+ ) {
31
+ pageInfo {
32
+ ...PageInfo
33
+ }
34
+ nodes {
35
+ ...Issue
36
+ }
37
+ }
38
+ }
39
+ ${fragments.issue}
40
+ ${fragments.pageInfo}
41
+ `,
42
+ getComment: `
43
+ query GetComment($commentId: String!) {
44
+ comment(id: $commentId) {
45
+ ...Comment
46
+ }
47
+ }
48
+ ${fragments.comment}
49
+ `,
50
+ listProjects: `
51
+ query ListProjects {
52
+ projects {
53
+ nodes {
54
+ id
55
+ name
56
+ }
57
+ }
58
+ }
59
+ `,
60
+ };
@@ -2,6 +2,7 @@ import { LinearClient } from "@linear/sdk";
2
2
  import constants from "./common/constants.mjs";
3
3
  import utils from "./common/utils.mjs";
4
4
  import { axios } from "@pipedream/platform";
5
+ import queries from "./common/queries.mjs";
5
6
 
6
7
  export default {
7
8
  type: "app",
@@ -57,7 +58,9 @@ export default {
57
58
  label: "Project",
58
59
  description: "The identifier or key of the project associated with the issue",
59
60
  optional: true,
60
- async options({ prevContext }) {
61
+ async options({
62
+ teamId, prevContext,
63
+ }) {
61
64
  return this.listResourcesOptions({
62
65
  prevContext,
63
66
  resourcesFn: this.listProjects,
@@ -67,6 +70,17 @@ export default {
67
70
  label: name,
68
71
  value: id,
69
72
  }),
73
+ resourcesArgs: teamId && {
74
+ filter: {
75
+ issues: {
76
+ team: {
77
+ id: {
78
+ eq: teamId,
79
+ },
80
+ },
81
+ },
82
+ },
83
+ },
70
84
  });
71
85
  },
72
86
  },
@@ -172,7 +186,7 @@ export default {
172
186
  Authorization: `${this.$auth.api_key}`,
173
187
  };
174
188
  },
175
- async makeAxiosRequest({
189
+ makeAxiosRequest({
176
190
  $ = this, ...args
177
191
  }) {
178
192
  return axios($, {
@@ -181,6 +195,12 @@ export default {
181
195
  ...args,
182
196
  });
183
197
  },
198
+ post(args = {}) {
199
+ return this.makeAxiosRequest({
200
+ method: "POST",
201
+ ...args,
202
+ });
203
+ },
184
204
  getClientOptions(options = {}) {
185
205
  return {
186
206
  apiKey: this.$auth.api_key,
@@ -205,18 +225,19 @@ export default {
205
225
  return this.client().updateIssue(issueId, input);
206
226
  },
207
227
  async listIssues(variables) {
208
- return this.client().issues(variables);
228
+ const { data: { issues } } = await this.post({
229
+ data: {
230
+ query: queries.listIssues,
231
+ variables,
232
+ },
233
+ });
234
+ return issues;
209
235
  },
210
- async getIssue(id) {
211
- const { data: { issue } } = await this.makeAxiosRequest({
212
- method: "POST",
236
+ async getIssue(variables) {
237
+ const { data: { issue } } = await this.post({
213
238
  data: {
214
- query: `
215
- {
216
- issue(id: "${id}") {
217
- ${constants.ISSUE_NODES}
218
- }
219
- }`,
239
+ query: queries.getIssue,
240
+ variables,
220
241
  },
221
242
  });
222
243
  return issue;
@@ -236,11 +257,11 @@ export default {
236
257
  async listTeams(variables = {}) {
237
258
  return this.client().teams(variables);
238
259
  },
239
- async listProjects() {
240
- const { data: { projects } } = await this.makeAxiosRequest({
241
- method: "POST",
260
+ async listProjects(variables) {
261
+ const { data: { projects } } = await this.post({
242
262
  data: {
243
- query: "{ projects { nodes { id name } } }",
263
+ query: queries.listProjects,
264
+ variables,
244
265
  },
245
266
  });
246
267
  return projects;
@@ -257,16 +278,11 @@ export default {
257
278
  async listComments(variables = {}) {
258
279
  return this.client().comments(variables);
259
280
  },
260
- async getComment(id) {
261
- const { data: { comment } } = await this.makeAxiosRequest({
262
- method: "POST",
281
+ async getComment(variables) {
282
+ const { data: { comment } } = await this.post({
263
283
  data: {
264
- query: `
265
- {
266
- comment(id: "${id}") {
267
- ${constants.COMMENT_NODES}
268
- }
269
- }`,
284
+ query: queries.getComment,
285
+ variables,
270
286
  },
271
287
  });
272
288
  return comment;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@pipedream/linear_app",
3
- "version": "0.5.4",
3
+ "version": "0.5.6",
4
4
  "description": "Pipedream Linear_app Components",
5
5
  "main": "linear_app.app.mjs",
6
6
  "keywords": [
@@ -14,7 +14,7 @@
14
14
  "access": "public"
15
15
  },
16
16
  "dependencies": {
17
- "@linear/sdk": "^2.6.0",
18
- "@pipedream/platform": "^1.3.0"
17
+ "@linear/sdk": "^13.0.0",
18
+ "@pipedream/platform": "^3.0.3"
19
19
  }
20
20
  }
@@ -7,7 +7,7 @@ export default {
7
7
  name: "New Created Comment (Instant)",
8
8
  description: "Emit new event when a new comment is created. See the docs [here](https://developers.linear.app/docs/graphql/webhooks)",
9
9
  type: "source",
10
- version: "0.1.4",
10
+ version: "0.1.6",
11
11
  dedupe: "unique",
12
12
  methods: {
13
13
  ...common.methods,
@@ -26,7 +26,9 @@ export default {
26
26
  return false;
27
27
  },
28
28
  async isFromProject(body) {
29
- const comment = await this.linearApp.getComment(body.data.id);
29
+ const comment = await this.linearApp.getComment({
30
+ commentId: body.data.id,
31
+ });
30
32
  return !this.projectId || comment?.issue?.project?.id == this.projectId;
31
33
  },
32
34
  getResourcesFnArgs() {
@@ -1,6 +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
5
 
5
6
  export default {
6
7
  props: {
@@ -12,6 +13,7 @@ export default {
12
13
  linearApp,
13
14
  "teamId",
14
15
  ],
16
+ reloadProps: true,
15
17
  },
16
18
  projectId: {
17
19
  propDefinition: [
@@ -22,6 +24,17 @@ export default {
22
24
  http: "$.interface.http",
23
25
  db: "$.service.db",
24
26
  },
27
+ async additionalProps() {
28
+ 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.",
34
+ };
35
+ }
36
+ return props;
37
+ },
25
38
  methods: {
26
39
  setWebhookId(teamId, id) {
27
40
  this.db.set(`webhook-${teamId}`, id);
@@ -59,9 +72,26 @@ export default {
59
72
  getLoadedProjectId() {
60
73
  throw new Error("Get loaded project ID not implemented");
61
74
  },
75
+ async isAdmin() {
76
+ const { data } = await this.linearApp.makeAxiosRequest({
77
+ method: "POST",
78
+ data: {
79
+ "query": `{
80
+ user(id: "me") {
81
+ admin
82
+ }
83
+ }`,
84
+ },
85
+ });
86
+ return data?.user?.admin;
87
+ },
62
88
  },
63
89
  hooks: {
64
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
+
65
95
  // Retrieve historical events
66
96
  console.log("Retrieving historical events...");
67
97
  const stream = this.linearApp.paginateResources({
@@ -7,7 +7,7 @@ export default {
7
7
  name: "New Created Issue (Instant)",
8
8
  description: "Emit new event when a new issue is created. See the docs [here](https://developers.linear.app/docs/graphql/webhooks)",
9
9
  type: "source",
10
- version: "0.3.4",
10
+ version: "0.3.6",
11
11
  dedupe: "unique",
12
12
  methods: {
13
13
  ...common.methods,
@@ -7,7 +7,7 @@ export default {
7
7
  name: "New Updated Issue (Instant)",
8
8
  description: "Emit new event when an issue is updated. See the docs [here](https://developers.linear.app/docs/graphql/webhooks)",
9
9
  type: "source",
10
- version: "0.3.4",
10
+ version: "0.3.6",
11
11
  dedupe: "unique",
12
12
  methods: {
13
13
  ...common.methods,
@@ -7,7 +7,7 @@ export default {
7
7
  name: "New Issue Status Updated (Instant)",
8
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
9
  type: "source",
10
- version: "0.1.4",
10
+ version: "0.1.6",
11
11
  dedupe: "unique",
12
12
  props: {
13
13
  linearApp: common.props.linearApp,