diff --git a/cli/api/commands/build.ts b/cli/api/commands/build.ts index 8d0c59688..db3dd4a2c 100644 --- a/cli/api/commands/build.ts +++ b/cli/api/commands/build.ts @@ -62,7 +62,8 @@ export class Builder { ) ), this.prunedGraph.operations.map(o => this.buildOperation(o)), - this.prunedGraph.assertions.map(a => this.buildAssertion(a)) + this.prunedGraph.assertions.map(a => this.buildAssertion(a)), + this.prunedGraph.propertyGraphs.map(g => this.buildPropertyGraph(g)) ); return dataform.ExecutionGraph.create({ projectConfig: this.prunedGraph.projectConfig, @@ -106,17 +107,31 @@ export class Builder { }; } + private buildPropertyGraph(propertyGraph: dataform.IPropertyGraph) { + return { + ...this.toPartialExecutionAction(propertyGraph), + type: "propertyGraph", + tasks: this.executionSql.createPropertyGraphTasks(propertyGraph) + }; + } + private toPartialExecutionAction( - action: dataform.ITable | dataform.IOperation | dataform.IAssertion + action: + | dataform.ITable + | dataform.IOperation + | dataform.IAssertion + | dataform.IPropertyGraph ) { const jitCode = "jitCode" in action ? action.jitCode : undefined; + const actionDescriptor = "actionDescriptor" in action ? action.actionDescriptor : undefined; + const disabled = !!action.disabled; const executionAction = dataform.ExecutionAction.create({ target: action.target, fileName: action.fileName, dependencyTargets: action.dependencyTargets, - actionDescriptor: action.actionDescriptor + actionDescriptor }); - if (jitCode && !action.disabled) { + if (jitCode && !disabled) { executionAction.jitCode = jitCode; } return executionAction; diff --git a/cli/api/dbadapters/execution_sql.ts b/cli/api/dbadapters/execution_sql.ts index 7e048a840..b0724cbcd 100644 --- a/cli/api/dbadapters/execution_sql.ts +++ b/cli/api/dbadapters/execution_sql.ts @@ -184,6 +184,15 @@ from (${query}) as insertions`; ); } + public createPropertyGraphTasks( + propertyGraph: dataform.IPropertyGraph + ): dataform.IExecutionTask[] { + const statement = + `CREATE OR REPLACE PROPERTY GRAPH ${this.resolveTarget(propertyGraph.target)} ` + + `${propertyGraph.graphBody}`; + return [dataform.ExecutionTask.create({ type: "statement", statement })]; + } + public createAssertionTasks(assertion: dataform.IAssertion): dataform.IExecutionTask[] { return assertion.disabled ? [] : this.assertTasks(assertion, this.project).build(); } diff --git a/cli/api/execution_sql_test.ts b/cli/api/execution_sql_test.ts index fbb87fe1c..07b1a0e71 100644 --- a/cli/api/execution_sql_test.ts +++ b/cli/api/execution_sql_test.ts @@ -117,3 +117,51 @@ suite("ExecutionSql with 'onSchemaChange'", () => { expect(sql).to.equal(expectedSql.trim()); }); }); + +suite("ExecutionSql for property graphs", () => { + const executionSql = new ExecutionSql( + { + defaultDatabase: "project-id", + defaultSchema: "dataset-id" + }, + "2.0.0", + () => "test_uuid" + ); + + test("emits CREATE OR REPLACE PROPERTY GRAPH for FinGraph", () => { + const graphBody = `NODE TABLES ( + \`project-id.dataset-id.account\` AS Account KEY (id) LABEL Account PROPERTIES ARE ALL COLUMNS, + \`project-id.dataset-id.person\` AS Person KEY (id) LABEL Person PROPERTIES ARE ALL COLUMNS +) +EDGE TABLES ( + \`project-id.dataset-id.person_own_account\` AS PersonOwnAccount SOURCE KEY (id) REFERENCES Person (id) DESTINATION KEY (account_id) REFERENCES Account (id) LABEL Owns PROPERTIES ARE ALL COLUMNS, + \`project-id.dataset-id.account_transfer_account\` AS AccountTransferAccount SOURCE KEY (id) REFERENCES Account (id) DESTINATION KEY (to_id) REFERENCES Account (id) LABEL Transfers PROPERTIES (amount, create_time) +)`; + const propertyGraph: dataform.IPropertyGraph = { + target: { database: "project-id", schema: "dataset-id", name: "FinGraph" }, + graphBody + }; + const tasks = executionSql.createPropertyGraphTasks(propertyGraph); + const sql = tasks.map(t => t.statement).join("\n;\n"); + const expectedSql = fs.readFileSync("cli/api/goldens/property_graph_fingraph.sql", "utf8"); + expect(sql).to.equal(expectedSql.trim()); + }); + + test("emits CREATE OR REPLACE PROPERTY GRAPH for HRGraph", () => { + const graphBody = `NODE TABLES ( + \`project-id.dataset-id.employee\` AS Employee KEY (id) LABEL Employee PROPERTIES ARE ALL COLUMNS EXCEPT (ssn, salary), + \`project-id.dataset-id.manager\` AS Manager KEY (id) LABEL Manager PROPERTIES ARE ALL COLUMNS EXCEPT (bonus) +) +EDGE TABLES ( + \`project-id.dataset-id.reports\` AS Reports SOURCE KEY (employee_id) REFERENCES Employee (id) DESTINATION KEY (manager_id) REFERENCES Manager (id) LABEL Reports PROPERTIES ARE ALL COLUMNS +)`; + const propertyGraph: dataform.IPropertyGraph = { + target: { database: "project-id", schema: "dataset-id", name: "HRGraph" }, + graphBody + }; + const tasks = executionSql.createPropertyGraphTasks(propertyGraph); + const sql = tasks.map(t => t.statement).join("\n;\n"); + const expectedSql = fs.readFileSync("cli/api/goldens/property_graph_hrgraph.sql", "utf8"); + expect(sql).to.equal(expectedSql.trim()); + }); +}); diff --git a/cli/api/goldens/property_graph_fingraph.sql b/cli/api/goldens/property_graph_fingraph.sql new file mode 100644 index 000000000..5daab5e7d --- /dev/null +++ b/cli/api/goldens/property_graph_fingraph.sql @@ -0,0 +1,8 @@ +CREATE OR REPLACE PROPERTY GRAPH `project-id.dataset-id.FinGraph` NODE TABLES ( + `project-id.dataset-id.account` AS Account KEY (id) LABEL Account PROPERTIES ARE ALL COLUMNS, + `project-id.dataset-id.person` AS Person KEY (id) LABEL Person PROPERTIES ARE ALL COLUMNS +) +EDGE TABLES ( + `project-id.dataset-id.person_own_account` AS PersonOwnAccount SOURCE KEY (id) REFERENCES Person (id) DESTINATION KEY (account_id) REFERENCES Account (id) LABEL Owns PROPERTIES ARE ALL COLUMNS, + `project-id.dataset-id.account_transfer_account` AS AccountTransferAccount SOURCE KEY (id) REFERENCES Account (id) DESTINATION KEY (to_id) REFERENCES Account (id) LABEL Transfers PROPERTIES (amount, create_time) +) diff --git a/cli/api/goldens/property_graph_hrgraph.sql b/cli/api/goldens/property_graph_hrgraph.sql new file mode 100644 index 000000000..61e5d9bea --- /dev/null +++ b/cli/api/goldens/property_graph_hrgraph.sql @@ -0,0 +1,7 @@ +CREATE OR REPLACE PROPERTY GRAPH `project-id.dataset-id.HRGraph` NODE TABLES ( + `project-id.dataset-id.employee` AS Employee KEY (id) LABEL Employee PROPERTIES ARE ALL COLUMNS EXCEPT (ssn, salary), + `project-id.dataset-id.manager` AS Manager KEY (id) LABEL Manager PROPERTIES ARE ALL COLUMNS EXCEPT (bonus) +) +EDGE TABLES ( + `project-id.dataset-id.reports` AS Reports SOURCE KEY (employee_id) REFERENCES Employee (id) DESTINATION KEY (manager_id) REFERENCES Manager (id) LABEL Reports PROPERTIES ARE ALL COLUMNS +)