jsonschema2ddl
Version:
Convert JSON Schema to DDL
243 lines • 8.83 kB
JavaScript
import * as crypto from "crypto";
import { COLUMNS_TYPES, COLUMNS_TYPES_PREFERENCE, FK_TYPES } from "./types";
import { db_column_name, get_one_schema } from "./utils";
/**
* Object to encapsulate a Column.
*
* Attributes:
* name(str): name of the Column.
* database_flavor(str): postgres or redshift.Defaults to postgres.
* comment(str): comment of the Column.Defaults to None.
* constraints(Dict): other columns constraints (not implemented).
* jsonschema_fields(Dict): Original fields in the jsonschema.
*/
export class Column {
constructor({ name, database_flavor, comment, constraints, jsonschema_type, jsonschema_fields }) {
this.constraints = {};
this.name = name;
this.database_flavor = database_flavor || "postgres";
this.comment = comment || '';
this.constraints = constraints || {};
this.jsonschema_type = jsonschema_type || '';
this.jsonschema_fields = jsonschema_fields || {};
}
get max_length() {
return this.jsonschema_fields["maxLength"] || 256;
}
/**
* Data type of the columns.
*
* It accounts of the mapping of the original type to the db types.
*
* Returns:
* str: data type of the column.
*/
get data_type() {
if ("format" in this.jsonschema_fields && this.jsonschema_fields["format"] in COLUMNS_TYPES_PREFERENCE) {
this.jsonschema_type = this.jsonschema_fields["format"];
}
return COLUMNS_TYPES[this.database_flavor][this.jsonschema_type]
.replace('{}', String(this.max_length));
}
get is_pk() {
return !!this.jsonschema_fields["pk"];
}
/**
* Returns true if the column is a index.
*
* Returns:
* bool: True if it is index.
*/
get is_index() {
return !!this.jsonschema_fields["index"];
}
/**
* Returns true if the column is a unique.
*
* Returns:
* bool: True if it is unique.
*/
get is_unique() {
return !!this.jsonschema_fields["unique"];
}
/**
* Returns true if the column is a foreign key.
*
* Returns:
* bool: True if it is foreign key
*/
get is_fk() {
return false;
}
hash() {
return crypto.createHash('sha256').update(this.name).digest('hex');
}
toString() {
return `Column(name=${this.name} data_type=${this.data_type})`;
}
}
/**
* Object to encapsulate a Table.
*
* Attributes:
* ref(str): id or reference to the table in the jsonschema.
* name(str): name of the table.
* database_flavor(str): postgres or redshift.Defaults to postgres.
* columns(List[Column]): columns of the table.
* primary_key(Column): Primary key column of the table.
* comment(str): comment of the table.Defaults to None.
* indexes(List[str]): Table indexeses(not implemented).
* unique_columns(List[str]): Table unique constraints(not implemented).
* jsonschema_fields(Dict): Original fields in the jsonschema.
*/
export class Table {
constructor({ ref, name, database_flavor, columns, primary_key, comment, indexes, unique_columns, jsonschema_fields }) {
this._expanded = false;
this.ref = ref;
this.name = name;
this.database_flavor = database_flavor || "postgres";
this.columns = columns || [];
this.primary_key = primary_key;
this.comment = comment;
this.indexes = indexes || [];
this.unique_columns = unique_columns || [];
this.jsonschema_fields = jsonschema_fields || {};
}
/**
* Expand the columns definitions of the
*
* Args:
* table_definitions(Dict, optional): Dictionary with the rest of the
* tables definitions.It is used for recursive calls to get the
* foreign keys.Defaults to dict().
* columns_definitions(Dict, optional): Dictionary with the definition
* of columns outside the main properties field.Defaults to dict().
* referenced(bool, optional): Whether or not the table is referenced
* by others.Used to make sure there is a Primary Key defined.
* Defaults to False.
*
* @param table_definitions
* @param columns_definitions
* @param referenced
* @returns
*/
expand_columns({ table_definitions = {}, columns_definitions = {}, referenced }) {
if (this._expanded) {
console.log("Already expanded table. Skipping...");
return this;
}
const props = this.jsonschema_fields["properties"];
for (let [col_name, col_object] of Object.entries(props)) {
console.log(`Creating column ${col_name}`);
col_name = db_column_name(col_name);
console.log(`Renamed column to ${col_name}`);
let col;
if ("$ref" in col_object) {
console.log(`Expanding ${col_name} reference ${col_object["$ref"]}`);
console.log(JSON.stringify(table_definitions, null, 2));
if (col_object["$ref"] in table_definitions) {
const myref = col_object["$ref"];
console.log(`Column is a FK! Expanding ${myref} before continue...`);
table_definitions[myref] =
table_definitions[myref].expand_columns({
table_definitions,
referenced: true,
});
col = new FKColumn({
table_ref: table_definitions[myref],
name: col_name,
database_flavor: this.database_flavor,
});
}
else if (col_object["$ref"] in columns_definitions) {
console.log("Column ref a type that is not a object. Copy Column from columns definitions");
const myref = col_object["$ref"];
const ref_col = columns_definitions[myref];
const col_as_dict = Object.assign(Object.assign({}, ref_col), { "name": col_name });
col = new Column(col_as_dict);
}
else {
console.log("Skipping ref as it is not in table definitions neither in columns definitions");
continue;
}
}
else {
if (!("type" in col_object)) {
col_object = get_one_schema(col_object);
}
col = new Column({
name: col_name,
database_flavor: this.database_flavor,
jsonschema_type: col_object["type"],
jsonschema_fields: col_object,
});
}
this.columns.push(col);
if (col.is_pk) {
this.primary_key = col;
}
console.log(`New created column ${col}`);
}
if (referenced && !this.primary_key) {
console.log("Creating id column for the table in order to reference it as PK");
let idcol = new Column({
name: "id",
database_flavor: this.database_flavor,
jsonschema_type: "id",
});
this.columns.push(idcol);
this.primary_key = idcol;
}
this.columns = this._deduplicate_columns(this.columns);
return this;
}
_deduplicate_columns(columns) {
return columns.reduce((a, c) => {
if (!a.find(x => x.name === c.name)) {
a.push(c);
}
return a;
}, []);
}
}
/**
* Special type of Column object to represent a foreign key
*
* Attributes:
* table_ref(Table): Pointer to the foreign table object
*/
export class FKColumn extends Column {
constructor(params) {
super(params);
this.table_ref = params.table_ref;
}
/**
* Data type of the foreign key.
*
* Accounts of the data type of the primary key of the foreing table.
*
* Returns:
* str: the column data type.
*/
get data_type() {
var _a;
const data_type_ref = ((_a = this.table_ref.primary_key) === null || _a === void 0 ? void 0 : _a.data_type) || '';
if ("varchar" === data_type_ref) {
return data_type_ref;
}
return FK_TYPES[data_type_ref] || "bigint";
}
/**
* Returns true if the column is a foreign key.
*
* Returns:
* bool: True if it is foreign key.
*/
get is_fk() {
return true;
}
toString() {
return `FKColumn(name=${this.name} data_type=${this.data_type} table_ref.name=${this.table_ref.name})`;
}
}
//# sourceMappingURL=models.js.map