@pipedream/productlane 0.0.1 → 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.
- package/README.md +11 -0
- package/actions/create-contact/create-contact.mjs +47 -0
- package/actions/create-feedback/create-feedback.mjs +105 -0
- package/actions/list-project-id-options/list-project-id-options.mjs +26 -0
- package/actions/upvote-project/upvote-project.mjs +41 -0
- package/common/constants.mjs +20 -0
- package/package.json +4 -1
- package/productlane.app.mjs +77 -5
package/README.md
ADDED
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
# Overview
|
|
2
|
+
|
|
3
|
+
The Productlane API on Pipedream allows you to integrate and automate your product feedback loop. You can craft workflows to manage feature requests, prioritize product development, and engage with your user base more effectively. It's useful for product managers who want to streamline user feedback into actionable insights, and for development teams aiming to align their work with customer needs.
|
|
4
|
+
|
|
5
|
+
# Example Use Cases
|
|
6
|
+
|
|
7
|
+
- **Syncing Feature Requests to Project Management Tools**: Automatically create tasks in project management apps like Trello or Asana when new feature requests are submitted via Productlane.
|
|
8
|
+
|
|
9
|
+
- **User Feedback Aggregation**: Gather and sort user feedback from Productlane, sending a daily or weekly digest to your team's Slack channel or email to keep everyone in the loop.
|
|
10
|
+
|
|
11
|
+
- **Feature Launch Announcements**: When a feature moves to 'launched' status in Productlane, trigger a workflow to announce the update across multiple platforms, such as Twitter, company blog, or newsletter.
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
import productlane from "../../productlane.app.mjs";
|
|
2
|
+
|
|
3
|
+
export default {
|
|
4
|
+
key: "productlane-create-contact",
|
|
5
|
+
name: "Create Contact",
|
|
6
|
+
description: "Creates a new contact with email, name, and an array of segments in Productlane. [See the documentation](https://productlane.com/docs/api-reference/contacts/create-contact)",
|
|
7
|
+
version: "0.0.2",
|
|
8
|
+
annotations: {
|
|
9
|
+
destructiveHint: false,
|
|
10
|
+
openWorldHint: true,
|
|
11
|
+
readOnlyHint: false,
|
|
12
|
+
},
|
|
13
|
+
type: "action",
|
|
14
|
+
props: {
|
|
15
|
+
productlane,
|
|
16
|
+
email: {
|
|
17
|
+
propDefinition: [
|
|
18
|
+
productlane,
|
|
19
|
+
"email",
|
|
20
|
+
],
|
|
21
|
+
},
|
|
22
|
+
name: {
|
|
23
|
+
type: "string",
|
|
24
|
+
label: "Name",
|
|
25
|
+
description: "The name of the contact",
|
|
26
|
+
optional: true,
|
|
27
|
+
},
|
|
28
|
+
segments: {
|
|
29
|
+
type: "string[]",
|
|
30
|
+
label: "Segments",
|
|
31
|
+
description: "Array of segments",
|
|
32
|
+
optional: true,
|
|
33
|
+
},
|
|
34
|
+
},
|
|
35
|
+
async run({ $ }) {
|
|
36
|
+
const response = await this.productlane.createContact({
|
|
37
|
+
$,
|
|
38
|
+
data: {
|
|
39
|
+
email: this.email,
|
|
40
|
+
name: this.name,
|
|
41
|
+
segments: this.segments,
|
|
42
|
+
},
|
|
43
|
+
});
|
|
44
|
+
$.export("$summary", `Successfully created contact ${this.email}`);
|
|
45
|
+
return response;
|
|
46
|
+
},
|
|
47
|
+
};
|
|
@@ -0,0 +1,105 @@
|
|
|
1
|
+
import {
|
|
2
|
+
ORIGIN_OPTIONS, PAIN_LEVEL_OPTIONS,
|
|
3
|
+
} from "../../common/constants.mjs";
|
|
4
|
+
import productlane from "../../productlane.app.mjs";
|
|
5
|
+
|
|
6
|
+
export default {
|
|
7
|
+
key: "productlane-create-feedback",
|
|
8
|
+
name: "Create Feedback",
|
|
9
|
+
description:
|
|
10
|
+
"Create new feedback in Productlane. [See the documentation](https://productlane.com/docs/api-reference/portal/create-feedback)",
|
|
11
|
+
version: "0.0.2",
|
|
12
|
+
annotations: {
|
|
13
|
+
destructiveHint: false,
|
|
14
|
+
openWorldHint: true,
|
|
15
|
+
readOnlyHint: false,
|
|
16
|
+
},
|
|
17
|
+
type: "action",
|
|
18
|
+
props: {
|
|
19
|
+
productlane,
|
|
20
|
+
projectId: {
|
|
21
|
+
propDefinition: [
|
|
22
|
+
productlane,
|
|
23
|
+
"projectId",
|
|
24
|
+
],
|
|
25
|
+
},
|
|
26
|
+
email: {
|
|
27
|
+
propDefinition: [
|
|
28
|
+
productlane,
|
|
29
|
+
"email",
|
|
30
|
+
],
|
|
31
|
+
description: "The email for the feedback",
|
|
32
|
+
},
|
|
33
|
+
text: {
|
|
34
|
+
type: "string",
|
|
35
|
+
label: "Text",
|
|
36
|
+
description: "The text of the feedback",
|
|
37
|
+
},
|
|
38
|
+
notifyByEmail: {
|
|
39
|
+
propDefinition: [
|
|
40
|
+
productlane,
|
|
41
|
+
"notify",
|
|
42
|
+
],
|
|
43
|
+
label: "Notify by Email",
|
|
44
|
+
description: "Whether to notify by email",
|
|
45
|
+
},
|
|
46
|
+
notifyBySlack: {
|
|
47
|
+
propDefinition: [
|
|
48
|
+
productlane,
|
|
49
|
+
"notify",
|
|
50
|
+
],
|
|
51
|
+
label: "Notify by Slack",
|
|
52
|
+
description: "Whether to notify by slack",
|
|
53
|
+
},
|
|
54
|
+
origin: {
|
|
55
|
+
type: "string",
|
|
56
|
+
label: "Origin",
|
|
57
|
+
description: "The origin of the feedback",
|
|
58
|
+
optional: true,
|
|
59
|
+
options: ORIGIN_OPTIONS,
|
|
60
|
+
},
|
|
61
|
+
painLevel: {
|
|
62
|
+
type: "string",
|
|
63
|
+
label: "Pain Level",
|
|
64
|
+
description: "The pain level of the feedback",
|
|
65
|
+
options: PAIN_LEVEL_OPTIONS,
|
|
66
|
+
},
|
|
67
|
+
},
|
|
68
|
+
async run({ $ }) {
|
|
69
|
+
const {
|
|
70
|
+
email,
|
|
71
|
+
notifyByEmail,
|
|
72
|
+
notifyBySlack,
|
|
73
|
+
origin,
|
|
74
|
+
painLevel,
|
|
75
|
+
text,
|
|
76
|
+
projectId,
|
|
77
|
+
} = this;
|
|
78
|
+
|
|
79
|
+
const data = {
|
|
80
|
+
email: email,
|
|
81
|
+
notify: ((notifyByEmail ?? notifyBySlack) !== undefined)
|
|
82
|
+
? {
|
|
83
|
+
email: notifyByEmail,
|
|
84
|
+
slack: notifyBySlack,
|
|
85
|
+
}
|
|
86
|
+
: undefined,
|
|
87
|
+
origin: origin,
|
|
88
|
+
painLevel: painLevel,
|
|
89
|
+
text: text,
|
|
90
|
+
projectId: projectId,
|
|
91
|
+
};
|
|
92
|
+
|
|
93
|
+
const response = await this.productlane.createFeedback({
|
|
94
|
+
$,
|
|
95
|
+
data,
|
|
96
|
+
});
|
|
97
|
+
|
|
98
|
+
$.export(
|
|
99
|
+
"$summary",
|
|
100
|
+
`Successfully created feedback with ID: ${response.id}`,
|
|
101
|
+
);
|
|
102
|
+
|
|
103
|
+
return response;
|
|
104
|
+
},
|
|
105
|
+
};
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import productlane from "../../productlane.app.mjs";
|
|
2
|
+
|
|
3
|
+
export default {
|
|
4
|
+
key: "productlane-list-project-id-options",
|
|
5
|
+
name: "List Project ID Options",
|
|
6
|
+
description: "Retrieves available options for the Project ID field.",
|
|
7
|
+
version: "0.0.1",
|
|
8
|
+
type: "action",
|
|
9
|
+
annotations: {
|
|
10
|
+
destructiveHint: false,
|
|
11
|
+
openWorldHint: true,
|
|
12
|
+
readOnlyHint: true,
|
|
13
|
+
},
|
|
14
|
+
props: {
|
|
15
|
+
productlane,
|
|
16
|
+
},
|
|
17
|
+
async run({ $ }) {
|
|
18
|
+
const options = await productlane.propDefinitions.projectId.options.call(this.productlane, {});
|
|
19
|
+
$.export("$summary", `Successfully retrieved ${options.length} option${
|
|
20
|
+
options.length === 1
|
|
21
|
+
? ""
|
|
22
|
+
: "s"
|
|
23
|
+
}`);
|
|
24
|
+
return options;
|
|
25
|
+
},
|
|
26
|
+
};
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
import productlane from "../../productlane.app.mjs";
|
|
2
|
+
|
|
3
|
+
export default {
|
|
4
|
+
key: "productlane-upvote-project",
|
|
5
|
+
name: "Upvote Project",
|
|
6
|
+
description: "Upvotes a project by ID. [See the documentation](https://productlane.com/docs/api-reference/portal/upvote-project)",
|
|
7
|
+
version: "0.0.2",
|
|
8
|
+
annotations: {
|
|
9
|
+
destructiveHint: false,
|
|
10
|
+
openWorldHint: true,
|
|
11
|
+
readOnlyHint: false,
|
|
12
|
+
},
|
|
13
|
+
type: "action",
|
|
14
|
+
props: {
|
|
15
|
+
productlane,
|
|
16
|
+
projectId: {
|
|
17
|
+
propDefinition: [
|
|
18
|
+
productlane,
|
|
19
|
+
"projectId",
|
|
20
|
+
],
|
|
21
|
+
},
|
|
22
|
+
email: {
|
|
23
|
+
propDefinition: [
|
|
24
|
+
productlane,
|
|
25
|
+
"email",
|
|
26
|
+
],
|
|
27
|
+
description: "The email associated with the upvote",
|
|
28
|
+
},
|
|
29
|
+
},
|
|
30
|
+
async run({ $ }) {
|
|
31
|
+
const response = await this.productlane.upvoteProject({
|
|
32
|
+
$,
|
|
33
|
+
projectId: this.projectId,
|
|
34
|
+
data: {
|
|
35
|
+
email: this.email,
|
|
36
|
+
},
|
|
37
|
+
});
|
|
38
|
+
$.export("$summary", "Successfully upvoted project");
|
|
39
|
+
return response;
|
|
40
|
+
},
|
|
41
|
+
};
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
export const ORIGIN_OPTIONS = [
|
|
2
|
+
"INAPP",
|
|
3
|
+
"PORTAL",
|
|
4
|
+
"API",
|
|
5
|
+
"SLACK",
|
|
6
|
+
"INTERCOM",
|
|
7
|
+
"INTERCOM_ATTACHMENT",
|
|
8
|
+
"ZENDESK_ATTACHMENT",
|
|
9
|
+
"FRONT_ATTACHMENT",
|
|
10
|
+
"EMAIL",
|
|
11
|
+
"ZAPIER",
|
|
12
|
+
"HUBSPOT",
|
|
13
|
+
];
|
|
14
|
+
|
|
15
|
+
export const PAIN_LEVEL_OPTIONS = [
|
|
16
|
+
"UNKNOWN",
|
|
17
|
+
"LOW",
|
|
18
|
+
"MEDIUM",
|
|
19
|
+
"HIGH",
|
|
20
|
+
];
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@pipedream/productlane",
|
|
3
|
-
"version": "0.0
|
|
3
|
+
"version": "0.2.0",
|
|
4
4
|
"description": "Pipedream Productlane Components",
|
|
5
5
|
"main": "productlane.app.mjs",
|
|
6
6
|
"keywords": [
|
|
@@ -11,5 +11,8 @@
|
|
|
11
11
|
"author": "Pipedream <support@pipedream.com> (https://pipedream.com/)",
|
|
12
12
|
"publishConfig": {
|
|
13
13
|
"access": "public"
|
|
14
|
+
},
|
|
15
|
+
"dependencies": {
|
|
16
|
+
"@pipedream/platform": "^1.6.8"
|
|
14
17
|
}
|
|
15
18
|
}
|
package/productlane.app.mjs
CHANGED
|
@@ -1,11 +1,83 @@
|
|
|
1
|
+
import { axios } from "@pipedream/platform";
|
|
2
|
+
|
|
1
3
|
export default {
|
|
2
4
|
type: "app",
|
|
3
5
|
app: "productlane",
|
|
4
|
-
propDefinitions: {
|
|
6
|
+
propDefinitions: {
|
|
7
|
+
projectId: {
|
|
8
|
+
type: "string",
|
|
9
|
+
label: "Project ID",
|
|
10
|
+
description: "The ID of the project. [See the documentation](https://productlane.com/docs/api-reference/portal/list-projects) for more information",
|
|
11
|
+
async options() {
|
|
12
|
+
const projects = await this.listProjects();
|
|
13
|
+
return projects.map((p) => ({
|
|
14
|
+
label: p.name,
|
|
15
|
+
value: p.id,
|
|
16
|
+
}));
|
|
17
|
+
},
|
|
18
|
+
},
|
|
19
|
+
email: {
|
|
20
|
+
type: "string",
|
|
21
|
+
label: "Email",
|
|
22
|
+
description: "The email of the contact",
|
|
23
|
+
},
|
|
24
|
+
notify: {
|
|
25
|
+
type: "boolean",
|
|
26
|
+
label: "Notify",
|
|
27
|
+
description: "Whether to notify",
|
|
28
|
+
optional: true,
|
|
29
|
+
},
|
|
30
|
+
},
|
|
5
31
|
methods: {
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
32
|
+
_baseUrl() {
|
|
33
|
+
return "https://productlane.com/api/v1";
|
|
34
|
+
},
|
|
35
|
+
_workspaceId() {
|
|
36
|
+
return this.$auth.workspace_id;
|
|
37
|
+
},
|
|
38
|
+
async _makeRequest({
|
|
39
|
+
$ = this,
|
|
40
|
+
path,
|
|
41
|
+
headers,
|
|
42
|
+
...otherOpts
|
|
43
|
+
}) {
|
|
44
|
+
return axios($, {
|
|
45
|
+
...otherOpts,
|
|
46
|
+
url: this._baseUrl() + path,
|
|
47
|
+
headers: {
|
|
48
|
+
...headers,
|
|
49
|
+
Authorization: `Bearer ${this.$auth.api_key}`,
|
|
50
|
+
},
|
|
51
|
+
});
|
|
52
|
+
},
|
|
53
|
+
async createContact(opts) {
|
|
54
|
+
return this._makeRequest({
|
|
55
|
+
...opts,
|
|
56
|
+
path: "/contacts",
|
|
57
|
+
method: "POST",
|
|
58
|
+
});
|
|
59
|
+
},
|
|
60
|
+
async upvoteProject({
|
|
61
|
+
projectId, ...opts
|
|
62
|
+
}) {
|
|
63
|
+
return this._makeRequest({
|
|
64
|
+
...opts,
|
|
65
|
+
path: `/projects/${projectId}/upvotes`,
|
|
66
|
+
method: "POST",
|
|
67
|
+
});
|
|
68
|
+
},
|
|
69
|
+
async createFeedback(opts) {
|
|
70
|
+
return this._makeRequest({
|
|
71
|
+
...opts,
|
|
72
|
+
path: "/feedback",
|
|
73
|
+
method: "POST",
|
|
74
|
+
});
|
|
75
|
+
},
|
|
76
|
+
async listProjects() {
|
|
77
|
+
const { projects } = await this._makeRequest({
|
|
78
|
+
path: `/projects/${this._workspaceId()}`,
|
|
79
|
+
});
|
|
80
|
+
return projects;
|
|
9
81
|
},
|
|
10
82
|
},
|
|
11
|
-
};
|
|
83
|
+
};
|