Compare commits

..

2 Commits

Author SHA1 Message Date
Steve Faulkner
2752d6af00 Create Cassandra Keyspace via ARM (#142) 2020-08-13 13:26:52 -05:00
Laurent Nguyen
cb5fe5316e Fix handling numeric partition keys (#113)
* Fix pk extraction from documentId in g.V() case

* Add unit test, cleanup pk related unit tests
2020-08-13 12:00:11 +02:00
42 changed files with 720 additions and 489 deletions

View File

@@ -0,0 +1,46 @@
import "jquery";
import * as Q from "q";
import * as DataModels from "../Contracts/DataModels";
import * as ViewModels from "../Contracts/ViewModels";
import { getAuthorizationHeader } from "../Utils/AuthorizationUtils";
import { userContext } from "../UserContext";
export class NotificationsClientBase {
private _extensionEndpoint: string;
private _notificationsApiSuffix: string;
protected constructor(notificationsApiSuffix: string) {
this._notificationsApiSuffix = notificationsApiSuffix;
}
public fetchNotifications(): Q.Promise<DataModels.Notification[]> {
const deferred: Q.Deferred<DataModels.Notification[]> = Q.defer<DataModels.Notification[]>();
const databaseAccount = userContext.databaseAccount;
const subscriptionId = userContext.subscriptionId;
const resourceGroup = userContext.resourceGroup;
const url = `${this._extensionEndpoint}${this._notificationsApiSuffix}?accountName=${databaseAccount.name}&subscriptionId=${subscriptionId}&resourceGroup=${resourceGroup}`;
const authorizationHeader: ViewModels.AuthorizationTokenHeaderMetadata = getAuthorizationHeader();
const headers: any = {};
headers[authorizationHeader.header] = authorizationHeader.token;
$.ajax({
url: url,
type: "GET",
headers: headers,
cache: false
}).then(
(notifications: DataModels.Notification[], textStatus: string, xhr: JQueryXHR<any>) => {
deferred.resolve(notifications);
},
(xhr: JQueryXHR<any>, textStatus: string, error: any) => {
deferred.reject(xhr.responseText);
}
);
return deferred.promise;
}
public setExtensionEndpoint(extensionEndpoint: string): void {
this._extensionEndpoint = extensionEndpoint;
}
}

View File

@@ -82,6 +82,7 @@ import { toRawContentUri, fromContentUri } from "../Utils/GitHubUtils";
import UserDefinedFunction from "./Tree/UserDefinedFunction";
import StoredProcedure from "./Tree/StoredProcedure";
import Trigger from "./Tree/Trigger";
import { NotificationsClientBase } from "../Common/NotificationsClientBase";
import { ContextualPaneBase } from "./Panes/ContextualPaneBase";
import TabsBase from "./Tabs/TabsBase";
import { CommandButtonComponentProps } from "./Controls/CommandButton/CommandButtonComponent";
@@ -97,6 +98,7 @@ enum ShareAccessToggleState {
}
interface ExplorerOptions {
notificationsClient: NotificationsClientBase;
isEmulator: boolean;
}
interface AdHocAccessData {
@@ -131,6 +133,7 @@ export default class Explorer {
public isPreferredApiTable: ko.Computed<boolean>;
public isFixedCollectionWithSharedThroughputSupported: ko.Computed<boolean>;
public isServerlessEnabled: ko.Computed<boolean>;
public isEmulator: boolean;
public isAccountReady: ko.Observable<boolean>;
public canSaveQueries: ko.Computed<boolean>;
public features: ko.Observable<any>;
@@ -138,6 +141,7 @@ export default class Explorer {
public extensionEndpoint: ko.Observable<string>;
public armEndpoint: ko.Observable<string>;
public isTryCosmosDBSubscription: ko.Observable<boolean>;
public notificationsClient: NotificationsClientBase;
public queriesClient: QueriesClient;
public tableDataClient: TableDataClient;
public splitter: Splitter;
@@ -265,7 +269,7 @@ export default class Explorer {
private static readonly MaxNbDatabasesToAutoExpand = 5;
constructor() {
constructor(options: ExplorerOptions) {
const startKey: number = TelemetryProcessor.traceStart(Action.InitializeDataExplorer, {
dataExplorerArea: Constants.Areas.ResourceTree
});
@@ -371,6 +375,8 @@ export default class Explorer {
}
});
this.memoryUsageInfo = ko.observable<DataModels.MemoryUsageInfo>();
this.notificationsClient = options.notificationsClient;
this.isEmulator = options.isEmulator;
this.features = ko.observable();
this.serverId = ko.observable<string>();
@@ -1933,6 +1939,7 @@ export default class Explorer {
this.serverId(inputs.serverId);
this.extensionEndpoint(inputs.extensionEndpoint || "");
this.armEndpoint(EnvironmentUtility.normalizeArmEndpointUri(inputs.csmEndpoint || configContext.ARM_ENDPOINT));
this.notificationsClient.setExtensionEndpoint(this.extensionEndpoint());
this.databaseAccount(databaseAccount);
this.subscriptionType(inputs.subscriptionType);
this.quotaId(inputs.quotaId);

View File

@@ -87,13 +87,26 @@ describe("getPkIdFromDocumentId", () => {
expect(GraphExplorer.getPkIdFromDocumentId(doc, "mypk")).toEqual("['pkvalue', 'id']");
});
it("should create pkid pair from partitioned graph (pk as number)", () => {
const doc = createFakeDoc({ id: "id", mypk: 234 });
expect(GraphExplorer.getPkIdFromDocumentId(doc, "mypk")).toEqual("[234, 'id']");
});
it("should create pkid pair from partitioned graph (pk as valid array value)", () => {
const doc = createFakeDoc({ id: "id", mypk: [{ id: "someid", _value: "pkvalue" }] });
expect(GraphExplorer.getPkIdFromDocumentId(doc, "mypk")).toEqual("['pkvalue', 'id']");
});
it("should error if id is not a string", () => {
const doc = createFakeDoc({ id: { foo: 1 } });
it("should error if id is not a string or number", () => {
let doc = createFakeDoc({ id: { foo: 1 } });
try {
GraphExplorer.getPkIdFromDocumentId(doc, undefined);
expect(true).toBe(false);
} catch (e) {
expect(true).toBe(true);
}
doc = createFakeDoc({ id: true });
try {
GraphExplorer.getPkIdFromDocumentId(doc, undefined);
expect(true).toBe(false);
@@ -102,16 +115,8 @@ describe("getPkIdFromDocumentId", () => {
}
});
it("should error if pk not string nor non-empty array", () => {
let doc = createFakeDoc({ mypk: { foo: 1 } });
try {
GraphExplorer.getPkIdFromDocumentId(doc, "mypk");
} catch (e) {
expect(true).toBe(true);
}
doc = createFakeDoc({ mypk: [] });
it("should error if pk is empty array", () => {
let doc = createFakeDoc({ mypk: [] });
try {
GraphExplorer.getPkIdFromDocumentId(doc, "mypk");
expect(true).toBe(false);

View File

@@ -1371,7 +1371,7 @@ export class GraphExplorer extends React.Component<GraphExplorerProps, GraphExpl
if (collectionPartitionKeyProperty && d.hasOwnProperty(collectionPartitionKeyProperty)) {
let pk = (d as any)[collectionPartitionKeyProperty];
if (typeof pk !== "string") {
if (typeof pk !== "string" && typeof pk !== "number") {
if (Array.isArray(pk) && pk.length > 0) {
// pk is [{ id: 'id', _value: 'value' }]
pk = pk[0]["_value"];

View File

@@ -194,7 +194,7 @@ export class CommandBarComponentButtonFactory {
buttons.push(fullScreenButton);
}
if (configContext.platform !== Platform.Emulator) {
if (!container.hasOwnProperty("isEmulator") || !container.isEmulator) {
const label = "Feedback";
const feedbackButtonOptions: CommandButtonComponentProps = {
iconSrc: FeedbackIcon,

View File

@@ -40,7 +40,7 @@ describe("Add Collection Pane", () => {
};
beforeEach(() => {
explorer = new Explorer();
explorer = new Explorer({ notificationsClient: null, isEmulator: false });
explorer.hasAutoPilotV2FeatureFlag = ko.computed<boolean>(() => true);
});

View File

@@ -329,7 +329,7 @@ export default class AddCollectionPane extends ContextualPaneBase {
this.canRequestSupport = ko.pureComputed(() => {
if (
configContext.platform !== Platform.Emulator &&
!this.container.isEmulator &&
!this.container.isTryCosmosDBSubscription() &&
this.container.getPlatformType() !== PlatformType.Portal
) {
@@ -341,7 +341,7 @@ export default class AddCollectionPane extends ContextualPaneBase {
});
this.costsVisible = ko.pureComputed(() => {
return configContext.platform !== Platform.Emulator;
return !this.container.isEmulator;
});
this.maxCollectionsReached = ko.computed<boolean>(() => {

View File

@@ -40,7 +40,10 @@ describe("Add Database Pane", () => {
};
beforeEach(() => {
explorer = new Explorer();
explorer = new Explorer({
notificationsClient: null,
isEmulator: false
});
});
it("should be true if subscription type is Benefits", () => {

View File

@@ -17,7 +17,6 @@ import { ContextualPaneBase } from "./ContextualPaneBase";
import { PlatformType } from "../../PlatformType";
import { refreshCachedOffers, refreshCachedResources, createDatabase } from "../../Common/DocumentClientUtilityBase";
import { userContext } from "../../UserContext";
import { configContext, Platform } from "../../ConfigContext";
export default class AddDatabasePane extends ContextualPaneBase {
public defaultExperience: ko.Computed<string>;
@@ -180,7 +179,7 @@ export default class AddDatabasePane extends ContextualPaneBase {
this.canRequestSupport = ko.pureComputed(() => {
if (
configContext.platform !== Platform.Emulator &&
!this.container.isEmulator &&
!this.container.isTryCosmosDBSubscription() &&
this.container.getPlatformType() !== PlatformType.Portal
) {
@@ -203,7 +202,7 @@ export default class AddDatabasePane extends ContextualPaneBase {
});
this.costsVisible = ko.pureComputed(() => {
return configContext.platform !== Platform.Emulator;
return !this.container.isEmulator;
});
this.throughputSpendAckVisible = ko.pureComputed<boolean>(() => {
@@ -422,7 +421,7 @@ export default class AddDatabasePane extends ContextualPaneBase {
startKey: number
): void {
if (EnvironmentUtility.isAadUser()) {
this._createKeyspaceUsingRP(this.container.armEndpoint(), createDatabaseParameters, autoPilotSettings, startKey);
this._createKeyspaceUsingRP(createDatabaseParameters, autoPilotSettings, startKey);
} else {
this._createKeyspaceUsingProxy(createDatabaseParameters.offerThroughput, startKey);
}
@@ -451,12 +450,11 @@ export default class AddDatabasePane extends ContextualPaneBase {
}
private _createKeyspaceUsingRP(
armEndpoint: string,
createKeyspaceParameters: DataModels.RpParameters,
autoPilotSettings: DataModels.RpOptions,
startKey: number
): void {
AddDbUtilities.createCassandraKeyspace(armEndpoint, createKeyspaceParameters, autoPilotSettings).then(() => {
AddDbUtilities.createCassandraKeyspace(createKeyspaceParameters, autoPilotSettings).then(() => {
Promise.all([refreshCachedOffers(), refreshCachedResources()]).then(() => {
this._onCreateDatabaseSuccess(createKeyspaceParameters.offerThroughput, startKey);
});

View File

@@ -12,7 +12,6 @@ import { Action, ActionModifiers } from "../../Shared/Telemetry/TelemetryConstan
import { CassandraAPIDataClient } from "../Tables/TableDataClient";
import { ContextualPaneBase } from "./ContextualPaneBase";
import { HashMap } from "../../Common/HashMap";
import { configContext, Platform } from "../../ConfigContext";
export default class CassandraAddCollectionPane extends ContextualPaneBase {
public createTableQuery: ko.Observable<string>;
@@ -232,11 +231,11 @@ export default class CassandraAddCollectionPane extends ContextualPaneBase {
});
this.costsVisible = ko.pureComputed(() => {
return configContext.platform !== Platform.Emulator;
return !this.container.isEmulator;
});
this.canRequestSupport = ko.pureComputed(() => {
if (configContext.platform !== Platform.Emulator && !this.container.isTryCosmosDBSubscription()) {
if (!this.container.isEmulator && !this.container.isTryCosmosDBSubscription()) {
const offerThroughput: number = this.throughput();
return offerThroughput <= 100000;
}

View File

@@ -17,7 +17,7 @@ describe("Delete Collection Confirmation Pane", () => {
let explorer: Explorer;
beforeEach(() => {
explorer = new Explorer();
explorer = new Explorer({ notificationsClient: null, isEmulator: false });
});
it("should be true if 1 database and 1 collection", () => {
@@ -56,7 +56,7 @@ describe("Delete Collection Confirmation Pane", () => {
describe("shouldRecordFeedback()", () => {
it("should return true if last collection and database does not have shared throughput else false", () => {
let fakeExplorer = new Explorer();
let fakeExplorer = new Explorer({ notificationsClient: null, isEmulator: false });
fakeExplorer.isNotificationConsoleExpanded = ko.observable<boolean>(false);
fakeExplorer.refreshAllDatabases = () => Q.resolve();

View File

@@ -22,7 +22,7 @@ describe("Delete Database Confirmation Pane", () => {
});
beforeEach(() => {
explorer = new Explorer();
explorer = new Explorer({ notificationsClient: null, isEmulator: false });
});
it("should be true if only 1 database", () => {

View File

@@ -7,7 +7,7 @@ describe("Settings Pane", () => {
let explorer: Explorer;
beforeEach(() => {
explorer = new Explorer();
explorer = new Explorer({ notificationsClient: null, isEmulator: false });
});
it("should be true for SQL API", () => {

View File

@@ -6,7 +6,7 @@ import Explorer from "../Explorer";
jest.mock("../Explorer");
const createExplorer = () => {
const mock = new Explorer();
const mock = new Explorer({} as any);
mock.selectedNode = ko.observable();
mock.isNotebookEnabled = ko.observable(false);
mock.addCollectionText = ko.observable("add collection");

View File

@@ -20,7 +20,6 @@ import { updateOffer } from "../../Common/DocumentClientUtilityBase";
import { CommandButtonComponentProps } from "../Controls/CommandButton/CommandButtonComponent";
import { userContext } from "../../UserContext";
import { updateOfferThroughputBeyondLimit } from "../../Common/dataAccess/updateOfferThroughputBeyondLimit";
import { configContext, Platform } from "../../ConfigContext";
const updateThroughputBeyondLimitWarningMessage: string = `
You are about to request an increase in throughput beyond the pre-allocated capacity.
@@ -197,7 +196,7 @@ export default class DatabaseSettingsTab extends TabsBase implements ViewModels.
});
this.costsVisible = ko.computed(() => {
return configContext.platform !== Platform.Emulator;
return !this.container.isEmulator;
});
this.shouldDisplayPortalUsePrompt = ko.pureComputed<boolean>(
@@ -208,7 +207,7 @@ export default class DatabaseSettingsTab extends TabsBase implements ViewModels.
);
this.canRequestSupport = ko.pureComputed(() => {
if (
configContext.platform === Platform.Emulator ||
!!this.container.isEmulator ||
this.container.getPlatformType() === PlatformType.Hosted ||
this.canThroughputExceedMaximumValue()
) {

View File

@@ -27,9 +27,15 @@ describe("Documents tab", () => {
});
describe("showPartitionKey", () => {
const explorer = new Explorer();
const explorer = new Explorer({
notificationsClient: null,
isEmulator: false
});
const mongoExplorer = new Explorer();
const mongoExplorer = new Explorer({
notificationsClient: null,
isEmulator: false
});
mongoExplorer.defaultExperience(Constants.DefaultAccountExperience.MongoDB);
const collectionWithoutPartitionKey = <ViewModels.Collection>(<unknown>{

View File

@@ -49,7 +49,7 @@ describe("Query Tab", () => {
let explorer: Explorer;
beforeEach(() => {
explorer = new Explorer();
explorer = new Explorer({ notificationsClient: null, isEmulator: false });
});
it("should be true for accounts using SQL API", () => {
@@ -69,7 +69,7 @@ describe("Query Tab", () => {
let explorer: Explorer;
beforeEach(() => {
explorer = new Explorer();
explorer = new Explorer({ notificationsClient: null, isEmulator: false });
});
it("should be visible when using a supported API", () => {

View File

@@ -78,7 +78,7 @@ describe("Settings tab", () => {
};
beforeEach(() => {
explorer = new Explorer();
explorer = new Explorer({ notificationsClient: null, isEmulator: false });
explorer.hasAutoPilotV2FeatureFlag = ko.computed<boolean>(() => true);
});
@@ -177,7 +177,7 @@ describe("Settings tab", () => {
let explorer: Explorer;
beforeEach(() => {
explorer = new Explorer();
explorer = new Explorer({ notificationsClient: null, isEmulator: false });
explorer.hasAutoPilotV2FeatureFlag = ko.computed<boolean>(() => true);
});
@@ -255,7 +255,7 @@ describe("Settings tab", () => {
let explorer: Explorer;
beforeEach(() => {
explorer = new Explorer();
explorer = new Explorer({ notificationsClient: null, isEmulator: false });
explorer.hasAutoPilotV2FeatureFlag = ko.computed<boolean>(() => true);
});
@@ -336,7 +336,10 @@ describe("Settings tab", () => {
}
function getCollection(defaultApi: string, partitionKeyOption: PartitionKeyOption) {
const explorer = new Explorer();
const explorer = new Explorer({
notificationsClient: null,
isEmulator: false
});
explorer.defaultExperience(defaultApi);
explorer.hasAutoPilotV2FeatureFlag = ko.computed<boolean>(() => true);
@@ -468,7 +471,10 @@ describe("Settings tab", () => {
describe("AutoPilot", () => {
function getCollection(autoPilotTier: DataModels.AutopilotTier) {
const explorer = new Explorer();
const explorer = new Explorer({
notificationsClient: null,
isEmulator: false
});
explorer.hasAutoPilotV2FeatureFlag = ko.computed<boolean>(() => true);
explorer.databaseAccount({

View File

@@ -21,8 +21,6 @@ import { updateOffer, updateCollection } from "../../Common/DocumentClientUtilit
import { CommandButtonComponentProps } from "../Controls/CommandButton/CommandButtonComponent";
import { userContext } from "../../UserContext";
import { updateOfferThroughputBeyondLimit } from "../../Common/dataAccess/updateOfferThroughputBeyondLimit";
import { config } from "process";
import { configContext, Platform } from "../../ConfigContext";
const ttlWarning: string = `
The system will automatically delete items based on the TTL value (in seconds) you provide, without needing a delete operation explicitly issued by a client application.
@@ -456,7 +454,7 @@ export default class SettingsTab extends TabsBase implements ViewModels.WaitsFor
});
this.rupmVisible = ko.computed(() => {
if (configContext.platform === Platform.Emulator) {
if (this.container.isEmulator) {
return false;
}
if (this.container.isFeatureEnabled(Constants.Features.enableRupm)) {
@@ -486,7 +484,7 @@ export default class SettingsTab extends TabsBase implements ViewModels.WaitsFor
});
this.costsVisible = ko.computed(() => {
return configContext.platform !== Platform.Emulator;
return !this.container.isEmulator;
});
this.isTryCosmosDBSubscription = ko.computed<boolean>(() => {
@@ -502,7 +500,7 @@ export default class SettingsTab extends TabsBase implements ViewModels.WaitsFor
});
this.canRequestSupport = ko.pureComputed(() => {
if (configContext.platform === Platform.Emulator) {
if (this.container.isEmulator) {
return false;
}
@@ -709,7 +707,7 @@ export default class SettingsTab extends TabsBase implements ViewModels.WaitsFor
}
const isThroughputGreaterThanMaxRus = this.throughput() > this.maxRUs();
const isEmulator = configContext.platform === Platform.Emulator;
const isEmulator = this.container.isEmulator;
if (isThroughputGreaterThanMaxRus && isEmulator) {
return false;
}
@@ -880,8 +878,7 @@ export default class SettingsTab extends TabsBase implements ViewModels.WaitsFor
this.maxRUs() <= SharedConstants.CollectionCreation.DefaultCollectionRUs1Million &&
this.throughput() > SharedConstants.CollectionCreation.DefaultCollectionRUs1Million;
const throughputExceedsMaxValue: boolean =
configContext.platform !== Platform.Emulator && this.throughput() > this.maxRUs();
const throughputExceedsMaxValue: boolean = !this.container.isEmulator && this.throughput() > this.maxRUs();
const ttlOptionDirty: boolean = this.timeToLive.editableIsDirty();
const ttlOrIndexingPolicyFieldsDirty: boolean =

View File

@@ -16,7 +16,7 @@ describe("Tabs manager tests", () => {
let documentsTab: DocumentsTab;
beforeAll(() => {
explorer = new Explorer();
explorer = new Explorer({ notificationsClient: undefined, isEmulator: false });
explorer.databaseAccount = ko.observable<DataModels.DatabaseAccount>({
id: "test",
name: "test",

View File

@@ -559,6 +559,7 @@ export default class Collection implements ViewModels.Collection {
});
const tabTitle = !this.offer() ? "Settings" : "Scale & Settings";
const pendingNotificationsPromise: Q.Promise<DataModels.Notification> = this._getPendingThroughputSplitNotification();
const matchingTabs = this.container.tabsManager.getTabs(ViewModels.CollectionTabKind.Settings, tab => {
return tab.collection && tab.collection.rid === this.rid;
});
@@ -574,8 +575,9 @@ export default class Collection implements ViewModels.Collection {
tabTitle: tabTitle
});
Q.all([this.readSettings()]).then(
Q.all([pendingNotificationsPromise, this.readSettings()]).then(
(data: any) => {
const pendingNotification: DataModels.Notification = data && data[0];
settingsTab = new SettingsTab({
tabKind: ViewModels.CollectionTabKind.Settings,
title: !this.offer() ? "Settings" : "Scale & Settings",
@@ -590,6 +592,7 @@ export default class Collection implements ViewModels.Collection {
onUpdateTabsButtons: this.container.onUpdateTabsButtons
});
this.container.tabsManager.activateNewTab(settingsTab);
settingsTab.pendingNotification(pendingNotification);
},
(error: any) => {
TelemetryProcessor.traceFailure(
@@ -613,7 +616,16 @@ export default class Collection implements ViewModels.Collection {
}
);
} else {
this.container.tabsManager.activateTab(settingsTab);
pendingNotificationsPromise.then(
(pendingNotification: DataModels.Notification) => {
settingsTab.pendingNotification(pendingNotification);
this.container.tabsManager.activateTab(settingsTab);
},
(error: any) => {
settingsTab.pendingNotification(undefined);
this.container.tabsManager.activateTab(settingsTab);
}
);
}
};
@@ -1271,6 +1283,48 @@ export default class Collection implements ViewModels.Collection {
return deferred.promise;
}
private _getPendingThroughputSplitNotification(): Q.Promise<DataModels.Notification> {
if (!this.container) {
return Q.resolve(undefined);
}
const deferred: Q.Deferred<DataModels.Notification> = Q.defer<DataModels.Notification>();
this.container.notificationsClient.fetchNotifications().then(
(notifications: DataModels.Notification[]) => {
if (!notifications || notifications.length === 0) {
deferred.resolve(undefined);
return;
}
const pendingNotification = _.find(notifications, (notification: DataModels.Notification) => {
const throughputUpdateRegExp: RegExp = new RegExp("Throughput update (.*) in progress");
return (
notification.kind === "message" &&
notification.collectionName === this.id() &&
notification.description &&
throughputUpdateRegExp.test(notification.description)
);
});
deferred.resolve(pendingNotification);
},
(error: any) => {
Logger.logError(
JSON.stringify({
error: JSON.stringify(error),
accountName: this.container && this.container.databaseAccount(),
databaseName: this.databaseId,
collectionName: this.id()
}),
"Settings tree node"
);
deferred.resolve(undefined);
}
);
return deferred.promise;
}
private _logUploadDetailsInConsole(uploadDetails: UploadDetails): void {
const uploadDetailsRecords: UploadDetailsRecord[] = uploadDetails.data;
const numFiles: number = uploadDetailsRecords.length;

View File

@@ -52,6 +52,7 @@ export default class Database implements ViewModels.Database {
dataExplorerArea: Constants.Areas.ResourceTree
});
const pendingNotificationsPromise: Q.Promise<DataModels.Notification> = this._getPendingThroughputSplitNotification();
const matchingTabs = this.container.tabsManager.getTabs(
ViewModels.CollectionTabKind.DatabaseSettings,
tab => tab.rid === this.rid
@@ -65,8 +66,9 @@ export default class Database implements ViewModels.Database {
dataExplorerArea: Constants.Areas.Tab,
tabTitle: "Scale"
});
Q.all([this.readSettings()]).then(
() => {
Q.all([pendingNotificationsPromise, this.readSettings()]).then(
(data: any) => {
const pendingNotification: DataModels.Notification = data && data[0];
settingsTab = new DatabaseSettingsTab({
tabKind: ViewModels.CollectionTabKind.DatabaseSettings,
title: "Scale",
@@ -81,6 +83,7 @@ export default class Database implements ViewModels.Database {
onUpdateTabsButtons: this.container.onUpdateTabsButtons
});
settingsTab.pendingNotification(pendingNotification);
this.container.tabsManager.activateNewTab(settingsTab);
},
(error: any) => {
@@ -105,7 +108,16 @@ export default class Database implements ViewModels.Database {
}
);
} else {
this.container.tabsManager.activateTab(settingsTab);
pendingNotificationsPromise.then(
(pendingNotification: DataModels.Notification) => {
settingsTab.pendingNotification(pendingNotification);
this.container.tabsManager.activateTab(settingsTab);
},
(error: any) => {
settingsTab.pendingNotification(undefined);
this.container.tabsManager.activateTab(settingsTab);
}
);
}
};
@@ -281,6 +293,49 @@ export default class Database implements ViewModels.Database {
return _.find(this.collections(), (collection: ViewModels.Collection) => collection.id() === collectionId);
}
private _getPendingThroughputSplitNotification(): Q.Promise<DataModels.Notification> {
if (!this.container) {
return Q.resolve(undefined);
}
const deferred: Q.Deferred<DataModels.Notification> = Q.defer<DataModels.Notification>();
this.container.notificationsClient.fetchNotifications().then(
(notifications: DataModels.Notification[]) => {
if (!notifications || notifications.length === 0) {
deferred.resolve(undefined);
return;
}
const pendingNotification = _.find(notifications, (notification: DataModels.Notification) => {
const throughputUpdateRegExp: RegExp = new RegExp("Throughput update (.*) in progress");
return (
notification.kind === "message" &&
!notification.collectionName &&
notification.databaseName === this.id() &&
notification.description &&
throughputUpdateRegExp.test(notification.description)
);
});
deferred.resolve(pendingNotification);
},
(error: any) => {
Logger.logError(
JSON.stringify({
error: JSON.stringify(error),
accountName: this.container && this.container.databaseAccount(),
databaseName: this.id(),
collectionName: this.id()
}),
"Settings tree node"
);
deferred.resolve(undefined);
}
);
return deferred.promise;
}
private getDeltaCollections(
updatedCollectionsList: DataModels.Collection[]
): { toAdd: DataModels.Collection[]; toDelete: Collection[] } {

View File

@@ -2,9 +2,14 @@ import { AccountKind, TagNames, DefaultAccountExperience } from "../../Common/Co
import Explorer from "../../Explorer/Explorer";
import { NotificationsClient } from "./NotificationsClient";
export default class EmulatorExplorerFactory {
public static createExplorer(): Explorer {
const explorer: Explorer = new Explorer();
const explorer: Explorer = new Explorer({
notificationsClient: new NotificationsClient(),
isEmulator: true
});
explorer.databaseAccount({
name: "",
id: "",

View File

@@ -1,24 +1,6 @@
import EmulatorExplorerFactory from "./ExplorerFactory";
import Explorer from "../../Explorer/Explorer";
import { AccountKind, DefaultAccountExperience, TagNames } from "../../Common/Constants";
export function initializeExplorer(): Explorer {
const explorer: Explorer = new Explorer();
explorer.databaseAccount({
name: "",
id: "",
location: "",
type: "",
kind: AccountKind.DocumentDB,
tags: {
[TagNames.defaultExperience]: DefaultAccountExperience.DocumentDB
},
properties: {
documentEndpoint: "",
tableEndpoint: "",
gremlinEndpoint: "",
cassandraEndpoint: ""
}
});
explorer.isAccountReady(true);
return explorer;
return EmulatorExplorerFactory.createExplorer();
}

View File

@@ -0,0 +1,16 @@
import Q from "q";
import * as DataModels from "../../Contracts/DataModels";
import { NotificationsClientBase } from "../../Common/NotificationsClientBase";
export class NotificationsClient extends NotificationsClientBase {
private static readonly _notificationsApiSuffix: string = "/api/notifications";
public constructor() {
super(NotificationsClient._notificationsApiSuffix);
}
public fetchNotifications(): Q.Promise<DataModels.Notification[]> {
// no notifications for the emulator
return Q([]);
}
}

View File

@@ -1,8 +1,14 @@
import Explorer from "../../Explorer/Explorer";
import { NotificationsClient } from "./NotificationsClient";
export default class HostedExplorerFactory {
public createExplorer(): Explorer {
return new Explorer();
const explorer = new Explorer({
notificationsClient: new NotificationsClient(),
isEmulator: false
});
return explorer;
}
public static reInitializeDocumentClientUtilityForExplorer(explorer: Explorer): void {

View File

@@ -0,0 +1,9 @@
import { NotificationsClientBase } from "../../Common/NotificationsClientBase";
export class NotificationsClient extends NotificationsClientBase {
private static readonly _notificationsApiSuffix: string = "/api/guest/notifications";
public constructor() {
super(NotificationsClient._notificationsApiSuffix);
}
}

View File

@@ -0,0 +1,13 @@
import Explorer from "../../Explorer/Explorer";
import { NotificationsClient } from "./NotificationsClient";
export default class PortalExplorerFactory {
public createExplorer(): Explorer {
var explorer = new Explorer({
notificationsClient: new NotificationsClient(),
isEmulator: false
});
return explorer;
}
}

View File

@@ -1,8 +1,10 @@
import PortalExplorerFactory from "./ExplorerFactory";
import "../../Explorer/Tables/DataTable/DataTableBindingManager";
import Explorer from "../../Explorer/Explorer";
export function initializeExplorer(): Explorer {
const explorer = new Explorer();
const portalExplorerFactory = new PortalExplorerFactory();
const explorer = portalExplorerFactory.createExplorer();
window.addEventListener("message", explorer.handleMessage.bind(explorer), false);
return explorer;

View File

@@ -0,0 +1,9 @@
import { NotificationsClientBase } from "../../Common/NotificationsClientBase";
export class NotificationsClient extends NotificationsClientBase {
private static readonly _notificationsApiSuffix: string = "/api/notifications";
public constructor() {
super(NotificationsClient._notificationsApiSuffix);
}
}

View File

@@ -9,7 +9,10 @@ describe("TabRouteHandler", () => {
let tabRouteHandler: TabRouteHandler;
beforeAll(() => {
(<any>window).dataExplorer = new Explorer(); // create a mock to avoid null refs
(<any>window).dataExplorer = new Explorer({
notificationsClient: null,
isEmulator: false
}); // create a mock to avoid null refs
});
beforeEach(() => {

View File

@@ -8,6 +8,7 @@ import { MessageTypes } from "../Contracts/ExplorerContracts";
import * as NotificationConsoleUtils from "../Utils/NotificationConsoleUtils";
import { ResourceProviderClient } from "../ResourceProvider/ResourceProviderClient";
import { userContext } from "../UserContext";
import { createUpdateCassandraKeyspace } from "../Utils/arm/generatedClients/2020-04-01/cassandraResources";
export class AddDbUtilities {
// todo - remove any
@@ -47,7 +48,6 @@ export class AddDbUtilities {
// todo - remove any
public static async createCassandraKeyspace(
armEndpoint: string,
params: DataModels.RpParameters,
rpOptions: DataModels.RpOptions
): Promise<any> {
@@ -70,9 +70,11 @@ export class AddDbUtilities {
}
try {
await AddDbUtilities.getRpClient<DataModels.CreateDatabaseWithRpResponse>(armEndpoint).putAsync(
AddDbUtilities._getCassandraKeyspaceUri(params),
DataExplorerConstants.ArmApiVersions.publicVersion,
await createUpdateCassandraKeyspace(
userContext.subscriptionId,
userContext.resourceGroup,
userContext.databaseAccount?.name,
params.db,
rpPayloadToCreateKeyspace
);
} catch (reason) {
@@ -159,10 +161,7 @@ export class AddDbUtilities {
}
private static _handleCreationError(reason: any, params: DataModels.RpParameters, dbType: string = "database") {
NotificationConsoleUtils.logConsoleMessage(
ConsoleDataType.Error,
`Error creating ${dbType}: ${JSON.stringify(reason)}, Payload: ${params}`
);
NotificationConsoleUtils.logConsoleError(`Error creating ${dbType}: ${JSON.stringify(reason)}, Payload: ${params}`);
if (reason.status === HttpStatusCodes.Forbidden) {
sendMessage({ type: MessageTypes.ForbiddenError });
return;

View File

@@ -60,7 +60,7 @@ describe("AuthorizationUtils", () => {
});
describe("displayTokenRenewalPromptForStatus()", () => {
let explorer = new Explorer() as jest.Mocked<Explorer>;
let explorer = new Explorer({} as any) as jest.Mocked<Explorer>;
beforeEach(() => {
jest.clearAllMocks();

View File

@@ -39,7 +39,7 @@ export async function createUpdateCassandraKeyspace(
body: Types.CassandraKeyspaceCreateUpdateParameters
): Promise<Types.CassandraKeyspaceGetResults | void> {
const path = `/subscriptions/${subscriptionId}/resourceGroups/${resourceGroupName}/providers/Microsoft.DocumentDB/databaseAccounts/${accountName}/cassandraKeyspaces/${keyspaceName}`;
return armRequest({ host: configContext.ARM_ENDPOINT, path, method: "PUT", apiVersion, body: JSON.stringify(body) });
return armRequest({ host: configContext.ARM_ENDPOINT, path, method: "PUT", apiVersion, body });
}
/* Deletes an existing Azure Cosmos DB Cassandra keyspace. */
@@ -73,7 +73,7 @@ export async function updateCassandraKeyspaceThroughput(
body: Types.ThroughputSettingsUpdateParameters
): Promise<Types.ThroughputSettingsGetResults | void> {
const path = `/subscriptions/${subscriptionId}/resourceGroups/${resourceGroupName}/providers/Microsoft.DocumentDB/databaseAccounts/${accountName}/cassandraKeyspaces/${keyspaceName}/throughputSettings/default`;
return armRequest({ host: configContext.ARM_ENDPOINT, path, method: "PUT", apiVersion, body: JSON.stringify(body) });
return armRequest({ host: configContext.ARM_ENDPOINT, path, method: "PUT", apiVersion, body });
}
/* Migrate an Azure Cosmos DB Cassandra Keyspace from manual throughput to autoscale */
@@ -131,7 +131,7 @@ export async function createUpdateCassandraTable(
body: Types.CassandraTableCreateUpdateParameters
): Promise<Types.CassandraTableGetResults | void> {
const path = `/subscriptions/${subscriptionId}/resourceGroups/${resourceGroupName}/providers/Microsoft.DocumentDB/databaseAccounts/${accountName}/cassandraKeyspaces/${keyspaceName}/tables/${tableName}`;
return armRequest({ host: configContext.ARM_ENDPOINT, path, method: "PUT", apiVersion, body: JSON.stringify(body) });
return armRequest({ host: configContext.ARM_ENDPOINT, path, method: "PUT", apiVersion, body });
}
/* Deletes an existing Azure Cosmos DB Cassandra table. */
@@ -168,7 +168,7 @@ export async function updateCassandraTableThroughput(
body: Types.ThroughputSettingsUpdateParameters
): Promise<Types.ThroughputSettingsGetResults | void> {
const path = `/subscriptions/${subscriptionId}/resourceGroups/${resourceGroupName}/providers/Microsoft.DocumentDB/databaseAccounts/${accountName}/cassandraKeyspaces/${keyspaceName}/tables/${tableName}/throughputSettings/default`;
return armRequest({ host: configContext.ARM_ENDPOINT, path, method: "PUT", apiVersion, body: JSON.stringify(body) });
return armRequest({ host: configContext.ARM_ENDPOINT, path, method: "PUT", apiVersion, body });
}
/* Migrate an Azure Cosmos DB Cassandra table from manual throughput to autoscale */

View File

@@ -27,13 +27,7 @@ export async function update(
body: Types.DatabaseAccountUpdateParameters
): Promise<Types.DatabaseAccountGetResults> {
const path = `/subscriptions/${subscriptionId}/resourceGroups/${resourceGroupName}/providers/Microsoft.DocumentDB/databaseAccounts/${accountName}`;
return armRequest({
host: configContext.ARM_ENDPOINT,
path,
method: "PATCH",
apiVersion,
body: JSON.stringify(body)
});
return armRequest({ host: configContext.ARM_ENDPOINT, path, method: "PATCH", apiVersion, body });
}
/* Creates or updates an Azure Cosmos DB database account. The "Update" method is preferred when performing updates on an account. */
@@ -44,7 +38,7 @@ export async function createOrUpdate(
body: Types.DatabaseAccountCreateUpdateParameters
): Promise<Types.DatabaseAccountGetResults> {
const path = `/subscriptions/${subscriptionId}/resourceGroups/${resourceGroupName}/providers/Microsoft.DocumentDB/databaseAccounts/${accountName}`;
return armRequest({ host: configContext.ARM_ENDPOINT, path, method: "PUT", apiVersion, body: JSON.stringify(body) });
return armRequest({ host: configContext.ARM_ENDPOINT, path, method: "PUT", apiVersion, body });
}
/* Deletes an existing Azure Cosmos DB database account. */
@@ -61,7 +55,7 @@ export async function failoverPriorityChange(
body: Types.FailoverPolicies
): Promise<void> {
const path = `/subscriptions/${subscriptionId}/resourceGroups/${resourceGroupName}/providers/Microsoft.DocumentDB/databaseAccounts/${accountName}/failoverPriorityChange`;
return armRequest({ host: configContext.ARM_ENDPOINT, path, method: "POST", apiVersion, body: JSON.stringify(body) });
return armRequest({ host: configContext.ARM_ENDPOINT, path, method: "POST", apiVersion, body });
}
/* Lists all the Azure Cosmos DB database accounts available under the subscription. */
@@ -107,7 +101,7 @@ export async function offlineRegion(
body: Types.RegionForOnlineOffline
): Promise<void | Types.ErrorResponse> {
const path = `/subscriptions/${subscriptionId}/resourceGroups/${resourceGroupName}/providers/Microsoft.DocumentDB/databaseAccounts/${accountName}/offlineRegion`;
return armRequest({ host: configContext.ARM_ENDPOINT, path, method: "POST", apiVersion, body: JSON.stringify(body) });
return armRequest({ host: configContext.ARM_ENDPOINT, path, method: "POST", apiVersion, body });
}
/* Online the specified region for the specified Azure Cosmos DB database account. */
@@ -118,7 +112,7 @@ export async function onlineRegion(
body: Types.RegionForOnlineOffline
): Promise<void | Types.ErrorResponse> {
const path = `/subscriptions/${subscriptionId}/resourceGroups/${resourceGroupName}/providers/Microsoft.DocumentDB/databaseAccounts/${accountName}/onlineRegion`;
return armRequest({ host: configContext.ARM_ENDPOINT, path, method: "POST", apiVersion, body: JSON.stringify(body) });
return armRequest({ host: configContext.ARM_ENDPOINT, path, method: "POST", apiVersion, body });
}
/* Lists the read-only access keys for the specified Azure Cosmos DB database account. */
@@ -149,7 +143,7 @@ export async function regenerateKey(
body: Types.DatabaseAccountRegenerateKeyParameters
): Promise<void> {
const path = `/subscriptions/${subscriptionId}/resourceGroups/${resourceGroupName}/providers/Microsoft.DocumentDB/databaseAccounts/${accountName}/regenerateKey`;
return armRequest({ host: configContext.ARM_ENDPOINT, path, method: "POST", apiVersion, body: JSON.stringify(body) });
return armRequest({ host: configContext.ARM_ENDPOINT, path, method: "POST", apiVersion, body });
}
/* Checks that the Azure Cosmos DB account name already exists. A valid account name may contain only lowercase letters, numbers, and the '-' character, and must be between 3 and 50 characters. */

View File

@@ -39,7 +39,7 @@ export async function createUpdateGremlinDatabase(
body: Types.GremlinDatabaseCreateUpdateParameters
): Promise<Types.GremlinDatabaseGetResults | void> {
const path = `/subscriptions/${subscriptionId}/resourceGroups/${resourceGroupName}/providers/Microsoft.DocumentDB/databaseAccounts/${accountName}/gremlinDatabases/${databaseName}`;
return armRequest({ host: configContext.ARM_ENDPOINT, path, method: "PUT", apiVersion, body: JSON.stringify(body) });
return armRequest({ host: configContext.ARM_ENDPOINT, path, method: "PUT", apiVersion, body });
}
/* Deletes an existing Azure Cosmos DB Gremlin database. */
@@ -73,7 +73,7 @@ export async function updateGremlinDatabaseThroughput(
body: Types.ThroughputSettingsUpdateParameters
): Promise<Types.ThroughputSettingsGetResults | void> {
const path = `/subscriptions/${subscriptionId}/resourceGroups/${resourceGroupName}/providers/Microsoft.DocumentDB/databaseAccounts/${accountName}/gremlinDatabases/${databaseName}/throughputSettings/default`;
return armRequest({ host: configContext.ARM_ENDPOINT, path, method: "PUT", apiVersion, body: JSON.stringify(body) });
return armRequest({ host: configContext.ARM_ENDPOINT, path, method: "PUT", apiVersion, body });
}
/* Migrate an Azure Cosmos DB Gremlin database from manual throughput to autoscale */
@@ -131,7 +131,7 @@ export async function createUpdateGremlinGraph(
body: Types.GremlinGraphCreateUpdateParameters
): Promise<Types.GremlinGraphGetResults | void> {
const path = `/subscriptions/${subscriptionId}/resourceGroups/${resourceGroupName}/providers/Microsoft.DocumentDB/databaseAccounts/${accountName}/gremlinDatabases/${databaseName}/graphs/${graphName}`;
return armRequest({ host: configContext.ARM_ENDPOINT, path, method: "PUT", apiVersion, body: JSON.stringify(body) });
return armRequest({ host: configContext.ARM_ENDPOINT, path, method: "PUT", apiVersion, body });
}
/* Deletes an existing Azure Cosmos DB Gremlin graph. */
@@ -168,7 +168,7 @@ export async function updateGremlinGraphThroughput(
body: Types.ThroughputSettingsUpdateParameters
): Promise<Types.ThroughputSettingsGetResults | void> {
const path = `/subscriptions/${subscriptionId}/resourceGroups/${resourceGroupName}/providers/Microsoft.DocumentDB/databaseAccounts/${accountName}/gremlinDatabases/${databaseName}/graphs/${graphName}/throughputSettings/default`;
return armRequest({ host: configContext.ARM_ENDPOINT, path, method: "PUT", apiVersion, body: JSON.stringify(body) });
return armRequest({ host: configContext.ARM_ENDPOINT, path, method: "PUT", apiVersion, body });
}
/* Migrate an Azure Cosmos DB Gremlin graph from manual throughput to autoscale */

View File

@@ -39,7 +39,7 @@ export async function createUpdateMongoDBDatabase(
body: Types.MongoDBDatabaseCreateUpdateParameters
): Promise<Types.MongoDBDatabaseGetResults | void> {
const path = `/subscriptions/${subscriptionId}/resourceGroups/${resourceGroupName}/providers/Microsoft.DocumentDB/databaseAccounts/${accountName}/mongodbDatabases/${databaseName}`;
return armRequest({ host: configContext.ARM_ENDPOINT, path, method: "PUT", apiVersion, body: JSON.stringify(body) });
return armRequest({ host: configContext.ARM_ENDPOINT, path, method: "PUT", apiVersion, body });
}
/* Deletes an existing Azure Cosmos DB MongoDB database. */
@@ -73,7 +73,7 @@ export async function updateMongoDBDatabaseThroughput(
body: Types.ThroughputSettingsUpdateParameters
): Promise<Types.ThroughputSettingsGetResults | void> {
const path = `/subscriptions/${subscriptionId}/resourceGroups/${resourceGroupName}/providers/Microsoft.DocumentDB/databaseAccounts/${accountName}/mongodbDatabases/${databaseName}/throughputSettings/default`;
return armRequest({ host: configContext.ARM_ENDPOINT, path, method: "PUT", apiVersion, body: JSON.stringify(body) });
return armRequest({ host: configContext.ARM_ENDPOINT, path, method: "PUT", apiVersion, body });
}
/* Migrate an Azure Cosmos DB MongoDB database from manual throughput to autoscale */
@@ -131,7 +131,7 @@ export async function createUpdateMongoDBCollection(
body: Types.MongoDBCollectionCreateUpdateParameters
): Promise<Types.MongoDBCollectionGetResults | void> {
const path = `/subscriptions/${subscriptionId}/resourceGroups/${resourceGroupName}/providers/Microsoft.DocumentDB/databaseAccounts/${accountName}/mongodbDatabases/${databaseName}/collections/${collectionName}`;
return armRequest({ host: configContext.ARM_ENDPOINT, path, method: "PUT", apiVersion, body: JSON.stringify(body) });
return armRequest({ host: configContext.ARM_ENDPOINT, path, method: "PUT", apiVersion, body });
}
/* Deletes an existing Azure Cosmos DB MongoDB Collection. */
@@ -168,7 +168,7 @@ export async function updateMongoDBCollectionThroughput(
body: Types.ThroughputSettingsUpdateParameters
): Promise<Types.ThroughputSettingsGetResults | void> {
const path = `/subscriptions/${subscriptionId}/resourceGroups/${resourceGroupName}/providers/Microsoft.DocumentDB/databaseAccounts/${accountName}/mongodbDatabases/${databaseName}/collections/${collectionName}/throughputSettings/default`;
return armRequest({ host: configContext.ARM_ENDPOINT, path, method: "PUT", apiVersion, body: JSON.stringify(body) });
return armRequest({ host: configContext.ARM_ENDPOINT, path, method: "PUT", apiVersion, body });
}
/* Migrate an Azure Cosmos DB MongoDB collection from manual throughput to autoscale */

View File

@@ -39,7 +39,7 @@ export async function createUpdateSqlDatabase(
body: Types.SqlDatabaseCreateUpdateParameters
): Promise<Types.SqlDatabaseGetResults | void> {
const path = `/subscriptions/${subscriptionId}/resourceGroups/${resourceGroupName}/providers/Microsoft.DocumentDB/databaseAccounts/${accountName}/sqlDatabases/${databaseName}`;
return armRequest({ host: configContext.ARM_ENDPOINT, path, method: "PUT", apiVersion, body: JSON.stringify(body) });
return armRequest({ host: configContext.ARM_ENDPOINT, path, method: "PUT", apiVersion, body });
}
/* Deletes an existing Azure Cosmos DB SQL database. */
@@ -73,7 +73,7 @@ export async function updateSqlDatabaseThroughput(
body: Types.ThroughputSettingsUpdateParameters
): Promise<Types.ThroughputSettingsGetResults | void> {
const path = `/subscriptions/${subscriptionId}/resourceGroups/${resourceGroupName}/providers/Microsoft.DocumentDB/databaseAccounts/${accountName}/sqlDatabases/${databaseName}/throughputSettings/default`;
return armRequest({ host: configContext.ARM_ENDPOINT, path, method: "PUT", apiVersion, body: JSON.stringify(body) });
return armRequest({ host: configContext.ARM_ENDPOINT, path, method: "PUT", apiVersion, body });
}
/* Migrate an Azure Cosmos DB SQL database from manual throughput to autoscale */
@@ -131,7 +131,7 @@ export async function createUpdateSqlContainer(
body: Types.SqlContainerCreateUpdateParameters
): Promise<Types.SqlContainerGetResults | void> {
const path = `/subscriptions/${subscriptionId}/resourceGroups/${resourceGroupName}/providers/Microsoft.DocumentDB/databaseAccounts/${accountName}/sqlDatabases/${databaseName}/containers/${containerName}`;
return armRequest({ host: configContext.ARM_ENDPOINT, path, method: "PUT", apiVersion, body: JSON.stringify(body) });
return armRequest({ host: configContext.ARM_ENDPOINT, path, method: "PUT", apiVersion, body });
}
/* Deletes an existing Azure Cosmos DB SQL container. */
@@ -168,7 +168,7 @@ export async function updateSqlContainerThroughput(
body: Types.ThroughputSettingsUpdateParameters
): Promise<Types.ThroughputSettingsGetResults | void> {
const path = `/subscriptions/${subscriptionId}/resourceGroups/${resourceGroupName}/providers/Microsoft.DocumentDB/databaseAccounts/${accountName}/sqlDatabases/${databaseName}/containers/${containerName}/throughputSettings/default`;
return armRequest({ host: configContext.ARM_ENDPOINT, path, method: "PUT", apiVersion, body: JSON.stringify(body) });
return armRequest({ host: configContext.ARM_ENDPOINT, path, method: "PUT", apiVersion, body });
}
/* Migrate an Azure Cosmos DB SQL container from manual throughput to autoscale */
@@ -231,7 +231,7 @@ export async function createUpdateSqlStoredProcedure(
body: Types.SqlStoredProcedureCreateUpdateParameters
): Promise<Types.SqlStoredProcedureGetResults | void> {
const path = `/subscriptions/${subscriptionId}/resourceGroups/${resourceGroupName}/providers/Microsoft.DocumentDB/databaseAccounts/${accountName}/sqlDatabases/${databaseName}/containers/${containerName}/storedProcedures/${storedProcedureName}`;
return armRequest({ host: configContext.ARM_ENDPOINT, path, method: "PUT", apiVersion, body: JSON.stringify(body) });
return armRequest({ host: configContext.ARM_ENDPOINT, path, method: "PUT", apiVersion, body });
}
/* Deletes an existing Azure Cosmos DB SQL storedProcedure. */
@@ -283,7 +283,7 @@ export async function createUpdateSqlUserDefinedFunction(
body: Types.SqlUserDefinedFunctionCreateUpdateParameters
): Promise<Types.SqlUserDefinedFunctionGetResults | void> {
const path = `/subscriptions/${subscriptionId}/resourceGroups/${resourceGroupName}/providers/Microsoft.DocumentDB/databaseAccounts/${accountName}/sqlDatabases/${databaseName}/containers/${containerName}/userDefinedFunctions/${userDefinedFunctionName}`;
return armRequest({ host: configContext.ARM_ENDPOINT, path, method: "PUT", apiVersion, body: JSON.stringify(body) });
return armRequest({ host: configContext.ARM_ENDPOINT, path, method: "PUT", apiVersion, body });
}
/* Deletes an existing Azure Cosmos DB SQL userDefinedFunction. */
@@ -335,7 +335,7 @@ export async function createUpdateSqlTrigger(
body: Types.SqlTriggerCreateUpdateParameters
): Promise<Types.SqlTriggerGetResults | void> {
const path = `/subscriptions/${subscriptionId}/resourceGroups/${resourceGroupName}/providers/Microsoft.DocumentDB/databaseAccounts/${accountName}/sqlDatabases/${databaseName}/containers/${containerName}/triggers/${triggerName}`;
return armRequest({ host: configContext.ARM_ENDPOINT, path, method: "PUT", apiVersion, body: JSON.stringify(body) });
return armRequest({ host: configContext.ARM_ENDPOINT, path, method: "PUT", apiVersion, body });
}
/* Deletes an existing Azure Cosmos DB SQL trigger. */

View File

@@ -39,7 +39,7 @@ export async function createUpdateTable(
body: Types.TableCreateUpdateParameters
): Promise<Types.TableGetResults | void> {
const path = `/subscriptions/${subscriptionId}/resourceGroups/${resourceGroupName}/providers/Microsoft.DocumentDB/databaseAccounts/${accountName}/tables/${tableName}`;
return armRequest({ host: configContext.ARM_ENDPOINT, path, method: "PUT", apiVersion, body: JSON.stringify(body) });
return armRequest({ host: configContext.ARM_ENDPOINT, path, method: "PUT", apiVersion, body });
}
/* Deletes an existing Azure Cosmos DB Table. */
@@ -73,7 +73,7 @@ export async function updateTableThroughput(
body: Types.ThroughputSettingsUpdateParameters
): Promise<Types.ThroughputSettingsGetResults | void> {
const path = `/subscriptions/${subscriptionId}/resourceGroups/${resourceGroupName}/providers/Microsoft.DocumentDB/databaseAccounts/${accountName}/tables/${tableName}/throughputSettings/default`;
return armRequest({ host: configContext.ARM_ENDPOINT, path, method: "PUT", apiVersion, body: JSON.stringify(body) });
return armRequest({ host: configContext.ARM_ENDPOINT, path, method: "PUT", apiVersion, body });
}
/* Migrate an Azure Cosmos DB Table from manual throughput to autoscale */

File diff suppressed because it is too large Load Diff

View File

@@ -1,81 +1,79 @@
{
"extends": "./tsconfig.json",
"compilerOptions": {
"noEmit": true,
"strictNullChecks": true,
"strict": true,
"noUnusedLocals": true
},
"files": [
"./src/AuthType.ts",
"./src/Bindings/BindingHandlersRegisterer.ts",
"./src/Bindings/ReactBindingHandler.ts",
"./src/Common/ArrayHashMap.ts",
"./src/Common/Constants.ts",
"./src/Common/DeleteFeedback.ts",
"./src/Common/HashMap.ts",
"./src/Common/HeadersUtility.ts",
"./src/Common/Logger.ts",
"./src/Common/MessageHandler.ts",
"./src/Common/MongoUtility.ts",
"./src/Common/ObjectCache.ts",
"./src/Common/ThemeUtility.ts",
"./src/Common/UrlUtility.ts",
"./src/Common/dataAccess/sendNotificationForError.ts",
"./src/ConfigContext.ts",
"./src/Contracts/ActionContracts.ts",
"./src/Contracts/DataModels.ts",
"./src/Contracts/Diagnostics.ts",
"./src/Contracts/ExplorerContracts.ts",
"./src/Contracts/Versions.ts",
"./src/Controls/Heatmap/Heatmap.ts",
"./src/Controls/Heatmap/HeatmapDatatypes.ts",
"./src/DefaultAccountExperienceType.ts",
"./src/Definitions/globals.d.ts",
"./src/Definitions/html.d.ts",
"./src/Definitions/jquery-ui.d.ts",
"./src/Definitions/jquery.d.ts",
"./src/Definitions/plotly.js-cartesian-dist.d-min.ts",
"./src/Definitions/svg.d.ts",
"./src/Explorer/Controls/ErrorDisplayComponent/ErrorDisplayComponent.ts",
"./src/Explorer/Controls/GitHub/GitHubStyleConstants.ts",
"./src/Explorer/Controls/SmartUi/InputUtils.ts",
"./src/Explorer/Notebook/FileSystemUtil.ts",
"./src/Explorer/Notebook/NTeractUtil.ts",
"./src/Explorer/Notebook/NotebookComponent/actions.ts",
"./src/Explorer/Notebook/NotebookComponent/loadTransform.ts",
"./src/Explorer/Notebook/NotebookComponent/reducers.ts",
"./src/Explorer/Notebook/NotebookComponent/types.ts",
"./src/Explorer/Notebook/NotebookContentItem.ts",
"./src/Explorer/Notebook/NotebookUtil.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/QueryBuilder/DateTimeUtilities.ts",
"./src/Explorer/Tabs/TabComponents.ts",
"./src/GitHub/GitHubConnector.ts",
"./src/Index.ts",
"./src/NotebookWorkspaceManager/NotebookWorkspaceResourceProviderMockClients.ts",
"./src/PlatformType.ts",
"./src/ReactDevTools.ts",
"./src/ResourceProvider/IResourceProviderClient.ts",
"./src/Shared/ExplorerSettings.ts",
"./src/Shared/StorageUtility.ts",
"./src/Shared/StringUtility.ts",
"./src/Shared/Telemetry/TelemetryConstants.ts",
"./src/Shared/Telemetry/TelemetryProcessor.ts",
"./src/Shared/appInsights.ts",
"./src/UserContext.ts",
"./src/Utils/Base64Utils.ts",
"./src/Utils/GitHubUtils.ts",
"./src/Utils/MessageValidation.ts",
"./src/Utils/OfferUtils.ts",
"./src/Utils/StringUtils.ts",
"./src/Utils/arm/generatedClients/2020-04-01/types.ts",
"./src/quickstart.ts",
"./src/setupTests.ts",
"./src/workers/upload/definitions.ts"
],
"include": []
}
"extends": "./tsconfig.json",
"compilerOptions": {
"noEmit": true,
"strictNullChecks": true,
"strict": true,
"noUnusedLocals": true
},
"files": [
"./src/AuthType.ts",
"./src/Bindings/BindingHandlersRegisterer.ts",
"./src/Bindings/ReactBindingHandler.ts",
"./src/Common/ArrayHashMap.ts",
"./src/Common/Constants.ts",
"./src/Common/DeleteFeedback.ts",
"./src/Common/HashMap.ts",
"./src/Common/HeadersUtility.ts",
"./src/Common/Logger.ts",
"./src/Common/MessageHandler.ts",
"./src/Common/MongoUtility.ts",
"./src/Common/ObjectCache.ts",
"./src/Common/ThemeUtility.ts",
"./src/Common/UrlUtility.ts",
"./src/Common/dataAccess/sendNotificationForError.ts",
"./src/ConfigContext.ts",
"./src/Contracts/ActionContracts.ts",
"./src/Contracts/DataModels.ts",
"./src/Contracts/Diagnostics.ts",
"./src/Contracts/ExplorerContracts.ts",
"./src/Contracts/Versions.ts",
"./src/Controls/Heatmap/Heatmap.ts",
"./src/Controls/Heatmap/HeatmapDatatypes.ts",
"./src/Definitions/globals.d.ts",
"./src/Definitions/html.d.ts",
"./src/Definitions/jquery-ui.d.ts",
"./src/Definitions/jquery.d.ts",
"./src/Definitions/plotly.js-cartesian-dist.d-min.ts",
"./src/Definitions/svg.d.ts",
"./src/Explorer/Controls/ErrorDisplayComponent/ErrorDisplayComponent.ts",
"./src/Explorer/Controls/GitHub/GitHubStyleConstants.ts",
"./src/Explorer/Controls/SmartUi/InputUtils.ts",
"./src/Explorer/Notebook/FileSystemUtil.ts",
"./src/Explorer/Notebook/NTeractUtil.ts",
"./src/Explorer/Notebook/NotebookComponent/actions.ts",
"./src/Explorer/Notebook/NotebookComponent/loadTransform.ts",
"./src/Explorer/Notebook/NotebookComponent/reducers.ts",
"./src/Explorer/Notebook/NotebookComponent/types.ts",
"./src/Explorer/Notebook/NotebookContentItem.ts",
"./src/Explorer/Notebook/NotebookUtil.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/QueryBuilder/DateTimeUtilities.ts",
"./src/Explorer/Tabs/TabComponents.ts",
"./src/GitHub/GitHubConnector.ts",
"./src/Index.ts",
"./src/NotebookWorkspaceManager/NotebookWorkspaceResourceProviderMockClients.ts",
"./src/PlatformType.ts",
"./src/ReactDevTools.ts",
"./src/ResourceProvider/IResourceProviderClient.ts",
"./src/Shared/ExplorerSettings.ts",
"./src/Shared/StorageUtility.ts",
"./src/Shared/StringUtility.ts",
"./src/Shared/Telemetry/TelemetryConstants.ts",
"./src/Shared/Telemetry/TelemetryProcessor.ts",
"./src/Shared/appInsights.ts",
"./src/UserContext.ts",
"./src/Utils/GitHubUtils.ts",
"./src/Utils/MessageValidation.ts",
"./src/Utils/OfferUtils.ts",
"./src/Utils/StringUtils.ts",
"./src/Utils/arm/generatedClients/2020-04-01/types.ts",
"./src/quickstart.ts",
"./src/setupTests.ts",
"./src/workers/upload/definitions.ts"
],
"include": []
}

View File

@@ -102,31 +102,31 @@ interface Property {
}[];
}
const propertyToType = (property: Property, prop: string) => {
const propertyToType = (property: Property, prop: string, required: boolean) => {
if (property) {
if (property.allOf) {
outputTypes.push(`
/* ${property.description || "undocumented"} */
${property.readOnly ? "readonly " : ""}${prop}: ${property.allOf
.map((allof: { $ref: string }) => refToType(allof.$ref))
.join(" & ")}`);
${property.readOnly ? "readonly " : ""}${prop}${
required ? "" : "?"
}: ${property.allOf.map((allof: { $ref: string }) => refToType(allof.$ref)).join(" & ")}`);
} else if (property.$ref) {
const type = refToType(property.$ref);
outputTypes.push(`
/* ${property.description || "undocumented"} */
${property.readOnly ? "readonly " : ""}${prop}: ${type}
${property.readOnly ? "readonly " : ""}${prop}${required ? "" : "?"}: ${type}
`);
} else if (property.type === "array") {
const type = refToType(property.items.$ref);
outputTypes.push(`
/* ${property.description || "undocumented"} */
${property.readOnly ? "readonly " : ""}${prop}: ${type}[]
${property.readOnly ? "readonly " : ""}${prop}${required ? "" : "?"}: ${type}[]
`);
} else if (property.type === "object") {
const type = refToType(property.$ref);
outputTypes.push(`
/* ${property.description || "undocumented"} */
${property.readOnly ? "readonly " : ""}${prop}: ${type}
${property.readOnly ? "readonly " : ""}${prop}${required ? "" : "?"}: ${type}
`);
} else {
if (property.type === undefined) {
@@ -135,7 +135,7 @@ const propertyToType = (property: Property, prop: string) => {
}
outputTypes.push(`
/* ${property.description || "undocumented"} */
${property.readOnly ? "readonly " : ""}${prop}: ${
${property.readOnly ? "readonly " : ""}${prop}${required ? "" : "?"}: ${
propertyMap[property.type] ? propertyMap[property.type] : property.type
}`);
}
@@ -166,7 +166,7 @@ async function main() {
}
for (const prop in schema.definitions[definition].properties) {
const property = schema.definitions[definition].properties[prop];
propertyToType(property, prop);
propertyToType(property, prop, schema.definitions[definition].required?.includes(prop));
}
outputTypes.push(`}`);
outputTypes.push("\n\n");
@@ -245,7 +245,7 @@ async function main() {
) : Promise<${responseType(operation, "Types")}> {
const path = \`${path.replace(/{/g, "${")}\`
return armRequest({ host: configContext.ARM_ENDPOINT, path, method: "${method.toLocaleUpperCase()}", apiVersion, ${
bodyParameter ? "body: JSON.stringify(body)" : ""
bodyParameter ? "body" : ""
} })
}
`);