Skip to content

Commit c5b2164

Browse files
author
Seung Yeon Joo
committed
Add Convert index to remote support for dashboard ism plugin
Signed-off-by: Seung Yeon Joo <seung.yeon.joo@oracle.com>
1 parent 66354e6 commit c5b2164

9 files changed

Lines changed: 461 additions & 2 deletions

File tree

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
{
2+
"policy": {
3+
"description": "Convert old indexes to searchable snapshots",
4+
"default_state": "active",
5+
"states": [
6+
{
7+
"name": "active",
8+
"actions": [],
9+
"transitions": [
10+
{
11+
"state_name": "archive",
12+
"conditions": {
13+
"min_index_age": "30d"
14+
}
15+
}
16+
]
17+
},
18+
{
19+
"name": "archive",
20+
"actions": [
21+
{
22+
"snapshot": {
23+
"repository": "remote-repo",
24+
"snapshot": "{{ctx.index}}"
25+
}
26+
},
27+
{
28+
"convert_index_to_remote": {
29+
"repository": "remote-repo",
30+
"snapshot": "{{ctx.index}}",
31+
"include_aliases": true,
32+
"ignore_index_settings": "index.refresh_interval,index.number_of_replicas",
33+
"number_of_replicas": 0
34+
}
35+
}
36+
],
37+
"transitions": []
38+
}
39+
]
40+
}
41+
}
42+

models/interfaces.ts

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -428,6 +428,16 @@ export interface SnapshotAction extends Action {
428428
};
429429
}
430430

