@pipedream/quaderno 0.0.1 → 0.0.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.
@@ -0,0 +1,163 @@
1
+ import app from "../../quaderno.app.mjs";
2
+ import constants from "../../common/constants.mjs";
3
+
4
+ const { SEP } = constants;
5
+
6
+ export default {
7
+ props: {
8
+ app,
9
+ firstName: {
10
+ description: "The customer's first name who will be billed.",
11
+ propDefinition: [
12
+ app,
13
+ "firstName",
14
+ ],
15
+ },
16
+ lastName: {
17
+ description: "The customer's last name who will be billed.",
18
+ propDefinition: [
19
+ app,
20
+ "lastName",
21
+ ],
22
+ },
23
+ dueDate: {
24
+ propDefinition: [
25
+ app,
26
+ "dueDate",
27
+ ],
28
+ },
29
+ currency: {
30
+ propDefinition: [
31
+ app,
32
+ "currency",
33
+ ],
34
+ },
35
+ recurringPeriod: {
36
+ propDefinition: [
37
+ app,
38
+ "recurringPeriod",
39
+ ],
40
+ },
41
+ recurringFrequency: {
42
+ propDefinition: [
43
+ app,
44
+ "recurringFrequency",
45
+ ],
46
+ },
47
+ country: {
48
+ propDefinition: [
49
+ app,
50
+ "country",
51
+ ],
52
+ },
53
+ postalCode: {
54
+ propDefinition: [
55
+ app,
56
+ "postalCode",
57
+ ],
58
+ },
59
+ region: {
60
+ propDefinition: [
61
+ app,
62
+ "region",
63
+ ],
64
+ },
65
+ streetLine1: {
66
+ propDefinition: [
67
+ app,
68
+ "streetLine1",
69
+ ],
70
+ },
71
+ subject: {
72
+ propDefinition: [
73
+ app,
74
+ "subject",
75
+ ],
76
+ },
77
+ howManyItems: {
78
+ propDefinition: [
79
+ app,
80
+ "howManyItems",
81
+ ],
82
+ },
83
+ },
84
+ additionalProps() {
85
+ return Array.from({
86
+ length: this.howManyItems,
87
+ }).reduce((props, _, idx) => {
88
+ const counter = idx + 1;
89
+ const item = `item${counter}`;
90
+ const label = `Item ${counter}:`;
91
+ const description = `${item}${SEP}description`;
92
+ const discountRate = `${item}${SEP}discountRate`;
93
+ const productCode = `${item}${SEP}productCode`;
94
+ const quantity = `${item}${SEP}quantity`;
95
+ const totalAmount = `${item}${SEP}totalAmount`;
96
+ const unitPrice = `${item}${SEP}unitPrice`;
97
+ return {
98
+ ...props,
99
+ [description]: {
100
+ type: "string",
101
+ label: `${label} Description`,
102
+ description: "The description of the item.",
103
+ optional: true,
104
+ },
105
+ [discountRate]: {
106
+ type: "string",
107
+ label: `${label} Discount Rate`,
108
+ description: "Discount percent out of 100, if applicable.",
109
+ optional: true,
110
+ },
111
+ [productCode]: {
112
+ type: "string",
113
+ label: `${label} Product Code`,
114
+ description: "The SKU of the Quaderno **Product** being invoiced. Use this attribute if you want to track your sales per product.",
115
+ optional: true,
116
+ },
117
+ [quantity]: {
118
+ type: "integer",
119
+ label: `${label} Quantity`,
120
+ description: "The quantity of the item.",
121
+ optional: true,
122
+ default: 1,
123
+ },
124
+ [totalAmount]: {
125
+ type: "string",
126
+ label: `${label} Total Amount`,
127
+ description: "The total amount to be charged after discounts and taxes. Required if **Unit Price** is not passed.",
128
+ optional: true,
129
+ },
130
+ [unitPrice]: {
131
+ type: "string",
132
+ label: `${label} Unit Price`,
133
+ description: "The unit price of the item before any discount or tax is applied. Required if **Total Amount** is not passed.",
134
+ optional: true,
135
+ },
136
+ };
137
+ }, {});
138
+ },
139
+ methods: {
140
+ getItems(length) {
141
+ return Array.from({
142
+ length,
143
+ }).map((_, idx) => {
144
+ const counter = idx + 1;
145
+ const item = `item${counter}`;
146
+ const description = this[`${item}${SEP}description`];
147
+ const discountRate = this[`${item}${SEP}discountRate`];
148
+ const productCode = this[`${item}${SEP}productCode`];
149
+ const quantity = this[`${item}${SEP}quantity`];
150
+ const totalAmount = this[`${item}${SEP}totalAmount`];
151
+ const unitPrice = this[`${item}${SEP}unitPrice`];
152
+ return {
153
+ description,
154
+ discount_rate: discountRate,
155
+ product_code: productCode,
156
+ quantity,
157
+ total_amount: totalAmount,
158
+ unit_price: unitPrice,
159
+ };
160
+ });
161
+ },
162
+ },
163
+ };
@@ -0,0 +1,143 @@
1
+ import constants from "../../common/constants.mjs";
2
+ import app from "../../quaderno.app.mjs";
3
+
4
+ export default {
5
+ key: "quaderno-create-contact",
6
+ name: "Create Contact",
7
+ description: "Add a new contact to Quaderno. [See the Documentation](https://developers.quaderno.io/api/#tag/Contacts/operation/createContact).",
8
+ type: "action",
9
+ version: "0.0.1",
10
+ props: {
11
+ app,
12
+ kind: {
13
+ type: "string",
14
+ label: "Kind",
15
+ description: "The type of contact. Can be `person` or `company`",
16
+ options: Object.values(constants.CONTACT_TYPE),
17
+ default: constants.CONTACT_TYPE.PERSON,
18
+ reloadProps: true,
19
+ },
20
+ country: {
21
+ propDefinition: [
22
+ app,
23
+ "country",
24
+ ],
25
+ },
26
+ city: {
27
+ propDefinition: [
28
+ app,
29
+ "city",
30
+ ],
31
+ },
32
+ region: {
33
+ propDefinition: [
34
+ app,
35
+ "region",
36
+ ],
37
+ },
38
+ streetLine1: {
39
+ propDefinition: [
40
+ app,
41
+ "streetLine1",
42
+ ],
43
+ },
44
+ email: {
45
+ propDefinition: [
46
+ app,
47
+ "email",
48
+ ],
49
+ },
50
+ phone1: {
51
+ propDefinition: [
52
+ app,
53
+ "phone1",
54
+ ],
55
+ },
56
+ postalCode: {
57
+ propDefinition: [
58
+ app,
59
+ "postalCode",
60
+ ],
61
+ },
62
+ fullName: {
63
+ propDefinition: [
64
+ app,
65
+ "fullName",
66
+ ],
67
+ },
68
+ },
69
+ async additionalProps() {
70
+ if (this.kind === constants.CONTACT_TYPE.PERSON) {
71
+ return {
72
+ firstName: {
73
+ type: "string",
74
+ label: "First Name",
75
+ description: "The contact's first name.",
76
+ },
77
+ lastName: {
78
+ type: "string",
79
+ label: "Last Name",
80
+ description: "The contact's last name.",
81
+ optional: true,
82
+ },
83
+ };
84
+ }
85
+ return {
86
+ firstName: {
87
+ type: "string",
88
+ label: "Bussiness Name",
89
+ description: "The contact's bussiness name.",
90
+ },
91
+ department: {
92
+ type: "string",
93
+ label: "Department",
94
+ description: "If the contact is a `company`, this is the deparment.",
95
+ optional: true,
96
+ },
97
+ contactPerson: {
98
+ type: "string",
99
+ label: "Contact Person",
100
+ description: "If the contact is a `company`, this is its contact person.",
101
+ optional: true,
102
+ },
103
+ };
104
+ },
105
+ methods: {
106
+ createContact(args = {}) {
107
+ return this.app.post({
108
+ path: "/contacts",
109
+ ...args,
110
+ });
111
+ },
112
+ },
113
+ async run({ $: step }) {
114
+ const {
115
+ kind, country, city, region, streetLine1, contactPerson,
116
+ department, email, phone1, postalCode, fullName, firstName,
117
+ lastName,
118
+ } = this;
119
+
120
+ const response = await this.createContact({
121
+ step,
122
+ data: {
123
+ kind,
124
+ country,
125
+ city,
126
+ region,
127
+ street_line_1: streetLine1,
128
+ contact_person: contactPerson,
129
+ department,
130
+ email,
131
+ phone_1: phone1,
132
+ postal_code: postalCode,
133
+ full_name: fullName,
134
+ first_name: firstName,
135
+ last_name: lastName,
136
+ },
137
+ });
138
+
139
+ step.export("$summary", `Successfully created contact with ID ${response.id}`);
140
+
141
+ return response;
142
+ },
143
+ };
@@ -0,0 +1,57 @@
1
+ import common from "../common/invoice.mjs";
2
+
3
+ export default {
4
+ ...common,
5
+ key: "quaderno-create-invoice",
6
+ name: "Create Invoice",
7
+ description: "Generate a new invoice in Quaderno. [See the Documentation](https://developers.quaderno.io/api/#tag/Invoices/operation/createInvoice).",
8
+ type: "action",
9
+ version: "0.0.1",
10
+ methods: {
11
+ ...common.methods,
12
+ createInvoice(args = {}) {
13
+ return this.app.post({
14
+ path: "/invoices",
15
+ ...args,
16
+ });
17
+ },
18
+ },
19
+ async run({ $: step }) {
20
+ const {
21
+ firstName,
22
+ lastName,
23
+ dueDate,
24
+ currency,
25
+ recurringPeriod,
26
+ recurringFrequency,
27
+ country,
28
+ postalCode,
29
+ region,
30
+ streetLine1,
31
+ howManyItems,
32
+ } = this;
33
+
34
+ const response = await this.createInvoice({
35
+ step,
36
+ data: {
37
+ contact: {
38
+ first_name: firstName,
39
+ last_name: lastName,
40
+ },
41
+ due_date: dueDate,
42
+ currency,
43
+ recurring_period: recurringPeriod,
44
+ recurring_frequency: recurringFrequency,
45
+ country,
46
+ postal_code: postalCode,
47
+ region,
48
+ street_line_1: streetLine1,
49
+ items_attributes: this.getItems(howManyItems),
50
+ },
51
+ });
52
+
53
+ step.export("$summary", `Successfully created invoice with ID ${response.id}`);
54
+
55
+ return response;
56
+ },
57
+ };
@@ -0,0 +1,78 @@
1
+ import common from "../common/invoice.mjs";
2
+
3
+ export default {
4
+ ...common,
5
+ key: "quaderno-update-invoice",
6
+ name: "Update Invoice",
7
+ description: "Modify an existing invoice's details in Quaderno. [See the Documentation](https://developers.quaderno.io/api/#tag/Invoices/operation/updateInvoice).",
8
+ type: "action",
9
+ version: "0.0.1",
10
+ props: {
11
+ ...common.props,
12
+ invoiceId: {
13
+ propDefinition: [
14
+ common.props.app,
15
+ "invoiceId",
16
+ ],
17
+ },
18
+ howManyItems: {
19
+ optional: true,
20
+ propDefinition: [
21
+ common.props.app,
22
+ "howManyItems",
23
+ ],
24
+ description: "The items here will be ADDED to the existing invoice items",
25
+ },
26
+ },
27
+ methods: {
28
+ ...common.methods,
29
+ updateInvoice({
30
+ invoiceId, ...args
31
+ } = {}) {
32
+ return this.app.put({
33
+ path: `/invoices/${invoiceId}`,
34
+ ...args,
35
+ });
36
+ },
37
+ },
38
+ async run({ $: step }) {
39
+ const {
40
+ invoiceId,
41
+ firstName,
42
+ lastName,
43
+ dueDate,
44
+ currency,
45
+ recurringPeriod,
46
+ recurringFrequency,
47
+ country,
48
+ postalCode,
49
+ region,
50
+ streetLine1,
51
+ howManyItems,
52
+ } = this;
53
+
54
+ const response = await this.updateInvoice({
55
+ step,
56
+ invoiceId,
57
+ data: {
58
+ contact: {
59
+ first_name: firstName,
60
+ last_name: lastName,
61
+ },
62
+ due_date: dueDate,
63
+ currency,
64
+ recurring_period: recurringPeriod,
65
+ recurring_frequency: recurringFrequency,
66
+ country,
67
+ postal_code: postalCode,
68
+ region,
69
+ street_line_1: streetLine1,
70
+ items_attributes: this.getItems(howManyItems),
71
+ },
72
+ });
73
+
74
+ step.export("$summary", `Successfully updated invoice with ID ${response.id}`);
75
+
76
+ return response;
77
+ },
78
+ };
@@ -0,0 +1,40 @@
1
+ const DOMAIN_PLACEHOLDER = "{domain}";
2
+ const ACCOUNT_PLACEHOLDER = "{account_name}";
3
+ const BASE_URL = `https://${ACCOUNT_PLACEHOLDER}.${DOMAIN_PLACEHOLDER}`;
4
+ const VERSION_PATH = "/api";
5
+ const LAST_CREATED_AT = "lastCreatedAt";
6
+ const DEFAULT_MAX = 600;
7
+
8
+ const API_VERSION = "20220325";
9
+
10
+ const CONTACT_TYPE = {
11
+ PERSON: "person",
12
+ COMPANY: "company",
13
+ };
14
+
15
+ const PERIOD = {
16
+ DAYS: "days",
17
+ WEEKS: "weeks",
18
+ MONTHS: "months",
19
+ YEARS: "years",
20
+ };
21
+
22
+ const SEP = "_";
23
+
24
+ const WEBHOOK_ID = "webhookId";
25
+ const AUTH_KEY = "authKey";
26
+
27
+ export default {
28
+ BASE_URL,
29
+ VERSION_PATH,
30
+ DEFAULT_MAX,
31
+ LAST_CREATED_AT,
32
+ API_VERSION,
33
+ DOMAIN_PLACEHOLDER,
34
+ ACCOUNT_PLACEHOLDER,
35
+ CONTACT_TYPE,
36
+ PERIOD,
37
+ SEP,
38
+ WEBHOOK_ID,
39
+ AUTH_KEY,
40
+ };
package/package.json CHANGED
@@ -1,18 +1,17 @@
1
1
  {
2
2
  "name": "@pipedream/quaderno",
3
- "version": "0.0.1",
3
+ "version": "0.0.3",
4
4
  "description": "Pipedream Quaderno Components",
5
- "main": "dist/app/quaderno.app.mjs",
5
+ "main": "quaderno.app.mjs",
6
6
  "keywords": [
7
7
  "pipedream",
8
8
  "quaderno"
9
9
  ],
10
- "files": [
11
- "dist"
12
- ],
13
10
  "homepage": "https://pipedream.com/apps/quaderno",
14
11
  "author": "Pipedream <support@pipedream.com> (https://pipedream.com/)",
15
- "license": "MIT",
12
+ "dependencies": {
13
+ "@pipedream/platform": "^1.4.1"
14
+ },
16
15
  "publishConfig": {
17
16
  "access": "public"
18
17
  }
@@ -0,0 +1,187 @@
1
+ import { axios } from "@pipedream/platform";
2
+ import constants from "./common/constants.mjs";
3
+
4
+ export default {
5
+ type: "app",
6
+ app: "quaderno",
7
+ propDefinitions: {
8
+ invoiceId: {
9
+ type: "string",
10
+ label: "Invoice ID",
11
+ description: "The ID of the invoice to retrieve.",
12
+ async options() {
13
+ const invoices = await this.listInvoices();
14
+ return invoices.map(({
15
+ id, contact,
16
+ }) => ({
17
+ value: String(id),
18
+ label: contact?.full_name
19
+ ? `${id} (${contact.full_name})`
20
+ : id,
21
+ }));
22
+ },
23
+ },
24
+ firstName: {
25
+ type: "string",
26
+ label: "First Name",
27
+ description: "The contact's first name.",
28
+ },
29
+ lastName: {
30
+ type: "string",
31
+ label: "Last Name",
32
+ description: "The contact's last name.",
33
+ optional: true,
34
+ },
35
+ country: {
36
+ type: "string",
37
+ label: "Country",
38
+ description: "2-letter [ISO country code](https://en.wikipedia.org/wiki/List_of_ISO_3166_country_codes).",
39
+ optional: true,
40
+ },
41
+ city: {
42
+ type: "string",
43
+ label: "City",
44
+ description: "City/District/Suburb/Town/Village.",
45
+ optional: true,
46
+ },
47
+ region: {
48
+ type: "string",
49
+ label: "Region",
50
+ description: "State/Province/Region.",
51
+ optional: true,
52
+ },
53
+ streetLine1: {
54
+ type: "string",
55
+ label: "Street Line 1",
56
+ description: "Address line 1 (Street address/PO Box).",
57
+ optional: true,
58
+ },
59
+ email: {
60
+ type: "string",
61
+ label: "Email",
62
+ description: "The contact's email address.",
63
+ optional: true,
64
+ },
65
+ phone1: {
66
+ type: "string",
67
+ label: "Phone Number",
68
+ description: "The contact's phone number.",
69
+ optional: true,
70
+ },
71
+ postalCode: {
72
+ type: "string",
73
+ label: "Postal Code",
74
+ description: "ZIP or postal code.",
75
+ optional: true,
76
+ },
77
+ fullName: {
78
+ type: "string",
79
+ label: "Full Name",
80
+ description: "The contact's full name.",
81
+ optional: true,
82
+ },
83
+ dueDate: {
84
+ type: "string",
85
+ label: "Due Date",
86
+ description: "The date on which payment for this invoice is due. Must be in `YYYY-MM-DD` format.",
87
+ optional: true,
88
+ },
89
+ currency: {
90
+ type: "string",
91
+ label: "Currency",
92
+ description: "Three-letter [ISO currency code](https://en.wikipedia.org/wiki/ISO_4217), in uppercase.",
93
+ optional: true,
94
+ },
95
+ recurringPeriod: {
96
+ type: "string",
97
+ label: "Recurring Period",
98
+ description: "The period of time between each invoice. Can be `days`, `weeks`, `months`, `years`.",
99
+ optional: true,
100
+ options: Object.values(constants.PERIOD),
101
+ },
102
+ recurringFrequency: {
103
+ type: "integer",
104
+ label: "Recurring Frequency",
105
+ description: "The number of periods between each invoice.",
106
+ optional: true,
107
+ },
108
+ subject: {
109
+ type: "string",
110
+ label: "Subject",
111
+ description: "The subject of the invoice.",
112
+ optional: true,
113
+ },
114
+ howManyItems: {
115
+ type: "integer",
116
+ label: "How Many Items",
117
+ description: "The number of line items to add to the invoice.",
118
+ reloadProps: true,
119
+ default: 1,
120
+ },
121
+ },
122
+ methods: {
123
+ getBaseUrl() {
124
+ const baseUrl = `${constants.BASE_URL}${constants.VERSION_PATH}`;
125
+ return baseUrl.replace(constants.ACCOUNT_PLACEHOLDER, this.$auth.account_name)
126
+ .replace(constants.DOMAIN_PLACEHOLDER, this.$auth.domain);
127
+ },
128
+ getUrl(path, url) {
129
+ return url || `${this.getBaseUrl()}${path}`;
130
+ },
131
+ getAuth() {
132
+ return {
133
+ username: this.$auth.api_key,
134
+ };
135
+ },
136
+ getHeaders(headers) {
137
+ return {
138
+ "Content-Type": "application/json",
139
+ "Accept": `application/json; api_version=${constants.API_VERSION}`,
140
+ ...headers,
141
+ };
142
+ },
143
+ makeRequest({
144
+ step = this, path, headers, url, ...args
145
+ } = {}) {
146
+
147
+ const config = {
148
+ auth: this.getAuth(),
149
+ headers: this.getHeaders(headers),
150
+ url: this.getUrl(path, url),
151
+ ...args,
152
+ };
153
+
154
+ return axios(step, config);
155
+ },
156
+ post(args = {}) {
157
+ return this.makeRequest({
158
+ method: "post",
159
+ ...args,
160
+ });
161
+ },
162
+ put(args = {}) {
163
+ return this.makeRequest({
164
+ method: "put",
165
+ ...args,
166
+ });
167
+ },
168
+ delete(args = {}) {
169
+ return this.makeRequest({
170
+ method: "delete",
171
+ ...args,
172
+ });
173
+ },
174
+ listInvoices(args = {}) {
175
+ return this.makeRequest({
176
+ path: "/invoices",
177
+ ...args,
178
+ });
179
+ },
180
+ listPayments(args = {}) {
181
+ return this.makeRequest({
182
+ path: "/payments",
183
+ ...args,
184
+ });
185
+ },
186
+ },
187
+ };
@@ -0,0 +1,14 @@
1
+ import { ConfigurationError } from "@pipedream/platform";
2
+ import app from "../../quaderno.app.mjs";
3
+
4
+ export default {
5
+ props: {
6
+ app,
7
+ db: "$.service.db",
8
+ },
9
+ methods: {
10
+ generateMeta() {
11
+ throw new ConfigurationError("generateMeta is not implemented");
12
+ },
13
+ },
14
+ };
@@ -0,0 +1,25 @@
1
+ // Docs: https://developers.quaderno.io/guides/webhooks/#events
2
+ export default {
3
+ ACCOUNT_UPDATED: "account.updated",
4
+ ACCOUNT_APPLICATION_DEAUTHORIZED: "account.application.deauthorized",
5
+ CHECKOUT_SUCCEEDED: "checkout.succeeded",
6
+ CHECKOUT_FAILED: "checkout.failed",
7
+ CHECKOUT_ABANDONED: "checkout.abandoned",
8
+ CONTACT_CREATED: "contact.created",
9
+ CONTACT_UPDATED: "contact.updated",
10
+ CONTACT_DELETED: "contact.deleted",
11
+ CREDIT_CREATED: "credit.created",
12
+ CREDIT_UPDATED: "credit.updated",
13
+ EXPENSE_CREATED: "expense.created",
14
+ EXPENSE_UPDATED: "expense.updated",
15
+ EXPENSE_DELETED: "expense.deleted",
16
+ INVOICE_CREATED: "invoice.created",
17
+ INVOICE_UPDATED: "invoice.updated",
18
+ PAYMENT_CREATED: "payment.created",
19
+ PAYMENT_DELETED: "payment.deleted",
20
+ REPORTING_REQUEST_SUCCEDED: "reporting.request.suceeded",
21
+ REPORTING_REQUEST_FAILED: "reporting.request.failed",
22
+ THRESHOLD_WARNING: "threshold.warning",
23
+ THRESHOLD_EXCEEDED: "threshold.exceeded",
24
+ THRESHOLD_EU_100K: "threshold.eu.100k",
25
+ };
@@ -0,0 +1,103 @@
1
+ import { createHmac } from "crypto";
2
+ import { ConfigurationError } from "@pipedream/platform";
3
+ import common from "./base.mjs";
4
+ import constants from "../../common/constants.mjs";
5
+
6
+ export default {
7
+ ...common,
8
+ props: {
9
+ ...common.props,
10
+ http: {
11
+ type: "$.interface.http",
12
+ customResponse: true,
13
+ },
14
+ },
15
+ hooks: {
16
+ async activate() {
17
+ const response =
18
+ await this.createWebhook({
19
+ data: {
20
+ url: this.http.endpoint,
21
+ events_types: this.getEventName(),
22
+ },
23
+ });
24
+
25
+ this.setWebhookId(response.id);
26
+ this.setAuthKey(response.auth_key);
27
+ },
28
+ async deactivate() {
29
+ const webhookId = this.getWebhookId();
30
+ if (webhookId) {
31
+ await this.deleteWebhook({
32
+ webhookId,
33
+ });
34
+ }
35
+ },
36
+ },
37
+ methods: {
38
+ ...common.methods,
39
+ setWebhookId(value) {
40
+ this.db.set(constants.WEBHOOK_ID, value);
41
+ },
42
+ getWebhookId() {
43
+ return this.db.get(constants.WEBHOOK_ID);
44
+ },
45
+ setAuthKey(value) {
46
+ this.db.set(constants.AUTH_KEY, value);
47
+ },
48
+ getAuthKey() {
49
+ return this.db.get(constants.AUTH_KEY);
50
+ },
51
+ getEventName() {
52
+ throw new ConfigurationError("getEventName is not implemented");
53
+ },
54
+ createWebhook(args = {}) {
55
+ return this.app.post({
56
+ path: "/webhooks",
57
+ ...args,
58
+ });
59
+ },
60
+ deleteWebhook({
61
+ webhookId, ...args
62
+ } = {}) {
63
+ return this.app.delete({
64
+ path: `/webhooks/${webhookId}`,
65
+ ...args,
66
+ });
67
+ },
68
+ isSignatureValid(signature, data, skip = true) {
69
+ // skip signature validation for now. Due to the following issue:
70
+ // https://github.com/quaderno/quaderno-api/issues/54
71
+ if (skip) {
72
+ return true;
73
+ }
74
+ const authKey = this.getAuthKey();
75
+ const computedSignature = createHmac("sha1", authKey)
76
+ .update(data)
77
+ .digest("base64");
78
+
79
+ return computedSignature === signature;
80
+ },
81
+ processEvent(event) {
82
+ this.$emit(event, this.generateMeta(event.data?.object || event));
83
+ },
84
+ },
85
+ async run({
86
+ method, url, body, headers, bodyRaw,
87
+ }) {
88
+ if (method === "HEAD") {
89
+ return this.http.respond({
90
+ status: 200,
91
+ });
92
+ }
93
+
94
+ const signature = headers["x-quaderno-signature"];
95
+ const data = `${url}${bodyRaw}`;
96
+
97
+ if (!this.isSignatureValid(signature, data)) {
98
+ throw new Error("Invalid signature");
99
+ }
100
+
101
+ this.processEvent(body);
102
+ },
103
+ };
@@ -0,0 +1,34 @@
1
+ import common from "../common/webhook.mjs";
2
+ import events from "../common/events.mjs";
3
+
4
+ export default {
5
+ ...common,
6
+ key: "quaderno-invoice-created",
7
+ name: "New Invoice Created",
8
+ description: "Emit new event when a new invoice is generated in Quaderno. [See the Documentation](https://developers.quaderno.io/api/#tag/Webhooks/operation/createWebhook).",
9
+ type: "source",
10
+ version: "0.0.1",
11
+ dedupe: "unique",
12
+ hooks: {
13
+ async deploy() {
14
+ const invoices = await this.app.listInvoices();
15
+ invoices.reverse().forEach(this.processEvent);
16
+ },
17
+ ...common.hooks,
18
+ },
19
+ methods: {
20
+ ...common.methods,
21
+ getEventName() {
22
+ return [
23
+ events.INVOICE_CREATED,
24
+ ];
25
+ },
26
+ generateMeta(resource) {
27
+ return {
28
+ id: resource.id,
29
+ summary: `New Invoice: ${resource.id}`,
30
+ ts: Date.now(),
31
+ };
32
+ },
33
+ },
34
+ };
@@ -0,0 +1,27 @@
1
+ import common from "../common/webhook.mjs";
2
+ import events from "../common/events.mjs";
3
+
4
+ export default {
5
+ ...common,
6
+ key: "quaderno-payment-received",
7
+ name: "New Payment Received",
8
+ description: "Emit new event when a payment is successfully processed in Quaderno. [See the Documentation](https://developers.quaderno.io/api/#tag/Webhooks/operation/createWebhook).",
9
+ type: "source",
10
+ version: "0.0.1",
11
+ dedupe: "unique",
12
+ methods: {
13
+ ...common.methods,
14
+ getEventName() {
15
+ return [
16
+ events.PAYMENT_CREATED,
17
+ ];
18
+ },
19
+ generateMeta(resource) {
20
+ return {
21
+ id: resource.id,
22
+ summary: `New Payment: ${resource.id}`,
23
+ ts: Date.now(),
24
+ };
25
+ },
26
+ },
27
+ };