diff --git a/components/gocanvas/actions/create-dispatch/create-dispatch.mjs b/components/gocanvas/actions/create-dispatch/create-dispatch.mjs
new file mode 100644
index 0000000000000..cc21e5d520f90
--- /dev/null
+++ b/components/gocanvas/actions/create-dispatch/create-dispatch.mjs
@@ -0,0 +1,49 @@
+import gocanvas from "../../gocanvas.app.mjs";
+
+export default {
+ key: "gocanvas-create-dispatch",
+ name: "Create Dispatch",
+ description: "Creates a dispatch item in GoCanvas. [See the documentation](https://help.gocanvas.com/hc/en-us/article_attachments/26468076609559)",
+ version: "0.0.1",
+ type: "action",
+ props: {
+ gocanvas,
+ form: {
+ propDefinition: [
+ gocanvas,
+ "form",
+ ],
+ description: "The name of the form you want to create a prepopulated submission for",
+ },
+ entries: {
+ type: "object",
+ label: "Entries",
+ description: `DIEntry elements consisting of label/value pairs.
+ \n Label: Either the Export Label or plain Label field attribute (caseinsensitive) as defined in the form builder.
+ \n Value: The value assigned to this Dispatch Item entry.
+ `,
+ },
+ },
+ async run({ $ }) {
+ let entriesString = "";
+ for (const [
+ key,
+ value,
+ ] of Object.entries(this.entries)) {
+ entriesString += ``;
+ }
+ const response = await this.gocanvas.dispatchItems({
+ $,
+ data: `
+
+
+
+ ${entriesString}
+
+
+ `,
+ });
+ $.export("$summary", `Successfully created dispatch item in ${this.form}`);
+ return response;
+ },
+};
diff --git a/components/gocanvas/actions/create-or-update-reference-data/create-or-update-reference-data.mjs b/components/gocanvas/actions/create-or-update-reference-data/create-or-update-reference-data.mjs
new file mode 100644
index 0000000000000..3280cc055cde4
--- /dev/null
+++ b/components/gocanvas/actions/create-or-update-reference-data/create-or-update-reference-data.mjs
@@ -0,0 +1,71 @@
+import gocanvas from "../../gocanvas.app.mjs";
+import { parse } from "csv-parse/sync";
+import { ConfigurationError } from "@pipedream/platform";
+
+export default {
+ key: "gocanvas-create-or-update-reference-data",
+ name: "Create or Update Reference Data",
+ description: "Creates or updates GoCanvas reference data. [See the documentation](https://help.gocanvas.com/hc/en-us/article_attachments/26468076609559)",
+ version: "0.0.1",
+ type: "action",
+ props: {
+ gocanvas,
+ name: {
+ type: "string",
+ label: "Reference Data Name",
+ description: "The attribute name of the dataset to operate on. Will be created if it doesn't already exist.",
+ },
+ data: {
+ type: "string",
+ label: "Data",
+ description: `A string of comma separated values representing the data to create/update. **Include Column names**:
+ \n Example:
+ \n Column1,Column2,Column3
+ \n Data1Column1,Data1Column2,Data1Column3
+ \n Data2Column1,Data2Column2,Data3Column3
+ `,
+ },
+ },
+ methods: {
+ csvToXml(data) {
+ const records = parse(data, {
+ columns: true,
+ trim: true,
+ });
+
+ if (!records?.length) {
+ throw new ConfigurationError("No data items found to create/update. Please enter column names and at least 1 row of data.");
+ }
+
+ // Extract columns
+ const columns = Object.keys(records[0]);
+ let result = "";
+ result += columns.map((col) => `${col}`).join("");
+ result += "\n\n";
+
+ // Extract rows
+ result += records
+ .map((row) => {
+ const rowValues = columns.map((col) => `${row[col]}`).join("");
+ return ` ${rowValues}`;
+ })
+ .join("\n");
+
+ result += "\n";
+ return result;
+ },
+ },
+ async run({ $ }) {
+ const response = await this.gocanvas.createUpdateReferenceData({
+ $,
+ data: `
+
+
+ ${await this.csvToXml(this.data)}
+
+ `,
+ });
+ $.export("$summary", "Successfully created/updated reference data");
+ return response;
+ },
+};
diff --git a/components/gocanvas/actions/delete-dispatch/delete-dispatch.mjs b/components/gocanvas/actions/delete-dispatch/delete-dispatch.mjs
new file mode 100644
index 0000000000000..ddafebef637d6
--- /dev/null
+++ b/components/gocanvas/actions/delete-dispatch/delete-dispatch.mjs
@@ -0,0 +1,46 @@
+import gocanvas from "../../gocanvas.app.mjs";
+
+export default {
+ key: "gocanvas-delete-dispatch",
+ name: "Delete Dispatch",
+ description: "Removes a specific dispatch from GoCanvas. [See the documentation](https://help.gocanvas.com/hc/en-us/article_attachments/26468076609559)",
+ version: "0.0.1",
+ type: "action",
+ props: {
+ gocanvas,
+ form: {
+ propDefinition: [
+ gocanvas,
+ "form",
+ ],
+ },
+ dispatchId: {
+ propDefinition: [
+ gocanvas,
+ "dispatchId",
+ (c) => ({
+ form: c.form,
+ }),
+ ],
+ },
+ },
+ async run({ $ }) {
+ const description = await this.gocanvas.getDispatchDescription({
+ $,
+ dispatchId: this.dispatchId,
+ });
+ const response = await this.gocanvas.dispatchItems({
+ $,
+ data: `
+
+
+
+
+
+
+ `,
+ });
+ $.export("$summary", `Successfully deleted dispatch with ID ${this.dispatchId}`);
+ return response;
+ },
+};
diff --git a/components/gocanvas/gocanvas.app.mjs b/components/gocanvas/gocanvas.app.mjs
index 45e1ba69a4cb3..40ec3505be34a 100644
--- a/components/gocanvas/gocanvas.app.mjs
+++ b/components/gocanvas/gocanvas.app.mjs
@@ -1,11 +1,151 @@
+import { axios } from "@pipedream/platform";
+import xml2js from "xml2js";
+
export default {
type: "app",
app: "gocanvas",
- propDefinitions: {},
+ propDefinitions: {
+ form: {
+ type: "string",
+ label: "Form",
+ description: "The identifier of a form",
+ async options() {
+ const forms = await this.listForms();
+ return forms?.map((form) => form.Name[0]) || [];
+ },
+ },
+ dispatchId: {
+ type: "string",
+ label: "Dispatch ID",
+ description: "Identifier of a dispatch",
+ async options({
+ page, form,
+ }) {
+ const dispatches = await this.getActiveDispatches({
+ form,
+ data: {
+ page: page + 1,
+ },
+ });
+ return dispatches?.map(({
+ $, Description: desc,
+ }) => ({
+ value: $.Id,
+ label: desc[0],
+ })) || [];
+ },
+ },
+ },
methods: {
- // this.$auth contains connected account data
- authKeys() {
- console.log(Object.keys(this.$auth));
+ _baseUrl() {
+ return "https://www.gocanvas.com/apiv2/";
+ },
+ _makeRequest(opts = {}) {
+ const {
+ $ = this,
+ path,
+ params,
+ ...otherOpts
+ } = opts;
+ return axios($, {
+ ...otherOpts,
+ url: `${this._baseUrl()}${path}`,
+ params: {
+ ...params,
+ username: `${this.$auth.username}`,
+ },
+ headers: {
+ Authorization: `Bearer ${this.$auth.api_key}`,
+ },
+ });
+ },
+ async getActiveDispatches({
+ form, ...opts
+ }) {
+ const response = await this._makeRequest({
+ path: "/dispatch_export",
+ ...opts,
+ });
+ const { CanvasResult: { Dispatches: dispatches } } = await new xml2js
+ .Parser().parseStringPromise(response);
+ if (!dispatches?.length) {
+ return [];
+ }
+ return dispatches
+ .flatMap((d) => d.Dispatch || [])
+ .filter((d) => d.Status[0] !== "deleted")
+ .filter((d) => !form || d.Form[0] === form);
+ },
+ async listForms(opts = {}) {
+ const response = await this._makeRequest({
+ path: "/forms",
+ ...opts,
+ });
+ const { CanvasResult: { Forms: forms } } = await new xml2js
+ .Parser().parseStringPromise(response);
+ if (!forms?.length) {
+ return [];
+ }
+ return forms.flatMap((form) => form.Form || []);
+ },
+ async listSubmissions(opts = {}) {
+ const response = await this._makeRequest({
+ path: "/submissions",
+ ...opts,
+ });
+ const { CanvasResult: { Submissions: submissions } } = await new xml2js
+ .Parser().parseStringPromise(response);
+ if (!submissions?.length) {
+ return [];
+ }
+ return submissions.flatMap((sub) => sub.Submission || []);
+ },
+ async getDispatchDescription({
+ dispatchId, ...opts
+ }) {
+ const dispatches = await this.getActiveDispatches({
+ ...opts,
+ });
+ const dispatch = dispatches.find(({ $ }) => $.Id === dispatchId);
+ return dispatch.Description[0];
+ },
+ dispatchItems(opts = {}) {
+ return this._makeRequest({
+ method: "POST",
+ path: "/dispatch_items",
+ ...opts,
+ });
+ },
+ createUpdateReferenceData(opts = {}) {
+ return this._makeRequest({
+ method: "POST",
+ path: "/reference_datas",
+ ...opts,
+ });
+ },
+ async *paginate({
+ fn,
+ params,
+ max,
+ }) {
+ params = {
+ ...params,
+ page: 1,
+ };
+ let total, count = 0;
+ do {
+ const results = await fn({
+ params,
+ });
+ for (const item of results) {
+ yield item;
+ if (max && ++count >= max) {
+ return;
+ }
+ }
+ total = results?.length;
+ params.page++;
+ } while (total);
},
},
};
diff --git a/components/gocanvas/package.json b/components/gocanvas/package.json
new file mode 100644
index 0000000000000..d5437280de080
--- /dev/null
+++ b/components/gocanvas/package.json
@@ -0,0 +1,20 @@
+{
+ "name": "@pipedream/gocanvas",
+ "version": "0.0.1",
+ "description": "Pipedream GoCanvas Components",
+ "main": "gocanvas.app.mjs",
+ "keywords": [
+ "pipedream",
+ "gocanvas"
+ ],
+ "homepage": "https://pipedream.com/apps/gocanvas",
+ "author": "Pipedream (https://pipedream.com/)",
+ "publishConfig": {
+ "access": "public"
+ },
+ "dependencies": {
+ "@pipedream/platform": "^3.0.3",
+ "csv-parse": "^5.5.6",
+ "xml2js": "^0.6.2"
+ }
+}
diff --git a/components/gocanvas/sources/new-submission-received/new-submission-received.mjs b/components/gocanvas/sources/new-submission-received/new-submission-received.mjs
new file mode 100644
index 0000000000000..5b655fb8f18df
--- /dev/null
+++ b/components/gocanvas/sources/new-submission-received/new-submission-received.mjs
@@ -0,0 +1,103 @@
+import gocanvas from "../../gocanvas.app.mjs";
+import { DEFAULT_POLLING_SOURCE_TIMER_INTERVAL } from "@pipedream/platform";
+
+export default {
+ key: "gocanvas-new-submission-received",
+ name: "New Submission Recieved",
+ description: "Emit new event when a new submission is uploaded to GoCanvas.",
+ version: "0.0.1",
+ type: "source",
+ dedupe: "unique",
+ props: {
+ gocanvas,
+ db: "$.service.db",
+ timer: {
+ type: "$.interface.timer",
+ default: {
+ intervalSeconds: DEFAULT_POLLING_SOURCE_TIMER_INTERVAL,
+ },
+ },
+ form: {
+ propDefinition: [
+ gocanvas,
+ "form",
+ ],
+ },
+ },
+ hooks: {
+ async deploy() {
+ await this.processEvent(25);
+ },
+ },
+ methods: {
+ _getLastSubmissionDate() {
+ return this.db.get("lastSubmissionDate");
+ },
+ _setLastSubmissionDate(lastSubmissionDate) {
+ this.db.set("lastSubmissionDate", lastSubmissionDate);
+ },
+ generateMeta(submission) {
+ return {
+ id: submission.ResponseID,
+ summary: `New Submission: ${submission.ResponseID}`,
+ ts: Date.parse(submission.Date),
+ };
+ },
+ currentDate() {
+ const currentDate = new Date();
+ return `${String(currentDate.getMonth() + 1)
+ .padStart(2, "0")}/${String(currentDate.getDate())
+ .padStart(2, "0")}/${currentDate.getFullYear()}`;
+ },
+ formatResponse(obj) {
+ if (Array.isArray(obj) && obj.length === 1) {
+ return this.formatResponse(obj[0]);
+ } else if (typeof obj === "object" && obj !== null) {
+ return Object.fromEntries(
+ Object.entries(obj).map(([
+ key,
+ value,
+ ]) => [
+ key,
+ this.formatResponse(value),
+ ]),
+ );
+ } else {
+ return obj;
+ }
+ },
+ async processEvent(max) {
+ let lastSubmissionDate = this._getLastSubmissionDate();
+
+ const params = {
+ form_name: this.form,
+ };
+ if (lastSubmissionDate) {
+ params.begin_date = new Date(lastSubmissionDate).toLocaleDateString("en-US");
+ params.end_date = this.currentDate();
+ }
+
+ const results = this.gocanvas.paginate({
+ fn: this.gocanvas.listSubmissions,
+ params,
+ max,
+ });
+
+ for await (const result of results) {
+ const submission = this.formatResponse(result);
+ const meta = this.generateMeta(submission);
+ this.$emit(submission, meta);
+
+ if (!lastSubmissionDate
+ || Date.parse(submission.Date) >= Date.parse(lastSubmissionDate)) {
+ lastSubmissionDate = submission.Date;
+ }
+ }
+
+ this._setLastSubmissionDate(lastSubmissionDate);
+ },
+ },
+ async run() {
+ await this.processEvent();
+ },
+};
diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml
index 48e44a6b26ac2..67a0ced94a27a 100644
--- a/pnpm-lock.yaml
+++ b/pnpm-lock.yaml
@@ -4080,6 +4080,16 @@ importers:
components/gobio_link:
specifiers: {}
+ components/gocanvas:
+ specifiers:
+ '@pipedream/platform': ^3.0.3
+ csv-parse: ^5.5.6
+ xml2js: ^0.6.2
+ dependencies:
+ '@pipedream/platform': 3.0.3
+ csv-parse: 5.5.6
+ xml2js: 0.6.2
+
components/godaddy:
specifiers: {}
@@ -24384,6 +24394,10 @@ packages:
resolution: {integrity: sha512-I7K1Uu0MBPzaFKg4nI5Q7Vs2t+3gWWW648spaF+Rg7pI9ds18Ugn+lvg4SHczUdKlHI5LWBXyqfS8+DufyBsgQ==}
dev: false
+ /csv-parse/5.5.6:
+ resolution: {integrity: sha512-uNpm30m/AGSkLxxy7d9yRXpJQFrZzVWLFBkS+6ngPcZkw/5k3L/jjFuj7tVnEpRn+QgmiXr21nDlhCiUK4ij2A==}
+ dev: false
+
/current-module-paths/1.1.1:
resolution: {integrity: sha512-8Ga5T8oMXBaSsHq9Gj+bddX7kHSaJKsl2vaAd3ep51eQLkr4W18eFEmEZM5bLo1zrz8tt3jE1U8QK9QGhaLR4g==}
engines: {node: '>=12.17'}