From 93eeee0d8e0cddc7f5476e7e531282edc07084ed Mon Sep 17 00:00:00 2001 From: Vitalii Bedletskyi Date: Mon, 10 Mar 2025 17:15:46 +0200 Subject: [PATCH 1/4] add entry point for RE from GraphQL server --- forward_engineering/api.js | 3 +- forward_engineering/types/types.d.ts | 4 - localization/en.json | 14 +- package-lock.json | 223 ++++++++++++++---- package.json | 3 +- reverse_engineering/api.js | 78 +++++- reverse_engineering/config.json | 22 +- .../connectionSettingsModalConfig.json | 88 +++++++ ...nvertIntrospectionSchemaToGraphQLSchema.js | 19 ++ .../helpers/fetchIntrospectionSchema.js | 66 ++++++ reverse_engineering/package.json | 6 +- reverse_engineering/types/types.d.ts | 68 +++++- shared/types/types.d.ts | 14 ++ 13 files changed, 535 insertions(+), 73 deletions(-) create mode 100644 reverse_engineering/connection_settings_modal/connectionSettingsModalConfig.json create mode 100644 reverse_engineering/helpers/convertIntrospectionSchemaToGraphQLSchema.js create mode 100644 reverse_engineering/helpers/fetchIntrospectionSchema.js create mode 100644 shared/types/types.d.ts diff --git a/forward_engineering/api.js b/forward_engineering/api.js index 9b1d76a..a100e02 100644 --- a/forward_engineering/api.js +++ b/forward_engineering/api.js @@ -1,5 +1,6 @@ /** - * @import { ContainerLevelScriptFEData, Logger, GenerateContainerLevelScriptCallback } from "./types/types" + * @import { ContainerLevelScriptFEData, GenerateContainerLevelScriptCallback } from "./types/types" + * @import { Logger } from "../shared/types/types" */ const validationHelper = require('./helpers/schemaValidationHelper'); diff --git a/forward_engineering/types/types.d.ts b/forward_engineering/types/types.d.ts index 96d9e3d..d5dbe64 100644 --- a/forward_engineering/types/types.d.ts +++ b/forward_engineering/types/types.d.ts @@ -207,10 +207,6 @@ export type ContainerLevelScriptFEData = { } }; -export type Logger = { - log: (logType: string, logData: object, logMessage: string) => void; -}; - export type GenerateContainerLevelScriptCallback = (error: Error | null, script?: string) => void; export type ValidationResponseItem = { diff --git a/localization/en.json b/localization/en.json index 4ce1c8b..e282c07 100644 --- a/localization/en.json +++ b/localization/en.json @@ -8,7 +8,7 @@ "MAIN_MENU___ADD_ATTRIBUTE": "Add Field", "MAIN_MENU___INSERT_FIELD": "Insert Field", "MAIN_MENU___APPEND_FIELD": "Append Field", - "MAIN_MENU___REVERSE_DB_COLLECTIONS": "GraphQL File...", + "MAIN_MENU___REVERSE_DB_COLLECTIONS": "GraphQL Server...", "MAIN_MENU___FORWARD_MODEL_COLLECTIONS": "GraphQL Schema...", "MAIN_MENU___FORWARD_DB_BUCKETS": "GraphQL Schema...", "TOOLBAR___ADD_BUCKET": "Add graph", @@ -83,9 +83,9 @@ "MODAL_WINDOW___EMPTY_MODEL_MESSAGE": "The Model does not contain any operations.", "MODAL_WINDOW___FIELD_INFERENCE": "Field Inference", "MODAL_WINDOW___KEEP_FIELD_ORDER": "Keep field order", - "MODAL_WINDOW___CANNOT_CONNECT_TO_DB": "Cannot connect to REST", - "MODAL_WINDOW___SUCCESSFULLY_CONNECT_TO_DB": "Successfully connected to REST", - "MODAL_WINDOW___UNABLE_CONNECT_TO_DB": "Unable to connect to REST", + "MODAL_WINDOW___CANNOT_CONNECT_TO_DB": "Cannot connect to the GraphQL server", + "MODAL_WINDOW___SUCCESSFULLY_CONNECT_TO_DB": "Successfully connected to the GraphQL server", + "MODAL_WINDOW___UNABLE_CONNECT_TO_DB": "Unable to connect to the GraphQL server", "MODAL_WINDOW___DB_ENTITIES_SELECTION_TITLE": "Operation selection", "MODAL_WINDOW___RECORDS_MAX": "Documents max", "MODAL_WINDOW___SUBDOCUMENT_IN_CHILD": "Sub-document in child", @@ -94,13 +94,13 @@ "MODAL_WINDOW___CREATE_COLLECTION": "Create request", "MODAL_WINDOW___CREATE_BUCKET": "Create operations", "MODAL_WINDOW___ALL_COLLECTIONS": "and all nested operations", - "MODAL_WINDOW___CONNENTION_ERROR": "The REST instance you are connected to does not contain any operations.", + "MODAL_WINDOW___CONNENTION_ERROR": "The GraphQL server instance you are connected to does not contain any operations.", "MODAL_WINDOW___CONTAIN_BUCKETS": "graphs", "MODAL_WINDOW___CONTAIN_COLLECTIONS": "operations", "MODAL_WINDOW___CONTAIN_BUCKET": "graphs", "MODAL_WINDOW___CONTAIN_COLLECTION": "operations", - "MODAL_WINDOW___DB_CONNECTION_PROCESS": "REST Reverse-Engineering Process", - "MODAL_WINDOW___DB_CONNECTIONS_LIST_TITLE": "REST Connections", + "MODAL_WINDOW___DB_CONNECTION_PROCESS": "GraphQL server Reverse-Engineering Process", + "MODAL_WINDOW___DB_CONNECTIONS_LIST_TITLE": "GraphQL Connections", "PROGRESS_BAR___DATABASE": "Operation", "PROGRESS_BAR___COLLECTION": "Request", "PROGRESS_BAR___PROCESS": "Process", diff --git a/package-lock.json b/package-lock.json index ea111ca..f5ba916 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,14 +1,15 @@ { "name": "GraphQL", - "version": "0.2.1", + "version": "0.2.3", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "GraphQL", - "version": "0.2.1", + "version": "0.2.3", "hasInstallScript": true, "dependencies": { + "@hackolade/fetch": "1.1.1", "graphql": "16.10.0" }, "devDependencies": { @@ -527,9 +528,9 @@ } }, "node_modules/@eslint/eslintrc": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-3.2.0.tgz", - "integrity": "sha512-grOjVNN8P3hjJn/eIETF1wwd12DdnwFDoyceUJLYYdkpbwq3nLi+4fqrTAONx7XDALqlL220wC/RHSC/QTI/0w==", + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-3.3.0.tgz", + "integrity": "sha512-yaVPAiNAalnCZedKLdR21GOGILMLKPyqSLWaAjQFvYA2i/ciDi8ArYVr69Anohb6cH2Ukhqti4aFnYyPm8wdwQ==", "dev": true, "license": "MIT", "dependencies": { @@ -584,13 +585,13 @@ } }, "node_modules/@eslint/plugin-kit": { - "version": "0.2.5", - "resolved": "https://registry.npmjs.org/@eslint/plugin-kit/-/plugin-kit-0.2.5.tgz", - "integrity": "sha512-lB05FkqEdUg2AA0xEbUz0SnkXT1LcCTa438W4IWTUh4hdOnVbQyOJ81OrDXsJk/LSiJHubgGEFoR5EHq1NsH1A==", + "version": "0.2.7", + "resolved": "https://registry.npmjs.org/@eslint/plugin-kit/-/plugin-kit-0.2.7.tgz", + "integrity": "sha512-JubJ5B2pJ4k4yGxaNLdbjrnk9d/iDz6/q8wOilpIowd6PJPgaxCuHBnBszq7Ce2TyMrywm5r4PnKm6V3iiZF+g==", "dev": true, "license": "Apache-2.0", "dependencies": { - "@eslint/core": "^0.10.0", + "@eslint/core": "^0.12.0", "levn": "^0.4.1" }, "engines": { @@ -598,9 +599,9 @@ } }, "node_modules/@eslint/plugin-kit/node_modules/@eslint/core": { - "version": "0.10.0", - "resolved": "https://registry.npmjs.org/@eslint/core/-/core-0.10.0.tgz", - "integrity": "sha512-gFHJ+xBOo4G3WRlR1e/3G8A6/KZAH6zcE/hkLRCZTi/B9avAG365QhFA8uOGzTMqgTghpn7/fSnscW++dpMSAw==", + "version": "0.12.0", + "resolved": "https://registry.npmjs.org/@eslint/core/-/core-0.12.0.tgz", + "integrity": "sha512-cmrR6pytBuSMTaBweKoGMwu3EiHiEC+DoyupPmlZ0HxBJBtIxwe+j/E4XPIKNx+Q74c8lXKPwYawBf5glsTkHg==", "dev": true, "license": "Apache-2.0", "dependencies": { @@ -610,6 +611,18 @@ "node": "^18.18.0 || ^20.9.0 || >=21.1.0" } }, + "node_modules/@hackolade/fetch": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@hackolade/fetch/-/fetch-1.1.1.tgz", + "integrity": "sha512-3KQhDuLfe16MtIGnqHBGxSh7gKtzpb5f/fGgwkMpJx6SeZD9S3CVHTQyXJ6oiskA8kG1+EdRSpRsnfplfuvxfw==", + "license": "MIT", + "dependencies": { + "@smithy/fetch-http-handler": "4.0.0", + "@smithy/protocol-http": "4.1.5", + "@smithy/querystring-builder": "3.0.8", + "@smithy/types": "3.6.0" + } + }, "node_modules/@hackolade/hck-esbuild-plugins-pack": { "version": "0.0.1", "resolved": "https://registry.npmjs.org/@hackolade/hck-esbuild-plugins-pack/-/hck-esbuild-plugins-pack-0.0.1.tgz", @@ -676,9 +689,9 @@ } }, "node_modules/@humanwhocodes/retry": { - "version": "0.4.1", - "resolved": "https://registry.npmjs.org/@humanwhocodes/retry/-/retry-0.4.1.tgz", - "integrity": "sha512-c7hNEllBlenFTHBky65mhq8WD2kbN9Q6gk0bTk8lSBvc554jpXSkST1iePudpt7+A/AQvuHs9EMqjHDXMY1lrA==", + "version": "0.4.2", + "resolved": "https://registry.npmjs.org/@humanwhocodes/retry/-/retry-0.4.2.tgz", + "integrity": "sha512-xeO57FpIu4p1Ri3Jq/EXq4ClRm86dVF2z/+kvFnyqVYRavTZmaFaUBbWCOuuTh0o/g7DSsk6kc2vrS4Vl5oPOQ==", "dev": true, "license": "Apache-2.0", "engines": { @@ -747,6 +760,122 @@ "dev": true, "license": "MIT" }, + "node_modules/@smithy/fetch-http-handler": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/@smithy/fetch-http-handler/-/fetch-http-handler-4.0.0.tgz", + "integrity": "sha512-MLb1f5tbBO2X6K4lMEKJvxeLooyg7guq48C2zKr4qM7F2Gpkz4dc+hdSgu77pCJ76jVqFBjZczHYAs6dp15N+g==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/protocol-http": "^4.1.5", + "@smithy/querystring-builder": "^3.0.8", + "@smithy/types": "^3.6.0", + "@smithy/util-base64": "^3.0.0", + "tslib": "^2.6.2" + } + }, + "node_modules/@smithy/is-array-buffer": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/@smithy/is-array-buffer/-/is-array-buffer-3.0.0.tgz", + "integrity": "sha512-+Fsu6Q6C4RSJiy81Y8eApjEB5gVtM+oFKTffg+jSuwtvomJJrhUJBu2zS8wjXSgH/g1MKEWrzyChTBe6clb5FQ==", + "license": "Apache-2.0", + "dependencies": { + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@smithy/protocol-http": { + "version": "4.1.5", + "resolved": "https://registry.npmjs.org/@smithy/protocol-http/-/protocol-http-4.1.5.tgz", + "integrity": "sha512-hsjtwpIemmCkm3ZV5fd/T0bPIugW1gJXwZ/hpuVubt2hEUApIoUTrf6qIdh9MAWlw0vjMrA1ztJLAwtNaZogvg==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/types": "^3.6.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@smithy/querystring-builder": { + "version": "3.0.8", + "resolved": "https://registry.npmjs.org/@smithy/querystring-builder/-/querystring-builder-3.0.8.tgz", + "integrity": "sha512-btYxGVqFUARbUrN6VhL9c3dnSviIwBYD9Rz1jHuN1hgh28Fpv2xjU1HeCeDJX68xctz7r4l1PBnFhGg1WBBPuA==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/types": "^3.6.0", + "@smithy/util-uri-escape": "^3.0.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@smithy/types": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/@smithy/types/-/types-3.6.0.tgz", + "integrity": "sha512-8VXK/KzOHefoC65yRgCn5vG1cysPJjHnOVt9d0ybFQSmJgQj152vMn4EkYhGuaOmnnZvCPav/KnYyE6/KsNZ2w==", + "license": "Apache-2.0", + "dependencies": { + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@smithy/util-base64": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/@smithy/util-base64/-/util-base64-3.0.0.tgz", + "integrity": "sha512-Kxvoh5Qtt0CDsfajiZOCpJxgtPHXOKwmM+Zy4waD43UoEMA+qPxxa98aE/7ZhdnBFZFXMOiBR5xbcaMhLtznQQ==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/util-buffer-from": "^3.0.0", + "@smithy/util-utf8": "^3.0.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@smithy/util-buffer-from": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/@smithy/util-buffer-from/-/util-buffer-from-3.0.0.tgz", + "integrity": "sha512-aEOHCgq5RWFbP+UDPvPot26EJHjOC+bRgse5A8V3FSShqd5E5UN4qc7zkwsvJPPAVsf73QwYcHN1/gt/rtLwQA==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/is-array-buffer": "^3.0.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@smithy/util-uri-escape": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/@smithy/util-uri-escape/-/util-uri-escape-3.0.0.tgz", + "integrity": "sha512-LqR7qYLgZTD7nWLBecUi4aqolw8Mhza9ArpNEQ881MJJIU2sE5iHCK6TdyqqzcDLy0OPe10IY4T8ctVdtynubg==", + "license": "Apache-2.0", + "dependencies": { + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@smithy/util-utf8": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/@smithy/util-utf8/-/util-utf8-3.0.0.tgz", + "integrity": "sha512-rUeT12bxFnplYDe815GXbq/oixEGHfRFFtcTF3YdDi/JaENIM6aSYYLJydG83UNzLXeRI5K8abYd/8Sp/QM0kA==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/util-buffer-from": "^3.0.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, "node_modules/@types/estree": { "version": "1.0.6", "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.6.tgz", @@ -769,9 +898,9 @@ "license": "MIT" }, "node_modules/acorn": { - "version": "8.14.0", - "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.14.0.tgz", - "integrity": "sha512-cl669nCJTZBsL97OF4kUQm5g5hC2uihk0NxY3WENAC0TYdILVkAyHymAntgxGkl7K+t0cXIrH5siy5S4XkFycA==", + "version": "8.14.1", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.14.1.tgz", + "integrity": "sha512-OvQ/2pUDKmgfCg++xsTX1wGxfTaszcHVcTctW4UJB4hibJx2HXxxO5UmVgyjMa+ZDsiaf5wWLXYpRWMmBI0QHg==", "dev": true, "license": "MIT", "bin": { @@ -1094,14 +1223,14 @@ } }, "node_modules/call-bound": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/call-bound/-/call-bound-1.0.3.tgz", - "integrity": "sha512-YTd+6wGlNlPxSuri7Y6X8tY2dmm12UMH66RpKMhiX6rsk5wXXnYgbUcOt8kiS31/AjfoTOvCsE+w8nZQLQnzHA==", + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/call-bound/-/call-bound-1.0.4.tgz", + "integrity": "sha512-+ys997U96po4Kx/ABpBCqhA9EuxJaQWDQg7295H4hBphv3IZg0boBKuwYpt4YXp6MZ5AmZQnU/tyMTlRpaSejg==", "dev": true, "license": "MIT", "dependencies": { - "call-bind-apply-helpers": "^1.0.1", - "get-intrinsic": "^1.2.6" + "call-bind-apply-helpers": "^1.0.2", + "get-intrinsic": "^1.3.0" }, "engines": { "node": ">= 0.4" @@ -1867,9 +1996,9 @@ } }, "node_modules/eslint-scope": { - "version": "8.2.0", - "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-8.2.0.tgz", - "integrity": "sha512-PHlWUfG6lvPc3yvP5A4PNyBL1W8fkDUccmI21JUu/+GKZBoH/W5u6usENXUrWFRsyoW5ACUjFGgAFQp5gUlb/A==", + "version": "8.3.0", + "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-8.3.0.tgz", + "integrity": "sha512-pUNxi75F8MJ/GdeKtVLSbYg4ZI34J6C0C7sbL4YOp2exGwen7ZsuBqKzUhXd0qMQ362yET3z+uPwKeg/0C2XCQ==", "dev": true, "license": "BSD-2-Clause", "dependencies": { @@ -2050,9 +2179,9 @@ "license": "MIT" }, "node_modules/fastq": { - "version": "1.19.0", - "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.19.0.tgz", - "integrity": "sha512-7SFSRCNjBQIZH/xZR3iy5iQYR8aGBE0h3VG6/cwlbrpdciNYBMotQav8c1XI3HjHH+NikUpP53nPdlZSdWmFzA==", + "version": "1.19.1", + "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.19.1.tgz", + "integrity": "sha512-GwLTyxkCXjXbxqIhTsMI2Nui8huMPtnxg7krajPJAjnEG/iiOS7i+zCtWGZR9G0NBKbXKh6X9m9UIsYX/N6vvQ==", "dev": true, "license": "ISC", "dependencies": { @@ -2117,9 +2246,9 @@ } }, "node_modules/flatted": { - "version": "3.3.2", - "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.3.2.tgz", - "integrity": "sha512-AiwGJM8YcNOaobumgtng+6NHuOqC3A7MixFeDafM3X9cIUM+xUXoS5Vfgf+OihAYe20fxqNM9yPBXJzRtZ/4eA==", + "version": "3.3.3", + "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.3.3.tgz", + "integrity": "sha512-GX+ysw4PBCz0PzosHDepZGANEuFCMLrnRTiEy9McGjmkCQYwRq4A/X786G/fjM/+OjsWSU1ZrY5qyARZmO/uwg==", "dev": true, "license": "ISC" }, @@ -2216,18 +2345,18 @@ } }, "node_modules/get-intrinsic": { - "version": "1.2.7", - "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.2.7.tgz", - "integrity": "sha512-VW6Pxhsrk0KAOqs3WEd0klDiF/+V7gQOpAvY1jVU/LHmaD/kQO4523aiJuikX/QAKYiW6x8Jh+RJej1almdtCA==", + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.3.0.tgz", + "integrity": "sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ==", "dev": true, "license": "MIT", "dependencies": { - "call-bind-apply-helpers": "^1.0.1", + "call-bind-apply-helpers": "^1.0.2", "es-define-property": "^1.0.1", "es-errors": "^1.3.0", - "es-object-atoms": "^1.0.0", + "es-object-atoms": "^1.1.1", "function-bind": "^1.1.2", - "get-proto": "^1.0.0", + "get-proto": "^1.0.1", "gopd": "^1.2.0", "has-symbols": "^1.1.0", "hasown": "^2.0.2", @@ -3883,9 +4012,9 @@ } }, "node_modules/reusify": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/reusify/-/reusify-1.0.4.tgz", - "integrity": "sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw==", + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/reusify/-/reusify-1.1.0.tgz", + "integrity": "sha512-g6QUff04oZpHs0eG5p83rFLhHeV00ug/Yf9nZM6fLeUrPguBTkTQOdpAWWspMh55TZfVQDPaN3NQJfbVRAxdIw==", "dev": true, "license": "MIT", "engines": { @@ -4430,7 +4559,6 @@ "version": "2.8.1", "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==", - "dev": true, "license": "0BSD" }, "node_modules/type-check": { @@ -4647,16 +4775,17 @@ } }, "node_modules/which-typed-array": { - "version": "1.1.18", - "resolved": "https://registry.npmjs.org/which-typed-array/-/which-typed-array-1.1.18.tgz", - "integrity": "sha512-qEcY+KJYlWyLH9vNbsr6/5j59AXk5ni5aakf8ldzBvGde6Iz4sxZGkJyWSAueTG7QhOvNRYb1lDdFmL5Td0QKA==", + "version": "1.1.19", + "resolved": "https://registry.npmjs.org/which-typed-array/-/which-typed-array-1.1.19.tgz", + "integrity": "sha512-rEvr90Bck4WZt9HHFC4DJMsjvu7x+r6bImz0/BrbWb7A2djJ8hnZMrWnHo9F8ssv0OMErasDhftrfROTyqSDrw==", "dev": true, "license": "MIT", "dependencies": { "available-typed-arrays": "^1.0.7", "call-bind": "^1.0.8", - "call-bound": "^1.0.3", - "for-each": "^0.3.3", + "call-bound": "^1.0.4", + "for-each": "^0.3.5", + "get-proto": "^1.0.1", "gopd": "^1.2.0", "has-tostringtag": "^1.0.2" }, diff --git a/package.json b/package.json index e9cca82..12048d2 100644 --- a/package.json +++ b/package.json @@ -97,6 +97,7 @@ "description": "Hackolade plugin for GraphQL", "disabled": false, "dependencies": { + "@hackolade/fetch": "1.1.1", "graphql": "16.10.0" }, "lint-staged": { @@ -126,4 +127,4 @@ "prettier": "3.5.0", "simple-git-hooks": "2.11.1" } -} \ No newline at end of file +} diff --git a/reverse_engineering/api.js b/reverse_engineering/api.js index 60c38db..0e001e4 100644 --- a/reverse_engineering/api.js +++ b/reverse_engineering/api.js @@ -1,13 +1,89 @@ /** - * @import { FileREData, REFromFileCallback } from "./types/types" + * @import { FileREData, REFromFileCallback, TestConnectionInfo, TestConnectionCallback, DisconnectCallback, REConnectionInfo } from "./types/types" + * @import { Logger } from "../shared/types/types" */ const { getFileName } = require('./helpers/getFileName'); const { parseSchema } = require('./helpers/parseSchema'); const { readFileContent } = require('./helpers/readFileContent'); const { getMappedSchema } = require('./mappers/schema'); +const { fetchIntrospectionSchema } = require('./helpers/fetchIntrospectionSchema'); +const { convertIntrospectionSchemaToGraphQLSchema } = require('./helpers/convertIntrospectionSchemaToGraphQLSchema'); module.exports = { + /** + * Common disconnect method - not needed for GraphQL API + * @param {null} connectionInfo + * @param {Logger} logger + * @param {DisconnectCallback} callback + */ + disconnect(connectionInfo, logger, callback) { + callback(); + }, + + /** + * Test a connection to the GraphQL server - executes introspection query + * @param {TestConnectionInfo} connectionInfo + * @param {Logger} logger + * @param {TestConnectionCallback} callback + * @returns {Promise} + */ + async testConnection(connectionInfo, logger, callback) { + try { + await fetchIntrospectionSchema({ connectionInfo, logger }); + callback(); + } catch (error) { + logger.log('error', error, 'Test connection error'); + callback(error); + } + }, + + /** + * + * @param {REConnectionInfo} data + * @param {Logger} logger + * @param callback + * @returns {Promise} + */ + async getDbCollectionsData(data, logger, callback) { + const introspectionSchema = await fetchIntrospectionSchema({ connectionInfo: data.connectionSettings, logger }); + // eslint-disable-next-line no-unused-vars + const graphQLSDLSchema = convertIntrospectionSchemaToGraphQLSchema(introspectionSchema); + // TODO: implement proper mapper for RE from instance + + callback( + null, + [ + { + dbName: 'New Graph', + collectionName: 'Query', + entityLevel: { + description: 'Test description', + }, + documents: [], + validation: { + jsonSchema: { + properties: { + 'test': { type: 'string' }, + }, + required: [], + }, + }, + emptyBucket: false, + bucketInfo: { + name: 'New Graph', + description: 'Test description', + }, + modelDefinitions: { + properties: {}, + }, + }, + ], + {}, + [], + ); + }, + /** * RE a GraphQL schema file and returns the mapped schema * @param {FileREData} data diff --git a/reverse_engineering/config.json b/reverse_engineering/config.json index 12ff3df..ad5546b 100644 --- a/reverse_engineering/config.json +++ b/reverse_engineering/config.json @@ -1,5 +1,17 @@ -{ - "menu_label": "GraphQL Schema File", - "name": "GraphQL", - "extensions": ["graphql", "gql"] -} +[ + { + "type": "file", + "menu_label": "GraphQL File...", + "name": "GraphQL", + "extensions": ["graphql", "gql"], + "useMultipleFileReverse": false + }, + { + "errors": {}, + "excludeDocKind": ["id"], + "type": "database", + "scenario": "getDataFromDB", + "connectionList": ["name", "host", "authType"], + "widestColumn": "host" + } +] diff --git a/reverse_engineering/connection_settings_modal/connectionSettingsModalConfig.json b/reverse_engineering/connection_settings_modal/connectionSettingsModalConfig.json new file mode 100644 index 0000000..95dffc4 --- /dev/null +++ b/reverse_engineering/connection_settings_modal/connectionSettingsModalConfig.json @@ -0,0 +1,88 @@ +[ + { + "lowerTab": "Connection", + "structure": [ + { + "inputLabel": "Name", + "inputKeyword": "name", + "inputType": "text", + "inputPlaceholder": "Name", + "description": "Choose a friendly connection name" + }, + { + "inputLabel": "GraphQL endpoint", + "inputKeyword": "host", + "inputType": "text", + "inputPlaceholder": "https://api.com/graphql", + "description": "Specify the GraphQL API endpoint URL", + "regex": "([^\\s])" + } + ] + }, + { + "tab": "Authentication", + "structure": [ + { + "inputLabel": "Authentication Type", + "inputKeyword": "authType", + "inputType": "select", + "defaultValue": "none", + "options": [ + { + "label": "None", + "value": "none" + }, + { + "label": "Basic", + "value": "basic" + }, + { + "label": "Bearer", + "value": "bearer" + } + ] + }, + { + "inputLabel": "Bearer Token", + "inputKeyword": "bearerToken", + "inputType": "text", + "inputPlaceholder": "Bearer Token", + "isHiddenKey": true, + "validation": { + "regex": "([^\\s])" + }, + "dependency": { + "key": "authType", + "value": ["bearer"] + } + }, + { + "inputLabel": "User Name", + "inputKeyword": "userName", + "inputType": "text", + "inputPlaceholder": "User Name", + "validation": { + "regex": "([^\\s])" + }, + "dependency": { + "key": "authType", + "value": ["basic"] + } + }, + { + "inputLabel": "Password", + "inputKeyword": "userPassword", + "inputType": "password", + "inputPlaceholder": "Password", + "isHiddenKey": true, + "validation": { + "regex": "([^\\s])" + }, + "dependency": { + "key": "authType", + "value": ["basic"] + } + } + ] + } +] diff --git a/reverse_engineering/helpers/convertIntrospectionSchemaToGraphQLSchema.js b/reverse_engineering/helpers/convertIntrospectionSchemaToGraphQLSchema.js new file mode 100644 index 0000000..b623507 --- /dev/null +++ b/reverse_engineering/helpers/convertIntrospectionSchemaToGraphQLSchema.js @@ -0,0 +1,19 @@ +/** + * @import { IntrospectionQuery } from 'graphql'; + */ + +const { buildClientSchema, printSchema } = require('graphql/utilities'); + +/** + * Convert introspection schema to GraphQL SDL schema + * @param {IntrospectionQuery} introspectionSchema + * @returns {string} + */ +function convertIntrospectionSchemaToGraphQLSchema(introspectionSchema) { + const schema = buildClientSchema(introspectionSchema); + return printSchema(schema); +} + +module.exports = { + convertIntrospectionSchemaToGraphQLSchema, +}; diff --git a/reverse_engineering/helpers/fetchIntrospectionSchema.js b/reverse_engineering/helpers/fetchIntrospectionSchema.js new file mode 100644 index 0000000..66b2fc4 --- /dev/null +++ b/reverse_engineering/helpers/fetchIntrospectionSchema.js @@ -0,0 +1,66 @@ +/** + * @import { ConnectionSettings } from '../../types/types'; + * @import { IntrospectionQuery } from 'graphql'; + */ + +const { getIntrospectionQuery } = require('graphql'); +const { hckFetch } = require('@hackolade/fetch'); + +/** + * Encode credentials to base64 for base authorization purposes + * @param {string} userName + * @param {string} userPassword + * @returns {string} + */ +function encodeCredentials(userName, userPassword) { + return Buffer.from(`${userName}:${userPassword}`).toString('base64'); +} + +/** + * Build request headers for the fetch request + * @param {AuthenticationType} authType + * @param {string} [bearerToken] + * @param {string} [userName] + * @param {string} [userPassword] + */ +function buildRequestHeaders({ authType, bearerToken, userName, userPassword }) { + const headers = { + 'Content-Type': 'application/json', + }; + + if (authType === 'basic') { + headers.Authorization = `Basic ${encodeCredentials(userName, userPassword)}`; + } else if (authType === 'bearer') { + headers.Authorization = `Bearer ${bearerToken}`; + } + + return headers; +} + +/** + * Fetch introspection schema from the GraphQL server + * @param {ConnectionSettings} connectionInfo + * @param {Logger} logger + * @returns {Promise} + */ +async function fetchIntrospectionSchema({ connectionInfo, logger }) { + try { + const options = { + method: 'POST', + headers: buildRequestHeaders(connectionInfo), + body: JSON.stringify({ query: getIntrospectionQuery() }), + }; + const response = await hckFetch(connectionInfo.host, options); + const introspectionSchemaResponse = await response.json(); + + logger.log('info', {}, 'Introspection schema fetched successfully'); + return introspectionSchemaResponse.data; + } catch (error) { + logger.log('error', error, 'Failed to fetch introspection schema'); + throw new error(); + } +} + +module.exports = { + fetchIntrospectionSchema, +}; diff --git a/reverse_engineering/package.json b/reverse_engineering/package.json index 61b34f9..bd46af0 100644 --- a/reverse_engineering/package.json +++ b/reverse_engineering/package.json @@ -1,4 +1,4 @@ { - "name": "GraphQL", - "type": "file" - } \ No newline at end of file + "name": "GraphQL", + "type": "multi" +} diff --git a/reverse_engineering/types/types.d.ts b/reverse_engineering/types/types.d.ts index d37585a..c25ee38 100644 --- a/reverse_engineering/types/types.d.ts +++ b/reverse_engineering/types/types.d.ts @@ -31,11 +31,71 @@ export type FileREModelLevelResponseData = { }; export type FileREData = { - filePath: string; + filePath: string; }; -export type Logger = { - log: (logType: string, logData: object, logMessage: string) => void; +export type REFromFileCallback = (err: Error | null, entitiesData: FileREEntityResponseData[], modelData: FileREModelLevelResponseData) => void; + +type ConnectionSourceType = 'database' | 'dataDictionary' | 'cloud' + +export type AuthenticationType = 'none' | 'basic' | 'bearer' + +type RecordSamplingMode = 'absolute' | 'relative' + +type RecordSamplingModeOptions = { + value: number +} + +type RecordSamplingSettings = { + absolute: RecordSamplingModeOptions; + relative: RecordSamplingModeOptions; + active: RecordSamplingMode; + maxValue: number; +} + +export type ConnectionSettings = { + id: string; + name: string; + host: string; + connectionSourceType: ConnectionSourceType; + authType: AuthenticationType; + bearerToken?: string; + userName?: string; + userPassword?: string; + target: 'GraphQL'; +} + +type GeneralRESettings = { + appVersion?: string; + tempFolder: string; + pluginVersion: string; + includeSystemCollection?: boolean; + includeEmptyCollection?: boolean; + pagination?: PaginationSettings; + recordSamplingSettings: RecordSamplingSettings; + queryRequestTimeout: number; + applyToInstanceQueryRequestTimeout?: number; + schemaRegistryConfig: boolean; + target?: 'GraphQL'; + appTarget?: 'GraphQL'; + pluginPath: string; + hiddenKeys: string[]; + excludeDocKind?: string[]; + probabilisticSchema?: boolean; + fieldInference: { + active: string; + } +} + +type PaginationSettings = { + enabled: boolean; + value: number; +} + +export type TestConnectionInfo = ConnectionSettings & GeneralRESettings; +export type REConnectionInfo = GeneralRESettings & { + connectionSettings: ConnectionSettings; }; -export type REFromFileCallback = (err: Error | null, entitiesData: FileREEntityResponseData[], modelData: FileREModelLevelResponseData) => void; \ No newline at end of file +export type TestConnectionCallback = (err: Error | null) => void; +export type DisconnectCallback = TestConnectionCallback; diff --git a/shared/types/types.d.ts b/shared/types/types.d.ts new file mode 100644 index 0000000..ea47b6c --- /dev/null +++ b/shared/types/types.d.ts @@ -0,0 +1,14 @@ +type LogType = 'info' | 'error'; + +type LogData = { + message?: string; + error?: Error; +} | Error; + +type LogTitle = string; + +export type Logger = { + log: (logType: LogType, logData: LogData, logTitle: LogTitle, hiddenKeys: string[]) => void; + clear: () => void; + progress: (data: object) => void; +}; From 8557ad51f6e2ce9b0efad7394f05d2276d7caa94 Mon Sep 17 00:00:00 2001 From: Vitalii Bedletskyi Date: Mon, 10 Mar 2025 17:20:26 +0200 Subject: [PATCH 2/4] reformat package.json --- package.json | 254 +++++++++++++++++++++++++-------------------------- 1 file changed, 127 insertions(+), 127 deletions(-) diff --git a/package.json b/package.json index 12048d2..166c776 100644 --- a/package.json +++ b/package.json @@ -1,130 +1,130 @@ { - "name": "GraphQL", - "version": "0.2.3", - "author": "hackolade", - "engines": { - "hackolade": "8.0.0", - "hackoladePlugin": "1.0.0", - "node": ">=20.18.0" - }, - "contributes": { - "target": { - "applicationTarget": "GraphQL", - "title": "GraphQL", - "versions": [] - }, - "features": { - "disableMultipleTypes": true, - "enableReverseEngineering": { - "jsonDocument": { - "entities": false, - "model_definitions": false - }, - "jsonSchema": { - "entities": false, - "model_definitions": false - }, - "ddl": { - "entities": false, - "model_definitions": false - }, - "xsd": { - "entities": false, - "model_definitions": false - }, - "excel": false, - "plugin": true, - "uploadPowerDesignerFile": false - }, - "disableDenormalization": true, - "enableForwardEngineering": { - "jsonDocument": false, - "jsonSchema": false, - "plugin": true, - "excel": false, - "apiSchema": false - }, - "disableCollibraDataDictionary": true, - "disablePatternField": true, - "disableChoices": true, - "enableJsonType": false, - "disableNamingConvention": { - "entityLevel": true, - "containerLevel": true, - "fieldLevel": { - "type": true, - "operationObject": true, - "extensions": true, - "media": true - } - }, - "disableDefinitions": { - "internal": true, - "external": true - }, - "disableRelationships": false, - "relationships": { - "definitionRelationships": true - }, - "disableJsonPreview": false, - "enableErdToggle": true, - "externalDefinitionsFromTargetSchema": true, - "apiTarget": true, - "FEScriptCommentsSupported": true, - "displayOptions": { - "hideNonNullableAttributes": true - }, - "openModelDefinitionsTabIfNoCollections": true, - "erdDefinitions": { - "enabled": true, - "propertyNames": [ - "schemas" - ] - }, - "definitionSources": { - "multipleSources": true - }, - "disablePickFromFieldList": true, - "hideDisabledAttributeTypes": true, - "showReferenceDefinitionNameInsteadOfType": true, - "disableReplaceByAttributes": true, - "polyglot": { - "disableDeriveFromPolyglotModel": true, - "disableConvertToPolyglotModel": true - } - } - }, - "description": "Hackolade plugin for GraphQL", - "disabled": false, - "dependencies": { + "name": "GraphQL", + "version": "0.2.3", + "author": "hackolade", + "engines": { + "hackolade": "8.0.0", + "hackoladePlugin": "1.0.0", + "node": ">=20.18.0" + }, + "contributes": { + "target": { + "applicationTarget": "GraphQL", + "title": "GraphQL", + "versions": [] + }, + "features": { + "disableMultipleTypes": true, + "enableReverseEngineering": { + "jsonDocument": { + "entities": false, + "model_definitions": false + }, + "jsonSchema": { + "entities": false, + "model_definitions": false + }, + "ddl": { + "entities": false, + "model_definitions": false + }, + "xsd": { + "entities": false, + "model_definitions": false + }, + "excel": false, + "plugin": true, + "uploadPowerDesignerFile": false + }, + "disableDenormalization": true, + "enableForwardEngineering": { + "jsonDocument": false, + "jsonSchema": false, + "plugin": true, + "excel": false, + "apiSchema": false + }, + "disableCollibraDataDictionary": true, + "disablePatternField": true, + "disableChoices": true, + "enableJsonType": false, + "disableNamingConvention": { + "entityLevel": true, + "containerLevel": true, + "fieldLevel": { + "type": true, + "operationObject": true, + "extensions": true, + "media": true + } + }, + "disableDefinitions": { + "internal": true, + "external": true + }, + "disableRelationships": false, + "relationships": { + "definitionRelationships": true + }, + "disableJsonPreview": false, + "enableErdToggle": true, + "externalDefinitionsFromTargetSchema": true, + "apiTarget": true, + "FEScriptCommentsSupported": true, + "displayOptions": { + "hideNonNullableAttributes": true + }, + "openModelDefinitionsTabIfNoCollections": true, + "erdDefinitions": { + "enabled": true, + "propertyNames": [ + "schemas" + ] + }, + "definitionSources": { + "multipleSources": true + }, + "disablePickFromFieldList": true, + "hideDisabledAttributeTypes": true, + "showReferenceDefinitionNameInsteadOfType": true, + "disableReplaceByAttributes": true, + "polyglot": { + "disableDeriveFromPolyglotModel": true, + "disableConvertToPolyglotModel": true + } + } + }, + "description": "Hackolade plugin for GraphQL", + "disabled": false, + "dependencies": { "@hackolade/fetch": "1.1.1", - "graphql": "16.10.0" - }, - "lint-staged": { - "*.{js,json}": "prettier --write" - }, - "simple-git-hooks": { - "pre-commit": "npx lint-staged", - "pre-push": "npx eslint . && npm run test:unit" - }, - "scripts": { - "lint": "eslint . --max-warnings=0", - "test:unit": "node --experimental-test-module-mocks --test-reporter dot --test ./test", - "package": "node esbuild.package.js", - "postinstall": "npx simple-git-hooks" - }, - "devDependencies": { - "@hackolade/hck-esbuild-plugins-pack": "0.0.1", - "esbuild": "0.25.0", - "esbuild-plugin-clean": "1.0.1", - "eslint": "9.20.1", - "eslint-config-prettier": "10.0.1", - "eslint-plugin-import": "2.31.0", - "eslint-plugin-prettier": "5.2.3", - "eslint-plugin-unused-imports": "4.1.4", - "globals": "15.15.0", - "lint-staged": "15.4.3", - "prettier": "3.5.0", - "simple-git-hooks": "2.11.1" - } + "graphql": "16.10.0" + }, + "lint-staged": { + "*.{js,json}": "prettier --write" + }, + "simple-git-hooks": { + "pre-commit": "npx lint-staged", + "pre-push": "npx eslint . && npm run test:unit" + }, + "scripts": { + "lint": "eslint . --max-warnings=0", + "test:unit": "node --experimental-test-module-mocks --test-reporter dot --test ./test", + "package": "node esbuild.package.js", + "postinstall": "npx simple-git-hooks" + }, + "devDependencies": { + "@hackolade/hck-esbuild-plugins-pack": "0.0.1", + "esbuild": "0.25.0", + "esbuild-plugin-clean": "1.0.1", + "eslint": "9.20.1", + "eslint-config-prettier": "10.0.1", + "eslint-plugin-import": "2.31.0", + "eslint-plugin-prettier": "5.2.3", + "eslint-plugin-unused-imports": "4.1.4", + "globals": "15.15.0", + "lint-staged": "15.4.3", + "prettier": "3.5.0", + "simple-git-hooks": "2.11.1" + } } From 25fe55515047a9905353c165000edcb40bc6472f Mon Sep 17 00:00:00 2001 From: Vitalii Bedletskyi Date: Mon, 10 Mar 2025 17:20:39 +0200 Subject: [PATCH 3/4] update title --- reverse_engineering/config.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/reverse_engineering/config.json b/reverse_engineering/config.json index ad5546b..2bb3b19 100644 --- a/reverse_engineering/config.json +++ b/reverse_engineering/config.json @@ -1,7 +1,7 @@ [ { "type": "file", - "menu_label": "GraphQL File...", + "menu_label": "GraphQL Schema File...", "name": "GraphQL", "extensions": ["graphql", "gql"], "useMultipleFileReverse": false From 8721bf9243dff3acb159adb1640e146b462107e3 Mon Sep 17 00:00:00 2001 From: Vitalii Bedletskyi Date: Mon, 10 Mar 2025 17:28:57 +0200 Subject: [PATCH 4/4] fix sonarlint remark --- reverse_engineering/helpers/fetchIntrospectionSchema.js | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/reverse_engineering/helpers/fetchIntrospectionSchema.js b/reverse_engineering/helpers/fetchIntrospectionSchema.js index 66b2fc4..28ae528 100644 --- a/reverse_engineering/helpers/fetchIntrospectionSchema.js +++ b/reverse_engineering/helpers/fetchIntrospectionSchema.js @@ -56,8 +56,9 @@ async function fetchIntrospectionSchema({ connectionInfo, logger }) { logger.log('info', {}, 'Introspection schema fetched successfully'); return introspectionSchemaResponse.data; } catch (error) { - logger.log('error', error, 'Failed to fetch introspection schema'); - throw new error(); + const message = 'Failed to fetch introspection schema'; + logger.log('error', error, message); + throw new Error(message); } }