Skip to content

Commit

Permalink
Release v1.0.3
Browse files Browse the repository at this point in the history
  • Loading branch information
noorzaie committed Dec 5, 2021
1 parent 852ea15 commit 70eef03
Show file tree
Hide file tree
Showing 12 changed files with 116 additions and 50 deletions.
6 changes: 2 additions & 4 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,6 +1,4 @@
test.ts
test.js
node_modules
output
.idea/
.idea
*.js
*.d.ts
4 changes: 4 additions & 0 deletions .npmignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
# Created by .ignore support plugin (hsz.mobi)
.idea
*.ts
!*.d.ts
48 changes: 48 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
When you create a web application, you need to validate client-side inputs, most of inputs are same as database fields and you need to create validation schema manually. With `sqema` you can automatically export your sql tables to [json-schema](https://json-schema.org/) format.

## Installation
```npm
npm install sqema
```

## Usage
### In scripts
```typescript
import SQLToJsonSchemaConvertor from 'sqema';

const convertor = new SQLToJsonSchemaConvertor(
'postgres',
{ host: 'localhost', port: 5432, database: '***', username: '***', password: '***'}
);

convertor.generateJsonSchemas()
.then(() => {
// This will write json schemas to output directory
convertor.writeJsonSchemas('output', 'single-file', 'json');
});
```

#### `writeJsonSchemas` Options
| Option | Description | Possible values | Default value |
| ------------- | ------------- | ------------- | ------------- |
| path | Path of a directory to write schemas | Any string | output |
| granularity | Scale of data to be written | `single-file`: Write all schemas in single file <br> `schema`: Write each schema in separate files <br> `table`: Write each table in separate file <br> `field`: Write each field in separate file | single-file |
| format | Format of output | `json` <br> `js` <br> `ts` | json |

### Using cli
You can use `sqma` command with following options:
```
Options:
-d, --dialect <dialect> database dialect (choices: "postgres", default: "postgres")
-h, --host <host> database host
-port, --port <port> database port
-db, --database <database> database name
-u, --username <username> database username
-p, --path <path> output folder (default: "output")
-g, --granularity <granularity> output files granularity (choices: "single-file", "schema", "table", "field", default: "single-file")
-f, --format <format> output format (choices: "json", "js", "ts", default: "json")
--help display help for command
```

## Note
This library currently supports `postgresql` database and also some complex data types not implemented yet.
6 changes: 5 additions & 1 deletion cli/cli.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,8 @@ import SQLToJsonSchemaConvertor from '../index';
program
.version('1.0.0')
.addOption(new Option('-d, --dialect <dialect>', 'database dialect').default('postgres').choices([ 'postgres' ]))
.addOption(new Option('-h, --host <host>', 'database host'))
.addOption(new Option('-port, --port <port>', 'database port'))
.requiredOption('-db, --database <database>', 'database name')
.requiredOption('-u, --username <username>', 'database username')
.addOption(new Option('-p, --path <path>', 'output folder').default('output'))
Expand All @@ -32,12 +34,14 @@ import SQLToJsonSchemaConvertor from '../index';
const convertor = new SQLToJsonSchemaConvertor(
options.dialect,
{
host: options.host,
port: options.port,
database: options.database,
username: options.username,
password: options.password
}
);

convertor.generateJsonSchemas();
await convertor.generateJsonSchemas();
convertor.writeJsonSchemas(options.path, options.granularity, options.format);
})();
15 changes: 4 additions & 11 deletions dialects/postgres/DB.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,8 @@
import { ColumnType, DatabaseConfigType, DBClassType, DefinitionsType } from '../../types';
import pgStructure, { BaseType, Db, EnumType } from 'pg-structure';
import { DatabaseConfigType, DBClassType, DefinitionsType } from '../../types';
import pgStructure, { Db, EnumType } from 'pg-structure';
import Schema from 'pg-structure/dist/pg-structure/schema';
import Entity from 'pg-structure/dist/pg-structure/base/entity';
import Column from 'pg-structure/dist/pg-structure/column';
import Type from 'pg-structure/dist/pg-structure/base/type';
import {
getBooleanRule,
getDateTimeRule,
Expand All @@ -12,7 +11,7 @@ import {
getNumberRule,
getStringRule
} from '../../json-schema/rules';
import { JSONSchema7, JSONSchema7Object, JSONSchema7Type } from 'json-schema';
import { JSONSchema7Object, JSONSchema7Type } from 'json-schema';

class DB implements DBClassType{
private db: Db;
Expand All @@ -24,7 +23,7 @@ class DB implements DBClassType{
public connect = async () => {
this.schemas = this.config.schemas || [ 'public' ];
this.db = await pgStructure(
{ database: this.config.database, user: this.config.username, password: this.config.password },
{ host: this.config.host, port: this.config.port, database: this.config.database, user: this.config.username, password: this.config.password },
{ includeSchemas: this.schemas }
);
}
Expand All @@ -43,10 +42,6 @@ class DB implements DBClassType{
}

private getTableColumns = (table: Entity): JSONSchema7Type => {
// console.log(table.columns);
// return table.columns.map(({ name, type, length, comment, default: defaultValue, notNull, arrayDimension }): ColumnType => ({
// name, type: this.getTypeObject(type), length, comment, notNull
// }));
const rules: JSONSchema7Object = {};
for (const column of table.columns) {
rules[column.name] = this.getTypeObject(column);
Expand All @@ -55,14 +50,12 @@ class DB implements DBClassType{
}

private getTypeObject = (column: Column): JSONSchema7Type => {
// console.log(column);
// https://www.postgresql.org/docs/current/catalog-pg-type.html#CATALOG-TYPCATEGORY-TABLE
// https://ajv.js.org/json-schema.html#json-data-type
// https://www.pg-structure.com/nav.02.api/classes/type.html#hierarchy
// https://json-schema.org/learn/getting-started-step-by-step.html
// https://json-schema.org/understanding-json-schema/reference/string.html#format
let rule: JSONSchema7Type = {};
// console.log(column.name, column.type.category);
switch (column.type.category) {
case 'A':
break;
Expand Down
16 changes: 16 additions & 0 deletions examples/test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
import SQLToJsonSchemaConvertor from '../index';

const convertor = new SQLToJsonSchemaConvertor('postgres', { host: 'localhost', port: 5432, database: 'test_db', username: 'postgres', password: 'postgres'});

// Generate all possible outputs and write them in output directory
convertor.generateJsonSchemas()
.then(() => {
const granularities = [ 'single-file', 'schema', 'table', 'field' ];
const outputFormats = [ 'json', 'js', 'ts' ];
for (const granularity of granularities) {
for (const format of outputFormats) {
// @ts-ignore
convertor.writeJsonSchemas(`output/${granularity}/${format}`, granularity, format);
}
}
});
26 changes: 15 additions & 11 deletions index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,8 @@ type FilesPathsType = { name: string; path: string; data: Object; }[];

class SQLToJsonSchemaConvertor {
private db: DBClassType;
private dbName: string;
private schema: DefinitionsType;
private readonly dbName: string;
private _schema: DefinitionsType;

constructor(dialect: DialectType, databaseConfig: DatabaseConfigType) {
switch (dialect) {
Expand All @@ -22,10 +22,14 @@ class SQLToJsonSchemaConvertor {

public generateJsonSchemas = async () => {
await this.db.connect();
this.schema = this.db.getColumns();
this._schema = this.db.getColumns();
};

public writeJsonSchemas = (path: string, granularity: 'single-file' | 'schema' | 'table' | 'field', format: 'json' | 'js' | 'ts') => {
get schema() {
return this._schema;
}

public writeJsonSchemas = (path: string, granularity: 'single-file' | 'schema' | 'table' | 'field' = 'single-file', format: 'json' | 'js' | 'ts' = 'json') => {
createDirectory(path);
let files: FilesPathsType = [];
let hasMultipleSchemas = false;
Expand All @@ -36,12 +40,12 @@ class SQLToJsonSchemaConvertor {
{
name: this.dbName,
path,
data: this.schema
data: this._schema
}
];
break;
case 'schema':
files = Object.entries(this.schema).map(([ schema, data ]) => ({
files = Object.entries(this._schema).map(([ schema, data ]) => ({
name: schema,
path,
data
Expand All @@ -50,8 +54,8 @@ class SQLToJsonSchemaConvertor {
break;
case 'table':
files = [];
hasMultipleSchemas = Object.keys(this.schema).length > 1;
for (const [ schema, tables ] of Object.entries(this.schema)) {
hasMultipleSchemas = Object.keys(this._schema).length > 1;
for (const [ schema, tables ] of Object.entries(this._schema)) {
const p = hasMultipleSchemas ? joinPaths(path, schema) : path;
createDirectory(p);
files = [
Expand All @@ -66,8 +70,8 @@ class SQLToJsonSchemaConvertor {
break;
case 'field':
files = [];
hasMultipleSchemas = Object.keys(this.schema).length > 1;
for (const [ schema, tables ] of Object.entries(this.schema)) {
hasMultipleSchemas = Object.keys(this._schema).length > 1;
for (const [ schema, tables ] of Object.entries(this._schema)) {
for (const [ table, fields ] of Object.entries(tables)) {
const p = hasMultipleSchemas ? joinPaths(path, schema, table) : joinPaths(path, table);
createDirectory(p);
Expand All @@ -90,7 +94,7 @@ class SQLToJsonSchemaConvertor {

private writeFiles = (files: FilesPathsType, format: 'json' | 'js' | 'ts') => {
for (const file of files) {
console.log(`Writing ${file.name}.${format} to ${joinPaths(process.cwd(), file.path)} ...`);
console.log(`Sqema: Writing ${file.name}.${format} to ${joinPaths(process.cwd(), file.path)} ...`);
switch (format) {
case 'js':
writeJsToFile(file.path, file.name, file.data);
Expand Down
4 changes: 0 additions & 4 deletions json-schema/rules.ts
Original file line number Diff line number Diff line change
Expand Up @@ -103,7 +103,3 @@ export const getNetworkRule = (type: BaseType) => {
export const getJsonRule = (type: BaseType) => {
return { type: 'object' };
}

export const addGeneralProperties = (ruleObject: ObjectType, name: string, notNull: boolean, defaultValue: any, values: Array<any>) => {
ruleObject
}
5 changes: 3 additions & 2 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

19 changes: 14 additions & 5 deletions package.json
Original file line number Diff line number Diff line change
@@ -1,14 +1,22 @@
{
"name": "sqema",
"version": "1.0.0",
"description": "Convert relational databases to json schemas",
"version": "1.0.2",
"description": "Convert relational databases to json schema format",
"keywords": [ "json-schema", "sql", "postgresql", "schema", "validation" ],
"author": {
"email": "[email protected]",
"name": "AzizAhmad Noorzaie"
},
"repository": {
"type": "git",
"url": "https://github.com/noorzaie/sqema"
},
"main": "index.js",
"scripts": {
"test": "ts-node test.ts",
"build": "tsc"
},
"author": "",
"license": "ISC",
"license": "MIT",
"dependencies": {
"commander": "^8.2.0",
"pg-structure": "^7.12.1",
Expand All @@ -21,5 +29,6 @@
"ts-node": "^10.2.1",
"typescript": "^4.4.3"
},
"bin": "cli/cli.js"
"bin": "cli/cli.js",
"types": "index.d.ts"
}
5 changes: 3 additions & 2 deletions tsconfig.json
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@
// "maxNodeModuleJsDepth": 1, /* Specify the maximum folder depth used for checking JavaScript files from `node_modules`. Only applicable with `allowJs`. */

/* Emit */
// "declaration": true, /* Generate .d.ts files from TypeScript and JavaScript files in your project. */
"declaration": true, /* Generate .d.ts files from TypeScript and JavaScript files in your project. */
// "declarationMap": true, /* Create sourcemaps for d.ts files. */
// "emitDeclarationOnly": true, /* Only output d.ts files and not JavaScript files. */
// "sourceMap": true, /* Create source map files for emitted JavaScript files. */
Expand Down Expand Up @@ -96,5 +96,6 @@
/* Completeness */
// "skipDefaultLibCheck": true, /* Skip type checking .d.ts files that are included with TypeScript. */
"skipLibCheck": true /* Skip type checking all .d.ts files. */
}
},
"exclude": [ "node_modules", "examples" ]
}
12 changes: 2 additions & 10 deletions types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,22 +3,14 @@ import { JSONSchema7Type } from 'json-schema';
export type DialectType = 'postgres';

export interface DatabaseConfigType {
host: string;
port: number;
username: string;
password: string;
database: string;
schemas?: string[];
}

export interface ColumnType {
name: string;
type: any;
length: number | undefined;
comment: any;
notNull: boolean;
minValue: number | undefined;
maxValue: number | undefined;
}

export interface DBClassType {
connect(): void;
getColumns(): DefinitionsType;
Expand Down

0 comments on commit 70eef03

Please sign in to comment.