From 30aa32adf7f0859119a7c7568f670236035b44d7 Mon Sep 17 00:00:00 2001 From: Matthew Orford Date: Sun, 28 Jun 2026 22:46:59 -0600 Subject: [PATCH 1/4] feat: support YAML access policy comparisons --- .../src/parser/PythonParser.ts | 23 ++++++++++++++++ .../test/unit/yaml-schema.test.ts | 27 +++++++++++++++++++ 2 files changed, 50 insertions(+) diff --git a/packages/cubejs-schema-compiler/src/parser/PythonParser.ts b/packages/cubejs-schema-compiler/src/parser/PythonParser.ts index 572509f78e696..dd3156bd576b0 100644 --- a/packages/cubejs-schema-compiler/src/parser/PythonParser.ts +++ b/packages/cubejs-schema-compiler/src/parser/PythonParser.ts @@ -24,6 +24,9 @@ import Python3Parser, { And_testContext, // eslint-disable-next-line camelcase Or_testContext, + ComparisonContext, + // eslint-disable-next-line camelcase + Comp_opContext, } from './Python3Parser'; import { UserError } from '../compiler/UserError'; import Python3ParserVisitor from './Python3ParserVisitor'; @@ -244,6 +247,26 @@ export class PythonParser { return children[0]; } return children.reduce((left, right) => t.logicalExpression('||', left, right)); + } else if (node instanceof Comp_opContext) { + if (node.EQUALS()) { + return '==='; + } else if (node.NOT_EQ_2()) { + return '!=='; + } + throw new UserError(`Unsupported Python comparison operator: ${node.getText()}`); + } else if (node instanceof ComparisonContext) { + if (children.length === 1) { + return children[0]; + } + + const comparisons: t.BinaryExpression[] = []; + for (let i = 1; i < children.length; i += 2) { + comparisons.push(t.binaryExpression(children[i], children[i - 1], children[i + 1])); + } + return comparisons.slice(1).reduce( + (left, right) => t.logicalExpression('&&', left, right), + comparisons[0] + ); } else if (node instanceof ArglistContext) { return children; } else { diff --git a/packages/cubejs-schema-compiler/test/unit/yaml-schema.test.ts b/packages/cubejs-schema-compiler/test/unit/yaml-schema.test.ts index 265c1c93a43e4..112b2357b7881 100644 --- a/packages/cubejs-schema-compiler/test/unit/yaml-schema.test.ts +++ b/packages/cubejs-schema-compiler/test/unit/yaml-schema.test.ts @@ -839,6 +839,33 @@ cubes: }); describe('Access policy: ', () => { + it('supports equality and inequality operators in conditions', async () => { + const compilers = prepareYamlCompiler( + ` + cubes: + - name: Orders + sql: "select * from tbl" + measures: + - name: count + type: count + accessPolicy: + - role: admin + conditions: + - if: "{ securityContext.region == 'EMEA' }" + - if: "{ securityContext.department != 'Sales' }" + ` + ); + + await compilers.compiler.compile(); + + const conditions = compilers.cubeEvaluator.cubeFromPath('Orders').accessPolicy![0].conditions!; + expect(conditions[0].if.toString()).toContain('securityContext.region === "EMEA"'); + expect(conditions[1].if.toString()).toContain('securityContext.department !== "Sales"'); + expect(conditions[0].if({ region: 'EMEA' })).toBe(true); + expect(conditions[1].if({ department: 'Sales' })).toBe(false); + expect(conditions[1].if({ department: 'Marketing' })).toBe(true); + }); + it('defines a correct accessPolicy', async () => { const { compiler } = prepareYamlCompiler( ` From 58d574b29ca43686639eb08141799e79437f03b5 Mon Sep 17 00:00:00 2001 From: Matthew Orford Date: Sun, 28 Jun 2026 22:58:02 -0600 Subject: [PATCH 2/4] update docs --- .../reference/data-modeling/data-access-policies.mdx | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/docs-mintlify/reference/data-modeling/data-access-policies.mdx b/docs-mintlify/reference/data-modeling/data-access-policies.mdx index 06cee4fb4dab4..f4251134a3b39 100644 --- a/docs-mintlify/reference/data-modeling/data-access-policies.mdx +++ b/docs-mintlify/reference/data-modeling/data-access-policies.mdx @@ -214,7 +214,8 @@ The `if` expression must evaluate to a boolean. The syntax you can use inside it depends on the data model format: - In **YAML** data models, the expression inside `{ }` is evaluated as a Python -expression. Only logical operators, member access, and method calls are supported. +expression. Logical operators, equality and inequality, member access, and method +calls are supported. - In **JavaScript** data models, the expression is evaluated as a JavaScript expression, so a broader set of operators is available. @@ -227,7 +228,7 @@ The following operators and constructs are supported: | Logical NOT | `not` | `!` | | Member access | `.` | `.` | | Method call (e.g., `includes`) | `.includes(…)` | `.includes(…)` | -| Equality and inequality | — | `===`, `!==`, `==`, `!=` | +| Equality and inequality | `==`, `!=` | `===`, `!==`, `==`, `!=` | | Comparison | — | `<`, `>`, `<=`, `>=` | | Arithmetic | — | `+`, `-`, `*`, `/`, `%`, `**` | | Ternary | — | `? :` | From 4d006b9ee2110a4d68c07d51d3e5e572d09e6815 Mon Sep 17 00:00:00 2001 From: Matthew Orford Date: Sun, 28 Jun 2026 23:12:15 -0600 Subject: [PATCH 3/4] temp not support chained comp --- .../src/parser/PythonParser.ts | 11 ++---- .../test/unit/yaml-schema.test.ts | 38 +++++++++++++++++++ 2 files changed, 41 insertions(+), 8 deletions(-) diff --git a/packages/cubejs-schema-compiler/src/parser/PythonParser.ts b/packages/cubejs-schema-compiler/src/parser/PythonParser.ts index dd3156bd576b0..b56a23077e48d 100644 --- a/packages/cubejs-schema-compiler/src/parser/PythonParser.ts +++ b/packages/cubejs-schema-compiler/src/parser/PythonParser.ts @@ -258,15 +258,10 @@ export class PythonParser { if (children.length === 1) { return children[0]; } - - const comparisons: t.BinaryExpression[] = []; - for (let i = 1; i < children.length; i += 2) { - comparisons.push(t.binaryExpression(children[i], children[i - 1], children[i + 1])); + if (children.length !== 3) { + throw new UserError('Chained Python comparisons are not supported'); } - return comparisons.slice(1).reduce( - (left, right) => t.logicalExpression('&&', left, right), - comparisons[0] - ); + return t.binaryExpression(children[1], children[0], children[2]); } else if (node instanceof ArglistContext) { return children; } else { diff --git a/packages/cubejs-schema-compiler/test/unit/yaml-schema.test.ts b/packages/cubejs-schema-compiler/test/unit/yaml-schema.test.ts index 112b2357b7881..20d142273b685 100644 --- a/packages/cubejs-schema-compiler/test/unit/yaml-schema.test.ts +++ b/packages/cubejs-schema-compiler/test/unit/yaml-schema.test.ts @@ -866,6 +866,44 @@ cubes: expect(conditions[1].if({ department: 'Marketing' })).toBe(true); }); + it('does not support chained comparisons in conditions', async () => { + const { compiler } = prepareYamlCompiler( + ` + cubes: + - name: Orders + sql: "select * from tbl" + measures: + - name: count + type: count + accessPolicy: + - role: admin + conditions: + - if: "{ securityContext.region == securityContext.department == 'Sales' }" + ` + ); + + await expect(compiler.compile()).rejects.toThrow('Chained Python comparisons are not supported'); + }); + + it.each(['===', '!=='])('does not support JavaScript operator %s in conditions', async (operator) => { + const { compiler } = prepareYamlCompiler( + ` + cubes: + - name: Orders + sql: "select * from tbl" + measures: + - name: count + type: count + accessPolicy: + - role: admin + conditions: + - if: "{ securityContext.region ${operator} 'EMEA' }" + ` + ); + + await expect(compiler.compile()).rejects.toThrow('Failed to parse Python expression'); + }); + it('defines a correct accessPolicy', async () => { const { compiler } = prepareYamlCompiler( ` From f6ac301e028c763239713a91c4b1958a67815a7e Mon Sep 17 00:00:00 2001 From: Matthew Orford Date: Sun, 28 Jun 2026 23:21:14 -0600 Subject: [PATCH 4/4] note chained comp omission in docs --- docs-mintlify/reference/data-modeling/data-access-policies.mdx | 3 +++ 1 file changed, 3 insertions(+) diff --git a/docs-mintlify/reference/data-modeling/data-access-policies.mdx b/docs-mintlify/reference/data-modeling/data-access-policies.mdx index f4251134a3b39..24b63e1a8648d 100644 --- a/docs-mintlify/reference/data-modeling/data-access-policies.mdx +++ b/docs-mintlify/reference/data-modeling/data-access-policies.mdx @@ -234,6 +234,9 @@ The following operators and constructs are supported: | Ternary | — | `? :` | | Optional chaining | — | `?.` | +Chained comparisons aren't supported in YAML. Write `a == b and b == c` instead +of `a == b == c`. + For example, the following policy applies to users who are not blocked, are either administrators or based in the EMEA region, and belong to the `admins` group. Each requirement is expressed as a separate condition, and all conditions must be `true`