Compare commits

..

33 Commits

Author SHA1 Message Date
Tanuj Mittal
832f8d560d Reset focus to trigger element when pane is closed (#217) 2020-09-17 15:08:46 -07:00
Steve Faulkner
d85c96d408 Allow remote config to set valid origins (#205) 2020-09-17 16:13:22 -05:00
Tanuj Mittal
bad6a60d07 Fix aria-labels in AddCollectionPane and DeleteCollectionConfirmationPane (#213)
* Fix aria-labels in AddCollectionPane and DeleteCollectionConfirmationPane

* Fix broken delete
2020-09-17 12:20:22 -07:00
Zachary Foster
b690fe18e6 Focuses header control element on Add Row click in Cassandra (#212) 2020-09-17 13:00:15 -04:00
Garrett Ausfeldt
1bbe08378c Fix focus when adding and removing a unique key (#214)
* fix focus when adding and removing a unique key

* cleanup

Co-authored-by: REDMOND\gaausfel <gaausfel@microsoft.com>
2020-09-17 09:48:42 -07:00
Laurent Nguyen
9b021b29b9 Fix a11y bugs in query stats table: too many columns, header info not passed to column contents (#209) 2020-09-17 10:43:01 +02:00
Laurent Nguyen
562ac38ff1 Fix contrast issue (blue hyperlink over black background) (#210) 2020-09-17 10:40:24 +02:00
DanielSPham
949f9203b8 Analytical storage label (#211)
Co-authored-by: Daniel Si Pham <v-danpha@microsoft.com>
2020-09-16 17:18:00 -07:00
DanielSPham
de7761ba4b Fixed aria label for autopilot throughput (#206)
Co-authored-by: Daniel Si Pham <v-danpha@microsoft.com>
2020-09-16 17:17:41 -07:00
DanielSPham
40f4efab7c Added heading role to pane title (#203)
Co-authored-by: Daniel Si Pham <v-danpha@microsoft.com>
2020-09-16 17:17:23 -07:00
Tanuj Mittal
34c41e1557 Add telemetry for OpenTerminal (#200) 2020-09-16 17:04:34 -07:00
Steve Faulkner
03b19fc875 Split all script data access methods (#197)
* Split all script data access methods

* More cleanup

* Fix Typo
2020-09-15 13:31:30 -05:00
Tanuj Mittal
d6a4924710 Add actionModifier to appInsights telemetry (#202) 2020-09-15 11:21:18 -07:00
DanielSPham
5ccf26e403 Change load more role (#190)
Co-authored-by: Daniel Si Pham <v-danpha@microsoft.com>
2020-09-14 17:02:29 -07:00
DanielSPham
ef7da10b6e Added alert role to query tables tab error (#199)
Co-authored-by: Daniel Si Pham <v-danpha@microsoft.com>
2020-09-14 16:53:39 -07:00
DanielSPham
dfd18152ca Fixed placeholder (#201)
* Fixed placeholder

* Clean up

Co-authored-by: Daniel Si Pham <v-danpha@microsoft.com>
2020-09-14 16:34:56 -07:00
DanielSPham
e22675bc40 Updated TreeComponent alt text (#188)
Co-authored-by: Daniel Si Pham <v-danpha@microsoft.com>
2020-09-14 16:27:12 -07:00
Tanuj Mittal
c4257bf4a9 Revert "Add telemetry for OpenTerminal success/failure (#192)" (#198)
This reverts commit 83b13de685.
2020-09-11 23:12:28 -07:00
Tanuj Mittal
728eeefa17 Fix file downloads from notebooks container (#196)
* Fix file downloads from notebooks container

* Add downloading message
2020-09-11 19:37:00 -07:00
Tanuj Mittal
83b13de685 Add telemetry for OpenTerminal success/failure (#192)
* Add telemetry for OpenTerminal success/failure

* Address review comments
2020-09-11 15:42:55 -07:00
Steve Faulkner
c401f88aae Improve ARM error parsing and display (#189) 2020-09-11 12:54:30 -05:00
DanielSPham
af820c0fbf Fixed notification console screen reader issue (#193)
* Fixed notification console screen reader issue

* Added aria expanded

Co-authored-by: Daniel Si Pham <v-danpha@microsoft.com>
2020-09-11 10:36:53 -07:00
swviswan
a2845a0102 Notebooks: Ability to download file in notebooks. (#194)
Today, if we have non notebook file in the container and we try to download the file, it fails. That is because, the filetype is always defined as notebook in the call to content provider's get method. Fixing it to use the right type in this change.
2020-09-10 23:39:33 -07:00
Tanuj Mittal
ed9b443bf6 Disable fetch tracking in appInsights (#187) 2020-09-10 12:15:24 -07:00
Zachary Foster
3fe63e88cb Adds e2e baseline SQL test in puppeteer (#160)
* Adds e2e baseline SQL test in puppeteer

* fix lint

* Skip index test

* add to ci

* Stop waiting for server

* Switch to headless mode

* Adds wait-for-server to package.json and uses it in puppeteer CI

* Adds waiton

* top level wait on

* Adds env var to secrets ci.yml

* use existing connection string

* redo tests

* nits and hopefully fix timeout issue

* Fix config file

* try waiting for delete container menu

* Removes statuseval

* Remove unused var

* Slow down and add quotes to selector

* Fix blocking point, remove wait

* Reduce to 50

* Adds database delete stuff

* remove logs, add back waitFors

* Finish container SQL spec test
2020-09-09 13:18:27 -04:00
DanielSPham
2de3c07f76 Fixed contrast ratio issues (#184)
Co-authored-by: Daniel Si Pham <v-danpha@microsoft.com>
2020-09-09 09:28:30 -07:00
DanielSPham
53bedb1641 Added descriptive aria label to autoscale throughput link (#185)
Co-authored-by: Daniel Si Pham <v-danpha@microsoft.com>
2020-09-08 11:03:48 -07:00
Steve Faulkner
e6ac5a7043 Telemetry Adjustments (#182) 2020-09-08 12:44:46 -05:00
Steve Faulkner
faf923f647 TypeScript 4.0 (#165) 2020-09-04 09:08:52 -05:00
Laurent Nguyen
d471cff77c Fix resource tree node selection bug (#170)
* Fix bug with resource tree selection for graphs

* Reformat and type fixes
2020-09-04 10:15:53 +02:00
DanielSPham
0a24a0b73e Updated aria label (#180)
Co-authored-by: Daniel Si Pham <v-danpha@microsoft.com>
2020-09-03 14:10:57 -07:00
DanielSPham
ab4753fd1d Fixed contrast ratio for links in notification area (#179)
Co-authored-by: Daniel Si Pham <v-danpha@microsoft.com>
2020-09-03 14:10:32 -07:00
victor-meng
6bc506b81f Clean up unused utility functions for creating databases and collections (#181) 2020-09-03 13:05:22 -07:00
133 changed files with 2166 additions and 2927 deletions

View File

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

View File

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

View File

@@ -196,6 +196,26 @@ jobs:
shell: bash
env:
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:
name: Publish Nuget
if: github.ref == 'refs/heads/master' || contains(github.ref, 'hotfix/') || contains(github.ref, 'release/')

View File

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

View File

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

View File

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

View File

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

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

View File

@@ -1,28 +1,26 @@
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 {
ConflictDefinition,
FeedOptions,
ItemDefinition,
OfferDefinition,
QueryIterator,
Resource,
TriggerDefinition,
OfferDefinition
Resource
} from "@azure/cosmos";
import { client } from "./CosmosClient";
import { LocalStorageUtility, StorageKey } from "../Shared/StorageUtility";
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 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 * as HeadersUtility from "./HeadersUtility";
import { sendCachedDataMessage } from "./MessageHandler";
export function getCommonQueryOptions(options: FeedOptions): any {
const storedItemPerPageSetting: number = LocalStorageUtility.getEntryNumber(StorageKey.ActualItemPerPage);
@@ -55,85 +53,55 @@ export function queryDocuments(
return Q(documentsIterator);
}
export function readStoredProcedures(
collection: ViewModels.Collection,
options?: any
): Q.Promise<DataModels.StoredProcedure[]> {
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 updateOffer(
offer: DataModels.Offer,
newOffer: DataModels.Offer,
options?: RequestOptions
): Q.Promise<DataModels.Offer> {
return Q(
client()
.database(collection.databaseId)
.container(collection.id())
.scripts.storedProcedures.readAll(options)
.fetchAll()
.then(response => response.resources as DataModels.StoredProcedure[])
.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 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 updateDocument(
collection: ViewModels.CollectionBase,
documentId: DocumentId,
newDocument: any
): Q.Promise<any> {
const partitionKey = documentId.partitionKeyValue;
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)
.item(documentId.id(), partitionKey)
.replace(newDocument)
.then(response => response.resource)
);
}
@@ -165,6 +133,16 @@ 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> {
const partitionKey = documentId.partitionKeyValue;
@@ -178,155 +156,6 @@ 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> {
const partitionKey = documentId.partitionKeyValue;
@@ -355,48 +184,6 @@ 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(
collection: ViewModels.Collection,
options: any

View File

@@ -1,18 +1,17 @@
import * as Constants from "./Constants";
import { ConflictDefinition, ItemDefinition, QueryIterator, Resource } from "@azure/cosmos";
import { RequestOptions } from "@azure/cosmos/dist-esm";
import Q from "q";
import * as DataModels from "../Contracts/DataModels";
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 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 * as DataAccessUtilityBase from "./DataAccessUtilityBase";
import { MinimalQueryIterator, nextPage } from "./IteratorUtilities";
import * as Logger from "./Logger";
// TODO: Log all promise resolutions and errors with verbosity levels
export function queryDocuments(
@@ -42,121 +41,6 @@ export function getEntityName() {
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(
collection: ViewModels.Collection,
storedProcedure: StoredProcedure,
@@ -165,22 +49,17 @@ export function executeStoredProcedure(
): Q.Promise<any> {
var deferred = Q.defer<any>();
const id = NotificationConsoleUtils.logConsoleMessage(
ConsoleDataType.InProgress,
`Executing stored procedure ${storedProcedure.id()}`
);
const clearMessage = logConsoleProgress(`Executing stored procedure ${storedProcedure.id()}`);
DataAccessUtilityBase.executeStoredProcedure(collection, storedProcedure, partitionKeyValue, params)
.then(
(response: any) => {
deferred.resolve(response);
NotificationConsoleUtils.logConsoleMessage(
ConsoleDataType.Info,
logConsoleInfo(
`Finished executing stored procedure ${storedProcedure.id()} for container ${storedProcedure.collection.id()}`
);
},
(error: any) => {
NotificationConsoleUtils.logConsoleMessage(
ConsoleDataType.Error,
logConsoleError(
`Failed to execute stored procedure ${storedProcedure.id()} for container ${storedProcedure.collection.id()}: ${JSON.stringify(
error
)}`
@@ -191,7 +70,7 @@ export function executeStoredProcedure(
}
)
.finally(() => {
NotificationConsoleUtils.clearInProgressMessageWithId(id);
clearMessage();
});
return deferred.promise;
@@ -205,32 +84,23 @@ export function queryDocumentsPage(
): Q.Promise<ViewModels.QueryResults> {
var deferred = Q.defer<ViewModels.QueryResults>();
const entityName = getEntityName();
const id = NotificationConsoleUtils.logConsoleMessage(
ConsoleDataType.InProgress,
`Querying ${entityName} for container ${resourceName}`
);
const clearMessage = logConsoleProgress(`Querying ${entityName} for container ${resourceName}`);
Q(nextPage(documentsIterator, firstItemIndex))
.then(
(result: ViewModels.QueryResults) => {
const itemCount = (result.documents && result.documents.length) || 0;
NotificationConsoleUtils.logConsoleMessage(
ConsoleDataType.Info,
`Successfully fetched ${itemCount} ${entityName} for container ${resourceName}`
);
logConsoleInfo(`Successfully fetched ${itemCount} ${entityName} for container ${resourceName}`);
deferred.resolve(result);
},
(error: any) => {
NotificationConsoleUtils.logConsoleMessage(
ConsoleDataType.Error,
`Failed to query ${entityName} for container ${resourceName}: ${JSON.stringify(error)}`
);
logConsoleError(`Failed to query ${entityName} for container ${resourceName}: ${JSON.stringify(error)}`);
Logger.logError(JSON.stringify(error), "QueryDocumentsPage", error.code);
sendNotificationForError(error);
deferred.reject(error);
}
)
.finally(() => {
NotificationConsoleUtils.clearInProgressMessageWithId(id);
clearMessage();
});
return deferred.promise;
@@ -239,27 +109,21 @@ export function queryDocumentsPage(
export function readDocument(collection: ViewModels.CollectionBase, documentId: DocumentId): Q.Promise<any> {
var deferred = Q.defer<any>();
const entityName = getEntityName();
const id = NotificationConsoleUtils.logConsoleMessage(
ConsoleDataType.InProgress,
`Reading ${entityName} ${documentId.id()}`
);
const clearMessage = logConsoleProgress(`Reading ${entityName} ${documentId.id()}`);
DataAccessUtilityBase.readDocument(collection, documentId)
.then(
(document: any) => {
deferred.resolve(document);
},
(error: any) => {
NotificationConsoleUtils.logConsoleMessage(
ConsoleDataType.Error,
`Failed to read ${entityName} ${documentId.id()}: ${JSON.stringify(error)}`
);
logConsoleError(`Failed to read ${entityName} ${documentId.id()}: ${JSON.stringify(error)}`);
Logger.logError(JSON.stringify(error), "ReadDocument", error.code);
sendNotificationForError(error);
deferred.reject(error);
}
)
.finally(() => {
NotificationConsoleUtils.clearInProgressMessageWithId(id);
clearMessage();
});
return deferred.promise;
@@ -272,31 +136,22 @@ export function updateDocument(
): Q.Promise<any> {
var deferred = Q.defer<any>();
const entityName = getEntityName();
const id = NotificationConsoleUtils.logConsoleMessage(
ConsoleDataType.InProgress,
`Updating ${entityName} ${documentId.id()}`
);
const clearMessage = logConsoleProgress(`Updating ${entityName} ${documentId.id()}`);
DataAccessUtilityBase.updateDocument(collection, documentId, newDocument)
.then(
(updatedDocument: any) => {
NotificationConsoleUtils.logConsoleMessage(
ConsoleDataType.Info,
`Successfully updated ${entityName} ${documentId.id()}`
);
logConsoleInfo(`Successfully updated ${entityName} ${documentId.id()}`);
deferred.resolve(updatedDocument);
},
(error: any) => {
NotificationConsoleUtils.logConsoleMessage(
ConsoleDataType.Error,
`Failed to update ${entityName} ${documentId.id()}: ${JSON.stringify(error)}`
);
logConsoleError(`Failed to update ${entityName} ${documentId.id()}: ${JSON.stringify(error)}`);
Logger.logError(JSON.stringify(error), "UpdateDocument", error.code);
sendNotificationForError(error);
deferred.reject(error);
}
)
.finally(() => {
NotificationConsoleUtils.clearInProgressMessageWithId(id);
clearMessage();
});
return deferred.promise;
@@ -308,24 +163,15 @@ export function updateOffer(
options: RequestOptions
): Q.Promise<DataModels.Offer> {
var deferred = Q.defer<any>();
const id = NotificationConsoleUtils.logConsoleMessage(
ConsoleDataType.InProgress,
`Updating offer for resource ${offer.resource}`
);
const clearMessage = logConsoleProgress(`Updating offer for resource ${offer.resource}`);
DataAccessUtilityBase.updateOffer(offer, newOffer, options)
.then(
(replacedOffer: DataModels.Offer) => {
NotificationConsoleUtils.logConsoleMessage(
ConsoleDataType.Info,
`Successfully updated offer for resource ${offer.resource}`
);
logConsoleInfo(`Successfully updated offer for resource ${offer.resource}`);
deferred.resolve(replacedOffer);
},
(error: any) => {
NotificationConsoleUtils.logConsoleMessage(
ConsoleDataType.Error,
`Error updating offer for resource ${offer.resource}: ${JSON.stringify(error)}`
);
logConsoleError(`Error updating offer for resource ${offer.resource}: ${JSON.stringify(error)}`);
Logger.logError(
JSON.stringify({
oldOffer: offer,
@@ -340,108 +186,7 @@ export function updateOffer(
}
)
.finally(() => {
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);
clearMessage();
});
return deferred.promise;
@@ -450,22 +195,15 @@ export function updateTrigger(
export function createDocument(collection: ViewModels.CollectionBase, newDocument: any): Q.Promise<any> {
var deferred = Q.defer<any>();
const entityName = getEntityName();
const id = NotificationConsoleUtils.logConsoleMessage(
ConsoleDataType.InProgress,
`Creating new ${entityName} for container ${collection.id()}`
);
const clearMessage = logConsoleProgress(`Creating new ${entityName} for container ${collection.id()}`);
DataAccessUtilityBase.createDocument(collection, newDocument)
.then(
(savedDocument: any) => {
NotificationConsoleUtils.logConsoleMessage(
ConsoleDataType.Info,
`Successfully created new ${entityName} for container ${collection.id()}`
);
logConsoleInfo(`Successfully created new ${entityName} for container ${collection.id()}`);
deferred.resolve(savedDocument);
},
(error: any) => {
NotificationConsoleUtils.logConsoleMessage(
ConsoleDataType.Error,
logConsoleError(
`Error while creating new ${entityName} for container ${collection.id()}:\n ${JSON.stringify(error)}`
);
Logger.logError(JSON.stringify(error), "CreateDocument", error.code);
@@ -474,115 +212,7 @@ export function createDocument(collection: ViewModels.CollectionBase, newDocumen
}
)
.finally(() => {
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);
clearMessage();
});
return deferred.promise;
@@ -591,31 +221,22 @@ export function createTrigger(
export function deleteDocument(collection: ViewModels.CollectionBase, documentId: DocumentId): Q.Promise<any> {
var deferred = Q.defer<any>();
const entityName = getEntityName();
const id = NotificationConsoleUtils.logConsoleMessage(
ConsoleDataType.InProgress,
`Deleting ${entityName} ${documentId.id()}`
);
const clearMessage = logConsoleProgress(`Deleting ${entityName} ${documentId.id()}`);
DataAccessUtilityBase.deleteDocument(collection, documentId)
.then(
(response: any) => {
NotificationConsoleUtils.logConsoleMessage(
ConsoleDataType.Info,
`Successfully deleted ${entityName} ${documentId.id()}`
);
logConsoleInfo(`Successfully deleted ${entityName} ${documentId.id()}`);
deferred.resolve(response);
},
(error: any) => {
NotificationConsoleUtils.logConsoleMessage(
ConsoleDataType.Error,
`Error while deleting ${entityName} ${documentId.id()}:\n ${JSON.stringify(error)}`
);
logConsoleError(`Error while deleting ${entityName} ${documentId.id()}:\n ${JSON.stringify(error)}`);
Logger.logError(JSON.stringify(error), "DeleteDocument", error.code);
sendNotificationForError(error);
deferred.reject(error);
}
)
.finally(() => {
NotificationConsoleUtils.clearInProgressMessageWithId(id);
clearMessage();
});
return deferred.promise;
@@ -628,134 +249,22 @@ export function deleteConflict(
): Q.Promise<any> {
var deferred = Q.defer<any>();
const id = NotificationConsoleUtils.logConsoleMessage(
ConsoleDataType.InProgress,
`Deleting conflict ${conflictId.id()}`
);
const clearMessage = logConsoleProgress(`Deleting conflict ${conflictId.id()}`);
DataAccessUtilityBase.deleteConflict(collection, conflictId, options)
.then(
(response: any) => {
NotificationConsoleUtils.logConsoleMessage(
ConsoleDataType.Info,
`Successfully deleted conflict ${conflictId.id()}`
);
logConsoleInfo(`Successfully deleted conflict ${conflictId.id()}`);
deferred.resolve(response);
},
(error: any) => {
NotificationConsoleUtils.logConsoleMessage(
ConsoleDataType.Error,
`Error while deleting conflict ${conflictId.id()}:\n ${JSON.stringify(error)}`
);
logConsoleError(`Error while deleting conflict ${conflictId.id()}:\n ${JSON.stringify(error)}`);
Logger.logError(JSON.stringify(error), "DeleteConflict", error.code);
sendNotificationForError(error);
deferred.reject(error);
}
)
.finally(() => {
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);
clearMessage();
});
return deferred.promise;
@@ -775,27 +284,21 @@ export function readCollectionQuotaInfo(
): Q.Promise<DataModels.CollectionQuotaInfo> {
var deferred = Q.defer<DataModels.CollectionQuotaInfo>();
const id = NotificationConsoleUtils.logConsoleMessage(
ConsoleDataType.InProgress,
`Querying quota info for container ${collection.id}`
);
const clearMessage = logConsoleProgress(`Querying quota info for container ${collection.id}`);
DataAccessUtilityBase.readCollectionQuotaInfo(collection, options)
.then(
(quota: DataModels.CollectionQuotaInfo) => {
deferred.resolve(quota);
},
(error: any) => {
NotificationConsoleUtils.logConsoleMessage(
ConsoleDataType.Error,
`Error while querying quota info for container ${collection.id}:\n ${JSON.stringify(error)}`
);
logConsoleError(`Error while querying quota info for container ${collection.id}:\n ${JSON.stringify(error)}`);
Logger.logError(JSON.stringify(error), "ReadCollectionQuotaInfo", error.code);
sendNotificationForError(error);
deferred.reject(error);
}
)
.finally(() => {
NotificationConsoleUtils.clearInProgressMessageWithId(id);
clearMessage();
});
return deferred.promise;
@@ -804,24 +307,21 @@ export function readCollectionQuotaInfo(
export function readOffers(options: any = {}): Q.Promise<DataModels.Offer[]> {
var deferred = Q.defer<DataModels.Offer[]>();
const id = NotificationConsoleUtils.logConsoleMessage(ConsoleDataType.InProgress, "Querying offers");
const clearMessage = logConsoleProgress("Querying offers");
DataAccessUtilityBase.readOffers(options)
.then(
(offers: DataModels.Offer[]) => {
deferred.resolve(offers);
},
(error: any) => {
NotificationConsoleUtils.logConsoleMessage(
ConsoleDataType.Error,
`Error while querying offers:\n ${JSON.stringify(error)}`
);
logConsoleError(`Error while querying offers:\n ${JSON.stringify(error)}`);
Logger.logError(JSON.stringify(error), "ReadOffers", error.code);
sendNotificationForError(error);
deferred.reject(error);
}
)
.finally(() => {
NotificationConsoleUtils.clearInProgressMessageWithId(id);
clearMessage();
});
return deferred.promise;
@@ -833,24 +333,21 @@ export function readOffer(
): Q.Promise<DataModels.OfferWithHeaders> {
var deferred = Q.defer<DataModels.OfferWithHeaders>();
const id = NotificationConsoleUtils.logConsoleMessage(ConsoleDataType.InProgress, "Querying offer");
const clearMessage = logConsoleProgress("Querying offer");
DataAccessUtilityBase.readOffer(requestedResource, options)
.then(
(offer: DataModels.OfferWithHeaders) => {
deferred.resolve(offer);
},
(error: any) => {
NotificationConsoleUtils.logConsoleMessage(
ConsoleDataType.Error,
`Error while querying offer:\n ${JSON.stringify(error)}`
);
logConsoleError(`Error while querying offer:\n ${JSON.stringify(error)}`);
Logger.logError(JSON.stringify(error), "ReadOffer", error.code);
sendNotificationForError(error);
deferred.reject(error);
}
)
.finally(() => {
NotificationConsoleUtils.clearInProgressMessageWithId(id);
clearMessage();
});
return deferred.promise;

View File

@@ -1,6 +1,5 @@
import Q from "q";
import * as MessageHandler from "./MessageHandler";
import { MessageTypes } from "../Contracts/ExplorerContracts";
describe("Message Handler", () => {
it("should handle cached message", async () => {
@@ -26,4 +25,34 @@ describe("Message Handler", () => {
MessageHandler.runGarbageCollector();
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,16 +48,38 @@ export function sendCachedDataMessage<TResponseDataModel>(
export function sendMessage(data: any): void {
if (canSendMessage()) {
window.parent.postMessage(
{
signature: "pcIframe",
data: data
},
window.document.referrer
);
const dataExplorerWindow = getDataExplorerWindow(window);
if (dataExplorerWindow) {
dataExplorerWindow.parent.postMessage(
{
signature: "pcIframe",
data: data
},
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 {
return window.parent !== window;
}

View File

@@ -5,14 +5,7 @@ import { Collection } from "../Contracts/ViewModels";
import DocumentId from "../Explorer/Tree/DocumentId";
import { ResourceProviderClient } from "../ResourceProvider/ResourceProviderClient";
import { updateUserContext } from "../UserContext";
import {
deleteDocument,
getEndpoint,
queryDocuments,
readDocument,
updateDocument,
_createMongoCollectionWithARM
} from "./MongoProxyClient";
import { deleteDocument, getEndpoint, queryDocuments, readDocument, updateDocument } from "./MongoProxyClient";
jest.mock("../ResourceProvider/ResourceProviderClient.ts");
const databaseId = "testDB";
@@ -260,58 +253,4 @@ describe("MongoProxyClient", () => {
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,15 +1,12 @@
import { Constants as CosmosSDKConstants } from "@azure/cosmos";
import queryString from "querystring";
import { AuthType } from "../AuthType";
import * as DataExplorerConstants from "../Common/Constants";
import { configContext } from "../ConfigContext";
import * as DataModels from "../Contracts/DataModels";
import { MessageTypes } from "../Contracts/ExplorerContracts";
import { Collection } from "../Contracts/ViewModels";
import { ConsoleDataType } from "../Explorer/Menus/NotificationConsole/NotificationConsoleComponent";
import DocumentId from "../Explorer/Tree/DocumentId";
import { ResourceProviderClient } from "../ResourceProvider/ResourceProviderClient";
import { AddDbUtilities } from "../Shared/AddDatabaseUtility";
import * as NotificationConsoleUtils from "../Utils/NotificationConsoleUtils";
import { ApiType, HttpHeaders, HttpStatusCodes } from "./Constants";
import { userContext } from "../UserContext";
@@ -330,48 +327,6 @@ 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 {
const serverId = window.dataExplorer.serverId();
const extensionEndpoint = window.dataExplorer.extensionEndpoint();
@@ -404,46 +359,3 @@ async function errorHandling(response: Response, action: string, params: unknown
export function getARMCreateCollectionEndpoint(params: DataModels.MongoParameters): string {
return `subscriptions/${params.sid}/resourceGroups/${params.rg}/providers/Microsoft.DocumentDB/databaseAccounts/${userContext.databaseAccount.name}/mongodbDatabases/${params.db}/collections/${params.coll}`;
}
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

@@ -0,0 +1,28 @@
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

@@ -0,0 +1,28 @@
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

@@ -0,0 +1,28 @@
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

@@ -0,0 +1,26 @@
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

@@ -0,0 +1,22 @@
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

@@ -0,0 +1,22 @@
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

@@ -0,0 +1,27 @@
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

@@ -0,0 +1,27 @@
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

@@ -0,0 +1,27 @@
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

@@ -0,0 +1,29 @@
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

@@ -0,0 +1,29 @@
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

@@ -0,0 +1,29 @@
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 {
platform: Platform;
allowedParentFrameOrigins: RegExp;
allowedParentFrameOrigins: string[];
gitSha?: string;
proxyPath?: string;
AAD_ENDPOINT: string;
@@ -30,7 +30,12 @@ interface ConfigContext {
// Default configuration
let configContext: Readonly<ConfigContext> = {
platform: Platform.Portal,
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$/,
allowedParentFrameOrigins: [
`^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
gitSha: process.env.GIT_SHA,
hostedExplorerURL: "https://cosmos.azure.com/",
@@ -73,8 +78,13 @@ export async function initializeConfiguration(): Promise<ConfigContext> {
const response = await fetch("./config.json");
if (response.status === 200) {
try {
const externalConfig = await response.json();
const { allowedParentFrameOrigins, ...externalConfig } = await response.json();
Object.assign(configContext, externalConfig);
if (allowedParentFrameOrigins && allowedParentFrameOrigins.length > 0) {
updateConfigContext({
allowedParentFrameOrigins: [...configContext.allowedParentFrameOrigins, ...allowedParentFrameOrigins]
});
}
} catch (error) {
console.error("Unable to parse json in config file");
console.error(error);

View File

@@ -88,10 +88,6 @@ export interface Resource {
id: string;
}
export interface ResourceRequest {
id: string;
}
export interface Collection extends Resource {
defaultTtl?: number;
indexingPolicy?: IndexingPolicy;
@@ -104,39 +100,12 @@ export interface Collection extends Resource {
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 {
collections?: Collection[];
}
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 {
resourceId?: string;
resourceType?: string;
@@ -259,28 +228,6 @@ export interface ErrorDataModel {
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 {
databaseId: string;
collectionId: string;
@@ -307,11 +254,6 @@ export enum AutopilotTier {
Tier4 = 4
}
export interface RpOptions {
// tier is sent as string, autoscale as object (AutoPilotCreationSettings)
[key: string]: string | AutoPilotCreationSettings;
}
export interface Query {
id: string;
resourceId: string;
@@ -347,12 +289,6 @@ export interface CreateCollectionParams {
uniqueKeyPolicy?: UniqueKeyPolicy;
}
export interface SharedThroughputRange {
minimumRU: number;
maximumRU: number;
defaultRU: number;
}
export interface Notification {
id: string;
kind: string;
@@ -495,25 +431,6 @@ export interface NotebookConfigurationEndpointInfo {
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 {
userName: string;
password: string;
@@ -555,79 +472,10 @@ export interface MongoParameters extends RpParameters {
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 {
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 {
properties: {
kind: "Jar";

View File

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

View File

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

View File

@@ -265,6 +265,9 @@ exports[`test render renders with filters 1`] = `
"buttonTextDisabled": "#a19f9d",
"buttonTextHovered": "#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",
"disabledBackground": "#f3f2f1",
"disabledBodySubtext": "#c8c6c4",
@@ -604,6 +607,9 @@ exports[`test render renders with filters 1`] = `
"buttonTextDisabled": "#a19f9d",
"buttonTextHovered": "#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",
"disabledBackground": "#f3f2f1",
"disabledBodySubtext": "#c8c6c4",
@@ -997,6 +1003,9 @@ exports[`test render renders with filters 1`] = `
"buttonTextDisabled": "#a19f9d",
"buttonTextHovered": "#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",
"disabledBackground": "#f3f2f1",
"disabledBodySubtext": "#c8c6c4",
@@ -1113,6 +1122,11 @@ exports[`test render renders with filters 1`] = `
},
"iconDisabled": Object {
"color": "#a19f9d",
"selectors": Object {
"@media screen and (-ms-high-contrast: active)": Object {
"color": "GrayText",
},
},
},
"label": Array [
Object {
@@ -1134,6 +1148,11 @@ exports[`test render renders with filters 1`] = `
},
"menuIconDisabled": Object {
"color": "#a19f9d",
"selectors": Object {
"@media screen and (-ms-high-contrast: active)": Object {
"color": "GrayText",
},
},
},
"root": Array [
Object {
@@ -1150,7 +1169,6 @@ exports[`test render renders with filters 1`] = `
"right": 2,
"selectors": Object {
"@media screen and (-ms-high-contrast: active)": Object {
"border": "none",
"bottom": -2,
"left": -2,
"outlineColor": "ButtonText",
@@ -1230,7 +1248,6 @@ exports[`test render renders with filters 1`] = `
"right": 2,
"selectors": Object {
"@media screen and (-ms-high-contrast: active)": Object {
"border": "none",
"bottom": -2,
"left": -2,
"outlineColor": "ButtonText",
@@ -1259,10 +1276,6 @@ exports[`test render renders with filters 1`] = `
":hover": Object {
"outline": 0,
},
"@media screen and (-ms-high-contrast: active)": Object {
"borderColor": "grayText",
"color": "grayText",
},
},
},
Object {
@@ -1362,13 +1375,21 @@ exports[`test render renders with filters 1`] = `
"selectors": Object {
"@media screen and (-ms-high-contrast: active)": Object {
"MsHighContrastAdjust": "none",
"backgroundColor": "WindowText",
"color": "Window",
"backgroundColor": "Window",
"border": "1px solid WindowText",
"borderRightWidth": "0",
"color": "WindowText",
},
},
},
".ms-Button--primary + .ms-Button": Object {
"border": "none",
"selectors": Object {
"@media screen and (-ms-high-contrast: active)": Object {
"border": "1px solid WindowText",
"borderLeftWidth": "0",
},
},
},
},
},
@@ -1408,6 +1429,9 @@ exports[`test render renders with filters 1`] = `
"borderColor": "GrayText",
"color": "GrayText",
},
"@media screen and (forced-colors: active)": Object {
"forcedColorAdjust": "none",
},
},
},
"splitButtonContainerFocused": Object {
@@ -1554,6 +1578,13 @@ 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 {
"cursor": "default",
},
@@ -1775,6 +1806,9 @@ exports[`test render renders with filters 1`] = `
"buttonTextDisabled": "#a19f9d",
"buttonTextHovered": "#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",
"disabledBackground": "#f3f2f1",
"disabledBodySubtext": "#c8c6c4",

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -32,7 +32,7 @@ import { Store, AnyAction, MiddlewareAPI, Middleware, Dispatch } from "redux";
import configureStore from "./NotebookComponent/store";
import { Notification } from "react-notification-system";
import TelemetryProcessor from "../../Shared/Telemetry/TelemetryProcessor";
import * as TelemetryProcessor from "../../Shared/Telemetry/TelemetryProcessor";
import { Action } from "../../Shared/Telemetry/TelemetryConstants";
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 { ConsoleDataType } from "../../Menus/NotificationConsole/NotificationConsoleComponent";
import * as CdbActions from "./actions";
import TelemetryProcessor from "../../../Shared/Telemetry/TelemetryProcessor";
import * as TelemetryProcessor from "../../../Shared/Telemetry/TelemetryProcessor";
import { Action as TelemetryAction } from "../../../Shared/Telemetry/TelemetryConstants";
import { CdbAppState } from "./types";
import { decryptJWTToken } from "../../../Utils/AuthorizationUtils";

View File

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

View File

@@ -194,17 +194,24 @@ export class NotebookContentClient {
});
}
public readFileContent(filePath: string): Promise<string> {
return this.contentProvider
.get(this.getServerConfig(), filePath, { type: "notebook", format: "text", content: 1 })
.toPromise()
.then(xhr => {
const content = (xhr.response as any).content;
if (!content) {
throw new Error("No content read");
}
public async readFileContent(filePath: string): Promise<string> {
const xhr = await this.contentProvider.get(this.getServerConfig(), filePath, { content: 1 }).toPromise();
const content = (xhr.response as any).content;
if (!content) {
throw new Error("No content read");
}
const format = (xhr.response as any).format;
switch (format) {
case "text":
return content;
case "base64":
return atob(content);
case "json":
return stringifyNotebook(content);
});
default:
throw new Error(`Unsupported content format ${format}`);
}
}
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 { GitHubReposPane } from "../Panes/GitHubReposPane";
import ko from "knockout";
import TelemetryProcessor from "../../Shared/Telemetry/TelemetryProcessor";
import * as TelemetryProcessor from "../../Shared/Telemetry/TelemetryProcessor";
import { Action, ActionModifiers } from "../../Shared/Telemetry/TelemetryConstants";
import { IContentProvider } from "@nteract/core";
import { NotebookContentProvider } from "./NotebookComponent/NotebookContentProvider";

View File

@@ -24,8 +24,8 @@
<script type="text/html" id="add-collection-inputs">
<!-- Add collection header - Start -->
<div class="firstdivbg headerline">
<span id="containerTitle" data-bind="text: title"></span>
<div class="closeImg" id="closeBtnAddCollection" role="button" aria-label="Close pane"
<span id="containerTitle" role="heading" aria-level="2" data-bind="text: title" ></span>
<div class="closeImg" id="closeBtnAddCollection" role="button" aria-label="Add collection close pane"
data-bind="click: cancel, event: { keypress: onCloseKeyPress }" tabindex="0">
<img src="../../../images/close-black.svg" title="Close" alt="Close" />
</div>
@@ -115,10 +115,10 @@
<!-- Database provisioned throughput - Start -->
<!-- ko if: canConfigureThroughput -->
<div class="databaseProvision" aria-label="New database provision support"
<div class="databaseProvision" aria-label="Provision database throughput"
data-bind="visible: databaseCreateNew">
<input tabindex="0" type="checkbox" data-test="addCollectionPane-databaseSharedThroughput"
id="addCollection-databaseSharedThroughput" title="Provision shared throughput"
id="addCollection-databaseSharedThroughput" title="Provision database throughput"
data-bind="checked: databaseCreateNewShared" />
<span class="databaseProvisionText" for="databaseSharedThroughput">Provision database throughput</span>
<span class="infoTooltip" role="tooltip" tabindex="0">
@@ -537,9 +537,11 @@
attr: {
'aria-checked': isAnalyticalStorageOn() ? 'true' : 'false'
}" />
<span for="enableAnalyticalStorageRadioOn" data-bind="disable: showEnableSynapseLink">
On
</span>
<label for="enableAnalyticalStorageRadioOn" class="enableAnalyticalStorageRadioLabel">
<span data-bind="disable: showEnableSynapseLink">
On
</span>
</label>
<input class="enableAnalyticalStorageRadio" id="enableAnalyticalStorageRadioOff"
name="analyticalStore" type="radio" role="radio" tabindex="0" data-bind="
@@ -549,9 +551,11 @@
attr: {
'aria-checked': isAnalyticalStorageOn() ? 'false' : 'true'
}" />
<span for="enableAnalyticalStorageRadioOff" data-bind="disable: showEnableSynapseLink">
Off
</span>
<label for="enableAnalyticalStorageRadioOff" class="enableAnalyticalStorageRadioLabel">
<span data-bind="disable: showEnableSynapseLink">
Off
</span>
</label>
</div>
<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 ViewModels from "../../Contracts/ViewModels";
import editable from "../../Common/EditableUtility";
import TelemetryProcessor from "../../Shared/Telemetry/TelemetryProcessor";
import * as TelemetryProcessor from "../../Shared/Telemetry/TelemetryProcessor";
import { Action, ActionModifiers } from "../../Shared/Telemetry/TelemetryConstants";
import { configContext, Platform } from "../../ConfigContext";
import { ContextualPaneBase } from "./ContextualPaneBase";
@@ -943,7 +943,7 @@ export default class AddCollectionPane extends ContextualPaneBase {
const defaultThroughput = this.container.collectionCreationDefaults.throughput;
this.throughputSinglePartition(defaultThroughput.fixed);
this.throughputMultiPartition(
AddCollectionUtility.Utilities.getMaxThroughput(this.container.collectionCreationDefaults, this.container)
AddCollectionUtility.getMaxThroughput(this.container.collectionCreationDefaults, this.container)
);
this.throughputDatabase(defaultThroughput.shared);
@@ -1167,17 +1167,19 @@ export default class AddCollectionPane extends ContextualPaneBase {
private _updateThroughputLimitByCollectionStorage() {
const storage = this.storage();
const minThroughputRU = AddCollectionUtility.Utilities.getMinRUForStorageOption(
this.container.collectionCreationDefaults,
storage
);
const minThroughputRU =
storage === SharedConstants.CollectionCreation.storage10Gb
? SharedConstants.CollectionCreation.DefaultCollectionRUs400
: this.container.collectionCreationDefaults.throughput.unlimitedmin;
let maxThroughputRU = AddCollectionUtility.Utilities.getMaxRUForStorageOption(
this.container.collectionCreationDefaults,
storage
);
let maxThroughputRU;
if (this.isTryCosmosDBSubscription()) {
maxThroughputRU = Constants.TryCosmosExperience.maxRU;
} else {
maxThroughputRU =
storage === SharedConstants.CollectionCreation.storage10Gb
? SharedConstants.CollectionCreation.DefaultCollectionRUs10K
: this.container.collectionCreationDefaults.throughput.unlimitedmax;
}
this.minThroughputRU(minThroughputRU);

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -4,11 +4,12 @@ import * as Constants from "../../Common/Constants";
import { Action, ActionModifiers } from "../../Shared/Telemetry/TelemetryConstants";
import { KeyCodes } from "../../Common/Constants";
import { WaitsForTemplateViewModel } from "../WaitsForTemplateViewModel";
import TelemetryProcessor from "../../Shared/Telemetry/TelemetryProcessor";
import * as TelemetryProcessor from "../../Shared/Telemetry/TelemetryProcessor";
import Explorer from "../Explorer";
// TODO: Use specific actions for logging telemetry data
export abstract class ContextualPaneBase extends WaitsForTemplateViewModel {
private initalFocusedElement: HTMLElement | undefined;
public id: string;
public container: Explorer;
public firstFieldHasFocus: ko.Observable<boolean>;
@@ -49,9 +50,11 @@ export abstract class ContextualPaneBase extends WaitsForTemplateViewModel {
this.visible(false);
this.isExecuting(false);
this.resetData();
this.resetFocus();
}
public open() {
this.initalFocusedElement = document.activeElement as HTMLElement;
this.visible(true);
this.firstFieldHasFocus(true);
this.resizePane();
@@ -123,4 +126,11 @@ export abstract class ContextualPaneBase extends WaitsForTemplateViewModel {
$(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 -->
<div class="firstdivbg headerline">
<span data-bind="text: title"></span>
<span role="heading" aria-level="2" data-bind="text: title"></span>
<div
class="closeImg"
role="button"
@@ -67,8 +67,7 @@
name="collectionIdConfirmation"
required
class="collid"
data-bind="value: collectionIdConfirmation, hasFocus: firstFieldHasFocus"
aria-label="Confirm by typing the collection id"
data-bind="value: collectionIdConfirmation, hasFocus: firstFieldHasFocus, attr: { 'aria-label': collectionIdConfirmationText }"
/>
</p>
</div>

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -70,7 +70,9 @@ export class GenericRightPaneComponent extends React.Component<GenericRightPaneP
private renderPanelHeader = (): JSX.Element => {
return (
<div className="firstdivbg headerline">
<span id="databaseTitle">{this.props.title}</span>
<span id="databaseTitle" role="heading" aria-level={2}>
{this.props.title}
</span>
<IconButton
ariaLabel="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 { IPinnedRepo, JunoClient } from "../../Juno/JunoClient";
import { Action, ActionModifiers } from "../../Shared/Telemetry/TelemetryConstants";
import TelemetryProcessor from "../../Shared/Telemetry/TelemetryProcessor";
import * as TelemetryProcessor from "../../Shared/Telemetry/TelemetryProcessor";
import * as GitHubUtils from "../../Utils/GitHubUtils";
import { JunoUtils } from "../../Utils/JunoUtils";
import * as NotificationConsoleUtils from "../../Utils/NotificationConsoleUtils";

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -5,7 +5,7 @@
<div class="contextual-pane-in">
<!-- Switch Directory header - Start -->
<div class="firstdivbg headerline">
<span data-bind="text: title"></span>
<span role="heading" aria-level="2" data-bind="text: title"></span>
<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" />
</div>

View File

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

View File

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

View File

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

View File

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

View File

@@ -6,7 +6,7 @@
<form class="paneContentContainer" data-bind="submit: submit">
<!-- Upload File header - Start -->
<div class="firstdivbg headerline">
<span data-bind="text: title"></span>
<span role="heading" aria-level="2" data-bind="text: title"></span>
<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" />
</div>

View File

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

View File

@@ -7,7 +7,7 @@ import * as CommonConstants from "../../../Common/Constants";
import * as Constants from "../Constants";
import * as Entities from "../Entities";
import QueryTablesTab from "../../Tabs/QueryTablesTab";
import TelemetryProcessor from "../../../Shared/Telemetry/TelemetryProcessor";
import * as TelemetryProcessor from "../../../Shared/Telemetry/TelemetryProcessor";
import { QueryIterator, ItemDefinition, Resource } from "@azure/cosmos";
// 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 QueryTablesTab from "../../Tabs/QueryTablesTab";
import * as TableEntityProcessor from "../TableEntityProcessor";
import TelemetryProcessor from "../../../Shared/Telemetry/TelemetryProcessor";
import * as TelemetryProcessor from "../../../Shared/Telemetry/TelemetryProcessor";
import * as ErrorParserUtility from "../../../Common/ErrorParserUtility";
import * as DataModels from "../../../Contracts/DataModels";
import * as ViewModels from "../../../Contracts/ViewModels";

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -6,7 +6,7 @@ import EnvironmentUtility from "../../Common/EnvironmentUtility";
import { isInvalidParentFrameOrigin } from "../../Utils/MessageValidation";
import Q from "q";
import TabsBase from "./TabsBase";
import TelemetryProcessor from "../../Shared/Telemetry/TelemetryProcessor";
import * as TelemetryProcessor from "../../Shared/Telemetry/TelemetryProcessor";
import { Action, ActionModifiers } from "../../Shared/Telemetry/TelemetryConstants";
import { ConsoleDataType } from "../Menus/NotificationConsole/NotificationConsoleComponent";
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 InterruptKernelIcon from "../../../images/notebook/Notebook-stop.svg";
import KillKernelIcon from "../../../images/notebook/Notebook-stop.svg";
import TelemetryProcessor from "../../Shared/Telemetry/TelemetryProcessor";
import * as TelemetryProcessor from "../../Shared/Telemetry/TelemetryProcessor";
import { Action, ActionModifiers } from "../../Shared/Telemetry/TelemetryConstants";
import { Areas, ArmApiVersions } from "../../Common/Constants";
import { CommandBarComponentButtonFactory } from "../Menus/CommandBar/CommandBarComponentButtonFactory";

View File

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

View File

@@ -195,6 +195,10 @@
overflow-y: auto;
overflow-x: hidden;
caption {
width: 100px;
}
.queryMetricsSummaryHead {
.flex-display();
}
@@ -221,11 +225,7 @@
text-overflow: ellipsis;
overflow: hidden;
white-space: nowrap;
flex: 0 1 auto;
}
&:nth-child(2) {
flex: 1 1 auto;
flex: 0 0 50%;
}
&:nth-child(3) {

View File

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

View File

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

View File

@@ -1,15 +1,14 @@
import * as Constants from "../../Common/Constants";
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 editable from "../../Common/EditableUtility";
import * as DataModels from "../../Contracts/DataModels";
import * as ViewModels from "../../Contracts/ViewModels";
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";
import TabsBase from "./TabsBase";
export default abstract class ScriptTabBase extends TabsBase implements ViewModels.WaitsForTemplate {
public ariaLabel: ko.Observable<string>;
@@ -30,7 +29,8 @@ export default abstract class ScriptTabBase extends TabsBase implements ViewMode
public formIsValid: ko.Computed<boolean>;
public formIsDirty: ko.Computed<boolean>;
public isNew: ko.Observable<boolean>;
public resource: ko.Observable<DataModels.Script>;
// 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<any>;
public isTemplateReady: ko.Observable<boolean>;
protected _partitionKey: DataModels.PartitionKey;
@@ -194,8 +194,8 @@ export default abstract class ScriptTabBase extends TabsBase implements ViewMode
});
}
public abstract onSaveClick: () => Q.Promise<any>;
public abstract onUpdateClick: () => Q.Promise<any>;
public abstract onSaveClick: () => Promise<any>;
public abstract onUpdateClick: () => Promise<any>;
public onDiscard = (): Q.Promise<any> => {
this.setBaselines();
@@ -206,14 +206,14 @@ export default abstract class ScriptTabBase extends TabsBase implements ViewMode
return Q();
};
public onSaveOrUpdateClick(): Q.Promise<any> {
public onSaveOrUpdateClick(): Promise<any> {
if (this.saveButton.visible()) {
return this.onSaveClick();
} else if (this.updateButton.visible()) {
return this.onUpdateClick();
}
return Q();
return undefined;
}
protected getTabsButtons(): CommandButtonComponentProps[] {

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