mirror of
https://github.com/Azure/cosmos-explorer.git
synced 2025-12-19 17:01:13 +00:00
Move updateCollection to ARM (#152)
This commit is contained in:
@@ -7,6 +7,7 @@ import Database from "../Tree/Database";
|
||||
import Explorer from "../Explorer";
|
||||
import SettingsTab from "../Tabs/SettingsTab";
|
||||
import { CommandButtonComponentProps } from "../Controls/CommandButton/CommandButtonComponent";
|
||||
import { IndexingPolicies } from "../../Shared/Constants";
|
||||
|
||||
describe("Settings tab", () => {
|
||||
const baseCollection: DataModels.Collection = {
|
||||
@@ -16,7 +17,7 @@ describe("Settings tab", () => {
|
||||
mode: DataModels.ConflictResolutionMode.LastWriterWins,
|
||||
conflictResolutionPath: "/_ts"
|
||||
},
|
||||
indexingPolicy: {},
|
||||
indexingPolicy: IndexingPolicies.SharedDatabaseDefault,
|
||||
_rid: "",
|
||||
_self: "",
|
||||
_etag: "",
|
||||
@@ -51,7 +52,7 @@ describe("Settings tab", () => {
|
||||
defaultTtl: 200,
|
||||
partitionKey: null,
|
||||
conflictResolutionPolicy: null,
|
||||
indexingPolicy: {},
|
||||
indexingPolicy: IndexingPolicies.SharedDatabaseDefault,
|
||||
_rid: "",
|
||||
_self: "",
|
||||
_etag: "",
|
||||
@@ -345,7 +346,6 @@ describe("Settings tab", () => {
|
||||
|
||||
const offer: DataModels.Offer = null;
|
||||
const defaultTtl = 200;
|
||||
const indexingPolicy = {};
|
||||
const database = new Database(explorer, baseDatabase, null);
|
||||
const conflictResolutionPolicy = {
|
||||
mode: DataModels.ConflictResolutionMode.LastWriterWins,
|
||||
@@ -367,7 +367,7 @@ describe("Settings tab", () => {
|
||||
}
|
||||
: null,
|
||||
conflictResolutionPolicy: conflictResolutionPolicy,
|
||||
indexingPolicy: indexingPolicy,
|
||||
indexingPolicy: IndexingPolicies.SharedDatabaseDefault,
|
||||
_rid: "",
|
||||
_self: "",
|
||||
_etag: "",
|
||||
|
||||
@@ -17,7 +17,8 @@ import { Action } from "../../Shared/Telemetry/TelemetryConstants";
|
||||
import { PlatformType } from "../../PlatformType";
|
||||
import { RequestOptions } from "@azure/cosmos/dist-esm";
|
||||
import Explorer from "../Explorer";
|
||||
import { updateOffer, updateCollection } from "../../Common/DocumentClientUtilityBase";
|
||||
import { updateOffer } from "../../Common/DocumentClientUtilityBase";
|
||||
import { updateCollection } from "../../Common/dataAccess/updateCollection";
|
||||
import { CommandButtonComponentProps } from "../Controls/CommandButton/CommandButtonComponent";
|
||||
import { userContext } from "../../UserContext";
|
||||
import { updateOfferThroughputBeyondLimit } from "../../Common/dataAccess/updateOfferThroughputBeyondLimit";
|
||||
@@ -1009,8 +1010,7 @@ export default class SettingsTab extends TabsBase implements ViewModels.WaitsFor
|
||||
);
|
||||
}
|
||||
|
||||
public onSaveClick = (): Q.Promise<any> => {
|
||||
let promises: Q.Promise<void>[] = [];
|
||||
public onSaveClick = async (): Promise<any> => {
|
||||
this.isExecutionError(false);
|
||||
|
||||
this.isExecuting(true);
|
||||
@@ -1023,50 +1023,60 @@ export default class SettingsTab extends TabsBase implements ViewModels.WaitsFor
|
||||
|
||||
const newCollectionAttributes: any = {};
|
||||
|
||||
if (this.shouldUpdateCollection()) {
|
||||
let defaultTtl: number;
|
||||
switch (this.timeToLive()) {
|
||||
case "on":
|
||||
defaultTtl = Number(this.timeToLiveSeconds());
|
||||
break;
|
||||
case "on-nodefault":
|
||||
defaultTtl = -1;
|
||||
break;
|
||||
case "off":
|
||||
default:
|
||||
defaultTtl = undefined;
|
||||
break;
|
||||
}
|
||||
try {
|
||||
if (this.shouldUpdateCollection()) {
|
||||
let defaultTtl: number;
|
||||
switch (this.timeToLive()) {
|
||||
case "on":
|
||||
defaultTtl = Number(this.timeToLiveSeconds());
|
||||
break;
|
||||
case "on-nodefault":
|
||||
defaultTtl = -1;
|
||||
break;
|
||||
case "off":
|
||||
default:
|
||||
defaultTtl = undefined;
|
||||
break;
|
||||
}
|
||||
|
||||
newCollectionAttributes.defaultTtl = defaultTtl;
|
||||
newCollectionAttributes.defaultTtl = defaultTtl;
|
||||
|
||||
newCollectionAttributes.indexingPolicy = this.indexingPolicyContent();
|
||||
newCollectionAttributes.indexingPolicy = this.indexingPolicyContent();
|
||||
|
||||
newCollectionAttributes.changeFeedPolicy =
|
||||
this.changeFeedPolicyVisible() && this.changeFeedPolicyToggled() === ChangeFeedPolicyToggledState.On
|
||||
? ({
|
||||
retentionDuration: Constants.BackendDefaults.maxChangeFeedRetentionDuration
|
||||
} as DataModels.ChangeFeedPolicy)
|
||||
newCollectionAttributes.changeFeedPolicy =
|
||||
this.changeFeedPolicyVisible() && this.changeFeedPolicyToggled() === ChangeFeedPolicyToggledState.On
|
||||
? ({
|
||||
retentionDuration: Constants.BackendDefaults.maxChangeFeedRetentionDuration
|
||||
} as DataModels.ChangeFeedPolicy)
|
||||
: undefined;
|
||||
|
||||
newCollectionAttributes.analyticalStorageTtl = this.isAnalyticalStorageEnabled
|
||||
? this.analyticalStorageTtlSelection() === "on"
|
||||
? Number(this.analyticalStorageTtlSeconds())
|
||||
: Constants.AnalyticalStorageTtl.Infinite
|
||||
: undefined;
|
||||
|
||||
newCollectionAttributes.analyticalStorageTtl = this.isAnalyticalStorageEnabled
|
||||
? this.analyticalStorageTtlSelection() === "on"
|
||||
? Number(this.analyticalStorageTtlSeconds())
|
||||
: Constants.AnalyticalStorageTtl.Infinite
|
||||
: undefined;
|
||||
newCollectionAttributes.geospatialConfig = {
|
||||
type: this.geospatialConfigType()
|
||||
};
|
||||
|
||||
newCollectionAttributes.geospatialConfig = {
|
||||
type: this.geospatialConfigType()
|
||||
};
|
||||
const conflictResolutionChanges: DataModels.ConflictResolutionPolicy = this.getUpdatedConflictResolutionPolicy();
|
||||
if (!!conflictResolutionChanges) {
|
||||
newCollectionAttributes.conflictResolutionPolicy = conflictResolutionChanges;
|
||||
}
|
||||
|
||||
const conflictResolutionChanges: DataModels.ConflictResolutionPolicy = this.getUpdatedConflictResolutionPolicy();
|
||||
if (!!conflictResolutionChanges) {
|
||||
newCollectionAttributes.conflictResolutionPolicy = conflictResolutionChanges;
|
||||
}
|
||||
const newCollection: DataModels.Collection = _.extend(
|
||||
{},
|
||||
this.collection.rawDataModel,
|
||||
newCollectionAttributes
|
||||
);
|
||||
const updatedCollection: DataModels.Collection = await updateCollection(
|
||||
this.collection.databaseId,
|
||||
this.collection.id(),
|
||||
newCollection
|
||||
);
|
||||
|
||||
const newCollection: DataModels.Collection = _.extend({}, this.collection.rawDataModel, newCollectionAttributes);
|
||||
const updateCollectionPromise = updateCollection(this.collection.databaseId, this.collection, newCollection).then(
|
||||
(updatedCollection: DataModels.Collection) => {
|
||||
if (updatedCollection) {
|
||||
this.collection.rawDataModel = updatedCollection;
|
||||
this.collection.defaultTtl(updatedCollection.defaultTtl);
|
||||
this.collection.analyticalStorageTtl(updatedCollection.analyticalStorageTtl);
|
||||
@@ -1076,164 +1086,133 @@ export default class SettingsTab extends TabsBase implements ViewModels.WaitsFor
|
||||
this.collection.changeFeedPolicy(updatedCollection.changeFeedPolicy);
|
||||
this.collection.geospatialConfig(updatedCollection.geospatialConfig);
|
||||
}
|
||||
);
|
||||
|
||||
promises.push(updateCollectionPromise);
|
||||
}
|
||||
|
||||
if (
|
||||
this.throughput.editableIsDirty() ||
|
||||
this.rupm.editableIsDirty() ||
|
||||
this._isAutoPilotDirty() ||
|
||||
this._hasProvisioningTypeChanged()
|
||||
) {
|
||||
const newThroughput = this.throughput();
|
||||
const isRUPerMinuteThroughputEnabled: boolean = this.rupm() === Constants.RUPMStates.on;
|
||||
let newOffer: DataModels.Offer = _.extend({}, this.collection.offer());
|
||||
const originalThroughputValue: number = this.throughput.getEditableOriginalValue();
|
||||
|
||||
if (newOffer.content) {
|
||||
newOffer.content.offerThroughput = newThroughput;
|
||||
newOffer.content.offerIsRUPerMinuteThroughputEnabled = isRUPerMinuteThroughputEnabled;
|
||||
} else {
|
||||
newOffer = _.extend({}, newOffer, {
|
||||
content: {
|
||||
offerThroughput: newThroughput,
|
||||
offerIsRUPerMinuteThroughputEnabled: isRUPerMinuteThroughputEnabled
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
const headerOptions: RequestOptions = { initialHeaders: {} };
|
||||
|
||||
if (this.isAutoPilotSelected()) {
|
||||
if (!this.hasAutoPilotV2FeatureFlag()) {
|
||||
newOffer.content.offerAutopilotSettings = {
|
||||
maxThroughput: this.autoPilotThroughput()
|
||||
};
|
||||
} else {
|
||||
newOffer.content.offerAutopilotSettings = {
|
||||
tier: this.selectedAutoPilotTier()
|
||||
};
|
||||
}
|
||||
|
||||
// user has changed from provisioned --> autoscale
|
||||
if (!this.hasAutoPilotV2FeatureFlag() && this._hasProvisioningTypeChanged()) {
|
||||
headerOptions.initialHeaders[Constants.HttpHeaders.migrateOfferToAutopilot] = "true";
|
||||
delete newOffer.content.offerAutopilotSettings;
|
||||
} else {
|
||||
delete newOffer.content.offerThroughput;
|
||||
}
|
||||
} else {
|
||||
this.isAutoPilotSelected(false);
|
||||
this.userCanChangeProvisioningTypes(false || !this.hasAutoPilotV2FeatureFlag());
|
||||
|
||||
// user has changed from autoscale --> provisioned
|
||||
if (!this.hasAutoPilotV2FeatureFlag() && this._hasProvisioningTypeChanged()) {
|
||||
headerOptions.initialHeaders[Constants.HttpHeaders.migrateOfferToManualThroughput] = "true";
|
||||
} else {
|
||||
delete newOffer.content.offerAutopilotSettings;
|
||||
}
|
||||
}
|
||||
|
||||
if (
|
||||
this.maxRUs() <= SharedConstants.CollectionCreation.DefaultCollectionRUs1Million &&
|
||||
newThroughput > SharedConstants.CollectionCreation.DefaultCollectionRUs1Million &&
|
||||
this.container != null
|
||||
this.throughput.editableIsDirty() ||
|
||||
this.rupm.editableIsDirty() ||
|
||||
this._isAutoPilotDirty() ||
|
||||
this._hasProvisioningTypeChanged()
|
||||
) {
|
||||
const requestPayload = {
|
||||
subscriptionId: userContext.subscriptionId,
|
||||
databaseAccountName: userContext.databaseAccount.name,
|
||||
resourceGroup: userContext.resourceGroup,
|
||||
databaseName: this.collection.databaseId,
|
||||
collectionName: this.collection.id(),
|
||||
throughput: newThroughput,
|
||||
offerIsRUPerMinuteThroughputEnabled: isRUPerMinuteThroughputEnabled
|
||||
};
|
||||
const updateOfferBeyondLimitPromise = updateOfferThroughputBeyondLimit(requestPayload).then(
|
||||
() => {
|
||||
this.collection.offer().content.offerThroughput = originalThroughputValue;
|
||||
this.throughput(originalThroughputValue);
|
||||
this.notificationStatusInfo(
|
||||
throughputApplyDelayedMessage(
|
||||
this.isAutoPilotSelected(),
|
||||
originalThroughputValue,
|
||||
this._getThroughputUnit(),
|
||||
this.collection.databaseId,
|
||||
this.collection.id(),
|
||||
newThroughput
|
||||
)
|
||||
);
|
||||
this.throughput.valueHasMutated(); // force component re-render
|
||||
},
|
||||
(error: any) => {
|
||||
TelemetryProcessor.traceFailure(
|
||||
Action.UpdateSettings,
|
||||
{
|
||||
databaseAccountName: this.container.databaseAccount().name,
|
||||
databaseName: this.collection && this.collection.databaseId,
|
||||
collectionName: this.collection && this.collection.id(),
|
||||
defaultExperience: this.container.defaultExperience(),
|
||||
dataExplorerArea: Constants.Areas.Tab,
|
||||
tabTitle: this.tabTitle(),
|
||||
error: error
|
||||
},
|
||||
startKey
|
||||
);
|
||||
}
|
||||
);
|
||||
promises.push(Q(updateOfferBeyondLimitPromise));
|
||||
} else {
|
||||
const updateOfferPromise = updateOffer(this.collection.offer(), newOffer, headerOptions).then(
|
||||
(updatedOffer: DataModels.Offer) => {
|
||||
this.collection.offer(updatedOffer);
|
||||
this.collection.offer.valueHasMutated();
|
||||
}
|
||||
);
|
||||
const newThroughput = this.throughput();
|
||||
const isRUPerMinuteThroughputEnabled: boolean = this.rupm() === Constants.RUPMStates.on;
|
||||
let newOffer: DataModels.Offer = _.extend({}, this.collection.offer());
|
||||
const originalThroughputValue: number = this.throughput.getEditableOriginalValue();
|
||||
|
||||
promises.push(updateOfferPromise);
|
||||
}
|
||||
}
|
||||
|
||||
if (promises.length === 0) {
|
||||
this.isExecuting(false);
|
||||
}
|
||||
|
||||
return Q.all(promises)
|
||||
.then(
|
||||
() => {
|
||||
this.container.isRefreshingExplorer(false);
|
||||
this._setBaseline();
|
||||
this.collection.readSettings();
|
||||
this._wasAutopilotOriginallySet(this.isAutoPilotSelected());
|
||||
TelemetryProcessor.traceSuccess(
|
||||
Action.UpdateSettings,
|
||||
{
|
||||
databaseAccountName: this.container.databaseAccount().name,
|
||||
defaultExperience: this.container.defaultExperience(),
|
||||
dataExplorerArea: Constants.Areas.Tab,
|
||||
tabTitle: this.tabTitle()
|
||||
},
|
||||
startKey
|
||||
);
|
||||
},
|
||||
(reason: any) => {
|
||||
this.container.isRefreshingExplorer(false);
|
||||
this.isExecutionError(true);
|
||||
console.error(reason);
|
||||
TelemetryProcessor.traceFailure(
|
||||
Action.UpdateSettings,
|
||||
{
|
||||
databaseAccountName: this.container.databaseAccount().name,
|
||||
defaultExperience: this.container.defaultExperience(),
|
||||
dataExplorerArea: Constants.Areas.Tab,
|
||||
tabTitle: this.tabTitle()
|
||||
},
|
||||
startKey
|
||||
);
|
||||
if (newOffer.content) {
|
||||
newOffer.content.offerThroughput = newThroughput;
|
||||
newOffer.content.offerIsRUPerMinuteThroughputEnabled = isRUPerMinuteThroughputEnabled;
|
||||
} else {
|
||||
newOffer = _.extend({}, newOffer, {
|
||||
content: {
|
||||
offerThroughput: newThroughput,
|
||||
offerIsRUPerMinuteThroughputEnabled: isRUPerMinuteThroughputEnabled
|
||||
}
|
||||
});
|
||||
}
|
||||
)
|
||||
.finally(() => this.isExecuting(false));
|
||||
|
||||
const headerOptions: RequestOptions = { initialHeaders: {} };
|
||||
|
||||
if (this.isAutoPilotSelected()) {
|
||||
if (!this.hasAutoPilotV2FeatureFlag()) {
|
||||
newOffer.content.offerAutopilotSettings = {
|
||||
maxThroughput: this.autoPilotThroughput()
|
||||
};
|
||||
} else {
|
||||
newOffer.content.offerAutopilotSettings = {
|
||||
tier: this.selectedAutoPilotTier()
|
||||
};
|
||||
}
|
||||
|
||||
// user has changed from provisioned --> autoscale
|
||||
if (!this.hasAutoPilotV2FeatureFlag() && this._hasProvisioningTypeChanged()) {
|
||||
headerOptions.initialHeaders[Constants.HttpHeaders.migrateOfferToAutopilot] = "true";
|
||||
delete newOffer.content.offerAutopilotSettings;
|
||||
} else {
|
||||
delete newOffer.content.offerThroughput;
|
||||
}
|
||||
} else {
|
||||
this.isAutoPilotSelected(false);
|
||||
this.userCanChangeProvisioningTypes(false || !this.hasAutoPilotV2FeatureFlag());
|
||||
|
||||
// user has changed from autoscale --> provisioned
|
||||
if (!this.hasAutoPilotV2FeatureFlag() && this._hasProvisioningTypeChanged()) {
|
||||
headerOptions.initialHeaders[Constants.HttpHeaders.migrateOfferToManualThroughput] = "true";
|
||||
} else {
|
||||
delete newOffer.content.offerAutopilotSettings;
|
||||
}
|
||||
}
|
||||
|
||||
if (
|
||||
this.maxRUs() <= SharedConstants.CollectionCreation.DefaultCollectionRUs1Million &&
|
||||
newThroughput > SharedConstants.CollectionCreation.DefaultCollectionRUs1Million &&
|
||||
this.container != null
|
||||
) {
|
||||
const requestPayload = {
|
||||
subscriptionId: userContext.subscriptionId,
|
||||
databaseAccountName: userContext.databaseAccount.name,
|
||||
resourceGroup: userContext.resourceGroup,
|
||||
databaseName: this.collection.databaseId,
|
||||
collectionName: this.collection.id(),
|
||||
throughput: newThroughput,
|
||||
offerIsRUPerMinuteThroughputEnabled: isRUPerMinuteThroughputEnabled
|
||||
};
|
||||
|
||||
await updateOfferThroughputBeyondLimit(requestPayload);
|
||||
this.collection.offer().content.offerThroughput = originalThroughputValue;
|
||||
this.throughput(originalThroughputValue);
|
||||
this.notificationStatusInfo(
|
||||
throughputApplyDelayedMessage(
|
||||
this.isAutoPilotSelected(),
|
||||
originalThroughputValue,
|
||||
this._getThroughputUnit(),
|
||||
this.collection.databaseId,
|
||||
this.collection.id(),
|
||||
newThroughput
|
||||
)
|
||||
);
|
||||
this.throughput.valueHasMutated(); // force component re-render
|
||||
} else {
|
||||
const updatedOffer: DataModels.Offer = await updateOffer(this.collection.offer(), newOffer, headerOptions);
|
||||
this.collection.offer(updatedOffer);
|
||||
this.collection.offer.valueHasMutated();
|
||||
}
|
||||
}
|
||||
|
||||
this.container.isRefreshingExplorer(false);
|
||||
this._setBaseline();
|
||||
this.collection.readSettings();
|
||||
this._wasAutopilotOriginallySet(this.isAutoPilotSelected());
|
||||
TelemetryProcessor.traceSuccess(
|
||||
Action.UpdateSettings,
|
||||
{
|
||||
databaseAccountName: this.container.databaseAccount().name,
|
||||
defaultExperience: this.container.defaultExperience(),
|
||||
dataExplorerArea: Constants.Areas.Tab,
|
||||
tabTitle: this.tabTitle()
|
||||
},
|
||||
startKey
|
||||
);
|
||||
} catch (error) {
|
||||
this.container.isRefreshingExplorer(false);
|
||||
this.isExecutionError(true);
|
||||
console.error(error);
|
||||
TelemetryProcessor.traceFailure(
|
||||
Action.UpdateSettings,
|
||||
{
|
||||
databaseAccountName: this.container.databaseAccount().name,
|
||||
databaseName: this.collection && this.collection.databaseId,
|
||||
collectionName: this.collection && this.collection.id(),
|
||||
defaultExperience: this.container.defaultExperience(),
|
||||
dataExplorerArea: Constants.Areas.Tab,
|
||||
tabTitle: this.tabTitle(),
|
||||
error: error
|
||||
},
|
||||
startKey
|
||||
);
|
||||
}
|
||||
|
||||
this.isExecuting(false);
|
||||
};
|
||||
|
||||
public onRevertClick = (): Q.Promise<any> => {
|
||||
|
||||
Reference in New Issue
Block a user