Skip to content

Commit 666e274

Browse files
committed
Merge branch 'WIP/specify_settings_filepath'
2 parents cc37592 + d33f513 commit 666e274

File tree

7 files changed

+110
-46
lines changed

7 files changed

+110
-46
lines changed

.gitignore

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,4 @@
11
node_modules
2-
custom_schema.json
3-
schema.json
42
dist
53
lib
64
coverage

.npmignore

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3,8 +3,6 @@ src
33
.gitignore
44
tsconfig.json
55
tslint.json
6-
custom_schema.json
7-
schema.json
86
coverage
97
test
10-
settings
8+
settings

README.md

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
This is a tool to easily fill a SQL database.
22
It is able to analyse a schema and generate a `settings/schema.jsonc` which will be used to generate accurate data. It does its best to handle foreign keys.
3-
You can provide a `settings/custom_schema.jsonc` to customize `settings/schema.jsonc` during the generation phase. This will allow you to override datatype for a column, force the use of a foreign key
3+
You can provide a `settings/schema_custom.jsonc` to customize `settings/schema.jsonc` during the generation phase. This will allow you to override datatype for a column, force the use of a foreign key
44
or specify a list of values.
55

66
## functionalities
@@ -16,13 +16,13 @@ or specify a list of values.
1616

1717
The first step is to analyse your database to generate a `settings/schema.jsonc` by providing database credentials:
1818

