diff --git a/src/content.js b/src/content.js index e9506a09..2dba1adb 100644 --- a/src/content.js +++ b/src/content.js @@ -37,6 +37,9 @@ export default class Content { this.values = github.context; break; } + if (config.inputs.payloadTemplated) { + this.values = this.templatize(this.values); + } if (config.inputs.payloadDelimiter) { this.values = flatten(this.values, { delimiter: config.inputs.payloadDelimiter, @@ -63,9 +66,8 @@ export default class Content { ); } try { - const input = this.templatize(config, config.inputs.payload); const content = /** @type {Content} */ ( - yaml.load(input, { + yaml.load(config.inputs.payload, { schema: yaml.JSON_SCHEMA, }) ); @@ -119,18 +121,17 @@ export default class Content { path.resolve(config.inputs.payloadFilePath), "utf-8", ); - const content = this.templatize(config, input); if ( config.inputs.payloadFilePath.endsWith("yaml") || config.inputs.payloadFilePath.endsWith("yml") ) { - const load = yaml.load(content, { + const load = yaml.load(input, { schema: yaml.JSON_SCHEMA, }); return /** @type {Content} */ (load); } if (config.inputs.payloadFilePath.endsWith("json")) { - return JSON.parse(content); + return JSON.parse(input); } throw new SlackError( config.core, @@ -148,20 +149,32 @@ export default class Content { } /** - * Replace templated variables in the provided content if requested. - * @param {Config} config - * @param {string} input - The initial value of the content. - * @returns {string} Content with templatized variables replaced. + * Replace templated variables in the provided content as requested. + * @param {unknown} input - The initial value of the content. + * @returns {unknown} Content with templatized variables replaced. */ - templatize(config, input) { - if (!config.inputs.payloadTemplated) { - return input; + templatize(input) { + if (Array.isArray(input)) { + return input.map((v) => this.templatize(v)); + } + if (input && typeof input === "object") { + /** + * @type {Record} + */ + const out = {}; + for (const [k, v] of Object.entries(input)) { + out[k] = this.templatize(v); + } + return out; + } + if (typeof input === "string") { + const template = input.replace(/\$\{\{/g, "{{"); // swap ${{ for {{ + const context = { + env: process.env, + github: github.context, + }; + return markup.up(template, context); } - const template = input.replace(/\$\{\{/g, "{{"); // swap ${{ for {{ - const context = { - env: process.env, - github: github.context, - }; - return markup.up(template, context); + return input; } } diff --git a/test/content.spec.js b/test/content.spec.js index 30a37286..737f6040 100644 --- a/test/content.spec.js +++ b/test/content.spec.js @@ -84,16 +84,120 @@ describe("content", () => { assert.deepEqual(config.content.values, expected); }); + it("templatizes variables requires configuration", async () => { + mocks.core.getInput.withArgs("payload").returns(`{ + "message": "this matches an existing variable: \${{ github.apiUrl }}", + "channel": "C0123456789" + } + `); + const config = new Config(mocks.core); + const expected = { + message: "this matches an existing variable: ${{ github.apiUrl }}", + channel: "C0123456789", + }; + assert.deepEqual(config.content.values, expected); + }); + it("templatizes variables with matching variables", async () => { - mocks.core.getInput - .withArgs("payload") - .returns("message: Served ${{ env.NUMBER }} from ${{ github.apiUrl }}"); + mocks.core.getInput.withArgs("payload").returns(` + channel: C0123456789 + reply_broadcast: false + message: Served \${{ env.NUMBER }} items + blocks: + - type: section + text: + type: mrkdwn + text: "Served \${{ env.NUMBER }} items on: \${{ env.DETAILS }}" + - type: divider + - type: section + block_id: selector + text: + type: mrkdwn + text: Send feedback + accessory: + action_id: response + type: multi_static_select + placeholder: + type: plain_text + text: Select URL + options: + - text: + type: plain_text + text: "\${{ github.apiUrl }}" + value: api + - text: + type: plain_text + text: "\${{ github.serverUrl }}" + value: server + - text: + type: plain_text + text: "\${{ github.graphqlUrl }}" + value: graphql + `); mocks.core.getBooleanInput.withArgs("payload-templated").returns(true); + process.env.DETAILS = ` +-fri +-sat +-sun`; process.env.NUMBER = 12; const config = new Config(mocks.core); + process.env.DETAILS = undefined; process.env.NUMBER = undefined; const expected = { - message: "Served 12 from https://api.github.com", + channel: "C0123456789", + reply_broadcast: false, + message: "Served 12 items", + blocks: [ + { + type: "section", + text: { + type: "mrkdwn", + text: "Served 12 items on: \n-fri\n-sat\n-sun", + }, + }, + { + type: "divider", + }, + { + type: "section", + block_id: "selector", + text: { + type: "mrkdwn", + text: "Send feedback", + }, + accessory: { + action_id: "response", + type: "multi_static_select", + placeholder: { + type: "plain_text", + text: "Select URL", + }, + options: [ + { + text: { + type: "plain_text", + text: "https://api.github.com", + }, + value: "api", + }, + { + text: { + type: "plain_text", + text: "https://github.com", + }, + value: "server", + }, + { + text: { + type: "plain_text", + text: "https://api.github.com/graphql", + }, + value: "graphql", + }, + ], + }, + }, + ], }; assert.deepEqual(config.content.values, expected); }); @@ -252,19 +356,147 @@ describe("content", () => { assert.deepEqual(config.content.values, expected); }); + it("templatizes variables requires configuration", async () => { + mocks.core.getInput.withArgs("payload-file-path").returns("example.json"); + mocks.fs.readFileSync + .withArgs(path.resolve("example.json"), "utf-8") + .returns(`{ + "message": "this matches an existing variable: \${{ github.apiUrl }}", + "channel": "C0123456789" + } + `); + const config = new Config(mocks.core); + const expected = { + message: "this matches an existing variable: ${{ github.apiUrl }}", + channel: "C0123456789", + }; + assert.deepEqual(config.content.values, expected); + }); + it("templatizes variables with matching variables", async () => { mocks.core.getInput.withArgs("payload-file-path").returns("example.json"); mocks.fs.readFileSync .withArgs(path.resolve("example.json"), "utf-8") .returns(`{ - "message": "Served $\{\{ env.NUMBER }} from $\{\{ github.apiUrl }}" + "channel": "C0123456789", + "reply_broadcast": false, + "message": "Served \${{ env.NUMBER }} items", + "blocks": [ + { + "type": "section", + "text": { + "type": "mrkdwn", + "text": "Served \${{ env.NUMBER }} items on: \${{ env.DETAILS }}" + } + }, + { + "type": "divider" + }, + { + "type": "section", + "block_id": "selector", + "text": { + "type": "mrkdwn", + "text": "Send feedback" + }, + "accessory": { + "action_id": "response", + "type": "multi_static_select", + "placeholder": { + "type": "plain_text", + "text": "Select URL" + }, + "options": [ + { + "text": { + "type": "plain_text", + "text": "\${{ github.apiUrl }}" + }, + "value": "api" + }, + { + "text": { + "type": "plain_text", + "text": "\${{ github.serverUrl }}" + }, + "value": "server" + }, + { + "text": { + "type": "plain_text", + "text": "\${{ github.graphqlUrl }}" + }, + "value": "graphql" + } + ] + } + } + ] }`); mocks.core.getBooleanInput.withArgs("payload-templated").returns(true); + process.env.DETAILS = ` +-fri +-sat +-sun`; process.env.NUMBER = 12; const config = new Config(mocks.core); + process.env.DETAILS = undefined; process.env.NUMBER = undefined; const expected = { - message: "Served 12 from https://api.github.com", + channel: "C0123456789", + reply_broadcast: false, + message: "Served 12 items", + blocks: [ + { + type: "section", + text: { + type: "mrkdwn", + text: "Served 12 items on: \n-fri\n-sat\n-sun", + }, + }, + { + type: "divider", + }, + { + type: "section", + block_id: "selector", + text: { + type: "mrkdwn", + text: "Send feedback", + }, + accessory: { + action_id: "response", + type: "multi_static_select", + placeholder: { + type: "plain_text", + text: "Select URL", + }, + options: [ + { + text: { + type: "plain_text", + text: "https://api.github.com", + }, + value: "api", + }, + { + text: { + type: "plain_text", + text: "https://github.com", + }, + value: "server", + }, + { + text: { + type: "plain_text", + text: "https://api.github.com/graphql", + }, + value: "graphql", + }, + ], + }, + }, + ], }; assert.deepEqual(config.content.values, expected); });