forked from fullstack-build/soniq
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Revert "db-schema-builder: some clean up fullstack-build#89"
This reverts commit d4f0e42.
- Loading branch information
1 parent
d4f0e42
commit 879cc8a
Showing
15 changed files
with
726 additions
and
28 deletions.
There are no files selected for viewing
This file was deleted.
Oops, something went wrong.
58 changes: 58 additions & 0 deletions
58
packages/schema-builder/lib/db-schema-builder/IMigrationSqlObj.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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; | ||
}; | ||
} |
75 changes: 75 additions & 0 deletions
75
packages/schema-builder/lib/db-schema-builder/createViewsFromDbMeta.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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; | ||
}; |
4 changes: 4 additions & 0 deletions
4
packages/schema-builder/lib/db-schema-builder/fromGQl/directiveParser/IDirectiveParser.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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; | ||
} |
16 changes: 16 additions & 0 deletions
16
packages/schema-builder/lib/db-schema-builder/fromGQl/directiveParser/index.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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; | ||
} |
90 changes: 90 additions & 0 deletions
90
packages/schema-builder/lib/db-schema-builder/fromGQl/initialDirectiveParser.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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); | ||
}); |
14 changes: 14 additions & 0 deletions
14
packages/schema-builder/lib/db-schema-builder/fromPg/queryParser/index.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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; | ||
} |
12 changes: 12 additions & 0 deletions
12
packages/schema-builder/lib/db-schema-builder/fromPg/triggerParser/index.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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; | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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(); | ||
} | ||
} |
Oops, something went wrong.