UNPKG

7.58 kBJavaScriptView Raw
1"use strict";
2var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
3 function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
4 return new (P || (P = Promise))(function (resolve, reject) {
5 function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
6 function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
7 function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
8 step((generator = generator.apply(thisArg, _arguments || [])).next());
9 });
10};
11Object.defineProperty(exports, "__esModule", { value: true });
12const change_case = require("change-case");
13const LIST_TYPE_VIEW = "VIEW";
14const LIST_TYPE_TABLE = "BASE TABLE";
15class ModelBuilder {
16 constructor(knex, databaseName) {
17 this.knex = knex;
18 this.databaseName = databaseName;
19 }
20 static init(knex) {
21 return new ModelBuilder(knex).renderDatabaseSchema();
22 }
23 /**
24 * Return a copy of an object but with all keys lower case
25 */
26 static keysToLower(obj) {
27 const newobj = {};
28 Object.keys(obj).forEach(key => newobj[key.toLowerCase()] = obj[key]);
29 return newobj;
30 }
31 /**
32 * Return the length of a type, eg varchar(255)=>255
33 * 0 if not parsable
34 * @param type
35 */
36 static getMysqlLength(strType) {
37 const strip = strType.replace(/\D/g, "");
38 return strip.length === 0 ? 0 : parseInt(strip, undefined);
39 }
40 /**
41 * Return the length of a type, eg varchar(255)=>varchar
42 * @param type
43 */
44 static stripMysqlLength(type) {
45 const pos = type.indexOf("(");
46 if (pos > -1) {
47 return type.substr(0, pos);
48 }
49 return type;
50 }
51 static getEnumValues(type) {
52 if (type.startsWith("enum")) {
53 const strList = type.replace("enum(", "").replace(")", "");
54 const list = strList.split(",").map(s => s.replace("'", "").replace("'", ""));
55 return list;
56 }
57 return null;
58 }
59 renderDatabaseSchema() {
60 return __awaiter(this, void 0, void 0, function* () {
61 if (!this.databaseName) {
62 this.databaseName = yield this.getDatabaseName();
63 }
64 const schema = {
65 storedProcedures: yield this.renderStoredProcedures(),
66 tables: yield this.renderTableModel(),
67 views: yield this.renderViewModel()
68 };
69 return schema;
70 });
71 }
72 getDatabaseName() {
73 return __awaiter(this, void 0, void 0, function* () {
74 const resp = yield this.knex.raw("SELECT DATABASE() as db");
75 return resp[0][0].db;
76 });
77 }
78 /**
79 * return a select query to list all tables or views from a database
80 */
81 listFromDatabase(listType) {
82 if (listType !== "BASE TABLE" && listType !== "VIEW") {
83 throw new Error("Illegal listtype");
84 }
85 const select = "`information_schema`.`TABLES`.`TABLE_NAME` AS `tname`";
86 const from = "`information_schema`.`TABLES`";
87 const dbClause = "`information_schema`.`TABLES`.`TABLE_SCHEMA` = '" + this.databaseName + "'";
88 const baseTable = "`information_schema`.`TABLES`.`TABLE_TYPE` = '" + listType + "'";
89 return `SELECT ${select} FROM ${from} WHERE ${dbClause} AND ${baseTable} `;
90 }
91 listViews() {
92 return __awaiter(this, void 0, void 0, function* () {
93 const rows = yield this.knex.raw(this.listFromDatabase(LIST_TYPE_VIEW));
94 return rows[0].map(item => item.tname);
95 });
96 }
97 /**
98 * Lists all the tables in current database
99 */
100 listTables() {
101 return __awaiter(this, void 0, void 0, function* () {
102 const rows = yield this.knex.raw(this.listFromDatabase(LIST_TYPE_TABLE));
103 return rows[0].map(item => item.tname);
104 });
105 }
106 columnArrayToDatabaseSchema(colArrMap) {
107 const schema = {};
108 for (const tableName in colArrMap) {
109 colArrMap[tableName] = colArrMap[tableName].map((col, i) => {
110 col = ModelBuilder.keysToLower(col);
111 col.length = ModelBuilder.getMysqlLength(col.type);
112 col.enumValues = ModelBuilder.getEnumValues(col.type);
113 col.isPrimary = col.key === "PRI";
114 col.index = i;
115 col.type = ModelBuilder.stripMysqlLength(col.type);
116 return col;
117 });
118 const newTable = {};
119 colArrMap[tableName].forEach(col => newTable[col.field] = col);
120 schema[tableName] = newTable;
121 }
122 return schema;
123 }
124 /**
125 * List all columns for a table given table name
126 */
127 listColumns(tableName) {
128 return __awaiter(this, void 0, void 0, function* () {
129 return yield this.knex.raw("SHOW COLUMNS FROM " + tableName).then(colData => colData[0]);
130 });
131 }
132 renderModel(tables) {
133 return __awaiter(this, void 0, void 0, function* () {
134 // TODO list all in one query instead of one query per table
135 const columnArrayMap = {};
136 const promises = tables.map((tableName) => __awaiter(this, void 0, void 0, function* () {
137 columnArrayMap[tableName] = yield this.listColumns(tableName);
138 }));
139 yield Promise.all(promises);
140 return this.columnArrayToDatabaseSchema(columnArrayMap);
141 });
142 }
143 listStoredProcedures() {
144 return __awaiter(this, void 0, void 0, function* () {
145 const SHOW_DB_QUERY = `SHOW PROCEDURE STATUS WHERE Db = ?`;
146 const sps = yield this.knex.raw(SHOW_DB_QUERY, [this.databaseName]);
147 return sps[0].map(sp => sp.Name);
148 });
149 }
150 listStoredProcedureParams() {
151 return __awaiter(this, void 0, void 0, function* () {
152 const LIST_PARAM_QUERY = `SELECT * FROM information_schema.parameters WHERE specific_schema = ?`;
153 const params = yield this.knex.raw(LIST_PARAM_QUERY, [this.databaseName]);
154 return params[0].map(item => {
155 const copy = {};
156 for (const key in item) {
157 copy[change_case.camelCase(key)] = item[key];
158 }
159 return copy;
160 });
161 });
162 }
163 renderStoredProcedures() {
164 return __awaiter(this, void 0, void 0, function* () {
165 const storedProcedures = yield this.listStoredProcedures();
166 const mapped = yield this.listStoredProcedureParams();
167 const storedProcedureDictionary = {};
168 storedProcedures.forEach(spName => storedProcedureDictionary[spName] = { name: spName, parameters: {} });
169 mapped.forEach(item => storedProcedureDictionary[item.specificName].parameters[item.parameterName] = item);
170 return storedProcedureDictionary;
171 });
172 }
173 renderViewModel() {
174 return __awaiter(this, void 0, void 0, function* () {
175 const tables = yield this.listViews();
176 return yield this.renderModel(tables);
177 });
178 }
179 renderTableModel() {
180 return __awaiter(this, void 0, void 0, function* () {
181 const tables = yield this.listTables();
182 return this.renderModel(tables);
183 });
184 }
185}
186exports.default = ModelBuilder;
187//# sourceMappingURL=model-builder.js.map
\No newline at end of file