From 24c7da7c2aec38a82daa01515752941ab93770eb Mon Sep 17 00:00:00 2001 From: Eric Wittmann Date: Wed, 2 Oct 2024 13:05:00 -0400 Subject: [PATCH] Restore the "Generate Client SDK" feature in the UI (#5289) * Restore the "Generate Client SDK" feature in the UI * Fix linting error * use kiota-wasm and wire everything up * Fix UI build errors * add ts-expect-error comment --------- Co-authored-by: Andrea Peruffo --- ui/ui-app/.gitignore | 1 + ui/ui-app/package-lock.json | 307 +++++++++++++++ ui/ui-app/package.json | 6 +- ui/ui-app/public/client-gen/.gitignore | 2 + ui/ui-app/public/client-gen/client-gen.js | 11 + .../components/modals/GenerateClientModal.tsx | 357 ++++++++++++++++++ ui/ui-app/src/app/components/modals/index.ts | 1 + .../src/app/pages/version/VersionPage.tsx | 10 +- .../components/tabs/VersionInfoTabContent.tsx | 9 + ui/ui-app/src/services/useDownloadService.ts | 19 +- 10 files changed, 719 insertions(+), 4 deletions(-) create mode 100644 ui/ui-app/public/client-gen/.gitignore create mode 100644 ui/ui-app/public/client-gen/client-gen.js create mode 100644 ui/ui-app/src/app/components/modals/GenerateClientModal.tsx diff --git a/ui/ui-app/.gitignore b/ui/ui-app/.gitignore index e67e11dbb4..cd2ead399b 100644 --- a/ui/ui-app/.gitignore +++ b/ui/ui-app/.gitignore @@ -1,2 +1,3 @@ /version.js /config.js +/public/kiota-wasm diff --git a/ui/ui-app/package-lock.json b/ui/ui-app/package-lock.json index f7b3e5a0c2..3cc17ceae6 100644 --- a/ui/ui-app/package-lock.json +++ b/ui/ui-app/package-lock.json @@ -37,6 +37,7 @@ }, "devDependencies": { "@apicurio/eslint-config": "0.3.0", + "@kiota-community/kiota-wasm": "^0.0.3", "@monaco-editor/react": "4.6.0", "@types/luxon": "3.4.2", "@types/pluralize": "0.0.33", @@ -45,6 +46,7 @@ "@typescript-eslint/eslint-plugin": "7.13.1", "@typescript-eslint/parser": "7.13.1", "@vitejs/plugin-react-swc": "3.7.1", + "copyfiles": "^2.4.1", "eslint": "8.57.0", "eslint-plugin-react-hooks": "4.6.2", "eslint-plugin-react-refresh": "0.4.7", @@ -844,6 +846,12 @@ "resolved": "https://registry.npmjs.org/@juggle/resize-observer/-/resize-observer-3.4.0.tgz", "integrity": "sha512-dfLbk+PwWvFzSxwk3n5ySL0hfBog779o8h68wK/7/APo/7cgyWp5jcXockbxdk5kFRkbeXWm4Fbi9FrdN381sA==" }, + "node_modules/@kiota-community/kiota-wasm": { + "version": "0.0.3", + "resolved": "https://registry.npmjs.org/@kiota-community/kiota-wasm/-/kiota-wasm-0.0.3.tgz", + "integrity": "sha512-8p9VqWE1PPy7QEznJi7o9Q3LrK0G82u/JJqHp5MxcbdFqGEKYabuZLr95vnho/yIpvFA/LEYlZ/rCpMaLn9HOQ==", + "dev": true + }, "node_modules/@microsoft/kiota-abstractions": { "version": "1.0.0-preview.66", "resolved": "https://registry.npmjs.org/@microsoft/kiota-abstractions/-/kiota-abstractions-1.0.0-preview.66.tgz", @@ -2046,6 +2054,54 @@ "node": ">= 6" } }, + "node_modules/cliui": { + "version": "7.0.4", + "resolved": "https://registry.npmjs.org/cliui/-/cliui-7.0.4.tgz", + "integrity": "sha512-OcRE68cOsVMXp1Yvonl/fzkQOyjLSu/8bhPDfQt0e0/Eb283TKP20Fs2MqoPsr9SwA595rRCA+QMzYc9nBP+JQ==", + "dev": true, + "dependencies": { + "string-width": "^4.2.0", + "strip-ansi": "^6.0.0", + "wrap-ansi": "^7.0.0" + } + }, + "node_modules/cliui/node_modules/emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", + "dev": true + }, + "node_modules/cliui/node_modules/string-width": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "dev": true, + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/cliui/node_modules/wrap-ansi": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", + "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", + "dev": true, + "dependencies": { + "ansi-styles": "^4.0.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/wrap-ansi?sponsor=1" + } + }, "node_modules/color-convert": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", @@ -2081,6 +2137,46 @@ "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==", "dev": true }, + "node_modules/copyfiles": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/copyfiles/-/copyfiles-2.4.1.tgz", + "integrity": "sha512-fereAvAvxDrQDOXybk3Qu3dPbOoKoysFMWtkY3mv5BsL8//OSZVL5DCLYqgRfY5cWirgRzlC+WSrxp6Bo3eNZg==", + "dev": true, + "dependencies": { + "glob": "^7.0.5", + "minimatch": "^3.0.3", + "mkdirp": "^1.0.4", + "noms": "0.0.0", + "through2": "^2.0.1", + "untildify": "^4.0.0", + "yargs": "^16.1.0" + }, + "bin": { + "copyfiles": "copyfiles", + "copyup": "copyfiles" + } + }, + "node_modules/copyfiles/node_modules/glob": { + "version": "7.2.3", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", + "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", + "deprecated": "Glob versions prior to v9 are no longer supported", + "dev": true, + "dependencies": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.1.1", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + }, + "engines": { + "node": "*" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, "node_modules/core-js": { "version": "3.26.1", "resolved": "https://registry.npmjs.org/core-js/-/core-js-3.26.1.tgz", @@ -2091,6 +2187,12 @@ "url": "https://opencollective.com/core-js" } }, + "node_modules/core-util-is": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.3.tgz", + "integrity": "sha512-ZQBvi1DcpJ4GDqanjucZ2Hj3wEO5pZDS89BWbkcrvdxksJorwUDDZamX9ldFkp9aw2lmBDLgkObEA4DWNJ9FYQ==", + "dev": true + }, "node_modules/cross-spawn": { "version": "7.0.3", "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz", @@ -2216,6 +2318,15 @@ "@esbuild/win32-x64": "0.21.5" } }, + "node_modules/escalade": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.2.0.tgz", + "integrity": "sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==", + "dev": true, + "engines": { + "node": ">=6" + } + }, "node_modules/escape-string-regexp": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", @@ -2628,6 +2739,15 @@ "node": "^8.16.0 || ^10.6.0 || >=11.0.0" } }, + "node_modules/get-caller-file": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz", + "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==", + "dev": true, + "engines": { + "node": "6.* || 8.* || >= 10.*" + } + }, "node_modules/glob": { "version": "10.3.10", "resolved": "https://registry.npmjs.org/glob/-/glob-10.3.10.tgz", @@ -2886,6 +3006,12 @@ "node": ">=8" } }, + "node_modules/isarray": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-0.0.1.tgz", + "integrity": "sha512-D2S+3GLxWH+uhrNEcoh/fnmYeP8E8/zHl644d/jdA0g2uyXvy3sb0qxotE+ne0LtccHknQzWwZEzhak7oJ0COQ==", + "dev": true + }, "node_modules/isexe": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", @@ -3094,6 +3220,18 @@ "node": ">=16 || 14 >=14.17" } }, + "node_modules/mkdirp": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-1.0.4.tgz", + "integrity": "sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw==", + "dev": true, + "bin": { + "mkdirp": "bin/cmd.js" + }, + "engines": { + "node": ">=10" + } + }, "node_modules/monaco-editor": { "version": "0.34.1", "resolved": "https://registry.npmjs.org/monaco-editor/-/monaco-editor-0.34.1.tgz", @@ -3130,6 +3268,16 @@ "integrity": "sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==", "dev": true }, + "node_modules/noms": { + "version": "0.0.0", + "resolved": "https://registry.npmjs.org/noms/-/noms-0.0.0.tgz", + "integrity": "sha512-lNDU9VJaOPxUmXcLb+HQFeUgQQPtMI24Gt6hgfuMHRJgMRHMF/qZ4HJD3GDru4sSw9IQl2jPjAYnQrdIeLbwow==", + "dev": true, + "dependencies": { + "inherits": "^2.0.1", + "readable-stream": "~1.0.31" + } + }, "node_modules/normalize-path": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", @@ -3352,6 +3500,12 @@ "node": ">= 0.8.0" } }, + "node_modules/process-nextick-args": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.1.tgz", + "integrity": "sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==", + "dev": true + }, "node_modules/prop-types": { "version": "15.8.1", "resolved": "https://registry.npmjs.org/prop-types/-/prop-types-15.8.1.tgz", @@ -3470,6 +3624,18 @@ "react-dom": ">=16.8" } }, + "node_modules/readable-stream": { + "version": "1.0.34", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-1.0.34.tgz", + "integrity": "sha512-ok1qVCJuRkNmvebYikljxJA/UEsKwLl2nI1OmaqAu4/UE+h0wKCHok4XkL/gvi39OacXvw59RJUOFUkDib2rHg==", + "dev": true, + "dependencies": { + "core-util-is": "~1.0.0", + "inherits": "~2.0.1", + "isarray": "0.0.1", + "string_decoder": "~0.10.x" + } + }, "node_modules/readdirp": { "version": "3.6.0", "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz", @@ -3484,6 +3650,15 @@ "node": ">=8.10.0" } }, + "node_modules/require-directory": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", + "integrity": "sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/resolve-from": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz", @@ -3579,6 +3754,12 @@ "queue-microtask": "^1.2.2" } }, + "node_modules/safe-buffer": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", + "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==", + "dev": true + }, "node_modules/sass": { "version": "1.69.3", "resolved": "https://registry.npmjs.org/sass/-/sass-1.69.3.tgz", @@ -3700,6 +3881,12 @@ "resolved": "https://registry.npmjs.org/state-local/-/state-local-1.0.7.tgz", "integrity": "sha512-HTEHMNieakEnoe33shBYcZ7NX83ACUjCu8c40iOGEZsngj9zRnkqS9j1pqQPXwobB0ZcVTk27REb7COQ0UR59w==" }, + "node_modules/string_decoder": { + "version": "0.10.31", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-0.10.31.tgz", + "integrity": "sha512-ev2QzSzWPYmy9GuqfIVildA4OdcGLeFZQrq5ys6RtiuF+RQQiZWr8TZNyAcuVXyQRYfEO+MsoB/1BuQVhOJuoQ==", + "dev": true + }, "node_modules/string-width": { "version": "5.1.2", "resolved": "https://registry.npmjs.org/string-width/-/string-width-5.1.2.tgz", @@ -3853,6 +4040,46 @@ "integrity": "sha512-N+8UisAXDGk8PFXP4HAzVR9nbfmVJ3zYLAWiTIoqC5v5isinhr+r5uaO8+7r3BMfuNIufIsA7RdpVgacC2cSpw==", "dev": true }, + "node_modules/through2": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/through2/-/through2-2.0.5.tgz", + "integrity": "sha512-/mrRod8xqpA+IHSLyGCQ2s8SPHiCDEeQJSep1jqLYeEUClOFG2Qsh+4FU6G9VeqpZnGW/Su8LQGc4YKni5rYSQ==", + "dev": true, + "dependencies": { + "readable-stream": "~2.3.6", + "xtend": "~4.0.1" + } + }, + "node_modules/through2/node_modules/isarray": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", + "integrity": "sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ==", + "dev": true + }, + "node_modules/through2/node_modules/readable-stream": { + "version": "2.3.8", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.8.tgz", + "integrity": "sha512-8p0AUk4XODgIewSi0l8Epjs+EVnWiK7NoDIEGU0HhE7+ZyY8D1IMY7odu5lRrFXGg71L15KG8QrPmum45RTtdA==", + "dev": true, + "dependencies": { + "core-util-is": "~1.0.0", + "inherits": "~2.0.3", + "isarray": "~1.0.0", + "process-nextick-args": "~2.0.0", + "safe-buffer": "~5.1.1", + "string_decoder": "~1.1.1", + "util-deprecate": "~1.0.1" + } + }, + "node_modules/through2/node_modules/string_decoder": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", + "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", + "dev": true, + "dependencies": { + "safe-buffer": "~5.1.0" + } + }, "node_modules/tinyduration": { "version": "3.3.1", "resolved": "https://registry.npmjs.org/tinyduration/-/tinyduration-3.3.1.tgz", @@ -3952,6 +4179,15 @@ "optional": true, "peer": true }, + "node_modules/untildify": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/untildify/-/untildify-4.0.0.tgz", + "integrity": "sha512-KK8xQ1mkzZeg9inewmFVDNkg3l5LUhoq9kN6iWYB/CC9YMG8HA+c1Q8HwDe6dEX7kErrEVNVBO3fWsVq5iDgtw==", + "dev": true, + "engines": { + "node": ">=8" + } + }, "node_modules/uri-js": { "version": "4.4.1", "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz", @@ -3973,6 +4209,12 @@ "react-dom": "16.8.0 - 18" } }, + "node_modules/util-deprecate": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", + "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==", + "dev": true + }, "node_modules/uuid": { "version": "10.0.0", "resolved": "https://registry.npmjs.org/uuid/-/uuid-10.0.0.tgz", @@ -4178,6 +4420,24 @@ "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==", "dev": true }, + "node_modules/xtend": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.2.tgz", + "integrity": "sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ==", + "dev": true, + "engines": { + "node": ">=0.4" + } + }, + "node_modules/y18n": { + "version": "5.0.8", + "resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz", + "integrity": "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==", + "dev": true, + "engines": { + "node": ">=10" + } + }, "node_modules/yallist": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", @@ -4195,6 +4455,53 @@ "node": ">= 14" } }, + "node_modules/yargs": { + "version": "16.2.0", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-16.2.0.tgz", + "integrity": "sha512-D1mvvtDG0L5ft/jGWkLpG1+m0eQxOfaBvTNELraWj22wSVUMWxZUvYgJYcKh6jGGIkJFhH4IZPQhR4TKpc8mBw==", + "dev": true, + "dependencies": { + "cliui": "^7.0.2", + "escalade": "^3.1.1", + "get-caller-file": "^2.0.5", + "require-directory": "^2.1.1", + "string-width": "^4.2.0", + "y18n": "^5.0.5", + "yargs-parser": "^20.2.2" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/yargs-parser": { + "version": "20.2.9", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-20.2.9.tgz", + "integrity": "sha512-y11nGElTIV+CT3Zv9t7VKl+Q3hTQoT9a1Qzezhhl6Rp21gJ/IVTW7Z3y9EWXhuUBC2Shnf+DX0antecpAwSP8w==", + "dev": true, + "engines": { + "node": ">=10" + } + }, + "node_modules/yargs/node_modules/emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", + "dev": true + }, + "node_modules/yargs/node_modules/string-width": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "dev": true, + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=8" + } + }, "node_modules/yocto-queue": { "version": "0.1.0", "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz", diff --git a/ui/ui-app/package.json b/ui/ui-app/package.json index 805881a972..53d24da14c 100644 --- a/ui/ui-app/package.json +++ b/ui/ui-app/package.json @@ -6,7 +6,7 @@ "license": "Apache-2.0", "private": true, "scripts": { - "postinstall": "node .fix_yaml.cjs", + "postinstall": "node .fix_yaml.cjs && rimraf ./public/kiota-wasm && copyfiles -u 4 'node_modules/@kiota-community/kiota-wasm/dist/**/*.*' 'public/kiota-wasm'", "clean": "rimraf dist", "dev": "vite", "build": "tsc && vite build", @@ -14,6 +14,7 @@ }, "devDependencies": { "@apicurio/eslint-config": "0.3.0", + "@kiota-community/kiota-wasm": "^0.0.3", "@monaco-editor/react": "4.6.0", "@types/luxon": "3.4.2", "@types/pluralize": "0.0.33", @@ -22,6 +23,7 @@ "@typescript-eslint/eslint-plugin": "7.13.1", "@typescript-eslint/parser": "7.13.1", "@vitejs/plugin-react-swc": "3.7.1", + "copyfiles": "^2.4.1", "eslint": "8.57.0", "eslint-plugin-react-hooks": "4.6.2", "eslint-plugin-react-refresh": "0.4.7", @@ -31,8 +33,8 @@ "vite-tsconfig-paths": "5.0.1" }, "dependencies": { - "@apicurio/common-ui-components": "2.0.4", "@apicurio/apicurio-registry-sdk": "file:../../typescript-sdk", + "@apicurio/common-ui-components": "2.0.4", "@apicurio/data-models": "1.1.27", "@microsoft/kiota-abstractions": "1.0.0-preview.66", "@microsoft/kiota-http-fetchlibrary": "1.0.0-preview.65", diff --git a/ui/ui-app/public/client-gen/.gitignore b/ui/ui-app/public/client-gen/.gitignore new file mode 100644 index 0000000000..0916ae8b4e --- /dev/null +++ b/ui/ui-app/public/client-gen/.gitignore @@ -0,0 +1,2 @@ +dist +kiota diff --git a/ui/ui-app/public/client-gen/client-gen.js b/ui/ui-app/public/client-gen/client-gen.js new file mode 100644 index 0000000000..b29ea5c962 --- /dev/null +++ b/ui/ui-app/public/client-gen/client-gen.js @@ -0,0 +1,11 @@ +import { generate } from "./dist/main.js"; + +try { + if (window.kiota === undefined) { + window.kiota = {}; + window.kiota.generate = generate; + console.log("Kiota is now available in the window"); + } +} catch (e) { + console.warn("Kiota not available"); +} diff --git a/ui/ui-app/src/app/components/modals/GenerateClientModal.tsx b/ui/ui-app/src/app/components/modals/GenerateClientModal.tsx new file mode 100644 index 0000000000..b4bf6db778 --- /dev/null +++ b/ui/ui-app/src/app/components/modals/GenerateClientModal.tsx @@ -0,0 +1,357 @@ +import React, { FunctionComponent, useEffect, useState } from "react"; +import { + Button, + ExpandableSection, + Form, + FormGroup, FormHelperText, + Grid, + GridItem, HelperText, HelperTextItem, + Modal, + TextInput +} from "@patternfly/react-core"; +import { If, ObjectSelect } from "@apicurio/common-ui-components"; +import { ClientGeneration } from "@services/useGroupsService.ts"; +import { DownloadService, useDownloadService } from "@services/useDownloadService.ts"; +import { CheckCircleIcon } from "@patternfly/react-icons"; +import { CodeEditor } from "@patternfly/react-code-editor"; + + +/** + * Properties + */ +export type GenerateClientModalProps = { + artifactContent: string; + isOpen: boolean; + onClose: () => void; +}; + +/** + * A modal to prompt the user to generate a client SDK. + */ +export const GenerateClientModal: FunctionComponent = (props: GenerateClientModalProps) => { + const [data, setData] = useState({ + content: props.artifactContent, + clientClassName: "MySdkClient", + namespaceName: "io.example.sdk", + language: "Java", + includePatterns: "", + excludePatterns: "", + }); + const [isValid, setIsValid] = useState(true); + const [isErrorVisible, setIsErrorVisible] = useState(false); + const [errorMessage, setErrorMessage] = useState(""); + const [isGenerating, setIsGenerating] = useState(false); + const [isGenerated, setIsGenerated] = useState(false); + const [isExpanded, setIsExpanded] = useState(false); + + const download: DownloadService = useDownloadService(); + + useEffect(() => { + validate(); + }, [data]); + + useEffect(() => { + setData({ + ...data, + content: props.artifactContent + }); + }, [props.artifactContent]); + + const onToggle = (_evt: any, isExpanded: boolean): void => { + setIsExpanded(isExpanded); + }; + + const triggerDownload = (content: string): void => { + const fname: string = `client-${data.language.toLowerCase()}.zip`; + download.downloadBase64DataToFS(content, fname); + }; + + const doGenerate = async (): Promise => { + setIsErrorVisible(false); + setErrorMessage(""); + setIsGenerating(true); + setIsGenerated(false); + + // @ts-expect-error unsafe inclusion of the wasm assets + const { generate } = await import("/kiota-wasm/main.js?url"); + + try { + console.debug("GENERATING USING KIOTA:"); + console.debug(data); + + const zipData = await generate( + data.content, + data.language, + data.clientClassName, + data.namespaceName, + data.includePatterns, + data.excludePatterns, + ); + + setIsErrorVisible(false); + setIsGenerated(true); + + setTimeout(() => { + triggerDownload(zipData); + setIsGenerating(false); + }, 250); + } catch (e) { + setIsErrorVisible(true); + setErrorMessage("" + e); + setIsGenerating(false); + setIsGenerated(false); + console.error(e); + } + }; + + const onLanguageSelect = (selectedLanguage: any): void => { + const newLang: string = selectedLanguage.id; + setData({ + ...data, + language: newLang, + }); + }; + + const onClientNameChange = (_evt: any, value: string): void => { + setData({ + ...data, + clientClassName: value, + }); + }; + + const onNamespaceChange = (_evt: any, value: string): void => { + setData({ + ...data, + namespaceName: value, + }); + }; + + const onIncludePatternsChange = (_evt: any, value: string): void => { + setData({ + ...data, + includePatterns: value, + }); + }; + + const onExcludePatternsChange = (_evt: any, value: string): void => { + setData({ + ...data, + excludePatterns: value, + }); + }; + + const isGenerateEnabled = (): boolean => { + return isValid; + }; + + const validate = (): void => { + const isValid: boolean = ( + data.clientClassName !== undefined && + data.clientClassName.trim().length > 0 && + data.namespaceName !== undefined && + data.namespaceName.trim().length > 0 + ); + setIsValid(isValid); + }; + + const generatingProps = { + spinnerAriaValueText: "Generating", + spinnerAriaLabelledBy: "generate-client-button", + isLoading: isGenerating + } as any; + + let actions = [ + , + + ]; + if (isErrorVisible) { + actions = [ + + ]; + } + + const onEditorDidMount = (editor: any, monaco: any) => { + editor.layout(); + editor.focus(); + monaco.editor.getModels()[0].updateOptions({ tabSize: 4 }); + }; + + const languages = [ + { id: "CSharp", testId: "language-type-csharp" }, + { id: "Go", testId: "language-type-go" }, + { id: "Java", testId: "language-type-java" }, + { id: "PHP", testId: "language-type-php" }, + { id: "Python", testId: "language-type-python" }, + { id: "Ruby", testId: "language-type-ruby" }, + { id: "Swift", testId: "language-type-swift" }, + { id: "TypeScript", testId: "language-type-typescript" }, + ]; + const selectedLanguage: any = languages.filter(item => item.id === data.language)[0]; + + return ( + +
+ +

+ Invalid artifact content. See the log below for details. When the issue is resolved, + upload a new version of the artifact and then try again. +

+ +
+ + + + + Configure your client SDK before you generate and download it. You must manually regenerate the client + SDK each time a new version of the artifact is registered. + + + + + Language} + fieldId="form-language" + isRequired={true} + > +
+ item.id} + itemToTestId={item => item.testId} + appendTo="document" + testId="select-language" + toggleId="select-language-toggle" + /> +
+
+
+ + + + + + + + + + + + + + + + +

