Compare commits

..

16 Commits

Author SHA1 Message Date
Steve Faulkner
4be1389705 Upload on failure 2020-12-17 17:01:28 -06:00
Steve Faulkner
65844414dd Test 2020-12-16 20:18:59 -06:00
Steve Faulkner
b9461de695 Test 2020-12-16 20:18:53 -06:00
Steve Faulkner
0473c49cc6 Merge branch 'e2e-test-debugging' of https://github.com/Azure/cosmos-explorer into e2e-test-debugging 2020-12-16 20:18:39 -06:00
Steve Faulkner
53ea8dc528 Test 2020-12-16 20:18:31 -06:00
Steve Faulkner
8b7d43823a Merge branch 'master' into e2e-test-debugging 2020-12-16 20:01:37 -06:00
Steve Faulkner
dfb1b50621 Explorer.ts Cleanup (#341)
Co-authored-by: victor-meng <56978073+victor-meng@users.noreply.github.com>
2020-12-16 20:00:39 -06:00
Steve Faulkner
3ab7e93bab Test 2020-12-16 19:56:40 -06:00
Steve Faulkner
9e2efa01e5 Test 2020-12-16 19:37:55 -06:00
Steve Faulkner
ba5ab37bac Debugging failed tests 2020-12-16 19:22:47 -06:00
victor-meng
f54e8eb692 Move queryDocuments out of DataAccessUtility (#334) 2020-12-16 15:27:17 -08:00
Steve Faulkner
ea39c1d092 Fix offer update notification for AAD users (#338) 2020-12-11 13:38:57 -06:00
vchske
c21f42159f Updated cost messaging for new db/container pane (#333)
* Adds information text further explaining estimated cost.

* Minor tweak to cost messaging

* Updated unit tests

* Added text and link for capacity planner when choosing manual RUs
2020-12-11 10:06:43 -08:00
victor-meng
31e4b49f11 Only call getCollectionDataUsageSize for AAD users (#337) 2020-12-10 14:13:08 -08:00
Tanuj Mittal
40491ec9c5 Gallery related fixes (#312)
* AVERT fixes

* Remove enableCodeOfConduct feature flag

* Fix reporting abuse

* Add empty screen for Liked and Published tabs in Gallery

* fix build

* Remove unused code

* Fix standalone public gallery
2020-12-10 13:09:18 -08:00
Tanuj Mittal
e133df18dd Record baseUrl for OpenTerminal success/failure telemetry (#335)
This is useful to know which terminal is opening.
2020-12-10 19:54:21 +00:00
501 changed files with 43854 additions and 44291 deletions

View File

@@ -14,7 +14,6 @@ src/Common/DataAccessUtilityBase.ts
src/Common/DeleteFeedback.ts
src/Common/DocumentClientUtilityBase.ts
src/Common/EditableUtility.ts
src/Common/EnvironmentUtility.ts
src/Common/HashMap.test.ts
src/Common/HashMap.ts
src/Common/HeadersUtility.test.ts

View File

@@ -1,39 +1,39 @@
module.exports = {
env: {
browser: true,
es6: true,
es6: true
},
plugins: ["@typescript-eslint", "no-null", "prefer-arrow"],
extends: ["eslint:recommended", "plugin:@typescript-eslint/recommended"],
globals: {
Atomics: "readonly",
SharedArrayBuffer: "readonly",
SharedArrayBuffer: "readonly"
},
parser: "@typescript-eslint/parser",
parserOptions: {
ecmaFeatures: {
jsx: true,
jsx: true
},
ecmaVersion: 2018,
sourceType: "module",
sourceType: "module"
},
overrides: [
{
files: ["**/*.tsx"],
env: {
jest: true,
jest: true
},
extends: ["plugin:react/recommended"],
plugins: ["react"],
plugins: ["react"]
},
{
files: ["**/*.{test,spec}.{ts,tsx}"],
env: {
jest: true,
jest: true
},
extends: ["plugin:jest/recommended"],
plugins: ["jest"],
},
plugins: ["jest"]
}
],
rules: {
curly: "error",
@@ -47,8 +47,8 @@ module.exports = {
"error",
{
selector: "CallExpression[callee.object.name='JSON'][callee.property.name='stringify'] Identifier[name=/$err/]",
message: "Do not use JSON.stringify(error). It will print '{}'",
},
],
},
message: "Do not use JSON.stringify(error). It will print '{}'"
}
]
}
};

View File

@@ -101,6 +101,7 @@ jobs:
PLATFORM: "Emulator"
NODE_TLS_REJECT_UNAUTHORIZED: 0
- uses: actions/upload-artifact@v2
if: failure()
with:
name: screenshots
path: failed-*
@@ -159,6 +160,7 @@ jobs:
TABLES_CONNECTION_STRING: ${{ secrets.CONNECTION_STRING_TABLE }}
DATA_EXPLORER_ENDPOINT: "https://localhost:1234/hostedExplorer.html"
- uses: actions/upload-artifact@v2
if: failure()
with:
name: screenshots
path: failed-*

View File

@@ -13,29 +13,18 @@ UI for Azure Cosmos DB. Powers the [Azure Portal](https://portal.azure.com/), ht
### Watch mode
Run `npm run watch` to start the development server and automatically rebuild on changes
Run `npm start` to start the development server and automatically rebuild on changes
### Specifying Development Platform
### Hosted Development (https://cosmos.azure.com)
Setting the environment variable `PLATFORM` during the build process will force the explorer to load the specified platform. By default in development it will run in `Hosted` mode. Valid options:
- Hosted
- Emulator
- Portal
`PLATFORM=Emulator npm run watch`
### Hosted Development
The default webpack dev server configuration will proxy requests to the production portal backend: `https://main.documentdb.ext.azure.com`. This will allow you to use production connection strings on your local machine.
To run pure hosted mode, in `webpack.config.js` change index HtmlWebpackPlugin to use hostedExplorer.html and change entry for index to use HostedExplorer.ts.
- Visit: `https://localhost:1234/hostedExplorer.html`
- Local sign in via AAD will NOT work. Connection string only in dev mode. Use the Portal if you need AAD auth.
- The default webpack dev server configuration will proxy requests to the production portal backend: `https://main.documentdb.ext.azure.com`. This will allow you to use production connection strings on your local machine.
### 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 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`
- Start the Cosmos Emulator
- Visit: https://localhost:1234/index.html
#### Setting up a Remote Emulator
@@ -55,16 +44,8 @@ The Cosmos emulator currently only runs in Windows environments. You can still d
### Portal Development
The Cosmos Portal that consumes this repo is not currently open source. If you have access to this project, `npm run build` will copy the built files over to the portal where they will be loaded by the portal development environment
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)
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`
4. Load the portal using the following link: https://ms.portal.azure.com/?dataExplorerSource=https%3A%2F%2Flocalhost%3A1234%2Fexplorer.html
Live reload will occur, but data explorer will not properly integrate again with the parent iframe. You will have to manually reload the page.
- Visit: https://ms.portal.azure.com/?dataExplorerSource=https%3A%2F%2Flocalhost%3A1234%2Fexplorer.html
- You may have to manually visit https://localhost:1234/explorer.html first and click through any SSL certificate warnings
### Testing

View File

@@ -1,3 +1,3 @@
module.exports = {
presets: [["@babel/preset-env", { targets: { node: "current" } }], "@babel/preset-react", "@babel/preset-typescript"],
presets: [["@babel/preset-env", { targets: { node: "current" } }], "@babel/preset-react", "@babel/preset-typescript"]
};

View File

@@ -6,6 +6,6 @@ module.exports = {
slowMo: 55,
defaultViewport: null,
ignoreHTTPSErrors: true,
args: ["--disable-web-security"],
},
args: ["--disable-web-security"]
}
};

View File

@@ -1,5 +1,5 @@
module.exports = {
preset: "jest-puppeteer",
testMatch: ["<rootDir>/test/**/*.spec.[jt]s?(x)"],
setupFiles: ["dotenv/config"],
setupFiles: ["dotenv/config"]
};

View File

@@ -42,8 +42,8 @@ module.exports = {
branches: 20,
functions: 24,
lines: 30,
statements: 29.0,
},
statements: 29.0
}
},
// Make calling deprecated APIs throw helpful error messages
@@ -76,7 +76,7 @@ module.exports = {
"office-ui-fabric-react/lib/(.*)$": "office-ui-fabric-react/lib-commonjs/$1", // https://github.com/OfficeDev/office-ui-fabric-react/wiki/Fabric-6-Release-Notes
"^dnd-core$": "dnd-core/dist/cjs",
"^react-dnd$": "react-dnd/dist/cjs",
"^react-dnd-html5-backend$": "react-dnd-html5-backend/dist/cjs",
"^react-dnd-html5-backend$": "react-dnd-html5-backend/dist/cjs"
},
// An array of regexp pattern strings, matched against all module paths before considered 'visible' to the module loader
@@ -164,11 +164,11 @@ module.exports = {
// A map from regular expressions to paths to transformers
transform: {
"^.+\\.html?$": "html-loader-jest",
"^.+\\.[t|j]sx?$": "babel-jest",
"^.+\\.[t|j]sx?$": "babel-jest"
},
// An array of regexp pattern strings that are matched against all source file paths, matched files will skip transformation
transformIgnorePatterns: ["/node_modules/", "/externals/"],
transformIgnorePatterns: ["/node_modules/", "/externals/"]
// An array of regexp pattern strings that are matched against all modules before the module loader will automatically return a mock for them
// unmockedModulePathPatterns: undefined,

12
package-lock.json generated
View File

@@ -17265,9 +17265,9 @@
"integrity": "sha1-IZMqVJ9eUv/ZqCf1cOBL5iqX2lQ="
},
"prettier": {
"version": "2.2.1",
"resolved": "https://registry.npmjs.org/prettier/-/prettier-2.2.1.tgz",
"integrity": "sha512-PqyhM2yCjg/oKkFPtTGUojv7gnZAoG80ttl45O6x2Ug/rMJw4wcc9k6aaf2hibP7BGVCCM33gZoGjyvt9mm16Q==",
"version": "1.19.1",
"resolved": "https://registry.npmjs.org/prettier/-/prettier-1.19.1.tgz",
"integrity": "sha512-s7PoyDv/II1ObgQunCbB9PdLmUcBZcnWOcxDh7O0N/UwDEsHyqkW+Qh28jW+mVuCdx7gLB0BotYI1Y6uI9iyew==",
"dev": true
},
"pretty-error": {
@@ -20728,9 +20728,9 @@
}
},
"typescript": {
"version": "4.1.2",
"resolved": "https://registry.npmjs.org/typescript/-/typescript-4.1.2.tgz",
"integrity": "sha512-thGloWsGH3SOxv1SoY7QojKi0tc+8FnOmiarEGMbd/lar7QOEd3hvlx3Fp5y6FlDUGl9L+pd4n2e+oToGMmhRQ==",
"version": "4.0.2",
"resolved": "https://registry.npmjs.org/typescript/-/typescript-4.0.2.tgz",
"integrity": "sha512-e4ERvRV2wb+rRZ/IQeb3jm2VxBsirQLpQhdxplZ2MEzGvDkkMmPglecnNDfSUBivMjP93vRbngYYDQqQ/78bcQ==",
"dev": true
},
"typestyle": {

View File

@@ -160,7 +160,7 @@
"mini-css-extract-plugin": "0.4.3",
"monaco-editor-webpack-plugin": "1.7.0",
"node-fetch": "2.6.1",
"prettier": "2.2.1",
"prettier": "1.19.1",
"puppeteer": "4.0.0",
"raw-loader": "0.5.1",
"rimraf": "3.0.0",
@@ -170,7 +170,7 @@
"ts-loader": "6.2.2",
"tslint": "5.11.0",
"tslint-microsoft-contrib": "6.0.0",
"typescript": "4.1.2",
"typescript": "4.0.2",
"url-loader": "1.1.1",
"wait-on": "4.0.2",
"webpack": "4.43.0",

View File

@@ -2,5 +2,5 @@ export enum AuthType {
AAD = "aad",
EncryptedToken = "encryptedtoken",
MasterKey = "masterkey",
ResourceToken = "resourcetoken",
ResourceToken = "resourcetoken"
}

View File

@@ -13,7 +13,7 @@ export class BindingHandlersRegisterer {
) {
const value = ko.unwrap(wrappedValueAccessor());
bindingContext?.$data.isTemplateReady(value);
},
}
} as ko.BindingHandler;
ReactBindingHandler.Registerer.register();

View File

@@ -42,7 +42,7 @@ export class Registerer {
// Initial rendering at mount point
ReactDOM.render(adapter.renderComponent(), element);
},
}
} as ko.BindingHandler;
}
}

View File

@@ -40,7 +40,7 @@ export class ArrayHashMap<T> {
public forEach(key: string, iteratorFct: (value: T) => void) {
const values = this.store.get(key);
if (values) {
values.forEach((value) => iteratorFct(value));
values.forEach(value => iteratorFct(value));
}
}

View File

@@ -14,7 +14,7 @@ export class CodeOfConductEndpoints {
export class EndpointsRegex {
public static readonly cassandra = [
"AccountEndpoint=(.*).cassandra.cosmosdb.azure.com",
"HostName=(.*).cassandra.cosmos.azure.com",
"HostName=(.*).cassandra.cosmos.azure.com"
];
public static readonly mongo = "mongodb://.*:(.*)@(.*).documents.azure.com";
public static readonly mongoCompute = "mongodb://.*:(.*)@(.*).mongo.cosmos.azure.com";
@@ -113,7 +113,6 @@ export class Features {
public static readonly enableTtl = "enablettl";
public static readonly enableNotebooks = "enablenotebooks";
public static readonly enableGalleryPublish = "enablegallerypublish";
public static readonly enableCodeOfConduct = "enablecodeofconduct";
public static readonly enableLinkInjection = "enablelinkinjection";
public static readonly enableSpark = "enablespark";
public static readonly livyEndpoint = "livyendpoint";
@@ -150,7 +149,7 @@ export class Spark {
"Cosmos.Spark.D8s": "D8s / 8 cores / 32GB RAM",
"Cosmos.Spark.D16s": "D16s / 16 cores / 64GB RAM",
"Cosmos.Spark.D32s": "D32s / 32 cores / 128GB RAM",
"Cosmos.Spark.D64s": "D64s / 64 cores / 256GB RAM",
"Cosmos.Spark.D64s": "D64s / 64 cores / 256GB RAM"
});
}
@@ -165,7 +164,7 @@ export class MongoDBAccounts {
export enum MongoBackendEndpointType {
local,
remote,
remote
}
// TODO: 435619 Add default endpoints per cloud and use regional only when available
@@ -292,7 +291,7 @@ export class HttpStatusCodes {
HttpStatusCodes.InternalServerError, // TODO: Handle all 500s on Portal backend and remove from retries list
HttpStatusCodes.BadGateway,
HttpStatusCodes.ServiceUnavailable,
HttpStatusCodes.GatewayTimeout,
HttpStatusCodes.GatewayTimeout
];
}
@@ -348,7 +347,10 @@ export class HashRoutePrefixes {
public static docsWithIds(databaseId: string, collectionId: string, docId: string) {
const transformedDatabasePrefix: string = this.docs.replace("{db_id}", databaseId);
return transformedDatabasePrefix.replace("{coll_id}", collectionId).replace("{doc_id}", docId).replace("/", ""); // strip the first slash since hasher adds it
return transformedDatabasePrefix
.replace("{coll_id}", collectionId)
.replace("{doc_id}", docId)
.replace("/", ""); // strip the first slash since hasher adds it
}
}
@@ -394,7 +396,7 @@ export class OfferVersions {
export enum ConflictOperationType {
Replace = "replace",
Create = "create",
Delete = "delete",
Delete = "delete"
}
export const EmulatorMasterKey =

View File

@@ -10,17 +10,17 @@ describe("tokenProvider", () => {
resourceId: "",
resourceType: "dbs" as ResourceType,
headers: {},
getAuthorizationTokenUsingMasterKey: () => "",
getAuthorizationTokenUsingMasterKey: () => ""
};
beforeEach(() => {
updateConfigContext({
BACKEND_ENDPOINT: "https://main.documentdb.ext.azure.com",
BACKEND_ENDPOINT: "https://main.documentdb.ext.azure.com"
});
window.fetch = jest.fn().mockImplementation(() => {
return {
json: () => "{}",
headers: new Map(),
headers: new Map()
};
});
});
@@ -36,7 +36,7 @@ describe("tokenProvider", () => {
it("does not call the auth service if a master key is set", async () => {
updateUserContext({
masterKey: "foo",
masterKey: "foo"
});
await tokenProvider(options);
expect((window.fetch as any).mock.calls.length).toBe(0);
@@ -50,7 +50,7 @@ describe("getTokenFromAuthService", () => {
window.fetch = jest.fn().mockImplementation(() => {
return {
json: () => "{}",
headers: new Map(),
headers: new Map()
};
});
});
@@ -61,7 +61,7 @@ describe("getTokenFromAuthService", () => {
it("builds the correct URL in production", () => {
updateConfigContext({
BACKEND_ENDPOINT: "https://main.documentdb.ext.azure.com",
BACKEND_ENDPOINT: "https://main.documentdb.ext.azure.com"
});
getTokenFromAuthService("GET", "dbs", "foo");
expect(window.fetch).toHaveBeenCalledWith(
@@ -72,7 +72,7 @@ describe("getTokenFromAuthService", () => {
it("builds the correct URL in dev", () => {
updateConfigContext({
BACKEND_ENDPOINT: "https://localhost:1234",
BACKEND_ENDPOINT: "https://localhost:1234"
});
getTokenFromAuthService("GET", "dbs", "foo");
expect(window.fetch).toHaveBeenCalledWith(
@@ -96,15 +96,15 @@ describe("endpoint", () => {
documentEndpoint: "bar",
gremlinEndpoint: "foo",
tableEndpoint: "foo",
cassandraEndpoint: "foo",
},
},
cassandraEndpoint: "foo"
}
}
});
expect(endpoint()).toEqual("bar");
});
it("uses _endpoint if set", () => {
updateUserContext({
endpoint: "baz",
endpoint: "baz"
});
expect(endpoint()).toEqual("baz");
});
@@ -121,7 +121,7 @@ describe("requestPlugin", () => {
updateConfigContext({
platform: Platform.Hosted,
BACKEND_ENDPOINT: "https://localhost:1234",
PROXY_PATH: "/proxy",
PROXY_PATH: "/proxy"
});
const headers = {};
const endpoint = "https://docs.azure.com";

View File

@@ -58,13 +58,13 @@ export async function getTokenFromAuthService(verb: string, resourceType: string
method: "POST",
headers: {
"content-type": "application/json",
"x-ms-encrypted-auth-token": userContext.accessToken,
"x-ms-encrypted-auth-token": userContext.accessToken
},
body: JSON.stringify({
verb,
resourceType,
resourceId,
}),
resourceId
})
});
//TODO I am not sure why we have to parse the JSON again here. fetch should do it for us when we call .json()
const result = JSON.parse(await response.json());
@@ -81,9 +81,9 @@ export function client(): Cosmos.CosmosClient {
key: userContext.masterKey,
tokenProvider,
connectionPolicy: {
enableEndpointDiscovery: false,
enableEndpointDiscovery: false
},
userAgentSuffix: "Azure Portal",
userAgentSuffix: "Azure Portal"
};
if (configContext.PROXY_PATH !== undefined) {

View File

@@ -1,155 +0,0 @@
import { ConflictDefinition, FeedOptions, ItemDefinition, QueryIterator, Resource } from "@azure/cosmos";
import Q from "q";
import * as DataModels from "../Contracts/DataModels";
import * as ViewModels from "../Contracts/ViewModels";
import ConflictId from "../Explorer/Tree/ConflictId";
import DocumentId from "../Explorer/Tree/DocumentId";
import StoredProcedure from "../Explorer/Tree/StoredProcedure";
import { LocalStorageUtility, StorageKey } from "../Shared/StorageUtility";
import * as Constants from "./Constants";
import { client } from "./CosmosClient";
export function getCommonQueryOptions(options: FeedOptions): any {
const storedItemPerPageSetting: number = LocalStorageUtility.getEntryNumber(StorageKey.ActualItemPerPage);
options = options || {};
options.populateQueryMetrics = true;
options.enableScanInQuery = options.enableScanInQuery || true;
if (!options.partitionKey) {
options.forceQueryPlan = true;
}
options.maxItemCount =
options.maxItemCount ||
(storedItemPerPageSetting !== undefined && storedItemPerPageSetting) ||
Constants.Queries.itemsPerPage;
options.maxDegreeOfParallelism = LocalStorageUtility.getEntryNumber(StorageKey.MaxDegreeOfParellism);
return options;
}
export function queryDocuments(
databaseId: string,
containerId: string,
query: string,
options: any
): Q.Promise<QueryIterator<ItemDefinition & Resource>> {
options = getCommonQueryOptions(options);
const documentsIterator = client().database(databaseId).container(containerId).items.query(query, options);
return Q(documentsIterator);
}
export function getPartitionKeyHeaderForConflict(conflictId: ConflictId): Object {
const partitionKeyDefinition: DataModels.PartitionKey = conflictId.partitionKey;
const partitionKeyValue: any = conflictId.partitionKeyValue;
return getPartitionKeyHeader(partitionKeyDefinition, partitionKeyValue);
}
export function getPartitionKeyHeader(partitionKeyDefinition: DataModels.PartitionKey, partitionKeyValue: any): Object {
if (!partitionKeyDefinition) {
return undefined;
}
if (partitionKeyValue === undefined) {
return [{}];
}
return [partitionKeyValue];
}
export function updateDocument(
collection: ViewModels.CollectionBase,
documentId: DocumentId,
newDocument: any
): Q.Promise<any> {
const partitionKey = documentId.partitionKeyValue;
return Q(
client()
.database(collection.databaseId)
.container(collection.id())
.item(documentId.id(), partitionKey)
.replace(newDocument)
.then((response) => response.resource)
);
}
export function executeStoredProcedure(
collection: ViewModels.Collection,
storedProcedure: StoredProcedure,
partitionKeyValue: any,
params: any[]
): Q.Promise<any> {
// TODO remove this deferred. Kept it because of timeout code at bottom of function
const deferred = Q.defer<any>();
client()
.database(collection.databaseId)
.container(collection.id())
.scripts.storedProcedure(storedProcedure.id())
.execute(partitionKeyValue, params, { enableScriptLogging: true })
.then((response) =>
deferred.resolve({
result: response.resource,
scriptLogs: response.headers[Constants.HttpHeaders.scriptLogResults],
})
)
.catch((error) => deferred.reject(error));
return deferred.promise.timeout(
Constants.ClientDefaults.requestTimeoutMs,
`Request timed out while executing stored procedure ${storedProcedure.id()}`
);
}
export function createDocument(collection: ViewModels.CollectionBase, newDocument: any): Q.Promise<any> {
return Q(
client()
.database(collection.databaseId)
.container(collection.id())
.items.create(newDocument)
.then((response) => response.resource)
);
}
export function readDocument(collection: ViewModels.CollectionBase, documentId: DocumentId): Q.Promise<any> {
const partitionKey = documentId.partitionKeyValue;
return Q(
client()
.database(collection.databaseId)
.container(collection.id())
.item(documentId.id(), partitionKey)
.read()
.then((response) => response.resource)
);
}
export function deleteDocument(collection: ViewModels.CollectionBase, documentId: DocumentId): Q.Promise<any> {
const partitionKey = documentId.partitionKeyValue;
return Q(
client().database(collection.databaseId).container(collection.id()).item(documentId.id(), partitionKey).delete()
);
}
export function deleteConflict(
collection: ViewModels.CollectionBase,
conflictId: ConflictId,
options: any = {}
): Q.Promise<any> {
options.partitionKey = options.partitionKey || getPartitionKeyHeaderForConflict(conflictId);
return Q(
client().database(collection.databaseId).container(collection.id()).conflict(conflictId.id()).delete(options)
);
}
export function queryConflicts(
databaseId: string,
containerId: string,
query: string,
options: any
): Q.Promise<QueryIterator<ConflictDefinition & Resource>> {
const documentsIterator = client().database(databaseId).container(containerId).conflicts.query(query, options);
return Q(documentsIterator);
}

View File

@@ -1,217 +0,0 @@
import { ConflictDefinition, ItemDefinition, QueryIterator, Resource } from "@azure/cosmos";
import Q from "q";
import * as ViewModels from "../Contracts/ViewModels";
import ConflictId from "../Explorer/Tree/ConflictId";
import DocumentId from "../Explorer/Tree/DocumentId";
import StoredProcedure from "../Explorer/Tree/StoredProcedure";
import { logConsoleInfo, logConsoleProgress } from "../Utils/NotificationConsoleUtils";
import * as Constants from "./Constants";
import * as DataAccessUtilityBase from "./DataAccessUtilityBase";
import { MinimalQueryIterator, nextPage } from "./IteratorUtilities";
import { handleError } from "./ErrorHandlingUtils";
// TODO: Log all promise resolutions and errors with verbosity levels
export function queryDocuments(
databaseId: string,
containerId: string,
query: string,
options: any
): Q.Promise<QueryIterator<ItemDefinition & Resource>> {
return DataAccessUtilityBase.queryDocuments(databaseId, containerId, query, options);
}
export function queryConflicts(
databaseId: string,
containerId: string,
query: string,
options: any
): Q.Promise<QueryIterator<ConflictDefinition & Resource>> {
return DataAccessUtilityBase.queryConflicts(databaseId, containerId, query, options);
}
export function getEntityName() {
const defaultExperience =
window.dataExplorer && window.dataExplorer.defaultExperience && window.dataExplorer.defaultExperience();
if (defaultExperience === Constants.DefaultAccountExperience.MongoDB) {
return "document";
}
return "item";
}
export function executeStoredProcedure(
collection: ViewModels.Collection,
storedProcedure: StoredProcedure,
partitionKeyValue: any,
params: any[]
): Q.Promise<any> {
var deferred = Q.defer<any>();
const clearMessage = logConsoleProgress(`Executing stored procedure ${storedProcedure.id()}`);
DataAccessUtilityBase.executeStoredProcedure(collection, storedProcedure, partitionKeyValue, params)
.then(
(response: any) => {
deferred.resolve(response);
logConsoleInfo(
`Finished executing stored procedure ${storedProcedure.id()} for container ${storedProcedure.collection.id()}`
);
},
(error: any) => {
handleError(
error,
"ExecuteStoredProcedure",
`Failed to execute stored procedure ${storedProcedure.id()} for container ${storedProcedure.collection.id()}`
);
deferred.reject(error);
}
)
.finally(() => {
clearMessage();
});
return deferred.promise;
}
export function queryDocumentsPage(
resourceName: string,
documentsIterator: MinimalQueryIterator,
firstItemIndex: number,
options: any
): Q.Promise<ViewModels.QueryResults> {
var deferred = Q.defer<ViewModels.QueryResults>();
const entityName = getEntityName();
const clearMessage = logConsoleProgress(`Querying ${entityName} for container ${resourceName}`);
Q(nextPage(documentsIterator, firstItemIndex))
.then(
(result: ViewModels.QueryResults) => {
const itemCount = (result.documents && result.documents.length) || 0;
logConsoleInfo(`Successfully fetched ${itemCount} ${entityName} for container ${resourceName}`);
deferred.resolve(result);
},
(error: any) => {
handleError(error, "QueryDocumentsPage", `Failed to query ${entityName} for container ${resourceName}`);
deferred.reject(error);
}
)
.finally(() => {
clearMessage();
});
return deferred.promise;
}
export function readDocument(collection: ViewModels.CollectionBase, documentId: DocumentId): Q.Promise<any> {
var deferred = Q.defer<any>();
const entityName = getEntityName();
const clearMessage = logConsoleProgress(`Reading ${entityName} ${documentId.id()}`);
DataAccessUtilityBase.readDocument(collection, documentId)
.then(
(document: any) => {
deferred.resolve(document);
},
(error: any) => {
handleError(error, "ReadDocument", `Failed to read ${entityName} ${documentId.id()}`);
deferred.reject(error);
}
)
.finally(() => {
clearMessage();
});
return deferred.promise;
}
export function updateDocument(
collection: ViewModels.CollectionBase,
documentId: DocumentId,
newDocument: any
): Q.Promise<any> {
var deferred = Q.defer<any>();
const entityName = getEntityName();
const clearMessage = logConsoleProgress(`Updating ${entityName} ${documentId.id()}`);
DataAccessUtilityBase.updateDocument(collection, documentId, newDocument)
.then(
(updatedDocument: any) => {
logConsoleInfo(`Successfully updated ${entityName} ${documentId.id()}`);
deferred.resolve(updatedDocument);
},
(error: any) => {
handleError(error, "UpdateDocument", `Failed to update ${entityName} ${documentId.id()}`);
deferred.reject(error);
}
)
.finally(() => {
clearMessage();
});
return deferred.promise;
}
export function createDocument(collection: ViewModels.CollectionBase, newDocument: any): Q.Promise<any> {
var deferred = Q.defer<any>();
const entityName = getEntityName();
const clearMessage = logConsoleProgress(`Creating new ${entityName} for container ${collection.id()}`);
DataAccessUtilityBase.createDocument(collection, newDocument)
.then(
(savedDocument: any) => {
logConsoleInfo(`Successfully created new ${entityName} for container ${collection.id()}`);
deferred.resolve(savedDocument);
},
(error: any) => {
handleError(error, "CreateDocument", `Error while creating new ${entityName} for container ${collection.id()}`);
deferred.reject(error);
}
)
.finally(() => {
clearMessage();
});
return deferred.promise;
}
export function deleteDocument(collection: ViewModels.CollectionBase, documentId: DocumentId): Q.Promise<any> {
var deferred = Q.defer<any>();
const entityName = getEntityName();
const clearMessage = logConsoleProgress(`Deleting ${entityName} ${documentId.id()}`);
DataAccessUtilityBase.deleteDocument(collection, documentId)
.then(
(response: any) => {
logConsoleInfo(`Successfully deleted ${entityName} ${documentId.id()}`);
deferred.resolve(response);
},
(error: any) => {
handleError(error, "DeleteDocument", `Error while deleting ${entityName} ${documentId.id()}`);
deferred.reject(error);
}
)
.finally(() => {
clearMessage();
});
return deferred.promise;
}
export function deleteConflict(
collection: ViewModels.CollectionBase,
conflictId: ConflictId,
options?: any
): Q.Promise<any> {
var deferred = Q.defer<any>();
const clearMessage = logConsoleProgress(`Deleting conflict ${conflictId.id()}`);
DataAccessUtilityBase.deleteConflict(collection, conflictId, options)
.then(
(response: any) => {
logConsoleInfo(`Successfully deleted conflict ${conflictId.id()}`);
deferred.resolve(response);
},
(error: any) => {
handleError(error, "DeleteConflict", `Error while deleting conflict ${conflictId.id()}`);
deferred.reject(error);
}
)
.finally(() => {
clearMessage();
});
return deferred.promise;
}

View File

@@ -0,0 +1,10 @@
import { DefaultAccountExperienceType } from "../DefaultAccountExperienceType";
import { userContext } from "../UserContext";
export const getEntityName = (): string => {
if (userContext.defaultExperience === DefaultAccountExperienceType.MongoDB) {
return "document";
}
return "item";
};

View File

@@ -73,7 +73,7 @@ export default class EditableUtility {
return false;
});
observable.subscribe((edit) => {
observable.subscribe(edit => {
var edits = observable.edits && observable.edits();
if (!edits) {
return;
@@ -83,9 +83,9 @@ export default class EditableUtility {
});
observable.editableIsValid = ko.observable<boolean>(true);
observable.subscribe((value) => {
observable.subscribe(value => {
const validations: ((value: T) => boolean)[] = (observable.validations && observable.validations()) || [];
const isValid = validations.every((validate) => validate(value));
const isValid = validations.every(validate => validate(value));
observable.editableIsValid(isValid);
});

View File

@@ -1,8 +1,6 @@
export default class EnvironmentUtility {
public static normalizeArmEndpointUri(uri: string): string {
if (uri && uri.slice(-1) !== "/") {
return `${uri}/`;
}
return uri;
export function normalizeArmEndpoint(uri: string): string {
if (uri && uri.slice(-1) !== "/") {
return `${uri}/`;
}
return uri;
}

View File

@@ -37,7 +37,7 @@ const sendNotificationForError = (errorMessage: string, errorCode: number | stri
}
sendMessage({
type: MessageTypes.ForbiddenError,
reason: errorMessage,
reason: errorMessage
});
}
};

View File

@@ -11,8 +11,8 @@ describe("nextPage", () => {
queryMetrics: {},
requestCharge: 1,
headers: {},
activityId: "foo",
}),
activityId: "foo"
})
};
expect(await nextPage(fakeIterator, 10)).toMatchSnapshot();

View File

@@ -14,7 +14,7 @@ export interface MinimalQueryIterator {
// Pick<QueryIterator<any>, "fetchNext">;
export function nextPage(documentsIterator: MinimalQueryIterator, firstItemIndex: number): Promise<QueryResults> {
return documentsIterator.fetchNext().then((response) => {
return documentsIterator.fetchNext().then(response => {
const documents = response.resources;
const headers = (response as any).headers || {}; // TODO this is a private key. Remove any
const itemCount = (documents && documents.length) || 0;
@@ -26,7 +26,7 @@ export function nextPage(documentsIterator: MinimalQueryIterator, firstItemIndex
lastItemIndex: Number(firstItemIndex) + Number(itemCount),
headers,
activityId: response.activityId,
requestCharge: response.requestCharge,
requestCharge: response.requestCharge
};
});
}

View File

@@ -29,7 +29,7 @@ export function logError(errorMessage: string, area: string, code?: number | str
function _logEntry(entry: Diagnostics.LogEntry): void {
sendMessage({
type: MessageTypes.LogInfo,
data: JSON.stringify(entry),
data: JSON.stringify(entry)
});
const severityLevel = ((level: Diagnostics.LogEntryLevel): SeverityLevel => {
@@ -60,6 +60,6 @@ function _generateLogEntry(
level,
message,
area,
code,
code
};
}

View File

@@ -6,7 +6,7 @@ describe("Message Handler", () => {
let mockPromise = {
id: "123",
startTime: new Date(),
deferred: Q.defer<any>(),
deferred: Q.defer<any>()
};
let mockMessage = { message: { id: "123", data: "{}" } };
MessageHandler.RequestMap[mockPromise.id] = mockPromise;
@@ -18,7 +18,7 @@ describe("Message Handler", () => {
let message = {
id: "123",
startTime: new Date(),
deferred: Q.defer<any>(),
deferred: Q.defer<any>()
};
MessageHandler.handleCachedDataMessage(message);

View File

@@ -35,7 +35,7 @@ export function sendCachedDataMessage<TResponseDataModel>(
let cachedDataPromise: CachedDataPromise<TResponseDataModel> = {
deferred: Q.defer<TResponseDataModel>(),
startTime: new Date(),
id: _.uniqueId(),
id: _.uniqueId()
};
RequestMap[cachedDataPromise.id] = cachedDataPromise;
sendMessage({ type: messageType, params: params, id: cachedDataPromise.id });
@@ -54,7 +54,7 @@ export function sendMessage(data: any): void {
portalChildWindow.parent.postMessage(
{
signature: "pcIframe",
data: data,
data: data
},
portalChildWindow.document.referrer
);

View File

@@ -14,7 +14,7 @@ const fetchMock = () => {
ok: true,
text: () => "{}",
json: () => "{}",
headers: new Map(),
headers: new Map()
});
};
@@ -27,8 +27,8 @@ const collection = {
partitionKey: {
paths: ["/pk"],
kind: "Hash",
version: 1,
},
version: 1
}
} as Collection;
const documentId = ({
@@ -38,8 +38,8 @@ const documentId = ({
partitionKey: {
paths: ["/pk"],
kind: "Hash",
version: 1,
},
version: 1
}
} as unknown) as DocumentId;
const databaseAccount = {
@@ -52,8 +52,8 @@ const databaseAccount = {
documentEndpoint: "bar",
gremlinEndpoint: "foo",
tableEndpoint: "foo",
cassandraEndpoint: "foo",
},
cassandraEndpoint: "foo"
}
} as DatabaseAccount;
describe("MongoProxyClient", () => {
@@ -61,10 +61,10 @@ describe("MongoProxyClient", () => {
beforeEach(() => {
resetConfigContext();
updateUserContext({
databaseAccount,
databaseAccount
});
updateConfigContext({
BACKEND_ENDPOINT: "https://main.documentdb.ext.azure.com",
BACKEND_ENDPOINT: "https://main.documentdb.ext.azure.com"
});
window.fetch = jest.fn().mockImplementation(fetchMock);
});
@@ -93,10 +93,10 @@ describe("MongoProxyClient", () => {
beforeEach(() => {
resetConfigContext();
updateUserContext({
databaseAccount,
databaseAccount
});
updateConfigContext({
BACKEND_ENDPOINT: "https://main.documentdb.ext.azure.com",
BACKEND_ENDPOINT: "https://main.documentdb.ext.azure.com"
});
window.fetch = jest.fn().mockImplementation(fetchMock);
});
@@ -125,10 +125,10 @@ describe("MongoProxyClient", () => {
beforeEach(() => {
resetConfigContext();
updateUserContext({
databaseAccount,
databaseAccount
});
updateConfigContext({
BACKEND_ENDPOINT: "https://main.documentdb.ext.azure.com",
BACKEND_ENDPOINT: "https://main.documentdb.ext.azure.com"
});
window.fetch = jest.fn().mockImplementation(fetchMock);
});
@@ -157,10 +157,10 @@ describe("MongoProxyClient", () => {
beforeEach(() => {
resetConfigContext();
updateUserContext({
databaseAccount,
databaseAccount
});
updateConfigContext({
BACKEND_ENDPOINT: "https://main.documentdb.ext.azure.com",
BACKEND_ENDPOINT: "https://main.documentdb.ext.azure.com"
});
window.fetch = jest.fn().mockImplementation(fetchMock);
});
@@ -189,10 +189,10 @@ describe("MongoProxyClient", () => {
beforeEach(() => {
resetConfigContext();
updateUserContext({
databaseAccount,
databaseAccount
});
updateConfigContext({
BACKEND_ENDPOINT: "https://main.documentdb.ext.azure.com",
BACKEND_ENDPOINT: "https://main.documentdb.ext.azure.com"
});
window.fetch = jest.fn().mockImplementation(fetchMock);
});
@@ -222,10 +222,10 @@ describe("MongoProxyClient", () => {
resetConfigContext();
delete window.authType;
updateUserContext({
databaseAccount,
databaseAccount
});
updateConfigContext({
BACKEND_ENDPOINT: "https://main.documentdb.ext.azure.com",
BACKEND_ENDPOINT: "https://main.documentdb.ext.azure.com"
});
});

View File

@@ -16,7 +16,7 @@ import { sendMessage } from "./MessageHandler";
const defaultHeaders = {
[HttpHeaders.apiType]: ApiType.MongoDB.toString(),
[CosmosSDKConstants.HttpHeaders.MaxEntityCount]: "100",
[CosmosSDKConstants.HttpHeaders.Version]: "2017-11-15",
[CosmosSDKConstants.HttpHeaders.Version]: "2017-11-15"
};
function authHeaders() {
@@ -31,7 +31,7 @@ export function queryIterator(databaseId: string, collection: Collection, query:
let continuationToken: string;
return {
fetchNext: () => {
return queryDocuments(databaseId, collection, false, query).then((response) => {
return queryDocuments(databaseId, collection, false, query).then(response => {
continuationToken = response.continuationToken;
const headers: { [key: string]: string | number } = {};
response.headers.forEach((value, key) => {
@@ -42,10 +42,10 @@ export function queryIterator(databaseId: string, collection: Collection, query:
headers,
requestCharge: Number(headers[CosmosSDKConstants.HttpHeaders.RequestCharge]),
activityId: String(headers[CosmosSDKConstants.HttpHeaders.ActivityId]),
hasMoreResults: !!continuationToken,
hasMoreResults: !!continuationToken
};
});
},
}
};
}
@@ -74,9 +74,7 @@ export function queryDocuments(
rg: userContext.resourceGroup,
dba: databaseAccount.name,
pk:
collection && collection.partitionKey && !collection.partitionKey.systemKey
? collection.partitionKeyProperty
: "",
collection && collection.partitionKey && !collection.partitionKey.systemKey ? collection.partitionKeyProperty : ""
};
const endpoint = getEndpoint() || "";
@@ -89,7 +87,7 @@ export function queryDocuments(
[CosmosSDKConstants.HttpHeaders.EnableScanInQuery]: "true",
[CosmosSDKConstants.HttpHeaders.EnableCrossPartitionQuery]: "true",
[CosmosSDKConstants.HttpHeaders.ParallelizeCrossPartitionQuery]: "true",
[HttpHeaders.contentType]: "application/query+json",
[HttpHeaders.contentType]: "application/query+json"
};
if (continuationToken) {
@@ -102,14 +100,14 @@ export function queryDocuments(
.fetch(`${endpoint}${path}?${queryString.stringify(params)}`, {
method: "POST",
body: JSON.stringify({ query }),
headers,
headers
})
.then(async (response) => {
.then(async response => {
if (response.ok) {
return {
continuationToken: response.headers.get(CosmosSDKConstants.HttpHeaders.Continuation),
documents: (await response.json()).Documents as DataModels.DocumentId[],
headers: response.headers,
headers: response.headers
};
}
errorHandling(response, "querying documents", params);
@@ -137,9 +135,7 @@ export function readDocument(
rg: userContext.resourceGroup,
dba: databaseAccount.name,
pk:
documentId && documentId.partitionKey && !documentId.partitionKey.systemKey
? documentId.partitionKeyProperty
: "",
documentId && documentId.partitionKey && !documentId.partitionKey.systemKey ? documentId.partitionKeyProperty : ""
};
const endpoint = getEndpoint();
@@ -151,10 +147,10 @@ export function readDocument(
...authHeaders(),
[CosmosSDKConstants.HttpHeaders.PartitionKey]: encodeURIComponent(
JSON.stringify(documentId.partitionKeyHeader())
),
},
)
}
})
.then((response) => {
.then(response => {
if (response.ok) {
return response.json();
}
@@ -179,7 +175,7 @@ export function createDocument(
sid: userContext.subscriptionId,
rg: userContext.resourceGroup,
dba: databaseAccount.name,
pk: collection && collection.partitionKey && !collection.partitionKey.systemKey ? partitionKeyProperty : "",
pk: collection && collection.partitionKey && !collection.partitionKey.systemKey ? partitionKeyProperty : ""
};
const endpoint = getEndpoint();
@@ -190,10 +186,10 @@ export function createDocument(
body: JSON.stringify(documentContent),
headers: {
...defaultHeaders,
...authHeaders(),
},
...authHeaders()
}
})
.then((response) => {
.then(response => {
if (response.ok) {
return response.json();
}
@@ -222,9 +218,7 @@ export function updateDocument(
rg: userContext.resourceGroup,
dba: databaseAccount.name,
pk:
documentId && documentId.partitionKey && !documentId.partitionKey.systemKey
? documentId.partitionKeyProperty
: "",
documentId && documentId.partitionKey && !documentId.partitionKey.systemKey ? documentId.partitionKeyProperty : ""
};
const endpoint = getEndpoint();
@@ -236,10 +230,10 @@ export function updateDocument(
...defaultHeaders,
...authHeaders(),
[HttpHeaders.contentType]: "application/json",
[CosmosSDKConstants.HttpHeaders.PartitionKey]: JSON.stringify(documentId.partitionKeyHeader()),
},
[CosmosSDKConstants.HttpHeaders.PartitionKey]: JSON.stringify(documentId.partitionKeyHeader())
}
})
.then((response) => {
.then(response => {
if (response.ok) {
return response.json();
}
@@ -263,9 +257,7 @@ export function deleteDocument(databaseId: string, collection: Collection, docum
rg: userContext.resourceGroup,
dba: databaseAccount.name,
pk:
documentId && documentId.partitionKey && !documentId.partitionKey.systemKey
? documentId.partitionKeyProperty
: "",
documentId && documentId.partitionKey && !documentId.partitionKey.systemKey ? documentId.partitionKeyProperty : ""
};
const endpoint = getEndpoint();
@@ -276,10 +268,10 @@ export function deleteDocument(databaseId: string, collection: Collection, docum
...defaultHeaders,
...authHeaders(),
[HttpHeaders.contentType]: "application/json",
[CosmosSDKConstants.HttpHeaders.PartitionKey]: JSON.stringify(documentId.partitionKeyHeader()),
},
[CosmosSDKConstants.HttpHeaders.PartitionKey]: JSON.stringify(documentId.partitionKeyHeader())
}
})
.then((response) => {
.then(response => {
if (response.ok) {
return undefined;
}
@@ -307,7 +299,7 @@ export function createMongoCollectionWithProxy(
rg: userContext.resourceGroup,
dba: databaseAccount.name,
isAutoPilot: !!params.autoPilotMaxThroughput,
autoPilotThroughput: params.autoPilotMaxThroughput?.toString(),
autoPilotThroughput: params.autoPilotMaxThroughput?.toString()
};
const endpoint = getEndpoint();
@@ -322,11 +314,11 @@ export function createMongoCollectionWithProxy(
headers: {
...defaultHeaders,
...authHeaders(),
[HttpHeaders.contentType]: "application/json",
},
[HttpHeaders.contentType]: "application/json"
}
}
)
.then((response) => {
.then(response => {
if (response.ok) {
return response.json();
}

View File

@@ -14,7 +14,7 @@
*/
export default class MongoUtility {
public static tojson = function (x: any, indent: string, nolint: boolean) {
public static tojson = function(x: any, indent: string, nolint: boolean) {
if (x === null || x === undefined) {
return String(x);
}
@@ -71,7 +71,7 @@ export default class MongoUtility {
}
};
private static tojsonObject = function (x: any, indent: string, nolint: boolean) {
private static tojsonObject = function(x: any, indent: string, nolint: boolean) {
var lineEnding = nolint ? " " : "\n";
var tabSpace = nolint ? "" : "\t";
indent = indent || "";
@@ -114,7 +114,7 @@ export default class MongoUtility {
}
}
// Add proper line endings, indents, and commas to each line
s += $.map(pairs, function (pair) {
s += $.map(pairs, function(pair) {
return lineEnding + indent + pair;
}).join(",");
s += lineEnding;
@@ -124,7 +124,7 @@ export default class MongoUtility {
return s + indent + "}";
};
private static tojsonArray = function (a: any, indent: string, nolint: boolean) {
private static tojsonArray = function(a: any, indent: string, nolint: boolean) {
if (a.length === 0) {
return "[ ]";
}
@@ -151,7 +151,7 @@ export default class MongoUtility {
return s;
};
private static hasDefinedProperty = function (obj: any, prop: string): boolean {
private static hasDefinedProperty = function(obj: any, prop: string): boolean {
if (Object.getPrototypeOf === undefined || Object.getPrototypeOf(obj) === null) {
return false;
} else if (obj.hasOwnProperty(prop)) {

View File

@@ -9,14 +9,14 @@ describe("parseSDKOfferResponse", () => {
offerThroughput: 500,
collectionThroughputInfo: {
minimumRUForCollection: 400,
numPhysicalPartitions: 1,
},
numPhysicalPartitions: 1
}
},
id: "test",
id: "test"
} as SDKOfferDefinition;
const mockResponse = {
resource: mockOfferDefinition,
resource: mockOfferDefinition
} as OfferResponse;
const expectedResult: Offer = {
@@ -25,6 +25,7 @@ describe("parseSDKOfferResponse", () => {
minimumThroughput: 400,
id: "test",
offerDefinition: mockOfferDefinition,
offerReplacePending: false
};
expect(OfferUtility.parseSDKOfferResponse(mockResponse)).toEqual(expectedResult);
@@ -36,17 +37,17 @@ describe("parseSDKOfferResponse", () => {
offerThroughput: 400,
collectionThroughputInfo: {
minimumRUForCollection: 400,
numPhysicalPartitions: 1,
numPhysicalPartitions: 1
},
offerAutopilotSettings: {
maxThroughput: 5000,
},
maxThroughput: 5000
}
},
id: "test",
id: "test"
} as SDKOfferDefinition;
const mockResponse = {
resource: mockOfferDefinition,
resource: mockOfferDefinition
} as OfferResponse;
const expectedResult: Offer = {
@@ -55,6 +56,7 @@ describe("parseSDKOfferResponse", () => {
minimumThroughput: 400,
id: "test",
offerDefinition: mockOfferDefinition,
offerReplacePending: false
};
expect(OfferUtility.parseSDKOfferResponse(mockResponse)).toEqual(expectedResult);

View File

@@ -1,5 +1,6 @@
import { Offer, SDKOfferDefinition } from "../Contracts/DataModels";
import { OfferResponse } from "@azure/cosmos";
import { HttpHeaders } from "./Constants";
export const parseSDKOfferResponse = (offerResponse: OfferResponse): Offer => {
const offerDefinition: SDKOfferDefinition = offerResponse?.resource;
@@ -18,7 +19,7 @@ export const parseSDKOfferResponse = (offerResponse: OfferResponse): Offer => {
manualThroughput: undefined,
minimumThroughput,
offerDefinition,
headers: offerResponse.headers,
offerReplacePending: offerResponse.headers?.[HttpHeaders.offerReplacePending] === "true"
};
}
@@ -28,6 +29,6 @@ export const parseSDKOfferResponse = (offerResponse: OfferResponse): Offer => {
manualThroughput: offerContent.offerThroughput,
minimumThroughput,
offerDefinition,
headers: offerResponse.headers,
offerReplacePending: offerResponse.headers?.[HttpHeaders.offerReplacePending] === "true"
};
};

View File

@@ -30,7 +30,7 @@ export const fetchPortalNotifications = async (): Promise<DataModels.Notificatio
const headers = { [authorizationHeader.header]: authorizationHeader.token };
const response = await window.fetch(url, {
headers,
headers
});
if (!response.ok) {

View File

@@ -3,22 +3,24 @@ import * as _ from "underscore";
import * as DataModels from "../Contracts/DataModels";
import * as ViewModels from "../Contracts/ViewModels";
import Explorer from "../Explorer/Explorer";
import { ConsoleDataType } from "../Explorer/Menus/NotificationConsole/NotificationConsoleComponent";
import DocumentsTab from "../Explorer/Tabs/DocumentsTab";
import DocumentId from "../Explorer/Tree/DocumentId";
import * as NotificationConsoleUtils from "../Utils/NotificationConsoleUtils";
import { QueryUtils } from "../Utils/QueryUtils";
import { BackendDefaults, HttpStatusCodes, SavedQueries } from "./Constants";
import { userContext } from "../UserContext";
import { createDocument, deleteDocument, queryDocuments, queryDocumentsPage } from "./DocumentClientUtilityBase";
import { queryDocumentsPage } from "./dataAccess/queryDocumentsPage";
import { createCollection } from "./dataAccess/createCollection";
import { handleError } from "./ErrorHandlingUtils";
import { createDocument } from "./dataAccess/createDocument";
import { deleteDocument } from "./dataAccess/deleteDocument";
import { queryDocuments } from "./dataAccess/queryDocuments";
export class QueriesClient {
private static readonly PartitionKey: DataModels.PartitionKey = {
paths: [`/${SavedQueries.PartitionKeyProperty}`],
kind: BackendDefaults.partitionKeyKind,
version: BackendDefaults.partitionKeyVersion,
version: BackendDefaults.partitionKeyVersion
};
private static readonly FetchQuery: string = "SELECT * FROM c";
private static readonly FetchMongoQuery: string = "{}";
@@ -31,24 +33,18 @@ export class QueriesClient {
return Promise.resolve(queriesCollection.rawDataModel);
}
const id = NotificationConsoleUtils.logConsoleMessage(
ConsoleDataType.InProgress,
"Setting up account for saving queries"
);
const clearMessage = NotificationConsoleUtils.logConsoleProgress("Setting up account for saving queries");
return createCollection({
collectionId: SavedQueries.CollectionName,
createNewDatabase: true,
databaseId: SavedQueries.DatabaseName,
partitionKey: QueriesClient.PartitionKey,
offerThroughput: SavedQueries.OfferThroughput,
databaseLevelThroughput: false,
databaseLevelThroughput: false
})
.then(
(collection: DataModels.Collection) => {
NotificationConsoleUtils.logConsoleMessage(
ConsoleDataType.Info,
"Successfully set up account for saving queries"
);
NotificationConsoleUtils.logConsoleInfo("Successfully set up account for saving queries");
return Promise.resolve(collection);
},
(error: any) => {
@@ -56,17 +52,14 @@ export class QueriesClient {
return Promise.reject(error);
}
)
.finally(() => NotificationConsoleUtils.clearInProgressMessageWithId(id));
.finally(() => clearMessage());
}
public async saveQuery(query: DataModels.Query): Promise<void> {
const queriesCollection = this.findQueriesCollection();
if (!queriesCollection) {
const errorMessage: string = "Account not set up to perform saved query operations";
NotificationConsoleUtils.logConsoleMessage(
ConsoleDataType.Error,
`Failed to save query ${query.queryName}: ${errorMessage}`
);
NotificationConsoleUtils.logConsoleError(`Failed to save query ${query.queryName}: ${errorMessage}`);
return Promise.reject(errorMessage);
}
@@ -74,25 +67,16 @@ export class QueriesClient {
this.validateQuery(query);
} catch (error) {
const errorMessage: string = "Invalid query specified";
NotificationConsoleUtils.logConsoleMessage(
ConsoleDataType.Error,
`Failed to save query ${query.queryName}: ${errorMessage}`
);
NotificationConsoleUtils.logConsoleError(`Failed to save query ${query.queryName}: ${errorMessage}`);
return Promise.reject(errorMessage);
}
const id = NotificationConsoleUtils.logConsoleMessage(
ConsoleDataType.InProgress,
`Saving query ${query.queryName}`
);
const clearMessage = NotificationConsoleUtils.logConsoleProgress(`Saving query ${query.queryName}`);
query.id = query.queryName;
return createDocument(queriesCollection, query)
.then(
(savedQuery: DataModels.Query) => {
NotificationConsoleUtils.logConsoleMessage(
ConsoleDataType.Info,
`Successfully saved query ${query.queryName}`
);
NotificationConsoleUtils.logConsoleInfo(`Successfully saved query ${query.queryName}`);
return Promise.resolve();
},
(error: any) => {
@@ -103,74 +87,65 @@ export class QueriesClient {
return Promise.reject(error);
}
)
.finally(() => NotificationConsoleUtils.clearInProgressMessageWithId(id));
.finally(() => clearMessage());
}
public async getQueries(): Promise<DataModels.Query[]> {
const queriesCollection = this.findQueriesCollection();
if (!queriesCollection) {
const errorMessage: string = "Account not set up to perform saved query operations";
NotificationConsoleUtils.logConsoleMessage(
ConsoleDataType.Error,
`Failed to fetch saved queries: ${errorMessage}`
);
NotificationConsoleUtils.logConsoleError(`Failed to fetch saved queries: ${errorMessage}`);
return Promise.reject(errorMessage);
}
const options: any = { enableCrossPartitionQuery: true };
const id = NotificationConsoleUtils.logConsoleMessage(ConsoleDataType.InProgress, "Fetching saved queries");
return queryDocuments(SavedQueries.DatabaseName, SavedQueries.CollectionName, this.fetchQueriesQuery(), options)
const clearMessage = NotificationConsoleUtils.logConsoleProgress("Fetching saved queries");
const queryIterator: QueryIterator<ItemDefinition & Resource> = queryDocuments(
SavedQueries.DatabaseName,
SavedQueries.CollectionName,
this.fetchQueriesQuery(),
options
);
const fetchQueries = async (firstItemIndex: number): Promise<ViewModels.QueryResults> =>
await queryDocumentsPage(queriesCollection.id(), queryIterator, firstItemIndex);
return QueryUtils.queryAllPages(fetchQueries)
.then(
(queryIterator: QueryIterator<ItemDefinition & Resource>) => {
const fetchQueries = (firstItemIndex: number): Q.Promise<ViewModels.QueryResults> =>
queryDocumentsPage(queriesCollection.id(), queryIterator, firstItemIndex, options);
return QueryUtils.queryAllPages(fetchQueries).then(
(results: ViewModels.QueryResults) => {
let queries: DataModels.Query[] = _.map(results.documents, (document: DataModels.Query) => {
if (!document) {
return undefined;
}
const { id, resourceId, query, queryName } = document;
const parsedQuery: DataModels.Query = {
resourceId: resourceId,
queryName: queryName,
query: query,
id: id,
};
try {
this.validateQuery(parsedQuery);
return parsedQuery;
} catch (error) {
return undefined;
}
});
queries = _.reject(queries, (parsedQuery: DataModels.Query) => !parsedQuery);
NotificationConsoleUtils.logConsoleMessage(ConsoleDataType.Info, "Successfully fetched saved queries");
return Promise.resolve(queries);
},
(error: any) => {
handleError(error, "getSavedQueries", "Failed to fetch saved queries");
return Promise.reject(error);
(results: ViewModels.QueryResults) => {
let queries: DataModels.Query[] = _.map(results.documents, (document: DataModels.Query) => {
if (!document) {
return undefined;
}
);
const { id, resourceId, query, queryName } = document;
const parsedQuery: DataModels.Query = {
resourceId: resourceId,
queryName: queryName,
query: query,
id: id
};
try {
this.validateQuery(parsedQuery);
return parsedQuery;
} catch (error) {
return undefined;
}
});
queries = _.reject(queries, (parsedQuery: DataModels.Query) => !parsedQuery);
NotificationConsoleUtils.logConsoleInfo("Successfully fetched saved queries");
return Promise.resolve(queries);
},
(error: any) => {
// should never get into this state but we handle this regardless
handleError(error, "getSavedQueries", "Failed to fetch saved queries");
return Promise.reject(error);
}
)
.finally(() => NotificationConsoleUtils.clearInProgressMessageWithId(id));
.finally(() => clearMessage());
}
public async deleteQuery(query: DataModels.Query): Promise<void> {
const queriesCollection = this.findQueriesCollection();
if (!queriesCollection) {
const errorMessage: string = "Account not set up to perform saved query operations";
NotificationConsoleUtils.logConsoleMessage(
ConsoleDataType.Error,
`Failed to fetch saved queries: ${errorMessage}`
);
NotificationConsoleUtils.logConsoleError(`Failed to fetch saved queries: ${errorMessage}`);
return Promise.reject(errorMessage);
}
@@ -178,21 +153,15 @@ export class QueriesClient {
this.validateQuery(query);
} catch (error) {
const errorMessage: string = "Invalid query specified";
NotificationConsoleUtils.logConsoleMessage(
ConsoleDataType.Error,
`Failed to delete query ${query.queryName}: ${errorMessage}`
);
NotificationConsoleUtils.logConsoleError(`Failed to delete query ${query.queryName}: ${errorMessage}`);
}
const id = NotificationConsoleUtils.logConsoleMessage(
ConsoleDataType.InProgress,
`Deleting query ${query.queryName}`
);
const clearMessage = NotificationConsoleUtils.logConsoleProgress(`Deleting query ${query.queryName}`);
query.id = query.queryName;
const documentId = new DocumentId(
{
partitionKey: QueriesClient.PartitionKey,
partitionKeyProperty: "id",
partitionKeyProperty: "id"
} as DocumentsTab,
query,
query.queryName
@@ -201,10 +170,7 @@ export class QueriesClient {
return deleteDocument(queriesCollection, documentId)
.then(
() => {
NotificationConsoleUtils.logConsoleMessage(
ConsoleDataType.Info,
`Successfully deleted query ${query.queryName}`
);
NotificationConsoleUtils.logConsoleInfo(`Successfully deleted query ${query.queryName}`);
return Promise.resolve();
},
(error: any) => {
@@ -212,7 +178,7 @@ export class QueriesClient {
return Promise.reject(error);
}
)
.finally(() => NotificationConsoleUtils.clearInProgressMessageWithId(id));
.finally(() => clearMessage());
}
public getResourceId(): string {

View File

@@ -4,7 +4,7 @@ import { SplitterMetrics } from "./Constants";
export enum SplitterDirection {
Horizontal = "horizontal",
Vertical = "vertical",
Vertical = "vertical"
}
export interface SplitterBounds {
@@ -50,7 +50,7 @@ export class Splitter {
animate: true,
animateDuration: "fast",
start: this.onResizeStart,
stop: this.onResizeStop,
stop: this.onResizeStop
};
if (isVerticalSplitter) {
@@ -90,7 +90,9 @@ export class Splitter {
this.lastWidth = $(this.leftSide).width();
$(this.splitter).css("left", SplitterMetrics.CollapsedPositionLeft);
$(this.leftSide).css("width", "");
$(this.leftSide).resizable("option", "disabled", true).removeClass("ui-resizable-disabled"); // remove class so splitter is visible
$(this.leftSide)
.resizable("option", "disabled", true)
.removeClass("ui-resizable-disabled"); // remove class so splitter is visible
$(this.splitter).removeClass("ui-resizable-e");
this.isCollapsed(true);
}

View File

@@ -32,8 +32,8 @@ export default class UrlUtility {
type: type,
objectBody: {
id: id,
self: resourcePath,
},
self: resourcePath
}
};
return result;

View File

@@ -1,6 +1,5 @@
jest.mock("../../Utils/arm/request");
jest.mock("../CosmosClient");
jest.mock("../DataAccessUtilityBase");
import { AuthType } from "../../AuthType";
import { CreateCollectionParams, DatabaseAccount } from "../../Contracts/DataModels";
import { DefaultAccountExperienceType } from "../../DefaultAccountExperienceType";
@@ -15,15 +14,15 @@ describe("createCollection", () => {
collectionId: "testContainer",
databaseId: "testDatabase",
databaseLevelThroughput: true,
offerThroughput: 400,
offerThroughput: 400
};
beforeAll(() => {
updateUserContext({
databaseAccount: {
name: "test",
name: "test"
} as DatabaseAccount,
defaultExperience: DefaultAccountExperienceType.DocumentDB,
defaultExperience: DefaultAccountExperienceType.DocumentDB
});
});
@@ -41,12 +40,12 @@ describe("createCollection", () => {
return {
database: {
containers: {
create: () => ({}),
},
},
create: () => ({})
}
}
};
},
},
}
}
});
await createCollection(createCollectionParams);
expect(client).toHaveBeenCalled();
@@ -60,7 +59,7 @@ describe("createCollection", () => {
collectionId: "testContainer",
databaseId: "testDatabase",
databaseLevelThroughput: false,
offerThroughput: 400,
offerThroughput: 400
};
expect(constructRpOptions(manualThroughputParams)).toEqual({ throughput: 400 });
@@ -70,12 +69,12 @@ describe("createCollection", () => {
databaseId: "testDatabase",
databaseLevelThroughput: false,
offerThroughput: 400,
autoPilotMaxThroughput: 4000,
autoPilotMaxThroughput: 4000
};
expect(constructRpOptions(autoPilotThroughputParams)).toEqual({
autoscaleSettings: {
maxThroughput: 4000,
},
maxThroughput: 4000
}
});
});
});

View File

@@ -11,15 +11,15 @@ import { createMongoCollectionWithProxy } from "../MongoProxyClient";
import { createUpdateSqlContainer, getSqlContainer } from "../../Utils/arm/generatedClients/2020-04-01/sqlResources";
import {
createUpdateCassandraTable,
getCassandraTable,
getCassandraTable
} from "../../Utils/arm/generatedClients/2020-04-01/cassandraResources";
import {
createUpdateMongoDBCollection,
getMongoDBCollection,
getMongoDBCollection
} from "../../Utils/arm/generatedClients/2020-04-01/mongoDBResources";
import {
createUpdateGremlinGraph,
getGremlinGraph,
getGremlinGraph
} from "../../Utils/arm/generatedClients/2020-04-01/gremlinResources";
import { createUpdateTable, getTable } from "../../Utils/arm/generatedClients/2020-04-01/tableResources";
import { logConsoleProgress, logConsoleInfo } from "../../Utils/NotificationConsoleUtils";
@@ -41,7 +41,7 @@ export const createCollection = async (params: DataModels.CreateCollectionParams
autoPilotMaxThroughput: params.autoPilotMaxThroughput,
databaseId: params.databaseId,
databaseLevelThroughput: params.databaseLevelThroughput,
offerThroughput: params.offerThroughput,
offerThroughput: params.offerThroughput
};
await createDatabase(createDatabaseParams);
}
@@ -100,7 +100,7 @@ const createSqlContainer = async (params: DataModels.CreateCollectionParams): Pr
const options: ARMTypes.CreateUpdateOptions = constructRpOptions(params);
const resource: ARMTypes.SqlContainerResource = {
id: params.collectionId,
id: params.collectionId
};
if (params.analyticalStorageTtl) {
resource.analyticalStorageTtl = params.analyticalStorageTtl;
@@ -118,8 +118,8 @@ const createSqlContainer = async (params: DataModels.CreateCollectionParams): Pr
const rpPayload: ARMTypes.SqlDatabaseCreateUpdateParameters = {
properties: {
resource,
options,
},
options
}
};
const createResponse = await createUpdateSqlContainer(
@@ -154,7 +154,7 @@ const createMongoCollection = async (params: DataModels.CreateCollectionParams):
const options: ARMTypes.CreateUpdateOptions = constructRpOptions(params);
const resource: ARMTypes.MongoDBCollectionResource = {
id: params.collectionId,
id: params.collectionId
};
if (params.analyticalStorageTtl) {
resource.analyticalStorageTtl = params.analyticalStorageTtl;
@@ -170,8 +170,8 @@ const createMongoCollection = async (params: DataModels.CreateCollectionParams):
const rpPayload: ARMTypes.MongoDBCollectionCreateUpdateParameters = {
properties: {
resource,
options,
},
options
}
};
const createResponse = await createUpdateMongoDBCollection(
@@ -185,7 +185,7 @@ const createMongoCollection = async (params: DataModels.CreateCollectionParams):
if (params.createMongoWildcardIndex) {
TelemetryProcessor.trace(Action.CreateMongoCollectionWithWildcardIndex, ActionModifiers.Mark, {
message: "Mongo Collection created with wildcard index on all fields.",
message: "Mongo Collection created with wildcard index on all fields."
});
}
@@ -212,7 +212,7 @@ const createCassandraTable = async (params: DataModels.CreateCollectionParams):
const options: ARMTypes.CreateUpdateOptions = constructRpOptions(params);
const resource: ARMTypes.CassandraTableResource = {
id: params.collectionId,
id: params.collectionId
};
if (params.analyticalStorageTtl) {
resource.analyticalStorageTtl = params.analyticalStorageTtl;
@@ -221,8 +221,8 @@ const createCassandraTable = async (params: DataModels.CreateCollectionParams):
const rpPayload: ARMTypes.CassandraTableCreateUpdateParameters = {
properties: {
resource,
options,
},
options
}
};
const createResponse = await createUpdateCassandraTable(
@@ -256,7 +256,7 @@ const createGraph = async (params: DataModels.CreateCollectionParams): Promise<D
const options: ARMTypes.CreateUpdateOptions = constructRpOptions(params);
const resource: ARMTypes.GremlinGraphResource = {
id: params.collectionId,
id: params.collectionId
};
if (params.indexingPolicy) {
@@ -272,8 +272,8 @@ const createGraph = async (params: DataModels.CreateCollectionParams): Promise<D
const rpPayload: ARMTypes.GremlinGraphCreateUpdateParameters = {
properties: {
resource,
options,
},
options
}
};
const createResponse = await createUpdateGremlinGraph(
@@ -306,14 +306,14 @@ const createTable = async (params: DataModels.CreateCollectionParams): Promise<D
const options: ARMTypes.CreateUpdateOptions = constructRpOptions(params);
const resource: ARMTypes.TableResource = {
id: params.collectionId,
id: params.collectionId
};
const rpPayload: ARMTypes.TableCreateUpdateParameters = {
properties: {
resource,
options,
},
options
}
};
const createResponse = await createUpdateTable(
@@ -334,13 +334,13 @@ export const constructRpOptions = (params: DataModels.CreateDatabaseParams): ARM
if (params.autoPilotMaxThroughput) {
return {
autoscaleSettings: {
maxThroughput: params.autoPilotMaxThroughput,
},
maxThroughput: params.autoPilotMaxThroughput
}
};
}
return {
throughput: params.offerThroughput,
throughput: params.offerThroughput
};
};
@@ -350,7 +350,7 @@ const createCollectionWithSDK = async (params: DataModels.CreateCollectionParams
partitionKey: params.partitionKey || undefined,
indexingPolicy: params.indexingPolicy || undefined,
uniqueKeyPolicy: params.uniqueKeyPolicy || undefined,
analyticalStorageTtl: params.analyticalStorageTtl,
analyticalStorageTtl: params.analyticalStorageTtl
} as ContainerRequest; // TODO: remove cast when https://github.com/Azure/azure-cosmos-js/issues/423 is fixed
const collectionOptions: RequestOptions = {};
const createDatabaseBody: DatabaseRequest = { id: params.databaseId };

View File

@@ -8,21 +8,21 @@ import {
GremlinDatabaseCreateUpdateParameters,
MongoDBDatabaseCreateUpdateParameters,
SqlDatabaseCreateUpdateParameters,
CreateUpdateOptions,
CreateUpdateOptions
} from "../../Utils/arm/generatedClients/2020-04-01/types";
import { client } from "../CosmosClient";
import { createUpdateSqlDatabase, getSqlDatabase } from "../../Utils/arm/generatedClients/2020-04-01/sqlResources";
import {
createUpdateCassandraKeyspace,
getCassandraKeyspace,
getCassandraKeyspace
} from "../../Utils/arm/generatedClients/2020-04-01/cassandraResources";
import {
createUpdateMongoDBDatabase,
getMongoDBDatabase,
getMongoDBDatabase
} from "../../Utils/arm/generatedClients/2020-04-01/mongoDBResources";
import {
createUpdateGremlinDatabase,
getGremlinDatabase,
getGremlinDatabase
} from "../../Utils/arm/generatedClients/2020-04-01/gremlinResources";
import { handleError } from "../ErrorHandlingUtils";
import { logConsoleProgress, logConsoleInfo } from "../../Utils/NotificationConsoleUtils";
@@ -85,10 +85,10 @@ async function createSqlDatabase(params: DataModels.CreateDatabaseParams): Promi
const rpPayload: SqlDatabaseCreateUpdateParameters = {
properties: {
resource: {
id: params.databaseId,
id: params.databaseId
},
options,
},
options
}
};
const createResponse = await createUpdateSqlDatabase(
userContext.subscriptionId,
@@ -121,10 +121,10 @@ async function createMongoDatabase(params: DataModels.CreateDatabaseParams): Pro
const rpPayload: MongoDBDatabaseCreateUpdateParameters = {
properties: {
resource: {
id: params.databaseId,
id: params.databaseId
},
options,
},
options
}
};
const createResponse = await createUpdateMongoDBDatabase(
userContext.subscriptionId,
@@ -157,10 +157,10 @@ async function createCassandraKeyspace(params: DataModels.CreateDatabaseParams):
const rpPayload: CassandraKeyspaceCreateUpdateParameters = {
properties: {
resource: {
id: params.databaseId,
id: params.databaseId
},
options,
},
options
}
};
const createResponse = await createUpdateCassandraKeyspace(
userContext.subscriptionId,
@@ -193,10 +193,10 @@ async function createGremlineDatabase(params: DataModels.CreateDatabaseParams):
const rpPayload: GremlinDatabaseCreateUpdateParameters = {
properties: {
resource: {
id: params.databaseId,
id: params.databaseId
},
options,
},
options
}
};
const createResponse = await createUpdateGremlinDatabase(
userContext.subscriptionId,
@@ -231,12 +231,12 @@ function constructRpOptions(params: DataModels.CreateDatabaseParams): CreateUpda
if (params.autoPilotMaxThroughput) {
return {
autoscaleSettings: {
maxThroughput: params.autoPilotMaxThroughput,
},
maxThroughput: params.autoPilotMaxThroughput
}
};
}
return {
throughput: params.offerThroughput,
throughput: params.offerThroughput
};
}

View File

@@ -0,0 +1,25 @@
import { CollectionBase } from "../../Contracts/ViewModels";
import { client } from "../CosmosClient";
import { getEntityName } from "../DocumentUtility";
import { handleError } from "../ErrorHandlingUtils";
import { logConsoleInfo, logConsoleProgress } from "../../Utils/NotificationConsoleUtils";
export const createDocument = async (collection: CollectionBase, newDocument: unknown): Promise<unknown> => {
const entityName = getEntityName();
const clearMessage = logConsoleProgress(`Creating new ${entityName} for container ${collection.id()}`);
try {
const response = await client()
.database(collection.databaseId)
.container(collection.id())
.items.create(newDocument);
logConsoleInfo(`Successfully created new ${entityName} for container ${collection.id()}`);
return response?.resource;
} catch (error) {
handleError(error, "CreateDocument", `Error while creating new ${entityName} for container ${collection.id()}`);
throw error;
} finally {
clearMessage();
}
};

View File

@@ -3,12 +3,12 @@ import { DefaultAccountExperienceType } from "../../DefaultAccountExperienceType
import { Resource, StoredProcedureDefinition } from "@azure/cosmos";
import {
SqlStoredProcedureCreateUpdateParameters,
SqlStoredProcedureResource,
SqlStoredProcedureResource
} from "../../Utils/arm/generatedClients/2020-04-01/types";
import { client } from "../CosmosClient";
import {
createUpdateSqlStoredProcedure,
getSqlStoredProcedure,
getSqlStoredProcedure
} from "../../Utils/arm/generatedClients/2020-04-01/sqlResources";
import { handleError } from "../ErrorHandlingUtils";
import { logConsoleProgress } from "../../Utils/NotificationConsoleUtils";
@@ -49,8 +49,8 @@ export async function createStoredProcedure(
const createSprocParams: SqlStoredProcedureCreateUpdateParameters = {
properties: {
resource: storedProcedure as SqlStoredProcedureResource,
options: {},
},
options: {}
}
};
const rpResponse = await createUpdateSqlStoredProcedure(
userContext.subscriptionId,

View File

@@ -3,7 +3,7 @@ import { DefaultAccountExperienceType } from "../../DefaultAccountExperienceType
import { Resource, TriggerDefinition } from "@azure/cosmos";
import {
SqlTriggerCreateUpdateParameters,
SqlTriggerResource,
SqlTriggerResource
} from "../../Utils/arm/generatedClients/2020-04-01/types";
import { client } from "../CosmosClient";
import { createUpdateSqlTrigger, getSqlTrigger } from "../../Utils/arm/generatedClients/2020-04-01/sqlResources";
@@ -44,8 +44,8 @@ export async function createTrigger(
const createTriggerParams: SqlTriggerCreateUpdateParameters = {
properties: {
resource: trigger as SqlTriggerResource,
options: {},
},
options: {}
}
};
const rpResponse = await createUpdateSqlTrigger(
userContext.subscriptionId,
@@ -59,7 +59,10 @@ export async function createTrigger(
return rpResponse && (rpResponse.properties?.resource as TriggerDefinition & Resource);
}
const response = await client().database(databaseId).container(collectionId).scripts.triggers.create(trigger);
const response = await client()
.database(databaseId)
.container(collectionId)
.scripts.triggers.create(trigger);
return response.resource;
} catch (error) {
handleError(error, "CreateTrigger", `Error while creating trigger ${trigger.id}`);

View File

@@ -3,12 +3,12 @@ import { DefaultAccountExperienceType } from "../../DefaultAccountExperienceType
import { Resource, UserDefinedFunctionDefinition } from "@azure/cosmos";
import {
SqlUserDefinedFunctionCreateUpdateParameters,
SqlUserDefinedFunctionResource,
SqlUserDefinedFunctionResource
} from "../../Utils/arm/generatedClients/2020-04-01/types";
import { client } from "../CosmosClient";
import {
createUpdateSqlUserDefinedFunction,
getSqlUserDefinedFunction,
getSqlUserDefinedFunction
} from "../../Utils/arm/generatedClients/2020-04-01/sqlResources";
import { handleError } from "../ErrorHandlingUtils";
import { logConsoleProgress } from "../../Utils/NotificationConsoleUtils";
@@ -49,8 +49,8 @@ export async function createUserDefinedFunction(
const createUDFParams: SqlUserDefinedFunctionCreateUpdateParameters = {
properties: {
resource: userDefinedFunction as SqlUserDefinedFunctionResource,
options: {},
},
options: {}
}
};
const rpResponse = await createUpdateSqlUserDefinedFunction(
userContext.subscriptionId,

View File

@@ -13,9 +13,9 @@ describe("deleteCollection", () => {
beforeAll(() => {
updateUserContext({
databaseAccount: {
name: "test",
name: "test"
} as DatabaseAccount,
defaultExperience: DefaultAccountExperienceType.DocumentDB,
defaultExperience: DefaultAccountExperienceType.DocumentDB
});
});
@@ -32,11 +32,11 @@ describe("deleteCollection", () => {
return {
container: () => {
return {
delete: (): unknown => undefined,
delete: (): unknown => undefined
};
},
}
};
},
}
});
await deleteCollection("database", "collection");
expect(client).toHaveBeenCalled();

View File

@@ -16,7 +16,10 @@ export async function deleteCollection(databaseId: string, collectionId: string)
if (window.authType === AuthType.AAD && !userContext.useSDKOperations) {
await deleteCollectionWithARM(databaseId, collectionId);
} else {
await client().database(databaseId).container(collectionId).delete();
await client()
.database(databaseId)
.container(collectionId)
.delete();
}
logConsoleInfo(`Successfully deleted container ${collectionId}`);
} catch (error) {

View File

@@ -0,0 +1,36 @@
import ConflictId from "../../Explorer/Tree/ConflictId";
import { CollectionBase } from "../../Contracts/ViewModels";
import { RequestOptions } from "@azure/cosmos";
import { client } from "../CosmosClient";
import { handleError } from "../ErrorHandlingUtils";
import { logConsoleProgress, logConsoleInfo } from "../../Utils/NotificationConsoleUtils";
export const deleteConflict = async (collection: CollectionBase, conflictId: ConflictId): Promise<void> => {
const clearMessage = logConsoleProgress(`Deleting conflict ${conflictId.id()}`);
try {
const options = {
partitionKey: getPartitionKeyHeaderForConflict(conflictId)
};
await client()
.database(collection.databaseId)
.container(collection.id())
.conflict(conflictId.id())
.delete(options as RequestOptions);
logConsoleInfo(`Successfully deleted conflict ${conflictId.id()}`);
} catch (error) {
handleError(error, "DeleteConflict", `Error while deleting conflict ${conflictId.id()}`);
throw error;
} finally {
clearMessage();
}
};
const getPartitionKeyHeaderForConflict = (conflictId: ConflictId): unknown => {
if (!conflictId.partitionKey) {
return undefined;
}
return conflictId.partitionKeyValue === undefined ? [{}] : [conflictId.partitionKeyValue];
};

View File

@@ -13,9 +13,9 @@ describe("deleteDatabase", () => {
beforeAll(() => {
updateUserContext({
databaseAccount: {
name: "test",
name: "test"
} as DatabaseAccount,
defaultExperience: DefaultAccountExperienceType.DocumentDB,
defaultExperience: DefaultAccountExperienceType.DocumentDB
});
});
@@ -30,9 +30,9 @@ describe("deleteDatabase", () => {
(client as jest.Mock).mockReturnValue({
database: () => {
return {
delete: (): unknown => undefined,
delete: (): unknown => undefined
};
},
}
});
await deleteDatabase("database");
expect(client).toHaveBeenCalled();

View File

@@ -19,7 +19,9 @@ export async function deleteDatabase(databaseId: string): Promise<void> {
if (window.authType === AuthType.AAD && !userContext.useSDKOperations) {
await deleteDatabaseWithARM(databaseId);
} else {
await client().database(databaseId).delete();
await client()
.database(databaseId)
.delete();
}
logConsoleInfo(`Successfully deleted database ${databaseId}`);
} catch (error) {

View File

@@ -0,0 +1,25 @@
import { CollectionBase } from "../../Contracts/ViewModels";
import { client } from "../CosmosClient";
import { getEntityName } from "../DocumentUtility";
import { handleError } from "../ErrorHandlingUtils";
import { logConsoleInfo, logConsoleProgress } from "../../Utils/NotificationConsoleUtils";
import DocumentId from "../../Explorer/Tree/DocumentId";
export const deleteDocument = async (collection: CollectionBase, documentId: DocumentId): Promise<void> => {
const entityName: string = getEntityName();
const clearMessage = logConsoleProgress(`Deleting ${entityName} ${documentId.id()}`);
try {
await client()
.database(collection.databaseId)
.container(collection.id())
.item(documentId.id(), documentId.partitionKeyValue)
.delete();
logConsoleInfo(`Successfully deleted ${entityName} ${documentId.id()}`);
} catch (error) {
handleError(error, "DeleteDocument", `Error while deleting ${entityName} ${documentId.id()}`);
throw error;
} finally {
clearMessage();
}
};

View File

@@ -27,7 +27,11 @@ export async function deleteStoredProcedure(
storedProcedureId
);
} else {
await client().database(databaseId).container(collectionId).scripts.storedProcedure(storedProcedureId).delete();
await client()
.database(databaseId)
.container(collectionId)
.scripts.storedProcedure(storedProcedureId)
.delete();
}
} catch (error) {
handleError(error, "DeleteStoredProcedure", `Error while deleting stored procedure ${storedProcedureId}`);

View File

@@ -23,7 +23,11 @@ export async function deleteTrigger(databaseId: string, collectionId: string, tr
triggerId
);
} else {
await client().database(databaseId).container(collectionId).scripts.trigger(triggerId).delete();
await client()
.database(databaseId)
.container(collectionId)
.scripts.trigger(triggerId)
.delete();
}
} catch (error) {
handleError(error, "DeleteTrigger", `Error while deleting trigger ${triggerId}`);

View File

@@ -23,7 +23,11 @@ export async function deleteUserDefinedFunction(databaseId: string, collectionId
id
);
} else {
await client().database(databaseId).container(collectionId).scripts.userDefinedFunction(id).delete();
await client()
.database(databaseId)
.container(collectionId)
.scripts.userDefinedFunction(id)
.delete();
}
} catch (error) {
handleError(error, "DeleteUserDefinedFunction", `Error while deleting user defined function ${id}`);

View File

@@ -0,0 +1,48 @@
import { Collection } from "../../Contracts/ViewModels";
import { ClientDefaults, HttpHeaders } from "../Constants";
import { client } from "../CosmosClient";
import { handleError } from "../ErrorHandlingUtils";
import { logConsoleProgress, logConsoleInfo } from "../../Utils/NotificationConsoleUtils";
import StoredProcedure from "../../Explorer/Tree/StoredProcedure";
export interface ExecuteSprocResult {
result: StoredProcedure;
scriptLogs: string;
}
export const executeStoredProcedure = async (
collection: Collection,
storedProcedure: StoredProcedure,
partitionKeyValue: string,
params: string[]
): Promise<ExecuteSprocResult> => {
const clearMessage = logConsoleProgress(`Executing stored procedure ${storedProcedure.id()}`);
const timeout = setTimeout(() => {
throw Error(`Request timed out while executing stored procedure ${storedProcedure.id()}`);
}, ClientDefaults.requestTimeoutMs);
try {
const response = await client()
.database(collection.databaseId)
.container(collection.id())
.scripts.storedProcedure(storedProcedure.id())
.execute(partitionKeyValue, params, { enableScriptLogging: true });
clearTimeout(timeout);
logConsoleInfo(
`Finished executing stored procedure ${storedProcedure.id()} for container ${storedProcedure.collection.id()}`
);
return {
result: response.resource,
scriptLogs: response.headers[HttpHeaders.scriptLogResults] as string
};
} catch (error) {
handleError(
error,
"ExecuteStoredProcedure",
`Failed to execute stored procedure ${storedProcedure.id()} for container ${storedProcedure.collection.id()}`
);
throw error;
} finally {
clearMessage();
}
};

View File

@@ -1,3 +1,4 @@
import { AuthType } from "../../AuthType";
import { armRequest } from "../../Utils/arm/request";
import { configContext } from "../../ConfigContext";
import { handleError } from "../ErrorHandlingUtils";
@@ -40,6 +41,10 @@ interface MetricsResponse {
}
export const getCollectionUsageSizeInKB = async (databaseName: string, containerName: string): Promise<number> => {
if (window.authType !== AuthType.AAD) {
return undefined;
}
const subscriptionId = userContext.subscriptionId;
const resourceGroup = userContext.resourceGroup;
const accountName = userContext.databaseAccount.name;
@@ -55,8 +60,8 @@ export const getCollectionUsageSizeInKB = async (databaseName: string, container
apiVersion: "2018-01-01",
queryParams: {
filter,
metricNames,
},
metricNames
}
});
if (metricsResponse?.value?.length !== 2) {

View File

@@ -11,7 +11,10 @@ export async function getIndexTransformationProgress(databaseId: string, collect
let indexTransformationPercentage: number;
const clearMessage = logConsoleProgress(`Reading container ${collectionId}`);
try {
const response = await client().database(databaseId).container(collectionId).read({ populateQuotaInfo: true });
const response = await client()
.database(databaseId)
.container(collectionId)
.read({ populateQuotaInfo: true });
indexTransformationPercentage = parseInt(
response.headers[Constants.HttpHeaders.collectionIndexTransformationProgress] as string

View File

@@ -0,0 +1,14 @@
import { ConflictDefinition, FeedOptions, QueryIterator, Resource } from "@azure/cosmos";
import { client } from "../CosmosClient";
export const queryConflicts = (
databaseId: string,
containerId: string,
query: string,
options: FeedOptions
): QueryIterator<ConflictDefinition & Resource> => {
return client()
.database(databaseId)
.container(containerId)
.conflicts.query(query, options);
};

View File

@@ -1,5 +1,5 @@
import { getCommonQueryOptions } from "./DataAccessUtilityBase";
import { LocalStorageUtility, StorageKey } from "../Shared/StorageUtility";
import { getCommonQueryOptions } from "./queryDocuments";
import { LocalStorageUtility, StorageKey } from "../../Shared/StorageUtility";
describe("getCommonQueryOptions", () => {
it("builds the correct default options objects", () => {

View File

@@ -0,0 +1,34 @@
import { Queries } from "../Constants";
import { FeedOptions, ItemDefinition, QueryIterator, Resource } from "@azure/cosmos";
import { LocalStorageUtility, StorageKey } from "../../Shared/StorageUtility";
import { client } from "../CosmosClient";
export const queryDocuments = (
databaseId: string,
containerId: string,
query: string,
options: FeedOptions
): QueryIterator<ItemDefinition & Resource> => {
options = getCommonQueryOptions(options);
return client()
.database(databaseId)
.container(containerId)
.items.query(query, options);
};
export const getCommonQueryOptions = (options: FeedOptions): FeedOptions => {
const storedItemPerPageSetting: number = LocalStorageUtility.getEntryNumber(StorageKey.ActualItemPerPage);
options = options || {};
options.populateQueryMetrics = true;
options.enableScanInQuery = options.enableScanInQuery || true;
if (!options.partitionKey) {
options.forceQueryPlan = true;
}
options.maxItemCount =
options.maxItemCount ||
(storedItemPerPageSetting !== undefined && storedItemPerPageSetting) ||
Queries.itemsPerPage;
options.maxDegreeOfParallelism = LocalStorageUtility.getEntryNumber(StorageKey.MaxDegreeOfParellism);
return options;
};

View File

@@ -0,0 +1,26 @@
import { QueryResults } from "../../Contracts/ViewModels";
import { logConsoleInfo, logConsoleProgress } from "../../Utils/NotificationConsoleUtils";
import { MinimalQueryIterator, nextPage } from "../IteratorUtilities";
import { handleError } from "../ErrorHandlingUtils";
import { getEntityName } from "../DocumentUtility";
export const queryDocumentsPage = async (
resourceName: string,
documentsIterator: MinimalQueryIterator,
firstItemIndex: number
): Promise<QueryResults> => {
const entityName = getEntityName();
const clearMessage = logConsoleProgress(`Querying ${entityName} for container ${resourceName}`);
try {
const result: QueryResults = await nextPage(documentsIterator, firstItemIndex);
const itemCount = (result.documents && result.documents.length) || 0;
logConsoleInfo(`Successfully fetched ${itemCount} ${entityName} for container ${resourceName}`);
return result;
} catch (error) {
handleError(error, "QueryDocumentsPage", `Failed to query ${entityName} for container ${resourceName}`);
throw error;
} finally {
clearMessage();
}
};

View File

@@ -10,9 +10,9 @@ describe("readCollection", () => {
beforeAll(() => {
updateUserContext({
databaseAccount: {
name: "test",
name: "test"
} as DatabaseAccount,
defaultExperience: DefaultAccountExperienceType.DocumentDB,
defaultExperience: DefaultAccountExperienceType.DocumentDB
});
});
@@ -23,11 +23,11 @@ describe("readCollection", () => {
return {
container: () => {
return {
read: (): unknown => ({}),
read: (): unknown => ({})
};
},
}
};
},
}
});
await readCollection("database", "collection");
expect(client).toHaveBeenCalled();

View File

@@ -7,7 +7,10 @@ export async function readCollection(databaseId: string, collectionId: string):
let collection: DataModels.Collection;
const clearMessage = logConsoleProgress(`Querying container ${collectionId}`);
try {
const response = await client().database(databaseId).container(collectionId).read();
const response = await client()
.database(databaseId)
.container(collectionId)
.read();
collection = response.resource as DataModels.Collection;
} catch (error) {
handleError(error, "ReadCollection", `Error while querying container ${collectionId}`);

View File

@@ -106,6 +106,7 @@ const readCollectionOfferWithARM = async (databaseId: string, collectionId: stri
autoscaleMaxThroughput: autoscaleSettings.maxThroughput,
manualThroughput: undefined,
minimumThroughput,
offerReplacePending: resource.offerReplacePending === "true"
};
}
@@ -114,6 +115,7 @@ const readCollectionOfferWithARM = async (databaseId: string, collectionId: stri
autoscaleMaxThroughput: undefined,
manualThroughput: resource.throughput,
minimumThroughput,
offerReplacePending: resource.offerReplacePending === "true"
};
}

View File

@@ -12,9 +12,9 @@ describe("readCollections", () => {
beforeAll(() => {
updateUserContext({
databaseAccount: {
name: "test",
name: "test"
} as DatabaseAccount,
defaultExperience: DefaultAccountExperienceType.DocumentDB,
defaultExperience: DefaultAccountExperienceType.DocumentDB
});
});
@@ -32,12 +32,12 @@ describe("readCollections", () => {
containers: {
readAll: () => {
return {
fetchAll: (): unknown => [],
fetchAll: (): unknown => []
};
},
},
}
}
};
},
}
});
await readCollections("database");
expect(client).toHaveBeenCalled();

View File

@@ -23,7 +23,10 @@ export async function readCollections(databaseId: string): Promise<DataModels.Co
return await readCollectionsWithARM(databaseId);
}
const sdkResponse = await client().database(databaseId).containers.readAll().fetchAll();
const sdkResponse = await client()
.database(databaseId)
.containers.readAll()
.fetchAll();
return sdkResponse.resources as DataModels.Collection[];
} catch (error) {
handleError(error, "ReadCollections", `Error while querying containers for database ${databaseId}`);
@@ -60,5 +63,5 @@ async function readCollectionsWithARM(databaseId: string): Promise<DataModels.Co
throw new Error(`Unsupported default experience type: ${defaultExperience}`);
}
return rpResponse?.value?.map((collection) => collection.properties?.resource as DataModels.Collection);
return rpResponse?.value?.map(collection => collection.properties?.resource as DataModels.Collection);
}

View File

@@ -78,6 +78,7 @@ const readDatabaseOfferWithARM = async (databaseId: string): Promise<Offer> => {
autoscaleMaxThroughput: autoscaleSettings.maxThroughput,
manualThroughput: undefined,
minimumThroughput,
offerReplacePending: resource.offerReplacePending === "true"
};
}
@@ -86,6 +87,7 @@ const readDatabaseOfferWithARM = async (databaseId: string): Promise<Offer> => {
autoscaleMaxThroughput: undefined,
manualThroughput: resource.throughput,
minimumThroughput,
offerReplacePending: resource.offerReplacePending === "true"
};
}

View File

@@ -12,9 +12,9 @@ describe("readDatabases", () => {
beforeAll(() => {
updateUserContext({
databaseAccount: {
name: "test",
name: "test"
} as DatabaseAccount,
defaultExperience: DefaultAccountExperienceType.DocumentDB,
defaultExperience: DefaultAccountExperienceType.DocumentDB
});
});
@@ -30,10 +30,10 @@ describe("readDatabases", () => {
databases: {
readAll: () => {
return {
fetchAll: (): unknown => [],
fetchAll: (): unknown => []
};
},
},
}
}
});
await readDatabases();
expect(client).toHaveBeenCalled();

View File

@@ -21,7 +21,9 @@ export async function readDatabases(): Promise<DataModels.Database[]> {
) {
databases = await readDatabasesWithARM();
} else {
const sdkResponse = await client().databases.readAll().fetchAll();
const sdkResponse = await client()
.databases.readAll()
.fetchAll();
databases = sdkResponse.resources as DataModels.Database[];
}
} catch (error) {
@@ -56,5 +58,5 @@ async function readDatabasesWithARM(): Promise<DataModels.Database[]> {
throw new Error(`Unsupported default experience type: ${defaultExperience}`);
}
return rpResponse?.value?.map((database) => database.properties?.resource as DataModels.Database);
return rpResponse?.value?.map(database => database.properties?.resource as DataModels.Database);
}

View File

@@ -0,0 +1,27 @@
import { Item } from "@azure/cosmos";
import { CollectionBase } from "../../Contracts/ViewModels";
import { client } from "../CosmosClient";
import { getEntityName } from "../DocumentUtility";
import { handleError } from "../ErrorHandlingUtils";
import { logConsoleProgress } from "../../Utils/NotificationConsoleUtils";
import DocumentId from "../../Explorer/Tree/DocumentId";
export const readDocument = async (collection: CollectionBase, documentId: DocumentId): Promise<Item> => {
const entityName = getEntityName();
const clearMessage = logConsoleProgress(`Reading ${entityName} ${documentId.id()}`);
try {
const response = await client()
.database(collection.databaseId)
.container(collection.id())
.item(documentId.id(), documentId.partitionKeyValue)
.read();
return response?.resource;
} catch (error) {
handleError(error, "ReadDocument", `Failed to read ${entityName} ${documentId.id()}`);
throw error;
} finally {
clearMessage();
}
};

View File

@@ -8,7 +8,7 @@ import { readOffers } from "./readOffers";
export const readOfferWithSDK = async (offerId: string, resourceId: string): Promise<Offer> => {
if (!offerId) {
const offers = await readOffers();
const offer = offers.find((offer) => offer.resource === resourceId);
const offer = offers.find(offer => offer.resource === resourceId);
if (!offer) {
return undefined;
@@ -18,10 +18,12 @@ export const readOfferWithSDK = async (offerId: string, resourceId: string): Pro
const options: RequestOptions = {
initialHeaders: {
[HttpHeaders.populateCollectionThroughputInfo]: true,
},
[HttpHeaders.populateCollectionThroughputInfo]: true
}
};
const response = await client().offer(offerId).read(options);
const response = await client()
.offer(offerId)
.read(options);
return parseSDKOfferResponse(response);
};

View File

@@ -7,7 +7,9 @@ export const readOffers = async (): Promise<SDKOfferDefinition[]> => {
const clearMessage = logConsoleProgress(`Querying offers`);
try {
const response = await client().offers.readAll().fetchAll();
const response = await client()
.offers.readAll()
.fetchAll();
return response?.resources;
} catch (error) {
// This should be removed when we can correctly identify if an account is serverless when connected using connection string too.

View File

@@ -25,7 +25,7 @@ export async function readStoredProcedures(
databaseId,
collectionId
);
return rpResponse?.value?.map((sproc) => sproc.properties?.resource as StoredProcedureDefinition & Resource);
return rpResponse?.value?.map(sproc => sproc.properties?.resource as StoredProcedureDefinition & Resource);
}
const response = await client()

View File

@@ -25,10 +25,14 @@ export async function readTriggers(
databaseId,
collectionId
);
return rpResponse?.value?.map((trigger) => trigger.properties?.resource as TriggerDefinition & Resource);
return rpResponse?.value?.map(trigger => trigger.properties?.resource as TriggerDefinition & Resource);
}
const response = await client().database(databaseId).container(collectionId).scripts.triggers.readAll().fetchAll();
const response = await client()
.database(databaseId)
.container(collectionId)
.scripts.triggers.readAll()
.fetchAll();
return response?.resources;
} catch (error) {
handleError(error, "ReadTriggers", `Failed to query triggers for container ${collectionId}`);

View File

@@ -25,7 +25,7 @@ export async function readUserDefinedFunctions(
databaseId,
collectionId
);
return rpResponse?.value?.map((udf) => udf.properties?.resource as UserDefinedFunctionDefinition & Resource);
return rpResponse?.value?.map(udf => udf.properties?.resource as UserDefinedFunctionDefinition & Resource);
}
const response = await client()

View File

@@ -8,22 +8,22 @@ import {
MongoDBCollectionCreateUpdateParameters,
MongoDBCollectionResource,
SqlContainerCreateUpdateParameters,
SqlContainerResource,
SqlContainerResource
} from "../../Utils/arm/generatedClients/2020-04-01/types";
import { RequestOptions } from "@azure/cosmos/dist-esm";
import { client } from "../CosmosClient";
import { createUpdateSqlContainer, getSqlContainer } from "../../Utils/arm/generatedClients/2020-04-01/sqlResources";
import {
createUpdateCassandraTable,
getCassandraTable,
getCassandraTable
} from "../../Utils/arm/generatedClients/2020-04-01/cassandraResources";
import {
createUpdateMongoDBCollection,
getMongoDBCollection,
getMongoDBCollection
} from "../../Utils/arm/generatedClients/2020-04-01/mongoDBResources";
import {
createUpdateGremlinGraph,
getGremlinGraph,
getGremlinGraph
} from "../../Utils/arm/generatedClients/2020-04-01/gremlinResources";
import { createUpdateTable, getTable } from "../../Utils/arm/generatedClients/2020-04-01/tableResources";
import { handleError } from "../ErrorHandlingUtils";
@@ -130,8 +130,8 @@ export async function updateMongoDBCollectionThroughRP(
const updateParams: MongoDBCollectionCreateUpdateParameters = {
properties: {
resource: newCollection,
options: updateOptions,
},
options: updateOptions
}
};
const updateResponse = await createUpdateMongoDBCollection(

View File

@@ -0,0 +1,32 @@
import { CollectionBase } from "../../Contracts/ViewModels";
import { Item } from "@azure/cosmos";
import { client } from "../CosmosClient";
import { getEntityName } from "../DocumentUtility";
import { handleError } from "../ErrorHandlingUtils";
import { logConsoleInfo, logConsoleProgress } from "../../Utils/NotificationConsoleUtils";
import DocumentId from "../../Explorer/Tree/DocumentId";
export const updateDocument = async (
collection: CollectionBase,
documentId: DocumentId,
newDocument: Item
): Promise<Item> => {
const entityName = getEntityName();
const clearMessage = logConsoleProgress(`Updating ${entityName} ${documentId.id()}`);
try {
const response = await client()
.database(collection.databaseId)
.container(collection.id())
.item(documentId.id(), documentId.partitionKeyValue)
.replace(newDocument);
logConsoleInfo(`Successfully updated ${entityName} ${documentId.id()}`);
return response?.resource;
} catch (error) {
handleError(error, "UpdateDocument", `Failed to update ${entityName} ${documentId.id()}`);
throw error;
} finally {
clearMessage();
}
};

View File

@@ -17,7 +17,7 @@ import {
migrateSqlDatabaseToManualThroughput,
migrateSqlContainerToAutoscale,
migrateSqlContainerToManualThroughput,
updateSqlContainerThroughput,
updateSqlContainerThroughput
} from "../../Utils/arm/generatedClients/2020-04-01/sqlResources";
import {
updateCassandraKeyspaceThroughput,
@@ -25,7 +25,7 @@ import {
migrateCassandraKeyspaceToManualThroughput,
migrateCassandraTableToAutoscale,
migrateCassandraTableToManualThroughput,
updateCassandraTableThroughput,
updateCassandraTableThroughput
} from "../../Utils/arm/generatedClients/2020-04-01/cassandraResources";
import {
updateMongoDBDatabaseThroughput,
@@ -33,7 +33,7 @@ import {
migrateMongoDBDatabaseToManualThroughput,
migrateMongoDBCollectionToAutoscale,
migrateMongoDBCollectionToManualThroughput,
updateMongoDBCollectionThroughput,
updateMongoDBCollectionThroughput
} from "../../Utils/arm/generatedClients/2020-04-01/mongoDBResources";
import {
updateGremlinDatabaseThroughput,
@@ -41,13 +41,13 @@ import {
migrateGremlinDatabaseToManualThroughput,
migrateGremlinGraphToAutoscale,
migrateGremlinGraphToManualThroughput,
updateGremlinGraphThroughput,
updateGremlinGraphThroughput
} from "../../Utils/arm/generatedClients/2020-04-01/gremlinResources";
import { userContext } from "../../UserContext";
import {
migrateTableToAutoscale,
migrateTableToManualThroughput,
updateTableThroughput,
updateTableThroughput
} from "../../Utils/arm/generatedClients/2020-04-01/tableResources";
export const updateOffer = async (params: UpdateOfferParams): Promise<Offer> => {
@@ -110,7 +110,7 @@ const updateCollectionOfferWithARM = async (params: UpdateOfferParams): Promise<
return await readCollectionOffer({
collectionId: params.collectionId,
databaseId: params.databaseId,
offerId: params.currentOffer.id,
offerId: params.currentOffer.id
});
};
@@ -140,7 +140,7 @@ const updateDatabaseOfferWithARM = async (params: UpdateOfferParams): Promise<Of
return await readDatabaseOffer({
databaseId: params.databaseId,
offerId: params.currentOffer.id,
offerId: params.currentOffer.id
});
};
@@ -358,13 +358,13 @@ const updateGremlinDatabaseOffer = async (params: UpdateOfferParams): Promise<vo
const createUpdateOfferBody = (params: UpdateOfferParams): ThroughputSettingsUpdateParameters => {
const body: ThroughputSettingsUpdateParameters = {
properties: {
resource: {},
},
resource: {}
}
};
if (params.autopilotThroughput) {
body.properties.resource.autoscaleSettings = {
maxThroughput: params.autopilotThroughput,
maxThroughput: params.autopilotThroughput
};
} else {
body.properties.resource.throughput = params.manualThroughput;
@@ -378,7 +378,7 @@ const updateOfferWithSDK = async (params: UpdateOfferParams): Promise<Offer> =>
const newOffer: SDKOfferDefinition = {
content: {
offerThroughput: undefined,
offerIsRUPerMinuteThroughputEnabled: false,
offerIsRUPerMinuteThroughputEnabled: false
},
_etag: undefined,
_ts: undefined,
@@ -388,12 +388,12 @@ const updateOfferWithSDK = async (params: UpdateOfferParams): Promise<Offer> =>
offerResourceId: sdkOfferDefinition.offerResourceId,
offerVersion: sdkOfferDefinition.offerVersion,
offerType: sdkOfferDefinition.offerType,
resource: sdkOfferDefinition.resource,
resource: sdkOfferDefinition.resource
};
if (params.autopilotThroughput) {
newOffer.content.offerAutopilotSettings = {
maxThroughput: params.autopilotThroughput,
maxThroughput: params.autopilotThroughput
};
} else {
newOffer.content.offerThroughput = params.manualThroughput;
@@ -402,12 +402,12 @@ const updateOfferWithSDK = async (params: UpdateOfferParams): Promise<Offer> =>
const options: RequestOptions = {};
if (params.migrateToAutoPilot) {
options.initialHeaders = {
[HttpHeaders.migrateOfferToAutopilot]: "true",
[HttpHeaders.migrateOfferToAutopilot]: "true"
};
delete newOffer.content.offerAutopilotSettings;
} else if (params.migrateToManual) {
options.initialHeaders = {
[HttpHeaders.migrateOfferToManualThroughput]: "true",
[HttpHeaders.migrateOfferToManualThroughput]: "true"
};
newOffer.content.offerAutopilotSettings = { maxThroughput: 0 };
}

View File

@@ -3,12 +3,12 @@ import { DefaultAccountExperienceType } from "../../DefaultAccountExperienceType
import { Resource, StoredProcedureDefinition } from "@azure/cosmos";
import {
SqlStoredProcedureCreateUpdateParameters,
SqlStoredProcedureResource,
SqlStoredProcedureResource
} from "../../Utils/arm/generatedClients/2020-04-01/types";
import { client } from "../CosmosClient";
import {
createUpdateSqlStoredProcedure,
getSqlStoredProcedure,
getSqlStoredProcedure
} from "../../Utils/arm/generatedClients/2020-04-01/sqlResources";
import { handleError } from "../ErrorHandlingUtils";
import { logConsoleProgress } from "../../Utils/NotificationConsoleUtils";
@@ -39,8 +39,8 @@ export async function updateStoredProcedure(
const createSprocParams: SqlStoredProcedureCreateUpdateParameters = {
properties: {
resource: storedProcedure as SqlStoredProcedureResource,
options: {},
},
options: {}
}
};
const rpResponse = await createUpdateSqlStoredProcedure(
userContext.subscriptionId,

View File

@@ -2,7 +2,7 @@ import { AuthType } from "../../AuthType";
import { DefaultAccountExperienceType } from "../../DefaultAccountExperienceType";
import {
SqlTriggerCreateUpdateParameters,
SqlTriggerResource,
SqlTriggerResource
} from "../../Utils/arm/generatedClients/2020-04-01/types";
import { TriggerDefinition } from "@azure/cosmos";
import { client } from "../CosmosClient";
@@ -36,8 +36,8 @@ export async function updateTrigger(
const createTriggerParams: SqlTriggerCreateUpdateParameters = {
properties: {
resource: trigger as SqlTriggerResource,
options: {},
},
options: {}
}
};
const rpResponse = await createUpdateSqlTrigger(
userContext.subscriptionId,

View File

@@ -3,12 +3,12 @@ import { DefaultAccountExperienceType } from "../../DefaultAccountExperienceType
import { Resource, UserDefinedFunctionDefinition } from "@azure/cosmos";
import {
SqlUserDefinedFunctionCreateUpdateParameters,
SqlUserDefinedFunctionResource,
SqlUserDefinedFunctionResource
} from "../../Utils/arm/generatedClients/2020-04-01/types";
import { client } from "../CosmosClient";
import {
createUpdateSqlUserDefinedFunction,
getSqlUserDefinedFunction,
getSqlUserDefinedFunction
} from "../../Utils/arm/generatedClients/2020-04-01/sqlResources";
import { handleError } from "../ErrorHandlingUtils";
import { logConsoleProgress } from "../../Utils/NotificationConsoleUtils";
@@ -39,8 +39,8 @@ export async function updateUserDefinedFunction(
const createUDFParams: SqlUserDefinedFunctionCreateUpdateParameters = {
properties: {
resource: userDefinedFunction as SqlUserDefinedFunctionResource,
options: {},
},
options: {}
}
};
const rpResponse = await createUpdateSqlUserDefinedFunction(
userContext.subscriptionId,

View File

@@ -1,7 +1,7 @@
export enum Platform {
Portal = "Portal",
Hosted = "Hosted",
Emulator = "Emulator",
Emulator = "Emulator"
}
interface ConfigContext {
@@ -37,7 +37,7 @@ let configContext: Readonly<ConfigContext> = {
`^https:\\/\\/[\\.\\w]*portal\\.microsoftazure.de$`,
`^https:\\/\\/[\\.\\w]*ext\\.azure\\.(com|cn|us)$`,
`^https:\\/\\/[\\.\\w]*\\.ext\\.microsoftazure\\.de$`,
`^https://cosmos-db-dataexplorer-germanycentral.azurewebsites.de$`,
`^https://cosmos-db-dataexplorer-germanycentral.azurewebsites.de$`
],
// Webpack injects this at build time
gitSha: process.env.GIT_SHA,
@@ -52,7 +52,7 @@ let configContext: Readonly<ConfigContext> = {
ARCADIA_LIVY_ENDPOINT_DNS_ZONE: "dev.azuresynapse.net",
GITHUB_CLIENT_ID: "6cb2f63cf6f7b5cbdeca", // Registered OAuth app: https://github.com/settings/applications/1189306
JUNO_ENDPOINT: "https://tools.cosmos.azure.com",
BACKEND_ENDPOINT: "https://main.documentdb.ext.azure.com",
BACKEND_ENDPOINT: "https://main.documentdb.ext.azure.com"
};
export function resetConfigContext(): void {
@@ -73,7 +73,7 @@ if (process.env.NODE_ENV === "development") {
BACKEND_ENDPOINT: "https://localhost:" + port,
MONGO_BACKEND_ENDPOINT: "https://localhost:" + port,
PROXY_PATH: "/proxy",
EMULATOR_ENDPOINT: "https://localhost:8081",
EMULATOR_ENDPOINT: "https://localhost:8081"
});
}
@@ -86,7 +86,7 @@ export async function initializeConfiguration(): Promise<ConfigContext> {
Object.assign(configContext, externalConfig);
if (allowedParentFrameOrigins && allowedParentFrameOrigins.length > 0) {
updateConfigContext({
allowedParentFrameOrigins: [...configContext.allowedParentFrameOrigins, ...allowedParentFrameOrigins],
allowedParentFrameOrigins: [...configContext.allowedParentFrameOrigins, ...allowedParentFrameOrigins]
});
}
} catch (error) {

View File

@@ -7,7 +7,7 @@ export enum TabKind {
TableEntities,
Graph,
SQLQuery,
ScaleSettings,
ScaleSettings
}
/**
@@ -20,7 +20,7 @@ export enum PaneKind {
DeleteDatabase,
GlobalSettings,
AdHocAccess,
SwitchDirectory,
SwitchDirectory
}
/**
@@ -79,5 +79,5 @@ export enum ActionType {
OpenCollectionTab,
OpenPane,
TransmitCachedData,
OpenSampleNotebook,
OpenSampleNotebook
}

View File

@@ -56,7 +56,7 @@ export enum ApiKind {
Table,
Cassandra,
Graph,
MongoDBCompute,
MongoDBCompute
}
export interface GenerateTokenResponse {
@@ -214,7 +214,7 @@ export interface Offer {
manualThroughput: number;
minimumThroughput: number;
offerDefinition?: SDKOfferDefinition;
headers?: any;
offerReplacePending: boolean;
}
export interface SDKOfferDefinition extends Resource {
@@ -334,7 +334,7 @@ export interface Notification {
export enum ConflictResolutionMode {
Custom = "Custom",
LastWriterWins = "LastWriterWins",
LastWriterWins = "LastWriterWins"
}
/**
@@ -472,7 +472,7 @@ export interface SparkClusterEndpoint {
export enum SparkClusterEndpointKind {
SparkUI = "SparkUI",
HistoryServerUI = "HistoryServerUI",
Livy = "Livy",
Livy = "Livy"
}
export interface RpParameters {

View File

@@ -21,7 +21,7 @@ export enum LogEntryLevel {
/**
* Error level.
*/
Error = 2,
Error = 2
}
/**
* Schema of a log entry.

View File

@@ -33,7 +33,7 @@ export enum MessageTypes {
CreateWorkspace,
CreateSparkPool,
RefreshDatabaseAccount,
InitTestExplorer,
InitTestExplorer
}
export { Versions, ActionContracts, Diagnostics };

View File

@@ -3,5 +3,5 @@ export enum SubscriptionType {
EA,
Free,
Internal,
PAYG,
PAYG
}

View File

@@ -3,7 +3,7 @@ import {
Resource,
StoredProcedureDefinition,
TriggerDefinition,
UserDefinedFunctionDefinition,
UserDefinedFunctionDefinition
} from "@azure/cosmos";
import Q from "q";
import { CommandButtonComponentProps } from "../Explorer/Controls/CommandButton/CommandButtonComponent";
@@ -195,7 +195,7 @@ export interface PaneOptions {
export enum NeighborType {
SOURCES_ONLY,
TARGETS_ONLY,
BOTH,
BOTH
}
/**
@@ -325,14 +325,14 @@ export enum DocumentExplorerState {
newDocumentInvalid,
exisitingDocumentNoEdits,
exisitingDocumentDirtyValid,
exisitingDocumentDirtyInvalid,
exisitingDocumentDirtyInvalid
}
export enum IndexingPolicyEditorState {
noCollectionSelected,
noEdits,
dirtyValid,
dirtyInvalid,
dirtyInvalid
}
export enum ScriptEditorState {
@@ -340,7 +340,7 @@ export enum ScriptEditorState {
newValid,
exisitingNoEdits,
exisitingDirtyValid,
exisitingDirtyInvalid,
exisitingDirtyInvalid
}
export enum CollectionTabKind {
@@ -362,13 +362,13 @@ export enum CollectionTabKind {
Gallery = 17,
NotebookViewer = 18,
Schema = 19,
SettingsV2 = 19,
SettingsV2 = 19
}
export enum TerminalKind {
Default = 0,
Mongo = 1,
Cassandra = 2,
Cassandra = 2
}
export interface DataExplorerInputsFrame {

View File

@@ -6,19 +6,19 @@ describe("The Heatmap Control", () => {
const dataPoints = {
"1": {
"2019-06-19T00:59:10Z": {
"Normalized Throughput": 0.35,
"Normalized Throughput": 0.35
},
"2019-06-19T00:48:10Z": {
"Normalized Throughput": 0.25,
},
},
"Normalized Throughput": 0.25
}
}
};
const chartCaptions = {
chartTitle: "chart title",
yAxisTitle: "YAxisTitle",
tooltipText: "Tooltip text",
timeWindow: 123456789,
timeWindow: 123456789
};
let heatmap: Heatmap;
@@ -75,12 +75,12 @@ describe("The Heatmap Control", () => {
if (dayjs().utcOffset()) {
expect(heatmap.generateMatrixFromMap(dataPoints).xAxisPoints).not.toEqual([
"2019-06-19T00:48:10Z",
"2019-06-19T00:59:10Z",
"2019-06-19T00:59:10Z"
]);
} else {
expect(heatmap.generateMatrixFromMap(dataPoints).xAxisPoints).toEqual([
"2019-06-19T00:48:10Z",
"2019-06-19T00:59:10Z",
"2019-06-19T00:59:10Z"
]);
}
});
@@ -106,9 +106,9 @@ describe("iframe rendering when there is no data", () => {
data: {
chartData: {},
chartSettings: {},
theme: 4,
},
},
theme: 4
}
}
};
const divElement: string = `<div id="${Heatmap.elementId}"></div>`;
@@ -126,9 +126,9 @@ describe("iframe rendering when there is no data", () => {
data: {
chartData: {},
chartSettings: {},
theme: 2,
},
},
theme: 2
}
}
};
const divElement: string = `<div id="${Heatmap.elementId}"></div>`;

View File

@@ -9,7 +9,7 @@ import {
HeatmapData,
LayoutSettings,
PartitionTimeStampToData,
PortalTheme,
PortalTheme
} from "./HeatmapDatatypes";
import { isInvalidParentFrameOrigin } from "../../Utils/MessageValidation";
import { sendCachedDataMessage, sendMessage } from "../../Common/MessageHandler";
@@ -43,7 +43,7 @@ export class Heatmap {
return {
family: StyleConstants.DataExplorerFont,
size,
color,
color
};
}
@@ -73,7 +73,7 @@ export class Heatmap {
return 0;
}
}
}),
})
};
// go thru all rows and create 2d matrix for heatmap...
for (let i = 0; i < rows.length; i++) {
@@ -115,7 +115,7 @@ export class Heatmap {
[0.7, "#E46612"],
[0.8, "#E64914"],
[0.9, "#B80016"],
[1.0, "#B80016"],
[1.0, "#B80016"]
],
name: "",
hovertemplate: this._heatmapCaptions.tooltipText,
@@ -123,11 +123,11 @@ export class Heatmap {
thickness: 15,
outlinewidth: 0,
tickcolor: StyleConstants.BaseDark,
tickfont: this._getFontStyles(10, this._defaultFontColor),
tickfont: this._getFontStyles(10, this._defaultFontColor)
},
y: this._chartData.yAxisPoints,
x: this._chartData.xAxisPoints,
},
x: this._chartData.xAxisPoints
}
];
}
@@ -138,7 +138,7 @@ export class Heatmap {
r: 10,
b: 35,
t: 30,
pad: 0,
pad: 0
},
paper_bgcolor: "transparent",
plot_bgcolor: "transparent",
@@ -154,7 +154,7 @@ export class Heatmap {
autotick: true,
fixedrange: true,
ticks: "",
showticklabels: false,
showticklabels: false
},
xaxis: {
fixedrange: true,
@@ -167,13 +167,13 @@ export class Heatmap {
autotick: true,
tickformat: this._heatmapCaptions.timeWindow > 7 ? "%I:%M %p" : "%b %e",
showticklabels: true,
tickfont: this._getFontStyles(10),
tickfont: this._getFontStyles(10)
},
title: {
text: this._heatmapCaptions.chartTitle,
x: 0.01,
font: this._getFontStyles(13, this._defaultFontColor),
},
font: this._getFontStyles(13, this._defaultFontColor)
}
};
}
@@ -181,7 +181,7 @@ export class Heatmap {
return {
/* heatmap can be fully responsive however the min-height needed in that case is greater than the iframe portal height, hence explicit width + height have been set in _getLayoutSettings
responsive: true,*/
displayModeBar: false,
displayModeBar: false
};
}

