From ac196c52f31bec381380bc35cf74d657716d7b78 Mon Sep 17 00:00:00 2001 From: Eric Koleda Date: Tue, 24 Sep 2024 10:59:29 -0400 Subject: [PATCH] Initial version --- package-lock.json | 19 +++++- package.json | 3 +- yaml/.coda-pack.json | 3 + yaml/pack.ts | 140 ++++++++++++++++++++++++++++++++++++++++ yaml/tests/pack_test.ts | 26 ++++++++ 5 files changed, 189 insertions(+), 2 deletions(-) create mode 100644 yaml/.coda-pack.json create mode 100644 yaml/pack.ts create mode 100644 yaml/tests/pack_test.ts diff --git a/package-lock.json b/package-lock.json index df9585a..22e5e9a 100644 --- a/package-lock.json +++ b/package-lock.json @@ -51,7 +51,8 @@ "text-encoding": "^0.7.0", "tokenize-words": "^0.0.4-alpha.0", "url-parse": "^1.5.10", - "url-polyfill": "^1.1.12" + "url-polyfill": "^1.1.12", + "yaml": "^2.5.1" }, "devDependencies": { "@types/chai": "^4.3.0", @@ -17180,6 +17181,17 @@ "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==" }, + "node_modules/yaml": { + "version": "2.5.1", + "resolved": "https://registry.npmjs.org/yaml/-/yaml-2.5.1.tgz", + "integrity": "sha512-bLQOjaX/ADgQ20isPJRvF0iRUHIxVhYvr53Of7wGcWlO2jvtUlH5m87DsmulFVxRpNLOnI4tB6p/oh8D7kpn9Q==", + "bin": { + "yaml": "bin.mjs" + }, + "engines": { + "node": ">= 14" + } + }, "node_modules/yargs": { "version": "17.7.2", "resolved": "https://registry.npmjs.org/yargs/-/yargs-17.7.2.tgz", @@ -30650,6 +30662,11 @@ "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==" }, + "yaml": { + "version": "2.5.1", + "resolved": "https://registry.npmjs.org/yaml/-/yaml-2.5.1.tgz", + "integrity": "sha512-bLQOjaX/ADgQ20isPJRvF0iRUHIxVhYvr53Of7wGcWlO2jvtUlH5m87DsmulFVxRpNLOnI4tB6p/oh8D7kpn9Q==" + }, "yargs": { "version": "17.7.2", "resolved": "https://registry.npmjs.org/yargs/-/yargs-17.7.2.tgz", diff --git a/package.json b/package.json index ae6f646..e69533a 100644 --- a/package.json +++ b/package.json @@ -45,7 +45,8 @@ "text-encoding": "^0.7.0", "tokenize-words": "^0.0.4-alpha.0", "url-parse": "^1.5.10", - "url-polyfill": "^1.1.12" + "url-polyfill": "^1.1.12", + "yaml": "^2.5.1" }, "devDependencies": { "@types/chai": "^4.3.0", diff --git a/yaml/.coda-pack.json b/yaml/.coda-pack.json new file mode 100644 index 0000000..0119a86 --- /dev/null +++ b/yaml/.coda-pack.json @@ -0,0 +1,3 @@ +{ + "packId": 34536 +} \ No newline at end of file diff --git a/yaml/pack.ts b/yaml/pack.ts new file mode 100644 index 0000000..02cb405 --- /dev/null +++ b/yaml/pack.ts @@ -0,0 +1,140 @@ +import * as coda from "@codahq/packs-sdk"; +import YAML from "yaml"; + +const OneDaySecs = 24 * 60 * 60; + +export const pack = coda.newPack(); + +const YAMLParam = coda.makeParameter({ + type: coda.ParameterType.String, + name: "yaml", + description: "The YAML contents as a string.", +}); + +const IndentParam = coda.makeParameter({ + type: coda.ParameterType.Number, + name: "indent", + description: "The number of spaces to indent by. Default: 2.", + optional: true, +}); + +pack.addFormula({ + name: "FormatYAML", + description: "Formats YAML content using the options provided.", + parameters: [ + YAMLParam, + IndentParam, + ], + resultType: coda.ValueType.String, + cacheTtlSecs: OneDaySecs, + examples: [ + { + params: [`a: [1, 2]`], + result: trimIndent(` + a: + - 1 + - 2 + `), + }, + { + params: [`a: [1, 2]`, 4], + result: trimIndent(` + a: + - 1 + - 2 + `), + }, + ], + execute: async function (args, context) { + let [yaml, indent] = args; + let options: Record = {}; + if (typeof indent == "number") { + options.indent = indent; + } + let value = parse(yaml); + return YAML.stringify(value, null, options); + }, +}); + +pack.addFormula({ + name: "IsValidYAML", + description: "Returns true if the content is valid YAML, false otherwise.", + parameters: [ + YAMLParam, + ], + resultType: coda.ValueType.Boolean, + cacheTtlSecs: OneDaySecs, + examples: [ + { params: [`a: [1, 2]`], result: true }, + { params: [`a: [1, 2`, ], result: false }, + ], + execute: async function (args, context) { + let [yaml] = args; + try { + YAML.parse(yaml); + } catch (e) { + if (e.toString().startsWith("YAMLParseError")) { + return false; + } + throw e; + } + return true; + }, +}); + +pack.addFormula({ + name: "ToJSON", + description: "Converts a YAML string to JSON.", + parameters: [ + YAMLParam, + IndentParam, + ], + resultType: coda.ValueType.String, + cacheTtlSecs: OneDaySecs, + examples: [ + { params: [`a: [1, 2]`], result: `{"a":[1,2]}` }, + { + params: [`a: [1, 2]`, 2], + result: trimIndent(` + { + "a": [ + 1, + 2 + ] + } + `, true) + }, + ], + execute: async function (args, context) { + let [yaml, spaces] = args; + let value = parse(yaml); + return JSON.stringify(value, null, spaces); + }, +}); + +function parse(yaml: string) { + try { + return YAML.parse(yaml); + } catch (e) { + if (e.toString().startsWith("YAMLParseError")) { + throw new coda.UserVisibleError(e); + } + throw e; + } +} + +function trimIndent(str: string, trim = false) { + let lines = str.split("\n"); + if (lines[0] == "") { + lines = lines.slice(1); + } + if (lines.at(-1) == "") { + lines = lines.slice(0, -1); + } + let indent = lines[0].match(/^\s*/)[0].length; + let result = lines.map(line => line.substring(indent)).join("\n"); + if (trim) { + result = result.trim(); + } + return result; +} \ No newline at end of file diff --git a/yaml/tests/pack_test.ts b/yaml/tests/pack_test.ts new file mode 100644 index 0000000..171856b --- /dev/null +++ b/yaml/tests/pack_test.ts @@ -0,0 +1,26 @@ +import {executeFormulaFromPackDef} from '@codahq/packs-sdk/dist/development'; +import {pack} from '../pack'; +import * as chai from "chai"; +import {assert} from "chai"; +import {describe} from "mocha"; +import {it} from "mocha"; +import chaiAsPromised from "chai-as-promised"; +chai.use(chaiAsPromised); +chai.should(); + +describe("Examples", () => { + for (let formula of pack.formulas) { + describe(formula.name, () => { + for (let [i, example] of formula.examples.entries()) { + it(`Example ${i}`, async () => { + const result = await executeFormulaFromPackDef(pack, formula.name, example.params as any); + if (typeof example.result == "object") { + assert.deepEqual(result, example.result); + } else { + assert.equal(result, example.result); + } + }); + } + }); + } +});