Compare commits

..

4 Commits

Author SHA1 Message Date
Steve Faulkner
c4fd56ecb0 Merge branch 'master' into generated-clients-strict-compile 2020-09-02 15:49:38 -05:00
Steve Faulkner
a8eeeebb59 merge 2020-08-12 19:23:12 -05:00
Steve Faulkner
abe491f5cd Fix test 2020-08-06 14:34:30 -05:00
Steve Faulkner
2fc15cf322 Add generated clients to strict compile 2020-08-06 14:29:16 -05:00
133 changed files with 2934 additions and 2173 deletions

View File

@@ -4,4 +4,3 @@ PORTAL_RUNNER_PASSWORD=
PORTAL_RUNNER_SUBSCRIPTION= PORTAL_RUNNER_SUBSCRIPTION=
PORTAL_RUNNER_RESOURCE_GROUP= PORTAL_RUNNER_RESOURCE_GROUP=
PORTAL_RUNNER_DATABASE_ACCOUNT= PORTAL_RUNNER_DATABASE_ACCOUNT=
PORTAL_RUNNER_CONNECTION_STRING=

View File

@@ -266,6 +266,10 @@ src/ResourceProvider/ResourceProviderClientFactory.ts
src/RouteHandlers/RouteHandler.ts src/RouteHandlers/RouteHandler.ts
src/RouteHandlers/TabRouteHandler.test.ts src/RouteHandlers/TabRouteHandler.test.ts
src/RouteHandlers/TabRouteHandler.ts src/RouteHandlers/TabRouteHandler.ts
src/Shared/AddCollectionUtility.test.ts
src/Shared/AddCollectionUtility.ts
src/Shared/AddDatabaseUtility.test.ts
src/Shared/AddDatabaseUtility.ts
src/Shared/Constants.ts src/Shared/Constants.ts
src/Shared/DefaultExperienceUtility.test.ts src/Shared/DefaultExperienceUtility.test.ts
src/Shared/DefaultExperienceUtility.ts src/Shared/DefaultExperienceUtility.ts
@@ -275,6 +279,8 @@ src/Shared/StorageUtility.test.ts
src/Shared/StorageUtility.ts src/Shared/StorageUtility.ts
src/Shared/StringUtility.test.ts src/Shared/StringUtility.test.ts
src/Shared/StringUtility.ts src/Shared/StringUtility.ts
src/Shared/Telemetry/TelemetryConstants.ts
src/Shared/Telemetry/TelemetryProcessor.ts
src/Shared/appInsights.ts src/Shared/appInsights.ts
src/SparkClusterManager/ArcadiaResourceManager.ts src/SparkClusterManager/ArcadiaResourceManager.ts
src/SparkClusterManager/SparkClusterManager.ts src/SparkClusterManager/SparkClusterManager.ts
@@ -412,5 +418,6 @@ cypress/integration/dataexplorer/SQL/addCollection.spec.ts
cypress/integration/dataexplorer/TABLE/addCollection.spec.ts cypress/integration/dataexplorer/TABLE/addCollection.spec.ts
cypress/integration/notebook/newNotebook.spec.ts cypress/integration/notebook/newNotebook.spec.ts
cypress/integration/notebook/resourceTree.spec.ts cypress/integration/notebook/resourceTree.spec.ts
__mocks__/AddDatabaseUtility.ts
__mocks__/monaco-editor.ts __mocks__/monaco-editor.ts
src/Explorer/Tree/ResourceTreeAdapterForResourceToken.test.tsx src/Explorer/Tree/ResourceTreeAdapterForResourceToken.test.tsx

View File

@@ -196,26 +196,6 @@ jobs:
shell: bash shell: bash
env: env:
NODE_TLS_REJECT_UNAUTHORIZED: 0 NODE_TLS_REJECT_UNAUTHORIZED: 0
endtoendpuppeteer:
name: "End to end puppeteer tests"
needs: [lint, format, compile, unittest]
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- name: Use Node.js 12.x
uses: actions/setup-node@v1
with:
node-version: 12.x
- name: End to End Puppeteer Tests
run: |
npm ci
npm start &
npm run wait-for-server
npm run test:e2e
shell: bash
env:
NODE_TLS_REJECT_UNAUTHORIZED: 0
PORTAL_RUNNER_CONNECTION_STRING: ${{ secrets.CONNECTION_STRING_SQL }}
nuget: nuget:
name: Publish Nuget name: Publish Nuget
if: github.ref == 'refs/heads/master' || contains(github.ref, 'hotfix/') || contains(github.ref, 'release/') if: github.ref == 'refs/heads/master' || contains(github.ref, 'hotfix/') || contains(github.ref, 'release/')

View File

@@ -0,0 +1,5 @@
export class AddDbUtilities {
createGremlinDatabase(params: any) {
return Promise.resolve(1)
}
}

View File

@@ -3,8 +3,7 @@ const isCI = require("is-ci");
module.exports = { module.exports = {
launch: { launch: {
headless: isCI, headless: isCI,
slowMo: 50, slowMo: isCI ? null : 20,
defaultViewport: null, defaultViewport: null
ignoreHTTPSErrors: true
} }
}; };

View File