View File

@@ -8,7 +8,7 @@ export enum PortalTheme {
blue = 1,
azure,
light,
dark,
dark
}
export interface HeatmapData {

View File

@@ -4,5 +4,5 @@ export enum DefaultAccountExperienceType {
MongoDB = "MongoDB",
Table = "Table",
Cassandra = "Cassandra",
ApiForMongoDB = "Azure Cosmos DB for MongoDB API",
ApiForMongoDB = "Azure Cosmos DB for MongoDB API"
}

View File

@@ -36,8 +36,8 @@ export class ResourceTreeContextMenuButtonFactory {
{
iconSrc: AddCollectionIcon,
onClick: () => container.onNewCollectionClicked(),
label: container.addCollectionText(),
},
label: container.addCollectionText()
}
];
if (userContext.defaultExperience !== DefaultAccountExperienceType.Table) {
@@ -45,7 +45,7 @@ export class ResourceTreeContextMenuButtonFactory {
iconSrc: DeleteDatabaseIcon,
onClick: () => container.deleteDatabaseConfirmationPane.open(),
label: container.deleteDatabaseText(),
styleClass: "deleteDatabaseMenuItem",
styleClass: "deleteDatabaseMenuItem"
});
}
return items;
@@ -60,7 +60,7 @@ export class ResourceTreeContextMenuButtonFactory {
items.push({
iconSrc: AddSqlQueryIcon,
onClick: () => selectedCollection && selectedCollection.onNewQueryClick(selectedCollection, null),
label: "New SQL Query",
label: "New SQL Query"
});
}
@@ -68,7 +68,7 @@ export class ResourceTreeContextMenuButtonFactory {
items.push({
iconSrc: AddSqlQueryIcon,
onClick: () => selectedCollection && selectedCollection.onNewMongoQueryClick(selectedCollection, null),
label: "New Query",
label: "New Query"
});
items.push({
@@ -77,7 +77,7 @@ export class ResourceTreeContextMenuButtonFactory {
const selectedCollection: ViewModels.Collection = container.findSelectedCollection();
selectedCollection && selectedCollection.onNewMongoShellClick();
},
label: "New Shell",
label: "New Shell"
});
}
@@ -88,7 +88,7 @@ export class ResourceTreeContextMenuButtonFactory {
const selectedCollection: ViewModels.Collection = container.findSelectedCollection();
selectedCollection && selectedCollection.onNewStoredProcedureClick(selectedCollection, null);
},
label: "New Stored Procedure",
label: "New Stored Procedure"
});
items.push({
@@ -97,7 +97,7 @@ export class ResourceTreeContextMenuButtonFactory {
const selectedCollection: ViewModels.Collection = container.findSelectedCollection();
selectedCollection && selectedCollection.onNewUserDefinedFunctionClick(selectedCollection, null);
},
label: "New UDF",
label: "New UDF"
});
items.push({
@@ -106,7 +106,7 @@ export class ResourceTreeContextMenuButtonFactory {
const selectedCollection: ViewModels.Collection = container.findSelectedCollection();
selectedCollection && selectedCollection.onNewTriggerClick(selectedCollection, null);
},
label: "New Trigger",
label: "New Trigger"
});
}
@@ -117,7 +117,7 @@ export class ResourceTreeContextMenuButtonFactory {
selectedCollection && selectedCollection.onDeleteCollectionContextMenuClick(selectedCollection, null);
},
label: container.deleteCollectionText(),
styleClass: "deleteCollectionMenuItem",
styleClass: "deleteCollectionMenuItem"
});
return items;
@@ -135,8 +135,8 @@ export class ResourceTreeContextMenuButtonFactory {
{
iconSrc: DeleteSprocIcon,
onClick: () => storedProcedure.delete(),
label: "Delete Store Procedure",
},
label: "Delete Store Procedure"
}
];
}
@@ -149,8 +149,8 @@ export class ResourceTreeContextMenuButtonFactory {
{
iconSrc: DeleteTriggerIcon,
onClick: () => trigger.delete(),
label: "Delete Trigger",
},
label: "Delete Trigger"
}
];
}
@@ -166,8 +166,8 @@ export class ResourceTreeContextMenuButtonFactory {
{
iconSrc: DeleteUDFIcon,
onClick: () => userDefinedFunction.delete(),
label: "Delete User Defined Function",
},
label: "Delete User Defined Function"
}
];
}
}

