Compare commits

..

2 Commits

Author SHA1 Message Date
Victor Meng
acd5974024 Hard code to always open old mongo shell 2022-12-07 10:21:08 -08:00
Victor Meng
22f5fc13b2 Empty commit to trigger new build 2022-11-14 12:42:07 -08:00
34 changed files with 394 additions and 692 deletions

View File

@@ -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,14 +388,15 @@ 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;
} }
@@ -409,111 +410,112 @@ 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 {
@@ -522,4 +524,4 @@ input::-webkit-inner-spin-button {
width: 100%; width: 100%;
padding-top: 10px; padding-top: 10px;
} }
}*/ }*/

View File

@@ -45,8 +45,6 @@ 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;

View File

@@ -73,17 +73,6 @@ 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;
@@ -137,28 +126,12 @@ export const TableEntity: FunctionComponent<TableEntityProps> = ({
/> />
{!isEntityValueDisable && ( {!isEntityValueDisable && (
<TooltipHost content="Edit property" id="editTooltip"> <TooltipHost content="Edit property" id="editTooltip">
<Image <Image {...imageProps} src={EditIcon} alt="editEntity" id="editEntity" onClick={onEditEntity} />
{...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 <Image {...imageProps} src={DeleteIcon} alt="delete entity" id="deleteEntity" onClick={onDeleteEntity} />
{...imageProps}
src={DeleteIcon}
alt="delete entity"
id="deleteEntity"
onClick={onDeleteEntity}
tabIndex={0}
onKeyPress={handleKeyPressdelete}
/>
</TooltipHost> </TooltipHost>
)} )}
</Stack> </Stack>

View File

@@ -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={children} className="panelInfoIcon" tabIndex={0} /> <Icon iconName="Info" ariaLabel="Info" className="panelInfoIcon" tabIndex={0} />
</TooltipHost> </TooltipHost>
</span> </span>
); );

View File

@@ -4,7 +4,6 @@ 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();
@@ -14,7 +13,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(), getPartitionKeyValue(documentId)) .item(documentId.id(), documentId.partitionKeyValue?.length === 0 ? undefined : documentId.partitionKeyValue)
.delete(); .delete();
logConsoleInfo(`Successfully deleted ${entityName} ${documentId.id()}`); logConsoleInfo(`Successfully deleted ${entityName} ${documentId.id()}`);
} catch (error) { } catch (error) {

View File

@@ -1,12 +0,0 @@
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;
};

View File

@@ -6,7 +6,6 @@ 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();
@@ -22,7 +21,8 @@ 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())
.item(documentId.id(), getPartitionKeyValue(documentId)) // use undefined if the partitionKeyValue is empty
.item(documentId.id(), documentId.partitionKeyValue?.length === 0 ? undefined : documentId.partitionKeyValue)
.read(options); .read(options);
return response?.resource; return response?.resource;

View File

@@ -6,7 +6,6 @@ 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,
@@ -26,7 +25,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(), getPartitionKeyValue(documentId)) .item(documentId.id(), documentId.partitionKeyValue?.length === 0 ? undefined : documentId.partitionKeyValue)
.replace(newDocument, options); .replace(newDocument, options);
logConsoleInfo(`Successfully updated ${entityName} ${documentId.id()}`); logConsoleInfo(`Successfully updated ${entityName} ${documentId.id()}`);

View File

@@ -397,7 +397,6 @@ 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[];

View File

@@ -85,11 +85,7 @@ export const createCollectionContextMenuButton = (
iconSrc: HostedTerminalIcon, iconSrc: HostedTerminalIcon,
onClick: () => { onClick: () => {
const selectedCollection: ViewModels.Collection = useSelectedNode.getState().findSelectedCollection(); const selectedCollection: ViewModels.Collection = useSelectedNode.getState().findSelectedCollection();
if (useNotebook.getState().isShellEnabled) { selectedCollection && selectedCollection.onNewMongoShellClick();
container.openNotebookTerminal(ViewModels.TerminalKind.Mongo);
} else {
selectedCollection && selectedCollection.onNewMongoShellClick();
}
}, },
label: useNotebook.getState().isShellEnabled ? "Open Mongo Shell" : "New Shell", label: useNotebook.getState().isShellEnabled ? "Open Mongo Shell" : "New Shell",
}); });

