From 4d0b1a6db8256a742cc6ad182094dd1a60543129 Mon Sep 17 00:00:00 2001 From: jawelton74 <103591340+jawelton74@users.noreply.github.com> Date: Tue, 18 Jun 2024 09:41:24 -0700 Subject: [PATCH 1/8] Switch accountrestrictions call to use new backend in MPAC. (#1868) --- src/Common/Constants.ts | 1 + src/Platform/Hosted/Components/ConnectExplorer.tsx | 9 +++++++-- src/Utils/EndpointUtils.ts | 1 + 3 files changed, 9 insertions(+), 2 deletions(-) diff --git a/src/Common/Constants.ts b/src/Common/Constants.ts index ff121f3a5..cb4c8f007 100644 --- a/src/Common/Constants.ts +++ b/src/Common/Constants.ts @@ -133,6 +133,7 @@ export enum MongoBackendEndpointType { export class BackendApi { public static readonly GenerateToken: string = "GenerateToken"; public static readonly PortalSettings: string = "PortalSettings"; + public static readonly AccountRestrictions: string = "AccountRestrictions"; } export class PortalBackendEndpoints { diff --git a/src/Platform/Hosted/Components/ConnectExplorer.tsx b/src/Platform/Hosted/Components/ConnectExplorer.tsx index 513247a6c..64f8540b7 100644 --- a/src/Platform/Hosted/Components/ConnectExplorer.tsx +++ b/src/Platform/Hosted/Components/ConnectExplorer.tsx @@ -51,13 +51,18 @@ export const fetchEncryptedToken_ToBeDeprecated = async (connectionString: strin export const isAccountRestrictedForConnectionStringLogin = async (connectionString: string): Promise => { const headers = new Headers(); headers.append(HttpHeaders.connectionString, connectionString); - const url = configContext.BACKEND_ENDPOINT + "/api/guest/accountrestrictions/checkconnectionstringlogin"; + + const backendEndpoint: string = useNewPortalBackendEndpoint(BackendApi.PortalSettings) + ? configContext.PORTAL_BACKEND_ENDPOINT + : configContext.BACKEND_ENDPOINT; + + const url = backendEndpoint + "/api/guest/accountrestrictions/checkconnectionstringlogin"; const response = await fetch(url, { headers, method: "POST" }); if (!response.ok) { throw response; } - return (await response.text()) === "True"; + return (await response.text()).toLowerCase() === "true"; }; export const ConnectExplorer: React.FunctionComponent = ({ diff --git a/src/Utils/EndpointUtils.ts b/src/Utils/EndpointUtils.ts index 97e733b98..b685dc71a 100644 --- a/src/Utils/EndpointUtils.ts +++ b/src/Utils/EndpointUtils.ts @@ -164,6 +164,7 @@ export function useNewPortalBackendEndpoint(backendApi: string): boolean { PortalBackendEndpoints.Mpac, PortalBackendEndpoints.Prod, ], + [BackendApi.AccountRestrictions]: [PortalBackendEndpoints.Development, PortalBackendEndpoints.Mpac], }; if (!newBackendApiEnvironmentMap[backendApi] || !configContext.PORTAL_BACKEND_ENDPOINT) { From bf225f91c44b950ae7c6e79f3cf7b54639f179a1 Mon Sep 17 00:00:00 2001 From: sunghyunkang1111 <114709653+sunghyunkang1111@users.noreply.github.com> Date: Tue, 18 Jun 2024 12:15:06 -0500 Subject: [PATCH 2/8] Remove notification for sample collection loading (#1874) --- src/Common/dataAccess/readCollection.ts | 3 --- 1 file changed, 3 deletions(-) diff --git a/src/Common/dataAccess/readCollection.ts b/src/Common/dataAccess/readCollection.ts index 3943b762d..a873aab37 100644 --- a/src/Common/dataAccess/readCollection.ts +++ b/src/Common/dataAccess/readCollection.ts @@ -2,7 +2,6 @@ import { CosmosClient } from "@azure/cosmos"; import { sampleDataClient } from "Common/SampleDataClient"; import { userContext } from "UserContext"; import * as DataModels from "../../Contracts/DataModels"; -import { logConsoleProgress } from "../../Utils/NotificationConsoleUtils"; import { client } from "../CosmosClient"; import { handleError } from "../ErrorHandlingUtils"; @@ -31,7 +30,6 @@ export async function readCollectionInternal( collectionId: string, ): Promise { let collection: DataModels.Collection; - const clearMessage = logConsoleProgress(`Querying container ${collectionId}`); try { const response = await cosmosClient.database(databaseId).container(collectionId).read(); collection = response.resource as DataModels.Collection; @@ -39,6 +37,5 @@ export async function readCollectionInternal( handleError(error, "ReadCollection", `Error while querying container ${collectionId}`); throw error; } - clearMessage(); return collection; } From d199311633a2036341de326fdc9d266847b5b827 Mon Sep 17 00:00:00 2001 From: SATYA SB <107645008+satya07sb@users.noreply.github.com> Date: Wed, 19 Jun 2024 16:02:09 +0530 Subject: [PATCH 3/8] Ensures ARIA attributes are allowed for an element's role. (#1846) * [accessibility-3048277]:[Programmatic Access - Azure Cosmos DB - Data Explorer>New Container]: Ensures ARIA attributes are allowed for an element's role * updated PartitionKeyPane --------- Co-authored-by: Satyapriya Bai --- src/Explorer/Panes/AddCollectionPanel.tsx | 4 +--- .../Panes/ChangePartitionKeyPane/ChangePartitionKeyPane.tsx | 2 +- .../Panes/__snapshots__/AddCollectionPanel.test.tsx.snap | 1 - 3 files changed, 2 insertions(+), 5 deletions(-) diff --git a/src/Explorer/Panes/AddCollectionPanel.tsx b/src/Explorer/Panes/AddCollectionPanel.tsx index f208720cf..a3a01d5fb 100644 --- a/src/Explorer/Panes/AddCollectionPanel.tsx +++ b/src/Explorer/Panes/AddCollectionPanel.tsx @@ -576,9 +576,7 @@ export class AddCollectionPanel extends React.Component - - {this.getPartitionKeySubtext()} - + {this.getPartitionKeySubtext()} = ({ - + {getPartitionKeySubtext(userContext.features.partitionKeyDefault, userContext.apiType)} diff --git a/src/Explorer/Panes/__snapshots__/AddCollectionPanel.test.tsx.snap b/src/Explorer/Panes/__snapshots__/AddCollectionPanel.test.tsx.snap index 0831c5a10..93f52c6ce 100644 --- a/src/Explorer/Panes/__snapshots__/AddCollectionPanel.test.tsx.snap +++ b/src/Explorer/Panes/__snapshots__/AddCollectionPanel.test.tsx.snap @@ -223,7 +223,6 @@ exports[`AddCollectionPanel should render Default properly 1`] = ` Date: Wed, 19 Jun 2024 10:12:38 -0500 Subject: [PATCH 4/8] Fix codeql issues (#1875) --- src/Common/MessageHandler.ts | 13 +++++++++++-- .../Graph/GraphExplorerComponent/GraphUtil.ts | 4 ++-- 2 files changed, 13 insertions(+), 4 deletions(-) diff --git a/src/Common/MessageHandler.ts b/src/Common/MessageHandler.ts index 378c58b5f..2a986f835 100644 --- a/src/Common/MessageHandler.ts +++ b/src/Common/MessageHandler.ts @@ -1,6 +1,7 @@ import { FabricMessageTypes } from "Contracts/FabricMessageTypes"; import Q from "q"; import * as _ from "underscore"; +import * as Logger from "../Common/Logger"; import { MessageTypes } from "../Contracts/ExplorerContracts"; import { getDataExplorerWindow } from "../Utils/WindowUtils"; import * as Constants from "./Constants"; @@ -96,10 +97,18 @@ const _sendMessage = (message: any): void => { const portalChildWindow = getDataExplorerWindow(window) || window; if (portalChildWindow === window) { // Current window is a child of portal, send message to portal window - portalChildWindow.parent.postMessage(message, portalChildWindow.document.referrer || "*"); + if (portalChildWindow.document.referrer) { + portalChildWindow.parent.postMessage(message, portalChildWindow.document.referrer); + } else { + Logger.logError("Iframe failed to send message to portal", "MessageHandler"); + } } else { // Current window is not a child of portal, send message to the child window instead (which is data explorer) - portalChildWindow.postMessage(message, portalChildWindow.location.origin || "*"); + if (portalChildWindow.location.origin) { + portalChildWindow.postMessage(message, portalChildWindow.location.origin); + } else { + Logger.logError("Iframe failed to send message to data explorer", "MessageHandler"); + } } } }; diff --git a/src/Explorer/Graph/GraphExplorerComponent/GraphUtil.ts b/src/Explorer/Graph/GraphExplorerComponent/GraphUtil.ts index 3be076000..c16e9aa30 100644 --- a/src/Explorer/Graph/GraphExplorerComponent/GraphUtil.ts +++ b/src/Explorer/Graph/GraphExplorerComponent/GraphUtil.ts @@ -162,7 +162,7 @@ export const addRootChildToGraph = ( * @param value */ export const escapeDoubleQuotes = (value: string): string => { - return value === undefined ? value : value.replace(/"/g, '\\"'); + return value === undefined ? value : value.replace(/\\/g, "\\\\").replace(/"/g, '\\"'); }; /** @@ -186,5 +186,5 @@ export const getQuotedPropValue = (ip: ViewModels.InputPropertyValue): string => * @param value */ export const escapeSingleQuotes = (value: string): string => { - return value === undefined ? value : value.replace(/'/g, "\\'"); + return value === undefined ? value : value.replace(/\\/g, "\\\\").replace(/'/g, "\\'"); }; From 380caba5f50dbfe921b3a77a804072784215e137 Mon Sep 17 00:00:00 2001 From: Asier Isayas Date: Thu, 20 Jun 2024 12:55:14 -0400 Subject: [PATCH 5/8] Cassandra: Delete a row for a table that has multiple partition keys (#1879) * Delete a row with multiple parition keys * clean up --------- Co-authored-by: Asier Isayas --- src/Explorer/Tables/TableDataClient.ts | 22 ++++++++++++++-------- 1 file changed, 14 insertions(+), 8 deletions(-) diff --git a/src/Explorer/Tables/TableDataClient.ts b/src/Explorer/Tables/TableDataClient.ts index a9d7c96a6..25a43f56b 100644 --- a/src/Explorer/Tables/TableDataClient.ts +++ b/src/Explorer/Tables/TableDataClient.ts @@ -363,18 +363,24 @@ export class CassandraAPIDataClient extends TableDataClient { entitiesToDelete: Entities.ITableEntity[], ): Promise { const query = `DELETE FROM ${collection.databaseId}.${collection.id()} WHERE `; - const partitionKeyProperty = this.getCassandraPartitionKeyProperty(collection); - + const partitionKeys: CassandraTableKey[] = collection.cassandraKeys.partitionKeys; await Promise.all( entitiesToDelete.map(async (currEntityToDelete: Entities.ITableEntity) => { const clearMessage = NotificationConsoleUtils.logConsoleProgress(`Deleting row ${currEntityToDelete.RowKey._}`); - const partitionKeyValue = currEntityToDelete[partitionKeyProperty]; - const currQuery = - query + - (this.isStringType(partitionKeyValue.$) - ? `${partitionKeyProperty} = '${partitionKeyValue._}'` - : `${partitionKeyProperty} = ${partitionKeyValue._}`); + let currQuery = query; + for (let partitionKeyIndex = 0; partitionKeyIndex < partitionKeys.length; partitionKeyIndex++) { + const partitionKey: CassandraTableKey = partitionKeys[partitionKeyIndex]; + const partitionKeyValue: Entities.ITableEntityAttribute = currEntityToDelete[partitionKey.property]; + currQuery = + currQuery + + (this.isStringType(partitionKeyValue.$) + ? `${partitionKey.property} = '${partitionKeyValue._}'` + : `${partitionKey.property} = ${partitionKeyValue._}`); + if (partitionKeyIndex < partitionKeys.length - 1) { + currQuery = `${currQuery} AND `; + } + } try { await this.queryDocuments(collection, currQuery); NotificationConsoleUtils.logConsoleInfo(`Successfully deleted row ${currEntityToDelete.RowKey._}`); From fe892dcc621875f9ba2db505c8da40e41e1b88cd Mon Sep 17 00:00:00 2001 From: Laurent Nguyen Date: Fri, 21 Jun 2024 09:37:34 +0200 Subject: [PATCH 6/8] Temporarily disable bulk Delete for old non-partitioned NoSQL containers (#1880) * Disable Delete button and document selection when partitionKey.systemKey = true for noSql. * Update unit tests * Always show delete button. Use single delete API for systemKey containers --- .../DocumentsTabV2/DocumentsTabV2.test.tsx | 2 +- .../Tabs/DocumentsTabV2/DocumentsTabV2.tsx | 19 +++++++++++++++---- 2 files changed, 16 insertions(+), 5 deletions(-) diff --git a/src/Explorer/Tabs/DocumentsTabV2/DocumentsTabV2.test.tsx b/src/Explorer/Tabs/DocumentsTabV2/DocumentsTabV2.test.tsx index 3d2b1e663..267db89b6 100644 --- a/src/Explorer/Tabs/DocumentsTabV2/DocumentsTabV2.test.tsx +++ b/src/Explorer/Tabs/DocumentsTabV2/DocumentsTabV2.test.tsx @@ -340,7 +340,7 @@ describe("Documents tab (noSql API)", () => { isPreferredApiMongoDB: false, documentIds: [], collection: undefined, - partitionKey: undefined, + partitionKey: { kind: "Hash", paths: ["/foo"], version: 2 }, onLoadStartKey: 0, tabTitle: "", onExecutionErrorChange: (isExecutionError: boolean): void => { diff --git a/src/Explorer/Tabs/DocumentsTabV2/DocumentsTabV2.tsx b/src/Explorer/Tabs/DocumentsTabV2/DocumentsTabV2.tsx index 60367cb66..b243968b6 100644 --- a/src/Explorer/Tabs/DocumentsTabV2/DocumentsTabV2.tsx +++ b/src/Explorer/Tabs/DocumentsTabV2/DocumentsTabV2.tsx @@ -7,7 +7,10 @@ import { getErrorMessage, getErrorStack } from "Common/ErrorHandlingUtils"; import MongoUtility from "Common/MongoUtility"; import { StyleConstants } from "Common/StyleConstants"; import { createDocument } from "Common/dataAccess/createDocument"; -import { deleteDocuments as deleteNoSqlDocuments } from "Common/dataAccess/deleteDocument"; +import { + deleteDocument as deleteNoSqlDocument, + deleteDocuments as deleteNoSqlDocuments, +} from "Common/dataAccess/deleteDocument"; import { queryDocuments } from "Common/dataAccess/queryDocuments"; import { readDocument } from "Common/dataAccess/readDocument"; import { updateDocument } from "Common/dataAccess/updateDocument"; @@ -824,7 +827,7 @@ export const DocumentsTabComponent: React.FunctionComponent => { @@ -834,7 +837,14 @@ export const DocumentsTabComponent: React.FunctionComponent [toDeleteDocumentIds[0]]) + : deleteNoSqlDocuments(_collection, toDeleteDocumentIds) + ) .then( (deletedIds) => { TelemetryProcessor.traceSuccess( @@ -1800,7 +1810,8 @@ export const DocumentsTabComponent: React.FunctionComponent {tableItems.length > 0 && ( From 28db549fa156afcd3796b4f16f9b16631d75d4b0 Mon Sep 17 00:00:00 2001 From: sunghyunkang1111 <114709653+sunghyunkang1111@users.noreply.github.com> Date: Fri, 21 Jun 2024 14:28:00 -0500 Subject: [PATCH 7/8] pagination loading of subscription and databaseaccounts (#1877) --- src/hooks/useDatabaseAccounts.tsx | 16 +++++++++++----- src/hooks/useSubscriptions.tsx | 16 +++++++++++----- 2 files changed, 22 insertions(+), 10 deletions(-) diff --git a/src/hooks/useDatabaseAccounts.tsx b/src/hooks/useDatabaseAccounts.tsx index 9a28afaad..f517b2e30 100644 --- a/src/hooks/useDatabaseAccounts.tsx +++ b/src/hooks/useDatabaseAccounts.tsx @@ -52,11 +52,17 @@ export async function fetchDatabaseAccountsFromGraph( const body = { query: databaseAccountsQuery, subscriptions: [subscriptionId], - ...(skipToken && { - options: { - $skipToken: skipToken, - } as QueryRequestOptions, - }), + ...(skipToken + ? { + options: { + $skipToken: skipToken, + } as QueryRequestOptions, + } + : { + options: { + $top: 150, + } as QueryRequestOptions, + }), }; const response = await fetch(managementResourceGraphAPIURL, { diff --git a/src/hooks/useSubscriptions.tsx b/src/hooks/useSubscriptions.tsx index 5fa7073be..a8e4addf5 100644 --- a/src/hooks/useSubscriptions.tsx +++ b/src/hooks/useSubscriptions.tsx @@ -51,11 +51,17 @@ export async function fetchSubscriptionsFromGraph(accessToken: string): Promise< do { const body = { query: subscriptionsQuery, - ...(skipToken && { - options: { - $skipToken: skipToken, - } as QueryRequestOptions, - }), + ...(skipToken + ? { + options: { + $skipToken: skipToken, + } as QueryRequestOptions, + } + : { + options: { + $top: 150, + } as QueryRequestOptions, + }), }; const response = await fetch(managementResourceGraphAPIURL, { From b07fa89a2363984cbe3d2312bd415b29ee76959e Mon Sep 17 00:00:00 2001 From: Asier Isayas Date: Wed, 26 Jun 2024 14:33:57 -0400 Subject: [PATCH 8/8] fix legacy mongo shell regression (#1883) Co-authored-by: Asier Isayas --- .../Tabs/MongoShellTab/MongoShellTabComponent.tsx | 9 +-------- 1 file changed, 1 insertion(+), 8 deletions(-) diff --git a/src/Explorer/Tabs/MongoShellTab/MongoShellTabComponent.tsx b/src/Explorer/Tabs/MongoShellTab/MongoShellTabComponent.tsx index 052c81ce6..61907c90a 100644 --- a/src/Explorer/Tabs/MongoShellTab/MongoShellTabComponent.tsx +++ b/src/Explorer/Tabs/MongoShellTab/MongoShellTabComponent.tsx @@ -1,6 +1,5 @@ import { useMongoProxyEndpoint } from "Common/MongoProxyClient"; import React, { Component } from "react"; -import * as Constants from "../../../Common/Constants"; import { configContext } from "../../../ConfigContext"; import * as ViewModels from "../../../Contracts/ViewModels"; import { Action, ActionModifiers } from "../../../Shared/Telemetry/TelemetryConstants"; @@ -113,12 +112,6 @@ export default class MongoShellTabComponent extends Component< const resourceId = databaseAccount?.id; const accountName = databaseAccount?.name; const documentEndpoint = databaseAccount?.properties.mongoEndpoint || databaseAccount?.properties.documentEndpoint; - const mongoEndpoint = - documentEndpoint.substr( - Constants.MongoDBAccounts.protocol.length + 3, - documentEndpoint.length - - (Constants.MongoDBAccounts.protocol.length + 2 + Constants.MongoDBAccounts.defaultPort.length), - ) + Constants.MongoDBAccounts.defaultPort.toString(); const databaseId = this.props.collection.databaseId; const collectionId = this.props.collection.id(); const apiEndpoint = this._useMongoProxyEndpoint @@ -132,7 +125,7 @@ export default class MongoShellTabComponent extends Component< data: { resourceId: resourceId, accountName: accountName, - mongoEndpoint: mongoEndpoint, + mongoEndpoint: documentEndpoint, authorization: authorization, databaseId: databaseId, collectionId: collectionId,