diff --git a/components/domo/actions/add-data-to-dataset/add-data-to-dataset.mjs b/components/domo/actions/add-data-to-dataset/add-data-to-dataset.mjs new file mode 100644 index 0000000000000..377edefa00e24 --- /dev/null +++ b/components/domo/actions/add-data-to-dataset/add-data-to-dataset.mjs @@ -0,0 +1,33 @@ +import domo from "../../domo.app.mjs"; +import { axios } from "@pipedream/platform"; + +export default { + key: "domo-add-data-to-dataset", + name: "Add Data to Dataset", + description: "Adds new rows of data to an existing Domo dataset. [See the documentation]()", + version: "0.0.{{ts}}", + type: "action", + props: { + domo, + datasetId: { + propDefinition: [ + domo, + "datasetId", + ], + }, + data: { + propDefinition: [ + domo, + "data", + ], + }, + }, + async run({ $ }) { + const response = await this.domo.addDatasetRows({ + datasetId: this.datasetId, + data: this.data, + }); + $.export("$summary", `Added ${this.data.length} rows to dataset ${this.datasetId}`); + return response; + }, +}; diff --git a/components/domo/actions/update-card-description/update-card-description.mjs b/components/domo/actions/update-card-description/update-card-description.mjs new file mode 100644 index 0000000000000..6e8e1f4c310fc --- /dev/null +++ b/components/domo/actions/update-card-description/update-card-description.mjs @@ -0,0 +1,33 @@ +import domo from "../../domo.app.mjs"; +import { axios } from "@pipedream/platform"; + +export default { + key: "domo-update-card-description", + name: "Update Card Description", + description: "Updates the description of an existing card in Domo. [See the documentation]()", + version: "0.0.{{ts}}", + type: "action", + props: { + domo, + updateCardId: { + propDefinition: [ + domo, + "updateCardId", + ], + }, + newDescription: { + propDefinition: [ + domo, + "newDescription", + ], + }, + }, + async run({ $ }) { + const response = await this.domo.updateCardDescription({ + cardId: this.updateCardId, + newDescription: this.newDescription, + }); + $.export("$summary", `Successfully updated description for card ID ${this.updateCardId}`); + return response; + }, +}; diff --git a/components/domo/domo.app.mjs b/components/domo/domo.app.mjs index 125c11738eb95..ee16c2c3857c0 100644 --- a/components/domo/domo.app.mjs +++ b/components/domo/domo.app.mjs @@ -1,11 +1,223 @@ +import { axios } from "@pipedream/platform"; + export default { type: "app", app: "domo", - propDefinitions: {}, + version: "0.0.{{ts}}", + propDefinitions: { + // Emit new event when a new dataset is added + datasetOwner: { + type: "string", + label: "Dataset Owner", + description: "Filter datasets by owner.", + optional: true, + }, + datasetTags: { + type: "string[]", + label: "Dataset Tags", + description: "Filter datasets by tags.", + optional: true, + }, + // Emit new event when data within a specific card is updated + cardId: { + type: "string", + label: "Card ID", + description: "The ID of the card to monitor for data updates.", + }, + thresholds: { + type: "string[]", + label: "Thresholds", + description: "Conditions or thresholds for data updates, in JSON format.", + optional: true, + }, + conditions: { + type: "string[]", + label: "Conditions", + description: "Conditions for data updates, in JSON format.", + optional: true, + }, + projectId: { + type: "string", + label: "Project ID", + description: "The ID of the project.", + optional: true, + async options() { + const projects = await this.listProjects(); + return projects.map((project) => ({ + label: project.name, + value: project.id, + })); + }, + }, + listId: { + type: "string", + label: "List ID", + description: "The ID of the list.", + optional: true, + async options() { + if (!this.projectId) { + return []; + } + const lists = await this.listLists({ + projectId: this.projectId, + }); + return lists.map((list) => ({ + label: list.name, + value: list.id, + })); + }, + }, + // Emit new event when an alert is triggered + alertTypes: { + type: "string[]", + label: "Alert Types", + description: "Filter alerts by specific types.", + optional: true, + }, + relatedDatasets: { + type: "string[]", + label: "Related Datasets", + description: "Filter alerts by related datasets.", + optional: true, + }, + // Add new rows of data to an existing dataset + datasetId: { + type: "string", + label: "Dataset ID", + description: "The ID of the dataset to add data to.", + }, + data: { + type: "string[]", + label: "Data", + description: + "The data to add to the dataset, as JSON strings representing objects or arrays.", + }, + // Update the description of an existing card + updateCardId: { + type: "string", + label: "Card ID", + description: "The ID of the card to update.", + }, + newDescription: { + type: "string", + label: "New Description", + description: "The new description for the card.", + }, + }, methods: { - // this.$auth contains connected account data + // Existing method authKeys() { console.log(Object.keys(this.$auth)); }, + _baseUrl() { + return "https://api.domo.com"; + }, + async _makeRequest(opts = {}) { + const { + $, method = "GET", path = "/", headers = {}, data, params, ...otherOpts + } = opts; + return axios($, { + method, + url: this._baseUrl() + path, + headers: { + ...headers, + "Authorization": `Bearer ${this.$auth.oauth_access_token}`, + "Content-Type": "application/json", + }, + data: data || undefined, + params: params || undefined, + ...otherOpts, + }); + }, + // Method to list all projects + async listProjects(opts = {}) { + return this._makeRequest({ + method: "GET", + path: "/v1/projects", + ...opts, + }); + }, + // Method to list all lists within a project + async listLists({ + projectId, ...opts + }) { + return this._makeRequest({ + method: "GET", + path: `/v1/projects/${projectId}/lists`, + ...opts, + }); + }, + // Method to add new rows to a dataset + async addDatasetRows({ + datasetId, data, ...opts + }) { + const parsedData = data.map((row) => JSON.parse(row)); + return this._makeRequest({ + method: "POST", + path: `/v1/datasets/${datasetId}/data`, + data: parsedData, + ...opts, + }); + }, + // Method to update card description + async updateCardDescription({ + cardId, newDescription, ...opts + }) { + return this._makeRequest({ + method: "PUT", + path: `/v1/cards/${cardId}`, + data: { + description: newDescription, + }, + ...opts, + }); + }, + // Method to list alerts + async listAlerts(opts = {}) { + return this._makeRequest({ + method: "GET", + path: "/v1/alerts", + ...opts, + }); + }, + // Method to get alert details + async getAlertDetails({ + alertId, ...opts + }) { + return this._makeRequest({ + method: "GET", + path: `/v1/alerts/${alertId}`, + ...opts, + }); + }, + // Method to get card data + async getCardData({ + cardId, ...opts + }) { + return this._makeRequest({ + method: "GET", + path: `/v1/cards/${cardId}/data`, + ...opts, + }); + }, + // Pagination method + async paginate(fn, ...opts) { + let results = []; + let response; + let offset = 0; + const limit = 100; + + do { + response = await fn({ + offset, + limit, + ...opts, + }); + results = results.concat(response); + offset += limit; + } while (response.length === limit); + + return results; + }, }, }; diff --git a/components/domo/package.json b/components/domo/package.json index 2dfabb4f0fcda..2c2d07042ad7c 100644 --- a/components/domo/package.json +++ b/components/domo/package.json @@ -12,4 +12,4 @@ "publishConfig": { "access": "public" } -} \ No newline at end of file +} diff --git a/components/domo/sources/new-alert-triggered/new-alert-triggered.mjs b/components/domo/sources/new-alert-triggered/new-alert-triggered.mjs new file mode 100644 index 0000000000000..257294f6e22cb --- /dev/null +++ b/components/domo/sources/new-alert-triggered/new-alert-triggered.mjs @@ -0,0 +1,119 @@ +import { + axios, DEFAULT_POLLING_SOURCE_TIMER_INTERVAL, +} from "@pipedream/platform"; +import domo from "../../domo.app.mjs"; + +export default { + key: "domo-new-alert-triggered", + name: "New Alert Triggered", + description: "Emit a new event when an alert is triggered in Domo based on predefined rules. [See the documentation]().", + version: "0.0.{{ts}}", + type: "source", + dedupe: "unique", + props: { + domo: { + type: "app", + app: "domo", + }, + db: "$.service.db", + timer: { + type: "$.interface.timer", + default: { + intervalSeconds: DEFAULT_POLLING_SOURCE_TIMER_INTERVAL, + }, + }, + alertTypes: { + propDefinition: [ + "domo", + "alertTypes", + ], + optional: true, + }, + relatedDatasets: { + propDefinition: [ + "domo", + "relatedDatasets", + ], + optional: true, + }, + }, + methods: { + emitAlert(alert) { + const summary = `New Alert: ${alert.type} on Dataset ${alert.datasetId}`; + const ts = alert.time + ? new Date(alert.time).getTime() + : Date.now(); + const id = alert.id || ts; + this.$emit(alert, { + id: id.toString(), + summary, + ts, + }); + }, + }, + hooks: { + async deploy() { + const lastRun = await this.db.get("lastRun"); + const params = { + offset: 0, + limit: 50, + }; + if (lastRun) { + params.start = lastRun; + } + const alerts = await this.domo.listAlerts({ + params, + }); + const sortedAlerts = alerts.sort((a, b) => new Date(b.time) - new Date(a.time)); + const latestAlerts = sortedAlerts.slice(0, 50); + + for (const alert of latestAlerts.reverse()) { + this.emitAlert(alert); + } + + if (latestAlerts.length > 0) { + const latestTime = new Date(latestAlerts[0].time).getTime(); + await this.db.set("lastRun", latestTime); + } + }, + async activate() { + // No webhook subscription needed + }, + async deactivate() { + // No webhook unsubscription needed + }, + }, + async run() { + const lastRun = await this.db.get("lastRun") || 0; + const currentTime = Date.now(); + const params = { + start: lastRun, + end: currentTime, + limit: 1000, + }; + if (this.alertTypes && this.alertTypes.length > 0) { + params.actionType = this.alertTypes.join(","); + } + if (this.relatedDatasets && this.relatedDatasets.length > 0) { + params.objectId = this.relatedDatasets.join(","); + } + const alerts = await this.domo.listAlerts({ + params, + }); + const newAlerts = alerts.filter((alert) => new Date(alert.time).getTime() > lastRun); + + for (const alert of newAlerts) { + this.emitAlert(alert); + } + + if (newAlerts.length > 0) { + const latestTime = newAlerts.reduce((max, alert) => { + const alertTime = new Date(alert.time).getTime(); + return alertTime > max + ? alertTime + : max; + }, lastRun); + await this.db.set("lastRun", latestTime); + } + }, +}; diff --git a/components/domo/sources/new-data-in-card/new-data-in-card.mjs b/components/domo/sources/new-data-in-card/new-data-in-card.mjs new file mode 100644 index 0000000000000..354d9d0bbee16 --- /dev/null +++ b/components/domo/sources/new-data-in-card/new-data-in-card.mjs @@ -0,0 +1,182 @@ +import { + axios, DEFAULT_POLLING_SOURCE_TIMER_INTERVAL, +} from "@pipedream/platform"; +import domo from "../../domo.app.mjs"; + +export default { + key: "domo-new-data-in-card", + name: "New Data in Domo Card", + description: "Emit a new event when data within a specific Domo card is updated. [See the documentation]()", + version: "0.0.{{ts}}", + type: "source", + dedupe: "unique", + props: { + domo: { + type: "app", + app: "domo", + }, + db: { + type: "$.service.db", + }, + timer: { + type: "$.interface.timer", + default: { + intervalSeconds: DEFAULT_POLLING_SOURCE_TIMER_INTERVAL, + }, + }, + cardId: { + propDefinition: [ + domo, + "cardId", + ], + }, + thresholds: { + propDefinition: [ + domo, + "thresholds", + ], + optional: true, + }, + conditions: { + propDefinition: [ + domo, + "conditions", + ], + optional: true, + }, + projectId: { + propDefinition: [ + domo, + "projectId", + ], + }, + listId: { + propDefinition: [ + domo, + "listId", + ], + optional: true, + }, + }, + methods: { + evaluateCondition(fieldValue, operator, value) { + switch (operator) { + case ">=": + return fieldValue >= value; + case "<=": + return fieldValue <= value; + case ">": + return fieldValue > value; + case "<": + return fieldValue < value; + case "==": + return fieldValue == value; + case "===": + return fieldValue === value; + case "!=": + return fieldValue != value; + case "!==": + return fieldValue !== value; + default: + return false; + } + }, + shouldEmitEvent(currentData, lastData) { + if (!lastData) { + return true; + } + if (JSON.stringify(currentData) !== JSON.stringify(lastData)) { + return true; + } + if (this.thresholds.length > 0 || this.conditions.length > 0) { + for (const thresholdStr of this.thresholds) { + try { + const threshold = JSON.parse(thresholdStr); + const { + field, operator, value, + } = threshold; + const fieldValue = currentData[field]; + if (typeof fieldValue !== "undefined" && this.evaluateCondition(fieldValue, operator, value)) { + return true; + } + } catch (error) { + continue; + } + } + for (const conditionStr of this.conditions) { + try { + const condition = JSON.parse(conditionStr); + const { + field, operator, value, + } = condition; + const fieldValue = currentData[field]; + if (typeof fieldValue !== "undefined" && this.evaluateCondition(fieldValue, operator, value)) { + return true; + } + } catch (error) { + continue; + } + } + } + return false; + }, + }, + hooks: { + async deploy() { + try { + const currentData = await this.domo.getCardData({ + cardId: this.cardId, + }); + await this.db.set("lastData", currentData); + if (Array.isArray(currentData)) { + const eventsToEmit = currentData.slice(0, 50); + for (const data of eventsToEmit) { + const ts = data.updatedAt + ? Date.parse(data.updatedAt) + : Date.now(); + const id = data.id || ts; + const summary = `Data updated for Card ID: ${this.cardId}`; + this.$emit(data, { + id, + summary, + ts, + }); + } + } + } catch (error) { + console.error("Error in deploy hook:", error); + } + }, + async activate() { + // No action needed on activation for polling + }, + async deactivate() { + // No action needed on deactivation for polling + }, + }, + async run() { + try { + const currentData = await this.domo.getCardData({ + cardId: this.cardId, + }); + const lastData = await this.db.get("lastData"); + let shouldEmit = this.shouldEmitEvent(currentData, lastData); + + if (shouldEmit) { + const ts = currentData.updatedAt + ? Date.parse(currentData.updatedAt) + : Date.now(); + const id = currentData.id || ts; + const summary = `Data updated for Card ID: ${this.cardId}`; + this.$emit(currentData, { + id, + summary, + ts, + }); + await this.db.set("lastData", currentData); + } + } catch (error) { + console.error("Error in run method:", error); + } + }, +}; diff --git a/components/domo/sources/new-dataset/new-dataset.mjs b/components/domo/sources/new-dataset/new-dataset.mjs new file mode 100644 index 0000000000000..49cfb206d43f3 --- /dev/null +++ b/components/domo/sources/new-dataset/new-dataset.mjs @@ -0,0 +1,100 @@ +import { axios, DEFAULT_POLLING_SOURCE_TIMER_INTERVAL } from "@pipedream/platform"; +import domoApp from "../../domo.app.mjs"; + +export default { + key: "domo-new-dataset", + name: "New Dataset Added", + description: "Emit new event when a new dataset is added to the Domo instance. [See the documentation](), + version: "0.0.{{ts}}", + type: "source", + dedupe: "unique", + props: { + domo: { + type: "app", + app: "domo", + }, + db: "$.service.db", + timer: { + type: "$.interface.timer", + default: { + intervalSeconds: DEFAULT_POLLING_SOURCE_TIMER_INTERVAL, + }, + }, + datasetOwner: { + propDefinition: [domoApp, "datasetOwner"], + optional: true, + }, + datasetTags: { + propDefinition: [domoApp, "datasetTags"], + optional: true, + }, + }, + hooks: { + async deploy() { + try { + const allDatasets = await this.domo.listDatasets({ + owner: this.datasetOwner, + tags: this.datasetTags, + limit: 50, + sort: "created", + order: "desc", + }); + + for (const dataset of allDatasets.reverse()) { + this.$emit( + dataset, + { + id: dataset.id, + summary: `New Dataset: ${dataset.name}`, + ts: new Date(dataset.created).getTime(), + } + ); + } + + const datasetIds = allDatasets.map((dataset) => dataset.id); + await this.db.set("dataset_ids", datasetIds); + } catch (error) { + this.$emit({ error: "Failed to fetch datasets during deploy." }); + } + }, + async activate() { + // No webhook to activate for polling source + }, + async deactivate() { + // No webhook to deactivate for polling source + }, + }, + async run() { + try { + const allDatasets = await this.domo.listDatasets({ + owner: this.datasetOwner, + tags: this.datasetTags, + limit: 100, + sort: "created", + order: "desc", + }); + + const previousDatasetIds = await this.db.get("dataset_ids") || []; + + const newDatasets = allDatasets.filter( + (dataset) => !previousDatasetIds.includes(dataset.id) + ); + + for (const dataset of newDatasets.reverse()) { + this.$emit( + dataset, + { + id: dataset.id, + summary: `New Dataset: ${dataset.name}`, + ts: new Date(dataset.created).getTime(), + } + ); + } + + const currentDatasetIds = allDatasets.slice(0, 100).map((dataset) => dataset.id); + await this.db.set("dataset_ids", currentDatasetIds); + } catch (error) { + this.$emit({ error: "Failed to fetch datasets during run." }); + } + }, +}; \ No newline at end of file