Skip to content

Commit

Permalink
Revert "db-schema-builder: some clean up fullstack-build#89"
Browse files Browse the repository at this point in the history
This reverts commit d4f0e42.
  • Loading branch information
davidsparkles committed Jul 26, 2019
1 parent d4f0e42 commit 879cc8a
Show file tree
Hide file tree
Showing 15 changed files with 726 additions and 28 deletions.
25 changes: 0 additions & 25 deletions packages/schema-builder/lib/createGraphqlViews.ts

This file was deleted.

58 changes: 58 additions & 0 deletions packages/schema-builder/lib/db-schema-builder/IMigrationSqlObj.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
export interface IAction {
ignore: boolean;
add: boolean;
remove: boolean;
rename: boolean;
change: boolean;
}

export interface ISqlObj {
up: undefined[];
down: undefined[];
}

export interface IMigrationSqlObj {
version: number;
schemas?: {
[name: string]: {
name: string;
sql: ISqlObj;
tables?: {
[name: string]: {
name: string;
sql: ISqlObj;
columns: {
[name: string]: {
name: string;
sql: ISqlObj;
};
};
constraints?: {
sql: ISqlObj;
};
};
};
views?: {
[name: string]: {
name: string;
sql: ISqlObj;
};
};
};
};
enums?: {
[name: string]: {
name: string;
sql: ISqlObj;
};
};
relations?: {
[name: string]: {
name: string;
sql: ISqlObj;
};
};
crud?: {
sql: ISqlObj;
};
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
const operationMapper = {
CREATE: "INSERT",
READ: "SELECT",
UPDATE: "SELECT, UPDATE",
DELETE: "SELECT, DELETE"
};

export default (dbMeta: any, databaseName: any, applicationUserName: any, includePrivileges: any) => {
const statements = [];
const viewSchemas = {};

if (dbMeta.schemas == null) {
return statements;
}

if (includePrivileges === true) {
statements.push(`REVOKE ALL PRIVILEGES ON DATABASE "${databaseName}" FROM ${applicationUserName};`);
statements.push(`GRANT USAGE ON SCHEMA "_meta" TO ${applicationUserName};`);

// TODO: @Eugene: Move this to versioning.ts
statements.push(`GRANT USAGE ON SCHEMA "_versions" TO ${applicationUserName};`);
statements.push(`GRANT SELECT ON "_meta".plv8_js_modules TO ${applicationUserName};`);

Object.values(dbMeta.schemas).forEach((schema: any) => {
Object.values(schema.tables).forEach((table: any) => {
// statements.push(`REVOKE ALL PRIVILEGES ON "${table.schemaName}"."${table.name}" FROM ${applicationUserName};`);
statements.push(`GRANT SELECT, UPDATE, INSERT, DELETE ON "${table.schemaName}"."A${table.name}" TO ${applicationUserName};`);

// TODO: @Eugene: Move this to versioning.ts
statements.push(`GRANT INSERT ON "_versions"."${table.schemaName}_${table.name}" TO ${applicationUserName};`);
});
});
}

Object.keys(dbMeta.schemas).forEach((schemaName: any) => {
const schema = dbMeta.schemas[schemaName];
if (includePrivileges === true) {
statements.push(`GRANT USAGE ON SCHEMA "${schemaName}" TO ${applicationUserName};`);
}
Object.values(schema.views).forEach((dbView: any) => {
let security = "";
let checkOption = "";
const fieldSelects = dbView.fields.map((field: any) => {
return field.expression;
});

if (true || dbView.operation === "READ") {
security = " WITH (security_barrier)";
}

if (dbView.operation === "CREATE") {
checkOption = " WITH LOCAL CHECK OPTION";
}

viewSchemas[dbView.viewSchemaName] = dbView.viewSchemaName;

// remove and recreate view
statements.push(`DROP VIEW IF EXISTS "${dbView.viewSchemaName}"."${dbView.viewName}";`);
statements.push(`CREATE VIEW "${dbView.viewSchemaName}"."${dbView.viewName}"${security}
AS SELECT ${fieldSelects.join(", ")} FROM "${dbView.schemaName}"."${dbView.tableName}"
WHERE ${dbView.expressions.join(" OR ")}${checkOption};`);

if (includePrivileges === true) {
// statements.push(`REVOKE ALL PRIVILEGES ON "${dbView.name}" FROM ${applicationUserName};`);
statements.push(`GRANT ${operationMapper[dbView.operation]} ON "${dbView.viewSchemaName}"."${dbView.viewName}" TO ${applicationUserName};`);
}
});
});

Object.values(viewSchemas).forEach((schemaName: any) => {
statements.unshift(`CREATE SCHEMA IF NOT EXISTS "${schemaName}";`);
});

return statements;
};
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
// Directive Parser
export interface IDirectiveParser {
[name: string]: (gQlDirectiveNode, dbMetaNode, refDbMeta, refDbMetaCurrentTable, refDbMetaCurrentTableColumn) => void;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
import { IDirectiveParser } from "./IDirectiveParser";

// object with all directive parser
const directiveParser: IDirectiveParser = {};
// register directive parser
export function registerDirectiveParser(
directiveNameInLowerCase: string,
fn: (gQlDirectiveNode, dbMetaNode, refDbMeta, refDbMetaCurrentTable, refDbMetaCurrentTableColumn) => void
): void {
directiveParser[directiveNameInLowerCase] = fn;
}

// return currently registered parser
export function getDirectiveParser(directiveName?: string): IDirectiveParser | any {
return directiveName != null ? directiveParser[directiveName] : directiveParser;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,90 @@
import * as _ from "lodash";
import { setDefaultValueForColumn, addMigration, createConstraint } from "./gQlAstToDbMetaHelper";
import * as utils from "../../gql-schema-builder/utils";
import { registerDirectiveParser } from "./directiveParser";

const { parseDirectiveArguments } = utils;

// ignore table, just make it available
registerDirectiveParser("table", (gQlDirectiveNode, dbMetaNode, refDbMeta, refDbMetaCurrentTable, refDbMetaCurrentTableColumn) => {
// nothing to do here -> has been done in ObjectTypeDefinition
});

// ignore relations, just make it available
registerDirectiveParser("relation", (gQlDirectiveNode, dbMetaNode, refDbMeta, refDbMetaCurrentTable, refDbMetaCurrentTableColumn) => {
// nothing to do here -> has been done during column creation
});

// mark as computed
registerDirectiveParser("computed", (gQlDirectiveNode, dbMetaNode, refDbMeta, refDbMetaCurrentTable, refDbMetaCurrentTableColumn) => {
dbMetaNode.type = "computed";
});

// mark as customResolver
registerDirectiveParser("custom", (gQlDirectiveNode, dbMetaNode, refDbMeta, refDbMetaCurrentTable, refDbMetaCurrentTableColumn) => {
dbMetaNode.type = "customResolver";
});
// add unique constraint
registerDirectiveParser("unique", (gQlDirectiveASTNode, dbMetaNode, refDbMeta, refDbMetaCurrentTable, refDbMetaCurrentTableColumn) => {
let constraintName = `${refDbMetaCurrentTable.name}_${refDbMetaCurrentTableColumn.name}_key`;
let options = {};
// iterate arguments
Object.values(gQlDirectiveASTNode.arguments).forEach((argument: any) => {
switch (argument.name.value) {
case "name":
// named unique constraint - override
const namedConstraintName = argument.value.value;
constraintName = `${refDbMetaCurrentTable.name}_${namedConstraintName}_key`;
break;
case "condition":
options = {
condition: argument.value.value
};
break;
}
});

createConstraint(constraintName, "UNIQUE", options, refDbMeta, refDbMetaCurrentTable, refDbMetaCurrentTableColumn);
});
// native PG check constraint
registerDirectiveParser("check", (gQlDirectiveASTNode, dbMetaNode, refDbMeta, refDbMetaCurrentTable, refDbMetaCurrentTableColumn) => {
// iterate over all constraints
gQlDirectiveASTNode.arguments.forEach((argument) => {
const checkName = argument.name.value;
const constraintName = `${refDbMetaCurrentTable.name}_${checkName}_check`;
const options = {
param1: argument.value.value
};

// create constraint
createConstraint(constraintName, "CHECK", options, refDbMeta, refDbMetaCurrentTable, refDbMetaCurrentTableColumn);
});
});

// embedded json types -> jsonb
registerDirectiveParser("json", (gQlDirectiveNode, dbMetaNode, refDbMeta, refDbMetaCurrentTable, refDbMetaCurrentTableColumn) => {
dbMetaNode.type = "jsonb";
});
// override type with PG native type
registerDirectiveParser("type", (gQlDirectiveNode, dbMetaNode, refDbMeta, refDbMetaCurrentTable, refDbMetaCurrentTableColumn) => {
const customType = _.get(gQlDirectiveNode, "arguments[0].value.value");

if (customType.toLowerCase() === "json") {
dbMetaNode.type = "json";
} else if (customType.toLowerCase() === "jsonb") {
dbMetaNode.type = "jsonb";
} else {
// assume everything unknown by GraphQL is a custom type
dbMetaNode.type = "customType";
dbMetaNode.customType = customType;
}
});
// set default value
registerDirectiveParser("default", (gQlDirectiveNode, dbMetaNode, refDbMeta, refDbMetaCurrentTable, refDbMetaCurrentTableColumn) => {
setDefaultValueForColumn(gQlDirectiveNode, dbMetaNode, refDbMeta, refDbMetaCurrentTable, refDbMetaCurrentTableColumn);
});

// add special migration
registerDirectiveParser("migrate", (gQlDirectiveNode, dbMetaNode, refDbMeta, refDbMetaCurrentTable, refDbMetaCurrentTableColumn) => {
addMigration(gQlDirectiveNode, dbMetaNode, refDbMeta);
});
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
import { IDbMeta } from "../../IDbMeta";
import { PostgresQueryRunner } from "@fullstack-one/db";

export type TQueryParser = (queryRunner: PostgresQueryRunner, dbMeta: IDbMeta) => void;

const queryParsers: TQueryParser[] = [];

export function registerQueryParser(queryParser: TQueryParser): void {
queryParsers.push(queryParser);
}

export function getQueryParser(): TQueryParser[] {
return queryParsers;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
import { IDbMeta } from "../../IDbMeta";

const triggerParser = [];

export function registerTriggerParser(callback: (trigger: any, dbMeta: IDbMeta, schemaName: string, tableName: string) => void): void {
triggerParser.push(callback);
}

// return currently registered parser
export function getTriggerParser(): any {
return triggerParser;
}
35 changes: 35 additions & 0 deletions packages/schema-builder/lib/db-schema-builder/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
import { Service, Inject } from "@fullstack-one/di";
import { LoggerFactory, ILogger } from "@fullstack-one/logger";
import { ORM } from "@fullstack-one/db";

@Service()
export class DbSchemaBuilder {
private readonly logger: ILogger;

constructor(@Inject((type) => LoggerFactory) loggerFactory: LoggerFactory, @Inject((type) => ORM) private readonly orm: ORM) {
this.logger = loggerFactory.create(this.constructor.name);
}

public async createGraphqlViews(graphqlViewSqlStatements: string[]): Promise<void> {
const queryRunner = this.orm.createQueryRunner();

try {
this.logger.trace("migration.begin");
await queryRunner.connect();
await queryRunner.query("BEGIN");

for (const statement of Object.values(graphqlViewSqlStatements)) {
this.logger.trace("migration.view.sql.statement", statement);
await queryRunner.query(statement);
}

this.logger.trace("migration.commit");
await queryRunner.query("COMMIT");
} catch (err) {
this.logger.warn("migration.rollback");
await queryRunner.query("ROLLBACK");
throw err;
}
await queryRunner.release();
}
}
Loading

0 comments on commit 879cc8a

Please sign in to comment.