Compare commits

...

6 Commits

Author SHA1 Message Date
Balaji Sridharan
5b0a98dce7 Removing TODO comments 2021-02-17 10:43:23 -08:00
Balaji Sridharan
67ebce444f Stylecop changes 2021-02-16 00:21:44 -08:00
Balaji Sridharan
a09bcc7197 Merge branch 'users/fnbalaji/PortalChangesForDGW' of https://github.com/Azure/cosmos-explorer into users/fnbalaji/PortalChangesForDGW 2021-02-15 23:49:54 -08:00
Balaji Sridharan
b2390e23e7 Portal changes for DedicatedGateway. CR feedback 2021-02-15 23:49:14 -08:00
fnbalaji
f922103e5c Merge branch 'master' into users/fnbalaji/PortalChangesForDGW 2021-02-15 23:39:50 -08:00
Balaji Sridharan
faa98de9e9 Portal changes for DedicatedGateway
Changes to support creation and deletion of DedicatedGateway resource.

Tested locally with various scenarios.
2021-02-08 05:16:10 -08:00
4 changed files with 207 additions and 40 deletions

View File

@@ -28,6 +28,23 @@
"SubmissionMessage": "Submitted successfully" "SubmissionMessage": "Submitted successfully"
}, },
"SqlX": { "SqlX": {
"DedicatedGatewayDescription": "Provisioning dedicated gateways for SqlX accounts.",
"DedicatedGateway": "Dedicated Gateway",
"Enable": "Enable",
"Disable": "Disable",
"LearnAboutDedicatedGateway": "Learn more about dedicated gateway.",
"SKUs": "SKUs",
"NumberOfInstances": "Number of instances",
"CosmosD4s": "Cosmos.D4s",
"CosmosD8s": "Cosmos.D8s",
"CosmosD16s": "Cosmos.D16s",
"CosmosD32s": "Cosmos.D32s",
"CreateMessage": "DedicatedGateway resource is being created.",
"UpdateMessage": "DedicatedGateway resource is being updated.",
"DeleteMessage": "DedicatedGateway resource is being deleted.",
"CannotSave": "Cannot save the changes to the DedicatedGateway resource at the moment",
"DedicatedGatewayEndpoint": "DedicatedGatewayEndpoint",
"NoValue": ""
} }
} }
} }

View File

