Lazy load collection offer (#234)

This commit is contained in:
victor-meng 2020-09-28 12:54:28 -07:00 committed by GitHub
parent f582887fd8
commit 23c5d2d7e0
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
10 changed files with 291 additions and 279 deletions

View File

@ -184,86 +184,6 @@ export function deleteConflict(
); );
} }
export function readCollectionQuotaInfo(
collection: ViewModels.Collection,
options: any
): Q.Promise<DataModels.CollectionQuotaInfo> {
options = options || {};
options.populateQuotaInfo = true;
options.initialHeaders = options.initialHeaders || {};
options.initialHeaders[Constants.HttpHeaders.populatePartitionStatistics] = true;
return Q(
client()
.database(collection.databaseId)
.container(collection.id())
.read(options)
// TODO any needed because SDK does not properly type response.resource.statistics
.then((response: any) => {
let quota: DataModels.CollectionQuotaInfo = HeadersUtility.getQuota(response.headers);
quota["usageSizeInKB"] = response.resource.statistics.reduce(
(
previousValue: number,
currentValue: DataModels.Statistic,
currentIndex: number,
array: DataModels.Statistic[]
) => {
return previousValue + currentValue.sizeInKB;
},
0
);
quota["numPartitions"] = response.resource.statistics.length;
quota["uniqueKeyPolicy"] = collection.uniqueKeyPolicy; // TODO: Remove after refactoring (#119617)
return quota;
})
);
}
export function readOffers(options: any): Q.Promise<DataModels.Offer[]> {
if (options.isServerless) {
return Q([]); // Reading offers is not supported for serverless accounts
}
try {
if (configContext.platform === Platform.Portal) {
return sendCachedDataMessage<DataModels.Offer[]>(MessageTypes.AllOffers, [
(<any>window).dataExplorer.databaseAccount().id,
Constants.ClientDefaults.portalCacheTimeoutMs
]);
}
} catch (error) {
// If error getting cached Offers, continue on and read via SDK
}
return Q(
client()
.offers.readAll()
.fetchAll()
.then(response => response.resources)
.catch(error => {
// This should be removed when we can correctly identify if an account is serverless when connected using connection string too.
if (error.message.includes("Reading or replacing offers is not supported for serverless accounts")) {
return [];
}
throw error;
})
);
}
export function readOffer(requestedResource: DataModels.Offer, options: any): Q.Promise<DataModels.OfferWithHeaders> {
options = options || {};
options.initialHeaders = options.initialHeaders || {};
if (!OfferUtils.isOfferV1(requestedResource)) {
options.initialHeaders[Constants.HttpHeaders.populateCollectionThroughputInfo] = true;
}
return Q(
client()
.offer(requestedResource.id)
.read(options)
.then(response => ({ ...response.resource, headers: response.headers }))
);
}
export function refreshCachedOffers(): Q.Promise<void> { export function refreshCachedOffers(): Q.Promise<void> {
if (configContext.platform === Platform.Portal) { if (configContext.platform === Platform.Portal) {
return sendCachedDataMessage(MessageTypes.RefreshOffers, []); return sendCachedDataMessage(MessageTypes.RefreshOffers, []);

View File

@ -277,78 +277,3 @@ export function refreshCachedResources(options: any = {}): Q.Promise<void> {
export function refreshCachedOffers(): Q.Promise<void> { export function refreshCachedOffers(): Q.Promise<void> {
return DataAccessUtilityBase.refreshCachedOffers(); return DataAccessUtilityBase.refreshCachedOffers();
} }
export function readCollectionQuotaInfo(
collection: ViewModels.Collection,
options?: any
): Q.Promise<DataModels.CollectionQuotaInfo> {
var deferred = Q.defer<DataModels.CollectionQuotaInfo>();
const clearMessage = logConsoleProgress(`Querying quota info for container ${collection.id}`);
DataAccessUtilityBase.readCollectionQuotaInfo(collection, options)
.then(
(quota: DataModels.CollectionQuotaInfo) => {
deferred.resolve(quota);
},
(error: any) => {
logConsoleError(`Error while querying quota info for container ${collection.id}:\n ${JSON.stringify(error)}`);
Logger.logError(JSON.stringify(error), "ReadCollectionQuotaInfo", error.code);
sendNotificationForError(error);
deferred.reject(error);
}
)
.finally(() => {
clearMessage();
});
return deferred.promise;
}
export function readOffers(options: any = {}): Q.Promise<DataModels.Offer[]> {
var deferred = Q.defer<DataModels.Offer[]>();
const clearMessage = logConsoleProgress("Querying offers");
DataAccessUtilityBase.readOffers(options)
.then(
(offers: DataModels.Offer[]) => {
deferred.resolve(offers);
},
(error: any) => {
logConsoleError(`Error while querying offers:\n ${JSON.stringify(error)}`);
Logger.logError(JSON.stringify(error), "ReadOffers", error.code);
sendNotificationForError(error);
deferred.reject(error);
}
)
.finally(() => {
clearMessage();
});
return deferred.promise;
}
export function readOffer(
requestedResource: DataModels.Offer,
options: any = {}
): Q.Promise<DataModels.OfferWithHeaders> {
var deferred = Q.defer<DataModels.OfferWithHeaders>();
const clearMessage = logConsoleProgress("Querying offer");
DataAccessUtilityBase.readOffer(requestedResource, options)
.then(
(offer: DataModels.OfferWithHeaders) => {
deferred.resolve(offer);
},
(error: any) => {
logConsoleError(`Error while querying offer:\n ${JSON.stringify(error)}`);
Logger.logError(JSON.stringify(error), "ReadOffer", error.code);
sendNotificationForError(error);
deferred.reject(error);
}
)
.finally(() => {
clearMessage();
});
return deferred.promise;
}

View File

@ -0,0 +1,126 @@
import * as DataModels from "../../Contracts/DataModels";
import { AuthType } from "../../AuthType";
import { DefaultAccountExperienceType } from "../../DefaultAccountExperienceType";
import { HttpHeaders } from "../Constants";
import { RequestOptions } from "@azure/cosmos/dist-esm";
import { client } from "../CosmosClient";
import { getSqlContainerThroughput } from "../../Utils/arm/generatedClients/2020-04-01/sqlResources";
import { getMongoDBCollectionThroughput } from "../../Utils/arm/generatedClients/2020-04-01/mongoDBResources";
import { getCassandraTableThroughput } from "../../Utils/arm/generatedClients/2020-04-01/cassandraResources";
import { getGremlinGraphThroughput } from "../../Utils/arm/generatedClients/2020-04-01/gremlinResources";
import { getTableThroughput } from "../../Utils/arm/generatedClients/2020-04-01/tableResources";
import { logConsoleProgress, logConsoleError } from "../../Utils/NotificationConsoleUtils";
import { logError } from "../Logger";
import { readOffers } from "./readOffers";
import { sendNotificationForError } from "./sendNotificationForError";
import { userContext } from "../../UserContext";
export const readCollectionOffer = async (
params: DataModels.ReadCollectionOfferParams
): Promise<DataModels.OfferWithHeaders> => {
const clearMessage = logConsoleProgress(`Querying offer for collection ${params.collectionId}`);
let offerId = params.offerId;
if (!offerId) {
if (window.authType === AuthType.AAD && !userContext.useSDKOperations) {
try {
offerId = await getCollectionOfferIdWithARM(params.databaseId, params.collectionId);
} catch (error) {
clearMessage();
if (error.code !== "NotFound") {
throw error;
}
return undefined;
}
} else {
offerId = await getCollectionOfferIdWithSDK(params.collectionResourceId);
if (!offerId) {
clearMessage();
return undefined;
}
}
}
const options: RequestOptions = {
initialHeaders: {
[HttpHeaders.populateCollectionThroughputInfo]: true
}
};
try {
const response = await client()
.offer(offerId)
.read(options);
return (
response && {
...response.resource,
headers: response.headers
}
);
} catch (error) {
logConsoleError(`Error while querying offer for collection ${params.collectionId}:\n ${JSON.stringify(error)}`);
logError(JSON.stringify(error), "ReadCollectionOffer", error.code);
sendNotificationForError(error);
throw error;
} finally {
clearMessage();
}
};
const getCollectionOfferIdWithARM = async (databaseId: string, collectionId: string): Promise<string> => {
let rpResponse;
const subscriptionId = userContext.subscriptionId;
const resourceGroup = userContext.resourceGroup;
const accountName = userContext.databaseAccount.name;
const defaultExperience = userContext.defaultExperience;
switch (defaultExperience) {
case DefaultAccountExperienceType.DocumentDB:
rpResponse = await getSqlContainerThroughput(
subscriptionId,
resourceGroup,
accountName,
databaseId,
collectionId
);
break;
case DefaultAccountExperienceType.MongoDB:
rpResponse = await getMongoDBCollectionThroughput(
subscriptionId,
resourceGroup,
accountName,
databaseId,
collectionId
);
break;
case DefaultAccountExperienceType.Cassandra:
rpResponse = await getCassandraTableThroughput(
subscriptionId,
resourceGroup,
accountName,
databaseId,
collectionId
);
break;
case DefaultAccountExperienceType.Graph:
rpResponse = await getGremlinGraphThroughput(
subscriptionId,
resourceGroup,
accountName,
databaseId,
collectionId
);
break;
case DefaultAccountExperienceType.Table:
rpResponse = await getTableThroughput(subscriptionId, resourceGroup, accountName, collectionId);
break;
default:
throw new Error(`Unsupported default experience type: ${defaultExperience}`);
}
return rpResponse?.name;
};
const getCollectionOfferIdWithSDK = async (collectionResourceId: string): Promise<string> => {
const offers = await readOffers();
const offer = offers.find(offer => offer.resource === collectionResourceId);
return offer?.id;
};

View File

@ -0,0 +1,48 @@
import * as DataModels from "../../Contracts/DataModels";
import * as HeadersUtility from "../HeadersUtility";
import * as ViewModels from "../../Contracts/ViewModels";
import { ContainerDefinition, Resource } from "@azure/cosmos";
import { HttpHeaders } from "../Constants";
import { RequestOptions } from "@azure/cosmos/dist-esm";
import { client } from "../CosmosClient";
import { logConsoleProgress, logConsoleError } from "../../Utils/NotificationConsoleUtils";
import { logError } from "../Logger";
import { sendNotificationForError } from "./sendNotificationForError";
interface ResourceWithStatistics {
statistics: DataModels.Statistic[];
}
export const readCollectionQuotaInfo = async (
collection: ViewModels.Collection
): Promise<DataModels.CollectionQuotaInfo> => {
const clearMessage = logConsoleProgress(`Querying containers for database ${collection.id}`);
const options: RequestOptions = {};
options.populateQuotaInfo = true;
options.initialHeaders = options.initialHeaders || {};
options.initialHeaders[HttpHeaders.populatePartitionStatistics] = true;
try {
const response = await client()
.database(collection.databaseId)
.container(collection.id())
.read(options);
const quota: DataModels.CollectionQuotaInfo = HeadersUtility.getQuota(response.headers);
const resource = response.resource as ContainerDefinition & Resource & ResourceWithStatistics;
quota["usageSizeInKB"] = resource.statistics.reduce(
(previousValue: number, currentValue: DataModels.Statistic) => previousValue + currentValue.sizeInKB,
0
);
quota["numPartitions"] = resource.statistics.length;
quota["uniqueKeyPolicy"] = collection.uniqueKeyPolicy; // TODO: Remove after refactoring (#119617)
return quota;
} catch (error) {
logConsoleError(`Error while querying quota info for container ${collection.id}:\n ${JSON.stringify(error)}`);
logError(JSON.stringify(error), "ReadCollectionQuotaInfo", error.code);
sendNotificationForError(error);
throw error;
} finally {
clearMessage();
}
};

View File

@ -8,12 +8,16 @@ import { getSqlDatabaseThroughput } from "../../Utils/arm/generatedClients/2020-
import { getMongoDBDatabaseThroughput } from "../../Utils/arm/generatedClients/2020-04-01/mongoDBResources"; import { getMongoDBDatabaseThroughput } from "../../Utils/arm/generatedClients/2020-04-01/mongoDBResources";
import { getCassandraKeyspaceThroughput } from "../../Utils/arm/generatedClients/2020-04-01/cassandraResources"; import { getCassandraKeyspaceThroughput } from "../../Utils/arm/generatedClients/2020-04-01/cassandraResources";
import { getGremlinDatabaseThroughput } from "../../Utils/arm/generatedClients/2020-04-01/gremlinResources"; import { getGremlinDatabaseThroughput } from "../../Utils/arm/generatedClients/2020-04-01/gremlinResources";
import { logConsoleProgress, logConsoleError } from "../../Utils/NotificationConsoleUtils";
import { logError } from "../Logger";
import { readOffers } from "./readOffers"; import { readOffers } from "./readOffers";
import { sendNotificationForError } from "./sendNotificationForError";
import { userContext } from "../../UserContext"; import { userContext } from "../../UserContext";
export const readDatabaseOffer = async ( export const readDatabaseOffer = async (
params: DataModels.ReadDatabaseOfferParams params: DataModels.ReadDatabaseOfferParams
): Promise<DataModels.OfferWithHeaders> => { ): Promise<DataModels.OfferWithHeaders> => {
const clearMessage = logConsoleProgress(`Querying offer for database ${params.databaseId}`);
let offerId = params.offerId; let offerId = params.offerId;
if (!offerId) { if (!offerId) {
if ( if (
@ -24,14 +28,16 @@ export const readDatabaseOffer = async (
try { try {
offerId = await getDatabaseOfferIdWithARM(params.databaseId); offerId = await getDatabaseOfferIdWithARM(params.databaseId);
} catch (error) { } catch (error) {
clearMessage();
if (error.code !== "NotFound") { if (error.code !== "NotFound") {
throw new Error(error); throw new error();
} }
return undefined; return undefined;
} }
} else { } else {
offerId = await getDatabaseOfferIdWithSDK(params.databaseResourceId); offerId = await getDatabaseOfferIdWithSDK(params.databaseResourceId);
if (!offerId) { if (!offerId) {
clearMessage();
return undefined; return undefined;
} }
} }
@ -43,15 +49,24 @@ export const readDatabaseOffer = async (
} }
}; };
const response = await client() try {
.offer(offerId) const response = await client()
.read(options); .offer(offerId)
return ( .read(options);
response && { return (
...response.resource, response && {
headers: response.headers ...response.resource,
} headers: response.headers
); }
);
} catch (error) {
logConsoleError(`Error while querying offer for database ${params.databaseId}:\n ${JSON.stringify(error)}`);
logError(JSON.stringify(error), "ReadDatabaseOffer", error.code);
sendNotificationForError(error);
throw error;
} finally {
clearMessage();
}
}; };
const getDatabaseOfferIdWithARM = async (databaseId: string): Promise<string> => { const getDatabaseOfferIdWithARM = async (databaseId: string): Promise<string> => {

View File

@ -3,10 +3,14 @@ import { ClientDefaults } from "../Constants";
import { MessageTypes } from "../../Contracts/ExplorerContracts"; import { MessageTypes } from "../../Contracts/ExplorerContracts";
import { Platform, configContext } from "../../ConfigContext"; import { Platform, configContext } from "../../ConfigContext";
import { client } from "../CosmosClient"; import { client } from "../CosmosClient";
import { logConsoleProgress, logConsoleError } from "../../Utils/NotificationConsoleUtils";
import { logError } from "../Logger";
import { sendCachedDataMessage } from "../MessageHandler"; import { sendCachedDataMessage } from "../MessageHandler";
import { sendNotificationForError } from "./sendNotificationForError";
import { userContext } from "../../UserContext"; import { userContext } from "../../UserContext";
export const readOffers = async (): Promise<Offer[]> => { export const readOffers = async (): Promise<Offer[]> => {
const clearMessage = logConsoleProgress(`Querying offers`);
try { try {
if (configContext.platform === Platform.Portal) { if (configContext.platform === Platform.Portal) {
return sendCachedDataMessage<Offer[]>(MessageTypes.AllOffers, [ return sendCachedDataMessage<Offer[]>(MessageTypes.AllOffers, [
@ -18,15 +22,22 @@ export const readOffers = async (): Promise<Offer[]> => {
// If error getting cached Offers, continue on and read via SDK // If error getting cached Offers, continue on and read via SDK
} }
return client() try {
.offers.readAll() const response = await client()
.fetchAll() .offers.readAll()
.then(response => response.resources) .fetchAll();
.catch(error => { return response?.resources;
// This should be removed when we can correctly identify if an account is serverless when connected using connection string too. } catch (error) {
if (error.message.includes("Reading or replacing offers is not supported for serverless accounts")) { // This should be removed when we can correctly identify if an account is serverless when connected using connection string too.
return []; if (error.message.includes("Reading or replacing offers is not supported for serverless accounts")) {
} return [];
throw error; }
});
logConsoleError(`Error while querying offers:\n ${JSON.stringify(error)}`);
logError(JSON.stringify(error), "ReadOffers", error.code);
sendNotificationForError(error);
throw error;
} finally {
clearMessage();
}
}; };

View File

@ -296,6 +296,13 @@ export interface ReadDatabaseOfferParams {
offerId?: string; offerId?: string;
} }
export interface ReadCollectionOfferParams {
collectionId: string;
databaseId: string;
collectionResourceId?: string;
offerId?: string;
}
export interface Notification { export interface Notification {
id: string; id: string;
kind: string; kind: string;

View File

@ -133,8 +133,7 @@ export interface Collection extends CollectionBase {
onMongoDBDocumentsClick(): void; onMongoDBDocumentsClick(): void;
openTab(): void; openTab(): void;
onSettingsClick: () => void; onSettingsClick: () => Promise<void>;
readSettings(): Q.Promise<void>;
onDeleteCollectionContextMenuClick(source: Collection, event: MouseEvent): void; onDeleteCollectionContextMenuClick(source: Collection, event: MouseEvent): void;
onNewGraphClick(): void; onNewGraphClick(): void;
@ -162,6 +161,7 @@ export interface Collection extends CollectionBase {
loadUserDefinedFunctions(): Promise<any>; loadUserDefinedFunctions(): Promise<any>;
loadStoredProcedures(): Promise<any>; loadStoredProcedures(): Promise<any>;
loadTriggers(): Promise<any>; loadTriggers(): Promise<any>;
loadOffer(): Promise<void>;
createStoredProcedureNode(data: StoredProcedureDefinition & Resource): StoredProcedure; createStoredProcedureNode(data: StoredProcedureDefinition & Resource): StoredProcedure;
createUserDefinedFunctionNode(data: UserDefinedFunctionDefinition & Resource): UserDefinedFunction; createUserDefinedFunctionNode(data: UserDefinedFunctionDefinition & Resource): UserDefinedFunction;

View File

@ -1181,7 +1181,6 @@ export default class SettingsTab extends TabsBase implements ViewModels.WaitsFor
this.container.isRefreshingExplorer(false); this.container.isRefreshingExplorer(false);
this._setBaseline(); this._setBaseline();
this.collection.readSettings();
this._wasAutopilotOriginallySet(this.isAutoPilotSelected()); this._wasAutopilotOriginallySet(this.isAutoPilotSelected());
TelemetryProcessor.traceSuccess( TelemetryProcessor.traceSuccess(
Action.UpdateSettings, Action.UpdateSettings,

View File

@ -8,7 +8,9 @@ import * as Constants from "../../Common/Constants";
import { readStoredProcedures } from "../../Common/dataAccess/readStoredProcedures"; import { readStoredProcedures } from "../../Common/dataAccess/readStoredProcedures";
import { readTriggers } from "../../Common/dataAccess/readTriggers"; import { readTriggers } from "../../Common/dataAccess/readTriggers";
import { readUserDefinedFunctions } from "../../Common/dataAccess/readUserDefinedFunctions"; import { readUserDefinedFunctions } from "../../Common/dataAccess/readUserDefinedFunctions";
import { createDocument, readCollectionQuotaInfo, readOffer, readOffers } from "../../Common/DocumentClientUtilityBase"; import { createDocument } from "../../Common/DocumentClientUtilityBase";
import { readCollectionOffer } from "../../Common/dataAccess/readCollectionOffer";
import { readCollectionQuotaInfo } from "../../Common/dataAccess/readCollectionQuotaInfo";
import * as Logger from "../../Common/Logger"; import * as Logger from "../../Common/Logger";
import { configContext } from "../../ConfigContext"; import { configContext } from "../../ConfigContext";
import * as DataModels from "../../Contracts/DataModels"; import * as DataModels from "../../Contracts/DataModels";
@ -541,7 +543,7 @@ export default class Collection implements ViewModels.Collection {
} }
}; };
public onSettingsClick = () => { public onSettingsClick = async (): Promise<void> => {
this.container.selectedNode(this); this.container.selectedNode(this);
this.selectedSubnodeKind(ViewModels.CollectionTabKind.Settings); this.selectedSubnodeKind(ViewModels.CollectionTabKind.Settings);
TelemetryProcessor.trace(Action.SelectItem, ActionModifiers.Mark, { TelemetryProcessor.trace(Action.SelectItem, ActionModifiers.Mark, {
@ -553,6 +555,7 @@ export default class Collection implements ViewModels.Collection {
dataExplorerArea: Constants.Areas.ResourceTree dataExplorerArea: Constants.Areas.ResourceTree
}); });
await this.loadOffer();
const tabTitle = !this.offer() ? "Settings" : "Scale & Settings"; const tabTitle = !this.offer() ? "Settings" : "Scale & Settings";
const pendingNotificationsPromise: Q.Promise<DataModels.Notification> = this._getPendingThroughputSplitNotification(); const pendingNotificationsPromise: Q.Promise<DataModels.Notification> = this._getPendingThroughputSplitNotification();
const matchingTabs = this.container.tabsManager.getTabs(ViewModels.CollectionTabKind.Settings, tab => { const matchingTabs = this.container.tabsManager.getTabs(ViewModels.CollectionTabKind.Settings, tab => {
@ -570,12 +573,12 @@ export default class Collection implements ViewModels.Collection {
tabTitle: tabTitle tabTitle: tabTitle
}); });
Q.all([pendingNotificationsPromise, this.readSettings()]).then( pendingNotificationsPromise.then(
(data: any) => { (data: any) => {
const pendingNotification: DataModels.Notification = data && data[0]; const pendingNotification: DataModels.Notification = data && data[0];
settingsTab = new SettingsTab({ settingsTab = new SettingsTab({
tabKind: ViewModels.CollectionTabKind.Settings, tabKind: ViewModels.CollectionTabKind.Settings,
title: !this.offer() ? "Settings" : "Scale & Settings", title: tabTitle,
tabPath: "", tabPath: "",
collection: this, collection: this,
@ -624,103 +627,12 @@ export default class Collection implements ViewModels.Collection {
} }
}; };
public readSettings(): Q.Promise<void> { private async loadCollectionQuotaInfo(): Promise<void> {
const deferred: Q.Deferred<void> = Q.defer<void>();
this.container.isRefreshingExplorer(true);
const collectionDataModel: DataModels.Collection = <DataModels.Collection>{
id: this.id(),
_rid: this.rid,
_self: this.self,
defaultTtl: this.defaultTtl(),
indexingPolicy: this.indexingPolicy(),
partitionKey: this.partitionKey
};
const startKey: number = TelemetryProcessor.traceStart(Action.LoadOffers, {
databaseAccountName: this.container.databaseAccount().name,
databaseName: this.databaseId,
collectionName: this.id(),
defaultExperience: this.container.defaultExperience()
});
// TODO: Use the collection entity cache to get quota info // TODO: Use the collection entity cache to get quota info
const quotaInfoPromise: Q.Promise<DataModels.CollectionQuotaInfo> = readCollectionQuotaInfo(this); const quotaInfoWithUniqueKeyPolicy = await readCollectionQuotaInfo(this);
const offerInfoPromise: Q.Promise<DataModels.Offer[]> = readOffers({ this.uniqueKeyPolicy = quotaInfoWithUniqueKeyPolicy.uniqueKeyPolicy;
isServerless: this.container.isServerlessEnabled() const quotaInfo = _.omit(quotaInfoWithUniqueKeyPolicy, "uniqueKeyPolicy");
}); this.quotaInfo(quotaInfo);
Q.all([quotaInfoPromise, offerInfoPromise]).then(
() => {
this.container.isRefreshingExplorer(false);
const quotaInfoWithUniqueKeyPolicy: DataModels.CollectionQuotaInfo = quotaInfoPromise.valueOf();
this.uniqueKeyPolicy = quotaInfoWithUniqueKeyPolicy.uniqueKeyPolicy;
const quotaInfo = _.omit(quotaInfoWithUniqueKeyPolicy, "uniqueKeyPolicy");
const collectionOffer = this._getOfferForCollection(offerInfoPromise.valueOf(), collectionDataModel);
if (!collectionOffer) {
this.quotaInfo(quotaInfo);
TelemetryProcessor.traceSuccess(
Action.LoadOffers,
{
databaseAccountName: this.container.databaseAccount().name,
databaseName: this.databaseId,
collectionName: this.id(),
defaultExperience: this.container.defaultExperience()
},
startKey
);
deferred.resolve();
return;
}
readOffer(collectionOffer).then((offerDetail: DataModels.OfferWithHeaders) => {
if (OfferUtils.isNotOfferV1(collectionOffer)) {
const offerThroughputInfo: DataModels.OfferThroughputInfo = {
minimumRUForCollection:
offerDetail.content &&
offerDetail.content.collectionThroughputInfo &&
offerDetail.content.collectionThroughputInfo.minimumRUForCollection,
numPhysicalPartitions:
offerDetail.content &&
offerDetail.content.collectionThroughputInfo &&
offerDetail.content.collectionThroughputInfo.numPhysicalPartitions
};
collectionOffer.content.collectionThroughputInfo = offerThroughputInfo;
}
(collectionOffer as DataModels.OfferWithHeaders).headers = offerDetail.headers;
this.offer(collectionOffer);
this.offer.valueHasMutated();
this.quotaInfo(quotaInfo);
TelemetryProcessor.traceSuccess(
Action.LoadOffers,
{
databaseAccountName: this.container.databaseAccount().name,
databaseName: this.databaseId,
collectionName: this.id(),
defaultExperience: this.container.defaultExperience(),
offerVersion: collectionOffer && collectionOffer.offerVersion
},
startKey
);
deferred.resolve();
});
},
(error: any) => {
this.container.isRefreshingExplorer(false);
deferred.reject(error);
TelemetryProcessor.traceFailure(
Action.LoadOffers,
{
databaseAccountName: this.container.databaseAccount().name,
databaseName: this.databaseId,
collectionName: this.id(),
defaultExperience: this.container.defaultExperience()
},
startKey
);
}
);
return deferred.promise;
} }
public onNewQueryClick(source: any, event: MouseEvent, queryText?: string) { public onNewQueryClick(source: any, event: MouseEvent, queryText?: string) {
@ -1399,4 +1311,53 @@ export default class Collection implements ViewModels.Collection {
public getDatabase(): ViewModels.Database { public getDatabase(): ViewModels.Database {
return this.container.findDatabaseWithId(this.databaseId); return this.container.findDatabaseWithId(this.databaseId);
} }
public async loadOffer(): Promise<void> {
if (!this.container.isServerlessEnabled() && !this.offer()) {
this.container.isRefreshingExplorer(true);
const startKey: number = TelemetryProcessor.traceStart(Action.LoadOffers, {
databaseAccountName: this.container.databaseAccount().name,
databaseName: this.databaseId,
collectionName: this.id(),
defaultExperience: this.container.defaultExperience()
});
const params: DataModels.ReadCollectionOfferParams = {
collectionId: this.id(),
collectionResourceId: this.self,
databaseId: this.databaseId
};
try {
this.offer(await readCollectionOffer(params));
await this.loadCollectionQuotaInfo();
TelemetryProcessor.traceSuccess(
Action.LoadOffers,
{
databaseAccountName: this.container.databaseAccount().name,
databaseName: this.databaseId,
collectionName: this.id(),
defaultExperience: this.container.defaultExperience(),
offerVersion: this.offer()?.offerVersion
},
startKey
);
} catch (error) {
TelemetryProcessor.traceFailure(
Action.LoadOffers,
{
databaseAccountName: this.container.databaseAccount().name,
databaseName: this.databaseId,
collectionName: this.id(),
defaultExperience: this.container.defaultExperience()
},
startKey
);
throw error;
} finally {
this.container.isRefreshingExplorer(false);
}
}
}
} }