mirror of
https://github.com/Azure/cosmos-explorer.git
synced 2025-12-19 08:51:24 +00:00
Lazy load database offer in data explorer (#208)
Co-authored-by: zfoster <notzachfoster@gmail.com>
This commit is contained in:
@@ -12,8 +12,8 @@ import * as NotificationConsoleUtils from "../../Utils/NotificationConsoleUtils"
|
||||
import { ConsoleDataType } from "../Menus/NotificationConsole/NotificationConsoleComponent";
|
||||
import * as Logger from "../../Common/Logger";
|
||||
import Explorer from "../Explorer";
|
||||
import { readOffers, readOffer } from "../../Common/DocumentClientUtilityBase";
|
||||
import { readCollections } from "../../Common/dataAccess/readCollections";
|
||||
import { readDatabaseOffer } from "../../Common/dataAccess/readDatabaseOffer";
|
||||
|
||||
export default class Database implements ViewModels.Database {
|
||||
public nodeKind: string;
|
||||
@@ -27,13 +27,13 @@ export default class Database implements ViewModels.Database {
|
||||
public isDatabaseShared: ko.Computed<boolean>;
|
||||
public selectedSubnodeKind: ko.Observable<ViewModels.CollectionTabKind>;
|
||||
|
||||
constructor(container: Explorer, data: any, offer: DataModels.Offer) {
|
||||
constructor(container: Explorer, data: any) {
|
||||
this.nodeKind = "Database";
|
||||
this.container = container;
|
||||
this.self = data._self;
|
||||
this.rid = data._rid;
|
||||
this.id = ko.observable(data.id);
|
||||
this.offer = ko.observable(offer);
|
||||
this.offer = ko.observable();
|
||||
this.collections = ko.observableArray<Collection>();
|
||||
this.isDatabaseExpanded = ko.observable<boolean>(false);
|
||||
this.selectedSubnodeKind = ko.observable<ViewModels.CollectionTabKind>();
|
||||
@@ -66,7 +66,7 @@ export default class Database implements ViewModels.Database {
|
||||
dataExplorerArea: Constants.Areas.Tab,
|
||||
tabTitle: "Scale"
|
||||
});
|
||||
Q.all([pendingNotificationsPromise, this.readSettings()]).then(
|
||||
pendingNotificationsPromise.then(
|
||||
(data: any) => {
|
||||
const pendingNotification: DataModels.Notification = data && data[0];
|
||||
settingsTab = new DatabaseSettingsTab({
|
||||
@@ -121,80 +121,6 @@ export default class Database implements ViewModels.Database {
|
||||
}
|
||||
};
|
||||
|
||||
public readSettings(): Q.Promise<void> {
|
||||
const deferred: Q.Deferred<void> = Q.defer<void>();
|
||||
this.container.isRefreshingExplorer(true);
|
||||
const databaseDataModel: DataModels.Database = <DataModels.Database>{
|
||||
id: this.id(),
|
||||
_rid: this.rid,
|
||||
_self: this.self
|
||||
};
|
||||
const startKey: number = TelemetryProcessor.traceStart(Action.LoadOffers, {
|
||||
databaseAccountName: this.container.databaseAccount().name,
|
||||
defaultExperience: this.container.defaultExperience()
|
||||
});
|
||||
|
||||
const offerInfoPromise: Q.Promise<DataModels.Offer[]> = readOffers({
|
||||
isServerless: this.container.isServerlessEnabled()
|
||||
});
|
||||
Q.all([offerInfoPromise]).then(
|
||||
() => {
|
||||
this.container.isRefreshingExplorer(false);
|
||||
|
||||
const databaseOffer: DataModels.Offer = this._getOfferForDatabase(
|
||||
offerInfoPromise.valueOf(),
|
||||
databaseDataModel
|
||||
);
|
||||
|
||||
if (!databaseOffer) {
|
||||
return;
|
||||
}
|
||||
|
||||
readOffer(databaseOffer).then((offerDetail: DataModels.OfferWithHeaders) => {
|
||||
const offerThroughputInfo: DataModels.OfferThroughputInfo = {
|
||||
minimumRUForCollection:
|
||||
offerDetail.content &&
|
||||
offerDetail.content.collectionThroughputInfo &&
|
||||
offerDetail.content.collectionThroughputInfo.minimumRUForCollection,
|
||||
numPhysicalPartitions:
|
||||
offerDetail.content &&
|
||||
offerDetail.content.collectionThroughputInfo &&
|
||||
offerDetail.content.collectionThroughputInfo.numPhysicalPartitions
|
||||
};
|
||||
|
||||
databaseOffer.content.collectionThroughputInfo = offerThroughputInfo;
|
||||
(databaseOffer as DataModels.OfferWithHeaders).headers = offerDetail.headers;
|
||||
this.offer(databaseOffer);
|
||||
this.offer.valueHasMutated();
|
||||
|
||||
TelemetryProcessor.traceSuccess(
|
||||
Action.LoadOffers,
|
||||
{
|
||||
databaseAccountName: this.container.databaseAccount().name,
|
||||
defaultExperience: this.container.defaultExperience()
|
||||
},
|
||||
startKey
|
||||
);
|
||||
deferred.resolve();
|
||||
});
|
||||
},
|
||||
(error: any) => {
|
||||
this.container.isRefreshingExplorer(false);
|
||||
deferred.reject(error);
|
||||
TelemetryProcessor.traceFailure(
|
||||
Action.LoadOffers,
|
||||
{
|
||||
databaseAccountName: this.container.databaseAccount().name,
|
||||
defaultExperience: this.container.defaultExperience()
|
||||
},
|
||||
startKey
|
||||
);
|
||||
}
|
||||
);
|
||||
|
||||
return deferred.promise;
|
||||
}
|
||||
|
||||
public isDatabaseNodeSelected(): boolean {
|
||||
return (
|
||||
!this.isDatabaseExpanded() &&
|
||||
@@ -219,23 +145,13 @@ export default class Database implements ViewModels.Database {
|
||||
});
|
||||
}
|
||||
|
||||
public expandCollapseDatabase() {
|
||||
this.selectDatabase();
|
||||
if (this.isDatabaseExpanded()) {
|
||||
this.collapseDatabase();
|
||||
} else {
|
||||
this.expandDatabase();
|
||||
}
|
||||
this.container.onUpdateTabsButtons([]);
|
||||
this.container.tabsManager.refreshActiveTab(tab => tab.collection && tab.collection.getDatabase().rid === this.rid);
|
||||
}
|
||||
|
||||
public expandDatabase() {
|
||||
public async expandDatabase() {
|
||||
if (this.isDatabaseExpanded()) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.loadCollections();
|
||||
await this.loadOffer();
|
||||
await this.loadCollections();
|
||||
this.isDatabaseExpanded(true);
|
||||
TelemetryProcessor.trace(Action.ExpandTreeNode, ActionModifiers.Mark, {
|
||||
description: "Database node",
|
||||
@@ -259,32 +175,19 @@ export default class Database implements ViewModels.Database {
|
||||
});
|
||||
}
|
||||
|
||||
public loadCollections(): Q.Promise<void> {
|
||||
let collectionVMs: Collection[] = [];
|
||||
let deferred: Q.Deferred<void> = Q.defer<void>();
|
||||
public async loadCollections(): Promise<void> {
|
||||
const collectionVMs: Collection[] = [];
|
||||
const collections: DataModels.Collection[] = await readCollections(this.id());
|
||||
const deltaCollections = this.getDeltaCollections(collections);
|
||||
|
||||
readCollections(this.id()).then(
|
||||
(collections: DataModels.Collection[]) => {
|
||||
let collectionsToAddVMPromises: Q.Promise<any>[] = [];
|
||||
let deltaCollections = this.getDeltaCollections(collections);
|
||||
deltaCollections.toAdd.forEach((collection: DataModels.Collection) => {
|
||||
const collectionVM: Collection = new Collection(this.container, this.id(), collection, null, null);
|
||||
collectionVMs.push(collectionVM);
|
||||
});
|
||||
|
||||
deltaCollections.toAdd.forEach((collection: DataModels.Collection) => {
|
||||
const collectionVM: Collection = new Collection(this.container, this.id(), collection, null, null);
|
||||
collectionVMs.push(collectionVM);
|
||||
});
|
||||
|
||||
//merge collections
|
||||
this.addCollectionsToList(collectionVMs);
|
||||
this.deleteCollectionsFromList(deltaCollections.toDelete);
|
||||
|
||||
deferred.resolve();
|
||||
},
|
||||
(error: any) => {
|
||||
deferred.reject(error);
|
||||
}
|
||||
);
|
||||
|
||||
return deferred.promise;
|
||||
//merge collections
|
||||
this.addCollectionsToList(collectionVMs);
|
||||
this.deleteCollectionsFromList(deltaCollections.toDelete);
|
||||
}
|
||||
|
||||
public openAddCollection(database: Database, event: MouseEvent) {
|
||||
@@ -296,6 +199,17 @@ export default class Database implements ViewModels.Database {
|
||||
return _.find(this.collections(), (collection: ViewModels.Collection) => collection.id() === collectionId);
|
||||
}
|
||||
|
||||
public async loadOffer(): Promise<void> {
|
||||
if (!this.offer()) {
|
||||
const params: DataModels.ReadDatabaseOfferParams = {
|
||||
databaseId: this.id(),
|
||||
databaseResourceId: this.self,
|
||||
isServerless: this.container.isServerlessEnabled()
|
||||
};
|
||||
this.offer(await readDatabaseOffer(params));
|
||||
}
|
||||
}
|
||||
|
||||
private _getPendingThroughputSplitNotification(): Q.Promise<DataModels.Notification> {
|
||||
if (!this.container) {
|
||||
return Q.resolve(undefined);
|
||||
@@ -387,8 +301,4 @@ export default class Database implements ViewModels.Database {
|
||||
|
||||
this.collections(collectionsToKeep);
|
||||
}
|
||||
|
||||
private _getOfferForDatabase(offers: DataModels.Offer[], database: DataModels.Database): DataModels.Offer {
|
||||
return _.find(offers, (offer: DataModels.Offer) => offer.resource === database._self);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -170,14 +170,17 @@ export class ResourceTreeAdapter implements ReactAdapter {
|
||||
children: [],
|
||||
isSelected: () => this.isDataNodeSelected(database.rid, "Database", undefined),
|
||||
contextMenu: ResourceTreeContextMenuButtonFactory.createDatabaseContextMenu(this.container, database),
|
||||
onClick: isExpanded => {
|
||||
onClick: async isExpanded => {
|
||||
// Rewritten version of expandCollapseDatabase():
|
||||
if (!isExpanded) {
|
||||
database.expandDatabase();
|
||||
database.loadCollections();
|
||||
} else {
|
||||
if (isExpanded) {
|
||||
database.collapseDatabase();
|
||||
} else {
|
||||
if (databaseNode.children?.length === 0) {
|
||||
databaseNode.isLoading = true;
|
||||
}
|
||||
await database.expandDatabase();
|
||||
}
|
||||
databaseNode.isLoading = false;
|
||||
database.selectDatabase();
|
||||
this.container.onUpdateTabsButtons([]);
|
||||
this.container.tabsManager.refreshActiveTab(
|
||||
@@ -203,6 +206,12 @@ export class ResourceTreeAdapter implements ReactAdapter {
|
||||
databaseNode.children.push(this.buildCollectionNode(database, collection))
|
||||
);
|
||||
|
||||
database.collections.subscribe((collections: ViewModels.Collection[]) => {
|
||||
collections.forEach((collection: ViewModels.Collection) =>
|
||||
databaseNode.children.push(this.buildCollectionNode(database, collection))
|
||||
);
|
||||
});
|
||||
|
||||
return databaseNode;
|
||||
});
|
||||
|
||||
|
||||
Reference in New Issue
Block a user