From 0d47046b5d1b056681e978a23773cdcbfa3beca0 Mon Sep 17 00:00:00 2001 From: John Calderon <81483067+johncalesp@users.noreply.github.com> Date: Wed, 29 Mar 2023 12:40:14 -0400 Subject: [PATCH] Providers schema (#53) * read local yaml file * completed unit testing * added general yaml schema for external cloud providers | changes unit testing | changed parsers.js * changed URL providers to S3 * get data from multiple urls * changed extension setting description * changed to centML S3 * local tests * local changes to skylinesession * installation of json parser * changed to json schema * finished reading json from external urls and show incorrect data * changed unit testing and tested with build script * changed default providers settings to empty string * PR review * changed utils.js file --- README.md | 32 +- skyline-vscode/package.json | 5 + skyline-vscode/react-ui/package-lock.json | 462 +++++++++++++----- skyline-vscode/react-ui/package.json | 8 +- skyline-vscode/react-ui/src/App.js | 11 +- .../react-ui/src/components/Modals.js | 48 ++ .../react-ui/src/components/ScatterGraph.js | 2 +- .../react-ui/src/data/properties.js | 25 + skyline-vscode/react-ui/src/data/providers.js | 283 ----------- .../src/schema/CloudProvidersSchema.js | 62 +++ .../react-ui/src/sections/DeploymentTab.js | 3 +- .../src/sections/DeploymentTab.test.js | 45 +- .../react-ui/src/sections/Habitat.js | 5 +- .../react-ui/src/sections/Habitat.test.js | 4 +- .../react-ui/src/sections/ProviderPanel.js | 450 +++++++++-------- .../src/sections/ProviderPanel.test.js | 58 ++- skyline-vscode/react-ui/src/utils/parsers.js | 90 ++++ .../react-ui/src/utils/parsers.test.js | 138 ++++++ skyline-vscode/react-ui/src/utils/utils.js | 202 ++++---- skyline-vscode/src/extension.ts | 2 +- skyline-vscode/src/skyline_session.ts | 19 +- skyline-vscode/src/utils.ts | 2 +- 22 files changed, 1199 insertions(+), 757 deletions(-) create mode 100644 skyline-vscode/react-ui/src/components/Modals.js create mode 100644 skyline-vscode/react-ui/src/data/properties.js delete mode 100644 skyline-vscode/react-ui/src/data/providers.js create mode 100644 skyline-vscode/react-ui/src/schema/CloudProvidersSchema.js create mode 100644 skyline-vscode/react-ui/src/utils/parsers.js create mode 100644 skyline-vscode/react-ui/src/utils/parsers.test.js diff --git a/README.md b/README.md index 3514725..e05ddab 100644 --- a/README.md +++ b/README.md @@ -29,6 +29,35 @@ To install, either: Once you have the vsix file, run `code --install-extension vscode*.vsix` to install the extension. **Note: the file [build_vsix_dev.sh] is only to be used for development** +**Adding cloud instances to the Deployment Tab:** You can include information about the instances that you use thrugh the extension settings. There you will find an option named **providers** that accepts a list of urls separated by commas. Each url must be a JSON file that follows the schema specified here : [schema](skyline-vscode/react-ui/src/schema/CloudProvidersSchema.js).
+Additionally, you need to add the necessary access so the extension can read the file.
+You can use an AWS S3 bucket to store your files. Change CORS settings in Permissions tab. + +**CORS requirements**: +``` +[ + { + "AllowedHeaders": [], + "AllowedMethods": [ + "GET" + ], + "AllowedOrigins": [ + "http://*", + "https://*", + "vscode-webview://*" + ], + "ExposeHeaders": [] + } +] +``` +and allow public access to the file + +This is our [file](https://deepview-explorer-public.s3.amazonaws.com/vscode-cloud-providers/providers.json) that you can use as an example. + + + + + ### Backend Installation This plugin requires Skyline (the installation instruction for which can be found [here](https://github.com/CentML/skyline)) and (optionally) Habitat (used to extrapolate GPU runtimes, instructions found [here](https://github.com/CentML/habitat)). **Skyline must be launched before running this extension.** @@ -36,7 +65,8 @@ After installation, please make note of the path to the `skyline` binary. To do ## Usage example 1. Open one of the examples DNN project examples, i.e. [Resnet](https://github.com/CentML/skyline/tree/main/examples/resnet) from Skyline in VSCode -2. Press `Ctrl+Shift+P`, then select `Skyline` from the dropdown list. +2. (Optional) You can add other external cloud instances. Use the CloudProvidersSchema.yaml file as a reference. This file is located under the schemas folder. Once you have created your own yaml file, you can reference the path to this file in the extension settings in **providers** option. The file will be read when the extension is called. +3. Press `Ctrl+Shift+P`, then select `Skyline` from the dropdown list. 3. Click on `Begin Analysis`. ## Disabling telemetry diff --git a/skyline-vscode/package.json b/skyline-vscode/package.json index d3684cb..8357492 100644 --- a/skyline-vscode/package.json +++ b/skyline-vscode/package.json @@ -36,6 +36,11 @@ "default": 60120, "description": "Specifies the port of the profiler." }, + "skyline.providers": { + "type": "string", + "default": "", + "description": "additional urls separated by commas" + }, "skyline.isTelemetryEnabled": { "type": "string", "default": "Ask me", diff --git a/skyline-vscode/react-ui/package-lock.json b/skyline-vscode/react-ui/package-lock.json index a69e29a..2afb952 100644 --- a/skyline-vscode/react-ui/package-lock.json +++ b/skyline-vscode/react-ui/package-lock.json @@ -5,9 +5,10 @@ "requires": true, "dependencies": { "@adobe/css-tools": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/@adobe/css-tools/-/css-tools-4.0.1.tgz", - "integrity": "sha512-+u76oB43nOHrF4DDWRLWDCtci7f3QJoEBigemIdIeTi1ODqjx6Tad9NCVnPRwewWlKkVab5PlK8DCtPTyX7S8g==" + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/@adobe/css-tools/-/css-tools-4.2.0.tgz", + "integrity": "sha512-E09FiIft46CmH5Qnjb0wsW54/YQd69LsxeKUOWawmws1XWvyFGURnAChH0mlr7YPFR1ofwvUQfcL0J3lMxXqPA==", + "dev": true }, "@ampproject/remapping": { "version": "2.2.0", @@ -1533,6 +1534,17 @@ "strip-json-comments": "^3.1.1" }, "dependencies": { + "ajv": { + "version": "6.12.6", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", + "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", + "requires": { + "fast-deep-equal": "^3.1.1", + "fast-json-stable-stringify": "^2.0.0", + "json-schema-traverse": "^0.4.1", + "uri-js": "^4.2.2" + } + }, "argparse": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", @@ -1562,6 +1574,11 @@ "argparse": "^2.0.1" } }, + "json-schema-traverse": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", + "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==" + }, "ms": { "version": "2.1.2", "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", @@ -1660,6 +1677,14 @@ "resolve-from": "^5.0.0" }, "dependencies": { + "argparse": { + "version": "1.0.10", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz", + "integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==", + "requires": { + "sprintf-js": "~1.0.2" + } + }, "camelcase": { "version": "5.3.1", "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-5.3.1.tgz", @@ -1674,6 +1699,15 @@ "path-exists": "^4.0.0" } }, + "js-yaml": { + "version": "3.14.1", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.14.1.tgz", + "integrity": "sha512-okMH7OXXJ7YrN9Ok3/SXrnu4iX9yOk+25nqX4imS2npuvTYDmo/QEZoqwZkYaIDk3jVvBOTOIEgEhaLOynBS9g==", + "requires": { + "argparse": "^1.0.7", + "esprima": "^4.0.0" + } + }, "locate-path": { "version": "5.0.0", "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz", @@ -1916,11 +1950,12 @@ } }, "@jest/expect-utils": { - "version": "29.4.1", - "resolved": "https://registry.npmjs.org/@jest/expect-utils/-/expect-utils-29.4.1.tgz", - "integrity": "sha512-w6YJMn5DlzmxjO00i9wu2YSozUYRBhIoJ6nQwpMYcBMtiqMGJm1QBzOf6DDgRao8dbtpDoaqLg6iiQTvv0UHhQ==", + "version": "29.5.0", + "resolved": "https://registry.npmjs.org/@jest/expect-utils/-/expect-utils-29.5.0.tgz", + "integrity": "sha512-fmKzsidoXQT2KwnrwE0SQq3uj8Z763vzR8LnLBwC2qYWEFpjX8daRsk6rHUM1QvNlEW/UJXNXm59ztmJJWs2Mg==", + "dev": true, "requires": { - "jest-get-type": "^29.2.0" + "jest-get-type": "^29.4.3" } }, "@jest/fake-timers": { @@ -2173,9 +2208,10 @@ } }, "@jest/schemas": { - "version": "29.4.0", - "resolved": "https://registry.npmjs.org/@jest/schemas/-/schemas-29.4.0.tgz", - "integrity": "sha512-0E01f/gOZeNTG76i5eWWSupvSHaIINrTie7vCyjiYFKgzNdyEGd12BUv4oNBFHOqlHDbtoJi3HrQ38KCC90NsQ==", + "version": "29.4.3", + "resolved": "https://registry.npmjs.org/@jest/schemas/-/schemas-29.4.3.tgz", + "integrity": "sha512-VLYKXQmtmuEz6IxJsrZwzG9NvtkQsWNnWMsKxqWNu3+CnfzJQhp0WDDKWLVV9hLKr0l3SLLFRqcYHjhtyuDVxg==", + "dev": true, "requires": { "@sinclair/typebox": "^0.25.16" } @@ -2310,11 +2346,12 @@ } }, "@jest/types": { - "version": "29.4.1", - "resolved": "https://registry.npmjs.org/@jest/types/-/types-29.4.1.tgz", - "integrity": "sha512-zbrAXDUOnpJ+FMST2rV7QZOgec8rskg2zv8g2ajeqitp4tvZiyqTCYXANrKsM+ryj5o+LI+ZN2EgU9drrkiwSA==", + "version": "29.5.0", + "resolved": "https://registry.npmjs.org/@jest/types/-/types-29.5.0.tgz", + "integrity": "sha512-qbu7kN6czmVRc3xWFQcAN03RAUamgppVUdXrvl1Wr3jlNF93o9mJbGcDWrwGB6ht44u7efB1qCFgVQmca24Uog==", + "dev": true, "requires": { - "@jest/schemas": "^29.4.0", + "@jest/schemas": "^29.4.3", "@types/istanbul-lib-coverage": "^2.0.0", "@types/istanbul-reports": "^3.0.0", "@types/node": "*", @@ -2326,6 +2363,7 @@ "version": "4.1.2", "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dev": true, "requires": { "ansi-styles": "^4.1.0", "supports-color": "^7.1.0" @@ -2535,9 +2573,10 @@ "integrity": "sha512-LwzQKA4vzIct1zNZzBmRKI9QuNpLgTQMEjsQLf3BXuGYb3QPTP4Yjf6mkdX+X1mYttZ808QpOwAzZjv28kq7DA==" }, "@sinclair/typebox": { - "version": "0.25.21", - "resolved": "https://registry.npmjs.org/@sinclair/typebox/-/typebox-0.25.21.tgz", - "integrity": "sha512-gFukHN4t8K4+wVC+ECqeqwzBDeFeTzBXroBTqE6vcWrQGbEUpHO7LYdG0f4xnvYq4VOEwITSlHlp0JBAIFMS/g==" + "version": "0.25.24", + "resolved": "https://registry.npmjs.org/@sinclair/typebox/-/typebox-0.25.24.tgz", + "integrity": "sha512-XJfwUVUKDHF5ugKwIcxEgc9k8b7HbznCp6eUfWgu710hMPNIO4aw4/zB5RogDQz8nd6gyCDpU9O/m6qYEWY6yQ==", + "dev": true }, "@sinonjs/commons": { "version": "1.8.6", @@ -2676,13 +2715,14 @@ } }, "@testing-library/dom": { - "version": "8.17.1", - "resolved": "https://registry.npmjs.org/@testing-library/dom/-/dom-8.17.1.tgz", - "integrity": "sha512-KnH2MnJUzmFNPW6RIKfd+zf2Wue8mEKX0M3cpX6aKl5ZXrJM1/c/Pc8c2xDNYQCnJO48Sm5ITbMXgqTr3h4jxQ==", + "version": "8.20.0", + "resolved": "https://registry.npmjs.org/@testing-library/dom/-/dom-8.20.0.tgz", + "integrity": "sha512-d9ULIT+a4EXLX3UU8FBjauG9NnsZHkHztXoIcTsOKoOw030fyjheN9svkTULjJxtYag9DZz5Jz5qkWZDPxTFwA==", + "dev": true, "requires": { "@babel/code-frame": "^7.10.4", "@babel/runtime": "^7.12.5", - "@types/aria-query": "^4.2.0", + "@types/aria-query": "^5.0.1", "aria-query": "^5.0.0", "chalk": "^4.1.0", "dom-accessibility-api": "^0.5.9", @@ -2694,6 +2734,7 @@ "version": "4.1.2", "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dev": true, "requires": { "ansi-styles": "^4.1.0", "supports-color": "^7.1.0" @@ -2705,6 +2746,7 @@ "version": "5.16.5", "resolved": "https://registry.npmjs.org/@testing-library/jest-dom/-/jest-dom-5.16.5.tgz", "integrity": "sha512-N5ixQ2qKpi5OLYfwQmUb/5mSV9LneAcaUfp32pn4yCnpb8r/Yz0pXFPck21dIicKmi+ta5WRAknkZCfA8refMA==", + "dev": true, "requires": { "@adobe/css-tools": "^4.0.1", "@babel/runtime": "^7.9.2", @@ -2718,19 +2760,20 @@ } }, "@testing-library/react": { - "version": "12.1.5", - "resolved": "https://registry.npmjs.org/@testing-library/react/-/react-12.1.5.tgz", - "integrity": "sha512-OfTXCJUFgjd/digLUuPxa0+/3ZxsQmE7ub9kcbW/wi96Bh3o/p5vrETcBGfP17NWPGqeYYl5LTRpwyGoMC4ysg==", + "version": "12.1.2", + "resolved": "https://registry.npmjs.org/@testing-library/react/-/react-12.1.2.tgz", + "integrity": "sha512-ihQiEOklNyHIpo2Y8FREkyD1QAea054U0MVbwH1m8N9TxeFz+KoJ9LkqoKqJlzx2JDm56DVwaJ1r36JYxZM05g==", + "dev": true, "requires": { "@babel/runtime": "^7.12.5", - "@testing-library/dom": "^8.0.0", - "@types/react-dom": "<18.0.0" + "@testing-library/dom": "^8.0.0" } }, "@testing-library/user-event": { "version": "13.5.0", "resolved": "https://registry.npmjs.org/@testing-library/user-event/-/user-event-13.5.0.tgz", "integrity": "sha512-5Kwtbo3Y/NowpkbRuSepbyMFkZmHgD+vPzYB/RJ4oxt5Gj/avFFBYjhw27cqSVPVw/3a67NK1PbiIr9k4Gwmdg==", + "dev": true, "requires": { "@babel/runtime": "^7.12.5" } @@ -2746,9 +2789,10 @@ "integrity": "sha512-L7z9BgrNEcYyUYtF+HaEfiS5ebkh9jXqbszz7pC0hRBPaatV0XjSD3+eHrpqFemQfgwiFF0QPIarnIihIDn7OA==" }, "@types/aria-query": { - "version": "4.2.2", - "resolved": "https://registry.npmjs.org/@types/aria-query/-/aria-query-4.2.2.tgz", - "integrity": "sha512-HnYpAE1Y6kRyKM/XkEuiRQhTHvkzMBurTHnpFLYLBGPIylZNPs9jJcuOOYWxPLJCSEtmZT0Y8rHDokKN7rRTig==" + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/@types/aria-query/-/aria-query-5.0.1.tgz", + "integrity": "sha512-XTIieEY+gvJ39ChLcB4If5zHtPxt3Syj5rgZR+e1ctpmK8NjPf0zFqsz4JpLJT0xla9GFDKjy8Cpu331nrmE1Q==", + "dev": true }, "@types/babel__core": { "version": "7.1.19", @@ -2962,9 +3006,10 @@ } }, "@types/jest": { - "version": "29.0.0", - "resolved": "https://registry.npmjs.org/@types/jest/-/jest-29.0.0.tgz", - "integrity": "sha512-X6Zjz3WO4cT39Gkl0lZ2baFRaEMqJl5NC1OjElkwtNzAlbkr2K/WJXkBkH5VP0zx4Hgsd2TZYdOEfvp2Dxia+Q==", + "version": "29.4.1", + "resolved": "https://registry.npmjs.org/@types/jest/-/jest-29.4.1.tgz", + "integrity": "sha512-zDQSWXG+ZkEvs2zFFMszePhx4euKz+Yt3Gg1P+RHjfJBinTTr6L2DEyovO4V/WrKXuF0Dgn56GWGZPDa6TW9eQ==", + "dev": true, "requires": { "expect": "^29.0.0", "pretty-format": "^29.0.0" @@ -2973,14 +3018,16 @@ "ansi-styles": { "version": "5.2.0", "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-5.2.0.tgz", - "integrity": "sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==" + "integrity": "sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==", + "dev": true }, "pretty-format": { - "version": "29.0.2", - "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-29.0.2.tgz", - "integrity": "sha512-wp3CdtUa3cSJVFn3Miu5a1+pxc1iPIQTenOAn+x5erXeN1+ryTcLesV5pbK/rlW5EKwp27x38MoYfNGaNXDDhg==", + "version": "29.5.0", + "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-29.5.0.tgz", + "integrity": "sha512-V2mGkI31qdttvTFX7Mt4efOqHXqJWMu4/r66Xh3Z3BwZaPfPJgp6/gbwoujRpPUtfEF6AUUWx3Jim3GCw5g/Qw==", + "dev": true, "requires": { - "@jest/schemas": "^29.0.0", + "@jest/schemas": "^29.4.3", "ansi-styles": "^5.0.0", "react-is": "^18.0.0" } @@ -2988,7 +3035,8 @@ "react-is": { "version": "18.2.0", "resolved": "https://registry.npmjs.org/react-is/-/react-is-18.2.0.tgz", - "integrity": "sha512-xWGDIW6x921xtzPkhiULtthJHoJvBbF3q26fzloPCK0hsvxtPVelvftw3zjbHWSkR2km9Z+4uxbDDK/6Zw9B8w==" + "integrity": "sha512-xWGDIW6x921xtzPkhiULtthJHoJvBbF3q26fzloPCK0hsvxtPVelvftw3zjbHWSkR2km9Z+4uxbDDK/6Zw9B8w==", + "dev": true } } }, @@ -3052,14 +3100,6 @@ "csstype": "^3.0.2" } }, - "@types/react-dom": { - "version": "17.0.17", - "resolved": "https://registry.npmjs.org/@types/react-dom/-/react-dom-17.0.17.tgz", - "integrity": "sha512-VjnqEmqGnasQKV0CWLevqMTXBYG9GbwuE6x3VetERLh0cq2LTptFE73MrQi2S7GkKXCf2GgwItB/melLnxfnsg==", - "requires": { - "@types/react": "^17" - } - }, "@types/react-transition-group": { "version": "4.4.5", "resolved": "https://registry.npmjs.org/@types/react-transition-group/-/react-transition-group-4.4.5.tgz", @@ -3120,6 +3160,7 @@ "version": "5.14.5", "resolved": "https://registry.npmjs.org/@types/testing-library__jest-dom/-/testing-library__jest-dom-5.14.5.tgz", "integrity": "sha512-SBwbxYoyPIvxHbeHxTZX2Pe/74F/tX2/D3mMvzabdeJ25bBojfW0TyB8BHrbq/9zaaKICJZjLP+8r6AeZMFCuQ==", + "dev": true, "requires": { "@types/jest": "*" } @@ -3589,13 +3630,13 @@ } }, "ajv": { - "version": "6.12.6", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", - "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", + "version": "8.12.0", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.12.0.tgz", + "integrity": "sha512-sRu1kpcO9yLtYxBKvqfTeh9KzZEwO3STyX1HT+4CaDzC6HpTGYhIhPIzj9XuKU7KYDwnaeh5hcOwjy1QuJzBPA==", "requires": { "fast-deep-equal": "^3.1.1", - "fast-json-stable-stringify": "^2.0.0", - "json-schema-traverse": "^0.4.1", + "json-schema-traverse": "^1.0.0", + "require-from-string": "^2.0.2", "uri-js": "^4.2.2" } }, @@ -3670,18 +3711,11 @@ "resolved": "https://registry.npmjs.org/arg/-/arg-5.0.2.tgz", "integrity": "sha512-PYjyFOLKQ9y57JvQ6QLo8dAgNqswh8M1RMJYdQduT6xbWSgK36P/Z/v+p888pM69jMMfS8Xd8F6I1kQ/I9HUGg==" }, - "argparse": { - "version": "1.0.10", - "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz", - "integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==", - "requires": { - "sprintf-js": "~1.0.2" - } - }, "aria-query": { "version": "5.0.2", "resolved": "https://registry.npmjs.org/aria-query/-/aria-query-5.0.2.tgz", - "integrity": "sha512-eigU3vhqSO+Z8BKDnVLN/ompjhf3pYzecKXz8+whRy+9gZu8n1TCGfwzQUUPnqdHl9ax1Hr9031orZ+UOEYr7Q==" + "integrity": "sha512-eigU3vhqSO+Z8BKDnVLN/ompjhf3pYzecKXz8+whRy+9gZu8n1TCGfwzQUUPnqdHl9ax1Hr9031orZ+UOEYr7Q==", + "dev": true }, "array-flatten": { "version": "2.1.2", @@ -3844,6 +3878,11 @@ "schema-utils": "^2.6.5" }, "dependencies": { + "json-schema-traverse": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", + "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==" + }, "make-dir": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-3.1.0.tgz", @@ -3860,6 +3899,19 @@ "@types/json-schema": "^7.0.5", "ajv": "^6.12.4", "ajv-keywords": "^3.5.2" + }, + "dependencies": { + "ajv": { + "version": "6.12.6", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", + "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", + "requires": { + "fast-deep-equal": "^3.1.1", + "fast-json-stable-stringify": "^2.0.0", + "json-schema-traverse": "^0.4.1", + "uri-js": "^4.2.2" + } + } } }, "semver": { @@ -4245,6 +4297,7 @@ "version": "3.0.0", "resolved": "https://registry.npmjs.org/chalk/-/chalk-3.0.0.tgz", "integrity": "sha512-4D3B6Wf41KOYRFdszmDqMCGq5VV/uMAB273JILmO+3jAlh8X4qDtdtgCR3fxtbLEMzSx22QdhnDcJvu2u1fVwg==", + "dev": true, "requires": { "ansi-styles": "^4.1.0", "supports-color": "^7.1.0" @@ -4586,6 +4639,15 @@ "yaml": "^1.10.0" } }, + "cross-fetch": { + "version": "3.1.5", + "resolved": "https://registry.npmjs.org/cross-fetch/-/cross-fetch-3.1.5.tgz", + "integrity": "sha512-lvb1SBsI0Z7GDwmuid+mU3kWVBwTVUbe7S0H52yaaAdQOXq2YktTCZdlAcNKFzE6QtRz0snpw9bNiPeOIkkQvw==", + "dev": true, + "requires": { + "node-fetch": "2.6.7" + } + }, "cross-spawn": { "version": "7.0.3", "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz", @@ -4756,7 +4818,8 @@ "css.escape": { "version": "1.5.1", "resolved": "https://registry.npmjs.org/css.escape/-/css.escape-1.5.1.tgz", - "integrity": "sha512-YUifsXXuknHlUsmlgyY0PKzgPOr7/FjCePfHNt0jxm83wHZi44VDMQ7/fGNkjY3/jV1MC+1CmZbaHzugyeRtpg==" + "integrity": "sha512-YUifsXXuknHlUsmlgyY0PKzgPOr7/FjCePfHNt0jxm83wHZi44VDMQ7/fGNkjY3/jV1MC+1CmZbaHzugyeRtpg==", + "dev": true }, "cssdb": { "version": "7.0.1", @@ -5046,9 +5109,10 @@ "integrity": "sha512-gxtyfqMg7GKyhQmb056K7M3xszy/myH8w+B4RT+QXBQsvAOdc3XymqDDPHx1BgPgsdAA5SIifona89YtRATDzw==" }, "diff-sequences": { - "version": "29.3.1", - "resolved": "https://registry.npmjs.org/diff-sequences/-/diff-sequences-29.3.1.tgz", - "integrity": "sha512-hlM3QR272NXCi4pq+N4Kok4kOp6EsgOM3ZSpJI7Da3UAs+Ttsi8MRmB6trM/lhyzUxGfOgnpkHtgqm5Q/CTcfQ==" + "version": "29.4.3", + "resolved": "https://registry.npmjs.org/diff-sequences/-/diff-sequences-29.4.3.tgz", + "integrity": "sha512-ofrBgwpPhCD85kMKtE9RYFFq6OC1A89oW2vvgWZNCwxrUpRUILopY7lsYyMDSjc8g6U6aiO0Qubg6r4Wgt5ZnA==", + "dev": true }, "dir-glob": { "version": "3.0.1", @@ -5087,7 +5151,8 @@ "dom-accessibility-api": { "version": "0.5.14", "resolved": "https://registry.npmjs.org/dom-accessibility-api/-/dom-accessibility-api-0.5.14.tgz", - "integrity": "sha512-NMt+m9zFMPZe0JcY9gN224Qvk6qLIdqex29clBvc/y75ZBX9YA9wNK3frsYvu2DI1xcCIwxwnX+TlsJ2DSOADg==" + "integrity": "sha512-NMt+m9zFMPZe0JcY9gN224Qvk6qLIdqex29clBvc/y75ZBX9YA9wNK3frsYvu2DI1xcCIwxwnX+TlsJ2DSOADg==", + "dev": true }, "dom-converter": { "version": "0.2.0", @@ -5423,6 +5488,17 @@ "text-table": "^0.2.0" }, "dependencies": { + "ajv": { + "version": "6.12.6", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", + "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", + "requires": { + "fast-deep-equal": "^3.1.1", + "fast-json-stable-stringify": "^2.0.0", + "json-schema-traverse": "^0.4.1", + "uri-js": "^4.2.2" + } + }, "argparse": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", @@ -5461,6 +5537,11 @@ "argparse": "^2.0.1" } }, + "json-schema-traverse": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", + "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==" + }, "ms": { "version": "2.1.2", "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", @@ -5845,15 +5926,16 @@ "integrity": "sha512-Zk/eNKV2zbjpKzrsQ+n1G6poVbErQxJ0LBOJXaKZ1EViLzH+hrLu9cdXI4zw9dBQJslwBEpbQ2P1oS7nDxs6jQ==" }, "expect": { - "version": "29.4.1", - "resolved": "https://registry.npmjs.org/expect/-/expect-29.4.1.tgz", - "integrity": "sha512-OKrGESHOaMxK3b6zxIq9SOW8kEXztKff/Dvg88j4xIJxur1hspEbedVkR3GpHe5LO+WB2Qw7OWN0RMTdp6as5A==", + "version": "29.5.0", + "resolved": "https://registry.npmjs.org/expect/-/expect-29.5.0.tgz", + "integrity": "sha512-yM7xqUrCO2JdpFo4XpM82t+PJBFybdqoQuJLDGeDX2ij8NZzqRHyu3Hp188/JX7SWqud+7t4MUdvcgGBICMHZg==", + "dev": true, "requires": { - "@jest/expect-utils": "^29.4.1", - "jest-get-type": "^29.2.0", - "jest-matcher-utils": "^29.4.1", - "jest-message-util": "^29.4.1", - "jest-util": "^29.4.1" + "@jest/expect-utils": "^29.5.0", + "jest-get-type": "^29.4.3", + "jest-matcher-utils": "^29.5.0", + "jest-message-util": "^29.5.0", + "jest-util": "^29.5.0" } }, "express": { @@ -6175,6 +6257,11 @@ "universalify": "^2.0.0" } }, + "json-schema-traverse": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", + "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==" + }, "schema-utils": { "version": "2.7.0", "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-2.7.0.tgz", @@ -6183,6 +6270,19 @@ "@types/json-schema": "^7.0.4", "ajv": "^6.12.2", "ajv-keywords": "^3.4.1" + }, + "dependencies": { + "ajv": { + "version": "6.12.6", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", + "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", + "requires": { + "fast-deep-equal": "^3.1.1", + "fast-json-stable-stringify": "^2.0.0", + "json-schema-traverse": "^0.4.1", + "uri-js": "^4.2.2" + } + } } }, "semver": { @@ -6748,7 +6848,8 @@ "indent-string": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/indent-string/-/indent-string-4.0.0.tgz", - "integrity": "sha512-EdDDZu4A2OyIK7Lr/2zG+w5jmbuk1DVBnEwREQvBzspBJkCEbRa8GxU1lghYcaGJCnRWibjDXlq779X1/y5xwg==" + "integrity": "sha512-EdDDZu4A2OyIK7Lr/2zG+w5jmbuk1DVBnEwREQvBzspBJkCEbRa8GxU1lghYcaGJCnRWibjDXlq779X1/y5xwg==", + "dev": true }, "inflight": { "version": "1.0.6", @@ -7427,31 +7528,34 @@ } }, "jest-diff": { - "version": "29.4.1", - "resolved": "https://registry.npmjs.org/jest-diff/-/jest-diff-29.4.1.tgz", - "integrity": "sha512-uazdl2g331iY56CEyfbNA0Ut7Mn2ulAG5vUaEHXycf1L6IPyuImIxSz4F0VYBKi7LYIuxOwTZzK3wh5jHzASMw==", + "version": "29.5.0", + "resolved": "https://registry.npmjs.org/jest-diff/-/jest-diff-29.5.0.tgz", + "integrity": "sha512-LtxijLLZBduXnHSniy0WMdaHjmQnt3g5sa16W4p0HqukYTTsyTW3GD1q41TyGl5YFXj/5B2U6dlh5FM1LIMgxw==", + "dev": true, "requires": { "chalk": "^4.0.0", - "diff-sequences": "^29.3.1", - "jest-get-type": "^29.2.0", - "pretty-format": "^29.4.1" + "diff-sequences": "^29.4.3", + "jest-get-type": "^29.4.3", + "pretty-format": "^29.5.0" }, "dependencies": { "chalk": { "version": "4.1.2", "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dev": true, "requires": { "ansi-styles": "^4.1.0", "supports-color": "^7.1.0" } }, "pretty-format": { - "version": "29.4.1", - "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-29.4.1.tgz", - "integrity": "sha512-dt/Z761JUVsrIKaY215o1xQJBGlSmTx/h4cSqXqjHLnU1+Kt+mavVE7UgqJJO5ukx5HjSswHfmXz4LjS2oIJfg==", + "version": "29.5.0", + "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-29.5.0.tgz", + "integrity": "sha512-V2mGkI31qdttvTFX7Mt4efOqHXqJWMu4/r66Xh3Z3BwZaPfPJgp6/gbwoujRpPUtfEF6AUUWx3Jim3GCw5g/Qw==", + "dev": true, "requires": { - "@jest/schemas": "^29.4.0", + "@jest/schemas": "^29.4.3", "ansi-styles": "^5.0.0", "react-is": "^18.0.0" }, @@ -7459,14 +7563,16 @@ "ansi-styles": { "version": "5.2.0", "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-5.2.0.tgz", - "integrity": "sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==" + "integrity": "sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==", + "dev": true } } }, "react-is": { "version": "18.2.0", "resolved": "https://registry.npmjs.org/react-is/-/react-is-18.2.0.tgz", - "integrity": "sha512-xWGDIW6x921xtzPkhiULtthJHoJvBbF3q26fzloPCK0hsvxtPVelvftw3zjbHWSkR2km9Z+4uxbDDK/6Zw9B8w==" + "integrity": "sha512-xWGDIW6x921xtzPkhiULtthJHoJvBbF3q26fzloPCK0hsvxtPVelvftw3zjbHWSkR2km9Z+4uxbDDK/6Zw9B8w==", + "dev": true } } }, @@ -7654,10 +7760,21 @@ } } }, + "jest-fetch-mock": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/jest-fetch-mock/-/jest-fetch-mock-3.0.3.tgz", + "integrity": "sha512-Ux1nWprtLrdrH4XwE7O7InRY6psIi3GOsqNESJgMJ+M5cv4A8Lh7SN9d2V2kKRZ8ebAfcd1LNyZguAOb6JiDqw==", + "dev": true, + "requires": { + "cross-fetch": "^3.0.4", + "promise-polyfill": "^8.1.3" + } + }, "jest-get-type": { - "version": "29.2.0", - "resolved": "https://registry.npmjs.org/jest-get-type/-/jest-get-type-29.2.0.tgz", - "integrity": "sha512-uXNJlg8hKFEnDgFsrCjznB+sTxdkuqiCL6zMgA75qEbAJjJYTs9XPrvDctrEig2GDow22T/LvHgO57iJhXB/UA==" + "version": "29.4.3", + "resolved": "https://registry.npmjs.org/jest-get-type/-/jest-get-type-29.4.3.tgz", + "integrity": "sha512-J5Xez4nRRMjk8emnTpWrlkyb9pfRQQanDrvWHhsR1+VUfbwxi30eVcZFlcdGInRibU4G5LwHXpI7IRHU0CY+gg==", + "dev": true }, "jest-haste-map": { "version": "27.5.1", @@ -7867,31 +7984,34 @@ } }, "jest-matcher-utils": { - "version": "29.4.1", - "resolved": "https://registry.npmjs.org/jest-matcher-utils/-/jest-matcher-utils-29.4.1.tgz", - "integrity": "sha512-k5h0u8V4nAEy6lSACepxL/rw78FLDkBnXhZVgFneVpnJONhb2DhZj/Gv4eNe+1XqQ5IhgUcqj745UwH0HJmMnA==", + "version": "29.5.0", + "resolved": "https://registry.npmjs.org/jest-matcher-utils/-/jest-matcher-utils-29.5.0.tgz", + "integrity": "sha512-lecRtgm/rjIK0CQ7LPQwzCs2VwW6WAahA55YBuI+xqmhm7LAaxokSB8C97yJeYyT+HvQkH741StzpU41wohhWw==", + "dev": true, "requires": { "chalk": "^4.0.0", - "jest-diff": "^29.4.1", - "jest-get-type": "^29.2.0", - "pretty-format": "^29.4.1" + "jest-diff": "^29.5.0", + "jest-get-type": "^29.4.3", + "pretty-format": "^29.5.0" }, "dependencies": { "chalk": { "version": "4.1.2", "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dev": true, "requires": { "ansi-styles": "^4.1.0", "supports-color": "^7.1.0" } }, "pretty-format": { - "version": "29.4.1", - "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-29.4.1.tgz", - "integrity": "sha512-dt/Z761JUVsrIKaY215o1xQJBGlSmTx/h4cSqXqjHLnU1+Kt+mavVE7UgqJJO5ukx5HjSswHfmXz4LjS2oIJfg==", + "version": "29.5.0", + "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-29.5.0.tgz", + "integrity": "sha512-V2mGkI31qdttvTFX7Mt4efOqHXqJWMu4/r66Xh3Z3BwZaPfPJgp6/gbwoujRpPUtfEF6AUUWx3Jim3GCw5g/Qw==", + "dev": true, "requires": { - "@jest/schemas": "^29.4.0", + "@jest/schemas": "^29.4.3", "ansi-styles": "^5.0.0", "react-is": "^18.0.0" }, @@ -7899,29 +8019,32 @@ "ansi-styles": { "version": "5.2.0", "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-5.2.0.tgz", - "integrity": "sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==" + "integrity": "sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==", + "dev": true } } }, "react-is": { "version": "18.2.0", "resolved": "https://registry.npmjs.org/react-is/-/react-is-18.2.0.tgz", - "integrity": "sha512-xWGDIW6x921xtzPkhiULtthJHoJvBbF3q26fzloPCK0hsvxtPVelvftw3zjbHWSkR2km9Z+4uxbDDK/6Zw9B8w==" + "integrity": "sha512-xWGDIW6x921xtzPkhiULtthJHoJvBbF3q26fzloPCK0hsvxtPVelvftw3zjbHWSkR2km9Z+4uxbDDK/6Zw9B8w==", + "dev": true } } }, "jest-message-util": { - "version": "29.4.1", - "resolved": "https://registry.npmjs.org/jest-message-util/-/jest-message-util-29.4.1.tgz", - "integrity": "sha512-H4/I0cXUaLeCw6FM+i4AwCnOwHRgitdaUFOdm49022YD5nfyr8C/DrbXOBEyJaj+w/y0gGJ57klssOaUiLLQGQ==", + "version": "29.5.0", + "resolved": "https://registry.npmjs.org/jest-message-util/-/jest-message-util-29.5.0.tgz", + "integrity": "sha512-Kijeg9Dag6CKtIDA7O21zNTACqD5MD/8HfIV8pdD94vFyFuer52SigdC3IQMhab3vACxXMiFk+yMHNdbqtyTGA==", + "dev": true, "requires": { "@babel/code-frame": "^7.12.13", - "@jest/types": "^29.4.1", + "@jest/types": "^29.5.0", "@types/stack-utils": "^2.0.0", "chalk": "^4.0.0", "graceful-fs": "^4.2.9", "micromatch": "^4.0.4", - "pretty-format": "^29.4.1", + "pretty-format": "^29.5.0", "slash": "^3.0.0", "stack-utils": "^2.0.3" }, @@ -7930,17 +8053,19 @@ "version": "4.1.2", "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dev": true, "requires": { "ansi-styles": "^4.1.0", "supports-color": "^7.1.0" } }, "pretty-format": { - "version": "29.4.1", - "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-29.4.1.tgz", - "integrity": "sha512-dt/Z761JUVsrIKaY215o1xQJBGlSmTx/h4cSqXqjHLnU1+Kt+mavVE7UgqJJO5ukx5HjSswHfmXz4LjS2oIJfg==", + "version": "29.5.0", + "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-29.5.0.tgz", + "integrity": "sha512-V2mGkI31qdttvTFX7Mt4efOqHXqJWMu4/r66Xh3Z3BwZaPfPJgp6/gbwoujRpPUtfEF6AUUWx3Jim3GCw5g/Qw==", + "dev": true, "requires": { - "@jest/schemas": "^29.4.0", + "@jest/schemas": "^29.4.3", "ansi-styles": "^5.0.0", "react-is": "^18.0.0" }, @@ -7948,14 +8073,16 @@ "ansi-styles": { "version": "5.2.0", "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-5.2.0.tgz", - "integrity": "sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==" + "integrity": "sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==", + "dev": true } } }, "react-is": { "version": "18.2.0", "resolved": "https://registry.npmjs.org/react-is/-/react-is-18.2.0.tgz", - "integrity": "sha512-xWGDIW6x921xtzPkhiULtthJHoJvBbF3q26fzloPCK0hsvxtPVelvftw3zjbHWSkR2km9Z+4uxbDDK/6Zw9B8w==" + "integrity": "sha512-xWGDIW6x921xtzPkhiULtthJHoJvBbF3q26fzloPCK0hsvxtPVelvftw3zjbHWSkR2km9Z+4uxbDDK/6Zw9B8w==", + "dev": true } } }, @@ -8438,11 +8565,12 @@ } }, "jest-util": { - "version": "29.4.1", - "resolved": "https://registry.npmjs.org/jest-util/-/jest-util-29.4.1.tgz", - "integrity": "sha512-bQy9FPGxVutgpN4VRc0hk6w7Hx/m6L53QxpDreTZgJd9gfx/AV2MjyPde9tGyZRINAUrSv57p2inGBu2dRLmkQ==", + "version": "29.5.0", + "resolved": "https://registry.npmjs.org/jest-util/-/jest-util-29.5.0.tgz", + "integrity": "sha512-RYMgG/MTadOr5t8KdhejfvUU82MxsCu5MF6KuDUHl+NuwzUt+Sm6jJWxTJVrDR1j5M/gJVCPKQEpWXY+yIQ6lQ==", + "dev": true, "requires": { - "@jest/types": "^29.4.1", + "@jest/types": "^29.5.0", "@types/node": "*", "chalk": "^4.0.0", "ci-info": "^3.2.0", @@ -8454,6 +8582,7 @@ "version": "4.1.2", "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dev": true, "requires": { "ansi-styles": "^4.1.0", "supports-color": "^7.1.0" @@ -8814,15 +8943,6 @@ "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==" }, - "js-yaml": { - "version": "3.14.1", - "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.14.1.tgz", - "integrity": "sha512-okMH7OXXJ7YrN9Ok3/SXrnu4iX9yOk+25nqX4imS2npuvTYDmo/QEZoqwZkYaIDk3jVvBOTOIEgEhaLOynBS9g==", - "requires": { - "argparse": "^1.0.7", - "esprima": "^4.0.0" - } - }, "jsdom": { "version": "16.7.0", "resolved": "https://registry.npmjs.org/jsdom/-/jsdom-16.7.0.tgz", @@ -8873,9 +8993,9 @@ "integrity": "sha512-es94M3nTIfsEPisRafak+HDLfHXnKBhV3vU5eqPcS3flIWqcxJWgXHXiey3YrpaNsanY5ei1VoYEbOzijuq9BA==" }, "json-schema-traverse": { - "version": "0.4.1", - "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", - "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==" + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz", + "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==" }, "json-stable-stringify-without-jsonify": { "version": "1.0.1", @@ -9057,9 +9177,10 @@ } }, "lz-string": { - "version": "1.4.4", - "resolved": "https://registry.npmjs.org/lz-string/-/lz-string-1.4.4.tgz", - "integrity": "sha512-0ckx7ZHRPqb0oUm8zNr+90mtf9DQB60H1wMCjBtfi62Kl3a7JbHob6gA2bC+xRvZoOL+1hzUK8jeuEIQE8svEQ==" + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/lz-string/-/lz-string-1.5.0.tgz", + "integrity": "sha512-h5bgJWpxJNswbU7qCrV0tIKQCaS3blPDrqKWx+QxzuzL1zGUzij9XCWLrSLsJPu5t+eWA/ycetzYAO5IOMcWAQ==", + "dev": true }, "magic-string": { "version": "0.25.9", @@ -9160,7 +9281,8 @@ "min-indent": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/min-indent/-/min-indent-1.0.1.tgz", - "integrity": "sha512-I9jwMn07Sy/IwOj3zVkVik2JTvgpaykDZEigL6Rx6N9LbMywwUSMtxET+7lVoDLLd3O3IXwJwvuuns8UB/HeAg==" + "integrity": "sha512-I9jwMn07Sy/IwOj3zVkVik2JTvgpaykDZEigL6Rx6N9LbMywwUSMtxET+7lVoDLLd3O3IXwJwvuuns8UB/HeAg==", + "dev": true }, "mini-css-extract-plugin": { "version": "2.6.1", @@ -9293,6 +9415,39 @@ "tslib": "^2.0.3" } }, + "node-fetch": { + "version": "2.6.7", + "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.6.7.tgz", + "integrity": "sha512-ZjMPFEfVx5j+y2yF35Kzx5sF7kDzxuDj6ziH4FFbOp87zKDZNx8yExJIb05OGF4Nlt9IHFIMBkRl41VdvcNdbQ==", + "dev": true, + "requires": { + "whatwg-url": "^5.0.0" + }, + "dependencies": { + "tr46": { + "version": "0.0.3", + "resolved": "https://registry.npmjs.org/tr46/-/tr46-0.0.3.tgz", + "integrity": "sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==", + "dev": true + }, + "webidl-conversions": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-3.0.1.tgz", + "integrity": "sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ==", + "dev": true + }, + "whatwg-url": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-5.0.0.tgz", + "integrity": "sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw==", + "dev": true, + "requires": { + "tr46": "~0.0.3", + "webidl-conversions": "^3.0.0" + } + } + } + }, "node-forge": { "version": "1.3.1", "resolved": "https://registry.npmjs.org/node-forge/-/node-forge-1.3.1.tgz", @@ -10409,6 +10564,12 @@ "asap": "~2.0.6" } }, + "promise-polyfill": { + "version": "8.3.0", + "resolved": "https://registry.npmjs.org/promise-polyfill/-/promise-polyfill-8.3.0.tgz", + "integrity": "sha512-H5oELycFml5yto/atYqmjyigJoAo3+OXwolYiH7OfQuYlAqhxNvTfiNMbV9hsC6Yp83yE5r2KTVmtrG6R9i6Pg==", + "dev": true + }, "prompts": { "version": "2.4.2", "resolved": "https://registry.npmjs.org/prompts/-/prompts-2.4.2.tgz", @@ -10912,6 +11073,7 @@ "version": "3.0.0", "resolved": "https://registry.npmjs.org/redent/-/redent-3.0.0.tgz", "integrity": "sha512-6tDA8g98We0zd0GvVeMT9arEOnTw9qM03L9cJXaCjrip1OO764RDBLBfrB4cwzNGDj5OA5ioymC9GkizgWJDUg==", + "dev": true, "requires": { "indent-string": "^4.0.0", "strip-indent": "^3.0.0" @@ -11219,6 +11381,24 @@ "@types/json-schema": "^7.0.8", "ajv": "^6.12.5", "ajv-keywords": "^3.5.2" + }, + "dependencies": { + "ajv": { + "version": "6.12.6", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", + "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", + "requires": { + "fast-deep-equal": "^3.1.1", + "fast-json-stable-stringify": "^2.0.0", + "json-schema-traverse": "^0.4.1", + "uri-js": "^4.2.2" + } + }, + "json-schema-traverse": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", + "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==" + } } }, "select-hose": { @@ -11677,6 +11857,7 @@ "version": "3.0.0", "resolved": "https://registry.npmjs.org/strip-indent/-/strip-indent-3.0.0.tgz", "integrity": "sha512-laJTa3Jb+VQpaC6DseHhF7dXVqHTfJPCRDaEbid/drOhgitgYku/letMUqOXFoWV0zIIUbjpdH2t+tYj4bQMRQ==", + "dev": true, "requires": { "min-indent": "^1.0.0" } @@ -11787,6 +11968,14 @@ "color-convert": "^1.9.0" } }, + "argparse": { + "version": "1.0.10", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz", + "integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==", + "requires": { + "sprintf-js": "~1.0.2" + } + }, "chalk": { "version": "2.4.2", "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", @@ -11861,6 +12050,15 @@ "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", "integrity": "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==" }, + "js-yaml": { + "version": "3.14.1", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.14.1.tgz", + "integrity": "sha512-okMH7OXXJ7YrN9Ok3/SXrnu4iX9yOk+25nqX4imS2npuvTYDmo/QEZoqwZkYaIDk3jVvBOTOIEgEhaLOynBS9g==", + "requires": { + "argparse": "^1.0.7", + "esprima": "^4.0.0" + } + }, "nth-check": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/nth-check/-/nth-check-1.0.2.tgz", diff --git a/skyline-vscode/react-ui/package.json b/skyline-vscode/react-ui/package.json index ca60b0e..705f570 100644 --- a/skyline-vscode/react-ui/package.json +++ b/skyline-vscode/react-ui/package.json @@ -7,9 +7,7 @@ "@fortawesome/fontawesome-svg-core": "^1.2.36", "@fortawesome/free-solid-svg-icons": "^5.15.4", "@fortawesome/react-fontawesome": "^0.1.17", - "@testing-library/jest-dom": "^5.16.1", - "@testing-library/react": "^12.1.2", - "@testing-library/user-event": "^13.5.0", + "ajv": "^8.12.0", "bootstrap": "^5.1.3", "less": "^4.1.2", "react": "^17.0.2", @@ -46,7 +44,11 @@ ] }, "devDependencies": { + "@testing-library/jest-dom": "^5.16.5", + "@testing-library/react": "^12.1.2", + "@testing-library/user-event": "^13.5.0", "jest": "^27.5.1", + "jest-fetch-mock": "^3.0.3", "webpack-bundle-analyzer": "^4.8.0" } } diff --git a/skyline-vscode/react-ui/src/App.js b/skyline-vscode/react-ui/src/App.js index b86143a..a4ebe08 100644 --- a/skyline-vscode/react-ui/src/App.js +++ b/skyline-vscode/react-ui/src/App.js @@ -94,7 +94,6 @@ function App() { ), fine: computePercentage(fine, state.breakdown.iteration_run_time_ms), }); - ReactTooltip.rebuild(); } }; @@ -103,9 +102,12 @@ function App() { if (event.data["message_type"] === "connection") { setConnectionStatus(event.data["status"]); } else if (event.data["message_type"] === "analysis") { - if(event.data.habitat[0][0]==="unavailable" && event.data.habitat[0][1]===-1.0){ - event.data.habitat = profiling_data.habitat - event.data.habitat.push(["demo",1]) + if ( + event.data.habitat[0][0] === "unavailable" && + event.data.habitat[0][1] === -1.0 + ) { + event.data.habitat = profiling_data.habitat; + event.data.habitat.push(["demo", 1]); } processAnalysisState(event.data); } else if (event.data["message_type"] === "text_change") { @@ -225,6 +227,7 @@ function App() { diff --git a/skyline-vscode/react-ui/src/components/Modals.js b/skyline-vscode/react-ui/src/components/Modals.js new file mode 100644 index 0000000..c26439f --- /dev/null +++ b/skyline-vscode/react-ui/src/components/Modals.js @@ -0,0 +1,48 @@ +import React, { useState } from "react"; +import Button from "react-bootstrap/Button"; +import Modal from "react-bootstrap/Modal"; + +export const ProviderPanelModal = ({ errors }) => { + const [show, setShow] = useState(false); + + const handleClose = () => setShow(false); + const handleShow = () => setShow(true); + + return ( + <> +
+ There were some errors fetching data from the urls. Please see the + List of Errors +
+ + + + List of Errors + + + {errors?.map((err, idx) => { + return ( +
+
{`Error message : ${err.msg}`}
+ {err.code &&

{`Error code: ${err.code}`}

} + {err.invalidFields?.map((incorrectField, id) => { + return ( +
+
{incorrectField.field}
+

{incorrectField.err}

+
+ ); + })} +
+ ); + })} +
+ + + +
+ + ); +}; diff --git a/skyline-vscode/react-ui/src/components/ScatterGraph.js b/skyline-vscode/react-ui/src/components/ScatterGraph.js index 2570a35..136cf43 100644 --- a/skyline-vscode/react-ui/src/components/ScatterGraph.js +++ b/skyline-vscode/react-ui/src/components/ScatterGraph.js @@ -14,7 +14,7 @@ import { } from "recharts"; import { calculate_training_time, currencyFormat } from "../utils/utils"; -import { gpuPropertyList } from "../data/providers"; +import { gpuPropertyList } from "../data/properties"; export const ProviderScatterGraph = ({ data, diff --git a/skyline-vscode/react-ui/src/data/properties.js b/skyline-vscode/react-ui/src/data/properties.js new file mode 100644 index 0000000..5e9d5fa --- /dev/null +++ b/skyline-vscode/react-ui/src/data/properties.js @@ -0,0 +1,25 @@ +export const gpuPropertyList = [ + { name: "p100", vmem: 16, type: "server" }, + { name: "p4000", vmem: 8, type: "server" }, + { name: "rtx2070", vmem: 8, type: "consumer" }, + { name: "rtx2080ti", vmem: 11, type: "consumer" }, + { name: "t4", vmem: 16, type: "server" }, + { name: "v100", vmem: 16, type: "server" }, + { name: "a100", vmem: 40, type: "server" }, + { name: "rtx3090", vmem: 24, type: "consumer" }, + { name: "a40", vmem: 48, type: "server" }, + { name: "a4000", vmem: 16, type: "server" }, + { name: "rtx4000", vmem: 8, type: "consumer" }, +]; + +/** + * COLOR DATA FOR DEPLOYMENT SCATTER GRAPH + */ +export const deploymentScatterGraphColorSize = { + HIGHLIGHTCOLOR: "#9b59b6", + NORMALSIZE: 200, + HIGHLIGHTSIZE: 280, +}; + +export const CENTML_CLOUD_PROVIDERS_URL = "https://deepview-explorer-public.s3.amazonaws.com/vscode-cloud-providers/providers.json"; + diff --git a/skyline-vscode/react-ui/src/data/providers.js b/skyline-vscode/react-ui/src/data/providers.js deleted file mode 100644 index 5ec8a13..0000000 --- a/skyline-vscode/react-ui/src/data/providers.js +++ /dev/null @@ -1,283 +0,0 @@ -/** - * GPU Instances - * costs are 1 GPU * hr - * For Google we need to add the cost for the instance itself, a100 run on specific A2 Series - * while t4, p4, v100, and p100 run on General purposes N1 Series - * - * References: - * FOR AWS: - * https://aws.amazon.com/ec2/instance-types/ - * https://aws.amazon.com/ec2/pricing/on-demand/ - * - * FOR GOOGLE: - * https://cloud.google.com/compute/gpus-pricing - * https://cloud.google.com/products/calculator/ - * - * FOR AZURE: - * https://learn.microsoft.com/en-us/azure/virtual-machines/sizes-gpu - * https://azure.microsoft.com/en-ca/pricing/details/virtual-machines/windows/#pricing - * - */ -export const cloudInstances = [ - { - id: 0, - provider: "google", - ngpus: 1, - instance: "a2-highgpu-1g", - gpu: "a100", - cost: 2.93 + 0.74, - }, - { - id: 1, - provider: "google", - ngpus: 1, - instance: "n1-standard-1", - gpu: "t4", - cost: 0.35 + 0.05, - }, - { - id: 2, - provider: "google", - ngpus: 1, - instance: "n1-standard-1", - gpu: "p4", - cost: 0.6 + 0.05, - }, - { - id: 3, - provider: "google", - ngpus: 1, - instance: "n1-standard-1", - gpu: "v100", - cost: 2.48 + 0.05, - }, - { - id: 4, - provider: "google", - ngpus: 1, - instance: "n1-standard-1", - gpu: "p100", - cost: 1.46 + 0.05, - }, - { - id: 6, - provider: "aws", - ngpus: 1, - instance: "p3.2xlarge", - gpu: "v100", - cost: 3.06, - }, - { - id: 8, - provider: "aws", - ngpus: 1, - instance: "g4dn.xlarge", - gpu: "t4", - cost: 0.526, - }, - { - id: 10, - provider: "azure", - ngpus: 1, - instance: "NC6s v2", - gpu: "p100", - cost: 2.07, - }, - { - id: 11, - provider: "azure", - ngpus: 1, - instance: "NC6s v3", - gpu: "v100", - cost: 3.06, - }, - { - id: 12, - provider: "azure", - ngpus: 1, - instance: "NC4as T4 v3", - gpu: "t4", - cost: 0.526, - }, - - // { - // id: 13, - // provider: "centml", - // ngpus: 1, - // instance: "CentML", - // gpu: "rtx2080ti", - // cost: 0.2, - // }, - - // 2/4 GPU instances - - { - id: 14, - provider: "google", - ngpus: 2, - instance: "a2-highgpu-2g", - gpu: "a100", - cost: 5.87 + 1.48, - }, - { - id: 15, - provider: "google", - ngpus: 2, - instance: "n1-standard-1", - gpu: "t4", - cost: 2 * 0.35 + 0.05, - }, - { - id: 16, - provider: "google", - ngpus: 2, - instance: "n1-standard-1", - gpu: "p4", - cost: 2 * 0.6 + 0.05, - }, - { - id: 17, - provider: "google", - ngpus: 2, - instance: "n1-standard-1", - gpu: "v100", - cost: 2 * 2.48 + 0.05, - }, - { - id: 18, - provider: "google", - ngpus: 2, - instance: "n1-standard-1", - gpu: "p100", - cost: 2 * 1.46 + 0.05, - }, - { - id: 20, - provider: "aws", - ngpus: 4, - instance: "g4dn.12xlarge", - gpu: "t4", - cost: 3.912, - }, - - { - id: 21, - provider: "google", - ngpus: 4, - instance: "a2-highgpu-4g", - gpu: "a100", - cost: 11.74 + 2.96, - }, - { - id: 22, - provider: "google", - ngpus: 4, - instance: "n1-standard-1", - gpu: "t4", - cost: 4 * 0.35 + 0.05, - }, - { - id: 23, - provider: "google", - ngpus: 4, - instance: "n1-standard-1", - gpu: "p4", - cost: 4 * 0.6 + 0.05, - }, - { - id: 24, - provider: "google", - ngpus: 4, - instance: "n1-standard-1", - gpu: "v100", - cost: 4 * 2.48 + 0.05, - }, - { - id: 25, - provider: "google", - ngpus: 4, - instance: "n1-standard-1", - gpu: "p100", - cost: 4 * 1.46 + 0.05, - }, - { - id: 27, - provider: "aws", - ngpus: 4, - instance: "p3.8xlarge", - gpu: "v100", - cost: 12.24, - }, - { - id: 28, - provider: "azure", - ngpus: 2, - instance: "NC12s v2", - gpu: "p100", - cost: 4.14, - }, - { - id: 29, - provider: "azure", - ngpus: 4, - instance: "NC24rs v2", - gpu: "p100", - cost: 9.1080, - }, - { - id: 30, - provider: "azure", - ngpus: 2, - instance: "NC12s v3", - gpu: "v100", - cost: 6.12, - }, - { - id: 31, - provider: "azure", - ngpus: 4, - instance: "NC24rs v3", - gpu: "v100", - cost: 13.46, - }, - { - id: 32, - provider: "azure", - ngpus: 4, - instance: "NC64as T4 v3", - gpu: "t4", - cost: 4.352, - }, -]; - -export const cloudProviders = { - google: { - name: "Google Cloud Platform", - logo: "resources/google.png", - color: "#ea4335", - }, - azure: { - name: "Microsoft Azure", - logo: "resources/azure.png", - color: "#008AD7", - }, - aws: { - name: "Amazon Web Services", - logo: "resources/aws.png", - color: "#ff9900", - }, -} - -export const gpuPropertyList = [ - { name: "p100", vmem: 16, type: "server" }, - { name: "p4000", vmem: 8, type: "server" }, - { name: "rtx2070", vmem: 8, type: "consumer" }, - { name: "rtx2080ti", vmem: 11, type: "consumer" }, - { name: "t4", vmem: 16, type: "server" }, - { name: "v100", vmem: 16, type: "server" }, - { name: "a100", vmem: 40, type: "server" }, - { name: "rtx3090", vmem: 24, type: "consumer" }, - { name: "a40", vmem: 48, type: "server" }, - { name: "a4000", vmem: 16, type: "server" }, - { name: "rtx4000", vmem: 8, type: "consumer" } -]; diff --git a/skyline-vscode/react-ui/src/schema/CloudProvidersSchema.js b/skyline-vscode/react-ui/src/schema/CloudProvidersSchema.js new file mode 100644 index 0000000..a7defa1 --- /dev/null +++ b/skyline-vscode/react-ui/src/schema/CloudProvidersSchema.js @@ -0,0 +1,62 @@ +export const cloudProviderSchema = { + title: "SCHEMA DEFINITION FOR CLOUD PROVIDERS", + description: + "list of cloud providers, (there needs to be at least 1 cloud provider)", + type: "array", + items: { + type: "object", + description: + "each cloud provider needs to include it's name, logo, color, and instance specifications", + required: ["name", "logo", "color", "instances"], + properties: { + name: { + type: "string", + description: "name of cloud provider. eg: gcp,aws,azure,coreweaver", + }, + logo: { type: "string", description: "url reference to the logo" }, + color: { type: "string", description: "color (CSS format)" }, + instances: { + type: "array", + description: + "list of instances (there needs to be at least 1 instance)", + items: { + type: "object", + required: ["name", "gpu", "ngpus", "cost"], + properties: { + name: { type: "string", description: "name of the instance" }, + gpu: { + enum: [ + "p100", + "p4000", + "rtx2070", + "rtx2080ti", + "t4", + "v100", + "a100", + "rtx3090", + "a40", + "a4000", + "rtx4000", + ], + description: + "only GPUs supported by our product DeepView.Predict", + }, + ngpus: { + type: "integer", + minimum: 1, + description: "number of gpus in the instance", + }, + cost: { + type: "number", + exclusiveMinimum: 0, + description: "cost per hour", + }, + }, + + }, + minItems: 1, + }, + }, + }, + minItems: 1, +}; diff --git a/skyline-vscode/react-ui/src/sections/DeploymentTab.js b/skyline-vscode/react-ui/src/sections/DeploymentTab.js index bd8eba7..f8aefd2 100644 --- a/skyline-vscode/react-ui/src/sections/DeploymentTab.js +++ b/skyline-vscode/react-ui/src/sections/DeploymentTab.js @@ -7,7 +7,7 @@ import Badge from "react-bootstrap/Badge"; import React from "react"; import { numberFormat } from "../utils/utils"; -const DeploymentTab = ({ numIterations, habitatData }) => { +const DeploymentTab = ({ numIterations, habitatData,additionalProviders }) => { return ( <> {habitatData.length === 0 ? ( @@ -32,6 +32,7 @@ const DeploymentTab = ({ numIterations, habitatData }) => { diff --git a/skyline-vscode/react-ui/src/sections/DeploymentTab.test.js b/skyline-vscode/react-ui/src/sections/DeploymentTab.test.js index f4e1c4b..87253bc 100644 --- a/skyline-vscode/react-ui/src/sections/DeploymentTab.test.js +++ b/skyline-vscode/react-ui/src/sections/DeploymentTab.test.js @@ -1,13 +1,14 @@ import { render, screen } from "@testing-library/react"; import DeploymentTab from "./DeploymentTab"; - +import { enableFetchMocks } from "jest-fetch-mock"; +enableFetchMocks(); const { ResizeObserver } = window; const numIterations = 10000; -const data = [ +const habitatData = [ ["source", 22.029312], ["P100", 14.069682], - ["P4000", 127.268085], // 27.268085 + ["P4000", 127.268085], ["RTX2070", 16.088268], ["RTX2080Ti", 11.826558], ["T4", 22.029312], @@ -19,13 +20,29 @@ const data = [ ["RTX4000", 20.2342], ]; +const correctData = [ + { + name:'google', + logo:'resources/google.png', + color:'#ea4335', + instances:[ + { + name:'a2-highgpu-1g', + gpu: 'a100', + ngpus:1, + cost:36.67 + } + ] + } +] + beforeEach(() => { delete window.ResizeObserver; window.ResizeObserver = jest.fn().mockImplementation(() => ({ observe: jest.fn(), unobserve: jest.fn(), disconnect: jest.fn(), - })); + })) }); afterEach(() => { @@ -59,17 +76,25 @@ test("Shows loading spinner when there is no habitat data", () => { expect(deploymentSection).toBeNull(); }); -test("Shows deployment target when there is habitat data", () => { +test("Shows deployment target when there is habitat data", async () => { + // ARRANGE - const { container } = render( - - ); + // Mock fetch response + jest.spyOn(global, 'fetch').mockResolvedValue({ + ok: true, + json: jest.fn().mockResolvedValue(correctData) + }) + + const { container } = render( + + ); // ASSERT - expect(screen.getByText(/deployment target/i)).toBeTruthy(); + expect(await screen.findByText(/deployment target/i)).toBeTruthy(); + const deploymentSection = screen.queryByText(/loading information/i); expect(deploymentSection).toBeNull(); expect( - container.querySelector(".recharts-responsive-container") // eslint-disable-line + container.querySelector(".recharts-responsive-container") // eslint-disable-line ).toBeTruthy(); }); diff --git a/skyline-vscode/react-ui/src/sections/Habitat.js b/skyline-vscode/react-ui/src/sections/Habitat.js index cda7d6f..bcac6b2 100644 --- a/skyline-vscode/react-ui/src/sections/Habitat.js +++ b/skyline-vscode/react-ui/src/sections/Habitat.js @@ -18,7 +18,7 @@ export default function Habitat({ habitatData }) {
-
The local GPU is not supported by Habitat
+
The local GPU is not supported by DeepView.Predict
@@ -37,8 +37,7 @@ export default function Habitat({ habitatData }) { - Loading Habitat - predictions. + Loading DeepView.Predict data diff --git a/skyline-vscode/react-ui/src/sections/Habitat.test.js b/skyline-vscode/react-ui/src/sections/Habitat.test.js index 9e3e53c..49fd53d 100644 --- a/skyline-vscode/react-ui/src/sections/Habitat.test.js +++ b/skyline-vscode/react-ui/src/sections/Habitat.test.js @@ -66,7 +66,7 @@ test("Shows loading spinner when there is no habitat data", () => { render(); // ASSERT - expect(screen.getByText(/loading habitat predictions/i)).toBeTruthy(); + expect(screen.getByText(/loading deepview.predict data/i)).toBeTruthy(); }); test("Shows graph when habitat data is present", () => { @@ -82,5 +82,5 @@ test("no habitat data received from backend", ()=>{ // ARRANGE const { container } = render(); // ASSERT - expect(screen.getByText(/the local gpu is not supported by habitat/i)).toBeTruthy(); + expect(screen.getByText(/the local gpu is not supported by deepview.predict/i)).toBeTruthy(); }) diff --git a/skyline-vscode/react-ui/src/sections/ProviderPanel.js b/skyline-vscode/react-ui/src/sections/ProviderPanel.js index 2d9260e..3bdc20f 100644 --- a/skyline-vscode/react-ui/src/sections/ProviderPanel.js +++ b/skyline-vscode/react-ui/src/sections/ProviderPanel.js @@ -13,55 +13,29 @@ import Col from "react-bootstrap/Col"; import Image from "react-bootstrap/Image"; import Table from "react-bootstrap/Table"; import Form from "react-bootstrap/Form"; -import Alert from 'react-bootstrap/Alert'; +import Spinner from "react-bootstrap/Spinner"; +import Alert from "react-bootstrap/Alert"; + +import { deploymentScatterGraphColorSize } from "../data/properties"; + +import { ProviderPanelModal } from "../components/Modals"; -import { - cloudInstances, - gpuPropertyList, - cloudProviders, -} from "../data/providers"; import { calculate_training_time, numberFormat, currencyFormat, } from "../utils/utils"; -const highlightColor = "#9b59b6"; -const normalSize = 200; -const highlightSize = 280; - -const populate_initial_data = (habitatData) => { - let filtered_instances = []; - if (habitatData) { - for (const instance of cloudInstances) { - const found_in_habitat = habitatData.find( - (item) => item[0].toLowerCase() === instance.gpu.toLowerCase() - ); - const found_in_gpuPropertyList = gpuPropertyList.find( - (item) => - item.name.toLocaleLowerCase() === instance.gpu.toLocaleLowerCase() - ); - if (found_in_habitat && found_in_gpuPropertyList) { - filtered_instances.push({ - id: instance.id, - x: found_in_habitat[1], // msec - y: (instance.cost / 3.6e6) * found_in_habitat[1], // cost per msec * habitatData = cost per 1 iteration - info: instance, - vmem: found_in_gpuPropertyList.vmem, - fill: cloudProviders[instance.provider].color, - z: normalSize, - }); - } - } - } - return filtered_instances; -}; +import { loadJsonFiles } from "../utils/parsers"; -const ProviderPanel = ({ numIterations, habitatData }) => { +const ProviderPanel = ({ numIterations, habitatData, additionalProviders }) => { const [providerPanelSettings, setProviderPanelSettings] = useState({ - plotData: populate_initial_data(habitatData), + plotData: null, + initialData: null, + cloudProviders: null, nearest: null, clicked: null, + fetchErrors: null, maxNumGpu: 0, provider: "all", gpu: "all", @@ -69,15 +43,17 @@ const ProviderPanel = ({ numIterations, habitatData }) => { estimated_time: 0, }); - const habitatIsDemo = habitatData.find(item=>item[0]==="demo" && item[1] === 1) + const habitatIsDemo = habitatData.find( + (item) => item[0] === "demo" && item[1] === 1 + ); const MAX_GPU = [1, 2, 4, 0]; // 0 is all - const initial_data = populate_initial_data(habitatData); + // const initial_data = populate_initial_data(habitatData); let gpuList = new Set(); - if (initial_data) { - initial_data.forEach((element) => { + if (providerPanelSettings.initialData) { + providerPanelSettings.initialData.forEach((element) => { gpuList.add(element.info.gpu.toLocaleLowerCase()); }); } @@ -98,7 +74,7 @@ const ProviderPanel = ({ numIterations, habitatData }) => { numGpuFilter = Number(value); } - const filtered_plot_data = initial_data + const filtered_plot_data = providerPanelSettings.initialData .filter((item) => { if (providerFilter !== "all") { return ( @@ -134,7 +110,9 @@ const ProviderPanel = ({ numIterations, habitatData }) => { const recalculateCost = (provider) => { if (provider == null) return; - const originalData = initial_data.find((item) => item.id === provider.id); + const originalData = providerPanelSettings.initialData.find( + (item) => item.id === provider.id + ); let totalHr = calculate_training_time(numIterations, originalData); // NEED YUBO FEEDBACK let totalCost = provider.info.cost * totalHr; @@ -154,14 +132,14 @@ const ProviderPanel = ({ numIterations, habitatData }) => { if (item.id === value.id) { return { ...item, - fill: highlightColor, - z: highlightSize, + fill: deploymentScatterGraphColorSize.HIGHLIGHTCOLOR, + z: deploymentScatterGraphColorSize.HIGHLIGHTSIZE, }; } return { ...item, - fill: cloudProviders[item.info.provider].color, - z: normalSize, + fill: providerPanelSettings.cloudProviders[item.info.provider].color, + z: deploymentScatterGraphColorSize.NORMALSIZE, }; }), })); @@ -173,176 +151,222 @@ const ProviderPanel = ({ numIterations, habitatData }) => { // eslint-disable-next-line react-hooks/exhaustive-deps }, [numIterations]); + useEffect(() => { + async function fetchData() { + const jsondata = await loadJsonFiles(habitatData, additionalProviders); + setProviderPanelSettings((prevState) => ({ + ...prevState, + plotData: jsondata.instanceArray, + initialData: jsondata.instanceArray, + cloudProviders: jsondata.cloudProviders, + fetchErrors: jsondata.errors, + })); + } + fetchData(); + // eslint-disable-next-line react-hooks/exhaustive-deps + }, []); return ( <> -
- Providers - - {habitatIsDemo && ( + {providerPanelSettings.plotData && + providerPanelSettings.cloudProviders ? ( +
+ Providers + + {habitatIsDemo && ( + + + Currently showing a demo data because local GPU is not + supported by DeepView.Predict + + + )} - Currently showing a demo data because local GPU is not supported by Habitat - - )} - - - - - -
-
Filter by provider
- - handleFilterChange("provider", e.target.value) - } - > - - {Object.keys(cloudProviders).map((provider, index) => { - return ( - - ); - })} - -
- - -
-
Filter by GPU
- - handleFilterChange("gpu", e.target.value) - } - > - - {[...gpuList].map((gpu, index) => { - return ( - - ); - })} - -
- - -
-
Filter Max Number of GPUs:
- - {MAX_GPU.map((numgpu) => ( - - handleFilterChange( - "numGpus ", - e.currentTarget.value - ) - } - > - {numgpu === 0 ? "all" : numgpu} - - ))} - -
- + + + + +
+
Filter by provider
+ + handleFilterChange("provider", e.target.value) + } + > + + {Object.keys( + providerPanelSettings.cloudProviders + ).map((provider, index) => { + return ( + + ); + })} + +
+ + +
+
Filter by GPU
+ + handleFilterChange("gpu", e.target.value) + } + > + + {[...gpuList].map((gpu, index) => { + return ( + + ); + })} + +
+ + +
+
Filter Max Number of GPUs:
+ + {MAX_GPU.map((numgpu) => ( + + handleFilterChange( + "numGpus ", + e.currentTarget.value + ) + } + > + {numgpu === 0 ? "all" : numgpu} + + ))} + +
+ +
+ + +
- - - -
- - -
- Deployment Plan - {providerPanelSettings.clicked && ( - - - - - - - - - -

- {providerPanelSettings.clicked.info.instance} -

- - {`Estimated Cost: ${currencyFormat( - providerPanelSettings.estimated_cost - )}`} - -

- - {`Estimated Training Time: ${numberFormat( - providerPanelSettings.estimated_time - )} Hours`} - -

- -
-
- - - - - - - - - - - - - - - -
GPUNum. GPUVRAM
{providerPanelSettings.clicked.info.gpu} - {providerPanelSettings.clicked.info.ngpus} - {providerPanelSettings.clicked.vmem} GB
-
-
-
- )} - {providerPanelSettings.clicked == null && ( - - - - Select a configuration. - - - + {providerPanelSettings.fetchErrors && ( + + + )} -
- -
-
-
+ + +
+ Deployment Plan + {providerPanelSettings.clicked && ( + + + + + + + + + +

+ {providerPanelSettings.clicked.info.instance} +

+ + {`Estimated Cost: ${currencyFormat( + providerPanelSettings.estimated_cost + )}`} + +

+ + {`Estimated Training Time: ${numberFormat( + providerPanelSettings.estimated_time + )} Hours`} + +

+ +
+
+ + + + + + + + + + + + + + + +
GPUNum. GPUVRAM
+ {providerPanelSettings.clicked.info.gpu} + + {providerPanelSettings.clicked.info.ngpus} + {providerPanelSettings.clicked.vmem} GB
+
+
+
+ )} + {providerPanelSettings.clicked == null && ( + + + + Select a configuration. + + + + )} +
+ + +
+
+ ) : ( +
+ + + + + Loading instances + + + + +
+ )} ); }; diff --git a/skyline-vscode/react-ui/src/sections/ProviderPanel.test.js b/skyline-vscode/react-ui/src/sections/ProviderPanel.test.js index d8b6c2a..213b2d4 100644 --- a/skyline-vscode/react-ui/src/sections/ProviderPanel.test.js +++ b/skyline-vscode/react-ui/src/sections/ProviderPanel.test.js @@ -1,10 +1,12 @@ import { render, screen } from "@testing-library/react"; import ProviderPanel from "./ProviderPanel"; +import { enableFetchMocks } from 'jest-fetch-mock' +enableFetchMocks() const { ResizeObserver } = window; const numIterations = 10000; -const data = [ +const habitatData = [ ["source", 22.029312], ["P100", 14.069682], ["P4000", 127.268085], // 27.268085 @@ -19,6 +21,35 @@ const data = [ ["RTX4000", 20.2342], ]; +const correctData = [ + { + name: "gcp", + logo: "resources/google.png", + color: "#ea4335", + instances: [ + { + name: "a2-highgpu-1g", + gpu: "a100", + ngpus: 1, + cost: 3.67, + }, + ], + }, + { + name: "aws", + logo: "resources/aws.png", + color: "#ff9900", + instances: [ + { + name: "p3.2xlarge", + gpu: "v100", + ngpus: 1, + cost: 3.06, + }, + ], + }, +]; + const noHabitatData = [ ["source", 22.029312], ["P100",14.069682], @@ -65,23 +96,34 @@ jest.mock("recharts", () => { }; }); -test("Shows a scatter chart", () => { +test("Shows a scatter chart", async() => { // ARRANGE + // Mock fetch response + jest.spyOn(global, 'fetch').mockResolvedValue({ + ok: true, + json: jest.fn().mockResolvedValue(correctData) + }) + const { container } = render( - + ); // ASSERT - expect(screen.getByText(/Providers/i)).toBeTruthy(); + expect(await screen.findByText(/Providers/i)).toBeTruthy(); expect( container.querySelector(".recharts-responsive-container") // eslint-disable-line ).toBeTruthy(); - expect(screen.getByText(/select a configuration/i)).toBeTruthy(); + expect(await screen.findByText(/select a configuration/i)).toBeTruthy(); }); -test("no habitat data received from backend", ()=>{ +test("no habitat data received from backend", async()=>{ // ARRANGE - const { container } = render(); + // Mock fetch response + jest.spyOn(global, 'fetch').mockResolvedValue({ + ok: true, + json: jest.fn().mockResolvedValue(correctData) + }) + const { container } = render(); // ASSERT - expect(screen.getByText(/currently showing a demo data because local gpu is not supported by habitat/i)).toBeTruthy(); + expect(await screen.findByText(/Currently showing a demo data because local GPU is not supported by DeepView.Predict/i)).toBeTruthy(); }) \ No newline at end of file diff --git a/skyline-vscode/react-ui/src/utils/parsers.js b/skyline-vscode/react-ui/src/utils/parsers.js new file mode 100644 index 0000000..035d48a --- /dev/null +++ b/skyline-vscode/react-ui/src/utils/parsers.js @@ -0,0 +1,90 @@ +import Ajv from "ajv"; +import { + gpuPropertyList, + CENTML_CLOUD_PROVIDERS_URL, + deploymentScatterGraphColorSize, +} from "../data/properties"; +import {getErrMsgFromInvalidURL} from '../utils/utils'; +import { cloudProviderSchema } from "../schema/CloudProvidersSchema"; + +const ajv = new Ajv({ allErrors: true }); // to report all validation errors (rather than failing on the first errors) +const validate = ajv.compile(cloudProviderSchema); + +export const loadJsonFiles = async (habitatData, additionalProviders) => { + let instanceId = 0; + const instanceArray = []; + const cloudProviders = {}; + const errors = []; + + // const buffer = new cloudProviderAndInstancesBuilder(); + let urlList = [ + CENTML_CLOUD_PROVIDERS_URL, + ]; + const additionalList = additionalProviders ? additionalProviders.split(","):[]; + urlList = urlList.concat(additionalList); + const listOfPromises = urlList.map((url) => + fetch(url, { cache: "no-store" }) + ); + const responses = await Promise.all(listOfPromises); + for (let resp of responses) { + if (resp.ok) { + try { + const respJsonData = await resp.json(); + const valid = validate(respJsonData); + if (valid) { + for (const cloudProvider of respJsonData) { + cloudProviders[cloudProvider.name.toLocaleLowerCase()] = { + name: cloudProvider.name, + logo: cloudProvider.logo, + color: cloudProvider.color, + }; + for (const instanceData of cloudProvider.instances) { + const found_in_habitat = habitatData.find( + (item) => + item[0].toLowerCase() === instanceData.gpu.toLowerCase() + ); + const found_in_gpuPropertyList = gpuPropertyList.find( + (item) => + item.name.toLocaleLowerCase() === + instanceData.gpu.toLocaleLowerCase() + ); + instanceArray.push({ + id: instanceId, + x: found_in_habitat[1], // msec + y: (instanceData.cost / 3.6e6) * found_in_habitat[1], // cost per msec * habitatData = cost per 1 iteration + info: { + instance: instanceData.name.toLocaleLowerCase(), + gpu: instanceData.gpu.toLocaleLowerCase(), + ngpus: instanceData.ngpus, + cost: instanceData.cost, + provider: cloudProvider.name.toLocaleLowerCase(), + }, + vmem: found_in_gpuPropertyList.vmem, + fill: cloudProvider.color, + z: deploymentScatterGraphColorSize.NORMALSIZE, + }); + instanceId += 1; + } + } + } else { + errors.push({ + msg: `invalid data format from url: ${resp.url}`, + invalidFields: validate.errors.map((err) => ({ + field: err.instancePath, + err: err.message, + })), + }); + } + } catch (error) { + errors.push(getErrMsgFromInvalidURL("noJsonResponseFromUrl",resp)); + } + } else { + errors.push(getErrMsgFromInvalidURL("invalidUrl",resp)); + } + } + return { + cloudProviders: Object.keys(cloudProviders).length > 0 ? cloudProviders: null, + instanceArray: instanceArray.length > 0 ? instanceArray:null, + errors: errors.length > 0 ? errors:null, + }; +}; diff --git a/skyline-vscode/react-ui/src/utils/parsers.test.js b/skyline-vscode/react-ui/src/utils/parsers.test.js new file mode 100644 index 0000000..2b3dffa --- /dev/null +++ b/skyline-vscode/react-ui/src/utils/parsers.test.js @@ -0,0 +1,138 @@ +import { loadJsonFiles } from "./parsers"; +import { enableFetchMocks } from "jest-fetch-mock"; +enableFetchMocks(); + +const habitatData = [ + ["source", 22.029312], + ["P100", 14.069682], + ["P4000", 127.268085], // 27.268085 + ["RTX2070", 16.088268], + ["RTX2080Ti", 11.826558], + ["T4", 22.029312], + ["V100", 10.182922], + ["A100", 10.068596], + ["RTX3090", 9.841998], + ["A40", 11.558072], + ["A4000", 14.67059], + ["RTX4000", 20.2342], +]; + +const correctData = [ + { + name: "gcp", + logo: "resources/google.png", + color: "#ea4335", + instances: [ + { + name: "a2-highgpu-1g", + gpu: "a100", + ngpus: 1, + cost: 3.67, + }, + ], + }, + { + name: "aws", + logo: "resources/aws.png", + color: "#ff9900", + instances: [ + { + name: "p3.2xlarge", + gpu: "v100", + ngpus: 1, + cost: 3.06, + }, + ], + }, +]; + +const incorrectData = [ + { + name: "gcp", + logo: "resources/google.png", + color: "#ea4335", + instances: [ + { + name: "a2-highgpu-1g", + gpu: "a100", + ngpus: 1, + cost: 3.67, + }, + ], + }, + { + name: "aws", + logo: "resources/aws.png", + instances: [ + { + name: "p3.2xlarge", + gpu: "rtx3050", + ngpus: 0, + cost: -3.06, + }, + ], + }, +]; + +test("Validate JSON file (URL) and return list of cloud providers and instances", async () => { + jest.spyOn(global, "fetch").mockResolvedValue({ + ok: true, + json: jest.fn().mockResolvedValue(correctData), + }); + const resp = await loadJsonFiles(habitatData, ""); + expect(resp.cloudProviders).toStrictEqual({ + gcp: { + name: "gcp", + logo: "resources/google.png", + color: "#ea4335", + }, + aws: { + name: "aws", + logo: "resources/aws.png", + color: "#ff9900", + }, + }); + expect(resp.instanceArray.length).toEqual(2); + expect(resp.instanceArray).toContainEqual({ + id: 0, + x: 10.068596, + y: 0.000010264374255555554, + info: { + instance: "a2-highgpu-1g", + gpu: "a100", + ngpus: 1, + cost: 3.67, + provider: "gcp", + }, + vmem: 40, + fill: "#ea4335", + z: 200, + }); + expect(resp.instanceArray).toContainEqual({ + id: 1, + x: 10.182922, + y: 0.0000086554837, + info: { + instance: "p3.2xlarge", + gpu: "v100", + ngpus: 1, + cost: 3.06, + provider: "aws", + }, + vmem: 16, + fill: "#ff9900", + z: 200, + }); +}); + +test("JSON file (URL) is not in the correct format", async () => { + jest.spyOn(global, "fetch").mockResolvedValue({ + ok: true, + json: jest.fn().mockResolvedValue(incorrectData), + }); + const resp = await loadJsonFiles(habitatData, ""); + expect(resp).toBeDefined(); + expect(resp.cloudProviders).toBeNull() + expect(resp.instanceArray).toBeNull() + expect(resp.errors[0].msg).toMatch(/invalid data format from url/) +}); diff --git a/skyline-vscode/react-ui/src/utils/utils.js b/skyline-vscode/react-ui/src/utils/utils.js index 1fae669..a64443e 100644 --- a/skyline-vscode/react-ui/src/utils/utils.js +++ b/skyline-vscode/react-ui/src/utils/utils.js @@ -1,79 +1,72 @@ -const BYTE_UNITS = [ - 'B', - 'KB', - 'MB', - 'GB', -]; +const BYTE_UNITS = ["B", "KB", "MB", "GB"]; -const ENERGY_UNITS = [ - 'J', - 'KJ', - 'MJ', - 'GJ', - 'TJ', - 'PJ', - 'EJ' -] +const ENERGY_UNITS = ["J", "KJ", "MJ", "GJ", "TJ", "PJ", "EJ"]; const GENERIC_UNITS = [ - '', - 'Thousands', - 'Millions', + "", + "Thousands", + "Millions", "Billions", - 'Trillion', - 'Quadrillion' -] + "Trillion", + "Quadrillion", +]; const formatTimeUnits = [ - [365,'day'], - [24, 'hour'], - [60, 'minute'], - [60, 'second'], - [1000,'msec'] -] + [365, "day"], + [24, "hour"], + [60, "minute"], + [60, "second"], + [1000, "msec"], +]; // reference : https://www.epa.gov/energy/greenhouse-gas-equivalencies-calculator const ENERGY_CONVERSION_UNITS = { kwh: 2.77778e-7, // 1J = 2.77778e-7 kwh carbon: 4.33e-4, // Electricity consumed (kilowatt-hours) : 7.09 × 10-4 metric tons CO2/kWh - miles: 1/(4.03e-4), // Miles driven by the average gasoline-powered passenger vehicle : 4.03 x 10-4 metric tons CO2E/mile - household: 1/7.94, // Home energy use : 7.94 metric tons CO2 per home per year. - phone: 1/(8.22e-6) // 8.22 x 10-6 metric tons CO2/smartphone charged -} + miles: 1 / 4.03e-4, // Miles driven by the average gasoline-powered passenger vehicle : 4.03 x 10-4 metric tons CO2E/mile + household: 1 / 7.94, // Home energy use : 7.94 metric tons CO2 per home per year. + phone: 1 / 8.22e-6, // 8.22 x 10-6 metric tons CO2/smartphone charged +}; -export function unitScale(quantity, unit){ +export function unitScale(quantity, unit) { let idx = 0; - while(quantity > 1000){ - quantity /=1000; - idx +=1; + while (quantity > 1000) { + quantity /= 1000; + idx += 1; } - switch(unit){ - case 'energy': - return {val:parseFloat(Number(quantity).toFixed(2)),scale:ENERGY_UNITS[idx],scale_index:idx} - case 'generic': - return {val:parseFloat(Number(quantity).toFixed(2)),scale:GENERIC_UNITS[idx],scale_index:idx} + switch (unit) { + case "energy": + return { + val: parseFloat(Number(quantity).toFixed(2)), + scale: ENERGY_UNITS[idx], + scale_index: idx, + }; + case "generic": + return { + val: parseFloat(Number(quantity).toFixed(2)), + scale: GENERIC_UNITS[idx], + scale_index: idx, + }; default: - return null + return null; } - } -export function homeEnergyUsedFormat(quantity){ - if (quantity >= 1){ - return [parseFloat(Number(quantity).toFixed(2)),'year'] +export function homeEnergyUsedFormat(quantity) { + if (quantity >= 1) { + return [parseFloat(Number(quantity).toFixed(2)), "year"]; } let idx = 0; let converter = []; - while(quantity < 1){ + while (quantity < 1) { converter = formatTimeUnits[idx]; quantity *= converter[0]; - idx+=1; + idx += 1; } - return [parseFloat(Number(quantity).toFixed(2)),converter[1]]; + return [parseFloat(Number(quantity).toFixed(2)), converter[1]]; } - // export function processFileReference(fileReferenceProto) { // return { // filePath: path.join(...(fileReferenceProto.getFilePath().getComponentsList())), @@ -82,8 +75,8 @@ export function homeEnergyUsedFormat(quantity){ // }; export function toPercentage(numerator, denominator) { - return numerator / denominator * 100; -}; + return (numerator / denominator) * 100; +} export function toReadableByteSize(sizeBytes) { let index = 0; @@ -99,10 +92,10 @@ export function toReadableByteSize(sizeBytes) { } return `${size.toFixed(1)} ${BYTE_UNITS[index]}`; -}; +} -export function scalePercentages({scaleSelector, shouldScale, applyFactor}) { - return function(list, scaleFactor) { +export function scalePercentages({ scaleSelector, shouldScale, applyFactor }) { + return function (list, scaleFactor) { let total = 0; const adjusted = []; @@ -119,18 +112,19 @@ export function scalePercentages({scaleSelector, shouldScale, applyFactor}) { } return adjusted.map(([newValue, element]) => - applyFactor(element, toPercentage(newValue, total))); + applyFactor(element, toPercentage(newValue, total)) + ); }; -}; +} export function getTraceByLevel(tree) { console.log("getTraceByLevel"); - var tree_size = function(idx) { + var tree_size = function (idx) { let total = 1; let num_children = tree[idx]["num_children"]; for (let i = 0; i < num_children; i++) { - tree[idx+total]["depth"] = 1 + tree[idx]["depth"]; - tree[idx+total]["parent"] = tree[idx]; + tree[idx + total]["depth"] = 1 + tree[idx]["depth"]; + tree[idx + total]["parent"] = tree[idx]; total += tree_size(idx + total); } return total; @@ -139,69 +133,87 @@ export function getTraceByLevel(tree) { tree[0]["depth"] = 0; tree_size(0); - let coarseDecomposition = tree.filter(node => { return node["depth"] === 1; }); - for (let fineLevel = 1; ; fineLevel ++) { - let fineDecomposition = tree.filter(node => { return node["depth"] === fineLevel; }); + let coarseDecomposition = tree.filter((node) => { + return node["depth"] === 1; + }); + for (let fineLevel = 1; ; fineLevel++) { + let fineDecomposition = tree.filter((node) => { + return node["depth"] === fineLevel; + }); console.log(`fineLevel: ${fineLevel}, length: ${fineDecomposition.length}`); - if (fineDecomposition.length === 0) return { coarse: coarseDecomposition, fine: coarseDecomposition }; - if (fineDecomposition.length >= 7) return { coarse: coarseDecomposition, fine: fineDecomposition }; + if (fineDecomposition.length === 0) + return { coarse: coarseDecomposition, fine: coarseDecomposition }; + if (fineDecomposition.length >= 7) + return { coarse: coarseDecomposition, fine: fineDecomposition }; } } export function computePercentage(operations, total_time) { let tracked_total_time = 0; for (let elem in operations) { - tracked_total_time += operations[elem]["forward_ms"] + operations[elem]["backward_ms"]; + tracked_total_time += + operations[elem]["forward_ms"] + operations[elem]["backward_ms"]; } total_time = Math.max(total_time, tracked_total_time); for (let elem in operations) { - operations[elem]["percentage"] = 100 * (operations[elem]["forward_ms"] + operations[elem]["backward_ms"]) / total_time; + operations[elem]["percentage"] = + (100 * + (operations[elem]["forward_ms"] + operations[elem]["backward_ms"])) / + total_time; } if (tracked_total_time < total_time) { operations.push({ name: "untracked", - percentage: 100 * (total_time - tracked_total_time) / total_time, + percentage: (100 * (total_time - tracked_total_time)) / total_time, num_children: 0, forward_ms: 0, backward_ms: 0, size_bytes: 0, - total_time: total_time - tracked_total_time + total_time: total_time - tracked_total_time, }); } return operations; } -export function energy_data(currentTotal){ - const kwh = currentTotal * ENERGY_CONVERSION_UNITS['kwh']; - const carbon_emission_tons = kwh*ENERGY_CONVERSION_UNITS['carbon']; - const carbon_unit = carbon_emission_tons < 1 ? - `${parseFloat(Number(carbon_emission_tons*1000).toFixed(2))} kg`: - `${parseFloat(Number(carbon_emission_tons).toFixed(2))} Metric Tons`; - const miles = unitScale(carbon_emission_tons * ENERGY_CONVERSION_UNITS['miles'],'generic'); - const household = homeEnergyUsedFormat(carbon_emission_tons * ENERGY_CONVERSION_UNITS['household']); - const phone = unitScale(carbon_emission_tons * ENERGY_CONVERSION_UNITS['phone'],'generic'); +export function energy_data(currentTotal) { + const kwh = currentTotal * ENERGY_CONVERSION_UNITS["kwh"]; + const carbon_emission_tons = kwh * ENERGY_CONVERSION_UNITS["carbon"]; + const carbon_unit = + carbon_emission_tons < 1 + ? `${parseFloat(Number(carbon_emission_tons * 1000).toFixed(2))} kg` + : `${parseFloat(Number(carbon_emission_tons).toFixed(2))} Metric Tons`; + const miles = unitScale( + carbon_emission_tons * ENERGY_CONVERSION_UNITS["miles"], + "generic" + ); + const household = homeEnergyUsedFormat( + carbon_emission_tons * ENERGY_CONVERSION_UNITS["household"] + ); + const phone = unitScale( + carbon_emission_tons * ENERGY_CONVERSION_UNITS["phone"], + "generic" + ); return { kwh: parseFloat(Number(kwh).toFixed(2)), carbon: carbon_unit, miles: `${miles.val} ${miles.scale}`, household: household, - phone: `${phone.val} ${phone.scale}` - } - + phone: `${phone.val} ${phone.scale}`, + }; } -export function calculate_training_time(numIterations, instance){ +export function calculate_training_time(numIterations, instance) { // instance.x is the time for 1 iterations in msec // 3.6e6 to convert total training time from msec to hours, divided by the total number of GPUS // output is in hours - return numIterations * instance.x / 3.6e6 / instance.info.ngpus; + return (numIterations * instance.x) / 3.6e6 / instance.info.ngpus; } -export function numberFormat(num){ +export function numberFormat(num) { const formatter = new Intl.NumberFormat("en-US", { notation: "compact", compactDisplay: "long", @@ -209,12 +221,28 @@ export function numberFormat(num){ return formatter.format(num); } -export function currencyFormat(cost){ +export function currencyFormat(cost) { const scientificFormater = new Intl.NumberFormat("en-US", { - style: 'currency', - currency: 'USD', + style: "currency", + currency: "USD", notation: "compact", compactDisplay: "short", }); return scientificFormater.format(cost); -} \ No newline at end of file +} + +export function getErrMsgFromInvalidURL(type, response) { + const code = `status: ${response?.statusText} | code: ${response?.status}`; + switch (type) { + case "noJsonResponseFromUrl": + return { + msg: `no json data from url: ${response?.url}`, + code + }; + default: // url is not accesible + return { + msg: `url is not accesible: ${response?.url}`, + code + }; + } +} diff --git a/skyline-vscode/src/extension.ts b/skyline-vscode/src/extension.ts index 20dcffa..6c61f66 100644 --- a/skyline-vscode/src/extension.ts +++ b/skyline-vscode/src/extension.ts @@ -51,12 +51,12 @@ export function activate(context: vscode.ExtensionContext) { retainContextWhenHidden: true } ); - let sess_options: SkylineSessionOptions = { context: context, projectRoot: uri[0].fsPath, addr: vsconfig.address, port: vsconfig.port, + providers: vsconfig.providers, isTelemetryEnabled: isTelemetryEnabled, webviewPanel: panel, telemetryLogger: logger diff --git a/skyline-vscode/src/skyline_session.ts b/skyline-vscode/src/skyline_session.ts index af1d505..c272233 100644 --- a/skyline-vscode/src/skyline_session.ts +++ b/skyline-vscode/src/skyline_session.ts @@ -1,4 +1,3 @@ - import * as vscode from 'vscode'; import * as pb from './protobuf/innpv_pb'; import * as path from 'path'; @@ -7,6 +6,7 @@ const fs = require('fs'); import {Socket} from 'net'; import { simpleDecoration } from './decorations'; import { energy_component_type_mapping, getObjectKeyNameFromValue } from './utils'; +import { stringify } from 'querystring'; const crypto = require('crypto'); const resolve = require('path').resolve; @@ -16,9 +16,10 @@ export interface SkylineSessionOptions { projectRoot: string; addr: string; port: number; + providers: string; isTelemetryEnabled: CallableFunction; - webviewPanel: vscode.WebviewPanel, - telemetryLogger: vscode.TelemetryLogger + webviewPanel: vscode.WebviewPanel; + telemetryLogger: vscode.TelemetryLogger; } export interface SkylineEnvironment { @@ -30,6 +31,7 @@ export class SkylineSession { connection: Socket; port: number; addr: string; + providers:string; seq_num: number; last_length: number; message_buffer: Uint8Array; @@ -68,8 +70,9 @@ export class SkylineSession { this.connection.on('connect', this.on_connect.bind(this)); this.connection.on('data', this.on_data.bind(this)); this.connection.on('close', this.on_close_connection.bind(this)); - this.port = options.port - this.addr = options.addr + this.port = options.port; + this.addr = options.addr; + this.providers = options.providers; this.seq_num = 0; this.last_length = -1; @@ -379,7 +382,7 @@ export class SkylineSession { Skyline - + @@ -407,7 +410,9 @@ export class SkylineSession { "breakdown": {}, "habitat": [] as Array<[string, number]>, - "energy": {} + "additionalProviders": this.providers, + "energy": {}, + }; if (this.msg_throughput) { diff --git a/skyline-vscode/src/utils.ts b/skyline-vscode/src/utils.ts index 5ad5fe9..9f8cff9 100644 --- a/skyline-vscode/src/utils.ts +++ b/skyline-vscode/src/utils.ts @@ -21,4 +21,4 @@ export const getObjectKeyNameFromValue = (obj: Object, val: any): string | undef export const filterObjectByKeyName = (obj: Object, prefix: string): Object => { return Object.fromEntries(Object.entries(obj).filter(([key, val])=> key.startsWith(prefix))); -}; \ No newline at end of file +};