1 | "use strict";
|
2 | Object.defineProperty(exports, "__esModule", { value: true });
|
3 | const change_case = require("change-case");
|
4 | const LIST_TYPE_VIEW = "VIEW";
|
5 | const LIST_TYPE_TABLE = "BASE TABLE";
|
6 | class ModelBuilder {
|
7 | constructor(knex, databaseName) {
|
8 | this.knex = knex;
|
9 | this.databaseName = databaseName;
|
10 | }
|
11 | static init(knex) {
|
12 | return new ModelBuilder(knex).renderDatabaseSchema();
|
13 | }
|
14 | |
15 |
|
16 |
|
17 | static keysToLower(obj) {
|
18 | const newobj = {};
|
19 | Object.keys(obj).forEach(key => newobj[key.toLowerCase()] = obj[key]);
|
20 | return newobj;
|
21 | }
|
22 | |
23 |
|
24 |
|
25 |
|
26 |
|
27 | static getMysqlLength(strType) {
|
28 | const strip = strType.replace(/\D/g, "");
|
29 | return strip.length === 0 ? 0 : parseInt(strip, undefined);
|
30 | }
|
31 | |
32 |
|
33 |
|
34 |
|
35 | static stripMysqlLength(type) {
|
36 | const pos = type.indexOf("(");
|
37 | if (pos > -1) {
|
38 | return type.substr(0, pos);
|
39 | }
|
40 | return type;
|
41 | }
|
42 | static getEnumValues(type) {
|
43 | if (type.startsWith("enum")) {
|
44 | const strList = type.replace("enum(", "").replace(")", "");
|
45 | const list = strList.split(",").map(s => s.replace("'", "").replace("'", ""));
|
46 | return list;
|
47 | }
|
48 | return null;
|
49 | }
|
50 | async renderDatabaseSchema() {
|
51 | if (!this.databaseName) {
|
52 | this.databaseName = await this.getDatabaseName();
|
53 | }
|
54 | const schema = {
|
55 | storedProcedures: await this.renderStoredProcedures(),
|
56 | tables: await this.renderTableModel(),
|
57 | views: await this.renderViewModel()
|
58 | };
|
59 | return schema;
|
60 | }
|
61 | async getDatabaseName() {
|
62 | const resp = await this.knex.raw("SELECT DATABASE() as db");
|
63 | return resp[0][0].db;
|
64 | }
|
65 | |
66 |
|
67 |
|
68 | listFromDatabase(listType) {
|
69 | if (listType !== "BASE TABLE" && listType !== "VIEW") {
|
70 | throw new Error("Illegal listtype");
|
71 | }
|
72 | const select = "`information_schema`.`TABLES`.`TABLE_NAME` AS `tname`";
|
73 | const from = "`information_schema`.`TABLES`";
|
74 | const dbClause = "`information_schema`.`TABLES`.`TABLE_SCHEMA` = '" + this.databaseName + "'";
|
75 | const baseTable = "`information_schema`.`TABLES`.`TABLE_TYPE` = '" + listType + "'";
|
76 | return `SELECT ${select} FROM ${from} WHERE ${dbClause} AND ${baseTable} `;
|
77 | }
|
78 | async listViews() {
|
79 | const rows = await this.knex.raw(this.listFromDatabase(LIST_TYPE_VIEW));
|
80 | return rows[0].map(item => item.tname);
|
81 | }
|
82 | |
83 |
|
84 |
|
85 | async listTables() {
|
86 | const rows = await this.knex.raw(this.listFromDatabase(LIST_TYPE_TABLE));
|
87 | return rows[0].map(item => item.tname);
|
88 | }
|
89 | columnArrayToDatabaseSchema(colArrMap) {
|
90 | const schema = {};
|
91 | for (const tableName in colArrMap) {
|
92 | colArrMap[tableName] = colArrMap[tableName].map((col, i) => {
|
93 | col = ModelBuilder.keysToLower(col);
|
94 | col.length = ModelBuilder.getMysqlLength(col.type);
|
95 | col.enumValues = ModelBuilder.getEnumValues(col.type);
|
96 | col.isPrimary = col.key === "PRI";
|
97 | col.index = i;
|
98 | col.type = ModelBuilder.stripMysqlLength(col.type);
|
99 | return col;
|
100 | });
|
101 | const newTable = {};
|
102 | colArrMap[tableName].forEach(col => newTable[col.field] = col);
|
103 | schema[tableName] = newTable;
|
104 | }
|
105 | return schema;
|
106 | }
|
107 | |
108 |
|
109 |
|
110 | async listColumns(tableName) {
|
111 | return await this.knex.raw("SHOW COLUMNS FROM " + tableName).then(colData => colData[0]);
|
112 | }
|
113 | async renderModel(tables) {
|
114 |
|
115 | const columnArrayMap = {};
|
116 | const promises = tables.map(async (tableName) => {
|
117 | columnArrayMap[tableName] = await this.listColumns(tableName);
|
118 | });
|
119 | await Promise.all(promises);
|
120 | return this.columnArrayToDatabaseSchema(columnArrayMap);
|
121 | }
|
122 | async listStoredProcedures() {
|
123 | const SHOW_DB_QUERY = `SHOW PROCEDURE STATUS WHERE Db = ?`;
|
124 | const sps = await this.knex.raw(SHOW_DB_QUERY, [this.databaseName]);
|
125 | return sps[0].map(sp => sp.Name);
|
126 | }
|
127 | async listStoredProcedureParams() {
|
128 | const LIST_PARAM_QUERY = `SELECT * FROM information_schema.parameters WHERE specific_schema = ?`;
|
129 | const params = await this.knex.raw(LIST_PARAM_QUERY, [this.databaseName]);
|
130 | return params[0].map(item => {
|
131 | const copy = {};
|
132 | for (const key in item) {
|
133 | copy[change_case.camelCase(key)] = item[key];
|
134 | }
|
135 | return copy;
|
136 | });
|
137 | }
|
138 | async renderStoredProcedures() {
|
139 | const storedProcedures = await this.listStoredProcedures();
|
140 | const mapped = await this.listStoredProcedureParams();
|
141 | const storedProcedureDictionary = {};
|
142 | storedProcedures.forEach(spName => storedProcedureDictionary[spName] = { name: spName, parameters: {} });
|
143 | mapped.forEach(item => storedProcedureDictionary[item.specificName].parameters[item.parameterName] = item);
|
144 | return storedProcedureDictionary;
|
145 | }
|
146 | async renderViewModel() {
|
147 | const tables = await this.listViews();
|
148 | return await this.renderModel(tables);
|
149 | }
|
150 | async renderTableModel() {
|
151 | const tables = await this.listTables();
|
152 | return this.renderModel(tables);
|
153 | }
|
154 | }
|
155 | exports.default = ModelBuilder;
|
156 |
|
\ | No newline at end of file |