mirror of
https://github.com/Azure/cosmos-explorer.git
synced 2025-12-22 10:21:37 +00:00
adding Materializedviews under selfserve (#1247)
* adding Materializedviews under selfserve * ran npm format & fixed few links * modified required key & changed URLs * modifying links to aka.ms * modifying few descriptions * Delete VSWorkspaceState.json * Delete .suo * modifying URLs & adding missing keys Co-authored-by: Chandra sekhar Gunturi <chguntur@microsoft.com>
This commit is contained in:
committed by
GitHub
parent
7ab57c9ec4
commit
e34f68b162
@@ -0,0 +1,228 @@
|
||||
import { configContext } from "../../ConfigContext";
|
||||
import { userContext } from "../../UserContext";
|
||||
import { armRequestWithoutPolling } from "../../Utils/arm/request";
|
||||
import { selfServeTraceFailure, selfServeTraceStart, selfServeTraceSuccess } from "../SelfServeTelemetryProcessor";
|
||||
import { RefreshResult } from "../SelfServeTypes";
|
||||
import MaterializedViewsBuilder from "./MaterializedViewsBuilder";
|
||||
import {
|
||||
FetchPricesResponse,
|
||||
PriceMapAndCurrencyCode,
|
||||
RegionsResponse,
|
||||
MaterializedViewsBuilderServiceResource,
|
||||
UpdateMaterializedViewsBuilderRequestParameters,
|
||||
} from "./MaterializedViewsBuilderTypes";
|
||||
|
||||
const apiVersion = "2021-07-01-preview";
|
||||
|
||||
export enum ResourceStatus {
|
||||
Running = "Running",
|
||||
Creating = "Creating",
|
||||
Updating = "Updating",
|
||||
Deleting = "Deleting",
|
||||
}
|
||||
|
||||
export interface MaterializedViewsBuilderResponse {
|
||||
sku: string;
|
||||
instances: number;
|
||||
status: string;
|
||||
endpoint: string;
|
||||
}
|
||||
|
||||
export const getPath = (subscriptionId: string, resourceGroup: string, name: string): string => {
|
||||
return `/subscriptions/${subscriptionId}/resourceGroups/${resourceGroup}/providers/Microsoft.DocumentDB/databaseAccounts/${name}/services/materializedviewsBuilder`;
|
||||
};
|
||||
|
||||
export const updateMaterializedViewsBuilderResource = async (sku: string, instances: number): Promise<string> => {
|
||||
const path = getPath(userContext.subscriptionId, userContext.resourceGroup, userContext.databaseAccount.name);
|
||||
const body: UpdateMaterializedViewsBuilderRequestParameters = {
|
||||
properties: {
|
||||
instanceSize: sku,
|
||||
instanceCount: instances,
|
||||
serviceType: "materializedviewsBuilder",
|
||||
},
|
||||
};
|
||||
const telemetryData = { ...body, httpMethod: "PUT", selfServeClassName: MaterializedViewsBuilder.name };
|
||||
const updateTimeStamp = selfServeTraceStart(telemetryData);
|
||||
let armRequestResult;
|
||||
try {
|
||||
armRequestResult = await armRequestWithoutPolling({
|
||||
host: configContext.ARM_ENDPOINT,
|
||||
path,
|
||||
method: "PUT",
|
||||
apiVersion,
|
||||
body,
|
||||
});
|
||||
selfServeTraceSuccess(telemetryData, updateTimeStamp);
|
||||
} catch (e) {
|
||||
const failureTelemetry = { ...body, e, selfServeClassName: MaterializedViewsBuilder.name };
|
||||
selfServeTraceFailure(failureTelemetry, updateTimeStamp);
|
||||
throw e;
|
||||
}
|
||||
return armRequestResult?.operationStatusUrl;
|
||||
};
|
||||
|
||||
export const deleteMaterializedViewsBuilderResource = async (): Promise<string> => {
|
||||
const path = getPath(userContext.subscriptionId, userContext.resourceGroup, userContext.databaseAccount.name);
|
||||
const telemetryData = { httpMethod: "DELETE", selfServeClassName: MaterializedViewsBuilder.name };
|
||||
const deleteTimeStamp = selfServeTraceStart(telemetryData);
|
||||
let armRequestResult;
|
||||
try {
|
||||
armRequestResult = await armRequestWithoutPolling({
|
||||
host: configContext.ARM_ENDPOINT,
|
||||
path,
|
||||
method: "DELETE",
|
||||
apiVersion,
|
||||
});
|
||||
selfServeTraceSuccess(telemetryData, deleteTimeStamp);
|
||||
} catch (e) {
|
||||
const failureTelemetry = { e, selfServeClassName: MaterializedViewsBuilder.name };
|
||||
selfServeTraceFailure(failureTelemetry, deleteTimeStamp);
|
||||
throw e;
|
||||
}
|
||||
return armRequestResult?.operationStatusUrl;
|
||||
};
|
||||
|
||||
export const getMaterializedViewsBuilderResource = async (): Promise<MaterializedViewsBuilderServiceResource> => {
|
||||
const path = getPath(userContext.subscriptionId, userContext.resourceGroup, userContext.databaseAccount.name);
|
||||
const telemetryData = { httpMethod: "GET", selfServeClassName: MaterializedViewsBuilder.name };
|
||||
const getResourceTimeStamp = selfServeTraceStart(telemetryData);
|
||||
let armRequestResult;
|
||||
try {
|
||||
armRequestResult = await armRequestWithoutPolling<MaterializedViewsBuilderServiceResource>({
|
||||
host: configContext.ARM_ENDPOINT,
|
||||
path,
|
||||
method: "GET",
|
||||
apiVersion,
|
||||
});
|
||||
selfServeTraceSuccess(telemetryData, getResourceTimeStamp);
|
||||
} catch (e) {
|
||||
const failureTelemetry = { e, selfServeClassName: MaterializedViewsBuilder.name };
|
||||
selfServeTraceFailure(failureTelemetry, getResourceTimeStamp);
|
||||
throw e;
|
||||
}
|
||||
return armRequestResult?.result;
|
||||
};
|
||||
|
||||
export const getCurrentProvisioningState = async (): Promise<MaterializedViewsBuilderResponse> => {
|
||||
try {
|
||||
const response = await getMaterializedViewsBuilderResource();
|
||||
return {
|
||||
sku: response.properties.instanceSize,
|
||||
instances: response.properties.instanceCount,
|
||||
status: response.properties.status,
|
||||
endpoint: response.properties.MaterializedViewsBuilderEndPoint,
|
||||
};
|
||||
} catch (e) {
|
||||
return { sku: undefined, instances: undefined, status: undefined, endpoint: undefined };
|
||||
}
|
||||
};
|
||||
|
||||
export const refreshMaterializedViewsBuilderProvisioning = async (): Promise<RefreshResult> => {
|
||||
try {
|
||||
const response = await getMaterializedViewsBuilderResource();
|
||||
if (response.properties.status === ResourceStatus.Running.toString()) {
|
||||
return { isUpdateInProgress: false, updateInProgressMessageTKey: undefined };
|
||||
} else if (response.properties.status === ResourceStatus.Creating.toString()) {
|
||||
return { isUpdateInProgress: true, updateInProgressMessageTKey: "CreateMessage" };
|
||||
} else if (response.properties.status === ResourceStatus.Deleting.toString()) {
|
||||
return { isUpdateInProgress: true, updateInProgressMessageTKey: "DeleteMessage" };
|
||||
} else {
|
||||
return { isUpdateInProgress: true, updateInProgressMessageTKey: "UpdateMessage" };
|
||||
}
|
||||
} catch {
|
||||
//TODO differentiate between different failures
|
||||
return { isUpdateInProgress: false, updateInProgressMessageTKey: undefined };
|
||||
}
|
||||
};
|
||||
|
||||
const getGeneralPath = (subscriptionId: string, resourceGroup: string, name: string): string => {
|
||||
return `/subscriptions/${subscriptionId}/resourceGroups/${resourceGroup}/providers/Microsoft.DocumentDB/databaseAccounts/${name}`;
|
||||
};
|
||||
|
||||
export const getRegions = async (): Promise<Array<string>> => {
|
||||
const telemetryData = {
|
||||
feature: "Calculate approximate cost",
|
||||
function: "getRegions",
|
||||
description: "",
|
||||
selfServeClassName: MaterializedViewsBuilder.name,
|
||||
};
|
||||
const getRegionsTimestamp = selfServeTraceStart(telemetryData);
|
||||
|
||||
try {
|
||||
const regions = new Array<string>();
|
||||
|
||||
const response = await armRequestWithoutPolling<RegionsResponse>({
|
||||
host: configContext.ARM_ENDPOINT,
|
||||
path: getGeneralPath(userContext.subscriptionId, userContext.resourceGroup, userContext.databaseAccount.name),
|
||||
method: "GET",
|
||||
apiVersion: "2021-07-01-preview",
|
||||
});
|
||||
|
||||
if (response.result.location !== undefined) {
|
||||
regions.push(response.result.location.split(" ").join("").toLowerCase());
|
||||
} else {
|
||||
for (const location of response.result.locations) {
|
||||
regions.push(location.locationName.split(" ").join("").toLowerCase());
|
||||
}
|
||||
}
|
||||
|
||||
selfServeTraceSuccess(telemetryData, getRegionsTimestamp);
|
||||
return regions;
|
||||
} catch (err) {
|
||||
const failureTelemetry = { err, selfServeClassName: MaterializedViewsBuilder.name };
|
||||
selfServeTraceFailure(failureTelemetry, getRegionsTimestamp);
|
||||
return new Array<string>();
|
||||
}
|
||||
};
|
||||
|
||||
const getFetchPricesPathForRegion = (subscriptionId: string): string => {
|
||||
return `/subscriptions/${subscriptionId}/providers/Microsoft.CostManagement/fetchPrices`;
|
||||
};
|
||||
|
||||
export const getPriceMapAndCurrencyCode = async (regions: Array<string>): Promise<PriceMapAndCurrencyCode> => {
|
||||
const telemetryData = {
|
||||
feature: "Calculate approximate cost",
|
||||
function: "getPriceMapAndCurrencyCode",
|
||||
description: "fetch prices API call",
|
||||
selfServeClassName: MaterializedViewsBuilder.name,
|
||||
};
|
||||
const getPriceMapAndCurrencyCodeTimestamp = selfServeTraceStart(telemetryData);
|
||||
|
||||
try {
|
||||
const priceMap = new Map<string, Map<string, number>>();
|
||||
let currencyCode;
|
||||
for (const region of regions) {
|
||||
const regionPriceMap = new Map<string, number>();
|
||||
|
||||
const response = await armRequestWithoutPolling<FetchPricesResponse>({
|
||||
host: configContext.ARM_ENDPOINT,
|
||||
path: getFetchPricesPathForRegion(userContext.subscriptionId),
|
||||
method: "POST",
|
||||
apiVersion: "2020-01-01-preview",
|
||||
queryParams: {
|
||||
filter:
|
||||
"armRegionName eq '" +
|
||||
region +
|
||||
"' and serviceFamily eq 'Databases' and productName eq 'Azure Cosmos DB MaterializedViews Builder - General Purpose'",
|
||||
},
|
||||
});
|
||||
|
||||
for (const item of response.result.Items) {
|
||||
if (currencyCode === undefined) {
|
||||
currencyCode = item.currencyCode;
|
||||
} else if (item.currencyCode !== currencyCode) {
|
||||
throw Error("Currency Code Mismatch: Currency code not same for all regions / skus.");
|
||||
}
|
||||
regionPriceMap.set(item.skuName, item.retailPrice);
|
||||
}
|
||||
priceMap.set(region, regionPriceMap);
|
||||
}
|
||||
|
||||
selfServeTraceSuccess(telemetryData, getPriceMapAndCurrencyCodeTimestamp);
|
||||
return { priceMap: priceMap, currencyCode: currencyCode };
|
||||
} catch (err) {
|
||||
const failureTelemetry = { err, selfServeClassName: MaterializedViewsBuilder.name };
|
||||
selfServeTraceFailure(failureTelemetry, getPriceMapAndCurrencyCodeTimestamp);
|
||||
return { priceMap: undefined, currencyCode: undefined };
|
||||
}
|
||||
};
|
||||
@@ -0,0 +1,416 @@
|
||||
import { IsDisplayable, OnChange, PropertyInfo, RefreshOptions, Values } from "../Decorators";
|
||||
import {
|
||||
selfServeTrace,
|
||||
selfServeTraceFailure,
|
||||
selfServeTraceStart,
|
||||
selfServeTraceSuccess,
|
||||
} from "../SelfServeTelemetryProcessor";
|
||||
import {
|
||||
ChoiceItem,
|
||||
Description,
|
||||
DescriptionType,
|
||||
Info,
|
||||
InputType,
|
||||
NumberUiType,
|
||||
OnSaveResult,
|
||||
RefreshResult,
|
||||
SelfServeBaseClass,
|
||||
SmartUiInput,
|
||||
} from "../SelfServeTypes";
|
||||
import { BladeType, generateBladeLink } from "../SelfServeUtils";
|
||||
import {
|
||||
deleteMaterializedViewsBuilderResource,
|
||||
getCurrentProvisioningState,
|
||||
getPriceMapAndCurrencyCode,
|
||||
getRegions,
|
||||
refreshMaterializedViewsBuilderProvisioning,
|
||||
updateMaterializedViewsBuilderResource,
|
||||
} from "./MaterializedViewsBuilder.rp";
|
||||
|
||||
const costPerHourDefaultValue: Description = {
|
||||
textTKey: "CostText",
|
||||
type: DescriptionType.Text,
|
||||
link: {
|
||||
href: "https://aka.ms/cosmos-db-materializedviewsbuilder-pricing",
|
||||
textTKey: "MaterializedviewsBuilderPricing",
|
||||
},
|
||||
};
|
||||
|
||||
const metricsStringValue: Description = {
|
||||
textTKey: "MetricsText",
|
||||
type: DescriptionType.Text,
|
||||
link: {
|
||||
href: generateBladeLink(BladeType.Metrics),
|
||||
textTKey: "MetricsBlade",
|
||||
},
|
||||
};
|
||||
|
||||
const CosmosD2s = "Cosmos.D2s";
|
||||
const CosmosD4s = "Cosmos.D4s";
|
||||
const CosmosD8s = "Cosmos.D8s";
|
||||
const CosmosD16s = "Cosmos.D16s";
|
||||
|
||||
const onSKUChange = (newValue: InputType, currentValues: Map<string, SmartUiInput>): Map<string, SmartUiInput> => {
|
||||
currentValues.set("sku", { value: newValue });
|
||||
currentValues.set("costPerHour", {
|
||||
value: calculateCost(newValue as string, currentValues.get("instances").value as number),
|
||||
});
|
||||
|
||||
return currentValues;
|
||||
};
|
||||
|
||||
const onNumberOfInstancesChange = (
|
||||
newValue: InputType,
|
||||
currentValues: Map<string, SmartUiInput>,
|
||||
baselineValues: Map<string, SmartUiInput>
|
||||
): Map<string, SmartUiInput> => {
|
||||
currentValues.set("instances", { value: newValue });
|
||||
const MaterializedViewsBuilderOriginallyEnabled = baselineValues.get("enableMaterializedViewsBuilder")
|
||||
?.value as boolean;
|
||||
const baselineInstances = baselineValues.get("instances")?.value as number;
|
||||
if (!MaterializedViewsBuilderOriginallyEnabled || baselineInstances !== newValue) {
|
||||
currentValues.set("warningBanner", {
|
||||
value: {
|
||||
textTKey: "WarningBannerOnUpdate",
|
||||
link: {
|
||||
href: "https://aka.ms/cosmos-db-materializedviewsbuilder-pricing",
|
||||
textTKey: "MaterializedviewsBuilderPricing",
|
||||
},
|
||||
} as Description,
|
||||
hidden: false,
|
||||
});
|
||||
} else {
|
||||
currentValues.set("warningBanner", undefined);
|
||||
}
|
||||
|
||||
currentValues.set("costPerHour", {
|
||||
value: calculateCost(currentValues.get("sku").value as string, newValue as number),
|
||||
});
|
||||
|
||||
return currentValues;
|
||||
};
|
||||
|
||||
const onEnableMaterializedViewsBuilderChange = (
|
||||
newValue: InputType,
|
||||
currentValues: Map<string, SmartUiInput>,
|
||||
baselineValues: ReadonlyMap<string, SmartUiInput>
|
||||
): Map<string, SmartUiInput> => {
|
||||
currentValues.set("enableMaterializedViewsBuilder", { value: newValue });
|
||||
const MaterializedViewsBuilderOriginallyEnabled = baselineValues.get("enableMaterializedViewsBuilder")
|
||||
?.value as boolean;
|
||||
if (MaterializedViewsBuilderOriginallyEnabled === newValue) {
|
||||
currentValues.set("sku", baselineValues.get("sku"));
|
||||
currentValues.set("instances", baselineValues.get("instances"));
|
||||
currentValues.set("costPerHour", baselineValues.get("costPerHour"));
|
||||
currentValues.set("warningBanner", baselineValues.get("warningBanner"));
|
||||
currentValues.set("metricsString", baselineValues.get("metricsString"));
|
||||
return currentValues;
|
||||
}
|
||||
|
||||
currentValues.set("warningBanner", undefined);
|
||||
if (newValue === true) {
|
||||
currentValues.set("warningBanner", {
|
||||
value: {
|
||||
textTKey: "WarningBannerOnUpdate",
|
||||
link: {
|
||||
href: "https://aka.ms/cosmos-db-materializedviewsbuilder-pricing",
|
||||
textTKey: "MaterializedviewsBuilderPricing",
|
||||
},
|
||||
} as Description,
|
||||
hidden: false,
|
||||
});
|
||||
|
||||
currentValues.set("costPerHour", {
|
||||
value: calculateCost(baselineValues.get("sku").value as string, baselineValues.get("instances").value as number),
|
||||
hidden: false,
|
||||
});
|
||||
} else {
|
||||
currentValues.set("warningBanner", {
|
||||
value: {
|
||||
textTKey: "WarningBannerOnDelete",
|
||||
link: {
|
||||
href: "https://aka.ms/cosmos-db-materializedviews",
|
||||
textTKey: "DeprovisioningDetailsText",
|
||||
},
|
||||
} as Description,
|
||||
hidden: false,
|
||||
});
|
||||
|
||||
currentValues.set("costPerHour", { value: costPerHourDefaultValue, hidden: true });
|
||||
}
|
||||
const sku = currentValues.get("sku");
|
||||
const instances = currentValues.get("instances");
|
||||
const hideAttributes = newValue === undefined || !(newValue as boolean);
|
||||
currentValues.set("sku", {
|
||||
value: sku.value,
|
||||
hidden: hideAttributes,
|
||||
disabled: MaterializedViewsBuilderOriginallyEnabled,
|
||||
});
|
||||
currentValues.set("instances", {
|
||||
value: instances.value,
|
||||
hidden: hideAttributes,
|
||||
disabled: MaterializedViewsBuilderOriginallyEnabled,
|
||||
});
|
||||
|
||||
currentValues.set("metricsString", {
|
||||
value: metricsStringValue,
|
||||
hidden: !newValue || !MaterializedViewsBuilderOriginallyEnabled,
|
||||
});
|
||||
|
||||
return currentValues;
|
||||
};
|
||||
|
||||
const skuDropDownItems: ChoiceItem[] = [
|
||||
{ labelTKey: "CosmosD2s", key: CosmosD2s },
|
||||
{ labelTKey: "CosmosD4s", key: CosmosD4s },
|
||||
{ labelTKey: "CosmosD8s", key: CosmosD8s },
|
||||
{ labelTKey: "CosmosD16s", key: CosmosD16s },
|
||||
];
|
||||
|
||||
const getSkus = async (): Promise<ChoiceItem[]> => {
|
||||
return skuDropDownItems;
|
||||
};
|
||||
|
||||
const getInstancesMin = async (): Promise<number> => {
|
||||
return 1;
|
||||
};
|
||||
|
||||
const getInstancesMax = async (): Promise<number> => {
|
||||
return 5;
|
||||
};
|
||||
|
||||
const NumberOfInstancesDropdownInfo: Info = {
|
||||
messageTKey: "ResizingDecisionText",
|
||||
link: {
|
||||
href: "https://aka.ms/cosmos-db-materializedviewsbuilder-size",
|
||||
textTKey: "ResizingDecisionLink",
|
||||
},
|
||||
};
|
||||
|
||||
const ApproximateCostDropDownInfo: Info = {
|
||||
messageTKey: "CostText",
|
||||
link: {
|
||||
href: "https://aka.ms/cosmos-db-materializedviewsbuilder-pricing",
|
||||
textTKey: "MaterializedviewsBuilderPricing",
|
||||
},
|
||||
};
|
||||
|
||||
let priceMap: Map<string, Map<string, number>>;
|
||||
let currencyCode: string;
|
||||
let regions: Array<string>;
|
||||
|
||||
const calculateCost = (skuName: string, instanceCount: number): Description => {
|
||||
const telemetryData = {
|
||||
feature: "Calculate approximate cost",
|
||||
function: "calculateCost",
|
||||
description: "performs final calculation",
|
||||
selfServeClassName: MaterializedViewsBuilder.name,
|
||||
};
|
||||
const calculateCostTimestamp = selfServeTraceStart(telemetryData);
|
||||
|
||||
try {
|
||||
let costPerHour = 0;
|
||||
for (const region of regions) {
|
||||
const incrementalCost = priceMap.get(region).get(skuName.replace("Cosmos.", ""));
|
||||
if (incrementalCost === undefined) {
|
||||
throw new Error("Value not found in map.");
|
||||
}
|
||||
costPerHour += incrementalCost;
|
||||
}
|
||||
|
||||
if (costPerHour === 0) {
|
||||
throw new Error("Cost per hour = 0");
|
||||
}
|
||||
|
||||
costPerHour *= instanceCount;
|
||||
costPerHour = Math.round(costPerHour * 100) / 100;
|
||||
|
||||
selfServeTraceSuccess(telemetryData, calculateCostTimestamp);
|
||||
return {
|
||||
textTKey: `${costPerHour} ${currencyCode}`,
|
||||
type: DescriptionType.Text,
|
||||
};
|
||||
} catch (err) {
|
||||
const failureTelemetry = { err, regions, priceMap, selfServeClassName: MaterializedViewsBuilder.name };
|
||||
selfServeTraceFailure(failureTelemetry, calculateCostTimestamp);
|
||||
|
||||
return costPerHourDefaultValue;
|
||||
}
|
||||
};
|
||||
|
||||
@IsDisplayable()
|
||||
@RefreshOptions({ retryIntervalInMs: 20000 })
|
||||
export default class MaterializedViewsBuilder extends SelfServeBaseClass {
|
||||
public onRefresh = async (): Promise<RefreshResult> => {
|
||||
return await refreshMaterializedViewsBuilderProvisioning();
|
||||
};
|
||||
|
||||
public onSave = async (
|
||||
currentValues: Map<string, SmartUiInput>,
|
||||
baselineValues: Map<string, SmartUiInput>
|
||||
): Promise<OnSaveResult> => {
|
||||
selfServeTrace({ selfServeClassName: MaterializedViewsBuilder.name });
|
||||
|
||||
const MaterializedViewsBuilderCurrentlyEnabled = currentValues.get("enableMaterializedViewsBuilder")
|
||||
?.value as boolean;
|
||||
const MaterializedViewsBuilderOriginallyEnabled = baselineValues.get("enableMaterializedViewsBuilder")
|
||||
?.value as boolean;
|
||||
|
||||
currentValues.set("warningBanner", undefined);
|
||||
|
||||
if (MaterializedViewsBuilderOriginallyEnabled) {
|
||||
if (!MaterializedViewsBuilderCurrentlyEnabled) {
|
||||
const operationStatusUrl = await deleteMaterializedViewsBuilderResource();
|
||||
return {
|
||||
operationStatusUrl: operationStatusUrl,
|
||||
portalNotification: {
|
||||
initialize: {
|
||||
titleTKey: "DeleteInitializeTitle",
|
||||
messageTKey: "DeleteInitializeMessage",
|
||||
},
|
||||
success: {
|
||||
titleTKey: "DeleteSuccessTitle",
|
||||
messageTKey: "DeleteSuccesseMessage",
|
||||
},
|
||||
failure: {
|
||||
titleTKey: "DeleteFailureTitle",
|
||||
messageTKey: "DeleteFailureMessage",
|
||||
},
|
||||
},
|
||||
};
|
||||
} else {
|
||||
const sku = currentValues.get("sku")?.value as string;
|
||||
const instances = currentValues.get("instances").value as number;
|
||||
const operationStatusUrl = await updateMaterializedViewsBuilderResource(sku, instances);
|
||||
return {
|
||||
operationStatusUrl: operationStatusUrl,
|
||||
portalNotification: {
|
||||
initialize: {
|
||||
titleTKey: "UpdateInitializeTitle",
|
||||
messageTKey: "UpdateInitializeMessage",
|
||||
},
|
||||
success: {
|
||||
titleTKey: "UpdateSuccessTitle",
|
||||
messageTKey: "UpdateSuccesseMessage",
|
||||
},
|
||||
failure: {
|
||||
titleTKey: "UpdateFailureTitle",
|
||||
messageTKey: "UpdateFailureMessage",
|
||||
},
|
||||
},
|
||||
};
|
||||
}
|
||||
} else {
|
||||
const sku = currentValues.get("sku")?.value as string;
|
||||
const instances = currentValues.get("instances").value as number;
|
||||
const operationStatusUrl = await updateMaterializedViewsBuilderResource(sku, instances);
|
||||
return {
|
||||
operationStatusUrl: operationStatusUrl,
|
||||
portalNotification: {
|
||||
initialize: {
|
||||
titleTKey: "CreateInitializeTitle",
|
||||
messageTKey: "CreateInitializeMessage",
|
||||
},
|
||||
success: {
|
||||
titleTKey: "CreateSuccessTitle",
|
||||
messageTKey: "CreateSuccesseMessage",
|
||||
},
|
||||
failure: {
|
||||
titleTKey: "CreateFailureTitle",
|
||||
messageTKey: "CreateFailureMessage",
|
||||
},
|
||||
},
|
||||
};
|
||||
}
|
||||
};
|
||||
|
||||
public initialize = async (): Promise<Map<string, SmartUiInput>> => {
|
||||
// Based on the RP call enableMaterializedViewsBuilder will be true if it has not yet been enabled and false if it has.
|
||||
const defaults = new Map<string, SmartUiInput>();
|
||||
defaults.set("enableMaterializedViewsBuilder", { value: false });
|
||||
defaults.set("sku", { value: CosmosD2s, hidden: true });
|
||||
defaults.set("instances", { value: await getInstancesMin(), hidden: true });
|
||||
defaults.set("costPerHour", undefined);
|
||||
defaults.set("metricsString", {
|
||||
value: undefined,
|
||||
hidden: true,
|
||||
});
|
||||
|
||||
regions = await getRegions();
|
||||
const priceMapAndCurrencyCode = await getPriceMapAndCurrencyCode(regions);
|
||||
priceMap = priceMapAndCurrencyCode.priceMap;
|
||||
currencyCode = priceMapAndCurrencyCode.currencyCode;
|
||||
|
||||
const response = await getCurrentProvisioningState();
|
||||
if (response.status && response.status !== "Deleting") {
|
||||
defaults.set("enableMaterializedViewsBuilder", { value: true });
|
||||
defaults.set("sku", { value: response.sku, disabled: true });
|
||||
defaults.set("instances", { value: response.instances, disabled: false });
|
||||
defaults.set("costPerHour", { value: calculateCost(response.sku, response.instances) });
|
||||
|
||||
defaults.set("metricsString", {
|
||||
value: metricsStringValue,
|
||||
hidden: false,
|
||||
});
|
||||
}
|
||||
defaults.set("warningBanner", undefined);
|
||||
return defaults;
|
||||
};
|
||||
|
||||
@Values({
|
||||
isDynamicDescription: true,
|
||||
})
|
||||
warningBanner: string;
|
||||
|
||||
@Values({
|
||||
description: {
|
||||
textTKey: "MaterializedViewsBuilderDescription",
|
||||
type: DescriptionType.Text,
|
||||
link: {
|
||||
href: "https://aka.ms/cosmos-db-materializedviews",
|
||||
textTKey: "LearnAboutMaterializedViews",
|
||||
},
|
||||
},
|
||||
})
|
||||
description: string;
|
||||
|
||||
@OnChange(onEnableMaterializedViewsBuilderChange)
|
||||
@Values({
|
||||
labelTKey: "MaterializedViewsBuilder",
|
||||
trueLabelTKey: "Provisioned",
|
||||
falseLabelTKey: "Deprovisioned",
|
||||
})
|
||||
enableMaterializedViewsBuilder: boolean;
|
||||
|
||||
@OnChange(onSKUChange)
|
||||
@Values({
|
||||
labelTKey: "SKUs",
|
||||
choices: getSkus,
|
||||
placeholderTKey: "SKUsPlaceHolder",
|
||||
})
|
||||
sku: ChoiceItem;
|
||||
|
||||
@OnChange(onNumberOfInstancesChange)
|
||||
@PropertyInfo(NumberOfInstancesDropdownInfo)
|
||||
@Values({
|
||||
labelTKey: "NumberOfInstances",
|
||||
min: getInstancesMin,
|
||||
max: getInstancesMax,
|
||||
step: 1,
|
||||
uiType: NumberUiType.Spinner,
|
||||
})
|
||||
instances: number;
|
||||
|
||||
@PropertyInfo(ApproximateCostDropDownInfo)
|
||||
@Values({
|
||||
labelTKey: "ApproximateCost",
|
||||
isDynamicDescription: true,
|
||||
})
|
||||
costPerHour: string;
|
||||
|
||||
@Values({
|
||||
labelTKey: "MonitorUsage",
|
||||
description: metricsStringValue,
|
||||
})
|
||||
metricsString: string;
|
||||
}
|
||||
@@ -0,0 +1,57 @@
|
||||
export type MaterializedViewsBuilderServiceResource = {
|
||||
id: string;
|
||||
name: string;
|
||||
type: string;
|
||||
properties: MaterializedViewsBuilderServiceProps;
|
||||
locations: MaterializedViewsBuilderServiceLocations;
|
||||
};
|
||||
export type MaterializedViewsBuilderServiceProps = {
|
||||
serviceType: string;
|
||||
creationTime: string;
|
||||
status: string;
|
||||
instanceSize: string;
|
||||
instanceCount: number;
|
||||
MaterializedViewsBuilderEndPoint: string;
|
||||
};
|
||||
|
||||
export type MaterializedViewsBuilderServiceLocations = {
|
||||
location: string;
|
||||
status: string;
|
||||
MaterializedViewsBuilderEndpoint: string;
|
||||
};
|
||||
|
||||
export type UpdateMaterializedViewsBuilderRequestParameters = {
|
||||
properties: UpdateMaterializedViewsBuilderRequestProperties;
|
||||
};
|
||||
|
||||
export type UpdateMaterializedViewsBuilderRequestProperties = {
|
||||
instanceSize: string;
|
||||
instanceCount: number;
|
||||
serviceType: string;
|
||||
};
|
||||
|
||||
export type FetchPricesResponse = {
|
||||
Items: Array<PriceItem>;
|
||||
NextPageLink: string | undefined;
|
||||
Count: number;
|
||||
};
|
||||
|
||||
export type PriceMapAndCurrencyCode = {
|
||||
priceMap: Map<string, Map<string, number>>;
|
||||
currencyCode: string;
|
||||
};
|
||||
|
||||
export type PriceItem = {
|
||||
retailPrice: number;
|
||||
skuName: string;
|
||||
currencyCode: string;
|
||||
};
|
||||
|
||||
export type RegionsResponse = {
|
||||
locations: Array<RegionItem>;
|
||||
location: string;
|
||||
};
|
||||
|
||||
export type RegionItem = {
|
||||
locationName: string;
|
||||
};
|
||||
@@ -58,6 +58,14 @@ const getDescriptor = async (selfServeType: SelfServeType): Promise<SelfServeDes
|
||||
await loadTranslations(graphAPICompute.constructor.name);
|
||||
return graphAPICompute.toSelfServeDescriptor();
|
||||
}
|
||||
case SelfServeType.materializedviewsbuilder: {
|
||||
const MaterializedViewsBuilder = await import(
|
||||
/* webpackChunkName: "MaterializedViewsBuilder" */ "./MaterializedViewsBuilder/MaterializedViewsBuilder"
|
||||
);
|
||||
const materializedViewsBuilder = new MaterializedViewsBuilder.default();
|
||||
await loadTranslations(materializedViewsBuilder.constructor.name);
|
||||
return materializedViewsBuilder.toSelfServeDescriptor();
|
||||
}
|
||||
default:
|
||||
return undefined;
|
||||
}
|
||||
|
||||
@@ -32,6 +32,7 @@ export enum SelfServeType {
|
||||
example = "example",
|
||||
sqlx = "sqlx",
|
||||
graphapicompute = "graphapicompute",
|
||||
materializedviewsbuilder = "materializedviewsbuilder",
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
Reference in New Issue
Block a user