mirror of
https://github.com/Azure/cosmos-explorer.git
synced 2025-12-24 03:11:32 +00:00
Compare commits
1 Commits
steve-self
...
hosted-msa
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
e41230e8c4 |
@@ -42,7 +42,6 @@ src/Contracts/ViewModels.ts
|
|||||||
src/Controls/Heatmap/Heatmap.test.ts
|
src/Controls/Heatmap/Heatmap.test.ts
|
||||||
src/Controls/Heatmap/Heatmap.ts
|
src/Controls/Heatmap/Heatmap.ts
|
||||||
src/Controls/Heatmap/HeatmapDatatypes.ts
|
src/Controls/Heatmap/HeatmapDatatypes.ts
|
||||||
src/Definitions/adal.d.ts
|
|
||||||
src/Definitions/datatables.d.ts
|
src/Definitions/datatables.d.ts
|
||||||
src/Definitions/gif.d.ts
|
src/Definitions/gif.d.ts
|
||||||
src/Definitions/globals.d.ts
|
src/Definitions/globals.d.ts
|
||||||
|
|||||||
@@ -69,10 +69,6 @@ Jest and Puppeteer are used for end to end browser based tests and are contained
|
|||||||
|
|
||||||
We generally adhere to the release strategy [documented by the Azure SDK Guidelines](https://azure.github.io/azure-sdk/policies_repobranching.html#release-branches). Most releases should happen from the master branch. If master contains commits that cannot be released, you may create a release from a `release/` or `hotfix/` branch. See linked documentation for more details.
|
We generally adhere to the release strategy [documented by the Azure SDK Guidelines](https://azure.github.io/azure-sdk/policies_repobranching.html#release-branches). Most releases should happen from the master branch. If master contains commits that cannot be released, you may create a release from a `release/` or `hotfix/` branch. See linked documentation for more details.
|
||||||
|
|
||||||
### Architechture
|
|
||||||
|
|
||||||
[](https://mermaid-js.github.io/mermaid-live-editor/#/edit/eyJjb2RlIjoiZ3JhcGggTFJcbiAgaG9zdGVkKGh0dHBzOi8vY29zbW9zLmF6dXJlLmNvbSlcbiAgcG9ydGFsKFBvcnRhbClcbiAgZW11bGF0b3IoRW11bGF0b3IpXG4gIGFhZFtBQURdXG4gIHJlc291cmNlVG9rZW5bUmVzb3VyY2UgVG9rZW5dXG4gIGNvbm5lY3Rpb25TdHJpbmdbQ29ubmVjdGlvbiBTdHJpbmddXG4gIHBvcnRhbFRva2VuW0VuY3J5cHRlZCBQb3J0YWwgVG9rZW5dXG4gIG1hc3RlcktleVtNYXN0ZXIgS2V5XVxuICBhcm1bQVJNIFJlc291cmNlIFByb3ZpZGVyXVxuICBkYXRhcGxhbmVbRGF0YSBQbGFuZV1cbiAgcHJveHlbUG9ydGFsIEFQSSBQcm94eV1cbiAgc3FsW1NRTF1cbiAgbW9uZ29bTW9uZ29dXG4gIHRhYmxlc1tUYWJsZXNdXG4gIGNhc3NhbmRyYVtDYXNzYW5kcmFdXG4gIGdyYWZbR3JhcGhdXG5cblxuICBlbXVsYXRvciAtLT4gbWFzdGVyS2V5IC0tLS0-IGRhdGFwbGFuZVxuICBwb3J0YWwgLS0-IGFhZFxuICBob3N0ZWQgLS0-IHBvcnRhbFRva2VuICYgcmVzb3VyY2VUb2tlbiAmIGNvbm5lY3Rpb25TdHJpbmcgJiBhYWRcbiAgYWFkIC0tLT4gYXJtXG4gIGFhZCAtLS0-IGRhdGFwbGFuZVxuICBhYWQgLS0tPiBwcm94eVxuICByZXNvdXJjZVRva2VuIC0tLT4gc3FsIC0tPiBkYXRhcGxhbmVcbiAgcG9ydGFsVG9rZW4gLS0tPiBwcm94eVxuICBwcm94eSAtLT4gZGF0YXBsYW5lXG4gIGNvbm5lY3Rpb25TdHJpbmcgLS0-IHNxbCAmIG1vbmdvICYgY2Fzc2FuZHJhICYgZ3JhZiAmIHRhYmxlc1xuICBzcWwgLS0-IGRhdGFwbGFuZVxuICB0YWJsZXMgLS0-IGRhdGFwbGFuZVxuICBtb25nbyAtLT4gcHJveHlcbiAgY2Fzc2FuZHJhIC0tPiBwcm94eVxuICBncmFmIC0tPiBwcm94eVxuXG5cdFx0IiwibWVybWFpZCI6eyJ0aGVtZSI6ImRlZmF1bHQifSwidXBkYXRlRWRpdG9yIjpmYWxzZX0)
|
|
||||||
|
|
||||||
# Contributing
|
# Contributing
|
||||||
|
|
||||||
Please read the [contribution guidelines](./CONTRIBUTING.md).
|
Please read the [contribution guidelines](./CONTRIBUTING.md).
|
||||||
|
|||||||
@@ -1,4 +1,3 @@
|
|||||||
module.exports = {
|
module.exports = {
|
||||||
presets: [["@babel/preset-env", { targets: { node: "current" } }], "@babel/preset-react", "@babel/preset-typescript"],
|
presets: [["@babel/preset-env", { targets: { node: "current" } }], "@babel/preset-react", "@babel/preset-typescript"]
|
||||||
plugins: [["@babel/plugin-proposal-decorators", { legacy: true }]]
|
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -1,7 +0,0 @@
|
|||||||
# Why?
|
|
||||||
|
|
||||||
This adds a mock module for `canvas`. Nteract has a ignored require and undeclared dependency on this module. `cavnas` is a server side node module and is not used in browser side code for nteract.
|
|
||||||
|
|
||||||
Installing it locally (`npm install canvas`) will resolve the problem, but it is a native module so it is flaky depending on the system, node version, processor arch, etc. This module provides a simpler, more robust solution.
|
|
||||||
|
|
||||||
Remove this workaround if [this bug](https://github.com/nteract/any-vega/issues/2) ever gets resolved
|
|
||||||
@@ -1 +0,0 @@
|
|||||||
module.exports = {}
|
|
||||||
@@ -1,11 +0,0 @@
|
|||||||
{
|
|
||||||
"name": "canvas",
|
|
||||||
"version": "1.0.0",
|
|
||||||
"description": "",
|
|
||||||
"main": "index.js",
|
|
||||||
"scripts": {
|
|
||||||
"test": "echo \"Error: no test specified\" && exit 1"
|
|
||||||
},
|
|
||||||
"author": "",
|
|
||||||
"license": "ISC"
|
|
||||||
}
|
|
||||||
1963
externals/adal.js
vendored
1963
externals/adal.js
vendored
File diff suppressed because it is too large
Load Diff
@@ -3,8 +3,8 @@
|
|||||||
/******************************************************************************/
|
/******************************************************************************/
|
||||||
|
|
||||||
@font-face {
|
@font-face {
|
||||||
font-family: wf_segoe-ui_normal;
|
font-family: wf_segoe-ui_normal;
|
||||||
src: url("../../fonts/segoe-ui/west-european/normal/latest.woff");
|
src: url('../../fonts/segoe-ui/west-european/normal/latest.woff');
|
||||||
}
|
}
|
||||||
|
|
||||||
@DataExplorerFont: wf_segoe-ui_normal, "Segoe UI", "Segoe WP", Tahoma, Arial, sans-serif;
|
@DataExplorerFont: wf_segoe-ui_normal, "Segoe UI", "Segoe WP", Tahoma, Arial, sans-serif;
|
||||||
@@ -20,26 +20,26 @@
|
|||||||
COLORS
|
COLORS
|
||||||
/******************************************************************************/
|
/******************************************************************************/
|
||||||
|
|
||||||
@AccentMediumHigh: #0058ad;
|
@AccentMediumHigh: #0058AD;
|
||||||
@AccentMedium: #004e87;
|
@AccentMedium: #004E87;
|
||||||
@AccentHigh: #1ebaed;
|
@AccentHigh: #1EBAED;
|
||||||
@AccentExtraHigh: #55b3ff;
|
@AccentExtraHigh: #55B3FF;
|
||||||
@AccentLow: #edf6ff;
|
@AccentLow: #EDF6FF;
|
||||||
@AccentMediumLow: #ddeefe;
|
@AccentMediumLow: #DDEEFE;
|
||||||
@AccentLight: #eef7ff;
|
@AccentLight: #EEF7FF;
|
||||||
@AccentExtra: #ddf0ff;
|
@AccentExtra: #DDF0FF;
|
||||||
|
|
||||||
@SelectionHigh: #b91f26;
|
@SelectionHigh: #B91F26;
|
||||||
@BaseLight: #ffffff;
|
@BaseLight: #FFFFFF;
|
||||||
@BaseDark: #000000;
|
@BaseDark: #000000;
|
||||||
@NotificationLow: #fff4ce;
|
@NotificationLow: #FFF4CE;
|
||||||
@NotificationHigh: #f9e9b0;
|
@NotificationHigh: #F9E9B0;
|
||||||
@Purple1: #8a2da5;
|
@Purple1: #8A2DA5;
|
||||||
@Dirty: #9b4f96;
|
@Dirty: #9b4f96;
|
||||||
|
|
||||||
@BaseLow: #f2f2f2;
|
@BaseLow: #F2F2F2;
|
||||||
@BaseMediumLow: #e6e6e6;
|
@BaseMediumLow: #E6E6E6;
|
||||||
@BaseMedium: #cccccc;
|
@BaseMedium: #CCCCCC;
|
||||||
@BaseMediumHigh: #767676;
|
@BaseMediumHigh: #767676;
|
||||||
@BaseHigh: #393939;
|
@BaseHigh: #393939;
|
||||||
|
|
||||||
@@ -53,7 +53,7 @@
|
|||||||
|
|
||||||
@ErrorColor: @SelectionHigh;
|
@ErrorColor: @SelectionHigh;
|
||||||
|
|
||||||
@SelectionColor: #3074b0;
|
@SelectionColor: #3074B0;
|
||||||
|
|
||||||
@FocusColor: #605e5c;
|
@FocusColor: #605e5c;
|
||||||
|
|
||||||
@@ -80,7 +80,7 @@
|
|||||||
@ImgWidth: 14px;
|
@ImgWidth: 14px;
|
||||||
@ImgHeight: 14px;
|
@ImgHeight: 14px;
|
||||||
|
|
||||||
@toggleFontWeight: 700;
|
@toggleFontWeight:700;
|
||||||
|
|
||||||
//Resource Tree
|
//Resource Tree
|
||||||
@TreeLineHeight: 17px;
|
@TreeLineHeight: 17px;
|
||||||
@@ -144,16 +144,16 @@
|
|||||||
/**********************************************************************************/
|
/**********************************************************************************/
|
||||||
|
|
||||||
.flex-display(@display: flex) {
|
.flex-display(@display: flex) {
|
||||||
display: ~"-webkit-@{display}";
|
display: ~"-webkit-@{display}";
|
||||||
display: ~"-ms-@{display}box"; // IE10 uses -ms-flexbox
|
display: ~"-ms-@{display}box"; // IE10 uses -ms-flexbox
|
||||||
display: ~"-ms-@{display}"; // IE11
|
display: ~"-ms-@{display}"; // IE11
|
||||||
display: @display;
|
display: @display;
|
||||||
}
|
}
|
||||||
|
|
||||||
.flex-direction(@direction: column) {
|
.flex-direction(@direction: column) {
|
||||||
-webkit-flex-direction: @direction;
|
-webkit-flex-direction: @direction;
|
||||||
-ms-flex-direction: @direction;
|
-ms-flex-direction: @direction;
|
||||||
flex-direction: @direction;
|
flex-direction: @direction;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*************************************************************************************
|
/*************************************************************************************
|
||||||
@@ -161,31 +161,32 @@
|
|||||||
**************************************************************************************/
|
**************************************************************************************/
|
||||||
|
|
||||||
@media all and (-ms-high-contrast: none), (-ms-high-contrast: active) {
|
@media all and (-ms-high-contrast: none), (-ms-high-contrast: active) {
|
||||||
.selectedRadio,
|
.selectedRadio,
|
||||||
.selectedRadio:hover,
|
.selectedRadio:hover,
|
||||||
.selectedRadio:active,
|
.selectedRadio:active,
|
||||||
.selectedRadio.dirty,
|
.selectedRadio.dirty,
|
||||||
.tab [type="radio"]:checked ~ label,
|
.tab [type=radio]:checked ~ label,
|
||||||
.tab [type="radio"]:checked ~ label:hover {
|
.tab [type=radio]:checked ~ label:hover {
|
||||||
-ms-high-contrast-adjust: none;
|
-ms-high-contrast-adjust: none;
|
||||||
-webkit-text-fill-color: HighlightText;
|
-webkit-text-fill-color: HighlightText;
|
||||||
color: HighlightText;
|
color: HighlightText;
|
||||||
border-color: HighlightText;
|
border-color: HighlightText;
|
||||||
background-color: Highlight;
|
background-color: Highlight;
|
||||||
}
|
}
|
||||||
|
|
||||||
.queryMetricsSummaryTuple {
|
.queryMetricsSummaryTuple {
|
||||||
th,
|
|
||||||
td {
|
th, td {
|
||||||
&:nth-child(2) {
|
|
||||||
width: @IETableDataWidth;
|
&:nth-child(2) {
|
||||||
}
|
width: @IETableDataWidth;
|
||||||
|
}
|
||||||
&:nth-child(3) {
|
|
||||||
width: 50%;
|
&:nth-child(3) {
|
||||||
}
|
width: 50%;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/********************************************************************************************
|
/********************************************************************************************
|
||||||
@@ -193,15 +194,15 @@
|
|||||||
*********************************************************************************************/
|
*********************************************************************************************/
|
||||||
|
|
||||||
.hover() {
|
.hover() {
|
||||||
background-color: @AccentLight;
|
background-color: @AccentLight;
|
||||||
}
|
}
|
||||||
|
|
||||||
.active() {
|
.active() {
|
||||||
background-color: @AccentExtra;
|
background-color: @AccentExtra;
|
||||||
}
|
}
|
||||||
|
|
||||||
.focus() {
|
.focus() {
|
||||||
outline: 1px dashed @FocusColor;
|
outline: 1px dashed @FocusColor;
|
||||||
}
|
}
|
||||||
|
|
||||||
/************************************************************************************************
|
/************************************************************************************************
|
||||||
@@ -211,87 +212,63 @@
|
|||||||
@ToggleWidth: 180px;
|
@ToggleWidth: 180px;
|
||||||
|
|
||||||
.toggleSwitch() {
|
.toggleSwitch() {
|
||||||
max-width: 100%;
|
max-width: 100%;
|
||||||
margin-bottom: @SmallSpace;
|
margin-bottom: @SmallSpace;
|
||||||
padding: @SmallSpace;
|
padding: @SmallSpace;
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
color: @BaseHigh;
|
color: @BaseHigh;
|
||||||
font-weight: 400;
|
font-weight: 400;
|
||||||
font-size: @mediumFontSize;
|
font-size: @mediumFontSize;
|
||||||
font-family: @DataExplorerFont;
|
font-family: @DataExplorerFont;
|
||||||
}
|
}
|
||||||
|
|
||||||
.selectedToggle() {
|
.selectedToggle() {
|
||||||
border-bottom: 2px solid @BaseHigh;
|
border-bottom: 2px solid @BaseHigh;
|
||||||
}
|
}
|
||||||
|
|
||||||
.unselectedToggle() {
|
.unselectedToggle() {
|
||||||
color: @AccentMediumHigh;
|
color: @AccentMediumHigh;
|
||||||
}
|
}
|
||||||
|
|
||||||
/********************************************************************************************************
|
/********************************************************************************************************
|
||||||
Common Data Explorer Icons
|
Common Data Explorer Icons
|
||||||
*********************************************************************************************************/
|
*********************************************************************************************************/
|
||||||
.dataExplorerIcons() {
|
.dataExplorerIcons() {
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
width: @ImgWidth;
|
width: @ImgWidth;
|
||||||
height: @ImgHeight;
|
height: @ImgHeight;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*********************************************************************************************************
|
/*********************************************************************************************************
|
||||||
Info Tooltip
|
Info Tooltip
|
||||||
**********************************************************************************************************/
|
**********************************************************************************************************/
|
||||||
.infoTooltip() {
|
.infoTooltip() {
|
||||||
position: relative;
|
position: relative;
|
||||||
display: inline-block;
|
display: inline-block;
|
||||||
}
|
}
|
||||||
|
|
||||||
.tooltipText(@textColor: @BaseLight, @backgroundColor: @BaseHigh) {
|
.tooltipText(@textColor: @BaseLight, @backgroundColor: @BaseHigh) {
|
||||||
visibility: hidden;
|
visibility: hidden;
|
||||||
background-color: @backgroundColor;
|
background-color: @backgroundColor;
|
||||||
color: @textColor;
|
color: @textColor;
|
||||||
position: absolute;
|
position: absolute;
|
||||||
z-index: 1;
|
z-index: 1;
|
||||||
left: @MediumSpace;
|
left: @MediumSpace;
|
||||||
padding: @MediumSpace;
|
padding: @MediumSpace;
|
||||||
}
|
}
|
||||||
|
|
||||||
.tooltipTextAfter(@color: @BaseDark) {
|
.tooltipTextAfter(@color: @BaseDark) {
|
||||||
content: "";
|
content: "";
|
||||||
position: absolute;
|
position: absolute;
|
||||||
right: 100%;
|
right: 100%;
|
||||||
border-style: solid;
|
border-style: solid;
|
||||||
border-color: transparent @color transparent transparent;
|
border-color: transparent @color transparent transparent;
|
||||||
left: 0px;
|
left: 0px;
|
||||||
width: 0;
|
width: 0;
|
||||||
height: 0;
|
height: 0;
|
||||||
border-color: @InfoPointerColor transparent;
|
border-color: @InfoPointerColor transparent;
|
||||||
}
|
}
|
||||||
|
|
||||||
.tooltipVisible() {
|
.tooltipVisible() {
|
||||||
visibility: visible;
|
visibility: visible;
|
||||||
}
|
|
||||||
|
|
||||||
.inputTooltip() {
|
|
||||||
position: relative;
|
|
||||||
}
|
|
||||||
|
|
||||||
.inputTooltipText(@textColor: @BaseLight, @backgroundColor: @BaseHigh) {
|
|
||||||
background-color: @backgroundColor;
|
|
||||||
color: @textColor;
|
|
||||||
position: absolute;
|
|
||||||
z-index: 1;
|
|
||||||
padding: @MediumSpace;
|
|
||||||
}
|
|
||||||
|
|
||||||
.inputTooltipTextAfter(@color: @BaseDark) {
|
|
||||||
content: "";
|
|
||||||
position: absolute;
|
|
||||||
right: 100%;
|
|
||||||
border-style: solid;
|
|
||||||
border-color: transparent @color transparent transparent;
|
|
||||||
left: 10px;
|
|
||||||
width: 0;
|
|
||||||
height: 0;
|
|
||||||
border-color: @InfoPointerColor transparent;
|
|
||||||
}
|
}
|
||||||
|
|||||||
3825
less/documentDB.less
3825
less/documentDB.less
File diff suppressed because it is too large
Load Diff
@@ -13,11 +13,6 @@
|
|||||||
@NavMediumSpace: 10px;
|
@NavMediumSpace: 10px;
|
||||||
@NavLargeSpace: 15px;
|
@NavLargeSpace: 15px;
|
||||||
|
|
||||||
.skip-link {
|
|
||||||
position: fixed;
|
|
||||||
top: -200px;
|
|
||||||
}
|
|
||||||
|
|
||||||
html {
|
html {
|
||||||
font-family: wf_segoe-ui_normal, "Segoe UI", "Segoe WP", Tahoma, Arial, sans-serif;
|
font-family: wf_segoe-ui_normal, "Segoe UI", "Segoe WP", Tahoma, Arial, sans-serif;
|
||||||
padding: 0px;
|
padding: 0px;
|
||||||
|
|||||||
@@ -1,12 +1,20 @@
|
|||||||
@import "./Common/Constants";
|
@import "./Common/Constants";
|
||||||
|
|
||||||
|
.main {
|
||||||
|
width: 100%;
|
||||||
|
float: left;
|
||||||
|
transition: all .0s ease-in-out;
|
||||||
|
-ms-transition: all 0s ease-in-out;
|
||||||
|
-webkit-transition: all 0s ease-in-out;
|
||||||
|
-moz-transition: all .0s ease-in-out;
|
||||||
|
height: 100%;
|
||||||
|
background-color: white;
|
||||||
|
border-left: 0px solid white;
|
||||||
|
}
|
||||||
|
|
||||||
.resourceTree {
|
.resourceTree {
|
||||||
height: 100%;
|
height: 100%;
|
||||||
flex: 0 0 auto;
|
flex: 0 0 auto;
|
||||||
.main {
|
|
||||||
height: 100%;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.resourceTreeScroll {
|
.resourceTreeScroll {
|
||||||
|
|||||||
280
package-lock.json
generated
280
package-lock.json
generated
@@ -275,6 +275,27 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"@azure/msal-browser": {
|
||||||
|
"version": "2.8.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/@azure/msal-browser/-/msal-browser-2.8.0.tgz",
|
||||||
|
"integrity": "sha512-I6n7EQnwsZXgKPOLlS5X48jhzUNUFwMVm180wDBA/pwEkUy8ei6zWiPMBfWaMSxz9uNx9WHaEhgAyhJy0ze3AQ==",
|
||||||
|
"requires": {
|
||||||
|
"@azure/msal-common": "^2.0.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"@azure/msal-common": {
|
||||||
|
"version": "2.0.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/@azure/msal-common/-/msal-common-2.0.0.tgz",
|
||||||
|
"integrity": "sha512-d1RNcJb+P1EGzMHtgbZoVlHLQWjlVfr504jywNk9YEfoq8Hw3BxJ0wepu+1w0hc64D8zG0wljcvHaIH1jTn2SA==",
|
||||||
|
"requires": {
|
||||||
|
"debug": "^4.1.1"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"@azure/msal-react": {
|
||||||
|
"version": "1.0.0-alpha.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/@azure/msal-react/-/msal-react-1.0.0-alpha.1.tgz",
|
||||||
|
"integrity": "sha512-8BftMP1DyXf7/Fa7mxi14/fmHBdDGDUONmE8sm1T6w7ERJyY1RN7PZgdnUOcYcqj2xMnxfz9++8HsrzMrtMc0Q=="
|
||||||
|
},
|
||||||
"@babel/code-frame": {
|
"@babel/code-frame": {
|
||||||
"version": "7.10.4",
|
"version": "7.10.4",
|
||||||
"resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.10.4.tgz",
|
"resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.10.4.tgz",
|
||||||
@@ -393,6 +414,7 @@
|
|||||||
"version": "7.12.1",
|
"version": "7.12.1",
|
||||||
"resolved": "https://registry.npmjs.org/@babel/helper-create-class-features-plugin/-/helper-create-class-features-plugin-7.12.1.tgz",
|
"resolved": "https://registry.npmjs.org/@babel/helper-create-class-features-plugin/-/helper-create-class-features-plugin-7.12.1.tgz",
|
||||||
"integrity": "sha512-hkL++rWeta/OVOBTRJc9a5Azh5mt5WgZUGAKMD8JM141YsE08K//bp1unBBieO6rUKkIPyUE0USQ30jAy3Sk1w==",
|
"integrity": "sha512-hkL++rWeta/OVOBTRJc9a5Azh5mt5WgZUGAKMD8JM141YsE08K//bp1unBBieO6rUKkIPyUE0USQ30jAy3Sk1w==",
|
||||||
|
"dev": true,
|
||||||
"requires": {
|
"requires": {
|
||||||
"@babel/helper-function-name": "^7.10.4",
|
"@babel/helper-function-name": "^7.10.4",
|
||||||
"@babel/helper-member-expression-to-functions": "^7.12.1",
|
"@babel/helper-member-expression-to-functions": "^7.12.1",
|
||||||
@@ -619,25 +641,6 @@
|
|||||||
"@babel/plugin-syntax-async-generators": "^7.8.0"
|
"@babel/plugin-syntax-async-generators": "^7.8.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"@babel/plugin-proposal-class-properties": {
|
|
||||||
"version": "7.12.1",
|
|
||||||
"resolved": "https://registry.npmjs.org/@babel/plugin-proposal-class-properties/-/plugin-proposal-class-properties-7.12.1.tgz",
|
|
||||||
"integrity": "sha512-cKp3dlQsFsEs5CWKnN7BnSHOd0EOW8EKpEjkoz1pO2E5KzIDNV9Ros1b0CnmbVgAGXJubOYVBOGCT1OmJwOI7w==",
|
|
||||||
"requires": {
|
|
||||||
"@babel/helper-create-class-features-plugin": "^7.12.1",
|
|
||||||
"@babel/helper-plugin-utils": "^7.10.4"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"@babel/plugin-proposal-decorators": {
|
|
||||||
"version": "7.12.12",
|
|
||||||
"resolved": "https://registry.npmjs.org/@babel/plugin-proposal-decorators/-/plugin-proposal-decorators-7.12.12.tgz",
|
|
||||||
"integrity": "sha512-fhkE9lJYpw2mjHelBpM2zCbaA11aov2GJs7q4cFaXNrWx0H3bW58H9Esy2rdtYOghFBEYUDRIpvlgi+ZD+AvvQ==",
|
|
||||||
"requires": {
|
|
||||||
"@babel/helper-create-class-features-plugin": "^7.12.1",
|
|
||||||
"@babel/helper-plugin-utils": "^7.10.4",
|
|
||||||
"@babel/plugin-syntax-decorators": "^7.12.1"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"@babel/plugin-proposal-dynamic-import": {
|
"@babel/plugin-proposal-dynamic-import": {
|
||||||
"version": "7.12.1",
|
"version": "7.12.1",
|
||||||
"resolved": "https://registry.npmjs.org/@babel/plugin-proposal-dynamic-import/-/plugin-proposal-dynamic-import-7.12.1.tgz",
|
"resolved": "https://registry.npmjs.org/@babel/plugin-proposal-dynamic-import/-/plugin-proposal-dynamic-import-7.12.1.tgz",
|
||||||
@@ -747,14 +750,6 @@
|
|||||||
"@babel/helper-plugin-utils": "^7.10.4"
|
"@babel/helper-plugin-utils": "^7.10.4"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"@babel/plugin-syntax-decorators": {
|
|
||||||
"version": "7.12.1",
|
|
||||||
"resolved": "https://registry.npmjs.org/@babel/plugin-syntax-decorators/-/plugin-syntax-decorators-7.12.1.tgz",
|
|
||||||
"integrity": "sha512-ir9YW5daRrTYiy9UJ2TzdNIJEZu8KclVzDcfSt4iEmOtwQ4llPtWInNKJyKnVXp1vE4bbVd5S31M/im3mYMO1w==",
|
|
||||||
"requires": {
|
|
||||||
"@babel/helper-plugin-utils": "^7.10.4"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"@babel/plugin-syntax-dynamic-import": {
|
"@babel/plugin-syntax-dynamic-import": {
|
||||||
"version": "7.8.3",
|
"version": "7.8.3",
|
||||||
"resolved": "https://registry.npmjs.org/@babel/plugin-syntax-dynamic-import/-/plugin-syntax-dynamic-import-7.8.3.tgz",
|
"resolved": "https://registry.npmjs.org/@babel/plugin-syntax-dynamic-import/-/plugin-syntax-dynamic-import-7.8.3.tgz",
|
||||||
@@ -5419,6 +5414,11 @@
|
|||||||
"resolved": "https://registry.npmjs.org/abab/-/abab-2.0.5.tgz",
|
"resolved": "https://registry.npmjs.org/abab/-/abab-2.0.5.tgz",
|
||||||
"integrity": "sha512-9IK9EadsbHo6jLWIpxpR6pL0sazTXV6+SQv25ZB+F7Bj9mJNaOc4nCRabwd5M/JwmUa8idz6Eci6eKfJryPs6Q=="
|
"integrity": "sha512-9IK9EadsbHo6jLWIpxpR6pL0sazTXV6+SQv25ZB+F7Bj9mJNaOc4nCRabwd5M/JwmUa8idz6Eci6eKfJryPs6Q=="
|
||||||
},
|
},
|
||||||
|
"abbrev": {
|
||||||
|
"version": "1.1.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/abbrev/-/abbrev-1.1.1.tgz",
|
||||||
|
"integrity": "sha512-nne9/IiQ/hzIhY6pdDnbBtz7DjPTKrY00P/zvPSm5pOFkl6xuGrGnXn/VtTNNfNtAfZ9/1RtehkszU9qcTii0Q=="
|
||||||
|
},
|
||||||
"abort-controller": {
|
"abort-controller": {
|
||||||
"version": "3.0.0",
|
"version": "3.0.0",
|
||||||
"resolved": "https://registry.npmjs.org/abort-controller/-/abort-controller-3.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/abort-controller/-/abort-controller-3.0.0.tgz",
|
||||||
@@ -5469,12 +5469,6 @@
|
|||||||
"resolved": "https://registry.npmjs.org/acorn-walk/-/acorn-walk-6.2.0.tgz",
|
"resolved": "https://registry.npmjs.org/acorn-walk/-/acorn-walk-6.2.0.tgz",
|
||||||
"integrity": "sha512-7evsyfH1cLOCdAzZAd43Cic04yKydNx0cF+7tiA19p1XnLLPU4dpCQOqpjqwokFe//vS0QqfqqjCS2JkiIs0cA=="
|
"integrity": "sha512-7evsyfH1cLOCdAzZAd43Cic04yKydNx0cF+7tiA19p1XnLLPU4dpCQOqpjqwokFe//vS0QqfqqjCS2JkiIs0cA=="
|
||||||
},
|
},
|
||||||
"adal-angular": {
|
|
||||||
"version": "1.0.15",
|
|
||||||
"resolved": "https://registry.npmjs.org/adal-angular/-/adal-angular-1.0.15.tgz",
|
|
||||||
"integrity": "sha1-8qnvgvNYxEToMUKs5l0yJ6RBBDs=",
|
|
||||||
"dev": true
|
|
||||||
},
|
|
||||||
"agent-base": {
|
"agent-base": {
|
||||||
"version": "6.0.2",
|
"version": "6.0.2",
|
||||||
"resolved": "https://registry.npmjs.org/agent-base/-/agent-base-6.0.2.tgz",
|
"resolved": "https://registry.npmjs.org/agent-base/-/agent-base-6.0.2.tgz",
|
||||||
@@ -5662,7 +5656,6 @@
|
|||||||
"version": "1.1.5",
|
"version": "1.1.5",
|
||||||
"resolved": "https://registry.npmjs.org/are-we-there-yet/-/are-we-there-yet-1.1.5.tgz",
|
"resolved": "https://registry.npmjs.org/are-we-there-yet/-/are-we-there-yet-1.1.5.tgz",
|
||||||
"integrity": "sha512-5hYdAkZlcG8tOLujVDTgCT+uPX0VnpAH28gWsLfzpXYm7wP6mp5Q/gYyR7YQ0cKVJcXJnl3j2kpBan13PtQf6w==",
|
"integrity": "sha512-5hYdAkZlcG8tOLujVDTgCT+uPX0VnpAH28gWsLfzpXYm7wP6mp5Q/gYyR7YQ0cKVJcXJnl3j2kpBan13PtQf6w==",
|
||||||
"optional": true,
|
|
||||||
"requires": {
|
"requires": {
|
||||||
"delegates": "^1.0.0",
|
"delegates": "^1.0.0",
|
||||||
"readable-stream": "^2.0.6"
|
"readable-stream": "^2.0.6"
|
||||||
@@ -6905,7 +6898,14 @@
|
|||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
"canvas": {
|
"canvas": {
|
||||||
"version": "file:canvas"
|
"version": "2.6.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/canvas/-/canvas-2.6.1.tgz",
|
||||||
|
"integrity": "sha512-S98rKsPcuhfTcYbtF53UIJhcbgIAK533d1kJKMwsMwAIFgfd58MOyxRud3kktlzWiEkFliaJtvyZCBtud/XVEA==",
|
||||||
|
"requires": {
|
||||||
|
"nan": "^2.14.0",
|
||||||
|
"node-pre-gyp": "^0.11.0",
|
||||||
|
"simple-get": "^3.0.3"
|
||||||
|
}
|
||||||
},
|
},
|
||||||
"capture-exit": {
|
"capture-exit": {
|
||||||
"version": "2.0.0",
|
"version": "2.0.0",
|
||||||
@@ -7469,8 +7469,7 @@
|
|||||||
"console-control-strings": {
|
"console-control-strings": {
|
||||||
"version": "1.1.0",
|
"version": "1.1.0",
|
||||||
"resolved": "https://registry.npmjs.org/console-control-strings/-/console-control-strings-1.1.0.tgz",
|
"resolved": "https://registry.npmjs.org/console-control-strings/-/console-control-strings-1.1.0.tgz",
|
||||||
"integrity": "sha1-PXz0Rk22RG6mRL9LOVB/mFEAjo4=",
|
"integrity": "sha1-PXz0Rk22RG6mRL9LOVB/mFEAjo4="
|
||||||
"optional": true
|
|
||||||
},
|
},
|
||||||
"constants-browserify": {
|
"constants-browserify": {
|
||||||
"version": "1.0.0",
|
"version": "1.0.0",
|
||||||
@@ -8451,7 +8450,6 @@
|
|||||||
"version": "4.2.1",
|
"version": "4.2.1",
|
||||||
"resolved": "https://registry.npmjs.org/decompress-response/-/decompress-response-4.2.1.tgz",
|
"resolved": "https://registry.npmjs.org/decompress-response/-/decompress-response-4.2.1.tgz",
|
||||||
"integrity": "sha512-jOSne2qbyE+/r8G1VU+G/82LBs2Fs4LAsTiLSHOCOMZQl2OKZ6i8i4IyHemTe+/yIXOtTcRQMzPcgyhoFlqPkw==",
|
"integrity": "sha512-jOSne2qbyE+/r8G1VU+G/82LBs2Fs4LAsTiLSHOCOMZQl2OKZ6i8i4IyHemTe+/yIXOtTcRQMzPcgyhoFlqPkw==",
|
||||||
"optional": true,
|
|
||||||
"requires": {
|
"requires": {
|
||||||
"mimic-response": "^2.0.0"
|
"mimic-response": "^2.0.0"
|
||||||
}
|
}
|
||||||
@@ -8477,8 +8475,7 @@
|
|||||||
"deep-extend": {
|
"deep-extend": {
|
||||||
"version": "0.6.0",
|
"version": "0.6.0",
|
||||||
"resolved": "https://registry.npmjs.org/deep-extend/-/deep-extend-0.6.0.tgz",
|
"resolved": "https://registry.npmjs.org/deep-extend/-/deep-extend-0.6.0.tgz",
|
||||||
"integrity": "sha512-LOHxIOaPYdHlJRtCQfDIVZtfw/ufM8+rVj649RIHzcm/vGwQRXFt6OPqIFWsm2XEMrNIEtWR64sY1LEKD2vAOA==",
|
"integrity": "sha512-LOHxIOaPYdHlJRtCQfDIVZtfw/ufM8+rVj649RIHzcm/vGwQRXFt6OPqIFWsm2XEMrNIEtWR64sY1LEKD2vAOA=="
|
||||||
"optional": true
|
|
||||||
},
|
},
|
||||||
"deep-is": {
|
"deep-is": {
|
||||||
"version": "0.1.3",
|
"version": "0.1.3",
|
||||||
@@ -8670,8 +8667,7 @@
|
|||||||
"delegates": {
|
"delegates": {
|
||||||
"version": "1.0.0",
|
"version": "1.0.0",
|
||||||
"resolved": "https://registry.npmjs.org/delegates/-/delegates-1.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/delegates/-/delegates-1.0.0.tgz",
|
||||||
"integrity": "sha1-hMbhWbgZBP3KWaDvRM2HDTElD5o=",
|
"integrity": "sha1-hMbhWbgZBP3KWaDvRM2HDTElD5o="
|
||||||
"optional": true
|
|
||||||
},
|
},
|
||||||
"depd": {
|
"depd": {
|
||||||
"version": "1.1.2",
|
"version": "1.1.2",
|
||||||
@@ -8707,8 +8703,7 @@
|
|||||||
"detect-libc": {
|
"detect-libc": {
|
||||||
"version": "1.0.3",
|
"version": "1.0.3",
|
||||||
"resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-1.0.3.tgz",
|
"resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-1.0.3.tgz",
|
||||||
"integrity": "sha1-+hN8S9aY7fVc1c0CrFWfkaTEups=",
|
"integrity": "sha1-+hN8S9aY7fVc1c0CrFWfkaTEups="
|
||||||
"optional": true
|
|
||||||
},
|
},
|
||||||
"detect-newline": {
|
"detect-newline": {
|
||||||
"version": "2.1.0",
|
"version": "2.1.0",
|
||||||
@@ -10694,6 +10689,14 @@
|
|||||||
"universalify": "^0.1.0"
|
"universalify": "^0.1.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"fs-minipass": {
|
||||||
|
"version": "1.2.7",
|
||||||
|
"resolved": "https://registry.npmjs.org/fs-minipass/-/fs-minipass-1.2.7.tgz",
|
||||||
|
"integrity": "sha512-GWSSJGFy4e9GUeCcbIkED+bgAoFyj7XF1mV8rma3QW4NIqX9Kyx79N/PF61H5udOV3aY1IaMLs6pGbH71nlCTA==",
|
||||||
|
"requires": {
|
||||||
|
"minipass": "^2.6.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
"fs-observable": {
|
"fs-observable": {
|
||||||
"version": "4.1.14",
|
"version": "4.1.14",
|
||||||
"resolved": "https://registry.npmjs.org/fs-observable/-/fs-observable-4.1.14.tgz",
|
"resolved": "https://registry.npmjs.org/fs-observable/-/fs-observable-4.1.14.tgz",
|
||||||
@@ -10835,7 +10838,6 @@
|
|||||||
"version": "2.7.4",
|
"version": "2.7.4",
|
||||||
"resolved": "https://registry.npmjs.org/gauge/-/gauge-2.7.4.tgz",
|
"resolved": "https://registry.npmjs.org/gauge/-/gauge-2.7.4.tgz",
|
||||||
"integrity": "sha1-LANAXHU4w51+s3sxcCLjJfsBi/c=",
|
"integrity": "sha1-LANAXHU4w51+s3sxcCLjJfsBi/c=",
|
||||||
"optional": true,
|
|
||||||
"requires": {
|
"requires": {
|
||||||
"aproba": "^1.0.3",
|
"aproba": "^1.0.3",
|
||||||
"console-control-strings": "^1.0.0",
|
"console-control-strings": "^1.0.0",
|
||||||
@@ -10850,14 +10852,12 @@
|
|||||||
"ansi-regex": {
|
"ansi-regex": {
|
||||||
"version": "2.1.1",
|
"version": "2.1.1",
|
||||||
"resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz",
|
"resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz",
|
||||||
"integrity": "sha1-w7M6te42DYbg5ijwRorn7yfWVN8=",
|
"integrity": "sha1-w7M6te42DYbg5ijwRorn7yfWVN8="
|
||||||
"optional": true
|
|
||||||
},
|
},
|
||||||
"is-fullwidth-code-point": {
|
"is-fullwidth-code-point": {
|
||||||
"version": "1.0.0",
|
"version": "1.0.0",
|
||||||
"resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-1.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-1.0.0.tgz",
|
||||||
"integrity": "sha1-754xOG8DGn8NZDr4L95QxFfvAMs=",
|
"integrity": "sha1-754xOG8DGn8NZDr4L95QxFfvAMs=",
|
||||||
"optional": true,
|
|
||||||
"requires": {
|
"requires": {
|
||||||
"number-is-nan": "^1.0.0"
|
"number-is-nan": "^1.0.0"
|
||||||
}
|
}
|
||||||
@@ -10866,7 +10866,6 @@
|
|||||||
"version": "1.0.2",
|
"version": "1.0.2",
|
||||||
"resolved": "https://registry.npmjs.org/string-width/-/string-width-1.0.2.tgz",
|
"resolved": "https://registry.npmjs.org/string-width/-/string-width-1.0.2.tgz",
|
||||||
"integrity": "sha1-EYvfW4zcUaKn5w0hHgfisLmxB9M=",
|
"integrity": "sha1-EYvfW4zcUaKn5w0hHgfisLmxB9M=",
|
||||||
"optional": true,
|
|
||||||
"requires": {
|
"requires": {
|
||||||
"code-point-at": "^1.0.0",
|
"code-point-at": "^1.0.0",
|
||||||
"is-fullwidth-code-point": "^1.0.0",
|
"is-fullwidth-code-point": "^1.0.0",
|
||||||
@@ -10877,7 +10876,6 @@
|
|||||||
"version": "3.0.1",
|
"version": "3.0.1",
|
||||||
"resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz",
|
"resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz",
|
||||||
"integrity": "sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8=",
|
"integrity": "sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8=",
|
||||||
"optional": true,
|
|
||||||
"requires": {
|
"requires": {
|
||||||
"ansi-regex": "^2.0.0"
|
"ansi-regex": "^2.0.0"
|
||||||
}
|
}
|
||||||
@@ -11367,8 +11365,7 @@
|
|||||||
"has-unicode": {
|
"has-unicode": {
|
||||||
"version": "2.0.1",
|
"version": "2.0.1",
|
||||||
"resolved": "https://registry.npmjs.org/has-unicode/-/has-unicode-2.0.1.tgz",
|
"resolved": "https://registry.npmjs.org/has-unicode/-/has-unicode-2.0.1.tgz",
|
||||||
"integrity": "sha1-4Ob+aijPUROIVeCG0Wkedx3iqLk=",
|
"integrity": "sha1-4Ob+aijPUROIVeCG0Wkedx3iqLk="
|
||||||
"optional": true
|
|
||||||
},
|
},
|
||||||
"has-value": {
|
"has-value": {
|
||||||
"version": "1.0.0",
|
"version": "1.0.0",
|
||||||
@@ -11850,6 +11847,14 @@
|
|||||||
"resolved": "https://registry.npmjs.org/ignore/-/ignore-5.1.8.tgz",
|
"resolved": "https://registry.npmjs.org/ignore/-/ignore-5.1.8.tgz",
|
||||||
"integrity": "sha512-BMpfD7PpiETpBl/A6S498BaIJ6Y/ABT93ETbby2fP00v4EbvPBXWEoaR1UBPKs3iR53pJY7EtZk5KACI57i1Uw=="
|
"integrity": "sha512-BMpfD7PpiETpBl/A6S498BaIJ6Y/ABT93ETbby2fP00v4EbvPBXWEoaR1UBPKs3iR53pJY7EtZk5KACI57i1Uw=="
|
||||||
},
|
},
|
||||||
|
"ignore-walk": {
|
||||||
|
"version": "3.0.3",
|
||||||
|
"resolved": "https://registry.npmjs.org/ignore-walk/-/ignore-walk-3.0.3.tgz",
|
||||||
|
"integrity": "sha512-m7o6xuOaT1aqheYHKf8W6J5pYH85ZI9w077erOzLje3JsB1gkafkAhHHY19dqjulgIZHFm32Cp5uNZgcQqdJKw==",
|
||||||
|
"requires": {
|
||||||
|
"minimatch": "^3.0.4"
|
||||||
|
}
|
||||||
|
},
|
||||||
"image-size": {
|
"image-size": {
|
||||||
"version": "0.5.5",
|
"version": "0.5.5",
|
||||||
"resolved": "https://registry.npmjs.org/image-size/-/image-size-0.5.5.tgz",
|
"resolved": "https://registry.npmjs.org/image-size/-/image-size-0.5.5.tgz",
|
||||||
@@ -15554,8 +15559,7 @@
|
|||||||
"mimic-response": {
|
"mimic-response": {
|
||||||
"version": "2.1.0",
|
"version": "2.1.0",
|
||||||
"resolved": "https://registry.npmjs.org/mimic-response/-/mimic-response-2.1.0.tgz",
|
"resolved": "https://registry.npmjs.org/mimic-response/-/mimic-response-2.1.0.tgz",
|
||||||
"integrity": "sha512-wXqjST+SLt7R009ySCglWBCFpjUygmCIfD790/kVbiGmUgfYGuB14PiTd5DwVxSV4NcYHjzMkoj5LjQZwTQLEA==",
|
"integrity": "sha512-wXqjST+SLt7R009ySCglWBCFpjUygmCIfD790/kVbiGmUgfYGuB14PiTd5DwVxSV4NcYHjzMkoj5LjQZwTQLEA=="
|
||||||
"optional": true
|
|
||||||
},
|
},
|
||||||
"min-document": {
|
"min-document": {
|
||||||
"version": "2.19.0",
|
"version": "2.19.0",
|
||||||
@@ -15612,6 +15616,15 @@
|
|||||||
"resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.5.tgz",
|
"resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.5.tgz",
|
||||||
"integrity": "sha512-FM9nNUYrRBAELZQT3xeZQ7fmMOBg6nWNmJKTcgsJeaLstP/UODVpGsr5OhXhhXg6f+qtJ8uiZ+PUxkDWcgIXLw=="
|
"integrity": "sha512-FM9nNUYrRBAELZQT3xeZQ7fmMOBg6nWNmJKTcgsJeaLstP/UODVpGsr5OhXhhXg6f+qtJ8uiZ+PUxkDWcgIXLw=="
|
||||||
},
|
},
|
||||||
|
"minipass": {
|
||||||
|
"version": "2.9.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/minipass/-/minipass-2.9.0.tgz",
|
||||||
|
"integrity": "sha512-wxfUjg9WebH+CUDX/CdbRlh5SmfZiy/hpkxaRI16Y9W56Pa75sWgd/rvFilSgrauD9NyFymP/+JFV3KwzIsJeg==",
|
||||||
|
"requires": {
|
||||||
|
"safe-buffer": "^5.1.2",
|
||||||
|
"yallist": "^3.0.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
"minipass-collect": {
|
"minipass-collect": {
|
||||||
"version": "1.0.2",
|
"version": "1.0.2",
|
||||||
"resolved": "https://registry.npmjs.org/minipass-collect/-/minipass-collect-1.0.2.tgz",
|
"resolved": "https://registry.npmjs.org/minipass-collect/-/minipass-collect-1.0.2.tgz",
|
||||||
@@ -15681,6 +15694,14 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"minizlib": {
|
||||||
|
"version": "1.3.3",
|
||||||
|
"resolved": "https://registry.npmjs.org/minizlib/-/minizlib-1.3.3.tgz",
|
||||||
|
"integrity": "sha512-6ZYMOEnmVsdCeTJVE0W9ZD+pVnE8h9Hma/iOwwRDsdQoePpoX56/8B6z3P9VNwppJuBKNRuFDRNRqRWexT9G9Q==",
|
||||||
|
"requires": {
|
||||||
|
"minipass": "^2.9.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
"mississippi": {
|
"mississippi": {
|
||||||
"version": "3.0.0",
|
"version": "3.0.0",
|
||||||
"resolved": "https://registry.npmjs.org/mississippi/-/mississippi-3.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/mississippi/-/mississippi-3.0.0.tgz",
|
||||||
@@ -15855,8 +15876,7 @@
|
|||||||
"nan": {
|
"nan": {
|
||||||
"version": "2.14.2",
|
"version": "2.14.2",
|
||||||
"resolved": "https://registry.npmjs.org/nan/-/nan-2.14.2.tgz",
|
"resolved": "https://registry.npmjs.org/nan/-/nan-2.14.2.tgz",
|
||||||
"integrity": "sha512-M2ufzIiINKCuDfBSAUr1vWQ+vuVcA9kqx8JJUsbQi6yf1uGRyb7HfpdfUr5qLXf3B/t8dPvcjhKMmlfnP47EzQ==",
|
"integrity": "sha512-M2ufzIiINKCuDfBSAUr1vWQ+vuVcA9kqx8JJUsbQi6yf1uGRyb7HfpdfUr5qLXf3B/t8dPvcjhKMmlfnP47EzQ=="
|
||||||
"optional": true
|
|
||||||
},
|
},
|
||||||
"nanomatch": {
|
"nanomatch": {
|
||||||
"version": "1.2.13",
|
"version": "1.2.13",
|
||||||
@@ -15906,6 +15926,26 @@
|
|||||||
"semver": "^5.4.1"
|
"semver": "^5.4.1"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"needle": {
|
||||||
|
"version": "2.5.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/needle/-/needle-2.5.2.tgz",
|
||||||
|
"integrity": "sha512-LbRIwS9BfkPvNwNHlsA41Q29kL2L/6VaOJ0qisM5lLWsTV3nP15abO5ITL6L81zqFhzjRKDAYjpcBcwM0AVvLQ==",
|
||||||
|
"requires": {
|
||||||
|
"debug": "^3.2.6",
|
||||||
|
"iconv-lite": "^0.4.4",
|
||||||
|
"sax": "^1.2.4"
|
||||||
|
},
|
||||||
|
"dependencies": {
|
||||||
|
"debug": {
|
||||||
|
"version": "3.2.6",
|
||||||
|
"resolved": "https://registry.npmjs.org/debug/-/debug-3.2.6.tgz",
|
||||||
|
"integrity": "sha512-mel+jf7nrtEl5Pn1Qx46zARXKDpBbvzezse7p7LqINmdoIk8PYP5SySaxEmYv6TZ0JyEKA1hsCId6DIhgITtWQ==",
|
||||||
|
"requires": {
|
||||||
|
"ms": "^2.1.1"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
"negotiator": {
|
"negotiator": {
|
||||||
"version": "0.6.2",
|
"version": "0.6.2",
|
||||||
"resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.2.tgz",
|
"resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.2.tgz",
|
||||||
@@ -16074,6 +16114,41 @@
|
|||||||
"which": "^1.3.0"
|
"which": "^1.3.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node-pre-gyp": {
|
||||||
|
"version": "0.11.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/node-pre-gyp/-/node-pre-gyp-0.11.0.tgz",
|
||||||
|
"integrity": "sha512-TwWAOZb0j7e9eGaf9esRx3ZcLaE5tQ2lvYy1pb5IAaG1a2e2Kv5Lms1Y4hpj+ciXJRofIxxlt5haeQ/2ANeE0Q==",
|
||||||
|
"requires": {
|
||||||
|
"detect-libc": "^1.0.2",
|
||||||
|
"mkdirp": "^0.5.1",
|
||||||
|
"needle": "^2.2.1",
|
||||||
|
"nopt": "^4.0.1",
|
||||||
|
"npm-packlist": "^1.1.6",
|
||||||
|
"npmlog": "^4.0.2",
|
||||||
|
"rc": "^1.2.7",
|
||||||
|
"rimraf": "^2.6.1",
|
||||||
|
"semver": "^5.3.0",
|
||||||
|
"tar": "^4"
|
||||||
|
},
|
||||||
|
"dependencies": {
|
||||||
|
"mkdirp": {
|
||||||
|
"version": "0.5.5",
|
||||||
|
"resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.5.tgz",
|
||||||
|
"integrity": "sha512-NKmAlESf6jMGym1++R0Ra7wvhV+wFW63FaSOFPwRahvea0gMUcGUhVeAg/0BC0wiv9ih5NYPB1Wn1UEI1/L+xQ==",
|
||||||
|
"requires": {
|
||||||
|
"minimist": "^1.2.5"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"rimraf": {
|
||||||
|
"version": "2.7.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.7.1.tgz",
|
||||||
|
"integrity": "sha512-uWjbaKIK3T1OSVptzX7Nl6PvQ3qAGtKEtVRjRuazjfL3Bx5eI409VZSqgND+4UNnmzLVdPj9FqFJNPqBZFve4w==",
|
||||||
|
"requires": {
|
||||||
|
"glob": "^7.1.3"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
"node-releases": {
|
"node-releases": {
|
||||||
"version": "1.1.66",
|
"version": "1.1.66",
|
||||||
"resolved": "https://registry.npmjs.org/node-releases/-/node-releases-1.1.66.tgz",
|
"resolved": "https://registry.npmjs.org/node-releases/-/node-releases-1.1.66.tgz",
|
||||||
@@ -16086,6 +16161,15 @@
|
|||||||
"integrity": "sha1-lKKxYzxPExdVMAfYlm/Q6EG2pMI=",
|
"integrity": "sha1-lKKxYzxPExdVMAfYlm/Q6EG2pMI=",
|
||||||
"optional": true
|
"optional": true
|
||||||
},
|
},
|
||||||
|
"nopt": {
|
||||||
|
"version": "4.0.3",
|
||||||
|
"resolved": "https://registry.npmjs.org/nopt/-/nopt-4.0.3.tgz",
|
||||||
|
"integrity": "sha512-CvaGwVMztSMJLOeXPrez7fyfObdZqNUK1cPAEzLHrTybIua9pMdmmPR5YwtfNftIOMv3DPUhFaxsZMNTQO20Kg==",
|
||||||
|
"requires": {
|
||||||
|
"abbrev": "1",
|
||||||
|
"osenv": "^0.1.4"
|
||||||
|
}
|
||||||
|
},
|
||||||
"normalize-package-data": {
|
"normalize-package-data": {
|
||||||
"version": "2.5.0",
|
"version": "2.5.0",
|
||||||
"resolved": "https://registry.npmjs.org/normalize-package-data/-/normalize-package-data-2.5.0.tgz",
|
"resolved": "https://registry.npmjs.org/normalize-package-data/-/normalize-package-data-2.5.0.tgz",
|
||||||
@@ -16110,6 +16194,29 @@
|
|||||||
"resolved": "https://registry.npmjs.org/normalize.css/-/normalize.css-8.0.1.tgz",
|
"resolved": "https://registry.npmjs.org/normalize.css/-/normalize.css-8.0.1.tgz",
|
||||||
"integrity": "sha512-qizSNPO93t1YUuUhP22btGOo3chcvDFqFaj2TRybP0DMxkHOCTYwp3n34fel4a31ORXy4m1Xq0Gyqpb5m33qIg=="
|
"integrity": "sha512-qizSNPO93t1YUuUhP22btGOo3chcvDFqFaj2TRybP0DMxkHOCTYwp3n34fel4a31ORXy4m1Xq0Gyqpb5m33qIg=="
|
||||||
},
|
},
|
||||||
|
"npm-bundled": {
|
||||||
|
"version": "1.1.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/npm-bundled/-/npm-bundled-1.1.1.tgz",
|
||||||
|
"integrity": "sha512-gqkfgGePhTpAEgUsGEgcq1rqPXA+tv/aVBlgEzfXwA1yiUJF7xtEt3CtVwOjNYQOVknDk0F20w58Fnm3EtG0fA==",
|
||||||
|
"requires": {
|
||||||
|
"npm-normalize-package-bin": "^1.0.1"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"npm-normalize-package-bin": {
|
||||||
|
"version": "1.0.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/npm-normalize-package-bin/-/npm-normalize-package-bin-1.0.1.tgz",
|
||||||
|
"integrity": "sha512-EPfafl6JL5/rU+ot6P3gRSCpPDW5VmIzX959Ob1+ySFUuuYHWHekXpwdUZcKP5C+DS4GEtdJluwBjnsNDl+fSA=="
|
||||||
|
},
|
||||||
|
"npm-packlist": {
|
||||||
|
"version": "1.4.8",
|
||||||
|
"resolved": "https://registry.npmjs.org/npm-packlist/-/npm-packlist-1.4.8.tgz",
|
||||||
|
"integrity": "sha512-5+AZgwru5IevF5ZdnFglB5wNlHG1AOOuw28WhUq8/8emhBmLv6jX5by4WJCh7lW0uSYZYS6DXqIsyZVIXRZU9A==",
|
||||||
|
"requires": {
|
||||||
|
"ignore-walk": "^3.0.1",
|
||||||
|
"npm-bundled": "^1.0.1",
|
||||||
|
"npm-normalize-package-bin": "^1.0.1"
|
||||||
|
}
|
||||||
|
},
|
||||||
"npm-run-path": {
|
"npm-run-path": {
|
||||||
"version": "2.0.2",
|
"version": "2.0.2",
|
||||||
"resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-2.0.2.tgz",
|
"resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-2.0.2.tgz",
|
||||||
@@ -16122,7 +16229,6 @@
|
|||||||
"version": "4.1.2",
|
"version": "4.1.2",
|
||||||
"resolved": "https://registry.npmjs.org/npmlog/-/npmlog-4.1.2.tgz",
|
"resolved": "https://registry.npmjs.org/npmlog/-/npmlog-4.1.2.tgz",
|
||||||
"integrity": "sha512-2uUqazuKlTaSI/dC8AzicUck7+IrEaOnN/e0jd3Xtt1KcGpwx30v50mL7oPyr/h9bL3E4aZccVwpwP+5W9Vjkg==",
|
"integrity": "sha512-2uUqazuKlTaSI/dC8AzicUck7+IrEaOnN/e0jd3Xtt1KcGpwx30v50mL7oPyr/h9bL3E4aZccVwpwP+5W9Vjkg==",
|
||||||
"optional": true,
|
|
||||||
"requires": {
|
"requires": {
|
||||||
"are-we-there-yet": "~1.1.2",
|
"are-we-there-yet": "~1.1.2",
|
||||||
"console-control-strings": "~1.1.0",
|
"console-control-strings": "~1.1.0",
|
||||||
@@ -16514,8 +16620,7 @@
|
|||||||
"os-homedir": {
|
"os-homedir": {
|
||||||
"version": "1.0.2",
|
"version": "1.0.2",
|
||||||
"resolved": "https://registry.npmjs.org/os-homedir/-/os-homedir-1.0.2.tgz",
|
"resolved": "https://registry.npmjs.org/os-homedir/-/os-homedir-1.0.2.tgz",
|
||||||
"integrity": "sha1-/7xJiDNuDoM94MFox+8VISGqf7M=",
|
"integrity": "sha1-/7xJiDNuDoM94MFox+8VISGqf7M="
|
||||||
"dev": true
|
|
||||||
},
|
},
|
||||||
"os-locale": {
|
"os-locale": {
|
||||||
"version": "1.4.0",
|
"version": "1.4.0",
|
||||||
@@ -16534,6 +16639,20 @@
|
|||||||
"windows-release": "^3.1.0"
|
"windows-release": "^3.1.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"os-tmpdir": {
|
||||||
|
"version": "1.0.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/os-tmpdir/-/os-tmpdir-1.0.2.tgz",
|
||||||
|
"integrity": "sha1-u+Z0BseaqFxc/sdm/lc0VV36EnQ="
|
||||||
|
},
|
||||||
|
"osenv": {
|
||||||
|
"version": "0.1.5",
|
||||||
|
"resolved": "https://registry.npmjs.org/osenv/-/osenv-0.1.5.tgz",
|
||||||
|
"integrity": "sha512-0CWcCECdMVc2Rw3U5w9ZjqX6ga6ubk1xDVKxtBQPK7wis/0F2r9T6k4ydGYhecl7YUBxBVxhL5oisPsNxAPe2g==",
|
||||||
|
"requires": {
|
||||||
|
"os-homedir": "^1.0.0",
|
||||||
|
"os-tmpdir": "^1.0.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
"p-defer": {
|
"p-defer": {
|
||||||
"version": "1.0.0",
|
"version": "1.0.0",
|
||||||
"resolved": "https://registry.npmjs.org/p-defer/-/p-defer-1.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/p-defer/-/p-defer-1.0.0.tgz",
|
||||||
@@ -17586,7 +17705,6 @@
|
|||||||
"version": "1.2.8",
|
"version": "1.2.8",
|
||||||
"resolved": "https://registry.npmjs.org/rc/-/rc-1.2.8.tgz",
|
"resolved": "https://registry.npmjs.org/rc/-/rc-1.2.8.tgz",
|
||||||
"integrity": "sha512-y3bGgqKj3QBdxLbLkomlohkvsA8gdAiUQlSBJnBhfn+BPxg4bc62d8TcBW15wavDfgexCgccckhcZvywyQYPOw==",
|
"integrity": "sha512-y3bGgqKj3QBdxLbLkomlohkvsA8gdAiUQlSBJnBhfn+BPxg4bc62d8TcBW15wavDfgexCgccckhcZvywyQYPOw==",
|
||||||
"optional": true,
|
|
||||||
"requires": {
|
"requires": {
|
||||||
"deep-extend": "^0.6.0",
|
"deep-extend": "^0.6.0",
|
||||||
"ini": "~1.3.0",
|
"ini": "~1.3.0",
|
||||||
@@ -17998,11 +18116,6 @@
|
|||||||
"resolved": "https://registry.npmjs.org/redux-observable/-/redux-observable-2.0.0-alpha.0.tgz",
|
"resolved": "https://registry.npmjs.org/redux-observable/-/redux-observable-2.0.0-alpha.0.tgz",
|
||||||
"integrity": "sha512-w0RsVGprIFiYi1AhFCOATiv3ld2AtuobvbcVsLvX19p8eAwLowWl2OrKYcCq/QEeEpmSHTXutXfVfcBnzaWmdw=="
|
"integrity": "sha512-w0RsVGprIFiYi1AhFCOATiv3ld2AtuobvbcVsLvX19p8eAwLowWl2OrKYcCq/QEeEpmSHTXutXfVfcBnzaWmdw=="
|
||||||
},
|
},
|
||||||
"reflect-metadata": {
|
|
||||||
"version": "0.1.13",
|
|
||||||
"resolved": "https://registry.npmjs.org/reflect-metadata/-/reflect-metadata-0.1.13.tgz",
|
|
||||||
"integrity": "sha512-Ts1Y/anZELhSsjMcU605fU9RE4Oi3p5ORujwbIKXfWa+0Zxs510Qrmrce5/Jowq3cHSZSJqBjypxmHarc+vEWg=="
|
|
||||||
},
|
|
||||||
"reflect.ownkeys": {
|
"reflect.ownkeys": {
|
||||||
"version": "0.2.0",
|
"version": "0.2.0",
|
||||||
"resolved": "https://registry.npmjs.org/reflect.ownkeys/-/reflect.ownkeys-0.2.0.tgz",
|
"resolved": "https://registry.npmjs.org/reflect.ownkeys/-/reflect.ownkeys-0.2.0.tgz",
|
||||||
@@ -19018,14 +19131,12 @@
|
|||||||
"simple-concat": {
|
"simple-concat": {
|
||||||
"version": "1.0.1",
|
"version": "1.0.1",
|
||||||
"resolved": "https://registry.npmjs.org/simple-concat/-/simple-concat-1.0.1.tgz",
|
"resolved": "https://registry.npmjs.org/simple-concat/-/simple-concat-1.0.1.tgz",
|
||||||
"integrity": "sha512-cSFtAPtRhljv69IK0hTVZQ+OfE9nePi/rtJmw5UjHeVyVroEqJXP1sFztKUy1qU+xvz3u/sfYJLa947b7nAN2Q==",
|
"integrity": "sha512-cSFtAPtRhljv69IK0hTVZQ+OfE9nePi/rtJmw5UjHeVyVroEqJXP1sFztKUy1qU+xvz3u/sfYJLa947b7nAN2Q=="
|
||||||
"optional": true
|
|
||||||
},
|
},
|
||||||
"simple-get": {
|
"simple-get": {
|
||||||
"version": "3.1.0",
|
"version": "3.1.0",
|
||||||
"resolved": "https://registry.npmjs.org/simple-get/-/simple-get-3.1.0.tgz",
|
"resolved": "https://registry.npmjs.org/simple-get/-/simple-get-3.1.0.tgz",
|
||||||
"integrity": "sha512-bCR6cP+aTdScaQCnQKbPKtJOKDp/hj9EDLJo3Nw4y1QksqaovlW/bnptB6/c1e+qmNIDHRK+oXFDdEqBT8WzUA==",
|
"integrity": "sha512-bCR6cP+aTdScaQCnQKbPKtJOKDp/hj9EDLJo3Nw4y1QksqaovlW/bnptB6/c1e+qmNIDHRK+oXFDdEqBT8WzUA==",
|
||||||
"optional": true,
|
|
||||||
"requires": {
|
"requires": {
|
||||||
"decompress-response": "^4.2.0",
|
"decompress-response": "^4.2.0",
|
||||||
"once": "^1.3.1",
|
"once": "^1.3.1",
|
||||||
@@ -20017,6 +20128,30 @@
|
|||||||
"integrity": "sha512-4WK/bYZmj8xLr+HUCODHGF1ZFzsYffasLUgEiMBY4fgtltdO6B4WJtlSbPaDTLpYTcGVwM2qLnFTICEcNxs3kA==",
|
"integrity": "sha512-4WK/bYZmj8xLr+HUCODHGF1ZFzsYffasLUgEiMBY4fgtltdO6B4WJtlSbPaDTLpYTcGVwM2qLnFTICEcNxs3kA==",
|
||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
|
"tar": {
|
||||||
|
"version": "4.4.13",
|
||||||
|
"resolved": "https://registry.npmjs.org/tar/-/tar-4.4.13.tgz",
|
||||||
|
"integrity": "sha512-w2VwSrBoHa5BsSyH+KxEqeQBAllHhccyMFVHtGtdMpF4W7IRWfZjFiQceJPChOeTsSDVUpER2T8FA93pr0L+QA==",
|
||||||
|
"requires": {
|
||||||
|
"chownr": "^1.1.1",
|
||||||
|
"fs-minipass": "^1.2.5",
|
||||||
|
"minipass": "^2.8.6",
|
||||||
|
"minizlib": "^1.2.1",
|
||||||
|
"mkdirp": "^0.5.0",
|
||||||
|
"safe-buffer": "^5.1.2",
|
||||||
|
"yallist": "^3.0.3"
|
||||||
|
},
|
||||||
|
"dependencies": {
|
||||||
|
"mkdirp": {
|
||||||
|
"version": "0.5.5",
|
||||||
|
"resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.5.tgz",
|
||||||
|
"integrity": "sha512-NKmAlESf6jMGym1++R0Ra7wvhV+wFW63FaSOFPwRahvea0gMUcGUhVeAg/0BC0wiv9ih5NYPB1Wn1UEI1/L+xQ==",
|
||||||
|
"requires": {
|
||||||
|
"minimist": "^1.2.5"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
"tar-fs": {
|
"tar-fs": {
|
||||||
"version": "2.1.1",
|
"version": "2.1.1",
|
||||||
"resolved": "https://registry.npmjs.org/tar-fs/-/tar-fs-2.1.1.tgz",
|
"resolved": "https://registry.npmjs.org/tar-fs/-/tar-fs-2.1.1.tgz",
|
||||||
@@ -22072,7 +22207,6 @@
|
|||||||
"version": "1.1.3",
|
"version": "1.1.3",
|
||||||
"resolved": "https://registry.npmjs.org/wide-align/-/wide-align-1.1.3.tgz",
|
"resolved": "https://registry.npmjs.org/wide-align/-/wide-align-1.1.3.tgz",
|
||||||
"integrity": "sha512-QGkOQc8XL6Bt5PwnsExKBPuMKBxnGxWWW3fU55Xt4feHozMUhdUMaBCk290qpm/wG5u/RSKzwdAC4i51YigihA==",
|
"integrity": "sha512-QGkOQc8XL6Bt5PwnsExKBPuMKBxnGxWWW3fU55Xt4feHozMUhdUMaBCk290qpm/wG5u/RSKzwdAC4i51YigihA==",
|
||||||
"optional": true,
|
|
||||||
"requires": {
|
"requires": {
|
||||||
"string-width": "^1.0.2 || 2"
|
"string-width": "^1.0.2 || 2"
|
||||||
},
|
},
|
||||||
@@ -22080,14 +22214,12 @@
|
|||||||
"ansi-regex": {
|
"ansi-regex": {
|
||||||
"version": "3.0.0",
|
"version": "3.0.0",
|
||||||
"resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-3.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-3.0.0.tgz",
|
||||||
"integrity": "sha1-7QMXwyIGT3lGbAKWa922Bas32Zg=",
|
"integrity": "sha1-7QMXwyIGT3lGbAKWa922Bas32Zg="
|
||||||
"optional": true
|
|
||||||
},
|
},
|
||||||
"string-width": {
|
"string-width": {
|
||||||
"version": "2.1.1",
|
"version": "2.1.1",
|
||||||
"resolved": "https://registry.npmjs.org/string-width/-/string-width-2.1.1.tgz",
|
"resolved": "https://registry.npmjs.org/string-width/-/string-width-2.1.1.tgz",
|
||||||
"integrity": "sha512-nOqH59deCq9SRHlxq1Aw85Jnt4w6KvLKqWVik6oA9ZklXLNIOlqg4F2yrT1MVaTjAqvVwdfeZ7w7aCvJD7ugkw==",
|
"integrity": "sha512-nOqH59deCq9SRHlxq1Aw85Jnt4w6KvLKqWVik6oA9ZklXLNIOlqg4F2yrT1MVaTjAqvVwdfeZ7w7aCvJD7ugkw==",
|
||||||
"optional": true,
|
|
||||||
"requires": {
|
"requires": {
|
||||||
"is-fullwidth-code-point": "^2.0.0",
|
"is-fullwidth-code-point": "^2.0.0",
|
||||||
"strip-ansi": "^4.0.0"
|
"strip-ansi": "^4.0.0"
|
||||||
@@ -22097,7 +22229,6 @@
|
|||||||
"version": "4.0.0",
|
"version": "4.0.0",
|
||||||
"resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-4.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-4.0.0.tgz",
|
||||||
"integrity": "sha1-qEeQIusaw2iocTibY1JixQXuNo8=",
|
"integrity": "sha1-qEeQIusaw2iocTibY1JixQXuNo8=",
|
||||||
"optional": true,
|
|
||||||
"requires": {
|
"requires": {
|
||||||
"ansi-regex": "^3.0.0"
|
"ansi-regex": "^3.0.0"
|
||||||
}
|
}
|
||||||
@@ -22267,8 +22398,7 @@
|
|||||||
"yallist": {
|
"yallist": {
|
||||||
"version": "3.1.1",
|
"version": "3.1.1",
|
||||||
"resolved": "https://registry.npmjs.org/yallist/-/yallist-3.1.1.tgz",
|
"resolved": "https://registry.npmjs.org/yallist/-/yallist-3.1.1.tgz",
|
||||||
"integrity": "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==",
|
"integrity": "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g=="
|
||||||
"dev": true
|
|
||||||
},
|
},
|
||||||
"yargs": {
|
"yargs": {
|
||||||
"version": "13.3.2",
|
"version": "13.3.2",
|
||||||
|
|||||||
@@ -8,8 +8,8 @@
|
|||||||
"@azure/cosmos": "3.9.0",
|
"@azure/cosmos": "3.9.0",
|
||||||
"@azure/cosmos-language-service": "0.0.5",
|
"@azure/cosmos-language-service": "0.0.5",
|
||||||
"@azure/identity": "1.1.0",
|
"@azure/identity": "1.1.0",
|
||||||
"@babel/plugin-proposal-class-properties": "7.12.1",
|
"@azure/msal-browser": "2.8.0",
|
||||||
"@babel/plugin-proposal-decorators": "7.12.12",
|
"@azure/msal-react": "1.0.0-alpha.1",
|
||||||
"@jupyterlab/services": "6.0.0-rc.2",
|
"@jupyterlab/services": "6.0.0-rc.2",
|
||||||
"@jupyterlab/terminal": "3.0.0-rc.2",
|
"@jupyterlab/terminal": "3.0.0-rc.2",
|
||||||
"@microsoft/applicationinsights-web": "2.5.9",
|
"@microsoft/applicationinsights-web": "2.5.9",
|
||||||
@@ -46,7 +46,7 @@
|
|||||||
"applicationinsights": "1.8.0",
|
"applicationinsights": "1.8.0",
|
||||||
"babel-polyfill": "6.26.0",
|
"babel-polyfill": "6.26.0",
|
||||||
"bootstrap": "3.4.1",
|
"bootstrap": "3.4.1",
|
||||||
"canvas": "file:./canvas",
|
"canvas": "2.6.1",
|
||||||
"clean-webpack-plugin": "0.1.19",
|
"clean-webpack-plugin": "0.1.19",
|
||||||
"copy-webpack-plugin": "6.0.2",
|
"copy-webpack-plugin": "6.0.2",
|
||||||
"crossroads": "0.12.2",
|
"crossroads": "0.12.2",
|
||||||
@@ -87,7 +87,6 @@
|
|||||||
"react-notification-system": "0.2.17",
|
"react-notification-system": "0.2.17",
|
||||||
"react-redux": "7.1.3",
|
"react-redux": "7.1.3",
|
||||||
"redux": "4.0.4",
|
"redux": "4.0.4",
|
||||||
"reflect-metadata": "0.1.13",
|
|
||||||
"rx-jupyter": "5.5.12",
|
"rx-jupyter": "5.5.12",
|
||||||
"rxjs": "6.6.3",
|
"rxjs": "6.6.3",
|
||||||
"styled-components": "4.3.2",
|
"styled-components": "4.3.2",
|
||||||
@@ -131,7 +130,6 @@
|
|||||||
"@types/webfontloader": "1.6.29",
|
"@types/webfontloader": "1.6.29",
|
||||||
"@typescript-eslint/eslint-plugin": "4.0.1",
|
"@typescript-eslint/eslint-plugin": "4.0.1",
|
||||||
"@typescript-eslint/parser": "4.0.1",
|
"@typescript-eslint/parser": "4.0.1",
|
||||||
"adal-angular": "1.0.15",
|
|
||||||
"axe-puppeteer": "1.1.0",
|
"axe-puppeteer": "1.1.0",
|
||||||
"babel-jest": "24.9.0",
|
"babel-jest": "24.9.0",
|
||||||
"babel-loader": "8.1.0",
|
"babel-loader": "8.1.0",
|
||||||
|
|||||||
@@ -1,437 +1,434 @@
|
|||||||
import { HashMap } from "./HashMap";
|
import { HashMap } from "./HashMap";
|
||||||
|
|
||||||
export class AuthorizationEndpoints {
|
export class AuthorizationEndpoints {
|
||||||
public static arm: string = "https://management.core.windows.net/";
|
public static arm: string = "https://management.core.windows.net/";
|
||||||
public static common: string = "https://login.windows.net/";
|
public static common: string = "https://login.windows.net/";
|
||||||
}
|
}
|
||||||
|
|
||||||
export class CodeOfConductEndpoints {
|
export class CodeOfConductEndpoints {
|
||||||
public static privacyStatement: string = "https://aka.ms/ms-privacy-policy";
|
public static privacyStatement: string = "https://aka.ms/ms-privacy-policy";
|
||||||
public static codeOfConduct: string = "https://aka.ms/cosmos-code-of-conduct";
|
public static codeOfConduct: string = "https://aka.ms/cosmos-code-of-conduct";
|
||||||
public static termsOfUse: string = "https://aka.ms/ms-terms-of-use";
|
public static termsOfUse: string = "https://aka.ms/ms-terms-of-use";
|
||||||
}
|
}
|
||||||
|
|
||||||
export class EndpointsRegex {
|
export class EndpointsRegex {
|
||||||
public static readonly cassandra = [
|
public static readonly cassandra = [
|
||||||
"AccountEndpoint=(.*).cassandra.cosmosdb.azure.com",
|
"AccountEndpoint=(.*).cassandra.cosmosdb.azure.com",
|
||||||
"HostName=(.*).cassandra.cosmos.azure.com"
|
"HostName=(.*).cassandra.cosmos.azure.com"
|
||||||
];
|
];
|
||||||
public static readonly mongo = "mongodb://.*:(.*)@(.*).documents.azure.com";
|
public static readonly mongo = "mongodb://.*:(.*)@(.*).documents.azure.com";
|
||||||
public static readonly mongoCompute = "mongodb://.*:(.*)@(.*).mongo.cosmos.azure.com";
|
public static readonly mongoCompute = "mongodb://.*:(.*)@(.*).mongo.cosmos.azure.com";
|
||||||
public static readonly sql = "AccountEndpoint=https://(.*).documents.azure.com";
|
public static readonly sql = "AccountEndpoint=https://(.*).documents.azure.com";
|
||||||
public static readonly table = "TableEndpoint=https://(.*).table.cosmosdb.azure.com";
|
public static readonly table = "TableEndpoint=https://(.*).table.cosmosdb.azure.com";
|
||||||
}
|
}
|
||||||
|
|
||||||
export class ApiEndpoints {
|
export class ApiEndpoints {
|
||||||
public static runtimeProxy: string = "/api/RuntimeProxy";
|
public static runtimeProxy: string = "/api/RuntimeProxy";
|
||||||
public static guestRuntimeProxy: string = "/api/guest/RuntimeProxy";
|
public static guestRuntimeProxy: string = "/api/guest/RuntimeProxy";
|
||||||
}
|
}
|
||||||
|
|
||||||
export class ServerIds {
|
export class ServerIds {
|
||||||
public static localhost: string = "localhost";
|
public static localhost: string = "localhost";
|
||||||
public static blackforest: string = "blackforest";
|
public static blackforest: string = "blackforest";
|
||||||
public static fairfax: string = "fairfax";
|
public static fairfax: string = "fairfax";
|
||||||
public static mooncake: string = "mooncake";
|
public static mooncake: string = "mooncake";
|
||||||
public static productionPortal: string = "prod";
|
public static productionPortal: string = "prod";
|
||||||
public static dev: string = "dev";
|
public static dev: string = "dev";
|
||||||
}
|
}
|
||||||
|
|
||||||
export class ArmApiVersions {
|
export class ArmApiVersions {
|
||||||
public static readonly documentDB: string = "2015-11-06";
|
public static readonly documentDB: string = "2015-11-06";
|
||||||
public static readonly arcadia: string = "2019-06-01-preview";
|
public static readonly arcadia: string = "2019-06-01-preview";
|
||||||
public static readonly arcadiaLivy: string = "2019-11-01-preview";
|
public static readonly arcadiaLivy: string = "2019-11-01-preview";
|
||||||
public static readonly arm: string = "2015-11-01";
|
public static readonly arm: string = "2015-11-01";
|
||||||
public static readonly armFeatures: string = "2014-08-01-preview";
|
public static readonly armFeatures: string = "2014-08-01-preview";
|
||||||
public static readonly publicVersion = "2020-04-01";
|
public static readonly publicVersion = "2020-04-01";
|
||||||
}
|
}
|
||||||
|
|
||||||
export class ArmResourceTypes {
|
export class ArmResourceTypes {
|
||||||
public static readonly notebookWorkspaces = "Microsoft.DocumentDB/databaseAccounts/notebookWorkspaces";
|
public static readonly notebookWorkspaces = "Microsoft.DocumentDB/databaseAccounts/notebookWorkspaces";
|
||||||
public static readonly synapseWorkspaces = "Microsoft.Synapse/workspaces";
|
public static readonly synapseWorkspaces = "Microsoft.Synapse/workspaces";
|
||||||
}
|
}
|
||||||
|
|
||||||
export class BackendDefaults {
|
export class BackendDefaults {
|
||||||
public static partitionKeyKind: string = "Hash";
|
public static partitionKeyKind: string = "Hash";
|
||||||
public static singlePartitionStorageInGb: string = "10";
|
public static singlePartitionStorageInGb: string = "10";
|
||||||
public static multiPartitionStorageInGb: string = "100";
|
public static multiPartitionStorageInGb: string = "100";
|
||||||
public static maxChangeFeedRetentionDuration: number = 10;
|
public static maxChangeFeedRetentionDuration: number = 10;
|
||||||
public static partitionKeyVersion = 2;
|
public static partitionKeyVersion = 2;
|
||||||
}
|
}
|
||||||
|
|
||||||
export class ClientDefaults {
|
export class ClientDefaults {
|
||||||
public static requestTimeoutMs: number = 60000;
|
public static requestTimeoutMs: number = 60000;
|
||||||
public static portalCacheTimeoutMs: number = 10000;
|
public static portalCacheTimeoutMs: number = 10000;
|
||||||
public static errorNotificationTimeoutMs: number = 5000;
|
public static errorNotificationTimeoutMs: number = 5000;
|
||||||
public static copyHelperTimeoutMs: number = 2000;
|
public static copyHelperTimeoutMs: number = 2000;
|
||||||
public static waitForDOMElementMs: number = 500;
|
public static waitForDOMElementMs: number = 500;
|
||||||
public static cacheBustingTimeoutMs: number =
|
public static cacheBustingTimeoutMs: number =
|
||||||
10 /** minutes **/ * 60 /** to seconds **/ * 1000 /** to milliseconds **/;
|
10 /** minutes **/ * 60 /** to seconds **/ * 1000 /** to milliseconds **/;
|
||||||
public static databaseThroughputIncreaseFactor: number = 100;
|
public static databaseThroughputIncreaseFactor: number = 100;
|
||||||
public static readonly arcadiaTokenRefreshInterval: number =
|
public static readonly arcadiaTokenRefreshInterval: number =
|
||||||
20 /** minutes **/ * 60 /** to seconds **/ * 1000 /** to milliseconds **/;
|
20 /** minutes **/ * 60 /** to seconds **/ * 1000 /** to milliseconds **/;
|
||||||
public static readonly arcadiaTokenRefreshIntervalPaddingMs: number = 2000;
|
public static readonly arcadiaTokenRefreshIntervalPaddingMs: number = 2000;
|
||||||
}
|
}
|
||||||
|
|
||||||
export class AccountKind {
|
export class AccountKind {
|
||||||
public static DocumentDB: string = "DocumentDB";
|
public static DocumentDB: string = "DocumentDB";
|
||||||
public static MongoDB: string = "MongoDB";
|
public static MongoDB: string = "MongoDB";
|
||||||
public static Parse: string = "Parse";
|
public static Parse: string = "Parse";
|
||||||
public static GlobalDocumentDB: string = "GlobalDocumentDB";
|
public static GlobalDocumentDB: string = "GlobalDocumentDB";
|
||||||
public static Default: string = AccountKind.DocumentDB;
|
public static Default: string = AccountKind.DocumentDB;
|
||||||
}
|
}
|
||||||
|
|
||||||
export class CorrelationBackend {
|
export class CorrelationBackend {
|
||||||
public static Url: string = "https://aka.ms/cosmosdbanalytics";
|
public static Url: string = "https://aka.ms/cosmosdbanalytics";
|
||||||
}
|
}
|
||||||
|
|
||||||
export class DefaultAccountExperience {
|
export class DefaultAccountExperience {
|
||||||
public static DocumentDB: string = "DocumentDB";
|
public static DocumentDB: string = "DocumentDB";
|
||||||
public static Graph: string = "Graph";
|
public static Graph: string = "Graph";
|
||||||
public static MongoDB: string = "MongoDB";
|
public static MongoDB: string = "MongoDB";
|
||||||
public static ApiForMongoDB: string = "Azure Cosmos DB for MongoDB API";
|
public static ApiForMongoDB: string = "Azure Cosmos DB for MongoDB API";
|
||||||
public static Table: string = "Table";
|
public static Table: string = "Table";
|
||||||
public static Cassandra: string = "Cassandra";
|
public static Cassandra: string = "Cassandra";
|
||||||
public static Default: string = DefaultAccountExperience.DocumentDB;
|
public static Default: string = DefaultAccountExperience.DocumentDB;
|
||||||
}
|
}
|
||||||
|
|
||||||
export class CapabilityNames {
|
export class CapabilityNames {
|
||||||
public static EnableTable: string = "EnableTable";
|
public static EnableTable: string = "EnableTable";
|
||||||
public static EnableGremlin: string = "EnableGremlin";
|
public static EnableGremlin: string = "EnableGremlin";
|
||||||
public static EnableCassandra: string = "EnableCassandra";
|
public static EnableCassandra: string = "EnableCassandra";
|
||||||
public static EnableAutoScale: string = "EnableAutoScale";
|
public static EnableAutoScale: string = "EnableAutoScale";
|
||||||
public static readonly EnableNotebooks: string = "EnableNotebooks";
|
public static readonly EnableNotebooks: string = "EnableNotebooks";
|
||||||
public static readonly EnableStorageAnalytics: string = "EnableStorageAnalytics";
|
public static readonly EnableStorageAnalytics: string = "EnableStorageAnalytics";
|
||||||
public static readonly EnableMongo: string = "EnableMongo";
|
public static readonly EnableMongo: string = "EnableMongo";
|
||||||
public static readonly EnableServerless: string = "EnableServerless";
|
public static readonly EnableServerless: string = "EnableServerless";
|
||||||
}
|
}
|
||||||
|
|
||||||
export class Features {
|
export class Features {
|
||||||
public static readonly cosmosdb = "cosmosdb";
|
public static readonly cosmosdb = "cosmosdb";
|
||||||
public static readonly enableChangeFeedPolicy = "enablechangefeedpolicy";
|
public static readonly enableChangeFeedPolicy = "enablechangefeedpolicy";
|
||||||
public static readonly executeSproc = "dataexplorerexecutesproc";
|
public static readonly executeSproc = "dataexplorerexecutesproc";
|
||||||
public static readonly hostedDataExplorer = "hosteddataexplorerenabled";
|
public static readonly hostedDataExplorer = "hosteddataexplorerenabled";
|
||||||
public static readonly enableTtl = "enablettl";
|
public static readonly enableTtl = "enablettl";
|
||||||
public static readonly enableNotebooks = "enablenotebooks";
|
public static readonly enableNotebooks = "enablenotebooks";
|
||||||
public static readonly enableGalleryPublish = "enablegallerypublish";
|
public static readonly enableGalleryPublish = "enablegallerypublish";
|
||||||
public static readonly enableLinkInjection = "enablelinkinjection";
|
public static readonly enableLinkInjection = "enablelinkinjection";
|
||||||
public static readonly enableSpark = "enablespark";
|
public static readonly enableSpark = "enablespark";
|
||||||
public static readonly livyEndpoint = "livyendpoint";
|
public static readonly livyEndpoint = "livyendpoint";
|
||||||
public static readonly notebookServerUrl = "notebookserverurl";
|
public static readonly notebookServerUrl = "notebookserverurl";
|
||||||
public static readonly notebookServerToken = "notebookservertoken";
|
public static readonly notebookServerToken = "notebookservertoken";
|
||||||
public static readonly notebookBasePath = "notebookbasepath";
|
public static readonly notebookBasePath = "notebookbasepath";
|
||||||
public static readonly canExceedMaximumValue = "canexceedmaximumvalue";
|
public static readonly canExceedMaximumValue = "canexceedmaximumvalue";
|
||||||
public static readonly enableFixedCollectionWithSharedThroughput = "enablefixedcollectionwithsharedthroughput";
|
public static readonly enableFixedCollectionWithSharedThroughput = "enablefixedcollectionwithsharedthroughput";
|
||||||
public static readonly ttl90Days = "ttl90days";
|
public static readonly ttl90Days = "ttl90days";
|
||||||
public static readonly enableRightPanelV2 = "enablerightpanelv2";
|
public static readonly enableRightPanelV2 = "enablerightpanelv2";
|
||||||
public static readonly enableSchema = "enableschema";
|
public static readonly enableSchema = "enableschema";
|
||||||
public static readonly enableSDKoperations = "enablesdkoperations";
|
public static readonly enableSDKoperations = "enablesdkoperations";
|
||||||
public static readonly showMinRUSurvey = "showminrusurvey";
|
public static readonly showMinRUSurvey = "showminrusurvey";
|
||||||
public static readonly selfServeType = "selfservetype";
|
}
|
||||||
}
|
|
||||||
|
// flight names returned from the portal are always lowercase
|
||||||
// flight names returned from the portal are always lowercase
|
export class Flights {
|
||||||
export class Flights {
|
public static readonly SettingsV2 = "settingsv2";
|
||||||
public static readonly SettingsV2 = "settingsv2";
|
public static readonly MongoIndexEditor = "mongoindexeditor";
|
||||||
public static readonly MongoIndexEditor = "mongoindexeditor";
|
}
|
||||||
public static readonly AutoscaleTest = "autoscaletest";
|
|
||||||
public static readonly MongoIndexing = "mongoindexing";
|
export class AfecFeatures {
|
||||||
}
|
public static readonly Spark = "spark-public-preview";
|
||||||
|
public static readonly Notebooks = "sparknotebooks-public-preview";
|
||||||
export class AfecFeatures {
|
public static readonly StorageAnalytics = "storageanalytics-public-preview";
|
||||||
public static readonly Spark = "spark-public-preview";
|
}
|
||||||
public static readonly Notebooks = "sparknotebooks-public-preview";
|
|
||||||
public static readonly StorageAnalytics = "storageanalytics-public-preview";
|
export class Spark {
|
||||||
}
|
public static readonly MaxWorkerCount = 10;
|
||||||
|
public static readonly SKUs: HashMap<string> = new HashMap({
|
||||||
export class Spark {
|
"Cosmos.Spark.D1s": "D1s / 1 core / 4GB RAM",
|
||||||
public static readonly MaxWorkerCount = 10;
|
"Cosmos.Spark.D2s": "D2s / 2 cores / 8GB RAM",
|
||||||
public static readonly SKUs: HashMap<string> = new HashMap({
|
"Cosmos.Spark.D4s": "D4s / 4 cores / 16GB RAM",
|
||||||
"Cosmos.Spark.D1s": "D1s / 1 core / 4GB RAM",
|
"Cosmos.Spark.D8s": "D8s / 8 cores / 32GB RAM",
|
||||||
"Cosmos.Spark.D2s": "D2s / 2 cores / 8GB RAM",
|
"Cosmos.Spark.D16s": "D16s / 16 cores / 64GB RAM",
|
||||||
"Cosmos.Spark.D4s": "D4s / 4 cores / 16GB RAM",
|
"Cosmos.Spark.D32s": "D32s / 32 cores / 128GB RAM",
|
||||||
"Cosmos.Spark.D8s": "D8s / 8 cores / 32GB RAM",
|
"Cosmos.Spark.D64s": "D64s / 64 cores / 256GB RAM"
|
||||||
"Cosmos.Spark.D16s": "D16s / 16 cores / 64GB RAM",
|
});
|
||||||
"Cosmos.Spark.D32s": "D32s / 32 cores / 128GB RAM",
|
}
|
||||||
"Cosmos.Spark.D64s": "D64s / 64 cores / 256GB RAM"
|
|
||||||
});
|
export class TagNames {
|
||||||
}
|
public static defaultExperience: string = "defaultExperience";
|
||||||
|
}
|
||||||
export class TagNames {
|
|
||||||
public static defaultExperience: string = "defaultExperience";
|
export class MongoDBAccounts {
|
||||||
}
|
public static protocol: string = "https";
|
||||||
|
public static defaultPort: string = "10255";
|
||||||
export class MongoDBAccounts {
|
}
|
||||||
public static protocol: string = "https";
|
|
||||||
public static defaultPort: string = "10255";
|
export enum MongoBackendEndpointType {
|
||||||
}
|
local,
|
||||||
|
remote
|
||||||
export enum MongoBackendEndpointType {
|
}
|
||||||
local,
|
|
||||||
remote
|
// TODO: 435619 Add default endpoints per cloud and use regional only when available
|
||||||
}
|
export class CassandraBackend {
|
||||||
|
public static readonly createOrDeleteApi: string = "api/cassandra/createordelete";
|
||||||
// TODO: 435619 Add default endpoints per cloud and use regional only when available
|
public static readonly guestCreateOrDeleteApi: string = "api/guest/cassandra/createordelete";
|
||||||
export class CassandraBackend {
|
public static readonly queryApi: string = "api/cassandra";
|
||||||
public static readonly createOrDeleteApi: string = "api/cassandra/createordelete";
|
public static readonly guestQueryApi: string = "api/guest/cassandra";
|
||||||
public static readonly guestCreateOrDeleteApi: string = "api/guest/cassandra/createordelete";
|
public static readonly keysApi: string = "api/cassandra/keys";
|
||||||
public static readonly queryApi: string = "api/cassandra";
|
public static readonly guestKeysApi: string = "api/guest/cassandra/keys";
|
||||||
public static readonly guestQueryApi: string = "api/guest/cassandra";
|
public static readonly schemaApi: string = "api/cassandra/schema";
|
||||||
public static readonly keysApi: string = "api/cassandra/keys";
|
public static readonly guestSchemaApi: string = "api/guest/cassandra/schema";
|
||||||
public static readonly guestKeysApi: string = "api/guest/cassandra/keys";
|
}
|
||||||
public static readonly schemaApi: string = "api/cassandra/schema";
|
|
||||||
public static readonly guestSchemaApi: string = "api/guest/cassandra/schema";
|
export class Queries {
|
||||||
}
|
public static CustomPageOption: string = "custom";
|
||||||
|
public static UnlimitedPageOption: string = "unlimited";
|
||||||
export class Queries {
|
public static itemsPerPage: number = 100;
|
||||||
public static CustomPageOption: string = "custom";
|
public static unlimitedItemsPerPage: number = 100; // TODO: Figure out appropriate value so it works for accounts with a large number of partitions
|
||||||
public static UnlimitedPageOption: string = "unlimited";
|
|
||||||
public static itemsPerPage: number = 100;
|
public static QueryEditorMinHeightRatio: number = 0.1;
|
||||||
public static unlimitedItemsPerPage: number = 100; // TODO: Figure out appropriate value so it works for accounts with a large number of partitions
|
public static QueryEditorMaxHeightRatio: number = 0.4;
|
||||||
|
public static readonly DefaultMaxDegreeOfParallelism = 6;
|
||||||
public static QueryEditorMinHeightRatio: number = 0.1;
|
}
|
||||||
public static QueryEditorMaxHeightRatio: number = 0.4;
|
|
||||||
public static readonly DefaultMaxDegreeOfParallelism = 6;
|
export class SavedQueries {
|
||||||
}
|
public static readonly CollectionName: string = "___Query";
|
||||||
|
public static readonly DatabaseName: string = "___Cosmos";
|
||||||
export class SavedQueries {
|
public static readonly OfferThroughput: number = 400;
|
||||||
public static readonly CollectionName: string = "___Query";
|
public static readonly PartitionKeyProperty: string = "id";
|
||||||
public static readonly DatabaseName: string = "___Cosmos";
|
}
|
||||||
public static readonly OfferThroughput: number = 400;
|
|
||||||
public static readonly PartitionKeyProperty: string = "id";
|
export class DocumentsGridMetrics {
|
||||||
}
|
public static DocumentsPerPage: number = 100;
|
||||||
|
public static IndividualRowHeight: number = 34;
|
||||||
export class DocumentsGridMetrics {
|
public static BufferHeight: number = 28;
|
||||||
public static DocumentsPerPage: number = 100;
|
public static SplitterMinWidth: number = 200;
|
||||||
public static IndividualRowHeight: number = 34;
|
public static SplitterMaxWidth: number = 360;
|
||||||
public static BufferHeight: number = 28;
|
|
||||||
public static SplitterMinWidth: number = 200;
|
public static DocumentEditorMinWidthRatio: number = 0.2;
|
||||||
public static SplitterMaxWidth: number = 360;
|
public static DocumentEditorMaxWidthRatio: number = 0.4;
|
||||||
|
}
|
||||||
public static DocumentEditorMinWidthRatio: number = 0.2;
|
|
||||||
public static DocumentEditorMaxWidthRatio: number = 0.4;
|
export class ExplorerMetrics {
|
||||||
}
|
public static SplitterMinWidth: number = 240;
|
||||||
|
public static SplitterMaxWidth: number = 400;
|
||||||
export class ExplorerMetrics {
|
public static CollapsedResourceTreeWidth: number = 36;
|
||||||
public static SplitterMinWidth: number = 240;
|
}
|
||||||
public static SplitterMaxWidth: number = 400;
|
|
||||||
public static CollapsedResourceTreeWidth: number = 36;
|
export class SplitterMetrics {
|
||||||
}
|
public static CollapsedPositionLeft: number = ExplorerMetrics.CollapsedResourceTreeWidth;
|
||||||
|
}
|
||||||
export class SplitterMetrics {
|
|
||||||
public static CollapsedPositionLeft: number = ExplorerMetrics.CollapsedResourceTreeWidth;
|
export class Areas {
|
||||||
}
|
public static ResourceTree: string = "Resource Tree";
|
||||||
|
public static ContextualPane: string = "Contextual Pane";
|
||||||
export class Areas {
|
public static Tab: string = "Tab";
|
||||||
public static ResourceTree: string = "Resource Tree";
|
public static ShareDialog: string = "Share Access Dialog";
|
||||||
public static ContextualPane: string = "Contextual Pane";
|
public static Notebook: string = "Notebook";
|
||||||
public static Tab: string = "Tab";
|
}
|
||||||
public static ShareDialog: string = "Share Access Dialog";
|
|
||||||
public static Notebook: string = "Notebook";
|
export class HttpHeaders {
|
||||||
}
|
public static activityId: string = "x-ms-activity-id";
|
||||||
|
public static apiType: string = "x-ms-cosmos-apitype";
|
||||||
export class HttpHeaders {
|
public static authorization: string = "authorization";
|
||||||
public static activityId: string = "x-ms-activity-id";
|
public static collectionIndexTransformationProgress: string =
|
||||||
public static apiType: string = "x-ms-cosmos-apitype";
|
"x-ms-documentdb-collection-index-transformation-progress";
|
||||||
public static authorization: string = "authorization";
|
public static continuation: string = "x-ms-continuation";
|
||||||
public static collectionIndexTransformationProgress: string =
|
public static correlationRequestId: string = "x-ms-correlation-request-id";
|
||||||
"x-ms-documentdb-collection-index-transformation-progress";
|
public static enableScriptLogging: string = "x-ms-documentdb-script-enable-logging";
|
||||||
public static continuation: string = "x-ms-continuation";
|
public static guestAccessToken: string = "x-ms-encrypted-auth-token";
|
||||||
public static correlationRequestId: string = "x-ms-correlation-request-id";
|
public static getReadOnlyKey: string = "x-ms-get-read-only-key";
|
||||||
public static enableScriptLogging: string = "x-ms-documentdb-script-enable-logging";
|
public static connectionString: string = "x-ms-connection-string";
|
||||||
public static guestAccessToken: string = "x-ms-encrypted-auth-token";
|
public static msDate: string = "x-ms-date";
|
||||||
public static getReadOnlyKey: string = "x-ms-get-read-only-key";
|
public static location: string = "Location";
|
||||||
public static connectionString: string = "x-ms-connection-string";
|
public static contentType: string = "Content-Type";
|
||||||
public static msDate: string = "x-ms-date";
|
public static offerReplacePending: string = "x-ms-offer-replace-pending";
|
||||||
public static location: string = "Location";
|
public static user: string = "x-ms-user";
|
||||||
public static contentType: string = "Content-Type";
|
public static populatePartitionStatistics: string = "x-ms-documentdb-populatepartitionstatistics";
|
||||||
public static offerReplacePending: string = "x-ms-offer-replace-pending";
|
public static queryMetrics: string = "x-ms-documentdb-query-metrics";
|
||||||
public static user: string = "x-ms-user";
|
public static requestCharge: string = "x-ms-request-charge";
|
||||||
public static populatePartitionStatistics: string = "x-ms-documentdb-populatepartitionstatistics";
|
public static resourceQuota: string = "x-ms-resource-quota";
|
||||||
public static queryMetrics: string = "x-ms-documentdb-query-metrics";
|
public static resourceUsage: string = "x-ms-resource-usage";
|
||||||
public static requestCharge: string = "x-ms-request-charge";
|
public static retryAfterMs: string = "x-ms-retry-after-ms";
|
||||||
public static resourceQuota: string = "x-ms-resource-quota";
|
public static scriptLogResults: string = "x-ms-documentdb-script-log-results";
|
||||||
public static resourceUsage: string = "x-ms-resource-usage";
|
public static populateCollectionThroughputInfo = "x-ms-documentdb-populatecollectionthroughputinfo";
|
||||||
public static retryAfterMs: string = "x-ms-retry-after-ms";
|
public static supportSpatialLegacyCoordinates = "x-ms-documentdb-supportspatiallegacycoordinates";
|
||||||
public static scriptLogResults: string = "x-ms-documentdb-script-log-results";
|
public static usePolygonsSmallerThanAHemisphere = "x-ms-documentdb-usepolygonssmallerthanahemisphere";
|
||||||
public static populateCollectionThroughputInfo = "x-ms-documentdb-populatecollectionthroughputinfo";
|
public static autoPilotThroughput = "autoscaleSettings";
|
||||||
public static supportSpatialLegacyCoordinates = "x-ms-documentdb-supportspatiallegacycoordinates";
|
public static autoPilotThroughputSDK = "x-ms-cosmos-offer-autopilot-settings";
|
||||||
public static usePolygonsSmallerThanAHemisphere = "x-ms-documentdb-usepolygonssmallerthanahemisphere";
|
public static partitionKey: string = "x-ms-documentdb-partitionkey";
|
||||||
public static autoPilotThroughput = "autoscaleSettings";
|
public static migrateOfferToManualThroughput: string = "x-ms-cosmos-migrate-offer-to-manual-throughput";
|
||||||
public static autoPilotThroughputSDK = "x-ms-cosmos-offer-autopilot-settings";
|
public static migrateOfferToAutopilot: string = "x-ms-cosmos-migrate-offer-to-autopilot";
|
||||||
public static partitionKey: string = "x-ms-documentdb-partitionkey";
|
}
|
||||||
public static migrateOfferToManualThroughput: string = "x-ms-cosmos-migrate-offer-to-manual-throughput";
|
|
||||||
public static migrateOfferToAutopilot: string = "x-ms-cosmos-migrate-offer-to-autopilot";
|
export class ApiType {
|
||||||
}
|
// Mapped to hexadecimal values in the backend
|
||||||
|
public static readonly MongoDB: number = 1;
|
||||||
export class ApiType {
|
public static readonly Gremlin: number = 2;
|
||||||
// Mapped to hexadecimal values in the backend
|
public static readonly Cassandra: number = 4;
|
||||||
public static readonly MongoDB: number = 1;
|
public static readonly Table: number = 8;
|
||||||
public static readonly Gremlin: number = 2;
|
public static readonly SQL: number = 16;
|
||||||
public static readonly Cassandra: number = 4;
|
}
|
||||||
public static readonly Table: number = 8;
|
|
||||||
public static readonly SQL: number = 16;
|
export class HttpStatusCodes {
|
||||||
}
|
public static readonly OK: number = 200;
|
||||||
|
public static readonly Created: number = 201;
|
||||||
export class HttpStatusCodes {
|
public static readonly Accepted: number = 202;
|
||||||
public static readonly OK: number = 200;
|
public static readonly NoContent: number = 204;
|
||||||
public static readonly Created: number = 201;
|
public static readonly NotModified: number = 304;
|
||||||
public static readonly Accepted: number = 202;
|
public static readonly Unauthorized: number = 401;
|
||||||
public static readonly NoContent: number = 204;
|
public static readonly Forbidden: number = 403;
|
||||||
public static readonly NotModified: number = 304;
|
public static readonly NotFound: number = 404;
|
||||||
public static readonly Unauthorized: number = 401;
|
public static readonly TooManyRequests: number = 429;
|
||||||
public static readonly Forbidden: number = 403;
|
public static readonly Conflict: number = 409;
|
||||||
public static readonly NotFound: number = 404;
|
|
||||||
public static readonly TooManyRequests: number = 429;
|
public static readonly InternalServerError: number = 500;
|
||||||
public static readonly Conflict: number = 409;
|
public static readonly BadGateway: number = 502;
|
||||||
|
public static readonly ServiceUnavailable: number = 503;
|
||||||
public static readonly InternalServerError: number = 500;
|
public static readonly GatewayTimeout: number = 504;
|
||||||
public static readonly BadGateway: number = 502;
|
|
||||||
public static readonly ServiceUnavailable: number = 503;
|
public static readonly RetryableStatusCodes: number[] = [
|
||||||
public static readonly GatewayTimeout: number = 504;
|
HttpStatusCodes.TooManyRequests,
|
||||||
|
HttpStatusCodes.InternalServerError, // TODO: Handle all 500s on Portal backend and remove from retries list
|
||||||
public static readonly RetryableStatusCodes: number[] = [
|
HttpStatusCodes.BadGateway,
|
||||||
HttpStatusCodes.TooManyRequests,
|
HttpStatusCodes.ServiceUnavailable,
|
||||||
HttpStatusCodes.InternalServerError, // TODO: Handle all 500s on Portal backend and remove from retries list
|
HttpStatusCodes.GatewayTimeout
|
||||||
HttpStatusCodes.BadGateway,
|
];
|
||||||
HttpStatusCodes.ServiceUnavailable,
|
}
|
||||||
HttpStatusCodes.GatewayTimeout
|
|
||||||
];
|
export class Urls {
|
||||||
}
|
public static feedbackEmail = "https://aka.ms/cosmosdbfeedback?subject=Cosmos%20DB%20Data%20Explorer%20Feedback";
|
||||||
|
public static autoscaleMigration = "https://aka.ms/cosmos-autoscale-migration";
|
||||||
export class Urls {
|
public static freeTierInformation = "https://aka.ms/cosmos-free-tier";
|
||||||
public static feedbackEmail = "https://aka.ms/cosmosdbfeedback?subject=Cosmos%20DB%20Data%20Explorer%20Feedback";
|
public static cosmosPricing = "https://aka.ms/azure-cosmos-db-pricing";
|
||||||
public static autoscaleMigration = "https://aka.ms/cosmos-autoscale-migration";
|
}
|
||||||
public static freeTierInformation = "https://aka.ms/cosmos-free-tier";
|
|
||||||
public static cosmosPricing = "https://aka.ms/azure-cosmos-db-pricing";
|
export class HashRoutePrefixes {
|
||||||
}
|
public static databases: string = "/dbs/{db_id}";
|
||||||
|
public static collections: string = "/dbs/{db_id}/colls/{coll_id}";
|
||||||
export class HashRoutePrefixes {
|
public static sprocHash: string = "/sprocs/";
|
||||||
public static databases: string = "/dbs/{db_id}";
|
public static sprocs: string = HashRoutePrefixes.collections + HashRoutePrefixes.sprocHash + "{sproc_id}";
|
||||||
public static collections: string = "/dbs/{db_id}/colls/{coll_id}";
|
public static docs: string = HashRoutePrefixes.collections + "/docs/{doc_id}/";
|
||||||
public static sprocHash: string = "/sprocs/";
|
public static conflicts: string = HashRoutePrefixes.collections + "/conflicts";
|
||||||
public static sprocs: string = HashRoutePrefixes.collections + HashRoutePrefixes.sprocHash + "{sproc_id}";
|
|
||||||
public static docs: string = HashRoutePrefixes.collections + "/docs/{doc_id}/";
|
public static databasesWithId(databaseId: string): string {
|
||||||
public static conflicts: string = HashRoutePrefixes.collections + "/conflicts";
|
return this.databases.replace("{db_id}", databaseId).replace("/", ""); // strip the first slash since hasher adds it
|
||||||
|
}
|
||||||
public static databasesWithId(databaseId: string): string {
|
|
||||||
return this.databases.replace("{db_id}", databaseId).replace("/", ""); // strip the first slash since hasher adds it
|
public static collectionsWithIds(databaseId: string, collectionId: string): string {
|
||||||
}
|
const transformedDatabasePrefix: string = this.collections.replace("{db_id}", databaseId);
|
||||||
|
|
||||||
public static collectionsWithIds(databaseId: string, collectionId: string): string {
|
return transformedDatabasePrefix.replace("{coll_id}", collectionId).replace("/", ""); // strip the first slash since hasher adds it
|
||||||
const transformedDatabasePrefix: string = this.collections.replace("{db_id}", databaseId);
|
}
|
||||||
|
|
||||||
return transformedDatabasePrefix.replace("{coll_id}", collectionId).replace("/", ""); // strip the first slash since hasher adds it
|
public static sprocWithIds(
|
||||||
}
|
databaseId: string,
|
||||||
|
collectionId: string,
|
||||||
public static sprocWithIds(
|
sprocId: string,
|
||||||
databaseId: string,
|
stripFirstSlash: boolean = true
|
||||||
collectionId: string,
|
): string {
|
||||||
sprocId: string,
|
const transformedDatabasePrefix: string = this.sprocs.replace("{db_id}", databaseId);
|
||||||
stripFirstSlash: boolean = true
|
|
||||||
): string {
|
const transformedSprocRoute: string = transformedDatabasePrefix
|
||||||
const transformedDatabasePrefix: string = this.sprocs.replace("{db_id}", databaseId);
|
.replace("{coll_id}", collectionId)
|
||||||
|
.replace("{sproc_id}", sprocId);
|
||||||
const transformedSprocRoute: string = transformedDatabasePrefix
|
if (!!stripFirstSlash) {
|
||||||
.replace("{coll_id}", collectionId)
|
return transformedSprocRoute.replace("/", ""); // strip the first slash since hasher adds it
|
||||||
.replace("{sproc_id}", sprocId);
|
}
|
||||||
if (!!stripFirstSlash) {
|
|
||||||
return transformedSprocRoute.replace("/", ""); // strip the first slash since hasher adds it
|
return transformedSprocRoute;
|
||||||
}
|
}
|
||||||
|
|
||||||
return transformedSprocRoute;
|
public static conflictsWithIds(databaseId: string, collectionId: string) {
|
||||||
}
|
const transformedDatabasePrefix: string = this.conflicts.replace("{db_id}", databaseId);
|
||||||
|
|
||||||
public static conflictsWithIds(databaseId: string, collectionId: string) {
|
return transformedDatabasePrefix.replace("{coll_id}", collectionId).replace("/", ""); // strip the first slash since hasher adds it;
|
||||||
const transformedDatabasePrefix: string = this.conflicts.replace("{db_id}", databaseId);
|
}
|
||||||
|
|
||||||
return transformedDatabasePrefix.replace("{coll_id}", collectionId).replace("/", ""); // strip the first slash since hasher adds it;
|
public static docsWithIds(databaseId: string, collectionId: string, docId: string) {
|
||||||
}
|
const transformedDatabasePrefix: string = this.docs.replace("{db_id}", databaseId);
|
||||||
|
|
||||||
public static docsWithIds(databaseId: string, collectionId: string, docId: string) {
|
return transformedDatabasePrefix
|
||||||
const transformedDatabasePrefix: string = this.docs.replace("{db_id}", databaseId);
|
.replace("{coll_id}", collectionId)
|
||||||
|
.replace("{doc_id}", docId)
|
||||||
return transformedDatabasePrefix
|
.replace("/", ""); // strip the first slash since hasher adds it
|
||||||
.replace("{coll_id}", collectionId)
|
}
|
||||||
.replace("{doc_id}", docId)
|
}
|
||||||
.replace("/", ""); // strip the first slash since hasher adds it
|
|
||||||
}
|
export class ConfigurationOverridesValues {
|
||||||
}
|
public static IsBsonSchemaV2: string = "true";
|
||||||
|
}
|
||||||
export class ConfigurationOverridesValues {
|
|
||||||
public static IsBsonSchemaV2: string = "true";
|
export class KeyCodes {
|
||||||
}
|
public static Space: number = 32;
|
||||||
|
public static Enter: number = 13;
|
||||||
export class KeyCodes {
|
public static Escape: number = 27;
|
||||||
public static Space: number = 32;
|
public static UpArrow: number = 38;
|
||||||
public static Enter: number = 13;
|
public static DownArrow: number = 40;
|
||||||
public static Escape: number = 27;
|
public static LeftArrow: number = 37;
|
||||||
public static UpArrow: number = 38;
|
public static RightArrow: number = 39;
|
||||||
public static DownArrow: number = 40;
|
public static Tab: number = 9;
|
||||||
public static LeftArrow: number = 37;
|
}
|
||||||
public static RightArrow: number = 39;
|
|
||||||
public static Tab: number = 9;
|
// Normalized per: https://www.w3.org/TR/uievents-key/#named-key-attribute-values
|
||||||
}
|
export class NormalizedEventKey {
|
||||||
|
public static readonly Space = " ";
|
||||||
// Normalized per: https://www.w3.org/TR/uievents-key/#named-key-attribute-values
|
public static readonly Enter = "Enter";
|
||||||
export class NormalizedEventKey {
|
public static readonly Escape = "Escape";
|
||||||
public static readonly Space = " ";
|
public static readonly UpArrow = "ArrowUp";
|
||||||
public static readonly Enter = "Enter";
|
public static readonly DownArrow = "ArrowDown";
|
||||||
public static readonly Escape = "Escape";
|
public static readonly LeftArrow = "ArrowLeft";
|
||||||
public static readonly UpArrow = "ArrowUp";
|
public static readonly RightArrow = "ArrowRight";
|
||||||
public static readonly DownArrow = "ArrowDown";
|
}
|
||||||
public static readonly LeftArrow = "ArrowLeft";
|
|
||||||
public static readonly RightArrow = "ArrowRight";
|
export class TryCosmosExperience {
|
||||||
}
|
public static extendUrl: string = "https://trycosmosdb.azure.com/api/resource/extendportal?userId={0}";
|
||||||
|
public static deleteUrl: string = "https://trycosmosdb.azure.com/api/resource/deleteportal?userId={0}";
|
||||||
export class TryCosmosExperience {
|
public static collectionsPerAccount: number = 3;
|
||||||
public static extendUrl: string = "https://trycosmosdb.azure.com/api/resource/extendportal?userId={0}";
|
public static maxRU: number = 5000;
|
||||||
public static deleteUrl: string = "https://trycosmosdb.azure.com/api/resource/deleteportal?userId={0}";
|
public static defaultRU: number = 3000;
|
||||||
public static collectionsPerAccount: number = 3;
|
}
|
||||||
public static maxRU: number = 5000;
|
|
||||||
public static defaultRU: number = 3000;
|
export class OfferVersions {
|
||||||
}
|
public static V1: string = "V1";
|
||||||
|
public static V2: string = "V2";
|
||||||
export class OfferVersions {
|
}
|
||||||
public static V1: string = "V1";
|
|
||||||
public static V2: string = "V2";
|
export enum ConflictOperationType {
|
||||||
}
|
Replace = "replace",
|
||||||
|
Create = "create",
|
||||||
export enum ConflictOperationType {
|
Delete = "delete"
|
||||||
Replace = "replace",
|
}
|
||||||
Create = "create",
|
|
||||||
Delete = "delete"
|
export const EmulatorMasterKey =
|
||||||
}
|
//[SuppressMessage("Microsoft.Security", "CS002:SecretInNextLine", Justification="Well known public masterKey for emulator")]
|
||||||
|
"C2y6yDjf5/R+ob0N8A7Cgv30VRDJIWEHLM+4QDU5DE2nQ9nDuVTqobD4b8mGGyPMbIZnqyMsEcaGQy67XIw/Jw==";
|
||||||
export const EmulatorMasterKey =
|
|
||||||
//[SuppressMessage("Microsoft.Security", "CS002:SecretInNextLine", Justification="Well known public masterKey for emulator")]
|
// A variable @MyVariable defined in Constants.less is accessible as StyleConstants.MyVariable
|
||||||
"C2y6yDjf5/R+ob0N8A7Cgv30VRDJIWEHLM+4QDU5DE2nQ9nDuVTqobD4b8mGGyPMbIZnqyMsEcaGQy67XIw/Jw==";
|
export const StyleConstants = require("less-vars-loader!../../less/Common/Constants.less");
|
||||||
|
|
||||||
// A variable @MyVariable defined in Constants.less is accessible as StyleConstants.MyVariable
|
export class Notebook {
|
||||||
export const StyleConstants = require("less-vars-loader!../../less/Common/Constants.less");
|
public static readonly defaultBasePath = "./notebooks";
|
||||||
|
public static readonly heartbeatDelayMs = 5000;
|
||||||
export class Notebook {
|
public static readonly kernelRestartInitialDelayMs = 1000;
|
||||||
public static readonly defaultBasePath = "./notebooks";
|
public static readonly kernelRestartMaxDelayMs = 20000;
|
||||||
public static readonly heartbeatDelayMs = 5000;
|
public static readonly autoSaveIntervalMs = 120000;
|
||||||
public static readonly kernelRestartInitialDelayMs = 1000;
|
}
|
||||||
public static readonly kernelRestartMaxDelayMs = 20000;
|
|
||||||
public static readonly autoSaveIntervalMs = 120000;
|
export class SparkLibrary {
|
||||||
}
|
public static readonly nameMinLength = 3;
|
||||||
|
public static readonly nameMaxLength = 63;
|
||||||
export class SparkLibrary {
|
}
|
||||||
public static readonly nameMinLength = 3;
|
|
||||||
public static readonly nameMaxLength = 63;
|
export class AnalyticalStorageTtl {
|
||||||
}
|
public static readonly Days90: number = 7776000;
|
||||||
|
public static readonly Infinite: number = -1;
|
||||||
export class AnalyticalStorageTtl {
|
public static readonly Disabled: number = 0;
|
||||||
public static readonly Days90: number = 7776000;
|
}
|
||||||
public static readonly Infinite: number = -1;
|
|
||||||
public static readonly Disabled: number = 0;
|
export class TerminalQueryParams {
|
||||||
}
|
public static readonly Terminal = "terminal";
|
||||||
|
public static readonly Server = "server";
|
||||||
export class TerminalQueryParams {
|
public static readonly Token = "token";
|
||||||
public static readonly Terminal = "terminal";
|
public static readonly SubscriptionId = "subscriptionId";
|
||||||
public static readonly Server = "server";
|
public static readonly TerminalEndpoint = "terminalEndpoint";
|
||||||
public static readonly Token = "token";
|
}
|
||||||
public static readonly SubscriptionId = "subscriptionId";
|
|
||||||
public static readonly TerminalEndpoint = "terminalEndpoint";
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -21,7 +21,7 @@ export const handleError = (error: string | ARMError | Error, area: string, cons
|
|||||||
sendNotificationForError(errorMessage, errorCode);
|
sendNotificationForError(errorMessage, errorCode);
|
||||||
};
|
};
|
||||||
|
|
||||||
export const getErrorMessage = (error: string | Error = ""): string => {
|
export const getErrorMessage = (error: string | Error): string => {
|
||||||
const errorMessage = typeof error === "string" ? error : error.message;
|
const errorMessage = typeof error === "string" ? error : error.message;
|
||||||
return replaceKnownError(errorMessage);
|
return replaceKnownError(errorMessage);
|
||||||
};
|
};
|
||||||
@@ -45,10 +45,10 @@ const sendNotificationForError = (errorMessage: string, errorCode: number | stri
|
|||||||
const replaceKnownError = (errorMessage: string): string => {
|
const replaceKnownError = (errorMessage: string): string => {
|
||||||
if (
|
if (
|
||||||
window.dataExplorer?.subscriptionType() === SubscriptionType.Internal &&
|
window.dataExplorer?.subscriptionType() === SubscriptionType.Internal &&
|
||||||
errorMessage?.indexOf("SharedOffer is Disabled for your account") >= 0
|
errorMessage.indexOf("SharedOffer is Disabled for your account") >= 0
|
||||||
) {
|
) {
|
||||||
return "Database throughput is not supported for internal subscriptions.";
|
return "Database throughput is not supported for internal subscriptions.";
|
||||||
} else if (errorMessage?.indexOf("Partition key paths must contain only valid") >= 0) {
|
} else if (errorMessage.indexOf("Partition key paths must contain only valid") >= 0) {
|
||||||
return "Partition key paths must contain only valid characters and not contain a trailing slash or wildcard character.";
|
return "Partition key paths must contain only valid characters and not contain a trailing slash or wildcard character.";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -2,11 +2,8 @@ import { Offer, SDKOfferDefinition } from "../Contracts/DataModels";
|
|||||||
import { OfferResponse } from "@azure/cosmos";
|
import { OfferResponse } from "@azure/cosmos";
|
||||||
import { HttpHeaders } from "./Constants";
|
import { HttpHeaders } from "./Constants";
|
||||||
|
|
||||||
export const parseSDKOfferResponse = (offerResponse: OfferResponse): Offer | undefined => {
|
export const parseSDKOfferResponse = (offerResponse: OfferResponse): Offer => {
|
||||||
const offerDefinition: SDKOfferDefinition | undefined = offerResponse?.resource;
|
const offerDefinition: SDKOfferDefinition = offerResponse?.resource;
|
||||||
if (!offerDefinition) {
|
|
||||||
return undefined;
|
|
||||||
}
|
|
||||||
const offerContent = offerDefinition.content;
|
const offerContent = offerDefinition.content;
|
||||||
if (!offerContent) {
|
if (!offerContent) {
|
||||||
return undefined;
|
return undefined;
|
||||||
@@ -15,7 +12,7 @@ export const parseSDKOfferResponse = (offerResponse: OfferResponse): Offer | und
|
|||||||
const minimumThroughput = offerContent.collectionThroughputInfo?.minimumRUForCollection;
|
const minimumThroughput = offerContent.collectionThroughputInfo?.minimumRUForCollection;
|
||||||
const autopilotSettings = offerContent.offerAutopilotSettings;
|
const autopilotSettings = offerContent.offerAutopilotSettings;
|
||||||
|
|
||||||
if (autopilotSettings && autopilotSettings.maxThroughput && minimumThroughput) {
|
if (autopilotSettings) {
|
||||||
return {
|
return {
|
||||||
id: offerDefinition.id,
|
id: offerDefinition.id,
|
||||||
autoscaleMaxThroughput: autopilotSettings.maxThroughput,
|
autoscaleMaxThroughput: autopilotSettings.maxThroughput,
|
||||||
|
|||||||
@@ -23,10 +23,10 @@ export class Splitter {
|
|||||||
public splitterId: string;
|
public splitterId: string;
|
||||||
public leftSideId: string;
|
public leftSideId: string;
|
||||||
|
|
||||||
public splitter!: HTMLElement;
|
public splitter: HTMLElement;
|
||||||
public leftSide!: HTMLElement;
|
public leftSide: HTMLElement;
|
||||||
public lastX!: number;
|
public lastX: number;
|
||||||
public lastWidth!: number;
|
public lastWidth: number;
|
||||||
|
|
||||||
private isCollapsed: ko.Observable<boolean>;
|
private isCollapsed: ko.Observable<boolean>;
|
||||||
private bounds: SplitterBounds;
|
private bounds: SplitterBounds;
|
||||||
@@ -42,10 +42,9 @@ export class Splitter {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public initialize() {
|
public initialize() {
|
||||||
if (document.getElementById(this.splitterId) !== null && document.getElementById(this.leftSideId) != null) {
|
this.splitter = document.getElementById(this.splitterId);
|
||||||
this.splitter = <HTMLElement>document.getElementById(this.splitterId);
|
this.leftSide = document.getElementById(this.leftSideId);
|
||||||
this.leftSide = <HTMLElement>document.getElementById(this.leftSideId);
|
|
||||||
}
|
|
||||||
const isVerticalSplitter: boolean = this.direction === SplitterDirection.Vertical;
|
const isVerticalSplitter: boolean = this.direction === SplitterDirection.Vertical;
|
||||||
const splitterOptions: JQueryUI.ResizableOptions = {
|
const splitterOptions: JQueryUI.ResizableOptions = {
|
||||||
animate: true,
|
animate: true,
|
||||||
|
|||||||
@@ -210,9 +210,9 @@ export interface QueryMetrics {
|
|||||||
|
|
||||||
export interface Offer {
|
export interface Offer {
|
||||||
id: string;
|
id: string;
|
||||||
autoscaleMaxThroughput: number | undefined;
|
autoscaleMaxThroughput: number;
|
||||||
manualThroughput: number | undefined;
|
manualThroughput: number;
|
||||||
minimumThroughput: number | undefined;
|
minimumThroughput: number;
|
||||||
offerDefinition?: SDKOfferDefinition;
|
offerDefinition?: SDKOfferDefinition;
|
||||||
offerReplacePending: boolean;
|
offerReplacePending: boolean;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -15,7 +15,6 @@ import DocumentId from "../Explorer/Tree/DocumentId";
|
|||||||
import StoredProcedure from "../Explorer/Tree/StoredProcedure";
|
import StoredProcedure from "../Explorer/Tree/StoredProcedure";
|
||||||
import Trigger from "../Explorer/Tree/Trigger";
|
import Trigger from "../Explorer/Tree/Trigger";
|
||||||
import UserDefinedFunction from "../Explorer/Tree/UserDefinedFunction";
|
import UserDefinedFunction from "../Explorer/Tree/UserDefinedFunction";
|
||||||
import { SelfServeType } from "../SelfServe/SelfServeUtils";
|
|
||||||
import { UploadDetails } from "../workers/upload/definitions";
|
import { UploadDetails } from "../workers/upload/definitions";
|
||||||
import * as DataModels from "./DataModels";
|
import * as DataModels from "./DataModels";
|
||||||
import { SubscriptionType } from "./SubscriptionType";
|
import { SubscriptionType } from "./SubscriptionType";
|
||||||
@@ -396,7 +395,6 @@ export interface DataExplorerInputsFrame {
|
|||||||
isAuthWithresourceToken?: boolean;
|
isAuthWithresourceToken?: boolean;
|
||||||
defaultCollectionThroughput?: CollectionCreationDefaults;
|
defaultCollectionThroughput?: CollectionCreationDefaults;
|
||||||
flights?: readonly string[];
|
flights?: readonly string[];
|
||||||
selfServeType?: SelfServeType;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface CollectionCreationDefaults {
|
export interface CollectionCreationDefaults {
|
||||||
|
|||||||
383
src/Definitions/adal.d.ts
vendored
383
src/Definitions/adal.d.ts
vendored
@@ -1,383 +0,0 @@
|
|||||||
// Type definitions for adal-angular 1.0.1.1
|
|
||||||
// Project: https://github.com/AzureAD/azure-activedirectory-library-for-js#readme
|
|
||||||
// Definitions by: Daniel Perez Alvarez <https://github.com/unindented>
|
|
||||||
// Anthony Ciccarello <https://github.com/aciccarello>
|
|
||||||
// Definitions: https://github.com/DefinitelyTyped/DefinitelyTyped
|
|
||||||
|
|
||||||
//This is a customized version of adal on top of version 1.0.1 which does not support multi tenant
|
|
||||||
// Customized version add tenantId to stored tokens so when tenant change, adal will refetch instead of read from sessionStorage
|
|
||||||
|
|
||||||
// In module contexts the class constructor function is the exported object
|
|
||||||
// export = AuthenticationContext;
|
|
||||||
|
|
||||||
// This class is defined globally in not in a module context
|
|
||||||
declare class AuthenticationContext {
|
|
||||||
instance: string;
|
|
||||||
config: AuthenticationContext.Options;
|
|
||||||
callback: AuthenticationContext.TokenCallback;
|
|
||||||
popUp: boolean;
|
|
||||||
isAngular: boolean;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Enum for request type
|
|
||||||
*/
|
|
||||||
REQUEST_TYPE: AuthenticationContext.RequestType;
|
|
||||||
RESPONSE_TYPE: AuthenticationContext.ResponseType;
|
|
||||||
CONSTANTS: AuthenticationContext.Constants;
|
|
||||||
|
|
||||||
constructor(options: AuthenticationContext.Options);
|
|
||||||
/**
|
|
||||||
* Initiates the login process by redirecting the user to Azure AD authorization endpoint.
|
|
||||||
*/
|
|
||||||
login(): void;
|
|
||||||
/**
|
|
||||||
* Returns whether a login is in progress.
|
|
||||||
*/
|
|
||||||
loginInProgress(): boolean;
|
|
||||||
/**
|
|
||||||
* Gets token for the specified resource from the cache.
|
|
||||||
* @param resource A URI that identifies the resource for which the token is requested.
|
|
||||||
* @param tenantId tenant Id.
|
|
||||||
*/
|
|
||||||
getCachedToken(resource: string, tenantId: string): string;
|
|
||||||
/**
|
|
||||||
* If user object exists, returns it. Else creates a new user object by decoding `id_token` from the cache.
|
|
||||||
*/
|
|
||||||
getCachedUser(): AuthenticationContext.UserInfo;
|
|
||||||
/**
|
|
||||||
* Adds the passed callback to the array of callbacks for the specified resource.
|
|
||||||
* @param resource A URI that identifies the resource for which the token is requested.
|
|
||||||
* @param expectedState A unique identifier (guid).
|
|
||||||
* @param callback The callback provided by the caller. It will be called with token or error.
|
|
||||||
*/
|
|
||||||
registerCallback(
|
|
||||||
expectedState: string,
|
|
||||||
resource: string,
|
|
||||||
callback: AuthenticationContext.TokenCallback,
|
|
||||||
tenantId: string
|
|
||||||
): void;
|
|
||||||
/**
|
|
||||||
* Acquires token from the cache if it is not expired. Otherwise sends request to AAD to obtain a new token.
|
|
||||||
* @param resource Resource URI identifying the target resource.
|
|
||||||
* @param callback The callback provided by the caller. It will be called with token or error.
|
|
||||||
*/
|
|
||||||
acquireToken(resource: string, tenantId: string, callback: AuthenticationContext.TokenCallback): void;
|
|
||||||
/**
|
|
||||||
* Acquires token (interactive flow using a popup window) by sending request to AAD to obtain a new token.
|
|
||||||
* @param resource Resource URI identifying the target resource.
|
|
||||||
* @param extraQueryParameters Query parameters to add to the authentication request.
|
|
||||||
* @param claims Claims to add to the authentication request.
|
|
||||||
* @param callback The callback provided by the caller. It will be called with token or error.
|
|
||||||
*/
|
|
||||||
acquireTokenPopup(
|
|
||||||
resource: string,
|
|
||||||
tenantId: string,
|
|
||||||
extraQueryParameters: string | null | undefined,
|
|
||||||
claims: string | null | undefined,
|
|
||||||
callback: AuthenticationContext.TokenCallback
|
|
||||||
): void;
|
|
||||||
/**
|
|
||||||
* Acquires token (interactive flow using a redirect) by sending request to AAD to obtain a new token. In this case the callback passed in the authentication request constructor will be called.
|
|
||||||
* @param resource Resource URI identifying the target resource.
|
|
||||||
* @param extraQueryParameters Query parameters to add to the authentication request.
|
|
||||||
* @param claims Claims to add to the authentication request.
|
|
||||||
*/
|
|
||||||
acquireTokenRedirect(
|
|
||||||
resource: string,
|
|
||||||
tenantId: string,
|
|
||||||
extraQueryParameters?: string | null,
|
|
||||||
claims?: string | null
|
|
||||||
): void;
|
|
||||||
/**
|
|
||||||
* Redirects the browser to Azure AD authorization endpoint.
|
|
||||||
* @param urlNavigate URL of the authorization endpoint.
|
|
||||||
*/
|
|
||||||
promptUser(urlNavigate: string): void;
|
|
||||||
/**
|
|
||||||
* Clears cache items.
|
|
||||||
*/
|
|
||||||
clearCache(): void;
|
|
||||||
/**
|
|
||||||
* Clears cache items for a given resource.
|
|
||||||
* @param resource Resource URI identifying the target resource.
|
|
||||||
*/
|
|
||||||
clearCacheForResource(resource: string): void;
|
|
||||||
/**
|
|
||||||
* Redirects user to logout endpoint. After logout, it will redirect to `postLogoutRedirectUri` if added as a property on the config object.
|
|
||||||
*/
|
|
||||||
logOut(): void;
|
|
||||||
/**
|
|
||||||
* Calls the passed in callback with the user object or error message related to the user.
|
|
||||||
* @param callback The callback provided by the caller. It will be called with user or error.
|
|
||||||
*/
|
|
||||||
getUser(callback: AuthenticationContext.UserCallback): void;
|
|
||||||
/**
|
|
||||||
* Checks if the URL fragment contains access token, id token or error description.
|
|
||||||
* @param hash Hash passed from redirect page.
|
|
||||||
*/
|
|
||||||
isCallback(hash: string): boolean;
|
|
||||||
/**
|
|
||||||
* Gets login error.
|
|
||||||
*/
|
|
||||||
getLoginError(): string;
|
|
||||||
/**
|
|
||||||
* Creates a request info object from the URL fragment and returns it.
|
|
||||||
*/
|
|
||||||
getRequestInfo(hash: string): AuthenticationContext.RequestInfo;
|
|
||||||
/**
|
|
||||||
* Saves token or error received in the response from AAD in the cache. In case of `id_token`, it also creates the user object.
|
|
||||||
*/
|
|
||||||
saveTokenFromHash(requestInfo: AuthenticationContext.RequestInfo): void;
|
|
||||||
/**
|
|
||||||
* Gets resource for given endpoint if mapping is provided with config.
|
|
||||||
* @param endpoint Resource URI identifying the target resource.
|
|
||||||
*/
|
|
||||||
getResourceForEndpoint(resource: string): string;
|
|
||||||
/**
|
|
||||||
* This method must be called for processing the response received from AAD. It extracts the hash, processes the token or error, saves it in the cache and calls the callbacks with the result.
|
|
||||||
* @param hash Hash fragment of URL. Defaults to `window.location.hash`.
|
|
||||||
*/
|
|
||||||
handleWindowCallback(hash?: string): void;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Checks the logging Level, constructs the log message and logs it. Users need to implement/override this method to turn on logging.
|
|
||||||
* @param level Level can be set 0, 1, 2 and 3 which turns on 'error', 'warning', 'info' or 'verbose' level logging respectively.
|
|
||||||
* @param message Message to log.
|
|
||||||
* @param error Error to log.
|
|
||||||
*/
|
|
||||||
log(level: AuthenticationContext.LoggingLevel, message: string, error: any): void;
|
|
||||||
/**
|
|
||||||
* Logs messages when logging level is set to 0.
|
|
||||||
* @param message Message to log.
|
|
||||||
* @param error Error to log.
|
|
||||||
*/
|
|
||||||
error(message: string, error: any): void;
|
|
||||||
/**
|
|
||||||
* Logs messages when logging level is set to 1.
|
|
||||||
* @param message Message to log.
|
|
||||||
*/
|
|
||||||
warn(message: string): void;
|
|
||||||
/**
|
|
||||||
* Logs messages when logging level is set to 2.
|
|
||||||
* @param message Message to log.
|
|
||||||
*/
|
|
||||||
info(message: string): void;
|
|
||||||
/**
|
|
||||||
* Logs messages when logging level is set to 3.
|
|
||||||
* @param message Message to log.
|
|
||||||
*/
|
|
||||||
verbose(message: string): void;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Logs Pii messages when Logging Level is set to 0 and window.piiLoggingEnabled is set to true.
|
|
||||||
* @param message Message to log.
|
|
||||||
* @param error Error to log.
|
|
||||||
*/
|
|
||||||
errorPii(message: string, error: any): void;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Logs Pii messages when Logging Level is set to 1 and window.piiLoggingEnabled is set to true.
|
|
||||||
* @param message Message to log.
|
|
||||||
*/
|
|
||||||
warnPii(message: string): void;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Logs messages when Logging Level is set to 2 and window.piiLoggingEnabled is set to true.
|
|
||||||
* @param message Message to log.
|
|
||||||
*/
|
|
||||||
infoPii(message: string): void;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Logs messages when Logging Level is set to 3 and window.piiLoggingEnabled is set to true.
|
|
||||||
* @param message Message to log.
|
|
||||||
*/
|
|
||||||
verbosePii(message: string): void;
|
|
||||||
}
|
|
||||||
|
|
||||||
declare namespace AuthenticationContext {
|
|
||||||
function inject(config: Options): AuthenticationContext;
|
|
||||||
|
|
||||||
type LoggingLevel = 0 | 1 | 2 | 3;
|
|
||||||
|
|
||||||
type RequestType = "LOGIN" | "RENEW_TOKEN" | "UNKNOWN";
|
|
||||||
|
|
||||||
type ResponseType = "id_token token" | "token";
|
|
||||||
|
|
||||||
interface RequestInfo {
|
|
||||||
/**
|
|
||||||
* Object comprising of fields such as id_token/error, session_state, state, e.t.c.
|
|
||||||
*/
|
|
||||||
parameters: any;
|
|
||||||
/**
|
|
||||||
* Request type.
|
|
||||||
*/
|
|
||||||
requestType: RequestType;
|
|
||||||
/**
|
|
||||||
* Whether state is valid.
|
|
||||||
*/
|
|
||||||
stateMatch: boolean;
|
|
||||||
/**
|
|
||||||
* Unique guid used to match the response with the request.
|
|
||||||
*/
|
|
||||||
stateResponse: string;
|
|
||||||
/**
|
|
||||||
* Whether `requestType` contains `id_token`, `access_token` or error.
|
|
||||||
*/
|
|
||||||
valid: boolean;
|
|
||||||
}
|
|
||||||
|
|
||||||
interface UserInfo {
|
|
||||||
/**
|
|
||||||
* Username assigned from UPN or email.
|
|
||||||
*/
|
|
||||||
userName: string;
|
|
||||||
/**
|
|
||||||
* Properties parsed from `id_token`.
|
|
||||||
*/
|
|
||||||
profile: any;
|
|
||||||
}
|
|
||||||
|
|
||||||
type TokenCallback = (errorDesc: string | null, token: string | null, error: any) => void;
|
|
||||||
|
|
||||||
type UserCallback = (errorDesc: string | null, user: UserInfo | null) => void;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Configuration options for Authentication Context
|
|
||||||
*/
|
|
||||||
interface Options {
|
|
||||||
/**
|
|
||||||
* Client ID assigned to your app by Azure Active Directory.
|
|
||||||
*/
|
|
||||||
clientId: string;
|
|
||||||
/**
|
|
||||||
* Endpoint at which you expect to receive tokens.Defaults to `window.location.href`.
|
|
||||||
*/
|
|
||||||
redirectUri?: string;
|
|
||||||
/**
|
|
||||||
* Azure Active Directory instance. Defaults to `https://login.microsoftonline.com/`.
|
|
||||||
*/
|
|
||||||
instance?: string;
|
|
||||||
/**
|
|
||||||
* Your target tenant. Defaults to `common`.
|
|
||||||
*/
|
|
||||||
tenant?: string;
|
|
||||||
/**
|
|
||||||
* Query parameters to add to the authentication request.
|
|
||||||
*/
|
|
||||||
extraQueryParameter?: string;
|
|
||||||
/**
|
|
||||||
* Unique identifier used to map the request with the response. Defaults to RFC4122 version 4 guid (128 bits).
|
|
||||||
*/
|
|
||||||
correlationId?: string;
|
|
||||||
/**
|
|
||||||
* User defined function of handling the navigation to Azure AD authorization endpoint in case of login.
|
|
||||||
*/
|
|
||||||
displayCall?: (url: string) => void;
|
|
||||||
/**
|
|
||||||
* Set this to true to enable login in a popup winodow instead of a full redirect. Defaults to `false`.
|
|
||||||
*/
|
|
||||||
popUp?: boolean;
|
|
||||||
/**
|
|
||||||
* Set this to the resource to request on login. Defaults to `clientId`.
|
|
||||||
*/
|
|
||||||
loginResource?: string;
|
|
||||||
/**
|
|
||||||
* Set this to redirect the user to a custom login page.
|
|
||||||
*/
|
|
||||||
localLoginUrl?: string;
|
|
||||||
/**
|
|
||||||
* Redirects to start page after login. Defaults to `true`.
|
|
||||||
*/
|
|
||||||
navigateToLoginRequestUrl?: boolean;
|
|
||||||
/**
|
|
||||||
* Set this to redirect the user to a custom logout page.
|
|
||||||
*/
|
|
||||||
logOutUri?: string;
|
|
||||||
/**
|
|
||||||
* Redirects the user to postLogoutRedirectUri after logout. Defaults to `redirectUri`.
|
|
||||||
*/
|
|
||||||
postLogoutRedirectUri?: string;
|
|
||||||
/**
|
|
||||||
* Sets browser storage to either 'localStorage' or sessionStorage'. Defaults to `sessionStorage`.
|
|
||||||
*/
|
|
||||||
cacheLocation?: "localStorage" | "sessionStorage";
|
|
||||||
/**
|
|
||||||
* Array of keywords or URIs. Adal will attach a token to outgoing requests that have these keywords or URIs.
|
|
||||||
*/
|
|
||||||
endpoints?: { [resource: string]: string };
|
|
||||||
/**
|
|
||||||
* Array of keywords or URIs. Adal will not attach a token to outgoing requests that have these keywords or URIs.
|
|
||||||
*/
|
|
||||||
anonymousEndpoints?: string[];
|
|
||||||
/**
|
|
||||||
* If the cached token is about to be expired in the expireOffsetSeconds (in seconds), Adal will renew the token instead of using the cached token. Defaults to 300 seconds.
|
|
||||||
*/
|
|
||||||
expireOffsetSeconds?: number;
|
|
||||||
/**
|
|
||||||
* The number of milliseconds of inactivity before a token renewal response from AAD should be considered timed out. Defaults to 6 seconds.
|
|
||||||
*/
|
|
||||||
loadFrameTimeout?: number;
|
|
||||||
/**
|
|
||||||
* Callback to be invoked when a token is acquired.
|
|
||||||
*/
|
|
||||||
callback?: TokenCallback;
|
|
||||||
}
|
|
||||||
|
|
||||||
interface LoggingConfig {
|
|
||||||
level: LoggingLevel;
|
|
||||||
log: (message: string) => void;
|
|
||||||
piiLoggingEnabled: boolean;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Enum for storage constants
|
|
||||||
*/
|
|
||||||
interface Constants {
|
|
||||||
ACCESS_TOKEN: "access_token";
|
|
||||||
EXPIRES_IN: "expires_in";
|
|
||||||
ID_TOKEN: "id_token";
|
|
||||||
ERROR_DESCRIPTION: "error_description";
|
|
||||||
SESSION_STATE: "session_state";
|
|
||||||
STORAGE: {
|
|
||||||
TOKEN_KEYS: "adal.token.keys";
|
|
||||||
ACCESS_TOKEN_KEY: "adal.access.token.key";
|
|
||||||
EXPIRATION_KEY: "adal.expiration.key";
|
|
||||||
STATE_LOGIN: "adal.state.login";
|
|
||||||
STATE_RENEW: "adal.state.renew";
|
|
||||||
NONCE_IDTOKEN: "adal.nonce.idtoken";
|
|
||||||
SESSION_STATE: "adal.session.state";
|
|
||||||
USERNAME: "adal.username";
|
|
||||||
IDTOKEN: "adal.idtoken";
|
|
||||||
ERROR: "adal.error";
|
|
||||||
ERROR_DESCRIPTION: "adal.error.description";
|
|
||||||
LOGIN_REQUEST: "adal.login.request";
|
|
||||||
LOGIN_ERROR: "adal.login.error";
|
|
||||||
RENEW_STATUS: "adal.token.renew.status";
|
|
||||||
};
|
|
||||||
RESOURCE_DELIMETER: "|";
|
|
||||||
LOADFRAME_TIMEOUT: "6000";
|
|
||||||
TOKEN_RENEW_STATUS_CANCELED: "Canceled";
|
|
||||||
TOKEN_RENEW_STATUS_COMPLETED: "Completed";
|
|
||||||
TOKEN_RENEW_STATUS_IN_PROGRESS: "In Progress";
|
|
||||||
LOGGING_LEVEL: {
|
|
||||||
ERROR: 0;
|
|
||||||
WARN: 1;
|
|
||||||
INFO: 2;
|
|
||||||
VERBOSE: 3;
|
|
||||||
};
|
|
||||||
LEVEL_STRING_MAP: {
|
|
||||||
0: "ERROR:";
|
|
||||||
1: "WARNING:";
|
|
||||||
2: "INFO:";
|
|
||||||
3: "VERBOSE:";
|
|
||||||
};
|
|
||||||
POPUP_WIDTH: 483;
|
|
||||||
POPUP_HEIGHT: 600;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// declare global {
|
|
||||||
// interface Window {
|
|
||||||
// Logging: AuthenticationContext.LoggingConfig;
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
@@ -42,7 +42,7 @@ interface CollapsiblePanelParams {
|
|||||||
* Use the optional "collapseToLeft" parameter to collapse to the left.
|
* Use the optional "collapseToLeft" parameter to collapse to the left.
|
||||||
*/
|
*/
|
||||||
class CollapsiblePanelViewModel {
|
class CollapsiblePanelViewModel {
|
||||||
public params: CollapsiblePanelParams;
|
private params: CollapsiblePanelParams;
|
||||||
private isCollapsed: ko.Observable<boolean>;
|
private isCollapsed: ko.Observable<boolean>;
|
||||||
|
|
||||||
public constructor(params: CollapsiblePanelParams) {
|
public constructor(params: CollapsiblePanelParams) {
|
||||||
@@ -50,7 +50,7 @@ class CollapsiblePanelViewModel {
|
|||||||
this.isCollapsed = params.isCollapsed || ko.observable(false);
|
this.isCollapsed = params.isCollapsed || ko.observable(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
public toggleCollapse(): void {
|
private toggleCollapse(): void {
|
||||||
this.isCollapsed(!this.isCollapsed());
|
this.isCollapsed(!this.isCollapsed());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -38,7 +38,7 @@ export interface CommandButtonComponentProps {
|
|||||||
/**
|
/**
|
||||||
* Label for the button
|
* Label for the button
|
||||||
*/
|
*/
|
||||||
commandButtonLabel: string;
|
commandButtonLabel?: string;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* True if this button opens a tab or pane, false otherwise.
|
* True if this button opens a tab or pane, false otherwise.
|
||||||
|
|||||||
@@ -48,7 +48,6 @@ export const FeaturePanelComponent: React.FunctionComponent = () => {
|
|||||||
{ key: "feature.hosteddataexplorerenabled", label: "Hosted Data Explorer (deprecated?)", value: "true" },
|
{ key: "feature.hosteddataexplorerenabled", label: "Hosted Data Explorer (deprecated?)", value: "true" },
|
||||||
{ key: "feature.enablettl", label: "Enable TTL", value: "true" },
|
{ key: "feature.enablettl", label: "Enable TTL", value: "true" },
|
||||||
{ key: "feature.enablegallerypublish", label: "Enable Notebook Gallery Publishing", value: "true" },
|
{ key: "feature.enablegallerypublish", label: "Enable Notebook Gallery Publishing", value: "true" },
|
||||||
{ key: "feature.selfServeType", label: "Self serve feature", value: "sample" },
|
|
||||||
{
|
{
|
||||||
key: "feature.enableLinkInjection",
|
key: "feature.enableLinkInjection",
|
||||||
label: "Enable Injecting Notebook Viewer Link into the first cell",
|
label: "Enable Injecting Notebook Viewer Link into the first cell",
|
||||||
|
|||||||
@@ -157,14 +157,14 @@ exports[`Feature panel renders all flags 1`] = `
|
|||||||
/>
|
/>
|
||||||
<StyledCheckboxBase
|
<StyledCheckboxBase
|
||||||
checked={false}
|
checked={false}
|
||||||
key="feature.selfServeType"
|
key="feature.enableLinkInjection"
|
||||||
label="Self serve feature"
|
label="Enable Injecting Notebook Viewer Link into the first cell"
|
||||||
onChange={[Function]}
|
onChange={[Function]}
|
||||||
/>
|
/>
|
||||||
<StyledCheckboxBase
|
<StyledCheckboxBase
|
||||||
checked={false}
|
checked={false}
|
||||||
key="feature.enableLinkInjection"
|
key="feature.canexceedmaximumvalue"
|
||||||
label="Enable Injecting Notebook Viewer Link into the first cell"
|
label="Can exceed max value"
|
||||||
onChange={[Function]}
|
onChange={[Function]}
|
||||||
/>
|
/>
|
||||||
</Stack>
|
</Stack>
|
||||||
@@ -172,12 +172,6 @@ exports[`Feature panel renders all flags 1`] = `
|
|||||||
className="checkboxRow"
|
className="checkboxRow"
|
||||||
horizontalAlign="space-between"
|
horizontalAlign="space-between"
|
||||||
>
|
>
|
||||||
<StyledCheckboxBase
|
|
||||||
checked={false}
|
|
||||||
key="feature.canexceedmaximumvalue"
|
|
||||||
label="Can exceed max value"
|
|
||||||
onChange={[Function]}
|
|
||||||
/>
|
|
||||||
<StyledCheckboxBase
|
<StyledCheckboxBase
|
||||||
checked={false}
|
checked={false}
|
||||||
key="feature.enablefixedcollectionwithsharedthroughput"
|
key="feature.enablefixedcollectionwithsharedthroughput"
|
||||||
|
|||||||
@@ -71,7 +71,7 @@ interface InputTypeaheadParams {
|
|||||||
/**
|
/**
|
||||||
* This function gets called when pressing ENTER on the input box
|
* This function gets called when pressing ENTER on the input box
|
||||||
*/
|
*/
|
||||||
submitFct?: (inputValue: string | null, selection: Item | null) => void;
|
submitFct?: (inputValue: string, selection: Item) => void;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Typehead comes with a Search button that we normally remove.
|
* Typehead comes with a Search button that we normally remove.
|
||||||
@@ -88,8 +88,8 @@ interface OnClickItem {
|
|||||||
}
|
}
|
||||||
|
|
||||||
interface Cache {
|
interface Cache {
|
||||||
inputValue: string | null;
|
inputValue: string;
|
||||||
selection: Item | null;
|
selection: Item;
|
||||||
}
|
}
|
||||||
|
|
||||||
class InputTypeaheadViewModel {
|
class InputTypeaheadViewModel {
|
||||||
@@ -98,12 +98,15 @@ class InputTypeaheadViewModel {
|
|||||||
private params: InputTypeaheadParams;
|
private params: InputTypeaheadParams;
|
||||||
|
|
||||||
private cache: Cache;
|
private cache: Cache;
|
||||||
|
private inputValue: string;
|
||||||
|
private selection: Item;
|
||||||
|
|
||||||
public constructor(params: InputTypeaheadParams) {
|
public constructor(params: InputTypeaheadParams) {
|
||||||
this.instanceNumber = InputTypeaheadViewModel.instanceCount++;
|
this.instanceNumber = InputTypeaheadViewModel.instanceCount++;
|
||||||
this.params = params;
|
this.params = params;
|
||||||
|
|
||||||
this.params.choices.subscribe(this.initializeTypeahead.bind(this));
|
this.params.choices.subscribe(this.initializeTypeahead.bind(this));
|
||||||
|
|
||||||
this.cache = {
|
this.cache = {
|
||||||
inputValue: null,
|
inputValue: null,
|
||||||
selection: null
|
selection: null
|
||||||
@@ -158,7 +161,7 @@ class InputTypeaheadViewModel {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
($ as any).typeahead(options);
|
$.typeahead(options);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -174,11 +177,11 @@ class InputTypeaheadViewModel {
|
|||||||
* Use ko's "template: afterRender" callback to do that without actually using any template.
|
* Use ko's "template: afterRender" callback to do that without actually using any template.
|
||||||
* Another way is to call it within setTimeout() in constructor.
|
* Another way is to call it within setTimeout() in constructor.
|
||||||
*/
|
*/
|
||||||
public afterRender(): void {
|
private afterRender(): void {
|
||||||
this.initializeTypeahead();
|
this.initializeTypeahead();
|
||||||
}
|
}
|
||||||
|
|
||||||
public submit(): void {
|
private submit(): void {
|
||||||
if (this.params.submitFct) {
|
if (this.params.submitFct) {
|
||||||
this.params.submitFct(this.cache.inputValue, this.cache.selection);
|
this.params.submitFct(this.cache.inputValue, this.cache.selection);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -59,12 +59,10 @@ export class JsonEditorViewModel extends WaitsForTemplateViewModel {
|
|||||||
this.params = params;
|
this.params = params;
|
||||||
|
|
||||||
this.params.content.subscribe((newValue: string) => {
|
this.params.content.subscribe((newValue: string) => {
|
||||||
if (newValue) {
|
if (!!this.editor) {
|
||||||
if (!!this.editor) {
|
this.editor.getModel().setValue(newValue);
|
||||||
this.editor.getModel().setValue(newValue);
|
} else {
|
||||||
} else {
|
this.createEditor(newValue, this.configureEditor.bind(this));
|
||||||
this.createEditor(newValue, this.configureEditor.bind(this));
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
@@ -231,7 +231,7 @@ describe("SettingsComponent", () => {
|
|||||||
|
|
||||||
it("getUpdatedConflictResolutionPolicy", () => {
|
it("getUpdatedConflictResolutionPolicy", () => {
|
||||||
const wrapper = shallow(<SettingsComponent {...baseProps} />);
|
const wrapper = shallow(<SettingsComponent {...baseProps} />);
|
||||||
const conflictResolutionPolicyPath = "/_ts";
|
const conflictResolutionPolicyPath = "_ts";
|
||||||
const conflictResolutionPolicyProcedure = "sample_sproc";
|
const conflictResolutionPolicyProcedure = "sample_sproc";
|
||||||
const expectSprocPath =
|
const expectSprocPath =
|
||||||
"/dbs/" + collection.databaseId + "/colls/" + collection.id() + "/sprocs/" + conflictResolutionPolicyProcedure;
|
"/dbs/" + collection.databaseId + "/colls/" + collection.id() + "/sprocs/" + conflictResolutionPolicyProcedure;
|
||||||
|
|||||||
@@ -138,8 +138,8 @@ export class SettingsComponent extends React.Component<SettingsComponentProps, S
|
|||||||
|
|
||||||
// Mongo container with system partition key still treat as "Fixed"
|
// Mongo container with system partition key still treat as "Fixed"
|
||||||
this.isFixedContainer =
|
this.isFixedContainer =
|
||||||
this.container.isPreferredApiMongoDB() &&
|
!this.collection.partitionKey ||
|
||||||
(!this.collection.partitionKey || this.collection.partitionKey.systemKey);
|
(this.container.isPreferredApiMongoDB() && this.collection.partitionKey.systemKey);
|
||||||
|
|
||||||
this.state = {
|
this.state = {
|
||||||
throughput: undefined,
|
throughput: undefined,
|
||||||
@@ -684,7 +684,7 @@ export class SettingsComponent extends React.Component<SettingsComponentProps, S
|
|||||||
|
|
||||||
if (policy.mode === DataModels.ConflictResolutionMode.LastWriterWins) {
|
if (policy.mode === DataModels.ConflictResolutionMode.LastWriterWins) {
|
||||||
policy.conflictResolutionPath = this.state.conflictResolutionPolicyPath;
|
policy.conflictResolutionPath = this.state.conflictResolutionPolicyPath;
|
||||||
if (!policy.conflictResolutionPath?.startsWith("/")) {
|
if (policy.conflictResolutionPath?.startsWith("/")) {
|
||||||
policy.conflictResolutionPath = "/" + policy.conflictResolutionPath;
|
policy.conflictResolutionPath = "/" + policy.conflictResolutionPath;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -66,7 +66,7 @@ export interface PriceBreakdown {
|
|||||||
currencySign: string;
|
currencySign: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
export const infoAndToolTipTextStyle: ITextStyles = { root: { fontSize: 14 } };
|
export const infoAndToolTipTextStyle: ITextStyles = { root: { fontSize: 12 } };
|
||||||
|
|
||||||
export const noLeftPaddingCheckBoxStyle: ICheckboxStyles = {
|
export const noLeftPaddingCheckBoxStyle: ICheckboxStyles = {
|
||||||
label: {
|
label: {
|
||||||
@@ -166,10 +166,7 @@ export const separatorStyles: Partial<ISeparatorStyles> = {
|
|||||||
]
|
]
|
||||||
};
|
};
|
||||||
|
|
||||||
export const messageBarStyles: Partial<IMessageBarStyles> = {
|
export const messageBarStyles: Partial<IMessageBarStyles> = { root: { marginTop: "5px" } };
|
||||||
root: { marginTop: "5px", backgroundColor: "white" },
|
|
||||||
text: { fontSize: 14 }
|
|
||||||
};
|
|
||||||
|
|
||||||
export const throughputUnit = "RU/s";
|
export const throughputUnit = "RU/s";
|
||||||
|
|
||||||
|
|||||||
@@ -8,7 +8,7 @@ exports[`IndexingPolicyRefreshComponent renders 1`] = `
|
|||||||
styles={
|
styles={
|
||||||
Object {
|
Object {
|
||||||
"root": Object {
|
"root": Object {
|
||||||
"fontSize": 14,
|
"fontSize": 12,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -16,7 +16,7 @@ import {
|
|||||||
} from "../SettingsRenderUtils";
|
} from "../SettingsRenderUtils";
|
||||||
import { hasDatabaseSharedThroughput } from "../SettingsUtils";
|
import { hasDatabaseSharedThroughput } from "../SettingsUtils";
|
||||||
import * as AutoPilotUtils from "../../../../Utils/AutoPilotUtils";
|
import * as AutoPilotUtils from "../../../../Utils/AutoPilotUtils";
|
||||||
import { Link, Text, TextField, Stack, Label, MessageBar, MessageBarType } from "office-ui-fabric-react";
|
import { Text, TextField, Stack, Label, MessageBar, MessageBarType } from "office-ui-fabric-react";
|
||||||
import { configContext, Platform } from "../../../../ConfigContext";
|
import { configContext, Platform } from "../../../../ConfigContext";
|
||||||
|
|
||||||
export interface ScaleComponentProps {
|
export interface ScaleComponentProps {
|
||||||
@@ -165,8 +165,6 @@ export class ScaleComponent extends React.Component<ScaleComponentProps> {
|
|||||||
private getThroughputInputComponent = (): JSX.Element => (
|
private getThroughputInputComponent = (): JSX.Element => (
|
||||||
<ThroughputInputAutoPilotV3Component
|
<ThroughputInputAutoPilotV3Component
|
||||||
databaseAccount={this.props.container.databaseAccount()}
|
databaseAccount={this.props.container.databaseAccount()}
|
||||||
databaseName={this.props.collection.databaseId}
|
|
||||||
collectionName={this.props.collection.id()}
|
|
||||||
serverId={this.props.container.serverId()}
|
serverId={this.props.container.serverId()}
|
||||||
throughput={this.props.throughput}
|
throughput={this.props.throughput}
|
||||||
throughputBaseline={this.props.throughputBaseline}
|
throughputBaseline={this.props.throughputBaseline}
|
||||||
@@ -178,7 +176,6 @@ export class ScaleComponent extends React.Component<ScaleComponentProps> {
|
|||||||
label={this.getThroughputTitle()}
|
label={this.getThroughputTitle()}
|
||||||
isEmulator={this.isEmulator}
|
isEmulator={this.isEmulator}
|
||||||
isFixed={this.props.isFixedContainer}
|
isFixed={this.props.isFixedContainer}
|
||||||
isFreeTierAccount={this.isFreeTierAccount()}
|
|
||||||
isAutoPilotSelected={this.props.isAutoPilotSelected}
|
isAutoPilotSelected={this.props.isAutoPilotSelected}
|
||||||
onAutoPilotSelected={this.props.onAutoPilotSelected}
|
onAutoPilotSelected={this.props.onAutoPilotSelected}
|
||||||
wasAutopilotOriginallySet={this.props.wasAutopilotOriginallySet}
|
wasAutopilotOriginallySet={this.props.wasAutopilotOriginallySet}
|
||||||
@@ -193,37 +190,9 @@ export class ScaleComponent extends React.Component<ScaleComponentProps> {
|
|||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
|
|
||||||
private isFreeTierAccount(): boolean {
|
|
||||||
const databaseAccount = this.props.container?.databaseAccount();
|
|
||||||
return databaseAccount?.properties?.enableFreeTier;
|
|
||||||
}
|
|
||||||
|
|
||||||
private getFreeTierInfoMessage(): JSX.Element {
|
|
||||||
return (
|
|
||||||
<Text>
|
|
||||||
With free tier, you will get the first 400 RU/s and 5 GB of storage in this account for free. To keep your
|
|
||||||
account free, keep the total RU/s across all resources in the account to 400 RU/s.
|
|
||||||
<Link
|
|
||||||
href="https://docs.microsoft.com/en-us/azure/cosmos-db/understand-your-bill#billing-examples-with-free-tier-accounts"
|
|
||||||
target="_blank"
|
|
||||||
>
|
|
||||||
Learn more.
|
|
||||||
</Link>
|
|
||||||
</Text>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
public render(): JSX.Element {
|
public render(): JSX.Element {
|
||||||
return (
|
return (
|
||||||
<Stack {...subComponentStackProps}>
|
<Stack {...subComponentStackProps}>
|
||||||
{this.isFreeTierAccount() && (
|
|
||||||
<MessageBar
|
|
||||||
messageBarIconProps={{ iconName: "InfoSolid", className: "messageBarInfoIcon" }}
|
|
||||||
styles={{ text: { fontSize: 14 } }}
|
|
||||||
>
|
|
||||||
{this.getFreeTierInfoMessage()}
|
|
||||||
</MessageBar>
|
|
||||||
)}
|
|
||||||
{this.getInitialNotificationElement() && (
|
{this.getInitialNotificationElement() && (
|
||||||
<MessageBar messageBarType={MessageBarType.warning}>{this.getInitialNotificationElement()}</MessageBar>
|
<MessageBar messageBarType={MessageBarType.warning}>{this.getInitialNotificationElement()}</MessageBar>
|
||||||
)}
|
)}
|
||||||
|
|||||||
@@ -13,7 +13,16 @@ import {
|
|||||||
} from "../SettingsUtils";
|
} from "../SettingsUtils";
|
||||||
import Explorer from "../../../Explorer";
|
import Explorer from "../../../Explorer";
|
||||||
import { Int32 } from "../../../Panes/Tables/Validators/EntityPropertyValidationCommon";
|
import { Int32 } from "../../../Panes/Tables/Validators/EntityPropertyValidationCommon";
|
||||||
import { Label, Text, TextField, Stack, IChoiceGroupOption, ChoiceGroup, MessageBar } from "office-ui-fabric-react";
|
import {
|
||||||
|
Label,
|
||||||
|
Text,
|
||||||
|
TextField,
|
||||||
|
Stack,
|
||||||
|
IChoiceGroupOption,
|
||||||
|
ChoiceGroup,
|
||||||
|
MessageBar,
|
||||||
|
MessageBarType
|
||||||
|
} from "office-ui-fabric-react";
|
||||||
import {
|
import {
|
||||||
getTextFieldStyles,
|
getTextFieldStyles,
|
||||||
changeFeedPolicyToolTip,
|
changeFeedPolicyToolTip,
|
||||||
@@ -181,10 +190,7 @@ export class SubSettingsComponent extends React.Component<SubSettingsComponentPr
|
|||||||
styles={getChoiceGroupStyles(this.props.timeToLive, this.props.timeToLiveBaseline)}
|
styles={getChoiceGroupStyles(this.props.timeToLive, this.props.timeToLiveBaseline)}
|
||||||
/>
|
/>
|
||||||
{isDirty(this.props.timeToLive, this.props.timeToLiveBaseline) && this.props.timeToLive === TtlType.On && (
|
{isDirty(this.props.timeToLive, this.props.timeToLiveBaseline) && this.props.timeToLive === TtlType.On && (
|
||||||
<MessageBar
|
<MessageBar messageBarType={MessageBarType.warning} styles={messageBarStyles}>
|
||||||
messageBarIconProps={{ iconName: "InfoSolid", className: "messageBarInfoIcon" }}
|
|
||||||
styles={messageBarStyles}
|
|
||||||
>
|
|
||||||
{ttlWarning}
|
{ttlWarning}
|
||||||
</MessageBar>
|
</MessageBar>
|
||||||
)}
|
)}
|
||||||
|
|||||||
@@ -9,8 +9,6 @@ import * as DataModels from "../../../../../Contracts/DataModels";
|
|||||||
describe("ThroughputInputAutoPilotV3Component", () => {
|
describe("ThroughputInputAutoPilotV3Component", () => {
|
||||||
const baseProps: ThroughputInputAutoPilotV3Props = {
|
const baseProps: ThroughputInputAutoPilotV3Props = {
|
||||||
databaseAccount: {} as DataModels.DatabaseAccount,
|
databaseAccount: {} as DataModels.DatabaseAccount,
|
||||||
databaseName: "test",
|
|
||||||
collectionName: "test",
|
|
||||||
serverId: undefined,
|
serverId: undefined,
|
||||||
wasAutopilotOriginallySet: false,
|
wasAutopilotOriginallySet: false,
|
||||||
throughput: 100,
|
throughput: 100,
|
||||||
@@ -28,7 +26,6 @@ describe("ThroughputInputAutoPilotV3Component", () => {
|
|||||||
spendAckVisible: false,
|
spendAckVisible: false,
|
||||||
showAsMandatory: true,
|
showAsMandatory: true,
|
||||||
isFixed: false,
|
isFixed: false,
|
||||||
isFreeTierAccount: false,
|
|
||||||
label: "label",
|
label: "label",
|
||||||
infoBubbleText: "infoBubbleText",
|
infoBubbleText: "infoBubbleText",
|
||||||
canExceedMaximumValue: true,
|
canExceedMaximumValue: true,
|
||||||
|
|||||||
@@ -28,6 +28,7 @@ import {
|
|||||||
Label,
|
Label,
|
||||||
Link,
|
Link,
|
||||||
MessageBar,
|
MessageBar,
|
||||||
|
MessageBarType,
|
||||||
FontIcon,
|
FontIcon,
|
||||||
IColumn
|
IColumn
|
||||||
} from "office-ui-fabric-react";
|
} from "office-ui-fabric-react";
|
||||||
@@ -41,13 +42,8 @@ import { SubscriptionType } from "../../../../../Contracts/SubscriptionType";
|
|||||||
import { usageInGB, calculateEstimateNumber } from "../../../../../Utils/PricingUtils";
|
import { usageInGB, calculateEstimateNumber } from "../../../../../Utils/PricingUtils";
|
||||||
import { Features } from "../../../../../Common/Constants";
|
import { Features } from "../../../../../Common/Constants";
|
||||||
|
|
||||||
import * as TelemetryProcessor from "../../../../../Shared/Telemetry/TelemetryProcessor";
|
|
||||||
import { Action, ActionModifiers } from "../../../../../Shared/Telemetry/TelemetryConstants";
|
|
||||||
|
|
||||||
export interface ThroughputInputAutoPilotV3Props {
|
export interface ThroughputInputAutoPilotV3Props {
|
||||||
databaseAccount: DataModels.DatabaseAccount;
|
databaseAccount: DataModels.DatabaseAccount;
|
||||||
databaseName: string;
|
|
||||||
collectionName: string;
|
|
||||||
serverId: string;
|
serverId: string;
|
||||||
throughput: number;
|
throughput: number;
|
||||||
throughputBaseline: number;
|
throughputBaseline: number;
|
||||||
@@ -62,7 +58,6 @@ export interface ThroughputInputAutoPilotV3Props {
|
|||||||
spendAckVisible?: boolean;
|
spendAckVisible?: boolean;
|
||||||
showAsMandatory?: boolean;
|
showAsMandatory?: boolean;
|
||||||
isFixed: boolean;
|
isFixed: boolean;
|
||||||
isFreeTierAccount: boolean;
|
|
||||||
isEmulator: boolean;
|
isEmulator: boolean;
|
||||||
label: string;
|
label: string;
|
||||||
infoBubbleText?: string;
|
infoBubbleText?: string;
|
||||||
@@ -81,7 +76,6 @@ export interface ThroughputInputAutoPilotV3Props {
|
|||||||
|
|
||||||
interface ThroughputInputAutoPilotV3State {
|
interface ThroughputInputAutoPilotV3State {
|
||||||
spendAckChecked: boolean;
|
spendAckChecked: boolean;
|
||||||
exceedFreeTierThroughput: boolean;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export class ThroughputInputAutoPilotV3Component extends React.Component<
|
export class ThroughputInputAutoPilotV3Component extends React.Component<
|
||||||
@@ -155,9 +149,7 @@ export class ThroughputInputAutoPilotV3Component extends React.Component<
|
|||||||
public constructor(props: ThroughputInputAutoPilotV3Props) {
|
public constructor(props: ThroughputInputAutoPilotV3Props) {
|
||||||
super(props);
|
super(props);
|
||||||
this.state = {
|
this.state = {
|
||||||
spendAckChecked: this.props.spendAckChecked,
|
spendAckChecked: this.props.spendAckChecked
|
||||||
exceedFreeTierThroughput:
|
|
||||||
this.props.isFreeTierAccount && !this.props.isAutoPilotSelected && this.props.throughput > 400
|
|
||||||
};
|
};
|
||||||
|
|
||||||
this.step = this.props.step ?? ThroughputInputAutoPilotV3Component.defaultStep;
|
this.step = this.props.step ?? ThroughputInputAutoPilotV3Component.defaultStep;
|
||||||
@@ -432,7 +424,7 @@ export class ThroughputInputAutoPilotV3Component extends React.Component<
|
|||||||
event: React.FormEvent<HTMLInputElement | HTMLTextAreaElement>,
|
event: React.FormEvent<HTMLInputElement | HTMLTextAreaElement>,
|
||||||
newValue?: string
|
newValue?: string
|
||||||
): void => {
|
): void => {
|
||||||
const newThroughput = getSanitizedInputValue(newValue);
|
const newThroughput = getSanitizedInputValue(newValue, this.autoPilotInputMaxValue);
|
||||||
this.props.onMaxAutoPilotThroughputChange(newThroughput);
|
this.props.onMaxAutoPilotThroughputChange(newThroughput);
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -440,11 +432,10 @@ export class ThroughputInputAutoPilotV3Component extends React.Component<
|
|||||||
event: React.FormEvent<HTMLInputElement | HTMLTextAreaElement>,
|
event: React.FormEvent<HTMLInputElement | HTMLTextAreaElement>,
|
||||||
newValue?: string
|
newValue?: string
|
||||||
): void => {
|
): void => {
|
||||||
const newThroughput = getSanitizedInputValue(newValue);
|
const newThroughput = getSanitizedInputValue(newValue, this.throughputInputMaxValue);
|
||||||
if (this.overrideWithAutoPilotSettings()) {
|
if (this.overrideWithAutoPilotSettings()) {
|
||||||
this.props.onMaxAutoPilotThroughputChange(newThroughput);
|
this.props.onMaxAutoPilotThroughputChange(newThroughput);
|
||||||
} else {
|
} else {
|
||||||
this.setState({ exceedFreeTierThroughput: this.props.isFreeTierAccount && newThroughput > 400 });
|
|
||||||
this.props.onThroughputChange(newThroughput);
|
this.props.onThroughputChange(newThroughput);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
@@ -452,19 +443,7 @@ export class ThroughputInputAutoPilotV3Component extends React.Component<
|
|||||||
private onChoiceGroupChange = (
|
private onChoiceGroupChange = (
|
||||||
event?: React.FormEvent<HTMLElement | HTMLInputElement>,
|
event?: React.FormEvent<HTMLElement | HTMLInputElement>,
|
||||||
option?: IChoiceGroupOption
|
option?: IChoiceGroupOption
|
||||||
): void => {
|
): void => this.props.onAutoPilotSelected(option.key === "true");
|
||||||
this.props.onAutoPilotSelected(option.key === "true");
|
|
||||||
TelemetryProcessor.trace(Action.ToggleAutoscaleSetting, ActionModifiers.Mark, {
|
|
||||||
changedSelectedValueTo:
|
|
||||||
option.key === "true" ? ActionModifiers.ToggleAutoscaleOn : ActionModifiers.ToggleAutoscaleOff,
|
|
||||||
subscriptionId: userContext.subscriptionId,
|
|
||||||
databaseAccountName: this.props.databaseAccount?.name,
|
|
||||||
databaseName: this.props.databaseName,
|
|
||||||
collectionName: this.props.collectionName,
|
|
||||||
apiKind: userContext.defaultExperience,
|
|
||||||
dataExplorerArea: "Scale Tab V2"
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
private minRUperGBSurvey = (): JSX.Element => {
|
private minRUperGBSurvey = (): JSX.Element => {
|
||||||
const href = `https://ncv.microsoft.com/vRBTO37jmO?ctx={"AzureSubscriptionId":"${userContext.subscriptionId}","CosmosDBAccountName":"${userContext.databaseAccount?.name}"}`;
|
const href = `https://ncv.microsoft.com/vRBTO37jmO?ctx={"AzureSubscriptionId":"${userContext.subscriptionId}","CosmosDBAccountName":"${userContext.databaseAccount?.name}"}`;
|
||||||
@@ -500,10 +479,7 @@ export class ThroughputInputAutoPilotV3Component extends React.Component<
|
|||||||
/>
|
/>
|
||||||
</Label>
|
</Label>
|
||||||
{this.overrideWithProvisionedThroughputSettings() && (
|
{this.overrideWithProvisionedThroughputSettings() && (
|
||||||
<MessageBar
|
<MessageBar messageBarType={MessageBarType.warning} styles={messageBarStyles}>
|
||||||
messageBarIconProps={{ iconName: "InfoSolid", className: "messageBarInfoIcon" }}
|
|
||||||
styles={messageBarStyles}
|
|
||||||
>
|
|
||||||
{manualToAutoscaleDisclaimerElement}
|
{manualToAutoscaleDisclaimerElement}
|
||||||
</MessageBar>
|
</MessageBar>
|
||||||
)}
|
)}
|
||||||
@@ -580,21 +556,8 @@ export class ThroughputInputAutoPilotV3Component extends React.Component<
|
|||||||
}
|
}
|
||||||
onChange={this.onThroughputChange}
|
onChange={this.onThroughputChange}
|
||||||
/>
|
/>
|
||||||
{this.state.exceedFreeTierThroughput && (
|
|
||||||
<MessageBar
|
|
||||||
messageBarIconProps={{ iconName: "WarningSolid", className: "messageBarWarningIcon" }}
|
|
||||||
styles={messageBarStyles}
|
|
||||||
>
|
|
||||||
{
|
|
||||||
"Billing will apply if you provision more than 400 RU/s of manual throughput, or if the resource scales beyond 400 RU/s with autoscale."
|
|
||||||
}
|
|
||||||
</MessageBar>
|
|
||||||
)}
|
|
||||||
{this.props.getThroughputWarningMessage() && (
|
{this.props.getThroughputWarningMessage() && (
|
||||||
<MessageBar
|
<MessageBar messageBarType={MessageBarType.warning} styles={messageBarStyles}>
|
||||||
messageBarIconProps={{ iconName: "InfoSolid", className: "messageBarInfoIcon" }}
|
|
||||||
styles={messageBarStyles}
|
|
||||||
>
|
|
||||||
{this.props.getThroughputWarningMessage()}
|
{this.props.getThroughputWarningMessage()}
|
||||||
</MessageBar>
|
</MessageBar>
|
||||||
)}
|
)}
|
||||||
@@ -620,15 +583,7 @@ export class ThroughputInputAutoPilotV3Component extends React.Component<
|
|||||||
warningMessage = saveThroughputWarningMessage;
|
warningMessage = saveThroughputWarningMessage;
|
||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return <>{warningMessage && <MessageBar messageBarType={MessageBarType.warning}>{warningMessage}</MessageBar>}</>;
|
||||||
<>
|
|
||||||
{warningMessage && (
|
|
||||||
<MessageBar messageBarIconProps={{ iconName: "WarningSolid", className: "messageBarWarningIcon" }}>
|
|
||||||
{warningMessage}
|
|
||||||
</MessageBar>
|
|
||||||
)}
|
|
||||||
</>
|
|
||||||
);
|
|
||||||
};
|
};
|
||||||
|
|
||||||
public render(): JSX.Element {
|
public render(): JSX.Element {
|
||||||
|
|||||||
@@ -9,18 +9,13 @@ exports[`ThroughputInputAutoPilotV3Component autopilot input visible 1`] = `
|
|||||||
}
|
}
|
||||||
>
|
>
|
||||||
<StyledMessageBarBase
|
<StyledMessageBarBase
|
||||||
messageBarIconProps={
|
messageBarType={5}
|
||||||
Object {
|
|
||||||
"className": "messageBarWarningIcon",
|
|
||||||
"iconName": "WarningSolid",
|
|
||||||
}
|
|
||||||
}
|
|
||||||
>
|
>
|
||||||
<Text
|
<Text
|
||||||
styles={
|
styles={
|
||||||
Object {
|
Object {
|
||||||
"root": Object {
|
"root": Object {
|
||||||
"fontSize": 14,
|
"fontSize": 12,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -39,7 +34,7 @@ exports[`ThroughputInputAutoPilotV3Component autopilot input visible 1`] = `
|
|||||||
styles={
|
styles={
|
||||||
Object {
|
Object {
|
||||||
"root": Object {
|
"root": Object {
|
||||||
"fontSize": 14,
|
"fontSize": 12,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -50,21 +45,12 @@ exports[`ThroughputInputAutoPilotV3Component autopilot input visible 1`] = `
|
|||||||
/>
|
/>
|
||||||
</StyledLabelBase>
|
</StyledLabelBase>
|
||||||
<StyledMessageBarBase
|
<StyledMessageBarBase
|
||||||
messageBarIconProps={
|
messageBarType={5}
|
||||||
Object {
|
|
||||||
"className": "messageBarInfoIcon",
|
|
||||||
"iconName": "InfoSolid",
|
|
||||||
}
|
|
||||||
}
|
|
||||||
styles={
|
styles={
|
||||||
Object {
|
Object {
|
||||||
"root": Object {
|
"root": Object {
|
||||||
"backgroundColor": "white",
|
|
||||||
"marginTop": "5px",
|
"marginTop": "5px",
|
||||||
},
|
},
|
||||||
"text": Object {
|
|
||||||
"fontSize": 14,
|
|
||||||
},
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
>
|
>
|
||||||
@@ -73,7 +59,7 @@ exports[`ThroughputInputAutoPilotV3Component autopilot input visible 1`] = `
|
|||||||
styles={
|
styles={
|
||||||
Object {
|
Object {
|
||||||
"root": Object {
|
"root": Object {
|
||||||
"fontSize": 14,
|
"fontSize": 12,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -185,7 +171,7 @@ exports[`ThroughputInputAutoPilotV3Component spendAck checkbox visible 1`] = `
|
|||||||
styles={
|
styles={
|
||||||
Object {
|
Object {
|
||||||
"root": Object {
|
"root": Object {
|
||||||
"fontSize": 14,
|
"fontSize": 12,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -458,7 +444,7 @@ exports[`ThroughputInputAutoPilotV3Component throughput input visible 1`] = `
|
|||||||
styles={
|
styles={
|
||||||
Object {
|
Object {
|
||||||
"root": Object {
|
"root": Object {
|
||||||
"fontSize": 14,
|
"fontSize": 12,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -16,7 +16,7 @@ exports[`ScaleComponent renders with correct initial notification 1`] = `
|
|||||||
styles={
|
styles={
|
||||||
Object {
|
Object {
|
||||||
"root": Object {
|
"root": Object {
|
||||||
"fontSize": 14,
|
"fontSize": 12,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -40,8 +40,6 @@ exports[`ScaleComponent renders with correct initial notification 1`] = `
|
|||||||
>
|
>
|
||||||
<ThroughputInputAutoPilotV3Component
|
<ThroughputInputAutoPilotV3Component
|
||||||
canExceedMaximumValue={true}
|
canExceedMaximumValue={true}
|
||||||
collectionName="test"
|
|
||||||
databaseName="test"
|
|
||||||
getThroughputWarningMessage={[Function]}
|
getThroughputWarningMessage={[Function]}
|
||||||
isAutoPilotSelected={false}
|
isAutoPilotSelected={false}
|
||||||
isEmulator={false}
|
isEmulator={false}
|
||||||
|
|||||||
@@ -136,7 +136,7 @@ exports[`SubSettingsComponent analyticalTimeToLive hidden 1`] = `
|
|||||||
styles={
|
styles={
|
||||||
Object {
|
Object {
|
||||||
"root": Object {
|
"root": Object {
|
||||||
"fontSize": 14,
|
"fontSize": 12,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -412,7 +412,7 @@ exports[`SubSettingsComponent analyticalTimeToLiveSeconds hidden 1`] = `
|
|||||||
styles={
|
styles={
|
||||||
Object {
|
Object {
|
||||||
"root": Object {
|
"root": Object {
|
||||||
"fontSize": 14,
|
"fontSize": 12,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -952,7 +952,7 @@ exports[`SubSettingsComponent renders 1`] = `
|
|||||||
styles={
|
styles={
|
||||||
Object {
|
Object {
|
||||||
"root": Object {
|
"root": Object {
|
||||||
"fontSize": 14,
|
"fontSize": 12,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1228,7 +1228,7 @@ exports[`SubSettingsComponent timeToLiveSeconds hidden 1`] = `
|
|||||||
styles={
|
styles={
|
||||||
Object {
|
Object {
|
||||||
"root": Object {
|
"root": Object {
|
||||||
"fontSize": 14,
|
"fontSize": 12,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -101,13 +101,13 @@ export const parseConflictResolutionProcedure = (procedureFromBackEnd: string):
|
|||||||
return procedureFromBackEnd;
|
return procedureFromBackEnd;
|
||||||
};
|
};
|
||||||
|
|
||||||
export const getSanitizedInputValue = (newValueString: string, max?: number): number => {
|
export const getSanitizedInputValue = (newValueString: string, max: number): number => {
|
||||||
const newValue = parseInt(newValueString);
|
const newValue = parseInt(newValueString);
|
||||||
if (isNaN(newValue)) {
|
if (isNaN(newValue)) {
|
||||||
return zeroValue;
|
return zeroValue;
|
||||||
}
|
}
|
||||||
// make sure new value does not exceed the maximum throughput
|
// make sure new value does not exceed the maximum throughput
|
||||||
return max ? Math.min(newValue, max) : newValue;
|
return Math.min(newValue, max);
|
||||||
};
|
};
|
||||||
|
|
||||||
export const isDirty = (current: isDirtyTypes, baseline: isDirtyTypes): boolean => {
|
export const isDirty = (current: isDirtyTypes, baseline: isDirtyTypes): boolean => {
|
||||||
|
|||||||
@@ -55,7 +55,6 @@ exports[`SettingsComponent renders 1`] = `
|
|||||||
"firstFieldHasFocus": [Function],
|
"firstFieldHasFocus": [Function],
|
||||||
"formErrors": [Function],
|
"formErrors": [Function],
|
||||||
"formErrorsDetails": [Function],
|
"formErrorsDetails": [Function],
|
||||||
"freeTierExceedThroughputTooltip": [Function],
|
|
||||||
"id": "adddatabasepane",
|
"id": "adddatabasepane",
|
||||||
"isAutoPilotSelected": [Function],
|
"isAutoPilotSelected": [Function],
|
||||||
"isExecuting": [Function],
|
"isExecuting": [Function],
|
||||||
@@ -105,7 +104,6 @@ exports[`SettingsComponent renders 1`] = `
|
|||||||
"formErrors": [Function],
|
"formErrors": [Function],
|
||||||
"formErrorsDetails": [Function],
|
"formErrorsDetails": [Function],
|
||||||
"formWarnings": [Function],
|
"formWarnings": [Function],
|
||||||
"freeTierExceedThroughputTooltip": [Function],
|
|
||||||
"id": "addcollectionpane",
|
"id": "addcollectionpane",
|
||||||
"isAnalyticalStorageOn": [Function],
|
"isAnalyticalStorageOn": [Function],
|
||||||
"isAutoPilotSelected": [Function],
|
"isAutoPilotSelected": [Function],
|
||||||
@@ -593,7 +591,6 @@ exports[`SettingsComponent renders 1`] = `
|
|||||||
"formErrors": [Function],
|
"formErrors": [Function],
|
||||||
"formErrorsDetails": [Function],
|
"formErrorsDetails": [Function],
|
||||||
"formWarnings": [Function],
|
"formWarnings": [Function],
|
||||||
"freeTierExceedThroughputTooltip": [Function],
|
|
||||||
"id": "addcollectionpane",
|
"id": "addcollectionpane",
|
||||||
"isAnalyticalStorageOn": [Function],
|
"isAnalyticalStorageOn": [Function],
|
||||||
"isAutoPilotSelected": [Function],
|
"isAutoPilotSelected": [Function],
|
||||||
@@ -668,7 +665,6 @@ exports[`SettingsComponent renders 1`] = `
|
|||||||
"firstFieldHasFocus": [Function],
|
"firstFieldHasFocus": [Function],
|
||||||
"formErrors": [Function],
|
"formErrors": [Function],
|
||||||
"formErrorsDetails": [Function],
|
"formErrorsDetails": [Function],
|
||||||
"freeTierExceedThroughputTooltip": [Function],
|
|
||||||
"id": "adddatabasepane",
|
"id": "adddatabasepane",
|
||||||
"isAutoPilotSelected": [Function],
|
"isAutoPilotSelected": [Function],
|
||||||
"isExecuting": [Function],
|
"isExecuting": [Function],
|
||||||
@@ -946,7 +942,6 @@ exports[`SettingsComponent renders 1`] = `
|
|||||||
"hasWriteAccess": [Function],
|
"hasWriteAccess": [Function],
|
||||||
"isAccountReady": [Function],
|
"isAccountReady": [Function],
|
||||||
"isAuthWithResourceToken": [Function],
|
"isAuthWithResourceToken": [Function],
|
||||||
"isAutoscaleDefaultEnabled": [Function],
|
|
||||||
"isCopyNotebookPaneEnabled": [Function],
|
"isCopyNotebookPaneEnabled": [Function],
|
||||||
"isEnableMongoCapabilityPresent": [Function],
|
"isEnableMongoCapabilityPresent": [Function],
|
||||||
"isFixedCollectionWithSharedThroughputSupported": [Function],
|
"isFixedCollectionWithSharedThroughputSupported": [Function],
|
||||||
@@ -955,7 +950,6 @@ exports[`SettingsComponent renders 1`] = `
|
|||||||
"isHostedDataExplorerEnabled": [Function],
|
"isHostedDataExplorerEnabled": [Function],
|
||||||
"isLeftPaneExpanded": [Function],
|
"isLeftPaneExpanded": [Function],
|
||||||
"isLinkInjectionEnabled": [Function],
|
"isLinkInjectionEnabled": [Function],
|
||||||
"isMongoIndexingEnabled": [Function],
|
|
||||||
"isNotebookEnabled": [Function],
|
"isNotebookEnabled": [Function],
|
||||||
"isNotebooksEnabledForAccount": [Function],
|
"isNotebooksEnabledForAccount": [Function],
|
||||||
"isNotificationConsoleExpanded": [Function],
|
"isNotificationConsoleExpanded": [Function],
|
||||||
@@ -1120,14 +1114,6 @@ exports[`SettingsComponent renders 1`] = `
|
|||||||
},
|
},
|
||||||
"selectedDatabaseId": [Function],
|
"selectedDatabaseId": [Function],
|
||||||
"selectedNode": [Function],
|
"selectedNode": [Function],
|
||||||
"selfServeComponentAdapter": SelfServeComponentAdapter {
|
|
||||||
"container": [Circular],
|
|
||||||
"parameters": [Function],
|
|
||||||
},
|
|
||||||
"selfServeLoadingComponentAdapter": SelfServeLoadingComponentAdapter {
|
|
||||||
"parameters": [Function],
|
|
||||||
},
|
|
||||||
"selfServeType": [Function],
|
|
||||||
"serverId": [Function],
|
"serverId": [Function],
|
||||||
"settingsPane": SettingsPane {
|
"settingsPane": SettingsPane {
|
||||||
"container": [Circular],
|
"container": [Circular],
|
||||||
@@ -1189,9 +1175,11 @@ exports[`SettingsComponent renders 1`] = `
|
|||||||
},
|
},
|
||||||
"direction": "vertical",
|
"direction": "vertical",
|
||||||
"isCollapsed": [Function],
|
"isCollapsed": [Function],
|
||||||
|
"leftSide": null,
|
||||||
"leftSideId": "resourcetree",
|
"leftSideId": "resourcetree",
|
||||||
"onResizeStart": [Function],
|
"onResizeStart": [Function],
|
||||||
"onResizeStop": [Function],
|
"onResizeStop": [Function],
|
||||||
|
"splitter": null,
|
||||||
"splitterId": "h_splitter1",
|
"splitterId": "h_splitter1",
|
||||||
},
|
},
|
||||||
"stringInputPane": StringInputPane {
|
"stringInputPane": StringInputPane {
|
||||||
@@ -1338,7 +1326,6 @@ exports[`SettingsComponent renders 1`] = `
|
|||||||
"firstFieldHasFocus": [Function],
|
"firstFieldHasFocus": [Function],
|
||||||
"formErrors": [Function],
|
"formErrors": [Function],
|
||||||
"formErrorsDetails": [Function],
|
"formErrorsDetails": [Function],
|
||||||
"freeTierExceedThroughputTooltip": [Function],
|
|
||||||
"id": "adddatabasepane",
|
"id": "adddatabasepane",
|
||||||
"isAutoPilotSelected": [Function],
|
"isAutoPilotSelected": [Function],
|
||||||
"isExecuting": [Function],
|
"isExecuting": [Function],
|
||||||
@@ -1388,7 +1375,6 @@ exports[`SettingsComponent renders 1`] = `
|
|||||||
"formErrors": [Function],
|
"formErrors": [Function],
|
||||||
"formErrorsDetails": [Function],
|
"formErrorsDetails": [Function],
|
||||||
"formWarnings": [Function],
|
"formWarnings": [Function],
|
||||||
"freeTierExceedThroughputTooltip": [Function],
|
|
||||||
"id": "addcollectionpane",
|
"id": "addcollectionpane",
|
||||||
"isAnalyticalStorageOn": [Function],
|
"isAnalyticalStorageOn": [Function],
|
||||||
"isAutoPilotSelected": [Function],
|
"isAutoPilotSelected": [Function],
|
||||||
@@ -1876,7 +1862,6 @@ exports[`SettingsComponent renders 1`] = `
|
|||||||
"formErrors": [Function],
|
"formErrors": [Function],
|
||||||
"formErrorsDetails": [Function],
|
"formErrorsDetails": [Function],
|
||||||
"formWarnings": [Function],
|
"formWarnings": [Function],
|
||||||
"freeTierExceedThroughputTooltip": [Function],
|
|
||||||
"id": "addcollectionpane",
|
"id": "addcollectionpane",
|
||||||
"isAnalyticalStorageOn": [Function],
|
"isAnalyticalStorageOn": [Function],
|
||||||
"isAutoPilotSelected": [Function],
|
"isAutoPilotSelected": [Function],
|
||||||
@@ -1951,7 +1936,6 @@ exports[`SettingsComponent renders 1`] = `
|
|||||||
"firstFieldHasFocus": [Function],
|
"firstFieldHasFocus": [Function],
|
||||||
"formErrors": [Function],
|
"formErrors": [Function],
|
||||||
"formErrorsDetails": [Function],
|
"formErrorsDetails": [Function],
|
||||||
"freeTierExceedThroughputTooltip": [Function],
|
|
||||||
"id": "adddatabasepane",
|
"id": "adddatabasepane",
|
||||||
"isAutoPilotSelected": [Function],
|
"isAutoPilotSelected": [Function],
|
||||||
"isExecuting": [Function],
|
"isExecuting": [Function],
|
||||||
@@ -2229,7 +2213,6 @@ exports[`SettingsComponent renders 1`] = `
|
|||||||
"hasWriteAccess": [Function],
|
"hasWriteAccess": [Function],
|
||||||
"isAccountReady": [Function],
|
"isAccountReady": [Function],
|
||||||
"isAuthWithResourceToken": [Function],
|
"isAuthWithResourceToken": [Function],
|
||||||
"isAutoscaleDefaultEnabled": [Function],
|
|
||||||
"isCopyNotebookPaneEnabled": [Function],
|
"isCopyNotebookPaneEnabled": [Function],
|
||||||
"isEnableMongoCapabilityPresent": [Function],
|
"isEnableMongoCapabilityPresent": [Function],
|
||||||
"isFixedCollectionWithSharedThroughputSupported": [Function],
|
"isFixedCollectionWithSharedThroughputSupported": [Function],
|
||||||
@@ -2238,7 +2221,6 @@ exports[`SettingsComponent renders 1`] = `
|
|||||||
"isHostedDataExplorerEnabled": [Function],
|
"isHostedDataExplorerEnabled": [Function],
|
||||||
"isLeftPaneExpanded": [Function],
|
"isLeftPaneExpanded": [Function],
|
||||||
"isLinkInjectionEnabled": [Function],
|
"isLinkInjectionEnabled": [Function],
|
||||||
"isMongoIndexingEnabled": [Function],
|
|
||||||
"isNotebookEnabled": [Function],
|
"isNotebookEnabled": [Function],
|
||||||
"isNotebooksEnabledForAccount": [Function],
|
"isNotebooksEnabledForAccount": [Function],
|
||||||
"isNotificationConsoleExpanded": [Function],
|
"isNotificationConsoleExpanded": [Function],
|
||||||
@@ -2403,14 +2385,6 @@ exports[`SettingsComponent renders 1`] = `
|
|||||||
},
|
},
|
||||||
"selectedDatabaseId": [Function],
|
"selectedDatabaseId": [Function],
|
||||||
"selectedNode": [Function],
|
"selectedNode": [Function],
|
||||||
"selfServeComponentAdapter": SelfServeComponentAdapter {
|
|
||||||
"container": [Circular],
|
|
||||||
"parameters": [Function],
|
|
||||||
},
|
|
||||||
"selfServeLoadingComponentAdapter": SelfServeLoadingComponentAdapter {
|
|
||||||
"parameters": [Function],
|
|
||||||
},
|
|
||||||
"selfServeType": [Function],
|
|
||||||
"serverId": [Function],
|
"serverId": [Function],
|
||||||
"settingsPane": SettingsPane {
|
"settingsPane": SettingsPane {
|
||||||
"container": [Circular],
|
"container": [Circular],
|
||||||
@@ -2472,9 +2446,11 @@ exports[`SettingsComponent renders 1`] = `
|
|||||||
},
|
},
|
||||||
"direction": "vertical",
|
"direction": "vertical",
|
||||||
"isCollapsed": [Function],
|
"isCollapsed": [Function],
|
||||||
|
"leftSide": null,
|
||||||
"leftSideId": "resourcetree",
|
"leftSideId": "resourcetree",
|
||||||
"onResizeStart": [Function],
|
"onResizeStart": [Function],
|
||||||
"onResizeStop": [Function],
|
"onResizeStop": [Function],
|
||||||
|
"splitter": null,
|
||||||
"splitterId": "h_splitter1",
|
"splitterId": "h_splitter1",
|
||||||
},
|
},
|
||||||
"stringInputPane": StringInputPane {
|
"stringInputPane": StringInputPane {
|
||||||
@@ -2634,7 +2610,6 @@ exports[`SettingsComponent renders 1`] = `
|
|||||||
"firstFieldHasFocus": [Function],
|
"firstFieldHasFocus": [Function],
|
||||||
"formErrors": [Function],
|
"formErrors": [Function],
|
||||||
"formErrorsDetails": [Function],
|
"formErrorsDetails": [Function],
|
||||||
"freeTierExceedThroughputTooltip": [Function],
|
|
||||||
"id": "adddatabasepane",
|
"id": "adddatabasepane",
|
||||||
"isAutoPilotSelected": [Function],
|
"isAutoPilotSelected": [Function],
|
||||||
"isExecuting": [Function],
|
"isExecuting": [Function],
|
||||||
@@ -2684,7 +2659,6 @@ exports[`SettingsComponent renders 1`] = `
|
|||||||
"formErrors": [Function],
|
"formErrors": [Function],
|
||||||
"formErrorsDetails": [Function],
|
"formErrorsDetails": [Function],
|
||||||
"formWarnings": [Function],
|
"formWarnings": [Function],
|
||||||
"freeTierExceedThroughputTooltip": [Function],
|
|
||||||
"id": "addcollectionpane",
|
"id": "addcollectionpane",
|
||||||
"isAnalyticalStorageOn": [Function],
|
"isAnalyticalStorageOn": [Function],
|
||||||
"isAutoPilotSelected": [Function],
|
"isAutoPilotSelected": [Function],
|
||||||
@@ -3172,7 +3146,6 @@ exports[`SettingsComponent renders 1`] = `
|
|||||||
"formErrors": [Function],
|
"formErrors": [Function],
|
||||||
"formErrorsDetails": [Function],
|
"formErrorsDetails": [Function],
|
||||||
"formWarnings": [Function],
|
"formWarnings": [Function],
|
||||||
"freeTierExceedThroughputTooltip": [Function],
|
|
||||||
"id": "addcollectionpane",
|
"id": "addcollectionpane",
|
||||||
"isAnalyticalStorageOn": [Function],
|
"isAnalyticalStorageOn": [Function],
|
||||||
"isAutoPilotSelected": [Function],
|
"isAutoPilotSelected": [Function],
|
||||||
@@ -3247,7 +3220,6 @@ exports[`SettingsComponent renders 1`] = `
|
|||||||
"firstFieldHasFocus": [Function],
|
"firstFieldHasFocus": [Function],
|
||||||
"formErrors": [Function],
|
"formErrors": [Function],
|
||||||
"formErrorsDetails": [Function],
|
"formErrorsDetails": [Function],
|
||||||
"freeTierExceedThroughputTooltip": [Function],
|
|
||||||
"id": "adddatabasepane",
|
"id": "adddatabasepane",
|
||||||
"isAutoPilotSelected": [Function],
|
"isAutoPilotSelected": [Function],
|
||||||
"isExecuting": [Function],
|
"isExecuting": [Function],
|
||||||
@@ -3525,7 +3497,6 @@ exports[`SettingsComponent renders 1`] = `
|
|||||||
"hasWriteAccess": [Function],
|
"hasWriteAccess": [Function],
|
||||||
"isAccountReady": [Function],
|
"isAccountReady": [Function],
|
||||||
"isAuthWithResourceToken": [Function],
|
"isAuthWithResourceToken": [Function],
|
||||||
"isAutoscaleDefaultEnabled": [Function],
|
|
||||||
"isCopyNotebookPaneEnabled": [Function],
|
"isCopyNotebookPaneEnabled": [Function],
|
||||||
"isEnableMongoCapabilityPresent": [Function],
|
"isEnableMongoCapabilityPresent": [Function],
|
||||||
"isFixedCollectionWithSharedThroughputSupported": [Function],
|
"isFixedCollectionWithSharedThroughputSupported": [Function],
|
||||||
@@ -3534,7 +3505,6 @@ exports[`SettingsComponent renders 1`] = `
|
|||||||
"isHostedDataExplorerEnabled": [Function],
|
"isHostedDataExplorerEnabled": [Function],
|
||||||
"isLeftPaneExpanded": [Function],
|
"isLeftPaneExpanded": [Function],
|
||||||
"isLinkInjectionEnabled": [Function],
|
"isLinkInjectionEnabled": [Function],
|
||||||
"isMongoIndexingEnabled": [Function],
|
|
||||||
"isNotebookEnabled": [Function],
|
"isNotebookEnabled": [Function],
|
||||||
"isNotebooksEnabledForAccount": [Function],
|
"isNotebooksEnabledForAccount": [Function],
|
||||||
"isNotificationConsoleExpanded": [Function],
|
"isNotificationConsoleExpanded": [Function],
|
||||||
@@ -3699,14 +3669,6 @@ exports[`SettingsComponent renders 1`] = `
|
|||||||
},
|
},
|
||||||
"selectedDatabaseId": [Function],
|
"selectedDatabaseId": [Function],
|
||||||
"selectedNode": [Function],
|
"selectedNode": [Function],
|
||||||
"selfServeComponentAdapter": SelfServeComponentAdapter {
|
|
||||||
"container": [Circular],
|
|
||||||
"parameters": [Function],
|
|
||||||
},
|
|
||||||
"selfServeLoadingComponentAdapter": SelfServeLoadingComponentAdapter {
|
|
||||||
"parameters": [Function],
|
|
||||||
},
|
|
||||||
"selfServeType": [Function],
|
|
||||||
"serverId": [Function],
|
"serverId": [Function],
|
||||||
"settingsPane": SettingsPane {
|
"settingsPane": SettingsPane {
|
||||||
"container": [Circular],
|
"container": [Circular],
|
||||||
@@ -3768,9 +3730,11 @@ exports[`SettingsComponent renders 1`] = `
|
|||||||
},
|
},
|
||||||
"direction": "vertical",
|
"direction": "vertical",
|
||||||
"isCollapsed": [Function],
|
"isCollapsed": [Function],
|
||||||
|
"leftSide": null,
|
||||||
"leftSideId": "resourcetree",
|
"leftSideId": "resourcetree",
|
||||||
"onResizeStart": [Function],
|
"onResizeStart": [Function],
|
||||||
"onResizeStop": [Function],
|
"onResizeStop": [Function],
|
||||||
|
"splitter": null,
|
||||||
"splitterId": "h_splitter1",
|
"splitterId": "h_splitter1",
|
||||||
},
|
},
|
||||||
"stringInputPane": StringInputPane {
|
"stringInputPane": StringInputPane {
|
||||||
@@ -3917,7 +3881,6 @@ exports[`SettingsComponent renders 1`] = `
|
|||||||
"firstFieldHasFocus": [Function],
|
"firstFieldHasFocus": [Function],
|
||||||
"formErrors": [Function],
|
"formErrors": [Function],
|
||||||
"formErrorsDetails": [Function],
|
"formErrorsDetails": [Function],
|
||||||
"freeTierExceedThroughputTooltip": [Function],
|
|
||||||
"id": "adddatabasepane",
|
"id": "adddatabasepane",
|
||||||
"isAutoPilotSelected": [Function],
|
"isAutoPilotSelected": [Function],
|
||||||
"isExecuting": [Function],
|
"isExecuting": [Function],
|
||||||
@@ -3967,7 +3930,6 @@ exports[`SettingsComponent renders 1`] = `
|
|||||||
"formErrors": [Function],
|
"formErrors": [Function],
|
||||||
"formErrorsDetails": [Function],
|
"formErrorsDetails": [Function],
|
||||||
"formWarnings": [Function],
|
"formWarnings": [Function],
|
||||||
"freeTierExceedThroughputTooltip": [Function],
|
|
||||||
"id": "addcollectionpane",
|
"id": "addcollectionpane",
|
||||||
"isAnalyticalStorageOn": [Function],
|
"isAnalyticalStorageOn": [Function],
|
||||||
"isAutoPilotSelected": [Function],
|
"isAutoPilotSelected": [Function],
|
||||||
@@ -4455,7 +4417,6 @@ exports[`SettingsComponent renders 1`] = `
|
|||||||
"formErrors": [Function],
|
"formErrors": [Function],
|
||||||
"formErrorsDetails": [Function],
|
"formErrorsDetails": [Function],
|
||||||
"formWarnings": [Function],
|
"formWarnings": [Function],
|
||||||
"freeTierExceedThroughputTooltip": [Function],
|
|
||||||
"id": "addcollectionpane",
|
"id": "addcollectionpane",
|
||||||
"isAnalyticalStorageOn": [Function],
|
"isAnalyticalStorageOn": [Function],
|
||||||
"isAutoPilotSelected": [Function],
|
"isAutoPilotSelected": [Function],
|
||||||
@@ -4530,7 +4491,6 @@ exports[`SettingsComponent renders 1`] = `
|
|||||||
"firstFieldHasFocus": [Function],
|
"firstFieldHasFocus": [Function],
|
||||||
"formErrors": [Function],
|
"formErrors": [Function],
|
||||||
"formErrorsDetails": [Function],
|
"formErrorsDetails": [Function],
|
||||||
"freeTierExceedThroughputTooltip": [Function],
|
|
||||||
"id": "adddatabasepane",
|
"id": "adddatabasepane",
|
||||||
"isAutoPilotSelected": [Function],
|
"isAutoPilotSelected": [Function],
|
||||||
"isExecuting": [Function],
|
"isExecuting": [Function],
|
||||||
@@ -4808,7 +4768,6 @@ exports[`SettingsComponent renders 1`] = `
|
|||||||
"hasWriteAccess": [Function],
|
"hasWriteAccess": [Function],
|
||||||
"isAccountReady": [Function],
|
"isAccountReady": [Function],
|
||||||
"isAuthWithResourceToken": [Function],
|
"isAuthWithResourceToken": [Function],
|
||||||
"isAutoscaleDefaultEnabled": [Function],
|
|
||||||
"isCopyNotebookPaneEnabled": [Function],
|
"isCopyNotebookPaneEnabled": [Function],
|
||||||
"isEnableMongoCapabilityPresent": [Function],
|
"isEnableMongoCapabilityPresent": [Function],
|
||||||
"isFixedCollectionWithSharedThroughputSupported": [Function],
|
"isFixedCollectionWithSharedThroughputSupported": [Function],
|
||||||
@@ -4817,7 +4776,6 @@ exports[`SettingsComponent renders 1`] = `
|
|||||||
"isHostedDataExplorerEnabled": [Function],
|
"isHostedDataExplorerEnabled": [Function],
|
||||||
"isLeftPaneExpanded": [Function],
|
"isLeftPaneExpanded": [Function],
|
||||||
"isLinkInjectionEnabled": [Function],
|
"isLinkInjectionEnabled": [Function],
|
||||||
"isMongoIndexingEnabled": [Function],
|
|
||||||
"isNotebookEnabled": [Function],
|
"isNotebookEnabled": [Function],
|
||||||
"isNotebooksEnabledForAccount": [Function],
|
"isNotebooksEnabledForAccount": [Function],
|
||||||
"isNotificationConsoleExpanded": [Function],
|
"isNotificationConsoleExpanded": [Function],
|
||||||
@@ -4982,14 +4940,6 @@ exports[`SettingsComponent renders 1`] = `
|
|||||||
},
|
},
|
||||||
"selectedDatabaseId": [Function],
|
"selectedDatabaseId": [Function],
|
||||||
"selectedNode": [Function],
|
"selectedNode": [Function],
|
||||||
"selfServeComponentAdapter": SelfServeComponentAdapter {
|
|
||||||
"container": [Circular],
|
|
||||||
"parameters": [Function],
|
|
||||||
},
|
|
||||||
"selfServeLoadingComponentAdapter": SelfServeLoadingComponentAdapter {
|
|
||||||
"parameters": [Function],
|
|
||||||
},
|
|
||||||
"selfServeType": [Function],
|
|
||||||
"serverId": [Function],
|
"serverId": [Function],
|
||||||
"settingsPane": SettingsPane {
|
"settingsPane": SettingsPane {
|
||||||
"container": [Circular],
|
"container": [Circular],
|
||||||
@@ -5051,9 +5001,11 @@ exports[`SettingsComponent renders 1`] = `
|
|||||||
},
|
},
|
||||||
"direction": "vertical",
|
"direction": "vertical",
|
||||||
"isCollapsed": [Function],
|
"isCollapsed": [Function],
|
||||||
|
"leftSide": null,
|
||||||
"leftSideId": "resourcetree",
|
"leftSideId": "resourcetree",
|
||||||
"onResizeStart": [Function],
|
"onResizeStart": [Function],
|
||||||
"onResizeStop": [Function],
|
"onResizeStop": [Function],
|
||||||
|
"splitter": null,
|
||||||
"splitterId": "h_splitter1",
|
"splitterId": "h_splitter1",
|
||||||
},
|
},
|
||||||
"stringInputPane": StringInputPane {
|
"stringInputPane": StringInputPane {
|
||||||
|
|||||||
@@ -159,7 +159,7 @@ exports[`SettingsUtils functions render 1`] = `
|
|||||||
styles={
|
styles={
|
||||||
Object {
|
Object {
|
||||||
"root": Object {
|
"root": Object {
|
||||||
"fontSize": 14,
|
"fontSize": 12,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -176,7 +176,7 @@ exports[`SettingsUtils functions render 1`] = `
|
|||||||
styles={
|
styles={
|
||||||
Object {
|
Object {
|
||||||
"root": Object {
|
"root": Object {
|
||||||
"fontSize": 14,
|
"fontSize": 12,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -195,7 +195,7 @@ exports[`SettingsUtils functions render 1`] = `
|
|||||||
styles={
|
styles={
|
||||||
Object {
|
Object {
|
||||||
"root": Object {
|
"root": Object {
|
||||||
"fontSize": 14,
|
"fontSize": 12,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -207,7 +207,7 @@ exports[`SettingsUtils functions render 1`] = `
|
|||||||
styles={
|
styles={
|
||||||
Object {
|
Object {
|
||||||
"root": Object {
|
"root": Object {
|
||||||
"fontSize": 14,
|
"fontSize": 12,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -219,7 +219,7 @@ exports[`SettingsUtils functions render 1`] = `
|
|||||||
styles={
|
styles={
|
||||||
Object {
|
Object {
|
||||||
"root": Object {
|
"root": Object {
|
||||||
"fontSize": 14,
|
"fontSize": 12,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -230,7 +230,7 @@ exports[`SettingsUtils functions render 1`] = `
|
|||||||
styles={
|
styles={
|
||||||
Object {
|
Object {
|
||||||
"root": Object {
|
"root": Object {
|
||||||
"fontSize": 14,
|
"fontSize": 12,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -249,7 +249,7 @@ exports[`SettingsUtils functions render 1`] = `
|
|||||||
styles={
|
styles={
|
||||||
Object {
|
Object {
|
||||||
"root": Object {
|
"root": Object {
|
||||||
"fontSize": 14,
|
"fontSize": 12,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -268,7 +268,7 @@ exports[`SettingsUtils functions render 1`] = `
|
|||||||
styles={
|
styles={
|
||||||
Object {
|
Object {
|
||||||
"root": Object {
|
"root": Object {
|
||||||
"fontSize": 14,
|
"fontSize": 12,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -286,7 +286,7 @@ exports[`SettingsUtils functions render 1`] = `
|
|||||||
styles={
|
styles={
|
||||||
Object {
|
Object {
|
||||||
"root": Object {
|
"root": Object {
|
||||||
"fontSize": 14,
|
"fontSize": 12,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -299,7 +299,7 @@ exports[`SettingsUtils functions render 1`] = `
|
|||||||
styles={
|
styles={
|
||||||
Object {
|
Object {
|
||||||
"root": Object {
|
"root": Object {
|
||||||
"fontSize": 14,
|
"fontSize": 12,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -310,7 +310,7 @@ exports[`SettingsUtils functions render 1`] = `
|
|||||||
styles={
|
styles={
|
||||||
Object {
|
Object {
|
||||||
"root": Object {
|
"root": Object {
|
||||||
"fontSize": 14,
|
"fontSize": 12,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -329,7 +329,7 @@ exports[`SettingsUtils functions render 1`] = `
|
|||||||
styles={
|
styles={
|
||||||
Object {
|
Object {
|
||||||
"root": Object {
|
"root": Object {
|
||||||
"fontSize": 14,
|
"fontSize": 12,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -371,7 +371,7 @@ exports[`SettingsUtils functions render 1`] = `
|
|||||||
styles={
|
styles={
|
||||||
Object {
|
Object {
|
||||||
"root": Object {
|
"root": Object {
|
||||||
"fontSize": 14,
|
"fontSize": 12,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -386,7 +386,7 @@ exports[`SettingsUtils functions render 1`] = `
|
|||||||
styles={
|
styles={
|
||||||
Object {
|
Object {
|
||||||
"root": Object {
|
"root": Object {
|
||||||
"fontSize": 14,
|
"fontSize": 12,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -402,7 +402,7 @@ exports[`SettingsUtils functions render 1`] = `
|
|||||||
styles={
|
styles={
|
||||||
Object {
|
Object {
|
||||||
"root": Object {
|
"root": Object {
|
||||||
"fontSize": 14,
|
"fontSize": 12,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,9 +1,9 @@
|
|||||||
import React from "react";
|
import React from "react";
|
||||||
import { shallow } from "enzyme";
|
import { shallow } from "enzyme";
|
||||||
import { SmartUiComponent, SmartUiDescriptor, UiType } from "./SmartUiComponent";
|
import { SmartUiComponent, Descriptor, InputType } from "./SmartUiComponent";
|
||||||
|
|
||||||
describe("SmartUiComponent", () => {
|
describe("SmartUiComponent", () => {
|
||||||
const exampleData: SmartUiDescriptor = {
|
const exampleData: Descriptor = {
|
||||||
root: {
|
root: {
|
||||||
id: "root",
|
id: "root",
|
||||||
info: {
|
info: {
|
||||||
@@ -24,7 +24,7 @@ describe("SmartUiComponent", () => {
|
|||||||
max: 500,
|
max: 500,
|
||||||
step: 10,
|
step: 10,
|
||||||
defaultValue: 400,
|
defaultValue: 400,
|
||||||
uiType: UiType.Spinner
|
inputType: "spin"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@@ -37,21 +37,7 @@ describe("SmartUiComponent", () => {
|
|||||||
max: 500,
|
max: 500,
|
||||||
step: 10,
|
step: 10,
|
||||||
defaultValue: 400,
|
defaultValue: 400,
|
||||||
uiType: UiType.Slider
|
inputType: "slider"
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
id: "throughput3",
|
|
||||||
input: {
|
|
||||||
label: "Throughput (invalid)",
|
|
||||||
dataFieldName: "throughput3",
|
|
||||||
type: "boolean",
|
|
||||||
min: 400,
|
|
||||||
max: 500,
|
|
||||||
step: 10,
|
|
||||||
defaultValue: 400,
|
|
||||||
uiType: UiType.Spinner,
|
|
||||||
errorMessage: "label, truelabel and falselabel are required for boolean input 'throughput3'"
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@@ -78,11 +64,11 @@ describe("SmartUiComponent", () => {
|
|||||||
input: {
|
input: {
|
||||||
label: "Database",
|
label: "Database",
|
||||||
dataFieldName: "database",
|
dataFieldName: "database",
|
||||||
type: "object",
|
type: "enum",
|
||||||
choices: [
|
choices: [
|
||||||
{ label: "Database 1", key: "db1" },
|
{ label: "Database 1", key: "db1", value: "database1" },
|
||||||
{ label: "Database 2", key: "db2" },
|
{ label: "Database 2", key: "db2", value: "database2" },
|
||||||
{ label: "Database 3", key: "db3" }
|
{ label: "Database 3", key: "db3", value: "database3" }
|
||||||
],
|
],
|
||||||
defaultKey: "db2"
|
defaultKey: "db2"
|
||||||
}
|
}
|
||||||
@@ -91,11 +77,12 @@ describe("SmartUiComponent", () => {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
it("should render", async () => {
|
const exampleCallbacks = (newValues: Map<string, InputType>): void => {
|
||||||
const wrapper = shallow(
|
console.log("New values:", newValues);
|
||||||
<SmartUiComponent descriptor={exampleData} currentValues={new Map()} onInputChange={undefined} />
|
};
|
||||||
);
|
|
||||||
await new Promise(resolve => setTimeout(resolve, 0));
|
it("should render", () => {
|
||||||
|
const wrapper = shallow(<SmartUiComponent descriptor={exampleData} onChange={exampleCallbacks} />);
|
||||||
expect(wrapper).toMatchSnapshot();
|
expect(wrapper).toMatchSnapshot();
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -5,9 +5,11 @@ import { SpinButton } from "office-ui-fabric-react/lib/SpinButton";
|
|||||||
import { Dropdown, IDropdownOption } from "office-ui-fabric-react/lib/Dropdown";
|
import { Dropdown, IDropdownOption } from "office-ui-fabric-react/lib/Dropdown";
|
||||||
import { TextField } from "office-ui-fabric-react/lib/TextField";
|
import { TextField } from "office-ui-fabric-react/lib/TextField";
|
||||||
import { Text } from "office-ui-fabric-react/lib/Text";
|
import { Text } from "office-ui-fabric-react/lib/Text";
|
||||||
|
import { InputType } from "../../Tables/Constants";
|
||||||
import { RadioSwitchComponent } from "../RadioSwitchComponent/RadioSwitchComponent";
|
import { RadioSwitchComponent } from "../RadioSwitchComponent/RadioSwitchComponent";
|
||||||
import { Stack, IStackTokens } from "office-ui-fabric-react/lib/Stack";
|
import { Stack, IStackTokens } from "office-ui-fabric-react/lib/Stack";
|
||||||
import { Link, MessageBar, MessageBarType } from "office-ui-fabric-react";
|
import { Link, MessageBar, MessageBarType } from "office-ui-fabric-react";
|
||||||
|
|
||||||
import * as InputUtils from "./InputUtils";
|
import * as InputUtils from "./InputUtils";
|
||||||
import "./SmartUiComponent.less";
|
import "./SmartUiComponent.less";
|
||||||
|
|
||||||
@@ -19,16 +21,45 @@ import "./SmartUiComponent.less";
|
|||||||
* - a descriptor of the UX.
|
* - a descriptor of the UX.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
export type InputTypeValue = "number" | "string" | "boolean" | "object";
|
export type InputTypeValue = "number" | "string" | "boolean" | "enum";
|
||||||
|
|
||||||
export enum UiType {
|
/* eslint-disable-next-line @typescript-eslint/no-explicit-any */
|
||||||
Spinner = "Spinner",
|
export type EnumItem = { label: string; key: string; value: any };
|
||||||
Slider = "Slider"
|
|
||||||
|
export type InputType = number | string | boolean | EnumItem;
|
||||||
|
|
||||||
|
interface BaseInput {
|
||||||
|
label: string;
|
||||||
|
dataFieldName: string;
|
||||||
|
type: InputTypeValue;
|
||||||
|
placeholder?: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
export type ChoiceItem = { label: string; key: string };
|
/**
|
||||||
|
* For now, this only supports integers
|
||||||
|
*/
|
||||||
|
export interface NumberInput extends BaseInput {
|
||||||
|
min?: number;
|
||||||
|
max?: number;
|
||||||
|
step: number;
|
||||||
|
defaultValue: number;
|
||||||
|
inputType: "spin" | "slider";
|
||||||
|
}
|
||||||
|
|
||||||
export type InputType = number | string | boolean | ChoiceItem;
|
export interface BooleanInput extends BaseInput {
|
||||||
|
trueLabel: string;
|
||||||
|
falseLabel: string;
|
||||||
|
defaultValue: boolean;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface StringInput extends BaseInput {
|
||||||
|
defaultValue?: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface EnumInput extends BaseInput {
|
||||||
|
choices: EnumItem[];
|
||||||
|
defaultKey: string;
|
||||||
|
}
|
||||||
|
|
||||||
export interface Info {
|
export interface Info {
|
||||||
message: string;
|
message: string;
|
||||||
@@ -38,62 +69,28 @@ export interface Info {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
interface BaseInput {
|
export type AnyInput = NumberInput | BooleanInput | StringInput | EnumInput;
|
||||||
label: string;
|
|
||||||
dataFieldName: string;
|
|
||||||
type: InputTypeValue;
|
|
||||||
placeholder?: string;
|
|
||||||
errorMessage?: string;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
export interface Node {
|
||||||
* For now, this only supports integers
|
|
||||||
*/
|
|
||||||
interface NumberInput extends BaseInput {
|
|
||||||
min: number;
|
|
||||||
max: number;
|
|
||||||
step: number;
|
|
||||||
defaultValue?: number;
|
|
||||||
uiType: UiType;
|
|
||||||
}
|
|
||||||
|
|
||||||
interface BooleanInput extends BaseInput {
|
|
||||||
trueLabel: string;
|
|
||||||
falseLabel: string;
|
|
||||||
defaultValue?: boolean;
|
|
||||||
}
|
|
||||||
|
|
||||||
interface StringInput extends BaseInput {
|
|
||||||
defaultValue?: string;
|
|
||||||
}
|
|
||||||
|
|
||||||
interface ChoiceInput extends BaseInput {
|
|
||||||
choices: ChoiceItem[];
|
|
||||||
defaultKey?: string;
|
|
||||||
}
|
|
||||||
|
|
||||||
type AnyInput = NumberInput | BooleanInput | StringInput | ChoiceInput;
|
|
||||||
|
|
||||||
interface Node {
|
|
||||||
id: string;
|
id: string;
|
||||||
info?: Info;
|
info?: Info;
|
||||||
input?: AnyInput;
|
input?: AnyInput;
|
||||||
children?: Node[];
|
children?: Node[];
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface SmartUiDescriptor {
|
export interface Descriptor {
|
||||||
root: Node;
|
root: Node;
|
||||||
}
|
}
|
||||||
|
|
||||||
/************************** Component implementation starts here ************************************* */
|
/************************** Component implementation starts here ************************************* */
|
||||||
|
|
||||||
export interface SmartUiComponentProps {
|
export interface SmartUiComponentProps {
|
||||||
descriptor: SmartUiDescriptor;
|
descriptor: Descriptor;
|
||||||
currentValues: Map<string, InputType>;
|
onChange: (newValues: Map<string, InputType>) => void;
|
||||||
onInputChange: (input: AnyInput, newValue: InputType) => void;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
interface SmartUiComponentState {
|
interface SmartUiComponentState {
|
||||||
|
currentValues: Map<string, InputType>;
|
||||||
errors: Map<string, string>;
|
errors: Map<string, string>;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -107,6 +104,7 @@ export class SmartUiComponent extends React.Component<SmartUiComponentProps, Sma
|
|||||||
constructor(props: SmartUiComponentProps) {
|
constructor(props: SmartUiComponentProps) {
|
||||||
super(props);
|
super(props);
|
||||||
this.state = {
|
this.state = {
|
||||||
|
currentValues: new Map(),
|
||||||
errors: new Map()
|
errors: new Map()
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
@@ -115,37 +113,42 @@ export class SmartUiComponent extends React.Component<SmartUiComponentProps, Sma
|
|||||||
return (
|
return (
|
||||||
<MessageBar>
|
<MessageBar>
|
||||||
{info.message}
|
{info.message}
|
||||||
{info.link && (
|
<Link href={info.link.href} target="_blank">
|
||||||
<Link href={info.link.href} target="_blank">
|
{info.link.text}
|
||||||
{info.link.text}
|
</Link>
|
||||||
</Link>
|
|
||||||
)}
|
|
||||||
</MessageBar>
|
</MessageBar>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
private renderTextInput(input: StringInput): JSX.Element {
|
private onInputChange = (newValue: string | number | boolean, dataFieldName: string) => {
|
||||||
const value = this.props.currentValues.get(input.dataFieldName) as string;
|
const { currentValues } = this.state;
|
||||||
|
currentValues.set(dataFieldName, newValue);
|
||||||
|
this.setState({ currentValues }, () => this.props.onChange(this.state.currentValues));
|
||||||
|
};
|
||||||
|
|
||||||
|
private renderStringInput(input: StringInput): JSX.Element {
|
||||||
return (
|
return (
|
||||||
<div className="stringInputContainer">
|
<div className="stringInputContainer">
|
||||||
<TextField
|
<div>
|
||||||
id={`${input.dataFieldName}-textBox-input`}
|
<TextField
|
||||||
label={input.label}
|
id={`${input.dataFieldName}-input`}
|
||||||
type="text"
|
label={input.label}
|
||||||
value={value}
|
type="text"
|
||||||
placeholder={input.placeholder}
|
value={input.defaultValue}
|
||||||
onChange={(_, newValue) => this.props.onInputChange(input, newValue)}
|
placeholder={input.placeholder}
|
||||||
styles={{
|
onChange={(_, newValue) => this.onInputChange(newValue, input.dataFieldName)}
|
||||||
subComponentStyles: {
|
styles={{
|
||||||
label: {
|
subComponentStyles: {
|
||||||
root: {
|
label: {
|
||||||
...SmartUiComponent.labelStyle,
|
root: {
|
||||||
fontWeight: 600
|
...SmartUiComponent.labelStyle,
|
||||||
|
fontWeight: 600
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}}
|
||||||
}}
|
/>
|
||||||
/>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@@ -156,11 +159,10 @@ export class SmartUiComponent extends React.Component<SmartUiComponentProps, Sma
|
|||||||
this.setState({ errors });
|
this.setState({ errors });
|
||||||
}
|
}
|
||||||
|
|
||||||
private onValidate = (input: AnyInput, value: string, min: number, max: number): string => {
|
private onValidate = (value: string, min: number, max: number, dataFieldName: string): string => {
|
||||||
const newValue = InputUtils.onValidateValueChange(value, min, max);
|
const newValue = InputUtils.onValidateValueChange(value, min, max);
|
||||||
const dataFieldName = input.dataFieldName;
|
|
||||||
if (newValue) {
|
if (newValue) {
|
||||||
this.props.onInputChange(input, newValue);
|
this.onInputChange(newValue, dataFieldName);
|
||||||
this.clearError(dataFieldName);
|
this.clearError(dataFieldName);
|
||||||
return newValue.toString();
|
return newValue.toString();
|
||||||
} else {
|
} else {
|
||||||
@@ -171,22 +173,20 @@ export class SmartUiComponent extends React.Component<SmartUiComponentProps, Sma
|
|||||||
return undefined;
|
return undefined;
|
||||||
};
|
};
|
||||||
|
|
||||||
private onIncrement = (input: AnyInput, value: string, step: number, max: number): string => {
|
private onIncrement = (value: string, step: number, max: number, dataFieldName: string): string => {
|
||||||
const newValue = InputUtils.onIncrementValue(value, step, max);
|
const newValue = InputUtils.onIncrementValue(value, step, max);
|
||||||
const dataFieldName = input.dataFieldName;
|
|
||||||
if (newValue) {
|
if (newValue) {
|
||||||
this.props.onInputChange(input, newValue);
|
this.onInputChange(newValue, dataFieldName);
|
||||||
this.clearError(dataFieldName);
|
this.clearError(dataFieldName);
|
||||||
return newValue.toString();
|
return newValue.toString();
|
||||||
}
|
}
|
||||||
return undefined;
|
return undefined;
|
||||||
};
|
};
|
||||||
|
|
||||||
private onDecrement = (input: AnyInput, value: string, step: number, min: number): string => {
|
private onDecrement = (value: string, step: number, min: number, dataFieldName: string): string => {
|
||||||
const newValue = InputUtils.onDecrementValue(value, step, min);
|
const newValue = InputUtils.onDecrementValue(value, step, min);
|
||||||
const dataFieldName = input.dataFieldName;
|
|
||||||
if (newValue) {
|
if (newValue) {
|
||||||
this.props.onInputChange(input, newValue);
|
this.onInputChange(newValue, dataFieldName);
|
||||||
this.clearError(dataFieldName);
|
this.clearError(dataFieldName);
|
||||||
return newValue.toString();
|
return newValue.toString();
|
||||||
}
|
}
|
||||||
@@ -194,26 +194,18 @@ export class SmartUiComponent extends React.Component<SmartUiComponentProps, Sma
|
|||||||
};
|
};
|
||||||
|
|
||||||
private renderNumberInput(input: NumberInput): JSX.Element {
|
private renderNumberInput(input: NumberInput): JSX.Element {
|
||||||
const { label, min, max, dataFieldName, step } = input;
|
const { label, min, max, defaultValue, dataFieldName, step } = input;
|
||||||
const props = {
|
const props = { label, min, max, ariaLabel: label, step };
|
||||||
label: label,
|
|
||||||
min: min,
|
|
||||||
max: max,
|
|
||||||
ariaLabel: label,
|
|
||||||
step: step
|
|
||||||
};
|
|
||||||
|
|
||||||
const value = this.props.currentValues.get(dataFieldName) as number;
|
if (input.inputType === "spin") {
|
||||||
if (input.uiType === UiType.Spinner) {
|
|
||||||
return (
|
return (
|
||||||
<>
|
<div>
|
||||||
<SpinButton
|
<SpinButton
|
||||||
{...props}
|
{...props}
|
||||||
id={`${input.dataFieldName}-spinner-input`}
|
defaultValue={defaultValue.toString()}
|
||||||
value={value?.toString()}
|
onValidate={newValue => this.onValidate(newValue, min, max, dataFieldName)}
|
||||||
onValidate={newValue => this.onValidate(input, newValue, props.min, props.max)}
|
onIncrement={newValue => this.onIncrement(newValue, step, max, dataFieldName)}
|
||||||
onIncrement={newValue => this.onIncrement(input, newValue, props.step, props.max)}
|
onDecrement={newValue => this.onDecrement(newValue, step, min, dataFieldName)}
|
||||||
onDecrement={newValue => this.onDecrement(input, newValue, props.step, props.min)}
|
|
||||||
labelPosition={Position.top}
|
labelPosition={Position.top}
|
||||||
styles={{
|
styles={{
|
||||||
label: {
|
label: {
|
||||||
@@ -225,35 +217,34 @@ export class SmartUiComponent extends React.Component<SmartUiComponentProps, Sma
|
|||||||
{this.state.errors.has(dataFieldName) && (
|
{this.state.errors.has(dataFieldName) && (
|
||||||
<MessageBar messageBarType={MessageBarType.error}>Error: {this.state.errors.get(dataFieldName)}</MessageBar>
|
<MessageBar messageBarType={MessageBarType.error}>Error: {this.state.errors.get(dataFieldName)}</MessageBar>
|
||||||
)}
|
)}
|
||||||
</>
|
|
||||||
);
|
|
||||||
} else if (input.uiType === UiType.Slider) {
|
|
||||||
return (
|
|
||||||
<div id={`${input.dataFieldName}-slider-input`}>
|
|
||||||
<Slider
|
|
||||||
{...props}
|
|
||||||
value={value}
|
|
||||||
onChange={newValue => this.props.onInputChange(input, newValue)}
|
|
||||||
styles={{
|
|
||||||
titleLabel: {
|
|
||||||
...SmartUiComponent.labelStyle,
|
|
||||||
fontWeight: 600
|
|
||||||
},
|
|
||||||
valueLabel: SmartUiComponent.labelStyle
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
} else if (input.inputType === "slider") {
|
||||||
|
return (
|
||||||
|
<Slider
|
||||||
|
// showValue={true}
|
||||||
|
// valueFormat={}
|
||||||
|
{...props}
|
||||||
|
defaultValue={defaultValue}
|
||||||
|
onChange={newValue => this.onInputChange(newValue, dataFieldName)}
|
||||||
|
styles={{
|
||||||
|
titleLabel: {
|
||||||
|
...SmartUiComponent.labelStyle,
|
||||||
|
fontWeight: 600
|
||||||
|
},
|
||||||
|
valueLabel: SmartUiComponent.labelStyle
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
);
|
||||||
} else {
|
} else {
|
||||||
return <>Unsupported number UI type {input.uiType}</>;
|
return <>Unsupported number input type {input.inputType}</>;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private renderBooleanInput(input: BooleanInput): JSX.Element {
|
private renderBooleanInput(input: BooleanInput): JSX.Element {
|
||||||
const value = this.props.currentValues.get(input.dataFieldName) as boolean;
|
const { dataFieldName } = input;
|
||||||
const selectedKey = value || input.defaultValue ? "true" : "false";
|
|
||||||
return (
|
return (
|
||||||
<div id={`${input.dataFieldName}-radioSwitch-input`}>
|
<div>
|
||||||
<div className="inputLabelContainer">
|
<div className="inputLabelContainer">
|
||||||
<Text variant="small" nowrap className="inputLabel">
|
<Text variant="small" nowrap className="inputLabel">
|
||||||
{input.label}
|
{input.label}
|
||||||
@@ -264,33 +255,41 @@ export class SmartUiComponent extends React.Component<SmartUiComponentProps, Sma
|
|||||||
{
|
{
|
||||||
label: input.falseLabel,
|
label: input.falseLabel,
|
||||||
key: "false",
|
key: "false",
|
||||||
onSelect: () => this.props.onInputChange(input, false)
|
onSelect: () => this.onInputChange(false, dataFieldName)
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
label: input.trueLabel,
|
label: input.trueLabel,
|
||||||
key: "true",
|
key: "true",
|
||||||
onSelect: () => this.props.onInputChange(input, true)
|
onSelect: () => this.onInputChange(true, dataFieldName)
|
||||||
}
|
}
|
||||||
]}
|
]}
|
||||||
selectedKey={selectedKey}
|
selectedKey={
|
||||||
|
(this.state.currentValues.has(dataFieldName)
|
||||||
|
? (this.state.currentValues.get(dataFieldName) as boolean)
|
||||||
|
: input.defaultValue)
|
||||||
|
? "true"
|
||||||
|
: "false"
|
||||||
|
}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
private renderChoiceInput(input: ChoiceInput): JSX.Element {
|
private renderEnumInput(input: EnumInput): JSX.Element {
|
||||||
const { label, defaultKey: defaultKey, dataFieldName, choices, placeholder } = input;
|
const { label, defaultKey, dataFieldName, choices, placeholder } = input;
|
||||||
const value = this.props.currentValues.get(dataFieldName) as string;
|
|
||||||
return (
|
return (
|
||||||
<Dropdown
|
<Dropdown
|
||||||
id={`${input.dataFieldName}-dropown-input`}
|
|
||||||
label={label}
|
label={label}
|
||||||
selectedKey={value ? value : defaultKey}
|
selectedKey={
|
||||||
onChange={(_, item: IDropdownOption) => this.props.onInputChange(input, item.key.toString())}
|
this.state.currentValues.has(dataFieldName)
|
||||||
|
? (this.state.currentValues.get(dataFieldName) as string)
|
||||||
|
: defaultKey
|
||||||
|
}
|
||||||
|
onChange={(_, item: IDropdownOption) => this.onInputChange(item.key.toString(), dataFieldName)}
|
||||||
placeholder={placeholder}
|
placeholder={placeholder}
|
||||||
options={choices.map(c => ({
|
options={choices.map(c => ({
|
||||||
key: c.key,
|
key: c.key,
|
||||||
text: c.label
|
text: c.value
|
||||||
}))}
|
}))}
|
||||||
styles={{
|
styles={{
|
||||||
label: {
|
label: {
|
||||||
@@ -303,48 +302,34 @@ export class SmartUiComponent extends React.Component<SmartUiComponentProps, Sma
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
private renderError(input: AnyInput): JSX.Element {
|
|
||||||
return <MessageBar messageBarType={MessageBarType.error}>Error: {input.errorMessage}</MessageBar>;
|
|
||||||
}
|
|
||||||
|
|
||||||
private renderInput(input: AnyInput): JSX.Element {
|
private renderInput(input: AnyInput): JSX.Element {
|
||||||
if (input.errorMessage) {
|
|
||||||
return this.renderError(input);
|
|
||||||
}
|
|
||||||
switch (input.type) {
|
switch (input.type) {
|
||||||
case "string":
|
case "string":
|
||||||
return this.renderTextInput(input as StringInput);
|
return this.renderStringInput(input as StringInput);
|
||||||
case "number":
|
case "number":
|
||||||
return this.renderNumberInput(input as NumberInput);
|
return this.renderNumberInput(input as NumberInput);
|
||||||
case "boolean":
|
case "boolean":
|
||||||
return this.renderBooleanInput(input as BooleanInput);
|
return this.renderBooleanInput(input as BooleanInput);
|
||||||
case "object":
|
case "enum":
|
||||||
return this.renderChoiceInput(input as ChoiceInput);
|
return this.renderEnumInput(input as EnumInput);
|
||||||
default:
|
default:
|
||||||
throw new Error(`Unknown input type: ${input.type}`);
|
throw new Error(`Unknown input type: ${input.type}`);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private renderNode(node: Node): JSX.Element {
|
private renderNode(node: Node): JSX.Element {
|
||||||
const containerStackTokens: IStackTokens = { childrenGap: 15 };
|
const containerStackTokens: IStackTokens = { childrenGap: 10 };
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Stack tokens={containerStackTokens} className="widgetRendererContainer">
|
<Stack tokens={containerStackTokens} className="widgetRendererContainer">
|
||||||
<Stack.Item>
|
{node.info && this.renderInfo(node.info)}
|
||||||
{node.info && this.renderInfo(node.info as Info)}
|
{node.input && this.renderInput(node.input)}
|
||||||
{node.input && this.renderInput(node.input)}
|
|
||||||
</Stack.Item>
|
|
||||||
{node.children && node.children.map(child => <div key={child.id}>{this.renderNode(child)}</div>)}
|
{node.children && node.children.map(child => <div key={child.id}>{this.renderNode(child)}</div>)}
|
||||||
</Stack>
|
</Stack>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
render(): JSX.Element {
|
render(): JSX.Element {
|
||||||
const containerStackTokens: IStackTokens = { childrenGap: 20 };
|
return <>{this.renderNode(this.props.descriptor.root)}</>;
|
||||||
return (
|
|
||||||
<Stack tokens={containerStackTokens} styles={{ root: { width: 400, padding: 10 } }}>
|
|
||||||
{this.renderNode(this.props.descriptor.root)}
|
|
||||||
</Stack>
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,40 +1,24 @@
|
|||||||
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
||||||
|
|
||||||
exports[`SmartUiComponent should render 1`] = `
|
exports[`SmartUiComponent should render 1`] = `
|
||||||
<Stack
|
<Fragment>
|
||||||
styles={
|
|
||||||
Object {
|
|
||||||
"root": Object {
|
|
||||||
"padding": 10,
|
|
||||||
"width": 400,
|
|
||||||
},
|
|
||||||
}
|
|
||||||
}
|
|
||||||
tokens={
|
|
||||||
Object {
|
|
||||||
"childrenGap": 20,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
>
|
|
||||||
<Stack
|
<Stack
|
||||||
className="widgetRendererContainer"
|
className="widgetRendererContainer"
|
||||||
tokens={
|
tokens={
|
||||||
Object {
|
Object {
|
||||||
"childrenGap": 15,
|
"childrenGap": 10,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
>
|
>
|
||||||
<StackItem>
|
<StyledMessageBarBase>
|
||||||
<StyledMessageBarBase>
|
Start at $24/mo per database
|
||||||
Start at $24/mo per database
|
<StyledLinkBase
|
||||||
<StyledLinkBase
|
href="https://aka.ms/azure-cosmos-db-pricing"
|
||||||
href="https://aka.ms/azure-cosmos-db-pricing"
|
target="_blank"
|
||||||
target="_blank"
|
>
|
||||||
>
|
More Details
|
||||||
More Details
|
</StyledLinkBase>
|
||||||
</StyledLinkBase>
|
</StyledMessageBarBase>
|
||||||
</StyledMessageBarBase>
|
|
||||||
</StackItem>
|
|
||||||
<div
|
<div
|
||||||
key="throughput"
|
key="throughput"
|
||||||
>
|
>
|
||||||
@@ -42,11 +26,11 @@ exports[`SmartUiComponent should render 1`] = `
|
|||||||
className="widgetRendererContainer"
|
className="widgetRendererContainer"
|
||||||
tokens={
|
tokens={
|
||||||
Object {
|
Object {
|
||||||
"childrenGap": 15,
|
"childrenGap": 10,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
>
|
>
|
||||||
<StackItem>
|
<div>
|
||||||
<CustomizedSpinButton
|
<CustomizedSpinButton
|
||||||
ariaLabel="Throughput (input)"
|
ariaLabel="Throughput (input)"
|
||||||
decrementButtonIcon={
|
decrementButtonIcon={
|
||||||
@@ -54,8 +38,8 @@ exports[`SmartUiComponent should render 1`] = `
|
|||||||
"iconName": "ChevronDownSmall",
|
"iconName": "ChevronDownSmall",
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
defaultValue="400"
|
||||||
disabled={false}
|
disabled={false}
|
||||||
id="throughput-spinner-input"
|
|
||||||
incrementButtonIcon={
|
incrementButtonIcon={
|
||||||
Object {
|
Object {
|
||||||
"iconName": "ChevronUpSmall",
|
"iconName": "ChevronUpSmall",
|
||||||
@@ -80,7 +64,7 @@ exports[`SmartUiComponent should render 1`] = `
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
/>
|
/>
|
||||||
</StackItem>
|
</div>
|
||||||
</Stack>
|
</Stack>
|
||||||
</div>
|
</div>
|
||||||
<div
|
<div
|
||||||
@@ -90,60 +74,34 @@ exports[`SmartUiComponent should render 1`] = `
|
|||||||
className="widgetRendererContainer"
|
className="widgetRendererContainer"
|
||||||
tokens={
|
tokens={
|
||||||
Object {
|
Object {
|
||||||
"childrenGap": 15,
|
"childrenGap": 10,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
>
|
>
|
||||||
<StackItem>
|
<StyledSliderBase
|
||||||
<div
|
ariaLabel="Throughput (Slider)"
|
||||||
id="throughput2-slider-input"
|
defaultValue={400}
|
||||||
>
|
label="Throughput (Slider)"
|
||||||
<StyledSliderBase
|
max={500}
|
||||||
ariaLabel="Throughput (Slider)"
|
min={400}
|
||||||
label="Throughput (Slider)"
|
onChange={[Function]}
|
||||||
max={500}
|
step={10}
|
||||||
min={400}
|
styles={
|
||||||
onChange={[Function]}
|
Object {
|
||||||
step={10}
|
"titleLabel": Object {
|
||||||
styles={
|
"color": "#393939",
|
||||||
Object {
|
"fontFamily": "wf_segoe-ui_normal, 'Segoe UI', 'Segoe WP', Tahoma, Arial, sans-serif",
|
||||||
"titleLabel": Object {
|
"fontSize": 12,
|
||||||
"color": "#393939",
|
"fontWeight": 600,
|
||||||
"fontFamily": "wf_segoe-ui_normal, 'Segoe UI', 'Segoe WP', Tahoma, Arial, sans-serif",
|
},
|
||||||
"fontSize": 12,
|
"valueLabel": Object {
|
||||||
"fontWeight": 600,
|
"color": "#393939",
|
||||||
},
|
"fontFamily": "wf_segoe-ui_normal, 'Segoe UI', 'Segoe WP', Tahoma, Arial, sans-serif",
|
||||||
"valueLabel": Object {
|
"fontSize": 12,
|
||||||
"color": "#393939",
|
},
|
||||||
"fontFamily": "wf_segoe-ui_normal, 'Segoe UI', 'Segoe WP', Tahoma, Arial, sans-serif",
|
}
|
||||||
"fontSize": 12,
|
|
||||||
},
|
|
||||||
}
|
|
||||||
}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
</StackItem>
|
|
||||||
</Stack>
|
|
||||||
</div>
|
|
||||||
<div
|
|
||||||
key="throughput3"
|
|
||||||
>
|
|
||||||
<Stack
|
|
||||||
className="widgetRendererContainer"
|
|
||||||
tokens={
|
|
||||||
Object {
|
|
||||||
"childrenGap": 15,
|
|
||||||
}
|
}
|
||||||
}
|
/>
|
||||||
>
|
|
||||||
<StackItem>
|
|
||||||
<StyledMessageBarBase
|
|
||||||
messageBarType={1}
|
|
||||||
>
|
|
||||||
Error:
|
|
||||||
label, truelabel and falselabel are required for boolean input 'throughput3'
|
|
||||||
</StyledMessageBarBase>
|
|
||||||
</StackItem>
|
|
||||||
</Stack>
|
</Stack>
|
||||||
</div>
|
</div>
|
||||||
<div
|
<div
|
||||||
@@ -153,16 +111,16 @@ exports[`SmartUiComponent should render 1`] = `
|
|||||||
className="widgetRendererContainer"
|
className="widgetRendererContainer"
|
||||||
tokens={
|
tokens={
|
||||||
Object {
|
Object {
|
||||||
"childrenGap": 15,
|
"childrenGap": 10,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
>
|
>
|
||||||
<StackItem>
|
<div
|
||||||
<div
|
className="stringInputContainer"
|
||||||
className="stringInputContainer"
|
>
|
||||||
>
|
<div>
|
||||||
<StyledTextFieldBase
|
<StyledTextFieldBase
|
||||||
id="containerId-textBox-input"
|
id="containerId-input"
|
||||||
label="Container id"
|
label="Container id"
|
||||||
onChange={[Function]}
|
onChange={[Function]}
|
||||||
styles={
|
styles={
|
||||||
@@ -182,7 +140,7 @@ exports[`SmartUiComponent should render 1`] = `
|
|||||||
type="text"
|
type="text"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
</StackItem>
|
</div>
|
||||||
</Stack>
|
</Stack>
|
||||||
</div>
|
</div>
|
||||||
<div
|
<div
|
||||||
@@ -192,44 +150,40 @@ exports[`SmartUiComponent should render 1`] = `
|
|||||||
className="widgetRendererContainer"
|
className="widgetRendererContainer"
|
||||||
tokens={
|
tokens={
|
||||||
Object {
|
Object {
|
||||||
"childrenGap": 15,
|
"childrenGap": 10,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
>
|
>
|
||||||
<StackItem>
|
<div>
|
||||||
<div
|
<div
|
||||||
id="analyticalStore-radioSwitch-input"
|
className="inputLabelContainer"
|
||||||
>
|
>
|
||||||
<div
|
<Text
|
||||||
className="inputLabelContainer"
|
className="inputLabel"
|
||||||
|
nowrap={true}
|
||||||
|
variant="small"
|
||||||
>
|
>
|
||||||
<Text
|
Analytical Store
|
||||||
className="inputLabel"
|
</Text>
|
||||||
nowrap={true}
|
|
||||||
variant="small"
|
|
||||||
>
|
|
||||||
Analytical Store
|
|
||||||
</Text>
|
|
||||||
</div>
|
|
||||||
<RadioSwitchComponent
|
|
||||||
choices={
|
|
||||||
Array [
|
|
||||||
Object {
|
|
||||||
"key": "false",
|
|
||||||
"label": "Disabled",
|
|
||||||
"onSelect": [Function],
|
|
||||||
},
|
|
||||||
Object {
|
|
||||||
"key": "true",
|
|
||||||
"label": "Enabled",
|
|
||||||
"onSelect": [Function],
|
|
||||||
},
|
|
||||||
]
|
|
||||||
}
|
|
||||||
selectedKey="true"
|
|
||||||
/>
|
|
||||||
</div>
|
</div>
|
||||||
</StackItem>
|
<RadioSwitchComponent
|
||||||
|
choices={
|
||||||
|
Array [
|
||||||
|
Object {
|
||||||
|
"key": "false",
|
||||||
|
"label": "Disabled",
|
||||||
|
"onSelect": [Function],
|
||||||
|
},
|
||||||
|
Object {
|
||||||
|
"key": "true",
|
||||||
|
"label": "Enabled",
|
||||||
|
"onSelect": [Function],
|
||||||
|
},
|
||||||
|
]
|
||||||
|
}
|
||||||
|
selectedKey="true"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
</Stack>
|
</Stack>
|
||||||
</div>
|
</div>
|
||||||
<div
|
<div
|
||||||
@@ -239,51 +193,48 @@ exports[`SmartUiComponent should render 1`] = `
|
|||||||
className="widgetRendererContainer"
|
className="widgetRendererContainer"
|
||||||
tokens={
|
tokens={
|
||||||
Object {
|
Object {
|
||||||
"childrenGap": 15,
|
"childrenGap": 10,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
>
|
>
|
||||||
<StackItem>
|
<StyledWithResponsiveMode
|
||||||
<StyledWithResponsiveMode
|
label="Database"
|
||||||
id="database-dropown-input"
|
onChange={[Function]}
|
||||||
label="Database"
|
options={
|
||||||
onChange={[Function]}
|
Array [
|
||||||
options={
|
|
||||||
Array [
|
|
||||||
Object {
|
|
||||||
"key": "db1",
|
|
||||||
"text": "Database 1",
|
|
||||||
},
|
|
||||||
Object {
|
|
||||||
"key": "db2",
|
|
||||||
"text": "Database 2",
|
|
||||||
},
|
|
||||||
Object {
|
|
||||||
"key": "db3",
|
|
||||||
"text": "Database 3",
|
|
||||||
},
|
|
||||||
]
|
|
||||||
}
|
|
||||||
selectedKey="db2"
|
|
||||||
styles={
|
|
||||||
Object {
|
Object {
|
||||||
"dropdown": Object {
|
"key": "db1",
|
||||||
"color": "#393939",
|
"text": "database1",
|
||||||
"fontFamily": "wf_segoe-ui_normal, 'Segoe UI', 'Segoe WP', Tahoma, Arial, sans-serif",
|
},
|
||||||
"fontSize": 12,
|
Object {
|
||||||
},
|
"key": "db2",
|
||||||
"label": Object {
|
"text": "database2",
|
||||||
"color": "#393939",
|
},
|
||||||
"fontFamily": "wf_segoe-ui_normal, 'Segoe UI', 'Segoe WP', Tahoma, Arial, sans-serif",
|
Object {
|
||||||
"fontSize": 12,
|
"key": "db3",
|
||||||
"fontWeight": 600,
|
"text": "database3",
|
||||||
},
|
},
|
||||||
}
|
]
|
||||||
|
}
|
||||||
|
selectedKey="db2"
|
||||||
|
styles={
|
||||||
|
Object {
|
||||||
|
"dropdown": Object {
|
||||||
|
"color": "#393939",
|
||||||
|
"fontFamily": "wf_segoe-ui_normal, 'Segoe UI', 'Segoe WP', Tahoma, Arial, sans-serif",
|
||||||
|
"fontSize": 12,
|
||||||
|
},
|
||||||
|
"label": Object {
|
||||||
|
"color": "#393939",
|
||||||
|
"fontFamily": "wf_segoe-ui_normal, 'Segoe UI', 'Segoe WP', Tahoma, Arial, sans-serif",
|
||||||
|
"fontSize": 12,
|
||||||
|
"fontWeight": 600,
|
||||||
|
},
|
||||||
}
|
}
|
||||||
/>
|
}
|
||||||
</StackItem>
|
/>
|
||||||
</Stack>
|
</Stack>
|
||||||
</div>
|
</div>
|
||||||
</Stack>
|
</Stack>
|
||||||
</Stack>
|
</Fragment>
|
||||||
`;
|
`;
|
||||||
|
|||||||
@@ -5,9 +5,6 @@ import ThroughputInputComponentAutoscaleV3 from "./ThroughputInputComponentAutos
|
|||||||
import { KeyCodes } from "../../../Common/Constants";
|
import { KeyCodes } from "../../../Common/Constants";
|
||||||
import { WaitsForTemplateViewModel } from "../../WaitsForTemplateViewModel";
|
import { WaitsForTemplateViewModel } from "../../WaitsForTemplateViewModel";
|
||||||
|
|
||||||
import { userContext } from "../../../UserContext";
|
|
||||||
import * as TelemetryProcessor from "../../../Shared/Telemetry/TelemetryProcessor";
|
|
||||||
import { Action, ActionModifiers } from "../../../Shared/Telemetry/TelemetryConstants";
|
|
||||||
/**
|
/**
|
||||||
* Throughput Input:
|
* Throughput Input:
|
||||||
*
|
*
|
||||||
@@ -132,8 +129,6 @@ export interface ThroughputInputParams {
|
|||||||
showAutoPilot?: ko.Observable<boolean>;
|
showAutoPilot?: ko.Observable<boolean>;
|
||||||
overrideWithAutoPilotSettings: ko.Observable<boolean>;
|
overrideWithAutoPilotSettings: ko.Observable<boolean>;
|
||||||
overrideWithProvisionedThroughputSettings: ko.Observable<boolean>;
|
overrideWithProvisionedThroughputSettings: ko.Observable<boolean>;
|
||||||
freeTierExceedThroughputTooltip?: ko.Observable<string>;
|
|
||||||
freeTierExceedThroughputWarning?: ko.Observable<string>;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export class ThroughputInputViewModel extends WaitsForTemplateViewModel {
|
export class ThroughputInputViewModel extends WaitsForTemplateViewModel {
|
||||||
@@ -170,10 +165,6 @@ export class ThroughputInputViewModel extends WaitsForTemplateViewModel {
|
|||||||
public overrideWithProvisionedThroughputSettings: ko.Observable<boolean>;
|
public overrideWithProvisionedThroughputSettings: ko.Observable<boolean>;
|
||||||
public isManualThroughputInputFieldRequired: ko.Computed<boolean>;
|
public isManualThroughputInputFieldRequired: ko.Computed<boolean>;
|
||||||
public isAutoscaleThroughputInputFieldRequired: ko.Computed<boolean>;
|
public isAutoscaleThroughputInputFieldRequired: ko.Computed<boolean>;
|
||||||
public freeTierExceedThroughputTooltip: ko.Observable<string>;
|
|
||||||
public freeTierExceedThroughputWarning: ko.Observable<string>;
|
|
||||||
public showFreeTierExceedThroughputTooltip: ko.Computed<boolean>;
|
|
||||||
public showFreeTierExceedThroughputWarning: ko.Computed<boolean>;
|
|
||||||
|
|
||||||
public constructor(options: ThroughputInputParams) {
|
public constructor(options: ThroughputInputParams) {
|
||||||
super();
|
super();
|
||||||
@@ -204,16 +195,6 @@ export class ThroughputInputViewModel extends WaitsForTemplateViewModel {
|
|||||||
this.label = options.label || ko.observable<string>();
|
this.label = options.label || ko.observable<string>();
|
||||||
this.showAutoPilot = options.showAutoPilot !== undefined ? options.showAutoPilot : ko.observable<boolean>(true);
|
this.showAutoPilot = options.showAutoPilot !== undefined ? options.showAutoPilot : ko.observable<boolean>(true);
|
||||||
this.isAutoPilotSelected = options.isAutoPilotSelected || ko.observable<boolean>(false);
|
this.isAutoPilotSelected = options.isAutoPilotSelected || ko.observable<boolean>(false);
|
||||||
this.isAutoPilotSelected.subscribe(value => {
|
|
||||||
TelemetryProcessor.trace(Action.ToggleAutoscaleSetting, ActionModifiers.Mark, {
|
|
||||||
changedSelectedValueTo: value ? ActionModifiers.ToggleAutoscaleOn : ActionModifiers.ToggleAutoscaleOff,
|
|
||||||
databaseAccountName: userContext.databaseAccount?.name,
|
|
||||||
subscriptionId: userContext.subscriptionId,
|
|
||||||
apiKind: userContext.defaultExperience,
|
|
||||||
dataExplorerArea: "Scale Tab V1"
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
this.throughputAutoPilotRadioId = options.throughputAutoPilotRadioId;
|
this.throughputAutoPilotRadioId = options.throughputAutoPilotRadioId;
|
||||||
this.throughputProvisionedRadioId = options.throughputProvisionedRadioId;
|
this.throughputProvisionedRadioId = options.throughputProvisionedRadioId;
|
||||||
this.throughputModeRadioName = options.throughputModeRadioName;
|
this.throughputModeRadioName = options.throughputModeRadioName;
|
||||||
@@ -238,16 +219,6 @@ export class ThroughputInputViewModel extends WaitsForTemplateViewModel {
|
|||||||
this.isAutoscaleThroughputInputFieldRequired = ko.pureComputed(
|
this.isAutoscaleThroughputInputFieldRequired = ko.pureComputed(
|
||||||
() => this.isEnabled() && this.isAutoPilotSelected()
|
() => this.isEnabled() && this.isAutoPilotSelected()
|
||||||
);
|
);
|
||||||
|
|
||||||
this.freeTierExceedThroughputTooltip = options.freeTierExceedThroughputTooltip || ko.observable<string>();
|
|
||||||
this.freeTierExceedThroughputWarning = options.freeTierExceedThroughputWarning || ko.observable<string>();
|
|
||||||
this.showFreeTierExceedThroughputTooltip = ko.pureComputed<boolean>(
|
|
||||||
() => !!this.freeTierExceedThroughputTooltip() && this.value() > 400
|
|
||||||
);
|
|
||||||
|
|
||||||
this.showFreeTierExceedThroughputWarning = ko.pureComputed<boolean>(
|
|
||||||
() => !!this.freeTierExceedThroughputWarning() && this.value() > 400
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public decreaseThroughput() {
|
public decreaseThroughput() {
|
||||||
|
|||||||
@@ -132,14 +132,6 @@
|
|||||||
<a target="_blank" href="https://cosmos.azure.com/capacitycalculator/">capacity calculator</a></span
|
<a target="_blank" href="https://cosmos.azure.com/capacitycalculator/">capacity calculator</a></span
|
||||||
>
|
>
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
<div class="inputTooltip">
|
|
||||||
<span
|
|
||||||
data-bind="text: freeTierExceedThroughputTooltip, visible: showFreeTierExceedThroughputTooltip"
|
|
||||||
class="inputTooltipText"
|
|
||||||
></span>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div data-bind="setTemplateReady: true">
|
<div data-bind="setTemplateReady: true">
|
||||||
<input
|
<input
|
||||||
data-bind="
|
data-bind="
|
||||||
@@ -162,11 +154,6 @@
|
|||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="freeTierInlineWarning" data-bind="visible: showFreeTierExceedThroughputWarning">
|
|
||||||
<span class="freeTierWarningIcon"><img src="/warning.svg" alt="Warning"/></span>
|
|
||||||
<span class="freeTierWarningMessage" data-bind="text: freeTierExceedThroughputWarning"></span>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<p data-bind="visible: costsVisible">
|
<p data-bind="visible: costsVisible">
|
||||||
<span data-bind="html: requestUnitsUsageCost"></span>
|
<span data-bind="html: requestUnitsUsageCost"></span>
|
||||||
</p>
|
</p>
|
||||||
|
|||||||
@@ -88,9 +88,6 @@ import { stringToBlob } from "../Utils/BlobUtils";
|
|||||||
import { IChoiceGroupProps } from "office-ui-fabric-react";
|
import { IChoiceGroupProps } from "office-ui-fabric-react";
|
||||||
import { getErrorMessage, handleError, getErrorStack } from "../Common/ErrorHandlingUtils";
|
import { getErrorMessage, handleError, getErrorStack } from "../Common/ErrorHandlingUtils";
|
||||||
import { SubscriptionType } from "../Contracts/SubscriptionType";
|
import { SubscriptionType } from "../Contracts/SubscriptionType";
|
||||||
import { SelfServeLoadingComponentAdapter } from "../SelfServe/SelfServeLoadingComponentAdapter";
|
|
||||||
import { SelfServeType } from "../SelfServe/SelfServeUtils";
|
|
||||||
import { SelfServeComponentAdapter } from "../SelfServe/SelfServeComponentAdapter";
|
|
||||||
|
|
||||||
BindingHandlersRegisterer.registerBindingHandlers();
|
BindingHandlersRegisterer.registerBindingHandlers();
|
||||||
// Hold a reference to ComponentRegisterer to prevent transpiler to ignore import
|
// Hold a reference to ComponentRegisterer to prevent transpiler to ignore import
|
||||||
@@ -134,7 +131,6 @@ export default class Explorer {
|
|||||||
public isEnableMongoCapabilityPresent: ko.Computed<boolean>;
|
public isEnableMongoCapabilityPresent: ko.Computed<boolean>;
|
||||||
public isServerlessEnabled: ko.Computed<boolean>;
|
public isServerlessEnabled: ko.Computed<boolean>;
|
||||||
public isAccountReady: ko.Observable<boolean>;
|
public isAccountReady: ko.Observable<boolean>;
|
||||||
public selfServeType: ko.Observable<SelfServeType>;
|
|
||||||
public canSaveQueries: ko.Computed<boolean>;
|
public canSaveQueries: ko.Computed<boolean>;
|
||||||
public features: ko.Observable<any>;
|
public features: ko.Observable<any>;
|
||||||
public serverId: ko.Observable<string>;
|
public serverId: ko.Observable<string>;
|
||||||
@@ -160,7 +156,6 @@ export default class Explorer {
|
|||||||
public selectedNode: ko.Observable<ViewModels.TreeNode>;
|
public selectedNode: ko.Observable<ViewModels.TreeNode>;
|
||||||
public isRefreshingExplorer: ko.Observable<boolean>;
|
public isRefreshingExplorer: ko.Observable<boolean>;
|
||||||
private resourceTree: ResourceTreeAdapter;
|
private resourceTree: ResourceTreeAdapter;
|
||||||
private selfServeComponentAdapter: SelfServeComponentAdapter;
|
|
||||||
|
|
||||||
// Resource Token
|
// Resource Token
|
||||||
public resourceTokenDatabaseId: ko.Observable<string>;
|
public resourceTokenDatabaseId: ko.Observable<string>;
|
||||||
@@ -212,9 +207,7 @@ export default class Explorer {
|
|||||||
public isCopyNotebookPaneEnabled: ko.Observable<boolean>;
|
public isCopyNotebookPaneEnabled: ko.Observable<boolean>;
|
||||||
public isHostedDataExplorerEnabled: ko.Computed<boolean>;
|
public isHostedDataExplorerEnabled: ko.Computed<boolean>;
|
||||||
public isRightPanelV2Enabled: ko.Computed<boolean>;
|
public isRightPanelV2Enabled: ko.Computed<boolean>;
|
||||||
public isMongoIndexingEnabled: ko.Observable<boolean>;
|
|
||||||
public canExceedMaximumValue: ko.Computed<boolean>;
|
public canExceedMaximumValue: ko.Computed<boolean>;
|
||||||
public isAutoscaleDefaultEnabled: ko.Observable<boolean>;
|
|
||||||
|
|
||||||
public shouldShowShareDialogContents: ko.Observable<boolean>;
|
public shouldShowShareDialogContents: ko.Observable<boolean>;
|
||||||
public shareAccessData: ko.Observable<AdHocAccessData>;
|
public shareAccessData: ko.Observable<AdHocAccessData>;
|
||||||
@@ -264,7 +257,6 @@ export default class Explorer {
|
|||||||
private _dialogProps: ko.Observable<DialogProps>;
|
private _dialogProps: ko.Observable<DialogProps>;
|
||||||
private addSynapseLinkDialog: DialogComponentAdapter;
|
private addSynapseLinkDialog: DialogComponentAdapter;
|
||||||
private _addSynapseLinkDialogProps: ko.Observable<DialogProps>;
|
private _addSynapseLinkDialogProps: ko.Observable<DialogProps>;
|
||||||
private selfServeLoadingComponentAdapter: SelfServeLoadingComponentAdapter;
|
|
||||||
|
|
||||||
private static readonly MaxNbDatabasesToAutoExpand = 5;
|
private static readonly MaxNbDatabasesToAutoExpand = 5;
|
||||||
|
|
||||||
@@ -300,7 +292,6 @@ export default class Explorer {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
this.isAccountReady = ko.observable<boolean>(false);
|
this.isAccountReady = ko.observable<boolean>(false);
|
||||||
this.selfServeType = ko.observable<SelfServeType>(undefined);
|
|
||||||
this._isInitializingNotebooks = false;
|
this._isInitializingNotebooks = false;
|
||||||
this._isInitializingSparkConnectionInfo = false;
|
this._isInitializingSparkConnectionInfo = false;
|
||||||
this.arcadiaToken = ko.observable<string>();
|
this.arcadiaToken = ko.observable<string>();
|
||||||
@@ -411,7 +402,6 @@ export default class Explorer {
|
|||||||
this.isFeatureEnabled(Constants.Features.enableLinkInjection)
|
this.isFeatureEnabled(Constants.Features.enableLinkInjection)
|
||||||
);
|
);
|
||||||
this.isGitHubPaneEnabled = ko.observable<boolean>(false);
|
this.isGitHubPaneEnabled = ko.observable<boolean>(false);
|
||||||
this.isMongoIndexingEnabled = ko.observable<boolean>(false);
|
|
||||||
this.isPublishNotebookPaneEnabled = ko.observable<boolean>(false);
|
this.isPublishNotebookPaneEnabled = ko.observable<boolean>(false);
|
||||||
this.isCopyNotebookPaneEnabled = ko.observable<boolean>(false);
|
this.isCopyNotebookPaneEnabled = ko.observable<boolean>(false);
|
||||||
|
|
||||||
@@ -422,8 +412,6 @@ export default class Explorer {
|
|||||||
this.isSchemaEnabled = ko.computed<boolean>(() => this.isFeatureEnabled(Constants.Features.enableSchema));
|
this.isSchemaEnabled = ko.computed<boolean>(() => this.isFeatureEnabled(Constants.Features.enableSchema));
|
||||||
this.isNotificationConsoleExpanded = ko.observable<boolean>(false);
|
this.isNotificationConsoleExpanded = ko.observable<boolean>(false);
|
||||||
|
|
||||||
this.isAutoscaleDefaultEnabled = ko.observable<boolean>(false);
|
|
||||||
|
|
||||||
this.databases = ko.observableArray<ViewModels.Database>();
|
this.databases = ko.observableArray<ViewModels.Database>();
|
||||||
this.canSaveQueries = ko.computed<boolean>(() => {
|
this.canSaveQueries = ko.computed<boolean>(() => {
|
||||||
const savedQueriesDatabase: ViewModels.Database = _.find(
|
const savedQueriesDatabase: ViewModels.Database = _.find(
|
||||||
@@ -705,7 +693,6 @@ export default class Explorer {
|
|||||||
});
|
});
|
||||||
|
|
||||||
this.uploadItemsPaneAdapter = new UploadItemsPaneAdapter(this);
|
this.uploadItemsPaneAdapter = new UploadItemsPaneAdapter(this);
|
||||||
this.selfServeComponentAdapter = new SelfServeComponentAdapter(this);
|
|
||||||
|
|
||||||
this.loadQueryPane = new LoadQueryPane({
|
this.loadQueryPane = new LoadQueryPane({
|
||||||
id: "loadquerypane",
|
id: "loadquerypane",
|
||||||
@@ -881,7 +868,6 @@ export default class Explorer {
|
|||||||
});
|
});
|
||||||
|
|
||||||
this.commandBarComponentAdapter = new CommandBarComponentAdapter(this);
|
this.commandBarComponentAdapter = new CommandBarComponentAdapter(this);
|
||||||
this.selfServeLoadingComponentAdapter = new SelfServeLoadingComponentAdapter();
|
|
||||||
this.notificationConsoleComponentAdapter = new NotificationConsoleComponentAdapter(this);
|
this.notificationConsoleComponentAdapter = new NotificationConsoleComponentAdapter(this);
|
||||||
|
|
||||||
this._initSettings();
|
this._initSettings();
|
||||||
@@ -1853,20 +1839,6 @@ export default class Explorer {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
public setSelfServeType(inputs: ViewModels.DataExplorerInputsFrame): void {
|
|
||||||
const selfServeFeature = inputs.features[Constants.Features.selfServeType];
|
|
||||||
if (selfServeFeature) {
|
|
||||||
// self serve type received from query string
|
|
||||||
const selfServeType = SelfServeType[selfServeFeature?.toLowerCase() as keyof typeof SelfServeType];
|
|
||||||
this.selfServeType(selfServeType ? selfServeType : SelfServeType.invalid);
|
|
||||||
} else if (inputs.selfServeType) {
|
|
||||||
// self serve type received from portal
|
|
||||||
this.selfServeType(inputs.selfServeType);
|
|
||||||
} else {
|
|
||||||
this.selfServeType(SelfServeType.none);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public initDataExplorerWithFrameInputs(inputs: ViewModels.DataExplorerInputsFrame): void {
|
public initDataExplorerWithFrameInputs(inputs: ViewModels.DataExplorerInputsFrame): void {
|
||||||
if (inputs != null) {
|
if (inputs != null) {
|
||||||
// In development mode, save the iframe message from the portal in session storage.
|
// In development mode, save the iframe message from the portal in session storage.
|
||||||
@@ -1890,7 +1862,6 @@ export default class Explorer {
|
|||||||
this.isTryCosmosDBSubscription(inputs.isTryCosmosDBSubscription);
|
this.isTryCosmosDBSubscription(inputs.isTryCosmosDBSubscription);
|
||||||
this.isAuthWithResourceToken(inputs.isAuthWithresourceToken);
|
this.isAuthWithResourceToken(inputs.isAuthWithresourceToken);
|
||||||
this.setFeatureFlagsFromFlights(inputs.flights);
|
this.setFeatureFlagsFromFlights(inputs.flights);
|
||||||
this.setSelfServeType(inputs);
|
|
||||||
this._importExplorerConfigComplete = true;
|
this._importExplorerConfigComplete = true;
|
||||||
|
|
||||||
updateConfigContext({
|
updateConfigContext({
|
||||||
@@ -1925,12 +1896,6 @@ export default class Explorer {
|
|||||||
if (!flights) {
|
if (!flights) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (flights.indexOf(Constants.Flights.AutoscaleTest) !== -1) {
|
|
||||||
this.isAutoscaleDefaultEnabled(true);
|
|
||||||
}
|
|
||||||
if (flights.indexOf(Constants.Flights.MongoIndexing) !== -1) {
|
|
||||||
this.isMongoIndexingEnabled(true);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public findSelectedCollection(): ViewModels.Collection {
|
public findSelectedCollection(): ViewModels.Collection {
|
||||||
@@ -3049,25 +3014,4 @@ export default class Explorer {
|
|||||||
})
|
})
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
public isFirstResourceCreated(): boolean {
|
|
||||||
const databases: ViewModels.Database[] = this.databases();
|
|
||||||
|
|
||||||
if (!databases || databases.length === 0) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
return databases.some(database => {
|
|
||||||
// user has created at least one collection
|
|
||||||
if (database.collections()?.length > 0) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
// user has created a database with shared throughput
|
|
||||||
if (database.offer()) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
// use has created an empty database without shared throughput
|
|
||||||
return false;
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -11,9 +11,7 @@ export class ArraysByKeyCache<T> {
|
|||||||
|
|
||||||
public constructor(maxNbElements: number) {
|
public constructor(maxNbElements: number) {
|
||||||
this.maxNbElements = maxNbElements;
|
this.maxNbElements = maxNbElements;
|
||||||
this.keyQueue = [];
|
this.clear();
|
||||||
this.cache = {};
|
|
||||||
this.totalElements = 0;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public clear(): void {
|
public clear(): void {
|
||||||
@@ -60,7 +58,7 @@ export class ArraysByKeyCache<T> {
|
|||||||
* @param startIndex
|
* @param startIndex
|
||||||
* @param pageSize
|
* @param pageSize
|
||||||
*/
|
*/
|
||||||
public retrieve(key: string, startIndex: number, pageSize: number): T[] | null {
|
public retrieve(key: string, startIndex: number, pageSize: number): T[] {
|
||||||
if (!this.cache.hasOwnProperty(key)) {
|
if (!this.cache.hasOwnProperty(key)) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
@@ -79,10 +77,8 @@ export class ArraysByKeyCache<T> {
|
|||||||
private reduceCacheSize(): void {
|
private reduceCacheSize(): void {
|
||||||
// remove an key and its array
|
// remove an key and its array
|
||||||
const oldKey = this.keyQueue.shift();
|
const oldKey = this.keyQueue.shift();
|
||||||
if (oldKey) {
|
this.totalElements -= this.cache[oldKey].length;
|
||||||
this.totalElements -= this.cache[oldKey].length;
|
delete this.cache[oldKey];
|
||||||
delete this.cache[oldKey];
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -413,13 +413,13 @@ export class GraphData<V extends GremlinVertex, E extends GremlinEdge> {
|
|||||||
* @param node
|
* @param node
|
||||||
* @param prop
|
* @param prop
|
||||||
*/
|
*/
|
||||||
public static getNodePropValue(node: D3Node, prop: string): undefined | string | number | boolean {
|
public static getNodePropValue(node: D3Node, prop: string): string | number | boolean {
|
||||||
if (node.hasOwnProperty(prop)) {
|
if (node.hasOwnProperty(prop)) {
|
||||||
return (node as any)[prop];
|
return (node as any)[prop];
|
||||||
}
|
}
|
||||||
|
|
||||||
// This is DocDB specific
|
// This is DocDB specific
|
||||||
if (node.properties && node.properties.hasOwnProperty(prop)) {
|
if (node.hasOwnProperty("properties") && node.properties.hasOwnProperty(prop)) {
|
||||||
return node.properties[prop][0]["value"];
|
return node.properties[prop][0]["value"];
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -496,7 +496,7 @@ export class GraphData<V extends GremlinVertex, E extends GremlinEdge> {
|
|||||||
* Get list of children ids of a given vertex
|
* Get list of children ids of a given vertex
|
||||||
* @param vertex
|
* @param vertex
|
||||||
*/
|
*/
|
||||||
public static getChildrenId(vertex: GremlinVertex): string[] {
|
private static getChildrenId(vertex: GremlinVertex): string[] {
|
||||||
const ids = <any>{}; // HashSet
|
const ids = <any>{}; // HashSet
|
||||||
if (vertex.hasOwnProperty("outE")) {
|
if (vertex.hasOwnProperty("outE")) {
|
||||||
let outE = vertex.outE;
|
let outE = vertex.outE;
|
||||||
|
|||||||
@@ -8,7 +8,7 @@ describe("CommandBarComponentButtonFactory tests", () => {
|
|||||||
let mockExplorer: Explorer;
|
let mockExplorer: Explorer;
|
||||||
|
|
||||||
describe("Enable Azure Synapse Link Button", () => {
|
describe("Enable Azure Synapse Link Button", () => {
|
||||||
const enableAzureSynapseLinkBtnLabel = "Enable Azure Synapse Link";
|
const enableAzureSynapseLinkBtnLabel = "Enable Azure Synapse Link (Preview)";
|
||||||
|
|
||||||
beforeAll(() => {
|
beforeAll(() => {
|
||||||
mockExplorer = {} as Explorer;
|
mockExplorer = {} as Explorer;
|
||||||
|
|||||||
@@ -269,7 +269,7 @@ export class CommandBarComponentButtonFactory {
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
const label = "Enable Azure Synapse Link";
|
const label = "Enable Azure Synapse Link (Preview)";
|
||||||
return {
|
return {
|
||||||
iconSrc: SynapseIcon,
|
iconSrc: SynapseIcon,
|
||||||
iconAlt: label,
|
iconAlt: label,
|
||||||
|
|||||||
@@ -1,3 +1,4 @@
|
|||||||
|
import "./MeControlComponent.less";
|
||||||
import * as React from "react";
|
import * as React from "react";
|
||||||
import { DefaultButton, BaseButton, IButtonProps } from "office-ui-fabric-react/lib/Button";
|
import { DefaultButton, BaseButton, IButtonProps } from "office-ui-fabric-react/lib/Button";
|
||||||
import { DirectionalHint, IContextualMenuProps } from "office-ui-fabric-react/lib/ContextualMenu";
|
import { DirectionalHint, IContextualMenuProps } from "office-ui-fabric-react/lib/ContextualMenu";
|
||||||
|
|||||||
@@ -11,8 +11,8 @@ import ErrorBlackIcon from "../../../../images/error_black.svg";
|
|||||||
import infoBubbleIcon from "../../../../images/info-bubble-9x9.svg";
|
import infoBubbleIcon from "../../../../images/info-bubble-9x9.svg";
|
||||||
import InfoIcon from "../../../../images/info_color.svg";
|
import InfoIcon from "../../../../images/info_color.svg";
|
||||||
import ErrorRedIcon from "../../../../images/error_red.svg";
|
import ErrorRedIcon from "../../../../images/error_red.svg";
|
||||||
import ClearIcon from "../../../../images/Clear.svg";
|
|
||||||
import LoaderIcon from "../../../../images/circular_loader_black_16x16.gif";
|
import LoaderIcon from "../../../../images/circular_loader_black_16x16.gif";
|
||||||
|
import ClearIcon from "../../../../images/Clear.svg";
|
||||||
import ChevronUpIcon from "../../../../images/QueryBuilder/CollapseChevronUp_16x.png";
|
import ChevronUpIcon from "../../../../images/QueryBuilder/CollapseChevronUp_16x.png";
|
||||||
import ChevronDownIcon from "../../../../images/QueryBuilder/CollapseChevronDown_16x.png";
|
import ChevronDownIcon from "../../../../images/QueryBuilder/CollapseChevronDown_16x.png";
|
||||||
|
|
||||||
@@ -59,9 +59,9 @@ export class NotificationConsoleComponent extends React.Component<
|
|||||||
{ key: "Info", text: "Info" },
|
{ key: "Info", text: "Info" },
|
||||||
{ key: "Error", text: "Error" }
|
{ key: "Error", text: "Error" }
|
||||||
];
|
];
|
||||||
private headerTimeoutId?: number;
|
private headerTimeoutId: number;
|
||||||
private prevHeaderStatus: string | null;
|
private prevHeaderStatus: string;
|
||||||
private consoleHeaderElement?: HTMLElement;
|
private consoleHeaderElement: HTMLElement;
|
||||||
|
|
||||||
constructor(props: NotificationConsoleComponentProps) {
|
constructor(props: NotificationConsoleComponentProps) {
|
||||||
super(props);
|
super(props);
|
||||||
@@ -99,10 +99,6 @@ export class NotificationConsoleComponent extends React.Component<
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public setElememntRef = (element: HTMLElement) => {
|
|
||||||
this.consoleHeaderElement = element;
|
|
||||||
};
|
|
||||||
|
|
||||||
public render(): JSX.Element {
|
public render(): JSX.Element {
|
||||||
const numInProgress = this.props.consoleData.filter((data: ConsoleData) => data.type === ConsoleDataType.InProgress)
|
const numInProgress = this.props.consoleData.filter((data: ConsoleData) => data.type === ConsoleDataType.InProgress)
|
||||||
.length;
|
.length;
|
||||||
@@ -114,7 +110,7 @@ export class NotificationConsoleComponent extends React.Component<
|
|||||||
<div className="notificationConsoleContainer">
|
<div className="notificationConsoleContainer">
|
||||||
<div
|
<div
|
||||||
className="notificationConsoleHeader"
|
className="notificationConsoleHeader"
|
||||||
ref={this.setElememntRef}
|
ref={(element: HTMLElement) => (this.consoleHeaderElement = element)}
|
||||||
onClick={(event: React.MouseEvent<HTMLDivElement>) => this.expandCollapseConsole()}
|
onClick={(event: React.MouseEvent<HTMLDivElement>) => this.expandCollapseConsole()}
|
||||||
onKeyDown={(event: React.KeyboardEvent<HTMLDivElement>) => this.onExpandCollapseKeyPress(event)}
|
onKeyDown={(event: React.KeyboardEvent<HTMLDivElement>) => this.onExpandCollapseKeyPress(event)}
|
||||||
tabIndex={0}
|
tabIndex={0}
|
||||||
@@ -224,12 +220,12 @@ export class NotificationConsoleComponent extends React.Component<
|
|||||||
));
|
));
|
||||||
}
|
}
|
||||||
|
|
||||||
private onFilterSelected = (event: React.ChangeEvent<HTMLSelectElement>, option: IDropdownOption): void => {
|
private onFilterSelected(event: React.ChangeEvent<HTMLSelectElement>, option: IDropdownOption): void {
|
||||||
this.setState({ selectedFilter: String(option.key) });
|
this.setState({ selectedFilter: String(option.key) });
|
||||||
};
|
}
|
||||||
|
|
||||||
private getFilteredConsoleData(): ConsoleData[] {
|
private getFilteredConsoleData(): ConsoleData[] {
|
||||||
let filterType: ConsoleDataType | null = null;
|
let filterType: ConsoleDataType = null;
|
||||||
|
|
||||||
switch (this.state.selectedFilter) {
|
switch (this.state.selectedFilter) {
|
||||||
case "All":
|
case "All":
|
||||||
@@ -276,7 +272,7 @@ export class NotificationConsoleComponent extends React.Component<
|
|||||||
|
|
||||||
private onConsoleWasExpanded = (): void => {
|
private onConsoleWasExpanded = (): void => {
|
||||||
this.props.onConsoleExpandedChange(this.state.isExpanded);
|
this.props.onConsoleExpandedChange(this.state.isExpanded);
|
||||||
if (this.state.isExpanded && this.consoleHeaderElement) {
|
if (this.state.isExpanded) {
|
||||||
this.consoleHeaderElement.focus();
|
this.consoleHeaderElement.focus();
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -1,17 +1,18 @@
|
|||||||
import { Observable, of } from "rxjs";
|
import { Observable, of } from "rxjs";
|
||||||
import { AjaxRequest, AjaxResponse } from "rxjs/ajax";
|
import { AjaxResponse } from "rxjs/ajax";
|
||||||
|
import { ServerConfig } from "rx-jupyter";
|
||||||
|
|
||||||
let fakeAjaxResponse: AjaxResponse = {
|
let fakeAjaxResponse: AjaxResponse = {
|
||||||
originalEvent: <Event>(<unknown>undefined),
|
originalEvent: undefined,
|
||||||
xhr: new XMLHttpRequest(),
|
xhr: new XMLHttpRequest(),
|
||||||
request: <AjaxRequest>(<unknown>null),
|
request: null,
|
||||||
status: 200,
|
status: 200,
|
||||||
response: {},
|
response: {},
|
||||||
responseText: "",
|
responseText: null,
|
||||||
responseType: "json"
|
responseType: "json"
|
||||||
};
|
};
|
||||||
export const sessions = {
|
export const sessions = {
|
||||||
create: (serverConfig: unknown, body: object): Observable<AjaxResponse> => of(fakeAjaxResponse),
|
create: (serverConfig: ServerConfig, body: object): Observable<AjaxResponse> => of(fakeAjaxResponse),
|
||||||
__setResponse: (response: AjaxResponse) => {
|
__setResponse: (response: AjaxResponse) => {
|
||||||
fakeAjaxResponse = response;
|
fakeAjaxResponse = response;
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -808,7 +808,7 @@ const closeUnsupportedMimetypesEpic = (
|
|||||||
const filepath = action.payload.filepath;
|
const filepath = action.payload.filepath;
|
||||||
// Close tab and show error message
|
// Close tab and show error message
|
||||||
explorer.tabsManager.closeTabsByComparator(
|
explorer.tabsManager.closeTabsByComparator(
|
||||||
(tab: any) => (tab as any).notebookPath && FileSystemUtil.isPathEqual((tab as any).notebookPath(), filepath)
|
tab => (tab as any).notebookPath && FileSystemUtil.isPathEqual((tab as any).notebookPath(), filepath)
|
||||||
);
|
);
|
||||||
const msg = `${filepath} cannot be rendered. Please download the file, in order to view it outside of Data Explorer.`;
|
const msg = `${filepath} cannot be rendered. Please download the file, in order to view it outside of Data Explorer.`;
|
||||||
explorer.showOkModalDialog("File cannot be rendered", msg);
|
explorer.showOkModalDialog("File cannot be rendered", msg);
|
||||||
@@ -836,7 +836,7 @@ const closeContentFailedToFetchEpic = (
|
|||||||
const filepath = action.payload.filepath;
|
const filepath = action.payload.filepath;
|
||||||
// Close tab and show error message
|
// Close tab and show error message
|
||||||
explorer.tabsManager.closeTabsByComparator(
|
explorer.tabsManager.closeTabsByComparator(
|
||||||
(tab: any) => (tab as any).notebookPath && FileSystemUtil.isPathEqual((tab as any).notebookPath(), filepath)
|
tab => (tab as any).notebookPath && FileSystemUtil.isPathEqual((tab as any).notebookPath(), filepath)
|
||||||
);
|
);
|
||||||
const msg = `Failed to load file: ${filepath}.`;
|
const msg = `Failed to load file: ${filepath}.`;
|
||||||
explorer.showOkModalDialog("Failure to load", msg);
|
explorer.showOkModalDialog("Failure to load", msg);
|
||||||
|
|||||||
@@ -11,7 +11,7 @@ import { stringifyNotebook } from "@nteract/commutable";
|
|||||||
export class NotebookContentClient {
|
export class NotebookContentClient {
|
||||||
constructor(
|
constructor(
|
||||||
private notebookServerInfo: ko.Observable<DataModels.NotebookWorkspaceConnectionInfo>,
|
private notebookServerInfo: ko.Observable<DataModels.NotebookWorkspaceConnectionInfo>,
|
||||||
public notebookBasePath: ko.Observable<string>,
|
private notebookBasePath: ko.Observable<string>,
|
||||||
private contentProvider: IContentProvider
|
private contentProvider: IContentProvider
|
||||||
) {}
|
) {}
|
||||||
|
|
||||||
@@ -117,11 +117,8 @@ export class NotebookContentClient {
|
|||||||
|
|
||||||
private async checkIfFilepathExists(filepath: string): Promise<boolean> {
|
private async checkIfFilepathExists(filepath: string): Promise<boolean> {
|
||||||
const parentDirPath = NotebookUtil.getParentPath(filepath);
|
const parentDirPath = NotebookUtil.getParentPath(filepath);
|
||||||
if (parentDirPath) {
|
const items = await this.fetchNotebookFiles(parentDirPath);
|
||||||
const items = await this.fetchNotebookFiles(parentDirPath);
|
return items.some(value => FileSystemUtil.isPathEqual(value.path, filepath));
|
||||||
return items.some(value => FileSystemUtil.isPathEqual(value.path, filepath));
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -192,7 +189,7 @@ export class NotebookContentClient {
|
|||||||
const dir = xhr.response;
|
const dir = xhr.response;
|
||||||
const item = NotebookUtil.createNotebookContentItem(dir.name, dir.path, dir.type);
|
const item = NotebookUtil.createNotebookContentItem(dir.name, dir.path, dir.type);
|
||||||
item.parent = parent;
|
item.parent = parent;
|
||||||
parent.children?.push(item);
|
parent.children.push(item);
|
||||||
return item;
|
return item;
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@@ -228,7 +225,7 @@ export class NotebookContentClient {
|
|||||||
* Convert rx-jupyter type to our type
|
* Convert rx-jupyter type to our type
|
||||||
* @param type
|
* @param type
|
||||||
*/
|
*/
|
||||||
public static getType(type: FileType): NotebookContentItemType {
|
private static getType(type: FileType): NotebookContentItemType {
|
||||||
switch (type) {
|
switch (type) {
|
||||||
case "directory":
|
case "directory":
|
||||||
return NotebookContentItemType.Directory;
|
return NotebookContentItemType.Directory;
|
||||||
|
|||||||
@@ -152,8 +152,7 @@
|
|||||||
maxAutoPilotThroughputSet: sharedAutoPilotThroughput,
|
maxAutoPilotThroughputSet: sharedAutoPilotThroughput,
|
||||||
autoPilotUsageCost: autoPilotUsageCost,
|
autoPilotUsageCost: autoPilotUsageCost,
|
||||||
canExceedMaximumValue: canExceedMaximumValue,
|
canExceedMaximumValue: canExceedMaximumValue,
|
||||||
showAutoPilot: !isFreeTierAccount(),
|
showAutoPilot: !isFreeTierAccount()
|
||||||
freeTierExceedThroughputTooltip: freeTierExceedThroughputTooltip
|
|
||||||
}">
|
}">
|
||||||
</throughput-input-autopilot-v3>
|
</throughput-input-autopilot-v3>
|
||||||
</div>
|
</div>
|
||||||
@@ -257,7 +256,7 @@
|
|||||||
range of values and is likely to have evenly distributed access patterns.</span>
|
range of values and is likely to have evenly distributed access patterns.</span>
|
||||||
</span>
|
</span>
|
||||||
</p>
|
</p>
|
||||||
<input type="text" id="addCollection-partitionKeyValue" data-test="addCollection-partitionKeyValue" aria-required="true" size="40"
|
<input type="text" id="partitionKeyValue" data-test="addCollection-partitionKeyValue" aria-required="true" size="40"
|
||||||
class="textfontclr collid" data-bind="textInput: partitionKey,
|
class="textfontclr collid" data-bind="textInput: partitionKey,
|
||||||
attr: {
|
attr: {
|
||||||
placeholder: partitionKeyPlaceholder,
|
placeholder: partitionKeyPlaceholder,
|
||||||
@@ -334,8 +333,7 @@
|
|||||||
maxAutoPilotThroughputSet: autoPilotThroughput,
|
maxAutoPilotThroughputSet: autoPilotThroughput,
|
||||||
autoPilotUsageCost: autoPilotUsageCost,
|
autoPilotUsageCost: autoPilotUsageCost,
|
||||||
canExceedMaximumValue: canExceedMaximumValue,
|
canExceedMaximumValue: canExceedMaximumValue,
|
||||||
showAutoPilot: !isFixedStorageSelected(),
|
showAutoPilot: !isFixedStorageSelected()
|
||||||
freeTierExceedThroughputTooltip: freeTierExceedThroughputTooltip
|
|
||||||
}">
|
}">
|
||||||
</throughput-input-autopilot-v3>
|
</throughput-input-autopilot-v3>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -74,7 +74,7 @@ describe("Add Collection Pane", () => {
|
|||||||
explorer.databaseAccount(mockFreeTierDatabaseAccount);
|
explorer.databaseAccount(mockFreeTierDatabaseAccount);
|
||||||
const addCollectionPane = explorer.addCollectionPane as AddCollectionPane;
|
const addCollectionPane = explorer.addCollectionPane as AddCollectionPane;
|
||||||
expect(addCollectionPane.isFreeTierAccount()).toBe(true);
|
expect(addCollectionPane.isFreeTierAccount()).toBe(true);
|
||||||
expect(addCollectionPane.upsellMessage()).toContain("With free tier");
|
expect(addCollectionPane.upsellMessage()).toContain("With free tier discount");
|
||||||
expect(addCollectionPane.upsellAnchorUrl()).toBe(Constants.Urls.freeTierInformation);
|
expect(addCollectionPane.upsellAnchorUrl()).toBe(Constants.Urls.freeTierInformation);
|
||||||
expect(addCollectionPane.upsellAnchorText()).toBe("Learn more");
|
expect(addCollectionPane.upsellAnchorText()).toBe("Learn more");
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -89,7 +89,6 @@ export default class AddCollectionPane extends ContextualPaneBase {
|
|||||||
public isSynapseLinkUpdating: ko.Computed<boolean>;
|
public isSynapseLinkUpdating: ko.Computed<boolean>;
|
||||||
public canExceedMaximumValue: ko.PureComputed<boolean>;
|
public canExceedMaximumValue: ko.PureComputed<boolean>;
|
||||||
public ruToolTipText: ko.Computed<string>;
|
public ruToolTipText: ko.Computed<string>;
|
||||||
public freeTierExceedThroughputTooltip: ko.Computed<string>;
|
|
||||||
public canConfigureThroughput: ko.PureComputed<boolean>;
|
public canConfigureThroughput: ko.PureComputed<boolean>;
|
||||||
public showUpsellMessage: ko.PureComputed<boolean>;
|
public showUpsellMessage: ko.PureComputed<boolean>;
|
||||||
public shouldCreateMongoWildcardIndex: ko.Observable<boolean>;
|
public shouldCreateMongoWildcardIndex: ko.Observable<boolean>;
|
||||||
@@ -100,6 +99,7 @@ export default class AddCollectionPane extends ContextualPaneBase {
|
|||||||
super(options);
|
super(options);
|
||||||
this.ruToolTipText = ko.pureComputed(() => PricingUtils.getRuToolTipText());
|
this.ruToolTipText = ko.pureComputed(() => PricingUtils.getRuToolTipText());
|
||||||
this.canConfigureThroughput = ko.pureComputed(() => !this.container.isServerlessEnabled());
|
this.canConfigureThroughput = ko.pureComputed(() => !this.container.isServerlessEnabled());
|
||||||
|
this.showUpsellMessage = ko.pureComputed(() => !this.container.isServerlessEnabled());
|
||||||
this.formWarnings = ko.observable<string>();
|
this.formWarnings = ko.observable<string>();
|
||||||
this.collectionId = ko.observable<string>();
|
this.collectionId = ko.observable<string>();
|
||||||
this.databaseId = ko.observable<string>();
|
this.databaseId = ko.observable<string>();
|
||||||
@@ -481,20 +481,8 @@ export default class AddCollectionPane extends ContextualPaneBase {
|
|||||||
this.resetData();
|
this.resetData();
|
||||||
});
|
});
|
||||||
|
|
||||||
this.freeTierExceedThroughputTooltip = ko.pureComputed<string>(() =>
|
|
||||||
this.isFreeTierAccount() && !this.container.isFirstResourceCreated()
|
|
||||||
? "The first 400 RU/s in this account are free. Billing will apply to any throughput beyond 400 RU/s."
|
|
||||||
: ""
|
|
||||||
);
|
|
||||||
|
|
||||||
this.upsellMessage = ko.pureComputed<string>(() => {
|
this.upsellMessage = ko.pureComputed<string>(() => {
|
||||||
return PricingUtils.getUpsellMessage(
|
return PricingUtils.getUpsellMessage(this.container.serverId(), this.isFreeTierAccount());
|
||||||
this.container.serverId(),
|
|
||||||
this.isFreeTierAccount(),
|
|
||||||
this.container.isFirstResourceCreated(),
|
|
||||||
this.container.defaultExperience(),
|
|
||||||
true
|
|
||||||
);
|
|
||||||
});
|
});
|
||||||
|
|
||||||
this.upsellMessageAriaLabel = ko.pureComputed<string>(() => {
|
this.upsellMessageAriaLabel = ko.pureComputed<string>(() => {
|
||||||
@@ -546,23 +534,6 @@ export default class AddCollectionPane extends ContextualPaneBase {
|
|||||||
return isFreeTierAccount;
|
return isFreeTierAccount;
|
||||||
});
|
});
|
||||||
|
|
||||||
this.showUpsellMessage = ko.pureComputed(() => {
|
|
||||||
if (this.container.isServerlessEnabled()) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (
|
|
||||||
this.isFreeTierAccount() &&
|
|
||||||
!this.databaseCreateNew() &&
|
|
||||||
this.databaseHasSharedOffer() &&
|
|
||||||
!this.collectionWithThroughputInShared()
|
|
||||||
) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
|
||||||
});
|
|
||||||
|
|
||||||
this.showIndexingOptionsForSharedThroughput = ko.computed<boolean>(() => {
|
this.showIndexingOptionsForSharedThroughput = ko.computed<boolean>(() => {
|
||||||
const newDatabaseWithSharedOffer = this.databaseCreateNew() && this.databaseCreateNewShared();
|
const newDatabaseWithSharedOffer = this.databaseCreateNew() && this.databaseCreateNewShared();
|
||||||
const existingDatabaseWithSharedOffer = !this.databaseCreateNew() && this.databaseHasSharedOffer();
|
const existingDatabaseWithSharedOffer = !this.databaseCreateNew() && this.databaseHasSharedOffer();
|
||||||
@@ -654,7 +625,7 @@ export default class AddCollectionPane extends ContextualPaneBase {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
this.shouldCreateMongoWildcardIndex = ko.observable(this.container.isMongoIndexingEnabled());
|
this.shouldCreateMongoWildcardIndex = ko.observable(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
public getSharedThroughputDefault(): boolean {
|
public getSharedThroughputDefault(): boolean {
|
||||||
@@ -679,7 +650,6 @@ export default class AddCollectionPane extends ContextualPaneBase {
|
|||||||
// TODO: Figure out if a database level partition split is about to happen once shared throughput read is available
|
// TODO: Figure out if a database level partition split is about to happen once shared throughput read is available
|
||||||
this.formWarnings("");
|
this.formWarnings("");
|
||||||
this.databaseCreateNewShared(this.getSharedThroughputDefault());
|
this.databaseCreateNewShared(this.getSharedThroughputDefault());
|
||||||
this.shouldCreateMongoWildcardIndex(this.container.isMongoIndexingEnabled());
|
|
||||||
if (this.isPreferredApiTable() && !databaseId) {
|
if (this.isPreferredApiTable() && !databaseId) {
|
||||||
databaseId = SharedConstants.CollectionCreation.TablesAPIDefaultDatabase;
|
databaseId = SharedConstants.CollectionCreation.TablesAPIDefaultDatabase;
|
||||||
}
|
}
|
||||||
@@ -929,13 +899,11 @@ export default class AddCollectionPane extends ContextualPaneBase {
|
|||||||
this.databaseId("");
|
this.databaseId("");
|
||||||
this.partitionKey("");
|
this.partitionKey("");
|
||||||
this.throughputSpendAck(false);
|
this.throughputSpendAck(false);
|
||||||
this.isAutoPilotSelected(this.container.isAutoscaleDefaultEnabled());
|
this.isAutoPilotSelected(false);
|
||||||
this.isSharedAutoPilotSelected(this.container.isAutoscaleDefaultEnabled());
|
this.isSharedAutoPilotSelected(false);
|
||||||
this.autoPilotThroughput(AutoPilotUtils.minAutoPilotThroughput);
|
this.autoPilotThroughput(AutoPilotUtils.minAutoPilotThroughput);
|
||||||
this.sharedAutoPilotThroughput(AutoPilotUtils.minAutoPilotThroughput);
|
this.sharedAutoPilotThroughput(AutoPilotUtils.minAutoPilotThroughput);
|
||||||
|
|
||||||
this.shouldCreateMongoWildcardIndex = ko.observable(this.container.isMongoIndexingEnabled());
|
|
||||||
|
|
||||||
this.uniqueKeys([]);
|
this.uniqueKeys([]);
|
||||||
this.useIndexingForSharedThroughput(true);
|
this.useIndexingForSharedThroughput(true);
|
||||||
|
|
||||||
|
|||||||
@@ -114,8 +114,7 @@
|
|||||||
maxAutoPilotThroughputSet: maxAutoPilotThroughputSet,
|
maxAutoPilotThroughputSet: maxAutoPilotThroughputSet,
|
||||||
autoPilotUsageCost: autoPilotUsageCost,
|
autoPilotUsageCost: autoPilotUsageCost,
|
||||||
canExceedMaximumValue: canExceedMaximumValue,
|
canExceedMaximumValue: canExceedMaximumValue,
|
||||||
showAutoPilot: !isFreeTierAccount(),
|
showAutoPilot: !isFreeTierAccount()
|
||||||
freeTierExceedThroughputTooltip: freeTierExceedThroughputTooltip
|
|
||||||
}">
|
}">
|
||||||
</throughput-input-autopilot-v3>
|
</throughput-input-autopilot-v3>
|
||||||
<p data-bind="visible: canRequestSupport">
|
<p data-bind="visible: canRequestSupport">
|
||||||
|
|||||||
@@ -77,7 +77,7 @@ describe("Add Database Pane", () => {
|
|||||||
explorer.databaseAccount(mockFreeTierDatabaseAccount);
|
explorer.databaseAccount(mockFreeTierDatabaseAccount);
|
||||||
const addDatabasePane = explorer.addDatabasePane as AddDatabasePane;
|
const addDatabasePane = explorer.addDatabasePane as AddDatabasePane;
|
||||||
expect(addDatabasePane.isFreeTierAccount()).toBe(true);
|
expect(addDatabasePane.isFreeTierAccount()).toBe(true);
|
||||||
expect(addDatabasePane.upsellMessage()).toContain("With free tier");
|
expect(addDatabasePane.upsellMessage()).toContain("With free tier discount");
|
||||||
expect(addDatabasePane.upsellAnchorUrl()).toBe(Constants.Urls.freeTierInformation);
|
expect(addDatabasePane.upsellAnchorUrl()).toBe(Constants.Urls.freeTierInformation);
|
||||||
expect(addDatabasePane.upsellAnchorText()).toBe("Learn more");
|
expect(addDatabasePane.upsellAnchorText()).toBe("Learn more");
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -44,7 +44,6 @@ export default class AddDatabasePane extends ContextualPaneBase {
|
|||||||
public autoPilotUsageCost: ko.Computed<string>;
|
public autoPilotUsageCost: ko.Computed<string>;
|
||||||
public canExceedMaximumValue: ko.PureComputed<boolean>;
|
public canExceedMaximumValue: ko.PureComputed<boolean>;
|
||||||
public ruToolTipText: ko.Computed<string>;
|
public ruToolTipText: ko.Computed<string>;
|
||||||
public freeTierExceedThroughputTooltip: ko.Computed<string>;
|
|
||||||
public isFreeTierAccount: ko.Computed<boolean>;
|
public isFreeTierAccount: ko.Computed<boolean>;
|
||||||
public canConfigureThroughput: ko.PureComputed<boolean>;
|
public canConfigureThroughput: ko.PureComputed<boolean>;
|
||||||
public showUpsellMessage: ko.PureComputed<boolean>;
|
public showUpsellMessage: ko.PureComputed<boolean>;
|
||||||
@@ -55,6 +54,7 @@ export default class AddDatabasePane extends ContextualPaneBase {
|
|||||||
this.databaseId = ko.observable<string>();
|
this.databaseId = ko.observable<string>();
|
||||||
this.ruToolTipText = ko.pureComputed(() => PricingUtils.getRuToolTipText());
|
this.ruToolTipText = ko.pureComputed(() => PricingUtils.getRuToolTipText());
|
||||||
this.canConfigureThroughput = ko.pureComputed(() => !this.container.isServerlessEnabled());
|
this.canConfigureThroughput = ko.pureComputed(() => !this.container.isServerlessEnabled());
|
||||||
|
this.showUpsellMessage = ko.pureComputed(() => !this.container.isServerlessEnabled());
|
||||||
|
|
||||||
this.canExceedMaximumValue = ko.pureComputed(() => this.container.canExceedMaximumValue());
|
this.canExceedMaximumValue = ko.pureComputed(() => this.container.canExceedMaximumValue());
|
||||||
|
|
||||||
@@ -182,18 +182,6 @@ export default class AddDatabasePane extends ContextualPaneBase {
|
|||||||
return isFreeTierAccount;
|
return isFreeTierAccount;
|
||||||
});
|
});
|
||||||
|
|
||||||
this.showUpsellMessage = ko.pureComputed(() => {
|
|
||||||
if (this.container.isServerlessEnabled()) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (this.isFreeTierAccount()) {
|
|
||||||
return this.databaseCreateNewShared();
|
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
|
||||||
});
|
|
||||||
|
|
||||||
this.maxThroughputRUText = ko.pureComputed(() => {
|
this.maxThroughputRUText = ko.pureComputed(() => {
|
||||||
return this.maxThroughputRU().toLocaleString();
|
return this.maxThroughputRU().toLocaleString();
|
||||||
});
|
});
|
||||||
@@ -231,20 +219,8 @@ export default class AddDatabasePane extends ContextualPaneBase {
|
|||||||
this.resetData();
|
this.resetData();
|
||||||
});
|
});
|
||||||
|
|
||||||
this.freeTierExceedThroughputTooltip = ko.pureComputed<string>(() =>
|
|
||||||
this.isFreeTierAccount() && !this.container.isFirstResourceCreated()
|
|
||||||
? "The first 400 RU/s in this account are free. Billing will apply to any throughput beyond 400 RU/s."
|
|
||||||
: ""
|
|
||||||
);
|
|
||||||
|
|
||||||
this.upsellMessage = ko.pureComputed<string>(() => {
|
this.upsellMessage = ko.pureComputed<string>(() => {
|
||||||
return PricingUtils.getUpsellMessage(
|
return PricingUtils.getUpsellMessage(this.container.serverId(), this.isFreeTierAccount());
|
||||||
this.container.serverId(),
|
|
||||||
this.isFreeTierAccount(),
|
|
||||||
this.container.isFirstResourceCreated(),
|
|
||||||
this.container.defaultExperience(),
|
|
||||||
false
|
|
||||||
);
|
|
||||||
});
|
});
|
||||||
|
|
||||||
this.upsellMessageAriaLabel = ko.pureComputed<string>(() => {
|
this.upsellMessageAriaLabel = ko.pureComputed<string>(() => {
|
||||||
@@ -337,7 +313,7 @@ export default class AddDatabasePane extends ContextualPaneBase {
|
|||||||
public resetData() {
|
public resetData() {
|
||||||
this.databaseId("");
|
this.databaseId("");
|
||||||
this.databaseCreateNewShared(this.getSharedThroughputDefault());
|
this.databaseCreateNewShared(this.getSharedThroughputDefault());
|
||||||
this.isAutoPilotSelected(this.container.isAutoscaleDefaultEnabled());
|
this.isAutoPilotSelected(false);
|
||||||
this.maxAutoPilotThroughputSet(AutoPilotUtils.minAutoPilotThroughput);
|
this.maxAutoPilotThroughputSet(AutoPilotUtils.minAutoPilotThroughput);
|
||||||
this._updateThroughputLimitByDatabase();
|
this._updateThroughputLimitByDatabase();
|
||||||
this.throughputSpendAck(false);
|
this.throughputSpendAck(false);
|
||||||
|
|||||||
@@ -451,8 +451,8 @@ export default class CassandraAddCollectionPane extends ContextualPaneBase {
|
|||||||
public resetData() {
|
public resetData() {
|
||||||
super.resetData();
|
super.resetData();
|
||||||
const throughputDefaults = this.container.collectionCreationDefaults.throughput;
|
const throughputDefaults = this.container.collectionCreationDefaults.throughput;
|
||||||
this.isAutoPilotSelected(this.container.isAutoscaleDefaultEnabled());
|
this.isAutoPilotSelected(false);
|
||||||
this.isSharedAutoPilotSelected(this.container.isAutoscaleDefaultEnabled());
|
this.isSharedAutoPilotSelected(false);
|
||||||
this.selectedAutoPilotThroughput(AutoPilotUtils.minAutoPilotThroughput);
|
this.selectedAutoPilotThroughput(AutoPilotUtils.minAutoPilotThroughput);
|
||||||
this.sharedAutoPilotThroughput(AutoPilotUtils.minAutoPilotThroughput);
|
this.sharedAutoPilotThroughput(AutoPilotUtils.minAutoPilotThroughput);
|
||||||
this.throughput(AddCollectionUtility.getMaxThroughput(this.container.collectionCreationDefaults, this.container));
|
this.throughput(AddCollectionUtility.getMaxThroughput(this.container.collectionCreationDefaults, this.container));
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
abstract class CacheBase<T> {
|
abstract class CacheBase<T> {
|
||||||
public data: T[] | null;
|
public data: T[];
|
||||||
public sortOrder: any;
|
public sortOrder: any;
|
||||||
public serverCallInProgress: boolean;
|
public serverCallInProgress: boolean;
|
||||||
|
|
||||||
|
|||||||
@@ -16,75 +16,14 @@ import * as Entities from "../Entities";
|
|||||||
import QueryTablesTab from "../../Tabs/QueryTablesTab";
|
import QueryTablesTab from "../../Tabs/QueryTablesTab";
|
||||||
import * as TableEntityProcessor from "../TableEntityProcessor";
|
import * as TableEntityProcessor from "../TableEntityProcessor";
|
||||||
import * as TelemetryProcessor from "../../../Shared/Telemetry/TelemetryProcessor";
|
import * as TelemetryProcessor from "../../../Shared/Telemetry/TelemetryProcessor";
|
||||||
|
import * as DataModels from "../../../Contracts/DataModels";
|
||||||
import * as ViewModels from "../../../Contracts/ViewModels";
|
import * as ViewModels from "../../../Contracts/ViewModels";
|
||||||
|
import { getErrorMessage, getErrorStack } from "../../../Common/ErrorHandlingUtils";
|
||||||
|
|
||||||
interface IListTableEntitiesSegmentedResult extends Entities.IListTableEntitiesResult {
|
interface IListTableEntitiesSegmentedResult extends Entities.IListTableEntitiesResult {
|
||||||
ExceedMaximumRetries?: boolean;
|
ExceedMaximumRetries?: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface ErrorDataModel {
|
|
||||||
message: string;
|
|
||||||
severity?: string;
|
|
||||||
location?: {
|
|
||||||
start: string;
|
|
||||||
end: string;
|
|
||||||
};
|
|
||||||
code?: string;
|
|
||||||
}
|
|
||||||
|
|
||||||
function parseError(err: any): ErrorDataModel[] {
|
|
||||||
try {
|
|
||||||
return _parse(err);
|
|
||||||
} catch (e) {
|
|
||||||
return [<ErrorDataModel>{ message: JSON.stringify(err) }];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function _parse(err: any): ErrorDataModel[] {
|
|
||||||
var normalizedErrors: ErrorDataModel[] = [];
|
|
||||||
if (err.message && !err.code) {
|
|
||||||
normalizedErrors.push(err);
|
|
||||||
} else {
|
|
||||||
const innerErrors: any[] = _getInnerErrors(err.message);
|
|
||||||
normalizedErrors = innerErrors.map(innerError =>
|
|
||||||
typeof innerError === "string" ? { message: innerError } : innerError
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
return normalizedErrors;
|
|
||||||
}
|
|
||||||
|
|
||||||
function _getInnerErrors(message: string): any[] {
|
|
||||||
/*
|
|
||||||
The backend error message has an inner-message which is a stringified object.
|
|
||||||
For SQL errors, the "errors" property is an array of SqlErrorDataModel.
|
|
||||||
Example:
|
|
||||||
"Message: {"Errors":["Resource with specified id or name already exists"]}\r\nActivityId: 80005000008d40b6a, Request URI: /apps/19000c000c0a0005/services/mctestdocdbprod-MasterService-0-00066ab9937/partitions/900005f9000e676fb8/replicas/13000000000955p"
|
|
||||||
For non-SQL errors the "Errors" propery is an array of string.
|
|
||||||
Example:
|
|
||||||
"Message: {"errors":[{"severity":"Error","location":{"start":7,"end":8},"code":"SC1001","message":"Syntax error, incorrect syntax near '.'."}]}\r\nActivityId: d3300016d4084e310a, Request URI: /apps/12401f9e1df77/services/dc100232b1f44545/partitions/f86f3bc0001a2f78/replicas/13085003638s"
|
|
||||||
*/
|
|
||||||
|
|
||||||
let innerMessage: any = null;
|
|
||||||
|
|
||||||
const singleLineMessage = message.replace(/[\r\n]|\r|\n/g, "");
|
|
||||||
try {
|
|
||||||
// Multi-Partition error flavor
|
|
||||||
const regExp = /^(.*)ActivityId: (.*)/g;
|
|
||||||
const regString = regExp.exec(singleLineMessage);
|
|
||||||
const innerMessageString = regString[1];
|
|
||||||
innerMessage = JSON.parse(innerMessageString);
|
|
||||||
} catch (e) {
|
|
||||||
// Single-partition error flavor
|
|
||||||
const regExp = /^Message: (.*)ActivityId: (.*), Request URI: (.*)/g;
|
|
||||||
const regString = regExp.exec(singleLineMessage);
|
|
||||||
const innerMessageString = regString[1];
|
|
||||||
innerMessage = JSON.parse(innerMessageString);
|
|
||||||
}
|
|
||||||
|
|
||||||
return innerMessage.errors ? innerMessage.errors : innerMessage.Errors;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Storage Table Entity List ViewModel
|
* Storage Table Entity List ViewModel
|
||||||
*/
|
*/
|
||||||
@@ -448,17 +387,8 @@ export default class TableEntityListViewModel extends DataTableViewModel {
|
|||||||
}
|
}
|
||||||
})
|
})
|
||||||
.catch((error: any) => {
|
.catch((error: any) => {
|
||||||
const parsedErrors = parseError(error);
|
const errorMessage = getErrorMessage(error);
|
||||||
var errors = parsedErrors.map(error => {
|
this.queryErrorMessage(errorMessage);
|
||||||
return <ViewModels.QueryError>{
|
|
||||||
message: error.message,
|
|
||||||
start: error.location ? error.location.start : undefined,
|
|
||||||
end: error.location ? error.location.end : undefined,
|
|
||||||
code: error.code,
|
|
||||||
severity: error.severity
|
|
||||||
};
|
|
||||||
});
|
|
||||||
this.queryErrorMessage(errors[0].message);
|
|
||||||
if (this.queryTablesTab.onLoadStartKey != null && this.queryTablesTab.onLoadStartKey != undefined) {
|
if (this.queryTablesTab.onLoadStartKey != null && this.queryTablesTab.onLoadStartKey != undefined) {
|
||||||
TelemetryProcessor.traceFailure(
|
TelemetryProcessor.traceFailure(
|
||||||
Action.Tab,
|
Action.Tab,
|
||||||
@@ -469,7 +399,8 @@ export default class TableEntityListViewModel extends DataTableViewModel {
|
|||||||
defaultExperience: this.queryTablesTab.collection.container.defaultExperience(),
|
defaultExperience: this.queryTablesTab.collection.container.defaultExperience(),
|
||||||
dataExplorerArea: Areas.Tab,
|
dataExplorerArea: Areas.Tab,
|
||||||
tabTitle: this.queryTablesTab.tabTitle(),
|
tabTitle: this.queryTablesTab.tabTitle(),
|
||||||
error: error
|
error: errorMessage,
|
||||||
|
errorStack: getErrorStack(error)
|
||||||
},
|
},
|
||||||
this.queryTablesTab.onLoadStartKey
|
this.queryTablesTab.onLoadStartKey
|
||||||
);
|
);
|
||||||
@@ -490,53 +421,47 @@ export default class TableEntityListViewModel extends DataTableViewModel {
|
|||||||
* Note that this also means that we can get less entities than the requested download size in a successful call.
|
* Note that this also means that we can get less entities than the requested download size in a successful call.
|
||||||
* See Microsoft Azure API Documentation at: https://msdn.microsoft.com/en-us/library/azure/dd135718.aspx
|
* See Microsoft Azure API Documentation at: https://msdn.microsoft.com/en-us/library/azure/dd135718.aspx
|
||||||
*/
|
*/
|
||||||
private prefetchData(
|
private async prefetchData(
|
||||||
tableQuery: Entities.ITableQuery,
|
tableQuery: Entities.ITableQuery,
|
||||||
downloadSize: number,
|
downloadSize: number,
|
||||||
currentRetry: number = 0
|
currentRetry: number = 0
|
||||||
): Q.Promise<any> {
|
): Promise<IListTableEntitiesSegmentedResult> {
|
||||||
if (!this.cache.serverCallInProgress) {
|
if (!this.cache.serverCallInProgress) {
|
||||||
this.cache.serverCallInProgress = true;
|
this.cache.serverCallInProgress = true;
|
||||||
this.allDownloaded = false;
|
this.allDownloaded = false;
|
||||||
this.lastPrefetchTime = new Date().getTime();
|
this.lastPrefetchTime = new Date().getTime();
|
||||||
var time = this.lastPrefetchTime;
|
const time = this.lastPrefetchTime;
|
||||||
|
|
||||||
var promise: Q.Promise<IListTableEntitiesSegmentedResult>;
|
|
||||||
if (this._documentIterator && this.continuationToken) {
|
if (this._documentIterator && this.continuationToken) {
|
||||||
// TODO handle Cassandra case
|
// TODO handle Cassandra case
|
||||||
|
const response = await this._documentIterator.fetchNext();
|
||||||
|
const entities: Entities.ITableEntity[] = TableEntityProcessor.convertDocumentsToEntities(response?.resources);
|
||||||
|
|
||||||
promise = Q(this._documentIterator.fetchNext().then(response => response.resources)).then(
|
return {
|
||||||
(documents: any[]) => {
|
Results: entities,
|
||||||
let entities: Entities.ITableEntity[] = TableEntityProcessor.convertDocumentsToEntities(documents);
|
ContinuationToken: this._documentIterator.hasMoreResults()
|
||||||
let finalEntities: IListTableEntitiesSegmentedResult = <IListTableEntitiesSegmentedResult>{
|
};
|
||||||
Results: entities,
|
}
|
||||||
ContinuationToken: this._documentIterator.hasMoreResults()
|
|
||||||
};
|
try {
|
||||||
return Q.resolve(finalEntities);
|
let documents: IListTableEntitiesSegmentedResult;
|
||||||
}
|
if (this.continuationToken && this.queryTablesTab.container.isPreferredApiCassandra()) {
|
||||||
);
|
documents = await this.queryTablesTab.container.tableDataClient.queryDocuments(
|
||||||
} else if (this.continuationToken && this.queryTablesTab.container.isPreferredApiCassandra()) {
|
|
||||||
promise = Q(
|
|
||||||
this.queryTablesTab.container.tableDataClient.queryDocuments(
|
|
||||||
this.queryTablesTab.collection,
|
this.queryTablesTab.collection,
|
||||||
this.cqlQuery(),
|
this.cqlQuery(),
|
||||||
true,
|
true,
|
||||||
this.continuationToken
|
this.continuationToken
|
||||||
)
|
);
|
||||||
);
|
} else {
|
||||||
} else {
|
const query = this.queryTablesTab.container.isPreferredApiCassandra() ? this.cqlQuery() : this.sqlQuery();
|
||||||
let query = this.sqlQuery();
|
documents = await this.queryTablesTab.container.tableDataClient.queryDocuments(
|
||||||
if (this.queryTablesTab.container.isPreferredApiCassandra()) {
|
this.queryTablesTab.collection,
|
||||||
query = this.cqlQuery();
|
query,
|
||||||
}
|
true
|
||||||
promise = Q(
|
);
|
||||||
this.queryTablesTab.container.tableDataClient.queryDocuments(this.queryTablesTab.collection, query, true)
|
|
||||||
);
|
|
||||||
}
|
|
||||||
return promise
|
|
||||||
.then((result: IListTableEntitiesSegmentedResult) => {
|
|
||||||
if (!this._documentIterator) {
|
if (!this._documentIterator) {
|
||||||
this._documentIterator = result.iterator;
|
this._documentIterator = documents.iterator;
|
||||||
}
|
}
|
||||||
var actualDownloadSize: number = 0;
|
var actualDownloadSize: number = 0;
|
||||||
|
|
||||||
@@ -547,11 +472,11 @@ export default class TableEntityListViewModel extends DataTableViewModel {
|
|||||||
return Q.resolve(null);
|
return Q.resolve(null);
|
||||||
}
|
}
|
||||||
|
|
||||||
var entities = result.Results;
|
var entities = documents.Results;
|
||||||
actualDownloadSize = entities.length;
|
actualDownloadSize = entities.length;
|
||||||
|
|
||||||
// Queries can fetch no results and still return a continuation header. See prefetchAndRender() method.
|
// Queries can fetch no results and still return a continuation header. See prefetchAndRender() method.
|
||||||
this.continuationToken = this.isCancelled ? null : result.ContinuationToken;
|
this.continuationToken = this.isCancelled ? null : documents.ContinuationToken;
|
||||||
|
|
||||||
if (!this.continuationToken) {
|
if (!this.continuationToken) {
|
||||||
this.allDownloaded = true;
|
this.allDownloaded = true;
|
||||||
@@ -583,20 +508,22 @@ export default class TableEntityListViewModel extends DataTableViewModel {
|
|||||||
// For #2.1, set prefetch exceeds maximum retry number and end prefetch.
|
// For #2.1, set prefetch exceeds maximum retry number and end prefetch.
|
||||||
// For #2.2, go to next round prefetch.
|
// For #2.2, go to next round prefetch.
|
||||||
if (this.allDownloaded || nextDownloadSize === 0) {
|
if (this.allDownloaded || nextDownloadSize === 0) {
|
||||||
return Q.resolve(result);
|
return documents;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (currentRetry >= TableEntityListViewModel._maximumNumberOfPrefetchRetries) {
|
if (currentRetry >= TableEntityListViewModel._maximumNumberOfPrefetchRetries) {
|
||||||
result.ExceedMaximumRetries = true;
|
documents.ExceedMaximumRetries = true;
|
||||||
return Q.resolve(result);
|
return documents;
|
||||||
}
|
}
|
||||||
return this.prefetchData(tableQuery, nextDownloadSize, currentRetry + 1);
|
|
||||||
})
|
return await this.prefetchData(tableQuery, nextDownloadSize, currentRetry + 1);
|
||||||
.catch((error: Error) => {
|
}
|
||||||
this.cache.serverCallInProgress = false;
|
} catch (error) {
|
||||||
return Q.reject(error);
|
this.cache.serverCallInProgress = false;
|
||||||
});
|
throw error;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return null;
|
|
||||||
|
return undefined;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -7,7 +7,7 @@ export interface ITableEntity {
|
|||||||
export interface ITableEntityForTablesAPI extends ITableEntity {
|
export interface ITableEntityForTablesAPI extends ITableEntity {
|
||||||
PartitionKey: ITableEntityAttribute;
|
PartitionKey: ITableEntityAttribute;
|
||||||
RowKey: ITableEntityAttribute;
|
RowKey: ITableEntityAttribute;
|
||||||
Timestamp: ITableEntityAttribute;
|
Timestamp?: ITableEntityAttribute;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface ITableEntityAttribute {
|
export interface ITableEntityAttribute {
|
||||||
|
|||||||
@@ -312,10 +312,9 @@ export class CassandraAPIDataClient extends TableDataClient {
|
|||||||
const clearMessage = NotificationConsoleUtils.logConsoleProgress(`Deleting row ${currEntityToDelete.RowKey._}`);
|
const clearMessage = NotificationConsoleUtils.logConsoleProgress(`Deleting row ${currEntityToDelete.RowKey._}`);
|
||||||
const partitionKeyValue = currEntityToDelete[partitionKeyProperty];
|
const partitionKeyValue = currEntityToDelete[partitionKeyProperty];
|
||||||
const currQuery =
|
const currQuery =
|
||||||
query +
|
query + this.isStringType(partitionKeyValue.$)
|
||||||
(this.isStringType(partitionKeyValue.$)
|
|
||||||
? `${partitionKeyProperty} = '${partitionKeyValue._}'`
|
? `${partitionKeyProperty} = '${partitionKeyValue._}'`
|
||||||
: `${partitionKeyProperty} = ${partitionKeyValue._}`);
|
: `${partitionKeyProperty} = ${partitionKeyValue._}`;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
await this.queryDocuments(collection, currQuery);
|
await this.queryDocuments(collection, currQuery);
|
||||||
|
|||||||
@@ -23,19 +23,6 @@
|
|||||||
<div class="scaleDivison" aria-label="Scale" aria-controls="scaleRegion">
|
<div class="scaleDivison" aria-label="Scale" aria-controls="scaleRegion">
|
||||||
<span class="scaleSettingTitle">Scale</span>
|
<span class="scaleSettingTitle">Scale</span>
|
||||||
</div>
|
</div>
|
||||||
<div class="freeTierInfoBanner" data-bind="visible: isFreeTierAccount">
|
|
||||||
<span class="freeTierInfoIcon"><img src="/info_color.svg" alt="Info"/></span>
|
|
||||||
<span class="freeTierInfoMessage"
|
|
||||||
>With free tier, you'll get the first 400 RU/s and 5 GB of storage in this account for free. To keep your
|
|
||||||
account free, keep the total RU/s across all resources in the account to 400 RU/s.
|
|
||||||
<a
|
|
||||||
href="https://docs.microsoft.com/en-us/azure/cosmos-db/understand-your-bill#billing-examples-with-free-tier-accounts"
|
|
||||||
target="_blank"
|
|
||||||
>
|
|
||||||
Learn more.</a
|
|
||||||
>
|
|
||||||
</span>
|
|
||||||
</div>
|
|
||||||
<div class="ssTextAllignment" id="scaleRegion">
|
<div class="ssTextAllignment" id="scaleRegion">
|
||||||
<throughput-input-autopilot-v3
|
<throughput-input-autopilot-v3
|
||||||
params="{
|
params="{
|
||||||
@@ -59,8 +46,7 @@
|
|||||||
autoPilotUsageCost: autoPilotUsageCost,
|
autoPilotUsageCost: autoPilotUsageCost,
|
||||||
canExceedMaximumValue: canExceedMaximumValue,
|
canExceedMaximumValue: canExceedMaximumValue,
|
||||||
overrideWithAutoPilotSettings: overrideWithAutoPilotSettings,
|
overrideWithAutoPilotSettings: overrideWithAutoPilotSettings,
|
||||||
overrideWithProvisionedThroughputSettings: overrideWithProvisionedThroughputSettings,
|
overrideWithProvisionedThroughputSettings: overrideWithProvisionedThroughputSettings
|
||||||
freeTierExceedThroughputWarning: freeTierExceedThroughputWarning
|
|
||||||
}"
|
}"
|
||||||
>
|
>
|
||||||
</throughput-input-autopilot-v3>
|
</throughput-input-autopilot-v3>
|
||||||
|
|||||||
@@ -57,7 +57,6 @@ export default class DatabaseSettingsTab extends TabsBase implements ViewModels.
|
|||||||
public canThroughputExceedMaximumValue: ko.Computed<boolean>;
|
public canThroughputExceedMaximumValue: ko.Computed<boolean>;
|
||||||
public costsVisible: ko.Computed<boolean>;
|
public costsVisible: ko.Computed<boolean>;
|
||||||
public displayedError: ko.Observable<string>;
|
public displayedError: ko.Observable<string>;
|
||||||
public isFreeTierAccount: ko.Computed<boolean>;
|
|
||||||
public isTemplateReady: ko.Observable<boolean>;
|
public isTemplateReady: ko.Observable<boolean>;
|
||||||
public minRUAnotationVisible: ko.Computed<boolean>;
|
public minRUAnotationVisible: ko.Computed<boolean>;
|
||||||
public minRUs: ko.Observable<number>;
|
public minRUs: ko.Observable<number>;
|
||||||
@@ -83,7 +82,6 @@ export default class DatabaseSettingsTab extends TabsBase implements ViewModels.
|
|||||||
public throughputAutoPilotRadioId: string;
|
public throughputAutoPilotRadioId: string;
|
||||||
public throughputProvisionedRadioId: string;
|
public throughputProvisionedRadioId: string;
|
||||||
public throughputModeRadioName: string;
|
public throughputModeRadioName: string;
|
||||||
public freeTierExceedThroughputWarning: ko.Computed<string>;
|
|
||||||
|
|
||||||
private _hasProvisioningTypeChanged: ko.Computed<boolean>;
|
private _hasProvisioningTypeChanged: ko.Computed<boolean>;
|
||||||
private _wasAutopilotOriginallySet: ko.Observable<boolean>;
|
private _wasAutopilotOriginallySet: ko.Observable<boolean>;
|
||||||
@@ -361,17 +359,6 @@ export default class DatabaseSettingsTab extends TabsBase implements ViewModels.
|
|||||||
|
|
||||||
this.isTemplateReady = ko.observable<boolean>(false);
|
this.isTemplateReady = ko.observable<boolean>(false);
|
||||||
|
|
||||||
this.isFreeTierAccount = ko.computed<boolean>(() => {
|
|
||||||
const databaseAccount = this.container?.databaseAccount();
|
|
||||||
return databaseAccount?.properties?.enableFreeTier;
|
|
||||||
});
|
|
||||||
|
|
||||||
this.freeTierExceedThroughputWarning = ko.computed<string>(() =>
|
|
||||||
this.isFreeTierAccount()
|
|
||||||
? "Billing will apply if you provision more than 400 RU/s of manual throughput, or if the resource scales beyond 400 RU/s with autoscale."
|
|
||||||
: ""
|
|
||||||
);
|
|
||||||
|
|
||||||
this._buildCommandBarOptions();
|
this._buildCommandBarOptions();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -11,17 +11,6 @@
|
|||||||
Start by writing a Mongo query, for example: <strong>{'id':'foo'}</strong> or <strong>{ }</strong> to get all the
|
Start by writing a Mongo query, for example: <strong>{'id':'foo'}</strong> or <strong>{ }</strong> to get all the
|
||||||
documents.
|
documents.
|
||||||
</div>
|
</div>
|
||||||
<div class="warningErrorContainer" aria-live="assertive" data-bind="visible: maybeSubQuery">
|
|
||||||
<div class="warningErrorContent">
|
|
||||||
<span><img class="paneErrorIcon" src="/info_color.svg" alt="Error"/></span>
|
|
||||||
<span class="warningErrorDetailsLinkContainer">
|
|
||||||
We have detected you may be using a subquery. Non-correlated subqueries are not currently supported.
|
|
||||||
<a href="https://docs.microsoft.com/en-us/azure/cosmos-db/sql-query-subquery"
|
|
||||||
>Please see Cosmos sub query documentation for further information</a
|
|
||||||
>
|
|
||||||
</span>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="queryEditorWithSplitter" data-bind="attr: { id: queryEditorId }">
|
<div class="queryEditorWithSplitter" data-bind="attr: { id: queryEditorId }">
|
||||||
<editor
|
<editor
|
||||||
class="queryEditor"
|
class="queryEditor"
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
import * as ko from "knockout";
|
import * as ko from "knockout";
|
||||||
|
import Q from "q";
|
||||||
import * as Constants from "../../Common/Constants";
|
import * as Constants from "../../Common/Constants";
|
||||||
import * as DataModels from "../../Contracts/DataModels";
|
import * as DataModels from "../../Contracts/DataModels";
|
||||||
import * as ViewModels from "../../Contracts/ViewModels";
|
import * as ViewModels from "../../Contracts/ViewModels";
|
||||||
@@ -6,6 +7,7 @@ import { Action } from "../../Shared/Telemetry/TelemetryConstants";
|
|||||||
import TabsBase from "./TabsBase";
|
import TabsBase from "./TabsBase";
|
||||||
import { HashMap } from "../../Common/HashMap";
|
import { HashMap } from "../../Common/HashMap";
|
||||||
import * as HeadersUtility from "../../Common/HeadersUtility";
|
import * as HeadersUtility from "../../Common/HeadersUtility";
|
||||||
|
import * as Logger from "../../Common/Logger";
|
||||||
import { Splitter, SplitterBounds, SplitterDirection } from "../../Common/Splitter";
|
import { Splitter, SplitterBounds, SplitterDirection } from "../../Common/Splitter";
|
||||||
import * as TelemetryProcessor from "../../Shared/Telemetry/TelemetryProcessor";
|
import * as TelemetryProcessor from "../../Shared/Telemetry/TelemetryProcessor";
|
||||||
import ExecuteQueryIcon from "../../../images/ExecuteQuery.svg";
|
import ExecuteQueryIcon from "../../../images/ExecuteQuery.svg";
|
||||||
@@ -29,7 +31,6 @@ export default class QueryTab extends TabsBase implements ViewModels.WaitsForTem
|
|||||||
public fetchNextPageButton: ViewModels.Button;
|
public fetchNextPageButton: ViewModels.Button;
|
||||||
public saveQueryButton: ViewModels.Button;
|
public saveQueryButton: ViewModels.Button;
|
||||||
public initialEditorContent: ko.Observable<string>;
|
public initialEditorContent: ko.Observable<string>;
|
||||||
public maybeSubQuery: ko.Computed<boolean>;
|
|
||||||
public sqlQueryEditorContent: ko.Observable<string>;
|
public sqlQueryEditorContent: ko.Observable<string>;
|
||||||
public selectedContent: ko.Observable<string>;
|
public selectedContent: ko.Observable<string>;
|
||||||
public sqlStatementToExecute: ko.Observable<string>;
|
public sqlStatementToExecute: ko.Observable<string>;
|
||||||
@@ -119,11 +120,6 @@ export default class QueryTab extends TabsBase implements ViewModels.WaitsForTem
|
|||||||
return (container && (container.isPreferredApiDocumentDB() || container.isPreferredApiGraph())) || false;
|
return (container && (container.isPreferredApiDocumentDB() || container.isPreferredApiGraph())) || false;
|
||||||
});
|
});
|
||||||
|
|
||||||
this.maybeSubQuery = ko.computed<boolean>(function() {
|
|
||||||
const sql = this.sqlQueryEditorContent();
|
|
||||||
return sql && /.*\(.*SELECT.*\)/i.test(sql);
|
|
||||||
}, this);
|
|
||||||
|
|
||||||
this.saveQueryButton = {
|
this.saveQueryButton = {
|
||||||
enabled: this._isSaveQueriesEnabled,
|
enabled: this._isSaveQueriesEnabled,
|
||||||
visible: this._isSaveQueriesEnabled
|
visible: this._isSaveQueriesEnabled
|
||||||
@@ -328,6 +324,21 @@ export default class QueryTab extends TabsBase implements ViewModels.WaitsForTem
|
|||||||
queryResults.itemCount > 0 ? `${queryResults.firstItemIndex} - ${queryResults.lastItemIndex}` : `0 - 0`;
|
queryResults.itemCount > 0 ? `${queryResults.firstItemIndex} - ${queryResults.lastItemIndex}` : `0 - 0`;
|
||||||
this.showingDocumentsDisplayText(resultsDisplay);
|
this.showingDocumentsDisplayText(resultsDisplay);
|
||||||
this.requestChargeDisplayText(`${queryResults.requestCharge} RUs`);
|
this.requestChargeDisplayText(`${queryResults.requestCharge} RUs`);
|
||||||
|
|
||||||
|
if (!this.queryResults() && !results) {
|
||||||
|
const errorMessage: string = JSON.stringify({
|
||||||
|
error: `Returned no results after query execution`,
|
||||||
|
accountName: this.collection && this.collection.container.databaseAccount(),
|
||||||
|
databaseName: this.collection && this.collection.databaseId,
|
||||||
|
collectionName: this.collection && this.collection.id(),
|
||||||
|
sqlQuery: this.sqlStatementToExecute(),
|
||||||
|
hasMoreResults: resultsMetadata.hasMoreResults,
|
||||||
|
itemCount: resultsMetadata.itemCount,
|
||||||
|
responseHeaders: queryResults && queryResults.headers
|
||||||
|
});
|
||||||
|
Logger.logError(errorMessage, "QueryTab");
|
||||||
|
}
|
||||||
|
|
||||||
this.queryResults(results);
|
this.queryResults(results);
|
||||||
|
|
||||||
TelemetryProcessor.traceSuccess(
|
TelemetryProcessor.traceSuccess(
|
||||||
|
|||||||
@@ -8,7 +8,7 @@ enum ScrollPosition {
|
|||||||
|
|
||||||
export class AccessibleVerticalList {
|
export class AccessibleVerticalList {
|
||||||
private items: any[] = [];
|
private items: any[] = [];
|
||||||
private onSelect?: (item: any) => void;
|
private onSelect: (item: any) => void;
|
||||||
|
|
||||||
public currentItemIndex: ko.Observable<number>;
|
public currentItemIndex: ko.Observable<number>;
|
||||||
public currentItem: ko.Computed<any>;
|
public currentItem: ko.Computed<any>;
|
||||||
@@ -42,9 +42,7 @@ export class AccessibleVerticalList {
|
|||||||
const targetElement = targetContainer
|
const targetElement = targetContainer
|
||||||
.getElementsByClassName("accessibleListElement")
|
.getElementsByClassName("accessibleListElement")
|
||||||
.item(this.currentItemIndex());
|
.item(this.currentItemIndex());
|
||||||
if (targetElement) {
|
this.scrollElementIntoContainerViewIfNeeded(targetElement, targetContainer, ScrollPosition.Top);
|
||||||
this.scrollElementIntoContainerViewIfNeeded(targetElement, targetContainer, ScrollPosition.Top);
|
|
||||||
}
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
if (event.keyCode === 40) {
|
if (event.keyCode === 40) {
|
||||||
@@ -54,9 +52,7 @@ export class AccessibleVerticalList {
|
|||||||
const targetElement = targetContainer
|
const targetElement = targetContainer
|
||||||
.getElementsByClassName("accessibleListElement")
|
.getElementsByClassName("accessibleListElement")
|
||||||
.item(this.currentItemIndex());
|
.item(this.currentItemIndex());
|
||||||
if (targetElement) {
|
this.scrollElementIntoContainerViewIfNeeded(targetElement, targetContainer, ScrollPosition.Bottom);
|
||||||
this.scrollElementIntoContainerViewIfNeeded(targetElement, targetContainer, ScrollPosition.Top);
|
|
||||||
}
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
|
|||||||
242
src/HostedExplorer.tsx
Normal file
242
src/HostedExplorer.tsx
Normal file
@@ -0,0 +1,242 @@
|
|||||||
|
import { Configuration, PublicClientApplication } from "@azure/msal-browser";
|
||||||
|
import { AuthenticatedTemplate, MsalProvider, UnauthenticatedTemplate } from "@azure/msal-react";
|
||||||
|
import { useBoolean } from "@uifabric/react-hooks";
|
||||||
|
import {
|
||||||
|
DefaultButton,
|
||||||
|
DirectionalHint,
|
||||||
|
FocusZone,
|
||||||
|
initializeIcons,
|
||||||
|
Panel,
|
||||||
|
Persona,
|
||||||
|
PersonaInitialsColor,
|
||||||
|
PersonaSize
|
||||||
|
} from "office-ui-fabric-react";
|
||||||
|
import * as React from "react";
|
||||||
|
import { render } from "react-dom";
|
||||||
|
import FeedbackIcon from "../images/Feedback.svg";
|
||||||
|
import ConnectIcon from "../images/HostedConnectwhite.svg";
|
||||||
|
import "../less/hostedexplorer.less";
|
||||||
|
import { CommandButtonComponent } from "./Explorer/Controls/CommandButton/CommandButtonComponent";
|
||||||
|
import { DefaultDirectoryDropdownComponent } from "./Explorer/Controls/Directory/DefaultDirectoryDropdownComponent";
|
||||||
|
import { DirectoryListComponent } from "./Explorer/Controls/Directory/DirectoryListComponent";
|
||||||
|
import "./Explorer/Menus/NavBar/MeControlComponent.less";
|
||||||
|
import { useGraphProfile } from "./hooks/useGraphProfile";
|
||||||
|
import "./Shared/appInsights";
|
||||||
|
|
||||||
|
initializeIcons();
|
||||||
|
|
||||||
|
// MSAL configuration
|
||||||
|
const configuration: Configuration = {
|
||||||
|
auth: {
|
||||||
|
clientId: "e8ae3d28-de2a-4dc8-8fa3-2d2998b1c38f",
|
||||||
|
redirectUri: "https://localhost:1234/hostedExplorer.html",
|
||||||
|
authority: "https://login.microsoftonline.com/72f988bf-86f1-41af-91ab-2d7cd011db47"
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const application = new PublicClientApplication(configuration);
|
||||||
|
|
||||||
|
const App: React.FunctionComponent = () => {
|
||||||
|
const [isOpen, { setTrue: openPanel, setFalse: dismissPanel }] = useBoolean(false);
|
||||||
|
const { graphData, photo } = useGraphProfile();
|
||||||
|
|
||||||
|
const menuProps = {
|
||||||
|
className: "mecontrolContextualMenu",
|
||||||
|
isBeakVisible: false,
|
||||||
|
directionalHintFixed: true,
|
||||||
|
directionalHint: DirectionalHint.bottomRightEdge,
|
||||||
|
calloutProps: {
|
||||||
|
minPagePadding: 0
|
||||||
|
},
|
||||||
|
items: [
|
||||||
|
{
|
||||||
|
key: "Persona",
|
||||||
|
onRender: () => <Persona />
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: "SwitchDirectory",
|
||||||
|
onRender: () => (
|
||||||
|
<div className="switchDirectoryLink" onClick={() => openPanel}>
|
||||||
|
Switch Directory
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: "SignOut",
|
||||||
|
onRender: () => (
|
||||||
|
<div
|
||||||
|
className="signOutLink"
|
||||||
|
onClick={() => {
|
||||||
|
instance.logout();
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
Sign out
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
]
|
||||||
|
};
|
||||||
|
const personaProps = {};
|
||||||
|
|
||||||
|
// {
|
||||||
|
// id: "commandbutton-settings",
|
||||||
|
// iconSrc: SettingsIcon,
|
||||||
|
// iconAlt: "setting button",
|
||||||
|
// onCommandClick: () => {},
|
||||||
|
// commandButtonLabel: undefined,
|
||||||
|
// ariaLabel: "setting button",
|
||||||
|
// tooltipText: "Global settings",
|
||||||
|
// hasPopup: true,
|
||||||
|
// disabled: false
|
||||||
|
// },
|
||||||
|
// {
|
||||||
|
// id: "commandbutton-feedback",
|
||||||
|
// iconSrc: FeedbackIcon,
|
||||||
|
// iconAlt: "feeback button",
|
||||||
|
// onCommandClick: () =>
|
||||||
|
// window.open(
|
||||||
|
// "https://aka.ms/cosmosdbfeedback?subject=Cosmos%20DB%20Hosted%20Data%20Explorer%20Feedback"
|
||||||
|
// ),
|
||||||
|
// commandButtonLabel: undefined,
|
||||||
|
// ariaLabel: "feeback button",
|
||||||
|
// tooltipText: "Send feedback",
|
||||||
|
// hasPopup: true,
|
||||||
|
// disabled: false
|
||||||
|
// }
|
||||||
|
|
||||||
|
const buttonProps = {
|
||||||
|
id: "mecontrolHeader",
|
||||||
|
className: "mecontrolHeaderButton",
|
||||||
|
menuProps: menuProps,
|
||||||
|
onRenderMenuIcon: () => <span />,
|
||||||
|
styles: {
|
||||||
|
rootHovered: { backgroundColor: "#393939" },
|
||||||
|
rootFocused: { backgroundColor: "#393939" },
|
||||||
|
rootPressed: { backgroundColor: "#393939" },
|
||||||
|
rootExpanded: { backgroundColor: "#393939" }
|
||||||
|
}
|
||||||
|
};
|
||||||
|
return (
|
||||||
|
<div>
|
||||||
|
<header>
|
||||||
|
<div className="items" role="menubar">
|
||||||
|
<div className="cosmosDBTitle">
|
||||||
|
<span
|
||||||
|
className="title"
|
||||||
|
data-bind="click: openAzurePortal, event: { keypress: onOpenAzurePortalKeyPress }"
|
||||||
|
tabIndex={0}
|
||||||
|
title="Go to Azure Portal"
|
||||||
|
>
|
||||||
|
Microsoft Azure
|
||||||
|
</span>
|
||||||
|
<span className="accontSplitter" /> <span className="serviceTitle">Cosmos DB</span>
|
||||||
|
<img
|
||||||
|
className="chevronRight"
|
||||||
|
src="/chevron-right.svg"
|
||||||
|
alt="account separator"
|
||||||
|
data-bind="visible: isAccountActive"
|
||||||
|
/>
|
||||||
|
<span
|
||||||
|
className="accountSwitchComponentContainer"
|
||||||
|
data-bind="react: accountSwitchComponentAdapter, visible: isAccountActive"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<div className="feedbackConnectSettingIcons">
|
||||||
|
<AuthenticatedTemplate>
|
||||||
|
<CommandButtonComponent
|
||||||
|
id="commandbutton-connect"
|
||||||
|
iconSrc={ConnectIcon}
|
||||||
|
iconAlt="connect button"
|
||||||
|
onCommandClick={() => {}}
|
||||||
|
ariaLabel="connect button"
|
||||||
|
tooltipText="Connect to a Cosmos DB account"
|
||||||
|
hasPopup={true}
|
||||||
|
disabled={false}
|
||||||
|
/>
|
||||||
|
</AuthenticatedTemplate>
|
||||||
|
<UnauthenticatedTemplate>
|
||||||
|
<CommandButtonComponent
|
||||||
|
id="commandbutton-feedback"
|
||||||
|
iconSrc={FeedbackIcon}
|
||||||
|
iconAlt="feeback button"
|
||||||
|
onCommandClick={() =>
|
||||||
|
window.open(
|
||||||
|
"https://aka.ms/cosmosdbfeedback?subject=Cosmos%20DB%20Hosted%20Data%20Explorer%20Feedback"
|
||||||
|
)
|
||||||
|
}
|
||||||
|
ariaLabel="feeback button"
|
||||||
|
tooltipText="Send feedback"
|
||||||
|
hasPopup={true}
|
||||||
|
disabled={false}
|
||||||
|
/>
|
||||||
|
</UnauthenticatedTemplate>
|
||||||
|
</div>
|
||||||
|
<div className="meControl">
|
||||||
|
<AuthenticatedTemplate>
|
||||||
|
<FocusZone>
|
||||||
|
<DefaultButton {...buttonProps}>
|
||||||
|
<Persona
|
||||||
|
imageUrl={photo}
|
||||||
|
text={graphData?.displayName}
|
||||||
|
secondaryText={graphData?.displayName}
|
||||||
|
showSecondaryText={true}
|
||||||
|
showInitialsUntilImageLoads={true}
|
||||||
|
initialsColor={PersonaInitialsColor.teal}
|
||||||
|
size={PersonaSize.size28}
|
||||||
|
className="mecontrolHeaderPersona"
|
||||||
|
/>
|
||||||
|
</DefaultButton>
|
||||||
|
</FocusZone>
|
||||||
|
</AuthenticatedTemplate>
|
||||||
|
<UnauthenticatedTemplate>
|
||||||
|
<DefaultButton
|
||||||
|
className="mecontrolSigninButton"
|
||||||
|
text="Sign In"
|
||||||
|
onClick={() => {
|
||||||
|
instance.loginPopup();
|
||||||
|
}}
|
||||||
|
styles={{
|
||||||
|
rootHovered: { backgroundColor: "#393939", color: "#fff" },
|
||||||
|
rootFocused: { backgroundColor: "#393939", color: "#fff" },
|
||||||
|
rootPressed: { backgroundColor: "#393939", color: "#fff" }
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
</UnauthenticatedTemplate>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</header>
|
||||||
|
{/* <iframe
|
||||||
|
id="explorerMenu"
|
||||||
|
name="explorer"
|
||||||
|
className="iframe"
|
||||||
|
title="explorer"
|
||||||
|
src="explorer.html?v=1.0.1&platform=Hosted"
|
||||||
|
data-bind="visible: navigationSelection() === 'explorer'"
|
||||||
|
></iframe> */}
|
||||||
|
<div data-bind="react: firewallWarningComponentAdapter" />
|
||||||
|
<div data-bind="react: dialogComponentAdapter" />
|
||||||
|
<Panel
|
||||||
|
headerText="Select Directory"
|
||||||
|
isOpen={!isOpen}
|
||||||
|
onDismiss={dismissPanel}
|
||||||
|
// You MUST provide this prop! Otherwise screen readers will just say "button" with no label.
|
||||||
|
closeButtonAriaLabel="Close"
|
||||||
|
>
|
||||||
|
<div className="directoryDropdownContainer">
|
||||||
|
<DefaultDirectoryDropdownComponent />
|
||||||
|
</div>
|
||||||
|
<div className="directoryDivider" />
|
||||||
|
<div className="directoryListContainer">
|
||||||
|
<DirectoryListComponent />
|
||||||
|
</div>
|
||||||
|
</Panel>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
render(
|
||||||
|
<MsalProvider instance={application}>
|
||||||
|
<App />
|
||||||
|
</MsalProvider>,
|
||||||
|
document.body
|
||||||
|
);
|
||||||
35
src/Main.tsx
35
src/Main.tsx
@@ -44,7 +44,6 @@ import "./Libs/jquery";
|
|||||||
import "bootstrap/dist/js/npm";
|
import "bootstrap/dist/js/npm";
|
||||||
import "../externals/jquery.typeahead.min.js";
|
import "../externals/jquery.typeahead.min.js";
|
||||||
import "../externals/jquery-ui.min.js";
|
import "../externals/jquery-ui.min.js";
|
||||||
import "../externals/adal.js";
|
|
||||||
import "promise-polyfill/src/polyfill";
|
import "promise-polyfill/src/polyfill";
|
||||||
import "abort-controller/polyfill";
|
import "abort-controller/polyfill";
|
||||||
import "whatwg-fetch";
|
import "whatwg-fetch";
|
||||||
@@ -126,17 +125,7 @@ const App: React.FunctionComponent = () => {
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="flexContainer">
|
<div className="flexContainer">
|
||||||
<div
|
<div id="divExplorer" className="flexContainer hideOverflows" style={{ display: "none" }}>
|
||||||
id="divSelfServe"
|
|
||||||
className="flexContainer"
|
|
||||||
data-bind="visible: selfServeType() && selfServeType() !== 'none', react: selfServeComponentAdapter"
|
|
||||||
></div>
|
|
||||||
<div
|
|
||||||
id="divExplorer"
|
|
||||||
data-bind="if: selfServeType() === 'none'"
|
|
||||||
className="flexContainer hideOverflows"
|
|
||||||
style={{ display: "none" }}
|
|
||||||
>
|
|
||||||
{/* Main Command Bar - Start */}
|
{/* Main Command Bar - Start */}
|
||||||
<div data-bind="react: commandBarComponentAdapter" />
|
<div data-bind="react: commandBarComponentAdapter" />
|
||||||
{/* Main Command Bar - End */}
|
{/* Main Command Bar - End */}
|
||||||
@@ -381,21 +370,17 @@ const App: React.FunctionComponent = () => {
|
|||||||
</div>
|
</div>
|
||||||
{/* Explorer Connection - Encryption Token / AAD - End */}
|
{/* Explorer Connection - Encryption Token / AAD - End */}
|
||||||
{/* Global loader - Start */}
|
{/* Global loader - Start */}
|
||||||
|
|
||||||
<div className="splashLoaderContainer" data-bind="visible: isRefreshingExplorer">
|
<div className="splashLoaderContainer" data-bind="visible: isRefreshingExplorer">
|
||||||
<div className="splashLoaderContentContainer">
|
<div className="splashLoaderContentContainer">
|
||||||
<div data-bind="visible: selfServeType() === undefined, react: selfServeLoadingComponentAdapter"></div>
|
<p className="connectExplorerContent">
|
||||||
<div data-bind="if: selfServeType() === 'none'" style={{ display: "none" }}>
|
<img src={hdeConnectImage} alt="Azure Cosmos DB" />
|
||||||
<p className="connectExplorerContent">
|
</p>
|
||||||
<img src={hdeConnectImage} alt="Azure Cosmos DB" />
|
<p className="splashLoaderTitle" id="explorerLoadingStatusTitle">
|
||||||
</p>
|
Welcome to Azure Cosmos DB
|
||||||
<p className="splashLoaderTitle" id="explorerLoadingStatusTitle">
|
</p>
|
||||||
Welcome to Azure Cosmos DB
|
<p className="splashLoaderText" id="explorerLoadingStatusText" role="alert">
|
||||||
</p>
|
Connecting...
|
||||||
<p className="splashLoaderText" id="explorerLoadingStatusText" role="alert">
|
</p>
|
||||||
Connecting...
|
|
||||||
</p>
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
{/* Global loader - End */}
|
{/* Global loader - End */}
|
||||||
|
|||||||
@@ -1,5 +1,3 @@
|
|||||||
import "expose-loader?AuthenticationContext!../../../externals/adal";
|
|
||||||
|
|
||||||
import Q from "q";
|
import Q from "q";
|
||||||
import * as Constants from "../../Common/Constants";
|
import * as Constants from "../../Common/Constants";
|
||||||
import * as DataModels from "../../Contracts/DataModels";
|
import * as DataModels from "../../Contracts/DataModels";
|
||||||
|
|||||||
@@ -1,14 +0,0 @@
|
|||||||
import { Info } from "../Explorer/Controls/SmartUi/SmartUiComponent";
|
|
||||||
import { addPropertyToMap, buildSmartUiDescriptor } from "./SelfServeUtils";
|
|
||||||
|
|
||||||
export const IsDisplayable = (): ClassDecorator => {
|
|
||||||
return target => {
|
|
||||||
buildSmartUiDescriptor(target.name, target.prototype);
|
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
||||||
export const ClassInfo = (info: (() => Promise<Info>) | Info): ClassDecorator => {
|
|
||||||
return target => {
|
|
||||||
addPropertyToMap(target.prototype, "root", target.name, "info", info);
|
|
||||||
};
|
|
||||||
};
|
|
||||||
@@ -1,167 +0,0 @@
|
|||||||
import { PropertyInfo, OnChange, Values } from "../PropertyDecorators";
|
|
||||||
import { ClassInfo, IsDisplayable } from "../ClassDecorators";
|
|
||||||
import { SelfServeBaseClass } from "../SelfServeUtils";
|
|
||||||
import { ChoiceItem, Info, InputType, UiType } from "../../Explorer/Controls/SmartUi/SmartUiComponent";
|
|
||||||
import { SessionStorageUtility } from "../../Shared/StorageUtility";
|
|
||||||
|
|
||||||
export enum Regions {
|
|
||||||
NorthCentralUS = "NCUS",
|
|
||||||
WestUS = "WUS",
|
|
||||||
EastUS2 = "EUS2"
|
|
||||||
}
|
|
||||||
|
|
||||||
export const regionDropdownItems: ChoiceItem[] = [
|
|
||||||
{ label: "North Central US", key: Regions.NorthCentralUS },
|
|
||||||
{ label: "West US", key: Regions.WestUS },
|
|
||||||
{ label: "East US 2", key: Regions.EastUS2 }
|
|
||||||
];
|
|
||||||
|
|
||||||
export const selfServeExampleInfo: Info = {
|
|
||||||
message: "This is a self serve class"
|
|
||||||
};
|
|
||||||
|
|
||||||
export const regionDropdownInfo: Info = {
|
|
||||||
message: "More regions can be added in the future."
|
|
||||||
};
|
|
||||||
|
|
||||||
const onDbThroughputChange = (currentState: Map<string, InputType>, newValue: InputType): Map<string, InputType> => {
|
|
||||||
currentState.set("dbThroughput", newValue);
|
|
||||||
currentState.set("collectionThroughput", newValue);
|
|
||||||
return currentState;
|
|
||||||
};
|
|
||||||
|
|
||||||
const initializeMaxThroughput = async (): Promise<number> => {
|
|
||||||
return 10000;
|
|
||||||
};
|
|
||||||
|
|
||||||
/*
|
|
||||||
This is an example self serve class that auto generates UI components for your feature.
|
|
||||||
|
|
||||||
Each self serve class
|
|
||||||
- Needs to extends the SelfServeBase class.
|
|
||||||
- Needs to have the @IsDisplayable() decorator to tell the compiler that UI needs to be generated from this class.
|
|
||||||
- Needs to define an onSubmit() function, a callback for when the submit button is clicked.
|
|
||||||
- Needs to define an initialize() function, to set default values for the inputs.
|
|
||||||
|
|
||||||
You can test this self serve UI by using the featureflag '?feature.selfServeType=example'
|
|
||||||
and plumb in similar feature flags for your own self serve class.
|
|
||||||
*/
|
|
||||||
|
|
||||||
/*
|
|
||||||
@IsDisplayable()
|
|
||||||
- role: Indicates to the compiler that UI should be generated from this class.
|
|
||||||
*/
|
|
||||||
@IsDisplayable()
|
|
||||||
/*
|
|
||||||
@ClassInfo()
|
|
||||||
- optional
|
|
||||||
- input: Info | () => Promise<Info>
|
|
||||||
- role: Display an Info bar as the first element of the UI.
|
|
||||||
*/
|
|
||||||
@ClassInfo(selfServeExampleInfo)
|
|
||||||
export default class SelfServeExample extends SelfServeBaseClass {
|
|
||||||
/*
|
|
||||||
onSubmit()
|
|
||||||
- input: (currentValues: Map<string, InputType>) => Promise<void>
|
|
||||||
- role: Callback that is triggerred when the submit button is clicked. You should perform your rest API
|
|
||||||
calls here using the data from the different inputs passed as a Map to this callback function.
|
|
||||||
|
|
||||||
In this example, the onSubmit callback simply sets the value for keys corresponding to the field name
|
|
||||||
in the SessionStorage.
|
|
||||||
*/
|
|
||||||
public onSubmit = async (currentValues: Map<string, InputType>): Promise<void> => {
|
|
||||||
SessionStorageUtility.setEntry("regions", currentValues.get("regions")?.toString());
|
|
||||||
SessionStorageUtility.setEntry("enableLogging", currentValues.get("enableLogging")?.toString());
|
|
||||||
SessionStorageUtility.setEntry("accountName", currentValues.get("accountName")?.toString());
|
|
||||||
SessionStorageUtility.setEntry("dbThroughput", currentValues.get("dbThroughput")?.toString());
|
|
||||||
SessionStorageUtility.setEntry("collectionThroughput", currentValues.get("collectionThroughput")?.toString());
|
|
||||||
};
|
|
||||||
|
|
||||||
/*
|
|
||||||
initialize()
|
|
||||||
- input: () => Promise<Map<string, InputType>>
|
|
||||||
- role: Set default values for the properties of this class.
|
|
||||||
|
|
||||||
The properties of this class (namely regions, enableLogging, accountName, dbThroughput, collectionThroughput),
|
|
||||||
having the @Values decorator, will each correspond to an UI element. Their values can be of 'InputType'. Their
|
|
||||||
defaults can be set by setting values in a Map corresponding to the field's name.
|
|
||||||
|
|
||||||
Typically, you can make rest calls in the async initialize function, to fetch the initial values for
|
|
||||||
these fields. This is called after the onSubmit callback, to reinitialize the defaults.
|
|
||||||
|
|
||||||
In this example, the initialize function simply reads the SessionStorage to fetch the default values
|
|
||||||
for these fields. These are then set when the changes are submitted.
|
|
||||||
*/
|
|
||||||
public initialize = async (): Promise<Map<string, InputType>> => {
|
|
||||||
const defaults = new Map<string, InputType>();
|
|
||||||
defaults.set("regions", SessionStorageUtility.getEntry("regions"));
|
|
||||||
defaults.set("enableLogging", SessionStorageUtility.getEntry("enableLogging") === "true");
|
|
||||||
const stringInput = SessionStorageUtility.getEntry("accountName");
|
|
||||||
defaults.set("accountName", stringInput ? stringInput : "");
|
|
||||||
const numberSliderInput = parseInt(SessionStorageUtility.getEntry("dbThroughput"));
|
|
||||||
defaults.set("dbThroughput", isNaN(numberSliderInput) ? 1 : numberSliderInput);
|
|
||||||
const numberSpinnerInput = parseInt(SessionStorageUtility.getEntry("collectionThroughput"));
|
|
||||||
defaults.set("collectionThroughput", isNaN(numberSpinnerInput) ? 1 : numberSpinnerInput);
|
|
||||||
return defaults;
|
|
||||||
};
|
|
||||||
|
|
||||||
/*
|
|
||||||
@PropertyInfo()
|
|
||||||
- optional
|
|
||||||
- input: Info | () => Promise<Info>
|
|
||||||
- role: Display an Info bar above the UI element for this property.
|
|
||||||
*/
|
|
||||||
@PropertyInfo(regionDropdownInfo)
|
|
||||||
|
|
||||||
/*
|
|
||||||
@Values() :
|
|
||||||
- input: NumberInputOptions | StringInputOptions | BooleanInputOptions | ChoiceInputOptions
|
|
||||||
- role: Specifies the required options to display the property as TextBox, Number Spinner/Slider, Radio buton or Dropdown.
|
|
||||||
*/
|
|
||||||
@Values({ label: "Regions", choices: regionDropdownItems })
|
|
||||||
regions: ChoiceItem;
|
|
||||||
|
|
||||||
@Values({
|
|
||||||
label: "Enable Logging",
|
|
||||||
trueLabel: "Enable",
|
|
||||||
falseLabel: "Disable"
|
|
||||||
})
|
|
||||||
enableLogging: boolean;
|
|
||||||
|
|
||||||
@Values({
|
|
||||||
label: "Account Name",
|
|
||||||
placeholder: "Enter the account name"
|
|
||||||
})
|
|
||||||
accountName: string;
|
|
||||||
|
|
||||||
/*
|
|
||||||
@OnChange()
|
|
||||||
- optional
|
|
||||||
- input: (currentValues: Map<string, InputType>, newValue: InputType) => Map<string, InputType>
|
|
||||||
- role: Takes a Map of current values and the newValue for this property as inputs. This is called when a property
|
|
||||||
changes its value in the UI. This can be used to change other input values based on some other input.
|
|
||||||
|
|
||||||
The new Map of propertyName -> value is returned.
|
|
||||||
|
|
||||||
In this example, the onDbThroughputChange function sets the collectionThroughput to the same value as the dbThroughput
|
|
||||||
when the slider in moved in the UI.
|
|
||||||
*/
|
|
||||||
@OnChange(onDbThroughputChange)
|
|
||||||
@Values({
|
|
||||||
label: "Database Throughput",
|
|
||||||
min: 400,
|
|
||||||
max: initializeMaxThroughput,
|
|
||||||
step: 100,
|
|
||||||
uiType: UiType.Slider
|
|
||||||
})
|
|
||||||
dbThroughput: number;
|
|
||||||
|
|
||||||
@Values({
|
|
||||||
label: "Collection Throughput",
|
|
||||||
min: 400,
|
|
||||||
max: initializeMaxThroughput,
|
|
||||||
step: 100,
|
|
||||||
uiType: UiType.Spinner
|
|
||||||
})
|
|
||||||
collectionThroughput: number;
|
|
||||||
}
|
|
||||||
@@ -1,99 +0,0 @@
|
|||||||
import { ChoiceItem, Info, InputType, UiType } from "../Explorer/Controls/SmartUi/SmartUiComponent";
|
|
||||||
import { addPropertyToMap, CommonInputTypes } from "./SelfServeUtils";
|
|
||||||
|
|
||||||
type ValueOf<T> = T[keyof T];
|
|
||||||
interface Decorator {
|
|
||||||
name: keyof CommonInputTypes;
|
|
||||||
value: ValueOf<CommonInputTypes>;
|
|
||||||
}
|
|
||||||
|
|
||||||
interface InputOptionsBase {
|
|
||||||
label: string;
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface NumberInputOptions extends InputOptionsBase {
|
|
||||||
min: (() => Promise<number>) | number;
|
|
||||||
max: (() => Promise<number>) | number;
|
|
||||||
step: (() => Promise<number>) | number;
|
|
||||||
uiType: UiType;
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface StringInputOptions extends InputOptionsBase {
|
|
||||||
placeholder?: (() => Promise<string>) | string;
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface BooleanInputOptions extends InputOptionsBase {
|
|
||||||
trueLabel: (() => Promise<string>) | string;
|
|
||||||
falseLabel: (() => Promise<string>) | string;
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface ChoiceInputOptions extends InputOptionsBase {
|
|
||||||
choices: (() => Promise<ChoiceItem[]>) | ChoiceItem[];
|
|
||||||
}
|
|
||||||
|
|
||||||
type InputOptions = NumberInputOptions | StringInputOptions | BooleanInputOptions | ChoiceInputOptions;
|
|
||||||
|
|
||||||
const isNumberInputOptions = (inputOptions: InputOptions): inputOptions is NumberInputOptions => {
|
|
||||||
return "min" in inputOptions;
|
|
||||||
};
|
|
||||||
|
|
||||||
const isBooleanInputOptions = (inputOptions: InputOptions): inputOptions is BooleanInputOptions => {
|
|
||||||
return "trueLabel" in inputOptions;
|
|
||||||
};
|
|
||||||
|
|
||||||
const isChoiceInputOptions = (inputOptions: InputOptions): inputOptions is ChoiceInputOptions => {
|
|
||||||
return "choices" in inputOptions;
|
|
||||||
};
|
|
||||||
|
|
||||||
const addToMap = (...decorators: Decorator[]): PropertyDecorator => {
|
|
||||||
return (target, property) => {
|
|
||||||
let className = target.constructor.name;
|
|
||||||
const propertyName = property.toString();
|
|
||||||
if (className === "Function") {
|
|
||||||
//eslint-disable-next-line @typescript-eslint/ban-types
|
|
||||||
className = (target as Function).name;
|
|
||||||
throw new Error(`Property '${propertyName}' in class '${className}'should be not be static.`);
|
|
||||||
}
|
|
||||||
|
|
||||||
const propertyType = (Reflect.getMetadata("design:type", target, property)?.name as string)?.toLowerCase();
|
|
||||||
addPropertyToMap(target, propertyName, className, "type", propertyType);
|
|
||||||
addPropertyToMap(target, propertyName, className, "dataFieldName", propertyName);
|
|
||||||
|
|
||||||
decorators.map(decorator => addPropertyToMap(target, propertyName, className, decorator.name, decorator.value));
|
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
||||||
export const OnChange = (
|
|
||||||
onChange: (currentState: Map<string, InputType>, newValue: InputType) => Map<string, InputType>
|
|
||||||
): PropertyDecorator => {
|
|
||||||
return addToMap({ name: "onChange", value: onChange });
|
|
||||||
};
|
|
||||||
|
|
||||||
export const PropertyInfo = (info: (() => Promise<Info>) | Info): PropertyDecorator => {
|
|
||||||
return addToMap({ name: "info", value: info });
|
|
||||||
};
|
|
||||||
|
|
||||||
export const Values = (inputOptions: InputOptions): PropertyDecorator => {
|
|
||||||
if (isNumberInputOptions(inputOptions)) {
|
|
||||||
return addToMap(
|
|
||||||
{ name: "label", value: inputOptions.label },
|
|
||||||
{ name: "min", value: inputOptions.min },
|
|
||||||
{ name: "max", value: inputOptions.max },
|
|
||||||
{ name: "step", value: inputOptions.step },
|
|
||||||
{ name: "uiType", value: inputOptions.uiType }
|
|
||||||
);
|
|
||||||
} else if (isBooleanInputOptions(inputOptions)) {
|
|
||||||
return addToMap(
|
|
||||||
{ name: "label", value: inputOptions.label },
|
|
||||||
{ name: "trueLabel", value: inputOptions.trueLabel },
|
|
||||||
{ name: "falseLabel", value: inputOptions.falseLabel }
|
|
||||||
);
|
|
||||||
} else if (isChoiceInputOptions(inputOptions)) {
|
|
||||||
return addToMap({ name: "label", value: inputOptions.label }, { name: "choices", value: inputOptions.choices });
|
|
||||||
} else {
|
|
||||||
return addToMap(
|
|
||||||
{ name: "label", value: inputOptions.label },
|
|
||||||
{ name: "placeholder", value: inputOptions.placeholder }
|
|
||||||
);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
@@ -1,104 +0,0 @@
|
|||||||
import React from "react";
|
|
||||||
import { shallow } from "enzyme";
|
|
||||||
import { SelfServeDescriptor, SelfServeComponent, SelfServeComponentState } from "./SelfServeComponent";
|
|
||||||
import { InputType, UiType } from "../Explorer/Controls/SmartUi/SmartUiComponent";
|
|
||||||
|
|
||||||
describe("SelfServeComponent", () => {
|
|
||||||
const defaultValues = new Map<string, InputType>([
|
|
||||||
["throughput", "450"],
|
|
||||||
["analyticalStore", "false"],
|
|
||||||
["database", "db2"]
|
|
||||||
]);
|
|
||||||
const initializeMock = jest.fn(async () => defaultValues);
|
|
||||||
const onSubmitMock = jest.fn(async () => {
|
|
||||||
return;
|
|
||||||
});
|
|
||||||
|
|
||||||
const exampleData: SelfServeDescriptor = {
|
|
||||||
initialize: initializeMock,
|
|
||||||
onSubmit: onSubmitMock,
|
|
||||||
inputNames: ["throughput", "containerId", "analyticalStore", "database"],
|
|
||||||
root: {
|
|
||||||
id: "root",
|
|
||||||
info: {
|
|
||||||
message: "Start at $24/mo per database",
|
|
||||||
link: {
|
|
||||||
href: "https://aka.ms/azure-cosmos-db-pricing",
|
|
||||||
text: "More Details"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
children: [
|
|
||||||
{
|
|
||||||
id: "throughput",
|
|
||||||
input: {
|
|
||||||
label: "Throughput (input)",
|
|
||||||
dataFieldName: "throughput",
|
|
||||||
type: "number",
|
|
||||||
min: 400,
|
|
||||||
max: 500,
|
|
||||||
step: 10,
|
|
||||||
defaultValue: 400,
|
|
||||||
uiType: UiType.Spinner
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
id: "containerId",
|
|
||||||
input: {
|
|
||||||
label: "Container id",
|
|
||||||
dataFieldName: "containerId",
|
|
||||||
type: "string"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
id: "analyticalStore",
|
|
||||||
input: {
|
|
||||||
label: "Analytical Store",
|
|
||||||
trueLabel: "Enabled",
|
|
||||||
falseLabel: "Disabled",
|
|
||||||
defaultValue: true,
|
|
||||||
dataFieldName: "analyticalStore",
|
|
||||||
type: "boolean"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
id: "database",
|
|
||||||
input: {
|
|
||||||
label: "Database",
|
|
||||||
dataFieldName: "database",
|
|
||||||
type: "object",
|
|
||||||
choices: [
|
|
||||||
{ label: "Database 1", key: "db1" },
|
|
||||||
{ label: "Database 2", key: "db2" },
|
|
||||||
{ label: "Database 3", key: "db3" }
|
|
||||||
],
|
|
||||||
defaultKey: "db2"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
const verifyDefaultsSet = (currentValues: Map<string, InputType>): void => {
|
|
||||||
for (const key of currentValues.keys()) {
|
|
||||||
if (defaultValues.has(key)) {
|
|
||||||
expect(defaultValues.get(key)).toEqual(currentValues.get(key));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
it("should render", async () => {
|
|
||||||
const wrapper = shallow(<SelfServeComponent descriptor={exampleData} />);
|
|
||||||
await new Promise(resolve => setTimeout(resolve, 0));
|
|
||||||
expect(wrapper).toMatchSnapshot();
|
|
||||||
|
|
||||||
// initialize() should be called and defaults should be set when component is mounted
|
|
||||||
expect(initializeMock).toHaveBeenCalled();
|
|
||||||
const state = wrapper.state() as SelfServeComponentState;
|
|
||||||
verifyDefaultsSet(state.currentValues);
|
|
||||||
|
|
||||||
// onSubmit() must be called when submit button is clicked
|
|
||||||
const submitButton = wrapper.find("#submitButton");
|
|
||||||
submitButton.simulate("click");
|
|
||||||
expect(onSubmitMock).toHaveBeenCalled();
|
|
||||||
});
|
|
||||||
});
|
|
||||||
@@ -1,218 +0,0 @@
|
|||||||
import React from "react";
|
|
||||||
import { IStackTokens, PrimaryButton, Spinner, SpinnerSize, Stack } from "office-ui-fabric-react";
|
|
||||||
import {
|
|
||||||
ChoiceItem,
|
|
||||||
InputType,
|
|
||||||
InputTypeValue,
|
|
||||||
SmartUiComponent,
|
|
||||||
UiType,
|
|
||||||
SmartUiDescriptor,
|
|
||||||
Info
|
|
||||||
} from "../Explorer/Controls/SmartUi/SmartUiComponent";
|
|
||||||
|
|
||||||
export interface BaseInput {
|
|
||||||
label: (() => Promise<string>) | string;
|
|
||||||
dataFieldName: string;
|
|
||||||
type: InputTypeValue;
|
|
||||||
onChange?: (currentState: Map<string, InputType>, newValue: InputType) => Map<string, InputType>;
|
|
||||||
placeholder?: (() => Promise<string>) | string;
|
|
||||||
errorMessage?: string;
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface NumberInput extends BaseInput {
|
|
||||||
min: (() => Promise<number>) | number;
|
|
||||||
max: (() => Promise<number>) | number;
|
|
||||||
step: (() => Promise<number>) | number;
|
|
||||||
defaultValue?: number;
|
|
||||||
uiType: UiType;
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface BooleanInput extends BaseInput {
|
|
||||||
trueLabel: (() => Promise<string>) | string;
|
|
||||||
falseLabel: (() => Promise<string>) | string;
|
|
||||||
defaultValue?: boolean;
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface StringInput extends BaseInput {
|
|
||||||
defaultValue?: string;
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface ChoiceInput extends BaseInput {
|
|
||||||
choices: (() => Promise<ChoiceItem[]>) | ChoiceItem[];
|
|
||||||
defaultKey?: string;
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface Node {
|
|
||||||
id: string;
|
|
||||||
info?: (() => Promise<Info>) | Info;
|
|
||||||
input?: AnyInput;
|
|
||||||
children?: Node[];
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface SelfServeDescriptor {
|
|
||||||
root: Node;
|
|
||||||
initialize?: () => Promise<Map<string, InputType>>;
|
|
||||||
onSubmit?: (currentValues: Map<string, InputType>) => Promise<void>;
|
|
||||||
inputNames?: string[];
|
|
||||||
}
|
|
||||||
|
|
||||||
export type AnyInput = NumberInput | BooleanInput | StringInput | ChoiceInput;
|
|
||||||
|
|
||||||
export interface SelfServeComponentProps {
|
|
||||||
descriptor: SelfServeDescriptor;
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface SelfServeComponentState {
|
|
||||||
root: SelfServeDescriptor;
|
|
||||||
currentValues: Map<string, InputType>;
|
|
||||||
baselineValues: Map<string, InputType>;
|
|
||||||
isRefreshing: boolean;
|
|
||||||
}
|
|
||||||
|
|
||||||
export class SelfServeComponent extends React.Component<SelfServeComponentProps, SelfServeComponentState> {
|
|
||||||
componentDidMount(): void {
|
|
||||||
this.initializeSmartUiComponent();
|
|
||||||
}
|
|
||||||
|
|
||||||
constructor(props: SelfServeComponentProps) {
|
|
||||||
super(props);
|
|
||||||
this.state = {
|
|
||||||
root: this.props.descriptor,
|
|
||||||
currentValues: new Map(),
|
|
||||||
baselineValues: new Map(),
|
|
||||||
isRefreshing: false
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
private initializeSmartUiComponent = async (): Promise<void> => {
|
|
||||||
this.setState({ isRefreshing: true });
|
|
||||||
await this.initializeSmartUiNode(this.props.descriptor.root);
|
|
||||||
await this.setDefaults();
|
|
||||||
this.setState({ isRefreshing: false });
|
|
||||||
};
|
|
||||||
|
|
||||||
private setDefaults = async (): Promise<void> => {
|
|
||||||
this.setState({ isRefreshing: true });
|
|
||||||
let { currentValues, baselineValues } = this.state;
|
|
||||||
|
|
||||||
const initialValues = await this.props.descriptor.initialize();
|
|
||||||
for (const key of initialValues.keys()) {
|
|
||||||
if (this.props.descriptor.inputNames.indexOf(key) === -1) {
|
|
||||||
this.setState({ isRefreshing: false });
|
|
||||||
throw new Error(`${key} is not an input property of this class.`);
|
|
||||||
}
|
|
||||||
|
|
||||||
currentValues = currentValues.set(key, initialValues.get(key));
|
|
||||||
baselineValues = baselineValues.set(key, initialValues.get(key));
|
|
||||||
}
|
|
||||||
this.setState({ currentValues, baselineValues, isRefreshing: false });
|
|
||||||
};
|
|
||||||
|
|
||||||
public discard = (): void => {
|
|
||||||
let { currentValues } = this.state;
|
|
||||||
const { baselineValues } = this.state;
|
|
||||||
for (const key of baselineValues.keys()) {
|
|
||||||
currentValues = currentValues.set(key, baselineValues.get(key));
|
|
||||||
}
|
|
||||||
this.setState({ currentValues });
|
|
||||||
};
|
|
||||||
|
|
||||||
private initializeSmartUiNode = async (currentNode: Node): Promise<void> => {
|
|
||||||
currentNode.info = await this.getResolvedValue(currentNode.info);
|
|
||||||
|
|
||||||
if (currentNode.input) {
|
|
||||||
currentNode.input = await this.getResolvedInput(currentNode.input);
|
|
||||||
}
|
|
||||||
|
|
||||||
const promises = currentNode.children?.map(async (child: Node) => await this.initializeSmartUiNode(child));
|
|
||||||
if (promises) {
|
|
||||||
await Promise.all(promises);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
private getResolvedInput = async (input: AnyInput): Promise<AnyInput> => {
|
|
||||||
input.label = await this.getResolvedValue(input.label);
|
|
||||||
input.placeholder = await this.getResolvedValue(input.placeholder);
|
|
||||||
|
|
||||||
switch (input.type) {
|
|
||||||
case "string": {
|
|
||||||
return input as StringInput;
|
|
||||||
}
|
|
||||||
case "number": {
|
|
||||||
const numberInput = input as NumberInput;
|
|
||||||
numberInput.min = await this.getResolvedValue(numberInput.min);
|
|
||||||
numberInput.max = await this.getResolvedValue(numberInput.max);
|
|
||||||
numberInput.step = await this.getResolvedValue(numberInput.step);
|
|
||||||
return numberInput;
|
|
||||||
}
|
|
||||||
case "boolean": {
|
|
||||||
const booleanInput = input as BooleanInput;
|
|
||||||
booleanInput.trueLabel = await this.getResolvedValue(booleanInput.trueLabel);
|
|
||||||
booleanInput.falseLabel = await this.getResolvedValue(booleanInput.falseLabel);
|
|
||||||
return booleanInput;
|
|
||||||
}
|
|
||||||
default: {
|
|
||||||
const choiceInput = input as ChoiceInput;
|
|
||||||
choiceInput.choices = await this.getResolvedValue(choiceInput.choices);
|
|
||||||
return choiceInput;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
public async getResolvedValue<T>(value: T | (() => Promise<T>)): Promise<T> {
|
|
||||||
if (value instanceof Function) {
|
|
||||||
return value();
|
|
||||||
}
|
|
||||||
return value;
|
|
||||||
}
|
|
||||||
|
|
||||||
private onInputChange = (input: AnyInput, newValue: InputType) => {
|
|
||||||
if (input.onChange) {
|
|
||||||
const newValues = input.onChange(this.state.currentValues, newValue);
|
|
||||||
this.setState({ currentValues: newValues });
|
|
||||||
} else {
|
|
||||||
const dataFieldName = input.dataFieldName;
|
|
||||||
const { currentValues } = this.state;
|
|
||||||
currentValues.set(dataFieldName, newValue);
|
|
||||||
this.setState({ currentValues });
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
public render(): JSX.Element {
|
|
||||||
const containerStackTokens: IStackTokens = { childrenGap: 20 };
|
|
||||||
return !this.state.isRefreshing ? (
|
|
||||||
<div style={{ overflowX: "auto" }}>
|
|
||||||
<Stack tokens={containerStackTokens} styles={{ root: { width: 400, padding: 10 } }}>
|
|
||||||
<SmartUiComponent
|
|
||||||
descriptor={this.state.root as SmartUiDescriptor}
|
|
||||||
currentValues={this.state.currentValues}
|
|
||||||
onInputChange={this.onInputChange}
|
|
||||||
/>
|
|
||||||
|
|
||||||
<Stack horizontal tokens={{ childrenGap: 10 }}>
|
|
||||||
<PrimaryButton
|
|
||||||
id="submitButton"
|
|
||||||
styles={{ root: { width: 100 } }}
|
|
||||||
text="submit"
|
|
||||||
onClick={async () => {
|
|
||||||
await this.props.descriptor.onSubmit(this.state.currentValues);
|
|
||||||
this.setDefaults();
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
<PrimaryButton
|
|
||||||
id="discardButton"
|
|
||||||
styles={{ root: { width: 100 } }}
|
|
||||||
text="discard"
|
|
||||||
onClick={() => this.discard()}
|
|
||||||
/>
|
|
||||||
</Stack>
|
|
||||||
</Stack>
|
|
||||||
</div>
|
|
||||||
) : (
|
|
||||||
<Spinner
|
|
||||||
size={SpinnerSize.large}
|
|
||||||
styles={{ root: { textAlign: "center", justifyContent: "center", width: "100%", height: "100%" } }}
|
|
||||||
/>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,51 +0,0 @@
|
|||||||
/**
|
|
||||||
* This adapter is responsible to render the React component
|
|
||||||
* If the component signals a change through the callback passed in the properties, it must render the React component when appropriate
|
|
||||||
* and update any knockout observables passed from the parent.
|
|
||||||
*/
|
|
||||||
import * as ko from "knockout";
|
|
||||||
import * as React from "react";
|
|
||||||
import { ReactAdapter } from "../Bindings/ReactBindingHandler";
|
|
||||||
import Explorer from "../Explorer/Explorer";
|
|
||||||
import { SelfServeDescriptor, SelfServeComponent } from "./SelfServeComponent";
|
|
||||||
import { SelfServeType } from "./SelfServeUtils";
|
|
||||||
|
|
||||||
export class SelfServeComponentAdapter implements ReactAdapter {
|
|
||||||
public parameters: ko.Observable<SelfServeDescriptor>;
|
|
||||||
public container: Explorer;
|
|
||||||
|
|
||||||
constructor(container: Explorer) {
|
|
||||||
this.container = container;
|
|
||||||
this.parameters = ko.observable(undefined);
|
|
||||||
this.container.selfServeType.subscribe(() => {
|
|
||||||
this.triggerRender();
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
public static getDescriptor = async (selfServeType: SelfServeType): Promise<SelfServeDescriptor> => {
|
|
||||||
switch (selfServeType) {
|
|
||||||
case SelfServeType.example: {
|
|
||||||
const SelfServeExample = await import(/* webpackChunkName: "SelfServeExample" */ "./Example/SelfServeExample");
|
|
||||||
return new SelfServeExample.default().toSelfServeDescriptor();
|
|
||||||
}
|
|
||||||
default:
|
|
||||||
return undefined;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
public renderComponent(): JSX.Element {
|
|
||||||
if (this.container.selfServeType() === SelfServeType.invalid) {
|
|
||||||
return <h1>Invalid self serve type!</h1>;
|
|
||||||
}
|
|
||||||
const smartUiDescriptor = this.parameters();
|
|
||||||
return smartUiDescriptor ? <SelfServeComponent descriptor={smartUiDescriptor} /> : <></>;
|
|
||||||
}
|
|
||||||
|
|
||||||
private triggerRender() {
|
|
||||||
window.requestAnimationFrame(async () => {
|
|
||||||
const selfServeType = this.container.selfServeType();
|
|
||||||
const smartUiDescriptor = await SelfServeComponentAdapter.getDescriptor(selfServeType);
|
|
||||||
this.parameters(smartUiDescriptor);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,25 +0,0 @@
|
|||||||
/**
|
|
||||||
* This adapter is responsible to render the React component
|
|
||||||
* If the component signals a change through the callback passed in the properties, it must render the React component when appropriate
|
|
||||||
* and update any knockout observables passed from the parent.
|
|
||||||
*/
|
|
||||||
import * as ko from "knockout";
|
|
||||||
import { Spinner, SpinnerSize } from "office-ui-fabric-react";
|
|
||||||
import * as React from "react";
|
|
||||||
import { ReactAdapter } from "../Bindings/ReactBindingHandler";
|
|
||||||
|
|
||||||
export class SelfServeLoadingComponentAdapter implements ReactAdapter {
|
|
||||||
public parameters: ko.Observable<number>;
|
|
||||||
|
|
||||||
constructor() {
|
|
||||||
this.parameters = ko.observable(Date.now());
|
|
||||||
}
|
|
||||||
|
|
||||||
public renderComponent(): JSX.Element {
|
|
||||||
return <Spinner size={SpinnerSize.large} />;
|
|
||||||
}
|
|
||||||
|
|
||||||
private triggerRender() {
|
|
||||||
window.requestAnimationFrame(() => this.renderComponent());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,277 +0,0 @@
|
|||||||
import {
|
|
||||||
CommonInputTypes,
|
|
||||||
mapToSmartUiDescriptor,
|
|
||||||
SelfServeBaseClass,
|
|
||||||
updateContextWithDecorator
|
|
||||||
} from "./SelfServeUtils";
|
|
||||||
import { InputType, UiType } from "./../Explorer/Controls/SmartUi/SmartUiComponent";
|
|
||||||
|
|
||||||
describe("SelfServeUtils", () => {
|
|
||||||
it("initialize should be declared for self serve classes", () => {
|
|
||||||
class Test extends SelfServeBaseClass {
|
|
||||||
public onSubmit = async (): Promise<void> => {
|
|
||||||
return;
|
|
||||||
};
|
|
||||||
public initialize: () => Promise<Map<string, InputType>>;
|
|
||||||
}
|
|
||||||
expect(() => new Test().toSelfServeDescriptor()).toThrow("initialize() was not declared for the class 'Test'");
|
|
||||||
});
|
|
||||||
|
|
||||||
it("onSubmit should be declared for self serve classes", () => {
|
|
||||||
class Test extends SelfServeBaseClass {
|
|
||||||
public onSubmit: () => Promise<void>;
|
|
||||||
public initialize = async (): Promise<Map<string, InputType>> => {
|
|
||||||
return undefined;
|
|
||||||
};
|
|
||||||
}
|
|
||||||
expect(() => new Test().toSelfServeDescriptor()).toThrow("onSubmit() was not declared for the class 'Test'");
|
|
||||||
});
|
|
||||||
|
|
||||||
it("@SmartUi decorator must be present for self serve classes", () => {
|
|
||||||
class Test extends SelfServeBaseClass {
|
|
||||||
public onSubmit = async (): Promise<void> => {
|
|
||||||
return;
|
|
||||||
};
|
|
||||||
public initialize = async (): Promise<Map<string, InputType>> => {
|
|
||||||
return undefined;
|
|
||||||
};
|
|
||||||
}
|
|
||||||
expect(() => new Test().toSelfServeDescriptor()).toThrow(
|
|
||||||
"@SmartUi decorator was not declared for the class 'Test'"
|
|
||||||
);
|
|
||||||
});
|
|
||||||
|
|
||||||
it("updateContextWithDecorator", () => {
|
|
||||||
const context = new Map<string, CommonInputTypes>();
|
|
||||||
updateContextWithDecorator(context, "dbThroughput", "testClass", "max", 1);
|
|
||||||
updateContextWithDecorator(context, "dbThroughput", "testClass", "min", 2);
|
|
||||||
updateContextWithDecorator(context, "collThroughput", "testClass", "max", 5);
|
|
||||||
expect(context.size).toEqual(2);
|
|
||||||
expect(context.get("dbThroughput")).toEqual({ id: "dbThroughput", max: 1, min: 2 });
|
|
||||||
expect(context.get("collThroughput")).toEqual({ id: "collThroughput", max: 5 });
|
|
||||||
});
|
|
||||||
|
|
||||||
it("mapToSmartUiDescriptor", () => {
|
|
||||||
const context: Map<string, CommonInputTypes> = new Map([
|
|
||||||
[
|
|
||||||
"dbThroughput",
|
|
||||||
{
|
|
||||||
id: "dbThroughput",
|
|
||||||
dataFieldName: "dbThroughput",
|
|
||||||
type: "number",
|
|
||||||
label: "Database Throughput",
|
|
||||||
min: 1,
|
|
||||||
max: 5,
|
|
||||||
step: 1,
|
|
||||||
uiType: UiType.Slider
|
|
||||||
}
|
|
||||||
],
|
|
||||||
[
|
|
||||||
"collThroughput",
|
|
||||||
{
|
|
||||||
id: "collThroughput",
|
|
||||||
dataFieldName: "collThroughput",
|
|
||||||
type: "number",
|
|
||||||
label: "Coll Throughput",
|
|
||||||
min: 1,
|
|
||||||
max: 5,
|
|
||||||
step: 1,
|
|
||||||
uiType: UiType.Spinner
|
|
||||||
}
|
|
||||||
],
|
|
||||||
[
|
|
||||||
"invalidThroughput",
|
|
||||||
{
|
|
||||||
id: "invalidThroughput",
|
|
||||||
dataFieldName: "invalidThroughput",
|
|
||||||
type: "boolean",
|
|
||||||
label: "Invalid Coll Throughput",
|
|
||||||
min: 1,
|
|
||||||
max: 5,
|
|
||||||
step: 1,
|
|
||||||
uiType: UiType.Spinner,
|
|
||||||
errorMessage: "label, truelabel and falselabel are required for boolean input"
|
|
||||||
}
|
|
||||||
],
|
|
||||||
[
|
|
||||||
"collName",
|
|
||||||
{
|
|
||||||
id: "collName",
|
|
||||||
dataFieldName: "collName",
|
|
||||||
type: "string",
|
|
||||||
label: "Coll Name",
|
|
||||||
placeholder: "placeholder text"
|
|
||||||
}
|
|
||||||
],
|
|
||||||
[
|
|
||||||
"enableLogging",
|
|
||||||
{
|
|
||||||
id: "enableLogging",
|
|
||||||
dataFieldName: "enableLogging",
|
|
||||||
type: "boolean",
|
|
||||||
label: "Enable Logging",
|
|
||||||
trueLabel: "Enable",
|
|
||||||
falseLabel: "Disable"
|
|
||||||
}
|
|
||||||
],
|
|
||||||
[
|
|
||||||
"invalidEnableLogging",
|
|
||||||
{
|
|
||||||
id: "invalidEnableLogging",
|
|
||||||
dataFieldName: "invalidEnableLogging",
|
|
||||||
type: "boolean",
|
|
||||||
label: "Invalid Enable Logging",
|
|
||||||
placeholder: "placeholder text"
|
|
||||||
}
|
|
||||||
],
|
|
||||||
[
|
|
||||||
"regions",
|
|
||||||
{
|
|
||||||
id: "regions",
|
|
||||||
dataFieldName: "regions",
|
|
||||||
type: "object",
|
|
||||||
label: "Regions",
|
|
||||||
choices: [
|
|
||||||
{ label: "South West US", key: "SWUS" },
|
|
||||||
{ label: "North Central US", key: "NCUS" },
|
|
||||||
{ label: "East US 2", key: "EUS2" }
|
|
||||||
]
|
|
||||||
}
|
|
||||||
],
|
|
||||||
[
|
|
||||||
"invalidRegions",
|
|
||||||
{
|
|
||||||
id: "invalidRegions",
|
|
||||||
dataFieldName: "invalidRegions",
|
|
||||||
type: "object",
|
|
||||||
label: "Invalid Regions",
|
|
||||||
placeholder: "placeholder text"
|
|
||||||
}
|
|
||||||
]
|
|
||||||
]);
|
|
||||||
const expectedDescriptor = {
|
|
||||||
root: {
|
|
||||||
id: "root",
|
|
||||||
children: [
|
|
||||||
{
|
|
||||||
id: "dbThroughput",
|
|
||||||
input: {
|
|
||||||
id: "dbThroughput",
|
|
||||||
dataFieldName: "dbThroughput",
|
|
||||||
type: "number",
|
|
||||||
label: "Database Throughput",
|
|
||||||
min: 1,
|
|
||||||
max: 5,
|
|
||||||
step: 1,
|
|
||||||
uiType: "Slider"
|
|
||||||
},
|
|
||||||
children: [] as Node[]
|
|
||||||
},
|
|
||||||
{
|
|
||||||
id: "collThroughput",
|
|
||||||
input: {
|
|
||||||
id: "collThroughput",
|
|
||||||
dataFieldName: "collThroughput",
|
|
||||||
type: "number",
|
|
||||||
label: "Coll Throughput",
|
|
||||||
min: 1,
|
|
||||||
max: 5,
|
|
||||||
step: 1,
|
|
||||||
uiType: "Spinner"
|
|
||||||
},
|
|
||||||
children: [] as Node[]
|
|
||||||
},
|
|
||||||
{
|
|
||||||
id: "invalidThroughput",
|
|
||||||
input: {
|
|
||||||
id: "invalidThroughput",
|
|
||||||
dataFieldName: "invalidThroughput",
|
|
||||||
type: "boolean",
|
|
||||||
label: "Invalid Coll Throughput",
|
|
||||||
min: 1,
|
|
||||||
max: 5,
|
|
||||||
step: 1,
|
|
||||||
uiType: "Spinner",
|
|
||||||
errorMessage: "label, truelabel and falselabel are required for boolean input 'invalidThroughput'."
|
|
||||||
},
|
|
||||||
children: [] as Node[]
|
|
||||||
},
|
|
||||||
{
|
|
||||||
id: "collName",
|
|
||||||
input: {
|
|
||||||
id: "collName",
|
|
||||||
dataFieldName: "collName",
|
|
||||||
type: "string",
|
|
||||||
label: "Coll Name",
|
|
||||||
placeholder: "placeholder text"
|
|
||||||
},
|
|
||||||
children: [] as Node[]
|
|
||||||
},
|
|
||||||
{
|
|
||||||
id: "enableLogging",
|
|
||||||
input: {
|
|
||||||
id: "enableLogging",
|
|
||||||
dataFieldName: "enableLogging",
|
|
||||||
type: "boolean",
|
|
||||||
label: "Enable Logging",
|
|
||||||
trueLabel: "Enable",
|
|
||||||
falseLabel: "Disable"
|
|
||||||
},
|
|
||||||
children: [] as Node[]
|
|
||||||
},
|
|
||||||
{
|
|
||||||
id: "invalidEnableLogging",
|
|
||||||
input: {
|
|
||||||
id: "invalidEnableLogging",
|
|
||||||
dataFieldName: "invalidEnableLogging",
|
|
||||||
type: "boolean",
|
|
||||||
label: "Invalid Enable Logging",
|
|
||||||
placeholder: "placeholder text",
|
|
||||||
errorMessage: "label, truelabel and falselabel are required for boolean input 'invalidEnableLogging'."
|
|
||||||
},
|
|
||||||
children: [] as Node[]
|
|
||||||
},
|
|
||||||
{
|
|
||||||
id: "regions",
|
|
||||||
input: {
|
|
||||||
id: "regions",
|
|
||||||
dataFieldName: "regions",
|
|
||||||
type: "object",
|
|
||||||
label: "Regions",
|
|
||||||
choices: [
|
|
||||||
{ label: "South West US", key: "SWUS" },
|
|
||||||
{ label: "North Central US", key: "NCUS" },
|
|
||||||
{ label: "East US 2", key: "EUS2" }
|
|
||||||
]
|
|
||||||
},
|
|
||||||
children: [] as Node[]
|
|
||||||
},
|
|
||||||
{
|
|
||||||
id: "invalidRegions",
|
|
||||||
input: {
|
|
||||||
id: "invalidRegions",
|
|
||||||
dataFieldName: "invalidRegions",
|
|
||||||
type: "object",
|
|
||||||
label: "Invalid Regions",
|
|
||||||
placeholder: "placeholder text",
|
|
||||||
errorMessage: "label and choices are required for Choice input 'invalidRegions'."
|
|
||||||
},
|
|
||||||
children: [] as Node[]
|
|
||||||
}
|
|
||||||
]
|
|
||||||
},
|
|
||||||
inputNames: [
|
|
||||||
"dbThroughput",
|
|
||||||
"collThroughput",
|
|
||||||
"invalidThroughput",
|
|
||||||
"collName",
|
|
||||||
"enableLogging",
|
|
||||||
"invalidEnableLogging",
|
|
||||||
"regions",
|
|
||||||
"invalidRegions"
|
|
||||||
]
|
|
||||||
};
|
|
||||||
const descriptor = mapToSmartUiDescriptor(context);
|
|
||||||
expect(descriptor).toEqual(expectedDescriptor);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
@@ -1,184 +0,0 @@
|
|||||||
import "reflect-metadata";
|
|
||||||
import { ChoiceItem, Info, InputTypeValue, InputType } from "../Explorer/Controls/SmartUi/SmartUiComponent";
|
|
||||||
import {
|
|
||||||
BooleanInput,
|
|
||||||
ChoiceInput,
|
|
||||||
SelfServeDescriptor,
|
|
||||||
NumberInput,
|
|
||||||
StringInput,
|
|
||||||
Node,
|
|
||||||
AnyInput
|
|
||||||
} from "./SelfServeComponent";
|
|
||||||
|
|
||||||
export enum SelfServeType {
|
|
||||||
// No self serve type passed, launch explorer
|
|
||||||
none = "none",
|
|
||||||
// Unsupported self serve type passed as feature flag
|
|
||||||
invalid = "invalid",
|
|
||||||
// Add your self serve types here
|
|
||||||
example = "example"
|
|
||||||
}
|
|
||||||
|
|
||||||
export abstract class SelfServeBaseClass {
|
|
||||||
public abstract onSubmit: (currentValues: Map<string, InputType>) => Promise<void>;
|
|
||||||
public abstract initialize: () => Promise<Map<string, InputType>>;
|
|
||||||
|
|
||||||
public toSelfServeDescriptor(): SelfServeDescriptor {
|
|
||||||
const className = this.constructor.name;
|
|
||||||
const smartUiDescriptor = Reflect.getMetadata(className, this) as SelfServeDescriptor;
|
|
||||||
|
|
||||||
if (!this.initialize) {
|
|
||||||
throw new Error(`initialize() was not declared for the class '${className}'`);
|
|
||||||
}
|
|
||||||
if (!this.onSubmit) {
|
|
||||||
throw new Error(`onSubmit() was not declared for the class '${className}'`);
|
|
||||||
}
|
|
||||||
if (!smartUiDescriptor?.root) {
|
|
||||||
throw new Error(`@SmartUi decorator was not declared for the class '${className}'`);
|
|
||||||
}
|
|
||||||
|
|
||||||
smartUiDescriptor.initialize = this.initialize;
|
|
||||||
smartUiDescriptor.onSubmit = this.onSubmit;
|
|
||||||
return smartUiDescriptor;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface CommonInputTypes {
|
|
||||||
id: string;
|
|
||||||
info?: (() => Promise<Info>) | Info;
|
|
||||||
type?: InputTypeValue;
|
|
||||||
label?: (() => Promise<string>) | string;
|
|
||||||
placeholder?: (() => Promise<string>) | string;
|
|
||||||
dataFieldName?: string;
|
|
||||||
min?: (() => Promise<number>) | number;
|
|
||||||
max?: (() => Promise<number>) | number;
|
|
||||||
step?: (() => Promise<number>) | number;
|
|
||||||
trueLabel?: (() => Promise<string>) | string;
|
|
||||||
falseLabel?: (() => Promise<string>) | string;
|
|
||||||
choices?: (() => Promise<ChoiceItem[]>) | ChoiceItem[];
|
|
||||||
uiType?: string;
|
|
||||||
errorMessage?: string;
|
|
||||||
onChange?: (currentState: Map<string, InputType>, newValue: InputType) => Map<string, InputType>;
|
|
||||||
onSubmit?: (currentValues: Map<string, InputType>) => Promise<void>;
|
|
||||||
initialize?: () => Promise<Map<string, InputType>>;
|
|
||||||
}
|
|
||||||
|
|
||||||
const setValue = <T extends keyof CommonInputTypes, K extends CommonInputTypes[T]>(
|
|
||||||
name: T,
|
|
||||||
value: K,
|
|
||||||
fieldObject: CommonInputTypes
|
|
||||||
): void => {
|
|
||||||
fieldObject[name] = value;
|
|
||||||
};
|
|
||||||
|
|
||||||
const getValue = <T extends keyof CommonInputTypes>(name: T, fieldObject: CommonInputTypes): unknown => {
|
|
||||||
return fieldObject[name];
|
|
||||||
};
|
|
||||||
|
|
||||||
export const addPropertyToMap = <T extends keyof CommonInputTypes, K extends CommonInputTypes[T]>(
|
|
||||||
target: unknown,
|
|
||||||
propertyName: string,
|
|
||||||
className: string,
|
|
||||||
descriptorName: keyof CommonInputTypes,
|
|
||||||
descriptorValue: K
|
|
||||||
): void => {
|
|
||||||
const context =
|
|
||||||
(Reflect.getMetadata(className, target) as Map<string, CommonInputTypes>) ?? new Map<string, CommonInputTypes>();
|
|
||||||
updateContextWithDecorator(context, propertyName, className, descriptorName, descriptorValue);
|
|
||||||
Reflect.defineMetadata(className, context, target);
|
|
||||||
};
|
|
||||||
|
|
||||||
export const updateContextWithDecorator = <T extends keyof CommonInputTypes, K extends CommonInputTypes[T]>(
|
|
||||||
context: Map<string, CommonInputTypes>,
|
|
||||||
propertyName: string,
|
|
||||||
className: string,
|
|
||||||
descriptorName: keyof CommonInputTypes,
|
|
||||||
descriptorValue: K
|
|
||||||
): void => {
|
|
||||||
if (!(context instanceof Map)) {
|
|
||||||
console.log(context);
|
|
||||||
throw new Error(`@SmartUi should be the first decorator for the class '${className}'.`);
|
|
||||||
}
|
|
||||||
|
|
||||||
const propertyObject = context.get(propertyName) ?? { id: propertyName };
|
|
||||||
|
|
||||||
if (getValue(descriptorName, propertyObject) && descriptorName !== "type" && descriptorName !== "dataFieldName") {
|
|
||||||
throw new Error(
|
|
||||||
`Duplicate value passed for '${descriptorName}' on property '${propertyName}' of class '${className}'`
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
setValue(descriptorName, descriptorValue, propertyObject);
|
|
||||||
context.set(propertyName, propertyObject);
|
|
||||||
};
|
|
||||||
|
|
||||||
export const buildSmartUiDescriptor = (className: string, target: unknown): void => {
|
|
||||||
const context = Reflect.getMetadata(className, target) as Map<string, CommonInputTypes>;
|
|
||||||
const smartUiDescriptor = mapToSmartUiDescriptor(context);
|
|
||||||
Reflect.defineMetadata(className, smartUiDescriptor, target);
|
|
||||||
};
|
|
||||||
|
|
||||||
export const mapToSmartUiDescriptor = (context: Map<string, CommonInputTypes>): SelfServeDescriptor => {
|
|
||||||
const root = context.get("root");
|
|
||||||
context.delete("root");
|
|
||||||
const inputNames: string[] = [];
|
|
||||||
|
|
||||||
const smartUiDescriptor: SelfServeDescriptor = {
|
|
||||||
root: {
|
|
||||||
id: "root",
|
|
||||||
info: root?.info,
|
|
||||||
children: []
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
while (context.size > 0) {
|
|
||||||
const key = context.keys().next().value;
|
|
||||||
addToDescriptor(context, smartUiDescriptor.root, key, inputNames);
|
|
||||||
}
|
|
||||||
smartUiDescriptor.inputNames = inputNames;
|
|
||||||
|
|
||||||
return smartUiDescriptor;
|
|
||||||
};
|
|
||||||
|
|
||||||
const addToDescriptor = (
|
|
||||||
context: Map<string, CommonInputTypes>,
|
|
||||||
root: Node,
|
|
||||||
key: string,
|
|
||||||
inputNames: string[]
|
|
||||||
): void => {
|
|
||||||
const value = context.get(key);
|
|
||||||
inputNames.push(value.id);
|
|
||||||
const element = {
|
|
||||||
id: value.id,
|
|
||||||
info: value.info,
|
|
||||||
input: getInput(value),
|
|
||||||
children: []
|
|
||||||
} as Node;
|
|
||||||
context.delete(key);
|
|
||||||
root.children.push(element);
|
|
||||||
};
|
|
||||||
|
|
||||||
const getInput = (value: CommonInputTypes): AnyInput => {
|
|
||||||
switch (value.type) {
|
|
||||||
case "number":
|
|
||||||
if (!value.label || !value.step || !value.uiType || !value.min || !value.max) {
|
|
||||||
value.errorMessage = `label, step, min, max and uiType are required for number input '${value.id}'.`;
|
|
||||||
}
|
|
||||||
return value as NumberInput;
|
|
||||||
case "string":
|
|
||||||
if (!value.label) {
|
|
||||||
value.errorMessage = `label is required for string input '${value.id}'.`;
|
|
||||||
}
|
|
||||||
return value as StringInput;
|
|
||||||
case "boolean":
|
|
||||||
if (!value.label || !value.trueLabel || !value.falseLabel) {
|
|
||||||
value.errorMessage = `label, truelabel and falselabel are required for boolean input '${value.id}'.`;
|
|
||||||
}
|
|
||||||
return value as BooleanInput;
|
|
||||||
default:
|
|
||||||
if (!value.label || !value.choices) {
|
|
||||||
value.errorMessage = `label and choices are required for Choice input '${value.id}'.`;
|
|
||||||
}
|
|
||||||
return value as ChoiceInput;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
@@ -1,168 +0,0 @@
|
|||||||
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
|
||||||
|
|
||||||
exports[`SelfServeComponent should render 1`] = `
|
|
||||||
<div
|
|
||||||
style={
|
|
||||||
Object {
|
|
||||||
"overflowX": "auto",
|
|
||||||
}
|
|
||||||
}
|
|
||||||
>
|
|
||||||
<Stack
|
|
||||||
styles={
|
|
||||||
Object {
|
|
||||||
"root": Object {
|
|
||||||
"padding": 10,
|
|
||||||
"width": 400,
|
|
||||||
},
|
|
||||||
}
|
|
||||||
}
|
|
||||||
tokens={
|
|
||||||
Object {
|
|
||||||
"childrenGap": 20,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
>
|
|
||||||
<SmartUiComponent
|
|
||||||
currentValues={
|
|
||||||
Map {
|
|
||||||
"throughput" => "450",
|
|
||||||
"analyticalStore" => "false",
|
|
||||||
"database" => "db2",
|
|
||||||
}
|
|
||||||
}
|
|
||||||
descriptor={
|
|
||||||
Object {
|
|
||||||
"initialize": [MockFunction] {
|
|
||||||
"calls": Array [
|
|
||||||
Array [],
|
|
||||||
],
|
|
||||||
"results": Array [
|
|
||||||
Object {
|
|
||||||
"type": "return",
|
|
||||||
"value": Promise {},
|
|
||||||
},
|
|
||||||
],
|
|
||||||
},
|
|
||||||
"inputNames": Array [
|
|
||||||
"throughput",
|
|
||||||
"containerId",
|
|
||||||
"analyticalStore",
|
|
||||||
"database",
|
|
||||||
],
|
|
||||||
"onSubmit": [MockFunction],
|
|
||||||
"root": Object {
|
|
||||||
"children": Array [
|
|
||||||
Object {
|
|
||||||
"id": "throughput",
|
|
||||||
"info": undefined,
|
|
||||||
"input": Object {
|
|
||||||
"dataFieldName": "throughput",
|
|
||||||
"defaultValue": 400,
|
|
||||||
"label": "Throughput (input)",
|
|
||||||
"max": 500,
|
|
||||||
"min": 400,
|
|
||||||
"placeholder": undefined,
|
|
||||||
"step": 10,
|
|
||||||
"type": "number",
|
|
||||||
"uiType": "Spinner",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
Object {
|
|
||||||
"id": "containerId",
|
|
||||||
"info": undefined,
|
|
||||||
"input": Object {
|
|
||||||
"dataFieldName": "containerId",
|
|
||||||
"label": "Container id",
|
|
||||||
"placeholder": undefined,
|
|
||||||
"type": "string",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
Object {
|
|
||||||
"id": "analyticalStore",
|
|
||||||
"info": undefined,
|
|
||||||
"input": Object {
|
|
||||||
"dataFieldName": "analyticalStore",
|
|
||||||
"defaultValue": true,
|
|
||||||
"falseLabel": "Disabled",
|
|
||||||
"label": "Analytical Store",
|
|
||||||
"placeholder": undefined,
|
|
||||||
"trueLabel": "Enabled",
|
|
||||||
"type": "boolean",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
Object {
|
|
||||||
"id": "database",
|
|
||||||
"info": undefined,
|
|
||||||
"input": Object {
|
|
||||||
"choices": Array [
|
|
||||||
Object {
|
|
||||||
"key": "db1",
|
|
||||||
"label": "Database 1",
|
|
||||||
},
|
|
||||||
Object {
|
|
||||||
"key": "db2",
|
|
||||||
"label": "Database 2",
|
|
||||||
},
|
|
||||||
Object {
|
|
||||||
"key": "db3",
|
|
||||||
"label": "Database 3",
|
|
||||||
},
|
|
||||||
],
|
|
||||||
"dataFieldName": "database",
|
|
||||||
"defaultKey": "db2",
|
|
||||||
"label": "Database",
|
|
||||||
"placeholder": undefined,
|
|
||||||
"type": "object",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
],
|
|
||||||
"id": "root",
|
|
||||||
"info": Object {
|
|
||||||
"link": Object {
|
|
||||||
"href": "https://aka.ms/azure-cosmos-db-pricing",
|
|
||||||
"text": "More Details",
|
|
||||||
},
|
|
||||||
"message": "Start at $24/mo per database",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
}
|
|
||||||
}
|
|
||||||
onInputChange={[Function]}
|
|
||||||
/>
|
|
||||||
<Stack
|
|
||||||
horizontal={true}
|
|
||||||
tokens={
|
|
||||||
Object {
|
|
||||||
"childrenGap": 10,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
>
|
|
||||||
<CustomizedPrimaryButton
|
|
||||||
id="submitButton"
|
|
||||||
onClick={[Function]}
|
|
||||||
styles={
|
|
||||||
Object {
|
|
||||||
"root": Object {
|
|
||||||
"width": 100,
|
|
||||||
},
|
|
||||||
}
|
|
||||||
}
|
|
||||||
text="submit"
|
|
||||||
/>
|
|
||||||
<CustomizedPrimaryButton
|
|
||||||
id="discardButton"
|
|
||||||
onClick={[Function]}
|
|
||||||
styles={
|
|
||||||
Object {
|
|
||||||
"root": Object {
|
|
||||||
"width": 100,
|
|
||||||
},
|
|
||||||
}
|
|
||||||
}
|
|
||||||
text="discard"
|
|
||||||
/>
|
|
||||||
</Stack>
|
|
||||||
</Stack>
|
|
||||||
</div>
|
|
||||||
`;
|
|
||||||
@@ -4,7 +4,7 @@ import * as DataModels from "../Contracts/DataModels";
|
|||||||
import { DefaultAccountExperienceType } from "../DefaultAccountExperienceType";
|
import { DefaultAccountExperienceType } from "../DefaultAccountExperienceType";
|
||||||
|
|
||||||
export class DefaultExperienceUtility {
|
export class DefaultExperienceUtility {
|
||||||
public static getDefaultExperienceFromDatabaseAccount(databaseAccount: DataModels.DatabaseAccount): string | null {
|
public static getDefaultExperienceFromDatabaseAccount(databaseAccount: DataModels.DatabaseAccount): string {
|
||||||
if (!databaseAccount) {
|
if (!databaseAccount) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
@@ -81,9 +81,11 @@ export class DefaultExperienceUtility {
|
|||||||
|
|
||||||
private static _getDefaultExperience(kind: string, capabilities: DataModels.Capability[]): string {
|
private static _getDefaultExperience(kind: string, capabilities: DataModels.Capability[]): string {
|
||||||
const defaultDefaultExperience: string = Constants.DefaultAccountExperience.DocumentDB;
|
const defaultDefaultExperience: string = Constants.DefaultAccountExperience.DocumentDB;
|
||||||
const defaultExperienceFromKind: string = DefaultExperienceUtility._getDefaultExperienceFromAccountKind(kind) || "";
|
const defaultExperienceFromKind: string = DefaultExperienceUtility._getDefaultExperienceFromAccountKind(kind);
|
||||||
const defaultExperienceFromCapabilities: string =
|
const defaultExperienceFromCapabilities: string = DefaultExperienceUtility._getDefaultExperienceFromAccountCapabilities(
|
||||||
DefaultExperienceUtility._getDefaultExperienceFromAccountCapabilities(capabilities) || "";
|
capabilities
|
||||||
|
);
|
||||||
|
|
||||||
if (!!defaultExperienceFromKind) {
|
if (!!defaultExperienceFromKind) {
|
||||||
return defaultExperienceFromKind;
|
return defaultExperienceFromKind;
|
||||||
}
|
}
|
||||||
@@ -95,7 +97,7 @@ export class DefaultExperienceUtility {
|
|||||||
return defaultDefaultExperience;
|
return defaultDefaultExperience;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static _getDefaultExperienceFromAccountKind(kind: string): string | null {
|
private static _getDefaultExperienceFromAccountKind(kind: string): string {
|
||||||
if (!kind) {
|
if (!kind) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
@@ -111,7 +113,7 @@ export class DefaultExperienceUtility {
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static _getDefaultExperienceFromAccountCapabilities(capabilities: DataModels.Capability[]): string | null {
|
private static _getDefaultExperienceFromAccountCapabilities(capabilities: DataModels.Capability[]): string {
|
||||||
if (!capabilities) {
|
if (!capabilities) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -22,7 +22,6 @@ export enum Action {
|
|||||||
MongoShell,
|
MongoShell,
|
||||||
ContextualPane,
|
ContextualPane,
|
||||||
ScaleThroughput,
|
ScaleThroughput,
|
||||||
ToggleAutoscaleSetting,
|
|
||||||
SelectItem,
|
SelectItem,
|
||||||
Tab,
|
Tab,
|
||||||
UpdateDocument,
|
UpdateDocument,
|
||||||
@@ -105,9 +104,7 @@ export const ActionModifiers = {
|
|||||||
Submit: "submit",
|
Submit: "submit",
|
||||||
IndexAll: "index all properties",
|
IndexAll: "index all properties",
|
||||||
NoIndex: "no indexing",
|
NoIndex: "no indexing",
|
||||||
Cancel: "cancel",
|
Cancel: "cancel"
|
||||||
ToggleAutoscaleOn: "autoscale on",
|
|
||||||
ToggleAutoscaleOff: "autoscale off"
|
|
||||||
} as const;
|
} as const;
|
||||||
|
|
||||||
export enum SourceBlade {
|
export enum SourceBlade {
|
||||||
|
|||||||
@@ -19,8 +19,8 @@ const getUrlVars = (): { [key: string]: string } => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
const createServerSettings = (urlVars: { [key: string]: string }): ServerConnection.ISettings => {
|
const createServerSettings = (urlVars: { [key: string]: string }): ServerConnection.ISettings => {
|
||||||
let body: BodyInit | undefined;
|
let body: BodyInit;
|
||||||
let headers: HeadersInit | undefined;
|
let headers: HeadersInit;
|
||||||
if (urlVars.hasOwnProperty(TerminalQueryParams.TerminalEndpoint)) {
|
if (urlVars.hasOwnProperty(TerminalQueryParams.TerminalEndpoint)) {
|
||||||
body = JSON.stringify({
|
body = JSON.stringify({
|
||||||
endpoint: urlVars[TerminalQueryParams.TerminalEndpoint]
|
endpoint: urlVars[TerminalQueryParams.TerminalEndpoint]
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
import * as Constants from "../Common/Constants";
|
import * as Constants from "../Common/Constants";
|
||||||
|
import { AutoPilotOfferSettings, Offer } from "../Contracts/DataModels";
|
||||||
|
|
||||||
export const manualToAutoscaleDisclaimer = `The starting autoscale max RU/s will be determined by the system, based on the current manual throughput settings and storage of your resource. After autoscale has been enabled, you can change the max RU/s. <a href="${Constants.Urls.autoscaleMigration}">Learn more</a>.`;
|
export const manualToAutoscaleDisclaimer = `The starting autoscale max RU/s will be determined by the system, based on the current manual throughput settings and storage of your resource. After autoscale has been enabled, you can change the max RU/s. <a href="${Constants.Urls.autoscaleMigration}">Learn more</a>.`;
|
||||||
|
|
||||||
|
|||||||
@@ -21,7 +21,7 @@ export function logConsoleMessage(type: ConsoleDataType, message: string, id?: s
|
|||||||
}
|
}
|
||||||
dataExplorer.logConsoleData({ type: type, date: formattedDate, message: message, id: id });
|
dataExplorer.logConsoleData({ type: type, date: formattedDate, message: message, id: id });
|
||||||
}
|
}
|
||||||
return id || "";
|
return id;
|
||||||
}
|
}
|
||||||
|
|
||||||
export function clearInProgressMessageWithId(id: string): void {
|
export function clearInProgressMessageWithId(id: string): void {
|
||||||
|
|||||||
@@ -1,6 +1,5 @@
|
|||||||
import * as AutoPilotUtils from "../Utils/AutoPilotUtils";
|
import * as AutoPilotUtils from "../Utils/AutoPilotUtils";
|
||||||
import * as Constants from "../Shared/Constants";
|
import * as Constants from "../Shared/Constants";
|
||||||
import { DefaultAccountExperienceType } from "../DefaultAccountExperienceType";
|
|
||||||
|
|
||||||
interface ComputeRUUsagePriceHourlyArgs {
|
interface ComputeRUUsagePriceHourlyArgs {
|
||||||
serverId: string;
|
serverId: string;
|
||||||
@@ -257,19 +256,9 @@ export function getEstimatedSpendAcknowledgeString(
|
|||||||
)} - ${currencySign}${calculateEstimateNumber(monthlyPrice)} monthly cost for the throughput above.`;
|
)} - ${currencySign}${calculateEstimateNumber(monthlyPrice)} monthly cost for the throughput above.`;
|
||||||
}
|
}
|
||||||
|
|
||||||
export function getUpsellMessage(
|
export function getUpsellMessage(serverId = "default", isFreeTier = false): string {
|
||||||
serverId = "default",
|
|
||||||
isFreeTier = false,
|
|
||||||
isFirstResourceCreated = false,
|
|
||||||
defaultExperience: string,
|
|
||||||
isCollection: boolean
|
|
||||||
): string {
|
|
||||||
if (isFreeTier) {
|
if (isFreeTier) {
|
||||||
const collectionName = getCollectionName(defaultExperience);
|
return "With free tier discount, you'll get the first 400 RU/s and 5 GB of storage in this account for free. Charges will apply if your resource throughput exceeds 400 RU/s.";
|
||||||
const resourceType = isCollection ? collectionName : "database";
|
|
||||||
return isFirstResourceCreated
|
|
||||||
? `The free tier discount of 400 RU/s has already been applied to a database or ${collectionName} in this account. Billing will apply to this ${resourceType} after it is created.`
|
|
||||||
: `With free tier, you'll get the first 400 RU/s and 5 GB of storage in this account for free. Billing will apply if you provision more than 400 RU/s of manual throughput, or if the ${resourceType} scales beyond 400 RU/s with autoscale.`;
|
|
||||||
} else {
|
} else {
|
||||||
let price: number = Constants.OfferPricing.MonthlyPricing.default.Standard.StartingPrice;
|
let price: number = Constants.OfferPricing.MonthlyPricing.default.Standard.StartingPrice;
|
||||||
|
|
||||||
@@ -280,19 +269,3 @@ export function getUpsellMessage(
|
|||||||
return `Start at ${getCurrencySign(serverId)}${price}/mo per database, multiple containers included`;
|
return `Start at ${getCurrencySign(serverId)}${price}/mo per database, multiple containers included`;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function getCollectionName(defaultExperience: string): string {
|
|
||||||
switch (defaultExperience) {
|
|
||||||
case DefaultAccountExperienceType.DocumentDB:
|
|
||||||
return "container";
|
|
||||||
case DefaultAccountExperienceType.MongoDB:
|
|
||||||
return "collection";
|
|
||||||
case DefaultAccountExperienceType.Table:
|
|
||||||
case DefaultAccountExperienceType.Cassandra:
|
|
||||||
return "table";
|
|
||||||
case DefaultAccountExperienceType.Graph:
|
|
||||||
return "graph";
|
|
||||||
default:
|
|
||||||
throw Error("unknown API type");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -1,7 +1,5 @@
|
|||||||
import { armRequest } from "./request";
|
import { armRequest } from "./request";
|
||||||
import fetch from "node-fetch";
|
import fetch from "node-fetch";
|
||||||
import { updateUserContext } from "../../UserContext";
|
|
||||||
import { AuthType } from "../../AuthType";
|
|
||||||
|
|
||||||
interface Global {
|
interface Global {
|
||||||
Headers: unknown;
|
Headers: unknown;
|
||||||
@@ -10,11 +8,6 @@ interface Global {
|
|||||||
((global as unknown) as Global).Headers = ((fetch as unknown) as Global).Headers;
|
((global as unknown) as Global).Headers = ((fetch as unknown) as Global).Headers;
|
||||||
|
|
||||||
describe("ARM request", () => {
|
describe("ARM request", () => {
|
||||||
window.authType = AuthType.AAD;
|
|
||||||
updateUserContext({
|
|
||||||
authorizationToken: "some-token"
|
|
||||||
});
|
|
||||||
|
|
||||||
it("should call window.fetch", async () => {
|
it("should call window.fetch", async () => {
|
||||||
window.fetch = jest.fn().mockResolvedValue({
|
window.fetch = jest.fn().mockResolvedValue({
|
||||||
ok: true,
|
ok: true,
|
||||||
@@ -55,24 +48,4 @@ describe("ARM request", () => {
|
|||||||
).rejects.toThrow();
|
).rejects.toThrow();
|
||||||
expect(window.fetch).toHaveBeenCalledTimes(2);
|
expect(window.fetch).toHaveBeenCalledTimes(2);
|
||||||
});
|
});
|
||||||
|
|
||||||
it("should throw token error", async () => {
|
|
||||||
window.authType = AuthType.AAD;
|
|
||||||
updateUserContext({
|
|
||||||
authorizationToken: undefined
|
|
||||||
});
|
|
||||||
const headers = new Headers();
|
|
||||||
headers.set("location", "https://foo.com/operationStatus");
|
|
||||||
window.fetch = jest.fn().mockResolvedValue({
|
|
||||||
ok: true,
|
|
||||||
headers,
|
|
||||||
status: 200,
|
|
||||||
json: async () => {
|
|
||||||
return { status: "Failed" };
|
|
||||||
}
|
|
||||||
});
|
|
||||||
await expect(() =>
|
|
||||||
armRequest({ apiVersion: "2001-01-01", host: "https://foo.com", path: "foo", method: "GET" })
|
|
||||||
).rejects.toThrow("No authority token provided");
|
|
||||||
});
|
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -30,7 +30,7 @@ export class ARMError extends Error {
|
|||||||
Object.setPrototypeOf(this, ARMError.prototype);
|
Object.setPrototypeOf(this, ARMError.prototype);
|
||||||
}
|
}
|
||||||
|
|
||||||
public code?: string | number;
|
public code: string | number;
|
||||||
}
|
}
|
||||||
|
|
||||||
interface ARMQueryParams {
|
interface ARMQueryParams {
|
||||||
@@ -63,10 +63,6 @@ export async function armRequest<T>({
|
|||||||
queryParams.metricNames && url.searchParams.append("metricnames", queryParams.metricNames);
|
queryParams.metricNames && url.searchParams.append("metricnames", queryParams.metricNames);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!userContext.authorizationToken) {
|
|
||||||
throw new Error("No authority token provided");
|
|
||||||
}
|
|
||||||
|
|
||||||
const response = await window.fetch(url.href, {
|
const response = await window.fetch(url.href, {
|
||||||
method,
|
method,
|
||||||
headers: {
|
headers: {
|
||||||
@@ -102,10 +98,6 @@ export async function armRequest<T>({
|
|||||||
}
|
}
|
||||||
|
|
||||||
async function getOperationStatus(operationStatusUrl: string) {
|
async function getOperationStatus(operationStatusUrl: string) {
|
||||||
if (!userContext.authorizationToken) {
|
|
||||||
throw new Error("No authority token provided");
|
|
||||||
}
|
|
||||||
|
|
||||||
const response = await window.fetch(operationStatusUrl, {
|
const response = await window.fetch(operationStatusUrl, {
|
||||||
headers: {
|
headers: {
|
||||||
Authorization: userContext.authorizationToken
|
Authorization: userContext.authorizationToken
|
||||||
|
|||||||
74
src/hooks/useGraphProfile.tsx
Normal file
74
src/hooks/useGraphProfile.tsx
Normal file
@@ -0,0 +1,74 @@
|
|||||||
|
import { useAccount, useMsal } from "@azure/msal-react";
|
||||||
|
import { useEffect, useState } from "react";
|
||||||
|
|
||||||
|
export async function fetchMe(accessToken: string): Promise<GraphMeResponse> {
|
||||||
|
const headers = new Headers();
|
||||||
|
const bearer = `Bearer ${accessToken}`;
|
||||||
|
|
||||||
|
headers.append("Authorization", bearer);
|
||||||
|
|
||||||
|
const options = {
|
||||||
|
method: "GET",
|
||||||
|
headers: headers
|
||||||
|
};
|
||||||
|
|
||||||
|
console.log("EXECUTING REQUEST");
|
||||||
|
return fetch("https://graph.microsoft.com/v1.0/me", options)
|
||||||
|
.then(response => response.json())
|
||||||
|
.catch(error => console.log(error));
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function fetchPhoto(accessToken: string): Promise<Blob | void> {
|
||||||
|
const headers = new Headers();
|
||||||
|
const bearer = `Bearer ${accessToken}`;
|
||||||
|
|
||||||
|
headers.append("Authorization", bearer);
|
||||||
|
headers.append("Content-Type", "image/jpg");
|
||||||
|
|
||||||
|
const options = {
|
||||||
|
method: "GET",
|
||||||
|
headers: headers
|
||||||
|
};
|
||||||
|
|
||||||
|
console.log("EXECUTING REQUEST");
|
||||||
|
return fetch("https://graph.microsoft.com/v1.0/me/photo/$value", options)
|
||||||
|
.then(response => response.blob())
|
||||||
|
.catch(error => console.log(error));
|
||||||
|
}
|
||||||
|
|
||||||
|
interface GraphMeResponse {
|
||||||
|
businessPhones: any[];
|
||||||
|
displayName: string;
|
||||||
|
givenName: string;
|
||||||
|
jobTitle: string;
|
||||||
|
mail: string;
|
||||||
|
mobilePhone: null;
|
||||||
|
officeLocation: string;
|
||||||
|
preferredLanguage: null;
|
||||||
|
surname: string;
|
||||||
|
userPrincipalName: string;
|
||||||
|
id: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function useGraphProfile(): { graphData: GraphMeResponse; photo: string } {
|
||||||
|
const { instance, accounts } = useMsal();
|
||||||
|
const account = useAccount(accounts[0] || {});
|
||||||
|
const [graphData, setGraphData] = useState<GraphMeResponse>();
|
||||||
|
const [photo, setPhoto] = useState<string>();
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
console.log("account", account);
|
||||||
|
if (account) {
|
||||||
|
instance
|
||||||
|
.acquireTokenSilent({
|
||||||
|
scopes: ["User.Read"],
|
||||||
|
account
|
||||||
|
})
|
||||||
|
.then(response => {
|
||||||
|
fetchMe(response.accessToken).then(response => setGraphData(response));
|
||||||
|
fetchPhoto(response.accessToken).then(response => setPhoto(URL.createObjectURL(response)));
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}, [account]);
|
||||||
|
return { graphData, photo };
|
||||||
|
}
|
||||||
@@ -7,62 +7,5 @@
|
|||||||
</head>
|
</head>
|
||||||
|
|
||||||
<body>
|
<body>
|
||||||
<a class="skip-link" href="#data-explorer-content">Skip to content</a>
|
|
||||||
<header>
|
|
||||||
<div class="items" role="menubar">
|
|
||||||
<div class="cosmosDBTitle">
|
|
||||||
<span
|
|
||||||
class="title"
|
|
||||||
data-bind="click: openAzurePortal, event: { keypress: onOpenAzurePortalKeyPress }"
|
|
||||||
tabindex="0"
|
|
||||||
title="Go to Azure Portal"
|
|
||||||
>Microsoft Azure</span
|
|
||||||
>
|
|
||||||
<span class="accontSplitter"></span> <span class="serviceTitle">Cosmos DB</span>
|
|
||||||
<img
|
|
||||||
class="chevronRight"
|
|
||||||
src="/chevron-right.svg"
|
|
||||||
alt="account separator"
|
|
||||||
data-bind="visible: isAccountActive"
|
|
||||||
/>
|
|
||||||
<span
|
|
||||||
class="accountSwitchComponentContainer"
|
|
||||||
data-bind="react: accountSwitchComponentAdapter, visible: isAccountActive"
|
|
||||||
></span>
|
|
||||||
</div>
|
|
||||||
<div class="feedbackConnectSettingIcons" data-bind="react: controlBarComponentAdapter"></div>
|
|
||||||
<div class="meControl" data-bind="react: meControlComponentAdapter"></div>
|
|
||||||
</div>
|
|
||||||
</header>
|
|
||||||
|
|
||||||
<!-- TODO display after introducing multiple account access -->
|
|
||||||
<nav class="fixedleftpane" style="display: none;">
|
|
||||||
<div class="fixedLeftPaneIcons"><img src="/Hamburger.svg" alt="Expand" /></div>
|
|
||||||
<div class="fixedLeftPaneIcons"><img src="/Connect.svg" alt="Connect to an account" /></div>
|
|
||||||
<div
|
|
||||||
class="fixedLeftPaneIcons"
|
|
||||||
data-bind="click: explorer_click, css:{ topSelected: navigationSelection() === 'explorer' }"
|
|
||||||
>
|
|
||||||
<img src="/HostedExplorer.svg" alt="Open Data Explorer" />
|
|
||||||
</div>
|
|
||||||
</nav>
|
|
||||||
|
|
||||||
<switch-directory-pane params="{data: switchDirectoryPane}"></switch-directory-pane>
|
|
||||||
|
|
||||||
<div id="data-explorer-content">
|
|
||||||
<!-- TODO generate version number dynamically -->
|
|
||||||
<iframe
|
|
||||||
id="explorerMenu"
|
|
||||||
name="explorer"
|
|
||||||
class="iframe"
|
|
||||||
title="explorer"
|
|
||||||
src="explorer.html?v=1.0.1&platform=Hosted"
|
|
||||||
data-bind="visible: navigationSelection() === 'explorer'"
|
|
||||||
>
|
|
||||||
</iframe>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div data-bind="react: firewallWarningComponentAdapter"></div>
|
|
||||||
<div data-bind="react: dialogComponentAdapter"></div>
|
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
|
|||||||
@@ -1,9 +1,59 @@
|
|||||||
import { ElementHandle, Frame } from "puppeteer";
|
import { ElementHandle, Frame } from "puppeteer";
|
||||||
|
import { TestExplorerParams } from "./testExplorer/TestExplorerParams";
|
||||||
import * as path from "path";
|
import * as path from "path";
|
||||||
|
|
||||||
export const NOTEBOOK_OPERATION_DELAY = 5000;
|
export const NOTEBOOK_OPERATION_DELAY = 5000;
|
||||||
export const RENDER_DELAY = 2500;
|
export const RENDER_DELAY = 2500;
|
||||||
|
|
||||||
|
let testExplorerFrame: Frame;
|
||||||
|
export const getTestExplorerFrame = async (): Promise<Frame> => {
|
||||||
|
if (testExplorerFrame) {
|
||||||
|
return testExplorerFrame;
|
||||||
|
}
|
||||||
|
|
||||||
|
const notebooksTestRunnerTenantId = process.env.NOTEBOOKS_TEST_RUNNER_TENANT_ID;
|
||||||
|
const notebooksTestRunnerClientId = process.env.NOTEBOOKS_TEST_RUNNER_CLIENT_ID;
|
||||||
|
const notebooksTestRunnerClientSecret = process.env.NOTEBOOKS_TEST_RUNNER_CLIENT_SECRET;
|
||||||
|
const portalRunnerDatabaseAccount = process.env.PORTAL_RUNNER_DATABASE_ACCOUNT;
|
||||||
|
const portalRunnerDatabaseAccountKey = process.env.PORTAL_RUNNER_DATABASE_ACCOUNT_KEY;
|
||||||
|
const portalRunnerSubscripton = process.env.PORTAL_RUNNER_SUBSCRIPTION;
|
||||||
|
const portalRunnerResourceGroup = process.env.PORTAL_RUNNER_RESOURCE_GROUP;
|
||||||
|
|
||||||
|
const testExplorerUrl = new URL("testExplorer.html", "https://localhost:1234");
|
||||||
|
testExplorerUrl.searchParams.append(
|
||||||
|
TestExplorerParams.notebooksTestRunnerTenantId,
|
||||||
|
encodeURI(notebooksTestRunnerTenantId)
|
||||||
|
);
|
||||||
|
testExplorerUrl.searchParams.append(
|
||||||
|
TestExplorerParams.notebooksTestRunnerClientId,
|
||||||
|
encodeURI(notebooksTestRunnerClientId)
|
||||||
|
);
|
||||||
|
testExplorerUrl.searchParams.append(
|
||||||
|
TestExplorerParams.notebooksTestRunnerClientSecret,
|
||||||
|
encodeURI(notebooksTestRunnerClientSecret)
|
||||||
|
);
|
||||||
|
testExplorerUrl.searchParams.append(
|
||||||
|
TestExplorerParams.portalRunnerDatabaseAccount,
|
||||||
|
encodeURI(portalRunnerDatabaseAccount)
|
||||||
|
);
|
||||||
|
testExplorerUrl.searchParams.append(
|
||||||
|
TestExplorerParams.portalRunnerDatabaseAccountKey,
|
||||||
|
encodeURI(portalRunnerDatabaseAccountKey)
|
||||||
|
);
|
||||||
|
testExplorerUrl.searchParams.append(TestExplorerParams.portalRunnerSubscripton, encodeURI(portalRunnerSubscripton));
|
||||||
|
testExplorerUrl.searchParams.append(
|
||||||
|
TestExplorerParams.portalRunnerResourceGroup,
|
||||||
|
encodeURI(portalRunnerResourceGroup)
|
||||||
|
);
|
||||||
|
|
||||||
|
await page.goto(testExplorerUrl.toString());
|
||||||
|
|
||||||
|
const handle = await page.waitForSelector("iframe");
|
||||||
|
testExplorerFrame = await handle.contentFrame();
|
||||||
|
await testExplorerFrame.waitForSelector(".galleryHeader");
|
||||||
|
return testExplorerFrame;
|
||||||
|
};
|
||||||
|
|
||||||
export const uploadNotebookIfNotExist = async (frame: Frame, notebookName: string): Promise<ElementHandle<Element>> => {
|
export const uploadNotebookIfNotExist = async (frame: Frame, notebookName: string): Promise<ElementHandle<Element>> => {
|
||||||
const notebookNode = await getNotebookNode(frame, notebookName);
|
const notebookNode = await getNotebookNode(frame, notebookName);
|
||||||
if (notebookNode) {
|
if (notebookNode) {
|
||||||
|
|||||||
@@ -1,11 +1,11 @@
|
|||||||
import { MessageTypes } from "../../src/Contracts/ExplorerContracts";
|
import { MessageTypes } from "../../../src/Contracts/ExplorerContracts";
|
||||||
import "../../less/hostedexplorer.less";
|
import "../../../less/hostedexplorer.less";
|
||||||
import { TestExplorerParams } from "./TestExplorerParams";
|
import { TestExplorerParams } from "./TestExplorerParams";
|
||||||
import { ClientSecretCredential } from "@azure/identity";
|
import { ClientSecretCredential } from "@azure/identity";
|
||||||
import { DatabaseAccountsGetResponse } from "@azure/arm-cosmosdb/esm/models";
|
import { DatabaseAccountsGetResponse } from "@azure/arm-cosmosdb/esm/models";
|
||||||
import { CosmosDBManagementClient } from "@azure/arm-cosmosdb";
|
import { CosmosDBManagementClient } from "@azure/arm-cosmosdb";
|
||||||
import * as msRest from "@azure/ms-rest-js";
|
import * as msRest from "@azure/ms-rest-js";
|
||||||
import * as ViewModels from "../../src/Contracts/ViewModels";
|
import * as ViewModels from "../../../src/Contracts/ViewModels";
|
||||||
|
|
||||||
class CustomSigner implements msRest.ServiceClientCredentials {
|
class CustomSigner implements msRest.ServiceClientCredentials {
|
||||||
private token: string;
|
private token: string;
|
||||||
@@ -87,7 +87,6 @@ const initTestExplorer = async (): Promise<void> => {
|
|||||||
const portalRunnerResourceGroup = decodeURIComponent(
|
const portalRunnerResourceGroup = decodeURIComponent(
|
||||||
urlSearchParams.get(TestExplorerParams.portalRunnerResourceGroup)
|
urlSearchParams.get(TestExplorerParams.portalRunnerResourceGroup)
|
||||||
);
|
);
|
||||||
const selfServeType = urlSearchParams.get(TestExplorerParams.selfServeType);
|
|
||||||
|
|
||||||
const token = await AADLogin(
|
const token = await AADLogin(
|
||||||
notebooksTestRunnerTenantId,
|
notebooksTestRunnerTenantId,
|
||||||
@@ -129,8 +128,7 @@ const initTestExplorer = async (): Promise<void> => {
|
|||||||
throughput: { fixed: 400, unlimited: 400, unlimitedmax: 100000, unlimitedmin: 400, shared: 400 }
|
throughput: { fixed: 400, unlimited: 400, unlimitedmax: 100000, unlimitedmin: 400, shared: 400 }
|
||||||
},
|
},
|
||||||
// add UI test only when feature is not dependent on flights anymore
|
// add UI test only when feature is not dependent on flights anymore
|
||||||
flights: [],
|
flights: []
|
||||||
selfServeType: selfServeType
|
|
||||||
} as ViewModels.DataExplorerInputsFrame
|
} as ViewModels.DataExplorerInputsFrame
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -5,6 +5,5 @@ export enum TestExplorerParams {
|
|||||||
portalRunnerDatabaseAccount = "portalRunnerDatabaseAccount",
|
portalRunnerDatabaseAccount = "portalRunnerDatabaseAccount",
|
||||||
portalRunnerDatabaseAccountKey = "portalRunnerDatabaseAccountKey",
|
portalRunnerDatabaseAccountKey = "portalRunnerDatabaseAccountKey",
|
||||||
portalRunnerSubscripton = "portalRunnerSubscripton",
|
portalRunnerSubscripton = "portalRunnerSubscripton",
|
||||||
portalRunnerResourceGroup = "portalRunnerResourceGroup",
|
portalRunnerResourceGroup = "portalRunnerResourceGroup"
|
||||||
selfServeType = "selfServeType"
|
|
||||||
}
|
}
|
||||||
@@ -1,6 +1,6 @@
|
|||||||
import { uploadNotebookIfNotExist } from "./notebookTestUtils";
|
import "expect-puppeteer";
|
||||||
|
import { getTestExplorerFrame, uploadNotebookIfNotExist } from "./notebookTestUtils";
|
||||||
import { ElementHandle, Frame } from "puppeteer";
|
import { ElementHandle, Frame } from "puppeteer";
|
||||||
import { getTestExplorerFrame } from "../testExplorer/TestExplorerUtils";
|
|
||||||
|
|
||||||
jest.setTimeout(300000);
|
jest.setTimeout(300000);
|
||||||
|
|
||||||
@@ -12,7 +12,6 @@ describe("Notebook UI tests", () => {
|
|||||||
it("Upload, Open and Delete Notebook", async () => {
|
it("Upload, Open and Delete Notebook", async () => {
|
||||||
try {
|
try {
|
||||||
frame = await getTestExplorerFrame();
|
frame = await getTestExplorerFrame();
|
||||||
await frame.waitForSelector(".galleryHeader");
|
|
||||||
uploadedNotebookNode = await uploadNotebookIfNotExist(frame, notebookName);
|
uploadedNotebookNode = await uploadNotebookIfNotExist(frame, notebookName);
|
||||||
await uploadedNotebookNode.click();
|
await uploadedNotebookNode.click();
|
||||||
await frame.waitForSelector(".tabNavText");
|
await frame.waitForSelector(".tabNavText");
|
||||||
|
|||||||
@@ -1,27 +0,0 @@
|
|||||||
import { Frame } from "puppeteer";
|
|
||||||
import { TestExplorerParams } from "../testExplorer/TestExplorerParams";
|
|
||||||
import { getTestExplorerFrame } from "../testExplorer/TestExplorerUtils";
|
|
||||||
import { SelfServeType } from "../../src/SelfServe/SelfServeUtils";
|
|
||||||
|
|
||||||
jest.setTimeout(300000);
|
|
||||||
|
|
||||||
let frame: Frame;
|
|
||||||
describe("Self Serve", () => {
|
|
||||||
it("Launch Self Serve Example", async () => {
|
|
||||||
try {
|
|
||||||
frame = await getTestExplorerFrame(
|
|
||||||
new Map<string, string>([[TestExplorerParams.selfServeType, SelfServeType.example]])
|
|
||||||
);
|
|
||||||
await frame.waitForSelector("#regions-dropown-input");
|
|
||||||
await frame.waitForSelector("#enableLogging-radioSwitch-input");
|
|
||||||
await frame.waitForSelector("#accountName-textBox-input");
|
|
||||||
await frame.waitForSelector("#dbThroughput-slider-input");
|
|
||||||
await frame.waitForSelector("#collectionThroughput-spinner-input");
|
|
||||||
} catch (error) {
|
|
||||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
||||||
const testName = (expect as any).getState().currentTestName;
|
|
||||||
await page.screenshot({ path: `Test Failed ${testName}.jpg` });
|
|
||||||
throw error;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
});
|
|
||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user