From 8e46f3d9d69575335a14534631eba3a10f5cb14a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9mi=20Bardon?= Date: Sat, 22 Jun 2024 20:03:15 +0200 Subject: [PATCH] Render templates using `env` before dereferencing Allows using `${{ env.anything }}` in `$ref`s. See https://github.com/stepci/stepci/issues/220 for a use case explanation. --- src/index.ts | 47 ++++++++++++++++++++-- tests/templating-in-$refs-external-env.yml | 26 ++++++++++++ tests/templating-in-$refs.yml | 26 ++++++++++++ tests/test.ts | 4 ++ 4 files changed, 100 insertions(+), 3 deletions(-) create mode 100644 tests/templating-in-$refs-external-env.yml create mode 100644 tests/templating-in-$refs.yml diff --git a/src/index.ts b/src/index.ts index 8952dcc..3caf5a3 100644 --- a/src/index.ts +++ b/src/index.ts @@ -1,5 +1,5 @@ import { CookieJar, Cookie } from 'tough-cookie' -import { renderObject as liquidlessRenderObject } from 'liquidless' +import { renderObject as liquidlessRenderObject, renderString as liquidlessRenderString } from 'liquidless' import { fake } from 'liquidless-faker' import { naughtystring } from 'liquidless-naughtystrings' import { EventEmitter } from 'node:events' @@ -200,14 +200,55 @@ function renderObject( }) } +function renderString( + string: string, + props: object, +): string { + return liquidlessRenderString(string, props, { + filters: { + fake, + naughtystring + }, + delimiters: templateDelimiters + }) +} + +function map$refs(obj: any, transform: (value: any) => any): void { + for (let key in obj) { + if (key === '$ref') { + obj[key] = transform(obj[key]); + } else if (typeof obj[key] === 'object' && obj[key] !== null) { + map$refs(obj[key], transform); + } + } +} + // Run from test file export async function runFromYAML(yamlString: string, options?: WorkflowOptions): Promise { - const workflow = yaml.load(yamlString) - const dereffed = await $RefParser.dereference(workflow as any, { + // Parse YAML file + const workflow = yaml.load(yamlString) as any + + // Render templates in `workflow.env`, giving `options?.env` as the only available props + workflow.env = renderObject(workflow.env, { env: { ...options?.env } }) + + // Render templates in `$ref`s, giving `workflow.env` and `options?.env` as the only available props + const env = { ...workflow.env, ...options?.env } + map$refs(workflow, (value) => { + if (typeof value === 'object') { + return renderObject(value, { env }) + } else { + return renderString(value, { env }) + } + }) + + // Dereference `$ref`s + const dereffed = await $RefParser.dereference(workflow, { dereference: { circular: 'ignore' } }) as unknown as Workflow + + // Run the workflow return run(dereffed, options) } diff --git a/tests/templating-in-$refs-external-env.yml b/tests/templating-in-$refs-external-env.yml new file mode 100644 index 0000000..9821ae3 --- /dev/null +++ b/tests/templating-in-$refs-external-env.yml @@ -0,0 +1,26 @@ +version: '1.1' +name: Process templates using env in `$ref`s (https://github.com/stepci/stepci/issues/220) +env: + host: ${{ env.petstore }}/api/v3 +tests: + example: + steps: + - http: + method: POST + url: ${{ env.host }}/user + headers: + Content-Type: application/json + json: + { + 'id': 10, + 'username': 'theUser', + 'firstName': 'John', + 'lastName': 'James', + 'email': 'john@email.com', + 'password': '12345', + 'phone': '12345', + 'userStatus': 1, + } + check: + schema: + $ref: ${{ env.host }}/openapi.yaml#/components/schemas/User diff --git a/tests/templating-in-$refs.yml b/tests/templating-in-$refs.yml new file mode 100644 index 0000000..032abb1 --- /dev/null +++ b/tests/templating-in-$refs.yml @@ -0,0 +1,26 @@ +version: '1.1' +name: Process templates using env in `$ref`s (https://github.com/stepci/stepci/issues/220) +env: + host: https://petstore3.swagger.io/api/v3 +tests: + example: + steps: + - http: + method: POST + url: ${{ env.host }}/user + headers: + Content-Type: application/json + json: + { + 'id': 10, + 'username': 'theUser', + 'firstName': 'John', + 'lastName': 'James', + 'email': 'john@email.com', + 'password': '12345', + 'phone': '12345', + 'userStatus': 1, + } + check: + schema: + $ref: ${{ env.host }}/openapi.yaml#/components/schemas/User diff --git a/tests/test.ts b/tests/test.ts index ee098e4..37117cc 100644 --- a/tests/test.ts +++ b/tests/test.ts @@ -4,3 +4,7 @@ import { EventEmitter } from 'node:events' const ee = new EventEmitter() runFromFile('./tests/basic.yml').then(({ result }) => console.log(result.tests[0].steps)) runFromFile('./tests/multipart.yml').then(({ result }) => console.log(result.tests[0].steps)) +runFromFile('./tests/templating-in-$refs.yml') + .then(({ result }) => console.log(result.tests[0].steps)) +runFromFile('./tests/templating-in-$refs-external-env.yml', { env: { petstore: "https://petstore3.swagger.io" }}) + .then(({ result }) => console.log(result.tests[0].steps))