Skip to content

Commit bbcb344

Browse files
authored
Adds support for creating and displaying the transform continuous mode (opensearch-project#153)
* Adds continuous transform mode display and create Signed-off-by: Robert Downs <downsrob@amazon.com> * Adds release note Signed-off-by: Clay Downs <89109232+downsrob@users.noreply.github.com>
1 parent 2de6e3b commit bbcb344

File tree

11 files changed

+187
-9
lines changed

11 files changed

+187
-9
lines changed

cypress/integration/transforms_spec.js

+83
Original file line numberDiff line numberDiff line change
@@ -241,4 +241,87 @@ describe("Transforms", () => {
241241
cy.contains(`"${TRANSFORM_ID}" is enabled`);
242242
});
243243
});
244+
245+
describe("can be created in continuous mode", () => {
246+
it("successfully", () => {
247+
// Confirm we loaded empty state
248+
cy.contains("Transform jobs help you create a materialized view on top of existing data.");
249+
250+
// Route to create transform page
251+
cy.contains("Create transform").click({ force: true });
252+
253+
// Type in transform ID
254+
cy.get(`input[placeholder="my-transformjob1"]`).type(TRANSFORM_ID, { force: true });
255+
256+
// Get description input box
257+
cy.get(`textarea[data-test-subj="description"]`).focus().type("some description");
258+
259+
// Enter source index
260+
cy.get(`div[data-test-subj="sourceIndexCombobox"]`)
261+
.find(`input[data-test-subj="comboBoxSearchInput"]`)
262+
.focus()
263+
.type("opensearch_dashboards_sample_data_ecommerce{enter}");
264+
265+
// Enter target index
266+
cy.get(`div[data-test-subj="targetIndexCombobox"]`)
267+
.find(`input[data-test-subj="comboBoxSearchInput"]`)
268+
.focus()
269+
.type("test_transform{enter}");
270+
271+
// Click the next button
272+
cy.get("button").contains("Next").click({ force: true });
273+
274+
// Confirm that we got to step 2 of creation page
275+
cy.contains("Select fields to transform");
276+
277+
cy.get(`button[data-test-subj="category.keywordOptionsPopover"]`).click({ force: true });
278+
279+
cy.contains("Group by terms").click({ force: true });
280+
281+
// Confirm group was added
282+
cy.contains("category.keyword_terms");
283+
284+
// Add aggregable field
285+
cy.contains("50 columns hidden").click({ force: true });
286+
cy.contains("taxless_total_price").click({ force: true });
287+
// Click out of the window
288+
cy.contains("Select fields to transform").click({ force: true });
289+
290+
cy.get(`button[data-test-subj="taxless_total_priceOptionsPopover"]`).click({ force: true });
291+
292+
cy.contains("Aggregate by avg").click({ force: true });
293+
294+
// Confirm agg was added
295+
cy.contains("avg_taxless_total_price");
296+
297+
// Click the next button
298+
cy.get("button").contains("Next").click({ force: true });
299+
300+
// Confirm that we got to step 3 of creation page
301+
cy.contains("Job enabled by default");
302+
303+
// Make the transform continuous
304+
cy.get("[id=yes]").click({ force: true });
305+
306+
// Click the next button
307+
cy.get("button").contains("Next").click({ force: true });
308+
309+
// Confirm that we got to step 4 of creation page
310+
cy.contains("Review and create");
311+
312+
// Confirm that the transform is continuous
313+
cy.contains("Continuous, every");
314+
315+
// Click the create button
316+
cy.get("button").contains("Create").click({ force: true });
317+
318+
// Verify that sample data is add by checking toast notification
319+
cy.contains(`Transform job "${TRANSFORM_ID}" successfully created.`);
320+
cy.location("hash").should("contain", "transforms");
321+
cy.get(`button[data-test-subj="transformLink_${TRANSFORM_ID}"]`);
322+
323+
// The continuous column should say 'Yes'
324+
cy.contains("Yes");
325+
});
326+
});
244327
});

public/pages/CreateTransform/components/ReviewSchedule/ReviewSchedule.tsx

+4-2
Original file line numberDiff line numberDiff line change
@@ -7,9 +7,11 @@ import React, { Component } from "react";
77
import { EuiFlexGrid, EuiFlexItem, EuiText } from "@elastic/eui";
88
import { ContentPanel, ContentPanelActions } from "../../../../components/ContentPanel";
99
import { ModalConsumer } from "../../../../components/Modal";
10+
import { buildIntervalScheduleText } from "../../../CreateRollup/utils/helpers";
1011

