From 65ae4a091f1f838619b2398174bdfc8e41f7a59f Mon Sep 17 00:00:00 2001 From: Serhii Filonenko Date: Tue, 25 Feb 2025 15:18:52 +0200 Subject: [PATCH 1/2] HCK-10154: add dbt provider --- esbuild.package.js | 1 + forward_engineering/dbtProvider.js | 88 +++++++++++ .../helpers/constraintHelper.js | 143 ++++++++++++++++++ forward_engineering/types.d.ts | 26 ++++ 4 files changed, 258 insertions(+) create mode 100644 forward_engineering/dbtProvider.js create mode 100644 forward_engineering/types.d.ts diff --git a/esbuild.package.js b/esbuild.package.js index 22e21ff..979015d 100644 --- a/esbuild.package.js +++ b/esbuild.package.js @@ -14,6 +14,7 @@ esbuild entryPoints: [ path.resolve(__dirname, 'forward_engineering', 'api.js'), path.resolve(__dirname, 'forward_engineering', 'ddlProvider.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..a3711b4 --- /dev/null +++ b/forward_engineering/dbtProvider.js @@ -0,0 +1,88 @@ +/** + * @typedef {import('./types').ColumnDefinition} ColumnDefinition + * @typedef {import('./types').JsonSchema} JsonSchema + * @typedef {import('./types').ConstraintDto} ConstraintDto + */ +const { toLower } = require('lodash'); + +const types = require('./configs/types'); +const defaultTypes = require('./configs/defaultTypes'); +const getColumnDefinitionHelper = require('./helpers/columnDefinitionHelper'); +const getConstraintHelper = require('./helpers/constraintHelper'); + +class DbtProvider { + /** + * @type {AppInstance} + */ + #appInstance; + + /** + * @param {{ appInstance: AppInstance }} + */ + constructor({ appInstance }) { + this.#appInstance = appInstance; + } + + /** + * @param {{ appInstance }} + * @returns {DbtProvider} + */ + static createDbtProvider({ appInstance }) { + return new DbtProvider({ appInstance }); + } + + /** + * @param {string} type + * @returns {string | undefined} + */ + getDefaultType(type) { + return defaultTypes[type]; + } + + /** + * @returns {Record} + */ + getTypesDescriptors() { + return types; + } + + /** + * @param {string} type + * @returns {boolean} + */ + hasType(type) { + return Object.keys(types).map(toLower).includes(toLower(type)); + } + + /** + * @param {{ type: string; columnDefinition: ColumnDefinition }} + * @returns {string} + */ + decorateType({ type, columnDefinition }) { + const columnDefinitionHelper = getColumnDefinitionHelper(this.#appInstance); + + return columnDefinitionHelper.decorateType(type, columnDefinition); + } + + /** + * @param {{ jsonSchema: JsonSchema }} + * @returns {ConstraintDto[]} + */ + getCompositeKeyConstraints({ jsonSchema }) { + const constraintHelper = getConstraintHelper(this.#appInstance); + + return constraintHelper.getCompositeKeyConstraints({ jsonSchema }); + } + + /** + * @param {{ columnDefinition: ColumnDefinition; jsonSchema: JsonSchema }} + * @returns {ConstraintDto[]} + */ + getColumnConstraints({ columnDefinition, jsonSchema }) { + const constraintHelper = getConstraintHelper(this.#appInstance); + + return constraintHelper.getColumnConstraints({ columnDefinition }); + } +} + +module.exports = DbtProvider; diff --git a/forward_engineering/helpers/constraintHelper.js b/forward_engineering/helpers/constraintHelper.js index 6f59388..f7972c1 100644 --- a/forward_engineering/helpers/constraintHelper.js +++ b/forward_engineering/helpers/constraintHelper.js @@ -1,3 +1,12 @@ +/** + * @typedef {import('../types').ColumnDefinition} ColumnDefinition + * @typedef {import('../types').ConstraintDtoColumn} ConstraintDtoColumn + * @typedef {import('../types').ConstraintDto} ConstraintDto + * @typedef {import('../types').JsonSchema} JsonSchema + */ + +const _ = require('lodash'); + module.exports = app => { const { foreignKeysToString, foreignActiveKeysToString } = require('./general')(app); const assignTemplates = app.require('@hackolade/ddl-fe-utils').assignTemplates; @@ -20,7 +29,141 @@ module.exports = app => { }; }; + /** + * @param {ColumnDefinition} columnDefinition + * @returns {boolean} + */ + const isPrimaryKey = columnDefinition => { + return !columnDefinition.compositePrimaryKey && columnDefinition.primaryKey; + }; + + /** + * @param {ColumnDefinition} columnDefinition + * @returns {boolean} + */ + const isUniqueKey = columnDefinition => { + return !columnDefinition.compositeUniqueKey && columnDefinition.unique; + }; + + /** + * @param {string} keyId + * @param {Record} properties + * @returns {string} + */ + const findName = (keyId, properties) => { + return Object.keys(properties).find(name => properties[name].GUID === keyId); + }; + + /** + * @param {string} keyId + * @param {Record} properties + * @returns {boolean} + */ + const checkIfActivated = (keyId, properties) => { + return _.get( + Object.values(properties).find(prop => prop.GUID === keyId), + 'isActivated', + true, + ); + }; + + /** + * @param {Array<{ keyId: string }>} keys + * @param {JsonSchema} jsonSchema + * @returns {ConstraintDtoColumn} + */ + const getKeys = (keys, jsonSchema) => { + return _.map(keys, key => { + return { + name: findName(key.keyId, jsonSchema.properties), + isActivated: checkIfActivated(key.keyId, jsonSchema.properties), + }; + }); + }; + + /** + * @param {{ jsonSchema: JsonSchema }} + * @returns {ConstraintDto[]} + */ + const getCompositePrimaryKeys = ({ jsonSchema }) => { + if (!Array.isArray(jsonSchema.primaryKey)) { + return []; + } + + return jsonSchema.primaryKey + .filter(primaryKey => !_.isEmpty(primaryKey.compositePrimaryKey)) + .map(primaryKey => ({ + keyType: 'PRIMARY KEY', + columns: getKeys(primaryKey.compositePrimaryKey, jsonSchema), + })); + }; + + /** + * @param {{ jsonSchema: JsonSchema }} + * @returns {ConstraintDto[]} + */ + const getCompositeUniqueKeys = ({ jsonSchema }) => { + if (!Array.isArray(jsonSchema.uniqueKey)) { + return []; + } + + return jsonSchema.uniqueKey + .filter(uniqueKey => !_.isEmpty(uniqueKey.compositeUniqueKey)) + .map(uniqueKey => ({ + keyType: 'UNIQUE', + columns: getKeys(uniqueKey.compositeUniqueKey, jsonSchema), + })); + }; + + /** + * @param {{ columnDefinition: ColumnDefinition }} + * @returns {ConstraintDto | undefined} + */ + const getPrimaryKeyConstraint = ({ columnDefinition }) => { + if (!isPrimaryKey(columnDefinition)) { + return; + } + + return { + keyType: 'PRIMARY KEY', + }; + }; + + /** + * @param {{ columnDefinition: ColumnDefinition }} + * @returns {ConstraintDto | undefined} + */ + const getUniqueKeyConstraint = ({ columnDefinition }) => { + if (!isUniqueKey(columnDefinition)) { + return; + } + + return { + keyType: 'UNIQUE', + }; + }; + + const getCompositeKeyConstraints = ({ jsonSchema }) => { + const compositePrimaryKeys = getCompositePrimaryKeys({ jsonSchema }); + const compositeUniqueKeys = getCompositeUniqueKeys({ jsonSchema }); + + return [...compositePrimaryKeys, ...compositeUniqueKeys]; + }; + + /** + * @param {{ columnDefinition: ColumnDefinition }} + * @returns {ConstraintDto[]} + */ + const getColumnConstraints = ({ columnDefinition }) => { + const primaryKeyConstraint = getPrimaryKeyConstraint({ columnDefinition }); + const uniqueKeyConstraint = getUniqueKeyConstraint({ columnDefinition }); + + return [primaryKeyConstraint, uniqueKeyConstraint].filter(Boolean); + }; + return { generateConstraint, + getCompositeKeyConstraints, + getColumnConstraints, }; }; diff --git a/forward_engineering/types.d.ts b/forward_engineering/types.d.ts new file mode 100644 index 0000000..1a88b8c --- /dev/null +++ b/forward_engineering/types.d.ts @@ -0,0 +1,26 @@ +export type ColumnDefinition = { + name: string; + type: string; + isActivated: boolean; + length?: number; + precision?: number; + primaryKey?: boolean; + scale?: number; + timePrecision?: number; + unique?: boolean; +}; + +export type ConstraintDtoColumn = { + name: string; + isActivated: boolean; +}; + +export type KeyType = 'PRIMARY KEY' | 'UNIQUE'; + +export type ConstraintDto = { + keyType: KeyType; + name: string; + columns?: ConstraintDtoColumn[]; +}; + +export type JsonSchema = Record; From 1784dfdcb7260b4ed097f9eccb6ce18093316d70 Mon Sep 17 00:00:00 2001 From: Serhii Filonenko Date: Tue, 25 Feb 2025 15:19:12 +0200 Subject: [PATCH 2/2] HCK-10154: enable dbt feature --- package.json | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/package.json b/package.json index dd4dad4..0cf40e6 100644 --- a/package.json +++ b/package.json @@ -24,7 +24,8 @@ "jsonDocument": true, "jsonSchema": true, "excel": true, - "plugin": true + "plugin": true, + "dbt": true }, "forwardEngineering": { "jsonSchema": {