diff --git a/esbuild.package.js b/esbuild.package.js index 19c1fd3..e6f7ca6 100644 --- a/esbuild.package.js +++ b/esbuild.package.js @@ -15,6 +15,7 @@ esbuild path.resolve(__dirname, 'api', 'fe.js'), path.resolve(__dirname, 'api', 're.js'), path.resolve(__dirname, 'forward_engineering', 'api.js'), + path.resolve(__dirname, 'forward_engineering', 'dbtProvider.js'), path.resolve(__dirname, 'reverse_engineering', 'api.js'), ], bundle: true, diff --git a/forward_engineering/dbtProvider.js b/forward_engineering/dbtProvider.js new file mode 100644 index 0000000..5aeaea9 --- /dev/null +++ b/forward_engineering/dbtProvider.js @@ -0,0 +1,49 @@ +/** + * @typedef {import('./types').ColumnDefinition} ColumnDefinition + * @typedef {import('./types').ConstraintDto} ConstraintDto + * @typedef {import('./types').JsonSchema} JsonSchema + */ + +const columnHelper = require('./helpers/columnHelper'); +const constraintHelper = require('./helpers/constraintHelper'); + +class DbtProvider { + /** + * @returns {DbtProvider} + */ + static createDbtProvider() { + return new DbtProvider(); + } + + /** + * @param {{ columnDefinition: ColumnDefinition }} + * @returns {string} + */ + decorateType({ columnDefinition }) { + const type = columnHelper.getTypeByProperty([], '')(columnDefinition); + const isComplexType = /^(array|struct)/i.test(type); + + return isComplexType ? type.replace(/<[\s\S]+>$/, '<>') : type; + } + + /** + * @param {{ jsonSchema: JsonSchema }} + * @returns {ConstraintDto[]} + */ + getCompositeKeyConstraints({ jsonSchema }) { + const compositePrimaryKeys = constraintHelper.getCompositePrimaryKeys({ jsonSchema }); + const compositeUniqueKeys = constraintHelper.getCompositeUniqueKeys({ jsonSchema }); + + return [...compositePrimaryKeys, ...compositeUniqueKeys]; + } + + /** + * @param {{ columnDefinition: ColumnDefinition; jsonSchema: JsonSchema }} + * @returns {ConstraintDto[]} + */ + getColumnConstraints({ columnDefinition, jsonSchema }) { + return constraintHelper.getColumnConstraints({ columnDefinition }); + } +} + +module.exports = DbtProvider; diff --git a/forward_engineering/helpers/constraintHelper.js b/forward_engineering/helpers/constraintHelper.js index 5e03433..bc55545 100644 --- a/forward_engineering/helpers/constraintHelper.js +++ b/forward_engineering/helpers/constraintHelper.js @@ -1,3 +1,9 @@ +/** + * @typedef {import('../types').ColumnDefinition} ColumnDefinition + * @typedef {import('../types').ConstraintDto} ConstraintDto + * @typedef {import('../types').JsonSchema} JsonSchema + */ + const findName = (keyId, properties) => { return Object.keys(properties).find(name => properties[name].GUID === keyId); }; @@ -91,8 +97,104 @@ const getCheckConstraint = jsonSchema => { return checkConstraint.filter(Boolean).join(',\n'); }; +/** + * @param {{ jsonSchema: JsonSchema }} + * @returns {ConstraintDto[]} + */ +const getCompositePrimaryKeys = ({ jsonSchema }) => { + if (!Array.isArray(jsonSchema.primaryKey)) { + return []; + } + + return jsonSchema.primaryKey + .filter(primaryKey => primaryKey.compositePrimaryKey?.length) + .map(primaryKey => ({ + keyType: 'PRIMARY KEY', + name: primaryKey.constraintName, + columns: getKeys(primaryKey.compositePrimaryKey, jsonSchema), + })); +}; + +/** + * @param {{ jsonSchema: JsonSchema }} + * @returns {ConstraintDto[]} + */ +const getCompositeUniqueKeys = ({ jsonSchema }) => { + if (!Array.isArray(jsonSchema.uniqueKey)) { + return []; + } + + return jsonSchema.uniqueKey + .filter(uniqueKey => uniqueKey.compositeUniqueKey?.length) + .map(uniqueKey => ({ + keyType: 'UNIQUE', + name: uniqueKey.constraintName, + columns: getKeys(uniqueKey.compositeUniqueKey, jsonSchema), + })); +}; + +/** + * @param {{ columnDefinition: ColumnDefinition }} + * @returns {ConstraintDto | undefined} + */ +const getColumnPrimaryKeyConstraint = ({ columnDefinition }) => { + const isPrimaryKey = columnDefinition.primaryKey && !columnDefinition.compositePrimaryKey; + + if (!isPrimaryKey) { + return; + } + + return { + keyType: 'PRIMARY KEY', + }; +}; + +/** + * @param {{ columnDefinition: ColumnDefinition }} + * @returns {ConstraintDto | undefined} + */ +const getColumnUniqueKeyConstraint = ({ columnDefinition }) => { + if (!columnDefinition.unique) { + return; + } + + return { + keyType: 'UNIQUE', + }; +}; + +/** + * @param {{ columnDefinition: ColumnDefinition }} + * @returns {ConstraintDto | undefined} + */ +const getColumnCheckConstraint = ({ columnDefinition }) => { + if (!columnDefinition.check) { + return; + } + + return { + keyType: 'CHECK', + expression: columnDefinition.check, + }; +}; + +/** + * @param {{ columnDefinition: ColumnDefinition; jsonSchema: JsonSchema }} + * @returns {ConstraintDto[]} + */ +const getColumnConstraints = ({ columnDefinition, jsonSchema }) => { + const primaryKeyConstraint = getColumnPrimaryKeyConstraint({ columnDefinition }); + const uniqueKeyConstraint = getColumnUniqueKeyConstraint({ columnDefinition }); + const checkConstraint = getColumnCheckConstraint({ columnDefinition }); + + return [primaryKeyConstraint, uniqueKeyConstraint, checkConstraint].filter(Boolean); +}; + module.exports = { getConstraintOpts, getUniqueKeyStatement, getCheckConstraint, + getCompositeUniqueKeys, + getCompositePrimaryKeys, + getColumnConstraints, }; diff --git a/forward_engineering/types.ts b/forward_engineering/types.ts new file mode 100644 index 0000000..53bc7f8 --- /dev/null +++ b/forward_engineering/types.ts @@ -0,0 +1,29 @@ +export type ColumnDefinition = { + name: string; + type: string; + nullable: boolean; + isActivated: boolean; + length?: number; + precision?: number; + primaryKey?: boolean; + scale?: number; + timePrecision?: number; + unique?: boolean; + check?: string; + properties?: Record; +}; + +export type ConstraintDtoColumn = { + name: string; + isActivated: boolean; +}; + +export type KeyType = 'PRIMARY KEY' | 'UNIQUE' | 'CHECK'; + +export type ConstraintDto = { + keyType: KeyType; + name: string; + columns?: ConstraintDtoColumn[]; +}; + +export type JsonSchema = Record; diff --git a/package-lock.json b/package-lock.json index 211dae9..195eb4d 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,13 @@ { "name": "Hive", - "version": "0.2.11", + "version": "0.2.12", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "Hive", - "version": "0.2.11", + "version": "0.2.12", + "hasInstallScript": true, "dependencies": { "antlr4": "4.8.0", "async": "2.6.4", diff --git a/package.json b/package.json index c9cad59..37c2a78 100644 --- a/package.json +++ b/package.json @@ -34,7 +34,13 @@ }, "nestedCollections": false, "disablePatternField": true, - "enableForwardEngineering": true, + "enableForwardEngineering": { + "jsonDocument": true, + "jsonSchema": true, + "excel": true, + "plugin": true, + "dbt": true + }, "disableMultipleTypes": false, "enableReverseEngineering": true, "enableComplexTypesNormalization": true,