From 0000512ed65ceda9d3db17aa91d8986d59580940 Mon Sep 17 00:00:00 2001 From: Mathieu Arnold Date: Tue, 6 Dec 2022 10:50:07 +0100 Subject: [PATCH 1/2] Add prependSchema and prependSchemaExclude to the configuration --- bin/sequelize-auto | 13 +++++++++++++ src/types.ts | 4 ++++ 2 files changed, 17 insertions(+) diff --git a/bin/sequelize-auto b/bin/sequelize-auto index aaaaaa25..bd4053a1 100755 --- a/bin/sequelize-auto +++ b/bin/sequelize-auto @@ -135,6 +135,17 @@ const argv = require('yargs') type: 'boolean', alias: 'sg' }) + .option('prependSchema', { + description: 'Prepend the schema name to file, class and type names', + type: 'boolean', + alias: 'ps' + }) + .option('prependSchemaExclude', { + description: 'Space-separated schema to not prepend to file, class and type names', + array: true, + type: 'string', + alias: 'pse', + }) .check(argv => Boolean((argv.database && (argv.host || argv.dialect === 'sqlite')) || argv.config)) .argv; @@ -220,6 +231,8 @@ async function readPassword() { configFile.useDefine = argv.useDefine || configFile.useDefine || false; configFile.indentation = argv.indentation || configFile.indentation || 2; configFile.noIndexes = argv.noIndexes || configFile.noIndexes || false; + configFile.prependSchema = argv.prependSchema || configFile.prependSchema || false; + configFile.prependSchemaExclude = argv.prependSchemaExclude || configFile.prependSchemaExclude || []; console.log(_.omit(configFile, 'password')); diff --git a/src/types.ts b/src/types.ts index cb3d52a0..5b74b065 100644 --- a/src/types.ts +++ b/src/types.ts @@ -163,6 +163,10 @@ export interface AutoOptions { password?: string; /** Database port */ port?: number; + /** Prepend schema to file/class/types */ + prependSchema?: boolean; + /** Schemas to not prepend */ + prependSchemaExclude: string[]; /** Database schema to export */ schema?: string; /** Whether to singularize model names */ From 00005130acf38a38e0556b3570a43ad3ef088b05 Mon Sep 17 00:00:00 2001 From: Mathieu Arnold Date: Tue, 6 Dec 2022 10:50:29 +0100 Subject: [PATCH 2/2] Use schema in file, class and types if needed --- src/auto-generator.ts | 10 +++++----- src/auto-relater.ts | 12 ++++++++---- src/auto-writer.ts | 40 ++++++++++++++++++++++------------------ src/types.ts | 15 +++++++++++++++ 4 files changed, 50 insertions(+), 27 deletions(-) diff --git a/src/auto-generator.ts b/src/auto-generator.ts index 9d3b4252..b50a75ee 100644 --- a/src/auto-generator.ts +++ b/src/auto-generator.ts @@ -1,7 +1,7 @@ import _ from "lodash"; import { ColumnDescription } from "sequelize/types"; import { DialectOptions, FKSpec } from "./dialects/dialect-options"; -import { AutoOptions, CaseFileOption, CaseOption, Field, IndexSpec, LangOption, makeIndent, makeTableName, pluralize, qNameJoin, qNameSplit, recase, Relation, singularize, TableData, TSField } from "./types"; +import { AutoOptions, CaseFileOption, CaseOption, Field, fileBaseName, IndexSpec, LangOption, makeIndent, makeTableName, modelBaseName, pluralize, qNameJoin, qNameSplit, recase, Relation, singularize, TableData, TSField } from "./types"; /** Generates text from each table in TableData */ export class AutoGenerator { @@ -25,6 +25,8 @@ export class AutoGenerator { singularize: boolean; useDefine: boolean; noIndexes?: boolean; + prependSchema?: boolean; + prependSchemaExclude: string[]; }; constructor(tableData: TableData, dialect: DialectOptions, options: AutoOptions) { @@ -84,16 +86,14 @@ export class AutoGenerator { const text: { [name: string]: string; } = {}; tableNames.forEach(table => { let str = header; - const [schemaName, tableNameOrig] = qNameSplit(table); - const tableName = makeTableName(this.options.caseModel, tableNameOrig, this.options.singularize, this.options.lang); + const tableName = makeTableName(this.options.caseModel, modelBaseName(table, this.options), this.options.singularize, this.options.lang); if (this.options.lang === 'ts') { const associations = this.addTypeScriptAssociationMixins(table); const needed = _.keys(associations.needed).sort(); needed.forEach(fkTable => { const set = associations.needed[fkTable]; - const [fkSchema, fkTableName] = qNameSplit(fkTable); - const filename = recase(this.options.caseFile, fkTableName, this.options.singularize); + const filename = recase(this.options.caseFile, fileBaseName(fkTable, this.options), this.options.singularize); str += 'import type { '; str += Array.from(set.values()).sort().join(', '); str += ` } from './${filename}';\n`; diff --git a/src/auto-relater.ts b/src/auto-relater.ts index 67a5e898..66487597 100644 --- a/src/auto-relater.ts +++ b/src/auto-relater.ts @@ -2,7 +2,7 @@ import _ from "lodash"; import { AutoOptions } from "."; import { FKSpec } from "./dialects/dialect-options"; -import { CaseOption, qNameJoin, qNameSplit, recase, Relation, TableData, singularize, pluralize } from "./types"; +import { CaseOption, qNameJoin, qNameSplit, recase, Relation, TableData, singularize, pluralize, modelBaseName } from "./types"; /** Constructs entity relationships from TableData.foreignKeys and populates TableData.relations */ export class AutoRelater { @@ -10,6 +10,8 @@ export class AutoRelater { caseProp: CaseOption; singularize: boolean; pkSuffixes: string[]; + prependSchema: boolean; + prependSchemaExclude: string[]; relations: Relation[]; private usedChildNames: Set; @@ -19,6 +21,8 @@ export class AutoRelater { this.caseProp = options.caseProp || 'o'; this.singularize = options.singularize; this.pkSuffixes = options.pkSuffixes || []; + this.prependSchema = options.prependSchema || false; + this.prependSchemaExclude = options.prependSchemaExclude || []; if (!this.pkSuffixes || this.pkSuffixes.length == 0){ this.pkSuffixes = ["id"]; @@ -52,9 +56,9 @@ export class AutoRelater { const [schemaName, tableName] = qNameSplit(table); const schema = schemaName as string; - const modelName = recase(this.caseModel, tableName, this.singularize); + const modelName = recase(this.caseModel, modelBaseName(table, this), this.singularize); - const targetModel = recase(this.caseModel, spec.foreignSources.target_table as string, this.singularize); + const targetModel = recase(this.caseModel, modelBaseName([spec.foreignSources.target_schema, spec.foreignSources.target_table as string], this), this.singularize); const alias = this.getAlias(fkFieldName, spec.foreignSources.target_table as string, spec.foreignSources.source_table as string); const childAlias = this.getChildAlias(fkFieldName, spec.foreignSources.source_table as string, spec.foreignSources.target_table as string); const sourceProp = recase(this.caseProp, fkFieldName); @@ -80,7 +84,7 @@ export class AutoRelater { const otherKeys = _.filter(fkFields, k => k.isForeignKey && k.isPrimaryKey && k.source_column !== fkFieldName); if (otherKeys.length === 1) { const otherKey = otherKeys[0]; - const otherModel = recase(this.caseModel, otherKey.foreignSources.target_table as string, this.singularize); + const otherModel = recase(this.caseModel, modelBaseName([otherKey.foreignSources.target_schema, otherKey.foreignSources.target_table as string], this), this.singularize); const otherProp = this.getAlias(otherKey.source_column, otherKey.foreignSources.target_table as string, otherKey.foreignSources.source_table as string, true); const otherId = recase(this.caseProp, otherKey.source_column); diff --git a/src/auto-writer.ts b/src/auto-writer.ts index 07f279ad..13cd1f85 100644 --- a/src/auto-writer.ts +++ b/src/auto-writer.ts @@ -3,7 +3,7 @@ import _ from "lodash"; import path from "path"; import util from "util"; import { FKSpec, TableData } from "."; -import { AutoOptions, CaseFileOption, CaseOption, LangOption, makeIndent, makeTableName, pluralize, qNameSplit, recase, Relation } from "./types"; +import { AutoOptions, CaseFileOption, CaseOption, fileBaseName, LangOption, makeIndent, makeTableName, modelBaseName, pluralize, qNameSplit, recase, Relation, shouldPrepend } from "./types"; const mkdirp = require('mkdirp'); /** Writes text into files from TableData.text, and writes init-models */ @@ -25,12 +25,14 @@ export class AutoWriter { useDefine?: boolean; spaces?: boolean; indentation?: number; + prependSchema?: boolean; + prependSchemaExclude: string[]; }; constructor(tableData: TableData, options: AutoOptions) { + this.options = options; this.tableText = tableData.text as { [name: string]: string }; this.foreignKeys = tableData.foreignKeys; this.relations = tableData.relations; - this.options = options; this.space = makeIndent(this.options.spaces, this.options.indentation); } @@ -52,16 +54,16 @@ export class AutoWriter { const isTypeScript = this.options.lang === 'ts'; const assoc = this.createAssociations(isTypeScript); - // get table names without schema - // TODO: add schema to model and file names when schema is non-default for the dialect - const tableNames = tables.map(t => { - const [schemaName, tableName] = qNameSplit(t); - return tableName as string; - }).sort(); - // write the init-models file if (!this.options.noInitModels) { - const initString = this.createInitString(tableNames, assoc, this.options.lang); + const sortedTables = this.options.prependSchema + ? tables.sort((a, b) => { return a.localeCompare(b); }) + : tables.sort((a, b) => { + const [_sa, ta] = qNameSplit(a) as [string,string]; + const [_sb, tb] = qNameSplit(b) as [string,string]; + return ta.localeCompare(tb); + }); + const initString = this.createInitString(sortedTables, assoc, this.options.lang); const initFilePath = path.join(this.options.directory, "init-models" + (isTypeScript ? '.ts' : '.js')); const writeFile = util.promisify(fs.writeFile); const initPromise = writeFile(path.resolve(initFilePath), initString); @@ -82,12 +84,14 @@ export class AutoWriter { return this.createES5InitString(tableNames, assoc, "var"); } } + private modelBaseName(table: string) { + return makeTableName(this.options.caseModel, modelBaseName(table, this.options), this.options.singularize, this.options.lang) + } private createFile(table: string) { // FIXME: schema is not used to write the file name and there could be collisions. For now it // is up to the developer to pick the right schema, and potentially chose different output // folders for each different schema. - const [schemaName, tableName] = qNameSplit(table); - const fileName = recase(this.options.caseFile, tableName, this.options.singularize); + const fileName = recase(this.options.caseFile, fileBaseName(table, this.options), this.options.singularize); const filePath = path.join(this.options.directory, fileName + (this.options.lang === 'ts' ? '.ts' : '.js')); const writeFile = util.promisify(fs.writeFile); @@ -130,8 +134,8 @@ export class AutoWriter { const modelNames: string[] = []; // import statements tables.forEach(t => { - const fileName = recase(this.options.caseFile, t, this.options.singularize); - const modelName = makeTableName(this.options.caseModel, t, this.options.singularize, this.options.lang); + const fileName = recase(this.options.caseFile, fileBaseName(t, this.options), this.options.singularize); + const modelName = this.modelBaseName(t); modelNames.push(modelName); str += `import { ${modelName} as _${modelName} } from "./${fileName}";\n`; str += `import type { ${modelName}Attributes, ${modelName}CreationAttributes } from "./${fileName}";\n`; @@ -178,8 +182,8 @@ export class AutoWriter { const modelNames: string[] = []; // import statements tables.forEach(t => { - const fileName = recase(this.options.caseFile, t, this.options.singularize); - const modelName = makeTableName(this.options.caseModel, t, this.options.singularize, this.options.lang); + const fileName = recase(this.options.caseFile, fileBaseName(t, this.options), this.options.singularize); + const modelName = this.modelBaseName(t); modelNames.push(modelName); str += `${vardef} _${modelName} = require("./${fileName}");\n`; }); @@ -214,8 +218,8 @@ export class AutoWriter { const modelNames: string[] = []; // import statements tables.forEach(t => { - const fileName = recase(this.options.caseFile, t, this.options.singularize); - const modelName = makeTableName(this.options.caseModel, t, this.options.singularize, this.options.lang); + const fileName = recase(this.options.caseFile, fileBaseName(t, this.options), this.options.singularize); + const modelName = this.modelBaseName(t); modelNames.push(modelName); str += `import _${modelName} from "./${fileName}.js";\n`; }); diff --git a/src/types.ts b/src/types.ts index 5b74b065..969e7ba7 100644 --- a/src/types.ts +++ b/src/types.ts @@ -102,6 +102,21 @@ export function qNameSplit(qname: string) { return [null, qname]; } + +export function shouldPrepend (schema: string|undefined|null, options: { prependSchema?: boolean; prependSchemaExclude: string[]; }) { + return schema != null && options.prependSchema && !options.prependSchemaExclude.includes(schema); +} + +export function modelBaseName(table: string|[string|null|undefined, string], options: { prependSchema?: boolean; prependSchemaExclude: string[]; } ) { + const [schemaName, tableName] = Array.isArray(table) ? table : qNameSplit(table) as [string|null, string]; + return shouldPrepend(schemaName, options) ? schemaName+'_'+tableName : tableName; +}; + +export function fileBaseName(table: string|[string|null|undefined, string], options: { prependSchema?: boolean; prependSchemaExclude: string[]; }) { + const [schemaName, tableName] = Array.isArray(table) ? table : qNameSplit(table) as [string|null, string]; + return shouldPrepend(schemaName, options) ? schemaName+'_'+tableName : tableName; +} + /** Get combined schema.table name */ export function qNameJoin(schema: string | undefined, table: string | undefined) { return !!schema ? schema + "." + table : table as string;