mirror of
https://github.com/Azure/cosmos-explorer.git
synced 2026-01-01 15:22:08 +00:00
Compare commits
17 Commits
remove-unu
...
users/srna
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
9e8793d1b1 | ||
|
|
f41de718a0 | ||
|
|
5820afc25a | ||
|
|
6d94f18f9a | ||
|
|
a133134b8b | ||
|
|
79dec6a8a8 | ||
|
|
53a8cea95e | ||
|
|
5f1f7a8266 | ||
|
|
a009a8ba5f | ||
|
|
3e782527d0 | ||
|
|
e6ca1d25c9 | ||
|
|
473f722dcc | ||
|
|
5741802c25 | ||
|
|
e2e58f73b1 | ||
|
|
79769e9689 | ||
|
|
542abf4d3a | ||
|
|
4bee46ce55 |
@@ -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,4 +1,3 @@
|
|||||||
import { AutopilotTier } from "../Contracts/DataModels";
|
|
||||||
import { HashMap } from "./HashMap";
|
import { HashMap } from "./HashMap";
|
||||||
|
|
||||||
export class AuthorizationEndpoints {
|
export class AuthorizationEndpoints {
|
||||||
@@ -124,7 +123,6 @@ export class Features {
|
|||||||
public static readonly notebookBasePath = "notebookbasepath";
|
public static readonly notebookBasePath = "notebookbasepath";
|
||||||
public static readonly canExceedMaximumValue = "canexceedmaximumvalue";
|
public static readonly canExceedMaximumValue = "canexceedmaximumvalue";
|
||||||
public static readonly enableFixedCollectionWithSharedThroughput = "enablefixedcollectionwithsharedthroughput";
|
public static readonly enableFixedCollectionWithSharedThroughput = "enablefixedcollectionwithsharedthroughput";
|
||||||
public static readonly enableAutoPilotV2 = "enableautopilotv2";
|
|
||||||
public static readonly ttl90Days = "ttl90days";
|
public static readonly ttl90Days = "ttl90days";
|
||||||
public static readonly enableRightPanelV2 = "enablerightpanelv2";
|
public static readonly enableRightPanelV2 = "enablerightpanelv2";
|
||||||
public static readonly enableSDKoperations = "enablesdkoperations";
|
public static readonly enableSDKoperations = "enablesdkoperations";
|
||||||
@@ -262,7 +260,6 @@ export class HttpHeaders {
|
|||||||
public static usePolygonsSmallerThanAHemisphere = "x-ms-documentdb-usepolygonssmallerthanahemisphere";
|
public static usePolygonsSmallerThanAHemisphere = "x-ms-documentdb-usepolygonssmallerthanahemisphere";
|
||||||
public static autoPilotThroughput = "autoscaleSettings";
|
public static autoPilotThroughput = "autoscaleSettings";
|
||||||
public static autoPilotThroughputSDK = "x-ms-cosmos-offer-autopilot-settings";
|
public static autoPilotThroughputSDK = "x-ms-cosmos-offer-autopilot-settings";
|
||||||
public static autoPilotTier = "x-ms-cosmos-offer-autopilot-tier";
|
|
||||||
public static partitionKey: string = "x-ms-documentdb-partitionkey";
|
public static partitionKey: string = "x-ms-documentdb-partitionkey";
|
||||||
public static migrateOfferToManualThroughput: string = "x-ms-cosmos-migrate-offer-to-manual-throughput";
|
public static migrateOfferToManualThroughput: string = "x-ms-cosmos-migrate-offer-to-manual-throughput";
|
||||||
public static migrateOfferToAutopilot: string = "x-ms-cosmos-migrate-offer-to-autopilot";
|
public static migrateOfferToAutopilot: string = "x-ms-cosmos-migrate-offer-to-autopilot";
|
||||||
@@ -407,54 +404,6 @@ export enum ConflictOperationType {
|
|||||||
Delete = "delete"
|
Delete = "delete"
|
||||||
}
|
}
|
||||||
|
|
||||||
export class AutoPilot {
|
|
||||||
public static tier1Text: string = "4,000 RU/s";
|
|
||||||
public static tier2Text: string = "20,000 RU/s";
|
|
||||||
public static tier3Text: string = "100,000 RU/s";
|
|
||||||
public static tier4Text: string = "500,000 RU/s";
|
|
||||||
|
|
||||||
public static tierText = {
|
|
||||||
[AutopilotTier.Tier1]: "Tier 1",
|
|
||||||
[AutopilotTier.Tier2]: "Tier 2",
|
|
||||||
[AutopilotTier.Tier3]: "Tier 3",
|
|
||||||
[AutopilotTier.Tier4]: "Tier 4"
|
|
||||||
};
|
|
||||||
|
|
||||||
public static tierMaxRus = {
|
|
||||||
[AutopilotTier.Tier1]: 2000,
|
|
||||||
[AutopilotTier.Tier2]: 20000,
|
|
||||||
[AutopilotTier.Tier3]: 100000,
|
|
||||||
[AutopilotTier.Tier4]: 500000
|
|
||||||
};
|
|
||||||
|
|
||||||
public static tierMinRus = {
|
|
||||||
[AutopilotTier.Tier1]: 0,
|
|
||||||
[AutopilotTier.Tier2]: 0,
|
|
||||||
[AutopilotTier.Tier3]: 0,
|
|
||||||
[AutopilotTier.Tier4]: 0
|
|
||||||
};
|
|
||||||
|
|
||||||
public static tierStorageInGB = {
|
|
||||||
[AutopilotTier.Tier1]: 50,
|
|
||||||
[AutopilotTier.Tier2]: 200,
|
|
||||||
[AutopilotTier.Tier3]: 1000,
|
|
||||||
[AutopilotTier.Tier4]: 5000
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
export class DataExplorerVersions {
|
|
||||||
public static readonly v_1_0_0: string = "1.0.0";
|
|
||||||
public static readonly v_1_0_1: string = "1.0.1";
|
|
||||||
}
|
|
||||||
|
|
||||||
export class DataExplorerFeatures {
|
|
||||||
public static offerCache: string = "OfferCache";
|
|
||||||
}
|
|
||||||
|
|
||||||
export const DataExplorerFeaturesVersions: any = {
|
|
||||||
OfferCache: DataExplorerVersions.v_1_0_1
|
|
||||||
};
|
|
||||||
|
|
||||||
export const EmulatorMasterKey =
|
export const EmulatorMasterKey =
|
||||||
//[SuppressMessage("Microsoft.Security", "CS002:SecretInNextLine", Justification="Well known public masterKey for emulator")]
|
//[SuppressMessage("Microsoft.Security", "CS002:SecretInNextLine", Justification="Well known public masterKey for emulator")]
|
||||||
"C2y6yDjf5/R+ob0N8A7Cgv30VRDJIWEHLM+4QDU5DE2nQ9nDuVTqobD4b8mGGyPMbIZnqyMsEcaGQy67XIw/Jw==";
|
"C2y6yDjf5/R+ob0N8A7Cgv30VRDJIWEHLM+4QDU5DE2nQ9nDuVTqobD4b8mGGyPMbIZnqyMsEcaGQy67XIw/Jw==";
|
||||||
|
|||||||
@@ -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();
|
||||||
|
|||||||
@@ -37,7 +37,7 @@ export const readCollectionQuotaInfo = async (
|
|||||||
|
|
||||||
return quota;
|
return quota;
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
handleError(error, `Error while querying quota info for container ${collection.id}`, "ReadCollectionQuotaInfo");
|
handleError(error, "ReadCollectionQuotaInfo", `Error while querying quota info for container ${collection.id}`);
|
||||||
throw error;
|
throw error;
|
||||||
} finally {
|
} finally {
|
||||||
clearMessage();
|
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,8 +1,9 @@
|
|||||||
import { Platform, configContext } from "../../ConfigContext";
|
import { Platform, configContext } from "../../ConfigContext";
|
||||||
import { getAuthorizationHeader } from "../../Utils/AuthorizationUtils";
|
import { getAuthorizationHeader } from "../../Utils/AuthorizationUtils";
|
||||||
import { AutoPilotOfferSettings } from "../../Contracts/DataModels";
|
import { AutoPilotOfferSettings } from "../../Contracts/DataModels";
|
||||||
import { logConsoleProgress, logConsoleInfo, logConsoleError } from "../../Utils/NotificationConsoleUtils";
|
import { logConsoleProgress, logConsoleInfo } from "../../Utils/NotificationConsoleUtils";
|
||||||
import { HttpHeaders } from "../Constants";
|
import { HttpHeaders } from "../Constants";
|
||||||
|
import { handleError } from "../ErrorHandlingUtils";
|
||||||
|
|
||||||
interface UpdateOfferThroughputRequest {
|
interface UpdateOfferThroughputRequest {
|
||||||
subscriptionId: string;
|
subscriptionId: string;
|
||||||
@@ -44,8 +45,13 @@ export async function updateOfferThroughputBeyondLimit(request: UpdateOfferThrou
|
|||||||
clearMessage();
|
clearMessage();
|
||||||
return undefined;
|
return undefined;
|
||||||
}
|
}
|
||||||
|
|
||||||
const error = await response.json();
|
const error = await response.json();
|
||||||
logConsoleError(`Failed to request an increase in throughput for ${request.throughput}: ${error.message}`);
|
handleError(
|
||||||
|
error,
|
||||||
|
"updateOfferThroughputBeyondLimit",
|
||||||
|
`Failed to request an increase in throughput for ${request.throughput}`
|
||||||
|
);
|
||||||
clearMessage();
|
clearMessage();
|
||||||
throw new Error(error.message);
|
throw error;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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 {
|
||||||
|
|||||||
@@ -216,18 +216,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;
|
||||||
@@ -239,21 +227,12 @@ export interface CreateDatabaseAndCollectionRequest {
|
|||||||
uniqueKeyPolicy?: UniqueKeyPolicy;
|
uniqueKeyPolicy?: UniqueKeyPolicy;
|
||||||
autoPilot?: AutoPilotCreationSettings;
|
autoPilot?: AutoPilotCreationSettings;
|
||||||
analyticalStorageTtl?: number;
|
analyticalStorageTtl?: number;
|
||||||
hasAutoPilotV2FeatureFlag?: boolean;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface AutoPilotCreationSettings {
|
export interface AutoPilotCreationSettings {
|
||||||
autopilotTier?: AutopilotTier;
|
|
||||||
maxThroughput?: number;
|
maxThroughput?: number;
|
||||||
}
|
}
|
||||||
|
|
||||||
export enum AutopilotTier {
|
|
||||||
Tier1 = 1,
|
|
||||||
Tier2 = 2,
|
|
||||||
Tier3 = 3,
|
|
||||||
Tier4 = 4
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface Query {
|
export interface Query {
|
||||||
id: string;
|
id: string;
|
||||||
resourceId: string;
|
resourceId: string;
|
||||||
@@ -262,9 +241,7 @@ export interface Query {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export interface AutoPilotOfferSettings {
|
export interface AutoPilotOfferSettings {
|
||||||
tier?: AutopilotTier;
|
|
||||||
maximumTierThroughput?: number;
|
maximumTierThroughput?: number;
|
||||||
targetTier?: AutopilotTier;
|
|
||||||
maxThroughput?: number;
|
maxThroughput?: number;
|
||||||
targetMaxThroughput?: number;
|
targetMaxThroughput?: number;
|
||||||
}
|
}
|
||||||
@@ -491,7 +468,6 @@ export interface MongoParameters extends RpParameters {
|
|||||||
rid?: string;
|
rid?: string;
|
||||||
rtype?: string;
|
rtype?: string;
|
||||||
isAutoPilot?: Boolean;
|
isAutoPilot?: Boolean;
|
||||||
autoPilotTier?: string;
|
|
||||||
autoPilotThroughput?: string;
|
autoPilotThroughput?: string;
|
||||||
analyticalStorageTtl?: number;
|
analyticalStorageTtl?: number;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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.
|
||||||
*/
|
*/
|
||||||
|
|||||||
@@ -162,6 +162,7 @@ export interface Collection extends CollectionBase {
|
|||||||
loadStoredProcedures(): Promise<any>;
|
loadStoredProcedures(): Promise<any>;
|
||||||
loadTriggers(): Promise<any>;
|
loadTriggers(): Promise<any>;
|
||||||
loadOffer(): Promise<void>;
|
loadOffer(): Promise<void>;
|
||||||
|
loadAutopilotOfferWithRetry(): Promise<DataModels.Offer>;
|
||||||
|
|
||||||
createStoredProcedureNode(data: StoredProcedureDefinition & Resource): StoredProcedure;
|
createStoredProcedureNode(data: StoredProcedureDefinition & Resource): StoredProcedure;
|
||||||
createUserDefinedFunctionNode(data: UserDefinedFunctionDefinition & Resource): UserDefinedFunction;
|
createUserDefinedFunctionNode(data: UserDefinedFunctionDefinition & Resource): UserDefinedFunction;
|
||||||
|
|||||||
@@ -123,8 +123,4 @@ describe("Component Registerer", () => {
|
|||||||
it("should register dynamic-list component", () => {
|
it("should register dynamic-list component", () => {
|
||||||
expect(ko.components.isRegistered("dynamic-list")).toBe(true);
|
expect(ko.components.isRegistered("dynamic-list")).toBe(true);
|
||||||
});
|
});
|
||||||
|
|
||||||
it("should register throughput-input component", () => {
|
|
||||||
expect(ko.components.isRegistered("throughput-input")).toBe(true);
|
|
||||||
});
|
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -11,7 +11,6 @@ import { InputTypeaheadComponent } from "./Controls/InputTypeahead/InputTypeahea
|
|||||||
import { JsonEditorComponent } from "./Controls/JsonEditor/JsonEditorComponent";
|
import { JsonEditorComponent } from "./Controls/JsonEditor/JsonEditorComponent";
|
||||||
import { NewVertexComponent } from "./Graph/NewVertexComponent/NewVertexComponent";
|
import { NewVertexComponent } from "./Graph/NewVertexComponent/NewVertexComponent";
|
||||||
import { TabsManagerKOComponent } from "./Tabs/TabsManager";
|
import { TabsManagerKOComponent } from "./Tabs/TabsManager";
|
||||||
import { ThroughputInputComponent } from "./Controls/ThroughputInput/ThroughputInputComponent";
|
|
||||||
import { ThroughputInputComponentAutoPilotV3 } from "./Controls/ThroughputInput/ThroughputInputComponentAutoPilotV3";
|
import { ThroughputInputComponentAutoPilotV3 } from "./Controls/ThroughputInput/ThroughputInputComponentAutoPilotV3";
|
||||||
|
|
||||||
ko.components.register("input-typeahead", new InputTypeaheadComponent());
|
ko.components.register("input-typeahead", new InputTypeaheadComponent());
|
||||||
@@ -23,7 +22,6 @@ ko.components.register("editor", new EditorComponent());
|
|||||||
ko.components.register("json-editor", new JsonEditorComponent());
|
ko.components.register("json-editor", new JsonEditorComponent());
|
||||||
ko.components.register("diff-editor", new DiffEditorComponent());
|
ko.components.register("diff-editor", new DiffEditorComponent());
|
||||||
ko.components.register("dynamic-list", DynamicListComponent);
|
ko.components.register("dynamic-list", DynamicListComponent);
|
||||||
ko.components.register("throughput-input", ThroughputInputComponent);
|
|
||||||
ko.components.register("throughput-input-autopilot-v3", ThroughputInputComponentAutoPilotV3);
|
ko.components.register("throughput-input-autopilot-v3", ThroughputInputComponentAutoPilotV3);
|
||||||
ko.components.register("tabs-manager", TabsManagerKOComponent());
|
ko.components.register("tabs-manager", TabsManagerKOComponent());
|
||||||
|
|
||||||
|
|||||||
@@ -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", () => ({
|
||||||
|
|||||||
@@ -46,10 +46,9 @@ import { Pivot, PivotItem, IPivotProps, IPivotItemProps } from "office-ui-fabric
|
|||||||
import "./SettingsComponent.less";
|
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 { MongoDBCollectionResource, MongoIndex } from "../../../Utils/arm/generatedClients/2020-04-01/types";
|
||||||
import {
|
import { readMongoDBCollectionThroughRP } from "../../../Common/dataAccess/readMongoDBCollection";
|
||||||
getMongoDBCollectionIndexTransformationProgress,
|
import { getIndexTransformationProgress } from "../../../Common/dataAccess/getIndexTransformationProgress";
|
||||||
readMongoDBCollectionThroughRP
|
import { getErrorMessage, getErrorStack } from "../../../Common/ErrorHandlingUtils";
|
||||||
} from "../../../Common/dataAccess/readMongoDBCollection";
|
|
||||||
|
|
||||||
interface SettingsV2TabInfo {
|
interface SettingsV2TabInfo {
|
||||||
tab: SettingsV2TabTypes;
|
tab: SettingsV2TabTypes;
|
||||||
@@ -125,7 +124,6 @@ export class SettingsComponent extends React.Component<SettingsComponentProps, S
|
|||||||
private container: Explorer;
|
private container: Explorer;
|
||||||
private changeFeedPolicyVisible: boolean;
|
private changeFeedPolicyVisible: boolean;
|
||||||
private isFixedContainer: boolean;
|
private isFixedContainer: boolean;
|
||||||
private autoPilotTiersList: ViewModels.DropdownOption<DataModels.AutopilotTier>[];
|
|
||||||
private shouldShowIndexingPolicyEditor: boolean;
|
private shouldShowIndexingPolicyEditor: boolean;
|
||||||
public mongoDBCollectionResource: MongoDBCollectionResource;
|
public mongoDBCollectionResource: MongoDBCollectionResource;
|
||||||
|
|
||||||
@@ -212,6 +210,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();
|
||||||
@@ -230,10 +229,9 @@ export class SettingsComponent extends React.Component<SettingsComponentProps, S
|
|||||||
if (
|
if (
|
||||||
this.container.isMongoIndexEditorEnabled() &&
|
this.container.isMongoIndexEditorEnabled() &&
|
||||||
this.container.isPreferredApiMongoDB() &&
|
this.container.isPreferredApiMongoDB() &&
|
||||||
|
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 +246,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 +346,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 +382,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,
|
||||||
@@ -395,24 +396,55 @@ export class SettingsComponent extends React.Component<SettingsComponentProps, S
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (this.state.isMongoIndexingPolicySaveable && this.mongoDBCollectionResource) {
|
if (this.state.isMongoIndexingPolicySaveable && this.mongoDBCollectionResource) {
|
||||||
const newMongoIndexes = this.getMongoIndexesToSave();
|
try {
|
||||||
const newMongoCollection: MongoDBCollectionResource = {
|
const newMongoIndexes = this.getMongoIndexesToSave();
|
||||||
...this.mongoDBCollectionResource,
|
const newMongoCollection: MongoDBCollectionResource = {
|
||||||
indexes: newMongoIndexes
|
...this.mongoDBCollectionResource,
|
||||||
};
|
indexes: newMongoIndexes
|
||||||
this.mongoDBCollectionResource = await updateMongoDBCollectionThroughRP(
|
};
|
||||||
this.collection.databaseId,
|
|
||||||
this.collection.id(),
|
|
||||||
newMongoCollection
|
|
||||||
);
|
|
||||||
|
|
||||||
await this.refreshIndexTransformationProgress();
|
this.mongoDBCollectionResource = await updateMongoDBCollectionThroughRP(
|
||||||
this.setState({
|
this.collection.databaseId,
|
||||||
isMongoIndexingPolicySaveable: false,
|
this.collection.id(),
|
||||||
indexesToDrop: [],
|
newMongoCollection
|
||||||
indexesToAdd: [],
|
);
|
||||||
currentMongoIndexes: [...this.mongoDBCollectionResource.indexes]
|
|
||||||
});
|
await this.refreshIndexTransformationProgress();
|
||||||
|
this.setState({
|
||||||
|
isMongoIndexingPolicySaveable: false,
|
||||||
|
indexesToDrop: [],
|
||||||
|
indexesToAdd: [],
|
||||||
|
currentMongoIndexes: [...this.mongoDBCollectionResource.indexes]
|
||||||
|
});
|
||||||
|
traceSuccess(
|
||||||
|
Action.MongoIndexUpdated,
|
||||||
|
{
|
||||||
|
databaseAccountName: this.container.databaseAccount()?.name,
|
||||||
|
databaseName: this.collection?.databaseId,
|
||||||
|
collectionName: this.collection?.id(),
|
||||||
|
defaultExperience: this.container.defaultExperience(),
|
||||||
|
dataExplorerArea: Constants.Areas.Tab,
|
||||||
|
tabTitle: this.props.settingsTab.tabTitle()
|
||||||
|
},
|
||||||
|
startKey
|
||||||
|
);
|
||||||
|
} catch (error) {
|
||||||
|
traceFailure(
|
||||||
|
Action.MongoIndexUpdated,
|
||||||
|
{
|
||||||
|
databaseAccountName: this.container.databaseAccount()?.name,
|
||||||
|
databaseName: this.collection?.databaseId,
|
||||||
|
collectionName: this.collection?.id(),
|
||||||
|
defaultExperience: this.container.defaultExperience(),
|
||||||
|
dataExplorerArea: Constants.Areas.Tab,
|
||||||
|
tabTitle: this.props.settingsTab.tabTitle(),
|
||||||
|
error: getErrorMessage(error),
|
||||||
|
errorStack: getErrorStack(error)
|
||||||
|
},
|
||||||
|
startKey
|
||||||
|
);
|
||||||
|
throw error;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (this.state.isScaleSaveable) {
|
if (this.state.isScaleSaveable) {
|
||||||
@@ -500,11 +532,12 @@ export class SettingsComponent extends React.Component<SettingsComponentProps, S
|
|||||||
}
|
}
|
||||||
const updatedOffer: DataModels.Offer = await updateOffer(updateOfferParams);
|
const updatedOffer: DataModels.Offer = await updateOffer(updateOfferParams);
|
||||||
this.collection.offer(updatedOffer);
|
this.collection.offer(updatedOffer);
|
||||||
this.setState({ isScaleSaveable: false, isScaleDiscardable: false });
|
|
||||||
if (this.state.isAutoPilotSelected) {
|
if (this.state.isAutoPilotSelected) {
|
||||||
|
const autoPilotOffer = await this.collection.loadAutopilotOfferWithRetry();
|
||||||
this.setState({
|
this.setState({
|
||||||
autoPilotThroughput: updatedOffer.content.offerAutopilotSettings.maxThroughput,
|
autoPilotThroughput: autoPilotOffer.content.offerAutopilotSettings.maxThroughput,
|
||||||
autoPilotThroughputBaseline: updatedOffer.content.offerAutopilotSettings.maxThroughput
|
autoPilotThroughputBaseline: autoPilotOffer.content.offerAutopilotSettings.maxThroughput
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
this.setState({
|
this.setState({
|
||||||
@@ -512,6 +545,8 @@ export class SettingsComponent extends React.Component<SettingsComponentProps, S
|
|||||||
throughputBaseline: updatedOffer.content.offerThroughput
|
throughputBaseline: updatedOffer.content.offerThroughput
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
this.setState({ isScaleSaveable: false, isScaleDiscardable: false });
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
this.container.isRefreshingExplorer(false);
|
this.container.isRefreshingExplorer(false);
|
||||||
@@ -529,10 +564,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,
|
||||||
{
|
{
|
||||||
@@ -542,7 +577,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
|
error: getErrorMessage(error),
|
||||||
|
errorStack: getErrorStack(error)
|
||||||
},
|
},
|
||||||
startKey
|
startKey
|
||||||
);
|
);
|
||||||
@@ -867,7 +903,6 @@ export class SettingsComponent extends React.Component<SettingsComponentProps, S
|
|||||||
collection: this.collection,
|
collection: this.collection,
|
||||||
container: this.container,
|
container: this.container,
|
||||||
isFixedContainer: this.isFixedContainer,
|
isFixedContainer: this.isFixedContainer,
|
||||||
autoPilotTiersList: this.autoPilotTiersList,
|
|
||||||
onThroughputChange: this.onThroughputChange,
|
onThroughputChange: this.onThroughputChange,
|
||||||
throughput: this.state.throughput,
|
throughput: this.state.throughput,
|
||||||
throughputBaseline: this.state.throughputBaseline,
|
throughputBaseline: this.state.throughputBaseline,
|
||||||
@@ -916,6 +951,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
|
||||||
|
|||||||
@@ -20,7 +20,6 @@ describe("ScaleComponent", () => {
|
|||||||
collection: collection,
|
collection: collection,
|
||||||
container: container,
|
container: container,
|
||||||
isFixedContainer: false,
|
isFixedContainer: false,
|
||||||
autoPilotTiersList: [],
|
|
||||||
onThroughputChange: () => {
|
onThroughputChange: () => {
|
||||||
return;
|
return;
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -24,7 +24,6 @@ export interface ScaleComponentProps {
|
|||||||
collection: ViewModels.Collection;
|
collection: ViewModels.Collection;
|
||||||
container: Explorer;
|
container: Explorer;
|
||||||
isFixedContainer: boolean;
|
isFixedContainer: boolean;
|
||||||
autoPilotTiersList: ViewModels.DropdownOption<DataModels.AutopilotTier>[];
|
|
||||||
onThroughputChange: (newThroughput: number) => void;
|
onThroughputChange: (newThroughput: number) => void;
|
||||||
throughput: number;
|
throughput: number;
|
||||||
throughputBaseline: number;
|
throughputBaseline: number;
|
||||||
@@ -86,7 +85,7 @@ export class ScaleComponent extends React.Component<ScaleComponentProps> {
|
|||||||
|
|
||||||
public getThroughputTitle = (): string => {
|
public getThroughputTitle = (): string => {
|
||||||
if (this.props.isAutoPilotSelected) {
|
if (this.props.isAutoPilotSelected) {
|
||||||
return AutoPilotUtils.getAutoPilotHeaderText(false);
|
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();
|
||||||
|
|||||||
@@ -8,6 +8,9 @@ exports[`IndexingPolicyComponent renders 1`] = `
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
>
|
>
|
||||||
|
<IndexingPolicyRefreshComponent
|
||||||
|
refreshIndexTransformationProgress={[Function]}
|
||||||
|
/>
|
||||||
<div
|
<div
|
||||||
className="settingsV2IndexingPolicyEditor"
|
className="settingsV2IndexingPolicyEditor"
|
||||||
tabIndex={0}
|
tabIndex={0}
|
||||||
|
|||||||
@@ -15,7 +15,8 @@ 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";
|
||||||
@@ -139,3 +140,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();
|
||||||
|
});
|
||||||
|
|||||||
@@ -250,3 +250,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;
|
||||||
|
|||||||
@@ -40,7 +40,6 @@ exports[`SettingsComponent renders 1`] = `
|
|||||||
"_openShareDialog": [Function],
|
"_openShareDialog": [Function],
|
||||||
"_panes": Array [
|
"_panes": Array [
|
||||||
AddDatabasePane {
|
AddDatabasePane {
|
||||||
"autoPilotTiersList": [Function],
|
|
||||||
"autoPilotUsageCost": [Function],
|
"autoPilotUsageCost": [Function],
|
||||||
"canConfigureThroughput": [Function],
|
"canConfigureThroughput": [Function],
|
||||||
"canExceedMaximumValue": [Function],
|
"canExceedMaximumValue": [Function],
|
||||||
@@ -56,7 +55,6 @@ exports[`SettingsComponent renders 1`] = `
|
|||||||
"firstFieldHasFocus": [Function],
|
"firstFieldHasFocus": [Function],
|
||||||
"formErrors": [Function],
|
"formErrors": [Function],
|
||||||
"formErrorsDetails": [Function],
|
"formErrorsDetails": [Function],
|
||||||
"hasAutoPilotV2FeatureFlag": [Function],
|
|
||||||
"id": "adddatabasepane",
|
"id": "adddatabasepane",
|
||||||
"isAutoPilotSelected": [Function],
|
"isAutoPilotSelected": [Function],
|
||||||
"isExecuting": [Function],
|
"isExecuting": [Function],
|
||||||
@@ -69,7 +67,6 @@ exports[`SettingsComponent renders 1`] = `
|
|||||||
"onMoreDetailsKeyPress": [Function],
|
"onMoreDetailsKeyPress": [Function],
|
||||||
"requestUnitsUsageCost": [Function],
|
"requestUnitsUsageCost": [Function],
|
||||||
"ruToolTipText": [Function],
|
"ruToolTipText": [Function],
|
||||||
"selectedAutoPilotTier": [Function],
|
|
||||||
"showUpsellMessage": [Function],
|
"showUpsellMessage": [Function],
|
||||||
"throughput": [Function],
|
"throughput": [Function],
|
||||||
"throughputRangeText": [Function],
|
"throughputRangeText": [Function],
|
||||||
@@ -86,7 +83,6 @@ exports[`SettingsComponent renders 1`] = `
|
|||||||
AddCollectionPane {
|
AddCollectionPane {
|
||||||
"_isSynapseLinkEnabled": [Function],
|
"_isSynapseLinkEnabled": [Function],
|
||||||
"autoPilotThroughput": [Function],
|
"autoPilotThroughput": [Function],
|
||||||
"autoPilotTiersList": [Function],
|
|
||||||
"autoPilotUsageCost": [Function],
|
"autoPilotUsageCost": [Function],
|
||||||
"canConfigureThroughput": [Function],
|
"canConfigureThroughput": [Function],
|
||||||
"canExceedMaximumValue": [Function],
|
"canExceedMaximumValue": [Function],
|
||||||
@@ -108,7 +104,6 @@ exports[`SettingsComponent renders 1`] = `
|
|||||||
"formErrors": [Function],
|
"formErrors": [Function],
|
||||||
"formErrorsDetails": [Function],
|
"formErrorsDetails": [Function],
|
||||||
"formWarnings": [Function],
|
"formWarnings": [Function],
|
||||||
"hasAutoPilotV2FeatureFlag": [Function],
|
|
||||||
"id": "addcollectionpane",
|
"id": "addcollectionpane",
|
||||||
"isAnalyticalStorageOn": [Function],
|
"isAnalyticalStorageOn": [Function],
|
||||||
"isAutoPilotSelected": [Function],
|
"isAutoPilotSelected": [Function],
|
||||||
@@ -140,10 +135,7 @@ exports[`SettingsComponent renders 1`] = `
|
|||||||
"ruToolTipText": [Function],
|
"ruToolTipText": [Function],
|
||||||
"rupm": [Function],
|
"rupm": [Function],
|
||||||
"rupmVisible": [Function],
|
"rupmVisible": [Function],
|
||||||
"selectedAutoPilotTier": [Function],
|
|
||||||
"selectedSharedAutoPilotTier": [Function],
|
|
||||||
"sharedAutoPilotThroughput": [Function],
|
"sharedAutoPilotThroughput": [Function],
|
||||||
"sharedAutoPilotTiersList": [Function],
|
|
||||||
"sharedThroughputRangeText": [Function],
|
"sharedThroughputRangeText": [Function],
|
||||||
"shouldCreateMongoWildcardIndex": [Function],
|
"shouldCreateMongoWildcardIndex": [Function],
|
||||||
"shouldUseDatabaseThroughput": [Function],
|
"shouldUseDatabaseThroughput": [Function],
|
||||||
@@ -354,7 +346,6 @@ exports[`SettingsComponent renders 1`] = `
|
|||||||
"visible": [Function],
|
"visible": [Function],
|
||||||
},
|
},
|
||||||
CassandraAddCollectionPane {
|
CassandraAddCollectionPane {
|
||||||
"autoPilotTiersList": [Function],
|
|
||||||
"autoPilotUsageCost": [Function],
|
"autoPilotUsageCost": [Function],
|
||||||
"canConfigureThroughput": [Function],
|
"canConfigureThroughput": [Function],
|
||||||
"canExceedMaximumValue": [Function],
|
"canExceedMaximumValue": [Function],
|
||||||
@@ -366,7 +357,6 @@ exports[`SettingsComponent renders 1`] = `
|
|||||||
"firstFieldHasFocus": [Function],
|
"firstFieldHasFocus": [Function],
|
||||||
"formErrors": [Function],
|
"formErrors": [Function],
|
||||||
"formErrorsDetails": [Function],
|
"formErrorsDetails": [Function],
|
||||||
"hasAutoPilotV2FeatureFlag": [Function],
|
|
||||||
"id": "cassandraaddcollectionpane",
|
"id": "cassandraaddcollectionpane",
|
||||||
"isAutoPilotSelected": [Function],
|
"isAutoPilotSelected": [Function],
|
||||||
"isExecuting": [Function],
|
"isExecuting": [Function],
|
||||||
@@ -387,10 +377,7 @@ exports[`SettingsComponent renders 1`] = `
|
|||||||
"requestUnitsUsageCostShared": [Function],
|
"requestUnitsUsageCostShared": [Function],
|
||||||
"ruToolTipText": [Function],
|
"ruToolTipText": [Function],
|
||||||
"selectedAutoPilotThroughput": [Function],
|
"selectedAutoPilotThroughput": [Function],
|
||||||
"selectedAutoPilotTier": [Function],
|
|
||||||
"selectedSharedAutoPilotTier": [Function],
|
|
||||||
"sharedAutoPilotThroughput": [Function],
|
"sharedAutoPilotThroughput": [Function],
|
||||||
"sharedAutoPilotTiersList": [Function],
|
|
||||||
"sharedThroughputRangeText": [Function],
|
"sharedThroughputRangeText": [Function],
|
||||||
"sharedThroughputSpendAck": [Function],
|
"sharedThroughputSpendAck": [Function],
|
||||||
"sharedThroughputSpendAckText": [Function],
|
"sharedThroughputSpendAckText": [Function],
|
||||||
@@ -585,7 +572,6 @@ exports[`SettingsComponent renders 1`] = `
|
|||||||
"addCollectionPane": AddCollectionPane {
|
"addCollectionPane": AddCollectionPane {
|
||||||
"_isSynapseLinkEnabled": [Function],
|
"_isSynapseLinkEnabled": [Function],
|
||||||
"autoPilotThroughput": [Function],
|
"autoPilotThroughput": [Function],
|
||||||
"autoPilotTiersList": [Function],
|
|
||||||
"autoPilotUsageCost": [Function],
|
"autoPilotUsageCost": [Function],
|
||||||
"canConfigureThroughput": [Function],
|
"canConfigureThroughput": [Function],
|
||||||
"canExceedMaximumValue": [Function],
|
"canExceedMaximumValue": [Function],
|
||||||
@@ -607,7 +593,6 @@ exports[`SettingsComponent renders 1`] = `
|
|||||||
"formErrors": [Function],
|
"formErrors": [Function],
|
||||||
"formErrorsDetails": [Function],
|
"formErrorsDetails": [Function],
|
||||||
"formWarnings": [Function],
|
"formWarnings": [Function],
|
||||||
"hasAutoPilotV2FeatureFlag": [Function],
|
|
||||||
"id": "addcollectionpane",
|
"id": "addcollectionpane",
|
||||||
"isAnalyticalStorageOn": [Function],
|
"isAnalyticalStorageOn": [Function],
|
||||||
"isAutoPilotSelected": [Function],
|
"isAutoPilotSelected": [Function],
|
||||||
@@ -639,10 +624,7 @@ exports[`SettingsComponent renders 1`] = `
|
|||||||
"ruToolTipText": [Function],
|
"ruToolTipText": [Function],
|
||||||
"rupm": [Function],
|
"rupm": [Function],
|
||||||
"rupmVisible": [Function],
|
"rupmVisible": [Function],
|
||||||
"selectedAutoPilotTier": [Function],
|
|
||||||
"selectedSharedAutoPilotTier": [Function],
|
|
||||||
"sharedAutoPilotThroughput": [Function],
|
"sharedAutoPilotThroughput": [Function],
|
||||||
"sharedAutoPilotTiersList": [Function],
|
|
||||||
"sharedThroughputRangeText": [Function],
|
"sharedThroughputRangeText": [Function],
|
||||||
"shouldCreateMongoWildcardIndex": [Function],
|
"shouldCreateMongoWildcardIndex": [Function],
|
||||||
"shouldUseDatabaseThroughput": [Function],
|
"shouldUseDatabaseThroughput": [Function],
|
||||||
@@ -672,7 +654,6 @@ exports[`SettingsComponent renders 1`] = `
|
|||||||
},
|
},
|
||||||
"addCollectionText": [Function],
|
"addCollectionText": [Function],
|
||||||
"addDatabasePane": AddDatabasePane {
|
"addDatabasePane": AddDatabasePane {
|
||||||
"autoPilotTiersList": [Function],
|
|
||||||
"autoPilotUsageCost": [Function],
|
"autoPilotUsageCost": [Function],
|
||||||
"canConfigureThroughput": [Function],
|
"canConfigureThroughput": [Function],
|
||||||
"canExceedMaximumValue": [Function],
|
"canExceedMaximumValue": [Function],
|
||||||
@@ -688,7 +669,6 @@ exports[`SettingsComponent renders 1`] = `
|
|||||||
"firstFieldHasFocus": [Function],
|
"firstFieldHasFocus": [Function],
|
||||||
"formErrors": [Function],
|
"formErrors": [Function],
|
||||||
"formErrorsDetails": [Function],
|
"formErrorsDetails": [Function],
|
||||||
"hasAutoPilotV2FeatureFlag": [Function],
|
|
||||||
"id": "adddatabasepane",
|
"id": "adddatabasepane",
|
||||||
"isAutoPilotSelected": [Function],
|
"isAutoPilotSelected": [Function],
|
||||||
"isExecuting": [Function],
|
"isExecuting": [Function],
|
||||||
@@ -701,7 +681,6 @@ exports[`SettingsComponent renders 1`] = `
|
|||||||
"onMoreDetailsKeyPress": [Function],
|
"onMoreDetailsKeyPress": [Function],
|
||||||
"requestUnitsUsageCost": [Function],
|
"requestUnitsUsageCost": [Function],
|
||||||
"ruToolTipText": [Function],
|
"ruToolTipText": [Function],
|
||||||
"selectedAutoPilotTier": [Function],
|
|
||||||
"showUpsellMessage": [Function],
|
"showUpsellMessage": [Function],
|
||||||
"throughput": [Function],
|
"throughput": [Function],
|
||||||
"throughputRangeText": [Function],
|
"throughputRangeText": [Function],
|
||||||
@@ -778,7 +757,6 @@ exports[`SettingsComponent renders 1`] = `
|
|||||||
"canExceedMaximumValue": [Function],
|
"canExceedMaximumValue": [Function],
|
||||||
"canSaveQueries": [Function],
|
"canSaveQueries": [Function],
|
||||||
"cassandraAddCollectionPane": CassandraAddCollectionPane {
|
"cassandraAddCollectionPane": CassandraAddCollectionPane {
|
||||||
"autoPilotTiersList": [Function],
|
|
||||||
"autoPilotUsageCost": [Function],
|
"autoPilotUsageCost": [Function],
|
||||||
"canConfigureThroughput": [Function],
|
"canConfigureThroughput": [Function],
|
||||||
"canExceedMaximumValue": [Function],
|
"canExceedMaximumValue": [Function],
|
||||||
@@ -790,7 +768,6 @@ exports[`SettingsComponent renders 1`] = `
|
|||||||
"firstFieldHasFocus": [Function],
|
"firstFieldHasFocus": [Function],
|
||||||
"formErrors": [Function],
|
"formErrors": [Function],
|
||||||
"formErrorsDetails": [Function],
|
"formErrorsDetails": [Function],
|
||||||
"hasAutoPilotV2FeatureFlag": [Function],
|
|
||||||
"id": "cassandraaddcollectionpane",
|
"id": "cassandraaddcollectionpane",
|
||||||
"isAutoPilotSelected": [Function],
|
"isAutoPilotSelected": [Function],
|
||||||
"isExecuting": [Function],
|
"isExecuting": [Function],
|
||||||
@@ -811,10 +788,7 @@ exports[`SettingsComponent renders 1`] = `
|
|||||||
"requestUnitsUsageCostShared": [Function],
|
"requestUnitsUsageCostShared": [Function],
|
||||||
"ruToolTipText": [Function],
|
"ruToolTipText": [Function],
|
||||||
"selectedAutoPilotThroughput": [Function],
|
"selectedAutoPilotThroughput": [Function],
|
||||||
"selectedAutoPilotTier": [Function],
|
|
||||||
"selectedSharedAutoPilotTier": [Function],
|
|
||||||
"sharedAutoPilotThroughput": [Function],
|
"sharedAutoPilotThroughput": [Function],
|
||||||
"sharedAutoPilotTiersList": [Function],
|
|
||||||
"sharedThroughputRangeText": [Function],
|
"sharedThroughputRangeText": [Function],
|
||||||
"sharedThroughputSpendAck": [Function],
|
"sharedThroughputSpendAck": [Function],
|
||||||
"sharedThroughputSpendAckText": [Function],
|
"sharedThroughputSpendAckText": [Function],
|
||||||
@@ -969,7 +943,6 @@ exports[`SettingsComponent renders 1`] = `
|
|||||||
"title": [Function],
|
"title": [Function],
|
||||||
"visible": [Function],
|
"visible": [Function],
|
||||||
},
|
},
|
||||||
"hasAutoPilotV2FeatureFlag": [Function],
|
|
||||||
"hasStorageAnalyticsAfecFeature": [Function],
|
"hasStorageAnalyticsAfecFeature": [Function],
|
||||||
"hasWriteAccess": [Function],
|
"hasWriteAccess": [Function],
|
||||||
"isAccountReady": [Function],
|
"isAccountReady": [Function],
|
||||||
@@ -1347,7 +1320,6 @@ exports[`SettingsComponent renders 1`] = `
|
|||||||
"_openShareDialog": [Function],
|
"_openShareDialog": [Function],
|
||||||
"_panes": Array [
|
"_panes": Array [
|
||||||
AddDatabasePane {
|
AddDatabasePane {
|
||||||
"autoPilotTiersList": [Function],
|
|
||||||
"autoPilotUsageCost": [Function],
|
"autoPilotUsageCost": [Function],
|
||||||
"canConfigureThroughput": [Function],
|
"canConfigureThroughput": [Function],
|
||||||
"canExceedMaximumValue": [Function],
|
"canExceedMaximumValue": [Function],
|
||||||
@@ -1363,7 +1335,6 @@ exports[`SettingsComponent renders 1`] = `
|
|||||||
"firstFieldHasFocus": [Function],
|
"firstFieldHasFocus": [Function],
|
||||||
"formErrors": [Function],
|
"formErrors": [Function],
|
||||||
"formErrorsDetails": [Function],
|
"formErrorsDetails": [Function],
|
||||||
"hasAutoPilotV2FeatureFlag": [Function],
|
|
||||||
"id": "adddatabasepane",
|
"id": "adddatabasepane",
|
||||||
"isAutoPilotSelected": [Function],
|
"isAutoPilotSelected": [Function],
|
||||||
"isExecuting": [Function],
|
"isExecuting": [Function],
|
||||||
@@ -1376,7 +1347,6 @@ exports[`SettingsComponent renders 1`] = `
|
|||||||
"onMoreDetailsKeyPress": [Function],
|
"onMoreDetailsKeyPress": [Function],
|
||||||
"requestUnitsUsageCost": [Function],
|
"requestUnitsUsageCost": [Function],
|
||||||
"ruToolTipText": [Function],
|
"ruToolTipText": [Function],
|
||||||
"selectedAutoPilotTier": [Function],
|
|
||||||
"showUpsellMessage": [Function],
|
"showUpsellMessage": [Function],
|
||||||
"throughput": [Function],
|
"throughput": [Function],
|
||||||
"throughputRangeText": [Function],
|
"throughputRangeText": [Function],
|
||||||
@@ -1393,7 +1363,6 @@ exports[`SettingsComponent renders 1`] = `
|
|||||||
AddCollectionPane {
|
AddCollectionPane {
|
||||||
"_isSynapseLinkEnabled": [Function],
|
"_isSynapseLinkEnabled": [Function],
|
||||||
"autoPilotThroughput": [Function],
|
"autoPilotThroughput": [Function],
|
||||||
"autoPilotTiersList": [Function],
|
|
||||||
"autoPilotUsageCost": [Function],
|
"autoPilotUsageCost": [Function],
|
||||||
"canConfigureThroughput": [Function],
|
"canConfigureThroughput": [Function],
|
||||||
"canExceedMaximumValue": [Function],
|
"canExceedMaximumValue": [Function],
|
||||||
@@ -1415,7 +1384,6 @@ exports[`SettingsComponent renders 1`] = `
|
|||||||
"formErrors": [Function],
|
"formErrors": [Function],
|
||||||
"formErrorsDetails": [Function],
|
"formErrorsDetails": [Function],
|
||||||
"formWarnings": [Function],
|
"formWarnings": [Function],
|
||||||
"hasAutoPilotV2FeatureFlag": [Function],
|
|
||||||
"id": "addcollectionpane",
|
"id": "addcollectionpane",
|
||||||
"isAnalyticalStorageOn": [Function],
|
"isAnalyticalStorageOn": [Function],
|
||||||
"isAutoPilotSelected": [Function],
|
"isAutoPilotSelected": [Function],
|
||||||
@@ -1447,10 +1415,7 @@ exports[`SettingsComponent renders 1`] = `
|
|||||||
"ruToolTipText": [Function],
|
"ruToolTipText": [Function],
|
||||||
"rupm": [Function],
|
"rupm": [Function],
|
||||||
"rupmVisible": [Function],
|
"rupmVisible": [Function],
|
||||||
"selectedAutoPilotTier": [Function],
|
|
||||||
"selectedSharedAutoPilotTier": [Function],
|
|
||||||
"sharedAutoPilotThroughput": [Function],
|
"sharedAutoPilotThroughput": [Function],
|
||||||
"sharedAutoPilotTiersList": [Function],
|
|
||||||
"sharedThroughputRangeText": [Function],
|
"sharedThroughputRangeText": [Function],
|
||||||
"shouldCreateMongoWildcardIndex": [Function],
|
"shouldCreateMongoWildcardIndex": [Function],
|
||||||
"shouldUseDatabaseThroughput": [Function],
|
"shouldUseDatabaseThroughput": [Function],
|
||||||
@@ -1661,7 +1626,6 @@ exports[`SettingsComponent renders 1`] = `
|
|||||||
"visible": [Function],
|
"visible": [Function],
|
||||||
},
|
},
|
||||||
CassandraAddCollectionPane {
|
CassandraAddCollectionPane {
|
||||||
"autoPilotTiersList": [Function],
|
|
||||||
"autoPilotUsageCost": [Function],
|
"autoPilotUsageCost": [Function],
|
||||||
"canConfigureThroughput": [Function],
|
"canConfigureThroughput": [Function],
|
||||||
"canExceedMaximumValue": [Function],
|
"canExceedMaximumValue": [Function],
|
||||||
@@ -1673,7 +1637,6 @@ exports[`SettingsComponent renders 1`] = `
|
|||||||
"firstFieldHasFocus": [Function],
|
"firstFieldHasFocus": [Function],
|
||||||
"formErrors": [Function],
|
"formErrors": [Function],
|
||||||
"formErrorsDetails": [Function],
|
"formErrorsDetails": [Function],
|
||||||
"hasAutoPilotV2FeatureFlag": [Function],
|
|
||||||
"id": "cassandraaddcollectionpane",
|
"id": "cassandraaddcollectionpane",
|
||||||
"isAutoPilotSelected": [Function],
|
"isAutoPilotSelected": [Function],
|
||||||
"isExecuting": [Function],
|
"isExecuting": [Function],
|
||||||
@@ -1694,10 +1657,7 @@ exports[`SettingsComponent renders 1`] = `
|
|||||||
"requestUnitsUsageCostShared": [Function],
|
"requestUnitsUsageCostShared": [Function],
|
||||||
"ruToolTipText": [Function],
|
"ruToolTipText": [Function],
|
||||||
"selectedAutoPilotThroughput": [Function],
|
"selectedAutoPilotThroughput": [Function],
|
||||||
"selectedAutoPilotTier": [Function],
|
|
||||||
"selectedSharedAutoPilotTier": [Function],
|
|
||||||
"sharedAutoPilotThroughput": [Function],
|
"sharedAutoPilotThroughput": [Function],
|
||||||
"sharedAutoPilotTiersList": [Function],
|
|
||||||
"sharedThroughputRangeText": [Function],
|
"sharedThroughputRangeText": [Function],
|
||||||
"sharedThroughputSpendAck": [Function],
|
"sharedThroughputSpendAck": [Function],
|
||||||
"sharedThroughputSpendAckText": [Function],
|
"sharedThroughputSpendAckText": [Function],
|
||||||
@@ -1892,7 +1852,6 @@ exports[`SettingsComponent renders 1`] = `
|
|||||||
"addCollectionPane": AddCollectionPane {
|
"addCollectionPane": AddCollectionPane {
|
||||||
"_isSynapseLinkEnabled": [Function],
|
"_isSynapseLinkEnabled": [Function],
|
||||||
"autoPilotThroughput": [Function],
|
"autoPilotThroughput": [Function],
|
||||||
"autoPilotTiersList": [Function],
|
|
||||||
"autoPilotUsageCost": [Function],
|
"autoPilotUsageCost": [Function],
|
||||||
"canConfigureThroughput": [Function],
|
"canConfigureThroughput": [Function],
|
||||||
"canExceedMaximumValue": [Function],
|
"canExceedMaximumValue": [Function],
|
||||||
@@ -1914,7 +1873,6 @@ exports[`SettingsComponent renders 1`] = `
|
|||||||
"formErrors": [Function],
|
"formErrors": [Function],
|
||||||
"formErrorsDetails": [Function],
|
"formErrorsDetails": [Function],
|
||||||
"formWarnings": [Function],
|
"formWarnings": [Function],
|
||||||
"hasAutoPilotV2FeatureFlag": [Function],
|
|
||||||
"id": "addcollectionpane",
|
"id": "addcollectionpane",
|
||||||
"isAnalyticalStorageOn": [Function],
|
"isAnalyticalStorageOn": [Function],
|
||||||
"isAutoPilotSelected": [Function],
|
"isAutoPilotSelected": [Function],
|
||||||
@@ -1946,10 +1904,7 @@ exports[`SettingsComponent renders 1`] = `
|
|||||||
"ruToolTipText": [Function],
|
"ruToolTipText": [Function],
|
||||||
"rupm": [Function],
|
"rupm": [Function],
|
||||||
"rupmVisible": [Function],
|
"rupmVisible": [Function],
|
||||||
"selectedAutoPilotTier": [Function],
|
|
||||||
"selectedSharedAutoPilotTier": [Function],
|
|
||||||
"sharedAutoPilotThroughput": [Function],
|
"sharedAutoPilotThroughput": [Function],
|
||||||
"sharedAutoPilotTiersList": [Function],
|
|
||||||
"sharedThroughputRangeText": [Function],
|
"sharedThroughputRangeText": [Function],
|
||||||
"shouldCreateMongoWildcardIndex": [Function],
|
"shouldCreateMongoWildcardIndex": [Function],
|
||||||
"shouldUseDatabaseThroughput": [Function],
|
"shouldUseDatabaseThroughput": [Function],
|
||||||
@@ -1979,7 +1934,6 @@ exports[`SettingsComponent renders 1`] = `
|
|||||||
},
|
},
|
||||||
"addCollectionText": [Function],
|
"addCollectionText": [Function],
|
||||||
"addDatabasePane": AddDatabasePane {
|
"addDatabasePane": AddDatabasePane {
|
||||||
"autoPilotTiersList": [Function],
|
|
||||||
"autoPilotUsageCost": [Function],
|
"autoPilotUsageCost": [Function],
|
||||||
"canConfigureThroughput": [Function],
|
"canConfigureThroughput": [Function],
|
||||||
"canExceedMaximumValue": [Function],
|
"canExceedMaximumValue": [Function],
|
||||||
@@ -1995,7 +1949,6 @@ exports[`SettingsComponent renders 1`] = `
|
|||||||
"firstFieldHasFocus": [Function],
|
"firstFieldHasFocus": [Function],
|
||||||
"formErrors": [Function],
|
"formErrors": [Function],
|
||||||
"formErrorsDetails": [Function],
|
"formErrorsDetails": [Function],
|
||||||
"hasAutoPilotV2FeatureFlag": [Function],
|
|
||||||
"id": "adddatabasepane",
|
"id": "adddatabasepane",
|
||||||
"isAutoPilotSelected": [Function],
|
"isAutoPilotSelected": [Function],
|
||||||
"isExecuting": [Function],
|
"isExecuting": [Function],
|
||||||
@@ -2008,7 +1961,6 @@ exports[`SettingsComponent renders 1`] = `
|
|||||||
"onMoreDetailsKeyPress": [Function],
|
"onMoreDetailsKeyPress": [Function],
|
||||||
"requestUnitsUsageCost": [Function],
|
"requestUnitsUsageCost": [Function],
|
||||||
"ruToolTipText": [Function],
|
"ruToolTipText": [Function],
|
||||||
"selectedAutoPilotTier": [Function],
|
|
||||||
"showUpsellMessage": [Function],
|
"showUpsellMessage": [Function],
|
||||||
"throughput": [Function],
|
"throughput": [Function],
|
||||||
"throughputRangeText": [Function],
|
"throughputRangeText": [Function],
|
||||||
@@ -2085,7 +2037,6 @@ exports[`SettingsComponent renders 1`] = `
|
|||||||
"canExceedMaximumValue": [Function],
|
"canExceedMaximumValue": [Function],
|
||||||
"canSaveQueries": [Function],
|
"canSaveQueries": [Function],
|
||||||
"cassandraAddCollectionPane": CassandraAddCollectionPane {
|
"cassandraAddCollectionPane": CassandraAddCollectionPane {
|
||||||
"autoPilotTiersList": [Function],
|
|
||||||
"autoPilotUsageCost": [Function],
|
"autoPilotUsageCost": [Function],
|
||||||
"canConfigureThroughput": [Function],
|
"canConfigureThroughput": [Function],
|
||||||
"canExceedMaximumValue": [Function],
|
"canExceedMaximumValue": [Function],
|
||||||
@@ -2097,7 +2048,6 @@ exports[`SettingsComponent renders 1`] = `
|
|||||||
"firstFieldHasFocus": [Function],
|
"firstFieldHasFocus": [Function],
|
||||||
"formErrors": [Function],
|
"formErrors": [Function],
|
||||||
"formErrorsDetails": [Function],
|
"formErrorsDetails": [Function],
|
||||||
"hasAutoPilotV2FeatureFlag": [Function],
|
|
||||||
"id": "cassandraaddcollectionpane",
|
"id": "cassandraaddcollectionpane",
|
||||||
"isAutoPilotSelected": [Function],
|
"isAutoPilotSelected": [Function],
|
||||||
"isExecuting": [Function],
|
"isExecuting": [Function],
|
||||||
@@ -2118,10 +2068,7 @@ exports[`SettingsComponent renders 1`] = `
|
|||||||
"requestUnitsUsageCostShared": [Function],
|
"requestUnitsUsageCostShared": [Function],
|
||||||
"ruToolTipText": [Function],
|
"ruToolTipText": [Function],
|
||||||
"selectedAutoPilotThroughput": [Function],
|
"selectedAutoPilotThroughput": [Function],
|
||||||
"selectedAutoPilotTier": [Function],
|
|
||||||
"selectedSharedAutoPilotTier": [Function],
|
|
||||||
"sharedAutoPilotThroughput": [Function],
|
"sharedAutoPilotThroughput": [Function],
|
||||||
"sharedAutoPilotTiersList": [Function],
|
|
||||||
"sharedThroughputRangeText": [Function],
|
"sharedThroughputRangeText": [Function],
|
||||||
"sharedThroughputSpendAck": [Function],
|
"sharedThroughputSpendAck": [Function],
|
||||||
"sharedThroughputSpendAckText": [Function],
|
"sharedThroughputSpendAckText": [Function],
|
||||||
@@ -2276,7 +2223,6 @@ exports[`SettingsComponent renders 1`] = `
|
|||||||
"title": [Function],
|
"title": [Function],
|
||||||
"visible": [Function],
|
"visible": [Function],
|
||||||
},
|
},
|
||||||
"hasAutoPilotV2FeatureFlag": [Function],
|
|
||||||
"hasStorageAnalyticsAfecFeature": [Function],
|
"hasStorageAnalyticsAfecFeature": [Function],
|
||||||
"hasWriteAccess": [Function],
|
"hasWriteAccess": [Function],
|
||||||
"isAccountReady": [Function],
|
"isAccountReady": [Function],
|
||||||
@@ -2667,7 +2613,6 @@ exports[`SettingsComponent renders 1`] = `
|
|||||||
"_openShareDialog": [Function],
|
"_openShareDialog": [Function],
|
||||||
"_panes": Array [
|
"_panes": Array [
|
||||||
AddDatabasePane {
|
AddDatabasePane {
|
||||||
"autoPilotTiersList": [Function],
|
|
||||||
"autoPilotUsageCost": [Function],
|
"autoPilotUsageCost": [Function],
|
||||||
"canConfigureThroughput": [Function],
|
"canConfigureThroughput": [Function],
|
||||||
"canExceedMaximumValue": [Function],
|
"canExceedMaximumValue": [Function],
|
||||||
@@ -2683,7 +2628,6 @@ exports[`SettingsComponent renders 1`] = `
|
|||||||
"firstFieldHasFocus": [Function],
|
"firstFieldHasFocus": [Function],
|
||||||
"formErrors": [Function],
|
"formErrors": [Function],
|
||||||
"formErrorsDetails": [Function],
|
"formErrorsDetails": [Function],
|
||||||
"hasAutoPilotV2FeatureFlag": [Function],
|
|
||||||
"id": "adddatabasepane",
|
"id": "adddatabasepane",
|
||||||
"isAutoPilotSelected": [Function],
|
"isAutoPilotSelected": [Function],
|
||||||
"isExecuting": [Function],
|
"isExecuting": [Function],
|
||||||
@@ -2696,7 +2640,6 @@ exports[`SettingsComponent renders 1`] = `
|
|||||||
"onMoreDetailsKeyPress": [Function],
|
"onMoreDetailsKeyPress": [Function],
|
||||||
"requestUnitsUsageCost": [Function],
|
"requestUnitsUsageCost": [Function],
|
||||||
"ruToolTipText": [Function],
|
"ruToolTipText": [Function],
|
||||||
"selectedAutoPilotTier": [Function],
|
|
||||||
"showUpsellMessage": [Function],
|
"showUpsellMessage": [Function],
|
||||||
"throughput": [Function],
|
"throughput": [Function],
|
||||||
"throughputRangeText": [Function],
|
"throughputRangeText": [Function],
|
||||||
@@ -2713,7 +2656,6 @@ exports[`SettingsComponent renders 1`] = `
|
|||||||
AddCollectionPane {
|
AddCollectionPane {
|
||||||
"_isSynapseLinkEnabled": [Function],
|
"_isSynapseLinkEnabled": [Function],
|
||||||
"autoPilotThroughput": [Function],
|
"autoPilotThroughput": [Function],
|
||||||
"autoPilotTiersList": [Function],
|
|
||||||
"autoPilotUsageCost": [Function],
|
"autoPilotUsageCost": [Function],
|
||||||
"canConfigureThroughput": [Function],
|
"canConfigureThroughput": [Function],
|
||||||
"canExceedMaximumValue": [Function],
|
"canExceedMaximumValue": [Function],
|
||||||
@@ -2735,7 +2677,6 @@ exports[`SettingsComponent renders 1`] = `
|
|||||||
"formErrors": [Function],
|
"formErrors": [Function],
|
||||||
"formErrorsDetails": [Function],
|
"formErrorsDetails": [Function],
|
||||||
"formWarnings": [Function],
|
"formWarnings": [Function],
|
||||||
"hasAutoPilotV2FeatureFlag": [Function],
|
|
||||||
"id": "addcollectionpane",
|
"id": "addcollectionpane",
|
||||||
"isAnalyticalStorageOn": [Function],
|
"isAnalyticalStorageOn": [Function],
|
||||||
"isAutoPilotSelected": [Function],
|
"isAutoPilotSelected": [Function],
|
||||||
@@ -2767,10 +2708,7 @@ exports[`SettingsComponent renders 1`] = `
|
|||||||
"ruToolTipText": [Function],
|
"ruToolTipText": [Function],
|
||||||
"rupm": [Function],
|
"rupm": [Function],
|
||||||
"rupmVisible": [Function],
|
"rupmVisible": [Function],
|
||||||
"selectedAutoPilotTier": [Function],
|
|
||||||
"selectedSharedAutoPilotTier": [Function],
|
|
||||||
"sharedAutoPilotThroughput": [Function],
|
"sharedAutoPilotThroughput": [Function],
|
||||||
"sharedAutoPilotTiersList": [Function],
|
|
||||||
"sharedThroughputRangeText": [Function],
|
"sharedThroughputRangeText": [Function],
|
||||||
"shouldCreateMongoWildcardIndex": [Function],
|
"shouldCreateMongoWildcardIndex": [Function],
|
||||||
"shouldUseDatabaseThroughput": [Function],
|
"shouldUseDatabaseThroughput": [Function],
|
||||||
@@ -2981,7 +2919,6 @@ exports[`SettingsComponent renders 1`] = `
|
|||||||
"visible": [Function],
|
"visible": [Function],
|
||||||
},
|
},
|
||||||
CassandraAddCollectionPane {
|
CassandraAddCollectionPane {
|
||||||
"autoPilotTiersList": [Function],
|
|
||||||
"autoPilotUsageCost": [Function],
|
"autoPilotUsageCost": [Function],
|
||||||
"canConfigureThroughput": [Function],
|
"canConfigureThroughput": [Function],
|
||||||
"canExceedMaximumValue": [Function],
|
"canExceedMaximumValue": [Function],
|
||||||
@@ -2993,7 +2930,6 @@ exports[`SettingsComponent renders 1`] = `
|
|||||||
"firstFieldHasFocus": [Function],
|
"firstFieldHasFocus": [Function],
|
||||||
"formErrors": [Function],
|
"formErrors": [Function],
|
||||||
"formErrorsDetails": [Function],
|
"formErrorsDetails": [Function],
|
||||||
"hasAutoPilotV2FeatureFlag": [Function],
|
|
||||||
"id": "cassandraaddcollectionpane",
|
"id": "cassandraaddcollectionpane",
|
||||||
"isAutoPilotSelected": [Function],
|
"isAutoPilotSelected": [Function],
|
||||||
"isExecuting": [Function],
|
"isExecuting": [Function],
|
||||||
@@ -3014,10 +2950,7 @@ exports[`SettingsComponent renders 1`] = `
|
|||||||
"requestUnitsUsageCostShared": [Function],
|
"requestUnitsUsageCostShared": [Function],
|
||||||
"ruToolTipText": [Function],
|
"ruToolTipText": [Function],
|
||||||
"selectedAutoPilotThroughput": [Function],
|
"selectedAutoPilotThroughput": [Function],
|
||||||
"selectedAutoPilotTier": [Function],
|
|
||||||
"selectedSharedAutoPilotTier": [Function],
|
|
||||||
"sharedAutoPilotThroughput": [Function],
|
"sharedAutoPilotThroughput": [Function],
|
||||||
"sharedAutoPilotTiersList": [Function],
|
|
||||||
"sharedThroughputRangeText": [Function],
|
"sharedThroughputRangeText": [Function],
|
||||||
"sharedThroughputSpendAck": [Function],
|
"sharedThroughputSpendAck": [Function],
|
||||||
"sharedThroughputSpendAckText": [Function],
|
"sharedThroughputSpendAckText": [Function],
|
||||||
@@ -3212,7 +3145,6 @@ exports[`SettingsComponent renders 1`] = `
|
|||||||
"addCollectionPane": AddCollectionPane {
|
"addCollectionPane": AddCollectionPane {
|
||||||
"_isSynapseLinkEnabled": [Function],
|
"_isSynapseLinkEnabled": [Function],
|
||||||
"autoPilotThroughput": [Function],
|
"autoPilotThroughput": [Function],
|
||||||
"autoPilotTiersList": [Function],
|
|
||||||
"autoPilotUsageCost": [Function],
|
"autoPilotUsageCost": [Function],
|
||||||
"canConfigureThroughput": [Function],
|
"canConfigureThroughput": [Function],
|
||||||
"canExceedMaximumValue": [Function],
|
"canExceedMaximumValue": [Function],
|
||||||
@@ -3234,7 +3166,6 @@ exports[`SettingsComponent renders 1`] = `
|
|||||||
"formErrors": [Function],
|
"formErrors": [Function],
|
||||||
"formErrorsDetails": [Function],
|
"formErrorsDetails": [Function],
|
||||||
"formWarnings": [Function],
|
"formWarnings": [Function],
|
||||||
"hasAutoPilotV2FeatureFlag": [Function],
|
|
||||||
"id": "addcollectionpane",
|
"id": "addcollectionpane",
|
||||||
"isAnalyticalStorageOn": [Function],
|
"isAnalyticalStorageOn": [Function],
|
||||||
"isAutoPilotSelected": [Function],
|
"isAutoPilotSelected": [Function],
|
||||||
@@ -3266,10 +3197,7 @@ exports[`SettingsComponent renders 1`] = `
|
|||||||
"ruToolTipText": [Function],
|
"ruToolTipText": [Function],
|
||||||
"rupm": [Function],
|
"rupm": [Function],
|
||||||
"rupmVisible": [Function],
|
"rupmVisible": [Function],
|
||||||
"selectedAutoPilotTier": [Function],
|
|
||||||
"selectedSharedAutoPilotTier": [Function],
|
|
||||||
"sharedAutoPilotThroughput": [Function],
|
"sharedAutoPilotThroughput": [Function],
|
||||||
"sharedAutoPilotTiersList": [Function],
|
|
||||||
"sharedThroughputRangeText": [Function],
|
"sharedThroughputRangeText": [Function],
|
||||||
"shouldCreateMongoWildcardIndex": [Function],
|
"shouldCreateMongoWildcardIndex": [Function],
|
||||||
"shouldUseDatabaseThroughput": [Function],
|
"shouldUseDatabaseThroughput": [Function],
|
||||||
@@ -3299,7 +3227,6 @@ exports[`SettingsComponent renders 1`] = `
|
|||||||
},
|
},
|
||||||
"addCollectionText": [Function],
|
"addCollectionText": [Function],
|
||||||
"addDatabasePane": AddDatabasePane {
|
"addDatabasePane": AddDatabasePane {
|
||||||
"autoPilotTiersList": [Function],
|
|
||||||
"autoPilotUsageCost": [Function],
|
"autoPilotUsageCost": [Function],
|
||||||
"canConfigureThroughput": [Function],
|
"canConfigureThroughput": [Function],
|
||||||
"canExceedMaximumValue": [Function],
|
"canExceedMaximumValue": [Function],
|
||||||
@@ -3315,7 +3242,6 @@ exports[`SettingsComponent renders 1`] = `
|
|||||||
"firstFieldHasFocus": [Function],
|
"firstFieldHasFocus": [Function],
|
||||||
"formErrors": [Function],
|
"formErrors": [Function],
|
||||||
"formErrorsDetails": [Function],
|
"formErrorsDetails": [Function],
|
||||||
"hasAutoPilotV2FeatureFlag": [Function],
|
|
||||||
"id": "adddatabasepane",
|
"id": "adddatabasepane",
|
||||||
"isAutoPilotSelected": [Function],
|
"isAutoPilotSelected": [Function],
|
||||||
"isExecuting": [Function],
|
"isExecuting": [Function],
|
||||||
@@ -3328,7 +3254,6 @@ exports[`SettingsComponent renders 1`] = `
|
|||||||
"onMoreDetailsKeyPress": [Function],
|
"onMoreDetailsKeyPress": [Function],
|
||||||
"requestUnitsUsageCost": [Function],
|
"requestUnitsUsageCost": [Function],
|
||||||
"ruToolTipText": [Function],
|
"ruToolTipText": [Function],
|
||||||
"selectedAutoPilotTier": [Function],
|
|
||||||
"showUpsellMessage": [Function],
|
"showUpsellMessage": [Function],
|
||||||
"throughput": [Function],
|
"throughput": [Function],
|
||||||
"throughputRangeText": [Function],
|
"throughputRangeText": [Function],
|
||||||
@@ -3405,7 +3330,6 @@ exports[`SettingsComponent renders 1`] = `
|
|||||||
"canExceedMaximumValue": [Function],
|
"canExceedMaximumValue": [Function],
|
||||||
"canSaveQueries": [Function],
|
"canSaveQueries": [Function],
|
||||||
"cassandraAddCollectionPane": CassandraAddCollectionPane {
|
"cassandraAddCollectionPane": CassandraAddCollectionPane {
|
||||||
"autoPilotTiersList": [Function],
|
|
||||||
"autoPilotUsageCost": [Function],
|
"autoPilotUsageCost": [Function],
|
||||||
"canConfigureThroughput": [Function],
|
"canConfigureThroughput": [Function],
|
||||||
"canExceedMaximumValue": [Function],
|
"canExceedMaximumValue": [Function],
|
||||||
@@ -3417,7 +3341,6 @@ exports[`SettingsComponent renders 1`] = `
|
|||||||
"firstFieldHasFocus": [Function],
|
"firstFieldHasFocus": [Function],
|
||||||
"formErrors": [Function],
|
"formErrors": [Function],
|
||||||
"formErrorsDetails": [Function],
|
"formErrorsDetails": [Function],
|
||||||
"hasAutoPilotV2FeatureFlag": [Function],
|
|
||||||
"id": "cassandraaddcollectionpane",
|
"id": "cassandraaddcollectionpane",
|
||||||
"isAutoPilotSelected": [Function],
|
"isAutoPilotSelected": [Function],
|
||||||
"isExecuting": [Function],
|
"isExecuting": [Function],
|
||||||
@@ -3438,10 +3361,7 @@ exports[`SettingsComponent renders 1`] = `
|
|||||||
"requestUnitsUsageCostShared": [Function],
|
"requestUnitsUsageCostShared": [Function],
|
||||||
"ruToolTipText": [Function],
|
"ruToolTipText": [Function],
|
||||||
"selectedAutoPilotThroughput": [Function],
|
"selectedAutoPilotThroughput": [Function],
|
||||||
"selectedAutoPilotTier": [Function],
|
|
||||||
"selectedSharedAutoPilotTier": [Function],
|
|
||||||
"sharedAutoPilotThroughput": [Function],
|
"sharedAutoPilotThroughput": [Function],
|
||||||
"sharedAutoPilotTiersList": [Function],
|
|
||||||
"sharedThroughputRangeText": [Function],
|
"sharedThroughputRangeText": [Function],
|
||||||
"sharedThroughputSpendAck": [Function],
|
"sharedThroughputSpendAck": [Function],
|
||||||
"sharedThroughputSpendAckText": [Function],
|
"sharedThroughputSpendAckText": [Function],
|
||||||
@@ -3596,7 +3516,6 @@ exports[`SettingsComponent renders 1`] = `
|
|||||||
"title": [Function],
|
"title": [Function],
|
||||||
"visible": [Function],
|
"visible": [Function],
|
||||||
},
|
},
|
||||||
"hasAutoPilotV2FeatureFlag": [Function],
|
|
||||||
"hasStorageAnalyticsAfecFeature": [Function],
|
"hasStorageAnalyticsAfecFeature": [Function],
|
||||||
"hasWriteAccess": [Function],
|
"hasWriteAccess": [Function],
|
||||||
"isAccountReady": [Function],
|
"isAccountReady": [Function],
|
||||||
@@ -3974,7 +3893,6 @@ exports[`SettingsComponent renders 1`] = `
|
|||||||
"_openShareDialog": [Function],
|
"_openShareDialog": [Function],
|
||||||
"_panes": Array [
|
"_panes": Array [
|
||||||
AddDatabasePane {
|
AddDatabasePane {
|
||||||
"autoPilotTiersList": [Function],
|
|
||||||
"autoPilotUsageCost": [Function],
|
"autoPilotUsageCost": [Function],
|
||||||
"canConfigureThroughput": [Function],
|
"canConfigureThroughput": [Function],
|
||||||
"canExceedMaximumValue": [Function],
|
"canExceedMaximumValue": [Function],
|
||||||
@@ -3990,7 +3908,6 @@ exports[`SettingsComponent renders 1`] = `
|
|||||||
"firstFieldHasFocus": [Function],
|
"firstFieldHasFocus": [Function],
|
||||||
"formErrors": [Function],
|
"formErrors": [Function],
|
||||||
"formErrorsDetails": [Function],
|
"formErrorsDetails": [Function],
|
||||||
"hasAutoPilotV2FeatureFlag": [Function],
|
|
||||||
"id": "adddatabasepane",
|
"id": "adddatabasepane",
|
||||||
"isAutoPilotSelected": [Function],
|
"isAutoPilotSelected": [Function],
|
||||||
"isExecuting": [Function],
|
"isExecuting": [Function],
|
||||||
@@ -4003,7 +3920,6 @@ exports[`SettingsComponent renders 1`] = `
|
|||||||
"onMoreDetailsKeyPress": [Function],
|
"onMoreDetailsKeyPress": [Function],
|
||||||
"requestUnitsUsageCost": [Function],
|
"requestUnitsUsageCost": [Function],
|
||||||
"ruToolTipText": [Function],
|
"ruToolTipText": [Function],
|
||||||
"selectedAutoPilotTier": [Function],
|
|
||||||
"showUpsellMessage": [Function],
|
"showUpsellMessage": [Function],
|
||||||
"throughput": [Function],
|
"throughput": [Function],
|
||||||
"throughputRangeText": [Function],
|
"throughputRangeText": [Function],
|
||||||
@@ -4020,7 +3936,6 @@ exports[`SettingsComponent renders 1`] = `
|
|||||||
AddCollectionPane {
|
AddCollectionPane {
|
||||||
"_isSynapseLinkEnabled": [Function],
|
"_isSynapseLinkEnabled": [Function],
|
||||||
"autoPilotThroughput": [Function],
|
"autoPilotThroughput": [Function],
|
||||||
"autoPilotTiersList": [Function],
|
|
||||||
"autoPilotUsageCost": [Function],
|
"autoPilotUsageCost": [Function],
|
||||||
"canConfigureThroughput": [Function],
|
"canConfigureThroughput": [Function],
|
||||||
"canExceedMaximumValue": [Function],
|
"canExceedMaximumValue": [Function],
|
||||||
@@ -4042,7 +3957,6 @@ exports[`SettingsComponent renders 1`] = `
|
|||||||
"formErrors": [Function],
|
"formErrors": [Function],
|
||||||
"formErrorsDetails": [Function],
|
"formErrorsDetails": [Function],
|
||||||
"formWarnings": [Function],
|
"formWarnings": [Function],
|
||||||
"hasAutoPilotV2FeatureFlag": [Function],
|
|
||||||
"id": "addcollectionpane",
|
"id": "addcollectionpane",
|
||||||
"isAnalyticalStorageOn": [Function],
|
"isAnalyticalStorageOn": [Function],
|
||||||
"isAutoPilotSelected": [Function],
|
"isAutoPilotSelected": [Function],
|
||||||
@@ -4074,10 +3988,7 @@ exports[`SettingsComponent renders 1`] = `
|
|||||||
"ruToolTipText": [Function],
|
"ruToolTipText": [Function],
|
||||||
"rupm": [Function],
|
"rupm": [Function],
|
||||||
"rupmVisible": [Function],
|
"rupmVisible": [Function],
|
||||||
"selectedAutoPilotTier": [Function],
|
|
||||||
"selectedSharedAutoPilotTier": [Function],
|
|
||||||
"sharedAutoPilotThroughput": [Function],
|
"sharedAutoPilotThroughput": [Function],
|
||||||
"sharedAutoPilotTiersList": [Function],
|
|
||||||
"sharedThroughputRangeText": [Function],
|
"sharedThroughputRangeText": [Function],
|
||||||
"shouldCreateMongoWildcardIndex": [Function],
|
"shouldCreateMongoWildcardIndex": [Function],
|
||||||
"shouldUseDatabaseThroughput": [Function],
|
"shouldUseDatabaseThroughput": [Function],
|
||||||
@@ -4288,7 +4199,6 @@ exports[`SettingsComponent renders 1`] = `
|
|||||||
"visible": [Function],
|
"visible": [Function],
|
||||||
},
|
},
|
||||||
CassandraAddCollectionPane {
|
CassandraAddCollectionPane {
|
||||||
"autoPilotTiersList": [Function],
|
|
||||||
"autoPilotUsageCost": [Function],
|
"autoPilotUsageCost": [Function],
|
||||||
"canConfigureThroughput": [Function],
|
"canConfigureThroughput": [Function],
|
||||||
"canExceedMaximumValue": [Function],
|
"canExceedMaximumValue": [Function],
|
||||||
@@ -4300,7 +4210,6 @@ exports[`SettingsComponent renders 1`] = `
|
|||||||
"firstFieldHasFocus": [Function],
|
"firstFieldHasFocus": [Function],
|
||||||
"formErrors": [Function],
|
"formErrors": [Function],
|
||||||
"formErrorsDetails": [Function],
|
"formErrorsDetails": [Function],
|
||||||
"hasAutoPilotV2FeatureFlag": [Function],
|
|
||||||
"id": "cassandraaddcollectionpane",
|
"id": "cassandraaddcollectionpane",
|
||||||
"isAutoPilotSelected": [Function],
|
"isAutoPilotSelected": [Function],
|
||||||
"isExecuting": [Function],
|
"isExecuting": [Function],
|
||||||
@@ -4321,10 +4230,7 @@ exports[`SettingsComponent renders 1`] = `
|
|||||||
"requestUnitsUsageCostShared": [Function],
|
"requestUnitsUsageCostShared": [Function],
|
||||||
"ruToolTipText": [Function],
|
"ruToolTipText": [Function],
|
||||||
"selectedAutoPilotThroughput": [Function],
|
"selectedAutoPilotThroughput": [Function],
|
||||||
"selectedAutoPilotTier": [Function],
|
|
||||||
"selectedSharedAutoPilotTier": [Function],
|
|
||||||
"sharedAutoPilotThroughput": [Function],
|
"sharedAutoPilotThroughput": [Function],
|
||||||
"sharedAutoPilotTiersList": [Function],
|
|
||||||
"sharedThroughputRangeText": [Function],
|
"sharedThroughputRangeText": [Function],
|
||||||
"sharedThroughputSpendAck": [Function],
|
"sharedThroughputSpendAck": [Function],
|
||||||
"sharedThroughputSpendAckText": [Function],
|
"sharedThroughputSpendAckText": [Function],
|
||||||
@@ -4519,7 +4425,6 @@ exports[`SettingsComponent renders 1`] = `
|
|||||||
"addCollectionPane": AddCollectionPane {
|
"addCollectionPane": AddCollectionPane {
|
||||||
"_isSynapseLinkEnabled": [Function],
|
"_isSynapseLinkEnabled": [Function],
|
||||||
"autoPilotThroughput": [Function],
|
"autoPilotThroughput": [Function],
|
||||||
"autoPilotTiersList": [Function],
|
|
||||||
"autoPilotUsageCost": [Function],
|
"autoPilotUsageCost": [Function],
|
||||||
"canConfigureThroughput": [Function],
|
"canConfigureThroughput": [Function],
|
||||||
"canExceedMaximumValue": [Function],
|
"canExceedMaximumValue": [Function],
|
||||||
@@ -4541,7 +4446,6 @@ exports[`SettingsComponent renders 1`] = `
|
|||||||
"formErrors": [Function],
|
"formErrors": [Function],
|
||||||
"formErrorsDetails": [Function],
|
"formErrorsDetails": [Function],
|
||||||
"formWarnings": [Function],
|
"formWarnings": [Function],
|
||||||
"hasAutoPilotV2FeatureFlag": [Function],
|
|
||||||
"id": "addcollectionpane",
|
"id": "addcollectionpane",
|
||||||
"isAnalyticalStorageOn": [Function],
|
"isAnalyticalStorageOn": [Function],
|
||||||
"isAutoPilotSelected": [Function],
|
"isAutoPilotSelected": [Function],
|
||||||
@@ -4573,10 +4477,7 @@ exports[`SettingsComponent renders 1`] = `
|
|||||||
"ruToolTipText": [Function],
|
"ruToolTipText": [Function],
|
||||||
"rupm": [Function],
|
"rupm": [Function],
|
||||||
"rupmVisible": [Function],
|
"rupmVisible": [Function],
|
||||||
"selectedAutoPilotTier": [Function],
|
|
||||||
"selectedSharedAutoPilotTier": [Function],
|
|
||||||
"sharedAutoPilotThroughput": [Function],
|
"sharedAutoPilotThroughput": [Function],
|
||||||
"sharedAutoPilotTiersList": [Function],
|
|
||||||
"sharedThroughputRangeText": [Function],
|
"sharedThroughputRangeText": [Function],
|
||||||
"shouldCreateMongoWildcardIndex": [Function],
|
"shouldCreateMongoWildcardIndex": [Function],
|
||||||
"shouldUseDatabaseThroughput": [Function],
|
"shouldUseDatabaseThroughput": [Function],
|
||||||
@@ -4606,7 +4507,6 @@ exports[`SettingsComponent renders 1`] = `
|
|||||||
},
|
},
|
||||||
"addCollectionText": [Function],
|
"addCollectionText": [Function],
|
||||||
"addDatabasePane": AddDatabasePane {
|
"addDatabasePane": AddDatabasePane {
|
||||||
"autoPilotTiersList": [Function],
|
|
||||||
"autoPilotUsageCost": [Function],
|
"autoPilotUsageCost": [Function],
|
||||||
"canConfigureThroughput": [Function],
|
"canConfigureThroughput": [Function],
|
||||||
"canExceedMaximumValue": [Function],
|
"canExceedMaximumValue": [Function],
|
||||||
@@ -4622,7 +4522,6 @@ exports[`SettingsComponent renders 1`] = `
|
|||||||
"firstFieldHasFocus": [Function],
|
"firstFieldHasFocus": [Function],
|
||||||
"formErrors": [Function],
|
"formErrors": [Function],
|
||||||
"formErrorsDetails": [Function],
|
"formErrorsDetails": [Function],
|
||||||
"hasAutoPilotV2FeatureFlag": [Function],
|
|
||||||
"id": "adddatabasepane",
|
"id": "adddatabasepane",
|
||||||
"isAutoPilotSelected": [Function],
|
"isAutoPilotSelected": [Function],
|
||||||
"isExecuting": [Function],
|
"isExecuting": [Function],
|
||||||
@@ -4635,7 +4534,6 @@ exports[`SettingsComponent renders 1`] = `
|
|||||||
"onMoreDetailsKeyPress": [Function],
|
"onMoreDetailsKeyPress": [Function],
|
||||||
"requestUnitsUsageCost": [Function],
|
"requestUnitsUsageCost": [Function],
|
||||||
"ruToolTipText": [Function],
|
"ruToolTipText": [Function],
|
||||||
"selectedAutoPilotTier": [Function],
|
|
||||||
"showUpsellMessage": [Function],
|
"showUpsellMessage": [Function],
|
||||||
"throughput": [Function],
|
"throughput": [Function],
|
||||||
"throughputRangeText": [Function],
|
"throughputRangeText": [Function],
|
||||||
@@ -4712,7 +4610,6 @@ exports[`SettingsComponent renders 1`] = `
|
|||||||
"canExceedMaximumValue": [Function],
|
"canExceedMaximumValue": [Function],
|
||||||
"canSaveQueries": [Function],
|
"canSaveQueries": [Function],
|
||||||
"cassandraAddCollectionPane": CassandraAddCollectionPane {
|
"cassandraAddCollectionPane": CassandraAddCollectionPane {
|
||||||
"autoPilotTiersList": [Function],
|
|
||||||
"autoPilotUsageCost": [Function],
|
"autoPilotUsageCost": [Function],
|
||||||
"canConfigureThroughput": [Function],
|
"canConfigureThroughput": [Function],
|
||||||
"canExceedMaximumValue": [Function],
|
"canExceedMaximumValue": [Function],
|
||||||
@@ -4724,7 +4621,6 @@ exports[`SettingsComponent renders 1`] = `
|
|||||||
"firstFieldHasFocus": [Function],
|
"firstFieldHasFocus": [Function],
|
||||||
"formErrors": [Function],
|
"formErrors": [Function],
|
||||||
"formErrorsDetails": [Function],
|
"formErrorsDetails": [Function],
|
||||||
"hasAutoPilotV2FeatureFlag": [Function],
|
|
||||||
"id": "cassandraaddcollectionpane",
|
"id": "cassandraaddcollectionpane",
|
||||||
"isAutoPilotSelected": [Function],
|
"isAutoPilotSelected": [Function],
|
||||||
"isExecuting": [Function],
|
"isExecuting": [Function],
|
||||||
@@ -4745,10 +4641,7 @@ exports[`SettingsComponent renders 1`] = `
|
|||||||
"requestUnitsUsageCostShared": [Function],
|
"requestUnitsUsageCostShared": [Function],
|
||||||
"ruToolTipText": [Function],
|
"ruToolTipText": [Function],
|
||||||
"selectedAutoPilotThroughput": [Function],
|
"selectedAutoPilotThroughput": [Function],
|
||||||
"selectedAutoPilotTier": [Function],
|
|
||||||
"selectedSharedAutoPilotTier": [Function],
|
|
||||||
"sharedAutoPilotThroughput": [Function],
|
"sharedAutoPilotThroughput": [Function],
|
||||||
"sharedAutoPilotTiersList": [Function],
|
|
||||||
"sharedThroughputRangeText": [Function],
|
"sharedThroughputRangeText": [Function],
|
||||||
"sharedThroughputSpendAck": [Function],
|
"sharedThroughputSpendAck": [Function],
|
||||||
"sharedThroughputSpendAckText": [Function],
|
"sharedThroughputSpendAckText": [Function],
|
||||||
@@ -4903,7 +4796,6 @@ exports[`SettingsComponent renders 1`] = `
|
|||||||
"title": [Function],
|
"title": [Function],
|
||||||
"visible": [Function],
|
"visible": [Function],
|
||||||
},
|
},
|
||||||
"hasAutoPilotV2FeatureFlag": [Function],
|
|
||||||
"hasStorageAnalyticsAfecFeature": [Function],
|
"hasStorageAnalyticsAfecFeature": [Function],
|
||||||
"hasWriteAccess": [Function],
|
"hasWriteAccess": [Function],
|
||||||
"isAccountReady": [Function],
|
"isAccountReady": [Function],
|
||||||
@@ -5297,6 +5189,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]}
|
||||||
|
|||||||
@@ -1,222 +0,0 @@
|
|||||||
import * as ko from "knockout";
|
|
||||||
import * as ViewModels from "../../../Contracts/ViewModels";
|
|
||||||
import editable from "../../../Common/EditableUtility";
|
|
||||||
import { ThroughputInputComponent, ThroughputInputParams, ThroughputInputViewModel } from "./ThroughputInputComponent";
|
|
||||||
|
|
||||||
const $ = (selector: string) => document.querySelector(selector) as HTMLElement;
|
|
||||||
|
|
||||||
describe.skip("Throughput Input Component", () => {
|
|
||||||
let component: any;
|
|
||||||
let vm: ThroughputInputViewModel;
|
|
||||||
const testId: string = "ThroughputValue";
|
|
||||||
const value: ViewModels.Editable<number> = editable.observable(500);
|
|
||||||
const minimum: ko.Observable<number> = ko.observable(400);
|
|
||||||
const maximum: ko.Observable<number> = ko.observable(2000);
|
|
||||||
|
|
||||||
function buildListOptions(
|
|
||||||
value: ViewModels.Editable<number>,
|
|
||||||
minimum: ko.Observable<number>,
|
|
||||||
maxium: ko.Observable<number>,
|
|
||||||
canExceedMaximumValue?: boolean
|
|
||||||
): ThroughputInputParams {
|
|
||||||
return {
|
|
||||||
testId,
|
|
||||||
value,
|
|
||||||
minimum,
|
|
||||||
maximum,
|
|
||||||
canExceedMaximumValue: ko.computed<boolean>(() => Boolean(canExceedMaximumValue)),
|
|
||||||
costsVisible: ko.observable(false),
|
|
||||||
isFixed: false,
|
|
||||||
label: ko.observable("Label"),
|
|
||||||
requestUnitsUsageCost: ko.observable("requestUnitsUsageCost"),
|
|
||||||
showAsMandatory: false,
|
|
||||||
autoPilotTiersList: null,
|
|
||||||
autoPilotUsageCost: null,
|
|
||||||
isAutoPilotSelected: null,
|
|
||||||
selectedAutoPilotTier: null,
|
|
||||||
throughputAutoPilotRadioId: null,
|
|
||||||
throughputProvisionedRadioId: null,
|
|
||||||
throughputModeRadioName: null
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
function simulateKeyPressSpace(target: HTMLElement): Promise<boolean> {
|
|
||||||
const event = new KeyboardEvent("keydown", {
|
|
||||||
key: "space"
|
|
||||||
});
|
|
||||||
|
|
||||||
const result = target.dispatchEvent(event);
|
|
||||||
|
|
||||||
return new Promise(resolve => {
|
|
||||||
setTimeout(() => {
|
|
||||||
resolve(result);
|
|
||||||
}, 1000);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
beforeEach(() => {
|
|
||||||
component = ThroughputInputComponent;
|
|
||||||
document.body.innerHTML = component.template as any;
|
|
||||||
});
|
|
||||||
|
|
||||||
afterEach(async () => {
|
|
||||||
await ko.cleanNode(document);
|
|
||||||
});
|
|
||||||
|
|
||||||
describe("Rendering", () => {
|
|
||||||
it("should display value text", async () => {
|
|
||||||
vm = new component.viewModel(buildListOptions(value, minimum, maximum));
|
|
||||||
await ko.applyBindings(vm);
|
|
||||||
expect(($("input") as HTMLInputElement).value).toContain(value().toString());
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
describe("Behavior", () => {
|
|
||||||
it("should decrease value", async () => {
|
|
||||||
vm = new component.viewModel(buildListOptions(value, minimum, maximum));
|
|
||||||
await ko.applyBindings(vm);
|
|
||||||
value(450);
|
|
||||||
$(".testhook-decreaseThroughput").click();
|
|
||||||
expect(value()).toBe(400);
|
|
||||||
$(".testhook-decreaseThroughput").click();
|
|
||||||
expect(value()).toBe(400);
|
|
||||||
});
|
|
||||||
|
|
||||||
it("should increase value", async () => {
|
|
||||||
vm = new component.viewModel(buildListOptions(value, minimum, maximum));
|
|
||||||
await ko.applyBindings(vm);
|
|
||||||
value(1950);
|
|
||||||
$(".test-increaseThroughput").click();
|
|
||||||
expect(value()).toBe(2000);
|
|
||||||
$(".test-increaseThroughput").click();
|
|
||||||
expect(value()).toBe(2000);
|
|
||||||
});
|
|
||||||
|
|
||||||
it("should respect lower bound limits", async () => {
|
|
||||||
vm = new component.viewModel(buildListOptions(value, minimum, maximum));
|
|
||||||
await ko.applyBindings(vm);
|
|
||||||
value(minimum());
|
|
||||||
$(".testhook-decreaseThroughput").click();
|
|
||||||
expect(value()).toBe(minimum());
|
|
||||||
});
|
|
||||||
|
|
||||||
it("should respect upper bound limits", async () => {
|
|
||||||
vm = new component.viewModel(buildListOptions(value, minimum, maximum));
|
|
||||||
await ko.applyBindings(vm);
|
|
||||||
value(maximum());
|
|
||||||
$(".test-increaseThroughput").click();
|
|
||||||
expect(value()).toBe(maximum());
|
|
||||||
});
|
|
||||||
|
|
||||||
it("should allow throughput to exceed upper bound limit when canExceedMaximumValue is set", async () => {
|
|
||||||
vm = new component.viewModel(buildListOptions(value, minimum, maximum, true));
|
|
||||||
await ko.applyBindings(vm);
|
|
||||||
value(maximum());
|
|
||||||
$(".test-increaseThroughput").click();
|
|
||||||
expect(value()).toBe(maximum() + 100);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
describe("Accessibility", () => {
|
|
||||||
it.skip("should decrease value with keypress", async () => {
|
|
||||||
vm = new component.viewModel(buildListOptions(value, minimum, maximum));
|
|
||||||
await ko.applyBindings(vm);
|
|
||||||
const target = $(".testhook-decreaseThroughput");
|
|
||||||
|
|
||||||
value(500);
|
|
||||||
expect(value()).toBe(500);
|
|
||||||
|
|
||||||
const result = await simulateKeyPressSpace(target);
|
|
||||||
expect(value()).toBe(400);
|
|
||||||
});
|
|
||||||
|
|
||||||
it.skip("should increase value with keypress", async () => {
|
|
||||||
vm = new component.viewModel(buildListOptions(value, minimum, maximum));
|
|
||||||
await ko.applyBindings(vm);
|
|
||||||
const target = $(".test-increaseThroughput");
|
|
||||||
|
|
||||||
value(400);
|
|
||||||
expect(value()).toBe(400);
|
|
||||||
|
|
||||||
const result = await simulateKeyPressSpace(target);
|
|
||||||
// expect(value()).toBe(500);
|
|
||||||
});
|
|
||||||
|
|
||||||
it("should set the decreaseButtonAriaLabel using the default step value", async () => {
|
|
||||||
vm = new component.viewModel(buildListOptions(value, minimum, maximum, true));
|
|
||||||
await ko.applyBindings(vm);
|
|
||||||
expect(vm.decreaseButtonAriaLabel).toBe("Decrease throughput by 100");
|
|
||||||
});
|
|
||||||
|
|
||||||
it("should set the increaseButtonAriaLabel using the default step value", async () => {
|
|
||||||
vm = new component.viewModel(buildListOptions(value, minimum, maximum, true));
|
|
||||||
await ko.applyBindings(vm);
|
|
||||||
expect(vm.increaseButtonAriaLabel).toBe("Increase throughput by 100");
|
|
||||||
});
|
|
||||||
|
|
||||||
it("should set the increaseButtonAriaLabel using the params step value", async () => {
|
|
||||||
const options = buildListOptions(value, minimum, maximum, true);
|
|
||||||
options.step = 10;
|
|
||||||
vm = new component.viewModel(options);
|
|
||||||
await ko.applyBindings(vm);
|
|
||||||
expect(vm.increaseButtonAriaLabel).toBe("Increase throughput by 10");
|
|
||||||
});
|
|
||||||
|
|
||||||
it("should set the decreaseButtonAriaLabel using the params step value", async () => {
|
|
||||||
const options = buildListOptions(value, minimum, maximum, true);
|
|
||||||
options.step = 10;
|
|
||||||
vm = new component.viewModel(options);
|
|
||||||
await ko.applyBindings(vm);
|
|
||||||
expect(vm.decreaseButtonAriaLabel).toBe("Decrease throughput by 10");
|
|
||||||
});
|
|
||||||
|
|
||||||
it("should set the decreaseButtonAriaLabel using the params step value", async () => {
|
|
||||||
const options = buildListOptions(value, minimum, maximum, true);
|
|
||||||
options.step = 10;
|
|
||||||
vm = new component.viewModel(options);
|
|
||||||
await ko.applyBindings(vm);
|
|
||||||
});
|
|
||||||
|
|
||||||
it("should have aria-label attribute on increase button", async () => {
|
|
||||||
vm = new component.viewModel(buildListOptions(value, minimum, maximum, true));
|
|
||||||
await ko.applyBindings(vm);
|
|
||||||
const ariaLabel = $(".test-increaseThroughput").attributes.getNamedItem("aria-label").value;
|
|
||||||
expect(ariaLabel).toBe("Increase throughput by 100");
|
|
||||||
});
|
|
||||||
|
|
||||||
it("should have aria-label attribute on increase button", async () => {
|
|
||||||
vm = new component.viewModel(buildListOptions(value, minimum, maximum, true));
|
|
||||||
await ko.applyBindings(vm);
|
|
||||||
const ariaLabel = $(".testhook-decreaseThroughput").attributes.getNamedItem("aria-label").value;
|
|
||||||
expect(ariaLabel).toBe("Decrease throughput by 100");
|
|
||||||
});
|
|
||||||
|
|
||||||
it("should have role on increase button", async () => {
|
|
||||||
vm = new component.viewModel(buildListOptions(value, minimum, maximum, true));
|
|
||||||
await ko.applyBindings(vm);
|
|
||||||
const role = $(".test-increaseThroughput").attributes.getNamedItem("role").value;
|
|
||||||
expect(role).toBe("button");
|
|
||||||
});
|
|
||||||
|
|
||||||
it("should have role on decrease button", async () => {
|
|
||||||
vm = new component.viewModel(buildListOptions(value, minimum, maximum, true));
|
|
||||||
await ko.applyBindings(vm);
|
|
||||||
const role = $(".testhook-decreaseThroughput").attributes.getNamedItem("role").value;
|
|
||||||
expect(role).toBe("button");
|
|
||||||
});
|
|
||||||
|
|
||||||
it("should have tabindex 0 on increase button", async () => {
|
|
||||||
vm = new component.viewModel(buildListOptions(value, minimum, maximum, true));
|
|
||||||
await ko.applyBindings(vm);
|
|
||||||
const role = $(".testhook-decreaseThroughput").attributes.getNamedItem("tabindex").value;
|
|
||||||
expect(role).toBe("0");
|
|
||||||
});
|
|
||||||
|
|
||||||
it("should have tabindex 0 on decrease button", async () => {
|
|
||||||
vm = new component.viewModel(buildListOptions(value, minimum, maximum, true));
|
|
||||||
await ko.applyBindings(vm);
|
|
||||||
const role = $(".testhook-decreaseThroughput").attributes.getNamedItem("tabindex").value;
|
|
||||||
expect(role).toBe("0");
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
|
||||||
@@ -1,145 +0,0 @@
|
|||||||
<div>
|
|
||||||
<div>
|
|
||||||
<p class="pkPadding">
|
|
||||||
<!-- ko if: showAsMandatory -->
|
|
||||||
<span class="mandatoryStar">*</span>
|
|
||||||
<!-- /ko -->
|
|
||||||
|
|
||||||
<span class="addCollectionLabel" data-bind="text: label"></span>
|
|
||||||
|
|
||||||
<!-- ko if: infoBubbleText -->
|
|
||||||
<span class="infoTooltip" role="tooltip" tabindex="0">
|
|
||||||
<img class="infoImg" src="../../../../images/info-bubble.svg" alt="More information" />
|
|
||||||
<span data-bind="text: infoBubbleText" class="tooltiptext throughputRuInfo"></span>
|
|
||||||
</span>
|
|
||||||
<!-- /ko -->
|
|
||||||
</p>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<!-- ko if: !isFixed -->
|
|
||||||
<div data-bind="visible: showAutoPilot" class="throughputModeContainer">
|
|
||||||
<input
|
|
||||||
class="throughputModeRadio"
|
|
||||||
aria-label="Autopilot mode"
|
|
||||||
data-test="throughput-autoPilot"
|
|
||||||
type="radio"
|
|
||||||
role="radio"
|
|
||||||
tabindex="0"
|
|
||||||
data-bind="
|
|
||||||
checked: isAutoPilotSelected,
|
|
||||||
checkedValue: true,
|
|
||||||
attr: {
|
|
||||||
id: throughputAutoPilotRadioId,
|
|
||||||
name: throughputModeRadioName,
|
|
||||||
'aria-checked': isAutoPilotSelected() ? 'true' : 'false'
|
|
||||||
}"
|
|
||||||
/>
|
|
||||||
<span
|
|
||||||
class="throughputModeSpace"
|
|
||||||
data-bind="
|
|
||||||
attr: {
|
|
||||||
for: throughputAutoPilotRadioId
|
|
||||||
}"
|
|
||||||
>Autopilot (preview)
|
|
||||||
</span>
|
|
||||||
|
|
||||||
<input
|
|
||||||
class="throughputModeRadio nonFirstRadio"
|
|
||||||
aria-label="Manual mode"
|
|
||||||
type="radio"
|
|
||||||
role="radio"
|
|
||||||
tabindex="0"
|
|
||||||
data-bind="
|
|
||||||
checked: isAutoPilotSelected,
|
|
||||||
checkedValue: false,
|
|
||||||
attr: {
|
|
||||||
id: throughputProvisionedRadioId,
|
|
||||||
name: throughputModeRadioName,
|
|
||||||
'aria-checked': !isAutoPilotSelected() ? 'true' : 'false'
|
|
||||||
}"
|
|
||||||
/>
|
|
||||||
<span
|
|
||||||
class="throughputModeSpace"
|
|
||||||
data-bind="
|
|
||||||
attr: {
|
|
||||||
for: throughputProvisionedRadioId
|
|
||||||
}"
|
|
||||||
>Manual
|
|
||||||
</span>
|
|
||||||
</div>
|
|
||||||
<!-- /ko -->
|
|
||||||
|
|
||||||
<div data-bind="visible: isAutoPilotSelected">
|
|
||||||
<select
|
|
||||||
name="autoPilotTiers"
|
|
||||||
class="collid select-font-size"
|
|
||||||
aria-label="Autopilot Max RU/s"
|
|
||||||
data-bind="
|
|
||||||
options: autoPilotTiersList,
|
|
||||||
optionsText: 'text',
|
|
||||||
optionsValue: 'value',
|
|
||||||
value: selectedAutoPilotTier,
|
|
||||||
optionsCaption: 'Choose Max RU/s'"
|
|
||||||
>
|
|
||||||
</select>
|
|
||||||
<p>
|
|
||||||
<span
|
|
||||||
data-bind="
|
|
||||||
html: autoPilotUsageCost,
|
|
||||||
visible: selectedAutoPilotTier"
|
|
||||||
>
|
|
||||||
</span>
|
|
||||||
</p>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div data-bind="visible: !isAutoPilotSelected()">
|
|
||||||
<div data-bind="setTemplateReady: true">
|
|
||||||
<p class="addContainerThroughputInput">
|
|
||||||
<input
|
|
||||||
type="number"
|
|
||||||
required
|
|
||||||
data-bind="
|
|
||||||
textInput: value,
|
|
||||||
css: {
|
|
||||||
dirty: value.editableIsDirty
|
|
||||||
},
|
|
||||||
enable: isEnabled,
|
|
||||||
attr:{
|
|
||||||
'data-test': testId,
|
|
||||||
'class': cssClass,
|
|
||||||
step: step,
|
|
||||||
min: minimum,
|
|
||||||
max: canExceedMaximumValue() ? null : maximum,
|
|
||||||
'aria-label': ariaLabel
|
|
||||||
}"
|
|
||||||
/>
|
|
||||||
</p>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<p data-bind="visible: costsVisible">
|
|
||||||
<span data-bind="html: requestUnitsUsageCost"></span>
|
|
||||||
</p>
|
|
||||||
|
|
||||||
<!-- ko if: spendAckVisible -->
|
|
||||||
<p class="pkPadding">
|
|
||||||
<input
|
|
||||||
type="checkbox"
|
|
||||||
aria-label="acknowledge spend throughput"
|
|
||||||
data-bind="
|
|
||||||
attr: {
|
|
||||||
title: spendAckText,
|
|
||||||
id: spendAckId
|
|
||||||
},
|
|
||||||
checked: spendAckChecked"
|
|
||||||
/>
|
|
||||||
<span data-bind="text: spendAckText, attr: { for: spendAckId }"></span>
|
|
||||||
</p>
|
|
||||||
<!-- /ko -->
|
|
||||||
|
|
||||||
<!-- ko if: isFixed -->
|
|
||||||
<p>
|
|
||||||
Choose unlimited storage capacity for more than 10,000 RU/s.
|
|
||||||
</p>
|
|
||||||
<!-- /ko -->
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
@@ -1,261 +0,0 @@
|
|||||||
import * as DataModels from "../../../Contracts/DataModels";
|
|
||||||
import * as ko from "knockout";
|
|
||||||
import * as ViewModels from "../../../Contracts/ViewModels";
|
|
||||||
import { KeyCodes } from "../../../Common/Constants";
|
|
||||||
import { WaitsForTemplateViewModel } from "../../WaitsForTemplateViewModel";
|
|
||||||
import ThroughputInputComponentTemplate from "./ThroughputInputComponent.html";
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Throughput Input:
|
|
||||||
*
|
|
||||||
* Creates a set of controls to input, sanitize and increase/decrease throughput
|
|
||||||
*
|
|
||||||
* How to use in your markup:
|
|
||||||
* <throughput-input params="{ value: anObservableToHoldTheValue, minimum: anObservableWithMinimum, maximum: anObservableWithMaximum }">
|
|
||||||
* </throughput-input>
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Parameters for this component
|
|
||||||
*/
|
|
||||||
export interface ThroughputInputParams {
|
|
||||||
/**
|
|
||||||
* Callback triggered when the template is bound to the component (for testing purposes)
|
|
||||||
*/
|
|
||||||
onTemplateReady?: () => void;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Observable to bind the Throughput value to
|
|
||||||
*/
|
|
||||||
value: ViewModels.Editable<number>;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Text to use as id for testing
|
|
||||||
*/
|
|
||||||
testId: string;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Text to use as aria-label
|
|
||||||
*/
|
|
||||||
ariaLabel?: ko.Observable<string>;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Minimum value in the range
|
|
||||||
*/
|
|
||||||
minimum: ko.Observable<number>;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Maximum value in the range
|
|
||||||
*/
|
|
||||||
maximum: ko.Observable<number>;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Step value for increase/decrease
|
|
||||||
*/
|
|
||||||
step?: number;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Observable to bind the Throughput enabled status
|
|
||||||
*/
|
|
||||||
isEnabled?: ko.Observable<boolean>;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Should show pricing controls
|
|
||||||
*/
|
|
||||||
costsVisible: ko.Observable<boolean>;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* RU price
|
|
||||||
*/
|
|
||||||
requestUnitsUsageCost: ko.Subscribable<string>; // Our code assigns to ko.Computed, but unit test assigns to ko.Observable
|
|
||||||
|
|
||||||
/**
|
|
||||||
* State of the spending acknowledge checkbox
|
|
||||||
*/
|
|
||||||
spendAckChecked?: ko.Observable<boolean>;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* id of the spending acknowledge checkbox
|
|
||||||
*/
|
|
||||||
spendAckId?: ko.Observable<string>;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* spending acknowledge text
|
|
||||||
*/
|
|
||||||
spendAckText?: ko.Observable<string>;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Show spending acknowledge controls
|
|
||||||
*/
|
|
||||||
spendAckVisible?: ko.Observable<boolean>;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Display * to the left of the label
|
|
||||||
*/
|
|
||||||
showAsMandatory: boolean;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* If true, it will display a text to prompt users to use unlimited collections to go beyond max for fixed
|
|
||||||
*/
|
|
||||||
isFixed: boolean;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Label of the provisioned throughut control
|
|
||||||
*/
|
|
||||||
label: ko.Observable<string>;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Text of the info bubble for provisioned throughut control
|
|
||||||
*/
|
|
||||||
infoBubbleText?: ko.Observable<string>;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Computed value that decides if value can exceed maximum allowable value
|
|
||||||
*/
|
|
||||||
canExceedMaximumValue?: ko.Computed<boolean>;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* CSS classes to apply on input element
|
|
||||||
*/
|
|
||||||
cssClass?: string;
|
|
||||||
|
|
||||||
isAutoPilotSelected: ko.Observable<boolean>;
|
|
||||||
throughputAutoPilotRadioId: string;
|
|
||||||
throughputProvisionedRadioId: string;
|
|
||||||
throughputModeRadioName: string;
|
|
||||||
autoPilotTiersList: ko.ObservableArray<ViewModels.DropdownOption<DataModels.AutopilotTier>>;
|
|
||||||
selectedAutoPilotTier: ko.Observable<DataModels.AutopilotTier>;
|
|
||||||
autoPilotUsageCost: ko.Computed<string>;
|
|
||||||
showAutoPilot?: ko.Observable<boolean>;
|
|
||||||
}
|
|
||||||
|
|
||||||
export class ThroughputInputViewModel extends WaitsForTemplateViewModel {
|
|
||||||
public ariaLabel: ko.Observable<string>;
|
|
||||||
public canExceedMaximumValue: ko.Computed<boolean>;
|
|
||||||
public step: number;
|
|
||||||
public testId: string;
|
|
||||||
public value: ViewModels.Editable<number>;
|
|
||||||
public minimum: ko.Observable<number>;
|
|
||||||
public maximum: ko.Observable<number>;
|
|
||||||
public isEnabled: ko.Observable<boolean>;
|
|
||||||
public cssClass: string;
|
|
||||||
public decreaseButtonAriaLabel: string;
|
|
||||||
public increaseButtonAriaLabel: string;
|
|
||||||
public costsVisible: ko.Observable<boolean>;
|
|
||||||
public requestUnitsUsageCost: ko.Subscribable<string>;
|
|
||||||
public spendAckChecked: ko.Observable<boolean>;
|
|
||||||
public spendAckId: ko.Observable<string>;
|
|
||||||
public spendAckText: ko.Observable<string>;
|
|
||||||
public spendAckVisible: ko.Observable<boolean>;
|
|
||||||
public showAsMandatory: boolean;
|
|
||||||
public infoBubbleText: string | ko.Observable<string>;
|
|
||||||
public label: ko.Observable<string>;
|
|
||||||
public isFixed: boolean;
|
|
||||||
public showAutoPilot: ko.Observable<boolean>;
|
|
||||||
public isAutoPilotSelected: ko.Observable<boolean>;
|
|
||||||
public throughputAutoPilotRadioId: string;
|
|
||||||
public throughputProvisionedRadioId: string;
|
|
||||||
public throughputModeRadioName: string;
|
|
||||||
public autoPilotTiersList: ko.ObservableArray<ViewModels.DropdownOption<DataModels.AutopilotTier>>;
|
|
||||||
public selectedAutoPilotTier: ko.Observable<DataModels.AutopilotTier>;
|
|
||||||
public autoPilotUsageCost: ko.Computed<string>;
|
|
||||||
|
|
||||||
public constructor(options: ThroughputInputParams) {
|
|
||||||
super();
|
|
||||||
super.onTemplateReady((isTemplateReady: boolean) => {
|
|
||||||
if (isTemplateReady && options.onTemplateReady) {
|
|
||||||
options.onTemplateReady();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
const params: ThroughputInputParams = options;
|
|
||||||
this.testId = params.testId || "ThroughputValue";
|
|
||||||
this.ariaLabel = ko.observable((params.ariaLabel && params.ariaLabel()) || "");
|
|
||||||
this.canExceedMaximumValue = params.canExceedMaximumValue || ko.computed(() => false);
|
|
||||||
this.step = params.step || ThroughputInputViewModel._defaultStep;
|
|
||||||
this.isEnabled = params.isEnabled || ko.observable(true);
|
|
||||||
this.cssClass = params.cssClass || "textfontclr collid";
|
|
||||||
this.minimum = params.minimum;
|
|
||||||
this.maximum = params.maximum;
|
|
||||||
this.value = params.value;
|
|
||||||
this.decreaseButtonAriaLabel = "Decrease throughput by " + this.step.toString();
|
|
||||||
this.increaseButtonAriaLabel = "Increase throughput by " + this.step.toString();
|
|
||||||
this.costsVisible = options.costsVisible;
|
|
||||||
this.requestUnitsUsageCost = options.requestUnitsUsageCost;
|
|
||||||
this.spendAckChecked = options.spendAckChecked || ko.observable<boolean>(false);
|
|
||||||
this.spendAckId = options.spendAckId || ko.observable<string>();
|
|
||||||
this.spendAckText = options.spendAckText || ko.observable<string>();
|
|
||||||
this.spendAckVisible = options.spendAckVisible || ko.observable<boolean>(false);
|
|
||||||
this.showAsMandatory = !!options.showAsMandatory;
|
|
||||||
this.isFixed = !!options.isFixed;
|
|
||||||
this.infoBubbleText = options.infoBubbleText || ko.observable<string>();
|
|
||||||
this.label = options.label || ko.observable<string>();
|
|
||||||
this.showAutoPilot = options.showAutoPilot !== undefined ? options.showAutoPilot : ko.observable<boolean>(true);
|
|
||||||
this.isAutoPilotSelected = options.isAutoPilotSelected || ko.observable<boolean>(false);
|
|
||||||
this.throughputAutoPilotRadioId = options.throughputAutoPilotRadioId;
|
|
||||||
this.throughputProvisionedRadioId = options.throughputProvisionedRadioId;
|
|
||||||
this.throughputModeRadioName = options.throughputModeRadioName;
|
|
||||||
this.autoPilotTiersList = options.autoPilotTiersList;
|
|
||||||
this.selectedAutoPilotTier = options.selectedAutoPilotTier;
|
|
||||||
this.autoPilotUsageCost = options.autoPilotUsageCost;
|
|
||||||
}
|
|
||||||
|
|
||||||
public decreaseThroughput() {
|
|
||||||
let offerThroughput: number = this._getSanitizedValue();
|
|
||||||
|
|
||||||
if (offerThroughput > this.minimum()) {
|
|
||||||
offerThroughput -= this.step;
|
|
||||||
if (offerThroughput < this.minimum()) {
|
|
||||||
offerThroughput = this.minimum();
|
|
||||||
}
|
|
||||||
|
|
||||||
this.value(offerThroughput);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public increaseThroughput() {
|
|
||||||
let offerThroughput: number = this._getSanitizedValue();
|
|
||||||
|
|
||||||
if (offerThroughput < this.maximum() || this.canExceedMaximumValue()) {
|
|
||||||
offerThroughput += this.step;
|
|
||||||
if (offerThroughput > this.maximum() && !this.canExceedMaximumValue()) {
|
|
||||||
offerThroughput = this.maximum();
|
|
||||||
}
|
|
||||||
|
|
||||||
this.value(offerThroughput);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public onIncreaseKeyDown = (source: any, event: KeyboardEvent): boolean => {
|
|
||||||
if (event.keyCode === KeyCodes.Enter || event.keyCode === KeyCodes.Space) {
|
|
||||||
this.increaseThroughput();
|
|
||||||
event.stopPropagation();
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
|
||||||
};
|
|
||||||
|
|
||||||
public onDecreaseKeyDown = (source: any, event: KeyboardEvent): boolean => {
|
|
||||||
if (event.keyCode === KeyCodes.Enter || event.keyCode === KeyCodes.Space) {
|
|
||||||
this.decreaseThroughput();
|
|
||||||
event.stopPropagation();
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
|
||||||
};
|
|
||||||
|
|
||||||
private _getSanitizedValue(): number {
|
|
||||||
const throughput = this.value();
|
|
||||||
return isNaN(throughput) ? 0 : Number(throughput);
|
|
||||||
}
|
|
||||||
|
|
||||||
private static _defaultStep: number = 100;
|
|
||||||
}
|
|
||||||
|
|
||||||
export const ThroughputInputComponent = {
|
|
||||||
viewModel: ThroughputInputViewModel,
|
|
||||||
template: ThroughputInputComponentTemplate
|
|
||||||
};
|
|
||||||
@@ -19,7 +19,6 @@ describe("ContainerSampleGenerator", () => {
|
|||||||
explorerStub.isPreferredApiTable = ko.computed<boolean>(() => false);
|
explorerStub.isPreferredApiTable = ko.computed<boolean>(() => false);
|
||||||
explorerStub.isPreferredApiCassandra = ko.computed<boolean>(() => false);
|
explorerStub.isPreferredApiCassandra = ko.computed<boolean>(() => false);
|
||||||
explorerStub.canExceedMaximumValue = ko.computed<boolean>(() => false);
|
explorerStub.canExceedMaximumValue = ko.computed<boolean>(() => false);
|
||||||
explorerStub.hasAutoPilotV2FeatureFlag = ko.computed<boolean>(() => true);
|
|
||||||
explorerStub.findDatabaseWithId = () => database;
|
explorerStub.findDatabaseWithId = () => database;
|
||||||
explorerStub.refreshAllDatabases = () => Q.resolve();
|
explorerStub.refreshAllDatabases = () => Q.resolve();
|
||||||
return explorerStub;
|
return explorerStub;
|
||||||
|
|||||||
@@ -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
|
||||||
@@ -212,7 +213,6 @@ export default class Explorer {
|
|||||||
public isHostedDataExplorerEnabled: ko.Computed<boolean>;
|
public isHostedDataExplorerEnabled: ko.Computed<boolean>;
|
||||||
public isRightPanelV2Enabled: ko.Computed<boolean>;
|
public isRightPanelV2Enabled: ko.Computed<boolean>;
|
||||||
public canExceedMaximumValue: ko.Computed<boolean>;
|
public canExceedMaximumValue: ko.Computed<boolean>;
|
||||||
public hasAutoPilotV2FeatureFlag: ko.Computed<boolean>;
|
|
||||||
|
|
||||||
public shouldShowShareDialogContents: ko.Observable<boolean>;
|
public shouldShowShareDialogContents: ko.Observable<boolean>;
|
||||||
public shareAccessData: ko.Observable<AdHocAccessData>;
|
public shareAccessData: ko.Observable<AdHocAccessData>;
|
||||||
@@ -422,13 +422,6 @@ export default class Explorer {
|
|||||||
this.isFeatureEnabled(Constants.Features.canExceedMaximumValue)
|
this.isFeatureEnabled(Constants.Features.canExceedMaximumValue)
|
||||||
);
|
);
|
||||||
|
|
||||||
this.hasAutoPilotV2FeatureFlag = ko.computed(() => {
|
|
||||||
if (this.isFeatureEnabled(Constants.Features.enableAutoPilotV2)) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
});
|
|
||||||
|
|
||||||
this.isNotificationConsoleExpanded = ko.observable<boolean>(false);
|
this.isNotificationConsoleExpanded = ko.observable<boolean>(false);
|
||||||
|
|
||||||
this.databases = ko.observableArray<ViewModels.Database>();
|
this.databases = ko.observableArray<ViewModels.Database>();
|
||||||
@@ -1047,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 {
|
||||||
@@ -1120,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,
|
||||||
@@ -1149,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);
|
||||||
}
|
}
|
||||||
@@ -1174,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);
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
@@ -1448,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}`
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
@@ -1479,7 +1477,7 @@ export default class Explorer {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
reason => {
|
error => {
|
||||||
if (resourceTreeStartKey != null) {
|
if (resourceTreeStartKey != null) {
|
||||||
TelemetryProcessor.traceFailure(
|
TelemetryProcessor.traceFailure(
|
||||||
Action.LoadResourceTree,
|
Action.LoadResourceTree,
|
||||||
@@ -1487,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
|
||||||
);
|
);
|
||||||
@@ -1536,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);
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
@@ -1554,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);
|
||||||
@@ -1562,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([]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1598,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 {
|
||||||
@@ -1624,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 = {
|
||||||
@@ -1651,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;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1677,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();
|
||||||
}
|
}
|
||||||
@@ -1693,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);
|
||||||
@@ -2060,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
|
||||||
);
|
);
|
||||||
@@ -2226,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);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -2408,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);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -2457,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);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -2479,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);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -2490,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);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -2522,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();
|
||||||
@@ -2572,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);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -2601,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);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
@@ -2626,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;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
@@ -2645,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);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -2695,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);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -2729,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
|
||||||
);
|
);
|
||||||
@@ -2781,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));
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -2968,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);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -20,7 +20,7 @@ describe("CommandBarComponentButtonFactory tests", () => {
|
|||||||
mockExplorer.isSparkEnabled = ko.observable(true);
|
mockExplorer.isSparkEnabled = ko.observable(true);
|
||||||
mockExplorer.isSynapseLinkUpdating = ko.observable(false);
|
mockExplorer.isSynapseLinkUpdating = ko.observable(false);
|
||||||
mockExplorer.isGalleryPublishEnabled = ko.computed<boolean>(() => false);
|
mockExplorer.isGalleryPublishEnabled = ko.computed<boolean>(() => false);
|
||||||
mockExplorer.hasAutoPilotV2FeatureFlag = ko.computed<boolean>(() => true);
|
|
||||||
mockExplorer.isDatabaseNodeOrNoneSelected = () => true;
|
mockExplorer.isDatabaseNodeOrNoneSelected = () => true;
|
||||||
mockExplorer.isNotebookEnabled = ko.observable(false);
|
mockExplorer.isNotebookEnabled = ko.observable(false);
|
||||||
mockExplorer.isNotebooksEnabledForAccount = ko.observable(false);
|
mockExplorer.isNotebooksEnabledForAccount = ko.observable(false);
|
||||||
@@ -62,7 +62,7 @@ describe("CommandBarComponentButtonFactory tests", () => {
|
|||||||
mockExplorer.isSparkEnabled = ko.observable(true);
|
mockExplorer.isSparkEnabled = ko.observable(true);
|
||||||
mockExplorer.isSynapseLinkUpdating = ko.observable(false);
|
mockExplorer.isSynapseLinkUpdating = ko.observable(false);
|
||||||
mockExplorer.isGalleryPublishEnabled = ko.computed<boolean>(() => false);
|
mockExplorer.isGalleryPublishEnabled = ko.computed<boolean>(() => false);
|
||||||
mockExplorer.hasAutoPilotV2FeatureFlag = ko.computed<boolean>(() => true);
|
|
||||||
mockExplorer.isDatabaseNodeOrNoneSelected = () => true;
|
mockExplorer.isDatabaseNodeOrNoneSelected = () => true;
|
||||||
mockExplorer.isServerlessEnabled = ko.computed<boolean>(() => false);
|
mockExplorer.isServerlessEnabled = ko.computed<boolean>(() => false);
|
||||||
});
|
});
|
||||||
@@ -126,7 +126,7 @@ describe("CommandBarComponentButtonFactory tests", () => {
|
|||||||
mockExplorer.isSparkEnabled = ko.observable(true);
|
mockExplorer.isSparkEnabled = ko.observable(true);
|
||||||
mockExplorer.isSynapseLinkUpdating = ko.observable(false);
|
mockExplorer.isSynapseLinkUpdating = ko.observable(false);
|
||||||
mockExplorer.isGalleryPublishEnabled = ko.computed<boolean>(() => false);
|
mockExplorer.isGalleryPublishEnabled = ko.computed<boolean>(() => false);
|
||||||
mockExplorer.hasAutoPilotV2FeatureFlag = ko.computed<boolean>(() => true);
|
|
||||||
mockExplorer.isDatabaseNodeOrNoneSelected = () => true;
|
mockExplorer.isDatabaseNodeOrNoneSelected = () => true;
|
||||||
mockExplorer.isServerlessEnabled = ko.computed<boolean>(() => false);
|
mockExplorer.isServerlessEnabled = ko.computed<boolean>(() => false);
|
||||||
});
|
});
|
||||||
@@ -208,7 +208,7 @@ describe("CommandBarComponentButtonFactory tests", () => {
|
|||||||
mockExplorer.isSynapseLinkUpdating = ko.observable(false);
|
mockExplorer.isSynapseLinkUpdating = ko.observable(false);
|
||||||
mockExplorer.isSparkEnabled = ko.observable(true);
|
mockExplorer.isSparkEnabled = ko.observable(true);
|
||||||
mockExplorer.isGalleryPublishEnabled = ko.computed<boolean>(() => false);
|
mockExplorer.isGalleryPublishEnabled = ko.computed<boolean>(() => false);
|
||||||
mockExplorer.hasAutoPilotV2FeatureFlag = ko.computed<boolean>(() => true);
|
|
||||||
mockExplorer.isDatabaseNodeOrNoneSelected = () => true;
|
mockExplorer.isDatabaseNodeOrNoneSelected = () => true;
|
||||||
mockExplorer.isServerlessEnabled = ko.computed<boolean>(() => false);
|
mockExplorer.isServerlessEnabled = ko.computed<boolean>(() => false);
|
||||||
});
|
});
|
||||||
@@ -289,7 +289,7 @@ describe("CommandBarComponentButtonFactory tests", () => {
|
|||||||
mockExplorer.isPreferredApiTable = ko.computed(() => true);
|
mockExplorer.isPreferredApiTable = ko.computed(() => true);
|
||||||
mockExplorer.isPreferredApiMongoDB = ko.computed<boolean>(() => false);
|
mockExplorer.isPreferredApiMongoDB = ko.computed<boolean>(() => false);
|
||||||
mockExplorer.isPreferredApiCassandra = ko.computed<boolean>(() => false);
|
mockExplorer.isPreferredApiCassandra = ko.computed<boolean>(() => false);
|
||||||
mockExplorer.hasAutoPilotV2FeatureFlag = ko.computed<boolean>(() => true);
|
|
||||||
mockExplorer.isSynapseLinkUpdating = ko.observable(false);
|
mockExplorer.isSynapseLinkUpdating = ko.observable(false);
|
||||||
mockExplorer.isSparkEnabled = ko.observable(true);
|
mockExplorer.isSparkEnabled = ko.observable(true);
|
||||||
mockExplorer.isDatabaseNodeOrNoneSelected = () => true;
|
mockExplorer.isDatabaseNodeOrNoneSelected = () => true;
|
||||||
@@ -348,7 +348,7 @@ describe("CommandBarComponentButtonFactory tests", () => {
|
|||||||
mockExplorer.isAuthWithResourceToken = ko.observable(true);
|
mockExplorer.isAuthWithResourceToken = ko.observable(true);
|
||||||
mockExplorer.isPreferredApiDocumentDB = ko.computed(() => true);
|
mockExplorer.isPreferredApiDocumentDB = ko.computed(() => true);
|
||||||
mockExplorer.isDatabaseNodeOrNoneSelected = () => true;
|
mockExplorer.isDatabaseNodeOrNoneSelected = () => true;
|
||||||
mockExplorer.hasAutoPilotV2FeatureFlag = ko.computed<boolean>(() => true);
|
|
||||||
mockExplorer.isResourceTokenCollectionNodeSelected = ko.computed(() => true);
|
mockExplorer.isResourceTokenCollectionNodeSelected = ko.computed(() => true);
|
||||||
mockExplorer.isServerlessEnabled = ko.computed<boolean>(() => false);
|
mockExplorer.isServerlessEnabled = ko.computed<boolean>(() => false);
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -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();
|
||||||
|
|||||||
@@ -127,7 +127,6 @@
|
|||||||
be shared across all containers within the database.</span>
|
be shared across all containers within the database.</span>
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
<!-- ko if: hasAutoPilotV2FeatureFlag && !hasAutoPilotV2FeatureFlag() -->
|
|
||||||
<div data-bind="visible: databaseCreateNewShared() && databaseCreateNew()">
|
<div data-bind="visible: databaseCreateNewShared() && databaseCreateNew()">
|
||||||
<!-- 1 -->
|
<!-- 1 -->
|
||||||
<throughput-input-autopilot-v3 params="{
|
<throughput-input-autopilot-v3 params="{
|
||||||
@@ -158,38 +157,6 @@
|
|||||||
</throughput-input-autopilot-v3>
|
</throughput-input-autopilot-v3>
|
||||||
</div>
|
</div>
|
||||||
<!-- /ko -->
|
<!-- /ko -->
|
||||||
<!-- ko if: hasAutoPilotV2FeatureFlag && hasAutoPilotV2FeatureFlag() -->
|
|
||||||
<div data-bind="visible: databaseCreateNewShared() && databaseCreateNew()">
|
|
||||||
<!-- 1 -->
|
|
||||||
<throughput-input params="{
|
|
||||||
testId: 'databaseThroughputValue',
|
|
||||||
value: throughputDatabase,
|
|
||||||
minimum: minThroughputRU,
|
|
||||||
maximum: maxThroughputRU,
|
|
||||||
isEnabled: databaseCreateNewShared() && databaseCreateNew(),
|
|
||||||
label: sharedThroughputRangeText,
|
|
||||||
ariaLabel: sharedThroughputRangeText,
|
|
||||||
costsVisible: costsVisible,
|
|
||||||
requestUnitsUsageCost: requestUnitsUsageCost,
|
|
||||||
spendAckChecked: throughputSpendAck,
|
|
||||||
spendAckId: 'throughputSpendAck',
|
|
||||||
spendAckText: throughputSpendAckText,
|
|
||||||
spendAckVisible: throughputSpendAckVisible,
|
|
||||||
showAsMandatory: true,
|
|
||||||
infoBubbleText: ruToolTipText,
|
|
||||||
throughputAutoPilotRadioId: 'newContainer-databaseThroughput-autoPilotRadio',
|
|
||||||
throughputProvisionedRadioId: 'newContainer-databaseThroughput-manualRadio',
|
|
||||||
throughputModeRadioName: 'sharedThroughputModeRadio',
|
|
||||||
isAutoPilotSelected: isSharedAutoPilotSelected,
|
|
||||||
autoPilotUsageCost: autoPilotUsageCost,
|
|
||||||
canExceedMaximumValue: canExceedMaximumValue,
|
|
||||||
autoPilotTiersList: sharedAutoPilotTiersList,
|
|
||||||
selectedAutoPilotTier: selectedSharedAutoPilotTier
|
|
||||||
}">
|
|
||||||
</throughput-input>
|
|
||||||
</div>
|
|
||||||
<!-- /ko -->
|
|
||||||
<!-- /ko -->
|
|
||||||
<!-- Database provisioned throughput - End -->
|
<!-- Database provisioned throughput - End -->
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
@@ -211,19 +178,6 @@
|
|||||||
data-bind="value: collectionId, attr: { 'aria-label': collectionIdTitle }">
|
data-bind="value: collectionId, attr: { 'aria-label': collectionIdTitle }">
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- <p class="seconddivpadding" data-bind="visible:(container.isPreferredApiDocumentDB() || container.isPreferredApiGraph()) && !databaseHasSharedOffer()">
|
|
||||||
Where did 'fixed' containers go?
|
|
||||||
<span class="infoTooltip" role="tooltip" tabindex="0">
|
|
||||||
<img class="infoImg" src="/info-bubble.svg" alt="More information">
|
|
||||||
<span class="tooltiptext noFixedCollectionsTooltipWidth">
|
|
||||||
We lowered the minimum throughput for partitioned containers to 400 RU/s, removing the only drawback partitioned containers had. <br/><br/>
|
|
||||||
We are planning to deprecate ability to create non-partitioned containers, as they do not allow you to scale elastically.
|
|
||||||
If for some reason you still need a container without partition key, you can use our SDKs to create one. <br/><br/>
|
|
||||||
Please <a href="https://aka.ms/cosmosdbfeedback?subject=Cosmos%20DB%20Hosted%20Data%20Explorer%20Feedback">contact us</a> if you have questions or concerns.
|
|
||||||
</span>
|
|
||||||
</span>
|
|
||||||
</p> -->
|
|
||||||
|
|
||||||
<!-- Indexing For Shared Throughput - start -->
|
<!-- Indexing For Shared Throughput - start -->
|
||||||
<div class="seconddivpadding"
|
<div class="seconddivpadding"
|
||||||
data-bind="visible: showIndexingOptionsForSharedThroughput() && !container.isPreferredApiMongoDB()">
|
data-bind="visible: showIndexingOptionsForSharedThroughput() && !container.isPreferredApiMongoDB()">
|
||||||
@@ -287,62 +241,6 @@
|
|||||||
</div>
|
</div>
|
||||||
<!-- Unlimited option button - End -->
|
<!-- Unlimited option button - End -->
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- Fixed Button Content - Start -->
|
|
||||||
<div class="tabcontent" data-bind="visible: isFixedStorageSelected() && !databaseHasSharedOffer()">
|
|
||||||
<!-- 2 -->
|
|
||||||
<!-- note: this is used when creating a fixed collection without shared throughput. only manual throughput is available. -->
|
|
||||||
<throughput-input params="{
|
|
||||||
testId: 'fixedThroughputValue',
|
|
||||||
value: throughputSinglePartition,
|
|
||||||
minimum: minThroughputRU,
|
|
||||||
maximum: maxThroughputRU,
|
|
||||||
isEnabled: isFixedStorageSelected() && !databaseHasSharedOffer(),
|
|
||||||
label: throughputRangeText,
|
|
||||||
ariaLabel: throughputRangeText,
|
|
||||||
costsVisible: costsVisible,
|
|
||||||
requestUnitsUsageCost: requestUnitsUsageCost
|
|
||||||
showAsMandatory: true,
|
|
||||||
isFixed: true,
|
|
||||||
infoBubbleText: ruToolTipText,
|
|
||||||
canExceedMaximumValue: canExceedMaximumValue
|
|
||||||
}">
|
|
||||||
</throughput-input>
|
|
||||||
<div data-bind="visible: rupmVisible">
|
|
||||||
<div class="tabs">
|
|
||||||
<p class="pkPadding">
|
|
||||||
<span class="mandatoryStar">*</span>
|
|
||||||
<span class="addCollectionLabel">RU/m</span>
|
|
||||||
<span class="infoTooltip" role="tooltip" tabindex="0">
|
|
||||||
<img class="infoImg" src="/info-bubble.svg" alt="More information">
|
|
||||||
<span class="tooltiptext throughputRuInfo">
|
|
||||||
For each 100 Request Units per second (RU/s) provisioned, 1,000 Request Units
|
|
||||||
per
|
|
||||||
minute
|
|
||||||
(RU/m) can be provisioned. E.g.: for a container with 5,000 RU/s provisioned
|
|
||||||
with
|
|
||||||
RU/m
|
|
||||||
enabled, the RU/m budget will be 50,000 RU/m.
|
|
||||||
</span>
|
|
||||||
</span>
|
|
||||||
</p>
|
|
||||||
<div tabindex="0" data-bind="event: { keydown: onRupmOptionsKeyDown }" aria-label="RU/m">
|
|
||||||
<div class="tab">
|
|
||||||
<input type="radio" id="rupmOn" name="rupmcoll" value="on" class="radio"
|
|
||||||
data-bind="checked: rupm">
|
|
||||||
<label for="rupmOn">ON</label>
|
|
||||||
</div>
|
|
||||||
<div class="tab">
|
|
||||||
<input type="radio" id="rupmOff" name="rupmcoll" value="off" class="radio"
|
|
||||||
data-bind="checked: rupm">
|
|
||||||
<label for="rupmOff">OFF</label>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<!-- Fixed Button Content - End -->
|
|
||||||
|
|
||||||
<!-- Unlimited Button Content - Start -->
|
<!-- Unlimited Button Content - Start -->
|
||||||
<div class="tabcontent" data-bind="visible: isUnlimitedStorageSelected() || databaseHasSharedOffer()">
|
<div class="tabcontent" data-bind="visible: isUnlimitedStorageSelected() || databaseHasSharedOffer()">
|
||||||
<div data-bind="visible: rupmVisible">
|
<div data-bind="visible: rupmVisible">
|
||||||
@@ -390,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,
|
||||||
@@ -442,7 +340,6 @@
|
|||||||
<!-- Provision collection throughput checkbox - end -->
|
<!-- Provision collection throughput checkbox - end -->
|
||||||
|
|
||||||
<!-- Provision collection throughput spinner - start -->
|
<!-- Provision collection throughput spinner - start -->
|
||||||
<!-- ko if: hasAutoPilotV2FeatureFlag && !hasAutoPilotV2FeatureFlag() -->
|
|
||||||
<div data-bind="visible: displayCollectionThroughput" data-test="addCollection-displayCollectionThroughput">
|
<div data-bind="visible: displayCollectionThroughput" data-test="addCollection-displayCollectionThroughput">
|
||||||
<!-- 3 -->
|
<!-- 3 -->
|
||||||
<throughput-input-autopilot-v3 params="{
|
<throughput-input-autopilot-v3 params="{
|
||||||
@@ -472,40 +369,6 @@
|
|||||||
}">
|
}">
|
||||||
</throughput-input-autopilot-v3>
|
</throughput-input-autopilot-v3>
|
||||||
</div>
|
</div>
|
||||||
<!-- /ko -->
|
|
||||||
|
|
||||||
<!-- ko if: hasAutoPilotV2FeatureFlag && hasAutoPilotV2FeatureFlag() -->
|
|
||||||
<div data-bind="visible: displayCollectionThroughput" data-test="addCollection-displayCollectionThroughput">
|
|
||||||
<!-- 3 -->
|
|
||||||
<throughput-input params="{
|
|
||||||
testId: 'collectionThroughputValue',
|
|
||||||
value: throughputMultiPartition,
|
|
||||||
minimum: minThroughputRU,
|
|
||||||
maximum: maxThroughputRU,
|
|
||||||
isEnabled: displayCollectionThroughput,
|
|
||||||
label: throughputRangeText,
|
|
||||||
ariaLabel: throughputRangeText,
|
|
||||||
costsVisible: costsVisible,
|
|
||||||
requestUnitsUsageCost: dedicatedRequestUnitsUsageCost,
|
|
||||||
spendAckChecked: throughputSpendAck,
|
|
||||||
spendAckId: 'throughputSpendAckCollection',
|
|
||||||
spendAckText: throughputSpendAckText,
|
|
||||||
spendAckVisible: throughputSpendAckVisible,
|
|
||||||
showAsMandatory: true,
|
|
||||||
infoBubbleText: ruToolTipText,
|
|
||||||
throughputAutoPilotRadioId: 'newContainer-containerThroughput-autoPilotRadio',
|
|
||||||
throughputProvisionedRadioId: 'newContainer-containerThroughput-manualRadio',
|
|
||||||
throughputModeRadioName: 'throughputModeRadioName',
|
|
||||||
isAutoPilotSelected: isAutoPilotSelected,
|
|
||||||
autoPilotUsageCost: autoPilotUsageCost,
|
|
||||||
canExceedMaximumValue: canExceedMaximumValue,
|
|
||||||
autoPilotTiersList: autoPilotTiersList,
|
|
||||||
selectedAutoPilotTier: selectedAutoPilotTier,
|
|
||||||
showAutoPilot: !isFixedStorageSelected()
|
|
||||||
}">
|
|
||||||
</throughput-input>
|
|
||||||
</div>
|
|
||||||
<!-- /ko -->
|
|
||||||
<!-- Provision collection throughput spinner - end -->
|
<!-- Provision collection throughput spinner - end -->
|
||||||
<!-- /ko -->
|
<!-- /ko -->
|
||||||
<!-- Provision collection throughput - end -->
|
<!-- Provision collection throughput - end -->
|
||||||
|
|||||||
@@ -1,8 +1,7 @@
|
|||||||
import * as Constants from "../../Common/Constants";
|
import * as Constants from "../../Common/Constants";
|
||||||
import AddCollectionPane from "./AddCollectionPane";
|
import AddCollectionPane from "./AddCollectionPane";
|
||||||
import Explorer from "../Explorer";
|
import Explorer from "../Explorer";
|
||||||
import ko from "knockout";
|
import { DatabaseAccount } from "../../Contracts/DataModels";
|
||||||
import { AutopilotTier, DatabaseAccount } from "../../Contracts/DataModels";
|
|
||||||
|
|
||||||
describe("Add Collection Pane", () => {
|
describe("Add Collection Pane", () => {
|
||||||
describe("isValid()", () => {
|
describe("isValid()", () => {
|
||||||
@@ -41,25 +40,6 @@ describe("Add Collection Pane", () => {
|
|||||||
|
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
explorer = new Explorer();
|
explorer = new Explorer();
|
||||||
explorer.hasAutoPilotV2FeatureFlag = ko.computed<boolean>(() => true);
|
|
||||||
});
|
|
||||||
|
|
||||||
it("should be true if autopilot enabled and select valid tier", () => {
|
|
||||||
explorer.databaseAccount(mockDatabaseAccount);
|
|
||||||
const addCollectionPane = explorer.addCollectionPane as AddCollectionPane;
|
|
||||||
addCollectionPane.hasAutoPilotV2FeatureFlag = ko.computed<boolean>(() => true);
|
|
||||||
addCollectionPane.isAutoPilotSelected(true);
|
|
||||||
addCollectionPane.selectedAutoPilotTier(AutopilotTier.Tier2);
|
|
||||||
expect(addCollectionPane.isValid()).toBe(true);
|
|
||||||
});
|
|
||||||
|
|
||||||
it("should be false if autopilot enabled and select invalid tier", () => {
|
|
||||||
explorer.databaseAccount(mockDatabaseAccount);
|
|
||||||
const addCollectionPane = explorer.addCollectionPane as AddCollectionPane;
|
|
||||||
addCollectionPane.hasAutoPilotV2FeatureFlag = ko.computed<boolean>(() => true);
|
|
||||||
addCollectionPane.isAutoPilotSelected(true);
|
|
||||||
addCollectionPane.selectedAutoPilotTier(0);
|
|
||||||
expect(addCollectionPane.isValid()).toBe(false);
|
|
||||||
});
|
});
|
||||||
|
|
||||||
it("should be true if graph API and partition key is not /id nor /label", () => {
|
it("should be true if graph API and partition key is not /id nor /label", () => {
|
||||||
|
|||||||
@@ -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>;
|
||||||
@@ -75,10 +75,6 @@ export default class AddCollectionPane extends ContextualPaneBase {
|
|||||||
public debugstring: ko.Computed<string>;
|
public debugstring: ko.Computed<string>;
|
||||||
public displayCollectionThroughput: ko.Computed<boolean>;
|
public displayCollectionThroughput: ko.Computed<boolean>;
|
||||||
public isAutoPilotSelected: ko.Observable<boolean>;
|
public isAutoPilotSelected: ko.Observable<boolean>;
|
||||||
public selectedAutoPilotTier: ko.Observable<DataModels.AutopilotTier>;
|
|
||||||
public selectedSharedAutoPilotTier: ko.Observable<DataModels.AutopilotTier>;
|
|
||||||
public autoPilotTiersList: ko.ObservableArray<ViewModels.DropdownOption<DataModels.AutopilotTier>>;
|
|
||||||
public sharedAutoPilotTiersList: ko.ObservableArray<ViewModels.DropdownOption<DataModels.AutopilotTier>>;
|
|
||||||
public isSharedAutoPilotSelected: ko.Observable<boolean>;
|
public isSharedAutoPilotSelected: ko.Observable<boolean>;
|
||||||
public autoPilotThroughput: ko.Observable<number>;
|
public autoPilotThroughput: ko.Observable<number>;
|
||||||
public sharedAutoPilotThroughput: ko.Observable<number>;
|
public sharedAutoPilotThroughput: ko.Observable<number>;
|
||||||
@@ -92,7 +88,6 @@ export default class AddCollectionPane extends ContextualPaneBase {
|
|||||||
public isAnalyticalStorageOn: ko.Observable<boolean>;
|
public isAnalyticalStorageOn: ko.Observable<boolean>;
|
||||||
public isSynapseLinkUpdating: ko.Computed<boolean>;
|
public isSynapseLinkUpdating: ko.Computed<boolean>;
|
||||||
public canExceedMaximumValue: ko.PureComputed<boolean>;
|
public canExceedMaximumValue: ko.PureComputed<boolean>;
|
||||||
public hasAutoPilotV2FeatureFlag: ko.PureComputed<boolean>;
|
|
||||||
public ruToolTipText: ko.Computed<string>;
|
public ruToolTipText: ko.Computed<string>;
|
||||||
public canConfigureThroughput: ko.PureComputed<boolean>;
|
public canConfigureThroughput: ko.PureComputed<boolean>;
|
||||||
public showUpsellMessage: ko.PureComputed<boolean>;
|
public showUpsellMessage: ko.PureComputed<boolean>;
|
||||||
@@ -102,8 +97,7 @@ export default class AddCollectionPane extends ContextualPaneBase {
|
|||||||
|
|
||||||
constructor(options: AddCollectionPaneOptions) {
|
constructor(options: AddCollectionPaneOptions) {
|
||||||
super(options);
|
super(options);
|
||||||
this.hasAutoPilotV2FeatureFlag = ko.pureComputed(() => this.container.hasAutoPilotV2FeatureFlag());
|
this.ruToolTipText = ko.pureComputed(() => PricingUtils.getRuToolTipText());
|
||||||
this.ruToolTipText = ko.pureComputed(() => PricingUtils.getRuToolTipText(this.hasAutoPilotV2FeatureFlag()));
|
|
||||||
this.canConfigureThroughput = ko.pureComputed(() => !this.container.isServerlessEnabled());
|
this.canConfigureThroughput = ko.pureComputed(() => !this.container.isServerlessEnabled());
|
||||||
this.showUpsellMessage = ko.pureComputed(() => !this.container.isServerlessEnabled());
|
this.showUpsellMessage = ko.pureComputed(() => !this.container.isServerlessEnabled());
|
||||||
this.formWarnings = ko.observable<string>();
|
this.formWarnings = ko.observable<string>();
|
||||||
@@ -171,13 +165,13 @@ export default class AddCollectionPane extends ContextualPaneBase {
|
|||||||
this.databaseHasSharedOffer = ko.observable<boolean>(true);
|
this.databaseHasSharedOffer = ko.observable<boolean>(true);
|
||||||
this.throughputRangeText = ko.pureComputed<string>(() => {
|
this.throughputRangeText = ko.pureComputed<string>(() => {
|
||||||
if (this.isAutoPilotSelected()) {
|
if (this.isAutoPilotSelected()) {
|
||||||
return AutoPilotUtils.getAutoPilotHeaderText(this.hasAutoPilotV2FeatureFlag());
|
return AutoPilotUtils.getAutoPilotHeaderText();
|
||||||
}
|
}
|
||||||
return `Throughput (${this.minThroughputRU().toLocaleString()} - ${this.maxThroughputRU().toLocaleString()} RU/s)`;
|
return `Throughput (${this.minThroughputRU().toLocaleString()} - ${this.maxThroughputRU().toLocaleString()} RU/s)`;
|
||||||
});
|
});
|
||||||
this.sharedThroughputRangeText = ko.pureComputed<string>(() => {
|
this.sharedThroughputRangeText = ko.pureComputed<string>(() => {
|
||||||
if (this.isSharedAutoPilotSelected()) {
|
if (this.isSharedAutoPilotSelected()) {
|
||||||
return AutoPilotUtils.getAutoPilotHeaderText(this.hasAutoPilotV2FeatureFlag());
|
return AutoPilotUtils.getAutoPilotHeaderText();
|
||||||
}
|
}
|
||||||
return `Throughput (${this.minThroughputRU().toLocaleString()} - ${this.maxThroughputRU().toLocaleString()} RU/s)`;
|
return `Throughput (${this.minThroughputRU().toLocaleString()} - ${this.maxThroughputRU().toLocaleString()} RU/s)`;
|
||||||
});
|
});
|
||||||
@@ -447,7 +441,7 @@ export default class AddCollectionPane extends ContextualPaneBase {
|
|||||||
|
|
||||||
this.throughputSpendAckVisible = ko.pureComputed<boolean>(() => {
|
this.throughputSpendAckVisible = ko.pureComputed<boolean>(() => {
|
||||||
const autoscaleThroughput = this.autoPilotThroughput() * 1;
|
const autoscaleThroughput = this.autoPilotThroughput() * 1;
|
||||||
if (!this.hasAutoPilotV2FeatureFlag() && this.isAutoPilotSelected()) {
|
if (this.isAutoPilotSelected()) {
|
||||||
return autoscaleThroughput > SharedConstants.CollectionCreation.DefaultCollectionRUs100K;
|
return autoscaleThroughput > SharedConstants.CollectionCreation.DefaultCollectionRUs100K;
|
||||||
}
|
}
|
||||||
const selectedThroughput: number = this._getThroughput();
|
const selectedThroughput: number = this._getThroughput();
|
||||||
@@ -490,14 +484,6 @@ export default class AddCollectionPane extends ContextualPaneBase {
|
|||||||
|
|
||||||
this.isAutoPilotSelected = ko.observable<boolean>(false);
|
this.isAutoPilotSelected = ko.observable<boolean>(false);
|
||||||
this.isSharedAutoPilotSelected = ko.observable<boolean>(false);
|
this.isSharedAutoPilotSelected = ko.observable<boolean>(false);
|
||||||
this.selectedAutoPilotTier = ko.observable<DataModels.AutopilotTier>();
|
|
||||||
this.selectedSharedAutoPilotTier = ko.observable<DataModels.AutopilotTier>();
|
|
||||||
this.autoPilotTiersList = ko.observableArray<ViewModels.DropdownOption<DataModels.AutopilotTier>>(
|
|
||||||
AutoPilotUtils.getAvailableAutoPilotTiersOptions()
|
|
||||||
);
|
|
||||||
this.sharedAutoPilotTiersList = ko.observableArray<ViewModels.DropdownOption<DataModels.AutopilotTier>>(
|
|
||||||
AutoPilotUtils.getAvailableAutoPilotTiersOptions()
|
|
||||||
);
|
|
||||||
this.autoPilotThroughput = ko.observable<number>(AutoPilotUtils.minAutoPilotThroughput);
|
this.autoPilotThroughput = ko.observable<number>(AutoPilotUtils.minAutoPilotThroughput);
|
||||||
this.sharedAutoPilotThroughput = ko.observable<number>(AutoPilotUtils.minAutoPilotThroughput);
|
this.sharedAutoPilotThroughput = ko.observable<number>(AutoPilotUtils.minAutoPilotThroughput);
|
||||||
this.autoPilotUsageCost = ko.pureComputed<string>(() => {
|
this.autoPilotUsageCost = ko.pureComputed<string>(() => {
|
||||||
@@ -506,9 +492,7 @@ export default class AddCollectionPane extends ContextualPaneBase {
|
|||||||
return "";
|
return "";
|
||||||
}
|
}
|
||||||
const isDatabaseThroughput: boolean = this.databaseCreateNewShared();
|
const isDatabaseThroughput: boolean = this.databaseCreateNewShared();
|
||||||
return !this.hasAutoPilotV2FeatureFlag()
|
return PricingUtils.getAutoPilotV3SpendHtml(autoPilot.maxThroughput, isDatabaseThroughput);
|
||||||
? PricingUtils.getAutoPilotV3SpendHtml(autoPilot.maxThroughput, isDatabaseThroughput)
|
|
||||||
: PricingUtils.getAutoPilotV2SpendHtml(autoPilot.autopilotTier, isDatabaseThroughput);
|
|
||||||
});
|
});
|
||||||
|
|
||||||
this.resetData();
|
this.resetData();
|
||||||
@@ -897,10 +881,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 = {
|
||||||
@@ -928,7 +911,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);
|
||||||
}
|
}
|
||||||
@@ -942,13 +926,9 @@ export default class AddCollectionPane extends ContextualPaneBase {
|
|||||||
this.throughputSpendAck(false);
|
this.throughputSpendAck(false);
|
||||||
this.isAutoPilotSelected(false);
|
this.isAutoPilotSelected(false);
|
||||||
this.isSharedAutoPilotSelected(false);
|
this.isSharedAutoPilotSelected(false);
|
||||||
if (!this.hasAutoPilotV2FeatureFlag()) {
|
this.autoPilotThroughput(AutoPilotUtils.minAutoPilotThroughput);
|
||||||
this.autoPilotThroughput(AutoPilotUtils.minAutoPilotThroughput);
|
this.sharedAutoPilotThroughput(AutoPilotUtils.minAutoPilotThroughput);
|
||||||
this.sharedAutoPilotThroughput(AutoPilotUtils.minAutoPilotThroughput);
|
|
||||||
} else {
|
|
||||||
this.selectedAutoPilotTier(undefined);
|
|
||||||
this.selectedSharedAutoPilotTier(undefined);
|
|
||||||
}
|
|
||||||
this.uniqueKeys([]);
|
this.uniqueKeys([]);
|
||||||
this.useIndexingForSharedThroughput(true);
|
this.useIndexingForSharedThroughput(true);
|
||||||
|
|
||||||
@@ -1027,17 +1007,12 @@ export default class AddCollectionPane extends ContextualPaneBase {
|
|||||||
if ((this.databaseCreateNewShared() && this.isSharedAutoPilotSelected()) || this.isAutoPilotSelected()) {
|
if ((this.databaseCreateNewShared() && this.isSharedAutoPilotSelected()) || this.isAutoPilotSelected()) {
|
||||||
const autoPilot = this._getAutoPilot();
|
const autoPilot = this._getAutoPilot();
|
||||||
if (
|
if (
|
||||||
(!this.hasAutoPilotV2FeatureFlag() &&
|
!autoPilot ||
|
||||||
(!autoPilot ||
|
!autoPilot.maxThroughput ||
|
||||||
!autoPilot.maxThroughput ||
|
!AutoPilotUtils.isValidAutoPilotThroughput(autoPilot.maxThroughput)
|
||||||
!AutoPilotUtils.isValidAutoPilotThroughput(autoPilot.maxThroughput))) ||
|
|
||||||
(this.hasAutoPilotV2FeatureFlag() &&
|
|
||||||
(!autoPilot || !autoPilot.autopilotTier || !AutoPilotUtils.isValidAutoPilotTier(autoPilot.autopilotTier)))
|
|
||||||
) {
|
) {
|
||||||
this.formErrors(
|
this.formErrors(
|
||||||
!this.hasAutoPilotV2FeatureFlag()
|
`Please enter a value greater than ${AutoPilotUtils.minAutoPilotThroughput} for autopilot throughput`
|
||||||
? `Please enter a value greater than ${AutoPilotUtils.minAutoPilotThroughput} for autopilot throughput`
|
|
||||||
: "Please select an Autopilot tier from the list."
|
|
||||||
);
|
);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@@ -1067,7 +1042,6 @@ export default class AddCollectionPane extends ContextualPaneBase {
|
|||||||
const autoscaleThroughput = this.autoPilotThroughput() * 1;
|
const autoscaleThroughput = this.autoPilotThroughput() * 1;
|
||||||
|
|
||||||
if (
|
if (
|
||||||
!this.hasAutoPilotV2FeatureFlag() &&
|
|
||||||
this.isAutoPilotSelected() &&
|
this.isAutoPilotSelected() &&
|
||||||
autoscaleThroughput > SharedConstants.CollectionCreation.DefaultCollectionRUs100K &&
|
autoscaleThroughput > SharedConstants.CollectionCreation.DefaultCollectionRUs100K &&
|
||||||
!this.throughputSpendAck()
|
!this.throughputSpendAck()
|
||||||
@@ -1114,31 +1088,15 @@ export default class AddCollectionPane extends ContextualPaneBase {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private _getAutoPilot(): DataModels.AutoPilotCreationSettings {
|
private _getAutoPilot(): DataModels.AutoPilotCreationSettings {
|
||||||
if (
|
if (this.databaseCreateNewShared() && this.isSharedAutoPilotSelected() && this.sharedAutoPilotThroughput()) {
|
||||||
(!this.hasAutoPilotV2FeatureFlag() &&
|
return {
|
||||||
this.databaseCreateNewShared() &&
|
maxThroughput: this.sharedAutoPilotThroughput() * 1
|
||||||
this.isSharedAutoPilotSelected() &&
|
};
|
||||||
this.sharedAutoPilotThroughput()) ||
|
|
||||||
(this.hasAutoPilotV2FeatureFlag() &&
|
|
||||||
this.databaseCreateNewShared() &&
|
|
||||||
this.isSharedAutoPilotSelected() &&
|
|
||||||
this.selectedSharedAutoPilotTier())
|
|
||||||
) {
|
|
||||||
return !this.hasAutoPilotV2FeatureFlag()
|
|
||||||
? {
|
|
||||||
maxThroughput: this.sharedAutoPilotThroughput() * 1
|
|
||||||
}
|
|
||||||
: { autopilotTier: this.selectedSharedAutoPilotTier() };
|
|
||||||
}
|
}
|
||||||
if (
|
if (this.isAutoPilotSelected() && this.autoPilotThroughput()) {
|
||||||
(!this.hasAutoPilotV2FeatureFlag() && this.isAutoPilotSelected() && this.autoPilotThroughput()) ||
|
return {
|
||||||
(this.hasAutoPilotV2FeatureFlag() && this.isAutoPilotSelected() && this.selectedAutoPilotTier())
|
maxThroughput: this.autoPilotThroughput() * 1
|
||||||
) {
|
};
|
||||||
return !this.hasAutoPilotV2FeatureFlag()
|
|
||||||
? {
|
|
||||||
maxThroughput: this.autoPilotThroughput() * 1
|
|
||||||
}
|
|
||||||
: { autopilotTier: this.selectedAutoPilotTier() };
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return undefined;
|
return undefined;
|
||||||
|
|||||||
@@ -89,7 +89,6 @@
|
|||||||
data-bind="text: databaseLevelThroughputTooltipText"></span>
|
data-bind="text: databaseLevelThroughputTooltipText"></span>
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
<!-- ko if: hasAutoPilotV2FeatureFlag && !hasAutoPilotV2FeatureFlag() -->
|
|
||||||
<div data-bind="visible: databaseCreateNewShared">
|
<div data-bind="visible: databaseCreateNewShared">
|
||||||
<throughput-input-autopilot-v3 params="{
|
<throughput-input-autopilot-v3 params="{
|
||||||
step: 100,
|
step: 100,
|
||||||
@@ -124,42 +123,6 @@
|
|||||||
support</a> for more than <span data-bind="text: maxThroughputRUText"></span> RU/s.</p>
|
support</a> for more than <span data-bind="text: maxThroughputRUText"></span> RU/s.</p>
|
||||||
</div>
|
</div>
|
||||||
<!-- /ko -->
|
<!-- /ko -->
|
||||||
<!-- ko if: hasAutoPilotV2FeatureFlag && hasAutoPilotV2FeatureFlag() -->
|
|
||||||
<div data-bind="visible: databaseCreateNewShared">
|
|
||||||
<throughput-input params="{
|
|
||||||
step: 100,
|
|
||||||
value: throughput,
|
|
||||||
testId: 'sharedThroughputValue',
|
|
||||||
minimum: minThroughputRU,
|
|
||||||
maximum: maxThroughputRU,
|
|
||||||
isEnabled: databaseCreateNewShared,
|
|
||||||
label: throughputRangeText,
|
|
||||||
ariaLabel: throughputRangeText,
|
|
||||||
costsVisible: costsVisible,
|
|
||||||
requestUnitsUsageCost: requestUnitsUsageCost,
|
|
||||||
spendAckChecked: throughputSpendAck,
|
|
||||||
spendAckId: 'throughputSpendAckDatabase',
|
|
||||||
spendAckText: throughputSpendAckText,
|
|
||||||
spendAckVisible: throughputSpendAckVisible,
|
|
||||||
showAsMandatory: true,
|
|
||||||
infoBubbleText: ruToolTipText,
|
|
||||||
throughputAutoPilotRadioId: 'newDatabase-databaseThroughput-autoPilotRadio',
|
|
||||||
throughputProvisionedRadioId: 'newDatabase-databaseThroughput-manualRadio',
|
|
||||||
throughputModeRadioName: 'throughputModeRadioName',
|
|
||||||
isAutoPilotSelected: isAutoPilotSelected,
|
|
||||||
autoPilotTiersList: autoPilotTiersList,
|
|
||||||
selectedAutoPilotTier: selectedAutoPilotTier,
|
|
||||||
autoPilotUsageCost: autoPilotUsageCost,
|
|
||||||
canExceedMaximumValue: canExceedMaximumValue
|
|
||||||
}">
|
|
||||||
</throughput-input>
|
|
||||||
<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>
|
|
||||||
<!-- /ko -->
|
|
||||||
<!-- /ko -->
|
|
||||||
<!-- Database provisioned throughput - End -->
|
<!-- Database provisioned throughput - End -->
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -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>;
|
||||||
@@ -38,12 +38,9 @@ export default class AddDatabasePane extends ContextualPaneBase {
|
|||||||
public upsellAnchorUrl: ko.PureComputed<string>;
|
public upsellAnchorUrl: ko.PureComputed<string>;
|
||||||
public upsellAnchorText: ko.PureComputed<string>;
|
public upsellAnchorText: ko.PureComputed<string>;
|
||||||
public isAutoPilotSelected: ko.Observable<boolean>;
|
public isAutoPilotSelected: ko.Observable<boolean>;
|
||||||
public selectedAutoPilotTier: ko.Observable<DataModels.AutopilotTier>;
|
|
||||||
public autoPilotTiersList: ko.ObservableArray<ViewModels.DropdownOption<DataModels.AutopilotTier>>;
|
|
||||||
public maxAutoPilotThroughputSet: ko.Observable<number>;
|
public maxAutoPilotThroughputSet: ko.Observable<number>;
|
||||||
public autoPilotUsageCost: ko.Computed<string>;
|
public autoPilotUsageCost: ko.Computed<string>;
|
||||||
public canExceedMaximumValue: ko.PureComputed<boolean>;
|
public canExceedMaximumValue: ko.PureComputed<boolean>;
|
||||||
public hasAutoPilotV2FeatureFlag: ko.PureComputed<boolean>;
|
|
||||||
public ruToolTipText: ko.Computed<string>;
|
public ruToolTipText: ko.Computed<string>;
|
||||||
public isFreeTierAccount: ko.Computed<boolean>;
|
public isFreeTierAccount: ko.Computed<boolean>;
|
||||||
public canConfigureThroughput: ko.PureComputed<boolean>;
|
public canConfigureThroughput: ko.PureComputed<boolean>;
|
||||||
@@ -53,8 +50,7 @@ export default class AddDatabasePane extends ContextualPaneBase {
|
|||||||
super(options);
|
super(options);
|
||||||
this.title((this.container && this.container.addDatabaseText()) || "New Database");
|
this.title((this.container && this.container.addDatabaseText()) || "New Database");
|
||||||
this.databaseId = ko.observable<string>();
|
this.databaseId = ko.observable<string>();
|
||||||
this.hasAutoPilotV2FeatureFlag = ko.pureComputed(() => this.container.hasAutoPilotV2FeatureFlag());
|
this.ruToolTipText = ko.pureComputed(() => PricingUtils.getRuToolTipText());
|
||||||
this.ruToolTipText = ko.pureComputed(() => PricingUtils.getRuToolTipText(this.hasAutoPilotV2FeatureFlag()));
|
|
||||||
this.canConfigureThroughput = ko.pureComputed(() => !this.container.isServerlessEnabled());
|
this.canConfigureThroughput = ko.pureComputed(() => !this.container.isServerlessEnabled());
|
||||||
this.showUpsellMessage = ko.pureComputed(() => !this.container.isServerlessEnabled());
|
this.showUpsellMessage = ko.pureComputed(() => !this.container.isServerlessEnabled());
|
||||||
|
|
||||||
@@ -94,10 +90,6 @@ export default class AddDatabasePane extends ContextualPaneBase {
|
|||||||
this.minThroughputRU = ko.observable<number>();
|
this.minThroughputRU = ko.observable<number>();
|
||||||
this.throughputSpendAckText = ko.observable<string>();
|
this.throughputSpendAckText = ko.observable<string>();
|
||||||
this.throughputSpendAck = ko.observable<boolean>(false);
|
this.throughputSpendAck = ko.observable<boolean>(false);
|
||||||
this.selectedAutoPilotTier = ko.observable<DataModels.AutopilotTier>();
|
|
||||||
this.autoPilotTiersList = ko.observableArray<ViewModels.DropdownOption<DataModels.AutopilotTier>>(
|
|
||||||
AutoPilotUtils.getAvailableAutoPilotTiersOptions()
|
|
||||||
);
|
|
||||||
this.isAutoPilotSelected = ko.observable<boolean>(false);
|
this.isAutoPilotSelected = ko.observable<boolean>(false);
|
||||||
this.maxAutoPilotThroughputSet = ko.observable<number>(AutoPilotUtils.minAutoPilotThroughput);
|
this.maxAutoPilotThroughputSet = ko.observable<number>(AutoPilotUtils.minAutoPilotThroughput);
|
||||||
this.autoPilotUsageCost = ko.pureComputed<string>(() => {
|
this.autoPilotUsageCost = ko.pureComputed<string>(() => {
|
||||||
@@ -105,13 +97,11 @@ export default class AddDatabasePane extends ContextualPaneBase {
|
|||||||
if (!autoPilot) {
|
if (!autoPilot) {
|
||||||
return "";
|
return "";
|
||||||
}
|
}
|
||||||
return !this.hasAutoPilotV2FeatureFlag()
|
return PricingUtils.getAutoPilotV3SpendHtml(autoPilot.maxThroughput, true /* isDatabaseThroughput */);
|
||||||
? PricingUtils.getAutoPilotV3SpendHtml(autoPilot.maxThroughput, true /* isDatabaseThroughput */)
|
|
||||||
: PricingUtils.getAutoPilotV2SpendHtml(autoPilot.autopilotTier, true /* isDatabaseThroughput */);
|
|
||||||
});
|
});
|
||||||
this.throughputRangeText = ko.pureComputed<string>(() => {
|
this.throughputRangeText = ko.pureComputed<string>(() => {
|
||||||
if (this.isAutoPilotSelected()) {
|
if (this.isAutoPilotSelected()) {
|
||||||
return AutoPilotUtils.getAutoPilotHeaderText(this.hasAutoPilotV2FeatureFlag());
|
return AutoPilotUtils.getAutoPilotHeaderText();
|
||||||
}
|
}
|
||||||
return `Throughput (${this.minThroughputRU().toLocaleString()} - ${this.maxThroughputRU().toLocaleString()} RU/s)`;
|
return `Throughput (${this.minThroughputRU().toLocaleString()} - ${this.maxThroughputRU().toLocaleString()} RU/s)`;
|
||||||
});
|
});
|
||||||
@@ -208,7 +198,7 @@ export default class AddDatabasePane extends ContextualPaneBase {
|
|||||||
|
|
||||||
this.throughputSpendAckVisible = ko.pureComputed<boolean>(() => {
|
this.throughputSpendAckVisible = ko.pureComputed<boolean>(() => {
|
||||||
const autoscaleThroughput = this.maxAutoPilotThroughputSet() * 1;
|
const autoscaleThroughput = this.maxAutoPilotThroughputSet() * 1;
|
||||||
if (!this.hasAutoPilotV2FeatureFlag() && this.isAutoPilotSelected()) {
|
if (this.isAutoPilotSelected()) {
|
||||||
return autoscaleThroughput > SharedConstants.CollectionCreation.DefaultCollectionRUs100K;
|
return autoscaleThroughput > SharedConstants.CollectionCreation.DefaultCollectionRUs100K;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -306,18 +296,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);
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@@ -325,7 +319,6 @@ export default class AddDatabasePane extends ContextualPaneBase {
|
|||||||
public resetData() {
|
public resetData() {
|
||||||
this.databaseId("");
|
this.databaseId("");
|
||||||
this.databaseCreateNewShared(this.getSharedThroughputDefault());
|
this.databaseCreateNewShared(this.getSharedThroughputDefault());
|
||||||
this.selectedAutoPilotTier(undefined);
|
|
||||||
this.isAutoPilotSelected(false);
|
this.isAutoPilotSelected(false);
|
||||||
this.maxAutoPilotThroughputSet(AutoPilotUtils.minAutoPilotThroughput);
|
this.maxAutoPilotThroughputSet(AutoPilotUtils.minAutoPilotThroughput);
|
||||||
this._updateThroughputLimitByDatabase();
|
this._updateThroughputLimitByDatabase();
|
||||||
@@ -367,11 +360,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(),
|
||||||
@@ -386,7 +379,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);
|
||||||
}
|
}
|
||||||
@@ -413,17 +407,12 @@ export default class AddDatabasePane extends ContextualPaneBase {
|
|||||||
if (this.isAutoPilotSelected()) {
|
if (this.isAutoPilotSelected()) {
|
||||||
const autoPilot = this._isAutoPilotSelectedAndWhatTier();
|
const autoPilot = this._isAutoPilotSelectedAndWhatTier();
|
||||||
if (
|
if (
|
||||||
(!this.hasAutoPilotV2FeatureFlag() &&
|
!autoPilot ||
|
||||||
(!autoPilot ||
|
!autoPilot.maxThroughput ||
|
||||||
!autoPilot.maxThroughput ||
|
!AutoPilotUtils.isValidAutoPilotThroughput(autoPilot.maxThroughput)
|
||||||
!AutoPilotUtils.isValidAutoPilotThroughput(autoPilot.maxThroughput))) ||
|
|
||||||
(this.hasAutoPilotV2FeatureFlag() &&
|
|
||||||
(!autoPilot || !autoPilot.autopilotTier || !AutoPilotUtils.isValidAutoPilotTier(autoPilot.autopilotTier)))
|
|
||||||
) {
|
) {
|
||||||
this.formErrors(
|
this.formErrors(
|
||||||
!this.hasAutoPilotV2FeatureFlag()
|
`Please enter a value greater than ${AutoPilotUtils.minAutoPilotThroughput} for autopilot throughput`
|
||||||
? `Please enter a value greater than ${AutoPilotUtils.minAutoPilotThroughput} for autopilot throughput`
|
|
||||||
: "Please select an Autopilot tier from the list."
|
|
||||||
);
|
);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@@ -438,7 +427,6 @@ export default class AddDatabasePane extends ContextualPaneBase {
|
|||||||
const autoscaleThroughput = this.maxAutoPilotThroughputSet() * 1;
|
const autoscaleThroughput = this.maxAutoPilotThroughputSet() * 1;
|
||||||
|
|
||||||
if (
|
if (
|
||||||
!this.hasAutoPilotV2FeatureFlag() &&
|
|
||||||
this.isAutoPilotSelected() &&
|
this.isAutoPilotSelected() &&
|
||||||
autoscaleThroughput > SharedConstants.CollectionCreation.DefaultCollectionRUs100K &&
|
autoscaleThroughput > SharedConstants.CollectionCreation.DefaultCollectionRUs100K &&
|
||||||
!this.throughputSpendAck()
|
!this.throughputSpendAck()
|
||||||
@@ -451,15 +439,10 @@ export default class AddDatabasePane extends ContextualPaneBase {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private _isAutoPilotSelectedAndWhatTier(): DataModels.AutoPilotCreationSettings {
|
private _isAutoPilotSelectedAndWhatTier(): DataModels.AutoPilotCreationSettings {
|
||||||
if (
|
if (this.isAutoPilotSelected() && this.maxAutoPilotThroughputSet()) {
|
||||||
(!this.hasAutoPilotV2FeatureFlag() && this.isAutoPilotSelected() && this.maxAutoPilotThroughputSet()) ||
|
return {
|
||||||
(this.hasAutoPilotV2FeatureFlag() && this.isAutoPilotSelected() && this.selectedAutoPilotTier())
|
maxThroughput: this.maxAutoPilotThroughputSet() * 1
|
||||||
) {
|
};
|
||||||
return !this.hasAutoPilotV2FeatureFlag()
|
|
||||||
? {
|
|
||||||
maxThroughput: this.maxAutoPilotThroughputSet() * 1
|
|
||||||
}
|
|
||||||
: { autopilotTier: this.selectedAutoPilotTier() };
|
|
||||||
}
|
}
|
||||||
return undefined;
|
return undefined;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -142,7 +142,6 @@
|
|||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
<!-- 1 -->
|
<!-- 1 -->
|
||||||
<!-- ko if: hasAutoPilotV2FeatureFlag && !hasAutoPilotV2FeatureFlag() -->
|
|
||||||
<div data-bind="visible: keyspaceCreateNew() && keyspaceHasSharedOffer()">
|
<div data-bind="visible: keyspaceCreateNew() && keyspaceHasSharedOffer()">
|
||||||
<throughput-input-autopilot-v3
|
<throughput-input-autopilot-v3
|
||||||
params="{
|
params="{
|
||||||
@@ -173,38 +172,6 @@
|
|||||||
</throughput-input-autopilot-v3>
|
</throughput-input-autopilot-v3>
|
||||||
</div>
|
</div>
|
||||||
<!-- /ko -->
|
<!-- /ko -->
|
||||||
<!-- ko if: hasAutoPilotV2FeatureFlag && hasAutoPilotV2FeatureFlag() -->
|
|
||||||
<div data-bind="visible: keyspaceCreateNew() && keyspaceHasSharedOffer()">
|
|
||||||
<throughput-input
|
|
||||||
params="{
|
|
||||||
testId: 'cassandraThroughputValue-v2-shared',
|
|
||||||
value: keyspaceThroughput,
|
|
||||||
minimum: minThroughputRU,
|
|
||||||
maximum: maxThroughputRU,
|
|
||||||
isEnabled: keyspaceCreateNew() && keyspaceHasSharedOffer(),
|
|
||||||
label: sharedThroughputRangeText,
|
|
||||||
ariaLabel: sharedThroughputRangeText,
|
|
||||||
requestUnitsUsageCost: requestUnitsUsageCostShared,
|
|
||||||
spendAckChecked: sharedThroughputSpendAck,
|
|
||||||
spendAckId: 'sharedThroughputSpendAck-v2-shared',
|
|
||||||
spendAckText: sharedThroughputSpendAckText,
|
|
||||||
spendAckVisible: sharedThroughputSpendAckVisible,
|
|
||||||
showAsMandatory: true,
|
|
||||||
infoBubbleText: ruToolTipText,
|
|
||||||
throughputAutoPilotRadioId: 'newKeyspace-databaseThroughput-autoPilotRadio-v2-shared',
|
|
||||||
throughputProvisionedRadioId: 'newKeyspace-databaseThroughput-manualRadio-v2-shared',
|
|
||||||
isAutoPilotSelected: isSharedAutoPilotSelected,
|
|
||||||
autoPilotTiersList: sharedAutoPilotTiersList,
|
|
||||||
costsVisible: costsVisible,
|
|
||||||
selectedAutoPilotTier: selectedSharedAutoPilotTier,
|
|
||||||
autoPilotUsageCost: autoPilotUsageCost,
|
|
||||||
canExceedMaximumValue: canExceedMaximumValue
|
|
||||||
}"
|
|
||||||
>
|
|
||||||
</throughput-input>
|
|
||||||
</div>
|
|
||||||
<!-- /ko -->
|
|
||||||
<!-- /ko -->
|
|
||||||
<!-- Database provisioned throughput - End -->
|
<!-- Database provisioned throughput - End -->
|
||||||
</div>
|
</div>
|
||||||
<div class="seconddivpadding">
|
<div class="seconddivpadding">
|
||||||
@@ -257,7 +224,6 @@
|
|||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
<!-- 2 -->
|
<!-- 2 -->
|
||||||
<!-- ko if: hasAutoPilotV2FeatureFlag && !hasAutoPilotV2FeatureFlag() -->
|
|
||||||
<div data-bind="visible: !keyspaceHasSharedOffer() || dedicateTableThroughput()">
|
<div data-bind="visible: !keyspaceHasSharedOffer() || dedicateTableThroughput()">
|
||||||
<throughput-input-autopilot-v3
|
<throughput-input-autopilot-v3
|
||||||
params="{
|
params="{
|
||||||
@@ -289,40 +255,6 @@
|
|||||||
</throughput-input-autopilot-v3>
|
</throughput-input-autopilot-v3>
|
||||||
</div>
|
</div>
|
||||||
<!-- /ko -->
|
<!-- /ko -->
|
||||||
|
|
||||||
<!-- ko if: hasAutoPilotV2FeatureFlag && hasAutoPilotV2FeatureFlag() -->
|
|
||||||
<div data-bind="visible: !keyspaceHasSharedOffer() || dedicateTableThroughput()">
|
|
||||||
<throughput-input
|
|
||||||
params="{
|
|
||||||
testId: 'cassandraSharedThroughputValue-v2-dedicated',
|
|
||||||
value: throughput,
|
|
||||||
minimum: minThroughputRU,
|
|
||||||
maximum: maxThroughputRU,
|
|
||||||
isEnabled: !keyspaceHasSharedOffer() || dedicateTableThroughput(),
|
|
||||||
label: throughputRangeText,
|
|
||||||
ariaLabel: throughputRangeText,
|
|
||||||
costsVisible: costsVisible,
|
|
||||||
requestUnitsUsageCost: requestUnitsUsageCostDedicated,
|
|
||||||
spendAckChecked: throughputSpendAck,
|
|
||||||
spendAckId: 'throughputSpendAckCassandra-v2-dedicated',
|
|
||||||
spendAckText: throughputSpendAckText,
|
|
||||||
spendAckVisible: throughputSpendAckVisible,
|
|
||||||
showAsMandatory: true,
|
|
||||||
infoBubbleText: ruToolTipText,
|
|
||||||
throughputAutoPilotRadioId: 'newKeyspace-containerThroughput-autoPilotRadio-v2-dedicated',
|
|
||||||
throughputProvisionedRadioId: 'newKeyspace-containerThroughput-manualRadio-v2-dedicated',
|
|
||||||
isAutoPilotSelected: isAutoPilotSelected,
|
|
||||||
autoPilotTiersList: autoPilotTiersList,
|
|
||||||
selectedAutoPilotTier: selectedAutoPilotTier,
|
|
||||||
autoPilotUsageCost: autoPilotUsageCost,
|
|
||||||
showAutoPilot: false,
|
|
||||||
canExceedMaximumValue: canExceedMaximumValue
|
|
||||||
}"
|
|
||||||
>
|
|
||||||
</throughput-input>
|
|
||||||
</div>
|
|
||||||
<!-- /ko -->
|
|
||||||
<!-- /ko -->
|
|
||||||
<!-- Provision table throughput - end -->
|
<!-- Provision table throughput - end -->
|
||||||
</div>
|
</div>
|
||||||
<div class="paneFooter">
|
<div class="paneFooter">
|
||||||
|
|||||||
@@ -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>;
|
||||||
@@ -38,10 +39,6 @@ export default class CassandraAddCollectionPane extends ContextualPaneBase {
|
|||||||
public sharedThroughputSpendAck: ko.Observable<boolean>;
|
public sharedThroughputSpendAck: ko.Observable<boolean>;
|
||||||
public sharedThroughputSpendAckText: ko.Observable<string>;
|
public sharedThroughputSpendAckText: ko.Observable<string>;
|
||||||
public isAutoPilotSelected: ko.Observable<boolean>;
|
public isAutoPilotSelected: ko.Observable<boolean>;
|
||||||
public selectedAutoPilotTier: ko.Observable<DataModels.AutopilotTier>;
|
|
||||||
public selectedSharedAutoPilotTier: ko.Observable<DataModels.AutopilotTier>;
|
|
||||||
public autoPilotTiersList: ko.ObservableArray<ViewModels.DropdownOption<DataModels.AutopilotTier>>;
|
|
||||||
public sharedAutoPilotTiersList: ko.ObservableArray<ViewModels.DropdownOption<DataModels.AutopilotTier>>;
|
|
||||||
public isSharedAutoPilotSelected: ko.Observable<boolean>;
|
public isSharedAutoPilotSelected: ko.Observable<boolean>;
|
||||||
public selectedAutoPilotThroughput: ko.Observable<number>;
|
public selectedAutoPilotThroughput: ko.Observable<number>;
|
||||||
public sharedAutoPilotThroughput: ko.Observable<number>;
|
public sharedAutoPilotThroughput: ko.Observable<number>;
|
||||||
@@ -49,7 +46,6 @@ export default class CassandraAddCollectionPane extends ContextualPaneBase {
|
|||||||
public sharedThroughputSpendAckVisible: ko.Computed<boolean>;
|
public sharedThroughputSpendAckVisible: ko.Computed<boolean>;
|
||||||
public throughputSpendAckVisible: ko.Computed<boolean>;
|
public throughputSpendAckVisible: ko.Computed<boolean>;
|
||||||
public canExceedMaximumValue: ko.PureComputed<boolean>;
|
public canExceedMaximumValue: ko.PureComputed<boolean>;
|
||||||
public hasAutoPilotV2FeatureFlag: ko.PureComputed<boolean>;
|
|
||||||
public isFreeTierAccount: ko.Computed<boolean>;
|
public isFreeTierAccount: ko.Computed<boolean>;
|
||||||
public ruToolTipText: ko.Computed<string>;
|
public ruToolTipText: ko.Computed<string>;
|
||||||
public canConfigureThroughput: ko.PureComputed<boolean>;
|
public canConfigureThroughput: ko.PureComputed<boolean>;
|
||||||
@@ -61,8 +57,7 @@ export default class CassandraAddCollectionPane extends ContextualPaneBase {
|
|||||||
this.title("Add Table");
|
this.title("Add Table");
|
||||||
this.createTableQuery = ko.observable<string>("CREATE TABLE ");
|
this.createTableQuery = ko.observable<string>("CREATE TABLE ");
|
||||||
this.keyspaceCreateNew = ko.observable<boolean>(true);
|
this.keyspaceCreateNew = ko.observable<boolean>(true);
|
||||||
this.hasAutoPilotV2FeatureFlag = ko.pureComputed(() => this.container.hasAutoPilotV2FeatureFlag());
|
this.ruToolTipText = ko.pureComputed(() => PricingUtils.getRuToolTipText());
|
||||||
this.ruToolTipText = ko.pureComputed(() => PricingUtils.getRuToolTipText(this.hasAutoPilotV2FeatureFlag()));
|
|
||||||
this.canConfigureThroughput = ko.pureComputed(() => !this.container.isServerlessEnabled());
|
this.canConfigureThroughput = ko.pureComputed(() => !this.container.isServerlessEnabled());
|
||||||
this.keyspaceOffers = new HashMap<DataModels.Offer>();
|
this.keyspaceOffers = new HashMap<DataModels.Offer>();
|
||||||
this.keyspaceIds = ko.observableArray<string>();
|
this.keyspaceIds = ko.observableArray<string>();
|
||||||
@@ -90,8 +85,6 @@ export default class CassandraAddCollectionPane extends ContextualPaneBase {
|
|||||||
});
|
});
|
||||||
|
|
||||||
this.tableId = ko.observable<string>("");
|
this.tableId = ko.observable<string>("");
|
||||||
this.selectedAutoPilotTier = ko.observable<DataModels.AutopilotTier>();
|
|
||||||
this.selectedSharedAutoPilotTier = ko.observable<DataModels.AutopilotTier>();
|
|
||||||
this.isAutoPilotSelected = ko.observable<boolean>(false);
|
this.isAutoPilotSelected = ko.observable<boolean>(false);
|
||||||
this.isSharedAutoPilotSelected = ko.observable<boolean>(false);
|
this.isSharedAutoPilotSelected = ko.observable<boolean>(false);
|
||||||
this.selectedAutoPilotThroughput = ko.observable<number>();
|
this.selectedAutoPilotThroughput = ko.observable<number>();
|
||||||
@@ -102,11 +95,11 @@ export default class CassandraAddCollectionPane extends ContextualPaneBase {
|
|||||||
if (!enableAutoPilot) {
|
if (!enableAutoPilot) {
|
||||||
return `Throughput (${this.minThroughputRU().toLocaleString()} - ${this.maxThroughputRU().toLocaleString()} RU/s)`;
|
return `Throughput (${this.minThroughputRU().toLocaleString()} - ${this.maxThroughputRU().toLocaleString()} RU/s)`;
|
||||||
}
|
}
|
||||||
return AutoPilotUtils.getAutoPilotHeaderText(this.hasAutoPilotV2FeatureFlag());
|
return AutoPilotUtils.getAutoPilotHeaderText();
|
||||||
});
|
});
|
||||||
this.sharedThroughputRangeText = ko.pureComputed<string>(() => {
|
this.sharedThroughputRangeText = ko.pureComputed<string>(() => {
|
||||||
if (this.isSharedAutoPilotSelected()) {
|
if (this.isSharedAutoPilotSelected()) {
|
||||||
return AutoPilotUtils.getAutoPilotHeaderText(this.hasAutoPilotV2FeatureFlag());
|
return AutoPilotUtils.getAutoPilotHeaderText();
|
||||||
}
|
}
|
||||||
return `Throughput (${this.minThroughputRU().toLocaleString()} - ${this.maxThroughputRU().toLocaleString()} RU/s)`;
|
return `Throughput (${this.minThroughputRU().toLocaleString()} - ${this.maxThroughputRU().toLocaleString()} RU/s)`;
|
||||||
});
|
});
|
||||||
@@ -246,7 +239,7 @@ export default class CassandraAddCollectionPane extends ContextualPaneBase {
|
|||||||
|
|
||||||
this.sharedThroughputSpendAckVisible = ko.computed<boolean>(() => {
|
this.sharedThroughputSpendAckVisible = ko.computed<boolean>(() => {
|
||||||
const autoscaleThroughput = this.sharedAutoPilotThroughput() * 1;
|
const autoscaleThroughput = this.sharedAutoPilotThroughput() * 1;
|
||||||
if (!this.hasAutoPilotV2FeatureFlag() && this.isSharedAutoPilotSelected()) {
|
if (this.isSharedAutoPilotSelected()) {
|
||||||
return autoscaleThroughput > SharedConstants.CollectionCreation.DefaultCollectionRUs100K;
|
return autoscaleThroughput > SharedConstants.CollectionCreation.DefaultCollectionRUs100K;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -255,7 +248,7 @@ export default class CassandraAddCollectionPane extends ContextualPaneBase {
|
|||||||
|
|
||||||
this.throughputSpendAckVisible = ko.pureComputed<boolean>(() => {
|
this.throughputSpendAckVisible = ko.pureComputed<boolean>(() => {
|
||||||
const autoscaleThroughput = this.selectedAutoPilotThroughput() * 1;
|
const autoscaleThroughput = this.selectedAutoPilotThroughput() * 1;
|
||||||
if (!this.hasAutoPilotV2FeatureFlag() && this.isAutoPilotSelected()) {
|
if (this.isAutoPilotSelected()) {
|
||||||
return autoscaleThroughput > SharedConstants.CollectionCreation.DefaultCollectionRUs100K;
|
return autoscaleThroughput > SharedConstants.CollectionCreation.DefaultCollectionRUs100K;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -280,22 +273,13 @@ export default class CassandraAddCollectionPane extends ContextualPaneBase {
|
|||||||
updateKeyspaceIds(this.container.nonSystemDatabases());
|
updateKeyspaceIds(this.container.nonSystemDatabases());
|
||||||
}
|
}
|
||||||
|
|
||||||
this.autoPilotTiersList = ko.observableArray<ViewModels.DropdownOption<DataModels.AutopilotTier>>(
|
|
||||||
AutoPilotUtils.getAvailableAutoPilotTiersOptions()
|
|
||||||
);
|
|
||||||
this.sharedAutoPilotTiersList = ko.observableArray<ViewModels.DropdownOption<DataModels.AutopilotTier>>(
|
|
||||||
AutoPilotUtils.getAvailableAutoPilotTiersOptions()
|
|
||||||
);
|
|
||||||
|
|
||||||
this.autoPilotUsageCost = ko.pureComputed<string>(() => {
|
this.autoPilotUsageCost = ko.pureComputed<string>(() => {
|
||||||
const autoPilot = this._getAutoPilot();
|
const autoPilot = this._getAutoPilot();
|
||||||
if (!autoPilot) {
|
if (!autoPilot) {
|
||||||
return "";
|
return "";
|
||||||
}
|
}
|
||||||
const isDatabaseThroughput: boolean = this.keyspaceCreateNew();
|
const isDatabaseThroughput: boolean = this.keyspaceCreateNew();
|
||||||
return !this.hasAutoPilotV2FeatureFlag()
|
return PricingUtils.getAutoPilotV3SpendHtml(autoPilot.maxThroughput, isDatabaseThroughput);
|
||||||
? PricingUtils.getAutoPilotV3SpendHtml(autoPilot.maxThroughput, isDatabaseThroughput)
|
|
||||||
: PricingUtils.getAutoPilotV2SpendHtml(autoPilot.autopilotTier, isDatabaseThroughput);
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -352,15 +336,11 @@ export default class CassandraAddCollectionPane extends ContextualPaneBase {
|
|||||||
const autoPilotCommand = `cosmosdb_autoscale_max_throughput`;
|
const autoPilotCommand = `cosmosdb_autoscale_max_throughput`;
|
||||||
let createTableAndKeyspacePromise: Q.Promise<any>;
|
let createTableAndKeyspacePromise: Q.Promise<any>;
|
||||||
const toCreateKeyspace: boolean = this.keyspaceCreateNew();
|
const toCreateKeyspace: boolean = this.keyspaceCreateNew();
|
||||||
const useAutoPilotForKeyspace: boolean =
|
const useAutoPilotForKeyspace: boolean = this.isSharedAutoPilotSelected() && !!this.sharedAutoPilotThroughput();
|
||||||
(!this.hasAutoPilotV2FeatureFlag() && this.isSharedAutoPilotSelected() && !!this.sharedAutoPilotThroughput()) ||
|
|
||||||
(this.hasAutoPilotV2FeatureFlag() && this.isSharedAutoPilotSelected() && !!this.selectedSharedAutoPilotTier());
|
|
||||||
const createKeyspaceQueryPrefix: string = `CREATE KEYSPACE ${this.keyspaceId().trim()} WITH REPLICATION = { 'class' : 'SimpleStrategy', 'replication_factor' : 3 }`;
|
const createKeyspaceQueryPrefix: string = `CREATE KEYSPACE ${this.keyspaceId().trim()} WITH REPLICATION = { 'class' : 'SimpleStrategy', 'replication_factor' : 3 }`;
|
||||||
const createKeyspaceQuery: string = this.keyspaceHasSharedOffer()
|
const createKeyspaceQuery: string = this.keyspaceHasSharedOffer()
|
||||||
? useAutoPilotForKeyspace
|
? useAutoPilotForKeyspace
|
||||||
? !this.hasAutoPilotV2FeatureFlag()
|
? `${createKeyspaceQueryPrefix} AND ${autoPilotCommand}=${this.sharedAutoPilotThroughput()};`
|
||||||
? `${createKeyspaceQueryPrefix} AND ${autoPilotCommand}=${this.sharedAutoPilotThroughput()};`
|
|
||||||
: `${createKeyspaceQueryPrefix} AND ${autoPilotCommand}=${this.selectedSharedAutoPilotTier()};`
|
|
||||||
: `${createKeyspaceQueryPrefix} AND cosmosdb_provisioned_throughput=${this.keyspaceThroughput()};`
|
: `${createKeyspaceQueryPrefix} AND cosmosdb_provisioned_throughput=${this.keyspaceThroughput()};`
|
||||||
: `${createKeyspaceQueryPrefix};`;
|
: `${createKeyspaceQueryPrefix};`;
|
||||||
const createTableQueryPrefix: string = `${this.createTableQuery()}${this.tableId().trim()} ${this.userTableQuery()}`;
|
const createTableQueryPrefix: string = `${this.createTableQuery()}${this.tableId().trim()} ${this.userTableQuery()}`;
|
||||||
@@ -450,8 +430,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,
|
||||||
@@ -477,7 +458,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);
|
||||||
}
|
}
|
||||||
@@ -489,8 +471,6 @@ export default class CassandraAddCollectionPane extends ContextualPaneBase {
|
|||||||
const throughputDefaults = this.container.collectionCreationDefaults.throughput;
|
const throughputDefaults = this.container.collectionCreationDefaults.throughput;
|
||||||
this.isAutoPilotSelected(false);
|
this.isAutoPilotSelected(false);
|
||||||
this.isSharedAutoPilotSelected(false);
|
this.isSharedAutoPilotSelected(false);
|
||||||
this.selectedAutoPilotTier(null);
|
|
||||||
this.selectedSharedAutoPilotTier(null);
|
|
||||||
this.selectedAutoPilotThroughput(AutoPilotUtils.minAutoPilotThroughput);
|
this.selectedAutoPilotThroughput(AutoPilotUtils.minAutoPilotThroughput);
|
||||||
this.sharedAutoPilotThroughput(AutoPilotUtils.minAutoPilotThroughput);
|
this.sharedAutoPilotThroughput(AutoPilotUtils.minAutoPilotThroughput);
|
||||||
this.throughput(AddCollectionUtility.getMaxThroughput(this.container.collectionCreationDefaults, this.container));
|
this.throughput(AddCollectionUtility.getMaxThroughput(this.container.collectionCreationDefaults, this.container));
|
||||||
@@ -512,7 +492,6 @@ export default class CassandraAddCollectionPane extends ContextualPaneBase {
|
|||||||
|
|
||||||
const sharedAutoscaleThroughput = this.sharedAutoPilotThroughput() * 1;
|
const sharedAutoscaleThroughput = this.sharedAutoPilotThroughput() * 1;
|
||||||
if (
|
if (
|
||||||
!this.hasAutoPilotV2FeatureFlag() &&
|
|
||||||
this.isSharedAutoPilotSelected() &&
|
this.isSharedAutoPilotSelected() &&
|
||||||
sharedAutoscaleThroughput > SharedConstants.CollectionCreation.DefaultCollectionRUs100K &&
|
sharedAutoscaleThroughput > SharedConstants.CollectionCreation.DefaultCollectionRUs100K &&
|
||||||
!this.sharedThroughputSpendAck()
|
!this.sharedThroughputSpendAck()
|
||||||
@@ -523,7 +502,6 @@ export default class CassandraAddCollectionPane extends ContextualPaneBase {
|
|||||||
|
|
||||||
const dedicatedAutoscaleThroughput = this.selectedAutoPilotThroughput() * 1;
|
const dedicatedAutoscaleThroughput = this.selectedAutoPilotThroughput() * 1;
|
||||||
if (
|
if (
|
||||||
!this.hasAutoPilotV2FeatureFlag() &&
|
|
||||||
this.isAutoPilotSelected() &&
|
this.isAutoPilotSelected() &&
|
||||||
dedicatedAutoscaleThroughput > SharedConstants.CollectionCreation.DefaultCollectionRUs100K &&
|
dedicatedAutoscaleThroughput > SharedConstants.CollectionCreation.DefaultCollectionRUs100K &&
|
||||||
!this.throughputSpendAck()
|
!this.throughputSpendAck()
|
||||||
@@ -538,17 +516,12 @@ export default class CassandraAddCollectionPane extends ContextualPaneBase {
|
|||||||
) {
|
) {
|
||||||
const autoPilot = this._getAutoPilot();
|
const autoPilot = this._getAutoPilot();
|
||||||
if (
|
if (
|
||||||
(!this.hasAutoPilotV2FeatureFlag() &&
|
!autoPilot ||
|
||||||
(!autoPilot ||
|
!autoPilot.maxThroughput ||
|
||||||
!autoPilot.maxThroughput ||
|
!AutoPilotUtils.isValidAutoPilotThroughput(autoPilot.maxThroughput)
|
||||||
!AutoPilotUtils.isValidAutoPilotThroughput(autoPilot.maxThroughput))) ||
|
|
||||||
(this.hasAutoPilotV2FeatureFlag() &&
|
|
||||||
(!autoPilot || !autoPilot.autopilotTier || !AutoPilotUtils.isValidAutoPilotTier(autoPilot.autopilotTier)))
|
|
||||||
) {
|
) {
|
||||||
this.formErrors(
|
this.formErrors(
|
||||||
!this.hasAutoPilotV2FeatureFlag()
|
`Please enter a value greater than ${AutoPilotUtils.minAutoPilotThroughput} for autopilot throughput`
|
||||||
? `Please enter a value greater than ${AutoPilotUtils.minAutoPilotThroughput} for autopilot throughput`
|
|
||||||
: "Please select an Autopilot tier from the list."
|
|
||||||
);
|
);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@@ -575,33 +548,20 @@ export default class CassandraAddCollectionPane extends ContextualPaneBase {
|
|||||||
|
|
||||||
private _getAutoPilot(): DataModels.AutoPilotCreationSettings {
|
private _getAutoPilot(): DataModels.AutoPilotCreationSettings {
|
||||||
if (
|
if (
|
||||||
(!this.hasAutoPilotV2FeatureFlag() &&
|
this.keyspaceCreateNew() &&
|
||||||
this.keyspaceCreateNew() &&
|
this.keyspaceHasSharedOffer() &&
|
||||||
this.keyspaceHasSharedOffer() &&
|
this.isSharedAutoPilotSelected() &&
|
||||||
this.isSharedAutoPilotSelected() &&
|
this.sharedAutoPilotThroughput()
|
||||||
this.sharedAutoPilotThroughput()) ||
|
|
||||||
(this.hasAutoPilotV2FeatureFlag() &&
|
|
||||||
this.keyspaceCreateNew() &&
|
|
||||||
this.keyspaceHasSharedOffer() &&
|
|
||||||
this.isSharedAutoPilotSelected() &&
|
|
||||||
this.selectedSharedAutoPilotTier())
|
|
||||||
) {
|
) {
|
||||||
return !this.hasAutoPilotV2FeatureFlag()
|
return {
|
||||||
? {
|
maxThroughput: this.sharedAutoPilotThroughput() * 1
|
||||||
maxThroughput: this.sharedAutoPilotThroughput() * 1
|
};
|
||||||
}
|
|
||||||
: { autopilotTier: this.selectedSharedAutoPilotTier() };
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (
|
if (this.selectedAutoPilotThroughput()) {
|
||||||
(!this.hasAutoPilotV2FeatureFlag() && this.selectedAutoPilotThroughput()) ||
|
return {
|
||||||
(this.hasAutoPilotV2FeatureFlag() && this.selectedAutoPilotTier())
|
maxThroughput: this.selectedAutoPilotThroughput() * 1
|
||||||
) {
|
};
|
||||||
return !this.hasAutoPilotV2FeatureFlag()
|
|
||||||
? {
|
|
||||||
maxThroughput: this.selectedAutoPilotThroughput() * 1
|
|
||||||
}
|
|
||||||
: { autopilotTier: this.selectedAutoPilotTier() };
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return undefined;
|
return undefined;
|
||||||
|
|||||||
@@ -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
|
||||||
);
|
);
|
||||||
|
|||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user