Skip to content

Commit

Permalink
Register action hook (#64)
Browse files Browse the repository at this point in the history
* register action hook

Signed-off-by: bowenlan-amzn <[email protected]>
  • Loading branch information
bowenlan-amzn authored Aug 17, 2021
1 parent 0e173f4 commit c1b76de
Show file tree
Hide file tree
Showing 9 changed files with 121 additions and 18 deletions.
13 changes: 11 additions & 2 deletions .github/workflows/cypress-workflow.yml
Original file line number Diff line number Diff line change
Expand Up @@ -33,16 +33,25 @@ jobs:
- name: Build OpenSearch
working-directory: ./OpenSearch
run: ./gradlew publishToMavenLocal -Dbuild.snapshot=false
- name: Checkout OpenSearch2
uses: actions/checkout@v2
with:
repository: 'opensearch-project/OpenSearch'
path: OpenSearch2
ref: '1.x'
- name: Build OpenSearch2
working-directory: ./OpenSearch2
run: ./gradlew publishToMavenLocal -Dbuild.snapshot=false
# dependencies: common-utils
- name: Checkout common-utils
uses: actions/checkout@v2
with:
repository: 'opensearch-project/common-utils'
path: common-utils
ref: '1.0'
ref: 'main'
- name: Build common-utils
working-directory: ./common-utils
run: ./gradlew publishToMavenLocal -Dopensearch.version=1.0.0
run: ./gradlew publishToMavenLocal -Dopensearch.version=1.1.0 -Dbuild.snapshot=false
# dependencies: job-scheduler
- name: Checkout job-scheduler
uses: actions/checkout@v2
Expand Down
2 changes: 1 addition & 1 deletion models/interfaces.ts
Original file line number Diff line number Diff line change
Expand Up @@ -153,7 +153,7 @@ export interface Retry {
export interface UIAction<Data> {
action: Data;
id: string;
type: ActionType;
type: ActionType | string;
render: (uiAction: UIAction<Data>, onChangeAction: (uiAction: UIAction<Data>) => void) => JSX.Element | null;
clone: (action: Data) => UIAction<Data>;
content: () => JSX.Element | string | null;
Expand Down
8 changes: 7 additions & 1 deletion public/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -26,8 +26,14 @@

import { PluginInitializerContext } from "opensearch-dashboards/public";
import { IndexManagementPlugin } from "./plugin";
import { Action, UIAction } from "../models/interfaces";

export interface IndexManagementPluginSetup {}
// export for other plugins to register action
export { Action, UIAction } from "../models/interfaces";

export interface IndexManagementPluginSetup {
registerAction: (actionType: string, uiActionCtor: new (action: Action) => UIAction<any>, defaultAction: Action) => void;
}
export interface IndexManagementPluginStart {}

export function plugin(initializerContext: PluginInitializerContext) {
Expand Down
4 changes: 2 additions & 2 deletions public/pages/VisualCreatePolicy/components/States/State.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ import ConfirmationModal from "../../../../components/ConfirmationModal";
import Badge from "../Badge";
import TransitionContent from "../Transition/TransitionContent";
import { makeId } from "../../../../utils/helpers";
import { getUIActionFromData } from "../../utils/helpers";
import { actionRepoSingleton } from "../../utils/helpers";

interface StateProps {
state: StateData;
Expand Down Expand Up @@ -112,7 +112,7 @@ const State = ({ state, isInitialState, idx, onClickEditState, onClickDeleteStat
<EuiFlexGroup>
{state.actions.map((action) => (
<EuiFlexItem grow={false} key={makeId()}>
<EuiPanel>{getUIActionFromData(action).content()}</EuiPanel>
<EuiPanel>{actionRepoSingleton.getUIActionFromData(action).content()}</EuiPanel>
</EuiFlexItem>
))}
</EuiFlexGroup>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ import { EuiFlyoutBody, EuiFlyoutFooter, EuiTitle, EuiFormRow, EuiSelect, EuiSpa
import { UIAction, Action } from "../../../../../models/interfaces";
import { actions } from "../../utils/constants";
import TimeoutRetrySettings from "../../components/TimeoutRetrySettings";
import { capitalizeFirstLetter, getUIAction } from "../../utils/helpers";
import { actionRepoSingleton, capitalizeFirstLetter } from "../../utils/helpers";
import FlyoutFooter from "../../components/FlyoutFooter";
import EuiFormCustomLabel from "../../components/EuiFormCustomLabel";

Expand Down Expand Up @@ -43,7 +43,7 @@ export default class CreateAction extends Component<CreateActionProps, CreateAct
// This should be ephemeral and not change the actual list of actions yet as they might click cancel so we put it separate
onChangeSelectedAction = (event: ChangeEvent<HTMLSelectElement>) => {
const selectedAction = event.target.value;
const uiAction = getUIAction(selectedAction);
const uiAction = actionRepoSingleton.getUIAction(selectedAction);
this.setState({ action: uiAction });
};

Expand All @@ -61,11 +61,7 @@ export default class CreateAction extends Component<CreateActionProps, CreateAct
const { action } = this.state;
const { editAction } = this.props;

const actionOptions = actions.map((action) => {
const key =
Object.keys(action)
.filter((k) => k !== "timeout" && k !== "retry")
.pop() || "";
const actionOptions = actionRepoSingleton.getAllActionTypes().map((key) => {
return {
value: key,
text: key
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -35,8 +35,8 @@ import CreateTransition from "../CreateTransition";
import CreateAction from "../CreateAction";
import Actions from "./Actions";
import Transitions from "./Transitions";
import { getOrderInfo, getUIActionFromData } from "../../utils/helpers";
import { makeId } from "../../../../utils/helpers";
import { actionRepoSingleton, getOrderInfo } from "../../utils/helpers";

interface CreateStateProps {
policy: Policy;
Expand Down Expand Up @@ -69,7 +69,7 @@ export default class CreateState extends Component<CreateStateProps, CreateState
editAction: null,
createTransition: false,
editTransition: null,
actions: props.state?.actions?.map((action) => getUIActionFromData(action)) || [],
actions: props.state?.actions?.map((action) => actionRepoSingleton.getUIActionFromData(action)) || [],
transitions: props.state?.transitions?.map((transition) => ({ id: makeId(), transition })) || [],
afterBeforeState,
order,
Expand Down
42 changes: 40 additions & 2 deletions public/pages/VisualCreatePolicy/utils/helpers.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,9 @@
* GitHub history for details.
*/

import { capitalizeFirstLetter, convertTemplatesToArray, getOrderInfo } from "./helpers";
import { ISMTemplate } from "../../../../models/interfaces";
import { actionRepoSingleton, capitalizeFirstLetter, convertTemplatesToArray, getOrderInfo } from "./helpers";
import { Action, ISMTemplate, UIAction } from "../../../../models/interfaces";
import { makeId } from "../../../utils/helpers";

test("converts all ism template formats into a list of ism templates", () => {
expect(convertTemplatesToArray(null)).toEqual([]);
Expand Down Expand Up @@ -48,3 +49,40 @@ test("getOrderInfo returns correct order info", () => {
expect(getOrderInfo(null, [state]).order).toBe("after");
expect(getOrderInfo(null, [state]).afterBeforeState).toBe(state.name);
});

interface DummyAction extends Action {
dummy: {};
}

const DEFAULT_DUMMY: DummyAction = {
dummy: {},
};

class DummyUIAction implements UIAction<DummyAction> {
id: string;
action: DummyAction;
type = "dummy";

constructor(action: DummyAction, id: string = makeId()) {
this.action = action;
this.id = id;
}

content = () => `Dummy content.`;

clone = (action: DummyAction) => new DummyUIAction(action, this.id);

render = (action: UIAction<DummyAction>, onChangeAction: (action: UIAction<DummyAction>) => void) => {
return null;
};

toAction = () => this.action;
}

test("action repository usage", () => {
expect(actionRepoSingleton.getAllActionTypes().length).toBe(13);
actionRepoSingleton.registerAction("dummy", DummyUIAction, DEFAULT_DUMMY);
expect(actionRepoSingleton.getAllActionTypes().length).toBe(14);
expect(actionRepoSingleton.getUIAction("dummy") instanceof DummyUIAction).toBe(true);
expect(actionRepoSingleton.getUIActionFromData(DEFAULT_DUMMY) instanceof DummyUIAction).toBe(true);
});
47 changes: 47 additions & 0 deletions public/pages/VisualCreatePolicy/utils/helpers.ts
Original file line number Diff line number Diff line change
Expand Up @@ -100,6 +100,53 @@ export const getUIAction = (actionType: string): UIAction<any> => {
}
};

class ActionRepository {
repository: { [actionType: string]: [new (action: Action) => UIAction<any>, Action] } = {
allocation: [AllocationUIAction, DEFAULT_ALLOCATION],
close: [CloseUIAction, DEFAULT_CLOSE],
delete: [DeleteUIAction, DEFAULT_DELETE],
force_merge: [ForceMergeUIAction, DEFAULT_FORCE_MERGE],
index_priority: [IndexPriorityUIAction, DEFAULT_INDEX_PRIORITY],
notification: [NotificationUIAction, DEFAULT_NOTIFICATION],
open: [OpenUIAction, DEFAULT_OPEN],
read_only: [ReadOnlyUIAction, DEFAULT_READ_ONLY],
read_write: [ReadWriteUIAction, DEFAULT_READ_WRITE],
replica_count: [ReplicaCountUIAction, DEFAULT_REPLICA_COUNT],
rollover: [RolloverUIAction, DEFAULT_ROLLOVER],
rollup: [RollupUIAction, DEFAULT_ROLLUP],
snapshot: [SnapshotUIAction, DEFAULT_SNAPSHOT],
};

getAllActionTypes = () => {
return Object.keys(this.repository);
};

registerAction = (actionType: string, uiActionCtor: new (action: Action) => UIAction<any>, defaultAction: Action) => {
if (this.repository.hasOwnProperty(actionType)) {
throw new Error(`Cannot register an action twice in the repository [type=${actionType}]`);
}

this.repository[actionType] = [uiActionCtor, defaultAction];
};

getUIActionFromData = (action: Action) => {
const actionType = Object.keys(action)
.filter((key) => key !== "timeout" && key !== "retry")
.pop();
if (!actionType) throw new Error(`Failed to get action using type [${actionType}]`);
const uiAction = this.getUIAction(actionType);
return uiAction.clone(action);
};

getUIAction = (actionType: string): UIAction<any> => {
const uiAction = this.repository[actionType];
if (!uiAction) throw new Error(`Action type [${actionType}] not supported`);
return new uiAction[0](uiAction[1]);
};
}

export const actionRepoSingleton = new ActionRepository();

// Takes in the ismTemplates which could be a single object, array, or not defined and returns them reformatted as a list
export const convertTemplatesToArray = (ismTemplates: ISMTemplate[] | ISMTemplate | null | undefined): ISMTemplate[] => {
const templates = [];
Expand Down
9 changes: 8 additions & 1 deletion public/plugin.ts
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@
import { AppMountParameters, CoreSetup, CoreStart, Plugin, PluginInitializerContext } from "../../../src/core/public";
import { IndexManagementPluginSetup } from ".";
import { IndexManagementPluginStart } from ".";
import { actionRepoSingleton } from "./pages/VisualCreatePolicy/utils/helpers";

export class IndexManagementPlugin implements Plugin<IndexManagementPluginSetup, IndexManagementPluginStart> {
constructor(private readonly initializerContext: PluginInitializerContext) {
Expand All @@ -49,10 +50,16 @@ export class IndexManagementPlugin implements Plugin<IndexManagementPluginSetup,
return renderApp(coreStart, params);
},
});
return {};
return {
registerAction: (actionType, uiActionCtor, defaultAction) => {
actionRepoSingleton.registerAction(actionType, uiActionCtor, defaultAction);
},
};
}

public start(core: CoreStart): IndexManagementPluginStart {
Object.freeze(actionRepoSingleton.repository);
// After this point, calling registerAction will throw error because "Object is not extensible"
return {};
}
}

0 comments on commit c1b76de

Please sign in to comment.