@@ -1,33 +1,78 @@
import { RefreshResult } from "../SelfServeTypes"; import { RefreshResult } from "../SelfServeTypes";
import { userContext } from "../../UserContext";
import { armRequest } from "../../Utils/arm/request";
import { configContext } from "../../ConfigContext";
import { SqlxServiceResource, UpdateDedicatedGatewayRequestParameters } from "./SqlxTypes";
const apiVersion = "2020-06-01-preview";
export enum ResourceStatus {
Running,
Creating,
Updating,
Deleting,
}
export interface DedicatedGatewayResponse { export interface DedicatedGatewayResponse {
sku: string; sku: string;
instances: number; instances: number;
status: string;
endpoint: string;
} }
export const getRegionSpecificMinInstances = async (): Promise<number> => { export const getPath = (subscriptionId: string, resourceGroup: string, name: string): string => {
// TODO: write RP call to get min number of instances needed for this region return `/subscriptions/${subscriptionId}/resourceGroups/${resourceGroup}/providers/Microsoft.DocumentDB/databaseAccounts/${name}/services/sqlx`;
throw new Error("getRegionSpecificMinInstances not implemented");
}; };
export const getRegionSpecificMaxInstances = async (): Promise<number> => { export const updateDedicatedGatewayResource = async (sku: string, instances: number): Promise<void> => {
// TODO: write RP call to get max number of instances needed for this region const path = getPath(userContext.subscriptionId, userContext.resourceGroup, userContext.databaseAccount.name);
throw new Error("getRegionSpecificMaxInstances not implemented"); const body: UpdateDedicatedGatewayRequestParameters = {
properties: {
instanceSize: sku,
instanceCount: instances,
serviceType: "Sqlx",
},
};
return armRequest({ host: configContext.ARM_ENDPOINT, path, method: "PUT", apiVersion, body });
}; };
export const updateDedicatedGatewayProvisioning = async (sku: string, instances: number): Promise<void> => { export const deleteDedicatedGatewayResource = async (): Promise<void> => {
// TODO: write RP call to update dedicated gateway provisioning const path = getPath(userContext.subscriptionId, userContext.resourceGroup, userContext.databaseAccount.name);
throw new Error( return armRequest({ host: configContext.ARM_ENDPOINT, path, method: "DELETE", apiVersion });
`updateDedicatedGatewayProvisioning not implemented. Parameters- sku: ${sku}, instances:${instances}`
);
}; };
export const initializeDedicatedGatewayProvisioning = async (): Promise<DedicatedGatewayResponse> => { export const getDedicatedGatewayResource = async (): Promise<SqlxServiceResource> => {
// TODO: write RP call to initialize UI for dedicated gateway provisioning const path = getPath(userContext.subscriptionId, userContext.resourceGroup, userContext.databaseAccount.name);
throw new Error("initializeDedicatedGatewayProvisioning not implemented"); return armRequest<SqlxServiceResource>({ host: configContext.ARM_ENDPOINT, path, method: "GET", apiVersion });
};
export const getCurrentProvisioningState = async (): Promise<DedicatedGatewayResponse> => {
try {
const response = await getDedicatedGatewayResource();
return {
sku: response.properties.instanceSize,
instances: response.properties.instanceCount,
status: response.properties.status,
endpoint: response.properties.sqlxEndPoint,
};
} catch (e) {
return { sku: undefined, instances: undefined, status: undefined, endpoint: undefined };
}
}; };
export const refreshDedicatedGatewayProvisioning = async (): Promise<RefreshResult> => { export const refreshDedicatedGatewayProvisioning = async (): Promise<RefreshResult> => {
// TODO: write RP call to check if dedicated gateway update has gone through try {
throw new Error("refreshDedicatedGatewayProvisioning not implemented"); const response = await getDedicatedGatewayResource();
if (response.properties.status === ResourceStatus.Running.toString()) {
return { isUpdateInProgress: false, notificationMessage: undefined };
} else if (response.properties.status === ResourceStatus.Creating.toString()) {
return { isUpdateInProgress: true, notificationMessage: "CreateMessage" };
} else if (response.properties.status === ResourceStatus.Deleting.toString()) {
return { isUpdateInProgress: true, notificationMessage: "DeleteMessage" };
} else {
return { isUpdateInProgress: true, notificationMessage: "UpdateMessage" };
}
} catch {
return { isUpdateInProgress: false, notificationMessage: undefined };
}
}; };

View File

