*
- Confirm by typing the {getDatabaseName()} id
+ {confirmDatabase}
-
+
- Confirm by typing the
- Database
- id
+ Confirm by typing the Database id (name)
{
}
private clearMostRecent = (): void => {
- MostRecentActivity.mostRecentActivity.clear(userContext.databaseAccount?.id);
+ MostRecentActivity.clear(userContext.databaseAccount?.name);
this.setState({});
};
@@ -498,7 +498,7 @@ export class SplashScreen extends React.Component {
}
private createRecentItems(): SplashScreenItem[] {
- return MostRecentActivity.mostRecentActivity.getItems(userContext.databaseAccount?.id).map((activity) => {
+ return MostRecentActivity.getItems(userContext.databaseAccount?.name).map((activity) => {
switch (activity.type) {
default: {
const unknownActivity: never = activity;
diff --git a/src/Explorer/Tables/TableDataClient.ts b/src/Explorer/Tables/TableDataClient.ts
index 8ba912bc5..4e0859f95 100644
--- a/src/Explorer/Tables/TableDataClient.ts
+++ b/src/Explorer/Tables/TableDataClient.ts
@@ -757,15 +757,7 @@ export class CassandraAPIDataClient extends TableDataClient {
CassandraProxyEndpoints.Mooncake,
];
- let canAccessCassandraProxy: boolean = userContext.databaseAccount.properties.publicNetworkAccess === "Enabled";
- if (
- configContext.CASSANDRA_PROXY_ENDPOINT !== CassandraProxyEndpoints.Development &&
- userContext.databaseAccount.properties.ipRules?.length > 0
- ) {
- canAccessCassandraProxy = canAccessCassandraProxy && configContext.CASSANDRA_PROXY_OUTBOUND_IPS_ALLOWLISTED;
- }
return (
- canAccessCassandraProxy &&
configContext.NEW_CASSANDRA_APIS?.includes(api) &&
activeCassandraProxyEndpoints.includes(configContext.CASSANDRA_PROXY_ENDPOINT)
);
diff --git a/src/Explorer/Tabs/DocumentsTabV2/DocumentsTabV2.tsx b/src/Explorer/Tabs/DocumentsTabV2/DocumentsTabV2.tsx
index 19314f005..8bcb35aa1 100644
--- a/src/Explorer/Tabs/DocumentsTabV2/DocumentsTabV2.tsx
+++ b/src/Explorer/Tabs/DocumentsTabV2/DocumentsTabV2.tsx
@@ -645,6 +645,7 @@ export const DocumentsTabComponent: React.FunctionComponent(undefined);
const [bulkDeleteOperation, setBulkDeleteOperation] = useState<{
onCompleted: (documentIds: DocumentId[]) => void;
@@ -754,6 +755,7 @@ export const DocumentsTabComponent: React.FunctionComponent 0,
}));
})
.catch((error) => {
@@ -764,6 +766,7 @@ export const DocumentsTabComponent: React.FunctionComponent setSelectedRows(new Set([documentIds.length - 1])))
+ .then(() => {
+ setSelectedRows(new Set([documentIds.length - 1]));
+ setClickedRowIndex(documentIds.length - 1);
+ })
.finally(() => setIsExecuting(false));
}, [
onExecutionErrorChange,
@@ -1139,6 +1145,7 @@ export const DocumentsTabComponent: React.FunctionComponent
)}
-
-
- Warning
- {get429WarningMessageNoSql()}{" "}
-
- Learn More
-
-
-
+ {bulkDeleteProcess.hasBeenThrottled && (
+
+
+ Warning
+ {get429WarningMessageNoSql()}{" "}
+
+ Learn More
+
+
+
+ )}
)}
diff --git a/src/Explorer/Tabs/DocumentsTabV2/DocumentsTableComponent.tsx b/src/Explorer/Tabs/DocumentsTabV2/DocumentsTableComponent.tsx
index c96d63ff5..36689dc59 100644
--- a/src/Explorer/Tabs/DocumentsTabV2/DocumentsTableComponent.tsx
+++ b/src/Explorer/Tabs/DocumentsTabV2/DocumentsTableComponent.tsx
@@ -38,6 +38,7 @@ import {
TextSortDescendingRegular,
} from "@fluentui/react-icons";
import { NormalizedEventKey } from "Common/Constants";
+import { Environment, getEnvironment } from "Common/EnvironmentUtility";
import { TableColumnSelectionPane } from "Explorer/Panes/TableColumnSelectionPane/TableColumnSelectionPane";
import {
ColumnSizesMap,
@@ -50,7 +51,6 @@ import {
import { INITIAL_SELECTED_ROW_INDEX, useDocumentsTabStyles } from "Explorer/Tabs/DocumentsTabV2/DocumentsTabV2";
import { selectionHelper } from "Explorer/Tabs/DocumentsTabV2/SelectionHelper";
import { LayoutConstants } from "Explorer/Theme/ThemeUtil";
-import { userContext } from "UserContext";
import { isEnvironmentCtrlPressed, isEnvironmentShiftPressed } from "Utils/KeyboardUtils";
import { useSidePanel } from "hooks/useSidePanel";
import React, { useCallback, useMemo } from "react";
@@ -228,7 +228,7 @@ export const DocumentsTableComponent: React.FC =
} onClick={onRefreshTable}>
Refresh
- {userContext.features.enableDocumentsTableColumnSelection && (
+ {[Environment.Development, Environment.Mpac].includes(getEnvironment()) && (
<>
}
@@ -260,24 +260,25 @@ export const DocumentsTableComponent: React.FC =
>
Resize with left/right arrow keys
- {userContext.features.enableDocumentsTableColumnSelection && !isColumnSelectionDisabled && (
- }
- onClick={() => {
- // Remove column id from selectedColumnIds
- const index = selectedColumnIds.indexOf(column.id);
- if (index === -1) {
- return;
- }
- const newSelectedColumnIds = [...selectedColumnIds];
- newSelectedColumnIds.splice(index, 1);
- onColumnSelectionChange(newSelectedColumnIds);
- }}
- >
- Remove column
-
- )}
+ {[Environment.Development, Environment.Mpac].includes(getEnvironment()) &&
+ !isColumnSelectionDisabled && (
+ }
+ onClick={() => {
+ // Remove column id from selectedColumnIds
+ const index = selectedColumnIds.indexOf(column.id);
+ if (index === -1) {
+ return;
+ }
+ const newSelectedColumnIds = [...selectedColumnIds];
+ newSelectedColumnIds.splice(index, 1);
+ onColumnSelectionChange(newSelectedColumnIds);
+ }}
+ >
+ Remove column
+
+ )}
diff --git a/src/Explorer/Tabs/MongoShellTab/MongoShellTabComponent.tsx b/src/Explorer/Tabs/MongoShellTab/MongoShellTabComponent.tsx
index bcf8b7d05..7bba85296 100644
--- a/src/Explorer/Tabs/MongoShellTab/MongoShellTabComponent.tsx
+++ b/src/Explorer/Tabs/MongoShellTab/MongoShellTabComponent.tsx
@@ -55,7 +55,7 @@ export default class MongoShellTabComponent extends Component<
constructor(props: IMongoShellTabComponentProps) {
super(props);
this._logTraces = new Map();
- this._useMongoProxyEndpoint = useMongoProxyEndpoint("legacyMongoShell");
+ this._useMongoProxyEndpoint = useMongoProxyEndpoint(Constants.MongoProxyApi.LegacyMongoShell);
this.state = {
url: getMongoShellUrl(this._useMongoProxyEndpoint),
diff --git a/src/Explorer/Tabs/Tabs.tsx b/src/Explorer/Tabs/Tabs.tsx
index 2bf21d3ab..3ee50992c 100644
--- a/src/Explorer/Tabs/Tabs.tsx
+++ b/src/Explorer/Tabs/Tabs.tsx
@@ -1,7 +1,7 @@
import { IMessageBarStyles, MessageBar, MessageBarButton, MessageBarType } from "@fluentui/react";
import { CassandraProxyEndpoints, MongoProxyEndpoints } from "Common/Constants";
import { sendMessage } from "Common/MessageHandler";
-import { configContext, updateConfigContext } from "ConfigContext";
+import { configContext } from "ConfigContext";
import { IpRule } from "Contracts/DataModels";
import { MessageTypes } from "Contracts/ExplorerContracts";
import { CollectionTabKind } from "Contracts/ViewModels";
@@ -370,12 +370,6 @@ const showMongoAndCassandraProxiesNetworkSettingsWarning = (): boolean => {
ipAddressesFromIPRules.includes(mongoProxyOutboundIP),
);
- if (ipRulesIncludeMongoProxy) {
- updateConfigContext({
- MONGO_PROXY_OUTBOUND_IPS_ALLOWLISTED: true,
- });
- }
-
return !ipRulesIncludeMongoProxy;
} else if (userContext.apiType === "Cassandra") {
const isProdOrMpacCassandraProxyEndpoint: boolean = [
@@ -394,12 +388,6 @@ const showMongoAndCassandraProxiesNetworkSettingsWarning = (): boolean => {
(cassandraProxyOutboundIP: string) => ipAddressesFromIPRules.includes(cassandraProxyOutboundIP),
);
- if (ipRulesIncludeCassandraProxy) {
- updateConfigContext({
- CASSANDRA_PROXY_OUTBOUND_IPS_ALLOWLISTED: true,
- });
- }
-
return !ipRulesIncludeCassandraProxy;
}
}
diff --git a/src/Explorer/Tree/ResourceTreeAdapter.tsx b/src/Explorer/Tree/ResourceTreeAdapter.tsx
index 3e669a1b2..76d3e9308 100644
--- a/src/Explorer/Tree/ResourceTreeAdapter.tsx
+++ b/src/Explorer/Tree/ResourceTreeAdapter.tsx
@@ -1,4 +1,5 @@
import { TreeNodeMenuItem } from "Explorer/Controls/TreeComponent/TreeNodeComponent";
+import { collectionWasOpened } from "Explorer/MostRecentActivity/MostRecentActivity";
import { shouldShowScriptNodes } from "Explorer/Tree/treeNodeUtil";
import { getItemName } from "Utils/APITypeUtils";
import * as ko from "knockout";
@@ -28,7 +29,6 @@ import { useDialog } from "../Controls/Dialog";
import { LegacyTreeComponent, LegacyTreeNode } from "../Controls/TreeComponent/LegacyTreeComponent";
import Explorer from "../Explorer";
import { useCommandBar } from "../Menus/CommandBar/CommandBarComponentAdapter";
-import { mostRecentActivity } from "../MostRecentActivity/MostRecentActivity";
import { NotebookContentItem, NotebookContentItemType } from "../Notebook/NotebookContentItem";
import { NotebookUtil } from "../Notebook/NotebookUtil";
import { useNotebook } from "../Notebook/useNotebook";
@@ -229,7 +229,7 @@ export class ResourceTreeAdapter implements ReactAdapter {
onClick: () => {
collection.openTab();
// push to most recent
- mostRecentActivity.collectionWasOpened(userContext.databaseAccount?.id, collection);
+ collectionWasOpened(userContext.databaseAccount?.name, collection);
},
isSelected: () =>
useSelectedNode
diff --git a/src/Explorer/Tree/treeNodeUtil.tsx b/src/Explorer/Tree/treeNodeUtil.tsx
index b6ec04e01..8e6c94559 100644
--- a/src/Explorer/Tree/treeNodeUtil.tsx
+++ b/src/Explorer/Tree/treeNodeUtil.tsx
@@ -1,5 +1,6 @@
import { DatabaseRegular, DocumentMultipleRegular, SettingsRegular } from "@fluentui/react-icons";
import { TreeNode } from "Explorer/Controls/TreeComponent/TreeNodeComponent";
+import { collectionWasOpened } from "Explorer/MostRecentActivity/MostRecentActivity";
import TabsBase from "Explorer/Tabs/TabsBase";
import StoredProcedure from "Explorer/Tree/StoredProcedure";
import Trigger from "Explorer/Tree/Trigger";
@@ -17,7 +18,6 @@ import { userContext } from "../../UserContext";
import * as ResourceTreeContextMenuButtonFactory from "../ContextMenuButtonFactory";
import Explorer from "../Explorer";
import { useCommandBar } from "../Menus/CommandBar/CommandBarComponentAdapter";
-import { mostRecentActivity } from "../MostRecentActivity/MostRecentActivity";
import { useNotebook } from "../Notebook/useNotebook";
import { useSelectedNode } from "../useSelectedNode";
@@ -98,7 +98,7 @@ export const createResourceTokenTreeNodes = (collection: ViewModels.CollectionBa
onClick: () => {
collection.onDocumentDBDocumentsClick();
// push to most recent
- mostRecentActivity.collectionWasOpened(userContext.databaseAccount?.id, collection);
+ collectionWasOpened(userContext.databaseAccount?.name, collection);
},
isSelected: () =>
useSelectedNode
@@ -234,7 +234,7 @@ export const buildCollectionNode = (
useSelectedNode.getState().setSelectedNode(collection);
collection.openTab();
// push to most recent
- mostRecentActivity.collectionWasOpened(userContext.databaseAccount?.id, collection);
+ collectionWasOpened(userContext.databaseAccount?.name, collection);
},
onExpanded: async () => {
// Rewritten version of expandCollapseCollection
@@ -282,7 +282,7 @@ const buildCollectionNodeChildren = (
onClick: () => {
collection.openTab();
// push to most recent
- mostRecentActivity.collectionWasOpened(userContext.databaseAccount?.id, collection);
+ collectionWasOpened(userContext.databaseAccount?.name, collection);
},
isSelected: () =>
useSelectedNode
diff --git a/src/KeyboardShortcuts.tsx b/src/KeyboardShortcuts.tsx
index 96e7181a3..b155f0483 100644
--- a/src/KeyboardShortcuts.tsx
+++ b/src/KeyboardShortcuts.tsx
@@ -83,8 +83,8 @@ const bindings: Record = {
[KeyboardAction.NEW_ITEM]: ["Alt+N I"],
[KeyboardAction.DELETE_ITEM]: ["Alt+D"],
[KeyboardAction.TOGGLE_COPILOT]: ["$mod+P"],
- [KeyboardAction.SELECT_LEFT_TAB]: ["$mod+Alt+[", "$mod+Shift+F6"],
- [KeyboardAction.SELECT_RIGHT_TAB]: ["$mod+Alt+]", "$mod+F6"],
+ [KeyboardAction.SELECT_LEFT_TAB]: ["$mod+Alt+BracketLeft", "$mod+Shift+F6"],
+ [KeyboardAction.SELECT_RIGHT_TAB]: ["$mod+Alt+BracketRight", "$mod+F6"],
[KeyboardAction.CLOSE_TAB]: ["$mod+Alt+W"],
[KeyboardAction.SEARCH]: ["$mod+Shift+F"],
[KeyboardAction.CLEAR_SEARCH]: ["$mod+Shift+C"],
diff --git a/src/Platform/Hosted/extractFeatures.ts b/src/Platform/Hosted/extractFeatures.ts
index 2ae14e59e..5bd84516e 100644
--- a/src/Platform/Hosted/extractFeatures.ts
+++ b/src/Platform/Hosted/extractFeatures.ts
@@ -38,7 +38,6 @@ export type Features = {
readonly copilotChatFixedMonacoEditorHeight: boolean;
readonly enablePriorityBasedExecution: boolean;
readonly disableConnectionStringLogin: boolean;
- readonly enableDocumentsTableColumnSelection: boolean;
// can be set via both flight and feature flag
autoscaleDefault: boolean;
@@ -109,7 +108,6 @@ export function extractFeatures(given = new URLSearchParams(window.location.sear
copilotChatFixedMonacoEditorHeight: "true" === get("copilotchatfixedmonacoeditorheight"),
enablePriorityBasedExecution: "true" === get("enableprioritybasedexecution"),
disableConnectionStringLogin: "true" === get("disableconnectionstringlogin"),
- enableDocumentsTableColumnSelection: "true" === get("enabledocumentstablecolumnselection"),
};
}
diff --git a/src/Shared/AppStatePersistenceUtility.ts b/src/Shared/AppStatePersistenceUtility.ts
index 3d65ff0a7..b48258d4f 100644
--- a/src/Shared/AppStatePersistenceUtility.ts
+++ b/src/Shared/AppStatePersistenceUtility.ts
@@ -3,6 +3,7 @@ import { LocalStorageUtility, StorageKey } from "Shared/StorageUtility";
// The component name whose state is being saved. Component name must not include special characters.
export enum AppStateComponentNames {
DocumentsTab = "DocumentsTab",
+ MostRecentActivity = "MostRecentActivity",
QueryCopilot = "QueryCopilot",
}
@@ -34,6 +35,7 @@ export const loadState = (path: StorePath): unknown => {
const key = createKeyFromPath(path);
return appState[key]?.data;
};
+
export const saveState = (path: StorePath, state: unknown): void => {
// Retrieve state object
const appState =
@@ -65,6 +67,10 @@ export const deleteState = (path: StorePath): void => {
LocalStorageUtility.setEntryObject(StorageKey.AppState, appState);
};
+export const hasState = (path: StorePath): boolean => {
+ return loadState(path) !== undefined;
+};
+
// This is for high-frequency state changes
let timeoutId: NodeJS.Timeout | undefined;
export const saveStateDebounced = (path: StorePath, state: unknown, debounceDelayMs = 1000): void => {
diff --git a/src/Shared/StorageUtility.ts b/src/Shared/StorageUtility.ts
index 7a55513ed..f2baa85da 100644
--- a/src/Shared/StorageUtility.ts
+++ b/src/Shared/StorageUtility.ts
@@ -24,7 +24,7 @@ export enum StorageKey {
MaxDegreeOfParellism,
IsGraphAutoVizDisabled,
TenantId,
- MostRecentActivity,
+ MostRecentActivity, // deprecated
SetPartitionKeyUndefined,
GalleryCalloutDismissed,
VisitedAccounts,
diff --git a/src/Utils/MessageValidation.test.ts b/src/Utils/MessageValidation.test.ts
index 9223fe623..f62396a87 100644
--- a/src/Utils/MessageValidation.test.ts
+++ b/src/Utils/MessageValidation.test.ts
@@ -13,9 +13,9 @@ describe("isInvalidParentFrameOrigin", () => {
${"https://subdomain.portal.azure.com"} | ${false}
${"https://subdomain.portal.azure.us"} | ${false}
${"https://subdomain.portal.azure.cn"} | ${false}
- ${"https://main.documentdb.ext.azure.com"} | ${false}
- ${"https://main.documentdb.ext.azure.us"} | ${false}
- ${"https://main.documentdb.ext.azure.cn"} | ${false}
+ ${"https://cdb-ms-prod-pbe.cosmos.azure.com"} | ${false}
+ ${"https://cdb-ff-prod-pbe.cosmos.azure.us"} | ${false}
+ ${"https://cdb-mc-prod-pbe.cosmos.azure.cn"} | ${false}
${"https://cosmos-db-dataexplorer-germanycentral.azurewebsites.de"} | ${false}
${"https://main.documentdb.ext.microsoftazure.de"} | ${false}
${"https://random.domain"} | ${true}
diff --git a/src/Utils/NetworkUtility.test.ts b/src/Utils/NetworkUtility.test.ts
index ba2eb2c67..b27c8db50 100644
--- a/src/Utils/NetworkUtility.test.ts
+++ b/src/Utils/NetworkUtility.test.ts
@@ -2,12 +2,11 @@ import { MongoProxyEndpoints, PortalBackendEndpoints } from "Common/Constants";
import { resetConfigContext, updateConfigContext } from "ConfigContext";
import { DatabaseAccount, IpRule } from "Contracts/DataModels";
import { updateUserContext } from "UserContext";
-import { MongoProxyOutboundIPs, PortalBackendIPs, PortalBackendOutboundIPs } from "Utils/EndpointUtils";
+import { MongoProxyOutboundIPs, PortalBackendOutboundIPs } from "Utils/EndpointUtils";
import { getNetworkSettingsWarningMessage } from "./NetworkUtility";
describe("NetworkUtility tests", () => {
describe("getNetworkSettingsWarningMessage", () => {
- const legacyBackendEndpoint: string = "https://main.documentdb.ext.azure.com";
const publicAccessMessagePart = "Please enable public access to proceed";
const accessMessagePart = "Please allow access from Azure Portal to proceed";
let warningMessageResult: string;
@@ -48,25 +47,23 @@ describe("NetworkUtility tests", () => {
});
it(`should return no message when the appropriate ip rules are added to mongo/cassandra account per endpoint`, async () => {
- const portalBackendOutboundIPsWithLegacyIPs: string[] = [
+ const portalBackendOutboundIPs: string[] = [
...PortalBackendOutboundIPs[PortalBackendEndpoints.Mpac],
...PortalBackendOutboundIPs[PortalBackendEndpoints.Prod],
...MongoProxyOutboundIPs[MongoProxyEndpoints.Mpac],
...MongoProxyOutboundIPs[MongoProxyEndpoints.Prod],
- ...PortalBackendIPs["https://main.documentdb.ext.azure.com"],
];
updateUserContext({
databaseAccount: {
kind: "MongoDB",
properties: {
- ipRules: portalBackendOutboundIPsWithLegacyIPs.map((ip: string) => ({ ipAddressOrRange: ip }) as IpRule),
+ ipRules: portalBackendOutboundIPs.map((ip: string) => ({ ipAddressOrRange: ip }) as IpRule),
publicNetworkAccess: "Enabled",
},
} as DatabaseAccount,
});
updateConfigContext({
- BACKEND_ENDPOINT: legacyBackendEndpoint,
PORTAL_BACKEND_ENDPOINT: PortalBackendEndpoints.Mpac,
MONGO_PROXY_ENDPOINT: MongoProxyEndpoints.Mpac,
});
@@ -90,7 +87,6 @@ describe("NetworkUtility tests", () => {
});
updateConfigContext({
- BACKEND_ENDPOINT: legacyBackendEndpoint,
PORTAL_BACKEND_ENDPOINT: PortalBackendEndpoints.Mpac,
MONGO_PROXY_ENDPOINT: MongoProxyEndpoints.Mpac,
});
diff --git a/src/Utils/NetworkUtility.ts b/src/Utils/NetworkUtility.ts
index 40f663624..8c3b02e20 100644
--- a/src/Utils/NetworkUtility.ts
+++ b/src/Utils/NetworkUtility.ts
@@ -2,12 +2,7 @@ import { CassandraProxyEndpoints, MongoProxyEndpoints, PortalBackendEndpoints }
import { configContext } from "ConfigContext";
import { checkFirewallRules } from "Explorer/Tabs/Shared/CheckFirewallRules";
import { userContext } from "UserContext";
-import {
- CassandraProxyOutboundIPs,
- MongoProxyOutboundIPs,
- PortalBackendIPs,
- PortalBackendOutboundIPs,
-} from "Utils/EndpointUtils";
+import { CassandraProxyOutboundIPs, MongoProxyOutboundIPs, PortalBackendOutboundIPs } from "Utils/EndpointUtils";
export const getNetworkSettingsWarningMessage = async (
setStateFunc: (warningMessage: string) => void,
@@ -61,7 +56,7 @@ export const getNetworkSettingsWarningMessage = async (
...PortalBackendOutboundIPs[PortalBackendEndpoints.Prod],
]
: PortalBackendOutboundIPs[configContext.PORTAL_BACKEND_ENDPOINT];
- let portalIPs: string[] = [...portalBackendOutboundIPs, ...PortalBackendIPs[configContext.BACKEND_ENDPOINT]];
+ let portalIPs: string[] = [...portalBackendOutboundIPs];
if (userContext.apiType === "Mongo") {
const isProdOrMpacMongoProxyEndpoint: boolean = [MongoProxyEndpoints.Mpac, MongoProxyEndpoints.Prod].includes(
diff --git a/src/Utils/QueryUtils.test.ts b/src/Utils/QueryUtils.test.ts
index 699626569..ab2b2a8b1 100644
--- a/src/Utils/QueryUtils.test.ts
+++ b/src/Utils/QueryUtils.test.ts
@@ -4,7 +4,28 @@ import * as sinon from "sinon";
import * as DataModels from "../Contracts/DataModels";
import * as ViewModels from "../Contracts/ViewModels";
import * as QueryUtils from "./QueryUtils";
-import { defaultQueryFields, extractPartitionKeyValues } from "./QueryUtils";
+import { defaultQueryFields, extractPartitionKeyValues, getValueForPath } from "./QueryUtils";
+
+const documentContent = {
+ "Volcano Name": "Adams",
+ Country: "United States",
+ Region: "US-Washington",
+ Location: {
+ type: "Point",
+ coordinates: [-121.49, 46.206],
+ },
+ Elevation: 3742,
+ Type: "Stratovolcano",
+ Category: "",
+ Status: "Tephrochronology",
+ "Last Known Eruption": "Last known eruption from A.D. 1-1499, inclusive",
+ id: "9e3c494e-8367-3f50-1f56-8c6fcb961363",
+ _rid: "xzo0AJRYUxUFAAAAAAAAAA==",
+ _self: "dbs/xzo0AA==/colls/xzo0AJRYUxU=/docs/xzo0AJRYUxUFAAAAAAAAAA==/",
+ _etag: '"ce00fa43-0000-0100-0000-652840440000"',
+ _attachments: "attachments/",
+ _ts: 1697136708,
+};
describe("Query Utils", () => {
const generatePartitionKeyForPath = (path: string): DataModels.PartitionKey => {
@@ -111,28 +132,30 @@ describe("Query Utils", () => {
});
});
- describe("extractPartitionKey", () => {
- const documentContent = {
- "Volcano Name": "Adams",
- Country: "United States",
- Region: "US-Washington",
- Location: {
- type: "Point",
- coordinates: [-121.49, 46.206],
- },
- Elevation: 3742,
- Type: "Stratovolcano",
- Category: "",
- Status: "Tephrochronology",
- "Last Known Eruption": "Last known eruption from A.D. 1-1499, inclusive",
- id: "9e3c494e-8367-3f50-1f56-8c6fcb961363",
- _rid: "xzo0AJRYUxUFAAAAAAAAAA==",
- _self: "dbs/xzo0AA==/colls/xzo0AJRYUxU=/docs/xzo0AJRYUxUFAAAAAAAAAA==/",
- _etag: '"ce00fa43-0000-0100-0000-652840440000"',
- _attachments: "attachments/",
- _ts: 1697136708,
- };
+ describe("getValueForPath", () => {
+ it("should return the correct value for a simple path", () => {
+ const pathSegments = ["Volcano Name"];
+ expect(getValueForPath(documentContent, pathSegments)).toBe("Adams");
+ });
+ it("should return the correct value for a nested path", () => {
+ const pathSegments = ["Location", "coordinates"];
+ expect(getValueForPath(documentContent, pathSegments)).toEqual([-121.49, 46.206]);
+ });
+ it("should return undefined for a non-existing path", () => {
+ const pathSegments = ["NonExistent", "Path"];
+ expect(getValueForPath(documentContent, pathSegments)).toBeUndefined();
+ });
+ it("should return undefined for an invalid path", () => {
+ const pathSegments = ["Location", "InvalidKey"];
+ expect(getValueForPath(documentContent, pathSegments)).toBeUndefined();
+ });
+ it("should return the root object if pathSegments is empty", () => {
+ const pathSegments: string[] = [];
+ expect(getValueForPath(documentContent, pathSegments)).toBeUndefined();
+ });
+ });
+ describe("extractPartitionKey", () => {
it("should extract single partition key value", () => {
const singlePartitionKeyDefinition: PartitionKeyDefinition = {
kind: PartitionKeyKind.Hash,
@@ -189,5 +212,18 @@ describe("Query Utils", () => {
);
expect(partitionKeyValues.length).toBe(0);
});
+
+ it("should extract all partition key values for hierarchical and nested partition keys", () => {
+ const mixedPartitionKeyDefinition: PartitionKeyDefinition = {
+ kind: PartitionKeyKind.MultiHash,
+ paths: ["/Country", "/Location/type"],
+ };
+ const partitionKeyValues: PartitionKey[] = extractPartitionKeyValues(
+ documentContent,
+ mixedPartitionKeyDefinition,
+ );
+ expect(partitionKeyValues.length).toBe(2);
+ expect(partitionKeyValues).toEqual(["United States", "Point"]);
+ });
});
});
diff --git a/src/Utils/QueryUtils.ts b/src/Utils/QueryUtils.ts
index 3cb3979d3..f0b39e4e2 100644
--- a/src/Utils/QueryUtils.ts
+++ b/src/Utils/QueryUtils.ts
@@ -95,6 +95,24 @@ export const queryPagesUntilContentPresent = async (
return await doRequest(firstItemIndex);
};
+/* eslint-disable @typescript-eslint/no-explicit-any */
+export const getValueForPath = (content: any, pathSegments: string[]): any => {
+ if (pathSegments.length === 0) {
+ return undefined;
+ }
+
+ let currentValue = content;
+
+ for (const segment of pathSegments) {
+ if (!currentValue || currentValue[segment] === undefined) {
+ return undefined;
+ }
+ currentValue = currentValue[segment];
+ }
+
+ return currentValue;
+};
+
/* eslint-disable @typescript-eslint/no-explicit-any */
export const extractPartitionKeyValues = (
documentContent: any,
@@ -105,11 +123,15 @@ export const extractPartitionKeyValues = (
}
const partitionKeyValues: PartitionKey[] = [];
+
partitionKeyDefinition.paths.forEach((partitionKeyPath: string) => {
- const partitionKeyPathWithoutSlash: string = partitionKeyPath.substring(1);
- if (documentContent[partitionKeyPathWithoutSlash] !== undefined) {
- partitionKeyValues.push(documentContent[partitionKeyPathWithoutSlash]);
+ const pathSegments: string[] = partitionKeyPath.substring(1).split("/");
+ const value = getValueForPath(documentContent, pathSegments);
+
+ if (value !== undefined) {
+ partitionKeyValues.push(value);
}
});
+
return partitionKeyValues;
};
diff --git a/test/cassandra/container.spec.ts b/test/cassandra/container.spec.ts
index ac89c915d..5b4b21d62 100644
--- a/test/cassandra/container.spec.ts
+++ b/test/cassandra/container.spec.ts
@@ -14,7 +14,6 @@ test("Cassandra keyspace and table CRUD", async ({ page }) => {
async (panel, okButton) => {
await panel.getByPlaceholder("Type a new keyspace id").fill(keyspaceId);
await panel.getByPlaceholder("Enter table Id").fill(tableId);
- await panel.getByLabel("Table max RU/s").fill("1000");
await okButton.click();
},
{ closeTimeout: 5 * 60 * 1000 },
diff --git a/test/fx.ts b/test/fx.ts
index cfd9aa5e6..b44028dc8 100644
--- a/test/fx.ts
+++ b/test/fx.ts
@@ -40,15 +40,15 @@ export enum TestAccount {
}
export const defaultAccounts: Record = {
- [TestAccount.Tables]: "portal-tables-runner",
- [TestAccount.Cassandra]: "portal-cassandra-runner",
- [TestAccount.Gremlin]: "portal-gremlin-runner",
- [TestAccount.Mongo]: "portal-mongo-runner",
- [TestAccount.Mongo32]: "portal-mongo32-runner",
- [TestAccount.SQL]: "portal-sql-runner-west-us",
+ [TestAccount.Tables]: "github-e2etests-tables",
+ [TestAccount.Cassandra]: "github-e2etests-cassandra",
+ [TestAccount.Gremlin]: "github-e2etests-gremlin",
+ [TestAccount.Mongo]: "github-e2etests-mongo",
+ [TestAccount.Mongo32]: "github-e2etests-mongo32",
+ [TestAccount.SQL]: "github-e2etests-sql",
};
-export const resourceGroupName = process.env.DE_TEST_RESOURCE_GROUP ?? "runners";
+export const resourceGroupName = process.env.DE_TEST_RESOURCE_GROUP ?? "de-e2e-tests";
export const subscriptionId = process.env.DE_TEST_SUBSCRIPTION_ID ?? "69e02f2d-f059-4409-9eac-97e8a276ae2c";
function tryGetStandardName(accountType: TestAccount) {
diff --git a/test/gremlin/container.spec.ts b/test/gremlin/container.spec.ts
index 7c6d066b7..f2efdb214 100644
--- a/test/gremlin/container.spec.ts
+++ b/test/gremlin/container.spec.ts
@@ -16,7 +16,6 @@ test("Gremlin graph CRUD", async ({ page }) => {
await panel.getByPlaceholder("Type a new database id").fill(databaseId);
await panel.getByRole("textbox", { name: "Graph id, Example Graph1" }).fill(graphId);
await panel.getByRole("textbox", { name: "Partition key" }).fill("/pk");
- await panel.getByLabel("Database max RU/s").fill("1000");
await okButton.click();
},
{ closeTimeout: 5 * 60 * 1000 },
diff --git a/test/mongo/container.spec.ts b/test/mongo/container.spec.ts
index 2a3a4d991..a896d4656 100644
--- a/test/mongo/container.spec.ts
+++ b/test/mongo/container.spec.ts
@@ -21,7 +21,6 @@ import { DataExplorer, TestAccount, generateUniqueName } from "../fx";
await panel.getByPlaceholder("Type a new database id").fill(databaseId);
await panel.getByRole("textbox", { name: "Collection id, Example Collection1" }).fill(collectionId);
await panel.getByRole("textbox", { name: "Shard key" }).fill("pk");
- await panel.getByLabel("Database max RU/s").fill("1000");
await okButton.click();
},
{ closeTimeout: 5 * 60 * 1000 },
diff --git a/test/sql/container.spec.ts b/test/sql/container.spec.ts
index 7061bb09f..2fbad2462 100644
--- a/test/sql/container.spec.ts
+++ b/test/sql/container.spec.ts
@@ -15,7 +15,6 @@ test("SQL database and container CRUD", async ({ page }) => {
await panel.getByPlaceholder("Type a new database id").fill(databaseId);
await panel.getByRole("textbox", { name: "Container id, Example Container1" }).fill(containerId);
await panel.getByRole("textbox", { name: "Partition key" }).fill("/pk");
- await panel.getByLabel("Database max RU/s").fill("1000");
await okButton.click();
},
{ closeTimeout: 5 * 60 * 1000 },
diff --git a/utils/cleanupDBs.js b/utils/cleanupDBs.js
index b2bbf0be8..be1e1dc0d 100644
--- a/utils/cleanupDBs.js
+++ b/utils/cleanupDBs.js
@@ -3,7 +3,7 @@ const { CosmosDBManagementClient } = require("@azure/arm-cosmosdb");
const ms = require("ms");
const subscriptionId = process.env["AZURE_SUBSCRIPTION_ID"];
-const resourceGroupName = "runners";
+const resourceGroupName = "de-e2e-tests";
const thirtyMinutesAgo = new Date(Date.now() - 1000 * 60 * 30).getTime();
diff --git a/webpack.config.js b/webpack.config.js
index 16fa3dbab..c21baf3ef 100644
--- a/webpack.config.js
+++ b/webpack.config.js
@@ -23,7 +23,7 @@ const gitSha = childProcess.execSync("git rev-parse HEAD").toString("utf8");
const AZURE_CLIENT_ID = "fd8753b0-0707-4e32-84e9-2532af865fb4";
const AZURE_TENANT_ID = "72f988bf-86f1-41af-91ab-2d7cd011db47";
const SUBSCRIPTION_ID = "69e02f2d-f059-4409-9eac-97e8a276ae2c";
-const RESOURCE_GROUP = "runners";
+const RESOURCE_GROUP = "de-e2e-tests";
const AZURE_CLIENT_SECRET = process.env.AZURE_CLIENT_SECRET || process.env.NOTEBOOKS_TEST_RUNNER_CLIENT_SECRET; // TODO Remove. Exists for backwards compat with old .env files. Prefer AZURE_CLIENT_SECRET
if (!AZURE_CLIENT_SECRET) {
@@ -301,7 +301,7 @@ module.exports = function (_env = {}, argv = {}) {
},
proxy: {
"/api": {
- target: "https://main.documentdb.ext.azure.com",
+ target: "https://cdb-ms-mpac-pbe.cosmos.azure.com",
changeOrigin: true,
logLevel: "debug",
bypass: (req, res) => {
@@ -312,7 +312,7 @@ module.exports = function (_env = {}, argv = {}) {
},
},
"/proxy": {
- target: "https://main.documentdb.ext.azure.com",
+ target: "https://cdb-ms-mpac-pbe.cosmos.azure.com",
changeOrigin: true,
secure: false,
logLevel: "debug",