Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: Support CloudFormation Join & Sub function in environment variable #1842

Open
wants to merge 1 commit into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 3 additions & 2 deletions src/lambda/LambdaFunction.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import { setTimeout } from "node:timers/promises"
import { emptyDir, ensureDir, remove } from "fs-extra"
import jszip from "jszip"
import { log } from "../utils/log.js"
import renderIntrinsicFunction from "../utils/renderIntrinsicFunction.js"
import HandlerRunner from "./handler-runner/index.js"
import LambdaContext from "./LambdaContext.js"
import {
Expand Down Expand Up @@ -114,8 +115,8 @@ export default class LambdaFunction {
entries(process.env).filter(([key]) => key.startsWith("AWS_")),
)),
...this.#getAwsEnvVars(),
...provider.environment,
...functionDefinition.environment,
...renderIntrinsicFunction(provider.environment),
...renderIntrinsicFunction(functionDefinition.environment),
IS_OFFLINE: "true",
}

Expand Down
80 changes: 80 additions & 0 deletions src/utils/__tests__/renderIntrinsicFunction.test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
import assert from "node:assert"
import renderIntrinsicFunction from "../renderIntrinsicFunction.js"

describe("renderIntrinsicFunction", () => {
it("should process Fn::Join correctly", () => {
const input = { "Fn::Join": [":", ["a", "b", "c"]] }
const result = renderIntrinsicFunction(input)
assert.strictEqual(result, "a:b:c")
})

it("should process !Join correctly", () => {
const input = { "!Join": [":", ["a", "b", "c"]] }
const result = renderIntrinsicFunction(input)
assert.strictEqual(result, "a:b:c")
})

it("should process Fn::Sub correctly", () => {
const input = {
"Fn::Sub": ["The name is ${name}", { name: "CloudFormation" }], // eslint-disable-line no-template-curly-in-string
}
const result = renderIntrinsicFunction(input)
assert.strictEqual(result, "The name is CloudFormation")
})

it("should process !Sub correctly", () => {
const input = { "!Sub": ["Hello ${name}", { name: "World" }] } // eslint-disable-line no-template-curly-in-string
const result = renderIntrinsicFunction(input)
assert.strictEqual(result, "Hello World")
})

it("should process nested Join correctly", () => {
const input = {
"Fn::Join": ["-", [{ "!Join": [":", ["a", "b", "c"]] }, "d"]],
}
const result = renderIntrinsicFunction(input)
assert.strictEqual(result, "a:b:c-d")
})

it("should process plain strings without modification", () => {
const input = "This is a plain string"
const result = renderIntrinsicFunction(input)
assert.strictEqual(result, "This is a plain string")
})

it("should process numbers without modification", () => {
const input = 42
const result = renderIntrinsicFunction(input)
assert.strictEqual(result, 42)
})

it("should process arrays without intrinsic functions", () => {
const input = ["a", "b", "c"]
const result = renderIntrinsicFunction(input)
assert.deepStrictEqual(result, ["a", "b", "c"])
})

it("should process nested intrinsic functions in arrays", () => {
const input = ["a", { "Fn::Join": [":", ["b", "c"]] }, "d"]
const result = renderIntrinsicFunction(input)
assert.deepStrictEqual(result, ["a", "b:c", "d"])
})

it("should throw an error for unsupported intrinsic functions", () => {
const input = { "Fn::UnsupportedFunction": "Value" }
const result = renderIntrinsicFunction(input)
assert.deepStrictEqual(result, { "Fn::UnsupportedFunction": "Value" })
})

it("should throw an error for unsupported shorthand intrinsic functions", () => {
const input = { "!UnsupportedFunction": "Value" }
const result = renderIntrinsicFunction(input)
assert.deepStrictEqual(result, { "!UnsupportedFunction": "Value" })
})

it("should process nested arrays with intrinsic functions", () => {
const input = ["a", ["b", { "!Join": [":", ["c", "d"]] }]]
const result = renderIntrinsicFunction(input)
assert.deepStrictEqual(result, ["a", ["b", "c:d"]])
})
})
34 changes: 34 additions & 0 deletions src/utils/renderIntrinsicFunction.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
export default function renderIntrinsicFunction(input) {
if (input === null || input === undefined) {
return input
}

if (typeof input === "string") {
return input
}

if (Array.isArray(input)) {
return input.map(renderIntrinsicFunction)
}

if (typeof input === "object") {
const result = {}
for (const [key, value] of Object.entries(input)) {
if (key === "Fn::Join" || key === "!Join") {
const [delimiter, list] = value
return list.map(renderIntrinsicFunction).join(delimiter)
}
if (key === "Fn::Sub" || key === "!Sub") {
const [template, variables] = value
result[key] = template.replaceAll(/\${(.*?)}/g, (match, variable) => {
return variable in variables ? variables[variable] : match
})
return result[key]
}
result[key] = renderIntrinsicFunction(value)
}
return result
}

return input
}
Loading