View File

@@ -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" tabIndex={0}> <div className="infoPanelMain">
<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>

View File

@@ -12,7 +12,6 @@ exports[`InfoComponent renders 1`] = `
> >
<div <div
className="infoPanelMain" className="infoPanelMain"
tabIndex={0}
> >
<Icon <Icon
className="infoIconMain" className="infoIconMain"

View File

@@ -310,9 +310,7 @@ export class SubSettingsComponent extends React.Component<SubSettingsComponentPr
/> />
)} )}
{userContext.apiType === "SQL" && this.isLargePartitionKeyEnabled() && ( {this.isLargePartitionKeyEnabled() && <Text>Large {this.partitionKeyName.toLowerCase()} has been enabled</Text>}
<Text>Large {this.partitionKeyName.toLowerCase()} has been enabled</Text>
)}
</Stack> </Stack>
); );

View File

@@ -82,6 +82,7 @@ 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
@@ -623,10 +624,7 @@ export class ThroughputInputAutoPilotV3Component extends React.Component<
return ( return (
<> <>
{warningMessage && ( {warningMessage && (
<MessageBar <MessageBar messageBarIconProps={{ iconName: "WarningSolid", className: "messageBarWarningIcon" }}>
messageBarIconProps={{ iconName: "WarningSolid", className: "messageBarWarningIcon" }}
role="alert"
>
{warningMessage} {warningMessage}
</MessageBar> </MessageBar>
)} )}

View File

@@ -15,7 +15,6 @@ exports[`ThroughputInputAutoPilotV3Component autopilot input visible 1`] = `
"iconName": "WarningSolid", "iconName": "WarningSolid",
} }
} }
role="alert"
> >
<Text <Text
styles={ styles={

View File

@@ -23,12 +23,12 @@ describe("ThroughputInput Pane", () => {
}); });
it("should switch mode properly", () => { it("should switch mode properly", () => {
wrapper.find('[aria-label="Manual database throughput"]').simulate("change"); wrapper.find('[aria-label="Manual mode"]').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 database throughput"]').simulate("change"); wrapper.find('[aria-label="Autoscale mode"]').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)");
}); });
}); });

View File

