Merge branch 'master' into users/srnara/selfServeDemo

This commit is contained in:
Srinath Narayanan
2021-05-19 09:42:09 +05:30
76 changed files with 14688 additions and 20261 deletions

View File

@@ -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[],
},

View File

@@ -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;
}

View File

@@ -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> => {

View File

@@ -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,