mirror of
https://github.com/Azure/cosmos-explorer.git
synced 2026-01-26 21:24:13 +00:00
Merge branch 'master' into users/srnara/selfServeDemo
This commit is contained in:
@@ -190,7 +190,8 @@ describe("SelfServeUtils", () => {
|
||||
max: 5,
|
||||
step: 1,
|
||||
uiType: "Spinner",
|
||||
errorMessage: "label, truelabel and falselabel are required for boolean input 'invalidThroughput'.",
|
||||
errorMessage:
|
||||
"labelTkey, trueLabelTKey and falseLabelTKey are required for boolean input 'invalidThroughput'.",
|
||||
},
|
||||
children: [] as Node[],
|
||||
},
|
||||
@@ -225,7 +226,8 @@ describe("SelfServeUtils", () => {
|
||||
type: "boolean",
|
||||
labelTKey: "Invalid Enable Logging",
|
||||
placeholderTKey: "placeholder text",
|
||||
errorMessage: "label, truelabel and falselabel are required for boolean input 'invalidEnableLogging'.",
|
||||
errorMessage:
|
||||
"labelTkey, trueLabelTKey and falseLabelTKey are required for boolean input 'invalidEnableLogging'.",
|
||||
},
|
||||
children: [] as Node[],
|
||||
},
|
||||
@@ -252,7 +254,7 @@ describe("SelfServeUtils", () => {
|
||||
type: "object",
|
||||
labelTKey: "Invalid Regions",
|
||||
placeholderTKey: "placeholder text",
|
||||
errorMessage: "label and choices are required for Choice input 'invalidRegions'.",
|
||||
errorMessage: "labelTKey and choices are required for Choice input 'invalidRegions'.",
|
||||
},
|
||||
children: [] as Node[],
|
||||
},
|
||||
|
||||
@@ -207,8 +207,8 @@ const addToDescriptor = (
|
||||
const getInput = (value: DecoratorProperties): AnyDisplay => {
|
||||
switch (value.type) {
|
||||
case "number":
|
||||
if (!value.labelTKey || !value.step || !value.uiType || !value.min || !value.max) {
|
||||
value.errorMessage = `label, step, min, max and uiType are required for number input '${value.id}'.`;
|
||||
if (!value.labelTKey || !value.uiType || !value.step || !value.max || value.min === undefined) {
|
||||
value.errorMessage = `labelTkey, step, min, max and uiType are required for number input '${value.id}'.`;
|
||||
}
|
||||
return value as NumberInput;
|
||||
case "string":
|
||||
@@ -219,17 +219,17 @@ const getInput = (value: DecoratorProperties): AnyDisplay => {
|
||||
return value as DescriptionDisplay;
|
||||
}
|
||||
if (!value.labelTKey) {
|
||||
value.errorMessage = `label is required for string input '${value.id}'.`;
|
||||
value.errorMessage = `labelTKey is required for string input '${value.id}'.`;
|
||||
}
|
||||
return value as StringInput;
|
||||
case "boolean":
|
||||
if (!value.labelTKey || !value.trueLabelTKey || !value.falseLabelTKey) {
|
||||
value.errorMessage = `label, truelabel and falselabel are required for boolean input '${value.id}'.`;
|
||||
value.errorMessage = `labelTkey, trueLabelTKey and falseLabelTKey are required for boolean input '${value.id}'.`;
|
||||
}
|
||||
return value as BooleanInput;
|
||||
default:
|
||||
if (!value.labelTKey || !value.choices) {
|
||||
value.errorMessage = `label and choices are required for Choice input '${value.id}'.`;
|
||||
value.errorMessage = `labelTKey and choices are required for Choice input '${value.id}'.`;
|
||||
}
|
||||
return value as ChoiceInput;
|
||||
}
|
||||
|
||||
@@ -1,10 +1,12 @@
|
||||
import { RefreshResult } from "../SelfServeTypes";
|
||||
import { configContext } from "../../ConfigContext";
|
||||
import { userContext } from "../../UserContext";
|
||||
import { armRequestWithoutPolling } from "../../Utils/arm/request";
|
||||
import { configContext } from "../../ConfigContext";
|
||||
import { selfServeTraceFailure, selfServeTraceStart, selfServeTraceSuccess } from "../SelfServeTelemetryProcessor";
|
||||
import { RefreshResult } from "../SelfServeTypes";
|
||||
import SqlX from "./SqlX";
|
||||
import { SqlxServiceResource, UpdateDedicatedGatewayRequestParameters } from "./SqlxTypes";
|
||||
|
||||
const apiVersion = "2020-06-01-preview";
|
||||
const apiVersion = "2021-04-01-preview";
|
||||
|
||||
export enum ResourceStatus {
|
||||
Running = "Running",
|
||||
@@ -21,7 +23,7 @@ export interface DedicatedGatewayResponse {
|
||||
}
|
||||
|
||||
export const getPath = (subscriptionId: string, resourceGroup: string, name: string): string => {
|
||||
return `/subscriptions/${subscriptionId}/resourceGroups/${resourceGroup}/providers/Microsoft.DocumentDB/databaseAccounts/${name}/services/sqlx`;
|
||||
return `/subscriptions/${subscriptionId}/resourceGroups/${resourceGroup}/providers/Microsoft.DocumentDB/databaseAccounts/${name}/services/SqlDedicatedGateway`;
|
||||
};
|
||||
|
||||
export const updateDedicatedGatewayResource = async (sku: string, instances: number): Promise<string> => {
|
||||
@@ -30,39 +32,66 @@ export const updateDedicatedGatewayResource = async (sku: string, instances: num
|
||||
properties: {
|
||||
instanceSize: sku,
|
||||
instanceCount: instances,
|
||||
serviceType: "Sqlx",
|
||||
serviceType: "SqlDedicatedGateway",
|
||||
},
|
||||
};
|
||||
const armRequestResult = await armRequestWithoutPolling({
|
||||
host: configContext.ARM_ENDPOINT,
|
||||
path,
|
||||
method: "PUT",
|
||||
apiVersion,
|
||||
body,
|
||||
});
|
||||
return armRequestResult.operationStatusUrl;
|
||||
const telemetryData = { ...body, httpMethod: "PUT", selfServeClassName: SqlX.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: SqlX.name };
|
||||
selfServeTraceFailure(failureTelemetry, updateTimeStamp);
|
||||
}
|
||||
return armRequestResult?.operationStatusUrl;
|
||||
};
|
||||
|
||||
export const deleteDedicatedGatewayResource = async (): Promise<string> => {
|
||||
const path = getPath(userContext.subscriptionId, userContext.resourceGroup, userContext.databaseAccount.name);
|
||||
const armRequestResult = await armRequestWithoutPolling({
|
||||
host: configContext.ARM_ENDPOINT,
|
||||
path,
|
||||
method: "DELETE",
|
||||
apiVersion,
|
||||
});
|
||||
return armRequestResult.operationStatusUrl;
|
||||
const telemetryData = { httpMethod: "DELETE", selfServeClassName: SqlX.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: SqlX.name };
|
||||
selfServeTraceFailure(failureTelemetry, deleteTimeStamp);
|
||||
}
|
||||
return armRequestResult?.operationStatusUrl;
|
||||
};
|
||||
|
||||
export const getDedicatedGatewayResource = async (): Promise<SqlxServiceResource> => {
|
||||
const path = getPath(userContext.subscriptionId, userContext.resourceGroup, userContext.databaseAccount.name);
|
||||
const armRequestResult = await armRequestWithoutPolling<SqlxServiceResource>({
|
||||
host: configContext.ARM_ENDPOINT,
|
||||
path,
|
||||
method: "GET",
|
||||
apiVersion,
|
||||
});
|
||||
return armRequestResult.result;
|
||||
const telemetryData = { httpMethod: "GET", selfServeClassName: SqlX.name };
|
||||
const getResourceTimeStamp = selfServeTraceStart(telemetryData);
|
||||
let armRequestResult;
|
||||
try {
|
||||
armRequestResult = await armRequestWithoutPolling<SqlxServiceResource>({
|
||||
host: configContext.ARM_ENDPOINT,
|
||||
path,
|
||||
method: "GET",
|
||||
apiVersion,
|
||||
});
|
||||
selfServeTraceSuccess(telemetryData, getResourceTimeStamp);
|
||||
} catch (e) {
|
||||
const failureTelemetry = { e, selfServeClassName: SqlX.name };
|
||||
selfServeTraceFailure(failureTelemetry, getResourceTimeStamp);
|
||||
}
|
||||
return armRequestResult?.result;
|
||||
};
|
||||
|
||||
export const getCurrentProvisioningState = async (): Promise<DedicatedGatewayResponse> => {
|
||||
|
||||
@@ -23,7 +23,7 @@ const costPerHourValue: Description = {
|
||||
textTKey: "CostText",
|
||||
type: DescriptionType.Text,
|
||||
link: {
|
||||
href: "https://azure.microsoft.com/en-us/pricing/details/cosmos-db/",
|
||||
href: "https://aka.ms/cosmos-db-dedicated-gateway-pricing",
|
||||
textTKey: "DedicatedGatewayPricing",
|
||||
},
|
||||
};
|
||||
@@ -37,43 +37,56 @@ const connectionStringValue: Description = {
|
||||
},
|
||||
};
|
||||
|
||||
const metricsStringValue: Description = {
|
||||
textTKey: "MetricsText",
|
||||
type: DescriptionType.Text,
|
||||
link: {
|
||||
href: generateBladeLink(BladeType.Metrics),
|
||||
textTKey: "MetricsBlade",
|
||||
},
|
||||
};
|
||||
|
||||
const resizingDecisionValue: Description = {
|
||||
textTKey: "ResizingDecisionText",
|
||||
type: DescriptionType.Text,
|
||||
link: {
|
||||
href: "https://aka.ms/cosmos-db-dedicated-gateway-size",
|
||||
textTKey: "ResizingDecisionLink",
|
||||
},
|
||||
};
|
||||
|
||||
const CosmosD4s = "Cosmos.D4s";
|
||||
const CosmosD8s = "Cosmos.D8s";
|
||||
const CosmosD16s = "Cosmos.D16s";
|
||||
const CosmosD32s = "Cosmos.D32s";
|
||||
|
||||
const getSKUDetails = (sku: string): string => {
|
||||
if (sku === CosmosD4s) {
|
||||
return "CosmosD4Details";
|
||||
} else if (sku === CosmosD8s) {
|
||||
return "CosmosD8Details";
|
||||
} else if (sku === CosmosD16s) {
|
||||
return "CosmosD16Details";
|
||||
} else if (sku === CosmosD32s) {
|
||||
return "CosmosD32Details";
|
||||
}
|
||||
return "Not Supported Yet";
|
||||
};
|
||||
|
||||
const onSKUChange = (newValue: InputType, currentValues: Map<string, SmartUiInput>): Map<string, SmartUiInput> => {
|
||||
currentValues.set("sku", { value: newValue });
|
||||
currentValues.set("skuDetails", {
|
||||
value: { textTKey: getSKUDetails(`${newValue.toString()}`), type: DescriptionType.Text } as Description,
|
||||
});
|
||||
currentValues.set("costPerHour", { value: costPerHourValue });
|
||||
return currentValues;
|
||||
};
|
||||
|
||||
const onNumberOfInstancesChange = (
|
||||
newValue: InputType,
|
||||
currentValues: Map<string, SmartUiInput>
|
||||
currentValues: Map<string, SmartUiInput>,
|
||||
baselineValues: Map<string, SmartUiInput>
|
||||
): Map<string, SmartUiInput> => {
|
||||
currentValues.set("instances", { value: newValue });
|
||||
currentValues.set("warningBanner", {
|
||||
value: { textTKey: "WarningBannerOnUpdate" } as Description,
|
||||
hidden: false,
|
||||
});
|
||||
|
||||
const dedicatedGatewayOriginallyEnabled = baselineValues.get("enableDedicatedGateway")?.value as boolean;
|
||||
const baselineInstances = baselineValues.get("instances")?.value as number;
|
||||
if (!dedicatedGatewayOriginallyEnabled || baselineInstances !== newValue) {
|
||||
currentValues.set("warningBanner", {
|
||||
value: {
|
||||
textTKey: "WarningBannerOnUpdate",
|
||||
link: {
|
||||
href: "https://aka.ms/cosmos-db-dedicated-gateway-overview",
|
||||
textTKey: "DedicatedGatewayPricing",
|
||||
},
|
||||
} as Description,
|
||||
hidden: false,
|
||||
});
|
||||
} else {
|
||||
currentValues.set("warningBanner", undefined);
|
||||
}
|
||||
return currentValues;
|
||||
};
|
||||
|
||||
@@ -87,10 +100,11 @@ const onEnableDedicatedGatewayChange = (
|
||||
if (dedicatedGatewayOriginallyEnabled === newValue) {
|
||||
currentValues.set("sku", baselineValues.get("sku"));
|
||||
currentValues.set("instances", baselineValues.get("instances"));
|
||||
currentValues.set("skuDetails", baselineValues.get("skuDetails"));
|
||||
currentValues.set("costPerHour", baselineValues.get("costPerHour"));
|
||||
currentValues.set("warningBanner", baselineValues.get("warningBanner"));
|
||||
currentValues.set("connectionString", baselineValues.get("connectionString"));
|
||||
currentValues.set("metricsString", baselineValues.get("metricsString"));
|
||||
currentValues.set("resizingDecisionString", baselineValues.get("resizingDecisionString"));
|
||||
return currentValues;
|
||||
}
|
||||
|
||||
@@ -100,7 +114,7 @@ const onEnableDedicatedGatewayChange = (
|
||||
value: {
|
||||
textTKey: "WarningBannerOnUpdate",
|
||||
link: {
|
||||
href: "https://docs.microsoft.com/en-us/azure/cosmos-db/introduction",
|
||||
href: "https://aka.ms/cosmos-db-dedicated-gateway-pricing",
|
||||
textTKey: "DedicatedGatewayPricing",
|
||||
},
|
||||
} as Description,
|
||||
@@ -111,7 +125,7 @@ const onEnableDedicatedGatewayChange = (
|
||||
value: {
|
||||
textTKey: "WarningBannerOnDelete",
|
||||
link: {
|
||||
href: "https://docs.microsoft.com/en-us/azure/cosmos-db/introduction",
|
||||
href: "https://aka.ms/cosmos-db-dedicated-gateway-overview",
|
||||
textTKey: "DeprovisioningDetailsText",
|
||||
},
|
||||
} as Description,
|
||||
@@ -132,18 +146,22 @@ const onEnableDedicatedGatewayChange = (
|
||||
disabled: dedicatedGatewayOriginallyEnabled,
|
||||
});
|
||||
|
||||
currentValues.set("skuDetails", {
|
||||
value: { textTKey: getSKUDetails(`${currentValues.get("sku").value}`), type: DescriptionType.Text } as Description,
|
||||
hidden: hideAttributes,
|
||||
disabled: dedicatedGatewayOriginallyEnabled,
|
||||
});
|
||||
|
||||
currentValues.set("costPerHour", { value: costPerHourValue, hidden: hideAttributes });
|
||||
currentValues.set("connectionString", {
|
||||
value: connectionStringValue,
|
||||
hidden: !newValue || !dedicatedGatewayOriginallyEnabled,
|
||||
});
|
||||
|
||||
currentValues.set("metricsString", {
|
||||
value: metricsStringValue,
|
||||
hidden: !newValue || !dedicatedGatewayOriginallyEnabled,
|
||||
});
|
||||
|
||||
currentValues.set("resizingDecisionString", {
|
||||
value: resizingDecisionValue,
|
||||
hidden: !newValue || !dedicatedGatewayOriginallyEnabled,
|
||||
});
|
||||
|
||||
return currentValues;
|
||||
};
|
||||
|
||||
@@ -151,7 +169,6 @@ const skuDropDownItems: ChoiceItem[] = [
|
||||
{ labelTKey: "CosmosD4s", key: CosmosD4s },
|
||||
{ labelTKey: "CosmosD8s", key: CosmosD8s },
|
||||
{ labelTKey: "CosmosD16s", key: CosmosD16s },
|
||||
{ labelTKey: "CosmosD32s", key: CosmosD32s },
|
||||
];
|
||||
|
||||
const getSkus = async (): Promise<ChoiceItem[]> => {
|
||||
@@ -184,7 +201,6 @@ export default class SqlX extends SelfServeBaseClass {
|
||||
|
||||
currentValues.set("warningBanner", undefined);
|
||||
|
||||
//TODO : Add try catch for each RP call and return relevant notifications
|
||||
if (dedicatedGatewayOriginallyEnabled) {
|
||||
if (!dedicatedGatewayCurrentlyEnabled) {
|
||||
const operationStatusUrl = await deleteDedicatedGatewayResource();
|
||||
@@ -206,9 +222,11 @@ export default class SqlX extends SelfServeBaseClass {
|
||||
},
|
||||
};
|
||||
} else {
|
||||
// Check for scaling up/down/in/out
|
||||
const sku = currentValues.get("sku")?.value as string;
|
||||
const instances = currentValues.get("instances").value as number;
|
||||
const operationStatusUrl = await updateDedicatedGatewayResource(sku, instances);
|
||||
return {
|
||||
operationStatusUrl: undefined,
|
||||
operationStatusUrl: operationStatusUrl,
|
||||
portalNotification: {
|
||||
initialize: {
|
||||
titleTKey: "UpdateInitializeTitle",
|
||||
@@ -255,24 +273,37 @@ export default class SqlX extends SelfServeBaseClass {
|
||||
defaults.set("enableDedicatedGateway", { value: false });
|
||||
defaults.set("sku", { value: CosmosD4s, hidden: true });
|
||||
defaults.set("instances", { value: await getInstancesMin(), hidden: true });
|
||||
defaults.set("skuDetails", undefined);
|
||||
defaults.set("costPerHour", undefined);
|
||||
defaults.set("connectionString", undefined);
|
||||
defaults.set("metricsString", {
|
||||
value: undefined,
|
||||
hidden: true,
|
||||
});
|
||||
defaults.set("resizingDecisionString", {
|
||||
value: undefined,
|
||||
hidden: true,
|
||||
});
|
||||
|
||||
const response = await getCurrentProvisioningState();
|
||||
if (response.status && response.status !== "Deleting") {
|
||||
defaults.set("enableDedicatedGateway", { value: true });
|
||||
defaults.set("sku", { value: response.sku, disabled: true });
|
||||
defaults.set("instances", { value: response.instances, disabled: true });
|
||||
defaults.set("instances", { value: response.instances, disabled: false });
|
||||
defaults.set("costPerHour", { value: costPerHourValue });
|
||||
defaults.set("skuDetails", {
|
||||
value: { textTKey: getSKUDetails(`${defaults.get("sku").value}`), type: DescriptionType.Text } as Description,
|
||||
hidden: false,
|
||||
});
|
||||
defaults.set("connectionString", {
|
||||
value: connectionStringValue,
|
||||
hidden: false,
|
||||
});
|
||||
|
||||
defaults.set("metricsString", {
|
||||
value: metricsStringValue,
|
||||
hidden: false,
|
||||
});
|
||||
|
||||
defaults.set("resizingDecisionString", {
|
||||
value: resizingDecisionValue,
|
||||
hidden: false,
|
||||
});
|
||||
}
|
||||
|
||||
defaults.set("warningBanner", undefined);
|
||||
@@ -289,7 +320,7 @@ export default class SqlX extends SelfServeBaseClass {
|
||||
textTKey: "DedicatedGatewayDescription",
|
||||
type: DescriptionType.Text,
|
||||
link: {
|
||||
href: "https://docs.microsoft.com/en-us/azure/cosmos-db/introduction",
|
||||
href: "https://aka.ms/cosmos-db-dedicated-gateway-overview",
|
||||
textTKey: "LearnAboutDedicatedGateway",
|
||||
},
|
||||
},
|
||||
@@ -312,12 +343,6 @@ export default class SqlX extends SelfServeBaseClass {
|
||||
})
|
||||
sku: ChoiceItem;
|
||||
|
||||
@Values({
|
||||
labelTKey: "SKUDetails",
|
||||
isDynamicDescription: true,
|
||||
})
|
||||
skuDetails: string;
|
||||
|
||||
@OnChange(onNumberOfInstancesChange)
|
||||
@Values({
|
||||
labelTKey: "NumberOfInstances",
|
||||
@@ -328,6 +353,16 @@ export default class SqlX extends SelfServeBaseClass {
|
||||
})
|
||||
instances: number;
|
||||
|
||||
@Values({
|
||||
description: metricsStringValue,
|
||||
})
|
||||
metricsString: string;
|
||||
|
||||
@Values({
|
||||
description: resizingDecisionValue,
|
||||
})
|
||||
resizingDecisionString: string;
|
||||
|
||||
@Values({
|
||||
labelTKey: "Cost",
|
||||
isDynamicDescription: true,
|
||||
|
||||
Reference in New Issue
Block a user