@carbonorm/carbonnode 1.1.2 → 1.1.7

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.
@@ -0,0 +1,381 @@
1
+ const {execSync} = require('child_process');
2
+ const fs = require('fs');
3
+ const path = require('path');
4
+ const Handlebars = require('handlebars');
5
+
6
+
7
+ const args = process.argv.slice(2); // Slice the first two elements
8
+ const argMap = {};
9
+
10
+ for (let i = 0; i < args.length; i += 2) {
11
+ argMap[args[i]] = args[i + 1];
12
+ }
13
+
14
+ const createDirIfNotExists = dir =>
15
+ !fs.existsSync(dir) ? fs.mkdirSync(dir) : undefined;
16
+
17
+ class MySQLDump {
18
+
19
+ static mysqlcnf: string = '';
20
+ static mysqldump: string = '';
21
+ static DB_USER = argMap['--user'] || 'root';
22
+ static DB_PASS = argMap['--pass'] || 'password';
23
+ static DB_HOST = argMap['--host'] || '127.0.0.1';
24
+ static DB_PORT = argMap['--port'] || '3306';
25
+ static DB_NAME = argMap['--dbname'] || 'carbonPHP';
26
+ static DB_PREFIX = argMap['--prefix'] || 'carbon_';
27
+ static RELATIVE_OUTPUT_DIR = argMap['--output'] || '/src/api/rest';
28
+ static OUTPUT_DIR = path.join(process.cwd(), MySQLDump.RELATIVE_OUTPUT_DIR);
29
+
30
+ static buildCNF(cnfFile = null) {
31
+
32
+ if (this.mysqlcnf !== '') {
33
+
34
+ return this.mysqlcnf;
35
+
36
+ }
37
+
38
+ const cnf = [
39
+ '[client]',
40
+ `user = ${this.DB_USER}`,
41
+ `password = ${this.DB_PASS}`,
42
+ `host = ${this.DB_HOST}`,
43
+ `port = ${this.DB_PORT}`,
44
+ '',
45
+ ];
46
+
47
+
48
+ cnf.push(``);
49
+
50
+ cnfFile ??= path.join(process.cwd(), '/mysql.cnf');
51
+
52
+ try {
53
+
54
+ fs.writeFileSync(cnfFile, cnf.join('\n'));
55
+
56
+ fs.chmodSync(cnfFile, 0o750);
57
+
58
+ console.log(`Successfully created mysql.cnf file in (${cnfFile})`);
59
+
60
+ } catch (error) {
61
+
62
+ console.error(`Failed to store file contents of mysql.cnf in (${process.cwd()})`, error);
63
+
64
+ process.exit(1);
65
+
66
+ }
67
+
68
+ return (this.mysqlcnf = cnfFile);
69
+
70
+ }
71
+
72
+ static MySQLDump(mysqldump = null, data = false, schemas = true, outputFile = null, otherOption = '', specificTable = null) {
73
+ specificTable = specificTable || '';
74
+
75
+ if (outputFile === null) {
76
+ outputFile = path.join(process.cwd(), 'mysqldump.sql');
77
+ }
78
+
79
+ if (!data && !schemas) {
80
+ console.warn("MysqlDump is running with --no-create-info and --no-data. Why?");
81
+ }
82
+
83
+ const defaultsExtraFile = this.buildCNF();
84
+
85
+ const hexBlobOption = data ? '--hex-blob ' : '--no-data ';
86
+
87
+ const createInfoOption = schemas ? '' : ' --no-create-info ';
88
+
89
+ const cmd = `${mysqldump || 'mysqldump'} --defaults-extra-file="${defaultsExtraFile}" ${otherOption} --skip-add-locks --single-transaction --quick ${createInfoOption}${hexBlobOption}${this.DB_NAME} ${specificTable} > '${outputFile}'`;
90
+
91
+ this.executeAndCheckStatus(cmd);
92
+
93
+ return (this.mysqldump = outputFile);
94
+
95
+ }
96
+
97
+ static executeAndCheckStatus(command, exitOnFailure = true, output = []) {
98
+
99
+ try {
100
+
101
+ const stdout = execSync(command, {encoding: 'utf-8'});
102
+
103
+ output.push(stdout);
104
+
105
+ } catch (error) {
106
+
107
+ console.log(`The command >> ${command} \n\t returned with a status code (${error.status}). Expecting 0 for success.`);
108
+
109
+ console.log(`Command output::\t ${error.stdout}`);
110
+
111
+ if (exitOnFailure) {
112
+
113
+ process.exit(error.status);
114
+
115
+ }
116
+
117
+ }
118
+
119
+ }
120
+
121
+ }
122
+
123
+ createDirIfNotExists(MySQLDump.OUTPUT_DIR)
124
+
125
+ const pathRuntimeReference = MySQLDump.RELATIVE_OUTPUT_DIR.replace(/(^\/(src\/)?)|(\/+$)/g, '')
126
+
127
+ // Usage example
128
+ const dumpFileLocation = MySQLDump.MySQLDump();
129
+
130
+ type ColumnInfo = {
131
+ type: string;
132
+ length?: string;
133
+ autoIncrement: boolean;
134
+ notNull: boolean;
135
+ defaultValue?: string;
136
+ };
137
+
138
+ type foreignKeyInfo = {
139
+ TABLE: string,
140
+ CONSTRAINT: string,
141
+ FOREIGN_KEY: string,
142
+ REFERENCES: string,
143
+ ON_DELETE: string,
144
+ ON_UPDATE: string
145
+ }
146
+
147
+ function capitalizeFirstLetter(string) {
148
+ return string.charAt(0).toUpperCase() + string.slice(1);
149
+ }
150
+
151
+ function determineTypeScriptType(mysqlType) {
152
+ switch (mysqlType.toLowerCase()) {
153
+ case 'varchar':
154
+ case 'text':
155
+ case 'char':
156
+ case 'datetime':
157
+ case 'timestamp':
158
+ case 'date':
159
+ return 'string';
160
+ case 'int':
161
+ case 'bigint':
162
+ case 'smallint':
163
+ case 'decimal':
164
+ case 'float':
165
+ case 'double':
166
+ return 'number';
167
+ case 'boolean':
168
+ case 'tinyint(1)':
169
+ return 'boolean';
170
+ case 'json':
171
+ return 'any'; // or 'object' based on usage
172
+ default:
173
+ return 'string';
174
+ }
175
+ }
176
+
177
+
178
+ const parseSQLToTypeScript = (sql: string) => {
179
+
180
+ const tableMatches = sql.matchAll(/CREATE\s+TABLE\s+`?(\w+)`?\s+\(((.|\n)+?)\)\s*(ENGINE=.+?);/gm);
181
+
182
+ let tableData: {
183
+ [TableName: string]: {
184
+ RELATIVE_OUTPUT_DIR: string,
185
+ TABLE_NAME: string,
186
+ TABLE_DEFINITION: string,
187
+ TABLE_CONSTRAINT: {},
188
+ TABLE_NAME_SHORT: string,
189
+ TABLE_NAME_LOWER: string,
190
+ TABLE_NAME_UPPER: string,
191
+ TABLE_NAME_PASCAL_CASE: string,
192
+ TABLE_NAME_SHORT_PASCAL_CASE: string,
193
+ TABLE_REFERENCED_BY?: {},
194
+ TABLE_REFERENCES?: {},
195
+ PRIMARY: string[],
196
+ PRIMARY_SHORT: string[],
197
+ COLUMNS: {},
198
+ COLUMNS_UPPERCASE: {},
199
+ TYPE_VALIDATION: {},
200
+ REGEX_VALIDATION: {},
201
+ }
202
+ } = {};
203
+
204
+ let references: foreignKeyInfo[] = [];
205
+
206
+ // @ts-ignore
207
+ for (const tableMatch of tableMatches) {
208
+
209
+ const tableName = tableMatch[1];
210
+ const columnDefinitions = tableMatch[2];
211
+
212
+ let columns: any = {};
213
+ const columnRegex: RegExp = /^\s*`(\w+)` (\w+)(?:\((\d+)\))?( NOT NULL)?( AUTO_INCREMENT)?(?: DEFAULT '(\w+)')?/g;
214
+ let columnMatch: RegExpExecArray | null;
215
+
216
+ while ((columnMatch = columnRegex.exec(columnDefinitions))) {
217
+ columns[columnMatch[1]] = {
218
+ type: columnMatch[2],
219
+ length: columnMatch[3] || '',
220
+ notNull: !!columnMatch[4],
221
+ autoIncrement: !!columnMatch[5],
222
+ defaultValue: columnMatch[6] || '',
223
+ };
224
+ }
225
+
226
+ // Extract primary keys
227
+ const primaryKeyMatch = columnDefinitions.match(/PRIMARY KEY \(([^)]+)\)/i);
228
+ const primaryKeys = primaryKeyMatch
229
+ ? primaryKeyMatch[1].split(',').map(key => key.trim().replace(/`/g, ''))
230
+ : [];
231
+
232
+ // Extract foreign keys
233
+ const foreignKeyRegex: RegExp = /CONSTRAINT `([^`]+)` FOREIGN KEY \(`([^`]+)`\) REFERENCES `([^`]+)` \(`([^`]+)`\)( ON DELETE (\w+))?( ON UPDATE (\w+))?/g;
234
+ let foreignKeyMatch: RegExpExecArray | null;
235
+
236
+ while ((foreignKeyMatch = foreignKeyRegex.exec(columnDefinitions))) {
237
+ const constraintName = foreignKeyMatch[1];
238
+ const localColumn = foreignKeyMatch[2];
239
+ const foreignTable = foreignKeyMatch[3];
240
+ const foreignColumn = foreignKeyMatch[4];
241
+ const onDeleteAction = foreignKeyMatch[6] || null;
242
+ const onUpdateAction = foreignKeyMatch[8] || null;
243
+
244
+ references.push({
245
+ TABLE: tableName,
246
+ CONSTRAINT: constraintName,
247
+ FOREIGN_KEY: localColumn,
248
+ REFERENCES: `${foreignTable}.${foreignColumn}`,
249
+ ON_DELETE: onDeleteAction,
250
+ ON_UPDATE: onUpdateAction
251
+ });
252
+
253
+ }
254
+
255
+ const tsModel = {
256
+ RELATIVE_OUTPUT_DIR: pathRuntimeReference,
257
+ TABLE_NAME: tableName,
258
+ TABLE_DEFINITION: tableMatch[0],
259
+ TABLE_CONSTRAINT: references,
260
+ TABLE_NAME_SHORT: tableName.replace(MySQLDump.DB_PREFIX, ''),
261
+ TABLE_NAME_LOWER: tableName.toLowerCase(),
262
+ TABLE_NAME_UPPER: tableName.toUpperCase(),
263
+ TABLE_NAME_PASCAL_CASE: tableName.split('_').map(capitalizeFirstLetter).join('_'),
264
+ TABLE_NAME_SHORT_PASCAL_CASE: tableName.replace(MySQLDump.DB_PREFIX, '').split('_').map(capitalizeFirstLetter).join('_'),
265
+ PRIMARY: primaryKeys.map(pk => `${tableName}.${pk}`),
266
+ PRIMARY_SHORT: primaryKeys,
267
+ COLUMNS: {},
268
+ COLUMNS_UPPERCASE: {},
269
+ TYPE_VALIDATION: {},
270
+ REGEX_VALIDATION: {},
271
+ TABLE_REFERENCES: {},
272
+ TABLE_REFERENCED_BY:{},
273
+ };
274
+
275
+ for (const colName in columns) {
276
+
277
+ tsModel.COLUMNS[`${tableName}.${colName}`] = colName;
278
+
279
+ tsModel.COLUMNS_UPPERCASE[colName.toUpperCase()] = tableName + '.' + colName;
280
+
281
+ const typescript_type = determineTypeScriptType(columns[colName].type.toLowerCase()) === "number" ? "number" : "string"
282
+
283
+ tsModel.TYPE_VALIDATION[`${tableName}.${colName}`] = {
284
+ COLUMN_NAME: colName,
285
+ MYSQL_TYPE: columns[colName].type.toLowerCase(),
286
+ TYPESCRIPT_TYPE: typescript_type,
287
+ TYPESCRIPT_TYPE_IS_STRING: 'string' === typescript_type,
288
+ TYPESCRIPT_TYPE_IS_NUMBER: 'number' === typescript_type,
289
+ MAX_LENGTH: columns[colName].length,
290
+ AUTO_INCREMENT: columns[colName].autoIncrement,
291
+ SKIP_COLUMN_IN_POST: !columns[colName].notNull && !columns[colName].defaultValue,
292
+ };
293
+
294
+ }
295
+
296
+ tableData[tableName] = tsModel;
297
+
298
+ }
299
+
300
+ for (const ref of references) {
301
+
302
+ const foreignTable = ref.REFERENCES.split('.')[0];
303
+ const foreignColumn = ref.REFERENCES.split('.')[1];
304
+ const tableName = ref.TABLE;
305
+ const columnName = ref.FOREIGN_KEY;
306
+ const constraintName = ref.CONSTRAINT;
307
+
308
+ if (!tableData[foreignTable]) {
309
+ console.log(`Foreign table ${foreignTable} not found for ${ref.TABLE}.${ref.CONSTRAINT}`);
310
+ continue;
311
+ }
312
+
313
+ if (!tableData[foreignTable].TABLE_REFERENCED_BY) {
314
+ tableData[foreignTable].TABLE_REFERENCED_BY = {};
315
+ }
316
+
317
+ if (!tableData[foreignTable].TABLE_REFERENCED_BY[foreignColumn]) {
318
+ tableData[foreignTable].TABLE_REFERENCED_BY[foreignColumn] = [];
319
+ }
320
+
321
+ tableData[foreignTable].TABLE_REFERENCED_BY[foreignColumn].push({
322
+ TABLE: tableName,
323
+ COLUMN: columnName,
324
+ CONSTRAINT: constraintName
325
+ });
326
+
327
+ if (!tableData[tableName].TABLE_REFERENCES) {
328
+ tableData[tableName].TABLE_REFERENCES = {};
329
+ }
330
+
331
+ if (!tableData[tableName].TABLE_REFERENCES[columnName]) {
332
+ tableData[tableName].TABLE_REFERENCES[columnName] = [];
333
+ }
334
+
335
+ tableData[tableName].TABLE_REFERENCES[columnName].push({
336
+ TABLE: foreignTable,
337
+ COLUMN: foreignColumn,
338
+ CONSTRAINT: constraintName
339
+ });
340
+
341
+ }
342
+
343
+ const tables = Object.values(tableData);
344
+
345
+
346
+ return {
347
+ TABLES: tables,
348
+ RestTableNames: tables.map(table => "'" + table.TABLE_NAME + "'").join('\n | '),
349
+ RestShortTableNames: tables.map(table => "'" + table.TABLE_NAME_SHORT + "'").join('\n | '),
350
+ RestTableInterfaces: tables.map(table => 'i' + table.TABLE_NAME_SHORT_PASCAL_CASE).join('\n | '),
351
+ };
352
+ };
353
+
354
+
355
+ // use dumpFileLocation to get sql
356
+ const sql = fs.readFileSync(dumpFileLocation, 'utf-8');
357
+
358
+ const tableData = parseSQLToTypeScript(sql);
359
+
360
+ // write to file
361
+ fs.writeFileSync(path.join(process.cwd(), 'C6MySqlDump.json'), JSON.stringify(tableData));
362
+
363
+
364
+ // import this file src/assets/handlebars/C6.tsx.handlebars for a mustache template
365
+
366
+ const template = fs.readFileSync(path.resolve(__dirname, 'assets/handlebars/C6.tsx.handlebars'), 'utf-8');
367
+
368
+ fs.writeFileSync(path.join(MySQLDump.OUTPUT_DIR, 'C6.tsx'), Handlebars.compile(template)(tableData));
369
+
370
+ const testTemplate = fs.readFileSync(path.resolve(__dirname, 'assets/handlebars/Tests.tsx.handlebars'), 'utf-8');
371
+
372
+ Object.values(tableData.TABLES).map((tableData, key) => {
373
+
374
+ const tableName = tableData.TABLE_NAME_SHORT
375
+
376
+ fs.writeFileSync(path.join(MySQLDump.OUTPUT_DIR, tableName + '.tsx'), Handlebars.compile(testTemplate)(tableData));
377
+
378
+ })
379
+
380
+ console.log('Successfully created CarbonORM bindings!')
381
+
@@ -1,10 +1,16 @@
1
1
  // @link https://www.npmjs.com/package/axios-cache-adapter
2
2
  import axios from "axios";
3
3
  import Qs from "qs";
4
- //import axiosInterceptors from "api/hoc/axiosInterceptors";
5
4
 
6
- // noinspection SpellCheckingInspection
7
5
 
6
+ // updating these values
7
+ // @link https://github.com/axios/axios/issues/209
8
+ //
9
+ // only affects the global instance and instances created afterwards
10
+ // axios.defaults.headers.common['Auth-Token'] = 'foo bar';
11
+ //
12
+ // immediately affects this instance
13
+ // axiosInstance.defaults.headers['Auth-Token'] = 'foo bar';
8
14
  export default (axios.create({
9
15
 
10
16
  // `baseURL` will be prepended to `url` unless `url` is absolute.
@@ -22,10 +22,19 @@ export interface iTypeValidation {
22
22
  SKIP_COLUMN_IN_POST: boolean
23
23
  }
24
24
 
25
+ export interface iConstraint {
26
+ TABLE: string,
27
+ COLUMN: string,
28
+ CONSTRAINT: string
29
+ }
30
+
25
31
  export interface C6RestfulModel<RestShortTableNames extends string = string> {
26
32
  TABLE_NAME: RestShortTableNames,
27
33
  PRIMARY: string[],
34
+ PRIMARY_SHORT: string[],
28
35
  COLUMNS: stringMap,
29
36
  REGEX_VALIDATION: RegExpMap,
30
37
  TYPE_VALIDATION: {[key: string]: iTypeValidation},
38
+ TABLE_REFERENCES: {[columnName: string]: iConstraint[]},
39
+ TABLE_REFERENCED_BY: {[columnName: string]: iConstraint[]}
31
40
  }
@@ -355,7 +355,8 @@ export default function restApi<
355
355
  } = any,
356
356
  RequestTableOverrides extends {
357
357
  [key: string]: any;
358
- } = any, ResponseDataType = any,
358
+ } = any,
359
+ ResponseDataType = any,
359
360
  RestShortTableNames extends string = any
360
361
  >({
361
362
  C6,