mirror of
https://github.com/Azure/cosmos-explorer.git
synced 2026-01-06 11:11:23 +00:00
Compare commits
5 Commits
2950560
...
users/sind
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
ad165ae069 | ||
|
|
913a96afec | ||
|
|
e26207e949 | ||
|
|
5d6273889d | ||
|
|
889cf77801 |
@@ -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
18
package-lock.json
generated
@@ -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",
|
||||
|
||||
@@ -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",
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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)
|
||||
);
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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 };
|
||||
|
||||
@@ -46,7 +46,6 @@ export enum MessageTypes {
|
||||
GetAuthorizationToken, // Data Explorer -> Fabric
|
||||
GetAllResourceTokens, // Data Explorer -> Fabric
|
||||
Ready, // Data Explorer -> Fabric
|
||||
OpenCESCVAFeedbackBlade,
|
||||
}
|
||||
|
||||
export interface AuthorizationToken {
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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,
|
||||
};
|
||||
|
||||
@@ -59,6 +59,7 @@ export const CassandraAddCollectionPane: FunctionComponent<CassandraAddCollectio
|
||||
defaultsCheck: {
|
||||
storage: "u",
|
||||
throughput: newKeySpaceThroughput || tableThroughput,
|
||||
flight: userContext.addCollectionFlight,
|
||||
},
|
||||
dataExplorerArea: Constants.Areas.ContextualPane,
|
||||
};
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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"
|
||||
>
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
|
||||
@@ -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}
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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 we’ll 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}
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
/>
|
||||
);
|
||||
|
||||
@@ -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)}
|
||||
|
||||
@@ -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"}
|
||||
|
||||
@@ -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()) {
|
||||
|
||||
@@ -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) => {
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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">
|
||||
|
||||
@@ -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";
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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";
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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";
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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,
|
||||
};
|
||||
|
||||
@@ -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",
|
||||
@@ -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", () => {
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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,
|
||||
});
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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",
|
||||
|
||||
Reference in New Issue
Block a user