19-
```
20-
npm install -g @corteks/mysql-data-generator
19+
The `schema` parameter allows you to specify a name for the output files and differentiate between multiple schemas and setups.
2120

22-
mysqldatagen --db mysql://user:password@127.0.0.1:3306/database --analyse
21+
```
22+
npx @corteks/mysql-data-generator --db mysql://user:password@127.0.0.1:3306/database --analyse --schema schema
2323
```
2424

25-
If you want to customize the schema, modify the default `settings/custom_schema.jsonc` that has also be generated.
25+
If you want to customize the schema, modify the default `settings/schema_custom.jsonc` that has also be generated.
2626

2727
## 2. Data generation
2828

@@ -42,7 +42,7 @@ For every tables listed in `settings/schema.jsonc`, the tool will:
4242
- insert rows until it reaches the defined table limit
4343
- columns in table are ordered accordingly to your custom schema so you can rely on other column value in the same row.
4444

45-
Available options in `custom_schema.json`:
45+
Available options in `schema_custom.json`:
4646

4747
- `settings`: Global settings
4848
- `disableTriggers: boolean` // disable triggers per table during process and recreate them afterward

src/database/mariadb-connector.ts

Lines changed: 10 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,12 @@
1+
import * as fs from 'fs-extra';
12
import Knex from 'knex';
23
import { getLogger } from 'log4js';
3-
import * as fs from 'fs-extra';
4+
import { Connection, MysqlError } from 'mysql';
45
import * as path from 'path';
5-
import { Table, Column, Schema } from '../schema/schema.class';
6-
import { DatabaseConnector } from './database-connector-builder';
7-
import { Generators } from '../generation/generators/generators';
86
import * as URI from 'uri-js';
9-
import { Connection, MysqlError } from 'mysql';
7+
import { Generators } from '../generation/generators/generators';
8+
import { Column, Schema, Table } from '../schema/schema.class';
9+
import { DatabaseConnector } from './database-connector-builder';
1010

1111
export class MariaDBConnector implements DatabaseConnector {
1212
private dbConnection: Knex;
@@ -84,7 +84,7 @@ export class MariaDBConnector implements DatabaseConnector {
8484

8585
async insert(table: string, rows: any[]): Promise<number> {
8686
if (rows.length === 0) return 0;
87-
const query = await this.dbConnection(table)
87+
const query = this.dbConnection(table)
8888
.insert(rows)
8989
.toQuery()
9090
.replace('insert into', 'insert ignore into');
@@ -114,7 +114,7 @@ export class MariaDBConnector implements DatabaseConnector {
114114
.filter((column: MySQLColumn) => {
115115
return ['enum', 'set'].includes(column.DATA_TYPE || '');
116116
}).forEach((column: MySQLColumn) => {
117-
column.NUMERIC_PRECISION = column.COLUMN_TYPE.match(/[enum,set]\((.*)\)$/)![1].split('\',\'').length;
117+
column.NUMERIC_PRECISION = column.COLUMN_TYPE.match(/(enum|set)\((.*)\)$/)![1].split('\',\'').length;
118118
});
119119

120120
table.columns = columns.map((mysqlColumn: MySQLColumn) => {
@@ -293,16 +293,15 @@ export class MariaDBConnector implements DatabaseConnector {
293293
.andWhere('t.TABLE_TYPE', 'BASE TABLE')
294294
.groupBy('t.TABLE_SCHEMA', 't.TABLE_NAME');
295295

296-
const tables = tableNames.map((row) => {
296+
return tableNames.map((row) => {
297297
const table = new Table();
298298
table.name = row.name;
299299
return table;
300300
});
301-
return tables;
302301
}
303302

304303
async getColumnsInformation(table: Table) {
305-
return await this.dbConnection.select()
304+
return this.dbConnection.select()
306305
.from('information_schema.COLUMNS')
307306
.where({
308307
'TABLE_SCHEMA': this.database,
@@ -329,8 +328,7 @@ export class MariaDBConnector implements DatabaseConnector {
329328
.having(this.dbConnection.raw('count(kcu2.CONSTRAINT_NAME) < 2'))
330329
.as('indexes');
331330

332-
333-
const foreignKeys = await this.dbConnection.select([
331+
return this.dbConnection.select([
334332
'kcu.column_name AS column',
335333
'kcu.referenced_table_name AS foreignTable',
336334
'kcu.referenced_column_name AS foreignColumn',
@@ -344,8 +342,6 @@ export class MariaDBConnector implements DatabaseConnector {
344342
})
345343
.where('kcu.table_name', table.name)
346344
.whereNotNull('kcu.referenced_column_name');
347-
348-
return foreignKeys;
349345
}
350346

351347
async getValuesForForeignKeys(

src/main.ts

Lines changed: 21 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -22,12 +22,15 @@ class Main extends CliMainClass {
2222
@CliParameter({ alias: 'db', demandOption: true, description: 'Database URI. Eg: mysql://user:password@127.0.0.1:3306/database' })
2323
private uri: string | undefined = undefined;
2424

25-
@CliParameter()
25+
@CliParameter({ description: 'Extrat schema information and generate default settings' })
2626
private analyse: boolean = false;
2727

28-
@CliParameter()
28+
@CliParameter({ description: 'Empty tables before filling them' })
2929
private reset: boolean = false;
3030

31+
@CliParameter({ description: 'Schema filename to use. Will be generated with --analyse' })
32+
private schema: string = 'schema';
33+
3134
private dbConnector: DatabaseConnector | undefined;
3235
private filler: Filler | undefined;
3336

@@ -38,7 +41,7 @@ class Main extends CliMainClass {
3841
try {
3942
this.dbConnector = await dbConnectorBuilder.build();
4043
} catch (err) {
41-
logger.error(err.message);
44+
logger.error((err as Error).message);
4245
return 1;
4346
}
4447
if (!fs.pathExistsSync('settings')) {
@@ -50,12 +53,12 @@ class Main extends CliMainClass {
5053
try {
5154
if (this.analyse) {
5255
return await this.generateSchemaFromDB();
53-
};
56+
}
5457

5558
await this.generateData();
5659
} catch (ex) {
57-
if (ex.code === 'ENOENT') {
58-
logger.error('Unable to read from ./settings/schema.json. Please run with --analyse first.');
60+
if ((ex as any).code === 'ENOENT') {
61+
logger.error(`Unable to read from ./settings/${this.schema}.json. Please run with --analyse first.`);
5962
} else {
6063
logger.error(ex);
6164
}
@@ -69,21 +72,23 @@ class Main extends CliMainClass {
6972
async generateSchemaFromDB() {
7073
if (!this.dbConnector) throw new Error('DB connection not ready');
7174
const schema = await this.dbConnector.getSchema();
72-
fs.writeJSONSync(path.join('settings', 'schema.json'), schema.toJSON(), { spaces: 4 });
73-
if (!fs.existsSync(path.join('settings', 'custom_schema.jsonc'))) {
75+
fs.writeJSONSync(path.join('settings', `${this.schema}.json`), schema.toJSON(), { spaces: 4 });
76+
if (!fs.existsSync(path.join('settings', `${this.schema}_custom.jsonc`))) {
7477
const customSchema = new CustomSchema();
75-
fs.writeJSONSync(path.join('settings', 'custom_schema.jsonc'), customSchema, { spaces: 4 });
78+
fs.writeJSONSync(path.join('settings', `${this.schema}_custom.jsonc`), customSchema, { spaces: 4 });
7679
}
7780
return 0;
7881
}
7982

8083
async runScripts() {
8184
if (!this.dbConnector) throw new Error('DB connection not ready');
82-
if (!fs.existsSync(path.join('settings', 'scripts'))) {
85+
const scriptsFolder = path.join('settings', 'scripts');
86+
if (!fs.existsSync(scriptsFolder)) {
87+
fs.mkdirSync(scriptsFolder);
8388
logger.info('No scripts provided.');
8489
return;
8590
}
86-
const scripts = fs.readdirSync(path.join('settings', 'scripts'));
91+
const scripts = fs.readdirSync(scriptsFolder);
8792
if (scripts.length === 0) {
8893
logger.info('No scripts provided.');
8994
return;
@@ -101,18 +106,18 @@ class Main extends CliMainClass {
101106

102107
async generateData() {
103108
if (!this.dbConnector) throw new Error('DB connection not ready');
104-
const schema: Schema = await Schema.fromJSON(fs.readJSONSync(path.join('settings', 'schema.json')));
109+
const schema: Schema = await Schema.fromJSON(fs.readJSONSync(path.join('settings', `${this.schema}.json`)));
105110
let customSchema: CustomSchema = new CustomSchema();
106111
try {
107-
customSchema = JSONC.parse(fs.readFileSync(path.join('settings', 'custom_schema.jsonc')).toString());
112+
customSchema = JSONC.parse(fs.readFileSync(path.join('settings', `${this.schema}_custom.jsonc`)).toString());
108113
} catch (ex) {
109-
logger.warn('Unable to read ./settings/custom_schema.json, this will not take any customization into account.');
114+
logger.warn(`Unable to read ./settings/${this.schema}_custom.json, this will not take any customization into account.`);
110115
}
111116
try {
112117
await this.runScripts();
113118
} catch (ex) {
114119
logger.error('An error occured while running scripts:');
115-
logger.error(ex.message);
120+
logger.error((ex as Error).message);
116121
return;
117122
}
118123
const customizedSchema = CustomizedSchema.create(schema, customSchema);
@@ -129,7 +134,7 @@ class Main extends CliMainClass {
129134
return (event: ProgressEvent) => {
130135
let diff = false;
131136
if (previousEvent.currentTable !== event.currentTable) {
132-
console.log(colors.green(event.currentTable));
137+
logger.info(colors.green(event.currentTable));
133138
diff = true;
134139
}
135140
if (previousEvent.step !== event.step) {

src/schema/custom-schema.class.ts

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,9 +21,13 @@ export class CustomSettings {
2121
@IsArray()
2222
@ValidateNested({ each: true })
2323
tablesToFill: string[] = [];
24+
/**
25+
* A default value has been provided to maxLengthValue as this can drastically improve performances. This is a parameter which used to be
26+
* defined a lot by user anyway. The value of 36 has been chosen to allow usage of UID in string generator.
27+
*/
2428
@IsNumber()
2529
@IsOptional()
26-
maxLengthValue?: number;
30+
maxLengthValue?: number = 36;
2731
values: { [key: string]: any[]; } = {};
2832
options: {
2933
generators: Generators[],

test/schema/customized-schema.test.ts

Lines changed: 67 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
1+
import { Builder } from '../../src/builder';
2+
import { Generators } from '../../src/generation/generators/generators';
13
import { CustomSchema } from '../../src/schema/custom-schema.class';
2-
import { Schema, Table, Column } from '../../src/schema/schema.class';
34
import { CustomizedSchema } from '../../src/schema/customized-schema.class';
4-
import { Generators } from '../../src/generation/generators/generators';
5-
import { Builder } from '../../src/builder';
5+
import { Column, Schema, Table } from '../../src/schema/schema.class';
66

77
describe('CustomizedSchema', () => {
88
it('handle missing custom table', async () => {
@@ -94,6 +94,69 @@ describe('CustomizedSchema', () => {
9494
schema.tables = [table];
9595

9696
const customSchema = new CustomSchema();
97+
customSchema.tables = [{
98+
name: 'table1',
99+
maxLines: 100,
100+
columns: [
101+
{
102+
name: 'column1',
103+
max: 10,
104+
values: [],
105+
},
106+
],
107+
}];
108+
const result = CustomizedSchema.create(schema, customSchema);
109+
expect(result.tables[0].columns[0].max).toBe(10);
110+
});
111+
it('Column options takes in account default maxLengthValue', async () => {
112+
const column = new Builder(Column)
113+
.set('name', 'column1')
114+
.set('generator', Generators.string)
115+
.build();
116+
117+
const table = new Builder(Table)
118+
.set('name', 'table1')
119+
.set('columns', [
120+
column,
121+
])
122+
.build();
123+
124+
const schema = new Schema();
125+
schema.tables = [table];
126+
127+
const customSchema = new CustomSchema();
128+
customSchema.tables = [{
129+
name: 'table1',
130+
maxLines: 100,
131+
columns: [
132+
{
133+
name: 'column1',
134+
max: 100,
135+
values: [],
136+
},
137+
],
138+
}];
139+
const result = CustomizedSchema.create(schema, customSchema);
140+
expect(result.tables[0].columns[0].max).toBe(36);
141+
});
142+
it('Column options do not override maxLengthValue', async () => {
143+
const column = new Builder(Column)
144+
.set('name', 'column1')
145+
.set('generator', Generators.string)
146+
.build();
147+
148+
const table = new Builder(Table)
149+
.set('name', 'table1')
150+
.set('columns', [
151+
column,
152+
])
153+
.build();
154+
155+
const schema = new Schema();
156+
schema.tables = [table];
157+
158+
const customSchema = new CustomSchema();
159+
customSchema.settings.maxLengthValue = 42;
97160
customSchema.tables = [{
98161
name: 'table1',
99162
maxLines: 100,
@@ -106,7 +169,7 @@ describe('CustomizedSchema', () => {
106169
],
107170
}];
108171
const result = CustomizedSchema.create(schema, customSchema);
109-
expect(result.tables[0].columns[0].max).toBe(100);
172+
expect(result.tables[0].columns[0].max).toBe(42);
110173
});
111174
it('reorder columns', async () => {
112175
const columns = [

0 commit comments

Comments
 (0)