@@ -6,9 +6,18 @@ import {
RefreshResult, RefreshResult,
SelfServeBaseClass, SelfServeBaseClass,
SelfServeNotification, SelfServeNotification,
SelfServeNotificationType,
SmartUiInput, SmartUiInput,
} from "../SelfServeTypes"; } from "../SelfServeTypes";
import { refreshDedicatedGatewayProvisioning } from "./SqlX.rp"; import {
ResourceStatus,
refreshDedicatedGatewayProvisioning,
updateDedicatedGatewayResource,
deleteDedicatedGatewayResource,
getCurrentProvisioningState,
} from "./SqlX.rp";
let disableAttributesOnDedicatedGatewayChange = false;
const onEnableDedicatedGatewayChange = ( const onEnableDedicatedGatewayChange = (
currentState: Map<string, SmartUiInput>, currentState: Map<string, SmartUiInput>,
@@ -16,31 +25,38 @@ const onEnableDedicatedGatewayChange = (
): Map<string, SmartUiInput> => { ): Map<string, SmartUiInput> => {
const sku = currentState.get("sku"); const sku = currentState.get("sku");
const instances = currentState.get("instances"); const instances = currentState.get("instances");
const isSkuHidden = newValue === undefined || !(newValue as boolean); const hideAttributes = newValue === undefined || !(newValue as boolean);
currentState.set("enableDedicatedGateway", { value: newValue }); currentState.set("enableDedicatedGateway", { value: newValue });
currentState.set("sku", { value: sku.value, hidden: isSkuHidden }); currentState.set("sku", {
currentState.set("instances", { value: instances.value, hidden: isSkuHidden }); value: sku.value,
hidden: hideAttributes,
disabled: disableAttributesOnDedicatedGatewayChange,
});
currentState.set("instances", {
value: instances.value,
hidden: hideAttributes,
disabled: disableAttributesOnDedicatedGatewayChange,
});
return currentState; return currentState;
}; };
const skuDropDownItems: ChoiceItem[] = [
{ label: "CosmosD4s", key: "Cosmos.D4s" },
{ label: "CosmosD8s", key: "Cosmos.D8s" },
{ label: "CosmosD16s", key: "Cosmos.D16s" },
{ label: "CosmosD32s", key: "Cosmos.D32s" },
];
const getSkus = async (): Promise<ChoiceItem[]> => { const getSkus = async (): Promise<ChoiceItem[]> => {
// TODO: get SKUs from getRegionSpecificSkus() RP call and return array of {label:..., key:...}. return skuDropDownItems;
throw new Error("getSkus not implemented.");
}; };
const getInstancesMin = async (): Promise<number> => { const getInstancesMin = async (): Promise<number> => {
// TODO: get SKUs from getRegionSpecificSkus() RP call and return array of {label:..., key:...}. return 1;
throw new Error("getInstancesMin not implemented.");
}; };
const getInstancesMax = async (): Promise<number> => { const getInstancesMax = async (): Promise<number> => {
// TODO: get SKUs from getRegionSpecificSkus() RP call and return array of {label:..., key:...}. return 5;
throw new Error("getInstancesMax not implemented.");
};
const validate = (currentValues: Map<string, SmartUiInput>): void => {
// TODO: add cusom validation logic to be called before Saving the data.
throw new Error(`validate not implemented. No. of properties to validate: ${currentValues.size}`);
}; };
@IsDisplayable() @IsDisplayable()
@@ -50,22 +66,80 @@ export default class SqlX extends SelfServeBaseClass {
}; };
public onSave = async (currentValues: Map<string, SmartUiInput>): Promise<SelfServeNotification> => { public onSave = async (currentValues: Map<string, SmartUiInput>): Promise<SelfServeNotification> => {
validate(currentValues); const response = await getCurrentProvisioningState();
// TODO: add pre processing logic before calling the updateDedicatedGatewayProvisioning() RP call.
throw new Error(`onSave not implemented. No. of properties to save: ${currentValues.size}`); // null implies the resource has not been provisioned.
if (response.status !== undefined && response.status !== ResourceStatus.Running.toString()) {
switch (response.status) {
case ResourceStatus.Creating.toString():
return { message: "CreateMessage", type: SelfServeNotificationType.error };
case ResourceStatus.Updating.toString():
return { message: "UpdateMessage", type: SelfServeNotificationType.error };
case ResourceStatus.Deleting.toString():
return { message: "DeleteMessage", type: SelfServeNotificationType.error };
default:
return { message: "CannotSave", type: SelfServeNotificationType.error };
}
}
const enableDedicatedGateway = currentValues.get("enableDedicatedGateway")?.value as boolean;
if (response.status !== undefined) {
if (!enableDedicatedGateway) {
try {
await deleteDedicatedGatewayResource();
return { message: "DedicatedGateway resource will be deleted.", type: SelfServeNotificationType.info };
} catch (e) {
return {
message: "Deleting Dedicated Gateway resource failed. DedicatedGateway will not be deleted.",
type: SelfServeNotificationType.error,
};
}
} else {
// Check for scaling up/down/in/out
}
} else {
if (enableDedicatedGateway) {
const sku = currentValues.get("sku")?.value as string;
const instances = currentValues.get("instances").value as number;
try {
await updateDedicatedGatewayResource(sku, instances);
return { message: "Dedicated Gateway resource will be provisioned.", type: SelfServeNotificationType.info };
} catch (e) {
return {
message: "Updating Dedicated Gateway resource failed. Dedicated Gateway will not be updated.",
type: SelfServeNotificationType.error,
};
}
}
}
return { message: "No updates were applied at this time", type: SelfServeNotificationType.warning };
}; };
public initialize = async (): Promise<Map<string, SmartUiInput>> => { public initialize = async (): Promise<Map<string, SmartUiInput>> => {
// TODO: get initialization data from initializeDedicatedGatewayProvisioning() RP call. // Based on the RP call enableDedicatedGateway will be true if it has not yet been enabled and false if it has.
throw new Error("onSave not implemented"); const defaults = new Map<string, SmartUiInput>();
const enableDedicatedGateway = false;
defaults.set("enableDedicatedGateway", { value: enableDedicatedGateway, hidden: false, disabled: false });
defaults.set("sku", { value: "Cosmos.D4s", hidden: !enableDedicatedGateway, disabled: false });
defaults.set("instances", { value: await getInstancesMin(), hidden: !enableDedicatedGateway, disabled: false });
const response = await getCurrentProvisioningState();
if (response.status !== undefined) {
disableAttributesOnDedicatedGatewayChange = true;
defaults.set("enableDedicatedGateway", { value: true, hidden: false, disabled: false });
defaults.set("sku", { value: response.sku, hidden: false, disabled: true });
defaults.set("instances", { value: response.instances, hidden: false, disabled: true });
}
return defaults;
}; };
@Values({ @Values({
description: { description: {
textTKey: "Provisioning dedicated gateways for SqlX accounts.", textTKey: "DedicatedGatewayDescription",
link: { link: {
href: "https://docs.microsoft.com/en-us/azure/cosmos-db/introduction", href: "https://docs.microsoft.com/en-us/azure/cosmos-db/introduction",
textTKey: "Learn more about dedicated gateway.", textTKey: "LearnAboutDedicatedGateway",
}, },
}, },
}) })
@@ -73,7 +147,7 @@ export default class SqlX extends SelfServeBaseClass {
@OnChange(onEnableDedicatedGatewayChange) @OnChange(onEnableDedicatedGatewayChange)
@Values({ @Values({
labelTKey: "Dedicated Gateway", labelTKey: "DedicatedGateway",
trueLabelTKey: "Enable", trueLabelTKey: "Enable",
falseLabelTKey: "Disable", falseLabelTKey: "Disable",
}) })
@@ -87,7 +161,7 @@ export default class SqlX extends SelfServeBaseClass {
sku: ChoiceItem; sku: ChoiceItem;
@Values({ @Values({
labelTKey: "Number of instances", labelTKey: "NumberOfInstances",
min: getInstancesMin, min: getInstancesMin,
max: getInstancesMax, max: getInstancesMax,
step: 1, step: 1,

View File

@@ -0,0 +1,31 @@
export type SqlxServiceResource = {
id: string;
name: string;
type: string;
properties: SqlxServiceProps;
locations: SqlxServiceLocations;
};
export type SqlxServiceProps = {
serviceType: string;
creationTime: string;
status: string;
instanceSize: string;
instanceCount: number;
sqlxEndPoint: string;
};
export type SqlxServiceLocations = {
location: string;
status: string;
sqlxEndpoint: string;
};
export type UpdateDedicatedGatewayRequestParameters = {
properties: UpdateDedicatedGatewayRequestProperties;
};
export type UpdateDedicatedGatewayRequestProperties = {
instanceSize: string;
instanceCount: number;
serviceType: string;
};