@pipedream/linear_app 0.3.3
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/LICENSE +7 -0
- package/actions/create-issue/create-issue.mjs +60 -0
- package/actions/get-teams/get-teams.mjs +19 -0
- package/actions/search-issues/search-issues.mjs +116 -0
- package/actions/update-issue/update-issue.mjs +73 -0
- package/common/constants.mjs +55 -0
- package/linear_app.app.mjs +201 -0
- package/package.json +20 -0
- package/sources/common/webhook.mjs +103 -0
- package/sources/issue-created-instant/issue-created-instant.mjs +40 -0
- package/sources/issue-updated-instant/issue-updated-instant.mjs +40 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
Copyright 2020 Pipedream, Inc.
|
|
2
|
+
|
|
3
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
|
|
4
|
+
|
|
5
|
+
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
|
|
6
|
+
|
|
7
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
import linearApp from "../../linear_app.app.mjs";
|
|
2
|
+
|
|
3
|
+
export default {
|
|
4
|
+
type: "action",
|
|
5
|
+
key: "linear_app-create-issue",
|
|
6
|
+
name: "Create Issue",
|
|
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.3.3",
|
|
9
|
+
props: {
|
|
10
|
+
linearApp,
|
|
11
|
+
teamId: {
|
|
12
|
+
propDefinition: [
|
|
13
|
+
linearApp,
|
|
14
|
+
"teamId",
|
|
15
|
+
],
|
|
16
|
+
},
|
|
17
|
+
title: {
|
|
18
|
+
propDefinition: [
|
|
19
|
+
linearApp,
|
|
20
|
+
"issueTitle",
|
|
21
|
+
],
|
|
22
|
+
},
|
|
23
|
+
description: {
|
|
24
|
+
propDefinition: [
|
|
25
|
+
linearApp,
|
|
26
|
+
"issueDescription",
|
|
27
|
+
],
|
|
28
|
+
},
|
|
29
|
+
assigneeId: {
|
|
30
|
+
propDefinition: [
|
|
31
|
+
linearApp,
|
|
32
|
+
"assigneeId",
|
|
33
|
+
],
|
|
34
|
+
},
|
|
35
|
+
},
|
|
36
|
+
async run({ $ }) {
|
|
37
|
+
const {
|
|
38
|
+
title,
|
|
39
|
+
description,
|
|
40
|
+
teamId,
|
|
41
|
+
assigneeId,
|
|
42
|
+
} = this;
|
|
43
|
+
|
|
44
|
+
const response =
|
|
45
|
+
await this.linearApp.createIssue({
|
|
46
|
+
teamId,
|
|
47
|
+
title,
|
|
48
|
+
description,
|
|
49
|
+
assigneeId,
|
|
50
|
+
});
|
|
51
|
+
|
|
52
|
+
if (response.success) {
|
|
53
|
+
$.export("summary", `Created issue ${response._issue.id}`);
|
|
54
|
+
} else {
|
|
55
|
+
$.export("summary", "Failed to create issue");
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
return response;
|
|
59
|
+
},
|
|
60
|
+
};
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import linearApp from "../../linear_app.app.mjs";
|
|
2
|
+
|
|
3
|
+
export default {
|
|
4
|
+
key: "linear_app-get-teams",
|
|
5
|
+
name: "Get Teams",
|
|
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.1.3",
|
|
8
|
+
type: "action",
|
|
9
|
+
props: {
|
|
10
|
+
linearApp,
|
|
11
|
+
},
|
|
12
|
+
async run({ $ }) {
|
|
13
|
+
const { nodes: teams } = await this.linearApp.listTeams();
|
|
14
|
+
|
|
15
|
+
$.export("summary", `Found ${teams.length} teams(s)`);
|
|
16
|
+
|
|
17
|
+
return teams;
|
|
18
|
+
},
|
|
19
|
+
};
|
|
@@ -0,0 +1,116 @@
|
|
|
1
|
+
import linearApp from "../../linear_app.app.mjs";
|
|
2
|
+
|
|
3
|
+
export default {
|
|
4
|
+
key: "linear_app-search-issues",
|
|
5
|
+
name: "Search Issues",
|
|
6
|
+
description: "Search issues (API Key). See the docs [here](https://developers.linear.app/docs/graphql/working-with-the-graphql-api)",
|
|
7
|
+
type: "action",
|
|
8
|
+
version: "0.1.0",
|
|
9
|
+
props: {
|
|
10
|
+
linearApp,
|
|
11
|
+
query: {
|
|
12
|
+
propDefinition: [
|
|
13
|
+
linearApp,
|
|
14
|
+
"query",
|
|
15
|
+
],
|
|
16
|
+
},
|
|
17
|
+
teamId: {
|
|
18
|
+
propDefinition: [
|
|
19
|
+
linearApp,
|
|
20
|
+
"teamId",
|
|
21
|
+
],
|
|
22
|
+
optional: true,
|
|
23
|
+
},
|
|
24
|
+
projectId: {
|
|
25
|
+
propDefinition: [
|
|
26
|
+
linearApp,
|
|
27
|
+
"projectId",
|
|
28
|
+
],
|
|
29
|
+
},
|
|
30
|
+
assigneeId: {
|
|
31
|
+
propDefinition: [
|
|
32
|
+
linearApp,
|
|
33
|
+
"assigneeId",
|
|
34
|
+
],
|
|
35
|
+
},
|
|
36
|
+
issueLabels: {
|
|
37
|
+
propDefinition: [
|
|
38
|
+
linearApp,
|
|
39
|
+
"issueLabels",
|
|
40
|
+
],
|
|
41
|
+
},
|
|
42
|
+
orderBy: {
|
|
43
|
+
propDefinition: [
|
|
44
|
+
linearApp,
|
|
45
|
+
"orderBy",
|
|
46
|
+
],
|
|
47
|
+
},
|
|
48
|
+
includeArchived: {
|
|
49
|
+
propDefinition: [
|
|
50
|
+
linearApp,
|
|
51
|
+
"includeArchived",
|
|
52
|
+
],
|
|
53
|
+
},
|
|
54
|
+
},
|
|
55
|
+
methods: {
|
|
56
|
+
buildFilter() {
|
|
57
|
+
return {
|
|
58
|
+
team: {
|
|
59
|
+
id: {
|
|
60
|
+
eq: this.teamId,
|
|
61
|
+
},
|
|
62
|
+
},
|
|
63
|
+
project: {
|
|
64
|
+
id: {
|
|
65
|
+
eq: this.projectId,
|
|
66
|
+
},
|
|
67
|
+
},
|
|
68
|
+
assignee: {
|
|
69
|
+
id: {
|
|
70
|
+
eq: this.assigneeId,
|
|
71
|
+
},
|
|
72
|
+
},
|
|
73
|
+
labels: {
|
|
74
|
+
name: {
|
|
75
|
+
in: this.issueLabels,
|
|
76
|
+
},
|
|
77
|
+
},
|
|
78
|
+
};
|
|
79
|
+
},
|
|
80
|
+
},
|
|
81
|
+
async run({ $ }) {
|
|
82
|
+
const {
|
|
83
|
+
query,
|
|
84
|
+
orderBy,
|
|
85
|
+
includeArchived,
|
|
86
|
+
} = this;
|
|
87
|
+
|
|
88
|
+
let issues = [];
|
|
89
|
+
let hasNextPage;
|
|
90
|
+
let after;
|
|
91
|
+
const filter = this.buildFilter();
|
|
92
|
+
|
|
93
|
+
do {
|
|
94
|
+
const {
|
|
95
|
+
nodes,
|
|
96
|
+
pageInfo,
|
|
97
|
+
} = await this.linearApp.searchIssues({
|
|
98
|
+
query,
|
|
99
|
+
variables: {
|
|
100
|
+
filter,
|
|
101
|
+
orderBy,
|
|
102
|
+
after,
|
|
103
|
+
includeArchived,
|
|
104
|
+
},
|
|
105
|
+
});
|
|
106
|
+
|
|
107
|
+
issues = issues.concat(nodes);
|
|
108
|
+
after = pageInfo.endCursor;
|
|
109
|
+
hasNextPage = pageInfo.hasNextPage;
|
|
110
|
+
} while (hasNextPage);
|
|
111
|
+
|
|
112
|
+
$.export("summary", `Found ${issues.length} issues`);
|
|
113
|
+
|
|
114
|
+
return issues;
|
|
115
|
+
},
|
|
116
|
+
};
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
import linearApp from "../../linear_app.app.mjs";
|
|
2
|
+
|
|
3
|
+
export default {
|
|
4
|
+
key: "linear_app-update-issue",
|
|
5
|
+
name: "Update Issue",
|
|
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
|
+
type: "action",
|
|
8
|
+
version: "0.0.2",
|
|
9
|
+
props: {
|
|
10
|
+
linearApp,
|
|
11
|
+
issueId: {
|
|
12
|
+
propDefinition: [
|
|
13
|
+
linearApp,
|
|
14
|
+
"issueId",
|
|
15
|
+
],
|
|
16
|
+
},
|
|
17
|
+
title: {
|
|
18
|
+
optional: true,
|
|
19
|
+
propDefinition: [
|
|
20
|
+
linearApp,
|
|
21
|
+
"issueTitle",
|
|
22
|
+
],
|
|
23
|
+
},
|
|
24
|
+
description: {
|
|
25
|
+
optional: true,
|
|
26
|
+
propDefinition: [
|
|
27
|
+
linearApp,
|
|
28
|
+
"issueDescription",
|
|
29
|
+
],
|
|
30
|
+
},
|
|
31
|
+
teamId: {
|
|
32
|
+
optional: true,
|
|
33
|
+
propDefinition: [
|
|
34
|
+
linearApp,
|
|
35
|
+
"teamId",
|
|
36
|
+
],
|
|
37
|
+
},
|
|
38
|
+
assigneeId: {
|
|
39
|
+
propDefinition: [
|
|
40
|
+
linearApp,
|
|
41
|
+
"assigneeId",
|
|
42
|
+
],
|
|
43
|
+
},
|
|
44
|
+
},
|
|
45
|
+
async run({ $ }) {
|
|
46
|
+
const {
|
|
47
|
+
issueId,
|
|
48
|
+
title,
|
|
49
|
+
description,
|
|
50
|
+
teamId,
|
|
51
|
+
assigneeId,
|
|
52
|
+
} = this;
|
|
53
|
+
|
|
54
|
+
const response =
|
|
55
|
+
await this.linearApp.updateIssue({
|
|
56
|
+
issueId,
|
|
57
|
+
input: {
|
|
58
|
+
teamId,
|
|
59
|
+
title,
|
|
60
|
+
description,
|
|
61
|
+
assigneeId,
|
|
62
|
+
},
|
|
63
|
+
});
|
|
64
|
+
|
|
65
|
+
if (response.success) {
|
|
66
|
+
$.export("summary", `Updated issue ${response._issue.id}`);
|
|
67
|
+
} else {
|
|
68
|
+
$.export("summary", "Failed to update issue");
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
return response;
|
|
72
|
+
},
|
|
73
|
+
};
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
const WEBHOOK_ID = "webhookId";
|
|
2
|
+
const LINEAR_DELIVERY_HEADER = "linear-delivery";
|
|
3
|
+
const DEFAULT_LIMIT = 50;
|
|
4
|
+
|
|
5
|
+
const ACTION = {
|
|
6
|
+
CREATE: "create",
|
|
7
|
+
UPDATE: "update",
|
|
8
|
+
};
|
|
9
|
+
|
|
10
|
+
const RESOURCE_TYPE = {
|
|
11
|
+
COMMENT: "Comment",
|
|
12
|
+
ISSUE: "Issue",
|
|
13
|
+
ISSUE_LABEL: "IssueLabel",
|
|
14
|
+
PROJECT: "Project",
|
|
15
|
+
CYCLE: "Cycle",
|
|
16
|
+
REACTION: "Reaction",
|
|
17
|
+
};
|
|
18
|
+
|
|
19
|
+
const RESOURCE_TYPES = Object.values(RESOURCE_TYPE);
|
|
20
|
+
|
|
21
|
+
// https://developers.linear.app/docs/graphql/webhooks#how-does-a-webhook-work
|
|
22
|
+
// To make sure a Webhook POST is truly created by Linear, check the request
|
|
23
|
+
// that originates from one of the following IPs:
|
|
24
|
+
const CLIENT_IPS = [
|
|
25
|
+
"35.231.147.226",
|
|
26
|
+
"35.243.134.228",
|
|
27
|
+
];
|
|
28
|
+
|
|
29
|
+
const FIELD = {
|
|
30
|
+
CREATED_AT: "createdAt",
|
|
31
|
+
UPDATED_AT: "updatedAt",
|
|
32
|
+
};
|
|
33
|
+
|
|
34
|
+
const ORDER_BY_OPTIONS = [
|
|
35
|
+
{
|
|
36
|
+
label: "Created at",
|
|
37
|
+
value: FIELD.CREATED_AT,
|
|
38
|
+
},
|
|
39
|
+
{
|
|
40
|
+
label: "Updated at",
|
|
41
|
+
value: FIELD.UPDATED_AT,
|
|
42
|
+
},
|
|
43
|
+
];
|
|
44
|
+
|
|
45
|
+
export default {
|
|
46
|
+
WEBHOOK_ID,
|
|
47
|
+
LINEAR_DELIVERY_HEADER,
|
|
48
|
+
DEFAULT_LIMIT,
|
|
49
|
+
ACTION,
|
|
50
|
+
RESOURCE_TYPE,
|
|
51
|
+
RESOURCE_TYPES,
|
|
52
|
+
CLIENT_IPS,
|
|
53
|
+
ORDER_BY_OPTIONS,
|
|
54
|
+
FIELD,
|
|
55
|
+
};
|
|
@@ -0,0 +1,201 @@
|
|
|
1
|
+
import { LinearClient } from "@linear/sdk";
|
|
2
|
+
import constants from "./common/constants.mjs";
|
|
3
|
+
|
|
4
|
+
export default {
|
|
5
|
+
type: "app",
|
|
6
|
+
app: "linear_app",
|
|
7
|
+
propDefinitions: {
|
|
8
|
+
issueId: {
|
|
9
|
+
type: "string",
|
|
10
|
+
label: "Issue ID",
|
|
11
|
+
description: "The issue ID to update",
|
|
12
|
+
async options({ prevContext }) {
|
|
13
|
+
return this.listResourcesOptions({
|
|
14
|
+
prevContext,
|
|
15
|
+
resourcesFn: this.listIssues,
|
|
16
|
+
resouceMapper: ({
|
|
17
|
+
id, title,
|
|
18
|
+
}) => ({
|
|
19
|
+
label: title,
|
|
20
|
+
value: id,
|
|
21
|
+
}),
|
|
22
|
+
});
|
|
23
|
+
},
|
|
24
|
+
},
|
|
25
|
+
teamId: {
|
|
26
|
+
type: "string",
|
|
27
|
+
label: "Team ID",
|
|
28
|
+
description: "The identifier or key of the team associated with the issue.",
|
|
29
|
+
async options({ prevContext }) {
|
|
30
|
+
return this.listResourcesOptions({
|
|
31
|
+
prevContext,
|
|
32
|
+
resourcesFn: this.listTeams,
|
|
33
|
+
resouceMapper: ({
|
|
34
|
+
id, name,
|
|
35
|
+
}) => ({
|
|
36
|
+
label: name,
|
|
37
|
+
value: id,
|
|
38
|
+
}),
|
|
39
|
+
});
|
|
40
|
+
},
|
|
41
|
+
},
|
|
42
|
+
projectId: {
|
|
43
|
+
type: "string",
|
|
44
|
+
label: "Project ID",
|
|
45
|
+
description: "The identifier or key of the project associated with the issue.",
|
|
46
|
+
optional: true,
|
|
47
|
+
async options({ prevContext }) {
|
|
48
|
+
return this.listResourcesOptions({
|
|
49
|
+
prevContext,
|
|
50
|
+
resourcesFn: this.listProjects,
|
|
51
|
+
resouceMapper: ({
|
|
52
|
+
id, name,
|
|
53
|
+
}) => ({
|
|
54
|
+
label: name,
|
|
55
|
+
value: id,
|
|
56
|
+
}),
|
|
57
|
+
});
|
|
58
|
+
},
|
|
59
|
+
},
|
|
60
|
+
issueTitle: {
|
|
61
|
+
type: "string",
|
|
62
|
+
label: "Title",
|
|
63
|
+
description: "The title of the issue.",
|
|
64
|
+
},
|
|
65
|
+
assigneeId: {
|
|
66
|
+
type: "string",
|
|
67
|
+
label: "Assignee ID",
|
|
68
|
+
description: "The identifier of the user to assign the issue to.",
|
|
69
|
+
optional: true,
|
|
70
|
+
async options({ prevContext }) {
|
|
71
|
+
return this.listResourcesOptions({
|
|
72
|
+
prevContext,
|
|
73
|
+
resourcesFn: this.listUsers,
|
|
74
|
+
resouceMapper: ({
|
|
75
|
+
id, name,
|
|
76
|
+
}) => ({
|
|
77
|
+
label: name,
|
|
78
|
+
value: id,
|
|
79
|
+
}),
|
|
80
|
+
});
|
|
81
|
+
},
|
|
82
|
+
},
|
|
83
|
+
boardOrder: {
|
|
84
|
+
type: "string",
|
|
85
|
+
label: "Board order",
|
|
86
|
+
description: "The position of the issue in its column on the board view.",
|
|
87
|
+
optional: true,
|
|
88
|
+
},
|
|
89
|
+
issueDescription: {
|
|
90
|
+
type: "string",
|
|
91
|
+
label: "Description",
|
|
92
|
+
description: "The issue description in markdown format.",
|
|
93
|
+
optional: true,
|
|
94
|
+
},
|
|
95
|
+
issueLabels: {
|
|
96
|
+
type: "string[]",
|
|
97
|
+
label: "Issue Labels",
|
|
98
|
+
description: "The labels in the issue.",
|
|
99
|
+
optional: true,
|
|
100
|
+
async options({ prevContext }) {
|
|
101
|
+
return this.listResourcesOptions({
|
|
102
|
+
prevContext,
|
|
103
|
+
resourcesFn: this.listIssueLabels,
|
|
104
|
+
resouceMapper: ({ name }) => name,
|
|
105
|
+
});
|
|
106
|
+
},
|
|
107
|
+
},
|
|
108
|
+
query: {
|
|
109
|
+
type: "string",
|
|
110
|
+
label: "Query",
|
|
111
|
+
description: "Search string to look for.",
|
|
112
|
+
},
|
|
113
|
+
orderBy: {
|
|
114
|
+
type: "string",
|
|
115
|
+
label: "Order by",
|
|
116
|
+
description: "By which field should the pagination order by. Available options are `createdAt` (default) and `updatedAt`.",
|
|
117
|
+
optional: true,
|
|
118
|
+
options: constants.ORDER_BY_OPTIONS,
|
|
119
|
+
},
|
|
120
|
+
includeArchived: {
|
|
121
|
+
type: "boolean",
|
|
122
|
+
label: "Include archived",
|
|
123
|
+
description: "Should archived resources be included (default: `false`).",
|
|
124
|
+
optional: true,
|
|
125
|
+
},
|
|
126
|
+
},
|
|
127
|
+
methods: {
|
|
128
|
+
getClientOptions(options = {}) {
|
|
129
|
+
return {
|
|
130
|
+
apiKey: this.$auth.api_key,
|
|
131
|
+
...options,
|
|
132
|
+
};
|
|
133
|
+
},
|
|
134
|
+
client(options = {}) {
|
|
135
|
+
return new LinearClient(this.getClientOptions(options));
|
|
136
|
+
},
|
|
137
|
+
async createWebhook(input) {
|
|
138
|
+
return this.client().webhookCreate(input);
|
|
139
|
+
},
|
|
140
|
+
async deleteWebhook(id) {
|
|
141
|
+
return this.client().webhookDelete(id);
|
|
142
|
+
},
|
|
143
|
+
async createIssue(input) {
|
|
144
|
+
return this.client().issueCreate(input);
|
|
145
|
+
},
|
|
146
|
+
async updateIssue({
|
|
147
|
+
issueId, input,
|
|
148
|
+
}) {
|
|
149
|
+
return this.client().issueUpdate(issueId, input);
|
|
150
|
+
},
|
|
151
|
+
async searchIssues({
|
|
152
|
+
query, variables,
|
|
153
|
+
}) {
|
|
154
|
+
return this.client().issueSearch(query, variables);
|
|
155
|
+
},
|
|
156
|
+
async listTeams(variables = {}) {
|
|
157
|
+
return this.client().teams(variables);
|
|
158
|
+
},
|
|
159
|
+
async listProjects(variables = {}) {
|
|
160
|
+
return this.client().projects(variables);
|
|
161
|
+
},
|
|
162
|
+
async listUsers(variables = {}) {
|
|
163
|
+
return this.client().users(variables);
|
|
164
|
+
},
|
|
165
|
+
async listIssues(variables = {}) {
|
|
166
|
+
return this.client().issues(variables);
|
|
167
|
+
},
|
|
168
|
+
async listIssueLabels(variables = {}) {
|
|
169
|
+
return this.client().issueLabels(variables);
|
|
170
|
+
},
|
|
171
|
+
async listResourcesOptions({
|
|
172
|
+
prevContext, resourcesFn, resouceMapper,
|
|
173
|
+
}) {
|
|
174
|
+
const {
|
|
175
|
+
after,
|
|
176
|
+
hasNextPage,
|
|
177
|
+
} = prevContext;
|
|
178
|
+
|
|
179
|
+
if (hasNextPage === false) {
|
|
180
|
+
return [];
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
const {
|
|
184
|
+
nodes,
|
|
185
|
+
pageInfo,
|
|
186
|
+
} =
|
|
187
|
+
await resourcesFn({
|
|
188
|
+
after,
|
|
189
|
+
first: constants.DEFAULT_LIMIT,
|
|
190
|
+
});
|
|
191
|
+
|
|
192
|
+
return {
|
|
193
|
+
options: nodes.map(resouceMapper),
|
|
194
|
+
context: {
|
|
195
|
+
after: pageInfo.endCursor,
|
|
196
|
+
hasNextPage: pageInfo.hasNextPage,
|
|
197
|
+
},
|
|
198
|
+
};
|
|
199
|
+
},
|
|
200
|
+
},
|
|
201
|
+
};
|
package/package.json
ADDED
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@pipedream/linear_app",
|
|
3
|
+
"version": "0.3.3",
|
|
4
|
+
"description": "Pipedream Linear_app Components",
|
|
5
|
+
"main": "linear_app.app.mjs",
|
|
6
|
+
"keywords": [
|
|
7
|
+
"pipedream",
|
|
8
|
+
"linear_app"
|
|
9
|
+
],
|
|
10
|
+
"homepage": "https://pipedream.com/apps/linear_app",
|
|
11
|
+
"author": "Pipedream <support@pipedream.com> (https://pipedream.com/)",
|
|
12
|
+
"license": "MIT",
|
|
13
|
+
"dependencies": {
|
|
14
|
+
"@linear/sdk": "^1.22.0"
|
|
15
|
+
},
|
|
16
|
+
"gitHead": "e12480b94cc03bed4808ebc6b13e7fdb3a1ba535",
|
|
17
|
+
"publishConfig": {
|
|
18
|
+
"access": "public"
|
|
19
|
+
}
|
|
20
|
+
}
|
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
import linearApp from "../../linear_app.app.mjs";
|
|
2
|
+
import constants from "../../common/constants.mjs";
|
|
3
|
+
|
|
4
|
+
export default {
|
|
5
|
+
props: {
|
|
6
|
+
linearApp,
|
|
7
|
+
teamId: {
|
|
8
|
+
propDefinition: [
|
|
9
|
+
linearApp,
|
|
10
|
+
"teamId",
|
|
11
|
+
],
|
|
12
|
+
optional: true,
|
|
13
|
+
},
|
|
14
|
+
projectId: {
|
|
15
|
+
propDefinition: [
|
|
16
|
+
linearApp,
|
|
17
|
+
"projectId",
|
|
18
|
+
],
|
|
19
|
+
},
|
|
20
|
+
http: "$.interface.http",
|
|
21
|
+
db: "$.service.db",
|
|
22
|
+
},
|
|
23
|
+
methods: {
|
|
24
|
+
setWebhookId(id) {
|
|
25
|
+
this.db.set(constants.WEBHOOK_ID, id);
|
|
26
|
+
},
|
|
27
|
+
getWebhookId() {
|
|
28
|
+
return this.db.get(constants.WEBHOOK_ID);
|
|
29
|
+
},
|
|
30
|
+
isRelevant(body) {
|
|
31
|
+
if (!this.getActions().includes(body?.action)) {
|
|
32
|
+
return false;
|
|
33
|
+
}
|
|
34
|
+
if (this.projectId) {
|
|
35
|
+
return body.data.projectId === this.projectId;
|
|
36
|
+
}
|
|
37
|
+
return true;
|
|
38
|
+
},
|
|
39
|
+
isWebhookValid(clientIp) {
|
|
40
|
+
return constants.CLIENT_IPS.includes(clientIp);
|
|
41
|
+
},
|
|
42
|
+
getResourceTypes() {
|
|
43
|
+
throw new Error("getResourceTypes is not implemented");
|
|
44
|
+
},
|
|
45
|
+
getWebhookLabel() {
|
|
46
|
+
throw new Error("getWebhookLabel is not implemented");
|
|
47
|
+
},
|
|
48
|
+
getActions() {
|
|
49
|
+
throw new Error("getActions is not implemented");
|
|
50
|
+
},
|
|
51
|
+
getMetadata() {
|
|
52
|
+
throw new Error("getMetadata is not implemented");
|
|
53
|
+
},
|
|
54
|
+
},
|
|
55
|
+
hooks: {
|
|
56
|
+
async activate() {
|
|
57
|
+
const params = {
|
|
58
|
+
resourceTypes: this.getResourceTypes(),
|
|
59
|
+
url: this.http.endpoint,
|
|
60
|
+
label: this.getWebhookLabel(),
|
|
61
|
+
};
|
|
62
|
+
|
|
63
|
+
if (this.teamId) {
|
|
64
|
+
params.teamId = this.teamId;
|
|
65
|
+
} else {
|
|
66
|
+
params.allPublicTeams = true;
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
const { _webhook: webhook } = await this.linearApp.createWebhook(params);
|
|
70
|
+
this.setWebhookId(webhook.id);
|
|
71
|
+
},
|
|
72
|
+
async deactivate() {
|
|
73
|
+
const webhookId = this.getWebhookId();
|
|
74
|
+
if (webhookId) {
|
|
75
|
+
await this.linearApp.deleteWebhook(webhookId);
|
|
76
|
+
}
|
|
77
|
+
},
|
|
78
|
+
},
|
|
79
|
+
async run(event) {
|
|
80
|
+
const {
|
|
81
|
+
client_ip: clientIp,
|
|
82
|
+
body,
|
|
83
|
+
headers,
|
|
84
|
+
} = event;
|
|
85
|
+
|
|
86
|
+
const { [constants.LINEAR_DELIVERY_HEADER]: delivery } = headers;
|
|
87
|
+
|
|
88
|
+
const resource = {
|
|
89
|
+
...body,
|
|
90
|
+
delivery,
|
|
91
|
+
};
|
|
92
|
+
|
|
93
|
+
if (!this.isWebhookValid(clientIp)) {
|
|
94
|
+
console.log("Webhook is not valid");
|
|
95
|
+
return;
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
if (this.isRelevant(body)) {
|
|
99
|
+
const meta = this.getMetadata(resource);
|
|
100
|
+
this.$emit(body, meta);
|
|
101
|
+
}
|
|
102
|
+
},
|
|
103
|
+
};
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
import common from "../common/webhook.mjs";
|
|
2
|
+
import constants from "../../common/constants.mjs";
|
|
3
|
+
|
|
4
|
+
export default {
|
|
5
|
+
...common,
|
|
6
|
+
key: "linear_app-issue-created-instant",
|
|
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)",
|
|
9
|
+
type: "source",
|
|
10
|
+
version: "0.1.0",
|
|
11
|
+
dedupe: "unique",
|
|
12
|
+
methods: {
|
|
13
|
+
...common.methods,
|
|
14
|
+
getResourceTypes() {
|
|
15
|
+
return [
|
|
16
|
+
constants.RESOURCE_TYPE.ISSUE,
|
|
17
|
+
];
|
|
18
|
+
},
|
|
19
|
+
getWebhookLabel() {
|
|
20
|
+
return "Issue created";
|
|
21
|
+
},
|
|
22
|
+
getActions() {
|
|
23
|
+
return [
|
|
24
|
+
constants.ACTION.CREATE,
|
|
25
|
+
];
|
|
26
|
+
},
|
|
27
|
+
getMetadata(resource) {
|
|
28
|
+
const {
|
|
29
|
+
delivery,
|
|
30
|
+
data,
|
|
31
|
+
createdAt,
|
|
32
|
+
} = resource;
|
|
33
|
+
return {
|
|
34
|
+
id: delivery,
|
|
35
|
+
summary: `Issue created: ${data.title}`,
|
|
36
|
+
ts: Date.parse(createdAt),
|
|
37
|
+
};
|
|
38
|
+
},
|
|
39
|
+
},
|
|
40
|
+
};
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
import common from "../common/webhook.mjs";
|
|
2
|
+
import constants from "../../common/constants.mjs";
|
|
3
|
+
|
|
4
|
+
export default {
|
|
5
|
+
...common,
|
|
6
|
+
key: "linear_app-issue-updated-instant",
|
|
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)",
|
|
9
|
+
type: "source",
|
|
10
|
+
version: "0.1.0",
|
|
11
|
+
dedupe: "unique",
|
|
12
|
+
methods: {
|
|
13
|
+
...common.methods,
|
|
14
|
+
getResourceTypes() {
|
|
15
|
+
return [
|
|
16
|
+
constants.RESOURCE_TYPE.ISSUE,
|
|
17
|
+
];
|
|
18
|
+
},
|
|
19
|
+
getWebhookLabel() {
|
|
20
|
+
return "Issue updated";
|
|
21
|
+
},
|
|
22
|
+
getActions() {
|
|
23
|
+
return [
|
|
24
|
+
constants.ACTION.UPDATE,
|
|
25
|
+
];
|
|
26
|
+
},
|
|
27
|
+
getMetadata(resource) {
|
|
28
|
+
const {
|
|
29
|
+
delivery,
|
|
30
|
+
data,
|
|
31
|
+
updatedAt,
|
|
32
|
+
} = resource;
|
|
33
|
+
return {
|
|
34
|
+
id: delivery,
|
|
35
|
+
summary: `Issue Updated: ${data.title}`,
|
|
36
|
+
ts: Date.parse(updatedAt),
|
|
37
|
+
};
|
|
38
|
+
},
|
|
39
|
+
},
|
|
40
|
+
};
|