Skip to content

Commit a3c435b

Browse files
fix: parse provided payloads before replacing templated variables (#449)
Co-authored-by: William Bergamin <william.bergamin.coen@gmail.com>
1 parent 6e4ac77 commit a3c435b

2 files changed

Lines changed: 269 additions & 24 deletions

File tree

src/content.js

Lines changed: 31 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,9 @@ export default class Content {
3737
this.values = github.context;
3838
break;
3939
}
40+
if (config.inputs.payloadTemplated) {
41+
this.values = this.templatize(this.values);
42+
}
4043
if (config.inputs.payloadDelimiter) {
4144
this.values = flatten(this.values, {
4245
delimiter: config.inputs.payloadDelimiter,
@@ -63,9 +66,8 @@ export default class Content {
6366
);
6467
}
6568
try {
66-
const input = this.templatize(config, config.inputs.payload);
6769
const content = /** @type {Content} */ (
68-
yaml.load(input, {
70+
yaml.load(config.inputs.payload, {
6971
schema: yaml.JSON_SCHEMA,
7072
})
7173
);
@@ -119,18 +121,17 @@ export default class Content {
119121
path.resolve(config.inputs.payloadFilePath),
120122
"utf-8",
121123
);
122-
const content = this.templatize(config, input);
123124
if (
124125
config.inputs.payloadFilePath.endsWith("yaml") ||
125126
config.inputs.payloadFilePath.endsWith("yml")
126127
) {
127-
const load = yaml.load(content, {
128+
const load = yaml.load(input, {
128129
schema: yaml.JSON_SCHEMA,
129130
});
130131
return /** @type {Content} */ (load);
131132
}
132133
if (config.inputs.payloadFilePath.endsWith("json")) {
133-
return JSON.parse(content);
134+
return JSON.parse(input);
134135
}
135136
throw new SlackError(
136137
config.core,
@@ -148,20 +149,32 @@ export default class Content {
148149
}
149150

150151
/**
151-
* Replace templated variables in the provided content if requested.
152-
* @param {Config} config
153-
* @param {string} input - The initial value of the content.
154-
* @returns {string} Content with templatized variables replaced.
152+
* Replace templated variables in the provided content as requested.
153+
* @param {unknown} input - The initial value of the content.
154+
* @returns {unknown} Content with templatized variables replaced.
155155
*/
156-
templatize(config, input) {
157-
if (!config.inputs.payloadTemplated) {
158-
return input;
156+
templatize(input) {
157+
if (Array.isArray(input)) {
158+
return input.map((v) => this.templatize(v));
159+
}
160+
if (input && typeof input === "object") {
161+
/**
162+
* @type {Record<string, unknown>}
163+
*/
164+
const out = {};
165+
for (const [k, v] of Object.entries(input)) {
166+
out[k] = this.templatize(v);
167+
}
168+
return out;
169+
}
170+
if (typeof input === "string") {
171+
const template = input.replace(/\$\{\{/g, "{{"); // swap ${{ for {{
172+
const context = {
173+
env: process.env,
174+
github: github.context,
175+
};
176+
return markup.up(template, context);
159177
}
160-
const template = input.replace(/\$\{\{/g, "{{"); // swap ${{ for {{
161-
const context = {
162-
env: process.env,
163-
github: github.context,
164-
};
165-
return markup.up(template, context);
178+
return input;
166179
}
167180
}

test/content.spec.js

Lines changed: 238 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -84,16 +84,120 @@ describe("content", () => {
8484
assert.deepEqual(config.content.values, expected);
8585
});
8686

87+
it("templatizes variables requires configuration", async () => {
88+
mocks.core.getInput.withArgs("payload").returns(`{
89+
"message": "this matches an existing variable: \${{ github.apiUrl }}",
90+
"channel": "C0123456789"
91+
}
92+
`);
93+
const config = new Config(mocks.core);
94+
const expected = {
95+
message: "this matches an existing variable: ${{ github.apiUrl }}",
96+
channel: "C0123456789",
97+
};
98+
assert.deepEqual(config.content.values, expected);
99+
});
100+
87101
it("templatizes variables with matching variables", async () => {
88-
mocks.core.getInput
89-
.withArgs("payload")
90-
.returns("message: Served ${{ env.NUMBER }} from ${{ github.apiUrl }}");
102+
mocks.core.getInput.withArgs("payload").returns(`
103+
channel: C0123456789
104+
reply_broadcast: false
105+
message: Served \${{ env.NUMBER }} items
106+
blocks:
107+
- type: section
108+
text:
109+
type: mrkdwn
110+
text: "Served \${{ env.NUMBER }} items on: \${{ env.DETAILS }}"
111+
- type: divider
112+
- type: section
113+
block_id: selector
114+
text:
115+
type: mrkdwn
116+
text: Send feedback
117+
accessory:
118+
action_id: response
119+
type: multi_static_select
120+
placeholder:
121+
type: plain_text
122+
text: Select URL
123+
options:
124+
- text:
125+
type: plain_text
126+
text: "\${{ github.apiUrl }}"
127+
value: api
128+
- text:
129+
type: plain_text
130+
text: "\${{ github.serverUrl }}"
131+
value: server
132+
- text:
133+
type: plain_text
134+
text: "\${{ github.graphqlUrl }}"
135+
value: graphql
136+
`);
91137
mocks.core.getBooleanInput.withArgs("payload-templated").returns(true);
138+
process.env.DETAILS = `
139+
-fri
140+
-sat
141+
-sun`;
92142
process.env.NUMBER = 12;
93143
const config = new Config(mocks.core);
144+
process.env.DETAILS = undefined;
94145
process.env.NUMBER = undefined;
95146
const expected = {
96-
message: "Served 12 from https://api.github.com",
147+
channel: "C0123456789",
148+
reply_broadcast: false,
149+
message: "Served 12 items",
150+
blocks: [
151+
{
152+
type: "section",
153+
text: {
154+
type: "mrkdwn",
155+
text: "Served 12 items on: \n-fri\n-sat\n-sun",
156+
},
157+
},
158+
{
159+
type: "divider",
160+
},
161+
{
162+
type: "section",
163+
block_id: "selector",
164+
text: {
165+
type: "mrkdwn",
166+
text: "Send feedback",
167+
},
168+
accessory: {
169+
action_id: "response",
170+
type: "multi_static_select",
171+
placeholder: {
172+
type: "plain_text",
173+
text: "Select URL",
174+
},
175+
options: [
176+
{
177+
text: {
178+
type: "plain_text",
179+
text: "https://api.github.com",
180+
},
181+
value: "api",
182+
},
183+
{
184+
text: {
185+
type: "plain_text",
186+
text: "https://github.com",
187+
},
188+
value: "server",
189+
},
190+
{
191+
text: {
192+
type: "plain_text",
193+
text: "https://api.github.com/graphql",
194+
},
195+
value: "graphql",
196+
},
197+
],
198+
},
199+
},
200+
],
97201
};
98202
assert.deepEqual(config.content.values, expected);
99203
});
@@ -252,19 +356,147 @@ describe("content", () => {
252356
assert.deepEqual(config.content.values, expected);
253357
});
254358

359+
it("templatizes variables requires configuration", async () => {
360+
mocks.core.getInput.withArgs("payload-file-path").returns("example.json");
361+
mocks.fs.readFileSync
362+
.withArgs(path.resolve("example.json"), "utf-8")
363+
.returns(`{
364+
"message": "this matches an existing variable: \${{ github.apiUrl }}",
365+
"channel": "C0123456789"
366+
}
367+
`);
368+
const config = new Config(mocks.core);
369+
const expected = {
370+
message: "this matches an existing variable: ${{ github.apiUrl }}",
371+
channel: "C0123456789",
372+
};
373+
assert.deepEqual(config.content.values, expected);
374+
});
375+
255376
it("templatizes variables with matching variables", async () => {
256377
mocks.core.getInput.withArgs("payload-file-path").returns("example.json");
257378
mocks.fs.readFileSync
258379
.withArgs(path.resolve("example.json"), "utf-8")
259380
.returns(`{
260-
"message": "Served $\{\{ env.NUMBER }} from $\{\{ github.apiUrl }}"
381+
"channel": "C0123456789",
382+
"reply_broadcast": false,
383+
"message": "Served \${{ env.NUMBER }} items",
384+
"blocks": [
385+
{
386+
"type": "section",
387+
"text": {
388+
"type": "mrkdwn",
389+
"text": "Served \${{ env.NUMBER }} items on: \${{ env.DETAILS }}"
390+
}
391+
},
392+
{
393+
"type": "divider"
394+
},
395+
{
396+
"type": "section",
397+
"block_id": "selector",
398+
"text": {
399+
"type": "mrkdwn",
400+
"text": "Send feedback"
401+
},
402+
"accessory": {
403+
"action_id": "response",
404+
"type": "multi_static_select",
405+
"placeholder": {
406+
"type": "plain_text",
407+
"text": "Select URL"
408+
},
409+
"options": [
410+
{
411+
"text": {
412+
"type": "plain_text",
413+
"text": "\${{ github.apiUrl }}"
414+
},
415+
"value": "api"
416+
},
417+
{
418+
"text": {
419+
"type": "plain_text",
420+
"text": "\${{ github.serverUrl }}"
421+
},
422+
"value": "server"
423+
},
424+
{
425+
"text": {
426+
"type": "plain_text",
427+
"text": "\${{ github.graphqlUrl }}"
428+
},
429+
"value": "graphql"
430+
}
431+
]
432+
}
433+
}
434+
]
261435
}`);
262436
mocks.core.getBooleanInput.withArgs("payload-templated").returns(true);
437+
process.env.DETAILS = `
438+
-fri
439+
-sat
440+
-sun`;
263441
process.env.NUMBER = 12;
264442
const config = new Config(mocks.core);
443+
process.env.DETAILS = undefined;
265444
process.env.NUMBER = undefined;
266445
const expected = {
267-
message: "Served 12 from https://api.github.com",
446+
channel: "C0123456789",
447+
reply_broadcast: false,
448+
message: "Served 12 items",
449+
blocks: [
450+
{
451+
type: "section",
452+
text: {
453+
type: "mrkdwn",
454+
text: "Served 12 items on: \n-fri\n-sat\n-sun",
455+
},
456+
},
457+
{
458+
type: "divider",
459+
},
460+
{
461+
type: "section",
462+
block_id: "selector",
463+
text: {
464+
type: "mrkdwn",
465+
text: "Send feedback",
466+
},
467+
accessory: {
468+
action_id: "response",
469+
type: "multi_static_select",
470+
placeholder: {
471+
type: "plain_text",
472+
text: "Select URL",
473+
},
474+
options: [
475+
{
476+
text: {
477+
type: "plain_text",
478+
text: "https://api.github.com",
479+
},
480+
value: "api",
481+
},
482+
{
483+
text: {
484+
type: "plain_text",
485+
text: "https://github.com",
486+
},
487+
value: "server",
488+
},
489+
{
490+
text: {
491+
type: "plain_text",
492+
text: "https://api.github.com/graphql",
493+
},
494+
value: "graphql",
495+
},
496+
],
497+
},
498+
},
499+
],
268500
};
269501
assert.deepEqual(config.content.values, expected);
270502
});

0 commit comments

Comments
 (0)