1112
interface ReviewScheduleProps {
1213
jobEnabledByDefault: boolean;
14+
continuousJob: string;
1315
interval: number;
1416
intervalTimeunit: string;
1517
pageSize: number;
@@ -22,11 +24,11 @@ export default class ReviewSchedule extends Component<ReviewScheduleProps> {
2224
}
2325

2426
render() {
25-
const { jobEnabledByDefault, interval, intervalTimeunit, pageSize, onChangeStep } = this.props;
27+
const { jobEnabledByDefault, continuousJob, interval, intervalTimeunit, pageSize, onChangeStep } = this.props;
2628

2729
const enabled = jobEnabledByDefault ? "Yes" : "No";
2830

29-
const schedule = "Every " + interval + " " + intervalTimeunit.toLowerCase();
31+
const schedule = buildIntervalScheduleText(continuousJob === "yes", interval, intervalTimeunit);
3032

3133
return (
3234
<ContentPanel

public/pages/CreateTransform/components/Schedule/Schedule.tsx

+26-2
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44
*/
55

66
import React, { ChangeEvent, Component } from "react";
7-
import { EuiSpacer, EuiCheckbox, EuiFormRow, EuiFieldNumber, EuiAccordion, EuiHorizontalRule } from "@elastic/eui";
7+
import { EuiSpacer, EuiCheckbox, EuiRadioGroup, EuiFormRow, EuiFieldNumber, EuiAccordion, EuiHorizontalRule } from "@elastic/eui";
88
import { ContentPanel } from "../../../../components/ContentPanel";
99
import { selectInterval } from "../../../Transforms/utils/metadataHelper";
1010

@@ -13,16 +13,38 @@ interface ScheduleProps {
1313
transformId: string;
1414
transformIdError: string;
1515
jobEnabledByDefault: boolean;
16+
continuousJob: string;
1617
pageSize: number;
1718
onChangeJobEnabledByDefault: () => void;
1819
interval: number;
1920
intervalTimeunit: string;
2021
intervalError: string;
22+
onChangeContinuousJob: (optionId: string) => void;
2123
onChangeIntervalTime: (e: ChangeEvent<HTMLInputElement>) => void;
2224
onChangeIntervalTimeunit: (e: ChangeEvent<HTMLSelectElement>) => void;
2325
onChangePage: (e: ChangeEvent<HTMLInputElement>) => void;
2426
}
2527

28+
const radios = [
29+
{
30+
id: "no",
31+
label: "No",
32+
},
33+
{
34+
id: "yes",
35+
label: "Yes",
36+
},
37+
];
38+
39+
const isContinuous = (continuousJob: string, onChangeContinuousJob: (optionId: string) => void) => (
40+
<React.Fragment>
41+
<EuiFormRow label="Continuous">
42+
<EuiRadioGroup options={radios} idSelected={continuousJob} onChange={(id) => onChangeContinuousJob(id)} name="continuousJob" />
43+
</EuiFormRow>
44+
<EuiSpacer size="m" />
45+
</React.Fragment>
46+
);
47+
2648
export default class Schedule extends Component<ScheduleProps> {
2749
constructor(props: ScheduleProps) {
2850
super(props);
@@ -32,11 +54,13 @@ export default class Schedule extends Component<ScheduleProps> {
3254
const {
3355
isEdit,
3456
jobEnabledByDefault,
57+
continuousJob,
3558
interval,
3659
intervalTimeunit,
3760
intervalError,
3861
pageSize,
3962
onChangeJobEnabledByDefault,
63+
onChangeContinuousJob,
4064
onChangeIntervalTime,
4165
onChangeIntervalTimeunit,
4266
onChangePage,
@@ -55,7 +79,7 @@ export default class Schedule extends Component<ScheduleProps> {
5579
)}
5680
<EuiSpacer size="m" />
5781

58-
{!isEdit}
82+
{!isEdit && isContinuous(continuousJob, onChangeContinuousJob)}
5983

6084
{/* TODO: Replace with switch block when define by cron expressions is supported. */}
6185
{selectInterval(interval, intervalTimeunit, intervalError, onChangeIntervalTime, onChangeIntervalTimeunit)}

public/pages/CreateTransform/containers/CreateTransformForm/CreateTransformForm.tsx

+12
Original file line numberDiff line numberDiff line change
@@ -68,6 +68,7 @@ interface CreateTransformFormState {
6868
selectedFields: FieldItem[];
6969
jobEnabledByDefault: boolean;
7070

71+
continuousJob: string;
7172
interval: number;
7273
intervalError: string;
7374
intervalTimeunit: string;
@@ -118,6 +119,7 @@ export default class CreateTransformForm extends Component<CreateTransformFormPr
118119
intervalError: "",
119120

120121
jobEnabledByDefault: true,
122+
continuousJob: "no",
121123
interval: 1,
122124
intervalTimeunit: "MINUTES",
123125
pageSize: 1000,
@@ -394,6 +396,12 @@ export default class CreateTransformForm extends Component<CreateTransformFormPr
394396
this.setState({ jobEnabledByDefault: !checked, transformJSON: newJSON });
395397
};
396398

399+
onChangeContinuousJob = (optionId: string): void => {
400+
let newJSON = this.state.transformJSON;
401+
newJSON.transform.continuous = optionId == "yes";
402+
this.setState({ continuousJob: optionId, transformJSON: newJSON });
403+
};
404+
397405
onChangeIntervalTime = (e: ChangeEvent<HTMLInputElement>): void => {
398406
this.setState({ interval: e.target.valueAsNumber });
399407
if (e.target.value == "") {
@@ -526,6 +534,7 @@ export default class CreateTransformForm extends Component<CreateTransformFormPr
526534
selectedAggregations,
527535
aggList,
528536
jobEnabledByDefault,
537+
continuousJob,
529538
interval,
530539
intervalTimeunit,
531540
intervalError,
@@ -580,11 +589,13 @@ export default class CreateTransformForm extends Component<CreateTransformFormPr
580589
{...this.props}
581590
currentStep={this.state.currentStep}
582591
jobEnabledByDefault={jobEnabledByDefault}
592+
continuousJob={continuousJob}
583593
interval={interval}
584594
intervalTimeunit={intervalTimeunit}
585595
intervalError={intervalError}
586596
pageSize={pageSize}
587597
onChangeJobEnabledByDefault={this.onChangeJobEnabledByDefault}
598+
onChangeContinuousJob={this.onChangeContinuousJob}
588599
onChangeIntervalTime={this.onChangeIntervalTime}
589600
onChangePage={this.onChangePage}
590601
onChangeIntervalTimeunit={this.onChangeIntervalTimeunit}
@@ -605,6 +616,7 @@ export default class CreateTransformForm extends Component<CreateTransformFormPr
605616
onRemoveTransformation={this.onRemoveTransformation}
606617
previewTransform={previewTransform}
607618
jobEnabledByDefault={jobEnabledByDefault}
619+
continuousJob={continuousJob}
608620
interval={interval}
609621
intervalTimeunit={intervalTimeunit}
610622
pageSize={pageSize}

public/pages/CreateTransform/containers/ReviewAndCreateStep/ReviewAndCreateStep.tsx

+1
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@ interface ReviewAndCreateStepProps extends RouteComponentProps {
2727
sourceIndexFilter: string;
2828

2929
jobEnabledByDefault: boolean;
30+
continuousJob: string;
3031
pageSize: number;
3132
fields: FieldItem[];
3233
selectedGroupField: TransformGroupItem[];

public/pages/CreateTransform/containers/SpecifyScheduleStep/SpecifyScheduleStep.tsx

+6
Original file line numberDiff line numberDiff line change
@@ -19,11 +19,13 @@ interface SpecifyScheduleStepProps extends RouteComponentProps {
1919
transformService: TransformService;
2020
currentStep: number;
2121
jobEnabledByDefault: boolean;
22+
continuousJob: string;
2223
interval: number;
2324
intervalTimeunit: string;
2425
intervalError: string;
2526
pageSize: number;
2627
onChangeJobEnabledByDefault: () => void;
28+
onChangeContinuousJob: (optionId: string) => void;
2729
onChangeIntervalTime: (e: ChangeEvent<HTMLInputElement>) => void;
2830
onChangePage: (e: ChangeEvent<HTMLInputElement>) => void;
2931
onChangeIntervalTimeunit: (e: ChangeEvent<HTMLSelectElement>) => void;
@@ -109,10 +111,12 @@ export default class SpecifyScheduleStep extends Component<SpecifyScheduleStepPr
109111
if (this.props.currentStep != 3) return null;
110112
const {
111113
jobEnabledByDefault,
114+
continuousJob,
112115
interval,
113116
intervalTimeunit,
114117
pageSize,
115118
onChangeJobEnabledByDefault,
119+
onChangeContinuousJob,
116120
onChangeIntervalTime,
117121
onChangePage,
118122
onChangeIntervalTimeunit,
@@ -135,10 +139,12 @@ export default class SpecifyScheduleStep extends Component<SpecifyScheduleStepPr
135139
transformId={transformId}
136140
transformIdError={transformIdError}
137141
jobEnabledByDefault={jobEnabledByDefault}
142+
continuousJob={continuousJob}
138143
interval={interval}
139144
intervalTimeunit={intervalTimeunit}
140145
pageSize={pageSize}
141146
onChangeJobEnabledByDefault={onChangeJobEnabledByDefault}
147+
onChangeContinuousJob={onChangeContinuousJob}
142148
onChangeIntervalTime={onChangeIntervalTime}
143149
onChangeIntervalTimeunit={onChangeIntervalTimeunit}
144150
onChangePage={onChangePage}

public/pages/Transforms/containers/Transforms/TransformDetails.tsx

+7-1
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,7 @@ import TransformStatus from "../../components/TransformStatus";
3535
import { EMPTY_TRANSFORM } from "../../utils/constants";
3636
import TransformSettings from "./TransformSettings";
3737
import GeneralInformation from "../../components/GeneralInformation";
38+
import { buildIntervalScheduleText } from "../../../CreateRollup/utils/helpers";
3839

3940
interface TransformDetailsProps extends RouteComponentProps {
4041
transformService: TransformService;
@@ -48,6 +49,7 @@ interface TransformDetailsState {
4849
updatedAt: number;
4950
pageSize: number;
5051
transformJson: any;
52+
continuousJob: string;
5153
sourceIndex: string;
5254
targetIndex: string;
5355
sourceIndexFilter: string;
@@ -75,6 +77,7 @@ export default class TransformDetails extends Component<TransformDetailsProps, T
7577
updatedAt: 1,
7678
pageSize: 1000,
7779
transformJson: EMPTY_TRANSFORM,
80+
continuousJob: "no",
7881
sourceIndex: "",
7982
targetIndex: "",
8083
sourceIndexFilter: "",
@@ -214,7 +217,10 @@ export default class TransformDetails extends Component<TransformDetailsProps, T
214217
isPopOverOpen,
215218
} = this.state;
216219

217-
let scheduleText = "Every " + interval + " " + intervalTimeUnit.toLowerCase();
220+
let scheduleText = "";
221+
if (transformJson.transform != null) {
222+
scheduleText = buildIntervalScheduleText(transformJson.transform.continuous, interval, intervalTimeUnit);
223+
}
218224
const actionButton = (
219225
<EuiButton iconType="arrowDown" iconSide="right" disabled={false} onClick={this.onActionButtonClick} data-test-subj="actionButton">
220226
Actions

public/pages/Transforms/containers/Transforms/Transforms.tsx

+9
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,7 @@ import { renderEnabled, renderStatus } from "../../utils/metadataHelper";
4343
import { DEFAULT_PAGE_SIZE_OPTIONS, DEFAULT_QUERY_PARAMS } from "../../../Indices/utils/constants";
4444
import _ from "lodash";
4545
import { ManagedCatIndex } from "../../../../../server/models/interfaces";
46+
import { renderContinuous } from "../../../Rollups/utils/helpers";
4647

4748
interface TransformProps extends RouteComponentProps {
4849
transformService: TransformService;
@@ -161,6 +162,14 @@ export default class Transforms extends Component<TransformProps, TransformState
161162
truncateText: true,
162163
render: renderEnabled,
163164
},
165+
{
166+
field: "transform.continuous",
167+
name: "Continuous",
168+
sortable: true,
169+
textOnly: true,
170+
truncateText: true,
171+
render: renderContinuous,
172+
},
164173
{
165174
field: "transform.updated_at",
166175
name: "Last updated time",

0 commit comments

Comments
 (0)