@@ -54,7 +54,7 @@
@SelectionColor: #3074B0; @SelectionColor: #3074B0;
@FocusColor: #605e5c; @FocusColor: #00bcf2;
/****************************************************************************** /******************************************************************************
METRICS METRICS

View File

@@ -1522,10 +1522,6 @@ p {
.tooltipVisible(); .tooltipVisible();
} }
.infoTooltip a {
color: @AccentHigh;
}
.nowrap { .nowrap {
white-space: nowrap; white-space: nowrap;
} }
@@ -1650,7 +1646,7 @@ p {
} }
.contextual-pane .collid { .contextual-pane .collid {
border: 1px solid #605e5c; border: 1px solid #bbbbbb;
font-size: 10px; font-size: 10px;
padding: 5px 10px; padding: 5px 10px;
color: #000; color: #000;
@@ -2427,6 +2423,22 @@ a:link {
display: none; display: none;
} }
::-webkit-input-placeholder {
color: #969696;
}
::-moz-placeholder {
color: #969696;
}
:-ms-input-placeholder {
color: #969696;
}
:-moz-placeholder {
color: #969696;
}
::-ms-expand { ::-ms-expand {
color: #969696; color: #969696;
} }
@@ -2976,10 +2988,6 @@ settings-pane {
.enableAnalyticalStorageRadio:nth-child(n+2) { .enableAnalyticalStorageRadio:nth-child(n+2) {
margin-left: @LargeSpace; margin-left: @LargeSpace;
} }
.enableAnalyticalStorageRadioLabel {
padding: 0px
}
} }
.addCollectionLabel { .addCollectionLabel {
@@ -3009,12 +3017,4 @@ settings-pane {
.italic { .italic {
font-style: italic; font-style: italic;
} }
.warningErrorContent a {
color: @AccentMediumHigh
}
.infoBoxContent a {
color: @AccentMediumHigh
}

858
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@@ -8,7 +8,7 @@
"@azure/cosmos-language-service": "0.0.4", "@azure/cosmos-language-service": "0.0.4",
"@jupyterlab/services": "4.2.0", "@jupyterlab/services": "4.2.0",
"@jupyterlab/terminal": "1.2.1", "@jupyterlab/terminal": "1.2.1",
"@microsoft/applicationinsights-web": "2.5.8", "@microsoft/applicationinsights-web": "2.5.4",
"@nteract/commutable": "7.1.4", "@nteract/commutable": "7.1.4",
"@nteract/connected-components": "6.7.8", "@nteract/connected-components": "6.7.8",
"@nteract/core": "13.0.0", "@nteract/core": "13.0.0",
@@ -66,7 +66,7 @@
"mkdirp": "1.0.4", "mkdirp": "1.0.4",
"monaco-editor": "0.15.6", "monaco-editor": "0.15.6",
"object.entries": "1.1.0", "object.entries": "1.1.0",
"office-ui-fabric-react": "7.134.1", "office-ui-fabric-react": "7.121.10",
"p-retry": "4.2.0", "p-retry": "4.2.0",
"plotly.js-cartesian-dist-min": "1.52.3", "plotly.js-cartesian-dist-min": "1.52.3",
"promise-polyfill": "8.1.0", "promise-polyfill": "8.1.0",
@@ -102,15 +102,12 @@
"@types/d3": "4.13.2", "@types/d3": "4.13.2",
"@types/enzyme": "3.10.3", "@types/enzyme": "3.10.3",
"@types/enzyme-adapter-react-16": "1.0.5", "@types/enzyme-adapter-react-16": "1.0.5",
"@types/expect-puppeteer": "4.4.3",
"@types/hasher": "0.0.31", "@types/hasher": "0.0.31",
"@types/jest": "23.3.10", "@types/jest": "23.3.10",
"@types/jest-environment-puppeteer": "4.3.2",
"@types/memoize-one": "4.1.1", "@types/memoize-one": "4.1.1",
"@types/node": "12.11.1", "@types/node": "12.11.1",
"@types/promise.prototype.finally": "2.0.3", "@types/promise.prototype.finally": "2.0.3",
"@types/prop-types": "15.5.8", "@types/prop-types": "15.5.8",
"@types/puppeteer": "3.0.1",
"@types/q": "1.5.1", "@types/q": "1.5.1",
"@types/react": "16.8.25", "@types/react": "16.8.25",
"@types/react-dom": "16.0.7", "@types/react-dom": "16.0.7",
@@ -121,8 +118,8 @@
"@types/text-encoding": "0.0.33", "@types/text-encoding": "0.0.33",
"@types/underscore": "1.7.36", "@types/underscore": "1.7.36",
"@types/webfontloader": "1.6.29", "@types/webfontloader": "1.6.29",
"@typescript-eslint/eslint-plugin": "4.0.1", "@typescript-eslint/eslint-plugin": "3.2.0",
"@typescript-eslint/parser": "4.0.1", "@typescript-eslint/parser": "3.2.0",
"adal-angular": "1.0.15", "adal-angular": "1.0.15",
"axe-puppeteer": "1.1.0", "axe-puppeteer": "1.1.0",
"babel-jest": "24.9.0", "babel-jest": "24.9.0",
@@ -135,7 +132,7 @@
"enzyme": "3.10.0", "enzyme": "3.10.0",
"enzyme-adapter-react-16": "1.15.1", "enzyme-adapter-react-16": "1.15.1",
"enzyme-to-json": "3.4.3", "enzyme-to-json": "3.4.3",
"eslint": "7.8.1", "eslint": "7.3.1",
"eslint-cli": "1.1.1", "eslint-cli": "1.1.1",
"eslint-plugin-no-null": "1.0.2", "eslint-plugin-no-null": "1.0.2",
"eslint-plugin-prefer-arrow": "1.2.2", "eslint-plugin-prefer-arrow": "1.2.2",
@@ -167,9 +164,8 @@
"ts-loader": "6.2.2", "ts-loader": "6.2.2",
"tslint": "5.11.0", "tslint": "5.11.0",
"tslint-microsoft-contrib": "6.0.0", "tslint-microsoft-contrib": "6.0.0",
"typescript": "4.0.2", "typescript": "3.9.6",
"url-loader": "1.1.1", "url-loader": "1.1.1",
"wait-on": "4.0.2",
"webpack": "4.43.0", "webpack": "4.43.0",
"webpack-bundle-analyzer": "3.6.1", "webpack-bundle-analyzer": "3.6.1",
"webpack-cli": "3.3.10", "webpack-cli": "3.3.10",
@@ -188,7 +184,6 @@
"test": "rimraf coverage && jest", "test": "rimraf coverage && jest",
"test:e2e": "jest -c ./jest.config.e2e.js --detectOpenHandles", "test:e2e": "jest -c ./jest.config.e2e.js --detectOpenHandles",
"watch": "npm run start", "watch": "npm run start",
"wait-for-server": "wait-on -t 240000 -i 5000 -v https-get://0.0.0.0:1234/",
"build:ase": "gulp build:ase", "build:ase": "gulp build:ase",
"compile": "tsc", "compile": "tsc",
"compile:contracts": "tsc -p ./tsconfig.contracts.json", "compile:contracts": "tsc -p ./tsconfig.contracts.json",

View File

@@ -1,26 +1,28 @@
import * as _ from "underscore";
import * as Constants from "./Constants";
import * as DataModels from "../Contracts/DataModels";
import * as HeadersUtility from "./HeadersUtility";
import * as ViewModels from "../Contracts/ViewModels";
import Q from "q";
import { import {
ConflictDefinition, ConflictDefinition,
FeedOptions, FeedOptions,
ItemDefinition, ItemDefinition,
OfferDefinition,
QueryIterator, QueryIterator,
Resource Resource,
TriggerDefinition,
OfferDefinition
} from "@azure/cosmos"; } from "@azure/cosmos";
import { RequestOptions } from "@azure/cosmos/dist-esm";
import Q from "q";
import { configContext, Platform } from "../ConfigContext";
import * as DataModels from "../Contracts/DataModels";
import { MessageTypes } from "../Contracts/ExplorerContracts";
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 { OfferUtils } from "../Utils/OfferUtils";
import * as Constants from "./Constants";
import { client } from "./CosmosClient"; import { client } from "./CosmosClient";
import * as HeadersUtility from "./HeadersUtility"; import { LocalStorageUtility, StorageKey } from "../Shared/StorageUtility";
import { sendCachedDataMessage } from "./MessageHandler"; import { sendCachedDataMessage } from "./MessageHandler";
import { MessageTypes } from "../Contracts/ExplorerContracts";
import { OfferUtils } from "../Utils/OfferUtils";
import { RequestOptions } from "@azure/cosmos/dist-esm";
import StoredProcedure from "../Explorer/Tree/StoredProcedure";
import { Platform, configContext } from "../ConfigContext";
import DocumentId from "../Explorer/Tree/DocumentId";
import ConflictId from "../Explorer/Tree/ConflictId";
export function getCommonQueryOptions(options: FeedOptions): any { export function getCommonQueryOptions(options: FeedOptions): any {
const storedItemPerPageSetting: number = LocalStorageUtility.getEntryNumber(StorageKey.ActualItemPerPage); const storedItemPerPageSetting: number = LocalStorageUtility.getEntryNumber(StorageKey.ActualItemPerPage);
@@ -53,55 +55,85 @@ export function queryDocuments(
return Q(documentsIterator); return Q(documentsIterator);
} }
export function getPartitionKeyHeaderForConflict(conflictId: ConflictId): Object { export function readStoredProcedures(
const partitionKeyDefinition: DataModels.PartitionKey = conflictId.partitionKey; collection: ViewModels.Collection,
const partitionKeyValue: any = conflictId.partitionKeyValue; options?: any
): Q.Promise<DataModels.StoredProcedure[]> {
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 updateOffer(
offer: DataModels.Offer,
newOffer: DataModels.Offer,
options?: RequestOptions
): Q.Promise<DataModels.Offer> {
return Q(
client()
.offer(offer.id)
// TODO Remove casting when SDK types are fixed (https://github.com/Azure/azure-sdk-for-js/issues/10660)
.replace((newOffer as unknown) as OfferDefinition, options)
.then(response => {
return Promise.all([refreshCachedOffers(), refreshCachedResources()]).then(() => response.resource);
})
);
}
export function updateDocument(
collection: ViewModels.CollectionBase,
documentId: DocumentId,
newDocument: any
): Q.Promise<any> {
const partitionKey = documentId.partitionKeyValue;
return Q( return Q(
client() client()
.database(collection.databaseId) .database(collection.databaseId)
.container(collection.id()) .container(collection.id())
.item(documentId.id(), partitionKey) .scripts.storedProcedures.readAll(options)
.replace(newDocument) .fetchAll()
.then(response => response.resource) .then(response => response.resources as DataModels.StoredProcedure[])
);
}
export function readStoredProcedure(
collection: ViewModels.Collection,
requestedResource: DataModels.Resource,
options?: any
): Q.Promise<DataModels.StoredProcedure> {
return Q(
client()
.database(collection.databaseId)
.container(collection.id())
.scripts.storedProcedure(requestedResource.id)
.read(options)
.then(response => response.resource as DataModels.StoredProcedure)
);
}
export function readUserDefinedFunctions(
collection: ViewModels.Collection,
options: any
): Q.Promise<DataModels.UserDefinedFunction[]> {
return Q(
client()
.database(collection.databaseId)
.container(collection.id())
.scripts.userDefinedFunctions.readAll(options)
.fetchAll()
.then(response => response.resources as DataModels.UserDefinedFunction[])
);
}
export function readUserDefinedFunction(
collection: ViewModels.Collection,
requestedResource: DataModels.Resource,
options?: any
): Q.Promise<DataModels.UserDefinedFunction> {
return Q(
client()
.database(collection.databaseId)
.container(collection.id())
.scripts.userDefinedFunction(requestedResource.id)
.read(options)
.then(response => response.resource as DataModels.UserDefinedFunction)
);
}
export function readTriggers(collection: ViewModels.Collection, options: any): Q.Promise<DataModels.Trigger[]> {
return Q(
client()
.database(collection.databaseId)
.container(collection.id())
.scripts.triggers.readAll(options)
.fetchAll()
.then(response => response.resources as DataModels.Trigger[])
);
}
export function readTrigger(
collection: ViewModels.Collection,
requestedResource: DataModels.Resource,
options?: any
): Q.Promise<DataModels.Trigger> {
return Q(
client()
.database(collection.databaseId)
.container(collection.id())
.scripts.trigger(requestedResource.id)
.read(options)
.then(response => response.resource as DataModels.Trigger)
); );
} }
@@ -133,16 +165,6 @@ export function executeStoredProcedure(
); );
} }
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> { export function readDocument(collection: ViewModels.CollectionBase, documentId: DocumentId): Q.Promise<any> {
const partitionKey = documentId.partitionKeyValue; const partitionKey = documentId.partitionKeyValue;
@@ -156,6 +178,155 @@ export function readDocument(collection: ViewModels.CollectionBase, documentId:
); );
} }
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 updateOffer(
offer: DataModels.Offer,
newOffer: DataModels.Offer,
options?: RequestOptions
): Q.Promise<DataModels.Offer> {
return Q(
client()
.offer(offer.id)
// TODO Remove casting when SDK types are fixed (https://github.com/Azure/azure-sdk-for-js/issues/10660)
.replace((newOffer as unknown) as OfferDefinition, options)
.then(response => {
return Promise.all([refreshCachedOffers(), refreshCachedResources()]).then(() => response.resource);
})
);
}
export function updateStoredProcedure(
collection: ViewModels.Collection,
storedProcedure: DataModels.StoredProcedure,
options: any
): Q.Promise<DataModels.StoredProcedure> {
return Q(
client()
.database(collection.databaseId)
.container(collection.id())
.scripts.storedProcedure(storedProcedure.id)
.replace(storedProcedure, options)
.then(response => response.resource as DataModels.StoredProcedure)
);
}
export function updateUserDefinedFunction(
collection: ViewModels.Collection,
userDefinedFunction: DataModels.UserDefinedFunction,
options?: any
): Q.Promise<DataModels.UserDefinedFunction> {
return Q(
client()
.database(collection.databaseId)
.container(collection.id())
.scripts.userDefinedFunction(userDefinedFunction.id)
.replace(userDefinedFunction, options)
.then(response => response.resource as DataModels.StoredProcedure)
);
}
export function updateTrigger(
collection: ViewModels.Collection,
trigger: DataModels.Trigger,
options?: any
): Q.Promise<DataModels.Trigger> {
return Q(
client()
.database(collection.databaseId)
.container(collection.id())
.scripts.trigger(trigger.id)
.replace(trigger as TriggerDefinition, options)
.then(response => response.resource as DataModels.Trigger)
);
}
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 as DataModels.StoredProcedure)
);
}
export function createStoredProcedure(
collection: ViewModels.Collection,
newStoredProcedure: DataModels.StoredProcedure,
options?: any
): Q.Promise<DataModels.StoredProcedure> {
return Q(
client()
.database(collection.databaseId)
.container(collection.id())
.scripts.storedProcedures.create(newStoredProcedure, options)
.then(response => response.resource as DataModels.StoredProcedure)
);
}
export function createUserDefinedFunction(
collection: ViewModels.Collection,
newUserDefinedFunction: DataModels.UserDefinedFunction,
options: any
): Q.Promise<DataModels.UserDefinedFunction> {
return Q(
client()
.database(collection.databaseId)
.container(collection.id())
.scripts.userDefinedFunctions.create(newUserDefinedFunction, options)
.then(response => response.resource as DataModels.UserDefinedFunction)
);
}
export function createTrigger(
collection: ViewModels.Collection,
newTrigger: DataModels.Trigger,
options?: any
): Q.Promise<DataModels.Trigger> {
return Q(
client()
.database(collection.databaseId)
.container(collection.id())
.scripts.triggers.create(newTrigger as TriggerDefinition, options)
.then(response => response.resource as DataModels.Trigger)
);
}
export function deleteDocument(collection: ViewModels.CollectionBase, documentId: DocumentId): Q.Promise<any> { export function deleteDocument(collection: ViewModels.CollectionBase, documentId: DocumentId): Q.Promise<any> {
const partitionKey = documentId.partitionKeyValue; const partitionKey = documentId.partitionKeyValue;
@@ -184,6 +355,48 @@ export function deleteConflict(
); );
} }
export function deleteStoredProcedure(
collection: ViewModels.Collection,
storedProcedure: DataModels.StoredProcedure,
options: any
): Q.Promise<any> {
return Q(
client()
.database(collection.databaseId)
.container(collection.id())
.scripts.storedProcedure(storedProcedure.id)
.delete()
);
}
export function deleteUserDefinedFunction(
collection: ViewModels.Collection,
userDefinedFunction: DataModels.UserDefinedFunction,
options: any
): Q.Promise<any> {
return Q(
client()
.database(collection.databaseId)
.container(collection.id())
.scripts.userDefinedFunction(userDefinedFunction.id)
.delete()
);
}
export function deleteTrigger(
collection: ViewModels.Collection,
trigger: DataModels.Trigger,
options: any
): Q.Promise<any> {
return Q(
client()
.database(collection.databaseId)
.container(collection.id())
.scripts.trigger(trigger.id)
.delete()
);
}
export function readCollectionQuotaInfo( export function readCollectionQuotaInfo(
collection: ViewModels.Collection, collection: ViewModels.Collection,
options: any options: any

View File

@@ -1,17 +1,18 @@
import { ConflictDefinition, ItemDefinition, QueryIterator, Resource } from "@azure/cosmos"; import * as Constants from "./Constants";
import { RequestOptions } from "@azure/cosmos/dist-esm";
import Q from "q";
import * as DataModels from "../Contracts/DataModels"; import * as DataModels from "../Contracts/DataModels";
import * as ViewModels from "../Contracts/ViewModels"; import * as ViewModels from "../Contracts/ViewModels";
import Q from "q";
import { ConflictDefinition, ItemDefinition, QueryIterator, Resource } from "@azure/cosmos";
import { ConsoleDataType } from "../Explorer/Menus/NotificationConsole/NotificationConsoleComponent";
import * as DataAccessUtilityBase from "./DataAccessUtilityBase";
import * as Logger from "./Logger";
import { MinimalQueryIterator, nextPage } from "./IteratorUtilities";
import * as NotificationConsoleUtils from "../Utils/NotificationConsoleUtils";
import { RequestOptions } from "@azure/cosmos/dist-esm";
import StoredProcedure from "../Explorer/Tree/StoredProcedure";
import ConflictId from "../Explorer/Tree/ConflictId"; import ConflictId from "../Explorer/Tree/ConflictId";
import DocumentId from "../Explorer/Tree/DocumentId"; import DocumentId from "../Explorer/Tree/DocumentId";
import StoredProcedure from "../Explorer/Tree/StoredProcedure";
import { logConsoleError, logConsoleInfo, logConsoleProgress } from "../Utils/NotificationConsoleUtils";
import * as Constants from "./Constants";
import { sendNotificationForError } from "./dataAccess/sendNotificationForError"; import { sendNotificationForError } from "./dataAccess/sendNotificationForError";
import * as DataAccessUtilityBase from "./DataAccessUtilityBase";
import { MinimalQueryIterator, nextPage } from "./IteratorUtilities";
import * as Logger from "./Logger";
// TODO: Log all promise resolutions and errors with verbosity levels // TODO: Log all promise resolutions and errors with verbosity levels
export function queryDocuments( export function queryDocuments(
@@ -41,6 +42,121 @@ export function getEntityName() {
return "item"; return "item";
} }
export function readStoredProcedures(
collection: ViewModels.Collection,
options: any = {}
): Q.Promise<DataModels.StoredProcedure[]> {
var deferred = Q.defer<DataModels.StoredProcedure[]>();
const id = NotificationConsoleUtils.logConsoleMessage(
ConsoleDataType.InProgress,
`Querying stored procedures for container ${collection.id()}`
);
DataAccessUtilityBase.readStoredProcedures(collection, options)
.then(
(storedProcedures: DataModels.StoredProcedure[]) => {
deferred.resolve(storedProcedures);
},
(error: any) => {
NotificationConsoleUtils.logConsoleMessage(
ConsoleDataType.Error,
`Failed to query stored procedures for container ${collection.id()}: ${JSON.stringify(error)}`
);
Logger.logError(JSON.stringify(error), "ReadStoredProcedures", error.code);
sendNotificationForError(error);
deferred.reject(error);
}
)
.finally(() => {
NotificationConsoleUtils.clearInProgressMessageWithId(id);
});
return deferred.promise;
}
export function readStoredProcedure(
collection: ViewModels.Collection,
requestedResource: DataModels.Resource,
options?: any
): Q.Promise<DataModels.StoredProcedure> {
return DataAccessUtilityBase.readStoredProcedure(collection, requestedResource, options);
}
export function readUserDefinedFunctions(
collection: ViewModels.Collection,
options: any = {}
): Q.Promise<DataModels.UserDefinedFunction[]> {
var deferred = Q.defer<DataModels.UserDefinedFunction[]>();
const id = NotificationConsoleUtils.logConsoleMessage(
ConsoleDataType.InProgress,
`Querying user defined functions for collection ${collection.id()}`
);
DataAccessUtilityBase.readUserDefinedFunctions(collection, options)
.then(
(userDefinedFunctions: DataModels.UserDefinedFunction[]) => {
deferred.resolve(userDefinedFunctions);
},
(error: any) => {
NotificationConsoleUtils.logConsoleMessage(
ConsoleDataType.Error,
`Failed to query user defined functions for container ${collection.id()}: ${JSON.stringify(error)}`
);
Logger.logError(JSON.stringify(error), "ReadUDFs", error.code);
sendNotificationForError(error);
deferred.reject(error);
}
)
.finally(() => {
NotificationConsoleUtils.clearInProgressMessageWithId(id);
});
return deferred.promise;
}
export function readUserDefinedFunction(
collection: ViewModels.Collection,
requestedResource: DataModels.Resource,
options: any
): Q.Promise<DataModels.UserDefinedFunction> {
return DataAccessUtilityBase.readUserDefinedFunction(collection, requestedResource, options);
}
export function readTriggers(collection: ViewModels.Collection, options: any): Q.Promise<DataModels.Trigger[]> {
var deferred = Q.defer<DataModels.Trigger[]>();
const id = NotificationConsoleUtils.logConsoleMessage(
ConsoleDataType.InProgress,
`Querying triggers for container ${collection.id()}`
);
DataAccessUtilityBase.readTriggers(collection, options)
.then(
(triggers: DataModels.Trigger[]) => {
deferred.resolve(triggers);
},
(error: any) => {
NotificationConsoleUtils.logConsoleMessage(
ConsoleDataType.Error,
`Failed to query triggers for container ${collection.id()}: ${JSON.stringify(error)}`
);
Logger.logError(JSON.stringify(error), "ReadTriggers", error.code);
sendNotificationForError(error);
deferred.reject(error);
}
)
.finally(() => {
NotificationConsoleUtils.clearInProgressMessageWithId(id);
});
return deferred.promise;
}
export function readTrigger(
collection: ViewModels.Collection,
requestedResource: DataModels.Resource,
options?: any
): Q.Promise<DataModels.Trigger> {
return DataAccessUtilityBase.readTrigger(collection, requestedResource, options);
}
export function executeStoredProcedure( export function executeStoredProcedure(
collection: ViewModels.Collection, collection: ViewModels.Collection,
storedProcedure: StoredProcedure, storedProcedure: StoredProcedure,
@@ -49,17 +165,22 @@ export function executeStoredProcedure(
): Q.Promise<any> { ): Q.Promise<any> {
var deferred = Q.defer<any>(); var deferred = Q.defer<any>();
const clearMessage = logConsoleProgress(`Executing stored procedure ${storedProcedure.id()}`); const id = NotificationConsoleUtils.logConsoleMessage(
ConsoleDataType.InProgress,
`Executing stored procedure ${storedProcedure.id()}`
);
DataAccessUtilityBase.executeStoredProcedure(collection, storedProcedure, partitionKeyValue, params) DataAccessUtilityBase.executeStoredProcedure(collection, storedProcedure, partitionKeyValue, params)
.then( .then(
(response: any) => { (response: any) => {
deferred.resolve(response); deferred.resolve(response);
logConsoleInfo( NotificationConsoleUtils.logConsoleMessage(
ConsoleDataType.Info,
`Finished executing stored procedure ${storedProcedure.id()} for container ${storedProcedure.collection.id()}` `Finished executing stored procedure ${storedProcedure.id()} for container ${storedProcedure.collection.id()}`
); );
}, },
(error: any) => { (error: any) => {
logConsoleError( NotificationConsoleUtils.logConsoleMessage(
ConsoleDataType.Error,
`Failed to execute stored procedure ${storedProcedure.id()} for container ${storedProcedure.collection.id()}: ${JSON.stringify( `Failed to execute stored procedure ${storedProcedure.id()} for container ${storedProcedure.collection.id()}: ${JSON.stringify(
error error
)}` )}`
@@ -70,7 +191,7 @@ export function executeStoredProcedure(
} }
) )
.finally(() => { .finally(() => {
clearMessage(); NotificationConsoleUtils.clearInProgressMessageWithId(id);
}); });
return deferred.promise; return deferred.promise;
@@ -84,23 +205,32 @@ export function queryDocumentsPage(
): Q.Promise<ViewModels.QueryResults> { ): Q.Promise<ViewModels.QueryResults> {
var deferred = Q.defer<ViewModels.QueryResults>(); var deferred = Q.defer<ViewModels.QueryResults>();
const entityName = getEntityName(); const entityName = getEntityName();
const clearMessage = logConsoleProgress(`Querying ${entityName} for container ${resourceName}`); const id = NotificationConsoleUtils.logConsoleMessage(
ConsoleDataType.InProgress,
`Querying ${entityName} for container ${resourceName}`
);
Q(nextPage(documentsIterator, firstItemIndex)) Q(nextPage(documentsIterator, firstItemIndex))
.then( .then(
(result: ViewModels.QueryResults) => { (result: ViewModels.QueryResults) => {
const itemCount = (result.documents && result.documents.length) || 0; const itemCount = (result.documents && result.documents.length) || 0;
logConsoleInfo(`Successfully fetched ${itemCount} ${entityName} for container ${resourceName}`); NotificationConsoleUtils.logConsoleMessage(
ConsoleDataType.Info,
`Successfully fetched ${itemCount} ${entityName} for container ${resourceName}`
);
deferred.resolve(result); deferred.resolve(result);
}, },
(error: any) => { (error: any) => {
logConsoleError(`Failed to query ${entityName} for container ${resourceName}: ${JSON.stringify(error)}`); NotificationConsoleUtils.logConsoleMessage(
ConsoleDataType.Error,
`Failed to query ${entityName} for container ${resourceName}: ${JSON.stringify(error)}`
);
Logger.logError(JSON.stringify(error), "QueryDocumentsPage", error.code); Logger.logError(JSON.stringify(error), "QueryDocumentsPage", error.code);
sendNotificationForError(error); sendNotificationForError(error);
deferred.reject(error); deferred.reject(error);
} }
) )
.finally(() => { .finally(() => {
clearMessage(); NotificationConsoleUtils.clearInProgressMessageWithId(id);
}); });
return deferred.promise; return deferred.promise;
@@ -109,21 +239,27 @@ export function queryDocumentsPage(
export function readDocument(collection: ViewModels.CollectionBase, documentId: DocumentId): Q.Promise<any> { export function readDocument(collection: ViewModels.CollectionBase, documentId: DocumentId): Q.Promise<any> {
var deferred = Q.defer<any>(); var deferred = Q.defer<any>();
const entityName = getEntityName(); const entityName = getEntityName();
const clearMessage = logConsoleProgress(`Reading ${entityName} ${documentId.id()}`); const id = NotificationConsoleUtils.logConsoleMessage(
ConsoleDataType.InProgress,
`Reading ${entityName} ${documentId.id()}`
);
DataAccessUtilityBase.readDocument(collection, documentId) DataAccessUtilityBase.readDocument(collection, documentId)
.then( .then(
(document: any) => { (document: any) => {
deferred.resolve(document); deferred.resolve(document);
}, },
(error: any) => { (error: any) => {
logConsoleError(`Failed to read ${entityName} ${documentId.id()}: ${JSON.stringify(error)}`); NotificationConsoleUtils.logConsoleMessage(
ConsoleDataType.Error,
`Failed to read ${entityName} ${documentId.id()}: ${JSON.stringify(error)}`
);
Logger.logError(JSON.stringify(error), "ReadDocument", error.code); Logger.logError(JSON.stringify(error), "ReadDocument", error.code);
sendNotificationForError(error); sendNotificationForError(error);
deferred.reject(error); deferred.reject(error);
} }
) )
.finally(() => { .finally(() => {
clearMessage(); NotificationConsoleUtils.clearInProgressMessageWithId(id);
}); });
return deferred.promise; return deferred.promise;
@@ -136,22 +272,31 @@ export function updateDocument(
): Q.Promise<any> { ): Q.Promise<any> {
var deferred = Q.defer<any>(); var deferred = Q.defer<any>();
const entityName = getEntityName(); const entityName = getEntityName();
const clearMessage = logConsoleProgress(`Updating ${entityName} ${documentId.id()}`); const id = NotificationConsoleUtils.logConsoleMessage(
ConsoleDataType.InProgress,
`Updating ${entityName} ${documentId.id()}`
);
DataAccessUtilityBase.updateDocument(collection, documentId, newDocument) DataAccessUtilityBase.updateDocument(collection, documentId, newDocument)
.then( .then(
(updatedDocument: any) => { (updatedDocument: any) => {
logConsoleInfo(`Successfully updated ${entityName} ${documentId.id()}`); NotificationConsoleUtils.logConsoleMessage(
ConsoleDataType.Info,
`Successfully updated ${entityName} ${documentId.id()}`
);
deferred.resolve(updatedDocument); deferred.resolve(updatedDocument);
}, },
(error: any) => { (error: any) => {
logConsoleError(`Failed to update ${entityName} ${documentId.id()}: ${JSON.stringify(error)}`); NotificationConsoleUtils.logConsoleMessage(
ConsoleDataType.Error,
`Failed to update ${entityName} ${documentId.id()}: ${JSON.stringify(error)}`
);
Logger.logError(JSON.stringify(error), "UpdateDocument", error.code); Logger.logError(JSON.stringify(error), "UpdateDocument", error.code);
sendNotificationForError(error); sendNotificationForError(error);
deferred.reject(error); deferred.reject(error);
} }
) )
.finally(() => { .finally(() => {
clearMessage(); NotificationConsoleUtils.clearInProgressMessageWithId(id);
}); });
return deferred.promise; return deferred.promise;
@@ -163,15 +308,24 @@ export function updateOffer(
options: RequestOptions options: RequestOptions
): Q.Promise<DataModels.Offer> { ): Q.Promise<DataModels.Offer> {
var deferred = Q.defer<any>(); var deferred = Q.defer<any>();
const clearMessage = logConsoleProgress(`Updating offer for resource ${offer.resource}`); const id = NotificationConsoleUtils.logConsoleMessage(
ConsoleDataType.InProgress,
`Updating offer for resource ${offer.resource}`
);
DataAccessUtilityBase.updateOffer(offer, newOffer, options) DataAccessUtilityBase.updateOffer(offer, newOffer, options)
.then( .then(
(replacedOffer: DataModels.Offer) => { (replacedOffer: DataModels.Offer) => {
logConsoleInfo(`Successfully updated offer for resource ${offer.resource}`); NotificationConsoleUtils.logConsoleMessage(
ConsoleDataType.Info,
`Successfully updated offer for resource ${offer.resource}`
);
deferred.resolve(replacedOffer); deferred.resolve(replacedOffer);
}, },
(error: any) => { (error: any) => {
logConsoleError(`Error updating offer for resource ${offer.resource}: ${JSON.stringify(error)}`); NotificationConsoleUtils.logConsoleMessage(
ConsoleDataType.Error,
`Error updating offer for resource ${offer.resource}: ${JSON.stringify(error)}`
);
Logger.logError( Logger.logError(
JSON.stringify({ JSON.stringify({
oldOffer: offer, oldOffer: offer,
@@ -186,7 +340,108 @@ export function updateOffer(
} }
) )
.finally(() => { .finally(() => {
clearMessage(); NotificationConsoleUtils.clearInProgressMessageWithId(id);
});
return deferred.promise;
}
export function updateStoredProcedure(
collection: ViewModels.Collection,
storedProcedure: DataModels.StoredProcedure,
options?: any
): Q.Promise<DataModels.StoredProcedure> {
var deferred = Q.defer<any>();
const id = NotificationConsoleUtils.logConsoleMessage(
ConsoleDataType.InProgress,
`Updating stored procedure ${storedProcedure.id}`
);
DataAccessUtilityBase.updateStoredProcedure(collection, storedProcedure, options)
.then(
(updatedStoredProcedure: DataModels.StoredProcedure) => {
NotificationConsoleUtils.logConsoleMessage(
ConsoleDataType.Info,
`Successfully updated stored procedure ${storedProcedure.id}`
);
deferred.resolve(updatedStoredProcedure);
},
(error: any) => {
NotificationConsoleUtils.logConsoleMessage(
ConsoleDataType.Error,
`Error while updating stored procedure ${storedProcedure.id}:\n ${JSON.stringify(error)}`
);
Logger.logError(JSON.stringify(error), "UpdateStoredProcedure", error.code);
sendNotificationForError(error);
deferred.reject(error);
}
)
.finally(() => {
NotificationConsoleUtils.clearInProgressMessageWithId(id);
});
return deferred.promise;
}
export function updateUserDefinedFunction(
collection: ViewModels.Collection,
userDefinedFunction: DataModels.UserDefinedFunction,
options: any = {}
): Q.Promise<DataModels.UserDefinedFunction> {
var deferred = Q.defer<any>();
const id = NotificationConsoleUtils.logConsoleMessage(
ConsoleDataType.InProgress,
`Updating user defined function ${userDefinedFunction.id}`
);
DataAccessUtilityBase.updateUserDefinedFunction(collection, userDefinedFunction, options)
.then(
(updatedUserDefinedFunction: DataModels.UserDefinedFunction) => {
NotificationConsoleUtils.logConsoleMessage(
ConsoleDataType.Info,
`Successfully updated user defined function ${userDefinedFunction.id}`
);
deferred.resolve(updatedUserDefinedFunction);
},
(error: any) => {
NotificationConsoleUtils.logConsoleMessage(
ConsoleDataType.Error,
`Error while updating user defined function ${userDefinedFunction.id}:\n ${JSON.stringify(error)}`
);
Logger.logError(JSON.stringify(error), "UpdateUDF", error.code);
sendNotificationForError(error);
deferred.reject(error);
}
)
.finally(() => {
NotificationConsoleUtils.clearInProgressMessageWithId(id);
});
return deferred.promise;
}
export function updateTrigger(
collection: ViewModels.Collection,
trigger: DataModels.Trigger
): Q.Promise<DataModels.Trigger> {
var deferred = Q.defer<any>();
const id = NotificationConsoleUtils.logConsoleMessage(ConsoleDataType.InProgress, `Updating trigger ${trigger.id}`);
DataAccessUtilityBase.updateTrigger(collection, trigger)
.then(
(updatedTrigger: DataModels.Trigger) => {
NotificationConsoleUtils.logConsoleMessage(ConsoleDataType.Info, `Updated trigger ${trigger.id}`);
deferred.resolve(updatedTrigger);
},
(error: any) => {
NotificationConsoleUtils.logConsoleMessage(
ConsoleDataType.Error,
`Error while updating trigger ${trigger.id}:\n ${JSON.stringify(error)}`
);
Logger.logError(JSON.stringify(error), "UpdateTrigger", error.code);
sendNotificationForError(error);
deferred.reject(error);
}
)
.finally(() => {
NotificationConsoleUtils.clearInProgressMessageWithId(id);
}); });
return deferred.promise; return deferred.promise;
@@ -195,15 +450,22 @@ export function updateOffer(
export function createDocument(collection: ViewModels.CollectionBase, newDocument: any): Q.Promise<any> { export function createDocument(collection: ViewModels.CollectionBase, newDocument: any): Q.Promise<any> {
var deferred = Q.defer<any>(); var deferred = Q.defer<any>();
const entityName = getEntityName(); const entityName = getEntityName();
const clearMessage = logConsoleProgress(`Creating new ${entityName} for container ${collection.id()}`); const id = NotificationConsoleUtils.logConsoleMessage(
ConsoleDataType.InProgress,
`Creating new ${entityName} for container ${collection.id()}`
);
DataAccessUtilityBase.createDocument(collection, newDocument) DataAccessUtilityBase.createDocument(collection, newDocument)
.then( .then(
(savedDocument: any) => { (savedDocument: any) => {
logConsoleInfo(`Successfully created new ${entityName} for container ${collection.id()}`); NotificationConsoleUtils.logConsoleMessage(
ConsoleDataType.Info,
`Successfully created new ${entityName} for container ${collection.id()}`
);
deferred.resolve(savedDocument); deferred.resolve(savedDocument);
}, },
(error: any) => { (error: any) => {
logConsoleError( NotificationConsoleUtils.logConsoleMessage(
ConsoleDataType.Error,
`Error while creating new ${entityName} for container ${collection.id()}:\n ${JSON.stringify(error)}` `Error while creating new ${entityName} for container ${collection.id()}:\n ${JSON.stringify(error)}`
); );
Logger.logError(JSON.stringify(error), "CreateDocument", error.code); Logger.logError(JSON.stringify(error), "CreateDocument", error.code);
@@ -212,7 +474,115 @@ export function createDocument(collection: ViewModels.CollectionBase, newDocumen
} }
) )
.finally(() => { .finally(() => {
clearMessage(); NotificationConsoleUtils.clearInProgressMessageWithId(id);
});
return deferred.promise;
}
export function createStoredProcedure(
collection: ViewModels.Collection,
newStoredProcedure: DataModels.StoredProcedure,
options?: any
): Q.Promise<DataModels.StoredProcedure> {
var deferred = Q.defer<any>();
const id = NotificationConsoleUtils.logConsoleMessage(
ConsoleDataType.InProgress,
`Creating stored procedure for container ${collection.id()}`
);
DataAccessUtilityBase.createStoredProcedure(collection, newStoredProcedure, options)
.then(
(createdStoredProcedure: DataModels.StoredProcedure) => {
NotificationConsoleUtils.logConsoleMessage(
ConsoleDataType.Info,
`Successfully created stored procedure for container ${collection.id()}`
);
deferred.resolve(createdStoredProcedure);
},
error => {
NotificationConsoleUtils.logConsoleMessage(
ConsoleDataType.Error,
`Error while creating stored procedure for container ${collection.id()}:\n ${JSON.stringify(error)}`
);
Logger.logError(JSON.stringify(error), "CreateStoredProcedure", error.code);
sendNotificationForError(error);
deferred.reject(error);
}
)
.finally(() => {
NotificationConsoleUtils.clearInProgressMessageWithId(id);
});
return deferred.promise;
}
export function createUserDefinedFunction(
collection: ViewModels.Collection,
newUserDefinedFunction: DataModels.UserDefinedFunction,
options?: any
): Q.Promise<DataModels.UserDefinedFunction> {
var deferred = Q.defer<any>();
const id = NotificationConsoleUtils.logConsoleMessage(
ConsoleDataType.InProgress,
`Creating user defined function for container ${collection.id()}`
);
DataAccessUtilityBase.createUserDefinedFunction(collection, newUserDefinedFunction, options)
.then(
(createdUserDefinedFunction: DataModels.UserDefinedFunction) => {
NotificationConsoleUtils.logConsoleMessage(
ConsoleDataType.Info,
`Successfully created user defined function for container ${collection.id()}`
);
deferred.resolve(createdUserDefinedFunction);
},
error => {
NotificationConsoleUtils.logConsoleMessage(
ConsoleDataType.Error,
`Error while creating user defined function for container ${collection.id()}:\n ${JSON.stringify(error)}`
);
Logger.logError(JSON.stringify(error), "CreateUDF", error.code);
sendNotificationForError(error);
deferred.reject(error);
}
)
.finally(() => {
NotificationConsoleUtils.clearInProgressMessageWithId(id);
});
return deferred.promise;
}
export function createTrigger(
collection: ViewModels.Collection,
newTrigger: DataModels.Trigger,
options: any = {}
): Q.Promise<DataModels.Trigger> {
var deferred = Q.defer<any>();
const id = NotificationConsoleUtils.logConsoleMessage(
ConsoleDataType.InProgress,
`Creating trigger for container ${collection.id()}`
);
DataAccessUtilityBase.createTrigger(collection, newTrigger, options)
.then(
(createdTrigger: DataModels.Trigger) => {
NotificationConsoleUtils.logConsoleMessage(
ConsoleDataType.Info,
`Successfully created trigger for container ${collection.id()}`
);
deferred.resolve(createdTrigger);
},
(error: any) => {
NotificationConsoleUtils.logConsoleMessage(
ConsoleDataType.Error,
`Error while creating trigger for container ${collection.id()}:\n ${JSON.stringify(error)}`
);
Logger.logError(JSON.stringify(error), "CreateTrigger", error.code);
sendNotificationForError(error);
deferred.reject(error);
}
)
.finally(() => {
NotificationConsoleUtils.clearInProgressMessageWithId(id);
}); });
return deferred.promise; return deferred.promise;
@@ -221,22 +591,31 @@ export function createDocument(collection: ViewModels.CollectionBase, newDocumen
export function deleteDocument(collection: ViewModels.CollectionBase, documentId: DocumentId): Q.Promise<any> { export function deleteDocument(collection: ViewModels.CollectionBase, documentId: DocumentId): Q.Promise<any> {
var deferred = Q.defer<any>(); var deferred = Q.defer<any>();
const entityName = getEntityName(); const entityName = getEntityName();
const clearMessage = logConsoleProgress(`Deleting ${entityName} ${documentId.id()}`); const id = NotificationConsoleUtils.logConsoleMessage(
ConsoleDataType.InProgress,
`Deleting ${entityName} ${documentId.id()}`
);
DataAccessUtilityBase.deleteDocument(collection, documentId) DataAccessUtilityBase.deleteDocument(collection, documentId)
.then( .then(
(response: any) => { (response: any) => {
logConsoleInfo(`Successfully deleted ${entityName} ${documentId.id()}`); NotificationConsoleUtils.logConsoleMessage(
ConsoleDataType.Info,
`Successfully deleted ${entityName} ${documentId.id()}`
);
deferred.resolve(response); deferred.resolve(response);
}, },
(error: any) => { (error: any) => {
logConsoleError(`Error while deleting ${entityName} ${documentId.id()}:\n ${JSON.stringify(error)}`); NotificationConsoleUtils.logConsoleMessage(
ConsoleDataType.Error,
`Error while deleting ${entityName} ${documentId.id()}:\n ${JSON.stringify(error)}`
);
Logger.logError(JSON.stringify(error), "DeleteDocument", error.code); Logger.logError(JSON.stringify(error), "DeleteDocument", error.code);
sendNotificationForError(error); sendNotificationForError(error);
deferred.reject(error); deferred.reject(error);
} }
) )
.finally(() => { .finally(() => {
clearMessage(); NotificationConsoleUtils.clearInProgressMessageWithId(id);
}); });
return deferred.promise; return deferred.promise;
@@ -249,22 +628,134 @@ export function deleteConflict(
): Q.Promise<any> { ): Q.Promise<any> {
var deferred = Q.defer<any>(); var deferred = Q.defer<any>();
const clearMessage = logConsoleProgress(`Deleting conflict ${conflictId.id()}`); const id = NotificationConsoleUtils.logConsoleMessage(
ConsoleDataType.InProgress,
`Deleting conflict ${conflictId.id()}`
);
DataAccessUtilityBase.deleteConflict(collection, conflictId, options) DataAccessUtilityBase.deleteConflict(collection, conflictId, options)
.then( .then(
(response: any) => { (response: any) => {
logConsoleInfo(`Successfully deleted conflict ${conflictId.id()}`); NotificationConsoleUtils.logConsoleMessage(
ConsoleDataType.Info,
`Successfully deleted conflict ${conflictId.id()}`
);
deferred.resolve(response); deferred.resolve(response);
}, },
(error: any) => { (error: any) => {
logConsoleError(`Error while deleting conflict ${conflictId.id()}:\n ${JSON.stringify(error)}`); NotificationConsoleUtils.logConsoleMessage(
ConsoleDataType.Error,
`Error while deleting conflict ${conflictId.id()}:\n ${JSON.stringify(error)}`
);
Logger.logError(JSON.stringify(error), "DeleteConflict", error.code); Logger.logError(JSON.stringify(error), "DeleteConflict", error.code);
sendNotificationForError(error); sendNotificationForError(error);
deferred.reject(error); deferred.reject(error);
} }
) )
.finally(() => { .finally(() => {
clearMessage(); NotificationConsoleUtils.clearInProgressMessageWithId(id);
});
return deferred.promise;
}
export function deleteStoredProcedure(
collection: ViewModels.Collection,
storedProcedure: DataModels.StoredProcedure,
options: any = {}
): Q.Promise<DataModels.StoredProcedure> {
var deferred = Q.defer<any>();
const id = NotificationConsoleUtils.logConsoleMessage(
ConsoleDataType.InProgress,
`Deleting stored procedure ${storedProcedure.id}`
);
DataAccessUtilityBase.deleteStoredProcedure(collection, storedProcedure, options)
.then(
(response: any) => {
NotificationConsoleUtils.logConsoleMessage(
ConsoleDataType.Info,
`Successfully deleted stored procedure ${storedProcedure.id}`
);
deferred.resolve(response);
},
(error: any) => {
NotificationConsoleUtils.logConsoleMessage(
ConsoleDataType.Error,
`Error while deleting stored procedure ${storedProcedure.id}:\n ${JSON.stringify(error)}`
);
Logger.logError(JSON.stringify(error), "DeleteStoredProcedure", error.code);
sendNotificationForError(error);
deferred.reject(error);
}
)
.finally(() => {
NotificationConsoleUtils.clearInProgressMessageWithId(id);
});
return deferred.promise;
}
export function deleteUserDefinedFunction(
collection: ViewModels.Collection,
userDefinedFunction: DataModels.UserDefinedFunction,
options: any = {}
): Q.Promise<DataModels.UserDefinedFunction> {
var deferred = Q.defer<any>();
const id = NotificationConsoleUtils.logConsoleMessage(
ConsoleDataType.InProgress,
`Deleting user defined function ${userDefinedFunction.id}`
);
DataAccessUtilityBase.deleteUserDefinedFunction(collection, userDefinedFunction, options)
.then(
(response: any) => {
NotificationConsoleUtils.logConsoleMessage(
ConsoleDataType.Info,
`Successfully deleted user defined function ${userDefinedFunction.id}`
);
deferred.resolve(response);
},
(error: any) => {
NotificationConsoleUtils.logConsoleMessage(
ConsoleDataType.Error,
`Error while deleting user defined function ${userDefinedFunction.id}:\n ${JSON.stringify(error)}`
);
Logger.logError(JSON.stringify(error), "DeleteUDF", error.code);
sendNotificationForError(error);
deferred.reject(error);
}
)
.finally(() => {
NotificationConsoleUtils.clearInProgressMessageWithId(id);
});
return deferred.promise;
}
export function deleteTrigger(
collection: ViewModels.Collection,
trigger: DataModels.Trigger,
options: any = {}
): Q.Promise<DataModels.Trigger> {
var deferred = Q.defer<any>();
const id = NotificationConsoleUtils.logConsoleMessage(ConsoleDataType.InProgress, `Deleting trigger ${trigger.id}`);
DataAccessUtilityBase.deleteTrigger(collection, trigger, options)
.then(
(response: any) => {
NotificationConsoleUtils.logConsoleMessage(ConsoleDataType.Info, `Successfully deleted trigger ${trigger.id}`);
deferred.resolve(response);
},
(error: any) => {
NotificationConsoleUtils.logConsoleMessage(
ConsoleDataType.Error,
`Error while deleting trigger ${trigger.id}:\n ${JSON.stringify(error)}`
);
Logger.logError(JSON.stringify(error), "DeleteTrigger", error.code);
sendNotificationForError(error);
deferred.reject(error);
}
)
.finally(() => {
NotificationConsoleUtils.clearInProgressMessageWithId(id);
}); });
return deferred.promise; return deferred.promise;
@@ -284,21 +775,27 @@ export function readCollectionQuotaInfo(
): Q.Promise<DataModels.CollectionQuotaInfo> { ): Q.Promise<DataModels.CollectionQuotaInfo> {
var deferred = Q.defer<DataModels.CollectionQuotaInfo>(); var deferred = Q.defer<DataModels.CollectionQuotaInfo>();
const clearMessage = logConsoleProgress(`Querying quota info for container ${collection.id}`); const id = NotificationConsoleUtils.logConsoleMessage(
ConsoleDataType.InProgress,
`Querying quota info for container ${collection.id}`
);
DataAccessUtilityBase.readCollectionQuotaInfo(collection, options) DataAccessUtilityBase.readCollectionQuotaInfo(collection, options)
.then( .then(
(quota: DataModels.CollectionQuotaInfo) => { (quota: DataModels.CollectionQuotaInfo) => {
deferred.resolve(quota); deferred.resolve(quota);
}, },
(error: any) => { (error: any) => {
logConsoleError(`Error while querying quota info for container ${collection.id}:\n ${JSON.stringify(error)}`); NotificationConsoleUtils.logConsoleMessage(
ConsoleDataType.Error,
`Error while querying quota info for container ${collection.id}:\n ${JSON.stringify(error)}`
);
Logger.logError(JSON.stringify(error), "ReadCollectionQuotaInfo", error.code); Logger.logError(JSON.stringify(error), "ReadCollectionQuotaInfo", error.code);
sendNotificationForError(error); sendNotificationForError(error);
deferred.reject(error); deferred.reject(error);
} }
) )
.finally(() => { .finally(() => {
clearMessage(); NotificationConsoleUtils.clearInProgressMessageWithId(id);
}); });
return deferred.promise; return deferred.promise;
@@ -307,21 +804,24 @@ export function readCollectionQuotaInfo(
export function readOffers(options: any = {}): Q.Promise<DataModels.Offer[]> { export function readOffers(options: any = {}): Q.Promise<DataModels.Offer[]> {
var deferred = Q.defer<DataModels.Offer[]>(); var deferred = Q.defer<DataModels.Offer[]>();
const clearMessage = logConsoleProgress("Querying offers"); const id = NotificationConsoleUtils.logConsoleMessage(ConsoleDataType.InProgress, "Querying offers");
DataAccessUtilityBase.readOffers(options) DataAccessUtilityBase.readOffers(options)
.then( .then(
(offers: DataModels.Offer[]) => { (offers: DataModels.Offer[]) => {
deferred.resolve(offers); deferred.resolve(offers);
}, },
(error: any) => { (error: any) => {
logConsoleError(`Error while querying offers:\n ${JSON.stringify(error)}`); NotificationConsoleUtils.logConsoleMessage(
ConsoleDataType.Error,
`Error while querying offers:\n ${JSON.stringify(error)}`
);
Logger.logError(JSON.stringify(error), "ReadOffers", error.code); Logger.logError(JSON.stringify(error), "ReadOffers", error.code);
sendNotificationForError(error); sendNotificationForError(error);
deferred.reject(error); deferred.reject(error);
} }
) )
.finally(() => { .finally(() => {
clearMessage(); NotificationConsoleUtils.clearInProgressMessageWithId(id);
}); });
return deferred.promise; return deferred.promise;
@@ -333,21 +833,24 @@ export function readOffer(
): Q.Promise<DataModels.OfferWithHeaders> { ): Q.Promise<DataModels.OfferWithHeaders> {
var deferred = Q.defer<DataModels.OfferWithHeaders>(); var deferred = Q.defer<DataModels.OfferWithHeaders>();
const clearMessage = logConsoleProgress("Querying offer"); const id = NotificationConsoleUtils.logConsoleMessage(ConsoleDataType.InProgress, "Querying offer");
DataAccessUtilityBase.readOffer(requestedResource, options) DataAccessUtilityBase.readOffer(requestedResource, options)
.then( .then(
(offer: DataModels.OfferWithHeaders) => { (offer: DataModels.OfferWithHeaders) => {
deferred.resolve(offer); deferred.resolve(offer);
}, },
(error: any) => { (error: any) => {
logConsoleError(`Error while querying offer:\n ${JSON.stringify(error)}`); NotificationConsoleUtils.logConsoleMessage(
ConsoleDataType.Error,
`Error while querying offer:\n ${JSON.stringify(error)}`
);
Logger.logError(JSON.stringify(error), "ReadOffer", error.code); Logger.logError(JSON.stringify(error), "ReadOffer", error.code);
sendNotificationForError(error); sendNotificationForError(error);
deferred.reject(error); deferred.reject(error);
} }
) )
.finally(() => { .finally(() => {
clearMessage(); NotificationConsoleUtils.clearInProgressMessageWithId(id);
}); });
return deferred.promise; return deferred.promise;

View File

@@ -1,5 +1,6 @@
import Q from "q"; import Q from "q";
import * as MessageHandler from "./MessageHandler"; import * as MessageHandler from "./MessageHandler";
import { MessageTypes } from "../Contracts/ExplorerContracts";
describe("Message Handler", () => { describe("Message Handler", () => {
it("should handle cached message", async () => { it("should handle cached message", async () => {
@@ -25,34 +26,4 @@ describe("Message Handler", () => {
MessageHandler.runGarbageCollector(); MessageHandler.runGarbageCollector();
expect(MessageHandler.RequestMap["123"]).toBeUndefined(); expect(MessageHandler.RequestMap["123"]).toBeUndefined();
}); });
describe("getDataExplorerWindow", () => {
it("should return current window if current window has dataExplorerPlatform property", () => {
const currentWindow: Window = { dataExplorerPlatform: 0 } as any;
expect(MessageHandler.getDataExplorerWindow(currentWindow)).toEqual(currentWindow);
});
it("should return current window's parent if current window's parent has dataExplorerPlatform property", () => {
const parentWindow: Window = { dataExplorerPlatform: 0 } as any;
const currentWindow: Window = { parent: parentWindow } as any;
expect(MessageHandler.getDataExplorerWindow(currentWindow)).toEqual(parentWindow);
});
it("should return undefined if none of the windows in the hierarchy have dataExplorerPlatform property and window's parent is reference to itself", () => {
const parentWindow: Window = {} as any;
(parentWindow as any).parent = parentWindow; // If a window does not have a parent, its parent property is a reference to itself.
const currentWindow: Window = { parent: parentWindow } as any;
expect(MessageHandler.getDataExplorerWindow(currentWindow)).toBeUndefined();
});
it("should return undefined if none of the windows in the hierarchy have dataExplorerPlatform property and window's parent is not defined", () => {
const parentWindow: Window = {} as any;
const currentWindow: Window = { parent: parentWindow } as any;
expect(MessageHandler.getDataExplorerWindow(currentWindow)).toBeUndefined();
});
});
}); });

View File

@@ -48,38 +48,16 @@ export function sendCachedDataMessage<TResponseDataModel>(
export function sendMessage(data: any): void { export function sendMessage(data: any): void {
if (canSendMessage()) { if (canSendMessage()) {
const dataExplorerWindow = getDataExplorerWindow(window); window.parent.postMessage(
if (dataExplorerWindow) { {
dataExplorerWindow.parent.postMessage( signature: "pcIframe",
{ data: data
signature: "pcIframe", },
data: data window.document.referrer
}, );
dataExplorerWindow.document.referrer
);
}
} }
} }
// Only exported for unit tests
export const getDataExplorerWindow = (currentWindow: Window): Window | undefined => {
// Start with the current window and traverse up the parent hierarchy to find a window
// with `dataExplorerPlatform` property
let dataExplorerWindow: Window | undefined = currentWindow;
// TODO: Need to `any` here since the window imports Explorer which can't be in strict mode yet
// eslint-disable-next-line @typescript-eslint/no-explicit-any
while (dataExplorerWindow && (dataExplorerWindow as any).dataExplorerPlatform == undefined) {
// If a window does not have a parent, its parent property is a reference to itself.
if (dataExplorerWindow.parent == dataExplorerWindow) {
dataExplorerWindow = undefined;
} else {
dataExplorerWindow = dataExplorerWindow.parent;
}
}
return dataExplorerWindow;
};
export function canSendMessage(): boolean { export function canSendMessage(): boolean {
return window.parent !== window; return window.parent !== window;
} }

View File

@@ -5,7 +5,14 @@ import { Collection } from "../Contracts/ViewModels";
import DocumentId from "../Explorer/Tree/DocumentId"; import DocumentId from "../Explorer/Tree/DocumentId";
import { ResourceProviderClient } from "../ResourceProvider/ResourceProviderClient"; import { ResourceProviderClient } from "../ResourceProvider/ResourceProviderClient";
import { updateUserContext } from "../UserContext"; import { updateUserContext } from "../UserContext";
import { deleteDocument, getEndpoint, queryDocuments, readDocument, updateDocument } from "./MongoProxyClient"; import {
deleteDocument,
getEndpoint,
queryDocuments,
readDocument,
updateDocument,
_createMongoCollectionWithARM
} from "./MongoProxyClient";
jest.mock("../ResourceProvider/ResourceProviderClient.ts"); jest.mock("../ResourceProvider/ResourceProviderClient.ts");
const databaseId = "testDB"; const databaseId = "testDB";
@@ -253,4 +260,58 @@ describe("MongoProxyClient", () => {
expect(endpoint).toEqual("https://main.documentdb.ext.azure.com/api/guest/mongo/explorer"); expect(endpoint).toEqual("https://main.documentdb.ext.azure.com/api/guest/mongo/explorer");
}); });
}); });
describe("createMongoCollectionWithARM", () => {
it("should create a collection with autopilot when autopilot is selected + shared throughput is false", () => {
const resourceProviderClientPutAsyncSpy = jest.spyOn(ResourceProviderClient.prototype, "putAsync");
const properties = {
pk: "state",
coll: "abc-collection",
cd: true,
db: "a1-db",
st: false,
sid: "a2",
rg: "c1",
dba: "main",
is: false
};
_createMongoCollectionWithARM("management.azure.com", properties, { "x-ms-cosmos-offer-autopilot-tier": "1" });
expect(resourceProviderClientPutAsyncSpy).toHaveBeenCalledWith(
"subscriptions/a2/resourceGroups/c1/providers/Microsoft.DocumentDB/databaseAccounts/foo/mongodbDatabases/a1-db/collections/abc-collection",
"2020-04-01",
{
properties: {
options: { "x-ms-cosmos-offer-autopilot-tier": "1" },
resource: { id: "abc-collection" }
}
}
);
});
it("should create a collection with provisioned throughput when provisioned throughput is selected + shared throughput is false", () => {
const resourceProviderClientPutAsyncSpy = jest.spyOn(ResourceProviderClient.prototype, "putAsync");
const properties = {
pk: "state",
coll: "abc-collection",
cd: true,
db: "a1-db",
st: false,
sid: "a2",
rg: "c1",
dba: "main",
is: false,
offerThroughput: 400
};
_createMongoCollectionWithARM("management.azure.com", properties, undefined);
expect(resourceProviderClientPutAsyncSpy).toHaveBeenCalledWith(
"subscriptions/a2/resourceGroups/c1/providers/Microsoft.DocumentDB/databaseAccounts/foo/mongodbDatabases/a1-db/collections/abc-collection",
"2020-04-01",
{
properties: {
options: { throughput: "400" },
resource: { id: "abc-collection" }
}
}
);
});
});
}); });

View File

@@ -1,12 +1,15 @@
import { Constants as CosmosSDKConstants } from "@azure/cosmos"; import { Constants as CosmosSDKConstants } from "@azure/cosmos";
import queryString from "querystring"; import queryString from "querystring";
import { AuthType } from "../AuthType"; import { AuthType } from "../AuthType";
import * as DataExplorerConstants from "../Common/Constants";
import { configContext } from "../ConfigContext"; import { configContext } from "../ConfigContext";
import * as DataModels from "../Contracts/DataModels"; import * as DataModels from "../Contracts/DataModels";
import { MessageTypes } from "../Contracts/ExplorerContracts"; import { MessageTypes } from "../Contracts/ExplorerContracts";
import { Collection } from "../Contracts/ViewModels"; import { Collection } from "../Contracts/ViewModels";
import { ConsoleDataType } from "../Explorer/Menus/NotificationConsole/NotificationConsoleComponent"; import { ConsoleDataType } from "../Explorer/Menus/NotificationConsole/NotificationConsoleComponent";
import DocumentId from "../Explorer/Tree/DocumentId"; import DocumentId from "../Explorer/Tree/DocumentId";
import { ResourceProviderClient } from "../ResourceProvider/ResourceProviderClient";
import { AddDbUtilities } from "../Shared/AddDatabaseUtility";
import * as NotificationConsoleUtils from "../Utils/NotificationConsoleUtils"; import * as NotificationConsoleUtils from "../Utils/NotificationConsoleUtils";
import { ApiType, HttpHeaders, HttpStatusCodes } from "./Constants"; import { ApiType, HttpHeaders, HttpStatusCodes } from "./Constants";
import { userContext } from "../UserContext"; import { userContext } from "../UserContext";
@@ -327,6 +330,48 @@ export function createMongoCollectionWithProxy(
}); });
} }
export function createMongoCollectionWithARM(
armEndpoint: string,
databaseId: string,
analyticalStorageTtl: number,
collectionId: string,
offerThroughput: number,
shardKey: string,
createDatabase: boolean,
sharedThroughput: boolean,
isSharded: boolean,
additionalOptions?: DataModels.RpOptions
): Promise<DataModels.CreateCollectionWithRpResponse> {
const databaseAccount = userContext.databaseAccount;
const params: DataModels.MongoParameters = {
resourceUrl: databaseAccount.properties.mongoEndpoint || databaseAccount.properties.documentEndpoint,
db: databaseId,
coll: collectionId,
pk: shardKey,
offerThroughput,
cd: createDatabase,
st: sharedThroughput,
is: isSharded,
rid: "",
rtype: "colls",
sid: userContext.subscriptionId,
rg: userContext.resourceGroup,
dba: databaseAccount.name,
analyticalStorageTtl
};
if (createDatabase) {
return AddDbUtilities.createMongoDatabaseWithARM(
armEndpoint,
params,
sharedThroughput ? additionalOptions : {}
).then(() => {
return _createMongoCollectionWithARM(armEndpoint, params, sharedThroughput ? {} : additionalOptions);
});
}
return _createMongoCollectionWithARM(armEndpoint, params, additionalOptions);
}
export function getEndpoint(databaseAccount: DataModels.DatabaseAccount): string { export function getEndpoint(databaseAccount: DataModels.DatabaseAccount): string {
const serverId = window.dataExplorer.serverId(); const serverId = window.dataExplorer.serverId();
const extensionEndpoint = window.dataExplorer.extensionEndpoint(); const extensionEndpoint = window.dataExplorer.extensionEndpoint();
@@ -359,3 +404,46 @@ async function errorHandling(response: Response, action: string, params: unknown
export function getARMCreateCollectionEndpoint(params: DataModels.MongoParameters): string { export function getARMCreateCollectionEndpoint(params: DataModels.MongoParameters): string {
return `subscriptions/${params.sid}/resourceGroups/${params.rg}/providers/Microsoft.DocumentDB/databaseAccounts/${userContext.databaseAccount.name}/mongodbDatabases/${params.db}/collections/${params.coll}`; return `subscriptions/${params.sid}/resourceGroups/${params.rg}/providers/Microsoft.DocumentDB/databaseAccounts/${userContext.databaseAccount.name}/mongodbDatabases/${params.db}/collections/${params.coll}`;
} }
export async function _createMongoCollectionWithARM(
armEndpoint: string,
params: DataModels.MongoParameters,
rpOptions: DataModels.RpOptions
): Promise<DataModels.CreateCollectionWithRpResponse> {
const rpPayloadToCreateCollection: DataModels.MongoCreationRequest = {
properties: {
resource: {
id: params.coll
},
options: {}
}
};
if (params.is) {
rpPayloadToCreateCollection.properties.resource["shardKey"] = { [params.pk]: "Hash" };
}
if (!params.st) {
if (rpOptions) {
rpPayloadToCreateCollection.properties.options = rpOptions;
} else {
rpPayloadToCreateCollection.properties.options["throughput"] =
params.offerThroughput && params.offerThroughput.toString();
}
}
if (params.analyticalStorageTtl) {
rpPayloadToCreateCollection.properties.resource.analyticalStorageTtl = params.analyticalStorageTtl;
}
try {
return new ResourceProviderClient<DataModels.CreateCollectionWithRpResponse>(armEndpoint).putAsync(
getARMCreateCollectionEndpoint(params),
DataExplorerConstants.ArmApiVersions.publicVersion,
rpPayloadToCreateCollection
);
} catch (response) {
errorHandling(response, "creating collection", undefined);
return undefined;
}
}

View File

@@ -1,28 +0,0 @@
import { Resource, StoredProcedureDefinition } from "@azure/cosmos";
import { logConsoleError, logConsoleProgress } from "../../Utils/NotificationConsoleUtils";
import { client } from "../CosmosClient";
import { logError } from "../Logger";
import { sendNotificationForError } from "./sendNotificationForError";
export async function createStoredProcedure(
databaseId: string,
collectionId: string,
storedProcedure: StoredProcedureDefinition
): Promise<StoredProcedureDefinition & Resource> {
let createdStoredProcedure: StoredProcedureDefinition & Resource;
const clearMessage = logConsoleProgress(`Creating stored procedure ${storedProcedure.id}`);
try {
const response = await client()
.database(databaseId)
.container(collectionId)
.scripts.storedProcedures.create(storedProcedure);
createdStoredProcedure = response.resource;
} catch (error) {
logConsoleError(`Error while creating stored procedure ${storedProcedure.id}:\n ${JSON.stringify(error)}`);
logError(JSON.stringify(error), "CreateStoredProcedure", error.code);
sendNotificationForError(error);
}
clearMessage();
return createdStoredProcedure;
}

View File

@@ -1,28 +0,0 @@
import { Resource, TriggerDefinition } from "@azure/cosmos";
import { logConsoleError, logConsoleProgress } from "../../Utils/NotificationConsoleUtils";
import { client } from "../CosmosClient";
import { logError } from "../Logger";
import { sendNotificationForError } from "./sendNotificationForError";
export async function createTrigger(
databaseId: string,
collectionId: string,
trigger: TriggerDefinition
): Promise<TriggerDefinition & Resource> {
let createdTrigger: TriggerDefinition & Resource;
const clearMessage = logConsoleProgress(`Creating trigger ${trigger.id}`);
try {
const response = await client()
.database(databaseId)
.container(collectionId)
.scripts.triggers.create(trigger);
createdTrigger = response.resource;
} catch (error) {
logConsoleError(`Error while creating trigger ${trigger.id}:\n ${JSON.stringify(error)}`);
logError(JSON.stringify(error), "CreateTrigger", error.code);
sendNotificationForError(error);
}
clearMessage();
return createdTrigger;
}

View File

@@ -1,28 +0,0 @@
import { Resource, UserDefinedFunctionDefinition } from "@azure/cosmos";
import { logConsoleError, logConsoleProgress } from "../../Utils/NotificationConsoleUtils";
import { client } from "../CosmosClient";
import { logError } from "../Logger";
import { sendNotificationForError } from "./sendNotificationForError";
export async function createUserDefinedFunction(
databaseId: string,
collectionId: string,
userDefinedFunction: UserDefinedFunctionDefinition
): Promise<UserDefinedFunctionDefinition & Resource> {
let createdUserDefinedFunction: UserDefinedFunctionDefinition & Resource;
const clearMessage = logConsoleProgress(`Creating user defined function ${userDefinedFunction.id}`);
try {
const response = await client()
.database(databaseId)
.container(collectionId)
.scripts.userDefinedFunctions.create(userDefinedFunction);
createdUserDefinedFunction = response.resource;
} catch (error) {
logConsoleError(`Error while creating user defined function ${userDefinedFunction.id}:\n ${JSON.stringify(error)}`);
logError(JSON.stringify(error), "CreateUserupdateUserDefinedFunction", error.code);
sendNotificationForError(error);
}
clearMessage();
return createdUserDefinedFunction;
}

View File

@@ -1,26 +0,0 @@
import { logConsoleError, logConsoleProgress } from "../../Utils/NotificationConsoleUtils";
import { client } from "../CosmosClient";
import { logError } from "../Logger";
import { sendNotificationForError } from "./sendNotificationForError";
export async function deleteStoredProcedure(
databaseId: string,
collectionId: string,
storedProcedureId: string
): Promise<void> {
const clearMessage = logConsoleProgress(`Deleting stored procedure ${storedProcedureId}`);
try {
await client()
.database(databaseId)
.container(collectionId)
.scripts.storedProcedure(storedProcedureId)
.delete();
} catch (error) {
logConsoleError(`Error while deleting stored procedure ${storedProcedureId}:\n ${JSON.stringify(error)}`);
logError(JSON.stringify(error), "DeleteStoredProcedure", error.code);
sendNotificationForError(error);
}
clearMessage();
return undefined;
}

View File

@@ -1,22 +0,0 @@
import { logConsoleError, logConsoleProgress } from "../../Utils/NotificationConsoleUtils";
import { client } from "../CosmosClient";
import { logError } from "../Logger";
import { sendNotificationForError } from "./sendNotificationForError";
export async function deleteTrigger(databaseId: string, collectionId: string, triggerId: string): Promise<void> {
const clearMessage = logConsoleProgress(`Deleting trigger ${triggerId}`);
try {
await client()
.database(databaseId)
.container(collectionId)
.scripts.trigger(triggerId)
.delete();
} catch (error) {
logConsoleError(`Error while deleting trigger ${triggerId}:\n ${JSON.stringify(error)}`);
logError(JSON.stringify(error), "DeleteTrigger", error.code);
sendNotificationForError(error);
}
clearMessage();
return undefined;
}

View File

@@ -1,22 +0,0 @@
import { logConsoleError, logConsoleProgress } from "../../Utils/NotificationConsoleUtils";
import { client } from "../CosmosClient";
import { logError } from "../Logger";
import { sendNotificationForError } from "./sendNotificationForError";
export async function deleteUserDefinedFunction(databaseId: string, collectionId: string, id: string): Promise<void> {
const clearMessage = logConsoleProgress(`Deleting user defined function ${id}`);
try {
await client()
.database(databaseId)
.container(collectionId)
.scripts.userDefinedFunction(id)
.delete();
} catch (error) {
logConsoleError(`Error while deleting user defined function ${id}:\n ${JSON.stringify(error)}`);
logError(JSON.stringify(error), "DeleteUserDefinedFunction", error.code);
sendNotificationForError(error);
}
clearMessage();
return undefined;
}

View File

@@ -1,27 +0,0 @@
import { Resource, StoredProcedureDefinition } from "@azure/cosmos";
import { logConsoleError, logConsoleProgress } from "../../Utils/NotificationConsoleUtils";
import { client } from "../CosmosClient";
import { logError } from "../Logger";
import { sendNotificationForError } from "./sendNotificationForError";
export async function readStoredProcedures(
databaseId: string,
collectionId: string
): Promise<(StoredProcedureDefinition & Resource)[]> {
let sprocs: (StoredProcedureDefinition & Resource)[];
const clearMessage = logConsoleProgress(`Querying stored procedures for container ${collectionId}`);
try {
const response = await client()
.database(databaseId)
.container(collectionId)
.scripts.storedProcedures.readAll()
.fetchAll();
sprocs = response.resources;
} catch (error) {
logConsoleError(`Failed to query stored procedures for container ${collectionId}: ${JSON.stringify(error)}`);
logError(JSON.stringify(error), "ReadStoredProcedures", error.code);
sendNotificationForError(error);
}
clearMessage();
return sprocs;
}

View File

@@ -1,27 +0,0 @@
import { Resource, TriggerDefinition } from "@azure/cosmos";
import { logConsoleError, logConsoleProgress } from "../../Utils/NotificationConsoleUtils";
import { client } from "../CosmosClient";
import { logError } from "../Logger";
import { sendNotificationForError } from "./sendNotificationForError";
export async function readTriggers(
databaseId: string,
collectionId: string
): Promise<(TriggerDefinition & Resource)[]> {
let triggers: (TriggerDefinition & Resource)[];
const clearMessage = logConsoleProgress(`Querying triggers for container ${collectionId}`);
try {
const response = await client()
.database(databaseId)
.container(collectionId)
.scripts.triggers.readAll()
.fetchAll();
triggers = response.resources;
} catch (error) {
logConsoleError(`Failed to query triggers for container ${collectionId}: ${JSON.stringify(error)}`);
logError(JSON.stringify(error), "ReadTriggers", error.code);
sendNotificationForError(error);
}
clearMessage();
return triggers;
}

View File

@@ -1,27 +0,0 @@
import { Resource, UserDefinedFunctionDefinition } from "@azure/cosmos";
import { logConsoleError, logConsoleProgress } from "../../Utils/NotificationConsoleUtils";
import { client } from "../CosmosClient";
import { logError } from "../Logger";
import { sendNotificationForError } from "./sendNotificationForError";
export async function readUserDefinedFunctions(
databaseId: string,
collectionId: string
): Promise<(UserDefinedFunctionDefinition & Resource)[]> {
let udfs: (UserDefinedFunctionDefinition & Resource)[];
const clearMessage = logConsoleProgress(`Querying user defined functions for container ${collectionId}`);
try {
const response = await client()
.database(databaseId)
.container(collectionId)
.scripts.userDefinedFunctions.readAll()
.fetchAll();
udfs = response.resources;
} catch (error) {
logConsoleError(`Failed to query user defined functions for container ${collectionId}: ${JSON.stringify(error)}`);
logError(JSON.stringify(error), "ReadUserDefinedFunctions", error.code);
sendNotificationForError(error);
}
clearMessage();
return udfs;
}

View File

@@ -1,29 +0,0 @@
import { Resource, StoredProcedureDefinition } from "@azure/cosmos";
import { logConsoleError, logConsoleProgress } from "../../Utils/NotificationConsoleUtils";
import { client } from "../CosmosClient";
import { logError } from "../Logger";
import { sendNotificationForError } from "./sendNotificationForError";
export async function updateStoredProcedure(
databaseId: string,
collectionId: string,
storedProcedure: StoredProcedureDefinition
): Promise<StoredProcedureDefinition & Resource> {
let updatedStoredProcedure: StoredProcedureDefinition & Resource;
const clearMessage = logConsoleProgress(`Updating stored procedure ${storedProcedure.id}`);
try {
const response = await client()
.database(databaseId)
.container(collectionId)
.scripts.storedProcedure(storedProcedure.id)
.replace(storedProcedure);
updatedStoredProcedure = response.resource;
} catch (error) {
logConsoleError(`Error while updating stored procedure ${storedProcedure.id}:\n ${JSON.stringify(error)}`);
logError(JSON.stringify(error), "UpdateStoredProcedure", error.code);
sendNotificationForError(error);
}
clearMessage();
return updatedStoredProcedure;
}

View File

@@ -1,29 +0,0 @@
import { TriggerDefinition } from "@azure/cosmos";
import { logConsoleError, logConsoleProgress } from "../../Utils/NotificationConsoleUtils";
import { client } from "../CosmosClient";
import { logError } from "../Logger";
import { sendNotificationForError } from "./sendNotificationForError";
export async function updateTrigger(
databaseId: string,
collectionId: string,
trigger: TriggerDefinition
): Promise<TriggerDefinition> {
let updatedTrigger: TriggerDefinition;
const clearMessage = logConsoleProgress(`Updating trigger ${trigger.id}`);
try {
const response = await client()
.database(databaseId)
.container(collectionId)
.scripts.trigger(trigger.id)
.replace(trigger);
updatedTrigger = response.resource;
} catch (error) {
logConsoleError(`Error while updating trigger ${trigger.id}:\n ${JSON.stringify(error)}`);
logError(JSON.stringify(error), "UpdateTrigger", error.code);
sendNotificationForError(error);
}
clearMessage();
return updatedTrigger;
}

View File

@@ -1,29 +0,0 @@
import { Resource, UserDefinedFunctionDefinition } from "@azure/cosmos";
import { logConsoleError, logConsoleProgress } from "../../Utils/NotificationConsoleUtils";
import { client } from "../CosmosClient";
import { logError } from "../Logger";
import { sendNotificationForError } from "./sendNotificationForError";
export async function updateUserDefinedFunction(
databaseId: string,
collectionId: string,
userDefinedFunction: UserDefinedFunctionDefinition
): Promise<UserDefinedFunctionDefinition & Resource> {
let updatedUserDefinedFunction: UserDefinedFunctionDefinition & Resource;
const clearMessage = logConsoleProgress(`Updating user defined function ${userDefinedFunction.id}`);
try {
const response = await client()
.database(databaseId)
.container(collectionId)
.scripts.userDefinedFunction(userDefinedFunction.id)
.replace(userDefinedFunction);
updatedUserDefinedFunction = response.resource;
} catch (error) {
logConsoleError(`Error while updating user defined function ${userDefinedFunction.id}:\n ${JSON.stringify(error)}`);
logError(JSON.stringify(error), "UpdateUserupdateUserDefinedFunction", error.code);
sendNotificationForError(error);
}
clearMessage();
return updatedUserDefinedFunction;
}

View File

@@ -6,7 +6,7 @@ export enum Platform {
interface ConfigContext { interface ConfigContext {
platform: Platform; platform: Platform;
allowedParentFrameOrigins: string[]; allowedParentFrameOrigins: RegExp;
gitSha?: string; gitSha?: string;
proxyPath?: string; proxyPath?: string;
AAD_ENDPOINT: string; AAD_ENDPOINT: string;
@@ -30,12 +30,7 @@ interface ConfigContext {
// Default configuration // Default configuration
let configContext: Readonly<ConfigContext> = { let configContext: Readonly<ConfigContext> = {
platform: Platform.Portal, platform: Platform.Portal,
allowedParentFrameOrigins: [ allowedParentFrameOrigins: /^https:\/\/portal\.azure\.com$|^https:\/\/portal\.azure\.us$|^https:\/\/portal\.azure\.cn$|^https:\/\/portal\.microsoftazure\.de$|^https:\/\/.+\.portal\.azure\.com$|^https:\/\/.+\.portal\.azure\.us$|^https:\/\/.+\.portal\.azure\.cn$|^https:\/\/.+\.portal\.microsoftazure\.de$|^https:\/\/main\.documentdb\.ext\.azure\.com$|^https:\/\/main\.documentdb\.ext\.microsoftazure\.de$|^https:\/\/main\.documentdb\.ext\.azure\.cn$|^https:\/\/main\.documentdb\.ext\.azure\.us$/,
`^https:\\/\\/cosmos.azure.(com|cn|us)$`,
`^https:\\/\\/[\\.\\w]+.portal.azure.(com|cn|us)$`,
`^https:\\/\\/[\\.\\w]+.ext.azure.(com|cn|us)$`,
`^https:\\/\\/[\\.\\w]+microsoftazure.de$`
],
// Webpack injects this at build time // Webpack injects this at build time
gitSha: process.env.GIT_SHA, gitSha: process.env.GIT_SHA,
hostedExplorerURL: "https://cosmos.azure.com/", hostedExplorerURL: "https://cosmos.azure.com/",
@@ -78,13 +73,8 @@ export async function initializeConfiguration(): Promise<ConfigContext> {
const response = await fetch("./config.json"); const response = await fetch("./config.json");
if (response.status === 200) { if (response.status === 200) {
try { try {
const { allowedParentFrameOrigins, ...externalConfig } = await response.json(); const externalConfig = await response.json();
Object.assign(configContext, externalConfig); Object.assign(configContext, externalConfig);
if (allowedParentFrameOrigins && allowedParentFrameOrigins.length > 0) {
updateConfigContext({
allowedParentFrameOrigins: [...configContext.allowedParentFrameOrigins, ...allowedParentFrameOrigins]
});
}
} catch (error) { } catch (error) {
console.error("Unable to parse json in config file"); console.error("Unable to parse json in config file");
console.error(error); console.error(error);

View File

@@ -88,6 +88,10 @@ export interface Resource {
id: string; id: string;
} }
export interface ResourceRequest {
id: string;
}
export interface Collection extends Resource { export interface Collection extends Resource {
defaultTtl?: number; defaultTtl?: number;
indexingPolicy?: IndexingPolicy; indexingPolicy?: IndexingPolicy;
@@ -100,12 +104,39 @@ export interface Collection extends Resource {
geospatialConfig?: GeospatialConfig; geospatialConfig?: GeospatialConfig;
} }
export interface CreateCollectionWithRpResponse extends Resource {
properties: Collection;
name: string;
type: string;
}
export interface CollectionRequest extends ResourceRequest {
defaultTtl?: number;
indexingPolicy?: IndexingPolicy;
partitionKey?: PartitionKey;
uniqueKeyPolicy?: UniqueKeyPolicy;
conflictResolutionPolicy?: ConflictResolutionPolicy;
}
export interface Database extends Resource { export interface Database extends Resource {
collections?: Collection[]; collections?: Collection[];
} }
export interface DocumentId extends Resource {} export interface DocumentId extends Resource {}
export interface Script extends Resource {
body: string;
}
export interface StoredProcedure extends Script {}
export interface UserDefinedFunction extends Script {}
export interface Trigger extends Script {
triggerType: string;
triggerOperation: string;
}
export interface ConflictId extends Resource { export interface ConflictId extends Resource {
resourceId?: string; resourceId?: string;
resourceType?: string; resourceType?: string;
@@ -228,6 +259,28 @@ export interface ErrorDataModel {
code?: string; code?: string;
} }
/**
* Defines a property bag for telemetry e.g. see ITelemetryError.
*/
export interface ITelemetryProperties {
[propertyName: string]: string;
}
/**
* Defines a property bag for telemetry e.g. see ITelemetryError.
*/
export interface ITelemetryEvent {
name: string;
properties?: ITelemetryProperties;
}
/**
* Defines an error to be logged as telemetry data.
*/
export interface ITelemetryError extends ITelemetryEvent {
error: any;
}
export interface CreateDatabaseAndCollectionRequest { export interface CreateDatabaseAndCollectionRequest {
databaseId: string; databaseId: string;
collectionId: string; collectionId: string;
@@ -254,6 +307,11 @@ export enum AutopilotTier {
Tier4 = 4 Tier4 = 4
} }
export interface RpOptions {
// tier is sent as string, autoscale as object (AutoPilotCreationSettings)
[key: string]: string | AutoPilotCreationSettings;
}
export interface Query { export interface Query {
id: string; id: string;
resourceId: string; resourceId: string;
@@ -289,6 +347,12 @@ export interface CreateCollectionParams {
uniqueKeyPolicy?: UniqueKeyPolicy; uniqueKeyPolicy?: UniqueKeyPolicy;
} }
export interface SharedThroughputRange {
minimumRU: number;
maximumRU: number;
defaultRU: number;
}
export interface Notification { export interface Notification {
id: string; id: string;
kind: string; kind: string;
@@ -431,6 +495,25 @@ export interface NotebookConfigurationEndpointInfo {
token: string; token: string;
} }
export interface SparkCluster {
id: string;
name: string;
type: string;
properties: {
kind: string;
driverSize: string;
workerSize: string;
workerInstanceCount: number;
creationTime: string;
status: string;
libraries?: SparkClusterLibrary[];
};
}
export interface SparkClusterFeedResponse {
value: SparkCluster[];
}
export interface SparkClusterConnectionInfo { export interface SparkClusterConnectionInfo {
userName: string; userName: string;
password: string; password: string;
@@ -472,10 +555,79 @@ export interface MongoParameters extends RpParameters {
analyticalStorageTtl?: number; analyticalStorageTtl?: number;
} }
export interface GraphParameters extends RpParameters {
pk: string;
coll: string;
cd: Boolean;
indexingPolicy?: IndexingPolicy;
}
export interface CreationRequest {
properties: {
resource: {
id: string;
};
options: RpOptions;
};
}
export interface SqlCollectionParameters extends RpParameters {
uniqueKeyPolicy?: UniqueKeyPolicy;
pk: string;
coll: string;
cd: Boolean;
analyticalStorageTtl?: number;
indexingPolicy?: IndexingPolicy;
}
export interface MongoCreationRequest extends CreationRequest {
properties: {
resource: {
id: string;
analyticalStorageTtl?: number;
shardKey?: {};
};
options: RpOptions;
};
}
export interface GraphCreationRequest extends CreationRequest {
properties: {
resource: {
id: string;
partitionKey: {};
indexingPolicy?: IndexingPolicy;
};
options: RpOptions;
};
}
export interface CreateDatabaseWithRpResponse {
id: string;
name: string;
type: string;
properties: {
id: string;
};
}
export interface SparkClusterLibrary { export interface SparkClusterLibrary {
name: string; name: string;
} }
export interface SqlCollectionCreationRequest extends CreationRequest {
properties: {
resource: {
uniqueKeyPolicy?: UniqueKeyPolicy;
id: string;
partitionKey: {};
analyticalStorageTtl?: number;
indexingPolicy?: IndexingPolicy;
};
options: RpOptions;
};
}
export interface Library extends SparkClusterLibrary { export interface Library extends SparkClusterLibrary {
properties: { properties: {
kind: "Jar"; kind: "Jar";

View File

@@ -1,22 +1,16 @@
import { import * as DataModels from "./DataModels";
QueryMetrics,
Resource,
StoredProcedureDefinition,
TriggerDefinition,
UserDefinedFunctionDefinition
} from "@azure/cosmos";
import Q from "q"; import Q from "q";
import { CommandButtonComponentProps } from "../Explorer/Controls/CommandButton/CommandButtonComponent";
import Explorer from "../Explorer/Explorer";
import { ConsoleData } from "../Explorer/Menus/NotificationConsole/NotificationConsoleComponent";
import { CassandraTableKey, CassandraTableKeys } from "../Explorer/Tables/TableDataClient"; import { CassandraTableKey, CassandraTableKeys } from "../Explorer/Tables/TableDataClient";
import ConflictId from "../Explorer/Tree/ConflictId"; import { CommandButtonComponentProps } from "../Explorer/Controls/CommandButton/CommandButtonComponent";
import DocumentId from "../Explorer/Tree/DocumentId"; import { ConsoleData } from "../Explorer/Menus/NotificationConsole/NotificationConsoleComponent";
import { QueryMetrics } from "@azure/cosmos";
import { UploadDetails } from "../workers/upload/definitions";
import Explorer from "../Explorer/Explorer";
import UserDefinedFunction from "../Explorer/Tree/UserDefinedFunction";
import StoredProcedure from "../Explorer/Tree/StoredProcedure"; import StoredProcedure from "../Explorer/Tree/StoredProcedure";
import Trigger from "../Explorer/Tree/Trigger"; import Trigger from "../Explorer/Tree/Trigger";
import UserDefinedFunction from "../Explorer/Tree/UserDefinedFunction"; import DocumentId from "../Explorer/Tree/DocumentId";
import { UploadDetails } from "../workers/upload/definitions"; import ConflictId from "../Explorer/Tree/ConflictId";
import * as DataModels from "./DataModels";
export interface TokenProvider { export interface TokenProvider {
getAuthHeader(): Promise<Headers>; getAuthHeader(): Promise<Headers>;
@@ -159,13 +153,13 @@ export interface Collection extends CollectionBase {
collapseUserDefinedFunctions(): void; collapseUserDefinedFunctions(): void;
collapseTriggers(): void; collapseTriggers(): void;
loadUserDefinedFunctions(): Promise<any>; loadUserDefinedFunctions(): Q.Promise<any>;
loadStoredProcedures(): Promise<any>; loadStoredProcedures(): Q.Promise<any>;
loadTriggers(): Promise<any>; loadTriggers(): Q.Promise<any>;
createStoredProcedureNode(data: StoredProcedureDefinition & Resource): StoredProcedure; createStoredProcedureNode(data: DataModels.StoredProcedure): StoredProcedure;
createUserDefinedFunctionNode(data: UserDefinedFunctionDefinition & Resource): UserDefinedFunction; createUserDefinedFunctionNode(data: DataModels.UserDefinedFunction): UserDefinedFunction;
createTriggerNode(data: TriggerDefinition & Resource): Trigger; createTriggerNode(data: DataModels.Trigger): Trigger;
findStoredProcedureWithId(sprocRid: string): StoredProcedure; findStoredProcedureWithId(sprocRid: string): StoredProcedure;
findTriggerWithId(triggerRid: string): Trigger; findTriggerWithId(triggerRid: string): Trigger;
findUserDefinedFunctionWithId(udfRid: string): UserDefinedFunction; findUserDefinedFunctionWithId(udfRid: string): UserDefinedFunction;

View File

@@ -1,6 +1,6 @@
import { StringUtils } from "../../../Utils/StringUtils"; import { StringUtils } from "../../../Utils/StringUtils";
import { KeyCodes } from "../../../Common/Constants"; import { KeyCodes } from "../../../Common/Constants";
import * as TelemetryProcessor from "../../../Shared/Telemetry/TelemetryProcessor"; import TelemetryProcessor from "../../../Shared/Telemetry/TelemetryProcessor";
import { Action, ActionModifiers } from "../../../Shared/Telemetry/TelemetryConstants"; import { Action, ActionModifiers } from "../../../Shared/Telemetry/TelemetryConstants";
import CollapseChevronDownIcon from "../../../../images/QueryBuilder/CollapseChevronDown_16x.png"; import CollapseChevronDownIcon from "../../../../images/QueryBuilder/CollapseChevronDown_16x.png";

View File

@@ -265,9 +265,6 @@ exports[`test render renders with filters 1`] = `
"buttonTextDisabled": "#a19f9d", "buttonTextDisabled": "#a19f9d",
"buttonTextHovered": "#201f1e", "buttonTextHovered": "#201f1e",
"buttonTextPressed": "#201f1e", "buttonTextPressed": "#201f1e",
"cardShadow": "0 1.6px 3.6px 0 rgba(0, 0, 0, 0.132), 0 0.3px 0.9px 0 rgba(0, 0, 0, 0.108)",
"cardShadowHovered": "0 3.2px 7.2px 0 rgba(0, 0, 0, 0.132), 0 0.6px 1.8px 0 rgba(0, 0, 0, 0.108)",
"cardStandoutBackground": "#ffffff",
"defaultStateBackground": "#faf9f8", "defaultStateBackground": "#faf9f8",
"disabledBackground": "#f3f2f1", "disabledBackground": "#f3f2f1",
"disabledBodySubtext": "#c8c6c4", "disabledBodySubtext": "#c8c6c4",
@@ -607,9 +604,6 @@ exports[`test render renders with filters 1`] = `
"buttonTextDisabled": "#a19f9d", "buttonTextDisabled": "#a19f9d",
"buttonTextHovered": "#201f1e", "buttonTextHovered": "#201f1e",
"buttonTextPressed": "#201f1e", "buttonTextPressed": "#201f1e",
"cardShadow": "0 1.6px 3.6px 0 rgba(0, 0, 0, 0.132), 0 0.3px 0.9px 0 rgba(0, 0, 0, 0.108)",
"cardShadowHovered": "0 3.2px 7.2px 0 rgba(0, 0, 0, 0.132), 0 0.6px 1.8px 0 rgba(0, 0, 0, 0.108)",
"cardStandoutBackground": "#ffffff",
"defaultStateBackground": "#faf9f8", "defaultStateBackground": "#faf9f8",
"disabledBackground": "#f3f2f1", "disabledBackground": "#f3f2f1",
"disabledBodySubtext": "#c8c6c4", "disabledBodySubtext": "#c8c6c4",
@@ -1003,9 +997,6 @@ exports[`test render renders with filters 1`] = `
"buttonTextDisabled": "#a19f9d", "buttonTextDisabled": "#a19f9d",
"buttonTextHovered": "#201f1e", "buttonTextHovered": "#201f1e",
"buttonTextPressed": "#201f1e", "buttonTextPressed": "#201f1e",
"cardShadow": "0 1.6px 3.6px 0 rgba(0, 0, 0, 0.132), 0 0.3px 0.9px 0 rgba(0, 0, 0, 0.108)",
"cardShadowHovered": "0 3.2px 7.2px 0 rgba(0, 0, 0, 0.132), 0 0.6px 1.8px 0 rgba(0, 0, 0, 0.108)",
"cardStandoutBackground": "#ffffff",
"defaultStateBackground": "#faf9f8", "defaultStateBackground": "#faf9f8",
"disabledBackground": "#f3f2f1", "disabledBackground": "#f3f2f1",
"disabledBodySubtext": "#c8c6c4", "disabledBodySubtext": "#c8c6c4",
@@ -1122,11 +1113,6 @@ exports[`test render renders with filters 1`] = `
}, },
"iconDisabled": Object { "iconDisabled": Object {
"color": "#a19f9d", "color": "#a19f9d",
"selectors": Object {
"@media screen and (-ms-high-contrast: active)": Object {
"color": "GrayText",
},
},
}, },
"label": Array [ "label": Array [
Object { Object {
@@ -1148,11 +1134,6 @@ exports[`test render renders with filters 1`] = `
}, },
"menuIconDisabled": Object { "menuIconDisabled": Object {
"color": "#a19f9d", "color": "#a19f9d",
"selectors": Object {
"@media screen and (-ms-high-contrast: active)": Object {
"color": "GrayText",
},
},
}, },
"root": Array [ "root": Array [
Object { Object {
@@ -1169,6 +1150,7 @@ exports[`test render renders with filters 1`] = `
"right": 2, "right": 2,
"selectors": Object { "selectors": Object {
"@media screen and (-ms-high-contrast: active)": Object { "@media screen and (-ms-high-contrast: active)": Object {
"border": "none",
"bottom": -2, "bottom": -2,
"left": -2, "left": -2,
"outlineColor": "ButtonText", "outlineColor": "ButtonText",
@@ -1248,6 +1230,7 @@ exports[`test render renders with filters 1`] = `
"right": 2, "right": 2,
"selectors": Object { "selectors": Object {
"@media screen and (-ms-high-contrast: active)": Object { "@media screen and (-ms-high-contrast: active)": Object {
"border": "none",
"bottom": -2, "bottom": -2,
"left": -2, "left": -2,
"outlineColor": "ButtonText", "outlineColor": "ButtonText",
@@ -1276,6 +1259,10 @@ exports[`test render renders with filters 1`] = `
":hover": Object { ":hover": Object {
"outline": 0, "outline": 0,
}, },
"@media screen and (-ms-high-contrast: active)": Object {
"borderColor": "grayText",
"color": "grayText",
},
}, },
}, },
Object { Object {
@@ -1375,21 +1362,13 @@ exports[`test render renders with filters 1`] = `
"selectors": Object { "selectors": Object {
"@media screen and (-ms-high-contrast: active)": Object { "@media screen and (-ms-high-contrast: active)": Object {
"MsHighContrastAdjust": "none", "MsHighContrastAdjust": "none",
"backgroundColor": "Window", "backgroundColor": "WindowText",
"border": "1px solid WindowText", "color": "Window",
"borderRightWidth": "0",
"color": "WindowText",
}, },
}, },
}, },
".ms-Button--primary + .ms-Button": Object { ".ms-Button--primary + .ms-Button": Object {
"border": "none", "border": "none",
"selectors": Object {
"@media screen and (-ms-high-contrast: active)": Object {
"border": "1px solid WindowText",
"borderLeftWidth": "0",
},
},
}, },
}, },
}, },
@@ -1429,9 +1408,6 @@ exports[`test render renders with filters 1`] = `
"borderColor": "GrayText", "borderColor": "GrayText",
"color": "GrayText", "color": "GrayText",
}, },
"@media screen and (forced-colors: active)": Object {
"forcedColorAdjust": "none",
},
}, },
}, },
"splitButtonContainerFocused": Object { "splitButtonContainerFocused": Object {
@@ -1578,13 +1554,6 @@ exports[`test render renders with filters 1`] = `
}, },
}, },
}, },
".ms-Button-menuIcon": Object {
"selectors": Object {
"@media screen and (-ms-high-contrast: active)": Object {
"color": "GrayText",
},
},
},
":hover": Object { ":hover": Object {
"cursor": "default", "cursor": "default",
}, },
@@ -1806,9 +1775,6 @@ exports[`test render renders with filters 1`] = `
"buttonTextDisabled": "#a19f9d", "buttonTextDisabled": "#a19f9d",
"buttonTextHovered": "#201f1e", "buttonTextHovered": "#201f1e",
"buttonTextPressed": "#201f1e", "buttonTextPressed": "#201f1e",
"cardShadow": "0 1.6px 3.6px 0 rgba(0, 0, 0, 0.132), 0 0.3px 0.9px 0 rgba(0, 0, 0, 0.108)",
"cardShadowHovered": "0 3.2px 7.2px 0 rgba(0, 0, 0, 0.132), 0 0.6px 1.8px 0 rgba(0, 0, 0, 0.108)",
"cardStandoutBackground": "#ffffff",
"defaultStateBackground": "#faf9f8", "defaultStateBackground": "#faf9f8",
"disabledBackground": "#f3f2f1", "disabledBackground": "#f3f2f1",
"disabledBodySubtext": "#c8c6c4", "disabledBodySubtext": "#c8c6c4",

View File

@@ -86,7 +86,6 @@ export class DynamicListViewModel extends WaitsForTemplateViewModel {
public onRemoveItemKeyPress = (data: any, event: KeyboardEvent, source: any): boolean => { public onRemoveItemKeyPress = (data: any, event: KeyboardEvent, source: any): boolean => {
if (event.keyCode === KeyCodes.Enter || event.keyCode === KeyCodes.Space) { if (event.keyCode === KeyCodes.Enter || event.keyCode === KeyCodes.Space) {
this.removeItem(data, event); this.removeItem(data, event);
(document.querySelector(".dynamicListItem:last-of-type input") as HTMLElement).focus();
event.stopPropagation(); event.stopPropagation();
return false; return false;
} }
@@ -95,7 +94,7 @@ export class DynamicListViewModel extends WaitsForTemplateViewModel {
public addItem(): void { public addItem(): void {
this.listItems.push({ value: ko.observable("") }); this.listItems.push({ value: ko.observable("") });
(document.querySelector(".dynamicListItem:last-of-type input") as HTMLElement).focus(); document.getElementById("uniqueKeyItems").focus();
} }
public onAddItemKeyPress = (source: any, event: KeyboardEvent): boolean => { public onAddItemKeyPress = (source: any, event: KeyboardEvent): boolean => {

View File

@@ -6,7 +6,7 @@ import { RepoListItem } from "./GitHubReposComponent";
import { ChildrenMargin } from "./GitHubStyleConstants"; import { ChildrenMargin } from "./GitHubStyleConstants";
import * as GitHubUtils from "../../../Utils/GitHubUtils"; import * as GitHubUtils from "../../../Utils/GitHubUtils";
import { IGitHubRepo } from "../../../GitHub/GitHubClient"; import { IGitHubRepo } from "../../../GitHub/GitHubClient";
import * as TelemetryProcessor from "../../../Shared/Telemetry/TelemetryProcessor"; import TelemetryProcessor from "../../../Shared/Telemetry/TelemetryProcessor";
import UrlUtility from "../../../Common/UrlUtility"; import UrlUtility from "../../../Common/UrlUtility";
import Explorer from "../../Explorer"; import Explorer from "../../Explorer";

View File

@@ -66,9 +66,7 @@ export class GitHubReposComponent extends React.Component<GitHubReposComponentPr
return ( return (
<> <>
<div className={"firstdivbg headerline"} role="heading" aria-level={2}> <div className={"firstdivbg headerline"}>{header}</div>
{header}
</div>
<div className={"paneMainContent"}>{content}</div> <div className={"paneMainContent"}>{content}</div>
{!this.props.showAuthorizeAccess && ( {!this.props.showAuthorizeAccess && (
<> <>

View File

@@ -24,7 +24,7 @@ import {
} from "office-ui-fabric-react/lib/utilities/selection/index"; } from "office-ui-fabric-react/lib/utilities/selection/index";
import { StyleConstants } from "../../../Common/Constants"; import { StyleConstants } from "../../../Common/Constants";
import { TextField, ITextFieldProps, ITextField } from "office-ui-fabric-react/lib/TextField"; import { TextField, ITextFieldProps, ITextField } from "office-ui-fabric-react/lib/TextField";
import * as TelemetryProcessor from "../../../Shared/Telemetry/TelemetryProcessor"; import TelemetryProcessor from "../../../Shared/Telemetry/TelemetryProcessor";
import SaveQueryBannerIcon from "../../../../images/save_query_banner.png"; import SaveQueryBannerIcon from "../../../../images/save_query_banner.png";
import { QueriesClient } from "../../../Common/QueriesClient"; import { QueriesClient } from "../../../Common/QueriesClient";

View File

@@ -84,7 +84,7 @@
step: step, step: step,
'class':'migration collid select-font-size', 'class':'migration collid select-font-size',
min: minAutoPilotThroughput, min: minAutoPilotThroughput,
'aria-label': 'Max request units per second', 'aria-label': ariaLabel,
type: isAutoscaleThroughputInputFieldRequired() ? 'number' : 'hidden', type: isAutoscaleThroughputInputFieldRequired() ? 'number' : 'hidden',
css: { css: {
dirty: maxAutoPilotThroughputSet.editableIsDirty dirty: maxAutoPilotThroughputSet.editableIsDirty

View File

@@ -282,7 +282,7 @@ export class TreeNodeComponent extends React.Component<TreeNodeComponentProps, T
<img <img
className="expandCollapseIcon" className="expandCollapseIcon"
src={this.state.isExpanded ? TriangleDownIcon : TriangleRightIcon} src={this.state.isExpanded ? TriangleDownIcon : TriangleRightIcon}
alt={this.state.isExpanded ? `${node.label} branch is expanded` : `${node.label} branch is collapsed`} alt={this.state.isExpanded ? "Branch is expanded" : "Branch is collapsed"}
onKeyPress={(event: React.KeyboardEvent<HTMLDivElement>) => this.onCollapseExpandIconKeyPress(event, node)} onKeyPress={(event: React.KeyboardEvent<HTMLDivElement>) => this.onCollapseExpandIconKeyPress(event, node)}
tabIndex={0} tabIndex={0}
role="button" role="button"

View File

@@ -49,7 +49,7 @@ exports[`TreeNodeComponent does not render children by default 1`] = `
tabIndex={-1} tabIndex={-1}
> >
<img <img
alt="label branch is collapsed" alt="Branch is collapsed"
className="expandCollapseIcon" className="expandCollapseIcon"
onKeyPress={[Function]} onKeyPress={[Function]}
role="button" role="button"
@@ -140,7 +140,7 @@ exports[`TreeNodeComponent renders a simple node (sorted children, expanded) 1`]
tabIndex={-1} tabIndex={-1}
> >
<img <img
alt="label branch is expanded" alt="Branch is expanded"
className="expandCollapseIcon" className="expandCollapseIcon"
onKeyPress={[Function]} onKeyPress={[Function]}
role="button" role="button"
@@ -278,7 +278,7 @@ exports[`TreeNodeComponent renders sorted children, expanded, leaves and parents
tabIndex={-1} tabIndex={-1}
> >
<img <img
alt="label branch is expanded" alt="Branch is expanded"
className="expandCollapseIcon" className="expandCollapseIcon"
onKeyPress={[Function]} onKeyPress={[Function]}
role="button" role="button"
@@ -436,7 +436,7 @@ exports[`TreeNodeComponent renders unsorted children by default 1`] = `
tabIndex={-1} tabIndex={-1}
> >
<img <img
alt="label branch is expanded" alt="Branch is expanded"
className="expandCollapseIcon" className="expandCollapseIcon"
onKeyPress={[Function]} onKeyPress={[Function]}
role="button" role="button"

View File

@@ -26,7 +26,7 @@ import NewVertexPane from "./Panes/NewVertexPane";
import NotebookV2Tab, { NotebookTabOptions } from "./Tabs/NotebookV2Tab"; import NotebookV2Tab, { NotebookTabOptions } from "./Tabs/NotebookV2Tab";
import Q from "q"; import Q from "q";
import ResourceTokenCollection from "./Tree/ResourceTokenCollection"; import ResourceTokenCollection from "./Tree/ResourceTokenCollection";
import * as TelemetryProcessor from "../Shared/Telemetry/TelemetryProcessor"; import TelemetryProcessor from "../Shared/Telemetry/TelemetryProcessor";
import TerminalTab from "./Tabs/TerminalTab"; import TerminalTab from "./Tabs/TerminalTab";
import { Action, ActionModifiers } from "../Shared/Telemetry/TelemetryConstants"; import { Action, ActionModifiers } from "../Shared/Telemetry/TelemetryConstants";
import { ActionContracts, MessageTypes } from "../Contracts/ExplorerContracts"; import { ActionContracts, MessageTypes } from "../Contracts/ExplorerContracts";
@@ -87,7 +87,6 @@ import { ContextualPaneBase } from "./Panes/ContextualPaneBase";
import TabsBase from "./Tabs/TabsBase"; import TabsBase from "./Tabs/TabsBase";
import { CommandButtonComponentProps } from "./Controls/CommandButton/CommandButtonComponent"; import { CommandButtonComponentProps } from "./Controls/CommandButton/CommandButtonComponent";
import { updateUserContext, userContext } from "../UserContext"; import { updateUserContext, userContext } from "../UserContext";
import { stringToBlob } from "../Utils/BlobUtils";
BindingHandlersRegisterer.registerBindingHandlers(); BindingHandlersRegisterer.registerBindingHandlers();
// Hold a reference to ComponentRegisterer to prevent transpiler to ignore import // Hold a reference to ComponentRegisterer to prevent transpiler to ignore import
@@ -1894,9 +1893,6 @@ export default class Explorer {
} }
public findSelectedDatabase(): ViewModels.Database { public findSelectedDatabase(): ViewModels.Database {
if (!this.selectedNode()) {
return null;
}
if (this.selectedNode().nodeKind === "Database") { if (this.selectedNode().nodeKind === "Database") {
return _.find(this.databases(), (database: ViewModels.Database) => database.rid === this.selectedNode().rid); return _.find(this.databases(), (database: ViewModels.Database) => database.rid === this.selectedNode().rid);
} }
@@ -2622,11 +2618,9 @@ export default class Explorer {
throw new Error(error); throw new Error(error);
} }
const clearMessage = NotificationConsoleUtils.logConsoleProgress(`Downloading ${notebookFile.path}`);
return this.notebookManager?.notebookContentClient.readFileContent(notebookFile.path).then( return this.notebookManager?.notebookContentClient.readFileContent(notebookFile.path).then(
(content: string) => { (content: string) => {
const blob = stringToBlob(content, "text/plain"); const blob = new Blob([content], { type: "octet/stream" });
if (navigator.msSaveBlob) { if (navigator.msSaveBlob) {
// for IE and Edge // for IE and Edge
navigator.msSaveBlob(blob, notebookFile.name); navigator.msSaveBlob(blob, notebookFile.name);
@@ -2643,16 +2637,12 @@ export default class Explorer {
downloadLink.click(); downloadLink.click();
downloadLink.remove(); downloadLink.remove();
} }
clearMessage();
}, },
(error: any) => { (error: any) => {
NotificationConsoleUtils.logConsoleMessage( NotificationConsoleUtils.logConsoleMessage(
ConsoleDataType.Error, ConsoleDataType.Error,
`Could not download notebook ${JSON.stringify(error)}` `Could not download notebook ${JSON.stringify(error)}`
); );
clearMessage();
} }
); );
} }
@@ -3127,6 +3117,12 @@ export default class Explorer {
} else { } else {
loadingTitle.innerHTML = title; loadingTitle.innerHTML = title;
} }
TelemetryProcessor.trace(
Action.LoadingStatus,
ActionModifiers.Mark,
title !== "Welcome to Azure Cosmos DB" ? `Title: ${title}, Text: ${text}` : text
);
} }
private _openSetupNotebooksPaneForQuickstart(): void { private _openSetupNotebooksPaneForQuickstart(): void {

View File

@@ -23,7 +23,7 @@ import { GraphConfig } from "../../Tabs/GraphTab";
import { EditorReact } from "../../Controls/Editor/EditorReact"; import { EditorReact } from "../../Controls/Editor/EditorReact";
import LoadGraphIcon from "../../../../images/LoadGraph.png"; import LoadGraphIcon from "../../../../images/LoadGraph.png";
import { Action } from "../../../Shared/Telemetry/TelemetryConstants"; import { Action } from "../../../Shared/Telemetry/TelemetryConstants";
import * as TelemetryProcessor from "../../../Shared/Telemetry/TelemetryProcessor"; import TelemetryProcessor from "../../../Shared/Telemetry/TelemetryProcessor";
import * as Constants from "../../../Common/Constants"; import * as Constants from "../../../Common/Constants";
import { InputProperty } from "../../../Contracts/ViewModels"; import { InputProperty } from "../../../Contracts/ViewModels";
import { QueryIterator, ItemDefinition, Resource } from "@azure/cosmos"; import { QueryIterator, ItemDefinition, Resource } from "@azure/cosmos";

View File

@@ -2,7 +2,7 @@ import * as ViewModels from "../../../Contracts/ViewModels";
import { PlatformType } from "../../../PlatformType"; import { PlatformType } from "../../../PlatformType";
import { Action, ActionModifiers } from "../../../Shared/Telemetry/TelemetryConstants"; import { Action, ActionModifiers } from "../../../Shared/Telemetry/TelemetryConstants";
import { Areas } from "../../../Common/Constants"; import { Areas } from "../../../Common/Constants";
import * as TelemetryProcessor from "../../../Shared/Telemetry/TelemetryProcessor"; import TelemetryProcessor from "../../../Shared/Telemetry/TelemetryProcessor";
import AddDatabaseIcon from "../../../../images/AddDatabase.svg"; import AddDatabaseIcon from "../../../../images/AddDatabase.svg";
import AddCollectionIcon from "../../../../images/AddCollection.svg"; import AddCollectionIcon from "../../../../images/AddCollection.svg";

View File

@@ -130,14 +130,11 @@ export class NotificationConsoleComponent extends React.Component<
<span className="headerStatusEllipsis">{this.state.headerStatus}</span> <span className="headerStatusEllipsis">{this.state.headerStatus}</span>
</span> </span>
</div> </div>
<div <div className="expandCollapseButton" role="button" tabIndex={0}>
className="expandCollapseButton" <img
role="button" src={this.state.isExpanded ? ChevronDownIcon : ChevronUpIcon}
tabIndex={0} alt={this.state.isExpanded ? "collapse console" : "expand console"}
aria-label={this.state.isExpanded ? "collapse console" : "expand console"} />
aria-expanded={this.state.isExpanded}
>
<img src={this.state.isExpanded ? ChevronDownIcon : ChevronUpIcon} alt="" />
</div> </div>
</div> </div>
<AnimateHeight <AnimateHeight

View File

@@ -68,14 +68,12 @@ exports[`NotificationConsoleComponent renders the console (expanded) 1`] = `
</span> </span>
</div> </div>
<div <div
aria-expanded={true}
aria-label="collapse console"
className="expandCollapseButton" className="expandCollapseButton"
role="button" role="button"
tabIndex={0} tabIndex={0}
> >
<img <img
alt="" alt="collapse console"
src="" src=""
/> />
</div> </div>

View File

@@ -32,7 +32,7 @@ import { Store, AnyAction, MiddlewareAPI, Middleware, Dispatch } from "redux";
import configureStore from "./NotebookComponent/store"; import configureStore from "./NotebookComponent/store";
import { Notification } from "react-notification-system"; import { Notification } from "react-notification-system";
import * as TelemetryProcessor from "../../Shared/Telemetry/TelemetryProcessor"; import TelemetryProcessor from "../../Shared/Telemetry/TelemetryProcessor";
import { Action } from "../../Shared/Telemetry/TelemetryConstants"; import { Action } from "../../Shared/Telemetry/TelemetryConstants";
export type KernelSpecsDisplay = { name: string; displayName: string }; export type KernelSpecsDisplay = { name: string; displayName: string };

View File

@@ -37,7 +37,7 @@ import * as Constants from "../../../Common/Constants";
import * as NotificationConsoleUtils from "../../../Utils/NotificationConsoleUtils"; import * as NotificationConsoleUtils from "../../../Utils/NotificationConsoleUtils";
import { ConsoleDataType } from "../../Menus/NotificationConsole/NotificationConsoleComponent"; import { ConsoleDataType } from "../../Menus/NotificationConsole/NotificationConsoleComponent";
import * as CdbActions from "./actions"; import * as CdbActions from "./actions";
import * as TelemetryProcessor from "../../../Shared/Telemetry/TelemetryProcessor"; import TelemetryProcessor from "../../../Shared/Telemetry/TelemetryProcessor";
import { Action as TelemetryAction } from "../../../Shared/Telemetry/TelemetryConstants"; import { Action as TelemetryAction } from "../../../Shared/Telemetry/TelemetryConstants";
import { CdbAppState } from "./types"; import { CdbAppState } from "./types";
import { decryptJWTToken } from "../../../Utils/AuthorizationUtils"; import { decryptJWTToken } from "../../../Utils/AuthorizationUtils";

View File

@@ -1,7 +1,7 @@
import { actions, CoreRecord, reducers as nteractReducers } from "@nteract/core"; import { actions, CoreRecord, reducers as nteractReducers } from "@nteract/core";
import { Action } from "redux"; import { Action } from "redux";
import { Areas } from "../../../Common/Constants"; import { Areas } from "../../../Common/Constants";
import * as TelemetryProcessor from "../../../Shared/Telemetry/TelemetryProcessor"; import TelemetryProcessor from "../../../Shared/Telemetry/TelemetryProcessor";
import * as cdbActions from "./actions"; import * as cdbActions from "./actions";
import { CdbRecord } from "./types"; import { CdbRecord } from "./types";

View File

@@ -194,24 +194,17 @@ export class NotebookContentClient {
}); });
} }
public async readFileContent(filePath: string): Promise<string> { public readFileContent(filePath: string): Promise<string> {
const xhr = await this.contentProvider.get(this.getServerConfig(), filePath, { content: 1 }).toPromise(); return this.contentProvider
const content = (xhr.response as any).content; .get(this.getServerConfig(), filePath, { type: "notebook", format: "text", content: 1 })
if (!content) { .toPromise()
throw new Error("No content read"); .then(xhr => {
} const content = (xhr.response as any).content;
if (!content) {
const format = (xhr.response as any).format; throw new Error("No content read");
switch (format) { }
case "text":
return content;
case "base64":
return atob(content);
case "json":
return stringifyNotebook(content); return stringifyNotebook(content);
default: });
throw new Error(`Unsupported content format ${format}`);
}
} }
private deleteNotebookFile(path: string): Promise<string> { private deleteNotebookFile(path: string): Promise<string> {

View File

@@ -9,7 +9,7 @@ import * as Logger from "../../Common/Logger";
import { HttpStatusCodes, Areas } from "../../Common/Constants"; import { HttpStatusCodes, Areas } from "../../Common/Constants";
import { GitHubReposPane } from "../Panes/GitHubReposPane"; import { GitHubReposPane } from "../Panes/GitHubReposPane";
import ko from "knockout"; import ko from "knockout";
import * as TelemetryProcessor from "../../Shared/Telemetry/TelemetryProcessor"; import TelemetryProcessor from "../../Shared/Telemetry/TelemetryProcessor";
import { Action, ActionModifiers } from "../../Shared/Telemetry/TelemetryConstants"; import { Action, ActionModifiers } from "../../Shared/Telemetry/TelemetryConstants";
import { IContentProvider } from "@nteract/core"; import { IContentProvider } from "@nteract/core";
import { NotebookContentProvider } from "./NotebookComponent/NotebookContentProvider"; import { NotebookContentProvider } from "./NotebookComponent/NotebookContentProvider";

View File

@@ -24,8 +24,8 @@
<script type="text/html" id="add-collection-inputs"> <script type="text/html" id="add-collection-inputs">
<!-- Add collection header - Start --> <!-- Add collection header - Start -->
<div class="firstdivbg headerline"> <div class="firstdivbg headerline">
<span id="containerTitle" role="heading" aria-level="2" data-bind="text: title" ></span> <span id="containerTitle" data-bind="text: title"></span>
<div class="closeImg" id="closeBtnAddCollection" role="button" aria-label="Add collection close pane" <div class="closeImg" id="closeBtnAddCollection" role="button" aria-label="Close pane"
data-bind="click: cancel, event: { keypress: onCloseKeyPress }" tabindex="0"> data-bind="click: cancel, event: { keypress: onCloseKeyPress }" tabindex="0">
<img src="../../../images/close-black.svg" title="Close" alt="Close" /> <img src="../../../images/close-black.svg" title="Close" alt="Close" />
</div> </div>
@@ -115,10 +115,10 @@
<!-- Database provisioned throughput - Start --> <!-- Database provisioned throughput - Start -->
<!-- ko if: canConfigureThroughput --> <!-- ko if: canConfigureThroughput -->
<div class="databaseProvision" aria-label="Provision database throughput" <div class="databaseProvision" aria-label="New database provision support"
data-bind="visible: databaseCreateNew"> data-bind="visible: databaseCreateNew">
<input tabindex="0" type="checkbox" data-test="addCollectionPane-databaseSharedThroughput" <input tabindex="0" type="checkbox" data-test="addCollectionPane-databaseSharedThroughput"
id="addCollection-databaseSharedThroughput" title="Provision database throughput" id="addCollection-databaseSharedThroughput" title="Provision shared throughput"
data-bind="checked: databaseCreateNewShared" /> data-bind="checked: databaseCreateNewShared" />
<span class="databaseProvisionText" for="databaseSharedThroughput">Provision database throughput</span> <span class="databaseProvisionText" for="databaseSharedThroughput">Provision database throughput</span>
<span class="infoTooltip" role="tooltip" tabindex="0"> <span class="infoTooltip" role="tooltip" tabindex="0">
@@ -537,11 +537,9 @@
attr: { attr: {
'aria-checked': isAnalyticalStorageOn() ? 'true' : 'false' 'aria-checked': isAnalyticalStorageOn() ? 'true' : 'false'
}" /> }" />
<label for="enableAnalyticalStorageRadioOn" class="enableAnalyticalStorageRadioLabel"> <span for="enableAnalyticalStorageRadioOn" data-bind="disable: showEnableSynapseLink">
<span data-bind="disable: showEnableSynapseLink"> On
On </span>
</span>
</label>
<input class="enableAnalyticalStorageRadio" id="enableAnalyticalStorageRadioOff" <input class="enableAnalyticalStorageRadio" id="enableAnalyticalStorageRadioOff"
name="analyticalStore" type="radio" role="radio" tabindex="0" data-bind=" name="analyticalStore" type="radio" role="radio" tabindex="0" data-bind="
@@ -551,11 +549,9 @@
attr: { attr: {
'aria-checked': isAnalyticalStorageOn() ? 'false' : 'true' 'aria-checked': isAnalyticalStorageOn() ? 'false' : 'true'
}" /> }" />
<label for="enableAnalyticalStorageRadioOff" class="enableAnalyticalStorageRadioLabel"> <span for="enableAnalyticalStorageRadioOff" data-bind="disable: showEnableSynapseLink">
<span data-bind="disable: showEnableSynapseLink"> Off
Off </span>
</span>
</label>
</div> </div>
<div class="paragraph italic" data-bind="visible: ttl90DaysEnabled() && isAnalyticalStorageOn()"> <div class="paragraph italic" data-bind="visible: ttl90DaysEnabled() && isAnalyticalStorageOn()">

View File

@@ -9,7 +9,7 @@ import * as PricingUtils from "../../Utils/PricingUtils";
import * as SharedConstants from "../../Shared/Constants"; import * as SharedConstants from "../../Shared/Constants";
import * as ViewModels from "../../Contracts/ViewModels"; import * as ViewModels from "../../Contracts/ViewModels";
import editable from "../../Common/EditableUtility"; import editable from "../../Common/EditableUtility";
import * as TelemetryProcessor from "../../Shared/Telemetry/TelemetryProcessor"; import TelemetryProcessor from "../../Shared/Telemetry/TelemetryProcessor";
import { Action, ActionModifiers } from "../../Shared/Telemetry/TelemetryConstants"; import { Action, ActionModifiers } from "../../Shared/Telemetry/TelemetryConstants";
import { configContext, Platform } from "../../ConfigContext"; import { configContext, Platform } from "../../ConfigContext";
import { ContextualPaneBase } from "./ContextualPaneBase"; import { ContextualPaneBase } from "./ContextualPaneBase";
@@ -943,7 +943,7 @@ export default class AddCollectionPane extends ContextualPaneBase {
const defaultThroughput = this.container.collectionCreationDefaults.throughput; const defaultThroughput = this.container.collectionCreationDefaults.throughput;
this.throughputSinglePartition(defaultThroughput.fixed); this.throughputSinglePartition(defaultThroughput.fixed);
this.throughputMultiPartition( this.throughputMultiPartition(
AddCollectionUtility.getMaxThroughput(this.container.collectionCreationDefaults, this.container) AddCollectionUtility.Utilities.getMaxThroughput(this.container.collectionCreationDefaults, this.container)
); );
this.throughputDatabase(defaultThroughput.shared); this.throughputDatabase(defaultThroughput.shared);
@@ -1167,19 +1167,17 @@ export default class AddCollectionPane extends ContextualPaneBase {
private _updateThroughputLimitByCollectionStorage() { private _updateThroughputLimitByCollectionStorage() {
const storage = this.storage(); const storage = this.storage();
const minThroughputRU = const minThroughputRU = AddCollectionUtility.Utilities.getMinRUForStorageOption(
storage === SharedConstants.CollectionCreation.storage10Gb this.container.collectionCreationDefaults,
? SharedConstants.CollectionCreation.DefaultCollectionRUs400 storage
: this.container.collectionCreationDefaults.throughput.unlimitedmin; );
let maxThroughputRU; let maxThroughputRU = AddCollectionUtility.Utilities.getMaxRUForStorageOption(
this.container.collectionCreationDefaults,
storage
);
if (this.isTryCosmosDBSubscription()) { if (this.isTryCosmosDBSubscription()) {
maxThroughputRU = Constants.TryCosmosExperience.maxRU; maxThroughputRU = Constants.TryCosmosExperience.maxRU;
} else {
maxThroughputRU =
storage === SharedConstants.CollectionCreation.storage10Gb
? SharedConstants.CollectionCreation.DefaultCollectionRUs10K
: this.container.collectionCreationDefaults.throughput.unlimitedmax;
} }
this.minThroughputRU(minThroughputRU); this.minThroughputRU(minThroughputRU);

View File

@@ -24,7 +24,7 @@
<script type="text/html" id="add-database-inputs"> <script type="text/html" id="add-database-inputs">
<!-- Add database header - Start --> <!-- Add database header - Start -->
<div class="firstdivbg headerline"> <div class="firstdivbg headerline">
<span id="databaseTitle" role="heading" aria-level="2" data-bind="text: title"></span> <span id="databaseTitle" data-bind="text: title"></span>
<div class="closeImg" role="button" aria-label="Close pane" <div class="closeImg" role="button" aria-label="Close pane"
data-bind="click: cancel, event: { keypress: onCloseKeyPress }" tabindex="0"> data-bind="click: cancel, event: { keypress: onCloseKeyPress }" tabindex="0">
<img src="../../../images/close-black.svg" title="Close" alt="Close" /> <img src="../../../images/close-black.svg" title="Close" alt="Close" />
@@ -73,8 +73,8 @@
</p> </p>
<input id="database-id" type="text" aria-required="true" autocomplete="off" pattern="[^/?#\\]*[^/?# \\]" <input id="database-id" type="text" aria-required="true" autocomplete="off" pattern="[^/?#\\]*[^/?# \\]"
title="May not end with space nor contain characters '\' '/' '#' '?'" title="May not end with space nor contain characters '\' '/' '#' '?'" placeholder="Type a new database id"
size="40" class="collid" data-bind="textInput: databaseId, hasFocus: firstFieldHasFocus, attr: { placeholder: databaseIdPlaceHolder }" size="40" class="collid" data-bind="textInput: databaseId, hasFocus: firstFieldHasFocus"
aria-label="Database id" autofocus> aria-label="Database id" autofocus>
<!-- Database provisioned throughput - Start --> <!-- Database provisioned throughput - Start -->

View File

@@ -1,3 +1,4 @@
import * as AddCollectionUtility from "../../Shared/AddCollectionUtility";
import * as AutoPilotUtils from "../../Utils/AutoPilotUtils"; import * as AutoPilotUtils from "../../Utils/AutoPilotUtils";
import * as Constants from "../../Common/Constants"; import * as Constants from "../../Common/Constants";
import * as DataModels from "../../Contracts/DataModels"; import * as DataModels from "../../Contracts/DataModels";
@@ -7,16 +8,19 @@ import * as PricingUtils from "../../Utils/PricingUtils";
import * as SharedConstants from "../../Shared/Constants"; import * as SharedConstants from "../../Shared/Constants";
import * as ViewModels from "../../Contracts/ViewModels"; import * as ViewModels from "../../Contracts/ViewModels";
import editable from "../../Common/EditableUtility"; import editable from "../../Common/EditableUtility";
import * as TelemetryProcessor from "../../Shared/Telemetry/TelemetryProcessor"; import EnvironmentUtility from "../../Common/EnvironmentUtility";
import TelemetryProcessor from "../../Shared/Telemetry/TelemetryProcessor";
import { Action, ActionModifiers } from "../../Shared/Telemetry/TelemetryConstants"; import { Action, ActionModifiers } from "../../Shared/Telemetry/TelemetryConstants";
import { AddDbUtilities } from "../../Shared/AddDatabaseUtility";
import { CassandraAPIDataClient } from "../Tables/TableDataClient";
import { ContextualPaneBase } from "./ContextualPaneBase"; import { ContextualPaneBase } from "./ContextualPaneBase";
import { createDatabase } from "../../Common/dataAccess/createDatabase"; import { createDatabase } from "../../Common/dataAccess/createDatabase";
import { PlatformType } from "../../PlatformType"; import { PlatformType } from "../../PlatformType";
import { userContext } from "../../UserContext";
export default class AddDatabasePane extends ContextualPaneBase { export default class AddDatabasePane extends ContextualPaneBase {
public defaultExperience: ko.Computed<string>; public defaultExperience: ko.Computed<string>;
public databaseIdLabel: ko.Computed<string>; public databaseIdLabel: ko.Computed<string>;
public databaseIdPlaceHolder: ko.Computed<string>;
public databaseId: ko.Observable<string>; public databaseId: ko.Observable<string>;
public databaseIdTooltipText: ko.Computed<string>; public databaseIdTooltipText: ko.Computed<string>;
public databaseLevelThroughputTooltipText: ko.Computed<string>; public databaseLevelThroughputTooltipText: ko.Computed<string>;
@@ -71,11 +75,6 @@ export default class AddDatabasePane extends ContextualPaneBase {
this.databaseIdLabel = ko.computed<string>(() => this.databaseIdLabel = ko.computed<string>(() =>
this.container.isPreferredApiCassandra() ? "Keyspace id" : "Database id" this.container.isPreferredApiCassandra() ? "Keyspace id" : "Database id"
); );
this.databaseIdPlaceHolder = ko.computed<string>(() =>
this.container.isPreferredApiCassandra() ? "Type a new keyspace id" : "Type a new database id"
);
this.databaseIdTooltipText = ko.computed<string>(() => { this.databaseIdTooltipText = ko.computed<string>(() => {
const isCassandraAccount: boolean = this.container.isPreferredApiCassandra(); const isCassandraAccount: boolean = this.container.isPreferredApiCassandra();
return `A ${isCassandraAccount ? "keyspace" : "database"} is a logical container of one or more ${ return `A ${isCassandraAccount ? "keyspace" : "database"} is a logical container of one or more ${

View File

@@ -6,7 +6,7 @@
<div class="paneContentContainer"> <div class="paneContentContainer">
<!-- Save Query header - Start --> <!-- Save Query header - Start -->
<div class="firstdivbg headerline"> <div class="firstdivbg headerline">
<span role="heading" aria-level="2" data-bind="text: title"></span> <span data-bind="text: title"></span>
<div <div
class="closeImg" class="closeImg"
role="button" role="button"

View File

@@ -5,7 +5,7 @@ import { Areas } from "../../Common/Constants";
import { ContextualPaneBase } from "./ContextualPaneBase"; import { ContextualPaneBase } from "./ContextualPaneBase";
import * as Logger from "../../Common/Logger"; import * as Logger from "../../Common/Logger";
import { QueriesGridComponentAdapter } from "../Controls/QueriesGridReactComponent/QueriesGridComponentAdapter"; import { QueriesGridComponentAdapter } from "../Controls/QueriesGridReactComponent/QueriesGridComponentAdapter";
import * as TelemetryProcessor from "../../Shared/Telemetry/TelemetryProcessor"; import TelemetryProcessor from "../../Shared/Telemetry/TelemetryProcessor";
import QueryTab from "../Tabs/QueryTab"; import QueryTab from "../Tabs/QueryTab";
export class BrowseQueriesPane extends ContextualPaneBase { export class BrowseQueriesPane extends ContextualPaneBase {

View File

@@ -17,7 +17,7 @@
> >
<!-- Add Cassandra collection header - Start --> <!-- Add Cassandra collection header - Start -->
<div class="firstdivbg headerline"> <div class="firstdivbg headerline">
<span role="heading" aria-level="2" data-bind="text: title"></span> <span data-bind="text: title"></span>
<div <div
class="closeImg" class="closeImg"
role="button" role="button"

View File

@@ -7,7 +7,7 @@ import * as ko from "knockout";
import * as PricingUtils from "../../Utils/PricingUtils"; import * as PricingUtils from "../../Utils/PricingUtils";
import * as SharedConstants from "../../Shared/Constants"; import * as SharedConstants from "../../Shared/Constants";
import * as ViewModels from "../../Contracts/ViewModels"; import * as ViewModels from "../../Contracts/ViewModels";
import * as TelemetryProcessor from "../../Shared/Telemetry/TelemetryProcessor"; import TelemetryProcessor from "../../Shared/Telemetry/TelemetryProcessor";
import { Action, ActionModifiers } from "../../Shared/Telemetry/TelemetryConstants"; import { Action, ActionModifiers } from "../../Shared/Telemetry/TelemetryConstants";
import { CassandraAPIDataClient } from "../Tables/TableDataClient"; import { CassandraAPIDataClient } from "../Tables/TableDataClient";
import { ContextualPaneBase } from "./ContextualPaneBase"; import { ContextualPaneBase } from "./ContextualPaneBase";
@@ -494,7 +494,9 @@ export default class CassandraAddCollectionPane extends ContextualPaneBase {
this.selectedSharedAutoPilotTier(null); this.selectedSharedAutoPilotTier(null);
this.selectedAutoPilotThroughput(AutoPilotUtils.minAutoPilotThroughput); this.selectedAutoPilotThroughput(AutoPilotUtils.minAutoPilotThroughput);
this.sharedAutoPilotThroughput(AutoPilotUtils.minAutoPilotThroughput); this.sharedAutoPilotThroughput(AutoPilotUtils.minAutoPilotThroughput);
this.throughput(AddCollectionUtility.getMaxThroughput(this.container.collectionCreationDefaults, this.container)); this.throughput(
AddCollectionUtility.Utilities.getMaxThroughput(this.container.collectionCreationDefaults, this.container)
);
this.keyspaceThroughput(throughputDefaults.shared); this.keyspaceThroughput(throughputDefaults.shared);
this.maxThroughputRU(throughputDefaults.unlimitedmax); this.maxThroughputRU(throughputDefaults.unlimitedmax);
this.minThroughputRU(throughputDefaults.unlimitedmin); this.minThroughputRU(throughputDefaults.unlimitedmin);

View File

@@ -4,12 +4,11 @@ import * as Constants from "../../Common/Constants";
import { Action, ActionModifiers } from "../../Shared/Telemetry/TelemetryConstants"; import { Action, ActionModifiers } from "../../Shared/Telemetry/TelemetryConstants";
import { KeyCodes } from "../../Common/Constants"; import { KeyCodes } from "../../Common/Constants";
import { WaitsForTemplateViewModel } from "../WaitsForTemplateViewModel"; import { WaitsForTemplateViewModel } from "../WaitsForTemplateViewModel";
import * as TelemetryProcessor from "../../Shared/Telemetry/TelemetryProcessor"; import TelemetryProcessor from "../../Shared/Telemetry/TelemetryProcessor";
import Explorer from "../Explorer"; import Explorer from "../Explorer";
// TODO: Use specific actions for logging telemetry data // TODO: Use specific actions for logging telemetry data
export abstract class ContextualPaneBase extends WaitsForTemplateViewModel { export abstract class ContextualPaneBase extends WaitsForTemplateViewModel {
private initalFocusedElement: HTMLElement | undefined;
public id: string; public id: string;
public container: Explorer; public container: Explorer;
public firstFieldHasFocus: ko.Observable<boolean>; public firstFieldHasFocus: ko.Observable<boolean>;
@@ -50,11 +49,9 @@ export abstract class ContextualPaneBase extends WaitsForTemplateViewModel {
this.visible(false); this.visible(false);
this.isExecuting(false); this.isExecuting(false);
this.resetData(); this.resetData();
this.resetFocus();
} }
public open() { public open() {
this.initalFocusedElement = document.activeElement as HTMLElement;
this.visible(true); this.visible(true);
this.firstFieldHasFocus(true); this.firstFieldHasFocus(true);
this.resizePane(); this.resizePane();
@@ -126,11 +123,4 @@ export abstract class ContextualPaneBase extends WaitsForTemplateViewModel {
$(paneElement).height(newPaneElementHeight); $(paneElement).height(newPaneElementHeight);
} }
private resetFocus(): void {
if (this.initalFocusedElement) {
this.initalFocusedElement.focus();
this.initalFocusedElement = undefined;
}
}
} }

View File

@@ -15,7 +15,7 @@
> >
<!-- Delete Collection Confirmation header - Start --> <!-- Delete Collection Confirmation header - Start -->
<div class="firstdivbg headerline"> <div class="firstdivbg headerline">
<span role="heading" aria-level="2" data-bind="text: title"></span> <span data-bind="text: title"></span>
<div <div
class="closeImg" class="closeImg"
role="button" role="button"
@@ -67,7 +67,8 @@
name="collectionIdConfirmation" name="collectionIdConfirmation"
required required
class="collid" class="collid"
data-bind="value: collectionIdConfirmation, hasFocus: firstFieldHasFocus, attr: { 'aria-label': collectionIdConfirmationText }" data-bind="value: collectionIdConfirmation, hasFocus: firstFieldHasFocus"
aria-label="Confirm by typing the collection id"
/> />
</p> </p>
</div> </div>

View File

@@ -8,7 +8,7 @@ import { Action, ActionModifiers } from "../../Shared/Telemetry/TelemetryConstan
import DeleteCollectionConfirmationPane from "./DeleteCollectionConfirmationPane"; import DeleteCollectionConfirmationPane from "./DeleteCollectionConfirmationPane";
import DeleteFeedback from "../../Common/DeleteFeedback"; import DeleteFeedback from "../../Common/DeleteFeedback";
import Explorer from "../Explorer"; import Explorer from "../Explorer";
import * as TelemetryProcessor from "../../Shared/Telemetry/TelemetryProcessor"; import TelemetryProcessor from "../../Shared/Telemetry/TelemetryProcessor";
import { TreeNode } from "../../Contracts/ViewModels"; import { TreeNode } from "../../Contracts/ViewModels";
import { deleteCollection } from "../../Common/dataAccess/deleteCollection"; import { deleteCollection } from "../../Common/dataAccess/deleteCollection";

View File

@@ -10,7 +10,7 @@ import { ContextualPaneBase } from "./ContextualPaneBase";
import { DefaultExperienceUtility } from "../../Shared/DefaultExperienceUtility"; import { DefaultExperienceUtility } from "../../Shared/DefaultExperienceUtility";
import DeleteFeedback from "../../Common/DeleteFeedback"; import DeleteFeedback from "../../Common/DeleteFeedback";
import * as NotificationConsoleUtils from "../../Utils/NotificationConsoleUtils"; import * as NotificationConsoleUtils from "../../Utils/NotificationConsoleUtils";
import * as TelemetryProcessor from "../../Shared/Telemetry/TelemetryProcessor"; import TelemetryProcessor from "../../Shared/Telemetry/TelemetryProcessor";
import { deleteCollection } from "../../Common/dataAccess/deleteCollection"; import { deleteCollection } from "../../Common/dataAccess/deleteCollection";
export default class DeleteCollectionConfirmationPane extends ContextualPaneBase { export default class DeleteCollectionConfirmationPane extends ContextualPaneBase {

View File

@@ -15,7 +15,7 @@
> >
<!-- Delete Database Confirmation header - Start --> <!-- Delete Database Confirmation header - Start -->
<div class="firstdivbg headerline"> <div class="firstdivbg headerline">
<span role="heading" aria-level="2" data-bind="text: title"></span> <span data-bind="text: title"></span>
<div <div
class="closeImg" class="closeImg"
role="button" role="button"

View File

@@ -8,7 +8,7 @@ import * as ViewModels from "../../Contracts/ViewModels";
import DeleteDatabaseConfirmationPane from "./DeleteDatabaseConfirmationPane"; import DeleteDatabaseConfirmationPane from "./DeleteDatabaseConfirmationPane";
import DeleteFeedback from "../../Common/DeleteFeedback"; import DeleteFeedback from "../../Common/DeleteFeedback";
import Explorer from "../Explorer"; import Explorer from "../Explorer";
import * as TelemetryProcessor from "../../Shared/Telemetry/TelemetryProcessor"; import TelemetryProcessor from "../../Shared/Telemetry/TelemetryProcessor";
import { TreeNode } from "../../Contracts/ViewModels"; import { TreeNode } from "../../Contracts/ViewModels";
import { TabsManager } from "../Tabs/TabsManager"; import { TabsManager } from "../Tabs/TabsManager";
import { deleteDatabase } from "../../Common/dataAccess/deleteDatabase"; import { deleteDatabase } from "../../Common/dataAccess/deleteDatabase";

View File

@@ -11,9 +11,8 @@ import { DefaultExperienceUtility } from "../../Shared/DefaultExperienceUtility"
import DeleteFeedback from "../../Common/DeleteFeedback"; import DeleteFeedback from "../../Common/DeleteFeedback";
import * as NotificationConsoleUtils from "../../Utils/NotificationConsoleUtils"; import * as NotificationConsoleUtils from "../../Utils/NotificationConsoleUtils";
import * as TelemetryProcessor from "../../Shared/Telemetry/TelemetryProcessor"; import TelemetryProcessor from "../../Shared/Telemetry/TelemetryProcessor";
import { deleteDatabase } from "../../Common/dataAccess/deleteDatabase"; import { deleteDatabase } from "../../Common/dataAccess/deleteDatabase";
import { ARMError } from "../../Utils/arm/request";
export default class DeleteDatabaseConfirmationPane extends ContextualPaneBase { export default class DeleteDatabaseConfirmationPane extends ContextualPaneBase {
public databaseIdConfirmationText: ko.Observable<string>; public databaseIdConfirmationText: ko.Observable<string>;
@@ -106,12 +105,11 @@ export default class DeleteDatabaseConfirmationPane extends ContextualPaneBase {
this.databaseDeleteFeedback(""); this.databaseDeleteFeedback("");
} }
}, },
(reason: unknown) => { (reason: any) => {
this.isExecuting(false); this.isExecuting(false);
const message = ErrorParserUtility.parse(reason);
const message = reason instanceof ARMError ? reason.message : ErrorParserUtility.parse(reason)[0].message; this.formErrors(message[0].message);
this.formErrors(message); this.formErrorsDetails(message[0].message);
this.formErrorsDetails(message);
TelemetryProcessor.traceFailure( TelemetryProcessor.traceFailure(
Action.DeleteDatabase, Action.DeleteDatabase,
{ {

View File

@@ -11,7 +11,7 @@
<form class="paneContentContainer" data-bind="submit: execute"> <form class="paneContentContainer" data-bind="submit: execute">
<!-- Input params header - Start --> <!-- Input params header - Start -->
<div class="firstdivbg headerline"> <div class="firstdivbg headerline">
<span role="heading" aria-level="2" data-bind="text: title"></span> <span data-bind="text: title"></span>
<div <div
class="closeImg" class="closeImg"
role="button" role="button"

View File

@@ -70,9 +70,7 @@ export class GenericRightPaneComponent extends React.Component<GenericRightPaneP
private renderPanelHeader = (): JSX.Element => { private renderPanelHeader = (): JSX.Element => {
return ( return (
<div className="firstdivbg headerline"> <div className="firstdivbg headerline">
<span id="databaseTitle" role="heading" aria-level={2}> <span id="databaseTitle">{this.props.title}</span>
{this.props.title}
</span>
<IconButton <IconButton
ariaLabel="Close pane" ariaLabel="Close pane"
title="Close pane" title="Close pane"

View File

@@ -5,7 +5,7 @@ import * as ViewModels from "../../Contracts/ViewModels";
import { GitHubClient, IGitHubPageInfo, IGitHubRepo } from "../../GitHub/GitHubClient"; import { GitHubClient, IGitHubPageInfo, IGitHubRepo } from "../../GitHub/GitHubClient";
import { IPinnedRepo, JunoClient } from "../../Juno/JunoClient"; import { IPinnedRepo, JunoClient } from "../../Juno/JunoClient";
import { Action, ActionModifiers } from "../../Shared/Telemetry/TelemetryConstants"; import { Action, ActionModifiers } from "../../Shared/Telemetry/TelemetryConstants";
import * as TelemetryProcessor from "../../Shared/Telemetry/TelemetryProcessor"; import TelemetryProcessor from "../../Shared/Telemetry/TelemetryProcessor";
import * as GitHubUtils from "../../Utils/GitHubUtils"; import * as GitHubUtils from "../../Utils/GitHubUtils";
import { JunoUtils } from "../../Utils/JunoUtils"; import { JunoUtils } from "../../Utils/JunoUtils";
import * as NotificationConsoleUtils from "../../Utils/NotificationConsoleUtils"; import * as NotificationConsoleUtils from "../../Utils/NotificationConsoleUtils";

View File

@@ -6,7 +6,7 @@
<form class="paneContentContainer" data-bind="submit: submit"> <form class="paneContentContainer" data-bind="submit: submit">
<!-- New Vertex header - Start --> <!-- New Vertex header - Start -->
<div class="firstdivbg headerline"> <div class="firstdivbg headerline">
<span role="heading" aria-level="2">New Vertex</span> <span>New Vertex</span>
<div <div
class="closeImg" class="closeImg"
role="button" role="button"

View File

@@ -6,7 +6,7 @@
<form class="paneContentContainer" data-bind="submit: submit"> <form class="paneContentContainer" data-bind="submit: submit">
<!-- Graph Styling header - Start --> <!-- Graph Styling header - Start -->
<div class="firstdivbg headerline"> <div class="firstdivbg headerline">
<span role="heading" aria-level="2">Graph Styling</span> <span>Graph Styling</span>
<div <div
class="closeImg" class="closeImg"
role="button" role="button"

View File

@@ -6,7 +6,7 @@
<form class="paneContentContainer" data-bind="submit: submit"> <form class="paneContentContainer" data-bind="submit: submit">
<!-- Load Query header - Start --> <!-- Load Query header - Start -->
<div class="firstdivbg headerline"> <div class="firstdivbg headerline">
<span role="heading" aria-level="2" data-bind="text: title"></span> <span data-bind="text: title"></span>
<div <div
class="closeImg" class="closeImg"
role="button" role="button"

View File

@@ -11,7 +11,7 @@
<form class="paneContentContainer" data-bind="submit: submit"> <form class="paneContentContainer" data-bind="submit: submit">
<!-- Renew ad-hoc access header - Start --> <!-- Renew ad-hoc access header - Start -->
<div class="firstdivbg headerline"> <div class="firstdivbg headerline">
<span role="heading" aria-level="2" data-bind="text: title"></span> <span data-bind="text: title"></span>
<div <div
class="closeImg" class="closeImg"
role="button" role="button"

View File

@@ -6,7 +6,7 @@
<form class="paneContentContainer" data-bind="submit: submit"> <form class="paneContentContainer" data-bind="submit: submit">
<!-- Save Query header - Start --> <!-- Save Query header - Start -->
<div class="firstdivbg headerline"> <div class="firstdivbg headerline">
<span role="heading" aria-level="2" data-bind="text: title"></span> <span data-bind="text: title"></span>
<div class="closeImg" role="button" aria-label="Close pane" tabindex="0" data-bind="click: cancel"> <div class="closeImg" role="button" aria-label="Close pane" tabindex="0" data-bind="click: cancel">
<img src="../../../images/close-black.svg" title="Close" alt="Close" /> <img src="../../../images/close-black.svg" title="Close" alt="Close" />
</div> </div>

View File

@@ -6,7 +6,7 @@ import { Action } from "../../Shared/Telemetry/TelemetryConstants";
import { ContextualPaneBase } from "./ContextualPaneBase"; import { ContextualPaneBase } from "./ContextualPaneBase";
import { ConsoleDataType } from "../Menus/NotificationConsole/NotificationConsoleComponent"; import { ConsoleDataType } from "../Menus/NotificationConsole/NotificationConsoleComponent";
import * as NotificationConsoleUtils from "../../Utils/NotificationConsoleUtils"; import * as NotificationConsoleUtils from "../../Utils/NotificationConsoleUtils";
import * as TelemetryProcessor from "../../Shared/Telemetry/TelemetryProcessor"; import TelemetryProcessor from "../../Shared/Telemetry/TelemetryProcessor";
import QueryTab from "../Tabs/QueryTab"; import QueryTab from "../Tabs/QueryTab";
export class SaveQueryPane extends ContextualPaneBase { export class SaveQueryPane extends ContextualPaneBase {

View File

@@ -12,7 +12,7 @@
<form class="paneContentContainer" data-bind="submit: submit"> <form class="paneContentContainer" data-bind="submit: submit">
<!-- Settings Confirmation header - Start --> <!-- Settings Confirmation header - Start -->
<div class="firstdivbg headerline"> <div class="firstdivbg headerline">
<span role="heading" aria-level="2" data-bind="text: title"></span> <span data-bind="text: title"></span>
<div <div
class="closeImg" class="closeImg"
role="button" role="button"

View File

@@ -6,7 +6,7 @@
<div class="paneContentContainer"> <div class="paneContentContainer">
<!-- Setup notebooks header - Start --> <!-- Setup notebooks header - Start -->
<div class="firstdivbg headerline"> <div class="firstdivbg headerline">
<span role="heading" aria-level="2" data-bind="text: title"></span> <span data-bind="text: title"></span>
<div <div
class="closeImg" class="closeImg"
role="button" role="button"

View File

@@ -4,7 +4,7 @@ import { Areas, KeyCodes } from "../../Common/Constants";
import { ConsoleDataType } from "../Menus/NotificationConsole/NotificationConsoleComponent"; import { ConsoleDataType } from "../Menus/NotificationConsole/NotificationConsoleComponent";
import { ContextualPaneBase } from "./ContextualPaneBase"; import { ContextualPaneBase } from "./ContextualPaneBase";
import * as NotificationConsoleUtils from "../../Utils/NotificationConsoleUtils"; import * as NotificationConsoleUtils from "../../Utils/NotificationConsoleUtils";
import * as TelemetryProcessor from "../../Shared/Telemetry/TelemetryProcessor"; import TelemetryProcessor from "../../Shared/Telemetry/TelemetryProcessor";
import * as ko from "knockout"; import * as ko from "knockout";
export class SetupNotebooksPane extends ContextualPaneBase { export class SetupNotebooksPane extends ContextualPaneBase {

View File

@@ -6,7 +6,7 @@
<form class="paneContentContainer" data-bind="submit: submit"> <form class="paneContentContainer" data-bind="submit: submit">
<!-- String Input header - Start --> <!-- String Input header - Start -->
<div class="firstdivbg headerline"> <div class="firstdivbg headerline">
<span role="heading" aria-level="2" data-bind="text: title"></span> <span data-bind="text: title"></span>
<div class="closeImg" role="button" aria-label="Close pane" tabindex="0" data-bind="click: cancel"></div> <div class="closeImg" role="button" aria-label="Close pane" tabindex="0" data-bind="click: cancel"></div>
</div> </div>
<!-- String Input header - End --> <!-- String Input header - End -->

View File

@@ -5,7 +5,7 @@
<div class="contextual-pane-in"> <div class="contextual-pane-in">
<!-- Switch Directory header - Start --> <!-- Switch Directory header - Start -->
<div class="firstdivbg headerline"> <div class="firstdivbg headerline">
<span role="heading" aria-level="2" data-bind="text: title"></span> <span data-bind="text: title"></span>
<div class="closeImg" role="button" aria-label="Close pane" tabindex="0" data-bind="click: close"> <div class="closeImg" role="button" aria-label="Close pane" tabindex="0" data-bind="click: close">
<img src="../../../images/close-black.svg" title="Close" alt="Close" /> <img src="../../../images/close-black.svg" title="Close" alt="Close" />
</div> </div>

View File

@@ -5,7 +5,7 @@ import { DirectoryListProps } from "../Controls/Directory/DirectoryListComponent
import { DefaultDirectoryDropdownProps } from "../Controls/Directory/DefaultDirectoryDropdownComponent"; import { DefaultDirectoryDropdownProps } from "../Controls/Directory/DefaultDirectoryDropdownComponent";
import { DirectoryComponentAdapter } from "../Controls/Directory/DirectoryComponentAdapter"; import { DirectoryComponentAdapter } from "../Controls/Directory/DirectoryComponentAdapter";
import SwitchDirectoryPaneTemplate from "./SwitchDirectoryPane.html"; import SwitchDirectoryPaneTemplate from "./SwitchDirectoryPane.html";
import * as TelemetryProcessor from "../../Shared/Telemetry/TelemetryProcessor"; import TelemetryProcessor from "../../Shared/Telemetry/TelemetryProcessor";
class PaneComponent { class PaneComponent {
constructor(data: any) { constructor(data: any) {

View File

@@ -80,7 +80,7 @@ export default class AddTableEntityPane extends TableEntityPane {
this.updateIsActionEnabled(); this.updateIsActionEnabled();
super.open(); super.open();
} }
const focusElement = document.getElementById("closeAddEntityPane"); const focusElement = document.getElementById("addTableEntityValue");
focusElement && focusElement.focus(); focusElement && focusElement.focus();
} }

View File

@@ -21,7 +21,6 @@
<div class="firstdivbg headerline"> <div class="firstdivbg headerline">
<span role="heading" aria-level="2" data-bind="text: title"></span> <span role="heading" aria-level="2" data-bind="text: title"></span>
<div <div
id="closeAddEntityPane"
class="closeImg" class="closeImg"
role="button" role="button"
aria-label="Close pane" aria-label="Close pane"

View File

@@ -19,7 +19,7 @@
> >
<!-- Edit table entity header - Start --> <!-- Edit table entity header - Start -->
<div class="firstdivbg headerline"> <div class="firstdivbg headerline">
<span role="heading" aria-level="2" data-bind="text: title"></span> <span data-bind="text: title"></span>
<div <div
class="closeImg" class="closeImg"
role="button" role="button"

View File

@@ -6,7 +6,7 @@
<form class="paneContentContainer" data-bind="submit: submit"> <form class="paneContentContainer" data-bind="submit: submit">
<!-- Upload File header - Start --> <!-- Upload File header - Start -->
<div class="firstdivbg headerline"> <div class="firstdivbg headerline">
<span role="heading" aria-level="2" data-bind="text: title"></span> <span data-bind="text: title"></span>
<div class="closeImg" role="button" aria-label="Close pane" tabindex="0" data-bind="click: cancel"> <div class="closeImg" role="button" aria-label="Close pane" tabindex="0" data-bind="click: cancel">
<img src="../../../images/close-black.svg" title="Close" alt="Close" /> <img src="../../../images/close-black.svg" title="Close" alt="Close" />
</div> </div>

View File

@@ -11,7 +11,7 @@
<form class="paneContentContainer" data-bind="submit: submit"> <form class="paneContentContainer" data-bind="submit: submit">
<!-- Upload items header - Start --> <!-- Upload items header - Start -->
<div class="firstdivbg headerline"> <div class="firstdivbg headerline">
<span role="heading" aria-level="2" data-bind="text: title"></span> <span data-bind="text: title"></span>
<div <div
class="closeImg" class="closeImg"
role="button" role="button"

View File

@@ -7,7 +7,7 @@ import * as CommonConstants from "../../../Common/Constants";
import * as Constants from "../Constants"; import * as Constants from "../Constants";
import * as Entities from "../Entities"; import * as Entities from "../Entities";
import QueryTablesTab from "../../Tabs/QueryTablesTab"; import QueryTablesTab from "../../Tabs/QueryTablesTab";
import * as TelemetryProcessor from "../../../Shared/Telemetry/TelemetryProcessor"; import TelemetryProcessor from "../../../Shared/Telemetry/TelemetryProcessor";
import { QueryIterator, ItemDefinition, Resource } from "@azure/cosmos"; import { QueryIterator, ItemDefinition, Resource } from "@azure/cosmos";
// This is the format of the data we will have to pass to Datatable render callback, // This is the format of the data we will have to pass to Datatable render callback,

View File

@@ -15,7 +15,7 @@ import * as Utilities from "../Utilities";
import * as Entities from "../Entities"; import * as Entities from "../Entities";
import QueryTablesTab from "../../Tabs/QueryTablesTab"; import QueryTablesTab from "../../Tabs/QueryTablesTab";
import * as TableEntityProcessor from "../TableEntityProcessor"; import * as TableEntityProcessor from "../TableEntityProcessor";
import * as TelemetryProcessor from "../../../Shared/Telemetry/TelemetryProcessor"; import TelemetryProcessor from "../../../Shared/Telemetry/TelemetryProcessor";
import * as ErrorParserUtility from "../../../Common/ErrorParserUtility"; import * as ErrorParserUtility from "../../../Common/ErrorParserUtility";
import * as DataModels from "../../../Contracts/DataModels"; import * as DataModels from "../../../Contracts/DataModels";
import * as ViewModels from "../../../Contracts/ViewModels"; import * as ViewModels from "../../../Contracts/ViewModels";

View File

@@ -100,7 +100,7 @@
</table> </table>
</div> </div>
<div class="loadMore"> <div class="loadMore">
<a role="button" data-bind="click: loadNextPage, event: { keypress: onLoadMoreKeyInput }" tabindex="0" <a role="link" data-bind="click: loadNextPage, event: { keypress: onLoadMoreKeyInput }" tabindex="0"
>Load more</a >Load more</a
> >
</div> </div>

View File

@@ -13,7 +13,7 @@ import * as HeadersUtility from "../../Common/HeadersUtility";
import TabsBase from "./TabsBase"; import TabsBase from "./TabsBase";
import { DocumentsGridMetrics } from "../../Common/Constants"; import { DocumentsGridMetrics } from "../../Common/Constants";
import { Splitter, SplitterBounds, SplitterDirection } from "../../Common/Splitter"; import { Splitter, SplitterBounds, SplitterDirection } from "../../Common/Splitter";
import * as TelemetryProcessor from "../../Shared/Telemetry/TelemetryProcessor"; import TelemetryProcessor from "../../Shared/Telemetry/TelemetryProcessor";
import SaveIcon from "../../../images/save-cosmos.svg"; import SaveIcon from "../../../images/save-cosmos.svg";
import DiscardIcon from "../../../images/discard.svg"; import DiscardIcon from "../../../images/discard.svg";
import DeleteIcon from "../../../images/delete.svg"; import DeleteIcon from "../../../images/delete.svg";

View File

@@ -11,7 +11,7 @@ import editable from "../../Common/EditableUtility";
import Q from "q"; import Q from "q";
import SaveIcon from "../../../images/save-cosmos.svg"; import SaveIcon from "../../../images/save-cosmos.svg";
import TabsBase from "./TabsBase"; import TabsBase from "./TabsBase";
import * as TelemetryProcessor from "../../Shared/Telemetry/TelemetryProcessor"; import TelemetryProcessor from "../../Shared/Telemetry/TelemetryProcessor";
import { Action } from "../../Shared/Telemetry/TelemetryConstants"; import { Action } from "../../Shared/Telemetry/TelemetryConstants";
import { PlatformType } from "../../PlatformType"; import { PlatformType } from "../../PlatformType";
import { RequestOptions } from "@azure/cosmos/dist-esm"; import { RequestOptions } from "@azure/cosmos/dist-esm";

View File

@@ -200,7 +200,7 @@
</table> </table>
</div> </div>
<div class="loadMore"> <div class="loadMore">
<a role="button" data-bind="click: loadNextPage, event: { keypress: onLoadMoreKeyInput }" tabindex="0" <a role="link" data-bind="click: loadNextPage, event: { keypress: onLoadMoreKeyInput }" tabindex="0"
>Load more</a >Load more</a
> >
</div> </div>

View File

@@ -14,7 +14,7 @@ import TabsBase from "./TabsBase";
import { DocumentsGridMetrics } from "../../Common/Constants"; import { DocumentsGridMetrics } from "../../Common/Constants";
import { QueryUtils } from "../../Utils/QueryUtils"; import { QueryUtils } from "../../Utils/QueryUtils";
import { Splitter, SplitterBounds, SplitterDirection } from "../../Common/Splitter"; import { Splitter, SplitterBounds, SplitterDirection } from "../../Common/Splitter";
import * as TelemetryProcessor from "../../Shared/Telemetry/TelemetryProcessor"; import TelemetryProcessor from "../../Shared/Telemetry/TelemetryProcessor";
import NewDocumentIcon from "../../../images/NewDocument.svg"; import NewDocumentIcon from "../../../images/NewDocument.svg";
import SaveIcon from "../../../images/save-cosmos.svg"; import SaveIcon from "../../../images/save-cosmos.svg";
import DiscardIcon from "../../../images/discard.svg"; import DiscardIcon from "../../../images/discard.svg";

View File

@@ -390,7 +390,7 @@
</table> </table>
<div class="loadMore"> <div class="loadMore">
<a role="button" data-bind="click: loadNextPage, event: { keypress: onLoadMoreKeyInput }" tabindex="0" <a role="link" data-bind="click: loadNextPage, event: { keypress: onLoadMoreKeyInput }" tabindex="0"
>Load more</a >Load more</a
> >
</div> </div>

View File

@@ -8,7 +8,7 @@ import * as ErrorParserUtility from "../../Common/ErrorParserUtility";
import MongoUtility from "../../Common/MongoUtility"; import MongoUtility from "../../Common/MongoUtility";
import ObjectId from "../Tree/ObjectId"; import ObjectId from "../Tree/ObjectId";
import Q from "q"; import Q from "q";
import * as TelemetryProcessor from "../../Shared/Telemetry/TelemetryProcessor"; import TelemetryProcessor from "../../Shared/Telemetry/TelemetryProcessor";
import { Action } from "../../Shared/Telemetry/TelemetryConstants"; import { Action } from "../../Shared/Telemetry/TelemetryConstants";
import { import {
createDocument, createDocument,

View File

@@ -6,7 +6,7 @@ import EnvironmentUtility from "../../Common/EnvironmentUtility";
import { isInvalidParentFrameOrigin } from "../../Utils/MessageValidation"; import { isInvalidParentFrameOrigin } from "../../Utils/MessageValidation";
import Q from "q"; import Q from "q";
import TabsBase from "./TabsBase"; import TabsBase from "./TabsBase";
import * as TelemetryProcessor from "../../Shared/Telemetry/TelemetryProcessor"; import TelemetryProcessor from "../../Shared/Telemetry/TelemetryProcessor";
import { Action, ActionModifiers } from "../../Shared/Telemetry/TelemetryConstants"; import { Action, ActionModifiers } from "../../Shared/Telemetry/TelemetryConstants";
import { ConsoleDataType } from "../Menus/NotificationConsole/NotificationConsoleComponent"; import { ConsoleDataType } from "../Menus/NotificationConsole/NotificationConsoleComponent";
import { HashMap } from "../../Common/HashMap"; import { HashMap } from "../../Common/HashMap";

View File

@@ -17,7 +17,7 @@ import SaveIcon from "../../../images/save-cosmos.svg";
import ClearAllOutputsIcon from "../../../images/notebook/Notebook-clear-all-outputs.svg"; import ClearAllOutputsIcon from "../../../images/notebook/Notebook-clear-all-outputs.svg";
import InterruptKernelIcon from "../../../images/notebook/Notebook-stop.svg"; import InterruptKernelIcon from "../../../images/notebook/Notebook-stop.svg";
import KillKernelIcon from "../../../images/notebook/Notebook-stop.svg"; import KillKernelIcon from "../../../images/notebook/Notebook-stop.svg";
import * as TelemetryProcessor from "../../Shared/Telemetry/TelemetryProcessor"; import TelemetryProcessor from "../../Shared/Telemetry/TelemetryProcessor";
import { Action, ActionModifiers } from "../../Shared/Telemetry/TelemetryConstants"; import { Action, ActionModifiers } from "../../Shared/Telemetry/TelemetryConstants";
import { Areas, ArmApiVersions } from "../../Common/Constants"; import { Areas, ArmApiVersions } from "../../Common/Constants";
import { CommandBarComponentButtonFactory } from "../Menus/CommandBar/CommandBarComponentButtonFactory"; import { CommandBarComponentButtonFactory } from "../Menus/CommandBar/CommandBarComponentButtonFactory";

View File

@@ -111,18 +111,17 @@
data-bind="visible: isMetricsToggled() && allResultsMetadata().length > 0 && errors().length === 0" data-bind="visible: isMetricsToggled() && allResultsMetadata().length > 0 && errors().length === 0"
> >
<table class="queryMetricsSummary"> <table class="queryMetricsSummary">
<caption>
Query Statistics
</caption>
<thead class="queryMetricsSummaryHead"> <thead class="queryMetricsSummaryHead">
<tr class="queryMetricsSummaryHeader queryMetricsSummaryTuple"> <tr class="queryMetricsSummaryHeader queryMetricsSummaryTuple">
<th title="METRIC" scope="col">METRIC</th> <th title="METRIC">METRIC</th>
<th title="VALUE" scope="col">VALUE</th> <th></th>
<th title="VALUE">VALUE</th>
</tr> </tr>
</thead> </thead>
<tbody class="queryMetricsSummaryBody" data-bind="with: aggregatedQueryMetrics"> <tbody class="queryMetricsSummaryBody" data-bind="with: aggregatedQueryMetrics">
<tr class="queryMetricsSummaryTuple"> <tr class="queryMetricsSummaryTuple">
<td title="Request Charge">Request Charge</td> <td title="Request Charge">Request Charge</td>
<td></td>
<td> <td>
<span <span
data-bind="text: $parent.requestChargeDisplayText, attr: { title: $parent.requestChargeDisplayText }" data-bind="text: $parent.requestChargeDisplayText, attr: { title: $parent.requestChargeDisplayText }"
@@ -131,6 +130,7 @@
</tr> </tr>
<tr class="queryMetricsSummaryTuple"> <tr class="queryMetricsSummaryTuple">
<td title="Showing Results">Showing Results</td> <td title="Showing Results">Showing Results</td>
<td></td>
<td> <td>
<span <span
data-bind="text: $parent.showingDocumentsDisplayText, attr: { title: $parent.showingDocumentsDisplayText }" data-bind="text: $parent.showingDocumentsDisplayText, attr: { title: $parent.showingDocumentsDisplayText }"
@@ -138,8 +138,8 @@
</td> </td>
</tr> </tr>
<tr class="queryMetricsSummaryTuple" data-bind="visible: $parent.isQueryMetricsEnabled"> <tr class="queryMetricsSummaryTuple" data-bind="visible: $parent.isQueryMetricsEnabled">
<td><span title="Retrieved document count">Retrieved document count</span></td>
<td> <td>
<span title="Retrieved document count">Retrieved document count</span>
<span class="queryMetricInfoTooltip" role="tooltip" tabindex="0"> <span class="queryMetricInfoTooltip" role="tooltip" tabindex="0">
<img class="infoImg" src="/info-bubble.svg" alt="More information" /> <img class="infoImg" src="/info-bubble.svg" alt="More information" />
<span class="queryMetricTooltipText">Total number of retrieved documents</span> <span class="queryMetricTooltipText">Total number of retrieved documents</span>
@@ -148,8 +148,8 @@
<td><span data-bind="text: retrievedDocumentCount, attr: { title: retrievedDocumentCount }"></span></td> <td><span data-bind="text: retrievedDocumentCount, attr: { title: retrievedDocumentCount }"></span></td>
</tr> </tr>
<tr class="queryMetricsSummaryTuple" data-bind="visible: $parent.isQueryMetricsEnabled"> <tr class="queryMetricsSummaryTuple" data-bind="visible: $parent.isQueryMetricsEnabled">
<td><span title="Retrieved document size">Retrieved document size</span></td>
<td> <td>
<span title="Retrieved document size">Retrieved document size</span>
<span class="queryMetricInfoTooltip" role="tooltip" tabindex="0"> <span class="queryMetricInfoTooltip" role="tooltip" tabindex="0">
<img class="infoImg" src="/info-bubble.svg" alt="More information" /> <img class="infoImg" src="/info-bubble.svg" alt="More information" />
<span class="queryMetricTooltipText">Total size of retrieved documents in bytes</span> <span class="queryMetricTooltipText">Total size of retrieved documents in bytes</span>
@@ -161,8 +161,8 @@
</td> </td>
</tr> </tr>
<tr class="queryMetricsSummaryTuple" data-bind="visible: $parent.isQueryMetricsEnabled"> <tr class="queryMetricsSummaryTuple" data-bind="visible: $parent.isQueryMetricsEnabled">
<td><span title="Output document count">Output document count</span></td>
<td> <td>
<span title="Output document count">Output document count</span>
<span class="queryMetricInfoTooltip" role="tooltip" tabindex="0"> <span class="queryMetricInfoTooltip" role="tooltip" tabindex="0">
<img class="infoImg" src="/info-bubble.svg" alt="More information" /> <img class="infoImg" src="/info-bubble.svg" alt="More information" />
<span class="queryMetricTooltipText">Number of output documents</span> <span class="queryMetricTooltipText">Number of output documents</span>
@@ -171,8 +171,8 @@
<td><span data-bind="text: outputDocumentCount, attr: { title: outputDocumentCount }"></span></td> <td><span data-bind="text: outputDocumentCount, attr: { title: outputDocumentCount }"></span></td>
</tr> </tr>
<tr class="queryMetricsSummaryTuple" data-bind="visible: $parent.isQueryMetricsEnabled"> <tr class="queryMetricsSummaryTuple" data-bind="visible: $parent.isQueryMetricsEnabled">
<td><span title="Output document size">Output document size</span></td>
<td> <td>
<span title="Output document size">Output document size</span>
<span class="queryMetricInfoTooltip" role="tooltip" tabindex="0"> <span class="queryMetricInfoTooltip" role="tooltip" tabindex="0">
<img class="infoImg" src="/info-bubble.svg" alt="More information" /> <img class="infoImg" src="/info-bubble.svg" alt="More information" />
<span class="queryMetricTooltipText">Total size of output documents in bytes</span> <span class="queryMetricTooltipText">Total size of output documents in bytes</span>
@@ -184,8 +184,8 @@
</td> </td>
</tr> </tr>
<tr class="queryMetricsSummaryTuple" data-bind="visible: $parent.isQueryMetricsEnabled"> <tr class="queryMetricsSummaryTuple" data-bind="visible: $parent.isQueryMetricsEnabled">
<td><span title="Index hit document count">Index hit document count</span></td>
<td> <td>
<span title="Index hit document count">Index hit document count</span>
<span class="queryMetricInfoTooltip" role="tooltip" tabindex="0"> <span class="queryMetricInfoTooltip" role="tooltip" tabindex="0">
<img class="infoImg" src="/info-bubble.svg" alt="More information" /> <img class="infoImg" src="/info-bubble.svg" alt="More information" />
<span class="queryMetricTooltipText">Total number of documents matched by the filter</span> <span class="queryMetricTooltipText">Total number of documents matched by the filter</span>
@@ -194,8 +194,8 @@
<td><span data-bind="text: indexHitDocumentCount, attr: { title: indexHitDocumentCount }"></span></td> <td><span data-bind="text: indexHitDocumentCount, attr: { title: indexHitDocumentCount }"></span></td>
</tr> </tr>
<tr class="queryMetricsSummaryTuple" data-bind="visible: $parent.isQueryMetricsEnabled"> <tr class="queryMetricsSummaryTuple" data-bind="visible: $parent.isQueryMetricsEnabled">
<td><span title="Index lookup time">Index lookup time</span></td>
<td> <td>
<span title="Index lookup time">Index lookup time</span>
<span class="queryMetricInfoTooltip" role="tooltip" tabindex="0"> <span class="queryMetricInfoTooltip" role="tooltip" tabindex="0">
<img class="infoImg" src="/info-bubble.svg" alt="More information" /> <img class="infoImg" src="/info-bubble.svg" alt="More information" />
<span class="queryMetricTooltipText">Time spent in physical index layer</span> <span class="queryMetricTooltipText">Time spent in physical index layer</span>
@@ -206,8 +206,8 @@
</td> </td>
</tr> </tr>
<tr class="queryMetricsSummaryTuple" data-bind="visible: $parent.isQueryMetricsEnabled"> <tr class="queryMetricsSummaryTuple" data-bind="visible: $parent.isQueryMetricsEnabled">
<td><span title="Document load time">Document load time</span></td>
<td> <td>
<span title="Document load time">Document load time</span>
<span class="queryMetricInfoTooltip" role="tooltip" tabindex="0"> <span class="queryMetricInfoTooltip" role="tooltip" tabindex="0">
<img class="infoImg" src="/info-bubble.svg" alt="More information" /> <img class="infoImg" src="/info-bubble.svg" alt="More information" />
<span class="queryMetricTooltipText">Time spent in loading documents</span> <span class="queryMetricTooltipText">Time spent in loading documents</span>
@@ -218,8 +218,8 @@
</td> </td>
</tr> </tr>
<tr class="queryMetricsSummaryTuple" data-bind="visible: $parent.isQueryMetricsEnabled"> <tr class="queryMetricsSummaryTuple" data-bind="visible: $parent.isQueryMetricsEnabled">
<td><span title="Query engine execution time">Query engine execution time</span></td>
<td> <td>
<span title="Query engine execution time">Query engine execution time</span>
<span class="queryMetricInfoTooltip" role="tooltip" tabindex="0"> <span class="queryMetricInfoTooltip" role="tooltip" tabindex="0">
<img class="infoImg" src="/info-bubble.svg" alt="More information" /> <img class="infoImg" src="/info-bubble.svg" alt="More information" />
<span class="queryMetricTooltipText queryEngineExeTimeInfo" <span class="queryMetricTooltipText queryEngineExeTimeInfo"
@@ -236,8 +236,8 @@
</td> </td>
</tr> </tr>
<tr class="queryMetricsSummaryTuple" data-bind="visible: $parent.isQueryMetricsEnabled"> <tr class="queryMetricsSummaryTuple" data-bind="visible: $parent.isQueryMetricsEnabled">
<td><span title="System function execution time">System function execution time</span></td>
<td> <td>
<span title="System function execution time">System function execution time</span>
<span class="queryMetricInfoTooltip" role="tooltip" tabindex="0"> <span class="queryMetricInfoTooltip" role="tooltip" tabindex="0">
<img class="infoImg" src="/info-bubble.svg" alt="More information" /> <img class="infoImg" src="/info-bubble.svg" alt="More information" />
<span class="queryMetricTooltipText">Total time spent executing system (built-in) functions</span> <span class="queryMetricTooltipText">Total time spent executing system (built-in) functions</span>
@@ -251,8 +251,8 @@
</td> </td>
</tr> </tr>
<tr class="queryMetricsSummaryTuple" data-bind="visible: $parent.isQueryMetricsEnabled"> <tr class="queryMetricsSummaryTuple" data-bind="visible: $parent.isQueryMetricsEnabled">
<td><span title="User defined function execution time">User defined function execution time</span></td>
<td> <td>
<span title="User defined function execution time">User defined function execution time</span>
<span class="queryMetricInfoTooltip" role="tooltip" tabindex="0"> <span class="queryMetricInfoTooltip" role="tooltip" tabindex="0">
<img class="infoImg" src="/info-bubble.svg" alt="More information" /> <img class="infoImg" src="/info-bubble.svg" alt="More information" />
<span class="queryMetricTooltipText">Total time spent executing user-defined functions</span> <span class="queryMetricTooltipText">Total time spent executing user-defined functions</span>
@@ -266,8 +266,8 @@
</td> </td>
</tr> </tr>
<tr class="queryMetricsSummaryTuple" data-bind="visible: $parent.isQueryMetricsEnabled"> <tr class="queryMetricsSummaryTuple" data-bind="visible: $parent.isQueryMetricsEnabled">
<td><span title="Document write time">Document write time</span></td>
<td> <td>
<span title="Document write time">Document write time</span>
<span class="queryMetricInfoTooltip" role="tooltip" tabindex="0"> <span class="queryMetricInfoTooltip" role="tooltip" tabindex="0">
<img class="infoImg" src="/info-bubble.svg" alt="More information" /> <img class="infoImg" src="/info-bubble.svg" alt="More information" />
<span class="queryMetricTooltipText">Time spent to write query result set to response buffer</span> <span class="queryMetricTooltipText">Time spent to write query result set to response buffer</span>
@@ -279,6 +279,7 @@
</tr> </tr>
<tr class="queryMetricsSummaryTuple" data-bind="visible: $parent.roundTrips() != null"> <tr class="queryMetricsSummaryTuple" data-bind="visible: $parent.roundTrips() != null">
<td title="Round Trips">Round Trips</td> <td title="Round Trips">Round Trips</td>
<td></td>
<td><span data-bind="text: $parent.roundTrips, attr: { title: $parent.roundTrips }"></span></td> <td><span data-bind="text: $parent.roundTrips, attr: { title: $parent.roundTrips }"></span></td>
</tr> </tr>
<!-- TODO: Report activity id for mongo queries --> <!-- TODO: Report activity id for mongo queries -->

View File

@@ -93,7 +93,7 @@
margin-left: @MediumSpace; margin-left: @MediumSpace;
.flex-display(); .flex-display();
.flex-direction(); .flex-direction();
.togglesWithMetadata { .togglesWithMetadata {
margin-top: @MediumSpace; margin-top: @MediumSpace;
@@ -102,23 +102,23 @@
height: @ToggleHeight; height: @ToggleHeight;
width: @ToggleWidth; width: @ToggleWidth;
margin-left: @MediumSpace; margin-left: @MediumSpace;
&:focus { &:focus {
.focus(); .focus();
} }
.tab { .tab {
margin-right: @MediumSpace; margin-right: @MediumSpace;
} }
.toggleSwitch { .toggleSwitch {
.toggleSwitch(); .toggleSwitch();
} }
.selectedToggle { .selectedToggle {
.selectedToggle(); .selectedToggle();
} }
.unselectedToggle { .unselectedToggle {
.unselectedToggle(); .unselectedToggle();
} }
@@ -136,7 +136,7 @@
.queryResultNextEnable { .queryResultNextEnable {
color: @AccentMediumHigh; color: @AccentMediumHigh;
font-size: @mediumFontSize; font-size: @mediumFontSize;
cursor: pointer; cursor: pointer;
img { img {
height: @ImgHeight; height: @ImgHeight;
@@ -153,7 +153,7 @@
img { img {
height: @ImgHeight; height: @ImgHeight;
width: @ImgWidth; width: @ImgWidth;
margin-left: @SmallSpace; margin-left: @SmallSpace;
} }
} }
} }
@@ -181,7 +181,7 @@
cursor: pointer; cursor: pointer;
padding: @SmallSpace; padding: @SmallSpace;
} }
.queryMetricsSummaryContainer { .queryMetricsSummaryContainer {
.flex-display(); .flex-display();
.flex-direction(); .flex-direction();
@@ -195,14 +195,10 @@
overflow-y: auto; overflow-y: auto;
overflow-x: hidden; overflow-x: hidden;
caption {
width: 100px;
}
.queryMetricsSummaryHead { .queryMetricsSummaryHead {
.flex-display(); .flex-display();
} }
.queryMetricsSummaryHeader.queryMetricsSummaryTuple { .queryMetricsSummaryHeader.queryMetricsSummaryTuple {
font-size: 10px; font-size: 10px;
} }
@@ -211,7 +207,7 @@
.flex-display(); .flex-display();
.flex-direction(); .flex-direction();
} }
.queryMetricsSummaryTuple { .queryMetricsSummaryTuple {
border-bottom: 1px solid @BaseMedium; border-bottom: 1px solid @BaseMedium;
height: 32px; height: 32px;
@@ -225,14 +221,18 @@
text-overflow: ellipsis; text-overflow: ellipsis;
overflow: hidden; overflow: hidden;
white-space: nowrap; white-space: nowrap;
flex: 0 0 50%; flex: 0 1 auto;
}
&:nth-child(2) {
flex: 1 1 auto;
} }
&:nth-child(3) { &:nth-child(3) {
text-overflow: ellipsis; text-overflow: ellipsis;
overflow: hidden; overflow: hidden;
white-space: nowrap; white-space: nowrap;
flex: 0 0 50%; flex: 0 0 50%;
} }
.queryMetricInfoTooltip { .queryMetricInfoTooltip {
@@ -264,7 +264,7 @@
.tooltipTextAfter(); .tooltipTextAfter();
} }
} }
.queryEngineExeTimeInfo { .queryEngineExeTimeInfo {
width: @QueryEngineExeInfo; width: @QueryEngineExeInfo;
top: -85px; top: -85px;

View File

@@ -10,7 +10,7 @@ import { HashMap } from "../../Common/HashMap";
import * as HeadersUtility from "../../Common/HeadersUtility"; import * as HeadersUtility from "../../Common/HeadersUtility";
import * as Logger from "../../Common/Logger"; import * as Logger from "../../Common/Logger";
import { Splitter, SplitterBounds, SplitterDirection } from "../../Common/Splitter"; import { Splitter, SplitterBounds, SplitterDirection } from "../../Common/Splitter";
import * as TelemetryProcessor from "../../Shared/Telemetry/TelemetryProcessor"; import TelemetryProcessor from "../../Shared/Telemetry/TelemetryProcessor";
import ExecuteQueryIcon from "../../../images/ExecuteQuery.svg"; import ExecuteQueryIcon from "../../../images/ExecuteQuery.svg";
import { QueryUtils } from "../../Utils/QueryUtils"; import { QueryUtils } from "../../Utils/QueryUtils";
import SaveQueryIcon from "../../../images/save-cosmos.svg"; import SaveQueryIcon from "../../../images/save-cosmos.svg";

View File

@@ -17,7 +17,7 @@
<div class="error-bar"> <div class="error-bar">
<div class="error-message" aria-label="Error Message" data-bind="visible: hasQueryError"> <div class="error-message" aria-label="Error Message" data-bind="visible: hasQueryError">
<span><img class="entity-error-Img" src="/error_red.svg"/></span> <span><img class="entity-error-Img" src="/error_red.svg"/></span>
<span class="error-text" role="alert" data-bind="text: queryErrorMessage"></span> <span class="error-text" data-bind="text: queryErrorMessage"></span>
</div> </div>
</div> </div>
<!-- Tables Query Tab Errors - End--> <!-- Tables Query Tab Errors - End-->

View File

@@ -1,14 +1,15 @@
import * as ko from "knockout";
import * as monaco from "monaco-editor";
import Q from "q";
import DiscardIcon from "../../../images/discard.svg";
import SaveIcon from "../../../images/save-cosmos.svg";
import * as Constants from "../../Common/Constants"; import * as Constants from "../../Common/Constants";
import editable from "../../Common/EditableUtility"; import * as ko from "knockout";
import Q from "q";
import * as DataModels from "../../Contracts/DataModels"; import * as DataModels from "../../Contracts/DataModels";
import * as ViewModels from "../../Contracts/ViewModels"; import * as ViewModels from "../../Contracts/ViewModels";
import { CommandButtonComponentProps } from "../Controls/CommandButton/CommandButtonComponent";
import TabsBase from "./TabsBase"; import TabsBase from "./TabsBase";
import editable from "../../Common/EditableUtility";
import * as monaco from "monaco-editor";
import SaveIcon from "../../../images/save-cosmos.svg";
import DiscardIcon from "../../../images/discard.svg";
import { CommandButtonComponentProps } from "../Controls/CommandButton/CommandButtonComponent";
export default abstract class ScriptTabBase extends TabsBase implements ViewModels.WaitsForTemplate { export default abstract class ScriptTabBase extends TabsBase implements ViewModels.WaitsForTemplate {
public ariaLabel: ko.Observable<string>; public ariaLabel: ko.Observable<string>;
@@ -29,8 +30,7 @@ export default abstract class ScriptTabBase extends TabsBase implements ViewMode
public formIsValid: ko.Computed<boolean>; public formIsValid: ko.Computed<boolean>;
public formIsDirty: ko.Computed<boolean>; public formIsDirty: ko.Computed<boolean>;
public isNew: ko.Observable<boolean>; public isNew: ko.Observable<boolean>;
// TODO: Remove any. The SDK types for all the script.body are slightly incorrect which makes this REALLY hard to type correct. public resource: ko.Observable<DataModels.Script>;
public resource: ko.Observable<any>;
public isTemplateReady: ko.Observable<boolean>; public isTemplateReady: ko.Observable<boolean>;
protected _partitionKey: DataModels.PartitionKey; protected _partitionKey: DataModels.PartitionKey;
@@ -194,8 +194,8 @@ export default abstract class ScriptTabBase extends TabsBase implements ViewMode
}); });
} }
public abstract onSaveClick: () => Promise<any>; public abstract onSaveClick: () => Q.Promise<any>;
public abstract onUpdateClick: () => Promise<any>; public abstract onUpdateClick: () => Q.Promise<any>;
public onDiscard = (): Q.Promise<any> => { public onDiscard = (): Q.Promise<any> => {
this.setBaselines(); this.setBaselines();
@@ -206,14 +206,14 @@ export default abstract class ScriptTabBase extends TabsBase implements ViewMode
return Q(); return Q();
}; };
public onSaveOrUpdateClick(): Promise<any> { public onSaveOrUpdateClick(): Q.Promise<any> {
if (this.saveButton.visible()) { if (this.saveButton.visible()) {
return this.onSaveClick(); return this.onSaveClick();
} else if (this.updateButton.visible()) { } else if (this.updateButton.visible()) {
return this.onUpdateClick(); return this.onUpdateClick();
} }
return undefined; return Q();
} }
protected getTabsButtons(): CommandButtonComponentProps[] { protected getTabsButtons(): CommandButtonComponentProps[] {

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