mirror of
https://github.com/Azure/cosmos-explorer.git
synced 2025-12-27 21:01:57 +00:00
Added more Self Serve functionalities (#401)
* added recursion and inition decorators * working version * added todo comment and removed console.log * Added Recursive add * removed type requirement * proper resolution of promises * added custom element and base class * Made selfServe standalone page * Added custom renderer as async type * Added overall defaults * added inital open from data explorer * removed landingpage * added feature for self serve type * renamed sqlx->example and added invalid type * Added comments for Example * removed unnecessary changes * Resolved PR comments Added tests Moved onSubmt and initialize inside base class Moved testExplorer to separate folder made fields of SelfServe Class non static * fixed lint errors * fixed compilation errors * Removed reactbinding changes * renamed dropdown -> choice * Added SelfServeComponent * Addressed PR comments * added toggle, visibility, text display,commandbar * added sqlx example * added onRefrssh * formatting changes * rmoved radioswitch display * updated smartui tests * Added more tests * onSubmit -> onSave * Resolved PR comments
This commit is contained in:
committed by
GitHub
parent
b0b973b21a
commit
49bf8c60db
@@ -1,37 +1,57 @@
|
||||
import { PropertyInfo, OnChange, Values } from "../PropertyDecorators";
|
||||
import { ClassInfo, IsDisplayable } from "../ClassDecorators";
|
||||
import { SelfServeBaseClass } from "../SelfServeUtils";
|
||||
import { ChoiceItem, Info, InputType, UiType } from "../../Explorer/Controls/SmartUi/SmartUiComponent";
|
||||
import { SessionStorageUtility } from "../../Shared/StorageUtility";
|
||||
import { PropertyInfo, OnChange, Values, IsDisplayable, ClassInfo } from "../Decorators";
|
||||
import {
|
||||
ChoiceItem,
|
||||
Info,
|
||||
InputType,
|
||||
NumberUiType,
|
||||
RefreshResult,
|
||||
SelfServeBaseClass,
|
||||
SelfServeNotification,
|
||||
SelfServeNotificationType,
|
||||
SmartUiInput,
|
||||
} from "../SelfServeTypes";
|
||||
import { onRefreshSelfServeExample, getMaxThroughput, Regions, update, initialize } from "./SelfServeExample.rp";
|
||||
|
||||
export enum Regions {
|
||||
NorthCentralUS = "NCUS",
|
||||
WestUS = "WUS",
|
||||
EastUS2 = "EUS2",
|
||||
}
|
||||
|
||||
export const regionDropdownItems: ChoiceItem[] = [
|
||||
const regionDropdownItems: ChoiceItem[] = [
|
||||
{ label: "North Central US", key: Regions.NorthCentralUS },
|
||||
{ label: "West US", key: Regions.WestUS },
|
||||
{ label: "East US 2", key: Regions.EastUS2 },
|
||||
];
|
||||
|
||||
export const selfServeExampleInfo: Info = {
|
||||
const selfServeExampleInfo: Info = {
|
||||
message: "This is a self serve class",
|
||||
};
|
||||
|
||||
export const regionDropdownInfo: Info = {
|
||||
const regionDropdownInfo: Info = {
|
||||
message: "More regions can be added in the future.",
|
||||
};
|
||||
|
||||
const onDbThroughputChange = (currentState: Map<string, InputType>, newValue: InputType): Map<string, InputType> => {
|
||||
currentState.set("dbThroughput", newValue);
|
||||
currentState.set("collectionThroughput", newValue);
|
||||
const onRegionsChange = (currentState: Map<string, SmartUiInput>, newValue: InputType): Map<string, SmartUiInput> => {
|
||||
currentState.set("regions", { value: newValue });
|
||||
const currentEnableLogging = currentState.get("enableLogging");
|
||||
if (newValue === Regions.NorthCentralUS) {
|
||||
currentState.set("enableLogging", { value: false, disabled: true });
|
||||
} else {
|
||||
currentState.set("enableLogging", { value: currentEnableLogging.value, disabled: false });
|
||||
}
|
||||
return currentState;
|
||||
};
|
||||
|
||||
const initializeMaxThroughput = async (): Promise<number> => {
|
||||
return 10000;
|
||||
const onEnableDbLevelThroughputChange = (
|
||||
currentState: Map<string, SmartUiInput>,
|
||||
newValue: InputType
|
||||
): Map<string, SmartUiInput> => {
|
||||
currentState.set("enableDbLevelThroughput", { value: newValue });
|
||||
const currentDbThroughput = currentState.get("dbThroughput");
|
||||
const isDbThroughputHidden = newValue === undefined || !(newValue as boolean);
|
||||
currentState.set("dbThroughput", { value: currentDbThroughput.value, hidden: isDbThroughputHidden });
|
||||
return currentState;
|
||||
};
|
||||
|
||||
const validate = (currentvalues: Map<string, SmartUiInput>): void => {
|
||||
if (!currentvalues.get("regions").value || !currentvalues.get("accountName").value) {
|
||||
throw new Error("Regions and AccountName should not be empty.");
|
||||
}
|
||||
};
|
||||
|
||||
/*
|
||||
@@ -40,8 +60,9 @@ const initializeMaxThroughput = async (): Promise<number> => {
|
||||
Each self serve class
|
||||
- Needs to extends the SelfServeBase class.
|
||||
- Needs to have the @IsDisplayable() decorator to tell the compiler that UI needs to be generated from this class.
|
||||
- Needs to define an onSubmit() function, a callback for when the submit button is clicked.
|
||||
- Needs to define an onSave() function, a callback for when the submit button is clicked.
|
||||
- Needs to define an initialize() function, to set default values for the inputs.
|
||||
- Needs to define an onRefresh() function, a callback for when the refresh button is clicked.
|
||||
|
||||
You can test this self serve UI by using the featureflag '?feature.selfServeType=example'
|
||||
and plumb in similar feature flags for your own self serve class.
|
||||
@@ -61,25 +82,46 @@ const initializeMaxThroughput = async (): Promise<number> => {
|
||||
@ClassInfo(selfServeExampleInfo)
|
||||
export default class SelfServeExample extends SelfServeBaseClass {
|
||||
/*
|
||||
onSubmit()
|
||||
onRefresh()
|
||||
- role : Callback that is triggerrd when the refresh button is clicked. You should perform the your rest API
|
||||
call to check if the update action is completed.
|
||||
- returns:
|
||||
RefreshResult -
|
||||
isComponentUpdating: Indicated if the state is still being updated
|
||||
notificationMessage: Notification message to be shown in case the component is still being updated
|
||||
i.e, isComponentUpdating is true
|
||||
*/
|
||||
public onRefresh = async (): Promise<RefreshResult> => {
|
||||
return onRefreshSelfServeExample();
|
||||
};
|
||||
|
||||
/*
|
||||
onSave()
|
||||
- input: (currentValues: Map<string, InputType>) => Promise<void>
|
||||
- role: Callback that is triggerred when the submit button is clicked. You should perform your rest API
|
||||
calls here using the data from the different inputs passed as a Map to this callback function.
|
||||
|
||||
In this example, the onSubmit callback simply sets the value for keys corresponding to the field name
|
||||
In this example, the onSave callback simply sets the value for keys corresponding to the field name
|
||||
in the SessionStorage.
|
||||
- returns: SelfServeNotification -
|
||||
message: The message to be displayed in the message bar after the onSave is completed
|
||||
type: The type of message bar to be used (info, warning, error)
|
||||
*/
|
||||
public onSubmit = async (currentValues: Map<string, InputType>): Promise<void> => {
|
||||
SessionStorageUtility.setEntry("regions", currentValues.get("regions")?.toString());
|
||||
SessionStorageUtility.setEntry("enableLogging", currentValues.get("enableLogging")?.toString());
|
||||
SessionStorageUtility.setEntry("accountName", currentValues.get("accountName")?.toString());
|
||||
SessionStorageUtility.setEntry("dbThroughput", currentValues.get("dbThroughput")?.toString());
|
||||
SessionStorageUtility.setEntry("collectionThroughput", currentValues.get("collectionThroughput")?.toString());
|
||||
public onSave = async (currentValues: Map<string, SmartUiInput>): Promise<SelfServeNotification> => {
|
||||
validate(currentValues);
|
||||
const regions = Regions[currentValues.get("regions")?.value as keyof typeof Regions];
|
||||
const enableLogging = currentValues.get("enableLogging")?.value as boolean;
|
||||
const accountName = currentValues.get("accountName")?.value as string;
|
||||
const collectionThroughput = currentValues.get("collectionThroughput")?.value as number;
|
||||
const enableDbLevelThroughput = currentValues.get("enableDbLevelThroughput")?.value as boolean;
|
||||
let dbThroughput = currentValues.get("dbThroughput")?.value as number;
|
||||
dbThroughput = enableDbLevelThroughput ? dbThroughput : undefined;
|
||||
await update(regions, enableLogging, accountName, collectionThroughput, dbThroughput);
|
||||
return { message: "submitted successfully", type: SelfServeNotificationType.info };
|
||||
};
|
||||
|
||||
/*
|
||||
initialize()
|
||||
- input: () => Promise<Map<string, InputType>>
|
||||
- role: Set default values for the properties of this class.
|
||||
|
||||
The properties of this class (namely regions, enableLogging, accountName, dbThroughput, collectionThroughput),
|
||||
@@ -87,24 +129,46 @@ export default class SelfServeExample extends SelfServeBaseClass {
|
||||
defaults can be set by setting values in a Map corresponding to the field's name.
|
||||
|
||||
Typically, you can make rest calls in the async initialize function, to fetch the initial values for
|
||||
these fields. This is called after the onSubmit callback, to reinitialize the defaults.
|
||||
these fields. This is called after the onSave callback, to reinitialize the defaults.
|
||||
|
||||
In this example, the initialize function simply reads the SessionStorage to fetch the default values
|
||||
for these fields. These are then set when the changes are submitted.
|
||||
- returns: () => Promise<Map<string, InputType>>
|
||||
*/
|
||||
public initialize = async (): Promise<Map<string, InputType>> => {
|
||||
const defaults = new Map<string, InputType>();
|
||||
defaults.set("regions", SessionStorageUtility.getEntry("regions"));
|
||||
defaults.set("enableLogging", SessionStorageUtility.getEntry("enableLogging") === "true");
|
||||
const stringInput = SessionStorageUtility.getEntry("accountName");
|
||||
defaults.set("accountName", stringInput ? stringInput : "");
|
||||
const numberSliderInput = parseInt(SessionStorageUtility.getEntry("dbThroughput"));
|
||||
defaults.set("dbThroughput", isNaN(numberSliderInput) ? 1 : numberSliderInput);
|
||||
const numberSpinnerInput = parseInt(SessionStorageUtility.getEntry("collectionThroughput"));
|
||||
defaults.set("collectionThroughput", isNaN(numberSpinnerInput) ? 1 : numberSpinnerInput);
|
||||
public initialize = async (): Promise<Map<string, SmartUiInput>> => {
|
||||
const initializeResponse = await initialize();
|
||||
const defaults = new Map<string, SmartUiInput>();
|
||||
defaults.set("regions", { value: initializeResponse.regions });
|
||||
defaults.set("enableLogging", { value: initializeResponse.enableLogging });
|
||||
const accountName = initializeResponse.accountName;
|
||||
defaults.set("accountName", { value: accountName ? accountName : "" });
|
||||
defaults.set("collectionThroughput", { value: initializeResponse.collectionThroughput });
|
||||
const enableDbLevelThroughput = !!initializeResponse.dbThroughput;
|
||||
defaults.set("enableDbLevelThroughput", { value: enableDbLevelThroughput });
|
||||
defaults.set("dbThroughput", { value: initializeResponse.dbThroughput, hidden: !enableDbLevelThroughput });
|
||||
return defaults;
|
||||
};
|
||||
|
||||
/*
|
||||
@Values() :
|
||||
- input: NumberInputOptions | StringInputOptions | BooleanInputOptions | ChoiceInputOptions | DescriptionDisplay
|
||||
- role: Specifies the required options to display the property as
|
||||
a) TextBox for text input
|
||||
b) Spinner/Slider for number input
|
||||
c) Radio buton/Toggle for boolean input
|
||||
d) Dropdown for choice input
|
||||
e) Text (with optional hyperlink) for descriptions
|
||||
*/
|
||||
@Values({
|
||||
description: {
|
||||
text: "This class sets collection and database throughput.",
|
||||
link: {
|
||||
href: "https://docs.microsoft.com/en-us/azure/cosmos-db/introduction",
|
||||
text: "Click here for more information",
|
||||
},
|
||||
},
|
||||
})
|
||||
description: string;
|
||||
/*
|
||||
@PropertyInfo()
|
||||
- optional
|
||||
@@ -114,11 +178,22 @@ export default class SelfServeExample extends SelfServeBaseClass {
|
||||
@PropertyInfo(regionDropdownInfo)
|
||||
|
||||
/*
|
||||
@Values() :
|
||||
- input: NumberInputOptions | StringInputOptions | BooleanInputOptions | ChoiceInputOptions
|
||||
- role: Specifies the required options to display the property as TextBox, Number Spinner/Slider, Radio buton or Dropdown.
|
||||
@OnChange()
|
||||
- optional
|
||||
- input: (currentValues: Map<string, InputType>, newValue: InputType) => Map<string, InputType>
|
||||
- role: Takes a Map of current values and the newValue for this property as inputs. This is called when a property,
|
||||
say prop1, changes its value in the UI. This can be used to
|
||||
a) Change the value (and reflect it in the UI) for prop2 based on prop1.
|
||||
b) Change the visibility for prop2 in the UI, based on prop1
|
||||
|
||||
The new Map of propertyName -> value is returned.
|
||||
|
||||
In this example, the onRegionsChange function sets the enableLogging property to false (and disables
|
||||
the corresponsing toggle UI) when "regions" is set to "North Central US", and enables the toggle for
|
||||
any other value of "regions"
|
||||
*/
|
||||
@Values({ label: "Regions", choices: regionDropdownItems })
|
||||
@OnChange(onRegionsChange)
|
||||
@Values({ label: "Regions", choices: regionDropdownItems, placeholder: "Select a region" })
|
||||
regions: ChoiceItem;
|
||||
|
||||
@Values({
|
||||
@@ -134,34 +209,33 @@ export default class SelfServeExample extends SelfServeBaseClass {
|
||||
})
|
||||
accountName: string;
|
||||
|
||||
/*
|
||||
@OnChange()
|
||||
- optional
|
||||
- input: (currentValues: Map<string, InputType>, newValue: InputType) => Map<string, InputType>
|
||||
- role: Takes a Map of current values and the newValue for this property as inputs. This is called when a property
|
||||
changes its value in the UI. This can be used to change other input values based on some other input.
|
||||
|
||||
The new Map of propertyName -> value is returned.
|
||||
|
||||
In this example, the onDbThroughputChange function sets the collectionThroughput to the same value as the dbThroughput
|
||||
when the slider in moved in the UI.
|
||||
*/
|
||||
@OnChange(onDbThroughputChange)
|
||||
@Values({
|
||||
label: "Database Throughput",
|
||||
min: 400,
|
||||
max: initializeMaxThroughput,
|
||||
step: 100,
|
||||
uiType: UiType.Slider,
|
||||
})
|
||||
dbThroughput: number;
|
||||
|
||||
@Values({
|
||||
label: "Collection Throughput",
|
||||
min: 400,
|
||||
max: initializeMaxThroughput,
|
||||
max: getMaxThroughput,
|
||||
step: 100,
|
||||
uiType: UiType.Spinner,
|
||||
uiType: NumberUiType.Spinner,
|
||||
})
|
||||
collectionThroughput: number;
|
||||
|
||||
/*
|
||||
In this example, the onEnableDbLevelThroughputChange function makes the dbThroughput property visible when
|
||||
enableDbLevelThroughput, a boolean, is set to true and hides dbThroughput property when it is set to false.
|
||||
*/
|
||||
@OnChange(onEnableDbLevelThroughputChange)
|
||||
@Values({
|
||||
label: "Enable DB level throughput",
|
||||
trueLabel: "Enable",
|
||||
falseLabel: "Disable",
|
||||
})
|
||||
enableDbLevelThroughput: boolean;
|
||||
|
||||
@Values({
|
||||
label: "Database Throughput",
|
||||
min: 400,
|
||||
max: getMaxThroughput,
|
||||
step: 100,
|
||||
uiType: NumberUiType.Slider,
|
||||
})
|
||||
dbThroughput: number;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user