@pipedream/sperse 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/actions/add-or-update-contact/add-or-update-contact.mjs +164 -0
- package/actions/create-invoice/create-invoice.mjs +141 -0
- package/actions/create-product/create-product.mjs +141 -0
- package/actions/get-contact-details/get-contact-details.mjs +40 -0
- package/actions/list-contact-group-id-options/list-contact-group-id-options.mjs +24 -0
- package/actions/list-currency-id-options/list-currency-id-options.mjs +24 -0
- package/common/constants.mjs +28 -0
- package/common/utils.mjs +37 -0
- package/package.json +4 -1
- package/sources/common/polling.mjs +67 -0
- package/sources/new-lead/new-lead.mjs +32 -0
- package/sources/new-subscription/new-subscription.mjs +41 -0
- package/sperse.app.mjs +167 -5
|
@@ -0,0 +1,164 @@
|
|
|
1
|
+
import app from "../../sperse.app.mjs";
|
|
2
|
+
import { parseArray } from "../../common/utils.mjs";
|
|
3
|
+
|
|
4
|
+
export default {
|
|
5
|
+
key: "sperse-add-or-update-contact",
|
|
6
|
+
name: "Add or Update Contact",
|
|
7
|
+
description: "Creates or updates a contact. [See the documentation](https://app.sperse.com/app/api/swagger)",
|
|
8
|
+
version: "0.0.1",
|
|
9
|
+
type: "action",
|
|
10
|
+
annotations: {
|
|
11
|
+
readOnlyHint: false,
|
|
12
|
+
destructiveHint: false,
|
|
13
|
+
openWorldHint: true,
|
|
14
|
+
},
|
|
15
|
+
props: {
|
|
16
|
+
app,
|
|
17
|
+
// eslint-disable-next-line pipedream/props-label, pipedream/props-description
|
|
18
|
+
info: {
|
|
19
|
+
type: "alert",
|
|
20
|
+
alertType: "info",
|
|
21
|
+
content: "At least one of the following fields is required: **First Name** or **Last Name**, **Company**, **Email** or **Phone**",
|
|
22
|
+
},
|
|
23
|
+
contactId: {
|
|
24
|
+
description: "The ID of the contact to update. Leave empty to create a new contact.",
|
|
25
|
+
optional: true,
|
|
26
|
+
propDefinition: [
|
|
27
|
+
app,
|
|
28
|
+
"contactId",
|
|
29
|
+
],
|
|
30
|
+
},
|
|
31
|
+
contactGroupId: {
|
|
32
|
+
propDefinition: [
|
|
33
|
+
app,
|
|
34
|
+
"contactGroupId",
|
|
35
|
+
],
|
|
36
|
+
},
|
|
37
|
+
firstName: {
|
|
38
|
+
type: "string",
|
|
39
|
+
label: "First Name",
|
|
40
|
+
description: "The first name of the contact",
|
|
41
|
+
optional: true,
|
|
42
|
+
},
|
|
43
|
+
lastName: {
|
|
44
|
+
type: "string",
|
|
45
|
+
label: "Last Name",
|
|
46
|
+
description: "The last name of the contact",
|
|
47
|
+
optional: true,
|
|
48
|
+
},
|
|
49
|
+
companyName: {
|
|
50
|
+
type: "string",
|
|
51
|
+
label: "Company Name",
|
|
52
|
+
description: "The company name of the contact",
|
|
53
|
+
optional: true,
|
|
54
|
+
},
|
|
55
|
+
emailAddresses: {
|
|
56
|
+
type: "string[]",
|
|
57
|
+
label: "Email Addresses",
|
|
58
|
+
description: `An array of email objects.
|
|
59
|
+
|
|
60
|
+
**Example:**
|
|
61
|
+
\`\`\`json
|
|
62
|
+
[
|
|
63
|
+
{
|
|
64
|
+
"emailAddress": "jane.doe@gmail.com",
|
|
65
|
+
"isActive": true,
|
|
66
|
+
"isConfirmed": true,
|
|
67
|
+
"comment": "Primary work email",
|
|
68
|
+
"usageTypeId": "H"
|
|
69
|
+
}
|
|
70
|
+
]
|
|
71
|
+
\`\`\``,
|
|
72
|
+
optional: true,
|
|
73
|
+
},
|
|
74
|
+
phoneNumbers: {
|
|
75
|
+
type: "string[]",
|
|
76
|
+
label: "Phone Numbers",
|
|
77
|
+
description: `An array of phone objects.
|
|
78
|
+
|
|
79
|
+
**Example:**
|
|
80
|
+
\`\`\`json
|
|
81
|
+
[
|
|
82
|
+
{
|
|
83
|
+
"phoneNumber": "+14155552671",
|
|
84
|
+
"phoneExtension": "101",
|
|
85
|
+
"isActive": true,
|
|
86
|
+
"isConfirmed": false,
|
|
87
|
+
"comment": "Direct work line",
|
|
88
|
+
"usageTypeId": "C"
|
|
89
|
+
}
|
|
90
|
+
]
|
|
91
|
+
\`\`\``,
|
|
92
|
+
optional: true,
|
|
93
|
+
},
|
|
94
|
+
addresses: {
|
|
95
|
+
type: "string[]",
|
|
96
|
+
label: "Addresses",
|
|
97
|
+
description: `Array of address objects.
|
|
98
|
+
|
|
99
|
+
**Example:**
|
|
100
|
+
\`\`\`json
|
|
101
|
+
[
|
|
102
|
+
{
|
|
103
|
+
"contactId": 12345,
|
|
104
|
+
"startDate": "2026-02-13",
|
|
105
|
+
"endDate": "2026-02-13",
|
|
106
|
+
"isActive": true,
|
|
107
|
+
"isConfirmed": true,
|
|
108
|
+
"usageTypeId": "H",
|
|
109
|
+
"ownershipTypeId": "O",
|
|
110
|
+
"streetAddress": "123 Main St",
|
|
111
|
+
"neighborhood": "Main St",
|
|
112
|
+
"city": "New York",
|
|
113
|
+
"stateId": "NY",
|
|
114
|
+
"stateName": "New York",
|
|
115
|
+
"zip": "10001",
|
|
116
|
+
"countryId": "US",
|
|
117
|
+
"countryName": "United States",
|
|
118
|
+
"comment": "Home address"
|
|
119
|
+
}
|
|
120
|
+
]
|
|
121
|
+
\`\`\``,
|
|
122
|
+
optional: true,
|
|
123
|
+
},
|
|
124
|
+
note: {
|
|
125
|
+
type: "string",
|
|
126
|
+
label: "Note",
|
|
127
|
+
description: "Additional notes about the contact",
|
|
128
|
+
optional: true,
|
|
129
|
+
},
|
|
130
|
+
},
|
|
131
|
+
async run({ $ }) {
|
|
132
|
+
const {
|
|
133
|
+
app,
|
|
134
|
+
contactId,
|
|
135
|
+
contactGroupId,
|
|
136
|
+
firstName,
|
|
137
|
+
lastName,
|
|
138
|
+
companyName,
|
|
139
|
+
emailAddresses,
|
|
140
|
+
phoneNumbers,
|
|
141
|
+
addresses,
|
|
142
|
+
note,
|
|
143
|
+
} = this;
|
|
144
|
+
|
|
145
|
+
const response = await app.createOrUpdateContact({
|
|
146
|
+
$,
|
|
147
|
+
data: {
|
|
148
|
+
contactId,
|
|
149
|
+
contactGroupId,
|
|
150
|
+
firstName,
|
|
151
|
+
lastName,
|
|
152
|
+
companyName,
|
|
153
|
+
emailAddresses: parseArray(emailAddresses),
|
|
154
|
+
phoneNumbers: parseArray(phoneNumbers),
|
|
155
|
+
addresses: parseArray(addresses),
|
|
156
|
+
note,
|
|
157
|
+
},
|
|
158
|
+
});
|
|
159
|
+
|
|
160
|
+
$.export("$summary", `Successfully created/updated contact with ID \`${response.result?.contactId ?? "unknown"}\``);
|
|
161
|
+
|
|
162
|
+
return response;
|
|
163
|
+
},
|
|
164
|
+
};
|
|
@@ -0,0 +1,141 @@
|
|
|
1
|
+
import app from "../../sperse.app.mjs";
|
|
2
|
+
import { parseArray } from "../../common/utils.mjs";
|
|
3
|
+
import constants from "../../common/constants.mjs";
|
|
4
|
+
|
|
5
|
+
export default {
|
|
6
|
+
key: "sperse-create-invoice",
|
|
7
|
+
name: "Create Invoice",
|
|
8
|
+
description: "Creates a new invoice in Sperse. [See the documentation](https://app.sperse.com/app/api/swagger)",
|
|
9
|
+
version: "0.0.1",
|
|
10
|
+
type: "action",
|
|
11
|
+
annotations: {
|
|
12
|
+
readOnlyHint: false,
|
|
13
|
+
destructiveHint: false,
|
|
14
|
+
openWorldHint: true,
|
|
15
|
+
},
|
|
16
|
+
props: {
|
|
17
|
+
app,
|
|
18
|
+
contactId: {
|
|
19
|
+
propDefinition: [
|
|
20
|
+
app,
|
|
21
|
+
"contactId",
|
|
22
|
+
],
|
|
23
|
+
},
|
|
24
|
+
status: {
|
|
25
|
+
type: "string",
|
|
26
|
+
label: "Status",
|
|
27
|
+
description: "The status of the invoice",
|
|
28
|
+
options: constants.INVOICE_STATUS,
|
|
29
|
+
},
|
|
30
|
+
date: {
|
|
31
|
+
type: "string",
|
|
32
|
+
label: "Invoice Date",
|
|
33
|
+
description: "The date of the invoice in ISO 8601 format (e.g., `2026-02-16`)",
|
|
34
|
+
},
|
|
35
|
+
currencyId: {
|
|
36
|
+
propDefinition: [
|
|
37
|
+
app,
|
|
38
|
+
"currencyId",
|
|
39
|
+
],
|
|
40
|
+
},
|
|
41
|
+
grandTotal: {
|
|
42
|
+
type: "string",
|
|
43
|
+
label: "Grand Total",
|
|
44
|
+
description: "The grand total amount",
|
|
45
|
+
},
|
|
46
|
+
lines: {
|
|
47
|
+
type: "string[]",
|
|
48
|
+
label: "Invoice Lines",
|
|
49
|
+
description: `An array of invoice line items. Either priceOptionId or unitId should be specified, productCode or description should be specified. [See the documentation](https://app.sperse.com/app/api/swagger)
|
|
50
|
+
|
|
51
|
+
**Example:**
|
|
52
|
+
\`\`\`json
|
|
53
|
+
[
|
|
54
|
+
{
|
|
55
|
+
"productCode": "123",
|
|
56
|
+
"unitId": "Day",
|
|
57
|
+
"quantity": 1,
|
|
58
|
+
"rate": 100,
|
|
59
|
+
"total": 100,
|
|
60
|
+
"sortOrder": 1
|
|
61
|
+
}
|
|
62
|
+
]
|
|
63
|
+
\`\`\``,
|
|
64
|
+
},
|
|
65
|
+
dueDate: {
|
|
66
|
+
type: "string",
|
|
67
|
+
label: "Due Date",
|
|
68
|
+
description: "The due date of the invoice in ISO 8601 format (e.g., `2026-10-15`)",
|
|
69
|
+
optional: true,
|
|
70
|
+
},
|
|
71
|
+
description: {
|
|
72
|
+
type: "string",
|
|
73
|
+
label: "Description",
|
|
74
|
+
description: "The description of the invoice",
|
|
75
|
+
optional: true,
|
|
76
|
+
},
|
|
77
|
+
note: {
|
|
78
|
+
type: "string",
|
|
79
|
+
label: "Note",
|
|
80
|
+
description: "Additional notes for the invoice",
|
|
81
|
+
optional: true,
|
|
82
|
+
},
|
|
83
|
+
taxTotal: {
|
|
84
|
+
type: "string",
|
|
85
|
+
label: "Tax Total",
|
|
86
|
+
description: "The total tax amount",
|
|
87
|
+
optional: true,
|
|
88
|
+
},
|
|
89
|
+
discountTotal: {
|
|
90
|
+
type: "string",
|
|
91
|
+
label: "Discount Total",
|
|
92
|
+
description: "The total discount amount",
|
|
93
|
+
optional: true,
|
|
94
|
+
},
|
|
95
|
+
shippingTotal: {
|
|
96
|
+
type: "string",
|
|
97
|
+
label: "Shipping Total",
|
|
98
|
+
description: "The total shipping cost",
|
|
99
|
+
optional: true,
|
|
100
|
+
},
|
|
101
|
+
},
|
|
102
|
+
async run({ $ }) {
|
|
103
|
+
const {
|
|
104
|
+
app,
|
|
105
|
+
contactId,
|
|
106
|
+
currencyId,
|
|
107
|
+
grandTotal,
|
|
108
|
+
lines,
|
|
109
|
+
date,
|
|
110
|
+
dueDate,
|
|
111
|
+
description,
|
|
112
|
+
note,
|
|
113
|
+
status,
|
|
114
|
+
taxTotal,
|
|
115
|
+
discountTotal,
|
|
116
|
+
shippingTotal,
|
|
117
|
+
} = this;
|
|
118
|
+
|
|
119
|
+
const response = await app.createInvoice({
|
|
120
|
+
$,
|
|
121
|
+
data: {
|
|
122
|
+
contactId,
|
|
123
|
+
currencyId,
|
|
124
|
+
grandTotal,
|
|
125
|
+
lines: parseArray(lines),
|
|
126
|
+
date,
|
|
127
|
+
dueDate,
|
|
128
|
+
description,
|
|
129
|
+
note,
|
|
130
|
+
status,
|
|
131
|
+
taxTotal,
|
|
132
|
+
discountTotal,
|
|
133
|
+
shippingTotal,
|
|
134
|
+
},
|
|
135
|
+
});
|
|
136
|
+
|
|
137
|
+
$.export("$summary", "Successfully created invoice.");
|
|
138
|
+
|
|
139
|
+
return response;
|
|
140
|
+
},
|
|
141
|
+
};
|
|
@@ -0,0 +1,141 @@
|
|
|
1
|
+
import app from "../../sperse.app.mjs";
|
|
2
|
+
import { parseArray } from "../../common/utils.mjs";
|
|
3
|
+
import constants from "../../common/constants.mjs";
|
|
4
|
+
|
|
5
|
+
export default {
|
|
6
|
+
key: "sperse-create-product",
|
|
7
|
+
name: "Create Product",
|
|
8
|
+
description: "Creates a new product. [See the documentation](https://app.sperse.com/app/api/swagger)",
|
|
9
|
+
version: "0.0.1",
|
|
10
|
+
type: "action",
|
|
11
|
+
annotations: {
|
|
12
|
+
readOnlyHint: false,
|
|
13
|
+
destructiveHint: false,
|
|
14
|
+
openWorldHint: true,
|
|
15
|
+
},
|
|
16
|
+
props: {
|
|
17
|
+
app,
|
|
18
|
+
// eslint-disable-next-line pipedream/props-label, pipedream/props-description
|
|
19
|
+
info: {
|
|
20
|
+
type: "alert",
|
|
21
|
+
alertType: "info",
|
|
22
|
+
content: "At least one **Price Option** should be specified for the product",
|
|
23
|
+
},
|
|
24
|
+
name: {
|
|
25
|
+
type: "string",
|
|
26
|
+
label: "Name",
|
|
27
|
+
description: "The name of the product",
|
|
28
|
+
},
|
|
29
|
+
code: {
|
|
30
|
+
type: "string",
|
|
31
|
+
label: "Product Code",
|
|
32
|
+
description: "The product code",
|
|
33
|
+
},
|
|
34
|
+
productType: {
|
|
35
|
+
type: "string",
|
|
36
|
+
label: "Product Type",
|
|
37
|
+
description: "The type of the product",
|
|
38
|
+
options: constants.PRODUCT_TYPES,
|
|
39
|
+
},
|
|
40
|
+
currencyId: {
|
|
41
|
+
propDefinition: [
|
|
42
|
+
app,
|
|
43
|
+
"currencyId",
|
|
44
|
+
],
|
|
45
|
+
},
|
|
46
|
+
priceOptions: {
|
|
47
|
+
type: "string[]",
|
|
48
|
+
label: "Price Options",
|
|
49
|
+
description: `An array of price option objects. [See the documentation](https://app.sperse.com/app/api/swagger)
|
|
50
|
+
|
|
51
|
+
**Example:**
|
|
52
|
+
\`\`\`json
|
|
53
|
+
[
|
|
54
|
+
{
|
|
55
|
+
"type": "Subscription",
|
|
56
|
+
"frequency": "OneTime",
|
|
57
|
+
"fee": 0,
|
|
58
|
+
"customerChoosesPrice": false,
|
|
59
|
+
"credits": 100,
|
|
60
|
+
"trialDayCount": 0,
|
|
61
|
+
"customPeriodCount": 14,
|
|
62
|
+
"customPeriodType": "Days"
|
|
63
|
+
}
|
|
64
|
+
]
|
|
65
|
+
\`\`\``,
|
|
66
|
+
},
|
|
67
|
+
description: {
|
|
68
|
+
type: "string",
|
|
69
|
+
label: "Description",
|
|
70
|
+
description: "The description of the product",
|
|
71
|
+
optional: true,
|
|
72
|
+
},
|
|
73
|
+
descriptionHtml: {
|
|
74
|
+
type: "string",
|
|
75
|
+
label: "Description HTML",
|
|
76
|
+
description: "The HTML description of the product",
|
|
77
|
+
optional: true,
|
|
78
|
+
},
|
|
79
|
+
barCode: {
|
|
80
|
+
type: "string",
|
|
81
|
+
label: "Bar Code",
|
|
82
|
+
description: "The product barcode",
|
|
83
|
+
optional: true,
|
|
84
|
+
},
|
|
85
|
+
isPublished: {
|
|
86
|
+
type: "boolean",
|
|
87
|
+
label: "Is Published",
|
|
88
|
+
description: "Whether the product is published",
|
|
89
|
+
optional: true,
|
|
90
|
+
},
|
|
91
|
+
publishDate: {
|
|
92
|
+
type: "string",
|
|
93
|
+
label: "Publish Date",
|
|
94
|
+
description: "The publish date is required if the product is published. Example: `2026-02-16`",
|
|
95
|
+
optional: true,
|
|
96
|
+
},
|
|
97
|
+
groupName: {
|
|
98
|
+
type: "string",
|
|
99
|
+
label: "Group Name",
|
|
100
|
+
description: "The product group name",
|
|
101
|
+
optional: true,
|
|
102
|
+
},
|
|
103
|
+
},
|
|
104
|
+
async run({ $ }) {
|
|
105
|
+
const {
|
|
106
|
+
app,
|
|
107
|
+
name,
|
|
108
|
+
code,
|
|
109
|
+
productType,
|
|
110
|
+
currencyId,
|
|
111
|
+
description,
|
|
112
|
+
descriptionHtml,
|
|
113
|
+
priceOptions,
|
|
114
|
+
groupName,
|
|
115
|
+
barCode,
|
|
116
|
+
isPublished,
|
|
117
|
+
publishDate,
|
|
118
|
+
} = this;
|
|
119
|
+
|
|
120
|
+
const response = await app.createProduct({
|
|
121
|
+
$,
|
|
122
|
+
data: {
|
|
123
|
+
name,
|
|
124
|
+
code,
|
|
125
|
+
type: productType,
|
|
126
|
+
currencyId,
|
|
127
|
+
description,
|
|
128
|
+
descriptionHtml,
|
|
129
|
+
barCode,
|
|
130
|
+
isPublished,
|
|
131
|
+
priceOptions: parseArray(priceOptions),
|
|
132
|
+
groupName,
|
|
133
|
+
publishDate,
|
|
134
|
+
},
|
|
135
|
+
});
|
|
136
|
+
|
|
137
|
+
$.export("$summary", `Successfully created product with ID \`${response.result.productId}\`.`);
|
|
138
|
+
|
|
139
|
+
return response;
|
|
140
|
+
},
|
|
141
|
+
};
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
import app from "../../sperse.app.mjs";
|
|
2
|
+
|
|
3
|
+
export default {
|
|
4
|
+
key: "sperse-get-contact-details",
|
|
5
|
+
name: "Get Contact Details",
|
|
6
|
+
description: "Retrieves detailed information about a contact. [See the documentation](https://app.sperse.com/app/api/swagger)",
|
|
7
|
+
version: "0.0.1",
|
|
8
|
+
type: "action",
|
|
9
|
+
annotations: {
|
|
10
|
+
readOnlyHint: true,
|
|
11
|
+
destructiveHint: false,
|
|
12
|
+
openWorldHint: true,
|
|
13
|
+
},
|
|
14
|
+
props: {
|
|
15
|
+
app,
|
|
16
|
+
contactId: {
|
|
17
|
+
propDefinition: [
|
|
18
|
+
app,
|
|
19
|
+
"contactId",
|
|
20
|
+
],
|
|
21
|
+
},
|
|
22
|
+
},
|
|
23
|
+
async run({ $ }) {
|
|
24
|
+
const {
|
|
25
|
+
app,
|
|
26
|
+
contactId,
|
|
27
|
+
} = this;
|
|
28
|
+
|
|
29
|
+
const response = await app.getContactDetails({
|
|
30
|
+
$,
|
|
31
|
+
params: {
|
|
32
|
+
contactId,
|
|
33
|
+
},
|
|
34
|
+
});
|
|
35
|
+
|
|
36
|
+
$.export("$summary", "Successfully retrieved contact details");
|
|
37
|
+
|
|
38
|
+
return response;
|
|
39
|
+
},
|
|
40
|
+
};
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import sperse from "../../sperse.app.mjs";
|
|
2
|
+
|
|
3
|
+
export default {
|
|
4
|
+
key: "sperse-list-contact-group-id-options",
|
|
5
|
+
name: "List Contact Group ID Options",
|
|
6
|
+
description: "Retrieves available options for the Contact Group 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
|
+
sperse,
|
|
16
|
+
},
|
|
17
|
+
async run({ $ }) {
|
|
18
|
+
const options = await sperse.propDefinitions.contactGroupId.options.call(this.sperse);
|
|
19
|
+
$.export("$summary", `Successfully retrieved ${options.length} option${options.length === 1
|
|
20
|
+
? ""
|
|
21
|
+
: "s"}`);
|
|
22
|
+
return options;
|
|
23
|
+
},
|
|
24
|
+
};
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import sperse from "../../sperse.app.mjs";
|
|
2
|
+
|
|
3
|
+
export default {
|
|
4
|
+
key: "sperse-list-currency-id-options",
|
|
5
|
+
name: "List Currency ID Options",
|
|
6
|
+
description: "Retrieves available options for the Currency 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
|
+
sperse,
|
|
16
|
+
},
|
|
17
|
+
async run({ $ }) {
|
|
18
|
+
const options = await sperse.propDefinitions.currencyId.options.call(this.sperse);
|
|
19
|
+
$.export("$summary", `Successfully retrieved ${options.length} option${options.length === 1
|
|
20
|
+
? ""
|
|
21
|
+
: "s"}`);
|
|
22
|
+
return options;
|
|
23
|
+
},
|
|
24
|
+
};
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
const DEFAULT_MAX = 600;
|
|
2
|
+
const DEFAULT_LIMIT = 100;
|
|
3
|
+
|
|
4
|
+
const INVOICE_STATUS = [
|
|
5
|
+
"Draft",
|
|
6
|
+
"Final",
|
|
7
|
+
"Sent",
|
|
8
|
+
"Paid",
|
|
9
|
+
"Canceled",
|
|
10
|
+
"PartiallyPaid",
|
|
11
|
+
"Refunded",
|
|
12
|
+
"PartiallyRefunded",
|
|
13
|
+
];
|
|
14
|
+
|
|
15
|
+
const PRODUCT_TYPES = [
|
|
16
|
+
"General",
|
|
17
|
+
"Subscription",
|
|
18
|
+
"Digital",
|
|
19
|
+
"Event",
|
|
20
|
+
"Donation",
|
|
21
|
+
];
|
|
22
|
+
|
|
23
|
+
export default {
|
|
24
|
+
DEFAULT_MAX,
|
|
25
|
+
DEFAULT_LIMIT,
|
|
26
|
+
INVOICE_STATUS,
|
|
27
|
+
PRODUCT_TYPES,
|
|
28
|
+
};
|
package/common/utils.mjs
ADDED
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
export const parseObject = (obj) => {
|
|
2
|
+
if (!obj) return undefined;
|
|
3
|
+
if (typeof obj === "string") {
|
|
4
|
+
return JSON.parse(obj);
|
|
5
|
+
}
|
|
6
|
+
return obj;
|
|
7
|
+
};
|
|
8
|
+
|
|
9
|
+
export const parseArray = (arr) => {
|
|
10
|
+
if (!arr) return undefined;
|
|
11
|
+
|
|
12
|
+
// If it's a string, parse it as JSON
|
|
13
|
+
if (typeof arr === "string") {
|
|
14
|
+
return JSON.parse(arr);
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
// If it's already an array
|
|
18
|
+
if (Array.isArray(arr)) {
|
|
19
|
+
// Check if first element is a string (needs parsing) or already an object
|
|
20
|
+
if (arr.length === 0) return arr;
|
|
21
|
+
|
|
22
|
+
// If first element is an object, assume the whole array is already parsed
|
|
23
|
+
if (typeof arr[0] === "object" && arr[0] !== null) {
|
|
24
|
+
return arr;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
// If elements are strings, parse each one
|
|
28
|
+
return arr.map((item) => {
|
|
29
|
+
if (typeof item === "string") {
|
|
30
|
+
return JSON.parse(item);
|
|
31
|
+
}
|
|
32
|
+
return item;
|
|
33
|
+
});
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
return arr;
|
|
37
|
+
};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@pipedream/sperse",
|
|
3
|
-
"version": "0.0
|
|
3
|
+
"version": "0.2.0",
|
|
4
4
|
"description": "Pipedream Sperse Components",
|
|
5
5
|
"main": "sperse.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": "^3.2.5"
|
|
14
17
|
}
|
|
15
18
|
}
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
import app from "../../sperse.app.mjs";
|
|
2
|
+
import {
|
|
3
|
+
DEFAULT_POLLING_SOURCE_TIMER_INTERVAL,
|
|
4
|
+
ConfigurationError,
|
|
5
|
+
} from "@pipedream/platform";
|
|
6
|
+
|
|
7
|
+
export default {
|
|
8
|
+
props: {
|
|
9
|
+
app,
|
|
10
|
+
db: "$.service.db",
|
|
11
|
+
timer: {
|
|
12
|
+
type: "$.interface.timer",
|
|
13
|
+
default: {
|
|
14
|
+
intervalSeconds: DEFAULT_POLLING_SOURCE_TIMER_INTERVAL,
|
|
15
|
+
},
|
|
16
|
+
},
|
|
17
|
+
},
|
|
18
|
+
methods: {
|
|
19
|
+
_getLastId() {
|
|
20
|
+
return this.db.get("lastId") || 0;
|
|
21
|
+
},
|
|
22
|
+
_setLastId(lastId) {
|
|
23
|
+
this.db.set("lastId", lastId);
|
|
24
|
+
},
|
|
25
|
+
generateMeta() {
|
|
26
|
+
throw new ConfigurationError("generateMeta is not implemented");
|
|
27
|
+
},
|
|
28
|
+
getResourceFn() {
|
|
29
|
+
throw new ConfigurationError("getResourceFn is not implemented");
|
|
30
|
+
},
|
|
31
|
+
getResourceFnArgs() {
|
|
32
|
+
return;
|
|
33
|
+
},
|
|
34
|
+
},
|
|
35
|
+
async run() {
|
|
36
|
+
const {
|
|
37
|
+
_getLastId,
|
|
38
|
+
_setLastId,
|
|
39
|
+
generateMeta,
|
|
40
|
+
getResourceFn,
|
|
41
|
+
getResourceFnArgs,
|
|
42
|
+
} = this;
|
|
43
|
+
|
|
44
|
+
let lastId = _getLastId();
|
|
45
|
+
let maxId = lastId;
|
|
46
|
+
|
|
47
|
+
const resourcesFn = getResourceFn();
|
|
48
|
+
const { result } = await resourcesFn(getResourceFnArgs());
|
|
49
|
+
|
|
50
|
+
const resources = result || [];
|
|
51
|
+
|
|
52
|
+
for (const resource of resources.reverse()) {
|
|
53
|
+
if (resource.id <= lastId) {
|
|
54
|
+
continue;
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
this.$emit(resource, generateMeta(resource));
|
|
58
|
+
if (resource.id > maxId) {
|
|
59
|
+
maxId = resource.id;
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
if (maxId > lastId) {
|
|
64
|
+
_setLastId(maxId);
|
|
65
|
+
}
|
|
66
|
+
},
|
|
67
|
+
};
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import common from "../common/polling.mjs";
|
|
2
|
+
import constants from "../../common/constants.mjs";
|
|
3
|
+
|
|
4
|
+
export default {
|
|
5
|
+
...common,
|
|
6
|
+
key: "sperse-new-lead",
|
|
7
|
+
name: "New Lead",
|
|
8
|
+
description: "Emit new event when a new lead is created. [See the documentation](https://app.sperse.com/app/api/swagger)",
|
|
9
|
+
version: "0.0.1",
|
|
10
|
+
type: "source",
|
|
11
|
+
dedupe: "unique",
|
|
12
|
+
methods: {
|
|
13
|
+
...common.methods,
|
|
14
|
+
getResourceFn() {
|
|
15
|
+
return this.app.getLeads;
|
|
16
|
+
},
|
|
17
|
+
getResourceFnArgs() {
|
|
18
|
+
return {
|
|
19
|
+
params: {
|
|
20
|
+
TopCount: constants.DEFAULT_MAX,
|
|
21
|
+
},
|
|
22
|
+
};
|
|
23
|
+
},
|
|
24
|
+
generateMeta(lead) {
|
|
25
|
+
return {
|
|
26
|
+
id: lead.id,
|
|
27
|
+
summary: `New Lead: ${lead.name}`,
|
|
28
|
+
ts: Date.now(),
|
|
29
|
+
};
|
|
30
|
+
},
|
|
31
|
+
},
|
|
32
|
+
};
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
import app from "../../sperse.app.mjs";
|
|
2
|
+
import common from "../common/polling.mjs";
|
|
3
|
+
|
|
4
|
+
export default {
|
|
5
|
+
...common,
|
|
6
|
+
key: "sperse-new-subscription",
|
|
7
|
+
name: "New Subscription",
|
|
8
|
+
description: "Emit new event when a new subscription is created. [See the documentation](https://app.sperse.com/app/api/swagger)",
|
|
9
|
+
version: "0.0.1",
|
|
10
|
+
type: "source",
|
|
11
|
+
dedupe: "unique",
|
|
12
|
+
props: {
|
|
13
|
+
...common.props,
|
|
14
|
+
contactId: {
|
|
15
|
+
propDefinition: [
|
|
16
|
+
app,
|
|
17
|
+
"contactId",
|
|
18
|
+
],
|
|
19
|
+
},
|
|
20
|
+
},
|
|
21
|
+
methods: {
|
|
22
|
+
...common.methods,
|
|
23
|
+
getResourceFn() {
|
|
24
|
+
return this.app.getSubscriptionHistory;
|
|
25
|
+
},
|
|
26
|
+
getResourceFnArgs() {
|
|
27
|
+
return {
|
|
28
|
+
params: {
|
|
29
|
+
contactId: this.contactId,
|
|
30
|
+
},
|
|
31
|
+
};
|
|
32
|
+
},
|
|
33
|
+
generateMeta(subscription) {
|
|
34
|
+
return {
|
|
35
|
+
id: subscription.id,
|
|
36
|
+
summary: `New Subscription: ${subscription.productName || subscription.productCode}`,
|
|
37
|
+
ts: Date.now(),
|
|
38
|
+
};
|
|
39
|
+
},
|
|
40
|
+
},
|
|
41
|
+
};
|
package/sperse.app.mjs
CHANGED
|
@@ -1,11 +1,173 @@
|
|
|
1
|
+
import { axios } from "@pipedream/platform";
|
|
2
|
+
import constants from "./common/constants.mjs";
|
|
3
|
+
|
|
1
4
|
export default {
|
|
2
5
|
type: "app",
|
|
3
6
|
app: "sperse",
|
|
4
|
-
propDefinitions: {
|
|
7
|
+
propDefinitions: {
|
|
8
|
+
contactId: {
|
|
9
|
+
type: "string",
|
|
10
|
+
label: "Contact ID",
|
|
11
|
+
description: "The ID of the contact",
|
|
12
|
+
async options() {
|
|
13
|
+
const { result } = await this.getContacts({
|
|
14
|
+
params: {
|
|
15
|
+
TopCount: constants.DEFAULT_LIMIT,
|
|
16
|
+
},
|
|
17
|
+
});
|
|
18
|
+
return result?.map(({
|
|
19
|
+
id: value, name, email,
|
|
20
|
+
}) => ({
|
|
21
|
+
label: `${name} ${email
|
|
22
|
+
? `(${email})`
|
|
23
|
+
: ""}`.trim(),
|
|
24
|
+
value,
|
|
25
|
+
})) || [];
|
|
26
|
+
},
|
|
27
|
+
},
|
|
28
|
+
contactGroupId: {
|
|
29
|
+
type: "string",
|
|
30
|
+
label: "Contact Group ID",
|
|
31
|
+
description: "The ID of the contact group",
|
|
32
|
+
async options() {
|
|
33
|
+
const { result } = await this.getContactGroups();
|
|
34
|
+
return result?.map(({
|
|
35
|
+
id: value, name: label,
|
|
36
|
+
}) => ({
|
|
37
|
+
label,
|
|
38
|
+
value,
|
|
39
|
+
})) || [];
|
|
40
|
+
},
|
|
41
|
+
},
|
|
42
|
+
currencyId: {
|
|
43
|
+
type: "string",
|
|
44
|
+
label: "Currency ID",
|
|
45
|
+
description: "The ID of the currency",
|
|
46
|
+
async options() {
|
|
47
|
+
const { result } = await this.getCurrencies();
|
|
48
|
+
return result?.map(({
|
|
49
|
+
id: value, name: label,
|
|
50
|
+
}) => ({
|
|
51
|
+
label,
|
|
52
|
+
value,
|
|
53
|
+
})) || [];
|
|
54
|
+
},
|
|
55
|
+
},
|
|
56
|
+
productId: {
|
|
57
|
+
type: "integer",
|
|
58
|
+
label: "Product ID",
|
|
59
|
+
description: "The ID of the product",
|
|
60
|
+
async options({ page }) {
|
|
61
|
+
const { result } = await this.getProducts({
|
|
62
|
+
params: {
|
|
63
|
+
phrase: "",
|
|
64
|
+
MaxResultCount: 20,
|
|
65
|
+
SkipCount: page * 20,
|
|
66
|
+
},
|
|
67
|
+
});
|
|
68
|
+
return result?.items?.map(({
|
|
69
|
+
id: value, name: label,
|
|
70
|
+
}) => ({
|
|
71
|
+
label,
|
|
72
|
+
value,
|
|
73
|
+
})) || [];
|
|
74
|
+
},
|
|
75
|
+
},
|
|
76
|
+
},
|
|
5
77
|
methods: {
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
78
|
+
getUrl(path) {
|
|
79
|
+
return `https://app.sperse.com${path}`;
|
|
80
|
+
},
|
|
81
|
+
getHeaders(headers) {
|
|
82
|
+
return {
|
|
83
|
+
"api-key": this.$auth.api_key,
|
|
84
|
+
"accept": "application/json",
|
|
85
|
+
...headers,
|
|
86
|
+
};
|
|
87
|
+
},
|
|
88
|
+
async _makeRequest({
|
|
89
|
+
$ = this, path, headers, ...opts
|
|
90
|
+
}) {
|
|
91
|
+
try {
|
|
92
|
+
return await axios($, {
|
|
93
|
+
url: this.getUrl(path),
|
|
94
|
+
headers: this.getHeaders(headers),
|
|
95
|
+
...opts,
|
|
96
|
+
});
|
|
97
|
+
|
|
98
|
+
} catch (error) {
|
|
99
|
+
throw new Error(JSON.stringify(error.response.data, null, 2));
|
|
100
|
+
}
|
|
101
|
+
},
|
|
102
|
+
async createOrUpdateContact(opts = {}) {
|
|
103
|
+
return this._makeRequest({
|
|
104
|
+
method: "POST",
|
|
105
|
+
path: "/api/services/CRM/Contact/CreateOrUpdateContact",
|
|
106
|
+
...opts,
|
|
107
|
+
});
|
|
108
|
+
},
|
|
109
|
+
async getContactDetails(opts = {}) {
|
|
110
|
+
return this._makeRequest({
|
|
111
|
+
path: "/api/services/CRM/Contact/GetContactDetails",
|
|
112
|
+
...opts,
|
|
113
|
+
});
|
|
114
|
+
},
|
|
115
|
+
async getContacts(opts = {}) {
|
|
116
|
+
return this._makeRequest({
|
|
117
|
+
path: "/api/services/CRM/Contact/GetAllByPhrase",
|
|
118
|
+
...opts,
|
|
119
|
+
});
|
|
120
|
+
},
|
|
121
|
+
async getContactGroups(opts = {}) {
|
|
122
|
+
return this._makeRequest({
|
|
123
|
+
path: "/api/services/CRM/Contact/GetContactGroups",
|
|
124
|
+
...opts,
|
|
125
|
+
});
|
|
126
|
+
},
|
|
127
|
+
async createLead(opts = {}) {
|
|
128
|
+
return this._makeRequest({
|
|
129
|
+
method: "POST",
|
|
130
|
+
path: "/api/services/CRM/Lead/Create",
|
|
131
|
+
...opts,
|
|
132
|
+
});
|
|
133
|
+
},
|
|
134
|
+
async getLeads(opts = {}) {
|
|
135
|
+
return this._makeRequest({
|
|
136
|
+
path: "/api/services/CRM/Lead/GetAllByPhrase",
|
|
137
|
+
...opts,
|
|
138
|
+
});
|
|
139
|
+
},
|
|
140
|
+
async createInvoice(opts = {}) {
|
|
141
|
+
return this._makeRequest({
|
|
142
|
+
method: "POST",
|
|
143
|
+
path: "/api/services/CRM/Invoice/Create",
|
|
144
|
+
...opts,
|
|
145
|
+
});
|
|
146
|
+
},
|
|
147
|
+
async createProduct(opts = {}) {
|
|
148
|
+
return this._makeRequest({
|
|
149
|
+
method: "POST",
|
|
150
|
+
path: "/api/services/CRM/Product/CreateProduct",
|
|
151
|
+
...opts,
|
|
152
|
+
});
|
|
153
|
+
},
|
|
154
|
+
async getProducts(opts = {}) {
|
|
155
|
+
return this._makeRequest({
|
|
156
|
+
path: "/api/services/CRM/Product/GetProductsByPhrase",
|
|
157
|
+
...opts,
|
|
158
|
+
});
|
|
159
|
+
},
|
|
160
|
+
async getSubscriptionHistory(opts = {}) {
|
|
161
|
+
return this._makeRequest({
|
|
162
|
+
path: "/api/services/CRM/OrderSubscription/GetSubscriptionHistory",
|
|
163
|
+
...opts,
|
|
164
|
+
});
|
|
165
|
+
},
|
|
166
|
+
async getCurrencies(opts = {}) {
|
|
167
|
+
return this._makeRequest({
|
|
168
|
+
path: "/api/services/CRM/Currency/GetCurrencies",
|
|
169
|
+
...opts,
|
|
170
|
+
});
|
|
9
171
|
},
|
|
10
172
|
},
|
|
11
|
-
};
|
|
173
|
+
};
|