From 358ba88e0f0abc4478b12c75c29904f85df3fe18 Mon Sep 17 00:00:00 2001 From: Jorge Cortes Date: Wed, 30 Oct 2024 12:48:50 -0500 Subject: [PATCH] [Components] columns_ai - Added new action components --- .../build-graph-from-scratch.mjs | 152 ++++++++++++++++++ .../build-graph-from-template.mjs | 67 ++++++++ components/columns_ai/columns_ai.app.mjs | 62 ++++++- components/columns_ai/common/utils.mjs | 51 ++++++ components/columns_ai/package.json | 8 +- pnpm-lock.yaml | 148 ++++++++++++++++- 6 files changed, 481 insertions(+), 7 deletions(-) create mode 100644 components/columns_ai/actions/build-graph-from-scratch/build-graph-from-scratch.mjs create mode 100644 components/columns_ai/actions/build-graph-from-template/build-graph-from-template.mjs create mode 100644 components/columns_ai/common/utils.mjs diff --git a/components/columns_ai/actions/build-graph-from-scratch/build-graph-from-scratch.mjs b/components/columns_ai/actions/build-graph-from-scratch/build-graph-from-scratch.mjs new file mode 100644 index 0000000000000..8d4aa283a1b25 --- /dev/null +++ b/components/columns_ai/actions/build-graph-from-scratch/build-graph-from-scratch.mjs @@ -0,0 +1,152 @@ +import { ChartType } from "columns-graph-model"; +import app from "../../columns_ai.app.mjs"; +import utils from "../../common/utils.mjs"; + +export default { + key: "columns_ai-build-graph-from-scratch", + name: "Build Graph From Scratch", + description: "Builds a graph object from scratch and publishes it. [See the documentation](https://github.com/varchar-io/vaas?tab=readme-ov-file#basic-usage)", + version: "0.0.1", + type: "action", + props: { + app, + name: { + propDefinition: [ + app, + "name", + ], + }, + chartType: { + type: "string", + label: "Chart Type", + description: "The type of chart to construct", + async options() { + return [ + { + label: "Bar", + value: ChartType.BAR, + }, + { + label: "Pie", + value: ChartType.PIE, + }, + { + label: "Doughnut", + value: ChartType.DOUGHNUT, + }, + { + label: "Line", + value: ChartType.LINE, + }, + { + label: "Area", + value: ChartType.AREA, + }, + { + label: "Scatter", + value: ChartType.SCATTER, + }, + { + label: "Bar Race", + value: ChartType.BAR_RACE, + }, + { + label: "Boxplot", + value: ChartType.BOXPLOT, + }, + { + label: "Column", + value: ChartType.COLUMN, + }, + { + label: "Dot", + value: ChartType.DOT, + }, + { + label: "Gauge", + value: ChartType.GAUGE, + }, + { + label: "Map", + value: ChartType.MAP, + }, + { + label: "Radar", + value: ChartType.RADAR, + }, + { + label: "Summary", + value: ChartType.SUMMARY, + }, + { + label: "Table", + value: ChartType.TABLE, + }, + { + label: "Timeline", + value: ChartType.TIMELINE, + }, + { + label: "Timeline Area", + value: ChartType.TIMELINE_AREA, + }, + { + label: "Timeline Bar", + value: ChartType.TIMELINE_BAR, + }, + { + label: "Tree", + value: ChartType.TREE, + }, + { + label: "Wordcloud", + value: ChartType.WORDCLOUD, + }, + ]; + }, + }, + keys: { + propDefinition: [ + app, + "keys", + ], + }, + metrics: { + propDefinition: [ + app, + "metrics", + ], + }, + rows: { + propDefinition: [ + app, + "rows", + ], + }, + }, + async run({ $ }) { + const { + app, + name, + chartType, + keys, + metrics, + rows, + } = this; + + const graph = await app.createGraphFromScratch({ + chartType, + keys, + metrics, + rows: utils.parseArray(rows), + }); + + const response = await app.publishGraph({ + name, + graph, + }); + + $.export("$summary", "Successfully built and published the graph from scratch."); + return response; + }, +}; diff --git a/components/columns_ai/actions/build-graph-from-template/build-graph-from-template.mjs b/components/columns_ai/actions/build-graph-from-template/build-graph-from-template.mjs new file mode 100644 index 0000000000000..fa8e224d5da19 --- /dev/null +++ b/components/columns_ai/actions/build-graph-from-template/build-graph-from-template.mjs @@ -0,0 +1,67 @@ +import app from "../../columns_ai.app.mjs"; +import utils from "../../common/utils.mjs"; + +export default { + key: "columns_ai-build-graph-from-template", + name: "Build Graph From Template", + description: "Builds a graph object from a template and publishes it. [See the documentation](https://github.com/varchar-io/vaas?tab=readme-ov-file#basic-usage).", + version: "0.0.1", + type: "action", + props: { + app, + name: { + propDefinition: [ + app, + "name", + ], + }, + visualId: { + type: "string", + label: "Visual ID", + description: "The ID of an existing graph template on Columns. Eg. `U6tALuJ3cTdPFw` wher it can be taken from the URL `https://columns.ai/visual/view/U6tALuJ3cTdPFw`.", + }, + keys: { + propDefinition: [ + app, + "keys", + ], + }, + metrics: { + propDefinition: [ + app, + "metrics", + ], + }, + rows: { + propDefinition: [ + app, + "rows", + ], + }, + }, + async run({ $ }) { + const { + app, + name, + visualId, + keys, + metrics, + rows, + } = this; + + const graph = await app.createGraphFromTemplate({ + visualId, + keys, + metrics, + rows: utils.parseArray(rows), + }); + + const response = await app.publishGraph({ + name, + graph, + }); + + $.export("$summary", "Successfully built and published the graph from template."); + return response; + }, +}; diff --git a/components/columns_ai/columns_ai.app.mjs b/components/columns_ai/columns_ai.app.mjs index 0111513c3f0a3..b656937fdf7cb 100644 --- a/components/columns_ai/columns_ai.app.mjs +++ b/components/columns_ai/columns_ai.app.mjs @@ -1,11 +1,65 @@ +import { ChartType } from "columns-graph-model"; +import { Columns } from "columns-sdk"; + export default { type: "app", app: "columns_ai", - propDefinitions: {}, + propDefinitions: { + name: { + type: "string", + label: "Graph Name", + description: "The name of the graph", + }, + keys: { + type: "string[]", + label: "Keys", + description: "An array of keys for the data rows.", + }, + metrics: { + type: "string[]", + label: "Metrics", + description: "An array of metrics for the data rows.", + }, + rows: { + type: "string[]", + label: "Rows", + description: "An array of data objects, where each object should be a JSON string. Eg. `{ \"metric\": 4000, \"key\": \"US\", \"parent\": null }`.", + }, + }, methods: { - // this.$auth contains connected account data - authKeys() { - console.log(Object.keys(this.$auth)); + getColumns() { + return new Columns(this.$auth.api_key); + }, + async createGraphFromScratch({ + keys = [], metrics = [], rows = [], chartType = ChartType.COLUMN, + } = {}) { + const columns = this.getColumns(); + const data = columns.data(keys, metrics, rows); + const graph = columns.graph(data); + + graph.type = chartType; + + return graph; + }, + async createGraphFromTemplate({ + visualId, keys = [], metrics = [], rows = [], + } = {}) { + const columns = this.getColumns(); + const graph = await columns.template(visualId); + + if (!graph) { + throw new Error(`Failed to load template from Columns: ${visualId}`); + } + + graph.data = columns.data(keys, metrics, rows); + + return graph; + }, + publishGraph({ + name, graph, + } = {}) { + const columns = this.getColumns(); + return columns.publish(name, graph); }, }, }; diff --git a/components/columns_ai/common/utils.mjs b/components/columns_ai/common/utils.mjs new file mode 100644 index 0000000000000..f48102438db77 --- /dev/null +++ b/components/columns_ai/common/utils.mjs @@ -0,0 +1,51 @@ +import { ConfigurationError } from "@pipedream/platform"; + +const parseJson = (input) => { + const parse = (value) => { + if (typeof(value) === "string") { + try { + return parseJson(JSON.parse(value)); + } catch (e) { + return value; + } + } else if (typeof(value) === "object" && value !== null) { + return Object.entries(value) + .reduce((acc, [ + key, + val, + ]) => Object.assign(acc, { + [key]: parse(val), + }), {}); + } + return value; + }; + + return parse(input); +}; + +function parseArray(value) { + try { + if (!value) { + return []; + } + + if (Array.isArray(value)) { + return value; + } + + const parsedValue = JSON.parse(value); + + if (!Array.isArray(parsedValue)) { + throw new Error("Not an array"); + } + + return parsedValue; + + } catch (e) { + throw new ConfigurationError("Make sure the custom expression contains a valid array object"); + } +} + +export default { + parseArray: (value) => parseArray(value).map(parseJson), +}; diff --git a/components/columns_ai/package.json b/components/columns_ai/package.json index bd40d3dc3b5d5..bff262bbc1214 100644 --- a/components/columns_ai/package.json +++ b/components/columns_ai/package.json @@ -1,6 +1,6 @@ { "name": "@pipedream/columns_ai", - "version": "0.0.1", + "version": "0.1.0", "description": "Pipedream Columns Ai Components", "main": "columns_ai.app.mjs", "keywords": [ @@ -11,5 +11,9 @@ "author": "Pipedream (https://pipedream.com/)", "publishConfig": { "access": "public" + }, + "dependencies": { + "columns-graph-model": "^1.1.4", + "columns-sdk": "^0.0.6" } -} \ No newline at end of file +} diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 1fcea59661dea..7ce15cc3bc317 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -2033,7 +2033,12 @@ importers: specifiers: {} components/columns_ai: - specifiers: {} + specifiers: + columns-graph-model: ^1.1.4 + columns-sdk: ^0.0.6 + dependencies: + columns-graph-model: 1.1.4 + columns-sdk: 0.0.6 components/cometly: specifiers: @@ -16240,6 +16245,11 @@ packages: engines: {node: '>=0.1.90'} dev: false + /@colors/colors/1.6.0: + resolution: {integrity: sha512-Ir+AOibqzrIsL6ajt3Rz3LskB7OiMVHqltZmspbW/TJuTVuyOMirVqAkjfY6JISiLHgyNqicAC8AyHHGzNd/dA==} + engines: {node: '>=0.1.90'} + dev: false + /@csstools/css-parser-algorithms/2.3.2_qabfbasg4cggam7o7issvon7wi: resolution: {integrity: sha512-sLYGdAdEY2x7TSw9FtmdaTrh2wFtRJO5VMbBrA8tEqEod7GEggFmxTSK9XqExib3yMuYNcvcTdCZIP6ukdjAIA==} engines: {node: ^14 || ^16 || >=18} @@ -18089,6 +18099,39 @@ packages: - supports-color dev: false + /@protobuf-ts/plugin-framework/2.9.4: + resolution: {integrity: sha512-9nuX1kjdMliv+Pes8dQCKyVhjKgNNfwxVHg+tx3fLXSfZZRcUHMc1PMwB9/vTvc6gBKt9QGz5ERqSqZc0++E9A==} + dependencies: + '@protobuf-ts/runtime': 2.9.4 + typescript: 3.9.10 + dev: false + + /@protobuf-ts/plugin/2.9.4: + resolution: {integrity: sha512-Db5Laq5T3mc6ERZvhIhkj1rn57/p8gbWiCKxQWbZBBl20wMuqKoHbRw4tuD7FyXi+IkwTToaNVXymv5CY3E8Rw==} + hasBin: true + dependencies: + '@protobuf-ts/plugin-framework': 2.9.4 + '@protobuf-ts/protoc': 2.9.4 + '@protobuf-ts/runtime': 2.9.4 + '@protobuf-ts/runtime-rpc': 2.9.4 + typescript: 3.9.10 + dev: false + + /@protobuf-ts/protoc/2.9.4: + resolution: {integrity: sha512-hQX+nOhFtrA+YdAXsXEDrLoGJqXHpgv4+BueYF0S9hy/Jq0VRTVlJS1Etmf4qlMt/WdigEes5LOd/LDzui4GIQ==} + hasBin: true + dev: false + + /@protobuf-ts/runtime-rpc/2.9.4: + resolution: {integrity: sha512-y9L9JgnZxXFqH5vD4d7j9duWvIJ7AShyBRoNKJGhu9Q27qIbchfzli66H9RvrQNIFk5ER7z1Twe059WZGqERcA==} + dependencies: + '@protobuf-ts/runtime': 2.9.4 + dev: false + + /@protobuf-ts/runtime/2.9.4: + resolution: {integrity: sha512-vHRFWtJJB/SiogWDF0ypoKfRIZ41Kq+G9cEFj6Qm1eQaAhJ1LDFvgZ7Ja4tb3iLOQhz0PaoPnnOijF1qmEqTxg==} + dev: false + /@protobufjs/aspromise/1.1.2: resolution: {integrity: sha512-j+gKExEuLmKwvz3OgROXtrJ2UG2x8Ch2YZUxahh+s1F2HZ+wAceUNLkvy6zKCPVRkU++ZWQrdxsUeQXmcg4uoQ==} dev: false @@ -22477,6 +22520,10 @@ packages: retry: 0.13.1 dev: false + /async/1.5.2: + resolution: {integrity: sha512-nSVgobk4rv61R9PUSDtYt7mPVB2olxNR5RWJcAsH676/ef11bUZwvu7+RGYrYauVdDPcO519v68wRhXQtxsV9w==} + dev: false + /async/3.2.4: resolution: {integrity: sha512-iAB+JbDEGXhyIUavoDl9WP/Jj106Kz9DEn1DPgYw5ruDn0e3Wgi3sKFm55sASdGBNOQB8F59d9qQ7deqrHA8wQ==} @@ -23580,6 +23627,25 @@ packages: text-hex: 1.0.0 dev: false + /columns-graph-model/1.1.4: + resolution: {integrity: sha512-44pHExxpELWGa3ekgiI/lX8EWUy4IDgcSN/BQlHyd1hgW++lBx1YkiVXIDcQPtQd8uakkDAk+BcwdLliDC5CQg==} + dependencies: + date-fns: 3.6.0 + dev: false + + /columns-sdk/0.0.6: + resolution: {integrity: sha512-yaOcRgaV+XdIarxDOvpzUErxvvzbAfxiM4zQrrGvzCRMxdBBIg6CYThkcPxhl0BdCYwDumL4GQFrkefAMk38cg==} + dependencies: + axios: 1.7.7 + columns-graph-model: 1.1.4 + nebula-js-lib: 0.3.2 + pako: 2.1.0 + short-unique-id: 5.2.0 + transitivePeerDependencies: + - debug + - supports-color + dev: false + /combined-stream/1.0.8: resolution: {integrity: sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==} engines: {node: '>= 0.8'} @@ -24133,6 +24199,10 @@ packages: '@babel/runtime': 7.23.1 dev: false + /date-fns/3.6.0: + resolution: {integrity: sha512-fRHTG8g/Gif+kSh50gaGEdToemgfj74aRX3swtiouboip5JDLAyDE9F11nHMIcvOaXeOC6D7SpNhi7uFyB7Uww==} + dev: false + /date-format/4.0.14: resolution: {integrity: sha512-39BOQLs9ZjKh0/patS9nrT8wc3ioX3/eA/zgbKNopnF2wCqJEoxywwwElATYvRsXdnOxA/OQeQoFZ3rFjVajhg==} engines: {node: '>=4.0'} @@ -24809,6 +24879,11 @@ packages: resolution: {integrity: sha512-AKrN98kuwOzMIdAizXGI86UFBoo26CL21UM763y1h/GMSJ4/OHU9k2YlsmBpyScFo/wbLzWQJBMCW4+IO3/+OQ==} dev: false + /encodeurl/2.0.0: + resolution: {integrity: sha512-Q0n9HRi4m6JuGIV1eFlmvJB7ZEVxu93IrMyiMsGC0lrMJMWzRgx6WGquyfQgZVb31vhGgXnfmPNNXmxnOkRBrg==} + engines: {node: '>= 0.8'} + dev: false + /encoding-japanese/2.0.0: resolution: {integrity: sha512-++P0RhebUC8MJAwJOsT93dT+5oc5oPImp1HubZpAuCZ5kTLnhuuBhKHj2jJeO/Gj93idPBWmIuQ9QWMe5rX3pQ==} engines: {node: '>=8.10.0'} @@ -25028,6 +25103,10 @@ packages: resolution: {integrity: sha512-ErCHMCae19vR8vQGe50xIsVomy19rg6gFu3+r3jkEO46suLMWBksvVyoGgQV+jOfl84ZSOSlmv6Gxa89PmTGmA==} engines: {node: '>=6'} + /escape-html/1.0.3: + resolution: {integrity: sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow==} + dev: false + /escape-string-regexp/1.0.5: resolution: {integrity: sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==} engines: {node: '>=0.8.0'} @@ -25727,6 +25806,21 @@ packages: engines: {node: '>=14.16'} dev: false + /finalhandler/1.3.1: + resolution: {integrity: sha512-6BN9trH7bp3qvnrRyzsBz+g3lZxTNZTbVO2EV1CS0WIcDbawYVdYvGflME/9QP0h0pYlCDBCTjYa9nZzMDpyxQ==} + engines: {node: '>= 0.8'} + dependencies: + debug: 2.6.9 + encodeurl: 2.0.0 + escape-html: 1.0.3 + on-finished: 2.4.1 + parseurl: 1.3.3 + statuses: 2.0.1 + unpipe: 1.0.0 + transitivePeerDependencies: + - supports-color + dev: false + /find-cache-dir/3.3.2: resolution: {integrity: sha512-wXZV5emFEjrridIgED11OoUKLxiYjAcqot/NJdAkOhlJ+vGzwhOAfcG5OX1jP+S0PcjEn8bdMJv+g2jwQ3Onig==} engines: {node: '>=8'} @@ -26647,6 +26741,10 @@ packages: node-forge: 1.3.1 dev: false + /google-protobuf/3.21.4: + resolution: {integrity: sha512-MnG7N936zcKTco4Jd2PX2U96Kf9PxygAPKBug+74LHzmHXmceN16MmRcdgZv+DGef/S9YvQAfRsNCn4cjf9yyQ==} + dev: false + /googleapis-common/5.1.0: resolution: {integrity: sha512-RXrif+Gzhq1QAzfjxulbGvAY3FPj8zq/CYcvgjzDbaBNCD6bUl+86I7mUs4DKWHGruuK26ijjR/eDpWIDgNROA==} engines: {node: '>=10.10.0'} @@ -30771,6 +30869,23 @@ packages: randexp: 0.4.6 dev: false + /nebula-js-lib/0.3.2: + resolution: {integrity: sha512-3BK0SMuxhXxqS1MClaIoGmHjZVJLLMXsSMTFvUAz9RklpfgdVFSO8MWPbkbOl0r5h43xtxxT5xx0PdsFn65CrQ==} + dependencies: + '@grpc/grpc-js': 1.11.1 + '@protobuf-ts/plugin': 2.9.4 + '@protobuf-ts/protoc': 2.9.4 + '@protobuf-ts/runtime': 2.9.4 + '@protobuf-ts/runtime-rpc': 2.9.4 + async: 1.5.2 + finalhandler: 1.3.1 + google-protobuf: 3.21.4 + json-bigint: 1.0.0 + winston: 3.11.0 + transitivePeerDependencies: + - supports-color + dev: false + /neo-async/2.6.2: resolution: {integrity: sha512-Yd3UES5mWCSqR+qNT93S3UoYUkqAZ9lLg8a7g9rimsWmYGK8cVToA4/sF3RrshdyV3sAGMXVUmpMYOw+dLpOuw==} dev: false @@ -31515,6 +31630,10 @@ packages: resolution: {integrity: sha512-HAKu/fG3HpHFO0AA8WE8q2g+gBJaZ9MG7fcKk+IJPLTGAD6Psw4443l+9DGRbOIh3/aXr7Phy0TjilYivJo5XQ==} dev: false + /pako/2.1.0: + resolution: {integrity: sha512-w+eufiZ1WuJYgPXbV/PO3NCMEc3xqylkKHzp8bxp1uW4qaSNQUkwmLLEc3kKsfz8lpV1F8Ht3U1Cm+9Srog2ug==} + dev: false + /parallel-transform/1.2.0: resolution: {integrity: sha512-P2vSmIu38uIlvdcU7fDkyrxj33gTUy/ABO5ZUbGowxNCopBq/OoD42bP4UmMrJoPyk4Uqf0mu3mtWBhHCZD8yg==} dependencies: @@ -31602,6 +31721,11 @@ packages: nearley: 2.20.1 dev: false + /parseurl/1.3.3: + resolution: {integrity: sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ==} + engines: {node: '>= 0.8'} + dev: false + /path-exists/3.0.0: resolution: {integrity: sha512-bpC7GYwiDYQ4wYLe+FA8lhRjhQCMcQGuSgGGqDkg/QerRWw9CmGRT0iSOVRSZJ29NMLZgIzqaljJ63oaL4NIJQ==} engines: {node: '>=4'} @@ -33608,6 +33732,11 @@ packages: stopcock: 1.1.0 dev: false + /short-unique-id/5.2.0: + resolution: {integrity: sha512-cMGfwNyfDZ/nzJ2k2M+ClthBIh//GlZl1JEf47Uoa9XR11bz8Pa2T2wQO4bVrRdH48LrIDWJahQziKo3MjhsWg==} + hasBin: true + dev: false + /should-equal/2.0.0: resolution: {integrity: sha512-ZP36TMrK9euEuWQYBig9W55WPC7uo37qzAEmbjHz4gfyuXrEUgF8cUvQVO+w+d3OMfPvSRQJ22lSm8MQJ43LTA==} dependencies: @@ -36109,6 +36238,23 @@ packages: winston-transport: 4.5.0 dev: false + /winston/3.11.0: + resolution: {integrity: sha512-L3yR6/MzZAOl0DsysUXHVjOwv8mKZ71TrA/41EIduGpOOV5LQVodqN+QdQ6BS6PJ/RdIshZhq84P/fStEZkk7g==} + engines: {node: '>= 12.0.0'} + dependencies: + '@colors/colors': 1.6.0 + '@dabh/diagnostics': 2.0.3 + async: 3.2.4 + is-stream: 2.0.1 + logform: 2.5.1 + one-time: 1.0.0 + readable-stream: 3.6.2 + safe-stable-stringify: 2.4.3 + stack-trace: 0.0.10 + triple-beam: 1.4.1 + winston-transport: 4.5.0 + dev: false + /woodpecker-api/1.1.0: resolution: {integrity: sha512-OLKMUEb1Fla1wq5JWM5G/RS+apcpAwq8oJVMRPDpG/9p/u+dbChtNVbqOnyEU3om8+WArvjQrGtMuKzxUS2paA==} dependencies: