mirror of
https://github.com/Azure/cosmos-explorer.git
synced 2025-12-25 20:01:45 +00:00
Compare commits
15 Commits
test_old_m
...
accessibil
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
5a660551c6 | ||
|
|
b2d59f3b3f | ||
|
|
850f1dfb97 | ||
|
|
a827e79317 | ||
|
|
7dbccff41d | ||
|
|
9184684e75 | ||
|
|
701f486d8f | ||
|
|
5059917edf | ||
|
|
ab1409efb1 | ||
|
|
5de9e682ba | ||
|
|
b2ab979360 | ||
|
|
2f32a676d0 | ||
|
|
950c8ee470 | ||
|
|
b0eaac5b84 | ||
|
|
952491a3ad |
@@ -3,337 +3,337 @@
|
|||||||
@import "../Common/Constants";
|
@import "../Common/Constants";
|
||||||
|
|
||||||
.query-panel {
|
.query-panel {
|
||||||
display: table;
|
display: table;
|
||||||
display: none;
|
display: none;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
border-top: 1px solid #DDDDDD;
|
border-top: 1px solid #dddddd;
|
||||||
/*[{environment-commandbar-toolbar-separator}]*/
|
/*[{environment-commandbar-toolbar-separator}]*/
|
||||||
background-color: #ffffff;
|
background-color: #ffffff;
|
||||||
/*[{plugin-background-color}]*/
|
/*[{plugin-background-color}]*/
|
||||||
padding: 2px 0px 0px 2px;
|
padding: 2px 0px 0px 2px;
|
||||||
resize: vertical;
|
resize: vertical;
|
||||||
}
|
}
|
||||||
|
|
||||||
.query-panel .row {
|
.query-panel .row {
|
||||||
display: table-row;
|
display: table-row;
|
||||||
}
|
}
|
||||||
|
|
||||||
.query-panel .row .cell {
|
.query-panel .row .cell {
|
||||||
display: table-cell;
|
display: table-cell;
|
||||||
}
|
}
|
||||||
|
|
||||||
.query-panel.transition-in {
|
.query-panel.transition-in {
|
||||||
display: table;
|
display: table;
|
||||||
top: 0px;
|
top: 0px;
|
||||||
-webkit-transition: top 2s linear;
|
-webkit-transition: top 2s linear;
|
||||||
-ms-transition: top 2s linear;
|
-ms-transition: top 2s linear;
|
||||||
-moz-transition: top 2s linear;
|
-moz-transition: top 2s linear;
|
||||||
-khtml-transition: top 2s linear;
|
-khtml-transition: top 2s linear;
|
||||||
-o-transition: top 2s linear;
|
-o-transition: top 2s linear;
|
||||||
transition: top 2s linear;
|
transition: top 2s linear;
|
||||||
}
|
}
|
||||||
|
|
||||||
.query-builder {
|
.query-builder {
|
||||||
width:100%;
|
width: 100%;
|
||||||
padding-right: @DefaultSpace;
|
padding-right: @DefaultSpace;
|
||||||
border-bottom: 1px solid @BaseMedium;
|
border-bottom: 1px solid @BaseMedium;
|
||||||
margin-bottom: @DefaultSpace;
|
margin-bottom: @DefaultSpace;
|
||||||
}
|
}
|
||||||
|
|
||||||
.query-builder-toolbar {
|
.query-builder-toolbar {
|
||||||
background-color: #ffffff;
|
background-color: #ffffff;
|
||||||
/*[{plugin-background-color}]*/
|
/*[{plugin-background-color}]*/
|
||||||
min-width: 600px;
|
min-width: 600px;
|
||||||
height: 30px;
|
height: 30px;
|
||||||
border-bottom: 1px solid #DDDDDD;
|
border-bottom: 1px solid #dddddd;
|
||||||
/*[1px solid {environment-commandbar-toolbar-separator}]*/
|
/*[1px solid {environment-commandbar-toolbar-separator}]*/
|
||||||
}
|
}
|
||||||
|
|
||||||
.query-builder-toolbar .query-toolbar-group {
|
.query-builder-toolbar .query-toolbar-group {
|
||||||
display: inline-block;
|
display: inline-block;
|
||||||
height: 24px;
|
height: 24px;
|
||||||
margin: 2px 0px;
|
margin: 2px 0px;
|
||||||
vertical-align: middle;
|
vertical-align: middle;
|
||||||
}
|
}
|
||||||
|
|
||||||
.query-builder-toolbar .query-toolbar-group .query-toolbar-button {
|
.query-builder-toolbar .query-toolbar-group .query-toolbar-button {
|
||||||
min-width: 0px;
|
min-width: 0px;
|
||||||
padding: 0px;
|
padding: 0px;
|
||||||
margin-left: 2px;
|
margin-left: 2px;
|
||||||
background-color: transparent;
|
background-color: transparent;
|
||||||
border: solid transparent;
|
border: solid transparent;
|
||||||
}
|
}
|
||||||
|
|
||||||
.query-builder-toolbar .query-toolbar-group .query-toolbar-button:active {
|
.query-builder-toolbar .query-toolbar-group .query-toolbar-button:active {
|
||||||
outline: 2px solid dodgerblue;
|
outline: 2px solid dodgerblue;
|
||||||
/*[2px solid {common-common-controls-button-border-hover}]*/
|
/*[2px solid {common-common-controls-button-border-hover}]*/
|
||||||
}
|
}
|
||||||
|
|
||||||
.query-builder-toolbar .query-toolbar-group .query-toolbar-button:hover {
|
.query-builder-toolbar .query-toolbar-group .query-toolbar-button:hover {
|
||||||
background-color: #CCCEDB;
|
background-color: #cccedb;
|
||||||
/*[{common-controls-button-hover-background}]*/
|
/*[{common-controls-button-hover-background}]*/
|
||||||
}
|
}
|
||||||
|
|
||||||
.query-builder-toolbar .query-toolbar-group .query-toolbar-button.active {
|
.query-builder-toolbar .query-toolbar-group .query-toolbar-button.active {
|
||||||
background-color: #E6E7ED;
|
background-color: #e6e7ed;
|
||||||
/*[{common-controls-inner-tab-active-background}]*/
|
/*[{common-controls-inner-tab-active-background}]*/
|
||||||
outline: none
|
outline: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
.query-builder-toolbar .query-toolbar-group .query-toolbar-button:disabled,
|
.query-builder-toolbar .query-toolbar-group .query-toolbar-button:disabled,
|
||||||
.query-builder-toolbar .query-toolbar-group .query-toolbar-button.disabled {
|
.query-builder-toolbar .query-toolbar-group .query-toolbar-button.disabled {
|
||||||
background-color: #ffffff;
|
background-color: #ffffff;
|
||||||
/*[{common-controls-button-disabled-background}]*/
|
/*[{common-controls-button-disabled-background}]*/
|
||||||
background: transparent;
|
background: transparent;
|
||||||
border: 1px solid transparent;
|
border: 1px solid transparent;
|
||||||
outline: none;
|
outline: none;
|
||||||
opacity: 0.4;
|
opacity: 0.4;
|
||||||
}
|
}
|
||||||
|
|
||||||
.tableContainer {
|
.tableContainer {
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
.flex-display();
|
.flex-display();
|
||||||
.flex-direction();
|
.flex-direction();
|
||||||
}
|
}
|
||||||
|
|
||||||
.tablesQueryTab{
|
.tablesQueryTab {
|
||||||
padding-left: @MediumSpace;
|
padding-left: @MediumSpace;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
margin-bottom:@LargeSpace;
|
margin-bottom: @LargeSpace;
|
||||||
}
|
}
|
||||||
|
|
||||||
.entity-error-Img {
|
.entity-error-Img {
|
||||||
width: @WarningErrorIconSize;
|
width: @WarningErrorIconSize;
|
||||||
height: @WarningErrorIconSize;
|
height: @WarningErrorIconSize;
|
||||||
margin: @DefaultSpace 0px 0px @SmallSpace;
|
margin: @DefaultSpace 0px 0px @SmallSpace;
|
||||||
}
|
}
|
||||||
|
|
||||||
.query-editor-panel {
|
.query-editor-panel {
|
||||||
margin-right: 16px;
|
margin-right: 16px;
|
||||||
margin-left: 16px;
|
margin-left: 16px;
|
||||||
margin-top: 25px;
|
margin-top: 25px;
|
||||||
position: relative;
|
position: relative;
|
||||||
vertical-align: middle;
|
vertical-align: middle;
|
||||||
cursor: default;
|
cursor: default;
|
||||||
}
|
}
|
||||||
|
|
||||||
.query-editor-text {
|
.query-editor-text {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
margin: 2px;
|
margin: 2px;
|
||||||
border: solid 1px #A9ACB3;
|
border: solid 1px #a9acb3;
|
||||||
/*[{plugin-textbox-disabled-color}]*/
|
/*[{plugin-textbox-disabled-color}]*/
|
||||||
resize: none;
|
resize: none;
|
||||||
margin-top: -39px;
|
margin-top: -39px;
|
||||||
background-color: #ddd;
|
background-color: #ddd;
|
||||||
padding: 5px;
|
padding: 5px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.error-bar {
|
.error-bar {
|
||||||
padding: @LargeSpace 34px @MediumSpace 24px;
|
padding: @LargeSpace 34px @MediumSpace 24px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.error-message {
|
.error-message {
|
||||||
background-color: @BaseLow;
|
background-color: @BaseLow;
|
||||||
padding: @DefaultSpace;
|
padding: @DefaultSpace;
|
||||||
display: inline-flex;
|
display: inline-flex;
|
||||||
}
|
}
|
||||||
|
|
||||||
.error-text {
|
.error-text {
|
||||||
padding-left: @MediumSpace;
|
padding-left: @MediumSpace;
|
||||||
}
|
}
|
||||||
|
|
||||||
.query-editor-text-invalid {
|
.query-editor-text-invalid {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
margin: 2px;
|
margin: 2px;
|
||||||
border: 1px solid #e51400;
|
border: 1px solid #e51400;
|
||||||
resize: none;
|
resize: none;
|
||||||
margin-top: -30px;
|
margin-top: -30px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.query-editor-panel .warning-bar {
|
.query-editor-panel .warning-bar {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
height: 20px;
|
height: 20px;
|
||||||
background-color: #ffffff;
|
background-color: #ffffff;
|
||||||
/*[{plugin-background-color}]*/
|
/*[{plugin-background-color}]*/
|
||||||
position: absolute;
|
position: absolute;
|
||||||
top: -24px;
|
top: -24px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.query-editor-panel .warning-bar .warning-message {
|
.query-editor-panel .warning-bar .warning-message {
|
||||||
display: inline-flex;
|
display: inline-flex;
|
||||||
padding-top: 2px;
|
padding-top: 2px;
|
||||||
vertical-align: middle;
|
vertical-align: middle;
|
||||||
}
|
}
|
||||||
|
|
||||||
.query-editor-panel .warning-bar .warning-message .warning-text {
|
.query-editor-panel .warning-bar .warning-message .warning-text {
|
||||||
margin-left: 2px;
|
margin-left: 2px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.advanced-options-panel{
|
.advanced-options-panel {
|
||||||
margin-bottom: @DefaultSpace;
|
margin-bottom: @DefaultSpace;
|
||||||
}
|
}
|
||||||
|
|
||||||
.advanced-options-panel .advanced-heading .advanced-title {
|
.advanced-options-panel .advanced-heading .advanced-title {
|
||||||
display: inline-flex;
|
display: inline-flex;
|
||||||
margin-left: 27px;
|
margin-left: 27px;
|
||||||
margin-top: 10px;
|
margin-top: 10px;
|
||||||
cursor: default;
|
cursor: default;
|
||||||
}
|
}
|
||||||
|
|
||||||
.advanced-options-panel .advanced-options {
|
.advanced-options-panel .advanced-options {
|
||||||
margin-left: 32px;
|
margin-left: 32px;
|
||||||
margin-top: 5px;
|
margin-top: 5px;
|
||||||
border: 1px solid transparent;
|
border: 1px solid transparent;
|
||||||
}
|
}
|
||||||
|
|
||||||
hr {
|
hr {
|
||||||
margin-top: 10px;
|
margin-top: 10px;
|
||||||
margin-bottom: 12px;
|
margin-bottom: 12px;
|
||||||
border: 0;
|
border: 0;
|
||||||
border-top: 1px solid #ccc;
|
border-top: 1px solid #ccc;
|
||||||
}
|
}
|
||||||
|
|
||||||
input::-webkit-outer-spin-button,
|
input::-webkit-outer-spin-button,
|
||||||
input::-webkit-inner-spin-button {
|
input::-webkit-inner-spin-button {
|
||||||
-webkit-appearance: none;
|
-webkit-appearance: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
.advanced-options-panel .advanced-options .top .top-input {
|
.advanced-options-panel .advanced-options .top .top-input {
|
||||||
width: 100px;
|
width: 100px;
|
||||||
word-spacing: normal;
|
word-spacing: normal;
|
||||||
color: #1E1E1E;
|
color: #1e1e1e;
|
||||||
/*[{common-controls-button-foreground}]*/
|
/*[{common-controls-button-foreground}]*/
|
||||||
border: 1px solid #CCCEDB;
|
border: 1px solid #cccedb;
|
||||||
/*[1px solid {plugin-textbox-border-color}]*/
|
/*[1px solid {plugin-textbox-border-color}]*/
|
||||||
height: 20px;
|
height: 20px;
|
||||||
margin-left: 8px;
|
margin-left: 8px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.advanced-options-panel .advanced-options .top .invalid-top {
|
.advanced-options-panel .advanced-options .top .invalid-top {
|
||||||
color: red;
|
color: red;
|
||||||
}
|
}
|
||||||
|
|
||||||
.advanced-options-panel .advanced-options .select {
|
.advanced-options-panel .advanced-options .select {
|
||||||
margin-top: 18px;
|
margin-top: 18px;
|
||||||
display: inline-flex;
|
display: inline-flex;
|
||||||
}
|
}
|
||||||
|
|
||||||
.advanced-options-icon {
|
.advanced-options-icon {
|
||||||
margin-left: 2px;
|
margin-left: 2px;
|
||||||
vertical-align: sub;
|
vertical-align: sub;
|
||||||
}
|
}
|
||||||
|
|
||||||
.advanced-options-panel .advanced-options .select .select-options-text {
|
.advanced-options-panel .advanced-options .select .select-options-text {
|
||||||
margin-left: 4px;
|
margin-left: 4px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.advanced-options-panel .advanced-options .select .select-options-link {
|
.advanced-options-panel .advanced-options .select .select-options-link {
|
||||||
margin-left: 4px;
|
margin-left: 4px;
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
outline: none;
|
outline: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
.query-panel .row .column-headers .Field {
|
.query-panel .row .column-headers .Field {
|
||||||
padding-left: 95px;
|
padding-left: 95px;
|
||||||
padding-right: 0px;
|
padding-right: 0px;
|
||||||
padding-bottom: 6px;
|
padding-bottom: 6px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.clause-table {
|
.clause-table {
|
||||||
border-spacing: 0px;
|
border-spacing: 0px;
|
||||||
display: table;
|
display: table;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
margin-top: -3px;
|
margin-top: -3px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.clause-table-row {
|
.clause-table-row {
|
||||||
display: row;
|
display: row;
|
||||||
margin-bottom: 10px;
|
margin-bottom: 10px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.clause-table-cell {
|
.clause-table-cell {
|
||||||
display: table-cell;
|
display: table-cell;
|
||||||
text-align: left;
|
text-align: left;
|
||||||
vertical-align: middle;
|
vertical-align: middle;
|
||||||
}
|
}
|
||||||
|
|
||||||
.action-column>button,
|
.action-column > button,
|
||||||
.group-control-header>button,
|
.group-control-header > button,
|
||||||
.group-indicator-column>button {
|
.group-indicator-column > button {
|
||||||
min-width: 20px;
|
min-width: 20px;
|
||||||
width: 20px;
|
width: 20px;
|
||||||
padding: 0px;
|
padding: 0px;
|
||||||
background-color: transparent;
|
background-color: transparent;
|
||||||
border-color: transparent;
|
border-color: transparent;
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
}
|
}
|
||||||
|
|
||||||
.group-control-header>button:disabled {
|
.group-control-header > button:disabled {
|
||||||
min-width: 20px;
|
min-width: 20px;
|
||||||
width: 20px;
|
width: 20px;
|
||||||
padding: 0px;
|
padding: 0px;
|
||||||
background-color: transparent;
|
background-color: transparent;
|
||||||
border-color: transparent;
|
border-color: transparent;
|
||||||
outline: none;
|
outline: none;
|
||||||
opacity: 0.4;
|
opacity: 0.4;
|
||||||
cursor: default;
|
cursor: default;
|
||||||
}
|
}
|
||||||
|
|
||||||
.clause-table-field {
|
.clause-table-field {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
border: 1px solid #bbbbbb;
|
border: 1px solid #bbbbbb;
|
||||||
}
|
}
|
||||||
|
|
||||||
.clause-table-cell button {
|
.clause-table-cell button {
|
||||||
height: 20px;
|
height: 20px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.clause-table-cell input[type="checkbox"] {
|
.clause-table-cell input[type="checkbox"] {
|
||||||
padding: 0px;
|
padding: 0px;
|
||||||
margin-bottom: 12px;
|
margin-bottom: 12px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.and-or-svg {
|
.and-or-svg {
|
||||||
margin-top: -8px;
|
margin-top: -8px;
|
||||||
margin-right: -5px;
|
margin-right: -5px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.scroll-box {
|
.scroll-box {
|
||||||
border-bottom: 1px transparent #DDD;
|
border-bottom: 1px transparent #ddd;
|
||||||
/*[1px solid {plugin-table-border-color}]*/
|
/*[1px solid {plugin-table-border-color}]*/
|
||||||
border-top: 1px transparent #DDD;
|
border-top: 1px transparent #ddd;
|
||||||
/*[1px solid {plugin-table-border-color}]*/
|
/*[1px solid {plugin-table-border-color}]*/
|
||||||
max-height: 20vh;
|
max-height: 20vh;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
}
|
}
|
||||||
|
|
||||||
.scrollable {
|
.scrollable {
|
||||||
overflow: auto;
|
overflow: auto;
|
||||||
overflow-x: hidden;
|
overflow-x: hidden;
|
||||||
}
|
}
|
||||||
|
|
||||||
.and-or-column,
|
.and-or-column,
|
||||||
.and-or-header {
|
.and-or-header {
|
||||||
min-width: 65px;
|
min-width: 65px;
|
||||||
padding-right: 10px;
|
padding-right: 10px;
|
||||||
padding-left: 5px;
|
padding-left: 5px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.operator-column,
|
.operator-column,
|
||||||
.operator-header {
|
.operator-header {
|
||||||
min-width: 65px;
|
min-width: 65px;
|
||||||
padding-right: 10px;
|
padding-right: 10px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.field-header,
|
.field-header,
|
||||||
.field-column {
|
.field-column {
|
||||||
min-width: 125px;
|
min-width: 125px;
|
||||||
padding-right: 10px;
|
padding-right: 10px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.type-header,
|
.type-header,
|
||||||
.type-column {
|
.type-column {
|
||||||
min-width: 85px;
|
min-width: 85px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.and-or-column,
|
.and-or-column,
|
||||||
@@ -345,41 +345,41 @@ input::-webkit-inner-spin-button {
|
|||||||
.type-header,
|
.type-header,
|
||||||
.type-column,
|
.type-column,
|
||||||
.action-header {
|
.action-header {
|
||||||
padding-right: 10px;
|
padding-right: 10px;
|
||||||
margin-bottom: 8px;
|
margin-bottom: 8px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.value-header,
|
.value-header,
|
||||||
.value-column,
|
.value-column,
|
||||||
.time-column {
|
.time-column {
|
||||||
min-width: 230px;
|
min-width: 230px;
|
||||||
padding: 0px 4px 0px 0px;
|
padding: 0px 4px 0px 0px;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
margin-bottom: 8px;
|
margin-bottom: 8px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.group-control-header,
|
.group-control-header,
|
||||||
.group-control-column {
|
.group-control-column {
|
||||||
min-width: 25px;
|
min-width: 25px;
|
||||||
text-align: right;
|
text-align: right;
|
||||||
}
|
}
|
||||||
|
|
||||||
.group-indicator-table {
|
.group-indicator-table {
|
||||||
border-spacing: 0px;
|
border-spacing: 0px;
|
||||||
min-height: 24px
|
min-height: 24px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.group-indicator-column {
|
.group-indicator-column {
|
||||||
min-width: 21px;
|
min-width: 21px;
|
||||||
padding: 0px;
|
padding: 0px;
|
||||||
border-style: none;
|
border-style: none;
|
||||||
height: 29px;
|
height: 29px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.clause-table-cell.action-column,
|
.clause-table-cell.action-column,
|
||||||
.clause-table-cell.action-header {
|
.clause-table-cell.action-header {
|
||||||
min-width: 60px;
|
min-width: 60px;
|
||||||
padding-left: @SmallSpace;
|
padding-left: @SmallSpace;
|
||||||
}
|
}
|
||||||
|
|
||||||
.action-header,
|
.action-header,
|
||||||
@@ -388,15 +388,14 @@ input::-webkit-inner-spin-button {
|
|||||||
.operator-header,
|
.operator-header,
|
||||||
.value-header,
|
.value-header,
|
||||||
.and-or-header {
|
.and-or-header {
|
||||||
padding-right: 4px;
|
padding-right: 4px;
|
||||||
padding-bottom: 5px;
|
padding-bottom: 5px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.header-background {
|
.header-background {
|
||||||
background-color: #ffffff;
|
background-color: #ffffff;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/*.type-header {
|
/*.type-header {
|
||||||
padding-right: 4px;
|
padding-right: 4px;
|
||||||
}
|
}
|
||||||
@@ -410,112 +409,111 @@ input::-webkit-inner-spin-button {
|
|||||||
}*/
|
}*/
|
||||||
|
|
||||||
.clause-table-field[readonly] {
|
.clause-table-field[readonly] {
|
||||||
background-color: #EEEEF2;
|
background-color: #eeeef2;
|
||||||
/*[{plugin-table-header-background-color}]*/
|
/*[{plugin-table-header-background-color}]*/
|
||||||
border: 1px solid #CCCEDB;
|
border: 1px solid #cccedb;
|
||||||
/*[{plugin-table-border-color}]*/
|
/*[{plugin-table-border-color}]*/
|
||||||
}
|
}
|
||||||
|
|
||||||
.addClause-title {
|
.addClause-title {
|
||||||
/*[{common-common-controls-button-border-hover}]*/
|
/*[{common-common-controls-button-border-hover}]*/
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
margin-left: -5px;
|
margin-left: -5px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.addClause {
|
.addClause {
|
||||||
width: 125px;
|
width: 125px;
|
||||||
padding: 8px 0px 5px 5px;
|
padding: 8px 0px 5px 5px;
|
||||||
border: 1px solid #fff;
|
border: 1px solid #fff;
|
||||||
margin-left: 5px;
|
margin-left: 5px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.addClause:hover {
|
.addClause:hover {
|
||||||
.hover();
|
.hover();
|
||||||
}
|
}
|
||||||
|
|
||||||
.addClause:active {
|
.addClause:active {
|
||||||
.active();
|
.active();
|
||||||
border: 1px dashed @AccentMedium;
|
border: 1px dashed @AccentMedium;
|
||||||
}
|
}
|
||||||
|
|
||||||
.clause-table-row {
|
.clause-table-row {
|
||||||
min-width: 550px;
|
min-width: 550px;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
}
|
}
|
||||||
|
|
||||||
.clause-table-field field-column {
|
.clause-table-field field-column {
|
||||||
min-width: 75px;
|
min-width: 75px;
|
||||||
height: 30px;
|
height: 30px;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
}
|
}
|
||||||
|
|
||||||
.clause-table-field field-input {
|
.clause-table-field field-input {
|
||||||
min-width: 54px;
|
min-width: 54px;
|
||||||
margin-left: -78px;
|
margin-left: -78px;
|
||||||
height: 25px;
|
height: 25px;
|
||||||
border: none;
|
border: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
.query-panel .row .spacing {
|
.query-panel .row .spacing {
|
||||||
padding-bottom: 6px;
|
padding-bottom: 6px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.query-panel .divider.horizontal {
|
.query-panel .divider.horizontal {
|
||||||
height: 10px;
|
height: 10px;
|
||||||
width: 100%
|
width: 100%;
|
||||||
}
|
}
|
||||||
|
|
||||||
.inline-div {
|
.inline-div {
|
||||||
display: inline
|
display: inline;
|
||||||
}
|
}
|
||||||
|
|
||||||
.querybuilder-addpropertyImg,
|
.querybuilder-addpropertyImg,
|
||||||
.querybuilder-cancelImg {
|
.querybuilder-cancelImg {
|
||||||
width: 14px;
|
width: 14px;
|
||||||
height: 14px;
|
height: 14px;
|
||||||
margin-left: 3px;
|
margin-left: 3px;
|
||||||
margin-bottom: 8px;
|
margin-bottom: 8px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.addclauseProperty-Img {
|
.addclauseProperty-Img {
|
||||||
width: 14px;
|
width: 14px;
|
||||||
height: 14px;
|
height: 14px;
|
||||||
margin-bottom: 5px;
|
margin-bottom: 5px;
|
||||||
margin-left: 12px;
|
margin-left: 12px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.entity-Add-Cancel {
|
.entity-Add-Cancel {
|
||||||
padding: @DefaultSpace @SmallSpace @SmallSpace;
|
// padding: @DefaultSpace @SmallSpace @SmallSpace;
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
}
|
}
|
||||||
|
|
||||||
.entity-Add-Cancel:hover {
|
.entity-Add-Cancel:hover {
|
||||||
.hover();
|
.hover();
|
||||||
}
|
}
|
||||||
|
|
||||||
.entity-Add-Cancel:active {
|
.entity-Add-Cancel:active {
|
||||||
.active();
|
.active();
|
||||||
}
|
}
|
||||||
|
|
||||||
.query-builder-isDisabled {
|
.query-builder-isDisabled {
|
||||||
border: 1px solid #CCCEDB;
|
border: 1px solid #cccedb;
|
||||||
color: #ccc;
|
color: #ccc;
|
||||||
}
|
}
|
||||||
|
|
||||||
.edit-value-text {
|
.edit-value-text {
|
||||||
padding-left: @DefaultSpace;
|
padding-left: @DefaultSpace;
|
||||||
}
|
}
|
||||||
|
|
||||||
.expand-triangle {
|
.expand-triangle {
|
||||||
width: 10px;
|
width: 10px;
|
||||||
height: 10px;
|
height: 10px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.expand-triangle-right {
|
.expand-triangle-right {
|
||||||
margin-bottom: 5px;
|
margin-bottom: 5px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@media only screen and (max-width: 1200px) {
|
@media only screen and (max-width: 1200px) {
|
||||||
.clause-table {
|
.clause-table {
|
||||||
@@ -524,4 +522,4 @@ input::-webkit-inner-spin-button {
|
|||||||
width: 100%;
|
width: 100%;
|
||||||
padding-top: 10px;
|
padding-top: 10px;
|
||||||
}
|
}
|
||||||
}*/
|
}*/
|
||||||
|
|||||||
@@ -45,6 +45,8 @@ export class ArmResourceTypes {
|
|||||||
|
|
||||||
export class BackendDefaults {
|
export class BackendDefaults {
|
||||||
public static partitionKeyKind = "Hash";
|
public static partitionKeyKind = "Hash";
|
||||||
|
public static partitionKeyMultiHash = "MultiHash";
|
||||||
|
public static maxNumMultiHashPartition = 2;
|
||||||
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;
|
||||||
|
|||||||
@@ -73,6 +73,17 @@ export const TableEntity: FunctionComponent<TableEntityProps> = ({
|
|||||||
|
|
||||||
const sectionStackTokens: IStackTokens = { childrenGap: 12 };
|
const sectionStackTokens: IStackTokens = { childrenGap: 12 };
|
||||||
|
|
||||||
|
const handleKeyPress = (event: React.KeyboardEvent<HTMLElement>) => {
|
||||||
|
if (event.key === "Enter" || event.key === "Space") {
|
||||||
|
onEditEntity();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
const handleKeyPressdelete = (event: React.KeyboardEvent<HTMLElement>) => {
|
||||||
|
if (event.key === "Enter" || event.key === "Space") {
|
||||||
|
onDeleteEntity();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
const getEntityValueType = (): string => {
|
const getEntityValueType = (): string => {
|
||||||
const { Int, Smallint, Tinyint } = CassandraType;
|
const { Int, Smallint, Tinyint } = CassandraType;
|
||||||
const { Double, Int32, Int64 } = TableType;
|
const { Double, Int32, Int64 } = TableType;
|
||||||
@@ -126,12 +137,28 @@ export const TableEntity: FunctionComponent<TableEntityProps> = ({
|
|||||||
/>
|
/>
|
||||||
{!isEntityValueDisable && (
|
{!isEntityValueDisable && (
|
||||||
<TooltipHost content="Edit property" id="editTooltip">
|
<TooltipHost content="Edit property" id="editTooltip">
|
||||||
<Image {...imageProps} src={EditIcon} alt="editEntity" id="editEntity" onClick={onEditEntity} />
|
<Image
|
||||||
|
{...imageProps}
|
||||||
|
src={EditIcon}
|
||||||
|
alt="editEntity"
|
||||||
|
id="editEntity"
|
||||||
|
onClick={onEditEntity}
|
||||||
|
tabIndex={0}
|
||||||
|
onKeyPress={handleKeyPress}
|
||||||
|
/>
|
||||||
</TooltipHost>
|
</TooltipHost>
|
||||||
)}
|
)}
|
||||||
{isDeleteOptionVisible && userContext.apiType !== "Cassandra" && (
|
{isDeleteOptionVisible && userContext.apiType !== "Cassandra" && (
|
||||||
<TooltipHost content="Delete property" id="deleteTooltip">
|
<TooltipHost content="Delete property" id="deleteTooltip">
|
||||||
<Image {...imageProps} src={DeleteIcon} alt="delete entity" id="deleteEntity" onClick={onDeleteEntity} />
|
<Image
|
||||||
|
{...imageProps}
|
||||||
|
src={DeleteIcon}
|
||||||
|
alt="delete entity"
|
||||||
|
id="deleteEntity"
|
||||||
|
onClick={onDeleteEntity}
|
||||||
|
tabIndex={0}
|
||||||
|
onKeyPress={handleKeyPressdelete}
|
||||||
|
/>
|
||||||
</TooltipHost>
|
</TooltipHost>
|
||||||
)}
|
)}
|
||||||
</Stack>
|
</Stack>
|
||||||
|
|||||||
@@ -9,7 +9,7 @@ export const InfoTooltip: React.FunctionComponent<TooltipProps> = ({ children }:
|
|||||||
return (
|
return (
|
||||||
<span>
|
<span>
|
||||||
<TooltipHost content={children}>
|
<TooltipHost content={children}>
|
||||||
<Icon iconName="Info" ariaLabel="Info" className="panelInfoIcon" tabIndex={0} />
|
<Icon iconName="Info" ariaLabel={children} className="panelInfoIcon" tabIndex={0} />
|
||||||
</TooltipHost>
|
</TooltipHost>
|
||||||
</span>
|
</span>
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -4,6 +4,7 @@ import { logConsoleInfo, logConsoleProgress } from "../../Utils/NotificationCons
|
|||||||
import { client } from "../CosmosClient";
|
import { client } from "../CosmosClient";
|
||||||
import { getEntityName } from "../DocumentUtility";
|
import { getEntityName } from "../DocumentUtility";
|
||||||
import { handleError } from "../ErrorHandlingUtils";
|
import { handleError } from "../ErrorHandlingUtils";
|
||||||
|
import { getPartitionKeyValue } from "./getPartitionKeyValue";
|
||||||
|
|
||||||
export const deleteDocument = async (collection: CollectionBase, documentId: DocumentId): Promise<void> => {
|
export const deleteDocument = async (collection: CollectionBase, documentId: DocumentId): Promise<void> => {
|
||||||
const entityName: string = getEntityName();
|
const entityName: string = getEntityName();
|
||||||
@@ -13,7 +14,7 @@ export const deleteDocument = async (collection: CollectionBase, documentId: Doc
|
|||||||
await client()
|
await client()
|
||||||
.database(collection.databaseId)
|
.database(collection.databaseId)
|
||||||
.container(collection.id())
|
.container(collection.id())
|
||||||
.item(documentId.id(), documentId.partitionKeyValue?.length === 0 ? undefined : documentId.partitionKeyValue)
|
.item(documentId.id(), getPartitionKeyValue(documentId))
|
||||||
.delete();
|
.delete();
|
||||||
logConsoleInfo(`Successfully deleted ${entityName} ${documentId.id()}`);
|
logConsoleInfo(`Successfully deleted ${entityName} ${documentId.id()}`);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
|
|||||||
12
src/Common/dataAccess/getPartitionKeyValue.ts
Normal file
12
src/Common/dataAccess/getPartitionKeyValue.ts
Normal file
@@ -0,0 +1,12 @@
|
|||||||
|
import { userContext } from "UserContext";
|
||||||
|
import DocumentId from "../../Explorer/Tree/DocumentId";
|
||||||
|
|
||||||
|
export const getPartitionKeyValue = (documentId: DocumentId) => {
|
||||||
|
if (userContext.apiType === "Tables" && documentId.partitionKeyValue?.length === 0) {
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
if (documentId.partitionKeyValue?.length === 0) {
|
||||||
|
return undefined;
|
||||||
|
}
|
||||||
|
return documentId.partitionKeyValue;
|
||||||
|
};
|
||||||
@@ -6,6 +6,7 @@ import { HttpHeaders } from "../Constants";
|
|||||||
import { client } from "../CosmosClient";
|
import { client } from "../CosmosClient";
|
||||||
import { getEntityName } from "../DocumentUtility";
|
import { getEntityName } from "../DocumentUtility";
|
||||||
import { handleError } from "../ErrorHandlingUtils";
|
import { handleError } from "../ErrorHandlingUtils";
|
||||||
|
import { getPartitionKeyValue } from "./getPartitionKeyValue";
|
||||||
|
|
||||||
export const readDocument = async (collection: CollectionBase, documentId: DocumentId): Promise<Item> => {
|
export const readDocument = async (collection: CollectionBase, documentId: DocumentId): Promise<Item> => {
|
||||||
const entityName = getEntityName();
|
const entityName = getEntityName();
|
||||||
@@ -21,8 +22,7 @@ export const readDocument = async (collection: CollectionBase, documentId: Docum
|
|||||||
const response = await client()
|
const response = await client()
|
||||||
.database(collection.databaseId)
|
.database(collection.databaseId)
|
||||||
.container(collection.id())
|
.container(collection.id())
|
||||||
// use undefined if the partitionKeyValue is empty
|
.item(documentId.id(), getPartitionKeyValue(documentId))
|
||||||
.item(documentId.id(), documentId.partitionKeyValue?.length === 0 ? undefined : documentId.partitionKeyValue)
|
|
||||||
.read(options);
|
.read(options);
|
||||||
|
|
||||||
return response?.resource;
|
return response?.resource;
|
||||||
|
|||||||
@@ -6,6 +6,7 @@ import { logConsoleInfo, logConsoleProgress } from "../../Utils/NotificationCons
|
|||||||
import { client } from "../CosmosClient";
|
import { client } from "../CosmosClient";
|
||||||
import { getEntityName } from "../DocumentUtility";
|
import { getEntityName } from "../DocumentUtility";
|
||||||
import { handleError } from "../ErrorHandlingUtils";
|
import { handleError } from "../ErrorHandlingUtils";
|
||||||
|
import { getPartitionKeyValue } from "./getPartitionKeyValue";
|
||||||
|
|
||||||
export const updateDocument = async (
|
export const updateDocument = async (
|
||||||
collection: CollectionBase,
|
collection: CollectionBase,
|
||||||
@@ -25,7 +26,7 @@ export const updateDocument = async (
|
|||||||
const response = await client()
|
const response = await client()
|
||||||
.database(collection.databaseId)
|
.database(collection.databaseId)
|
||||||
.container(collection.id())
|
.container(collection.id())
|
||||||
.item(documentId.id(), documentId.partitionKeyValue?.length === 0 ? undefined : documentId.partitionKeyValue)
|
.item(documentId.id(), getPartitionKeyValue(documentId))
|
||||||
.replace(newDocument, options);
|
.replace(newDocument, options);
|
||||||
|
|
||||||
logConsoleInfo(`Successfully updated ${entityName} ${documentId.id()}`);
|
logConsoleInfo(`Successfully updated ${entityName} ${documentId.id()}`);
|
||||||
|
|||||||
@@ -397,6 +397,7 @@ export interface DataExplorerInputsFrame {
|
|||||||
defaultCollectionThroughput?: CollectionCreationDefaults;
|
defaultCollectionThroughput?: CollectionCreationDefaults;
|
||||||
isPostgresAccount?: boolean;
|
isPostgresAccount?: boolean;
|
||||||
isReplica?: boolean;
|
isReplica?: boolean;
|
||||||
|
clientIpAddress?: string;
|
||||||
// TODO: Update this param in the OSS extension to remove isFreeTier, isMarlinServerGroup, and make nodes a flat array instead of an nested array
|
// TODO: Update this param in the OSS extension to remove isFreeTier, isMarlinServerGroup, and make nodes a flat array instead of an nested array
|
||||||
connectionStringParams?: any;
|
connectionStringParams?: any;
|
||||||
flights?: readonly string[];
|
flights?: readonly string[];
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
|
import { HoverCard, HoverCardType, Icon, Label, Link, Stack } from "@fluentui/react";
|
||||||
import * as React from "react";
|
import * as React from "react";
|
||||||
import { Icon, Label, Stack, HoverCard, HoverCardType, Link } from "@fluentui/react";
|
|
||||||
import { CodeOfConductEndpoints } from "../../../../Common/Constants";
|
import { CodeOfConductEndpoints } from "../../../../Common/Constants";
|
||||||
import "./InfoComponent.less";
|
import "./InfoComponent.less";
|
||||||
|
|
||||||
@@ -41,7 +41,7 @@ export class InfoComponent extends React.Component<InfoComponentProps> {
|
|||||||
public render(): JSX.Element {
|
public render(): JSX.Element {
|
||||||
return (
|
return (
|
||||||
<HoverCard plainCardProps={{ onRenderPlainCard: this.onHover }} instantOpenOnClick type={HoverCardType.plain}>
|
<HoverCard plainCardProps={{ onRenderPlainCard: this.onHover }} instantOpenOnClick type={HoverCardType.plain}>
|
||||||
<div className="infoPanelMain">
|
<div className="infoPanelMain" tabIndex={0}>
|
||||||
<Icon className="infoIconMain" iconName="Help" styles={{ root: { verticalAlign: "middle" } }} />
|
<Icon className="infoIconMain" iconName="Help" styles={{ root: { verticalAlign: "middle" } }} />
|
||||||
<Label className="infoLabelMain">Help</Label>
|
<Label className="infoLabelMain">Help</Label>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -12,6 +12,7 @@ exports[`InfoComponent renders 1`] = `
|
|||||||
>
|
>
|
||||||
<div
|
<div
|
||||||
className="infoPanelMain"
|
className="infoPanelMain"
|
||||||
|
tabIndex={0}
|
||||||
>
|
>
|
||||||
<Icon
|
<Icon
|
||||||
className="infoIconMain"
|
className="infoIconMain"
|
||||||
|
|||||||
@@ -310,7 +310,9 @@ export class SubSettingsComponent extends React.Component<SubSettingsComponentPr
|
|||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
{this.isLargePartitionKeyEnabled() && <Text>Large {this.partitionKeyName.toLowerCase()} has been enabled</Text>}
|
{userContext.apiType === "SQL" && this.isLargePartitionKeyEnabled() && (
|
||||||
|
<Text>Large {this.partitionKeyName.toLowerCase()} has been enabled</Text>
|
||||||
|
)}
|
||||||
</Stack>
|
</Stack>
|
||||||
);
|
);
|
||||||
|
|
||||||
|
|||||||
@@ -82,7 +82,6 @@ interface ThroughputInputAutoPilotV3State {
|
|||||||
spendAckChecked: boolean;
|
spendAckChecked: boolean;
|
||||||
exceedFreeTierThroughput: boolean;
|
exceedFreeTierThroughput: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
export class ThroughputInputAutoPilotV3Component extends React.Component<
|
export class ThroughputInputAutoPilotV3Component extends React.Component<
|
||||||
ThroughputInputAutoPilotV3Props,
|
ThroughputInputAutoPilotV3Props,
|
||||||
ThroughputInputAutoPilotV3State
|
ThroughputInputAutoPilotV3State
|
||||||
@@ -624,7 +623,10 @@ export class ThroughputInputAutoPilotV3Component extends React.Component<
|
|||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
{warningMessage && (
|
{warningMessage && (
|
||||||
<MessageBar messageBarIconProps={{ iconName: "WarningSolid", className: "messageBarWarningIcon" }}>
|
<MessageBar
|
||||||
|
messageBarIconProps={{ iconName: "WarningSolid", className: "messageBarWarningIcon" }}
|
||||||
|
role="alert"
|
||||||
|
>
|
||||||
{warningMessage}
|
{warningMessage}
|
||||||
</MessageBar>
|
</MessageBar>
|
||||||
)}
|
)}
|
||||||
|
|||||||
@@ -15,6 +15,7 @@ exports[`ThroughputInputAutoPilotV3Component autopilot input visible 1`] = `
|
|||||||
"iconName": "WarningSolid",
|
"iconName": "WarningSolid",
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
role="alert"
|
||||||
>
|
>
|
||||||
<Text
|
<Text
|
||||||
styles={
|
styles={
|
||||||
|
|||||||
@@ -23,12 +23,12 @@ describe("ThroughputInput Pane", () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
it("should switch mode properly", () => {
|
it("should switch mode properly", () => {
|
||||||
wrapper.find('[aria-label="Manual mode"]').simulate("change");
|
wrapper.find('[aria-label="Manual database throughput"]').simulate("change");
|
||||||
expect(wrapper.find('[aria-label="Throughput header"]').at(0).text()).toBe(
|
expect(wrapper.find('[aria-label="Throughput header"]').at(0).text()).toBe(
|
||||||
"Container throughput (400 - unlimited RU/s)"
|
"Container throughput (400 - unlimited RU/s)"
|
||||||
);
|
);
|
||||||
|
|
||||||
wrapper.find('[aria-label="Autoscale mode"]').simulate("change");
|
wrapper.find('[aria-label="Autoscale database throughput"]').simulate("change");
|
||||||
expect(wrapper.find('[aria-label="Throughput header"]').at(0).text()).toBe("Container throughput (autoscale)");
|
expect(wrapper.find('[aria-label="Throughput header"]').at(0).text()).toBe("Container throughput (autoscale)");
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -186,8 +186,9 @@ export const ThroughputInput: FunctionComponent<ThroughputInputProps> = ({
|
|||||||
|
|
||||||
<Stack horizontal verticalAlign="center">
|
<Stack horizontal verticalAlign="center">
|
||||||
<input
|
<input
|
||||||
|
id="Autoscale-input"
|
||||||
className="throughputInputRadioBtn"
|
className="throughputInputRadioBtn"
|
||||||
aria-label="Autoscale mode"
|
aria-label="Autoscale database throughput"
|
||||||
aria-required={true}
|
aria-required={true}
|
||||||
checked={isAutoscaleSelected}
|
checked={isAutoscaleSelected}
|
||||||
type="radio"
|
type="radio"
|
||||||
@@ -195,11 +196,14 @@ export const ThroughputInput: FunctionComponent<ThroughputInputProps> = ({
|
|||||||
tabIndex={0}
|
tabIndex={0}
|
||||||
onChange={(e) => handleOnChangeMode(e, "Autoscale")}
|
onChange={(e) => handleOnChangeMode(e, "Autoscale")}
|
||||||
/>
|
/>
|
||||||
<span className="throughputInputRadioBtnLabel">Autoscale</span>
|
<label htmlFor="Autoscale-input" className="throughputInputRadioBtnLabel">
|
||||||
|
Autoscale
|
||||||
|
</label>
|
||||||
|
|
||||||
<input
|
<input
|
||||||
|
id="Manual-input"
|
||||||
className="throughputInputRadioBtn"
|
className="throughputInputRadioBtn"
|
||||||
aria-label="Manual mode"
|
aria-label="Manual database throughput"
|
||||||
checked={!isAutoscaleSelected}
|
checked={!isAutoscaleSelected}
|
||||||
type="radio"
|
type="radio"
|
||||||
aria-required={true}
|
aria-required={true}
|
||||||
@@ -207,14 +211,20 @@ export const ThroughputInput: FunctionComponent<ThroughputInputProps> = ({
|
|||||||
tabIndex={0}
|
tabIndex={0}
|
||||||
onChange={(e) => handleOnChangeMode(e, "Manual")}
|
onChange={(e) => handleOnChangeMode(e, "Manual")}
|
||||||
/>
|
/>
|
||||||
<span className="throughputInputRadioBtnLabel">Manual</span>
|
<label className="throughputInputRadioBtnLabel" htmlFor="Manual-input">
|
||||||
|
Manual
|
||||||
|
</label>
|
||||||
</Stack>
|
</Stack>
|
||||||
|
|
||||||
{isAutoscaleSelected && (
|
{isAutoscaleSelected && (
|
||||||
<Stack className="throughputInputSpacing">
|
<Stack className="throughputInputSpacing">
|
||||||
<Text variant="small" aria-label="ruDescription">
|
<Text variant="small" aria-label="capacity calculator of azure cosmos db">
|
||||||
Estimate your required RU/s with{" "}
|
Estimate your required RU/s with{" "}
|
||||||
<Link target="_blank" href="https://cosmos.azure.com/capacitycalculator/" aria-label="ruDescription">
|
<Link
|
||||||
|
target="_blank"
|
||||||
|
href="https://cosmos.azure.com/capacitycalculator/"
|
||||||
|
aria-label="capacity calculator of azure cosmos db"
|
||||||
|
>
|
||||||
capacity calculator
|
capacity calculator
|
||||||
</Link>
|
</Link>
|
||||||
.
|
.
|
||||||
|
|||||||
@@ -344,13 +344,13 @@ exports[`ThroughputInput Pane should render Default properly 1`] = `
|
|||||||
onMouseLeave={[Function]}
|
onMouseLeave={[Function]}
|
||||||
>
|
>
|
||||||
<StyledIconBase
|
<StyledIconBase
|
||||||
ariaLabel="Info"
|
ariaLabel="Set the throughput — Request Units per second (RU/s) — required for the workload. A read of a 1 KB document uses 1 RU. Select manual if you plan to scale RU/s yourself. Select autoscale to allow the system to scale RU/s based on usage."
|
||||||
className="panelInfoIcon"
|
className="panelInfoIcon"
|
||||||
iconName="Info"
|
iconName="Info"
|
||||||
tabIndex={0}
|
tabIndex={0}
|
||||||
>
|
>
|
||||||
<IconBase
|
<IconBase
|
||||||
ariaLabel="Info"
|
ariaLabel="Set the throughput — Request Units per second (RU/s) — required for the workload. A read of a 1 KB document uses 1 RU. Select manual if you plan to scale RU/s yourself. Select autoscale to allow the system to scale RU/s based on usage."
|
||||||
className="panelInfoIcon"
|
className="panelInfoIcon"
|
||||||
iconName="Info"
|
iconName="Info"
|
||||||
styles={[Function]}
|
styles={[Function]}
|
||||||
@@ -630,7 +630,7 @@ exports[`ThroughputInput Pane should render Default properly 1`] = `
|
|||||||
}
|
}
|
||||||
>
|
>
|
||||||
<i
|
<i
|
||||||
aria-label="Info"
|
aria-label="Set the throughput — Request Units per second (RU/s) — required for the workload. A read of a 1 KB document uses 1 RU. Select manual if you plan to scale RU/s yourself. Select autoscale to allow the system to scale RU/s based on usage."
|
||||||
className="panelInfoIcon root-57"
|
className="panelInfoIcon root-57"
|
||||||
data-icon-name="Info"
|
data-icon-name="Info"
|
||||||
role="img"
|
role="img"
|
||||||
@@ -655,39 +655,43 @@ exports[`ThroughputInput Pane should render Default properly 1`] = `
|
|||||||
className="ms-Stack css-58"
|
className="ms-Stack css-58"
|
||||||
>
|
>
|
||||||
<input
|
<input
|
||||||
aria-label="Autoscale mode"
|
aria-label="Autoscale database throughput"
|
||||||
aria-required={true}
|
aria-required={true}
|
||||||
checked={true}
|
checked={true}
|
||||||
className="throughputInputRadioBtn"
|
className="throughputInputRadioBtn"
|
||||||
|
id="Autoscale-input"
|
||||||
key=".0:$.0"
|
key=".0:$.0"
|
||||||
onChange={[Function]}
|
onChange={[Function]}
|
||||||
role="radio"
|
role="radio"
|
||||||
tabIndex={0}
|
tabIndex={0}
|
||||||
type="radio"
|
type="radio"
|
||||||
/>
|
/>
|
||||||
<span
|
<label
|
||||||
className="throughputInputRadioBtnLabel"
|
className="throughputInputRadioBtnLabel"
|
||||||
|
htmlFor="Autoscale-input"
|
||||||
key=".0:$.1"
|
key=".0:$.1"
|
||||||
>
|
>
|
||||||
Autoscale
|
Autoscale
|
||||||
</span>
|
</label>
|
||||||
<input
|
<input
|
||||||
aria-label="Manual mode"
|
aria-label="Manual database throughput"
|
||||||
aria-required={true}
|
aria-required={true}
|
||||||
checked={false}
|
checked={false}
|
||||||
className="throughputInputRadioBtn"
|
className="throughputInputRadioBtn"
|
||||||
|
id="Manual-input"
|
||||||
key=".0:$.2"
|
key=".0:$.2"
|
||||||
onChange={[Function]}
|
onChange={[Function]}
|
||||||
role="radio"
|
role="radio"
|
||||||
tabIndex={0}
|
tabIndex={0}
|
||||||
type="radio"
|
type="radio"
|
||||||
/>
|
/>
|
||||||
<span
|
<label
|
||||||
className="throughputInputRadioBtnLabel"
|
className="throughputInputRadioBtnLabel"
|
||||||
|
htmlFor="Manual-input"
|
||||||
key=".0:$.3"
|
key=".0:$.3"
|
||||||
>
|
>
|
||||||
Manual
|
Manual
|
||||||
</span>
|
</label>
|
||||||
</div>
|
</div>
|
||||||
</Stack>
|
</Stack>
|
||||||
<Stack
|
<Stack
|
||||||
@@ -697,23 +701,23 @@ exports[`ThroughputInput Pane should render Default properly 1`] = `
|
|||||||
className="ms-Stack throughputInputSpacing css-59"
|
className="ms-Stack throughputInputSpacing css-59"
|
||||||
>
|
>
|
||||||
<Text
|
<Text
|
||||||
aria-label="ruDescription"
|
aria-label="capacity calculator of azure cosmos db"
|
||||||
key=".0:$.0"
|
key=".0:$.0"
|
||||||
variant="small"
|
variant="small"
|
||||||
>
|
>
|
||||||
<span
|
<span
|
||||||
aria-label="ruDescription"
|
aria-label="capacity calculator of azure cosmos db"
|
||||||
className="css-54"
|
className="css-54"
|
||||||
>
|
>
|
||||||
Estimate your required RU/s with
|
Estimate your required RU/s with
|
||||||
|
|
||||||
<StyledLinkBase
|
<StyledLinkBase
|
||||||
aria-label="ruDescription"
|
aria-label="capacity calculator of azure cosmos db"
|
||||||
href="https://cosmos.azure.com/capacitycalculator/"
|
href="https://cosmos.azure.com/capacitycalculator/"
|
||||||
target="_blank"
|
target="_blank"
|
||||||
>
|
>
|
||||||
<LinkBase
|
<LinkBase
|
||||||
aria-label="ruDescription"
|
aria-label="capacity calculator of azure cosmos db"
|
||||||
href="https://cosmos.azure.com/capacitycalculator/"
|
href="https://cosmos.azure.com/capacitycalculator/"
|
||||||
styles={[Function]}
|
styles={[Function]}
|
||||||
target="_blank"
|
target="_blank"
|
||||||
@@ -992,7 +996,7 @@ exports[`ThroughputInput Pane should render Default properly 1`] = `
|
|||||||
}
|
}
|
||||||
>
|
>
|
||||||
<a
|
<a
|
||||||
aria-label="ruDescription"
|
aria-label="capacity calculator of azure cosmos db"
|
||||||
className="ms-Link root-60"
|
className="ms-Link root-60"
|
||||||
href="https://cosmos.azure.com/capacitycalculator/"
|
href="https://cosmos.azure.com/capacitycalculator/"
|
||||||
onClick={[Function]}
|
onClick={[Function]}
|
||||||
@@ -1331,13 +1335,13 @@ exports[`ThroughputInput Pane should render Default properly 1`] = `
|
|||||||
onMouseLeave={[Function]}
|
onMouseLeave={[Function]}
|
||||||
>
|
>
|
||||||
<StyledIconBase
|
<StyledIconBase
|
||||||
ariaLabel="Info"
|
ariaLabel="Set the max RU/s to the highest RU/s you want your container to scale to. The container will scale between 10% of max RU/s to the max RU/s based on usage."
|
||||||
className="panelInfoIcon"
|
className="panelInfoIcon"
|
||||||
iconName="Info"
|
iconName="Info"
|
||||||
tabIndex={0}
|
tabIndex={0}
|
||||||
>
|
>
|
||||||
<IconBase
|
<IconBase
|
||||||
ariaLabel="Info"
|
ariaLabel="Set the max RU/s to the highest RU/s you want your container to scale to. The container will scale between 10% of max RU/s to the max RU/s based on usage."
|
||||||
className="panelInfoIcon"
|
className="panelInfoIcon"
|
||||||
iconName="Info"
|
iconName="Info"
|
||||||
styles={[Function]}
|
styles={[Function]}
|
||||||
@@ -1617,7 +1621,7 @@ exports[`ThroughputInput Pane should render Default properly 1`] = `
|
|||||||
}
|
}
|
||||||
>
|
>
|
||||||
<i
|
<i
|
||||||
aria-label="Info"
|
aria-label="Set the max RU/s to the highest RU/s you want your container to scale to. The container will scale between 10% of max RU/s to the max RU/s based on usage."
|
||||||
className="panelInfoIcon root-57"
|
className="panelInfoIcon root-57"
|
||||||
data-icon-name="Info"
|
data-icon-name="Info"
|
||||||
role="img"
|
role="img"
|
||||||
|
|||||||
@@ -133,7 +133,6 @@ describe("ContainerSampleGenerator", () => {
|
|||||||
} as DatabaseAccount,
|
} as DatabaseAccount,
|
||||||
});
|
});
|
||||||
|
|
||||||
// Rejects with error that contains experience
|
|
||||||
expect(ContainerSampleGenerator.createSampleGeneratorAsync(explorerStub)).rejects.toMatch(experience);
|
expect(ContainerSampleGenerator.createSampleGeneratorAsync(explorerStub)).rejects.toMatch(experience);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
@@ -89,9 +89,10 @@ export interface AddCollectionPanelState {
|
|||||||
enableIndexing: boolean;
|
enableIndexing: boolean;
|
||||||
isSharded: boolean;
|
isSharded: boolean;
|
||||||
partitionKey: string;
|
partitionKey: string;
|
||||||
|
subPartitionKeys: string[];
|
||||||
enableDedicatedThroughput: boolean;
|
enableDedicatedThroughput: boolean;
|
||||||
createMongoWildCardIndex: boolean;
|
createMongoWildCardIndex: boolean;
|
||||||
useHashV2: boolean;
|
useHashV1: boolean;
|
||||||
enableAnalyticalStore: boolean;
|
enableAnalyticalStore: boolean;
|
||||||
uniqueKeys: string[];
|
uniqueKeys: string[];
|
||||||
errorMessage: string;
|
errorMessage: string;
|
||||||
@@ -121,10 +122,11 @@ export class AddCollectionPanel extends React.Component<AddCollectionPanelProps,
|
|||||||
enableIndexing: true,
|
enableIndexing: true,
|
||||||
isSharded: userContext.apiType !== "Tables",
|
isSharded: userContext.apiType !== "Tables",
|
||||||
partitionKey: this.getPartitionKey(),
|
partitionKey: this.getPartitionKey(),
|
||||||
|
subPartitionKeys: [],
|
||||||
enableDedicatedThroughput: false,
|
enableDedicatedThroughput: false,
|
||||||
createMongoWildCardIndex:
|
createMongoWildCardIndex:
|
||||||
isCapabilityEnabled("EnableMongo") && !isCapabilityEnabled("EnableMongo16MBDocumentSupport"),
|
isCapabilityEnabled("EnableMongo") && !isCapabilityEnabled("EnableMongo16MBDocumentSupport"),
|
||||||
useHashV2: false,
|
useHashV1: false,
|
||||||
enableAnalyticalStore: false,
|
enableAnalyticalStore: false,
|
||||||
uniqueKeys: [],
|
uniqueKeys: [],
|
||||||
errorMessage: "",
|
errorMessage: "",
|
||||||
@@ -260,7 +262,14 @@ export class AddCollectionPanel extends React.Component<AddCollectionPanelProps,
|
|||||||
true
|
true
|
||||||
).toLocaleLowerCase()}.`}
|
).toLocaleLowerCase()}.`}
|
||||||
>
|
>
|
||||||
<Icon iconName="Info" className="panelInfoIcon" tabIndex={0} />
|
<Icon
|
||||||
|
iconName="Info"
|
||||||
|
className="panelInfoIcon"
|
||||||
|
tabIndex={0}
|
||||||
|
ariaLabel={`A database is analogous to a namespace. It is the unit of management for a set of ${getCollectionName(
|
||||||
|
true
|
||||||
|
).toLocaleLowerCase()}.`}
|
||||||
|
/>
|
||||||
</TooltipHost>
|
</TooltipHost>
|
||||||
</Stack>
|
</Stack>
|
||||||
|
|
||||||
@@ -336,7 +345,14 @@ export class AddCollectionPanel extends React.Component<AddCollectionPanelProps,
|
|||||||
true
|
true
|
||||||
).toLocaleLowerCase()} within the database.`}
|
).toLocaleLowerCase()} within the database.`}
|
||||||
>
|
>
|
||||||
<Icon iconName="Info" className="panelInfoIcon" tabIndex={0} />
|
<Icon
|
||||||
|
iconName="Info"
|
||||||
|
className="panelInfoIcon"
|
||||||
|
tabIndex={0}
|
||||||
|
ariaLabel={`Throughput configured at the database level will be shared across all ${getCollectionName(
|
||||||
|
true
|
||||||
|
).toLocaleLowerCase()} within the database.`}
|
||||||
|
/>
|
||||||
</TooltipHost>
|
</TooltipHost>
|
||||||
</Stack>
|
</Stack>
|
||||||
)}
|
)}
|
||||||
@@ -384,7 +400,12 @@ export class AddCollectionPanel extends React.Component<AddCollectionPanelProps,
|
|||||||
directionalHint={DirectionalHint.bottomLeftEdge}
|
directionalHint={DirectionalHint.bottomLeftEdge}
|
||||||
content={`Unique identifier for the ${getCollectionName().toLocaleLowerCase()} and used for id-based routing through REST and all SDKs.`}
|
content={`Unique identifier for the ${getCollectionName().toLocaleLowerCase()} and used for id-based routing through REST and all SDKs.`}
|
||||||
>
|
>
|
||||||
<Icon iconName="Info" className="panelInfoIcon" tabIndex={0} />
|
<Icon
|
||||||
|
iconName="Info"
|
||||||
|
className="panelInfoIcon"
|
||||||
|
tabIndex={0}
|
||||||
|
ariaLabel={`Unique identifier for the ${getCollectionName().toLocaleLowerCase()} and used for id-based routing through REST and all SDKs.`}
|
||||||
|
/>
|
||||||
</TooltipHost>
|
</TooltipHost>
|
||||||
</Stack>
|
</Stack>
|
||||||
|
|
||||||
@@ -467,7 +488,14 @@ export class AddCollectionPanel extends React.Component<AddCollectionPanelProps,
|
|||||||
"Sharded collections split your data across many replica sets (shards) to achieve unlimited scalability. Sharded collections require choosing a shard key (field) to evenly distribute your data."
|
"Sharded collections split your data across many replica sets (shards) to achieve unlimited scalability. Sharded collections require choosing a shard key (field) to evenly distribute your data."
|
||||||
}
|
}
|
||||||
>
|
>
|
||||||
<Icon iconName="Info" className="panelInfoIcon" tabIndex={0} />
|
<Icon
|
||||||
|
iconName="Info"
|
||||||
|
className="panelInfoIcon"
|
||||||
|
tabIndex={0}
|
||||||
|
ariaLabel={
|
||||||
|
"Sharded collections split your data across many replica sets (shards) to achieve unlimited scalability. Sharded collections require choosing a shard key (field) to evenly distribute your data."
|
||||||
|
}
|
||||||
|
/>
|
||||||
</TooltipHost>
|
</TooltipHost>
|
||||||
</Stack>
|
</Stack>
|
||||||
|
|
||||||
@@ -514,7 +542,12 @@ export class AddCollectionPanel extends React.Component<AddCollectionPanelProps,
|
|||||||
directionalHint={DirectionalHint.bottomLeftEdge}
|
directionalHint={DirectionalHint.bottomLeftEdge}
|
||||||
content={this.getPartitionKeyTooltipText()}
|
content={this.getPartitionKeyTooltipText()}
|
||||||
>
|
>
|
||||||
<Icon iconName="Info" className="panelInfoIcon" tabIndex={0} />
|
<Icon
|
||||||
|
iconName="Info"
|
||||||
|
className="panelInfoIcon"
|
||||||
|
tabIndex={0}
|
||||||
|
ariaLabel={this.getPartitionKeyTooltipText()}
|
||||||
|
/>
|
||||||
</TooltipHost>
|
</TooltipHost>
|
||||||
</Stack>
|
</Stack>
|
||||||
|
|
||||||
@@ -546,6 +579,77 @@ export class AddCollectionPanel extends React.Component<AddCollectionPanelProps,
|
|||||||
}
|
}
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
|
{userContext.apiType === "SQL" &&
|
||||||
|
this.state.subPartitionKeys.map((subPartitionKey: string, index: number) => {
|
||||||
|
return (
|
||||||
|
<Stack style={{ marginBottom: 8 }} key={`uniqueKey${index}`} horizontal>
|
||||||
|
<div
|
||||||
|
style={{
|
||||||
|
width: "20px",
|
||||||
|
border: "solid",
|
||||||
|
borderWidth: "0px 0px 1px 1px",
|
||||||
|
marginRight: "5px",
|
||||||
|
}}
|
||||||
|
></div>
|
||||||
|
<input
|
||||||
|
type="text"
|
||||||
|
id="addCollection-partitionKeyValue"
|
||||||
|
key={`addCollection-partitionKeyValue_${index}`}
|
||||||
|
aria-required
|
||||||
|
required
|
||||||
|
size={40}
|
||||||
|
tabIndex={index > 0 ? 1 : 0}
|
||||||
|
className="panelTextField"
|
||||||
|
autoComplete="off"
|
||||||
|
placeholder={this.getPartitionKeyPlaceHolder(index)}
|
||||||
|
aria-label={this.getPartitionKeyName()}
|
||||||
|
pattern={".*"}
|
||||||
|
title={""}
|
||||||
|
value={subPartitionKey}
|
||||||
|
onChange={(event: React.ChangeEvent<HTMLInputElement>) => {
|
||||||
|
const subPartitionKeys = [...this.state.subPartitionKeys];
|
||||||
|
if (!this.state.subPartitionKeys[index] && !event.target.value.startsWith("/")) {
|
||||||
|
subPartitionKeys[index] = "/" + event.target.value.trim();
|
||||||
|
this.setState({ subPartitionKeys });
|
||||||
|
} else {
|
||||||
|
subPartitionKeys[index] = event.target.value.trim();
|
||||||
|
this.setState({ subPartitionKeys });
|
||||||
|
}
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
<IconButton
|
||||||
|
iconProps={{ iconName: "Delete" }}
|
||||||
|
style={{ height: 27 }}
|
||||||
|
onClick={() => {
|
||||||
|
const subPartitionKeys = this.state.subPartitionKeys.filter((uniqueKey, j) => index !== j);
|
||||||
|
this.setState({ subPartitionKeys });
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
</Stack>
|
||||||
|
);
|
||||||
|
})}
|
||||||
|
{userContext.apiType === "SQL" && (
|
||||||
|
<Stack className="panelGroupSpacing">
|
||||||
|
<DefaultButton
|
||||||
|
styles={{ root: { padding: 0, width: 250, height: 30 }, label: { fontSize: 12 } }}
|
||||||
|
hidden={this.state.useHashV1}
|
||||||
|
disabled={this.state.subPartitionKeys.length >= Constants.BackendDefaults.maxNumMultiHashPartition}
|
||||||
|
onClick={() => this.setState({ subPartitionKeys: [...this.state.subPartitionKeys, ""] })}
|
||||||
|
>
|
||||||
|
Add hierarchical partition key (preview)
|
||||||
|
</DefaultButton>
|
||||||
|
{this.state.subPartitionKeys.length > 0 && (
|
||||||
|
<Text variant="small">
|
||||||
|
<Icon iconName="InfoSolid" className="removeIcon" tabIndex={0} /> This feature allows you to
|
||||||
|
partition your data with up to three levels of keys for better data distribution. Requires preview
|
||||||
|
version of .NET V3 or Java V4 SDK.{" "}
|
||||||
|
<Link href="https://aka.ms/cosmos-hierarchical-partitioning" target="_blank">
|
||||||
|
Learn more
|
||||||
|
</Link>
|
||||||
|
</Text>
|
||||||
|
)}
|
||||||
|
</Stack>
|
||||||
|
)}
|
||||||
</Stack>
|
</Stack>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
@@ -572,7 +676,17 @@ export class AddCollectionPanel extends React.Component<AddCollectionPanelProps,
|
|||||||
does not count towards the throughput you provisioned for the database. This throughput amount will be
|
does not count towards the throughput you provisioned for the database. This throughput amount will be
|
||||||
billed in addition to the throughput amount you provisioned at the database level.`}
|
billed in addition to the throughput amount you provisioned at the database level.`}
|
||||||
>
|
>
|
||||||
<Icon iconName="Info" className="panelInfoIcon" tabIndex={0} />
|
<Icon
|
||||||
|
iconName="Info"
|
||||||
|
className="panelInfoIcon"
|
||||||
|
tabIndex={0}
|
||||||
|
ariaLabel={`You can optionally provision dedicated throughput for a ${getCollectionName().toLocaleLowerCase()} within a database that has throughput
|
||||||
|
provisioned. This dedicated throughput amount will not be shared with other ${getCollectionName(
|
||||||
|
true
|
||||||
|
).toLocaleLowerCase()} in the database and
|
||||||
|
does not count towards the throughput you provisioned for the database. This throughput amount will be
|
||||||
|
billed in addition to the throughput amount you provisioned at the database level.`}
|
||||||
|
/>
|
||||||
</TooltipHost>
|
</TooltipHost>
|
||||||
</Stack>
|
</Stack>
|
||||||
)}
|
)}
|
||||||
@@ -603,11 +717,18 @@ export class AddCollectionPanel extends React.Component<AddCollectionPanelProps,
|
|||||||
</Text>
|
</Text>
|
||||||
<TooltipHost
|
<TooltipHost
|
||||||
directionalHint={DirectionalHint.bottomLeftEdge}
|
directionalHint={DirectionalHint.bottomLeftEdge}
|
||||||
content="Unique keys provide developers with the ability to add a layer of data integrity to their database. By
|
content={
|
||||||
creating a unique key policy when a container is created, you ensure the uniqueness of one or more values
|
"Unique keys provide developers with the ability to add a layer of data integrity to their database. By creating a unique key policy when a container is created, you ensure the uniqueness of one or more values per partition key."
|
||||||
per partition key."
|
}
|
||||||
>
|
>
|
||||||
<Icon iconName="Info" className="panelInfoIcon" tabIndex={0} />
|
<Icon
|
||||||
|
iconName="Info"
|
||||||
|
className="panelInfoIcon"
|
||||||
|
tabIndex={0}
|
||||||
|
ariaLabel={
|
||||||
|
"Unique keys provide developers with the ability to add a layer of data integrity to their database. By creating a unique key policy when a container is created, you ensure the uniqueness of one or more values per partition key."
|
||||||
|
}
|
||||||
|
/>
|
||||||
</TooltipHost>
|
</TooltipHost>
|
||||||
</Stack>
|
</Stack>
|
||||||
|
|
||||||
@@ -670,7 +791,13 @@ export class AddCollectionPanel extends React.Component<AddCollectionPanelProps,
|
|||||||
directionalHint={DirectionalHint.bottomLeftEdge}
|
directionalHint={DirectionalHint.bottomLeftEdge}
|
||||||
content={this.getAnalyticalStorageTooltipContent()}
|
content={this.getAnalyticalStorageTooltipContent()}
|
||||||
>
|
>
|
||||||
<Icon iconName="Info" className="panelInfoIcon" tabIndex={0} />
|
<Icon
|
||||||
|
iconName="Info"
|
||||||
|
className="panelInfoIcon"
|
||||||
|
tabIndex={0}
|
||||||
|
ariaLabel="Enable analytical store capability to perform near real-time analytics on your operational data, without
|
||||||
|
impacting the performance of transactional workloads."
|
||||||
|
/>
|
||||||
</TooltipHost>
|
</TooltipHost>
|
||||||
</Stack>
|
</Stack>
|
||||||
|
|
||||||
@@ -747,7 +874,12 @@ export class AddCollectionPanel extends React.Component<AddCollectionPanelProps,
|
|||||||
directionalHint={DirectionalHint.bottomLeftEdge}
|
directionalHint={DirectionalHint.bottomLeftEdge}
|
||||||
content="The _id field is indexed by default. Creating a wildcard index for all fields will optimize queries and is recommended for development."
|
content="The _id field is indexed by default. Creating a wildcard index for all fields will optimize queries and is recommended for development."
|
||||||
>
|
>
|
||||||
<Icon iconName="Info" className="panelInfoIcon" tabIndex={0} />
|
<Icon
|
||||||
|
iconName="Info"
|
||||||
|
className="panelInfoIcon"
|
||||||
|
tabIndex={0}
|
||||||
|
ariaLabel="The _id field is indexed by default. Creating a wildcard index for all fields will optimize queries and is recommended for development."
|
||||||
|
/>
|
||||||
</TooltipHost>
|
</TooltipHost>
|
||||||
</Stack>
|
</Stack>
|
||||||
|
|
||||||
@@ -767,18 +899,29 @@ export class AddCollectionPanel extends React.Component<AddCollectionPanelProps,
|
|||||||
)}
|
)}
|
||||||
|
|
||||||
{userContext.apiType === "SQL" && (
|
{userContext.apiType === "SQL" && (
|
||||||
<Checkbox
|
<Stack className="panelGroupSpacing">
|
||||||
label="My partition key is larger than 101 bytes"
|
<Checkbox
|
||||||
checked={this.state.useHashV2}
|
label="My application uses an older Cosmos .NET or Java SDK version (.NET V1 or Java V2)"
|
||||||
styles={{
|
checked={this.state.useHashV1}
|
||||||
text: { fontSize: 12 },
|
styles={{
|
||||||
checkbox: { width: 12, height: 12 },
|
text: { fontSize: 12 },
|
||||||
label: { padding: 0, alignItems: "center" },
|
checkbox: { width: 12, height: 12 },
|
||||||
}}
|
label: { padding: 0, alignItems: "center", wordWrap: "break-word", whiteSpace: "break-spaces" },
|
||||||
onChange={(ev: React.FormEvent<HTMLElement>, isChecked: boolean) =>
|
}}
|
||||||
this.setState({ useHashV2: isChecked })
|
onChange={(ev: React.FormEvent<HTMLElement>, isChecked: boolean) =>
|
||||||
}
|
this.setState({ useHashV1: isChecked, subPartitionKeys: [] })
|
||||||
/>
|
}
|
||||||
|
/>
|
||||||
|
<Text variant="small">
|
||||||
|
<Icon iconName="InfoSolid" className="removeIcon" tabIndex={0} /> To ensure compatibility with
|
||||||
|
older SDKs, the created container will use a legacy partitioning scheme that supports partition
|
||||||
|
key values of size only up to 101 bytes. If this is enabled, you will not be able to use
|
||||||
|
hierarchical partition keys.{" "}
|
||||||
|
<Link href="https://aka.ms/cosmos-large-pk" target="_blank">
|
||||||
|
Learn more
|
||||||
|
</Link>
|
||||||
|
</Text>
|
||||||
|
</Stack>
|
||||||
)}
|
)}
|
||||||
</Stack>
|
</Stack>
|
||||||
</CollapsibleSectionComponent>
|
</CollapsibleSectionComponent>
|
||||||
@@ -833,12 +976,20 @@ export class AddCollectionPanel extends React.Component<AddCollectionPanelProps,
|
|||||||
return isLowerCase ? partitionKeyName.toLocaleLowerCase() : partitionKeyName;
|
return isLowerCase ? partitionKeyName.toLocaleLowerCase() : partitionKeyName;
|
||||||
}
|
}
|
||||||
|
|
||||||
private getPartitionKeyPlaceHolder(): string {
|
private getPartitionKeyPlaceHolder(index?: number): string {
|
||||||
switch (userContext.apiType) {
|
switch (userContext.apiType) {
|
||||||
case "Mongo":
|
case "Mongo":
|
||||||
return "e.g., address.zipCode";
|
return "e.g., address.zipCode";
|
||||||
case "Gremlin":
|
case "Gremlin":
|
||||||
return "e.g., /address";
|
return "e.g., /address";
|
||||||
|
case "SQL":
|
||||||
|
return `${
|
||||||
|
index === undefined
|
||||||
|
? "Required - first partition key e.g., /TenantId"
|
||||||
|
: index === 0
|
||||||
|
? "second partition key e.g., /UserId"
|
||||||
|
: "third partition key e.g., /SessionId"
|
||||||
|
}`;
|
||||||
default:
|
default:
|
||||||
return "e.g., /address/zipCode";
|
return "e.g., /address/zipCode";
|
||||||
}
|
}
|
||||||
@@ -1164,11 +1315,16 @@ export class AddCollectionPanel extends React.Component<AddCollectionPanelProps,
|
|||||||
}
|
}
|
||||||
|
|
||||||
const uniqueKeyPolicy: DataModels.UniqueKeyPolicy = this.parseUniqueKeys();
|
const uniqueKeyPolicy: DataModels.UniqueKeyPolicy = this.parseUniqueKeys();
|
||||||
const partitionKeyVersion = this.state.useHashV2 ? 2 : undefined;
|
const partitionKeyVersion = this.state.useHashV1 ? undefined : 2;
|
||||||
const partitionKey: DataModels.PartitionKey = partitionKeyString
|
const partitionKey: DataModels.PartitionKey = partitionKeyString
|
||||||
? {
|
? {
|
||||||
paths: [partitionKeyString],
|
paths: [
|
||||||
kind: "Hash",
|
partitionKeyString,
|
||||||
|
...(userContext.apiType === "SQL" && this.state.subPartitionKeys.length > 0
|
||||||
|
? this.state.subPartitionKeys
|
||||||
|
: []),
|
||||||
|
],
|
||||||
|
kind: userContext.apiType === "SQL" && this.state.subPartitionKeys.length > 0 ? "MultiHash" : "Hash",
|
||||||
version: partitionKeyVersion,
|
version: partitionKeyVersion,
|
||||||
}
|
}
|
||||||
: undefined;
|
: undefined;
|
||||||
|
|||||||
@@ -89,8 +89,8 @@ export const DeleteDatabaseConfirmationPanel: FunctionComponent<DeleteDatabaseCo
|
|||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
setLoadingFalse();
|
setLoadingFalse();
|
||||||
setFormError(error);
|
|
||||||
const errorMessage = getErrorMessage(error);
|
const errorMessage = getErrorMessage(error);
|
||||||
|
setFormError(errorMessage);
|
||||||
TelemetryProcessor.traceFailure(
|
TelemetryProcessor.traceFailure(
|
||||||
Action.DeleteDatabase,
|
Action.DeleteDatabase,
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -48,7 +48,7 @@ export const PanelInfoErrorComponent: React.FunctionComponent<PanelInfoErrorProp
|
|||||||
)}
|
)}
|
||||||
</Text>
|
</Text>
|
||||||
{showErrorDetails && (
|
{showErrorDetails && (
|
||||||
<a className="paneErrorLink" role="link" onClick={expandConsole}>
|
<a className="paneErrorLink" role="link" onClick={expandConsole} tabIndex={0} onKeyPress={expandConsole}>
|
||||||
More details
|
More details
|
||||||
</a>
|
</a>
|
||||||
)}
|
)}
|
||||||
|
|||||||
@@ -242,6 +242,11 @@ export const AddTableEntityPanel: FunctionComponent<AddTableEntityPanelProps> =
|
|||||||
submitButtonText: getButtonLabel(userContext.apiType),
|
submitButtonText: getButtonLabel(userContext.apiType),
|
||||||
onSubmit,
|
onSubmit,
|
||||||
};
|
};
|
||||||
|
const handlekeypressaddentity = (event: React.KeyboardEvent<HTMLElement>) => {
|
||||||
|
if (event.key === "Enter" || event.key === "Space") {
|
||||||
|
addNewEntity();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<RightPaneForm {...props}>
|
<RightPaneForm {...props}>
|
||||||
@@ -284,7 +289,13 @@ export const AddTableEntityPanel: FunctionComponent<AddTableEntityPanelProps> =
|
|||||||
);
|
);
|
||||||
})}
|
})}
|
||||||
{userContext.apiType !== "Cassandra" && (
|
{userContext.apiType !== "Cassandra" && (
|
||||||
<Stack horizontal onClick={addNewEntity} className="addButtonEntiy">
|
<Stack
|
||||||
|
horizontal
|
||||||
|
onClick={addNewEntity}
|
||||||
|
className="addButtonEntiy"
|
||||||
|
tabIndex={0}
|
||||||
|
onKeyPress={handlekeypressaddentity}
|
||||||
|
>
|
||||||
<Image {...imageProps} src={AddPropertyIcon} alt="Add Entity" />
|
<Image {...imageProps} src={AddPropertyIcon} alt="Add Entity" />
|
||||||
<Text className="addNewParamStyle">{getAddButtonLabel(userContext.apiType)}</Text>
|
<Text className="addNewParamStyle">{getAddButtonLabel(userContext.apiType)}</Text>
|
||||||
</Stack>
|
</Stack>
|
||||||
|
|||||||
@@ -29,10 +29,14 @@ exports[`Excute Add Table Entity Pane should render Default properly 1`] = `
|
|||||||
className="addButtonEntiy"
|
className="addButtonEntiy"
|
||||||
horizontal={true}
|
horizontal={true}
|
||||||
onClick={[Function]}
|
onClick={[Function]}
|
||||||
|
onKeyPress={[Function]}
|
||||||
|
tabIndex={0}
|
||||||
>
|
>
|
||||||
<div
|
<div
|
||||||
className="ms-Stack addButtonEntiy css-53"
|
className="ms-Stack addButtonEntiy css-53"
|
||||||
onClick={[Function]}
|
onClick={[Function]}
|
||||||
|
onKeyPress={[Function]}
|
||||||
|
tabIndex={0}
|
||||||
>
|
>
|
||||||
<StyledImageBase
|
<StyledImageBase
|
||||||
alt="Add Entity"
|
alt="Add Entity"
|
||||||
|
|||||||
@@ -31,6 +31,7 @@ exports[`AddCollectionPanel should render Default properly 1`] = `
|
|||||||
directionalHint={4}
|
directionalHint={4}
|
||||||
>
|
>
|
||||||
<Icon
|
<Icon
|
||||||
|
ariaLabel="A database is analogous to a namespace. It is the unit of management for a set of containers."
|
||||||
className="panelInfoIcon"
|
className="panelInfoIcon"
|
||||||
iconName="Info"
|
iconName="Info"
|
||||||
tabIndex={0}
|
tabIndex={0}
|
||||||
@@ -124,6 +125,7 @@ exports[`AddCollectionPanel should render Default properly 1`] = `
|
|||||||
directionalHint={4}
|
directionalHint={4}
|
||||||
>
|
>
|
||||||
<Icon
|
<Icon
|
||||||
|
ariaLabel="Throughput configured at the database level will be shared across all containers within the database."
|
||||||
className="panelInfoIcon"
|
className="panelInfoIcon"
|
||||||
iconName="Info"
|
iconName="Info"
|
||||||
tabIndex={0}
|
tabIndex={0}
|
||||||
@@ -163,6 +165,7 @@ exports[`AddCollectionPanel should render Default properly 1`] = `
|
|||||||
directionalHint={4}
|
directionalHint={4}
|
||||||
>
|
>
|
||||||
<Icon
|
<Icon
|
||||||
|
ariaLabel="Unique identifier for the container and used for id-based routing through REST and all SDKs."
|
||||||
className="panelInfoIcon"
|
className="panelInfoIcon"
|
||||||
iconName="Info"
|
iconName="Info"
|
||||||
tabIndex={0}
|
tabIndex={0}
|
||||||
@@ -206,6 +209,7 @@ exports[`AddCollectionPanel should render Default properly 1`] = `
|
|||||||
directionalHint={4}
|
directionalHint={4}
|
||||||
>
|
>
|
||||||
<Icon
|
<Icon
|
||||||
|
ariaLabel="The partition key is used to automatically distribute data across partitions for scalability. Choose a property in your JSON document that has a wide range of values and evenly distributes request volume. For small read-heavy workloads or write-heavy workloads of any size, id is often a good choice."
|
||||||
className="panelInfoIcon"
|
className="panelInfoIcon"
|
||||||
iconName="Info"
|
iconName="Info"
|
||||||
tabIndex={0}
|
tabIndex={0}
|
||||||
@@ -223,13 +227,36 @@ exports[`AddCollectionPanel should render Default properly 1`] = `
|
|||||||
id="addCollection-partitionKeyValue"
|
id="addCollection-partitionKeyValue"
|
||||||
onChange={[Function]}
|
onChange={[Function]}
|
||||||
pattern=".*"
|
pattern=".*"
|
||||||
placeholder="e.g., /address/zipCode"
|
placeholder="Required - first partition key e.g., /TenantId"
|
||||||
required={true}
|
required={true}
|
||||||
size={40}
|
size={40}
|
||||||
title=""
|
title=""
|
||||||
type="text"
|
type="text"
|
||||||
value=""
|
value=""
|
||||||
/>
|
/>
|
||||||
|
<Stack
|
||||||
|
className="panelGroupSpacing"
|
||||||
|
>
|
||||||
|
<CustomizedDefaultButton
|
||||||
|
disabled={false}
|
||||||
|
hidden={false}
|
||||||
|
onClick={[Function]}
|
||||||
|
styles={
|
||||||
|
Object {
|
||||||
|
"label": Object {
|
||||||
|
"fontSize": 12,
|
||||||
|
},
|
||||||
|
"root": Object {
|
||||||
|
"height": 30,
|
||||||
|
"padding": 0,
|
||||||
|
"width": 250,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
>
|
||||||
|
Add hierarchical partition key (preview)
|
||||||
|
</CustomizedDefaultButton>
|
||||||
|
</Stack>
|
||||||
</Stack>
|
</Stack>
|
||||||
<Stack>
|
<Stack>
|
||||||
<Stack
|
<Stack
|
||||||
@@ -246,6 +273,7 @@ exports[`AddCollectionPanel should render Default properly 1`] = `
|
|||||||
directionalHint={4}
|
directionalHint={4}
|
||||||
>
|
>
|
||||||
<Icon
|
<Icon
|
||||||
|
ariaLabel="Unique keys provide developers with the ability to add a layer of data integrity to their database. By creating a unique key policy when a container is created, you ensure the uniqueness of one or more values per partition key."
|
||||||
className="panelInfoIcon"
|
className="panelInfoIcon"
|
||||||
iconName="Info"
|
iconName="Info"
|
||||||
tabIndex={0}
|
tabIndex={0}
|
||||||
@@ -303,6 +331,7 @@ exports[`AddCollectionPanel should render Default properly 1`] = `
|
|||||||
directionalHint={4}
|
directionalHint={4}
|
||||||
>
|
>
|
||||||
<Icon
|
<Icon
|
||||||
|
ariaLabel="Enable analytical store capability to perform near real-time analytics on your operational data, without impacting the performance of transactional workloads."
|
||||||
className="panelInfoIcon"
|
className="panelInfoIcon"
|
||||||
iconName="Info"
|
iconName="Info"
|
||||||
tabIndex={0}
|
tabIndex={0}
|
||||||
@@ -396,26 +425,49 @@ exports[`AddCollectionPanel should render Default properly 1`] = `
|
|||||||
className="panelGroupSpacing"
|
className="panelGroupSpacing"
|
||||||
id="collapsibleSectionContent"
|
id="collapsibleSectionContent"
|
||||||
>
|
>
|
||||||
<StyledCheckboxBase
|
<Stack
|
||||||
checked={false}
|
className="panelGroupSpacing"
|
||||||
label="My partition key is larger than 101 bytes"
|
>
|
||||||
onChange={[Function]}
|
<StyledCheckboxBase
|
||||||
styles={
|
checked={false}
|
||||||
Object {
|
label="My application uses an older Cosmos .NET or Java SDK version (.NET V1 or Java V2)"
|
||||||
"checkbox": Object {
|
onChange={[Function]}
|
||||||
"height": 12,
|
styles={
|
||||||
"width": 12,
|
Object {
|
||||||
},
|
"checkbox": Object {
|
||||||
"label": Object {
|
"height": 12,
|
||||||
"alignItems": "center",
|
"width": 12,
|
||||||
"padding": 0,
|
},
|
||||||
},
|
"label": Object {
|
||||||
"text": Object {
|
"alignItems": "center",
|
||||||
"fontSize": 12,
|
"padding": 0,
|
||||||
},
|
"whiteSpace": "break-spaces",
|
||||||
|
"wordWrap": "break-word",
|
||||||
|
},
|
||||||
|
"text": Object {
|
||||||
|
"fontSize": 12,
|
||||||
|
},
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
/>
|
||||||
/>
|
<Text
|
||||||
|
variant="small"
|
||||||
|
>
|
||||||
|
<Icon
|
||||||
|
className="removeIcon"
|
||||||
|
iconName="InfoSolid"
|
||||||
|
tabIndex={0}
|
||||||
|
/>
|
||||||
|
To ensure compatibility with older SDKs, the created container will use a legacy partitioning scheme that supports partition key values of size only up to 101 bytes. If this is enabled, you will not be able to use hierarchical partition keys.
|
||||||
|
|
||||||
|
<StyledLinkBase
|
||||||
|
href="https://aka.ms/cosmos-large-pk"
|
||||||
|
target="_blank"
|
||||||
|
>
|
||||||
|
Learn more
|
||||||
|
</StyledLinkBase>
|
||||||
|
</Text>
|
||||||
|
</Stack>
|
||||||
</Stack>
|
</Stack>
|
||||||
</CollapsibleSectionComponent>
|
</CollapsibleSectionComponent>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -9,7 +9,7 @@ import {
|
|||||||
Stack,
|
Stack,
|
||||||
TeachingBubble,
|
TeachingBubble,
|
||||||
TeachingBubbleContent,
|
TeachingBubbleContent,
|
||||||
Text,
|
Text
|
||||||
} from "@fluentui/react";
|
} from "@fluentui/react";
|
||||||
import { sendMessage } from "Common/MessageHandler";
|
import { sendMessage } from "Common/MessageHandler";
|
||||||
import { MessageTypes } from "Contracts/ExplorerContracts";
|
import { MessageTypes } from "Contracts/ExplorerContracts";
|
||||||
@@ -528,7 +528,7 @@ export class SplashScreen extends React.Component<SplashScreenProps> {
|
|||||||
>
|
>
|
||||||
{item.title}
|
{item.title}
|
||||||
</Link>
|
</Link>
|
||||||
<Image src={LinkIcon} />
|
<Image src={LinkIcon} alt=" " />
|
||||||
</Stack>
|
</Stack>
|
||||||
<Text>{item.description}</Text>
|
<Text>{item.description}</Text>
|
||||||
</Stack>
|
</Stack>
|
||||||
|
|||||||
@@ -516,7 +516,7 @@ export default class QueryBuilderViewModel {
|
|||||||
};
|
};
|
||||||
|
|
||||||
public onAddNewClauseKeyDown = (event: KeyboardEvent): boolean => {
|
public onAddNewClauseKeyDown = (event: KeyboardEvent): boolean => {
|
||||||
if (event.keyCode === KeyCodes.Enter || event.keyCode === KeyCodes.Space) {
|
if (event.key === "Enter" || event.key === "Space") {
|
||||||
this.addClauseIndex(this.clauseArray().length - 1);
|
this.addClauseIndex(this.clauseArray().length - 1);
|
||||||
event.stopPropagation();
|
event.stopPropagation();
|
||||||
return false;
|
return false;
|
||||||
|
|||||||
@@ -70,24 +70,19 @@
|
|||||||
<tbody data-bind="template: { name: 'queryClause-template', foreach: clauseArray, as: 'clause' }"></tbody>
|
<tbody data-bind="template: { name: 'queryClause-template', foreach: clauseArray, as: 'clause' }"></tbody>
|
||||||
</table>
|
</table>
|
||||||
</div>
|
</div>
|
||||||
<div
|
<button
|
||||||
class="addClause"
|
data-bind="click: addNewClause, event: { keydown: onAddNewClauseKeyDown }"
|
||||||
role="button"
|
style="border: none; background: none"
|
||||||
data-bind="click: addNewClause, event: { keydown: onAddNewClauseKeyDown }, attr: { title: addNewClauseLine }"
|
|
||||||
tabindex="0"
|
|
||||||
>
|
>
|
||||||
<div class="addClause-heading">
|
<div class="addClause" data-bind=" ">
|
||||||
<span class="clause-table addClause-title">
|
<div class="addClause-heading">
|
||||||
<img
|
<span class="clause-table addClause-title">
|
||||||
class="addclauseProperty-Img"
|
<img class="addclauseProperty-Img" style="margin-bottom: 5px" src="/Add-property.svg" />
|
||||||
style="margin-bottom: 5px"
|
<span style="margin-left: 5px" data-bind="text: addNewClauseLine"></span>
|
||||||
src="/Add-property.svg"
|
</span>
|
||||||
alt="Add new clause"
|
</div>
|
||||||
/>
|
|
||||||
<span style="margin-left: 5px" data-bind="text: addNewClauseLine"></span>
|
|
||||||
</span>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<!-- Tables Query Tab Query Helper - End-->
|
<!-- Tables Query Tab Query Helper - End-->
|
||||||
@@ -168,22 +163,20 @@
|
|||||||
<script type="text/html" id="queryClause-template">
|
<script type="text/html" id="queryClause-template">
|
||||||
<tr class="clause-table-row">
|
<tr class="clause-table-row">
|
||||||
<td class="clause-table-cell action-column">
|
<td class="clause-table-cell action-column">
|
||||||
<span
|
<button
|
||||||
class="entity-Add-Cancel"
|
|
||||||
role="button"
|
|
||||||
tabindex="0"
|
|
||||||
data-bind="click: $parent.addClauseIndex.bind($data, $index()), event: { keydown: $parent.onAddClauseKeyDown.bind($data, $index()) }, attr:{title: $parent.insertNewFilterLine}"
|
data-bind="click: $parent.addClauseIndex.bind($data, $index()), event: { keydown: $parent.onAddClauseKeyDown.bind($data, $index()) }, attr:{title: $parent.insertNewFilterLine}"
|
||||||
>
|
>
|
||||||
<img class="querybuilder-addpropertyImg" src="/Add-property.svg" alt="Add clause" />
|
<span class="entity-Add-Cancel" role="button">
|
||||||
</span>
|
<img class="querybuilder-addpropertyImg" src="/Add-property.svg" alt="Add clause" />
|
||||||
<span
|
</span>
|
||||||
class="entity-Add-Cancel"
|
</button>
|
||||||
role="button"
|
<button
|
||||||
tabindex="0"
|
|
||||||
data-bind="hasFocus: isDeleteButtonFocused, click: $parent.deleteClause.bind($data, $index()), event: { keydown: $parent.onDeleteClauseKeyDown.bind($data, $index()) }, attr:{title: $parent.removeThisFilterLine}"
|
data-bind="hasFocus: isDeleteButtonFocused, click: $parent.deleteClause.bind($data, $index()), event: { keydown: $parent.onDeleteClauseKeyDown.bind($data, $index()) }, attr:{title: $parent.removeThisFilterLine}"
|
||||||
>
|
>
|
||||||
<img class="querybuilder-cancelImg" src="/Entity_cancel.svg" alt="Delete clause" />
|
<span class="entity-Add-Cancel" role="button">
|
||||||
</span>
|
<img class="querybuilder-cancelImg" src="/Entity_cancel.svg" alt="Delete clause" />
|
||||||
|
</span>
|
||||||
|
</button>
|
||||||
</td>
|
</td>
|
||||||
<td class="clause-table-cell group-control-column">
|
<td class="clause-table-cell group-control-column">
|
||||||
<input type="checkbox" aria-label="And/Or" data-bind="checked: checkedForGrouping" />
|
<input type="checkbox" aria-label="And/Or" data-bind="checked: checkedForGrouping" />
|
||||||
|
|||||||
@@ -24,22 +24,21 @@ interface TabsProps {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export const Tabs = ({ explorer }: TabsProps): JSX.Element => {
|
export const Tabs = ({ explorer }: TabsProps): JSX.Element => {
|
||||||
const { openedTabs, openedReactTabs, activeTab, activeReactTab, showNetworkSettingsWarning } = useTabs();
|
const { openedTabs, openedReactTabs, activeTab, activeReactTab, networkSettingsWarning } = useTabs();
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="tabsManagerContainer">
|
<div className="tabsManagerContainer">
|
||||||
{showNetworkSettingsWarning && (
|
{networkSettingsWarning && (
|
||||||
<MessageBar
|
<MessageBar
|
||||||
messageBarType={MessageBarType.warning}
|
messageBarType={MessageBarType.warning}
|
||||||
actions={
|
actions={
|
||||||
<MessageBarButton onClick={() => sendMessage({ type: MessageTypes.OpenCosmosDBNetworkingBlade })}>
|
<MessageBarButton onClick={() => sendMessage({ type: MessageTypes.OpenPostgresNetworkingBlade })}>
|
||||||
Change network settings
|
Change network settings
|
||||||
</MessageBarButton>
|
</MessageBarButton>
|
||||||
}
|
}
|
||||||
messageBarIconProps={{ iconName: "WarningSolid", className: "messageBarWarningIcon" }}
|
messageBarIconProps={{ iconName: "WarningSolid", className: "messageBarWarningIcon" }}
|
||||||
>
|
>
|
||||||
The Network settings for this account are preventing access from Data Explorer. Please allow access from Azure
|
{networkSettingsWarning}
|
||||||
Portal to proceed.
|
|
||||||
</MessageBar>
|
</MessageBar>
|
||||||
)}
|
)}
|
||||||
<div id="content" className="flexContainer hideOverflows">
|
<div id="content" className="flexContainer hideOverflows">
|
||||||
|
|||||||
@@ -29,6 +29,7 @@ export type Features = {
|
|||||||
readonly mongoProxyEndpoint?: string;
|
readonly mongoProxyEndpoint?: string;
|
||||||
readonly mongoProxyAPIs?: string;
|
readonly mongoProxyAPIs?: string;
|
||||||
readonly enableThroughputCap: boolean;
|
readonly enableThroughputCap: boolean;
|
||||||
|
readonly enableHierarchicalKeys: boolean;
|
||||||
|
|
||||||
// can be set via both flight and feature flag
|
// can be set via both flight and feature flag
|
||||||
autoscaleDefault: boolean;
|
autoscaleDefault: boolean;
|
||||||
@@ -90,6 +91,7 @@ export function extractFeatures(given = new URLSearchParams(window.location.sear
|
|||||||
partitionKeyDefault2: "true" === get("pkpartitionkeytest"),
|
partitionKeyDefault2: "true" === get("pkpartitionkeytest"),
|
||||||
notebooksDownBanner: "true" === get("notebooksDownBanner"),
|
notebooksDownBanner: "true" === get("notebooksDownBanner"),
|
||||||
enableThroughputCap: "true" === get("enablethroughputcap"),
|
enableThroughputCap: "true" === get("enablethroughputcap"),
|
||||||
|
enableHierarchicalKeys: "true" === get("enablehierarchicalkeys"),
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -10,31 +10,43 @@ const PortalIPs: { [key: string]: string[] } = {
|
|||||||
usnat: ["7.28.202.68"],
|
usnat: ["7.28.202.68"],
|
||||||
};
|
};
|
||||||
|
|
||||||
export const doNetworkSettingsAllowDataExplorerAccess = (): boolean => {
|
export const getNetworkSettingsWarningMessage = (clientIpAddress: string): string => {
|
||||||
const accountProperties = userContext.databaseAccount?.properties;
|
const accountProperties = userContext.databaseAccount?.properties;
|
||||||
|
|
||||||
if (!accountProperties) {
|
if (!accountProperties) {
|
||||||
return false;
|
return "";
|
||||||
}
|
}
|
||||||
|
|
||||||
// public network access is disabled
|
// public network access is disabled
|
||||||
if (accountProperties.publicNetworkAccess !== "Enabled") {
|
if (accountProperties.publicNetworkAccess !== "Enabled") {
|
||||||
return false;
|
return "The Network settings for this account are preventing access from Data Explorer. Please enable public access to proceed.";
|
||||||
}
|
}
|
||||||
|
|
||||||
const ipRules = accountProperties.ipRules;
|
const ipRules = accountProperties.ipRules;
|
||||||
// public network access is set to "All networks"
|
// public network access is set to "All networks"
|
||||||
if (ipRules.length === 0) {
|
if (ipRules.length === 0) {
|
||||||
return true;
|
return "";
|
||||||
}
|
}
|
||||||
|
|
||||||
const portalIPs = PortalIPs[userContext.portalEnv];
|
if (userContext.apiType === "Cassandra" || userContext.apiType === "Mongo") {
|
||||||
let numberOfMatches = 0;
|
const portalIPs = PortalIPs[userContext.portalEnv];
|
||||||
ipRules.forEach((ipRule) => {
|
let numberOfMatches = 0;
|
||||||
if (portalIPs.indexOf(ipRule.ipAddressOrRange) !== -1) {
|
ipRules.forEach((ipRule) => {
|
||||||
numberOfMatches++;
|
if (portalIPs.indexOf(ipRule.ipAddressOrRange) !== -1) {
|
||||||
}
|
numberOfMatches++;
|
||||||
});
|
}
|
||||||
|
});
|
||||||
|
|
||||||
return numberOfMatches === portalIPs.length;
|
if (numberOfMatches !== portalIPs.length) {
|
||||||
|
return "The Network settings for this account are preventing access from Data Explorer. Please allow access from Azure Portal to proceed.";
|
||||||
|
}
|
||||||
|
|
||||||
|
return "";
|
||||||
|
} else {
|
||||||
|
if (!clientIpAddress || ipRules.some((ipRule) => ipRule.ipAddressOrRange === clientIpAddress)) {
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
|
||||||
|
return "The Network settings for this account are preventing access from Data Explorer. Please add your current IP to the firewall rules to proceed.";
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
import { ReactTabKind, useTabs } from "hooks/useTabs";
|
import { ReactTabKind, useTabs } from "hooks/useTabs";
|
||||||
import { useEffect, useState } from "react";
|
import { useEffect, useState } from "react";
|
||||||
import { doNetworkSettingsAllowDataExplorerAccess } from "Utils/NetworkUtility";
|
import { getNetworkSettingsWarningMessage } from "Utils/NetworkUtility";
|
||||||
import { applyExplorerBindings } from "../applyExplorerBindings";
|
import { applyExplorerBindings } from "../applyExplorerBindings";
|
||||||
import { AuthType } from "../AuthType";
|
import { AuthType } from "../AuthType";
|
||||||
import { AccountKind, Flights } from "../Common/Constants";
|
import { AccountKind, Flights } from "../Common/Constants";
|
||||||
@@ -382,7 +382,8 @@ function updateContextsFromPortalMessage(inputs: DataExplorerInputsFrame) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
useTabs.getState().setShowNetworkSettingsWarning(!doNetworkSettingsAllowDataExplorerAccess());
|
const warningMessage = getNetworkSettingsWarningMessage(inputs.clientIpAddress);
|
||||||
|
useTabs.getState().setNetworkSettingsWarning(warningMessage);
|
||||||
|
|
||||||
if (inputs.features) {
|
if (inputs.features) {
|
||||||
Object.assign(userContext.features, extractFeatures(new URLSearchParams(inputs.features)));
|
Object.assign(userContext.features, extractFeatures(new URLSearchParams(inputs.features)));
|
||||||
|
|||||||
@@ -9,7 +9,7 @@ interface TabsState {
|
|||||||
openedReactTabs: ReactTabKind[];
|
openedReactTabs: ReactTabKind[];
|
||||||
activeTab: TabsBase | undefined;
|
activeTab: TabsBase | undefined;
|
||||||
activeReactTab: ReactTabKind | undefined;
|
activeReactTab: ReactTabKind | undefined;
|
||||||
showNetworkSettingsWarning: boolean;
|
networkSettingsWarning: string;
|
||||||
activateTab: (tab: TabsBase) => void;
|
activateTab: (tab: TabsBase) => void;
|
||||||
activateNewTab: (tab: TabsBase) => void;
|
activateNewTab: (tab: TabsBase) => void;
|
||||||
activateReactTab: (tabkind: ReactTabKind) => void;
|
activateReactTab: (tabkind: ReactTabKind) => void;
|
||||||
@@ -21,7 +21,7 @@ interface TabsState {
|
|||||||
closeAllNotebookTabs: (hardClose: boolean) => void;
|
closeAllNotebookTabs: (hardClose: boolean) => void;
|
||||||
openAndActivateReactTab: (tabKind: ReactTabKind) => void;
|
openAndActivateReactTab: (tabKind: ReactTabKind) => void;
|
||||||
closeReactTab: (tabKind: ReactTabKind) => void;
|
closeReactTab: (tabKind: ReactTabKind) => void;
|
||||||
setShowNetworkSettingsWarning: (showWarning: boolean) => void;
|
setNetworkSettingsWarning: (warningMessage: string) => void;
|
||||||
}
|
}
|
||||||
|
|
||||||
export enum ReactTabKind {
|
export enum ReactTabKind {
|
||||||
@@ -35,7 +35,7 @@ export const useTabs: UseStore<TabsState> = create((set, get) => ({
|
|||||||
openedReactTabs: [ReactTabKind.Home],
|
openedReactTabs: [ReactTabKind.Home],
|
||||||
activeTab: undefined,
|
activeTab: undefined,
|
||||||
activeReactTab: ReactTabKind.Home,
|
activeReactTab: ReactTabKind.Home,
|
||||||
showNetworkSettingsWarning: false,
|
networkSettingsWarning: "",
|
||||||
activateTab: (tab: TabsBase): void => {
|
activateTab: (tab: TabsBase): void => {
|
||||||
if (get().openedTabs.some((openedTab) => openedTab.tabId === tab.tabId)) {
|
if (get().openedTabs.some((openedTab) => openedTab.tabId === tab.tabId)) {
|
||||||
set({ activeTab: tab, activeReactTab: undefined });
|
set({ activeTab: tab, activeReactTab: undefined });
|
||||||
@@ -145,5 +145,5 @@ export const useTabs: UseStore<TabsState> = create((set, get) => ({
|
|||||||
|
|
||||||
set({ openedReactTabs: updatedOpenedReactTabs });
|
set({ openedReactTabs: updatedOpenedReactTabs });
|
||||||
},
|
},
|
||||||
setShowNetworkSettingsWarning: (showWarning: boolean) => set({ showNetworkSettingsWarning: showWarning }),
|
setNetworkSettingsWarning: (warningMessage: string) => set({ networkSettingsWarning: warningMessage }),
|
||||||
}));
|
}));
|
||||||
|
|||||||
Reference in New Issue
Block a user