diff --git a/README.md b/README.md
index 9057742d5..fb28c5d7a 100644
--- a/README.md
+++ b/README.md
@@ -69,6 +69,10 @@ Jest and Puppeteer are used for end to end browser based tests and are contained
We generally adhere to the release strategy [documented by the Azure SDK Guidelines](https://azure.github.io/azure-sdk/policies_repobranching.html#release-branches). Most releases should happen from the master branch. If master contains commits that cannot be released, you may create a release from a `release/` or `hotfix/` branch. See linked documentation for more details.
+### Architechture
+
+[](https://mermaid-js.github.io/mermaid-live-editor/#/edit/eyJjb2RlIjoiZ3JhcGggTFJcbiAgaG9zdGVkKGh0dHBzOi8vY29zbW9zLmF6dXJlLmNvbSlcbiAgcG9ydGFsKFBvcnRhbClcbiAgZW11bGF0b3IoRW11bGF0b3IpXG4gIGFhZFtBQURdXG4gIHJlc291cmNlVG9rZW5bUmVzb3VyY2UgVG9rZW5dXG4gIGNvbm5lY3Rpb25TdHJpbmdbQ29ubmVjdGlvbiBTdHJpbmddXG4gIHBvcnRhbFRva2VuW0VuY3J5cHRlZCBQb3J0YWwgVG9rZW5dXG4gIG1hc3RlcktleVtNYXN0ZXIgS2V5XVxuICBhcm1bQVJNIFJlc291cmNlIFByb3ZpZGVyXVxuICBkYXRhcGxhbmVbRGF0YSBQbGFuZV1cbiAgcHJveHlbUG9ydGFsIEFQSSBQcm94eV1cbiAgc3FsW1NRTF1cbiAgbW9uZ29bTW9uZ29dXG4gIHRhYmxlc1tUYWJsZXNdXG4gIGNhc3NhbmRyYVtDYXNzYW5kcmFdXG4gIGdyYWZbR3JhcGhdXG5cblxuICBlbXVsYXRvciAtLT4gbWFzdGVyS2V5IC0tLS0-IGRhdGFwbGFuZVxuICBwb3J0YWwgLS0-IGFhZFxuICBob3N0ZWQgLS0-IHBvcnRhbFRva2VuICYgcmVzb3VyY2VUb2tlbiAmIGNvbm5lY3Rpb25TdHJpbmcgJiBhYWRcbiAgYWFkIC0tLT4gYXJtXG4gIGFhZCAtLS0-IGRhdGFwbGFuZVxuICBhYWQgLS0tPiBwcm94eVxuICByZXNvdXJjZVRva2VuIC0tLT4gc3FsIC0tPiBkYXRhcGxhbmVcbiAgcG9ydGFsVG9rZW4gLS0tPiBwcm94eVxuICBwcm94eSAtLT4gZGF0YXBsYW5lXG4gIGNvbm5lY3Rpb25TdHJpbmcgLS0-IHNxbCAmIG1vbmdvICYgY2Fzc2FuZHJhICYgZ3JhZiAmIHRhYmxlc1xuICBzcWwgLS0-IGRhdGFwbGFuZVxuICB0YWJsZXMgLS0-IGRhdGFwbGFuZVxuICBtb25nbyAtLT4gcHJveHlcbiAgY2Fzc2FuZHJhIC0tPiBwcm94eVxuICBncmFmIC0tPiBwcm94eVxuXG5cdFx0IiwibWVybWFpZCI6eyJ0aGVtZSI6ImRlZmF1bHQifSwidXBkYXRlRWRpdG9yIjpmYWxzZX0)
+
# Contributing
Please read the [contribution guidelines](./CONTRIBUTING.md).
diff --git a/less/hostedexplorer.less b/less/hostedexplorer.less
index 137c76f11..86186d366 100644
--- a/less/hostedexplorer.less
+++ b/less/hostedexplorer.less
@@ -13,6 +13,11 @@
@NavMediumSpace: 10px;
@NavLargeSpace: 15px;
+.skip-link {
+ position: fixed;
+ top: -200px;
+}
+
html {
font-family: wf_segoe-ui_normal, "Segoe UI", "Segoe WP", Tahoma, Arial, sans-serif;
padding: 0px;
diff --git a/less/tree.less b/less/tree.less
index f2f7dcd6b..56a5ee38e 100644
--- a/less/tree.less
+++ b/less/tree.less
@@ -1,20 +1,12 @@
@import "./Common/Constants";
-.main {
- width: 100%;
- float: left;
- transition: all .0s ease-in-out;
- -ms-transition: all 0s ease-in-out;
- -webkit-transition: all 0s ease-in-out;
- -moz-transition: all .0s ease-in-out;
- height: 100%;
- background-color: white;
- border-left: 0px solid white;
-}
.resourceTree {
height: 100%;
flex: 0 0 auto;
+ .main {
+ height: 100%;
+ }
}
.resourceTreeScroll {
diff --git a/src/Common/Constants.ts b/src/Common/Constants.ts
index 0a72c55b8..9d17f96ae 100644
--- a/src/Common/Constants.ts
+++ b/src/Common/Constants.ts
@@ -133,6 +133,7 @@ export class Features {
export class Flights {
public static readonly SettingsV2 = "settingsv2";
public static readonly MongoIndexEditor = "mongoindexeditor";
+ public static readonly MongoIndexing = "mongoindexing";
}
export class AfecFeatures {
diff --git a/src/Common/ErrorHandlingUtils.ts b/src/Common/ErrorHandlingUtils.ts
index 0491fab77..440b07b25 100644
--- a/src/Common/ErrorHandlingUtils.ts
+++ b/src/Common/ErrorHandlingUtils.ts
@@ -21,7 +21,7 @@ export const handleError = (error: string | ARMError | Error, area: string, cons
sendNotificationForError(errorMessage, errorCode);
};
-export const getErrorMessage = (error: string | Error): string => {
+export const getErrorMessage = (error: string | Error = ""): string => {
const errorMessage = typeof error === "string" ? error : error.message;
return replaceKnownError(errorMessage);
};
@@ -45,10 +45,10 @@ const sendNotificationForError = (errorMessage: string, errorCode: number | stri
const replaceKnownError = (errorMessage: string): string => {
if (
window.dataExplorer?.subscriptionType() === SubscriptionType.Internal &&
- errorMessage.indexOf("SharedOffer is Disabled for your account") >= 0
+ errorMessage?.indexOf("SharedOffer is Disabled for your account") >= 0
) {
return "Database throughput is not supported for internal subscriptions.";
- } else if (errorMessage.indexOf("Partition key paths must contain only valid") >= 0) {
+ } else if (errorMessage?.indexOf("Partition key paths must contain only valid") >= 0) {
return "Partition key paths must contain only valid characters and not contain a trailing slash or wildcard character.";
}
diff --git a/src/Common/OfferUtility.ts b/src/Common/OfferUtility.ts
index a6e1377fe..6d7f6bf7f 100644
--- a/src/Common/OfferUtility.ts
+++ b/src/Common/OfferUtility.ts
@@ -2,8 +2,11 @@ import { Offer, SDKOfferDefinition } from "../Contracts/DataModels";
import { OfferResponse } from "@azure/cosmos";
import { HttpHeaders } from "./Constants";
-export const parseSDKOfferResponse = (offerResponse: OfferResponse): Offer => {
- const offerDefinition: SDKOfferDefinition = offerResponse?.resource;
+export const parseSDKOfferResponse = (offerResponse: OfferResponse): Offer | undefined => {
+ const offerDefinition: SDKOfferDefinition | undefined = offerResponse?.resource;
+ if (!offerDefinition) {
+ return undefined;
+ }
const offerContent = offerDefinition.content;
if (!offerContent) {
return undefined;
@@ -12,7 +15,7 @@ export const parseSDKOfferResponse = (offerResponse: OfferResponse): Offer => {
const minimumThroughput = offerContent.collectionThroughputInfo?.minimumRUForCollection;
const autopilotSettings = offerContent.offerAutopilotSettings;
- if (autopilotSettings) {
+ if (autopilotSettings && autopilotSettings.maxThroughput && minimumThroughput) {
return {
id: offerDefinition.id,
autoscaleMaxThroughput: autopilotSettings.maxThroughput,
diff --git a/src/Common/Splitter.ts b/src/Common/Splitter.ts
index 823a88f99..5699247f1 100644
--- a/src/Common/Splitter.ts
+++ b/src/Common/Splitter.ts
@@ -23,10 +23,10 @@ export class Splitter {
public splitterId: string;
public leftSideId: string;
- public splitter: HTMLElement;
- public leftSide: HTMLElement;
- public lastX: number;
- public lastWidth: number;
+ public splitter!: HTMLElement;
+ public leftSide!: HTMLElement;
+ public lastX!: number;
+ public lastWidth!: number;
private isCollapsed: ko.Observable;
private bounds: SplitterBounds;
@@ -42,9 +42,10 @@ export class Splitter {
}
public initialize() {
- this.splitter = document.getElementById(this.splitterId);
- this.leftSide = document.getElementById(this.leftSideId);
-
+ if (document.getElementById(this.splitterId) !== null && document.getElementById(this.leftSideId) != null) {
+ this.splitter = document.getElementById(this.splitterId);
+ this.leftSide = document.getElementById(this.leftSideId);
+ }
const isVerticalSplitter: boolean = this.direction === SplitterDirection.Vertical;
const splitterOptions: JQueryUI.ResizableOptions = {
animate: true,
diff --git a/src/Contracts/DataModels.ts b/src/Contracts/DataModels.ts
index 505090c18..1ee61c489 100644
--- a/src/Contracts/DataModels.ts
+++ b/src/Contracts/DataModels.ts
@@ -210,9 +210,9 @@ export interface QueryMetrics {
export interface Offer {
id: string;
- autoscaleMaxThroughput: number;
- manualThroughput: number;
- minimumThroughput: number;
+ autoscaleMaxThroughput: number | undefined;
+ manualThroughput: number | undefined;
+ minimumThroughput: number | undefined;
offerDefinition?: SDKOfferDefinition;
offerReplacePending: boolean;
}
diff --git a/src/Explorer/Controls/CollapsiblePanel/CollapsiblePanelComponent.ts b/src/Explorer/Controls/CollapsiblePanel/CollapsiblePanelComponent.ts
index 959980371..b0f398f1a 100644
--- a/src/Explorer/Controls/CollapsiblePanel/CollapsiblePanelComponent.ts
+++ b/src/Explorer/Controls/CollapsiblePanel/CollapsiblePanelComponent.ts
@@ -42,7 +42,7 @@ interface CollapsiblePanelParams {
* Use the optional "collapseToLeft" parameter to collapse to the left.
*/
class CollapsiblePanelViewModel {
- private params: CollapsiblePanelParams;
+ public params: CollapsiblePanelParams;
private isCollapsed: ko.Observable;
public constructor(params: CollapsiblePanelParams) {
@@ -50,7 +50,7 @@ class CollapsiblePanelViewModel {
this.isCollapsed = params.isCollapsed || ko.observable(false);
}
- private toggleCollapse(): void {
+ public toggleCollapse(): void {
this.isCollapsed(!this.isCollapsed());
}
}
diff --git a/src/Explorer/Controls/InputTypeahead/InputTypeahead.ts b/src/Explorer/Controls/InputTypeahead/InputTypeahead.ts
index 9a7f9257f..aff4a14fd 100644
--- a/src/Explorer/Controls/InputTypeahead/InputTypeahead.ts
+++ b/src/Explorer/Controls/InputTypeahead/InputTypeahead.ts
@@ -71,7 +71,7 @@ interface InputTypeaheadParams {
/**
* This function gets called when pressing ENTER on the input box
*/
- submitFct?: (inputValue: string, selection: Item) => void;
+ submitFct?: (inputValue: string | null, selection: Item | null) => void;
/**
* Typehead comes with a Search button that we normally remove.
@@ -88,8 +88,8 @@ interface OnClickItem {
}
interface Cache {
- inputValue: string;
- selection: Item;
+ inputValue: string | null;
+ selection: Item | null;
}
class InputTypeaheadViewModel {
@@ -98,15 +98,12 @@ class InputTypeaheadViewModel {
private params: InputTypeaheadParams;
private cache: Cache;
- private inputValue: string;
- private selection: Item;
public constructor(params: InputTypeaheadParams) {
this.instanceNumber = InputTypeaheadViewModel.instanceCount++;
this.params = params;
this.params.choices.subscribe(this.initializeTypeahead.bind(this));
-
this.cache = {
inputValue: null,
selection: null
@@ -161,7 +158,7 @@ class InputTypeaheadViewModel {
}
}
- $.typeahead(options);
+ ($ as any).typeahead(options);
}
/**
@@ -177,11 +174,11 @@ class InputTypeaheadViewModel {
* Use ko's "template: afterRender" callback to do that without actually using any template.
* Another way is to call it within setTimeout() in constructor.
*/
- private afterRender(): void {
+ public afterRender(): void {
this.initializeTypeahead();
}
- private submit(): void {
+ public submit(): void {
if (this.params.submitFct) {
this.params.submitFct(this.cache.inputValue, this.cache.selection);
}
diff --git a/src/Explorer/Controls/JsonEditor/JsonEditorComponent.ts b/src/Explorer/Controls/JsonEditor/JsonEditorComponent.ts
index 459f3b884..7611fe567 100644
--- a/src/Explorer/Controls/JsonEditor/JsonEditorComponent.ts
+++ b/src/Explorer/Controls/JsonEditor/JsonEditorComponent.ts
@@ -59,10 +59,12 @@ export class JsonEditorViewModel extends WaitsForTemplateViewModel {
this.params = params;
this.params.content.subscribe((newValue: string) => {
- if (!!this.editor) {
- this.editor.getModel().setValue(newValue);
- } else {
- this.createEditor(newValue, this.configureEditor.bind(this));
+ if (newValue) {
+ if (!!this.editor) {
+ this.editor.getModel().setValue(newValue);
+ } else {
+ this.createEditor(newValue, this.configureEditor.bind(this));
+ }
}
});
diff --git a/src/Explorer/Controls/Settings/SettingsComponent.test.tsx b/src/Explorer/Controls/Settings/SettingsComponent.test.tsx
index c8ea4f390..01d0c8d6d 100644
--- a/src/Explorer/Controls/Settings/SettingsComponent.test.tsx
+++ b/src/Explorer/Controls/Settings/SettingsComponent.test.tsx
@@ -231,7 +231,7 @@ describe("SettingsComponent", () => {
it("getUpdatedConflictResolutionPolicy", () => {
const wrapper = shallow();
- const conflictResolutionPolicyPath = "_ts";
+ const conflictResolutionPolicyPath = "/_ts";
const conflictResolutionPolicyProcedure = "sample_sproc";
const expectSprocPath =
"/dbs/" + collection.databaseId + "/colls/" + collection.id() + "/sprocs/" + conflictResolutionPolicyProcedure;
diff --git a/src/Explorer/Controls/Settings/SettingsComponent.tsx b/src/Explorer/Controls/Settings/SettingsComponent.tsx
index 704bebb24..8b901bb19 100644
--- a/src/Explorer/Controls/Settings/SettingsComponent.tsx
+++ b/src/Explorer/Controls/Settings/SettingsComponent.tsx
@@ -138,8 +138,8 @@ export class SettingsComponent extends React.Component,
newValue?: string
): void => {
- const newThroughput = getSanitizedInputValue(newValue, this.autoPilotInputMaxValue);
+ const newThroughput = getSanitizedInputValue(newValue);
this.props.onMaxAutoPilotThroughputChange(newThroughput);
};
@@ -435,7 +435,7 @@ export class ThroughputInputAutoPilotV3Component extends React.Component<
event: React.FormEvent,
newValue?: string
): void => {
- const newThroughput = getSanitizedInputValue(newValue, this.throughputInputMaxValue);
+ const newThroughput = getSanitizedInputValue(newValue);
if (this.overrideWithAutoPilotSettings()) {
this.props.onMaxAutoPilotThroughputChange(newThroughput);
} else {
diff --git a/src/Explorer/Controls/Settings/SettingsUtils.tsx b/src/Explorer/Controls/Settings/SettingsUtils.tsx
index 06b29b4b3..daa9f5e85 100644
--- a/src/Explorer/Controls/Settings/SettingsUtils.tsx
+++ b/src/Explorer/Controls/Settings/SettingsUtils.tsx
@@ -101,13 +101,13 @@ export const parseConflictResolutionProcedure = (procedureFromBackEnd: string):
return procedureFromBackEnd;
};
-export const getSanitizedInputValue = (newValueString: string, max: number): number => {
+export const getSanitizedInputValue = (newValueString: string, max?: number): number => {
const newValue = parseInt(newValueString);
if (isNaN(newValue)) {
return zeroValue;
}
// make sure new value does not exceed the maximum throughput
- return Math.min(newValue, max);
+ return max ? Math.min(newValue, max) : newValue;
};
export const isDirty = (current: isDirtyTypes, baseline: isDirtyTypes): boolean => {
diff --git a/src/Explorer/Controls/Settings/__snapshots__/SettingsComponent.test.tsx.snap b/src/Explorer/Controls/Settings/__snapshots__/SettingsComponent.test.tsx.snap
index 788652f7a..e2a70a521 100644
--- a/src/Explorer/Controls/Settings/__snapshots__/SettingsComponent.test.tsx.snap
+++ b/src/Explorer/Controls/Settings/__snapshots__/SettingsComponent.test.tsx.snap
@@ -954,6 +954,7 @@ exports[`SettingsComponent renders 1`] = `
"isHostedDataExplorerEnabled": [Function],
"isLeftPaneExpanded": [Function],
"isLinkInjectionEnabled": [Function],
+ "isMongoIndexingEnabled": [Function],
"isNotebookEnabled": [Function],
"isNotebooksEnabledForAccount": [Function],
"isNotificationConsoleExpanded": [Function],
@@ -1187,11 +1188,9 @@ exports[`SettingsComponent renders 1`] = `
},
"direction": "vertical",
"isCollapsed": [Function],
- "leftSide": null,
"leftSideId": "resourcetree",
"onResizeStart": [Function],
"onResizeStop": [Function],
- "splitter": null,
"splitterId": "h_splitter1",
},
"stringInputPane": StringInputPane {
@@ -2237,6 +2236,7 @@ exports[`SettingsComponent renders 1`] = `
"isHostedDataExplorerEnabled": [Function],
"isLeftPaneExpanded": [Function],
"isLinkInjectionEnabled": [Function],
+ "isMongoIndexingEnabled": [Function],
"isNotebookEnabled": [Function],
"isNotebooksEnabledForAccount": [Function],
"isNotificationConsoleExpanded": [Function],
@@ -2470,11 +2470,9 @@ exports[`SettingsComponent renders 1`] = `
},
"direction": "vertical",
"isCollapsed": [Function],
- "leftSide": null,
"leftSideId": "resourcetree",
"onResizeStart": [Function],
"onResizeStop": [Function],
- "splitter": null,
"splitterId": "h_splitter1",
},
"stringInputPane": StringInputPane {
@@ -3533,6 +3531,7 @@ exports[`SettingsComponent renders 1`] = `
"isHostedDataExplorerEnabled": [Function],
"isLeftPaneExpanded": [Function],
"isLinkInjectionEnabled": [Function],
+ "isMongoIndexingEnabled": [Function],
"isNotebookEnabled": [Function],
"isNotebooksEnabledForAccount": [Function],
"isNotificationConsoleExpanded": [Function],
@@ -3766,11 +3765,9 @@ exports[`SettingsComponent renders 1`] = `
},
"direction": "vertical",
"isCollapsed": [Function],
- "leftSide": null,
"leftSideId": "resourcetree",
"onResizeStart": [Function],
"onResizeStop": [Function],
- "splitter": null,
"splitterId": "h_splitter1",
},
"stringInputPane": StringInputPane {
@@ -4816,6 +4813,7 @@ exports[`SettingsComponent renders 1`] = `
"isHostedDataExplorerEnabled": [Function],
"isLeftPaneExpanded": [Function],
"isLinkInjectionEnabled": [Function],
+ "isMongoIndexingEnabled": [Function],
"isNotebookEnabled": [Function],
"isNotebooksEnabledForAccount": [Function],
"isNotificationConsoleExpanded": [Function],
@@ -5049,11 +5047,9 @@ exports[`SettingsComponent renders 1`] = `
},
"direction": "vertical",
"isCollapsed": [Function],
- "leftSide": null,
"leftSideId": "resourcetree",
"onResizeStart": [Function],
"onResizeStop": [Function],
- "splitter": null,
"splitterId": "h_splitter1",
},
"stringInputPane": StringInputPane {
diff --git a/src/Explorer/Explorer.ts b/src/Explorer/Explorer.ts
index ff8667c96..542cf12f5 100644
--- a/src/Explorer/Explorer.ts
+++ b/src/Explorer/Explorer.ts
@@ -212,6 +212,7 @@ export default class Explorer {
public isCopyNotebookPaneEnabled: ko.Observable;
public isHostedDataExplorerEnabled: ko.Computed;
public isRightPanelV2Enabled: ko.Computed;
+ public isMongoIndexingEnabled: ko.Observable;
public canExceedMaximumValue: ko.Computed;
public shouldShowShareDialogContents: ko.Observable;
@@ -409,6 +410,7 @@ export default class Explorer {
this.isFeatureEnabled(Constants.Features.enableLinkInjection)
);
this.isGitHubPaneEnabled = ko.observable(false);
+ this.isMongoIndexingEnabled = ko.observable(false);
this.isPublishNotebookPaneEnabled = ko.observable(false);
this.isCopyNotebookPaneEnabled = ko.observable(false);
@@ -1918,6 +1920,9 @@ export default class Explorer {
if (!flights) {
return;
}
+ if (flights.indexOf(Constants.Flights.MongoIndexing) !== -1) {
+ this.isMongoIndexingEnabled(true);
+ }
}
public findSelectedCollection(): ViewModels.Collection {
diff --git a/src/Explorer/Graph/GraphExplorerComponent/ArraysByKeyCache.ts b/src/Explorer/Graph/GraphExplorerComponent/ArraysByKeyCache.ts
index 81324ecfb..0a4d8fd85 100644
--- a/src/Explorer/Graph/GraphExplorerComponent/ArraysByKeyCache.ts
+++ b/src/Explorer/Graph/GraphExplorerComponent/ArraysByKeyCache.ts
@@ -11,7 +11,9 @@ export class ArraysByKeyCache {
public constructor(maxNbElements: number) {
this.maxNbElements = maxNbElements;
- this.clear();
+ this.keyQueue = [];
+ this.cache = {};
+ this.totalElements = 0;
}
public clear(): void {
@@ -58,7 +60,7 @@ export class ArraysByKeyCache {
* @param startIndex
* @param pageSize
*/
- public retrieve(key: string, startIndex: number, pageSize: number): T[] {
+ public retrieve(key: string, startIndex: number, pageSize: number): T[] | null {
if (!this.cache.hasOwnProperty(key)) {
return null;
}
@@ -77,8 +79,10 @@ export class ArraysByKeyCache {
private reduceCacheSize(): void {
// remove an key and its array
const oldKey = this.keyQueue.shift();
- this.totalElements -= this.cache[oldKey].length;
- delete this.cache[oldKey];
+ if (oldKey) {
+ this.totalElements -= this.cache[oldKey].length;
+ delete this.cache[oldKey];
+ }
}
/**
diff --git a/src/Explorer/Graph/GraphExplorerComponent/GraphData.ts b/src/Explorer/Graph/GraphExplorerComponent/GraphData.ts
index 420d98e5e..9cbf502bd 100644
--- a/src/Explorer/Graph/GraphExplorerComponent/GraphData.ts
+++ b/src/Explorer/Graph/GraphExplorerComponent/GraphData.ts
@@ -413,13 +413,13 @@ export class GraphData {
* @param node
* @param prop
*/
- public static getNodePropValue(node: D3Node, prop: string): string | number | boolean {
+ public static getNodePropValue(node: D3Node, prop: string): undefined | string | number | boolean {
if (node.hasOwnProperty(prop)) {
return (node as any)[prop];
}
// This is DocDB specific
- if (node.hasOwnProperty("properties") && node.properties.hasOwnProperty(prop)) {
+ if (node.properties && node.properties.hasOwnProperty(prop)) {
return node.properties[prop][0]["value"];
}
@@ -496,7 +496,7 @@ export class GraphData {
* Get list of children ids of a given vertex
* @param vertex
*/
- private static getChildrenId(vertex: GremlinVertex): string[] {
+ public static getChildrenId(vertex: GremlinVertex): string[] {
const ids = {}; // HashSet
if (vertex.hasOwnProperty("outE")) {
let outE = vertex.outE;
diff --git a/src/Explorer/Notebook/NotebookComponent/__mocks__/rx-jupyter.ts b/src/Explorer/Notebook/NotebookComponent/__mocks__/rx-jupyter.ts
index 1472d5f5d..1b02ef08e 100644
--- a/src/Explorer/Notebook/NotebookComponent/__mocks__/rx-jupyter.ts
+++ b/src/Explorer/Notebook/NotebookComponent/__mocks__/rx-jupyter.ts
@@ -1,18 +1,17 @@
import { Observable, of } from "rxjs";
-import { AjaxResponse } from "rxjs/ajax";
-import { ServerConfig } from "rx-jupyter";
+import { AjaxRequest, AjaxResponse } from "rxjs/ajax";
let fakeAjaxResponse: AjaxResponse = {
- originalEvent: undefined,
+ originalEvent: (undefined),
xhr: new XMLHttpRequest(),
- request: null,
+ request: (null),
status: 200,
response: {},
- responseText: null,
+ responseText: "",
responseType: "json"
};
export const sessions = {
- create: (serverConfig: ServerConfig, body: object): Observable => of(fakeAjaxResponse),
+ create: (serverConfig: unknown, body: object): Observable => of(fakeAjaxResponse),
__setResponse: (response: AjaxResponse) => {
fakeAjaxResponse = response;
},
diff --git a/src/Explorer/Notebook/NotebookContentClient.ts b/src/Explorer/Notebook/NotebookContentClient.ts
index d745c7e4f..60358d306 100644
--- a/src/Explorer/Notebook/NotebookContentClient.ts
+++ b/src/Explorer/Notebook/NotebookContentClient.ts
@@ -11,7 +11,7 @@ import { stringifyNotebook } from "@nteract/commutable";
export class NotebookContentClient {
constructor(
private notebookServerInfo: ko.Observable,
- private notebookBasePath: ko.Observable,
+ public notebookBasePath: ko.Observable,
private contentProvider: IContentProvider
) {}
@@ -117,8 +117,11 @@ export class NotebookContentClient {
private async checkIfFilepathExists(filepath: string): Promise {
const parentDirPath = NotebookUtil.getParentPath(filepath);
- const items = await this.fetchNotebookFiles(parentDirPath);
- return items.some(value => FileSystemUtil.isPathEqual(value.path, filepath));
+ if (parentDirPath) {
+ const items = await this.fetchNotebookFiles(parentDirPath);
+ return items.some(value => FileSystemUtil.isPathEqual(value.path, filepath));
+ }
+ return false;
}
/**
@@ -189,7 +192,7 @@ export class NotebookContentClient {
const dir = xhr.response;
const item = NotebookUtil.createNotebookContentItem(dir.name, dir.path, dir.type);
item.parent = parent;
- parent.children.push(item);
+ parent.children!.push(item);
return item;
});
}
@@ -225,7 +228,7 @@ export class NotebookContentClient {
* Convert rx-jupyter type to our type
* @param type
*/
- private static getType(type: FileType): NotebookContentItemType {
+ public static getType(type: FileType): NotebookContentItemType {
switch (type) {
case "directory":
return NotebookContentItemType.Directory;
diff --git a/src/Explorer/Panes/AddCollectionPane.html b/src/Explorer/Panes/AddCollectionPane.html
index 89096032c..cbbdc8195 100644
--- a/src/Explorer/Panes/AddCollectionPane.html
+++ b/src/Explorer/Panes/AddCollectionPane.html
@@ -257,7 +257,7 @@
range of values and is likely to have evenly distributed access patterns.
- {
- public data: T[];
+ public data: T[] | null;
public sortOrder: any;
public serverCallInProgress: boolean;
diff --git a/src/Explorer/Tables/DataTable/TableEntityListViewModel.ts b/src/Explorer/Tables/DataTable/TableEntityListViewModel.ts
index 668ec71db..2a3e7765f 100644
--- a/src/Explorer/Tables/DataTable/TableEntityListViewModel.ts
+++ b/src/Explorer/Tables/DataTable/TableEntityListViewModel.ts
@@ -16,14 +16,75 @@ import * as Entities from "../Entities";
import QueryTablesTab from "../../Tabs/QueryTablesTab";
import * as TableEntityProcessor from "../TableEntityProcessor";
import * as TelemetryProcessor from "../../../Shared/Telemetry/TelemetryProcessor";
-import * as DataModels from "../../../Contracts/DataModels";
import * as ViewModels from "../../../Contracts/ViewModels";
-import { getErrorMessage, getErrorStack } from "../../../Common/ErrorHandlingUtils";
interface IListTableEntitiesSegmentedResult extends Entities.IListTableEntitiesResult {
ExceedMaximumRetries?: boolean;
}
+export interface ErrorDataModel {
+ message: string;
+ severity?: string;
+ location?: {
+ start: string;
+ end: string;
+ };
+ code?: string;
+}
+
+function parseError(err: any): ErrorDataModel[] {
+ try {
+ return _parse(err);
+ } catch (e) {
+ return [{ message: JSON.stringify(err) }];
+ }
+}
+
+function _parse(err: any): ErrorDataModel[] {
+ var normalizedErrors: ErrorDataModel[] = [];
+ if (err.message && !err.code) {
+ normalizedErrors.push(err);
+ } else {
+ const innerErrors: any[] = _getInnerErrors(err.message);
+ normalizedErrors = innerErrors.map(innerError =>
+ typeof innerError === "string" ? { message: innerError } : innerError
+ );
+ }
+
+ return normalizedErrors;
+}
+
+function _getInnerErrors(message: string): any[] {
+ /*
+ The backend error message has an inner-message which is a stringified object.
+ For SQL errors, the "errors" property is an array of SqlErrorDataModel.
+ Example:
+ "Message: {"Errors":["Resource with specified id or name already exists"]}\r\nActivityId: 80005000008d40b6a, Request URI: /apps/19000c000c0a0005/services/mctestdocdbprod-MasterService-0-00066ab9937/partitions/900005f9000e676fb8/replicas/13000000000955p"
+ For non-SQL errors the "Errors" propery is an array of string.
+ Example:
+ "Message: {"errors":[{"severity":"Error","location":{"start":7,"end":8},"code":"SC1001","message":"Syntax error, incorrect syntax near '.'."}]}\r\nActivityId: d3300016d4084e310a, Request URI: /apps/12401f9e1df77/services/dc100232b1f44545/partitions/f86f3bc0001a2f78/replicas/13085003638s"
+ */
+
+ let innerMessage: any = null;
+
+ const singleLineMessage = message.replace(/[\r\n]|\r|\n/g, "");
+ try {
+ // Multi-Partition error flavor
+ const regExp = /^(.*)ActivityId: (.*)/g;
+ const regString = regExp.exec(singleLineMessage);
+ const innerMessageString = regString[1];
+ innerMessage = JSON.parse(innerMessageString);
+ } catch (e) {
+ // Single-partition error flavor
+ const regExp = /^Message: (.*)ActivityId: (.*), Request URI: (.*)/g;
+ const regString = regExp.exec(singleLineMessage);
+ const innerMessageString = regString[1];
+ innerMessage = JSON.parse(innerMessageString);
+ }
+
+ return innerMessage.errors ? innerMessage.errors : innerMessage.Errors;
+}
+
/**
* Storage Table Entity List ViewModel
*/
@@ -387,8 +448,17 @@ export default class TableEntityListViewModel extends DataTableViewModel {
}
})
.catch((error: any) => {
- const errorMessage = getErrorMessage(error);
- this.queryErrorMessage(errorMessage);
+ const parsedErrors = parseError(error);
+ var errors = parsedErrors.map(error => {
+ return {
+ message: error.message,
+ start: error.location ? error.location.start : undefined,
+ end: error.location ? error.location.end : undefined,
+ code: error.code,
+ severity: error.severity
+ };
+ });
+ this.queryErrorMessage(errors[0].message);
if (this.queryTablesTab.onLoadStartKey != null && this.queryTablesTab.onLoadStartKey != undefined) {
TelemetryProcessor.traceFailure(
Action.Tab,
@@ -399,8 +469,7 @@ export default class TableEntityListViewModel extends DataTableViewModel {
defaultExperience: this.queryTablesTab.collection.container.defaultExperience(),
dataExplorerArea: Areas.Tab,
tabTitle: this.queryTablesTab.tabTitle(),
- error: errorMessage,
- errorStack: getErrorStack(error)
+ error: error
},
this.queryTablesTab.onLoadStartKey
);
@@ -421,47 +490,53 @@ export default class TableEntityListViewModel extends DataTableViewModel {
* Note that this also means that we can get less entities than the requested download size in a successful call.
* See Microsoft Azure API Documentation at: https://msdn.microsoft.com/en-us/library/azure/dd135718.aspx
*/
- private async prefetchData(
+ private prefetchData(
tableQuery: Entities.ITableQuery,
downloadSize: number,
currentRetry: number = 0
- ): Promise {
+ ): Q.Promise {
if (!this.cache.serverCallInProgress) {
this.cache.serverCallInProgress = true;
this.allDownloaded = false;
this.lastPrefetchTime = new Date().getTime();
- const time = this.lastPrefetchTime;
+ var time = this.lastPrefetchTime;
+ var promise: Q.Promise;
if (this._documentIterator && this.continuationToken) {
// TODO handle Cassandra case
- const response = await this._documentIterator.fetchNext();
- const entities: Entities.ITableEntity[] = TableEntityProcessor.convertDocumentsToEntities(response?.resources);
- return {
- Results: entities,
- ContinuationToken: this._documentIterator.hasMoreResults()
- };
- }
-
- try {
- let documents: IListTableEntitiesSegmentedResult;
- if (this.continuationToken && this.queryTablesTab.container.isPreferredApiCassandra()) {
- documents = await this.queryTablesTab.container.tableDataClient.queryDocuments(
+ promise = Q(this._documentIterator.fetchNext().then(response => response.resources)).then(
+ (documents: any[]) => {
+ let entities: Entities.ITableEntity[] = TableEntityProcessor.convertDocumentsToEntities(documents);
+ let finalEntities: IListTableEntitiesSegmentedResult = {
+ Results: entities,
+ ContinuationToken: this._documentIterator.hasMoreResults()
+ };
+ return Q.resolve(finalEntities);
+ }
+ );
+ } else if (this.continuationToken && this.queryTablesTab.container.isPreferredApiCassandra()) {
+ promise = Q(
+ this.queryTablesTab.container.tableDataClient.queryDocuments(
this.queryTablesTab.collection,
this.cqlQuery(),
true,
this.continuationToken
- );
- } else {
- const query = this.queryTablesTab.container.isPreferredApiCassandra() ? this.cqlQuery() : this.sqlQuery();
- documents = await this.queryTablesTab.container.tableDataClient.queryDocuments(
- this.queryTablesTab.collection,
- query,
- true
- );
-
+ )
+ );
+ } else {
+ let query = this.sqlQuery();
+ if (this.queryTablesTab.container.isPreferredApiCassandra()) {
+ query = this.cqlQuery();
+ }
+ promise = Q(
+ this.queryTablesTab.container.tableDataClient.queryDocuments(this.queryTablesTab.collection, query, true)
+ );
+ }
+ return promise
+ .then((result: IListTableEntitiesSegmentedResult) => {
if (!this._documentIterator) {
- this._documentIterator = documents.iterator;
+ this._documentIterator = result.iterator;
}
var actualDownloadSize: number = 0;
@@ -472,11 +547,11 @@ export default class TableEntityListViewModel extends DataTableViewModel {
return Q.resolve(null);
}
- var entities = documents.Results;
+ var entities = result.Results;
actualDownloadSize = entities.length;
// Queries can fetch no results and still return a continuation header. See prefetchAndRender() method.
- this.continuationToken = this.isCancelled ? null : documents.ContinuationToken;
+ this.continuationToken = this.isCancelled ? null : result.ContinuationToken;
if (!this.continuationToken) {
this.allDownloaded = true;
@@ -508,22 +583,20 @@ export default class TableEntityListViewModel extends DataTableViewModel {
// For #2.1, set prefetch exceeds maximum retry number and end prefetch.
// For #2.2, go to next round prefetch.
if (this.allDownloaded || nextDownloadSize === 0) {
- return documents;
+ return Q.resolve(result);
}
if (currentRetry >= TableEntityListViewModel._maximumNumberOfPrefetchRetries) {
- documents.ExceedMaximumRetries = true;
- return documents;
+ result.ExceedMaximumRetries = true;
+ return Q.resolve(result);
}
-
- return await this.prefetchData(tableQuery, nextDownloadSize, currentRetry + 1);
- }
- } catch (error) {
- this.cache.serverCallInProgress = false;
- throw error;
- }
+ return this.prefetchData(tableQuery, nextDownloadSize, currentRetry + 1);
+ })
+ .catch((error: Error) => {
+ this.cache.serverCallInProgress = false;
+ return Q.reject(error);
+ });
}
-
- return undefined;
+ return null;
}
}
diff --git a/src/Explorer/Tables/Entities.ts b/src/Explorer/Tables/Entities.ts
index 0972e8410..f5f3f3386 100644
--- a/src/Explorer/Tables/Entities.ts
+++ b/src/Explorer/Tables/Entities.ts
@@ -7,7 +7,7 @@ export interface ITableEntity {
export interface ITableEntityForTablesAPI extends ITableEntity {
PartitionKey: ITableEntityAttribute;
RowKey: ITableEntityAttribute;
- Timestamp?: ITableEntityAttribute;
+ Timestamp: ITableEntityAttribute;
}
export interface ITableEntityAttribute {
diff --git a/src/Explorer/Tabs/QueryTab.html b/src/Explorer/Tabs/QueryTab.html
index e27a4bb7c..ff4b40b06 100644
--- a/src/Explorer/Tabs/QueryTab.html
+++ b/src/Explorer/Tabs/QueryTab.html
@@ -11,6 +11,17 @@
Start by writing a Mongo query, for example: {'id':'foo'} or { } to get all the
documents.
+
;
+ public maybeSubQuery: ko.Computed;
public sqlQueryEditorContent: ko.Observable;
public selectedContent: ko.Observable;
public sqlStatementToExecute: ko.Observable;
@@ -120,6 +119,11 @@ export default class QueryTab extends TabsBase implements ViewModels.WaitsForTem
return (container && (container.isPreferredApiDocumentDB() || container.isPreferredApiGraph())) || false;
});
+ this.maybeSubQuery = ko.computed(function() {
+ const sql = this.sqlQueryEditorContent();
+ return sql && /.*\(.*SELECT.*\)/i.test(sql);
+ }, this);
+
this.saveQueryButton = {
enabled: this._isSaveQueriesEnabled,
visible: this._isSaveQueriesEnabled
diff --git a/src/Explorer/Tree/AccessibleVerticalList.ts b/src/Explorer/Tree/AccessibleVerticalList.ts
index 818328a41..96db22df8 100644
--- a/src/Explorer/Tree/AccessibleVerticalList.ts
+++ b/src/Explorer/Tree/AccessibleVerticalList.ts
@@ -8,7 +8,7 @@ enum ScrollPosition {
export class AccessibleVerticalList {
private items: any[] = [];
- private onSelect: (item: any) => void;
+ private onSelect?: (item: any) => void;
public currentItemIndex: ko.Observable;
public currentItem: ko.Computed;
@@ -42,7 +42,9 @@ export class AccessibleVerticalList {
const targetElement = targetContainer
.getElementsByClassName("accessibleListElement")
.item(this.currentItemIndex());
- this.scrollElementIntoContainerViewIfNeeded(targetElement, targetContainer, ScrollPosition.Top);
+ if (targetElement) {
+ this.scrollElementIntoContainerViewIfNeeded(targetElement, targetContainer, ScrollPosition.Top);
+ }
return false;
}
if (event.keyCode === 40) {
@@ -52,7 +54,9 @@ export class AccessibleVerticalList {
const targetElement = targetContainer
.getElementsByClassName("accessibleListElement")
.item(this.currentItemIndex());
- this.scrollElementIntoContainerViewIfNeeded(targetElement, targetContainer, ScrollPosition.Bottom);
+ if (targetElement) {
+ this.scrollElementIntoContainerViewIfNeeded(targetElement, targetContainer, ScrollPosition.Top);
+ }
return false;
}
return true;
diff --git a/src/Shared/DefaultExperienceUtility.ts b/src/Shared/DefaultExperienceUtility.ts
index 81550466e..4007296b0 100644
--- a/src/Shared/DefaultExperienceUtility.ts
+++ b/src/Shared/DefaultExperienceUtility.ts
@@ -4,7 +4,7 @@ import * as DataModels from "../Contracts/DataModels";
import { DefaultAccountExperienceType } from "../DefaultAccountExperienceType";
export class DefaultExperienceUtility {
- public static getDefaultExperienceFromDatabaseAccount(databaseAccount: DataModels.DatabaseAccount): string {
+ public static getDefaultExperienceFromDatabaseAccount(databaseAccount: DataModels.DatabaseAccount): string | null {
if (!databaseAccount) {
return null;
}
@@ -81,11 +81,9 @@ export class DefaultExperienceUtility {
private static _getDefaultExperience(kind: string, capabilities: DataModels.Capability[]): string {
const defaultDefaultExperience: string = Constants.DefaultAccountExperience.DocumentDB;
- const defaultExperienceFromKind: string = DefaultExperienceUtility._getDefaultExperienceFromAccountKind(kind);
- const defaultExperienceFromCapabilities: string = DefaultExperienceUtility._getDefaultExperienceFromAccountCapabilities(
- capabilities
- );
-
+ const defaultExperienceFromKind: string = DefaultExperienceUtility._getDefaultExperienceFromAccountKind(kind) || "";
+ const defaultExperienceFromCapabilities: string =
+ DefaultExperienceUtility._getDefaultExperienceFromAccountCapabilities(capabilities) || "";
if (!!defaultExperienceFromKind) {
return defaultExperienceFromKind;
}
@@ -97,7 +95,7 @@ export class DefaultExperienceUtility {
return defaultDefaultExperience;
}
- private static _getDefaultExperienceFromAccountKind(kind: string): string {
+ private static _getDefaultExperienceFromAccountKind(kind: string): string | null {
if (!kind) {
return null;
}
@@ -113,7 +111,7 @@ export class DefaultExperienceUtility {
return null;
}
- private static _getDefaultExperienceFromAccountCapabilities(capabilities: DataModels.Capability[]): string {
+ private static _getDefaultExperienceFromAccountCapabilities(capabilities: DataModels.Capability[]): string | null {
if (!capabilities) {
return null;
}
diff --git a/src/Terminal/index.ts b/src/Terminal/index.ts
index 7b49dde8b..3a7ae8913 100644
--- a/src/Terminal/index.ts
+++ b/src/Terminal/index.ts
@@ -19,8 +19,8 @@ const getUrlVars = (): { [key: string]: string } => {
};
const createServerSettings = (urlVars: { [key: string]: string }): ServerConnection.ISettings => {
- let body: BodyInit;
- let headers: HeadersInit;
+ let body: BodyInit | undefined;
+ let headers: HeadersInit | undefined;
if (urlVars.hasOwnProperty(TerminalQueryParams.TerminalEndpoint)) {
body = JSON.stringify({
endpoint: urlVars[TerminalQueryParams.TerminalEndpoint]
diff --git a/src/Utils/arm/request.test.ts b/src/Utils/arm/request.test.ts
index 89b6d488a..6ed76093c 100644
--- a/src/Utils/arm/request.test.ts
+++ b/src/Utils/arm/request.test.ts
@@ -1,5 +1,7 @@
import { armRequest } from "./request";
import fetch from "node-fetch";
+import { updateUserContext } from "../../UserContext";
+import { AuthType } from "../../AuthType";
interface Global {
Headers: unknown;
@@ -8,6 +10,11 @@ interface Global {
((global as unknown) as Global).Headers = ((fetch as unknown) as Global).Headers;
describe("ARM request", () => {
+ window.authType = AuthType.AAD;
+ updateUserContext({
+ authorizationToken: "some-token"
+ });
+
it("should call window.fetch", async () => {
window.fetch = jest.fn().mockResolvedValue({
ok: true,
@@ -48,4 +55,24 @@ describe("ARM request", () => {
).rejects.toThrow();
expect(window.fetch).toHaveBeenCalledTimes(2);
});
+
+ it("should throw token error", async () => {
+ window.authType = AuthType.AAD;
+ updateUserContext({
+ authorizationToken: undefined
+ });
+ const headers = new Headers();
+ headers.set("location", "https://foo.com/operationStatus");
+ window.fetch = jest.fn().mockResolvedValue({
+ ok: true,
+ headers,
+ status: 200,
+ json: async () => {
+ return { status: "Failed" };
+ }
+ });
+ await expect(() =>
+ armRequest({ apiVersion: "2001-01-01", host: "https://foo.com", path: "foo", method: "GET" })
+ ).rejects.toThrow("No authority token provided");
+ });
});
diff --git a/src/Utils/arm/request.ts b/src/Utils/arm/request.ts
index 23229d46b..429f69c51 100644
--- a/src/Utils/arm/request.ts
+++ b/src/Utils/arm/request.ts
@@ -30,7 +30,7 @@ export class ARMError extends Error {
Object.setPrototypeOf(this, ARMError.prototype);
}
- public code: string | number;
+ public code?: string | number;
}
interface ARMQueryParams {
@@ -63,6 +63,10 @@ export async function armRequest({
queryParams.metricNames && url.searchParams.append("metricnames", queryParams.metricNames);
}
+ if (!userContext.authorizationToken) {
+ throw new Error("No authority token provided");
+ }
+
const response = await window.fetch(url.href, {
method,
headers: {
@@ -98,6 +102,10 @@ export async function armRequest({
}
async function getOperationStatus(operationStatusUrl: string) {
+ if (!userContext.authorizationToken) {
+ throw new Error("No authority token provided");
+ }
+
const response = await window.fetch(operationStatusUrl, {
headers: {
Authorization: userContext.authorizationToken
diff --git a/src/hostedExplorer.html b/src/hostedExplorer.html
index 6b1dfb202..07763f12d 100644
--- a/src/hostedExplorer.html
+++ b/src/hostedExplorer.html
@@ -7,6 +7,7 @@
+ Skip to content
@@ -48,16 +49,18 @@
-
-
+
+
+
+
diff --git a/tsconfig.strict.json b/tsconfig.strict.json
index c2cb50fad..2a7da5bcd 100644
--- a/tsconfig.strict.json
+++ b/tsconfig.strict.json
@@ -13,6 +13,7 @@
"./src/Common/ArrayHashMap.ts",
"./src/Common/Constants.ts",
"./src/Common/DeleteFeedback.ts",
+ "./src/Common/DocumentUtility.ts",
"./src/Common/EnvironmentUtility.ts",
"./src/Common/HashMap.ts",
"./src/Common/HeadersUtility.ts",
@@ -20,8 +21,10 @@
"./src/Common/MessageHandler.ts",
"./src/Common/MongoUtility.ts",
"./src/Common/ObjectCache.ts",
+ "./src/Common/OfferUtility.ts",
"./src/Common/ThemeUtility.ts",
"./src/Common/UrlUtility.ts",
+ "./src/Common/Splitter.ts",
"./src/ConfigContext.ts",
"./src/Contracts/ActionContracts.ts",
"./src/Contracts/DataModels.ts",
@@ -39,9 +42,15 @@
"./src/Definitions/plotly.js-cartesian-dist.d-min.ts",
"./src/Definitions/svg.d.ts",
"./src/Explorer/Controls/ErrorDisplayComponent/ErrorDisplayComponent.ts",
+ "./src/Explorer/Controls/CollapsiblePanel/CollapsiblePanelComponent.ts",
"./src/Explorer/Controls/GitHub/GitHubStyleConstants.ts",
+ "./src/Explorer/Controls/InputTypeahead/InputTypeahead.ts",
"./src/Explorer/Controls/SmartUi/InputUtils.ts",
"./src/Explorer/Graph/GraphExplorerComponent/__mocks__/GremlinClient.ts",
+ "./src/Explorer/Graph/GraphExplorerComponent/ArraysByKeyCache.ts",
+ "./src/Explorer/Graph/GraphExplorerComponent/EdgeInfoCache.ts",
+ "./src/Explorer/Graph/GraphExplorerComponent/GraphData.ts",
+ "./src/Explorer/Notebook/NotebookComponent/__mocks__/rx-jupyter.ts",
"./src/Explorer/Notebook/FileSystemUtil.ts",
"./src/Explorer/Notebook/NTeractUtil.ts",
"./src/Explorer/Notebook/NotebookComponent/actions.ts",
@@ -50,13 +59,18 @@
"./src/Explorer/Notebook/NotebookComponent/types.ts",
"./src/Explorer/Notebook/NotebookContentItem.ts",
"./src/Explorer/Notebook/NotebookUtil.ts",
+ "./src/Explorer/Tree/AccessibleVerticalList.ts",
"./src/Explorer/Panes/PaneComponents.ts",
"./src/Explorer/Panes/Tables/Validators/EntityPropertyNameValidator.ts",
"./src/Explorer/Panes/Tables/Validators/EntityPropertyValidationCommon.ts",
"./src/Explorer/Tables/Constants.ts",
"./src/Explorer/Tables/CqlUtilities.ts",
"./src/Explorer/Tables/QueryBuilder/DateTimeUtilities.ts",
+ "./src/Explorer/Tables/DataTable/CacheBase.ts",
+ "./src/Explorer/Tables/Entities.ts",
"./src/Explorer/Tabs/TabComponents.ts",
+ "./src/Explorer/Notebook/NotebookComponent/__mocks__/rx-jupyter.ts",
+ "./src/Explorer/Notebook/NotebookContentClient.ts",
"./src/GitHub/GitHubConnector.ts",
"./src/Index.ts",
"./src/NotebookWorkspaceManager/NotebookWorkspaceResourceProviderMockClients.ts",
@@ -70,6 +84,8 @@
"./src/Shared/Telemetry/TelemetryConstants.ts",
"./src/Shared/Telemetry/TelemetryProcessor.ts",
"./src/Shared/appInsights.ts",
+ "./src/Shared/DefaultExperienceUtility.ts",
+ "./src/Terminal/index.ts",
"./src/Terminal/JupyterLabAppFactory.ts",
"./src/UserContext.ts",
"./src/Utils/Base64Utils.ts",
@@ -79,6 +95,25 @@
"./src/Utils/StringUtils.ts",
"./src/Utils/WindowUtils.ts",
"./src/Utils/arm/generatedClients/2020-04-01/types.ts",
+ "./src/Utils/arm/generatedClients/2020-04-01/cassandraResources.ts",
+ "./src/Utils/arm/generatedClients/2020-04-01/collection.ts",
+ "./src/Utils/arm/generatedClients/2020-04-01/collectionPartition.ts",
+ "./src/Utils/arm/generatedClients/2020-04-01/collectionPartitionRegion.ts",
+ "./src/Utils/arm/generatedClients/2020-04-01/collectionRegion.ts",
+ "./src/Utils/arm/generatedClients/2020-04-01/database.ts",
+ "./src/Utils/arm/generatedClients/2020-04-01/databaseAccountRegion.ts",
+ "./src/Utils/arm/generatedClients/2020-04-01/databaseAccounts.ts",
+ "./src/Utils/arm/generatedClients/2020-04-01/gremlinResources.ts",
+ "./src/Utils/arm/generatedClients/2020-04-01/mongoDBResources.ts",
+ "./src/Utils/arm/generatedClients/2020-04-01/operations.ts",
+ "./src/Utils/arm/generatedClients/2020-04-01/partitionKeyRangeId.ts",
+ "./src/Utils/arm/generatedClients/2020-04-01/partitionKeyRangeIdRegion.ts",
+ "./src/Utils/arm/generatedClients/2020-04-01/percentile.ts",
+ "./src/Utils/arm/generatedClients/2020-04-01/percentileSourceTarget.ts",
+ "./src/Utils/arm/generatedClients/2020-04-01/percentileTarget.ts",
+ "./src/Utils/arm/generatedClients/2020-04-01/sqlResources.ts",
+ "./src/Utils/arm/generatedClients/2020-04-01/tableResources.ts",
+ "./src/Utils/arm/request.ts",
"./src/quickstart.ts",
"./src/setupTests.ts",
"./src/workers/upload/definitions.ts"