Skip to content

Allow prepending the schema name #630

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 2 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 13 additions & 0 deletions bin/sequelize-auto
Original file line number Diff line number Diff line change
Expand Up @@ -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;

Expand Down Expand Up @@ -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'));

Expand Down
10 changes: 5 additions & 5 deletions src/auto-generator.ts
Original file line number Diff line number Diff line change
@@ -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 {
Expand All @@ -25,6 +25,8 @@ export class AutoGenerator {
singularize: boolean;
useDefine: boolean;
noIndexes?: boolean;
prependSchema?: boolean;
prependSchemaExclude: string[];
};

constructor(tableData: TableData, dialect: DialectOptions, options: AutoOptions) {
Expand Down Expand Up @@ -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`;
Expand Down
12 changes: 8 additions & 4 deletions src/auto-relater.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,14 +2,16 @@
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 {
caseModel: CaseOption;
caseProp: CaseOption;
singularize: boolean;
pkSuffixes: string[];
prependSchema: boolean;
prependSchemaExclude: string[];

relations: Relation[];
private usedChildNames: Set<string>;
Expand All @@ -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"];
Expand Down Expand Up @@ -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);
Expand All @@ -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);

Expand Down
40 changes: 22 additions & 18 deletions src/auto-writer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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 */
Expand All @@ -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);
}

Expand All @@ -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);
Expand All @@ -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);
Expand Down Expand Up @@ -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`;
Expand Down Expand Up @@ -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`;
});
Expand Down Expand Up @@ -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`;
});
Expand Down
19 changes: 19 additions & 0 deletions src/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -163,6 +178,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 */
Expand Down