Skip to content

Commit

Permalink
fix(template): allow partially resolved vars in arithmetic expressions (
Browse files Browse the repository at this point in the history
#6228)

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

* test(template): add test-cases
  • Loading branch information
vvagaytsev authored Jun 25, 2024
1 parent f12c8ab commit 8d85a1a
Show file tree
Hide file tree
Showing 2 changed files with 69 additions and 2 deletions.
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

0 comments on commit 8d85a1a

Please sign in to comment.