431+
export interface ConvertIndexToRemoteAction extends Action {
432+
convert_index_to_remote: {
433+
repository: string;
434+
snapshot: string;
435+
include_aliases?: boolean;
436+
ignore_index_settings?: string;
437+
number_of_replicas?: number;
438+
};
439+
}
440+
431441
export interface IndexPriorityAction extends Action {
432442
index_priority: {
433443
priority?: number;
Lines changed: 186 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,186 @@
1+
/*
2+
* Copyright OpenSearch Contributors
3+
* SPDX-License-Identifier: Apache-2.0
4+
*/
5+
6+
import React from "react";
7+
import "@testing-library/jest-dom";
8+
import { render, screen, cleanup, fireEvent } from "@testing-library/react";
9+
import userEventModule from "@testing-library/user-event";
10+
import { DEFAULT_CONVERT_INDEX_TO_REMOTE } from "../../utils/constants";
11+
import { ConvertIndexToRemoteAction, UIAction } from "../../../../../models/interfaces";
12+
import { actionRepoSingleton } from "../../utils/helpers";
13+
14+
const TEST_PROPS: UIAction<ConvertIndexToRemoteAction> = {
15+
action: DEFAULT_CONVERT_INDEX_TO_REMOTE,
16+
} as UIAction<ConvertIndexToRemoteAction>;
17+
18+
const renderComponent = (uiAction: UIAction<ConvertIndexToRemoteAction> = TEST_PROPS) => {
19+
render(actionRepoSingleton.getUIAction("convert_index_to_remote").render(uiAction, mockOnChangeAction));
20+
};
21+
const mockOnChangeAction = (uiAction: UIAction<ConvertIndexToRemoteAction> = TEST_PROPS) => {
22+
cleanup();
23+
renderComponent(uiAction);
24+
};
25+
26+
afterEach(() => cleanup());
27+
28+
describe("ConvertIndexToRemoteUIAction component", () => {
29+
const userEvent = userEventModule.setup();
30+
31+
it("renders with default values", () => {
32+
const { container } = render(actionRepoSingleton.getUIAction("convert_index_to_remote").render(TEST_PROPS, mockOnChangeAction));
33+
34+
// Check that repository field exists with default value
35+
const repositoryInput = screen.getByTestId("action-render-convert-index-to-remote-repository");
36+
expect(repositoryInput).toBeInTheDocument();
37+
expect(repositoryInput).toHaveValue("example-repository");
38+
39+
// Check that snapshot field exists with default value
40+
const snapshotInput = screen.getByTestId("action-render-convert-index-to-remote-snapshot");
41+
expect(snapshotInput).toBeInTheDocument();
42+
expect(snapshotInput).toHaveValue("example-snapshot");
43+
44+
// Check that include_aliases switch exists
45+
const includeAliasesSwitch = screen.getByTestId("action-render-convert-index-to-remote-include-aliases");
46+
expect(includeAliasesSwitch).toBeInTheDocument();
47+
expect(includeAliasesSwitch).not.toBeChecked();
48+
49+
// Check that ignore_index_settings field exists
50+
const ignoreIndexSettingsInput = screen.getByTestId("action-render-convert-index-to-remote-ignore-index-settings");
51+
expect(ignoreIndexSettingsInput).toBeInTheDocument();
52+
expect(ignoreIndexSettingsInput).toHaveValue("");
53+
54+
// Check that number_of_replicas field exists
55+
const numberOfReplicasInput = screen.getByTestId("action-render-convert-index-to-remote-number-of-replicas");
56+
expect(numberOfReplicasInput).toBeInTheDocument();
57+
expect(numberOfReplicasInput).toHaveValue(0);
58+
59+
expect(container).toMatchSnapshot();
60+
});
61+
62+
it("updates repository value on change", async () => {
63+
renderComponent();
64+
65+
const repositoryInput = screen.getByTestId("action-render-convert-index-to-remote-repository");
66+
await userEvent.clear(repositoryInput);
67+
await userEvent.type(repositoryInput, "my-remote-repo");
68+
69+
expect(repositoryInput).toHaveValue("my-remote-repo");
70+
});
71+
72+
it("updates snapshot value on change", async () => {
73+
renderComponent();
74+
75+
const snapshotInput = screen.getByTestId("action-render-convert-index-to-remote-snapshot");
76+
await userEvent.clear(snapshotInput);
77+
await userEvent.type(snapshotInput, "{{ctx.index}}");
78+
79+
expect(snapshotInput).toHaveValue("{{ctx.index}}");
80+
});
81+
82+
it("toggles include_aliases switch", async () => {
83+
renderComponent();
84+
85+
const includeAliasesSwitch = screen.getByTestId("action-render-convert-index-to-remote-include-aliases");
86+
expect(includeAliasesSwitch).not.toBeChecked();
87+
88+
await userEvent.click(includeAliasesSwitch);
89+
expect(includeAliasesSwitch).toBeChecked();
90+
});
91+
92+
it("updates ignore_index_settings value on change", async () => {
93+
renderComponent();
94+
95+
const ignoreIndexSettingsInput = screen.getByTestId("action-render-convert-index-to-remote-ignore-index-settings");
96+
await userEvent.type(ignoreIndexSettingsInput, "index.refresh_interval,index.number_of_replicas");
97+
98+
expect(ignoreIndexSettingsInput).toHaveValue("index.refresh_interval,index.number_of_replicas");
99+
});
100+
101+
it("updates number_of_replicas value on change", async () => {
102+
renderComponent();
103+
104+
const numberOfReplicasInput = screen.getByTestId("action-render-convert-index-to-remote-number-of-replicas");
105+
await userEvent.clear(numberOfReplicasInput);
106+
await userEvent.type(numberOfReplicasInput, "2");
107+
108+
expect(numberOfReplicasInput).toHaveValue(2);
109+
});
110+
111+
it("validates required fields", () => {
112+
const uiAction = actionRepoSingleton.getUIAction("convert_index_to_remote");
113+
114+
// Valid action
115+
expect(uiAction.isValid()).toBe(true);
116+
117+
// Invalid action - missing repository
118+
const invalidAction1 = {
119+
...TEST_PROPS,
120+
action: {
121+
convert_index_to_remote: {
122+
...DEFAULT_CONVERT_INDEX_TO_REMOTE.convert_index_to_remote,
123+
repository: "",
124+
},
125+
},
126+
};
127+
const invalidUIAction1 = actionRepoSingleton.getUIActionFromData(invalidAction1.action);
128+
expect(invalidUIAction1.isValid()).toBe(false);
129+
130+
// Invalid action - missing snapshot
131+
const invalidAction2 = {
132+
...TEST_PROPS,
133+
action: {
134+
convert_index_to_remote: {
135+
...DEFAULT_CONVERT_INDEX_TO_REMOTE.convert_index_to_remote,
136+
snapshot: "",
137+
},
138+
},
139+
};
140+
const invalidUIAction2 = actionRepoSingleton.getUIActionFromData(invalidAction2.action);
141+
expect(invalidUIAction2.isValid()).toBe(false);
142+
});
143+
144+
it("renders with custom values", () => {
145+
const customAction: ConvertIndexToRemoteAction = {
146+
convert_index_to_remote: {
147+
repository: "s3-repo",
148+
snapshot: "{{ctx.index}}-snapshot",
149+
include_aliases: true,
150+
ignore_index_settings: "index.refresh_interval",
151+
number_of_replicas: 2,
152+
},
153+
};
154+
155+
const customProps = { ...TEST_PROPS, action: customAction };
156+
const { container } = render(
157+
actionRepoSingleton.getUIAction("convert_index_to_remote").render(customProps, mockOnChangeAction)
158+
);
159+
160+
expect(screen.getByTestId("action-render-convert-index-to-remote-repository")).toHaveValue("s3-repo");
161+
expect(screen.getByTestId("action-render-convert-index-to-remote-snapshot")).toHaveValue("{{ctx.index}}-snapshot");
162+
expect(screen.getByTestId("action-render-convert-index-to-remote-include-aliases")).toBeChecked();
163+
expect(screen.getByTestId("action-render-convert-index-to-remote-ignore-index-settings")).toHaveValue("index.refresh_interval");
164+
expect(screen.getByTestId("action-render-convert-index-to-remote-number-of-replicas")).toHaveValue(2);
165+
166+
expect(container).toMatchSnapshot();
167+
});
168+
169+
it("returns correct content", () => {
170+
const uiAction = actionRepoSingleton.getUIAction("convert_index_to_remote");
171+
expect(uiAction.content()).toBe("Convert index to remote");
172+
});
173+
174+
it("converts to action correctly", () => {
175+
const uiAction = actionRepoSingleton.getUIAction("convert_index_to_remote");
176+
const action = uiAction.toAction();
177+
178+
expect(action).toHaveProperty("convert_index_to_remote");
179+
expect(action.convert_index_to_remote).toHaveProperty("repository");
180+
expect(action.convert_index_to_remote).toHaveProperty("snapshot");
181+
expect(action.convert_index_to_remote).toHaveProperty("include_aliases");
182+
expect(action.convert_index_to_remote).toHaveProperty("ignore_index_settings");
183+
expect(action.convert_index_to_remote).toHaveProperty("number_of_replicas");
184+
});
185+
});
186+

0 commit comments

Comments
 (0)