mirror of
https://github.com/Azure/cosmos-explorer.git
synced 2025-02-23 04:27:09 +00:00
Merge branch 'master' into users/srnara/selfserve
This commit is contained in:
commit
41f37055ef
@ -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.
|
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
|
# Contributing
|
||||||
|
|
||||||
Please read the [contribution guidelines](./CONTRIBUTING.md).
|
Please read the [contribution guidelines](./CONTRIBUTING.md).
|
||||||
|
@ -13,6 +13,11 @@
|
|||||||
@NavMediumSpace: 10px;
|
@NavMediumSpace: 10px;
|
||||||
@NavLargeSpace: 15px;
|
@NavLargeSpace: 15px;
|
||||||
|
|
||||||
|
.skip-link {
|
||||||
|
position: fixed;
|
||||||
|
top: -200px;
|
||||||
|
}
|
||||||
|
|
||||||
html {
|
html {
|
||||||
font-family: wf_segoe-ui_normal, "Segoe UI", "Segoe WP", Tahoma, Arial, sans-serif;
|
font-family: wf_segoe-ui_normal, "Segoe UI", "Segoe WP", Tahoma, Arial, sans-serif;
|
||||||
padding: 0px;
|
padding: 0px;
|
||||||
|
@ -1,20 +1,12 @@
|
|||||||
@import "./Common/Constants";
|
@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 {
|
.resourceTree {
|
||||||
height: 100%;
|
height: 100%;
|
||||||
flex: 0 0 auto;
|
flex: 0 0 auto;
|
||||||
|
.main {
|
||||||
|
height: 100%;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.resourceTreeScroll {
|
.resourceTreeScroll {
|
||||||
|
@ -133,6 +133,7 @@ export class Features {
|
|||||||
export class Flights {
|
export class Flights {
|
||||||
public static readonly SettingsV2 = "settingsv2";
|
public static readonly SettingsV2 = "settingsv2";
|
||||||
public static readonly MongoIndexEditor = "mongoindexeditor";
|
public static readonly MongoIndexEditor = "mongoindexeditor";
|
||||||
|
public static readonly MongoIndexing = "mongoindexing";
|
||||||
}
|
}
|
||||||
|
|
||||||
export class AfecFeatures {
|
export class AfecFeatures {
|
||||||
|
@ -21,7 +21,7 @@ export const handleError = (error: string | ARMError | Error, area: string, cons
|
|||||||
sendNotificationForError(errorMessage, errorCode);
|
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;
|
const errorMessage = typeof error === "string" ? error : error.message;
|
||||||
return replaceKnownError(errorMessage);
|
return replaceKnownError(errorMessage);
|
||||||
};
|
};
|
||||||
@ -45,10 +45,10 @@ const sendNotificationForError = (errorMessage: string, errorCode: number | stri
|
|||||||
const replaceKnownError = (errorMessage: string): string => {
|
const replaceKnownError = (errorMessage: string): string => {
|
||||||
if (
|
if (
|
||||||
window.dataExplorer?.subscriptionType() === SubscriptionType.Internal &&
|
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.";
|
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.";
|
return "Partition key paths must contain only valid characters and not contain a trailing slash or wildcard character.";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -2,8 +2,11 @@ import { Offer, SDKOfferDefinition } from "../Contracts/DataModels";
|
|||||||
import { OfferResponse } from "@azure/cosmos";
|
import { OfferResponse } from "@azure/cosmos";
|
||||||
import { HttpHeaders } from "./Constants";
|
import { HttpHeaders } from "./Constants";
|
||||||
|
|
||||||
export const parseSDKOfferResponse = (offerResponse: OfferResponse): Offer => {
|
export const parseSDKOfferResponse = (offerResponse: OfferResponse): Offer | undefined => {
|
||||||
const offerDefinition: SDKOfferDefinition = offerResponse?.resource;
|
const offerDefinition: SDKOfferDefinition | undefined = offerResponse?.resource;
|
||||||
|
if (!offerDefinition) {
|
||||||
|
return undefined;
|
||||||
|
}
|
||||||
const offerContent = offerDefinition.content;
|
const offerContent = offerDefinition.content;
|
||||||
if (!offerContent) {
|
if (!offerContent) {
|
||||||
return undefined;
|
return undefined;
|
||||||
@ -12,7 +15,7 @@ export const parseSDKOfferResponse = (offerResponse: OfferResponse): Offer => {
|
|||||||
const minimumThroughput = offerContent.collectionThroughputInfo?.minimumRUForCollection;
|
const minimumThroughput = offerContent.collectionThroughputInfo?.minimumRUForCollection;
|
||||||
const autopilotSettings = offerContent.offerAutopilotSettings;
|
const autopilotSettings = offerContent.offerAutopilotSettings;
|
||||||
|
|
||||||
if (autopilotSettings) {
|
if (autopilotSettings && autopilotSettings.maxThroughput && minimumThroughput) {
|
||||||
return {
|
return {
|
||||||
id: offerDefinition.id,
|
id: offerDefinition.id,
|
||||||
autoscaleMaxThroughput: autopilotSettings.maxThroughput,
|
autoscaleMaxThroughput: autopilotSettings.maxThroughput,
|
||||||
|
@ -23,10 +23,10 @@ export class Splitter {
|
|||||||
public splitterId: string;
|
public splitterId: string;
|
||||||
public leftSideId: string;
|
public leftSideId: string;
|
||||||
|
|
||||||
public splitter: HTMLElement;
|
public splitter!: HTMLElement;
|
||||||
public leftSide: HTMLElement;
|
public leftSide!: HTMLElement;
|
||||||
public lastX: number;
|
public lastX!: number;
|
||||||
public lastWidth: number;
|
public lastWidth!: number;
|
||||||
|
|
||||||
private isCollapsed: ko.Observable<boolean>;
|
private isCollapsed: ko.Observable<boolean>;
|
||||||
private bounds: SplitterBounds;
|
private bounds: SplitterBounds;
|
||||||
@ -42,9 +42,10 @@ export class Splitter {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public initialize() {
|
public initialize() {
|
||||||
this.splitter = document.getElementById(this.splitterId);
|
if (document.getElementById(this.splitterId) !== null && document.getElementById(this.leftSideId) != null) {
|
||||||
this.leftSide = document.getElementById(this.leftSideId);
|
this.splitter = <HTMLElement>document.getElementById(this.splitterId);
|
||||||
|
this.leftSide = <HTMLElement>document.getElementById(this.leftSideId);
|
||||||
|
}
|
||||||
const isVerticalSplitter: boolean = this.direction === SplitterDirection.Vertical;
|
const isVerticalSplitter: boolean = this.direction === SplitterDirection.Vertical;
|
||||||
const splitterOptions: JQueryUI.ResizableOptions = {
|
const splitterOptions: JQueryUI.ResizableOptions = {
|
||||||
animate: true,
|
animate: true,
|
||||||
|
@ -210,9 +210,9 @@ export interface QueryMetrics {
|
|||||||
|
|
||||||
export interface Offer {
|
export interface Offer {
|
||||||
id: string;
|
id: string;
|
||||||
autoscaleMaxThroughput: number;
|
autoscaleMaxThroughput: number | undefined;
|
||||||
manualThroughput: number;
|
manualThroughput: number | undefined;
|
||||||
minimumThroughput: number;
|
minimumThroughput: number | undefined;
|
||||||
offerDefinition?: SDKOfferDefinition;
|
offerDefinition?: SDKOfferDefinition;
|
||||||
offerReplacePending: boolean;
|
offerReplacePending: boolean;
|
||||||
}
|
}
|
||||||
|
@ -42,7 +42,7 @@ interface CollapsiblePanelParams {
|
|||||||
* Use the optional "collapseToLeft" parameter to collapse to the left.
|
* Use the optional "collapseToLeft" parameter to collapse to the left.
|
||||||
*/
|
*/
|
||||||
class CollapsiblePanelViewModel {
|
class CollapsiblePanelViewModel {
|
||||||
private params: CollapsiblePanelParams;
|
public params: CollapsiblePanelParams;
|
||||||
private isCollapsed: ko.Observable<boolean>;
|
private isCollapsed: ko.Observable<boolean>;
|
||||||
|
|
||||||
public constructor(params: CollapsiblePanelParams) {
|
public constructor(params: CollapsiblePanelParams) {
|
||||||
@ -50,7 +50,7 @@ class CollapsiblePanelViewModel {
|
|||||||
this.isCollapsed = params.isCollapsed || ko.observable(false);
|
this.isCollapsed = params.isCollapsed || ko.observable(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
private toggleCollapse(): void {
|
public toggleCollapse(): void {
|
||||||
this.isCollapsed(!this.isCollapsed());
|
this.isCollapsed(!this.isCollapsed());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -71,7 +71,7 @@ interface InputTypeaheadParams {
|
|||||||
/**
|
/**
|
||||||
* This function gets called when pressing ENTER on the input box
|
* 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.
|
* Typehead comes with a Search button that we normally remove.
|
||||||
@ -88,8 +88,8 @@ interface OnClickItem {
|
|||||||
}
|
}
|
||||||
|
|
||||||
interface Cache {
|
interface Cache {
|
||||||
inputValue: string;
|
inputValue: string | null;
|
||||||
selection: Item;
|
selection: Item | null;
|
||||||
}
|
}
|
||||||
|
|
||||||
class InputTypeaheadViewModel {
|
class InputTypeaheadViewModel {
|
||||||
@ -98,15 +98,12 @@ class InputTypeaheadViewModel {
|
|||||||
private params: InputTypeaheadParams;
|
private params: InputTypeaheadParams;
|
||||||
|
|
||||||
private cache: Cache;
|
private cache: Cache;
|
||||||
private inputValue: string;
|
|
||||||
private selection: Item;
|
|
||||||
|
|
||||||
public constructor(params: InputTypeaheadParams) {
|
public constructor(params: InputTypeaheadParams) {
|
||||||
this.instanceNumber = InputTypeaheadViewModel.instanceCount++;
|
this.instanceNumber = InputTypeaheadViewModel.instanceCount++;
|
||||||
this.params = params;
|
this.params = params;
|
||||||
|
|
||||||
this.params.choices.subscribe(this.initializeTypeahead.bind(this));
|
this.params.choices.subscribe(this.initializeTypeahead.bind(this));
|
||||||
|
|
||||||
this.cache = {
|
this.cache = {
|
||||||
inputValue: null,
|
inputValue: null,
|
||||||
selection: 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.
|
* Use ko's "template: afterRender" callback to do that without actually using any template.
|
||||||
* Another way is to call it within setTimeout() in constructor.
|
* Another way is to call it within setTimeout() in constructor.
|
||||||
*/
|
*/
|
||||||
private afterRender(): void {
|
public afterRender(): void {
|
||||||
this.initializeTypeahead();
|
this.initializeTypeahead();
|
||||||
}
|
}
|
||||||
|
|
||||||
private submit(): void {
|
public submit(): void {
|
||||||
if (this.params.submitFct) {
|
if (this.params.submitFct) {
|
||||||
this.params.submitFct(this.cache.inputValue, this.cache.selection);
|
this.params.submitFct(this.cache.inputValue, this.cache.selection);
|
||||||
}
|
}
|
||||||
|
@ -59,10 +59,12 @@ export class JsonEditorViewModel extends WaitsForTemplateViewModel {
|
|||||||
this.params = params;
|
this.params = params;
|
||||||
|
|
||||||
this.params.content.subscribe((newValue: string) => {
|
this.params.content.subscribe((newValue: string) => {
|
||||||
if (!!this.editor) {
|
if (newValue) {
|
||||||
this.editor.getModel().setValue(newValue);
|
if (!!this.editor) {
|
||||||
} else {
|
this.editor.getModel().setValue(newValue);
|
||||||
this.createEditor(newValue, this.configureEditor.bind(this));
|
} else {
|
||||||
|
this.createEditor(newValue, this.configureEditor.bind(this));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -231,7 +231,7 @@ describe("SettingsComponent", () => {
|
|||||||
|
|
||||||
it("getUpdatedConflictResolutionPolicy", () => {
|
it("getUpdatedConflictResolutionPolicy", () => {
|
||||||
const wrapper = shallow(<SettingsComponent {...baseProps} />);
|
const wrapper = shallow(<SettingsComponent {...baseProps} />);
|
||||||
const conflictResolutionPolicyPath = "_ts";
|
const conflictResolutionPolicyPath = "/_ts";
|
||||||
const conflictResolutionPolicyProcedure = "sample_sproc";
|
const conflictResolutionPolicyProcedure = "sample_sproc";
|
||||||
const expectSprocPath =
|
const expectSprocPath =
|
||||||
"/dbs/" + collection.databaseId + "/colls/" + collection.id() + "/sprocs/" + conflictResolutionPolicyProcedure;
|
"/dbs/" + collection.databaseId + "/colls/" + collection.id() + "/sprocs/" + conflictResolutionPolicyProcedure;
|
||||||
|
@ -138,8 +138,8 @@ export class SettingsComponent extends React.Component<SettingsComponentProps, S
|
|||||||
|
|
||||||
// Mongo container with system partition key still treat as "Fixed"
|
// Mongo container with system partition key still treat as "Fixed"
|
||||||
this.isFixedContainer =
|
this.isFixedContainer =
|
||||||
!this.collection.partitionKey ||
|
this.container.isPreferredApiMongoDB() &&
|
||||||
(this.container.isPreferredApiMongoDB() && this.collection.partitionKey.systemKey);
|
(!this.collection.partitionKey || this.collection.partitionKey.systemKey);
|
||||||
|
|
||||||
this.state = {
|
this.state = {
|
||||||
throughput: undefined,
|
throughput: undefined,
|
||||||
@ -684,7 +684,7 @@ export class SettingsComponent extends React.Component<SettingsComponentProps, S
|
|||||||
|
|
||||||
if (policy.mode === DataModels.ConflictResolutionMode.LastWriterWins) {
|
if (policy.mode === DataModels.ConflictResolutionMode.LastWriterWins) {
|
||||||
policy.conflictResolutionPath = this.state.conflictResolutionPolicyPath;
|
policy.conflictResolutionPath = this.state.conflictResolutionPolicyPath;
|
||||||
if (policy.conflictResolutionPath?.startsWith("/")) {
|
if (!policy.conflictResolutionPath?.startsWith("/")) {
|
||||||
policy.conflictResolutionPath = "/" + policy.conflictResolutionPath;
|
policy.conflictResolutionPath = "/" + policy.conflictResolutionPath;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -427,7 +427,7 @@ export class ThroughputInputAutoPilotV3Component extends React.Component<
|
|||||||
event: React.FormEvent<HTMLInputElement | HTMLTextAreaElement>,
|
event: React.FormEvent<HTMLInputElement | HTMLTextAreaElement>,
|
||||||
newValue?: string
|
newValue?: string
|
||||||
): void => {
|
): void => {
|
||||||
const newThroughput = getSanitizedInputValue(newValue, this.autoPilotInputMaxValue);
|
const newThroughput = getSanitizedInputValue(newValue);
|
||||||
this.props.onMaxAutoPilotThroughputChange(newThroughput);
|
this.props.onMaxAutoPilotThroughputChange(newThroughput);
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -435,7 +435,7 @@ export class ThroughputInputAutoPilotV3Component extends React.Component<
|
|||||||
event: React.FormEvent<HTMLInputElement | HTMLTextAreaElement>,
|
event: React.FormEvent<HTMLInputElement | HTMLTextAreaElement>,
|
||||||
newValue?: string
|
newValue?: string
|
||||||
): void => {
|
): void => {
|
||||||
const newThroughput = getSanitizedInputValue(newValue, this.throughputInputMaxValue);
|
const newThroughput = getSanitizedInputValue(newValue);
|
||||||
if (this.overrideWithAutoPilotSettings()) {
|
if (this.overrideWithAutoPilotSettings()) {
|
||||||
this.props.onMaxAutoPilotThroughputChange(newThroughput);
|
this.props.onMaxAutoPilotThroughputChange(newThroughput);
|
||||||
} else {
|
} else {
|
||||||
|
@ -101,13 +101,13 @@ export const parseConflictResolutionProcedure = (procedureFromBackEnd: string):
|
|||||||
return procedureFromBackEnd;
|
return procedureFromBackEnd;
|
||||||
};
|
};
|
||||||
|
|
||||||
export const getSanitizedInputValue = (newValueString: string, max: number): number => {
|
export const getSanitizedInputValue = (newValueString: string, max?: number): number => {
|
||||||
const newValue = parseInt(newValueString);
|
const newValue = parseInt(newValueString);
|
||||||
if (isNaN(newValue)) {
|
if (isNaN(newValue)) {
|
||||||
return zeroValue;
|
return zeroValue;
|
||||||
}
|
}
|
||||||
// make sure new value does not exceed the maximum throughput
|
// 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 => {
|
export const isDirty = (current: isDirtyTypes, baseline: isDirtyTypes): boolean => {
|
||||||
|
@ -954,6 +954,7 @@ exports[`SettingsComponent renders 1`] = `
|
|||||||
"isHostedDataExplorerEnabled": [Function],
|
"isHostedDataExplorerEnabled": [Function],
|
||||||
"isLeftPaneExpanded": [Function],
|
"isLeftPaneExpanded": [Function],
|
||||||
"isLinkInjectionEnabled": [Function],
|
"isLinkInjectionEnabled": [Function],
|
||||||
|
"isMongoIndexingEnabled": [Function],
|
||||||
"isNotebookEnabled": [Function],
|
"isNotebookEnabled": [Function],
|
||||||
"isNotebooksEnabledForAccount": [Function],
|
"isNotebooksEnabledForAccount": [Function],
|
||||||
"isNotificationConsoleExpanded": [Function],
|
"isNotificationConsoleExpanded": [Function],
|
||||||
@ -1187,11 +1188,9 @@ exports[`SettingsComponent renders 1`] = `
|
|||||||
},
|
},
|
||||||
"direction": "vertical",
|
"direction": "vertical",
|
||||||
"isCollapsed": [Function],
|
"isCollapsed": [Function],
|
||||||
"leftSide": null,
|
|
||||||
"leftSideId": "resourcetree",
|
"leftSideId": "resourcetree",
|
||||||
"onResizeStart": [Function],
|
"onResizeStart": [Function],
|
||||||
"onResizeStop": [Function],
|
"onResizeStop": [Function],
|
||||||
"splitter": null,
|
|
||||||
"splitterId": "h_splitter1",
|
"splitterId": "h_splitter1",
|
||||||
},
|
},
|
||||||
"stringInputPane": StringInputPane {
|
"stringInputPane": StringInputPane {
|
||||||
@ -2237,6 +2236,7 @@ exports[`SettingsComponent renders 1`] = `
|
|||||||
"isHostedDataExplorerEnabled": [Function],
|
"isHostedDataExplorerEnabled": [Function],
|
||||||
"isLeftPaneExpanded": [Function],
|
"isLeftPaneExpanded": [Function],
|
||||||
"isLinkInjectionEnabled": [Function],
|
"isLinkInjectionEnabled": [Function],
|
||||||
|
"isMongoIndexingEnabled": [Function],
|
||||||
"isNotebookEnabled": [Function],
|
"isNotebookEnabled": [Function],
|
||||||
"isNotebooksEnabledForAccount": [Function],
|
"isNotebooksEnabledForAccount": [Function],
|
||||||
"isNotificationConsoleExpanded": [Function],
|
"isNotificationConsoleExpanded": [Function],
|
||||||
@ -2470,11 +2470,9 @@ exports[`SettingsComponent renders 1`] = `
|
|||||||
},
|
},
|
||||||
"direction": "vertical",
|
"direction": "vertical",
|
||||||
"isCollapsed": [Function],
|
"isCollapsed": [Function],
|
||||||
"leftSide": null,
|
|
||||||
"leftSideId": "resourcetree",
|
"leftSideId": "resourcetree",
|
||||||
"onResizeStart": [Function],
|
"onResizeStart": [Function],
|
||||||
"onResizeStop": [Function],
|
"onResizeStop": [Function],
|
||||||
"splitter": null,
|
|
||||||
"splitterId": "h_splitter1",
|
"splitterId": "h_splitter1",
|
||||||
},
|
},
|
||||||
"stringInputPane": StringInputPane {
|
"stringInputPane": StringInputPane {
|
||||||
@ -3533,6 +3531,7 @@ exports[`SettingsComponent renders 1`] = `
|
|||||||
"isHostedDataExplorerEnabled": [Function],
|
"isHostedDataExplorerEnabled": [Function],
|
||||||
"isLeftPaneExpanded": [Function],
|
"isLeftPaneExpanded": [Function],
|
||||||
"isLinkInjectionEnabled": [Function],
|
"isLinkInjectionEnabled": [Function],
|
||||||
|
"isMongoIndexingEnabled": [Function],
|
||||||
"isNotebookEnabled": [Function],
|
"isNotebookEnabled": [Function],
|
||||||
"isNotebooksEnabledForAccount": [Function],
|
"isNotebooksEnabledForAccount": [Function],
|
||||||
"isNotificationConsoleExpanded": [Function],
|
"isNotificationConsoleExpanded": [Function],
|
||||||
@ -3766,11 +3765,9 @@ exports[`SettingsComponent renders 1`] = `
|
|||||||
},
|
},
|
||||||
"direction": "vertical",
|
"direction": "vertical",
|
||||||
"isCollapsed": [Function],
|
"isCollapsed": [Function],
|
||||||
"leftSide": null,
|
|
||||||
"leftSideId": "resourcetree",
|
"leftSideId": "resourcetree",
|
||||||
"onResizeStart": [Function],
|
"onResizeStart": [Function],
|
||||||
"onResizeStop": [Function],
|
"onResizeStop": [Function],
|
||||||
"splitter": null,
|
|
||||||
"splitterId": "h_splitter1",
|
"splitterId": "h_splitter1",
|
||||||
},
|
},
|
||||||
"stringInputPane": StringInputPane {
|
"stringInputPane": StringInputPane {
|
||||||
@ -4816,6 +4813,7 @@ exports[`SettingsComponent renders 1`] = `
|
|||||||
"isHostedDataExplorerEnabled": [Function],
|
"isHostedDataExplorerEnabled": [Function],
|
||||||
"isLeftPaneExpanded": [Function],
|
"isLeftPaneExpanded": [Function],
|
||||||
"isLinkInjectionEnabled": [Function],
|
"isLinkInjectionEnabled": [Function],
|
||||||
|
"isMongoIndexingEnabled": [Function],
|
||||||
"isNotebookEnabled": [Function],
|
"isNotebookEnabled": [Function],
|
||||||
"isNotebooksEnabledForAccount": [Function],
|
"isNotebooksEnabledForAccount": [Function],
|
||||||
"isNotificationConsoleExpanded": [Function],
|
"isNotificationConsoleExpanded": [Function],
|
||||||
@ -5049,11 +5047,9 @@ exports[`SettingsComponent renders 1`] = `
|
|||||||
},
|
},
|
||||||
"direction": "vertical",
|
"direction": "vertical",
|
||||||
"isCollapsed": [Function],
|
"isCollapsed": [Function],
|
||||||
"leftSide": null,
|
|
||||||
"leftSideId": "resourcetree",
|
"leftSideId": "resourcetree",
|
||||||
"onResizeStart": [Function],
|
"onResizeStart": [Function],
|
||||||
"onResizeStop": [Function],
|
"onResizeStop": [Function],
|
||||||
"splitter": null,
|
|
||||||
"splitterId": "h_splitter1",
|
"splitterId": "h_splitter1",
|
||||||
},
|
},
|
||||||
"stringInputPane": StringInputPane {
|
"stringInputPane": StringInputPane {
|
||||||
|
@ -212,6 +212,7 @@ export default class Explorer {
|
|||||||
public isCopyNotebookPaneEnabled: ko.Observable<boolean>;
|
public isCopyNotebookPaneEnabled: ko.Observable<boolean>;
|
||||||
public isHostedDataExplorerEnabled: ko.Computed<boolean>;
|
public isHostedDataExplorerEnabled: ko.Computed<boolean>;
|
||||||
public isRightPanelV2Enabled: ko.Computed<boolean>;
|
public isRightPanelV2Enabled: ko.Computed<boolean>;
|
||||||
|
public isMongoIndexingEnabled: ko.Observable<boolean>;
|
||||||
public canExceedMaximumValue: ko.Computed<boolean>;
|
public canExceedMaximumValue: ko.Computed<boolean>;
|
||||||
|
|
||||||
public shouldShowShareDialogContents: ko.Observable<boolean>;
|
public shouldShowShareDialogContents: ko.Observable<boolean>;
|
||||||
@ -409,6 +410,7 @@ export default class Explorer {
|
|||||||
this.isFeatureEnabled(Constants.Features.enableLinkInjection)
|
this.isFeatureEnabled(Constants.Features.enableLinkInjection)
|
||||||
);
|
);
|
||||||
this.isGitHubPaneEnabled = ko.observable<boolean>(false);
|
this.isGitHubPaneEnabled = ko.observable<boolean>(false);
|
||||||
|
this.isMongoIndexingEnabled = ko.observable<boolean>(false);
|
||||||
this.isPublishNotebookPaneEnabled = ko.observable<boolean>(false);
|
this.isPublishNotebookPaneEnabled = ko.observable<boolean>(false);
|
||||||
this.isCopyNotebookPaneEnabled = ko.observable<boolean>(false);
|
this.isCopyNotebookPaneEnabled = ko.observable<boolean>(false);
|
||||||
|
|
||||||
@ -1918,6 +1920,9 @@ export default class Explorer {
|
|||||||
if (!flights) {
|
if (!flights) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
if (flights.indexOf(Constants.Flights.MongoIndexing) !== -1) {
|
||||||
|
this.isMongoIndexingEnabled(true);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public findSelectedCollection(): ViewModels.Collection {
|
public findSelectedCollection(): ViewModels.Collection {
|
||||||
|
@ -11,7 +11,9 @@ export class ArraysByKeyCache<T> {
|
|||||||
|
|
||||||
public constructor(maxNbElements: number) {
|
public constructor(maxNbElements: number) {
|
||||||
this.maxNbElements = maxNbElements;
|
this.maxNbElements = maxNbElements;
|
||||||
this.clear();
|
this.keyQueue = [];
|
||||||
|
this.cache = {};
|
||||||
|
this.totalElements = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
public clear(): void {
|
public clear(): void {
|
||||||
@ -58,7 +60,7 @@ export class ArraysByKeyCache<T> {
|
|||||||
* @param startIndex
|
* @param startIndex
|
||||||
* @param pageSize
|
* @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)) {
|
if (!this.cache.hasOwnProperty(key)) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
@ -77,8 +79,10 @@ export class ArraysByKeyCache<T> {
|
|||||||
private reduceCacheSize(): void {
|
private reduceCacheSize(): void {
|
||||||
// remove an key and its array
|
// remove an key and its array
|
||||||
const oldKey = this.keyQueue.shift();
|
const oldKey = this.keyQueue.shift();
|
||||||
this.totalElements -= this.cache[oldKey].length;
|
if (oldKey) {
|
||||||
delete this.cache[oldKey];
|
this.totalElements -= this.cache[oldKey].length;
|
||||||
|
delete this.cache[oldKey];
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -413,13 +413,13 @@ export class GraphData<V extends GremlinVertex, E extends GremlinEdge> {
|
|||||||
* @param node
|
* @param node
|
||||||
* @param prop
|
* @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)) {
|
if (node.hasOwnProperty(prop)) {
|
||||||
return (node as any)[prop];
|
return (node as any)[prop];
|
||||||
}
|
}
|
||||||
|
|
||||||
// This is DocDB specific
|
// 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"];
|
return node.properties[prop][0]["value"];
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -496,7 +496,7 @@ export class GraphData<V extends GremlinVertex, E extends GremlinEdge> {
|
|||||||
* Get list of children ids of a given vertex
|
* Get list of children ids of a given vertex
|
||||||
* @param vertex
|
* @param vertex
|
||||||
*/
|
*/
|
||||||
private static getChildrenId(vertex: GremlinVertex): string[] {
|
public static getChildrenId(vertex: GremlinVertex): string[] {
|
||||||
const ids = <any>{}; // HashSet
|
const ids = <any>{}; // HashSet
|
||||||
if (vertex.hasOwnProperty("outE")) {
|
if (vertex.hasOwnProperty("outE")) {
|
||||||
let outE = vertex.outE;
|
let outE = vertex.outE;
|
||||||
|
@ -1,18 +1,17 @@
|
|||||||
import { Observable, of } from "rxjs";
|
import { Observable, of } from "rxjs";
|
||||||
import { AjaxResponse } from "rxjs/ajax";
|
import { AjaxRequest, AjaxResponse } from "rxjs/ajax";
|
||||||
import { ServerConfig } from "rx-jupyter";
|
|
||||||
|
|
||||||
let fakeAjaxResponse: AjaxResponse = {
|
let fakeAjaxResponse: AjaxResponse = {
|
||||||
originalEvent: undefined,
|
originalEvent: <Event>(<unknown>undefined),
|
||||||
xhr: new XMLHttpRequest(),
|
xhr: new XMLHttpRequest(),
|
||||||
request: null,
|
request: <AjaxRequest>(<unknown>null),
|
||||||
status: 200,
|
status: 200,
|
||||||
response: {},
|
response: {},
|
||||||
responseText: null,
|
responseText: "",
|
||||||
responseType: "json"
|
responseType: "json"
|
||||||
};
|
};
|
||||||
export const sessions = {
|
export const sessions = {
|
||||||
create: (serverConfig: ServerConfig, body: object): Observable<AjaxResponse> => of(fakeAjaxResponse),
|
create: (serverConfig: unknown, body: object): Observable<AjaxResponse> => of(fakeAjaxResponse),
|
||||||
__setResponse: (response: AjaxResponse) => {
|
__setResponse: (response: AjaxResponse) => {
|
||||||
fakeAjaxResponse = response;
|
fakeAjaxResponse = response;
|
||||||
},
|
},
|
||||||
|
@ -11,7 +11,7 @@ import { stringifyNotebook } from "@nteract/commutable";
|
|||||||
export class NotebookContentClient {
|
export class NotebookContentClient {
|
||||||
constructor(
|
constructor(
|
||||||
private notebookServerInfo: ko.Observable<DataModels.NotebookWorkspaceConnectionInfo>,
|
private notebookServerInfo: ko.Observable<DataModels.NotebookWorkspaceConnectionInfo>,
|
||||||
private notebookBasePath: ko.Observable<string>,
|
public notebookBasePath: ko.Observable<string>,
|
||||||
private contentProvider: IContentProvider
|
private contentProvider: IContentProvider
|
||||||
) {}
|
) {}
|
||||||
|
|
||||||
@ -117,8 +117,11 @@ export class NotebookContentClient {
|
|||||||
|
|
||||||
private async checkIfFilepathExists(filepath: string): Promise<boolean> {
|
private async checkIfFilepathExists(filepath: string): Promise<boolean> {
|
||||||
const parentDirPath = NotebookUtil.getParentPath(filepath);
|
const parentDirPath = NotebookUtil.getParentPath(filepath);
|
||||||
const items = await this.fetchNotebookFiles(parentDirPath);
|
if (parentDirPath) {
|
||||||
return items.some(value => FileSystemUtil.isPathEqual(value.path, filepath));
|
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 dir = xhr.response;
|
||||||
const item = NotebookUtil.createNotebookContentItem(dir.name, dir.path, dir.type);
|
const item = NotebookUtil.createNotebookContentItem(dir.name, dir.path, dir.type);
|
||||||
item.parent = parent;
|
item.parent = parent;
|
||||||
parent.children.push(item);
|
parent.children!.push(item);
|
||||||
return item;
|
return item;
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@ -225,7 +228,7 @@ export class NotebookContentClient {
|
|||||||
* Convert rx-jupyter type to our type
|
* Convert rx-jupyter type to our type
|
||||||
* @param type
|
* @param type
|
||||||
*/
|
*/
|
||||||
private static getType(type: FileType): NotebookContentItemType {
|
public static getType(type: FileType): NotebookContentItemType {
|
||||||
switch (type) {
|
switch (type) {
|
||||||
case "directory":
|
case "directory":
|
||||||
return NotebookContentItemType.Directory;
|
return NotebookContentItemType.Directory;
|
||||||
|
@ -257,7 +257,7 @@
|
|||||||
range of values and is likely to have evenly distributed access patterns.</span>
|
range of values and is likely to have evenly distributed access patterns.</span>
|
||||||
</span>
|
</span>
|
||||||
</p>
|
</p>
|
||||||
<input type="text" id="partitionKeyValue" data-test="addCollection-partitionKeyValue" aria-required="true" size="40"
|
<input type="text" id="addCollection-partitionKeyValue" data-test="addCollection-partitionKeyValue" aria-required="true" size="40"
|
||||||
class="textfontclr collid" data-bind="textInput: partitionKey,
|
class="textfontclr collid" data-bind="textInput: partitionKey,
|
||||||
attr: {
|
attr: {
|
||||||
placeholder: partitionKeyPlaceholder,
|
placeholder: partitionKeyPlaceholder,
|
||||||
|
@ -654,7 +654,7 @@ export default class AddCollectionPane extends ContextualPaneBase {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
this.shouldCreateMongoWildcardIndex = ko.observable(false);
|
this.shouldCreateMongoWildcardIndex = ko.observable(this.container.isMongoIndexingEnabled());
|
||||||
}
|
}
|
||||||
|
|
||||||
public getSharedThroughputDefault(): boolean {
|
public getSharedThroughputDefault(): boolean {
|
||||||
@ -679,6 +679,7 @@ export default class AddCollectionPane extends ContextualPaneBase {
|
|||||||
// TODO: Figure out if a database level partition split is about to happen once shared throughput read is available
|
// TODO: Figure out if a database level partition split is about to happen once shared throughput read is available
|
||||||
this.formWarnings("");
|
this.formWarnings("");
|
||||||
this.databaseCreateNewShared(this.getSharedThroughputDefault());
|
this.databaseCreateNewShared(this.getSharedThroughputDefault());
|
||||||
|
this.shouldCreateMongoWildcardIndex(this.container.isMongoIndexingEnabled());
|
||||||
if (this.isPreferredApiTable() && !databaseId) {
|
if (this.isPreferredApiTable() && !databaseId) {
|
||||||
databaseId = SharedConstants.CollectionCreation.TablesAPIDefaultDatabase;
|
databaseId = SharedConstants.CollectionCreation.TablesAPIDefaultDatabase;
|
||||||
}
|
}
|
||||||
@ -933,6 +934,8 @@ export default class AddCollectionPane extends ContextualPaneBase {
|
|||||||
this.autoPilotThroughput(AutoPilotUtils.minAutoPilotThroughput);
|
this.autoPilotThroughput(AutoPilotUtils.minAutoPilotThroughput);
|
||||||
this.sharedAutoPilotThroughput(AutoPilotUtils.minAutoPilotThroughput);
|
this.sharedAutoPilotThroughput(AutoPilotUtils.minAutoPilotThroughput);
|
||||||
|
|
||||||
|
this.shouldCreateMongoWildcardIndex = ko.observable(this.container.isMongoIndexingEnabled());
|
||||||
|
|
||||||
this.uniqueKeys([]);
|
this.uniqueKeys([]);
|
||||||
this.useIndexingForSharedThroughput(true);
|
this.useIndexingForSharedThroughput(true);
|
||||||
|
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
abstract class CacheBase<T> {
|
abstract class CacheBase<T> {
|
||||||
public data: T[];
|
public data: T[] | null;
|
||||||
public sortOrder: any;
|
public sortOrder: any;
|
||||||
public serverCallInProgress: boolean;
|
public serverCallInProgress: boolean;
|
||||||
|
|
||||||
|
@ -16,14 +16,75 @@ import * as Entities from "../Entities";
|
|||||||
import QueryTablesTab from "../../Tabs/QueryTablesTab";
|
import QueryTablesTab from "../../Tabs/QueryTablesTab";
|
||||||
import * as TableEntityProcessor from "../TableEntityProcessor";
|
import * as TableEntityProcessor from "../TableEntityProcessor";
|
||||||
import * as TelemetryProcessor from "../../../Shared/Telemetry/TelemetryProcessor";
|
import * as TelemetryProcessor from "../../../Shared/Telemetry/TelemetryProcessor";
|
||||||
import * as DataModels from "../../../Contracts/DataModels";
|
|
||||||
import * as ViewModels from "../../../Contracts/ViewModels";
|
import * as ViewModels from "../../../Contracts/ViewModels";
|
||||||
import { getErrorMessage, getErrorStack } from "../../../Common/ErrorHandlingUtils";
|
|
||||||
|
|
||||||
interface IListTableEntitiesSegmentedResult extends Entities.IListTableEntitiesResult {
|
interface IListTableEntitiesSegmentedResult extends Entities.IListTableEntitiesResult {
|
||||||
ExceedMaximumRetries?: boolean;
|
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 [<ErrorDataModel>{ 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
|
* Storage Table Entity List ViewModel
|
||||||
*/
|
*/
|
||||||
@ -387,8 +448,17 @@ export default class TableEntityListViewModel extends DataTableViewModel {
|
|||||||
}
|
}
|
||||||
})
|
})
|
||||||
.catch((error: any) => {
|
.catch((error: any) => {
|
||||||
const errorMessage = getErrorMessage(error);
|
const parsedErrors = parseError(error);
|
||||||
this.queryErrorMessage(errorMessage);
|
var errors = parsedErrors.map(error => {
|
||||||
|
return <ViewModels.QueryError>{
|
||||||
|
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) {
|
if (this.queryTablesTab.onLoadStartKey != null && this.queryTablesTab.onLoadStartKey != undefined) {
|
||||||
TelemetryProcessor.traceFailure(
|
TelemetryProcessor.traceFailure(
|
||||||
Action.Tab,
|
Action.Tab,
|
||||||
@ -399,8 +469,7 @@ export default class TableEntityListViewModel extends DataTableViewModel {
|
|||||||
defaultExperience: this.queryTablesTab.collection.container.defaultExperience(),
|
defaultExperience: this.queryTablesTab.collection.container.defaultExperience(),
|
||||||
dataExplorerArea: Areas.Tab,
|
dataExplorerArea: Areas.Tab,
|
||||||
tabTitle: this.queryTablesTab.tabTitle(),
|
tabTitle: this.queryTablesTab.tabTitle(),
|
||||||
error: errorMessage,
|
error: error
|
||||||
errorStack: getErrorStack(error)
|
|
||||||
},
|
},
|
||||||
this.queryTablesTab.onLoadStartKey
|
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.
|
* 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
|
* See Microsoft Azure API Documentation at: https://msdn.microsoft.com/en-us/library/azure/dd135718.aspx
|
||||||
*/
|
*/
|
||||||
private async prefetchData(
|
private prefetchData(
|
||||||
tableQuery: Entities.ITableQuery,
|
tableQuery: Entities.ITableQuery,
|
||||||
downloadSize: number,
|
downloadSize: number,
|
||||||
currentRetry: number = 0
|
currentRetry: number = 0
|
||||||
): Promise<IListTableEntitiesSegmentedResult> {
|
): Q.Promise<any> {
|
||||||
if (!this.cache.serverCallInProgress) {
|
if (!this.cache.serverCallInProgress) {
|
||||||
this.cache.serverCallInProgress = true;
|
this.cache.serverCallInProgress = true;
|
||||||
this.allDownloaded = false;
|
this.allDownloaded = false;
|
||||||
this.lastPrefetchTime = new Date().getTime();
|
this.lastPrefetchTime = new Date().getTime();
|
||||||
const time = this.lastPrefetchTime;
|
var time = this.lastPrefetchTime;
|
||||||
|
|
||||||
|
var promise: Q.Promise<IListTableEntitiesSegmentedResult>;
|
||||||
if (this._documentIterator && this.continuationToken) {
|
if (this._documentIterator && this.continuationToken) {
|
||||||
// TODO handle Cassandra case
|
// TODO handle Cassandra case
|
||||||
const response = await this._documentIterator.fetchNext();
|
|
||||||
const entities: Entities.ITableEntity[] = TableEntityProcessor.convertDocumentsToEntities(response?.resources);
|
|
||||||
|
|
||||||
return {
|
promise = Q(this._documentIterator.fetchNext().then(response => response.resources)).then(
|
||||||
Results: entities,
|
(documents: any[]) => {
|
||||||
ContinuationToken: this._documentIterator.hasMoreResults()
|
let entities: Entities.ITableEntity[] = TableEntityProcessor.convertDocumentsToEntities(documents);
|
||||||
};
|
let finalEntities: IListTableEntitiesSegmentedResult = <IListTableEntitiesSegmentedResult>{
|
||||||
}
|
Results: entities,
|
||||||
|
ContinuationToken: this._documentIterator.hasMoreResults()
|
||||||
try {
|
};
|
||||||
let documents: IListTableEntitiesSegmentedResult;
|
return Q.resolve(finalEntities);
|
||||||
if (this.continuationToken && this.queryTablesTab.container.isPreferredApiCassandra()) {
|
}
|
||||||
documents = await this.queryTablesTab.container.tableDataClient.queryDocuments(
|
);
|
||||||
|
} else if (this.continuationToken && this.queryTablesTab.container.isPreferredApiCassandra()) {
|
||||||
|
promise = Q(
|
||||||
|
this.queryTablesTab.container.tableDataClient.queryDocuments(
|
||||||
this.queryTablesTab.collection,
|
this.queryTablesTab.collection,
|
||||||
this.cqlQuery(),
|
this.cqlQuery(),
|
||||||
true,
|
true,
|
||||||
this.continuationToken
|
this.continuationToken
|
||||||
);
|
)
|
||||||
} else {
|
);
|
||||||
const query = this.queryTablesTab.container.isPreferredApiCassandra() ? this.cqlQuery() : this.sqlQuery();
|
} else {
|
||||||
documents = await this.queryTablesTab.container.tableDataClient.queryDocuments(
|
let query = this.sqlQuery();
|
||||||
this.queryTablesTab.collection,
|
if (this.queryTablesTab.container.isPreferredApiCassandra()) {
|
||||||
query,
|
query = this.cqlQuery();
|
||||||
true
|
}
|
||||||
);
|
promise = Q(
|
||||||
|
this.queryTablesTab.container.tableDataClient.queryDocuments(this.queryTablesTab.collection, query, true)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
return promise
|
||||||
|
.then((result: IListTableEntitiesSegmentedResult) => {
|
||||||
if (!this._documentIterator) {
|
if (!this._documentIterator) {
|
||||||
this._documentIterator = documents.iterator;
|
this._documentIterator = result.iterator;
|
||||||
}
|
}
|
||||||
var actualDownloadSize: number = 0;
|
var actualDownloadSize: number = 0;
|
||||||
|
|
||||||
@ -472,11 +547,11 @@ export default class TableEntityListViewModel extends DataTableViewModel {
|
|||||||
return Q.resolve(null);
|
return Q.resolve(null);
|
||||||
}
|
}
|
||||||
|
|
||||||
var entities = documents.Results;
|
var entities = result.Results;
|
||||||
actualDownloadSize = entities.length;
|
actualDownloadSize = entities.length;
|
||||||
|
|
||||||
// Queries can fetch no results and still return a continuation header. See prefetchAndRender() method.
|
// 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) {
|
if (!this.continuationToken) {
|
||||||
this.allDownloaded = true;
|
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.1, set prefetch exceeds maximum retry number and end prefetch.
|
||||||
// For #2.2, go to next round prefetch.
|
// For #2.2, go to next round prefetch.
|
||||||
if (this.allDownloaded || nextDownloadSize === 0) {
|
if (this.allDownloaded || nextDownloadSize === 0) {
|
||||||
return documents;
|
return Q.resolve(result);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (currentRetry >= TableEntityListViewModel._maximumNumberOfPrefetchRetries) {
|
if (currentRetry >= TableEntityListViewModel._maximumNumberOfPrefetchRetries) {
|
||||||
documents.ExceedMaximumRetries = true;
|
result.ExceedMaximumRetries = true;
|
||||||
return documents;
|
return Q.resolve(result);
|
||||||
}
|
}
|
||||||
|
return this.prefetchData(tableQuery, nextDownloadSize, currentRetry + 1);
|
||||||
return await this.prefetchData(tableQuery, nextDownloadSize, currentRetry + 1);
|
})
|
||||||
}
|
.catch((error: Error) => {
|
||||||
} catch (error) {
|
this.cache.serverCallInProgress = false;
|
||||||
this.cache.serverCallInProgress = false;
|
return Q.reject(error);
|
||||||
throw error;
|
});
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
return null;
|
||||||
return undefined;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -7,7 +7,7 @@ export interface ITableEntity {
|
|||||||
export interface ITableEntityForTablesAPI extends ITableEntity {
|
export interface ITableEntityForTablesAPI extends ITableEntity {
|
||||||
PartitionKey: ITableEntityAttribute;
|
PartitionKey: ITableEntityAttribute;
|
||||||
RowKey: ITableEntityAttribute;
|
RowKey: ITableEntityAttribute;
|
||||||
Timestamp?: ITableEntityAttribute;
|
Timestamp: ITableEntityAttribute;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface ITableEntityAttribute {
|
export interface ITableEntityAttribute {
|
||||||
|
@ -11,6 +11,17 @@
|
|||||||
Start by writing a Mongo query, for example: <strong>{'id':'foo'}</strong> or <strong>{ }</strong> to get all the
|
Start by writing a Mongo query, for example: <strong>{'id':'foo'}</strong> or <strong>{ }</strong> to get all the
|
||||||
documents.
|
documents.
|
||||||
</div>
|
</div>
|
||||||
|
<div class="warningErrorContainer" aria-live="assertive" data-bind="visible: maybeSubQuery">
|
||||||
|
<div class="warningErrorContent">
|
||||||
|
<span><img class="paneErrorIcon" src="/info_color.svg" alt="Error"/></span>
|
||||||
|
<span class="warningErrorDetailsLinkContainer">
|
||||||
|
We have detected you may be using a subquery. Non-correlated subqueries are not currently supported.
|
||||||
|
<a href="https://docs.microsoft.com/en-us/azure/cosmos-db/sql-query-subquery"
|
||||||
|
>Please see Cosmos sub query documentation for further information</a
|
||||||
|
>
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
<div class="queryEditorWithSplitter" data-bind="attr: { id: queryEditorId }">
|
<div class="queryEditorWithSplitter" data-bind="attr: { id: queryEditorId }">
|
||||||
<editor
|
<editor
|
||||||
class="queryEditor"
|
class="queryEditor"
|
||||||
|
@ -1,5 +1,4 @@
|
|||||||
import * as ko from "knockout";
|
import * as ko from "knockout";
|
||||||
import Q from "q";
|
|
||||||
import * as Constants from "../../Common/Constants";
|
import * as Constants from "../../Common/Constants";
|
||||||
import * as DataModels from "../../Contracts/DataModels";
|
import * as DataModels from "../../Contracts/DataModels";
|
||||||
import * as ViewModels from "../../Contracts/ViewModels";
|
import * as ViewModels from "../../Contracts/ViewModels";
|
||||||
@ -7,7 +6,6 @@ import { Action } from "../../Shared/Telemetry/TelemetryConstants";
|
|||||||
import TabsBase from "./TabsBase";
|
import TabsBase from "./TabsBase";
|
||||||
import { HashMap } from "../../Common/HashMap";
|
import { HashMap } from "../../Common/HashMap";
|
||||||
import * as HeadersUtility from "../../Common/HeadersUtility";
|
import * as HeadersUtility from "../../Common/HeadersUtility";
|
||||||
import * as Logger from "../../Common/Logger";
|
|
||||||
import { Splitter, SplitterBounds, SplitterDirection } from "../../Common/Splitter";
|
import { Splitter, SplitterBounds, SplitterDirection } from "../../Common/Splitter";
|
||||||
import * as TelemetryProcessor from "../../Shared/Telemetry/TelemetryProcessor";
|
import * as TelemetryProcessor from "../../Shared/Telemetry/TelemetryProcessor";
|
||||||
import ExecuteQueryIcon from "../../../images/ExecuteQuery.svg";
|
import ExecuteQueryIcon from "../../../images/ExecuteQuery.svg";
|
||||||
@ -31,6 +29,7 @@ export default class QueryTab extends TabsBase implements ViewModels.WaitsForTem
|
|||||||
public fetchNextPageButton: ViewModels.Button;
|
public fetchNextPageButton: ViewModels.Button;
|
||||||
public saveQueryButton: ViewModels.Button;
|
public saveQueryButton: ViewModels.Button;
|
||||||
public initialEditorContent: ko.Observable<string>;
|
public initialEditorContent: ko.Observable<string>;
|
||||||
|
public maybeSubQuery: ko.Computed<boolean>;
|
||||||
public sqlQueryEditorContent: ko.Observable<string>;
|
public sqlQueryEditorContent: ko.Observable<string>;
|
||||||
public selectedContent: ko.Observable<string>;
|
public selectedContent: ko.Observable<string>;
|
||||||
public sqlStatementToExecute: ko.Observable<string>;
|
public sqlStatementToExecute: ko.Observable<string>;
|
||||||
@ -120,6 +119,11 @@ export default class QueryTab extends TabsBase implements ViewModels.WaitsForTem
|
|||||||
return (container && (container.isPreferredApiDocumentDB() || container.isPreferredApiGraph())) || false;
|
return (container && (container.isPreferredApiDocumentDB() || container.isPreferredApiGraph())) || false;
|
||||||
});
|
});
|
||||||
|
|
||||||
|
this.maybeSubQuery = ko.computed<boolean>(function() {
|
||||||
|
const sql = this.sqlQueryEditorContent();
|
||||||
|
return sql && /.*\(.*SELECT.*\)/i.test(sql);
|
||||||
|
}, this);
|
||||||
|
|
||||||
this.saveQueryButton = {
|
this.saveQueryButton = {
|
||||||
enabled: this._isSaveQueriesEnabled,
|
enabled: this._isSaveQueriesEnabled,
|
||||||
visible: this._isSaveQueriesEnabled
|
visible: this._isSaveQueriesEnabled
|
||||||
|
@ -8,7 +8,7 @@ enum ScrollPosition {
|
|||||||
|
|
||||||
export class AccessibleVerticalList {
|
export class AccessibleVerticalList {
|
||||||
private items: any[] = [];
|
private items: any[] = [];
|
||||||
private onSelect: (item: any) => void;
|
private onSelect?: (item: any) => void;
|
||||||
|
|
||||||
public currentItemIndex: ko.Observable<number>;
|
public currentItemIndex: ko.Observable<number>;
|
||||||
public currentItem: ko.Computed<any>;
|
public currentItem: ko.Computed<any>;
|
||||||
@ -42,7 +42,9 @@ export class AccessibleVerticalList {
|
|||||||
const targetElement = targetContainer
|
const targetElement = targetContainer
|
||||||
.getElementsByClassName("accessibleListElement")
|
.getElementsByClassName("accessibleListElement")
|
||||||
.item(this.currentItemIndex());
|
.item(this.currentItemIndex());
|
||||||
this.scrollElementIntoContainerViewIfNeeded(targetElement, targetContainer, ScrollPosition.Top);
|
if (targetElement) {
|
||||||
|
this.scrollElementIntoContainerViewIfNeeded(targetElement, targetContainer, ScrollPosition.Top);
|
||||||
|
}
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
if (event.keyCode === 40) {
|
if (event.keyCode === 40) {
|
||||||
@ -52,7 +54,9 @@ export class AccessibleVerticalList {
|
|||||||
const targetElement = targetContainer
|
const targetElement = targetContainer
|
||||||
.getElementsByClassName("accessibleListElement")
|
.getElementsByClassName("accessibleListElement")
|
||||||
.item(this.currentItemIndex());
|
.item(this.currentItemIndex());
|
||||||
this.scrollElementIntoContainerViewIfNeeded(targetElement, targetContainer, ScrollPosition.Bottom);
|
if (targetElement) {
|
||||||
|
this.scrollElementIntoContainerViewIfNeeded(targetElement, targetContainer, ScrollPosition.Top);
|
||||||
|
}
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
|
@ -4,7 +4,7 @@ import * as DataModels from "../Contracts/DataModels";
|
|||||||
import { DefaultAccountExperienceType } from "../DefaultAccountExperienceType";
|
import { DefaultAccountExperienceType } from "../DefaultAccountExperienceType";
|
||||||
|
|
||||||
export class DefaultExperienceUtility {
|
export class DefaultExperienceUtility {
|
||||||
public static getDefaultExperienceFromDatabaseAccount(databaseAccount: DataModels.DatabaseAccount): string {
|
public static getDefaultExperienceFromDatabaseAccount(databaseAccount: DataModels.DatabaseAccount): string | null {
|
||||||
if (!databaseAccount) {
|
if (!databaseAccount) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
@ -81,11 +81,9 @@ export class DefaultExperienceUtility {
|
|||||||
|
|
||||||
private static _getDefaultExperience(kind: string, capabilities: DataModels.Capability[]): string {
|
private static _getDefaultExperience(kind: string, capabilities: DataModels.Capability[]): string {
|
||||||
const defaultDefaultExperience: string = Constants.DefaultAccountExperience.DocumentDB;
|
const defaultDefaultExperience: string = Constants.DefaultAccountExperience.DocumentDB;
|
||||||
const defaultExperienceFromKind: string = DefaultExperienceUtility._getDefaultExperienceFromAccountKind(kind);
|
const defaultExperienceFromKind: string = DefaultExperienceUtility._getDefaultExperienceFromAccountKind(kind) || "";
|
||||||
const defaultExperienceFromCapabilities: string = DefaultExperienceUtility._getDefaultExperienceFromAccountCapabilities(
|
const defaultExperienceFromCapabilities: string =
|
||||||
capabilities
|
DefaultExperienceUtility._getDefaultExperienceFromAccountCapabilities(capabilities) || "";
|
||||||
);
|
|
||||||
|
|
||||||
if (!!defaultExperienceFromKind) {
|
if (!!defaultExperienceFromKind) {
|
||||||
return defaultExperienceFromKind;
|
return defaultExperienceFromKind;
|
||||||
}
|
}
|
||||||
@ -97,7 +95,7 @@ export class DefaultExperienceUtility {
|
|||||||
return defaultDefaultExperience;
|
return defaultDefaultExperience;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static _getDefaultExperienceFromAccountKind(kind: string): string {
|
private static _getDefaultExperienceFromAccountKind(kind: string): string | null {
|
||||||
if (!kind) {
|
if (!kind) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
@ -113,7 +111,7 @@ export class DefaultExperienceUtility {
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static _getDefaultExperienceFromAccountCapabilities(capabilities: DataModels.Capability[]): string {
|
private static _getDefaultExperienceFromAccountCapabilities(capabilities: DataModels.Capability[]): string | null {
|
||||||
if (!capabilities) {
|
if (!capabilities) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
@ -19,8 +19,8 @@ const getUrlVars = (): { [key: string]: string } => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
const createServerSettings = (urlVars: { [key: string]: string }): ServerConnection.ISettings => {
|
const createServerSettings = (urlVars: { [key: string]: string }): ServerConnection.ISettings => {
|
||||||
let body: BodyInit;
|
let body: BodyInit | undefined;
|
||||||
let headers: HeadersInit;
|
let headers: HeadersInit | undefined;
|
||||||
if (urlVars.hasOwnProperty(TerminalQueryParams.TerminalEndpoint)) {
|
if (urlVars.hasOwnProperty(TerminalQueryParams.TerminalEndpoint)) {
|
||||||
body = JSON.stringify({
|
body = JSON.stringify({
|
||||||
endpoint: urlVars[TerminalQueryParams.TerminalEndpoint]
|
endpoint: urlVars[TerminalQueryParams.TerminalEndpoint]
|
||||||
|
@ -1,5 +1,7 @@
|
|||||||
import { armRequest } from "./request";
|
import { armRequest } from "./request";
|
||||||
import fetch from "node-fetch";
|
import fetch from "node-fetch";
|
||||||
|
import { updateUserContext } from "../../UserContext";
|
||||||
|
import { AuthType } from "../../AuthType";
|
||||||
|
|
||||||
interface Global {
|
interface Global {
|
||||||
Headers: unknown;
|
Headers: unknown;
|
||||||
@ -8,6 +10,11 @@ interface Global {
|
|||||||
((global as unknown) as Global).Headers = ((fetch as unknown) as Global).Headers;
|
((global as unknown) as Global).Headers = ((fetch as unknown) as Global).Headers;
|
||||||
|
|
||||||
describe("ARM request", () => {
|
describe("ARM request", () => {
|
||||||
|
window.authType = AuthType.AAD;
|
||||||
|
updateUserContext({
|
||||||
|
authorizationToken: "some-token"
|
||||||
|
});
|
||||||
|
|
||||||
it("should call window.fetch", async () => {
|
it("should call window.fetch", async () => {
|
||||||
window.fetch = jest.fn().mockResolvedValue({
|
window.fetch = jest.fn().mockResolvedValue({
|
||||||
ok: true,
|
ok: true,
|
||||||
@ -48,4 +55,24 @@ describe("ARM request", () => {
|
|||||||
).rejects.toThrow();
|
).rejects.toThrow();
|
||||||
expect(window.fetch).toHaveBeenCalledTimes(2);
|
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");
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
@ -30,7 +30,7 @@ export class ARMError extends Error {
|
|||||||
Object.setPrototypeOf(this, ARMError.prototype);
|
Object.setPrototypeOf(this, ARMError.prototype);
|
||||||
}
|
}
|
||||||
|
|
||||||
public code: string | number;
|
public code?: string | number;
|
||||||
}
|
}
|
||||||
|
|
||||||
interface ARMQueryParams {
|
interface ARMQueryParams {
|
||||||
@ -63,6 +63,10 @@ export async function armRequest<T>({
|
|||||||
queryParams.metricNames && url.searchParams.append("metricnames", queryParams.metricNames);
|
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, {
|
const response = await window.fetch(url.href, {
|
||||||
method,
|
method,
|
||||||
headers: {
|
headers: {
|
||||||
@ -98,6 +102,10 @@ export async function armRequest<T>({
|
|||||||
}
|
}
|
||||||
|
|
||||||
async function getOperationStatus(operationStatusUrl: string) {
|
async function getOperationStatus(operationStatusUrl: string) {
|
||||||
|
if (!userContext.authorizationToken) {
|
||||||
|
throw new Error("No authority token provided");
|
||||||
|
}
|
||||||
|
|
||||||
const response = await window.fetch(operationStatusUrl, {
|
const response = await window.fetch(operationStatusUrl, {
|
||||||
headers: {
|
headers: {
|
||||||
Authorization: userContext.authorizationToken
|
Authorization: userContext.authorizationToken
|
||||||
|
@ -7,6 +7,7 @@
|
|||||||
</head>
|
</head>
|
||||||
|
|
||||||
<body>
|
<body>
|
||||||
|
<a class="skip-link" href="#data-explorer-content">Skip to content</a>
|
||||||
<header>
|
<header>
|
||||||
<div class="items" role="menubar">
|
<div class="items" role="menubar">
|
||||||
<div class="cosmosDBTitle">
|
<div class="cosmosDBTitle">
|
||||||
@ -48,16 +49,18 @@
|
|||||||
|
|
||||||
<switch-directory-pane params="{data: switchDirectoryPane}"></switch-directory-pane>
|
<switch-directory-pane params="{data: switchDirectoryPane}"></switch-directory-pane>
|
||||||
|
|
||||||
<!-- TODO generate version number dynamically -->
|
<div id="data-explorer-content">
|
||||||
<iframe
|
<!-- TODO generate version number dynamically -->
|
||||||
id="explorerMenu"
|
<iframe
|
||||||
name="explorer"
|
id="explorerMenu"
|
||||||
class="iframe"
|
name="explorer"
|
||||||
title="explorer"
|
class="iframe"
|
||||||
src="explorer.html?v=1.0.1&platform=Hosted"
|
title="explorer"
|
||||||
data-bind="visible: navigationSelection() === 'explorer'"
|
src="explorer.html?v=1.0.1&platform=Hosted"
|
||||||
>
|
data-bind="visible: navigationSelection() === 'explorer'"
|
||||||
</iframe>
|
>
|
||||||
|
</iframe>
|
||||||
|
</div>
|
||||||
|
|
||||||
<div data-bind="react: firewallWarningComponentAdapter"></div>
|
<div data-bind="react: firewallWarningComponentAdapter"></div>
|
||||||
<div data-bind="react: dialogComponentAdapter"></div>
|
<div data-bind="react: dialogComponentAdapter"></div>
|
||||||
|
@ -13,6 +13,7 @@
|
|||||||
"./src/Common/ArrayHashMap.ts",
|
"./src/Common/ArrayHashMap.ts",
|
||||||
"./src/Common/Constants.ts",
|
"./src/Common/Constants.ts",
|
||||||
"./src/Common/DeleteFeedback.ts",
|
"./src/Common/DeleteFeedback.ts",
|
||||||
|
"./src/Common/DocumentUtility.ts",
|
||||||
"./src/Common/EnvironmentUtility.ts",
|
"./src/Common/EnvironmentUtility.ts",
|
||||||
"./src/Common/HashMap.ts",
|
"./src/Common/HashMap.ts",
|
||||||
"./src/Common/HeadersUtility.ts",
|
"./src/Common/HeadersUtility.ts",
|
||||||
@ -20,8 +21,10 @@
|
|||||||
"./src/Common/MessageHandler.ts",
|
"./src/Common/MessageHandler.ts",
|
||||||
"./src/Common/MongoUtility.ts",
|
"./src/Common/MongoUtility.ts",
|
||||||
"./src/Common/ObjectCache.ts",
|
"./src/Common/ObjectCache.ts",
|
||||||
|
"./src/Common/OfferUtility.ts",
|
||||||
"./src/Common/ThemeUtility.ts",
|
"./src/Common/ThemeUtility.ts",
|
||||||
"./src/Common/UrlUtility.ts",
|
"./src/Common/UrlUtility.ts",
|
||||||
|
"./src/Common/Splitter.ts",
|
||||||
"./src/ConfigContext.ts",
|
"./src/ConfigContext.ts",
|
||||||
"./src/Contracts/ActionContracts.ts",
|
"./src/Contracts/ActionContracts.ts",
|
||||||
"./src/Contracts/DataModels.ts",
|
"./src/Contracts/DataModels.ts",
|
||||||
@ -39,9 +42,15 @@
|
|||||||
"./src/Definitions/plotly.js-cartesian-dist.d-min.ts",
|
"./src/Definitions/plotly.js-cartesian-dist.d-min.ts",
|
||||||
"./src/Definitions/svg.d.ts",
|
"./src/Definitions/svg.d.ts",
|
||||||
"./src/Explorer/Controls/ErrorDisplayComponent/ErrorDisplayComponent.ts",
|
"./src/Explorer/Controls/ErrorDisplayComponent/ErrorDisplayComponent.ts",
|
||||||
|
"./src/Explorer/Controls/CollapsiblePanel/CollapsiblePanelComponent.ts",
|
||||||
"./src/Explorer/Controls/GitHub/GitHubStyleConstants.ts",
|
"./src/Explorer/Controls/GitHub/GitHubStyleConstants.ts",
|
||||||
|
"./src/Explorer/Controls/InputTypeahead/InputTypeahead.ts",
|
||||||
"./src/Explorer/Controls/SmartUi/InputUtils.ts",
|
"./src/Explorer/Controls/SmartUi/InputUtils.ts",
|
||||||
"./src/Explorer/Graph/GraphExplorerComponent/__mocks__/GremlinClient.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/FileSystemUtil.ts",
|
||||||
"./src/Explorer/Notebook/NTeractUtil.ts",
|
"./src/Explorer/Notebook/NTeractUtil.ts",
|
||||||
"./src/Explorer/Notebook/NotebookComponent/actions.ts",
|
"./src/Explorer/Notebook/NotebookComponent/actions.ts",
|
||||||
@ -50,13 +59,18 @@
|
|||||||
"./src/Explorer/Notebook/NotebookComponent/types.ts",
|
"./src/Explorer/Notebook/NotebookComponent/types.ts",
|
||||||
"./src/Explorer/Notebook/NotebookContentItem.ts",
|
"./src/Explorer/Notebook/NotebookContentItem.ts",
|
||||||
"./src/Explorer/Notebook/NotebookUtil.ts",
|
"./src/Explorer/Notebook/NotebookUtil.ts",
|
||||||
|
"./src/Explorer/Tree/AccessibleVerticalList.ts",
|
||||||
"./src/Explorer/Panes/PaneComponents.ts",
|
"./src/Explorer/Panes/PaneComponents.ts",
|
||||||
"./src/Explorer/Panes/Tables/Validators/EntityPropertyNameValidator.ts",
|
"./src/Explorer/Panes/Tables/Validators/EntityPropertyNameValidator.ts",
|
||||||
"./src/Explorer/Panes/Tables/Validators/EntityPropertyValidationCommon.ts",
|
"./src/Explorer/Panes/Tables/Validators/EntityPropertyValidationCommon.ts",
|
||||||
"./src/Explorer/Tables/Constants.ts",
|
"./src/Explorer/Tables/Constants.ts",
|
||||||
"./src/Explorer/Tables/CqlUtilities.ts",
|
"./src/Explorer/Tables/CqlUtilities.ts",
|
||||||
"./src/Explorer/Tables/QueryBuilder/DateTimeUtilities.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/Tabs/TabComponents.ts",
|
||||||
|
"./src/Explorer/Notebook/NotebookComponent/__mocks__/rx-jupyter.ts",
|
||||||
|
"./src/Explorer/Notebook/NotebookContentClient.ts",
|
||||||
"./src/GitHub/GitHubConnector.ts",
|
"./src/GitHub/GitHubConnector.ts",
|
||||||
"./src/Index.ts",
|
"./src/Index.ts",
|
||||||
"./src/NotebookWorkspaceManager/NotebookWorkspaceResourceProviderMockClients.ts",
|
"./src/NotebookWorkspaceManager/NotebookWorkspaceResourceProviderMockClients.ts",
|
||||||
@ -70,6 +84,8 @@
|
|||||||
"./src/Shared/Telemetry/TelemetryConstants.ts",
|
"./src/Shared/Telemetry/TelemetryConstants.ts",
|
||||||
"./src/Shared/Telemetry/TelemetryProcessor.ts",
|
"./src/Shared/Telemetry/TelemetryProcessor.ts",
|
||||||
"./src/Shared/appInsights.ts",
|
"./src/Shared/appInsights.ts",
|
||||||
|
"./src/Shared/DefaultExperienceUtility.ts",
|
||||||
|
"./src/Terminal/index.ts",
|
||||||
"./src/Terminal/JupyterLabAppFactory.ts",
|
"./src/Terminal/JupyterLabAppFactory.ts",
|
||||||
"./src/UserContext.ts",
|
"./src/UserContext.ts",
|
||||||
"./src/Utils/Base64Utils.ts",
|
"./src/Utils/Base64Utils.ts",
|
||||||
@ -79,6 +95,25 @@
|
|||||||
"./src/Utils/StringUtils.ts",
|
"./src/Utils/StringUtils.ts",
|
||||||
"./src/Utils/WindowUtils.ts",
|
"./src/Utils/WindowUtils.ts",
|
||||||
"./src/Utils/arm/generatedClients/2020-04-01/types.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/quickstart.ts",
|
||||||
"./src/setupTests.ts",
|
"./src/setupTests.ts",
|
||||||
"./src/workers/upload/definitions.ts"
|
"./src/workers/upload/definitions.ts"
|
||||||
|
Loading…
x
Reference in New Issue
Block a user