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

fix(template): allow partially resolved vars in arithmetic expressions #6228

Merged
Merged
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
19 changes: 17 additions & 2 deletions core/src/template-string/template-string.ts
Original file line number Diff line number Diff line change
Expand Up @@ -69,7 +69,16 @@ interface ConditionalTree {
}

function getValue(v: Primitive | undefined | ResolvedClause) {
return isPlainObject(v) ? (<ResolvedClause>v).resolved : v
return isPlainObject(v) ? (v as ResolvedClause).resolved : v
}

function isPartiallyResolved(v: Primitive | undefined | ResolvedClause): boolean {
if (!isPlainObject(v)) {
return false
}

const clause = v as ResolvedClause
return !!clause.partial
}

export class TemplateError extends GardenError {
Expand Down Expand Up @@ -823,10 +832,16 @@ function buildBinaryExpression(head: any, tail: any) {
if (rightRes && rightRes._error) {
return rightRes
}

const left = getValue(leftRes)
const right = getValue(rightRes)

// if any operand is partially resolved, preserve the original expression
const leftResPartial = isPartiallyResolved(leftRes)
const rightResPartial = isPartiallyResolved(rightRes)
if (leftResPartial || rightResPartial) {
return `${left} ${operator} ${right}`
}

// Disallow undefined values for comparisons
if (left === undefined || right === undefined) {
const message = [leftRes, rightRes]
Expand Down
52 changes: 52 additions & 0 deletions core/test/unit/src/template-string.ts
Original file line number Diff line number Diff line change
Expand Up @@ -406,6 +406,58 @@ describe("resolveTemplateString", () => {
})
})

context("partial resolution in binary operators", () => {
context("arithmetic operators", () => {
const arithmeticOperators = ["-", "*", "/", "%", ">", ">=", "<", "<="]
for (const operator of arithmeticOperators) {
describe(`with ${operator} operator`, () => {
it("a missing reference as the first clause returns the original template", () => {
const res = resolveTemplateString({
string: `$\{var.foo ${operator} 2}`,
context: new TestContext({ var: {} }),
contextOpts: { allowPartial: true },
})
expect(res).to.equal(`$\{var.foo ${operator} 2}`)
})

it("a missing reference as the second clause returns the original template", () => {
const res = resolveTemplateString({
string: `$\{2 ${operator} var.foo}`,
context: new TestContext({ var: {} }),
contextOpts: { allowPartial: true },
})
expect(res).to.equal(`$\{2 ${operator} var.foo}`)
})
})
}
})

context("overloaded operators", () => {
const overLoadedOperators = ["+"]
for (const operator of overLoadedOperators) {
describe(`with ${operator} operator`, () => {
it(`a missing reference as the first clause returns the original template`, () => {
const res = resolveTemplateString({
string: `$\{var.foo ${operator} '2'}`,
context: new TestContext({ var: {} }),
contextOpts: { allowPartial: true },
})
expect(res).to.equal(`$\{var.foo ${operator} '2'}`)
})

it("a missing reference as the second clause returns the original template", () => {
const res = resolveTemplateString({
string: `$\{2 ${operator} var.foo}`,
context: new TestContext({ var: {} }),
contextOpts: { allowPartial: true },
})
expect(res).to.equal(`$\{2 ${operator} var.foo}`)
})
})
}
})
})

it("should handle a positive equality comparison between equal resolved values", () => {
const res = resolveTemplateString({ string: "${a == b}", context: new TestContext({ a: "a", b: "a" }) })
expect(res).to.equal(true)
Expand Down