View File

@@ -31,7 +31,7 @@ export class AccessibleElement extends React.Component<AccessibleElementProps> {
...elementProps,
onKeyPress: this.onKeyPress,
onClick: this.props.onActivated,
tabIndex,
tabIndex
});
}
}

View File

@@ -38,7 +38,7 @@ export class AccordionItemComponent extends React.Component<AccordionItemCompone
super(props);
this.isExpanded = props.isExpanded;
this.state = {
isExpanded: true,
isExpanded: true
};
}
@@ -46,7 +46,7 @@ export class AccordionItemComponent extends React.Component<AccordionItemCompone
if (this.props.isExpanded !== this.isExpanded) {
this.isExpanded = this.props.isExpanded;
this.setState({
isExpanded: this.props.isExpanded,
isExpanded: this.props.isExpanded
});
}
}

View File

@@ -16,7 +16,7 @@ const createBlankProps = (): AccountSwitchComponentProps => {
subscriptions: [],
selectedSubscriptionId: null,
isLoadingSubscriptions: false,
onSubscriptionChange: jest.fn(),
onSubscriptionChange: jest.fn()
};
};
@@ -28,7 +28,7 @@ const createBlankAccount = (): DatabaseAccount => {
properties: null,
location: "",
tags: null,
type: "",
type: ""
};
};
@@ -40,7 +40,7 @@ const createBlankSubscription = (): Subscription => {
state: "",
subscriptionPolicies: null,
tenantId: "",
uniqueDisplayName: "",
uniqueDisplayName: ""
};
};

