From 11b867b3faba5ef476110c716468e708a730cd82 Mon Sep 17 00:00:00 2001 From: YingXue Date: Thu, 12 Aug 2021 13:07:49 -0700 Subject: [PATCH] Ying/update event hub sdk (#463) * update eventhub node sdk; fix breaking change where hub connection string cannot be directly used to create client * package verison update --- package-lock.json | 1026 ++++++++--------- package.json | 4 +- public/constants.ts | 1 - public/electron.ts | 3 +- public/factories/eventHubInterfaceFactory.ts | 3 - public/handlers/eventHubHandler.ts | 186 +-- public/interfaces/eventHubInterface.ts | 2 - public/utils/eventHubHelper.ts | 83 ++ src/app/api/parameters/deviceParameters.ts | 1 - src/app/api/services/devicesService.spec.ts | 19 +- src/app/api/services/devicesService.ts | 5 - src/app/devices/deviceEvents/actions.ts | 2 +- .../customEventHub.spec.tsx.snap | 9 - .../__snapshots__/deviceEvents.spec.tsx.snap | 3 - .../components/customEventHub.spec.tsx | 7 - .../components/customEventHub.tsx | 18 - .../components/deviceEvents.spec.tsx | 3 - .../deviceEvents/components/deviceEvents.tsx | 44 +- src/app/devices/deviceEvents/reducer.spec.ts | 19 +- src/app/devices/deviceEvents/reducers.ts | 18 +- src/app/devices/deviceEvents/saga.spec.ts | 51 - src/app/devices/deviceEvents/saga.ts | 23 +- src/localization/locales/en.json | 8 +- src/localization/resourceKeys.ts | 4 - src/server/eventHubHelper.ts | 83 ++ src/server/serverBase.spec.ts | 20 - src/server/serverBase.ts | 186 +-- 27 files changed, 797 insertions(+), 1034 deletions(-) create mode 100644 public/utils/eventHubHelper.ts create mode 100644 src/server/eventHubHelper.ts diff --git a/package-lock.json b/package-lock.json index eff858efe..8c61b5fcd 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,6 +1,6 @@ { "name": "azure-iot-explorer", - "version": "0.14.5", + "version": "0.14.6", "lockfileVersion": 1, "requires": true, "dependencies": { @@ -10,40 +10,89 @@ "integrity": "sha512-GLyWIFBbGvpKPGo55JyRZAo4lVbnBiD52cKlw/0Vt+wnmKvWJkpZvsjVoaIolyBXDeAQKSicRtqFNPem9w0WYA==", "dev": true }, - "@azure/amqp-common": { - "version": "0.1.9", - "resolved": "https://registry.npmjs.org/@azure/amqp-common/-/amqp-common-0.1.9.tgz", - "integrity": "sha512-B/HFWNbqAjFjhj8x/zlHcpuYtsr92l3ZVArJdumi2kpN2Di/h4g6GIa2JeQEDD+rkLa3oAR6zHKfJbGnybOmvg==", + "@azure/abort-controller": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/@azure/abort-controller/-/abort-controller-1.0.4.tgz", + "integrity": "sha512-lNUmDRVGpanCsiUN3NWxFTdwmdFI53xwhkTFfHDGTYk46ca7Ind3nanJc+U6Zj9Tv+9nTCWRBscWEW1DyKOpTw==", "requires": { - "async-lock": "^1.1.3", - "debug": "^3.1.0", + "tslib": "^2.0.0" + } + }, + "@azure/core-amqp": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/@azure/core-amqp/-/core-amqp-3.0.0.tgz", + "integrity": "sha512-CThDTapwSE2jKr/m9ISHwynagFPkUslwPsyppuJkLaFvc9re5/Kvv5sEP2i5DTG41lxYi0SznqRKEqIBZBJYOw==", + "requires": { + "@azure/abort-controller": "^1.0.0", + "@azure/core-auth": "^1.3.0", + "@azure/logger": "^1.0.0", + "buffer": "^5.2.1", + "events": "^3.0.0", + "jssha": "^3.1.0", + "process": "^0.11.10", + "rhea": "^2.0.2", + "rhea-promise": "^2.0.0", + "tslib": "^2.0.0", + "url": "^0.11.0", + "util": "^0.12.1" + } + }, + "@azure/core-asynciterator-polyfill": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/@azure/core-asynciterator-polyfill/-/core-asynciterator-polyfill-1.0.0.tgz", + "integrity": "sha512-kmv8CGrPfN9SwMwrkiBK9VTQYxdFQEGe0BmQk+M8io56P9KNzpAxcWE/1fxJj7uouwN4kXF0BHW8DNlgx+wtCg==" + }, + "@azure/core-auth": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/@azure/core-auth/-/core-auth-1.3.2.tgz", + "integrity": "sha512-7CU6DmCHIZp5ZPiZ9r3J17lTKMmYsm/zGvNkjArQwPkrLlZ1TZ+EUYfGgh2X31OLMVAQCTJZW4cXHJi02EbJnA==", + "requires": { + "@azure/abort-controller": "^1.0.0", + "tslib": "^2.2.0" + } + }, + "@azure/core-tracing": { + "version": "1.0.0-preview.12", + "resolved": "https://registry.npmjs.org/@azure/core-tracing/-/core-tracing-1.0.0-preview.12.tgz", + "integrity": "sha512-nvo2Wc4EKZGN6eFu9n3U7OXmASmL8VxoPIH7xaD6OlQqi44bouF0YIi9ID5rEsKLiAU59IYx6M297nqWVMWPDg==", + "requires": { + "@opentelemetry/api": "^1.0.0", + "tslib": "^2.2.0" + } + }, + "@azure/event-hubs": { + "version": "5.6.0", + "resolved": "https://registry.npmjs.org/@azure/event-hubs/-/event-hubs-5.6.0.tgz", + "integrity": "sha512-+JtP2SYqGZelvZ6nnA7T0xxcDjCu3O0K+8A8vEXOJgQPT8+hA11fz61DSpFQJH1KaXffoN5YeXnKSaU4idwiAQ==", + "requires": { + "@azure/abort-controller": "^1.0.0", + "@azure/core-amqp": "^3.0.0", + "@azure/core-asynciterator-polyfill": "^1.0.0", + "@azure/core-auth": "^1.3.0", + "@azure/core-tracing": "1.0.0-preview.12", + "@azure/logger": "^1.0.0", + "buffer": "^5.2.1", "is-buffer": "^2.0.3", - "jssha": "^2.3.1", - "ms-rest-azure": "^2.5.9", - "tslib": "^1.9.3" + "jssha": "^3.1.0", + "process": "^0.11.10", + "rhea-promise": "^2.1.0", + "tslib": "^2.2.0", + "uuid": "^8.3.0" }, "dependencies": { - "is-buffer": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/is-buffer/-/is-buffer-2.0.5.tgz", - "integrity": "sha512-i2R6zNFDwgEHJyQUtJEk0XFi1i0dPFn/oqjK3/vPCcDeJvW5NQ83V8QbicfF1SupOaB0h8ntgBC2YiE7dfyctQ==" + "uuid": { + "version": "8.3.2", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-8.3.2.tgz", + "integrity": "sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==" } } }, - "@azure/event-hubs": { - "version": "1.0.7", - "resolved": "https://registry.npmjs.org/@azure/event-hubs/-/event-hubs-1.0.7.tgz", - "integrity": "sha512-BbKhS2m+3remYTpY1+o9NJT8dO1QAB3FwyDyglQ7tw+4u4gUgLyHH+gdv0Zfk4T1UCt35PKYRXCYykhi1wFEug==", + "@azure/logger": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/@azure/logger/-/logger-1.0.2.tgz", + "integrity": "sha512-YZNjNV0vL3nN2nedmcjQBcpCTo3oqceXmgiQtEm6fLpucjRZyQKAQruhCmCpRlB1iykqKJJ/Y8CDmT5rIE6IJw==", "requires": { - "@azure/amqp-common": "^0.1.3", - "async-lock": "^1.1.3", - "debug": "^3.1.0", - "is-buffer": "2.0.2", - "jssha": "^2.3.1", - "ms-rest-azure": "^2.5.9", - "rhea-promise": "^0.1.6", - "tslib": "^1.9.3", - "uuid": "^3.3.2" + "tslib": "^2.0.0" } }, "@babel/code-frame": { @@ -56,9 +105,9 @@ } }, "@babel/compat-data": { - "version": "7.14.7", - "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.14.7.tgz", - "integrity": "sha512-nS6dZaISCXJ3+518CWiBfEr//gHyMO02uDxBkXTKZDN5POruCnOZ1N4YBRZDCabwF8nZMWBpRxIicmXtBs+fvw==", + "version": "7.14.9", + "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.14.9.tgz", + "integrity": "sha512-p3QjZmMGHDGdpcwEYYWu7i7oJShJvtgMjJeb0W95PPhSm++3lm8YXYOh45Y6iCN9PkZLTZ7CIX5nFrp7pw7TXw==", "dev": true }, "@babel/core": { @@ -117,12 +166,12 @@ } }, "@babel/generator": { - "version": "7.14.8", - "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.14.8.tgz", - "integrity": "sha512-cYDUpvIzhBVnMzRoY1fkSEhK/HmwEVwlyULYgn/tMQYd6Obag3ylCjONle3gdErfXBW61SVTlR9QR7uWlgeIkg==", + "version": "7.14.9", + "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.14.9.tgz", + "integrity": "sha512-4yoHbhDYzFa0GLfCzLp5GxH7vPPMAHdZjyE7M/OajM9037zhx0rf+iNsJwp4PT0MSFpwjG7BsHEbPkBQpZ6cYA==", "dev": true, "requires": { - "@babel/types": "^7.14.8", + "@babel/types": "^7.14.9", "jsesc": "^2.5.1", "source-map": "^0.5.0" }, @@ -256,9 +305,9 @@ } }, "@babel/helper-validator-identifier": { - "version": "7.14.8", - "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.14.8.tgz", - "integrity": "sha512-ZGy6/XQjllhYQrNw/3zfWRwZCTVSiBLZ9DHVZxn9n2gip/7ab8mv2TWlKPIBk26RwedCBoWdjLmn+t9na2Gcow==", + "version": "7.14.9", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.14.9.tgz", + "integrity": "sha512-pQYxPY0UP6IHISRitNe8bsijHex4TWZXi2HwKVsjPiltzlhse2znVcm9Ace510VT1kxIHjGJCZZQBX2gJDbo0g==", "dev": true }, "@babel/helper-validator-option": { @@ -342,9 +391,9 @@ } }, "@babel/parser": { - "version": "7.14.8", - "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.14.8.tgz", - "integrity": "sha512-syoCQFOoo/fzkWDeM0dLEZi5xqurb5vuyzwIMNZRNun+N/9A4cUZeQaE7dTrB8jGaKuJRBtEOajtnmw0I5hvvA==", + "version": "7.14.9", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.14.9.tgz", + "integrity": "sha512-RdUTOseXJ8POjjOeEBEvNMIZU/nm4yu2rufRkcibzkkg7DmQvXU8v3M4Xk9G7uuI86CDGkKcuDWgioqZm+mScQ==", "dev": true }, "@babel/plugin-syntax-async-generators": { @@ -484,18 +533,18 @@ } }, "@babel/traverse": { - "version": "7.14.8", - "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.14.8.tgz", - "integrity": "sha512-kexHhzCljJcFNn1KYAQ6A5wxMRzq9ebYpEDV4+WdNyr3i7O44tanbDOR/xjiG2F3sllan+LgwK+7OMk0EmydHg==", + "version": "7.14.9", + "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.14.9.tgz", + "integrity": "sha512-bldh6dtB49L8q9bUyB7bC20UKgU+EFDwKJylwl234Kv+ySZeMD31Xeht6URyueQ6LrRRpF2tmkfcZooZR9/e8g==", "dev": true, "requires": { "@babel/code-frame": "^7.14.5", - "@babel/generator": "^7.14.8", + "@babel/generator": "^7.14.9", "@babel/helper-function-name": "^7.14.5", "@babel/helper-hoist-variables": "^7.14.5", "@babel/helper-split-export-declaration": "^7.14.5", - "@babel/parser": "^7.14.8", - "@babel/types": "^7.14.8", + "@babel/parser": "^7.14.9", + "@babel/types": "^7.14.9", "debug": "^4.1.0", "globals": "^11.1.0" }, @@ -518,12 +567,12 @@ } }, "@babel/types": { - "version": "7.14.8", - "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.14.8.tgz", - "integrity": "sha512-iob4soQa7dZw8nodR/KlOQkPh9S4I8RwCxwRIFuiMRYjOzH/KJzdUfDgz6cGi5dDaclXF4P2PAhCdrBJNIg68Q==", + "version": "7.14.9", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.14.9.tgz", + "integrity": "sha512-u0bLTnv3DFHeaQLYzb7oRJ1JHr1sv/SYDM7JSqHFFLwXG1wTZRughxFI5NCP8qBEo1rVVsn7Yg2Lvw49nne/Ow==", "dev": true, "requires": { - "@babel/helper-validator-identifier": "^7.14.8", + "@babel/helper-validator-identifier": "^7.14.9", "to-fast-properties": "^2.0.0" } }, @@ -588,102 +637,60 @@ } }, "@fluentui/date-time-utilities": { - "version": "8.2.1", - "resolved": "https://registry.npmjs.org/@fluentui/date-time-utilities/-/date-time-utilities-8.2.1.tgz", - "integrity": "sha512-0AYXaXFQ3bPsOtiOi3bJSihzf+w3L44iQK38EiQgp3uAXei/i36VtDCToHZehYV+eS4s1qb/QGksoL0F4G6WCQ==", + "version": "8.2.2", + "resolved": "https://registry.npmjs.org/@fluentui/date-time-utilities/-/date-time-utilities-8.2.2.tgz", + "integrity": "sha512-djHrX/38ty+F93qLQjzmRzPzK598CW9g/RPhQH6GyrFBLPSWM1swYKB5TP6E7FrIf+fT4pVqrNUSYZhgi2rrOQ==", "requires": { - "@fluentui/set-version": "^8.1.3", + "@fluentui/set-version": "^8.1.4", "tslib": "^2.1.0" - }, - "dependencies": { - "tslib": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.3.0.tgz", - "integrity": "sha512-N82ooyxVNm6h1riLCoyS9e3fuJ3AMG2zIZs2Gd1ATcSFjSA23Q0fzjjZeh0jbJvWVDZ0cJT8yaNNaaXHzueNjg==" - } } }, "@fluentui/dom-utilities": { - "version": "2.1.3", - "resolved": "https://registry.npmjs.org/@fluentui/dom-utilities/-/dom-utilities-2.1.3.tgz", - "integrity": "sha512-i2YECSldnkzPAhVmrxksuKSbqBKfMbHrexGqDJm7dy6KoIx+JSilbA5Lz0YNhA7VEgCy1X01GHlWBvqhCNQW8g==", + "version": "2.1.4", + "resolved": "https://registry.npmjs.org/@fluentui/dom-utilities/-/dom-utilities-2.1.4.tgz", + "integrity": "sha512-+gsAnEjgoKB37o+tsMdSLtgqZ9z2PzpvnHx/2IqhRWjQQd7Xc7MbQsbZaQ5qfkioFHLnWGc/+WORpqKPy/sWrg==", "requires": { - "@fluentui/set-version": "^8.1.3", + "@fluentui/set-version": "^8.1.4", "tslib": "^2.1.0" - }, - "dependencies": { - "tslib": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.3.0.tgz", - "integrity": "sha512-N82ooyxVNm6h1riLCoyS9e3fuJ3AMG2zIZs2Gd1ATcSFjSA23Q0fzjjZeh0jbJvWVDZ0cJT8yaNNaaXHzueNjg==" - } } }, "@fluentui/font-icons-mdl2": { - "version": "8.1.7", - "resolved": "https://registry.npmjs.org/@fluentui/font-icons-mdl2/-/font-icons-mdl2-8.1.7.tgz", - "integrity": "sha512-piTdL/rKTx3cNBaMBc+u7YtluLfKYt9NY7Z341h3gZyYfgzeO8djU4RD7KpSozocPPGjooMjCjfPWDVh3DZqkw==", + "version": "8.1.8", + "resolved": "https://registry.npmjs.org/@fluentui/font-icons-mdl2/-/font-icons-mdl2-8.1.8.tgz", + "integrity": "sha512-kZkCHM/mP8WWLLExz3x3wK5yQHPP4tAcvlHVqe69TbG8+3fxRGJSMOxzZO/04CFQp2A7/wOskSRtqeIBtaXJfw==", "requires": { - "@fluentui/set-version": "^8.1.3", - "@fluentui/style-utilities": "^8.2.1", + "@fluentui/set-version": "^8.1.4", + "@fluentui/style-utilities": "^8.2.2", "tslib": "^2.1.0" - }, - "dependencies": { - "tslib": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.3.0.tgz", - "integrity": "sha512-N82ooyxVNm6h1riLCoyS9e3fuJ3AMG2zIZs2Gd1ATcSFjSA23Q0fzjjZeh0jbJvWVDZ0cJT8yaNNaaXHzueNjg==" - } } }, "@fluentui/foundation-legacy": { - "version": "8.1.7", - "resolved": "https://registry.npmjs.org/@fluentui/foundation-legacy/-/foundation-legacy-8.1.7.tgz", - "integrity": "sha512-hzzgbtMFp0gPi5qG9+Wa2ImYrfEuld3880g3xsxfrjRgVQ5zcRIkNFNc5//YfiLRqi0y9wNUuDrEI08ZMvxSBA==", - "requires": { - "@fluentui/merge-styles": "^8.1.3", - "@fluentui/set-version": "^8.1.3", - "@fluentui/style-utilities": "^8.2.1", - "@fluentui/utilities": "^8.2.1", + "version": "8.1.8", + "resolved": "https://registry.npmjs.org/@fluentui/foundation-legacy/-/foundation-legacy-8.1.8.tgz", + "integrity": "sha512-m0zbRbZaJbzBjv8Ziv3zpwoGFjuzzPIAmhsn58g66MZvQBd9vN92hFJBNG2bO2+ivlprns4WnLEAiPK8CjoAsA==", + "requires": { + "@fluentui/merge-styles": "^8.1.4", + "@fluentui/set-version": "^8.1.4", + "@fluentui/style-utilities": "^8.2.2", + "@fluentui/utilities": "^8.2.2", "tslib": "^2.1.0" - }, - "dependencies": { - "tslib": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.3.0.tgz", - "integrity": "sha512-N82ooyxVNm6h1riLCoyS9e3fuJ3AMG2zIZs2Gd1ATcSFjSA23Q0fzjjZeh0jbJvWVDZ0cJT8yaNNaaXHzueNjg==" - } } }, "@fluentui/keyboard-key": { - "version": "0.3.3", - "resolved": "https://registry.npmjs.org/@fluentui/keyboard-key/-/keyboard-key-0.3.3.tgz", - "integrity": "sha512-3qX1WNCgJlKq7uGH76rLC4cdESgwdLhMH9WjcQUkQNJKtBpL4vs5O99M1keEhd3pfooW7zasr6AcYcWq4BRB4g==", + "version": "0.3.4", + "resolved": "https://registry.npmjs.org/@fluentui/keyboard-key/-/keyboard-key-0.3.4.tgz", + "integrity": "sha512-pVY2m3IC5+LLmMzsaPApX9eKTzpOzdgQwrR3FNTE6mGx3N/+QWYM7fdF+T1ldZQt87dCRSeQnmAo5kqjtxeA/w==", "requires": { "tslib": "^2.1.0" - }, - "dependencies": { - "tslib": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.3.0.tgz", - "integrity": "sha512-N82ooyxVNm6h1riLCoyS9e3fuJ3AMG2zIZs2Gd1ATcSFjSA23Q0fzjjZeh0jbJvWVDZ0cJT8yaNNaaXHzueNjg==" - } } }, "@fluentui/merge-styles": { - "version": "8.1.3", - "resolved": "https://registry.npmjs.org/@fluentui/merge-styles/-/merge-styles-8.1.3.tgz", - "integrity": "sha512-5vZUyXnbOb9M1rMLzQ7Kj5uadHgSTsp3gv0xDv6bfPvzB9RgQa3dEuJ6TA34tLezw8sFYuA6NnKd57nlb4aXMA==", + "version": "8.1.4", + "resolved": "https://registry.npmjs.org/@fluentui/merge-styles/-/merge-styles-8.1.4.tgz", + "integrity": "sha512-zCAEjZyALk0CGW1H9YNJU+e/MW0P5sFJfrDvac27K4S/dIQvKnOwMUNOWRkNz3yUEt0R9vo0NtiO3cW04cZq3A==", "requires": { - "@fluentui/set-version": "^8.1.3", + "@fluentui/set-version": "^8.1.4", "tslib": "^2.1.0" - }, - "dependencies": { - "tslib": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.3.0.tgz", - "integrity": "sha512-N82ooyxVNm6h1riLCoyS9e3fuJ3AMG2zIZs2Gd1ATcSFjSA23Q0fzjjZeh0jbJvWVDZ0cJT8yaNNaaXHzueNjg==" - } } }, "@fluentui/react": { @@ -704,138 +711,82 @@ "@fluentui/utilities": "^8.1.2", "@microsoft/load-themed-styles": "^1.10.26", "tslib": "^2.1.0" - }, - "dependencies": { - "tslib": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.3.0.tgz", - "integrity": "sha512-N82ooyxVNm6h1riLCoyS9e3fuJ3AMG2zIZs2Gd1ATcSFjSA23Q0fzjjZeh0jbJvWVDZ0cJT8yaNNaaXHzueNjg==" - } } }, "@fluentui/react-focus": { - "version": "8.1.9", - "resolved": "https://registry.npmjs.org/@fluentui/react-focus/-/react-focus-8.1.9.tgz", - "integrity": "sha512-vYnenLKUj2Uxj5sjuv6AJJ1kYCqq/stM4ZMCQmJWq2qiMm2TpqW3vMpRspuxwD9BqOVbIFNoj9pEWpxAaTCEqw==", - "requires": { - "@fluentui/keyboard-key": "^0.3.3", - "@fluentui/merge-styles": "^8.1.3", - "@fluentui/set-version": "^8.1.3", - "@fluentui/style-utilities": "^8.2.1", - "@fluentui/utilities": "^8.2.1", + "version": "8.1.10", + "resolved": "https://registry.npmjs.org/@fluentui/react-focus/-/react-focus-8.1.10.tgz", + "integrity": "sha512-sojXA6epu2QJbFf+XqP1AHOrrWssoQJWJNuzp0MCzQOWCUlLLqRpRUHtUKZzCnrbD9G5MOW8/192m/rSPyM7eA==", + "requires": { + "@fluentui/keyboard-key": "^0.3.4", + "@fluentui/merge-styles": "^8.1.4", + "@fluentui/set-version": "^8.1.4", + "@fluentui/style-utilities": "^8.2.2", + "@fluentui/utilities": "^8.2.2", "tslib": "^2.1.0" - }, - "dependencies": { - "tslib": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.3.0.tgz", - "integrity": "sha512-N82ooyxVNm6h1riLCoyS9e3fuJ3AMG2zIZs2Gd1ATcSFjSA23Q0fzjjZeh0jbJvWVDZ0cJT8yaNNaaXHzueNjg==" - } } }, "@fluentui/react-hooks": { - "version": "8.2.4", - "resolved": "https://registry.npmjs.org/@fluentui/react-hooks/-/react-hooks-8.2.4.tgz", - "integrity": "sha512-qc/j0YdxC0zAWVqh8BJppZuK3o9/rfyu5psY4N/AL9dmKrTFWszRgTSB5uiRShN99L88UUEV9RtlfknnLDGrUg==", + "version": "8.2.6", + "resolved": "https://registry.npmjs.org/@fluentui/react-hooks/-/react-hooks-8.2.6.tgz", + "integrity": "sha512-nz0iycSUmGX6eBKsmW23ocmKn/HdV7c8HnMHx5fcGIQbOqOH8Hv4wq8t3RozsZBapIi/nDjpZs2UvB4zDFsg1g==", "requires": { - "@fluentui/react-window-provider": "^2.1.3", - "@fluentui/set-version": "^8.1.3", - "@fluentui/utilities": "^8.2.1", + "@fluentui/react-window-provider": "^2.1.4", + "@fluentui/set-version": "^8.1.4", + "@fluentui/utilities": "^8.2.2", "tslib": "^2.1.0" - }, - "dependencies": { - "tslib": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.3.0.tgz", - "integrity": "sha512-N82ooyxVNm6h1riLCoyS9e3fuJ3AMG2zIZs2Gd1ATcSFjSA23Q0fzjjZeh0jbJvWVDZ0cJT8yaNNaaXHzueNjg==" - } } }, "@fluentui/react-window-provider": { - "version": "2.1.3", - "resolved": "https://registry.npmjs.org/@fluentui/react-window-provider/-/react-window-provider-2.1.3.tgz", - "integrity": "sha512-3NWL3Kkqp3elD/aTrUaEufRUrN7K7hYsXEsOY2bDCDjPadLGtZlTGWNYFbUYNsaL/v79gZHhH+voCECP85HqRg==", + "version": "2.1.4", + "resolved": "https://registry.npmjs.org/@fluentui/react-window-provider/-/react-window-provider-2.1.4.tgz", + "integrity": "sha512-RztmJ7ol2eMDr3NCs2OcAA1cQjZdPPUEa4aurgh4Aq+JM/BiY0aK6S4SeFtVD7F8Q7PBOz/xwOG4HlnSMQtlsg==", "requires": { - "@fluentui/set-version": "^8.1.3", + "@fluentui/set-version": "^8.1.4", "tslib": "^2.1.0" - }, - "dependencies": { - "tslib": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.3.0.tgz", - "integrity": "sha512-N82ooyxVNm6h1riLCoyS9e3fuJ3AMG2zIZs2Gd1ATcSFjSA23Q0fzjjZeh0jbJvWVDZ0cJT8yaNNaaXHzueNjg==" - } } }, "@fluentui/set-version": { - "version": "8.1.3", - "resolved": "https://registry.npmjs.org/@fluentui/set-version/-/set-version-8.1.3.tgz", - "integrity": "sha512-QYLFBnwa6xJj0phEy6r+iO5bXWXxkhS1uNngUwfWsHaTskDa4PXDDdjZAXjTVV935xqmoM4GZhZk+Tcoe/OaXA==", + "version": "8.1.4", + "resolved": "https://registry.npmjs.org/@fluentui/set-version/-/set-version-8.1.4.tgz", + "integrity": "sha512-2otMyJ+s+W+hjBD4BKjwYKKinJUDeIKYKz93qKrrJS0i3fKfftNroy9dHFlIblZ7n747L334plLi3bzQO1bnvA==", "requires": { "tslib": "^2.1.0" - }, - "dependencies": { - "tslib": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.3.0.tgz", - "integrity": "sha512-N82ooyxVNm6h1riLCoyS9e3fuJ3AMG2zIZs2Gd1ATcSFjSA23Q0fzjjZeh0jbJvWVDZ0cJT8yaNNaaXHzueNjg==" - } } }, "@fluentui/style-utilities": { - "version": "8.2.1", - "resolved": "https://registry.npmjs.org/@fluentui/style-utilities/-/style-utilities-8.2.1.tgz", - "integrity": "sha512-+ZSl7Hz/C9DX1dYTZBnmP/57fHPK2qrLr0oTLJTq4uLjmG5Hxyo6gZ9/YCrEnW0h1d3tZlNa4LH5X8rux+D8kg==", - "requires": { - "@fluentui/merge-styles": "^8.1.3", - "@fluentui/set-version": "^8.1.3", - "@fluentui/theme": "^2.2.0", - "@fluentui/utilities": "^8.2.1", + "version": "8.2.2", + "resolved": "https://registry.npmjs.org/@fluentui/style-utilities/-/style-utilities-8.2.2.tgz", + "integrity": "sha512-PKixlYfY93XOZRPNi+I8Qw9SkcBZsnG/qg2+3IxLGXpCVYKOmP52oR7N5j/nmspQZXdEoHehYa2z/lsKC2xw1w==", + "requires": { + "@fluentui/merge-styles": "^8.1.4", + "@fluentui/set-version": "^8.1.4", + "@fluentui/theme": "^2.2.1", + "@fluentui/utilities": "^8.2.2", "@microsoft/load-themed-styles": "^1.10.26", "tslib": "^2.1.0" - }, - "dependencies": { - "tslib": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.3.0.tgz", - "integrity": "sha512-N82ooyxVNm6h1riLCoyS9e3fuJ3AMG2zIZs2Gd1ATcSFjSA23Q0fzjjZeh0jbJvWVDZ0cJT8yaNNaaXHzueNjg==" - } } }, "@fluentui/theme": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/@fluentui/theme/-/theme-2.2.0.tgz", - "integrity": "sha512-LKM2ML05WU22It39iw523wGrpLG5qcvmQkSV7HV5NERSeZkI79TYntmXecaKaKWZjdUFTsZDijuhCOkWypNG5Q==", + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/@fluentui/theme/-/theme-2.2.1.tgz", + "integrity": "sha512-1G92TftVulrGXklL5upaN/WrrSzY/va39RM1eo0XO/Q3+kAhAajclQAXb7XanqOsVFcAqK1DbVvWayrY9DG2Qg==", "requires": { - "@fluentui/merge-styles": "^8.1.3", - "@fluentui/set-version": "^8.1.3", - "@fluentui/utilities": "^8.2.1", + "@fluentui/merge-styles": "^8.1.4", + "@fluentui/set-version": "^8.1.4", + "@fluentui/utilities": "^8.2.2", "tslib": "^2.1.0" - }, - "dependencies": { - "tslib": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.3.0.tgz", - "integrity": "sha512-N82ooyxVNm6h1riLCoyS9e3fuJ3AMG2zIZs2Gd1ATcSFjSA23Q0fzjjZeh0jbJvWVDZ0cJT8yaNNaaXHzueNjg==" - } } }, "@fluentui/utilities": { - "version": "8.2.1", - "resolved": "https://registry.npmjs.org/@fluentui/utilities/-/utilities-8.2.1.tgz", - "integrity": "sha512-ezRkBUDhHQjrqAWA6H1TwWU3STauW/EjthDAe/upJbmXeP3ynn7tTpx1gpNi/vHjJlRkO1JjNoiSc6P4MZWkYw==", + "version": "8.2.2", + "resolved": "https://registry.npmjs.org/@fluentui/utilities/-/utilities-8.2.2.tgz", + "integrity": "sha512-aM2/CgoTIssMDs7MoTla+q/VXN5Gkk4s12S8GZNp87cmEzDy008tKkiRpHj6PXZuvJL5bctZco9YQgusO0jZEg==", "requires": { - "@fluentui/dom-utilities": "^2.1.3", - "@fluentui/merge-styles": "^8.1.3", - "@fluentui/set-version": "^8.1.3", + "@fluentui/dom-utilities": "^2.1.4", + "@fluentui/merge-styles": "^8.1.4", + "@fluentui/set-version": "^8.1.4", "tslib": "^2.1.0" - }, - "dependencies": { - "tslib": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.3.0.tgz", - "integrity": "sha512-N82ooyxVNm6h1riLCoyS9e3fuJ3AMG2zIZs2Gd1ATcSFjSA23Q0fzjjZeh0jbJvWVDZ0cJT8yaNNaaXHzueNjg==" - } } }, "@istanbuljs/load-nyc-config": { @@ -1287,9 +1238,9 @@ } }, "@microsoft/load-themed-styles": { - "version": "1.10.196", - "resolved": "https://registry.npmjs.org/@microsoft/load-themed-styles/-/load-themed-styles-1.10.196.tgz", - "integrity": "sha512-eImlYYB/ZJH8Y3t2d/JbjxwSwWVeiQ7x+Xl5mTlTAlByzAiEs7tB3LUOEEz0XrlasvYV9ynq2i8b1+Gi/+KlMQ==" + "version": "1.10.197", + "resolved": "https://registry.npmjs.org/@microsoft/load-themed-styles/-/load-themed-styles-1.10.197.tgz", + "integrity": "sha512-w/RyqDpXR5unGjcFpzla6CE4L/r6AMbVkrxbK8Xq4Wl1GMfmK/5YWUz0wdCBHqCb+FuhQKni+ThGPti2pzI2mg==" }, "@nodelib/fs.scandir": { "version": "2.1.5", @@ -1335,6 +1286,11 @@ } } }, + "@opentelemetry/api": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/@opentelemetry/api/-/api-1.0.2.tgz", + "integrity": "sha512-DCF9oC89ao8/EJUqrp/beBlDR8Bp2R43jqtzayqCoomIvkwTuPfLcHdVhIGRR69GFlkykFjcDW+V92t0AS7Tww==" + }, "@redux-saga/core": { "version": "1.1.3", "resolved": "https://registry.npmjs.org/@redux-saga/core/-/core-1.1.3.tgz", @@ -1706,9 +1662,9 @@ } }, "@types/json-schema": { - "version": "7.0.8", - "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.8.tgz", - "integrity": "sha512-YSBPTLTVm2e2OoQIDYx8HaeWJ5tTToLH67kXR7zYNGupXMEHa2++G8k+DczX2cFVgalypqtyZIcU19AFcmOpmg==", + "version": "7.0.9", + "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.9.tgz", + "integrity": "sha512-qcUXuemtEu+E5wZSJHNxUXeCZhAfXKQ41D+duX+VYPde7xyEVZci+/oXKJL13tnRs9lR2pr4fod59GT6/X1/yQ==", "dev": true }, "@types/mime": { @@ -2200,29 +2156,6 @@ "integrity": "sha512-OPdCF6GsMIP+Az+aWfAAOEt2/+iVDKE7oy6lJ098aoe59oAmK76qV6Gw60SbZ8jHuG2wH058GF4pLFbYamYrVA==", "dev": true }, - "adal-node": { - "version": "0.1.28", - "resolved": "https://registry.npmjs.org/adal-node/-/adal-node-0.1.28.tgz", - "integrity": "sha1-RoxLs+u9lrEnBmn0ucuk4AZepIU=", - "requires": { - "@types/node": "^8.0.47", - "async": ">=0.6.0", - "date-utils": "*", - "jws": "3.x.x", - "request": ">= 2.52.0", - "underscore": ">= 1.3.1", - "uuid": "^3.1.0", - "xmldom": ">= 0.1.x", - "xpath.js": "~1.1.0" - }, - "dependencies": { - "@types/node": { - "version": "8.10.66", - "resolved": "https://registry.npmjs.org/@types/node/-/node-8.10.66.tgz", - "integrity": "sha512-tktOkFUA4kXx2hhhrB8bIFb5TbwzS4uOhKEmwiD+NoiL0qtP2OQ9mFldbgD4dV1djrlBYP6eBuQZiWjuHUpqFw==" - } - } - }, "agent-base": { "version": "6.0.2", "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-6.0.2.tgz", @@ -2632,12 +2565,9 @@ "dev": true }, "async": { - "version": "2.6.0", - "resolved": "https://registry.npmjs.org/async/-/async-2.6.0.tgz", - "integrity": "sha512-xAfGg1/NTLBBKlHFmnd7PlmUW9KhVQIUuSrYem9xzFUZy13ScvtyGGejaae9iAVRiRq9+Cx7DPFaAAhCpyxyPw==", - "requires": { - "lodash": "^4.14.0" - } + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/async/-/async-3.2.0.tgz", + "integrity": "sha512-TR2mEZFVOj2pLStYxLht7TyfuRzaydfpxr3k9RpHIzMgw7A64dzsdqCxH1WJyQdoe8T10nDXd9wnEigmiuHIZw==" }, "async-each": { "version": "1.0.3", @@ -2656,11 +2586,6 @@ "resolved": "https://registry.npmjs.org/async-limiter/-/async-limiter-1.0.1.tgz", "integrity": "sha512-csOlWGAcRFJaI6m+F2WKdnMKr4HhdhFVBk0H/QbJFMCr+uO2kwohwXQPxw/9OCxp05r5ghVBFSyioixx3gfkNQ==" }, - "async-lock": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/async-lock/-/async-lock-1.3.0.tgz", - "integrity": "sha512-8A7SkiisnEgME2zEedtDYPxUPzdv3x//E7n5IFktPAtMYSEAV7eNJF0rMwrVyUFj6d/8rgajLantbjcNRQYXIg==" - }, "asynckit": { "version": "0.4.0", "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", @@ -2678,6 +2603,11 @@ "integrity": "sha512-Wm6ukoaOGJi/73p/cl2GvLjTI5JM1k/O14isD73YML8StrH/7/lRFgmg8nICZgD3bZZvjwCGxtMOD3wWNAu8cg==", "dev": true }, + "available-typed-arrays": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/available-typed-arrays/-/available-typed-arrays-1.0.4.tgz", + "integrity": "sha512-SA5mXJWrId1TaQjfxUYghbqQ/hYioKmLJvPJyDuYRtXXenFNMjj4hSSt1Cf1xsuXSXrtxrVC5Ot4eU6cOtBDdA==" + }, "awesome-typescript-loader": { "version": "5.2.1", "resolved": "https://registry.npmjs.org/awesome-typescript-loader/-/awesome-typescript-loader-5.2.1.tgz", @@ -2880,6 +2810,14 @@ "ws": "^6.0.0" }, "dependencies": { + "async": { + "version": "2.6.3", + "resolved": "https://registry.npmjs.org/async/-/async-2.6.3.tgz", + "integrity": "sha512-zflvls11DCy+dQWzTW2dzuilv8Z5X/pjfmZOWba6TNIVDm+2UDaJmXSOXlasHKfNBs8oo3M0aT50fDEWfKZjXg==", + "requires": { + "lodash": "^4.17.14" + } + }, "azure-iot-common": { "version": "1.8.1", "resolved": "https://registry.npmjs.org/azure-iot-common/-/azure-iot-common-1.8.1.tgz", @@ -3201,8 +3139,7 @@ "base64-js": { "version": "1.5.1", "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz", - "integrity": "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==", - "dev": true + "integrity": "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==" }, "batch": { "version": "0.6.1", @@ -3242,6 +3179,16 @@ "integrity": "sha512-jDctJ/IVQbZoJykoeHbhXpOlNBqGNcwXJKJog42E5HDPUwQTSdjCHdihjj0DlnheQ7blbT6dHOafNAiS8ooQKA==", "dev": true }, + "bindings": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/bindings/-/bindings-1.5.0.tgz", + "integrity": "sha512-p2q/t/mhvuOj/UeLlV6566GD/guowlr0hHxClI0W9m7MWYkL1F0hLo+0Aexs9HSPCtR1SXQ0TD3MMKrXZajbiQ==", + "dev": true, + "optional": true, + "requires": { + "file-uri-to-path": "1.0.0" + } + }, "bluebird": { "version": "3.7.2", "resolved": "https://registry.npmjs.org/bluebird/-/bluebird-3.7.2.tgz", @@ -3496,12 +3443,6 @@ "safe-buffer": "^5.2.0" }, "dependencies": { - "inherits": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", - "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", - "dev": true - }, "readable-stream": { "version": "3.6.0", "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.0.tgz", @@ -3525,16 +3466,16 @@ } }, "browserslist": { - "version": "4.16.6", - "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.16.6.tgz", - "integrity": "sha512-Wspk/PqO+4W9qp5iUTJsa1B/QrYn1keNCcEP5OvP7WBwT4KaDly0uONYmC6Xa3Z5IqnUgS0KcgLYu1l74x0ZXQ==", + "version": "4.16.7", + "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.16.7.tgz", + "integrity": "sha512-7I4qVwqZltJ7j37wObBe3SoTz+nS8APaNcrBOlgoirb6/HbEU2XxW/LpUDTCngM6iauwFqmRTuOMfyKnFGY5JA==", "dev": true, "requires": { - "caniuse-lite": "^1.0.30001219", + "caniuse-lite": "^1.0.30001248", "colorette": "^1.2.2", - "electron-to-chromium": "^1.3.723", + "electron-to-chromium": "^1.3.793", "escalade": "^3.1.1", - "node-releases": "^1.1.71" + "node-releases": "^1.1.73" } }, "bs-logger": { @@ -3556,22 +3497,12 @@ } }, "buffer": { - "version": "4.9.2", - "resolved": "https://registry.npmjs.org/buffer/-/buffer-4.9.2.tgz", - "integrity": "sha512-xq+q3SRMOxGivLhBNaUdC64hDTQwejJ+H0T/NB1XMtTVEwNTrfFF3gAxiyW0Bu/xWEGhjVKgUcMhCrUy2+uCWg==", - "dev": true, + "version": "5.7.1", + "resolved": "https://registry.npmjs.org/buffer/-/buffer-5.7.1.tgz", + "integrity": "sha512-EHcyIPBQ4BSGlvjB16k5KgAJ27CIsHY/2JBmCRReo48y9rQ3MaUzWX3KVlBa4U7MyX02HdVj0K7C3WaB3ju7FQ==", "requires": { - "base64-js": "^1.0.2", - "ieee754": "^1.1.4", - "isarray": "^1.0.0" - }, - "dependencies": { - "isarray": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", - "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=", - "dev": true - } + "base64-js": "^1.3.1", + "ieee754": "^1.1.13" } }, "buffer-crc32": { @@ -3580,11 +3511,6 @@ "integrity": "sha1-DTM+PwDqxQqhRUq9MO+MKl2ackI=", "dev": true }, - "buffer-equal-constant-time": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/buffer-equal-constant-time/-/buffer-equal-constant-time-1.0.1.tgz", - "integrity": "sha1-+OcRMvf/5uAaXJaXpMbz5I1cyBk=" - }, "buffer-from": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.2.tgz", @@ -3800,7 +3726,6 @@ "version": "1.0.2", "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.2.tgz", "integrity": "sha512-7O+FbCihrB5WGbFYesctwmTKae6rOiIzmz1icreWJ+0aA7LJfuqhEso2T9ncpcFtzMQtzXf2QGGueWJGTYsqrA==", - "dev": true, "requires": { "function-bind": "^1.1.1", "get-intrinsic": "^1.0.2" @@ -3887,9 +3812,9 @@ "integrity": "sha1-G2gcIf+EAzyCZUMJBolCDRhxUdw=" }, "chalk": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.1.tgz", - "integrity": "sha512-diHzdDKxcU+bAsUboHLPEDQiw0qEe0qd7SYUn3HgcFlWgbDcfLGswOHYeGrHKzG9z6UYf01d9VFMfZxPM1xZSg==", + "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", @@ -3921,14 +3846,6 @@ "parse5": "^6.0.1", "parse5-htmlparser2-tree-adapter": "^6.0.1", "tslib": "^2.2.0" - }, - "dependencies": { - "tslib": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.3.0.tgz", - "integrity": "sha512-N82ooyxVNm6h1riLCoyS9e3fuJ3AMG2zIZs2Gd1ATcSFjSA23Q0fzjjZeh0jbJvWVDZ0cJT8yaNNaaXHzueNjg==", - "dev": true - } } }, "cheerio-select": { @@ -5233,11 +5150,6 @@ "resolved": "https://registry.npmjs.org/date-fns/-/date-fns-2.14.0.tgz", "integrity": "sha512-1zD+68jhFgDIM0rF05rcwYO8cExdNqxjq4xP1QKM60Q45mnO6zaMWB4tOzrIr4M4GSLntsKeE4c9Bdl2jhL/yw==" }, - "date-utils": { - "version": "1.2.21", - "resolved": "https://registry.npmjs.org/date-utils/-/date-utils-1.2.21.tgz", - "integrity": "sha1-YfsWzcEnSzyayq/+n8ad+HIKK2Q=" - }, "debug": { "version": "3.2.7", "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.7.tgz", @@ -5325,7 +5237,6 @@ "version": "1.1.3", "resolved": "https://registry.npmjs.org/define-properties/-/define-properties-1.1.3.tgz", "integrity": "sha512-3MqfYKj2lLzdMSf8ZIZE/V+Zuy+BgD6f164e8K2w7dgnpKArBDerGYpM46IYYcjnkdPNMjPk9A6VFB8+3SKlXQ==", - "dev": true, "requires": { "object-keys": "^1.0.12" } @@ -5707,7 +5618,8 @@ "duplexer": { "version": "0.1.2", "resolved": "https://registry.npmjs.org/duplexer/-/duplexer-0.1.2.tgz", - "integrity": "sha512-jtD6YG370ZCIi/9GTaJKQxWTZD045+4R4hTk/x1UyoqadyJ9x9CgSi1RlVDQF8U2sxLLSnFkCaMihqljHIWgMg==" + "integrity": "sha512-jtD6YG370ZCIi/9GTaJKQxWTZD045+4R4hTk/x1UyoqadyJ9x9CgSi1RlVDQF8U2sxLLSnFkCaMihqljHIWgMg==", + "dev": true }, "duplexer3": { "version": "0.1.4", @@ -5736,14 +5648,6 @@ "safer-buffer": "^2.1.0" } }, - "ecdsa-sig-formatter": { - "version": "1.0.11", - "resolved": "https://registry.npmjs.org/ecdsa-sig-formatter/-/ecdsa-sig-formatter-1.0.11.tgz", - "integrity": "sha512-nagl3RYrbNv6kQkeJIpt6NJZy8twLB/2vtz6yN9Z4vRKHN4/QZJIEbqohALSgwKdnksuY3k5Addp5lg8sVoVcQ==", - "requires": { - "safe-buffer": "^5.0.1" - } - }, "ee-first": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz", @@ -5770,9 +5674,9 @@ }, "dependencies": { "@types/node": { - "version": "12.20.17", - "resolved": "https://registry.npmjs.org/@types/node/-/node-12.20.17.tgz", - "integrity": "sha512-so8EHl4S6MmatPS0f9sE1ND94/ocbcEshW5OpyYthRqeRpiYyW2uXYTo/84kmfdfeNrDycARkvuiXl6nO40NGg==", + "version": "12.20.19", + "resolved": "https://registry.npmjs.org/@types/node/-/node-12.20.19.tgz", + "integrity": "sha512-niAuZrwrjKck4+XhoCw6AAVQBENHftpXw9F4ryk66fTgYaKQ53R4FI7c9vUGGw5vQis1HKBHDR1gcYI/Bq1xvw==", "dev": true } } @@ -6008,9 +5912,9 @@ } }, "electron-to-chromium": { - "version": "1.3.790", - "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.3.790.tgz", - "integrity": "sha512-epMH/S2MkhBv+Y0+nHK8dC7bzmOaPwcmiYqt+VwxSUJLgPzkqZnGUEQ8eVhy5zGmgWm9tDDdXkHDzOEsVU979A==", + "version": "1.3.795", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.3.795.tgz", + "integrity": "sha512-4TPxrLf9Fzsi4rVgTlDm+ubxoXm3/TN67/LGHx/a4UkVubKILa6L26O6eTnHewixG/knzU9L3lLmfL39eElwlQ==", "dev": true }, "electron-window-state": { @@ -6042,12 +5946,6 @@ "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.12.0.tgz", "integrity": "sha512-c98Bf3tPniI+scsdk237ku1Dc3ujXQTSgyiPUDEOe7tRkhrqridvh8klBv0HCEso1OLOYcHuCv/cS6DNxKH+ZA==", "dev": true - }, - "inherits": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", - "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", - "dev": true } } }, @@ -6222,10 +6120,9 @@ } }, "es-abstract": { - "version": "1.18.3", - "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.18.3.tgz", - "integrity": "sha512-nQIr12dxV7SSxE6r6f1l3DtAeEYdsGpps13dR0TwJg1S8gyp4ZPgy3FZcHBgbiQqnoqSTb+oC+kO4UQ0C/J8vw==", - "dev": true, + "version": "1.18.5", + "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.18.5.tgz", + "integrity": "sha512-DDggyJLoS91CkJjgauM5c0yZMjiD1uK3KcaCeAmffGwZ+ODWzOkPN4QwRbsK5DOFf06fywmyLci3ZD8jLGhVYA==", "requires": { "call-bind": "^1.0.2", "es-to-primitive": "^1.2.1", @@ -6233,11 +6130,12 @@ "get-intrinsic": "^1.1.1", "has": "^1.0.3", "has-symbols": "^1.0.2", + "internal-slot": "^1.0.3", "is-callable": "^1.2.3", "is-negative-zero": "^2.0.1", "is-regex": "^1.1.3", "is-string": "^1.0.6", - "object-inspect": "^1.10.3", + "object-inspect": "^1.11.0", "object-keys": "^1.1.1", "object.assign": "^4.1.2", "string.prototype.trimend": "^1.0.4", @@ -6255,7 +6153,6 @@ "version": "1.2.1", "resolved": "https://registry.npmjs.org/es-to-primitive/-/es-to-primitive-1.2.1.tgz", "integrity": "sha512-QCOllgZJtaUo9miYBcLChTUaHNjJF3PYs1VidD7AwiEj1kYxKeQTctLAezAOH5ZKRH0g2IgPn6KwB4IT8iRpvA==", - "dev": true, "requires": { "is-callable": "^1.1.4", "is-date-object": "^1.0.1", @@ -6396,8 +6293,7 @@ "events": { "version": "3.3.0", "resolved": "https://registry.npmjs.org/events/-/events-3.3.0.tgz", - "integrity": "sha512-mQw+2fkQbALzQ7V0MY0IqdnXNOeTtP4r0lN9z7AAawCXgqea7bDii20AYrIBrFd/Hx0M2Ocz6S111CaFkUcb0Q==", - "dev": true + "integrity": "sha512-mQw+2fkQbALzQ7V0MY0IqdnXNOeTtP4r0lN9z7AAawCXgqea7bDii20AYrIBrFd/Hx0M2Ocz6S111CaFkUcb0Q==" }, "eventsource": { "version": "1.1.0", @@ -6858,6 +6754,13 @@ } } }, + "file-uri-to-path": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/file-uri-to-path/-/file-uri-to-path-1.0.0.tgz", + "integrity": "sha512-0Zt+s3L7Vf1biwWZ29aARiVYLx7iMGnEUl9x33fbB/j3jR81u/O2LbqK+Bm1CDSNDKVtJ/YjwY7TUd5SkeLQLw==", + "dev": true, + "optional": true + }, "filelist": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/filelist/-/filelist-1.0.2.tgz", @@ -7081,6 +6984,11 @@ "integrity": "sha1-gQaNKVqBQuwKxybG4iAMMPttXoA=", "dev": true }, + "foreach": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/foreach/-/foreach-2.0.5.tgz", + "integrity": "sha1-C+4AUBiusmDQo6865ljdATbsG5k=" + }, "forever-agent": { "version": "0.6.1", "resolved": "https://registry.npmjs.org/forever-agent/-/forever-agent-0.6.1.tgz", @@ -7173,8 +7081,7 @@ "function-bind": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz", - "integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==", - "dev": true + "integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==" }, "function.prototype.name": { "version": "1.1.4", @@ -7210,7 +7117,6 @@ "version": "1.1.1", "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.1.1.tgz", "integrity": "sha512-kWZrnVM42QCiEA2Ig1bG8zjoIMOgxWwYCEeNdwY6Tv/cOSeGpcoX4pXHfKUxNKVoArnrEr2e9srnAxxGIraS9Q==", - "dev": true, "requires": { "function-bind": "^1.1.1", "has": "^1.0.3", @@ -7244,13 +7150,6 @@ "integrity": "sha512-U56CfOK17OKgTVqozZjUKNdkfEv6jk5WISBJ8SHoagjE6L69zOwl3Z+O8myjY9MEW3i2HPWQBt/LTbCgcC973Q==", "requires": { "async": "^3.2.0" - }, - "dependencies": { - "async": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/async/-/async-3.2.0.tgz", - "integrity": "sha512-TR2mEZFVOj2pLStYxLht7TyfuRzaydfpxr3k9RpHIzMgw7A64dzsdqCxH1WJyQdoe8T10nDXd9wnEigmiuHIZw==" - } } }, "getpass": { @@ -7301,9 +7200,9 @@ }, "dependencies": { "core-js": { - "version": "3.15.2", - "resolved": "https://registry.npmjs.org/core-js/-/core-js-3.15.2.tgz", - "integrity": "sha512-tKs41J7NJVuaya8DxIOCnl8QuPHx5/ZVbFo1oKgVl1qHFBBrDctzQGtuLjPpRdNTWmKPH6oEvgN/MUID+l485Q==", + "version": "3.16.0", + "resolved": "https://registry.npmjs.org/core-js/-/core-js-3.16.0.tgz", + "integrity": "sha512-5+5VxRFmSf97nM8Jr2wzOwLqRo6zphH2aX+7KsAUONObyzakDNq2G/bgbhinxB4PoV9L3aXQYhiDKyIKWd2c8g==", "dev": true, "optional": true }, @@ -7382,15 +7281,6 @@ "lodash": "^4.17.10", "npm-conf": "^1.1.3", "tunnel": "^0.0.6" - }, - "dependencies": { - "tunnel": { - "version": "0.0.6", - "resolved": "https://registry.npmjs.org/tunnel/-/tunnel-0.0.6.tgz", - "integrity": "sha512-1h/Lnq9yajKY2PEbBadPXj3VxsDDu844OnaAo52UVmIzIvwwtBPIuNvkjuzBlTWpfJyUbG3ez0KSBibQkj4ojg==", - "dev": true, - "optional": true - } } }, "globals": { @@ -7496,7 +7386,6 @@ "version": "1.0.3", "resolved": "https://registry.npmjs.org/has/-/has-1.0.3.tgz", "integrity": "sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==", - "dev": true, "requires": { "function-bind": "^1.1.1" } @@ -7521,8 +7410,7 @@ "has-bigints": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/has-bigints/-/has-bigints-1.0.1.tgz", - "integrity": "sha512-LSBS2LjbNBTf6287JEbEzvJgftkF5qFkmCo9hDRpAzKhUOlJ+hx8dd4USs00SgsUNwc4617J9ki5YtEClM2ffA==", - "dev": true + "integrity": "sha512-LSBS2LjbNBTf6287JEbEzvJgftkF5qFkmCo9hDRpAzKhUOlJ+hx8dd4USs00SgsUNwc4617J9ki5YtEClM2ffA==" }, "has-flag": { "version": "4.0.0", @@ -7533,8 +7421,7 @@ "has-symbols": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.2.tgz", - "integrity": "sha512-chXa79rL/UC2KlX17jo3vRGz0azaWEx5tGqZg5pO3NUyEJVB17dMruQlzCCOfUvElghKcm5194+BCRvi2Rv/Gw==", - "dev": true + "integrity": "sha512-chXa79rL/UC2KlX17jo3vRGz0azaWEx5tGqZg5pO3NUyEJVB17dMruQlzCCOfUvElghKcm5194+BCRvi2Rv/Gw==" }, "has-value": { "version": "1.0.0", @@ -7611,12 +7498,6 @@ "safe-buffer": "^5.2.0" }, "dependencies": { - "inherits": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", - "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", - "dev": true - }, "readable-stream": { "version": "3.6.0", "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.0.tgz", @@ -7871,6 +7752,13 @@ "inherits": "2.0.3", "setprototypeof": "1.1.0", "statuses": ">= 1.4.0 < 2" + }, + "dependencies": { + "inherits": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz", + "integrity": "sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4=" + } } }, "http-proxy": { @@ -8005,8 +7893,7 @@ "ieee754": { "version": "1.2.1", "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.2.1.tgz", - "integrity": "sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==", - "dev": true + "integrity": "sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==" }, "iferr": { "version": "0.1.5", @@ -8100,9 +7987,9 @@ } }, "inherits": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz", - "integrity": "sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4=" + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", + "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==" }, "ini": { "version": "1.3.8", @@ -8120,6 +8007,16 @@ "ipaddr.js": "^1.9.0" } }, + "internal-slot": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/internal-slot/-/internal-slot-1.0.3.tgz", + "integrity": "sha512-O0DB1JC/sPyZl7cIo78n5dR7eUSwwpYPiXRhTzNxZVAMUuB8vlnRFyLxdrVToks6XPLVnFfbzaVd5WLjhgg+vA==", + "requires": { + "get-intrinsic": "^1.1.0", + "has": "^1.0.3", + "side-channel": "^1.0.4" + } + }, "interpret": { "version": "1.4.0", "resolved": "https://registry.npmjs.org/interpret/-/interpret-1.4.0.tgz", @@ -8179,7 +8076,6 @@ "version": "1.1.0", "resolved": "https://registry.npmjs.org/is-arguments/-/is-arguments-1.1.0.tgz", "integrity": "sha512-1Ij4lOMPl/xB5kBDn7I+b2ttPMKa8szhEIrXDuXQD/oe3HJLTLhqhgGspwgyGd6MOywBUqVvYicF72lkgDnIHg==", - "dev": true, "requires": { "call-bind": "^1.0.0" } @@ -8193,8 +8089,7 @@ "is-bigint": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/is-bigint/-/is-bigint-1.0.2.tgz", - "integrity": "sha512-0JV5+SOCQkIdzjBK9buARcV804Ddu7A0Qet6sHi3FimE9ne6m4BGQZfRn+NZiXbBk4F4XmHfDZIipLj9pX8dSA==", - "dev": true + "integrity": "sha512-0JV5+SOCQkIdzjBK9buARcV804Ddu7A0Qet6sHi3FimE9ne6m4BGQZfRn+NZiXbBk4F4XmHfDZIipLj9pX8dSA==" }, "is-binary-path": { "version": "2.1.0", @@ -8209,21 +8104,19 @@ "version": "1.1.1", "resolved": "https://registry.npmjs.org/is-boolean-object/-/is-boolean-object-1.1.1.tgz", "integrity": "sha512-bXdQWkECBUIAcCkeH1unwJLIpZYaa5VvuygSyS/c2lf719mTKZDU5UdDRlpd01UjADgmW8RfqaP+mRaVPdr/Ng==", - "dev": true, "requires": { "call-bind": "^1.0.2" } }, "is-buffer": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/is-buffer/-/is-buffer-2.0.2.tgz", - "integrity": "sha512-imvkm8cOGKeZ/NwkAd+FAURi0hsL9gr3kvdi0r3MnqChcOdPaQRIOQiOU+sD40XzUIe6nFmSHYtQjbkDvaQbEg==" + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/is-buffer/-/is-buffer-2.0.5.tgz", + "integrity": "sha512-i2R6zNFDwgEHJyQUtJEk0XFi1i0dPFn/oqjK3/vPCcDeJvW5NQ83V8QbicfF1SupOaB0h8ntgBC2YiE7dfyctQ==" }, "is-callable": { "version": "1.2.3", "resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.2.3.tgz", - "integrity": "sha512-J1DcMe8UYTBSrKezuIUTUwjXsho29693unXM2YhJUTR2txK/eG47bvNa/wipPFmZFgr/N6f1GA66dv0mEyTIyQ==", - "dev": true + "integrity": "sha512-J1DcMe8UYTBSrKezuIUTUwjXsho29693unXM2YhJUTR2txK/eG47bvNa/wipPFmZFgr/N6f1GA66dv0mEyTIyQ==" }, "is-ci": { "version": "2.0.0", @@ -8286,8 +8179,7 @@ "is-date-object": { "version": "1.0.4", "resolved": "https://registry.npmjs.org/is-date-object/-/is-date-object-1.0.4.tgz", - "integrity": "sha512-/b4ZVsG7Z5XVtIxs/h9W8nvfLgSAyKYdtGWQLbqy6jA1icmgjf8WCoTKgeS4wy5tYaPePouzFMANbnj94c2Z+A==", - "dev": true + "integrity": "sha512-/b4ZVsG7Z5XVtIxs/h9W8nvfLgSAyKYdtGWQLbqy6jA1icmgjf8WCoTKgeS4wy5tYaPePouzFMANbnj94c2Z+A==" }, "is-descriptor": { "version": "0.1.6", @@ -8345,6 +8237,11 @@ "integrity": "sha512-cTIB4yPYL/Grw0EaSzASzg6bBy9gqCofvWN8okThAYIxKJZC+udlRAmGbM0XLeniEJSs8uEgHPGuHSe1XsOLSQ==", "dev": true }, + "is-generator-function": { + "version": "1.0.9", + "resolved": "https://registry.npmjs.org/is-generator-function/-/is-generator-function-1.0.9.tgz", + "integrity": "sha512-ZJ34p1uvIfptHCN7sFTjGibB9/oBg17sHqzDLfuwhvmN/qLVvIQXRQ8licZQ35WJ8KuEQt/etnnzQFI9C9Ue/A==" + }, "is-glob": { "version": "4.0.1", "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.1.tgz", @@ -8367,8 +8264,7 @@ "is-negative-zero": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/is-negative-zero/-/is-negative-zero-2.0.1.tgz", - "integrity": "sha512-2z6JzQvZRa9A2Y7xC6dQQm4FSTSTNWjKIYYTt4246eMTJmIo0Q+ZyOsU66X8lxK1AbB92dFeglPLrhwpeRKO6w==", - "dev": true + "integrity": "sha512-2z6JzQvZRa9A2Y7xC6dQQm4FSTSTNWjKIYYTt4246eMTJmIo0Q+ZyOsU66X8lxK1AbB92dFeglPLrhwpeRKO6w==" }, "is-npm": { "version": "4.0.0", @@ -8385,8 +8281,7 @@ "is-number-object": { "version": "1.0.5", "resolved": "https://registry.npmjs.org/is-number-object/-/is-number-object-1.0.5.tgz", - "integrity": "sha512-RU0lI/n95pMoUKu9v1BZP5MBcZuNSVJkMkAG2dJqC4z2GlkGUNeH68SuHuBKBD/XFe+LHZ+f9BKkLET60Niedw==", - "dev": true + "integrity": "sha512-RU0lI/n95pMoUKu9v1BZP5MBcZuNSVJkMkAG2dJqC4z2GlkGUNeH68SuHuBKBD/XFe+LHZ+f9BKkLET60Niedw==" }, "is-obj": { "version": "2.0.0", @@ -8451,7 +8346,6 @@ "version": "1.1.3", "resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.1.3.tgz", "integrity": "sha512-qSVXFz28HM7y+IWX6vLCsexdlvzT1PJNFSBuaQLQ5o0IEw8UDYW6/2+eCMVyIsbM8CNLX2a/QWmSpyxYEHY7CQ==", - "dev": true, "requires": { "call-bind": "^1.0.2", "has-symbols": "^1.0.2" @@ -8466,13 +8360,13 @@ "is-stream": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-1.1.0.tgz", - "integrity": "sha1-EtSj3U5o4Lec6428hBc66A2RykQ=" + "integrity": "sha1-EtSj3U5o4Lec6428hBc66A2RykQ=", + "dev": true }, "is-string": { "version": "1.0.6", "resolved": "https://registry.npmjs.org/is-string/-/is-string-1.0.6.tgz", - "integrity": "sha512-2gdzbKUuqtQ3lYNrUTQYoClPhm7oQu4UdpSZMp1/DGgkHBT8E2Z1l0yMdb6D4zNAxwDiMv8MdulKROJGNl0Q0w==", - "dev": true + "integrity": "sha512-2gdzbKUuqtQ3lYNrUTQYoClPhm7oQu4UdpSZMp1/DGgkHBT8E2Z1l0yMdb6D4zNAxwDiMv8MdulKROJGNl0Q0w==" }, "is-subset": { "version": "0.1.1", @@ -8493,11 +8387,22 @@ "version": "1.0.4", "resolved": "https://registry.npmjs.org/is-symbol/-/is-symbol-1.0.4.tgz", "integrity": "sha512-C/CPBqKWnvdcxqIARxyOh4v1UUEOCHpgDa0WYgpKDFMszcrPcffg5uhwSgPCLD2WWxmq6isisz87tzT01tuGhg==", - "dev": true, "requires": { "has-symbols": "^1.0.2" } }, + "is-typed-array": { + "version": "1.1.5", + "resolved": "https://registry.npmjs.org/is-typed-array/-/is-typed-array-1.1.5.tgz", + "integrity": "sha512-S+GRDgJlR3PyEbsX/Fobd9cqpZBuvUS+8asRqYDMLCb2qMzt1oz5m5oxQCxOgUDxiWsOVNi4yaF+/uvdlHlYug==", + "requires": { + "available-typed-arrays": "^1.0.2", + "call-bind": "^1.0.2", + "es-abstract": "^1.18.0-next.2", + "foreach": "^2.0.5", + "has-symbols": "^1.0.1" + } + }, "is-typedarray": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/is-typedarray/-/is-typedarray-1.0.0.tgz", @@ -10187,9 +10092,9 @@ "integrity": "sha1-peZUwuWi3rXyAdls77yoDA7y9RM=" }, "jsdom": { - "version": "16.6.0", - "resolved": "https://registry.npmjs.org/jsdom/-/jsdom-16.6.0.tgz", - "integrity": "sha512-Ty1vmF4NHJkolaEmdjtxTfSfkdb8Ywarwf63f+F8/mDD1uLSSWDxDuMiZxiPhwunLrn9LOSVItWj4bLYsLN3Dg==", + "version": "16.7.0", + "resolved": "https://registry.npmjs.org/jsdom/-/jsdom-16.7.0.tgz", + "integrity": "sha512-u9Smc2G1USStM+s/x1ru5Sxrl6mPYCbByG1U/hUmqaVsm4tbNyS7CicOSRyuGQYZhTu0h84qkZZQ/I+dzizSVw==", "dev": true, "requires": { "abab": "^2.0.5", @@ -10217,7 +10122,7 @@ "whatwg-encoding": "^1.0.5", "whatwg-mimetype": "^2.3.0", "whatwg-url": "^8.5.0", - "ws": "^7.4.5", + "ws": "^7.4.6", "xml-name-validator": "^3.0.0" }, "dependencies": { @@ -10232,6 +10137,12 @@ "mime-types": "^2.1.12" } }, + "punycode": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.1.1.tgz", + "integrity": "sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A==", + "dev": true + }, "tough-cookie": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-4.0.0.tgz", @@ -10330,28 +10241,9 @@ } }, "jssha": { - "version": "2.4.2", - "resolved": "https://registry.npmjs.org/jssha/-/jssha-2.4.2.tgz", - "integrity": "sha512-/jsi/9C0S70zfkT/4UlKQa5E1xKurDnXcQizcww9JSR/Fv+uIbWM2btG+bFcL3iNoK9jIGS0ls9HWLr1iw0kFg==" - }, - "jwa": { - "version": "1.4.1", - "resolved": "https://registry.npmjs.org/jwa/-/jwa-1.4.1.tgz", - "integrity": "sha512-qiLX/xhEEFKUAJ6FiBMbes3w9ATzyk5W7Hvzpa/SLYdxNtng+gcurvrI7TbACjIXlsJyr05/S1oUhZrc63evQA==", - "requires": { - "buffer-equal-constant-time": "1.0.1", - "ecdsa-sig-formatter": "1.0.11", - "safe-buffer": "^5.0.1" - } - }, - "jws": { - "version": "3.2.2", - "resolved": "https://registry.npmjs.org/jws/-/jws-3.2.2.tgz", - "integrity": "sha512-YHlZCB6lMTllWDtSPHz/ZXTsi8S00usEV6v1tjq8tOUZzw7DpSDWVXjXDre6ed1w/pd495ODpHZYSdkRTsa0HA==", - "requires": { - "jwa": "^1.4.1", - "safe-buffer": "^5.0.1" - } + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/jssha/-/jssha-3.2.0.tgz", + "integrity": "sha512-QuruyBENDWdN4tZwJbQq7/eAK85FqrI4oDbXjy5IBhYD+2pTJyBUWZe8ctWaCkrV0gy6AaelgOZZBMeswEa/6Q==" }, "keyv": { "version": "3.1.0", @@ -10976,11 +10868,6 @@ "minimist": "^1.2.5" } }, - "moment": { - "version": "2.29.1", - "resolved": "https://registry.npmjs.org/moment/-/moment-2.29.1.tgz", - "integrity": "sha512-kHmoybcPV8Sqy59DwNDY3Jefr64lK/by/da0ViFcuA4DH0vQg5Q6Ze5VimxkfQNSC+Mls/Kx53s7TjP1RhFEDQ==" - }, "moo": { "version": "0.5.1", "resolved": "https://registry.npmjs.org/moo/-/moo-0.5.1.tgz", @@ -11017,47 +10904,19 @@ "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==" }, - "ms-rest": { - "version": "2.5.4", - "resolved": "https://registry.npmjs.org/ms-rest/-/ms-rest-2.5.4.tgz", - "integrity": "sha512-VeqCbawxRM6nhw0RKNfj7TWL7SL8PB6MypqwgylXCi+u412uvYoyY/kSmO8n06wyd8nIcnTbYToCmSKFMI1mCg==", - "requires": { - "duplexer": "^0.1.1", - "is-buffer": "^1.1.6", - "is-stream": "^1.1.0", - "moment": "^2.21.0", - "request": "^2.88.0", - "through": "^2.3.8", - "tunnel": "0.0.5", - "uuid": "^3.2.1" - }, - "dependencies": { - "is-buffer": { - "version": "1.1.6", - "resolved": "https://registry.npmjs.org/is-buffer/-/is-buffer-1.1.6.tgz", - "integrity": "sha512-NcdALwpXkTm5Zvvbk7owOUSvVvBKDgKP5/ewfXEznmQFfs4ZRmanOeKBTjRVjka3QFoN6XJ+9F3USqfHqTaU5w==" - } - } - }, - "ms-rest-azure": { - "version": "2.6.0", - "resolved": "https://registry.npmjs.org/ms-rest-azure/-/ms-rest-azure-2.6.0.tgz", - "integrity": "sha512-J6386a9krZ4VtU7CRt+Ypgo9RGf8+d3gjMBkH7zbkM4zzkhbbMOYiPRaZ+bHZcfihkKLlktTgA6rjshTjF329A==", - "requires": { - "adal-node": "^0.1.28", - "async": "2.6.0", - "moment": "^2.22.2", - "ms-rest": "^2.3.2", - "request": "^2.88.0", - "uuid": "^3.2.1" - } - }, "msal": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/msal/-/msal-1.2.0.tgz", "integrity": "sha512-Z74fTT8G1s1YKcqup6PTQ4Y4z0zh5n7fdK+RXZ47+yLH4/z1Y5LualkRuHr6C7vi74l0daNZn2nj159AV5mhOQ==", "requires": { "tslib": "^1.9.3" + }, + "dependencies": { + "tslib": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", + "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==" + } } }, "multicast-dns": { @@ -11076,6 +10935,13 @@ "integrity": "sha1-iZ8R2WhuXgXLkbNdXw5jt3PPyQE=", "dev": true }, + "nan": { + "version": "2.15.0", + "resolved": "https://registry.npmjs.org/nan/-/nan-2.15.0.tgz", + "integrity": "sha512-8ZtvEnA2c5aYCZYd1cvgdnU6cqwixRoYg70xPLWUws5ORTa/lnw+u4amixRS/Ac5U5mQVgp9pnlSUnbNWFaWZQ==", + "dev": true, + "optional": true + }, "nanoid": { "version": "2.1.11", "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-2.1.11.tgz", @@ -11193,11 +11059,37 @@ "vm-browserify": "^1.0.1" }, "dependencies": { - "punycode": { - "version": "1.4.1", - "resolved": "https://registry.npmjs.org/punycode/-/punycode-1.4.1.tgz", - "integrity": "sha1-wNWmOycYgArY4esPpSachN1BhF4=", + "buffer": { + "version": "4.9.2", + "resolved": "https://registry.npmjs.org/buffer/-/buffer-4.9.2.tgz", + "integrity": "sha512-xq+q3SRMOxGivLhBNaUdC64hDTQwejJ+H0T/NB1XMtTVEwNTrfFF3gAxiyW0Bu/xWEGhjVKgUcMhCrUy2+uCWg==", + "dev": true, + "requires": { + "base64-js": "^1.0.2", + "ieee754": "^1.1.4", + "isarray": "^1.0.0" + } + }, + "inherits": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz", + "integrity": "sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4=", + "dev": true + }, + "isarray": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", + "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=", "dev": true + }, + "util": { + "version": "0.11.1", + "resolved": "https://registry.npmjs.org/util/-/util-0.11.1.tgz", + "integrity": "sha512-HShAsny+zS2TZfaXxD9tYj4HQGlBezXZMZuM/S5PKLLoZkShZiGk9o5CzukI1LVHZvjdvZ2Sj1aW/Ndn2NB/HQ==", + "dev": true, + "requires": { + "inherits": "2.0.3" + } } } }, @@ -11424,8 +11316,7 @@ "object-inspect": { "version": "1.11.0", "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.11.0.tgz", - "integrity": "sha512-jp7ikS6Sd3GxQfZJPyH3cjcbJF6GZPClgdV+EFygjFLQ5FmW/dRUnTd9PQ9k0JhoNDabWFbpF1yCdSWCC6gexg==", - "dev": true + "integrity": "sha512-jp7ikS6Sd3GxQfZJPyH3cjcbJF6GZPClgdV+EFygjFLQ5FmW/dRUnTd9PQ9k0JhoNDabWFbpF1yCdSWCC6gexg==" }, "object-is": { "version": "1.1.5", @@ -11440,8 +11331,7 @@ "object-keys": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/object-keys/-/object-keys-1.1.1.tgz", - "integrity": "sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA==", - "dev": true + "integrity": "sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA==" }, "object-visit": { "version": "1.0.1", @@ -11456,7 +11346,6 @@ "version": "4.1.2", "resolved": "https://registry.npmjs.org/object.assign/-/object.assign-4.1.2.tgz", "integrity": "sha512-ixT2L5THXsApyiUPYKmW+2EHpXXe5Ii3M+f4e+aJFAHao5amFRW6J0OO6c/LU8Be47utCx2GL89hxGB6XSmKuQ==", - "dev": true, "requires": { "call-bind": "^1.0.0", "define-properties": "^1.1.3", @@ -14458,8 +14347,7 @@ "process": { "version": "0.11.10", "resolved": "https://registry.npmjs.org/process/-/process-0.11.10.tgz", - "integrity": "sha1-czIwDoQBYb2j5podHZGn1LwW8YI=", - "dev": true + "integrity": "sha1-czIwDoQBYb2j5podHZGn1LwW8YI=" }, "process-nextick-args": { "version": "2.0.1", @@ -14599,9 +14487,9 @@ } }, "punycode": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.1.1.tgz", - "integrity": "sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A==" + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/punycode/-/punycode-1.3.2.tgz", + "integrity": "sha1-llOgNvt8HuQjQvIyXM7v6jkmxI0=" }, "pupa": { "version": "2.1.1", @@ -14636,8 +14524,7 @@ "querystring": { "version": "0.2.0", "resolved": "https://registry.npmjs.org/querystring/-/querystring-0.2.0.tgz", - "integrity": "sha1-sgmEkgO7Jd+CDadW50cAWHhSFiA=", - "dev": true + "integrity": "sha1-sgmEkgO7Jd+CDadW50cAWHhSFiA=" }, "querystring-es3": { "version": "0.2.1", @@ -15033,9 +14920,9 @@ } }, "redux": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/redux/-/redux-4.1.0.tgz", - "integrity": "sha512-uI2dQN43zqLWCt6B/BMGRMY6db7TTY4qeHHfGeKb3EOhmOKjU3KdWvNLJyqaHRksv/ErdNH7cFZWg9jXtewy4g==", + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/redux/-/redux-4.1.1.tgz", + "integrity": "sha512-hZQZdDEM25UY2P493kPYuKqviVwZ58lEmGQNeQ+gXa+U0gYPUBf7NKYazbe3m+bs/DzM/ahN12DbF+NG8i0CWw==", "requires": { "@babel/runtime": "^7.9.2" } @@ -15295,21 +15182,21 @@ "dev": true }, "rhea": { - "version": "1.0.24", - "resolved": "https://registry.npmjs.org/rhea/-/rhea-1.0.24.tgz", - "integrity": "sha512-PEl62U2EhxCO5wMUZ2/bCBcXAVKN9AdMSNQOrp3+R5b77TEaOSiy16MQ0sIOmzj/iqsgIAgPs1mt3FYfu1vIXA==", + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/rhea/-/rhea-2.0.4.tgz", + "integrity": "sha512-GpGqJV6dPG+bh88ZvlkzcaqKM3bW6+YDHSegSjdPkoCi3UIsWN6yu6i4LMhpojNg1U5UOGqshCoMMNO5Hkoeog==", "requires": { "debug": "0.8.0 - 3.5.0" } }, "rhea-promise": { - "version": "0.1.15", - "resolved": "https://registry.npmjs.org/rhea-promise/-/rhea-promise-0.1.15.tgz", - "integrity": "sha512-+6uilZXSJGyiqVeHQI3Krv6NTAd8cWRCY2uyCxmzR4/5IFtBqqFem1HV2OiwSj0Gu7OFChIJDfH2JyjN7J0vRA==", + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/rhea-promise/-/rhea-promise-2.1.0.tgz", + "integrity": "sha512-CRMwdJ/o4oO/xKcvAwAsd0AHy5fVvSlqso7AadRmaaLGzAzc9LCoW7FOFnucI8THasVmOeCnv5c/fH/n7FcNaA==", "requires": { "debug": "^3.1.0", - "rhea": "^1.0.4", - "tslib": "^1.9.3" + "rhea": "^2.0.3", + "tslib": "^2.2.0" } }, "rimraf": { @@ -15387,6 +15274,14 @@ "dev": true, "requires": { "tslib": "^1.9.0" + }, + "dependencies": { + "tslib": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", + "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==", + "dev": true + } } }, "safe-buffer": { @@ -15862,6 +15757,16 @@ "nanoid": "^2.1.0" } }, + "side-channel": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.0.4.tgz", + "integrity": "sha512-q5XPytqFEIKHkGdiMIrY10mvLRvnQh42/+GoBlFW3b2LXLE2xxJpZFdm94we0BaoV3RwJyGqg5wS7epxTv0Zvw==", + "requires": { + "call-bind": "^1.0.0", + "get-intrinsic": "^1.0.2", + "object-inspect": "^1.9.0" + } + }, "signal-exit": { "version": "3.0.3", "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.3.tgz", @@ -16117,6 +16022,17 @@ "requires": { "async": "^2.5.0", "loader-utils": "^1.1.0" + }, + "dependencies": { + "async": { + "version": "2.6.3", + "resolved": "https://registry.npmjs.org/async/-/async-2.6.3.tgz", + "integrity": "sha512-zflvls11DCy+dQWzTW2dzuilv8Z5X/pjfmZOWba6TNIVDm+2UDaJmXSOXlasHKfNBs8oo3M0aT50fDEWfKZjXg==", + "dev": true, + "requires": { + "lodash": "^4.17.14" + } + } } }, "source-map-resolve": { @@ -16446,7 +16362,6 @@ "version": "1.0.4", "resolved": "https://registry.npmjs.org/string.prototype.trimend/-/string.prototype.trimend-1.0.4.tgz", "integrity": "sha512-y9xCjw1P23Awk8EvTpcyL2NIr1j7wJ39f+k6lvRnSMz+mz9CGz9NYPelDk42kOz6+ql8xjfK8oYzy3jAP5QU5A==", - "dev": true, "requires": { "call-bind": "^1.0.2", "define-properties": "^1.1.3" @@ -16456,7 +16371,6 @@ "version": "1.0.4", "resolved": "https://registry.npmjs.org/string.prototype.trimstart/-/string.prototype.trimstart-1.0.4.tgz", "integrity": "sha512-jh6e984OBfvxS50tdY2nRZnoC5/mLFKOREQfw8t5yytkoUsJRNxvI/E39qu1sD0OtWI3OC0XgKSmcWwziwYuZw==", - "dev": true, "requires": { "call-bind": "^1.0.2", "define-properties": "^1.1.3" @@ -16831,9 +16745,9 @@ "dev": true }, "tar": { - "version": "6.1.2", - "resolved": "https://registry.npmjs.org/tar/-/tar-6.1.2.tgz", - "integrity": "sha512-EwKEgqJ7nJoS+s8QfLYVGMDmAsj+StbI2AM/RTHeUSsOw6Z8bwNBRv5z3CY0m7laC5qUAqruLX5AhMuc5deY3Q==", + "version": "6.1.6", + "resolved": "https://registry.npmjs.org/tar/-/tar-6.1.6.tgz", + "integrity": "sha512-oaWyu5dQbHaYcyZCTfyPpC+VmI62/OM2RTUYavTk1MDr1cwW5Boi3baeYQKiZbY2uSQJGr+iMOzb/JFxLrft+g==", "dev": true, "requires": { "chownr": "^2.0.0", @@ -17075,11 +16989,6 @@ "integrity": "sha512-fcwX4mndzpLQKBS1DVYhGAcYaYt7vsHNIvQV+WXMvnow5cgjPphq5CaayLaGsjRdSCKZFNGt7/GYAuXaNOiYCA==", "dev": true }, - "through": { - "version": "2.3.8", - "resolved": "https://registry.npmjs.org/through/-/through-2.3.8.tgz", - "integrity": "sha1-DdTJ/6q8NXlgsbckEV1+Doai4fU=" - }, "through2": { "version": "2.0.5", "resolved": "https://registry.npmjs.org/through2/-/through2-2.0.5.tgz", @@ -17236,6 +17145,14 @@ "dev": true, "requires": { "punycode": "^2.1.1" + }, + "dependencies": { + "punycode": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.1.1.tgz", + "integrity": "sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A==", + "dev": true + } } }, "tree-kill": { @@ -17389,9 +17306,9 @@ } }, "tslib": { - "version": "1.14.1", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", - "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==" + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.3.0.tgz", + "integrity": "sha512-N82ooyxVNm6h1riLCoyS9e3fuJ3AMG2zIZs2Gd1ATcSFjSA23Q0fzjjZeh0jbJvWVDZ0cJT8yaNNaaXHzueNjg==" }, "tslint": { "version": "5.11.0", @@ -17468,6 +17385,12 @@ "requires": { "has-flag": "^3.0.0" } + }, + "tslib": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", + "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==", + "dev": true } } }, @@ -17526,6 +17449,14 @@ "dev": true, "requires": { "tslib": "^1.8.1" + }, + "dependencies": { + "tslib": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", + "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==", + "dev": true + } } }, "tty-browserify": { @@ -17535,9 +17466,11 @@ "dev": true }, "tunnel": { - "version": "0.0.5", - "resolved": "https://registry.npmjs.org/tunnel/-/tunnel-0.0.5.tgz", - "integrity": "sha512-gj5sdqherx4VZKMcBA4vewER7zdK25Td+z1npBqpbDys4eJrLx+SlYjJvq1bDXs2irkuJM5pf8ktaEQVipkrbA==" + "version": "0.0.6", + "resolved": "https://registry.npmjs.org/tunnel/-/tunnel-0.0.6.tgz", + "integrity": "sha512-1h/Lnq9yajKY2PEbBadPXj3VxsDDu844OnaAo52UVmIzIvwwtBPIuNvkjuzBlTWpfJyUbG3ez0KSBibQkj4ojg==", + "dev": true, + "optional": true }, "tunnel-agent": { "version": "0.6.0", @@ -17663,7 +17596,6 @@ "version": "1.0.1", "resolved": "https://registry.npmjs.org/unbox-primitive/-/unbox-primitive-1.0.1.tgz", "integrity": "sha512-tZU/3NqK3dA5gpE1KtyiJUrEB0lxnGkMFHptJ7q6ewdZ8s12QrODwNbhIJStmJkd1QDXa1NRA8aF2A1zk/Ypyw==", - "dev": true, "requires": { "function-bind": "^1.1.1", "has-bigints": "^1.0.1", @@ -17697,11 +17629,6 @@ } } }, - "underscore": { - "version": "1.13.1", - "resolved": "https://registry.npmjs.org/underscore/-/underscore-1.13.1.tgz", - "integrity": "sha512-hzSoAVtJF+3ZtiFX0VgfFPHEDRm7Y/QPjGyNo4TVdnDTdft3tr8hEkD25a1jC+TjTuE7tkHGKkhwCgs9dgBB2g==" - }, "union-value": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/union-value/-/union-value-1.0.1.tgz", @@ -17867,6 +17794,13 @@ "integrity": "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==", "requires": { "punycode": "^2.1.0" + }, + "dependencies": { + "punycode": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.1.1.tgz", + "integrity": "sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A==" + } } }, "urix": { @@ -17879,18 +17813,9 @@ "version": "0.11.0", "resolved": "https://registry.npmjs.org/url/-/url-0.11.0.tgz", "integrity": "sha1-ODjpfPxgUh63PFJajlW/3Z4uKPE=", - "dev": true, "requires": { "punycode": "1.3.2", "querystring": "0.2.0" - }, - "dependencies": { - "punycode": { - "version": "1.3.2", - "resolved": "https://registry.npmjs.org/punycode/-/punycode-1.3.2.tgz", - "integrity": "sha1-llOgNvt8HuQjQvIyXM7v6jkmxI0=", - "dev": true - } } }, "url-parse": { @@ -17925,12 +17850,16 @@ "dev": true }, "util": { - "version": "0.11.1", - "resolved": "https://registry.npmjs.org/util/-/util-0.11.1.tgz", - "integrity": "sha512-HShAsny+zS2TZfaXxD9tYj4HQGlBezXZMZuM/S5PKLLoZkShZiGk9o5CzukI1LVHZvjdvZ2Sj1aW/Ndn2NB/HQ==", - "dev": true, + "version": "0.12.4", + "resolved": "https://registry.npmjs.org/util/-/util-0.12.4.tgz", + "integrity": "sha512-bxZ9qtSlGUWSOy9Qa9Xgk11kSslpuZwaxCg4sNIDj6FLucDab2JxnHwyNTCpHMtK1MjoQiWQ6DiUMZYbSrO+Sw==", "requires": { - "inherits": "2.0.3" + "inherits": "^2.0.3", + "is-arguments": "^1.0.4", + "is-generator-function": "^1.0.7", + "is-typed-array": "^1.1.3", + "safe-buffer": "^5.1.2", + "which-typed-array": "^1.1.2" } }, "util-deprecate": { @@ -18198,7 +18127,11 @@ "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-1.2.13.tgz", "integrity": "sha512-oWb1Z6mkHIskLzEJ/XWX0srkpkTQ7vaopMQkyaEIoq0fmtFVxOthb8cCxeT+p3ynTdkk/RZwbgG4brR5BeWECw==", "dev": true, - "optional": true + "optional": true, + "requires": { + "bindings": "^1.5.0", + "nan": "^2.12.1" + } }, "glob-parent": { "version": "3.1.0", @@ -19044,7 +18977,11 @@ "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-1.2.13.tgz", "integrity": "sha512-oWb1Z6mkHIskLzEJ/XWX0srkpkTQ7vaopMQkyaEIoq0fmtFVxOthb8cCxeT+p3ynTdkk/RZwbgG4brR5BeWECw==", "dev": true, - "optional": true + "optional": true, + "requires": { + "bindings": "^1.5.0", + "nan": "^2.12.1" + } }, "glob-parent": { "version": "3.1.0", @@ -19084,6 +19021,14 @@ "setprototypeof": "1.1.1", "statuses": ">= 1.5.0 < 2", "toidentifier": "1.0.0" + }, + "dependencies": { + "inherits": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz", + "integrity": "sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4=", + "dev": true + } } }, "http-proxy-middleware": { @@ -19519,7 +19464,6 @@ "version": "1.0.2", "resolved": "https://registry.npmjs.org/which-boxed-primitive/-/which-boxed-primitive-1.0.2.tgz", "integrity": "sha512-bwZdv0AKLpplFY2KZRX6TvyuN7ojjr7lwkg6ml0roIy9YeuSr7JS372qlNW18UQYzgYK9ziGcerWqZOmEn9VNg==", - "dev": true, "requires": { "is-bigint": "^1.0.1", "is-boolean-object": "^1.1.0", @@ -19534,6 +19478,20 @@ "integrity": "sha1-2e8H3Od7mQK4o6j6SzHD4/fm6Ho=", "dev": true }, + "which-typed-array": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/which-typed-array/-/which-typed-array-1.1.4.tgz", + "integrity": "sha512-49E0SpUe90cjpoc7BOJwyPHRqSAd12c10Qm2amdEZrJPCY2NDxaW01zHITrem+rnETY3dwrbH3UUrUwagfCYDA==", + "requires": { + "available-typed-arrays": "^1.0.2", + "call-bind": "^1.0.0", + "es-abstract": "^1.18.0-next.1", + "foreach": "^2.0.5", + "function-bind": "^1.1.1", + "has-symbols": "^1.0.1", + "is-typed-array": "^1.1.3" + } + }, "widest-line": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/widest-line/-/widest-line-3.1.0.tgz", @@ -19679,16 +19637,6 @@ "integrity": "sha512-JZnDKK8B0RCDw84FNdDAIpZK+JuJw+s7Lz8nksI7SIuU3UXJJslUthsi+uWBUYOwPFwW7W7PRLRfUKpxjtjFCw==", "dev": true }, - "xmldom": { - "version": "0.6.0", - "resolved": "https://registry.npmjs.org/xmldom/-/xmldom-0.6.0.tgz", - "integrity": "sha512-iAcin401y58LckRZ0TkI4k0VSM1Qg0KGSc3i8rU+xrxe19A/BN1zHyVSJY7uoutVlaTSzYyk/v5AmkewAP7jtg==" - }, - "xpath.js": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/xpath.js/-/xpath.js-1.1.0.tgz", - "integrity": "sha512-jg+qkfS4K8E7965sqaUl8mRngXiKb3WZGfONgE18pr03FUQiuSV6G+Ej4tS55B+rIQSFEIw3phdVAQ4pPqNWfQ==" - }, "xtend": { "version": "4.0.2", "resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.2.tgz", diff --git a/package.json b/package.json index d249d3642..50fec7791 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "azure-iot-explorer", - "version": "0.14.5", + "version": "0.14.6", "description": "This project welcomes contributions and suggestions. Most contributions require you to agree to a\r Contributor License Agreement (CLA) declaring that you have the right to, and actually do, grant us\r the rights to use your contribution. For details, visit https://cla.microsoft.com.", "main": "host/electron.js", "build": { @@ -69,7 +69,7 @@ }, "homepage": "https://github.com/Azure/azure-iot-explorer#readme", "dependencies": { - "@azure/event-hubs": "1.0.7", + "@azure/event-hubs": "5.6.0", "@fluentui/react": "8.20.2", "azure-iot-common": "1.10.3", "azure-iothub": "1.8.1", diff --git a/public/constants.ts b/public/constants.ts index f9307a6e8..2a5ef7039 100644 --- a/public/constants.ts +++ b/public/constants.ts @@ -10,7 +10,6 @@ export const MESSAGE_CHANNELS = { DEVICE_SEND_MESSAGE: 'device_sendMessage', DIRECTORY_GET_DIRECTORIES: 'directory_getDirectories', EVENTHUB_START_MONITORING: 'eventhub_startMonitoring', - EVENTHUB_STOP_MONITORING: 'eventhub_stopMonitoring', MODEL_REPOSITORY_GET_DEFINITION: 'model_definition', SETTING_HIGH_CONTRAST: 'setting_highContrast', }; diff --git a/public/electron.ts b/public/electron.ts index 106a65425..cc534e274 100644 --- a/public/electron.ts +++ b/public/electron.ts @@ -11,7 +11,7 @@ import { onSettingsHighContrast } from './handlers/settingsHandler'; import { onGetInterfaceDefinition } from './handlers/modelRepositoryHandler'; import { onGetDirectories } from './handlers/directoryHandler'; import { onSendMessageToDevice } from './handlers/deviceHandler'; -import { onStartMonitoring, onStopMonitoring } from './handlers/eventHubHandler'; +import { onStartMonitoring } from './handlers/eventHubHandler'; import { formatError } from './utils/errorHelper'; import '../dist/server/serverElectron'; @@ -34,7 +34,6 @@ class Main { Main.registerHandler(MESSAGE_CHANNELS.DIRECTORY_GET_DIRECTORIES, onGetDirectories); Main.registerHandler(MESSAGE_CHANNELS.DEVICE_SEND_MESSAGE, onSendMessageToDevice); Main.registerHandler(MESSAGE_CHANNELS.EVENTHUB_START_MONITORING, onStartMonitoring); - Main.registerHandler(MESSAGE_CHANNELS.EVENTHUB_STOP_MONITORING, onStopMonitoring); } private static setApplicationLock(): void { diff --git a/public/factories/eventHubInterfaceFactory.ts b/public/factories/eventHubInterfaceFactory.ts index 807993a78..c3d1e1fdc 100644 --- a/public/factories/eventHubInterfaceFactory.ts +++ b/public/factories/eventHubInterfaceFactory.ts @@ -10,9 +10,6 @@ export const generateEventHubInterface = (): EventHubInterface => { return { startEventHubMonitoring: async (params: StartEventHubMonitoringParameters): Promise => { return invokeInMainWorld(MESSAGE_CHANNELS.EVENTHUB_START_MONITORING, params); - }, - stopEventHubMonitoring: async (): Promise => { - return invokeInMainWorld(MESSAGE_CHANNELS.EVENTHUB_STOP_MONITORING); } }; }; diff --git a/public/handlers/eventHubHandler.ts b/public/handlers/eventHubHandler.ts index 65fddf027..41ece3824 100644 --- a/public/handlers/eventHubHandler.ts +++ b/public/handlers/eventHubHandler.ts @@ -3,147 +3,81 @@ * Licensed under the MIT License **********************************************************/ import { IpcMainInvokeEvent } from 'electron'; -import { EventHubClient, EventPosition, ReceiveHandler } from '@azure/event-hubs'; +import { EventHubConsumerClient, ReceivedEventData, earliestEventPosition } from '@azure/event-hubs'; import { Message, StartEventHubMonitoringParameters } from '../interfaces/eventHubInterface'; - -let client: EventHubClient = null; -let messages: Message[] = []; -let receivers: ReceiveHandler[] = []; -let connectionString: string = ''; // would equal `${hubConnectionString}` or `${customEventHubConnectionString}/${customEventHubName}` -let deviceId: string = ''; -let moduleId: string = ''; +import { convertIotHubToEventHubsConnectionString } from '../utils/eventHubHelper'; const IOTHUB_CONNECTION_DEVICE_ID = 'iothub-connection-device-id'; const IOTHUB_CONNECTION_MODULE_ID = 'iothub-connection-module-id'; +let hubConnectionString = ''; +let eventHubCompatibleConnectionString = ''; +let deviceId = ''; +let moduleId = ''; +let messages: Message[] = []; -export const onStartMonitoring = async (event: IpcMainInvokeEvent, params: StartEventHubMonitoringParameters): Promise=> { - return eventHubProvider(params).then(result => { - return result; - }); +export const onStartMonitoring = async (event: IpcMainInvokeEvent, params: StartEventHubMonitoringParameters): Promise=> { + await eventHubProvider(params); + const result = [...messages]; + messages = []; + return result; } -export const onStopMonitoring = async (): Promise => { - try { - return stopClient(); - } catch (error) { - // swallow the error as we set client to null anyways +const eventHubProvider = async (params: any) => { + let connectionString = ''; + if (params.customEventHubConnectionString) { + connectionString = params.customEventHubConnectionString; } -} + else { + if (params.hubConnectionString != hubConnectionString) { + connectionString = await convertIotHubToEventHubsConnectionString(params.hubConnectionString); + // save strings for future use + hubConnectionString = params.hubConnectionString; + eventHubCompatibleConnectionString = connectionString; -const eventHubProvider = async (params: StartEventHubMonitoringParameters) => { - if (needToCreateNewEventHubClient(params)) - { - // hub has changed, reinitialize client, receivers and mesages - client = params.customEventHubConnectionString ? - await EventHubClient.createFromConnectionString(params.customEventHubConnectionString, params.customEventHubName) : - await EventHubClient.createFromIotHubConnectionString(params.hubConnectionString); - - connectionString = params.customEventHubConnectionString ? - `${params.customEventHubConnectionString}/${params.customEventHubName}` : - params.hubConnectionString; - receivers = []; - messages = []; + } + else { + connectionString = eventHubCompatibleConnectionString; + } } - updateEntityIdIfNecessary(params); - return listeningToMessages(client, params); -}; - -const listeningToMessages = async (eventHubClient: EventHubClient, params: StartEventHubMonitoringParameters) => { - if (params.startListeners || !receivers) { - const partitionIds = await client.getPartitionIds(); - const hubInfo = await client.getHubRuntimeInformation(); - const startTime = params.startTime ? Date.parse(params.startTime) : Date.now(); - - partitionIds && partitionIds.forEach(async (partitionId: string) => { - const receiveOptions = { - consumerGroup: params.consumerGroup, - enableReceiverRuntimeMetric: true, - eventPosition: EventPosition.fromEnqueuedTime(startTime), - name: `${hubInfo.path}_${partitionId}`, - }; - - const receiver = eventHubClient.receive( - partitionId, - onMessageReceived, - (err: object) => {}, - receiveOptions); - receivers.push(receiver); - }); + if (deviceId != params.deviceId || moduleId != params.moduleId) { + messages = []; // clear the messages when switching identites + deviceId = params.deviceId; + moduleId = params.moduleId; } - return handleMessages(); -}; - -const handleMessages = () => { - let results: Message[] = []; - messages.forEach(message => { - if (!results.some(result => result.systemProperties?.['x-opt-sequence-number'] === message.systemProperties?.['x-opt-sequence-number'])) { - // if user click stop/start too refrequently, it's possible duplicate receivers are created before the cleanup happens as it's async - // remove duplicate messages before proper cleanup is finished - results.push(message); - } - }) - messages = []; // empty the array everytime the result is returned - return results; -} - -const stopClient = async () => { - return stopReceivers().then(() => { - return client && client.close().catch(error => { - console.log(`client cleanup error: ${error}`); // swallow the error as we will cleanup anyways - }); - }).finally (() => { - client = null; - receivers = []; - }); -}; - -const stopReceivers = async () => { - return Promise.all( - receivers.map(receiver => { - if (receiver && (receiver.isReceiverOpen === undefined || receiver.isReceiverOpen)) { - return stopReceiver(receiver); - } else { - return null; + const client = new EventHubConsumerClient(params.consumerGroup, connectionString); + const subscription = client.subscribe( + { + processEvents: async (events) => { + handleMessages(events, params) + }, + processError: async (err) => { + console.log(err); } - }) + }, + { startPosition: params.startTime ? { enqueuedOn: new Date(params.startTime).getTime() } : earliestEventPosition } ); -}; - -const stopReceiver = async (receiver: ReceiveHandler) => { - receiver.stop().catch((err: object) => { - throw new Error(`receivers cleanup error: ${err}`); - }); -} - -const needToCreateNewEventHubClient = (parmas: StartEventHubMonitoringParameters): boolean => { - return !client || - parmas.hubConnectionString && parmas.hubConnectionString !== connectionString || - parmas.customEventHubConnectionString && `${parmas.customEventHubConnectionString}/${parmas.customEventHubName}` !== connectionString; -} -const updateEntityIdIfNecessary = (parmas: StartEventHubMonitoringParameters) => { - if( !deviceId || parmas.deviceId !== deviceId) { - deviceId = parmas.deviceId; - messages = []; - } - if (parmas.moduleId !== moduleId) { - moduleId = parmas.moduleId; - messages = []; - } -} + // Wait for a few seconds to receive events before closing + setTimeout(async () => { + await subscription.close(); + await client.close(); + }, 3 * 1000); +}; -const onMessageReceived = async (eventData: any) => { - if (eventData && eventData.annotations && eventData.annotations[IOTHUB_CONNECTION_DEVICE_ID] === deviceId) { - if (!moduleId || eventData?.annotations?.[IOTHUB_CONNECTION_MODULE_ID] === moduleId) { - const message: Message = { - body: eventData.body, - enqueuedTime: eventData.enqueuedTimeUtc.toString(), - properties: eventData.applicationProperties - }; - message.systemProperties = eventData.annotations; - messages.push(message); +const handleMessages = (events: ReceivedEventData[], params: any) => { + events.forEach(event => { + if (event?.systemProperties?.[IOTHUB_CONNECTION_DEVICE_ID] === params.deviceId) { + if (!params.moduleId || event?.systemProperties?.[IOTHUB_CONNECTION_MODULE_ID] === params.moduleId) { + const message: Message = { + body: event.body, + enqueuedTime: event.enqueuedTimeUtc.toString(), + properties: event.properties + }; + message.systemProperties = event.systemProperties; + messages.push(message); + } } - } -}; + }); +} \ No newline at end of file diff --git a/public/interfaces/eventHubInterface.ts b/public/interfaces/eventHubInterface.ts index be0f4c14e..56cb4d74d 100644 --- a/public/interfaces/eventHubInterface.ts +++ b/public/interfaces/eventHubInterface.ts @@ -7,7 +7,6 @@ export interface StartEventHubMonitoringParameters { moduleId: string; consumerGroup: string; startTime: string; - startListeners: boolean; customEventHubName?: string; customEventHubConnectionString?: string; @@ -23,5 +22,4 @@ export interface Message { export interface EventHubInterface { startEventHubMonitoring(params: StartEventHubMonitoringParameters): Promise; - stopEventHubMonitoring(): Promise; } \ No newline at end of file diff --git a/public/utils/eventHubHelper.ts b/public/utils/eventHubHelper.ts new file mode 100644 index 000000000..b0a374830 --- /dev/null +++ b/public/utils/eventHubHelper.ts @@ -0,0 +1,83 @@ + +/*********************************************************** + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License + **********************************************************/ +import { AmqpError, isAmqpError, Connection, ReceiverEvents, parseConnectionString } from "rhea-promise"; +import * as crypto from "crypto"; + +// The following helper functions are directly copied from: +// https://raw.githubusercontent.com/Azure/azure-sdk-for-js/main/sdk/eventhub/event-hubs/samples/v5/typescript/src/iothubConnectionString.ts +export const convertIotHubToEventHubsConnectionString = async (connectionString: string): Promise => +{ + const { HostName, SharedAccessKeyName, SharedAccessKey } = parseConnectionString<{ + HostName: string; + SharedAccessKeyName: string; + SharedAccessKey: string; + }>(connectionString); + + // Verify that the required info is in the connection string. + if (!HostName || !SharedAccessKey || !SharedAccessKeyName) { + throw new Error(`Invalid IotHub connection string.`); + } + + //Extract the IotHub name from the hostname. + const [iotHubName] = HostName.split("."); + + if (!iotHubName) { + throw new Error(`Unable to extract the IotHub name from the connection string.`); + } + + // Generate a token to authenticate to the service. + // The code for generateSasToken can be found at https://docs.microsoft.com/en-us/azure/iot-hub/iot-hub-devguide-security#security-tokens + const token = generateSasToken(`${HostName}/messages/events`, SharedAccessKey, SharedAccessKeyName, 5); + + const connection = new Connection({ + transport: "tls", + host: HostName, + hostname: HostName, + username: `${SharedAccessKeyName}@sas.root.${iotHubName}`, + port: 5671, + reconnect: false, + password: token + }); + await connection.open(); + + // Create the receiver that will trigger a redirect error. + const receiver = await connection.createReceiver({ source: { address: `amqps://${HostName}/messages/events/$management` }}); + + return new Promise((resolve, reject) => { + receiver.on(ReceiverEvents.receiverError, (context) => { + const error = context.receiver && context.receiver.error; + if (isAmqpError(error) && (error as AmqpError).condition === "amqp:link:redirect") { + const hostname = (error as AmqpError).info?.hostname; + if (!hostname) { + reject(error); + } else { + resolve(`Endpoint=sb://${hostname}/;EntityPath=${iotHubName};SharedAccessKeyName=${SharedAccessKeyName};SharedAccessKey=${SharedAccessKey}`); + } + } else { + reject(error); + } + connection.close().catch(() => { + /* ignore error */ + }); + }); + }); +} + +// This code copied from event hub's sample +// https://raw.githubusercontent.com/Azure/azure-sdk-for-js/main/sdk/eventhub/event-hubs/samples/v5/typescript/src/iothubConnectionString.ts +const generateSasToken = (resourceUri: string, signingKey: string, policyName: string, expiresInMins: number): string => { + resourceUri = encodeURIComponent(resourceUri); + const expiresInSeconds = Math.ceil(Date.now() / 1000 + expiresInMins * 60); + const toSign = resourceUri + "\n" + expiresInSeconds; + + // Use the crypto module to create the hmac. + const hmac = crypto.createHmac("sha256", Buffer.from(signingKey, "base64")); + hmac.update(toSign); + const base64UriEncoded = encodeURIComponent(hmac.digest("base64")); + + // Construct authorization string. + return `SharedAccessSignature sr=${resourceUri}&sig=${base64UriEncoded}&se=${expiresInSeconds}&skn=${policyName}`; +} \ No newline at end of file diff --git a/src/app/api/parameters/deviceParameters.ts b/src/app/api/parameters/deviceParameters.ts index ef671b526..3d49346c7 100644 --- a/src/app/api/parameters/deviceParameters.ts +++ b/src/app/api/parameters/deviceParameters.ts @@ -27,7 +27,6 @@ export interface MonitorEventsParameters { deviceId: string; moduleId: string; consumerGroup: string; - startListeners: boolean; customEventHubName?: string; customEventHubConnectionString?: string; diff --git a/src/app/api/services/devicesService.spec.ts b/src/app/api/services/devicesService.spec.ts index 11d095f3e..6fcb3c923 100644 --- a/src/app/api/services/devicesService.spec.ts +++ b/src/app/api/services/devicesService.spec.ts @@ -599,7 +599,7 @@ describe('deviceTwinService', () => { customEventHubConnectionString: undefined, deviceId, hubConnectionString: undefined, - startListeners: true, + moduleId: undefined, startTime: undefined }; @@ -609,8 +609,7 @@ describe('deviceTwinService', () => { const startEventHubMonitoring = jest.fn(); jest.spyOn(interfaceUtils, 'getEventHubInterface').mockReturnValue({ - startEventHubMonitoring, - stopEventHubMonitoring: jest.fn() + startEventHubMonitoring }); await DevicesService.monitorEvents(parameters); @@ -621,18 +620,4 @@ describe('deviceTwinService', () => { }); }); }); - - context('stopMonitoringEvents', () => { - it('calls stopEventHubMonitoring', async () => { - - const stopEventHubMonitoring = jest.fn(); - jest.spyOn(interfaceUtils, 'getEventHubInterface').mockReturnValue({ - startEventHubMonitoring: jest.fn(), - stopEventHubMonitoring - }); - - await DevicesService.stopMonitoringEvents(); - expect(stopEventHubMonitoring).toBeCalled(); - }); - }); }); diff --git a/src/app/api/services/devicesService.ts b/src/app/api/services/devicesService.ts index 8519387fb..7fb7c8583 100644 --- a/src/app/api/services/devicesService.ts +++ b/src/app/api/services/devicesService.ts @@ -248,8 +248,3 @@ export const monitorEvents = async (parameters: MonitorEventsParameters): Promis const result = await api.startEventHubMonitoring(requestParameters); return result && result.length && result.length !== 0 && result.map(message => parseEventHubMessage(message)) || []; }; - -export const stopMonitoringEvents = async (): Promise => { - const api = getEventHubInterface(); - await api.stopEventHubMonitoring(); -}; diff --git a/src/app/devices/deviceEvents/actions.ts b/src/app/devices/deviceEvents/actions.ts index 2d35e105f..022a4140d 100644 --- a/src/app/devices/deviceEvents/actions.ts +++ b/src/app/devices/deviceEvents/actions.ts @@ -10,5 +10,5 @@ import { MonitorEventsParameters } from '../../api/parameters/deviceParameters'; const deviceContentCreator = actionCreatorFactory(DEVICECONTENT); export const startEventsMonitoringAction = deviceContentCreator.async(START_EVENTS_MONITORING); -export const stopEventsMonitoringAction = deviceContentCreator.async(STOP_EVENTS_MONITORING); +export const stopEventsMonitoringAction = deviceContentCreator(STOP_EVENTS_MONITORING); export const clearMonitoringEventsAction = deviceContentCreator(CLEAR_MONITORING_EVENTS); diff --git a/src/app/devices/deviceEvents/components/__snapshots__/customEventHub.spec.tsx.snap b/src/app/devices/deviceEvents/components/__snapshots__/customEventHub.spec.tsx.snap index aabbeac46..e25b81da1 100644 --- a/src/app/devices/deviceEvents/components/__snapshots__/customEventHub.spec.tsx.snap +++ b/src/app/devices/deviceEvents/components/__snapshots__/customEventHub.spec.tsx.snap @@ -29,15 +29,6 @@ exports[`customEventHub matches snapshot 1`] = ` required={true} underlined={true} /> - `; diff --git a/src/app/devices/deviceEvents/components/__snapshots__/deviceEvents.spec.tsx.snap b/src/app/devices/deviceEvents/components/__snapshots__/deviceEvents.spec.tsx.snap index b19ab70a2..dbc73e231 100644 --- a/src/app/devices/deviceEvents/components/__snapshots__/deviceEvents.spec.tsx.snap +++ b/src/app/devices/deviceEvents/components/__snapshots__/deviceEvents.spec.tsx.snap @@ -45,7 +45,6 @@ exports[`deviceEvents deviceEvents in non-pnp context matches snapshot 1`] = ` { )).toMatchSnapshot(); @@ -33,16 +31,13 @@ describe('customEventHub', () => { it('specifies custom strings', () => { const mockSetUseBuiltInEventHub = jest.fn(); - const mockSetCustomEventHubName = jest.fn(); const mockSetCustomEventHubConnectionString = jest.fn(); const wrapper = mount( ); @@ -52,9 +47,7 @@ describe('customEventHub', () => { expect(mockSetUseBuiltInEventHub).toBeCalledWith(true); act(() => wrapper.find(TextField).first().props().onChange(undefined, 'connectionString')); - act(() => wrapper.find(TextField).at(1).props().onChange(undefined, 'hubName')); wrapper.update(); expect(mockSetCustomEventHubConnectionString).toBeCalledWith('connectionString'); - expect(mockSetCustomEventHubName).toBeCalledWith('hubName'); }); }); diff --git a/src/app/devices/deviceEvents/components/customEventHub.tsx b/src/app/devices/deviceEvents/components/customEventHub.tsx index 9a13aac79..218f20e21 100644 --- a/src/app/devices/deviceEvents/components/customEventHub.tsx +++ b/src/app/devices/deviceEvents/components/customEventHub.tsx @@ -12,10 +12,8 @@ import './deviceEvents.scss'; export interface CustomEventHubProps { monitoringData: boolean; useBuiltInEventHub: boolean; - customEventHubName: string; customEventHubConnectionString: string; setUseBuiltInEventHub: (monitoringData: boolean) => void; - setCustomEventHubName: (customEventHubName: string) => void; setCustomEventHubConnectionString: (customEventHubConnectionString: string) => void; setHasError: (hasError: boolean) => void; } @@ -23,10 +21,8 @@ export interface CustomEventHubProps { export const CustomEventHub: React.FC = ({ monitoringData, useBuiltInEventHub, - customEventHubName, customEventHubConnectionString, setUseBuiltInEventHub, - setCustomEventHubName, setCustomEventHubConnectionString, setHasError}) => { @@ -52,10 +48,6 @@ export const CustomEventHub: React.FC = ({ setCustomEventHubConnectionString(newValue); }; - const customEventHubNameChange = (event: React.FormEvent, newValue?: string) => { - setCustomEventHubName(newValue); - }; - return ( = ({ errorMessage={error} required={true} /> - } diff --git a/src/app/devices/deviceEvents/components/deviceEvents.spec.tsx b/src/app/devices/deviceEvents/components/deviceEvents.spec.tsx index 45df31cc4..1281cc38d 100644 --- a/src/app/devices/deviceEvents/components/deviceEvents.spec.tsx +++ b/src/app/devices/deviceEvents/components/deviceEvents.spec.tsx @@ -50,7 +50,6 @@ describe('deviceEvents', () => { jest.spyOn(React, 'useState').mockImplementationOnce(() => realUseState(undefined)); jest.spyOn(React, 'useState').mockImplementationOnce(() => realUseState(false)); jest.spyOn(React, 'useState').mockImplementationOnce(() => realUseState(undefined)); - jest.spyOn(React, 'useState').mockImplementationOnce(() => realUseState(undefined)); jest.spyOn(React, 'useState').mockImplementationOnce(() => realUseState(false)); jest.spyOn(React, 'useState').mockImplementationOnce(() => realUseState(false)); jest.spyOn(React, 'useState').mockImplementationOnce(() => realUseState(false)); @@ -84,7 +83,6 @@ describe('deviceEvents', () => { consumerGroup: DEFAULT_CONSUMER_GROUP, deviceId: 'device1', moduleId: null, - startListeners: true, startTime: undefined }); }); @@ -130,7 +128,6 @@ describe('deviceEvents', () => { jest.spyOn(React, 'useState').mockImplementationOnce(() => realUseState(undefined)); jest.spyOn(React, 'useState').mockImplementationOnce(() => realUseState(false)); jest.spyOn(React, 'useState').mockImplementationOnce(() => realUseState(undefined)); - jest.spyOn(React, 'useState').mockImplementationOnce(() => realUseState(undefined)); jest.spyOn(React, 'useState').mockImplementationOnce(() => realUseState(false)); jest.spyOn(React, 'useState').mockImplementationOnce(() => realUseState(false)); jest.spyOn(React, 'useState').mockImplementationOnce(() => realUseState(false)); diff --git a/src/app/devices/deviceEvents/components/deviceEvents.tsx b/src/app/devices/deviceEvents/components/deviceEvents.tsx index 4868e1bd3..d49d69289 100644 --- a/src/app/devices/deviceEvents/components/deviceEvents.tsx +++ b/src/app/devices/deviceEvents/components/deviceEvents.tsx @@ -55,7 +55,6 @@ export const DeviceEvents: React.FC = () => { const [startTime, setStartTime] = React.useState(); const [useBuiltInEventHub, setUseBuiltInEventHub] = React.useState(true); const [customEventHubConnectionString, setCustomEventHubConnectionString] = React.useState(undefined); - const [customEventHubName, setCustomEventHubName] = React.useState(undefined); const [showSystemProperties, setShowSystemProperties] = React.useState(false); // event message state @@ -77,44 +76,31 @@ export const DeviceEvents: React.FC = () => { // simulation specific const [showSimulationPanel, setShowSimulationPanel] = React.useState(false); - React.useEffect( - () => { - return () => { - stopMonitoring(); - }; - }, - []); - React.useEffect( // tslint:disable-next-line: cyclomatic-complexity () => { if (synchronizationStatus === SynchronizationStatus.updating || // when specifying start time, valid time need to be provided (specifyStartTime && (!startTime || hasError)) || // when using custom event hub, both valid connection string and name need to be provided - (!useBuiltInEventHub && (!customEventHubConnectionString || !customEventHubName || hasError))) { + (!useBuiltInEventHub && (!customEventHubConnectionString || hasError))) { setStartDisabled(true); } else { setStartDisabled(false); } }, - [hasError, synchronizationStatus, useBuiltInEventHub, customEventHubConnectionString, customEventHubName, specifyStartTime, startTime]); + [hasError, synchronizationStatus, useBuiltInEventHub, customEventHubConnectionString, specifyStartTime, startTime]); React.useEffect(// tslint:disable-next-line: cyclomatic-complexity () => { if (synchronizationStatus === SynchronizationStatus.fetched) { - if (appConfig.hostMode !== HostMode.Browser) { - if (monitoringData) { - setStartTime(new Date()); - setTimeout( - () => { - fetchData(false)(); - }, - LOADING_LOCK); - } - else { - stopMonitoring(); - } + if (monitoringData) { + setStartTime(new Date()); + setTimeout( + () => { + fetchData(); + }, + LOADING_LOCK); } else { stopMonitoring(); @@ -143,7 +129,7 @@ export const DeviceEvents: React.FC = () => { setShowPnpModeledEvents={setShowPnpModeledEvents} setShowSimulationPanel={setShowSimulationPanel} dispatch={dispatch} - fetchData={fetchData(true)} + fetchData={fetchData} /> ); }; @@ -179,10 +165,8 @@ export const DeviceEvents: React.FC = () => { @@ -191,7 +175,7 @@ export const DeviceEvents: React.FC = () => { }; const stopMonitoring = () => { - dispatch(stopEventsMonitoringAction.started()); + dispatch(stopEventsMonitoringAction()); }; // tslint:disable-next-line: cyclomatic-complexity @@ -478,20 +462,18 @@ export const DeviceEvents: React.FC = () => { ); }; - const fetchData = (startListeners: boolean) => () => { + const fetchData = () => { let parameters: MonitorEventsParameters = { consumerGroup, deviceId, moduleId, - startListeners, startTime }; if (!useBuiltInEventHub) { parameters = { ...parameters, - customEventHubConnectionString, - customEventHubName + customEventHubConnectionString }; } diff --git a/src/app/devices/deviceEvents/reducer.spec.ts b/src/app/devices/deviceEvents/reducer.spec.ts index eab91ba17..d82aeb2fe 100644 --- a/src/app/devices/deviceEvents/reducer.spec.ts +++ b/src/app/devices/deviceEvents/reducer.spec.ts @@ -12,7 +12,7 @@ import { DEFAULT_CONSUMER_GROUP } from './../../constants/apiConstants'; describe('deviceEventsReducer', () => { const deviceId = 'testDeviceId'; - const params = {consumerGroup: DEFAULT_CONSUMER_GROUP, deviceId, startTime: new Date()}; + const params = {consumerGroup: DEFAULT_CONSUMER_GROUP, deviceId, moduleId: undefined, startTime: new Date()}; const events = [{ body: { humid: '123' // intentionally set a value which type is double @@ -44,21 +44,8 @@ describe('deviceEventsReducer', () => { synchronizationStatus: SynchronizationStatus.fetched }); - it (`handles ${STOP_EVENTS_MONITORING}/ACTION_START action`, () => { - const action = stopEventsMonitoringAction.started(); - expect(deviceEventsReducer(deviceEventsStateInitial(), action).synchronizationStatus).toEqual(SynchronizationStatus.updating); - expect(deviceEventsReducer(deviceEventsStateInitial(), action).payload).toEqual([]); - }); - - it (`handles ${STOP_EVENTS_MONITORING}/ACTION_DONE action`, () => { - const action = stopEventsMonitoringAction.done({}); + it (`handles ${STOP_EVENTS_MONITORING} action`, () => { + const action = stopEventsMonitoringAction(); expect(deviceEventsReducer(deviceEventsStateInitial(), action).synchronizationStatus).toEqual(SynchronizationStatus.upserted); - expect(deviceEventsReducer(deviceEventsStateInitial(), action).payload).toEqual([]); - }); - - it (`handles ${STOP_EVENTS_MONITORING}/ACTION_FAILED action`, () => { - const action = stopEventsMonitoringAction.failed({error: -1}); - expect(deviceEventsReducer(deviceEventsStateInitial(), action).synchronizationStatus).toEqual(SynchronizationStatus.failed); - expect(deviceEventsReducer(deviceEventsStateInitial(), action).payload).toEqual([]); }); }); diff --git a/src/app/devices/deviceEvents/reducers.ts b/src/app/devices/deviceEvents/reducers.ts index 03d0cd191..fb8ea5219 100644 --- a/src/app/devices/deviceEvents/reducers.ts +++ b/src/app/devices/deviceEvents/reducers.ts @@ -4,11 +4,7 @@ **********************************************************/ import { reducerWithInitialState } from 'typescript-fsa-reducers'; import { DeviceEventsStateInterface, deviceEventsStateInitial, DeviceEventsStateType } from './state'; -import { - startEventsMonitoringAction, - stopEventsMonitoringAction, - clearMonitoringEventsAction -} from './actions'; +import { startEventsMonitoringAction, clearMonitoringEventsAction, stopEventsMonitoringAction } from './actions'; import { SynchronizationStatus } from '../../api/models/synchronizationStatus'; import { MonitorEventsParameters } from '../../api/parameters/deviceParameters'; import { Message } from '../../api/models/messages'; @@ -36,21 +32,11 @@ export const deviceEventsReducer = reducerWithInitialState { - return state.merge({ - synchronizationStatus: SynchronizationStatus.updating - }); - }) - .case(stopEventsMonitoringAction.done, (state: DeviceEventsStateType) => { + .case(stopEventsMonitoringAction, (state: DeviceEventsStateType) => { return state.merge({ synchronizationStatus: SynchronizationStatus.upserted }); }) - .case(stopEventsMonitoringAction.failed, (state: DeviceEventsStateType) => { - return state.merge({ - synchronizationStatus: SynchronizationStatus.failed - }); - }) .case(clearMonitoringEventsAction, (state: DeviceEventsStateType) => { return state.merge({ payload: [] diff --git a/src/app/devices/deviceEvents/saga.spec.ts b/src/app/devices/deviceEvents/saga.spec.ts index 02e8a7fcd..662852f8f 100644 --- a/src/app/devices/deviceEvents/saga.spec.ts +++ b/src/app/devices/deviceEvents/saga.spec.ts @@ -16,20 +16,15 @@ import { DEFAULT_CONSUMER_GROUP } from '../../constants/apiConstants'; describe('deviceMonitoringSaga', () => { let startEventsMonitoringSagaGenerator; - let stopEventsMonitoringSagaGenerator; const mockMonitorEventsFn = jest.spyOn(DevicesService, 'monitorEvents').mockImplementationOnce(parameters => { return null; }); - const mockStopMonitorEventsFn = jest.spyOn(DevicesService, 'stopMonitoringEvents').mockImplementationOnce(() => { - return null; - }); const deviceId = 'test_id'; const params = {consumerGroup: DEFAULT_CONSUMER_GROUP, deviceId, startTime: new Date()}; beforeEach(() => { startEventsMonitoringSagaGenerator = cloneableGenerator(startEventsMonitoringSagaWorker)(startEventsMonitoringAction.started(params)); - stopEventsMonitoringSagaGenerator = cloneableGenerator(stopEventsMonitoringSagaWorker)(); }); it('start device events monitoring', () => { @@ -81,50 +76,4 @@ describe('deviceMonitoringSaga', () => { expect(failed.next().done).toEqual(true); }); - - it('stop device events monitoring', () => { - // call for device id - expect(stopEventsMonitoringSagaGenerator.next()).toEqual({ - done: false, - value: call(mockStopMonitorEventsFn) - }); - - // add to store - expect(stopEventsMonitoringSagaGenerator.next([])).toEqual({ - done: false, - value: put(stopEventsMonitoringAction.done({})) - }); - - // success - const success = stopEventsMonitoringSagaGenerator.clone(); - expect(success.next()).toEqual({ - done: true - }); - - // failure - const failed = stopEventsMonitoringSagaGenerator.clone(); - const error = { code: -1 }; - - expect(failed.throw(error)).toEqual({ - done: false, - value: call(raiseNotificationToast, { - text: { - translationKey: ResourceKeys.notifications.stopEventMonitoringOnError, - translationOptions: { - error - }, - }, - type: NotificationType.error - }) - }); - - expect(failed.next([])).toEqual({ - done: false, - value: put(stopEventsMonitoringAction.failed({ - error - })) - }); - - expect(failed.next().done).toEqual(true); - }); }); diff --git a/src/app/devices/deviceEvents/saga.ts b/src/app/devices/deviceEvents/saga.ts index b8b685d51..c773aa5d0 100644 --- a/src/app/devices/deviceEvents/saga.ts +++ b/src/app/devices/deviceEvents/saga.ts @@ -4,10 +4,10 @@ **********************************************************/ import { call, put, all, takeLatest, takeEvery } from 'redux-saga/effects'; import { Action } from 'typescript-fsa'; -import { monitorEvents, stopMonitoringEvents } from '../../api/services/devicesService'; +import { monitorEvents } from '../../api/services/devicesService'; import { NotificationType } from '../../api/models/notification'; import { ResourceKeys } from '../../../localization/resourceKeys'; -import { startEventsMonitoringAction, stopEventsMonitoringAction } from './actions'; +import { startEventsMonitoringAction } from './actions'; import { raiseNotificationToast } from '../../notifications/components/notificationToast'; import { MonitorEventsParameters } from '../../api/parameters/deviceParameters'; @@ -29,27 +29,8 @@ export function* startEventsMonitoringSagaWorker(action: Action/;SharedAccessKeyName=;SharedAccessKey=", "error": "Event hub connection string format should look like 'Endpoint=sb:///;SharedAccessKeyName=;SharedAccessKey='" - }, - "name": { - "label": "Custom event hub name" } }, "headerText": "Telemetry", @@ -856,12 +853,11 @@ "portIsInUseError": "The port {{portNumber}} is in use. To get around with the issue, configure a custom port by setting the system environment variable 'AZURE_IOT_EXPLORER_PORT' to an available port number. To learn more, please visit https://github.com/Azure/azure-iot-explorer/wiki/FAQ", "modelRepoistorySettingsUpdated": "Model repository locations successfully updated.", "startEventMonitoringOnError": "Failed to start monitoring device telemetry: {{error}}", - "stopEventMonitoringOnError": "Failed to stop monitoring device telemetry: {{error}}", "connectionStringsWithoutExpiryRemovalWarning": "Due to security requirements, previously stored hub connection strings have been removed. The new connection string being added would automatically have an expiration date one year from now.", "connectionStringsWithExpiryRemovalWarning": "Due to security requirements, one or more hub connection strings stored more than a year ago have been removed." }, "errorBoundary": { - "text": "Something went wrong" + "text": "Something unexpected went wrong. Help us identify the issue by opening the bug at https://github.com/Azure/azure-iot-explorer/issues" }, "template": { "array":{ diff --git a/src/localization/resourceKeys.ts b/src/localization/resourceKeys.ts index 9cca046a5..6f98b0dec 100644 --- a/src/localization/resourceKeys.ts +++ b/src/localization/resourceKeys.ts @@ -296,9 +296,6 @@ export class ResourceKeys { label : "deviceEvents.customEventHub.connectionString.label", placeHolder : "deviceEvents.customEventHub.connectionString.placeHolder", }, - name : { - label : "deviceEvents.customEventHub.name.label", - }, }, error : "deviceEvents.error", event : { @@ -900,7 +897,6 @@ export class ResourceKeys { savedToIotHubConnectionString : "notifications.savedToIotHubConnectionString", sendingCloudToDeviceMessage : "notifications.sendingCloudToDeviceMessage", startEventMonitoringOnError : "notifications.startEventMonitoringOnError", - stopEventMonitoringOnError : "notifications.stopEventMonitoringOnError", updateDeviceOnError : "notifications.updateDeviceOnError", updateDeviceOnSucceed : "notifications.updateDeviceOnSucceed", updateDeviceTwinOnError : "notifications.updateDeviceTwinOnError", diff --git a/src/server/eventHubHelper.ts b/src/server/eventHubHelper.ts new file mode 100644 index 000000000..b0a374830 --- /dev/null +++ b/src/server/eventHubHelper.ts @@ -0,0 +1,83 @@ + +/*********************************************************** + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License + **********************************************************/ +import { AmqpError, isAmqpError, Connection, ReceiverEvents, parseConnectionString } from "rhea-promise"; +import * as crypto from "crypto"; + +// The following helper functions are directly copied from: +// https://raw.githubusercontent.com/Azure/azure-sdk-for-js/main/sdk/eventhub/event-hubs/samples/v5/typescript/src/iothubConnectionString.ts +export const convertIotHubToEventHubsConnectionString = async (connectionString: string): Promise => +{ + const { HostName, SharedAccessKeyName, SharedAccessKey } = parseConnectionString<{ + HostName: string; + SharedAccessKeyName: string; + SharedAccessKey: string; + }>(connectionString); + + // Verify that the required info is in the connection string. + if (!HostName || !SharedAccessKey || !SharedAccessKeyName) { + throw new Error(`Invalid IotHub connection string.`); + } + + //Extract the IotHub name from the hostname. + const [iotHubName] = HostName.split("."); + + if (!iotHubName) { + throw new Error(`Unable to extract the IotHub name from the connection string.`); + } + + // Generate a token to authenticate to the service. + // The code for generateSasToken can be found at https://docs.microsoft.com/en-us/azure/iot-hub/iot-hub-devguide-security#security-tokens + const token = generateSasToken(`${HostName}/messages/events`, SharedAccessKey, SharedAccessKeyName, 5); + + const connection = new Connection({ + transport: "tls", + host: HostName, + hostname: HostName, + username: `${SharedAccessKeyName}@sas.root.${iotHubName}`, + port: 5671, + reconnect: false, + password: token + }); + await connection.open(); + + // Create the receiver that will trigger a redirect error. + const receiver = await connection.createReceiver({ source: { address: `amqps://${HostName}/messages/events/$management` }}); + + return new Promise((resolve, reject) => { + receiver.on(ReceiverEvents.receiverError, (context) => { + const error = context.receiver && context.receiver.error; + if (isAmqpError(error) && (error as AmqpError).condition === "amqp:link:redirect") { + const hostname = (error as AmqpError).info?.hostname; + if (!hostname) { + reject(error); + } else { + resolve(`Endpoint=sb://${hostname}/;EntityPath=${iotHubName};SharedAccessKeyName=${SharedAccessKeyName};SharedAccessKey=${SharedAccessKey}`); + } + } else { + reject(error); + } + connection.close().catch(() => { + /* ignore error */ + }); + }); + }); +} + +// This code copied from event hub's sample +// https://raw.githubusercontent.com/Azure/azure-sdk-for-js/main/sdk/eventhub/event-hubs/samples/v5/typescript/src/iothubConnectionString.ts +const generateSasToken = (resourceUri: string, signingKey: string, policyName: string, expiresInMins: number): string => { + resourceUri = encodeURIComponent(resourceUri); + const expiresInSeconds = Math.ceil(Date.now() / 1000 + expiresInMins * 60); + const toSign = resourceUri + "\n" + expiresInSeconds; + + // Use the crypto module to create the hmac. + const hmac = crypto.createHmac("sha256", Buffer.from(signingKey, "base64")); + hmac.update(toSign); + const base64UriEncoded = encodeURIComponent(hmac.digest("base64")); + + // Construct authorization string. + return `SharedAccessSignature sr=${resourceUri}&sig=${base64UriEncoded}&se=${expiresInSeconds}&skn=${policyName}`; +} \ No newline at end of file diff --git a/src/server/serverBase.spec.ts b/src/server/serverBase.spec.ts index 3420609d6..9f331cdac 100644 --- a/src/server/serverBase.spec.ts +++ b/src/server/serverBase.spec.ts @@ -58,26 +58,6 @@ describe('serverBase', () => { }); }); - context('handleEventHubStopPostRequest ', () => { - it('returns 400 if body is not provided', async () => { - const req = mockRequest(); - const res = mockResponse(); - - await ServerBase.handleEventHubStopPostRequest (req, res); - expect(res.status).toHaveBeenCalledWith(400); // tslint:disable-line:no-magic-numbers - }); - - it('calls stopClient when body is provided', async () => { - const req = mockRequest('testBody'); - const res = mockResponse(); - const promise = {then: jest.fn()} as any; // tslint:disable-line:no-any - jest.spyOn(ServerBase, 'stopClient').mockReturnValue(promise); - - await ServerBase.handleEventHubStopPostRequest(req, res); - expect(ServerBase.stopClient).toBeCalled(); - }); - }); - context('handleModelRepoPostRequest', () => { it('returns 400 if body is not provided', async () => { const req = mockRequest(); diff --git a/src/server/serverBase.ts b/src/server/serverBase.ts index dcf112c3a..88a546814 100644 --- a/src/server/serverBase.ts +++ b/src/server/serverBase.ts @@ -11,8 +11,9 @@ import bodyParser = require('body-parser'); import cors = require('cors'); import { Client as HubClient } from 'azure-iothub'; import { Message as CloudToDeviceMessage } from 'azure-iot-common'; -import { EventHubClient, EventPosition, ReceiveHandler } from '@azure/event-hubs'; +import { EventHubConsumerClient, ReceivedEventData, earliestEventPosition } from '@azure/event-hubs'; import { generateDataPlaneRequestBody, generateDataPlaneResponse } from './dataPlaneHelper'; +import { convertIotHubToEventHubsConnectionString } from './eventHubHelper'; const SERVER_ERROR = 500; const BAD_REQUEST = 400; @@ -44,7 +45,6 @@ export class ServerBase { app.post(dataPlaneUri, handleDataPlanePostRequest); app.post(cloudToDeviceUri, handleCloudToDevicePostRequest); app.post(eventHubMonitorUri, handleEventHubMonitorPostRequest); - app.post(eventHubStopUri, handleEventHubStopPostRequest); app.post(modelRepoUri, handleModelRepoPostRequest); app.get(readFileUri, handleReadFileRequest); app.get(getDirectoriesUri, handleGetDirectoriesRequest); @@ -225,41 +225,26 @@ export const handleCloudToDevicePostRequest = (req: express.Request, res: expres const eventHubMonitorUri = '/api/EventHub/monitor'; const IOTHUB_CONNECTION_DEVICE_ID = 'iothub-connection-device-id'; const IOTHUB_CONNECTION_MODULE_ID = 'iothub-connection-module-id'; -let client: EventHubClient = null; +let hubConnectionString = ''; +let eventHubCompatibleConnectionString = ''; +let deviceId = ''; +let moduleId = ''; let messages: Message[] = []; -let receivers: ReceiveHandler[] = []; -let connectionString: string = ''; // would equal `${hubConnectionString}` or `${customEventHubConnectionString}/${customEventHubName}` -let deviceId: string = ''; -let moduleId: string = ''; export const handleEventHubMonitorPostRequest = (req: express.Request, res: express.Response) => { try { if (!req.body) { res.status(BAD_REQUEST).send(); } - - eventHubProvider(req.body).then(result => { - res.status(SUCCESS).send(result); + eventHubProvider(req.body).then(() => { + res.status(SUCCESS).send(messages); + messages = []; }); } catch (error) { res.status(SERVER_ERROR).send(error); } }; -const eventHubStopUri = '/api/EventHub/stop'; -export const handleEventHubStopPostRequest = (req: express.Request, res: express.Response) => { - try { - if (!req.body) { - res.status(BAD_REQUEST).send(); - } - - stopClient(); - res.status(SUCCESS).send(); - } catch (error) { - res.status(SERVER_ERROR).send(error); - } -}; - const modelRepoUri = '/api/ModelRepo'; export const handleModelRepoPostRequest = (req: express.Request, res: express.Response) => { try { @@ -282,7 +267,6 @@ export const handleModelRepoPostRequest = (req: express.Request, res: express.Re } }); } catch (error) { - stopReceivers(); res.status(SERVER_ERROR).send(error); } }; @@ -328,119 +312,61 @@ export const addPropertiesToCloudToDeviceMessage = (message: CloudToDeviceMessag }; export const eventHubProvider = async (params: any) => { - if (needToCreateNewEventHubClient(params)) - { - // hub has changed, reinitialize client, receivers and mesages - client = params.customEventHubConnectionString ? - await EventHubClient.createFromConnectionString(params.customEventHubConnectionString, params.customEventHubName) : - await EventHubClient.createFromIotHubConnectionString(params.hubConnectionString); - - connectionString = params.customEventHubConnectionString ? - `${params.customEventHubConnectionString}/${params.customEventHubName}` : - params.hubConnectionString; - receivers = []; - messages = []; + let connectionString = ''; + if (params.customEventHubConnectionString) { + connectionString = params.customEventHubConnectionString; } - updateEntityIdIfNecessary(params); + else { + if (params.hubConnectionString != hubConnectionString) { + connectionString = await convertIotHubToEventHubsConnectionString(params.hubConnectionString); + // save strings for future use + hubConnectionString = params.hubConnectionString; + eventHubCompatibleConnectionString = connectionString; - return listeningToMessages(client, params); -}; - -const listeningToMessages = async (eventHubClient: EventHubClient, params: any) => { - if (params.startListeners || !receivers) { - const partitionIds = await client.getPartitionIds(); - const hubInfo = await client.getHubRuntimeInformation(); - const startTime = params.startTime ? Date.parse(params.startTime) : Date.now(); - - partitionIds && partitionIds.forEach(async (partitionId: string) => { - const receiveOptions = { - consumerGroup: params.consumerGroup, - enableReceiverRuntimeMetric: true, - eventPosition: EventPosition.fromEnqueuedTime(startTime), - name: `${hubInfo.path}_${partitionId}`, - }; - - const receiver = eventHubClient.receive( - partitionId, - onMessageReceived, - (err: object) => {}, - receiveOptions); - receivers.push(receiver); - }); - } - - return handleMessages(); -}; - -const handleMessages = () => { - let results: Message[] = []; - messages.forEach(message => { - if (!results.some(result => result.systemProperties?.['x-opt-sequence-number'] === message.systemProperties?.['x-opt-sequence-number'])) { - // if user click stop/start too refrequently, it's possible duplicate receivers are created before the cleanup happens as it's async - // remove duplicate messages before proper cleanup is finished - results.push(message); } - }) - messages = []; // empty the array everytime the result is returned - return results; -} + else { + connectionString = eventHubCompatibleConnectionString; + } + } -export const stopClient = async () => { - return stopReceivers().then(() => { - return client && client.close().catch(error => { - console.log(`client cleanup error: ${error}`); // swallow the error as we will cleanup anyways - }); - }).finally (() => { - client = null; - receivers = []; - }); -}; + if (deviceId != params.deviceId || moduleId != params.moduleId) { + messages = []; // clear the messages when switching identites + deviceId = params.deviceId; + moduleId = params.moduleId; + } -const stopReceivers = async () => { - return Promise.all( - receivers.map(receiver => { - if (receiver && (receiver.isReceiverOpen === undefined || receiver.isReceiverOpen)) { - return stopReceiver(receiver); - } else { - return null; + const client = new EventHubConsumerClient(params.consumerGroup, connectionString); + const subscription = client.subscribe( + { + processEvents: async (events) => { + handleMessages(events, params) + }, + processError: async (err) => { + console.log(err); } - }) + }, + { startPosition: params.startTime ? { enqueuedOn: new Date(params.startTime).getTime() } : earliestEventPosition } ); + + // Wait for a few seconds to receive events before closing + setTimeout(async () => { + await subscription.close(); + await client.close(); + }, 3 * 1000); }; -const stopReceiver = async (receiver: ReceiveHandler) => { - receiver.stop().catch((err: object) => { - throw new Error(`receivers cleanup error: ${err}`); +const handleMessages = (events: ReceivedEventData[], params: any) => { + events.forEach(event => { + if (event?.systemProperties?.[IOTHUB_CONNECTION_DEVICE_ID] === params.deviceId) { + if (!params.moduleId || event?.systemProperties?.[IOTHUB_CONNECTION_MODULE_ID] === params.moduleId) { + const message: Message = { + body: event.body, + enqueuedTime: event.enqueuedTimeUtc.toString(), + properties: event.properties + }; + message.systemProperties = event.systemProperties; + messages.push(message); + } + } }); } - -const needToCreateNewEventHubClient = (parmas: any): boolean => { - return !client || - parmas.hubConnectionString && parmas.hubConnectionString !== connectionString || - parmas.customEventHubConnectionString && `${parmas.customEventHubConnectionString}/${parmas.customEventHubName}` !== connectionString; -} - -const updateEntityIdIfNecessary = (parmas: any) => { - if (!deviceId || parmas.deviceId !== deviceId) { - deviceId = parmas.deviceId; - messages = []; - } - if (parmas.moduleId !== moduleId) { - moduleId = parmas.moduleId; - messages = []; - } -} - -const onMessageReceived = async (eventData: any) => { - if (eventData?.annotations?.[IOTHUB_CONNECTION_DEVICE_ID] === deviceId) { - if (!moduleId || eventData?.annotations?.[IOTHUB_CONNECTION_MODULE_ID] === moduleId) { - const message: Message = { - body: eventData.body, - enqueuedTime: eventData.enqueuedTimeUtc.toString(), - properties: eventData.applicationProperties - }; - message.systemProperties = eventData.annotations; - messages.push(message); - } - } -};