Compare commits

..

5 Commits

Author SHA1 Message Date
Senthamil Sindhu
ad165ae069 Add more logs for NPS 2024-01-23 14:55:00 -08:00
Senthamil Sindhu
913a96afec Fix error in code 2024-01-18 11:11:03 -08:00
Senthamil Sindhu
e26207e949 Address comments 2024-01-18 11:04:24 -08:00
Senthamil Sindhu
5d6273889d Remove lint error 2024-01-18 09:49:34 -08:00
Senthamil Sindhu
889cf77801 Update logic for NPS survey for existing accounts > 90 days 2024-01-18 09:22:46 -08:00
47 changed files with 180 additions and 764 deletions

View File

@@ -145,5 +145,4 @@ src/Explorer/Notebook/temp/inputs/connected-editors/codemirror.tsx
src/Explorer/Tree/ResourceTreeAdapter.tsx
__mocks__/monaco-editor.ts
src/Explorer/Tree/ResourceTree.tsx
src/Utils/EndpointUtils.ts
src/Utils/PriorityBasedExecutionUtils.ts

18
package-lock.json generated
View File

@@ -10,7 +10,7 @@
"hasInstallScript": true,
"dependencies": {
"@azure/arm-cosmosdb": "9.1.0",
"@azure/cosmos": "4.0.1-beta.2",
"@azure/cosmos": "4.0.0",
"@azure/cosmos-language-service": "0.0.5",
"@azure/identity": "1.2.1",
"@azure/ms-rest-nodeauth": "3.0.7",
@@ -396,9 +396,9 @@
"integrity": "sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q=="
},
"node_modules/@azure/cosmos": {
"version": "4.0.1-beta.2",
"resolved": "https://registry.npmjs.org/@azure/cosmos/-/cosmos-4.0.1-beta.2.tgz",
"integrity": "sha512-iuqg/QwLQlxgRi4pnXU8JUYv+f24wkRvJ9ZZI4/sYk+DxSgkuQ194Cc2IpckpeO8z7ZpcBkVQFa82wcZVVZ8Zg==",
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/@azure/cosmos/-/cosmos-4.0.0.tgz",
"integrity": "sha512-/Z27p1+FTkmjmm8jk90zi/HrczPHw2t8WecFnsnTe4xGocWl0Z4clP0YlLUTJPhRLWYa5upwD9rMvKJkS1f1kg==",
"dependencies": {
"@azure/abort-controller": "^1.0.0",
"@azure/core-auth": "^1.3.0",
@@ -408,14 +408,14 @@
"fast-json-stable-stringify": "^2.1.0",
"jsbi": "^3.1.3",
"node-abort-controller": "^3.0.0",
"priorityqueuejs": "^2.0.0",
"priorityqueuejs": "^1.0.0",
"semaphore": "^1.0.5",
"tslib": "^2.2.0",
"universal-user-agent": "^6.0.0",
"uuid": "^8.3.0"
},
"engines": {
"node": ">=18.0.0"
"node": ">=14.0.0"
}
},
"node_modules/@azure/cosmos-language-service": {
@@ -33707,9 +33707,9 @@
}
},
"node_modules/priorityqueuejs": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/priorityqueuejs/-/priorityqueuejs-2.0.0.tgz",
"integrity": "sha512-19BMarhgpq3x4ccvVi8k2QpJZcymo/iFUcrhPd4V96kYGovOdTsWwy7fxChYi4QY+m2EnGBWSX9Buakz+tWNQQ=="
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/priorityqueuejs/-/priorityqueuejs-1.0.0.tgz",
"integrity": "sha512-lg++21mreCEOuGWTbO5DnJKAdxfjrdN0S9ysoW9SzdSJvbkWpkaDdpG/cdsPCsEnoLUwmd9m3WcZhngW7yKA2g=="
},
"node_modules/prismjs": {
"version": "1.29.0",

View File

@@ -5,7 +5,7 @@
"main": "index.js",
"dependencies": {
"@azure/arm-cosmosdb": "9.1.0",
"@azure/cosmos": "4.0.1-beta.2",
"@azure/cosmos": "4.0.0",
"@azure/cosmos-language-service": "0.0.5",
"@azure/identity": "1.2.1",
"@azure/ms-rest-nodeauth": "3.0.7",

View File

@@ -211,10 +211,6 @@ export class HttpHeaders {
public static migrateOfferToAutopilot: string = "x-ms-cosmos-migrate-offer-to-autopilot";
}
export class ContentType {
public static applicationJson: string = "application/json";
}
export class ApiType {
// Mapped to hexadecimal values in the backend
public static readonly MongoDB: number = 1;

View File

@@ -1,4 +1,3 @@
import { QueryOperationOptions } from "@azure/cosmos";
import { QueryResults } from "../Contracts/ViewModels";
interface QueryResponse {
@@ -11,17 +10,13 @@ interface QueryResponse {
}
export interface MinimalQueryIterator {
fetchNext: (queryOperationOptions?: QueryOperationOptions) => Promise<QueryResponse>;
fetchNext: () => Promise<QueryResponse>;
}
// Pick<QueryIterator<any>, "fetchNext">;
export function nextPage(
documentsIterator: MinimalQueryIterator,
firstItemIndex: number,
queryOperationOptions?: QueryOperationOptions,
): Promise<QueryResults> {
return documentsIterator.fetchNext(queryOperationOptions).then((response) => {
export function nextPage(documentsIterator: MinimalQueryIterator, firstItemIndex: number): Promise<QueryResults> {
return documentsIterator.fetchNext().then((response) => {
const documents = response.resources;
// eslint-disable-next-line @typescript-eslint/no-explicit-any
const headers = (response as any).headers || {}; // TODO this is a private key. Remove any

View File

@@ -1,6 +1,6 @@
import { Constants as CosmosSDKConstants } from "@azure/cosmos";
import { MongoProxyEndpoints, allowedMongoProxyEndpoints_ToBeDeprecated, validateEndpoint } from "Utils/EndpointUtils";
import queryString from "querystring";
import { allowedMongoProxyEndpoints, validateEndpoint } from "Utils/EndpointValidation";
import { AuthType } from "../AuthType";
import { configContext } from "../ConfigContext";
import * as DataModels from "../Contracts/DataModels";
@@ -10,7 +10,7 @@ import DocumentId from "../Explorer/Tree/DocumentId";
import { hasFlag } from "../Platform/Hosted/extractFeatures";
import { userContext } from "../UserContext";
import { logConsoleError } from "../Utils/NotificationConsoleUtils";
import { ApiType, ContentType, HttpHeaders, HttpStatusCodes } from "./Constants";
import { ApiType, HttpHeaders, HttpStatusCodes } from "./Constants";
import { MinimalQueryIterator } from "./IteratorUtilities";
import { sendMessage } from "./MessageHandler";
@@ -62,73 +62,6 @@ export function queryDocuments(
isResourceList: boolean,
query: string,
continuationToken?: string,
): Promise<QueryResponse> {
if (!useMongoProxyEndpoint("resourcelist")) {
return queryDocuments_ToBeDeprecated(databaseId, collection, isResourceList, query, continuationToken);
}
const { databaseAccount } = userContext;
const resourceEndpoint = databaseAccount.properties.mongoEndpoint || databaseAccount.properties.documentEndpoint;
const params = {
databaseID: databaseId,
collectionID: collection.id(),
resourceUrl: `${resourceEndpoint}dbs/${databaseId}/colls/${collection.id()}/docs/`,
resourceID: collection.rid,
resourceType: "docs",
subscriptionID: userContext.subscriptionId,
resourceGroup: userContext.resourceGroup,
databaseAccountName: databaseAccount.name,
partitionKey:
collection && collection.partitionKey && !collection.partitionKey.systemKey
? collection.partitionKeyProperties?.[0]
: "",
query,
};
const endpoint = getFeatureEndpointOrDefault("resourcelist") || "";
const headers = {
...defaultHeaders,
...authHeaders(),
[CosmosSDKConstants.HttpHeaders.IsQuery]: "true",
[CosmosSDKConstants.HttpHeaders.PopulateQueryMetrics]: "true",
[CosmosSDKConstants.HttpHeaders.EnableScanInQuery]: "true",
[CosmosSDKConstants.HttpHeaders.EnableCrossPartitionQuery]: "true",
[CosmosSDKConstants.HttpHeaders.ParallelizeCrossPartitionQuery]: "true",
[HttpHeaders.contentType]: "application/query+json",
};
if (continuationToken) {
headers[CosmosSDKConstants.HttpHeaders.Continuation] = continuationToken;
}
const path = isResourceList ? "/resourcelist" : "";
return window
.fetch(`${endpoint}${path}`, {
method: "POST",
body: JSON.stringify(params),
headers,
})
.then(async (response) => {
if (response.ok) {
return {
continuationToken: response.headers.get(CosmosSDKConstants.HttpHeaders.Continuation),
documents: (await response.json()).Documents as DataModels.DocumentId[],
headers: response.headers,
};
}
await errorHandling(response, "querying documents", params);
return undefined;
});
}
function queryDocuments_ToBeDeprecated(
databaseId: string,
collection: Collection,
isResourceList: boolean,
query: string,
continuationToken?: string,
): Promise<QueryResponse> {
const { databaseAccount } = userContext;
const resourceEndpoint = databaseAccount.properties.mongoEndpoint || databaseAccount.properties.documentEndpoint;
@@ -189,54 +122,6 @@ export function readDocument(
databaseId: string,
collection: Collection,
documentId: DocumentId,
): Promise<DataModels.DocumentId> {
if (!useMongoProxyEndpoint("readDocument")) {
return readDocument_ToBeDeprecated(databaseId, collection, documentId);
}
const { databaseAccount } = userContext;
const resourceEndpoint = databaseAccount.properties.mongoEndpoint || databaseAccount.properties.documentEndpoint;
const idComponents = documentId.self.split("/");
const path = idComponents.slice(0, 4).join("/");
const rid = encodeURIComponent(idComponents[5]);
const params = {
databaseID: databaseId,
collectionID: collection.id(),
resourceUrl: `${resourceEndpoint}${path}/${rid}`,
resourceID: rid,
resourceType: "docs",
subscriptionID: userContext.subscriptionId,
resourceGroup: userContext.resourceGroup,
databaseAccountName: databaseAccount.name,
partitionKey:
documentId && documentId.partitionKey && !documentId.partitionKey.systemKey
? documentId.partitionKeyProperties?.[0]
: "",
};
const endpoint = getFeatureEndpointOrDefault("readDocument");
return window
.fetch(endpoint, {
method: "POST",
body: JSON.stringify(params),
headers: {
...defaultHeaders,
...authHeaders(),
[HttpHeaders.contentType]: ContentType.applicationJson,
},
})
.then(async (response) => {
if (response.ok) {
return response.json();
}
return await errorHandling(response, "reading document", params);
});
}
export function readDocument_ToBeDeprecated(
databaseId: string,
collection: Collection,
documentId: DocumentId,
): Promise<DataModels.DocumentId> {
const { databaseAccount } = userContext;
const resourceEndpoint = databaseAccount.properties.mongoEndpoint || databaseAccount.properties.documentEndpoint;
@@ -284,51 +169,6 @@ export function createDocument(
collection: Collection,
partitionKeyProperty: string,
documentContent: unknown,
): Promise<DataModels.DocumentId> {
if (!useMongoProxyEndpoint("createDocument")) {
return createDocument_ToBeDeprecated(databaseId, collection, partitionKeyProperty, documentContent);
}
const { databaseAccount } = userContext;
const resourceEndpoint = databaseAccount.properties.mongoEndpoint || databaseAccount.properties.documentEndpoint;
const params = {
databaseID: databaseId,
collectionID: collection.id(),
resourceUrl: `${resourceEndpoint}dbs/${databaseId}/colls/${collection.id()}/docs/`,
resourceID: collection.rid,
resourceType: "docs",
subscriptionID: userContext.subscriptionId,
resourceGroup: userContext.resourceGroup,
databaseAccountName: databaseAccount.name,
partitionKey:
collection && collection.partitionKey && !collection.partitionKey.systemKey ? partitionKeyProperty : "",
documentContent: JSON.stringify(documentContent),
};
const endpoint = getFeatureEndpointOrDefault("createDocument");
return window
.fetch(`${endpoint}/createDocument`, {
method: "POST",
body: JSON.stringify(params),
headers: {
...defaultHeaders,
...authHeaders(),
[HttpHeaders.contentType]: ContentType.applicationJson,
},
})
.then(async (response) => {
if (response.ok) {
return response.json();
}
return await errorHandling(response, "creating document", params);
});
}
export function createDocument_ToBeDeprecated(
databaseId: string,
collection: Collection,
partitionKeyProperty: string,
documentContent: unknown,
): Promise<DataModels.DocumentId> {
const { databaseAccount } = userContext;
const resourceEndpoint = databaseAccount.properties.mongoEndpoint || databaseAccount.properties.documentEndpoint;
@@ -368,56 +208,6 @@ export function updateDocument(
collection: Collection,
documentId: DocumentId,
documentContent: string,
): Promise<DataModels.DocumentId> {
if (!useMongoProxyEndpoint("updateDocument")) {
return updateDocument_ToBeDeprecated(databaseId, collection, documentId, documentContent);
}
const { databaseAccount } = userContext;
const resourceEndpoint = databaseAccount.properties.mongoEndpoint || databaseAccount.properties.documentEndpoint;
const idComponents = documentId.self.split("/");
const path = idComponents.slice(0, 5).join("/");
const rid = encodeURIComponent(idComponents[5]);
const params = {
databaseID: databaseId,
collectionID: collection.id(),
resourceUrl: `${resourceEndpoint}${path}/${rid}`,
resourceID: rid,
resourceType: "docs",
subscriptionID: userContext.subscriptionId,
resourceGroup: userContext.resourceGroup,
databaseAccountName: databaseAccount.name,
partitionKey:
documentId && documentId.partitionKey && !documentId.partitionKey.systemKey
? documentId.partitionKeyProperties?.[0]
: "",
documentContent,
};
const endpoint = getFeatureEndpointOrDefault("updateDocument");
return window
.fetch(endpoint, {
method: "PUT",
body: JSON.stringify(params),
headers: {
...defaultHeaders,
...authHeaders(),
[HttpHeaders.contentType]: ContentType.applicationJson,
[CosmosSDKConstants.HttpHeaders.PartitionKey]: JSON.stringify(documentId.partitionKeyHeader()),
},
})
.then(async (response) => {
if (response.ok) {
return response.json();
}
return await errorHandling(response, "updating document", params);
});
}
export function updateDocument_ToBeDeprecated(
databaseId: string,
collection: Collection,
documentId: DocumentId,
documentContent: string,
): Promise<DataModels.DocumentId> {
const { databaseAccount } = userContext;
const resourceEndpoint = databaseAccount.properties.mongoEndpoint || databaseAccount.properties.documentEndpoint;
@@ -447,7 +237,7 @@ export function updateDocument_ToBeDeprecated(
headers: {
...defaultHeaders,
...authHeaders(),
[HttpHeaders.contentType]: ContentType.applicationJson,
[HttpHeaders.contentType]: "application/json",
[CosmosSDKConstants.HttpHeaders.PartitionKey]: JSON.stringify(documentId.partitionKeyHeader()),
},
})
@@ -460,53 +250,6 @@ export function updateDocument_ToBeDeprecated(
}
export function deleteDocument(databaseId: string, collection: Collection, documentId: DocumentId): Promise<void> {
if (!useMongoProxyEndpoint("deleteDocument")) {
deleteDocument_ToBeDeprecated(databaseId, collection, documentId);
}
const { databaseAccount } = userContext;
const resourceEndpoint = databaseAccount.properties.mongoEndpoint || databaseAccount.properties.documentEndpoint;
const idComponents = documentId.self.split("/");
const path = idComponents.slice(0, 5).join("/");
const rid = encodeURIComponent(idComponents[5]);
const params = {
databaseID: databaseId,
collectionID: collection.id(),
resourceUrl: `${resourceEndpoint}${path}/${rid}`,
resourceID: rid,
resourceType: "docs",
subscriptionID: userContext.subscriptionId,
resourceGroup: userContext.resourceGroup,
databaseAccountName: databaseAccount.name,
partitionKey:
documentId && documentId.partitionKey && !documentId.partitionKey.systemKey
? documentId.partitionKeyProperties?.[0]
: "",
};
const endpoint = getFeatureEndpointOrDefault("deleteDocument");
return window
.fetch(endpoint, {
method: "DELETE",
body: JSON.stringify(params),
headers: {
...defaultHeaders,
...authHeaders(),
[HttpHeaders.contentType]: ContentType.applicationJson,
},
})
.then(async (response) => {
if (response.ok) {
return undefined;
}
return await errorHandling(response, "deleting document", params);
});
}
export function deleteDocument_ToBeDeprecated(
databaseId: string,
collection: Collection,
documentId: DocumentId,
): Promise<void> {
const { databaseAccount } = userContext;
const resourceEndpoint = databaseAccount.properties.mongoEndpoint || databaseAccount.properties.documentEndpoint;
const idComponents = documentId.self.split("/");
@@ -534,7 +277,7 @@ export function deleteDocument_ToBeDeprecated(
headers: {
...defaultHeaders,
...authHeaders(),
[HttpHeaders.contentType]: ContentType.applicationJson,
[HttpHeaders.contentType]: "application/json",
[CosmosSDKConstants.HttpHeaders.PartitionKey]: JSON.stringify(documentId.partitionKeyHeader()),
},
})
@@ -548,52 +291,6 @@ export function deleteDocument_ToBeDeprecated(
export function createMongoCollectionWithProxy(
params: DataModels.CreateCollectionParams,
): Promise<DataModels.Collection> {
if (!useMongoProxyEndpoint("createCollectionWithProxy")) {
createMongoCollectionWithProxy_ToBeDeprecated(params);
}
const { databaseAccount } = userContext;
const shardKey: string = params.partitionKey?.paths[0];
const createCollectionParams = {
databaseID: params.databaseId,
collectionID: params.collectionId,
resourceUrl: databaseAccount.properties.mongoEndpoint || databaseAccount.properties.documentEndpoint,
resourceID: "",
resourceType: "colls",
subscriptionID: userContext.subscriptionId,
resourceGroup: userContext.resourceGroup,
databaseAccountName: databaseAccount.name,
partitionKey: shardKey,
isAutoscale: !!params.autoPilotMaxThroughput,
hasSharedThroughput: params.databaseLevelThroughput,
offerThroughput: params.autoPilotMaxThroughput || params.offerThroughput,
createDatabase: params.createNewDatabase,
isSharded: !!shardKey,
};
const endpoint = getFeatureEndpointOrDefault("createCollectionWithProxy");
return window
.fetch(`${endpoint}/createCollection`, {
method: "POST",
body: JSON.stringify(createCollectionParams),
headers: {
...defaultHeaders,
...authHeaders(),
[HttpHeaders.contentType]: ContentType.applicationJson,
},
})
.then(async (response) => {
if (response.ok) {
return response.json();
}
return await errorHandling(response, "creating collection", createCollectionParams);
});
}
export function createMongoCollectionWithProxy_ToBeDeprecated(
params: DataModels.CreateCollectionParams,
): Promise<DataModels.Collection> {
const { databaseAccount } = userContext;
const shardKey: string = params.partitionKey?.paths[0];
@@ -637,17 +334,13 @@ export function createMongoCollectionWithProxy_ToBeDeprecated(
return await errorHandling(response, "creating collection", mongoParams);
});
}
export function getFeatureEndpointOrDefault(feature: string): string {
let endpoint;
if (useMongoProxyEndpoint(feature)) {
endpoint = configContext.MONGO_PROXY_ENDPOINT;
} else {
endpoint =
hasFlag(userContext.features.mongoProxyAPIs, feature) &&
validateEndpoint(userContext.features.mongoProxyEndpoint, allowedMongoProxyEndpoints_ToBeDeprecated)
? userContext.features.mongoProxyEndpoint
: configContext.MONGO_BACKEND_ENDPOINT || configContext.BACKEND_ENDPOINT;
}
const endpoint =
hasFlag(userContext.features.mongoProxyAPIs, feature) &&
validateEndpoint(userContext.features.mongoProxyEndpoint, allowedMongoProxyEndpoints)
? userContext.features.mongoProxyEndpoint
: configContext.MONGO_BACKEND_ENDPOINT || configContext.BACKEND_ENDPOINT;
return getEndpoint(endpoint);
}
@@ -656,11 +349,7 @@ export function getEndpoint(endpoint: string): string {
let url = endpoint + "/api/mongo/explorer";
if (userContext.authType === AuthType.EncryptedToken) {
if (endpoint === configContext.MONGO_PROXY_ENDPOINT) {
url = url.replace("api/mongo", "api/connectionstring/mongo");
} else {
url = url.replace("api/mongo", "api/guest/mongo");
}
url = url.replace("api/mongo", "api/guest/mongo");
}
return url;
}
@@ -681,10 +370,3 @@ async function errorHandling(response: Response, action: string, params: unknown
export function getARMCreateCollectionEndpoint(params: DataModels.MongoParameters): string {
return `subscriptions/${params.sid}/resourceGroups/${params.rg}/providers/Microsoft.DocumentDB/databaseAccounts/${userContext.databaseAccount.name}/mongodbDatabases/${params.db}/collections/${params.coll}`;
}
function useMongoProxyEndpoint(api: string): boolean {
return (
configContext.NEW_MONGO_APIS?.includes(api) &&
[MongoProxyEndpoints.Development, MongoProxyEndpoints.MPAC].includes(configContext.MONGO_PROXY_ENDPOINT)
);
}

View File

@@ -1,4 +1,3 @@
import { QueryOperationOptions } from "@azure/cosmos";
import { QueryResults } from "../../Contracts/ViewModels";
import { logConsoleInfo, logConsoleProgress } from "../../Utils/NotificationConsoleUtils";
import { getEntityName } from "../DocumentUtility";
@@ -9,13 +8,12 @@ export const queryDocumentsPage = async (
resourceName: string,
documentsIterator: MinimalQueryIterator,
firstItemIndex: number,
queryOperationOptions?: QueryOperationOptions,
): Promise<QueryResults> => {
const entityName = getEntityName();
const clearMessage = logConsoleProgress(`Querying ${entityName} for container ${resourceName}`);
try {
const result: QueryResults = await nextPage(documentsIterator, firstItemIndex, queryOperationOptions);
const result: QueryResults = await nextPage(documentsIterator, firstItemIndex);
const itemCount = (result.documents && result.documents.length) || 0;
logConsoleInfo(`Successfully fetched ${itemCount} ${entityName} for container ${resourceName}`);
return result;

View File

@@ -7,12 +7,11 @@ import {
allowedHostedExplorerEndpoints,
allowedJunoOrigins,
allowedMongoBackendEndpoints,
allowedMongoProxyEndpoints,
allowedMsalRedirectEndpoints,
defaultAllowedArmEndpoints,
defaultAllowedBackendEndpoints,
validateEndpoint,
} from "Utils/EndpointUtils";
} from "Utils/EndpointValidation";
export enum Platform {
Portal = "Portal",
@@ -39,8 +38,6 @@ export interface ConfigContext {
ARCADIA_LIVY_ENDPOINT_DNS_ZONE: string;
BACKEND_ENDPOINT?: string;
MONGO_BACKEND_ENDPOINT?: string;
MONGO_PROXY_ENDPOINT?: string;
NEW_MONGO_APIS?: string[];
PROXY_PATH?: string;
JUNO_ENDPOINT: string;
GITHUB_CLIENT_ID: string;
@@ -85,15 +82,6 @@ let configContext: Readonly<ConfigContext> = {
GITHUB_TEST_ENV_CLIENT_ID: "b63fc8cbf87fd3c6e2eb", // Registered OAuth app: https://github.com/organizations/AzureCosmosDBNotebooks/settings/applications/1777772
JUNO_ENDPOINT: JunoEndpoints.Prod,
BACKEND_ENDPOINT: "https://main.documentdb.ext.azure.com",
MONGO_PROXY_ENDPOINT: "https://cdb-ms-prod-mp.cosmos.azure.com",
NEW_MONGO_APIS: [
// "resourcelist",
// "createDocument",
// "readDocument",
// "updateDocument",
// "deleteDocument",
// "createCollectionWithProxy",
],
isTerminalEnabled: false,
isPhoenixEnabled: false,
};
@@ -139,10 +127,6 @@ export function updateConfigContext(newContext: Partial<ConfigContext>): void {
delete newContext.BACKEND_ENDPOINT;
}
if (!validateEndpoint(newContext.MONGO_PROXY_ENDPOINT, allowedMongoProxyEndpoints)) {
delete newContext.MONGO_PROXY_ENDPOINT;
}
if (!validateEndpoint(newContext.MONGO_BACKEND_ENDPOINT, allowedMongoBackendEndpoints)) {
delete newContext.MONGO_BACKEND_ENDPOINT;
}

View File

@@ -1,6 +1,6 @@
import { MessageTypes } from "Contracts/MessageTypes";
import * as ActionContracts from "./ActionContracts";
import * as Diagnostics from "./Diagnostics";
import { MessageTypes } from "./MessageTypes";
import * as Versions from "./Versions";
export { ActionContracts, Diagnostics, MessageTypes, Versions };

View File

@@ -46,7 +46,6 @@ export enum MessageTypes {
GetAuthorizationToken, // Data Explorer -> Fabric
GetAllResourceTokens, // Data Explorer -> Fabric
Ready, // Data Explorer -> Fabric
OpenCESCVAFeedbackBlade,
}
export interface AuthorizationToken {

View File

@@ -386,9 +386,9 @@ export interface DataExplorerInputsFrame {
dnsSuffix?: string;
serverId?: string;
extensionEndpoint?: string;
mongoProxyEndpoint?: string;
subscriptionType?: SubscriptionType;
quotaId?: string;
addCollectionDefaultFlight?: string;
isTryCosmosDBSubscription?: boolean;
loadDatabaseAccountTimestamp?: number;
sharedThroughputMinimum?: number;

View File

@@ -7,7 +7,7 @@ import { getCopilotEnabled, isCopilotFeatureRegistered } from "Explorer/QueryCop
import { IGalleryItem } from "Juno/JunoClient";
import { scheduleRefreshDatabaseResourceToken } from "Platform/Fabric/FabricUtil";
import { LocalStorageUtility, StorageKey } from "Shared/StorageUtility";
import { allowedNotebookServerUrls, validateEndpoint } from "Utils/EndpointUtils";
import { allowedNotebookServerUrls, validateEndpoint } from "Utils/EndpointValidation";
import { useQueryCopilot } from "hooks/useQueryCopilot";
import * as ko from "knockout";
import React from "react";
@@ -265,37 +265,80 @@ export default class Explorer {
// TODO: return result
}
private getRandomInt(max: number) {
return Math.floor(Math.random() * max);
}
public openNPSSurveyDialog(): void {
if (!Platform.Portal) {
return;
}
const NINETY_DAYS_IN_MS = 7776000000;
const ONE_DAY_IN_MS = 86400000;
const SEVEN_DAYS_IN_MS = 604800000;
const THREE_DAYS_IN_MS = 259200000;
const lastSubmitted: string = localStorage.getItem("lastSubmitted");
Logger.logInfo(`NPS Survey last shown date: ${lastSubmitted}`, "Explorer/openNPSSurveyDialog");
if (lastSubmitted !== null) {
Logger.logInfo(`NPS Survey last shown is not empty ${lastSubmitted}`, "Explorer/openNPSSurveyDialog");
let lastSubmittedDate: number = parseInt(lastSubmitted);
Logger.logInfo(`NPS Survey last shown is parsed ${lastSubmittedDate.toString()}`, "Explorer/openNPSSurveyDialog");
if (isNaN(lastSubmittedDate)) {
Logger.logInfo(
`NPS Survey last shown is not a number ${lastSubmittedDate.toString()}`,
"Explorer/openNPSSurveyDialog",
);
lastSubmittedDate = 0;
}
const nowMs: number = Date.now();
Logger.logInfo(`NPS Survey current date ${nowMs.toString()}`, "Explorer/openNPSSurveyDialog");
const millisecsSinceLastSubmitted = nowMs - lastSubmittedDate;
if (millisecsSinceLastSubmitted < NINETY_DAYS_IN_MS) {
Logger.logInfo(
`NPS Survey last shown is less than ninety days ${millisecsSinceLastSubmitted.toString()}`,
"Explorer/openNPSSurveyDialog",
);
return;
}
}
// Try Cosmos DB subscription - survey shown to 100% of users at day 1 in Data Explorer.
if (userContext.isTryCosmosDBSubscription) {
if (isAccountNewerThanThresholdInMs(userContext.databaseAccount?.systemData?.createdAt || "", ONE_DAY_IN_MS)) {
Logger.logInfo(
`Sending message to Portal to check if NPS Survey can be displayed in Try Cosmos DB ${userContext.apiType}`,
`Displaying NPS Survey for Try Cosmos DB ${userContext.apiType}`,
"Explorer/openNPSSurveyDialog",
);
sendMessage({ type: MessageTypes.DisplayNPSSurvey });
this.sendNPSMessage();
}
} else {
// Show survey when an existing account is older than 7 days
// Show survey when an existing account is older than 3 days
if (
!isAccountNewerThanThresholdInMs(userContext.databaseAccount?.systemData?.createdAt || "", SEVEN_DAYS_IN_MS)
!isAccountNewerThanThresholdInMs(userContext.databaseAccount?.systemData?.createdAt || "", THREE_DAYS_IN_MS)
) {
Logger.logInfo(
`Sending message to Portal to check if NPS Survey can be displayed for existing ${userContext.apiType} account older than 7 days`,
`Displaying NPS Survey for users with existing ${userContext.apiType} account older than 3 days`,
"Explorer/openNPSSurveyDialog",
);
sendMessage({ type: MessageTypes.DisplayNPSSurvey });
this.sendNPSMessage();
}
}
}
private sendNPSMessage() {
sendMessage({ type: MessageTypes.DisplayNPSSurvey });
Logger.logInfo(
`NPS Survey logging current date when survey is shown ${Date.now().toString()}`,
"Explorer/openNPSSurveyDialog",
);
localStorage.setItem("lastSubmitted", Date.now().toString());
}
public async refreshDatabaseForResourceToken(): Promise<void> {
const databaseId = userContext.parsedResourceToken?.databaseId;
const collectionId = userContext.parsedResourceToken?.collectionId;

View File

@@ -135,7 +135,7 @@ export function createStaticCommandBarButtons(
buttons.push(newSqlQueryBtn);
}
if (isQuerySupported && selectedNodeState.findSelectedCollection() && configContext.platform !== Platform.Fabric) {
if (isQuerySupported && selectedNodeState.findSelectedCollection()) {
const openQueryBtn = createOpenQueryButton(container);
openQueryBtn.children = [createOpenQueryButton(container), createOpenQueryFromDiskButton()];
buttons.push(openQueryBtn);

View File

@@ -1,11 +1,12 @@
import { Checkbox, Stack, Text, TextField } from "@fluentui/react";
import React, { FunctionComponent, useEffect, useState } from "react";
import * as Constants from "../../../Common/Constants";
import { createDatabase } from "../../../Common/dataAccess/createDatabase";
import { getErrorMessage, getErrorStack } from "../../../Common/ErrorHandlingUtils";
import { InfoTooltip } from "../../../Common/Tooltip/InfoTooltip";
import { createDatabase } from "../../../Common/dataAccess/createDatabase";
import * as DataModels from "../../../Contracts/DataModels";
import { SubscriptionType } from "../../../Contracts/SubscriptionType";
import { useSidePanel } from "../../../hooks/useSidePanel";
import * as SharedConstants from "../../../Shared/Constants";
import { Action, ActionModifiers } from "../../../Shared/Telemetry/TelemetryConstants";
import * as TelemetryProcessor from "../../../Shared/Telemetry/TelemetryProcessor";
@@ -13,7 +14,6 @@ import { userContext } from "../../../UserContext";
import * as AutoPilotUtils from "../../../Utils/AutoPilotUtils";
import { isServerlessAccount } from "../../../Utils/CapabilityUtils";
import { getUpsellMessage } from "../../../Utils/PricingUtils";
import { useSidePanel } from "../../../hooks/useSidePanel";
import { ThroughputInput } from "../../Controls/ThroughputInput/ThroughputInput";
import Explorer from "../../Explorer";
import { useDatabases } from "../../useDatabases";
@@ -63,6 +63,9 @@ export const AddDatabasePanel: FunctionComponent<AddDatabasePaneProps> = ({
},
subscriptionType: SubscriptionType[subscriptionType],
subscriptionQuotaId: userContext.quotaId,
defaultsCheck: {
flight: userContext.addCollectionFlight,
},
dataExplorerArea: Constants.Areas.ContextualPane,
};
@@ -72,6 +75,7 @@ export const AddDatabasePanel: FunctionComponent<AddDatabasePaneProps> = ({
subscriptionQuotaId: userContext.quotaId,
defaultsCheck: {
throughput,
flight: userContext.addCollectionFlight,
},
dataExplorerArea: Constants.Areas.ContextualPane,
};

View File

@@ -59,6 +59,7 @@ export const CassandraAddCollectionPane: FunctionComponent<CassandraAddCollectio
defaultsCheck: {
storage: "u",
throughput: newKeySpaceThroughput || tableThroughput,
flight: userContext.addCollectionFlight,
},
dataExplorerArea: Constants.Areas.ContextualPane,
};

View File

@@ -11,13 +11,7 @@ import {
import * as Constants from "Common/Constants";
import { InfoTooltip } from "Common/Tooltip/InfoTooltip";
import { configContext } from "ConfigContext";
import {
DefaultRUThreshold,
LocalStorageUtility,
StorageKey,
getRUThreshold,
ruThresholdEnabled as isRUThresholdEnabled,
} from "Shared/StorageUtility";
import { LocalStorageUtility, StorageKey } from "Shared/StorageUtility";
import * as StringUtility from "Shared/StringUtility";
import { userContext } from "UserContext";
import { logConsoleInfo } from "Utils/NotificationConsoleUtils";
@@ -41,8 +35,6 @@ export const SettingsPane: FunctionComponent<{ explorer: Explorer }> = ({
? Constants.Queries.UnlimitedPageOption
: Constants.Queries.CustomPageOption,
);
const [ruThresholdEnabled, setRUThresholdEnabled] = useState<boolean>(isRUThresholdEnabled());
const [ruThreshold, setRUThreshold] = useState<number>(getRUThreshold());
const [queryTimeoutEnabled, setQueryTimeoutEnabled] = useState<boolean>(
LocalStorageUtility.getEntryBoolean(StorageKey.QueryTimeoutEnabled),
);
@@ -111,7 +103,6 @@ export const SettingsPane: FunctionComponent<{ explorer: Explorer }> = ({
isCustomPageOptionSelected() ? customItemPerPage : Constants.Queries.unlimitedItemsPerPage,
);
LocalStorageUtility.setEntryNumber(StorageKey.CustomItemPerPage, customItemPerPage);
LocalStorageUtility.setEntryBoolean(StorageKey.RUThresholdEnabled, ruThresholdEnabled);
LocalStorageUtility.setEntryBoolean(StorageKey.QueryTimeoutEnabled, queryTimeoutEnabled);
LocalStorageUtility.setEntryNumber(StorageKey.RetryAttempts, retryAttempts);
LocalStorageUtility.setEntryNumber(StorageKey.RetryInterval, retryInterval);
@@ -129,10 +120,6 @@ export const SettingsPane: FunctionComponent<{ explorer: Explorer }> = ({
);
}
if (ruThresholdEnabled) {
LocalStorageUtility.setEntryNumber(StorageKey.RUThreshold, ruThreshold);
}
if (queryTimeoutEnabled) {
LocalStorageUtility.setEntryNumber(StorageKey.QueryTimeout, queryTimeout);
LocalStorageUtility.setEntryBoolean(
@@ -208,17 +195,6 @@ export const SettingsPane: FunctionComponent<{ explorer: Explorer }> = ({
setPageOption(option.key);
};
const handleOnRUThresholdToggleChange = (ev: React.MouseEvent<HTMLElement>, checked?: boolean): void => {
setRUThresholdEnabled(checked);
};
const handleOnRUThresholdSpinButtonChange = (ev: React.MouseEvent<HTMLElement>, newValue?: string): void => {
const ruThreshold = Number(newValue);
if (!isNaN(ruThreshold)) {
setRUThreshold(ruThreshold);
}
};
const handleOnQueryTimeoutToggleChange = (ev: React.MouseEvent<HTMLElement>, checked?: boolean): void => {
setQueryTimeoutEnabled(checked);
};
@@ -258,7 +234,7 @@ export const SettingsPane: FunctionComponent<{ explorer: Explorer }> = ({
const handleSampleDatabaseChange = async (ev: React.MouseEvent<HTMLElement>, checked?: boolean): Promise<void> => {
setCopilotSampleDBEnabled(checked);
useQueryCopilot.getState().setCopilotSampleDBEnabled(checked);
setRefreshExplorer(false);
setRefreshExplorer(!refreshExplorer);
};
const choiceButtonStyles = {
@@ -283,7 +259,7 @@ export const SettingsPane: FunctionComponent<{ explorer: Explorer }> = ({
],
};
const toggleStyles: IToggleStyles = {
const queryTimeoutToggleStyles: IToggleStyles = {
label: {
fontSize: 12,
fontWeight: 400,
@@ -296,7 +272,7 @@ export const SettingsPane: FunctionComponent<{ explorer: Explorer }> = ({
text: {},
};
const spinButtonStyles: ISpinButtonStyles = {
const queryTimeoutSpinButtonStyles: ISpinButtonStyles = {
label: {
fontSize: 12,
fontWeight: 400,
@@ -362,83 +338,48 @@ export const SettingsPane: FunctionComponent<{ explorer: Explorer }> = ({
</div>
)}
{userContext.apiType === "SQL" && (
<>
<div className="settingsSection">
<div className="settingsSectionPart">
<div>
<legend id="ruThresholdLabel" className="settingsSectionLabel legendLabel">
RU Threshold
</legend>
<InfoTooltip>If a query exceeds a configured RU threshold, the query will be aborted.</InfoTooltip>
</div>
<div className="settingsSection">
<div className="settingsSectionPart">
<div>
<legend id="queryTimeoutLabel" className="settingsSectionLabel legendLabel">
Query Timeout
</legend>
<InfoTooltip>
When a query reaches a specified time limit, a popup with an option to cancel the query will show
unless automatic cancellation has been enabled
</InfoTooltip>
</div>
<div>
<Toggle
styles={queryTimeoutToggleStyles}
label="Enable query timeout"
onChange={handleOnQueryTimeoutToggleChange}
defaultChecked={queryTimeoutEnabled}
/>
</div>
{queryTimeoutEnabled && (
<div>
<SpinButton
label="Query timeout (ms)"
labelPosition={Position.top}
defaultValue={(queryTimeout || 5000).toString()}
min={100}
step={1000}
onChange={handleOnQueryTimeoutSpinButtonChange}
incrementButtonAriaLabel="Increase value by 1000"
decrementButtonAriaLabel="Decrease value by 1000"
styles={queryTimeoutSpinButtonStyles}
/>
<Toggle
styles={toggleStyles}
label="Enable RU threshold"
onChange={handleOnRUThresholdToggleChange}
defaultChecked={ruThresholdEnabled}
label="Automatically cancel query after timeout"
styles={queryTimeoutToggleStyles}
onChange={handleOnAutomaticallyCancelQueryToggleChange}
defaultChecked={automaticallyCancelQueryAfterTimeout}
/>
</div>
{ruThresholdEnabled && (
<div>
<SpinButton
label="RU Threshold (RU)"
labelPosition={Position.top}
defaultValue={(ruThreshold || DefaultRUThreshold).toString()}
min={1}
step={1000}
onChange={handleOnRUThresholdSpinButtonChange}
incrementButtonAriaLabel="Increase value by 1000"
decrementButtonAriaLabel="Decrease value by 1000"
styles={spinButtonStyles}
/>
</div>
)}
</div>
)}
</div>
<div className="settingsSection">
<div className="settingsSectionPart">
<div>
<legend id="queryTimeoutLabel" className="settingsSectionLabel legendLabel">
Query Timeout
</legend>
<InfoTooltip>
When a query reaches a specified time limit, a popup with an option to cancel the query will show
unless automatic cancellation has been enabled
</InfoTooltip>
</div>
<div>
<Toggle
styles={toggleStyles}
label="Enable query timeout"
onChange={handleOnQueryTimeoutToggleChange}
defaultChecked={queryTimeoutEnabled}
/>
</div>
{queryTimeoutEnabled && (
<div>
<SpinButton
label="Query timeout (ms)"
labelPosition={Position.top}
defaultValue={(queryTimeout || 5000).toString()}
min={100}
step={1000}
onChange={handleOnQueryTimeoutSpinButtonChange}
incrementButtonAriaLabel="Increase value by 1000"
decrementButtonAriaLabel="Decrease value by 1000"
styles={spinButtonStyles}
/>
<Toggle
label="Automatically cancel query after timeout"
styles={toggleStyles}
onChange={handleOnAutomaticallyCancelQueryToggleChange}
defaultChecked={automaticallyCancelQueryAfterTimeout}
/>
</div>
)}
</div>
</div>
</>
</div>
)}
<div className="settingsSection">
<div className="settingsSectionPart">
@@ -463,7 +404,7 @@ export const SettingsPane: FunctionComponent<{ explorer: Explorer }> = ({
onIncrement={(newValue) => setRetryAttempts(parseInt(newValue) + 1 || retryAttempts)}
onDecrement={(newValue) => setRetryAttempts(parseInt(newValue) - 1 || retryAttempts)}
onValidate={(newValue) => setRetryAttempts(parseInt(newValue) || retryAttempts)}
styles={spinButtonStyles}
styles={queryTimeoutSpinButtonStyles}
/>
<div>
<legend id="queryRetryAttemptsLabel" className="settingsSectionLabel legendLabel">
@@ -485,7 +426,7 @@ export const SettingsPane: FunctionComponent<{ explorer: Explorer }> = ({
onIncrement={(newValue) => setRetryInterval(parseInt(newValue) + 1000 || retryInterval)}
onDecrement={(newValue) => setRetryInterval(parseInt(newValue) - 1000 || retryInterval)}
onValidate={(newValue) => setRetryInterval(parseInt(newValue) || retryInterval)}
styles={spinButtonStyles}
styles={queryTimeoutSpinButtonStyles}
/>
<div>
<legend id="queryRetryAttemptsLabel" className="settingsSectionLabel legendLabel">
@@ -507,7 +448,7 @@ export const SettingsPane: FunctionComponent<{ explorer: Explorer }> = ({
onIncrement={(newValue) => setMaxWaitTimeInSeconds(parseInt(newValue) + 1 || MaxWaitTimeInSeconds)}
onDecrement={(newValue) => setMaxWaitTimeInSeconds(parseInt(newValue) - 1 || MaxWaitTimeInSeconds)}
onValidate={(newValue) => setMaxWaitTimeInSeconds(parseInt(newValue) || MaxWaitTimeInSeconds)}
styles={spinButtonStyles}
styles={queryTimeoutSpinButtonStyles}
/>
</div>
</div>

View File

@@ -97,74 +97,6 @@ exports[`Settings Pane should render Default properly 1`] = `
</div>
</div>
</div>
<div
className="settingsSection"
>
<div
className="settingsSectionPart"
>
<div>
<legend
className="settingsSectionLabel legendLabel"
id="ruThresholdLabel"
>
RU Threshold
</legend>
<InfoTooltip>
If a query exceeds a configured RU threshold, the query will be aborted.
</InfoTooltip>
</div>
<div>
<StyledToggleBase
defaultChecked={true}
label="Enable RU threshold"
onChange={[Function]}
styles={
Object {
"container": Object {},
"label": Object {
"display": "block",
"fontSize": 12,
"fontWeight": 400,
},
"pill": Object {},
"root": Object {},
"text": Object {},
"thumb": Object {},
}
}
/>
</div>
<div>
<StyledSpinButton
decrementButtonAriaLabel="Decrease value by 1000"
defaultValue="5000"
incrementButtonAriaLabel="Increase value by 1000"
label="RU Threshold (RU)"
labelPosition={0}
min={1}
onChange={[Function]}
step={1000}
styles={
Object {
"arrowButtonsContainer": Object {},
"icon": Object {},
"input": Object {},
"label": Object {
"fontSize": 12,
"fontWeight": 400,
},
"labelWrapper": Object {},
"root": Object {
"paddingBottom": 10,
},
"spinButtonWrapper": Object {},
}
}
/>
</div>
</div>
</div>
<div
className="settingsSection"
>

View File

@@ -1,6 +1,5 @@
import { IDropdownOption, Image, Label, Stack, Text, TextField } from "@fluentui/react";
import { useBoolean } from "@fluentui/react-hooks";
import { logConsoleError } from "Utils/NotificationConsoleUtils";
import React, { FunctionComponent, useEffect, useState } from "react";
import * as _ from "underscore";
import AddPropertyIcon from "../../../../images/Add-property.svg";
@@ -98,19 +97,9 @@ export const AddTableEntityPanel: FunctionComponent<AddTableEntityPanelProps> =
/* Add new entity attribute */
const onSubmit = async (): Promise<void> => {
for (let i = 0; i < entities.length; i++) {
const { property, type, value } = entities[i];
if ((property === "PartitionKey" && value === "") || (property === "RowKey" && value === "")) {
logConsoleError(`${property} cannot be empty. Please input a value for ${property}`);
setFormError(`${property} cannot be empty. Please input a value for ${property}`);
return;
}
if (
(property === "PartitionKey" && containsAnyWhiteSpace(value) === true) ||
(property === "RowKey" && containsAnyWhiteSpace(value) === true)
) {
logConsoleError(`${property} cannot have whitespace. Please input a value for ${property} without whitespace`);
setFormError(`${property} cannot have whitespace. Please input a value for ${property} without whitespace`);
const { property, type } = entities[i];
if (property === "" || property === undefined) {
setFormError(`Property name cannot be empty. Please enter a property name`);
return;
}
@@ -118,8 +107,6 @@ export const AddTableEntityPanel: FunctionComponent<AddTableEntityPanelProps> =
setFormError(`Property type cannot be empty. Please select a type from the dropdown for property ${property}`);
return;
}
setFormError("");
}
setIsExecuting(true);
@@ -140,13 +127,6 @@ export const AddTableEntityPanel: FunctionComponent<AddTableEntityPanelProps> =
}
};
const containsAnyWhiteSpace = (entityValue: string) => {
if (/\s/.test(entityValue)) {
return true;
}
return false;
};
const tryInsertNewHeaders = (viewModel: TableEntityListViewModel, newEntity: Entities.ITableEntity): boolean => {
let newHeaders: string[] = [];
const keys = Object.keys(newEntity);
@@ -202,14 +182,9 @@ export const AddTableEntityPanel: FunctionComponent<AddTableEntityPanelProps> =
const entityChange = (value: string | Date, indexOfInput: number, key: string): void => {
const cloneEntities: EntityRowType[] = [...entities];
if (key === "property") {
cloneEntities[indexOfInput].property = value.toString().trim();
cloneEntities[indexOfInput].property = value.toString();
} else if (key === "time") {
cloneEntities[indexOfInput].entityTimeValue = value.toString();
} else if (
cloneEntities[indexOfInput].property === "PartitionKey" ||
cloneEntities[indexOfInput].property === "RowKey"
) {
cloneEntities[indexOfInput].value = value.toString().trim();
} else {
cloneEntities[indexOfInput].value = value.toString();
}

View File

@@ -1,6 +1,5 @@
import { IDropdownOption, Image, Label, Stack, Text, TextField } from "@fluentui/react";
import { useBoolean } from "@fluentui/react-hooks";
import { logConsoleError } from "Utils/NotificationConsoleUtils";
import React, { FunctionComponent, useEffect, useState } from "react";
import * as _ from "underscore";
import AddPropertyIcon from "../../../../images/Add-property.svg";
@@ -191,7 +190,7 @@ export const EditTableEntityPanel: FunctionComponent<EditTableEntityPanelProps>
const onSubmit = async (): Promise<void> => {
for (let i = 0; i < entities.length; i++) {
const { property, type, value } = entities[i];
const { property, type } = entities[i];
if (property === "" || property === undefined) {
setFormError(`Property name cannot be empty. Please enter a property name`);
return;
@@ -201,17 +200,6 @@ export const EditTableEntityPanel: FunctionComponent<EditTableEntityPanelProps>
setFormError(`Property type cannot be empty. Please select a type from the dropdown for property ${property}`);
return;
}
if (
(property === "PartitionKey" && value === "") ||
(property === "PartitionKey" && value === undefined) ||
(property === "RowKey" && value === "") ||
(property === "RowKey" && value === undefined)
) {
logConsoleError(`${property} cannot be empty. Please input a value for ${property}`);
setFormError(`${property} cannot be empty. Please input a value for ${property}`);
return;
}
}
setIsExecuting(true);
@@ -371,7 +359,7 @@ export const EditTableEntityPanel: FunctionComponent<EditTableEntityPanelProps>
selectedKey={entity.type}
entityPropertyPlaceHolder={detailedHelp}
entityValuePlaceholder={entity.entityValuePlaceholder}
entityValue={entity.value.toString()}
entityValue={entity.value?.toString()}
isEntityTypeDate={entity.isEntityTypeDate}
entityTimeValue={entity.entityTimeValue}
isEntityValueDisable={entity.isEntityValueDisable}

View File

@@ -50,9 +50,7 @@ export const WelcomeModal = ({ visible }: { visible: boolean }): JSX.Element =>
</Stack>
<Stack horizontalAlign="center">
<Stack.Item align="center" style={{ textAlign: "center" }}>
<Text className="title bold" as={"h1"}>
Welcome to Microsoft Copilot for Azure in Cosmos DB (preview)
</Text>
<Text className="title bold">Welcome to Microsoft Copilot for Azure in Cosmos DB</Text>
</Stack.Item>
<Stack.Item align="center" className="text">
<Stack horizontal>

View File

@@ -67,10 +67,9 @@ exports[`Query Copilot Welcome Modal snapshot test should render when isOpen is
}
>
<Text
as="h1"
className="title bold"
>
Welcome to Microsoft Copilot for Azure in Cosmos DB (preview)
Welcome to Microsoft Copilot for Azure in Cosmos DB
</Text>
</StackItem>
<StackItem

View File

@@ -30,7 +30,6 @@ const CopilotProvider = ({ children }: { children: React.ReactNode }): JSX.Eleme
queryResults: undefined,
errorMessage: "",
isSamplePromptsOpen: false,
showPromptTeachingBubble: true,
showDeletePopup: false,
showFeedbackBar: false,
showCopyPopup: false,
@@ -66,7 +65,6 @@ const CopilotProvider = ({ children }: { children: React.ReactNode }): JSX.Eleme
setQueryResults: (queryResults: QueryResults | undefined) => set({ queryResults }),
setErrorMessage: (errorMessage: string) => set({ errorMessage }),
setIsSamplePromptsOpen: (isSamplePromptsOpen: boolean) => set({ isSamplePromptsOpen }),
setShowPromptTeachingBubble: (showPromptTeachingBubble: boolean) => set({ showPromptTeachingBubble }),
setShowDeletePopup: (showDeletePopup: boolean) => set({ showDeletePopup }),
setShowFeedbackBar: (showFeedbackBar: boolean) => set({ showFeedbackBar }),
setshowCopyPopup: (showCopyPopup: boolean) => set({ showCopyPopup }),
@@ -105,7 +103,6 @@ const CopilotProvider = ({ children }: { children: React.ReactNode }): JSX.Eleme
queryResults: undefined,
errorMessage: "",
isSamplePromptsOpen: false,
showPromptTeachingBubble: true,
showDeletePopup: false,
showFeedbackBar: false,
showCopyPopup: false,

View File

@@ -18,6 +18,7 @@ import {
Text,
TextField,
} from "@fluentui/react";
import { useBoolean } from "@fluentui/react-hooks";
import { HttpStatusCodes } from "Common/Constants";
import { handleError } from "Common/ErrorHandlingUtils";
import { createUri } from "Common/UrlUtility";
@@ -70,7 +71,7 @@ export const QueryCopilotPromptbar: React.FC<QueryCopilotPromptProps> = ({
databaseId,
containerId,
}: QueryCopilotPromptProps): JSX.Element => {
const [copilotTeachingBubbleVisible, setCopilotTeachingBubbleVisible] = useState<boolean>(false);
const [copilotTeachingBubbleVisible, { toggle: toggleCopilotTeachingBubbleVisible }] = useBoolean(false);
const inputEdited = useRef(false);
const {
openFeedbackModal,
@@ -93,8 +94,6 @@ export const QueryCopilotPromptbar: React.FC<QueryCopilotPromptProps> = ({
setIsSamplePromptsOpen,
showSamplePrompts,
setShowSamplePrompts,
showPromptTeachingBubble,
setShowPromptTeachingBubble,
showDeletePopup,
setShowDeletePopup,
showFeedbackBar,
@@ -273,23 +272,16 @@ export const QueryCopilotPromptbar: React.FC<QueryCopilotPromptProps> = ({
};
const showTeachingBubble = (): void => {
if (showPromptTeachingBubble && !inputEdited.current) {
if (!inputEdited.current) {
setTimeout(() => {
if (!inputEdited.current && !isWelcomModalVisible()) {
setCopilotTeachingBubbleVisible(true);
toggleCopilotTeachingBubbleVisible();
inputEdited.current = true;
}
}, 30000);
} else {
toggleCopilotTeachingBubbleVisible(false);
}
};
const toggleCopilotTeachingBubbleVisible = (visible: boolean): void => {
setCopilotTeachingBubbleVisible(visible);
setShowPromptTeachingBubble(visible);
};
const isWelcomModalVisible = (): boolean => {
return localStorage.getItem("hideWelcomeModal") !== "true";
};
@@ -348,7 +340,6 @@ export const QueryCopilotPromptbar: React.FC<QueryCopilotPromptProps> = ({
},
}}
ariaLabel="Close"
title="Close copilot"
/>
</Stack>
<Stack horizontal verticalAlign="center">
@@ -373,13 +364,13 @@ export const QueryCopilotPromptbar: React.FC<QueryCopilotPromptProps> = ({
placeholder="Ask a question in natural language and well generate the query for you."
aria-labelledby="copilot-textfield-label"
/>
{showPromptTeachingBubble && copilotTeachingBubbleVisible && (
{copilotTeachingBubbleVisible && (
<TeachingBubble
calloutProps={{ directionalHint: DirectionalHint.bottomCenter }}
target="#naturalLanguageInput"
hasCloseButton={true}
closeButtonAriaLabel="Close"
onDismiss={() => toggleCopilotTeachingBubbleVisible(false)}
onDismiss={toggleCopilotTeachingBubbleVisible}
hasSmallHeadline={true}
headline="Write a prompt"
>
@@ -387,7 +378,7 @@ export const QueryCopilotPromptbar: React.FC<QueryCopilotPromptProps> = ({
<Link
onClick={() => {
setShowSamplePrompts(true);
toggleCopilotTeachingBubbleVisible(false);
toggleCopilotTeachingBubbleVisible();
}}
style={{ color: "white", fontWeight: 600 }}
>
@@ -543,7 +534,6 @@ export const QueryCopilotPromptbar: React.FC<QueryCopilotPromptProps> = ({
<Text style={{ fontWeight: 600, fontSize: 12 }}>Provide feedback on the query generated</Text>
{showCallout && !hideFeedbackModalForLikedQueries && (
<Callout
role="status"
style={{ padding: 8 }}
target="#likeBtn"
onDismiss={() => {
@@ -579,18 +569,11 @@ export const QueryCopilotPromptbar: React.FC<QueryCopilotPromptProps> = ({
<IconButton
id="likeBtn"
style={{ marginLeft: 20 }}
aria-label="Like"
role="toggle"
iconProps={{ iconName: likeQuery === true ? "LikeSolid" : "Like" }}
aria-label="Like"
onClick={() => {
setShowCallout(!likeQuery);
setLikeQuery(!likeQuery);
if (likeQuery === true) {
document.getElementById("likeStatus").innerHTML = "Unpressed";
}
if (likeQuery === false) {
document.getElementById("likeStatus").innerHTML = "Liked";
}
if (dislikeQuery) {
setDislikeQuery(!dislikeQuery);
}
@@ -598,24 +581,17 @@ export const QueryCopilotPromptbar: React.FC<QueryCopilotPromptProps> = ({
/>
<IconButton
style={{ margin: "0 10px" }}
role="toggle"
aria-label="Dislike"
iconProps={{ iconName: dislikeQuery === true ? "DislikeSolid" : "Dislike" }}
onClick={() => {
let toggleStatusValue = "Unpressed";
if (!dislikeQuery) {
openFeedbackModal(generatedQuery, false, userPrompt);
setLikeQuery(false);
toggleStatusValue = "Disliked";
}
setDislikeQuery(!dislikeQuery);
setShowCallout(false);
document.getElementById("likeStatus").innerHTML = toggleStatusValue;
}}
aria-label="Dislike"
/>
<span role="status" style={{ position: "absolute", left: "-9999px" }} id="likeStatus"></span>
<Separator vertical style={{ color: "#EDEBE9" }} />
<CommandBarButton
onClick={copyGeneratedCode}

View File

@@ -10,7 +10,7 @@ import { OnExecuteQueryClick } from "Explorer/QueryCopilot/Shared/QueryCopilotCl
import { QueryCopilotProps } from "Explorer/QueryCopilot/Shared/QueryCopilotInterfaces";
import { QueryCopilotResults } from "Explorer/QueryCopilot/Shared/QueryCopilotResults";
import { userContext } from "UserContext";
import { QueryCopilotState, useQueryCopilot } from "hooks/useQueryCopilot";
import { useQueryCopilot } from "hooks/useQueryCopilot";
import { useSidePanel } from "hooks/useSidePanel";
import { ReactTabKind, TabsState, useTabs } from "hooks/useTabs";
import React, { useState } from "react";
@@ -37,7 +37,7 @@ export const QueryCopilotTab: React.FC<QueryCopilotProps> = ({ explorer }: Query
const executeQueryBtn = {
iconSrc: ExecuteQueryIcon,
iconAlt: executeQueryBtnLabel,
onCommandClick: () => OnExecuteQueryClick(useQueryCopilot as Partial<QueryCopilotState>),
onCommandClick: () => OnExecuteQueryClick(useQueryCopilot),
commandButtonLabel: executeQueryBtnLabel,
ariaLabel: executeQueryBtnLabel,
hasPopup: false,

View File

@@ -1,6 +1,6 @@
import { QueryDocumentsPerPage } from "Explorer/QueryCopilot/Shared/QueryCopilotClient";
import { QueryResultSection } from "Explorer/Tabs/QueryTab/QueryResultSection";
import { QueryCopilotState, useQueryCopilot } from "hooks/useQueryCopilot";
import { useQueryCopilot } from "hooks/useQueryCopilot";
import React from "react";
export const QueryCopilotResults: React.FC = (): JSX.Element => {
@@ -12,11 +12,7 @@ export const QueryCopilotResults: React.FC = (): JSX.Element => {
queryResults={useQueryCopilot.getState().queryResults}
isExecuting={useQueryCopilot.getState().isExecuting}
executeQueryDocumentsPage={(firstItemIndex: number) =>
QueryDocumentsPerPage(
firstItemIndex,
useQueryCopilot.getState().queryIterator,
useQueryCopilot as Partial<QueryCopilotState>,
)
QueryDocumentsPerPage(firstItemIndex, useQueryCopilot.getState().queryIterator, useQueryCopilot)
}
/>
);

View File

@@ -24,9 +24,7 @@ export const QuickstartCarousel: React.FC<QuickstartCarouselProps> = ({
>
<Stack>
<Stack horizontal horizontalAlign="space-between" style={{ padding: 16 }}>
<Text role="heading" aria-level={1} variant="xLarge">
{getHeaderText(page)}
</Text>
<Text variant="xLarge">{getHeaderText(page)}</Text>
<IconButton iconProps={{ iconName: "Cancel" }} onClick={() => setPage(4)} ariaLabel="Close" />
</Stack>
{getContent(page)}

View File

@@ -148,7 +148,7 @@ export class SplashScreen extends React.Component<SplashScreenProps> {
/>
</Stack>
<Stack horizontal tokens={{ childrenGap: 16 }}>
{useQueryCopilot.getState().copilotEnabled && (
{useQueryCopilot.getState().copilotEnabled && useQueryCopilot.getState().copilotSampleDBEnabled && (
<SplashScreenButton
imgSrc={CopilotIcon}
title={"Query faster with Copilot"}

View File

@@ -881,11 +881,6 @@ export default class DocumentsTab extends TabsBase {
}
protected getTabsButtons(): CommandButtonComponentProps[] {
if (!userContext.hasWriteAccess) {
// All the following buttons require write access
return [];
}
const buttons: CommandButtonComponentProps[] = [];
const label = !this.isPreferredApiMongoDB ? "New Item" : "New Document";
if (this.newDocumentButton.visible()) {

View File

@@ -3,13 +3,13 @@ import {
DetailsListLayoutMode,
IColumn,
Icon,
IconButton,
Link,
Pivot,
PivotItem,
SelectionMode,
Stack,
Text,
IconButton,
TooltipHost,
} from "@fluentui/react";
import { HttpHeaders, NormalizedEventKey } from "Common/Constants";
@@ -18,15 +18,15 @@ import { QueryMetrics } from "Contracts/DataModels";
import { EditorReact } from "Explorer/Controls/Editor/EditorReact";
import { IDocument } from "Explorer/Tabs/QueryTab/QueryTabComponent";
import { userContext } from "UserContext";
import copy from "clipboard-copy";
import { useNotificationConsole } from "hooks/useNotificationConsole";
import React from "react";
import CopilotCopy from "../../../../images/CopilotCopy.svg";
import DownloadQueryMetrics from "../../../../images/DownloadQuery.svg";
import QueryEditorNext from "../../../../images/Query-Editor-Next.svg";
import RunQuery from "../../../../images/RunQuery.png";
import InfoColor from "../../../../images/info_color.svg";
import { QueryResults } from "../../../Contracts/ViewModels";
import copy from "clipboard-copy";
import CopilotCopy from "../../../../images/CopilotCopy.svg";
interface QueryResultProps {
isMongoDB: boolean;
@@ -62,12 +62,9 @@ export const QueryResultSection: React.FC<QueryResultProps> = ({
const columns: IColumn[] = [
{
key: "column1",
name: "Description",
iconName: "Info",
isIconOnly: true,
name: "",
minWidth: 10,
maxWidth: 12,
iconClassName: "iconheadercell",
data: String,
fieldName: "",
onRender: (item: IDocument) => {

View File

@@ -91,6 +91,9 @@
div[role="tabpanel"] {
height: 100%;
div:nth-child(1) {
height: 100%;
}
}
.result-metadata {
@@ -280,6 +283,3 @@
}
}
}
.iconheadercell {
font-size: 12px;
}

View File

@@ -1,7 +1,6 @@
/* eslint-disable @typescript-eslint/no-explicit-any */
/* eslint-disable no-console */
import { FeedOptions, QueryOperationOptions } from "@azure/cosmos";
import { Platform, configContext } from "ConfigContext";
import { FeedOptions } from "@azure/cosmos";
import { useDialog } from "Explorer/Controls/Dialog";
import { QueryCopilotFeedbackModal } from "Explorer/QueryCopilot/Modal/QueryCopilotFeedbackModal";
import { useCopilotStore } from "Explorer/QueryCopilot/QueryCopilotContext";
@@ -11,7 +10,7 @@ import { QueryCopilotSidebar } from "Explorer/QueryCopilot/V2/Sidebar/QueryCopil
import { QueryResultSection } from "Explorer/Tabs/QueryTab/QueryResultSection";
import { useSelectedNode } from "Explorer/useSelectedNode";
import { QueryConstants } from "Shared/Constants";
import { LocalStorageUtility, StorageKey, getRUThreshold, ruThresholdEnabled } from "Shared/StorageUtility";
import { LocalStorageUtility, StorageKey } from "Shared/StorageUtility";
import { Action } from "Shared/Telemetry/TelemetryConstants";
import { QueryCopilotState, useQueryCopilot } from "hooks/useQueryCopilot";
import { TabsState, useTabs } from "hooks/useTabs";
@@ -304,20 +303,8 @@ export default class QueryTabComponent extends React.Component<IQueryTabComponen
isExecutionError: false,
});
let queryOperationOptions: QueryOperationOptions;
if (userContext.apiType === "SQL" && ruThresholdEnabled()) {
const ruThreshold: number = getRUThreshold();
queryOperationOptions = {
ruCapPerOperation: ruThreshold,
} as QueryOperationOptions;
}
const queryDocuments = async (firstItemIndex: number) =>
await queryDocumentsPage(
this.props.collection && this.props.collection.id(),
this._iterator,
firstItemIndex,
queryOperationOptions,
);
await queryDocumentsPage(this.props.collection && this.props.collection.id(), this._iterator, firstItemIndex);
this.props.tabsBaseInstance.isExecuting(true);
this.setState({
isExecuting: true,
@@ -403,7 +390,7 @@ export default class QueryTabComponent extends React.Component<IQueryTabComponen
});
}
if (this.saveQueryButton.visible && configContext.platform !== Platform.Fabric) {
if (this.saveQueryButton.visible) {
const label = "Save Query";
buttons.push({
iconSrc: SaveQueryIcon,

View File

@@ -10,7 +10,6 @@ import { PostgresConnectTab } from "Explorer/Tabs/PostgresConnectTab";
import { QuickstartTab } from "Explorer/Tabs/QuickstartTab";
import { VcoreMongoConnectTab } from "Explorer/Tabs/VCoreMongoConnectTab";
import { VcoreMongoQuickstartTab } from "Explorer/Tabs/VCoreMongoQuickstartTab";
import { hasRUThresholdBeenConfigured } from "Shared/StorageUtility";
import { userContext } from "UserContext";
import { useTeachingBubble } from "hooks/useTeachingBubble";
import ko from "knockout";
@@ -30,9 +29,7 @@ interface TabsProps {
export const Tabs = ({ explorer }: TabsProps): JSX.Element => {
const { openedTabs, openedReactTabs, activeTab, activeReactTab, networkSettingsWarning } = useTabs();
const [showRUThresholdMessageBar, setShowRUThresholdMessageBar] = useState<boolean>(
userContext.apiType === "SQL" && !hasRUThresholdBeenConfigured(),
);
return (
<div className="tabsManagerContainer">
{networkSettingsWarning && (
@@ -57,23 +54,6 @@ export const Tabs = ({ explorer }: TabsProps): JSX.Element => {
{networkSettingsWarning}
</MessageBar>
)}
{showRUThresholdMessageBar && (
<MessageBar
messageBarType={MessageBarType.info}
onDismiss={() => {
setShowRUThresholdMessageBar(false);
}}
styles={{
innerText: {
fontWeight: "bold",
},
}}
>
{
"To prevent queries from using excessive RUs, Data Explorer has a 5,000 RU default limit. To modify or remove the limit, go to the Settings cog on the right and find 'RU Threshold'."
}
</MessageBar>
)}
<div id="content" className="flexContainer hideOverflows">
<div className="nav-tabs-margin">
<ul className="nav nav-tabs level navTabHeight" id="navTabs" role="tablist">

View File

@@ -4,9 +4,9 @@ import React, { Component } from "react";
import DiscardIcon from "../../../images/discard.svg";
import SaveIcon from "../../../images/save-cosmos.svg";
import * as Constants from "../../Common/Constants";
import { getErrorMessage, getErrorStack } from "../../Common/ErrorHandlingUtils";
import { createTrigger } from "../../Common/dataAccess/createTrigger";
import { updateTrigger } from "../../Common/dataAccess/updateTrigger";
import { getErrorMessage, getErrorStack } from "../../Common/ErrorHandlingUtils";
import * as ViewModels from "../../Contracts/ViewModels";
import { Action } from "../../Shared/Telemetry/TelemetryConstants";
import * as TelemetryProcessor from "../../Shared/Telemetry/TelemetryProcessor";

View File

@@ -68,9 +68,7 @@ export const useDatabases: UseStore<DatabasesState> = create((set, get) => ({
return true;
},
findDatabaseWithId: (databaseId: string, isSampleDatabase?: boolean) => {
return isSampleDatabase === undefined
? get().databases.find((db) => databaseId === db.id())
: get().databases.find((db) => databaseId === db.id() && db.isSampleDB === isSampleDatabase);
return get().databases.find((db) => databaseId === db.id() && db.isSampleDB === isSampleDatabase);
},
isLastNonEmptyDatabase: () => {
const databases = get().databases;

View File

@@ -1,6 +1,6 @@
import { allowedJunoOrigins, validateEndpoint } from "Utils/EndpointUtils";
import { GetGithubClientId } from "Utils/GitHubUtils";
import ko from "knockout";
import { allowedJunoOrigins, validateEndpoint } from "Utils/EndpointValidation";
import { GetGithubClientId } from "Utils/GitHubUtils";
import { HttpHeaders, HttpStatusCodes } from "../Common/Constants";
import { configContext } from "../ConfigContext";
import * as DataModels from "../Contracts/DataModels";

View File

@@ -2,7 +2,7 @@ import { configContext } from "ConfigContext";
import { useDialog } from "Explorer/Controls/Dialog";
import { Action } from "Shared/Telemetry/TelemetryConstants";
import { userContext } from "UserContext";
import { allowedJunoOrigins, validateEndpoint } from "Utils/EndpointUtils";
import { allowedJunoOrigins, validateEndpoint } from "Utils/EndpointValidation";
import { useQueryCopilot } from "hooks/useQueryCopilot";
import promiseRetry, { AbortError } from "p-retry";
import {

View File

@@ -31,7 +31,6 @@ const requestDatabaseResourceTokens = async (): Promise<void> => {
updateUserContext({
fabricContext: { ...userContext.fabricContext, databaseConnectionInfo: fabricDatabaseConnectionInfo },
databaseAccount: { ...userContext.databaseAccount },
hasWriteAccess: false, // TODO: receive from fabricDatabaseConnectionInfo
});
scheduleRefreshDatabaseResourceToken();
} catch (error) {

View File

@@ -172,6 +172,7 @@ export class CollectionCreation {
public static readonly DefaultCollectionRUs100K: number = 100000;
public static readonly DefaultCollectionRUs1Million: number = 1000000;
public static readonly DefaultAddCollectionDefaultFlight: string = "0";
public static readonly DefaultSubscriptionType: SubscriptionType = SubscriptionType.Free;
public static readonly TablesAPIDefaultDatabase: string = "TablesDB";

View File

@@ -1,12 +1,9 @@
import * as LocalStorageUtility from "./LocalStorageUtility";
import * as SessionStorageUtility from "./SessionStorageUtility";
import * as StringUtility from "./StringUtility";
export { LocalStorageUtility, SessionStorageUtility };
export enum StorageKey {
ActualItemPerPage,
RUThresholdEnabled,
RUThreshold,
QueryTimeoutEnabled,
QueryTimeout,
RetryAttempts,
@@ -28,27 +25,3 @@ export enum StorageKey {
VisitedAccounts,
PriorityLevel,
}
export const hasRUThresholdBeenConfigured = (): boolean => {
const ruThresholdEnabledLocalStorageRaw: string | null = LocalStorageUtility.getEntryString(
StorageKey.RUThresholdEnabled,
);
return ruThresholdEnabledLocalStorageRaw === "true" || ruThresholdEnabledLocalStorageRaw === "false";
};
export const ruThresholdEnabled = (): boolean => {
const ruThresholdEnabledLocalStorageRaw: string | null = LocalStorageUtility.getEntryString(
StorageKey.RUThresholdEnabled,
);
return ruThresholdEnabledLocalStorageRaw === null || StringUtility.toBoolean(ruThresholdEnabledLocalStorageRaw);
};
export const getRUThreshold = (): number => {
const ruThresholdRaw = LocalStorageUtility.getEntryNumber(StorageKey.RUThreshold);
if (ruThresholdRaw !== 0) {
return ruThresholdRaw;
}
return DefaultRUThreshold;
};
export const DefaultRUThreshold = 5000;

View File

@@ -72,6 +72,7 @@ interface UserContext {
readonly isTryCosmosDBSubscription?: boolean;
readonly portalEnv?: PortalEnv;
readonly features: Features;
readonly addCollectionFlight: string;
readonly hasWriteAccess: boolean;
readonly parsedResourceToken?: {
databaseId: string;
@@ -98,6 +99,7 @@ const userContext: UserContext = {
isTryCosmosDBSubscription: false,
portalEnv: "prod",
features,
addCollectionFlight: CollectionCreation.DefaultAddCollectionDefaultFlight,
subscriptionType: CollectionCreation.DefaultSubscriptionType,
collectionCreationDefaults: CollectionCreationDefaults,
};

View File

@@ -67,23 +67,7 @@ export const PortalBackendIPs: { [key: string]: string[] } = {
//usnat: ["7.28.202.68"],
};
export class MongoProxyEndpoints {
public static readonly Development: string = "https://localhost:7238";
public static readonly MPAC: string = "https://cdb-ms-mpac-mp.cosmos.azure.com";
public static readonly Prod: string = "https://cdb-ms-prod-mp.cosmos.azure.com";
public static readonly Fairfax: string = "https://cdb-ff-prod-mp.cosmos.azure.us";
public static readonly Mooncake: string = "https://cdb-mc-prod-mp.cosmos.azure.cn";
}
export const allowedMongoProxyEndpoints: ReadonlyArray<string> = [
MongoProxyEndpoints.Development,
MongoProxyEndpoints.MPAC,
MongoProxyEndpoints.Prod,
MongoProxyEndpoints.Fairfax,
MongoProxyEndpoints.Mooncake,
];
export const allowedMongoProxyEndpoints_ToBeDeprecated: ReadonlyArray<string> = [
"https://main.documentdb.ext.azure.com",
"https://main.documentdb.ext.azure.cn",
"https://main.documentdb.ext.azure.us",

View File

@@ -1,7 +1,7 @@
import { resetConfigContext, updateConfigContext } from "ConfigContext";
import { DatabaseAccount, IpRule } from "Contracts/DataModels";
import { updateUserContext } from "UserContext";
import { PortalBackendIPs } from "Utils/EndpointUtils";
import { PortalBackendIPs } from "Utils/EndpointValidation";
import { getNetworkSettingsWarningMessage } from "./NetworkUtility";
describe("NetworkUtility tests", () => {

View File

@@ -1,7 +1,7 @@
import { configContext } from "ConfigContext";
import { checkFirewallRules } from "Explorer/Tabs/Shared/CheckFirewallRules";
import { userContext } from "UserContext";
import { PortalBackendIPs } from "Utils/EndpointUtils";
import { PortalBackendIPs } from "Utils/EndpointValidation";
export const getNetworkSettingsWarningMessage = async (
setStateFunc: (warningMessage: string) => void,

View File

@@ -33,6 +33,7 @@ import {
getDatabaseAccountPropertiesFromMetadata,
} from "../Platform/Hosted/HostedUtils";
import { extractFeatures } from "../Platform/Hosted/extractFeatures";
import { CollectionCreation } from "../Shared/Constants";
import { DefaultExperienceUtility } from "../Shared/DefaultExperienceUtility";
import { Node, PortalEnv, updateUserContext, userContext } from "../UserContext";
import { getAuthorizationHeader, getMsalInstance } from "../Utils/AuthorizationUtils";
@@ -478,7 +479,6 @@ function updateContextsFromPortalMessage(inputs: DataExplorerInputsFrame) {
updateConfigContext({
BACKEND_ENDPOINT: inputs.extensionEndpoint || configContext.BACKEND_ENDPOINT,
ARM_ENDPOINT: normalizeArmEndpoint(inputs.csmEndpoint || configContext.ARM_ENDPOINT),
MONGO_PROXY_ENDPOINT: inputs.mongoProxyEndpoint,
});
updateUserContext({
@@ -491,6 +491,7 @@ function updateContextsFromPortalMessage(inputs: DataExplorerInputsFrame) {
quotaId: inputs.quotaId,
portalEnv: inputs.serverId as PortalEnv,
hasWriteAccess: inputs.hasWriteAccess ?? true,
addCollectionFlight: inputs.addCollectionDefaultFlight || CollectionCreation.DefaultAddCollectionDefaultFlight,
collectionCreationDefaults: inputs.defaultCollectionThroughput,
isTryCosmosDBSubscription: inputs.isTryCosmosDBSubscription,
});

View File

@@ -29,7 +29,6 @@ export interface QueryCopilotState {
queryResults: QueryResults | undefined;
errorMessage: string;
isSamplePromptsOpen: boolean;
showPromptTeachingBubble: boolean;
showDeletePopup: boolean;
showFeedbackBar: boolean;
showCopyPopup: boolean;
@@ -72,7 +71,6 @@ export interface QueryCopilotState {
setQueryResults: (queryResults: QueryResults | undefined) => void;
setErrorMessage: (errorMessage: string) => void;
setIsSamplePromptsOpen: (isSamplePromptsOpen: boolean) => void;
setShowPromptTeachingBubble: (showPromptTeachingBubble: boolean) => void;
setShowDeletePopup: (showDeletePopup: boolean) => void;
setShowFeedbackBar: (showFeedbackBar: boolean) => void;
setshowCopyPopup: (showCopyPopup: boolean) => void;
@@ -95,7 +93,7 @@ export interface QueryCopilotState {
resetQueryCopilotStates: () => void;
}
type QueryCopilotStore = UseStore<Partial<QueryCopilotState>>;
type QueryCopilotStore = UseStore<QueryCopilotState>;
export const useQueryCopilot: QueryCopilotStore = create((set) => ({
copilotEnabled: false,

View File

@@ -54,6 +54,7 @@ const initTestExplorer = async (): Promise<void> => {
extensionEndpoint: "/proxy",
subscriptionType: 3,
quotaId: "Internal_2014-09-01",
addCollectionDefaultFlight: "2",
isTryCosmosDBSubscription: false,
masterKey: keys.primaryMasterKey,
loadDatabaseAccountTimestamp: 1604663109836,

View File

@@ -112,7 +112,6 @@
"./src/Utils/BlobUtils.ts",
"./src/Utils/CapabilityUtils.ts",
"./src/Utils/CloudUtils.ts",
"./src/Utils/EndpointUtils.ts",
"./src/Utils/GitHubUtils.test.ts",
"./src/Utils/GitHubUtils.ts",
"./src/Utils/MessageValidation.test.ts",