View File

@@ -34,13 +34,13 @@ export class AccountSwitchComponent extends React.Component<AccountSwitchCompone
items: [
{
key: "switchSubscription",
onRender: this._renderSubscriptionDropdown.bind(this),
onRender: this._renderSubscriptionDropdown.bind(this)
},
{
key: "switchAccount",
onRender: this._renderAccountDropDown.bind(this),
},
],
onRender: this._renderAccountDropDown.bind(this)
}
]
};
const buttonStyles: IButtonStyles = {
@@ -51,27 +51,27 @@ export class AccountSwitchComponent extends React.Component<AccountSwitchCompone
paddingLeft: 10,
marginRight: 5,
backgroundColor: StyleConstants.BaseDark,
color: StyleConstants.BaseLight,
color: StyleConstants.BaseLight
},
rootHovered: {
backgroundColor: StyleConstants.BaseHigh,
color: StyleConstants.BaseLight,
color: StyleConstants.BaseLight
},
rootFocused: {
backgroundColor: StyleConstants.BaseHigh,
color: StyleConstants.BaseLight,
color: StyleConstants.BaseLight
},
rootPressed: {
backgroundColor: StyleConstants.BaseHigh,
color: StyleConstants.BaseLight,
color: StyleConstants.BaseLight
},
rootExpanded: {
backgroundColor: StyleConstants.BaseHigh,
color: StyleConstants.BaseLight,
color: StyleConstants.BaseLight
},
textContainer: {
flexGrow: "initial",
},
flexGrow: "initial"
}
};
const buttonProps: IButtonProps = {
@@ -79,7 +79,7 @@ export class AccountSwitchComponent extends React.Component<AccountSwitchCompone
menuProps: menuProps,
styles: buttonStyles,
className: "accountSwitchButton",
id: "accountSwitchButton",
id: "accountSwitchButton"
};
return <DefaultButton {...buttonProps} />;
@@ -87,11 +87,11 @@ export class AccountSwitchComponent extends React.Component<AccountSwitchCompone
private _renderSubscriptionDropdown(): JSX.Element {
const { subscriptions, selectedSubscriptionId, isLoadingSubscriptions } = this.props;
const options: IDropdownOption[] = subscriptions.map((sub) => {
const options: IDropdownOption[] = subscriptions.map(sub => {
return {
key: sub.subscriptionId,
text: sub.displayName,
data: sub,
data: sub
};
});
@@ -109,8 +109,8 @@ export class AccountSwitchComponent extends React.Component<AccountSwitchCompone
defaultSelectedKey: selectedSubscriptionId,
placeholder: placeHolderText,
styles: {
callout: "accountSwitchSubscriptionDropdownMenu",
},
callout: "accountSwitchSubscriptionDropdownMenu"
}
};
return <Dropdown {...dropdownProps} />;
@@ -126,11 +126,11 @@ export class AccountSwitchComponent extends React.Component<AccountSwitchCompone
private _renderAccountDropDown(): JSX.Element {
const { accounts, selectedAccountName, isLoadingAccounts } = this.props;
const options: IDropdownOption[] = accounts.map((account) => {
const options: IDropdownOption[] = accounts.map(account => {
return {
key: account.name,
text: account.name,
data: account,
data: account
};
});
// Fabric UI will also try to select the first non-disabled option from dropdown.
@@ -138,7 +138,7 @@ export class AccountSwitchComponent extends React.Component<AccountSwitchCompone
options.unshift({
key: "select from list",
text: "Select Cosmos DB account from list",
data: undefined,
data: undefined
});
const placeHolderText = isLoadingAccounts
@@ -155,8 +155,8 @@ export class AccountSwitchComponent extends React.Component<AccountSwitchCompone
defaultSelectedKey: selectedAccountName,
placeholder: placeHolderText,
styles: {
callout: "accountSwitchAccountDropdownMenu",
},
callout: "accountSwitchAccountDropdownMenu"
}
};
return <Dropdown {...dropdownProps} />;

View File

@@ -30,7 +30,7 @@ export class ArcadiaMenuPicker extends React.Component<ArcadiaMenuPickerProps, A
constructor(props: ArcadiaMenuPickerProps) {
super(props);
this.state = {
selectedSparkPool: props.selectedSparkPool,
selectedSparkPool: props.selectedSparkPool
};
}
@@ -41,7 +41,7 @@ export class ArcadiaMenuPicker extends React.Component<ArcadiaMenuPickerProps, A
try {
this.props.onSparkPoolSelect(e, item);
this.setState({
selectedSparkPool: item.text,
selectedSparkPool: item.text
});
} catch (error) {
Logger.logError(getErrorMessage(error), "ArcadiaMenuPicker/_onSparkPoolClicked");
@@ -65,28 +65,28 @@ export class ArcadiaMenuPicker extends React.Component<ArcadiaMenuPickerProps, A
public render() {
const { workspaces } = this.props;
let workspaceMenuItems: IContextualMenuItem[] = workspaces.map((workspace) => {
let workspaceMenuItems: IContextualMenuItem[] = workspaces.map(workspace => {
let sparkPoolsMenuProps: IContextualMenuProps = {
items: workspace.sparkPools.map(
(sparkpool): IContextualMenuItem => ({
key: sparkpool.id,
text: sparkpool.name,
onClick: this._onSparkPoolClicked,
onClick: this._onSparkPoolClicked
})
),
)
};
if (!sparkPoolsMenuProps.items.length) {
sparkPoolsMenuProps.items.push({
key: workspace.id,
text: "Create new spark pool",
onClick: this._onCreateNewSparkPoolClicked,
onClick: this._onCreateNewSparkPoolClicked
});
}
return {
key: workspace.id,
text: workspace.name,
subMenuProps: this.props.disableSubmenu ? undefined : sparkPoolsMenuProps,
subMenuProps: this.props.disableSubmenu ? undefined : sparkPoolsMenuProps
};
});
@@ -94,7 +94,7 @@ export class ArcadiaMenuPicker extends React.Component<ArcadiaMenuPickerProps, A
workspaceMenuItems.push({
key: "create_workspace",
text: "Create new workspace",
onClick: this._onCreateNewWorkspaceClicked,
onClick: this._onCreateNewWorkspaceClicked
});
}
@@ -103,29 +103,29 @@ export class ArcadiaMenuPicker extends React.Component<ArcadiaMenuPickerProps, A
backgroundColor: "transparent",
margin: "auto 5px",
padding: "0",
border: "0",
border: "0"
},
rootHovered: {
backgroundColor: "transparent",
backgroundColor: "transparent"
},
rootChecked: {
backgroundColor: "transparent",
backgroundColor: "transparent"
},
rootFocused: {
backgroundColor: "transparent",
backgroundColor: "transparent"
},
rootExpanded: {
backgroundColor: "transparent",
backgroundColor: "transparent"
},
flexContainer: {
height: "30px",
border: "1px solid #a6a6a6",
padding: "0 8px",
padding: "0 8px"
},
label: {
fontWeight: "400",
fontSize: "12px",
},
fontSize: "12px"
}
};
return (
@@ -134,7 +134,7 @@ export class ArcadiaMenuPicker extends React.Component<ArcadiaMenuPickerProps, A
persistMenu={true}
className="arcadia-menu-picker"
menuProps={{
items: workspaceMenuItems,
items: workspaceMenuItems
}}
styles={dropdownStyle}
/>

View File

@@ -8,7 +8,7 @@ export class CollapsiblePanelComponent {
constructor() {
return {
viewModel: CollapsiblePanelViewModel,
template,
template
};
}
}

View File

@@ -5,7 +5,7 @@ import { CollapsibleSectionComponent, CollapsibleSectionProps } from "./Collapsi
describe("CollapsibleSectionComponent", () => {
it("renders", () => {
const props: CollapsibleSectionProps = {
title: "Sample title",
title: "Sample title"
};
const wrapper = shallow(<CollapsibleSectionComponent {...props} />);

Some files were not shown because too many files have changed in this diff Show More