dbmodel 0.1.1 → 5.2.2-alpha.11

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,210 +0,0 @@
1
- const _ = require('lodash');
2
- const query = require('./query');
3
-
4
- function extractCamelCaseResult(result) {
5
- return result.map(x => _.mapKeys(x, (v, k) => _.camelCase(k)));
6
- }
7
-
8
- async function loadColumnsByTablesFromDatabase(options) {
9
- const result = await query(
10
- options,
11
- `
12
- select c.name as column_name, t.name as type_name, c.object_id, c.is_identity,
13
- c.max_length, c.precision, c.scale, c.is_nullable,
14
- d.definition as default_value, d.name as default_constraint,
15
- m.definition as computed_expression, m.is_persisted, c.column_id,
16
- o.name as table_name, ic.character_maximum_length
17
- from sys.columns c
18
- inner join sys.types t on c.system_type_id = t.system_type_id and c.user_type_id = t.user_type_id
19
- inner join sys.objects o on c.object_id = o.object_id
20
- inner join sys.schemas u on u.schema_id = o.schema_id
21
- left join sys.default_constraints d on c.default_object_id = d.object_id
22
- left join sys.computed_columns m on m.object_id = c.object_id and m.column_id = c.column_id
23
- inner join information_schema.columns ic on ic.table_name = o.name and ic.column_name = c.name and ic.table_schema='dbo'
24
- where o.type = 'U' and u.name = 'dbo'
25
- order by c.column_id
26
- `
27
- );
28
- const columns = extractCamelCaseResult(result);
29
- return _.groupBy(columns, 'tableName');
30
- }
31
-
32
- async function loadIndexesByTablesFromDatabase(options) {
33
- const result = await query(
34
- options,
35
- `select
36
- i.object_id,
37
- i.name as index_name,
38
- i.type_desc,
39
- i.is_unique,
40
- i.index_id,
41
- i.is_unique_constraint,
42
- i.filter_definition,
43
- c.name as column_name,
44
- ic.key_ordinal,
45
- ic.is_included_column,
46
- o.name as table_name
47
- from sys.indexes i
48
- inner join sys.index_columns ic on i.index_id = ic.index_id and i.object_id = ic.object_id
49
- inner join sys.columns c ON c.object_id = i.object_id and c.column_id = ic.column_id
50
- inner join sys.objects o ON o.object_id = i.object_id
51
- where i.is_primary_key=0
52
- and i.is_hypothetical=0 and indexproperty(i.object_id, i.name, 'IsStatistics') = 0
53
- and objectproperty(i.object_id, 'IsUserTable') = 1
54
- and i.index_id between 1 and 254
55
- order by ic.key_ordinal`
56
- );
57
- const columns = extractCamelCaseResult(result);
58
- const columnsByTable = _.groupBy(columns, 'tableName');
59
- const indexesByTable = _.mapValues(columnsByTable, cols => _.groupBy(cols, 'indexName'));
60
- const indexesByTable2 = _.mapValues(indexesByTable, tableIndexes =>
61
- _.mapValues(tableIndexes, columns => ({
62
- ..._.pick(columns[0], ['tableName', 'indexName', 'isUnique', 'filterDefinition']),
63
- columns: columns.map(x => _.pick(x, ['columnName', 'isIncludedColumn'])),
64
- }))
65
- );
66
- return indexesByTable2;
67
- }
68
-
69
- async function loadViewsFromDatabase(options) {
70
- const result = await query(options, `select * from information_schema.views where TABLE_SCHEMA = 'dbo'`);
71
- return _.mapValues(_.keyBy(extractCamelCaseResult(result), 'tableName'), 'viewDefinition');
72
- }
73
-
74
- async function loadRoutinesFromDatabase(options) {
75
- const result = await query(
76
- options,
77
- `select s.name as ROUTINE_NAME, u.name as ROUTINE_SCHEMA, c.text AS ROUTINE_DEFINITION, LTRIM(RTRIM(s.type)) AS ROUTINE_TYPE
78
- from sys.objects s
79
- inner join sys.syscomments c on s.object_id = c.id
80
- inner join sys.schemas u on u.schema_id = s.schema_id
81
- where u.name='dbo'
82
- order by u.name, s.name, c.colid
83
- `
84
- );
85
- const camelCaseResult = extractCamelCaseResult(result);
86
- const functionsArray = camelCaseResult.filter(
87
- x => x.routineType == 'IF' || x.routineType == 'FN' || x.routineType == 'TF'
88
- );
89
- const proceduresArray = camelCaseResult.filter(x => x.routineType == 'P');
90
- return {
91
- functions: _.mapValues(_.groupBy(functionsArray, 'routineName'), x => x.map(y => y.routineDefinition).join('')),
92
- procedures: _.mapValues(_.groupBy(proceduresArray, 'routineName'), x => x.map(y => y.routineDefinition).join('')),
93
- };
94
- }
95
-
96
- async function loadReferencesFromDatabase(options) {
97
- const result = await query(
98
- options,
99
- `SELECT
100
- fkSchema = FK.TABLE_SCHEMA,
101
- fkTable = FK.TABLE_NAME,
102
- fkColumn = CU.COLUMN_NAME,
103
-
104
- pkSchema = PK.TABLE_SCHEMA,
105
- pkTable = PK.TABLE_NAME,
106
-
107
- constraintName = C.CONSTRAINT_NAME
108
-
109
- FROM INFORMATION_SCHEMA.REFERENTIAL_CONSTRAINTS C
110
- INNER JOIN INFORMATION_SCHEMA.TABLE_CONSTRAINTS FK
111
- ON C.CONSTRAINT_NAME = FK.CONSTRAINT_NAME AND c.CONSTRAINT_SCHEMA = FK.CONSTRAINT_SCHEMA
112
- LEFT JOIN INFORMATION_SCHEMA.TABLE_CONSTRAINTS PK
113
- ON C.UNIQUE_CONSTRAINT_NAME = PK.CONSTRAINT_NAME AND c.UNIQUE_CONSTRAINT_SCHEMA = PK.CONSTRAINT_SCHEMA
114
- LEFT JOIN INFORMATION_SCHEMA.KEY_COLUMN_USAGE CU
115
- ON C.CONSTRAINT_NAME = CU.CONSTRAINT_NAME AND C.CONSTRAINT_SCHEMA = CU.CONSTRAINT_SCHEMA
116
- WHERE c.CONSTRAINT_SCHEMA = 'dbo'
117
- `
118
- );
119
- return result;
120
- }
121
-
122
- async function loadPrimaryKeysFromDatabase(options) {
123
- const result = await query(
124
- options,
125
- `select key_column_usage.table_name, key_column_usage.column_name from information_schema.table_constraints
126
- inner join information_schema.key_column_usage on key_column_usage.constraint_name = table_constraints.constraint_name and key_column_usage.constraint_schema = table_constraints.constraint_schema
127
- where constraint_type = 'PRIMARY KEY' and table_constraints.constraint_schema = 'dbo'
128
- order by ordinal_position
129
- `
130
- );
131
- return _(result)
132
- .groupBy('table_name')
133
- .mapValues(x => _.map(x, 'column_name'))
134
- .value();
135
- }
136
-
137
- function parseLogicalModel({ tables, primaryKeys, references, views, procedures, functions, indexes }) {
138
- const resTables = {};
139
- for (const table of Object.keys(tables)) {
140
- let columns = tables[table];
141
- const resTable = { name: table, columns: [] };
142
- resTables[table] = resTable;
143
- for (const column of columns) {
144
- const resColumn = {
145
- name: column.columnName,
146
- type: column.typeName,
147
- };
148
- if (column.characterMaximumLength) resColumn.length = column.characterMaximumLength;
149
- if (column.isIdentity) resColumn.autoIncrement = true;
150
- if (!column.isNullable) resColumn.notNull = true;
151
-
152
- const refColumn = references.find(x => x.fkTable == table && x.fkColumn == column.columnName);
153
- if (refColumn) {
154
- resColumn.references = refColumn.pkTable;
155
- }
156
- resTable.columns.push(resColumn);
157
- }
158
- if (table in primaryKeys) {
159
- resTable.primaryKey = primaryKeys[table];
160
- }
161
- if (table in indexes) {
162
- resTable.indexes = [];
163
-
164
- for (const { indexName, isUnique, columns } of Object.values(indexes[table])) {
165
- const resIndex = {
166
- name: indexName,
167
- unique: isUnique,
168
- columns: columns.map(x => x.columnName),
169
- };
170
- resTable.indexes.push(resIndex);
171
- }
172
- }
173
- }
174
-
175
- return {
176
- tables: resTables,
177
- views,
178
- procedures,
179
- functions,
180
- };
181
- }
182
-
183
- async function load(options) {
184
- const tables = await loadColumnsByTablesFromDatabase(options);
185
- console.log(`Loaded ${Object.keys(tables).length} tables`);
186
- const primaryKeys = await loadPrimaryKeysFromDatabase(options);
187
- console.log(`Loaded ${Object.keys(primaryKeys).length} primary keys`);
188
- const references = await loadReferencesFromDatabase(options);
189
- console.log(`Loaded ${references.length} references`);
190
- const views = await loadViewsFromDatabase(options);
191
- console.log(`Loaded ${Object.keys(views).length} views`);
192
- const { procedures, functions } = await loadRoutinesFromDatabase(options);
193
- console.log(`Loaded ${Object.keys(procedures).length} procedures and ${Object.keys(functions).length} functions`);
194
- const indexes = await loadIndexesByTablesFromDatabase(options);
195
- console.log(`Loaded indexes for ${Object.keys(indexes).length} tables`);
196
-
197
- const mssqlModel = {
198
- tables,
199
- primaryKeys,
200
- references,
201
- views,
202
- procedures,
203
- functions,
204
- indexes,
205
- };
206
- const logicalModel = parseLogicalModel(mssqlModel);
207
- return { ...logicalModel, mssqlModel };
208
- }
209
-
210
- module.exports = load;
@@ -1,6 +0,0 @@
1
- async function query({ mssqlPool }, sql) {
2
- const result = await mssqlPool.request().query(sql);
3
- return result.recordset;
4
- }
5
-
6
- module.exports = query;
package/lib/connect.js DELETED
@@ -1,8 +0,0 @@
1
- async function connect(options) {
2
- const { client } = options;
3
- const connectFunc = require(`./clients/${client}/connect`);
4
- await connectFunc(options);
5
- return options;
6
- }
7
-
8
- module.exports = connect;
package/lib/deploy.js DELETED
@@ -1,22 +0,0 @@
1
- const read = require('./read');
2
- const load = require('./load');
3
- const connect = require('./connect');
4
- const runHooks = require('./hooks/runHooks');
5
-
6
- async function deploy(options) {
7
- const { client } = options;
8
-
9
- options = await connect(options);
10
- options = await read(options);
11
- options = await load(options);
12
-
13
- const deployFunc = require(`./clients/${client}/deploy`);
14
-
15
- runHooks(options, 'before-deploy');
16
- await deployFunc(options);
17
- runHooks(options, 'after-deploy');
18
-
19
- return options;
20
- }
21
-
22
- module.exports = deploy;
package/lib/dump.js DELETED
@@ -1,38 +0,0 @@
1
- const fs = require('fs');
2
- const path = require('path');
3
- const yaml = require('js-yaml');
4
-
5
- const load = require('./load');
6
-
7
- async function dump(options) {
8
- try {
9
- const { outputDir } = options;
10
- const { databaseStructure } = await load(options);
11
- const { tables, views, procedures, functions } = databaseStructure;
12
-
13
- if (!fs.existsSync(outputDir)) {
14
- fs.mkdirSync(outputDir);
15
- }
16
-
17
- for (const table of Object.keys(tables)) {
18
- const content = yaml.dump(tables[table]);
19
- fs.writeFileSync(path.join(outputDir, table + '.table.yaml'), content);
20
- }
21
-
22
- for (const view of Object.keys(views)) {
23
- fs.writeFileSync(path.join(outputDir, view + '.view.sql'), views[view]);
24
- }
25
-
26
- for (const procedure of Object.keys(procedures)) {
27
- fs.writeFileSync(path.join(outputDir, procedure + '.proc.sql'), procedures[procedure]);
28
- }
29
-
30
- for (const func of Object.keys(functions)) {
31
- fs.writeFileSync(path.join(outputDir, func + '.func.sql'), functions[func]);
32
- }
33
- } catch (err) {
34
- console.log('dbmodel error: ', err);
35
- }
36
- }
37
-
38
- module.exports = dump;
package/lib/efmodel.js DELETED
@@ -1,132 +0,0 @@
1
- // generate EntityFramework Core model
2
-
3
- const _ = require('lodash');
4
- const fs = require('fs');
5
- const read = require('./read');
6
-
7
- function getDotNetType(type) {
8
- if (!type) return null;
9
- if (type.includes('char')) return 'string';
10
- if (type.includes('bigint')) return 'long';
11
- if (type.includes('int')) return 'int';
12
- if (type.includes('date')) return 'DateTime';
13
- if (type.includes('decimal')) return 'decimal';
14
- if (type.includes('bit')) return 'bool';
15
- if (type === 'json') return 'string';
16
- return null;
17
- }
18
-
19
- function getDotNetName(name) {
20
- if (name === 'params') return '@params';
21
- return name;
22
- }
23
-
24
- async function efmodel(options) {
25
- const { outputFile } = options;
26
- const { projectStructure, namespaceName, contextClassName } = await read(options);
27
-
28
- const { tables } = projectStructure;
29
-
30
- let s = '';
31
- s += 'using System;\n';
32
- s += 'using Microsoft.EntityFrameworkCore;\n';
33
- s += 'using System.ComponentModel.DataAnnotations.Schema;\n';
34
- s += 'using System.Collections.Generic;\n';
35
- s += 'using Newtonsoft.Json;\n';
36
-
37
- s += `namespace ${namespaceName} {\n`;
38
- s += ` partial class ${contextClassName} { \n`;
39
- for (const tableName of Object.keys(tables)) {
40
- s += ` public DbSet<Db${tableName}> ${tableName} { get; set; }\n`;
41
- }
42
-
43
- s += ' protected override void OnModelCreating(ModelBuilder modelBuilder)\n';
44
- s += ' {\n';
45
- for (const tableName of Object.keys(tables)) {
46
- const table = tables[tableName];
47
- s += ` modelBuilder.Entity<Db${tableName}>().HasKey(o=>new {${table.primaryKey
48
- .map(t => `o.${t}`)
49
- .join(', ')} });\n`;
50
- }
51
- s += ' }\n';
52
- s += ' }\n';
53
-
54
- for (const tableName of Object.keys(tables)) {
55
- const table = tables[tableName];
56
- const { columns, data } = table;
57
-
58
- const bases = [];
59
- const enumDef = table['enum'];
60
-
61
- if (options.getEntityBaseClass) {
62
- const optionBase = options.getEntityBaseClass(table);
63
- if (_.isString(optionBase)) bases.push(optionBase);
64
- else if (_.isArray(optionBase)) bases.push(...optionBase);
65
- }
66
- s += ` public partial class Db${tableName} ${bases.length > 0 ? ':' : ''} ${bases.join(',')} {\n`;
67
-
68
- if (enumDef) {
69
- for (const dataRow of data) {
70
- s += ` public const int ${_.camelCase(dataRow[enumDef.name])} = ${dataRow[enumDef.value]};\n`;
71
- }
72
- s += ` public static Dictionary<string, int> keyToInt = new Dictionary<string, int>();\n`;
73
- s += ` public static Dictionary<int, string> intToKey = new Dictionary<int, string>();\n`;
74
- }
75
-
76
- s += ` static Db${tableName}() {\n`;
77
- if (enumDef) {
78
- for (const dataRow of data) {
79
- s += ` keyToInt["${dataRow[enumDef.name]}"] = ${dataRow[enumDef.value]};\n`;
80
- s += ` intToKey[${dataRow[enumDef.value]}] = "${dataRow[enumDef.name]}";\n`;
81
- }
82
- }
83
- s += ` }\n`;
84
-
85
- s += ` public Db${tableName} Duplicate() {\n`;
86
- s += ` var res = new Db${tableName}();\n`;
87
- for (const column of columns) {
88
- const { name } = column;
89
- if (options.getEntityDuplicateColumnFilter == null || options.getEntityDuplicateColumnFilter(name)) {
90
- s += ` res.${getDotNetName(name)} = ${getDotNetName(name)};\n`;
91
- }
92
- }
93
- s += ` return res;\n`;
94
- s += ` }\n`;
95
-
96
- for (const column of columns) {
97
- const { type, notNull, name: colname, references } = column;
98
- let defaultValue = column['default'];
99
- const dotNetType = getDotNetType(type);
100
- if (dotNetType === 'bool' && defaultValue != null) defaultValue = defaultValue ? 'true' : 'false';
101
-
102
- if (dotNetType) {
103
- s += ` public ${dotNetType}${notNull ? '' : '?'} ${getDotNetName(colname)} {get; set; }`;
104
- if (defaultValue) s += ` = ${defaultValue};`;
105
- s += '\n';
106
- }
107
- if (references) {
108
- s += ` [ForeignKey("${colname}")]\n`;
109
- s += ` [JsonIgnore]\n`;
110
- s += ` public Db${references}${notNull ? '' : '?'} ${colname}Ref {get; set; }\n`;
111
- }
112
- }
113
-
114
- for (const otherTableName of Object.keys(tables)) {
115
- const otherTable = tables[otherTableName];
116
- const { columns: otherColumns } = otherTable;
117
- for (const otherColumn of otherColumns) {
118
- if (otherColumn.references === tableName) {
119
- s += ` [InverseProperty("${otherColumn.name}Ref")]\n`;
120
- s += ` [JsonIgnore]\n`;
121
- s += ` public List<Db${otherTableName}> ${otherColumn.name}${otherTableName}List {get; set; }\n`;
122
- }
123
- }
124
- }
125
-
126
- s += ' }\n';
127
- }
128
- s += '}\n';
129
- fs.writeFileSync(outputFile, s);
130
- }
131
-
132
- module.exports = efmodel;
@@ -1,23 +0,0 @@
1
- async function autoIndexForeignKeys(options, event) {
2
- if (event !== 'before-deploy' && event !== 'before-build') return;
3
-
4
- const { projectStructure } = options;
5
- if (!projectStructure) return;
6
- const { tables } = projectStructure;
7
- if (!tables) return;
8
-
9
- for (const tableName of Object.keys(tables)) {
10
- const table = tables[tableName];
11
- for (const column of table.columns) {
12
- if (column.references) {
13
- if (!table.indexes) table.indexes = [];
14
- table.indexes.push({
15
- name: `IX_FK_${tableName}_${column.name}`,
16
- columns: [column.name],
17
- });
18
- }
19
- }
20
- }
21
- }
22
-
23
- module.exports = autoIndexForeignKeys;
@@ -1,3 +0,0 @@
1
- const autoIndexForeignKeys = require('./autoIndexForeignKeys');
2
-
3
- module.exports = { autoIndexForeignKeys };
@@ -1,10 +0,0 @@
1
- async function runHooks(options, event) {
2
- const { hooks } = options;
3
- if (hooks) {
4
- for (const hook of hooks) {
5
- await hook(options, event);
6
- }
7
- }
8
- }
9
-
10
- module.exports = runHooks;
package/lib/index.js DELETED
@@ -1,20 +0,0 @@
1
- const load = require('./load');
2
- const connect = require('./connect');
3
- const dump = require('./dump');
4
- const deploy = require('./deploy');
5
- const build = require('./build');
6
- const efmodel = require('./efmodel');
7
-
8
- const hooks = require('./hooks');
9
- const runAndExit = require('./runAndExit');
10
-
11
- module.exports = {
12
- load,
13
- connect,
14
- dump,
15
- deploy,
16
- hooks,
17
- build,
18
- efmodel,
19
- runAndExit,
20
- };
package/lib/load.js DELETED
@@ -1,22 +0,0 @@
1
- const _ = require('lodash');
2
- const connect = require('./connect');
3
- const query = require('./query');
4
-
5
- async function load(options) {
6
- await connect(options);
7
- const { client } = options;
8
- const loadFunc = require(`./clients/${client}/load`);
9
- options.databaseStructure = await loadFunc(options);
10
- if (options.loadDataCondition) {
11
- const { tables } = options.databaseStructure;
12
- for (const tableName of _.keys(tables)) {
13
- const table = tables[tableName];
14
- if (!options.loadDataCondition(table)) continue;
15
- const data = await query(options, `SELECT * FROM [${tableName}]`);
16
- table.data = data;
17
- }
18
- }
19
- return options;
20
- }
21
-
22
- module.exports = load;
package/lib/query.js DELETED
@@ -1,7 +0,0 @@
1
- async function query(options, sql) {
2
- const { client } = options;
3
- const queryFunc = require(`./clients/${client}/query`);
4
- return await queryFunc(options, sql);
5
- }
6
-
7
- module.exports = query;
package/lib/read.js DELETED
@@ -1,61 +0,0 @@
1
- const _ = require('lodash');
2
- const readdirSyncRecursive = require('fs-readdir-recursive');
3
- const yaml = require('js-yaml');
4
- const path = require('path');
5
- const fs = require('fs');
6
-
7
- function readTablesFromDirectory(directory) {
8
- const files = readdirSyncRecursive(directory);
9
- const tables = _.keyBy(
10
- files
11
- .filter(x => x.endsWith('.table.yaml'))
12
- .map(file => yaml.safeLoad(fs.readFileSync(path.join(directory, file)))),
13
- 'name'
14
- );
15
- return tables;
16
- }
17
-
18
- function escapeRegExp(str) {
19
- return str.replace(/([.*+?^=!:${}()|[\]/\\])/g, '\\$1');
20
- }
21
-
22
- function processReplacements(value, replacements) {
23
- if (!replacements) return value;
24
- return Object.keys(replacements).reduce(
25
- (acc, key) => acc.replace(new RegExp(escapeRegExp(key), 'g'), replacements[key]),
26
- value
27
- );
28
- }
29
-
30
- function readObjectsFromDirectory(directory, extension, replacements) {
31
- const files = readdirSyncRecursive(directory).filter(x => x.endsWith(extension));
32
- const views = files.map(file =>
33
- processReplacements(fs.readFileSync(path.join(directory, file)).toString(), replacements)
34
- );
35
- return _.zipObject(
36
- files.map(name => {
37
- const res = name.slice(0, -extension.length);
38
- const max = Math.max(res.lastIndexOf('/'), res.lastIndexOf('\\'));
39
- if (max > 0) return res.substring(max + 1);
40
- return res;
41
- }),
42
- views
43
- );
44
- }
45
-
46
- async function read(options) {
47
- const { projectDir, replacements } = options;
48
- const tables = readTablesFromDirectory(projectDir);
49
- const views = readObjectsFromDirectory(projectDir, '.view.sql', replacements);
50
- const procedures = readObjectsFromDirectory(projectDir, '.proc.sql', replacements);
51
- const functions = readObjectsFromDirectory(projectDir, '.func.sql', replacements);
52
- options.projectStructure = {
53
- tables,
54
- views,
55
- procedures,
56
- functions,
57
- };
58
- return options;
59
- }
60
-
61
- module.exports = read;
package/lib/runAndExit.js DELETED
@@ -1,13 +0,0 @@
1
- async function runAndExit(promise) {
2
- try {
3
- await promise;
4
- console.log('SUCCESS');
5
- process.exit();
6
- } catch (e) {
7
- console.log('ERROR');
8
- console.log(e);
9
- process.exit(1);
10
- }
11
- }
12
-
13
- module.exports = runAndExit;