@@ -186,9 +186,8 @@ 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 database throughput" aria-label="Autoscale mode"
aria-required={true} aria-required={true}
checked={isAutoscaleSelected} checked={isAutoscaleSelected}
type="radio" type="radio"
@@ -196,14 +195,11 @@ export const ThroughputInput: FunctionComponent<ThroughputInputProps> = ({
tabIndex={0} tabIndex={0}
onChange={(e) => handleOnChangeMode(e, "Autoscale")} onChange={(e) => handleOnChangeMode(e, "Autoscale")}
/> />
<label htmlFor="Autoscale-input" className="throughputInputRadioBtnLabel"> <span className="throughputInputRadioBtnLabel">Autoscale</span>
Autoscale
</label>
<input <input
id="Manual-input"
className="throughputInputRadioBtn" className="throughputInputRadioBtn"
aria-label="Manual database throughput" aria-label="Manual mode"
checked={!isAutoscaleSelected} checked={!isAutoscaleSelected}
type="radio" type="radio"
aria-required={true} aria-required={true}
@@ -211,20 +207,14 @@ export const ThroughputInput: FunctionComponent<ThroughputInputProps> = ({
tabIndex={0} tabIndex={0}
onChange={(e) => handleOnChangeMode(e, "Manual")} onChange={(e) => handleOnChangeMode(e, "Manual")}
/> />
<label className="throughputInputRadioBtnLabel" htmlFor="Manual-input"> <span className="throughputInputRadioBtnLabel">Manual</span>
Manual
</label>
</Stack> </Stack>
{isAutoscaleSelected && ( {isAutoscaleSelected && (
<Stack className="throughputInputSpacing"> <Stack className="throughputInputSpacing">
<Text variant="small" aria-label="capacity calculator of azure cosmos db"> <Text variant="small" aria-label="ruDescription">
Estimate your required RU/s with{" "} Estimate your required RU/s with{" "}
<Link <Link target="_blank" href="https://cosmos.azure.com/capacitycalculator/" aria-label="ruDescription">
target="_blank"
href="https://cosmos.azure.com/capacitycalculator/"
aria-label="capacity calculator of azure cosmos db"
>
capacity calculator capacity calculator
</Link> </Link>
. .

View File

@@ -344,13 +344,13 @@ exports[`ThroughputInput Pane should render Default properly 1`] = `
onMouseLeave={[Function]} onMouseLeave={[Function]}
> >
<StyledIconBase <StyledIconBase
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." ariaLabel="Info"
className="panelInfoIcon" className="panelInfoIcon"
iconName="Info" iconName="Info"
tabIndex={0} tabIndex={0}
> >
<IconBase <IconBase
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." ariaLabel="Info"
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="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." aria-label="Info"
className="panelInfoIcon root-57" className="panelInfoIcon root-57"
data-icon-name="Info" data-icon-name="Info"
role="img" role="img"
@@ -655,43 +655,39 @@ exports[`ThroughputInput Pane should render Default properly 1`] = `
className="ms-Stack css-58" className="ms-Stack css-58"
> >
<input <input
aria-label="Autoscale database throughput" aria-label="Autoscale mode"
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"
/> />
<label <span
className="throughputInputRadioBtnLabel" className="throughputInputRadioBtnLabel"
htmlFor="Autoscale-input"
key=".0:$.1" key=".0:$.1"
> >
Autoscale Autoscale
</label> </span>
<input <input
aria-label="Manual database throughput" aria-label="Manual mode"
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"
/> />
<label <span
className="throughputInputRadioBtnLabel" className="throughputInputRadioBtnLabel"
htmlFor="Manual-input"
key=".0:$.3" key=".0:$.3"
> >
Manual Manual
</label> </span>
</div> </div>
</Stack> </Stack>
<Stack <Stack
@@ -701,23 +697,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="capacity calculator of azure cosmos db" aria-label="ruDescription"
key=".0:$.0" key=".0:$.0"
variant="small" variant="small"
> >
<span <span
aria-label="capacity calculator of azure cosmos db" aria-label="ruDescription"
className="css-54" className="css-54"
> >
Estimate your required RU/s with Estimate your required RU/s with
<StyledLinkBase <StyledLinkBase
aria-label="capacity calculator of azure cosmos db" aria-label="ruDescription"
href="https://cosmos.azure.com/capacitycalculator/" href="https://cosmos.azure.com/capacitycalculator/"
target="_blank" target="_blank"
> >
<LinkBase <LinkBase
aria-label="capacity calculator of azure cosmos db" aria-label="ruDescription"
href="https://cosmos.azure.com/capacitycalculator/" href="https://cosmos.azure.com/capacitycalculator/"
styles={[Function]} styles={[Function]}
target="_blank" target="_blank"
@@ -996,7 +992,7 @@ exports[`ThroughputInput Pane should render Default properly 1`] = `
} }
> >
<a <a
aria-label="capacity calculator of azure cosmos db" aria-label="ruDescription"
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]}
@@ -1335,13 +1331,13 @@ exports[`ThroughputInput Pane should render Default properly 1`] = `
onMouseLeave={[Function]} onMouseLeave={[Function]}
> >
<StyledIconBase <StyledIconBase
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." ariaLabel="Info"
className="panelInfoIcon" className="panelInfoIcon"
iconName="Info" iconName="Info"
tabIndex={0} tabIndex={0}
> >
<IconBase <IconBase
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." ariaLabel="Info"
className="panelInfoIcon" className="panelInfoIcon"
iconName="Info" iconName="Info"
styles={[Function]} styles={[Function]}
@@ -1621,7 +1617,7 @@ exports[`ThroughputInput Pane should render Default properly 1`] = `
} }
> >
<i <i
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." aria-label="Info"
className="panelInfoIcon root-57" className="panelInfoIcon root-57"
data-icon-name="Info" data-icon-name="Info"
role="img" role="img"

View File

@@ -133,6 +133,7 @@ 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);
}); });

View File

@@ -166,11 +166,7 @@ export function createContextCommandBarButtons(
iconAlt: label, iconAlt: label,
onCommandClick: () => { onCommandClick: () => {
const selectedCollection: ViewModels.Collection = selectedNodeState.findSelectedCollection(); const selectedCollection: ViewModels.Collection = selectedNodeState.findSelectedCollection();
if (useNotebook.getState().isShellEnabled) { selectedCollection && selectedCollection.onNewMongoShellClick();
container.openNotebookTerminal(ViewModels.TerminalKind.Mongo);
} else {
selectedCollection && selectedCollection.onNewMongoShellClick();
}
}, },
commandButtonLabel: label, commandButtonLabel: label,
ariaLabel: label, ariaLabel: label,

View File

@@ -89,10 +89,9 @@ 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;
useHashV1: boolean; useHashV2: boolean;
enableAnalyticalStore: boolean; enableAnalyticalStore: boolean;
uniqueKeys: string[]; uniqueKeys: string[];
errorMessage: string; errorMessage: string;
@@ -122,11 +121,10 @@ 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"),
useHashV1: false, useHashV2: false,
enableAnalyticalStore: false, enableAnalyticalStore: false,
uniqueKeys: [], uniqueKeys: [],
errorMessage: "", errorMessage: "",
@@ -262,14 +260,7 @@ export class AddCollectionPanel extends React.Component<AddCollectionPanelProps,
true true
).toLocaleLowerCase()}.`} ).toLocaleLowerCase()}.`}
> >
<Icon <Icon iconName="Info" className="panelInfoIcon" tabIndex={0} />
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>
@@ -345,14 +336,7 @@ export class AddCollectionPanel extends React.Component<AddCollectionPanelProps,
true true
).toLocaleLowerCase()} within the database.`} ).toLocaleLowerCase()} within the database.`}
> >
<Icon <Icon iconName="Info" className="panelInfoIcon" tabIndex={0} />
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>
)} )}
@@ -400,12 +384,7 @@ 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 <Icon iconName="Info" className="panelInfoIcon" tabIndex={0} />
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>
@@ -488,14 +467,7 @@ 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 <Icon iconName="Info" className="panelInfoIcon" tabIndex={0} />
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>
@@ -542,12 +514,7 @@ export class AddCollectionPanel extends React.Component<AddCollectionPanelProps,
directionalHint={DirectionalHint.bottomLeftEdge} directionalHint={DirectionalHint.bottomLeftEdge}
content={this.getPartitionKeyTooltipText()} content={this.getPartitionKeyTooltipText()}
> >
<Icon <Icon iconName="Info" className="panelInfoIcon" tabIndex={0} />
iconName="Info"
className="panelInfoIcon"
tabIndex={0}
ariaLabel={this.getPartitionKeyTooltipText()}
/>
</TooltipHost> </TooltipHost>
</Stack> </Stack>
@@ -579,77 +546,6 @@ 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>
)} )}
@@ -676,17 +572,7 @@ 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 <Icon iconName="Info" className="panelInfoIcon" tabIndex={0} />
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>
)} )}
@@ -717,18 +603,11 @@ export class AddCollectionPanel extends React.Component<AddCollectionPanelProps,
</Text> </Text>
<TooltipHost <TooltipHost
directionalHint={DirectionalHint.bottomLeftEdge} directionalHint={DirectionalHint.bottomLeftEdge}
content={ content="Unique keys provide developers with the ability to add a layer of data integrity to their database. By
"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." creating a unique key policy when a container is created, you ensure the uniqueness of one or more values
} per partition key."
> >
<Icon <Icon iconName="Info" className="panelInfoIcon" tabIndex={0} />
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>
@@ -791,13 +670,7 @@ export class AddCollectionPanel extends React.Component<AddCollectionPanelProps,
directionalHint={DirectionalHint.bottomLeftEdge} directionalHint={DirectionalHint.bottomLeftEdge}
content={this.getAnalyticalStorageTooltipContent()} content={this.getAnalyticalStorageTooltipContent()}
> >
<Icon <Icon iconName="Info" className="panelInfoIcon" tabIndex={0} />
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>
@@ -874,12 +747,7 @@ 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 <Icon iconName="Info" className="panelInfoIcon" tabIndex={0} />
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>
@@ -899,29 +767,18 @@ export class AddCollectionPanel extends React.Component<AddCollectionPanelProps,
)} )}
{userContext.apiType === "SQL" && ( {userContext.apiType === "SQL" && (
<Stack className="panelGroupSpacing"> <Checkbox
<Checkbox label="My partition key is larger than 101 bytes"
label="My application uses an older Cosmos .NET or Java SDK version (.NET V1 or Java V2)" checked={this.state.useHashV2}
checked={this.state.useHashV1} styles={{
styles={{ text: { fontSize: 12 },
text: { fontSize: 12 }, checkbox: { width: 12, height: 12 },
checkbox: { width: 12, height: 12 }, label: { padding: 0, alignItems: "center" },
label: { padding: 0, alignItems: "center", wordWrap: "break-word", whiteSpace: "break-spaces" }, }}
}} onChange={(ev: React.FormEvent<HTMLElement>, isChecked: boolean) =>
onChange={(ev: React.FormEvent<HTMLElement>, isChecked: boolean) => this.setState({ useHashV2: isChecked })
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>
@@ -976,20 +833,12 @@ export class AddCollectionPanel extends React.Component<AddCollectionPanelProps,
return isLowerCase ? partitionKeyName.toLocaleLowerCase() : partitionKeyName; return isLowerCase ? partitionKeyName.toLocaleLowerCase() : partitionKeyName;
} }
private getPartitionKeyPlaceHolder(index?: number): string { private getPartitionKeyPlaceHolder(): 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";
} }
@@ -1315,16 +1164,11 @@ export class AddCollectionPanel extends React.Component<AddCollectionPanelProps,
} }
const uniqueKeyPolicy: DataModels.UniqueKeyPolicy = this.parseUniqueKeys(); const uniqueKeyPolicy: DataModels.UniqueKeyPolicy = this.parseUniqueKeys();
const partitionKeyVersion = this.state.useHashV1 ? undefined : 2; const partitionKeyVersion = this.state.useHashV2 ? 2 : undefined;
const partitionKey: DataModels.PartitionKey = partitionKeyString const partitionKey: DataModels.PartitionKey = partitionKeyString
? { ? {
paths: [ paths: [partitionKeyString],
partitionKeyString, kind: "Hash",
...(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;

View File

@@ -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,
{ {

View File

@@ -48,7 +48,7 @@ export const PanelInfoErrorComponent: React.FunctionComponent<PanelInfoErrorProp
)} )}
</Text> </Text>
{showErrorDetails && ( {showErrorDetails && (
<a className="paneErrorLink" role="link" onClick={expandConsole} tabIndex={0} onKeyPress={expandConsole}> <a className="paneErrorLink" role="link" onClick={expandConsole}>
More details More details
</a> </a>
)} )}

View File

@@ -242,11 +242,6 @@ 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}>
@@ -289,13 +284,7 @@ export const AddTableEntityPanel: FunctionComponent<AddTableEntityPanelProps> =
); );
})} })}
{userContext.apiType !== "Cassandra" && ( {userContext.apiType !== "Cassandra" && (
<Stack <Stack horizontal onClick={addNewEntity} className="addButtonEntiy">
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>

View File

@@ -29,14 +29,10 @@ 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"

View File

@@ -31,7 +31,6 @@ 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}
@@ -125,7 +124,6 @@ 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}
@@ -165,7 +163,6 @@ 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}
@@ -209,7 +206,6 @@ 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}
@@ -227,36 +223,13 @@ exports[`AddCollectionPanel should render Default properly 1`] = `
id="addCollection-partitionKeyValue" id="addCollection-partitionKeyValue"
onChange={[Function]} onChange={[Function]}
pattern=".*" pattern=".*"
placeholder="Required - first partition key e.g., /TenantId" placeholder="e.g., /address/zipCode"
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
@@ -273,7 +246,6 @@ 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}
@@ -331,7 +303,6 @@ 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}
@@ -425,49 +396,26 @@ exports[`AddCollectionPanel should render Default properly 1`] = `
className="panelGroupSpacing" className="panelGroupSpacing"
id="collapsibleSectionContent" id="collapsibleSectionContent"
> >
<Stack <StyledCheckboxBase
className="panelGroupSpacing" checked={false}
> label="My partition key is larger than 101 bytes"
<StyledCheckboxBase onChange={[Function]}
checked={false} styles={
label="My application uses an older Cosmos .NET or Java SDK version (.NET V1 or Java V2)" Object {
onChange={[Function]} "checkbox": Object {
styles={ "height": 12,
Object { "width": 12,
"checkbox": Object { },
"height": 12, "label": Object {
"width": 12, "alignItems": "center",
}, "padding": 0,
"label": Object { },
"alignItems": "center", "text": Object {
"padding": 0, "fontSize": 12,
"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>

View File

@@ -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} alt=" " /> <Image src={LinkIcon} />
</Stack> </Stack>
<Text>{item.description}</Text> <Text>{item.description}</Text>
</Stack> </Stack>

View File

@@ -516,7 +516,7 @@ export default class QueryBuilderViewModel {
}; };
public onAddNewClauseKeyDown = (event: KeyboardEvent): boolean => { public onAddNewClauseKeyDown = (event: KeyboardEvent): boolean => {
if (event.key === "Enter" || event.key === "Space") { if (event.keyCode === KeyCodes.Enter || event.keyCode === KeyCodes.Space) {
this.addClauseIndex(this.clauseArray().length - 1); this.addClauseIndex(this.clauseArray().length - 1);
event.stopPropagation(); event.stopPropagation();
return false; return false;

View File

@@ -70,19 +70,24 @@
<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>
<button <div
data-bind="click: addNewClause, event: { keydown: onAddNewClauseKeyDown }" class="addClause"
style="border: none; background: none" role="button"
data-bind="click: addNewClause, event: { keydown: onAddNewClauseKeyDown }, attr: { title: addNewClauseLine }"
tabindex="0"
> >
<div class="addClause" data-bind=" "> <div class="addClause-heading">
<div class="addClause-heading"> <span class="clause-table addClause-title">
<span class="clause-table addClause-title"> <img
<img class="addclauseProperty-Img" style="margin-bottom: 5px" src="/Add-property.svg" /> class="addclauseProperty-Img"
<span style="margin-left: 5px" data-bind="text: addNewClauseLine"></span> style="margin-bottom: 5px"
</span> src="/Add-property.svg"
</div> alt="Add new clause"
/>
<span style="margin-left: 5px" data-bind="text: addNewClauseLine"></span>
</span>
</div> </div>
</button> </div>
</div> </div>
</div> </div>
<!-- Tables Query Tab Query Helper - End--> <!-- Tables Query Tab Query Helper - End-->
@@ -163,20 +168,22 @@
<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">
<button <span
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}"
> >
<span class="entity-Add-Cancel" role="button"> <img class="querybuilder-addpropertyImg" src="/Add-property.svg" alt="Add clause" />
<img class="querybuilder-addpropertyImg" src="/Add-property.svg" alt="Add clause" /> </span>
</span> <span
</button> class="entity-Add-Cancel"
<button role="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}"
> >
<span class="entity-Add-Cancel" role="button"> <img class="querybuilder-cancelImg" src="/Entity_cancel.svg" alt="Delete clause" />
<img class="querybuilder-cancelImg" src="/Entity_cancel.svg" alt="Delete clause" /> </span>
</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" />

View File

@@ -24,21 +24,22 @@ interface TabsProps {
} }
export const Tabs = ({ explorer }: TabsProps): JSX.Element => { export const Tabs = ({ explorer }: TabsProps): JSX.Element => {
const { openedTabs, openedReactTabs, activeTab, activeReactTab, networkSettingsWarning } = useTabs(); const { openedTabs, openedReactTabs, activeTab, activeReactTab, showNetworkSettingsWarning } = useTabs();
return ( return (
<div className="tabsManagerContainer"> <div className="tabsManagerContainer">
{networkSettingsWarning && ( {showNetworkSettingsWarning && (
<MessageBar <MessageBar
messageBarType={MessageBarType.warning} messageBarType={MessageBarType.warning}
actions={ actions={
<MessageBarButton onClick={() => sendMessage({ type: MessageTypes.OpenPostgresNetworkingBlade })}> <MessageBarButton onClick={() => sendMessage({ type: MessageTypes.OpenCosmosDBNetworkingBlade })}>
Change network settings Change network settings
</MessageBarButton> </MessageBarButton>
} }
messageBarIconProps={{ iconName: "WarningSolid", className: "messageBarWarningIcon" }} messageBarIconProps={{ iconName: "WarningSolid", className: "messageBarWarningIcon" }}
> >
{networkSettingsWarning} The Network settings for this account are preventing access from Data Explorer. Please allow access from Azure
Portal to proceed.
</MessageBar> </MessageBar>
)} )}
<div id="content" className="flexContainer hideOverflows"> <div id="content" className="flexContainer hideOverflows">

View File

@@ -29,7 +29,6 @@ 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;
@@ -91,7 +90,6 @@ 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"),
}; };
} }

View File

@@ -10,43 +10,31 @@ const PortalIPs: { [key: string]: string[] } = {
usnat: ["7.28.202.68"], usnat: ["7.28.202.68"],
}; };
export const getNetworkSettingsWarningMessage = (clientIpAddress: string): string => { export const doNetworkSettingsAllowDataExplorerAccess = (): boolean => {
const accountProperties = userContext.databaseAccount?.properties; const accountProperties = userContext.databaseAccount?.properties;
if (!accountProperties) { if (!accountProperties) {
return ""; return false;
} }
// public network access is disabled // public network access is disabled
if (accountProperties.publicNetworkAccess !== "Enabled") { if (accountProperties.publicNetworkAccess !== "Enabled") {
return "The Network settings for this account are preventing access from Data Explorer. Please enable public access to proceed."; return false;
} }
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 ""; return true;
} }
if (userContext.apiType === "Cassandra" || userContext.apiType === "Mongo") { const portalIPs = PortalIPs[userContext.portalEnv];
const portalIPs = PortalIPs[userContext.portalEnv]; let numberOfMatches = 0;
let numberOfMatches = 0; ipRules.forEach((ipRule) => {
ipRules.forEach((ipRule) => { if (portalIPs.indexOf(ipRule.ipAddressOrRange) !== -1) {
if (portalIPs.indexOf(ipRule.ipAddressOrRange) !== -1) { numberOfMatches++;
numberOfMatches++;
}
});
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 ""; return numberOfMatches === portalIPs.length;
} 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.";
}
}; };

View File

@@ -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 { getNetworkSettingsWarningMessage } from "Utils/NetworkUtility"; import { doNetworkSettingsAllowDataExplorerAccess } 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,8 +382,8 @@ function updateContextsFromPortalMessage(inputs: DataExplorerInputsFrame) {
} }
} }
const warningMessage = getNetworkSettingsWarningMessage(inputs.clientIpAddress); const isDataExplorerAccessAllowed = doNetworkSettingsAllowDataExplorerAccess();
useTabs.getState().setNetworkSettingsWarning(warningMessage); useTabs.getState().setShowNetworkSettingsWarning(!isDataExplorerAccessAllowed);
if (inputs.features) { if (inputs.features) {
Object.assign(userContext.features, extractFeatures(new URLSearchParams(inputs.features))); Object.assign(userContext.features, extractFeatures(new URLSearchParams(inputs.features)));

View File

@@ -9,7 +9,7 @@ interface TabsState {
openedReactTabs: ReactTabKind[]; openedReactTabs: ReactTabKind[];
activeTab: TabsBase | undefined; activeTab: TabsBase | undefined;
activeReactTab: ReactTabKind | undefined; activeReactTab: ReactTabKind | undefined;
networkSettingsWarning: string; showNetworkSettingsWarning: boolean;
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;
setNetworkSettingsWarning: (warningMessage: string) => void; setShowNetworkSettingsWarning: (showWarning: boolean) => 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,
networkSettingsWarning: "", showNetworkSettingsWarning: false,
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 });
}, },
setNetworkSettingsWarning: (warningMessage: string) => set({ networkSettingsWarning: warningMessage }), setShowNetworkSettingsWarning: (showWarning: boolean) => set({ showNetworkSettingsWarning: showWarning }),
})); }));