Include paths

+

+ Enter a comma-separated list of patterns to specify the paths used to generate the + client SDK (for example, /., **/my-path/*). +

+ + + + + + If this field is empty, all paths are included + + + + + + + + + If this field is empty, no paths are excluded + + + + +
+
+
+
+
+
+ ); + +}; diff --git a/ui/ui-app/src/app/components/modals/index.ts b/ui/ui-app/src/app/components/modals/index.ts index dda63bc0cf..d56565d02b 100644 --- a/ui/ui-app/src/app/components/modals/index.ts +++ b/ui/ui-app/src/app/components/modals/index.ts @@ -9,3 +9,4 @@ export * from "./EditCommentModal"; export * from "./EditMetaDataModal"; export * from "./InvalidContentModal"; export * from "./LabelsFormGroup"; +export * from "./GenerateClientModal"; \ No newline at end of file diff --git a/ui/ui-app/src/app/pages/version/VersionPage.tsx b/ui/ui-app/src/app/pages/version/VersionPage.tsx index f7981b527c..fdcad0f2c2 100644 --- a/ui/ui-app/src/app/pages/version/VersionPage.tsx +++ b/ui/ui-app/src/app/pages/version/VersionPage.tsx @@ -13,7 +13,7 @@ import { VersionPageHeader } from "@app/pages"; import { ReferencesTabContent } from "@app/pages/version/components/tabs/ReferencesTabContent.tsx"; -import { ConfirmDeleteModal, EditMetaDataModal, IfFeature, MetaData } from "@app/components"; +import { ConfirmDeleteModal, EditMetaDataModal, GenerateClientModal, IfFeature, MetaData } from "@app/components"; import { ContentTypes } from "@models/contentTypes.model.ts"; import { PleaseWaitModal } from "@apicurio/common-ui-components"; import { AppNavigation, useAppNavigation } from "@services/useAppNavigation.ts"; @@ -41,6 +41,7 @@ export const VersionPage: FunctionComponent = () => { const [isEditModalOpen, setIsEditModalOpen] = useState(false); const [isPleaseWaitModalOpen, setIsPleaseWaitModalOpen] = useState(false); const [pleaseWaitMessage, setPleaseWaitMessage] = useState(""); + const [isGenerateClientModalOpen, setIsGenerateClientModalOpen] = useState(false); const appNavigation: AppNavigation = useAppNavigation(); const logger: LoggerService = useLoggerService(); @@ -223,7 +224,9 @@ export const VersionPage: FunctionComponent = () => { setIsGenerateClientModalOpen(true)} /> , @@ -303,6 +306,11 @@ export const VersionPage: FunctionComponent = () => { onClose={onEditModalClose} onEditMetaData={doEditMetaData} /> + setIsGenerateClientModalOpen(false)} + isOpen={isGenerateClientModalOpen} + /> diff --git a/ui/ui-app/src/app/pages/version/components/tabs/VersionInfoTabContent.tsx b/ui/ui-app/src/app/pages/version/components/tabs/VersionInfoTabContent.tsx index 8b3e16f136..26f26ab715 100644 --- a/ui/ui-app/src/app/pages/version/components/tabs/VersionInfoTabContent.tsx +++ b/ui/ui-app/src/app/pages/version/components/tabs/VersionInfoTabContent.tsx @@ -29,7 +29,9 @@ import { VersionComments } from "@app/pages"; export type VersionInfoTabContentProps = { artifact: ArtifactMetaData; version: VersionMetaData; + codegenEnabled: boolean; onEditMetaData: () => void; + onGenerateClient: () => void; }; /** @@ -58,6 +60,13 @@ export const VersionInfoTabContent: FunctionComponent Version metadata + + +