@carbonorm/carbonnode 1.1.3 → 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.
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@carbonorm/carbonnode",
|
|
3
|
-
"version": "1.1.
|
|
3
|
+
"version": "1.1.7",
|
|
4
4
|
"main": "dist/index.cjs.js",
|
|
5
5
|
"module": "dist/index.esm.js",
|
|
6
6
|
"browser": "dist/index.umd.js",
|
|
@@ -25,6 +25,7 @@
|
|
|
25
25
|
"@types/qs": "^6.9.8",
|
|
26
26
|
"autoprefixer": "^10.4.14",
|
|
27
27
|
"deepmerge": "^4.3.1",
|
|
28
|
+
"handlebars": "^4.7.8",
|
|
28
29
|
"postcss": "^8.4.27",
|
|
29
30
|
"postcss-nested": "^6.0.1",
|
|
30
31
|
"postcss-simple-vars": "^7.0.1",
|
|
@@ -34,14 +35,20 @@
|
|
|
34
35
|
"typescript": "^5.1.6"
|
|
35
36
|
},
|
|
36
37
|
"scripts": {
|
|
37
|
-
"build": "npm run build:index && rollup -c",
|
|
38
|
+
"build": "npm run build:index && npm run build:generateRestBindings && rollup -c",
|
|
38
39
|
"dev": "rollup -c -w",
|
|
39
40
|
"test": "node test/test.js",
|
|
40
41
|
"pretest": "npm run build",
|
|
41
|
-
"build:index": "npx barrelsby -d ./src --delete --exclude '(jestHoc|\\.test|\\.d).(js|tsx?)$' --exportDefault --verbose"
|
|
42
|
+
"build:index": "npx barrelsby -d ./src --delete --exclude '(jestHoc|\\.test|\\.d).(js|tsx?)$' --exportDefault --verbose",
|
|
43
|
+
"build:generateRestBindings": "cd ./scripts/ && tsc --downlevelIteration generateRestBindings.ts && mv generateRestBindings.js generateRestBindings.cjs",
|
|
44
|
+
"generateRestBindings": "npm run build:generateRestBindings && node ./scripts/generateRestBindings.cjs"
|
|
45
|
+
},
|
|
46
|
+
"bin": {
|
|
47
|
+
"generateRestBindings": "./scripts/generateRestBindings.cjs"
|
|
42
48
|
},
|
|
43
49
|
"files": [
|
|
44
50
|
"dist",
|
|
45
|
-
"src"
|
|
51
|
+
"src",
|
|
52
|
+
"scripts"
|
|
46
53
|
]
|
|
47
54
|
}
|
|
@@ -0,0 +1,115 @@
|
|
|
1
|
+
import {
|
|
2
|
+
C6RestfulModel,
|
|
3
|
+
C6Constants
|
|
4
|
+
} from "@carbonorm/carbonnode";
|
|
5
|
+
|
|
6
|
+
export type RestTableNames = {{{RestTableNames}}};
|
|
7
|
+
|
|
8
|
+
export type RestShortTableNames = {{{RestShortTableNames}}};
|
|
9
|
+
|
|
10
|
+
{{#TABLES}}
|
|
11
|
+
|
|
12
|
+
export interface i{{TABLE_NAME_SHORT_PASCAL_CASE}} {
|
|
13
|
+
{{#each TYPE_VALIDATION}}
|
|
14
|
+
'{{this.COLUMN_NAME}}'?: {{this.TYPESCRIPT_TYPE}};
|
|
15
|
+
{{/each}}
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
interface iDefine{{TABLE_NAME_SHORT_PASCAL_CASE}} {
|
|
19
|
+
{{#each COLUMNS_UPPERCASE}}
|
|
20
|
+
'{{@key}}': string;
|
|
21
|
+
{{/each}}
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
export const {{TABLE_NAME_SHORT}}: C6RestfulModel & iDefine{{TABLE_NAME_SHORT_PASCAL_CASE}} = {
|
|
25
|
+
TABLE_NAME: '{{TABLE_NAME}}',
|
|
26
|
+
{{#each COLUMNS_UPPERCASE}}
|
|
27
|
+
{{@key}}: '{{this}}',
|
|
28
|
+
{{/each}}
|
|
29
|
+
PRIMARY: [
|
|
30
|
+
{{#PRIMARY}}
|
|
31
|
+
'{{this}}',
|
|
32
|
+
{{/PRIMARY}}
|
|
33
|
+
],
|
|
34
|
+
PRIMARY_SHORT: [
|
|
35
|
+
{{#each PRIMARY_SHORT}}
|
|
36
|
+
'{{this}}',
|
|
37
|
+
{{/each}}
|
|
38
|
+
],
|
|
39
|
+
COLUMNS: {
|
|
40
|
+
{{#each COLUMNS}}
|
|
41
|
+
'{{@key}}': '{{this}}',
|
|
42
|
+
{{/each}}
|
|
43
|
+
},
|
|
44
|
+
TYPE_VALIDATION: {
|
|
45
|
+
{{#each TYPE_VALIDATION}}
|
|
46
|
+
'{{@key}}': {
|
|
47
|
+
MYSQL_TYPE: '{{this.MYSQL_TYPE}}',
|
|
48
|
+
MAX_LENGTH: '{{this.MAX_LENGTH}}',
|
|
49
|
+
AUTO_INCREMENT: {{this.AUTO_INCREMENT}},
|
|
50
|
+
SKIP_COLUMN_IN_POST: {{this.SKIP_COLUMN_IN_POST}}
|
|
51
|
+
},
|
|
52
|
+
{{/each}}
|
|
53
|
+
},
|
|
54
|
+
REGEX_VALIDATION: {
|
|
55
|
+
{{#each REGEX_VALIDATION}}
|
|
56
|
+
'{{@key}}': '{{this}}',
|
|
57
|
+
{{/each}}
|
|
58
|
+
},
|
|
59
|
+
TABLE_REFERENCES: {
|
|
60
|
+
{{#each TABLE_REFERENCES}}'{{@key}}': [{{#this}}{
|
|
61
|
+
TABLE: '{{TABLE}}',
|
|
62
|
+
COLUMN: '{{COLUMN}}',
|
|
63
|
+
CONSTRAINT: '{{CONSTRAINT}}',
|
|
64
|
+
},{{/this}}],{{/each}}
|
|
65
|
+
},
|
|
66
|
+
TABLE_REFERENCED_BY: {
|
|
67
|
+
{{#each TABLE_REFERENCED_BY}}'{{@key}}': [{{#this}}{
|
|
68
|
+
TABLE: '{{TABLE}}',
|
|
69
|
+
COLUMN: '{{COLUMN}}',
|
|
70
|
+
CONSTRAINT: '{{CONSTRAINT}}',
|
|
71
|
+
},{{/this}}],{{/each}}
|
|
72
|
+
},
|
|
73
|
+
}
|
|
74
|
+
{{/TABLES}}
|
|
75
|
+
|
|
76
|
+
export const TABLES = {
|
|
77
|
+
{{#TABLES}}
|
|
78
|
+
{{TABLE_NAME_SHORT}}: {{TABLE_NAME_SHORT}},
|
|
79
|
+
{{/TABLES}}
|
|
80
|
+
};
|
|
81
|
+
|
|
82
|
+
export const C6 : { TABLES: { [key: string]: (C6RestfulModel & { [key: string]: any }) } }
|
|
83
|
+
& { [key: string]: any } = {
|
|
84
|
+
...C6Constants,
|
|
85
|
+
TABLES: TABLES,
|
|
86
|
+
...TABLES
|
|
87
|
+
};
|
|
88
|
+
|
|
89
|
+
export const COLUMNS = {
|
|
90
|
+
{{#TABLES}}{{#each TYPE_VALIDATION}}'{{@key}}': '{{this.COLUMN_NAME}}',{{/each}}
|
|
91
|
+
{{/TABLES}}
|
|
92
|
+
|
|
93
|
+
};
|
|
94
|
+
|
|
95
|
+
|
|
96
|
+
export type RestTableInterfaces = {{{RestTableInterfaces}}};
|
|
97
|
+
|
|
98
|
+
export type tStatefulApiData<T> = T[] | undefined | null;
|
|
99
|
+
|
|
100
|
+
|
|
101
|
+
// this refers to the value types of the keys above, aka values in the state
|
|
102
|
+
export interface iRestfulObjectArrayTypes {
|
|
103
|
+
{{#TABLES}}
|
|
104
|
+
{{TABLE_NAME_SHORT}}: tStatefulApiData<i{{TABLE_NAME_SHORT_PASCAL_CASE}}>,
|
|
105
|
+
{{/TABLES}}
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
export const initialRestfulObjectsState: iRestfulObjectArrayTypes = {
|
|
109
|
+
{{#TABLES}}
|
|
110
|
+
{{TABLE_NAME_SHORT}}: undefined,
|
|
111
|
+
{{/TABLES}}
|
|
112
|
+
};
|
|
113
|
+
|
|
114
|
+
export type tRestfulObjectArrayValues = iRestfulObjectArrayTypes[keyof iRestfulObjectArrayTypes];
|
|
115
|
+
|
|
@@ -0,0 +1,121 @@
|
|
|
1
|
+
import {xdescribe, expect, test} from '@jest/globals';
|
|
2
|
+
import {CarbonReact} from "@carbonorm/carbonreact";
|
|
3
|
+
import {checkAllRequestsComplete} from "@carbonorm/carbonnode";
|
|
4
|
+
import {act, waitFor} from '@testing-library/react';
|
|
5
|
+
import {C6, iRestfulObjectArrayTypes, i{{TABLE_NAME_SHORT_PASCAL_CASE}}, {{TABLE_NAME_SHORT}} } from "{{RELATIVE_OUTPUT_DIR}}/C6";
|
|
6
|
+
|
|
7
|
+
const randomString = Math.random().toString(36).substring(7);
|
|
8
|
+
const randomInt = Math.floor(Math.random() * 1000000);
|
|
9
|
+
const fillString = 'string' + randomString + randomInt;
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
{{{TABLE_DEFINITION}}}
|
|
13
|
+
**/
|
|
14
|
+
|
|
15
|
+
const Test_Data: i{{TABLE_NAME_SHORT_PASCAL_CASE}} = {
|
|
16
|
+
{{#each TYPE_VALIDATION}}
|
|
17
|
+
{{#SKIP_COLUMN_IN_POST}}{{COLUMN_NAME}}: {{#TYPESCRIPT_TYPE_IS_STRING}}fillString.substring(0, {{MAX_LENGTH}}){{/TYPESCRIPT_TYPE_IS_STRING}}{{#TYPESCRIPT_TYPE_IS_NUMBER}}randomInt,{{/TYPESCRIPT_TYPE_IS_NUMBER}}{{/SKIP_COLUMN_IN_POST}}
|
|
18
|
+
{{/each}}
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
export default Test_Data;
|
|
22
|
+
|
|
23
|
+
xdescribe('REST {{TABLE_NAME_SHORT_PASCAL_CASE}} api', () => {
|
|
24
|
+
|
|
25
|
+
let testData = Test_Data;
|
|
26
|
+
|
|
27
|
+
test('GET POST PUT DELETE', async () => {
|
|
28
|
+
|
|
29
|
+
await act(async () => {
|
|
30
|
+
|
|
31
|
+
let selectAllResponse = await {{TABLE_NAME_SHORT}}.Get({})
|
|
32
|
+
|
|
33
|
+
if ('function' === typeof selectAllResponse) {
|
|
34
|
+
throw Error('selectAllResponse is a promise, this typically means this specific get request has already run during test setup.');
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
// We don't care if it is filled or not, just that the request can be made.
|
|
38
|
+
expect(selectAllResponse?.data?.rest).not.toBeUndefined();
|
|
39
|
+
|
|
40
|
+
const postResponse = await {{TABLE_NAME_SHORT}}.Post(testData);
|
|
41
|
+
|
|
42
|
+
console.log('postResponse', postResponse?.data)
|
|
43
|
+
|
|
44
|
+
expect(postResponse?.data?.created).not.toBeUndefined();
|
|
45
|
+
|
|
46
|
+
const primaryKey = {{TABLE_NAME_SHORT}}.PRIMARY_SHORT[0];
|
|
47
|
+
|
|
48
|
+
const postID = postResponse?.data?.created
|
|
49
|
+
|
|
50
|
+
const singleRowSelect = await {{TABLE_NAME_SHORT}}.Get({
|
|
51
|
+
[C6.WHERE]: {
|
|
52
|
+
[{{TABLE_NAME_SHORT}}[primaryKey.toUpperCase()]]: postID,
|
|
53
|
+
}
|
|
54
|
+
})
|
|
55
|
+
|
|
56
|
+
if ('function' === typeof singleRowSelect) {
|
|
57
|
+
throw Error('singleRowSelect is a promise, this is unexpected.');
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
console.log('singleRowSelect', singleRowSelect?.data)
|
|
61
|
+
|
|
62
|
+
// Ensure the expected response datastructure is returned
|
|
63
|
+
expect(singleRowSelect?.data?.rest).not.toBeUndefined();
|
|
64
|
+
|
|
65
|
+
// Make sure the previously created post is now returned
|
|
66
|
+
expect(typeof singleRowSelect?.data?.rest).toEqual('object');
|
|
67
|
+
|
|
68
|
+
// todo - make this work correctly with multiple primary keys
|
|
69
|
+
const selectedPostId = singleRowSelect?.data?.rest[0][primaryKey]
|
|
70
|
+
|
|
71
|
+
expect(selectedPostId).toEqual(postID);
|
|
72
|
+
|
|
73
|
+
const multipleRowSelect = await {{TABLE_NAME_SHORT}}.Get({
|
|
74
|
+
[C6.WHERE]: {
|
|
75
|
+
[{{TABLE_NAME_SHORT}}[primaryKey.toUpperCase()]]: [C6.IN, [0, postID]],
|
|
76
|
+
}
|
|
77
|
+
})
|
|
78
|
+
|
|
79
|
+
if ('function' === typeof multipleRowSelect) {
|
|
80
|
+
throw Error('singleRowSelect is a promise, this is unexpected.');
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
console.log('singleRowSelect', multipleRowSelect?.data)
|
|
84
|
+
|
|
85
|
+
// Ensure the expected response datastructure is returned
|
|
86
|
+
expect(multipleRowSelect?.data?.rest).not.toBeUndefined();
|
|
87
|
+
|
|
88
|
+
// Make sure the previously created post is now returned
|
|
89
|
+
expect(typeof multipleRowSelect?.data?.rest).toEqual('object');
|
|
90
|
+
|
|
91
|
+
testData[primaryKey] = postID
|
|
92
|
+
|
|
93
|
+
{{#each TYPE_VALIDATION}}
|
|
94
|
+
testData.{{@key}} = {{#TYPESCRIPT_TYPE_IS_STRING}}fillString.substring(0, {{TYPE_VALIDATION.MAX_LENGTH}}){{/TYPESCRIPT_TYPE_IS_STRING}}{{#TYPESCRIPT_TYPE_IS_NUMBER}}randomInt{{/TYPESCRIPT_TYPE_IS_NUMBER}};
|
|
95
|
+
{{/each}}
|
|
96
|
+
|
|
97
|
+
// wait for the global state to be updated
|
|
98
|
+
expect(CarbonReact.getState<iRestfulObjectArrayTypes>().{{TABLE_NAME_SHORT}}).not.toBeUndefined();
|
|
99
|
+
|
|
100
|
+
const updateResponse = await {{TABLE_NAME_SHORT}}.Put(testData)
|
|
101
|
+
|
|
102
|
+
expect(updateResponse?.data?.updated).not.toBeUndefined();
|
|
103
|
+
|
|
104
|
+
const deleteResponse = await {{TABLE_NAME_SHORT}}.Delete({
|
|
105
|
+
[primaryKey]: postID
|
|
106
|
+
})
|
|
107
|
+
|
|
108
|
+
console.log('deleteResponse', deleteResponse?.data)
|
|
109
|
+
|
|
110
|
+
expect(deleteResponse?.data?.deleted).not.toBeUndefined();
|
|
111
|
+
|
|
112
|
+
await waitFor(async () => {
|
|
113
|
+
expect(checkAllRequestsComplete()).toEqual(true);
|
|
114
|
+
}, {timeout: 10000, interval: 1000});
|
|
115
|
+
|
|
116
|
+
})
|
|
117
|
+
|
|
118
|
+
}, 100000);
|
|
119
|
+
|
|
120
|
+
})
|
|
121
|
+
|
|
@@ -0,0 +1,291 @@
|
|
|
1
|
+
var __values = (this && this.__values) || function(o) {
|
|
2
|
+
var s = typeof Symbol === "function" && Symbol.iterator, m = s && o[s], i = 0;
|
|
3
|
+
if (m) return m.call(o);
|
|
4
|
+
if (o && typeof o.length === "number") return {
|
|
5
|
+
next: function () {
|
|
6
|
+
if (o && i >= o.length) o = void 0;
|
|
7
|
+
return { value: o && o[i++], done: !o };
|
|
8
|
+
}
|
|
9
|
+
};
|
|
10
|
+
throw new TypeError(s ? "Object is not iterable." : "Symbol.iterator is not defined.");
|
|
11
|
+
};
|
|
12
|
+
var execSync = require('child_process').execSync;
|
|
13
|
+
var fs = require('fs');
|
|
14
|
+
var path = require('path');
|
|
15
|
+
var Handlebars = require('handlebars');
|
|
16
|
+
var args = process.argv.slice(2); // Slice the first two elements
|
|
17
|
+
var argMap = {};
|
|
18
|
+
for (var i = 0; i < args.length; i += 2) {
|
|
19
|
+
argMap[args[i]] = args[i + 1];
|
|
20
|
+
}
|
|
21
|
+
var createDirIfNotExists = function (dir) {
|
|
22
|
+
return !fs.existsSync(dir) ? fs.mkdirSync(dir) : undefined;
|
|
23
|
+
};
|
|
24
|
+
var MySQLDump = /** @class */ (function () {
|
|
25
|
+
function MySQLDump() {
|
|
26
|
+
}
|
|
27
|
+
MySQLDump.buildCNF = function (cnfFile) {
|
|
28
|
+
if (cnfFile === void 0) { cnfFile = null; }
|
|
29
|
+
if (this.mysqlcnf !== '') {
|
|
30
|
+
return this.mysqlcnf;
|
|
31
|
+
}
|
|
32
|
+
var cnf = [
|
|
33
|
+
'[client]',
|
|
34
|
+
"user = ".concat(this.DB_USER),
|
|
35
|
+
"password = ".concat(this.DB_PASS),
|
|
36
|
+
"host = ".concat(this.DB_HOST),
|
|
37
|
+
"port = ".concat(this.DB_PORT),
|
|
38
|
+
'',
|
|
39
|
+
];
|
|
40
|
+
cnf.push("");
|
|
41
|
+
cnfFile !== null && cnfFile !== void 0 ? cnfFile : (cnfFile = path.join(process.cwd(), '/mysql.cnf'));
|
|
42
|
+
try {
|
|
43
|
+
fs.writeFileSync(cnfFile, cnf.join('\n'));
|
|
44
|
+
fs.chmodSync(cnfFile, 488);
|
|
45
|
+
console.log("Successfully created mysql.cnf file in (".concat(cnfFile, ")"));
|
|
46
|
+
}
|
|
47
|
+
catch (error) {
|
|
48
|
+
console.error("Failed to store file contents of mysql.cnf in (".concat(process.cwd(), ")"), error);
|
|
49
|
+
process.exit(1);
|
|
50
|
+
}
|
|
51
|
+
return (this.mysqlcnf = cnfFile);
|
|
52
|
+
};
|
|
53
|
+
MySQLDump.MySQLDump = function (mysqldump, data, schemas, outputFile, otherOption, specificTable) {
|
|
54
|
+
if (mysqldump === void 0) { mysqldump = null; }
|
|
55
|
+
if (data === void 0) { data = false; }
|
|
56
|
+
if (schemas === void 0) { schemas = true; }
|
|
57
|
+
if (outputFile === void 0) { outputFile = null; }
|
|
58
|
+
if (otherOption === void 0) { otherOption = ''; }
|
|
59
|
+
if (specificTable === void 0) { specificTable = null; }
|
|
60
|
+
specificTable = specificTable || '';
|
|
61
|
+
if (outputFile === null) {
|
|
62
|
+
outputFile = path.join(process.cwd(), 'mysqldump.sql');
|
|
63
|
+
}
|
|
64
|
+
if (!data && !schemas) {
|
|
65
|
+
console.warn("MysqlDump is running with --no-create-info and --no-data. Why?");
|
|
66
|
+
}
|
|
67
|
+
var defaultsExtraFile = this.buildCNF();
|
|
68
|
+
var hexBlobOption = data ? '--hex-blob ' : '--no-data ';
|
|
69
|
+
var createInfoOption = schemas ? '' : ' --no-create-info ';
|
|
70
|
+
var cmd = "".concat(mysqldump || 'mysqldump', " --defaults-extra-file=\"").concat(defaultsExtraFile, "\" ").concat(otherOption, " --skip-add-locks --single-transaction --quick ").concat(createInfoOption).concat(hexBlobOption).concat(this.DB_NAME, " ").concat(specificTable, " > '").concat(outputFile, "'");
|
|
71
|
+
this.executeAndCheckStatus(cmd);
|
|
72
|
+
return (this.mysqldump = outputFile);
|
|
73
|
+
};
|
|
74
|
+
MySQLDump.executeAndCheckStatus = function (command, exitOnFailure, output) {
|
|
75
|
+
if (exitOnFailure === void 0) { exitOnFailure = true; }
|
|
76
|
+
if (output === void 0) { output = []; }
|
|
77
|
+
try {
|
|
78
|
+
var stdout = execSync(command, { encoding: 'utf-8' });
|
|
79
|
+
output.push(stdout);
|
|
80
|
+
}
|
|
81
|
+
catch (error) {
|
|
82
|
+
console.log("The command >> ".concat(command, " \n\t returned with a status code (").concat(error.status, "). Expecting 0 for success."));
|
|
83
|
+
console.log("Command output::\t ".concat(error.stdout));
|
|
84
|
+
if (exitOnFailure) {
|
|
85
|
+
process.exit(error.status);
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
};
|
|
89
|
+
MySQLDump.mysqlcnf = '';
|
|
90
|
+
MySQLDump.mysqldump = '';
|
|
91
|
+
MySQLDump.DB_USER = argMap['--user'] || 'root';
|
|
92
|
+
MySQLDump.DB_PASS = argMap['--pass'] || 'password';
|
|
93
|
+
MySQLDump.DB_HOST = argMap['--host'] || '127.0.0.1';
|
|
94
|
+
MySQLDump.DB_PORT = argMap['--port'] || '3306';
|
|
95
|
+
MySQLDump.DB_NAME = argMap['--dbname'] || 'carbonPHP';
|
|
96
|
+
MySQLDump.DB_PREFIX = argMap['--prefix'] || 'carbon_';
|
|
97
|
+
MySQLDump.RELATIVE_OUTPUT_DIR = argMap['--output'] || '/src/api/rest';
|
|
98
|
+
MySQLDump.OUTPUT_DIR = path.join(process.cwd(), MySQLDump.RELATIVE_OUTPUT_DIR);
|
|
99
|
+
return MySQLDump;
|
|
100
|
+
}());
|
|
101
|
+
createDirIfNotExists(MySQLDump.OUTPUT_DIR);
|
|
102
|
+
var pathRuntimeReference = MySQLDump.RELATIVE_OUTPUT_DIR.replace(/(^\/(src\/)?)|(\/+$)/g, '');
|
|
103
|
+
// Usage example
|
|
104
|
+
var dumpFileLocation = MySQLDump.MySQLDump();
|
|
105
|
+
function capitalizeFirstLetter(string) {
|
|
106
|
+
return string.charAt(0).toUpperCase() + string.slice(1);
|
|
107
|
+
}
|
|
108
|
+
function determineTypeScriptType(mysqlType) {
|
|
109
|
+
switch (mysqlType.toLowerCase()) {
|
|
110
|
+
case 'varchar':
|
|
111
|
+
case 'text':
|
|
112
|
+
case 'char':
|
|
113
|
+
case 'datetime':
|
|
114
|
+
case 'timestamp':
|
|
115
|
+
case 'date':
|
|
116
|
+
return 'string';
|
|
117
|
+
case 'int':
|
|
118
|
+
case 'bigint':
|
|
119
|
+
case 'smallint':
|
|
120
|
+
case 'decimal':
|
|
121
|
+
case 'float':
|
|
122
|
+
case 'double':
|
|
123
|
+
return 'number';
|
|
124
|
+
case 'boolean':
|
|
125
|
+
case 'tinyint(1)':
|
|
126
|
+
return 'boolean';
|
|
127
|
+
case 'json':
|
|
128
|
+
return 'any'; // or 'object' based on usage
|
|
129
|
+
default:
|
|
130
|
+
return 'string';
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
var parseSQLToTypeScript = function (sql) {
|
|
134
|
+
var e_1, _a, e_2, _b;
|
|
135
|
+
var tableMatches = sql.matchAll(/CREATE\s+TABLE\s+`?(\w+)`?\s+\(((.|\n)+?)\)\s*(ENGINE=.+?);/gm);
|
|
136
|
+
var tableData = {};
|
|
137
|
+
var references = [];
|
|
138
|
+
var _loop_1 = function (tableMatch) {
|
|
139
|
+
var tableName = tableMatch[1];
|
|
140
|
+
var columnDefinitions = tableMatch[2];
|
|
141
|
+
var columns = {};
|
|
142
|
+
var columnRegex = /^\s*`(\w+)` (\w+)(?:\((\d+)\))?( NOT NULL)?( AUTO_INCREMENT)?(?: DEFAULT '(\w+)')?/g;
|
|
143
|
+
var columnMatch = void 0;
|
|
144
|
+
while ((columnMatch = columnRegex.exec(columnDefinitions))) {
|
|
145
|
+
columns[columnMatch[1]] = {
|
|
146
|
+
type: columnMatch[2],
|
|
147
|
+
length: columnMatch[3] || '',
|
|
148
|
+
notNull: !!columnMatch[4],
|
|
149
|
+
autoIncrement: !!columnMatch[5],
|
|
150
|
+
defaultValue: columnMatch[6] || '',
|
|
151
|
+
};
|
|
152
|
+
}
|
|
153
|
+
// Extract primary keys
|
|
154
|
+
var primaryKeyMatch = columnDefinitions.match(/PRIMARY KEY \(([^)]+)\)/i);
|
|
155
|
+
var primaryKeys = primaryKeyMatch
|
|
156
|
+
? primaryKeyMatch[1].split(',').map(function (key) { return key.trim().replace(/`/g, ''); })
|
|
157
|
+
: [];
|
|
158
|
+
// Extract foreign keys
|
|
159
|
+
var foreignKeyRegex = /CONSTRAINT `([^`]+)` FOREIGN KEY \(`([^`]+)`\) REFERENCES `([^`]+)` \(`([^`]+)`\)( ON DELETE (\w+))?( ON UPDATE (\w+))?/g;
|
|
160
|
+
var foreignKeyMatch = void 0;
|
|
161
|
+
while ((foreignKeyMatch = foreignKeyRegex.exec(columnDefinitions))) {
|
|
162
|
+
var constraintName = foreignKeyMatch[1];
|
|
163
|
+
var localColumn = foreignKeyMatch[2];
|
|
164
|
+
var foreignTable = foreignKeyMatch[3];
|
|
165
|
+
var foreignColumn = foreignKeyMatch[4];
|
|
166
|
+
var onDeleteAction = foreignKeyMatch[6] || null;
|
|
167
|
+
var onUpdateAction = foreignKeyMatch[8] || null;
|
|
168
|
+
references.push({
|
|
169
|
+
TABLE: tableName,
|
|
170
|
+
CONSTRAINT: constraintName,
|
|
171
|
+
FOREIGN_KEY: localColumn,
|
|
172
|
+
REFERENCES: "".concat(foreignTable, ".").concat(foreignColumn),
|
|
173
|
+
ON_DELETE: onDeleteAction,
|
|
174
|
+
ON_UPDATE: onUpdateAction
|
|
175
|
+
});
|
|
176
|
+
}
|
|
177
|
+
var tsModel = {
|
|
178
|
+
RELATIVE_OUTPUT_DIR: pathRuntimeReference,
|
|
179
|
+
TABLE_NAME: tableName,
|
|
180
|
+
TABLE_DEFINITION: tableMatch[0],
|
|
181
|
+
TABLE_CONSTRAINT: references,
|
|
182
|
+
TABLE_NAME_SHORT: tableName.replace(MySQLDump.DB_PREFIX, ''),
|
|
183
|
+
TABLE_NAME_LOWER: tableName.toLowerCase(),
|
|
184
|
+
TABLE_NAME_UPPER: tableName.toUpperCase(),
|
|
185
|
+
TABLE_NAME_PASCAL_CASE: tableName.split('_').map(capitalizeFirstLetter).join('_'),
|
|
186
|
+
TABLE_NAME_SHORT_PASCAL_CASE: tableName.replace(MySQLDump.DB_PREFIX, '').split('_').map(capitalizeFirstLetter).join('_'),
|
|
187
|
+
PRIMARY: primaryKeys.map(function (pk) { return "".concat(tableName, ".").concat(pk); }),
|
|
188
|
+
PRIMARY_SHORT: primaryKeys,
|
|
189
|
+
COLUMNS: {},
|
|
190
|
+
COLUMNS_UPPERCASE: {},
|
|
191
|
+
TYPE_VALIDATION: {},
|
|
192
|
+
REGEX_VALIDATION: {},
|
|
193
|
+
TABLE_REFERENCES: {},
|
|
194
|
+
TABLE_REFERENCED_BY: {},
|
|
195
|
+
};
|
|
196
|
+
for (var colName in columns) {
|
|
197
|
+
tsModel.COLUMNS["".concat(tableName, ".").concat(colName)] = colName;
|
|
198
|
+
tsModel.COLUMNS_UPPERCASE[colName.toUpperCase()] = tableName + '.' + colName;
|
|
199
|
+
var typescript_type = determineTypeScriptType(columns[colName].type.toLowerCase()) === "number" ? "number" : "string";
|
|
200
|
+
tsModel.TYPE_VALIDATION["".concat(tableName, ".").concat(colName)] = {
|
|
201
|
+
COLUMN_NAME: colName,
|
|
202
|
+
MYSQL_TYPE: columns[colName].type.toLowerCase(),
|
|
203
|
+
TYPESCRIPT_TYPE: typescript_type,
|
|
204
|
+
TYPESCRIPT_TYPE_IS_STRING: 'string' === typescript_type,
|
|
205
|
+
TYPESCRIPT_TYPE_IS_NUMBER: 'number' === typescript_type,
|
|
206
|
+
MAX_LENGTH: columns[colName].length,
|
|
207
|
+
AUTO_INCREMENT: columns[colName].autoIncrement,
|
|
208
|
+
SKIP_COLUMN_IN_POST: !columns[colName].notNull && !columns[colName].defaultValue,
|
|
209
|
+
};
|
|
210
|
+
}
|
|
211
|
+
tableData[tableName] = tsModel;
|
|
212
|
+
};
|
|
213
|
+
try {
|
|
214
|
+
// @ts-ignore
|
|
215
|
+
for (var tableMatches_1 = __values(tableMatches), tableMatches_1_1 = tableMatches_1.next(); !tableMatches_1_1.done; tableMatches_1_1 = tableMatches_1.next()) {
|
|
216
|
+
var tableMatch = tableMatches_1_1.value;
|
|
217
|
+
_loop_1(tableMatch);
|
|
218
|
+
}
|
|
219
|
+
}
|
|
220
|
+
catch (e_1_1) { e_1 = { error: e_1_1 }; }
|
|
221
|
+
finally {
|
|
222
|
+
try {
|
|
223
|
+
if (tableMatches_1_1 && !tableMatches_1_1.done && (_a = tableMatches_1.return)) _a.call(tableMatches_1);
|
|
224
|
+
}
|
|
225
|
+
finally { if (e_1) throw e_1.error; }
|
|
226
|
+
}
|
|
227
|
+
try {
|
|
228
|
+
for (var references_1 = __values(references), references_1_1 = references_1.next(); !references_1_1.done; references_1_1 = references_1.next()) {
|
|
229
|
+
var ref = references_1_1.value;
|
|
230
|
+
var foreignTable = ref.REFERENCES.split('.')[0];
|
|
231
|
+
var foreignColumn = ref.REFERENCES.split('.')[1];
|
|
232
|
+
var tableName = ref.TABLE;
|
|
233
|
+
var columnName = ref.FOREIGN_KEY;
|
|
234
|
+
var constraintName = ref.CONSTRAINT;
|
|
235
|
+
if (!tableData[foreignTable]) {
|
|
236
|
+
console.log("Foreign table ".concat(foreignTable, " not found for ").concat(ref.TABLE, ".").concat(ref.CONSTRAINT));
|
|
237
|
+
continue;
|
|
238
|
+
}
|
|
239
|
+
if (!tableData[foreignTable].TABLE_REFERENCED_BY) {
|
|
240
|
+
tableData[foreignTable].TABLE_REFERENCED_BY = {};
|
|
241
|
+
}
|
|
242
|
+
if (!tableData[foreignTable].TABLE_REFERENCED_BY[foreignColumn]) {
|
|
243
|
+
tableData[foreignTable].TABLE_REFERENCED_BY[foreignColumn] = [];
|
|
244
|
+
}
|
|
245
|
+
tableData[foreignTable].TABLE_REFERENCED_BY[foreignColumn].push({
|
|
246
|
+
TABLE: tableName,
|
|
247
|
+
COLUMN: columnName,
|
|
248
|
+
CONSTRAINT: constraintName
|
|
249
|
+
});
|
|
250
|
+
if (!tableData[tableName].TABLE_REFERENCES) {
|
|
251
|
+
tableData[tableName].TABLE_REFERENCES = {};
|
|
252
|
+
}
|
|
253
|
+
if (!tableData[tableName].TABLE_REFERENCES[columnName]) {
|
|
254
|
+
tableData[tableName].TABLE_REFERENCES[columnName] = [];
|
|
255
|
+
}
|
|
256
|
+
tableData[tableName].TABLE_REFERENCES[columnName].push({
|
|
257
|
+
TABLE: foreignTable,
|
|
258
|
+
COLUMN: foreignColumn,
|
|
259
|
+
CONSTRAINT: constraintName
|
|
260
|
+
});
|
|
261
|
+
}
|
|
262
|
+
}
|
|
263
|
+
catch (e_2_1) { e_2 = { error: e_2_1 }; }
|
|
264
|
+
finally {
|
|
265
|
+
try {
|
|
266
|
+
if (references_1_1 && !references_1_1.done && (_b = references_1.return)) _b.call(references_1);
|
|
267
|
+
}
|
|
268
|
+
finally { if (e_2) throw e_2.error; }
|
|
269
|
+
}
|
|
270
|
+
var tables = Object.values(tableData);
|
|
271
|
+
return {
|
|
272
|
+
TABLES: tables,
|
|
273
|
+
RestTableNames: tables.map(function (table) { return "'" + table.TABLE_NAME + "'"; }).join('\n | '),
|
|
274
|
+
RestShortTableNames: tables.map(function (table) { return "'" + table.TABLE_NAME_SHORT + "'"; }).join('\n | '),
|
|
275
|
+
RestTableInterfaces: tables.map(function (table) { return 'i' + table.TABLE_NAME_SHORT_PASCAL_CASE; }).join('\n | '),
|
|
276
|
+
};
|
|
277
|
+
};
|
|
278
|
+
// use dumpFileLocation to get sql
|
|
279
|
+
var sql = fs.readFileSync(dumpFileLocation, 'utf-8');
|
|
280
|
+
var tableData = parseSQLToTypeScript(sql);
|
|
281
|
+
// write to file
|
|
282
|
+
fs.writeFileSync(path.join(process.cwd(), 'C6MySqlDump.json'), JSON.stringify(tableData));
|
|
283
|
+
// import this file src/assets/handlebars/C6.tsx.handlebars for a mustache template
|
|
284
|
+
var template = fs.readFileSync(path.resolve(__dirname, 'assets/handlebars/C6.tsx.handlebars'), 'utf-8');
|
|
285
|
+
fs.writeFileSync(path.join(MySQLDump.OUTPUT_DIR, 'C6.tsx'), Handlebars.compile(template)(tableData));
|
|
286
|
+
var testTemplate = fs.readFileSync(path.resolve(__dirname, 'assets/handlebars/Tests.tsx.handlebars'), 'utf-8');
|
|
287
|
+
Object.values(tableData.TABLES).map(function (tableData, key) {
|
|
288
|
+
var tableName = tableData.TABLE_NAME_SHORT;
|
|
289
|
+
fs.writeFileSync(path.join(MySQLDump.OUTPUT_DIR, tableName + '.tsx'), Handlebars.compile(testTemplate)(tableData));
|
|
290
|
+
});
|
|
291
|
+
console.log('Successfully created CarbonORM bindings!');
|
|
@@ -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
|
+
|
|
@@ -22,6 +22,12 @@ 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[],
|
|
@@ -29,4 +35,6 @@ export interface C6RestfulModel<RestShortTableNames extends string = string> {
|
|
|
29
35
|
COLUMNS: stringMap,
|
|
30
36
|
REGEX_VALIDATION: RegExpMap,
|
|
31
37
|
TYPE_VALIDATION: {[key: string]: iTypeValidation},
|
|
38
|
+
TABLE_REFERENCES: {[columnName: string]: iConstraint[]},
|
|
39
|
+
TABLE_REFERENCED_BY: {[columnName: string]: iConstraint[]}
|
|
32
40
|
}
|