Dedicated Gateway Portal Changes (#540)
* Portal changes for DedicatedGateway Changes to support creation and deletion of DedicatedGateway resource. Tested locally with various scenarios. * Portal changes for DedicatedGateway. CR feedback * Stylecop changes * Removing TODO comments * exposed baselineValues * added getOnSaveNotification * disable UI when onSave is taking place * minro edits * made polling optional * added optional polling * added default * Added portal notifications * merged more changes * minor edits * added label for description * Added correlationids and polling of refresh * Added correlationids and polling of refresh * minor edit * added label tooltip * removed ClassInfo decorator * Added dynamic decription * added info and warninf types for description * more changes to promise retry * promise retry changes * compile errors fixed * New changes * added operationstatus link * merged sqlxEdits * undid sqlx changes * added completed notification * passed retryInterval in notif options * more changes * added polling on landing on the page * edits for error display * added keys blade link * added link generation * added link to blade * Modified info and description * fixed format errors * Second cut of the Portal * OnChange for Number of instances * added keys for texts * fixed lint errors * Added support for undefined dynamic description * fixed failing test * disable save/discard buttons * fixed sqlx errors * Dedicated Gateway changes to add the keys blade * Change connectionStringText * Change connectionStringText * Text changes * Added UI improvements * Code review feedback * undid package lock changes Co-authored-by: Srinath Narayanan <srnara@microsoft.com>
This commit is contained in:
parent
be4e490a64
commit
909a9fa522
|
@ -204,7 +204,7 @@
|
||||||
"pack:fast": "node --max_old_space_size=10196 ./node_modules/webpack/bin/webpack.js --mode development --progress",
|
"pack:fast": "node --max_old_space_size=10196 ./node_modules/webpack/bin/webpack.js --mode development --progress",
|
||||||
"copyToConsumers": "node copyToConsumers",
|
"copyToConsumers": "node copyToConsumers",
|
||||||
"test": "rimraf coverage && jest",
|
"test": "rimraf coverage && jest",
|
||||||
"test:e2e": "jest -c ./jest.config.e2e.js --detectOpenHandles selfServeExample.spec.ts",
|
"test:e2e": "jest -c ./jest.config.e2e.js --detectOpenHandles",
|
||||||
"watch": "npm run start",
|
"watch": "npm run start",
|
||||||
"wait-for-server": "wait-on -t 240000 -i 5000 -v https-get://0.0.0.0:1234/",
|
"wait-for-server": "wait-on -t 240000 -i 5000 -v https-get://0.0.0.0:1234/",
|
||||||
"build:ase": "gulp build:ase",
|
"build:ase": "gulp build:ase",
|
||||||
|
|
|
@ -36,6 +36,57 @@
|
||||||
"SubmissionMessageErrorText": "Data update failed because of errors.",
|
"SubmissionMessageErrorText": "Data update failed because of errors.",
|
||||||
"OnSaveFailureMessage": "Data save operation not currently permitted."
|
"OnSaveFailureMessage": "Data save operation not currently permitted."
|
||||||
},
|
},
|
||||||
"SqlX": {}
|
"SqlX": {
|
||||||
|
"DedicatedGatewayDescription": "Provision a dedicated gateway cluster for your Azure Cosmos DB account. A dedicated gateway is compute that is a front-end to data in your Azure Cosmos DB account. Your dedicated gateway automatically includes the integrated cache, which can improve read performance. ",
|
||||||
|
"DedicatedGateway": "Dedicated Gateway",
|
||||||
|
"Enable": "Enable",
|
||||||
|
"Disable": "Disable",
|
||||||
|
"LearnAboutDedicatedGateway": "Learn more about dedicated gateway.",
|
||||||
|
"DeprovisioningDetailsText": "Learn more about deprovisioning the dedicated gateway.",
|
||||||
|
"DedicatedGatewayPricing": "Learn more about dedicated gateway pricing",
|
||||||
|
"SKUs": "SKUs",
|
||||||
|
"SKUsPlaceHolder": "Select SKUs",
|
||||||
|
"NumberOfInstances": "Number of instances",
|
||||||
|
"CosmosD4s": "Cosmos.D4s (General Purpose Cosmos Compute with 4 vCPUs, 16 GB Memory)",
|
||||||
|
"CosmosD8s": "Cosmos.D8s (General Purpose Cosmos Compute with 8 vCPUs, 32 GB Memory)",
|
||||||
|
"CosmosD16s": "Cosmos.D16s (General Purpose Cosmos Compute with 16 vCPUs, 64 GB Memory)",
|
||||||
|
"CosmosD32s": "Cosmos.D32s (General Purpose Cosmos Compute with 32 vCPUs, 128 GB Memory)",
|
||||||
|
"CreateMessage": "Dedicated gateway resource is being created.",
|
||||||
|
"CreateInitializeTitle": "Provisioning resource",
|
||||||
|
"CreateInitializeMessage": "Dedicated gateway resource will be provisioned.",
|
||||||
|
"CreateSuccessTitle": "Resource provisioned",
|
||||||
|
"CreateSuccesseMessage": "Dedicated gateway resource provisioned.",
|
||||||
|
"CreateFailureTitle": "Failed to provision resource",
|
||||||
|
"CreateFailureMessage": "Dedicated gateway resource provisioning failed.",
|
||||||
|
"UpdateMessage": "Dedicated gateway resource is being updated.",
|
||||||
|
"UpdateInitializeTitle": "Updating resource",
|
||||||
|
"UpdateInitializeMessage": "Dedicated gateway resource will be updated.",
|
||||||
|
"UpdateSuccessTitle": "Resource updated",
|
||||||
|
"UpdateSuccesseMessage": "Dedicated gateway resource updated.",
|
||||||
|
"UpdateFailureTitle": "Failed to update resource",
|
||||||
|
"UpdateFailureMessage": "Dedicated gateway resource updation failed.",
|
||||||
|
"DeleteMessage": "Dedicated gateway resource is being deleted.",
|
||||||
|
"DeleteInitializeTitle": "Deleting resource",
|
||||||
|
"DeleteInitializeMessage": "Dedicated gateway resource will be deleted.",
|
||||||
|
"DeleteSuccessTitle": "Resource deleted",
|
||||||
|
"DeleteSuccesseMessage": "Dedicated gateway resource deleted.",
|
||||||
|
"DeleteFailureTitle": "Failed to delete resource",
|
||||||
|
"DeleteFailureMessage": "Dedicated gateway resource deletion failed.",
|
||||||
|
"CannotSave": "Cannot save the changes to the Dedicated gateway resource at the moment",
|
||||||
|
"DedicatedGatewayEndpoint": "Dedicated gatewayEndpoint",
|
||||||
|
"NoValue": "",
|
||||||
|
"SKUDetails": "SKU Details: ",
|
||||||
|
"CosmosD4Details": "General Purpose Cosmos Compute with 4 vCPUs, 16 GB Memory",
|
||||||
|
"CosmosD8Details": "General Purpose Cosmos Compute with 8 vCPUs, 32 GB Memory",
|
||||||
|
"CosmosD16Details": "General Purpose Cosmos Compute with 16 vCPUs, 64 GB Memory",
|
||||||
|
"CosmosD32Details": "General Purpose Cosmos Compute with 32 vCPUs, 128 GB Memory",
|
||||||
|
"Cost": "Cost",
|
||||||
|
"CostText": "Hourly cost of the dedicated gateway resource depends on the SKU selection, number of instances per region, and number of regions.",
|
||||||
|
"ConnectionString": "Connection String",
|
||||||
|
"ConnectionStringText": "To use the dedicated gateway, use the connection string shown in ",
|
||||||
|
"KeysBlade": "the keys blade",
|
||||||
|
"WarningBannerOnUpdate": "Adding or modifying dedicated gateway instances may affect your bill.",
|
||||||
|
"WarningBannerOnDelete": "After deprovisioning the dedicated gateway, you must update any applications using the old dedicated gateway connection string."
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -1,33 +1,98 @@
|
||||||
import { RefreshResult } from "../SelfServeTypes";
|
import { RefreshResult } from "../SelfServeTypes";
|
||||||
|
import { userContext } from "../../UserContext";
|
||||||
|
import { armRequestWithoutPolling } from "../../Utils/arm/request";
|
||||||
|
import { configContext } from "../../ConfigContext";
|
||||||
|
import { SqlxServiceResource, UpdateDedicatedGatewayRequestParameters } from "./SqlxTypes";
|
||||||
|
|
||||||
|
const apiVersion = "2020-06-01-preview";
|
||||||
|
|
||||||
|
export enum ResourceStatus {
|
||||||
|
Running = "Running",
|
||||||
|
Creating = "Creating",
|
||||||
|
Updating = "Updating",
|
||||||
|
Deleting = "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<string> => {
|
||||||
// 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",
|
||||||
|
},
|
||||||
|
};
|
||||||
|
const armRequestResult = await armRequestWithoutPolling({
|
||||||
|
host: configContext.ARM_ENDPOINT,
|
||||||
|
path,
|
||||||
|
method: "PUT",
|
||||||
|
apiVersion,
|
||||||
|
body,
|
||||||
|
});
|
||||||
|
return armRequestResult.operationStatusUrl;
|
||||||
};
|
};
|
||||||
|
|
||||||
export const updateDedicatedGatewayProvisioning = async (sku: string, instances: number): Promise<void> => {
|
export const deleteDedicatedGatewayResource = async (): Promise<string> => {
|
||||||
// TODO: write RP call to update dedicated gateway provisioning
|
const path = getPath(userContext.subscriptionId, userContext.resourceGroup, userContext.databaseAccount.name);
|
||||||
throw new Error(
|
const armRequestResult = await armRequestWithoutPolling({
|
||||||
`updateDedicatedGatewayProvisioning not implemented. Parameters- sku: ${sku}, instances:${instances}`
|
host: configContext.ARM_ENDPOINT,
|
||||||
);
|
path,
|
||||||
|
method: "DELETE",
|
||||||
|
apiVersion,
|
||||||
|
});
|
||||||
|
return armRequestResult.operationStatusUrl;
|
||||||
};
|
};
|
||||||
|
|
||||||
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");
|
const armRequestResult = await armRequestWithoutPolling<SqlxServiceResource>({
|
||||||
|
host: configContext.ARM_ENDPOINT,
|
||||||
|
path,
|
||||||
|
method: "GET",
|
||||||
|
apiVersion,
|
||||||
|
});
|
||||||
|
return armRequestResult.result;
|
||||||
|
};
|
||||||
|
|
||||||
|
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, 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 };
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
import { IsDisplayable, OnChange, Values } from "../Decorators";
|
import { IsDisplayable, OnChange, RefreshOptions, Values } from "../Decorators";
|
||||||
import {
|
import {
|
||||||
ChoiceItem,
|
ChoiceItem,
|
||||||
|
Description,
|
||||||
DescriptionType,
|
DescriptionType,
|
||||||
InputType,
|
InputType,
|
||||||
NumberUiType,
|
NumberUiType,
|
||||||
|
@ -9,65 +10,284 @@ import {
|
||||||
SelfServeBaseClass,
|
SelfServeBaseClass,
|
||||||
SmartUiInput,
|
SmartUiInput,
|
||||||
} from "../SelfServeTypes";
|
} from "../SelfServeTypes";
|
||||||
import { refreshDedicatedGatewayProvisioning } from "./SqlX.rp";
|
import { BladeType, generateBladeLink } from "../SelfServeUtils";
|
||||||
|
import {
|
||||||
|
deleteDedicatedGatewayResource,
|
||||||
|
getCurrentProvisioningState,
|
||||||
|
refreshDedicatedGatewayProvisioning,
|
||||||
|
updateDedicatedGatewayResource,
|
||||||
|
} from "./SqlX.rp";
|
||||||
|
|
||||||
|
const costPerHourValue: Description = {
|
||||||
|
textTKey: "CostText",
|
||||||
|
type: DescriptionType.Text,
|
||||||
|
link: {
|
||||||
|
href: "https://azure.microsoft.com/en-us/pricing/details/cosmos-db/",
|
||||||
|
textTKey: "DedicatedGatewayPricing",
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
const connectionStringValue: Description = {
|
||||||
|
textTKey: "ConnectionStringText",
|
||||||
|
type: DescriptionType.Text,
|
||||||
|
link: {
|
||||||
|
href: generateBladeLink(BladeType.SqlKeys),
|
||||||
|
textTKey: "KeysBlade",
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
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>
|
||||||
|
): Map<string, SmartUiInput> => {
|
||||||
|
currentValues.set("instances", { value: newValue });
|
||||||
|
currentValues.set("warningBanner", {
|
||||||
|
value: { textTKey: "WarningBannerOnUpdate" } as Description,
|
||||||
|
hidden: false,
|
||||||
|
});
|
||||||
|
|
||||||
|
return currentValues;
|
||||||
|
};
|
||||||
|
|
||||||
const onEnableDedicatedGatewayChange = (
|
const onEnableDedicatedGatewayChange = (
|
||||||
newValue: InputType,
|
newValue: InputType,
|
||||||
currentState: Map<string, SmartUiInput>
|
currentValues: Map<string, SmartUiInput>,
|
||||||
|
baselineValues: ReadonlyMap<string, SmartUiInput>
|
||||||
): Map<string, SmartUiInput> => {
|
): Map<string, SmartUiInput> => {
|
||||||
const sku = currentState.get("sku");
|
currentValues.set("enableDedicatedGateway", { value: newValue });
|
||||||
const instances = currentState.get("instances");
|
const dedicatedGatewayOriginallyEnabled = baselineValues.get("enableDedicatedGateway")?.value as boolean;
|
||||||
const isSkuHidden = newValue === undefined || !(newValue as boolean);
|
if (dedicatedGatewayOriginallyEnabled === newValue) {
|
||||||
currentState.set("enableDedicatedGateway", { value: newValue });
|
currentValues.set("sku", baselineValues.get("sku"));
|
||||||
currentState.set("sku", { value: sku.value, hidden: isSkuHidden });
|
currentValues.set("instances", baselineValues.get("instances"));
|
||||||
currentState.set("instances", { value: instances.value, hidden: isSkuHidden });
|
currentValues.set("skuDetails", baselineValues.get("skuDetails"));
|
||||||
return currentState;
|
currentValues.set("costPerHour", baselineValues.get("costPerHour"));
|
||||||
|
currentValues.set("warningBanner", baselineValues.get("warningBanner"));
|
||||||
|
currentValues.set("connectionString", baselineValues.get("connectionString"));
|
||||||
|
return currentValues;
|
||||||
|
}
|
||||||
|
|
||||||
|
currentValues.set("warningBanner", undefined);
|
||||||
|
if (newValue === true) {
|
||||||
|
currentValues.set("warningBanner", {
|
||||||
|
value: {
|
||||||
|
textTKey: "WarningBannerOnUpdate",
|
||||||
|
link: {
|
||||||
|
href: "https://docs.microsoft.com/en-us/azure/cosmos-db/introduction",
|
||||||
|
textTKey: "DedicatedGatewayPricing",
|
||||||
|
},
|
||||||
|
} as Description,
|
||||||
|
hidden: false,
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
currentValues.set("warningBanner", {
|
||||||
|
value: {
|
||||||
|
textTKey: "WarningBannerOnDelete",
|
||||||
|
link: {
|
||||||
|
href: "https://docs.microsoft.com/en-us/azure/cosmos-db/introduction",
|
||||||
|
textTKey: "DeprovisioningDetailsText",
|
||||||
|
},
|
||||||
|
} as Description,
|
||||||
|
hidden: false,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
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: dedicatedGatewayOriginallyEnabled,
|
||||||
|
});
|
||||||
|
currentValues.set("instances", {
|
||||||
|
value: instances.value,
|
||||||
|
hidden: hideAttributes,
|
||||||
|
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,
|
||||||
|
});
|
||||||
|
|
||||||
|
return currentValues;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const skuDropDownItems: ChoiceItem[] = [
|
||||||
|
{ label: "CosmosD4s", key: CosmosD4s },
|
||||||
|
{ label: "CosmosD8s", key: CosmosD8s },
|
||||||
|
{ label: "CosmosD16s", key: CosmosD16s },
|
||||||
|
{ label: "CosmosD32s", key: CosmosD32s },
|
||||||
|
];
|
||||||
|
|
||||||
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()
|
||||||
|
@RefreshOptions({ retryIntervalInMs: 20000 })
|
||||||
export default class SqlX extends SelfServeBaseClass {
|
export default class SqlX extends SelfServeBaseClass {
|
||||||
public onRefresh = async (): Promise<RefreshResult> => {
|
public onRefresh = async (): Promise<RefreshResult> => {
|
||||||
return refreshDedicatedGatewayProvisioning();
|
return await refreshDedicatedGatewayProvisioning();
|
||||||
};
|
};
|
||||||
|
|
||||||
public onSave = async (currentValues: Map<string, SmartUiInput>): Promise<OnSaveResult> => {
|
public onSave = async (
|
||||||
validate(currentValues);
|
currentValues: Map<string, SmartUiInput>,
|
||||||
// TODO: add pre processing logic before calling the updateDedicatedGatewayProvisioning() RP call.
|
baselineValues: Map<string, SmartUiInput>
|
||||||
throw new Error(`onSave not implemented. No. of properties to save: ${currentValues.size}`);
|
): Promise<OnSaveResult> => {
|
||||||
|
const dedicatedGatewayCurrentlyEnabled = currentValues.get("enableDedicatedGateway")?.value as boolean;
|
||||||
|
const dedicatedGatewayOriginallyEnabled = baselineValues.get("enableDedicatedGateway")?.value as boolean;
|
||||||
|
|
||||||
|
currentValues.set("warningBanner", undefined);
|
||||||
|
|
||||||
|
//TODO : Add try catch for each RP call and return relevant notifications
|
||||||
|
if (dedicatedGatewayOriginallyEnabled) {
|
||||||
|
if (!dedicatedGatewayCurrentlyEnabled) {
|
||||||
|
const operationStatusUrl = await deleteDedicatedGatewayResource();
|
||||||
|
return {
|
||||||
|
operationStatusUrl: operationStatusUrl,
|
||||||
|
portalNotification: {
|
||||||
|
initialize: {
|
||||||
|
titleTKey: "DeleteInitializeTitle",
|
||||||
|
messageTKey: "DeleteInitializeMessage",
|
||||||
|
},
|
||||||
|
success: {
|
||||||
|
titleTKey: "DeleteSuccessTitle",
|
||||||
|
messageTKey: "DeleteSuccesseMessage",
|
||||||
|
},
|
||||||
|
failure: {
|
||||||
|
titleTKey: "DeleteFailureTitle",
|
||||||
|
messageTKey: "DeleteFailureMessage",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
} else {
|
||||||
|
// Check for scaling up/down/in/out
|
||||||
|
return {
|
||||||
|
operationStatusUrl: undefined,
|
||||||
|
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 updateDedicatedGatewayResource(sku, instances);
|
||||||
|
return {
|
||||||
|
operationStatusUrl: operationStatusUrl,
|
||||||
|
portalNotification: {
|
||||||
|
initialize: {
|
||||||
|
titleTKey: "CreateInitializeTitle",
|
||||||
|
messageTKey: "CreateInitializeTitle",
|
||||||
|
},
|
||||||
|
success: {
|
||||||
|
titleTKey: "CreateSuccessTitle",
|
||||||
|
messageTKey: "CreateSuccesseMessage",
|
||||||
|
},
|
||||||
|
failure: {
|
||||||
|
titleTKey: "CreateFailureTitle",
|
||||||
|
messageTKey: "CreateFailureMessage",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
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>();
|
||||||
|
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);
|
||||||
|
|
||||||
|
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("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("warningBanner", undefined);
|
||||||
|
return defaults;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@Values({
|
||||||
|
isDynamicDescription: true,
|
||||||
|
})
|
||||||
|
warningBanner: string;
|
||||||
|
|
||||||
@Values({
|
@Values({
|
||||||
description: {
|
description: {
|
||||||
textTKey: "Provisioning dedicated gateways for SqlX accounts.",
|
textTKey: "DedicatedGatewayDescription",
|
||||||
type: DescriptionType.Text,
|
type: DescriptionType.Text,
|
||||||
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",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
|
@ -75,25 +295,45 @@ export default class SqlX extends SelfServeBaseClass {
|
||||||
|
|
||||||
@OnChange(onEnableDedicatedGatewayChange)
|
@OnChange(onEnableDedicatedGatewayChange)
|
||||||
@Values({
|
@Values({
|
||||||
labelTKey: "Dedicated Gateway",
|
labelTKey: "DedicatedGateway",
|
||||||
trueLabelTKey: "Enable",
|
trueLabelTKey: "Provisioned",
|
||||||
falseLabelTKey: "Disable",
|
falseLabelTKey: "Deprovisioned",
|
||||||
})
|
})
|
||||||
enableDedicatedGateway: boolean;
|
enableDedicatedGateway: boolean;
|
||||||
|
|
||||||
|
@OnChange(onSKUChange)
|
||||||
@Values({
|
@Values({
|
||||||
labelTKey: "SKUs",
|
labelTKey: "SKUs",
|
||||||
choices: getSkus,
|
choices: getSkus,
|
||||||
placeholderTKey: "Select SKUs",
|
placeholderTKey: "SKUsPlaceHolder",
|
||||||
})
|
})
|
||||||
sku: ChoiceItem;
|
sku: ChoiceItem;
|
||||||
|
|
||||||
@Values({
|
@Values({
|
||||||
labelTKey: "Number of instances",
|
labelTKey: "SKUDetails",
|
||||||
|
isDynamicDescription: true,
|
||||||
|
})
|
||||||
|
skuDetails: string;
|
||||||
|
|
||||||
|
@OnChange(onNumberOfInstancesChange)
|
||||||
|
@Values({
|
||||||
|
labelTKey: "NumberOfInstances",
|
||||||
min: getInstancesMin,
|
min: getInstancesMin,
|
||||||
max: getInstancesMax,
|
max: getInstancesMax,
|
||||||
step: 1,
|
step: 1,
|
||||||
uiType: NumberUiType.Spinner,
|
uiType: NumberUiType.Spinner,
|
||||||
})
|
})
|
||||||
instances: number;
|
instances: number;
|
||||||
|
|
||||||
|
@Values({
|
||||||
|
labelTKey: "Cost",
|
||||||
|
isDynamicDescription: true,
|
||||||
|
})
|
||||||
|
costPerHour: string;
|
||||||
|
|
||||||
|
@Values({
|
||||||
|
labelTKey: "ConnectionString",
|
||||||
|
isDynamicDescription: true,
|
||||||
|
})
|
||||||
|
connectionString: string;
|
||||||
}
|
}
|
||||||
|
|
|
@ -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;
|
||||||
|
};
|
Loading…
Reference in New Issue