diff --git a/core/src/template-string/functions.ts b/core/src/template-string/functions.ts index 4f7c0766e1..aaf5367d46 100644 --- a/core/src/template-string/functions.ts +++ b/core/src/template-string/functions.ts @@ -8,10 +8,12 @@ import uuid from "uuid" import { TemplateStringError } from "../exceptions" -import { keyBy, mapValues, escapeRegExp, trim, isEmpty, camelCase, kebabCase } from "lodash" +import { keyBy, mapValues, escapeRegExp, trim, isEmpty, camelCase, kebabCase, isArrayLike } from "lodash" import { joi, JoiDescription } from "../config/common" import Joi from "@hapi/joi" import { validateSchema } from "../config/validation" +import { safeLoad, safeLoadAll } from "js-yaml" +import { safeDumpYaml } from "../util/util" interface TemplateHelperFunction { name: string @@ -188,6 +190,53 @@ const helperFunctionSpecs: TemplateHelperFunction[] = [ exampleOutput: "1b9d6bcd-bbfd-4b2d-9b5d-ab8dfbbd4bed", fn: () => uuid.v4(), }, + { + name: "yamlDecode", + description: + "Decodes the given YAML-encoded string. Note that for multi-document YAML strings, you need to set the 2nd argument to true (see below).", + arguments: { + string: joi.string().required().description("The YAML-encoded string to decode."), + multiDocument: joi.boolean().description("Set to true if you'd like to parse a multi-document YAML string."), + }, + outputSchema: joi.any(), + exampleArguments: [["a: 1\nb: 2\n"], ["a: 1\nb: 2\n---\na: 3\nb: 4\n", true]], + fn: (str: string, multi?: boolean) => (multi ? safeLoadAll(str) : safeLoad(str)), + }, + { + name: "yamlEncode", + description: "Encodes the given value as YAML.", + arguments: { + value: joi.any().required().description("The value to encode as YAML."), + multiDocument: joi.boolean().description("Set to true if you'd like to output a multi-document YAML string."), + }, + outputSchema: joi.string(), + exampleArguments: [ + [{ my: "simple document" }], + [ + [ + { a: 1, b: 2 }, + { a: 3, b: 4 }, + ], + true, + ], + ], + fn: (value: any, multiDocument?: boolean) => { + if (multiDocument) { + if (!isArrayLike(value)) { + throw new TemplateStringError( + `yamlEncode: Set multiDocument=true but value is not an array (got ${typeof value})`, + { + value, + multiDocument, + } + ) + } + return "---" + value.map(safeDumpYaml).join("---") + } else { + return safeDumpYaml(value) + } + }, + }, ] export const helperFunctions = keyBy( diff --git a/docs/reference/template-strings.md b/docs/reference/template-strings.md index 1607b4f41b..714686af3e 100644 --- a/docs/reference/template-strings.md +++ b/docs/reference/template-strings.md @@ -153,6 +153,26 @@ Usage: `uuidv4()` Examples: * `${uuidv4()}` -> `1b9d6bcd-bbfd-4b2d-9b5d-ab8dfbbd4bed` +### yamlDecode + +Decodes the given YAML-encoded string. Note that for multi-document YAML strings, you need to set the 2nd argument to true (see below). + +Usage: `yamlDecode(string, [multiDocument])` + +Examples: +* `${yamlDecode("a: 1\nb: 2\n")}` -> `{"a":1,"b":2}` +* `${yamlDecode("a: 1\nb: 2\n---\na: 3\nb: 4\n", true)}` -> `[{"a":1,"b":2},{"a":3,"b":4}]` + +### yamlEncode + +Encodes the given value as YAML. + +Usage: `yamlEncode(value, [multiDocument])` + +Examples: +* `${yamlEncode({"my":"simple document"})}` -> `"my: simple document\n"` +* `${yamlEncode([{"a":1,"b":2},{"a":3,"b":4}], true)}` -> `"---a: 1\nb: 2\n---a: 3\nb: 4\n"` + ## Project configuration context The following keys are available in any template strings within project definitions in `garden.yml` config files, except the `name` field (which cannot be templated). See the [Environment](#environment-configuration-context) and [Provider](#provider-configuration-context) sections below for additional keys available when configuring `environments` and `providers`, respectively.