mirror of
https://github.com/Azure/cosmos-explorer.git
synced 2025-12-25 11:51:07 +00:00
Compare commits
12 Commits
remove-rup
...
remove-ru-
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
627c346559 | ||
|
|
415ebc505b | ||
|
|
a133134b8b | ||
|
|
79dec6a8a8 | ||
|
|
53a8cea95e | ||
|
|
5f1f7a8266 | ||
|
|
a009a8ba5f | ||
|
|
3e782527d0 | ||
|
|
e6ca1d25c9 | ||
|
|
473f722dcc | ||
|
|
5741802c25 | ||
|
|
e2e58f73b1 |
@@ -1,7 +1,10 @@
|
|||||||
# These options are only needed when if running end to end tests locally
|
|
||||||
PORTAL_RUNNER_USERNAME=
|
PORTAL_RUNNER_USERNAME=
|
||||||
PORTAL_RUNNER_PASSWORD=
|
PORTAL_RUNNER_PASSWORD=
|
||||||
PORTAL_RUNNER_SUBSCRIPTION=
|
PORTAL_RUNNER_SUBSCRIPTION=
|
||||||
PORTAL_RUNNER_RESOURCE_GROUP=
|
PORTAL_RUNNER_RESOURCE_GROUP=
|
||||||
PORTAL_RUNNER_DATABASE_ACCOUNT=
|
PORTAL_RUNNER_DATABASE_ACCOUNT=
|
||||||
PORTAL_RUNNER_CONNECTION_STRING=
|
PORTAL_RUNNER_CONNECTION_STRING=
|
||||||
|
CASSANDRA_CONNECTION_STRING=
|
||||||
|
MONGO_CONNECTION_STRING=
|
||||||
|
TABLES_CONNECTION_STRING=
|
||||||
|
DATA_EXPLORER_ENDPOINT=https://localhost:1234/hostedExplorer.html
|
||||||
@@ -15,8 +15,6 @@ src/Common/DeleteFeedback.ts
|
|||||||
src/Common/DocumentClientUtilityBase.ts
|
src/Common/DocumentClientUtilityBase.ts
|
||||||
src/Common/EditableUtility.ts
|
src/Common/EditableUtility.ts
|
||||||
src/Common/EnvironmentUtility.ts
|
src/Common/EnvironmentUtility.ts
|
||||||
src/Common/ErrorParserUtility.test.ts
|
|
||||||
src/Common/ErrorParserUtility.ts
|
|
||||||
src/Common/HashMap.test.ts
|
src/Common/HashMap.test.ts
|
||||||
src/Common/HashMap.ts
|
src/Common/HashMap.ts
|
||||||
src/Common/HeadersUtility.test.ts
|
src/Common/HeadersUtility.test.ts
|
||||||
|
|||||||
6
.github/workflows/ci.yml
vendored
6
.github/workflows/ci.yml
vendored
@@ -150,6 +150,12 @@ jobs:
|
|||||||
PORTAL_RUNNER_CONNECTION_STRING: ${{ secrets.CONNECTION_STRING_SQL }}
|
PORTAL_RUNNER_CONNECTION_STRING: ${{ secrets.CONNECTION_STRING_SQL }}
|
||||||
MONGO_CONNECTION_STRING: ${{ secrets.CONNECTION_STRING_MONGO }}
|
MONGO_CONNECTION_STRING: ${{ secrets.CONNECTION_STRING_MONGO }}
|
||||||
CASSANDRA_CONNECTION_STRING: ${{ secrets.CONNECTION_STRING_CASSANDRA }}
|
CASSANDRA_CONNECTION_STRING: ${{ secrets.CONNECTION_STRING_CASSANDRA }}
|
||||||
|
TABLES_CONNECTION_STRING: ${{ secrets.CONNECTION_STRING_TABLE }}
|
||||||
|
DATA_EXPLORER_ENDPOINT: "https://localhost:1234/hostedExplorer.html"
|
||||||
|
- uses: actions/upload-artifact@v2
|
||||||
|
with:
|
||||||
|
name: screenshots
|
||||||
|
path: failed-*
|
||||||
nuget:
|
nuget:
|
||||||
name: Publish Nuget
|
name: Publish Nuget
|
||||||
if: github.ref == 'refs/heads/master' || contains(github.ref, 'hotfix/') || contains(github.ref, 'release/')
|
if: github.ref == 'refs/heads/master' || contains(github.ref, 'hotfix/') || contains(github.ref, 'release/')
|
||||||
|
|||||||
15
README.md
15
README.md
@@ -33,7 +33,7 @@ To run pure hosted mode, in `webpack.config.js` change index HtmlWebpackPlugin t
|
|||||||
|
|
||||||
### Emulator Development
|
### Emulator Development
|
||||||
|
|
||||||
In a window environment, running `npm run build` will automatically copy the built files from `/dist` over to the default emulator install paths. In a non-windows enironment you can specify an alternate endpoint using `EMULATOR_ENDPOINT` and webpack dev server will proxy requests for you.
|
In a window environment, running `npm run build` will automatically copy the built files from `/dist` over to the default emulator install paths. In a non-windows environment you can specify an alternate endpoint using `EMULATOR_ENDPOINT` and webpack dev server will proxy requests for you.
|
||||||
|
|
||||||
`PLATFORM=Emulator EMULATOR_ENDPOINT=https://my-vm.azure.com:8081 npm run watch`
|
`PLATFORM=Emulator EMULATOR_ENDPOINT=https://my-vm.azure.com:8081 npm run watch`
|
||||||
|
|
||||||
@@ -60,7 +60,7 @@ The Cosmos Portal that consumes this repo is not currently open source. If you h
|
|||||||
You can however load a local running instance of data explorer in the production portal.
|
You can however load a local running instance of data explorer in the production portal.
|
||||||
|
|
||||||
1. Turn off browser SSL validation for localhost: chrome://flags/#allow-insecure-localhost OR Install valid SSL certs for localhost (on IE, follow these [instructions](https://www.technipages.com/ie-bypass-problem-with-this-websites-security-certificate) to install the localhost certificate in the right place)
|
1. Turn off browser SSL validation for localhost: chrome://flags/#allow-insecure-localhost OR Install valid SSL certs for localhost (on IE, follow these [instructions](https://www.technipages.com/ie-bypass-problem-with-this-websites-security-certificate) to install the localhost certificate in the right place)
|
||||||
2. Whitelist `https://localhost:1234` domain for CORS in the Azure Cosmos DB portal
|
2. Allowlist `https://localhost:1234` domain for CORS in the Azure Cosmos DB portal
|
||||||
3. Start the project in portal mode: `PLATFORM=Portal npm run watch`
|
3. Start the project in portal mode: `PLATFORM=Portal npm run watch`
|
||||||
4. Load the portal using the following link: https://ms.portal.azure.com/?dataExplorerSource=https%3A%2F%2Flocalhost%3A1234%2Fexplorer.html
|
4. Load the portal using the following link: https://ms.portal.azure.com/?dataExplorerSource=https%3A%2F%2Flocalhost%3A1234%2Fexplorer.html
|
||||||
|
|
||||||
@@ -84,16 +84,19 @@ Unit tests are located adjacent to the code under test and run with [Jest](https
|
|||||||
4. Install dependencies: `npm install`
|
4. Install dependencies: `npm install`
|
||||||
5. Run cypress headless(`npm run test`) or in interactive mode(`npm run test:debug`)
|
5. Run cypress headless(`npm run test`) or in interactive mode(`npm run test:debug`)
|
||||||
|
|
||||||
#### End to End Production Runners
|
#### End to End Production Tests
|
||||||
|
|
||||||
Jest and Puppeteer are used for end to end production runners and are contained in `test/`. To run these tests locally:
|
Jest and Puppeteer are used for end to end production runners and are contained in `test/`. To run these tests locally:
|
||||||
|
|
||||||
1. Copy .env.example to .env and fill in all variables
|
1. Copy .env.example to .env
|
||||||
2. Run `npm run test:e2e`
|
2. Update the values in .env including your local data explorer endpoint (ask a teammate/codeowner for help with .env values)
|
||||||
|
3. Make sure all packages are installed `npm install`
|
||||||
|
4. Run the server `npm run start` and wait for it to start
|
||||||
|
5. Run `npm run test:e2e`
|
||||||
|
|
||||||
### Releasing
|
### Releasing
|
||||||
|
|
||||||
We generally adhear to the release strategy [documented by the Azure SDK Guidelines](https://azure.github.io/azure-sdk/policies_repobranching.html#release-branches). Most releases should happen from the master branch. If master contains commits that cannot be released, you may create a release from a `release/` or `hotfix/` branch. See linked documentation for more details.
|
We generally adhere to the release strategy [documented by the Azure SDK Guidelines](https://azure.github.io/azure-sdk/policies_repobranching.html#release-branches). Most releases should happen from the master branch. If master contains commits that cannot be released, you may create a release from a `release/` or `hotfix/` branch. See linked documentation for more details.
|
||||||
|
|
||||||
# Contributing
|
# Contributing
|
||||||
|
|
||||||
|
|||||||
48
package-lock.json
generated
48
package-lock.json
generated
@@ -2803,12 +2803,13 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"@nteract/monaco-editor": {
|
"@nteract/monaco-editor": {
|
||||||
"version": "3.2.0",
|
"version": "3.2.2",
|
||||||
"resolved": "https://registry.npmjs.org/@nteract/monaco-editor/-/monaco-editor-3.2.0.tgz",
|
"resolved": "https://registry.npmjs.org/@nteract/monaco-editor/-/monaco-editor-3.2.2.tgz",
|
||||||
"integrity": "sha512-PGEUvy/GTBMECy4RUfh4wxO7GfA9YDBSV3hGt8MyrVz/GxUDtjB7FqrYS0ZhmVQPYl8hnV2i48F3YlypC+xIXA==",
|
"integrity": "sha512-51Pxt6v6qaAlbDY0BgEydk/Jxuu93t+uB8Geg3vJfE6VDphTEakB0wocBIfvcTKVV55Lx53/rTSp6QHqtaHiGg==",
|
||||||
"requires": {
|
"requires": {
|
||||||
"@nteract/core": "^14.0.0",
|
"@nteract/core": "^14.0.0",
|
||||||
"@nteract/messaging": "^7.0.10",
|
"@nteract/messaging": "^7.0.12",
|
||||||
|
"lodash.debounce": "^4.0.6",
|
||||||
"monaco-editor": "0.18.1",
|
"monaco-editor": "0.18.1",
|
||||||
"rxjs": "^6.3.3"
|
"rxjs": "^6.3.3"
|
||||||
},
|
},
|
||||||
@@ -2857,6 +2858,40 @@
|
|||||||
"rxjs": "^6.3.3"
|
"rxjs": "^6.3.3"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"@nteract/messaging": {
|
||||||
|
"version": "7.0.12",
|
||||||
|
"resolved": "https://registry.npmjs.org/@nteract/messaging/-/messaging-7.0.12.tgz",
|
||||||
|
"integrity": "sha512-5z2Ffd1hj7AsGBJTAoqJshLlUZ+ISJBjiZAdNDjb70PNEv0x8UOMk/di80RI3WBLK5MKxSJkGXfs4jfzfdW6bA==",
|
||||||
|
"requires": {
|
||||||
|
"@nteract/types": "^7.1.2",
|
||||||
|
"@types/uuid": "^8.0.0",
|
||||||
|
"lodash.clonedeep": "^4.5.0",
|
||||||
|
"rxjs": "^6.6.0",
|
||||||
|
"uuid": "^8.0.0"
|
||||||
|
},
|
||||||
|
"dependencies": {
|
||||||
|
"@nteract/commutable": {
|
||||||
|
"version": "7.3.4",
|
||||||
|
"resolved": "https://registry.npmjs.org/@nteract/commutable/-/commutable-7.3.4.tgz",
|
||||||
|
"integrity": "sha512-Z6aUtIZN0CKUMJwbZjUUqaaBhT6P0RiEG5nHso+oG/FOXF20Qv+hf/TyvYhw9SXQVmmacaMk4zj0iOID20pIng==",
|
||||||
|
"requires": {
|
||||||
|
"immutable": "^4.0.0-rc.12",
|
||||||
|
"uuid": "^8.0.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"@nteract/types": {
|
||||||
|
"version": "7.1.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/@nteract/types/-/types-7.1.2.tgz",
|
||||||
|
"integrity": "sha512-I/1TvaUC/m9I/LFk1HemsOUqB0eNdagu0KRLA1YEtChPh9pk5F9flglA7m5+0/j31gLXBISj5+6tL8ikA8BxOQ==",
|
||||||
|
"requires": {
|
||||||
|
"@nteract/commutable": "^7.3.4",
|
||||||
|
"immutable": "^4.0.0-rc.12",
|
||||||
|
"rxjs": "^6.6.0",
|
||||||
|
"uuid": "^8.0.0"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
"@nteract/mythic-notifications": {
|
"@nteract/mythic-notifications": {
|
||||||
"version": "0.1.9",
|
"version": "0.1.9",
|
||||||
"resolved": "https://registry.npmjs.org/@nteract/mythic-notifications/-/mythic-notifications-0.1.9.tgz",
|
"resolved": "https://registry.npmjs.org/@nteract/mythic-notifications/-/mythic-notifications-0.1.9.tgz",
|
||||||
@@ -14422,6 +14457,11 @@
|
|||||||
"resolved": "https://registry.npmjs.org/lodash.curry/-/lodash.curry-4.1.1.tgz",
|
"resolved": "https://registry.npmjs.org/lodash.curry/-/lodash.curry-4.1.1.tgz",
|
||||||
"integrity": "sha1-JI42By7ekGUB11lmIAqG2riyMXA="
|
"integrity": "sha1-JI42By7ekGUB11lmIAqG2riyMXA="
|
||||||
},
|
},
|
||||||
|
"lodash.debounce": {
|
||||||
|
"version": "4.0.8",
|
||||||
|
"resolved": "https://registry.npmjs.org/lodash.debounce/-/lodash.debounce-4.0.8.tgz",
|
||||||
|
"integrity": "sha1-gteb/zCmfEAF/9XiUVMArZyk168="
|
||||||
|
},
|
||||||
"lodash.defaults": {
|
"lodash.defaults": {
|
||||||
"version": "4.2.0",
|
"version": "4.2.0",
|
||||||
"resolved": "https://registry.npmjs.org/lodash.defaults/-/lodash.defaults-4.2.0.tgz",
|
"resolved": "https://registry.npmjs.org/lodash.defaults/-/lodash.defaults-4.2.0.tgz",
|
||||||
|
|||||||
@@ -21,7 +21,7 @@
|
|||||||
"@nteract/jupyter-widgets": "2.0.0",
|
"@nteract/jupyter-widgets": "2.0.0",
|
||||||
"@nteract/logos": "1.0.0",
|
"@nteract/logos": "1.0.0",
|
||||||
"@nteract/markdown": "4.4.0",
|
"@nteract/markdown": "4.4.0",
|
||||||
"@nteract/monaco-editor": "3.2.0",
|
"@nteract/monaco-editor": "3.2.2",
|
||||||
"@nteract/octicons": "2.0.0",
|
"@nteract/octicons": "2.0.0",
|
||||||
"@nteract/outputs": "3.0.9",
|
"@nteract/outputs": "3.0.9",
|
||||||
"@nteract/presentational-components": "3.0.7",
|
"@nteract/presentational-components": "3.0.7",
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
import * as Cosmos from "@azure/cosmos";
|
import * as Cosmos from "@azure/cosmos";
|
||||||
import { RequestInfo, setAuthorizationTokenHeaderUsingMasterKey } from "@azure/cosmos";
|
import { RequestInfo, setAuthorizationTokenHeaderUsingMasterKey } from "@azure/cosmos";
|
||||||
import { configContext, Platform } from "../ConfigContext";
|
import { configContext, Platform } from "../ConfigContext";
|
||||||
|
import { getErrorMessage } from "./ErrorHandlingUtils";
|
||||||
import { logConsoleError } from "../Utils/NotificationConsoleUtils";
|
import { logConsoleError } from "../Utils/NotificationConsoleUtils";
|
||||||
import { EmulatorMasterKey, HttpHeaders } from "./Constants";
|
import { EmulatorMasterKey, HttpHeaders } from "./Constants";
|
||||||
import { userContext } from "../UserContext";
|
import { userContext } from "../UserContext";
|
||||||
@@ -69,7 +70,7 @@ export async function getTokenFromAuthService(verb: string, resourceType: string
|
|||||||
const result = JSON.parse(await response.json());
|
const result = JSON.parse(await response.json());
|
||||||
return result;
|
return result;
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
logConsoleError(`Failed to get authorization headers for ${resourceType}: ${error.message}`);
|
logConsoleError(`Failed to get authorization headers for ${resourceType}: ${getErrorMessage(error)}`);
|
||||||
return Promise.reject(error);
|
return Promise.reject(error);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -58,8 +58,8 @@ export function executeStoredProcedure(
|
|||||||
(error: any) => {
|
(error: any) => {
|
||||||
handleError(
|
handleError(
|
||||||
error,
|
error,
|
||||||
`Failed to execute stored procedure ${storedProcedure.id()} for container ${storedProcedure.collection.id()}`,
|
"ExecuteStoredProcedure",
|
||||||
"ExecuteStoredProcedure"
|
`Failed to execute stored procedure ${storedProcedure.id()} for container ${storedProcedure.collection.id()}`
|
||||||
);
|
);
|
||||||
deferred.reject(error);
|
deferred.reject(error);
|
||||||
}
|
}
|
||||||
@@ -88,7 +88,7 @@ export function queryDocumentsPage(
|
|||||||
deferred.resolve(result);
|
deferred.resolve(result);
|
||||||
},
|
},
|
||||||
(error: any) => {
|
(error: any) => {
|
||||||
handleError(error, `Failed to query ${entityName} for container ${resourceName}`, "QueryDocumentsPage");
|
handleError(error, "QueryDocumentsPage", `Failed to query ${entityName} for container ${resourceName}`);
|
||||||
deferred.reject(error);
|
deferred.reject(error);
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
@@ -109,7 +109,7 @@ export function readDocument(collection: ViewModels.CollectionBase, documentId:
|
|||||||
deferred.resolve(document);
|
deferred.resolve(document);
|
||||||
},
|
},
|
||||||
(error: any) => {
|
(error: any) => {
|
||||||
handleError(error, `Failed to read ${entityName} ${documentId.id()}`, "ReadDocument");
|
handleError(error, "ReadDocument", `Failed to read ${entityName} ${documentId.id()}`);
|
||||||
deferred.reject(error);
|
deferred.reject(error);
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
@@ -135,7 +135,7 @@ export function updateDocument(
|
|||||||
deferred.resolve(updatedDocument);
|
deferred.resolve(updatedDocument);
|
||||||
},
|
},
|
||||||
(error: any) => {
|
(error: any) => {
|
||||||
handleError(error, `Failed to update ${entityName} ${documentId.id()}`, "UpdateDocument");
|
handleError(error, "UpdateDocument", `Failed to update ${entityName} ${documentId.id()}`);
|
||||||
deferred.reject(error);
|
deferred.reject(error);
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
@@ -157,7 +157,7 @@ export function createDocument(collection: ViewModels.CollectionBase, newDocumen
|
|||||||
deferred.resolve(savedDocument);
|
deferred.resolve(savedDocument);
|
||||||
},
|
},
|
||||||
(error: any) => {
|
(error: any) => {
|
||||||
handleError(error, `Error while creating new ${entityName} for container ${collection.id()}`, "CreateDocument");
|
handleError(error, "CreateDocument", `Error while creating new ${entityName} for container ${collection.id()}`);
|
||||||
deferred.reject(error);
|
deferred.reject(error);
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
@@ -179,7 +179,7 @@ export function deleteDocument(collection: ViewModels.CollectionBase, documentId
|
|||||||
deferred.resolve(response);
|
deferred.resolve(response);
|
||||||
},
|
},
|
||||||
(error: any) => {
|
(error: any) => {
|
||||||
handleError(error, `Error while deleting ${entityName} ${documentId.id()}`, "DeleteDocument");
|
handleError(error, "DeleteDocument", `Error while deleting ${entityName} ${documentId.id()}`);
|
||||||
deferred.reject(error);
|
deferred.reject(error);
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
@@ -205,7 +205,7 @@ export function deleteConflict(
|
|||||||
deferred.resolve(response);
|
deferred.resolve(response);
|
||||||
},
|
},
|
||||||
(error: any) => {
|
(error: any) => {
|
||||||
handleError(error, `Error while deleting conflict ${conflictId.id()}`, "DeleteConflict");
|
handleError(error, "DeleteConflict", `Error while deleting conflict ${conflictId.id()}`);
|
||||||
deferred.reject(error);
|
deferred.reject(error);
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -1,11 +1,56 @@
|
|||||||
import { CosmosError, sendNotificationForError } from "./dataAccess/sendNotificationForError";
|
import { ARMError } from "../Utils/arm/request";
|
||||||
|
import { HttpStatusCodes } from "./Constants";
|
||||||
|
import { MessageTypes } from "../Contracts/ExplorerContracts";
|
||||||
|
import { SubscriptionType } from "../Contracts/ViewModels";
|
||||||
import { logConsoleError } from "../Utils/NotificationConsoleUtils";
|
import { logConsoleError } from "../Utils/NotificationConsoleUtils";
|
||||||
import { logError } from "./Logger";
|
import { logError } from "./Logger";
|
||||||
import { replaceKnownError } from "./ErrorParserUtility";
|
import { sendMessage } from "./MessageHandler";
|
||||||
|
|
||||||
export const handleError = (error: CosmosError, consoleErrorPrefix: string, area: string): void => {
|
export const handleError = (error: string | ARMError | Error, area: string, consoleErrorPrefix?: string): void => {
|
||||||
const sanitizedErrorMsg = replaceKnownError(error.message);
|
const errorMessage = getErrorMessage(error);
|
||||||
logConsoleError(`${consoleErrorPrefix}:\n ${sanitizedErrorMsg}`);
|
const errorCode = error instanceof ARMError ? error.code : undefined;
|
||||||
logError(sanitizedErrorMsg, area, error.code);
|
|
||||||
sendNotificationForError(error);
|
// logs error to data explorer console
|
||||||
|
const consoleErrorMessage = consoleErrorPrefix ? `${consoleErrorPrefix}:\n ${errorMessage}` : errorMessage;
|
||||||
|
logConsoleError(consoleErrorMessage);
|
||||||
|
|
||||||
|
// logs error to both app insight and kusto
|
||||||
|
logError(errorMessage, area, errorCode);
|
||||||
|
|
||||||
|
// checks for errors caused by firewall and sends them to portal to handle
|
||||||
|
sendNotificationForError(errorMessage, errorCode);
|
||||||
|
};
|
||||||
|
|
||||||
|
export const getErrorMessage = (error: string | Error): string => {
|
||||||
|
const errorMessage = typeof error === "string" ? error : error.message;
|
||||||
|
return replaceKnownError(errorMessage);
|
||||||
|
};
|
||||||
|
|
||||||
|
export const getErrorStack = (error: string | Error): string => {
|
||||||
|
return typeof error === "string" ? undefined : error.stack;
|
||||||
|
};
|
||||||
|
|
||||||
|
const sendNotificationForError = (errorMessage: string, errorCode: number | string): void => {
|
||||||
|
if (errorCode === HttpStatusCodes.Forbidden) {
|
||||||
|
if (errorMessage?.toLowerCase().indexOf("sharedoffer is disabled for your account") > 0) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
sendMessage({
|
||||||
|
type: MessageTypes.ForbiddenError,
|
||||||
|
reason: errorMessage
|
||||||
|
});
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const replaceKnownError = (errorMessage: string): string => {
|
||||||
|
if (
|
||||||
|
window.dataExplorer?.subscriptionType() === SubscriptionType.Internal &&
|
||||||
|
errorMessage.indexOf("SharedOffer is Disabled for your account") >= 0
|
||||||
|
) {
|
||||||
|
return "Database throughput is not supported for internal subscriptions.";
|
||||||
|
} else if (errorMessage.indexOf("Partition key paths must contain only valid") >= 0) {
|
||||||
|
return "Partition key paths must contain only valid characters and not contain a trailing slash or wildcard character.";
|
||||||
|
}
|
||||||
|
|
||||||
|
return errorMessage;
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -1,24 +0,0 @@
|
|||||||
import * as ErrorParserUtility from "./ErrorParserUtility";
|
|
||||||
|
|
||||||
describe("Error Parser Utility", () => {
|
|
||||||
describe("shouldEnableCrossPartitionKeyForResourceWithPartitionKey()", () => {
|
|
||||||
it("should parse a backend error correctly", () => {
|
|
||||||
// A fake error matching what is thrown by the SDK on a bad collection create request
|
|
||||||
const innerMessage =
|
|
||||||
"The partition key component definition path '/asdwqr31 @#$#$WRadf' could not be accepted, failed near position '10'. Partition key paths must contain only valid characters and not contain a trailing slash or wildcard character.";
|
|
||||||
const message = `Message: {\"Errors\":[\"${innerMessage}\"]}\r\nActivityId: 97b2e684-7505-4921-85f6-2513b9b28220, Request URI: /apps/89fdcf25-2a0b-4d2a-aab6-e161e565b26f/services/54911149-7bb1-4e7d-a1fa-22c8b36a4bb9/partitions/cc2a7a04-5f5a-4709-bcf7-8509b264963f/replicas/132304018743619218p, RequestStats: , SDK: Microsoft.Azure.Documents.Common/2.10.0`;
|
|
||||||
const err = new Error(message) as any;
|
|
||||||
err.code = 400;
|
|
||||||
err.body = {
|
|
||||||
code: "BadRequest",
|
|
||||||
message
|
|
||||||
};
|
|
||||||
err.headers = {};
|
|
||||||
err.activityId = "97b2e684-7505-4921-85f6-2513b9b28220";
|
|
||||||
|
|
||||||
const parsedError = ErrorParserUtility.parse(err);
|
|
||||||
expect(parsedError.length).toBe(1);
|
|
||||||
expect(parsedError[0].message).toBe(innerMessage);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
|
||||||
@@ -1,69 +0,0 @@
|
|||||||
import * as DataModels from "../Contracts/DataModels";
|
|
||||||
import * as ViewModels from "../Contracts/ViewModels";
|
|
||||||
|
|
||||||
export function replaceKnownError(err: string): string {
|
|
||||||
if (
|
|
||||||
window.dataExplorer.subscriptionType() === ViewModels.SubscriptionType.Internal &&
|
|
||||||
err.indexOf("SharedOffer is Disabled for your account") >= 0
|
|
||||||
) {
|
|
||||||
return "Database throughput is not supported for internal subscriptions.";
|
|
||||||
} else if (err.indexOf("Partition key paths must contain only valid") >= 0) {
|
|
||||||
return "Partition key paths must contain only valid characters and not contain a trailing slash or wildcard character.";
|
|
||||||
}
|
|
||||||
|
|
||||||
return err;
|
|
||||||
}
|
|
||||||
|
|
||||||
export function parse(err: any): DataModels.ErrorDataModel[] {
|
|
||||||
try {
|
|
||||||
return _parse(err);
|
|
||||||
} catch (e) {
|
|
||||||
return [<DataModels.ErrorDataModel>{ message: JSON.stringify(err) }];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function _parse(err: any): DataModels.ErrorDataModel[] {
|
|
||||||
var normalizedErrors: DataModels.ErrorDataModel[] = [];
|
|
||||||
if (err.message && !err.code) {
|
|
||||||
normalizedErrors.push(err);
|
|
||||||
} else {
|
|
||||||
const innerErrors: any[] = _getInnerErrors(err.message);
|
|
||||||
normalizedErrors = innerErrors.map(innerError =>
|
|
||||||
typeof innerError === "string" ? { message: innerError } : innerError
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
return normalizedErrors;
|
|
||||||
}
|
|
||||||
|
|
||||||
function _getInnerErrors(message: string): any[] {
|
|
||||||
/*
|
|
||||||
The backend error message has an inner-message which is a stringified object.
|
|
||||||
|
|
||||||
For SQL errors, the "errors" property is an array of SqlErrorDataModel.
|
|
||||||
Example:
|
|
||||||
"Message: {"Errors":["Resource with specified id or name already exists"]}\r\nActivityId: 80005000008d40b6a, Request URI: /apps/19000c000c0a0005/services/mctestdocdbprod-MasterService-0-00066ab9937/partitions/900005f9000e676fb8/replicas/13000000000955p"
|
|
||||||
For non-SQL errors the "Errors" propery is an array of string.
|
|
||||||
Example:
|
|
||||||
"Message: {"errors":[{"severity":"Error","location":{"start":7,"end":8},"code":"SC1001","message":"Syntax error, incorrect syntax near '.'."}]}\r\nActivityId: d3300016d4084e310a, Request URI: /apps/12401f9e1df77/services/dc100232b1f44545/partitions/f86f3bc0001a2f78/replicas/13085003638s"
|
|
||||||
*/
|
|
||||||
|
|
||||||
let innerMessage: any = null;
|
|
||||||
|
|
||||||
const singleLineMessage = message.replace(/[\r\n]|\r|\n/g, "");
|
|
||||||
try {
|
|
||||||
// Multi-Partition error flavor
|
|
||||||
const regExp = /^(.*)ActivityId: (.*)/g;
|
|
||||||
const regString = regExp.exec(singleLineMessage);
|
|
||||||
const innerMessageString = regString[1];
|
|
||||||
innerMessage = JSON.parse(innerMessageString);
|
|
||||||
} catch (e) {
|
|
||||||
// Single-partition error flavor
|
|
||||||
const regExp = /^Message: (.*)ActivityId: (.*), Request URI: (.*)/g;
|
|
||||||
const regString = regExp.exec(singleLineMessage);
|
|
||||||
const innerMessageString = regString[1];
|
|
||||||
innerMessage = JSON.parse(innerMessageString);
|
|
||||||
}
|
|
||||||
|
|
||||||
return innerMessage.errors ? innerMessage.errors : innerMessage.Errors;
|
|
||||||
}
|
|
||||||
@@ -21,14 +21,8 @@ export function logWarning(message: string, area: string, code?: number): void {
|
|||||||
return _logEntry(entry);
|
return _logEntry(entry);
|
||||||
}
|
}
|
||||||
|
|
||||||
export function logError(message: string | Error, area: string, code?: number): void {
|
export function logError(errorMessage: string, area: string, code?: number | string): void {
|
||||||
let logMessage: string;
|
const entry: Diagnostics.LogEntry = _generateLogEntry(Diagnostics.LogEntryLevel.Error, errorMessage, area, code);
|
||||||
if (typeof message === "string") {
|
|
||||||
logMessage = message;
|
|
||||||
} else {
|
|
||||||
logMessage = JSON.stringify(message, Object.getOwnPropertyNames(message));
|
|
||||||
}
|
|
||||||
const entry: Diagnostics.LogEntry = _generateLogEntry(Diagnostics.LogEntryLevel.Error, logMessage, area, code);
|
|
||||||
return _logEntry(entry);
|
return _logEntry(entry);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -59,7 +53,7 @@ function _generateLogEntry(
|
|||||||
level: Diagnostics.LogEntryLevel,
|
level: Diagnostics.LogEntryLevel,
|
||||||
message: string,
|
message: string,
|
||||||
area: string,
|
area: string,
|
||||||
code?: number
|
code?: number | string
|
||||||
): Diagnostics.LogEntry {
|
): Diagnostics.LogEntry {
|
||||||
return {
|
return {
|
||||||
timestamp: new Date().getUTCSeconds(),
|
timestamp: new Date().getUTCSeconds(),
|
||||||
|
|||||||
@@ -12,8 +12,7 @@ import { BackendDefaults, HttpStatusCodes, SavedQueries } from "./Constants";
|
|||||||
import { userContext } from "../UserContext";
|
import { userContext } from "../UserContext";
|
||||||
import { createDocument, deleteDocument, queryDocuments, queryDocumentsPage } from "./DocumentClientUtilityBase";
|
import { createDocument, deleteDocument, queryDocuments, queryDocumentsPage } from "./DocumentClientUtilityBase";
|
||||||
import { createCollection } from "./dataAccess/createCollection";
|
import { createCollection } from "./dataAccess/createCollection";
|
||||||
import * as ErrorParserUtility from "./ErrorParserUtility";
|
import { handleError } from "./ErrorHandlingUtils";
|
||||||
import * as Logger from "./Logger";
|
|
||||||
|
|
||||||
export class QueriesClient {
|
export class QueriesClient {
|
||||||
private static readonly PartitionKey: DataModels.PartitionKey = {
|
private static readonly PartitionKey: DataModels.PartitionKey = {
|
||||||
@@ -53,13 +52,8 @@ export class QueriesClient {
|
|||||||
return Promise.resolve(collection);
|
return Promise.resolve(collection);
|
||||||
},
|
},
|
||||||
(error: any) => {
|
(error: any) => {
|
||||||
const stringifiedError: string = error.message;
|
handleError(error, "setupQueriesCollection", "Failed to set up account for saving queries");
|
||||||
NotificationConsoleUtils.logConsoleMessage(
|
return Promise.reject(error);
|
||||||
ConsoleDataType.Error,
|
|
||||||
`Failed to set up account for saving queries: ${stringifiedError}`
|
|
||||||
);
|
|
||||||
Logger.logError(stringifiedError, "setupQueriesCollection");
|
|
||||||
return Promise.reject(stringifiedError);
|
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
.finally(() => NotificationConsoleUtils.clearInProgressMessageWithId(id));
|
.finally(() => NotificationConsoleUtils.clearInProgressMessageWithId(id));
|
||||||
@@ -102,19 +96,11 @@ export class QueriesClient {
|
|||||||
return Promise.resolve();
|
return Promise.resolve();
|
||||||
},
|
},
|
||||||
(error: any) => {
|
(error: any) => {
|
||||||
let errorMessage: string;
|
if (error.code === HttpStatusCodes.Conflict.toString()) {
|
||||||
const parsedError: DataModels.ErrorDataModel = ErrorParserUtility.parse(error)[0];
|
error = `Query ${query.queryName} already exists`;
|
||||||
if (parsedError.code === HttpStatusCodes.Conflict.toString()) {
|
|
||||||
errorMessage = `Query ${query.queryName} already exists`;
|
|
||||||
} else {
|
|
||||||
errorMessage = parsedError.message;
|
|
||||||
}
|
}
|
||||||
NotificationConsoleUtils.logConsoleMessage(
|
handleError(error, "saveQuery", `Failed to save query ${query.queryName}`);
|
||||||
ConsoleDataType.Error,
|
return Promise.reject(error);
|
||||||
`Failed to save query ${query.queryName}: ${errorMessage}`
|
|
||||||
);
|
|
||||||
Logger.logError(JSON.stringify(parsedError), "saveQuery");
|
|
||||||
return Promise.reject(errorMessage);
|
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
.finally(() => NotificationConsoleUtils.clearInProgressMessageWithId(id));
|
.finally(() => NotificationConsoleUtils.clearInProgressMessageWithId(id));
|
||||||
@@ -163,25 +149,15 @@ export class QueriesClient {
|
|||||||
return Promise.resolve(queries);
|
return Promise.resolve(queries);
|
||||||
},
|
},
|
||||||
(error: any) => {
|
(error: any) => {
|
||||||
const stringifiedError: string = error.message;
|
handleError(error, "getSavedQueries", "Failed to fetch saved queries");
|
||||||
NotificationConsoleUtils.logConsoleMessage(
|
return Promise.reject(error);
|
||||||
ConsoleDataType.Error,
|
|
||||||
`Failed to fetch saved queries: ${stringifiedError}`
|
|
||||||
);
|
|
||||||
Logger.logError(stringifiedError, "getSavedQueries");
|
|
||||||
return Promise.reject(stringifiedError);
|
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
(error: any) => {
|
(error: any) => {
|
||||||
// should never get into this state but we handle this regardless
|
// should never get into this state but we handle this regardless
|
||||||
const stringifiedError: string = error.message;
|
handleError(error, "getSavedQueries", "Failed to fetch saved queries");
|
||||||
NotificationConsoleUtils.logConsoleMessage(
|
return Promise.reject(error);
|
||||||
ConsoleDataType.Error,
|
|
||||||
`Failed to fetch saved queries: ${stringifiedError}`
|
|
||||||
);
|
|
||||||
Logger.logError(stringifiedError, "getSavedQueries");
|
|
||||||
return Promise.reject(stringifiedError);
|
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
.finally(() => NotificationConsoleUtils.clearInProgressMessageWithId(id));
|
.finally(() => NotificationConsoleUtils.clearInProgressMessageWithId(id));
|
||||||
@@ -232,13 +208,8 @@ export class QueriesClient {
|
|||||||
return Promise.resolve();
|
return Promise.resolve();
|
||||||
},
|
},
|
||||||
(error: any) => {
|
(error: any) => {
|
||||||
const stringifiedError: string = error.message;
|
handleError(error, "deleteQuery", `Failed to delete query ${query.queryName}`);
|
||||||
NotificationConsoleUtils.logConsoleMessage(
|
return Promise.reject(error);
|
||||||
ConsoleDataType.Error,
|
|
||||||
`Failed to delete query ${query.queryName}: ${stringifiedError}`
|
|
||||||
);
|
|
||||||
Logger.logError(stringifiedError, "deleteQuery");
|
|
||||||
return Promise.reject(stringifiedError);
|
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
.finally(() => NotificationConsoleUtils.clearInProgressMessageWithId(id));
|
.finally(() => NotificationConsoleUtils.clearInProgressMessageWithId(id));
|
||||||
|
|||||||
@@ -55,7 +55,7 @@ export const createCollection = async (params: DataModels.CreateCollectionParams
|
|||||||
logConsoleInfo(`Successfully created container ${params.collectionId}`);
|
logConsoleInfo(`Successfully created container ${params.collectionId}`);
|
||||||
return collection;
|
return collection;
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
handleError(error, `Error while creating container ${params.collectionId}`, "CreateCollection");
|
handleError(error, "CreateCollection", `Error while creating container ${params.collectionId}`);
|
||||||
throw error;
|
throw error;
|
||||||
} finally {
|
} finally {
|
||||||
clearMessage();
|
clearMessage();
|
||||||
|
|||||||
@@ -41,7 +41,7 @@ export async function createDatabase(params: DataModels.CreateDatabaseParams): P
|
|||||||
logConsoleInfo(`Successfully created database ${params.databaseId}`);
|
logConsoleInfo(`Successfully created database ${params.databaseId}`);
|
||||||
return database;
|
return database;
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
handleError(error, `Error while creating database ${params.databaseId}`, "CreateDatabase");
|
handleError(error, "CreateDatabase", `Error while creating database ${params.databaseId}`);
|
||||||
throw error;
|
throw error;
|
||||||
} finally {
|
} finally {
|
||||||
clearMessage();
|
clearMessage();
|
||||||
|
|||||||
@@ -70,7 +70,7 @@ export async function createStoredProcedure(
|
|||||||
.scripts.storedProcedures.create(storedProcedure);
|
.scripts.storedProcedures.create(storedProcedure);
|
||||||
return response?.resource;
|
return response?.resource;
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
handleError(error, `Error while creating stored procedure ${storedProcedure.id}`, "CreateStoredProcedure");
|
handleError(error, "CreateStoredProcedure", `Error while creating stored procedure ${storedProcedure.id}`);
|
||||||
throw error;
|
throw error;
|
||||||
} finally {
|
} finally {
|
||||||
clearMessage();
|
clearMessage();
|
||||||
|
|||||||
@@ -7,9 +7,8 @@ import {
|
|||||||
} from "../../Utils/arm/generatedClients/2020-04-01/types";
|
} from "../../Utils/arm/generatedClients/2020-04-01/types";
|
||||||
import { client } from "../CosmosClient";
|
import { client } from "../CosmosClient";
|
||||||
import { createUpdateSqlTrigger, getSqlTrigger } from "../../Utils/arm/generatedClients/2020-04-01/sqlResources";
|
import { createUpdateSqlTrigger, getSqlTrigger } from "../../Utils/arm/generatedClients/2020-04-01/sqlResources";
|
||||||
import { logConsoleError, logConsoleProgress } from "../../Utils/NotificationConsoleUtils";
|
import { handleError } from "../ErrorHandlingUtils";
|
||||||
import { logError } from "../Logger";
|
import { logConsoleProgress } from "../../Utils/NotificationConsoleUtils";
|
||||||
import { sendNotificationForError } from "./sendNotificationForError";
|
|
||||||
import { userContext } from "../../UserContext";
|
import { userContext } from "../../UserContext";
|
||||||
|
|
||||||
export async function createTrigger(
|
export async function createTrigger(
|
||||||
@@ -66,9 +65,7 @@ export async function createTrigger(
|
|||||||
.scripts.triggers.create(trigger);
|
.scripts.triggers.create(trigger);
|
||||||
return response.resource;
|
return response.resource;
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
logConsoleError(`Error while creating trigger ${trigger.id}:\n ${error.message}`);
|
handleError(error, "CreateTrigger", `Error while creating trigger ${trigger.id}`);
|
||||||
logError(error.message, "CreateTrigger", error.code);
|
|
||||||
sendNotificationForError(error);
|
|
||||||
throw error;
|
throw error;
|
||||||
} finally {
|
} finally {
|
||||||
clearMessage();
|
clearMessage();
|
||||||
|
|||||||
@@ -72,8 +72,8 @@ export async function createUserDefinedFunction(
|
|||||||
} catch (error) {
|
} catch (error) {
|
||||||
handleError(
|
handleError(
|
||||||
error,
|
error,
|
||||||
`Error while creating user defined function ${userDefinedFunction.id}`,
|
"CreateUserupdateUserDefinedFunction",
|
||||||
"CreateUserupdateUserDefinedFunction"
|
`Error while creating user defined function ${userDefinedFunction.id}`
|
||||||
);
|
);
|
||||||
throw error;
|
throw error;
|
||||||
} finally {
|
} finally {
|
||||||
|
|||||||
@@ -23,7 +23,7 @@ export async function deleteCollection(databaseId: string, collectionId: string)
|
|||||||
}
|
}
|
||||||
logConsoleInfo(`Successfully deleted container ${collectionId}`);
|
logConsoleInfo(`Successfully deleted container ${collectionId}`);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
handleError(error, `Error while deleting container ${collectionId}`, "DeleteCollection");
|
handleError(error, "DeleteCollection", `Error while deleting container ${collectionId}`);
|
||||||
throw error;
|
throw error;
|
||||||
} finally {
|
} finally {
|
||||||
clearMessage();
|
clearMessage();
|
||||||
|
|||||||
@@ -25,7 +25,7 @@ export async function deleteDatabase(databaseId: string): Promise<void> {
|
|||||||
}
|
}
|
||||||
logConsoleInfo(`Successfully deleted database ${databaseId}`);
|
logConsoleInfo(`Successfully deleted database ${databaseId}`);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
handleError(error, `Error while deleting database ${databaseId}`, "DeleteDatabase");
|
handleError(error, "DeleteDatabase", `Error while deleting database ${databaseId}`);
|
||||||
throw error;
|
throw error;
|
||||||
} finally {
|
} finally {
|
||||||
clearMessage();
|
clearMessage();
|
||||||
|
|||||||
@@ -34,7 +34,7 @@ export async function deleteStoredProcedure(
|
|||||||
.delete();
|
.delete();
|
||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
handleError(error, `Error while deleting stored procedure ${storedProcedureId}`, "DeleteStoredProcedure");
|
handleError(error, "DeleteStoredProcedure", `Error while deleting stored procedure ${storedProcedureId}`);
|
||||||
throw error;
|
throw error;
|
||||||
} finally {
|
} finally {
|
||||||
clearMessage();
|
clearMessage();
|
||||||
|
|||||||
@@ -30,7 +30,7 @@ export async function deleteTrigger(databaseId: string, collectionId: string, tr
|
|||||||
.delete();
|
.delete();
|
||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
handleError(error, `Error while deleting trigger ${triggerId}`, "DeleteTrigger");
|
handleError(error, "DeleteTrigger", `Error while deleting trigger ${triggerId}`);
|
||||||
throw error;
|
throw error;
|
||||||
} finally {
|
} finally {
|
||||||
clearMessage();
|
clearMessage();
|
||||||
|
|||||||
@@ -30,7 +30,7 @@ export async function deleteUserDefinedFunction(databaseId: string, collectionId
|
|||||||
.delete();
|
.delete();
|
||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
handleError(error, `Error while deleting user defined function ${id}`, "DeleteUserDefinedFunction");
|
handleError(error, "DeleteUserDefinedFunction", `Error while deleting user defined function ${id}`);
|
||||||
throw error;
|
throw error;
|
||||||
} finally {
|
} finally {
|
||||||
clearMessage();
|
clearMessage();
|
||||||
|
|||||||
28
src/Common/dataAccess/getIndexTransformationProgress.ts
Normal file
28
src/Common/dataAccess/getIndexTransformationProgress.ts
Normal file
@@ -0,0 +1,28 @@
|
|||||||
|
import { client } from "../CosmosClient";
|
||||||
|
import { handleError } from "../ErrorHandlingUtils";
|
||||||
|
import { logConsoleProgress } from "../../Utils/NotificationConsoleUtils";
|
||||||
|
import * as Constants from "../Constants";
|
||||||
|
import { AuthType } from "../../AuthType";
|
||||||
|
|
||||||
|
export async function getIndexTransformationProgress(databaseId: string, collectionId: string): Promise<number> {
|
||||||
|
if (window.authType !== AuthType.AAD) {
|
||||||
|
return undefined;
|
||||||
|
}
|
||||||
|
let indexTransformationPercentage: number;
|
||||||
|
const clearMessage = logConsoleProgress(`Reading container ${collectionId}`);
|
||||||
|
try {
|
||||||
|
const response = await client()
|
||||||
|
.database(databaseId)
|
||||||
|
.container(collectionId)
|
||||||
|
.read({ populateQuotaInfo: true });
|
||||||
|
|
||||||
|
indexTransformationPercentage = parseInt(
|
||||||
|
response.headers[Constants.HttpHeaders.collectionIndexTransformationProgress] as string
|
||||||
|
);
|
||||||
|
} catch (error) {
|
||||||
|
handleError(error, "ReadMongoDBCollection", `Error while reading container ${collectionId}`);
|
||||||
|
throw error;
|
||||||
|
}
|
||||||
|
clearMessage();
|
||||||
|
return indexTransformationPercentage;
|
||||||
|
}
|
||||||
@@ -13,7 +13,7 @@ export async function readCollection(databaseId: string, collectionId: string):
|
|||||||
.read();
|
.read();
|
||||||
collection = response.resource as DataModels.Collection;
|
collection = response.resource as DataModels.Collection;
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
handleError(error, `Error while querying container ${collectionId}`, "ReadCollection");
|
handleError(error, "ReadCollection", `Error while querying container ${collectionId}`);
|
||||||
throw error;
|
throw error;
|
||||||
}
|
}
|
||||||
clearMessage();
|
clearMessage();
|
||||||
|
|||||||
@@ -56,7 +56,7 @@ export const readCollectionOffer = async (
|
|||||||
}
|
}
|
||||||
);
|
);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
handleError(error, `Error while querying offer for collection ${params.collectionId}`, "ReadCollectionOffer");
|
handleError(error, "ReadCollectionOffer", `Error while querying offer for collection ${params.collectionId}`);
|
||||||
throw error;
|
throw error;
|
||||||
} finally {
|
} finally {
|
||||||
clearMessage();
|
clearMessage();
|
||||||
|
|||||||
@@ -1,45 +0,0 @@
|
|||||||
import * as DataModels from "../../Contracts/DataModels";
|
|
||||||
import * as HeadersUtility from "../HeadersUtility";
|
|
||||||
import * as ViewModels from "../../Contracts/ViewModels";
|
|
||||||
import { ContainerDefinition, Resource } from "@azure/cosmos";
|
|
||||||
import { HttpHeaders } from "../Constants";
|
|
||||||
import { RequestOptions } from "@azure/cosmos/dist-esm";
|
|
||||||
import { client } from "../CosmosClient";
|
|
||||||
import { handleError } from "../ErrorHandlingUtils";
|
|
||||||
import { logConsoleProgress } from "../../Utils/NotificationConsoleUtils";
|
|
||||||
|
|
||||||
interface ResourceWithStatistics {
|
|
||||||
statistics: DataModels.Statistic[];
|
|
||||||
}
|
|
||||||
|
|
||||||
export const readCollectionQuotaInfo = async (
|
|
||||||
collection: ViewModels.Collection
|
|
||||||
): Promise<DataModels.CollectionQuotaInfo> => {
|
|
||||||
const clearMessage = logConsoleProgress(`Querying containers for database ${collection.id}`);
|
|
||||||
const options: RequestOptions = {};
|
|
||||||
options.populateQuotaInfo = true;
|
|
||||||
options.initialHeaders = options.initialHeaders || {};
|
|
||||||
options.initialHeaders[HttpHeaders.populatePartitionStatistics] = true;
|
|
||||||
|
|
||||||
try {
|
|
||||||
const response = await client()
|
|
||||||
.database(collection.databaseId)
|
|
||||||
.container(collection.id())
|
|
||||||
.read(options);
|
|
||||||
const quota: DataModels.CollectionQuotaInfo = HeadersUtility.getQuota(response.headers);
|
|
||||||
const resource = response.resource as ContainerDefinition & Resource & ResourceWithStatistics;
|
|
||||||
quota["usageSizeInKB"] = resource.statistics.reduce(
|
|
||||||
(previousValue: number, currentValue: DataModels.Statistic) => previousValue + currentValue.sizeInKB,
|
|
||||||
0
|
|
||||||
);
|
|
||||||
quota["numPartitions"] = resource.statistics.length;
|
|
||||||
quota["uniqueKeyPolicy"] = collection.uniqueKeyPolicy; // TODO: Remove after refactoring (#119617)
|
|
||||||
|
|
||||||
return quota;
|
|
||||||
} catch (error) {
|
|
||||||
handleError(error, `Error while querying quota info for container ${collection.id}`, "ReadCollectionQuotaInfo");
|
|
||||||
throw error;
|
|
||||||
} finally {
|
|
||||||
clearMessage();
|
|
||||||
}
|
|
||||||
};
|
|
||||||
@@ -29,7 +29,7 @@ export async function readCollections(databaseId: string): Promise<DataModels.Co
|
|||||||
.fetchAll();
|
.fetchAll();
|
||||||
return sdkResponse.resources as DataModels.Collection[];
|
return sdkResponse.resources as DataModels.Collection[];
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
handleError(error, `Error while querying containers for database ${databaseId}`, "ReadCollections");
|
handleError(error, "ReadCollections", `Error while querying containers for database ${databaseId}`);
|
||||||
throw error;
|
throw error;
|
||||||
} finally {
|
} finally {
|
||||||
clearMessage();
|
clearMessage();
|
||||||
|
|||||||
@@ -47,7 +47,7 @@ export const readDatabaseOffer = async (
|
|||||||
}
|
}
|
||||||
);
|
);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
handleError(error, `Error while querying offer for database ${params.databaseId}`, "ReadDatabaseOffer");
|
handleError(error, "ReadDatabaseOffer", `Error while querying offer for database ${params.databaseId}`);
|
||||||
throw error;
|
throw error;
|
||||||
} finally {
|
} finally {
|
||||||
clearMessage();
|
clearMessage();
|
||||||
|
|||||||
@@ -27,7 +27,7 @@ export async function readDatabases(): Promise<DataModels.Database[]> {
|
|||||||
databases = sdkResponse.resources as DataModels.Database[];
|
databases = sdkResponse.resources as DataModels.Database[];
|
||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
handleError(error, `Error while querying databases`, "ReadDatabases");
|
handleError(error, "ReadDatabases", `Error while querying databases`);
|
||||||
throw error;
|
throw error;
|
||||||
}
|
}
|
||||||
clearMessage();
|
clearMessage();
|
||||||
|
|||||||
@@ -2,8 +2,6 @@ import { userContext } from "../../UserContext";
|
|||||||
import { getMongoDBCollection } from "../../Utils/arm/generatedClients/2020-04-01/mongoDBResources";
|
import { getMongoDBCollection } from "../../Utils/arm/generatedClients/2020-04-01/mongoDBResources";
|
||||||
import { MongoDBCollectionResource } from "../../Utils/arm/generatedClients/2020-04-01/types";
|
import { MongoDBCollectionResource } from "../../Utils/arm/generatedClients/2020-04-01/types";
|
||||||
import { logConsoleProgress } from "../../Utils/NotificationConsoleUtils";
|
import { logConsoleProgress } from "../../Utils/NotificationConsoleUtils";
|
||||||
import * as Constants from "../Constants";
|
|
||||||
import { client } from "../CosmosClient";
|
|
||||||
import { handleError } from "../ErrorHandlingUtils";
|
import { handleError } from "../ErrorHandlingUtils";
|
||||||
import { AuthType } from "../../AuthType";
|
import { AuthType } from "../../AuthType";
|
||||||
|
|
||||||
@@ -24,35 +22,9 @@ export async function readMongoDBCollectionThroughRP(
|
|||||||
const response = await getMongoDBCollection(subscriptionId, resourceGroup, accountName, databaseId, collectionId);
|
const response = await getMongoDBCollection(subscriptionId, resourceGroup, accountName, databaseId, collectionId);
|
||||||
collection = response.properties.resource;
|
collection = response.properties.resource;
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
handleError(error, `Error while reading container ${collectionId}`, "ReadMongoDBCollection");
|
handleError(error, "ReadMongoDBCollection", `Error while reading container ${collectionId}`);
|
||||||
throw error;
|
throw error;
|
||||||
}
|
}
|
||||||
clearMessage();
|
clearMessage();
|
||||||
return collection;
|
return collection;
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function getMongoDBCollectionIndexTransformationProgress(
|
|
||||||
databaseId: string,
|
|
||||||
collectionId: string
|
|
||||||
): Promise<number> {
|
|
||||||
if (window.authType !== AuthType.AAD) {
|
|
||||||
return undefined;
|
|
||||||
}
|
|
||||||
let indexTransformationPercentage: number;
|
|
||||||
const clearMessage = logConsoleProgress(`Reading container ${collectionId}`);
|
|
||||||
try {
|
|
||||||
const response = await client()
|
|
||||||
.database(databaseId)
|
|
||||||
.container(collectionId)
|
|
||||||
.read({ populateQuotaInfo: true });
|
|
||||||
|
|
||||||
indexTransformationPercentage = parseInt(
|
|
||||||
response.headers[Constants.HttpHeaders.collectionIndexTransformationProgress] as string
|
|
||||||
);
|
|
||||||
} catch (error) {
|
|
||||||
handleError(error, `Error while reading container ${collectionId}`, "ReadMongoDBCollection");
|
|
||||||
throw error;
|
|
||||||
}
|
|
||||||
clearMessage();
|
|
||||||
return indexTransformationPercentage;
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
import { Offer } from "../../Contracts/DataModels";
|
import { Offer } from "../../Contracts/DataModels";
|
||||||
import { logConsoleProgress } from "../../Utils/NotificationConsoleUtils";
|
import { logConsoleProgress } from "../../Utils/NotificationConsoleUtils";
|
||||||
import { client } from "../CosmosClient";
|
import { client } from "../CosmosClient";
|
||||||
import { handleError } from "../ErrorHandlingUtils";
|
import { handleError, getErrorMessage } from "../ErrorHandlingUtils";
|
||||||
|
|
||||||
export const readOffers = async (): Promise<Offer[]> => {
|
export const readOffers = async (): Promise<Offer[]> => {
|
||||||
const clearMessage = logConsoleProgress(`Querying offers`);
|
const clearMessage = logConsoleProgress(`Querying offers`);
|
||||||
@@ -13,11 +13,11 @@ export const readOffers = async (): Promise<Offer[]> => {
|
|||||||
return response?.resources;
|
return response?.resources;
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
// This should be removed when we can correctly identify if an account is serverless when connected using connection string too.
|
// This should be removed when we can correctly identify if an account is serverless when connected using connection string too.
|
||||||
if (error.message.includes("Reading or replacing offers is not supported for serverless accounts")) {
|
if (getErrorMessage(error)?.includes("Reading or replacing offers is not supported for serverless accounts")) {
|
||||||
return [];
|
return [];
|
||||||
}
|
}
|
||||||
|
|
||||||
handleError(error, `Error while querying offers`, "ReadOffers");
|
handleError(error, "ReadOffers", `Error while querying offers`);
|
||||||
throw error;
|
throw error;
|
||||||
} finally {
|
} finally {
|
||||||
clearMessage();
|
clearMessage();
|
||||||
|
|||||||
@@ -35,7 +35,7 @@ export async function readStoredProcedures(
|
|||||||
.fetchAll();
|
.fetchAll();
|
||||||
return response?.resources;
|
return response?.resources;
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
handleError(error, `Failed to query stored procedures for container ${collectionId}`, "ReadStoredProcedures");
|
handleError(error, "ReadStoredProcedures", `Failed to query stored procedures for container ${collectionId}`);
|
||||||
throw error;
|
throw error;
|
||||||
} finally {
|
} finally {
|
||||||
clearMessage();
|
clearMessage();
|
||||||
|
|||||||
@@ -35,7 +35,7 @@ export async function readTriggers(
|
|||||||
.fetchAll();
|
.fetchAll();
|
||||||
return response?.resources;
|
return response?.resources;
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
handleError(error, `Failed to query triggers for container ${collectionId}`, "ReadTriggers");
|
handleError(error, "ReadTriggers", `Failed to query triggers for container ${collectionId}`);
|
||||||
throw error;
|
throw error;
|
||||||
} finally {
|
} finally {
|
||||||
clearMessage();
|
clearMessage();
|
||||||
|
|||||||
@@ -37,8 +37,8 @@ export async function readUserDefinedFunctions(
|
|||||||
} catch (error) {
|
} catch (error) {
|
||||||
handleError(
|
handleError(
|
||||||
error,
|
error,
|
||||||
`Failed to query user defined functions for container ${collectionId}`,
|
"ReadUserDefinedFunctions",
|
||||||
"ReadUserDefinedFunctions"
|
`Failed to query user defined functions for container ${collectionId}`
|
||||||
);
|
);
|
||||||
throw error;
|
throw error;
|
||||||
} finally {
|
} finally {
|
||||||
|
|||||||
@@ -1,20 +0,0 @@
|
|||||||
import * as Constants from "../Constants";
|
|
||||||
import { sendMessage } from "../MessageHandler";
|
|
||||||
import { MessageTypes } from "../../Contracts/ExplorerContracts";
|
|
||||||
|
|
||||||
export interface CosmosError {
|
|
||||||
code: number;
|
|
||||||
message?: string;
|
|
||||||
}
|
|
||||||
|
|
||||||
export function sendNotificationForError(error: CosmosError): void {
|
|
||||||
if (error && error.code === Constants.HttpStatusCodes.Forbidden) {
|
|
||||||
if (error.message && error.message.toLowerCase().indexOf("sharedoffer is disabled for your account") > 0) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
sendMessage({
|
|
||||||
type: MessageTypes.ForbiddenError,
|
|
||||||
reason: error && error.message ? error.message : error
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -59,7 +59,7 @@ export async function updateCollection(
|
|||||||
logConsoleInfo(`Successfully updated container ${collectionId}`);
|
logConsoleInfo(`Successfully updated container ${collectionId}`);
|
||||||
return collection;
|
return collection;
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
handleError(error, `Failed to update container ${collectionId}`, "UpdateCollection");
|
handleError(error, "UpdateCollection", `Failed to update container ${collectionId}`);
|
||||||
throw error;
|
throw error;
|
||||||
} finally {
|
} finally {
|
||||||
clearMessage();
|
clearMessage();
|
||||||
|
|||||||
@@ -72,7 +72,7 @@ export const updateOffer = async (params: UpdateOfferParams): Promise<Offer> =>
|
|||||||
logConsoleInfo(`Successfully updated offer for ${offerResourceText}`);
|
logConsoleInfo(`Successfully updated offer for ${offerResourceText}`);
|
||||||
return updatedOffer;
|
return updatedOffer;
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
handleError(error, `Error updating offer for ${offerResourceText}`, "UpdateCollection");
|
handleError(error, "UpdateCollection", `Error updating offer for ${offerResourceText}`);
|
||||||
throw error;
|
throw error;
|
||||||
} finally {
|
} finally {
|
||||||
clearMessage();
|
clearMessage();
|
||||||
|
|||||||
@@ -1,25 +0,0 @@
|
|||||||
import { updateOfferThroughputBeyondLimit } from "./updateOfferThroughputBeyondLimit";
|
|
||||||
|
|
||||||
describe("updateOfferThroughputBeyondLimit", () => {
|
|
||||||
it("should call fetch", async () => {
|
|
||||||
window.fetch = jest.fn(() => {
|
|
||||||
return {
|
|
||||||
ok: true
|
|
||||||
};
|
|
||||||
});
|
|
||||||
window.dataExplorer = {
|
|
||||||
logConsoleData: jest.fn(),
|
|
||||||
deleteInProgressConsoleDataWithId: jest.fn()
|
|
||||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
||||||
} as any;
|
|
||||||
await updateOfferThroughputBeyondLimit({
|
|
||||||
subscriptionId: "foo",
|
|
||||||
resourceGroup: "foo",
|
|
||||||
databaseAccountName: "foo",
|
|
||||||
databaseName: "foo",
|
|
||||||
throughput: 1000000000,
|
|
||||||
offerIsRUPerMinuteThroughputEnabled: false
|
|
||||||
});
|
|
||||||
expect(window.fetch).toHaveBeenCalled();
|
|
||||||
});
|
|
||||||
});
|
|
||||||
@@ -1,51 +0,0 @@
|
|||||||
import { Platform, configContext } from "../../ConfigContext";
|
|
||||||
import { getAuthorizationHeader } from "../../Utils/AuthorizationUtils";
|
|
||||||
import { AutoPilotOfferSettings } from "../../Contracts/DataModels";
|
|
||||||
import { logConsoleProgress, logConsoleInfo, logConsoleError } from "../../Utils/NotificationConsoleUtils";
|
|
||||||
import { HttpHeaders } from "../Constants";
|
|
||||||
|
|
||||||
interface UpdateOfferThroughputRequest {
|
|
||||||
subscriptionId: string;
|
|
||||||
resourceGroup: string;
|
|
||||||
databaseAccountName: string;
|
|
||||||
databaseName: string;
|
|
||||||
collectionName?: string;
|
|
||||||
throughput: number;
|
|
||||||
offerIsRUPerMinuteThroughputEnabled: boolean;
|
|
||||||
offerAutopilotSettings?: AutoPilotOfferSettings;
|
|
||||||
}
|
|
||||||
|
|
||||||
export async function updateOfferThroughputBeyondLimit(request: UpdateOfferThroughputRequest): Promise<void> {
|
|
||||||
if (configContext.platform !== Platform.Portal) {
|
|
||||||
throw new Error("Updating throughput beyond specified limit is not supported on this platform");
|
|
||||||
}
|
|
||||||
|
|
||||||
const resourceDescriptionInfo = request.collectionName
|
|
||||||
? `database ${request.databaseName} and container ${request.collectionName}`
|
|
||||||
: `database ${request.databaseName}`;
|
|
||||||
|
|
||||||
const clearMessage = logConsoleProgress(
|
|
||||||
`Requesting increase in throughput to ${request.throughput} for ${resourceDescriptionInfo}`
|
|
||||||
);
|
|
||||||
|
|
||||||
const url = `${configContext.BACKEND_ENDPOINT}/api/offerthroughputrequest/updatebeyondspecifiedlimit`;
|
|
||||||
const authorizationHeader = getAuthorizationHeader();
|
|
||||||
|
|
||||||
const response = await fetch(url, {
|
|
||||||
method: "POST",
|
|
||||||
body: JSON.stringify(request),
|
|
||||||
headers: { [authorizationHeader.header]: authorizationHeader.token, [HttpHeaders.contentType]: "application/json" }
|
|
||||||
});
|
|
||||||
|
|
||||||
if (response.ok) {
|
|
||||||
logConsoleInfo(
|
|
||||||
`Successfully requested an increase in throughput to ${request.throughput} for ${resourceDescriptionInfo}`
|
|
||||||
);
|
|
||||||
clearMessage();
|
|
||||||
return undefined;
|
|
||||||
}
|
|
||||||
const error = await response.json();
|
|
||||||
logConsoleError(`Failed to request an increase in throughput for ${request.throughput}: ${error.message}`);
|
|
||||||
clearMessage();
|
|
||||||
throw new Error(error.message);
|
|
||||||
}
|
|
||||||
@@ -64,7 +64,7 @@ export async function updateStoredProcedure(
|
|||||||
.replace(storedProcedure);
|
.replace(storedProcedure);
|
||||||
return response?.resource;
|
return response?.resource;
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
handleError(error, `Error while updating stored procedure ${storedProcedure.id}`, "UpdateStoredProcedure");
|
handleError(error, "UpdateStoredProcedure", `Error while updating stored procedure ${storedProcedure.id}`);
|
||||||
throw error;
|
throw error;
|
||||||
} finally {
|
} finally {
|
||||||
clearMessage();
|
clearMessage();
|
||||||
|
|||||||
@@ -61,7 +61,7 @@ export async function updateTrigger(
|
|||||||
.replace(trigger);
|
.replace(trigger);
|
||||||
return response?.resource;
|
return response?.resource;
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
handleError(error, `Error while updating trigger ${trigger.id}`, "UpdateTrigger");
|
handleError(error, "UpdateTrigger", `Error while updating trigger ${trigger.id}`);
|
||||||
throw error;
|
throw error;
|
||||||
} finally {
|
} finally {
|
||||||
clearMessage();
|
clearMessage();
|
||||||
|
|||||||
@@ -66,8 +66,8 @@ export async function updateUserDefinedFunction(
|
|||||||
} catch (error) {
|
} catch (error) {
|
||||||
handleError(
|
handleError(
|
||||||
error,
|
error,
|
||||||
`Error while updating user defined function ${userDefinedFunction.id}`,
|
"UpdateUserupdateUserDefinedFunction",
|
||||||
"UpdateUserupdateUserDefinedFunction"
|
`Error while updating user defined function ${userDefinedFunction.id}`
|
||||||
);
|
);
|
||||||
throw error;
|
throw error;
|
||||||
} finally {
|
} finally {
|
||||||
|
|||||||
@@ -191,18 +191,6 @@ export interface OfferWithHeaders extends Offer {
|
|||||||
headers: any;
|
headers: any;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface CollectionQuotaInfo {
|
|
||||||
storedProcedures: number;
|
|
||||||
triggers: number;
|
|
||||||
functions: number;
|
|
||||||
documentsSize: number;
|
|
||||||
collectionSize: number;
|
|
||||||
documentsCount: number;
|
|
||||||
usageSizeInKB: number;
|
|
||||||
numPartitions: number;
|
|
||||||
uniqueKeyPolicy?: UniqueKeyPolicy; // TODO: This should ideally not be a part of the collection quota. Remove after refactoring. (#119617)
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface OfferThroughputInfo {
|
export interface OfferThroughputInfo {
|
||||||
minimumRUForCollection: number;
|
minimumRUForCollection: number;
|
||||||
numPhysicalPartitions: number;
|
numPhysicalPartitions: number;
|
||||||
@@ -216,18 +204,6 @@ export interface UniqueKey {
|
|||||||
paths: string[];
|
paths: string[];
|
||||||
}
|
}
|
||||||
|
|
||||||
// Returned by DocumentDb client proxy
|
|
||||||
// Inner errors in BackendErrorDataModel when error is in SQL syntax
|
|
||||||
export interface ErrorDataModel {
|
|
||||||
message: string;
|
|
||||||
severity?: string;
|
|
||||||
location?: {
|
|
||||||
start: string;
|
|
||||||
end: string;
|
|
||||||
};
|
|
||||||
code?: string;
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface CreateDatabaseAndCollectionRequest {
|
export interface CreateDatabaseAndCollectionRequest {
|
||||||
databaseId: string;
|
databaseId: string;
|
||||||
collectionId: string;
|
collectionId: string;
|
||||||
|
|||||||
@@ -46,7 +46,7 @@ export interface LogEntry {
|
|||||||
/**
|
/**
|
||||||
* The message code.
|
* The message code.
|
||||||
*/
|
*/
|
||||||
code?: number;
|
code?: number | string;
|
||||||
/**
|
/**
|
||||||
* Any additional data to be logged.
|
* Any additional data to be logged.
|
||||||
*/
|
*/
|
||||||
|
|||||||
@@ -117,7 +117,6 @@ export interface Collection extends CollectionBase {
|
|||||||
analyticalStorageTtl: ko.Observable<number>;
|
analyticalStorageTtl: ko.Observable<number>;
|
||||||
indexingPolicy: ko.Observable<DataModels.IndexingPolicy>;
|
indexingPolicy: ko.Observable<DataModels.IndexingPolicy>;
|
||||||
uniqueKeyPolicy: DataModels.UniqueKeyPolicy;
|
uniqueKeyPolicy: DataModels.UniqueKeyPolicy;
|
||||||
quotaInfo: ko.Observable<DataModels.CollectionQuotaInfo>;
|
|
||||||
offer: ko.Observable<DataModels.Offer>;
|
offer: ko.Observable<DataModels.Offer>;
|
||||||
conflictResolutionPolicy: ko.Observable<DataModels.ConflictResolutionPolicy>;
|
conflictResolutionPolicy: ko.Observable<DataModels.ConflictResolutionPolicy>;
|
||||||
changeFeedPolicy: ko.Observable<DataModels.ChangeFeedPolicy>;
|
changeFeedPolicy: ko.Observable<DataModels.ChangeFeedPolicy>;
|
||||||
|
|||||||
@@ -1,12 +1,9 @@
|
|||||||
import * as React from "react";
|
import * as React from "react";
|
||||||
import { ArcadiaWorkspace, SparkPool } from "../../../Contracts/DataModels";
|
import { ArcadiaWorkspace, SparkPool } from "../../../Contracts/DataModels";
|
||||||
import { DefaultButton, IButtonStyles } from "office-ui-fabric-react/lib/Button";
|
import { DefaultButton, IButtonStyles } from "office-ui-fabric-react/lib/Button";
|
||||||
import {
|
import { IContextualMenuItem, IContextualMenuProps } from "office-ui-fabric-react/lib/ContextualMenu";
|
||||||
IContextualMenuItem,
|
|
||||||
IContextualMenuProps,
|
|
||||||
ContextualMenuItemType
|
|
||||||
} from "office-ui-fabric-react/lib/ContextualMenu";
|
|
||||||
import * as Logger from "../../../Common/Logger";
|
import * as Logger from "../../../Common/Logger";
|
||||||
|
import { getErrorMessage } from "../../../Common/ErrorHandlingUtils";
|
||||||
|
|
||||||
export interface ArcadiaMenuPickerProps {
|
export interface ArcadiaMenuPickerProps {
|
||||||
selectText?: string;
|
selectText?: string;
|
||||||
@@ -47,7 +44,7 @@ export class ArcadiaMenuPicker extends React.Component<ArcadiaMenuPickerProps, A
|
|||||||
selectedSparkPool: item.text
|
selectedSparkPool: item.text
|
||||||
});
|
});
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
Logger.logError(error, "ArcadiaMenuPicker/_onSparkPoolClicked");
|
Logger.logError(getErrorMessage(error), "ArcadiaMenuPicker/_onSparkPoolClicked");
|
||||||
throw error;
|
throw error;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -4,12 +4,10 @@
|
|||||||
|
|
||||||
import * as React from "react";
|
import * as React from "react";
|
||||||
import * as DataModels from "../../../Contracts/DataModels";
|
import * as DataModels from "../../../Contracts/DataModels";
|
||||||
import * as Logger from "../../../Common/Logger";
|
|
||||||
import * as NotificationConsoleUtils from "../../../Utils/NotificationConsoleUtils";
|
|
||||||
import { ConsoleDataType } from "../../Menus/NotificationConsole/NotificationConsoleComponent";
|
|
||||||
import { StringUtils } from "../../../Utils/StringUtils";
|
import { StringUtils } from "../../../Utils/StringUtils";
|
||||||
import { userContext } from "../../../UserContext";
|
import { userContext } from "../../../UserContext";
|
||||||
import { TerminalQueryParams } from "../../../Common/Constants";
|
import { TerminalQueryParams } from "../../../Common/Constants";
|
||||||
|
import { handleError } from "../../../Common/ErrorHandlingUtils";
|
||||||
|
|
||||||
export interface NotebookTerminalComponentProps {
|
export interface NotebookTerminalComponentProps {
|
||||||
notebookServerInfo: DataModels.NotebookWorkspaceConnectionInfo;
|
notebookServerInfo: DataModels.NotebookWorkspaceConnectionInfo;
|
||||||
@@ -71,9 +69,10 @@ export class NotebookTerminalComponent extends React.Component<NotebookTerminalC
|
|||||||
params: Map<string, string>
|
params: Map<string, string>
|
||||||
): string {
|
): string {
|
||||||
if (!serverInfo.notebookServerEndpoint) {
|
if (!serverInfo.notebookServerEndpoint) {
|
||||||
const error = "Notebook server endpoint not defined. Terminal will fail to connect to jupyter server.";
|
handleError(
|
||||||
Logger.logError(error, "NotebookTerminalComponent/createNotebookAppSrc");
|
"Notebook server endpoint not defined. Terminal will fail to connect to jupyter server.",
|
||||||
NotificationConsoleUtils.logConsoleMessage(ConsoleDataType.Error, error);
|
"NotebookTerminalComponent/createNotebookAppSrc"
|
||||||
|
);
|
||||||
return "";
|
return "";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,9 +1,8 @@
|
|||||||
import * as React from "react";
|
import * as React from "react";
|
||||||
import { JunoClient } from "../../../Juno/JunoClient";
|
import { JunoClient } from "../../../Juno/JunoClient";
|
||||||
import { HttpStatusCodes, CodeOfConductEndpoints } from "../../../Common/Constants";
|
import { HttpStatusCodes, CodeOfConductEndpoints } from "../../../Common/Constants";
|
||||||
import * as Logger from "../../../Common/Logger";
|
|
||||||
import { logConsoleError } from "../../../Utils/NotificationConsoleUtils";
|
|
||||||
import { Stack, Text, Checkbox, PrimaryButton, Link } from "office-ui-fabric-react";
|
import { Stack, Text, Checkbox, PrimaryButton, Link } from "office-ui-fabric-react";
|
||||||
|
import { handleError } from "../../../Common/ErrorHandlingUtils";
|
||||||
|
|
||||||
export interface CodeOfConductComponentProps {
|
export interface CodeOfConductComponentProps {
|
||||||
junoClient: JunoClient;
|
junoClient: JunoClient;
|
||||||
@@ -45,9 +44,7 @@ export class CodeOfConductComponent extends React.Component<CodeOfConductCompone
|
|||||||
|
|
||||||
this.props.onAcceptCodeOfConduct(response.data);
|
this.props.onAcceptCodeOfConduct(response.data);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
const message = `Failed to accept code of conduct: ${error}`;
|
handleError(error, "CodeOfConductComponent/acceptCodeOfConduct", "Failed to accept code of conduct");
|
||||||
Logger.logError(message, "CodeOfConductComponent/acceptCodeOfConduct");
|
|
||||||
logConsoleError(message);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -16,11 +16,8 @@ import {
|
|||||||
Text
|
Text
|
||||||
} from "office-ui-fabric-react";
|
} from "office-ui-fabric-react";
|
||||||
import * as React from "react";
|
import * as React from "react";
|
||||||
import * as Logger from "../../../Common/Logger";
|
|
||||||
import { IGalleryItem, JunoClient, IJunoResponse, IPublicGalleryData } from "../../../Juno/JunoClient";
|
import { IGalleryItem, JunoClient, IJunoResponse, IPublicGalleryData } from "../../../Juno/JunoClient";
|
||||||
import * as GalleryUtils from "../../../Utils/GalleryUtils";
|
import * as GalleryUtils from "../../../Utils/GalleryUtils";
|
||||||
import * as NotificationConsoleUtils from "../../../Utils/NotificationConsoleUtils";
|
|
||||||
import { ConsoleDataType } from "../../Menus/NotificationConsole/NotificationConsoleComponent";
|
|
||||||
import { DialogComponent, DialogProps } from "../DialogReactComponent/DialogComponent";
|
import { DialogComponent, DialogProps } from "../DialogReactComponent/DialogComponent";
|
||||||
import { GalleryCardComponent, GalleryCardComponentProps } from "./Cards/GalleryCardComponent";
|
import { GalleryCardComponent, GalleryCardComponentProps } from "./Cards/GalleryCardComponent";
|
||||||
import "./GalleryViewerComponent.less";
|
import "./GalleryViewerComponent.less";
|
||||||
@@ -28,6 +25,7 @@ import { HttpStatusCodes } from "../../../Common/Constants";
|
|||||||
import Explorer from "../../Explorer";
|
import Explorer from "../../Explorer";
|
||||||
import { CodeOfConductComponent } from "./CodeOfConductComponent";
|
import { CodeOfConductComponent } from "./CodeOfConductComponent";
|
||||||
import { InfoComponent } from "./InfoComponent/InfoComponent";
|
import { InfoComponent } from "./InfoComponent/InfoComponent";
|
||||||
|
import { handleError } from "../../../Common/ErrorHandlingUtils";
|
||||||
|
|
||||||
export interface GalleryViewerComponentProps {
|
export interface GalleryViewerComponentProps {
|
||||||
container?: Explorer;
|
container?: Explorer;
|
||||||
@@ -354,9 +352,7 @@ export class GalleryViewerComponent extends React.Component<GalleryViewerCompone
|
|||||||
|
|
||||||
this.sampleNotebooks = response.data;
|
this.sampleNotebooks = response.data;
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
const message = `Failed to load sample notebooks: ${error}`;
|
handleError(error, "GalleryViewerComponent/loadSampleNotebooks", "Failed to load sample notebooks");
|
||||||
Logger.logError(message, "GalleryViewerComponent/loadSampleNotebooks");
|
|
||||||
NotificationConsoleUtils.logConsoleMessage(ConsoleDataType.Error, message);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -382,9 +378,7 @@ export class GalleryViewerComponent extends React.Component<GalleryViewerCompone
|
|||||||
throw new Error(`Received HTTP ${response.status} when loading public notebooks`);
|
throw new Error(`Received HTTP ${response.status} when loading public notebooks`);
|
||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
const message = `Failed to load public notebooks: ${error}`;
|
handleError(error, "GalleryViewerComponent/loadPublicNotebooks", "Failed to load public notebooks");
|
||||||
Logger.logError(message, "GalleryViewerComponent/loadPublicNotebooks");
|
|
||||||
NotificationConsoleUtils.logConsoleMessage(ConsoleDataType.Error, message);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -404,9 +398,7 @@ export class GalleryViewerComponent extends React.Component<GalleryViewerCompone
|
|||||||
|
|
||||||
this.favoriteNotebooks = response.data;
|
this.favoriteNotebooks = response.data;
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
const message = `Failed to load favorite notebooks: ${error}`;
|
handleError(error, "GalleryViewerComponent/loadFavoriteNotebooks", "Failed to load favorite notebooks");
|
||||||
Logger.logError(message, "GalleryViewerComponent/loadFavoriteNotebooks");
|
|
||||||
NotificationConsoleUtils.logConsoleMessage(ConsoleDataType.Error, message);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -432,9 +424,7 @@ export class GalleryViewerComponent extends React.Component<GalleryViewerCompone
|
|||||||
|
|
||||||
this.publishedNotebooks = response.data;
|
this.publishedNotebooks = response.data;
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
const message = `Failed to load published notebooks: ${error}`;
|
handleError(error, "GalleryViewerComponent/loadPublishedNotebooks", "Failed to load published notebooks");
|
||||||
Logger.logError(message, "GalleryViewerComponent/loadPublishedNotebooks");
|
|
||||||
NotificationConsoleUtils.logConsoleMessage(ConsoleDataType.Error, message);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -21,6 +21,7 @@ import Explorer from "../../Explorer";
|
|||||||
import { NotebookV4 } from "@nteract/commutable/lib/v4";
|
import { NotebookV4 } from "@nteract/commutable/lib/v4";
|
||||||
import { SessionStorageUtility } from "../../../Shared/StorageUtility";
|
import { SessionStorageUtility } from "../../../Shared/StorageUtility";
|
||||||
import { DialogHost } from "../../../Utils/GalleryUtils";
|
import { DialogHost } from "../../../Utils/GalleryUtils";
|
||||||
|
import { getErrorMessage, handleError } from "../../../Common/ErrorHandlingUtils";
|
||||||
|
|
||||||
export interface NotebookViewerComponentProps {
|
export interface NotebookViewerComponentProps {
|
||||||
container?: Explorer;
|
container?: Explorer;
|
||||||
@@ -100,9 +101,7 @@ export class NotebookViewerComponent extends React.Component<NotebookViewerCompo
|
|||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
this.setState({ showProgressBar: false });
|
this.setState({ showProgressBar: false });
|
||||||
const message = `Failed to load notebook content: ${error}`;
|
handleError(error, "NotebookViewerComponent/loadNotebookContent", "Failed to load notebook content");
|
||||||
Logger.logError(message, "NotebookViewerComponent/loadNotebookContent");
|
|
||||||
NotificationConsoleUtils.logConsoleMessage(ConsoleDataType.Error, message);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -28,6 +28,7 @@ import * as TelemetryProcessor from "../../../Shared/Telemetry/TelemetryProcesso
|
|||||||
|
|
||||||
import SaveQueryBannerIcon from "../../../../images/save_query_banner.png";
|
import SaveQueryBannerIcon from "../../../../images/save_query_banner.png";
|
||||||
import { QueriesClient } from "../../../Common/QueriesClient";
|
import { QueriesClient } from "../../../Common/QueriesClient";
|
||||||
|
import { getErrorMessage, getErrorStack } from "../../../Common/ErrorHandlingUtils";
|
||||||
|
|
||||||
export interface QueriesGridComponentProps {
|
export interface QueriesGridComponentProps {
|
||||||
queriesClient: QueriesClient;
|
queriesClient: QueriesClient;
|
||||||
@@ -244,7 +245,9 @@ export class QueriesGridComponent extends React.Component<QueriesGridComponentPr
|
|||||||
databaseAccountName: container && container.databaseAccount().name,
|
databaseAccountName: container && container.databaseAccount().name,
|
||||||
defaultExperience: container && container.defaultExperience(),
|
defaultExperience: container && container.defaultExperience(),
|
||||||
dataExplorerArea: Constants.Areas.ContextualPane,
|
dataExplorerArea: Constants.Areas.ContextualPane,
|
||||||
paneTitle: container && container.browseQueriesPane.title()
|
paneTitle: container && container.browseQueriesPane.title(),
|
||||||
|
error: getErrorMessage(error),
|
||||||
|
errorStack: getErrorStack(error)
|
||||||
},
|
},
|
||||||
startKey
|
startKey
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -8,8 +8,8 @@ import * as DataModels from "../../../Contracts/DataModels";
|
|||||||
import ko from "knockout";
|
import ko from "knockout";
|
||||||
import { TtlType, isDirty } from "./SettingsUtils";
|
import { TtlType, isDirty } from "./SettingsUtils";
|
||||||
import Explorer from "../../Explorer";
|
import Explorer from "../../Explorer";
|
||||||
jest.mock("../../../Common/dataAccess/readMongoDBCollection", () => ({
|
jest.mock("../../../Common/dataAccess/getIndexTransformationProgress", () => ({
|
||||||
getMongoDBCollectionIndexTransformationProgress: jest.fn().mockReturnValue(undefined)
|
getIndexTransformationProgress: jest.fn().mockReturnValue(undefined)
|
||||||
}));
|
}));
|
||||||
import { updateCollection, updateMongoDBCollectionThroughRP } from "../../../Common/dataAccess/updateCollection";
|
import { updateCollection, updateMongoDBCollectionThroughRP } from "../../../Common/dataAccess/updateCollection";
|
||||||
jest.mock("../../../Common/dataAccess/updateCollection", () => ({
|
jest.mock("../../../Common/dataAccess/updateCollection", () => ({
|
||||||
|
|||||||
@@ -1,55 +1,49 @@
|
|||||||
|
import { RequestOptions } from "@azure/cosmos/dist-esm";
|
||||||
|
import { IPivotItemProps, IPivotProps, Pivot, PivotItem } from "office-ui-fabric-react";
|
||||||
import * as React from "react";
|
import * as React from "react";
|
||||||
import * as AutoPilotUtils from "../../../Utils/AutoPilotUtils";
|
|
||||||
import * as Constants from "../../../Common/Constants";
|
|
||||||
import * as DataModels from "../../../Contracts/DataModels";
|
|
||||||
import * as SharedConstants from "../../../Shared/Constants";
|
|
||||||
import * as ViewModels from "../../../Contracts/ViewModels";
|
|
||||||
import DiscardIcon from "../../../../images/discard.svg";
|
import DiscardIcon from "../../../../images/discard.svg";
|
||||||
import SaveIcon from "../../../../images/save-cosmos.svg";
|
import SaveIcon from "../../../../images/save-cosmos.svg";
|
||||||
import { traceStart, traceFailure, traceSuccess, trace } from "../../../Shared/Telemetry/TelemetryProcessor";
|
import * as Constants from "../../../Common/Constants";
|
||||||
import { Action, ActionModifiers } from "../../../Shared/Telemetry/TelemetryConstants";
|
import { getIndexTransformationProgress } from "../../../Common/dataAccess/getIndexTransformationProgress";
|
||||||
import { RequestOptions } from "@azure/cosmos/dist-esm";
|
import { readMongoDBCollectionThroughRP } from "../../../Common/dataAccess/readMongoDBCollection";
|
||||||
import Explorer from "../../Explorer";
|
|
||||||
import { updateOffer } from "../../../Common/dataAccess/updateOffer";
|
|
||||||
import { updateCollection, updateMongoDBCollectionThroughRP } from "../../../Common/dataAccess/updateCollection";
|
import { updateCollection, updateMongoDBCollectionThroughRP } from "../../../Common/dataAccess/updateCollection";
|
||||||
|
import { updateOffer } from "../../../Common/dataAccess/updateOffer";
|
||||||
|
import { getErrorMessage, getErrorStack } from "../../../Common/ErrorHandlingUtils";
|
||||||
|
import * as DataModels from "../../../Contracts/DataModels";
|
||||||
|
import * as ViewModels from "../../../Contracts/ViewModels";
|
||||||
|
import { Action, ActionModifiers } from "../../../Shared/Telemetry/TelemetryConstants";
|
||||||
|
import { trace, traceFailure, traceStart, traceSuccess } from "../../../Shared/Telemetry/TelemetryProcessor";
|
||||||
|
import { MongoDBCollectionResource, MongoIndex } from "../../../Utils/arm/generatedClients/2020-04-01/types";
|
||||||
|
import * as AutoPilotUtils from "../../../Utils/AutoPilotUtils";
|
||||||
import { CommandButtonComponentProps } from "../../Controls/CommandButton/CommandButtonComponent";
|
import { CommandButtonComponentProps } from "../../Controls/CommandButton/CommandButtonComponent";
|
||||||
import { userContext } from "../../../UserContext";
|
import Explorer from "../../Explorer";
|
||||||
import { updateOfferThroughputBeyondLimit } from "../../../Common/dataAccess/updateOfferThroughputBeyondLimit";
|
|
||||||
import SettingsTab from "../../Tabs/SettingsTabV2";
|
import SettingsTab from "../../Tabs/SettingsTabV2";
|
||||||
import { throughputUnit } from "./SettingsRenderUtils";
|
import "./SettingsComponent.less";
|
||||||
import { ScaleComponent, ScaleComponentProps } from "./SettingsSubComponents/ScaleComponent";
|
|
||||||
import {
|
|
||||||
MongoIndexingPolicyComponent,
|
|
||||||
MongoIndexingPolicyComponentProps
|
|
||||||
} from "./SettingsSubComponents/MongoIndexingPolicy/MongoIndexingPolicyComponent";
|
|
||||||
import {
|
|
||||||
getMaxRUs,
|
|
||||||
hasDatabaseSharedThroughput,
|
|
||||||
GeospatialConfigType,
|
|
||||||
TtlType,
|
|
||||||
ChangeFeedPolicyState,
|
|
||||||
SettingsV2TabTypes,
|
|
||||||
getTabTitle,
|
|
||||||
isDirty,
|
|
||||||
AddMongoIndexProps,
|
|
||||||
MongoIndexTypes,
|
|
||||||
parseConflictResolutionMode,
|
|
||||||
parseConflictResolutionProcedure,
|
|
||||||
getMongoNotification
|
|
||||||
} from "./SettingsUtils";
|
|
||||||
import {
|
import {
|
||||||
ConflictResolutionComponent,
|
ConflictResolutionComponent,
|
||||||
ConflictResolutionComponentProps
|
ConflictResolutionComponentProps
|
||||||
} from "./SettingsSubComponents/ConflictResolutionComponent";
|
} from "./SettingsSubComponents/ConflictResolutionComponent";
|
||||||
import { SubSettingsComponent, SubSettingsComponentProps } from "./SettingsSubComponents/SubSettingsComponent";
|
|
||||||
import { Pivot, PivotItem, IPivotProps, IPivotItemProps } from "office-ui-fabric-react";
|
|
||||||
import "./SettingsComponent.less";
|
|
||||||
import { IndexingPolicyComponent, IndexingPolicyComponentProps } from "./SettingsSubComponents/IndexingPolicyComponent";
|
import { IndexingPolicyComponent, IndexingPolicyComponentProps } from "./SettingsSubComponents/IndexingPolicyComponent";
|
||||||
import { MongoDBCollectionResource, MongoIndex } from "../../../Utils/arm/generatedClients/2020-04-01/types";
|
|
||||||
import {
|
import {
|
||||||
getMongoDBCollectionIndexTransformationProgress,
|
MongoIndexingPolicyComponent,
|
||||||
readMongoDBCollectionThroughRP
|
MongoIndexingPolicyComponentProps
|
||||||
} from "../../../Common/dataAccess/readMongoDBCollection";
|
} from "./SettingsSubComponents/MongoIndexingPolicy/MongoIndexingPolicyComponent";
|
||||||
|
import { ScaleComponent, ScaleComponentProps } from "./SettingsSubComponents/ScaleComponent";
|
||||||
|
import { SubSettingsComponent, SubSettingsComponentProps } from "./SettingsSubComponents/SubSettingsComponent";
|
||||||
|
import {
|
||||||
|
AddMongoIndexProps,
|
||||||
|
ChangeFeedPolicyState,
|
||||||
|
GeospatialConfigType,
|
||||||
|
getMongoNotification,
|
||||||
|
getTabTitle,
|
||||||
|
hasDatabaseSharedThroughput,
|
||||||
|
isDirty,
|
||||||
|
MongoIndexTypes,
|
||||||
|
parseConflictResolutionMode,
|
||||||
|
parseConflictResolutionProcedure,
|
||||||
|
SettingsV2TabTypes,
|
||||||
|
TtlType
|
||||||
|
} from "./SettingsUtils";
|
||||||
|
|
||||||
interface SettingsV2TabInfo {
|
interface SettingsV2TabInfo {
|
||||||
tab: SettingsV2TabTypes;
|
tab: SettingsV2TabTypes;
|
||||||
@@ -211,6 +205,7 @@ export class SettingsComponent extends React.Component<SettingsComponentProps, S
|
|||||||
}
|
}
|
||||||
|
|
||||||
componentDidMount(): void {
|
componentDidMount(): void {
|
||||||
|
this.refreshIndexTransformationProgress();
|
||||||
this.loadMongoIndexes();
|
this.loadMongoIndexes();
|
||||||
this.setAutoPilotStates();
|
this.setAutoPilotStates();
|
||||||
this.setBaseline();
|
this.setBaseline();
|
||||||
@@ -232,8 +227,6 @@ export class SettingsComponent extends React.Component<SettingsComponentProps, S
|
|||||||
this.container.isEnableMongoCapabilityPresent() &&
|
this.container.isEnableMongoCapabilityPresent() &&
|
||||||
this.container.databaseAccount()
|
this.container.databaseAccount()
|
||||||
) {
|
) {
|
||||||
await this.refreshIndexTransformationProgress();
|
|
||||||
|
|
||||||
this.mongoDBCollectionResource = await readMongoDBCollectionThroughRP(
|
this.mongoDBCollectionResource = await readMongoDBCollectionThroughRP(
|
||||||
this.collection.databaseId,
|
this.collection.databaseId,
|
||||||
this.collection.id()
|
this.collection.id()
|
||||||
@@ -248,10 +241,7 @@ export class SettingsComponent extends React.Component<SettingsComponentProps, S
|
|||||||
};
|
};
|
||||||
|
|
||||||
public refreshIndexTransformationProgress = async (): Promise<void> => {
|
public refreshIndexTransformationProgress = async (): Promise<void> => {
|
||||||
const currentProgress = await getMongoDBCollectionIndexTransformationProgress(
|
const currentProgress = await getIndexTransformationProgress(this.collection.databaseId, this.collection.id());
|
||||||
this.collection.databaseId,
|
|
||||||
this.collection.id()
|
|
||||||
);
|
|
||||||
this.setState({ indexTransformationProgress: currentProgress });
|
this.setState({ indexTransformationProgress: currentProgress });
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -351,6 +341,7 @@ export class SettingsComponent extends React.Component<SettingsComponentProps, S
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const wasIndexingPolicyModified = this.state.isIndexingPolicyDirty;
|
||||||
newCollection.defaultTtl = defaultTtl;
|
newCollection.defaultTtl = defaultTtl;
|
||||||
|
|
||||||
newCollection.indexingPolicy = this.state.indexingPolicyContent;
|
newCollection.indexingPolicy = this.state.indexingPolicyContent;
|
||||||
@@ -386,6 +377,11 @@ export class SettingsComponent extends React.Component<SettingsComponentProps, S
|
|||||||
this.collection.conflictResolutionPolicy(updatedCollection.conflictResolutionPolicy);
|
this.collection.conflictResolutionPolicy(updatedCollection.conflictResolutionPolicy);
|
||||||
this.collection.changeFeedPolicy(updatedCollection.changeFeedPolicy);
|
this.collection.changeFeedPolicy(updatedCollection.changeFeedPolicy);
|
||||||
this.collection.geospatialConfig(updatedCollection.geospatialConfig);
|
this.collection.geospatialConfig(updatedCollection.geospatialConfig);
|
||||||
|
|
||||||
|
if (wasIndexingPolicyModified) {
|
||||||
|
await this.refreshIndexTransformationProgress();
|
||||||
|
}
|
||||||
|
|
||||||
this.setState({
|
this.setState({
|
||||||
isSubSettingsSaveable: false,
|
isSubSettingsSaveable: false,
|
||||||
isSubSettingsDiscardable: false,
|
isSubSettingsDiscardable: false,
|
||||||
@@ -437,7 +433,8 @@ export class SettingsComponent extends React.Component<SettingsComponentProps, S
|
|||||||
defaultExperience: this.container.defaultExperience(),
|
defaultExperience: this.container.defaultExperience(),
|
||||||
dataExplorerArea: Constants.Areas.Tab,
|
dataExplorerArea: Constants.Areas.Tab,
|
||||||
tabTitle: this.props.settingsTab.tabTitle(),
|
tabTitle: this.props.settingsTab.tabTitle(),
|
||||||
error: error.message
|
error: getErrorMessage(error),
|
||||||
|
errorStack: getErrorStack(error)
|
||||||
},
|
},
|
||||||
startKey
|
startKey
|
||||||
);
|
);
|
||||||
@@ -448,7 +445,6 @@ export class SettingsComponent extends React.Component<SettingsComponentProps, S
|
|||||||
if (this.state.isScaleSaveable) {
|
if (this.state.isScaleSaveable) {
|
||||||
const newThroughput = this.state.throughput;
|
const newThroughput = this.state.throughput;
|
||||||
const newOffer: DataModels.Offer = { ...this.collection.offer() };
|
const newOffer: DataModels.Offer = { ...this.collection.offer() };
|
||||||
const originalThroughputValue: number = this.state.throughput;
|
|
||||||
|
|
||||||
if (newOffer.content) {
|
if (newOffer.content) {
|
||||||
newOffer.content.offerThroughput = newThroughput;
|
newOffer.content.offerThroughput = newThroughput;
|
||||||
@@ -486,62 +482,33 @@ export class SettingsComponent extends React.Component<SettingsComponentProps, S
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (
|
const updateOfferParams: DataModels.UpdateOfferParams = {
|
||||||
getMaxRUs(this.collection, this.container) <=
|
databaseId: this.collection.databaseId,
|
||||||
SharedConstants.CollectionCreation.DefaultCollectionRUs1Million &&
|
collectionId: this.collection.id(),
|
||||||
newThroughput > SharedConstants.CollectionCreation.DefaultCollectionRUs1Million &&
|
currentOffer: this.collection.offer(),
|
||||||
this.container
|
autopilotThroughput: this.state.isAutoPilotSelected ? this.state.autoPilotThroughput : undefined,
|
||||||
) {
|
manualThroughput: this.state.isAutoPilotSelected ? undefined : newThroughput
|
||||||
const requestPayload = {
|
};
|
||||||
subscriptionId: userContext.subscriptionId,
|
if (this.hasProvisioningTypeChanged()) {
|
||||||
databaseAccountName: userContext.databaseAccount.name,
|
if (this.state.isAutoPilotSelected) {
|
||||||
resourceGroup: userContext.resourceGroup,
|
updateOfferParams.migrateToAutoPilot = true;
|
||||||
databaseName: this.collection.databaseId,
|
} else {
|
||||||
collectionName: this.collection.id(),
|
updateOfferParams.migrateToManual = true;
|
||||||
throughput: newThroughput,
|
}
|
||||||
offerIsRUPerMinuteThroughputEnabled: false
|
}
|
||||||
};
|
const updatedOffer: DataModels.Offer = await updateOffer(updateOfferParams);
|
||||||
|
this.collection.offer(updatedOffer);
|
||||||
await updateOfferThroughputBeyondLimit(requestPayload);
|
this.setState({ isScaleSaveable: false, isScaleDiscardable: false });
|
||||||
this.collection.offer().content.offerThroughput = originalThroughputValue;
|
if (this.state.isAutoPilotSelected) {
|
||||||
this.setState({
|
this.setState({
|
||||||
isScaleSaveable: false,
|
autoPilotThroughput: updatedOffer.content.offerAutopilotSettings.maxThroughput,
|
||||||
isScaleDiscardable: false,
|
autoPilotThroughputBaseline: updatedOffer.content.offerAutopilotSettings.maxThroughput
|
||||||
throughput: originalThroughputValue,
|
|
||||||
throughputBaseline: originalThroughputValue,
|
|
||||||
initialNotification: {
|
|
||||||
description: `Throughput update for ${newThroughput} ${throughputUnit}`
|
|
||||||
} as DataModels.Notification
|
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
const updateOfferParams: DataModels.UpdateOfferParams = {
|
this.setState({
|
||||||
databaseId: this.collection.databaseId,
|
throughput: updatedOffer.content.offerThroughput,
|
||||||
collectionId: this.collection.id(),
|
throughputBaseline: updatedOffer.content.offerThroughput
|
||||||
currentOffer: this.collection.offer(),
|
});
|
||||||
autopilotThroughput: this.state.isAutoPilotSelected ? this.state.autoPilotThroughput : undefined,
|
|
||||||
manualThroughput: this.state.isAutoPilotSelected ? undefined : newThroughput
|
|
||||||
};
|
|
||||||
if (this.hasProvisioningTypeChanged()) {
|
|
||||||
if (this.state.isAutoPilotSelected) {
|
|
||||||
updateOfferParams.migrateToAutoPilot = true;
|
|
||||||
} else {
|
|
||||||
updateOfferParams.migrateToManual = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
const updatedOffer: DataModels.Offer = await updateOffer(updateOfferParams);
|
|
||||||
this.collection.offer(updatedOffer);
|
|
||||||
this.setState({ isScaleSaveable: false, isScaleDiscardable: false });
|
|
||||||
if (this.state.isAutoPilotSelected) {
|
|
||||||
this.setState({
|
|
||||||
autoPilotThroughput: updatedOffer.content.offerAutopilotSettings.maxThroughput,
|
|
||||||
autoPilotThroughputBaseline: updatedOffer.content.offerAutopilotSettings.maxThroughput
|
|
||||||
});
|
|
||||||
} else {
|
|
||||||
this.setState({
|
|
||||||
throughput: updatedOffer.content.offerThroughput,
|
|
||||||
throughputBaseline: updatedOffer.content.offerThroughput
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
this.container.isRefreshingExplorer(false);
|
this.container.isRefreshingExplorer(false);
|
||||||
@@ -559,10 +526,10 @@ export class SettingsComponent extends React.Component<SettingsComponentProps, S
|
|||||||
},
|
},
|
||||||
startKey
|
startKey
|
||||||
);
|
);
|
||||||
} catch (reason) {
|
} catch (error) {
|
||||||
this.container.isRefreshingExplorer(false);
|
this.container.isRefreshingExplorer(false);
|
||||||
this.props.settingsTab.isExecutionError(true);
|
this.props.settingsTab.isExecutionError(true);
|
||||||
console.error(reason);
|
console.error(error);
|
||||||
traceFailure(
|
traceFailure(
|
||||||
Action.SettingsV2Updated,
|
Action.SettingsV2Updated,
|
||||||
{
|
{
|
||||||
@@ -572,7 +539,8 @@ export class SettingsComponent extends React.Component<SettingsComponentProps, S
|
|||||||
defaultExperience: this.container.defaultExperience(),
|
defaultExperience: this.container.defaultExperience(),
|
||||||
dataExplorerArea: Constants.Areas.Tab,
|
dataExplorerArea: Constants.Areas.Tab,
|
||||||
tabTitle: this.props.settingsTab.tabTitle(),
|
tabTitle: this.props.settingsTab.tabTitle(),
|
||||||
error: reason.message
|
error: getErrorMessage(error),
|
||||||
|
errorStack: getErrorStack(error)
|
||||||
},
|
},
|
||||||
startKey
|
startKey
|
||||||
);
|
);
|
||||||
@@ -945,6 +913,8 @@ export class SettingsComponent extends React.Component<SettingsComponentProps, S
|
|||||||
indexingPolicyContentBaseline: this.state.indexingPolicyContentBaseline,
|
indexingPolicyContentBaseline: this.state.indexingPolicyContentBaseline,
|
||||||
onIndexingPolicyContentChange: this.onIndexingPolicyContentChange,
|
onIndexingPolicyContentChange: this.onIndexingPolicyContentChange,
|
||||||
logIndexingPolicySuccessMessage: this.logIndexingPolicySuccessMessage,
|
logIndexingPolicySuccessMessage: this.logIndexingPolicySuccessMessage,
|
||||||
|
indexTransformationProgress: this.state.indexTransformationProgress,
|
||||||
|
refreshIndexTransformationProgress: this.refreshIndexTransformationProgress,
|
||||||
onIndexingPolicyDirtyChange: this.onIndexingPolicyDirtyChange
|
onIndexingPolicyDirtyChange: this.onIndexingPolicyDirtyChange
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -6,7 +6,7 @@ import {
|
|||||||
getEstimatedAutoscaleSpendElement,
|
getEstimatedAutoscaleSpendElement,
|
||||||
manualToAutoscaleDisclaimerElement,
|
manualToAutoscaleDisclaimerElement,
|
||||||
ttlWarning,
|
ttlWarning,
|
||||||
indexingPolicyTTLWarningMessage,
|
indexingPolicynUnsavedWarningMessage,
|
||||||
updateThroughputBeyondLimitWarningMessage,
|
updateThroughputBeyondLimitWarningMessage,
|
||||||
updateThroughputDelayedApplyWarningMessage,
|
updateThroughputDelayedApplyWarningMessage,
|
||||||
getThroughputApplyDelayedMessage,
|
getThroughputApplyDelayedMessage,
|
||||||
@@ -37,7 +37,7 @@ class SettingsRenderUtilsTestComponent extends React.Component {
|
|||||||
|
|
||||||
{manualToAutoscaleDisclaimerElement}
|
{manualToAutoscaleDisclaimerElement}
|
||||||
{ttlWarning}
|
{ttlWarning}
|
||||||
{indexingPolicyTTLWarningMessage}
|
{indexingPolicynUnsavedWarningMessage}
|
||||||
{updateThroughputBeyondLimitWarningMessage}
|
{updateThroughputBeyondLimitWarningMessage}
|
||||||
{updateThroughputDelayedApplyWarningMessage}
|
{updateThroughputDelayedApplyWarningMessage}
|
||||||
|
|
||||||
|
|||||||
@@ -36,7 +36,7 @@ import {
|
|||||||
} from "office-ui-fabric-react";
|
} from "office-ui-fabric-react";
|
||||||
import { isDirtyTypes, isDirty } from "./SettingsUtils";
|
import { isDirtyTypes, isDirty } from "./SettingsUtils";
|
||||||
|
|
||||||
const infoAndToolTipTextStyle: ITextStyles = { root: { fontSize: 12 } };
|
export const infoAndToolTipTextStyle: ITextStyles = { root: { fontSize: 12 } };
|
||||||
|
|
||||||
export const noLeftPaddingCheckBoxStyle: ICheckboxStyles = {
|
export const noLeftPaddingCheckBoxStyle: ICheckboxStyles = {
|
||||||
label: {
|
label: {
|
||||||
@@ -245,15 +245,9 @@ export const ttlWarning: JSX.Element = (
|
|||||||
</Text>
|
</Text>
|
||||||
);
|
);
|
||||||
|
|
||||||
export const indexingPolicyTTLWarningMessage: JSX.Element = (
|
export const indexingPolicynUnsavedWarningMessage: JSX.Element = (
|
||||||
<Text styles={infoAndToolTipTextStyle}>
|
<Text styles={infoAndToolTipTextStyle}>
|
||||||
Changing the Indexing Policy impacts query results while the index transformation occurs. When a change is made and
|
You have not saved the latest changes made to your indexing policy. Please click save to confirm the changes.
|
||||||
the indexing mode is set to consistent or lazy, queries return eventual results until the operation completes. For
|
|
||||||
more information see,{" "}
|
|
||||||
<Link target="_blank" href="https://aka.ms/cosmosdb/modify-index-policy">
|
|
||||||
Modifying Indexing Policies
|
|
||||||
</Link>
|
|
||||||
.
|
|
||||||
</Text>
|
</Text>
|
||||||
);
|
);
|
||||||
|
|
||||||
@@ -410,8 +404,8 @@ export const mongoIndexingPolicyAADError: JSX.Element = (
|
|||||||
|
|
||||||
export const mongoIndexTransformationRefreshingMessage: JSX.Element = (
|
export const mongoIndexTransformationRefreshingMessage: JSX.Element = (
|
||||||
<Stack horizontal {...mongoWarningStackProps}>
|
<Stack horizontal {...mongoWarningStackProps}>
|
||||||
<Text>Refreshing index transformation progress</Text>
|
<Text styles={infoAndToolTipTextStyle}>Refreshing index transformation progress</Text>
|
||||||
<Spinner size={SpinnerSize.medium} />
|
<Spinner size={SpinnerSize.small} />
|
||||||
</Stack>
|
</Stack>
|
||||||
);
|
);
|
||||||
|
|
||||||
@@ -421,14 +415,14 @@ export const renderMongoIndexTransformationRefreshMessage = (
|
|||||||
): JSX.Element => {
|
): JSX.Element => {
|
||||||
if (progress === 0) {
|
if (progress === 0) {
|
||||||
return (
|
return (
|
||||||
<Text>
|
<Text styles={infoAndToolTipTextStyle}>
|
||||||
{"You can make more indexing changes once the current index transformation is complete. "}
|
{"You can make more indexing changes once the current index transformation is complete. "}
|
||||||
<Link onClick={performRefresh}>{"Refresh to check if it has completed."}</Link>
|
<Link onClick={performRefresh}>{"Refresh to check if it has completed."}</Link>
|
||||||
</Text>
|
</Text>
|
||||||
);
|
);
|
||||||
} else {
|
} else {
|
||||||
return (
|
return (
|
||||||
<Text>
|
<Text styles={infoAndToolTipTextStyle}>
|
||||||
{`You can make more indexing changes once the current index transformation has completed. It is ${progress}% complete. `}
|
{`You can make more indexing changes once the current index transformation has completed. It is ${progress}% complete. `}
|
||||||
<Link onClick={performRefresh}>{"Refresh to check the progress."}</Link>
|
<Link onClick={performRefresh}>{"Refresh to check the progress."}</Link>
|
||||||
</Text>
|
</Text>
|
||||||
|
|||||||
@@ -25,7 +25,9 @@ describe("IndexingPolicyComponent", () => {
|
|||||||
},
|
},
|
||||||
onIndexingPolicyDirtyChange: () => {
|
onIndexingPolicyDirtyChange: () => {
|
||||||
return;
|
return;
|
||||||
}
|
},
|
||||||
|
indexTransformationProgress: undefined,
|
||||||
|
refreshIndexTransformationProgress: () => new Promise(jest.fn())
|
||||||
};
|
};
|
||||||
|
|
||||||
it("renders", () => {
|
it("renders", () => {
|
||||||
|
|||||||
@@ -1,9 +1,10 @@
|
|||||||
import * as React from "react";
|
import * as React from "react";
|
||||||
import * as DataModels from "../../../../Contracts/DataModels";
|
import * as DataModels from "../../../../Contracts/DataModels";
|
||||||
import * as monaco from "monaco-editor";
|
import * as monaco from "monaco-editor";
|
||||||
import { isDirty } from "../SettingsUtils";
|
import { isDirty, isIndexTransforming } from "../SettingsUtils";
|
||||||
import { MessageBar, MessageBarType, Stack } from "office-ui-fabric-react";
|
import { MessageBar, MessageBarType, Stack } from "office-ui-fabric-react";
|
||||||
import { indexingPolicyTTLWarningMessage, titleAndInputStackProps } from "../SettingsRenderUtils";
|
import { indexingPolicynUnsavedWarningMessage, titleAndInputStackProps } from "../SettingsRenderUtils";
|
||||||
|
import { IndexingPolicyRefreshComponent } from "./IndexingPolicyRefresh/IndexingPolicyRefreshComponent";
|
||||||
|
|
||||||
export interface IndexingPolicyComponentProps {
|
export interface IndexingPolicyComponentProps {
|
||||||
shouldDiscardIndexingPolicy: boolean;
|
shouldDiscardIndexingPolicy: boolean;
|
||||||
@@ -12,6 +13,8 @@ export interface IndexingPolicyComponentProps {
|
|||||||
indexingPolicyContentBaseline: DataModels.IndexingPolicy;
|
indexingPolicyContentBaseline: DataModels.IndexingPolicy;
|
||||||
onIndexingPolicyContentChange: (newIndexingPolicy: DataModels.IndexingPolicy) => void;
|
onIndexingPolicyContentChange: (newIndexingPolicy: DataModels.IndexingPolicy) => void;
|
||||||
logIndexingPolicySuccessMessage: () => void;
|
logIndexingPolicySuccessMessage: () => void;
|
||||||
|
indexTransformationProgress: number;
|
||||||
|
refreshIndexTransformationProgress: () => Promise<void>;
|
||||||
onIndexingPolicyDirtyChange: (isIndexingPolicyDirty: boolean) => void;
|
onIndexingPolicyDirtyChange: (isIndexingPolicyDirty: boolean) => void;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -51,6 +54,9 @@ export class IndexingPolicyComponent extends React.Component<
|
|||||||
if (!this.indexingPolicyEditor) {
|
if (!this.indexingPolicyEditor) {
|
||||||
this.createIndexingPolicyEditor();
|
this.createIndexingPolicyEditor();
|
||||||
} else {
|
} else {
|
||||||
|
this.indexingPolicyEditor.updateOptions({
|
||||||
|
readOnly: isIndexTransforming(this.props.indexTransformationProgress)
|
||||||
|
});
|
||||||
const indexingPolicyEditorModel = this.indexingPolicyEditor.getModel();
|
const indexingPolicyEditorModel = this.indexingPolicyEditor.getModel();
|
||||||
const value: string = JSON.stringify(this.props.indexingPolicyContent, undefined, 4);
|
const value: string = JSON.stringify(this.props.indexingPolicyContent, undefined, 4);
|
||||||
indexingPolicyEditorModel.setValue(value);
|
indexingPolicyEditorModel.setValue(value);
|
||||||
@@ -84,7 +90,7 @@ export class IndexingPolicyComponent extends React.Component<
|
|||||||
this.indexingPolicyEditor = monaco.editor.create(this.indexingPolicyDiv.current, {
|
this.indexingPolicyEditor = monaco.editor.create(this.indexingPolicyDiv.current, {
|
||||||
value: value,
|
value: value,
|
||||||
language: "json",
|
language: "json",
|
||||||
readOnly: false,
|
readOnly: isIndexTransforming(this.props.indexTransformationProgress),
|
||||||
ariaLabel: "Indexing Policy"
|
ariaLabel: "Indexing Policy"
|
||||||
});
|
});
|
||||||
if (this.indexingPolicyEditor) {
|
if (this.indexingPolicyEditor) {
|
||||||
@@ -108,8 +114,12 @@ export class IndexingPolicyComponent extends React.Component<
|
|||||||
public render(): JSX.Element {
|
public render(): JSX.Element {
|
||||||
return (
|
return (
|
||||||
<Stack {...titleAndInputStackProps}>
|
<Stack {...titleAndInputStackProps}>
|
||||||
|
<IndexingPolicyRefreshComponent
|
||||||
|
indexTransformationProgress={this.props.indexTransformationProgress}
|
||||||
|
refreshIndexTransformationProgress={this.props.refreshIndexTransformationProgress}
|
||||||
|
/>
|
||||||
{isDirty(this.props.indexingPolicyContent, this.props.indexingPolicyContentBaseline) && (
|
{isDirty(this.props.indexingPolicyContent, this.props.indexingPolicyContentBaseline) && (
|
||||||
<MessageBar messageBarType={MessageBarType.warning}>{indexingPolicyTTLWarningMessage}</MessageBar>
|
<MessageBar messageBarType={MessageBarType.warning}>{indexingPolicynUnsavedWarningMessage}</MessageBar>
|
||||||
)}
|
)}
|
||||||
<div className="settingsV2IndexingPolicyEditor" tabIndex={0} ref={this.indexingPolicyDiv}></div>
|
<div className="settingsV2IndexingPolicyEditor" tabIndex={0} ref={this.indexingPolicyDiv}></div>
|
||||||
</Stack>
|
</Stack>
|
||||||
|
|||||||
@@ -0,0 +1,15 @@
|
|||||||
|
import { shallow } from "enzyme";
|
||||||
|
import React from "react";
|
||||||
|
import { IndexingPolicyRefreshComponentProps, IndexingPolicyRefreshComponent } from "./IndexingPolicyRefreshComponent";
|
||||||
|
|
||||||
|
describe("IndexingPolicyRefreshComponent", () => {
|
||||||
|
it("renders", () => {
|
||||||
|
const props: IndexingPolicyRefreshComponentProps = {
|
||||||
|
indexTransformationProgress: 90,
|
||||||
|
refreshIndexTransformationProgress: () => new Promise(jest.fn())
|
||||||
|
};
|
||||||
|
|
||||||
|
const wrapper = shallow(<IndexingPolicyRefreshComponent {...props} />);
|
||||||
|
expect(wrapper).toMatchSnapshot();
|
||||||
|
});
|
||||||
|
});
|
||||||
@@ -0,0 +1,62 @@
|
|||||||
|
import * as React from "react";
|
||||||
|
import { MessageBar, MessageBarType } from "office-ui-fabric-react";
|
||||||
|
import {
|
||||||
|
mongoIndexTransformationRefreshingMessage,
|
||||||
|
renderMongoIndexTransformationRefreshMessage
|
||||||
|
} from "../../SettingsRenderUtils";
|
||||||
|
import { handleError } from "../../../../../Common/ErrorHandlingUtils";
|
||||||
|
import { isIndexTransforming } from "../../SettingsUtils";
|
||||||
|
|
||||||
|
export interface IndexingPolicyRefreshComponentProps {
|
||||||
|
indexTransformationProgress: number;
|
||||||
|
refreshIndexTransformationProgress: () => Promise<void>;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface IndexingPolicyRefreshComponentState {
|
||||||
|
isRefreshing: boolean;
|
||||||
|
}
|
||||||
|
|
||||||
|
export class IndexingPolicyRefreshComponent extends React.Component<
|
||||||
|
IndexingPolicyRefreshComponentProps,
|
||||||
|
IndexingPolicyRefreshComponentState
|
||||||
|
> {
|
||||||
|
constructor(props: IndexingPolicyRefreshComponentProps) {
|
||||||
|
super(props);
|
||||||
|
this.state = {
|
||||||
|
isRefreshing: false
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
private onClickRefreshIndexingTransformationLink = async () => await this.refreshIndexTransformationProgress();
|
||||||
|
|
||||||
|
private renderIndexTransformationWarning = (): JSX.Element => {
|
||||||
|
if (this.state.isRefreshing) {
|
||||||
|
return mongoIndexTransformationRefreshingMessage;
|
||||||
|
} else if (isIndexTransforming(this.props.indexTransformationProgress)) {
|
||||||
|
return renderMongoIndexTransformationRefreshMessage(
|
||||||
|
this.props.indexTransformationProgress,
|
||||||
|
this.onClickRefreshIndexingTransformationLink
|
||||||
|
);
|
||||||
|
}
|
||||||
|
return undefined;
|
||||||
|
};
|
||||||
|
|
||||||
|
private refreshIndexTransformationProgress = async () => {
|
||||||
|
this.setState({ isRefreshing: true });
|
||||||
|
try {
|
||||||
|
await this.props.refreshIndexTransformationProgress();
|
||||||
|
} catch (error) {
|
||||||
|
handleError(error, "RefreshIndexTransformationProgress", "Refreshing index transformation progress failed");
|
||||||
|
} finally {
|
||||||
|
this.setState({ isRefreshing: false });
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
public render(): JSX.Element {
|
||||||
|
return this.renderIndexTransformationWarning() ? (
|
||||||
|
<MessageBar messageBarType={MessageBarType.warning}>{this.renderIndexTransformationWarning()}</MessageBar>
|
||||||
|
) : (
|
||||||
|
<></>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,24 @@
|
|||||||
|
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
||||||
|
|
||||||
|
exports[`IndexingPolicyRefreshComponent renders 1`] = `
|
||||||
|
<StyledMessageBarBase
|
||||||
|
messageBarType={5}
|
||||||
|
>
|
||||||
|
<Text
|
||||||
|
styles={
|
||||||
|
Object {
|
||||||
|
"root": Object {
|
||||||
|
"fontSize": 12,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
>
|
||||||
|
You can make more indexing changes once the current index transformation has completed. It is 90% complete.
|
||||||
|
<StyledLinkBase
|
||||||
|
onClick={[Function]}
|
||||||
|
>
|
||||||
|
Refresh to check the progress.
|
||||||
|
</StyledLinkBase>
|
||||||
|
</Text>
|
||||||
|
</StyledMessageBarBase>
|
||||||
|
`;
|
||||||
@@ -2,6 +2,7 @@ import { shallow } from "enzyme";
|
|||||||
import React from "react";
|
import React from "react";
|
||||||
import { MongoIndexTypes, MongoNotificationMessage, MongoNotificationType } from "../../SettingsUtils";
|
import { MongoIndexTypes, MongoNotificationMessage, MongoNotificationType } from "../../SettingsUtils";
|
||||||
import { MongoIndexingPolicyComponent, MongoIndexingPolicyComponentProps } from "./MongoIndexingPolicyComponent";
|
import { MongoIndexingPolicyComponent, MongoIndexingPolicyComponentProps } from "./MongoIndexingPolicyComponent";
|
||||||
|
import { renderToString } from "react-dom/server";
|
||||||
|
|
||||||
describe("MongoIndexingPolicyComponent", () => {
|
describe("MongoIndexingPolicyComponent", () => {
|
||||||
const baseProps: MongoIndexingPolicyComponentProps = {
|
const baseProps: MongoIndexingPolicyComponentProps = {
|
||||||
@@ -21,10 +22,7 @@ describe("MongoIndexingPolicyComponent", () => {
|
|||||||
return;
|
return;
|
||||||
},
|
},
|
||||||
indexTransformationProgress: undefined,
|
indexTransformationProgress: undefined,
|
||||||
refreshIndexTransformationProgress: () =>
|
refreshIndexTransformationProgress: () => new Promise(jest.fn()),
|
||||||
new Promise(() => {
|
|
||||||
return;
|
|
||||||
}),
|
|
||||||
onMongoIndexingPolicySaveableChange: () => {
|
onMongoIndexingPolicySaveableChange: () => {
|
||||||
return;
|
return;
|
||||||
},
|
},
|
||||||
@@ -38,16 +36,6 @@ describe("MongoIndexingPolicyComponent", () => {
|
|||||||
expect(wrapper).toMatchSnapshot();
|
expect(wrapper).toMatchSnapshot();
|
||||||
});
|
});
|
||||||
|
|
||||||
it("isIndexingTransforming", () => {
|
|
||||||
const wrapper = shallow(<MongoIndexingPolicyComponent {...baseProps} />);
|
|
||||||
const mongoIndexingPolicyComponent = wrapper.instance() as MongoIndexingPolicyComponent;
|
|
||||||
expect(mongoIndexingPolicyComponent.isIndexingTransforming()).toEqual(false);
|
|
||||||
wrapper.setProps({ indexTransformationProgress: 50 });
|
|
||||||
expect(mongoIndexingPolicyComponent.isIndexingTransforming()).toEqual(true);
|
|
||||||
wrapper.setProps({ indexTransformationProgress: 100 });
|
|
||||||
expect(mongoIndexingPolicyComponent.isIndexingTransforming()).toEqual(false);
|
|
||||||
});
|
|
||||||
|
|
||||||
describe("AddMongoIndexProps test", () => {
|
describe("AddMongoIndexProps test", () => {
|
||||||
const wrapper = shallow(<MongoIndexingPolicyComponent {...baseProps} />);
|
const wrapper = shallow(<MongoIndexingPolicyComponent {...baseProps} />);
|
||||||
const mongoIndexingPolicyComponent = wrapper.instance() as MongoIndexingPolicyComponent;
|
const mongoIndexingPolicyComponent = wrapper.instance() as MongoIndexingPolicyComponent;
|
||||||
@@ -55,7 +43,7 @@ describe("MongoIndexingPolicyComponent", () => {
|
|||||||
it("defaults", () => {
|
it("defaults", () => {
|
||||||
expect(mongoIndexingPolicyComponent.isMongoIndexingPolicySaveable()).toEqual(false);
|
expect(mongoIndexingPolicyComponent.isMongoIndexingPolicySaveable()).toEqual(false);
|
||||||
expect(mongoIndexingPolicyComponent.isMongoIndexingPolicyDiscardable()).toEqual(false);
|
expect(mongoIndexingPolicyComponent.isMongoIndexingPolicyDiscardable()).toEqual(false);
|
||||||
expect(mongoIndexingPolicyComponent.getMongoWarningNotificationMessage()).toEqual(undefined);
|
expect(mongoIndexingPolicyComponent.getMongoWarningNotificationMessage()).toBeUndefined();
|
||||||
});
|
});
|
||||||
|
|
||||||
const sampleWarning = "sampleWarning";
|
const sampleWarning = "sampleWarning";
|
||||||
@@ -113,9 +101,12 @@ describe("MongoIndexingPolicyComponent", () => {
|
|||||||
expect(mongoIndexingPolicyComponent.isMongoIndexingPolicyDiscardable()).toEqual(
|
expect(mongoIndexingPolicyComponent.isMongoIndexingPolicyDiscardable()).toEqual(
|
||||||
isMongoIndexingPolicyDiscardable
|
isMongoIndexingPolicyDiscardable
|
||||||
);
|
);
|
||||||
expect(mongoIndexingPolicyComponent.getMongoWarningNotificationMessage()).toEqual(
|
if (mongoWarningNotificationMessage) {
|
||||||
mongoWarningNotificationMessage
|
const elementAsString = renderToString(mongoIndexingPolicyComponent.getMongoWarningNotificationMessage());
|
||||||
);
|
expect(elementAsString).toContain(mongoWarningNotificationMessage);
|
||||||
|
} else {
|
||||||
|
expect(mongoIndexingPolicyComponent.getMongoWarningNotificationMessage()).toBeUndefined();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -25,8 +25,8 @@ import {
|
|||||||
createAndAddMongoIndexStackProps,
|
createAndAddMongoIndexStackProps,
|
||||||
separatorStyles,
|
separatorStyles,
|
||||||
mongoIndexingPolicyAADError,
|
mongoIndexingPolicyAADError,
|
||||||
mongoIndexTransformationRefreshingMessage,
|
indexingPolicynUnsavedWarningMessage,
|
||||||
renderMongoIndexTransformationRefreshMessage
|
infoAndToolTipTextStyle
|
||||||
} from "../../SettingsRenderUtils";
|
} from "../../SettingsRenderUtils";
|
||||||
import { MongoIndex } from "../../../../../Utils/arm/generatedClients/2020-04-01/types";
|
import { MongoIndex } from "../../../../../Utils/arm/generatedClients/2020-04-01/types";
|
||||||
import {
|
import {
|
||||||
@@ -35,12 +35,13 @@ import {
|
|||||||
MongoIndexIdField,
|
MongoIndexIdField,
|
||||||
MongoNotificationType,
|
MongoNotificationType,
|
||||||
getMongoIndexType,
|
getMongoIndexType,
|
||||||
getMongoIndexTypeText
|
getMongoIndexTypeText,
|
||||||
|
isIndexTransforming
|
||||||
} from "../../SettingsUtils";
|
} from "../../SettingsUtils";
|
||||||
import { AddMongoIndexComponent } from "./AddMongoIndexComponent";
|
import { AddMongoIndexComponent } from "./AddMongoIndexComponent";
|
||||||
import { CollapsibleSectionComponent } from "../../../CollapsiblePanel/CollapsibleSectionComponent";
|
import { CollapsibleSectionComponent } from "../../../CollapsiblePanel/CollapsibleSectionComponent";
|
||||||
import { handleError } from "../../../../../Common/ErrorHandlingUtils";
|
|
||||||
import { AuthType } from "../../../../../AuthType";
|
import { AuthType } from "../../../../../AuthType";
|
||||||
|
import { IndexingPolicyRefreshComponent } from "../IndexingPolicyRefresh/IndexingPolicyRefreshComponent";
|
||||||
|
|
||||||
export interface MongoIndexingPolicyComponentProps {
|
export interface MongoIndexingPolicyComponentProps {
|
||||||
mongoIndexes: MongoIndex[];
|
mongoIndexes: MongoIndex[];
|
||||||
@@ -56,20 +57,13 @@ export interface MongoIndexingPolicyComponentProps {
|
|||||||
onMongoIndexingPolicyDiscardableChange: (isMongoIndexingPolicyDiscardable: boolean) => void;
|
onMongoIndexingPolicyDiscardableChange: (isMongoIndexingPolicyDiscardable: boolean) => void;
|
||||||
}
|
}
|
||||||
|
|
||||||
interface MongoIndexingPolicyComponentState {
|
|
||||||
isRefreshingIndexTransformationProgress: boolean;
|
|
||||||
}
|
|
||||||
|
|
||||||
interface MongoIndexDisplayProps {
|
interface MongoIndexDisplayProps {
|
||||||
definition: JSX.Element;
|
definition: JSX.Element;
|
||||||
type: JSX.Element;
|
type: JSX.Element;
|
||||||
actionButton: JSX.Element;
|
actionButton: JSX.Element;
|
||||||
}
|
}
|
||||||
|
|
||||||
export class MongoIndexingPolicyComponent extends React.Component<
|
export class MongoIndexingPolicyComponent extends React.Component<MongoIndexingPolicyComponentProps> {
|
||||||
MongoIndexingPolicyComponentProps,
|
|
||||||
MongoIndexingPolicyComponentState
|
|
||||||
> {
|
|
||||||
private shouldCheckComponentIsDirty = true;
|
private shouldCheckComponentIsDirty = true;
|
||||||
private addMongoIndexComponentRefs: React.RefObject<AddMongoIndexComponent>[] = [];
|
private addMongoIndexComponentRefs: React.RefObject<AddMongoIndexComponent>[] = [];
|
||||||
private initialIndexesColumns: IColumn[] = [
|
private initialIndexesColumns: IColumn[] = [
|
||||||
@@ -98,13 +92,6 @@ export class MongoIndexingPolicyComponent extends React.Component<
|
|||||||
}
|
}
|
||||||
];
|
];
|
||||||
|
|
||||||
constructor(props: MongoIndexingPolicyComponentProps) {
|
|
||||||
super(props);
|
|
||||||
this.state = {
|
|
||||||
isRefreshingIndexTransformationProgress: false
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
componentDidUpdate(prevProps: MongoIndexingPolicyComponentProps): void {
|
componentDidUpdate(prevProps: MongoIndexingPolicyComponentProps): void {
|
||||||
if (this.props.indexesToAdd.length > prevProps.indexesToAdd.length) {
|
if (this.props.indexesToAdd.length > prevProps.indexesToAdd.length) {
|
||||||
this.addMongoIndexComponentRefs[prevProps.indexesToAdd.length]?.current?.focus();
|
this.addMongoIndexComponentRefs[prevProps.indexesToAdd.length]?.current?.focus();
|
||||||
@@ -144,10 +131,15 @@ export class MongoIndexingPolicyComponent extends React.Component<
|
|||||||
return this.props.indexesToAdd.length > 0 || this.props.indexesToDrop.length > 0;
|
return this.props.indexesToAdd.length > 0 || this.props.indexesToDrop.length > 0;
|
||||||
};
|
};
|
||||||
|
|
||||||
public getMongoWarningNotificationMessage = (): string => {
|
public getMongoWarningNotificationMessage = (): JSX.Element => {
|
||||||
return this.props.indexesToAdd.find(
|
const warningMessage = this.props.indexesToAdd.find(
|
||||||
addMongoIndexProps => addMongoIndexProps.notification?.type === MongoNotificationType.Warning
|
addMongoIndexProps => addMongoIndexProps.notification?.type === MongoNotificationType.Warning
|
||||||
)?.notification.message;
|
)?.notification.message;
|
||||||
|
|
||||||
|
if (warningMessage) {
|
||||||
|
return <Text styles={infoAndToolTipTextStyle}>{warningMessage}</Text>;
|
||||||
|
}
|
||||||
|
return undefined;
|
||||||
};
|
};
|
||||||
|
|
||||||
private onRenderRow = (props: IDetailsRowProps): JSX.Element => {
|
private onRenderRow = (props: IDetailsRowProps): JSX.Element => {
|
||||||
@@ -159,7 +151,7 @@ export class MongoIndexingPolicyComponent extends React.Component<
|
|||||||
<IconButton
|
<IconButton
|
||||||
ariaLabel="Delete index Button"
|
ariaLabel="Delete index Button"
|
||||||
iconProps={{ iconName: "Delete" }}
|
iconProps={{ iconName: "Delete" }}
|
||||||
disabled={this.isIndexingTransforming()}
|
disabled={isIndexTransforming(this.props.indexTransformationProgress)}
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
this.props.onIndexDrop(arrayPosition);
|
this.props.onIndexDrop(arrayPosition);
|
||||||
}}
|
}}
|
||||||
@@ -230,7 +222,7 @@ export class MongoIndexingPolicyComponent extends React.Component<
|
|||||||
|
|
||||||
<AddMongoIndexComponent
|
<AddMongoIndexComponent
|
||||||
ref={this.addMongoIndexComponentRefs[indexesToAddLength]}
|
ref={this.addMongoIndexComponentRefs[indexesToAddLength]}
|
||||||
disabled={this.isIndexingTransforming()}
|
disabled={isIndexTransforming(this.props.indexTransformationProgress)}
|
||||||
position={indexesToAddLength}
|
position={indexesToAddLength}
|
||||||
key={indexesToAddLength}
|
key={indexesToAddLength}
|
||||||
description={undefined}
|
description={undefined}
|
||||||
@@ -298,55 +290,21 @@ export class MongoIndexingPolicyComponent extends React.Component<
|
|||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
private refreshIndexTransformationProgress = async () => {
|
|
||||||
this.setState({ isRefreshingIndexTransformationProgress: true });
|
|
||||||
try {
|
|
||||||
await this.props.refreshIndexTransformationProgress();
|
|
||||||
} catch (error) {
|
|
||||||
handleError(error, "Refreshing index transformation progress failed.", "RefreshIndexTransformationProgress");
|
|
||||||
} finally {
|
|
||||||
this.setState({ isRefreshingIndexTransformationProgress: false });
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
public isIndexingTransforming = (): boolean =>
|
|
||||||
// index transformation progress can be 0
|
|
||||||
this.props.indexTransformationProgress !== undefined && this.props.indexTransformationProgress !== 100;
|
|
||||||
|
|
||||||
private onClickRefreshIndexingTransformationLink = async () => await this.refreshIndexTransformationProgress();
|
|
||||||
|
|
||||||
private renderIndexTransformationWarning = (): JSX.Element => {
|
|
||||||
if (this.state.isRefreshingIndexTransformationProgress) {
|
|
||||||
return mongoIndexTransformationRefreshingMessage;
|
|
||||||
} else if (this.isIndexingTransforming()) {
|
|
||||||
return renderMongoIndexTransformationRefreshMessage(
|
|
||||||
this.props.indexTransformationProgress,
|
|
||||||
this.onClickRefreshIndexingTransformationLink
|
|
||||||
);
|
|
||||||
}
|
|
||||||
return undefined;
|
|
||||||
};
|
|
||||||
|
|
||||||
private renderWarningMessage = (): JSX.Element => {
|
private renderWarningMessage = (): JSX.Element => {
|
||||||
let warningMessage: string;
|
let warningMessage: JSX.Element;
|
||||||
if (this.getMongoWarningNotificationMessage()) {
|
if (this.getMongoWarningNotificationMessage()) {
|
||||||
warningMessage = this.getMongoWarningNotificationMessage();
|
warningMessage = this.getMongoWarningNotificationMessage();
|
||||||
} else if (this.isMongoIndexingPolicySaveable()) {
|
} else if (this.isMongoIndexingPolicySaveable()) {
|
||||||
warningMessage =
|
warningMessage = indexingPolicynUnsavedWarningMessage;
|
||||||
"You have not saved the latest changes made to your indexing policy. Please click save to confirm the changes.";
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
{this.renderIndexTransformationWarning() && (
|
<IndexingPolicyRefreshComponent
|
||||||
<MessageBar messageBarType={MessageBarType.warning}>{this.renderIndexTransformationWarning()}</MessageBar>
|
indexTransformationProgress={this.props.indexTransformationProgress}
|
||||||
)}
|
refreshIndexTransformationProgress={this.props.refreshIndexTransformationProgress}
|
||||||
|
/>
|
||||||
{warningMessage && (
|
{warningMessage && <MessageBar messageBarType={MessageBarType.warning}>{warningMessage}</MessageBar>}
|
||||||
<MessageBar messageBarType={MessageBarType.warning}>
|
|
||||||
<Text>{warningMessage}</Text>
|
|
||||||
</MessageBar>
|
|
||||||
)}
|
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -8,6 +8,9 @@ exports[`MongoIndexingPolicyComponent renders 1`] = `
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
>
|
>
|
||||||
|
<IndexingPolicyRefreshComponent
|
||||||
|
refreshIndexTransformationProgress={[Function]}
|
||||||
|
/>
|
||||||
<Text>
|
<Text>
|
||||||
For queries that filter on multiple properties, create multiple single field indexes instead of a compound index.
|
For queries that filter on multiple properties, create multiple single field indexes instead of a compound index.
|
||||||
<StyledLinkBase
|
<StyledLinkBase
|
||||||
|
|||||||
@@ -1,14 +1,13 @@
|
|||||||
import { shallow } from "enzyme";
|
import { shallow } from "enzyme";
|
||||||
|
import ko from "knockout";
|
||||||
import React from "react";
|
import React from "react";
|
||||||
import { ScaleComponent, ScaleComponentProps } from "./ScaleComponent";
|
|
||||||
import { container, collection } from "../TestUtils";
|
|
||||||
import { ThroughputInputAutoPilotV3Component } from "./ThroughputInputComponents/ThroughputInputAutoPilotV3Component";
|
|
||||||
import Explorer from "../../../Explorer";
|
|
||||||
import * as Constants from "../../../../Common/Constants";
|
import * as Constants from "../../../../Common/Constants";
|
||||||
import * as DataModels from "../../../../Contracts/DataModels";
|
import * as DataModels from "../../../../Contracts/DataModels";
|
||||||
|
import Explorer from "../../../Explorer";
|
||||||
import { throughputUnit } from "../SettingsRenderUtils";
|
import { throughputUnit } from "../SettingsRenderUtils";
|
||||||
import * as SharedConstants from "../../../../Shared/Constants";
|
import { collection, container } from "../TestUtils";
|
||||||
import ko from "knockout";
|
import { ScaleComponent, ScaleComponentProps } from "./ScaleComponent";
|
||||||
|
import { ThroughputInputAutoPilotV3Component } from "./ThroughputInputComponents/ThroughputInputAutoPilotV3Component";
|
||||||
|
|
||||||
describe("ScaleComponent", () => {
|
describe("ScaleComponent", () => {
|
||||||
const nonNationalCloudContainer = new Explorer();
|
const nonNationalCloudContainer = new Explorer();
|
||||||
@@ -48,9 +47,7 @@ describe("ScaleComponent", () => {
|
|||||||
let wrapper = shallow(<ScaleComponent {...baseProps} />);
|
let wrapper = shallow(<ScaleComponent {...baseProps} />);
|
||||||
expect(wrapper).toMatchSnapshot();
|
expect(wrapper).toMatchSnapshot();
|
||||||
expect(wrapper.exists(ThroughputInputAutoPilotV3Component)).toEqual(true);
|
expect(wrapper.exists(ThroughputInputAutoPilotV3Component)).toEqual(true);
|
||||||
expect(wrapper.exists("#throughputApplyLongDelayMessage")).toEqual(true);
|
|
||||||
expect(wrapper.exists("#throughputApplyShortDelayMessage")).toEqual(false);
|
expect(wrapper.exists("#throughputApplyShortDelayMessage")).toEqual(false);
|
||||||
expect(wrapper.find("#throughputApplyLongDelayMessage").html()).toContain(targetThroughput);
|
|
||||||
|
|
||||||
const newCollection = { ...collection };
|
const newCollection = { ...collection };
|
||||||
const maxThroughput = 5000;
|
const maxThroughput = 5000;
|
||||||
@@ -109,11 +106,6 @@ describe("ScaleComponent", () => {
|
|||||||
expect(scaleComponent.isAutoScaleEnabled()).toEqual(true);
|
expect(scaleComponent.isAutoScaleEnabled()).toEqual(true);
|
||||||
});
|
});
|
||||||
|
|
||||||
it("getMaxRUThroughputInputLimit", () => {
|
|
||||||
const scaleComponent = new ScaleComponent(baseProps);
|
|
||||||
expect(scaleComponent.getMaxRUThroughputInputLimit()).toEqual(40000);
|
|
||||||
});
|
|
||||||
|
|
||||||
it("getThroughputTitle", () => {
|
it("getThroughputTitle", () => {
|
||||||
let scaleComponent = new ScaleComponent(baseProps);
|
let scaleComponent = new ScaleComponent(baseProps);
|
||||||
expect(scaleComponent.getThroughputTitle()).toEqual("Throughput (6,000 - unlimited RU/s)");
|
expect(scaleComponent.getThroughputTitle()).toEqual("Throughput (6,000 - unlimited RU/s)");
|
||||||
@@ -126,26 +118,4 @@ describe("ScaleComponent", () => {
|
|||||||
scaleComponent = new ScaleComponent(newProps);
|
scaleComponent = new ScaleComponent(newProps);
|
||||||
expect(scaleComponent.getThroughputTitle()).toEqual("Throughput (autoscale)");
|
expect(scaleComponent.getThroughputTitle()).toEqual("Throughput (autoscale)");
|
||||||
});
|
});
|
||||||
|
|
||||||
it("canThroughputExceedMaximumValue", () => {
|
|
||||||
let scaleComponent = new ScaleComponent(baseProps);
|
|
||||||
expect(scaleComponent.canThroughputExceedMaximumValue()).toEqual(true);
|
|
||||||
|
|
||||||
const newProps = { ...baseProps, container: nonNationalCloudContainer };
|
|
||||||
scaleComponent = new ScaleComponent(newProps);
|
|
||||||
expect(scaleComponent.canThroughputExceedMaximumValue()).toEqual(true);
|
|
||||||
});
|
|
||||||
|
|
||||||
it("getThroughputWarningMessage", () => {
|
|
||||||
const throughputBeyondLimit = SharedConstants.CollectionCreation.DefaultCollectionRUs1Million + 1000;
|
|
||||||
const throughputBeyondMaxRus = SharedConstants.CollectionCreation.DefaultCollectionRUs1Million - 1000;
|
|
||||||
|
|
||||||
const newProps = { ...baseProps, container: nonNationalCloudContainer, throughput: throughputBeyondLimit };
|
|
||||||
let scaleComponent = new ScaleComponent(newProps);
|
|
||||||
expect(scaleComponent.getThroughputWarningMessage().props.id).toEqual("updateThroughputBeyondLimitWarningMessage");
|
|
||||||
|
|
||||||
newProps.throughput = throughputBeyondMaxRus;
|
|
||||||
scaleComponent = new ScaleComponent(newProps);
|
|
||||||
expect(scaleComponent.getThroughputWarningMessage().props.id).toEqual("updateThroughputDelayedApplyWarningMessage");
|
|
||||||
});
|
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -1,24 +1,20 @@
|
|||||||
|
import { Label, MessageBar, MessageBarType, Stack, Text, TextField } from "office-ui-fabric-react";
|
||||||
import * as React from "react";
|
import * as React from "react";
|
||||||
import * as Constants from "../../../../Common/Constants";
|
import * as Constants from "../../../../Common/Constants";
|
||||||
import { ThroughputInputAutoPilotV3Component } from "./ThroughputInputComponents/ThroughputInputAutoPilotV3Component";
|
import { configContext, Platform } from "../../../../ConfigContext";
|
||||||
import * as ViewModels from "../../../../Contracts/ViewModels";
|
|
||||||
import * as DataModels from "../../../../Contracts/DataModels";
|
import * as DataModels from "../../../../Contracts/DataModels";
|
||||||
import * as SharedConstants from "../../../../Shared/Constants";
|
import * as ViewModels from "../../../../Contracts/ViewModels";
|
||||||
|
import * as AutoPilotUtils from "../../../../Utils/AutoPilotUtils";
|
||||||
import Explorer from "../../../Explorer";
|
import Explorer from "../../../Explorer";
|
||||||
import {
|
import {
|
||||||
getTextFieldStyles,
|
getTextFieldStyles,
|
||||||
subComponentStackProps,
|
|
||||||
titleAndInputStackProps,
|
|
||||||
throughputUnit,
|
|
||||||
getThroughputApplyLongDelayMessage,
|
|
||||||
getThroughputApplyShortDelayMessage,
|
getThroughputApplyShortDelayMessage,
|
||||||
updateThroughputBeyondLimitWarningMessage,
|
subComponentStackProps,
|
||||||
updateThroughputDelayedApplyWarningMessage
|
throughputUnit,
|
||||||
|
titleAndInputStackProps
|
||||||
} from "../SettingsRenderUtils";
|
} from "../SettingsRenderUtils";
|
||||||
import { getMaxRUs, getMinRUs, hasDatabaseSharedThroughput } from "../SettingsUtils";
|
import { getMinRUs, hasDatabaseSharedThroughput } from "../SettingsUtils";
|
||||||
import * as AutoPilotUtils from "../../../../Utils/AutoPilotUtils";
|
import { ThroughputInputAutoPilotV3Component } from "./ThroughputInputComponents/ThroughputInputAutoPilotV3Component";
|
||||||
import { Text, TextField, Stack, Label, MessageBar, MessageBarType } from "office-ui-fabric-react";
|
|
||||||
import { configContext, Platform } from "../../../../ConfigContext";
|
|
||||||
|
|
||||||
export interface ScaleComponentProps {
|
export interface ScaleComponentProps {
|
||||||
collection: ViewModels.Collection;
|
collection: ViewModels.Collection;
|
||||||
@@ -75,40 +71,17 @@ export class ScaleComponent extends React.Component<ScaleComponentProps> {
|
|||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
public getMaxRUThroughputInputLimit = (): number => {
|
|
||||||
if (configContext.platform === Platform.Hosted && this.props.collection.partitionKey) {
|
|
||||||
return SharedConstants.CollectionCreation.DefaultCollectionRUs1Million;
|
|
||||||
}
|
|
||||||
|
|
||||||
return getMaxRUs(this.props.collection, this.props.container);
|
|
||||||
};
|
|
||||||
|
|
||||||
public getThroughputTitle = (): string => {
|
public getThroughputTitle = (): string => {
|
||||||
if (this.props.isAutoPilotSelected) {
|
if (this.props.isAutoPilotSelected) {
|
||||||
return AutoPilotUtils.getAutoPilotHeaderText();
|
return AutoPilotUtils.getAutoPilotHeaderText();
|
||||||
}
|
}
|
||||||
|
|
||||||
const minThroughput: string = getMinRUs(this.props.collection, this.props.container).toLocaleString();
|
const minThroughput: string = getMinRUs(this.props.collection, this.props.container).toLocaleString();
|
||||||
const maxThroughput: string =
|
const maxThroughput: string = !this.props.isFixedContainer ? "unlimited" : "10000";
|
||||||
this.canThroughputExceedMaximumValue() && !this.props.isFixedContainer
|
|
||||||
? "unlimited"
|
|
||||||
: getMaxRUs(this.props.collection, this.props.container).toLocaleString();
|
|
||||||
return `Throughput (${minThroughput} - ${maxThroughput} RU/s)`;
|
return `Throughput (${minThroughput} - ${maxThroughput} RU/s)`;
|
||||||
};
|
};
|
||||||
|
|
||||||
public canThroughputExceedMaximumValue = (): boolean => {
|
|
||||||
return (
|
|
||||||
!this.props.isFixedContainer &&
|
|
||||||
configContext.platform === Platform.Portal &&
|
|
||||||
!this.props.container.isRunningOnNationalCloud()
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
public getInitialNotificationElement = (): JSX.Element => {
|
public getInitialNotificationElement = (): JSX.Element => {
|
||||||
if (this.props.initialNotification) {
|
|
||||||
return this.getLongDelayMessage();
|
|
||||||
}
|
|
||||||
|
|
||||||
const offer = this.props.collection?.offer && this.props.collection.offer();
|
const offer = this.props.collection?.offer && this.props.collection.offer();
|
||||||
if (
|
if (
|
||||||
offer &&
|
offer &&
|
||||||
@@ -135,47 +108,6 @@ export class ScaleComponent extends React.Component<ScaleComponentProps> {
|
|||||||
return undefined;
|
return undefined;
|
||||||
};
|
};
|
||||||
|
|
||||||
public getThroughputWarningMessage = (): JSX.Element => {
|
|
||||||
const throughputExceedsBackendLimits: boolean =
|
|
||||||
this.canThroughputExceedMaximumValue() &&
|
|
||||||
getMaxRUs(this.props.collection, this.props.container) <=
|
|
||||||
SharedConstants.CollectionCreation.DefaultCollectionRUs1Million &&
|
|
||||||
this.props.throughput > SharedConstants.CollectionCreation.DefaultCollectionRUs1Million;
|
|
||||||
|
|
||||||
if (throughputExceedsBackendLimits && !!this.props.collection.partitionKey && !this.props.isFixedContainer) {
|
|
||||||
return updateThroughputBeyondLimitWarningMessage;
|
|
||||||
}
|
|
||||||
|
|
||||||
const throughputExceedsMaxValue: boolean =
|
|
||||||
!this.isEmulator && this.props.throughput > getMaxRUs(this.props.collection, this.props.container);
|
|
||||||
|
|
||||||
if (throughputExceedsMaxValue && !!this.props.collection.partitionKey && !this.props.isFixedContainer) {
|
|
||||||
return updateThroughputDelayedApplyWarningMessage;
|
|
||||||
}
|
|
||||||
|
|
||||||
return undefined;
|
|
||||||
};
|
|
||||||
|
|
||||||
public getLongDelayMessage = (): JSX.Element => {
|
|
||||||
const matches: string[] = this.props.initialNotification?.description.match(
|
|
||||||
`Throughput update for (.*) ${throughputUnit}`
|
|
||||||
);
|
|
||||||
|
|
||||||
const throughput = this.props.throughputBaseline;
|
|
||||||
const targetThroughput: number = matches.length > 1 && Number(matches[1]);
|
|
||||||
if (targetThroughput) {
|
|
||||||
return getThroughputApplyLongDelayMessage(
|
|
||||||
this.props.wasAutopilotOriginallySet,
|
|
||||||
throughput,
|
|
||||||
throughputUnit,
|
|
||||||
this.props.collection.databaseId,
|
|
||||||
this.props.collection.id(),
|
|
||||||
targetThroughput
|
|
||||||
);
|
|
||||||
}
|
|
||||||
return <></>;
|
|
||||||
};
|
|
||||||
|
|
||||||
private getThroughputInputComponent = (): JSX.Element => (
|
private getThroughputInputComponent = (): JSX.Element => (
|
||||||
<ThroughputInputAutoPilotV3Component
|
<ThroughputInputAutoPilotV3Component
|
||||||
databaseAccount={this.props.container.databaseAccount()}
|
databaseAccount={this.props.container.databaseAccount()}
|
||||||
@@ -184,9 +116,7 @@ export class ScaleComponent extends React.Component<ScaleComponentProps> {
|
|||||||
throughputBaseline={this.props.throughputBaseline}
|
throughputBaseline={this.props.throughputBaseline}
|
||||||
onThroughputChange={this.props.onThroughputChange}
|
onThroughputChange={this.props.onThroughputChange}
|
||||||
minimum={getMinRUs(this.props.collection, this.props.container)}
|
minimum={getMinRUs(this.props.collection, this.props.container)}
|
||||||
maximum={this.getMaxRUThroughputInputLimit()}
|
|
||||||
isEnabled={!hasDatabaseSharedThroughput(this.props.collection)}
|
isEnabled={!hasDatabaseSharedThroughput(this.props.collection)}
|
||||||
canExceedMaximumValue={this.canThroughputExceedMaximumValue()}
|
|
||||||
label={this.getThroughputTitle()}
|
label={this.getThroughputTitle()}
|
||||||
isEmulator={this.isEmulator}
|
isEmulator={this.isEmulator}
|
||||||
isFixed={this.props.isFixedContainer}
|
isFixed={this.props.isFixedContainer}
|
||||||
@@ -199,7 +129,6 @@ export class ScaleComponent extends React.Component<ScaleComponentProps> {
|
|||||||
spendAckChecked={false}
|
spendAckChecked={false}
|
||||||
onScaleSaveableChange={this.props.onScaleSaveableChange}
|
onScaleSaveableChange={this.props.onScaleSaveableChange}
|
||||||
onScaleDiscardableChange={this.props.onScaleDiscardableChange}
|
onScaleDiscardableChange={this.props.onScaleDiscardableChange}
|
||||||
getThroughputWarningMessage={this.getThroughputWarningMessage}
|
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
|
|
||||||
|
|||||||
@@ -15,7 +15,6 @@ describe("ThroughputInputAutoPilotV3Component", () => {
|
|||||||
throughputBaseline: 100,
|
throughputBaseline: 100,
|
||||||
onThroughputChange: undefined,
|
onThroughputChange: undefined,
|
||||||
minimum: 10000,
|
minimum: 10000,
|
||||||
maximum: 400,
|
|
||||||
step: 100,
|
step: 100,
|
||||||
isEnabled: true,
|
isEnabled: true,
|
||||||
isEmulator: false,
|
isEmulator: false,
|
||||||
@@ -38,8 +37,7 @@ describe("ThroughputInputAutoPilotV3Component", () => {
|
|||||||
},
|
},
|
||||||
onScaleDiscardableChange: () => {
|
onScaleDiscardableChange: () => {
|
||||||
return;
|
return;
|
||||||
},
|
}
|
||||||
getThroughputWarningMessage: () => undefined
|
|
||||||
};
|
};
|
||||||
|
|
||||||
it("throughput input visible", () => {
|
it("throughput input visible", () => {
|
||||||
|
|||||||
@@ -1,35 +1,33 @@
|
|||||||
import React from "react";
|
|
||||||
import * as AutoPilotUtils from "../../../../../Utils/AutoPilotUtils";
|
|
||||||
import {
|
import {
|
||||||
getTextFieldStyles,
|
Checkbox,
|
||||||
getToolTipContainer,
|
|
||||||
noLeftPaddingCheckBoxStyle,
|
|
||||||
titleAndInputStackProps,
|
|
||||||
checkBoxAndInputStackProps,
|
|
||||||
getChoiceGroupStyles,
|
|
||||||
messageBarStyles,
|
|
||||||
getEstimatedSpendElement,
|
|
||||||
getEstimatedAutoscaleSpendElement,
|
|
||||||
getAutoPilotV3SpendElement,
|
|
||||||
manualToAutoscaleDisclaimerElement
|
|
||||||
} from "../../SettingsRenderUtils";
|
|
||||||
import {
|
|
||||||
Text,
|
|
||||||
TextField,
|
|
||||||
ChoiceGroup,
|
ChoiceGroup,
|
||||||
IChoiceGroupOption,
|
IChoiceGroupOption,
|
||||||
Checkbox,
|
|
||||||
Stack,
|
|
||||||
Label,
|
Label,
|
||||||
Link,
|
Link,
|
||||||
MessageBar,
|
MessageBar,
|
||||||
MessageBarType
|
MessageBarType,
|
||||||
|
Stack,
|
||||||
|
Text,
|
||||||
|
TextField
|
||||||
} from "office-ui-fabric-react";
|
} from "office-ui-fabric-react";
|
||||||
import { ToolTipLabelComponent } from "../ToolTipLabelComponent";
|
import React from "react";
|
||||||
import { getSanitizedInputValue, IsComponentDirtyResult, isDirty } from "../../SettingsUtils";
|
|
||||||
import * as SharedConstants from "../../../../../Shared/Constants";
|
|
||||||
import * as DataModels from "../../../../../Contracts/DataModels";
|
import * as DataModels from "../../../../../Contracts/DataModels";
|
||||||
import { Int32 } from "../../../../Panes/Tables/Validators/EntityPropertyValidationCommon";
|
import * as AutoPilotUtils from "../../../../../Utils/AutoPilotUtils";
|
||||||
|
import {
|
||||||
|
checkBoxAndInputStackProps,
|
||||||
|
getAutoPilotV3SpendElement,
|
||||||
|
getChoiceGroupStyles,
|
||||||
|
getEstimatedAutoscaleSpendElement,
|
||||||
|
getEstimatedSpendElement,
|
||||||
|
getTextFieldStyles,
|
||||||
|
getToolTipContainer,
|
||||||
|
manualToAutoscaleDisclaimerElement,
|
||||||
|
messageBarStyles,
|
||||||
|
noLeftPaddingCheckBoxStyle,
|
||||||
|
titleAndInputStackProps
|
||||||
|
} from "../../SettingsRenderUtils";
|
||||||
|
import { getSanitizedInputValue, IsComponentDirtyResult, isDirty } from "../../SettingsUtils";
|
||||||
|
import { ToolTipLabelComponent } from "../ToolTipLabelComponent";
|
||||||
|
|
||||||
export interface ThroughputInputAutoPilotV3Props {
|
export interface ThroughputInputAutoPilotV3Props {
|
||||||
databaseAccount: DataModels.DatabaseAccount;
|
databaseAccount: DataModels.DatabaseAccount;
|
||||||
@@ -38,7 +36,6 @@ export interface ThroughputInputAutoPilotV3Props {
|
|||||||
throughputBaseline: number;
|
throughputBaseline: number;
|
||||||
onThroughputChange: (newThroughput: number) => void;
|
onThroughputChange: (newThroughput: number) => void;
|
||||||
minimum: number;
|
minimum: number;
|
||||||
maximum: number;
|
|
||||||
step?: number;
|
step?: number;
|
||||||
isEnabled?: boolean;
|
isEnabled?: boolean;
|
||||||
spendAckChecked?: boolean;
|
spendAckChecked?: boolean;
|
||||||
@@ -59,7 +56,6 @@ export interface ThroughputInputAutoPilotV3Props {
|
|||||||
onMaxAutoPilotThroughputChange: (newThroughput: number) => void;
|
onMaxAutoPilotThroughputChange: (newThroughput: number) => void;
|
||||||
onScaleSaveableChange: (isScaleSaveable: boolean) => void;
|
onScaleSaveableChange: (isScaleSaveable: boolean) => void;
|
||||||
onScaleDiscardableChange: (isScaleDiscardable: boolean) => void;
|
onScaleDiscardableChange: (isScaleDiscardable: boolean) => void;
|
||||||
getThroughputWarningMessage: () => JSX.Element;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
interface ThroughputInputAutoPilotV3State {
|
interface ThroughputInputAutoPilotV3State {
|
||||||
@@ -119,13 +115,7 @@ export class ThroughputInputAutoPilotV3Component extends React.Component<
|
|||||||
if (isDirty(this.props.throughput, this.props.throughputBaseline)) {
|
if (isDirty(this.props.throughput, this.props.throughputBaseline)) {
|
||||||
isDiscardable = true;
|
isDiscardable = true;
|
||||||
isSaveable = true;
|
isSaveable = true;
|
||||||
if (
|
if (!this.props.throughput || this.props.throughput < this.props.minimum) {
|
||||||
!this.props.throughput ||
|
|
||||||
this.props.throughput < this.props.minimum ||
|
|
||||||
(this.props.throughput > this.props.maximum && (this.props.isEmulator || this.props.isFixed)) ||
|
|
||||||
(this.props.throughput > SharedConstants.CollectionCreation.DefaultCollectionRUs1Million &&
|
|
||||||
!this.props.canExceedMaximumValue)
|
|
||||||
) {
|
|
||||||
isSaveable = false;
|
isSaveable = false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -141,8 +131,8 @@ export class ThroughputInputAutoPilotV3Component extends React.Component<
|
|||||||
};
|
};
|
||||||
|
|
||||||
this.step = this.props.step ?? ThroughputInputAutoPilotV3Component.defaultStep;
|
this.step = this.props.step ?? ThroughputInputAutoPilotV3Component.defaultStep;
|
||||||
this.throughputInputMaxValue = this.props.canExceedMaximumValue ? Int32.Max : this.props.maximum;
|
this.throughputInputMaxValue = Number.MAX_SAFE_INTEGER;
|
||||||
this.autoPilotInputMaxValue = this.props.isFixed ? this.props.maximum : Int32.Max;
|
this.autoPilotInputMaxValue = Number.MAX_SAFE_INTEGER;
|
||||||
}
|
}
|
||||||
|
|
||||||
public hasProvisioningTypeChanged = (): boolean =>
|
public hasProvisioningTypeChanged = (): boolean =>
|
||||||
@@ -306,12 +296,6 @@ export class ThroughputInputAutoPilotV3Component extends React.Component<
|
|||||||
onChange={this.onThroughputChange}
|
onChange={this.onThroughputChange}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
{this.props.getThroughputWarningMessage() && (
|
|
||||||
<MessageBar messageBarType={MessageBarType.warning} styles={messageBarStyles}>
|
|
||||||
{this.props.getThroughputWarningMessage()}
|
|
||||||
</MessageBar>
|
|
||||||
)}
|
|
||||||
|
|
||||||
{!this.props.isEmulator && this.getRequestUnitsUsageCost()}
|
{!this.props.isEmulator && this.getRequestUnitsUsageCost()}
|
||||||
|
|
||||||
{this.props.spendAckVisible && (
|
{this.props.spendAckVisible && (
|
||||||
|
|||||||
@@ -8,6 +8,9 @@ exports[`IndexingPolicyComponent renders 1`] = `
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
>
|
>
|
||||||
|
<IndexingPolicyRefreshComponent
|
||||||
|
refreshIndexTransformationProgress={[Function]}
|
||||||
|
/>
|
||||||
<div
|
<div
|
||||||
className="settingsV2IndexingPolicyEditor"
|
className="settingsV2IndexingPolicyEditor"
|
||||||
tabIndex={0}
|
tabIndex={0}
|
||||||
|
|||||||
@@ -8,29 +8,6 @@ exports[`ScaleComponent renders with correct intiial notification 1`] = `
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
>
|
>
|
||||||
<StyledMessageBarBase
|
|
||||||
messageBarType={5}
|
|
||||||
>
|
|
||||||
<Text
|
|
||||||
id="throughputApplyLongDelayMessage"
|
|
||||||
styles={
|
|
||||||
Object {
|
|
||||||
"root": Object {
|
|
||||||
"fontSize": 12,
|
|
||||||
},
|
|
||||||
}
|
|
||||||
}
|
|
||||||
>
|
|
||||||
A request to increase the throughput is currently in progress. This operation will take 1-3 business days to complete. View the latest status in Notifications.
|
|
||||||
<br />
|
|
||||||
Database:
|
|
||||||
test
|
|
||||||
, Container:
|
|
||||||
test
|
|
||||||
|
|
||||||
, Current autoscale throughput: 100 - 1000 RU/s, Target autoscale throughput: 600 - 6000 RU/s
|
|
||||||
</Text>
|
|
||||||
</StyledMessageBarBase>
|
|
||||||
<Stack
|
<Stack
|
||||||
tokens={
|
tokens={
|
||||||
Object {
|
Object {
|
||||||
@@ -39,8 +16,6 @@ exports[`ScaleComponent renders with correct intiial notification 1`] = `
|
|||||||
}
|
}
|
||||||
>
|
>
|
||||||
<ThroughputInputAutoPilotV3Component
|
<ThroughputInputAutoPilotV3Component
|
||||||
canExceedMaximumValue={true}
|
|
||||||
getThroughputWarningMessage={[Function]}
|
|
||||||
isAutoPilotSelected={false}
|
isAutoPilotSelected={false}
|
||||||
isEmulator={false}
|
isEmulator={false}
|
||||||
isEnabled={true}
|
isEnabled={true}
|
||||||
@@ -48,7 +23,6 @@ exports[`ScaleComponent renders with correct intiial notification 1`] = `
|
|||||||
label="Throughput (6,000 - unlimited RU/s)"
|
label="Throughput (6,000 - unlimited RU/s)"
|
||||||
maxAutoPilotThroughput={4000}
|
maxAutoPilotThroughput={4000}
|
||||||
maxAutoPilotThroughputBaseline={4000}
|
maxAutoPilotThroughputBaseline={4000}
|
||||||
maximum={40000}
|
|
||||||
minimum={6000}
|
minimum={6000}
|
||||||
onAutoPilotSelected={[Function]}
|
onAutoPilotSelected={[Function]}
|
||||||
onMaxAutoPilotThroughputChange={[Function]}
|
onMaxAutoPilotThroughputChange={[Function]}
|
||||||
|
|||||||
@@ -1,6 +1,5 @@
|
|||||||
import { collection, container } from "./TestUtils";
|
import { collection, container } from "./TestUtils";
|
||||||
import {
|
import {
|
||||||
getMaxRUs,
|
|
||||||
getMinRUs,
|
getMinRUs,
|
||||||
getMongoIndexType,
|
getMongoIndexType,
|
||||||
getMongoNotification,
|
getMongoNotification,
|
||||||
@@ -15,18 +14,14 @@ import {
|
|||||||
MongoWildcardPlaceHolder,
|
MongoWildcardPlaceHolder,
|
||||||
getMongoIndexTypeText,
|
getMongoIndexTypeText,
|
||||||
SingleFieldText,
|
SingleFieldText,
|
||||||
WildcardText
|
WildcardText,
|
||||||
|
isIndexTransforming
|
||||||
} from "./SettingsUtils";
|
} from "./SettingsUtils";
|
||||||
import * as DataModels from "../../../Contracts/DataModels";
|
import * as DataModels from "../../../Contracts/DataModels";
|
||||||
import * as ViewModels from "../../../Contracts/ViewModels";
|
import * as ViewModels from "../../../Contracts/ViewModels";
|
||||||
import ko from "knockout";
|
import ko from "knockout";
|
||||||
|
|
||||||
describe("SettingsUtils", () => {
|
describe("SettingsUtils", () => {
|
||||||
it("getMaxRUs", () => {
|
|
||||||
expect(collection.offer().content.collectionThroughputInfo.numPhysicalPartitions).toEqual(4);
|
|
||||||
expect(getMaxRUs(collection, container)).toEqual(40000);
|
|
||||||
});
|
|
||||||
|
|
||||||
it("getMinRUs", () => {
|
it("getMinRUs", () => {
|
||||||
expect(collection.offer().content.collectionThroughputInfo.numPhysicalPartitions).toEqual(4);
|
expect(collection.offer().content.collectionThroughputInfo.numPhysicalPartitions).toEqual(4);
|
||||||
expect(getMinRUs(collection, container)).toEqual(6000);
|
expect(getMinRUs(collection, container)).toEqual(6000);
|
||||||
@@ -139,3 +134,10 @@ describe("SettingsUtils", () => {
|
|||||||
expect(notification.type).toEqual(MongoNotificationType.Error);
|
expect(notification.type).toEqual(MongoNotificationType.Error);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it("isIndexingTransforming", () => {
|
||||||
|
expect(isIndexTransforming(undefined)).toBeFalsy();
|
||||||
|
expect(isIndexTransforming(0)).toBeTruthy();
|
||||||
|
expect(isIndexTransforming(90)).toBeTruthy();
|
||||||
|
expect(isIndexTransforming(100)).toBeFalsy();
|
||||||
|
});
|
||||||
|
|||||||
@@ -1,11 +1,9 @@
|
|||||||
import * as ViewModels from "../../../Contracts/ViewModels";
|
|
||||||
import * as DataModels from "../../../Contracts/DataModels";
|
|
||||||
import * as Constants from "../../../Common/Constants";
|
import * as Constants from "../../../Common/Constants";
|
||||||
|
import * as DataModels from "../../../Contracts/DataModels";
|
||||||
|
import * as ViewModels from "../../../Contracts/ViewModels";
|
||||||
import * as SharedConstants from "../../../Shared/Constants";
|
import * as SharedConstants from "../../../Shared/Constants";
|
||||||
import * as PricingUtils from "../../../Utils/PricingUtils";
|
|
||||||
|
|
||||||
import Explorer from "../../Explorer";
|
|
||||||
import { MongoIndex } from "../../../Utils/arm/generatedClients/2020-04-01/types";
|
import { MongoIndex } from "../../../Utils/arm/generatedClients/2020-04-01/types";
|
||||||
|
import Explorer from "../../Explorer";
|
||||||
|
|
||||||
const zeroValue = 0;
|
const zeroValue = 0;
|
||||||
export type isDirtyTypes = boolean | string | number | DataModels.IndexingPolicy;
|
export type isDirtyTypes = boolean | string | number | DataModels.IndexingPolicy;
|
||||||
@@ -71,22 +69,6 @@ export const hasDatabaseSharedThroughput = (collection: ViewModels.Collection):
|
|||||||
return database?.isDatabaseShared() && !collection.offer();
|
return database?.isDatabaseShared() && !collection.offer();
|
||||||
};
|
};
|
||||||
|
|
||||||
export const getMaxRUs = (collection: ViewModels.Collection, container: Explorer): number => {
|
|
||||||
const isTryCosmosDBSubscription = container?.isTryCosmosDBSubscription() || false;
|
|
||||||
if (isTryCosmosDBSubscription) {
|
|
||||||
return Constants.TryCosmosExperience.maxRU;
|
|
||||||
}
|
|
||||||
|
|
||||||
const numPartitionsFromOffer: number =
|
|
||||||
collection?.offer && collection.offer()?.content?.collectionThroughputInfo?.numPhysicalPartitions;
|
|
||||||
|
|
||||||
const numPartitionsFromQuotaInfo: number = collection?.quotaInfo()?.numPartitions;
|
|
||||||
|
|
||||||
const numPartitions = numPartitionsFromOffer ?? numPartitionsFromQuotaInfo ?? 1;
|
|
||||||
|
|
||||||
return SharedConstants.CollectionCreation.MaxRUPerPartition * numPartitions;
|
|
||||||
};
|
|
||||||
|
|
||||||
export const getMinRUs = (collection: ViewModels.Collection, container: Explorer): number => {
|
export const getMinRUs = (collection: ViewModels.Collection, container: Explorer): number => {
|
||||||
const isTryCosmosDBSubscription = container?.isTryCosmosDBSubscription();
|
const isTryCosmosDBSubscription = container?.isTryCosmosDBSubscription();
|
||||||
if (isTryCosmosDBSubscription) {
|
if (isTryCosmosDBSubscription) {
|
||||||
@@ -105,21 +87,7 @@ export const getMinRUs = (collection: ViewModels.Collection, container: Explorer
|
|||||||
return collectionThroughputInfo.minimumRUForCollection;
|
return collectionThroughputInfo.minimumRUForCollection;
|
||||||
}
|
}
|
||||||
|
|
||||||
const numPartitions = collectionThroughputInfo?.numPhysicalPartitions ?? collection.quotaInfo()?.numPartitions;
|
return SharedConstants.CollectionCreation.DefaultCollectionRUs400;
|
||||||
|
|
||||||
if (!numPartitions || numPartitions === 1) {
|
|
||||||
return SharedConstants.CollectionCreation.DefaultCollectionRUs400;
|
|
||||||
}
|
|
||||||
|
|
||||||
const baseRU = SharedConstants.CollectionCreation.DefaultCollectionRUs400;
|
|
||||||
|
|
||||||
const quotaInKb = collection.quotaInfo().collectionSize;
|
|
||||||
const quotaInGb = PricingUtils.usageInGB(quotaInKb);
|
|
||||||
|
|
||||||
const perPartitionGBQuota: number = Math.max(10, quotaInGb / numPartitions);
|
|
||||||
const baseRUbyPartitions: number = ((numPartitions * perPartitionGBQuota) / 10) * 100;
|
|
||||||
|
|
||||||
return Math.max(baseRU, baseRUbyPartitions);
|
|
||||||
};
|
};
|
||||||
|
|
||||||
export const parseConflictResolutionMode = (modeFromBackend: string): DataModels.ConflictResolutionMode => {
|
export const parseConflictResolutionMode = (modeFromBackend: string): DataModels.ConflictResolutionMode => {
|
||||||
@@ -250,3 +218,7 @@ export const getMongoIndexTypeText = (index: MongoIndexTypes): string => {
|
|||||||
}
|
}
|
||||||
return WildcardText;
|
return WildcardText;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export const isIndexTransforming = (indexTransformationProgress: number): boolean =>
|
||||||
|
// index transformation progress can be 0
|
||||||
|
indexTransformationProgress !== undefined && indexTransformationProgress !== 100;
|
||||||
|
|||||||
@@ -18,7 +18,6 @@ export const collection = ({
|
|||||||
excludedPaths: []
|
excludedPaths: []
|
||||||
}),
|
}),
|
||||||
uniqueKeyPolicy: {} as DataModels.UniqueKeyPolicy,
|
uniqueKeyPolicy: {} as DataModels.UniqueKeyPolicy,
|
||||||
quotaInfo: ko.observable<DataModels.CollectionQuotaInfo>({} as DataModels.CollectionQuotaInfo),
|
|
||||||
offer: ko.observable<DataModels.Offer>({
|
offer: ko.observable<DataModels.Offer>({
|
||||||
content: {
|
content: {
|
||||||
offerThroughput: 10000,
|
offerThroughput: 10000,
|
||||||
|
|||||||
@@ -43,7 +43,6 @@ exports[`SettingsComponent renders 1`] = `
|
|||||||
"autoPilotUsageCost": [Function],
|
"autoPilotUsageCost": [Function],
|
||||||
"canConfigureThroughput": [Function],
|
"canConfigureThroughput": [Function],
|
||||||
"canExceedMaximumValue": [Function],
|
"canExceedMaximumValue": [Function],
|
||||||
"canRequestSupport": [Function],
|
|
||||||
"container": [Circular],
|
"container": [Circular],
|
||||||
"costsVisible": [Function],
|
"costsVisible": [Function],
|
||||||
"databaseCreateNewShared": [Function],
|
"databaseCreateNewShared": [Function],
|
||||||
@@ -86,7 +85,6 @@ exports[`SettingsComponent renders 1`] = `
|
|||||||
"autoPilotUsageCost": [Function],
|
"autoPilotUsageCost": [Function],
|
||||||
"canConfigureThroughput": [Function],
|
"canConfigureThroughput": [Function],
|
||||||
"canExceedMaximumValue": [Function],
|
"canExceedMaximumValue": [Function],
|
||||||
"canRequestSupport": [Function],
|
|
||||||
"collectionId": [Function],
|
"collectionId": [Function],
|
||||||
"collectionIdTitle": [Function],
|
"collectionIdTitle": [Function],
|
||||||
"collectionWithThroughputInShared": [Function],
|
"collectionWithThroughputInShared": [Function],
|
||||||
@@ -349,7 +347,6 @@ exports[`SettingsComponent renders 1`] = `
|
|||||||
"autoPilotUsageCost": [Function],
|
"autoPilotUsageCost": [Function],
|
||||||
"canConfigureThroughput": [Function],
|
"canConfigureThroughput": [Function],
|
||||||
"canExceedMaximumValue": [Function],
|
"canExceedMaximumValue": [Function],
|
||||||
"canRequestSupport": [Function],
|
|
||||||
"container": [Circular],
|
"container": [Circular],
|
||||||
"costsVisible": [Function],
|
"costsVisible": [Function],
|
||||||
"createTableQuery": [Function],
|
"createTableQuery": [Function],
|
||||||
@@ -575,7 +572,6 @@ exports[`SettingsComponent renders 1`] = `
|
|||||||
"autoPilotUsageCost": [Function],
|
"autoPilotUsageCost": [Function],
|
||||||
"canConfigureThroughput": [Function],
|
"canConfigureThroughput": [Function],
|
||||||
"canExceedMaximumValue": [Function],
|
"canExceedMaximumValue": [Function],
|
||||||
"canRequestSupport": [Function],
|
|
||||||
"collectionId": [Function],
|
"collectionId": [Function],
|
||||||
"collectionIdTitle": [Function],
|
"collectionIdTitle": [Function],
|
||||||
"collectionWithThroughputInShared": [Function],
|
"collectionWithThroughputInShared": [Function],
|
||||||
@@ -657,7 +653,6 @@ exports[`SettingsComponent renders 1`] = `
|
|||||||
"autoPilotUsageCost": [Function],
|
"autoPilotUsageCost": [Function],
|
||||||
"canConfigureThroughput": [Function],
|
"canConfigureThroughput": [Function],
|
||||||
"canExceedMaximumValue": [Function],
|
"canExceedMaximumValue": [Function],
|
||||||
"canRequestSupport": [Function],
|
|
||||||
"container": [Circular],
|
"container": [Circular],
|
||||||
"costsVisible": [Function],
|
"costsVisible": [Function],
|
||||||
"databaseCreateNewShared": [Function],
|
"databaseCreateNewShared": [Function],
|
||||||
@@ -760,7 +755,6 @@ exports[`SettingsComponent renders 1`] = `
|
|||||||
"autoPilotUsageCost": [Function],
|
"autoPilotUsageCost": [Function],
|
||||||
"canConfigureThroughput": [Function],
|
"canConfigureThroughput": [Function],
|
||||||
"canExceedMaximumValue": [Function],
|
"canExceedMaximumValue": [Function],
|
||||||
"canRequestSupport": [Function],
|
|
||||||
"container": [Circular],
|
"container": [Circular],
|
||||||
"costsVisible": [Function],
|
"costsVisible": [Function],
|
||||||
"createTableQuery": [Function],
|
"createTableQuery": [Function],
|
||||||
@@ -1301,7 +1295,6 @@ exports[`SettingsComponent renders 1`] = `
|
|||||||
"version": 2,
|
"version": 2,
|
||||||
},
|
},
|
||||||
"partitionKeyProperty": "partitionKey",
|
"partitionKeyProperty": "partitionKey",
|
||||||
"quotaInfo": [Function],
|
|
||||||
"readSettings": [Function],
|
"readSettings": [Function],
|
||||||
"uniqueKeyPolicy": Object {},
|
"uniqueKeyPolicy": Object {},
|
||||||
}
|
}
|
||||||
@@ -1323,7 +1316,6 @@ exports[`SettingsComponent renders 1`] = `
|
|||||||
"autoPilotUsageCost": [Function],
|
"autoPilotUsageCost": [Function],
|
||||||
"canConfigureThroughput": [Function],
|
"canConfigureThroughput": [Function],
|
||||||
"canExceedMaximumValue": [Function],
|
"canExceedMaximumValue": [Function],
|
||||||
"canRequestSupport": [Function],
|
|
||||||
"container": [Circular],
|
"container": [Circular],
|
||||||
"costsVisible": [Function],
|
"costsVisible": [Function],
|
||||||
"databaseCreateNewShared": [Function],
|
"databaseCreateNewShared": [Function],
|
||||||
@@ -1366,7 +1358,6 @@ exports[`SettingsComponent renders 1`] = `
|
|||||||
"autoPilotUsageCost": [Function],
|
"autoPilotUsageCost": [Function],
|
||||||
"canConfigureThroughput": [Function],
|
"canConfigureThroughput": [Function],
|
||||||
"canExceedMaximumValue": [Function],
|
"canExceedMaximumValue": [Function],
|
||||||
"canRequestSupport": [Function],
|
|
||||||
"collectionId": [Function],
|
"collectionId": [Function],
|
||||||
"collectionIdTitle": [Function],
|
"collectionIdTitle": [Function],
|
||||||
"collectionWithThroughputInShared": [Function],
|
"collectionWithThroughputInShared": [Function],
|
||||||
@@ -1629,7 +1620,6 @@ exports[`SettingsComponent renders 1`] = `
|
|||||||
"autoPilotUsageCost": [Function],
|
"autoPilotUsageCost": [Function],
|
||||||
"canConfigureThroughput": [Function],
|
"canConfigureThroughput": [Function],
|
||||||
"canExceedMaximumValue": [Function],
|
"canExceedMaximumValue": [Function],
|
||||||
"canRequestSupport": [Function],
|
|
||||||
"container": [Circular],
|
"container": [Circular],
|
||||||
"costsVisible": [Function],
|
"costsVisible": [Function],
|
||||||
"createTableQuery": [Function],
|
"createTableQuery": [Function],
|
||||||
@@ -1855,7 +1845,6 @@ exports[`SettingsComponent renders 1`] = `
|
|||||||
"autoPilotUsageCost": [Function],
|
"autoPilotUsageCost": [Function],
|
||||||
"canConfigureThroughput": [Function],
|
"canConfigureThroughput": [Function],
|
||||||
"canExceedMaximumValue": [Function],
|
"canExceedMaximumValue": [Function],
|
||||||
"canRequestSupport": [Function],
|
|
||||||
"collectionId": [Function],
|
"collectionId": [Function],
|
||||||
"collectionIdTitle": [Function],
|
"collectionIdTitle": [Function],
|
||||||
"collectionWithThroughputInShared": [Function],
|
"collectionWithThroughputInShared": [Function],
|
||||||
@@ -1937,7 +1926,6 @@ exports[`SettingsComponent renders 1`] = `
|
|||||||
"autoPilotUsageCost": [Function],
|
"autoPilotUsageCost": [Function],
|
||||||
"canConfigureThroughput": [Function],
|
"canConfigureThroughput": [Function],
|
||||||
"canExceedMaximumValue": [Function],
|
"canExceedMaximumValue": [Function],
|
||||||
"canRequestSupport": [Function],
|
|
||||||
"container": [Circular],
|
"container": [Circular],
|
||||||
"costsVisible": [Function],
|
"costsVisible": [Function],
|
||||||
"databaseCreateNewShared": [Function],
|
"databaseCreateNewShared": [Function],
|
||||||
@@ -2040,7 +2028,6 @@ exports[`SettingsComponent renders 1`] = `
|
|||||||
"autoPilotUsageCost": [Function],
|
"autoPilotUsageCost": [Function],
|
||||||
"canConfigureThroughput": [Function],
|
"canConfigureThroughput": [Function],
|
||||||
"canExceedMaximumValue": [Function],
|
"canExceedMaximumValue": [Function],
|
||||||
"canRequestSupport": [Function],
|
|
||||||
"container": [Circular],
|
"container": [Circular],
|
||||||
"costsVisible": [Function],
|
"costsVisible": [Function],
|
||||||
"createTableQuery": [Function],
|
"createTableQuery": [Function],
|
||||||
@@ -2616,7 +2603,6 @@ exports[`SettingsComponent renders 1`] = `
|
|||||||
"autoPilotUsageCost": [Function],
|
"autoPilotUsageCost": [Function],
|
||||||
"canConfigureThroughput": [Function],
|
"canConfigureThroughput": [Function],
|
||||||
"canExceedMaximumValue": [Function],
|
"canExceedMaximumValue": [Function],
|
||||||
"canRequestSupport": [Function],
|
|
||||||
"container": [Circular],
|
"container": [Circular],
|
||||||
"costsVisible": [Function],
|
"costsVisible": [Function],
|
||||||
"databaseCreateNewShared": [Function],
|
"databaseCreateNewShared": [Function],
|
||||||
@@ -2659,7 +2645,6 @@ exports[`SettingsComponent renders 1`] = `
|
|||||||
"autoPilotUsageCost": [Function],
|
"autoPilotUsageCost": [Function],
|
||||||
"canConfigureThroughput": [Function],
|
"canConfigureThroughput": [Function],
|
||||||
"canExceedMaximumValue": [Function],
|
"canExceedMaximumValue": [Function],
|
||||||
"canRequestSupport": [Function],
|
|
||||||
"collectionId": [Function],
|
"collectionId": [Function],
|
||||||
"collectionIdTitle": [Function],
|
"collectionIdTitle": [Function],
|
||||||
"collectionWithThroughputInShared": [Function],
|
"collectionWithThroughputInShared": [Function],
|
||||||
@@ -2922,7 +2907,6 @@ exports[`SettingsComponent renders 1`] = `
|
|||||||
"autoPilotUsageCost": [Function],
|
"autoPilotUsageCost": [Function],
|
||||||
"canConfigureThroughput": [Function],
|
"canConfigureThroughput": [Function],
|
||||||
"canExceedMaximumValue": [Function],
|
"canExceedMaximumValue": [Function],
|
||||||
"canRequestSupport": [Function],
|
|
||||||
"container": [Circular],
|
"container": [Circular],
|
||||||
"costsVisible": [Function],
|
"costsVisible": [Function],
|
||||||
"createTableQuery": [Function],
|
"createTableQuery": [Function],
|
||||||
@@ -3148,7 +3132,6 @@ exports[`SettingsComponent renders 1`] = `
|
|||||||
"autoPilotUsageCost": [Function],
|
"autoPilotUsageCost": [Function],
|
||||||
"canConfigureThroughput": [Function],
|
"canConfigureThroughput": [Function],
|
||||||
"canExceedMaximumValue": [Function],
|
"canExceedMaximumValue": [Function],
|
||||||
"canRequestSupport": [Function],
|
|
||||||
"collectionId": [Function],
|
"collectionId": [Function],
|
||||||
"collectionIdTitle": [Function],
|
"collectionIdTitle": [Function],
|
||||||
"collectionWithThroughputInShared": [Function],
|
"collectionWithThroughputInShared": [Function],
|
||||||
@@ -3230,7 +3213,6 @@ exports[`SettingsComponent renders 1`] = `
|
|||||||
"autoPilotUsageCost": [Function],
|
"autoPilotUsageCost": [Function],
|
||||||
"canConfigureThroughput": [Function],
|
"canConfigureThroughput": [Function],
|
||||||
"canExceedMaximumValue": [Function],
|
"canExceedMaximumValue": [Function],
|
||||||
"canRequestSupport": [Function],
|
|
||||||
"container": [Circular],
|
"container": [Circular],
|
||||||
"costsVisible": [Function],
|
"costsVisible": [Function],
|
||||||
"databaseCreateNewShared": [Function],
|
"databaseCreateNewShared": [Function],
|
||||||
@@ -3333,7 +3315,6 @@ exports[`SettingsComponent renders 1`] = `
|
|||||||
"autoPilotUsageCost": [Function],
|
"autoPilotUsageCost": [Function],
|
||||||
"canConfigureThroughput": [Function],
|
"canConfigureThroughput": [Function],
|
||||||
"canExceedMaximumValue": [Function],
|
"canExceedMaximumValue": [Function],
|
||||||
"canRequestSupport": [Function],
|
|
||||||
"container": [Circular],
|
"container": [Circular],
|
||||||
"costsVisible": [Function],
|
"costsVisible": [Function],
|
||||||
"createTableQuery": [Function],
|
"createTableQuery": [Function],
|
||||||
@@ -3874,7 +3855,6 @@ exports[`SettingsComponent renders 1`] = `
|
|||||||
"version": 2,
|
"version": 2,
|
||||||
},
|
},
|
||||||
"partitionKeyProperty": "partitionKey",
|
"partitionKeyProperty": "partitionKey",
|
||||||
"quotaInfo": [Function],
|
|
||||||
"readSettings": [Function],
|
"readSettings": [Function],
|
||||||
"uniqueKeyPolicy": Object {},
|
"uniqueKeyPolicy": Object {},
|
||||||
}
|
}
|
||||||
@@ -3896,7 +3876,6 @@ exports[`SettingsComponent renders 1`] = `
|
|||||||
"autoPilotUsageCost": [Function],
|
"autoPilotUsageCost": [Function],
|
||||||
"canConfigureThroughput": [Function],
|
"canConfigureThroughput": [Function],
|
||||||
"canExceedMaximumValue": [Function],
|
"canExceedMaximumValue": [Function],
|
||||||
"canRequestSupport": [Function],
|
|
||||||
"container": [Circular],
|
"container": [Circular],
|
||||||
"costsVisible": [Function],
|
"costsVisible": [Function],
|
||||||
"databaseCreateNewShared": [Function],
|
"databaseCreateNewShared": [Function],
|
||||||
@@ -3939,7 +3918,6 @@ exports[`SettingsComponent renders 1`] = `
|
|||||||
"autoPilotUsageCost": [Function],
|
"autoPilotUsageCost": [Function],
|
||||||
"canConfigureThroughput": [Function],
|
"canConfigureThroughput": [Function],
|
||||||
"canExceedMaximumValue": [Function],
|
"canExceedMaximumValue": [Function],
|
||||||
"canRequestSupport": [Function],
|
|
||||||
"collectionId": [Function],
|
"collectionId": [Function],
|
||||||
"collectionIdTitle": [Function],
|
"collectionIdTitle": [Function],
|
||||||
"collectionWithThroughputInShared": [Function],
|
"collectionWithThroughputInShared": [Function],
|
||||||
@@ -4202,7 +4180,6 @@ exports[`SettingsComponent renders 1`] = `
|
|||||||
"autoPilotUsageCost": [Function],
|
"autoPilotUsageCost": [Function],
|
||||||
"canConfigureThroughput": [Function],
|
"canConfigureThroughput": [Function],
|
||||||
"canExceedMaximumValue": [Function],
|
"canExceedMaximumValue": [Function],
|
||||||
"canRequestSupport": [Function],
|
|
||||||
"container": [Circular],
|
"container": [Circular],
|
||||||
"costsVisible": [Function],
|
"costsVisible": [Function],
|
||||||
"createTableQuery": [Function],
|
"createTableQuery": [Function],
|
||||||
@@ -4428,7 +4405,6 @@ exports[`SettingsComponent renders 1`] = `
|
|||||||
"autoPilotUsageCost": [Function],
|
"autoPilotUsageCost": [Function],
|
||||||
"canConfigureThroughput": [Function],
|
"canConfigureThroughput": [Function],
|
||||||
"canExceedMaximumValue": [Function],
|
"canExceedMaximumValue": [Function],
|
||||||
"canRequestSupport": [Function],
|
|
||||||
"collectionId": [Function],
|
"collectionId": [Function],
|
||||||
"collectionIdTitle": [Function],
|
"collectionIdTitle": [Function],
|
||||||
"collectionWithThroughputInShared": [Function],
|
"collectionWithThroughputInShared": [Function],
|
||||||
@@ -4510,7 +4486,6 @@ exports[`SettingsComponent renders 1`] = `
|
|||||||
"autoPilotUsageCost": [Function],
|
"autoPilotUsageCost": [Function],
|
||||||
"canConfigureThroughput": [Function],
|
"canConfigureThroughput": [Function],
|
||||||
"canExceedMaximumValue": [Function],
|
"canExceedMaximumValue": [Function],
|
||||||
"canRequestSupport": [Function],
|
|
||||||
"container": [Circular],
|
"container": [Circular],
|
||||||
"costsVisible": [Function],
|
"costsVisible": [Function],
|
||||||
"databaseCreateNewShared": [Function],
|
"databaseCreateNewShared": [Function],
|
||||||
@@ -4613,7 +4588,6 @@ exports[`SettingsComponent renders 1`] = `
|
|||||||
"autoPilotUsageCost": [Function],
|
"autoPilotUsageCost": [Function],
|
||||||
"canConfigureThroughput": [Function],
|
"canConfigureThroughput": [Function],
|
||||||
"canExceedMaximumValue": [Function],
|
"canExceedMaximumValue": [Function],
|
||||||
"canRequestSupport": [Function],
|
|
||||||
"container": [Circular],
|
"container": [Circular],
|
||||||
"costsVisible": [Function],
|
"costsVisible": [Function],
|
||||||
"createTableQuery": [Function],
|
"createTableQuery": [Function],
|
||||||
@@ -5189,6 +5163,7 @@ exports[`SettingsComponent renders 1`] = `
|
|||||||
logIndexingPolicySuccessMessage={[Function]}
|
logIndexingPolicySuccessMessage={[Function]}
|
||||||
onIndexingPolicyContentChange={[Function]}
|
onIndexingPolicyContentChange={[Function]}
|
||||||
onIndexingPolicyDirtyChange={[Function]}
|
onIndexingPolicyDirtyChange={[Function]}
|
||||||
|
refreshIndexTransformationProgress={[Function]}
|
||||||
resetShouldDiscardIndexingPolicy={[Function]}
|
resetShouldDiscardIndexingPolicy={[Function]}
|
||||||
shouldDiscardIndexingPolicy={false}
|
shouldDiscardIndexingPolicy={false}
|
||||||
/>
|
/>
|
||||||
|
|||||||
@@ -166,15 +166,7 @@ exports[`SettingsUtils functions render 1`] = `
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
>
|
>
|
||||||
Changing the Indexing Policy impacts query results while the index transformation occurs. When a change is made and the indexing mode is set to consistent or lazy, queries return eventual results until the operation completes. For more information see,
|
You have not saved the latest changes made to your indexing policy. Please click save to confirm the changes.
|
||||||
|
|
||||||
<StyledLinkBase
|
|
||||||
href="https://aka.ms/cosmosdb/modify-index-policy"
|
|
||||||
target="_blank"
|
|
||||||
>
|
|
||||||
Modifying Indexing Policies
|
|
||||||
</StyledLinkBase>
|
|
||||||
.
|
|
||||||
</Text>
|
</Text>
|
||||||
<Text
|
<Text
|
||||||
id="updateThroughputBeyondLimitWarningMessage"
|
id="updateThroughputBeyondLimitWarningMessage"
|
||||||
@@ -341,14 +333,30 @@ exports[`SettingsUtils functions render 1`] = `
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
>
|
>
|
||||||
<Text>
|
<Text
|
||||||
|
styles={
|
||||||
|
Object {
|
||||||
|
"root": Object {
|
||||||
|
"fontSize": 12,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
>
|
||||||
Refreshing index transformation progress
|
Refreshing index transformation progress
|
||||||
</Text>
|
</Text>
|
||||||
<StyledSpinnerBase
|
<StyledSpinnerBase
|
||||||
size={2}
|
size={1}
|
||||||
/>
|
/>
|
||||||
</Stack>
|
</Stack>
|
||||||
<Text>
|
<Text
|
||||||
|
styles={
|
||||||
|
Object {
|
||||||
|
"root": Object {
|
||||||
|
"fontSize": 12,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
>
|
||||||
You can make more indexing changes once the current index transformation is complete.
|
You can make more indexing changes once the current index transformation is complete.
|
||||||
<StyledLinkBase
|
<StyledLinkBase
|
||||||
onClick={[Function]}
|
onClick={[Function]}
|
||||||
@@ -356,7 +364,15 @@ exports[`SettingsUtils functions render 1`] = `
|
|||||||
Refresh to check if it has completed.
|
Refresh to check if it has completed.
|
||||||
</StyledLinkBase>
|
</StyledLinkBase>
|
||||||
</Text>
|
</Text>
|
||||||
<Text>
|
<Text
|
||||||
|
styles={
|
||||||
|
Object {
|
||||||
|
"root": Object {
|
||||||
|
"fontSize": 12,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
>
|
||||||
You can make more indexing changes once the current index transformation has completed. It is 90% complete.
|
You can make more indexing changes once the current index transformation has completed. It is 90% complete.
|
||||||
<StyledLinkBase
|
<StyledLinkBase
|
||||||
onClick={[Function]}
|
onClick={[Function]}
|
||||||
|
|||||||
@@ -86,6 +86,7 @@ import { CommandButtonComponentProps } from "./Controls/CommandButton/CommandBut
|
|||||||
import { updateUserContext, userContext } from "../UserContext";
|
import { updateUserContext, userContext } from "../UserContext";
|
||||||
import { stringToBlob } from "../Utils/BlobUtils";
|
import { stringToBlob } from "../Utils/BlobUtils";
|
||||||
import { IChoiceGroupProps } from "office-ui-fabric-react";
|
import { IChoiceGroupProps } from "office-ui-fabric-react";
|
||||||
|
import { getErrorMessage, handleError, getErrorStack } from "../Common/ErrorHandlingUtils";
|
||||||
|
|
||||||
BindingHandlersRegisterer.registerBindingHandlers();
|
BindingHandlersRegisterer.registerBindingHandlers();
|
||||||
// Hold a reference to ComponentRegisterer to prevent transpiler to ignore import
|
// Hold a reference to ComponentRegisterer to prevent transpiler to ignore import
|
||||||
@@ -1039,11 +1040,11 @@ export default class Explorer {
|
|||||||
);
|
);
|
||||||
TelemetryProcessor.traceSuccess(Action.EnableAzureSynapseLink, startTime);
|
TelemetryProcessor.traceSuccess(Action.EnableAzureSynapseLink, startTime);
|
||||||
this.databaseAccount(databaseAccount);
|
this.databaseAccount(databaseAccount);
|
||||||
} catch (e) {
|
} catch (error) {
|
||||||
NotificationConsoleUtils.clearInProgressMessageWithId(logId);
|
NotificationConsoleUtils.clearInProgressMessageWithId(logId);
|
||||||
NotificationConsoleUtils.logConsoleMessage(
|
NotificationConsoleUtils.logConsoleMessage(
|
||||||
ConsoleDataType.Error,
|
ConsoleDataType.Error,
|
||||||
`Enabling Azure Synapse Link for this account failed. ${e.message || JSON.stringify(e)}`
|
`Enabling Azure Synapse Link for this account failed. ${getErrorMessage(error)}`
|
||||||
);
|
);
|
||||||
TelemetryProcessor.traceFailure(Action.EnableAzureSynapseLink, startTime);
|
TelemetryProcessor.traceFailure(Action.EnableAzureSynapseLink, startTime);
|
||||||
} finally {
|
} finally {
|
||||||
@@ -1112,7 +1113,7 @@ export default class Explorer {
|
|||||||
);
|
);
|
||||||
this.renewExplorerShareAccess(this, this.tokenForRenewal())
|
this.renewExplorerShareAccess(this, this.tokenForRenewal())
|
||||||
.fail((error: any) => {
|
.fail((error: any) => {
|
||||||
const stringifiedError: string = error.message;
|
const stringifiedError: string = getErrorMessage(error);
|
||||||
this.renewTokenError("Invalid connection string specified");
|
this.renewTokenError("Invalid connection string specified");
|
||||||
NotificationConsoleUtils.logConsoleMessage(
|
NotificationConsoleUtils.logConsoleMessage(
|
||||||
ConsoleDataType.Error,
|
ConsoleDataType.Error,
|
||||||
@@ -1141,7 +1142,7 @@ export default class Explorer {
|
|||||||
NotificationConsoleUtils.clearInProgressMessageWithId(id);
|
NotificationConsoleUtils.clearInProgressMessageWithId(id);
|
||||||
NotificationConsoleUtils.logConsoleMessage(
|
NotificationConsoleUtils.logConsoleMessage(
|
||||||
ConsoleDataType.Error,
|
ConsoleDataType.Error,
|
||||||
`Failed to generate share url: ${error.message}`
|
`Failed to generate share url: ${getErrorMessage(error)}`
|
||||||
);
|
);
|
||||||
console.error(error);
|
console.error(error);
|
||||||
}
|
}
|
||||||
@@ -1166,7 +1167,10 @@ export default class Explorer {
|
|||||||
deferred.resolve();
|
deferred.resolve();
|
||||||
},
|
},
|
||||||
(error: any) => {
|
(error: any) => {
|
||||||
NotificationConsoleUtils.logConsoleMessage(ConsoleDataType.Error, `Failed to connect: ${error.message}`);
|
NotificationConsoleUtils.logConsoleMessage(
|
||||||
|
ConsoleDataType.Error,
|
||||||
|
`Failed to connect: ${getErrorMessage(error)}`
|
||||||
|
);
|
||||||
deferred.reject(error);
|
deferred.reject(error);
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
@@ -1440,19 +1444,21 @@ export default class Explorer {
|
|||||||
this._setLoadingStatusText("Failed to fetch databases.");
|
this._setLoadingStatusText("Failed to fetch databases.");
|
||||||
this.isRefreshingExplorer(false);
|
this.isRefreshingExplorer(false);
|
||||||
deferred.reject(error);
|
deferred.reject(error);
|
||||||
|
const errorMessage = getErrorMessage(error);
|
||||||
TelemetryProcessor.traceFailure(
|
TelemetryProcessor.traceFailure(
|
||||||
Action.LoadDatabases,
|
Action.LoadDatabases,
|
||||||
{
|
{
|
||||||
databaseAccountName: this.databaseAccount().name,
|
databaseAccountName: this.databaseAccount().name,
|
||||||
defaultExperience: this.defaultExperience(),
|
defaultExperience: this.defaultExperience(),
|
||||||
dataExplorerArea: Constants.Areas.ResourceTree,
|
dataExplorerArea: Constants.Areas.ResourceTree,
|
||||||
error: error.message
|
error: errorMessage,
|
||||||
|
errorStack: getErrorStack(error)
|
||||||
},
|
},
|
||||||
startKey
|
startKey
|
||||||
);
|
);
|
||||||
NotificationConsoleUtils.logConsoleMessage(
|
NotificationConsoleUtils.logConsoleMessage(
|
||||||
ConsoleDataType.Error,
|
ConsoleDataType.Error,
|
||||||
`Error while refreshing databases: ${error.message}`
|
`Error while refreshing databases: ${errorMessage}`
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
@@ -1471,7 +1477,7 @@ export default class Explorer {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
reason => {
|
error => {
|
||||||
if (resourceTreeStartKey != null) {
|
if (resourceTreeStartKey != null) {
|
||||||
TelemetryProcessor.traceFailure(
|
TelemetryProcessor.traceFailure(
|
||||||
Action.LoadResourceTree,
|
Action.LoadResourceTree,
|
||||||
@@ -1479,7 +1485,8 @@ export default class Explorer {
|
|||||||
databaseAccountName: this.databaseAccount() && this.databaseAccount().name,
|
databaseAccountName: this.databaseAccount() && this.databaseAccount().name,
|
||||||
defaultExperience: this.defaultExperience && this.defaultExperience(),
|
defaultExperience: this.defaultExperience && this.defaultExperience(),
|
||||||
dataExplorerArea: Constants.Areas.ResourceTree,
|
dataExplorerArea: Constants.Areas.ResourceTree,
|
||||||
error: reason
|
error: getErrorMessage(error),
|
||||||
|
errorStack: getErrorStack(error)
|
||||||
},
|
},
|
||||||
resourceTreeStartKey
|
resourceTreeStartKey
|
||||||
);
|
);
|
||||||
@@ -1528,7 +1535,7 @@ export default class Explorer {
|
|||||||
resolve(token);
|
resolve(token);
|
||||||
},
|
},
|
||||||
(error: any) => {
|
(error: any) => {
|
||||||
Logger.logError(error, "Explorer/getArcadiaToken");
|
Logger.logError(getErrorMessage(error), "Explorer/getArcadiaToken");
|
||||||
resolve(undefined);
|
resolve(undefined);
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
@@ -1546,7 +1553,7 @@ export default class Explorer {
|
|||||||
workspaceItems[i] = { ...workspace, sparkPools: sparkpools };
|
workspaceItems[i] = { ...workspace, sparkPools: sparkpools };
|
||||||
},
|
},
|
||||||
error => {
|
error => {
|
||||||
Logger.logError(error, "Explorer/this._arcadiaManager.listSparkPoolsAsync");
|
Logger.logError(getErrorMessage(error), "Explorer/this._arcadiaManager.listSparkPoolsAsync");
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
sparkPromises.push(promise);
|
sparkPromises.push(promise);
|
||||||
@@ -1554,8 +1561,7 @@ export default class Explorer {
|
|||||||
|
|
||||||
return Promise.all(sparkPromises).then(() => workspaceItems);
|
return Promise.all(sparkPromises).then(() => workspaceItems);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
Logger.logError(error, "Explorer/this._arcadiaManager.listWorkspacesAsync");
|
handleError(error, "Explorer/this._arcadiaManager.listWorkspacesAsync", "Get Arcadia workspaces failed");
|
||||||
NotificationConsoleUtils.logConsoleMessage(ConsoleDataType.Error, error.message);
|
|
||||||
return Promise.resolve([]);
|
return Promise.resolve([]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1590,10 +1596,10 @@ export default class Explorer {
|
|||||||
);
|
);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
this._isInitializingNotebooks = false;
|
this._isInitializingNotebooks = false;
|
||||||
Logger.logError(error, "initNotebooks/getNotebookConnectionInfoAsync");
|
handleError(
|
||||||
NotificationConsoleUtils.logConsoleMessage(
|
error,
|
||||||
ConsoleDataType.Error,
|
"initNotebooks/getNotebookConnectionInfoAsync",
|
||||||
`Failed to get notebook workspace connection info: ${error.message}`
|
`Failed to get notebook workspace connection info: ${getErrorMessage(error)}`
|
||||||
);
|
);
|
||||||
throw error;
|
throw error;
|
||||||
} finally {
|
} finally {
|
||||||
@@ -1616,9 +1622,10 @@ export default class Explorer {
|
|||||||
|
|
||||||
public resetNotebookWorkspace() {
|
public resetNotebookWorkspace() {
|
||||||
if (!this.isNotebookEnabled() || !this.notebookManager?.notebookClient) {
|
if (!this.isNotebookEnabled() || !this.notebookManager?.notebookClient) {
|
||||||
const error = "Attempt to reset notebook workspace, but notebook is not enabled";
|
handleError(
|
||||||
Logger.logError(error, "Explorer/resetNotebookWorkspace");
|
"Attempt to reset notebook workspace, but notebook is not enabled",
|
||||||
NotificationConsoleUtils.logConsoleMessage(ConsoleDataType.Error, error);
|
"Explorer/resetNotebookWorkspace"
|
||||||
|
);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
const resetConfirmationDialogProps: DialogProps = {
|
const resetConfirmationDialogProps: DialogProps = {
|
||||||
@@ -1643,7 +1650,7 @@ export default class Explorer {
|
|||||||
const workspaces = await this.notebookWorkspaceManager.getNotebookWorkspacesAsync(databaseAccount?.id);
|
const workspaces = await this.notebookWorkspaceManager.getNotebookWorkspacesAsync(databaseAccount?.id);
|
||||||
return workspaces && workspaces.length > 0 && workspaces.some(workspace => workspace.name === "default");
|
return workspaces && workspaces.length > 0 && workspaces.some(workspace => workspace.name === "default");
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
Logger.logError(error, "Explorer/_containsDefaultNotebookWorkspace");
|
Logger.logError(getErrorMessage(error), "Explorer/_containsDefaultNotebookWorkspace");
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1669,8 +1676,7 @@ export default class Explorer {
|
|||||||
await this.notebookWorkspaceManager.startNotebookWorkspaceAsync(this.databaseAccount().id, "default");
|
await this.notebookWorkspaceManager.startNotebookWorkspaceAsync(this.databaseAccount().id, "default");
|
||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
Logger.logError(error, "Explorer/ensureNotebookWorkspaceRunning");
|
handleError(error, "Explorer/ensureNotebookWorkspaceRunning", "Failed to initialize notebook workspace");
|
||||||
NotificationConsoleUtils.logConsoleError(`Failed to initialize notebook workspace: ${error.message}`);
|
|
||||||
} finally {
|
} finally {
|
||||||
clearMessage && clearMessage();
|
clearMessage && clearMessage();
|
||||||
}
|
}
|
||||||
@@ -1685,7 +1691,10 @@ export default class Explorer {
|
|||||||
TelemetryProcessor.traceSuccess(Action.ResetNotebookWorkspace);
|
TelemetryProcessor.traceSuccess(Action.ResetNotebookWorkspace);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
NotificationConsoleUtils.logConsoleMessage(ConsoleDataType.Error, `Failed to reset notebook workspace: ${error}`);
|
NotificationConsoleUtils.logConsoleMessage(ConsoleDataType.Error, `Failed to reset notebook workspace: ${error}`);
|
||||||
TelemetryProcessor.traceFailure(Action.ResetNotebookWorkspace, error);
|
TelemetryProcessor.traceFailure(Action.ResetNotebookWorkspace, {
|
||||||
|
error: getErrorMessage(error),
|
||||||
|
errorStack: getErrorStack(error)
|
||||||
|
});
|
||||||
throw error;
|
throw error;
|
||||||
} finally {
|
} finally {
|
||||||
NotificationConsoleUtils.clearInProgressMessageWithId(id);
|
NotificationConsoleUtils.clearInProgressMessageWithId(id);
|
||||||
@@ -2052,7 +2061,8 @@ export default class Explorer {
|
|||||||
databaseAccountName: this.databaseAccount() && this.databaseAccount().name,
|
databaseAccountName: this.databaseAccount() && this.databaseAccount().name,
|
||||||
defaultExperience: this.defaultExperience && this.defaultExperience(),
|
defaultExperience: this.defaultExperience && this.defaultExperience(),
|
||||||
dataExplorerArea: Constants.Areas.ResourceTree,
|
dataExplorerArea: Constants.Areas.ResourceTree,
|
||||||
trace: error.message
|
error: getErrorMessage(error),
|
||||||
|
errorStack: getErrorStack(error)
|
||||||
},
|
},
|
||||||
startKey
|
startKey
|
||||||
);
|
);
|
||||||
@@ -2218,8 +2228,7 @@ export default class Explorer {
|
|||||||
public uploadFile(name: string, content: string, parent: NotebookContentItem): Promise<NotebookContentItem> {
|
public uploadFile(name: string, content: string, parent: NotebookContentItem): Promise<NotebookContentItem> {
|
||||||
if (!this.isNotebookEnabled() || !this.notebookManager?.notebookContentClient) {
|
if (!this.isNotebookEnabled() || !this.notebookManager?.notebookContentClient) {
|
||||||
const error = "Attempt to upload notebook, but notebook is not enabled";
|
const error = "Attempt to upload notebook, but notebook is not enabled";
|
||||||
Logger.logError(error, "Explorer/uploadFile");
|
handleError(error, "Explorer/uploadFile");
|
||||||
NotificationConsoleUtils.logConsoleMessage(ConsoleDataType.Error, error);
|
|
||||||
throw new Error(error);
|
throw new Error(error);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -2400,8 +2409,7 @@ export default class Explorer {
|
|||||||
public renameNotebook(notebookFile: NotebookContentItem): Q.Promise<NotebookContentItem> {
|
public renameNotebook(notebookFile: NotebookContentItem): Q.Promise<NotebookContentItem> {
|
||||||
if (!this.isNotebookEnabled() || !this.notebookManager?.notebookContentClient) {
|
if (!this.isNotebookEnabled() || !this.notebookManager?.notebookContentClient) {
|
||||||
const error = "Attempt to rename notebook, but notebook is not enabled";
|
const error = "Attempt to rename notebook, but notebook is not enabled";
|
||||||
Logger.logError(error, "Explorer/renameNotebook");
|
handleError(error, "Explorer/renameNotebook");
|
||||||
NotificationConsoleUtils.logConsoleMessage(ConsoleDataType.Error, error);
|
|
||||||
throw new Error(error);
|
throw new Error(error);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -2449,8 +2457,7 @@ export default class Explorer {
|
|||||||
public onCreateDirectory(parent: NotebookContentItem): Q.Promise<NotebookContentItem> {
|
public onCreateDirectory(parent: NotebookContentItem): Q.Promise<NotebookContentItem> {
|
||||||
if (!this.isNotebookEnabled() || !this.notebookManager?.notebookContentClient) {
|
if (!this.isNotebookEnabled() || !this.notebookManager?.notebookContentClient) {
|
||||||
const error = "Attempt to create notebook directory, but notebook is not enabled";
|
const error = "Attempt to create notebook directory, but notebook is not enabled";
|
||||||
Logger.logError(error, "Explorer/onCreateDirectory");
|
handleError(error, "Explorer/onCreateDirectory");
|
||||||
NotificationConsoleUtils.logConsoleMessage(ConsoleDataType.Error, error);
|
|
||||||
throw new Error(error);
|
throw new Error(error);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -2471,8 +2478,7 @@ export default class Explorer {
|
|||||||
public readFile(notebookFile: NotebookContentItem): Promise<string> {
|
public readFile(notebookFile: NotebookContentItem): Promise<string> {
|
||||||
if (!this.isNotebookEnabled() || !this.notebookManager?.notebookContentClient) {
|
if (!this.isNotebookEnabled() || !this.notebookManager?.notebookContentClient) {
|
||||||
const error = "Attempt to read file, but notebook is not enabled";
|
const error = "Attempt to read file, but notebook is not enabled";
|
||||||
Logger.logError(error, "Explorer/downloadFile");
|
handleError(error, "Explorer/downloadFile");
|
||||||
NotificationConsoleUtils.logConsoleMessage(ConsoleDataType.Error, error);
|
|
||||||
throw new Error(error);
|
throw new Error(error);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -2482,8 +2488,7 @@ export default class Explorer {
|
|||||||
public downloadFile(notebookFile: NotebookContentItem): Promise<void> {
|
public downloadFile(notebookFile: NotebookContentItem): Promise<void> {
|
||||||
if (!this.isNotebookEnabled() || !this.notebookManager?.notebookContentClient) {
|
if (!this.isNotebookEnabled() || !this.notebookManager?.notebookContentClient) {
|
||||||
const error = "Attempt to download file, but notebook is not enabled";
|
const error = "Attempt to download file, but notebook is not enabled";
|
||||||
Logger.logError(error, "Explorer/downloadFile");
|
handleError(error, "Explorer/downloadFile");
|
||||||
NotificationConsoleUtils.logConsoleMessage(ConsoleDataType.Error, error);
|
|
||||||
throw new Error(error);
|
throw new Error(error);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -2514,7 +2519,7 @@ export default class Explorer {
|
|||||||
(error: any) => {
|
(error: any) => {
|
||||||
NotificationConsoleUtils.logConsoleMessage(
|
NotificationConsoleUtils.logConsoleMessage(
|
||||||
ConsoleDataType.Error,
|
ConsoleDataType.Error,
|
||||||
`Could not download notebook ${error.message}`
|
`Could not download notebook ${getErrorMessage(error)}`
|
||||||
);
|
);
|
||||||
|
|
||||||
clearMessage();
|
clearMessage();
|
||||||
@@ -2564,7 +2569,7 @@ export default class Explorer {
|
|||||||
);
|
);
|
||||||
this.isNotebooksEnabledForAccount(isAccountInAllowedLocation);
|
this.isNotebooksEnabledForAccount(isAccountInAllowedLocation);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
Logger.logError(error, "Explorer/isNotebooksEnabledForAccount");
|
Logger.logError(getErrorMessage(error), "Explorer/isNotebooksEnabledForAccount");
|
||||||
this.isNotebooksEnabledForAccount(false);
|
this.isNotebooksEnabledForAccount(false);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -2593,7 +2598,7 @@ export default class Explorer {
|
|||||||
false;
|
false;
|
||||||
this.isSparkEnabledForAccount(isEnabled);
|
this.isSparkEnabledForAccount(isEnabled);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
Logger.logError(error, "Explorer/isSparkEnabledForAccount");
|
Logger.logError(getErrorMessage(error), "Explorer/isSparkEnabledForAccount");
|
||||||
this.isSparkEnabledForAccount(false);
|
this.isSparkEnabledForAccount(false);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
@@ -2618,7 +2623,7 @@ export default class Explorer {
|
|||||||
(featureStatus && featureStatus.properties && featureStatus.properties.state === "Registered") || false;
|
(featureStatus && featureStatus.properties && featureStatus.properties.state === "Registered") || false;
|
||||||
return isEnabled;
|
return isEnabled;
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
Logger.logError(error, "Explorer/isSparkEnabledForAccount");
|
Logger.logError(getErrorMessage(error), "Explorer/isSparkEnabledForAccount");
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
@@ -2637,8 +2642,7 @@ export default class Explorer {
|
|||||||
public deleteNotebookFile(item: NotebookContentItem): Promise<void> {
|
public deleteNotebookFile(item: NotebookContentItem): Promise<void> {
|
||||||
if (!this.isNotebookEnabled() || !this.notebookManager?.notebookContentClient) {
|
if (!this.isNotebookEnabled() || !this.notebookManager?.notebookContentClient) {
|
||||||
const error = "Attempt to delete notebook file, but notebook is not enabled";
|
const error = "Attempt to delete notebook file, but notebook is not enabled";
|
||||||
Logger.logError(error, "Explorer/deleteNotebookFile");
|
handleError(error, "Explorer/deleteNotebookFile");
|
||||||
NotificationConsoleUtils.logConsoleMessage(ConsoleDataType.Error, error);
|
|
||||||
throw new Error(error);
|
throw new Error(error);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -2687,8 +2691,7 @@ export default class Explorer {
|
|||||||
public onNewNotebookClicked(parent?: NotebookContentItem): void {
|
public onNewNotebookClicked(parent?: NotebookContentItem): void {
|
||||||
if (!this.isNotebookEnabled() || !this.notebookManager?.notebookContentClient) {
|
if (!this.isNotebookEnabled() || !this.notebookManager?.notebookContentClient) {
|
||||||
const error = "Attempt to create new notebook, but notebook is not enabled";
|
const error = "Attempt to create new notebook, but notebook is not enabled";
|
||||||
Logger.logError(error, "Explorer/onNewNotebookClicked");
|
handleError(error, "Explorer/onNewNotebookClicked");
|
||||||
NotificationConsoleUtils.logConsoleMessage(ConsoleDataType.Error, error);
|
|
||||||
throw new Error(error);
|
throw new Error(error);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -2721,16 +2724,17 @@ export default class Explorer {
|
|||||||
return this.openNotebook(newFile);
|
return this.openNotebook(newFile);
|
||||||
})
|
})
|
||||||
.then(() => this.resourceTree.triggerRender())
|
.then(() => this.resourceTree.triggerRender())
|
||||||
.catch((reason: any) => {
|
.catch((error: any) => {
|
||||||
const error = `Failed to create a new notebook: ${reason}`;
|
const errorMessage = `Failed to create a new notebook: ${getErrorMessage(error)}`;
|
||||||
NotificationConsoleUtils.logConsoleMessage(ConsoleDataType.Error, error);
|
NotificationConsoleUtils.logConsoleMessage(ConsoleDataType.Error, errorMessage);
|
||||||
TelemetryProcessor.traceFailure(
|
TelemetryProcessor.traceFailure(
|
||||||
Action.CreateNewNotebook,
|
Action.CreateNewNotebook,
|
||||||
{
|
{
|
||||||
databaseAccountName: this.databaseAccount().name,
|
databaseAccountName: this.databaseAccount().name,
|
||||||
defaultExperience: this.defaultExperience(),
|
defaultExperience: this.defaultExperience(),
|
||||||
dataExplorerArea: Constants.Areas.Notebook,
|
dataExplorerArea: Constants.Areas.Notebook,
|
||||||
error
|
error: errorMessage,
|
||||||
|
errorStack: getErrorStack(error)
|
||||||
},
|
},
|
||||||
startKey
|
startKey
|
||||||
);
|
);
|
||||||
@@ -2773,8 +2777,7 @@ export default class Explorer {
|
|||||||
public refreshContentItem(item: NotebookContentItem): Promise<void> {
|
public refreshContentItem(item: NotebookContentItem): Promise<void> {
|
||||||
if (!this.isNotebookEnabled() || !this.notebookManager?.notebookContentClient) {
|
if (!this.isNotebookEnabled() || !this.notebookManager?.notebookContentClient) {
|
||||||
const error = "Attempt to refresh notebook list, but notebook is not enabled";
|
const error = "Attempt to refresh notebook list, but notebook is not enabled";
|
||||||
Logger.logError(error, "Explorer/refreshContentItem");
|
handleError(error, "Explorer/refreshContentItem");
|
||||||
NotificationConsoleUtils.logConsoleMessage(ConsoleDataType.Error, error);
|
|
||||||
return Promise.reject(new Error(error));
|
return Promise.reject(new Error(error));
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -2960,7 +2963,7 @@ export default class Explorer {
|
|||||||
}
|
}
|
||||||
return tokenRefreshInterval;
|
return tokenRefreshInterval;
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
Logger.logError(error, "Explorer/getTokenRefreshInterval");
|
Logger.logError(getErrorMessage(error), "Explorer/getTokenRefreshInterval");
|
||||||
return tokenRefreshInterval;
|
return tokenRefreshInterval;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -29,6 +29,7 @@ import { InputProperty } from "../../../Contracts/ViewModels";
|
|||||||
import { QueryIterator, ItemDefinition, Resource } from "@azure/cosmos";
|
import { QueryIterator, ItemDefinition, Resource } from "@azure/cosmos";
|
||||||
import LoadingIndicatorIcon from "../../../../images/LoadingIndicator_3Squares.gif";
|
import LoadingIndicatorIcon from "../../../../images/LoadingIndicator_3Squares.gif";
|
||||||
import { queryDocuments, queryDocumentsPage } from "../../../Common/DocumentClientUtilityBase";
|
import { queryDocuments, queryDocumentsPage } from "../../../Common/DocumentClientUtilityBase";
|
||||||
|
import { getErrorMessage } from "../../../Common/ErrorHandlingUtils";
|
||||||
|
|
||||||
export interface GraphAccessor {
|
export interface GraphAccessor {
|
||||||
applyFilter: () => void;
|
applyFilter: () => void;
|
||||||
@@ -892,7 +893,7 @@ export class GraphExplorer extends React.Component<GraphExplorerProps, GraphExpl
|
|||||||
backendPromise.then(
|
backendPromise.then(
|
||||||
(result: UserQueryResult) => (this.queryTotalRequestCharge = result.requestCharge),
|
(result: UserQueryResult) => (this.queryTotalRequestCharge = result.requestCharge),
|
||||||
(error: any) => {
|
(error: any) => {
|
||||||
const errorMsg = `Failure in submitting query: ${query}: ${error.message}`;
|
const errorMsg = `Failure in submitting query: ${query}: ${getErrorMessage(error)}`;
|
||||||
GraphExplorer.reportToConsole(ConsoleDataType.Error, errorMsg);
|
GraphExplorer.reportToConsole(ConsoleDataType.Error, errorMsg);
|
||||||
this.setState({
|
this.setState({
|
||||||
filterQueryError: errorMsg
|
filterQueryError: errorMsg
|
||||||
@@ -1826,7 +1827,7 @@ export class GraphExplorer extends React.Component<GraphExplorerProps, GraphExpl
|
|||||||
promise
|
promise
|
||||||
.then((result: GremlinClient.GremlinRequestResult) => this.processGremlinQueryResults(result))
|
.then((result: GremlinClient.GremlinRequestResult) => this.processGremlinQueryResults(result))
|
||||||
.catch((error: any) => {
|
.catch((error: any) => {
|
||||||
const errorMsg = `Failed to process query result: ${error.message}`;
|
const errorMsg = `Failed to process query result: ${getErrorMessage(error)}`;
|
||||||
GraphExplorer.reportToConsole(ConsoleDataType.Error, errorMsg);
|
GraphExplorer.reportToConsole(ConsoleDataType.Error, errorMsg);
|
||||||
this.setState({
|
this.setState({
|
||||||
filterQueryError: errorMsg
|
filterQueryError: errorMsg
|
||||||
|
|||||||
@@ -94,7 +94,7 @@ describe("Gremlin Client", () => {
|
|||||||
|
|
||||||
it("should log and display error out on unknown requestId", () => {
|
it("should log and display error out on unknown requestId", () => {
|
||||||
const gremlinClient = new GremlinClient();
|
const gremlinClient = new GremlinClient();
|
||||||
const logConsoleSpy = sinon.spy(NotificationConsoleUtils, "logConsoleMessage");
|
const logConsoleSpy = sinon.spy(NotificationConsoleUtils, "logConsoleError");
|
||||||
const logErrorSpy = sinon.spy(Logger, "logError");
|
const logErrorSpy = sinon.spy(Logger, "logError");
|
||||||
|
|
||||||
gremlinClient.initialize(emptyParams);
|
gremlinClient.initialize(emptyParams);
|
||||||
@@ -122,7 +122,7 @@ describe("Gremlin Client", () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
it("should not aggregate RU if not a number and reset totalRequestCharge to undefined", done => {
|
it("should not aggregate RU if not a number and reset totalRequestCharge to undefined", done => {
|
||||||
const logConsoleSpy = sinon.spy(NotificationConsoleUtils, "logConsoleMessage");
|
const logConsoleSpy = sinon.spy(NotificationConsoleUtils, "logConsoleError");
|
||||||
const logErrorSpy = sinon.spy(Logger, "logError");
|
const logErrorSpy = sinon.spy(Logger, "logError");
|
||||||
|
|
||||||
const gremlinClient = new GremlinClient();
|
const gremlinClient = new GremlinClient();
|
||||||
@@ -165,7 +165,7 @@ describe("Gremlin Client", () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
it("should not aggregate RU if undefined and reset totalRequestCharge to undefined", done => {
|
it("should not aggregate RU if undefined and reset totalRequestCharge to undefined", done => {
|
||||||
const logConsoleSpy = sinon.spy(NotificationConsoleUtils, "logConsoleMessage");
|
const logConsoleSpy = sinon.spy(NotificationConsoleUtils, "logConsoleError");
|
||||||
const logErrorSpy = sinon.spy(Logger, "logError");
|
const logErrorSpy = sinon.spy(Logger, "logError");
|
||||||
|
|
||||||
const gremlinClient = new GremlinClient();
|
const gremlinClient = new GremlinClient();
|
||||||
|
|||||||
@@ -7,7 +7,7 @@ import { GremlinSimpleClient, Result } from "./GremlinSimpleClient";
|
|||||||
import * as NotificationConsoleUtils from "../../../Utils/NotificationConsoleUtils";
|
import * as NotificationConsoleUtils from "../../../Utils/NotificationConsoleUtils";
|
||||||
import { ConsoleDataType } from "../../Menus/NotificationConsole/NotificationConsoleComponent";
|
import { ConsoleDataType } from "../../Menus/NotificationConsole/NotificationConsoleComponent";
|
||||||
import { HashMap } from "../../../Common/HashMap";
|
import { HashMap } from "../../../Common/HashMap";
|
||||||
import * as Logger from "../../../Common/Logger";
|
import { getErrorMessage, handleError } from "../../../Common/ErrorHandlingUtils";
|
||||||
|
|
||||||
export interface GremlinClientParameters {
|
export interface GremlinClientParameters {
|
||||||
endpoint: string;
|
endpoint: string;
|
||||||
@@ -58,26 +58,23 @@ export class GremlinClient {
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
failureCallback: (result: Result, error: any) => {
|
failureCallback: (result: Result, error: any) => {
|
||||||
if (typeof error !== "string") {
|
const errorMessage = getErrorMessage(error);
|
||||||
error = error.message;
|
|
||||||
}
|
|
||||||
|
|
||||||
const requestId = result.requestId;
|
const requestId = result.requestId;
|
||||||
|
|
||||||
if (!requestId || !this.pendingResults.has(requestId)) {
|
if (!requestId || !this.pendingResults.has(requestId)) {
|
||||||
const msg = `Error: ${error}, unknown requestId:${requestId} ${GremlinClient.getRequestChargeString(
|
const errorMsg = `Error: ${errorMessage}, unknown requestId:${requestId} ${GremlinClient.getRequestChargeString(
|
||||||
result.requestCharge
|
result.requestCharge
|
||||||
)}`;
|
)}`;
|
||||||
GremlinClient.reportError(msg);
|
handleError(errorMsg, GremlinClient.LOG_AREA);
|
||||||
|
|
||||||
// Fail all pending requests if no request id (fatal)
|
// Fail all pending requests if no request id (fatal)
|
||||||
if (!requestId) {
|
if (!requestId) {
|
||||||
this.pendingResults.keys().forEach((reqId: string) => {
|
this.pendingResults.keys().forEach((reqId: string) => {
|
||||||
this.abortPendingRequest(reqId, error, null);
|
this.abortPendingRequest(reqId, errorMessage, null);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
this.abortPendingRequest(requestId, error, result.requestCharge);
|
this.abortPendingRequest(requestId, errorMessage, result.requestCharge);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
infoCallback: (msg: string) => {
|
infoCallback: (msg: string) => {
|
||||||
@@ -132,15 +129,16 @@ export class GremlinClient {
|
|||||||
deferred.reject(error);
|
deferred.reject(error);
|
||||||
this.pendingResults.delete(requestId);
|
this.pendingResults.delete(requestId);
|
||||||
|
|
||||||
GremlinClient.reportError(
|
const errorMsg = `Aborting pending request ${requestId}. Error:${error} ${GremlinClient.getRequestChargeString(
|
||||||
`Aborting pending request ${requestId}. Error:${error} ${GremlinClient.getRequestChargeString(requestCharge)}`
|
requestCharge
|
||||||
);
|
)}`;
|
||||||
|
handleError(errorMsg, GremlinClient.LOG_AREA);
|
||||||
}
|
}
|
||||||
|
|
||||||
private flushResult(requestId: string) {
|
private flushResult(requestId: string) {
|
||||||
if (!this.pendingResults.has(requestId)) {
|
if (!this.pendingResults.has(requestId)) {
|
||||||
const msg = `Unknown requestId:${requestId}`;
|
const errorMsg = `Unknown requestId:${requestId}`;
|
||||||
GremlinClient.reportError(msg);
|
handleError(errorMsg, GremlinClient.LOG_AREA);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -158,8 +156,8 @@ export class GremlinClient {
|
|||||||
*/
|
*/
|
||||||
private storePendingResult(result: Result): boolean {
|
private storePendingResult(result: Result): boolean {
|
||||||
if (!this.pendingResults.has(result.requestId)) {
|
if (!this.pendingResults.has(result.requestId)) {
|
||||||
const msg = `Dropping result for unknown requestId:${result.requestId}`;
|
const errorMsg = `Dropping result for unknown requestId:${result.requestId}`;
|
||||||
GremlinClient.reportError(msg);
|
handleError(errorMsg, GremlinClient.LOG_AREA);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
const pendingResults = this.pendingResults.get(result.requestId).result;
|
const pendingResults = this.pendingResults.get(result.requestId).result;
|
||||||
@@ -179,9 +177,8 @@ export class GremlinClient {
|
|||||||
if (result.requestCharge === undefined || typeof result.requestCharge !== "number") {
|
if (result.requestCharge === undefined || typeof result.requestCharge !== "number") {
|
||||||
// Clear totalRequestCharge, even if it was a valid number as the total might be incomplete therefore incorrect
|
// Clear totalRequestCharge, even if it was a valid number as the total might be incomplete therefore incorrect
|
||||||
pendingResults.totalRequestCharge = undefined;
|
pendingResults.totalRequestCharge = undefined;
|
||||||
GremlinClient.reportError(
|
const errorMsg = `Unable to perform RU aggregation calculation with non numbers. Result request charge: ${result.requestCharge}. RequestId: ${result.requestId}`;
|
||||||
`Unable to perform RU aggregation calculation with non numbers. Result request charge: ${result.requestCharge}. RequestId: ${result.requestId}`
|
handleError(errorMsg, GremlinClient.LOG_AREA);
|
||||||
);
|
|
||||||
} else {
|
} else {
|
||||||
if (pendingResults.totalRequestCharge === undefined) {
|
if (pendingResults.totalRequestCharge === undefined) {
|
||||||
pendingResults.totalRequestCharge = 0;
|
pendingResults.totalRequestCharge = 0;
|
||||||
@@ -190,9 +187,4 @@ export class GremlinClient {
|
|||||||
}
|
}
|
||||||
return pendingResults.isIncomplete;
|
return pendingResults.isIncomplete;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static reportError(msg: string): void {
|
|
||||||
NotificationConsoleUtils.logConsoleMessage(ConsoleDataType.Error, msg);
|
|
||||||
Logger.logError(msg, GremlinClient.LOG_AREA);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -6,6 +6,7 @@ import * as Constants from "../../Common/Constants";
|
|||||||
import { ConsoleDataType } from "../Menus/NotificationConsole/NotificationConsoleComponent";
|
import { ConsoleDataType } from "../Menus/NotificationConsole/NotificationConsoleComponent";
|
||||||
import * as NotificationConsoleUtils from "../../Utils/NotificationConsoleUtils";
|
import * as NotificationConsoleUtils from "../../Utils/NotificationConsoleUtils";
|
||||||
import * as Logger from "../../Common/Logger";
|
import * as Logger from "../../Common/Logger";
|
||||||
|
import { getErrorMessage } from "../../Common/ErrorHandlingUtils";
|
||||||
|
|
||||||
export class NotebookContainerClient {
|
export class NotebookContainerClient {
|
||||||
private reconnectingNotificationId: string;
|
private reconnectingNotificationId: string;
|
||||||
@@ -74,7 +75,7 @@ export class NotebookContainerClient {
|
|||||||
}
|
}
|
||||||
return undefined;
|
return undefined;
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
Logger.logError(error, "NotebookContainerClient/getMemoryUsage");
|
Logger.logError(getErrorMessage(error), "NotebookContainerClient/getMemoryUsage");
|
||||||
if (!this.reconnectingNotificationId) {
|
if (!this.reconnectingNotificationId) {
|
||||||
this.reconnectingNotificationId = NotificationConsoleUtils.logConsoleMessage(
|
this.reconnectingNotificationId = NotificationConsoleUtils.logConsoleMessage(
|
||||||
ConsoleDataType.InProgress,
|
ConsoleDataType.InProgress,
|
||||||
@@ -110,7 +111,7 @@ export class NotebookContainerClient {
|
|||||||
headers: { Authorization: authToken }
|
headers: { Authorization: authToken }
|
||||||
});
|
});
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
Logger.logError(error, "NotebookContainerClient/resetWorkspace");
|
Logger.logError(getErrorMessage(error), "NotebookContainerClient/resetWorkspace");
|
||||||
await this.recreateNotebookWorkspaceAsync();
|
await this.recreateNotebookWorkspaceAsync();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -140,7 +141,7 @@ export class NotebookContainerClient {
|
|||||||
await notebookWorkspaceManager.deleteNotebookWorkspaceAsync(explorer.databaseAccount().id, "default");
|
await notebookWorkspaceManager.deleteNotebookWorkspaceAsync(explorer.databaseAccount().id, "default");
|
||||||
await notebookWorkspaceManager.createNotebookWorkspaceAsync(explorer.databaseAccount().id, "default");
|
await notebookWorkspaceManager.createNotebookWorkspaceAsync(explorer.databaseAccount().id, "default");
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
Logger.logError(error, "NotebookContainerClient/recreateNotebookWorkspaceAsync");
|
Logger.logError(getErrorMessage(error), "NotebookContainerClient/recreateNotebookWorkspaceAsync");
|
||||||
return Promise.reject(error);
|
return Promise.reject(error);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -26,6 +26,7 @@ import { ImmutableNotebook } from "@nteract/commutable";
|
|||||||
import Explorer from "../Explorer";
|
import Explorer from "../Explorer";
|
||||||
import { ContextualPaneBase } from "../Panes/ContextualPaneBase";
|
import { ContextualPaneBase } from "../Panes/ContextualPaneBase";
|
||||||
import { CopyNotebookPaneAdapter } from "../Panes/CopyNotebookPane";
|
import { CopyNotebookPaneAdapter } from "../Panes/CopyNotebookPane";
|
||||||
|
import { getErrorMessage } from "../../Common/ErrorHandlingUtils";
|
||||||
|
|
||||||
export interface NotebookManagerOptions {
|
export interface NotebookManagerOptions {
|
||||||
container: Explorer;
|
container: Explorer;
|
||||||
@@ -147,7 +148,7 @@ export default class NotebookManager {
|
|||||||
// Octokit's error handler uses any
|
// Octokit's error handler uses any
|
||||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||||
private onGitHubClientError = (error: any): void => {
|
private onGitHubClientError = (error: any): void => {
|
||||||
Logger.logError(error, "NotebookManager/onGitHubClientError");
|
Logger.logError(getErrorMessage(error), "NotebookManager/onGitHubClientError");
|
||||||
|
|
||||||
if (error.status === HttpStatusCodes.Unauthorized) {
|
if (error.status === HttpStatusCodes.Unauthorized) {
|
||||||
this.gitHubOAuthService.resetToken();
|
this.gitHubOAuthService.resetToken();
|
||||||
|
|||||||
@@ -288,7 +288,7 @@
|
|||||||
range of values and is likely to have evenly distributed access patterns.</span>
|
range of values and is likely to have evenly distributed access patterns.</span>
|
||||||
</span>
|
</span>
|
||||||
</p>
|
</p>
|
||||||
<input type="text" data-test="addCollection-partitionKeyValue" aria-required="true" size="40"
|
<input type="text" id="partitionKeyValue" data-test="addCollection-partitionKeyValue" aria-required="true" size="40"
|
||||||
class="textfontclr collid" data-bind="textInput: partitionKey,
|
class="textfontclr collid" data-bind="textInput: partitionKey,
|
||||||
attr: {
|
attr: {
|
||||||
placeholder: partitionKeyPlaceholder,
|
placeholder: partitionKeyPlaceholder,
|
||||||
|
|||||||
@@ -3,7 +3,6 @@ import * as AddCollectionUtility from "../../Shared/AddCollectionUtility";
|
|||||||
import * as AutoPilotUtils from "../../Utils/AutoPilotUtils";
|
import * as AutoPilotUtils from "../../Utils/AutoPilotUtils";
|
||||||
import * as Constants from "../../Common/Constants";
|
import * as Constants from "../../Common/Constants";
|
||||||
import * as DataModels from "../../Contracts/DataModels";
|
import * as DataModels from "../../Contracts/DataModels";
|
||||||
import * as ErrorParserUtility from "../../Common/ErrorParserUtility";
|
|
||||||
import * as ko from "knockout";
|
import * as ko from "knockout";
|
||||||
import * as PricingUtils from "../../Utils/PricingUtils";
|
import * as PricingUtils from "../../Utils/PricingUtils";
|
||||||
import * as SharedConstants from "../../Shared/Constants";
|
import * as SharedConstants from "../../Shared/Constants";
|
||||||
@@ -15,6 +14,7 @@ import { configContext, Platform } from "../../ConfigContext";
|
|||||||
import { ContextualPaneBase } from "./ContextualPaneBase";
|
import { ContextualPaneBase } from "./ContextualPaneBase";
|
||||||
import { DynamicListItem } from "../Controls/DynamicList/DynamicListComponent";
|
import { DynamicListItem } from "../Controls/DynamicList/DynamicListComponent";
|
||||||
import { createCollection } from "../../Common/dataAccess/createCollection";
|
import { createCollection } from "../../Common/dataAccess/createCollection";
|
||||||
|
import { getErrorMessage, getErrorStack } from "../../Common/ErrorHandlingUtils";
|
||||||
|
|
||||||
export interface AddCollectionPaneOptions extends ViewModels.PaneOptions {
|
export interface AddCollectionPaneOptions extends ViewModels.PaneOptions {
|
||||||
isPreferredApiTable: ko.Computed<boolean>;
|
isPreferredApiTable: ko.Computed<boolean>;
|
||||||
@@ -61,7 +61,6 @@ export default class AddCollectionPane extends ContextualPaneBase {
|
|||||||
public maxCollectionsReachedMessage: ko.Observable<string>;
|
public maxCollectionsReachedMessage: ko.Observable<string>;
|
||||||
public requestUnitsUsageCost: ko.Computed<string>;
|
public requestUnitsUsageCost: ko.Computed<string>;
|
||||||
public dedicatedRequestUnitsUsageCost: ko.Computed<string>;
|
public dedicatedRequestUnitsUsageCost: ko.Computed<string>;
|
||||||
public canRequestSupport: ko.PureComputed<boolean>;
|
|
||||||
public largePartitionKey: ko.Observable<boolean> = ko.observable<boolean>(false);
|
public largePartitionKey: ko.Observable<boolean> = ko.observable<boolean>(false);
|
||||||
public useIndexingForSharedThroughput: ko.Observable<boolean> = ko.observable<boolean>(true);
|
public useIndexingForSharedThroughput: ko.Observable<boolean> = ko.observable<boolean>(true);
|
||||||
public costsVisible: ko.PureComputed<boolean>;
|
public costsVisible: ko.PureComputed<boolean>;
|
||||||
@@ -314,19 +313,6 @@ export default class AddCollectionPane extends ContextualPaneBase {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
this.canRequestSupport = ko.pureComputed(() => {
|
|
||||||
if (
|
|
||||||
configContext.platform !== Platform.Emulator &&
|
|
||||||
!this.container.isTryCosmosDBSubscription() &&
|
|
||||||
configContext.platform !== Platform.Portal
|
|
||||||
) {
|
|
||||||
const offerThroughput: number = this._getThroughput();
|
|
||||||
return offerThroughput <= 100000;
|
|
||||||
}
|
|
||||||
|
|
||||||
return false;
|
|
||||||
});
|
|
||||||
|
|
||||||
this.costsVisible = ko.pureComputed(() => {
|
this.costsVisible = ko.pureComputed(() => {
|
||||||
return configContext.platform !== Platform.Emulator;
|
return configContext.platform !== Platform.Emulator;
|
||||||
});
|
});
|
||||||
@@ -881,10 +867,9 @@ export default class AddCollectionPane extends ContextualPaneBase {
|
|||||||
this.resetData();
|
this.resetData();
|
||||||
this.container.refreshAllDatabases();
|
this.container.refreshAllDatabases();
|
||||||
},
|
},
|
||||||
(reason: any) => {
|
(error: any) => {
|
||||||
this.isExecuting(false);
|
this.isExecuting(false);
|
||||||
const message = ErrorParserUtility.parse(reason);
|
const errorMessage: string = getErrorMessage(error);
|
||||||
const errorMessage = ErrorParserUtility.replaceKnownError(message[0].message);
|
|
||||||
this.formErrors(errorMessage);
|
this.formErrors(errorMessage);
|
||||||
this.formErrorsDetails(errorMessage);
|
this.formErrorsDetails(errorMessage);
|
||||||
const addCollectionPaneFailedMessage = {
|
const addCollectionPaneFailedMessage = {
|
||||||
@@ -912,7 +897,8 @@ export default class AddCollectionPane extends ContextualPaneBase {
|
|||||||
flight: this.container.flight()
|
flight: this.container.flight()
|
||||||
},
|
},
|
||||||
dataExplorerArea: Constants.Areas.ContextualPane,
|
dataExplorerArea: Constants.Areas.ContextualPane,
|
||||||
error: reason
|
error: errorMessage,
|
||||||
|
errorStack: getErrorStack(error)
|
||||||
};
|
};
|
||||||
TelemetryProcessor.traceFailure(Action.CreateCollection, addCollectionPaneFailedMessage, startKey);
|
TelemetryProcessor.traceFailure(Action.CreateCollection, addCollectionPaneFailedMessage, startKey);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -117,10 +117,6 @@
|
|||||||
showAutoPilot: !isFreeTierAccount()
|
showAutoPilot: !isFreeTierAccount()
|
||||||
}">
|
}">
|
||||||
</throughput-input-autopilot-v3>
|
</throughput-input-autopilot-v3>
|
||||||
<p data-bind="visible: canRequestSupport">
|
|
||||||
<!-- TODO: Replace link with call to the Azure Support blade --><a
|
|
||||||
href="https://aka.ms/cosmosdbfeedback?subject=Cosmos%20DB%20More%20Throughput%20Request">Contact
|
|
||||||
support</a> for more than <span data-bind="text: maxThroughputRUText"></span> RU/s.</p>
|
|
||||||
</div>
|
</div>
|
||||||
<!-- /ko -->
|
<!-- /ko -->
|
||||||
<!-- Database provisioned throughput - End -->
|
<!-- Database provisioned throughput - End -->
|
||||||
|
|||||||
@@ -1,7 +1,6 @@
|
|||||||
import * as AutoPilotUtils from "../../Utils/AutoPilotUtils";
|
import * as AutoPilotUtils from "../../Utils/AutoPilotUtils";
|
||||||
import * as Constants from "../../Common/Constants";
|
import * as Constants from "../../Common/Constants";
|
||||||
import * as DataModels from "../../Contracts/DataModels";
|
import * as DataModels from "../../Contracts/DataModels";
|
||||||
import * as ErrorParserUtility from "../../Common/ErrorParserUtility";
|
|
||||||
import * as ko from "knockout";
|
import * as ko from "knockout";
|
||||||
import * as PricingUtils from "../../Utils/PricingUtils";
|
import * as PricingUtils from "../../Utils/PricingUtils";
|
||||||
import * as SharedConstants from "../../Shared/Constants";
|
import * as SharedConstants from "../../Shared/Constants";
|
||||||
@@ -12,6 +11,7 @@ import { Action, ActionModifiers } from "../../Shared/Telemetry/TelemetryConstan
|
|||||||
import { ContextualPaneBase } from "./ContextualPaneBase";
|
import { ContextualPaneBase } from "./ContextualPaneBase";
|
||||||
import { createDatabase } from "../../Common/dataAccess/createDatabase";
|
import { createDatabase } from "../../Common/dataAccess/createDatabase";
|
||||||
import { configContext, Platform } from "../../ConfigContext";
|
import { configContext, Platform } from "../../ConfigContext";
|
||||||
|
import { getErrorMessage, getErrorStack } from "../../Common/ErrorHandlingUtils";
|
||||||
|
|
||||||
export default class AddDatabasePane extends ContextualPaneBase {
|
export default class AddDatabasePane extends ContextualPaneBase {
|
||||||
public defaultExperience: ko.Computed<string>;
|
public defaultExperience: ko.Computed<string>;
|
||||||
@@ -31,7 +31,6 @@ export default class AddDatabasePane extends ContextualPaneBase {
|
|||||||
public throughputSpendAck: ko.Observable<boolean>;
|
public throughputSpendAck: ko.Observable<boolean>;
|
||||||
public throughputSpendAckVisible: ko.Computed<boolean>;
|
public throughputSpendAckVisible: ko.Computed<boolean>;
|
||||||
public requestUnitsUsageCost: ko.Computed<string>;
|
public requestUnitsUsageCost: ko.Computed<string>;
|
||||||
public canRequestSupport: ko.PureComputed<boolean>;
|
|
||||||
public costsVisible: ko.PureComputed<boolean>;
|
public costsVisible: ko.PureComputed<boolean>;
|
||||||
public upsellMessage: ko.PureComputed<string>;
|
public upsellMessage: ko.PureComputed<string>;
|
||||||
public upsellMessageAriaLabel: ko.PureComputed<string>;
|
public upsellMessageAriaLabel: ko.PureComputed<string>;
|
||||||
@@ -168,19 +167,6 @@ export default class AddDatabasePane extends ContextualPaneBase {
|
|||||||
return estimatedSpend;
|
return estimatedSpend;
|
||||||
});
|
});
|
||||||
|
|
||||||
this.canRequestSupport = ko.pureComputed(() => {
|
|
||||||
if (
|
|
||||||
configContext.platform !== Platform.Emulator &&
|
|
||||||
!this.container.isTryCosmosDBSubscription() &&
|
|
||||||
configContext.platform !== Platform.Portal
|
|
||||||
) {
|
|
||||||
const offerThroughput: number = this.throughput();
|
|
||||||
return offerThroughput <= 100000;
|
|
||||||
}
|
|
||||||
|
|
||||||
return false;
|
|
||||||
});
|
|
||||||
|
|
||||||
this.isFreeTierAccount = ko.computed<boolean>(() => {
|
this.isFreeTierAccount = ko.computed<boolean>(() => {
|
||||||
const databaseAccount = this.container && this.container.databaseAccount && this.container.databaseAccount();
|
const databaseAccount = this.container && this.container.databaseAccount && this.container.databaseAccount();
|
||||||
const isFreeTierAccount =
|
const isFreeTierAccount =
|
||||||
@@ -296,18 +282,22 @@ export default class AddDatabasePane extends ContextualPaneBase {
|
|||||||
this.isExecuting(true);
|
this.isExecuting(true);
|
||||||
|
|
||||||
const createDatabaseParams: DataModels.CreateDatabaseParams = {
|
const createDatabaseParams: DataModels.CreateDatabaseParams = {
|
||||||
autoPilotMaxThroughput: this.maxAutoPilotThroughputSet(),
|
|
||||||
databaseId: addDatabasePaneStartMessage.database.id,
|
databaseId: addDatabasePaneStartMessage.database.id,
|
||||||
databaseLevelThroughput: addDatabasePaneStartMessage.database.shared,
|
databaseLevelThroughput: addDatabasePaneStartMessage.database.shared
|
||||||
offerThroughput: addDatabasePaneStartMessage.offerThroughput
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
if (this.isAutoPilotSelected()) {
|
||||||
|
createDatabaseParams.autoPilotMaxThroughput = this.maxAutoPilotThroughputSet();
|
||||||
|
} else {
|
||||||
|
createDatabaseParams.offerThroughput = addDatabasePaneStartMessage.offerThroughput;
|
||||||
|
}
|
||||||
|
|
||||||
createDatabase(createDatabaseParams).then(
|
createDatabase(createDatabaseParams).then(
|
||||||
(database: DataModels.Database) => {
|
(database: DataModels.Database) => {
|
||||||
this._onCreateDatabaseSuccess(offerThroughput, startKey);
|
this._onCreateDatabaseSuccess(offerThroughput, startKey);
|
||||||
},
|
},
|
||||||
(reason: any) => {
|
(error: any) => {
|
||||||
this._onCreateDatabaseFailure(reason, offerThroughput, reason);
|
this._onCreateDatabaseFailure(error, offerThroughput, startKey);
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@@ -356,11 +346,11 @@ export default class AddDatabasePane extends ContextualPaneBase {
|
|||||||
this.resetData();
|
this.resetData();
|
||||||
}
|
}
|
||||||
|
|
||||||
private _onCreateDatabaseFailure(reason: any, offerThroughput: number, startKey: number): void {
|
private _onCreateDatabaseFailure(error: any, offerThroughput: number, startKey: number): void {
|
||||||
this.isExecuting(false);
|
this.isExecuting(false);
|
||||||
const message = ErrorParserUtility.parse(reason);
|
const errorMessage = getErrorMessage(error);
|
||||||
this.formErrors(message[0].message);
|
this.formErrors(errorMessage);
|
||||||
this.formErrorsDetails(message[0].message);
|
this.formErrorsDetails(errorMessage);
|
||||||
const addDatabasePaneFailedMessage = {
|
const addDatabasePaneFailedMessage = {
|
||||||
databaseAccountName: this.container.databaseAccount().name,
|
databaseAccountName: this.container.databaseAccount().name,
|
||||||
defaultExperience: this.container.defaultExperience(),
|
defaultExperience: this.container.defaultExperience(),
|
||||||
@@ -375,7 +365,8 @@ export default class AddDatabasePane extends ContextualPaneBase {
|
|||||||
flight: this.container.flight()
|
flight: this.container.flight()
|
||||||
},
|
},
|
||||||
dataExplorerArea: Constants.Areas.ContextualPane,
|
dataExplorerArea: Constants.Areas.ContextualPane,
|
||||||
error: reason
|
error: errorMessage,
|
||||||
|
errorStack: getErrorStack(error)
|
||||||
};
|
};
|
||||||
TelemetryProcessor.traceFailure(Action.CreateDatabase, addDatabasePaneFailedMessage, startKey);
|
TelemetryProcessor.traceFailure(Action.CreateDatabase, addDatabasePaneFailedMessage, startKey);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -7,6 +7,7 @@ import * as Logger from "../../Common/Logger";
|
|||||||
import { QueriesGridComponentAdapter } from "../Controls/QueriesGridReactComponent/QueriesGridComponentAdapter";
|
import { QueriesGridComponentAdapter } from "../Controls/QueriesGridReactComponent/QueriesGridComponentAdapter";
|
||||||
import * as TelemetryProcessor from "../../Shared/Telemetry/TelemetryProcessor";
|
import * as TelemetryProcessor from "../../Shared/Telemetry/TelemetryProcessor";
|
||||||
import QueryTab from "../Tabs/QueryTab";
|
import QueryTab from "../Tabs/QueryTab";
|
||||||
|
import { getErrorMessage, getErrorStack } from "../../Common/ErrorHandlingUtils";
|
||||||
|
|
||||||
export class BrowseQueriesPane extends ContextualPaneBase {
|
export class BrowseQueriesPane extends ContextualPaneBase {
|
||||||
public queriesGridComponentAdapter: QueriesGridComponentAdapter;
|
public queriesGridComponentAdapter: QueriesGridComponentAdapter;
|
||||||
@@ -60,17 +61,20 @@ export class BrowseQueriesPane extends ContextualPaneBase {
|
|||||||
startKey
|
startKey
|
||||||
);
|
);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
|
const errorMessage = getErrorMessage(error);
|
||||||
TelemetryProcessor.traceFailure(
|
TelemetryProcessor.traceFailure(
|
||||||
Action.SetupSavedQueries,
|
Action.SetupSavedQueries,
|
||||||
{
|
{
|
||||||
databaseAccountName: this.container && this.container.databaseAccount().name,
|
databaseAccountName: this.container && this.container.databaseAccount().name,
|
||||||
defaultExperience: this.container && this.container.defaultExperience(),
|
defaultExperience: this.container && this.container.defaultExperience(),
|
||||||
dataExplorerArea: Areas.ContextualPane,
|
dataExplorerArea: Areas.ContextualPane,
|
||||||
paneTitle: this.title()
|
paneTitle: this.title(),
|
||||||
|
error: errorMessage,
|
||||||
|
errorStack: getErrorStack(error)
|
||||||
},
|
},
|
||||||
startKey
|
startKey
|
||||||
);
|
);
|
||||||
this.formErrors(`Failed to setup a collection for saved queries: ${error.message}`);
|
this.formErrors(`Failed to setup a collection for saved queries: ${errorMessage}`);
|
||||||
} finally {
|
} finally {
|
||||||
this.isExecuting(false);
|
this.isExecuting(false);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -13,6 +13,7 @@ import { CassandraAPIDataClient } from "../Tables/TableDataClient";
|
|||||||
import { ContextualPaneBase } from "./ContextualPaneBase";
|
import { ContextualPaneBase } from "./ContextualPaneBase";
|
||||||
import { HashMap } from "../../Common/HashMap";
|
import { HashMap } from "../../Common/HashMap";
|
||||||
import { configContext, Platform } from "../../ConfigContext";
|
import { configContext, Platform } from "../../ConfigContext";
|
||||||
|
import { getErrorMessage, getErrorStack } from "../../Common/ErrorHandlingUtils";
|
||||||
|
|
||||||
export default class CassandraAddCollectionPane extends ContextualPaneBase {
|
export default class CassandraAddCollectionPane extends ContextualPaneBase {
|
||||||
public createTableQuery: ko.Observable<string>;
|
public createTableQuery: ko.Observable<string>;
|
||||||
@@ -32,7 +33,6 @@ export default class CassandraAddCollectionPane extends ContextualPaneBase {
|
|||||||
public keyspaceThroughput: ko.Observable<number>;
|
public keyspaceThroughput: ko.Observable<number>;
|
||||||
public keyspaceCreateNew: ko.Observable<boolean>;
|
public keyspaceCreateNew: ko.Observable<boolean>;
|
||||||
public dedicateTableThroughput: ko.Observable<boolean>;
|
public dedicateTableThroughput: ko.Observable<boolean>;
|
||||||
public canRequestSupport: ko.PureComputed<boolean>;
|
|
||||||
public throughputSpendAckText: ko.Observable<string>;
|
public throughputSpendAckText: ko.Observable<string>;
|
||||||
public throughputSpendAck: ko.Observable<boolean>;
|
public throughputSpendAck: ko.Observable<boolean>;
|
||||||
public sharedThroughputSpendAck: ko.Observable<boolean>;
|
public sharedThroughputSpendAck: ko.Observable<boolean>;
|
||||||
@@ -227,15 +227,6 @@ export default class CassandraAddCollectionPane extends ContextualPaneBase {
|
|||||||
return configContext.platform !== Platform.Emulator;
|
return configContext.platform !== Platform.Emulator;
|
||||||
});
|
});
|
||||||
|
|
||||||
this.canRequestSupport = ko.pureComputed(() => {
|
|
||||||
if (configContext.platform !== Platform.Emulator && !this.container.isTryCosmosDBSubscription()) {
|
|
||||||
const offerThroughput: number = this.throughput();
|
|
||||||
return offerThroughput <= 100000;
|
|
||||||
}
|
|
||||||
|
|
||||||
return false;
|
|
||||||
});
|
|
||||||
|
|
||||||
this.sharedThroughputSpendAckVisible = ko.computed<boolean>(() => {
|
this.sharedThroughputSpendAckVisible = ko.computed<boolean>(() => {
|
||||||
const autoscaleThroughput = this.sharedAutoPilotThroughput() * 1;
|
const autoscaleThroughput = this.sharedAutoPilotThroughput() * 1;
|
||||||
if (this.isSharedAutoPilotSelected()) {
|
if (this.isSharedAutoPilotSelected()) {
|
||||||
@@ -429,8 +420,9 @@ export default class CassandraAddCollectionPane extends ContextualPaneBase {
|
|||||||
};
|
};
|
||||||
TelemetryProcessor.traceSuccess(Action.CreateCollection, addCollectionPaneSuccessMessage, startKey);
|
TelemetryProcessor.traceSuccess(Action.CreateCollection, addCollectionPaneSuccessMessage, startKey);
|
||||||
},
|
},
|
||||||
reason => {
|
error => {
|
||||||
this.formErrors(reason.exceptionMessage);
|
const errorMessage = getErrorMessage(error);
|
||||||
|
this.formErrors(errorMessage);
|
||||||
this.isExecuting(false);
|
this.isExecuting(false);
|
||||||
const addCollectionPaneFailedMessage = {
|
const addCollectionPaneFailedMessage = {
|
||||||
databaseAccountName: this.container.databaseAccount().name,
|
databaseAccountName: this.container.databaseAccount().name,
|
||||||
@@ -456,7 +448,8 @@ export default class CassandraAddCollectionPane extends ContextualPaneBase {
|
|||||||
toCreateKeyspace: toCreateKeyspace,
|
toCreateKeyspace: toCreateKeyspace,
|
||||||
createKeyspaceQuery: createKeyspaceQuery,
|
createKeyspaceQuery: createKeyspaceQuery,
|
||||||
createTableQuery: createTableQuery,
|
createTableQuery: createTableQuery,
|
||||||
error: reason
|
error: errorMessage,
|
||||||
|
errorStack: getErrorStack(error)
|
||||||
};
|
};
|
||||||
TelemetryProcessor.traceFailure(Action.CreateCollection, addCollectionPaneFailedMessage, startKey);
|
TelemetryProcessor.traceFailure(Action.CreateCollection, addCollectionPaneFailedMessage, startKey);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,7 +1,6 @@
|
|||||||
import ko from "knockout";
|
import ko from "knockout";
|
||||||
import * as React from "react";
|
import * as React from "react";
|
||||||
import { ReactAdapter } from "../../Bindings/ReactBindingHandler";
|
import { ReactAdapter } from "../../Bindings/ReactBindingHandler";
|
||||||
import * as Logger from "../../Common/Logger";
|
|
||||||
import { JunoClient, IPinnedRepo } from "../../Juno/JunoClient";
|
import { JunoClient, IPinnedRepo } from "../../Juno/JunoClient";
|
||||||
import * as NotificationConsoleUtils from "../../Utils/NotificationConsoleUtils";
|
import * as NotificationConsoleUtils from "../../Utils/NotificationConsoleUtils";
|
||||||
import Explorer from "../Explorer";
|
import Explorer from "../Explorer";
|
||||||
@@ -13,6 +12,7 @@ import { HttpStatusCodes } from "../../Common/Constants";
|
|||||||
import * as GitHubUtils from "../../Utils/GitHubUtils";
|
import * as GitHubUtils from "../../Utils/GitHubUtils";
|
||||||
import { NotebookContentItemType, NotebookContentItem } from "../Notebook/NotebookContentItem";
|
import { NotebookContentItemType, NotebookContentItem } from "../Notebook/NotebookContentItem";
|
||||||
import { ResourceTreeAdapter } from "../Tree/ResourceTreeAdapter";
|
import { ResourceTreeAdapter } from "../Tree/ResourceTreeAdapter";
|
||||||
|
import { handleError, getErrorMessage } from "../../Common/ErrorHandlingUtils";
|
||||||
|
|
||||||
interface Location {
|
interface Location {
|
||||||
type: "MyNotebooks" | "GitHub";
|
type: "MyNotebooks" | "GitHub";
|
||||||
@@ -90,9 +90,7 @@ export class CopyNotebookPaneAdapter implements ReactAdapter {
|
|||||||
if (this.gitHubOAuthService.isLoggedIn()) {
|
if (this.gitHubOAuthService.isLoggedIn()) {
|
||||||
const response = await this.junoClient.getPinnedRepos(this.gitHubOAuthService.getTokenObservable()()?.scope);
|
const response = await this.junoClient.getPinnedRepos(this.gitHubOAuthService.getTokenObservable()()?.scope);
|
||||||
if (response.status !== HttpStatusCodes.OK && response.status !== HttpStatusCodes.NoContent) {
|
if (response.status !== HttpStatusCodes.OK && response.status !== HttpStatusCodes.NoContent) {
|
||||||
const message = `Received HTTP ${response.status} when fetching pinned repos`;
|
handleError(`Received HTTP ${response.status} when fetching pinned repos`, "CopyNotebookPaneAdapter/submit");
|
||||||
Logger.logError(message, "CopyNotebookPaneAdapter/submit");
|
|
||||||
NotificationConsoleUtils.logConsoleError(message);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (response.data?.length > 0) {
|
if (response.data?.length > 0) {
|
||||||
@@ -134,12 +132,10 @@ export class CopyNotebookPaneAdapter implements ReactAdapter {
|
|||||||
|
|
||||||
NotificationConsoleUtils.logConsoleInfo(`Successfully copied ${this.name} to ${destination}`);
|
NotificationConsoleUtils.logConsoleInfo(`Successfully copied ${this.name} to ${destination}`);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
|
const errorMessage = getErrorMessage(error);
|
||||||
this.formError = `Failed to copy ${this.name} to ${destination}`;
|
this.formError = `Failed to copy ${this.name} to ${destination}`;
|
||||||
this.formErrorDetail = `${error}`;
|
this.formErrorDetail = `${errorMessage}`;
|
||||||
|
handleError(errorMessage, "CopyNotebookPaneAdapter/submit", this.formError);
|
||||||
const message = `${this.formError}: ${this.formErrorDetail}`;
|
|
||||||
Logger.logError(message, "CopyNotebookPaneAdapter/submit");
|
|
||||||
NotificationConsoleUtils.logConsoleError(message);
|
|
||||||
return;
|
return;
|
||||||
} finally {
|
} finally {
|
||||||
clearMessage && clearMessage();
|
clearMessage && clearMessage();
|
||||||
|
|||||||
@@ -3,7 +3,6 @@ import Q from "q";
|
|||||||
import * as ViewModels from "../../Contracts/ViewModels";
|
import * as ViewModels from "../../Contracts/ViewModels";
|
||||||
import * as Constants from "../../Common/Constants";
|
import * as Constants from "../../Common/Constants";
|
||||||
import { Action, ActionModifiers } from "../../Shared/Telemetry/TelemetryConstants";
|
import { Action, ActionModifiers } from "../../Shared/Telemetry/TelemetryConstants";
|
||||||
import * as ErrorParserUtility from "../../Common/ErrorParserUtility";
|
|
||||||
import { CassandraAPIDataClient } from "../Tables/TableDataClient";
|
import { CassandraAPIDataClient } from "../Tables/TableDataClient";
|
||||||
import { ConsoleDataType } from "../Menus/NotificationConsole/NotificationConsoleComponent";
|
import { ConsoleDataType } from "../Menus/NotificationConsole/NotificationConsoleComponent";
|
||||||
import { ContextualPaneBase } from "./ContextualPaneBase";
|
import { ContextualPaneBase } from "./ContextualPaneBase";
|
||||||
@@ -12,6 +11,7 @@ import DeleteFeedback from "../../Common/DeleteFeedback";
|
|||||||
import * as NotificationConsoleUtils from "../../Utils/NotificationConsoleUtils";
|
import * as NotificationConsoleUtils from "../../Utils/NotificationConsoleUtils";
|
||||||
import * as TelemetryProcessor from "../../Shared/Telemetry/TelemetryProcessor";
|
import * as TelemetryProcessor from "../../Shared/Telemetry/TelemetryProcessor";
|
||||||
import { deleteCollection } from "../../Common/dataAccess/deleteCollection";
|
import { deleteCollection } from "../../Common/dataAccess/deleteCollection";
|
||||||
|
import { getErrorMessage, getErrorStack } from "../../Common/ErrorHandlingUtils";
|
||||||
|
|
||||||
export default class DeleteCollectionConfirmationPane extends ContextualPaneBase {
|
export default class DeleteCollectionConfirmationPane extends ContextualPaneBase {
|
||||||
public collectionIdConfirmationText: ko.Observable<string>;
|
public collectionIdConfirmationText: ko.Observable<string>;
|
||||||
@@ -99,11 +99,11 @@ export default class DeleteCollectionConfirmationPane extends ContextualPaneBase
|
|||||||
this.containerDeleteFeedback("");
|
this.containerDeleteFeedback("");
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
(reason: any) => {
|
(error: any) => {
|
||||||
this.isExecuting(false);
|
this.isExecuting(false);
|
||||||
const message = ErrorParserUtility.parse(reason);
|
const errorMessage = getErrorMessage(error);
|
||||||
this.formErrors(message[0].message);
|
this.formErrors(errorMessage);
|
||||||
this.formErrorsDetails(message[0].message);
|
this.formErrorsDetails(errorMessage);
|
||||||
TelemetryProcessor.traceFailure(
|
TelemetryProcessor.traceFailure(
|
||||||
Action.DeleteCollection,
|
Action.DeleteCollection,
|
||||||
{
|
{
|
||||||
@@ -111,7 +111,9 @@ export default class DeleteCollectionConfirmationPane extends ContextualPaneBase
|
|||||||
defaultExperience: this.container.defaultExperience(),
|
defaultExperience: this.container.defaultExperience(),
|
||||||
collectionId: selectedCollection.id(),
|
collectionId: selectedCollection.id(),
|
||||||
dataExplorerArea: Constants.Areas.ContextualPane,
|
dataExplorerArea: Constants.Areas.ContextualPane,
|
||||||
paneTitle: this.title()
|
paneTitle: this.title(),
|
||||||
|
error: errorMessage,
|
||||||
|
errorStack: getErrorStack(error)
|
||||||
},
|
},
|
||||||
startKey
|
startKey
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -3,7 +3,6 @@ import Q from "q";
|
|||||||
import * as Constants from "../../Common/Constants";
|
import * as Constants from "../../Common/Constants";
|
||||||
import * as ViewModels from "../../Contracts/ViewModels";
|
import * as ViewModels from "../../Contracts/ViewModels";
|
||||||
import { Action, ActionModifiers } from "../../Shared/Telemetry/TelemetryConstants";
|
import { Action, ActionModifiers } from "../../Shared/Telemetry/TelemetryConstants";
|
||||||
import * as ErrorParserUtility from "../../Common/ErrorParserUtility";
|
|
||||||
import { CassandraAPIDataClient } from "../Tables/TableDataClient";
|
import { CassandraAPIDataClient } from "../Tables/TableDataClient";
|
||||||
import { ConsoleDataType } from "../Menus/NotificationConsole/NotificationConsoleComponent";
|
import { ConsoleDataType } from "../Menus/NotificationConsole/NotificationConsoleComponent";
|
||||||
import { ContextualPaneBase } from "./ContextualPaneBase";
|
import { ContextualPaneBase } from "./ContextualPaneBase";
|
||||||
@@ -14,6 +13,7 @@ import * as NotificationConsoleUtils from "../../Utils/NotificationConsoleUtils"
|
|||||||
import * as TelemetryProcessor from "../../Shared/Telemetry/TelemetryProcessor";
|
import * as TelemetryProcessor from "../../Shared/Telemetry/TelemetryProcessor";
|
||||||
import { deleteDatabase } from "../../Common/dataAccess/deleteDatabase";
|
import { deleteDatabase } from "../../Common/dataAccess/deleteDatabase";
|
||||||
import { ARMError } from "../../Utils/arm/request";
|
import { ARMError } from "../../Utils/arm/request";
|
||||||
|
import { getErrorMessage, getErrorStack } from "../../Common/ErrorHandlingUtils";
|
||||||
|
|
||||||
export default class DeleteDatabaseConfirmationPane extends ContextualPaneBase {
|
export default class DeleteDatabaseConfirmationPane extends ContextualPaneBase {
|
||||||
public databaseIdConfirmationText: ko.Observable<string>;
|
public databaseIdConfirmationText: ko.Observable<string>;
|
||||||
@@ -108,12 +108,11 @@ export default class DeleteDatabaseConfirmationPane extends ContextualPaneBase {
|
|||||||
this.databaseDeleteFeedback("");
|
this.databaseDeleteFeedback("");
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
(reason: unknown) => {
|
(error: any) => {
|
||||||
this.isExecuting(false);
|
this.isExecuting(false);
|
||||||
|
const errorMessage = getErrorMessage(error);
|
||||||
const message = reason instanceof ARMError ? reason.message : ErrorParserUtility.parse(reason)[0].message;
|
this.formErrors(errorMessage);
|
||||||
this.formErrors(message);
|
this.formErrorsDetails(errorMessage);
|
||||||
this.formErrorsDetails(message);
|
|
||||||
TelemetryProcessor.traceFailure(
|
TelemetryProcessor.traceFailure(
|
||||||
Action.DeleteDatabase,
|
Action.DeleteDatabase,
|
||||||
{
|
{
|
||||||
@@ -121,7 +120,9 @@ export default class DeleteDatabaseConfirmationPane extends ContextualPaneBase {
|
|||||||
defaultExperience: this.container.defaultExperience(),
|
defaultExperience: this.container.defaultExperience(),
|
||||||
databaseId: selectedDatabase.id(),
|
databaseId: selectedDatabase.id(),
|
||||||
dataExplorerArea: Constants.Areas.ContextualPane,
|
dataExplorerArea: Constants.Areas.ContextualPane,
|
||||||
paneTitle: this.title()
|
paneTitle: this.title(),
|
||||||
|
error: errorMessage,
|
||||||
|
errorStack: getErrorStack(error)
|
||||||
},
|
},
|
||||||
startKey
|
startKey
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -1,6 +1,5 @@
|
|||||||
import _ from "underscore";
|
import _ from "underscore";
|
||||||
import { Areas, HttpStatusCodes } from "../../Common/Constants";
|
import { Areas, HttpStatusCodes } from "../../Common/Constants";
|
||||||
import * as Logger from "../../Common/Logger";
|
|
||||||
import * as ViewModels from "../../Contracts/ViewModels";
|
import * as ViewModels from "../../Contracts/ViewModels";
|
||||||
import { GitHubClient, IGitHubPageInfo, IGitHubRepo } from "../../GitHub/GitHubClient";
|
import { GitHubClient, IGitHubPageInfo, IGitHubRepo } from "../../GitHub/GitHubClient";
|
||||||
import { IPinnedRepo, JunoClient } from "../../Juno/JunoClient";
|
import { IPinnedRepo, JunoClient } from "../../Juno/JunoClient";
|
||||||
@@ -8,13 +7,12 @@ import { Action, ActionModifiers } from "../../Shared/Telemetry/TelemetryConstan
|
|||||||
import * as TelemetryProcessor from "../../Shared/Telemetry/TelemetryProcessor";
|
import * as TelemetryProcessor from "../../Shared/Telemetry/TelemetryProcessor";
|
||||||
import * as GitHubUtils from "../../Utils/GitHubUtils";
|
import * as GitHubUtils from "../../Utils/GitHubUtils";
|
||||||
import { JunoUtils } from "../../Utils/JunoUtils";
|
import { JunoUtils } from "../../Utils/JunoUtils";
|
||||||
import * as NotificationConsoleUtils from "../../Utils/NotificationConsoleUtils";
|
|
||||||
import { AuthorizeAccessComponent } from "../Controls/GitHub/AuthorizeAccessComponent";
|
import { AuthorizeAccessComponent } from "../Controls/GitHub/AuthorizeAccessComponent";
|
||||||
import { GitHubReposComponent, GitHubReposComponentProps, RepoListItem } from "../Controls/GitHub/GitHubReposComponent";
|
import { GitHubReposComponent, GitHubReposComponentProps, RepoListItem } from "../Controls/GitHub/GitHubReposComponent";
|
||||||
import { GitHubReposComponentAdapter } from "../Controls/GitHub/GitHubReposComponentAdapter";
|
import { GitHubReposComponentAdapter } from "../Controls/GitHub/GitHubReposComponentAdapter";
|
||||||
import { BranchesProps, PinnedReposProps, UnpinnedReposProps } from "../Controls/GitHub/ReposListComponent";
|
import { BranchesProps, PinnedReposProps, UnpinnedReposProps } from "../Controls/GitHub/ReposListComponent";
|
||||||
import { ConsoleDataType } from "../Menus/NotificationConsole/NotificationConsoleComponent";
|
|
||||||
import { ContextualPaneBase } from "./ContextualPaneBase";
|
import { ContextualPaneBase } from "./ContextualPaneBase";
|
||||||
|
import { handleError } from "../../Common/ErrorHandlingUtils";
|
||||||
|
|
||||||
interface GitHubReposPaneOptions extends ViewModels.PaneOptions {
|
interface GitHubReposPaneOptions extends ViewModels.PaneOptions {
|
||||||
gitHubClient: GitHubClient;
|
gitHubClient: GitHubClient;
|
||||||
@@ -105,9 +103,7 @@ export class GitHubReposPane extends ContextualPaneBase {
|
|||||||
throw new Error(`Received HTTP ${response.status} when saving pinned repos`);
|
throw new Error(`Received HTTP ${response.status} when saving pinned repos`);
|
||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
const message = `Failed to save pinned repos: ${error}`;
|
handleError(error, "GitHubReposPane/submit", "Failed to save pinned repos");
|
||||||
Logger.logError(message, "GitHubReposPane/submit");
|
|
||||||
NotificationConsoleUtils.logConsoleMessage(ConsoleDataType.Error, message);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -206,9 +202,7 @@ export class GitHubReposPane extends ContextualPaneBase {
|
|||||||
branchesProps.lastPageInfo = response.pageInfo;
|
branchesProps.lastPageInfo = response.pageInfo;
|
||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
const message = `Failed to fetch branches: ${error}`;
|
handleError(error, "GitHubReposPane/loadMoreBranches", "Failed to fetch branches");
|
||||||
Logger.logError(message, "GitHubReposPane/loadMoreBranches");
|
|
||||||
NotificationConsoleUtils.logConsoleMessage(ConsoleDataType.Error, message);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
branchesProps.isLoading = false;
|
branchesProps.isLoading = false;
|
||||||
@@ -236,9 +230,7 @@ export class GitHubReposPane extends ContextualPaneBase {
|
|||||||
this.unpinnedReposProps.repos = this.calculateUnpinnedRepos();
|
this.unpinnedReposProps.repos = this.calculateUnpinnedRepos();
|
||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
const message = `Failed to fetch unpinned repos: ${error}`;
|
handleError(error, "GitHubReposPane/loadMoreUnpinnedRepos", "Failed to fetch unpinned repos");
|
||||||
Logger.logError(message, "GitHubReposPane/loadMoreUnpinnedRepos");
|
|
||||||
NotificationConsoleUtils.logConsoleMessage(ConsoleDataType.Error, message);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
this.unpinnedReposProps.isLoading = false;
|
this.unpinnedReposProps.isLoading = false;
|
||||||
@@ -255,9 +247,7 @@ export class GitHubReposPane extends ContextualPaneBase {
|
|||||||
|
|
||||||
return response.data;
|
return response.data;
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
const message = `Failed to fetch repo: ${error}`;
|
handleError(error, "GitHubReposPane/getRepo", "Failed to fetch repo");
|
||||||
Logger.logError(message, "GitHubReposPane/getRepo");
|
|
||||||
NotificationConsoleUtils.logConsoleMessage(ConsoleDataType.Error, message);
|
|
||||||
return Promise.resolve(undefined);
|
return Promise.resolve(undefined);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -320,9 +310,7 @@ export class GitHubReposPane extends ContextualPaneBase {
|
|||||||
this.triggerRender();
|
this.triggerRender();
|
||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
const message = `Failed to fetch pinned repos: ${error}`;
|
handleError(error, "GitHubReposPane/refreshPinnedReposListItems", "Failed to fetch pinned repos");
|
||||||
Logger.logError(message, "GitHubReposPane/refreshPinnedReposListItems");
|
|
||||||
NotificationConsoleUtils.logConsoleMessage(ConsoleDataType.Error, message);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,17 +1,16 @@
|
|||||||
import ko from "knockout";
|
import ko from "knockout";
|
||||||
import * as React from "react";
|
import * as React from "react";
|
||||||
import { ReactAdapter } from "../../Bindings/ReactBindingHandler";
|
import { ReactAdapter } from "../../Bindings/ReactBindingHandler";
|
||||||
import * as Logger from "../../Common/Logger";
|
|
||||||
import Explorer from "../Explorer";
|
import Explorer from "../Explorer";
|
||||||
import { JunoClient } from "../../Juno/JunoClient";
|
import { JunoClient } from "../../Juno/JunoClient";
|
||||||
import * as NotificationConsoleUtils from "../../Utils/NotificationConsoleUtils";
|
import * as NotificationConsoleUtils from "../../Utils/NotificationConsoleUtils";
|
||||||
import { ConsoleDataType } from "../Menus/NotificationConsole/NotificationConsoleComponent";
|
|
||||||
import { GenericRightPaneComponent, GenericRightPaneProps } from "./GenericRightPaneComponent";
|
import { GenericRightPaneComponent, GenericRightPaneProps } from "./GenericRightPaneComponent";
|
||||||
import { PublishNotebookPaneComponent, PublishNotebookPaneProps } from "./PublishNotebookPaneComponent";
|
import { PublishNotebookPaneComponent, PublishNotebookPaneProps } from "./PublishNotebookPaneComponent";
|
||||||
import { ImmutableNotebook } from "@nteract/commutable/src";
|
import { ImmutableNotebook } from "@nteract/commutable/src";
|
||||||
import { toJS } from "@nteract/commutable";
|
import { toJS } from "@nteract/commutable";
|
||||||
import { CodeOfConductComponent } from "../Controls/NotebookGallery/CodeOfConductComponent";
|
import { CodeOfConductComponent } from "../Controls/NotebookGallery/CodeOfConductComponent";
|
||||||
import { HttpStatusCodes } from "../../Common/Constants";
|
import { HttpStatusCodes } from "../../Common/Constants";
|
||||||
|
import { handleError, getErrorMessage } from "../../Common/ErrorHandlingUtils";
|
||||||
|
|
||||||
export class PublishNotebookPaneAdapter implements ReactAdapter {
|
export class PublishNotebookPaneAdapter implements ReactAdapter {
|
||||||
parameters: ko.Observable<number>;
|
parameters: ko.Observable<number>;
|
||||||
@@ -111,9 +110,11 @@ export class PublishNotebookPaneAdapter implements ReactAdapter {
|
|||||||
|
|
||||||
this.isCodeOfConductAccepted = response.data;
|
this.isCodeOfConductAccepted = response.data;
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
const message = `Failed to check if code of conduct was accepted: ${error}`;
|
handleError(
|
||||||
Logger.logError(message, "PublishNotebookPaneAdapter/isCodeOfConductAccepted");
|
error,
|
||||||
NotificationConsoleUtils.logConsoleMessage(ConsoleDataType.Error, message);
|
"PublishNotebookPaneAdapter/isCodeOfConductAccepted",
|
||||||
|
"Failed to check if code of conduct was accepted"
|
||||||
|
);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
this.isCodeOfConductAccepted = true;
|
this.isCodeOfConductAccepted = true;
|
||||||
@@ -170,12 +171,10 @@ export class PublishNotebookPaneAdapter implements ReactAdapter {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
|
const errorMessage = getErrorMessage(error);
|
||||||
this.formError = `Failed to publish ${this.name} to gallery`;
|
this.formError = `Failed to publish ${this.name} to gallery`;
|
||||||
this.formErrorDetail = `${error}`;
|
this.formErrorDetail = `${errorMessage}`;
|
||||||
|
handleError(errorMessage, "PublishNotebookPaneAdapter/submit", this.formError);
|
||||||
const message = `${this.formError}: ${this.formErrorDetail}`;
|
|
||||||
Logger.logError(message, "PublishNotebookPaneAdapter/submit");
|
|
||||||
NotificationConsoleUtils.logConsoleError(message);
|
|
||||||
return;
|
return;
|
||||||
} finally {
|
} finally {
|
||||||
clearPublishingMessage();
|
clearPublishingMessage();
|
||||||
@@ -189,10 +188,7 @@ export class PublishNotebookPaneAdapter implements ReactAdapter {
|
|||||||
private createFormErrorForLargeImageSelection = (formError: string, formErrorDetail: string, area: string): void => {
|
private createFormErrorForLargeImageSelection = (formError: string, formErrorDetail: string, area: string): void => {
|
||||||
this.formError = formError;
|
this.formError = formError;
|
||||||
this.formErrorDetail = formErrorDetail;
|
this.formErrorDetail = formErrorDetail;
|
||||||
|
handleError(formErrorDetail, area, formError);
|
||||||
const message = `${this.formError}: ${this.formErrorDetail}`;
|
|
||||||
Logger.logError(message, area);
|
|
||||||
NotificationConsoleUtils.logConsoleMessage(ConsoleDataType.Error, message);
|
|
||||||
this.triggerRender();
|
this.triggerRender();
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -7,6 +7,7 @@ import { ContextualPaneBase } from "./ContextualPaneBase";
|
|||||||
import { ConsoleDataType } from "../Menus/NotificationConsole/NotificationConsoleComponent";
|
import { ConsoleDataType } from "../Menus/NotificationConsole/NotificationConsoleComponent";
|
||||||
import { DefaultExperienceUtility } from "../../Shared/DefaultExperienceUtility";
|
import { DefaultExperienceUtility } from "../../Shared/DefaultExperienceUtility";
|
||||||
import * as NotificationConsoleUtils from "../../Utils/NotificationConsoleUtils";
|
import * as NotificationConsoleUtils from "../../Utils/NotificationConsoleUtils";
|
||||||
|
import { getErrorMessage } from "../../Common/ErrorHandlingUtils";
|
||||||
|
|
||||||
export class RenewAdHocAccessPane extends ContextualPaneBase {
|
export class RenewAdHocAccessPane extends ContextualPaneBase {
|
||||||
public accessKey: ko.Observable<string>;
|
public accessKey: ko.Observable<string>;
|
||||||
@@ -82,7 +83,7 @@ export class RenewAdHocAccessPane extends ContextualPaneBase {
|
|||||||
this.container
|
this.container
|
||||||
.renewShareAccess(this.accessKey())
|
.renewShareAccess(this.accessKey())
|
||||||
.fail((error: any) => {
|
.fail((error: any) => {
|
||||||
const errorMessage: string = error.message;
|
const errorMessage: string = getErrorMessage(error);
|
||||||
NotificationConsoleUtils.logConsoleMessage(ConsoleDataType.Error, `Failed to connect: ${errorMessage}`);
|
NotificationConsoleUtils.logConsoleMessage(ConsoleDataType.Error, `Failed to connect: ${errorMessage}`);
|
||||||
this.formErrors(errorMessage);
|
this.formErrors(errorMessage);
|
||||||
this.formErrorsDetails(errorMessage);
|
this.formErrorsDetails(errorMessage);
|
||||||
|
|||||||
@@ -8,6 +8,7 @@ import { ConsoleDataType } from "../Menus/NotificationConsole/NotificationConsol
|
|||||||
import * as NotificationConsoleUtils from "../../Utils/NotificationConsoleUtils";
|
import * as NotificationConsoleUtils from "../../Utils/NotificationConsoleUtils";
|
||||||
import * as TelemetryProcessor from "../../Shared/Telemetry/TelemetryProcessor";
|
import * as TelemetryProcessor from "../../Shared/Telemetry/TelemetryProcessor";
|
||||||
import QueryTab from "../Tabs/QueryTab";
|
import QueryTab from "../Tabs/QueryTab";
|
||||||
|
import { getErrorMessage, getErrorStack } from "../../Common/ErrorHandlingUtils";
|
||||||
|
|
||||||
export class SaveQueryPane extends ContextualPaneBase {
|
export class SaveQueryPane extends ContextualPaneBase {
|
||||||
public queryName: ko.Observable<string>;
|
public queryName: ko.Observable<string>;
|
||||||
@@ -87,18 +88,18 @@ export class SaveQueryPane extends ContextualPaneBase {
|
|||||||
},
|
},
|
||||||
(error: any) => {
|
(error: any) => {
|
||||||
this.isExecuting(false);
|
this.isExecuting(false);
|
||||||
if (typeof error != "string") {
|
const errorMessage = getErrorMessage(error);
|
||||||
error = error.message;
|
|
||||||
}
|
|
||||||
this.formErrors("Failed to save query");
|
this.formErrors("Failed to save query");
|
||||||
this.formErrorsDetails(`Failed to save query: ${error}`);
|
this.formErrorsDetails(`Failed to save query: ${errorMessage}`);
|
||||||
TelemetryProcessor.traceFailure(
|
TelemetryProcessor.traceFailure(
|
||||||
Action.SaveQuery,
|
Action.SaveQuery,
|
||||||
{
|
{
|
||||||
databaseAccountName: this.container.databaseAccount().name,
|
databaseAccountName: this.container.databaseAccount().name,
|
||||||
defaultExperience: this.container.defaultExperience(),
|
defaultExperience: this.container.defaultExperience(),
|
||||||
dataExplorerArea: Constants.Areas.ContextualPane,
|
dataExplorerArea: Constants.Areas.ContextualPane,
|
||||||
paneTitle: this.title()
|
paneTitle: this.title(),
|
||||||
|
error: errorMessage,
|
||||||
|
errorStack: getErrorStack(error)
|
||||||
},
|
},
|
||||||
startKey
|
startKey
|
||||||
);
|
);
|
||||||
@@ -132,18 +133,21 @@ export class SaveQueryPane extends ContextualPaneBase {
|
|||||||
startKey
|
startKey
|
||||||
);
|
);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
|
const errorMessage = getErrorMessage(error);
|
||||||
TelemetryProcessor.traceFailure(
|
TelemetryProcessor.traceFailure(
|
||||||
Action.SetupSavedQueries,
|
Action.SetupSavedQueries,
|
||||||
{
|
{
|
||||||
databaseAccountName: this.container && this.container.databaseAccount().name,
|
databaseAccountName: this.container && this.container.databaseAccount().name,
|
||||||
defaultExperience: this.container && this.container.defaultExperience(),
|
defaultExperience: this.container && this.container.defaultExperience(),
|
||||||
dataExplorerArea: Constants.Areas.ContextualPane,
|
dataExplorerArea: Constants.Areas.ContextualPane,
|
||||||
paneTitle: this.title()
|
paneTitle: this.title(),
|
||||||
|
error: errorMessage,
|
||||||
|
errorStack: getErrorStack(error)
|
||||||
},
|
},
|
||||||
startKey
|
startKey
|
||||||
);
|
);
|
||||||
this.formErrors("Failed to setup a container for saved queries");
|
this.formErrors("Failed to setup a container for saved queries");
|
||||||
this.formErrors(`Failed to setup a container for saved queries: ${error.message}`);
|
this.formErrorsDetails(`Failed to setup a container for saved queries: ${errorMessage}`);
|
||||||
} finally {
|
} finally {
|
||||||
this.isExecuting(false);
|
this.isExecuting(false);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -6,6 +6,7 @@ import { ContextualPaneBase } from "./ContextualPaneBase";
|
|||||||
import * as NotificationConsoleUtils from "../../Utils/NotificationConsoleUtils";
|
import * as NotificationConsoleUtils from "../../Utils/NotificationConsoleUtils";
|
||||||
import * as TelemetryProcessor from "../../Shared/Telemetry/TelemetryProcessor";
|
import * as TelemetryProcessor from "../../Shared/Telemetry/TelemetryProcessor";
|
||||||
import * as ko from "knockout";
|
import * as ko from "knockout";
|
||||||
|
import { getErrorMessage, getErrorStack } from "../../Common/ErrorHandlingUtils";
|
||||||
|
|
||||||
export class SetupNotebooksPane extends ContextualPaneBase {
|
export class SetupNotebooksPane extends ContextualPaneBase {
|
||||||
private description: ko.Observable<string>;
|
private description: ko.Observable<string>;
|
||||||
@@ -85,7 +86,7 @@ export class SetupNotebooksPane extends ContextualPaneBase {
|
|||||||
"Successfully created a default notebook workspace for the account"
|
"Successfully created a default notebook workspace for the account"
|
||||||
);
|
);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
const errorMessage = typeof error == "string" ? error : error.message;
|
const errorMessage = getErrorMessage(error);
|
||||||
TelemetryProcessor.traceFailure(
|
TelemetryProcessor.traceFailure(
|
||||||
Action.CreateNotebookWorkspace,
|
Action.CreateNotebookWorkspace,
|
||||||
{
|
{
|
||||||
@@ -93,7 +94,8 @@ export class SetupNotebooksPane extends ContextualPaneBase {
|
|||||||
defaultExperience: this.container && this.container.defaultExperience(),
|
defaultExperience: this.container && this.container.defaultExperience(),
|
||||||
dataExplorerArea: Areas.ContextualPane,
|
dataExplorerArea: Areas.ContextualPane,
|
||||||
paneTitle: this.title(),
|
paneTitle: this.title(),
|
||||||
error: errorMessage
|
error: errorMessage,
|
||||||
|
errorStack: getErrorStack(error)
|
||||||
},
|
},
|
||||||
startKey
|
startKey
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -3,11 +3,6 @@ export var Int32 = {
|
|||||||
Max: 2147483647
|
Max: 2147483647
|
||||||
};
|
};
|
||||||
|
|
||||||
export var Int64 = {
|
|
||||||
Min: -9223372036854775808,
|
|
||||||
Max: 9223372036854775807
|
|
||||||
};
|
|
||||||
|
|
||||||
var yearMonthDay = "\\d{4}[- ][01]\\d[- ][0-3]\\d";
|
var yearMonthDay = "\\d{4}[- ][01]\\d[- ][0-3]\\d";
|
||||||
var timeOfDay = "T[0-2]\\d:[0-5]\\d(:[0-5]\\d(\\.\\d+)?)?";
|
var timeOfDay = "T[0-2]\\d:[0-5]\\d(:[0-5]\\d(\\.\\d+)?)?";
|
||||||
var timeZone = "Z|[+-][0-2]\\d:[0-5]\\d";
|
var timeZone = "Z|[+-][0-2]\\d:[0-5]\\d";
|
||||||
|
|||||||
@@ -4,8 +4,8 @@ import * as ViewModels from "../../Contracts/ViewModels";
|
|||||||
import { ContextualPaneBase } from "./ContextualPaneBase";
|
import { ContextualPaneBase } from "./ContextualPaneBase";
|
||||||
import { ConsoleDataType } from "../Menus/NotificationConsole/NotificationConsoleComponent";
|
import { ConsoleDataType } from "../Menus/NotificationConsole/NotificationConsoleComponent";
|
||||||
import * as NotificationConsoleUtils from "../../Utils/NotificationConsoleUtils";
|
import * as NotificationConsoleUtils from "../../Utils/NotificationConsoleUtils";
|
||||||
import * as ErrorParserUtility from "../../Common/ErrorParserUtility";
|
|
||||||
import { UploadDetailsRecord, UploadDetails } from "../../workers/upload/definitions";
|
import { UploadDetailsRecord, UploadDetails } from "../../workers/upload/definitions";
|
||||||
|
import { getErrorMessage } from "../../Common/ErrorHandlingUtils";
|
||||||
|
|
||||||
const UPLOAD_FILE_SIZE_LIMIT = 2097152;
|
const UPLOAD_FILE_SIZE_LIMIT = 2097152;
|
||||||
|
|
||||||
@@ -61,9 +61,9 @@ export class UploadItemsPane extends ContextualPaneBase {
|
|||||||
this._resetFileInput();
|
this._resetFileInput();
|
||||||
},
|
},
|
||||||
(error: any) => {
|
(error: any) => {
|
||||||
const message = ErrorParserUtility.parse(error);
|
const errorMessage = getErrorMessage(error);
|
||||||
this.formErrors(message[0].message);
|
this.formErrors(errorMessage);
|
||||||
this.formErrorsDetails(message[0].message);
|
this.formErrorsDetails(errorMessage);
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
.finally(() => {
|
.finally(() => {
|
||||||
|
|||||||
@@ -1,4 +1,3 @@
|
|||||||
import * as ErrorParserUtility from "../../Common/ErrorParserUtility";
|
|
||||||
import * as ko from "knockout";
|
import * as ko from "knockout";
|
||||||
import * as NotificationConsoleUtils from "../../Utils/NotificationConsoleUtils";
|
import * as NotificationConsoleUtils from "../../Utils/NotificationConsoleUtils";
|
||||||
import * as React from "react";
|
import * as React from "react";
|
||||||
@@ -9,6 +8,7 @@ import { ReactAdapter } from "../../Bindings/ReactBindingHandler";
|
|||||||
import { UploadDetailsRecord, UploadDetails } from "../../workers/upload/definitions";
|
import { UploadDetailsRecord, UploadDetails } from "../../workers/upload/definitions";
|
||||||
import { UploadItemsPaneComponent, UploadItemsPaneProps } from "./UploadItemsPaneComponent";
|
import { UploadItemsPaneComponent, UploadItemsPaneProps } from "./UploadItemsPaneComponent";
|
||||||
import Explorer from "../Explorer";
|
import Explorer from "../Explorer";
|
||||||
|
import { getErrorMessage } from "../../Common/ErrorHandlingUtils";
|
||||||
|
|
||||||
const UPLOAD_FILE_SIZE_LIMIT = 2097152;
|
const UPLOAD_FILE_SIZE_LIMIT = 2097152;
|
||||||
|
|
||||||
@@ -107,9 +107,9 @@ export class UploadItemsPaneAdapter implements ReactAdapter {
|
|||||||
this.selectedFilesTitle = "";
|
this.selectedFilesTitle = "";
|
||||||
},
|
},
|
||||||
error => {
|
error => {
|
||||||
const message = ErrorParserUtility.parse(error);
|
const errorMessage = getErrorMessage(error);
|
||||||
this.formError = message[0].message;
|
this.formError = errorMessage;
|
||||||
this.formErrorDetail = message[0].message;
|
this.formErrorDetail = errorMessage;
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
.finally(() => {
|
.finally(() => {
|
||||||
|
|||||||
@@ -16,9 +16,9 @@ import * as Entities from "../Entities";
|
|||||||
import QueryTablesTab from "../../Tabs/QueryTablesTab";
|
import QueryTablesTab from "../../Tabs/QueryTablesTab";
|
||||||
import * as TableEntityProcessor from "../TableEntityProcessor";
|
import * as TableEntityProcessor from "../TableEntityProcessor";
|
||||||
import * as TelemetryProcessor from "../../../Shared/Telemetry/TelemetryProcessor";
|
import * as TelemetryProcessor from "../../../Shared/Telemetry/TelemetryProcessor";
|
||||||
import * as ErrorParserUtility from "../../../Common/ErrorParserUtility";
|
|
||||||
import * as DataModels from "../../../Contracts/DataModels";
|
import * as DataModels from "../../../Contracts/DataModels";
|
||||||
import * as ViewModels from "../../../Contracts/ViewModels";
|
import * as ViewModels from "../../../Contracts/ViewModels";
|
||||||
|
import { getErrorMessage, getErrorStack } from "../../../Common/ErrorHandlingUtils";
|
||||||
|
|
||||||
interface IListTableEntitiesSegmentedResult extends Entities.IListTableEntitiesResult {
|
interface IListTableEntitiesSegmentedResult extends Entities.IListTableEntitiesResult {
|
||||||
ExceedMaximumRetries?: boolean;
|
ExceedMaximumRetries?: boolean;
|
||||||
@@ -387,17 +387,8 @@ export default class TableEntityListViewModel extends DataTableViewModel {
|
|||||||
}
|
}
|
||||||
})
|
})
|
||||||
.catch((error: any) => {
|
.catch((error: any) => {
|
||||||
const parsedErrors = ErrorParserUtility.parse(error);
|
const errorMessage = getErrorMessage(error);
|
||||||
var errors = parsedErrors.map((error: DataModels.ErrorDataModel) => {
|
this.queryErrorMessage(errorMessage);
|
||||||
return <ViewModels.QueryError>{
|
|
||||||
message: error.message,
|
|
||||||
start: error.location ? error.location.start : undefined,
|
|
||||||
end: error.location ? error.location.end : undefined,
|
|
||||||
code: error.code,
|
|
||||||
severity: error.severity
|
|
||||||
};
|
|
||||||
});
|
|
||||||
this.queryErrorMessage(errors[0].message);
|
|
||||||
if (this.queryTablesTab.onLoadStartKey != null && this.queryTablesTab.onLoadStartKey != undefined) {
|
if (this.queryTablesTab.onLoadStartKey != null && this.queryTablesTab.onLoadStartKey != undefined) {
|
||||||
TelemetryProcessor.traceFailure(
|
TelemetryProcessor.traceFailure(
|
||||||
Action.Tab,
|
Action.Tab,
|
||||||
@@ -408,7 +399,8 @@ export default class TableEntityListViewModel extends DataTableViewModel {
|
|||||||
defaultExperience: this.queryTablesTab.collection.container.defaultExperience(),
|
defaultExperience: this.queryTablesTab.collection.container.defaultExperience(),
|
||||||
dataExplorerArea: Areas.Tab,
|
dataExplorerArea: Areas.Tab,
|
||||||
tabTitle: this.queryTablesTab.tabTitle(),
|
tabTitle: this.queryTablesTab.tabTitle(),
|
||||||
error: error
|
error: errorMessage,
|
||||||
|
errorStack: getErrorStack(error)
|
||||||
},
|
},
|
||||||
this.queryTablesTab.onLoadStartKey
|
this.queryTablesTab.onLoadStartKey
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -7,16 +7,14 @@ import { ConsoleDataType } from "../../Explorer/Menus/NotificationConsole/Notifi
|
|||||||
import * as Constants from "../../Common/Constants";
|
import * as Constants from "../../Common/Constants";
|
||||||
import * as Entities from "./Entities";
|
import * as Entities from "./Entities";
|
||||||
import * as HeadersUtility from "../../Common/HeadersUtility";
|
import * as HeadersUtility from "../../Common/HeadersUtility";
|
||||||
import * as Logger from "../../Common/Logger";
|
|
||||||
import * as NotificationConsoleUtils from "../../Utils/NotificationConsoleUtils";
|
import * as NotificationConsoleUtils from "../../Utils/NotificationConsoleUtils";
|
||||||
import * as TableConstants from "./Constants";
|
import * as TableConstants from "./Constants";
|
||||||
import * as TableEntityProcessor from "./TableEntityProcessor";
|
import * as TableEntityProcessor from "./TableEntityProcessor";
|
||||||
import * as ViewModels from "../../Contracts/ViewModels";
|
import * as ViewModels from "../../Contracts/ViewModels";
|
||||||
import { MessageTypes } from "../../Contracts/ExplorerContracts";
|
|
||||||
import { sendMessage } from "../../Common/MessageHandler";
|
|
||||||
import Explorer from "../Explorer";
|
import Explorer from "../Explorer";
|
||||||
import { queryDocuments, deleteDocument, updateDocument, createDocument } from "../../Common/DocumentClientUtilityBase";
|
import { queryDocuments, deleteDocument, updateDocument, createDocument } from "../../Common/DocumentClientUtilityBase";
|
||||||
import { configContext } from "../../ConfigContext";
|
import { configContext } from "../../ConfigContext";
|
||||||
|
import { handleError } from "../../Common/ErrorHandlingUtils";
|
||||||
|
|
||||||
export interface CassandraTableKeys {
|
export interface CassandraTableKeys {
|
||||||
partitionKeys: CassandraTableKey[];
|
partitionKeys: CassandraTableKey[];
|
||||||
@@ -188,14 +186,9 @@ export class CassandraAPIDataClient extends TableDataClient {
|
|||||||
);
|
);
|
||||||
deferred.resolve(entity);
|
deferred.resolve(entity);
|
||||||
},
|
},
|
||||||
reason => {
|
error => {
|
||||||
NotificationConsoleUtils.logConsoleMessage(
|
handleError(error, "AddRowCassandra", `Error while adding new row to table ${collection.id()}`);
|
||||||
ConsoleDataType.Error,
|
deferred.reject(error);
|
||||||
`Error while adding new row to table ${collection.id()}:\n ${JSON.stringify(reason)}`
|
|
||||||
);
|
|
||||||
Logger.logError(JSON.stringify(reason), "AddRowCassandra", reason.code);
|
|
||||||
this._checkForbiddenError(reason);
|
|
||||||
deferred.reject(reason);
|
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
.finally(() => {
|
.finally(() => {
|
||||||
@@ -267,14 +260,9 @@ export class CassandraAPIDataClient extends TableDataClient {
|
|||||||
);
|
);
|
||||||
deferred.resolve(newEntity);
|
deferred.resolve(newEntity);
|
||||||
},
|
},
|
||||||
reason => {
|
error => {
|
||||||
NotificationConsoleUtils.logConsoleMessage(
|
handleError(error, "UpdateRowCassandra", `Failed to update row ${newEntity.RowKey._}`);
|
||||||
ConsoleDataType.Error,
|
deferred.reject(error);
|
||||||
`Failed to update row ${newEntity.RowKey._}: ${JSON.stringify(reason)}`
|
|
||||||
);
|
|
||||||
Logger.logError(JSON.stringify(reason), "UpdateRowCassandra", reason.code);
|
|
||||||
this._checkForbiddenError(reason);
|
|
||||||
deferred.reject(reason);
|
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
.finally(() => {
|
.finally(() => {
|
||||||
@@ -332,16 +320,11 @@ export class CassandraAPIDataClient extends TableDataClient {
|
|||||||
ContinuationToken: data.paginationToken
|
ContinuationToken: data.paginationToken
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
reason => {
|
(error: any) => {
|
||||||
if (shouldNotify) {
|
if (shouldNotify) {
|
||||||
NotificationConsoleUtils.logConsoleMessage(
|
handleError(error, "QueryDocumentsCassandra", `Failed to query rows for table ${collection.id()}`);
|
||||||
ConsoleDataType.Error,
|
|
||||||
`Failed to query rows for table ${collection.id()}: ${JSON.stringify(reason)}`
|
|
||||||
);
|
|
||||||
Logger.logError(JSON.stringify(reason), "QueryDocumentsCassandra", reason.status);
|
|
||||||
this._checkForbiddenError(reason);
|
|
||||||
}
|
}
|
||||||
deferred.reject(reason);
|
deferred.reject(error);
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
.done(() => {
|
.done(() => {
|
||||||
@@ -379,13 +362,8 @@ export class CassandraAPIDataClient extends TableDataClient {
|
|||||||
`Successfully deleted row ${currEntityToDelete.RowKey._}`
|
`Successfully deleted row ${currEntityToDelete.RowKey._}`
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
reason => {
|
error => {
|
||||||
NotificationConsoleUtils.logConsoleMessage(
|
handleError(error, "DeleteRowCassandra", `Error while deleting row ${currEntityToDelete.RowKey._}`);
|
||||||
ConsoleDataType.Error,
|
|
||||||
`Error while deleting row ${currEntityToDelete.RowKey._}:\n ${JSON.stringify(reason)}`
|
|
||||||
);
|
|
||||||
Logger.logError(JSON.stringify(reason), "DeleteRowCassandra", reason.code);
|
|
||||||
this._checkForbiddenError(reason);
|
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
.finally(() => {
|
.finally(() => {
|
||||||
@@ -420,14 +398,13 @@ export class CassandraAPIDataClient extends TableDataClient {
|
|||||||
);
|
);
|
||||||
deferred.resolve();
|
deferred.resolve();
|
||||||
},
|
},
|
||||||
reason => {
|
error => {
|
||||||
NotificationConsoleUtils.logConsoleMessage(
|
handleError(
|
||||||
ConsoleDataType.Error,
|
error,
|
||||||
`Error while creating a keyspace with query ${createKeyspaceQuery}:\n ${JSON.stringify(reason)}`
|
"CreateKeyspaceCassandra",
|
||||||
|
`Error while creating a keyspace with query ${createKeyspaceQuery}`
|
||||||
);
|
);
|
||||||
Logger.logError(JSON.stringify(reason), "CreateKeyspaceCassandra", reason.code);
|
deferred.reject(error);
|
||||||
this._checkForbiddenError(reason);
|
|
||||||
deferred.reject(reason);
|
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
.finally(() => {
|
.finally(() => {
|
||||||
@@ -467,14 +444,9 @@ export class CassandraAPIDataClient extends TableDataClient {
|
|||||||
);
|
);
|
||||||
deferred.resolve();
|
deferred.resolve();
|
||||||
},
|
},
|
||||||
reason => {
|
error => {
|
||||||
NotificationConsoleUtils.logConsoleMessage(
|
handleError(error, "CreateTableCassandra", `Error while creating a table with query ${createTableQuery}`);
|
||||||
ConsoleDataType.Error,
|
deferred.reject(error);
|
||||||
`Error while creating a table with query ${createTableQuery}:\n ${JSON.stringify(reason)}`
|
|
||||||
);
|
|
||||||
Logger.logError(JSON.stringify(reason), "CreateTableCassandra", reason.code);
|
|
||||||
this._checkForbiddenError(reason);
|
|
||||||
deferred.reject(reason);
|
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
.finally(() => {
|
.finally(() => {
|
||||||
@@ -508,14 +480,13 @@ export class CassandraAPIDataClient extends TableDataClient {
|
|||||||
);
|
);
|
||||||
deferred.resolve();
|
deferred.resolve();
|
||||||
},
|
},
|
||||||
reason => {
|
error => {
|
||||||
NotificationConsoleUtils.logConsoleMessage(
|
handleError(
|
||||||
ConsoleDataType.Error,
|
error,
|
||||||
`Error while deleting resource with query ${deleteQuery}:\n ${JSON.stringify(reason)}`
|
"DeleteKeyspaceOrTableCassandra",
|
||||||
|
`Error while deleting resource with query ${deleteQuery}`
|
||||||
);
|
);
|
||||||
Logger.logError(JSON.stringify(reason), "DeleteKeyspaceOrTableCassandra", reason.code);
|
deferred.reject(error);
|
||||||
this._checkForbiddenError(reason);
|
|
||||||
deferred.reject(reason);
|
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
.finally(() => {
|
.finally(() => {
|
||||||
@@ -563,14 +534,9 @@ export class CassandraAPIDataClient extends TableDataClient {
|
|||||||
);
|
);
|
||||||
deferred.resolve(data);
|
deferred.resolve(data);
|
||||||
},
|
},
|
||||||
reason => {
|
(error: any) => {
|
||||||
NotificationConsoleUtils.logConsoleMessage(
|
handleError(error, "FetchKeysCassandra", `Error fetching keys for table ${collection.id()}`);
|
||||||
ConsoleDataType.Error,
|
deferred.reject(error);
|
||||||
`Error fetching keys for table ${collection.id()}:\n ${JSON.stringify(reason)}`
|
|
||||||
);
|
|
||||||
Logger.logError(JSON.stringify(reason), "FetchKeysCassandra", reason.status);
|
|
||||||
this._checkForbiddenError(reason);
|
|
||||||
deferred.reject(reason);
|
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
.done(() => {
|
.done(() => {
|
||||||
@@ -618,14 +584,9 @@ export class CassandraAPIDataClient extends TableDataClient {
|
|||||||
);
|
);
|
||||||
deferred.resolve(data.columns);
|
deferred.resolve(data.columns);
|
||||||
},
|
},
|
||||||
reason => {
|
(error: any) => {
|
||||||
NotificationConsoleUtils.logConsoleMessage(
|
handleError(error, "FetchSchemaCassandra", `Error fetching schema for table ${collection.id()}`);
|
||||||
ConsoleDataType.Error,
|
deferred.reject(error);
|
||||||
`Error fetching schema for table ${collection.id()}:\n ${JSON.stringify(reason)}`
|
|
||||||
);
|
|
||||||
Logger.logError(JSON.stringify(reason), "FetchSchemaCassandra", reason.status);
|
|
||||||
this._checkForbiddenError(reason);
|
|
||||||
deferred.reject(reason);
|
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
.done(() => {
|
.done(() => {
|
||||||
@@ -712,13 +673,4 @@ export class CassandraAPIDataClient extends TableDataClient {
|
|||||||
|
|
||||||
displayTokenRenewalPromptForStatus(xhrObj.status);
|
displayTokenRenewalPromptForStatus(xhrObj.status);
|
||||||
};
|
};
|
||||||
|
|
||||||
private _checkForbiddenError(reason: any) {
|
|
||||||
if (reason && reason.code === Constants.HttpStatusCodes.Forbidden) {
|
|
||||||
sendMessage({
|
|
||||||
type: MessageTypes.ForbiddenError,
|
|
||||||
reason: typeof reason === "string" ? "reason" : JSON.stringify(reason)
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user