From e9494689027c96c08fe8990d945cf7f785a4696f Mon Sep 17 00:00:00 2001 From: jayesh hathila Date: Tue, 13 Apr 2021 21:09:07 +0530 Subject: [PATCH 01/93] Reduce duplicate code in Rollup Pages (#167) Move duplicate code to base class for Edit and Create Rollup pages. --- .../BaseAggregationAndMetricSettings.tsx | 181 ++++++++++++++++++ .../HistogramAndMetrics.tsx | 167 ++++------------ .../AggregationAndMetricsSettings.tsx | 161 +++------------- 3 files changed, 246 insertions(+), 263 deletions(-) create mode 100644 public/pages/Commons/BaseAggregationAndMetricSettings.tsx diff --git a/public/pages/Commons/BaseAggregationAndMetricSettings.tsx b/public/pages/Commons/BaseAggregationAndMetricSettings.tsx new file mode 100644 index 000000000..a0468f000 --- /dev/null +++ b/public/pages/Commons/BaseAggregationAndMetricSettings.tsx @@ -0,0 +1,181 @@ +/* + * Copyright 2020 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"). + * You may not use this file except in compliance with the License. + * A copy of the License is located at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * or in the "license" file accompanying this file. This file is distributed + * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either + * express or implied. See the License for the specific language governing + * permissions and limitations under the License. + */ + +import React, {Fragment} from "react"; + +import { + EuiFlexItem, + EuiText, + EuiBasicTable, + EuiTableFieldDataColumnType, + EuiPanel, + EuiFlexGroup, + EuiIcon, +} from "@elastic/eui"; + +import {DimensionItem, MetricItem} from "../../../models/interfaces"; + +export const AGGREGATION_AND_METRIC_SETTINGS = 'Aggregation and metrics settings' + +export interface BaseAggregationAndMetricsState { + from: number; + size: number; + sortField: string; + sortDirection: string; + dimensionFrom: number; + dimensionSize: number; + dimensionSortField: string; + dimensionSortDirection: string; +} + + +export const BaseAggregationColumns: Readonly>[] = [ + { + field: "sequence", + name: "Sequence", + sortable: true, + align: "left", + dataType: "number", + }, + { + field: "field.label", + name: "Field name", + align: "left", + }, + { + field: "aggregationMethod", + name: "Aggregation method", + align: "left", + }, + { + field: "interval", + name: "Interval", + dataType: "number", + align: "left", + render: (interval: null | number) => { + if (interval == null) return "-"; + else return `${interval}`; + }, + }, +]; + + +export const BaseMetricsColumns: Readonly>[] = [ + { + field: "source_field", + name: "Field Name", + }, + { + field: "min", + name: "Min", + align: "center", + render: (min: boolean) => min && , + }, + { + field: "max", + name: "Max", + align: "center", + render: (max: boolean) => max && , + }, + { + field: "sum", + name: "Sum", + align: "center", + render: (sum: boolean) => sum && , + }, + { + field: "avg", + name: "Avg", + align: "center", + render: (avg: boolean) => avg && , + }, + { + field: "value_count", + name: "Value count", + align: "center", + render: (value_count: boolean) => value_count && , + }, +]; + +export function sequenceTableComponents(selectedDimensionField, items, columns, pagination, sorting, onChange) { + if(selectedDimensionField.length == 0) { + return ( + +
No fields added for aggregation
+
+ ) + } + + return ( + + + + + ) +} + +export function additionalMetricsComponent(selectedMetrics) { + return ( + + + +

Additional metrics

+
+
+ + +

{`(${selectedMetrics.length})`}

+
+
+
+ ) +} + +export function sourceFieldComponents(selectedMetrics, items, columns, pagination, sorting, onChange) { + + if(selectedMetrics.length == 0) { + return ( + +
No fields added for metrics
+
+ ) + } + + return ( + + + + + + ) +} diff --git a/public/pages/CreateRollup/components/HistogramAndMetrics/HistogramAndMetrics.tsx b/public/pages/CreateRollup/components/HistogramAndMetrics/HistogramAndMetrics.tsx index 856339ee3..bd15b66e2 100644 --- a/public/pages/CreateRollup/components/HistogramAndMetrics/HistogramAndMetrics.tsx +++ b/public/pages/CreateRollup/components/HistogramAndMetrics/HistogramAndMetrics.tsx @@ -13,16 +13,14 @@ * permissions and limitations under the License. */ -import React, { Component, Fragment } from "react"; +import React, { Component } from "react"; import { EuiComboBoxOptionOption, EuiFlexGrid, EuiFlexItem, EuiSpacer, EuiText, - EuiBasicTable, EuiTableFieldDataColumnType, - EuiPanel, EuiFlexGroup, // @ts-ignore Criteria, @@ -36,6 +34,12 @@ import { ModalConsumer } from "../../../../components/Modal"; import { DEFAULT_PAGE_SIZE_OPTIONS } from "../../../Rollups/utils/constants"; import { parseTimeunit } from "../../utils/helpers"; import { DimensionItem, MetricItem } from "../../../../../models/interfaces"; +import { + additionalMetricsComponent, + AGGREGATION_AND_METRIC_SETTINGS, + BaseAggregationAndMetricsState, + BaseAggregationColumns, BaseMetricsColumns, sequenceTableComponents, sourceFieldComponents +} from "../../../Commons/BaseAggregationAndMetricSettings"; interface HistogramAndMetricsProps { rollupId: string; @@ -49,97 +53,35 @@ interface HistogramAndMetricsProps { selectedMetrics: MetricItem[]; } -interface HistogramAndMetricsState { - from: number; - size: number; - sortField: string; - sortDirection: string; +interface HistogramAndMetricsState extends BaseAggregationAndMetricsState { metricsShown: MetricItem[]; - dimensionFrom: number; - dimensionSize: number; - dimensionSortField: string; - dimensionSortDirection: string; dimensionsShown: DimensionItem[]; } -const aggregationColumns: EuiTableFieldDataColumnType[] = [ - { - field: "sequence", - name: "Sequence", - sortable: true, - align: "left", - dataType: "number", - }, - { - field: "field.label", - name: "Field name", - align: "left", - }, + +const _createFlowAggregateColumns: Readonly>[] = [ { field: "field.type", name: "Field type", align: "left", render: (type) => (type == undefined ? "-" : type), }, - { - field: "aggregationMethod", - name: "Aggregation method", - align: "left", - }, - { - field: "interval", - name: "Interval", - dataType: "number", - align: "left", - render: (interval: null | number) => { - if (interval == null) return "-"; - else return `${interval}`; - }, - }, -]; +] -const metricsColumns: EuiTableFieldDataColumnType[] = [ - { - field: "source_field.label", - name: "Field Name", - }, - { +const _createFlowMetricsColumn: Readonly> = { field: "all", name: "All", align: "center", render: (all: boolean) => all && , - }, - { - field: "min", - name: "Min", - align: "center", - render: (min: boolean) => min && , - }, - { - field: "max", - name: "Max", - align: "center", - render: (max: boolean) => max && , - }, - { - field: "sum", - name: "Sum", - align: "center", - render: (sum: boolean) => sum && , - }, - { - field: "avg", - name: "Avg", - align: "center", - render: (avg: boolean) => avg && , - }, - { - field: "value_count", - name: "Value count", - align: "center", - render: (value_count: boolean) => value_count && , - }, -]; + } + + +const aggregationColumns: Readonly>[] + = [...BaseAggregationColumns,..._createFlowAggregateColumns]; + +// Adding 'all' column at 1st index. +const metricsColumns: EuiTableFieldDataColumnType[] = + [...BaseMetricsColumns].splice(1, 0, _createFlowMetricsColumn); export default class HistogramAndMetrics extends Component { constructor(props: HistogramAndMetricsProps) { @@ -260,7 +202,7 @@ export default class HistogramAndMetrics extends Component } bodyStyles={{ padding: "initial" }} - title="Aggregation and metrics setting" + title={AGGREGATION_AND_METRIC_SETTINGS} titleSize="m" >
@@ -303,61 +245,22 @@ export default class HistogramAndMetrics extends Component - {selectedDimensionField.length ? ( - - - - - - ) : ( - -
No fields added for aggregation
-
- )} + { + sequenceTableComponents( selectedDimensionField, dimensionsShown, aggregationColumns, + dimensionPagination, dimensionSorting, this.onDimensionTableChange ) + } + - - - -

Additional metrics

-
-
- - -

{`(${selectedMetrics.length})`}

-
-
-
- {selectedMetrics.length ? ( - - - - - - ) : ( - -
No fields added for metrics
-
- )} + { + additionalMetricsComponent(selectedMetrics) + } + + { + sourceFieldComponents(selectedMetrics, metricsShown, metricsColumns, pagination, + sorting, this.onTableChange) + }
diff --git a/public/pages/RollupDetails/components/AggregationAndMetricsSettings/AggregationAndMetricsSettings.tsx b/public/pages/RollupDetails/components/AggregationAndMetricsSettings/AggregationAndMetricsSettings.tsx index 37f4babfe..3051097ad 100644 --- a/public/pages/RollupDetails/components/AggregationAndMetricsSettings/AggregationAndMetricsSettings.tsx +++ b/public/pages/RollupDetails/components/AggregationAndMetricsSettings/AggregationAndMetricsSettings.tsx @@ -13,16 +13,13 @@ * permissions and limitations under the License. */ -import React, { Component, Fragment } from "react"; +import React, { Component } from "react"; import { EuiFlexGrid, EuiSpacer, EuiFlexItem, EuiText, EuiFlexGroup, - EuiPanel, - EuiBasicTable, - EuiIcon, EuiTableFieldDataColumnType, //@ts-ignore Criteria, @@ -34,6 +31,13 @@ import { ContentPanel } from "../../../../components/ContentPanel"; import { DEFAULT_PAGE_SIZE_OPTIONS } from "../../../Rollups/utils/constants"; import { parseTimeunit } from "../../../CreateRollup/utils/helpers"; import { DimensionItem, FieldItem, MetricItem } from "../../../../../models/interfaces"; +import { + additionalMetricsComponent, + AGGREGATION_AND_METRIC_SETTINGS, + BaseAggregationAndMetricsState, + BaseAggregationColumns, + BaseMetricsColumns, sequenceTableComponents, sourceFieldComponents +} from "../../../Commons/BaseAggregationAndMetricSettings"; interface AggregationAndMetricsSettingsProps { timestamp: string; @@ -47,83 +51,12 @@ interface AggregationAndMetricsSettingsProps { onChangeMetricsShown: (from: number, size: number) => void; } -interface AggregationAndMetricsSettingsState { - from: number; - size: number; - sortField: string; - sortDirection: string; - dimensionFrom: number; - dimensionSize: number; - dimensionSortField: string; - dimensionSortDirection: string; +interface AggregationAndMetricsSettingsState extends BaseAggregationAndMetricsState { } -const aggregationColumns: EuiTableFieldDataColumnType[] = [ - { - field: "sequence", - name: "Sequence", - sortable: true, - align: "left", - dataType: "number", - }, - { - field: "field.label", - name: "Field name", - align: "left", - }, - { - field: "aggregationMethod", - name: "Aggregation method", - align: "left", - }, - { - field: "interval", - name: "Interval", - dataType: "number", - align: "left", - render: (interval: null | number) => { - if (interval == null) return "-"; - else return `${interval}`; - }, - }, -]; +const aggregationColumns: Readonly>[] = BaseAggregationColumns; -const metricsColumns = [ - { - field: "source_field", - name: "Field Name", - }, - { - field: "min", - name: "Min", - align: "center", - render: (min: boolean) => min && , - }, - { - field: "max", - name: "Max", - align: "center", - render: (max: boolean) => max && , - }, - { - field: "sum", - name: "Sum", - align: "center", - render: (sum: boolean) => sum && , - }, - { - field: "avg", - name: "Avg", - align: "center", - render: (avg: boolean) => avg && , - }, - { - field: "value_count", - name: "Value count", - align: "center", - render: (value_count: boolean) => value_count && , - }, -]; +const metricsColumns = BaseMetricsColumns; export default class AggregationAndMetricsSettings extends Component< AggregationAndMetricsSettingsProps, @@ -207,7 +140,11 @@ export default class AggregationAndMetricsSettings extends Component< interval = intervalValue[0] + " " + parseTimeunit(intervalUnit[0]); } return ( - +
@@ -247,61 +184,23 @@ export default class AggregationAndMetricsSettings extends Component< - {selectedDimensionField.length ? ( - - - - - - ) : ( - -
No fields added for aggregation
-
- )} + { + sequenceTableComponents(selectedDimensionField, dimensionsShown, aggregationColumns, + dimensionPagination, dimensionSorting, this.onDimensionTableChange) + } + - - - -

Additional metrics

-
-
- - -

{`(${selectedMetrics.length})`}

-
-
-
- {selectedMetrics.length ? ( - - - - - - ) : ( - -
No fields added for metrics
-
- )} + + { + additionalMetricsComponent(selectedMetrics) + } + + { + sourceFieldComponents(selectedMetrics, metricsShown, metricsColumns, pagination, + sorting, this.onTableChange) + }
From 067f3483cd48c6072695fb778e780c07cedc6682 Mon Sep 17 00:00:00 2001 From: Ravi Thaluru Date: Mon, 19 Apr 2021 21:17:09 -0700 Subject: [PATCH 02/93] Initial commit to introduce transforms --- public/pages/Main/Main.tsx | 40 ++++++++++++++++++++++++++++++++++++++ public/utils/constants.ts | 8 ++++++++ 2 files changed, 48 insertions(+) diff --git a/public/pages/Main/Main.tsx b/public/pages/Main/Main.tsx index f930f2cb9..f92019afc 100644 --- a/public/pages/Main/Main.tsx +++ b/public/pages/Main/Main.tsx @@ -39,6 +39,7 @@ enum Navigation { ManagedIndices = "Managed Indices", Indices = "Indices", Rollups = "Rollup Jobs", + Transforms = "Transform Jobs", } enum Pathname { @@ -46,6 +47,7 @@ enum Pathname { ManagedIndices = "/managed-indices", Indices = "/indices", Rollups = "/rollups", + Transforms = "/transforms", } interface MainProps extends RouteComponentProps {} @@ -85,6 +87,12 @@ export default class Main extends Component { href: `#${Pathname.Rollups}`, isSelected: pathname === Pathname.Rollups, }, + { + name: Navigation.Transforms, + id: 5, + href: `#${Pathname.Transforms}`, + isSelected: pathname === Pathname.Transforms + }, ], }, ]; @@ -184,6 +192,38 @@ export default class Main extends Component { )} /> + ( +
+ +
+ )} + /> + ( +
+ +
+ )} + /> + ( +
+ +
+ )} + /> + ( +
+ +
+ )} + /> diff --git a/public/utils/constants.ts b/public/utils/constants.ts index 19368625f..61db333ca 100644 --- a/public/utils/constants.ts +++ b/public/utils/constants.ts @@ -30,6 +30,10 @@ export const ROUTES = Object.freeze({ CREATE_ROLLUP: "/create-rollup", EDIT_ROLLUP: "/edit-rollup", ROLLUP_DETAILS: "/rollup-details", + TRANSFORMS: "/transforms", + CREATE_TRANSFORM: "/create-transform", + EDIT_TRANSFORM: "/edit-transform", + TRANSFORM_DETAILS: "/transform-details", }); export const BREADCRUMBS = Object.freeze({ @@ -44,6 +48,10 @@ export const BREADCRUMBS = Object.freeze({ CREATE_ROLLUP: { text: "Create rollup job" }, EDIT_ROLLUP: { text: "Edit rollup job" }, ROLLUP_DETAILS: { text: "Rollup details" }, + TRANSFORMS: { text: "Transform jobs", href: `#${ROUTES.TRANSFORMS}` }, + CREATE_TRANSFORM: { text: "Create transform job" }, + EDIT_TRANSFORM: { text: "Edit transform job" }, + TRANSFORM_DETAILS: { text: "Transform details" }, }); // TODO: Kibana EUI has a SortDirection already From eac5e4445e117ab656757d55ed9fb746a01b6033 Mon Sep 17 00:00:00 2001 From: Ravi Thaluru Date: Mon, 19 Apr 2021 22:27:50 -0700 Subject: [PATCH 03/93] Adding transform models and client service --- models/interfaces.ts | 46 ++++++++++++++++++++ public/services/TransformService.ts | 67 +++++++++++++++++++++++++++++ server/models/interfaces.ts | 37 +++++++++++++++- utils/constants.ts | 1 + 4 files changed, 150 insertions(+), 1 deletion(-) create mode 100644 public/services/TransformService.ts diff --git a/models/interfaces.ts b/models/interfaces.ts index c8e4860a6..5d122bcb8 100644 --- a/models/interfaces.ts +++ b/models/interfaces.ts @@ -67,6 +67,14 @@ export interface DocumentRollup { metadata: any; } +export interface DocumentTransform { + _id: string; + _seqNo: string; + _primaryTerm: number; + transform: Transform; + metadata: any; +} + // TODO: Fill out when needed // TODO: separate a frontend Policy from backendPolicy export interface Policy { @@ -125,6 +133,44 @@ export interface RollupMetadata { }; } +export interface Transform { + description: string; + groups: RollupDimensionItem[]; + enabled: boolean; + enabled_at: number | null; + updated_at: number; + metadata_id: string | null; + aggregations: Map; + page_size: number; + schedule: IntervalSchedule | CronSchedule; + schema_version: number; + source_index: string; + target_index: string; + roles: String[]; + data_selection_query: Map; +} + +export interface TransformMetadata { + metadata_id: string; + transform_metadata: { + id: string; + seq_no: number; + primary_term: number; + transform_id: string; + after_key: Map | null; + last_updated_at: number; + status: string; + failure_reason: string | null; + stats: { + pages_processed: number | null; + documents_processed: number | null; + documents_indexed: number | null; + index_time_in_millis: number | null; + search_time_in_millis: number | null; + } + } +} + export interface IntervalSchedule { interval: { startTime: number | null; diff --git a/public/services/TransformService.ts b/public/services/TransformService.ts new file mode 100644 index 000000000..e7ec6d607 --- /dev/null +++ b/public/services/TransformService.ts @@ -0,0 +1,67 @@ +/* + * Copyright 2021 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"). + * You may not use this file except in compliance with the License. + * A copy of the License is located at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * or in the "license" file accompanying this file. This file is distributed + * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either + * express or implied. See the License for the specific language governing + * permissions and limitations under the License. + */ + +import {HttpSetup} from "kibana/public"; +import {ServerResponse} from "../../server/models/types"; +import {GetTransformResponse, PutTransformResponse} from "../../server/models/interfaces"; +import {NODE_API} from "../../utils/constants"; +import {DocumentTransform, Transform} from "../../models/interfaces"; + +export default class TransformService { + httpClient: HttpSetup; + + constructor(httpClient: HttpSetup) { + this.httpClient = httpClient + } + + getTransforms = async (queryObject: object): Promise> => { + const url = `..${NODE_API.TRANSFORMS}` + // @ts-ignore + return (await this.httpClient.get(url, { query: queryObject })) as ServerResponse; + }; + + putTransform = async ( + transform: Transform, + transformId: string, + seqNo?: number, + primaryTerm?: number + ): Promise> => { + const url = `..${NODE_API.TRANSFORMS}/${transformId}` + return (await this.httpClient.put(url, { query: {seqNo, primaryTerm}, body: JSON.stringify(transform) })) as + ServerResponse; + }; + + getTransform = async (transformId: string): Promise> => { + const url = `..${NODE_API.TRANSFORMS}/${transformId}` + return (await this.httpClient.get(url)) as ServerResponse + }; + + deleteTransform = async(transformId: string): Promise> => { + const url = `..${NODE_API.TRANSFORMS}/${transformId}`; + return (await this.httpClient.delete(url)) as ServerResponse; + }; + + startTransform = async(transformId: string): Promise> => { + const url = `..${NODE_API.TRANSFORMS}/${transformId}/_start`; + return (await this.httpClient.post(url)) as ServerResponse; + }; + + stopTransform = async(transformId: string): Promise> => { + const url = `..${NODE_API.TRANSFORMS}/${transformId}/_stop`; + return (await this.httpClient.post(url)) as ServerResponse; + } + + // TODO: implement preview transform +} diff --git a/server/models/interfaces.ts b/server/models/interfaces.ts index 1075be34d..b7e3a497c 100644 --- a/server/models/interfaces.ts +++ b/server/models/interfaces.ts @@ -14,7 +14,14 @@ */ import { IndexService, ManagedIndexService, PolicyService, RollupService } from "../services"; -import { DocumentPolicy, DocumentRollup, ManagedIndexItem, Rollup } from "../../models/interfaces"; +import { + DocumentPolicy, + DocumentRollup, + DocumentTransform, + ManagedIndexItem, + Rollup, + Transform +} from "../../models/interfaces"; export interface NodeServices { indexService: IndexService; @@ -88,6 +95,23 @@ export interface PutRollupResponse { rollup: { rollup: Rollup }; } +export interface DeleteTransformResponse { + result: string; +} + +export interface GetTransformResponse { + transforms: DocumentTransform[]; + totalTransforms: number; + metadata: any; +} + +export interface PutTransformResponse { + _id: string; + _primary_term: string; + _seq_no: string; + transform: { transform: Transform }; +} + export interface IndexUpdateResponse { updatedIndices: number; failures: boolean; @@ -139,6 +163,17 @@ export interface PutRollupParams { body: string; } +export interface PutTransformParams { + transformId: string; + if_seq_no?: string; + if_primary_term?: string; + body: string; +} + +export interface DeleteTransformParams { + transformId: string; +} + // TODO: remove optional failedIndices after fixing retry API to always array export interface RetryResponse { failures: boolean; diff --git a/utils/constants.ts b/utils/constants.ts index f76d056bc..7db04af57 100644 --- a/utils/constants.ts +++ b/utils/constants.ts @@ -22,6 +22,7 @@ export const NODE_API = Object.freeze({ EDIT_ROLLOVER_ALIAS: `${BASE_API_PATH}/editRolloverAlias`, POLICIES: `${BASE_API_PATH}/policies`, ROLLUPS: `${BASE_API_PATH}/rollups`, + TRANSFORMS: `${BASE_API_PATH}/transforms`, MANAGED_INDICES: `${BASE_API_PATH}/managedIndices`, RETRY: `${BASE_API_PATH}/retry`, CHANGE_POLICY: `${BASE_API_PATH}/changePolicy`, From 96c3d0356ecc4aa7fd17fec5e7d70892a14bf8eb Mon Sep 17 00:00:00 2001 From: Ravi Thaluru Date: Wed, 21 Apr 2021 14:54:35 -0700 Subject: [PATCH 04/93] Adding changes to get transform page --- public/models/interfaces.ts | 2 + public/pages/Main/Main.tsx | 3 +- .../components/DeleteModal/DeleteModal.tsx | 67 ++++ .../components/DeleteModal/index.ts | 18 + .../TransformEmptyPrompt.tsx | 71 ++++ .../components/TransformEmptyPrompt/index.ts | 18 + .../containers/Transforms/Transforms.tsx | 352 ++++++++++++++++++ public/pages/Transforms/models/interfaces.ts | 25 ++ public/pages/Transforms/utils/constants.tsx | 25 ++ public/pages/Transforms/utils/helpers.ts | 32 ++ .../pages/Transforms/utils/metadataHelper.tsx | 85 +++++ public/services/index.ts | 5 +- 12 files changed, 701 insertions(+), 2 deletions(-) create mode 100644 public/pages/Transforms/components/DeleteModal/DeleteModal.tsx create mode 100644 public/pages/Transforms/components/DeleteModal/index.ts create mode 100644 public/pages/Transforms/components/TransformEmptyPrompt/TransformEmptyPrompt.tsx create mode 100644 public/pages/Transforms/components/TransformEmptyPrompt/index.ts create mode 100644 public/pages/Transforms/containers/Transforms/Transforms.tsx create mode 100644 public/pages/Transforms/models/interfaces.ts create mode 100644 public/pages/Transforms/utils/constants.tsx create mode 100644 public/pages/Transforms/utils/helpers.ts create mode 100644 public/pages/Transforms/utils/metadataHelper.tsx diff --git a/public/models/interfaces.ts b/public/models/interfaces.ts index 4fbade055..bf614f57c 100644 --- a/public/models/interfaces.ts +++ b/public/models/interfaces.ts @@ -14,10 +14,12 @@ */ import { IndexService, ManagedIndexService, PolicyService, RollupService } from "../services"; +import TransformService from "../services/TransformService"; export interface BrowserServices { indexService: IndexService; managedIndexService: ManagedIndexService; policyService: PolicyService; rollupService: RollupService; + transformService: TransformService; } diff --git a/public/pages/Main/Main.tsx b/public/pages/Main/Main.tsx index f92019afc..1f2f426e3 100644 --- a/public/pages/Main/Main.tsx +++ b/public/pages/Main/Main.tsx @@ -32,6 +32,7 @@ import { CoreServicesConsumer } from "../../components/core_services"; import CreateRollupForm from "../CreateRollup/containers/CreateRollupForm"; import EditRollup from "../EditRollup/containers"; import RollupDetails from "../RollupDetails/containers/RollupDetails"; +import Transforms from "../Transforms/containers/Transforms/Transforms"; enum Navigation { IndexManagement = "Index Management", @@ -196,7 +197,7 @@ export default class Main extends Component { path={ROUTES.TRANSFORMS} render = {(props: RouteComponentProps) => (
- +
)} /> diff --git a/public/pages/Transforms/components/DeleteModal/DeleteModal.tsx b/public/pages/Transforms/components/DeleteModal/DeleteModal.tsx new file mode 100644 index 000000000..1d6539e15 --- /dev/null +++ b/public/pages/Transforms/components/DeleteModal/DeleteModal.tsx @@ -0,0 +1,67 @@ +/* + * Copyright 2021 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"). + * You may not use this file except in compliance with the License. + * A copy of the License is located at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * or in the "license" file accompanying this file. This file is distributed + * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either + * express or implied. See the License for the specific language governing + * permissions and limitations under the License. + */ + +import React, { ChangeEvent, Component, Fragment } from "react"; +import { EuiConfirmModal, EuiForm, EuiFormRow, EuiFieldText, EuiOverlayMask, EuiSpacer } from "@elastic/eui"; + +// TODO: Merge with Rollup to create generic component +interface DeleteModalProps { + item: string; + closeDeleteModal: (event?: any) => void; + onClickDelete: (event?: any) => void; +} + +interface DeleteModalState { + confirmDeleteText: string; +} + +export default class DeleteModal extends Component { + state = { confirmDeleteText: "" }; + + onChange = (e: ChangeEvent): void => { + this.setState({ confirmDeleteText: e.target.value }); + }; + + render() { + const { item, closeDeleteModal, onClickDelete } = this.props; + const { confirmDeleteText } = this.state; + + return ( + + + + + By deleting "{item}", all future scheduled executions will be canceled. However, your target index + and data in it will remain intact. + + + + + + + + + ); + } +} diff --git a/public/pages/Transforms/components/DeleteModal/index.ts b/public/pages/Transforms/components/DeleteModal/index.ts new file mode 100644 index 000000000..0d2c98428 --- /dev/null +++ b/public/pages/Transforms/components/DeleteModal/index.ts @@ -0,0 +1,18 @@ +/* + * Copyright 2021 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"). + * You may not use this file except in compliance with the License. + * A copy of the License is located at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * or in the "license" file accompanying this file. This file is distributed + * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either + * express or implied. See the License for the specific language governing + * permissions and limitations under the License. + */ + +import DeleteModal from "./DeleteModal"; + +export default DeleteModal; diff --git a/public/pages/Transforms/components/TransformEmptyPrompt/TransformEmptyPrompt.tsx b/public/pages/Transforms/components/TransformEmptyPrompt/TransformEmptyPrompt.tsx new file mode 100644 index 000000000..9a4868e1d --- /dev/null +++ b/public/pages/Transforms/components/TransformEmptyPrompt/TransformEmptyPrompt.tsx @@ -0,0 +1,71 @@ +/* + * Copyright 2021 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"). + * You may not use this file except in compliance with the License. + * A copy of the License is located at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * or in the "license" file accompanying this file. This file is distributed + * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either + * express or implied. See the License for the specific language governing + * permissions and limitations under the License. + */ + +import React from "react"; +import { EuiButton, EuiEmptyPrompt, EuiText } from "@elastic/eui"; +import {PLUGIN_NAME, ROUTES} from "../../../../utils/constants"; + +interface TransformEmptyPromptProps { + filterIsApplied: boolean; + loading: boolean; + resetFilters: () => void; +} + +export const TEXT = { + RESET_FILTERS: "There are no transform jobs matching your applied filters. Reset your filters to view your transform jobs.", + NO_TRANSFORMS: + "Transform jobs help you create a materialized view on top of existing data.", + LOADING: "Loading transform jobs...", +}; + +const getMessagePrompt = ({ filterIsApplied, loading }: TransformEmptyPromptProps) => { + if (loading) return TEXT.LOADING; + if (filterIsApplied) return TEXT.RESET_FILTERS; + return TEXT.NO_TRANSFORMS; +}; + +const getActions: React.SFC = ({ filterIsApplied, loading, resetFilters }) => { + if (loading) { + return null; + } + + if (filterIsApplied) { + return ( + + Reset Filters + + ); + } + + return ( + + Create transform + + ); +}; + +const TransformEmptyPrompt: React.SFC = (props) => ( + +

{getMessagePrompt(props)}

+ + } + actions={getActions(props)} + /> +); + +export default TransformEmptyPrompt; diff --git a/public/pages/Transforms/components/TransformEmptyPrompt/index.ts b/public/pages/Transforms/components/TransformEmptyPrompt/index.ts new file mode 100644 index 000000000..7a5714db4 --- /dev/null +++ b/public/pages/Transforms/components/TransformEmptyPrompt/index.ts @@ -0,0 +1,18 @@ +/* + * Copyright 2021 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"). + * You may not use this file except in compliance with the License. + * A copy of the License is located at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * or in the "license" file accompanying this file. This file is distributed + * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either + * express or implied. See the License for the specific language governing + * permissions and limitations under the License. + */ + +import TransformEmptyPrompt from "./TransformEmptyPrompt"; + +export default TransformEmptyPrompt; diff --git a/public/pages/Transforms/containers/Transforms/Transforms.tsx b/public/pages/Transforms/containers/Transforms/Transforms.tsx new file mode 100644 index 000000000..044cffc2d --- /dev/null +++ b/public/pages/Transforms/containers/Transforms/Transforms.tsx @@ -0,0 +1,352 @@ +/* + * Copyright 2021 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"). + * You may not use this file except in compliance with the License. + * A copy of the License is located at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * or in the "license" file accompanying this file. This file is distributed + * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either + * express or implied. See the License for the specific language governing + * permissions and limitations under the License. + */ + +import { + Direction, + EuiPanel, + EuiFlexGroup, + EuiFlexItem, + EuiTitle, + EuiButton, + EuiBasicTable, + EuiPopover, + EuiContextMenuPanel, + EuiFieldSearch, + EuiHorizontalRule, + EuiPagination, + EuiLink, + EuiTextColor, + EuiTableFieldDataColumnType, + EuiContextMenuItem, + // @ts-ignore + Pagination, + EuiTableSelectionType, + EuiTableSortingType, +} from "@elastic/eui"; +import queryString from "query-string"; +import { RouteComponentProps } from "react-router-dom"; +import TransformService from "../../../../services/TransformService"; +import {DocumentTransform} from "../../../../../models/interfaces"; +import React, { Component } from "react"; +import { CoreServicesContext } from "../../../../components/core_services"; +import {getURLQueryParams} from "../../utils/helpers"; +import {TransformQueryParams} from "../../models/interfaces"; +import {getErrorMessage} from "../../../../utils/helpers"; +import {ROUTES} from "../../../../utils/constants"; +import DeleteModal from "../../components/DeleteModal"; +import TransformEmptyPrompt from "../../components/TransformEmptyPrompt"; +import {renderEnabled, renderStatus} from "../../utils/metadataHelper"; +import {DEFAULT_PAGE_SIZE_OPTIONS} from "../../../Indices/utils/constants"; +import _ from "lodash"; + +interface TransformProps extends RouteComponentProps { + transformService: TransformService +} + +interface TransformState { + totalTransforms: number; + from: number; + size: number; + search: string; + sortField: keyof DocumentTransform; + sortDirection: Direction; + selectedItems: DocumentTransform[]; + transforms: DocumentTransform[]; + fetchingTransforms: boolean; + transformMetadata: {}; + isPopOverOpen: boolean; + isDeleteModalVisible: boolean; +} + +export default class Transforms extends Component { + static contextType = CoreServicesContext; + constructor(props: TransformProps) { + super(props); + + const { from, size, search, sortField, sortDirection } = getURLQueryParams(this.props.location); + + this.state = { + totalTransforms: 0, + from, + size, + search, + sortField, + sortDirection, + selectedItems: [], + transforms: [], + fetchingTransforms: false, + transformMetadata: {}, + isPopOverOpen: false, + isDeleteModalVisible: false, + }; + + this.getTransforms = _.debounce(this.getTransforms, 500, { leading: true }); + }; + + render() { + const { + totalTransforms, + from, + size, + search, + sortField, + sortDirection, + selectedItems, + transforms, + fetchingTransforms, + isPopOverOpen, + isDeleteModalVisible, + } = this.state; + + const filterIsApplied = !!search; + const pageCount = Math.ceil(totalTransforms / size) || 1; + const page = Math.floor(from / size); + const pagination: Pagination = { + pageIndex: page, + pageSize: size, + pageSizeOptions: DEFAULT_PAGE_SIZE_OPTIONS, + totalItemCount: totalTransforms, + }; + + const columns: EuiTableFieldDataColumnType[] = [ + { + field: "_id", + name: "Name", + sortable: true, + textOnly: true, + truncateText: true, + render: (_id) => ( + this.props.history.push(`${ROUTES.TRANSFORM_DETAILS}?id=${_id}`)} data-test-subj={`transformLink_${_id}`}> + {_id} + + ), + }, + { + field: "transform.source_index", + name: "Source index", + sortable: true, + textOnly: true, + truncateText: true, + }, + { + field: "transform.target_index", + name: "Target index", + sortable: true, + textOnly: true, + truncateText: true, + }, + { + field: "transform.enabled", + name: "Job state", + sortable: true, + textOnly: true, + truncateText: true, + render: renderEnabled, + }, + { + field: "metadata", + name: "transform job status", + sortable: false, + textOnly: true, + render: (metadata) => renderStatus(metadata), + }, + ]; + + const actionButton = ( + + Actions + + ); + + const actionItems = [ + { + this.closePopover(); + this.onClickEdit(); + }} + > + Edit + , + { + this.closePopover(); + this.showDeleteModal(); + }} + > + Delete + , + ]; + + const selection: EuiTableSelectionType = { + onSelectionChange: this.onSelectionChange, + }; + + const sorting: EuiTableSortingType = { + sort: { + direction: sortDirection, + field: sortField, + }, + }; + + + return ( + + + + +

{"Transform jobs (" + `${transforms.length}` + ")"}

+
+
+ + + + + Disable + + + + { + this.onEnable(); + }} + data-test-subj="enableButton" + > + Enable + + + + + + + + + + Create transform job + + + + +
+ +
+ + + + + {pageCount > 1 && ( + + + + )} + + + + + + } + onChange={this.onTableChange} + pagination={pagination} + selection={selection} + sorting={sorting} + tableLayout="auto" + /> + {isDeleteModalVisible && ( + + )} +
+
+ ); + } + + getTransforms = async(): Promise => { + this.setState( { fetchingTransforms: true }); + try { + const { transformService, history } = this.props; + const queryObject = Transforms.getQueryObjectFromState(this.state); + const queryParamsString = queryString.stringify(Transforms.getQueryObjectFromState(this.state)); + history.replace({ ...this.props.location, search: queryParamsString }); + const response = await transformService.getTransforms(queryObject); + if (response.ok) { + const { transforms, totalTransforms, metadata } = response.response; + this.setState({transforms, totalTransforms, transformMetadata: metadata}); + } else { + this.context.notifications.toasts.addDanger(response.error); + } + } catch (err) { + this.context.notifications.toasts.addDanger(getErrorMessage(err, "There was problem loading transforms")); + } + this.setState({ fetchingTransforms: false }); + }; + + getSelectedTransformIds() { return "asd" }; + onSelectionChange() {}; + closeDeleteModal() {}; + onClickCreate() {}; + onClickDelete() {}; + onPageClick() {}; + onTableChange() {}; + onEnable() {}; + onDisable() {}; + closePopover() {}; + resetFilters() {}; + onSearchChange() {}; + onActionButtonClick() {}; + onClickEdit() {}; + showDeleteModal() {}; + + + static getQueryObjectFromState(transformState : TransformState) : TransformQueryParams { + return transformState; + } +} + diff --git a/public/pages/Transforms/models/interfaces.ts b/public/pages/Transforms/models/interfaces.ts new file mode 100644 index 000000000..620afece3 --- /dev/null +++ b/public/pages/Transforms/models/interfaces.ts @@ -0,0 +1,25 @@ +/* + * Copyright 2021 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"). + * You may not use this file except in compliance with the License. + * A copy of the License is located at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * or in the "license" file accompanying this file. This file is distributed + * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either + * express or implied. See the License for the specific language governing + * permissions and limitations under the License. + */ + +import { Direction } from "@elastic/eui"; +import {DocumentTransform} from "../../../../models/interfaces"; + +export interface TransformQueryParams { + from: number; + size: number; + search: string; + sortField: keyof DocumentTransform; + sortDirection: Direction; +} diff --git a/public/pages/Transforms/utils/constants.tsx b/public/pages/Transforms/utils/constants.tsx new file mode 100644 index 000000000..3d26beeb6 --- /dev/null +++ b/public/pages/Transforms/utils/constants.tsx @@ -0,0 +1,25 @@ +/* + * Copyright 2021 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"). + * You may not use this file except in compliance with the License. + * A copy of the License is located at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * or in the "license" file accompanying this file. This file is distributed + * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either + * express or implied. See the License for the specific language governing + * permissions and limitations under the License. + */ + +import {SortDirection} from "../../../utils/constants"; + +// TODO: Consolidate with Rollup +export const DEFAULT_QUERY_PARAMS = { + from: 0, + size: 20, + search: "", + sortField: "_id", + sortDirection: SortDirection.DESC, +}; diff --git a/public/pages/Transforms/utils/helpers.ts b/public/pages/Transforms/utils/helpers.ts new file mode 100644 index 000000000..c305ee1ae --- /dev/null +++ b/public/pages/Transforms/utils/helpers.ts @@ -0,0 +1,32 @@ +/* + * Copyright 2021 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"). + * You may not use this file except in compliance with the License. + * A copy of the License is located at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * or in the "license" file accompanying this file. This file is distributed + * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either + * express or implied. See the License for the specific language governing + * permissions and limitations under the License. + */ + +import queryString from "query-string"; +import {TransformQueryParams} from "../models/interfaces"; +import {DEFAULT_QUERY_PARAMS} from "./constants"; + +export function getURLQueryParams(location: { search: string }): TransformQueryParams { + const { from, size, search, sortField, sortDirection } = queryString.parse(location.search); + + return { + // @ts-ignores + from: isNaN(parseInt(from, 10)) ? DEFAULT_QUERY_PARAMS.from : parseInt(from, 10), + // @ts-ignores + size: isNaN(parseInt(size, 10)) ? DEFAULT_QUERY_PARAMS.size : parseInt(size, 10), + search: typeof search !== "string" ? DEFAULT_QUERY_PARAMS.search : search, + sortField: typeof sortField !== "string" ? DEFAULT_QUERY_PARAMS.sortField : sortField, + sortDirection: typeof sortDirection !== "string" ? DEFAULT_QUERY_PARAMS.sortDirection : sortDirection, + }; +} diff --git a/public/pages/Transforms/utils/metadataHelper.tsx b/public/pages/Transforms/utils/metadataHelper.tsx new file mode 100644 index 000000000..c05f4c6b5 --- /dev/null +++ b/public/pages/Transforms/utils/metadataHelper.tsx @@ -0,0 +1,85 @@ +/* + * Copyright 2021 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"). + * You may not use this file except in compliance with the License. + * A copy of the License is located at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * or in the "license" file accompanying this file. This file is distributed + * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either + * express or implied. See the License for the specific language governing + * permissions and limitations under the License. + */ + +import {TransformMetadata} from "../../../../models/interfaces"; +import React from "react"; +import { EuiFlexGroup, EuiFlexItem, EuiIcon, EuiText } from "@elastic/eui"; + +// TODO: merge with rollup helper to have a common helper +export const renderStatus = (metadata: TransformMetadata | undefined): JSX.Element => { + if (metadata == null || metadata.transform_metadata == null) return
-
; + let icon; + let iconColor; + let textColor: "default" | "subdued" | "secondary" | "ghost" | "accent" | "warning" | "danger" | undefined; + let text; + switch (metadata.transform_metadata.status) { + case "failed": + icon = "alert"; + iconColor = "danger"; + textColor = "danger"; + text = "Failed: " + metadata.transform_metadata.failure_reason; + break; + case "finished": + icon = "check"; + iconColor = "success"; + textColor = "secondary"; + text = "Complete"; + break; + case "init": + return ( + + + + + + + Initializing + + + + ); + case "started": + icon = "play"; + iconColor = "success"; + textColor = "secondary"; + text = "Started"; + break; + case "stopped": + icon = "stop"; + iconColor = "subdued"; + textColor = "subdued"; + text = "Stopped"; + break; + default: + return
-
; + } + + return ( + + + + + + + {text} + + + + ); +}; + +export const renderEnabled = (isEnabled: boolean): string => { + return isEnabled ? "Enabled" : "Disabled"; +}; diff --git a/public/services/index.ts b/public/services/index.ts index b1402f8f4..ecd048210 100644 --- a/public/services/index.ts +++ b/public/services/index.ts @@ -18,5 +18,8 @@ import IndexService from "./IndexService"; import ManagedIndexService from "./ManagedIndexService"; import PolicyService from "./PolicyService"; import RollupService from "./RollupService"; +import TransformService from "./TransformService"; -export { ServicesConsumer, ServicesContext, IndexService, ManagedIndexService, PolicyService, RollupService }; +export { + ServicesConsumer, ServicesContext, IndexService, ManagedIndexService, PolicyService, RollupService, TransformService +}; From 897c73f41b116df777bf6c8819aebf8a5f58546d Mon Sep 17 00:00:00 2001 From: Eric Lobdell Date: Tue, 27 Apr 2021 10:55:36 -0700 Subject: [PATCH 05/93] Initial draft commit for CreateTransform UI --- .../ConfigureTransform/ConfigureTransform.tsx | 56 +++ .../components/ConfigureTransform/index.ts | 18 + .../CreateTransformSteps.tsx | 51 +++ .../components/CreateTransformSteps/index.ts | 18 + .../JobNameAndIndices/JobNameAndIndices.tsx | 93 ++++ .../components/JobNameAndIndices/index.ts | 18 + .../components/Schedule/Schedule.tsx | 95 ++++ .../components/Schedule/index.ts | 18 + .../TransformIndices/TransformIndices.tsx | 175 ++++++++ .../components/TransformIndices/index.ts | 18 + .../CreateTransform/CreateTransform.tsx | 73 ++++ .../containers/CreateTransform/index.ts | 18 + .../CreateTransformForm.tsx | 411 ++++++++++++++++++ .../containers/CreateTransformForm/index.ts | 18 + .../CreateTransformStep2.tsx | 65 +++ .../containers/CreateTransformStep2/index.ts | 18 + .../CreateTransformStep3.tsx | 146 +++++++ .../containers/CreateTransformStep3/index.ts | 18 + .../CreateTransformStep4.tsx | 84 ++++ .../containers/CreateTransformStep4/index.ts | 18 + public/pages/CreateTransform/index.ts | 18 + .../pages/CreateTransform/utils/constants.ts | 88 ++++ public/pages/CreateTransform/utils/helpers.ts | 63 +++ public/pages/Main/Main.tsx | 3 +- .../containers/Transforms/Transforms.tsx | 3 +- 25 files changed, 1601 insertions(+), 3 deletions(-) create mode 100644 public/pages/CreateTransform/components/ConfigureTransform/ConfigureTransform.tsx create mode 100644 public/pages/CreateTransform/components/ConfigureTransform/index.ts create mode 100644 public/pages/CreateTransform/components/CreateTransformSteps/CreateTransformSteps.tsx create mode 100644 public/pages/CreateTransform/components/CreateTransformSteps/index.ts create mode 100644 public/pages/CreateTransform/components/JobNameAndIndices/JobNameAndIndices.tsx create mode 100644 public/pages/CreateTransform/components/JobNameAndIndices/index.ts create mode 100644 public/pages/CreateTransform/components/Schedule/Schedule.tsx create mode 100644 public/pages/CreateTransform/components/Schedule/index.ts create mode 100644 public/pages/CreateTransform/components/TransformIndices/TransformIndices.tsx create mode 100644 public/pages/CreateTransform/components/TransformIndices/index.ts create mode 100644 public/pages/CreateTransform/containers/CreateTransform/CreateTransform.tsx create mode 100644 public/pages/CreateTransform/containers/CreateTransform/index.ts create mode 100644 public/pages/CreateTransform/containers/CreateTransformForm/CreateTransformForm.tsx create mode 100644 public/pages/CreateTransform/containers/CreateTransformForm/index.ts create mode 100644 public/pages/CreateTransform/containers/CreateTransformStep2/CreateTransformStep2.tsx create mode 100644 public/pages/CreateTransform/containers/CreateTransformStep2/index.ts create mode 100644 public/pages/CreateTransform/containers/CreateTransformStep3/CreateTransformStep3.tsx create mode 100644 public/pages/CreateTransform/containers/CreateTransformStep3/index.ts create mode 100644 public/pages/CreateTransform/containers/CreateTransformStep4/CreateTransformStep4.tsx create mode 100644 public/pages/CreateTransform/containers/CreateTransformStep4/index.ts create mode 100644 public/pages/CreateTransform/index.ts create mode 100644 public/pages/CreateTransform/utils/constants.ts create mode 100644 public/pages/CreateTransform/utils/helpers.ts diff --git a/public/pages/CreateTransform/components/ConfigureTransform/ConfigureTransform.tsx b/public/pages/CreateTransform/components/ConfigureTransform/ConfigureTransform.tsx new file mode 100644 index 000000000..6a1fa6732 --- /dev/null +++ b/public/pages/CreateTransform/components/ConfigureTransform/ConfigureTransform.tsx @@ -0,0 +1,56 @@ +/* + * Copyright 2021 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"). + * You may not use this file except in compliance with the License. + * A copy of the License is located at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * or in the "license" file accompanying this file. This file is distributed + * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either + * express or implied. See the License for the specific language governing + * permissions and limitations under the License. + */ + +import React, { ChangeEvent } from "react"; +import { EuiSpacer, EuiFormRow, EuiFieldText, EuiTextArea, EuiText, EuiFlexGroup, EuiFlexItem } from "@elastic/eui"; +import { ContentPanel } from "../../../../components/ContentPanel"; + +interface ConfigureTransformProps { + isEdit: boolean; + transformId: string; + transformIdError: string; + onChangeName: (value: ChangeEvent) => void; + onChangeDescription: (value: ChangeEvent) => void; + description: string; +} + +const ConfigureTransform = ({ isEdit, transformId, transformIdError, onChangeName, onChangeDescription, description }: ConfigureTransformProps) => ( + +
+ + + + + + + + +

Description

+
+
+ + + - optional + + +
+ + + + +
+
+); +export default ConfigureTransform; diff --git a/public/pages/CreateTransform/components/ConfigureTransform/index.ts b/public/pages/CreateTransform/components/ConfigureTransform/index.ts new file mode 100644 index 000000000..b935263fd --- /dev/null +++ b/public/pages/CreateTransform/components/ConfigureTransform/index.ts @@ -0,0 +1,18 @@ +/* + * Copyright 2021 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"). + * You may not use this file except in compliance with the License. + * A copy of the License is located at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * or in the "license" file accompanying this file. This file is distributed + * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either + * express or implied. See the License for the specific language governing + * permissions and limitations under the License. + */ + +import ConfigureTransform from "./ConfigureTransform"; + +export default ConfigureTransform; diff --git a/public/pages/CreateTransform/components/CreateTransformSteps/CreateTransformSteps.tsx b/public/pages/CreateTransform/components/CreateTransformSteps/CreateTransformSteps.tsx new file mode 100644 index 000000000..02bdc136b --- /dev/null +++ b/public/pages/CreateTransform/components/CreateTransformSteps/CreateTransformSteps.tsx @@ -0,0 +1,51 @@ +/* + * Copyright 2021 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"). + * You may not use this file except in compliance with the License. + * A copy of the License is located at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * or in the "license" file accompanying this file. This file is distributed + * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either + * express or implied. See the License for the specific language governing + * permissions and limitations under the License. + */ +import React from "react"; +import { EuiSteps } from "@elastic/eui"; + +interface CreateTransformStepsProps { + step: number; +} + +const setOfSteps = (step: number) => { + return [ + { + title: "Set up indices", + children: null, + }, + { + title: "Define aggregations and metrics", + children: null, + status: step < 2 ? "disabled" : null, + }, + { + title: "Specify schedule", + children: null, + status: step < 3 ? "disabled" : null, + }, + { + title: "Review and create", + children: null, + status: step < 4 ? "disabled" : null, + }, + ]; +}; +const CreateTransformSteps = ({ step }: CreateTransformStepsProps) => ( +
+ +
+); + +export default CreateTransformSteps; diff --git a/public/pages/CreateTransform/components/CreateTransformSteps/index.ts b/public/pages/CreateTransform/components/CreateTransformSteps/index.ts new file mode 100644 index 000000000..fe481f23a --- /dev/null +++ b/public/pages/CreateTransform/components/CreateTransformSteps/index.ts @@ -0,0 +1,18 @@ +/* + * Copyright 2021 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"). + * You may not use this file except in compliance with the License. + * A copy of the License is located at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * or in the "license" file accompanying this file. This file is distributed + * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either + * express or implied. See the License for the specific language governing + * permissions and limitations under the License. + */ + +import CreateTransformSteps from "./CreateTransformSteps"; + +export default CreateTransformSteps; diff --git a/public/pages/CreateTransform/components/JobNameAndIndices/JobNameAndIndices.tsx b/public/pages/CreateTransform/components/JobNameAndIndices/JobNameAndIndices.tsx new file mode 100644 index 000000000..e2e01d3dd --- /dev/null +++ b/public/pages/CreateTransform/components/JobNameAndIndices/JobNameAndIndices.tsx @@ -0,0 +1,93 @@ +/* + * Copyright 2021 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"). + * You may not use this file except in compliance with the License. + * A copy of the License is located at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * or in the "license" file accompanying this file. This file is distributed + * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either + * express or implied. See the License for the specific language governing + * permissions and limitations under the License. + */ + +import React, { Component } from "react"; +import { EuiFlexGrid, EuiSpacer, EuiFlexItem, EuiText } from "@elastic/eui"; +import { ContentPanel, ContentPanelActions } from "../../../../components/ContentPanel"; +import { ModalConsumer } from "../../../../components/Modal"; +import { IndexItem } from "../../../../../models/interfaces"; + +interface JobNameAndIndicesProps { + transformId: string; + description: string; + sourceIndex: { label: string; value?: IndexItem }[]; + targetIndex: { label: string; value?: IndexItem }[]; + onChangeStep: (step: number) => void; +} + +export default class JobNameAndIndices extends Component { + constructor(props: JobNameAndIndicesProps) { + super(props); + } + + render() { + const { transformId, description, onChangeStep, sourceIndex, targetIndex } = this.props; + + return ( + + {() => ( + onChangeStep(1), + }, + }, + ]} + /> + )} + + } + bodyStyles={{ padding: "initial" }} + title="Job name and indices" + titleSize="m" + > +
+ + + + +
Name
+
{transformId}
+
+
+ + +
Source Index
+
{sourceIndex[0].label}
+
+
+ + +
Target index
+
{targetIndex[0].label}
+
+
+ + +
Description
+
{description == "" ? "-" : description}
+
+
+
+ +
+
+ ); + } +} diff --git a/public/pages/CreateTransform/components/JobNameAndIndices/index.ts b/public/pages/CreateTransform/components/JobNameAndIndices/index.ts new file mode 100644 index 000000000..aa4b17446 --- /dev/null +++ b/public/pages/CreateTransform/components/JobNameAndIndices/index.ts @@ -0,0 +1,18 @@ +/* + * Copyright 2021 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"). + * You may not use this file except in compliance with the License. + * A copy of the License is located at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * or in the "license" file accompanying this file. This file is distributed + * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either + * express or implied. See the License for the specific language governing + * permissions and limitations under the License. + */ + +import JobNameAndIndices from "./JobNameAndIndices"; + +export default JobNameAndIndices; diff --git a/public/pages/CreateTransform/components/Schedule/Schedule.tsx b/public/pages/CreateTransform/components/Schedule/Schedule.tsx new file mode 100644 index 000000000..9a8e45021 --- /dev/null +++ b/public/pages/CreateTransform/components/Schedule/Schedule.tsx @@ -0,0 +1,95 @@ +/* + * Copyright 2020 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"). + * You may not use this file except in compliance with the License. + * A copy of the License is located at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * or in the "license" file accompanying this file. This file is distributed + * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either + * express or implied. See the License for the specific language governing + * permissions and limitations under the License. + */ + +import React, { ChangeEvent, Component } from "react"; +import moment from "moment-timezone"; +import { + EuiSpacer, + EuiCheckbox, + EuiRadioGroup, + EuiFormRow, + EuiSelect, + EuiFieldNumber, + EuiFlexGroup, + EuiFlexItem, + EuiTextArea, + EuiFormHelpText, + EuiText, +} from "@elastic/eui"; +import { DelayTimeunitOptions, ScheduleIntervalTimeunitOptions } from "../../utils/constants"; +import { ContentPanel } from "../../../../components/ContentPanel"; + +interface ScheduleProps { + isEdit: boolean; + transformId: string; + transformIdError: string; + jobEnabledByDefault: boolean; + pageSize: number; + onChangeJobEnabledByDefault: () => void; + onChangePage: (e: ChangeEvent) => void; +} + +const radios = [ + { + id: "no", + label: "No", + }, + { + id: "yes", + label: "Yes", + }, +]; + +const timezones = moment.tz.names().map((tz) => ({ label: tz, text: tz })); + +export default class Schedule extends Component { + constructor(props: ScheduleProps) { + super(props); + } + + render() { + const { + isEdit, + jobEnabledByDefault, + pageSize, + onChangeJobEnabledByDefault, + onChangePage, + } = this.props; + return ( + +
+ {!isEdit && ( + + )} + + + + + + +
+
+ ); + } +} diff --git a/public/pages/CreateTransform/components/Schedule/index.ts b/public/pages/CreateTransform/components/Schedule/index.ts new file mode 100644 index 000000000..f18d83cb2 --- /dev/null +++ b/public/pages/CreateTransform/components/Schedule/index.ts @@ -0,0 +1,18 @@ +/* + * Copyright 2021 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"). + * You may not use this file except in compliance with the License. + * A copy of the License is located at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * or in the "license" file accompanying this file. This file is distributed + * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either + * express or implied. See the License for the specific language governing + * permissions and limitations under the License. + */ + +import Schedule from "./Schedule"; + +export default Schedule; diff --git a/public/pages/CreateTransform/components/TransformIndices/TransformIndices.tsx b/public/pages/CreateTransform/components/TransformIndices/TransformIndices.tsx new file mode 100644 index 000000000..2b0abb1f0 --- /dev/null +++ b/public/pages/CreateTransform/components/TransformIndices/TransformIndices.tsx @@ -0,0 +1,175 @@ +/* + * Copyright 2021 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"). + * You may not use this file except in compliance with the License. + * A copy of the License is located at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * or in the "license" file accompanying this file. This file is distributed + * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either + * express or implied. See the License for the specific language governing + * permissions and limitations under the License. + */ + +import React, { Component, Fragment } from "react"; +import { EuiSpacer, EuiFormRow, EuiComboBox, EuiCallOut } from "@elastic/eui"; +import { ContentPanel } from "../../../../components/ContentPanel"; +import { EuiComboBoxOptionOption } from "@elastic/eui/src/components/combo_box/types"; +import { IndexItem } from "../../../../../models/interfaces"; +import IndexService from "../../../../services/IndexService"; +import _ from "lodash"; +import { CoreServicesContext } from "../../../../components/core_services"; + +interface TransformIndicesProps { + indexService: IndexService; + sourceIndex: { label: string; value?: IndexItem }[]; + sourceIndexError: string; + targetIndex: { label: string; value?: IndexItem }[]; + targetIndexError: string; + onChangeSourceIndex: (options: EuiComboBoxOptionOption[]) => void; + onChangeTargetIndex: (options: EuiComboBoxOptionOption[]) => void; + hasAggregation: boolean; +} + +interface TransformIndicesState { + isLoading: boolean; + indexOptions: { label: string; value?: IndexItem }[]; + targetIndexOptions: { label: string; value?: IndexItem }[]; +} + +export default class TransformIndices extends Component { + static contextType = CoreServicesContext; + constructor(props: TransformIndicesProps) { + super(props); + this.state = { + isLoading: true, + indexOptions: [], + targetIndexOptions: [], + }; + + this.onIndexSearchChange = _.debounce(this.onIndexSearchChange, 500, { leading: true }); + } + + async componentDidMount(): Promise { + await this.onIndexSearchChange(""); + } + + onIndexSearchChange = async (searchValue: string): Promise => { + const { indexService } = this.props; + this.setState({ isLoading: true, indexOptions: [] }); + try { + const queryObject = { from: 0, size: 10, search: searchValue, sortDirection: "desc", sortField: "index" }; + const getIndicesResponse = await indexService.getIndices(queryObject); + if (getIndicesResponse.ok) { + const options = searchValue.trim() ? [{ label: `${searchValue}*` }] : []; + const indices = getIndicesResponse.response.indices.map((index: IndexItem) => ({ + label: index.index, + })); + this.setState({ indexOptions: options.concat(indices), targetIndexOptions: indices }); + } else { + if (getIndicesResponse.error.startsWith("[index_not_found_exception]")) { + this.context.notifications.toasts.addDanger("No index available"); + } else { + this.context.notifications.toasts.addDanger(getIndicesResponse.error); + } + } + } catch (err) { + this.context.notifications.toasts.addDanger(err.message); + } + + this.setState({ isLoading: false }); + }; + + onCreateOption = (searchValue: string, flattenedOptions: { label: string; value?: IndexItem }[]): void => { + const { targetIndexOptions } = this.state; + const { onChangeTargetIndex } = this.props; + const normalizedSearchValue = searchValue.trim(); + + if (!normalizedSearchValue) { + return; + } + + const newOption = { + label: searchValue, + }; + + // Create the option if it doesn't exist. + if (flattenedOptions.findIndex((option) => option.label.trim() === normalizedSearchValue) === -1) { + targetIndexOptions.concat(newOption); + this.setState({ targetIndexOptions: targetIndexOptions }); + } + onChangeTargetIndex([newOption]); + }; + + render() { + const { + sourceIndex, + sourceIndexError, + targetIndex, + targetIndexError, + onChangeSourceIndex, + onChangeTargetIndex, + hasAggregation, + } = this.props; + const { isLoading, indexOptions, targetIndexOptions } = this.state; + return ( + +
+ + +

You can't change indices after creating a job. Double-check the source and target index names before proceeding.

+
+ {hasAggregation && ( + + + +

Note: changing source index will erase all existing definitions about aggregations and metrics.

+
+
+ )} + + + + + + + + +
+
+ ); + } +} diff --git a/public/pages/CreateTransform/components/TransformIndices/index.ts b/public/pages/CreateTransform/components/TransformIndices/index.ts new file mode 100644 index 000000000..ffee9e56f --- /dev/null +++ b/public/pages/CreateTransform/components/TransformIndices/index.ts @@ -0,0 +1,18 @@ +/* + * Copyright 2021 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"). + * You may not use this file except in compliance with the License. + * A copy of the License is located at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * or in the "license" file accompanying this file. This file is distributed + * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either + * express or implied. See the License for the specific language governing + * permissions and limitations under the License. + */ + +import TransformIndices from "./TransformIndices"; + +export default TransformIndices; diff --git a/public/pages/CreateTransform/containers/CreateTransform/CreateTransform.tsx b/public/pages/CreateTransform/containers/CreateTransform/CreateTransform.tsx new file mode 100644 index 000000000..05b223724 --- /dev/null +++ b/public/pages/CreateTransform/containers/CreateTransform/CreateTransform.tsx @@ -0,0 +1,73 @@ +/* + * Copyright 2021 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"). + * You may not use this file except in compliance with the License. + * A copy of the License is located at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * or in the "license" file accompanying this file. This file is distributed + * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either + * express or implied. See the License for the specific language governing + * permissions and limitations under the License. + */ + +import React, { ChangeEvent, Component } from "react"; +import { EuiSpacer, EuiTitle, EuiFlexGroup, EuiFlexItem, EuiComboBoxOptionOption } from "@elastic/eui"; +import { RouteComponentProps } from "react-router-dom"; +import { TransformService } from "../../../../services"; +import ConfigureTransform from "../../components/ConfigureTransform"; +import TransformIndices from "../../components/TransformIndices"; +import CreateTransformSteps from "../../components/CreateTransformSteps"; +import IndexService from "../../../../services/IndexService"; +import { IndexItem } from "../../../../../models/interfaces"; + +interface CreateTransformProps extends RouteComponentProps { + transformService: TransformService; + indexService: IndexService; + transformId: string; + transformIdError: string; + submitError: string; + isSubmitting: boolean; + hasSubmitted: boolean; + description: string; + sourceIndex: { label: string; value?: IndexItem }[]; + sourceIndexError: string; + targetIndex: { label: string; value?: IndexItem }[]; + targetIndexError: string; + onChangeName: (e: ChangeEvent) => void; + onChangeDescription: (value: ChangeEvent) => void; + onChangeSourceIndex: (options: EuiComboBoxOptionOption[]) => void; + onChangeTargetIndex: (options: EuiComboBoxOptionOption[]) => void; + currentStep: number; + hasAggregation: boolean; +} + +export default class CreateTransform extends Component { + render() { + if (this.props.currentStep !== 1) { + return null; + } + + return ( +
+ + + + + + +

Set up indices

+
+ + + + +
+
+ +
+ ); + } +} diff --git a/public/pages/CreateTransform/containers/CreateTransform/index.ts b/public/pages/CreateTransform/containers/CreateTransform/index.ts new file mode 100644 index 000000000..b01f74a8a --- /dev/null +++ b/public/pages/CreateTransform/containers/CreateTransform/index.ts @@ -0,0 +1,18 @@ +/* + * Copyright 2021 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"). + * You may not use this file except in compliance with the License. + * A copy of the License is located at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * or in the "license" file accompanying this file. This file is distributed + * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either + * express or implied. See the License for the specific language governing + * permissions and limitations under the License. + */ + +import CreateTransform from "./CreateTransform"; + +export default CreateTransform; diff --git a/public/pages/CreateTransform/containers/CreateTransformForm/CreateTransformForm.tsx b/public/pages/CreateTransform/containers/CreateTransformForm/CreateTransformForm.tsx new file mode 100644 index 000000000..16c135c2c --- /dev/null +++ b/public/pages/CreateTransform/containers/CreateTransformForm/CreateTransformForm.tsx @@ -0,0 +1,411 @@ +/* + * Copyright 2021 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"). + * You may not use this file except in compliance with the License. + * A copy of the License is located at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * or in the "license" file accompanying this file. This file is distributed + * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either + * express or implied. See the License for the specific language governing + * permissions and limitations under the License. + */ + +import React, { ChangeEvent, Component } from "react"; +import { EuiButton, EuiButtonEmpty, EuiComboBoxOptionOption, EuiFlexGroup, EuiFlexItem } from "@elastic/eui"; +import { RouteComponentProps } from "react-router-dom"; +import moment from "moment"; +import { TransformService } from "../../../../services"; +import { BREADCRUMBS, ROUTES } from "../../../../utils/constants"; +import IndexService from "../../../../services/IndexService"; +import { ManagedCatIndex } from "../../../../../server/models/interfaces"; +import CreateTransform from "../CreateTransform"; +import CreateTransformStep2 from "../CreateTransformStep2"; +import { DimensionItem, FieldItem, IndexItem, MetricItem, Transform } from "../../../../../models/interfaces"; +import { getErrorMessage } from "../../../../utils/helpers"; +import { EMPTY_TRANSFORM } from "../../utils/constants"; +import CreateTransformStep3 from "../CreateTransformStep3"; +import CreateTransformStep4 from "../CreateTransformStep4"; +import { compareFieldItem, parseFieldOptions } from "../../utils/helpers"; +import { CoreServicesContext } from "../../../../components/core_services"; + +interface CreateTransformFormProps extends RouteComponentProps { + transformService: TransformService; + indexService: IndexService; +} + +interface CreateTransformFormState { + currentStep: number; + transformId: string; + transformIdError: string; + transformSeqNo: number | null; + transformPrimaryTerm: number | null; + submitError: string; + isSubmitting: boolean; + hasSubmitted: boolean; + loadingIndices: boolean; + indices: ManagedCatIndex[]; + totalIndices: number; + + description: string; + sourceIndex: { label: string; value?: IndexItem }[]; + sourceIndexError: string; + targetIndex: { label: string; value?: IndexItem }[]; + targetIndexError: string; + + mappings: any; + allMappings: FieldItem[][]; + fields: FieldItem[]; + selectedTerms: FieldItem[]; + selectedDimensionField: DimensionItem[]; + selectedMetrics: MetricItem[]; + metricError: string; + timestamp: EuiComboBoxOptionOption[]; + timestampError: string; + intervalType: string; + intervalValue: number; + timezone: string; + timeunit: string; + selectedFields: FieldItem[]; + jobEnabledByDefault: boolean; + + continuousJob: string; + continuousDefinition: string; + interval: number; + intervalError: string; + intervalTimeunit: string; + cronExpression: string; + cronTimezone: string; + pageSize: number; + delayTime: number | undefined; + delayTimeunit: string; + transformJSON: any; +} + +export default class CreateTransformForm extends Component { + static contextType = CoreServicesContext; + + constructor(props: CreateTransformFormProps) { + super(props); + + this.state = { + currentStep: 1, + transformSeqNo: null, + transformPrimaryTerm: null, + transformId: "", + transformIdError: "", + submitError: "", + isSubmitting: false, + hasSubmitted: false, + loadingIndices: true, + indices: [], + totalIndices: 0, + + mappings: "", + allMappings: [], + fields: [], + description: "", + + sourceIndex: [], + sourceIndexError: "", + targetIndex: [], + targetIndexError: "", + + jobEnabledByDefault: true, + pageSize: 1000, + transformJSON: JSON.parse(EMPTY_TRANSFORM), + }; + this._next = this._next.bind(this); + this._prev = this._prev.bind(this); + } + + componentDidMount = async (): Promise => { + this.context.chrome.setBreadcrumbs([BREADCRUMBS.INDEX_MANAGEMENT, BREADCRUMBS.TRANSFORMS, BREADCRUMBS.CREATE_TRANSFORM]); + }; + + getMappings = async (srcIndex: string): Promise => { + if (!srcIndex.length) return; + try { + const { transformService } = this.props; + const response = await transformService.getMappings(srcIndex); + if (response.ok) { + let allMappings: FieldItem[][] = []; + const mappings = response.response; + //Push mappings array to allMappings 2D array first + for (let index in mappings) { + allMappings.push(parseFieldOptions("", mappings[index].mappings.properties)); + } + //Find intersect from all mappings + const fields = allMappings.reduce((mappingA, mappingB) => + mappingA.filter((itemA) => mappingB.some((itemB) => compareFieldItem(itemA, itemB))) + ); + this.setState({ mappings, fields, allMappings }); + } else { + this.context.notifications.toasts.addDanger(`Could not load fields: ${response.error}`); + } + } catch (err) { + this.context.notifications.toasts.addDanger(getErrorMessage(err, "Could not load fields")); + } + }; + + _next() { + let currentStep = this.state.currentStep; + let error = false; + //Verification here + if (currentStep == 1) { + const { transformId, sourceIndex, targetIndex } = this.state; + + if (!transformId) { + this.setState({ submitError: "Job name is required.", transformIdError: "Job name is required." }); + error = true; + } + if (sourceIndex.length == 0) { + this.setState({ submitError: "Source index is required.", sourceIndexError: "Source index is required." }); + error = true; + } + if (targetIndex.length == 0) { + this.setState({ submitError: "Target index is required.", targetIndexError: "Target index is required." }); + error = true; + } + } else if (currentStep == 2) { + } else if (currentStep == 3) { + //Check if interval is a valid value and is specified. + const { intervalError, continuousDefinition } = this.state; + if (continuousDefinition == "fixed") { + if (intervalError != "") { + const intervalErrorMsg = "Interval value is required."; + this.setState({ submitError: intervalErrorMsg, intervalError: intervalErrorMsg }); + error = true; + } + } + } + + if (error) return; + + currentStep = currentStep >= 3 ? 4 : currentStep + 1; + + this.setState({ + submitError: "", + currentStep: currentStep, + }); + } + + _prev() { + let currentStep = this.state.currentStep ; + // If the current step is 2 or 3, then subtract one on "previous" button click + currentStep = currentStep <= 1 ? 1 : currentStep - 1; + this.setState({ + currentStep: currentStep, + }); + } + + onChangeStep = (step: number): void => { + if (step > 3) return; + this.setState({ + currentStep: step, + }); + }; + + onChangeDescription = (e: ChangeEvent): void => { + const description = e.target.value; + let newJSON = this.state.transformJSON; + newJSON.transform.description = description; + this.setState({ description: description, transformJSON: newJSON }); + }; + + onChangeName = (e: ChangeEvent): void => { + const transformId = e.target.value; + this.setState({ transformId, transformIdError: transformId ? "" : "Name is required" }); + }; + + onChangeSourceIndex = async (options: EuiComboBoxOptionOption[]): Promise => { + let newJSON = this.state.transformJSON; + let sourceIndex = options.map(function (option) { + return option.label; + }); + const sourceIndexError = sourceIndex.length ? "" : "Source index is required"; + const srcIndexText = sourceIndex.length ? sourceIndex[0] : ""; + newJSON.transform.source_index = srcIndexText; + this.setState({ sourceIndex: options, transformJSON: newJSON, sourceIndexError: sourceIndexError }); + this.setState({ + selectedDimensionField: [], + selectedMetrics: [], + }); + await this.getMappings(srcIndexText); + }; + + onChangeTargetIndex = (options: EuiComboBoxOptionOption[]): void => { + //Try to get label text from option from the only array element in options, if exists + let newJSON = this.state.transformJSON; + let targetIndex = options.map(function (option) { + return option.label; + }); + + const targetIndexError = targetIndex.length ? "" : "Target index is required"; + + newJSON.transform.target_index = targetIndex[0]; + this.setState({ targetIndex: options, transformJSON: newJSON, targetIndexError: targetIndexError }); + }; + + setDateHistogram = (): void => { + const { intervalType, intervalValue, timeunit } = this.state; + let newJSON = this.state.transformJSON; + if (intervalType == "calendar") { + newJSON.transform.dimensions[0].date_histogram.calendar_interval = `1${timeunit}`; + delete newJSON.transform.dimensions[0].date_histogram["fixed_interval"]; + } else { + newJSON.transform.dimensions[0].date_histogram.fixed_interval = `${intervalValue}${timeunit}`; + delete newJSON.transform.dimensions[0].date_histogram["calendar_interval"]; + } + this.setState({ transformJSON: newJSON }); + }; + + onChangeJobEnabledByDefault = (): void => { + const checked = this.state.jobEnabledByDefault; + let newJSON = this.state.transformJSON; + newJSON.transform.enabled = !checked; + this.setState({ jobEnabledByDefault: !checked, transformJSON: newJSON }); + }; + + onChangePage = (e: ChangeEvent): void => { + let newJSON = this.state.transformJSON; + newJSON.transform.page_size = e.target.valueAsNumber; + this.setState({ pageSize: e.target.valueAsNumber, transformJSON: newJSON }); + }; + + onSubmit = async (): Promise => { + const { transformId, transformJSON } = this.state; + this.setState({ submitError: "", isSubmitting: true, hasSubmitted: true }); + try { + if (!transformId) { + this.setState({ transformIdError: "Required" }); + } else { + this.setDateHistogram(); + await this.onCreate(transformId, transformJSON); + } + } catch (err) { + this.context.notifications.toasts.addDanger("Invalid Transform JSON"); + console.error(err); + } + + this.setState({ isSubmitting: false }); + }; + + onCancel = (): void => { + this.props.history.push(ROUTES.TRANSFORMS); + }; + + onCreate = async (transformId: string, transform: Transform): Promise => { + const { transformService } = this.props; + try { + const response = await transformService.putTransform(transform, transformId); + if (response.ok) { + this.context.notifications.toasts.addSuccess(`Created transform: ${response.response._id}`); + this.props.history.push(ROUTES.TRANSFORMS); + } else { + this.setState({ submitError: response.error }); + this.context.notifications.toasts.addDanger(`Failed to create transform: ${response.error}`); + } + } catch (err) { + this.setState({ submitError: getErrorMessage(err, "There was a problem creating the transform job") }); + this.context.notifications.toasts.addDanger( + `Failed to create transform: ${getErrorMessage(err, "There was a problem creating the transform job")}` + ); + } + }; + + render() { + const { + transformId, + transformIdError, + submitError, + isSubmitting, + hasSubmitted, + description, + sourceIndex, + sourceIndexError, + targetIndex, + targetIndexError, + currentStep, + + jobEnabledByDefault, + pageSize, + } = this.state; + return ( +
+ + + + + + + + Cancel + + + {currentStep != 1 && ( + + + Previous + + + )} + + {currentStep == 4 ? ( + + + Create + + + ) : ( + + + Next + + + )} + + + ); + } +} diff --git a/public/pages/CreateTransform/containers/CreateTransformForm/index.ts b/public/pages/CreateTransform/containers/CreateTransformForm/index.ts new file mode 100644 index 000000000..d933dd68a --- /dev/null +++ b/public/pages/CreateTransform/containers/CreateTransformForm/index.ts @@ -0,0 +1,18 @@ +/* + * Copyright 2021 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"). + * You may not use this file except in compliance with the License. + * A copy of the License is located at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * or in the "license" file accompanying this file. This file is distributed + * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either + * express or implied. See the License for the specific language governing + * permissions and limitations under the License. + */ + +import CreateTransformForm from "./CreateTransformForm"; + +export default CreateTransformForm; diff --git a/public/pages/CreateTransform/containers/CreateTransformStep2/CreateTransformStep2.tsx b/public/pages/CreateTransform/containers/CreateTransformStep2/CreateTransformStep2.tsx new file mode 100644 index 000000000..9ad07d434 --- /dev/null +++ b/public/pages/CreateTransform/containers/CreateTransformStep2/CreateTransformStep2.tsx @@ -0,0 +1,65 @@ +/* + * Copyright 2021 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"). + * You may not use this file except in compliance with the License. + * A copy of the License is located at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * or in the "license" file accompanying this file. This file is distributed + * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either + * express or implied. See the License for the specific language governing + * permissions and limitations under the License. + */ + +import React, { ChangeEvent, Component } from "react"; +import { EuiSpacer, EuiTitle, EuiFlexGroup, EuiFlexItem, EuiCallOut, EuiComboBoxOptionOption } from "@elastic/eui"; +import { RouteComponentProps } from "react-router-dom"; +import { TransformService } from "../../../../services"; +import { BREADCRUMBS, ROUTES } from "../../../../utils/constants"; +import CreateTransformSteps from "../../components/CreateTransformSteps"; +import { DimensionItem, FieldItem, MetricItem } from "../../../../../models/interfaces"; +import { CoreServicesContext } from "../../../../components/core_services"; + +interface CreateTransformStep2Props extends RouteComponentProps { + transformService: TransformService; + currentStep: number; +} + +export default class CreateTransformStep2 extends Component { + static contextType = CoreServicesContext; + constructor(props: CreateTransformStep2Props) { + super(props); + } + + componentDidMount = async (): Promise => { + this.context.chrome.setBreadcrumbs([BREADCRUMBS.INDEX_MANAGEMENT, BREADCRUMBS.TRANSFORMS]); + }; + + onCancel = (): void => { + this.props.history.push(ROUTES.TRANSFORMS); + }; + + render() { + if (this.props.currentStep !== 2) return null; + const { fields, timestamp } = this.props; + + return ( +
+ + + + + + +

Definition Placeholder

+
+ +
+
+ +
+ ); + } +} diff --git a/public/pages/CreateTransform/containers/CreateTransformStep2/index.ts b/public/pages/CreateTransform/containers/CreateTransformStep2/index.ts new file mode 100644 index 000000000..1b7f6dbd7 --- /dev/null +++ b/public/pages/CreateTransform/containers/CreateTransformStep2/index.ts @@ -0,0 +1,18 @@ +/* + * Copyright 2021 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"). + * You may not use this file except in compliance with the License. + * A copy of the License is located at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * or in the "license" file accompanying this file. This file is distributed + * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either + * express or implied. See the License for the specific language governing + * permissions and limitations under the License. + */ + +import CreateTransformStep2 from "./CreateTransformStep2"; + +export default CreateTransformStep2; diff --git a/public/pages/CreateTransform/containers/CreateTransformStep3/CreateTransformStep3.tsx b/public/pages/CreateTransform/containers/CreateTransformStep3/CreateTransformStep3.tsx new file mode 100644 index 000000000..c359130ef --- /dev/null +++ b/public/pages/CreateTransform/containers/CreateTransformStep3/CreateTransformStep3.tsx @@ -0,0 +1,146 @@ +/* + * Copyright 2020 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"). + * You may not use this file except in compliance with the License. + * A copy of the License is located at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * or in the "license" file accompanying this file. This file is distributed + * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either + * express or implied. See the License for the specific language governing + * permissions and limitations under the License. + */ + +import React, { ChangeEvent, Component } from "react"; +import { EuiSpacer, EuiTitle, EuiFlexGroup, EuiFlexItem } from "@elastic/eui"; +import { RouteComponentProps } from "react-router-dom"; +import { TransformService } from "../../../../services"; +import { BREADCRUMBS, ROUTES } from "../../../../utils/constants"; +import { getErrorMessage } from "../../../../utils/helpers"; +import { Transform } from "../../../../../models/interfaces"; +import CreateTransformSteps from "../../components/CreateTransformSteps"; +import Schedule from "../../components/Schedule"; +import { CoreServicesContext } from "../../../../components/core_services"; + +interface CreateTransformProps extends RouteComponentProps { + transformService: TransformService; + currentStep: number; + jobEnabledByDefault: boolean; + pageSize: number; + onChangeJobEnabledByDefault: () => void; + onChangePage: (e: ChangeEvent) => void; +} + +interface CreateTransformState { + transformId: string; + transformIdError: string; + transformSeqNo: number | null; + transformPrimaryTerm: number | null; + submitError: string; + isSubmitting: boolean; + hasSubmitted: boolean; +} + +export default class CreateTransformStep3 extends Component { + static contextType = CoreServicesContext; + constructor(props: CreateTransformProps) { + super(props); + + this.state = { + transformSeqNo: null, + transformPrimaryTerm: null, + transformId: "", + transformIdError: "", + submitError: "", + isSubmitting: false, + hasSubmitted: false, + }; + } + + componentDidMount = async (): Promise => { + this.context.chrome.setBreadcrumbs([BREADCRUMBS.INDEX_MANAGEMENT, BREADCRUMBS.TRANSFORMS]); + }; + + onCreate = async (transformId: string, transform: Transform): Promise => { + const { transformService } = this.props; + try { + const response = await transformService.putTransform(transform, transformId); + if (response.ok) { + this.context.notifications.toasts.addSuccess(`Created transform: ${response.response._id}`); + this.props.history.push(ROUTES.TRANSFORMS); + } else { + this.setState({ submitError: response.error }); + } + } catch (err) { + this.setState({ submitError: getErrorMessage(err, "There was a problem creating the transform") }); + } + }; + + onUpdate = async (transformId: string, transform: Transform): Promise => { + try { + const { transformService } = this.props; + const { transformPrimaryTerm, transformSeqNo } = this.state; + if (transformSeqNo == null || transformPrimaryTerm == null) { + this.context.notifications.toasts.addDanger("Could not update transform without seqNo and primaryTerm"); + return; + } + const response = await transformService.putTransform(transform, transformId, transformSeqNo, transformPrimaryTerm); + if (response.ok) { + this.context.notifications.toasts.addSuccess(`Updated transform: ${response.response._id}`); + this.props.history.push(ROUTES.TRANSFORMS); + } else { + this.setState({ submitError: response.error }); + } + } catch (err) { + this.setState({ submitError: getErrorMessage(err, "There was a problem updating the transform") }); + } + }; + + onCancel = (): void => { + this.props.history.push(ROUTES.TRANSFORMS); + }; + + onChange = (e: ChangeEvent): void => { + const { hasSubmitted } = this.state; + const transformId = e.target.value; + if (hasSubmitted) this.setState({ transformId, transformIdError: transformId ? "" : "Required" }); + else this.setState({ transformId }); + }; + + render() { + if (this.props.currentStep != 3) return null; + const { + jobEnabledByDefault, + pageSize, + onChangePage, + } = this.props; + const { transformId, transformIdError } = this.state; + return ( +
+ + + + + + +

Specify schedule

+
+ + +
+
+ +
+ ); + } +} diff --git a/public/pages/CreateTransform/containers/CreateTransformStep3/index.ts b/public/pages/CreateTransform/containers/CreateTransformStep3/index.ts new file mode 100644 index 000000000..c2626fe72 --- /dev/null +++ b/public/pages/CreateTransform/containers/CreateTransformStep3/index.ts @@ -0,0 +1,18 @@ +/* + * Copyright 2020 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"). + * You may not use this file except in compliance with the License. + * A copy of the License is located at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * or in the "license" file accompanying this file. This file is distributed + * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either + * express or implied. See the License for the specific language governing + * permissions and limitations under the License. + */ + +import CreateTransformStep3 from "./CreateTransformStep3"; + +export default CreateTransformStep3; diff --git a/public/pages/CreateTransform/containers/CreateTransformStep4/CreateTransformStep4.tsx b/public/pages/CreateTransform/containers/CreateTransformStep4/CreateTransformStep4.tsx new file mode 100644 index 000000000..328533609 --- /dev/null +++ b/public/pages/CreateTransform/containers/CreateTransformStep4/CreateTransformStep4.tsx @@ -0,0 +1,84 @@ +/* + * Copyright 2021 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"). + * You may not use this file except in compliance with the License. + * A copy of the License is located at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * or in the "license" file accompanying this file. This file is distributed + * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either + * express or implied. See the License for the specific language governing + * permissions and limitations under the License. + */ + +import React, { Component } from "react"; +import { EuiSpacer, EuiTitle, EuiFlexGroup, EuiFlexItem, EuiComboBoxOptionOption, EuiCallOut } from "@elastic/eui"; +import { RouteComponentProps } from "react-router-dom"; +import { TransformService } from "../../../../services"; +import { BREADCRUMBS, ROUTES } from "../../../../utils/constants"; +import { DimensionItem, IndexItem, MetricItem } from "../../../../../models/interfaces"; +import CreateTransformSteps from "../../components/CreateTransformSteps"; +import JobNameAndIndices from "../../components/JobNameAndIndices"; +import { CoreServicesContext } from "../../../../components/core_services"; + +interface CreateTransformProps extends RouteComponentProps { + transformService: TransformService; + submitError: string; + currentStep: number; + onChangeStep: (step: number) => void; + transformId: string; + description: string; + sourceIndex: { label: string; value?: IndexItem }[]; + targetIndex: { label: string; value?: IndexItem }[]; + + timestamp: EuiComboBoxOptionOption[]; + timezone: string; + timeunit: string; + + jobEnabledByDefault: boolean; + pageSize: number; +} + +export default class CreateTransformStep4 extends Component { + static contextType = CoreServicesContext; + constructor(props: CreateTransformProps) { + super(props); + } + + componentDidMount = async (): Promise => { + this.context.chrome.setBreadcrumbs([BREADCRUMBS.INDEX_MANAGEMENT, BREADCRUMBS.TRANSFORMS]); + }; + + onCancel = (): void => { + this.props.history.push(ROUTES.TRANSFORMS); + }; + + render() { + if (this.props.currentStep != 4) return null; + + return ( +
+ + + + + + +

Review and create

+
+ + + + + +

You can't change aggregations or metrics after creating a job. Double-check your choices before proceeding.

+
+
+
+ +
+ ); + } +} diff --git a/public/pages/CreateTransform/containers/CreateTransformStep4/index.ts b/public/pages/CreateTransform/containers/CreateTransformStep4/index.ts new file mode 100644 index 000000000..379985a8a --- /dev/null +++ b/public/pages/CreateTransform/containers/CreateTransformStep4/index.ts @@ -0,0 +1,18 @@ +/* + * Copyright 2021 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"). + * You may not use this file except in compliance with the License. + * A copy of the License is located at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * or in the "license" file accompanying this file. This file is distributed + * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either + * express or implied. See the License for the specific language governing + * permissions and limitations under the License. + */ + +import CreateTransformStep4 from "./CreateTransformStep4"; + +export default CreateTransformStep4; diff --git a/public/pages/CreateTransform/index.ts b/public/pages/CreateTransform/index.ts new file mode 100644 index 000000000..2ca67cce6 --- /dev/null +++ b/public/pages/CreateTransform/index.ts @@ -0,0 +1,18 @@ +/* + * Copyright 2021 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"). + * You may not use this file except in compliance with the License. + * A copy of the License is located at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * or in the "license" file accompanying this file. This file is distributed + * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either + * express or implied. See the License for the specific language governing + * permissions and limitations under the License. + */ + +import CreateTransform from "./containers/CreateTransform"; + +export default CreateTransform; diff --git a/public/pages/CreateTransform/utils/constants.ts b/public/pages/CreateTransform/utils/constants.ts new file mode 100644 index 000000000..008f1bf34 --- /dev/null +++ b/public/pages/CreateTransform/utils/constants.ts @@ -0,0 +1,88 @@ +/* + * Copyright 2021 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"). + * You may not use this file except in compliance with the License. + * A copy of the License is located at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * or in the "license" file accompanying this file. This file is distributed + * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either + * express or implied. See the License for the specific language governing + * permissions and limitations under the License. + */ + +export const EMPTY_TRANSFORM = JSON.stringify({ + transform: { + continuous: false, + description: "", + dimensions: [ + { + date_histogram: { + source_field: "", + fixed_interval: "1h", + timezone: "UTC", + }, + }, + ], + enabled: true, + metrics: [], + page_size: 1000, + roles: [], + schedule: { + interval: { + start_time: 234802, + period: 1, + unit: "MINUTES", + }, + }, + source_index: "", + target_index: "", + }, +}); + +export const FixedTimeunitOptions = [ + { value: "ms", text: "Millisecond(s)" }, + { value: "s", text: "Second(s)" }, + { value: "m", text: "Minute(s)" }, + { value: "h", text: "Hour(s)" }, + { value: "d", text: "Day(s)" }, +]; + +export const DelayTimeunitOptions = [ + { value: "SECONDS", text: "Second(s)" }, + { value: "MINUTES", text: "Minute(s)" }, + { value: "HOURS", text: "Hour(s)" }, + { value: "DAYS", text: "Day(s)" }, +]; + +export const CalendarTimeunitOptions = [ + { value: "m", text: "Minute" }, + { value: "h", text: "Hour" }, + { value: "d", text: "Day" }, + { value: "w", text: "Week" }, + { value: "M", text: "Month" }, + { value: "q", text: "Quarter" }, + { value: "y", text: "Year" }, +]; + +export const ScheduleIntervalTimeunitOptions = [ + { value: "MINUTES", text: "Minute(s)" }, + { value: "HOURS", text: "Hour(s)" }, + { value: "DAYS", text: "Day(s)" }, +]; + +export const AddFieldsColumns = [ + { + field: "label", + name: "Field name", + sortable: true, + }, + { + field: "type", + name: "Field type", + sortable: true, + render: (type: string | undefined) => (type == null || type == undefined ? "-" : type), + }, +]; diff --git a/public/pages/CreateTransform/utils/helpers.ts b/public/pages/CreateTransform/utils/helpers.ts new file mode 100644 index 000000000..8e66b67fc --- /dev/null +++ b/public/pages/CreateTransform/utils/helpers.ts @@ -0,0 +1,63 @@ +/* + * Copyright 2021 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"). + * You may not use this file except in compliance with the License. + * A copy of the License is located at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * or in the "license" file accompanying this file. This file is distributed + * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either + * express or implied. See the License for the specific language governing + * permissions and limitations under the License. + */ + +import { FieldItem } from "../../../../models/interfaces"; + +export const parseTimeunit = (timeunit: string): string => { + if (timeunit == "ms" || timeunit == "Milliseconds") return "millisecond(s)"; + else if (timeunit == "SECONDS" || timeunit == "s" || timeunit == "Seconds") return "second(s)"; + else if (timeunit == "MINUTES" || timeunit == "m" || timeunit == "Minutes") return "minute(s)"; + else if (timeunit == "HOURS" || timeunit == "h" || timeunit == "Hours") return "hour(s)"; + else if (timeunit == "DAYS" || timeunit == "d" || timeunit == "Days") return "day(s)"; + else if (timeunit == "w") return "week"; + else if (timeunit == "M") return "month"; + else if (timeunit == "q") return "quarter"; + else if (timeunit == "y") return "year"; + + return timeunit; +}; + +//Returns true if field type is numeric +export const isNumericMapping = (fieldType: string | undefined): boolean => { + return ( + fieldType == "long" || + fieldType == "integer" || + fieldType == "short" || + fieldType == "byte" || + fieldType == "double" || + fieldType == "float" || + fieldType == "half_float" || + fieldType == "scaled_float" + ); +}; + +export const compareFieldItem = (itemA: FieldItem, itemB: FieldItem): boolean => { + return itemB.label == itemA.label && itemA.type == itemB.type; +}; + +export const parseFieldOptions = (prefix: string, mappings: any): FieldItem[] => { + let fieldsOption: FieldItem[] = []; + for (let field in mappings) { + if (mappings.hasOwnProperty(field)) { + if (mappings[field].type != "object" && mappings[field].type != null && mappings[field].type != "nested") + fieldsOption.push({ label: prefix + field, type: mappings[field].type }); + if (mappings[field].fields != null) + fieldsOption = fieldsOption.concat(parseFieldOptions(prefix + field + ".", mappings[field].fields)); + if (mappings[field].properties != null) + fieldsOption = fieldsOption.concat(parseFieldOptions(prefix + field + ".", mappings[field].properties)); + } + } + return fieldsOption; +}; diff --git a/public/pages/Main/Main.tsx b/public/pages/Main/Main.tsx index 1f2f426e3..4f389e157 100644 --- a/public/pages/Main/Main.tsx +++ b/public/pages/Main/Main.tsx @@ -30,6 +30,7 @@ import { BrowserServices } from "../../models/interfaces"; import { ROUTES } from "../../utils/constants"; import { CoreServicesConsumer } from "../../components/core_services"; import CreateRollupForm from "../CreateRollup/containers/CreateRollupForm"; +import CreateTransformForm from "../CreateTransform/containers/CreateTransformForm"; import EditRollup from "../EditRollup/containers"; import RollupDetails from "../RollupDetails/containers/RollupDetails"; import Transforms from "../Transforms/containers/Transforms/Transforms"; @@ -205,7 +206,7 @@ export default class Main extends Component { path={ROUTES.CREATE_TRANSFORM} render={(props: RouteComponentProps) => (
- +
)} /> diff --git a/public/pages/Transforms/containers/Transforms/Transforms.tsx b/public/pages/Transforms/containers/Transforms/Transforms.tsx index 044cffc2d..211746a62 100644 --- a/public/pages/Transforms/containers/Transforms/Transforms.tsx +++ b/public/pages/Transforms/containers/Transforms/Transforms.tsx @@ -44,7 +44,7 @@ import { CoreServicesContext } from "../../../../components/core_services"; import {getURLQueryParams} from "../../utils/helpers"; import {TransformQueryParams} from "../../models/interfaces"; import {getErrorMessage} from "../../../../utils/helpers"; -import {ROUTES} from "../../../../utils/constants"; +import { BREADCRUMBS, ROUTES } from "../../../../utils/constants"; import DeleteModal from "../../components/DeleteModal"; import TransformEmptyPrompt from "../../components/TransformEmptyPrompt"; import {renderEnabled, renderStatus} from "../../utils/metadataHelper"; @@ -349,4 +349,3 @@ export default class Transforms extends Component Date: Tue, 27 Apr 2021 12:54:50 -0700 Subject: [PATCH 06/93] adding transform service routes and backend connection --- models/interfaces.ts | 2 +- public/index_management_app.tsx | 5 +- public/models/interfaces.ts | 3 +- .../containers/Transforms/Transforms.tsx | 7 +- public/services/TransformService.ts | 2 +- server/clusters/ism/ismPlugin.ts | 22 +++++ server/models/interfaces.ts | 4 +- server/plugin.ts | 8 +- server/routes/index.ts | 3 +- server/routes/transforms.ts | 39 ++++++++ server/services/TransformService.ts | 89 +++++++++++++++++++ server/services/index.ts | 3 +- server/utils/constants.ts | 2 + 13 files changed, 176 insertions(+), 13 deletions(-) create mode 100644 server/routes/transforms.ts create mode 100644 server/services/TransformService.ts diff --git a/models/interfaces.ts b/models/interfaces.ts index 5d122bcb8..aff6065b9 100644 --- a/models/interfaces.ts +++ b/models/interfaces.ts @@ -69,7 +69,7 @@ export interface DocumentRollup { export interface DocumentTransform { _id: string; - _seqNo: string; + _seqNo: number; _primaryTerm: number; transform: Transform; metadata: any; diff --git a/public/index_management_app.tsx b/public/index_management_app.tsx index ac4d71bce..d60dc5ffa 100644 --- a/public/index_management_app.tsx +++ b/public/index_management_app.tsx @@ -18,7 +18,7 @@ import React from "react"; import ReactDOM from "react-dom"; import { render, unmountComponentAtNode } from "react-dom"; import { HashRouter as Router, Route } from "react-router-dom"; -import { IndexService, ManagedIndexService, PolicyService, RollupService, ServicesContext } from "./services"; +import { IndexService, ManagedIndexService, PolicyService, RollupService, TransformService, ServicesContext } from "./services"; import { DarkModeContext } from "./components/DarkMode"; import Main from "./pages/Main"; import { CoreServicesContext } from "./components/core_services"; @@ -30,7 +30,8 @@ export function renderApp(coreStart: CoreStart, params: AppMountParameters) { const managedIndexService = new ManagedIndexService(http); const policyService = new PolicyService(http); const rollupService = new RollupService(http); - const services = { indexService, managedIndexService, policyService, rollupService }; + const transformService = new TransformService(http); + const services = { indexService, managedIndexService, policyService, rollupService, transformService }; const isDarkMode = coreStart.uiSettings.get("theme:darkMode") || false; diff --git a/public/models/interfaces.ts b/public/models/interfaces.ts index bf614f57c..83d84d071 100644 --- a/public/models/interfaces.ts +++ b/public/models/interfaces.ts @@ -13,8 +13,7 @@ * permissions and limitations under the License. */ -import { IndexService, ManagedIndexService, PolicyService, RollupService } from "../services"; -import TransformService from "../services/TransformService"; +import { IndexService, ManagedIndexService, PolicyService, RollupService, TransformService } from "../services"; export interface BrowserServices { indexService: IndexService; diff --git a/public/pages/Transforms/containers/Transforms/Transforms.tsx b/public/pages/Transforms/containers/Transforms/Transforms.tsx index 044cffc2d..3a291798e 100644 --- a/public/pages/Transforms/containers/Transforms/Transforms.tsx +++ b/public/pages/Transforms/containers/Transforms/Transforms.tsx @@ -44,7 +44,7 @@ import { CoreServicesContext } from "../../../../components/core_services"; import {getURLQueryParams} from "../../utils/helpers"; import {TransformQueryParams} from "../../models/interfaces"; import {getErrorMessage} from "../../../../utils/helpers"; -import {ROUTES} from "../../../../utils/constants"; +import {BREADCRUMBS, ROUTES} from "../../../../utils/constants"; import DeleteModal from "../../components/DeleteModal"; import TransformEmptyPrompt from "../../components/TransformEmptyPrompt"; import {renderEnabled, renderStatus} from "../../utils/metadataHelper"; @@ -95,6 +95,11 @@ export default class Transforms extends Component; } - + // TODO: implement preview transform } diff --git a/server/clusters/ism/ismPlugin.ts b/server/clusters/ism/ismPlugin.ts index e9e1acc96..8bcd7101c 100644 --- a/server/clusters/ism/ismPlugin.ts +++ b/server/clusters/ism/ismPlugin.ts @@ -270,4 +270,26 @@ export default function ismPlugin(Client: any, config: any, components: any) { }, method: "GET", }); + + // TODO: Add other transform APIs + + ism.getTransform = ca({ + url: { + fmt: `${API.TRANSFORM_BASE}/<%=transformId%>`, + req: { + transformId: { + type: "string", + required: true, + } + } + }, + method: "GET", + }); + + ism.getTransforms = ca({ + url: { + fmt: `${API.TRANSFORM_BASE}/`, + }, + method: "GET", + }); } diff --git a/server/models/interfaces.ts b/server/models/interfaces.ts index b7e3a497c..636e9e2de 100644 --- a/server/models/interfaces.ts +++ b/server/models/interfaces.ts @@ -13,7 +13,7 @@ * permissions and limitations under the License. */ -import { IndexService, ManagedIndexService, PolicyService, RollupService } from "../services"; +import { IndexService, ManagedIndexService, PolicyService, RollupService, TransformService } from "../services"; import { DocumentPolicy, DocumentRollup, @@ -28,6 +28,7 @@ export interface NodeServices { managedIndexService: ManagedIndexService; policyService: PolicyService; rollupService: RollupService; + transformService: TransformService; } export interface SearchResponse { @@ -236,6 +237,7 @@ export interface IndexManagementApi { readonly REMOVE_POLICY_BASE: string; readonly CHANGE_POLICY_BASE: string; readonly ROLLUP_JOBS_BASE: string; + readonly TRANSFORM_BASE: string; } export interface DefaultHeaders { diff --git a/server/plugin.ts b/server/plugin.ts index b29558d83..fa94579a1 100644 --- a/server/plugin.ts +++ b/server/plugin.ts @@ -16,8 +16,8 @@ import { IndexManagementPluginSetup, IndexManagementPluginStart } from "."; import { Plugin, CoreSetup, CoreStart, IClusterClient } from "../../../src/core/server"; import ismPlugin from "./clusters/ism/ismPlugin"; -import { PolicyService, ManagedIndexService, IndexService, RollupService } from "./services"; -import { indices, policies, managedIndices, rollups } from "../server/routes"; +import { PolicyService, ManagedIndexService, IndexService, RollupService, TransformService } from "./services"; +import { indices, policies, managedIndices, rollups, transforms } from "../server/routes"; export class IndexPatternManagementPlugin implements Plugin { public async setup(core: CoreSetup) { @@ -31,7 +31,8 @@ export class IndexPatternManagementPlugin implements Plugin>> => { + try { + const { from, size, search, sortDirection, sortField } = request.query as { + from: number; + size: number; + search: string; + sortDirection: string; + sortField: string; + }; + + const transformSortMap: { [key: string]: string } = { + "_id": "transform.transform_id.keyword", + "transform.source_index": "transform.source_index.keyword", + "transform.target_index": "transform.target_index.keyword", + "transform.transform.enabled": "transform.enabled", + }; + + // TODO: Correct the parsing + const params = { + // from: parseInt(from, 10), + // size: parseInt(size, 10), + // search, + // sortField: transformSortMap[sortField] || transformSortMap._id, + // sortDirection, + }; + + const { callAsCurrentUser: callWithRequest } = this.esDriver.asScoped(request); + const getTransformsResponse = await callWithRequest("ism.getTransforms", params); + const totalTransforms = getTransformsResponse.total_transforms; + const transforms = getTransformsResponse.transforms.map((transform: DocumentTransform) => ({ + seqNo: transform._seqNo as number, + primaryTerm: transform._primaryTerm as number, + id: transform._id, + transform: transform.transform, + metadata: null + })); + + return response.custom({ + statusCode: 200, + body: {ok: true, response: {transforms: transforms, totalTransforms: totalTransforms, metadata: {}}}, + }); + } catch (err) { + return response.custom({ + statusCode: 200, + body: { + ok: false, + error: "Error in getTransforms" + err.message, + } + }) + } + }; +} diff --git a/server/services/index.ts b/server/services/index.ts index a503f77c4..a7ca6d701 100644 --- a/server/services/index.ts +++ b/server/services/index.ts @@ -17,5 +17,6 @@ import IndexService from "./IndexService"; import PolicyService from "./PolicyService"; import ManagedIndexService from "./ManagedIndexService"; import RollupService from "./RollupService"; +import TransformService from "./TransformService"; -export { IndexService, PolicyService, ManagedIndexService, RollupService }; +export { IndexService, PolicyService, ManagedIndexService, RollupService, TransformService }; diff --git a/server/utils/constants.ts b/server/utils/constants.ts index d950b4719..b8f156402 100644 --- a/server/utils/constants.ts +++ b/server/utils/constants.ts @@ -17,6 +17,7 @@ import { DefaultHeaders, IndexManagementApi } from "../models/interfaces"; export const API_ROUTE_PREFIX = "/_opendistro/_ism"; export const API_ROUTE_PREFIX_ROLLUP = "/_opendistro/_rollup"; +export const TRANSFORM_ROUTE_PREFIX = "/_opendistro/_transform"; export const API: IndexManagementApi = { POLICY_BASE: `${API_ROUTE_PREFIX}/policies`, @@ -26,6 +27,7 @@ export const API: IndexManagementApi = { REMOVE_POLICY_BASE: `${API_ROUTE_PREFIX}/remove`, CHANGE_POLICY_BASE: `${API_ROUTE_PREFIX}/change_policy`, ROLLUP_JOBS_BASE: `${API_ROUTE_PREFIX_ROLLUP}/jobs`, + TRANSFORM_BASE: `${TRANSFORM_ROUTE_PREFIX}`, }; export const DEFAULT_HEADERS: DefaultHeaders = { From 568c1396ea0f78f27607c433daed221cfaff553d Mon Sep 17 00:00:00 2001 From: Annie Date: Tue, 27 Apr 2021 15:15:17 -0700 Subject: [PATCH 07/93] Update step2 title --- .../components/CreateTransformSteps/CreateTransformSteps.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/public/pages/CreateTransform/components/CreateTransformSteps/CreateTransformSteps.tsx b/public/pages/CreateTransform/components/CreateTransformSteps/CreateTransformSteps.tsx index 02bdc136b..9d42e6e9a 100644 --- a/public/pages/CreateTransform/components/CreateTransformSteps/CreateTransformSteps.tsx +++ b/public/pages/CreateTransform/components/CreateTransformSteps/CreateTransformSteps.tsx @@ -26,7 +26,7 @@ const setOfSteps = (step: number) => { children: null, }, { - title: "Define aggregations and metrics", + title: "Define transforms", children: null, status: step < 2 ? "disabled" : null, }, From 479c3326d55ac128c0ac05c087ef2d8cba083f88 Mon Sep 17 00:00:00 2001 From: Annie Date: Tue, 27 Apr 2021 15:28:33 -0700 Subject: [PATCH 08/93] Add DefineTransform component panel --- .../DefineTransforms/DefineTransforms.tsx | 35 +++++++++++++++++++ .../components/DefineTransforms/index.ts | 18 ++++++++++ .../CreateTransformForm.tsx | 7 ++-- .../CreateTransformStep2.tsx | 14 ++++---- 4 files changed, 63 insertions(+), 11 deletions(-) create mode 100644 public/pages/CreateTransform/components/DefineTransforms/DefineTransforms.tsx create mode 100644 public/pages/CreateTransform/components/DefineTransforms/index.ts diff --git a/public/pages/CreateTransform/components/DefineTransforms/DefineTransforms.tsx b/public/pages/CreateTransform/components/DefineTransforms/DefineTransforms.tsx new file mode 100644 index 000000000..47ea523b9 --- /dev/null +++ b/public/pages/CreateTransform/components/DefineTransforms/DefineTransforms.tsx @@ -0,0 +1,35 @@ +/* + * Copyright 2021 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"). + * You may not use this file except in compliance with the License. + * A copy of the License is located at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * or in the "license" file accompanying this file. This file is distributed + * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either + * express or implied. See the License for the specific language governing + * permissions and limitations under the License. + */ + +import React, { Component } from "react"; +import { ContentPanel } from "../../../../components/ContentPanel"; + +interface DefineTransformsProps { + transformId: string; +} + +interface DefineTransformsState {} + +export default class DefineTransforms extends Component { + constructor(props: DefineTransformsProps) { + super(props); + const { transfromId } = this.props; + this.state = {}; + } + + render() { + return ; + } +} diff --git a/public/pages/CreateTransform/components/DefineTransforms/index.ts b/public/pages/CreateTransform/components/DefineTransforms/index.ts new file mode 100644 index 000000000..ac624d7f2 --- /dev/null +++ b/public/pages/CreateTransform/components/DefineTransforms/index.ts @@ -0,0 +1,18 @@ +/* + * Copyright 2021 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"). + * You may not use this file except in compliance with the License. + * A copy of the License is located at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * or in the "license" file accompanying this file. This file is distributed + * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either + * express or implied. See the License for the specific language governing + * permissions and limitations under the License. + */ + +import DefineTransforms from "./DefineTransforms"; + +export default DefineTransforms; diff --git a/public/pages/CreateTransform/containers/CreateTransformForm/CreateTransformForm.tsx b/public/pages/CreateTransform/containers/CreateTransformForm/CreateTransformForm.tsx index 16c135c2c..950e8db45 100644 --- a/public/pages/CreateTransform/containers/CreateTransformForm/CreateTransformForm.tsx +++ b/public/pages/CreateTransform/containers/CreateTransformForm/CreateTransformForm.tsx @@ -193,7 +193,7 @@ export default class CreateTransformForm extends Component - + @@ -53,9 +54,10 @@ export default class CreateTransformStep2 extends Component -

Definition Placeholder

+

Define transform

+
From 71bb63664f11c8754a183afccbe5e836baafbcd4 Mon Sep 17 00:00:00 2001 From: Annie Date: Tue, 27 Apr 2021 16:03:04 -0700 Subject: [PATCH 09/93] Add full screen button --- .../DefineTransforms/DefineTransforms.tsx | 29 +++++++++++++++++-- 1 file changed, 27 insertions(+), 2 deletions(-) diff --git a/public/pages/CreateTransform/components/DefineTransforms/DefineTransforms.tsx b/public/pages/CreateTransform/components/DefineTransforms/DefineTransforms.tsx index 47ea523b9..d47dbc063 100644 --- a/public/pages/CreateTransform/components/DefineTransforms/DefineTransforms.tsx +++ b/public/pages/CreateTransform/components/DefineTransforms/DefineTransforms.tsx @@ -14,7 +14,7 @@ */ import React, { Component } from "react"; -import { ContentPanel } from "../../../../components/ContentPanel"; +import { ContentPanel, ContentPanelActions } from "../../../../components/ContentPanel"; interface DefineTransformsProps { transformId: string; @@ -30,6 +30,31 @@ export default class DefineTransforms extends Component
; + return ( + + // onShow(ApplyPolicyModal, { + // indices: selectedItems.map((item: ManagedCatIndex) => item.index), + // core: this.context, + // }), + }, + }, + ]} + /> + } + bodyStyles={{ padding: "initial" }} + title="Select fields to transform" + titleSize="m" + > + ); } } From f941b1e25a8fb6d93952a3d116b8315b580891d5 Mon Sep 17 00:00:00 2001 From: Ravi Thaluru Date: Wed, 28 Apr 2021 11:37:59 -0700 Subject: [PATCH 10/93] Adding working GetTransform page --- .../containers/Transforms/Transforms.tsx | 167 +++++++++++--- public/services/TransformService.ts | 6 +- server/clusters/ism/ismPlugin.ts | 60 ++++- server/models/interfaces.ts | 2 +- server/routes/rollups.ts | 2 +- server/routes/transforms.ts | 48 ++++ server/services/TransformService.ts | 207 +++++++++++++++++- 7 files changed, 443 insertions(+), 49 deletions(-) diff --git a/public/pages/Transforms/containers/Transforms/Transforms.tsx b/public/pages/Transforms/containers/Transforms/Transforms.tsx index 3a291798e..25d8c54de 100644 --- a/public/pages/Transforms/containers/Transforms/Transforms.tsx +++ b/public/pages/Transforms/containers/Transforms/Transforms.tsx @@ -14,6 +14,8 @@ */ import { + // @ts-ignore + Criteria, Direction, EuiPanel, EuiFlexGroup, @@ -38,18 +40,19 @@ import { import queryString from "query-string"; import { RouteComponentProps } from "react-router-dom"; import TransformService from "../../../../services/TransformService"; -import {DocumentTransform} from "../../../../../models/interfaces"; +import { DocumentTransform } from "../../../../../models/interfaces"; import React, { Component } from "react"; import { CoreServicesContext } from "../../../../components/core_services"; -import {getURLQueryParams} from "../../utils/helpers"; -import {TransformQueryParams} from "../../models/interfaces"; -import {getErrorMessage} from "../../../../utils/helpers"; -import {BREADCRUMBS, ROUTES} from "../../../../utils/constants"; +import { getURLQueryParams } from "../../utils/helpers"; +import { TransformQueryParams } from "../../models/interfaces"; +import { getErrorMessage } from "../../../../utils/helpers"; +import { BREADCRUMBS, ROUTES } from "../../../../utils/constants"; import DeleteModal from "../../components/DeleteModal"; import TransformEmptyPrompt from "../../components/TransformEmptyPrompt"; -import {renderEnabled, renderStatus} from "../../utils/metadataHelper"; -import {DEFAULT_PAGE_SIZE_OPTIONS} from "../../../Indices/utils/constants"; +import { renderEnabled, renderStatus } from "../../utils/metadataHelper"; +import {DEFAULT_PAGE_SIZE_OPTIONS, DEFAULT_QUERY_PARAMS} from "../../../Indices/utils/constants"; import _ from "lodash"; +import { ManagedCatIndex } from "../../../../../server/models/interfaces"; interface TransformProps extends RouteComponentProps { transformService: TransformService @@ -86,7 +89,7 @@ export default class Transforms extends Component => { + getTransforms = async() => { this.setState( { fetchingTransforms: true }); try { const { transformService, history } = this.props; @@ -333,25 +344,125 @@ export default class Transforms extends Component { + this.state.selectedItems.map((item: DocumentTransform) => { return item._id }).join(", "); + }; + + onSelectionChange = (selectedItems: DocumentTransform[]): void => { + this.setState({ selectedItems }); + }; + + showDeleteModal = () => { + this.setState({ isDeleteModalVisible: true }); + }; + + closeDeleteModal = () => { + this.setState({ isDeleteModalVisible: false }); + }; + + onClickCreate = () => { + this.props.history.push(ROUTES.CREATE_TRANSFORM); + }; + + onClickEdit = () => { + const { selectedItems: [{_id}] } = this.state; + if (_id) this.props.history.push(`${ROUTES.EDIT_TRANSFORM}?id=${_id}`); + }; + + onClickDelete = async() => { + const { transformService } = this.props; + const { selectedItems } = this.state; + for (let item of selectedItems) { + const transformId = item._id; + try { + const response = await transformService.deleteTransform(transformId); + + if (response.ok) { + this.closeDeleteModal(); + this.context.notification.toasts.addSuccess(`"${transformId}" successfully deleted!`); + } else { + this.context.notifications.toasts.addDanger(`could not delete transform job "${transformId}" : ${response.error}`); + } + } catch (err) { + this.context.notification.toasts.addDanger(getErrorMessage(err, "Could not delete the transform job")); + } + } + + await this.getTransforms(); + }; + + onPageClick = (page: number) => { + this.setState({from: page * this.state.size }); + }; + + onTableChange = ({ page: tablePage, sort }: Criteria) => { + const { index: page, size } = tablePage; + const { field: sortField, direction: sortDirection } = sort; + this.setState({ from: page * size, size, sortField, sortDirection }); + }; + + closePopover = () => { + this.setState({ isPopOverOpen: false }); + }; + + resetFilters = () => { + this.setState({ search: DEFAULT_QUERY_PARAMS.search }); + }; + + onEnable = async() => { + const { transformService } = this.props; + const { selectedItems } = this.state; + + for (const item of selectedItems) { + const transformId = item._id; + try { + const response = await transformService.startTransform(transformId); + + if (response.ok) { + this.context.notifications.toasts.addSuccess(`${transformId} is enabled`); + } else { + this.context.notifications.toasts.addDanger(`Could not start transform job "${transformId}": ${response.error}`); + } + } catch (err) { + this.context.notifications.toasts.addDanger(getErrorMessage(err, `Could not start transform job ${transformId}`)) + } + } + + await this.getTransforms(); + }; + + onDisable = async() => { + const { transformService } = this.props; + const { selectedItems } = this.state; + + for (const item of selectedItems) { + const transformId = item._id; + try { + const response = await transformService.stopTransform(transformId); + + if (response.ok) { + this.context.notifications.toasts.addSuccess(`${transformId} is disabled`); + } else { + this.context.notifications.toasts.addDanger(`Could not stop transform job "${transformId}": ${response.error}`); + } + } catch (err) { + this.context.notifications.toasts.addDanger(getErrorMessage(err, `Could not stop transform job ${transformId}`)) + } + } + + await this.getTransforms(); + }; + + onSearchChange = (e: React.ChangeEvent) => { + this.setState({ from: 0, search: e.target.value }); + }; + + onActionButtonClick = () => { + this.setState({ isPopOverOpen: !this.state.isPopOverOpen }); + }; + + static getQueryObjectFromState({ from, size, search, sortField, sortDirection} : TransformState) : TransformQueryParams { + return { from, size, search, sortField, sortDirection }; } } diff --git a/public/services/TransformService.ts b/public/services/TransformService.ts index 0e3ebb166..b31a49fa3 100644 --- a/public/services/TransformService.ts +++ b/public/services/TransformService.ts @@ -15,7 +15,7 @@ import {HttpSetup} from "kibana/public"; import {ServerResponse} from "../../server/models/types"; -import {GetTransformResponse, PutTransformResponse} from "../../server/models/interfaces"; +import {GetTransformsResponse, PutTransformResponse} from "../../server/models/interfaces"; import {NODE_API} from "../../utils/constants"; import {DocumentTransform, Transform} from "../../models/interfaces"; @@ -26,10 +26,10 @@ export default class TransformService { this.httpClient = httpClient } - getTransforms = async (queryObject: object): Promise> => { + getTransforms = async (queryObject: object): Promise> => { const url = `..${NODE_API.TRANSFORMS}` // @ts-ignore - return (await this.httpClient.get(url, { query: queryObject })) as ServerResponse; + return (await this.httpClient.get(url, { query: queryObject })) as ServerResponse; }; putTransform = async ( diff --git a/server/clusters/ism/ismPlugin.ts b/server/clusters/ism/ismPlugin.ts index 8bcd7101c..4ca75fb72 100644 --- a/server/clusters/ism/ismPlugin.ts +++ b/server/clusters/ism/ismPlugin.ts @@ -287,9 +287,61 @@ export default function ismPlugin(Client: any, config: any, components: any) { }); ism.getTransforms = ca({ - url: { - fmt: `${API.TRANSFORM_BASE}/`, - }, - method: "GET", + url: { + fmt: `${API.TRANSFORM_BASE}/`, + }, + method: "GET", + }); + + ism.explainTransform = ca({ + url: { + fmt: `${API.TRANSFORM_BASE}/<%=transformId%>/_explain`, + req: { + transformId: { + type: "string", + required: true, + } + } + }, + method: "GET", + }); + + ism.startTransform = ca({ + url: { + fmt: `${API.TRANSFORM_BASE}/<%=transformId%>/_start`, + req: { + transformId: { + type: "string", + required: true, + } + } + }, + method: "POST", + }); + + ism.stopTransform = ca({ + url: { + fmt: `${API.TRANSFORM_BASE}/<%=transformId%>/_stop`, + req: { + transformId: { + type: "string", + required: true, + } + } + }, + method: "POST", + }); + + ism.deleteTransform = ca({ + url: { + fmt: `${API.TRANSFORM_BASE}/<%=transformId%>`, + req: { + transformId: { + type: "string", + required: true, + } + } + }, + method: "DELETE" }); } diff --git a/server/models/interfaces.ts b/server/models/interfaces.ts index 636e9e2de..3269f07be 100644 --- a/server/models/interfaces.ts +++ b/server/models/interfaces.ts @@ -100,7 +100,7 @@ export interface DeleteTransformResponse { result: string; } -export interface GetTransformResponse { +export interface GetTransformsResponse { transforms: DocumentTransform[]; totalTransforms: number; metadata: any; diff --git a/server/routes/rollups.ts b/server/routes/rollups.ts index 9e32e397a..7ec4e86de 100644 --- a/server/routes/rollups.ts +++ b/server/routes/rollups.ts @@ -19,7 +19,7 @@ import { NodeServices } from "../models/interfaces"; import { NODE_API } from "../../utils/constants"; export default function (services: NodeServices, router: IRouter) { - const { rollupService } = services; + const { rollupService, transformService } = services; router.get( { diff --git a/server/routes/transforms.ts b/server/routes/transforms.ts index d987aa569..ce7dd0de3 100644 --- a/server/routes/transforms.ts +++ b/server/routes/transforms.ts @@ -36,4 +36,52 @@ export default function (services: NodeServices, router: IRouter) { }, transformService.getTransforms ); + + router.get( + { + path: `${NODE_API.TRANSFORMS}/{id}`, + validate: { + params: schema.object({ + id: schema.string(), + }), + }, + }, + transformService.getTransform + ); + + router.post( + { + path: `${NODE_API.TRANSFORMS}/{id}/_stop`, + validate: { + params: schema.object({ + id: schema.string(), + }), + }, + }, + transformService.stopTransform + ); + + router.post( + { + path: `${NODE_API.TRANSFORMS}/{id}/_start`, + validate: { + params: schema.object({ + id: schema.string(), + }), + }, + }, + transformService.startTransform + ); + + router.delete( + { + path: `${NODE_API.TRANSFORMS}/{id}`, + validate: { + params: schema.object({ + id: schema.string(), + }), + }, + }, + transformService.deleteTransform + ) } diff --git a/server/services/TransformService.ts b/server/services/TransformService.ts index 383f45c82..4a1774947 100644 --- a/server/services/TransformService.ts +++ b/server/services/TransformService.ts @@ -21,8 +21,9 @@ import { RequestHandlerContext } from "kibana/server"; import {ServerResponse} from "../models/types"; -import {GetTransformResponse} from "../models/interfaces"; -import {DocumentTransform} from "../../models/interfaces"; +import {GetTransformsResponse} from "../models/interfaces"; +import {DocumentTransform, Transform} from "../../models/interfaces"; +import _ from "lodash"; export default class TransformService { esDriver: IClusterClient; @@ -35,7 +36,7 @@ export default class TransformService { context: RequestHandlerContext, request: KibanaRequest, response: KibanaResponseFactory - ): Promise>> => { + ): Promise>> => { try { const { from, size, search, sortDirection, sortField } = request.query as { from: number; @@ -52,31 +53,59 @@ export default class TransformService { "transform.transform.enabled": "transform.enabled", }; - // TODO: Correct the parsing const params = { - // from: parseInt(from, 10), - // size: parseInt(size, 10), - // search, - // sortField: transformSortMap[sortField] || transformSortMap._id, - // sortDirection, + from: parseInt(from, 10), + size: parseInt(size, 10), + search, + sortField: transformSortMap[sortField] || transformSortMap._id, + sortDirection, }; const { callAsCurrentUser: callWithRequest } = this.esDriver.asScoped(request); const getTransformsResponse = await callWithRequest("ism.getTransforms", params); const totalTransforms = getTransformsResponse.total_transforms; const transforms = getTransformsResponse.transforms.map((transform: DocumentTransform) => ({ - seqNo: transform._seqNo as number, - primaryTerm: transform._primaryTerm as number, - id: transform._id, + _seqNo: transform._seqNo as number, + _primaryTerm: transform._primaryTerm as number, + _id: transform._id, transform: transform.transform, metadata: null })); + if (totalTransforms) { + const ids = transforms.map((transform: DocumentTransform) => transform._id).join(","); + const explainResponse = await callWithRequest("ism.explainTransform", { transformId: ids }); + if (!explainResponse.error) { + transforms.map((transform: DocumentTransform) => { + transform.metadata = explainResponse[transform._id]; + }); + + return response.custom({ + statusCode: 200, + body: { ok: true, response: {transforms: transforms, totalTransforms: totalTransforms, metadata: explainResponse} }, + }); + } else { + return response.custom({ + statusCode: 200, + body: { + ok: false, + error: explainResponse ? explainResponse.error : "An error occurred when calling getExplain API.", + } + }); + } + } return response.custom({ statusCode: 200, body: {ok: true, response: {transforms: transforms, totalTransforms: totalTransforms, metadata: {}}}, }); } catch (err) { + if (err.statusCode === 404 && err.body.error.type === "index_not_found_exception") { + return response.custom({ + statusCode: 200, + body: { ok: true, response: { transforms: [], totalTransforms: 0, metadata: null } }, + }); + } + console.error("Index Management - TransformService - getTransforms", err); return response.custom({ statusCode: 200, body: { @@ -86,4 +115,158 @@ export default class TransformService { }) } }; + + getTransform = async( + context: RequestHandlerContext, + request: KibanaRequest, + response: KibanaResponseFactory + ): Promise>> => { + try { + const { id } = request.params as { id: string }; + const params = { transformId: id }; + const { callAsCurrentUser: callWithRequest } = this.esDriver.asScoped(request); + const getResponse = await callWithRequest("ism.getTransform", params); + const metadata = await callWithRequest("ism.explainTransform", params); + const transform = _.get(getResponse, "transform", null); + const seqNo = _.get(getResponse, "_seg_no", null); + const primaryTerm = _.get(getResponse, "_primary_term", null); + + if (transform) { + if (metadata) { + return response.custom({ + statusCode: 200, + body: { + ok: true, + response: { + _id: id, + _seqNo: seqNo as number, + _primaryTerm: primaryTerm as number, + transform: transform as Transform, + metadata: metadata, + } + } + }); + } else { + return response.custom({ + statusCode: 200, + body: { + ok: false, + error: "Failed to load metadata for transform", + } + }); + } + } else { + return response.custom({ + statusCode: 200, + body: { + ok: false, + error: "Failed to load transform", + } + }); + } + } catch (err) { + console.error("Index Management - TransformService - getTransform:", err); + return response.custom({ + statusCode: 200, + body: { + ok: false, + error: err.message, + }, + }); + } + }; + + startTransform = async( + context: RequestHandlerContext, + request: KibanaRequest, + response: KibanaResponseFactory + ): Promise>> => { + try { + const { id } = request.params as { id: string }; + console.log("received "+ JSON.stringify(request.params)); + const params = { transformId: id }; + const { callAsCurrentUser: callWithRequest } = this.esDriver.asScoped(request); + const startResponse = await callWithRequest("ism.startTransform", params); + const acknowledged = _.get(startResponse, "acknowledged"); + if (acknowledged) { + return response.custom({ + statusCode: 200, + body: { ok: true, response: true }, + }); + } else { + return response.custom({ + statusCode: 200, + body: { ok: false, error: "Failed to start transform" }, + }); + } + } catch (err) { + console.error("Index Management - TransformService - startTransform", err); + return response.custom({ + statusCode: 200, + body: { ok: false, error: err.message }, + }); + } + }; + + stopTransform = async( + context: RequestHandlerContext, + request: KibanaRequest, + response: KibanaResponseFactory + ): Promise>> => { + try { + const { id } = request.params as { id: string }; + const params = { transformId: id }; + const { callAsCurrentUser: callWithRequest } = this.esDriver.asScoped(request); + const stopResponse = await callWithRequest("ism.stopTransform", params); + const acknowledged = _.get(stopResponse, "acknowledged"); + if (acknowledged) { + return response.custom({ + statusCode: 200, + body: { ok: true, response: true }, + }); + } else { + return response.custom({ + statusCode: 200, + body: { ok: false, error: "Failed to stop transform" }, + }); + } + } catch (err) { + console.error("Index Management - TransformService - stopTransform", err); + return response.custom({ + statusCode: 200, + body: { ok: false, error: err.message }, + }); + } + }; + + deleteTransform = async( + context: RequestHandlerContext, + request: KibanaRequest, + response: KibanaResponseFactory + ): Promise>> => { + try { + const { id } = request.params as { id: string }; + const params = { transformId: id }; + const { callAsCurrentUser: callWithRequest } = this.esDriver.asScoped(request); + const deleteResponse = await callWithRequest("ism.deleteTransform", params); + const acknowledged = _.get(deleteResponse, "acknowledged"); + if (acknowledged) { + return response.custom({ + statusCode: 200, + body: { ok: true, response: true }, + }); + } else { + return response.custom({ + statusCode: 200, + body: { ok: false, error: "Failed to delete transform" }, + }); + } + } catch (err) { + console.error("Index Management - TransformService - deleteTransform", err); + return response.custom({ + statusCode: 200, + body: { ok: false, error: err.message }, + }) + } + } } From ccdf22819fce7b787a7a70a27d7ac48a84920319 Mon Sep 17 00:00:00 2001 From: Ravi Thaluru Date: Wed, 28 Apr 2021 17:02:02 -0700 Subject: [PATCH 11/93] Adding edit/details pages for transforms --- public/pages/Main/Main.tsx | 27 +- .../ConfigureTransform/Configure.tsx | 57 ++++ .../components/ConfigureTransform/index.ts | 18 ++ .../GeneralInformation/GeneralInformation.tsx | 85 +++++ .../components/GeneralInformation/index.ts | 18 ++ .../TransformStatus/TransformStatus.tsx | 91 ++++++ .../components/TransformStatus/index.ts | 18 ++ .../containers/Transforms/EditTransform.tsx | 140 +++++++++ .../Transforms/TransformDetails.tsx | 292 ++++++++++++++++++ .../containers/Transforms/Transforms.tsx | 35 ++- .../Transforms/containers/Transforms/index.ts | 19 ++ public/pages/Transforms/index.ts | 18 ++ public/pages/Transforms/utils/constants.tsx | 22 +- public/pages/Transforms/utils/helpers.ts | 13 +- 14 files changed, 821 insertions(+), 32 deletions(-) create mode 100644 public/pages/Transforms/components/ConfigureTransform/Configure.tsx create mode 100644 public/pages/Transforms/components/ConfigureTransform/index.ts create mode 100644 public/pages/Transforms/components/GeneralInformation/GeneralInformation.tsx create mode 100644 public/pages/Transforms/components/GeneralInformation/index.ts create mode 100644 public/pages/Transforms/components/TransformStatus/TransformStatus.tsx create mode 100644 public/pages/Transforms/components/TransformStatus/index.ts create mode 100644 public/pages/Transforms/containers/Transforms/EditTransform.tsx create mode 100644 public/pages/Transforms/containers/Transforms/TransformDetails.tsx create mode 100644 public/pages/Transforms/containers/Transforms/index.ts create mode 100644 public/pages/Transforms/index.ts diff --git a/public/pages/Main/Main.tsx b/public/pages/Main/Main.tsx index 1f2f426e3..c48320e83 100644 --- a/public/pages/Main/Main.tsx +++ b/public/pages/Main/Main.tsx @@ -32,7 +32,8 @@ import { CoreServicesConsumer } from "../../components/core_services"; import CreateRollupForm from "../CreateRollup/containers/CreateRollupForm"; import EditRollup from "../EditRollup/containers"; import RollupDetails from "../RollupDetails/containers/RollupDetails"; -import Transforms from "../Transforms/containers/Transforms/Transforms"; +import { EditTransform, Transforms } from "../Transforms"; +import TransformDetails from "../Transforms/containers/Transforms/TransformDetails"; enum Navigation { IndexManagement = "Index Management", @@ -92,7 +93,7 @@ export default class Main extends Component { name: Navigation.Transforms, id: 5, href: `#${Pathname.Transforms}`, - isSelected: pathname === Pathname.Transforms + isSelected: pathname === Pathname.Transforms, }, ], }, @@ -108,11 +109,15 @@ export default class Main extends Component { {/*Hide side navigation bar when creating or editing rollup job*/} - {pathname != ROUTES.CREATE_ROLLUP && pathname != ROUTES.EDIT_ROLLUP && pathname != ROUTES.ROLLUP_DETAILS && ( - - - - )} + {pathname != ROUTES.CREATE_ROLLUP && + pathname != ROUTES.EDIT_ROLLUP && + pathname != ROUTES.ROLLUP_DETAILS && + pathname != ROUTES.EDIT_TRANSFORM && + pathname != ROUTES.TRANSFORM_DETAILS && ( + + + + )} { /> ( + render={(props: RouteComponentProps) => (
- +
)} /> @@ -213,7 +218,7 @@ export default class Main extends Component { path={ROUTES.EDIT_TRANSFORM} render={(props: RouteComponentProps) => (
- +
)} /> @@ -221,7 +226,7 @@ export default class Main extends Component { path={ROUTES.TRANSFORM_DETAILS} render={(props: RouteComponentProps) => (
- +
)} /> diff --git a/public/pages/Transforms/components/ConfigureTransform/Configure.tsx b/public/pages/Transforms/components/ConfigureTransform/Configure.tsx new file mode 100644 index 000000000..f014fb233 --- /dev/null +++ b/public/pages/Transforms/components/ConfigureTransform/Configure.tsx @@ -0,0 +1,57 @@ +/* + * Copyright 2021 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"). + * You may not use this file except in compliance with the License. + * A copy of the License is located at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * or in the "license" file accompanying this file. This file is distributed + * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either + * express or implied. See the License for the specific language governing + * permissions and limitations under the License. + */ + +import React, { ChangeEvent } from "react"; +import { EuiSpacer, EuiFormRow, EuiFieldText, EuiTextArea, EuiText, EuiFlexGroup, EuiFlexItem } from "@elastic/eui"; +import { ContentPanel } from "../../../../components/ContentPanel"; + +interface ConfigureTransformProps { + inEdit: boolean; + id: string; + error: string; + onChangeName: (value: ChangeEvent) => void; + onChangeDescription: (value: ChangeEvent) => void; + description: string; +} + +const ConfigureTransform = ({ inEdit, id, error, onChangeName, onChangeDescription, description }: ConfigureTransformProps) => ( + +
+ + + + + + + + +

Description

+
+
+ + + - optional + + +
+ + + + +
+
+); + +export default ConfigureTransform; diff --git a/public/pages/Transforms/components/ConfigureTransform/index.ts b/public/pages/Transforms/components/ConfigureTransform/index.ts new file mode 100644 index 000000000..fd7952e60 --- /dev/null +++ b/public/pages/Transforms/components/ConfigureTransform/index.ts @@ -0,0 +1,18 @@ +/* + * Copyright 2021 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"). + * You may not use this file except in compliance with the License. + * A copy of the License is located at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * or in the "license" file accompanying this file. This file is distributed + * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either + * express or implied. See the License for the specific language governing + * permissions and limitations under the License. + */ + +import ConfigureTransform from "./Configure"; + +export default ConfigureTransform; diff --git a/public/pages/Transforms/components/GeneralInformation/GeneralInformation.tsx b/public/pages/Transforms/components/GeneralInformation/GeneralInformation.tsx new file mode 100644 index 000000000..fe6990559 --- /dev/null +++ b/public/pages/Transforms/components/GeneralInformation/GeneralInformation.tsx @@ -0,0 +1,85 @@ +/* + * Copyright 2021 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"). + * You may not use this file except in compliance with the License. + * A copy of the License is located at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * or in the "license" file accompanying this file. This file is distributed + * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either + * express or implied. See the License for the specific language governing + * permissions and limitations under the License. + */ + +import React, { Component } from "react"; +import { EuiFlexGrid, EuiSpacer, EuiFlexItem, EuiText } from "@elastic/eui"; +import { ContentPanel, ContentPanelActions } from "../../../../components/ContentPanel"; +import { ModalConsumer } from "../../../../components/Modal"; + +interface GeneralInformationProps { + id: string; + description: string; + sourceIndex: string; + targetIndex: string; + scheduledText: string; + pageSize: number; + updatedAt: number; + onEdit: () => void; +} + +export default class GenerationInformation extends Component { + constructor(props: GeneralInformationProps) { + super(props); + } + + render() { + const { id, description, sourceIndex, targetIndex, scheduledText, pageSize, updatedAt, onEdit } = this.props; + const infoItems = [ + { term: "Name", value: id }, + { term: "Source index", value: sourceIndex }, + { term: "Target index", value: targetIndex }, + { term: "Schedule", value: scheduledText }, + { term: "Description", value: description || "-" }, + { term: "UpdatedAt", value: updatedAt }, + { term: "Pages per execution", value: pageSize }, + ]; + + return ( + + {() => ( + onEdit(), + }, + }, + ]} + /> + )} + + } + > +
+ + + {infoItems.map((item) => ( + + +
{item.term}
+
{item.value}
+
+
+ ))} +
+ +
+
+ ); + } +} diff --git a/public/pages/Transforms/components/GeneralInformation/index.ts b/public/pages/Transforms/components/GeneralInformation/index.ts new file mode 100644 index 000000000..dd311c3f6 --- /dev/null +++ b/public/pages/Transforms/components/GeneralInformation/index.ts @@ -0,0 +1,18 @@ +/* + * Copyright 2021 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"). + * You may not use this file except in compliance with the License. + * A copy of the License is located at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * or in the "license" file accompanying this file. This file is distributed + * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either + * express or implied. See the License for the specific language governing + * permissions and limitations under the License. + */ + +import GeneralInformation from "./GeneralInformation"; + +export default GeneralInformation; diff --git a/public/pages/Transforms/components/TransformStatus/TransformStatus.tsx b/public/pages/Transforms/components/TransformStatus/TransformStatus.tsx new file mode 100644 index 000000000..77343c4f7 --- /dev/null +++ b/public/pages/Transforms/components/TransformStatus/TransformStatus.tsx @@ -0,0 +1,91 @@ +/* + * Copyright 2021 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"). + * You may not use this file except in compliance with the License. + * A copy of the License is located at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * or in the "license" file accompanying this file. This file is distributed + * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either + * express or implied. See the License for the specific language governing + * permissions and limitations under the License. + */ + +import React, { Component } from "react"; +import { EuiFlexGrid, EuiSpacer, EuiFlexItem, EuiText } from "@elastic/eui"; +import { TransformMetadata } from "../../../../../models/interfaces"; +import { ContentPanel } from "../../../../components/ContentPanel"; +import { renderStatus } from "../../utils/metadataHelper"; + +interface TransformStatusProps { + metadata: TransformMetadata | undefined; +} +export default class TransformStatus extends Component { + constructor(props: TransformStatusProps) { + super(props); + } + + render() { + const { metadata } = this.props; + return ( + +
+ + + + +
Status
+ {renderStatus(metadata)} +
+
+ + +
Documents indexed
+
+ {metadata == null || metadata.transform_metadata == null ? "-" : metadata.transform_metadata.stats.documents_indexed} +
+
+
+ + +
Indexed time (ms)
+
+ {metadata == null || metadata.transform_metadata == null ? "-" : metadata.transform_metadata.stats.index_time_in_millis} +
+
+
+ + + + +
Document processed
+
+ {metadata == null || metadata.transform_metadata == null ? "-" : metadata.transform_metadata.stats.documents_processed} +
+
+
+ + +
Search time (ms)
+
+ {metadata == null || metadata.transform_metadata == null ? "-" : metadata.transform_metadata.stats.search_time_in_millis} +
+
+
+ + + + +
Page processed
+
{metadata == null || metadata.transform_metadata == null ? "-" : metadata.transform_metadata.stats.pages_processed}
+
+
+
+ +
+
+ ); + } +} diff --git a/public/pages/Transforms/components/TransformStatus/index.ts b/public/pages/Transforms/components/TransformStatus/index.ts new file mode 100644 index 000000000..8f2f8df70 --- /dev/null +++ b/public/pages/Transforms/components/TransformStatus/index.ts @@ -0,0 +1,18 @@ +/* + * Copyright 2021 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"). + * You may not use this file except in compliance with the License. + * A copy of the License is located at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * or in the "license" file accompanying this file. This file is distributed + * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either + * express or implied. See the License for the specific language governing + * permissions and limitations under the License. + */ + +import TransformStatus from "./TransformStatus"; + +export default TransformStatus; diff --git a/public/pages/Transforms/containers/Transforms/EditTransform.tsx b/public/pages/Transforms/containers/Transforms/EditTransform.tsx new file mode 100644 index 000000000..897f3db56 --- /dev/null +++ b/public/pages/Transforms/containers/Transforms/EditTransform.tsx @@ -0,0 +1,140 @@ +/* + * Copyright 2021 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"). + * You may not use this file except in compliance with the License. + * A copy of the License is located at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * or in the "license" file accompanying this file. This file is distributed + * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either + * express or implied. See the License for the specific language governing + * permissions and limitations under the License. + */ + +import { RouteComponentProps } from "react-router-dom"; +import { TransformService } from "../../../../services"; +import { CoreServicesContext } from "../../../../components/core_services"; +import React, { Component } from "react"; +import { EMPTY_TRANSFORM } from "../../utils/constants"; +import queryString from "query-string"; +import { BREADCRUMBS, ROUTES } from "../../../../utils/constants"; +import { getErrorMessage } from "../../../../utils/helpers"; +import { EuiFlexItem, EuiFlexGroup, EuiButton, EuiTitle, EuiSpacer, EuiButtonEmpty } from "@elastic/eui"; +import ConfigureTransform from "../../components/ConfigureTransform"; + +interface EditTransformProps extends RouteComponentProps { + transformService: TransformService; +} + +interface EditTransformState { + id: string; + error: string; + seqNo: number | null; + primaryTerm: number | null; + description: string; + pageSize: number; + enabled: boolean; + transformJSON: any; + isLoading: boolean; +} + +export default class EditTransform extends Component { + static contextType = CoreServicesContext; + + constructor(props: EditTransformProps) { + super(props); + this.state = { + id: "", + error: "", + seqNo: null, + primaryTerm: null, + description: "", + pageSize: 1000, + enabled: true, + transformJSON: EMPTY_TRANSFORM, + isLoading: false, + }; + } + + componentDidMount = async () => { + const { id } = queryString.parse(this.props.location.search); + if (typeof id === "string" && !!id) { + this.context.chrome.setBreadcrumbs([BREADCRUMBS.INDEX_MANAGEMENT, BREADCRUMBS.TRANSFORMS, BREADCRUMBS.EDIT_TRANSFORM, { text: id }]); + await this.getTransform(id); + } else { + this.context.notifications.toasts.addDanger(`Invalid transform id: ${id}`); + this.props.history.push(ROUTES.TRANSFORMS); + } + }; + + getTransform = async (transformId: string) => { + try { + const { transformService } = this.props; + const response = await transformService.getTransform(transformId); + + if (response.ok) { + let json = JSON.parse(this.state.transformJSON); + json.transform = response.response.transform; + + this.setState({ + seqNo: response.response._seqNo, + primaryTerm: response.response._primaryTerm, + id: response.response._id, + description: response.response.transform.description, + enabled: response.response.transform.enabled, + pageSize: response.response.transform.page_size, + transformJSON: json, + }); + } else { + this.context.notifications.toasts.addDanger(`Could not load transform job ${transformId}: ${response.error}`); + this.props.history.push(ROUTES.TRANSFORMS); + } + } catch (err) { + this.context.notifications.toasts.addDanger(getErrorMessage(err, `Could not load transform job ${transformId}`)); + this.props.history.push(ROUTES.TRANSFORMS); + } + }; + + render() { + const { id, error, description, isLoading } = this.state; + return ( +
+ +

Edit transform job

+
+ + + + + + + + + + Cancel + + + + + Save changes + + + +
+ ); + } + + onCancel = () => {}; + onSubmit = () => {}; + onChangeName = () => {}; + onChangeDescription = () => {}; +} diff --git a/public/pages/Transforms/containers/Transforms/TransformDetails.tsx b/public/pages/Transforms/containers/Transforms/TransformDetails.tsx new file mode 100644 index 000000000..a567aae8b --- /dev/null +++ b/public/pages/Transforms/containers/Transforms/TransformDetails.tsx @@ -0,0 +1,292 @@ +/* + * Copyright 2021 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"). + * You may not use this file except in compliance with the License. + * A copy of the License is located at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * or in the "license" file accompanying this file. This file is distributed + * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either + * express or implied. See the License for the specific language governing + * permissions and limitations under the License. + */ + +import { + EuiSpacer, + EuiTitle, + EuiFlexGroup, + EuiFlexItem, + EuiButton, + EuiOverlayMask, + EuiButtonEmpty, + EuiModalFooter, + EuiModal, + EuiModalHeader, + EuiModalHeaderTitle, + EuiModalBody, + EuiCodeBlock, + EuiHealth, +} from "@elastic/eui"; +import { TransformService } from "../../../../services"; +import { RouteComponentProps } from "react-router-dom"; +import React, { Component } from "react"; +import { CoreServicesContext } from "../../../../components/core_services"; +import { BREADCRUMBS, ROUTES } from "../../../../utils/constants"; +import queryString from "query-string"; +import { getErrorMessage } from "../../../../utils/helpers"; +import { DimensionItem, MetricItem, RollupDimensionItem, TransformMetadata } from "../../../../../models/interfaces"; +import DeleteModal from "../../components/DeleteModal"; +import GenerationInformation from "../../components/GeneralInformation"; +import TransformStatus from "../../components/TransformStatus"; +import { EMPTY_TRANSFORM } from "../../utils/constants"; + +interface TransformDetailsProps extends RouteComponentProps { + transformService: TransformService; +} + +interface TransformDetailsState { + id: string; + description: string; + enabled: boolean; + enabledAt: number | null; + updatedAt: number; + pageSize: number; + transformJson: any; + sourceIndex: string; + targetIndex: string; + aggregationsShown: MetricItem[]; + groupsShown: DimensionItem[]; + metadata: TransformMetadata | undefined; + interval: number; + intervalTimeUnit: string; + cronExpression: string; + isModalOpen: boolean; + isDeleteModalOpen: boolean; +} + +export default class TransformDetails extends Component { + static contextType = CoreServicesContext; + constructor(props: TransformDetailsProps) { + super(props); + + this.state = { + id: "", + description: "", + enabled: false, + enabledAt: null, + updatedAt: 1, + pageSize: 1000, + transformJson: EMPTY_TRANSFORM, + sourceIndex: "", + targetIndex: "", + aggregationsShown: [], + groupsShown: [], + metadata: undefined, + interval: 2, + intervalTimeUnit: "", + cronExpression: "", + isModalOpen: false, + isDeleteModalOpen: false, + }; + } + + componentDidMount = async (): Promise => { + this.context.chrome.setBreadcrumbs([BREADCRUMBS.INDEX_MANAGEMENT, BREADCRUMBS.TRANSFORMS]); + const { id } = queryString.parse(this.props.location.search); + if (typeof id === "string") { + this.context.chrome.setBreadcrumbs([BREADCRUMBS.INDEX_MANAGEMENT, BREADCRUMBS.TRANSFORMS, { text: id }]); + this.props.history.push(`${ROUTES.TRANSFORM_DETAILS}?id=${id}`); + await this.getTransform(id); + this.forceUpdate(); + } else { + this.context.notifications.toasts.addDanger(`Invalid rollup id: ${id}`); + this.props.history.push(ROUTES.TRANSFORMS); + } + }; + + getTransform = async (transformId: string) => { + try { + const { transformService } = this.props; + const response = await transformService.getTransform(transformId); + + if (response.ok) { + let json = response.response; + // let aggregations = this.parseAggregations(response.response.transform.aggregations); + let groups = this.parseGroups(response.response.transform.groups); + this.setState({ + id: response.response._id, + description: response.response.transform.description, + enabled: response.response.transform.enabled, + enabledAt: response.response.transform.enabled_at, + updatedAt: response.response.transform.updated_at, + pageSize: response.response.transform.page_size, + transformJson: json, + sourceIndex: response.response.transform.source_index, + targetIndex: response.response.transform.target_index, + aggregationsShown: [], + groupsShown: groups.slice(0, 10), + }); + + if (response.response.metadata != null) { + this.setState({ metadata: response.response.metadata[response.response._id] }); + } + if ("interval" in response.response.transform.schedule) { + this.setState({ + interval: response.response.transform.schedule.interval.period, + intervalTimeUnit: response.response.transform.schedule.interval.unit, + }); + } else { + this.setState({ cronExpression: response.response.transform.schedule.cron.expression }); + } + } else { + this.context.notifications.toasts.addDanger(`Could not load transform job ${transformId}: ${response.error}`); + this.props.history.push(ROUTES.TRANSFORMS); + } + } catch (err) { + this.context.notifications.toasts.addDanger(getErrorMessage(err, `Could not load transform job ${transformId}`)); + this.props.history.push(ROUTES.TRANSFORMS); + } + }; + + parseGroups = (groups: RollupDimensionItem[]): DimensionItem[] => { + const sourceArray = groups.slice(1, groups.length); + if (sourceArray.length == 0) return []; + // @ts-ignore + return sourceArray.map((group: RollupDimensionItem) => { + let sequence = groups.indexOf(group); + switch (true) { + case group.date_histogram != null: + return { + sequence: sequence, + aggregationMethod: "date_histogram", + field: { + label: group.date_histogram?.source_field, + }, + interval: group.date_histogram?.interval, + }; + case group.histogram != null: + return { + sequence: sequence, + aggregationMethod: "histogram", + field: { + label: group.histogram?.source_field, + }, + interval: group.histogram?.interval, + }; + case group.terms != null: + return { + sequence: sequence, + aggregationMethod: "terms", + field: { + label: group.terms?.source_field, + }, + interval: null, + }; + } + }); + }; + + render() { + const { + id, + enabled, + updatedAt, + description, + sourceIndex, + targetIndex, + pageSize, + metadata, + transformJson, + isDeleteModalOpen, + isModalOpen, + } = this.state; + + let scheduleText = "At some time"; + return ( +
+ + + +

{id}

+
+
+ + {enabled ? {"Enabled on " + updatedAt} : Disabled} + + + + + + + Disable + + + + + Enable + + + + View JSON + + + + Delete + + + + +
+ + + + + + + + + {isModalOpen && ( + + + + {"View JSON of " + id} + + + + + {JSON.stringify(transformJson, null, 4)} + + + + + Close + + + + )} + + {isDeleteModalOpen && } +
+ ); + } + + closeModal = () => {}; + closeDeleteModal = () => {}; + onClickDelete = () => {}; + onEdit = () => {}; + onEnable = () => {}; + onDisable = () => {}; + showModal = () => {}; + showDeleteModal = () => {}; +} diff --git a/public/pages/Transforms/containers/Transforms/Transforms.tsx b/public/pages/Transforms/containers/Transforms/Transforms.tsx index 25d8c54de..c53b86d59 100644 --- a/public/pages/Transforms/containers/Transforms/Transforms.tsx +++ b/public/pages/Transforms/containers/Transforms/Transforms.tsx @@ -50,12 +50,12 @@ import { BREADCRUMBS, ROUTES } from "../../../../utils/constants"; import DeleteModal from "../../components/DeleteModal"; import TransformEmptyPrompt from "../../components/TransformEmptyPrompt"; import { renderEnabled, renderStatus } from "../../utils/metadataHelper"; -import {DEFAULT_PAGE_SIZE_OPTIONS, DEFAULT_QUERY_PARAMS} from "../../../Indices/utils/constants"; +import { DEFAULT_PAGE_SIZE_OPTIONS, DEFAULT_QUERY_PARAMS } from "../../../Indices/utils/constants"; import _ from "lodash"; import { ManagedCatIndex } from "../../../../../server/models/interfaces"; interface TransformProps extends RouteComponentProps { - transformService: TransformService + transformService: TransformService; } interface TransformState { @@ -96,7 +96,7 @@ export default class Transforms extends Component @@ -324,8 +323,8 @@ export default class Transforms extends Component { - this.setState( { fetchingTransforms: true }); + getTransforms = async () => { + this.setState({ fetchingTransforms: true }); try { const { transformService, history } = this.props; const queryObject = Transforms.getQueryObjectFromState(this.state); @@ -334,7 +333,7 @@ export default class Transforms extends Component { - this.state.selectedItems.map((item: DocumentTransform) => { return item._id }).join(", "); + return "asd"; + // this.state.selectedItems.map((item: DocumentTransform) => { return item._id }).join(", "); }; onSelectionChange = (selectedItems: DocumentTransform[]): void => { @@ -365,11 +365,13 @@ export default class Transforms extends Component { - const { selectedItems: [{_id}] } = this.state; + const { + selectedItems: [{ _id }], + } = this.state; if (_id) this.props.history.push(`${ROUTES.EDIT_TRANSFORM}?id=${_id}`); }; - onClickDelete = async() => { + onClickDelete = async () => { const { transformService } = this.props; const { selectedItems } = this.state; for (let item of selectedItems) { @@ -392,7 +394,7 @@ export default class Transforms extends Component { - this.setState({from: page * this.state.size }); + this.setState({ from: page * this.state.size }); }; onTableChange = ({ page: tablePage, sort }: Criteria) => { @@ -409,7 +411,7 @@ export default class Transforms extends Component { + onEnable = async () => { const { transformService } = this.props; const { selectedItems } = this.state; @@ -424,14 +426,14 @@ export default class Transforms extends Component { + onDisable = async () => { const { transformService } = this.props; const { selectedItems } = this.state; @@ -446,7 +448,7 @@ export default class Transforms extends Component { + return { // @ts-ignores from: isNaN(parseInt(from, 10)) ? DEFAULT_QUERY_PARAMS.from : parseInt(from, 10), // @ts-ignores @@ -30,3 +31,9 @@ export function getURLQueryParams(location: { search: string }): TransformQueryP sortDirection: typeof sortDirection !== "string" ? DEFAULT_QUERY_PARAMS.sortDirection : sortDirection, }; } + +export const renderTime = (time: number): string => { + const momentTime = moment(time).local(); + if (time && momentTime.isValid()) return momentTime.format("MM/DD/YY h:mm a"); + return "-"; +}; From a6ea779f7b8a18f0096cafed0e49a661c9183ab8 Mon Sep 17 00:00:00 2001 From: Annie Date: Wed, 28 Apr 2021 17:38:06 -0700 Subject: [PATCH 12/93] Add text --- .../DefineTransforms/DefineTransforms.tsx | 18 +++++++++++++++--- 1 file changed, 15 insertions(+), 3 deletions(-) diff --git a/public/pages/CreateTransform/components/DefineTransforms/DefineTransforms.tsx b/public/pages/CreateTransform/components/DefineTransforms/DefineTransforms.tsx index d47dbc063..fd21b5ca7 100644 --- a/public/pages/CreateTransform/components/DefineTransforms/DefineTransforms.tsx +++ b/public/pages/CreateTransform/components/DefineTransforms/DefineTransforms.tsx @@ -13,11 +13,13 @@ * permissions and limitations under the License. */ +import { EuiSpacer, EuiText } from "@elastic/eui"; import React, { Component } from "react"; import { ContentPanel, ContentPanelActions } from "../../../../components/ContentPanel"; interface DefineTransformsProps { transformId: string; + sourceIndex: string; } interface DefineTransformsState {} @@ -25,11 +27,12 @@ interface DefineTransformsState {} export default class DefineTransforms extends Component { constructor(props: DefineTransformsProps) { super(props); - const { transfromId } = this.props; + const { transfromId, sourceIndex } = this.props; this.state = {}; } render() { + const { transfromId, sourceIndex } = this.props; return ( } - bodyStyles={{ padding: "initial" }} + bodyStyles={{ padding: "10px 10px" }} title="Select fields to transform" titleSize="m" - > + > + +

Original fields with sample data

+
+ + {/*TODO: Substitute "source index", and "filtered by" fields with actual values*/} + +

{`Viewing sample data from index ${sourceIndex}, filtered by order.type:sales_order, order.success:true`}

+
+ ); } } From 2b6344949ce9ccea7765cab3caea3c119c9922be Mon Sep 17 00:00:00 2001 From: Annie Date: Wed, 28 Apr 2021 17:53:20 -0700 Subject: [PATCH 13/93] Pass sourceIndex as string to step2 --- .../components/DefineTransforms/DefineTransforms.tsx | 5 ++++- .../CreateTransformForm/CreateTransformForm.tsx | 10 +++++++++- .../CreateTransformStep2/CreateTransformStep2.tsx | 7 +++++-- 3 files changed, 18 insertions(+), 4 deletions(-) diff --git a/public/pages/CreateTransform/components/DefineTransforms/DefineTransforms.tsx b/public/pages/CreateTransform/components/DefineTransforms/DefineTransforms.tsx index fd21b5ca7..bb44fff01 100644 --- a/public/pages/CreateTransform/components/DefineTransforms/DefineTransforms.tsx +++ b/public/pages/CreateTransform/components/DefineTransforms/DefineTransforms.tsx @@ -13,7 +13,7 @@ * permissions and limitations under the License. */ -import { EuiSpacer, EuiText } from "@elastic/eui"; +import { EuiDataGrid, EuiSpacer, EuiText } from "@elastic/eui"; import React, { Component } from "react"; import { ContentPanel, ContentPanelActions } from "../../../../components/ContentPanel"; @@ -66,6 +66,9 @@ export default class DefineTransforms extends Component

{`Viewing sample data from index ${sourceIndex}, filtered by order.type:sales_order, order.success:true`}

+ {/**/} ); } diff --git a/public/pages/CreateTransform/containers/CreateTransformForm/CreateTransformForm.tsx b/public/pages/CreateTransform/containers/CreateTransformForm/CreateTransformForm.tsx index 950e8db45..f3294d122 100644 --- a/public/pages/CreateTransform/containers/CreateTransformForm/CreateTransformForm.tsx +++ b/public/pages/CreateTransform/containers/CreateTransformForm/CreateTransformForm.tsx @@ -330,6 +330,8 @@ export default class CreateTransformForm extends Component - + { @@ -43,7 +46,7 @@ export default class CreateTransformStep2 extends ComponentDefine transform - +
From fedddb4838370a6c20d775bce15029540cde1807 Mon Sep 17 00:00:00 2001 From: Eric Lobdell Date: Tue, 27 Apr 2021 10:55:36 -0700 Subject: [PATCH 14/93] Initial draft commit for CreateTransform UI --- .../ConfigureTransform/ConfigureTransform.tsx | 56 +++ .../components/ConfigureTransform/index.ts | 18 + .../CreateTransformSteps.tsx | 51 +++ .../components/CreateTransformSteps/index.ts | 18 + .../JobNameAndIndices/JobNameAndIndices.tsx | 93 ++++ .../components/JobNameAndIndices/index.ts | 18 + .../components/Schedule/Schedule.tsx | 95 ++++ .../components/Schedule/index.ts | 18 + .../TransformIndices/TransformIndices.tsx | 175 ++++++++ .../components/TransformIndices/index.ts | 18 + .../CreateTransform/CreateTransform.tsx | 73 ++++ .../containers/CreateTransform/index.ts | 18 + .../CreateTransformForm.tsx | 411 ++++++++++++++++++ .../containers/CreateTransformForm/index.ts | 18 + .../CreateTransformStep2.tsx | 65 +++ .../containers/CreateTransformStep2/index.ts | 18 + .../CreateTransformStep3.tsx | 146 +++++++ .../containers/CreateTransformStep3/index.ts | 18 + .../CreateTransformStep4.tsx | 84 ++++ .../containers/CreateTransformStep4/index.ts | 18 + public/pages/CreateTransform/index.ts | 18 + .../pages/CreateTransform/utils/constants.ts | 88 ++++ public/pages/CreateTransform/utils/helpers.ts | 63 +++ public/pages/Main/Main.tsx | 3 +- 24 files changed, 1600 insertions(+), 1 deletion(-) create mode 100644 public/pages/CreateTransform/components/ConfigureTransform/ConfigureTransform.tsx create mode 100644 public/pages/CreateTransform/components/ConfigureTransform/index.ts create mode 100644 public/pages/CreateTransform/components/CreateTransformSteps/CreateTransformSteps.tsx create mode 100644 public/pages/CreateTransform/components/CreateTransformSteps/index.ts create mode 100644 public/pages/CreateTransform/components/JobNameAndIndices/JobNameAndIndices.tsx create mode 100644 public/pages/CreateTransform/components/JobNameAndIndices/index.ts create mode 100644 public/pages/CreateTransform/components/Schedule/Schedule.tsx create mode 100644 public/pages/CreateTransform/components/Schedule/index.ts create mode 100644 public/pages/CreateTransform/components/TransformIndices/TransformIndices.tsx create mode 100644 public/pages/CreateTransform/components/TransformIndices/index.ts create mode 100644 public/pages/CreateTransform/containers/CreateTransform/CreateTransform.tsx create mode 100644 public/pages/CreateTransform/containers/CreateTransform/index.ts create mode 100644 public/pages/CreateTransform/containers/CreateTransformForm/CreateTransformForm.tsx create mode 100644 public/pages/CreateTransform/containers/CreateTransformForm/index.ts create mode 100644 public/pages/CreateTransform/containers/CreateTransformStep2/CreateTransformStep2.tsx create mode 100644 public/pages/CreateTransform/containers/CreateTransformStep2/index.ts create mode 100644 public/pages/CreateTransform/containers/CreateTransformStep3/CreateTransformStep3.tsx create mode 100644 public/pages/CreateTransform/containers/CreateTransformStep3/index.ts create mode 100644 public/pages/CreateTransform/containers/CreateTransformStep4/CreateTransformStep4.tsx create mode 100644 public/pages/CreateTransform/containers/CreateTransformStep4/index.ts create mode 100644 public/pages/CreateTransform/index.ts create mode 100644 public/pages/CreateTransform/utils/constants.ts create mode 100644 public/pages/CreateTransform/utils/helpers.ts diff --git a/public/pages/CreateTransform/components/ConfigureTransform/ConfigureTransform.tsx b/public/pages/CreateTransform/components/ConfigureTransform/ConfigureTransform.tsx new file mode 100644 index 000000000..6a1fa6732 --- /dev/null +++ b/public/pages/CreateTransform/components/ConfigureTransform/ConfigureTransform.tsx @@ -0,0 +1,56 @@ +/* + * Copyright 2021 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"). + * You may not use this file except in compliance with the License. + * A copy of the License is located at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * or in the "license" file accompanying this file. This file is distributed + * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either + * express or implied. See the License for the specific language governing + * permissions and limitations under the License. + */ + +import React, { ChangeEvent } from "react"; +import { EuiSpacer, EuiFormRow, EuiFieldText, EuiTextArea, EuiText, EuiFlexGroup, EuiFlexItem } from "@elastic/eui"; +import { ContentPanel } from "../../../../components/ContentPanel"; + +interface ConfigureTransformProps { + isEdit: boolean; + transformId: string; + transformIdError: string; + onChangeName: (value: ChangeEvent) => void; + onChangeDescription: (value: ChangeEvent) => void; + description: string; +} + +const ConfigureTransform = ({ isEdit, transformId, transformIdError, onChangeName, onChangeDescription, description }: ConfigureTransformProps) => ( + +
+ + + + + + + + +

Description

+
+
+ + + - optional + + +
+ + + + +
+
+); +export default ConfigureTransform; diff --git a/public/pages/CreateTransform/components/ConfigureTransform/index.ts b/public/pages/CreateTransform/components/ConfigureTransform/index.ts new file mode 100644 index 000000000..b935263fd --- /dev/null +++ b/public/pages/CreateTransform/components/ConfigureTransform/index.ts @@ -0,0 +1,18 @@ +/* + * Copyright 2021 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"). + * You may not use this file except in compliance with the License. + * A copy of the License is located at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * or in the "license" file accompanying this file. This file is distributed + * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either + * express or implied. See the License for the specific language governing + * permissions and limitations under the License. + */ + +import ConfigureTransform from "./ConfigureTransform"; + +export default ConfigureTransform; diff --git a/public/pages/CreateTransform/components/CreateTransformSteps/CreateTransformSteps.tsx b/public/pages/CreateTransform/components/CreateTransformSteps/CreateTransformSteps.tsx new file mode 100644 index 000000000..02bdc136b --- /dev/null +++ b/public/pages/CreateTransform/components/CreateTransformSteps/CreateTransformSteps.tsx @@ -0,0 +1,51 @@ +/* + * Copyright 2021 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"). + * You may not use this file except in compliance with the License. + * A copy of the License is located at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * or in the "license" file accompanying this file. This file is distributed + * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either + * express or implied. See the License for the specific language governing + * permissions and limitations under the License. + */ +import React from "react"; +import { EuiSteps } from "@elastic/eui"; + +interface CreateTransformStepsProps { + step: number; +} + +const setOfSteps = (step: number) => { + return [ + { + title: "Set up indices", + children: null, + }, + { + title: "Define aggregations and metrics", + children: null, + status: step < 2 ? "disabled" : null, + }, + { + title: "Specify schedule", + children: null, + status: step < 3 ? "disabled" : null, + }, + { + title: "Review and create", + children: null, + status: step < 4 ? "disabled" : null, + }, + ]; +}; +const CreateTransformSteps = ({ step }: CreateTransformStepsProps) => ( +
+ +
+); + +export default CreateTransformSteps; diff --git a/public/pages/CreateTransform/components/CreateTransformSteps/index.ts b/public/pages/CreateTransform/components/CreateTransformSteps/index.ts new file mode 100644 index 000000000..fe481f23a --- /dev/null +++ b/public/pages/CreateTransform/components/CreateTransformSteps/index.ts @@ -0,0 +1,18 @@ +/* + * Copyright 2021 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"). + * You may not use this file except in compliance with the License. + * A copy of the License is located at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * or in the "license" file accompanying this file. This file is distributed + * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either + * express or implied. See the License for the specific language governing + * permissions and limitations under the License. + */ + +import CreateTransformSteps from "./CreateTransformSteps"; + +export default CreateTransformSteps; diff --git a/public/pages/CreateTransform/components/JobNameAndIndices/JobNameAndIndices.tsx b/public/pages/CreateTransform/components/JobNameAndIndices/JobNameAndIndices.tsx new file mode 100644 index 000000000..e2e01d3dd --- /dev/null +++ b/public/pages/CreateTransform/components/JobNameAndIndices/JobNameAndIndices.tsx @@ -0,0 +1,93 @@ +/* + * Copyright 2021 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"). + * You may not use this file except in compliance with the License. + * A copy of the License is located at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * or in the "license" file accompanying this file. This file is distributed + * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either + * express or implied. See the License for the specific language governing + * permissions and limitations under the License. + */ + +import React, { Component } from "react"; +import { EuiFlexGrid, EuiSpacer, EuiFlexItem, EuiText } from "@elastic/eui"; +import { ContentPanel, ContentPanelActions } from "../../../../components/ContentPanel"; +import { ModalConsumer } from "../../../../components/Modal"; +import { IndexItem } from "../../../../../models/interfaces"; + +interface JobNameAndIndicesProps { + transformId: string; + description: string; + sourceIndex: { label: string; value?: IndexItem }[]; + targetIndex: { label: string; value?: IndexItem }[]; + onChangeStep: (step: number) => void; +} + +export default class JobNameAndIndices extends Component { + constructor(props: JobNameAndIndicesProps) { + super(props); + } + + render() { + const { transformId, description, onChangeStep, sourceIndex, targetIndex } = this.props; + + return ( + + {() => ( + onChangeStep(1), + }, + }, + ]} + /> + )} + + } + bodyStyles={{ padding: "initial" }} + title="Job name and indices" + titleSize="m" + > +
+ + + + +
Name
+
{transformId}
+
+
+ + +
Source Index
+
{sourceIndex[0].label}
+
+
+ + +
Target index
+
{targetIndex[0].label}
+
+
+ + +
Description
+
{description == "" ? "-" : description}
+
+
+
+ +
+
+ ); + } +} diff --git a/public/pages/CreateTransform/components/JobNameAndIndices/index.ts b/public/pages/CreateTransform/components/JobNameAndIndices/index.ts new file mode 100644 index 000000000..aa4b17446 --- /dev/null +++ b/public/pages/CreateTransform/components/JobNameAndIndices/index.ts @@ -0,0 +1,18 @@ +/* + * Copyright 2021 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"). + * You may not use this file except in compliance with the License. + * A copy of the License is located at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * or in the "license" file accompanying this file. This file is distributed + * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either + * express or implied. See the License for the specific language governing + * permissions and limitations under the License. + */ + +import JobNameAndIndices from "./JobNameAndIndices"; + +export default JobNameAndIndices; diff --git a/public/pages/CreateTransform/components/Schedule/Schedule.tsx b/public/pages/CreateTransform/components/Schedule/Schedule.tsx new file mode 100644 index 000000000..9a8e45021 --- /dev/null +++ b/public/pages/CreateTransform/components/Schedule/Schedule.tsx @@ -0,0 +1,95 @@ +/* + * Copyright 2020 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"). + * You may not use this file except in compliance with the License. + * A copy of the License is located at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * or in the "license" file accompanying this file. This file is distributed + * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either + * express or implied. See the License for the specific language governing + * permissions and limitations under the License. + */ + +import React, { ChangeEvent, Component } from "react"; +import moment from "moment-timezone"; +import { + EuiSpacer, + EuiCheckbox, + EuiRadioGroup, + EuiFormRow, + EuiSelect, + EuiFieldNumber, + EuiFlexGroup, + EuiFlexItem, + EuiTextArea, + EuiFormHelpText, + EuiText, +} from "@elastic/eui"; +import { DelayTimeunitOptions, ScheduleIntervalTimeunitOptions } from "../../utils/constants"; +import { ContentPanel } from "../../../../components/ContentPanel"; + +interface ScheduleProps { + isEdit: boolean; + transformId: string; + transformIdError: string; + jobEnabledByDefault: boolean; + pageSize: number; + onChangeJobEnabledByDefault: () => void; + onChangePage: (e: ChangeEvent) => void; +} + +const radios = [ + { + id: "no", + label: "No", + }, + { + id: "yes", + label: "Yes", + }, +]; + +const timezones = moment.tz.names().map((tz) => ({ label: tz, text: tz })); + +export default class Schedule extends Component { + constructor(props: ScheduleProps) { + super(props); + } + + render() { + const { + isEdit, + jobEnabledByDefault, + pageSize, + onChangeJobEnabledByDefault, + onChangePage, + } = this.props; + return ( + +
+ {!isEdit && ( + + )} + + + + + + +
+
+ ); + } +} diff --git a/public/pages/CreateTransform/components/Schedule/index.ts b/public/pages/CreateTransform/components/Schedule/index.ts new file mode 100644 index 000000000..f18d83cb2 --- /dev/null +++ b/public/pages/CreateTransform/components/Schedule/index.ts @@ -0,0 +1,18 @@ +/* + * Copyright 2021 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"). + * You may not use this file except in compliance with the License. + * A copy of the License is located at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * or in the "license" file accompanying this file. This file is distributed + * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either + * express or implied. See the License for the specific language governing + * permissions and limitations under the License. + */ + +import Schedule from "./Schedule"; + +export default Schedule; diff --git a/public/pages/CreateTransform/components/TransformIndices/TransformIndices.tsx b/public/pages/CreateTransform/components/TransformIndices/TransformIndices.tsx new file mode 100644 index 000000000..2b0abb1f0 --- /dev/null +++ b/public/pages/CreateTransform/components/TransformIndices/TransformIndices.tsx @@ -0,0 +1,175 @@ +/* + * Copyright 2021 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"). + * You may not use this file except in compliance with the License. + * A copy of the License is located at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * or in the "license" file accompanying this file. This file is distributed + * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either + * express or implied. See the License for the specific language governing + * permissions and limitations under the License. + */ + +import React, { Component, Fragment } from "react"; +import { EuiSpacer, EuiFormRow, EuiComboBox, EuiCallOut } from "@elastic/eui"; +import { ContentPanel } from "../../../../components/ContentPanel"; +import { EuiComboBoxOptionOption } from "@elastic/eui/src/components/combo_box/types"; +import { IndexItem } from "../../../../../models/interfaces"; +import IndexService from "../../../../services/IndexService"; +import _ from "lodash"; +import { CoreServicesContext } from "../../../../components/core_services"; + +interface TransformIndicesProps { + indexService: IndexService; + sourceIndex: { label: string; value?: IndexItem }[]; + sourceIndexError: string; + targetIndex: { label: string; value?: IndexItem }[]; + targetIndexError: string; + onChangeSourceIndex: (options: EuiComboBoxOptionOption[]) => void; + onChangeTargetIndex: (options: EuiComboBoxOptionOption[]) => void; + hasAggregation: boolean; +} + +interface TransformIndicesState { + isLoading: boolean; + indexOptions: { label: string; value?: IndexItem }[]; + targetIndexOptions: { label: string; value?: IndexItem }[]; +} + +export default class TransformIndices extends Component { + static contextType = CoreServicesContext; + constructor(props: TransformIndicesProps) { + super(props); + this.state = { + isLoading: true, + indexOptions: [], + targetIndexOptions: [], + }; + + this.onIndexSearchChange = _.debounce(this.onIndexSearchChange, 500, { leading: true }); + } + + async componentDidMount(): Promise { + await this.onIndexSearchChange(""); + } + + onIndexSearchChange = async (searchValue: string): Promise => { + const { indexService } = this.props; + this.setState({ isLoading: true, indexOptions: [] }); + try { + const queryObject = { from: 0, size: 10, search: searchValue, sortDirection: "desc", sortField: "index" }; + const getIndicesResponse = await indexService.getIndices(queryObject); + if (getIndicesResponse.ok) { + const options = searchValue.trim() ? [{ label: `${searchValue}*` }] : []; + const indices = getIndicesResponse.response.indices.map((index: IndexItem) => ({ + label: index.index, + })); + this.setState({ indexOptions: options.concat(indices), targetIndexOptions: indices }); + } else { + if (getIndicesResponse.error.startsWith("[index_not_found_exception]")) { + this.context.notifications.toasts.addDanger("No index available"); + } else { + this.context.notifications.toasts.addDanger(getIndicesResponse.error); + } + } + } catch (err) { + this.context.notifications.toasts.addDanger(err.message); + } + + this.setState({ isLoading: false }); + }; + + onCreateOption = (searchValue: string, flattenedOptions: { label: string; value?: IndexItem }[]): void => { + const { targetIndexOptions } = this.state; + const { onChangeTargetIndex } = this.props; + const normalizedSearchValue = searchValue.trim(); + + if (!normalizedSearchValue) { + return; + } + + const newOption = { + label: searchValue, + }; + + // Create the option if it doesn't exist. + if (flattenedOptions.findIndex((option) => option.label.trim() === normalizedSearchValue) === -1) { + targetIndexOptions.concat(newOption); + this.setState({ targetIndexOptions: targetIndexOptions }); + } + onChangeTargetIndex([newOption]); + }; + + render() { + const { + sourceIndex, + sourceIndexError, + targetIndex, + targetIndexError, + onChangeSourceIndex, + onChangeTargetIndex, + hasAggregation, + } = this.props; + const { isLoading, indexOptions, targetIndexOptions } = this.state; + return ( + +
+ + +

You can't change indices after creating a job. Double-check the source and target index names before proceeding.

+
+ {hasAggregation && ( + + + +

Note: changing source index will erase all existing definitions about aggregations and metrics.

+
+
+ )} + + + + + + + + +
+
+ ); + } +} diff --git a/public/pages/CreateTransform/components/TransformIndices/index.ts b/public/pages/CreateTransform/components/TransformIndices/index.ts new file mode 100644 index 000000000..ffee9e56f --- /dev/null +++ b/public/pages/CreateTransform/components/TransformIndices/index.ts @@ -0,0 +1,18 @@ +/* + * Copyright 2021 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"). + * You may not use this file except in compliance with the License. + * A copy of the License is located at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * or in the "license" file accompanying this file. This file is distributed + * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either + * express or implied. See the License for the specific language governing + * permissions and limitations under the License. + */ + +import TransformIndices from "./TransformIndices"; + +export default TransformIndices; diff --git a/public/pages/CreateTransform/containers/CreateTransform/CreateTransform.tsx b/public/pages/CreateTransform/containers/CreateTransform/CreateTransform.tsx new file mode 100644 index 000000000..05b223724 --- /dev/null +++ b/public/pages/CreateTransform/containers/CreateTransform/CreateTransform.tsx @@ -0,0 +1,73 @@ +/* + * Copyright 2021 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"). + * You may not use this file except in compliance with the License. + * A copy of the License is located at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * or in the "license" file accompanying this file. This file is distributed + * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either + * express or implied. See the License for the specific language governing + * permissions and limitations under the License. + */ + +import React, { ChangeEvent, Component } from "react"; +import { EuiSpacer, EuiTitle, EuiFlexGroup, EuiFlexItem, EuiComboBoxOptionOption } from "@elastic/eui"; +import { RouteComponentProps } from "react-router-dom"; +import { TransformService } from "../../../../services"; +import ConfigureTransform from "../../components/ConfigureTransform"; +import TransformIndices from "../../components/TransformIndices"; +import CreateTransformSteps from "../../components/CreateTransformSteps"; +import IndexService from "../../../../services/IndexService"; +import { IndexItem } from "../../../../../models/interfaces"; + +interface CreateTransformProps extends RouteComponentProps { + transformService: TransformService; + indexService: IndexService; + transformId: string; + transformIdError: string; + submitError: string; + isSubmitting: boolean; + hasSubmitted: boolean; + description: string; + sourceIndex: { label: string; value?: IndexItem }[]; + sourceIndexError: string; + targetIndex: { label: string; value?: IndexItem }[]; + targetIndexError: string; + onChangeName: (e: ChangeEvent) => void; + onChangeDescription: (value: ChangeEvent) => void; + onChangeSourceIndex: (options: EuiComboBoxOptionOption[]) => void; + onChangeTargetIndex: (options: EuiComboBoxOptionOption[]) => void; + currentStep: number; + hasAggregation: boolean; +} + +export default class CreateTransform extends Component { + render() { + if (this.props.currentStep !== 1) { + return null; + } + + return ( +
+ + + + + + +

Set up indices

+
+ + + + +
+
+ +
+ ); + } +} diff --git a/public/pages/CreateTransform/containers/CreateTransform/index.ts b/public/pages/CreateTransform/containers/CreateTransform/index.ts new file mode 100644 index 000000000..b01f74a8a --- /dev/null +++ b/public/pages/CreateTransform/containers/CreateTransform/index.ts @@ -0,0 +1,18 @@ +/* + * Copyright 2021 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"). + * You may not use this file except in compliance with the License. + * A copy of the License is located at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * or in the "license" file accompanying this file. This file is distributed + * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either + * express or implied. See the License for the specific language governing + * permissions and limitations under the License. + */ + +import CreateTransform from "./CreateTransform"; + +export default CreateTransform; diff --git a/public/pages/CreateTransform/containers/CreateTransformForm/CreateTransformForm.tsx b/public/pages/CreateTransform/containers/CreateTransformForm/CreateTransformForm.tsx new file mode 100644 index 000000000..16c135c2c --- /dev/null +++ b/public/pages/CreateTransform/containers/CreateTransformForm/CreateTransformForm.tsx @@ -0,0 +1,411 @@ +/* + * Copyright 2021 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"). + * You may not use this file except in compliance with the License. + * A copy of the License is located at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * or in the "license" file accompanying this file. This file is distributed + * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either + * express or implied. See the License for the specific language governing + * permissions and limitations under the License. + */ + +import React, { ChangeEvent, Component } from "react"; +import { EuiButton, EuiButtonEmpty, EuiComboBoxOptionOption, EuiFlexGroup, EuiFlexItem } from "@elastic/eui"; +import { RouteComponentProps } from "react-router-dom"; +import moment from "moment"; +import { TransformService } from "../../../../services"; +import { BREADCRUMBS, ROUTES } from "../../../../utils/constants"; +import IndexService from "../../../../services/IndexService"; +import { ManagedCatIndex } from "../../../../../server/models/interfaces"; +import CreateTransform from "../CreateTransform"; +import CreateTransformStep2 from "../CreateTransformStep2"; +import { DimensionItem, FieldItem, IndexItem, MetricItem, Transform } from "../../../../../models/interfaces"; +import { getErrorMessage } from "../../../../utils/helpers"; +import { EMPTY_TRANSFORM } from "../../utils/constants"; +import CreateTransformStep3 from "../CreateTransformStep3"; +import CreateTransformStep4 from "../CreateTransformStep4"; +import { compareFieldItem, parseFieldOptions } from "../../utils/helpers"; +import { CoreServicesContext } from "../../../../components/core_services"; + +interface CreateTransformFormProps extends RouteComponentProps { + transformService: TransformService; + indexService: IndexService; +} + +interface CreateTransformFormState { + currentStep: number; + transformId: string; + transformIdError: string; + transformSeqNo: number | null; + transformPrimaryTerm: number | null; + submitError: string; + isSubmitting: boolean; + hasSubmitted: boolean; + loadingIndices: boolean; + indices: ManagedCatIndex[]; + totalIndices: number; + + description: string; + sourceIndex: { label: string; value?: IndexItem }[]; + sourceIndexError: string; + targetIndex: { label: string; value?: IndexItem }[]; + targetIndexError: string; + + mappings: any; + allMappings: FieldItem[][]; + fields: FieldItem[]; + selectedTerms: FieldItem[]; + selectedDimensionField: DimensionItem[]; + selectedMetrics: MetricItem[]; + metricError: string; + timestamp: EuiComboBoxOptionOption[]; + timestampError: string; + intervalType: string; + intervalValue: number; + timezone: string; + timeunit: string; + selectedFields: FieldItem[]; + jobEnabledByDefault: boolean; + + continuousJob: string; + continuousDefinition: string; + interval: number; + intervalError: string; + intervalTimeunit: string; + cronExpression: string; + cronTimezone: string; + pageSize: number; + delayTime: number | undefined; + delayTimeunit: string; + transformJSON: any; +} + +export default class CreateTransformForm extends Component { + static contextType = CoreServicesContext; + + constructor(props: CreateTransformFormProps) { + super(props); + + this.state = { + currentStep: 1, + transformSeqNo: null, + transformPrimaryTerm: null, + transformId: "", + transformIdError: "", + submitError: "", + isSubmitting: false, + hasSubmitted: false, + loadingIndices: true, + indices: [], + totalIndices: 0, + + mappings: "", + allMappings: [], + fields: [], + description: "", + + sourceIndex: [], + sourceIndexError: "", + targetIndex: [], + targetIndexError: "", + + jobEnabledByDefault: true, + pageSize: 1000, + transformJSON: JSON.parse(EMPTY_TRANSFORM), + }; + this._next = this._next.bind(this); + this._prev = this._prev.bind(this); + } + + componentDidMount = async (): Promise => { + this.context.chrome.setBreadcrumbs([BREADCRUMBS.INDEX_MANAGEMENT, BREADCRUMBS.TRANSFORMS, BREADCRUMBS.CREATE_TRANSFORM]); + }; + + getMappings = async (srcIndex: string): Promise => { + if (!srcIndex.length) return; + try { + const { transformService } = this.props; + const response = await transformService.getMappings(srcIndex); + if (response.ok) { + let allMappings: FieldItem[][] = []; + const mappings = response.response; + //Push mappings array to allMappings 2D array first + for (let index in mappings) { + allMappings.push(parseFieldOptions("", mappings[index].mappings.properties)); + } + //Find intersect from all mappings + const fields = allMappings.reduce((mappingA, mappingB) => + mappingA.filter((itemA) => mappingB.some((itemB) => compareFieldItem(itemA, itemB))) + ); + this.setState({ mappings, fields, allMappings }); + } else { + this.context.notifications.toasts.addDanger(`Could not load fields: ${response.error}`); + } + } catch (err) { + this.context.notifications.toasts.addDanger(getErrorMessage(err, "Could not load fields")); + } + }; + + _next() { + let currentStep = this.state.currentStep; + let error = false; + //Verification here + if (currentStep == 1) { + const { transformId, sourceIndex, targetIndex } = this.state; + + if (!transformId) { + this.setState({ submitError: "Job name is required.", transformIdError: "Job name is required." }); + error = true; + } + if (sourceIndex.length == 0) { + this.setState({ submitError: "Source index is required.", sourceIndexError: "Source index is required." }); + error = true; + } + if (targetIndex.length == 0) { + this.setState({ submitError: "Target index is required.", targetIndexError: "Target index is required." }); + error = true; + } + } else if (currentStep == 2) { + } else if (currentStep == 3) { + //Check if interval is a valid value and is specified. + const { intervalError, continuousDefinition } = this.state; + if (continuousDefinition == "fixed") { + if (intervalError != "") { + const intervalErrorMsg = "Interval value is required."; + this.setState({ submitError: intervalErrorMsg, intervalError: intervalErrorMsg }); + error = true; + } + } + } + + if (error) return; + + currentStep = currentStep >= 3 ? 4 : currentStep + 1; + + this.setState({ + submitError: "", + currentStep: currentStep, + }); + } + + _prev() { + let currentStep = this.state.currentStep ; + // If the current step is 2 or 3, then subtract one on "previous" button click + currentStep = currentStep <= 1 ? 1 : currentStep - 1; + this.setState({ + currentStep: currentStep, + }); + } + + onChangeStep = (step: number): void => { + if (step > 3) return; + this.setState({ + currentStep: step, + }); + }; + + onChangeDescription = (e: ChangeEvent): void => { + const description = e.target.value; + let newJSON = this.state.transformJSON; + newJSON.transform.description = description; + this.setState({ description: description, transformJSON: newJSON }); + }; + + onChangeName = (e: ChangeEvent): void => { + const transformId = e.target.value; + this.setState({ transformId, transformIdError: transformId ? "" : "Name is required" }); + }; + + onChangeSourceIndex = async (options: EuiComboBoxOptionOption[]): Promise => { + let newJSON = this.state.transformJSON; + let sourceIndex = options.map(function (option) { + return option.label; + }); + const sourceIndexError = sourceIndex.length ? "" : "Source index is required"; + const srcIndexText = sourceIndex.length ? sourceIndex[0] : ""; + newJSON.transform.source_index = srcIndexText; + this.setState({ sourceIndex: options, transformJSON: newJSON, sourceIndexError: sourceIndexError }); + this.setState({ + selectedDimensionField: [], + selectedMetrics: [], + }); + await this.getMappings(srcIndexText); + }; + + onChangeTargetIndex = (options: EuiComboBoxOptionOption[]): void => { + //Try to get label text from option from the only array element in options, if exists + let newJSON = this.state.transformJSON; + let targetIndex = options.map(function (option) { + return option.label; + }); + + const targetIndexError = targetIndex.length ? "" : "Target index is required"; + + newJSON.transform.target_index = targetIndex[0]; + this.setState({ targetIndex: options, transformJSON: newJSON, targetIndexError: targetIndexError }); + }; + + setDateHistogram = (): void => { + const { intervalType, intervalValue, timeunit } = this.state; + let newJSON = this.state.transformJSON; + if (intervalType == "calendar") { + newJSON.transform.dimensions[0].date_histogram.calendar_interval = `1${timeunit}`; + delete newJSON.transform.dimensions[0].date_histogram["fixed_interval"]; + } else { + newJSON.transform.dimensions[0].date_histogram.fixed_interval = `${intervalValue}${timeunit}`; + delete newJSON.transform.dimensions[0].date_histogram["calendar_interval"]; + } + this.setState({ transformJSON: newJSON }); + }; + + onChangeJobEnabledByDefault = (): void => { + const checked = this.state.jobEnabledByDefault; + let newJSON = this.state.transformJSON; + newJSON.transform.enabled = !checked; + this.setState({ jobEnabledByDefault: !checked, transformJSON: newJSON }); + }; + + onChangePage = (e: ChangeEvent): void => { + let newJSON = this.state.transformJSON; + newJSON.transform.page_size = e.target.valueAsNumber; + this.setState({ pageSize: e.target.valueAsNumber, transformJSON: newJSON }); + }; + + onSubmit = async (): Promise => { + const { transformId, transformJSON } = this.state; + this.setState({ submitError: "", isSubmitting: true, hasSubmitted: true }); + try { + if (!transformId) { + this.setState({ transformIdError: "Required" }); + } else { + this.setDateHistogram(); + await this.onCreate(transformId, transformJSON); + } + } catch (err) { + this.context.notifications.toasts.addDanger("Invalid Transform JSON"); + console.error(err); + } + + this.setState({ isSubmitting: false }); + }; + + onCancel = (): void => { + this.props.history.push(ROUTES.TRANSFORMS); + }; + + onCreate = async (transformId: string, transform: Transform): Promise => { + const { transformService } = this.props; + try { + const response = await transformService.putTransform(transform, transformId); + if (response.ok) { + this.context.notifications.toasts.addSuccess(`Created transform: ${response.response._id}`); + this.props.history.push(ROUTES.TRANSFORMS); + } else { + this.setState({ submitError: response.error }); + this.context.notifications.toasts.addDanger(`Failed to create transform: ${response.error}`); + } + } catch (err) { + this.setState({ submitError: getErrorMessage(err, "There was a problem creating the transform job") }); + this.context.notifications.toasts.addDanger( + `Failed to create transform: ${getErrorMessage(err, "There was a problem creating the transform job")}` + ); + } + }; + + render() { + const { + transformId, + transformIdError, + submitError, + isSubmitting, + hasSubmitted, + description, + sourceIndex, + sourceIndexError, + targetIndex, + targetIndexError, + currentStep, + + jobEnabledByDefault, + pageSize, + } = this.state; + return ( +
+ + + + + + + + Cancel + + + {currentStep != 1 && ( + + + Previous + + + )} + + {currentStep == 4 ? ( + + + Create + + + ) : ( + + + Next + + + )} + + + ); + } +} diff --git a/public/pages/CreateTransform/containers/CreateTransformForm/index.ts b/public/pages/CreateTransform/containers/CreateTransformForm/index.ts new file mode 100644 index 000000000..d933dd68a --- /dev/null +++ b/public/pages/CreateTransform/containers/CreateTransformForm/index.ts @@ -0,0 +1,18 @@ +/* + * Copyright 2021 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"). + * You may not use this file except in compliance with the License. + * A copy of the License is located at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * or in the "license" file accompanying this file. This file is distributed + * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either + * express or implied. See the License for the specific language governing + * permissions and limitations under the License. + */ + +import CreateTransformForm from "./CreateTransformForm"; + +export default CreateTransformForm; diff --git a/public/pages/CreateTransform/containers/CreateTransformStep2/CreateTransformStep2.tsx b/public/pages/CreateTransform/containers/CreateTransformStep2/CreateTransformStep2.tsx new file mode 100644 index 000000000..9ad07d434 --- /dev/null +++ b/public/pages/CreateTransform/containers/CreateTransformStep2/CreateTransformStep2.tsx @@ -0,0 +1,65 @@ +/* + * Copyright 2021 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"). + * You may not use this file except in compliance with the License. + * A copy of the License is located at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * or in the "license" file accompanying this file. This file is distributed + * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either + * express or implied. See the License for the specific language governing + * permissions and limitations under the License. + */ + +import React, { ChangeEvent, Component } from "react"; +import { EuiSpacer, EuiTitle, EuiFlexGroup, EuiFlexItem, EuiCallOut, EuiComboBoxOptionOption } from "@elastic/eui"; +import { RouteComponentProps } from "react-router-dom"; +import { TransformService } from "../../../../services"; +import { BREADCRUMBS, ROUTES } from "../../../../utils/constants"; +import CreateTransformSteps from "../../components/CreateTransformSteps"; +import { DimensionItem, FieldItem, MetricItem } from "../../../../../models/interfaces"; +import { CoreServicesContext } from "../../../../components/core_services"; + +interface CreateTransformStep2Props extends RouteComponentProps { + transformService: TransformService; + currentStep: number; +} + +export default class CreateTransformStep2 extends Component { + static contextType = CoreServicesContext; + constructor(props: CreateTransformStep2Props) { + super(props); + } + + componentDidMount = async (): Promise => { + this.context.chrome.setBreadcrumbs([BREADCRUMBS.INDEX_MANAGEMENT, BREADCRUMBS.TRANSFORMS]); + }; + + onCancel = (): void => { + this.props.history.push(ROUTES.TRANSFORMS); + }; + + render() { + if (this.props.currentStep !== 2) return null; + const { fields, timestamp } = this.props; + + return ( +
+ + + + + + +

Definition Placeholder

+
+ +
+
+ +
+ ); + } +} diff --git a/public/pages/CreateTransform/containers/CreateTransformStep2/index.ts b/public/pages/CreateTransform/containers/CreateTransformStep2/index.ts new file mode 100644 index 000000000..1b7f6dbd7 --- /dev/null +++ b/public/pages/CreateTransform/containers/CreateTransformStep2/index.ts @@ -0,0 +1,18 @@ +/* + * Copyright 2021 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"). + * You may not use this file except in compliance with the License. + * A copy of the License is located at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * or in the "license" file accompanying this file. This file is distributed + * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either + * express or implied. See the License for the specific language governing + * permissions and limitations under the License. + */ + +import CreateTransformStep2 from "./CreateTransformStep2"; + +export default CreateTransformStep2; diff --git a/public/pages/CreateTransform/containers/CreateTransformStep3/CreateTransformStep3.tsx b/public/pages/CreateTransform/containers/CreateTransformStep3/CreateTransformStep3.tsx new file mode 100644 index 000000000..c359130ef --- /dev/null +++ b/public/pages/CreateTransform/containers/CreateTransformStep3/CreateTransformStep3.tsx @@ -0,0 +1,146 @@ +/* + * Copyright 2020 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"). + * You may not use this file except in compliance with the License. + * A copy of the License is located at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * or in the "license" file accompanying this file. This file is distributed + * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either + * express or implied. See the License for the specific language governing + * permissions and limitations under the License. + */ + +import React, { ChangeEvent, Component } from "react"; +import { EuiSpacer, EuiTitle, EuiFlexGroup, EuiFlexItem } from "@elastic/eui"; +import { RouteComponentProps } from "react-router-dom"; +import { TransformService } from "../../../../services"; +import { BREADCRUMBS, ROUTES } from "../../../../utils/constants"; +import { getErrorMessage } from "../../../../utils/helpers"; +import { Transform } from "../../../../../models/interfaces"; +import CreateTransformSteps from "../../components/CreateTransformSteps"; +import Schedule from "../../components/Schedule"; +import { CoreServicesContext } from "../../../../components/core_services"; + +interface CreateTransformProps extends RouteComponentProps { + transformService: TransformService; + currentStep: number; + jobEnabledByDefault: boolean; + pageSize: number; + onChangeJobEnabledByDefault: () => void; + onChangePage: (e: ChangeEvent) => void; +} + +interface CreateTransformState { + transformId: string; + transformIdError: string; + transformSeqNo: number | null; + transformPrimaryTerm: number | null; + submitError: string; + isSubmitting: boolean; + hasSubmitted: boolean; +} + +export default class CreateTransformStep3 extends Component { + static contextType = CoreServicesContext; + constructor(props: CreateTransformProps) { + super(props); + + this.state = { + transformSeqNo: null, + transformPrimaryTerm: null, + transformId: "", + transformIdError: "", + submitError: "", + isSubmitting: false, + hasSubmitted: false, + }; + } + + componentDidMount = async (): Promise => { + this.context.chrome.setBreadcrumbs([BREADCRUMBS.INDEX_MANAGEMENT, BREADCRUMBS.TRANSFORMS]); + }; + + onCreate = async (transformId: string, transform: Transform): Promise => { + const { transformService } = this.props; + try { + const response = await transformService.putTransform(transform, transformId); + if (response.ok) { + this.context.notifications.toasts.addSuccess(`Created transform: ${response.response._id}`); + this.props.history.push(ROUTES.TRANSFORMS); + } else { + this.setState({ submitError: response.error }); + } + } catch (err) { + this.setState({ submitError: getErrorMessage(err, "There was a problem creating the transform") }); + } + }; + + onUpdate = async (transformId: string, transform: Transform): Promise => { + try { + const { transformService } = this.props; + const { transformPrimaryTerm, transformSeqNo } = this.state; + if (transformSeqNo == null || transformPrimaryTerm == null) { + this.context.notifications.toasts.addDanger("Could not update transform without seqNo and primaryTerm"); + return; + } + const response = await transformService.putTransform(transform, transformId, transformSeqNo, transformPrimaryTerm); + if (response.ok) { + this.context.notifications.toasts.addSuccess(`Updated transform: ${response.response._id}`); + this.props.history.push(ROUTES.TRANSFORMS); + } else { + this.setState({ submitError: response.error }); + } + } catch (err) { + this.setState({ submitError: getErrorMessage(err, "There was a problem updating the transform") }); + } + }; + + onCancel = (): void => { + this.props.history.push(ROUTES.TRANSFORMS); + }; + + onChange = (e: ChangeEvent): void => { + const { hasSubmitted } = this.state; + const transformId = e.target.value; + if (hasSubmitted) this.setState({ transformId, transformIdError: transformId ? "" : "Required" }); + else this.setState({ transformId }); + }; + + render() { + if (this.props.currentStep != 3) return null; + const { + jobEnabledByDefault, + pageSize, + onChangePage, + } = this.props; + const { transformId, transformIdError } = this.state; + return ( +
+ + + + + + +

Specify schedule

+
+ + +
+
+ +
+ ); + } +} diff --git a/public/pages/CreateTransform/containers/CreateTransformStep3/index.ts b/public/pages/CreateTransform/containers/CreateTransformStep3/index.ts new file mode 100644 index 000000000..c2626fe72 --- /dev/null +++ b/public/pages/CreateTransform/containers/CreateTransformStep3/index.ts @@ -0,0 +1,18 @@ +/* + * Copyright 2020 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"). + * You may not use this file except in compliance with the License. + * A copy of the License is located at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * or in the "license" file accompanying this file. This file is distributed + * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either + * express or implied. See the License for the specific language governing + * permissions and limitations under the License. + */ + +import CreateTransformStep3 from "./CreateTransformStep3"; + +export default CreateTransformStep3; diff --git a/public/pages/CreateTransform/containers/CreateTransformStep4/CreateTransformStep4.tsx b/public/pages/CreateTransform/containers/CreateTransformStep4/CreateTransformStep4.tsx new file mode 100644 index 000000000..328533609 --- /dev/null +++ b/public/pages/CreateTransform/containers/CreateTransformStep4/CreateTransformStep4.tsx @@ -0,0 +1,84 @@ +/* + * Copyright 2021 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"). + * You may not use this file except in compliance with the License. + * A copy of the License is located at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * or in the "license" file accompanying this file. This file is distributed + * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either + * express or implied. See the License for the specific language governing + * permissions and limitations under the License. + */ + +import React, { Component } from "react"; +import { EuiSpacer, EuiTitle, EuiFlexGroup, EuiFlexItem, EuiComboBoxOptionOption, EuiCallOut } from "@elastic/eui"; +import { RouteComponentProps } from "react-router-dom"; +import { TransformService } from "../../../../services"; +import { BREADCRUMBS, ROUTES } from "../../../../utils/constants"; +import { DimensionItem, IndexItem, MetricItem } from "../../../../../models/interfaces"; +import CreateTransformSteps from "../../components/CreateTransformSteps"; +import JobNameAndIndices from "../../components/JobNameAndIndices"; +import { CoreServicesContext } from "../../../../components/core_services"; + +interface CreateTransformProps extends RouteComponentProps { + transformService: TransformService; + submitError: string; + currentStep: number; + onChangeStep: (step: number) => void; + transformId: string; + description: string; + sourceIndex: { label: string; value?: IndexItem }[]; + targetIndex: { label: string; value?: IndexItem }[]; + + timestamp: EuiComboBoxOptionOption[]; + timezone: string; + timeunit: string; + + jobEnabledByDefault: boolean; + pageSize: number; +} + +export default class CreateTransformStep4 extends Component { + static contextType = CoreServicesContext; + constructor(props: CreateTransformProps) { + super(props); + } + + componentDidMount = async (): Promise => { + this.context.chrome.setBreadcrumbs([BREADCRUMBS.INDEX_MANAGEMENT, BREADCRUMBS.TRANSFORMS]); + }; + + onCancel = (): void => { + this.props.history.push(ROUTES.TRANSFORMS); + }; + + render() { + if (this.props.currentStep != 4) return null; + + return ( +
+ + + + + + +

Review and create

+
+ + + + + +

You can't change aggregations or metrics after creating a job. Double-check your choices before proceeding.

+
+
+
+ +
+ ); + } +} diff --git a/public/pages/CreateTransform/containers/CreateTransformStep4/index.ts b/public/pages/CreateTransform/containers/CreateTransformStep4/index.ts new file mode 100644 index 000000000..379985a8a --- /dev/null +++ b/public/pages/CreateTransform/containers/CreateTransformStep4/index.ts @@ -0,0 +1,18 @@ +/* + * Copyright 2021 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"). + * You may not use this file except in compliance with the License. + * A copy of the License is located at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * or in the "license" file accompanying this file. This file is distributed + * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either + * express or implied. See the License for the specific language governing + * permissions and limitations under the License. + */ + +import CreateTransformStep4 from "./CreateTransformStep4"; + +export default CreateTransformStep4; diff --git a/public/pages/CreateTransform/index.ts b/public/pages/CreateTransform/index.ts new file mode 100644 index 000000000..2ca67cce6 --- /dev/null +++ b/public/pages/CreateTransform/index.ts @@ -0,0 +1,18 @@ +/* + * Copyright 2021 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"). + * You may not use this file except in compliance with the License. + * A copy of the License is located at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * or in the "license" file accompanying this file. This file is distributed + * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either + * express or implied. See the License for the specific language governing + * permissions and limitations under the License. + */ + +import CreateTransform from "./containers/CreateTransform"; + +export default CreateTransform; diff --git a/public/pages/CreateTransform/utils/constants.ts b/public/pages/CreateTransform/utils/constants.ts new file mode 100644 index 000000000..008f1bf34 --- /dev/null +++ b/public/pages/CreateTransform/utils/constants.ts @@ -0,0 +1,88 @@ +/* + * Copyright 2021 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"). + * You may not use this file except in compliance with the License. + * A copy of the License is located at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * or in the "license" file accompanying this file. This file is distributed + * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either + * express or implied. See the License for the specific language governing + * permissions and limitations under the License. + */ + +export const EMPTY_TRANSFORM = JSON.stringify({ + transform: { + continuous: false, + description: "", + dimensions: [ + { + date_histogram: { + source_field: "", + fixed_interval: "1h", + timezone: "UTC", + }, + }, + ], + enabled: true, + metrics: [], + page_size: 1000, + roles: [], + schedule: { + interval: { + start_time: 234802, + period: 1, + unit: "MINUTES", + }, + }, + source_index: "", + target_index: "", + }, +}); + +export const FixedTimeunitOptions = [ + { value: "ms", text: "Millisecond(s)" }, + { value: "s", text: "Second(s)" }, + { value: "m", text: "Minute(s)" }, + { value: "h", text: "Hour(s)" }, + { value: "d", text: "Day(s)" }, +]; + +export const DelayTimeunitOptions = [ + { value: "SECONDS", text: "Second(s)" }, + { value: "MINUTES", text: "Minute(s)" }, + { value: "HOURS", text: "Hour(s)" }, + { value: "DAYS", text: "Day(s)" }, +]; + +export const CalendarTimeunitOptions = [ + { value: "m", text: "Minute" }, + { value: "h", text: "Hour" }, + { value: "d", text: "Day" }, + { value: "w", text: "Week" }, + { value: "M", text: "Month" }, + { value: "q", text: "Quarter" }, + { value: "y", text: "Year" }, +]; + +export const ScheduleIntervalTimeunitOptions = [ + { value: "MINUTES", text: "Minute(s)" }, + { value: "HOURS", text: "Hour(s)" }, + { value: "DAYS", text: "Day(s)" }, +]; + +export const AddFieldsColumns = [ + { + field: "label", + name: "Field name", + sortable: true, + }, + { + field: "type", + name: "Field type", + sortable: true, + render: (type: string | undefined) => (type == null || type == undefined ? "-" : type), + }, +]; diff --git a/public/pages/CreateTransform/utils/helpers.ts b/public/pages/CreateTransform/utils/helpers.ts new file mode 100644 index 000000000..8e66b67fc --- /dev/null +++ b/public/pages/CreateTransform/utils/helpers.ts @@ -0,0 +1,63 @@ +/* + * Copyright 2021 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"). + * You may not use this file except in compliance with the License. + * A copy of the License is located at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * or in the "license" file accompanying this file. This file is distributed + * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either + * express or implied. See the License for the specific language governing + * permissions and limitations under the License. + */ + +import { FieldItem } from "../../../../models/interfaces"; + +export const parseTimeunit = (timeunit: string): string => { + if (timeunit == "ms" || timeunit == "Milliseconds") return "millisecond(s)"; + else if (timeunit == "SECONDS" || timeunit == "s" || timeunit == "Seconds") return "second(s)"; + else if (timeunit == "MINUTES" || timeunit == "m" || timeunit == "Minutes") return "minute(s)"; + else if (timeunit == "HOURS" || timeunit == "h" || timeunit == "Hours") return "hour(s)"; + else if (timeunit == "DAYS" || timeunit == "d" || timeunit == "Days") return "day(s)"; + else if (timeunit == "w") return "week"; + else if (timeunit == "M") return "month"; + else if (timeunit == "q") return "quarter"; + else if (timeunit == "y") return "year"; + + return timeunit; +}; + +//Returns true if field type is numeric +export const isNumericMapping = (fieldType: string | undefined): boolean => { + return ( + fieldType == "long" || + fieldType == "integer" || + fieldType == "short" || + fieldType == "byte" || + fieldType == "double" || + fieldType == "float" || + fieldType == "half_float" || + fieldType == "scaled_float" + ); +}; + +export const compareFieldItem = (itemA: FieldItem, itemB: FieldItem): boolean => { + return itemB.label == itemA.label && itemA.type == itemB.type; +}; + +export const parseFieldOptions = (prefix: string, mappings: any): FieldItem[] => { + let fieldsOption: FieldItem[] = []; + for (let field in mappings) { + if (mappings.hasOwnProperty(field)) { + if (mappings[field].type != "object" && mappings[field].type != null && mappings[field].type != "nested") + fieldsOption.push({ label: prefix + field, type: mappings[field].type }); + if (mappings[field].fields != null) + fieldsOption = fieldsOption.concat(parseFieldOptions(prefix + field + ".", mappings[field].fields)); + if (mappings[field].properties != null) + fieldsOption = fieldsOption.concat(parseFieldOptions(prefix + field + ".", mappings[field].properties)); + } + } + return fieldsOption; +}; diff --git a/public/pages/Main/Main.tsx b/public/pages/Main/Main.tsx index c48320e83..8c6fde960 100644 --- a/public/pages/Main/Main.tsx +++ b/public/pages/Main/Main.tsx @@ -30,6 +30,7 @@ import { BrowserServices } from "../../models/interfaces"; import { ROUTES } from "../../utils/constants"; import { CoreServicesConsumer } from "../../components/core_services"; import CreateRollupForm from "../CreateRollup/containers/CreateRollupForm"; +import CreateTransformForm from "../CreateTransform/containers/CreateTransformForm"; import EditRollup from "../EditRollup/containers"; import RollupDetails from "../RollupDetails/containers/RollupDetails"; import { EditTransform, Transforms } from "../Transforms"; @@ -210,7 +211,7 @@ export default class Main extends Component { path={ROUTES.CREATE_TRANSFORM} render={(props: RouteComponentProps) => (
- +
)} /> From 3820a54cb7352bdd9a8414f56e6c85336f57224d Mon Sep 17 00:00:00 2001 From: Annie Date: Wed, 28 Apr 2021 18:00:32 -0700 Subject: [PATCH 15/93] Add dependency to rollupService to get mappings --- .../DefineTransforms/DefineTransforms.tsx | 4 ++-- .../CreateTransformForm/CreateTransformForm.tsx | 8 ++++---- .../CreateTransformStep2/CreateTransformStep2.tsx | 2 ++ public/pages/Main/Main.tsx | 13 +++++++++---- 4 files changed, 17 insertions(+), 10 deletions(-) diff --git a/public/pages/CreateTransform/components/DefineTransforms/DefineTransforms.tsx b/public/pages/CreateTransform/components/DefineTransforms/DefineTransforms.tsx index bb44fff01..0373f0701 100644 --- a/public/pages/CreateTransform/components/DefineTransforms/DefineTransforms.tsx +++ b/public/pages/CreateTransform/components/DefineTransforms/DefineTransforms.tsx @@ -27,7 +27,7 @@ interface DefineTransformsState {} export default class DefineTransforms extends Component { constructor(props: DefineTransformsProps) { super(props); - const { transfromId, sourceIndex } = this.props; + const { transfromId } = this.props; this.state = {}; } @@ -67,7 +67,7 @@ export default class DefineTransforms extends Component{`Viewing sample data from index ${sourceIndex}, filtered by order.type:sales_order, order.success:true`}

{/**/} ); diff --git a/public/pages/CreateTransform/containers/CreateTransformForm/CreateTransformForm.tsx b/public/pages/CreateTransform/containers/CreateTransformForm/CreateTransformForm.tsx index f3294d122..4b182c00e 100644 --- a/public/pages/CreateTransform/containers/CreateTransformForm/CreateTransformForm.tsx +++ b/public/pages/CreateTransform/containers/CreateTransformForm/CreateTransformForm.tsx @@ -16,8 +16,7 @@ import React, { ChangeEvent, Component } from "react"; import { EuiButton, EuiButtonEmpty, EuiComboBoxOptionOption, EuiFlexGroup, EuiFlexItem } from "@elastic/eui"; import { RouteComponentProps } from "react-router-dom"; -import moment from "moment"; -import { TransformService } from "../../../../services"; +import { RollupService, TransformService } from "../../../../services"; import { BREADCRUMBS, ROUTES } from "../../../../utils/constants"; import IndexService from "../../../../services/IndexService"; import { ManagedCatIndex } from "../../../../../server/models/interfaces"; @@ -32,6 +31,7 @@ import { compareFieldItem, parseFieldOptions } from "../../utils/helpers"; import { CoreServicesContext } from "../../../../components/core_services"; interface CreateTransformFormProps extends RouteComponentProps { + rollupService: RollupService; transformService: TransformService; indexService: IndexService; } @@ -128,8 +128,8 @@ export default class CreateTransformForm extends Component => { if (!srcIndex.length) return; try { - const { transformService } = this.props; - const response = await transformService.getMappings(srcIndex); + const { rollupService } = this.props; + const response = await rollupService.getMappings(srcIndex); if (response.ok) { let allMappings: FieldItem[][] = []; const mappings = response.response; diff --git a/public/pages/CreateTransform/containers/CreateTransformStep2/CreateTransformStep2.tsx b/public/pages/CreateTransform/containers/CreateTransformStep2/CreateTransformStep2.tsx index d616e13ea..e0bdb1196 100644 --- a/public/pages/CreateTransform/containers/CreateTransformStep2/CreateTransformStep2.tsx +++ b/public/pages/CreateTransform/containers/CreateTransformStep2/CreateTransformStep2.tsx @@ -48,6 +48,8 @@ export default class CreateTransformStep2 extends Component diff --git a/public/pages/Main/Main.tsx b/public/pages/Main/Main.tsx index 4f389e157..c093a4082 100644 --- a/public/pages/Main/Main.tsx +++ b/public/pages/Main/Main.tsx @@ -93,7 +93,7 @@ export default class Main extends Component { name: Navigation.Transforms, id: 5, href: `#${Pathname.Transforms}`, - isSelected: pathname === Pathname.Transforms + isSelected: pathname === Pathname.Transforms, }, ], }, @@ -196,9 +196,9 @@ export default class Main extends Component { /> ( + render={(props: RouteComponentProps) => (
- +
)} /> @@ -206,7 +206,12 @@ export default class Main extends Component { path={ROUTES.CREATE_TRANSFORM} render={(props: RouteComponentProps) => (
- +
)} /> From 42ace95d83fd412daee475ddb971b5892345200b Mon Sep 17 00:00:00 2001 From: Eric Lobdell Date: Wed, 28 Apr 2021 17:51:14 -0700 Subject: [PATCH 16/93] Added execution interval to Schedule page --- .../components/Schedule/Schedule.tsx | 70 +++++- .../CreateTransformForm.tsx | 200 ++++++++++++++---- .../CreateTransformStep3.tsx | 19 +- .../pages/CreateTransform/utils/constants.ts | 11 - public/services/TransformService.ts | 8 + server/clusters/ism/ismPlugin.ts | 27 +++ server/routes/transforms.ts | 17 ++ server/services/TransformService.ts | 43 ++++ 8 files changed, 337 insertions(+), 58 deletions(-) diff --git a/public/pages/CreateTransform/components/Schedule/Schedule.tsx b/public/pages/CreateTransform/components/Schedule/Schedule.tsx index 9a8e45021..7cbefbbea 100644 --- a/public/pages/CreateTransform/components/Schedule/Schedule.tsx +++ b/public/pages/CreateTransform/components/Schedule/Schedule.tsx @@ -27,6 +27,8 @@ import { EuiTextArea, EuiFormHelpText, EuiText, + EuiAccordion, + EuiHorizontalRule, } from "@elastic/eui"; import { DelayTimeunitOptions, ScheduleIntervalTimeunitOptions } from "../../utils/constants"; import { ContentPanel } from "../../../../components/ContentPanel"; @@ -36,8 +38,13 @@ interface ScheduleProps { transformId: string; transformIdError: string; jobEnabledByDefault: boolean; + interval: number; + intervalTimeunit: string; + intervalError: string; pageSize: number; onChangeJobEnabledByDefault: () => void; + onChangeIntervalTime: (e: ChangeEvent) => void; + onChangeIntervalTimeunit: (e: ChangeEvent) => void; onChangePage: (e: ChangeEvent) => void; } @@ -52,6 +59,35 @@ const radios = [ }, ]; +const selectInterval = ( + interval: number, + intervalTimeunit: string, + intervalError: string, + onChangeInterval: (e: ChangeEvent) => void, + onChangeTimeunit: (value: ChangeEvent) => void +) => ( + + + + + + + + + + + + + + +); + const timezones = moment.tz.names().map((tz) => ({ label: tz, text: tz })); export default class Schedule extends Component { @@ -63,8 +99,13 @@ export default class Schedule extends Component { const { isEdit, jobEnabledByDefault, + interval, + intervalTimeunit, + intervalError, pageSize, onChangeJobEnabledByDefault, + onChangeIntervalTime, + onChangeIntervalTimeunit, onChangePage, } = this.props; return ( @@ -73,7 +114,7 @@ export default class Schedule extends Component { {!isEdit && ( { )} - - + {!isEdit} + + + + + {selectInterval(interval, intervalTimeunit, intervalError, onChangeIntervalTime, onChangeIntervalTimeunit)} + + + + + + + + ); diff --git a/public/pages/CreateTransform/containers/CreateTransformForm/CreateTransformForm.tsx b/public/pages/CreateTransform/containers/CreateTransformForm/CreateTransformForm.tsx index 16c135c2c..44b27a4ae 100644 --- a/public/pages/CreateTransform/containers/CreateTransformForm/CreateTransformForm.tsx +++ b/public/pages/CreateTransform/containers/CreateTransformForm/CreateTransformForm.tsx @@ -23,7 +23,7 @@ import IndexService from "../../../../services/IndexService"; import { ManagedCatIndex } from "../../../../../server/models/interfaces"; import CreateTransform from "../CreateTransform"; import CreateTransformStep2 from "../CreateTransformStep2"; -import { DimensionItem, FieldItem, IndexItem, MetricItem, Transform } from "../../../../../models/interfaces"; +import { DimensionItem, FieldItem, IndexItem, Transform } from "../../../../../models/interfaces"; import { getErrorMessage } from "../../../../utils/helpers"; import { EMPTY_TRANSFORM } from "../../utils/constants"; import CreateTransformStep3 from "../CreateTransformStep3"; @@ -59,28 +59,16 @@ interface CreateTransformFormState { allMappings: FieldItem[][]; fields: FieldItem[]; selectedTerms: FieldItem[]; - selectedDimensionField: DimensionItem[]; - selectedMetrics: MetricItem[]; - metricError: string; - timestamp: EuiComboBoxOptionOption[]; - timestampError: string; - intervalType: string; - intervalValue: number; - timezone: string; - timeunit: string; + selectedGroupField: DimensionItem[]; + selectedAggregations: MetricItem[]; // Needs to be Map + aggregationsError: string; selectedFields: FieldItem[]; jobEnabledByDefault: boolean; - continuousJob: string; - continuousDefinition: string; interval: number; intervalError: string; intervalTimeunit: string; - cronExpression: string; - cronTimezone: string; pageSize: number; - delayTime: number | undefined; - delayTimeunit: string; transformJSON: any; } @@ -106,6 +94,11 @@ export default class CreateTransformForm extends Component { + if (!(aggregation.min || + aggregation.max || + aggregation.sum || + aggregation.avg || + aggregation.value_count|| + aggregation.percentiles + )) { + const errorMsg = "Must specify at least one aggregation for: " + aggregation.source_field.label; + this.setState({ submitError: errorMsg, aggregationsError: errorMsg }); + invalidAggregation = true; + error = true; + } + }); + //If nothing invalid found, clear error. + if (!invalidAggregation) this.setState({ aggregationsError: "" }); + } } else if (currentStep == 3) { //Check if interval is a valid value and is specified. - const { intervalError, continuousDefinition } = this.state; - if (continuousDefinition == "fixed") { - if (intervalError != "") { - const intervalErrorMsg = "Interval value is required."; - this.setState({ submitError: intervalErrorMsg, intervalError: intervalErrorMsg }); - error = true; - } - } + const { intervalError } = this.state; } if (error) return; @@ -193,7 +205,7 @@ export default class CreateTransformForm extends Component { - const { intervalType, intervalValue, timeunit } = this.state; - let newJSON = this.state.transformJSON; - if (intervalType == "calendar") { - newJSON.transform.dimensions[0].date_histogram.calendar_interval = `1${timeunit}`; - delete newJSON.transform.dimensions[0].date_histogram["fixed_interval"]; - } else { - newJSON.transform.dimensions[0].date_histogram.fixed_interval = `${intervalValue}${timeunit}`; - delete newJSON.transform.dimensions[0].date_histogram["calendar_interval"]; - } - this.setState({ transformJSON: newJSON }); + onGroupSelectionChange = (selectedFields: DimensionItem[]): void => { + this.setState({ selectedGroupField: selectedFields }); + }; + + onAggregationSelectionChange = (selectedFields: MetricItem[]): void => { + this.setState({ selectedAggregations: selectedFields }); }; onChangeJobEnabledByDefault = (): void => { @@ -269,12 +276,99 @@ export default class CreateTransformForm extends Component): void => { + this.setState({ interval: e.target.valueAsNumber }); + if (e.target.value == "") { + const intervalErrorMsg = "Interval value is required."; + this.setState({ submitError: intervalErrorMsg, intervalError: intervalErrorMsg }); + } else { + this.setState({ intervalError: "" }); + } + }; + onChangePage = (e: ChangeEvent): void => { let newJSON = this.state.transformJSON; newJSON.transform.page_size = e.target.valueAsNumber; this.setState({ pageSize: e.target.valueAsNumber, transformJSON: newJSON }); }; + updateSchedule = (): void => { + const { interval, intervalTimeunit } = this.state; + let newJSON = this.state.transformJSON; + + newJSON.transform.schedule.interval = { + start_time: moment().unix(), + unit: `${intervalTimeunit}`, + period: `${interval}`, + }; + delete newJSON.transform.schedule["cron"]; + + this.setState({ transformJSON: newJSON }); + }; + + onChangeIntervalTimeunit = (e: ChangeEvent): void => { + this.setState({ intervalTimeunit: e.target.value }); + }; + + updateGroup = (): void => { + const { transformJSON, selectedGroupField } = this.state; + let newJSON = transformJSON; + + // Clear the groups fields + newJSON.transform.groups = {}; + + // Push rest of groups + selectedGroupField.map((group) => { + if (group.aggregationMethod == "terms") { + newJSON.transform.groups.push({ + terms: { + source_field: group.field.label, + target_field: "", // needs target_field source, null target_field test + }, + }); + } else if (group.aggregationMethod == "histogram") { + newJSON.transform.groups.push({ + histogram: { + source_field: group.field.label, + interval: group.interval, + }, + }); + } else { + newJSON.transform.groups.push({ + date_histogram: { + source_field: group.field.label, + // need to fill out other date histogram data + } + }); + } + }); + this.setState({ transformJSON: newJSON }); + }; + + updateAggregation = (): void => { + const { transformJSON, selectedAggregations } = this.state; + let newJSON = transformJSON; + + //Clear the aggregations array before pushing + newJSON.transform.aggregations = []; + + //Push all aggregations + selectedAggregations.map((aggregation) => { + const aggregations = []; + if (aggregation.min) aggregations.push({ min: {} }); + if (aggregation.max) aggregations.push({ max: {} }); + if (aggregation.sum) aggregations.push({ sum: {} }); + if (aggregation.avg) aggregations.push({ avg: {} }); + if (aggregation.value_count) aggregations.push({ value_count: {} }); + if (aggregation.percentiles) aggregations.push({ percentiles: {} }); + newJSON.transform.aggregations.push({ + source_field: aggregation.source_field.label, + aggregations: aggregations, + }); + }); + this.setState({ transformJSON: newJSON }); + }; + onSubmit = async (): Promise => { const { transformId, transformJSON } = this.state; this.setState({ submitError: "", isSubmitting: true, hasSubmitted: true }); @@ -282,7 +376,9 @@ export default class CreateTransformForm extends Component void; + onChangeIntervalTime: (e: ChangeEvent) => void; onChangePage: (e: ChangeEvent) => void; + onChangeIntervalTimeunit: (e: ChangeEvent) => void; } interface CreateTransformState { @@ -113,8 +118,13 @@ export default class CreateTransformStep3 extends Component -

Specify schedule

+

Specify Schedule

diff --git a/public/pages/CreateTransform/utils/constants.ts b/public/pages/CreateTransform/utils/constants.ts index 008f1bf34..1ab5afeb0 100644 --- a/public/pages/CreateTransform/utils/constants.ts +++ b/public/pages/CreateTransform/utils/constants.ts @@ -15,19 +15,8 @@ export const EMPTY_TRANSFORM = JSON.stringify({ transform: { - continuous: false, description: "", - dimensions: [ - { - date_histogram: { - source_field: "", - fixed_interval: "1h", - timezone: "UTC", - }, - }, - ], enabled: true, - metrics: [], page_size: 1000, roles: [], schedule: { diff --git a/public/services/TransformService.ts b/public/services/TransformService.ts index b31a49fa3..bea66f665 100644 --- a/public/services/TransformService.ts +++ b/public/services/TransformService.ts @@ -64,4 +64,12 @@ export default class TransformService { } // TODO: implement preview transform + + //Function to search for fields from a source index using GET /${source_index}/_mapping + getMappings = async (index: string): Promise> => { + const url = `..${NODE_API._MAPPINGS}`; + const body = { index: index }; + const response = (await this.httpClient.post(url, { body: JSON.stringify(body) })) as ServerResponse; + return response; + }; } diff --git a/server/clusters/ism/ismPlugin.ts b/server/clusters/ism/ismPlugin.ts index 4ca75fb72..7a1c63abb 100644 --- a/server/clusters/ism/ismPlugin.ts +++ b/server/clusters/ism/ismPlugin.ts @@ -344,4 +344,31 @@ export default function ismPlugin(Client: any, config: any, components: any) { }, method: "DELETE" }); + + ism.createTransform = ca({ + url: { + fmt: `${API.TRANSFORM_BASE}/<%=transformId%>?refresh=wait_for`, + req: { + transformId: { + type: "string", + required: true, + }, + }, + }, + needBody: true, + method: "PUT", + }); + + ism.putTransform = ca({ + url: { + fmt: `${API.TRANSFORM_BASE}/<%=transformId%>`, + req: { + transformId: { + type: "string", + required: true, + }, + }, + }, + method: "PUT", + }); } diff --git a/server/routes/transforms.ts b/server/routes/transforms.ts index ce7dd0de3..091274118 100644 --- a/server/routes/transforms.ts +++ b/server/routes/transforms.ts @@ -84,4 +84,21 @@ export default function (services: NodeServices, router: IRouter) { }, transformService.deleteTransform ) + + router.put( + { + path: `${NODE_API.TRANSFORMS}/{id}`, + validate: { + params: schema.object({ + id: schema.string(), + }), + query: schema.object({ + seqNo: schema.maybe(schema.number()), + primaryTerm: schema.maybe(schema.number()), + }), + body: schema.any(), + }, + }, + transformService.putTransform + ); } diff --git a/server/services/TransformService.ts b/server/services/TransformService.ts index 4a1774947..0728d0db1 100644 --- a/server/services/TransformService.ts +++ b/server/services/TransformService.ts @@ -269,4 +269,47 @@ export default class TransformService { }) } } + + /** + * Calls backend Put Transform API + */ + putTransform = async ( + context: RequestHandlerContext, + request: KibanaRequest, + response: KibanaResponseFactory + ): Promise | ResponseError>> => { + try { + const { id } = request.params as { id: string }; + const { seqNo, primaryTerm } = request.query as { seqNo?: string; primaryTerm?: string }; + let method = "ism.putTransform"; + let params: PutTransformParams = { + transformId: id, + if_seq_no: seqNo, + if_primary_term: primaryTerm, + body: JSON.stringify(request.body), + }; + if (seqNo === undefined || primaryTerm === undefined) { + method = "ism.createTransform"; + params = { transformId: id, body: JSON.stringify(request.body) }; + } + const { callAsCurrentUser: callWithRequest } = this.esDriver.asScoped(request); + const putTransformResponse: PutTransformResponse = await callWithRequest(method, params); + return response.custom({ + statusCode: 200, + body: { + ok: true, + response: putTransformResponse, + }, + }); + } catch (err) { + console.error("Index Management - TransformService - putTransform", err); + return response.custom({ + statusCode: 200, + body: { + ok: false, + error: err.message, + }, + }); + } + }; } From 1ce781d111d8853ce44d96f41c84a2b8ffdfdbde Mon Sep 17 00:00:00 2001 From: Annie Date: Thu, 29 Apr 2021 12:34:11 -0700 Subject: [PATCH 17/93] Data grid showing up with empty data --- .../DefineTransforms/DefineTransforms.tsx | 44 ++++++++++++------- 1 file changed, 27 insertions(+), 17 deletions(-) diff --git a/public/pages/CreateTransform/components/DefineTransforms/DefineTransforms.tsx b/public/pages/CreateTransform/components/DefineTransforms/DefineTransforms.tsx index 0373f0701..80c1e6eaf 100644 --- a/public/pages/CreateTransform/components/DefineTransforms/DefineTransforms.tsx +++ b/public/pages/CreateTransform/components/DefineTransforms/DefineTransforms.tsx @@ -13,8 +13,8 @@ * permissions and limitations under the License. */ -import { EuiDataGrid, EuiSpacer, EuiText } from "@elastic/eui"; -import React, { Component } from "react"; +import { EuiDataGrid, EuiDataGridColumn, EuiSpacer, EuiText } from "@elastic/eui"; +import React, { Component, createContext, useMemo, useState, useEffect, useContext } from "react"; import { ContentPanel, ContentPanelActions } from "../../../../components/ContentPanel"; interface DefineTransformsProps { @@ -22,18 +22,23 @@ interface DefineTransformsProps { sourceIndex: string; } -interface DefineTransformsState {} +interface DefineTransformsState { + columns: EuiDataGridColumn[]; + visibleColumns: string[]; +} + +export default function DefineTransforms({ transfromId, sourceIndex }: DefineTransformsProps) { + const columns = [ + { + id: "name", + displayAsText: "Name", + }, + ]; -export default class DefineTransforms extends Component { - constructor(props: DefineTransformsProps) { - super(props); - const { transfromId } = this.props; - this.state = {}; - } + const [visibleColumns, setVisibleColumns] = useState(() => columns.map(({ id }) => id)); - render() { - const { transfromId, sourceIndex } = this.props; - return ( + return ( + <>

{`Viewing sample data from index ${sourceIndex}, filtered by order.type:sales_order, order.success:true`}

- {/**/} + { + return null; + }} + />
- ); - } + + ); } From 9664bf66d4d5803d316642513e90559faec41a49 Mon Sep 17 00:00:00 2001 From: Annie Date: Thu, 29 Apr 2021 12:34:32 -0700 Subject: [PATCH 18/93] Update DefineTransforms.tsx --- .../components/DefineTransforms/DefineTransforms.tsx | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/public/pages/CreateTransform/components/DefineTransforms/DefineTransforms.tsx b/public/pages/CreateTransform/components/DefineTransforms/DefineTransforms.tsx index 80c1e6eaf..9e79c8f04 100644 --- a/public/pages/CreateTransform/components/DefineTransforms/DefineTransforms.tsx +++ b/public/pages/CreateTransform/components/DefineTransforms/DefineTransforms.tsx @@ -14,7 +14,7 @@ */ import { EuiDataGrid, EuiDataGridColumn, EuiSpacer, EuiText } from "@elastic/eui"; -import React, { Component, createContext, useMemo, useState, useEffect, useContext } from "react"; +import React, { useState } from "react"; import { ContentPanel, ContentPanelActions } from "../../../../components/ContentPanel"; interface DefineTransformsProps { @@ -22,11 +22,6 @@ interface DefineTransformsProps { sourceIndex: string; } -interface DefineTransformsState { - columns: EuiDataGridColumn[]; - visibleColumns: string[]; -} - export default function DefineTransforms({ transfromId, sourceIndex }: DefineTransformsProps) { const columns = [ { From 7f0111dbcc4032fd77c5a8ca3fc54feb2d937719 Mon Sep 17 00:00:00 2001 From: Annie Date: Thu, 29 Apr 2021 15:19:15 -0700 Subject: [PATCH 19/93] Able to show fields on step 2 --- .../DefineTransforms/DefineTransforms.tsx | 13 ++++++------- .../CreateTransformStep2/CreateTransformStep2.tsx | 2 +- 2 files changed, 7 insertions(+), 8 deletions(-) diff --git a/public/pages/CreateTransform/components/DefineTransforms/DefineTransforms.tsx b/public/pages/CreateTransform/components/DefineTransforms/DefineTransforms.tsx index 9e79c8f04..a0ebef615 100644 --- a/public/pages/CreateTransform/components/DefineTransforms/DefineTransforms.tsx +++ b/public/pages/CreateTransform/components/DefineTransforms/DefineTransforms.tsx @@ -16,19 +16,17 @@ import { EuiDataGrid, EuiDataGridColumn, EuiSpacer, EuiText } from "@elastic/eui"; import React, { useState } from "react"; import { ContentPanel, ContentPanelActions } from "../../../../components/ContentPanel"; +import { FieldItem } from "../../../../../models/interfaces"; interface DefineTransformsProps { transformId: string; sourceIndex: string; + fields: FieldItem[]; } -export default function DefineTransforms({ transfromId, sourceIndex }: DefineTransformsProps) { - const columns = [ - { - id: "name", - displayAsText: "Name", - }, - ]; +export default function DefineTransforms({ transfromId, sourceIndex, fields }: DefineTransformsProps) { + let columns: EuiDataGridColumn[] = []; + fields.map((field: FieldItem) => columns.push({ id: field.label })); const [visibleColumns, setVisibleColumns] = useState(() => columns.map(({ id }) => id)); @@ -66,6 +64,7 @@ export default function DefineTransforms({ transfromId, sourceIndex }: DefineTra

{`Viewing sample data from index ${sourceIndex}, filtered by order.type:sales_order, order.success:true`}

+ Define transform - + From e00a1ea2c3dc583e75d1f91a6a70ce309f822326 Mon Sep 17 00:00:00 2001 From: Annie Date: Thu, 29 Apr 2021 15:19:33 -0700 Subject: [PATCH 20/93] Update CreateTransformStep2.tsx --- .../containers/CreateTransformStep2/CreateTransformStep2.tsx | 2 -- 1 file changed, 2 deletions(-) diff --git a/public/pages/CreateTransform/containers/CreateTransformStep2/CreateTransformStep2.tsx b/public/pages/CreateTransform/containers/CreateTransformStep2/CreateTransformStep2.tsx index 1c9074f29..9a1c14d1f 100644 --- a/public/pages/CreateTransform/containers/CreateTransformStep2/CreateTransformStep2.tsx +++ b/public/pages/CreateTransform/containers/CreateTransformStep2/CreateTransformStep2.tsx @@ -48,8 +48,6 @@ export default class CreateTransformStep2 extends Component From 7ca3e40822c0d7d571bb8536bd90a85623522d90 Mon Sep 17 00:00:00 2001 From: Annie Date: Thu, 29 Apr 2021 16:23:30 -0700 Subject: [PATCH 21/93] Add pagination --- .../DefineTransforms/DefineTransforms.tsx | 148 +++++++++++++----- 1 file changed, 105 insertions(+), 43 deletions(-) diff --git a/public/pages/CreateTransform/components/DefineTransforms/DefineTransforms.tsx b/public/pages/CreateTransform/components/DefineTransforms/DefineTransforms.tsx index a0ebef615..2bd160f0d 100644 --- a/public/pages/CreateTransform/components/DefineTransforms/DefineTransforms.tsx +++ b/public/pages/CreateTransform/components/DefineTransforms/DefineTransforms.tsx @@ -14,9 +14,11 @@ */ import { EuiDataGrid, EuiDataGridColumn, EuiSpacer, EuiText } from "@elastic/eui"; -import React, { useState } from "react"; +import React, { useContext, useEffect, useState } from "react"; import { ContentPanel, ContentPanelActions } from "../../../../components/ContentPanel"; import { FieldItem } from "../../../../../models/interfaces"; +import { useCallback } from "react"; +import { useMemo } from "react"; interface DefineTransformsProps { transformId: string; @@ -26,54 +28,114 @@ interface DefineTransformsProps { export default function DefineTransforms({ transfromId, sourceIndex, fields }: DefineTransformsProps) { let columns: EuiDataGridColumn[] = []; - fields.map((field: FieldItem) => columns.push({ id: field.label })); + fields.map((field: FieldItem) => columns.push({ id: field.label, displayAsText: field.label + " type: " + field.type })); + + const [pagination, setPagination] = useState({ pageIndex: 0, pageSize: 10 }); + + const onChangeItemsPerPage = useCallback( + (pageSize) => + setPagination((pagination) => ({ + ...pagination, + pageSize, + pageIndex: 0, + })), + [setPagination] + ); + + const onChangePage = useCallback((pageIndex) => setPagination((pagination) => ({ ...pagination, pageIndex })), [setPagination]); + + const [sortingColumns, setSortingColumns] = useState([]); + const onSort = useCallback( + (sortingColumns) => { + setSortingColumns(sortingColumns); + }, + [setSortingColumns] + ); const [visibleColumns, setVisibleColumns] = useState(() => columns.map(({ id }) => id)); + // const renderCellValue = useMemo(() => { + // return ({ rowIndex, columnId, setCellProps }) => { + // // const data = useContext(DataContext); + // useEffect(() => { + // if (columnId === 'amount') { + // if (data.hasOwnProperty(rowIndex)) { + // const numeric = parseFloat( + // data[rowIndex][columnId].match(/\d+\.\d+/)[0], + // 10 + // ); + // setCellProps({ + // style: { + // backgroundColor: `rgba(0, 255, 0, ${numeric * 0.0002})`, + // }, + // }); + // } + // } + // }, [rowIndex, columnId, setCellProps, data]); + // + // function getFormatted() { + // return data[rowIndex][columnId].formatted + // ? data[rowIndex][columnId].formatted + // : data[rowIndex][columnId]; + // } + // + // return data.hasOwnProperty(rowIndex) + // ? getFormatted(rowIndex, columnId) + // : null; + // }; + // }, []); + return ( - <> - - // onShow(ApplyPolicyModal, { - // indices: selectedItems.map((item: ManagedCatIndex) => item.index), - // core: this.context, - // }), - }, + // onClick: () => + // onShow(ApplyPolicyModal, { + // indices: selectedItems.map((item: ManagedCatIndex) => item.index), + // core: this.context, + // }), }, - ]} - /> - } - bodyStyles={{ padding: "10px 10px" }} - title="Select fields to transform" - titleSize="m" - > - -

Original fields with sample data

-
- - {/*TODO: Substitute "source index", and "filtered by" fields with actual values*/} - -

{`Viewing sample data from index ${sourceIndex}, filtered by order.type:sales_order, order.success:true`}

-
- - { - return null; - }} + }, + ]} /> -
- + } + bodyStyles={{ padding: "10px 10px" }} + title="Select fields to transform" + titleSize="m" + > + +

Original fields with sample data

+
+ + {/*TODO: Substitute "source index", and "filtered by" fields with actual values*/} + +

{`Viewing sample data from index ${sourceIndex}, filtered by order.type:sales_order, order.success:true`}

+
+ + {/*TODO: add rowCount*/} + { + return null; + }} + sorting={{ columns: sortingColumns, onSort }} + pagination={{ + ...pagination, + pageSizeOptions: [5, 10, 50, 100], + onChangeItemsPerPage: onChangeItemsPerPage, + onChangePage: onChangePage, + }} + /> + ); } From 621582a2808c41b927b85ceae6cd1811ad3dd61f Mon Sep 17 00:00:00 2001 From: Annie Date: Thu, 29 Apr 2021 17:22:07 -0700 Subject: [PATCH 22/93] Attempt to search sample data --- .../CreateTransformSteps.tsx | 1 + .../DefineTransforms/DefineTransforms.tsx | 23 +++++--- .../CreateTransformStep2.tsx | 2 +- public/services/TransformService.ts | 9 ++++ server/routes/transforms.ts | 10 ++++ server/services/TransformService.ts | 53 ++++++++++++++++++- utils/constants.ts | 1 + 7 files changed, 90 insertions(+), 9 deletions(-) diff --git a/public/pages/CreateTransform/components/CreateTransformSteps/CreateTransformSteps.tsx b/public/pages/CreateTransform/components/CreateTransformSteps/CreateTransformSteps.tsx index 9d42e6e9a..3b404ea58 100644 --- a/public/pages/CreateTransform/components/CreateTransformSteps/CreateTransformSteps.tsx +++ b/public/pages/CreateTransform/components/CreateTransformSteps/CreateTransformSteps.tsx @@ -42,6 +42,7 @@ const setOfSteps = (step: number) => { }, ]; }; + const CreateTransformSteps = ({ step }: CreateTransformStepsProps) => (
diff --git a/public/pages/CreateTransform/components/DefineTransforms/DefineTransforms.tsx b/public/pages/CreateTransform/components/DefineTransforms/DefineTransforms.tsx index 2bd160f0d..ee8479fd6 100644 --- a/public/pages/CreateTransform/components/DefineTransforms/DefineTransforms.tsx +++ b/public/pages/CreateTransform/components/DefineTransforms/DefineTransforms.tsx @@ -18,19 +18,34 @@ import React, { useContext, useEffect, useState } from "react"; import { ContentPanel, ContentPanelActions } from "../../../../components/ContentPanel"; import { FieldItem } from "../../../../../models/interfaces"; import { useCallback } from "react"; -import { useMemo } from "react"; +import { TransformService } from "../../../../services"; interface DefineTransformsProps { + transformService: TransformService; transformId: string; sourceIndex: string; fields: FieldItem[]; } -export default function DefineTransforms({ transfromId, sourceIndex, fields }: DefineTransformsProps) { +export default function DefineTransforms({ transformService, transfromId, sourceIndex, fields }: DefineTransformsProps) { let columns: EuiDataGridColumn[] = []; + fields.map((field: FieldItem) => columns.push({ id: field.label, displayAsText: field.label + " type: " + field.type })); + const [loading, setLoading] = useState(true); + const [pagination, setPagination] = useState({ pageIndex: 0, pageSize: 10 }); + const [sortingColumns, setSortingColumns] = useState([]); + const [visibleColumns, setVisibleColumns] = useState(() => columns.map(({ id }) => id)); + + const fetchData = useCallback(async () => { + const response = await transformService.searchSampleData(sourceIndex); + if (response.ok) console.log("Successfully searched sample data: " + JSON.stringify(response)); + }, []); + + // React.useEffect(() => { + // fetchData(); + // }, [ fetchData]); const onChangeItemsPerPage = useCallback( (pageSize) => @@ -41,10 +56,8 @@ export default function DefineTransforms({ transfromId, sourceIndex, fields }: D })), [setPagination] ); - const onChangePage = useCallback((pageIndex) => setPagination((pagination) => ({ ...pagination, pageIndex })), [setPagination]); - const [sortingColumns, setSortingColumns] = useState([]); const onSort = useCallback( (sortingColumns) => { setSortingColumns(sortingColumns); @@ -52,8 +65,6 @@ export default function DefineTransforms({ transfromId, sourceIndex, fields }: D [setSortingColumns] ); - const [visibleColumns, setVisibleColumns] = useState(() => columns.map(({ id }) => id)); - // const renderCellValue = useMemo(() => { // return ({ rowIndex, columnId, setCellProps }) => { // // const data = useContext(DataContext); diff --git a/public/pages/CreateTransform/containers/CreateTransformStep2/CreateTransformStep2.tsx b/public/pages/CreateTransform/containers/CreateTransformStep2/CreateTransformStep2.tsx index 9a1c14d1f..4d05d7d3e 100644 --- a/public/pages/CreateTransform/containers/CreateTransformStep2/CreateTransformStep2.tsx +++ b/public/pages/CreateTransform/containers/CreateTransformStep2/CreateTransformStep2.tsx @@ -60,7 +60,7 @@ export default class CreateTransformStep2 extends ComponentDefine transform - + diff --git a/public/services/TransformService.ts b/public/services/TransformService.ts index 8fbd0f6f8..1691e3c01 100644 --- a/public/services/TransformService.ts +++ b/public/services/TransformService.ts @@ -73,4 +73,13 @@ export default class TransformService { const response = (await this.httpClient.post(url, { body: JSON.stringify(body) })) as ServerResponse; return response; }; + + searchSampleData = async (index: string): Promise> => { + const url = `..${NODE_API._SEARCH_SAMPLE_DATA}`; + const body = { index: index }; + const response = (await this.httpClient.get(url, { body: JSON.stringify(body) })) as ServerResponse; + //Debug use + console.log("response: " + JSON.stringify(response)); + return response; + }; } diff --git a/server/routes/transforms.ts b/server/routes/transforms.ts index 851a586dc..340bc8025 100644 --- a/server/routes/transforms.ts +++ b/server/routes/transforms.ts @@ -101,4 +101,14 @@ export default function (services: NodeServices, router: IRouter) { }, transformService.putTransform ); + + router.post( + { + path: NODE_API._SEARCH_SAMPLE_DATA, + validate: { + body: schema.any(), + }, + }, + transformService.searchSampleData + ); } diff --git a/server/services/TransformService.ts b/server/services/TransformService.ts index cfaca1e71..031d550b0 100644 --- a/server/services/TransformService.ts +++ b/server/services/TransformService.ts @@ -13,9 +13,9 @@ * permissions and limitations under the License. */ -import { IClusterClient, IKibanaResponse, KibanaRequest, KibanaResponseFactory, RequestHandlerContext } from "kibana/server"; +import { IClusterClient, IKibanaResponse, KibanaRequest, KibanaResponseFactory, RequestHandlerContext, ResponseError } from "kibana/server"; import { ServerResponse } from "../models/types"; -import { GetTransformsResponse } from "../models/interfaces"; +import { GetTransformsResponse, PutTransformParams, PutTransformResponse, SearchResponse } from "../models/interfaces"; import { DocumentTransform, Transform } from "../../models/interfaces"; import _ from "lodash"; @@ -306,4 +306,53 @@ export default class TransformService { }); } }; + + searchSampleData = async ( + context: RequestHandlerContext, + request: KibanaRequest, + response: KibanaResponseFactory + ): Promise>> => { + try { + const { index } = request.body as { index: string }; + const params = { index: index }; + const { from, size, search, sortField, sortDirection } = request.query as { + from: string; + size: string; + search: string; + sortField: string; + sortDirection: string; + }; + const { callAsCurrentUser: callWithRequest } = this.esDriver.asScoped(request); + const searchResponse: SearchResponse = await callWithRequest(request, "search", params); + + //Debug use + console.log(JSON.stringify(searchResponse)); + + return response.custom({ + statusCode: 200, + body: { + ok: true, + response: {}, + }, + }); + } catch (err) { + if (err.statusCode === 404 && err.body.error.type === "index_not_found_exception") { + return response.custom({ + statusCode: 200, + body: { + ok: true, + response: {}, + }, + }); + } + console.error("Index Management - TransformService - searchSampleData", err); + return response.custom({ + statusCode: 200, + body: { + ok: false, + error: err.message, + }, + }); + } + }; } diff --git a/utils/constants.ts b/utils/constants.ts index 7db04af57..bfdbfc2b7 100644 --- a/utils/constants.ts +++ b/utils/constants.ts @@ -16,6 +16,7 @@ export const BASE_API_PATH = "/api/ism"; export const NODE_API = Object.freeze({ _SEARCH: `${BASE_API_PATH}/_search`, + _SEARCH_SAMPLE_DATA: `${BASE_API_PATH}/_searchSampleData`, _INDICES: `${BASE_API_PATH}/_indices`, _MAPPINGS: `${BASE_API_PATH}/_mappings`, APPLY_POLICY: `${BASE_API_PATH}/applyPolicy`, From a7e78bc6a53cb8b1787933bd1d6f859fed27a392 Mon Sep 17 00:00:00 2001 From: Annie Date: Thu, 29 Apr 2021 23:44:52 -0700 Subject: [PATCH 23/93] Update DefineTransforms.tsx --- .../components/DefineTransforms/DefineTransforms.tsx | 1 + 1 file changed, 1 insertion(+) diff --git a/public/pages/CreateTransform/components/DefineTransforms/DefineTransforms.tsx b/public/pages/CreateTransform/components/DefineTransforms/DefineTransforms.tsx index ee8479fd6..038482d3c 100644 --- a/public/pages/CreateTransform/components/DefineTransforms/DefineTransforms.tsx +++ b/public/pages/CreateTransform/components/DefineTransforms/DefineTransforms.tsx @@ -143,6 +143,7 @@ export default function DefineTransforms({ transformService, transfromId, source pagination={{ ...pagination, pageSizeOptions: [5, 10, 50, 100], + pageSizeOptions: [5, 10, 20, 50], onChangeItemsPerPage: onChangeItemsPerPage, onChangePage: onChangePage, }} From 8f6eb8a931b2508323894c0cd66ae10338e6a315 Mon Sep 17 00:00:00 2001 From: Annie Date: Thu, 29 Apr 2021 23:44:59 -0700 Subject: [PATCH 24/93] Update DefineTransforms.tsx --- .../components/DefineTransforms/DefineTransforms.tsx | 1 - 1 file changed, 1 deletion(-) diff --git a/public/pages/CreateTransform/components/DefineTransforms/DefineTransforms.tsx b/public/pages/CreateTransform/components/DefineTransforms/DefineTransforms.tsx index 038482d3c..abb262361 100644 --- a/public/pages/CreateTransform/components/DefineTransforms/DefineTransforms.tsx +++ b/public/pages/CreateTransform/components/DefineTransforms/DefineTransforms.tsx @@ -142,7 +142,6 @@ export default function DefineTransforms({ transformService, transfromId, source sorting={{ columns: sortingColumns, onSort }} pagination={{ ...pagination, - pageSizeOptions: [5, 10, 50, 100], pageSizeOptions: [5, 10, 20, 50], onChangeItemsPerPage: onChangeItemsPerPage, onChangePage: onChangePage, From 769e2fefaf76b9ec86a3673cdb7034a8919c7f26 Mon Sep 17 00:00:00 2001 From: Annie Date: Thu, 29 Apr 2021 23:45:16 -0700 Subject: [PATCH 25/93] Update DefineTransforms.tsx --- .../components/DefineTransforms/DefineTransforms.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/public/pages/CreateTransform/components/DefineTransforms/DefineTransforms.tsx b/public/pages/CreateTransform/components/DefineTransforms/DefineTransforms.tsx index abb262361..7596a3e80 100644 --- a/public/pages/CreateTransform/components/DefineTransforms/DefineTransforms.tsx +++ b/public/pages/CreateTransform/components/DefineTransforms/DefineTransforms.tsx @@ -132,7 +132,7 @@ export default function DefineTransforms({ transformService, transfromId, source {/*TODO: add rowCount*/} Date: Fri, 30 Apr 2021 13:20:40 -0700 Subject: [PATCH 26/93] Getting search response --- .../DefineTransforms/DefineTransforms.tsx | 39 ++++++++++++------- .../CreateTransformStep2.tsx | 8 +++- public/services/TransformService.ts | 9 +++-- server/routes/transforms.ts | 8 ++-- server/services/TransformService.ts | 38 ++++++++++++------ 5 files changed, 69 insertions(+), 33 deletions(-) diff --git a/public/pages/CreateTransform/components/DefineTransforms/DefineTransforms.tsx b/public/pages/CreateTransform/components/DefineTransforms/DefineTransforms.tsx index 7596a3e80..4cf4cb027 100644 --- a/public/pages/CreateTransform/components/DefineTransforms/DefineTransforms.tsx +++ b/public/pages/CreateTransform/components/DefineTransforms/DefineTransforms.tsx @@ -14,20 +14,22 @@ */ import { EuiDataGrid, EuiDataGridColumn, EuiSpacer, EuiText } from "@elastic/eui"; -import React, { useContext, useEffect, useState } from "react"; +import { CoreStart } from "kibana/public"; +import React, { useState, useCallback } from "react"; import { ContentPanel, ContentPanelActions } from "../../../../components/ContentPanel"; import { FieldItem } from "../../../../../models/interfaces"; -import { useCallback } from "react"; import { TransformService } from "../../../../services"; +import { getErrorMessage } from "../../../../utils/helpers"; interface DefineTransformsProps { transformService: TransformService; + notifications: CoreStart["notifications"]; transformId: string; sourceIndex: string; fields: FieldItem[]; } -export default function DefineTransforms({ transformService, transfromId, sourceIndex, fields }: DefineTransformsProps) { +export default function DefineTransforms({ transformService, notifications, transfromId, sourceIndex, fields }: DefineTransformsProps) { let columns: EuiDataGridColumn[] = []; fields.map((field: FieldItem) => columns.push({ id: field.label, displayAsText: field.label + " type: " + field.type })); @@ -37,15 +39,28 @@ export default function DefineTransforms({ transformService, transfromId, source const [pagination, setPagination] = useState({ pageIndex: 0, pageSize: 10 }); const [sortingColumns, setSortingColumns] = useState([]); const [visibleColumns, setVisibleColumns] = useState(() => columns.map(({ id }) => id)); + const [data, setData] = useState([]); + const [dataCount, setDataCount] = useState(0); const fetchData = useCallback(async () => { - const response = await transformService.searchSampleData(sourceIndex); - if (response.ok) console.log("Successfully searched sample data: " + JSON.stringify(response)); - }, []); + console.log("Entering fetchData..."); + try { + const response = await transformService.searchSampleData(sourceIndex); - // React.useEffect(() => { - // fetchData(); - // }, [ fetchData]); + if (response.ok) { + //Debug use + console.log("Successfully searched sample data: " + JSON.stringify(response)); + setData(response.response.data); + setDataCount(response.response.total); + } + } catch (err) { + notifications.toasts.addDanger(getErrorMessage(err, "There was a problem loading the rollups")); + } + }, [sourceIndex]); + + React.useEffect(() => { + fetchData(); + }, [fetchData]); const onChangeItemsPerPage = useCallback( (pageSize) => @@ -135,10 +150,8 @@ export default function DefineTransforms({ transformService, transfromId, source aria-label="Define transforms" columns={columns} columnVisibility={{ visibleColumns, setVisibleColumns }} - rowCount={200} - renderCellValue={() => { - return null; - }} + rowCount={dataCount} + renderCellValue={({ rowIndex, columnId }) => data[rowIndex][columnId]} sorting={{ columns: sortingColumns, onSort }} pagination={{ ...pagination, diff --git a/public/pages/CreateTransform/containers/CreateTransformStep2/CreateTransformStep2.tsx b/public/pages/CreateTransform/containers/CreateTransformStep2/CreateTransformStep2.tsx index 4d05d7d3e..851fbb4c7 100644 --- a/public/pages/CreateTransform/containers/CreateTransformStep2/CreateTransformStep2.tsx +++ b/public/pages/CreateTransform/containers/CreateTransformStep2/CreateTransformStep2.tsx @@ -60,7 +60,13 @@ export default class CreateTransformStep2 extends ComponentDefine transform - + diff --git a/public/services/TransformService.ts b/public/services/TransformService.ts index 1691e3c01..eaee7728f 100644 --- a/public/services/TransformService.ts +++ b/public/services/TransformService.ts @@ -15,7 +15,7 @@ import { HttpSetup } from "kibana/public"; import { ServerResponse } from "../../server/models/types"; -import { GetTransformsResponse, PutTransformResponse } from "../../server/models/interfaces"; +import { GetFieldsResponse, GetTransformsResponse, PutTransformResponse, SearchSampleDataResponse } from "../../server/models/interfaces"; import { NODE_API } from "../../utils/constants"; import { DocumentTransform, Transform } from "../../models/interfaces"; @@ -75,9 +75,10 @@ export default class TransformService { }; searchSampleData = async (index: string): Promise> => { - const url = `..${NODE_API._SEARCH_SAMPLE_DATA}`; - const body = { index: index }; - const response = (await this.httpClient.get(url, { body: JSON.stringify(body) })) as ServerResponse; + //Debug use + console.log("Entering browser side service..."); + const url = `..${NODE_API._SEARCH_SAMPLE_DATA}/${index}`; + const response = (await this.httpClient.get(url)) as ServerResponse; //Debug use console.log("response: " + JSON.stringify(response)); return response; diff --git a/server/routes/transforms.ts b/server/routes/transforms.ts index 340bc8025..41c50dd60 100644 --- a/server/routes/transforms.ts +++ b/server/routes/transforms.ts @@ -102,11 +102,13 @@ export default function (services: NodeServices, router: IRouter) { transformService.putTransform ); - router.post( + router.get( { - path: NODE_API._SEARCH_SAMPLE_DATA, + path: `${NODE_API._SEARCH_SAMPLE_DATA}/{index}`, validate: { - body: schema.any(), + params: schema.object({ + index: schema.string(), + }), }, }, transformService.searchSampleData diff --git a/server/services/TransformService.ts b/server/services/TransformService.ts index 031d550b0..cdb8cde16 100644 --- a/server/services/TransformService.ts +++ b/server/services/TransformService.ts @@ -15,7 +15,13 @@ import { IClusterClient, IKibanaResponse, KibanaRequest, KibanaResponseFactory, RequestHandlerContext, ResponseError } from "kibana/server"; import { ServerResponse } from "../models/types"; -import { GetTransformsResponse, PutTransformParams, PutTransformResponse, SearchResponse } from "../models/interfaces"; +import { + GetTransformsResponse, + PutTransformParams, + PutTransformResponse, + SearchResponse, + SearchSampleDataResponse, +} from "../models/interfaces"; import { DocumentTransform, Transform } from "../../models/interfaces"; import _ from "lodash"; @@ -313,17 +319,19 @@ export default class TransformService { response: KibanaResponseFactory ): Promise>> => { try { - const { index } = request.body as { index: string }; + // Debug use + console.log("Entering server side service..."); + // const { from, size, search, sortField, sortDirection } = request.query as { + // from: string; + // size: string; + // search: string; + // sortField: string; + // sortDirection: string; + // }; + const { index } = request.params as { index: string }; const params = { index: index }; - const { from, size, search, sortField, sortDirection } = request.query as { - from: string; - size: string; - search: string; - sortField: string; - sortDirection: string; - }; const { callAsCurrentUser: callWithRequest } = this.esDriver.asScoped(request); - const searchResponse: SearchResponse = await callWithRequest(request, "search", params); + const searchResponse: SearchResponse = await callWithRequest("search", params); //Debug use console.log(JSON.stringify(searchResponse)); @@ -332,7 +340,10 @@ export default class TransformService { statusCode: 200, body: { ok: true, - response: {}, + response: { + total: searchResponse.hits.total, + data: searchResponse.hits.hits, + }, }, }); } catch (err) { @@ -341,7 +352,10 @@ export default class TransformService { statusCode: 200, body: { ok: true, - response: {}, + response: { + total: 0, + data: [], + }, }, }); } From 6725203f4e76db61b2651d2860040f898a489a18 Mon Sep 17 00:00:00 2001 From: Annie Date: Fri, 30 Apr 2021 13:48:38 -0700 Subject: [PATCH 27/93] Showing correct total number --- .../components/DefineTransforms/DefineTransforms.tsx | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/public/pages/CreateTransform/components/DefineTransforms/DefineTransforms.tsx b/public/pages/CreateTransform/components/DefineTransforms/DefineTransforms.tsx index 4cf4cb027..b3979a29b 100644 --- a/public/pages/CreateTransform/components/DefineTransforms/DefineTransforms.tsx +++ b/public/pages/CreateTransform/components/DefineTransforms/DefineTransforms.tsx @@ -51,7 +51,7 @@ export default function DefineTransforms({ transformService, notifications, tran //Debug use console.log("Successfully searched sample data: " + JSON.stringify(response)); setData(response.response.data); - setDataCount(response.response.total); + setDataCount(response.response.total.value); } } catch (err) { notifications.toasts.addDanger(getErrorMessage(err, "There was a problem loading the rollups")); @@ -151,7 +151,8 @@ export default function DefineTransforms({ transformService, notifications, tran columns={columns} columnVisibility={{ visibleColumns, setVisibleColumns }} rowCount={dataCount} - renderCellValue={({ rowIndex, columnId }) => data[rowIndex][columnId]} + // renderCellValue={({ rowIndex, columnId }) => data[rowIndex][columnId]} + renderCellValue={({}) => null} sorting={{ columns: sortingColumns, onSort }} pagination={{ ...pagination, From 81462cd34ccf16b08c0f3e597680ffcd656f47ca Mon Sep 17 00:00:00 2001 From: Annie Date: Fri, 30 Apr 2021 14:46:16 -0700 Subject: [PATCH 28/93] Able to reach data at certain field, but still need to finish render method --- .../DefineTransforms/DefineTransforms.tsx | 57 ++++++++----------- 1 file changed, 24 insertions(+), 33 deletions(-) diff --git a/public/pages/CreateTransform/components/DefineTransforms/DefineTransforms.tsx b/public/pages/CreateTransform/components/DefineTransforms/DefineTransforms.tsx index b3979a29b..ef2f0a388 100644 --- a/public/pages/CreateTransform/components/DefineTransforms/DefineTransforms.tsx +++ b/public/pages/CreateTransform/components/DefineTransforms/DefineTransforms.tsx @@ -15,11 +15,13 @@ import { EuiDataGrid, EuiDataGridColumn, EuiSpacer, EuiText } from "@elastic/eui"; import { CoreStart } from "kibana/public"; -import React, { useState, useCallback } from "react"; +import React, { useState, useCallback, useEffect } from "react"; import { ContentPanel, ContentPanelActions } from "../../../../components/ContentPanel"; import { FieldItem } from "../../../../../models/interfaces"; import { TransformService } from "../../../../services"; import { getErrorMessage } from "../../../../utils/helpers"; +import { useMemo } from "react"; +import * as repl from "repl"; interface DefineTransformsProps { transformService: TransformService; @@ -52,6 +54,7 @@ export default function DefineTransforms({ transformService, notifications, tran console.log("Successfully searched sample data: " + JSON.stringify(response)); setData(response.response.data); setDataCount(response.response.total.value); + console.log("First item: " + JSON.stringify(response.response.data[0])); } } catch (err) { notifications.toasts.addDanger(getErrorMessage(err, "There was a problem loading the rollups")); @@ -80,36 +83,19 @@ export default function DefineTransforms({ transformService, notifications, tran [setSortingColumns] ); - // const renderCellValue = useMemo(() => { - // return ({ rowIndex, columnId, setCellProps }) => { - // // const data = useContext(DataContext); - // useEffect(() => { - // if (columnId === 'amount') { - // if (data.hasOwnProperty(rowIndex)) { - // const numeric = parseFloat( - // data[rowIndex][columnId].match(/\d+\.\d+/)[0], - // 10 - // ); - // setCellProps({ - // style: { - // backgroundColor: `rgba(0, 255, 0, ${numeric * 0.0002})`, - // }, - // }); - // } - // } - // }, [rowIndex, columnId, setCellProps, data]); - // - // function getFormatted() { - // return data[rowIndex][columnId].formatted - // ? data[rowIndex][columnId].formatted - // : data[rowIndex][columnId]; - // } - // - // return data.hasOwnProperty(rowIndex) - // ? getFormatted(rowIndex, columnId) - // : null; - // }; - // }, []); + const renderCellValue = useMemo(() => { + return ({ rowIndex, columnId }) => { + // const data = useContext(DataContext); + useEffect(() => { + { + //Debug use + console.log("rowIndex: " + rowIndex + " columnId: " + columnId + " data: " + JSON.stringify(data[rowIndex]._source[columnId])); + return data[rowIndex]._source[columnId] ? data[rowIndex]._source[columnId] : null; + // return null; + } + }, [rowIndex, columnId, setCellProps, data]); + }; + }, []); return ( data[rowIndex][columnId]} - renderCellValue={({}) => null} + renderCellValue={({ rowIndex, columnId }) => { + //Debug use + console.log("rowIndex: " + rowIndex + " columnId: " + columnId + " data: " + JSON.stringify(data[rowIndex]._source[columnId])); + // return data[rowIndex]._source[columnId] ? data[rowIndex]._source[columnId] : null + return null; + }} + // renderCellValue={({}) => null} sorting={{ columns: sortingColumns, onSort }} pagination={{ ...pagination, From 475786575119a1d1bf978670ba16eabf2a3cd397 Mon Sep 17 00:00:00 2001 From: Annie Date: Fri, 30 Apr 2021 20:53:00 -0700 Subject: [PATCH 29/93] Able to show first 10 data --- .../DefineTransforms/DefineTransforms.tsx | 37 ++++++++++++------- 1 file changed, 23 insertions(+), 14 deletions(-) diff --git a/public/pages/CreateTransform/components/DefineTransforms/DefineTransforms.tsx b/public/pages/CreateTransform/components/DefineTransforms/DefineTransforms.tsx index ef2f0a388..42b4eb9ae 100644 --- a/public/pages/CreateTransform/components/DefineTransforms/DefineTransforms.tsx +++ b/public/pages/CreateTransform/components/DefineTransforms/DefineTransforms.tsx @@ -13,15 +13,14 @@ * permissions and limitations under the License. */ -import { EuiDataGrid, EuiDataGridColumn, EuiSpacer, EuiText } from "@elastic/eui"; +import { EuiDataGrid, EuiDataGridCellValueElementProps, EuiDataGridColumn, EuiSpacer, EuiText } from "@elastic/eui"; import { CoreStart } from "kibana/public"; -import React, { useState, useCallback, useEffect } from "react"; +import React, { useState, useCallback, useEffect, Component } from "react"; import { ContentPanel, ContentPanelActions } from "../../../../components/ContentPanel"; import { FieldItem } from "../../../../../models/interfaces"; import { TransformService } from "../../../../services"; import { getErrorMessage } from "../../../../utils/helpers"; import { useMemo } from "react"; -import * as repl from "repl"; interface DefineTransformsProps { transformService: TransformService; @@ -46,6 +45,7 @@ export default function DefineTransforms({ transformService, notifications, tran const fetchData = useCallback(async () => { console.log("Entering fetchData..."); + setLoading(true); try { const response = await transformService.searchSampleData(sourceIndex); @@ -54,11 +54,12 @@ export default function DefineTransforms({ transformService, notifications, tran console.log("Successfully searched sample data: " + JSON.stringify(response)); setData(response.response.data); setDataCount(response.response.total.value); - console.log("First item: " + JSON.stringify(response.response.data[0])); + // console.log("First item: " + JSON.stringify(response.response.data[0])); } } catch (err) { notifications.toasts.addDanger(getErrorMessage(err, "There was a problem loading the rollups")); } + setLoading(false); }, [sourceIndex]); React.useEffect(() => { @@ -84,16 +85,16 @@ export default function DefineTransforms({ transformService, notifications, tran ); const renderCellValue = useMemo(() => { - return ({ rowIndex, columnId }) => { + return ({ rowIndex, columnId }: EuiDataGridCellValueElementProps) => { // const data = useContext(DataContext); useEffect(() => { { //Debug use console.log("rowIndex: " + rowIndex + " columnId: " + columnId + " data: " + JSON.stringify(data[rowIndex]._source[columnId])); - return data[rowIndex]._source[columnId] ? data[rowIndex]._source[columnId] : null; - // return null; + if (!loading && data.hasOwnProperty(rowIndex)) return data[rowIndex]._source[columnId] ? data[rowIndex]._source[columnId] : null; + return null; } - }, [rowIndex, columnId, setCellProps, data]); + }, [rowIndex, columnId, data]); }; }, []); @@ -137,12 +138,20 @@ export default function DefineTransforms({ transformService, notifications, tran columns={columns} columnVisibility={{ visibleColumns, setVisibleColumns }} rowCount={dataCount} - renderCellValue={({ rowIndex, columnId }) => { - //Debug use - console.log("rowIndex: " + rowIndex + " columnId: " + columnId + " data: " + JSON.stringify(data[rowIndex]._source[columnId])); - // return data[rowIndex]._source[columnId] ? data[rowIndex]._source[columnId] : null - return null; - }} + renderCellValue={ + ({ rowIndex, columnId }) => { + //Debug use + console.log("rowIndex: " + rowIndex + " columnId: " + columnId + " data: " + JSON.stringify(data[rowIndex]._source[columnId])); + // return data[rowIndex]; + if (!loading && data.hasOwnProperty(rowIndex)) + return data[rowIndex]._source[columnId] ? data[rowIndex]._source[columnId] : null; + return null; + } + + // ({ rowIndex, columnId }) => + // `${rowIndex}, ${columnId}` + // renderCellValue + } // renderCellValue={({}) => null} sorting={{ columns: sortingColumns, onSort }} pagination={{ From 6fd8f5d3f3dcd718da8e69b497bea7abb70a69b0 Mon Sep 17 00:00:00 2001 From: Annie Date: Fri, 30 Apr 2021 21:13:09 -0700 Subject: [PATCH 30/93] Update renderCellValue --- .../DefineTransforms/DefineTransforms.tsx | 59 +++++++++++-------- 1 file changed, 33 insertions(+), 26 deletions(-) diff --git a/public/pages/CreateTransform/components/DefineTransforms/DefineTransforms.tsx b/public/pages/CreateTransform/components/DefineTransforms/DefineTransforms.tsx index 42b4eb9ae..ac723ab40 100644 --- a/public/pages/CreateTransform/components/DefineTransforms/DefineTransforms.tsx +++ b/public/pages/CreateTransform/components/DefineTransforms/DefineTransforms.tsx @@ -21,6 +21,7 @@ import { FieldItem } from "../../../../../models/interfaces"; import { TransformService } from "../../../../services"; import { getErrorMessage } from "../../../../utils/helpers"; import { useMemo } from "react"; +import { RollupQueryParams } from "../../../Rollups/models/interfaces"; interface DefineTransformsProps { transformService: TransformService; @@ -38,6 +39,8 @@ export default function DefineTransforms({ transformService, notifications, tran const [loading, setLoading] = useState(true); const [pagination, setPagination] = useState({ pageIndex: 0, pageSize: 10 }); + const [from, setFrom] = useState(0); + const [size, setSize] = useState(10); const [sortingColumns, setSortingColumns] = useState([]); const [visibleColumns, setVisibleColumns] = useState(() => columns.map(({ id }) => id)); const [data, setData] = useState([]); @@ -47,7 +50,7 @@ export default function DefineTransforms({ transformService, notifications, tran console.log("Entering fetchData..."); setLoading(true); try { - const response = await transformService.searchSampleData(sourceIndex); + const response = await transformService.searchSampleData(sourceIndex, { from, size }); if (response.ok) { //Debug use @@ -67,15 +70,26 @@ export default function DefineTransforms({ transformService, notifications, tran }, [fetchData]); const onChangeItemsPerPage = useCallback( - (pageSize) => + (pageSize) => { setPagination((pagination) => ({ ...pagination, pageSize, pageIndex: 0, - })), + })); + setFrom(0); + setSize(pageSize); + }, + [setPagination] + ); + const onChangePage = useCallback( + (pageIndex) => { + setPagination((pagination) => ({ ...pagination, pageIndex })); + setFrom(pageIndex * size); + //debug use + console.log("From: " + pageIndex * size); + }, [setPagination] ); - const onChangePage = useCallback((pageIndex) => setPagination((pagination) => ({ ...pagination, pageIndex })), [setPagination]); const onSort = useCallback( (sortingColumns) => { @@ -84,19 +98,12 @@ export default function DefineTransforms({ transformService, notifications, tran [setSortingColumns] ); - const renderCellValue = useMemo(() => { - return ({ rowIndex, columnId }: EuiDataGridCellValueElementProps) => { - // const data = useContext(DataContext); - useEffect(() => { - { - //Debug use - console.log("rowIndex: " + rowIndex + " columnId: " + columnId + " data: " + JSON.stringify(data[rowIndex]._source[columnId])); - if (!loading && data.hasOwnProperty(rowIndex)) return data[rowIndex]._source[columnId] ? data[rowIndex]._source[columnId] : null; - return null; - } - }, [rowIndex, columnId, data]); - }; - }, []); + const renderCellValue = ({ rowIndex, columnId }) => { + //Debug use + console.log("rowIndex: " + rowIndex + " columnId: " + columnId + " data: " + JSON.stringify(data[rowIndex]._source[columnId])); + if (!loading && data.hasOwnProperty(rowIndex)) return data[rowIndex]._source[columnId] ? data[rowIndex]._source[columnId] : null; + return null; + }; return ( { - //Debug use - console.log("rowIndex: " + rowIndex + " columnId: " + columnId + " data: " + JSON.stringify(data[rowIndex]._source[columnId])); - // return data[rowIndex]; - if (!loading && data.hasOwnProperty(rowIndex)) - return data[rowIndex]._source[columnId] ? data[rowIndex]._source[columnId] : null; - return null; - } + // ({ rowIndex, columnId }) => { + // //Debug use + // console.log("rowIndex: " + rowIndex + " columnId: " + columnId + " data: " + JSON.stringify(data[rowIndex]._source[columnId])); + // // return data[rowIndex]; + // if (!loading && data.hasOwnProperty(rowIndex)) + // return data[rowIndex]._source[columnId] ? data[rowIndex]._source[columnId] : null; + // return null; + // } // ({ rowIndex, columnId }) => // `${rowIndex}, ${columnId}` - // renderCellValue + renderCellValue } // renderCellValue={({}) => null} sorting={{ columns: sortingColumns, onSort }} From 16fefd3f5c863e764809c53626ea1ace7d6121aa Mon Sep 17 00:00:00 2001 From: Annie Date: Sun, 2 May 2021 15:47:26 -0700 Subject: [PATCH 31/93] Add popover options --- .../DefineTransforms/DefineTransforms.tsx | 134 ++++++++++++++---- .../CreateTransformStep2.tsx | 14 +- 2 files changed, 122 insertions(+), 26 deletions(-) diff --git a/public/pages/CreateTransform/components/DefineTransforms/DefineTransforms.tsx b/public/pages/CreateTransform/components/DefineTransforms/DefineTransforms.tsx index ac723ab40..a61964710 100644 --- a/public/pages/CreateTransform/components/DefineTransforms/DefineTransforms.tsx +++ b/public/pages/CreateTransform/components/DefineTransforms/DefineTransforms.tsx @@ -13,15 +13,14 @@ * permissions and limitations under the License. */ -import { EuiDataGrid, EuiDataGridCellValueElementProps, EuiDataGridColumn, EuiSpacer, EuiText } from "@elastic/eui"; +import { EuiDataGrid, EuiDataGridColumn, EuiSpacer, EuiText } from "@elastic/eui"; import { CoreStart } from "kibana/public"; -import React, { useState, useCallback, useEffect, Component } from "react"; +import React, { useState, useCallback } from "react"; import { ContentPanel, ContentPanelActions } from "../../../../components/ContentPanel"; import { FieldItem } from "../../../../../models/interfaces"; import { TransformService } from "../../../../services"; import { getErrorMessage } from "../../../../utils/helpers"; -import { useMemo } from "react"; -import { RollupQueryParams } from "../../../Rollups/models/interfaces"; +import { isNumericMapping } from "../../utils/helpers"; interface DefineTransformsProps { transformService: TransformService; @@ -29,12 +28,98 @@ interface DefineTransformsProps { transformId: string; sourceIndex: string; fields: FieldItem[]; + onGroupSelectionChange: void; + onAggregationSelectionChange: void; } -export default function DefineTransforms({ transformService, notifications, transfromId, sourceIndex, fields }: DefineTransformsProps) { +export default function DefineTransforms({ + transformService, + notifications, + transfromId, + sourceIndex, + fields, + onGroupSelectionChange, + onAggregationSelectionChange, +}: DefineTransformsProps) { let columns: EuiDataGridColumn[] = []; - fields.map((field: FieldItem) => columns.push({ id: field.label, displayAsText: field.label + " type: " + field.type })); + fields.map((field: FieldItem) => { + const isNumeric = isNumericMapping(field.type); + const isDate = field.type == "date"; + columns.push({ + id: field.label, + displayAsText: field.label + " type: " + field.type, + actions: { + showHide: false, + showMoveLeft: false, + showMoveRight: false, + showSortAsc: false, + showSortDesc: false, + additional: [ + { + label: "Group by histogram ", + onClick: () => {}, + size: "xs", + color: isNumeric ? "text" : "subdued", + }, + { + label: "Group by date histogram ", + onClick: () => {}, + size: "xs", + color: isDate ? "text" : "subdued", + }, + { + label: "Group by terms ", + onClick: () => {}, + size: "xs", + color: "text", + }, + { + label: "Aggregate by sum ", + onClick: () => {}, + size: "xs", + color: "text", + }, + { + label: "Aggregate by max ", + onClick: () => {}, + size: "xs", + color: "text", + }, + { + label: "Aggregate by min ", + onClick: () => {}, + size: "xs", + color: "text", + }, + { + label: "Aggregate by avg ", + onClick: () => {}, + size: "xs", + color: "text", + }, + { + label: "Aggregate by count ", + onClick: () => {}, + size: "xs", + color: "text", + }, + { + label: "Aggregate by percentile ", + onClick: () => {}, + size: "xs", + color: "text", + }, + { + label: "Aggregate by scripted metrics ", + onClick: () => {}, + size: "xs", + color: "text", + }, + ], + }, + }); + }); const [loading, setLoading] = useState(true); @@ -107,25 +192,24 @@ export default function DefineTransforms({ transformService, notifications, tran return ( - // onShow(ApplyPolicyModal, { - // indices: selectedItems.map((item: ManagedCatIndex) => item.index), - // core: this.context, - // }), - }, - }, - ]} - /> - } + //TODO: Add action to enter full screen view + // actions={ + // + // // onShow(ApplyPolicyModal, { + // // indices: selectedItems.map((item: ManagedCatIndex) => item.index), + // // core: this.context, + // // }), + // }, + // }, + // ]} + // /> + // } bodyStyles={{ padding: "10px 10px" }} title="Select fields to transform" titleSize="m" diff --git a/public/pages/CreateTransform/containers/CreateTransformStep2/CreateTransformStep2.tsx b/public/pages/CreateTransform/containers/CreateTransformStep2/CreateTransformStep2.tsx index 851fbb4c7..0c2496c6e 100644 --- a/public/pages/CreateTransform/containers/CreateTransformStep2/CreateTransformStep2.tsx +++ b/public/pages/CreateTransform/containers/CreateTransformStep2/CreateTransformStep2.tsx @@ -29,6 +29,8 @@ interface CreateTransformStep2Props extends RouteComponentProps { currentStep: number; sourceIndex: string; fields: FieldItem[]; + onGroupSelectionChange: void; + onAggregationSelectionChange: void; } export default class CreateTransformStep2 extends Component { @@ -46,7 +48,15 @@ export default class CreateTransformStep2 extends Component From e374f1911b214ce995c433f9965de257959992da Mon Sep 17 00:00:00 2001 From: Annie Date: Sun, 2 May 2021 16:51:48 -0700 Subject: [PATCH 32/93] Able to create transform with group by histogram --- models/interfaces.ts | 12 +++++ .../DefineTransforms/DefineTransforms.tsx | 48 ++++++++++++++----- .../CreateTransformForm.tsx | 48 ++++++++++--------- .../CreateTransformStep2.tsx | 4 +- public/services/TransformService.ts | 4 +- server/models/interfaces.ts | 11 +++++ server/services/TransformService.ts | 17 +++---- 7 files changed, 98 insertions(+), 46 deletions(-) diff --git a/models/interfaces.ts b/models/interfaces.ts index 8f57048d0..4f3f3fa55 100644 --- a/models/interfaces.ts +++ b/models/interfaces.ts @@ -250,3 +250,15 @@ export interface RollupMetricItem { } ]; } +export interface GroupItem { + sourceField: FieldItem; + targetField: string; + interval?: number; + aggregationMethod: GROUP_TYPES; +} + +export enum GROUP_TYPES { + histogram = "histogram", + dateHistogram = "date_histogram", + terms = "terms", +} diff --git a/public/pages/CreateTransform/components/DefineTransforms/DefineTransforms.tsx b/public/pages/CreateTransform/components/DefineTransforms/DefineTransforms.tsx index a61964710..503928712 100644 --- a/public/pages/CreateTransform/components/DefineTransforms/DefineTransforms.tsx +++ b/public/pages/CreateTransform/components/DefineTransforms/DefineTransforms.tsx @@ -15,9 +15,9 @@ import { EuiDataGrid, EuiDataGridColumn, EuiSpacer, EuiText } from "@elastic/eui"; import { CoreStart } from "kibana/public"; -import React, { useState, useCallback } from "react"; -import { ContentPanel, ContentPanelActions } from "../../../../components/ContentPanel"; -import { FieldItem } from "../../../../../models/interfaces"; +import React, { useCallback, useState } from "react"; +import { ContentPanel } from "../../../../components/ContentPanel"; +import { FieldItem, GROUP_TYPES, GroupItem } from "../../../../../models/interfaces"; import { TransformService } from "../../../../services"; import { getErrorMessage } from "../../../../utils/helpers"; import { isNumericMapping } from "../../utils/helpers"; @@ -28,7 +28,7 @@ interface DefineTransformsProps { transformId: string; sourceIndex: string; fields: FieldItem[]; - onGroupSelectionChange: void; + onGroupSelectionChange: (selectedFields: GroupItem[]) => void; onAggregationSelectionChange: void; } @@ -46,6 +46,7 @@ export default function DefineTransforms({ fields.map((field: FieldItem) => { const isNumeric = isNumericMapping(field.type); const isDate = field.type == "date"; + // TODO: Handle the available options according to column types columns.push({ id: field.label, displayAsText: field.label + " type: " + field.type, @@ -58,19 +59,40 @@ export default function DefineTransforms({ additional: [ { label: "Group by histogram ", - onClick: () => {}, + onClick: () => { + groupSelection.push({ + sourceField: field, + targetField: `${field.label}_${GROUP_TYPES.histogram}`, + aggregationMethod: GROUP_TYPES.histogram, + }); + onGroupSelectionChange(groupSelection); + }, size: "xs", color: isNumeric ? "text" : "subdued", }, { label: "Group by date histogram ", - onClick: () => {}, + onClick: () => { + groupSelection.push({ + sourceField: field, + targetField: `${field.label}_${GROUP_TYPES.dateHistogram}`, + aggregationMethod: GROUP_TYPES.dateHistogram, + }); + onGroupSelectionChange(groupSelection); + }, size: "xs", color: isDate ? "text" : "subdued", }, { label: "Group by terms ", - onClick: () => {}, + onClick: () => { + groupSelection.push({ + sourceField: field, + targetField: `${field.label}_${GROUP_TYPES.terms}`, + aggregationMethod: GROUP_TYPES.terms, + }); + onGroupSelectionChange(groupSelection); + }, size: "xs", color: "text", }, @@ -121,15 +143,16 @@ export default function DefineTransforms({ }); }); - const [loading, setLoading] = useState(true); + const [loading, setLoading] = useState(true); const [pagination, setPagination] = useState({ pageIndex: 0, pageSize: 10 }); - const [from, setFrom] = useState(0); - const [size, setSize] = useState(10); + const [from, setFrom] = useState(0); + const [size, setSize] = useState(10); const [sortingColumns, setSortingColumns] = useState([]); const [visibleColumns, setVisibleColumns] = useState(() => columns.map(({ id }) => id)); const [data, setData] = useState([]); - const [dataCount, setDataCount] = useState(0); + const [dataCount, setDataCount] = useState(0); + const [groupSelection, setGroupSelection] = useState([]); const fetchData = useCallback(async () => { console.log("Entering fetchData..."); @@ -252,6 +275,9 @@ export default function DefineTransforms({ onChangePage: onChangePage, }} /> + + {/*Debug use*/} + {JSON.stringify(groupSelection)} ); } diff --git a/public/pages/CreateTransform/containers/CreateTransformForm/CreateTransformForm.tsx b/public/pages/CreateTransform/containers/CreateTransformForm/CreateTransformForm.tsx index 0936cbf92..bcd18c5cf 100644 --- a/public/pages/CreateTransform/containers/CreateTransformForm/CreateTransformForm.tsx +++ b/public/pages/CreateTransform/containers/CreateTransformForm/CreateTransformForm.tsx @@ -16,13 +16,14 @@ import React, { ChangeEvent, Component } from "react"; import { EuiButton, EuiButtonEmpty, EuiComboBoxOptionOption, EuiFlexGroup, EuiFlexItem } from "@elastic/eui"; import { RouteComponentProps } from "react-router-dom"; +import moment from "moment"; import { RollupService, TransformService } from "../../../../services"; import { BREADCRUMBS, ROUTES } from "../../../../utils/constants"; import IndexService from "../../../../services/IndexService"; import { ManagedCatIndex } from "../../../../../server/models/interfaces"; import CreateTransform from "../CreateTransform"; import CreateTransformStep2 from "../CreateTransformStep2"; -import { DimensionItem, FieldItem, IndexItem, MetricItem, Transform } from "../../../../../models/interfaces"; +import { DimensionItem, FieldItem, GroupItem, IndexItem, MetricItem, Transform } from "../../../../../models/interfaces"; import { getErrorMessage } from "../../../../utils/helpers"; import { EMPTY_TRANSFORM } from "../../utils/constants"; import CreateTransformStep3 from "../CreateTransformStep3"; @@ -60,7 +61,7 @@ interface CreateTransformFormState { fields: FieldItem[]; selectedTerms: FieldItem[]; - selectedGroupField: DimensionItem[]; + selectedGroupField: GroupItem[]; selectedAggregations: MetricItem[]; // Needs to be Map aggregationsError: string; selectedFields: FieldItem[]; @@ -265,7 +266,7 @@ export default class CreateTransformForm extends Component { + onGroupSelectionChange = (selectedFields: GroupItem[]): void => { this.setState({ selectedGroupField: selectedFields }); }; @@ -319,28 +320,29 @@ export default class CreateTransformForm extends Component { if (group.aggregationMethod == "terms") { newJSON.transform.groups.push({ terms: { - source_field: group.field.label, - target_field: "", // needs target_field source, null target_field test + source_field: group.sourceField.label, + target_field: group.targetField, // needs target_field source, null target_field test }, }); } else if (group.aggregationMethod == "histogram") { newJSON.transform.groups.push({ histogram: { - source_field: group.field.label, - interval: group.interval, + source_field: group.sourceField.label, + //TODO: Remove the if else condition after implementing define interval + interval: group.interval ? group.interval : 5, }, }); } else { newJSON.transform.groups.push({ date_histogram: { - source_field: group.field.label, + source_field: group.sourceField.label, // need to fill out other date histogram data }, }); @@ -354,22 +356,22 @@ export default class CreateTransformForm extends Component { - const aggregations = []; - if (aggregation.min) aggregations.push({ min: {} }); - if (aggregation.max) aggregations.push({ max: {} }); - if (aggregation.sum) aggregations.push({ sum: {} }); - if (aggregation.avg) aggregations.push({ avg: {} }); - if (aggregation.value_count) aggregations.push({ value_count: {} }); - if (aggregation.percentiles) aggregations.push({ percentiles: {} }); - newJSON.transform.aggregations.push({ - source_field: aggregation.source_field.label, - aggregations: aggregations, - }); - }); + // selectedAggregations.map((aggregation) => { + // const aggregations = []; + // if (aggregation.min) aggregations.push({ min: {} }); + // if (aggregation.max) aggregations.push({ max: {} }); + // if (aggregation.sum) aggregations.push({ sum: {} }); + // if (aggregation.avg) aggregations.push({ avg: {} }); + // if (aggregation.value_count) aggregations.push({ value_count: {} }); + // if (aggregation.percentiles) aggregations.push({ percentiles: {} }); + // newJSON.transform.aggregations.push({ + // source_field: aggregation.source_field.label, + // aggregations: aggregations, + // }); + // }); this.setState({ transformJSON: newJSON }); }; diff --git a/public/pages/CreateTransform/containers/CreateTransformStep2/CreateTransformStep2.tsx b/public/pages/CreateTransform/containers/CreateTransformStep2/CreateTransformStep2.tsx index 0c2496c6e..bcee8245e 100644 --- a/public/pages/CreateTransform/containers/CreateTransformStep2/CreateTransformStep2.tsx +++ b/public/pages/CreateTransform/containers/CreateTransformStep2/CreateTransformStep2.tsx @@ -21,7 +21,7 @@ import { BREADCRUMBS, ROUTES } from "../../../../utils/constants"; import CreateTransformSteps from "../../components/CreateTransformSteps"; import { CoreServicesContext } from "../../../../components/core_services"; import DefineTransforms from "../../components/DefineTransforms"; -import { FieldItem } from "../../../../../models/interfaces"; +import { FieldItem, GroupItem } from "../../../../../models/interfaces"; interface CreateTransformStep2Props extends RouteComponentProps { transformService: TransformService; @@ -29,7 +29,7 @@ interface CreateTransformStep2Props extends RouteComponentProps { currentStep: number; sourceIndex: string; fields: FieldItem[]; - onGroupSelectionChange: void; + onGroupSelectionChange: (selectedFields: GroupItem[]) => void; onAggregationSelectionChange: void; } diff --git a/public/services/TransformService.ts b/public/services/TransformService.ts index eaee7728f..9282766ce 100644 --- a/public/services/TransformService.ts +++ b/public/services/TransformService.ts @@ -74,11 +74,11 @@ export default class TransformService { return response; }; - searchSampleData = async (index: string): Promise> => { + searchSampleData = async (index: string, queryObject: object): Promise> => { //Debug use console.log("Entering browser side service..."); const url = `..${NODE_API._SEARCH_SAMPLE_DATA}/${index}`; - const response = (await this.httpClient.get(url)) as ServerResponse; + const response = (await this.httpClient.get(url, { query: queryObject })) as ServerResponse; //Debug use console.log("response: " + JSON.stringify(response)); return response; diff --git a/server/models/interfaces.ts b/server/models/interfaces.ts index c36436592..15182efd4 100644 --- a/server/models/interfaces.ts +++ b/server/models/interfaces.ts @@ -220,6 +220,17 @@ export interface ExplainAPIManagedIndexMetaData { enabled: boolean; } +export interface SearchSampleDataResponse { + total: number; + data: { + _index: string; + _type: string; + _id: string; + _score: number; + _source: object; + }[]; +} + export interface IndexManagementApi { [API_ROUTE: string]: string; diff --git a/server/services/TransformService.ts b/server/services/TransformService.ts index cdb8cde16..847948163 100644 --- a/server/services/TransformService.ts +++ b/server/services/TransformService.ts @@ -321,15 +321,16 @@ export default class TransformService { try { // Debug use console.log("Entering server side service..."); - // const { from, size, search, sortField, sortDirection } = request.query as { - // from: string; - // size: string; - // search: string; - // sortField: string; - // sortDirection: string; - // }; + const { from, size } = request.query as { + from: string; + size: string; + }; const { index } = request.params as { index: string }; - const params = { index: index }; + const params = { + index: index, + from: from, + size: size, + }; const { callAsCurrentUser: callWithRequest } = this.esDriver.asScoped(request); const searchResponse: SearchResponse = await callWithRequest("search", params); From 1aa12cde9263f4642e51cc1bdb5acc55c39dd687 Mon Sep 17 00:00:00 2001 From: Annie Date: Sun, 2 May 2021 17:20:37 -0700 Subject: [PATCH 33/93] Able to create transform job with group by date histogram --- models/interfaces.ts | 8 +-- .../DefineTransforms/DefineTransforms.tsx | 23 ++++---- .../CreateTransformForm.tsx | 52 +++++++++---------- 3 files changed, 42 insertions(+), 41 deletions(-) diff --git a/models/interfaces.ts b/models/interfaces.ts index 4f3f3fa55..e4c810889 100644 --- a/models/interfaces.ts +++ b/models/interfaces.ts @@ -250,12 +250,8 @@ export interface RollupMetricItem { } ]; } -export interface GroupItem { - sourceField: FieldItem; - targetField: string; - interval?: number; - aggregationMethod: GROUP_TYPES; -} + +export type TransformGroupItem = DateHistogramItem | TermsItem | HistogramItem; export enum GROUP_TYPES { histogram = "histogram", diff --git a/public/pages/CreateTransform/components/DefineTransforms/DefineTransforms.tsx b/public/pages/CreateTransform/components/DefineTransforms/DefineTransforms.tsx index 503928712..dc2d54912 100644 --- a/public/pages/CreateTransform/components/DefineTransforms/DefineTransforms.tsx +++ b/public/pages/CreateTransform/components/DefineTransforms/DefineTransforms.tsx @@ -61,9 +61,11 @@ export default function DefineTransforms({ label: "Group by histogram ", onClick: () => { groupSelection.push({ - sourceField: field, - targetField: `${field.label}_${GROUP_TYPES.histogram}`, - aggregationMethod: GROUP_TYPES.histogram, + histogram: { + source_field: field.label, + target_field: `${field.label}_${GROUP_TYPES.histogram}`, + interval: 5, + }, }); onGroupSelectionChange(groupSelection); }, @@ -74,9 +76,10 @@ export default function DefineTransforms({ label: "Group by date histogram ", onClick: () => { groupSelection.push({ - sourceField: field, - targetField: `${field.label}_${GROUP_TYPES.dateHistogram}`, - aggregationMethod: GROUP_TYPES.dateHistogram, + terms: { + source_field: field.label, + target_field: `${field.label}_${GROUP_TYPES.dateHistogram}`, + }, }); onGroupSelectionChange(groupSelection); }, @@ -87,9 +90,11 @@ export default function DefineTransforms({ label: "Group by terms ", onClick: () => { groupSelection.push({ - sourceField: field, - targetField: `${field.label}_${GROUP_TYPES.terms}`, - aggregationMethod: GROUP_TYPES.terms, + date_histogram: { + source_field: field.label, + target_field: `${field.label}_${GROUP_TYPES.terms}`, + calendar_interval: "1d", + }, }); onGroupSelectionChange(groupSelection); }, diff --git a/public/pages/CreateTransform/containers/CreateTransformForm/CreateTransformForm.tsx b/public/pages/CreateTransform/containers/CreateTransformForm/CreateTransformForm.tsx index bcd18c5cf..3a75541fd 100644 --- a/public/pages/CreateTransform/containers/CreateTransformForm/CreateTransformForm.tsx +++ b/public/pages/CreateTransform/containers/CreateTransformForm/CreateTransformForm.tsx @@ -320,34 +320,34 @@ export default class CreateTransformForm extends Component { - if (group.aggregationMethod == "terms") { - newJSON.transform.groups.push({ - terms: { - source_field: group.sourceField.label, - target_field: group.targetField, // needs target_field source, null target_field test - }, - }); - } else if (group.aggregationMethod == "histogram") { - newJSON.transform.groups.push({ - histogram: { - source_field: group.sourceField.label, - //TODO: Remove the if else condition after implementing define interval - interval: group.interval ? group.interval : 5, - }, - }); - } else { - newJSON.transform.groups.push({ - date_histogram: { - source_field: group.sourceField.label, - // need to fill out other date histogram data - }, - }); - } - }); + // selectedGroupField.map((group) => { + // if (group.aggregationMethod == "terms") { + // newJSON.transform.groups.push({ + // terms: { + // source_field: group.sourceField.label, + // target_field: group.targetField, // needs target_field source, null target_field test + // }, + // }); + // } else if (group.aggregationMethod == "histogram") { + // newJSON.transform.groups.push({ + // histogram: { + // source_field: group.sourceField.label, + // //TODO: Remove the if else condition after implementing define interval + // interval: group.interval ? group.interval : 5, + // }, + // }); + // } else { + // newJSON.transform.groups.push({ + // date_histogram: { + // source_field: group.sourceField.label, + // // need to fill out other date histogram data + // }, + // }); + // } + // }); this.setState({ transformJSON: newJSON }); }; From e5692871fb1ab126298437ac66efdc29f8482216 Mon Sep 17 00:00:00 2001 From: Ravi <6005951+thalurur@users.noreply.github.com> Date: Mon, 3 May 2021 09:42:43 -0700 Subject: [PATCH 34/93] Get Transforms Page, Edit Transform page, Transform Details page (#168) --- models/interfaces.ts | 46 ++ public/index_management_app.tsx | 5 +- public/models/interfaces.ts | 3 +- public/pages/Main/Main.tsx | 56 ++- .../ConfigureTransform/Configure.tsx | 57 +++ .../components/ConfigureTransform/index.ts | 18 + .../components/DeleteModal/DeleteModal.tsx | 67 +++ .../components/DeleteModal/index.ts | 18 + .../GeneralInformation/GeneralInformation.tsx | 88 ++++ .../components/GeneralInformation/index.ts | 18 + .../components/Schedule/Schedule.tsx | 163 ++++++ .../Transforms/components/Schedule/index.ts | 18 + .../TransformEmptyPrompt.tsx | 71 +++ .../components/TransformEmptyPrompt/index.ts | 18 + .../TransformStatus/TransformStatus.tsx | 92 ++++ .../components/TransformStatus/index.ts | 18 + .../containers/Transforms/EditTransform.tsx | 292 +++++++++++ .../Transforms/TransformDetails.tsx | 444 ++++++++++++++++ .../Transforms/TransformSettings.tsx | 59 +++ .../containers/Transforms/Transforms.tsx | 476 ++++++++++++++++++ .../Transforms/containers/Transforms/index.ts | 20 + public/pages/Transforms/index.ts | 18 + public/pages/Transforms/models/interfaces.ts | 25 + public/pages/Transforms/utils/constants.tsx | 51 ++ public/pages/Transforms/utils/helpers.ts | 39 ++ .../pages/Transforms/utils/metadataHelper.tsx | 85 ++++ public/services/TransformService.ts | 71 +++ public/services/index.ts | 5 +- public/utils/constants.ts | 8 + server/clusters/ism/ismPlugin.ts | 85 ++++ server/models/interfaces.ts | 41 +- server/plugin.ts | 8 +- server/routes/index.ts | 3 +- server/routes/rollups.ts | 2 +- server/routes/transforms.ts | 104 ++++ server/services/TransformService.ts | 306 +++++++++++ server/services/index.ts | 3 +- server/utils/constants.ts | 2 + utils/constants.ts | 1 + 39 files changed, 2887 insertions(+), 17 deletions(-) create mode 100644 public/pages/Transforms/components/ConfigureTransform/Configure.tsx create mode 100644 public/pages/Transforms/components/ConfigureTransform/index.ts create mode 100644 public/pages/Transforms/components/DeleteModal/DeleteModal.tsx create mode 100644 public/pages/Transforms/components/DeleteModal/index.ts create mode 100644 public/pages/Transforms/components/GeneralInformation/GeneralInformation.tsx create mode 100644 public/pages/Transforms/components/GeneralInformation/index.ts create mode 100644 public/pages/Transforms/components/Schedule/Schedule.tsx create mode 100644 public/pages/Transforms/components/Schedule/index.ts create mode 100644 public/pages/Transforms/components/TransformEmptyPrompt/TransformEmptyPrompt.tsx create mode 100644 public/pages/Transforms/components/TransformEmptyPrompt/index.ts create mode 100644 public/pages/Transforms/components/TransformStatus/TransformStatus.tsx create mode 100644 public/pages/Transforms/components/TransformStatus/index.ts create mode 100644 public/pages/Transforms/containers/Transforms/EditTransform.tsx create mode 100644 public/pages/Transforms/containers/Transforms/TransformDetails.tsx create mode 100644 public/pages/Transforms/containers/Transforms/TransformSettings.tsx create mode 100644 public/pages/Transforms/containers/Transforms/Transforms.tsx create mode 100644 public/pages/Transforms/containers/Transforms/index.ts create mode 100644 public/pages/Transforms/index.ts create mode 100644 public/pages/Transforms/models/interfaces.ts create mode 100644 public/pages/Transforms/utils/constants.tsx create mode 100644 public/pages/Transforms/utils/helpers.ts create mode 100644 public/pages/Transforms/utils/metadataHelper.tsx create mode 100644 public/services/TransformService.ts create mode 100644 server/routes/transforms.ts create mode 100644 server/services/TransformService.ts diff --git a/models/interfaces.ts b/models/interfaces.ts index c8e4860a6..aff6065b9 100644 --- a/models/interfaces.ts +++ b/models/interfaces.ts @@ -67,6 +67,14 @@ export interface DocumentRollup { metadata: any; } +export interface DocumentTransform { + _id: string; + _seqNo: number; + _primaryTerm: number; + transform: Transform; + metadata: any; +} + // TODO: Fill out when needed // TODO: separate a frontend Policy from backendPolicy export interface Policy { @@ -125,6 +133,44 @@ export interface RollupMetadata { }; } +export interface Transform { + description: string; + groups: RollupDimensionItem[]; + enabled: boolean; + enabled_at: number | null; + updated_at: number; + metadata_id: string | null; + aggregations: Map; + page_size: number; + schedule: IntervalSchedule | CronSchedule; + schema_version: number; + source_index: string; + target_index: string; + roles: String[]; + data_selection_query: Map; +} + +export interface TransformMetadata { + metadata_id: string; + transform_metadata: { + id: string; + seq_no: number; + primary_term: number; + transform_id: string; + after_key: Map | null; + last_updated_at: number; + status: string; + failure_reason: string | null; + stats: { + pages_processed: number | null; + documents_processed: number | null; + documents_indexed: number | null; + index_time_in_millis: number | null; + search_time_in_millis: number | null; + } + } +} + export interface IntervalSchedule { interval: { startTime: number | null; diff --git a/public/index_management_app.tsx b/public/index_management_app.tsx index ac4d71bce..d60dc5ffa 100644 --- a/public/index_management_app.tsx +++ b/public/index_management_app.tsx @@ -18,7 +18,7 @@ import React from "react"; import ReactDOM from "react-dom"; import { render, unmountComponentAtNode } from "react-dom"; import { HashRouter as Router, Route } from "react-router-dom"; -import { IndexService, ManagedIndexService, PolicyService, RollupService, ServicesContext } from "./services"; +import { IndexService, ManagedIndexService, PolicyService, RollupService, TransformService, ServicesContext } from "./services"; import { DarkModeContext } from "./components/DarkMode"; import Main from "./pages/Main"; import { CoreServicesContext } from "./components/core_services"; @@ -30,7 +30,8 @@ export function renderApp(coreStart: CoreStart, params: AppMountParameters) { const managedIndexService = new ManagedIndexService(http); const policyService = new PolicyService(http); const rollupService = new RollupService(http); - const services = { indexService, managedIndexService, policyService, rollupService }; + const transformService = new TransformService(http); + const services = { indexService, managedIndexService, policyService, rollupService, transformService }; const isDarkMode = coreStart.uiSettings.get("theme:darkMode") || false; diff --git a/public/models/interfaces.ts b/public/models/interfaces.ts index 4fbade055..83d84d071 100644 --- a/public/models/interfaces.ts +++ b/public/models/interfaces.ts @@ -13,11 +13,12 @@ * permissions and limitations under the License. */ -import { IndexService, ManagedIndexService, PolicyService, RollupService } from "../services"; +import { IndexService, ManagedIndexService, PolicyService, RollupService, TransformService } from "../services"; export interface BrowserServices { indexService: IndexService; managedIndexService: ManagedIndexService; policyService: PolicyService; rollupService: RollupService; + transformService: TransformService; } diff --git a/public/pages/Main/Main.tsx b/public/pages/Main/Main.tsx index f930f2cb9..c48320e83 100644 --- a/public/pages/Main/Main.tsx +++ b/public/pages/Main/Main.tsx @@ -32,6 +32,8 @@ import { CoreServicesConsumer } from "../../components/core_services"; import CreateRollupForm from "../CreateRollup/containers/CreateRollupForm"; import EditRollup from "../EditRollup/containers"; import RollupDetails from "../RollupDetails/containers/RollupDetails"; +import { EditTransform, Transforms } from "../Transforms"; +import TransformDetails from "../Transforms/containers/Transforms/TransformDetails"; enum Navigation { IndexManagement = "Index Management", @@ -39,6 +41,7 @@ enum Navigation { ManagedIndices = "Managed Indices", Indices = "Indices", Rollups = "Rollup Jobs", + Transforms = "Transform Jobs", } enum Pathname { @@ -46,6 +49,7 @@ enum Pathname { ManagedIndices = "/managed-indices", Indices = "/indices", Rollups = "/rollups", + Transforms = "/transforms", } interface MainProps extends RouteComponentProps {} @@ -85,6 +89,12 @@ export default class Main extends Component { href: `#${Pathname.Rollups}`, isSelected: pathname === Pathname.Rollups, }, + { + name: Navigation.Transforms, + id: 5, + href: `#${Pathname.Transforms}`, + isSelected: pathname === Pathname.Transforms, + }, ], }, ]; @@ -99,11 +109,15 @@ export default class Main extends Component { {/*Hide side navigation bar when creating or editing rollup job*/} - {pathname != ROUTES.CREATE_ROLLUP && pathname != ROUTES.EDIT_ROLLUP && pathname != ROUTES.ROLLUP_DETAILS && ( - - - - )} + {pathname != ROUTES.CREATE_ROLLUP && + pathname != ROUTES.EDIT_ROLLUP && + pathname != ROUTES.ROLLUP_DETAILS && + pathname != ROUTES.EDIT_TRANSFORM && + pathname != ROUTES.TRANSFORM_DETAILS && ( + + + + )} {
)} /> + ( +
+ +
+ )} + /> + ( +
+ +
+ )} + /> + ( +
+ +
+ )} + /> + ( +
+ +
+ )} + />
diff --git a/public/pages/Transforms/components/ConfigureTransform/Configure.tsx b/public/pages/Transforms/components/ConfigureTransform/Configure.tsx new file mode 100644 index 000000000..c800cb5a1 --- /dev/null +++ b/public/pages/Transforms/components/ConfigureTransform/Configure.tsx @@ -0,0 +1,57 @@ +/* + * Copyright 2021 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"). + * You may not use this file except in compliance with the License. + * A copy of the License is located at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * or in the "license" file accompanying this file. This file is distributed + * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either + * express or implied. See the License for the specific language governing + * permissions and limitations under the License. + */ + +import React, { ChangeEvent } from "react"; +import { EuiSpacer, EuiFormRow, EuiFieldText, EuiTextArea, EuiText, EuiFlexGroup, EuiFlexItem } from "@elastic/eui"; +import { ContentPanel } from "../../../../components/ContentPanel"; + +interface ConfigureTransformProps { + inEdit: boolean; + transformId: string; + error: string; + onChangeName: (value: ChangeEvent) => void; + onChangeDescription: (value: ChangeEvent) => void; + description: string; +} + +const ConfigureTransform = ({ inEdit, transformId, error, onChangeName, onChangeDescription, description }: ConfigureTransformProps) => ( + +
+ + + + + + + + +

Description

+
+
+ + + - optional + + +
+ + + + +
+
+); + +export default ConfigureTransform; diff --git a/public/pages/Transforms/components/ConfigureTransform/index.ts b/public/pages/Transforms/components/ConfigureTransform/index.ts new file mode 100644 index 000000000..fd7952e60 --- /dev/null +++ b/public/pages/Transforms/components/ConfigureTransform/index.ts @@ -0,0 +1,18 @@ +/* + * Copyright 2021 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"). + * You may not use this file except in compliance with the License. + * A copy of the License is located at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * or in the "license" file accompanying this file. This file is distributed + * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either + * express or implied. See the License for the specific language governing + * permissions and limitations under the License. + */ + +import ConfigureTransform from "./Configure"; + +export default ConfigureTransform; diff --git a/public/pages/Transforms/components/DeleteModal/DeleteModal.tsx b/public/pages/Transforms/components/DeleteModal/DeleteModal.tsx new file mode 100644 index 000000000..1d6539e15 --- /dev/null +++ b/public/pages/Transforms/components/DeleteModal/DeleteModal.tsx @@ -0,0 +1,67 @@ +/* + * Copyright 2021 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"). + * You may not use this file except in compliance with the License. + * A copy of the License is located at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * or in the "license" file accompanying this file. This file is distributed + * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either + * express or implied. See the License for the specific language governing + * permissions and limitations under the License. + */ + +import React, { ChangeEvent, Component, Fragment } from "react"; +import { EuiConfirmModal, EuiForm, EuiFormRow, EuiFieldText, EuiOverlayMask, EuiSpacer } from "@elastic/eui"; + +// TODO: Merge with Rollup to create generic component +interface DeleteModalProps { + item: string; + closeDeleteModal: (event?: any) => void; + onClickDelete: (event?: any) => void; +} + +interface DeleteModalState { + confirmDeleteText: string; +} + +export default class DeleteModal extends Component { + state = { confirmDeleteText: "" }; + + onChange = (e: ChangeEvent): void => { + this.setState({ confirmDeleteText: e.target.value }); + }; + + render() { + const { item, closeDeleteModal, onClickDelete } = this.props; + const { confirmDeleteText } = this.state; + + return ( + + + + + By deleting "{item}", all future scheduled executions will be canceled. However, your target index + and data in it will remain intact. + + + + + + + + + ); + } +} diff --git a/public/pages/Transforms/components/DeleteModal/index.ts b/public/pages/Transforms/components/DeleteModal/index.ts new file mode 100644 index 000000000..0d2c98428 --- /dev/null +++ b/public/pages/Transforms/components/DeleteModal/index.ts @@ -0,0 +1,18 @@ +/* + * Copyright 2021 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"). + * You may not use this file except in compliance with the License. + * A copy of the License is located at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * or in the "license" file accompanying this file. This file is distributed + * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either + * express or implied. See the License for the specific language governing + * permissions and limitations under the License. + */ + +import DeleteModal from "./DeleteModal"; + +export default DeleteModal; diff --git a/public/pages/Transforms/components/GeneralInformation/GeneralInformation.tsx b/public/pages/Transforms/components/GeneralInformation/GeneralInformation.tsx new file mode 100644 index 000000000..5742547f3 --- /dev/null +++ b/public/pages/Transforms/components/GeneralInformation/GeneralInformation.tsx @@ -0,0 +1,88 @@ +/* + * Copyright 2021 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"). + * You may not use this file except in compliance with the License. + * A copy of the License is located at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * or in the "license" file accompanying this file. This file is distributed + * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either + * express or implied. See the License for the specific language governing + * permissions and limitations under the License. + */ + +import React, { Component } from "react"; +import { EuiFlexGrid, EuiSpacer, EuiFlexItem, EuiText } from "@elastic/eui"; +import { ContentPanel, ContentPanelActions } from "../../../../components/ContentPanel"; +import { ModalConsumer } from "../../../../components/Modal"; + +interface GeneralInformationProps { + id: string; + description: string; + sourceIndex: string; + targetIndex: string; + scheduledText: string; + pageSize: number; + updatedAt: number; + onEdit: () => void; +} + +export default class GenerationInformation extends Component { + constructor(props: GeneralInformationProps) { + super(props); + } + + render() { + const { id, description, sourceIndex, targetIndex, scheduledText, pageSize, updatedAt, onEdit } = this.props; + const infoItems = [ + { term: "Name", value: id }, + { term: "Source index", value: sourceIndex }, + { term: "Target index", value: targetIndex }, + { term: "Schedule", value: scheduledText }, + { term: "Description", value: description || "-" }, + { term: "UpdatedAt", value: updatedAt }, + { term: "Pages per execution", value: pageSize }, + ]; + + return ( + + {() => ( + onEdit(), + }, + }, + ]} + /> + )} + + } + bodyStyles={{ padding: "initial" }} + title="General information" + titleSize="m" + > +
+ + + {infoItems.map((item, index) => ( + + +
{item.term}
+
{item.value}
+
+
+ ))} +
+ +
+
+ ); + } +} diff --git a/public/pages/Transforms/components/GeneralInformation/index.ts b/public/pages/Transforms/components/GeneralInformation/index.ts new file mode 100644 index 000000000..dd311c3f6 --- /dev/null +++ b/public/pages/Transforms/components/GeneralInformation/index.ts @@ -0,0 +1,18 @@ +/* + * Copyright 2021 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"). + * You may not use this file except in compliance with the License. + * A copy of the License is located at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * or in the "license" file accompanying this file. This file is distributed + * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either + * express or implied. See the License for the specific language governing + * permissions and limitations under the License. + */ + +import GeneralInformation from "./GeneralInformation"; + +export default GeneralInformation; diff --git a/public/pages/Transforms/components/Schedule/Schedule.tsx b/public/pages/Transforms/components/Schedule/Schedule.tsx new file mode 100644 index 000000000..d16cc0853 --- /dev/null +++ b/public/pages/Transforms/components/Schedule/Schedule.tsx @@ -0,0 +1,163 @@ +/* + * Copyright 2021 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"). + * You may not use this file except in compliance with the License. + * A copy of the License is located at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * or in the "license" file accompanying this file. This file is distributed + * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either + * express or implied. See the License for the specific language governing + * permissions and limitations under the License. + */ + +import React, { ChangeEvent, Component } from "react"; +import moment from "moment-timezone"; +import { + EuiSpacer, + EuiCheckbox, + EuiAccordion, + EuiFormRow, + EuiSelect, + EuiFieldNumber, + EuiFlexGroup, + EuiFlexItem, + EuiTextArea, +} from "@elastic/eui"; +// @ts-ignore +import { htmlIdGenerator } from "@elastic/eui/lib/services"; +import { ScheduleIntervalTimeunitOptions } from "../../utils/constants"; +import { ContentPanel } from "../../../../components/ContentPanel"; + +interface ScheduleProps { + transformId: string; + error: string; + enabled: boolean; + pageSize: number; + onEnabledChange: () => void; + schedule: string; + interval: number; + intervalTimeUnit: string; + intervalError: string; + cronExpression: string; + cronTimeZone: string; + onPageChange: (e: ChangeEvent) => void; + onScheduleChange: (e: ChangeEvent) => void; + onCronExpressionChange: (e: ChangeEvent) => void; + onCronTimeZoneChange: (e: ChangeEvent) => void; + onIntervalChange: (e: ChangeEvent) => void; + onIntervalTimeUnitChange: (e: ChangeEvent) => void; +} + +const selectInterval = ( + interval: number, + intervalTimeunit: string, + intervalError: string, + onIntervalChange: (e: ChangeEvent) => void, + onIntervalTimeUnitChange: (value: ChangeEvent) => void +) => ( + + + + + + + + + + + + + + +); + +const timezones = moment.tz.names().map((tz) => ({ label: tz, text: tz })); + +// TODO: Check wording for page size form with UX team +export default class Schedule extends Component { + constructor(props: ScheduleProps) { + super(props); + } + + render() { + const { + enabled, + pageSize, + onEnabledChange, + onPageChange, + schedule, + onScheduleChange, + interval, + intervalError, + intervalTimeUnit, + onIntervalChange, + onIntervalTimeUnitChange, + cronExpression, + cronTimeZone, + onCronExpressionChange, + onCronTimeZoneChange, + } = this.props; + return ( + +
+ + + + + + + + + {schedule == "fixed" ? ( + selectInterval(interval, intervalTimeUnit, intervalError, onIntervalChange, onIntervalTimeUnitChange) + ) : ( + + + + + + + + + )} + + + + + + + + +
+
+ ); + } +} diff --git a/public/pages/Transforms/components/Schedule/index.ts b/public/pages/Transforms/components/Schedule/index.ts new file mode 100644 index 000000000..f18d83cb2 --- /dev/null +++ b/public/pages/Transforms/components/Schedule/index.ts @@ -0,0 +1,18 @@ +/* + * Copyright 2021 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"). + * You may not use this file except in compliance with the License. + * A copy of the License is located at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * or in the "license" file accompanying this file. This file is distributed + * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either + * express or implied. See the License for the specific language governing + * permissions and limitations under the License. + */ + +import Schedule from "./Schedule"; + +export default Schedule; diff --git a/public/pages/Transforms/components/TransformEmptyPrompt/TransformEmptyPrompt.tsx b/public/pages/Transforms/components/TransformEmptyPrompt/TransformEmptyPrompt.tsx new file mode 100644 index 000000000..9a4868e1d --- /dev/null +++ b/public/pages/Transforms/components/TransformEmptyPrompt/TransformEmptyPrompt.tsx @@ -0,0 +1,71 @@ +/* + * Copyright 2021 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"). + * You may not use this file except in compliance with the License. + * A copy of the License is located at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * or in the "license" file accompanying this file. This file is distributed + * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either + * express or implied. See the License for the specific language governing + * permissions and limitations under the License. + */ + +import React from "react"; +import { EuiButton, EuiEmptyPrompt, EuiText } from "@elastic/eui"; +import {PLUGIN_NAME, ROUTES} from "../../../../utils/constants"; + +interface TransformEmptyPromptProps { + filterIsApplied: boolean; + loading: boolean; + resetFilters: () => void; +} + +export const TEXT = { + RESET_FILTERS: "There are no transform jobs matching your applied filters. Reset your filters to view your transform jobs.", + NO_TRANSFORMS: + "Transform jobs help you create a materialized view on top of existing data.", + LOADING: "Loading transform jobs...", +}; + +const getMessagePrompt = ({ filterIsApplied, loading }: TransformEmptyPromptProps) => { + if (loading) return TEXT.LOADING; + if (filterIsApplied) return TEXT.RESET_FILTERS; + return TEXT.NO_TRANSFORMS; +}; + +const getActions: React.SFC = ({ filterIsApplied, loading, resetFilters }) => { + if (loading) { + return null; + } + + if (filterIsApplied) { + return ( + + Reset Filters + + ); + } + + return ( + + Create transform + + ); +}; + +const TransformEmptyPrompt: React.SFC = (props) => ( + +

{getMessagePrompt(props)}

+ + } + actions={getActions(props)} + /> +); + +export default TransformEmptyPrompt; diff --git a/public/pages/Transforms/components/TransformEmptyPrompt/index.ts b/public/pages/Transforms/components/TransformEmptyPrompt/index.ts new file mode 100644 index 000000000..7a5714db4 --- /dev/null +++ b/public/pages/Transforms/components/TransformEmptyPrompt/index.ts @@ -0,0 +1,18 @@ +/* + * Copyright 2021 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"). + * You may not use this file except in compliance with the License. + * A copy of the License is located at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * or in the "license" file accompanying this file. This file is distributed + * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either + * express or implied. See the License for the specific language governing + * permissions and limitations under the License. + */ + +import TransformEmptyPrompt from "./TransformEmptyPrompt"; + +export default TransformEmptyPrompt; diff --git a/public/pages/Transforms/components/TransformStatus/TransformStatus.tsx b/public/pages/Transforms/components/TransformStatus/TransformStatus.tsx new file mode 100644 index 000000000..a091160de --- /dev/null +++ b/public/pages/Transforms/components/TransformStatus/TransformStatus.tsx @@ -0,0 +1,92 @@ +/* + * Copyright 2021 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"). + * You may not use this file except in compliance with the License. + * A copy of the License is located at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * or in the "license" file accompanying this file. This file is distributed + * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either + * express or implied. See the License for the specific language governing + * permissions and limitations under the License. + */ + +import React, { Component } from "react"; +import { EuiFlexGrid, EuiSpacer, EuiFlexItem, EuiText } from "@elastic/eui"; +import { TransformMetadata } from "../../../../../models/interfaces"; +import { ContentPanel } from "../../../../components/ContentPanel"; +import { renderStatus } from "../../utils/metadataHelper"; + +interface TransformStatusProps { + metadata: TransformMetadata | undefined; +} + +export default class TransformStatus extends Component { + constructor(props: TransformStatusProps) { + super(props); + } + + render() { + const { metadata } = this.props; + return ( + +
+ + + + +
Status
+ {renderStatus(metadata)} +
+
+ + +
Documents indexed
+
+ {metadata == null || metadata.transform_metadata == null ? "-" : metadata.transform_metadata.stats.documents_indexed} +
+
+
+ + +
Indexed time (ms)
+
+ {metadata == null || metadata.transform_metadata == null ? "-" : metadata.transform_metadata.stats.index_time_in_millis} +
+
+
+ + + + +
Document processed
+
+ {metadata == null || metadata.transform_metadata == null ? "-" : metadata.transform_metadata.stats.documents_processed} +
+
+
+ + +
Search time (ms)
+
+ {metadata == null || metadata.transform_metadata == null ? "-" : metadata.transform_metadata.stats.search_time_in_millis} +
+
+
+ + + + +
Page processed
+
{metadata == null || metadata.transform_metadata == null ? "-" : metadata.transform_metadata.stats.pages_processed}
+
+
+
+ +
+
+ ); + } +} diff --git a/public/pages/Transforms/components/TransformStatus/index.ts b/public/pages/Transforms/components/TransformStatus/index.ts new file mode 100644 index 000000000..8f2f8df70 --- /dev/null +++ b/public/pages/Transforms/components/TransformStatus/index.ts @@ -0,0 +1,18 @@ +/* + * Copyright 2021 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"). + * You may not use this file except in compliance with the License. + * A copy of the License is located at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * or in the "license" file accompanying this file. This file is distributed + * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either + * express or implied. See the License for the specific language governing + * permissions and limitations under the License. + */ + +import TransformStatus from "./TransformStatus"; + +export default TransformStatus; diff --git a/public/pages/Transforms/containers/Transforms/EditTransform.tsx b/public/pages/Transforms/containers/Transforms/EditTransform.tsx new file mode 100644 index 000000000..9ba1eb804 --- /dev/null +++ b/public/pages/Transforms/containers/Transforms/EditTransform.tsx @@ -0,0 +1,292 @@ +/* + * Copyright 2021 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"). + * You may not use this file except in compliance with the License. + * A copy of the License is located at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * or in the "license" file accompanying this file. This file is distributed + * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either + * express or implied. See the License for the specific language governing + * permissions and limitations under the License. + */ + +import { RouteComponentProps } from "react-router-dom"; +import { TransformService } from "../../../../services"; +import { CoreServicesContext } from "../../../../components/core_services"; +import React, { ChangeEvent, Component } from "react"; +import { EMPTY_TRANSFORM } from "../../utils/constants"; +import queryString from "query-string"; +import { BREADCRUMBS, ROUTES } from "../../../../utils/constants"; +import { getErrorMessage } from "../../../../utils/helpers"; +import { EuiFlexItem, EuiFlexGroup, EuiButton, EuiTitle, EuiSpacer, EuiButtonEmpty } from "@elastic/eui"; +import ConfigureTransform from "../../components/ConfigureTransform"; +import Schedule from "../../components/Schedule"; +import moment from "moment"; +import { Transform } from "../../../../../models/interfaces"; + +interface EditTransformProps extends RouteComponentProps { + transformService: TransformService; +} + +interface EditTransformState { + id: string; + error: string; + seqNo: number | null; + primaryTerm: number | null; + description: string; + pageSize: number; + enabled: boolean; + transformJSON: any; + submitError: string; + isSubmitting: boolean; + hasSubmitted: boolean; + interval: number; + intervalError: string; + intervalTimeUnit: string; + cronExpression: string; + cronTimeZone: string; + schedule: string; +} + +export default class EditTransform extends Component { + static contextType = CoreServicesContext; + + constructor(props: EditTransformProps) { + super(props); + this.state = { + id: "", + error: "", + seqNo: null, + primaryTerm: null, + description: "", + pageSize: 1000, + enabled: true, + transformJSON: EMPTY_TRANSFORM, + isSubmitting: false, + interval: 2, + intervalError: "", + intervalTimeUnit: "MINUTES", + cronExpression: "", + cronTimeZone: "UTC", + schedule: "fixed", + submitError: "", + hasSubmitted: false, + }; + } + + componentDidMount = async () => { + const { id } = queryString.parse(this.props.location.search); + if (typeof id === "string" && !!id) { + this.context.chrome.setBreadcrumbs([BREADCRUMBS.INDEX_MANAGEMENT, BREADCRUMBS.TRANSFORMS, BREADCRUMBS.EDIT_TRANSFORM, { text: id }]); + await this.getTransform(id); + } else { + this.context.notifications.toasts.addDanger(`Invalid transform id: ${id}`); + this.props.history.push(ROUTES.TRANSFORMS); + } + }; + + getTransform = async (transformId: string) => { + try { + const { transformService } = this.props; + const response = await transformService.getTransform(transformId); + + if (response.ok) { + let json = JSON.parse(this.state.transformJSON); + json.transform = response.response.transform; + + this.setState({ + seqNo: response.response._seqNo, + primaryTerm: response.response._primaryTerm, + id: response.response._id, + description: response.response.transform.description, + enabled: response.response.transform.enabled, + pageSize: response.response.transform.page_size, + transformJSON: json, + }); + } else { + this.context.notifications.toasts.addDanger(`Could not load transform job ${transformId}: ${response.error}`); + this.props.history.push(ROUTES.TRANSFORMS); + } + } catch (err) { + this.context.notifications.toasts.addDanger(getErrorMessage(err, `Could not load transform job ${transformId}`)); + this.props.history.push(ROUTES.TRANSFORMS); + } + }; + + render() { + const { + id, + error, + pageSize, + description, + isSubmitting, + enabled, + interval, + intervalError, + intervalTimeUnit, + cronExpression, + cronTimeZone, + schedule, + } = this.state; + return ( +
+ +

Edit transform job

+
+ + + + + + + + + + + Cancel + + + + + Save changes + + + +
+ ); + } + + onCancel = () => { + this.props.history.push(ROUTES.TRANSFORMS); + }; + + onSubmit = async (): Promise => { + const { id, transformJSON } = this.state; + this.setState({ submitError: "", isSubmitting: true, hasSubmitted: true }); + try { + this.updateSchedule(); + await this.update(id, transformJSON); + } catch (err) { + this.context.notifications.toasts.addDanger("Invalid Transform JSON"); + console.error(err); + } + }; + + updateSchedule = () => { + const { schedule, cronExpression, cronTimeZone, interval, intervalTimeUnit } = this.state; + let json = this.state.transformJSON; + if (schedule == "cron") { + json.transform.schedule.cron = { expression: `${cronExpression}`, timezone: `${cronTimeZone}` }; + delete json.transform.schedule["interval"]; + } else { + json.transform.schedule.interval = { + start_time: moment().unix(), + unit: intervalTimeUnit, + period: interval, + }; + delete json.transform.schedule["cron"]; + } + + this.setState({ transformJSON: json }); + }; + + update = async (transformId: string, transform: Transform): Promise => { + try { + const { transformService } = this.props; + const { primaryTerm, seqNo } = this.state; + if (primaryTerm == null || seqNo == null) { + this.context.notifications.toasts.addDanger("Could not update transform without seqNo and primaryTerm"); + return; + } + const response = await transformService.putTransform(transform, transformId, seqNo, primaryTerm); + if (response.ok) { + this.context.notifications.toasts.addSuccess(`Changes to transform saved`); + this.props.history.push(ROUTES.TRANSFORMS); + } else { + this.context.notifications.toasts.addDanger(`Couldn't update transform ${transformId}: ${response.error}`); + this.setState({ submitError: response.error }); + } + } catch (err) { + this.setState({ submitError: getErrorMessage(err, `Couldn't update transform ${transformId}`) }); + } + }; + + onNameChange = (e: ChangeEvent) => { + // DO NOTHING SINCE edit is disabled for this page + }; + + onDescriptionChange = (e: ChangeEvent) => { + const description = e.target.value; + let json = this.state.transformJSON; + json.transform.description = description; + this.setState({ transformJSON: json, description: description }); + }; + + onEnabledChange = () => { + const enabled = this.state.enabled; + let json = this.state.transformJSON; + json.transform.enabled = enabled; + this.setState({ transformJSON: json, enabled: !enabled }); + }; + + onCronExpressionChange = (e: ChangeEvent) => { + this.setState({ cronExpression: e.target.value }); + }; + + onCronTimeZoneChange = (e: ChangeEvent) => { + this.setState({ cronTimeZone: e.target.value }); + }; + + onIntervalChange = (e: ChangeEvent) => { + this.setState({ interval: e.target.valueAsNumber }); + if (e.target.value == "") { + const intervalError = "Interval value is required."; + this.setState({ intervalError: intervalError }); + } else { + this.setState({ intervalError: "" }); + } + }; + + onPageChange = (e: ChangeEvent) => { + const pageSize = e.target.valueAsNumber; + let json = this.state.transformJSON; + json.transform.pageSize = pageSize; + this.setState({ pageSize: pageSize, transformJSON: json }); + }; + + onScheduleChange = (e: ChangeEvent) => { + this.setState({ schedule: e.target.value }); + }; + + onIntervalTimeUnitChange = (e: ChangeEvent) => { + this.setState({ intervalTimeUnit: e.target.value }); + }; +} diff --git a/public/pages/Transforms/containers/Transforms/TransformDetails.tsx b/public/pages/Transforms/containers/Transforms/TransformDetails.tsx new file mode 100644 index 000000000..b4e60e3e1 --- /dev/null +++ b/public/pages/Transforms/containers/Transforms/TransformDetails.tsx @@ -0,0 +1,444 @@ +/* + * Copyright 2021 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"). + * You may not use this file except in compliance with the License. + * A copy of the License is located at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * or in the "license" file accompanying this file. This file is distributed + * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either + * express or implied. See the License for the specific language governing + * permissions and limitations under the License. + */ + +import { + EuiSpacer, + EuiTitle, + EuiFlexGroup, + EuiFlexItem, + EuiButton, + EuiOverlayMask, + EuiButtonEmpty, + EuiModalFooter, + EuiModal, + EuiModalHeader, + EuiModalHeaderTitle, + EuiModalBody, + EuiCodeBlock, + EuiHealth, + EuiContextMenuItem, + EuiContextMenuPanel, + EuiTextColor, + EuiPopover, +} from "@elastic/eui"; +import { TransformService } from "../../../../services"; +import { RouteComponentProps } from "react-router-dom"; +import React, { Component } from "react"; +import { CoreServicesContext } from "../../../../components/core_services"; +import { BREADCRUMBS, ROUTES } from "../../../../utils/constants"; +import queryString from "query-string"; +import { getErrorMessage } from "../../../../utils/helpers"; +import { DimensionItem, MetricItem, RollupDimensionItem, TransformMetadata } from "../../../../../models/interfaces"; +import DeleteModal from "../../components/DeleteModal"; +import GenerationInformation from "../../components/GeneralInformation"; +import TransformStatus from "../../components/TransformStatus"; +import { EMPTY_TRANSFORM } from "../../utils/constants"; +import TransformSettings from "./TransformSettings"; + +interface TransformDetailsProps extends RouteComponentProps { + transformService: TransformService; +} + +interface TransformDetailsState { + id: string; + description: string; + enabled: boolean; + enabledAt: number | null; + updatedAt: number; + pageSize: number; + transformJson: any; + sourceIndex: string; + targetIndex: string; + aggregationsShown: MetricItem[]; + groupsShown: DimensionItem[]; + metadata: TransformMetadata | undefined; + interval: number; + intervalTimeUnit: string; + cronExpression: string; + isModalOpen: boolean; + isDeleteModalOpen: boolean; + isPopOverOpen: boolean; +} + +export default class TransformDetails extends Component { + static contextType = CoreServicesContext; + constructor(props: TransformDetailsProps) { + super(props); + + this.state = { + id: "", + description: "", + enabled: false, + enabledAt: null, + updatedAt: 1, + pageSize: 1000, + transformJson: EMPTY_TRANSFORM, + sourceIndex: "", + targetIndex: "", + aggregationsShown: [], + groupsShown: [], + metadata: undefined, + interval: 2, + intervalTimeUnit: "", + cronExpression: "", + isModalOpen: false, + isDeleteModalOpen: false, + isPopOverOpen: false, + }; + } + + componentDidMount = async (): Promise => { + this.context.chrome.setBreadcrumbs([BREADCRUMBS.INDEX_MANAGEMENT, BREADCRUMBS.TRANSFORMS]); + const { id } = queryString.parse(this.props.location.search); + if (typeof id === "string") { + this.context.chrome.setBreadcrumbs([BREADCRUMBS.INDEX_MANAGEMENT, BREADCRUMBS.TRANSFORMS, { text: id }]); + this.props.history.push(`${ROUTES.TRANSFORM_DETAILS}?id=${id}`); + await this.getTransform(id); + this.forceUpdate(); + } else { + this.context.notifications.toasts.addDanger(`Invalid transform id: ${id}`); + this.props.history.push(ROUTES.TRANSFORMS); + } + }; + + getTransform = async (transformId: string) => { + try { + const { transformService } = this.props; + const response = await transformService.getTransform(transformId); + + if (response.ok) { + let json = response.response; + // let aggregations = this.parseAggregations(response.response.transform.aggregations); + let groups = this.parseGroups(response.response.transform.groups); + this.setState({ + id: response.response._id, + description: response.response.transform.description, + enabled: response.response.transform.enabled, + enabledAt: response.response.transform.enabled_at, + updatedAt: response.response.transform.updated_at, + pageSize: response.response.transform.page_size, + transformJson: json, + sourceIndex: response.response.transform.source_index, + targetIndex: response.response.transform.target_index, + aggregationsShown: [], + groupsShown: groups.slice(0, 10), + }); + + if (response.response.metadata != null) { + this.setState({ metadata: response.response.metadata[response.response._id] }); + } + if ("interval" in response.response.transform.schedule) { + this.setState({ + interval: response.response.transform.schedule.interval.period, + intervalTimeUnit: response.response.transform.schedule.interval.unit, + }); + } else { + this.setState({ cronExpression: response.response.transform.schedule.cron.expression }); + } + } else { + this.context.notifications.toasts.addDanger(`Could not load transform job ${transformId}: ${response.error}`); + this.props.history.push(ROUTES.TRANSFORMS); + } + } catch (err) { + this.context.notifications.toasts.addDanger(getErrorMessage(err, `Could not load transform job ${transformId}`)); + this.props.history.push(ROUTES.TRANSFORMS); + } + }; + + parseGroups = (groups: RollupDimensionItem[]): DimensionItem[] => { + const sourceArray = groups.slice(1, groups.length); + if (sourceArray.length == 0) return []; + // @ts-ignore + return sourceArray.map((group: RollupDimensionItem) => { + let sequence = groups.indexOf(group); + switch (true) { + case group.date_histogram != null: + return { + sequence: sequence, + aggregationMethod: "date_histogram", + field: { + label: group.date_histogram?.source_field, + }, + interval: group.date_histogram?.interval, + }; + case group.histogram != null: + return { + sequence: sequence, + aggregationMethod: "histogram", + field: { + label: group.histogram?.source_field, + }, + interval: group.histogram?.interval, + }; + case group.terms != null: + return { + sequence: sequence, + aggregationMethod: "terms", + field: { + label: group.terms?.source_field, + }, + interval: null, + }; + } + }); + }; + + render() { + const { + id, + enabled, + updatedAt, + description, + sourceIndex, + targetIndex, + pageSize, + metadata, + transformJson, + isDeleteModalOpen, + isModalOpen, + isPopOverOpen, + } = this.state; + + let scheduleText = "At some time"; + const actionButton = ( + + Actions + + ); + + const actionItems = [ + { + this.closePopover(); + this.onEnable(); + }} + > + Enable job + , + { + this.closePopover(); + this.onDisable(); + }} + > + Disable job + , + { + this.closePopover(); + this.showJsonModal(); + }} + > + View JSON + , + { + this.closePopover(); + this.showDeleteModal(); + }} + > + Duplicate job + , + { + this.closePopover(); + this.showDeleteModal(); + }} + > + Delete + , + ]; + + return ( +
+ + + +

{id}

+
+
+ + {enabled ? {"Enabled on " + updatedAt} : Disabled} + + + + + + + + + + + + View target index in Discover + + + + +
+ + + + + + + + + + + {isModalOpen && ( + + + + {"View JSON of " + id} + + + + + {JSON.stringify(transformJson, null, 4)} + + + + + Close + + + + )} + + {isDeleteModalOpen && } +
+ ); + } + + onClickDelete = async () => { + const { id } = this.state; + const { transformService } = this.props; + try { + const response = await transformService.deleteTransform(id); + if (response.ok) { + this.closeDeleteModal(); + this.context.notification.toasts.addSuccess(`"${id}" successfully deleted!`); + this.props.history.push(ROUTES.TRANSFORMS); + } else { + this.context.notifications.toasts.addDanger(`could not delete transform job "${id}" : ${response.error}`); + } + } catch (err) { + this.context.notification.toasts.addDanger(getErrorMessage(err, "Could not delete the transform job")); + } + }; + + onEdit = () => { + const { id } = this.state; + this.props.history.push(`${ROUTES.EDIT_TRANSFORM}?id=${id}`); + }; + + onEnable = async () => { + const { id } = this.state; + const { transformService } = this.props; + try { + const response = await transformService.startTransform(id); + if (response.ok) { + this.setState({ enabled: true }); + await this.getTransform(id); + this.forceUpdate(); + this.context.notifications.toasts.addSuccess(`${id} is enabled`); + } else { + this.context.notifications.toasts.addDanger(`Could not enable transform job "${id}": ${response.error}`); + } + } catch (err) { + this.context.notifications.toasts.addDanger(getErrorMessage(err, `Could not enable transform job ${id}`)); + } + }; + + onDisable = async () => { + const { id } = this.state; + const { transformService } = this.props; + try { + const response = await transformService.stopTransform(id); + if (response.ok) { + this.setState({ enabled: false }); + await this.getTransform(id); + this.forceUpdate(); + this.context.notifications.toasts.addSuccess(`${id} is disabled`); + } else { + this.context.notifications.toasts.addDanger(`Could not disable transform job "${id}": ${response.error}`); + } + } catch (err) { + this.context.notifications.toasts.addDanger(getErrorMessage(err, `Could not disable transform job ${id}`)); + } + }; + + showJsonModal = () => { + this.setState({ isModalOpen: true }); + }; + + closeModal = () => { + this.setState({ isModalOpen: false }); + }; + + showDeleteModal = () => { + this.setState({ isDeleteModalOpen: true }); + }; + + closeDeleteModal = () => { + this.setState({ isDeleteModalOpen: false }); + }; + + onActionButtonClick = () => { + this.setState({ isPopOverOpen: !this.state.isPopOverOpen }); + }; + + closePopover = () => { + this.setState({ isPopOverOpen: false }); + }; +} diff --git a/public/pages/Transforms/containers/Transforms/TransformSettings.tsx b/public/pages/Transforms/containers/Transforms/TransformSettings.tsx new file mode 100644 index 000000000..99a804d16 --- /dev/null +++ b/public/pages/Transforms/containers/Transforms/TransformSettings.tsx @@ -0,0 +1,59 @@ +/* + * Copyright 2021 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"). + * You may not use this file except in compliance with the License. + * A copy of the License is located at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * or in the "license" file accompanying this file. This file is distributed + * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either + * express or implied. See the License for the specific language governing + * permissions and limitations under the License. + */ + +import React, { Component } from "react"; +import { EuiSpacer, EuiText, EuiAccordion } from "@elastic/eui"; +// @ts-ignore +import { htmlIdGenerator } from "@elastic/eui/lib/services"; +import { ContentPanel } from "../../../../components/ContentPanel"; +import { TransformService } from "../../../../services"; + +interface TransformSettingsProps { + transformService: TransformService; + transformJson: Map; +} + +interface TransformSettingsState {} + +export default class TransformSettings extends Component { + constructor(props: TransformSettingsProps) { + super(props); + this.state = {}; + } + + render() { + return ( + +
+ + +

Groups

+
+
+ +
+ // TODO: Use the source data preview table from create workflow // TODO: Use the transformed preview table from create workflow +
+
+
+ ); + } + + onClick = async () => { + const response = await this.props.transformService.previewTransform(this.props.transformJson); + console.log(response); + console.log("tada"); + }; +} diff --git a/public/pages/Transforms/containers/Transforms/Transforms.tsx b/public/pages/Transforms/containers/Transforms/Transforms.tsx new file mode 100644 index 000000000..9057680da --- /dev/null +++ b/public/pages/Transforms/containers/Transforms/Transforms.tsx @@ -0,0 +1,476 @@ +/* + * Copyright 2021 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"). + * You may not use this file except in compliance with the License. + * A copy of the License is located at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * or in the "license" file accompanying this file. This file is distributed + * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either + * express or implied. See the License for the specific language governing + * permissions and limitations under the License. + */ + +import { + // @ts-ignore + Criteria, + Direction, + EuiPanel, + EuiFlexGroup, + EuiFlexItem, + EuiTitle, + EuiButton, + EuiBasicTable, + EuiPopover, + EuiContextMenuPanel, + EuiFieldSearch, + EuiHorizontalRule, + EuiPagination, + EuiLink, + EuiTextColor, + EuiTableFieldDataColumnType, + EuiContextMenuItem, + // @ts-ignore + Pagination, + EuiTableSelectionType, + EuiTableSortingType, +} from "@elastic/eui"; +import queryString from "query-string"; +import { RouteComponentProps } from "react-router-dom"; +import TransformService from "../../../../services/TransformService"; +import { DocumentTransform } from "../../../../../models/interfaces"; +import React, { Component } from "react"; +import { CoreServicesContext } from "../../../../components/core_services"; +import { getURLQueryParams, renderTime } from "../../utils/helpers"; +import { TransformQueryParams } from "../../models/interfaces"; +import { getErrorMessage } from "../../../../utils/helpers"; +import { BREADCRUMBS, ROUTES } from "../../../../utils/constants"; +import DeleteModal from "../../components/DeleteModal"; +import TransformEmptyPrompt from "../../components/TransformEmptyPrompt"; +import { renderEnabled, renderStatus } from "../../utils/metadataHelper"; +import { DEFAULT_PAGE_SIZE_OPTIONS, DEFAULT_QUERY_PARAMS } from "../../../Indices/utils/constants"; +import _ from "lodash"; +import { ManagedCatIndex } from "../../../../../server/models/interfaces"; + +interface TransformProps extends RouteComponentProps { + transformService: TransformService; +} + +interface TransformState { + totalTransforms: number; + from: number; + size: number; + search: string; + sortField: keyof DocumentTransform; + sortDirection: Direction; + selectedItems: DocumentTransform[]; + transforms: DocumentTransform[]; + fetchingTransforms: boolean; + transformMetadata: {}; + isPopOverOpen: boolean; + isDeleteModalVisible: boolean; +} + +export default class Transforms extends Component { + static contextType = CoreServicesContext; + constructor(props: TransformProps) { + super(props); + + const { from, size, search, sortField, sortDirection } = getURLQueryParams(this.props.location); + + this.state = { + totalTransforms: 0, + from, + size, + search, + sortField, + sortDirection, + selectedItems: [], + transforms: [], + fetchingTransforms: true, + transformMetadata: {}, + isPopOverOpen: false, + isDeleteModalVisible: false, + }; + + this.getTransforms = _.debounce(this.getTransforms, 500, { leading: true }); + } + + async componentDidMount() { + this.context.chrome.setBreadcrumbs([BREADCRUMBS.INDEX_MANAGEMENT, BREADCRUMBS.TRANSFORMS]); + await this.getTransforms(); + } + + async componentDidUpdate(prevProps: TransformProps, prevState: TransformState) { + const prevQuery = Transforms.getQueryObjectFromState(prevState); + const currQuery = Transforms.getQueryObjectFromState(this.state); + if (!_.isEqual(prevQuery, currQuery)) { + await this.getTransforms(); + } + } + + render() { + const { + totalTransforms, + from, + size, + search, + sortField, + sortDirection, + selectedItems, + transforms, + fetchingTransforms, + isPopOverOpen, + isDeleteModalVisible, + } = this.state; + + const filterIsApplied = !!search; + const pageCount = Math.ceil(totalTransforms / size) || 1; + const page = Math.floor(from / size); + const pagination: Pagination = { + pageIndex: page, + pageSize: size, + pageSizeOptions: DEFAULT_PAGE_SIZE_OPTIONS, + totalItemCount: totalTransforms, + }; + + const columns: EuiTableFieldDataColumnType[] = [ + { + field: "_id", + name: "Name", + sortable: true, + textOnly: true, + truncateText: true, + render: (_id) => ( + this.props.history.push(`${ROUTES.TRANSFORM_DETAILS}?id=${_id}`)} data-test-subj={`transformLink_${_id}`}> + {_id} + + ), + }, + { + field: "transform.source_index", + name: "Source index", + sortable: true, + textOnly: true, + truncateText: true, + }, + { + field: "transform.target_index", + name: "Target index", + sortable: true, + textOnly: true, + truncateText: true, + }, + { + field: "transform.enabled", + name: "Job state", + sortable: true, + textOnly: true, + truncateText: true, + render: renderEnabled, + }, + { + field: "transform.updated_at", + name: "Last updated time", + sortable: true, + textOnly: true, + render: (updated_at) => renderTime(updated_at), + }, + { + field: "metadata", + name: "transform job status", + sortable: false, + textOnly: true, + render: (metadata) => renderStatus(metadata), + }, + ]; + + const actionButton = ( + + Actions + + ); + + const actionItems = [ + { + this.closePopover(); + this.onClickEdit(); + }} + > + Edit + , + { + this.closePopover(); + this.showDeleteModal(); + }} + > + Delete + , + ]; + + const selection: EuiTableSelectionType = { + onSelectionChange: this.onSelectionChange, + }; + + const sorting: EuiTableSortingType = { + sort: { + direction: sortDirection, + field: sortField, + }, + }; + + return ( + + + + +

{"Transform jobs (" + `${transforms.length}` + ")"}

+
+
+ + + + + Disable + + + + { + this.onEnable(); + }} + data-test-subj="enableButton" + > + Enable + + + + + + + + + + Create transform job + + + + +
+ +
+ + + + + {pageCount > 1 && ( + + + + )} + + + + + + } + onChange={this.onTableChange} + pagination={pagination} + selection={selection} + sorting={sorting} + tableLayout="auto" + /> + {isDeleteModalVisible && ( + + )} +
+
+ ); + } + + getTransforms = async () => { + this.setState({ fetchingTransforms: true }); + try { + const { transformService, history } = this.props; + const queryObject = Transforms.getQueryObjectFromState(this.state); + const queryParamsString = queryString.stringify(Transforms.getQueryObjectFromState(this.state)); + history.replace({ ...this.props.location, search: queryParamsString }); + const response = await transformService.getTransforms(queryObject); + if (response.ok) { + const { transforms, totalTransforms, metadata } = response.response; + this.setState({ transforms, totalTransforms, transformMetadata: metadata }); + } else { + this.context.notifications.toasts.addDanger(response.error); + } + } catch (err) { + this.context.notifications.toasts.addDanger(getErrorMessage(err, "There was problem loading transforms")); + } + this.setState({ fetchingTransforms: false }); + }; + + getSelectedTransformIds = () => { + return "asd"; + // this.state.selectedItems.map((item: DocumentTransform) => { return item._id }).join(", "); + }; + + onSelectionChange = (selectedItems: DocumentTransform[]): void => { + this.setState({ selectedItems }); + }; + + showDeleteModal = () => { + this.setState({ isDeleteModalVisible: true }); + }; + + closeDeleteModal = () => { + this.setState({ isDeleteModalVisible: false }); + }; + + onClickCreate = () => { + this.props.history.push(ROUTES.CREATE_TRANSFORM); + }; + + onClickEdit = () => { + const { + selectedItems: [{ _id }], + } = this.state; + if (_id) this.props.history.push(`${ROUTES.EDIT_TRANSFORM}?id=${_id}`); + }; + + onClickDelete = async () => { + const { transformService } = this.props; + const { selectedItems } = this.state; + for (let item of selectedItems) { + const transformId = item._id; + try { + const response = await transformService.deleteTransform(transformId); + + if (response.ok) { + this.closeDeleteModal(); + this.context.notification.toasts.addSuccess(`"${transformId}" successfully deleted!`); + } else { + this.context.notifications.toasts.addDanger(`could not delete transform job "${transformId}" : ${response.error}`); + } + } catch (err) { + this.context.notification.toasts.addDanger(getErrorMessage(err, "Could not delete the transform job")); + } + } + + await this.getTransforms(); + }; + + onPageClick = (page: number) => { + this.setState({ from: page * this.state.size }); + }; + + onTableChange = ({ page: tablePage, sort }: Criteria) => { + const { index: page, size } = tablePage; + const { field: sortField, direction: sortDirection } = sort; + this.setState({ from: page * size, size, sortField, sortDirection }); + }; + + closePopover = () => { + this.setState({ isPopOverOpen: false }); + }; + + resetFilters = () => { + this.setState({ search: DEFAULT_QUERY_PARAMS.search }); + }; + + onEnable = async () => { + const { transformService } = this.props; + const { selectedItems } = this.state; + + for (const item of selectedItems) { + const transformId = item._id; + try { + const response = await transformService.startTransform(transformId); + + if (response.ok) { + this.context.notifications.toasts.addSuccess(`${transformId} is enabled`); + } else { + this.context.notifications.toasts.addDanger(`Could not start transform job "${transformId}": ${response.error}`); + } + } catch (err) { + this.context.notifications.toasts.addDanger(getErrorMessage(err, `Could not start transform job ${transformId}`)); + } + } + + await this.getTransforms(); + }; + + onDisable = async () => { + const { transformService } = this.props; + const { selectedItems } = this.state; + + for (const item of selectedItems) { + const transformId = item._id; + try { + const response = await transformService.stopTransform(transformId); + + if (response.ok) { + this.context.notifications.toasts.addSuccess(`${transformId} is disabled`); + } else { + this.context.notifications.toasts.addDanger(`Could not stop transform job "${transformId}": ${response.error}`); + } + } catch (err) { + this.context.notifications.toasts.addDanger(getErrorMessage(err, `Could not stop transform job ${transformId}`)); + } + } + + await this.getTransforms(); + }; + + onSearchChange = (e: React.ChangeEvent) => { + this.setState({ from: 0, search: e.target.value }); + }; + + onActionButtonClick = () => { + this.setState({ isPopOverOpen: !this.state.isPopOverOpen }); + }; + + static getQueryObjectFromState({ from, size, search, sortField, sortDirection }: TransformState): TransformQueryParams { + return { from, size, search, sortField, sortDirection }; + } +} diff --git a/public/pages/Transforms/containers/Transforms/index.ts b/public/pages/Transforms/containers/Transforms/index.ts new file mode 100644 index 000000000..9ccbf849d --- /dev/null +++ b/public/pages/Transforms/containers/Transforms/index.ts @@ -0,0 +1,20 @@ +/* + * Copyright 2021 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"). + * You may not use this file except in compliance with the License. + * A copy of the License is located at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * or in the "license" file accompanying this file. This file is distributed + * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either + * express or implied. See the License for the specific language governing + * permissions and limitations under the License. + */ + +import EditTransform from "./EditTransform"; +import Transforms from "./Transforms"; +import TransformSettings from "./TransformSettings"; + +export { Transforms, EditTransform, TransformSettings }; diff --git a/public/pages/Transforms/index.ts b/public/pages/Transforms/index.ts new file mode 100644 index 000000000..7f224906b --- /dev/null +++ b/public/pages/Transforms/index.ts @@ -0,0 +1,18 @@ +/* + * Copyright 2021 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"). + * You may not use this file except in compliance with the License. + * A copy of the License is located at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * or in the "license" file accompanying this file. This file is distributed + * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either + * express or implied. See the License for the specific language governing + * permissions and limitations under the License. + */ + +import { EditTransform, Transforms } from "./containers/Transforms"; + +export { Transforms, EditTransform }; diff --git a/public/pages/Transforms/models/interfaces.ts b/public/pages/Transforms/models/interfaces.ts new file mode 100644 index 000000000..620afece3 --- /dev/null +++ b/public/pages/Transforms/models/interfaces.ts @@ -0,0 +1,25 @@ +/* + * Copyright 2021 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"). + * You may not use this file except in compliance with the License. + * A copy of the License is located at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * or in the "license" file accompanying this file. This file is distributed + * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either + * express or implied. See the License for the specific language governing + * permissions and limitations under the License. + */ + +import { Direction } from "@elastic/eui"; +import {DocumentTransform} from "../../../../models/interfaces"; + +export interface TransformQueryParams { + from: number; + size: number; + search: string; + sortField: keyof DocumentTransform; + sortDirection: Direction; +} diff --git a/public/pages/Transforms/utils/constants.tsx b/public/pages/Transforms/utils/constants.tsx new file mode 100644 index 000000000..2adea0dbe --- /dev/null +++ b/public/pages/Transforms/utils/constants.tsx @@ -0,0 +1,51 @@ +/* + * Copyright 2021 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"). + * You may not use this file except in compliance with the License. + * A copy of the License is located at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * or in the "license" file accompanying this file. This file is distributed + * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either + * express or implied. See the License for the specific language governing + * permissions and limitations under the License. + */ + +import { SortDirection } from "../../../utils/constants"; + +// TODO: Consolidate with Rollup +export const DEFAULT_QUERY_PARAMS = { + from: 0, + size: 20, + search: "", + sortField: "_id", + sortDirection: SortDirection.DESC, +}; + +export const EMPTY_TRANSFORM = JSON.stringify({ + transform: { + description: "", + groups: [], + enabled: true, + aggregations: {}, + data_selection_query: {}, + roles: [], + schedule: { + interval: { + start_time: 234802, + period: 1, + unit: "MINUTES", + }, + }, + source_index: "", + target_index: "", + }, +}); + +export const ScheduleIntervalTimeunitOptions = [ + { value: "MINUTES", text: "Minute(s)" }, + { value: "HOURS", text: "Hour(s)" }, + { value: "DAYS", text: "Day(s)" }, +]; diff --git a/public/pages/Transforms/utils/helpers.ts b/public/pages/Transforms/utils/helpers.ts new file mode 100644 index 000000000..687e289ab --- /dev/null +++ b/public/pages/Transforms/utils/helpers.ts @@ -0,0 +1,39 @@ +/* + * Copyright 2021 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"). + * You may not use this file except in compliance with the License. + * A copy of the License is located at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * or in the "license" file accompanying this file. This file is distributed + * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either + * express or implied. See the License for the specific language governing + * permissions and limitations under the License. + */ + +import queryString from "query-string"; +import { TransformQueryParams } from "../models/interfaces"; +import { DEFAULT_QUERY_PARAMS } from "./constants"; +import moment from "moment"; + +export function getURLQueryParams(location: { search: string }): TransformQueryParams { + const { from, size, search, sortField, sortDirection } = queryString.parse(location.search); + + return { + // @ts-ignores + from: isNaN(parseInt(from, 10)) ? DEFAULT_QUERY_PARAMS.from : parseInt(from, 10), + // @ts-ignores + size: isNaN(parseInt(size, 10)) ? DEFAULT_QUERY_PARAMS.size : parseInt(size, 10), + search: typeof search !== "string" ? DEFAULT_QUERY_PARAMS.search : search, + sortField: typeof sortField !== "string" ? DEFAULT_QUERY_PARAMS.sortField : sortField, + sortDirection: typeof sortDirection !== "string" ? DEFAULT_QUERY_PARAMS.sortDirection : sortDirection, + }; +} + +export const renderTime = (time: number): string => { + const momentTime = moment(time).local(); + if (time && momentTime.isValid()) return momentTime.format("MM/DD/YY h:mmA"); + return "-"; +}; diff --git a/public/pages/Transforms/utils/metadataHelper.tsx b/public/pages/Transforms/utils/metadataHelper.tsx new file mode 100644 index 000000000..c05f4c6b5 --- /dev/null +++ b/public/pages/Transforms/utils/metadataHelper.tsx @@ -0,0 +1,85 @@ +/* + * Copyright 2021 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"). + * You may not use this file except in compliance with the License. + * A copy of the License is located at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * or in the "license" file accompanying this file. This file is distributed + * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either + * express or implied. See the License for the specific language governing + * permissions and limitations under the License. + */ + +import {TransformMetadata} from "../../../../models/interfaces"; +import React from "react"; +import { EuiFlexGroup, EuiFlexItem, EuiIcon, EuiText } from "@elastic/eui"; + +// TODO: merge with rollup helper to have a common helper +export const renderStatus = (metadata: TransformMetadata | undefined): JSX.Element => { + if (metadata == null || metadata.transform_metadata == null) return
-
; + let icon; + let iconColor; + let textColor: "default" | "subdued" | "secondary" | "ghost" | "accent" | "warning" | "danger" | undefined; + let text; + switch (metadata.transform_metadata.status) { + case "failed": + icon = "alert"; + iconColor = "danger"; + textColor = "danger"; + text = "Failed: " + metadata.transform_metadata.failure_reason; + break; + case "finished": + icon = "check"; + iconColor = "success"; + textColor = "secondary"; + text = "Complete"; + break; + case "init": + return ( + + + + + + + Initializing + + + + ); + case "started": + icon = "play"; + iconColor = "success"; + textColor = "secondary"; + text = "Started"; + break; + case "stopped": + icon = "stop"; + iconColor = "subdued"; + textColor = "subdued"; + text = "Stopped"; + break; + default: + return
-
; + } + + return ( + + + + + + + {text} + + + + ); +}; + +export const renderEnabled = (isEnabled: boolean): string => { + return isEnabled ? "Enabled" : "Disabled"; +}; diff --git a/public/services/TransformService.ts b/public/services/TransformService.ts new file mode 100644 index 000000000..718fea4ed --- /dev/null +++ b/public/services/TransformService.ts @@ -0,0 +1,71 @@ +/* + * Copyright 2021 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"). + * You may not use this file except in compliance with the License. + * A copy of the License is located at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * or in the "license" file accompanying this file. This file is distributed + * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either + * express or implied. See the License for the specific language governing + * permissions and limitations under the License. + */ + +import { HttpSetup } from "kibana/public"; +import { ServerResponse } from "../../server/models/types"; +import { GetTransformsResponse, PutTransformResponse } from "../../server/models/interfaces"; +import { NODE_API } from "../../utils/constants"; +import { DocumentTransform, Transform } from "../../models/interfaces"; + +export default class TransformService { + httpClient: HttpSetup; + + constructor(httpClient: HttpSetup) { + this.httpClient = httpClient; + } + + getTransforms = async (queryObject: object): Promise> => { + const url = `..${NODE_API.TRANSFORMS}`; + // @ts-ignore + return (await this.httpClient.get(url, { query: queryObject })) as ServerResponse; + }; + + putTransform = async ( + transform: Transform, + transformId: string, + seqNo?: number, + primaryTerm?: number + ): Promise> => { + const url = `..${NODE_API.TRANSFORMS}/${transformId}`; + return (await this.httpClient.put(url, { query: { seqNo, primaryTerm }, body: JSON.stringify(transform) })) as ServerResponse< + PutTransformResponse + >; + }; + + getTransform = async (transformId: string): Promise> => { + const url = `..${NODE_API.TRANSFORMS}/${transformId}`; + return (await this.httpClient.get(url)) as ServerResponse; + }; + + deleteTransform = async (transformId: string): Promise> => { + const url = `..${NODE_API.TRANSFORMS}/${transformId}`; + return (await this.httpClient.delete(url)) as ServerResponse; + }; + + startTransform = async (transformId: string): Promise> => { + const url = `..${NODE_API.TRANSFORMS}/${transformId}/_start`; + return (await this.httpClient.post(url)) as ServerResponse; + }; + + stopTransform = async (transformId: string): Promise> => { + const url = `..${NODE_API.TRANSFORMS}/${transformId}/_stop`; + return (await this.httpClient.post(url)) as ServerResponse; + }; + + previewTransform = async (transform: Map): Promise>> => { + const url = `..${NODE_API.TRANSFORMS}/_preview`; + return (await this.httpClient.post(url, { body: JSON.stringify(transform) })) as ServerResponse>; + }; +} diff --git a/public/services/index.ts b/public/services/index.ts index b1402f8f4..ecd048210 100644 --- a/public/services/index.ts +++ b/public/services/index.ts @@ -18,5 +18,8 @@ import IndexService from "./IndexService"; import ManagedIndexService from "./ManagedIndexService"; import PolicyService from "./PolicyService"; import RollupService from "./RollupService"; +import TransformService from "./TransformService"; -export { ServicesConsumer, ServicesContext, IndexService, ManagedIndexService, PolicyService, RollupService }; +export { + ServicesConsumer, ServicesContext, IndexService, ManagedIndexService, PolicyService, RollupService, TransformService +}; diff --git a/public/utils/constants.ts b/public/utils/constants.ts index 19368625f..61db333ca 100644 --- a/public/utils/constants.ts +++ b/public/utils/constants.ts @@ -30,6 +30,10 @@ export const ROUTES = Object.freeze({ CREATE_ROLLUP: "/create-rollup", EDIT_ROLLUP: "/edit-rollup", ROLLUP_DETAILS: "/rollup-details", + TRANSFORMS: "/transforms", + CREATE_TRANSFORM: "/create-transform", + EDIT_TRANSFORM: "/edit-transform", + TRANSFORM_DETAILS: "/transform-details", }); export const BREADCRUMBS = Object.freeze({ @@ -44,6 +48,10 @@ export const BREADCRUMBS = Object.freeze({ CREATE_ROLLUP: { text: "Create rollup job" }, EDIT_ROLLUP: { text: "Edit rollup job" }, ROLLUP_DETAILS: { text: "Rollup details" }, + TRANSFORMS: { text: "Transform jobs", href: `#${ROUTES.TRANSFORMS}` }, + CREATE_TRANSFORM: { text: "Create transform job" }, + EDIT_TRANSFORM: { text: "Edit transform job" }, + TRANSFORM_DETAILS: { text: "Transform details" }, }); // TODO: Kibana EUI has a SortDirection already diff --git a/server/clusters/ism/ismPlugin.ts b/server/clusters/ism/ismPlugin.ts index e9e1acc96..04ddfa9e5 100644 --- a/server/clusters/ism/ismPlugin.ts +++ b/server/clusters/ism/ismPlugin.ts @@ -270,4 +270,89 @@ export default function ismPlugin(Client: any, config: any, components: any) { }, method: "GET", }); + + ism.getTransform = ca({ + url: { + fmt: `${API.TRANSFORM_BASE}/<%=transformId%>`, + req: { + transformId: { + type: "string", + required: true, + }, + }, + }, + method: "GET", + }); + + ism.getTransforms = ca({ + url: { + fmt: `${API.TRANSFORM_BASE}/`, + }, + method: "GET", + }); + + ism.explainTransform = ca({ + url: { + fmt: `${API.TRANSFORM_BASE}/<%=transformId%>/_explain`, + req: { + transformId: { + type: "string", + required: true, + }, + }, + }, + method: "GET", + }); + + ism.startTransform = ca({ + url: { + fmt: `${API.TRANSFORM_BASE}/<%=transformId%>/_start`, + req: { + transformId: { + type: "string", + required: true, + }, + }, + }, + method: "POST", + }); + + ism.stopTransform = ca({ + url: { + fmt: `${API.TRANSFORM_BASE}/<%=transformId%>/_stop`, + req: { + transformId: { + type: "string", + required: true, + }, + }, + }, + method: "POST", + }); + + ism.deleteTransform = ca({ + url: { + fmt: `${API.TRANSFORM_BASE}/<%=transformId%>`, + req: { + transformId: { + type: "string", + required: true, + }, + }, + }, + method: "DELETE", + }); + + ism.putTransform = ca({ + url: { + fmt: `${API.TRANSFORM_BASE}/<%=transformId%>`, + req: { + transformId: { + type: "string", + required: true, + }, + }, + }, + method: "PUT", + }); } diff --git a/server/models/interfaces.ts b/server/models/interfaces.ts index 1075be34d..3269f07be 100644 --- a/server/models/interfaces.ts +++ b/server/models/interfaces.ts @@ -13,14 +13,22 @@ * permissions and limitations under the License. */ -import { IndexService, ManagedIndexService, PolicyService, RollupService } from "../services"; -import { DocumentPolicy, DocumentRollup, ManagedIndexItem, Rollup } from "../../models/interfaces"; +import { IndexService, ManagedIndexService, PolicyService, RollupService, TransformService } from "../services"; +import { + DocumentPolicy, + DocumentRollup, + DocumentTransform, + ManagedIndexItem, + Rollup, + Transform +} from "../../models/interfaces"; export interface NodeServices { indexService: IndexService; managedIndexService: ManagedIndexService; policyService: PolicyService; rollupService: RollupService; + transformService: TransformService; } export interface SearchResponse { @@ -88,6 +96,23 @@ export interface PutRollupResponse { rollup: { rollup: Rollup }; } +export interface DeleteTransformResponse { + result: string; +} + +export interface GetTransformsResponse { + transforms: DocumentTransform[]; + totalTransforms: number; + metadata: any; +} + +export interface PutTransformResponse { + _id: string; + _primary_term: string; + _seq_no: string; + transform: { transform: Transform }; +} + export interface IndexUpdateResponse { updatedIndices: number; failures: boolean; @@ -139,6 +164,17 @@ export interface PutRollupParams { body: string; } +export interface PutTransformParams { + transformId: string; + if_seq_no?: string; + if_primary_term?: string; + body: string; +} + +export interface DeleteTransformParams { + transformId: string; +} + // TODO: remove optional failedIndices after fixing retry API to always array export interface RetryResponse { failures: boolean; @@ -201,6 +237,7 @@ export interface IndexManagementApi { readonly REMOVE_POLICY_BASE: string; readonly CHANGE_POLICY_BASE: string; readonly ROLLUP_JOBS_BASE: string; + readonly TRANSFORM_BASE: string; } export interface DefaultHeaders { diff --git a/server/plugin.ts b/server/plugin.ts index b29558d83..fa94579a1 100644 --- a/server/plugin.ts +++ b/server/plugin.ts @@ -16,8 +16,8 @@ import { IndexManagementPluginSetup, IndexManagementPluginStart } from "."; import { Plugin, CoreSetup, CoreStart, IClusterClient } from "../../../src/core/server"; import ismPlugin from "./clusters/ism/ismPlugin"; -import { PolicyService, ManagedIndexService, IndexService, RollupService } from "./services"; -import { indices, policies, managedIndices, rollups } from "../server/routes"; +import { PolicyService, ManagedIndexService, IndexService, RollupService, TransformService } from "./services"; +import { indices, policies, managedIndices, rollups, transforms } from "../server/routes"; export class IndexPatternManagementPlugin implements Plugin { public async setup(core: CoreSetup) { @@ -31,7 +31,8 @@ export class IndexPatternManagementPlugin implements Plugin>> => { + try { + const { from, size, search, sortDirection, sortField } = request.query as { + from: number; + size: number; + search: string; + sortDirection: string; + sortField: string; + }; + + const transformSortMap: { [key: string]: string } = { + _id: "transform.transform_id.keyword", + "transform.source_index": "transform.source_index.keyword", + "transform.target_index": "transform.target_index.keyword", + "transform.transform.enabled": "transform.enabled", + }; + + const params = { + from: parseInt(from, 10), + size: parseInt(size, 10), + search, + sortField: transformSortMap[sortField] || transformSortMap._id, + sortDirection, + }; + + const { callAsCurrentUser: callWithRequest } = this.esDriver.asScoped(request); + const getTransformsResponse = await callWithRequest("ism.getTransforms", params); + const totalTransforms = getTransformsResponse.total_transforms; + const transforms = getTransformsResponse.transforms.map((transform: DocumentTransform) => ({ + _seqNo: transform._seqNo as number, + _primaryTerm: transform._primaryTerm as number, + _id: transform._id, + transform: transform.transform, + metadata: null, + })); + if (totalTransforms) { + const ids = transforms.map((transform: DocumentTransform) => transform._id).join(","); + const explainResponse = await callWithRequest("ism.explainTransform", { transformId: ids }); + if (!explainResponse.error) { + transforms.map((transform: DocumentTransform) => { + transform.metadata = explainResponse[transform._id]; + }); + + return response.custom({ + statusCode: 200, + body: { ok: true, response: { transforms: transforms, totalTransforms: totalTransforms, metadata: explainResponse } }, + }); + } else { + return response.custom({ + statusCode: 200, + body: { + ok: false, + error: explainResponse ? explainResponse.error : "An error occurred when calling getExplain API.", + }, + }); + } + } + + return response.custom({ + statusCode: 200, + body: { ok: true, response: { transforms: transforms, totalTransforms: totalTransforms, metadata: {} } }, + }); + } catch (err) { + if (err.statusCode === 404 && err.body.error.type === "index_not_found_exception") { + return response.custom({ + statusCode: 200, + body: { ok: true, response: { transforms: [], totalTransforms: 0, metadata: null } }, + }); + } + console.error("Index Management - TransformService - getTransforms", err); + return response.custom({ + statusCode: 200, + body: { + ok: false, + error: "Error in getTransforms" + err.message, + }, + }); + } + }; + + getTransform = async ( + context: RequestHandlerContext, + request: KibanaRequest, + response: KibanaResponseFactory + ): Promise>> => { + try { + const { id } = request.params as { id: string }; + const params = { transformId: id }; + const { callAsCurrentUser: callWithRequest } = this.esDriver.asScoped(request); + const getResponse = await callWithRequest("ism.getTransform", params); + const metadata = await callWithRequest("ism.explainTransform", params); + const transform = _.get(getResponse, "transform", null); + const seqNo = _.get(getResponse, "_seq_no", null); + const primaryTerm = _.get(getResponse, "_primary_term", null); + + if (transform) { + if (metadata) { + return response.custom({ + statusCode: 200, + body: { + ok: true, + response: { + _id: id, + _seqNo: seqNo as number, + _primaryTerm: primaryTerm as number, + transform: transform as Transform, + metadata: metadata, + }, + }, + }); + } else { + return response.custom({ + statusCode: 200, + body: { + ok: false, + error: "Failed to load metadata for transform", + }, + }); + } + } else { + return response.custom({ + statusCode: 200, + body: { + ok: false, + error: "Failed to load transform", + }, + }); + } + } catch (err) { + console.error("Index Management - TransformService - getTransform:", err); + return response.custom({ + statusCode: 200, + body: { + ok: false, + error: err.message, + }, + }); + } + }; + + startTransform = async ( + context: RequestHandlerContext, + request: KibanaRequest, + response: KibanaResponseFactory + ): Promise>> => { + try { + const { id } = request.params as { id: string }; + console.log("received " + JSON.stringify(request.params)); + const params = { transformId: id }; + const { callAsCurrentUser: callWithRequest } = this.esDriver.asScoped(request); + const startResponse = await callWithRequest("ism.startTransform", params); + const acknowledged = _.get(startResponse, "acknowledged"); + if (acknowledged) { + return response.custom({ + statusCode: 200, + body: { ok: true, response: true }, + }); + } else { + return response.custom({ + statusCode: 200, + body: { ok: false, error: "Failed to start transform" }, + }); + } + } catch (err) { + console.error("Index Management - TransformService - startTransform", err); + return response.custom({ + statusCode: 200, + body: { ok: false, error: err.message }, + }); + } + }; + + stopTransform = async ( + context: RequestHandlerContext, + request: KibanaRequest, + response: KibanaResponseFactory + ): Promise>> => { + try { + const { id } = request.params as { id: string }; + const params = { transformId: id }; + const { callAsCurrentUser: callWithRequest } = this.esDriver.asScoped(request); + const stopResponse = await callWithRequest("ism.stopTransform", params); + const acknowledged = _.get(stopResponse, "acknowledged"); + if (acknowledged) { + return response.custom({ + statusCode: 200, + body: { ok: true, response: true }, + }); + } else { + return response.custom({ + statusCode: 200, + body: { ok: false, error: "Failed to stop transform" }, + }); + } + } catch (err) { + console.error("Index Management - TransformService - stopTransform", err); + return response.custom({ + statusCode: 200, + body: { ok: false, error: err.message }, + }); + } + }; + + deleteTransform = async ( + context: RequestHandlerContext, + request: KibanaRequest, + response: KibanaResponseFactory + ): Promise>> => { + try { + const { id } = request.params as { id: string }; + const params = { transformId: id }; + const { callAsCurrentUser: callWithRequest } = this.esDriver.asScoped(request); + const deleteResponse = await callWithRequest("ism.deleteTransform", params); + const acknowledged = _.get(deleteResponse, "acknowledged"); + if (acknowledged) { + return response.custom({ + statusCode: 200, + body: { ok: true, response: true }, + }); + } else { + return response.custom({ + statusCode: 200, + body: { ok: false, error: "Failed to delete transform" }, + }); + } + } catch (err) { + console.error("Index Management - TransformService - deleteTransform", err); + return response.custom({ + statusCode: 200, + body: { ok: false, error: err.message }, + }); + } + }; + + putTransform = async ( + context: RequestHandlerContext, + request: KibanaRequest, + response: KibanaResponseFactory + ): Promise>> => { + try { + const { id } = request.params as { id: string }; + const { seqNo, primaryTerm } = request.query as { seqNo?: string; primaryTerm?: string }; + let method = "ism.putTransform"; + let params: PutTransformParams = { + transformId: id, + if_seq_no: seqNo, + if_primary_term: primaryTerm, + body: JSON.stringify(request.body), + }; + if (seqNo === undefined || primaryTerm === undefined) { + method = "ism.putTransform"; + params = { transformId: id, body: JSON.stringify(request.body) }; + } + const { callAsCurrentUser: callWithRequest } = this.esDriver.asScoped(request); + const putTransformResponse: PutTransformResponse = await callWithRequest(method, params); + return response.custom({ + statusCode: 200, + body: { + ok: true, + response: putTransformResponse, + }, + }); + } catch (err) { + console.error("Index Management - TransformService - putTransform", err); + return response.custom({ + statusCode: 200, + body: { + ok: false, + error: err.message, + }, + }); + } + }; +} diff --git a/server/services/index.ts b/server/services/index.ts index a503f77c4..a7ca6d701 100644 --- a/server/services/index.ts +++ b/server/services/index.ts @@ -17,5 +17,6 @@ import IndexService from "./IndexService"; import PolicyService from "./PolicyService"; import ManagedIndexService from "./ManagedIndexService"; import RollupService from "./RollupService"; +import TransformService from "./TransformService"; -export { IndexService, PolicyService, ManagedIndexService, RollupService }; +export { IndexService, PolicyService, ManagedIndexService, RollupService, TransformService }; diff --git a/server/utils/constants.ts b/server/utils/constants.ts index d950b4719..b8f156402 100644 --- a/server/utils/constants.ts +++ b/server/utils/constants.ts @@ -17,6 +17,7 @@ import { DefaultHeaders, IndexManagementApi } from "../models/interfaces"; export const API_ROUTE_PREFIX = "/_opendistro/_ism"; export const API_ROUTE_PREFIX_ROLLUP = "/_opendistro/_rollup"; +export const TRANSFORM_ROUTE_PREFIX = "/_opendistro/_transform"; export const API: IndexManagementApi = { POLICY_BASE: `${API_ROUTE_PREFIX}/policies`, @@ -26,6 +27,7 @@ export const API: IndexManagementApi = { REMOVE_POLICY_BASE: `${API_ROUTE_PREFIX}/remove`, CHANGE_POLICY_BASE: `${API_ROUTE_PREFIX}/change_policy`, ROLLUP_JOBS_BASE: `${API_ROUTE_PREFIX_ROLLUP}/jobs`, + TRANSFORM_BASE: `${TRANSFORM_ROUTE_PREFIX}`, }; export const DEFAULT_HEADERS: DefaultHeaders = { diff --git a/utils/constants.ts b/utils/constants.ts index f76d056bc..7db04af57 100644 --- a/utils/constants.ts +++ b/utils/constants.ts @@ -22,6 +22,7 @@ export const NODE_API = Object.freeze({ EDIT_ROLLOVER_ALIAS: `${BASE_API_PATH}/editRolloverAlias`, POLICIES: `${BASE_API_PATH}/policies`, ROLLUPS: `${BASE_API_PATH}/rollups`, + TRANSFORMS: `${BASE_API_PATH}/transforms`, MANAGED_INDICES: `${BASE_API_PATH}/managedIndices`, RETRY: `${BASE_API_PATH}/retry`, CHANGE_POLICY: `${BASE_API_PATH}/changePolicy`, From c6fdeabeb394626155fd0b4f249e29d921a55f57 Mon Sep 17 00:00:00 2001 From: Ravi Thaluru Date: Mon, 3 May 2021 09:47:05 -0700 Subject: [PATCH 35/93] Enabling github workflows for develpoment branches --- .github/workflows/cypress-workflow.yml | 1 + .github/workflows/unit-tests-workflow.yml | 1 + 2 files changed, 2 insertions(+) diff --git a/.github/workflows/cypress-workflow.yml b/.github/workflows/cypress-workflow.yml index 0428cddeb..0d3c1a4dd 100644 --- a/.github/workflows/cypress-workflow.yml +++ b/.github/workflows/cypress-workflow.yml @@ -3,6 +3,7 @@ on: push: branches: - main + - development-* jobs: tests: diff --git a/.github/workflows/unit-tests-workflow.yml b/.github/workflows/unit-tests-workflow.yml index 39b99e244..65916cb7e 100644 --- a/.github/workflows/unit-tests-workflow.yml +++ b/.github/workflows/unit-tests-workflow.yml @@ -3,6 +3,7 @@ on: push: branches: - main + - development-* jobs: tests: From 21f965ec9f2ea71a04eebbe1c6bb646ab8c76941 Mon Sep 17 00:00:00 2001 From: Annie Date: Mon, 3 May 2021 14:46:49 -0700 Subject: [PATCH 36/93] Flip date histogram and terms object --- models/interfaces.ts | 10 ++ .../DefineTransforms/DefineTransforms.tsx | 100 ++++++++++++------ .../CreateTransformForm.tsx | 83 ++++----------- .../CreateTransformStep2.tsx | 9 +- 4 files changed, 105 insertions(+), 97 deletions(-) diff --git a/models/interfaces.ts b/models/interfaces.ts index e4c810889..3f9f66be2 100644 --- a/models/interfaces.ts +++ b/models/interfaces.ts @@ -258,3 +258,13 @@ export enum GROUP_TYPES { dateHistogram = "date_histogram", terms = "terms", } + +export interface TransformAggItem { + sum?: { field: string }; + max?: { field: string }; + min?: { field: string }; + avg?: { field: string }; + count?: { field: string }; + percentiles?: { field: string; percents: number[] }; + scripted_metric?: object; +} diff --git a/public/pages/CreateTransform/components/DefineTransforms/DefineTransforms.tsx b/public/pages/CreateTransform/components/DefineTransforms/DefineTransforms.tsx index dc2d54912..9e57b3038 100644 --- a/public/pages/CreateTransform/components/DefineTransforms/DefineTransforms.tsx +++ b/public/pages/CreateTransform/components/DefineTransforms/DefineTransforms.tsx @@ -17,7 +17,7 @@ import { EuiDataGrid, EuiDataGridColumn, EuiSpacer, EuiText } from "@elastic/eui import { CoreStart } from "kibana/public"; import React, { useCallback, useState } from "react"; import { ContentPanel } from "../../../../components/ContentPanel"; -import { FieldItem, GROUP_TYPES, GroupItem } from "../../../../../models/interfaces"; +import { FieldItem, GROUP_TYPES, TransformAggItem, TransformGroupItem } from "../../../../../models/interfaces"; import { TransformService } from "../../../../services"; import { getErrorMessage } from "../../../../utils/helpers"; import { isNumericMapping } from "../../utils/helpers"; @@ -28,8 +28,9 @@ interface DefineTransformsProps { transformId: string; sourceIndex: string; fields: FieldItem[]; - onGroupSelectionChange: (selectedFields: GroupItem[]) => void; - onAggregationSelectionChange: void; + onGroupSelectionChange: (selectedFields: TransformGroupItem[]) => void; + selectedAggregations: Map; + onAggregationSelectionChange: (selectedFields: Map) => void; } export default function DefineTransforms({ @@ -39,6 +40,7 @@ export default function DefineTransforms({ sourceIndex, fields, onGroupSelectionChange, + selectedAggregations, onAggregationSelectionChange, }: DefineTransformsProps) { let columns: EuiDataGridColumn[] = []; @@ -76,9 +78,10 @@ export default function DefineTransforms({ label: "Group by date histogram ", onClick: () => { groupSelection.push({ - terms: { + date_histogram: { source_field: field.label, target_field: `${field.label}_${GROUP_TYPES.dateHistogram}`, + calendar_interval: "1d", }, }); onGroupSelectionChange(groupSelection); @@ -90,10 +93,9 @@ export default function DefineTransforms({ label: "Group by terms ", onClick: () => { groupSelection.push({ - date_histogram: { + terms: { source_field: field.label, target_field: `${field.label}_${GROUP_TYPES.terms}`, - calendar_interval: "1d", }, }); onGroupSelectionChange(groupSelection); @@ -103,43 +105,85 @@ export default function DefineTransforms({ }, { label: "Aggregate by sum ", - onClick: () => {}, + onClick: () => { + aggSelection.set(`sum_${field.label}`, { + sum: { field: field.label }, + }); + onAggregationSelectionChange(aggSelection); + }, size: "xs", color: "text", }, { label: "Aggregate by max ", - onClick: () => {}, + onClick: () => { + aggSelection.set(`max_${field.label}`, { + max: { field: field.label }, + }); + onAggregationSelectionChange(aggSelection); + }, size: "xs", color: "text", }, { label: "Aggregate by min ", - onClick: () => {}, + onClick: () => { + aggSelection.set(`min_${field.label}`, { + min: { field: field.label }, + }); + onAggregationSelectionChange(aggSelection); + }, size: "xs", color: "text", }, { label: "Aggregate by avg ", - onClick: () => {}, + onClick: () => { + console.log("Before set: " + JSON.stringify(aggSelection)); + aggSelection.set("AVG", { + avg: { field: field.label }, + }); + console.log("After set: " + JSON.stringify(aggSelection.get("AVG"))); + onAggregationSelectionChange(aggSelection); + }, size: "xs", color: "text", }, { label: "Aggregate by count ", - onClick: () => {}, + onClick: () => { + aggSelection.set(`count_${field.label}`, { + count: { field: field.label }, + }); + onAggregationSelectionChange(aggSelection); + }, size: "xs", color: "text", }, { - label: "Aggregate by percentile ", - onClick: () => {}, + label: "Aggregate by percentile", + onClick: () => { + aggSelection.set(`percentiles_${field.label}`, { + percentiles: { field: field.label, percents: [1, 5, 25, 99] }, + }); + onAggregationSelectionChange(aggSelection); + }, size: "xs", color: "text", }, { label: "Aggregate by scripted metrics ", - onClick: () => {}, + onClick: () => { + aggSelection.set(`scripted_metric_${field.label}`, { + scripted_metric: { + init_script: "", + map_script: "", + combine_script: "", + reduce_script: "", + }, + }); + onAggregationSelectionChange(aggSelection); + }, size: "xs", color: "text", }, @@ -157,7 +201,11 @@ export default function DefineTransforms({ const [visibleColumns, setVisibleColumns] = useState(() => columns.map(({ id }) => id)); const [data, setData] = useState([]); const [dataCount, setDataCount] = useState(0); - const [groupSelection, setGroupSelection] = useState([]); + const [groupSelection, setGroupSelection] = useState([]); + let exMap = new Map(); + exMap.set("EXXX", { sum: { field: "order_date" } }); + const [aggSelection, setAggSelection] = useState(exMap); + console.log(JSON.stringify(exMap.get("EXXX"))); const fetchData = useCallback(async () => { console.log("Entering fetchData..."); @@ -166,11 +214,8 @@ export default function DefineTransforms({ const response = await transformService.searchSampleData(sourceIndex, { from, size }); if (response.ok) { - //Debug use - console.log("Successfully searched sample data: " + JSON.stringify(response)); setData(response.response.data); setDataCount(response.response.total.value); - // console.log("First item: " + JSON.stringify(response.response.data[0])); } } catch (err) { notifications.toasts.addDanger(getErrorMessage(err, "There was a problem loading the rollups")); @@ -213,7 +258,7 @@ export default function DefineTransforms({ const renderCellValue = ({ rowIndex, columnId }) => { //Debug use - console.log("rowIndex: " + rowIndex + " columnId: " + columnId + " data: " + JSON.stringify(data[rowIndex]._source[columnId])); + // console.log("rowIndex: " + rowIndex + " columnId: " + columnId + " data: " + JSON.stringify(data[rowIndex]._source[columnId])); if (!loading && data.hasOwnProperty(rowIndex)) return data[rowIndex]._source[columnId] ? data[rowIndex]._source[columnId] : null; return null; }; @@ -257,21 +302,7 @@ export default function DefineTransforms({ columns={columns} columnVisibility={{ visibleColumns, setVisibleColumns }} rowCount={dataCount} - renderCellValue={ - // ({ rowIndex, columnId }) => { - // //Debug use - // console.log("rowIndex: " + rowIndex + " columnId: " + columnId + " data: " + JSON.stringify(data[rowIndex]._source[columnId])); - // // return data[rowIndex]; - // if (!loading && data.hasOwnProperty(rowIndex)) - // return data[rowIndex]._source[columnId] ? data[rowIndex]._source[columnId] : null; - // return null; - // } - - // ({ rowIndex, columnId }) => - // `${rowIndex}, ${columnId}` - renderCellValue - } - // renderCellValue={({}) => null} + renderCellValue={renderCellValue} sorting={{ columns: sortingColumns, onSort }} pagination={{ ...pagination, @@ -283,6 +314,7 @@ export default function DefineTransforms({ {/*Debug use*/} {JSON.stringify(groupSelection)} + {JSON.stringify(aggSelection)} ); } diff --git a/public/pages/CreateTransform/containers/CreateTransformForm/CreateTransformForm.tsx b/public/pages/CreateTransform/containers/CreateTransformForm/CreateTransformForm.tsx index 3a75541fd..b1cb04a44 100644 --- a/public/pages/CreateTransform/containers/CreateTransformForm/CreateTransformForm.tsx +++ b/public/pages/CreateTransform/containers/CreateTransformForm/CreateTransformForm.tsx @@ -23,7 +23,16 @@ import IndexService from "../../../../services/IndexService"; import { ManagedCatIndex } from "../../../../../server/models/interfaces"; import CreateTransform from "../CreateTransform"; import CreateTransformStep2 from "../CreateTransformStep2"; -import { DimensionItem, FieldItem, GroupItem, IndexItem, MetricItem, Transform } from "../../../../../models/interfaces"; +import { + DimensionItem, + FieldItem, + GroupItem, + IndexItem, + MetricItem, + Transform, + TransformAggItem, + TransformGroupItem, +} from "../../../../../models/interfaces"; import { getErrorMessage } from "../../../../utils/helpers"; import { EMPTY_TRANSFORM } from "../../utils/constants"; import CreateTransformStep3 from "../CreateTransformStep3"; @@ -61,8 +70,8 @@ interface CreateTransformFormState { fields: FieldItem[]; selectedTerms: FieldItem[]; - selectedGroupField: GroupItem[]; - selectedAggregations: MetricItem[]; // Needs to be Map + selectedGroupField: TransformGroupItem[]; + selectedAggregations: Map; // Needs to be Map aggregationsError: string; selectedFields: FieldItem[]; jobEnabledByDefault: boolean; @@ -99,7 +108,7 @@ export default class CreateTransformForm extends Component(), aggregationsError: "", description: "", @@ -169,31 +178,7 @@ export default class CreateTransformForm extends Component { - if ( - !( - aggregation.min || - aggregation.max || - aggregation.sum || - aggregation.avg || - aggregation.value_count || - aggregation.percentiles - ) - ) { - const errorMsg = "Must specify at least one aggregation for: " + aggregation.source_field.label; - this.setState({ submitError: errorMsg, aggregationsError: errorMsg }); - invalidAggregation = true; - error = true; - } - }); - //If nothing invalid found, clear error. - if (!invalidAggregation) this.setState({ aggregationsError: "" }); - } + //TODO: Add checking to see if grouping is defined } else if (currentStep == 3) { //Check if interval is a valid value and is specified. const { intervalError } = this.state; @@ -248,7 +233,7 @@ export default class CreateTransformForm extends Component(), }); await this.getMappings(srcIndexText); }; @@ -270,7 +255,9 @@ export default class CreateTransformForm extends Component { + onAggregationSelectionChange = (selectedFields: Map): void => { + //Debug use + console.log(JSON.stringify(selectedFields)); this.setState({ selectedAggregations: selectedFields }); }; @@ -320,34 +307,8 @@ export default class CreateTransformForm extends Component { - // if (group.aggregationMethod == "terms") { - // newJSON.transform.groups.push({ - // terms: { - // source_field: group.sourceField.label, - // target_field: group.targetField, // needs target_field source, null target_field test - // }, - // }); - // } else if (group.aggregationMethod == "histogram") { - // newJSON.transform.groups.push({ - // histogram: { - // source_field: group.sourceField.label, - // //TODO: Remove the if else condition after implementing define interval - // interval: group.interval ? group.interval : 5, - // }, - // }); - // } else { - // newJSON.transform.groups.push({ - // date_histogram: { - // source_field: group.sourceField.label, - // // need to fill out other date histogram data - // }, - // }); - // } - // }); + if (selectedGroupField.length) newJSON.transform.groups = selectedGroupField; + this.setState({ transformJSON: newJSON }); }; @@ -356,7 +317,9 @@ export default class CreateTransformForm extends Component { diff --git a/public/pages/CreateTransform/containers/CreateTransformStep2/CreateTransformStep2.tsx b/public/pages/CreateTransform/containers/CreateTransformStep2/CreateTransformStep2.tsx index bcee8245e..9ef429eb0 100644 --- a/public/pages/CreateTransform/containers/CreateTransformStep2/CreateTransformStep2.tsx +++ b/public/pages/CreateTransform/containers/CreateTransformStep2/CreateTransformStep2.tsx @@ -21,7 +21,7 @@ import { BREADCRUMBS, ROUTES } from "../../../../utils/constants"; import CreateTransformSteps from "../../components/CreateTransformSteps"; import { CoreServicesContext } from "../../../../components/core_services"; import DefineTransforms from "../../components/DefineTransforms"; -import { FieldItem, GroupItem } from "../../../../../models/interfaces"; +import { FieldItem, TransformGroupItem, TransformAggItem } from "../../../../../models/interfaces"; interface CreateTransformStep2Props extends RouteComponentProps { transformService: TransformService; @@ -29,8 +29,9 @@ interface CreateTransformStep2Props extends RouteComponentProps { currentStep: number; sourceIndex: string; fields: FieldItem[]; - onGroupSelectionChange: (selectedFields: GroupItem[]) => void; - onAggregationSelectionChange: void; + onGroupSelectionChange: (selectedFields: TransformGroupItem[]) => void; + selectedAggregations: Map; + onAggregationSelectionChange: (selectedFields: Map) => void; } export default class CreateTransformStep2 extends Component { @@ -55,6 +56,7 @@ export default class CreateTransformStep2 extends Component From ec9cf23d2deab3d98f23182a6fa31fbda3d3e037 Mon Sep 17 00:00:00 2001 From: Annie Date: Mon, 3 May 2021 14:57:13 -0700 Subject: [PATCH 37/93] Able to add aggregations --- .../DefineTransforms/DefineTransforms.tsx | 39 +++++++++---------- .../CreateTransformForm.tsx | 26 ++----------- .../CreateTransformStep2.tsx | 4 +- 3 files changed, 24 insertions(+), 45 deletions(-) diff --git a/public/pages/CreateTransform/components/DefineTransforms/DefineTransforms.tsx b/public/pages/CreateTransform/components/DefineTransforms/DefineTransforms.tsx index 9e57b3038..1f30be5bb 100644 --- a/public/pages/CreateTransform/components/DefineTransforms/DefineTransforms.tsx +++ b/public/pages/CreateTransform/components/DefineTransforms/DefineTransforms.tsx @@ -29,8 +29,8 @@ interface DefineTransformsProps { sourceIndex: string; fields: FieldItem[]; onGroupSelectionChange: (selectedFields: TransformGroupItem[]) => void; - selectedAggregations: Map; - onAggregationSelectionChange: (selectedFields: Map) => void; + selectedAggregations: any; + onAggregationSelectionChange: (selectedFields: any) => void; } export default function DefineTransforms({ @@ -106,9 +106,9 @@ export default function DefineTransforms({ { label: "Aggregate by sum ", onClick: () => { - aggSelection.set(`sum_${field.label}`, { + aggSelection[`sum_${field.label}`] = { sum: { field: field.label }, - }); + }; onAggregationSelectionChange(aggSelection); }, size: "xs", @@ -117,9 +117,9 @@ export default function DefineTransforms({ { label: "Aggregate by max ", onClick: () => { - aggSelection.set(`max_${field.label}`, { + aggSelection[`max_${field.label}`] = { max: { field: field.label }, - }); + }; onAggregationSelectionChange(aggSelection); }, size: "xs", @@ -128,9 +128,9 @@ export default function DefineTransforms({ { label: "Aggregate by min ", onClick: () => { - aggSelection.set(`min_${field.label}`, { + aggSelection[`min_${field.label}`] = { min: { field: field.label }, - }); + }; onAggregationSelectionChange(aggSelection); }, size: "xs", @@ -140,10 +140,10 @@ export default function DefineTransforms({ label: "Aggregate by avg ", onClick: () => { console.log("Before set: " + JSON.stringify(aggSelection)); - aggSelection.set("AVG", { + aggSelection[`avg_${field.label}`] = { avg: { field: field.label }, - }); - console.log("After set: " + JSON.stringify(aggSelection.get("AVG"))); + }; + console.log("After set: " + JSON.stringify(aggSelection)); onAggregationSelectionChange(aggSelection); }, size: "xs", @@ -152,9 +152,9 @@ export default function DefineTransforms({ { label: "Aggregate by count ", onClick: () => { - aggSelection.set(`count_${field.label}`, { + aggSelection[`count_${field.label}`] = { count: { field: field.label }, - }); + }; onAggregationSelectionChange(aggSelection); }, size: "xs", @@ -163,9 +163,9 @@ export default function DefineTransforms({ { label: "Aggregate by percentile", onClick: () => { - aggSelection.set(`percentiles_${field.label}`, { + aggSelection[`percentiles_${field.label}`] = { percentiles: { field: field.label, percents: [1, 5, 25, 99] }, - }); + }; onAggregationSelectionChange(aggSelection); }, size: "xs", @@ -174,14 +174,14 @@ export default function DefineTransforms({ { label: "Aggregate by scripted metrics ", onClick: () => { - aggSelection.set(`scripted_metric_${field.label}`, { + aggSelection[`scripted_metric_${field.label}`] = { scripted_metric: { init_script: "", map_script: "", combine_script: "", reduce_script: "", }, - }); + }; onAggregationSelectionChange(aggSelection); }, size: "xs", @@ -202,10 +202,7 @@ export default function DefineTransforms({ const [data, setData] = useState([]); const [dataCount, setDataCount] = useState(0); const [groupSelection, setGroupSelection] = useState([]); - let exMap = new Map(); - exMap.set("EXXX", { sum: { field: "order_date" } }); - const [aggSelection, setAggSelection] = useState(exMap); - console.log(JSON.stringify(exMap.get("EXXX"))); + const [aggSelection, setAggSelection] = useState(selectedAggregations); const fetchData = useCallback(async () => { console.log("Entering fetchData..."); diff --git a/public/pages/CreateTransform/containers/CreateTransformForm/CreateTransformForm.tsx b/public/pages/CreateTransform/containers/CreateTransformForm/CreateTransformForm.tsx index b1cb04a44..da62a8f01 100644 --- a/public/pages/CreateTransform/containers/CreateTransformForm/CreateTransformForm.tsx +++ b/public/pages/CreateTransform/containers/CreateTransformForm/CreateTransformForm.tsx @@ -71,7 +71,7 @@ interface CreateTransformFormState { selectedTerms: FieldItem[]; selectedGroupField: TransformGroupItem[]; - selectedAggregations: Map; // Needs to be Map + selectedAggregations: any; // Needs to be Map aggregationsError: string; selectedFields: FieldItem[]; jobEnabledByDefault: boolean; @@ -233,7 +233,7 @@ export default class CreateTransformForm extends Component(), + selectedAggregations: {}, }); await this.getMappings(srcIndexText); }; @@ -255,7 +255,7 @@ export default class CreateTransformForm extends Component): void => { + onAggregationSelectionChange = (selectedFields: any): void => { //Debug use console.log(JSON.stringify(selectedFields)); this.setState({ selectedAggregations: selectedFields }); @@ -306,7 +306,6 @@ export default class CreateTransformForm extends Component { - // const aggregations = []; - // if (aggregation.min) aggregations.push({ min: {} }); - // if (aggregation.max) aggregations.push({ max: {} }); - // if (aggregation.sum) aggregations.push({ sum: {} }); - // if (aggregation.avg) aggregations.push({ avg: {} }); - // if (aggregation.value_count) aggregations.push({ value_count: {} }); - // if (aggregation.percentiles) aggregations.push({ percentiles: {} }); - // newJSON.transform.aggregations.push({ - // source_field: aggregation.source_field.label, - // aggregations: aggregations, - // }); - // }); + this.setState({ transformJSON: newJSON }); }; diff --git a/public/pages/CreateTransform/containers/CreateTransformStep2/CreateTransformStep2.tsx b/public/pages/CreateTransform/containers/CreateTransformStep2/CreateTransformStep2.tsx index 9ef429eb0..389623083 100644 --- a/public/pages/CreateTransform/containers/CreateTransformStep2/CreateTransformStep2.tsx +++ b/public/pages/CreateTransform/containers/CreateTransformStep2/CreateTransformStep2.tsx @@ -30,8 +30,8 @@ interface CreateTransformStep2Props extends RouteComponentProps { sourceIndex: string; fields: FieldItem[]; onGroupSelectionChange: (selectedFields: TransformGroupItem[]) => void; - selectedAggregations: Map; - onAggregationSelectionChange: (selectedFields: Map) => void; + selectedAggregations: any; + onAggregationSelectionChange: (selectedFields: any) => void; } export default class CreateTransformStep2 extends Component { From 8efbcb049787284e0cc0f1c859851d8ea05e90d0 Mon Sep 17 00:00:00 2001 From: Annie Date: Mon, 3 May 2021 16:02:27 -0700 Subject: [PATCH 38/93] Merge changes related to define transform --- models/interfaces.ts | 22 +- .../ConfigureTransform/ConfigureTransform.tsx | 69 +++ .../components/ConfigureTransform/index.ts | 18 + .../CreateTransformSteps.tsx | 52 ++ .../components/CreateTransformSteps/index.ts | 18 + .../DefineTransforms/DefineTransforms.tsx | 317 ++++++++++++ .../components/DefineTransforms/index.ts | 18 + .../JobNameAndIndices/JobNameAndIndices.tsx | 93 ++++ .../components/JobNameAndIndices/index.ts | 18 + .../components/Schedule/Schedule.tsx | 150 ++++++ .../components/Schedule/index.ts | 18 + .../TransformIndices/TransformIndices.tsx | 175 +++++++ .../components/TransformIndices/index.ts | 18 + .../CreateTransform/CreateTransform.tsx | 73 +++ .../containers/CreateTransform/index.ts | 18 + .../CreateTransformForm.tsx | 485 ++++++++++++++++++ .../containers/CreateTransformForm/index.ts | 18 + .../CreateTransformStep2.tsx | 91 ++++ .../containers/CreateTransformStep2/index.ts | 18 + .../CreateTransformStep3.tsx | 161 ++++++ .../containers/CreateTransformStep3/index.ts | 18 + .../CreateTransformStep4.tsx | 84 +++ .../containers/CreateTransformStep4/index.ts | 18 + public/pages/CreateTransform/index.ts | 18 + .../pages/CreateTransform/utils/constants.ts | 77 +++ public/pages/CreateTransform/utils/helpers.ts | 63 +++ public/pages/Main/Main.tsx | 8 +- .../TransformStatus/TransformStatus.tsx | 2 +- public/services/TransformService.ts | 18 + server/clusters/ism/ismPlugin.ts | 14 + server/models/interfaces.ts | 20 +- server/routes/transforms.ts | 12 + server/services/TransformService.ts | 60 ++- utils/constants.ts | 1 + 34 files changed, 2250 insertions(+), 13 deletions(-) create mode 100644 public/pages/CreateTransform/components/ConfigureTransform/ConfigureTransform.tsx create mode 100644 public/pages/CreateTransform/components/ConfigureTransform/index.ts create mode 100644 public/pages/CreateTransform/components/CreateTransformSteps/CreateTransformSteps.tsx create mode 100644 public/pages/CreateTransform/components/CreateTransformSteps/index.ts create mode 100644 public/pages/CreateTransform/components/DefineTransforms/DefineTransforms.tsx create mode 100644 public/pages/CreateTransform/components/DefineTransforms/index.ts create mode 100644 public/pages/CreateTransform/components/JobNameAndIndices/JobNameAndIndices.tsx create mode 100644 public/pages/CreateTransform/components/JobNameAndIndices/index.ts create mode 100644 public/pages/CreateTransform/components/Schedule/Schedule.tsx create mode 100644 public/pages/CreateTransform/components/Schedule/index.ts create mode 100644 public/pages/CreateTransform/components/TransformIndices/TransformIndices.tsx create mode 100644 public/pages/CreateTransform/components/TransformIndices/index.ts create mode 100644 public/pages/CreateTransform/containers/CreateTransform/CreateTransform.tsx create mode 100644 public/pages/CreateTransform/containers/CreateTransform/index.ts create mode 100644 public/pages/CreateTransform/containers/CreateTransformForm/CreateTransformForm.tsx create mode 100644 public/pages/CreateTransform/containers/CreateTransformForm/index.ts create mode 100644 public/pages/CreateTransform/containers/CreateTransformStep2/CreateTransformStep2.tsx create mode 100644 public/pages/CreateTransform/containers/CreateTransformStep2/index.ts create mode 100644 public/pages/CreateTransform/containers/CreateTransformStep3/CreateTransformStep3.tsx create mode 100644 public/pages/CreateTransform/containers/CreateTransformStep3/index.ts create mode 100644 public/pages/CreateTransform/containers/CreateTransformStep4/CreateTransformStep4.tsx create mode 100644 public/pages/CreateTransform/containers/CreateTransformStep4/index.ts create mode 100644 public/pages/CreateTransform/index.ts create mode 100644 public/pages/CreateTransform/utils/constants.ts create mode 100644 public/pages/CreateTransform/utils/helpers.ts diff --git a/models/interfaces.ts b/models/interfaces.ts index aff6065b9..3f9f66be2 100644 --- a/models/interfaces.ts +++ b/models/interfaces.ts @@ -167,8 +167,8 @@ export interface TransformMetadata { documents_indexed: number | null; index_time_in_millis: number | null; search_time_in_millis: number | null; - } - } + }; + }; } export interface IntervalSchedule { @@ -250,3 +250,21 @@ export interface RollupMetricItem { } ]; } + +export type TransformGroupItem = DateHistogramItem | TermsItem | HistogramItem; + +export enum GROUP_TYPES { + histogram = "histogram", + dateHistogram = "date_histogram", + terms = "terms", +} + +export interface TransformAggItem { + sum?: { field: string }; + max?: { field: string }; + min?: { field: string }; + avg?: { field: string }; + count?: { field: string }; + percentiles?: { field: string; percents: number[] }; + scripted_metric?: object; +} diff --git a/public/pages/CreateTransform/components/ConfigureTransform/ConfigureTransform.tsx b/public/pages/CreateTransform/components/ConfigureTransform/ConfigureTransform.tsx new file mode 100644 index 000000000..db5eca2e3 --- /dev/null +++ b/public/pages/CreateTransform/components/ConfigureTransform/ConfigureTransform.tsx @@ -0,0 +1,69 @@ +/* + * Copyright 2021 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"). + * You may not use this file except in compliance with the License. + * A copy of the License is located at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * or in the "license" file accompanying this file. This file is distributed + * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either + * express or implied. See the License for the specific language governing + * permissions and limitations under the License. + */ + +import React, { ChangeEvent } from "react"; +import { EuiSpacer, EuiFormRow, EuiFieldText, EuiTextArea, EuiText, EuiFlexGroup, EuiFlexItem } from "@elastic/eui"; +import { ContentPanel } from "../../../../components/ContentPanel"; + +interface ConfigureTransformProps { + isEdit: boolean; + transformId: string; + transformIdError: string; + onChangeName: (value: ChangeEvent) => void; + onChangeDescription: (value: ChangeEvent) => void; + description: string; +} + +const ConfigureTransform = ({ + isEdit, + transformId, + transformIdError, + onChangeName, + onChangeDescription, + description, +}: ConfigureTransformProps) => ( + +
+ + + + + + + + +

Description

+
+
+ + + - optional + + +
+ + + + +
+
+); +export default ConfigureTransform; diff --git a/public/pages/CreateTransform/components/ConfigureTransform/index.ts b/public/pages/CreateTransform/components/ConfigureTransform/index.ts new file mode 100644 index 000000000..b935263fd --- /dev/null +++ b/public/pages/CreateTransform/components/ConfigureTransform/index.ts @@ -0,0 +1,18 @@ +/* + * Copyright 2021 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"). + * You may not use this file except in compliance with the License. + * A copy of the License is located at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * or in the "license" file accompanying this file. This file is distributed + * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either + * express or implied. See the License for the specific language governing + * permissions and limitations under the License. + */ + +import ConfigureTransform from "./ConfigureTransform"; + +export default ConfigureTransform; diff --git a/public/pages/CreateTransform/components/CreateTransformSteps/CreateTransformSteps.tsx b/public/pages/CreateTransform/components/CreateTransformSteps/CreateTransformSteps.tsx new file mode 100644 index 000000000..3b404ea58 --- /dev/null +++ b/public/pages/CreateTransform/components/CreateTransformSteps/CreateTransformSteps.tsx @@ -0,0 +1,52 @@ +/* + * Copyright 2021 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"). + * You may not use this file except in compliance with the License. + * A copy of the License is located at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * or in the "license" file accompanying this file. This file is distributed + * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either + * express or implied. See the License for the specific language governing + * permissions and limitations under the License. + */ +import React from "react"; +import { EuiSteps } from "@elastic/eui"; + +interface CreateTransformStepsProps { + step: number; +} + +const setOfSteps = (step: number) => { + return [ + { + title: "Set up indices", + children: null, + }, + { + title: "Define transforms", + children: null, + status: step < 2 ? "disabled" : null, + }, + { + title: "Specify schedule", + children: null, + status: step < 3 ? "disabled" : null, + }, + { + title: "Review and create", + children: null, + status: step < 4 ? "disabled" : null, + }, + ]; +}; + +const CreateTransformSteps = ({ step }: CreateTransformStepsProps) => ( +
+ +
+); + +export default CreateTransformSteps; diff --git a/public/pages/CreateTransform/components/CreateTransformSteps/index.ts b/public/pages/CreateTransform/components/CreateTransformSteps/index.ts new file mode 100644 index 000000000..fe481f23a --- /dev/null +++ b/public/pages/CreateTransform/components/CreateTransformSteps/index.ts @@ -0,0 +1,18 @@ +/* + * Copyright 2021 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"). + * You may not use this file except in compliance with the License. + * A copy of the License is located at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * or in the "license" file accompanying this file. This file is distributed + * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either + * express or implied. See the License for the specific language governing + * permissions and limitations under the License. + */ + +import CreateTransformSteps from "./CreateTransformSteps"; + +export default CreateTransformSteps; diff --git a/public/pages/CreateTransform/components/DefineTransforms/DefineTransforms.tsx b/public/pages/CreateTransform/components/DefineTransforms/DefineTransforms.tsx new file mode 100644 index 000000000..1f30be5bb --- /dev/null +++ b/public/pages/CreateTransform/components/DefineTransforms/DefineTransforms.tsx @@ -0,0 +1,317 @@ +/* + * Copyright 2021 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"). + * You may not use this file except in compliance with the License. + * A copy of the License is located at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * or in the "license" file accompanying this file. This file is distributed + * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either + * express or implied. See the License for the specific language governing + * permissions and limitations under the License. + */ + +import { EuiDataGrid, EuiDataGridColumn, EuiSpacer, EuiText } from "@elastic/eui"; +import { CoreStart } from "kibana/public"; +import React, { useCallback, useState } from "react"; +import { ContentPanel } from "../../../../components/ContentPanel"; +import { FieldItem, GROUP_TYPES, TransformAggItem, TransformGroupItem } from "../../../../../models/interfaces"; +import { TransformService } from "../../../../services"; +import { getErrorMessage } from "../../../../utils/helpers"; +import { isNumericMapping } from "../../utils/helpers"; + +interface DefineTransformsProps { + transformService: TransformService; + notifications: CoreStart["notifications"]; + transformId: string; + sourceIndex: string; + fields: FieldItem[]; + onGroupSelectionChange: (selectedFields: TransformGroupItem[]) => void; + selectedAggregations: any; + onAggregationSelectionChange: (selectedFields: any) => void; +} + +export default function DefineTransforms({ + transformService, + notifications, + transfromId, + sourceIndex, + fields, + onGroupSelectionChange, + selectedAggregations, + onAggregationSelectionChange, +}: DefineTransformsProps) { + let columns: EuiDataGridColumn[] = []; + + fields.map((field: FieldItem) => { + const isNumeric = isNumericMapping(field.type); + const isDate = field.type == "date"; + // TODO: Handle the available options according to column types + columns.push({ + id: field.label, + displayAsText: field.label + " type: " + field.type, + actions: { + showHide: false, + showMoveLeft: false, + showMoveRight: false, + showSortAsc: false, + showSortDesc: false, + additional: [ + { + label: "Group by histogram ", + onClick: () => { + groupSelection.push({ + histogram: { + source_field: field.label, + target_field: `${field.label}_${GROUP_TYPES.histogram}`, + interval: 5, + }, + }); + onGroupSelectionChange(groupSelection); + }, + size: "xs", + color: isNumeric ? "text" : "subdued", + }, + { + label: "Group by date histogram ", + onClick: () => { + groupSelection.push({ + date_histogram: { + source_field: field.label, + target_field: `${field.label}_${GROUP_TYPES.dateHistogram}`, + calendar_interval: "1d", + }, + }); + onGroupSelectionChange(groupSelection); + }, + size: "xs", + color: isDate ? "text" : "subdued", + }, + { + label: "Group by terms ", + onClick: () => { + groupSelection.push({ + terms: { + source_field: field.label, + target_field: `${field.label}_${GROUP_TYPES.terms}`, + }, + }); + onGroupSelectionChange(groupSelection); + }, + size: "xs", + color: "text", + }, + { + label: "Aggregate by sum ", + onClick: () => { + aggSelection[`sum_${field.label}`] = { + sum: { field: field.label }, + }; + onAggregationSelectionChange(aggSelection); + }, + size: "xs", + color: "text", + }, + { + label: "Aggregate by max ", + onClick: () => { + aggSelection[`max_${field.label}`] = { + max: { field: field.label }, + }; + onAggregationSelectionChange(aggSelection); + }, + size: "xs", + color: "text", + }, + { + label: "Aggregate by min ", + onClick: () => { + aggSelection[`min_${field.label}`] = { + min: { field: field.label }, + }; + onAggregationSelectionChange(aggSelection); + }, + size: "xs", + color: "text", + }, + { + label: "Aggregate by avg ", + onClick: () => { + console.log("Before set: " + JSON.stringify(aggSelection)); + aggSelection[`avg_${field.label}`] = { + avg: { field: field.label }, + }; + console.log("After set: " + JSON.stringify(aggSelection)); + onAggregationSelectionChange(aggSelection); + }, + size: "xs", + color: "text", + }, + { + label: "Aggregate by count ", + onClick: () => { + aggSelection[`count_${field.label}`] = { + count: { field: field.label }, + }; + onAggregationSelectionChange(aggSelection); + }, + size: "xs", + color: "text", + }, + { + label: "Aggregate by percentile", + onClick: () => { + aggSelection[`percentiles_${field.label}`] = { + percentiles: { field: field.label, percents: [1, 5, 25, 99] }, + }; + onAggregationSelectionChange(aggSelection); + }, + size: "xs", + color: "text", + }, + { + label: "Aggregate by scripted metrics ", + onClick: () => { + aggSelection[`scripted_metric_${field.label}`] = { + scripted_metric: { + init_script: "", + map_script: "", + combine_script: "", + reduce_script: "", + }, + }; + onAggregationSelectionChange(aggSelection); + }, + size: "xs", + color: "text", + }, + ], + }, + }); + }); + + const [loading, setLoading] = useState(true); + + const [pagination, setPagination] = useState({ pageIndex: 0, pageSize: 10 }); + const [from, setFrom] = useState(0); + const [size, setSize] = useState(10); + const [sortingColumns, setSortingColumns] = useState([]); + const [visibleColumns, setVisibleColumns] = useState(() => columns.map(({ id }) => id)); + const [data, setData] = useState([]); + const [dataCount, setDataCount] = useState(0); + const [groupSelection, setGroupSelection] = useState([]); + const [aggSelection, setAggSelection] = useState(selectedAggregations); + + const fetchData = useCallback(async () => { + console.log("Entering fetchData..."); + setLoading(true); + try { + const response = await transformService.searchSampleData(sourceIndex, { from, size }); + + if (response.ok) { + setData(response.response.data); + setDataCount(response.response.total.value); + } + } catch (err) { + notifications.toasts.addDanger(getErrorMessage(err, "There was a problem loading the rollups")); + } + setLoading(false); + }, [sourceIndex]); + + React.useEffect(() => { + fetchData(); + }, [fetchData]); + + const onChangeItemsPerPage = useCallback( + (pageSize) => { + setPagination((pagination) => ({ + ...pagination, + pageSize, + pageIndex: 0, + })); + setFrom(0); + setSize(pageSize); + }, + [setPagination] + ); + const onChangePage = useCallback( + (pageIndex) => { + setPagination((pagination) => ({ ...pagination, pageIndex })); + setFrom(pageIndex * size); + //debug use + console.log("From: " + pageIndex * size); + }, + [setPagination] + ); + + const onSort = useCallback( + (sortingColumns) => { + setSortingColumns(sortingColumns); + }, + [setSortingColumns] + ); + + const renderCellValue = ({ rowIndex, columnId }) => { + //Debug use + // console.log("rowIndex: " + rowIndex + " columnId: " + columnId + " data: " + JSON.stringify(data[rowIndex]._source[columnId])); + if (!loading && data.hasOwnProperty(rowIndex)) return data[rowIndex]._source[columnId] ? data[rowIndex]._source[columnId] : null; + return null; + }; + + return ( + + // // onShow(ApplyPolicyModal, { + // // indices: selectedItems.map((item: ManagedCatIndex) => item.index), + // // core: this.context, + // // }), + // }, + // }, + // ]} + // /> + // } + bodyStyles={{ padding: "10px 10px" }} + title="Select fields to transform" + titleSize="m" + > + +

Original fields with sample data

+
+ + {/*TODO: Substitute "source index", and "filtered by" fields with actual values*/} + +

{`Viewing sample data from index ${sourceIndex}, filtered by order.type:sales_order, order.success:true`}

+
+ + {/*TODO: add rowCount*/} + + + {/*Debug use*/} + {JSON.stringify(groupSelection)} + {JSON.stringify(aggSelection)} +
+ ); +} diff --git a/public/pages/CreateTransform/components/DefineTransforms/index.ts b/public/pages/CreateTransform/components/DefineTransforms/index.ts new file mode 100644 index 000000000..ac624d7f2 --- /dev/null +++ b/public/pages/CreateTransform/components/DefineTransforms/index.ts @@ -0,0 +1,18 @@ +/* + * Copyright 2021 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"). + * You may not use this file except in compliance with the License. + * A copy of the License is located at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * or in the "license" file accompanying this file. This file is distributed + * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either + * express or implied. See the License for the specific language governing + * permissions and limitations under the License. + */ + +import DefineTransforms from "./DefineTransforms"; + +export default DefineTransforms; diff --git a/public/pages/CreateTransform/components/JobNameAndIndices/JobNameAndIndices.tsx b/public/pages/CreateTransform/components/JobNameAndIndices/JobNameAndIndices.tsx new file mode 100644 index 000000000..e2e01d3dd --- /dev/null +++ b/public/pages/CreateTransform/components/JobNameAndIndices/JobNameAndIndices.tsx @@ -0,0 +1,93 @@ +/* + * Copyright 2021 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"). + * You may not use this file except in compliance with the License. + * A copy of the License is located at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * or in the "license" file accompanying this file. This file is distributed + * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either + * express or implied. See the License for the specific language governing + * permissions and limitations under the License. + */ + +import React, { Component } from "react"; +import { EuiFlexGrid, EuiSpacer, EuiFlexItem, EuiText } from "@elastic/eui"; +import { ContentPanel, ContentPanelActions } from "../../../../components/ContentPanel"; +import { ModalConsumer } from "../../../../components/Modal"; +import { IndexItem } from "../../../../../models/interfaces"; + +interface JobNameAndIndicesProps { + transformId: string; + description: string; + sourceIndex: { label: string; value?: IndexItem }[]; + targetIndex: { label: string; value?: IndexItem }[]; + onChangeStep: (step: number) => void; +} + +export default class JobNameAndIndices extends Component { + constructor(props: JobNameAndIndicesProps) { + super(props); + } + + render() { + const { transformId, description, onChangeStep, sourceIndex, targetIndex } = this.props; + + return ( + + {() => ( + onChangeStep(1), + }, + }, + ]} + /> + )} + + } + bodyStyles={{ padding: "initial" }} + title="Job name and indices" + titleSize="m" + > +
+ + + + +
Name
+
{transformId}
+
+
+ + +
Source Index
+
{sourceIndex[0].label}
+
+
+ + +
Target index
+
{targetIndex[0].label}
+
+
+ + +
Description
+
{description == "" ? "-" : description}
+
+
+
+ +
+
+ ); + } +} diff --git a/public/pages/CreateTransform/components/JobNameAndIndices/index.ts b/public/pages/CreateTransform/components/JobNameAndIndices/index.ts new file mode 100644 index 000000000..aa4b17446 --- /dev/null +++ b/public/pages/CreateTransform/components/JobNameAndIndices/index.ts @@ -0,0 +1,18 @@ +/* + * Copyright 2021 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"). + * You may not use this file except in compliance with the License. + * A copy of the License is located at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * or in the "license" file accompanying this file. This file is distributed + * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either + * express or implied. See the License for the specific language governing + * permissions and limitations under the License. + */ + +import JobNameAndIndices from "./JobNameAndIndices"; + +export default JobNameAndIndices; diff --git a/public/pages/CreateTransform/components/Schedule/Schedule.tsx b/public/pages/CreateTransform/components/Schedule/Schedule.tsx new file mode 100644 index 000000000..11391718b --- /dev/null +++ b/public/pages/CreateTransform/components/Schedule/Schedule.tsx @@ -0,0 +1,150 @@ +/* + * Copyright 2020 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"). + * You may not use this file except in compliance with the License. + * A copy of the License is located at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * or in the "license" file accompanying this file. This file is distributed + * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either + * express or implied. See the License for the specific language governing + * permissions and limitations under the License. + */ + +import React, { ChangeEvent, Component } from "react"; +import moment from "moment-timezone"; +import { + EuiSpacer, + EuiCheckbox, + EuiRadioGroup, + EuiFormRow, + EuiSelect, + EuiFieldNumber, + EuiFlexGroup, + EuiFlexItem, + EuiTextArea, + EuiFormHelpText, + EuiText, + EuiAccordion, + EuiHorizontalRule, +} from "@elastic/eui"; +import { DelayTimeunitOptions, ScheduleIntervalTimeunitOptions } from "../../utils/constants"; +import { ContentPanel } from "../../../../components/ContentPanel"; + +interface ScheduleProps { + isEdit: boolean; + transformId: string; + transformIdError: string; + jobEnabledByDefault: boolean; + pageSize: number; + onChangeJobEnabledByDefault: () => void; + interval: number; + intervalTimeunit: string; + intervalError: string; + pageSize: number; + onChangeJobEnabledByDefault: () => void; + onChangeIntervalTime: (e: ChangeEvent) => void; + onChangeIntervalTimeunit: (e: ChangeEvent) => void; + onChangePage: (e: ChangeEvent) => void; +} + +const radios = [ + { + id: "no", + label: "No", + }, + { + id: "yes", + label: "Yes", + }, +]; + +const selectInterval = ( + interval: number, + intervalTimeunit: string, + intervalError: string, + onChangeInterval: (e: ChangeEvent) => void, + onChangeTimeunit: (value: ChangeEvent) => void +) => ( + + + + + + + + + + + + + + +); + +const timezones = moment.tz.names().map((tz) => ({ label: tz, text: tz })); + +export default class Schedule extends Component { + constructor(props: ScheduleProps) { + super(props); + } + + render() { + const { + isEdit, + jobEnabledByDefault, + interval, + intervalTimeunit, + intervalError, + pageSize, + onChangeJobEnabledByDefault, + onChangeIntervalTime, + onChangeIntervalTimeunit, + onChangePage, + } = this.props; + return ( + +
+ {!isEdit && ( + + )} + + + {!isEdit} + + + + + + + {selectInterval(interval, intervalTimeunit, intervalError, onChangeIntervalTime, onChangeIntervalTimeunit)} + + + + + + + + +
+
+ ); + } +} diff --git a/public/pages/CreateTransform/components/Schedule/index.ts b/public/pages/CreateTransform/components/Schedule/index.ts new file mode 100644 index 000000000..f18d83cb2 --- /dev/null +++ b/public/pages/CreateTransform/components/Schedule/index.ts @@ -0,0 +1,18 @@ +/* + * Copyright 2021 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"). + * You may not use this file except in compliance with the License. + * A copy of the License is located at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * or in the "license" file accompanying this file. This file is distributed + * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either + * express or implied. See the License for the specific language governing + * permissions and limitations under the License. + */ + +import Schedule from "./Schedule"; + +export default Schedule; diff --git a/public/pages/CreateTransform/components/TransformIndices/TransformIndices.tsx b/public/pages/CreateTransform/components/TransformIndices/TransformIndices.tsx new file mode 100644 index 000000000..2b0abb1f0 --- /dev/null +++ b/public/pages/CreateTransform/components/TransformIndices/TransformIndices.tsx @@ -0,0 +1,175 @@ +/* + * Copyright 2021 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"). + * You may not use this file except in compliance with the License. + * A copy of the License is located at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * or in the "license" file accompanying this file. This file is distributed + * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either + * express or implied. See the License for the specific language governing + * permissions and limitations under the License. + */ + +import React, { Component, Fragment } from "react"; +import { EuiSpacer, EuiFormRow, EuiComboBox, EuiCallOut } from "@elastic/eui"; +import { ContentPanel } from "../../../../components/ContentPanel"; +import { EuiComboBoxOptionOption } from "@elastic/eui/src/components/combo_box/types"; +import { IndexItem } from "../../../../../models/interfaces"; +import IndexService from "../../../../services/IndexService"; +import _ from "lodash"; +import { CoreServicesContext } from "../../../../components/core_services"; + +interface TransformIndicesProps { + indexService: IndexService; + sourceIndex: { label: string; value?: IndexItem }[]; + sourceIndexError: string; + targetIndex: { label: string; value?: IndexItem }[]; + targetIndexError: string; + onChangeSourceIndex: (options: EuiComboBoxOptionOption[]) => void; + onChangeTargetIndex: (options: EuiComboBoxOptionOption[]) => void; + hasAggregation: boolean; +} + +interface TransformIndicesState { + isLoading: boolean; + indexOptions: { label: string; value?: IndexItem }[]; + targetIndexOptions: { label: string; value?: IndexItem }[]; +} + +export default class TransformIndices extends Component { + static contextType = CoreServicesContext; + constructor(props: TransformIndicesProps) { + super(props); + this.state = { + isLoading: true, + indexOptions: [], + targetIndexOptions: [], + }; + + this.onIndexSearchChange = _.debounce(this.onIndexSearchChange, 500, { leading: true }); + } + + async componentDidMount(): Promise { + await this.onIndexSearchChange(""); + } + + onIndexSearchChange = async (searchValue: string): Promise => { + const { indexService } = this.props; + this.setState({ isLoading: true, indexOptions: [] }); + try { + const queryObject = { from: 0, size: 10, search: searchValue, sortDirection: "desc", sortField: "index" }; + const getIndicesResponse = await indexService.getIndices(queryObject); + if (getIndicesResponse.ok) { + const options = searchValue.trim() ? [{ label: `${searchValue}*` }] : []; + const indices = getIndicesResponse.response.indices.map((index: IndexItem) => ({ + label: index.index, + })); + this.setState({ indexOptions: options.concat(indices), targetIndexOptions: indices }); + } else { + if (getIndicesResponse.error.startsWith("[index_not_found_exception]")) { + this.context.notifications.toasts.addDanger("No index available"); + } else { + this.context.notifications.toasts.addDanger(getIndicesResponse.error); + } + } + } catch (err) { + this.context.notifications.toasts.addDanger(err.message); + } + + this.setState({ isLoading: false }); + }; + + onCreateOption = (searchValue: string, flattenedOptions: { label: string; value?: IndexItem }[]): void => { + const { targetIndexOptions } = this.state; + const { onChangeTargetIndex } = this.props; + const normalizedSearchValue = searchValue.trim(); + + if (!normalizedSearchValue) { + return; + } + + const newOption = { + label: searchValue, + }; + + // Create the option if it doesn't exist. + if (flattenedOptions.findIndex((option) => option.label.trim() === normalizedSearchValue) === -1) { + targetIndexOptions.concat(newOption); + this.setState({ targetIndexOptions: targetIndexOptions }); + } + onChangeTargetIndex([newOption]); + }; + + render() { + const { + sourceIndex, + sourceIndexError, + targetIndex, + targetIndexError, + onChangeSourceIndex, + onChangeTargetIndex, + hasAggregation, + } = this.props; + const { isLoading, indexOptions, targetIndexOptions } = this.state; + return ( + +
+ + +

You can't change indices after creating a job. Double-check the source and target index names before proceeding.

+
+ {hasAggregation && ( + + + +

Note: changing source index will erase all existing definitions about aggregations and metrics.

+
+
+ )} + + + + + + + + +
+
+ ); + } +} diff --git a/public/pages/CreateTransform/components/TransformIndices/index.ts b/public/pages/CreateTransform/components/TransformIndices/index.ts new file mode 100644 index 000000000..ffee9e56f --- /dev/null +++ b/public/pages/CreateTransform/components/TransformIndices/index.ts @@ -0,0 +1,18 @@ +/* + * Copyright 2021 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"). + * You may not use this file except in compliance with the License. + * A copy of the License is located at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * or in the "license" file accompanying this file. This file is distributed + * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either + * express or implied. See the License for the specific language governing + * permissions and limitations under the License. + */ + +import TransformIndices from "./TransformIndices"; + +export default TransformIndices; diff --git a/public/pages/CreateTransform/containers/CreateTransform/CreateTransform.tsx b/public/pages/CreateTransform/containers/CreateTransform/CreateTransform.tsx new file mode 100644 index 000000000..05b223724 --- /dev/null +++ b/public/pages/CreateTransform/containers/CreateTransform/CreateTransform.tsx @@ -0,0 +1,73 @@ +/* + * Copyright 2021 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"). + * You may not use this file except in compliance with the License. + * A copy of the License is located at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * or in the "license" file accompanying this file. This file is distributed + * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either + * express or implied. See the License for the specific language governing + * permissions and limitations under the License. + */ + +import React, { ChangeEvent, Component } from "react"; +import { EuiSpacer, EuiTitle, EuiFlexGroup, EuiFlexItem, EuiComboBoxOptionOption } from "@elastic/eui"; +import { RouteComponentProps } from "react-router-dom"; +import { TransformService } from "../../../../services"; +import ConfigureTransform from "../../components/ConfigureTransform"; +import TransformIndices from "../../components/TransformIndices"; +import CreateTransformSteps from "../../components/CreateTransformSteps"; +import IndexService from "../../../../services/IndexService"; +import { IndexItem } from "../../../../../models/interfaces"; + +interface CreateTransformProps extends RouteComponentProps { + transformService: TransformService; + indexService: IndexService; + transformId: string; + transformIdError: string; + submitError: string; + isSubmitting: boolean; + hasSubmitted: boolean; + description: string; + sourceIndex: { label: string; value?: IndexItem }[]; + sourceIndexError: string; + targetIndex: { label: string; value?: IndexItem }[]; + targetIndexError: string; + onChangeName: (e: ChangeEvent) => void; + onChangeDescription: (value: ChangeEvent) => void; + onChangeSourceIndex: (options: EuiComboBoxOptionOption[]) => void; + onChangeTargetIndex: (options: EuiComboBoxOptionOption[]) => void; + currentStep: number; + hasAggregation: boolean; +} + +export default class CreateTransform extends Component { + render() { + if (this.props.currentStep !== 1) { + return null; + } + + return ( +
+ + + + + + +

Set up indices

+
+ + + + +
+
+ +
+ ); + } +} diff --git a/public/pages/CreateTransform/containers/CreateTransform/index.ts b/public/pages/CreateTransform/containers/CreateTransform/index.ts new file mode 100644 index 000000000..b01f74a8a --- /dev/null +++ b/public/pages/CreateTransform/containers/CreateTransform/index.ts @@ -0,0 +1,18 @@ +/* + * Copyright 2021 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"). + * You may not use this file except in compliance with the License. + * A copy of the License is located at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * or in the "license" file accompanying this file. This file is distributed + * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either + * express or implied. See the License for the specific language governing + * permissions and limitations under the License. + */ + +import CreateTransform from "./CreateTransform"; + +export default CreateTransform; diff --git a/public/pages/CreateTransform/containers/CreateTransformForm/CreateTransformForm.tsx b/public/pages/CreateTransform/containers/CreateTransformForm/CreateTransformForm.tsx new file mode 100644 index 000000000..da62a8f01 --- /dev/null +++ b/public/pages/CreateTransform/containers/CreateTransformForm/CreateTransformForm.tsx @@ -0,0 +1,485 @@ +/* + * Copyright 2021 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"). + * You may not use this file except in compliance with the License. + * A copy of the License is located at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * or in the "license" file accompanying this file. This file is distributed + * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either + * express or implied. See the License for the specific language governing + * permissions and limitations under the License. + */ + +import React, { ChangeEvent, Component } from "react"; +import { EuiButton, EuiButtonEmpty, EuiComboBoxOptionOption, EuiFlexGroup, EuiFlexItem } from "@elastic/eui"; +import { RouteComponentProps } from "react-router-dom"; +import moment from "moment"; +import { RollupService, TransformService } from "../../../../services"; +import { BREADCRUMBS, ROUTES } from "../../../../utils/constants"; +import IndexService from "../../../../services/IndexService"; +import { ManagedCatIndex } from "../../../../../server/models/interfaces"; +import CreateTransform from "../CreateTransform"; +import CreateTransformStep2 from "../CreateTransformStep2"; +import { + DimensionItem, + FieldItem, + GroupItem, + IndexItem, + MetricItem, + Transform, + TransformAggItem, + TransformGroupItem, +} from "../../../../../models/interfaces"; +import { getErrorMessage } from "../../../../utils/helpers"; +import { EMPTY_TRANSFORM } from "../../utils/constants"; +import CreateTransformStep3 from "../CreateTransformStep3"; +import CreateTransformStep4 from "../CreateTransformStep4"; +import { compareFieldItem, parseFieldOptions } from "../../utils/helpers"; +import { CoreServicesContext } from "../../../../components/core_services"; + +interface CreateTransformFormProps extends RouteComponentProps { + rollupService: RollupService; + transformService: TransformService; + indexService: IndexService; +} + +interface CreateTransformFormState { + currentStep: number; + transformId: string; + transformIdError: string; + transformSeqNo: number | null; + transformPrimaryTerm: number | null; + submitError: string; + isSubmitting: boolean; + hasSubmitted: boolean; + loadingIndices: boolean; + indices: ManagedCatIndex[]; + totalIndices: number; + + description: string; + sourceIndex: { label: string; value?: IndexItem }[]; + sourceIndexError: string; + targetIndex: { label: string; value?: IndexItem }[]; + targetIndexError: string; + + mappings: any; + allMappings: FieldItem[][]; + fields: FieldItem[]; + selectedTerms: FieldItem[]; + + selectedGroupField: TransformGroupItem[]; + selectedAggregations: any; // Needs to be Map + aggregationsError: string; + selectedFields: FieldItem[]; + jobEnabledByDefault: boolean; + + interval: number; + intervalError: string; + intervalTimeunit: string; + pageSize: number; + transformJSON: any; +} + +export default class CreateTransformForm extends Component { + static contextType = CoreServicesContext; + + constructor(props: CreateTransformFormProps) { + super(props); + + this.state = { + currentStep: 1, + transformSeqNo: null, + transformPrimaryTerm: null, + transformId: "", + transformIdError: "", + submitError: "", + isSubmitting: false, + hasSubmitted: false, + loadingIndices: true, + indices: [], + totalIndices: 0, + + mappings: "", + allMappings: [], + fields: [], + selectedFields: [], + selectedTerms: [], + selectedGroupField: [], + selectedAggregations: new Map(), + aggregationsError: "", + description: "", + + sourceIndex: [], + sourceIndexError: "", + targetIndex: [], + targetIndexError: "", + + intervalError: "", + + jobEnabledByDefault: true, + interval: 1, + intervalTimeunit: "MINUTES", + pageSize: 1000, + transformJSON: JSON.parse(EMPTY_TRANSFORM), + }; + this._next = this._next.bind(this); + this._prev = this._prev.bind(this); + } + + componentDidMount = async (): Promise => { + this.context.chrome.setBreadcrumbs([BREADCRUMBS.INDEX_MANAGEMENT, BREADCRUMBS.TRANSFORMS, BREADCRUMBS.CREATE_TRANSFORM]); + }; + + getMappings = async (srcIndex: string): Promise => { + if (!srcIndex.length) return; + try { + const { rollupService } = this.props; + const response = await rollupService.getMappings(srcIndex); + if (response.ok) { + let allMappings: FieldItem[][] = []; + const mappings = response.response; + //Push mappings array to allMappings 2D array first + for (let index in mappings) { + allMappings.push(parseFieldOptions("", mappings[index].mappings.properties)); + } + //Find intersect from all mappings + const fields = allMappings.reduce((mappingA, mappingB) => + mappingA.filter((itemA) => mappingB.some((itemB) => compareFieldItem(itemA, itemB))) + ); + this.setState({ mappings, fields, allMappings }); + } else { + this.context.notifications.toasts.addDanger(`Could not load fields: ${response.error}`); + } + } catch (err) { + this.context.notifications.toasts.addDanger(getErrorMessage(err, "Could not load fields")); + } + }; + + _next() { + let currentStep = this.state.currentStep; + let error = false; + //Verification here + if (currentStep == 1) { + const { transformId, sourceIndex, targetIndex } = this.state; + + if (!transformId) { + this.setState({ submitError: "Job name is required.", transformIdError: "Job name is required." }); + error = true; + } + if (sourceIndex.length == 0) { + this.setState({ submitError: "Source index is required.", sourceIndexError: "Source index is required." }); + error = true; + } + if (targetIndex.length == 0) { + this.setState({ submitError: "Target index is required.", targetIndexError: "Target index is required." }); + error = true; + } + } else if (currentStep == 2) { + //TODO: Add checking to see if grouping is defined + } else if (currentStep == 3) { + //Check if interval is a valid value and is specified. + const { intervalError } = this.state; + } + + if (error) return; + + currentStep = currentStep >= 3 ? 4 : currentStep + 1; + + this.setState({ + submitError: "", + currentStep: currentStep, + }); + } + + _prev() { + let currentStep = this.state.currentStep; + // If the current step is 2 or 3, then subtract one on "previous" button click + currentStep = currentStep <= 1 ? 1 : currentStep - 1; + this.setState({ + currentStep: currentStep, + }); + } + + onChangeStep = (step: number): void => { + if (step > 3) return; + this.setState({ + currentStep: step, + }); + }; + + onChangeDescription = (e: ChangeEvent): void => { + const description = e.target.value; + let newJSON = this.state.transformJSON; + newJSON.transform.description = description; + this.setState({ description: description, transformJSON: newJSON }); + }; + + onChangeName = (e: ChangeEvent): void => { + const transformId = e.target.value; + this.setState({ transformId, transformIdError: transformId ? "" : "Name is required" }); + }; + + onChangeSourceIndex = async (options: EuiComboBoxOptionOption[]): Promise => { + let newJSON = this.state.transformJSON; + let sourceIndex = options.map(function (option) { + return option.label; + }); + const sourceIndexError = sourceIndex.length ? "" : "Source index is required"; + const srcIndexText = sourceIndex.length ? sourceIndex[0] : ""; + newJSON.transform.source_index = srcIndexText; + this.setState({ sourceIndex: options, transformJSON: newJSON, sourceIndexError: sourceIndexError }); + this.setState({ + selectedGroupField: [], + selectedAggregations: {}, + }); + await this.getMappings(srcIndexText); + }; + + onChangeTargetIndex = (options: EuiComboBoxOptionOption[]): void => { + //Try to get label text from option from the only array element in options, if exists + let newJSON = this.state.transformJSON; + let targetIndex = options.map(function (option) { + return option.label; + }); + + const targetIndexError = targetIndex.length ? "" : "Target index is required"; + + newJSON.transform.target_index = targetIndex[0]; + this.setState({ targetIndex: options, transformJSON: newJSON, targetIndexError: targetIndexError }); + }; + + onGroupSelectionChange = (selectedFields: GroupItem[]): void => { + this.setState({ selectedGroupField: selectedFields }); + }; + + onAggregationSelectionChange = (selectedFields: any): void => { + //Debug use + console.log(JSON.stringify(selectedFields)); + this.setState({ selectedAggregations: selectedFields }); + }; + + onChangeJobEnabledByDefault = (): void => { + const checked = this.state.jobEnabledByDefault; + let newJSON = this.state.transformJSON; + newJSON.transform.enabled = !checked; + this.setState({ jobEnabledByDefault: !checked, transformJSON: newJSON }); + }; + + onChangeIntervalTime = (e: ChangeEvent): void => { + this.setState({ interval: e.target.valueAsNumber }); + if (e.target.value == "") { + const intervalErrorMsg = "Interval value is required."; + this.setState({ submitError: intervalErrorMsg, intervalError: intervalErrorMsg }); + } else { + this.setState({ intervalError: "" }); + } + }; + + onChangePage = (e: ChangeEvent): void => { + let newJSON = this.state.transformJSON; + newJSON.transform.page_size = e.target.valueAsNumber; + this.setState({ pageSize: e.target.valueAsNumber, transformJSON: newJSON }); + }; + + updateSchedule = (): void => { + const { interval, intervalTimeunit } = this.state; + let newJSON = this.state.transformJSON; + + newJSON.transform.schedule.interval = { + start_time: moment().unix(), + unit: `${intervalTimeunit}`, + period: `${interval}`, + }; + delete newJSON.transform.schedule["cron"]; + + this.setState({ transformJSON: newJSON }); + }; + + onChangeIntervalTimeunit = (e: ChangeEvent): void => { + this.setState({ intervalTimeunit: e.target.value }); + }; + + updateGroup = (): void => { + const { transformJSON, selectedGroupField } = this.state; + let newJSON = transformJSON; + + if (selectedGroupField.length) newJSON.transform.groups = selectedGroupField; + + this.setState({ transformJSON: newJSON }); + }; + + updateAggregation = (): void => { + const { transformJSON, selectedAggregations } = this.state; + let newJSON = transformJSON; + + newJSON.transform.aggregations = selectedAggregations; + + this.setState({ transformJSON: newJSON }); + }; + + onSubmit = async (): Promise => { + const { transformId, transformJSON } = this.state; + this.setState({ submitError: "", isSubmitting: true, hasSubmitted: true }); + try { + if (!transformId) { + this.setState({ transformIdError: "Required" }); + } else { + this.updateGroup(); + this.updateAggregation(); + this.updateSchedule(); + await this.onCreate(transformId, transformJSON); + } + } catch (err) { + this.context.notifications.toasts.addDanger("Invalid Transform JSON"); + console.error(err); + } + + this.setState({ isSubmitting: false }); + }; + + onCancel = (): void => { + this.props.history.push(ROUTES.TRANSFORMS); + }; + + onCreate = async (transformId: string, transform: Transform): Promise => { + const { transformService } = this.props; + try { + const response = await transformService.putTransform(transform, transformId); + if (response.ok) { + this.context.notifications.toasts.addSuccess(`Created transform: ${response.response._id}`); + this.props.history.push(ROUTES.TRANSFORMS); + } else { + this.setState({ submitError: response.error }); + this.context.notifications.toasts.addDanger(`Failed to create transform: ${response.error}`); + } + } catch (err) { + this.setState({ submitError: getErrorMessage(err, "There was a problem creating the transform job") }); + this.context.notifications.toasts.addDanger( + `Failed to create transform: ${getErrorMessage(err, "There was a problem creating the transform job")}` + ); + } + }; + + render() { + const { + transformId, + transformIdError, + submitError, + isSubmitting, + hasSubmitted, + description, + sourceIndex, + sourceIndexError, + targetIndex, + targetIndexError, + currentStep, + + fields, + selectedTerms, + selectedGroupField, + selectedAggregations, + aggregationsError, + + jobEnabledByDefault, + interval, + intervalTimeunit, + intervalError, + pageSize, + } = this.state; + return ( +
+ + + + + + + + Cancel + + + {currentStep != 1 && ( + + + Previous + + + )} + + {currentStep == 4 ? ( + + + Create + + + ) : ( + + + Next + + + )} + + + ); + } +} diff --git a/public/pages/CreateTransform/containers/CreateTransformForm/index.ts b/public/pages/CreateTransform/containers/CreateTransformForm/index.ts new file mode 100644 index 000000000..d933dd68a --- /dev/null +++ b/public/pages/CreateTransform/containers/CreateTransformForm/index.ts @@ -0,0 +1,18 @@ +/* + * Copyright 2021 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"). + * You may not use this file except in compliance with the License. + * A copy of the License is located at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * or in the "license" file accompanying this file. This file is distributed + * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either + * express or implied. See the License for the specific language governing + * permissions and limitations under the License. + */ + +import CreateTransformForm from "./CreateTransformForm"; + +export default CreateTransformForm; diff --git a/public/pages/CreateTransform/containers/CreateTransformStep2/CreateTransformStep2.tsx b/public/pages/CreateTransform/containers/CreateTransformStep2/CreateTransformStep2.tsx new file mode 100644 index 000000000..389623083 --- /dev/null +++ b/public/pages/CreateTransform/containers/CreateTransformStep2/CreateTransformStep2.tsx @@ -0,0 +1,91 @@ +/* + * Copyright 2021 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"). + * You may not use this file except in compliance with the License. + * A copy of the License is located at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * or in the "license" file accompanying this file. This file is distributed + * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either + * express or implied. See the License for the specific language governing + * permissions and limitations under the License. + */ + +import React, { Component } from "react"; +import { EuiSpacer, EuiTitle, EuiFlexGroup, EuiFlexItem } from "@elastic/eui"; +import { RouteComponentProps } from "react-router-dom"; +import { TransformService } from "../../../../services"; +import { BREADCRUMBS, ROUTES } from "../../../../utils/constants"; +import CreateTransformSteps from "../../components/CreateTransformSteps"; +import { CoreServicesContext } from "../../../../components/core_services"; +import DefineTransforms from "../../components/DefineTransforms"; +import { FieldItem, TransformGroupItem, TransformAggItem } from "../../../../../models/interfaces"; + +interface CreateTransformStep2Props extends RouteComponentProps { + transformService: TransformService; + transformId: string; + currentStep: number; + sourceIndex: string; + fields: FieldItem[]; + onGroupSelectionChange: (selectedFields: TransformGroupItem[]) => void; + selectedAggregations: any; + onAggregationSelectionChange: (selectedFields: any) => void; +} + +export default class CreateTransformStep2 extends Component { + static contextType = CoreServicesContext; + constructor(props: CreateTransformStep2Props) { + super(props); + } + + componentDidMount = async (): Promise => { + this.context.chrome.setBreadcrumbs([BREADCRUMBS.INDEX_MANAGEMENT, BREADCRUMBS.TRANSFORMS]); + }; + + onCancel = (): void => { + this.props.history.push(ROUTES.TRANSFORMS); + }; + + render() { + const { + transformService, + transformId, + currentStep, + sourceIndex, + fields, + onGroupSelectionChange, + selectedAggregations, + onAggregationSelectionChange, + } = this.props; + if (currentStep !== 2) return null; + + return ( +
+ + + + + + +

Define transform

+
+ + +
+
+ +
+ ); + } +} diff --git a/public/pages/CreateTransform/containers/CreateTransformStep2/index.ts b/public/pages/CreateTransform/containers/CreateTransformStep2/index.ts new file mode 100644 index 000000000..1b7f6dbd7 --- /dev/null +++ b/public/pages/CreateTransform/containers/CreateTransformStep2/index.ts @@ -0,0 +1,18 @@ +/* + * Copyright 2021 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"). + * You may not use this file except in compliance with the License. + * A copy of the License is located at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * or in the "license" file accompanying this file. This file is distributed + * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either + * express or implied. See the License for the specific language governing + * permissions and limitations under the License. + */ + +import CreateTransformStep2 from "./CreateTransformStep2"; + +export default CreateTransformStep2; diff --git a/public/pages/CreateTransform/containers/CreateTransformStep3/CreateTransformStep3.tsx b/public/pages/CreateTransform/containers/CreateTransformStep3/CreateTransformStep3.tsx new file mode 100644 index 000000000..6640cae95 --- /dev/null +++ b/public/pages/CreateTransform/containers/CreateTransformStep3/CreateTransformStep3.tsx @@ -0,0 +1,161 @@ +/* + * Copyright 2021 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"). + * You may not use this file except in compliance with the License. + * A copy of the License is located at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * or in the "license" file accompanying this file. This file is distributed + * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either + * express or implied. See the License for the specific language governing + * permissions and limitations under the License. + */ + +import React, { ChangeEvent, Component } from "react"; +import { EuiSpacer, EuiTitle, EuiFlexGroup, EuiFlexItem } from "@elastic/eui"; +import { RouteComponentProps } from "react-router-dom"; +import { TransformService } from "../../../../services"; +import { BREADCRUMBS, ROUTES } from "../../../../utils/constants"; +import { getErrorMessage } from "../../../../utils/helpers"; +import { Transform } from "../../../../../models/interfaces"; +import CreateTransformSteps from "../../components/CreateTransformSteps"; +import Schedule from "../../components/Schedule"; +import { CoreServicesContext } from "../../../../components/core_services"; + +interface CreateTransformProps extends RouteComponentProps { + transformService: TransformService; + currentStep: number; + jobEnabledByDefault: boolean; + interval: number; + intervalTimeunit: string; + intervalError: string; + pageSize: number; + onChangeJobEnabledByDefault: () => void; + onChangeIntervalTime: (e: ChangeEvent) => void; + onChangePage: (e: ChangeEvent) => void; + onChangeIntervalTimeunit: (e: ChangeEvent) => void; +} + +interface CreateTransformState { + transformId: string; + transformIdError: string; + transformSeqNo: number | null; + transformPrimaryTerm: number | null; + submitError: string; + isSubmitting: boolean; + hasSubmitted: boolean; +} + +export default class CreateTransformStep3 extends Component { + static contextType = CoreServicesContext; + constructor(props: CreateTransformProps) { + super(props); + + this.state = { + transformSeqNo: null, + transformPrimaryTerm: null, + transformId: "", + transformIdError: "", + submitError: "", + isSubmitting: false, + hasSubmitted: false, + }; + } + + componentDidMount = async (): Promise => { + this.context.chrome.setBreadcrumbs([BREADCRUMBS.INDEX_MANAGEMENT, BREADCRUMBS.TRANSFORMS]); + }; + + onCreate = async (transformId: string, transform: Transform): Promise => { + const { transformService } = this.props; + try { + const response = await transformService.putTransform(transform, transformId); + if (response.ok) { + this.context.notifications.toasts.addSuccess(`Created transform: ${response.response._id}`); + this.props.history.push(ROUTES.TRANSFORMS); + } else { + this.setState({ submitError: response.error }); + } + } catch (err) { + this.setState({ submitError: getErrorMessage(err, "There was a problem creating the transform") }); + } + }; + + onUpdate = async (transformId: string, transform: Transform): Promise => { + try { + const { transformService } = this.props; + const { transformPrimaryTerm, transformSeqNo } = this.state; + if (transformSeqNo == null || transformPrimaryTerm == null) { + this.context.notifications.toasts.addDanger("Could not update transform without seqNo and primaryTerm"); + return; + } + const response = await transformService.putTransform(transform, transformId, transformSeqNo, transformPrimaryTerm); + if (response.ok) { + this.context.notifications.toasts.addSuccess(`Updated transform: ${response.response._id}`); + this.props.history.push(ROUTES.TRANSFORMS); + } else { + this.setState({ submitError: response.error }); + } + } catch (err) { + this.setState({ submitError: getErrorMessage(err, "There was a problem updating the transform") }); + } + }; + + onCancel = (): void => { + this.props.history.push(ROUTES.TRANSFORMS); + }; + + onChange = (e: ChangeEvent): void => { + const { hasSubmitted } = this.state; + const transformId = e.target.value; + if (hasSubmitted) this.setState({ transformId, transformIdError: transformId ? "" : "Required" }); + else this.setState({ transformId }); + }; + + render() { + if (this.props.currentStep != 3) return null; + const { + jobEnabledByDefault, + interval, + intervalTimeunit, + pageSize, + onChangeJobEnabledByDefault, + onChangeIntervalTime, + onChangePage, + onChangeIntervalTimeunit, + } = this.props; + const { transformId, transformIdError } = this.state; + return ( +
+ + + + + + +

Specify Schedule

+
+ + +
+
+ +
+ ); + } +} diff --git a/public/pages/CreateTransform/containers/CreateTransformStep3/index.ts b/public/pages/CreateTransform/containers/CreateTransformStep3/index.ts new file mode 100644 index 000000000..c2626fe72 --- /dev/null +++ b/public/pages/CreateTransform/containers/CreateTransformStep3/index.ts @@ -0,0 +1,18 @@ +/* + * Copyright 2020 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"). + * You may not use this file except in compliance with the License. + * A copy of the License is located at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * or in the "license" file accompanying this file. This file is distributed + * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either + * express or implied. See the License for the specific language governing + * permissions and limitations under the License. + */ + +import CreateTransformStep3 from "./CreateTransformStep3"; + +export default CreateTransformStep3; diff --git a/public/pages/CreateTransform/containers/CreateTransformStep4/CreateTransformStep4.tsx b/public/pages/CreateTransform/containers/CreateTransformStep4/CreateTransformStep4.tsx new file mode 100644 index 000000000..328533609 --- /dev/null +++ b/public/pages/CreateTransform/containers/CreateTransformStep4/CreateTransformStep4.tsx @@ -0,0 +1,84 @@ +/* + * Copyright 2021 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"). + * You may not use this file except in compliance with the License. + * A copy of the License is located at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * or in the "license" file accompanying this file. This file is distributed + * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either + * express or implied. See the License for the specific language governing + * permissions and limitations under the License. + */ + +import React, { Component } from "react"; +import { EuiSpacer, EuiTitle, EuiFlexGroup, EuiFlexItem, EuiComboBoxOptionOption, EuiCallOut } from "@elastic/eui"; +import { RouteComponentProps } from "react-router-dom"; +import { TransformService } from "../../../../services"; +import { BREADCRUMBS, ROUTES } from "../../../../utils/constants"; +import { DimensionItem, IndexItem, MetricItem } from "../../../../../models/interfaces"; +import CreateTransformSteps from "../../components/CreateTransformSteps"; +import JobNameAndIndices from "../../components/JobNameAndIndices"; +import { CoreServicesContext } from "../../../../components/core_services"; + +interface CreateTransformProps extends RouteComponentProps { + transformService: TransformService; + submitError: string; + currentStep: number; + onChangeStep: (step: number) => void; + transformId: string; + description: string; + sourceIndex: { label: string; value?: IndexItem }[]; + targetIndex: { label: string; value?: IndexItem }[]; + + timestamp: EuiComboBoxOptionOption[]; + timezone: string; + timeunit: string; + + jobEnabledByDefault: boolean; + pageSize: number; +} + +export default class CreateTransformStep4 extends Component { + static contextType = CoreServicesContext; + constructor(props: CreateTransformProps) { + super(props); + } + + componentDidMount = async (): Promise => { + this.context.chrome.setBreadcrumbs([BREADCRUMBS.INDEX_MANAGEMENT, BREADCRUMBS.TRANSFORMS]); + }; + + onCancel = (): void => { + this.props.history.push(ROUTES.TRANSFORMS); + }; + + render() { + if (this.props.currentStep != 4) return null; + + return ( +
+ + + + + + +

Review and create

+
+ + + + + +

You can't change aggregations or metrics after creating a job. Double-check your choices before proceeding.

+
+
+
+ +
+ ); + } +} diff --git a/public/pages/CreateTransform/containers/CreateTransformStep4/index.ts b/public/pages/CreateTransform/containers/CreateTransformStep4/index.ts new file mode 100644 index 000000000..379985a8a --- /dev/null +++ b/public/pages/CreateTransform/containers/CreateTransformStep4/index.ts @@ -0,0 +1,18 @@ +/* + * Copyright 2021 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"). + * You may not use this file except in compliance with the License. + * A copy of the License is located at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * or in the "license" file accompanying this file. This file is distributed + * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either + * express or implied. See the License for the specific language governing + * permissions and limitations under the License. + */ + +import CreateTransformStep4 from "./CreateTransformStep4"; + +export default CreateTransformStep4; diff --git a/public/pages/CreateTransform/index.ts b/public/pages/CreateTransform/index.ts new file mode 100644 index 000000000..2ca67cce6 --- /dev/null +++ b/public/pages/CreateTransform/index.ts @@ -0,0 +1,18 @@ +/* + * Copyright 2021 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"). + * You may not use this file except in compliance with the License. + * A copy of the License is located at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * or in the "license" file accompanying this file. This file is distributed + * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either + * express or implied. See the License for the specific language governing + * permissions and limitations under the License. + */ + +import CreateTransform from "./containers/CreateTransform"; + +export default CreateTransform; diff --git a/public/pages/CreateTransform/utils/constants.ts b/public/pages/CreateTransform/utils/constants.ts new file mode 100644 index 000000000..1ab5afeb0 --- /dev/null +++ b/public/pages/CreateTransform/utils/constants.ts @@ -0,0 +1,77 @@ +/* + * Copyright 2021 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"). + * You may not use this file except in compliance with the License. + * A copy of the License is located at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * or in the "license" file accompanying this file. This file is distributed + * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either + * express or implied. See the License for the specific language governing + * permissions and limitations under the License. + */ + +export const EMPTY_TRANSFORM = JSON.stringify({ + transform: { + description: "", + enabled: true, + page_size: 1000, + roles: [], + schedule: { + interval: { + start_time: 234802, + period: 1, + unit: "MINUTES", + }, + }, + source_index: "", + target_index: "", + }, +}); + +export const FixedTimeunitOptions = [ + { value: "ms", text: "Millisecond(s)" }, + { value: "s", text: "Second(s)" }, + { value: "m", text: "Minute(s)" }, + { value: "h", text: "Hour(s)" }, + { value: "d", text: "Day(s)" }, +]; + +export const DelayTimeunitOptions = [ + { value: "SECONDS", text: "Second(s)" }, + { value: "MINUTES", text: "Minute(s)" }, + { value: "HOURS", text: "Hour(s)" }, + { value: "DAYS", text: "Day(s)" }, +]; + +export const CalendarTimeunitOptions = [ + { value: "m", text: "Minute" }, + { value: "h", text: "Hour" }, + { value: "d", text: "Day" }, + { value: "w", text: "Week" }, + { value: "M", text: "Month" }, + { value: "q", text: "Quarter" }, + { value: "y", text: "Year" }, +]; + +export const ScheduleIntervalTimeunitOptions = [ + { value: "MINUTES", text: "Minute(s)" }, + { value: "HOURS", text: "Hour(s)" }, + { value: "DAYS", text: "Day(s)" }, +]; + +export const AddFieldsColumns = [ + { + field: "label", + name: "Field name", + sortable: true, + }, + { + field: "type", + name: "Field type", + sortable: true, + render: (type: string | undefined) => (type == null || type == undefined ? "-" : type), + }, +]; diff --git a/public/pages/CreateTransform/utils/helpers.ts b/public/pages/CreateTransform/utils/helpers.ts new file mode 100644 index 000000000..8e66b67fc --- /dev/null +++ b/public/pages/CreateTransform/utils/helpers.ts @@ -0,0 +1,63 @@ +/* + * Copyright 2021 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"). + * You may not use this file except in compliance with the License. + * A copy of the License is located at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * or in the "license" file accompanying this file. This file is distributed + * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either + * express or implied. See the License for the specific language governing + * permissions and limitations under the License. + */ + +import { FieldItem } from "../../../../models/interfaces"; + +export const parseTimeunit = (timeunit: string): string => { + if (timeunit == "ms" || timeunit == "Milliseconds") return "millisecond(s)"; + else if (timeunit == "SECONDS" || timeunit == "s" || timeunit == "Seconds") return "second(s)"; + else if (timeunit == "MINUTES" || timeunit == "m" || timeunit == "Minutes") return "minute(s)"; + else if (timeunit == "HOURS" || timeunit == "h" || timeunit == "Hours") return "hour(s)"; + else if (timeunit == "DAYS" || timeunit == "d" || timeunit == "Days") return "day(s)"; + else if (timeunit == "w") return "week"; + else if (timeunit == "M") return "month"; + else if (timeunit == "q") return "quarter"; + else if (timeunit == "y") return "year"; + + return timeunit; +}; + +//Returns true if field type is numeric +export const isNumericMapping = (fieldType: string | undefined): boolean => { + return ( + fieldType == "long" || + fieldType == "integer" || + fieldType == "short" || + fieldType == "byte" || + fieldType == "double" || + fieldType == "float" || + fieldType == "half_float" || + fieldType == "scaled_float" + ); +}; + +export const compareFieldItem = (itemA: FieldItem, itemB: FieldItem): boolean => { + return itemB.label == itemA.label && itemA.type == itemB.type; +}; + +export const parseFieldOptions = (prefix: string, mappings: any): FieldItem[] => { + let fieldsOption: FieldItem[] = []; + for (let field in mappings) { + if (mappings.hasOwnProperty(field)) { + if (mappings[field].type != "object" && mappings[field].type != null && mappings[field].type != "nested") + fieldsOption.push({ label: prefix + field, type: mappings[field].type }); + if (mappings[field].fields != null) + fieldsOption = fieldsOption.concat(parseFieldOptions(prefix + field + ".", mappings[field].fields)); + if (mappings[field].properties != null) + fieldsOption = fieldsOption.concat(parseFieldOptions(prefix + field + ".", mappings[field].properties)); + } + } + return fieldsOption; +}; diff --git a/public/pages/Main/Main.tsx b/public/pages/Main/Main.tsx index c48320e83..1022492b3 100644 --- a/public/pages/Main/Main.tsx +++ b/public/pages/Main/Main.tsx @@ -30,6 +30,7 @@ import { BrowserServices } from "../../models/interfaces"; import { ROUTES } from "../../utils/constants"; import { CoreServicesConsumer } from "../../components/core_services"; import CreateRollupForm from "../CreateRollup/containers/CreateRollupForm"; +import CreateTransformForm from "../CreateTransform/containers/CreateTransformForm"; import EditRollup from "../EditRollup/containers"; import RollupDetails from "../RollupDetails/containers/RollupDetails"; import { EditTransform, Transforms } from "../Transforms"; @@ -210,7 +211,12 @@ export default class Main extends Component { path={ROUTES.CREATE_TRANSFORM} render={(props: RouteComponentProps) => (
- +
)} /> diff --git a/public/pages/Transforms/components/TransformStatus/TransformStatus.tsx b/public/pages/Transforms/components/TransformStatus/TransformStatus.tsx index a091160de..578e10df6 100644 --- a/public/pages/Transforms/components/TransformStatus/TransformStatus.tsx +++ b/public/pages/Transforms/components/TransformStatus/TransformStatus.tsx @@ -31,7 +31,7 @@ export default class TransformStatus extends Component { render() { const { metadata } = this.props; return ( - +
diff --git a/public/services/TransformService.ts b/public/services/TransformService.ts index 718fea4ed..18bd83792 100644 --- a/public/services/TransformService.ts +++ b/public/services/TransformService.ts @@ -68,4 +68,22 @@ export default class TransformService { const url = `..${NODE_API.TRANSFORMS}/_preview`; return (await this.httpClient.post(url, { body: JSON.stringify(transform) })) as ServerResponse>; }; + + //Function to search for fields from a source index using GET /${source_index}/_mapping + getMappings = async (index: string): Promise> => { + const url = `..${NODE_API._MAPPINGS}`; + const body = { index: index }; + const response = (await this.httpClient.post(url, { body: JSON.stringify(body) })) as ServerResponse; + return response; + }; + + searchSampleData = async (index: string, queryObject: object): Promise> => { + //Debug use + console.log("Entering browser side service..."); + const url = `..${NODE_API._SEARCH_SAMPLE_DATA}/${index}`; + const response = (await this.httpClient.get(url, { query: queryObject })) as ServerResponse; + //Debug use + console.log("response: " + JSON.stringify(response)); + return response; + }; } diff --git a/server/clusters/ism/ismPlugin.ts b/server/clusters/ism/ismPlugin.ts index 04ddfa9e5..265f7eebb 100644 --- a/server/clusters/ism/ismPlugin.ts +++ b/server/clusters/ism/ismPlugin.ts @@ -343,6 +343,20 @@ export default function ismPlugin(Client: any, config: any, components: any) { method: "DELETE", }); + ism.createTransform = ca({ + url: { + fmt: `${API.TRANSFORM_BASE}/<%=transformId%>?refresh=wait_for`, + req: { + transformId: { + type: "string", + required: true, + }, + }, + }, + needBody: true, + method: "PUT", + }); + ism.putTransform = ca({ url: { fmt: `${API.TRANSFORM_BASE}/<%=transformId%>`, diff --git a/server/models/interfaces.ts b/server/models/interfaces.ts index 3269f07be..15182efd4 100644 --- a/server/models/interfaces.ts +++ b/server/models/interfaces.ts @@ -14,14 +14,7 @@ */ import { IndexService, ManagedIndexService, PolicyService, RollupService, TransformService } from "../services"; -import { - DocumentPolicy, - DocumentRollup, - DocumentTransform, - ManagedIndexItem, - Rollup, - Transform -} from "../../models/interfaces"; +import { DocumentPolicy, DocumentRollup, DocumentTransform, ManagedIndexItem, Rollup, Transform } from "../../models/interfaces"; export interface NodeServices { indexService: IndexService; @@ -227,6 +220,17 @@ export interface ExplainAPIManagedIndexMetaData { enabled: boolean; } +export interface SearchSampleDataResponse { + total: number; + data: { + _index: string; + _type: string; + _id: string; + _score: number; + _source: object; + }[]; +} + export interface IndexManagementApi { [API_ROUTE: string]: string; diff --git a/server/routes/transforms.ts b/server/routes/transforms.ts index 851a586dc..41c50dd60 100644 --- a/server/routes/transforms.ts +++ b/server/routes/transforms.ts @@ -101,4 +101,16 @@ export default function (services: NodeServices, router: IRouter) { }, transformService.putTransform ); + + router.get( + { + path: `${NODE_API._SEARCH_SAMPLE_DATA}/{index}`, + validate: { + params: schema.object({ + index: schema.string(), + }), + }, + }, + transformService.searchSampleData + ); } diff --git a/server/services/TransformService.ts b/server/services/TransformService.ts index a3826e8f0..1f69c4a52 100644 --- a/server/services/TransformService.ts +++ b/server/services/TransformService.ts @@ -15,7 +15,7 @@ import { IClusterClient, IKibanaResponse, KibanaRequest, KibanaResponseFactory, RequestHandlerContext } from "kibana/server"; import { ServerResponse } from "../models/types"; -import { GetTransformsResponse, PutTransformResponse, PutTransformParams } from "../models/interfaces"; +import { GetTransformsResponse, PutTransformParams, PutTransformResponse, SearchResponse } from "../models/interfaces"; import { DocumentTransform, Transform } from "../../models/interfaces"; import _ from "lodash"; @@ -303,4 +303,62 @@ export default class TransformService { }); } }; + + searchSampleData = async ( + context: RequestHandlerContext, + request: KibanaRequest, + response: KibanaResponseFactory + ): Promise>> => { + try { + // Debug use + console.log("Entering server side service..."); + const { from, size } = request.query as { + from: string; + size: string; + }; + const { index } = request.params as { index: string }; + const params = { + index: index, + from: from, + size: size, + }; + const { callAsCurrentUser: callWithRequest } = this.esDriver.asScoped(request); + const searchResponse: SearchResponse = await callWithRequest("search", params); + + //Debug use + console.log(JSON.stringify(searchResponse)); + + return response.custom({ + statusCode: 200, + body: { + ok: true, + response: { + total: searchResponse.hits.total, + data: searchResponse.hits.hits, + }, + }, + }); + } catch (err) { + if (err.statusCode === 404 && err.body.error.type === "index_not_found_exception") { + return response.custom({ + statusCode: 200, + body: { + ok: true, + response: { + total: 0, + data: [], + }, + }, + }); + } + console.error("Index Management - TransformService - searchSampleData", err); + return response.custom({ + statusCode: 200, + body: { + ok: false, + error: err.message, + }, + }); + } + }; } diff --git a/utils/constants.ts b/utils/constants.ts index 7db04af57..bfdbfc2b7 100644 --- a/utils/constants.ts +++ b/utils/constants.ts @@ -16,6 +16,7 @@ export const BASE_API_PATH = "/api/ism"; export const NODE_API = Object.freeze({ _SEARCH: `${BASE_API_PATH}/_search`, + _SEARCH_SAMPLE_DATA: `${BASE_API_PATH}/_searchSampleData`, _INDICES: `${BASE_API_PATH}/_indices`, _MAPPINGS: `${BASE_API_PATH}/_mappings`, APPLY_POLICY: `${BASE_API_PATH}/applyPolicy`, From b8624366248b815d0f919f052676a4796b6a9584 Mon Sep 17 00:00:00 2001 From: Eric Lobdell Date: Mon, 3 May 2021 17:17:02 -0700 Subject: [PATCH 39/93] Merging changes related to Create Transforms, Added incomplete Source index filter popover --- .../IndexFilterPopover/IndexFilterPopover.tsx | 80 +++++++++++++++++++ .../components/IndexFilterPopover/index.ts | 18 +++++ .../components/Schedule/Schedule.tsx | 2 - .../TransformIndices/TransformIndices.tsx | 60 +++++++++++++- server/plugin.ts | 2 +- 5 files changed, 156 insertions(+), 6 deletions(-) create mode 100644 public/pages/CreateTransform/components/IndexFilterPopover/IndexFilterPopover.tsx create mode 100644 public/pages/CreateTransform/components/IndexFilterPopover/index.ts diff --git a/public/pages/CreateTransform/components/IndexFilterPopover/IndexFilterPopover.tsx b/public/pages/CreateTransform/components/IndexFilterPopover/IndexFilterPopover.tsx new file mode 100644 index 000000000..dcd4b6044 --- /dev/null +++ b/public/pages/CreateTransform/components/IndexFilterPopover/IndexFilterPopover.tsx @@ -0,0 +1,80 @@ +/* + * Copyright 2021 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"). + * You may not use this file except in compliance with the License. + * A copy of the License is located at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * or in the "license" file accompanying this file. This file is distributed + * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either + * express or implied. See the License for the specific language governing + * permissions and limitations under the License. + */ + +import React, { Component } from "react"; +import { EuiComboBox, + EuiForm, + EuiFlexGrid, + EuiFlexItem, + } from "@elastic/eui"; +import { EuiComboBoxOptionOption } from "@elastic/eui/src/components/combo_box/types"; +import _ from "lodash"; +import { CoreServicesContext } from "../../../../components/core_services"; + +interface IndexFilterPopoverProps { + +} + +interface IndexFilterPopoverState { + fieldSelectedOptions: []; +} + +export default class IndexFilterPopover extends Component { + static contextType = CoreServicesContext; + + constructor(props: IndexFilterPopoverProps) { + super(props); + this.state = { + fieldSelectedOptions: [], + } + } + + async componentDidMount(): Promise { + + } + + onFieldChange = (options) => {}; + + render() { + + const { + fieldSelectedOptions, + } = this.state; + + return( + + + + Fields + + + + Operator + + + Value + + + + ) + } +} diff --git a/public/pages/CreateTransform/components/IndexFilterPopover/index.ts b/public/pages/CreateTransform/components/IndexFilterPopover/index.ts new file mode 100644 index 000000000..38ae26bfa --- /dev/null +++ b/public/pages/CreateTransform/components/IndexFilterPopover/index.ts @@ -0,0 +1,18 @@ +/* + * Copyright 2021 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"). + * You may not use this file except in compliance with the License. + * A copy of the License is located at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * or in the "license" file accompanying this file. This file is distributed + * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either + * express or implied. See the License for the specific language governing + * permissions and limitations under the License. + */ + +import IndexFilterPopover from "./IndexFilterPopover"; + +export default IndexFilterPopover; diff --git a/public/pages/CreateTransform/components/Schedule/Schedule.tsx b/public/pages/CreateTransform/components/Schedule/Schedule.tsx index 11391718b..dd8f3f20e 100644 --- a/public/pages/CreateTransform/components/Schedule/Schedule.tsx +++ b/public/pages/CreateTransform/components/Schedule/Schedule.tsx @@ -43,8 +43,6 @@ interface ScheduleProps { interval: number; intervalTimeunit: string; intervalError: string; - pageSize: number; - onChangeJobEnabledByDefault: () => void; onChangeIntervalTime: (e: ChangeEvent) => void; onChangeIntervalTimeunit: (e: ChangeEvent) => void; onChangePage: (e: ChangeEvent) => void; diff --git a/public/pages/CreateTransform/components/TransformIndices/TransformIndices.tsx b/public/pages/CreateTransform/components/TransformIndices/TransformIndices.tsx index 2b0abb1f0..f074f1f7d 100644 --- a/public/pages/CreateTransform/components/TransformIndices/TransformIndices.tsx +++ b/public/pages/CreateTransform/components/TransformIndices/TransformIndices.tsx @@ -14,8 +14,16 @@ */ import React, { Component, Fragment } from "react"; -import { EuiSpacer, EuiFormRow, EuiComboBox, EuiCallOut } from "@elastic/eui"; +import { EuiSpacer, + EuiFormRow, + EuiComboBox, + EuiCallOut, + EuiFacetButton, + EuiAvatar, + EuiPopover, + } from "@elastic/eui"; import { ContentPanel } from "../../../../components/ContentPanel"; +import IndexFilterPopover from "../IndexFilterPopover"; import { EuiComboBoxOptionOption } from "@elastic/eui/src/components/combo_box/types"; import { IndexItem } from "../../../../../models/interfaces"; import IndexService from "../../../../services/IndexService"; @@ -37,6 +45,8 @@ interface TransformIndicesState { isLoading: boolean; indexOptions: { label: string; value?: IndexItem }[]; targetIndexOptions: { label: string; value?: IndexItem }[]; + isPopoverOpen: boolean; + selectFieldValue: string; } export default class TransformIndices extends Component { @@ -47,6 +57,8 @@ export default class TransformIndices extends Component { + const { isPopoverOpen } = this.state; + if (isPopoverOpen) { + this.setState({isPopoverOpen: false}); + } else { + this.setState({isPopoverOpen: true}); + } + } + + closePopover = () => this.setState({isPopoverOpen: false}); + render() { + const { sourceIndex, sourceIndexError, @@ -113,7 +137,22 @@ export default class TransformIndices extends Component} // Should be filter icon per design doc + onClick={this.onButtonClick} + > + + Add data filter + + ) + return (
@@ -134,7 +173,8 @@ export default class TransformIndices extends Component + + + + + + Date: Mon, 3 May 2021 17:42:10 -0700 Subject: [PATCH 40/93] Show only 5 columns initially --- .../components/DefineTransforms/DefineTransforms.tsx | 10 +++++----- .../CreateTransformForm/CreateTransformForm.tsx | 4 ---- .../CreateTransformStep2/CreateTransformStep2.tsx | 2 +- 3 files changed, 6 insertions(+), 10 deletions(-) diff --git a/public/pages/CreateTransform/components/DefineTransforms/DefineTransforms.tsx b/public/pages/CreateTransform/components/DefineTransforms/DefineTransforms.tsx index 1f30be5bb..0e30ab654 100644 --- a/public/pages/CreateTransform/components/DefineTransforms/DefineTransforms.tsx +++ b/public/pages/CreateTransform/components/DefineTransforms/DefineTransforms.tsx @@ -17,7 +17,7 @@ import { EuiDataGrid, EuiDataGridColumn, EuiSpacer, EuiText } from "@elastic/eui import { CoreStart } from "kibana/public"; import React, { useCallback, useState } from "react"; import { ContentPanel } from "../../../../components/ContentPanel"; -import { FieldItem, GROUP_TYPES, TransformAggItem, TransformGroupItem } from "../../../../../models/interfaces"; +import { FieldItem, GROUP_TYPES, TransformGroupItem } from "../../../../../models/interfaces"; import { TransformService } from "../../../../services"; import { getErrorMessage } from "../../../../utils/helpers"; import { isNumericMapping } from "../../utils/helpers"; @@ -198,14 +198,13 @@ export default function DefineTransforms({ const [from, setFrom] = useState(0); const [size, setSize] = useState(10); const [sortingColumns, setSortingColumns] = useState([]); - const [visibleColumns, setVisibleColumns] = useState(() => columns.map(({ id }) => id)); + const [visibleColumns, setVisibleColumns] = useState(() => columns.map(({ id }) => id).slice(0, 5)); const [data, setData] = useState([]); const [dataCount, setDataCount] = useState(0); const [groupSelection, setGroupSelection] = useState([]); const [aggSelection, setAggSelection] = useState(selectedAggregations); const fetchData = useCallback(async () => { - console.log("Entering fetchData..."); setLoading(true); try { const response = await transformService.searchSampleData(sourceIndex, { from, size }); @@ -254,8 +253,6 @@ export default function DefineTransforms({ ); const renderCellValue = ({ rowIndex, columnId }) => { - //Debug use - // console.log("rowIndex: " + rowIndex + " columnId: " + columnId + " data: " + JSON.stringify(data[rowIndex]._source[columnId])); if (!loading && data.hasOwnProperty(rowIndex)) return data[rowIndex]._source[columnId] ? data[rowIndex]._source[columnId] : null; return null; }; @@ -309,8 +306,11 @@ export default function DefineTransforms({ }} /> + Group selection {/*Debug use*/} {JSON.stringify(groupSelection)} + + Aggregation {JSON.stringify(aggSelection)} ); diff --git a/public/pages/CreateTransform/containers/CreateTransformForm/CreateTransformForm.tsx b/public/pages/CreateTransform/containers/CreateTransformForm/CreateTransformForm.tsx index da62a8f01..f47e4028d 100644 --- a/public/pages/CreateTransform/containers/CreateTransformForm/CreateTransformForm.tsx +++ b/public/pages/CreateTransform/containers/CreateTransformForm/CreateTransformForm.tsx @@ -256,8 +256,6 @@ export default class CreateTransformForm extends Component { - //Debug use - console.log(JSON.stringify(selectedFields)); this.setState({ selectedAggregations: selectedFields }); }; @@ -314,9 +312,7 @@ export default class CreateTransformForm extends Component { const { transformJSON, selectedAggregations } = this.state; let newJSON = transformJSON; - newJSON.transform.aggregations = selectedAggregations; - this.setState({ transformJSON: newJSON }); }; diff --git a/public/pages/CreateTransform/containers/CreateTransformStep2/CreateTransformStep2.tsx b/public/pages/CreateTransform/containers/CreateTransformStep2/CreateTransformStep2.tsx index 389623083..07dcb63ac 100644 --- a/public/pages/CreateTransform/containers/CreateTransformStep2/CreateTransformStep2.tsx +++ b/public/pages/CreateTransform/containers/CreateTransformStep2/CreateTransformStep2.tsx @@ -21,7 +21,7 @@ import { BREADCRUMBS, ROUTES } from "../../../../utils/constants"; import CreateTransformSteps from "../../components/CreateTransformSteps"; import { CoreServicesContext } from "../../../../components/core_services"; import DefineTransforms from "../../components/DefineTransforms"; -import { FieldItem, TransformGroupItem, TransformAggItem } from "../../../../../models/interfaces"; +import { FieldItem, TransformGroupItem } from "../../../../../models/interfaces"; interface CreateTransformStep2Props extends RouteComponentProps { transformService: TransformService; From df883721337633c3dfb8df369a75fb52f53725d6 Mon Sep 17 00:00:00 2001 From: Annie Date: Mon, 3 May 2021 17:43:51 -0700 Subject: [PATCH 41/93] Remove debug logs --- .../components/DefineTransforms/DefineTransforms.tsx | 2 -- public/services/TransformService.ts | 4 ---- server/services/TransformService.ts | 5 ----- 3 files changed, 11 deletions(-) diff --git a/public/pages/CreateTransform/components/DefineTransforms/DefineTransforms.tsx b/public/pages/CreateTransform/components/DefineTransforms/DefineTransforms.tsx index 0e30ab654..b657858de 100644 --- a/public/pages/CreateTransform/components/DefineTransforms/DefineTransforms.tsx +++ b/public/pages/CreateTransform/components/DefineTransforms/DefineTransforms.tsx @@ -239,8 +239,6 @@ export default function DefineTransforms({ (pageIndex) => { setPagination((pagination) => ({ ...pagination, pageIndex })); setFrom(pageIndex * size); - //debug use - console.log("From: " + pageIndex * size); }, [setPagination] ); diff --git a/public/services/TransformService.ts b/public/services/TransformService.ts index 9282766ce..a9becffde 100644 --- a/public/services/TransformService.ts +++ b/public/services/TransformService.ts @@ -75,12 +75,8 @@ export default class TransformService { }; searchSampleData = async (index: string, queryObject: object): Promise> => { - //Debug use - console.log("Entering browser side service..."); const url = `..${NODE_API._SEARCH_SAMPLE_DATA}/${index}`; const response = (await this.httpClient.get(url, { query: queryObject })) as ServerResponse; - //Debug use - console.log("response: " + JSON.stringify(response)); return response; }; } diff --git a/server/services/TransformService.ts b/server/services/TransformService.ts index 847948163..d48f6f979 100644 --- a/server/services/TransformService.ts +++ b/server/services/TransformService.ts @@ -319,8 +319,6 @@ export default class TransformService { response: KibanaResponseFactory ): Promise>> => { try { - // Debug use - console.log("Entering server side service..."); const { from, size } = request.query as { from: string; size: string; @@ -334,9 +332,6 @@ export default class TransformService { const { callAsCurrentUser: callWithRequest } = this.esDriver.asScoped(request); const searchResponse: SearchResponse = await callWithRequest("search", params); - //Debug use - console.log(JSON.stringify(searchResponse)); - return response.custom({ statusCode: 200, body: { From cf1a1ce3ad65458cdd1f704a9d130bee5edcb775 Mon Sep 17 00:00:00 2001 From: Annie Date: Mon, 3 May 2021 17:50:58 -0700 Subject: [PATCH 42/93] Hide unused toolbar options --- .../components/DefineTransforms/DefineTransforms.tsx | 6 ++++++ server/services/TransformService.ts | 2 +- 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/public/pages/CreateTransform/components/DefineTransforms/DefineTransforms.tsx b/public/pages/CreateTransform/components/DefineTransforms/DefineTransforms.tsx index b657858de..5311de311 100644 --- a/public/pages/CreateTransform/components/DefineTransforms/DefineTransforms.tsx +++ b/public/pages/CreateTransform/components/DefineTransforms/DefineTransforms.tsx @@ -302,6 +302,12 @@ export default function DefineTransforms({ onChangeItemsPerPage: onChangeItemsPerPage, onChangePage: onChangePage, }} + toolbarVisibility={{ + showColumnSelector: true, + showStyleSelector: false, + showSortSelector: false, + showFullScreenSelector: false, + }} /> Group selection diff --git a/server/services/TransformService.ts b/server/services/TransformService.ts index d48f6f979..48a968985 100644 --- a/server/services/TransformService.ts +++ b/server/services/TransformService.ts @@ -289,7 +289,7 @@ export default class TransformService { body: JSON.stringify(request.body), }; if (seqNo === undefined || primaryTerm === undefined) { - method = "ism.createTransform"; + method = "ism.putTransform"; params = { transformId: id, body: JSON.stringify(request.body) }; } const { callAsCurrentUser: callWithRequest } = this.esDriver.asScoped(request); From 5dac71b2d06a3fa33eff97bc4fa2b600c6a6e82e Mon Sep 17 00:00:00 2001 From: Annie Date: Mon, 3 May 2021 18:34:58 -0700 Subject: [PATCH 43/93] Replace parent element of createTransform from form to div --- .../containers/CreateTransformForm/CreateTransformForm.tsx | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/public/pages/CreateTransform/containers/CreateTransformForm/CreateTransformForm.tsx b/public/pages/CreateTransform/containers/CreateTransformForm/CreateTransformForm.tsx index f47e4028d..385ee4675 100644 --- a/public/pages/CreateTransform/containers/CreateTransformForm/CreateTransformForm.tsx +++ b/public/pages/CreateTransform/containers/CreateTransformForm/CreateTransformForm.tsx @@ -108,7 +108,7 @@ export default class CreateTransformForm extends Component(), + selectedAggregations: {}, aggregationsError: "", description: "", @@ -386,7 +386,7 @@ export default class CreateTransformForm extends Component +
)} - +
); } } From 701f821e404d494fb2bb385eadf5610efdd5242e Mon Sep 17 00:00:00 2001 From: Annie Date: Mon, 3 May 2021 18:44:15 -0700 Subject: [PATCH 44/93] Modify create transform to avoid sending request early --- .../DefineTransforms/DefineTransforms.tsx | 18 +++++++++++------- .../components/Schedule/Schedule.tsx | 2 ++ .../CreateTransformForm.tsx | 10 +++------- .../CreateTransformStep2.tsx | 2 +- public/services/TransformService.ts | 4 ---- server/services/TransformService.ts | 5 ----- 6 files changed, 17 insertions(+), 24 deletions(-) diff --git a/public/pages/CreateTransform/components/DefineTransforms/DefineTransforms.tsx b/public/pages/CreateTransform/components/DefineTransforms/DefineTransforms.tsx index 1f30be5bb..5311de311 100644 --- a/public/pages/CreateTransform/components/DefineTransforms/DefineTransforms.tsx +++ b/public/pages/CreateTransform/components/DefineTransforms/DefineTransforms.tsx @@ -17,7 +17,7 @@ import { EuiDataGrid, EuiDataGridColumn, EuiSpacer, EuiText } from "@elastic/eui import { CoreStart } from "kibana/public"; import React, { useCallback, useState } from "react"; import { ContentPanel } from "../../../../components/ContentPanel"; -import { FieldItem, GROUP_TYPES, TransformAggItem, TransformGroupItem } from "../../../../../models/interfaces"; +import { FieldItem, GROUP_TYPES, TransformGroupItem } from "../../../../../models/interfaces"; import { TransformService } from "../../../../services"; import { getErrorMessage } from "../../../../utils/helpers"; import { isNumericMapping } from "../../utils/helpers"; @@ -198,14 +198,13 @@ export default function DefineTransforms({ const [from, setFrom] = useState(0); const [size, setSize] = useState(10); const [sortingColumns, setSortingColumns] = useState([]); - const [visibleColumns, setVisibleColumns] = useState(() => columns.map(({ id }) => id)); + const [visibleColumns, setVisibleColumns] = useState(() => columns.map(({ id }) => id).slice(0, 5)); const [data, setData] = useState([]); const [dataCount, setDataCount] = useState(0); const [groupSelection, setGroupSelection] = useState([]); const [aggSelection, setAggSelection] = useState(selectedAggregations); const fetchData = useCallback(async () => { - console.log("Entering fetchData..."); setLoading(true); try { const response = await transformService.searchSampleData(sourceIndex, { from, size }); @@ -240,8 +239,6 @@ export default function DefineTransforms({ (pageIndex) => { setPagination((pagination) => ({ ...pagination, pageIndex })); setFrom(pageIndex * size); - //debug use - console.log("From: " + pageIndex * size); }, [setPagination] ); @@ -254,8 +251,6 @@ export default function DefineTransforms({ ); const renderCellValue = ({ rowIndex, columnId }) => { - //Debug use - // console.log("rowIndex: " + rowIndex + " columnId: " + columnId + " data: " + JSON.stringify(data[rowIndex]._source[columnId])); if (!loading && data.hasOwnProperty(rowIndex)) return data[rowIndex]._source[columnId] ? data[rowIndex]._source[columnId] : null; return null; }; @@ -307,10 +302,19 @@ export default function DefineTransforms({ onChangeItemsPerPage: onChangeItemsPerPage, onChangePage: onChangePage, }} + toolbarVisibility={{ + showColumnSelector: true, + showStyleSelector: false, + showSortSelector: false, + showFullScreenSelector: false, + }} /> + Group selection {/*Debug use*/} {JSON.stringify(groupSelection)} + + Aggregation {JSON.stringify(aggSelection)} ); diff --git a/public/pages/CreateTransform/components/Schedule/Schedule.tsx b/public/pages/CreateTransform/components/Schedule/Schedule.tsx index dd8f3f20e..11391718b 100644 --- a/public/pages/CreateTransform/components/Schedule/Schedule.tsx +++ b/public/pages/CreateTransform/components/Schedule/Schedule.tsx @@ -43,6 +43,8 @@ interface ScheduleProps { interval: number; intervalTimeunit: string; intervalError: string; + pageSize: number; + onChangeJobEnabledByDefault: () => void; onChangeIntervalTime: (e: ChangeEvent) => void; onChangeIntervalTimeunit: (e: ChangeEvent) => void; onChangePage: (e: ChangeEvent) => void; diff --git a/public/pages/CreateTransform/containers/CreateTransformForm/CreateTransformForm.tsx b/public/pages/CreateTransform/containers/CreateTransformForm/CreateTransformForm.tsx index da62a8f01..385ee4675 100644 --- a/public/pages/CreateTransform/containers/CreateTransformForm/CreateTransformForm.tsx +++ b/public/pages/CreateTransform/containers/CreateTransformForm/CreateTransformForm.tsx @@ -108,7 +108,7 @@ export default class CreateTransformForm extends Component(), + selectedAggregations: {}, aggregationsError: "", description: "", @@ -256,8 +256,6 @@ export default class CreateTransformForm extends Component { - //Debug use - console.log(JSON.stringify(selectedFields)); this.setState({ selectedAggregations: selectedFields }); }; @@ -314,9 +312,7 @@ export default class CreateTransformForm extends Component { const { transformJSON, selectedAggregations } = this.state; let newJSON = transformJSON; - newJSON.transform.aggregations = selectedAggregations; - this.setState({ transformJSON: newJSON }); }; @@ -390,7 +386,7 @@ export default class CreateTransformForm extends Component +
)} - +
); } } diff --git a/public/pages/CreateTransform/containers/CreateTransformStep2/CreateTransformStep2.tsx b/public/pages/CreateTransform/containers/CreateTransformStep2/CreateTransformStep2.tsx index 389623083..07dcb63ac 100644 --- a/public/pages/CreateTransform/containers/CreateTransformStep2/CreateTransformStep2.tsx +++ b/public/pages/CreateTransform/containers/CreateTransformStep2/CreateTransformStep2.tsx @@ -21,7 +21,7 @@ import { BREADCRUMBS, ROUTES } from "../../../../utils/constants"; import CreateTransformSteps from "../../components/CreateTransformSteps"; import { CoreServicesContext } from "../../../../components/core_services"; import DefineTransforms from "../../components/DefineTransforms"; -import { FieldItem, TransformGroupItem, TransformAggItem } from "../../../../../models/interfaces"; +import { FieldItem, TransformGroupItem } from "../../../../../models/interfaces"; interface CreateTransformStep2Props extends RouteComponentProps { transformService: TransformService; diff --git a/public/services/TransformService.ts b/public/services/TransformService.ts index 18bd83792..36184546d 100644 --- a/public/services/TransformService.ts +++ b/public/services/TransformService.ts @@ -78,12 +78,8 @@ export default class TransformService { }; searchSampleData = async (index: string, queryObject: object): Promise> => { - //Debug use - console.log("Entering browser side service..."); const url = `..${NODE_API._SEARCH_SAMPLE_DATA}/${index}`; const response = (await this.httpClient.get(url, { query: queryObject })) as ServerResponse; - //Debug use - console.log("response: " + JSON.stringify(response)); return response; }; } diff --git a/server/services/TransformService.ts b/server/services/TransformService.ts index 1f69c4a52..a4003707c 100644 --- a/server/services/TransformService.ts +++ b/server/services/TransformService.ts @@ -310,8 +310,6 @@ export default class TransformService { response: KibanaResponseFactory ): Promise>> => { try { - // Debug use - console.log("Entering server side service..."); const { from, size } = request.query as { from: string; size: string; @@ -325,9 +323,6 @@ export default class TransformService { const { callAsCurrentUser: callWithRequest } = this.esDriver.asScoped(request); const searchResponse: SearchResponse = await callWithRequest("search", params); - //Debug use - console.log(JSON.stringify(searchResponse)); - return response.custom({ statusCode: 200, body: { From 9805fcf836575ec0893a7c0524484148bad9d270 Mon Sep 17 00:00:00 2001 From: Annie Date: Tue, 4 May 2021 00:52:37 -0700 Subject: [PATCH 45/93] Add data filter component draft --- public/pages/CreateRollup/utils/constants.ts | 52 +++++++ public/pages/CreateRollup/utils/helpers.ts | 8 ++ .../DefineTransforms/DefineTransforms.tsx | 2 - .../IndexFilterPopover/IndexFilterPopover.tsx | 136 +++++++++++------- .../TransformIndices/TransformIndices.tsx | 91 +++++++----- .../CreateTransform/CreateTransform.tsx | 5 +- .../CreateTransformForm.tsx | 2 + 7 files changed, 205 insertions(+), 91 deletions(-) diff --git a/public/pages/CreateRollup/utils/constants.ts b/public/pages/CreateRollup/utils/constants.ts index 3cf6e8ea4..c3688d5c4 100644 --- a/public/pages/CreateRollup/utils/constants.ts +++ b/public/pages/CreateRollup/utils/constants.ts @@ -86,3 +86,55 @@ export const AddFieldsColumns = [ render: (type: string | undefined) => (type == null || type == undefined ? "-" : type), }, ]; + +export const WHERE_BOOLEAN_FILTERS = [ + { text: "Select value", value: "" }, + { text: "True", value: true }, + { text: "False", value: false }, +]; + +export const OPERATORS_MAP = { + IS: "is", + IS_NOT: "is_not", + IS_NULL: "is_null", + IS_NOT_NULL: "is_not_null", + IS_GREATER: "is_greater", + IS_GREATER_EQUAL: "is_greater_equal", + IS_LESS: "is_less", + IS_LESS_EQUAL: "is_less_equal", + STARTS_WITH: "starts_with", + ENDS_WITH: "ends_with", + CONTAINS: "contains", + NOT_CONTAINS: "does_not_contains", + IN_RANGE: "in_range", + NOT_IN_RANGE: "not_in_range", +}; + +export const COMPARISON_OPERATORS = [ + { text: "is", value: OPERATORS_MAP.IS, dataTypes: ["number", "text", "keyword", "boolean"] }, + { + text: "is not", + value: OPERATORS_MAP.IS_NOT, + dataTypes: ["number", "text", "keyword", "boolean"], + }, + { + text: "is null", + value: OPERATORS_MAP.IS_NULL, + dataTypes: ["number", "text", "keyword", "boolean"], + }, + { + text: "is not null", + value: OPERATORS_MAP.IS_NOT_NULL, + dataTypes: ["number", "text", "keyword"], + }, + { text: "is greater than", value: OPERATORS_MAP.IS_GREATER, dataTypes: ["number"] }, + { text: "is greater than equal", value: OPERATORS_MAP.IS_GREATER_EQUAL, dataTypes: ["number"] }, + { text: "is less than", value: OPERATORS_MAP.IS_LESS, dataTypes: ["number"] }, + { text: "is less than equal", value: OPERATORS_MAP.IS_LESS_EQUAL, dataTypes: ["number"] }, + { text: "is in range", value: OPERATORS_MAP.IN_RANGE, dataTypes: ["number"] }, + { text: "is not in range", value: OPERATORS_MAP.NOT_IN_RANGE, dataTypes: ["number"] }, + { text: "starts with", value: OPERATORS_MAP.STARTS_WITH, dataTypes: ["text", "keyword"] }, + { text: "ends with", value: OPERATORS_MAP.ENDS_WITH, dataTypes: ["text", "keyword"] }, + { text: "contains", value: OPERATORS_MAP.CONTAINS, dataTypes: ["text", "keyword"] }, + { text: "does not contains", value: OPERATORS_MAP.NOT_CONTAINS, dataTypes: ["text"] }, +]; diff --git a/public/pages/CreateRollup/utils/helpers.ts b/public/pages/CreateRollup/utils/helpers.ts index 029145ed9..e090837b8 100644 --- a/public/pages/CreateRollup/utils/helpers.ts +++ b/public/pages/CreateRollup/utils/helpers.ts @@ -14,6 +14,7 @@ */ import { FieldItem } from "../../../../models/interfaces"; +import { COMPARISON_OPERATORS } from "./constants"; export const parseTimeunit = (timeunit: string): string => { if (timeunit == "ms" || timeunit == "Milliseconds") return "millisecond(s)"; @@ -61,3 +62,10 @@ export const parseFieldOptions = (prefix: string, mappings: any): FieldItem[] => } return fieldsOption; }; + +export const getOperators = (fieldType) => + COMPARISON_OPERATORS.reduce( + (acc, currentOperator) => + currentOperator.dataTypes.includes(fieldType) ? [...acc, { text: currentOperator.text, value: currentOperator.value }] : acc, + [] + ); diff --git a/public/pages/CreateTransform/components/DefineTransforms/DefineTransforms.tsx b/public/pages/CreateTransform/components/DefineTransforms/DefineTransforms.tsx index 5311de311..d269b1472 100644 --- a/public/pages/CreateTransform/components/DefineTransforms/DefineTransforms.tsx +++ b/public/pages/CreateTransform/components/DefineTransforms/DefineTransforms.tsx @@ -139,11 +139,9 @@ export default function DefineTransforms({ { label: "Aggregate by avg ", onClick: () => { - console.log("Before set: " + JSON.stringify(aggSelection)); aggSelection[`avg_${field.label}`] = { avg: { field: field.label }, }; - console.log("After set: " + JSON.stringify(aggSelection)); onAggregationSelectionChange(aggSelection); }, size: "xs", diff --git a/public/pages/CreateTransform/components/IndexFilterPopover/IndexFilterPopover.tsx b/public/pages/CreateTransform/components/IndexFilterPopover/IndexFilterPopover.tsx index dcd4b6044..f8c3df631 100644 --- a/public/pages/CreateTransform/components/IndexFilterPopover/IndexFilterPopover.tsx +++ b/public/pages/CreateTransform/components/IndexFilterPopover/IndexFilterPopover.tsx @@ -13,68 +13,96 @@ * permissions and limitations under the License. */ -import React, { Component } from "react"; -import { EuiComboBox, - EuiForm, - EuiFlexGrid, - EuiFlexItem, - } from "@elastic/eui"; -import { EuiComboBoxOptionOption } from "@elastic/eui/src/components/combo_box/types"; -import _ from "lodash"; -import { CoreServicesContext } from "../../../../components/core_services"; +import React, { ChangeEvent, useState } from "react"; +import { EuiForm, EuiFlexItem, EuiFormRow, EuiSelect, EuiPopoverTitle, EuiSpacer, EuiFlexGroup } from "@elastic/eui"; +import { FieldItem } from "../../../../../models/interfaces"; interface IndexFilterPopoverProps { - -} - -interface IndexFilterPopoverState { - fieldSelectedOptions: []; + fields: FieldItem[]; + selectedField: string; + setSelectedField: () => void; } -export default class IndexFilterPopover extends Component { - static contextType = CoreServicesContext; - - constructor(props: IndexFilterPopoverProps) { - super(props); - this.state = { - fieldSelectedOptions: [], - } - } - - async componentDidMount(): Promise { +export default function IndexFilterPopover({ fields }: IndexFilterPopoverProps) { + const [selectedField, setSelectedField] = useState(""); + const [selectedOperator, setSelectedOperator] = useState(""); + const [selectedValue, setSelectedValue] = useState(""); - } + const onChangeSelectedField = (e: ChangeEvent): void => { + setSelectedField(e.target.value); + }; + const onChangeSelectedOperator = (e: ChangeEvent): void => { + setSelectedOperator(e.target.value); + }; + const onChangeSelectedValue = (e: ChangeEvent): void => { + setSelectedValue(e.target.value); + }; - onFieldChange = (options) => {}; - - render() { - - const { - fieldSelectedOptions, - } = this.state; - - return( + return ( +
+ + {/**/} + {/**/} + Add data filter + {/**/} + {/**/} + {/**/} + {/*TODO:Add custom expression editor*/} + {/**/} + {/* {this.state.isCustomEditorOpen ? (*/} + {/* */} + {/* ) : (*/} + {/* */} + {/* )}*/} + {/**/} + {/**/} + {/**/} + - - - Fields - - - - Operator + + + + { + return { + value: item.label, + text: item.label, + }; + })} + value={selectedField} + onChange={onChangeSelectedField} + /> + - - Value + + + + - + + + + + + - ) - } +
+ ); } diff --git a/public/pages/CreateTransform/components/TransformIndices/TransformIndices.tsx b/public/pages/CreateTransform/components/TransformIndices/TransformIndices.tsx index f074f1f7d..d3c8eee07 100644 --- a/public/pages/CreateTransform/components/TransformIndices/TransformIndices.tsx +++ b/public/pages/CreateTransform/components/TransformIndices/TransformIndices.tsx @@ -14,14 +14,20 @@ */ import React, { Component, Fragment } from "react"; -import { EuiSpacer, - EuiFormRow, - EuiComboBox, - EuiCallOut, - EuiFacetButton, - EuiAvatar, - EuiPopover, - } from "@elastic/eui"; +import { + EuiSpacer, + EuiFormRow, + EuiComboBox, + EuiCallOut, + EuiFacetButton, + EuiAvatar, + EuiPopover, + EuiFlexGroup, + EuiFlexItem, + EuiText, + EuiButtonEmpty, + EuiHorizontalRule, +} from "@elastic/eui"; import { ContentPanel } from "../../../../components/ContentPanel"; import IndexFilterPopover from "../IndexFilterPopover"; import { EuiComboBoxOptionOption } from "@elastic/eui/src/components/combo_box/types"; @@ -118,16 +124,15 @@ export default class TransformIndices extends Component { const { isPopoverOpen } = this.state; if (isPopoverOpen) { - this.setState({isPopoverOpen: false}); + this.setState({ isPopoverOpen: false }); } else { - this.setState({isPopoverOpen: true}); + this.setState({ isPopoverOpen: true }); } - } + }; - closePopover = () => this.setState({isPopoverOpen: false}); + closePopover = () => this.setState({ isPopoverOpen: false }); render() { - const { sourceIndex, sourceIndexError, @@ -138,20 +143,16 @@ export default class TransformIndices extends Component} // Should be filter icon per design doc onClick={this.onButtonClick} > - + Add data filter + + Add data filter - ) + ); return ( @@ -173,7 +174,7 @@ export default class TransformIndices extends Component
- + + +

Source index filter

+
+
+ + + - optional + + + + + + Choose a subset of source index to focus on to optimize for performance and computing resource. You can’t change filter once the + job is created. + + + this.onButtonClick()} + data-test-subj="addFilter" + className="globalFilterBar__addButton" + > + + Add data filter + + } + isOpen={isPopoverOpen} + closePopover={this.closePopover} > - - - -
- + + + + []) => void; currentStep: number; hasAggregation: boolean; + fields: FieldItem[]; + fieldSelectedOption: string; + onFieldChange: () => void; } export default class CreateTransform extends Component { diff --git a/public/pages/CreateTransform/containers/CreateTransformForm/CreateTransformForm.tsx b/public/pages/CreateTransform/containers/CreateTransformForm/CreateTransformForm.tsx index 385ee4675..7a3e81bdd 100644 --- a/public/pages/CreateTransform/containers/CreateTransformForm/CreateTransformForm.tsx +++ b/public/pages/CreateTransform/containers/CreateTransformForm/CreateTransformForm.tsx @@ -68,6 +68,7 @@ interface CreateTransformFormState { mappings: any; allMappings: FieldItem[][]; fields: FieldItem[]; + fieldSelectedOption: string; selectedTerms: FieldItem[]; selectedGroupField: TransformGroupItem[]; @@ -405,6 +406,7 @@ export default class CreateTransformForm extends Component Date: Tue, 4 May 2021 00:55:51 -0700 Subject: [PATCH 46/93] Hide add data filter --- .../TransformIndices/TransformIndices.tsx | 72 +++++++++---------- 1 file changed, 36 insertions(+), 36 deletions(-) diff --git a/public/pages/CreateTransform/components/TransformIndices/TransformIndices.tsx b/public/pages/CreateTransform/components/TransformIndices/TransformIndices.tsx index d3c8eee07..c3b45e013 100644 --- a/public/pages/CreateTransform/components/TransformIndices/TransformIndices.tsx +++ b/public/pages/CreateTransform/components/TransformIndices/TransformIndices.tsx @@ -190,42 +190,42 @@ export default class TransformIndices extends Component - - - -

Source index filter

-
-
- - - - optional - - -
- - - Choose a subset of source index to focus on to optimize for performance and computing resource. You can’t change filter once the - job is created. - - - this.onButtonClick()} - data-test-subj="addFilter" - className="globalFilterBar__addButton" - > - + Add data filter - - } - isOpen={isPopoverOpen} - closePopover={this.closePopover} - > - - - - + {/**/} + {/* */} + {/* */} + {/*

Source index filter

*/} + {/*
*/} + {/*
*/} + {/* */} + {/* */} + {/* - optional*/} + {/* */} + {/* */} + {/*
*/} + + {/**/} + {/* Choose a subset of source index to focus on to optimize for performance and computing resource. You can’t change filter once the*/} + {/* job is created.*/} + {/**/} + + {/* this.onButtonClick()}*/} + {/* data-test-subj="addFilter"*/} + {/* className="globalFilterBar__addButton"*/} + {/* >*/} + {/* + Add data filter*/} + {/* */} + {/* }*/} + {/* isOpen={isPopoverOpen}*/} + {/* closePopover={this.closePopover}*/} + {/*>*/} + {/* */} + {/**/} + {/**/} + {/**/} Date: Tue, 4 May 2021 15:15:01 -0700 Subject: [PATCH 47/93] Show keyword fields --- .../DefineTransforms/DefineTransforms.tsx | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/public/pages/CreateTransform/components/DefineTransforms/DefineTransforms.tsx b/public/pages/CreateTransform/components/DefineTransforms/DefineTransforms.tsx index d269b1472..95c35108d 100644 --- a/public/pages/CreateTransform/components/DefineTransforms/DefineTransforms.tsx +++ b/public/pages/CreateTransform/components/DefineTransforms/DefineTransforms.tsx @@ -52,6 +52,7 @@ export default function DefineTransforms({ columns.push({ id: field.label, displayAsText: field.label + " type: " + field.type, + schema: field.type, actions: { showHide: false, showMoveLeft: false, @@ -249,8 +250,16 @@ export default function DefineTransforms({ ); const renderCellValue = ({ rowIndex, columnId }) => { - if (!loading && data.hasOwnProperty(rowIndex)) return data[rowIndex]._source[columnId] ? data[rowIndex]._source[columnId] : null; - return null; + if (!loading && data.hasOwnProperty(rowIndex)) { + // TODO: work on truncating the value to certain length defined by the keyword field + if (columns?.find((column) => column.id == columnId).schema == "keyword") { + // Strip off the keyword postfix + const correspondingTextColumnId = columnId.replace(".keyword", ""); + return data[rowIndex]._source[correspondingTextColumnId] ? data[rowIndex]._source[correspondingTextColumnId] : "-"; + } + return data[rowIndex]._source[columnId] ? data[rowIndex]._source[columnId] : "-"; + } + return "-"; }; return ( From 130161c60a4f23e19afeee10a18edcfea0dc0730 Mon Sep 17 00:00:00 2001 From: Annie Date: Tue, 4 May 2021 15:17:08 -0700 Subject: [PATCH 48/93] Update Group selection and aggregation title --- .../components/DefineTransforms/DefineTransforms.tsx | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/public/pages/CreateTransform/components/DefineTransforms/DefineTransforms.tsx b/public/pages/CreateTransform/components/DefineTransforms/DefineTransforms.tsx index 95c35108d..f17bbff25 100644 --- a/public/pages/CreateTransform/components/DefineTransforms/DefineTransforms.tsx +++ b/public/pages/CreateTransform/components/DefineTransforms/DefineTransforms.tsx @@ -317,11 +317,15 @@ export default function DefineTransforms({ }} /> - Group selection + +

Group selection

+
{/*Debug use*/} {JSON.stringify(groupSelection)} - Aggregation + +

Aggregation

+
{JSON.stringify(aggSelection)} ); From 7c10d600797f9a2de568f994f5ccddb2427656c8 Mon Sep 17 00:00:00 2001 From: Annie Date: Tue, 4 May 2021 15:20:09 -0700 Subject: [PATCH 49/93] Update DefineTransforms.tsx --- .../components/DefineTransforms/DefineTransforms.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/public/pages/CreateTransform/components/DefineTransforms/DefineTransforms.tsx b/public/pages/CreateTransform/components/DefineTransforms/DefineTransforms.tsx index f17bbff25..ba988d8b3 100644 --- a/public/pages/CreateTransform/components/DefineTransforms/DefineTransforms.tsx +++ b/public/pages/CreateTransform/components/DefineTransforms/DefineTransforms.tsx @@ -292,7 +292,7 @@ export default function DefineTransforms({ {/*TODO: Substitute "source index", and "filtered by" fields with actual values*/} -

{`Viewing sample data from index ${sourceIndex}, filtered by order.type:sales_order, order.success:true`}

+

{`Viewing sample data from index ${sourceIndex}`}

{/*TODO: add rowCount*/} From 7b1e1049253ee7fb3665b683746ee00bb0790fff Mon Sep 17 00:00:00 2001 From: Eric Lobdell Date: Tue, 4 May 2021 15:57:26 -0700 Subject: [PATCH 50/93] Added group and aggregation to display on create transform review page --- .../JobNameAndIndices/JobNameAndIndices.tsx | 16 ++- .../ReviewDefinition/ReviewDefinition.tsx | 98 +++++++++++++++++++ .../components/ReviewDefinition/index.ts | 18 ++++ .../CreateTransformForm.tsx | 5 +- .../CreateTransformStep4.tsx | 12 ++- 5 files changed, 145 insertions(+), 4 deletions(-) create mode 100644 public/pages/CreateTransform/components/ReviewDefinition/ReviewDefinition.tsx create mode 100644 public/pages/CreateTransform/components/ReviewDefinition/index.ts diff --git a/public/pages/CreateTransform/components/JobNameAndIndices/JobNameAndIndices.tsx b/public/pages/CreateTransform/components/JobNameAndIndices/JobNameAndIndices.tsx index e2e01d3dd..a7ea29ceb 100644 --- a/public/pages/CreateTransform/components/JobNameAndIndices/JobNameAndIndices.tsx +++ b/public/pages/CreateTransform/components/JobNameAndIndices/JobNameAndIndices.tsx @@ -24,6 +24,7 @@ interface JobNameAndIndicesProps { description: string; sourceIndex: { label: string; value?: IndexItem }[]; targetIndex: { label: string; value?: IndexItem }[]; + sourceIndexFilter: {}[]; onChangeStep: (step: number) => void; } @@ -33,7 +34,12 @@ export default class JobNameAndIndices extends Component } render() { - const { transformId, description, onChangeStep, sourceIndex, targetIndex } = this.props; + const { transformId, + description, + onChangeStep, + sourceIndex, + targetIndex, + sourceIndexFilter } = this.props; return ( } bodyStyles={{ padding: "initial" }} - title="Job name and indices" + title="Set up indices" titleSize="m" >
@@ -84,6 +90,12 @@ export default class JobNameAndIndices extends Component
{description == "" ? "-" : description}
+ + +
Source index filter
+
{sourceIndexFilter}
+
+
diff --git a/public/pages/CreateTransform/components/ReviewDefinition/ReviewDefinition.tsx b/public/pages/CreateTransform/components/ReviewDefinition/ReviewDefinition.tsx new file mode 100644 index 000000000..5b013ed93 --- /dev/null +++ b/public/pages/CreateTransform/components/ReviewDefinition/ReviewDefinition.tsx @@ -0,0 +1,98 @@ +/* + * Copyright 2021 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"). + * You may not use this file except in compliance with the License. + * A copy of the License is located at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * or in the "license" file accompanying this file. This file is distributed + * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either + * express or implied. See the License for the specific language governing + * permissions and limitations under the License. + */ + +import React, { Component } from "react"; +import { EuiFlexGrid, EuiSpacer, EuiFlexItem, EuiText } from "@elastic/eui"; +import { ContentPanel, ContentPanelActions } from "../../../../components/ContentPanel"; +import { ModalConsumer } from "../../../../components/Modal"; +import { TransformGroupItem } from "../../../../../models/interfaces"; + +interface ReviewDefinitionProps { + + selectedGroupField: TransformGroupItem[]; + selectedAggregations: any; + onChangeStep: (step: number) => void; +} + +export default class ReviewDefinition extends Component { + constructor(props: ReviewDefinitionProps) { + super(props); + } + + render() { + const { selectedGroupField, + selectedAggregations, + onChangeStep } = this.props; + + const groupItems = () => { + return (selectedGroupField.map((group) => { + return( + + +
Group
+
{JSON.stringify(group)}
+
+
+ ) + })); + }; + + const aggItems = () => { + return (Object.keys(selectedAggregations).map((key) => { + return( + + +
{key}
+
{JSON.stringify(selectedAggregations[key])}
+
+
+ ) + })); + } + + return ( + + {() => ( + onChangeStep(2), + }, + }, + ]} + /> + )} + + } + bodyStyles={{ padding: "initial" }} + title="Define transforms" + titleSize="m" + > +
+ + + {groupItems()} + {aggItems()} + +
+ +
+ ) + } +} diff --git a/public/pages/CreateTransform/components/ReviewDefinition/index.ts b/public/pages/CreateTransform/components/ReviewDefinition/index.ts new file mode 100644 index 000000000..7776389d4 --- /dev/null +++ b/public/pages/CreateTransform/components/ReviewDefinition/index.ts @@ -0,0 +1,18 @@ +/* + * Copyright 2021 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"). + * You may not use this file except in compliance with the License. + * A copy of the License is located at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * or in the "license" file accompanying this file. This file is distributed + * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either + * express or implied. See the License for the specific language governing + * permissions and limitations under the License. + */ + +import ReviewDefinition from "./ReviewDefinition"; + +export default ReviewDefinition; diff --git a/public/pages/CreateTransform/containers/CreateTransformForm/CreateTransformForm.tsx b/public/pages/CreateTransform/containers/CreateTransformForm/CreateTransformForm.tsx index 7a3e81bdd..33013fe66 100644 --- a/public/pages/CreateTransform/containers/CreateTransformForm/CreateTransformForm.tsx +++ b/public/pages/CreateTransform/containers/CreateTransformForm/CreateTransformForm.tsx @@ -62,6 +62,7 @@ interface CreateTransformFormState { description: string; sourceIndex: { label: string; value?: IndexItem }[]; sourceIndexError: string; + sourceIndexFilter: {}[]; targetIndex: { label: string; value?: IndexItem }[]; targetIndexError: string; @@ -72,7 +73,7 @@ interface CreateTransformFormState { selectedTerms: FieldItem[]; selectedGroupField: TransformGroupItem[]; - selectedAggregations: any; // Needs to be Map + selectedAggregations: any; aggregationsError: string; selectedFields: FieldItem[]; jobEnabledByDefault: boolean; @@ -115,6 +116,7 @@ export default class CreateTransformForm extends Component[]; timezone: string; @@ -71,6 +80,7 @@ export default class CreateTransformStep4 extends Component +

You can't change aggregations or metrics after creating a job. Double-check your choices before proceeding.

From 67a6dfd1e198ead9f1e456bc5c8ca91650b2d3e2 Mon Sep 17 00:00:00 2001 From: Annie Date: Tue, 4 May 2021 16:22:20 -0700 Subject: [PATCH 51/93] Showing keyword field preview on step 2 of create --- .../DefineTransforms/DefineTransforms.tsx | 23 +++++++++++++++---- 1 file changed, 18 insertions(+), 5 deletions(-) diff --git a/public/pages/CreateTransform/components/DefineTransforms/DefineTransforms.tsx b/public/pages/CreateTransform/components/DefineTransforms/DefineTransforms.tsx index d269b1472..ba988d8b3 100644 --- a/public/pages/CreateTransform/components/DefineTransforms/DefineTransforms.tsx +++ b/public/pages/CreateTransform/components/DefineTransforms/DefineTransforms.tsx @@ -52,6 +52,7 @@ export default function DefineTransforms({ columns.push({ id: field.label, displayAsText: field.label + " type: " + field.type, + schema: field.type, actions: { showHide: false, showMoveLeft: false, @@ -249,8 +250,16 @@ export default function DefineTransforms({ ); const renderCellValue = ({ rowIndex, columnId }) => { - if (!loading && data.hasOwnProperty(rowIndex)) return data[rowIndex]._source[columnId] ? data[rowIndex]._source[columnId] : null; - return null; + if (!loading && data.hasOwnProperty(rowIndex)) { + // TODO: work on truncating the value to certain length defined by the keyword field + if (columns?.find((column) => column.id == columnId).schema == "keyword") { + // Strip off the keyword postfix + const correspondingTextColumnId = columnId.replace(".keyword", ""); + return data[rowIndex]._source[correspondingTextColumnId] ? data[rowIndex]._source[correspondingTextColumnId] : "-"; + } + return data[rowIndex]._source[columnId] ? data[rowIndex]._source[columnId] : "-"; + } + return "-"; }; return ( @@ -283,7 +292,7 @@ export default function DefineTransforms({ {/*TODO: Substitute "source index", and "filtered by" fields with actual values*/} -

{`Viewing sample data from index ${sourceIndex}, filtered by order.type:sales_order, order.success:true`}

+

{`Viewing sample data from index ${sourceIndex}`}

{/*TODO: add rowCount*/} @@ -308,11 +317,15 @@ export default function DefineTransforms({ }} /> - Group selection + +

Group selection

+
{/*Debug use*/} {JSON.stringify(groupSelection)} - Aggregation + +

Aggregation

+
{JSON.stringify(aggSelection)}
); From 05ab2a231349861d46d9212d020e9bce9d0e3317 Mon Sep 17 00:00:00 2001 From: Eric Lobdell Date: Thu, 6 May 2021 11:28:15 -0700 Subject: [PATCH 52/93] Added Specify schedule Review panel --- .../ReviewSchedule/ReviewSchedule.tsx | 94 +++++++++++++++++++ .../components/ReviewSchedule/index.ts | 18 ++++ .../components/Schedule/Schedule.tsx | 2 - .../CreateTransformStep4.tsx | 3 + 4 files changed, 115 insertions(+), 2 deletions(-) create mode 100644 public/pages/CreateTransform/components/ReviewSchedule/ReviewSchedule.tsx create mode 100644 public/pages/CreateTransform/components/ReviewSchedule/index.ts diff --git a/public/pages/CreateTransform/components/ReviewSchedule/ReviewSchedule.tsx b/public/pages/CreateTransform/components/ReviewSchedule/ReviewSchedule.tsx new file mode 100644 index 000000000..74aa9d58b --- /dev/null +++ b/public/pages/CreateTransform/components/ReviewSchedule/ReviewSchedule.tsx @@ -0,0 +1,94 @@ +/* + * Copyright 2021 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"). + * You may not use this file except in compliance with the License. + * A copy of the License is located at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * or in the "license" file accompanying this file. This file is distributed + * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either + * express or implied. See the License for the specific language governing + * permissions and limitations under the License. + */ + +import React, { Component } from "react"; +import { EuiFlexGrid, EuiSpacer, EuiFlexItem, EuiText } from "@elastic/eui"; +import { ContentPanel, ContentPanelActions } from "../../../../components/ContentPanel"; +import { ModalConsumer } from "../../../../components/Modal"; + +interface ReviewScheduleProps { + jobEnabledByDefault: boolean; + interval: number; + intervalTimeunit: string; + pageSize: number; + onChangeStep: (step: number) => void; +} + +export default class ReviewSchedule extends Component { + constructor(props: ReviewScheduleProps) { + super(props); + } + + render() { + const { jobEnabledByDefault, + interval, + intervalTimeunit, + pageSize, + onChangeStep, + } = this.props; + + const enabled = (jobEnabledByDefault) ? ("Yes") : ("No"); + + const schedule = "Every " + interval + " " + intervalTimeunit.toLowerCase(); + + return ( + + {() => ( + onChangeStep(3) + }, + }, + ]} + /> + )} + + } + bodyStyles={{ padding: "initial" }} + title="Specify schedule" + titleSize="m" + > +
+ + + + +
Enabled by default
+
{enabled}
+
+
+ + +
Schedule
+
{schedule}
+
+
+ + +
Pages per execution
+
{pageSize}
+
+
+
+
+
+ ) + } +} diff --git a/public/pages/CreateTransform/components/ReviewSchedule/index.ts b/public/pages/CreateTransform/components/ReviewSchedule/index.ts new file mode 100644 index 000000000..516d7cc30 --- /dev/null +++ b/public/pages/CreateTransform/components/ReviewSchedule/index.ts @@ -0,0 +1,18 @@ +/* + * Copyright 2021 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"). + * You may not use this file except in compliance with the License. + * A copy of the License is located at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * or in the "license" file accompanying this file. This file is distributed + * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either + * express or implied. See the License for the specific language governing + * permissions and limitations under the License. + */ + +import ReviewSchedule from "./ReviewSchedule"; + +export default ReviewSchedule; diff --git a/public/pages/CreateTransform/components/Schedule/Schedule.tsx b/public/pages/CreateTransform/components/Schedule/Schedule.tsx index 11391718b..dd8f3f20e 100644 --- a/public/pages/CreateTransform/components/Schedule/Schedule.tsx +++ b/public/pages/CreateTransform/components/Schedule/Schedule.tsx @@ -43,8 +43,6 @@ interface ScheduleProps { interval: number; intervalTimeunit: string; intervalError: string; - pageSize: number; - onChangeJobEnabledByDefault: () => void; onChangeIntervalTime: (e: ChangeEvent) => void; onChangeIntervalTimeunit: (e: ChangeEvent) => void; onChangePage: (e: ChangeEvent) => void; diff --git a/public/pages/CreateTransform/containers/CreateTransformStep4/CreateTransformStep4.tsx b/public/pages/CreateTransform/containers/CreateTransformStep4/CreateTransformStep4.tsx index a9b64b211..ef5a1612e 100644 --- a/public/pages/CreateTransform/containers/CreateTransformStep4/CreateTransformStep4.tsx +++ b/public/pages/CreateTransform/containers/CreateTransformStep4/CreateTransformStep4.tsx @@ -26,6 +26,7 @@ import { DimensionItem, import CreateTransformSteps from "../../components/CreateTransformSteps"; import JobNameAndIndices from "../../components/JobNameAndIndices"; import ReviewDefinition from "../../components/ReviewDefinition"; +import ReviewSchedule from "../../components/ReviewSchedule"; import { CoreServicesContext } from "../../../../components/core_services"; interface CreateTransformProps extends RouteComponentProps { @@ -82,6 +83,8 @@ export default class CreateTransformStep4 extends Component + +

You can't change aggregations or metrics after creating a job. Double-check your choices before proceeding.

From b246f2911d6143b744b126ea6f91ab42af2bde70 Mon Sep 17 00:00:00 2001 From: Annie Date: Thu, 6 May 2021 15:06:53 -0700 Subject: [PATCH 53/93] Add source index filter --- .../IndexFilterPopover/IndexFilterPopover.tsx | 108 +++++++++++------ .../TransformIndices/TransformIndices.tsx | 109 +++++++++++------- .../CreateTransform/CreateTransform.tsx | 4 + .../CreateTransformForm.tsx | 21 +++- 4 files changed, 160 insertions(+), 82 deletions(-) diff --git a/public/pages/CreateTransform/components/IndexFilterPopover/IndexFilterPopover.tsx b/public/pages/CreateTransform/components/IndexFilterPopover/IndexFilterPopover.tsx index f8c3df631..0247ac4ed 100644 --- a/public/pages/CreateTransform/components/IndexFilterPopover/IndexFilterPopover.tsx +++ b/public/pages/CreateTransform/components/IndexFilterPopover/IndexFilterPopover.tsx @@ -14,19 +14,33 @@ */ import React, { ChangeEvent, useState } from "react"; -import { EuiForm, EuiFlexItem, EuiFormRow, EuiSelect, EuiPopoverTitle, EuiSpacer, EuiFlexGroup } from "@elastic/eui"; -import { FieldItem } from "../../../../../models/interfaces"; +import { + EuiForm, + EuiFlexItem, + EuiFormRow, + EuiSelect, + EuiPopoverTitle, + EuiSpacer, + EuiFlexGroup, + EuiButtonEmpty, + EuiCodeEditor, + EuiButton, +} from "@elastic/eui"; +import { FieldItem, IndexItem } from "../../../../../models/interfaces"; interface IndexFilterPopoverProps { + sourceIndex: { label: string; value?: IndexItem }[]; fields: FieldItem[]; - selectedField: string; - setSelectedField: () => void; + onChangeSourceIndexFilter: (sourceIndexFilter: string) => void; + closePopover: () => void; } -export default function IndexFilterPopover({ fields }: IndexFilterPopoverProps) { +export default function IndexFilterPopover({ sourceIndex, fields, onChangeSourceIndexFilter, closePopover }: IndexFilterPopoverProps) { const [selectedField, setSelectedField] = useState(""); const [selectedOperator, setSelectedOperator] = useState(""); const [selectedValue, setSelectedValue] = useState(""); + const [isCustomEditorOpen, setIsCustomEditorOpen] = useState(false); + const [queryDsl, setQueryDsl] = useState(""); const onChangeSelectedField = (e: ChangeEvent): void => { setSelectedField(e.target.value); @@ -38,37 +52,9 @@ export default function IndexFilterPopover({ fields }: IndexFilterPopoverProps) setSelectedValue(e.target.value); }; - return ( -
- - {/**/} - {/**/} - Add data filter - {/**/} - {/**/} - {/**/} - {/*TODO:Add custom expression editor*/} - {/**/} - {/* {this.state.isCustomEditorOpen ? (*/} - {/* */} - {/* ) : (*/} - {/* */} - {/* )}*/} - {/**/} - {/**/} - {/**/} - - + function paramsEditor() { + return ( +
@@ -102,6 +88,56 @@ export default function IndexFilterPopover({ fields }: IndexFilterPopoverProps) +
+ ); + } + + function customEditor() { + return ( + + setQueryDsl(string)} mode="json" width="100%" height="250px" /> + + ); + } + + return ( +
+ + + Add data filter + {/**/} + {/* setIsCustomEditorOpen(!isCustomEditorOpen)}>*/} + {/* {isCustomEditorOpen ? "Edit filter values" : "Custom expression"}*/} + {/* */} + {/**/} + + + + {/*TODO: implement paramsEditor and uncomment the line below*/} + {/*{isCustomEditorOpen ? customEditor() : paramsEditor()}*/} + {customEditor()} + + + + { + onChangeSourceIndexFilter(queryDsl); + closePopover(); + }} + // isDisabled={!this.isFilterValid()} + data-test-subj="saveFilter" + > + Save + + + + + Cancel + + + +
); diff --git a/public/pages/CreateTransform/components/TransformIndices/TransformIndices.tsx b/public/pages/CreateTransform/components/TransformIndices/TransformIndices.tsx index c3b45e013..137f2b770 100644 --- a/public/pages/CreateTransform/components/TransformIndices/TransformIndices.tsx +++ b/public/pages/CreateTransform/components/TransformIndices/TransformIndices.tsx @@ -27,24 +27,30 @@ import { EuiText, EuiButtonEmpty, EuiHorizontalRule, + EuiComboBoxOptionOption, + EuiBadge, } from "@elastic/eui"; +import _ from "lodash"; import { ContentPanel } from "../../../../components/ContentPanel"; import IndexFilterPopover from "../IndexFilterPopover"; -import { EuiComboBoxOptionOption } from "@elastic/eui/src/components/combo_box/types"; -import { IndexItem } from "../../../../../models/interfaces"; +import { FieldItem, IndexItem } from "../../../../../models/interfaces"; import IndexService from "../../../../services/IndexService"; -import _ from "lodash"; import { CoreServicesContext } from "../../../../components/core_services"; interface TransformIndicesProps { indexService: IndexService; sourceIndex: { label: string; value?: IndexItem }[]; + //TODO: Uncomment the following line when multiple data filter is supported + // sourceIndexFilter: string[]; + sourceIndexFilter: string; sourceIndexError: string; targetIndex: { label: string; value?: IndexItem }[]; targetIndexError: string; onChangeSourceIndex: (options: EuiComboBoxOptionOption[]) => void; + onChangeSourceIndexFilter: (sourceIndexFilter: string) => void; onChangeTargetIndex: (options: EuiComboBoxOptionOption[]) => void; hasAggregation: boolean; + fields: FieldItem[]; } interface TransformIndicesState { @@ -53,6 +59,8 @@ interface TransformIndicesState { targetIndexOptions: { label: string; value?: IndexItem }[]; isPopoverOpen: boolean; selectFieldValue: string; + // dataFilters: string[]; + dataFilters: string; } export default class TransformIndices extends Component { @@ -65,6 +73,8 @@ export default class TransformIndices extends Component { + // const { dataFilters } = this.state; + // //debug + // console.log("to add: " + dataFilter + " existing filters: " + dataFilters); + // dataFilters.push(dataFilter); + // this.setState({ dataFilters }); + // this.closePopover(); + // }; + closePopover = () => this.setState({ isPopoverOpen: false }); render() { const { sourceIndex, sourceIndexError, + sourceIndexFilter, targetIndex, targetIndexError, onChangeSourceIndex, @@ -146,12 +166,9 @@ export default class TransformIndices extends Component} // Should be filter icon per design doc - onClick={this.onButtonClick} - > + this.onButtonClick()} data-test-subj="addFilter"> + Add data filter - + ); return ( @@ -190,42 +207,46 @@ export default class TransformIndices extends Component - {/**/} - {/* */} - {/* */} - {/*

Source index filter

*/} - {/*
*/} - {/*
*/} - {/* */} - {/* */} - {/* - optional*/} - {/* */} - {/* */} - {/*
*/} - - {/**/} - {/* Choose a subset of source index to focus on to optimize for performance and computing resource. You can’t change filter once the*/} - {/* job is created.*/} - {/**/} - - {/* this.onButtonClick()}*/} - {/* data-test-subj="addFilter"*/} - {/* className="globalFilterBar__addButton"*/} - {/* >*/} - {/* + Add data filter*/} - {/* */} - {/* }*/} - {/* isOpen={isPopoverOpen}*/} - {/* closePopover={this.closePopover}*/} - {/*>*/} - {/* */} - {/**/} - {/**/} - {/**/} + + + +

Source index filter

+
+
+ + + - optional + + +
+ + + Choose a subset of source index to focus on to optimize for performance and computing resource. You can’t change filter once the + job is created. + + + {/*{this.state.dataFilters.map((item) => (*/} + {/* {item}*/} + {/*))}*/} + {sourceIndexFilter} + this.onButtonClick()} + data-test-subj="addFilter" + className="globalFilterBar__addButton" + > + + Add data filter + + } + isOpen={isPopoverOpen} + closePopover={this.closePopover} + > + + + + ) => void; onChangeDescription: (value: ChangeEvent) => void; onChangeSourceIndex: (options: EuiComboBoxOptionOption[]) => void; + onChangeSourceIndexFilter: (sourceIndexFilter: string) => void; onChangeTargetIndex: (options: EuiComboBoxOptionOption[]) => void; currentStep: number; hasAggregation: boolean; diff --git a/public/pages/CreateTransform/containers/CreateTransformForm/CreateTransformForm.tsx b/public/pages/CreateTransform/containers/CreateTransformForm/CreateTransformForm.tsx index 33013fe66..4b507a5df 100644 --- a/public/pages/CreateTransform/containers/CreateTransformForm/CreateTransformForm.tsx +++ b/public/pages/CreateTransform/containers/CreateTransformForm/CreateTransformForm.tsx @@ -62,7 +62,9 @@ interface CreateTransformFormState { description: string; sourceIndex: { label: string; value?: IndexItem }[]; sourceIndexError: string; - sourceIndexFilter: {}[]; + //TODO: Uncomment the following line when multiple data filter is supported + // sourceIndexFilter: string[]; + sourceIndexFilter: string; targetIndex: { label: string; value?: IndexItem }[]; targetIndexError: string; @@ -116,7 +118,9 @@ export default class CreateTransformForm extends Component { + let newJSON = this.state.transformJSON; + try { + newJSON.dataSelectionQuery = JSON.parse(sourceIndexFilter); + } catch (err) { + this.context.notifications.toasts.addDanger('Invalid source index filter JSON: "' + sourceIndexFilter + '"'); + } + this.setState({ sourceIndexFilter: sourceIndexFilter, transformJSON: newJSON }); + }; + onChangeTargetIndex = (options: EuiComboBoxOptionOption[]): void => { //Try to get label text from option from the only array element in options, if exists let newJSON = this.state.transformJSON; @@ -399,6 +414,7 @@ export default class CreateTransformForm extends Component Date: Mon, 10 May 2021 15:23:20 -0700 Subject: [PATCH 54/93] Completed basic implementation of Source Index Filter custom editor --- .../IndexFilterPopover/IndexFilterPopover.tsx | 5 +++-- .../JobNameAndIndices/JobNameAndIndices.tsx | 4 ++-- .../TransformIndices/TransformIndices.tsx | 14 +++++++++++++- .../CreateTransformForm/CreateTransformForm.tsx | 4 +++- .../CreateTransformStep4/CreateTransformStep4.tsx | 2 +- 5 files changed, 22 insertions(+), 7 deletions(-) diff --git a/public/pages/CreateTransform/components/IndexFilterPopover/IndexFilterPopover.tsx b/public/pages/CreateTransform/components/IndexFilterPopover/IndexFilterPopover.tsx index 0247ac4ed..bf937eb40 100644 --- a/public/pages/CreateTransform/components/IndexFilterPopover/IndexFilterPopover.tsx +++ b/public/pages/CreateTransform/components/IndexFilterPopover/IndexFilterPopover.tsx @@ -31,16 +31,17 @@ import { FieldItem, IndexItem } from "../../../../../models/interfaces"; interface IndexFilterPopoverProps { sourceIndex: { label: string; value?: IndexItem }[]; fields: FieldItem[]; + sourceIndexFilter: string; onChangeSourceIndexFilter: (sourceIndexFilter: string) => void; closePopover: () => void; } -export default function IndexFilterPopover({ sourceIndex, fields, onChangeSourceIndexFilter, closePopover }: IndexFilterPopoverProps) { +export default function IndexFilterPopover({ sourceIndex, fields, sourceIndexFilter, onChangeSourceIndexFilter, closePopover }: IndexFilterPopoverProps) { const [selectedField, setSelectedField] = useState(""); const [selectedOperator, setSelectedOperator] = useState(""); const [selectedValue, setSelectedValue] = useState(""); const [isCustomEditorOpen, setIsCustomEditorOpen] = useState(false); - const [queryDsl, setQueryDsl] = useState(""); + const [queryDsl, setQueryDsl] = useState(sourceIndexFilter); const onChangeSelectedField = (e: ChangeEvent): void => { setSelectedField(e.target.value); diff --git a/public/pages/CreateTransform/components/JobNameAndIndices/JobNameAndIndices.tsx b/public/pages/CreateTransform/components/JobNameAndIndices/JobNameAndIndices.tsx index a7ea29ceb..a07fb6191 100644 --- a/public/pages/CreateTransform/components/JobNameAndIndices/JobNameAndIndices.tsx +++ b/public/pages/CreateTransform/components/JobNameAndIndices/JobNameAndIndices.tsx @@ -24,7 +24,7 @@ interface JobNameAndIndicesProps { description: string; sourceIndex: { label: string; value?: IndexItem }[]; targetIndex: { label: string; value?: IndexItem }[]; - sourceIndexFilter: {}[]; + sourceIndexFilter: string; onChangeStep: (step: number) => void; } @@ -93,7 +93,7 @@ export default class JobNameAndIndices extends Component
Source index filter
-
{sourceIndexFilter}
+
{sourceIndexFilter == "" ? "-" : sourceIndexFilter}
diff --git a/public/pages/CreateTransform/components/TransformIndices/TransformIndices.tsx b/public/pages/CreateTransform/components/TransformIndices/TransformIndices.tsx index 137f2b770..ed6595fc4 100644 --- a/public/pages/CreateTransform/components/TransformIndices/TransformIndices.tsx +++ b/public/pages/CreateTransform/components/TransformIndices/TransformIndices.tsx @@ -159,6 +159,7 @@ export default class TransformIndices extends Component ); + const clearIndexFilter = () => { + onChangeSourceIndexFilter("{}"); + } + return (
@@ -228,7 +233,14 @@ export default class TransformIndices extends Component (*/} {/* {item}*/} {/*))}*/} - {sourceIndexFilter} + this.onButtonClick()} + onClickAriaLabel="Edit Source Index Filter" + iconOnClick={() => clearIndexFilter()} + iconOnClickAriaLabel="Clear Source Index Filter" + >{sourceIndexFilter} { let newJSON = this.state.transformJSON; try { - newJSON.dataSelectionQuery = JSON.parse(sourceIndexFilter); + newJSON.transform.data_selection_query = JSON.parse(sourceIndexFilter); } catch (err) { this.context.notifications.toasts.addDanger('Invalid source index filter JSON: "' + sourceIndexFilter + '"'); } @@ -336,6 +336,7 @@ export default class CreateTransformForm extends Component => { const { transformId, transformJSON } = this.state; + console.log(transformJSON); this.setState({ submitError: "", isSubmitting: true, hasSubmitted: true }); try { if (!transformId) { @@ -459,6 +460,7 @@ export default class CreateTransformForm extends Component Date: Tue, 11 May 2021 16:26:03 -0700 Subject: [PATCH 55/93] Partially functioning preview API --- .../DefineTransforms/DefineTransforms.tsx | 84 +++++++++++++++++-- .../CreateTransformForm.tsx | 39 +++++++-- .../CreateTransformStep2.tsx | 3 + public/services/TransformService.ts | 7 +- server/clusters/ism/ismPlugin.ts | 8 ++ server/models/interfaces.ts | 4 + server/routes/transforms.ts | 10 +++ server/services/TransformService.ts | 45 +++++++++- 8 files changed, 182 insertions(+), 18 deletions(-) diff --git a/public/pages/CreateTransform/components/DefineTransforms/DefineTransforms.tsx b/public/pages/CreateTransform/components/DefineTransforms/DefineTransforms.tsx index ba988d8b3..e6b6fef73 100644 --- a/public/pages/CreateTransform/components/DefineTransforms/DefineTransforms.tsx +++ b/public/pages/CreateTransform/components/DefineTransforms/DefineTransforms.tsx @@ -13,13 +13,12 @@ * permissions and limitations under the License. */ -import { EuiDataGrid, EuiDataGridColumn, EuiSpacer, EuiText } from "@elastic/eui"; +import { EuiDataGrid, EuiDataGridColumn, EuiEmptyPrompt, EuiSpacer, EuiText } from "@elastic/eui"; import { CoreStart } from "kibana/public"; import React, { useCallback, useState } from "react"; import { ContentPanel } from "../../../../components/ContentPanel"; import { FieldItem, GROUP_TYPES, TransformGroupItem } from "../../../../../models/interfaces"; import { TransformService } from "../../../../services"; -import { getErrorMessage } from "../../../../utils/helpers"; import { isNumericMapping } from "../../utils/helpers"; interface DefineTransformsProps { @@ -28,9 +27,11 @@ interface DefineTransformsProps { transformId: string; sourceIndex: string; fields: FieldItem[]; + selectedGroupField: TransformGroupItem[]; onGroupSelectionChange: (selectedFields: TransformGroupItem[]) => void; selectedAggregations: any; onAggregationSelectionChange: (selectedFields: any) => void; + previewTransform: any[]; } export default function DefineTransforms({ @@ -39,15 +40,35 @@ export default function DefineTransforms({ transfromId, sourceIndex, fields, + selectedGroupField, onGroupSelectionChange, selectedAggregations, onAggregationSelectionChange, + previewTransform, }: DefineTransformsProps) { let columns: EuiDataGridColumn[] = []; + const [previewColumns, setPreviewColumns] = useState([]); + + const updatePreviewColumns = (): void => { + console.log(JSON.stringify(previewTransform)); + if (previewTransform.length) { + for (const [key, value] of Object.entries(previewTransform[0])) { + previewColumns.push({ + id: key, + }); + //debug use + // console.log(key); + } + } + //Debug use + console.log("Preview columns: " + JSON.stringify(previewColumns)); + }; + fields.map((field: FieldItem) => { const isNumeric = isNumericMapping(field.type); const isDate = field.type == "date"; + // TODO: Handle the available options according to column types columns.push({ id: field.label, @@ -63,14 +84,18 @@ export default function DefineTransforms({ { label: "Group by histogram ", onClick: () => { + const targetFieldName = `${field.label}_${GROUP_TYPES.histogram}`; groupSelection.push({ histogram: { source_field: field.label, - target_field: `${field.label}_${GROUP_TYPES.histogram}`, + target_field: targetFieldName, interval: 5, }, }); onGroupSelectionChange(groupSelection); + previewColumns.push({ + id: targetFieldName, + }); }, size: "xs", color: isNumeric ? "text" : "subdued", @@ -111,6 +136,7 @@ export default function DefineTransforms({ sum: { field: field.label }, }; onAggregationSelectionChange(aggSelection); + console.log(Object.entries(selectedAggregations)); }, size: "xs", color: "text", @@ -192,15 +218,15 @@ export default function DefineTransforms({ }); const [loading, setLoading] = useState(true); - const [pagination, setPagination] = useState({ pageIndex: 0, pageSize: 10 }); const [from, setFrom] = useState(0); const [size, setSize] = useState(10); const [sortingColumns, setSortingColumns] = useState([]); const [visibleColumns, setVisibleColumns] = useState(() => columns.map(({ id }) => id).slice(0, 5)); + const [visiblePreviewColumns, setVisiblePreviewColumns] = useState(() => previewColumns.map(({ id }) => id).slice(0, 5)); const [data, setData] = useState([]); const [dataCount, setDataCount] = useState(0); - const [groupSelection, setGroupSelection] = useState([]); + const [groupSelection, setGroupSelection] = useState(selectedGroupField); const [aggSelection, setAggSelection] = useState(selectedAggregations); const fetchData = useCallback(async () => { @@ -220,7 +246,11 @@ export default function DefineTransforms({ React.useEffect(() => { fetchData(); - }, [fetchData]); + }, []); + + React.useEffect(() => { + updatePreviewColumns(); + }, [previewTransform]); const onChangeItemsPerPage = useCallback( (pageSize) => { @@ -262,6 +292,13 @@ export default function DefineTransforms({ return "-"; }; + const renderPreviewCellValue = ({ rowIndex, columnId }) => { + if (!loading && previewTransform.hasOwnProperty(rowIndex)) { + return previewTransform[rowIndex][columnId] ? data[rowIndex][columnId] : "-"; + } + return "-"; + }; + return ( Group selection {/*Debug use*/} - {JSON.stringify(groupSelection)} + {JSON.stringify(selectedGroupField)}

Aggregation

- {JSON.stringify(aggSelection)} + {JSON.stringify(selectedAggregations)} + + +

Transformed fields preview based on sample data

+
+ {previewTransform.length ? ( + No fields selected} + body={

From the table above, select a field you want to transform by clicking the “plus” button next to the field name

} + /> + ) : ( + + )}
); } diff --git a/public/pages/CreateTransform/containers/CreateTransformForm/CreateTransformForm.tsx b/public/pages/CreateTransform/containers/CreateTransformForm/CreateTransformForm.tsx index b9c57d0f6..fae585f92 100644 --- a/public/pages/CreateTransform/containers/CreateTransformForm/CreateTransformForm.tsx +++ b/public/pages/CreateTransform/containers/CreateTransformForm/CreateTransformForm.tsx @@ -20,7 +20,7 @@ import moment from "moment"; import { RollupService, TransformService } from "../../../../services"; import { BREADCRUMBS, ROUTES } from "../../../../utils/constants"; import IndexService from "../../../../services/IndexService"; -import { ManagedCatIndex } from "../../../../../server/models/interfaces"; +import { ManagedCatIndex, PreviewTransformResponse } from "../../../../../server/models/interfaces"; import CreateTransform from "../CreateTransform"; import CreateTransformStep2 from "../CreateTransformStep2"; import { @@ -58,6 +58,7 @@ interface CreateTransformFormState { loadingIndices: boolean; indices: ManagedCatIndex[]; totalIndices: number; + previewTransform: any[]; description: string; sourceIndex: { label: string; value?: IndexItem }[]; @@ -105,6 +106,7 @@ export default class CreateTransformForm extends Component => { + try { + const { transformService } = this.props; + const { selectedGroupField, transformJSON } = this.state; + const previewResponse = await transformService.previewTransform(transformJSON); + if (previewResponse.ok) this.setState({ previewTransform: previewResponse.response.documents }); + + //Debug use + console.log("Documents: " + JSON.stringify(previewResponse.response.documents)); + } catch (err) { + this.context.notifications.toasts.addDanger(getErrorMessage(err, "Could not load preview transform")); + } + }; + _next() { let currentStep = this.state.currentStep; let error = false; @@ -269,12 +285,23 @@ export default class CreateTransformForm extends Component { - this.setState({ selectedGroupField: selectedFields }); + onGroupSelectionChange = (selectedGroupField: GroupItem[]): void => { + let newJSON = this.state.transformJSON; + + if (selectedGroupField.length) newJSON.transform.groups = selectedGroupField; + this.setState({ selectedGroupField, transformJSON: newJSON }); + this.previewTransform(); }; - onAggregationSelectionChange = (selectedFields: any): void => { - this.setState({ selectedAggregations: selectedFields }); + onAggregationSelectionChange = (selectedAggregations: any): void => { + const { transformService } = this.props; + let newJSON = this.state.transformJSON; + + if (selectedAggregations.length) newJSON.transform.aggregations = selectedAggregations; + const previewResponse = transformService.previewTransform(newJSON); + console.log(JSON.stringify(previewResponse)); + this.setState({ selectedAggregations: selectedAggregations, transformJSON: newJSON }); + this.previewTransform(); }; onChangeJobEnabledByDefault = (): void => { @@ -392,6 +419,7 @@ export default class CreateTransformForm extends Component void; selectedAggregations: any; onAggregationSelectionChange: (selectedFields: any) => void; + previewTransform: any[]; } export default class CreateTransformStep2 extends Component { @@ -73,6 +75,7 @@ export default class CreateTransformStep2 extends Component ; }; - previewTransform = async (transform: Map): Promise>> => { + previewTransform = async (transform: any): Promise> => { const url = `..${NODE_API.TRANSFORMS}/_preview`; - return (await this.httpClient.post(url, { body: JSON.stringify(transform) })) as ServerResponse>; + // @ts-ignore + return (await this.httpClient.post(url, { body: JSON.stringify(transform) })) as ServerResponse; }; //Function to search for fields from a source index using GET /${source_index}/_mapping diff --git a/server/clusters/ism/ismPlugin.ts b/server/clusters/ism/ismPlugin.ts index 265f7eebb..8352f15b7 100644 --- a/server/clusters/ism/ismPlugin.ts +++ b/server/clusters/ism/ismPlugin.ts @@ -369,4 +369,12 @@ export default function ismPlugin(Client: any, config: any, components: any) { }, method: "PUT", }); + + ism.previewTransform = ca({ + url: { + fmt: `${API.TRANSFORM_BASE}/_preview`, + }, + needBody: true, + method: "POST", + }); } diff --git a/server/models/interfaces.ts b/server/models/interfaces.ts index 15182efd4..0b8f8f36c 100644 --- a/server/models/interfaces.ts +++ b/server/models/interfaces.ts @@ -106,6 +106,10 @@ export interface PutTransformResponse { transform: { transform: Transform }; } +export interface PreviewTransformResponse { + documents: object[]; +} + export interface IndexUpdateResponse { updatedIndices: number; failures: boolean; diff --git a/server/routes/transforms.ts b/server/routes/transforms.ts index 41c50dd60..964ccd93d 100644 --- a/server/routes/transforms.ts +++ b/server/routes/transforms.ts @@ -113,4 +113,14 @@ export default function (services: NodeServices, router: IRouter) { }, transformService.searchSampleData ); + + router.post( + { + path: `${NODE_API.TRANSFORMS}/_preview`, + validate: { + body: schema.any(), + }, + }, + transformService.previewTransform + ); } diff --git a/server/services/TransformService.ts b/server/services/TransformService.ts index a4003707c..650dcca87 100644 --- a/server/services/TransformService.ts +++ b/server/services/TransformService.ts @@ -15,7 +15,13 @@ import { IClusterClient, IKibanaResponse, KibanaRequest, KibanaResponseFactory, RequestHandlerContext } from "kibana/server"; import { ServerResponse } from "../models/types"; -import { GetTransformsResponse, PutTransformParams, PutTransformResponse, SearchResponse } from "../models/interfaces"; +import { + GetTransformsResponse, + PreviewTransformResponse, + PutTransformParams, + PutTransformResponse, + SearchResponse, +} from "../models/interfaces"; import { DocumentTransform, Transform } from "../../models/interfaces"; import _ from "lodash"; @@ -75,7 +81,10 @@ export default class TransformService { return response.custom({ statusCode: 200, - body: { ok: true, response: { transforms: transforms, totalTransforms: totalTransforms, metadata: explainResponse } }, + body: { + ok: true, + response: { transforms: transforms, totalTransforms: totalTransforms, metadata: explainResponse }, + }, }); } else { return response.custom({ @@ -356,4 +365,36 @@ export default class TransformService { }); } }; + + previewTransform = async ( + context: RequestHandlerContext, + request: KibanaRequest, + response: KibanaResponseFactory + ): Promise>> => { + try { + //Debug use + console.log(JSON.stringify(request.body)); + let params = { + body: JSON.stringify(request.body), + }; + const { callAsCurrentUser: callWithRequest } = this.esDriver.asScoped(request); + const previewResponse: PreviewTransformResponse = await callWithRequest("ism.previewTransform", params); + return response.custom({ + statusCode: 200, + body: { + ok: true, + response: previewResponse, + }, + }); + } catch (err) { + console.error("Index Management - TransformService - previewTransform", err); + return response.custom({ + statusCode: 200, + body: { + ok: false, + error: err.message, + }, + }); + } + }; } From 7f11e501c2eb71f4d876113fba3f599051fb9af9 Mon Sep 17 00:00:00 2001 From: Annie Date: Tue, 11 May 2021 16:30:40 -0700 Subject: [PATCH 56/93] Update empty state condition --- .../DefineTransforms/DefineTransforms.tsx | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/public/pages/CreateTransform/components/DefineTransforms/DefineTransforms.tsx b/public/pages/CreateTransform/components/DefineTransforms/DefineTransforms.tsx index e6b6fef73..bd9b07f51 100644 --- a/public/pages/CreateTransform/components/DefineTransforms/DefineTransforms.tsx +++ b/public/pages/CreateTransform/components/DefineTransforms/DefineTransforms.tsx @@ -19,6 +19,7 @@ import React, { useCallback, useState } from "react"; import { ContentPanel } from "../../../../components/ContentPanel"; import { FieldItem, GROUP_TYPES, TransformGroupItem } from "../../../../../models/interfaces"; import { TransformService } from "../../../../services"; +import { getErrorMessage } from "../../../../utils/helpers"; import { isNumericMapping } from "../../utils/helpers"; interface DefineTransformsProps { @@ -56,9 +57,8 @@ export default function DefineTransforms({ for (const [key, value] of Object.entries(previewTransform[0])) { previewColumns.push({ id: key, + displayAsText: key, }); - //debug use - // console.log(key); } } //Debug use @@ -369,11 +369,6 @@ export default function DefineTransforms({

Transformed fields preview based on sample data

{previewTransform.length ? ( - No fields selected} - body={

From the table above, select a field you want to transform by clicking the “plus” button next to the field name

} - /> - ) : ( + ) : ( + No fields selected} + body={

From the table above, select a field you want to transform by clicking the “plus” button next to the field name

} + /> )} ); From a5c1261ef604cd3ac70795c441d971247d5046ce Mon Sep 17 00:00:00 2001 From: Annie Date: Tue, 11 May 2021 16:38:10 -0700 Subject: [PATCH 57/93] Clean up previewColumns when updating --- .../DefineTransforms/DefineTransforms.tsx | 48 +++++++++++-------- .../CreateTransformForm.tsx | 3 -- 2 files changed, 28 insertions(+), 23 deletions(-) diff --git a/public/pages/CreateTransform/components/DefineTransforms/DefineTransforms.tsx b/public/pages/CreateTransform/components/DefineTransforms/DefineTransforms.tsx index bd9b07f51..c533023c3 100644 --- a/public/pages/CreateTransform/components/DefineTransforms/DefineTransforms.tsx +++ b/public/pages/CreateTransform/components/DefineTransforms/DefineTransforms.tsx @@ -52,14 +52,22 @@ export default function DefineTransforms({ const [previewColumns, setPreviewColumns] = useState([]); const updatePreviewColumns = (): void => { - console.log(JSON.stringify(previewTransform)); + setPreviewColumns([]); if (previewTransform.length) { for (const [key, value] of Object.entries(previewTransform[0])) { previewColumns.push({ id: key, displayAsText: key, + actions: { + showHide: false, + showMoveLeft: false, + showMoveRight: false, + showSortAsc: false, + showSortDesc: false, + }, }); } + setVisiblePreviewColumns(previewColumns.map(({ id }) => id).slice(0, 5)); } //Debug use console.log("Preview columns: " + JSON.stringify(previewColumns)); @@ -293,7 +301,7 @@ export default function DefineTransforms({ }; const renderPreviewCellValue = ({ rowIndex, columnId }) => { - if (!loading && previewTransform.hasOwnProperty(rowIndex)) { + if (previewTransform.hasOwnProperty(rowIndex)) { return previewTransform[rowIndex][columnId] ? data[rowIndex][columnId] : "-"; } return "-"; @@ -354,17 +362,6 @@ export default function DefineTransforms({ }} /> - -

Group selection

-
- {/*Debug use*/} - {JSON.stringify(selectedGroupField)} - - -

Aggregation

-
- {JSON.stringify(selectedAggregations)} -

Transformed fields preview based on sample data

@@ -375,13 +372,13 @@ export default function DefineTransforms({ columnVisibility={{ visibleColumns: visiblePreviewColumns, setVisibleColumns: setVisiblePreviewColumns }} rowCount={previewColumns.length} renderCellValue={renderPreviewCellValue} - sorting={{ columns: sortingColumns, onSort }} - pagination={{ - ...pagination, - pageSizeOptions: [5, 10, 20, 50], - onChangeItemsPerPage: onChangeItemsPerPage, - onChangePage: onChangePage, - }} + // sorting={{ columns: sortingColumns, onSort }} + // pagination={{ + // ...pagination, + // pageSizeOptions: [5, 10, 20, 50], + // onChangeItemsPerPage: onChangeItemsPerPage, + // onChangePage: onChangePage, + // }} toolbarVisibility={{ showColumnSelector: true, showStyleSelector: false, @@ -395,6 +392,17 @@ export default function DefineTransforms({ body={

From the table above, select a field you want to transform by clicking the “plus” button next to the field name

} /> )} + + +

Group selection

+
+ {/*Debug use*/} + {JSON.stringify(selectedGroupField)} + + +

Aggregation

+
+ {JSON.stringify(selectedAggregations)} ); } diff --git a/public/pages/CreateTransform/containers/CreateTransformForm/CreateTransformForm.tsx b/public/pages/CreateTransform/containers/CreateTransformForm/CreateTransformForm.tsx index fae585f92..97f0a29ad 100644 --- a/public/pages/CreateTransform/containers/CreateTransformForm/CreateTransformForm.tsx +++ b/public/pages/CreateTransform/containers/CreateTransformForm/CreateTransformForm.tsx @@ -294,12 +294,9 @@ export default class CreateTransformForm extends Component { - const { transformService } = this.props; let newJSON = this.state.transformJSON; if (selectedAggregations.length) newJSON.transform.aggregations = selectedAggregations; - const previewResponse = transformService.previewTransform(newJSON); - console.log(JSON.stringify(previewResponse)); this.setState({ selectedAggregations: selectedAggregations, transformJSON: newJSON }); this.previewTransform(); }; From 9dfc1f5c3ec4702a0375d573db5f0e3e227689af Mon Sep 17 00:00:00 2001 From: Annie Date: Tue, 11 May 2021 16:40:51 -0700 Subject: [PATCH 58/93] Update CreateTransformForm.tsx --- .../CreateTransformForm/CreateTransformForm.tsx | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/public/pages/CreateTransform/containers/CreateTransformForm/CreateTransformForm.tsx b/public/pages/CreateTransform/containers/CreateTransformForm/CreateTransformForm.tsx index 97f0a29ad..1f81d892d 100644 --- a/public/pages/CreateTransform/containers/CreateTransformForm/CreateTransformForm.tsx +++ b/public/pages/CreateTransform/containers/CreateTransformForm/CreateTransformForm.tsx @@ -167,11 +167,11 @@ export default class CreateTransformForm extends Component => { + previewTransform = async (transform: any): Promise => { try { const { transformService } = this.props; - const { selectedGroupField, transformJSON } = this.state; - const previewResponse = await transformService.previewTransform(transformJSON); + const { selectedGroupField } = this.state; + const previewResponse = await transformService.previewTransform(transform); if (previewResponse.ok) this.setState({ previewTransform: previewResponse.response.documents }); //Debug use @@ -285,20 +285,20 @@ export default class CreateTransformForm extends Component { + onGroupSelectionChange = async (selectedGroupField: GroupItem[]): Promise => { let newJSON = this.state.transformJSON; if (selectedGroupField.length) newJSON.transform.groups = selectedGroupField; this.setState({ selectedGroupField, transformJSON: newJSON }); - this.previewTransform(); + await this.previewTransform(newJSON); }; - onAggregationSelectionChange = (selectedAggregations: any): void => { + onAggregationSelectionChange = async (selectedAggregations: any): Promise => { let newJSON = this.state.transformJSON; if (selectedAggregations.length) newJSON.transform.aggregations = selectedAggregations; this.setState({ selectedAggregations: selectedAggregations, transformJSON: newJSON }); - this.previewTransform(); + await this.previewTransform(newJSON); }; onChangeJobEnabledByDefault = (): void => { From b6069f93f8a7be13e19eeff199289a2ab09f56d4 Mon Sep 17 00:00:00 2001 From: Annie Date: Tue, 11 May 2021 16:51:56 -0700 Subject: [PATCH 59/93] Preview columns showing up --- .../DefineTransforms/DefineTransforms.tsx | 17 ++++++++++------- .../CreateTransformForm/CreateTransformForm.tsx | 1 - 2 files changed, 10 insertions(+), 8 deletions(-) diff --git a/public/pages/CreateTransform/components/DefineTransforms/DefineTransforms.tsx b/public/pages/CreateTransform/components/DefineTransforms/DefineTransforms.tsx index c533023c3..4db618995 100644 --- a/public/pages/CreateTransform/components/DefineTransforms/DefineTransforms.tsx +++ b/public/pages/CreateTransform/components/DefineTransforms/DefineTransforms.tsx @@ -52,10 +52,12 @@ export default function DefineTransforms({ const [previewColumns, setPreviewColumns] = useState([]); const updatePreviewColumns = (): void => { - setPreviewColumns([]); + // setPreviewColumns([{id: "ex-col"}]); + // setPreviewColumns([]); if (previewTransform.length) { + let tempCol: EuiDataGridColumn[] = []; for (const [key, value] of Object.entries(previewTransform[0])) { - previewColumns.push({ + tempCol.push({ id: key, displayAsText: key, actions: { @@ -67,7 +69,8 @@ export default function DefineTransforms({ }, }); } - setVisiblePreviewColumns(previewColumns.map(({ id }) => id).slice(0, 5)); + setPreviewColumns(tempCol); + setVisiblePreviewColumns(() => tempCol.map(({ id }) => id).slice(0, 5)); } //Debug use console.log("Preview columns: " + JSON.stringify(previewColumns)); @@ -301,9 +304,9 @@ export default function DefineTransforms({ }; const renderPreviewCellValue = ({ rowIndex, columnId }) => { - if (previewTransform.hasOwnProperty(rowIndex)) { - return previewTransform[rowIndex][columnId] ? data[rowIndex][columnId] : "-"; - } + // if (previewTransform.hasOwnProperty(rowIndex)) { + // return previewTransform[rowIndex][columnId] ? data[rowIndex][columnId] : "-"; + // } return "-"; }; @@ -370,7 +373,7 @@ export default function DefineTransforms({ aria-label="Preview transforms" columns={previewColumns} columnVisibility={{ visibleColumns: visiblePreviewColumns, setVisibleColumns: setVisiblePreviewColumns }} - rowCount={previewColumns.length} + rowCount={previewTransform.length} renderCellValue={renderPreviewCellValue} // sorting={{ columns: sortingColumns, onSort }} // pagination={{ diff --git a/public/pages/CreateTransform/containers/CreateTransformForm/CreateTransformForm.tsx b/public/pages/CreateTransform/containers/CreateTransformForm/CreateTransformForm.tsx index 1f81d892d..252fcf21c 100644 --- a/public/pages/CreateTransform/containers/CreateTransformForm/CreateTransformForm.tsx +++ b/public/pages/CreateTransform/containers/CreateTransformForm/CreateTransformForm.tsx @@ -170,7 +170,6 @@ export default class CreateTransformForm extends Component => { try { const { transformService } = this.props; - const { selectedGroupField } = this.state; const previewResponse = await transformService.previewTransform(transform); if (previewResponse.ok) this.setState({ previewTransform: previewResponse.response.documents }); From eaf6e06ebf6ba3527014f2cd13000d785de84834 Mon Sep 17 00:00:00 2001 From: Annie Date: Tue, 11 May 2021 17:02:52 -0700 Subject: [PATCH 60/93] Able to show preview --- .../components/DefineTransforms/DefineTransforms.tsx | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/public/pages/CreateTransform/components/DefineTransforms/DefineTransforms.tsx b/public/pages/CreateTransform/components/DefineTransforms/DefineTransforms.tsx index 4db618995..190069f62 100644 --- a/public/pages/CreateTransform/components/DefineTransforms/DefineTransforms.tsx +++ b/public/pages/CreateTransform/components/DefineTransforms/DefineTransforms.tsx @@ -304,9 +304,9 @@ export default function DefineTransforms({ }; const renderPreviewCellValue = ({ rowIndex, columnId }) => { - // if (previewTransform.hasOwnProperty(rowIndex)) { - // return previewTransform[rowIndex][columnId] ? data[rowIndex][columnId] : "-"; - // } + if (previewTransform.hasOwnProperty(rowIndex)) { + return previewTransform[rowIndex][columnId] ? previewTransform[rowIndex][columnId] : "-"; + } return "-"; }; From e4f32dd8a9c8cf139a4d68c6265d1f58e6144663 Mon Sep 17 00:00:00 2001 From: Annie Date: Tue, 11 May 2021 17:07:48 -0700 Subject: [PATCH 61/93] Set maximum of sample data to 200 --- .../DefineTransforms/DefineTransforms.tsx | 13 +------------ 1 file changed, 1 insertion(+), 12 deletions(-) diff --git a/public/pages/CreateTransform/components/DefineTransforms/DefineTransforms.tsx b/public/pages/CreateTransform/components/DefineTransforms/DefineTransforms.tsx index 190069f62..d2f556007 100644 --- a/public/pages/CreateTransform/components/DefineTransforms/DefineTransforms.tsx +++ b/public/pages/CreateTransform/components/DefineTransforms/DefineTransforms.tsx @@ -348,7 +348,7 @@ export default function DefineTransforms({ aria-label="Define transforms" columns={columns} columnVisibility={{ visibleColumns, setVisibleColumns }} - rowCount={dataCount} + rowCount={Math.min(dataCount, 200)} renderCellValue={renderCellValue} sorting={{ columns: sortingColumns, onSort }} pagination={{ @@ -395,17 +395,6 @@ export default function DefineTransforms({ body={

From the table above, select a field you want to transform by clicking the “plus” button next to the field name

} /> )} - - -

Group selection

-
- {/*Debug use*/} - {JSON.stringify(selectedGroupField)} - - -

Aggregation

-
- {JSON.stringify(selectedAggregations)} ); } From 1d9fb25c7e8fe2411e61d564f0514a272ef7e4d1 Mon Sep 17 00:00:00 2001 From: Annie Date: Tue, 11 May 2021 17:16:19 -0700 Subject: [PATCH 62/93] Add pagination to preview --- .../DefineTransforms/DefineTransforms.tsx | 44 ++++++++++++++----- 1 file changed, 32 insertions(+), 12 deletions(-) diff --git a/public/pages/CreateTransform/components/DefineTransforms/DefineTransforms.tsx b/public/pages/CreateTransform/components/DefineTransforms/DefineTransforms.tsx index d2f556007..f1e9c69c8 100644 --- a/public/pages/CreateTransform/components/DefineTransforms/DefineTransforms.tsx +++ b/public/pages/CreateTransform/components/DefineTransforms/DefineTransforms.tsx @@ -13,7 +13,7 @@ * permissions and limitations under the License. */ -import { EuiDataGrid, EuiDataGridColumn, EuiEmptyPrompt, EuiSpacer, EuiText } from "@elastic/eui"; +import { EuiDataGrid, EuiDataGridColumn, EuiEmptyPrompt, EuiPanel, EuiSpacer, EuiText } from "@elastic/eui"; import { CoreStart } from "kibana/public"; import React, { useCallback, useState } from "react"; import { ContentPanel } from "../../../../components/ContentPanel"; @@ -59,7 +59,6 @@ export default function DefineTransforms({ for (const [key, value] of Object.entries(previewTransform[0])) { tempCol.push({ id: key, - displayAsText: key, actions: { showHide: false, showMoveLeft: false, @@ -230,6 +229,7 @@ export default function DefineTransforms({ const [loading, setLoading] = useState(true); const [pagination, setPagination] = useState({ pageIndex: 0, pageSize: 10 }); + const [previewPagination, setPreviewPagination] = useState({ pageIndex: 0, pageSize: 10 }); const [from, setFrom] = useState(0); const [size, setSize] = useState(10); const [sortingColumns, setSortingColumns] = useState([]); @@ -283,6 +283,23 @@ export default function DefineTransforms({ [setPagination] ); + const onChangePreviewPerPage = useCallback( + (pageSize) => { + setPreviewPagination((previewPagination) => ({ + ...previewPagination, + pageSize, + pageIndex: 0, + })); + }, + [setPreviewPagination] + ); + const onChangePreviewPage = useCallback( + (pageIndex) => { + setPreviewPagination((previewPagination) => ({ ...previewPagination, pageIndex })); + }, + [setPreviewPagination] + ); + const onSort = useCallback( (sortingColumns) => { setSortingColumns(sortingColumns); @@ -368,6 +385,7 @@ export default function DefineTransforms({

Transformed fields preview based on sample data

+ {previewTransform.length ? ( ) : ( - No fields selected} - body={

From the table above, select a field you want to transform by clicking the “plus” button next to the field name

} - /> + + No fields selected} + body={

From the table above, select a field you want to transform by clicking the “plus” button next to the field name

} + /> +
)} ); From ed05b25d079ac2e1139e69053f7be61221f64052 Mon Sep 17 00:00:00 2001 From: Annie Date: Tue, 11 May 2021 17:19:33 -0700 Subject: [PATCH 63/93] Add error toast to preview --- .../containers/CreateTransformForm/CreateTransformForm.tsx | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/public/pages/CreateTransform/containers/CreateTransformForm/CreateTransformForm.tsx b/public/pages/CreateTransform/containers/CreateTransformForm/CreateTransformForm.tsx index 252fcf21c..27aa2f625 100644 --- a/public/pages/CreateTransform/containers/CreateTransformForm/CreateTransformForm.tsx +++ b/public/pages/CreateTransform/containers/CreateTransformForm/CreateTransformForm.tsx @@ -172,9 +172,7 @@ export default class CreateTransformForm extends Component Date: Tue, 11 May 2021 17:21:07 -0700 Subject: [PATCH 64/93] Add preview table --- .../DefineTransforms/DefineTransforms.tsx | 116 +++++++++++++++--- .../CreateTransformForm.tsx | 33 ++++- .../CreateTransformStep2.tsx | 3 + public/services/TransformService.ts | 7 +- server/clusters/ism/ismPlugin.ts | 8 ++ server/models/interfaces.ts | 4 + server/routes/transforms.ts | 10 ++ server/services/TransformService.ts | 45 ++++++- 8 files changed, 202 insertions(+), 24 deletions(-) diff --git a/public/pages/CreateTransform/components/DefineTransforms/DefineTransforms.tsx b/public/pages/CreateTransform/components/DefineTransforms/DefineTransforms.tsx index ba988d8b3..f1e9c69c8 100644 --- a/public/pages/CreateTransform/components/DefineTransforms/DefineTransforms.tsx +++ b/public/pages/CreateTransform/components/DefineTransforms/DefineTransforms.tsx @@ -13,7 +13,7 @@ * permissions and limitations under the License. */ -import { EuiDataGrid, EuiDataGridColumn, EuiSpacer, EuiText } from "@elastic/eui"; +import { EuiDataGrid, EuiDataGridColumn, EuiEmptyPrompt, EuiPanel, EuiSpacer, EuiText } from "@elastic/eui"; import { CoreStart } from "kibana/public"; import React, { useCallback, useState } from "react"; import { ContentPanel } from "../../../../components/ContentPanel"; @@ -28,9 +28,11 @@ interface DefineTransformsProps { transformId: string; sourceIndex: string; fields: FieldItem[]; + selectedGroupField: TransformGroupItem[]; onGroupSelectionChange: (selectedFields: TransformGroupItem[]) => void; selectedAggregations: any; onAggregationSelectionChange: (selectedFields: any) => void; + previewTransform: any[]; } export default function DefineTransforms({ @@ -39,15 +41,44 @@ export default function DefineTransforms({ transfromId, sourceIndex, fields, + selectedGroupField, onGroupSelectionChange, selectedAggregations, onAggregationSelectionChange, + previewTransform, }: DefineTransformsProps) { let columns: EuiDataGridColumn[] = []; + const [previewColumns, setPreviewColumns] = useState([]); + + const updatePreviewColumns = (): void => { + // setPreviewColumns([{id: "ex-col"}]); + // setPreviewColumns([]); + if (previewTransform.length) { + let tempCol: EuiDataGridColumn[] = []; + for (const [key, value] of Object.entries(previewTransform[0])) { + tempCol.push({ + id: key, + actions: { + showHide: false, + showMoveLeft: false, + showMoveRight: false, + showSortAsc: false, + showSortDesc: false, + }, + }); + } + setPreviewColumns(tempCol); + setVisiblePreviewColumns(() => tempCol.map(({ id }) => id).slice(0, 5)); + } + //Debug use + console.log("Preview columns: " + JSON.stringify(previewColumns)); + }; + fields.map((field: FieldItem) => { const isNumeric = isNumericMapping(field.type); const isDate = field.type == "date"; + // TODO: Handle the available options according to column types columns.push({ id: field.label, @@ -63,14 +94,18 @@ export default function DefineTransforms({ { label: "Group by histogram ", onClick: () => { + const targetFieldName = `${field.label}_${GROUP_TYPES.histogram}`; groupSelection.push({ histogram: { source_field: field.label, - target_field: `${field.label}_${GROUP_TYPES.histogram}`, + target_field: targetFieldName, interval: 5, }, }); onGroupSelectionChange(groupSelection); + previewColumns.push({ + id: targetFieldName, + }); }, size: "xs", color: isNumeric ? "text" : "subdued", @@ -111,6 +146,7 @@ export default function DefineTransforms({ sum: { field: field.label }, }; onAggregationSelectionChange(aggSelection); + console.log(Object.entries(selectedAggregations)); }, size: "xs", color: "text", @@ -192,15 +228,16 @@ export default function DefineTransforms({ }); const [loading, setLoading] = useState(true); - const [pagination, setPagination] = useState({ pageIndex: 0, pageSize: 10 }); + const [previewPagination, setPreviewPagination] = useState({ pageIndex: 0, pageSize: 10 }); const [from, setFrom] = useState(0); const [size, setSize] = useState(10); const [sortingColumns, setSortingColumns] = useState([]); const [visibleColumns, setVisibleColumns] = useState(() => columns.map(({ id }) => id).slice(0, 5)); + const [visiblePreviewColumns, setVisiblePreviewColumns] = useState(() => previewColumns.map(({ id }) => id).slice(0, 5)); const [data, setData] = useState([]); const [dataCount, setDataCount] = useState(0); - const [groupSelection, setGroupSelection] = useState([]); + const [groupSelection, setGroupSelection] = useState(selectedGroupField); const [aggSelection, setAggSelection] = useState(selectedAggregations); const fetchData = useCallback(async () => { @@ -220,7 +257,11 @@ export default function DefineTransforms({ React.useEffect(() => { fetchData(); - }, [fetchData]); + }, []); + + React.useEffect(() => { + updatePreviewColumns(); + }, [previewTransform]); const onChangeItemsPerPage = useCallback( (pageSize) => { @@ -242,6 +283,23 @@ export default function DefineTransforms({ [setPagination] ); + const onChangePreviewPerPage = useCallback( + (pageSize) => { + setPreviewPagination((previewPagination) => ({ + ...previewPagination, + pageSize, + pageIndex: 0, + })); + }, + [setPreviewPagination] + ); + const onChangePreviewPage = useCallback( + (pageIndex) => { + setPreviewPagination((previewPagination) => ({ ...previewPagination, pageIndex })); + }, + [setPreviewPagination] + ); + const onSort = useCallback( (sortingColumns) => { setSortingColumns(sortingColumns); @@ -262,6 +320,13 @@ export default function DefineTransforms({ return "-"; }; + const renderPreviewCellValue = ({ rowIndex, columnId }) => { + if (previewTransform.hasOwnProperty(rowIndex)) { + return previewTransform[rowIndex][columnId] ? previewTransform[rowIndex][columnId] : "-"; + } + return "-"; + }; + return ( -

Group selection

+

Transformed fields preview based on sample data

- {/*Debug use*/} - {JSON.stringify(groupSelection)} - - -

Aggregation

-
- {JSON.stringify(aggSelection)} + + {previewTransform.length ? ( + + ) : ( + + No fields selected} + body={

From the table above, select a field you want to transform by clicking the “plus” button next to the field name

} + /> +
+ )}
); } diff --git a/public/pages/CreateTransform/containers/CreateTransformForm/CreateTransformForm.tsx b/public/pages/CreateTransform/containers/CreateTransformForm/CreateTransformForm.tsx index b9c57d0f6..27aa2f625 100644 --- a/public/pages/CreateTransform/containers/CreateTransformForm/CreateTransformForm.tsx +++ b/public/pages/CreateTransform/containers/CreateTransformForm/CreateTransformForm.tsx @@ -20,7 +20,7 @@ import moment from "moment"; import { RollupService, TransformService } from "../../../../services"; import { BREADCRUMBS, ROUTES } from "../../../../utils/constants"; import IndexService from "../../../../services/IndexService"; -import { ManagedCatIndex } from "../../../../../server/models/interfaces"; +import { ManagedCatIndex, PreviewTransformResponse } from "../../../../../server/models/interfaces"; import CreateTransform from "../CreateTransform"; import CreateTransformStep2 from "../CreateTransformStep2"; import { @@ -58,6 +58,7 @@ interface CreateTransformFormState { loadingIndices: boolean; indices: ManagedCatIndex[]; totalIndices: number; + previewTransform: any[]; description: string; sourceIndex: { label: string; value?: IndexItem }[]; @@ -105,6 +106,7 @@ export default class CreateTransformForm extends Component => { + try { + const { transformService } = this.props; + const previewResponse = await transformService.previewTransform(transform); + if (previewResponse.ok) this.setState({ previewTransform: previewResponse.response.documents }); + else this.context.notifications.toasts.addDanger(`Could not preview transform: ${previewResponse.error}`); + } catch (err) { + this.context.notifications.toasts.addDanger(getErrorMessage(err, "Could not load preview transform")); + } + }; + _next() { let currentStep = this.state.currentStep; let error = false; @@ -269,12 +282,20 @@ export default class CreateTransformForm extends Component { - this.setState({ selectedGroupField: selectedFields }); + onGroupSelectionChange = async (selectedGroupField: GroupItem[]): Promise => { + let newJSON = this.state.transformJSON; + + if (selectedGroupField.length) newJSON.transform.groups = selectedGroupField; + this.setState({ selectedGroupField, transformJSON: newJSON }); + await this.previewTransform(newJSON); }; - onAggregationSelectionChange = (selectedFields: any): void => { - this.setState({ selectedAggregations: selectedFields }); + onAggregationSelectionChange = async (selectedAggregations: any): Promise => { + let newJSON = this.state.transformJSON; + + if (selectedAggregations.length) newJSON.transform.aggregations = selectedAggregations; + this.setState({ selectedAggregations: selectedAggregations, transformJSON: newJSON }); + await this.previewTransform(newJSON); }; onChangeJobEnabledByDefault = (): void => { @@ -392,6 +413,7 @@ export default class CreateTransformForm extends Component void; selectedAggregations: any; onAggregationSelectionChange: (selectedFields: any) => void; + previewTransform: any[]; } export default class CreateTransformStep2 extends Component { @@ -73,6 +75,7 @@ export default class CreateTransformStep2 extends Component ; }; - previewTransform = async (transform: Map): Promise>> => { + previewTransform = async (transform: any): Promise> => { const url = `..${NODE_API.TRANSFORMS}/_preview`; - return (await this.httpClient.post(url, { body: JSON.stringify(transform) })) as ServerResponse>; + // @ts-ignore + return (await this.httpClient.post(url, { body: JSON.stringify(transform) })) as ServerResponse; }; //Function to search for fields from a source index using GET /${source_index}/_mapping diff --git a/server/clusters/ism/ismPlugin.ts b/server/clusters/ism/ismPlugin.ts index 265f7eebb..8352f15b7 100644 --- a/server/clusters/ism/ismPlugin.ts +++ b/server/clusters/ism/ismPlugin.ts @@ -369,4 +369,12 @@ export default function ismPlugin(Client: any, config: any, components: any) { }, method: "PUT", }); + + ism.previewTransform = ca({ + url: { + fmt: `${API.TRANSFORM_BASE}/_preview`, + }, + needBody: true, + method: "POST", + }); } diff --git a/server/models/interfaces.ts b/server/models/interfaces.ts index 15182efd4..0b8f8f36c 100644 --- a/server/models/interfaces.ts +++ b/server/models/interfaces.ts @@ -106,6 +106,10 @@ export interface PutTransformResponse { transform: { transform: Transform }; } +export interface PreviewTransformResponse { + documents: object[]; +} + export interface IndexUpdateResponse { updatedIndices: number; failures: boolean; diff --git a/server/routes/transforms.ts b/server/routes/transforms.ts index 41c50dd60..964ccd93d 100644 --- a/server/routes/transforms.ts +++ b/server/routes/transforms.ts @@ -113,4 +113,14 @@ export default function (services: NodeServices, router: IRouter) { }, transformService.searchSampleData ); + + router.post( + { + path: `${NODE_API.TRANSFORMS}/_preview`, + validate: { + body: schema.any(), + }, + }, + transformService.previewTransform + ); } diff --git a/server/services/TransformService.ts b/server/services/TransformService.ts index a4003707c..650dcca87 100644 --- a/server/services/TransformService.ts +++ b/server/services/TransformService.ts @@ -15,7 +15,13 @@ import { IClusterClient, IKibanaResponse, KibanaRequest, KibanaResponseFactory, RequestHandlerContext } from "kibana/server"; import { ServerResponse } from "../models/types"; -import { GetTransformsResponse, PutTransformParams, PutTransformResponse, SearchResponse } from "../models/interfaces"; +import { + GetTransformsResponse, + PreviewTransformResponse, + PutTransformParams, + PutTransformResponse, + SearchResponse, +} from "../models/interfaces"; import { DocumentTransform, Transform } from "../../models/interfaces"; import _ from "lodash"; @@ -75,7 +81,10 @@ export default class TransformService { return response.custom({ statusCode: 200, - body: { ok: true, response: { transforms: transforms, totalTransforms: totalTransforms, metadata: explainResponse } }, + body: { + ok: true, + response: { transforms: transforms, totalTransforms: totalTransforms, metadata: explainResponse }, + }, }); } else { return response.custom({ @@ -356,4 +365,36 @@ export default class TransformService { }); } }; + + previewTransform = async ( + context: RequestHandlerContext, + request: KibanaRequest, + response: KibanaResponseFactory + ): Promise>> => { + try { + //Debug use + console.log(JSON.stringify(request.body)); + let params = { + body: JSON.stringify(request.body), + }; + const { callAsCurrentUser: callWithRequest } = this.esDriver.asScoped(request); + const previewResponse: PreviewTransformResponse = await callWithRequest("ism.previewTransform", params); + return response.custom({ + statusCode: 200, + body: { + ok: true, + response: previewResponse, + }, + }); + } catch (err) { + console.error("Index Management - TransformService - previewTransform", err); + return response.custom({ + statusCode: 200, + body: { + ok: false, + error: err.message, + }, + }); + } + }; } From b70bfbed960ed7baea47db8820ddaf3b262476c9 Mon Sep 17 00:00:00 2001 From: Annie Date: Tue, 11 May 2021 17:29:51 -0700 Subject: [PATCH 65/93] Bug fix for aggregations --- .../components/DefineTransforms/DefineTransforms.tsx | 2 +- .../containers/CreateTransformForm/CreateTransformForm.tsx | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/public/pages/CreateTransform/components/DefineTransforms/DefineTransforms.tsx b/public/pages/CreateTransform/components/DefineTransforms/DefineTransforms.tsx index f1e9c69c8..77f1d7348 100644 --- a/public/pages/CreateTransform/components/DefineTransforms/DefineTransforms.tsx +++ b/public/pages/CreateTransform/components/DefineTransforms/DefineTransforms.tsx @@ -188,7 +188,7 @@ export default function DefineTransforms({ label: "Aggregate by count ", onClick: () => { aggSelection[`count_${field.label}`] = { - count: { field: field.label }, + value_count: { field: field.label }, }; onAggregationSelectionChange(aggSelection); }, diff --git a/public/pages/CreateTransform/containers/CreateTransformForm/CreateTransformForm.tsx b/public/pages/CreateTransform/containers/CreateTransformForm/CreateTransformForm.tsx index 27aa2f625..f45a44f7c 100644 --- a/public/pages/CreateTransform/containers/CreateTransformForm/CreateTransformForm.tsx +++ b/public/pages/CreateTransform/containers/CreateTransformForm/CreateTransformForm.tsx @@ -293,7 +293,7 @@ export default class CreateTransformForm extends Component => { let newJSON = this.state.transformJSON; - if (selectedAggregations.length) newJSON.transform.aggregations = selectedAggregations; + newJSON.transform.aggregations = selectedAggregations; this.setState({ selectedAggregations: selectedAggregations, transformJSON: newJSON }); await this.previewTransform(newJSON); }; From ff06e289c673e689d3db3d4541cbe85e2c2d9357 Mon Sep 17 00:00:00 2001 From: Eric Lobdell Date: Tue, 11 May 2021 20:33:05 -0700 Subject: [PATCH 66/93] Rearranged data fields on Transform details page, formatted Enable date --- .../GeneralInformation/GeneralInformation.tsx | 23 +++++++-- .../TransformStatus/TransformStatus.tsx | 22 ++++----- .../Transforms/TransformDetails.tsx | 37 ++++++++++----- .../Transforms/TransformSettings.tsx | 47 +++++++++++++++++-- 4 files changed, 97 insertions(+), 32 deletions(-) diff --git a/public/pages/Transforms/components/GeneralInformation/GeneralInformation.tsx b/public/pages/Transforms/components/GeneralInformation/GeneralInformation.tsx index 5742547f3..68227c402 100644 --- a/public/pages/Transforms/components/GeneralInformation/GeneralInformation.tsx +++ b/public/pages/Transforms/components/GeneralInformation/GeneralInformation.tsx @@ -23,8 +23,10 @@ interface GeneralInformationProps { description: string; sourceIndex: string; targetIndex: string; + sourceIndexFilter: string; scheduledText: string; pageSize: number; + enabledAt: number; updatedAt: number; onEdit: () => void; } @@ -35,15 +37,30 @@ export default class GenerationInformation extends Component { - - -
Indexed time (ms)
-
- {metadata == null || metadata.transform_metadata == null ? "-" : metadata.transform_metadata.stats.index_time_in_millis} -
-
-
- -
Document processed
@@ -69,9 +59,9 @@ export default class TransformStatus extends Component {
-
Search time (ms)
+
Indexed time (ms)
- {metadata == null || metadata.transform_metadata == null ? "-" : metadata.transform_metadata.stats.search_time_in_millis} + {metadata == null || metadata.transform_metadata == null ? "-" : metadata.transform_metadata.stats.index_time_in_millis}
@@ -83,6 +73,14 @@ export default class TransformStatus extends Component {
{metadata == null || metadata.transform_metadata == null ? "-" : metadata.transform_metadata.stats.pages_processed}
+ + +
Search time (ms)
+
+ {metadata == null || metadata.transform_metadata == null ? "-" : metadata.transform_metadata.stats.search_time_in_millis} +
+
+
diff --git a/public/pages/Transforms/containers/Transforms/TransformDetails.tsx b/public/pages/Transforms/containers/Transforms/TransformDetails.tsx index b4e60e3e1..86b953f9b 100644 --- a/public/pages/Transforms/containers/Transforms/TransformDetails.tsx +++ b/public/pages/Transforms/containers/Transforms/TransformDetails.tsx @@ -61,7 +61,8 @@ interface TransformDetailsState { transformJson: any; sourceIndex: string; targetIndex: string; - aggregationsShown: MetricItem[]; + sourceIndexFilter: string; + aggregationsShown: any; groupsShown: DimensionItem[]; metadata: TransformMetadata | undefined; interval: number; @@ -87,7 +88,8 @@ export default class TransformDetails extends Component { - const sourceArray = groups.slice(1, groups.length); - if (sourceArray.length == 0) return []; + if (groups.length == 0) return []; // @ts-ignore - return sourceArray.map((group: RollupDimensionItem) => { + return groups.map((group: RollupDimensionItem) => { let sequence = groups.indexOf(group); switch (true) { case group.date_histogram != null: @@ -195,6 +197,12 @@ export default class TransformDetails extends Component { + if (aggregations.size == 0) return {}; + // @ts-ignore + return aggregations; + } + render() { const { id, @@ -203,9 +211,12 @@ export default class TransformDetails extends Component{id} - - {enabled ? {"Enabled on " + updatedAt} : Disabled} - @@ -323,6 +331,7 @@ export default class TransformDetails extends Component - - + {isModalOpen && ( diff --git a/public/pages/Transforms/containers/Transforms/TransformSettings.tsx b/public/pages/Transforms/containers/Transforms/TransformSettings.tsx index 99a804d16..6f76918cb 100644 --- a/public/pages/Transforms/containers/Transforms/TransformSettings.tsx +++ b/public/pages/Transforms/containers/Transforms/TransformSettings.tsx @@ -14,15 +14,18 @@ */ import React, { Component } from "react"; -import { EuiSpacer, EuiText, EuiAccordion } from "@elastic/eui"; +import { EuiSpacer, EuiText, EuiAccordion, EuiFlexGrid, EuiFlexItem } from "@elastic/eui"; // @ts-ignore import { htmlIdGenerator } from "@elastic/eui/lib/services"; import { ContentPanel } from "../../../../components/ContentPanel"; import { TransformService } from "../../../../services"; +import { DimensionItem, MetricItem } from "../../../../../models/interfaces"; interface TransformSettingsProps { transformService: TransformService; transformJson: Map; + groupsShown: DimensionItem[]; + aggregationsShown: any; } interface TransformSettingsState {} @@ -34,13 +37,47 @@ export default class TransformSettings extends Component { + console.log(groupsShown); + return (groupsShown.map((group) => { + return( + + +
Group by {group.aggregationMethod}
+
{group.field.label}
+
+
+ ) + })); + }; + + const aggItems = () => { + console.log(aggregationsShown); + return (Object.keys(aggregationsShown).map((key) => { + return( + + +
{key}
+
{JSON.stringify(aggregationsShown[key])}
+
+
+ ) + })); + } + return (
- - -

Groups

-
+ + + {groupItems()} + {aggItems()} + +
From 2a363a34d11f75c56e590261998371fbaab81c0f Mon Sep 17 00:00:00 2001 From: Annie Date: Wed, 12 May 2021 13:23:39 -0700 Subject: [PATCH 67/93] Decouple preview transform table from define transform --- .../DefineTransforms/DefineTransforms.tsx | 93 +------------------ .../PreviewEmptyPrompt/PreviewEmptyPrompt.tsx | 13 +++ .../components/PreviewEmptyPrompt/index.ts | 18 ++++ .../PreviewTransform/PreviewTransform.tsx | 85 +++++++++++++++++ .../components/PreviewTransform/index.ts | 18 ++++ .../ReviewDefinition/ReviewDefinition.tsx | 47 +++++----- 6 files changed, 160 insertions(+), 114 deletions(-) create mode 100644 public/pages/CreateTransform/components/PreviewEmptyPrompt/PreviewEmptyPrompt.tsx create mode 100644 public/pages/CreateTransform/components/PreviewEmptyPrompt/index.ts create mode 100644 public/pages/CreateTransform/components/PreviewTransform/PreviewTransform.tsx create mode 100644 public/pages/CreateTransform/components/PreviewTransform/index.ts diff --git a/public/pages/CreateTransform/components/DefineTransforms/DefineTransforms.tsx b/public/pages/CreateTransform/components/DefineTransforms/DefineTransforms.tsx index 77f1d7348..5bb91e180 100644 --- a/public/pages/CreateTransform/components/DefineTransforms/DefineTransforms.tsx +++ b/public/pages/CreateTransform/components/DefineTransforms/DefineTransforms.tsx @@ -13,7 +13,7 @@ * permissions and limitations under the License. */ -import { EuiDataGrid, EuiDataGridColumn, EuiEmptyPrompt, EuiPanel, EuiSpacer, EuiText } from "@elastic/eui"; +import { EuiDataGrid, EuiDataGridColumn, EuiSpacer, EuiText } from "@elastic/eui"; import { CoreStart } from "kibana/public"; import React, { useCallback, useState } from "react"; import { ContentPanel } from "../../../../components/ContentPanel"; @@ -21,6 +21,7 @@ import { FieldItem, GROUP_TYPES, TransformGroupItem } from "../../../../../model import { TransformService } from "../../../../services"; import { getErrorMessage } from "../../../../utils/helpers"; import { isNumericMapping } from "../../utils/helpers"; +import PreviewTransform from "../PreviewTransform"; interface DefineTransformsProps { transformService: TransformService; @@ -49,32 +50,6 @@ export default function DefineTransforms({ }: DefineTransformsProps) { let columns: EuiDataGridColumn[] = []; - const [previewColumns, setPreviewColumns] = useState([]); - - const updatePreviewColumns = (): void => { - // setPreviewColumns([{id: "ex-col"}]); - // setPreviewColumns([]); - if (previewTransform.length) { - let tempCol: EuiDataGridColumn[] = []; - for (const [key, value] of Object.entries(previewTransform[0])) { - tempCol.push({ - id: key, - actions: { - showHide: false, - showMoveLeft: false, - showMoveRight: false, - showSortAsc: false, - showSortDesc: false, - }, - }); - } - setPreviewColumns(tempCol); - setVisiblePreviewColumns(() => tempCol.map(({ id }) => id).slice(0, 5)); - } - //Debug use - console.log("Preview columns: " + JSON.stringify(previewColumns)); - }; - fields.map((field: FieldItem) => { const isNumeric = isNumericMapping(field.type); const isDate = field.type == "date"; @@ -103,9 +78,6 @@ export default function DefineTransforms({ }, }); onGroupSelectionChange(groupSelection); - previewColumns.push({ - id: targetFieldName, - }); }, size: "xs", color: isNumeric ? "text" : "subdued", @@ -146,7 +118,6 @@ export default function DefineTransforms({ sum: { field: field.label }, }; onAggregationSelectionChange(aggSelection); - console.log(Object.entries(selectedAggregations)); }, size: "xs", color: "text", @@ -229,12 +200,10 @@ export default function DefineTransforms({ const [loading, setLoading] = useState(true); const [pagination, setPagination] = useState({ pageIndex: 0, pageSize: 10 }); - const [previewPagination, setPreviewPagination] = useState({ pageIndex: 0, pageSize: 10 }); const [from, setFrom] = useState(0); const [size, setSize] = useState(10); const [sortingColumns, setSortingColumns] = useState([]); const [visibleColumns, setVisibleColumns] = useState(() => columns.map(({ id }) => id).slice(0, 5)); - const [visiblePreviewColumns, setVisiblePreviewColumns] = useState(() => previewColumns.map(({ id }) => id).slice(0, 5)); const [data, setData] = useState([]); const [dataCount, setDataCount] = useState(0); const [groupSelection, setGroupSelection] = useState(selectedGroupField); @@ -259,10 +228,6 @@ export default function DefineTransforms({ fetchData(); }, []); - React.useEffect(() => { - updatePreviewColumns(); - }, [previewTransform]); - const onChangeItemsPerPage = useCallback( (pageSize) => { setPagination((pagination) => ({ @@ -283,23 +248,6 @@ export default function DefineTransforms({ [setPagination] ); - const onChangePreviewPerPage = useCallback( - (pageSize) => { - setPreviewPagination((previewPagination) => ({ - ...previewPagination, - pageSize, - pageIndex: 0, - })); - }, - [setPreviewPagination] - ); - const onChangePreviewPage = useCallback( - (pageIndex) => { - setPreviewPagination((previewPagination) => ({ ...previewPagination, pageIndex })); - }, - [setPreviewPagination] - ); - const onSort = useCallback( (sortingColumns) => { setSortingColumns(sortingColumns); @@ -320,13 +268,6 @@ export default function DefineTransforms({ return "-"; }; - const renderPreviewCellValue = ({ rowIndex, columnId }) => { - if (previewTransform.hasOwnProperty(rowIndex)) { - return previewTransform[rowIndex][columnId] ? previewTransform[rowIndex][columnId] : "-"; - } - return "-"; - }; - return ( Transformed fields preview based on sample data - {previewTransform.length ? ( - - ) : ( - - No fields selected} - body={

From the table above, select a field you want to transform by clicking the “plus” button next to the field name

} - /> -
- )} +
); } diff --git a/public/pages/CreateTransform/components/PreviewEmptyPrompt/PreviewEmptyPrompt.tsx b/public/pages/CreateTransform/components/PreviewEmptyPrompt/PreviewEmptyPrompt.tsx new file mode 100644 index 000000000..18616e374 --- /dev/null +++ b/public/pages/CreateTransform/components/PreviewEmptyPrompt/PreviewEmptyPrompt.tsx @@ -0,0 +1,13 @@ +import { EuiEmptyPrompt, EuiPanel } from "@elastic/eui"; +import React from "react"; + +export default function PreviewEmptyPrompt() { + return ( + + No fields selected} + body={

From the table above, select a field you want to transform by clicking the “plus” button next to the field name

} + /> +
+ ); +} diff --git a/public/pages/CreateTransform/components/PreviewEmptyPrompt/index.ts b/public/pages/CreateTransform/components/PreviewEmptyPrompt/index.ts new file mode 100644 index 000000000..a393f37d6 --- /dev/null +++ b/public/pages/CreateTransform/components/PreviewEmptyPrompt/index.ts @@ -0,0 +1,18 @@ +/* + * Copyright 2021 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"). + * You may not use this file except in compliance with the License. + * A copy of the License is located at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * or in the "license" file accompanying this file. This file is distributed + * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either + * express or implied. See the License for the specific language governing + * permissions and limitations under the License. + */ + +import PreviewEmptyPrompt from "./PreviewEmptyPrompt"; + +export default PreviewEmptyPrompt; diff --git a/public/pages/CreateTransform/components/PreviewTransform/PreviewTransform.tsx b/public/pages/CreateTransform/components/PreviewTransform/PreviewTransform.tsx new file mode 100644 index 000000000..db1755663 --- /dev/null +++ b/public/pages/CreateTransform/components/PreviewTransform/PreviewTransform.tsx @@ -0,0 +1,85 @@ +import React, { useCallback, useState } from "react"; +import { EuiDataGrid, EuiDataGridColumn } from "@elastic/eui"; +import PreviewEmptyPrompt from "../PreviewEmptyPrompt"; + +interface PreviewTransformsProps { + previewTransform: any[]; +} + +export default function PreviewTransforms({ previewTransform }: PreviewTransformsProps) { + const [previewColumns, setPreviewColumns] = useState([]); + const [visiblePreviewColumns, setVisiblePreviewColumns] = useState(() => previewColumns.map(({ id }) => id).slice(0, 5)); + const [previewPagination, setPreviewPagination] = useState({ pageIndex: 0, pageSize: 10 }); + + const renderPreviewCellValue = ({ rowIndex, columnId }) => { + if (previewTransform.hasOwnProperty(rowIndex)) { + return previewTransform[rowIndex][columnId] ? previewTransform[rowIndex][columnId] : "-"; + } + return "-"; + }; + const onChangePreviewPerPage = useCallback( + (pageSize) => { + setPreviewPagination((previewPagination) => ({ + ...previewPagination, + pageSize, + pageIndex: 0, + })); + }, + [setPreviewPagination] + ); + + const onChangePreviewPage = useCallback( + (pageIndex) => { + setPreviewPagination((previewPagination) => ({ ...previewPagination, pageIndex })); + }, + [setPreviewPagination] + ); + + const updatePreviewColumns = (): void => { + if (previewTransform.length) { + let tempCol: EuiDataGridColumn[] = []; + for (const [key, value] of Object.entries(previewTransform[0])) { + tempCol.push({ + id: key, + actions: { + showHide: false, + showMoveLeft: false, + showMoveRight: false, + showSortAsc: false, + showSortDesc: false, + }, + }); + } + setPreviewColumns(tempCol); + setVisiblePreviewColumns(() => tempCol.map(({ id }) => id).slice(0, 5)); + } + }; + + React.useEffect(() => { + updatePreviewColumns(); + }, [previewTransform]); + + return previewTransform.length ? ( + + ) : ( + + ); +} diff --git a/public/pages/CreateTransform/components/PreviewTransform/index.ts b/public/pages/CreateTransform/components/PreviewTransform/index.ts new file mode 100644 index 000000000..e08a3b2d8 --- /dev/null +++ b/public/pages/CreateTransform/components/PreviewTransform/index.ts @@ -0,0 +1,18 @@ +/* + * Copyright 2021 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"). + * You may not use this file except in compliance with the License. + * A copy of the License is located at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * or in the "license" file accompanying this file. This file is distributed + * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either + * express or implied. See the License for the specific language governing + * permissions and limitations under the License. + */ + +import PreviewTransform from "./PreviewTransform"; + +export default PreviewTransform; diff --git a/public/pages/CreateTransform/components/ReviewDefinition/ReviewDefinition.tsx b/public/pages/CreateTransform/components/ReviewDefinition/ReviewDefinition.tsx index 5b013ed93..a93bc9496 100644 --- a/public/pages/CreateTransform/components/ReviewDefinition/ReviewDefinition.tsx +++ b/public/pages/CreateTransform/components/ReviewDefinition/ReviewDefinition.tsx @@ -14,13 +14,13 @@ */ import React, { Component } from "react"; -import { EuiFlexGrid, EuiSpacer, EuiFlexItem, EuiText } from "@elastic/eui"; +import { EuiFlexGrid, EuiSpacer, EuiFlexItem, EuiText, EuiAccordion } from "@elastic/eui"; import { ContentPanel, ContentPanelActions } from "../../../../components/ContentPanel"; import { ModalConsumer } from "../../../../components/Modal"; import { TransformGroupItem } from "../../../../../models/interfaces"; +import DefineTransforms from "../DefineTransforms"; interface ReviewDefinitionProps { - selectedGroupField: TransformGroupItem[]; selectedAggregations: any; onChangeStep: (step: number) => void; @@ -32,35 +32,33 @@ export default class ReviewDefinition extends Component { } render() { - const { selectedGroupField, - selectedAggregations, - onChangeStep } = this.props; + const { selectedGroupField, selectedAggregations, onChangeStep } = this.props; const groupItems = () => { - return (selectedGroupField.map((group) => { - return( + return selectedGroupField.map((group) => { + return (
Group
{JSON.stringify(group)}
- ) - })); + ); + }); }; const aggItems = () => { - return (Object.keys(selectedAggregations).map((key) => { - return( - - -
{key}
-
{JSON.stringify(selectedAggregations[key])}
-
-
- ) - })); - } + return Object.keys(selectedAggregations).map((key) => { + return ( + + +
{key}
+
{JSON.stringify(selectedAggregations[key])}
+
+
+ ); + }); + }; return ( {
- {groupItems()} - {aggItems()} + {groupItems()} + {aggItems()} + +
-
- ) + ); } } From 9de68eb68a2a6d9aa2648b516e048e5299f742d3 Mon Sep 17 00:00:00 2001 From: Eric Lobdell Date: Thu, 13 May 2021 15:37:59 -0700 Subject: [PATCH 68/93] Added Indices section to Edit Transform page --- .../Transforms/components/Indices/Indices.tsx | 55 +++++++++++++++++++ .../Transforms/components/Indices/index.ts | 18 ++++++ .../containers/Transforms/EditTransform.tsx | 19 +++++++ .../Transforms/TransformDetails.tsx | 4 +- 4 files changed, 95 insertions(+), 1 deletion(-) create mode 100644 public/pages/Transforms/components/Indices/Indices.tsx create mode 100644 public/pages/Transforms/components/Indices/index.ts diff --git a/public/pages/Transforms/components/Indices/Indices.tsx b/public/pages/Transforms/components/Indices/Indices.tsx new file mode 100644 index 000000000..763a3fcb2 --- /dev/null +++ b/public/pages/Transforms/components/Indices/Indices.tsx @@ -0,0 +1,55 @@ +/* + * Copyright 2021 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"). + * You may not use this file except in compliance with the License. + * A copy of the License is located at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * or in the "license" file accompanying this file. This file is distributed + * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either + * express or implied. See the License for the specific language governing + * permissions and limitations under the License. + */ + +import React, { Component } from "react"; +import { EuiFlexGrid, EuiSpacer, EuiFlexItem, EuiText } from "@elastic/eui"; +import { ContentPanel } from "../../../../components/ContentPanel"; + +interface IndicesProps { + sourceIndex: string; + targetIndex: string; + sourceIndexFilter: string; +} + +export default class Indices extends Component { + constructor(props: IndicesProps) { + super(props); + } + + render() { + const { sourceIndex, targetIndex, sourceIndexFilter } = this.props; + + return ( + +
+ +
Source index
+
{sourceIndex}
+
+ + +
Source index filter
+
{sourceIndexFilter}
+
+ + +
Target index
+
{targetIndex}
+
+
+
+ ) + } +} diff --git a/public/pages/Transforms/components/Indices/index.ts b/public/pages/Transforms/components/Indices/index.ts new file mode 100644 index 000000000..10506cb9f --- /dev/null +++ b/public/pages/Transforms/components/Indices/index.ts @@ -0,0 +1,18 @@ +/* + * Copyright 2021 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"). + * You may not use this file except in compliance with the License. + * A copy of the License is located at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * or in the "license" file accompanying this file. This file is distributed + * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either + * express or implied. See the License for the specific language governing + * permissions and limitations under the License. + */ + +import Indices from "./Indices"; + +export default Indices; diff --git a/public/pages/Transforms/containers/Transforms/EditTransform.tsx b/public/pages/Transforms/containers/Transforms/EditTransform.tsx index 9ba1eb804..bcc8fd166 100644 --- a/public/pages/Transforms/containers/Transforms/EditTransform.tsx +++ b/public/pages/Transforms/containers/Transforms/EditTransform.tsx @@ -24,6 +24,7 @@ import { getErrorMessage } from "../../../../utils/helpers"; import { EuiFlexItem, EuiFlexGroup, EuiButton, EuiTitle, EuiSpacer, EuiButtonEmpty } from "@elastic/eui"; import ConfigureTransform from "../../components/ConfigureTransform"; import Schedule from "../../components/Schedule"; +import Indices from "../../components/Indices"; import moment from "moment"; import { Transform } from "../../../../../models/interfaces"; @@ -37,6 +38,9 @@ interface EditTransformState { seqNo: number | null; primaryTerm: number | null; description: string; + sourceIndex: string; + targetIndex: string; + sourceIndexFilter: string; pageSize: number; enabled: boolean; transformJSON: any; @@ -62,6 +66,9 @@ export default class EditTransform extends Component + + Actions From 7c9114cb1007d522ec6ea0b2d2a856d9ff5dda03 Mon Sep 17 00:00:00 2001 From: Annie Date: Fri, 14 May 2021 13:39:47 -0700 Subject: [PATCH 69/93] Showing preview table on details page --- .../DefineTransforms/DefineTransforms.tsx | 2 + .../CreateTransformStep4.tsx | 6 +- .../Transforms/TransformDetails.tsx | 7 +- .../Transforms/TransformSettings.tsx | 117 ++++++++++++++---- 4 files changed, 99 insertions(+), 33 deletions(-) diff --git a/public/pages/CreateTransform/components/DefineTransforms/DefineTransforms.tsx b/public/pages/CreateTransform/components/DefineTransforms/DefineTransforms.tsx index 5bb91e180..52f8256bf 100644 --- a/public/pages/CreateTransform/components/DefineTransforms/DefineTransforms.tsx +++ b/public/pages/CreateTransform/components/DefineTransforms/DefineTransforms.tsx @@ -34,6 +34,7 @@ interface DefineTransformsProps { selectedAggregations: any; onAggregationSelectionChange: (selectedFields: any) => void; previewTransform: any[]; + isReadOnly: boolean; } export default function DefineTransforms({ @@ -47,6 +48,7 @@ export default function DefineTransforms({ selectedAggregations, onAggregationSelectionChange, previewTransform, + isReadOnly, }: DefineTransformsProps) { let columns: EuiDataGridColumn[] = []; diff --git a/public/pages/CreateTransform/containers/CreateTransformStep4/CreateTransformStep4.tsx b/public/pages/CreateTransform/containers/CreateTransformStep4/CreateTransformStep4.tsx index 80d37c433..bd5015755 100644 --- a/public/pages/CreateTransform/containers/CreateTransformStep4/CreateTransformStep4.tsx +++ b/public/pages/CreateTransform/containers/CreateTransformStep4/CreateTransformStep4.tsx @@ -18,11 +18,7 @@ import { EuiSpacer, EuiTitle, EuiFlexGroup, EuiFlexItem, EuiComboBoxOptionOption import { RouteComponentProps } from "react-router-dom"; import { TransformService } from "../../../../services"; import { BREADCRUMBS, ROUTES } from "../../../../utils/constants"; -import { DimensionItem, - IndexItem, - MetricItem, - TransformAggItem, - TransformGroupItem,} from "../../../../../models/interfaces"; +import { IndexItem, TransformGroupItem } from "../../../../../models/interfaces"; import CreateTransformSteps from "../../components/CreateTransformSteps"; import JobNameAndIndices from "../../components/JobNameAndIndices"; import ReviewDefinition from "../../components/ReviewDefinition"; diff --git a/public/pages/Transforms/containers/Transforms/TransformDetails.tsx b/public/pages/Transforms/containers/Transforms/TransformDetails.tsx index 86b953f9b..c9eae0393 100644 --- a/public/pages/Transforms/containers/Transforms/TransformDetails.tsx +++ b/public/pages/Transforms/containers/Transforms/TransformDetails.tsx @@ -201,7 +201,7 @@ export default class TransformDetails extends Component + /> {isModalOpen && ( diff --git a/public/pages/Transforms/containers/Transforms/TransformSettings.tsx b/public/pages/Transforms/containers/Transforms/TransformSettings.tsx index 6f76918cb..02488e1b0 100644 --- a/public/pages/Transforms/containers/Transforms/TransformSettings.tsx +++ b/public/pages/Transforms/containers/Transforms/TransformSettings.tsx @@ -19,69 +19,134 @@ import { EuiSpacer, EuiText, EuiAccordion, EuiFlexGrid, EuiFlexItem } from "@ela import { htmlIdGenerator } from "@elastic/eui/lib/services"; import { ContentPanel } from "../../../../components/ContentPanel"; import { TransformService } from "../../../../services"; -import { DimensionItem, MetricItem } from "../../../../../models/interfaces"; +import { DimensionItem, FieldItem } from "../../../../../models/interfaces"; +import DefineTransforms from "../../../CreateTransform/components/DefineTransforms"; +import { compareFieldItem, parseFieldOptions } from "../../../CreateTransform/utils/helpers"; +import { getErrorMessage } from "../../../../utils/helpers"; +import PreviewTransforms from "../../../CreateTransform/components/PreviewTransform"; interface TransformSettingsProps { transformService: TransformService; - transformJson: Map; + transformId: string; + sourceIndex: string; + transformJson: any; groupsShown: DimensionItem[]; aggregationsShown: any; } -interface TransformSettingsState {} +interface TransformSettingsState { + previewTransform: any[]; +} export default class TransformSettings extends Component { constructor(props: TransformSettingsProps) { super(props); - this.state = {}; + this.state = { + previewTransform: [], + }; } + // getMappings = async (srcIndex: string): Promise => { + // if (!srcIndex.length) return; + // try { + // const { rollupService } = this.props; + // const response = await rollupService.getMappings(srcIndex); + // if (response.ok) { + // let allMappings: FieldItem[][] = []; + // const mappings = response.response; + // //Push mappings array to allMappings 2D array first + // for (let index in mappings) { + // allMappings.push(parseFieldOptions("", mappings[index].mappings.properties)); + // } + // //Find intersect from all mappings + // const fields = allMappings.reduce((mappingA, mappingB) => + // mappingA.filter((itemA) => mappingB.some((itemB) => compareFieldItem(itemA, itemB))) + // ); + // this.setState({ mappings, fields, allMappings }); + // } else { + // this.context.notifications.toasts.addDanger(`Could not load fields: ${response.error}`); + // } + // } catch (err) { + // this.context.notifications.toasts.addDanger(getErrorMessage(err, "Could not load fields")); + // } + // }; + + previewTransform = async (transform: any): Promise => { + try { + const { transformService } = this.props; + const previewResponse = await transformService.previewTransform(transform); + if (previewResponse.ok) this.setState({ previewTransform: previewResponse.response.documents }); + else this.context.notifications.toasts.addDanger(`Could not preview transform: ${previewResponse.error}`); + } catch (err) { + this.context.notifications.toasts.addDanger(getErrorMessage(err, "Could not load preview transform")); + } + }; + + componentDidMount = async (): Promise => { + await this.previewTransform({ transform: this.props.transformJson.transform }); + }; + render() { - const { groupsShown, - aggregationsShown, - } = this.props; + const { groupsShown, aggregationsShown } = this.props; const groupItems = () => { console.log(groupsShown); - return (groupsShown.map((group) => { - return( + return groupsShown.map((group) => { + return (
Group by {group.aggregationMethod}
{group.field.label}
- ) - })); + ); + }); }; const aggItems = () => { console.log(aggregationsShown); - return (Object.keys(aggregationsShown).map((key) => { - return( - - -
{key}
-
{JSON.stringify(aggregationsShown[key])}
-
-
- ) - })); - } + return Object.keys(aggregationsShown).map((key) => { + return ( + + +
{key}
+
{JSON.stringify(aggregationsShown[key])}
+
+
+ ); + }); + }; return (
- {groupItems()} - {aggItems()} + {groupItems()} + {aggItems()}
- // TODO: Use the source data preview table from create workflow // TODO: Use the transformed preview table from create workflow + + +

Preview result based on sample data

+
+ {/*// TODO: Use the source data preview table from create workflow */} + {/*()}*/} + {/* selectedAggregations={{}}*/} + {/* onAggregationSelectionChange={()=> ()}*/} + {/* previewTransform={[]}*/} + {/*/>*/} + {/*// TODO: Use the transformed preview table from create workflow*/} +
@@ -89,7 +154,7 @@ export default class TransformSettings extends Component { - const response = await this.props.transformService.previewTransform(this.props.transformJson); + const response = await this.previewTransform({ transform: this.props.transformJson.transform }); console.log(response); console.log("tada"); }; From 65ed41ea5faf655eca74e19c2e02ff2d622da585 Mon Sep 17 00:00:00 2001 From: Annie Date: Fri, 14 May 2021 13:52:26 -0700 Subject: [PATCH 70/93] Support percentile on preview table --- .../PreviewTransform/PreviewTransform.tsx | 7 ++++++- .../containers/Transforms/TransformSettings.tsx | 13 +++++++------ 2 files changed, 13 insertions(+), 7 deletions(-) diff --git a/public/pages/CreateTransform/components/PreviewTransform/PreviewTransform.tsx b/public/pages/CreateTransform/components/PreviewTransform/PreviewTransform.tsx index db1755663..5aedcfa3f 100644 --- a/public/pages/CreateTransform/components/PreviewTransform/PreviewTransform.tsx +++ b/public/pages/CreateTransform/components/PreviewTransform/PreviewTransform.tsx @@ -13,7 +13,12 @@ export default function PreviewTransforms({ previewTransform }: PreviewTransform const renderPreviewCellValue = ({ rowIndex, columnId }) => { if (previewTransform.hasOwnProperty(rowIndex)) { - return previewTransform[rowIndex][columnId] ? previewTransform[rowIndex][columnId] : "-"; + if (previewTransform[rowIndex][columnId]) { + // Case for percentile + return typeof previewTransform[rowIndex][columnId] !== ("string" || "number") + ? JSON.stringify(previewTransform[rowIndex][columnId]) + : previewTransform[rowIndex][columnId]; + } } return "-"; }; diff --git a/public/pages/Transforms/containers/Transforms/TransformSettings.tsx b/public/pages/Transforms/containers/Transforms/TransformSettings.tsx index 02488e1b0..f88f9aeca 100644 --- a/public/pages/Transforms/containers/Transforms/TransformSettings.tsx +++ b/public/pages/Transforms/containers/Transforms/TransformSettings.tsx @@ -128,11 +128,9 @@ export default class TransformSettings extends Component
-
- - -

Preview result based on sample data

-
+
+ + {/*// TODO: Use the source data preview table from create workflow */} {/* ()}*/} {/* previewTransform={[]}*/} {/*/>*/} - {/*// TODO: Use the transformed preview table from create workflow*/} + +

Preview result based on sample data

+
+
From 40f61a94e1cce041bffccd4763dc08afc4605287 Mon Sep 17 00:00:00 2001 From: Annie Date: Fri, 14 May 2021 14:22:35 -0700 Subject: [PATCH 71/93] Update define transform options menu --- .../DefineTransforms/DefineTransforms.tsx | 277 +++++++------- .../PreviewEmptyPrompt/PreviewEmptyPrompt.tsx | 23 +- .../PreviewTransform/PreviewTransform.tsx | 19 +- .../ReviewDefinition/ReviewDefinition.tsx | 1 - .../Panels/HistogramPanel/HistogramPanel.tsx | 65 ++++ .../Panels/HistogramPanel/index.ts | 18 + .../PercentilePanel/PercentilePanel.tsx | 113 ++++++ .../Panels/PercentilePanel/index.ts | 18 + .../TransformOptions/TranformOptions.tsx | 345 ++++++++++++++++++ .../components/TransformOptions/index.ts | 18 + 10 files changed, 757 insertions(+), 140 deletions(-) create mode 100644 public/pages/CreateTransform/components/TransformOptions/Panels/HistogramPanel/HistogramPanel.tsx create mode 100644 public/pages/CreateTransform/components/TransformOptions/Panels/HistogramPanel/index.ts create mode 100644 public/pages/CreateTransform/components/TransformOptions/Panels/PercentilePanel/PercentilePanel.tsx create mode 100644 public/pages/CreateTransform/components/TransformOptions/Panels/PercentilePanel/index.ts create mode 100644 public/pages/CreateTransform/components/TransformOptions/TranformOptions.tsx create mode 100644 public/pages/CreateTransform/components/TransformOptions/index.ts diff --git a/public/pages/CreateTransform/components/DefineTransforms/DefineTransforms.tsx b/public/pages/CreateTransform/components/DefineTransforms/DefineTransforms.tsx index 52f8256bf..965907bee 100644 --- a/public/pages/CreateTransform/components/DefineTransforms/DefineTransforms.tsx +++ b/public/pages/CreateTransform/components/DefineTransforms/DefineTransforms.tsx @@ -17,11 +17,11 @@ import { EuiDataGrid, EuiDataGridColumn, EuiSpacer, EuiText } from "@elastic/eui import { CoreStart } from "kibana/public"; import React, { useCallback, useState } from "react"; import { ContentPanel } from "../../../../components/ContentPanel"; -import { FieldItem, GROUP_TYPES, TransformGroupItem } from "../../../../../models/interfaces"; +import { FieldItem, TransformGroupItem } from "../../../../../models/interfaces"; import { TransformService } from "../../../../services"; import { getErrorMessage } from "../../../../utils/helpers"; -import { isNumericMapping } from "../../utils/helpers"; import PreviewTransform from "../PreviewTransform"; +import TransformOptions from "../TransformOptions/TranformOptions"; interface DefineTransformsProps { transformService: TransformService; @@ -53,12 +53,20 @@ export default function DefineTransforms({ let columns: EuiDataGridColumn[] = []; fields.map((field: FieldItem) => { - const isNumeric = isNumericMapping(field.type); - const isDate = field.type == "date"; - + const isText = field.type == "text"; // TODO: Handle the available options according to column types columns.push({ id: field.label, + display: !isText && ( + + ), displayAsText: field.label + " type: " + field.type, schema: field.type, actions: { @@ -67,135 +75,135 @@ export default function DefineTransforms({ showMoveRight: false, showSortAsc: false, showSortDesc: false, - additional: [ - { - label: "Group by histogram ", - onClick: () => { - const targetFieldName = `${field.label}_${GROUP_TYPES.histogram}`; - groupSelection.push({ - histogram: { - source_field: field.label, - target_field: targetFieldName, - interval: 5, - }, - }); - onGroupSelectionChange(groupSelection); - }, - size: "xs", - color: isNumeric ? "text" : "subdued", - }, - { - label: "Group by date histogram ", - onClick: () => { - groupSelection.push({ - date_histogram: { - source_field: field.label, - target_field: `${field.label}_${GROUP_TYPES.dateHistogram}`, - calendar_interval: "1d", - }, - }); - onGroupSelectionChange(groupSelection); - }, - size: "xs", - color: isDate ? "text" : "subdued", - }, - { - label: "Group by terms ", - onClick: () => { - groupSelection.push({ - terms: { - source_field: field.label, - target_field: `${field.label}_${GROUP_TYPES.terms}`, - }, - }); - onGroupSelectionChange(groupSelection); - }, - size: "xs", - color: "text", - }, - { - label: "Aggregate by sum ", - onClick: () => { - aggSelection[`sum_${field.label}`] = { - sum: { field: field.label }, - }; - onAggregationSelectionChange(aggSelection); - }, - size: "xs", - color: "text", - }, - { - label: "Aggregate by max ", - onClick: () => { - aggSelection[`max_${field.label}`] = { - max: { field: field.label }, - }; - onAggregationSelectionChange(aggSelection); - }, - size: "xs", - color: "text", - }, - { - label: "Aggregate by min ", - onClick: () => { - aggSelection[`min_${field.label}`] = { - min: { field: field.label }, - }; - onAggregationSelectionChange(aggSelection); - }, - size: "xs", - color: "text", - }, - { - label: "Aggregate by avg ", - onClick: () => { - aggSelection[`avg_${field.label}`] = { - avg: { field: field.label }, - }; - onAggregationSelectionChange(aggSelection); - }, - size: "xs", - color: "text", - }, - { - label: "Aggregate by count ", - onClick: () => { - aggSelection[`count_${field.label}`] = { - value_count: { field: field.label }, - }; - onAggregationSelectionChange(aggSelection); - }, - size: "xs", - color: "text", - }, - { - label: "Aggregate by percentile", - onClick: () => { - aggSelection[`percentiles_${field.label}`] = { - percentiles: { field: field.label, percents: [1, 5, 25, 99] }, - }; - onAggregationSelectionChange(aggSelection); - }, - size: "xs", - color: "text", - }, - { - label: "Aggregate by scripted metrics ", - onClick: () => { - aggSelection[`scripted_metric_${field.label}`] = { - scripted_metric: { - init_script: "", - map_script: "", - combine_script: "", - reduce_script: "", - }, - }; - onAggregationSelectionChange(aggSelection); - }, - size: "xs", - color: "text", - }, - ], + // additional: [ + // { + // label: "Group by histogram ", + // onClick: () => { + // const targetFieldName = `${field.label}_${GROUP_TYPES.histogram}`; + // groupSelection.push({ + // histogram: { + // source_field: field.label, + // target_field: targetFieldName, + // interval: 5, + // }, + // }); + // onGroupSelectionChange(groupSelection); + // }, + // size: "xs", + // color: isNumeric ? "text" : "subdued", + // }, + // { + // label: "Group by date histogram ", + // onClick: () => { + // groupSelection.push({ + // date_histogram: { + // source_field: field.label, + // target_field: `${field.label}_${GROUP_TYPES.dateHistogram}`, + // calendar_interval: "1d", + // }, + // }); + // onGroupSelectionChange(groupSelection); + // }, + // size: "xs", + // color: isDate ? "text" : "subdued", + // }, + // { + // label: "Group by terms ", + // onClick: () => { + // groupSelection.push({ + // terms: { + // source_field: field.label, + // target_field: `${field.label}_${GROUP_TYPES.terms}`, + // }, + // }); + // onGroupSelectionChange(groupSelection); + // }, + // size: "xs", + // color: "text", + // }, + // { + // label: "Aggregate by sum ", + // onClick: () => { + // aggSelection[`sum_${field.label}`] = { + // sum: { field: field.label }, + // }; + // onAggregationSelectionChange(aggSelection); + // }, + // size: "xs", + // color: "text", + // }, + // { + // label: "Aggregate by max ", + // onClick: () => { + // aggSelection[`max_${field.label}`] = { + // max: { field: field.label }, + // }; + // onAggregationSelectionChange(aggSelection); + // }, + // size: "xs", + // color: "text", + // }, + // { + // label: "Aggregate by min ", + // onClick: () => { + // aggSelection[`min_${field.label}`] = { + // min: { field: field.label }, + // }; + // onAggregationSelectionChange(aggSelection); + // }, + // size: "xs", + // color: "text", + // }, + // { + // label: "Aggregate by avg ", + // onClick: () => { + // aggSelection[`avg_${field.label}`] = { + // avg: { field: field.label }, + // }; + // onAggregationSelectionChange(aggSelection); + // }, + // size: "xs", + // color: "text", + // }, + // { + // label: "Aggregate by count ", + // onClick: () => { + // aggSelection[`count_${field.label}`] = { + // value_count: { field: field.label }, + // }; + // onAggregationSelectionChange(aggSelection); + // }, + // size: "xs", + // color: "text", + // }, + // { + // label: "Aggregate by percentile", + // onClick: () => { + // aggSelection[`percentiles_${field.label}`] = { + // percentiles: { field: field.label, percents: [1, 5, 25, 99] }, + // }; + // onAggregationSelectionChange(aggSelection); + // }, + // size: "xs", + // color: "text", + // }, + // { + // label: "Aggregate by scripted metrics ", + // onClick: () => { + // aggSelection[`scripted_metric_${field.label}`] = { + // scripted_metric: { + // init_script: "", + // map_script: "", + // combine_script: "", + // reduce_script: "", + // }, + // }; + // onAggregationSelectionChange(aggSelection); + // }, + // size: "xs", + // color: "text", + // }, + // ], }, }); }); @@ -303,7 +311,6 @@ export default function DefineTransforms({

{`Viewing sample data from index ${sourceIndex}`}

- {/*TODO: add rowCount*/} No fields selected} + title={ + +

No fields selected

+
+ } body={

From the table above, select a field you want to transform by clicking the “plus” button next to the field name

} /> diff --git a/public/pages/CreateTransform/components/PreviewTransform/PreviewTransform.tsx b/public/pages/CreateTransform/components/PreviewTransform/PreviewTransform.tsx index 5aedcfa3f..92e7afc81 100644 --- a/public/pages/CreateTransform/components/PreviewTransform/PreviewTransform.tsx +++ b/public/pages/CreateTransform/components/PreviewTransform/PreviewTransform.tsx @@ -1,12 +1,27 @@ +/* + * Copyright 2021 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"). + * You may not use this file except in compliance with the License. + * A copy of the License is located at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * or in the "license" file accompanying this file. This file is distributed + * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either + * express or implied. See the License for the specific language governing + * permissions and limitations under the License. + */ + import React, { useCallback, useState } from "react"; import { EuiDataGrid, EuiDataGridColumn } from "@elastic/eui"; import PreviewEmptyPrompt from "../PreviewEmptyPrompt"; -interface PreviewTransformsProps { +interface PreviewTransformProps { previewTransform: any[]; } -export default function PreviewTransforms({ previewTransform }: PreviewTransformsProps) { +export default function PreviewTransform({ previewTransform }: PreviewTransformProps) { const [previewColumns, setPreviewColumns] = useState([]); const [visiblePreviewColumns, setVisiblePreviewColumns] = useState(() => previewColumns.map(({ id }) => id).slice(0, 5)); const [previewPagination, setPreviewPagination] = useState({ pageIndex: 0, pageSize: 10 }); diff --git a/public/pages/CreateTransform/components/ReviewDefinition/ReviewDefinition.tsx b/public/pages/CreateTransform/components/ReviewDefinition/ReviewDefinition.tsx index a93bc9496..a61a54382 100644 --- a/public/pages/CreateTransform/components/ReviewDefinition/ReviewDefinition.tsx +++ b/public/pages/CreateTransform/components/ReviewDefinition/ReviewDefinition.tsx @@ -18,7 +18,6 @@ import { EuiFlexGrid, EuiSpacer, EuiFlexItem, EuiText, EuiAccordion } from "@ela import { ContentPanel, ContentPanelActions } from "../../../../components/ContentPanel"; import { ModalConsumer } from "../../../../components/Modal"; import { TransformGroupItem } from "../../../../../models/interfaces"; -import DefineTransforms from "../DefineTransforms"; interface ReviewDefinitionProps { selectedGroupField: TransformGroupItem[]; diff --git a/public/pages/CreateTransform/components/TransformOptions/Panels/HistogramPanel/HistogramPanel.tsx b/public/pages/CreateTransform/components/TransformOptions/Panels/HistogramPanel/HistogramPanel.tsx new file mode 100644 index 000000000..04c2d5d0a --- /dev/null +++ b/public/pages/CreateTransform/components/TransformOptions/Panels/HistogramPanel/HistogramPanel.tsx @@ -0,0 +1,65 @@ +/* + * Copyright 2021 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"). + * You may not use this file except in compliance with the License. + * A copy of the License is located at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * or in the "license" file accompanying this file. This file is distributed + * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either + * express or implied. See the License for the specific language governing + * permissions and limitations under the License. + */ + +import { GROUP_TYPES, TransformGroupItem } from "../../../../../../../models/interfaces"; +import React, { useState } from "react"; +import { EuiButton, EuiFieldNumber, EuiFlexGroup, EuiFlexItem, EuiFormRow, EuiPanel } from "@elastic/eui"; +interface HistogramPanelProps { + name: string; + handleGroupSelectionChange: (newGroupItem: TransformGroupItem) => void; + closePopover: () => void; +} + +export default function HistogramPanel({ name, handleGroupSelectionChange, closePopover }: HistogramPanelProps) { + const [histogramInterval, setHistogramInterval] = useState(5); + + return ( + + + + + setHistogramInterval(e.target.valueAsNumber)} /> + + + + + + + closePopover()}> + Cancel + + + + { + const targetFieldName = `${name} _${GROUP_TYPES.histogram}`; + handleGroupSelectionChange({ + histogram: { + source_field: name, + target_field: targetFieldName, + interval: histogramInterval, + }, + }); + }} + > + OK + + + + + ); +} diff --git a/public/pages/CreateTransform/components/TransformOptions/Panels/HistogramPanel/index.ts b/public/pages/CreateTransform/components/TransformOptions/Panels/HistogramPanel/index.ts new file mode 100644 index 000000000..68f34c95f --- /dev/null +++ b/public/pages/CreateTransform/components/TransformOptions/Panels/HistogramPanel/index.ts @@ -0,0 +1,18 @@ +/* + * Copyright 2021 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"). + * You may not use this file except in compliance with the License. + * A copy of the License is located at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * or in the "license" file accompanying this file. This file is distributed + * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either + * express or implied. See the License for the specific language governing + * permissions and limitations under the License. + */ + +import HistogramPanel from "./HistogramPanel"; + +export default HistogramPanel; diff --git a/public/pages/CreateTransform/components/TransformOptions/Panels/PercentilePanel/PercentilePanel.tsx b/public/pages/CreateTransform/components/TransformOptions/Panels/PercentilePanel/PercentilePanel.tsx new file mode 100644 index 000000000..157f232dd --- /dev/null +++ b/public/pages/CreateTransform/components/TransformOptions/Panels/PercentilePanel/PercentilePanel.tsx @@ -0,0 +1,113 @@ +/* + * Copyright 2021 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"). + * You may not use this file except in compliance with the License. + * A copy of the License is located at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * or in the "license" file accompanying this file. This file is distributed + * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either + * express or implied. See the License for the specific language governing + * permissions and limitations under the License. + */ + +import React, { useState } from "react"; +import { EuiButton, EuiComboBox, EuiFlexGroup, EuiFlexItem, EuiFormRow, EuiPanel } from "@elastic/eui"; + +interface PercentilePanelProps { + name: string; + aggSelection: any; + handleAggSelectionChange: () => void; + closePopover: () => void; +} + +export default function PercentilePanel({ name, aggSelection, handleAggSelectionChange, closePopover }: PercentilePanelProps) { + const [percents, setPercents] = useState<{ label: string }[]>([]); + const [isInvalid, setInvalid] = useState(false); + + const onChangePercents = (selectedPercent: { label: string }[]): void => { + setPercents(selectedPercent); + setInvalid(false); + }; + + const isValidPercent = (value: string) => { + // Only numbers between 0-100 including decimals. No spaces, numbers, or special characters. + const numericValue = parseFloat(value); + return numericValue >= 0.0 && numericValue <= 100.0; + }; + + const onCreateOption = (searchValue: string) => { + if (!isValidPercent(searchValue)) { + // Return false to explicitly reject the user's input. + return false; + } + + const newOption = { + label: searchValue, + }; + + // Select the option. + setPercents([...percents, newOption]); + }; + + const onSearchChange = (searchValue: string) => { + if (!searchValue) { + setInvalid(false); + + return; + } + + setInvalid(!isValidPercent(searchValue)); + }; + + return ( + + + + + + + + + + + + closePopover()}> + Cancel + + + + { + aggSelection[`percentiles_${name}`] = { + percentiles: { + field: name, + percents: percents.map((value) => parseFloat(value.label)), + }, + }; + handleAggSelectionChange(); + }} + > + OK + + + + + ); +} diff --git a/public/pages/CreateTransform/components/TransformOptions/Panels/PercentilePanel/index.ts b/public/pages/CreateTransform/components/TransformOptions/Panels/PercentilePanel/index.ts new file mode 100644 index 000000000..b4fd5ea91 --- /dev/null +++ b/public/pages/CreateTransform/components/TransformOptions/Panels/PercentilePanel/index.ts @@ -0,0 +1,18 @@ +/* + * Copyright 2021 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"). + * You may not use this file except in compliance with the License. + * A copy of the License is located at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * or in the "license" file accompanying this file. This file is distributed + * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either + * express or implied. See the License for the specific language governing + * permissions and limitations under the License. + */ + +import PercentilePanel from "./PercentilePanel"; + +export default PercentilePanel; diff --git a/public/pages/CreateTransform/components/TransformOptions/TranformOptions.tsx b/public/pages/CreateTransform/components/TransformOptions/TranformOptions.tsx new file mode 100644 index 000000000..c62c81cf0 --- /dev/null +++ b/public/pages/CreateTransform/components/TransformOptions/TranformOptions.tsx @@ -0,0 +1,345 @@ +/* + * Copyright 2021 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"). + * You may not use this file except in compliance with the License. + * A copy of the License is located at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * or in the "license" file accompanying this file. This file is distributed + * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either + * express or implied. See the License for the specific language governing + * permissions and limitations under the License. + */ + +import React from "react"; +import { EuiButtonIcon, EuiContextMenu, EuiContextMenuPanelDescriptor, EuiFlexGroup, EuiFlexItem, EuiPopover } from "@elastic/eui"; +import { useState } from "react"; +import { isNumericMapping } from "../../utils/helpers"; +import { GROUP_TYPES, TransformGroupItem } from "../../../../../models/interfaces"; +import HistogramPanel from "./Panels/HistogramPanel"; +import PercentilePanel from "./Panels/PercentilePanel"; + +interface TransformOptionsProps { + name: string; + type?: string; + selectedGroupField: TransformGroupItem[]; + onGroupSelectionChange: (selectedFields: TransformGroupItem[]) => void; + selectedAggregations: any; + onAggregationSelectionChange: (selectedFields: any) => void; +} + +export default function TransformOptions({ + name, + type, + selectedGroupField, + onGroupSelectionChange, + selectedAggregations, + onAggregationSelectionChange, +}: TransformOptionsProps) { + const isNumeric = isNumericMapping(type); + const isDate = type == "date"; + const isText = type == "text"; + + const [isPopoverOpen, setIsPopoverOpen] = useState(false); + const [groupSelection, setGroupSelection] = useState(selectedGroupField); + const [aggSelection, setAggSelection] = useState(selectedAggregations); + + const closePopover = () => { + setIsPopoverOpen(false); + }; + + const handleGroupSelectionChange = (newAggItem: any): void => { + groupSelection.push(newAggItem); + onGroupSelectionChange(groupSelection); + closePopover(); + }; + const handleAggSelectionChange = (): void => { + onAggregationSelectionChange(aggSelection); + closePopover(); + }; + + const panels: EuiContextMenuPanelDescriptor[] = [ + { + id: 0, + title: "Transform options", + items: [ + { + name: "Group by histogram", + panel: 1, + }, + { + name: "Aggregate by sum", + onClick: () => { + aggSelection[`sum_${name}`] = { + sum: { field: name }, + }; + handleAggSelectionChange(); + }, + }, + { + name: "Aggregate by max", + onClick: () => { + aggSelection[`max_${name}`] = { + max: { field: name }, + }; + handleAggSelectionChange(); + }, + }, + { + name: "Aggregate by min", + onClick: () => { + aggSelection[`min_${name}`] = { + min: { field: name }, + }; + handleAggSelectionChange(); + }, + }, + { + name: "Aggregate by avg", + onClick: () => { + aggSelection[`avg_${name}`] = { + avg: { field: name }, + }; + handleAggSelectionChange(); + }, + }, + { + name: "Aggregate by count", + onClick: () => { + aggSelection[`count_${name}`] = { + value_count: { field: name }, + }; + handleAggSelectionChange(); + }, + }, + { + name: "Aggregate by percentile", + panel: 2, + }, + { + name: "Aggregate by scripted metrics", + panel: 3, + }, + ], + }, + { + id: 1, + title: "Back", + content: , + }, + { + id: 2, + title: "Back", + content: ( + + ), + }, + { + id: 3, + title: "Back", + }, + ]; + const datePanels: EuiContextMenuPanelDescriptor[] = [ + { + id: 0, + title: "Transform options", + items: [ + { + name: "Group by date histogram", + panel: 1, + }, + { + name: "Aggregate by count", + onClick: () => { + aggSelection[`count_${name}`] = { + value_count: { field: name }, + }; + handleAggSelectionChange(); + }, + }, + ], + }, + { + id: 1, + title: "Back", + items: [ + { + name: "Millisecond", + onClick: () => { + groupSelection.push({ + date_histogram: { + source_field: name, + target_field: `${name}_${GROUP_TYPES.dateHistogram}_millisecond`, + fixed_interval: "1ms", + }, + }); + }, + }, + { + name: "Second", + onClick: () => { + handleGroupSelectionChange({ + date_histogram: { + source_field: name, + target_field: `${name}_${GROUP_TYPES.dateHistogram}_second`, + fixed_interval: "1s", + }, + }); + }, + }, + { + name: "Minute", + onClick: () => { + handleGroupSelectionChange({ + date_histogram: { + source_field: name, + target_field: `${name}_${GROUP_TYPES.dateHistogram}_minute`, + fixed_interval: "1m", + }, + }); + }, + }, + { + name: "Hour", + onClick: () => { + handleGroupSelectionChange({ + date_histogram: { + source_field: name, + target_field: `${name}_${GROUP_TYPES.dateHistogram}_hour`, + fixed_interval: "1h", + }, + }); + }, + }, + { + name: "Day", + onClick: () => { + handleGroupSelectionChange({ + date_histogram: { + source_field: name, + target_field: `${name}_${GROUP_TYPES.dateHistogram}_day`, + calendar_interval: "1d", + }, + }); + }, + }, + { + name: "Week", + onClick: () => { + handleGroupSelectionChange({ + date_histogram: { + source_field: name, + target_field: `${name}_${GROUP_TYPES.dateHistogram}_week`, + calendar_interval: "1w", + }, + }); + }, + }, + { + name: "Month", + onClick: () => { + handleGroupSelectionChange({ + date_histogram: { + source_field: name, + target_field: `${name}_${GROUP_TYPES.dateHistogram}_month`, + calendar_interval: "1M", + }, + }); + }, + }, + { + name: "Quarter", + onClick: () => { + handleGroupSelectionChange({ + date_histogram: { + source_field: name, + target_field: `${name}_${GROUP_TYPES.dateHistogram}_quarter`, + calendar_interval: "1q", + }, + }); + }, + }, + { + name: "Year", + onClick: () => { + handleGroupSelectionChange({ + date_histogram: { + source_field: name, + target_field: `${name}_${GROUP_TYPES.dateHistogram}_year`, + calendar_interval: "1y", + }, + }); + }, + }, + ], + }, + ]; + const textPanels: EuiContextMenuPanelDescriptor[] = [ + { + id: 0, + title: "Transform options", + items: [ + { + name: "No options available for text fields", + }, + ], + }, + ]; + const keywordPanels: EuiContextMenuPanelDescriptor[] = [ + { + id: 0, + title: "Transform options", + items: [ + { + name: "Group by terms", + onClick: () => { + handleGroupSelectionChange({ + terms: { + source_field: name, + target_field: `${name}_${GROUP_TYPES.terms}`, + }, + }); + }, + }, + { + name: "Aggregate by count", + onClick: () => { + aggSelection[`count_${name}`] = { + value_count: { field: name }, + }; + handleAggSelectionChange(); + }, + }, + ], + }, + ]; + + const button = setIsPopoverOpen(!isPopoverOpen)} />; + + return ( +
+ + {name} + + + + + + +
+ ); +} diff --git a/public/pages/CreateTransform/components/TransformOptions/index.ts b/public/pages/CreateTransform/components/TransformOptions/index.ts new file mode 100644 index 000000000..054d1acf1 --- /dev/null +++ b/public/pages/CreateTransform/components/TransformOptions/index.ts @@ -0,0 +1,18 @@ +/* + * Copyright 2021 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"). + * You may not use this file except in compliance with the License. + * A copy of the License is located at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * or in the "license" file accompanying this file. This file is distributed + * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either + * express or implied. See the License for the specific language governing + * permissions and limitations under the License. + */ + +import TransformOptions from "./TransformOptions"; + +export default TransformOptions; From f58272e80a731fb56e6fa0b6bab7386410b37c74 Mon Sep 17 00:00:00 2001 From: Annie Date: Fri, 14 May 2021 14:25:23 -0700 Subject: [PATCH 72/93] Remove scripted metrics menu --- .../TransformOptions/TranformOptions.tsx | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/public/pages/CreateTransform/components/TransformOptions/TranformOptions.tsx b/public/pages/CreateTransform/components/TransformOptions/TranformOptions.tsx index c62c81cf0..40636310e 100644 --- a/public/pages/CreateTransform/components/TransformOptions/TranformOptions.tsx +++ b/public/pages/CreateTransform/components/TransformOptions/TranformOptions.tsx @@ -118,10 +118,10 @@ export default function TransformOptions({ name: "Aggregate by percentile", panel: 2, }, - { - name: "Aggregate by scripted metrics", - panel: 3, - }, + // { + // name: "Aggregate by scripted metrics", + // panel: 3, + // }, ], }, { @@ -141,10 +141,10 @@ export default function TransformOptions({ /> ), }, - { - id: 3, - title: "Back", - }, + // { + // id: 3, + // title: "Back", + // }, ]; const datePanels: EuiContextMenuPanelDescriptor[] = [ { From 1288646d60f78c0599b9f7bb371cb658d6089887 Mon Sep 17 00:00:00 2001 From: Annie Date: Tue, 18 May 2021 11:15:21 -0500 Subject: [PATCH 73/93] Modify button display, text, and number of data sample size Hide type for fields Fetch 50 sample data --- .../components/DefineTransforms/DefineTransforms.tsx | 12 +++++------- .../CreateTransformForm/CreateTransformForm.tsx | 11 +---------- public/pages/CreateTransform/utils/constants.ts | 2 ++ server/routes/transforms.ts | 4 ++++ server/services/TransformService.ts | 11 ++++++----- 5 files changed, 18 insertions(+), 22 deletions(-) diff --git a/public/pages/CreateTransform/components/DefineTransforms/DefineTransforms.tsx b/public/pages/CreateTransform/components/DefineTransforms/DefineTransforms.tsx index 965907bee..057e1b754 100644 --- a/public/pages/CreateTransform/components/DefineTransforms/DefineTransforms.tsx +++ b/public/pages/CreateTransform/components/DefineTransforms/DefineTransforms.tsx @@ -22,6 +22,7 @@ import { TransformService } from "../../../../services"; import { getErrorMessage } from "../../../../utils/helpers"; import PreviewTransform from "../PreviewTransform"; import TransformOptions from "../TransformOptions/TranformOptions"; +import { DefaultSampleDataSize } from "../../utils/constants"; interface DefineTransformsProps { transformService: TransformService; @@ -53,11 +54,9 @@ export default function DefineTransforms({ let columns: EuiDataGridColumn[] = []; fields.map((field: FieldItem) => { - const isText = field.type == "text"; - // TODO: Handle the available options according to column types columns.push({ id: field.label, - display: !isText && ( + display: ( ), - displayAsText: field.label + " type: " + field.type, schema: field.type, actions: { showHide: false, @@ -222,7 +220,7 @@ export default function DefineTransforms({ const fetchData = useCallback(async () => { setLoading(true); try { - const response = await transformService.searchSampleData(sourceIndex, { from, size }); + const response = await transformService.searchSampleData(sourceIndex, { from: 0, size: DefaultSampleDataSize }); if (response.ok) { setData(response.response.data); @@ -269,7 +267,7 @@ export default function DefineTransforms({ if (!loading && data.hasOwnProperty(rowIndex)) { // TODO: work on truncating the value to certain length defined by the keyword field if (columns?.find((column) => column.id == columnId).schema == "keyword") { - // Strip off the keyword postfix + // Remove the keyword postfix for getting correct data from array const correspondingTextColumnId = columnId.replace(".keyword", ""); return data[rowIndex]._source[correspondingTextColumnId] ? data[rowIndex]._source[correspondingTextColumnId] : "-"; } @@ -315,7 +313,7 @@ export default function DefineTransforms({ aria-label="Define transforms" columns={columns} columnVisibility={{ visibleColumns, setVisibleColumns }} - rowCount={Math.min(dataCount, 200)} + rowCount={Math.min(dataCount, DefaultSampleDataSize)} renderCellValue={renderCellValue} sorting={{ columns: sortingColumns, onSort }} pagination={{ diff --git a/public/pages/CreateTransform/containers/CreateTransformForm/CreateTransformForm.tsx b/public/pages/CreateTransform/containers/CreateTransformForm/CreateTransformForm.tsx index f45a44f7c..4a35a3cc2 100644 --- a/public/pages/CreateTransform/containers/CreateTransformForm/CreateTransformForm.tsx +++ b/public/pages/CreateTransform/containers/CreateTransformForm/CreateTransformForm.tsx @@ -23,16 +23,7 @@ import IndexService from "../../../../services/IndexService"; import { ManagedCatIndex, PreviewTransformResponse } from "../../../../../server/models/interfaces"; import CreateTransform from "../CreateTransform"; import CreateTransformStep2 from "../CreateTransformStep2"; -import { - DimensionItem, - FieldItem, - GroupItem, - IndexItem, - MetricItem, - Transform, - TransformAggItem, - TransformGroupItem, -} from "../../../../../models/interfaces"; +import { FieldItem, GroupItem, IndexItem, Transform, TransformGroupItem } from "../../../../../models/interfaces"; import { getErrorMessage } from "../../../../utils/helpers"; import { EMPTY_TRANSFORM } from "../../utils/constants"; import CreateTransformStep3 from "../CreateTransformStep3"; diff --git a/public/pages/CreateTransform/utils/constants.ts b/public/pages/CreateTransform/utils/constants.ts index 1ab5afeb0..b692b3eaf 100644 --- a/public/pages/CreateTransform/utils/constants.ts +++ b/public/pages/CreateTransform/utils/constants.ts @@ -75,3 +75,5 @@ export const AddFieldsColumns = [ render: (type: string | undefined) => (type == null || type == undefined ? "-" : type), }, ]; + +export const DefaultSampleDataSize = 50; diff --git a/server/routes/transforms.ts b/server/routes/transforms.ts index 964ccd93d..48292d211 100644 --- a/server/routes/transforms.ts +++ b/server/routes/transforms.ts @@ -109,6 +109,10 @@ export default function (services: NodeServices, router: IRouter) { params: schema.object({ index: schema.string(), }), + query: schema.object({ + from: schema.number(), + size: schema.number(), + }), }, }, transformService.searchSampleData diff --git a/server/services/TransformService.ts b/server/services/TransformService.ts index 650dcca87..b6e54da6b 100644 --- a/server/services/TransformService.ts +++ b/server/services/TransformService.ts @@ -320,8 +320,8 @@ export default class TransformService { ): Promise>> => { try { const { from, size } = request.query as { - from: string; - size: string; + from: number; + size: number; }; const { index } = request.params as { index: string }; const params = { @@ -329,9 +329,12 @@ export default class TransformService { from: from, size: size, }; + //debug use + console.log("From: " + from + " size: " + size); const { callAsCurrentUser: callWithRequest } = this.esDriver.asScoped(request); const searchResponse: SearchResponse = await callWithRequest("search", params); - + //debug use + // console.log("Server search response: " + JSON.stringify(searchResponse)); return response.custom({ statusCode: 200, body: { @@ -372,8 +375,6 @@ export default class TransformService { response: KibanaResponseFactory ): Promise>> => { try { - //Debug use - console.log(JSON.stringify(request.body)); let params = { body: JSON.stringify(request.body), }; From 0272380639db01b13f4b526ec2847512e72a3c1f Mon Sep 17 00:00:00 2001 From: AWSHurneyt <79280347+AWSHurneyt@users.noreply.github.com> Date: Tue, 18 May 2021 18:20:47 -0700 Subject: [PATCH 74/93] Refactored UX based on feedback and to better align with mockups. (#172) * Refactored UX based on feedback and to better align with mockups. * Refactored UX based on feedback and to better align with mockups. --- .../ReviewDefinition/ReviewDefinition.tsx | 9 ++- .../components/Schedule/Schedule.tsx | 53 +++++------------ .../pages/CreateTransform/utils/constants.ts | 5 ++ .../Transforms/components/Indices/Indices.tsx | 54 ++++++++--------- .../components/Schedule/Schedule.tsx | 57 ++++-------------- .../Transforms/TransformSettings.tsx | 10 +++- .../pages/Transforms/utils/metadataHelper.tsx | 59 +++++++++++++++++-- 7 files changed, 132 insertions(+), 115 deletions(-) diff --git a/public/pages/CreateTransform/components/ReviewDefinition/ReviewDefinition.tsx b/public/pages/CreateTransform/components/ReviewDefinition/ReviewDefinition.tsx index a61a54382..12b8008b8 100644 --- a/public/pages/CreateTransform/components/ReviewDefinition/ReviewDefinition.tsx +++ b/public/pages/CreateTransform/components/ReviewDefinition/ReviewDefinition.tsx @@ -88,7 +88,14 @@ export default class ReviewDefinition extends Component { {aggItems()} - + +

Sample source index and transform result

+ + } + >
); diff --git a/public/pages/CreateTransform/components/Schedule/Schedule.tsx b/public/pages/CreateTransform/components/Schedule/Schedule.tsx index dd8f3f20e..8acc4540c 100644 --- a/public/pages/CreateTransform/components/Schedule/Schedule.tsx +++ b/public/pages/CreateTransform/components/Schedule/Schedule.tsx @@ -30,8 +30,9 @@ import { EuiAccordion, EuiHorizontalRule, } from "@elastic/eui"; -import { DelayTimeunitOptions, ScheduleIntervalTimeunitOptions } from "../../utils/constants"; +import { DelayTimeunitOptions, ExecutionFrequencyDefinitionOptions, ScheduleIntervalTimeunitOptions } from "../../utils/constants"; import { ContentPanel } from "../../../../components/ContentPanel"; +import { selectInterval } from "../../../Transforms/utils/metadataHelper"; interface ScheduleProps { isEdit: boolean; @@ -59,37 +60,6 @@ const radios = [ }, ]; -const selectInterval = ( - interval: number, - intervalTimeunit: string, - intervalError: string, - onChangeInterval: (e: ChangeEvent) => void, - onChangeTimeunit: (value: ChangeEvent) => void -) => ( - - - - - - - - - - - - - - -); - -const timezones = moment.tz.names().map((tz) => ({ label: tz, text: tz })); - export default class Schedule extends Component { constructor(props: ScheduleProps) { super(props); @@ -124,15 +94,24 @@ export default class Schedule extends Component { {!isEdit} - - - - + {/* TODO: Removing transform execution frequency dropdown menu as only fix interval will be supported in P0. */} + {/**/} + {/* */} + {/**/} + {/**/} + {/* TODO: Replace with switch block when define by cron expressions is supported. */} {selectInterval(interval, intervalTimeunit, intervalError, onChangeIntervalTime, onChangeIntervalTimeunit)} - + +

Advanced

+ + } + > { - constructor(props: IndicesProps) { - super(props); - } + constructor(props: IndicesProps) { + super(props); + } - render() { - const { sourceIndex, targetIndex, sourceIndexFilter } = this.props; + render() { + const { sourceIndex, targetIndex, sourceIndexFilter } = this.props; - return ( - -
- -
Source index
-
{sourceIndex}
-
- - -
Source index filter
-
{sourceIndexFilter}
-
- - -
Target index
-
{targetIndex}
-
-
-
- ) - } + return ( + +
+ +
Source index
+
{sourceIndex}
+
+ + +
Source index filter
+ +
+ + +
Target index
+
{targetIndex}
+
+
+
+ ); + } } diff --git a/public/pages/Transforms/components/Schedule/Schedule.tsx b/public/pages/Transforms/components/Schedule/Schedule.tsx index d16cc0853..ca96d0817 100644 --- a/public/pages/Transforms/components/Schedule/Schedule.tsx +++ b/public/pages/Transforms/components/Schedule/Schedule.tsx @@ -25,11 +25,13 @@ import { EuiFlexGroup, EuiFlexItem, EuiTextArea, + EuiText, } from "@elastic/eui"; // @ts-ignore import { htmlIdGenerator } from "@elastic/eui/lib/services"; import { ScheduleIntervalTimeunitOptions } from "../../utils/constants"; import { ContentPanel } from "../../../../components/ContentPanel"; +import { selectCronExpression, selectInterval } from "../../utils/metadataHelper"; interface ScheduleProps { transformId: string; @@ -51,37 +53,6 @@ interface ScheduleProps { onIntervalTimeUnitChange: (e: ChangeEvent) => void; } -const selectInterval = ( - interval: number, - intervalTimeunit: string, - intervalError: string, - onIntervalChange: (e: ChangeEvent) => void, - onIntervalTimeUnitChange: (value: ChangeEvent) => void -) => ( - - - - - - - - - - - - - - -); - -const timezones = moment.tz.names().map((tz) => ({ label: tz, text: tz })); - // TODO: Check wording for page size form with UX team export default class Schedule extends Component { constructor(props: ScheduleProps) { @@ -131,22 +102,20 @@ export default class Schedule extends Component {
- {schedule == "fixed" ? ( - selectInterval(interval, intervalTimeUnit, intervalError, onIntervalChange, onIntervalTimeUnitChange) - ) : ( - - - - - - - - - )} + {schedule == "fixed" + ? selectInterval(interval, intervalTimeUnit, intervalError, onIntervalChange, onIntervalTimeUnitChange) + : selectCronExpression(cronExpression, onCronExpressionChange, cronTimeZone, onCronTimeZoneChange)} - + +

Advanced

+ + } + >
- + +

Sample source index and transform result

+ + } + onClick={this.onClick} + >
diff --git a/public/pages/Transforms/utils/metadataHelper.tsx b/public/pages/Transforms/utils/metadataHelper.tsx index c05f4c6b5..eb9473685 100644 --- a/public/pages/Transforms/utils/metadataHelper.tsx +++ b/public/pages/Transforms/utils/metadataHelper.tsx @@ -13,9 +13,11 @@ * permissions and limitations under the License. */ -import {TransformMetadata} from "../../../../models/interfaces"; -import React from "react"; -import { EuiFlexGroup, EuiFlexItem, EuiIcon, EuiText } from "@elastic/eui"; +import { TransformMetadata } from "../../../../models/interfaces"; +import React, { ChangeEvent } from "react"; +import moment from "moment-timezone"; +import { EuiFieldNumber, EuiFlexGroup, EuiFlexItem, EuiIcon, EuiFormRow, EuiSelect, EuiTextArea, EuiText } from "@elastic/eui"; +import { ScheduleIntervalTimeunitOptions } from "../../CreateTransform/utils/constants"; // TODO: merge with rollup helper to have a common helper export const renderStatus = (metadata: TransformMetadata | undefined): JSX.Element => { @@ -39,7 +41,7 @@ export const renderStatus = (metadata: TransformMetadata | undefined): JSX.Eleme break; case "init": return ( - + @@ -67,7 +69,7 @@ export const renderStatus = (metadata: TransformMetadata | undefined): JSX.Eleme } return ( - + @@ -83,3 +85,50 @@ export const renderStatus = (metadata: TransformMetadata | undefined): JSX.Eleme export const renderEnabled = (isEnabled: boolean): string => { return isEnabled ? "Enabled" : "Disabled"; }; + +export const selectInterval = ( + interval: number, + intervalTimeunit: string, + intervalError: string, + onChangeInterval: (e: ChangeEvent) => void, + onChangeTimeunit: (value: ChangeEvent) => void +) => ( + + + + + + + + + + + + + + +); + +export const selectCronExpression = ( + cronExpression: string, + onCronExpressionChange: (e: ChangeEvent) => void, + cronTimeZone: string, + onCronTimeZoneChange: (e: ChangeEvent) => void +) => ( + + + + + + + + +); + +export const timezones = moment.tz.names().map((tz) => ({ label: tz, text: tz })); From ac6083efdb7a16d97dfb81d6076971796fa511e3 Mon Sep 17 00:00:00 2001 From: lobdelle <78931475+lobdelle@users.noreply.github.com> Date: Wed, 19 May 2021 11:27:17 -0700 Subject: [PATCH 75/93] Review aggregate and group on Create Transform now matches mock (#174) --- .../ReviewDefinition/ReviewDefinition.tsx | 50 ++++++++++++++++--- 1 file changed, 44 insertions(+), 6 deletions(-) diff --git a/public/pages/CreateTransform/components/ReviewDefinition/ReviewDefinition.tsx b/public/pages/CreateTransform/components/ReviewDefinition/ReviewDefinition.tsx index 12b8008b8..59dfa74e1 100644 --- a/public/pages/CreateTransform/components/ReviewDefinition/ReviewDefinition.tsx +++ b/public/pages/CreateTransform/components/ReviewDefinition/ReviewDefinition.tsx @@ -17,7 +17,7 @@ import React, { Component } from "react"; import { EuiFlexGrid, EuiSpacer, EuiFlexItem, EuiText, EuiAccordion } from "@elastic/eui"; import { ContentPanel, ContentPanelActions } from "../../../../components/ContentPanel"; import { ModalConsumer } from "../../../../components/Modal"; -import { TransformGroupItem } from "../../../../../models/interfaces"; +import { TransformGroupItem, DimensionItem } from "../../../../../models/interfaces"; interface ReviewDefinitionProps { selectedGroupField: TransformGroupItem[]; @@ -30,29 +30,67 @@ export default class ReviewDefinition extends Component { super(props); } + + render() { const { selectedGroupField, selectedAggregations, onChangeStep } = this.props; const groupItems = () => { return selectedGroupField.map((group) => { + let parsedGroup = parseGroup(group); return ( -
Group
-
{JSON.stringify(group)}
+
Group by {parsedGroup.aggregationMethod}
+
{parsedGroup.field.label}
); }); }; + const parseGroup = (group: TransformGroupItem): DimensionItem => { + switch (true) { + case group.date_histogram != null: + return { + sequence: 0, + aggregationMethod: "date_histogram", + field: { + label: group.date_histogram?.source_field, + }, + interval: group.date_histogram?.interval, + }; + case group.histogram != null: + return { + sequence: 0, + aggregationMethod: "histogram", + field: { + label: group.histogram?.source_field, + }, + interval: group.histogram?.interval, + }; + case group.terms != null: + return { + sequence: 0, + aggregationMethod: "terms", + field: { + label: group.terms?.source_field, + }, + interval: null, + }; + } + } + const aggItems = () => { return Object.keys(selectedAggregations).map((key) => { + let aggregationType = Object.keys(selectedAggregations[key])[0]; + let sourceField = selectedAggregations[key][aggregationType].field; + return ( -
{key}
-
{JSON.stringify(selectedAggregations[key])}
+
{aggregationType}()
+
{sourceField}
); @@ -83,7 +121,7 @@ export default class ReviewDefinition extends Component { >
- + {groupItems()} {aggItems()} From dc4b18f746292d7ffebb27a36b1e3c2c69f377e7 Mon Sep 17 00:00:00 2001 From: AWSHurneyt <79280347+AWSHurneyt@users.noreply.github.com> Date: Wed, 19 May 2021 12:41:20 -0700 Subject: [PATCH 76/93] Removed cron expression schedule option, reformatted popovers, and changed display condition for warning message. (#173) * Refactored UX based on feedback and to better align with mockups. * Refactored UX based on feedback and to better align with mockups. * Removed cron expression schedule option, reformatted popovers, and changed display condition for warning message. * Implemented inline error for duplicate job names. --- .../TransformIndices/TransformIndices.tsx | 196 +++++++++--------- .../Panels/HistogramPanel/HistogramPanel.tsx | 10 +- .../PercentilePanel/PercentilePanel.tsx | 14 +- .../CreateTransform/CreateTransform.tsx | 1 + .../CreateTransformForm.tsx | 27 ++- .../components/Schedule/Schedule.tsx | 31 +-- 6 files changed, 159 insertions(+), 120 deletions(-) diff --git a/public/pages/CreateTransform/components/TransformIndices/TransformIndices.tsx b/public/pages/CreateTransform/components/TransformIndices/TransformIndices.tsx index ed6595fc4..b1ab92ab0 100644 --- a/public/pages/CreateTransform/components/TransformIndices/TransformIndices.tsx +++ b/public/pages/CreateTransform/components/TransformIndices/TransformIndices.tsx @@ -51,6 +51,7 @@ interface TransformIndicesProps { onChangeTargetIndex: (options: EuiComboBoxOptionOption[]) => void; hasAggregation: boolean; fields: FieldItem[]; + beenWarned: boolean; } interface TransformIndicesState { @@ -162,6 +163,7 @@ export default class TransformIndices extends Component { onChangeSourceIndexFilter("{}"); - } + }; return ( - -
- - -

You can't change indices after creating a job. Double-check the source and target index names before proceeding.

-
+
+ {hasAggregation && ( - +

Note: changing source index will erase all existing definitions about aggregations and metrics.

)} - - - + + - - - - - -

Source index filter

-
-
- - - - optional - - -
+ helpText="The index where this transform job is performed on. Type in * as wildcard for index pattern. + Indices cannot be changed once the job is created. Please ensure that you select the right source index." + > + +
+ + + + +

Source index filter

+
+
+ + + - optional + + +
- - Choose a subset of source index to focus on to optimize for performance and computing resource. You can’t change filter once the - job is created. - + + Choose a subset of source index to focus on to optimize for performance and computing resource. You can’t change filter once + the job is created. + - {/*{this.state.dataFilters.map((item) => (*/} - {/* {item}*/} - {/*))}*/} - this.onButtonClick()} - onClickAriaLabel="Edit Source Index Filter" - iconOnClick={() => clearIndexFilter()} - iconOnClickAriaLabel="Clear Source Index Filter" - >{sourceIndexFilter} - this.onButtonClick()} - data-test-subj="addFilter" - className="globalFilterBar__addButton" - > - + Add data filter - - } - isOpen={isPopoverOpen} - closePopover={this.closePopover} - > - - - - - - (*/} + {/* {item}*/} + {/*))}*/} + this.onButtonClick()} + onClickAriaLabel="Edit Source Index Filter" + iconOnClick={() => clearIndexFilter()} + iconOnClickAriaLabel="Clear Source Index Filter" + > + {sourceIndexFilter} + + this.onButtonClick()} + data-test-subj="addFilter" + className="globalFilterBar__addButton" + > + + Add data filter + + } + isOpen={isPopoverOpen} + closePopover={this.closePopover} + > + + + + + - -
- + helpText="The index stores transform results. You can look up an existing index to reuse or type to create a new index." + > + + +
+
+ {beenWarned && ( + + + +

You can't change indices after creating a job. Double-check the source and target index names before proceeding.

+
+
+ )} +
); } } diff --git a/public/pages/CreateTransform/components/TransformOptions/Panels/HistogramPanel/HistogramPanel.tsx b/public/pages/CreateTransform/components/TransformOptions/Panels/HistogramPanel/HistogramPanel.tsx index 04c2d5d0a..f600279ca 100644 --- a/public/pages/CreateTransform/components/TransformOptions/Panels/HistogramPanel/HistogramPanel.tsx +++ b/public/pages/CreateTransform/components/TransformOptions/Panels/HistogramPanel/HistogramPanel.tsx @@ -15,7 +15,7 @@ import { GROUP_TYPES, TransformGroupItem } from "../../../../../../../models/interfaces"; import React, { useState } from "react"; -import { EuiButton, EuiFieldNumber, EuiFlexGroup, EuiFlexItem, EuiFormRow, EuiPanel } from "@elastic/eui"; +import { EuiButton, EuiFieldNumber, EuiFlexGroup, EuiFlexItem, EuiFormRow, EuiPanel, EuiSpacer } from "@elastic/eui"; interface HistogramPanelProps { name: string; handleGroupSelectionChange: (newGroupItem: TransformGroupItem) => void; @@ -28,16 +28,17 @@ export default function HistogramPanel({ name, handleGroupSelectionChange, close return ( - + setHistogramInterval(e.target.valueAsNumber)} /> + - + - closePopover()}> + closePopover()} style={{ minWidth: 84 }}> Cancel @@ -55,6 +56,7 @@ export default function HistogramPanel({ name, handleGroupSelectionChange, close }, }); }} + style={{ minWidth: 55 }} > OK diff --git a/public/pages/CreateTransform/components/TransformOptions/Panels/PercentilePanel/PercentilePanel.tsx b/public/pages/CreateTransform/components/TransformOptions/Panels/PercentilePanel/PercentilePanel.tsx index 157f232dd..72d98f5e9 100644 --- a/public/pages/CreateTransform/components/TransformOptions/Panels/PercentilePanel/PercentilePanel.tsx +++ b/public/pages/CreateTransform/components/TransformOptions/Panels/PercentilePanel/PercentilePanel.tsx @@ -14,7 +14,7 @@ */ import React, { useState } from "react"; -import { EuiButton, EuiComboBox, EuiFlexGroup, EuiFlexItem, EuiFormRow, EuiPanel } from "@elastic/eui"; +import { EuiButton, EuiComboBox, EuiFlexGroup, EuiFlexItem, EuiFormRow, EuiPanel, EuiSpacer } from "@elastic/eui"; interface PercentilePanelProps { name: string; @@ -64,15 +64,17 @@ export default function PercentilePanel({ name, aggSelection, handleAggSelection return ( - - + + + - + - closePopover()}> + closePopover()} style={{ minWidth: 84 }}> Cancel @@ -103,6 +106,7 @@ export default function PercentilePanel({ name, aggSelection, handleAggSelection }; handleAggSelectionChange(); }} + style={{ minWidth: 55 }} > OK diff --git a/public/pages/CreateTransform/containers/CreateTransform/CreateTransform.tsx b/public/pages/CreateTransform/containers/CreateTransform/CreateTransform.tsx index c49f326f0..1e61cea41 100644 --- a/public/pages/CreateTransform/containers/CreateTransform/CreateTransform.tsx +++ b/public/pages/CreateTransform/containers/CreateTransform/CreateTransform.tsx @@ -49,6 +49,7 @@ interface CreateTransformProps extends RouteComponentProps { fields: FieldItem[]; fieldSelectedOption: string; onFieldChange: () => void; + beenWarned: boolean; } export default class CreateTransform extends Component { diff --git a/public/pages/CreateTransform/containers/CreateTransformForm/CreateTransformForm.tsx b/public/pages/CreateTransform/containers/CreateTransformForm/CreateTransformForm.tsx index 4a35a3cc2..c02be1485 100644 --- a/public/pages/CreateTransform/containers/CreateTransformForm/CreateTransformForm.tsx +++ b/public/pages/CreateTransform/containers/CreateTransformForm/CreateTransformForm.tsx @@ -35,6 +35,7 @@ interface CreateTransformFormProps extends RouteComponentProps { rollupService: RollupService; transformService: TransformService; indexService: IndexService; + beenWarned: boolean; } interface CreateTransformFormState { @@ -77,6 +78,8 @@ interface CreateTransformFormState { intervalTimeunit: string; pageSize: number; transformJSON: any; + + beenWarned: boolean; } export default class CreateTransformForm extends Component { @@ -124,6 +127,8 @@ export default class CreateTransformForm extends Component { let currentStep = this.state.currentStep; + let warned = this.state.beenWarned; let error = false; //Verification here if (currentStep == 1) { const { transformId, sourceIndex, targetIndex } = this.state; + const response = await this.props.transformService.getTransform(transformId); + if (response.ok && response.response._id == transformId) { + this.setState({ + submitError: `There is already a job named "${transformId}". Please provide a different name.`, + transformIdError: `There is already a job named "${transformId}". Please provide a different name.`, + }); + error = true; + } if (!transformId) { this.setState({ submitError: "Job name is required.", transformIdError: "Job name is required." }); error = true; @@ -197,13 +211,17 @@ export default class CreateTransformForm extends Component= 3 ? 4 : currentStep + 1; + if (warned) { + currentStep = currentStep >= 3 ? 4 : currentStep + 1; + } + warned = true; this.setState({ submitError: "", currentStep: currentStep, + beenWarned: warned, }); - } + }; _prev() { let currentStep = this.state.currentStep; @@ -417,6 +435,8 @@ export default class CreateTransformForm extends Component @@ -441,6 +461,7 @@ export default class CreateTransformForm extends Component { /> - - - - + {/* TODO: Removing transform execution frequency dropdown menu as only fix interval will be supported in P0. */} + {/**/} + {/* */} + {/**/} + {/**/} - {schedule == "fixed" - ? selectInterval(interval, intervalTimeUnit, intervalError, onIntervalChange, onIntervalTimeUnitChange) - : selectCronExpression(cronExpression, onCronExpressionChange, cronTimeZone, onCronTimeZoneChange)} + {/* TODO: Replace with switch block when define by cron expressions is supported. */} + {selectInterval(interval, intervalTimeUnit, intervalError, onIntervalChange, onIntervalTimeUnitChange)} + {/*{schedule == "fixed"*/} + {/* ? selectInterval(interval, intervalTimeUnit, intervalError, onIntervalChange, onIntervalTimeUnitChange)*/} + {/* : selectCronExpression(cronExpression, onCronExpressionChange, cronTimeZone, onCronTimeZoneChange)}*/} From 449089f02b03d36a7ae6d5ebb7183babc10d8201 Mon Sep 17 00:00:00 2001 From: Annie Lee <71157062+leeyun-amzn@users.noreply.github.com> Date: Wed, 19 May 2021 14:51:28 -0500 Subject: [PATCH 77/93] Support of deleting transform definition (#175) * Decouple preview transform table from define transform * Decouple EmptyPrompt * Some code cleanup * Add plus button * Update PreviewEmptyPrompt.tsx * Have dummy context menu showing up * Update transform options menu text * Able to add histogram using new menu * Add histogramPanel component * Add date histogram panel * Update TranformOptions.tsx * Update date histogram options * Simplify popover actions * Percentile panel * Percentile panel * Remove transformation in preview initial commit * Correcting the name of file TransformOptions * Show preview table with temporary aggList * Remove transformation * clean up code * Update DefineTransforms.tsx * Hide pagination in preview table * Clean up code Co-authored-by: Eric Lobdell --- models/interfaces.ts | 19 +- .../DefineTransforms/DefineTransforms.tsx | 27 +- .../PreviewOptions/PreviewOptions.tsx | 83 ++++ .../components/PreviewOptions/index.ts | 18 + .../PreviewTransform/PreviewTransform.tsx | 54 ++- .../Panels/HistogramPanel/HistogramPanel.tsx | 23 +- .../PercentilePanel/PercentilePanel.tsx | 13 +- .../TransformOptions/TranformOptions.tsx | 345 -------------- .../TransformOptions/TransformOptions.tsx | 437 ++++++++++++++++++ .../CreateTransformForm.tsx | 81 +++- .../CreateTransformStep2.tsx | 10 +- .../containers/Transforms/EditTransform.tsx | 6 +- 12 files changed, 713 insertions(+), 403 deletions(-) create mode 100644 public/pages/CreateTransform/components/PreviewOptions/PreviewOptions.tsx create mode 100644 public/pages/CreateTransform/components/PreviewOptions/index.ts delete mode 100644 public/pages/CreateTransform/components/TransformOptions/TranformOptions.tsx create mode 100644 public/pages/CreateTransform/components/TransformOptions/TransformOptions.tsx diff --git a/models/interfaces.ts b/models/interfaces.ts index 3f9f66be2..a3a0e6a4a 100644 --- a/models/interfaces.ts +++ b/models/interfaces.ts @@ -260,11 +260,28 @@ export enum GROUP_TYPES { } export interface TransformAggItem { + type: TRANSFORM_AGG_TYPE; + name: string; + item: any | DateHistogramItem | TermsItem | HistogramItem; + percents?: number[]; sum?: { field: string }; max?: { field: string }; min?: { field: string }; avg?: { field: string }; - count?: { field: string }; + value_count?: { field: string }; percentiles?: { field: string; percents: number[] }; scripted_metric?: object; } + +export enum TRANSFORM_AGG_TYPE { + sum = "sum", + max = "max", + min = "min", + avg = "avg", + value_count = "value_count", + percentiles = "percentiles", + scripted_metric = "scripted_metric", + terms = "terms", + histogram = "histogram", + date_histogram = "date_histogram", +} diff --git a/public/pages/CreateTransform/components/DefineTransforms/DefineTransforms.tsx b/public/pages/CreateTransform/components/DefineTransforms/DefineTransforms.tsx index 057e1b754..4b44fbda2 100644 --- a/public/pages/CreateTransform/components/DefineTransforms/DefineTransforms.tsx +++ b/public/pages/CreateTransform/components/DefineTransforms/DefineTransforms.tsx @@ -17,11 +17,11 @@ import { EuiDataGrid, EuiDataGridColumn, EuiSpacer, EuiText } from "@elastic/eui import { CoreStart } from "kibana/public"; import React, { useCallback, useState } from "react"; import { ContentPanel } from "../../../../components/ContentPanel"; -import { FieldItem, TransformGroupItem } from "../../../../../models/interfaces"; +import { FieldItem, TransformAggItem, TransformGroupItem } from "../../../../../models/interfaces"; import { TransformService } from "../../../../services"; import { getErrorMessage } from "../../../../utils/helpers"; import PreviewTransform from "../PreviewTransform"; -import TransformOptions from "../TransformOptions/TranformOptions"; +import TransformOptions from "../TransformOptions"; import { DefaultSampleDataSize } from "../../utils/constants"; interface DefineTransformsProps { @@ -31,9 +31,11 @@ interface DefineTransformsProps { sourceIndex: string; fields: FieldItem[]; selectedGroupField: TransformGroupItem[]; - onGroupSelectionChange: (selectedFields: TransformGroupItem[]) => void; + onGroupSelectionChange: (selectedFields: TransformGroupItem[], aggItem: TransformAggItem) => void; selectedAggregations: any; - onAggregationSelectionChange: (selectedFields: any) => void; + aggList: TransformAggItem[]; + onAggregationSelectionChange: (selectedFields: any, aggItem: TransformAggItem) => void; + onRemoveTransformation: (name: string) => void; previewTransform: any[]; isReadOnly: boolean; } @@ -41,13 +43,15 @@ interface DefineTransformsProps { export default function DefineTransforms({ transformService, notifications, - transfromId, + transformId, sourceIndex, fields, selectedGroupField, onGroupSelectionChange, selectedAggregations, + aggList, onAggregationSelectionChange, + onRemoveTransformation, previewTransform, isReadOnly, }: DefineTransformsProps) { @@ -62,6 +66,7 @@ export default function DefineTransforms({ type={field.type} selectedGroupField={selectedGroupField} onGroupSelectionChange={onGroupSelectionChange} + aggList={aggList} selectedAggregations={selectedAggregations} onAggregationSelectionChange={onAggregationSelectionChange} /> @@ -227,7 +232,7 @@ export default function DefineTransforms({ setDataCount(response.response.total.value); } } catch (err) { - notifications.toasts.addDanger(getErrorMessage(err, "There was a problem loading the rollups")); + notifications.toasts.addDanger(getErrorMessage(err, "There was a problem loading the transforms")); } setLoading(false); }, [sourceIndex]); @@ -334,7 +339,15 @@ export default function DefineTransforms({

Transformed fields preview based on sample data

- + ); } diff --git a/public/pages/CreateTransform/components/PreviewOptions/PreviewOptions.tsx b/public/pages/CreateTransform/components/PreviewOptions/PreviewOptions.tsx new file mode 100644 index 000000000..76bf07b08 --- /dev/null +++ b/public/pages/CreateTransform/components/PreviewOptions/PreviewOptions.tsx @@ -0,0 +1,83 @@ +/* + * Copyright 2021 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"). + * You may not use this file except in compliance with the License. + * A copy of the License is located at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * or in the "license" file accompanying this file. This file is distributed + * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either + * express or implied. See the License for the specific language governing + * permissions and limitations under the License. + */ + +import React from "react"; +import { EuiButtonIcon, EuiContextMenu, EuiContextMenuPanelDescriptor, EuiFlexGroup, EuiFlexItem, EuiPopover } from "@elastic/eui"; +import { useState } from "react"; +import { TransformAggItem, TransformGroupItem } from "../../../../../models/interfaces"; + +interface PreviewOptionsProps { + name: string; + selectedGroupField: TransformGroupItem[]; + onGroupSelectionChange: (selectedFields: TransformGroupItem[], aggItem: TransformAggItem) => void; + aggList: TransformAggItem[]; + selectedAggregations: any; + onAggregationSelectionChange: (selectedFields: any, aggItem: TransformAggItem) => void; + onRemoveTransformation: (name: string) => void; +} + +export default function PreviewOptions({ + name, + selectedGroupField, + onGroupSelectionChange, + selectedAggregations, + aggList, + onAggregationSelectionChange, + onRemoveTransformation, +}: PreviewOptionsProps) { + const [isPopoverOpen, setIsPopoverOpen] = useState(false); + + const closePopover = () => { + setIsPopoverOpen(false); + }; + + const button = setIsPopoverOpen(!isPopoverOpen)} />; + + const panels: EuiContextMenuPanelDescriptor[] = [ + { + id: 0, + title: "", + items: [ + { + name: "Remove transformation", + onClick: () => { + // Remove this transform + onRemoveTransformation(name); + }, + }, + ], + }, + ]; + + return ( +
+ + {name} + + + + + + +
+ ); +} diff --git a/public/pages/CreateTransform/components/PreviewOptions/index.ts b/public/pages/CreateTransform/components/PreviewOptions/index.ts new file mode 100644 index 000000000..ead095700 --- /dev/null +++ b/public/pages/CreateTransform/components/PreviewOptions/index.ts @@ -0,0 +1,18 @@ +/* + * Copyright 2021 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"). + * You may not use this file except in compliance with the License. + * A copy of the License is located at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * or in the "license" file accompanying this file. This file is distributed + * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either + * express or implied. See the License for the specific language governing + * permissions and limitations under the License. + */ + +import PreviewOptions from "./PreviewOptions"; + +export default PreviewOptions; diff --git a/public/pages/CreateTransform/components/PreviewTransform/PreviewTransform.tsx b/public/pages/CreateTransform/components/PreviewTransform/PreviewTransform.tsx index 92e7afc81..c82a7bdb2 100644 --- a/public/pages/CreateTransform/components/PreviewTransform/PreviewTransform.tsx +++ b/public/pages/CreateTransform/components/PreviewTransform/PreviewTransform.tsx @@ -16,12 +16,28 @@ import React, { useCallback, useState } from "react"; import { EuiDataGrid, EuiDataGridColumn } from "@elastic/eui"; import PreviewEmptyPrompt from "../PreviewEmptyPrompt"; +import PreviewOptions from "../PreviewOptions"; +import { TransformAggItem, TransformGroupItem } from "../../../../../models/interfaces"; interface PreviewTransformProps { previewTransform: any[]; + selectedGroupField: TransformGroupItem[]; + onGroupSelectionChange: (selectedFields: TransformGroupItem[], aggItem: TransformAggItem) => void; + aggList: TransformAggItem[]; + selectedAggregations: any; + onAggregationSelectionChange: (selectedFields: any, aggItem: TransformAggItem) => void; + onRemoveTransformation: (name: string) => void; } -export default function PreviewTransform({ previewTransform }: PreviewTransformProps) { +export default function PreviewTransform({ + previewTransform, + selectedGroupField, + onGroupSelectionChange, + selectedAggregations, + aggList, + onAggregationSelectionChange, + onRemoveTransformation, +}: PreviewTransformProps) { const [previewColumns, setPreviewColumns] = useState([]); const [visiblePreviewColumns, setVisiblePreviewColumns] = useState(() => previewColumns.map(({ id }) => id).slice(0, 5)); const [previewPagination, setPreviewPagination] = useState({ pageIndex: 0, pageSize: 10 }); @@ -56,11 +72,22 @@ export default function PreviewTransform({ previewTransform }: PreviewTransformP ); const updatePreviewColumns = (): void => { - if (previewTransform.length) { + if (aggList.length) { let tempCol: EuiDataGridColumn[] = []; - for (const [key, value] of Object.entries(previewTransform[0])) { + aggList.map((aggItem) => { tempCol.push({ - id: key, + id: aggItem.name, + display: ( + + ), actions: { showHide: false, showMoveLeft: false, @@ -69,7 +96,8 @@ export default function PreviewTransform({ previewTransform }: PreviewTransformP showSortDesc: false, }, }); - } + }); + setPreviewColumns(tempCol); setVisiblePreviewColumns(() => tempCol.map(({ id }) => id).slice(0, 5)); } @@ -77,21 +105,21 @@ export default function PreviewTransform({ previewTransform }: PreviewTransformP React.useEffect(() => { updatePreviewColumns(); - }, [previewTransform]); + }, [previewTransform, aggList]); - return previewTransform.length ? ( + return aggList.length ? ( void; + handleGroupSelectionChange: (newGroupItem: TransformGroupItem, type: TRANSFORM_AGG_TYPE, name: string) => void; + aggList: TransformAggItem[]; closePopover: () => void; } -export default function HistogramPanel({ name, handleGroupSelectionChange, closePopover }: HistogramPanelProps) { +export default function HistogramPanel({ name, handleGroupSelectionChange, aggList, closePopover }: HistogramPanelProps) { const [histogramInterval, setHistogramInterval] = useState(5); return ( @@ -48,13 +49,17 @@ export default function HistogramPanel({ name, handleGroupSelectionChange, close fullWidth={false} onClick={() => { const targetFieldName = `${name} _${GROUP_TYPES.histogram}`; - handleGroupSelectionChange({ - histogram: { - source_field: name, - target_field: targetFieldName, - interval: histogramInterval, + handleGroupSelectionChange( + { + histogram: { + source_field: name, + target_field: targetFieldName, + interval: histogramInterval, + }, }, - }); + TRANSFORM_AGG_TYPE.histogram, + targetFieldName + ); }} style={{ minWidth: 55 }} > diff --git a/public/pages/CreateTransform/components/TransformOptions/Panels/PercentilePanel/PercentilePanel.tsx b/public/pages/CreateTransform/components/TransformOptions/Panels/PercentilePanel/PercentilePanel.tsx index 72d98f5e9..a01413566 100644 --- a/public/pages/CreateTransform/components/TransformOptions/Panels/PercentilePanel/PercentilePanel.tsx +++ b/public/pages/CreateTransform/components/TransformOptions/Panels/PercentilePanel/PercentilePanel.tsx @@ -15,11 +15,12 @@ import React, { useState } from "react"; import { EuiButton, EuiComboBox, EuiFlexGroup, EuiFlexItem, EuiFormRow, EuiPanel, EuiSpacer } from "@elastic/eui"; +import { TRANSFORM_AGG_TYPE, TransformAggItem } from "../../../../../../../models/interfaces"; interface PercentilePanelProps { name: string; aggSelection: any; - handleAggSelectionChange: () => void; + handleAggSelectionChange: (aggItem: TransformAggItem) => void; closePopover: () => void; } @@ -98,13 +99,21 @@ export default function PercentilePanel({ name, aggSelection, handleAggSelection fill fullWidth={false} onClick={() => { + const aggItem: TransformAggItem = { + type: TRANSFORM_AGG_TYPE.percentiles, + name: `percentiles_${name}`, + percentiles: { + field: name, + percents: percents.map((value) => parseFloat(value.label)), + }, + }; aggSelection[`percentiles_${name}`] = { percentiles: { field: name, percents: percents.map((value) => parseFloat(value.label)), }, }; - handleAggSelectionChange(); + handleAggSelectionChange(aggItem); }} style={{ minWidth: 55 }} > diff --git a/public/pages/CreateTransform/components/TransformOptions/TranformOptions.tsx b/public/pages/CreateTransform/components/TransformOptions/TranformOptions.tsx deleted file mode 100644 index 40636310e..000000000 --- a/public/pages/CreateTransform/components/TransformOptions/TranformOptions.tsx +++ /dev/null @@ -1,345 +0,0 @@ -/* - * Copyright 2021 Amazon.com, Inc. or its affiliates. All Rights Reserved. - * - * Licensed under the Apache License, Version 2.0 (the "License"). - * You may not use this file except in compliance with the License. - * A copy of the License is located at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * or in the "license" file accompanying this file. This file is distributed - * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either - * express or implied. See the License for the specific language governing - * permissions and limitations under the License. - */ - -import React from "react"; -import { EuiButtonIcon, EuiContextMenu, EuiContextMenuPanelDescriptor, EuiFlexGroup, EuiFlexItem, EuiPopover } from "@elastic/eui"; -import { useState } from "react"; -import { isNumericMapping } from "../../utils/helpers"; -import { GROUP_TYPES, TransformGroupItem } from "../../../../../models/interfaces"; -import HistogramPanel from "./Panels/HistogramPanel"; -import PercentilePanel from "./Panels/PercentilePanel"; - -interface TransformOptionsProps { - name: string; - type?: string; - selectedGroupField: TransformGroupItem[]; - onGroupSelectionChange: (selectedFields: TransformGroupItem[]) => void; - selectedAggregations: any; - onAggregationSelectionChange: (selectedFields: any) => void; -} - -export default function TransformOptions({ - name, - type, - selectedGroupField, - onGroupSelectionChange, - selectedAggregations, - onAggregationSelectionChange, -}: TransformOptionsProps) { - const isNumeric = isNumericMapping(type); - const isDate = type == "date"; - const isText = type == "text"; - - const [isPopoverOpen, setIsPopoverOpen] = useState(false); - const [groupSelection, setGroupSelection] = useState(selectedGroupField); - const [aggSelection, setAggSelection] = useState(selectedAggregations); - - const closePopover = () => { - setIsPopoverOpen(false); - }; - - const handleGroupSelectionChange = (newAggItem: any): void => { - groupSelection.push(newAggItem); - onGroupSelectionChange(groupSelection); - closePopover(); - }; - const handleAggSelectionChange = (): void => { - onAggregationSelectionChange(aggSelection); - closePopover(); - }; - - const panels: EuiContextMenuPanelDescriptor[] = [ - { - id: 0, - title: "Transform options", - items: [ - { - name: "Group by histogram", - panel: 1, - }, - { - name: "Aggregate by sum", - onClick: () => { - aggSelection[`sum_${name}`] = { - sum: { field: name }, - }; - handleAggSelectionChange(); - }, - }, - { - name: "Aggregate by max", - onClick: () => { - aggSelection[`max_${name}`] = { - max: { field: name }, - }; - handleAggSelectionChange(); - }, - }, - { - name: "Aggregate by min", - onClick: () => { - aggSelection[`min_${name}`] = { - min: { field: name }, - }; - handleAggSelectionChange(); - }, - }, - { - name: "Aggregate by avg", - onClick: () => { - aggSelection[`avg_${name}`] = { - avg: { field: name }, - }; - handleAggSelectionChange(); - }, - }, - { - name: "Aggregate by count", - onClick: () => { - aggSelection[`count_${name}`] = { - value_count: { field: name }, - }; - handleAggSelectionChange(); - }, - }, - { - name: "Aggregate by percentile", - panel: 2, - }, - // { - // name: "Aggregate by scripted metrics", - // panel: 3, - // }, - ], - }, - { - id: 1, - title: "Back", - content: , - }, - { - id: 2, - title: "Back", - content: ( - - ), - }, - // { - // id: 3, - // title: "Back", - // }, - ]; - const datePanels: EuiContextMenuPanelDescriptor[] = [ - { - id: 0, - title: "Transform options", - items: [ - { - name: "Group by date histogram", - panel: 1, - }, - { - name: "Aggregate by count", - onClick: () => { - aggSelection[`count_${name}`] = { - value_count: { field: name }, - }; - handleAggSelectionChange(); - }, - }, - ], - }, - { - id: 1, - title: "Back", - items: [ - { - name: "Millisecond", - onClick: () => { - groupSelection.push({ - date_histogram: { - source_field: name, - target_field: `${name}_${GROUP_TYPES.dateHistogram}_millisecond`, - fixed_interval: "1ms", - }, - }); - }, - }, - { - name: "Second", - onClick: () => { - handleGroupSelectionChange({ - date_histogram: { - source_field: name, - target_field: `${name}_${GROUP_TYPES.dateHistogram}_second`, - fixed_interval: "1s", - }, - }); - }, - }, - { - name: "Minute", - onClick: () => { - handleGroupSelectionChange({ - date_histogram: { - source_field: name, - target_field: `${name}_${GROUP_TYPES.dateHistogram}_minute`, - fixed_interval: "1m", - }, - }); - }, - }, - { - name: "Hour", - onClick: () => { - handleGroupSelectionChange({ - date_histogram: { - source_field: name, - target_field: `${name}_${GROUP_TYPES.dateHistogram}_hour`, - fixed_interval: "1h", - }, - }); - }, - }, - { - name: "Day", - onClick: () => { - handleGroupSelectionChange({ - date_histogram: { - source_field: name, - target_field: `${name}_${GROUP_TYPES.dateHistogram}_day`, - calendar_interval: "1d", - }, - }); - }, - }, - { - name: "Week", - onClick: () => { - handleGroupSelectionChange({ - date_histogram: { - source_field: name, - target_field: `${name}_${GROUP_TYPES.dateHistogram}_week`, - calendar_interval: "1w", - }, - }); - }, - }, - { - name: "Month", - onClick: () => { - handleGroupSelectionChange({ - date_histogram: { - source_field: name, - target_field: `${name}_${GROUP_TYPES.dateHistogram}_month`, - calendar_interval: "1M", - }, - }); - }, - }, - { - name: "Quarter", - onClick: () => { - handleGroupSelectionChange({ - date_histogram: { - source_field: name, - target_field: `${name}_${GROUP_TYPES.dateHistogram}_quarter`, - calendar_interval: "1q", - }, - }); - }, - }, - { - name: "Year", - onClick: () => { - handleGroupSelectionChange({ - date_histogram: { - source_field: name, - target_field: `${name}_${GROUP_TYPES.dateHistogram}_year`, - calendar_interval: "1y", - }, - }); - }, - }, - ], - }, - ]; - const textPanels: EuiContextMenuPanelDescriptor[] = [ - { - id: 0, - title: "Transform options", - items: [ - { - name: "No options available for text fields", - }, - ], - }, - ]; - const keywordPanels: EuiContextMenuPanelDescriptor[] = [ - { - id: 0, - title: "Transform options", - items: [ - { - name: "Group by terms", - onClick: () => { - handleGroupSelectionChange({ - terms: { - source_field: name, - target_field: `${name}_${GROUP_TYPES.terms}`, - }, - }); - }, - }, - { - name: "Aggregate by count", - onClick: () => { - aggSelection[`count_${name}`] = { - value_count: { field: name }, - }; - handleAggSelectionChange(); - }, - }, - ], - }, - ]; - - const button = setIsPopoverOpen(!isPopoverOpen)} />; - - return ( -
- - {name} - - - - - - -
- ); -} diff --git a/public/pages/CreateTransform/components/TransformOptions/TransformOptions.tsx b/public/pages/CreateTransform/components/TransformOptions/TransformOptions.tsx new file mode 100644 index 000000000..d26ff86e0 --- /dev/null +++ b/public/pages/CreateTransform/components/TransformOptions/TransformOptions.tsx @@ -0,0 +1,437 @@ +/* + * Copyright 2021 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"). + * You may not use this file except in compliance with the License. + * A copy of the License is located at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * or in the "license" file accompanying this file. This file is distributed + * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either + * express or implied. See the License for the specific language governing + * permissions and limitations under the License. + */ + +import React, { useState } from "react"; +import { EuiButtonIcon, EuiContextMenu, EuiContextMenuPanelDescriptor, EuiFlexGroup, EuiFlexItem, EuiPopover } from "@elastic/eui"; +import { isNumericMapping } from "../../utils/helpers"; +import { GROUP_TYPES, TRANSFORM_AGG_TYPE, TransformAggItem, TransformGroupItem } from "../../../../../models/interfaces"; +import HistogramPanel from "./Panels/HistogramPanel"; +import PercentilePanel from "./Panels/PercentilePanel"; + +interface TransformOptionsProps { + name: string; + type?: string; + selectedGroupField: TransformGroupItem[]; + onGroupSelectionChange: (selectedFields: TransformGroupItem[], aggItem: TransformAggItem) => void; + selectedAggregations: any; + aggList: TransformAggItem[]; + onAggregationSelectionChange: (selectedFields: any, aggItem: TransformAggItem) => void; +} + +export default function TransformOptions({ + name, + type, + selectedGroupField, + onGroupSelectionChange, + selectedAggregations, + aggList, + onAggregationSelectionChange, +}: TransformOptionsProps) { + const isNumeric = isNumericMapping(type); + const isDate = type == "date"; + const isText = type == "text"; + + const [isPopoverOpen, setIsPopoverOpen] = useState(false); + const [groupSelection, setGroupSelection] = useState(selectedGroupField); + const [aggSelection, setAggSelection] = useState(selectedAggregations); + + const closePopover = () => { + setIsPopoverOpen(false); + }; + + const handleGroupSelectionChange = (newAggItem: any, type: TRANSFORM_AGG_TYPE, name: string): void => { + groupSelection.push(newAggItem); + onGroupSelectionChange(groupSelection, { + type, + name, + item: newAggItem, + }); + closePopover(); + }; + const handleAggSelectionChange = (aggItem: TransformAggItem): void => { + onAggregationSelectionChange(aggSelection, aggItem); + closePopover(); + }; + + const panels: EuiContextMenuPanelDescriptor[] = [ + { + id: 0, + title: "Transform options", + items: [ + { + name: "Group by histogram", + panel: 1, + }, + { + name: "Aggregate by sum", + onClick: () => { + const aggItem: TransformAggItem = { + type: TRANSFORM_AGG_TYPE.sum, + name: `sum_${name}`, + item: { sum: { field: name } }, + }; + aggSelection[`sum_${name}`] = { + sum: { field: name }, + }; + handleAggSelectionChange(aggItem); + }, + }, + { + name: "Aggregate by max", + onClick: () => { + const aggItem: TransformAggItem = { + type: TRANSFORM_AGG_TYPE.max, + name: `max_${name}`, + item: { max: { field: name } }, + }; + aggSelection[`max_${name}`] = { + max: { field: name }, + }; + handleAggSelectionChange(aggItem); + }, + }, + { + name: "Aggregate by min", + onClick: () => { + const aggItem: TransformAggItem = { + type: TRANSFORM_AGG_TYPE.min, + name: `min_${name}`, + item: { min: { field: name } }, + }; + aggSelection[`min_${name}`] = { + min: { field: name }, + }; + handleAggSelectionChange(aggItem); + }, + }, + { + name: "Aggregate by avg", + onClick: () => { + const aggItem: TransformAggItem = { + type: TRANSFORM_AGG_TYPE.avg, + name: `avg_${name}`, + item: { avg: { field: name } }, + }; + aggSelection[`avg_${name}`] = { + avg: { field: name }, + }; + handleAggSelectionChange(aggItem); + }, + }, + { + name: "Aggregate by count", + onClick: () => { + const aggItem: TransformAggItem = { + type: TRANSFORM_AGG_TYPE.value_count, + name: `count_${name}`, + item: { value_count: { field: name } }, + }; + aggSelection[`count_${name}`] = { + value_count: { field: name }, + }; + handleAggSelectionChange(aggItem); + }, + }, + { + name: "Aggregate by percentile", + panel: 2, + }, + // { + // name: "Aggregate by scripted metrics", + // panel: 3, + // }, + ], + }, + { + id: 1, + title: "Back", + content: ( + + ), + }, + { + id: 2, + title: "Back", + content: ( + + ), + }, + // { + // id: 3, + // title: "Back", + // }, + ]; + const datePanels: EuiContextMenuPanelDescriptor[] = [ + { + id: 0, + title: "Transform options", + items: [ + { + name: "Group by date histogram", + panel: 1, + }, + { + name: "Aggregate by count", + onClick: () => { + const aggItem: TransformAggItem = { + type: TRANSFORM_AGG_TYPE.value_count, + name: `count_${name}`, + item: { value_count: { field: name } }, + }; + aggSelection[`count_${name}`] = { + value_count: { field: name }, + }; + handleAggSelectionChange(aggItem); + }, + }, + ], + }, + { + id: 1, + title: "Back", + items: [ + { + name: "Millisecond", + onClick: () => { + const targetField = `${name}_${GROUP_TYPES.dateHistogram}_millisecond`; + handleGroupSelectionChange( + { + date_histogram: { + source_field: name, + target_field: targetField, + fixed_interval: "1ms", + }, + }, + TRANSFORM_AGG_TYPE.date_histogram, + targetField + ); + }, + }, + { + name: "Second", + onClick: () => { + const targetField = `${name}_${GROUP_TYPES.dateHistogram}_second`; + handleGroupSelectionChange( + { + date_histogram: { + source_field: name, + target_field: targetField, + fixed_interval: "1s", + }, + }, + TRANSFORM_AGG_TYPE.date_histogram, + targetField + ); + }, + }, + { + name: "Minute", + onClick: () => { + const targetField = `${name}_${GROUP_TYPES.dateHistogram}_minute`; + handleGroupSelectionChange( + { + date_histogram: { + source_field: name, + target_field: targetField, + fixed_interval: "1m", + }, + }, + TRANSFORM_AGG_TYPE.date_histogram, + targetField + ); + }, + }, + { + name: "Hour", + onClick: () => { + const targetField = `${name}_${GROUP_TYPES.dateHistogram}_hour`; + handleGroupSelectionChange( + { + date_histogram: { + source_field: name, + target_field: targetField, + fixed_interval: "1h", + }, + }, + TRANSFORM_AGG_TYPE.date_histogram, + targetField + ); + }, + }, + { + name: "Day", + onClick: () => { + const targetField = `${name}_${GROUP_TYPES.dateHistogram}_day`; + handleGroupSelectionChange( + { + date_histogram: { + source_field: name, + target_field: targetField, + calendar_interval: "1d", + }, + }, + TRANSFORM_AGG_TYPE.date_histogram, + targetField + ); + }, + }, + { + name: "Week", + onClick: () => { + const targetField = `${name}_${GROUP_TYPES.dateHistogram}_week`; + handleGroupSelectionChange( + { + date_histogram: { + source_field: name, + target_field: targetField, + calendar_interval: "1w", + }, + }, + TRANSFORM_AGG_TYPE.date_histogram, + targetField + ); + }, + }, + { + name: "Month", + onClick: () => { + const targetField = `${name}_${GROUP_TYPES.dateHistogram}_month`; + handleGroupSelectionChange( + { + date_histogram: { + source_field: name, + target_field: targetField, + calendar_interval: "1M", + }, + }, + TRANSFORM_AGG_TYPE.date_histogram, + targetField + ); + }, + }, + { + name: "Quarter", + onClick: () => { + const targetField = `${name}_${GROUP_TYPES.dateHistogram}_quarter`; + handleGroupSelectionChange( + { + date_histogram: { + source_field: name, + target_field: targetField, + calendar_interval: "1q", + }, + }, + TRANSFORM_AGG_TYPE.date_histogram, + targetField + ); + }, + }, + { + name: "Year", + onClick: () => { + const targetField = `${name}_${GROUP_TYPES.dateHistogram}_year`; + handleGroupSelectionChange( + { + date_histogram: { + source_field: name, + target_field: targetField, + calendar_interval: "1y", + }, + }, + TRANSFORM_AGG_TYPE.date_histogram, + targetField + ); + }, + }, + ], + }, + ]; + const textPanels: EuiContextMenuPanelDescriptor[] = [ + { + id: 0, + title: "Transform options", + items: [ + { + name: "No options available for text fields", + }, + ], + }, + ]; + const keywordPanels: EuiContextMenuPanelDescriptor[] = [ + { + id: 0, + title: "Transform options", + items: [ + { + name: "Group by terms", + onClick: () => { + const targetField = `${name}_${GROUP_TYPES.terms}`; + handleGroupSelectionChange( + { + terms: { + source_field: name, + target_field: targetField, + }, + }, + TRANSFORM_AGG_TYPE.terms, + targetField + ); + }, + }, + { + name: "Aggregate by count", + onClick: () => { + const aggItem: TransformAggItem = { + type: TRANSFORM_AGG_TYPE.value_count, + name: `count_${name}`, + item: { value_count: { field: name } }, + }; + aggSelection[`count_${name}`] = { + value_count: { field: name }, + }; + handleAggSelectionChange(aggItem); + }, + }, + ], + }, + ]; + + const button = setIsPopoverOpen(!isPopoverOpen)} />; + + return ( +
+ + {name} + + + + + + +
+ ); +} diff --git a/public/pages/CreateTransform/containers/CreateTransformForm/CreateTransformForm.tsx b/public/pages/CreateTransform/containers/CreateTransformForm/CreateTransformForm.tsx index c02be1485..a0b119ffb 100644 --- a/public/pages/CreateTransform/containers/CreateTransformForm/CreateTransformForm.tsx +++ b/public/pages/CreateTransform/containers/CreateTransformForm/CreateTransformForm.tsx @@ -20,10 +20,18 @@ import moment from "moment"; import { RollupService, TransformService } from "../../../../services"; import { BREADCRUMBS, ROUTES } from "../../../../utils/constants"; import IndexService from "../../../../services/IndexService"; -import { ManagedCatIndex, PreviewTransformResponse } from "../../../../../server/models/interfaces"; +import { ManagedCatIndex } from "../../../../../server/models/interfaces"; import CreateTransform from "../CreateTransform"; import CreateTransformStep2 from "../CreateTransformStep2"; -import { FieldItem, GroupItem, IndexItem, Transform, TransformGroupItem } from "../../../../../models/interfaces"; +import { + FieldItem, + GroupItem, + IndexItem, + Transform, + TRANSFORM_AGG_TYPE, + TransformAggItem, + TransformGroupItem, +} from "../../../../../models/interfaces"; import { getErrorMessage } from "../../../../utils/helpers"; import { EMPTY_TRANSFORM } from "../../utils/constants"; import CreateTransformStep3 from "../CreateTransformStep3"; @@ -69,6 +77,7 @@ interface CreateTransformFormState { selectedGroupField: TransformGroupItem[]; selectedAggregations: any; + aggList: TransformAggItem[]; aggregationsError: string; selectedFields: FieldItem[]; jobEnabledByDefault: boolean; @@ -109,6 +118,7 @@ export default class CreateTransformForm extends Component => { - let newJSON = this.state.transformJSON; + onGroupSelectionChange = async (selectedGroupField: GroupItem[], aggItem: TransformAggItem): Promise => { + const { aggList } = this.state; + aggList.push(aggItem); + this.updateGroup(); - if (selectedGroupField.length) newJSON.transform.groups = selectedGroupField; - this.setState({ selectedGroupField, transformJSON: newJSON }); - await this.previewTransform(newJSON); + let previewResponse = await this.previewTransform(this.state.transformJSON); + this.setState({ selectedGroupField }); }; - onAggregationSelectionChange = async (selectedAggregations: any): Promise => { - let newJSON = this.state.transformJSON; + onAggregationSelectionChange = async (selectedAggregations: any, aggItem: TransformAggItem): Promise => { + const { aggList } = this.state; + aggList.push(aggItem); + this.updateAggregation(); + + let previewResponse = await this.previewTransform(this.state.transformJSON); + this.setState({ selectedAggregations: selectedAggregations }); + }; + + onRemoveTransformation = async (name: string): Promise => { + const { aggList } = this.state; + const toRemoveIndex = aggList.findIndex((item) => { + return item.name === name; + }); + aggList.splice(toRemoveIndex, 1); + this.setState({ aggList }); - newJSON.transform.aggregations = selectedAggregations; - this.setState({ selectedAggregations: selectedAggregations, transformJSON: newJSON }); - await this.previewTransform(newJSON); + this.updateGroup(); + this.updateAggregation(); + await this.previewTransform(this.state.transformJSON); }; onChangeJobEnabledByDefault = (): void => { @@ -349,18 +374,35 @@ export default class CreateTransformForm extends Component { - const { transformJSON, selectedGroupField } = this.state; + const { transformJSON, aggList } = this.state; let newJSON = transformJSON; - - if (selectedGroupField.length) newJSON.transform.groups = selectedGroupField; - + let tempGroupSelect: GroupItem[] = []; + aggList.map((aggItem) => { + if ( + aggItem.type == TRANSFORM_AGG_TYPE.histogram || + aggItem.type == TRANSFORM_AGG_TYPE.terms || + aggItem.type == TRANSFORM_AGG_TYPE.date_histogram + ) + tempGroupSelect.push(aggItem.item); + }); + if (tempGroupSelect.length) newJSON.transform.groups = tempGroupSelect; this.setState({ transformJSON: newJSON }); }; updateAggregation = (): void => { - const { transformJSON, selectedAggregations } = this.state; + const { transformJSON, aggList } = this.state; let newJSON = transformJSON; - newJSON.transform.aggregations = selectedAggregations; + let aggJSON: any = {}; + aggList.map((aggItem) => { + // Form the final aggregation object with items with correct types from aggList + if ( + aggItem.type !== TRANSFORM_AGG_TYPE.histogram && + aggItem.type !== TRANSFORM_AGG_TYPE.terms && + aggItem.type !== TRANSFORM_AGG_TYPE.date_histogram + ) + aggJSON[aggItem.name] = aggItem.item; + }); + newJSON.transform.aggregations = aggJSON; this.setState({ transformJSON: newJSON }); }; @@ -428,6 +470,7 @@ export default class CreateTransformForm extends Component void; + onGroupSelectionChange: (selectedFields: TransformGroupItem[], aggItem: TransformAggItem) => void; selectedAggregations: any; - onAggregationSelectionChange: (selectedFields: any) => void; + aggList: TransformAggItem[]; + onAggregationSelectionChange: (selectedFields: any, aggItem: TransformAggItem) => void; + onRemoveTransformation: (name: string) => void; previewTransform: any[]; } @@ -60,6 +62,7 @@ export default class CreateTransformStep2 extends Component
diff --git a/public/pages/Transforms/containers/Transforms/EditTransform.tsx b/public/pages/Transforms/containers/Transforms/EditTransform.tsx index bcc8fd166..3f834f7eb 100644 --- a/public/pages/Transforms/containers/Transforms/EditTransform.tsx +++ b/public/pages/Transforms/containers/Transforms/EditTransform.tsx @@ -159,11 +159,7 @@ export default class EditTransform extends Component - + Date: Wed, 19 May 2021 15:59:29 -0500 Subject: [PATCH 78/93] Tables on create transform review page (#176) * Update Schedule component title size * Add isReadOnly prop to tables * Update preview table to support details page * Update ReviewDefinition.tsx --- .../DefineTransforms/DefineTransforms.tsx | 49 +++++++++++- .../PreviewTransform/PreviewTransform.tsx | 76 ++++++++++++------- .../ReviewDefinition/ReviewDefinition.tsx | 51 +++++++++++-- .../components/Schedule/Schedule.tsx | 13 +--- .../CreateTransformForm.tsx | 7 +- .../CreateTransformStep2.tsx | 1 + .../CreateTransformStep4.tsx | 15 ++-- .../Transforms/TransformSettings.tsx | 2 +- 8 files changed, 161 insertions(+), 53 deletions(-) diff --git a/public/pages/CreateTransform/components/DefineTransforms/DefineTransforms.tsx b/public/pages/CreateTransform/components/DefineTransforms/DefineTransforms.tsx index 4b44fbda2..2849857ec 100644 --- a/public/pages/CreateTransform/components/DefineTransforms/DefineTransforms.tsx +++ b/public/pages/CreateTransform/components/DefineTransforms/DefineTransforms.tsx @@ -60,7 +60,7 @@ export default function DefineTransforms({ fields.map((field: FieldItem) => { columns.push({ id: field.label, - display: ( + display: !isReadOnly && ( + +

Original fields with sample data

+
+ + + + +

Transformed fields preview based on sample data

+
+ + +
+ ); + return ( ); diff --git a/public/pages/CreateTransform/components/PreviewTransform/PreviewTransform.tsx b/public/pages/CreateTransform/components/PreviewTransform/PreviewTransform.tsx index c82a7bdb2..39183519f 100644 --- a/public/pages/CreateTransform/components/PreviewTransform/PreviewTransform.tsx +++ b/public/pages/CreateTransform/components/PreviewTransform/PreviewTransform.tsx @@ -27,6 +27,7 @@ interface PreviewTransformProps { selectedAggregations: any; onAggregationSelectionChange: (selectedFields: any, aggItem: TransformAggItem) => void; onRemoveTransformation: (name: string) => void; + isReadOnly: boolean; } export default function PreviewTransform({ @@ -37,6 +38,7 @@ export default function PreviewTransform({ aggList, onAggregationSelectionChange, onRemoveTransformation, + isReadOnly, }: PreviewTransformProps) { const [previewColumns, setPreviewColumns] = useState([]); const [visiblePreviewColumns, setVisiblePreviewColumns] = useState(() => previewColumns.map(({ id }) => id).slice(0, 5)); @@ -72,34 +74,54 @@ export default function PreviewTransform({ ); const updatePreviewColumns = (): void => { - if (aggList.length) { - let tempCol: EuiDataGridColumn[] = []; - aggList.map((aggItem) => { - tempCol.push({ - id: aggItem.name, - display: ( - - ), - actions: { - showHide: false, - showMoveLeft: false, - showMoveRight: false, - showSortAsc: false, - showSortDesc: false, - }, + if (isReadOnly) { + if (previewTransform.length) { + let tempCol: EuiDataGridColumn[] = []; + for (const [key, value] of Object.entries(previewTransform[0])) { + tempCol.push({ + id: key, + actions: { + showHide: false, + showMoveLeft: false, + showMoveRight: false, + showSortAsc: false, + showSortDesc: false, + }, + }); + } + setPreviewColumns(tempCol); + setVisiblePreviewColumns(() => tempCol.map(({ id }) => id).slice(0, 5)); + } + } else { + if (aggList.length) { + let tempCol: EuiDataGridColumn[] = []; + aggList.map((aggItem) => { + tempCol.push({ + id: aggItem.name, + display: !isReadOnly && ( + + ), + actions: { + showHide: false, + showMoveLeft: false, + showMoveRight: false, + showSortAsc: false, + showSortDesc: false, + }, + }); }); - }); - setPreviewColumns(tempCol); - setVisiblePreviewColumns(() => tempCol.map(({ id }) => id).slice(0, 5)); + setPreviewColumns(tempCol); + setVisiblePreviewColumns(() => tempCol.map(({ id }) => id).slice(0, 5)); + } } }; @@ -107,7 +129,7 @@ export default function PreviewTransform({ updatePreviewColumns(); }, [previewTransform, aggList]); - return aggList.length ? ( + return (!isReadOnly && aggList.length) || (isReadOnly && previewTransform.length) ? ( void; selectedAggregations: any; + aggList: TransformAggItem[]; + onAggregationSelectionChange: (selectedFields: any, aggItem: TransformAggItem) => void; + onRemoveTransformation: (name: string) => void; + previewTransform: any[]; onChangeStep: (step: number) => void; } @@ -30,10 +43,22 @@ export default class ReviewDefinition extends Component { super(props); } - - render() { - const { selectedGroupField, selectedAggregations, onChangeStep } = this.props; + const { + transformService, + notifications, + transformId, + sourceIndex, + fields, + selectedGroupField, + onGroupSelectionChange, + selectedAggregations, + aggList, + onAggregationSelectionChange, + onRemoveTransformation, + previewTransform, + onChangeStep, + } = this.props; const groupItems = () => { return selectedGroupField.map((group) => { @@ -79,7 +104,7 @@ export default class ReviewDefinition extends Component { interval: null, }; } - } + }; const aggItems = () => { return Object.keys(selectedAggregations).map((key) => { @@ -133,7 +158,21 @@ export default class ReviewDefinition extends Component {

Sample source index and transform result

} - >
+ > + +
); diff --git a/public/pages/CreateTransform/components/Schedule/Schedule.tsx b/public/pages/CreateTransform/components/Schedule/Schedule.tsx index 8acc4540c..50a03f9b9 100644 --- a/public/pages/CreateTransform/components/Schedule/Schedule.tsx +++ b/public/pages/CreateTransform/components/Schedule/Schedule.tsx @@ -49,17 +49,6 @@ interface ScheduleProps { onChangePage: (e: ChangeEvent) => void; } -const radios = [ - { - id: "no", - label: "No", - }, - { - id: "yes", - label: "Yes", - }, -]; - export default class Schedule extends Component { constructor(props: ScheduleProps) { super(props); @@ -79,7 +68,7 @@ export default class Schedule extends Component { onChangePage, } = this.props; return ( - +
{!isEdit && ( diff --git a/public/pages/CreateTransform/containers/CreateTransformStep4/CreateTransformStep4.tsx b/public/pages/CreateTransform/containers/CreateTransformStep4/CreateTransformStep4.tsx index bd5015755..03889aced 100644 --- a/public/pages/CreateTransform/containers/CreateTransformStep4/CreateTransformStep4.tsx +++ b/public/pages/CreateTransform/containers/CreateTransformStep4/CreateTransformStep4.tsx @@ -18,7 +18,7 @@ import { EuiSpacer, EuiTitle, EuiFlexGroup, EuiFlexItem, EuiComboBoxOptionOption import { RouteComponentProps } from "react-router-dom"; import { TransformService } from "../../../../services"; import { BREADCRUMBS, ROUTES } from "../../../../utils/constants"; -import { IndexItem, TransformGroupItem } from "../../../../../models/interfaces"; +import { FieldItem, IndexItem, TransformAggItem, TransformGroupItem } from "../../../../../models/interfaces"; import CreateTransformSteps from "../../components/CreateTransformSteps"; import JobNameAndIndices from "../../components/JobNameAndIndices"; import ReviewDefinition from "../../components/ReviewDefinition"; @@ -36,15 +36,20 @@ interface CreateTransformProps extends RouteComponentProps { targetIndex: { label: string; value?: IndexItem }[]; sourceIndexFilter: string; + jobEnabledByDefault: boolean; + pageSize: number; + fields: FieldItem[]; selectedGroupField: TransformGroupItem[]; + onGroupSelectionChange: (selectedFields: TransformGroupItem[], aggItem: TransformAggItem) => void; selectedAggregations: any; + aggList: TransformAggItem[]; + onAggregationSelectionChange: (selectedFields: any, aggItem: TransformAggItem) => void; + onRemoveTransformation: (name: string) => void; + previewTransform: any[]; timestamp: EuiComboBoxOptionOption[]; timezone: string; timeunit: string; - - jobEnabledByDefault: boolean; - pageSize: number; } export default class CreateTransformStep4 extends Component { @@ -77,7 +82,7 @@ export default class CreateTransformStep4 extends Component - + diff --git a/public/pages/Transforms/containers/Transforms/TransformSettings.tsx b/public/pages/Transforms/containers/Transforms/TransformSettings.tsx index 7cb54b8c3..a552a3bb4 100644 --- a/public/pages/Transforms/containers/Transforms/TransformSettings.tsx +++ b/public/pages/Transforms/containers/Transforms/TransformSettings.tsx @@ -155,7 +155,7 @@ export default class TransformSettings extends ComponentPreview result based on sample data - +
From 1116ef9020f96697e8140ffffa5869142318ed1f Mon Sep 17 00:00:00 2001 From: lobdelle <78931475+lobdelle@users.noreply.github.com> Date: Thu, 20 May 2021 11:08:24 -0700 Subject: [PATCH 79/93] Updated Pages per Execution helptext and spacing (#177) * Review aggregate and group on Create Transform now matches mock * Schedule page formatting changes and updated helpText * Adjusted further Schedule page spacing per QA meeting --- .../components/Schedule/Schedule.tsx | 16 ++++++++++++---- 1 file changed, 12 insertions(+), 4 deletions(-) diff --git a/public/pages/CreateTransform/components/Schedule/Schedule.tsx b/public/pages/CreateTransform/components/Schedule/Schedule.tsx index 50a03f9b9..6694809a7 100644 --- a/public/pages/CreateTransform/components/Schedule/Schedule.tsx +++ b/public/pages/CreateTransform/components/Schedule/Schedule.tsx @@ -70,6 +70,7 @@ export default class Schedule extends Component { return (
+ {!isEdit && ( { {/* TODO: Replace with switch block when define by cron expressions is supported. */} {selectInterval(interval, intervalTimeunit, intervalError, onChangeIntervalTime, onChangeIntervalTimeunit)} - - + + { } > + From fde5db4d73cca25673583c495ffd2fa60086dd4a Mon Sep 17 00:00:00 2001 From: AWSHurneyt <79280347+AWSHurneyt@users.noreply.github.com> Date: Thu, 20 May 2021 11:23:11 -0700 Subject: [PATCH 80/93] Refactored transform job status icons, and implemented error modal for displaying error messages. (#178) * Refactored UX based on feedback and to better align with mockups. * Refactored UX based on feedback and to better align with mockups. * Removed cron expression schedule option, reformatted popovers, and changed display condition for warning message. * Implemented inline error for duplicate job names. * Refactored transform job status icons, and implemented error modal for displaying error messages. * Rephrased the warning message displayed in step 4, and the toast displayed upon transform creation. * Adding spacer between accordion button and accordion body contents. --- .../ReviewDefinition/ReviewDefinition.tsx | 1 + .../CreateTransformForm.tsx | 4 +- .../CreateTransformStep3.tsx | 3 +- .../CreateTransformStep4.tsx | 2 +- public/pages/CreateTransform/utils/helpers.ts | 4 ++ .../components/ErrorModal/ErrorModal.tsx | 55 +++++++++++++++++++ .../Transforms/components/ErrorModal/index.ts | 18 ++++++ .../components/Schedule/Schedule.tsx | 1 + .../Transforms/TransformSettings.tsx | 2 +- .../containers/Transforms/Transforms.tsx | 2 +- .../pages/Transforms/utils/metadataHelper.tsx | 53 ++++++++---------- 11 files changed, 110 insertions(+), 35 deletions(-) create mode 100644 public/pages/Transforms/components/ErrorModal/ErrorModal.tsx create mode 100644 public/pages/Transforms/components/ErrorModal/index.ts diff --git a/public/pages/CreateTransform/components/ReviewDefinition/ReviewDefinition.tsx b/public/pages/CreateTransform/components/ReviewDefinition/ReviewDefinition.tsx index 39a92a159..be1f2eb32 100644 --- a/public/pages/CreateTransform/components/ReviewDefinition/ReviewDefinition.tsx +++ b/public/pages/CreateTransform/components/ReviewDefinition/ReviewDefinition.tsx @@ -159,6 +159,7 @@ export default class ReviewDefinition extends Component { } > + -

You can't change aggregations or metrics after creating a job. Double-check your choices before proceeding.

+

You can only change the description and schedule after creating a job. Double-check your choices before proceeding.

diff --git a/public/pages/CreateTransform/utils/helpers.ts b/public/pages/CreateTransform/utils/helpers.ts index 8e66b67fc..a68587ae0 100644 --- a/public/pages/CreateTransform/utils/helpers.ts +++ b/public/pages/CreateTransform/utils/helpers.ts @@ -61,3 +61,7 @@ export const parseFieldOptions = (prefix: string, mappings: any): FieldItem[] => } return fieldsOption; }; + +export const createdTransformToastMessage = (transformId: string): string => { + return `Transform job, ${transformId}, successfully created.`; +}; diff --git a/public/pages/Transforms/components/ErrorModal/ErrorModal.tsx b/public/pages/Transforms/components/ErrorModal/ErrorModal.tsx new file mode 100644 index 000000000..7e7b794ef --- /dev/null +++ b/public/pages/Transforms/components/ErrorModal/ErrorModal.tsx @@ -0,0 +1,55 @@ +/* + * Copyright 2021 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"). + * You may not use this file except in compliance with the License. + * A copy of the License is located at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * or in the "license" file accompanying this file. This file is distributed + * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either + * express or implied. See the License for the specific language governing + * permissions and limitations under the License. + */ + +import React from "react"; +import { + EuiButton, + EuiModal, + EuiModalBody, + EuiModalFooter, + EuiModalHeader, + EuiModalHeaderTitle, + EuiOverlayMask, + EuiText, +} from "@elastic/eui"; + +interface ErrorModalProps { + metadata: object; + onClose: () => void; +} + +const ErrorModal = ({ metadata, onClose }: ErrorModalProps) => ( + + {/* + // @ts-ignore */} + + + Error + + + + {metadata.transform_metadata.failure_reason} + + + + + Close + + + + +); + +export default ErrorModal; diff --git a/public/pages/Transforms/components/ErrorModal/index.ts b/public/pages/Transforms/components/ErrorModal/index.ts new file mode 100644 index 000000000..a3ed8e5b5 --- /dev/null +++ b/public/pages/Transforms/components/ErrorModal/index.ts @@ -0,0 +1,18 @@ +/* + * Copyright 2021 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"). + * You may not use this file except in compliance with the License. + * A copy of the License is located at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * or in the "license" file accompanying this file. This file is distributed + * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either + * express or implied. See the License for the specific language governing + * permissions and limitations under the License. + */ + +import ErrorModal from "./ErrorModal"; + +export default ErrorModal; diff --git a/public/pages/Transforms/components/Schedule/Schedule.tsx b/public/pages/Transforms/components/Schedule/Schedule.tsx index 400eb23ed..98f37a499 100644 --- a/public/pages/Transforms/components/Schedule/Schedule.tsx +++ b/public/pages/Transforms/components/Schedule/Schedule.tsx @@ -117,6 +117,7 @@ export default class Schedule extends Component { } > +
- + {/*// TODO: Use the source data preview table from create workflow */} {/* renderStatus(metadata), diff --git a/public/pages/Transforms/utils/metadataHelper.tsx b/public/pages/Transforms/utils/metadataHelper.tsx index eb9473685..c1ce56a20 100644 --- a/public/pages/Transforms/utils/metadataHelper.tsx +++ b/public/pages/Transforms/utils/metadataHelper.tsx @@ -16,52 +16,49 @@ import { TransformMetadata } from "../../../../models/interfaces"; import React, { ChangeEvent } from "react"; import moment from "moment-timezone"; -import { EuiFieldNumber, EuiFlexGroup, EuiFlexItem, EuiIcon, EuiFormRow, EuiSelect, EuiTextArea, EuiText } from "@elastic/eui"; +import { EuiFieldNumber, EuiFlexGroup, EuiFlexItem, EuiIcon, EuiFormRow, EuiLink, EuiSelect, EuiTextArea, EuiText } from "@elastic/eui"; import { ScheduleIntervalTimeunitOptions } from "../../CreateTransform/utils/constants"; +import ErrorModal from "../components/ErrorModal"; +import { ModalConsumer } from "../../../components/Modal"; // TODO: merge with rollup helper to have a common helper export const renderStatus = (metadata: TransformMetadata | undefined): JSX.Element => { if (metadata == null || metadata.transform_metadata == null) return
-
; - let icon; - let iconColor; - let textColor: "default" | "subdued" | "secondary" | "ghost" | "accent" | "warning" | "danger" | undefined; + + // Notes regarding color options: + // 'subdued' = a shade of black + // 'success' = a shade of green + // 'danger' = a shade of red + // '#DDDDDD' = a shade of grey + let iconColor: "subdued" | "success" | "danger" | "#DDDDDD" | undefined; let text; + switch (metadata.transform_metadata.status) { case "failed": - icon = "alert"; iconColor = "danger"; - textColor = "danger"; - text = "Failed: " + metadata.transform_metadata.failure_reason; - break; - case "finished": - icon = "check"; - iconColor = "success"; - textColor = "secondary"; - text = "Complete"; - break; - case "init": + text = "Error"; return ( - + - - Initializing + + {({ onShow }) => onShow(ErrorModal, { metadata })}>{text}} ); - case "started": - icon = "play"; + case "finished": iconColor = "success"; - textColor = "secondary"; - text = "Started"; + text = "Complete"; break; - case "stopped": - icon = "stop"; + case "init" || "started": iconColor = "subdued"; - textColor = "subdued"; + text = "Initializing..."; + break; + case "stopped": + iconColor = "#DDDDDD"; text = "Stopped"; break; default: @@ -71,12 +68,10 @@ export const renderStatus = (metadata: TransformMetadata | undefined): JSX.Eleme return ( - + - - {text} - + {text} ); From 824ed48b59be000d1c76bf2c283b400d8e512496 Mon Sep 17 00:00:00 2001 From: Annie Lee <71157062+leeyun-amzn@users.noreply.github.com> Date: Thu, 20 May 2021 13:50:14 -0500 Subject: [PATCH 81/93] Text modification and parse date fields on create step 2 (#179) * Update Index filter popover text and clear aggList when source index is changed * Clean up code and add helptext to preview table * Modify target field name for histogram * Parse date histogram render in step 2 tables --- .../DefineTransforms/DefineTransforms.tsx | 141 ++---------------- .../IndexFilterPopover/IndexFilterPopover.tsx | 10 +- .../PreviewTransform/PreviewTransform.tsx | 30 +--- .../Panels/HistogramPanel/HistogramPanel.tsx | 2 +- .../CreateTransformForm.tsx | 1 + 5 files changed, 26 insertions(+), 158 deletions(-) diff --git a/public/pages/CreateTransform/components/DefineTransforms/DefineTransforms.tsx b/public/pages/CreateTransform/components/DefineTransforms/DefineTransforms.tsx index 2849857ec..95d269c49 100644 --- a/public/pages/CreateTransform/components/DefineTransforms/DefineTransforms.tsx +++ b/public/pages/CreateTransform/components/DefineTransforms/DefineTransforms.tsx @@ -23,6 +23,7 @@ import { getErrorMessage } from "../../../../utils/helpers"; import PreviewTransform from "../PreviewTransform"; import TransformOptions from "../TransformOptions"; import { DefaultSampleDataSize } from "../../utils/constants"; +import { renderTime } from "../../../Transforms/utils/helpers"; interface DefineTransformsProps { transformService: TransformService; @@ -78,135 +79,6 @@ export default function DefineTransforms({ showMoveRight: false, showSortAsc: false, showSortDesc: false, - // additional: [ - // { - // label: "Group by histogram ", - // onClick: () => { - // const targetFieldName = `${field.label}_${GROUP_TYPES.histogram}`; - // groupSelection.push({ - // histogram: { - // source_field: field.label, - // target_field: targetFieldName, - // interval: 5, - // }, - // }); - // onGroupSelectionChange(groupSelection); - // }, - // size: "xs", - // color: isNumeric ? "text" : "subdued", - // }, - // { - // label: "Group by date histogram ", - // onClick: () => { - // groupSelection.push({ - // date_histogram: { - // source_field: field.label, - // target_field: `${field.label}_${GROUP_TYPES.dateHistogram}`, - // calendar_interval: "1d", - // }, - // }); - // onGroupSelectionChange(groupSelection); - // }, - // size: "xs", - // color: isDate ? "text" : "subdued", - // }, - // { - // label: "Group by terms ", - // onClick: () => { - // groupSelection.push({ - // terms: { - // source_field: field.label, - // target_field: `${field.label}_${GROUP_TYPES.terms}`, - // }, - // }); - // onGroupSelectionChange(groupSelection); - // }, - // size: "xs", - // color: "text", - // }, - // { - // label: "Aggregate by sum ", - // onClick: () => { - // aggSelection[`sum_${field.label}`] = { - // sum: { field: field.label }, - // }; - // onAggregationSelectionChange(aggSelection); - // }, - // size: "xs", - // color: "text", - // }, - // { - // label: "Aggregate by max ", - // onClick: () => { - // aggSelection[`max_${field.label}`] = { - // max: { field: field.label }, - // }; - // onAggregationSelectionChange(aggSelection); - // }, - // size: "xs", - // color: "text", - // }, - // { - // label: "Aggregate by min ", - // onClick: () => { - // aggSelection[`min_${field.label}`] = { - // min: { field: field.label }, - // }; - // onAggregationSelectionChange(aggSelection); - // }, - // size: "xs", - // color: "text", - // }, - // { - // label: "Aggregate by avg ", - // onClick: () => { - // aggSelection[`avg_${field.label}`] = { - // avg: { field: field.label }, - // }; - // onAggregationSelectionChange(aggSelection); - // }, - // size: "xs", - // color: "text", - // }, - // { - // label: "Aggregate by count ", - // onClick: () => { - // aggSelection[`count_${field.label}`] = { - // value_count: { field: field.label }, - // }; - // onAggregationSelectionChange(aggSelection); - // }, - // size: "xs", - // color: "text", - // }, - // { - // label: "Aggregate by percentile", - // onClick: () => { - // aggSelection[`percentiles_${field.label}`] = { - // percentiles: { field: field.label, percents: [1, 5, 25, 99] }, - // }; - // onAggregationSelectionChange(aggSelection); - // }, - // size: "xs", - // color: "text", - // }, - // { - // label: "Aggregate by scripted metrics ", - // onClick: () => { - // aggSelection[`scripted_metric_${field.label}`] = { - // scripted_metric: { - // init_script: "", - // map_script: "", - // combine_script: "", - // reduce_script: "", - // }, - // }; - // onAggregationSelectionChange(aggSelection); - // }, - // size: "xs", - // color: "text", - // }, - // ], }, }); }); @@ -270,11 +142,12 @@ export default function DefineTransforms({ const renderCellValue = ({ rowIndex, columnId }) => { if (!loading && data.hasOwnProperty(rowIndex)) { - // TODO: work on truncating the value to certain length defined by the keyword field if (columns?.find((column) => column.id == columnId).schema == "keyword") { // Remove the keyword postfix for getting correct data from array const correspondingTextColumnId = columnId.replace(".keyword", ""); return data[rowIndex]._source[correspondingTextColumnId] ? data[rowIndex]._source[correspondingTextColumnId] : "-"; + } else if (columns?.find((column) => column.id == columnId).schema == "date") { + return data[rowIndex]._source[columnId] ? renderTime(data[rowIndex]._source[columnId]) : "-"; } return data[rowIndex]._source[columnId] ? data[rowIndex]._source[columnId] : "-"; } @@ -309,7 +182,7 @@ export default function DefineTransforms({ showFullScreenSelector: false, }} /> - +

Transformed fields preview based on sample data

@@ -380,11 +253,15 @@ export default function DefineTransforms({ showFullScreenSelector: false, }} /> - +

Transformed fields preview based on sample data

+ +

This fields preview displays only the first 10 results of your transform job.

+
+ void; } -export default function IndexFilterPopover({ sourceIndex, fields, sourceIndexFilter, onChangeSourceIndexFilter, closePopover }: IndexFilterPopoverProps) { +export default function IndexFilterPopover({ + sourceIndex, + fields, + sourceIndexFilter, + onChangeSourceIndexFilter, + closePopover, +}: IndexFilterPopoverProps) { const [selectedField, setSelectedField] = useState(""); const [selectedOperator, setSelectedOperator] = useState(""); const [selectedValue, setSelectedValue] = useState(""); @@ -95,7 +101,7 @@ export default function IndexFilterPopover({ sourceIndex, fields, sourceIndexFil function customEditor() { return ( - + setQueryDsl(string)} mode="json" width="100%" height="250px" /> ); diff --git a/public/pages/CreateTransform/components/PreviewTransform/PreviewTransform.tsx b/public/pages/CreateTransform/components/PreviewTransform/PreviewTransform.tsx index 39183519f..bb23470a3 100644 --- a/public/pages/CreateTransform/components/PreviewTransform/PreviewTransform.tsx +++ b/public/pages/CreateTransform/components/PreviewTransform/PreviewTransform.tsx @@ -18,6 +18,7 @@ import { EuiDataGrid, EuiDataGridColumn } from "@elastic/eui"; import PreviewEmptyPrompt from "../PreviewEmptyPrompt"; import PreviewOptions from "../PreviewOptions"; import { TransformAggItem, TransformGroupItem } from "../../../../../models/interfaces"; +import { renderTime } from "../../../Transforms/utils/helpers"; interface PreviewTransformProps { previewTransform: any[]; @@ -47,6 +48,12 @@ export default function PreviewTransform({ const renderPreviewCellValue = ({ rowIndex, columnId }) => { if (previewTransform.hasOwnProperty(rowIndex)) { if (previewTransform[rowIndex][columnId]) { + // Case for date histogram type + //TODO: Check if there's a better way to check for date histogram types + if (columnId.includes("date_histogram")) { + return renderTime(previewTransform[rowIndex][columnId]); + } + // Case for percentile return typeof previewTransform[rowIndex][columnId] !== ("string" || "number") ? JSON.stringify(previewTransform[rowIndex][columnId]) @@ -55,23 +62,6 @@ export default function PreviewTransform({ } return "-"; }; - const onChangePreviewPerPage = useCallback( - (pageSize) => { - setPreviewPagination((previewPagination) => ({ - ...previewPagination, - pageSize, - pageIndex: 0, - })); - }, - [setPreviewPagination] - ); - - const onChangePreviewPage = useCallback( - (pageIndex) => { - setPreviewPagination((previewPagination) => ({ ...previewPagination, pageIndex })); - }, - [setPreviewPagination] - ); const updatePreviewColumns = (): void => { if (isReadOnly) { @@ -136,12 +126,6 @@ export default function PreviewTransform({ columnVisibility={{ visibleColumns: visiblePreviewColumns, setVisibleColumns: setVisiblePreviewColumns }} rowCount={previewTransform.length} renderCellValue={renderPreviewCellValue} - // pagination={{ - // ...previewPagination, - // pageSizeOptions: [5, 10, 20, 50], - // onChangeItemsPerPage: onChangePreviewPerPage, - // onChangePage: onChangePreviewPage, - // }} toolbarVisibility={{ showColumnSelector: true, showStyleSelector: false, diff --git a/public/pages/CreateTransform/components/TransformOptions/Panels/HistogramPanel/HistogramPanel.tsx b/public/pages/CreateTransform/components/TransformOptions/Panels/HistogramPanel/HistogramPanel.tsx index d325893ed..3a8d61568 100644 --- a/public/pages/CreateTransform/components/TransformOptions/Panels/HistogramPanel/HistogramPanel.tsx +++ b/public/pages/CreateTransform/components/TransformOptions/Panels/HistogramPanel/HistogramPanel.tsx @@ -48,7 +48,7 @@ export default function HistogramPanel({ name, handleGroupSelectionChange, aggLi fill fullWidth={false} onClick={() => { - const targetFieldName = `${name} _${GROUP_TYPES.histogram}`; + const targetFieldName = `${name} _${GROUP_TYPES.histogram}_${histogramInterval}`; handleGroupSelectionChange( { histogram: { diff --git a/public/pages/CreateTransform/containers/CreateTransformForm/CreateTransformForm.tsx b/public/pages/CreateTransform/containers/CreateTransformForm/CreateTransformForm.tsx index a7d9e96f5..520b6274f 100644 --- a/public/pages/CreateTransform/containers/CreateTransformForm/CreateTransformForm.tsx +++ b/public/pages/CreateTransform/containers/CreateTransformForm/CreateTransformForm.tsx @@ -273,6 +273,7 @@ export default class CreateTransformForm extends Component Date: Thu, 20 May 2021 15:02:59 -0500 Subject: [PATCH 82/93] Modify filter definition text from add to edit --- .../components/IndexFilterPopover/IndexFilterPopover.tsx | 2 +- .../components/TransformIndices/TransformIndices.tsx | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/public/pages/CreateTransform/components/IndexFilterPopover/IndexFilterPopover.tsx b/public/pages/CreateTransform/components/IndexFilterPopover/IndexFilterPopover.tsx index 081a84c70..5e2fc72b5 100644 --- a/public/pages/CreateTransform/components/IndexFilterPopover/IndexFilterPopover.tsx +++ b/public/pages/CreateTransform/components/IndexFilterPopover/IndexFilterPopover.tsx @@ -111,7 +111,7 @@ export default function IndexFilterPopover({
- Add data filter + Edit data filter {/**/} {/* setIsCustomEditorOpen(!isCustomEditorOpen)}>*/} {/* {isCustomEditorOpen ? "Edit filter values" : "Custom expression"}*/} diff --git a/public/pages/CreateTransform/components/TransformIndices/TransformIndices.tsx b/public/pages/CreateTransform/components/TransformIndices/TransformIndices.tsx index b1ab92ab0..86d596c47 100644 --- a/public/pages/CreateTransform/components/TransformIndices/TransformIndices.tsx +++ b/public/pages/CreateTransform/components/TransformIndices/TransformIndices.tsx @@ -170,7 +170,7 @@ export default class TransformIndices extends Component this.onButtonClick()} data-test-subj="addFilter"> - + Add data filter + Edit data filter ); @@ -250,7 +250,7 @@ export default class TransformIndices extends Component - + Add data filter + Edit data filter } isOpen={isPopoverOpen} From 2877ad8c758abda5ad1c161d41dfdaaed21fb292 Mon Sep 17 00:00:00 2001 From: AWSHurneyt <79280347+AWSHurneyt@users.noreply.github.com> Date: Thu, 20 May 2021 19:09:55 -0700 Subject: [PATCH 83/93] Refactored padding around tables, wrapped job names in quotation marks, resized accordion titles, and some other cleanup. (#181) * Refactored UX based on feedback and to better align with mockups. * Refactored UX based on feedback and to better align with mockups. * Removed cron expression schedule option, reformatted popovers, and changed display condition for warning message. * Implemented inline error for duplicate job names. * Refactored transform job status icons, and implemented error modal for displaying error messages. * Rephrased the warning message displayed in step 4, and the toast displayed upon transform creation. * Adding spacer between accordion button and accordion body contents. * Refactored padding around tables, wrapped job names in quotation marks, resized accordion titles, and some other cleanup. --- .../DefineTransforms/DefineTransforms.tsx | 14 ++--- .../ReviewDefinition/ReviewDefinition.tsx | 41 +++++++------- .../components/Schedule/Schedule.tsx | 9 +--- .../CreateTransformStep2.tsx | 2 +- public/pages/CreateTransform/utils/helpers.ts | 15 +++++- .../components/ErrorModal/ErrorModal.tsx | 5 +- .../components/Schedule/Schedule.tsx | 9 +--- .../Transforms/TransformDetails.tsx | 10 ++-- .../Transforms/TransformSettings.tsx | 54 +++++++++---------- .../containers/Transforms/Transforms.tsx | 8 +-- 10 files changed, 79 insertions(+), 88 deletions(-) diff --git a/public/pages/CreateTransform/components/DefineTransforms/DefineTransforms.tsx b/public/pages/CreateTransform/components/DefineTransforms/DefineTransforms.tsx index 95d269c49..e6c6715b0 100644 --- a/public/pages/CreateTransform/components/DefineTransforms/DefineTransforms.tsx +++ b/public/pages/CreateTransform/components/DefineTransforms/DefineTransforms.tsx @@ -159,7 +159,7 @@ export default function DefineTransforms({ return (
-

Original fields with sample data

+
Original fields with sample data
- + -

Transformed fields preview based on sample data

+
Transformed fields preview based on sample data
// } - bodyStyles={{ padding: "10px 10px" }} + bodyStyles={{ padding: "20px 20px" }} title="Select fields to transform" titleSize="m" > -

Original fields with sample data

+
Original fields with sample data
{/*TODO: Substitute "source index", and "filtered by" fields with actual values*/} @@ -253,9 +253,9 @@ export default function DefineTransforms({ showFullScreenSelector: false, }} /> - + -

Transformed fields preview based on sample data

+
Transformed fields preview based on sample data
diff --git a/public/pages/CreateTransform/components/ReviewDefinition/ReviewDefinition.tsx b/public/pages/CreateTransform/components/ReviewDefinition/ReviewDefinition.tsx index be1f2eb32..1839725a7 100644 --- a/public/pages/CreateTransform/components/ReviewDefinition/ReviewDefinition.tsx +++ b/public/pages/CreateTransform/components/ReviewDefinition/ReviewDefinition.tsx @@ -144,35 +144,30 @@ export default class ReviewDefinition extends Component { title="Define transforms" titleSize="m" > -
+
{groupItems()} {aggItems()} - -

Sample source index and transform result

- - } - > - - + +
+ + +
diff --git a/public/pages/CreateTransform/components/Schedule/Schedule.tsx b/public/pages/CreateTransform/components/Schedule/Schedule.tsx index 6694809a7..86e04e203 100644 --- a/public/pages/CreateTransform/components/Schedule/Schedule.tsx +++ b/public/pages/CreateTransform/components/Schedule/Schedule.tsx @@ -94,14 +94,7 @@ export default class Schedule extends Component { {selectInterval(interval, intervalTimeunit, intervalError, onChangeIntervalTime, onChangeIntervalTimeunit)} - -

Advanced

- - } - > + +
diff --git a/public/pages/CreateTransform/utils/helpers.ts b/public/pages/CreateTransform/utils/helpers.ts index a68587ae0..7ea24c8f0 100644 --- a/public/pages/CreateTransform/utils/helpers.ts +++ b/public/pages/CreateTransform/utils/helpers.ts @@ -63,5 +63,18 @@ export const parseFieldOptions = (prefix: string, mappings: any): FieldItem[] => }; export const createdTransformToastMessage = (transformId: string): string => { - return `Transform job, ${transformId}, successfully created.`; + return `Transform job "${transformId}" successfully created.`; +}; + +/** + * Searches the stringToSearch for all occurrences of the transformId, + * and wraps each occurrence with opening and closing quotation marks. + * @param transformId The string that serves as the transform's job name. + * @param stringToSearch The string to search. + * @return The stringToSearch but with any occurrences of transformId wrapped in quotation marks. + */ +export const wrapQuotesAroundTransformId = (transformId: string, stringToSearch: string): string => { + const regex = new RegExp(transformId, "g"); + const idWrappedWithQuotes = `"${transformId}"`; + return stringToSearch.replace(regex, idWrappedWithQuotes); }; diff --git a/public/pages/Transforms/components/ErrorModal/ErrorModal.tsx b/public/pages/Transforms/components/ErrorModal/ErrorModal.tsx index 7e7b794ef..c6e511866 100644 --- a/public/pages/Transforms/components/ErrorModal/ErrorModal.tsx +++ b/public/pages/Transforms/components/ErrorModal/ErrorModal.tsx @@ -24,6 +24,7 @@ import { EuiOverlayMask, EuiText, } from "@elastic/eui"; +import { wrapQuotesAroundTransformId } from "../../../CreateTransform/utils/helpers"; interface ErrorModalProps { metadata: object; @@ -40,7 +41,9 @@ const ErrorModal = ({ metadata, onClose }: ErrorModalProps) => ( - {metadata.transform_metadata.failure_reason} + + {wrapQuotesAroundTransformId(metadata.transform_metadata.transform_id, metadata.transform_metadata.failure_reason)} + diff --git a/public/pages/Transforms/components/Schedule/Schedule.tsx b/public/pages/Transforms/components/Schedule/Schedule.tsx index 98f37a499..c80a1cdc8 100644 --- a/public/pages/Transforms/components/Schedule/Schedule.tsx +++ b/public/pages/Transforms/components/Schedule/Schedule.tsx @@ -109,14 +109,7 @@ export default class Schedule extends Component { - -

Advanced

- - } - > +
- -

Sample source index and transform result

- - } - onClick={this.onClick} - > -
- +
+ +
+ - {/*// TODO: Use the source data preview table from create workflow */} - {/*()}*/} - {/* selectedAggregations={{}}*/} - {/* onAggregationSelectionChange={()=> ()}*/} - {/* previewTransform={[]}*/} - {/*/>*/} - -

Preview result based on sample data

-
- - -
-
+ {/*// TODO: Use the source data preview table from create workflow */} + {/*()}*/} + {/* selectedAggregations={{}}*/} + {/* onAggregationSelectionChange={()=> ()}*/} + {/* previewTransform={[]}*/} + {/*/>*/} + +
Preview result based on sample data
+
+ + +
+ +
); } diff --git a/public/pages/Transforms/containers/Transforms/Transforms.tsx b/public/pages/Transforms/containers/Transforms/Transforms.tsx index 884bb3227..96be48d2d 100644 --- a/public/pages/Transforms/containers/Transforms/Transforms.tsx +++ b/public/pages/Transforms/containers/Transforms/Transforms.tsx @@ -390,7 +390,7 @@ export default class Transforms extends Component Date: Thu, 20 May 2021 19:14:31 -0700 Subject: [PATCH 84/93] Fixed change source index warning conditional and added padding (#180) * Fixed change source index warning conditional and added padding * Updated aggregations check to use aggList and added right side padding * Further adjusted right side warning padding --- .../components/TransformIndices/TransformIndices.tsx | 4 ++-- .../containers/CreateTransformForm/CreateTransformForm.tsx | 4 +++- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/public/pages/CreateTransform/components/TransformIndices/TransformIndices.tsx b/public/pages/CreateTransform/components/TransformIndices/TransformIndices.tsx index 86d596c47..051e01ae2 100644 --- a/public/pages/CreateTransform/components/TransformIndices/TransformIndices.tsx +++ b/public/pages/CreateTransform/components/TransformIndices/TransformIndices.tsx @@ -181,6 +181,7 @@ export default class TransformIndices extends Component +
{hasAggregation && ( @@ -189,8 +190,7 @@ export default class TransformIndices extends Component )} -
- + From 4386652a79940d2760ae43a17d520d4ebfd0f04e Mon Sep 17 00:00:00 2001 From: lobdelle <78931475+lobdelle@users.noreply.github.com> Date: Thu, 20 May 2021 19:15:12 -0700 Subject: [PATCH 85/93] Changed clear source index filter to empty string instead of curly braces, updated function to delete DSQ property (#182) --- .../TransformIndices/TransformIndices.tsx | 2 +- .../CreateTransformForm/CreateTransformForm.tsx | 16 ++++++++++------ 2 files changed, 11 insertions(+), 7 deletions(-) diff --git a/public/pages/CreateTransform/components/TransformIndices/TransformIndices.tsx b/public/pages/CreateTransform/components/TransformIndices/TransformIndices.tsx index 051e01ae2..34bff7496 100644 --- a/public/pages/CreateTransform/components/TransformIndices/TransformIndices.tsx +++ b/public/pages/CreateTransform/components/TransformIndices/TransformIndices.tsx @@ -175,7 +175,7 @@ export default class TransformIndices extends Component { - onChangeSourceIndexFilter("{}"); + onChangeSourceIndexFilter(""); }; return ( diff --git a/public/pages/CreateTransform/containers/CreateTransformForm/CreateTransformForm.tsx b/public/pages/CreateTransform/containers/CreateTransformForm/CreateTransformForm.tsx index 4d3a2c2fe..a40bea814 100644 --- a/public/pages/CreateTransform/containers/CreateTransformForm/CreateTransformForm.tsx +++ b/public/pages/CreateTransform/containers/CreateTransformForm/CreateTransformForm.tsx @@ -279,14 +279,18 @@ export default class CreateTransformForm extends Component { + onChangeSourceIndexFilter = (newFilter: string): void => { let newJSON = this.state.transformJSON; - try { - newJSON.transform.data_selection_query = JSON.parse(sourceIndexFilter); - } catch (err) { - this.context.notifications.toasts.addDanger('Invalid source index filter JSON: "' + sourceIndexFilter + '"'); + if (newFilter == "") { + newJSON.transform.hasOwnProperty("data_selection_query") && delete newJSON.transform.data_selection_query + } else { + try { + newJSON.transform.data_selection_query = JSON.parse(newFilter); + } catch (err) { + this.context.notifications.toasts.addDanger('Invalid source index filter JSON: "' + newFilter + '"'); + } } - this.setState({ sourceIndexFilter: sourceIndexFilter, transformJSON: newJSON }); + this.setState({ sourceIndexFilter: newFilter, transformJSON: newJSON }); }; onChangeTargetIndex = (options: EuiComboBoxOptionOption[]): void => { From c0738f2849167670568a255194c015f17ece791c Mon Sep 17 00:00:00 2001 From: Annie Lee <71157062+leeyun-amzn@users.noreply.github.com> Date: Thu, 20 May 2021 21:15:55 -0500 Subject: [PATCH 86/93] Modify components on create transform step 2 (#183) * Update Index filter popover text and clear aggList when source index is changed * Clean up code and add helptext to preview table * Modify target field name for histogram * Parse date histogram render in step 2 tables * Add horizontal scroll to define transform table and remove highlight when is readonly * Remove highlight on preview table when is read-only * Add horizontal scroll to preview table * Show all preview columns by default * Modify delete icon * Truncate field text * Truncate text on preview columns * Add tooltip to column headers * Add tooltip to readonly tables * Update DefineTransforms.tsx --- .../DefineTransforms/DefineTransforms.tsx | 15 +++++++++++-- .../PreviewOptions/PreviewOptions.tsx | 21 ++++++++++++++++--- .../PreviewTransform/PreviewTransform.tsx | 17 ++++++++++++--- .../TransformOptions/TransformOptions.tsx | 19 +++++++++++++++-- .../CreateTransformStep2.tsx | 2 +- .../CreateTransformStep4.tsx | 2 +- public/pages/Main/Main.tsx | 3 ++- 7 files changed, 66 insertions(+), 13 deletions(-) diff --git a/public/pages/CreateTransform/components/DefineTransforms/DefineTransforms.tsx b/public/pages/CreateTransform/components/DefineTransforms/DefineTransforms.tsx index e6c6715b0..2b2fc6906 100644 --- a/public/pages/CreateTransform/components/DefineTransforms/DefineTransforms.tsx +++ b/public/pages/CreateTransform/components/DefineTransforms/DefineTransforms.tsx @@ -13,7 +13,7 @@ * permissions and limitations under the License. */ -import { EuiDataGrid, EuiDataGridColumn, EuiSpacer, EuiText } from "@elastic/eui"; +import { EuiDataGrid, EuiDataGridColumn, EuiSpacer, EuiText, EuiToolTip } from "@elastic/eui"; import { CoreStart } from "kibana/public"; import React, { useCallback, useState } from "react"; import { ContentPanel } from "../../../../components/ContentPanel"; @@ -61,7 +61,15 @@ export default function DefineTransforms({ fields.map((field: FieldItem) => { columns.push({ id: field.label, - display: !isReadOnly && ( + display: isReadOnly ? ( +
+ + + {field.label} + + +
+ ) : ( @@ -233,6 +243,7 @@ export default function DefineTransforms({

{`Viewing sample data from index ${sourceIndex}`}

+ setIsPopoverOpen(!isPopoverOpen)} />; + const button = setIsPopoverOpen(!isPopoverOpen)} />; const panels: EuiContextMenuPanelDescriptor[] = [ { @@ -64,7 +73,13 @@ export default function PreviewOptions({ return (
- {name} + + + + {name} + + + + + + {key} + + +
+ ), actions: { showHide: false, showMoveLeft: false, @@ -88,7 +97,7 @@ export default function PreviewTransform({ aggList.map((aggItem) => { tempCol.push({ id: aggItem.name, - display: !isReadOnly && ( + display: ( tempCol.map(({ id }) => id).slice(0, 5)); + setVisiblePreviewColumns(() => tempCol.map(({ id }) => id)); } } }; @@ -121,6 +130,7 @@ export default function PreviewTransform({ return (!isReadOnly && aggList.length) || (isReadOnly && previewTransform.length) ? ( ) : ( diff --git a/public/pages/CreateTransform/components/TransformOptions/TransformOptions.tsx b/public/pages/CreateTransform/components/TransformOptions/TransformOptions.tsx index d26ff86e0..9b7a8aa5d 100644 --- a/public/pages/CreateTransform/components/TransformOptions/TransformOptions.tsx +++ b/public/pages/CreateTransform/components/TransformOptions/TransformOptions.tsx @@ -14,7 +14,16 @@ */ import React, { useState } from "react"; -import { EuiButtonIcon, EuiContextMenu, EuiContextMenuPanelDescriptor, EuiFlexGroup, EuiFlexItem, EuiPopover } from "@elastic/eui"; +import { + EuiButtonIcon, + EuiContextMenu, + EuiContextMenuPanelDescriptor, + EuiFlexGroup, + EuiFlexItem, + EuiPopover, + EuiText, + EuiToolTip, +} from "@elastic/eui"; import { isNumericMapping } from "../../utils/helpers"; import { GROUP_TYPES, TRANSFORM_AGG_TYPE, TransformAggItem, TransformGroupItem } from "../../../../../models/interfaces"; import HistogramPanel from "./Panels/HistogramPanel"; @@ -418,7 +427,13 @@ export default function TransformOptions({ return (
- {name} + + + + {name} + + + - +

Define transform

diff --git a/public/pages/CreateTransform/containers/CreateTransformStep4/CreateTransformStep4.tsx b/public/pages/CreateTransform/containers/CreateTransformStep4/CreateTransformStep4.tsx index 14d5c04e3..c8fac9b7e 100644 --- a/public/pages/CreateTransform/containers/CreateTransformStep4/CreateTransformStep4.tsx +++ b/public/pages/CreateTransform/containers/CreateTransformStep4/CreateTransformStep4.tsx @@ -75,7 +75,7 @@ export default class CreateTransformStep4 extends Component
- +

Review and create

diff --git a/public/pages/Main/Main.tsx b/public/pages/Main/Main.tsx index 1022492b3..3e16f12d0 100644 --- a/public/pages/Main/Main.tsx +++ b/public/pages/Main/Main.tsx @@ -108,11 +108,12 @@ export default class Main extends Component { services && ( - + {/*Hide side navigation bar when creating or editing rollup job*/} {pathname != ROUTES.CREATE_ROLLUP && pathname != ROUTES.EDIT_ROLLUP && pathname != ROUTES.ROLLUP_DETAILS && + pathname != ROUTES.CREATE_TRANSFORM && pathname != ROUTES.EDIT_TRANSFORM && pathname != ROUTES.TRANSFORM_DETAILS && ( From ddaf57b44c6cc76f7e29825ec7a4891aef875aaf Mon Sep 17 00:00:00 2001 From: Annie Date: Thu, 20 May 2021 21:38:15 -0500 Subject: [PATCH 87/93] Modify paddings --- .../ConfigureTransform/ConfigureTransform.tsx | 5 ++-- .../DefineTransforms/DefineTransforms.tsx | 3 ++- .../JobNameAndIndices/JobNameAndIndices.tsx | 13 ++++------ .../ReviewDefinition/ReviewDefinition.tsx | 4 ++-- .../ReviewSchedule/ReviewSchedule.tsx | 17 +++++-------- .../components/Schedule/Schedule.tsx | 7 +++--- .../TransformIndices/TransformIndices.tsx | 24 +++++++++---------- 7 files changed, 30 insertions(+), 43 deletions(-) diff --git a/public/pages/CreateTransform/components/ConfigureTransform/ConfigureTransform.tsx b/public/pages/CreateTransform/components/ConfigureTransform/ConfigureTransform.tsx index db5eca2e3..4df0d5fd2 100644 --- a/public/pages/CreateTransform/components/ConfigureTransform/ConfigureTransform.tsx +++ b/public/pages/CreateTransform/components/ConfigureTransform/ConfigureTransform.tsx @@ -34,9 +34,8 @@ const ConfigureTransform = ({ onChangeDescription, description, }: ConfigureTransformProps) => ( - -
- + +
// } - bodyStyles={{ padding: "20px 20px" }} + panelStyles={{ padding: "20px 20px" }} + bodyStyles={{ padding: "10px" }} title="Select fields to transform" titleSize="m" > diff --git a/public/pages/CreateTransform/components/JobNameAndIndices/JobNameAndIndices.tsx b/public/pages/CreateTransform/components/JobNameAndIndices/JobNameAndIndices.tsx index a07fb6191..dd49d16df 100644 --- a/public/pages/CreateTransform/components/JobNameAndIndices/JobNameAndIndices.tsx +++ b/public/pages/CreateTransform/components/JobNameAndIndices/JobNameAndIndices.tsx @@ -34,12 +34,7 @@ export default class JobNameAndIndices extends Component } render() { - const { transformId, - description, - onChangeStep, - sourceIndex, - targetIndex, - sourceIndexFilter } = this.props; + const { transformId, description, onChangeStep, sourceIndex, targetIndex, sourceIndexFilter } = this.props; return ( )} } - bodyStyles={{ padding: "initial" }} + panelStyles={{ padding: "20px 20px" }} + bodyStyles={{ padding: "10px" }} title="Set up indices" titleSize="m" > -
- +
diff --git a/public/pages/CreateTransform/components/ReviewDefinition/ReviewDefinition.tsx b/public/pages/CreateTransform/components/ReviewDefinition/ReviewDefinition.tsx index 1839725a7..b84e95964 100644 --- a/public/pages/CreateTransform/components/ReviewDefinition/ReviewDefinition.tsx +++ b/public/pages/CreateTransform/components/ReviewDefinition/ReviewDefinition.tsx @@ -140,12 +140,12 @@ export default class ReviewDefinition extends Component { )} } - bodyStyles={{ padding: "initial" }} + panelStyles={{ padding: "20px 20px" }} + bodyStyles={{ padding: "10px" }} title="Define transforms" titleSize="m" >
- {groupItems()} {aggItems()} diff --git a/public/pages/CreateTransform/components/ReviewSchedule/ReviewSchedule.tsx b/public/pages/CreateTransform/components/ReviewSchedule/ReviewSchedule.tsx index 74aa9d58b..e9cd129c3 100644 --- a/public/pages/CreateTransform/components/ReviewSchedule/ReviewSchedule.tsx +++ b/public/pages/CreateTransform/components/ReviewSchedule/ReviewSchedule.tsx @@ -32,14 +32,9 @@ export default class ReviewSchedule extends Component { } render() { - const { jobEnabledByDefault, - interval, - intervalTimeunit, - pageSize, - onChangeStep, - } = this.props; + const { jobEnabledByDefault, interval, intervalTimeunit, pageSize, onChangeStep } = this.props; - const enabled = (jobEnabledByDefault) ? ("Yes") : ("No"); + const enabled = jobEnabledByDefault ? "Yes" : "No"; const schedule = "Every " + interval + " " + intervalTimeunit.toLowerCase(); @@ -53,7 +48,7 @@ export default class ReviewSchedule extends Component { { text: "Edit", buttonProps: { - onClick: () => onChangeStep(3) + onClick: () => onChangeStep(3), }, }, ]} @@ -61,12 +56,12 @@ export default class ReviewSchedule extends Component { )} } - bodyStyles={{ padding: "initial" }} + panelStyles={{ padding: "20px 20px" }} + bodyStyles={{ padding: "10px" }} title="Specify schedule" titleSize="m" >
- @@ -89,6 +84,6 @@ export default class ReviewSchedule extends Component {
- ) + ); } } diff --git a/public/pages/CreateTransform/components/Schedule/Schedule.tsx b/public/pages/CreateTransform/components/Schedule/Schedule.tsx index 86e04e203..deb3cfe7c 100644 --- a/public/pages/CreateTransform/components/Schedule/Schedule.tsx +++ b/public/pages/CreateTransform/components/Schedule/Schedule.tsx @@ -68,9 +68,8 @@ export default class Schedule extends Component { onChangePage, } = this.props; return ( - -
- + +
{!isEdit && ( { {selectInterval(interval, intervalTimeunit, intervalError, onChangeIntervalTime, onChangeIntervalTimeunit)} + { -
); diff --git a/public/pages/CreateTransform/components/TransformIndices/TransformIndices.tsx b/public/pages/CreateTransform/components/TransformIndices/TransformIndices.tsx index 34bff7496..065e93d5a 100644 --- a/public/pages/CreateTransform/components/TransformIndices/TransformIndices.tsx +++ b/public/pages/CreateTransform/components/TransformIndices/TransformIndices.tsx @@ -180,17 +180,15 @@ export default class TransformIndices extends Component - -
- {hasAggregation && ( - - - -

Note: changing source index will erase all existing definitions about aggregations and metrics.

-
-
- )} - + +
+ {hasAggregation && ( + + +

Note: changing source index will erase all existing definitions about aggregations and metrics.

+
+
+ )} - + @@ -258,7 +256,7 @@ export default class TransformIndices extends Component - + Date: Thu, 20 May 2021 21:45:37 -0500 Subject: [PATCH 88/93] Modify source filter help text --- .../TransformIndices/TransformIndices.tsx | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) diff --git a/public/pages/CreateTransform/components/TransformIndices/TransformIndices.tsx b/public/pages/CreateTransform/components/TransformIndices/TransformIndices.tsx index 065e93d5a..0d9d6f310 100644 --- a/public/pages/CreateTransform/components/TransformIndices/TransformIndices.tsx +++ b/public/pages/CreateTransform/components/TransformIndices/TransformIndices.tsx @@ -29,6 +29,7 @@ import { EuiHorizontalRule, EuiComboBoxOptionOption, EuiBadge, + EuiLink, } from "@elastic/eui"; import _ from "lodash"; import { ContentPanel } from "../../../../components/ContentPanel"; @@ -222,11 +223,18 @@ export default class TransformIndices extends Component - - Choose a subset of source index to focus on to optimize for performance and computing resource. You can’t change filter once - the job is created. + + You can use the custom DSL to filter a subset of the source index to use in the transform job, which optimizes the job for + performance and computing resources. You cannot change these filters once the job is created. + + + {" "} + Learn more + + + {/*{this.state.dataFilters.map((item) => (*/} {/* {item}*/} {/*))}*/} From 566901e4d78d04766c4bd9dd40f5c7e307847809 Mon Sep 17 00:00:00 2001 From: Ravi Thaluru Date: Fri, 21 May 2021 17:43:30 -0700 Subject: [PATCH 89/93] Depending on OpenSearchDashboards Signed-off-by: Ravi Thaluru --- .../components/DefineTransforms/DefineTransforms.tsx | 2 +- .../components/ReviewDefinition/ReviewDefinition.tsx | 2 +- public/services/TransformService.ts | 2 +- server/plugin.ts | 10 +++++----- server/routes/transforms.ts | 4 ++-- 5 files changed, 10 insertions(+), 10 deletions(-) diff --git a/public/pages/CreateTransform/components/DefineTransforms/DefineTransforms.tsx b/public/pages/CreateTransform/components/DefineTransforms/DefineTransforms.tsx index 164f589e9..19f51186d 100644 --- a/public/pages/CreateTransform/components/DefineTransforms/DefineTransforms.tsx +++ b/public/pages/CreateTransform/components/DefineTransforms/DefineTransforms.tsx @@ -14,7 +14,7 @@ */ import { EuiDataGrid, EuiDataGridColumn, EuiSpacer, EuiText, EuiToolTip } from "@elastic/eui"; -import { CoreStart } from "kibana/public"; +import { CoreStart } from "opensearch-dashboards/public"; import React, { useCallback, useState } from "react"; import { ContentPanel } from "../../../../components/ContentPanel"; import { FieldItem, TransformAggItem, TransformGroupItem } from "../../../../../models/interfaces"; diff --git a/public/pages/CreateTransform/components/ReviewDefinition/ReviewDefinition.tsx b/public/pages/CreateTransform/components/ReviewDefinition/ReviewDefinition.tsx index b84e95964..dc3d6a429 100644 --- a/public/pages/CreateTransform/components/ReviewDefinition/ReviewDefinition.tsx +++ b/public/pages/CreateTransform/components/ReviewDefinition/ReviewDefinition.tsx @@ -20,7 +20,7 @@ import { ModalConsumer } from "../../../../components/Modal"; import { TransformGroupItem, DimensionItem, FieldItem, TransformAggItem } from "../../../../../models/interfaces"; import DefineTransforms from "../DefineTransforms"; import { TransformService } from "../../../../services"; -import { CoreStart } from "kibana/public"; +import { CoreStart } from "opensearch-dashboards/public"; interface ReviewDefinitionProps { transformService: TransformService; diff --git a/public/services/TransformService.ts b/public/services/TransformService.ts index 1decb1041..1c3a6710c 100644 --- a/public/services/TransformService.ts +++ b/public/services/TransformService.ts @@ -13,7 +13,7 @@ * permissions and limitations under the License. */ -import { HttpSetup } from "kibana/public"; +import { HttpSetup } from "opensearch-dashboards/public"; import { ServerResponse } from "../../server/models/types"; import { GetTransformsResponse, PreviewTransformResponse, PutTransformResponse } from "../../server/models/interfaces"; import { NODE_API } from "../../utils/constants"; diff --git a/server/plugin.ts b/server/plugin.ts index 2e0fe66c2..c1e59c1f9 100644 --- a/server/plugin.ts +++ b/server/plugin.ts @@ -38,11 +38,11 @@ export class IndexPatternManagementPlugin implements Plugin Date: Fri, 21 May 2021 16:51:16 -0700 Subject: [PATCH 90/93] Code cleanup removing unused imports and props --- .../DefineTransforms/DefineTransforms.tsx | 12 ----------- .../IndexFilterPopover/IndexFilterPopover.tsx | 1 - .../PreviewOptions/PreviewOptions.tsx | 11 ---------- .../PreviewTransform/PreviewTransform.tsx | 18 ++-------------- .../ReviewDefinition/ReviewDefinition.tsx | 5 ----- .../ReviewSchedule/ReviewSchedule.tsx | 2 +- .../components/Schedule/Schedule.tsx | 9 -------- .../TransformIndices/TransformIndices.tsx | 12 ----------- .../Panels/HistogramPanel/HistogramPanel.tsx | 2 +- .../CreateTransform/CreateTransform.tsx | 3 --- .../CreateTransformForm.tsx | 21 +++++++------------ .../CreateTransformStep2.tsx | 3 --- .../CreateTransformStep4.tsx | 7 +++---- .../Transforms/components/Indices/Indices.tsx | 2 +- .../components/Schedule/Schedule.tsx | 16 +------------- .../Transforms/TransformDetails.tsx | 3 +-- .../Transforms/TransformSettings.tsx | 2 -- 17 files changed, 17 insertions(+), 112 deletions(-) diff --git a/public/pages/CreateTransform/components/DefineTransforms/DefineTransforms.tsx b/public/pages/CreateTransform/components/DefineTransforms/DefineTransforms.tsx index 19f51186d..b5f79f2fa 100644 --- a/public/pages/CreateTransform/components/DefineTransforms/DefineTransforms.tsx +++ b/public/pages/CreateTransform/components/DefineTransforms/DefineTransforms.tsx @@ -28,7 +28,6 @@ import { renderTime } from "../../../Transforms/utils/helpers"; interface DefineTransformsProps { transformService: TransformService; notifications: CoreStart["notifications"]; - transformId: string; sourceIndex: string; fields: FieldItem[]; selectedGroupField: TransformGroupItem[]; @@ -44,7 +43,6 @@ interface DefineTransformsProps { export default function DefineTransforms({ transformService, notifications, - transformId, sourceIndex, fields, selectedGroupField, @@ -99,8 +97,6 @@ export default function DefineTransforms({ const [visibleColumns, setVisibleColumns] = useState(() => columns.map(({ id }) => id).slice(0, 5)); const [data, setData] = useState([]); const [dataCount, setDataCount] = useState(0); - const [groupSelection, setGroupSelection] = useState(selectedGroupField); - const [aggSelection, setAggSelection] = useState(selectedAggregations); const fetchData = useCallback(async () => { setLoading(true); @@ -199,11 +195,7 @@ export default function DefineTransforms({ @@ -276,11 +268,7 @@ export default function DefineTransforms({ diff --git a/public/pages/CreateTransform/components/IndexFilterPopover/IndexFilterPopover.tsx b/public/pages/CreateTransform/components/IndexFilterPopover/IndexFilterPopover.tsx index 5e2fc72b5..6ed65cabb 100644 --- a/public/pages/CreateTransform/components/IndexFilterPopover/IndexFilterPopover.tsx +++ b/public/pages/CreateTransform/components/IndexFilterPopover/IndexFilterPopover.tsx @@ -37,7 +37,6 @@ interface IndexFilterPopoverProps { } export default function IndexFilterPopover({ - sourceIndex, fields, sourceIndexFilter, onChangeSourceIndexFilter, diff --git a/public/pages/CreateTransform/components/PreviewOptions/PreviewOptions.tsx b/public/pages/CreateTransform/components/PreviewOptions/PreviewOptions.tsx index 9bb5de565..db0ced29a 100644 --- a/public/pages/CreateTransform/components/PreviewOptions/PreviewOptions.tsx +++ b/public/pages/CreateTransform/components/PreviewOptions/PreviewOptions.tsx @@ -25,25 +25,14 @@ import { EuiToolTip, } from "@elastic/eui"; import { useState } from "react"; -import { TransformAggItem, TransformGroupItem } from "../../../../../models/interfaces"; interface PreviewOptionsProps { name: string; - selectedGroupField: TransformGroupItem[]; - onGroupSelectionChange: (selectedFields: TransformGroupItem[], aggItem: TransformAggItem) => void; - aggList: TransformAggItem[]; - selectedAggregations: any; - onAggregationSelectionChange: (selectedFields: any, aggItem: TransformAggItem) => void; onRemoveTransformation: (name: string) => void; } export default function PreviewOptions({ name, - selectedGroupField, - onGroupSelectionChange, - selectedAggregations, - aggList, - onAggregationSelectionChange, onRemoveTransformation, }: PreviewOptionsProps) { const [isPopoverOpen, setIsPopoverOpen] = useState(false); diff --git a/public/pages/CreateTransform/components/PreviewTransform/PreviewTransform.tsx b/public/pages/CreateTransform/components/PreviewTransform/PreviewTransform.tsx index 911f2e809..3209330b8 100644 --- a/public/pages/CreateTransform/components/PreviewTransform/PreviewTransform.tsx +++ b/public/pages/CreateTransform/components/PreviewTransform/PreviewTransform.tsx @@ -13,37 +13,28 @@ * permissions and limitations under the License. */ -import React, { useCallback, useState } from "react"; +import React, { useState } from "react"; import { EuiDataGrid, EuiDataGridColumn, EuiText, EuiToolTip } from "@elastic/eui"; import PreviewEmptyPrompt from "../PreviewEmptyPrompt"; import PreviewOptions from "../PreviewOptions"; -import { TransformAggItem, TransformGroupItem } from "../../../../../models/interfaces"; +import { TransformAggItem } from "../../../../../models/interfaces"; import { renderTime } from "../../../Transforms/utils/helpers"; interface PreviewTransformProps { previewTransform: any[]; - selectedGroupField: TransformGroupItem[]; - onGroupSelectionChange: (selectedFields: TransformGroupItem[], aggItem: TransformAggItem) => void; aggList: TransformAggItem[]; - selectedAggregations: any; - onAggregationSelectionChange: (selectedFields: any, aggItem: TransformAggItem) => void; onRemoveTransformation: (name: string) => void; isReadOnly: boolean; } export default function PreviewTransform({ previewTransform, - selectedGroupField, - onGroupSelectionChange, - selectedAggregations, aggList, - onAggregationSelectionChange, onRemoveTransformation, isReadOnly, }: PreviewTransformProps) { const [previewColumns, setPreviewColumns] = useState([]); const [visiblePreviewColumns, setVisiblePreviewColumns] = useState(() => previewColumns.map(({ id }) => id).slice(0, 5)); - const [previewPagination, setPreviewPagination] = useState({ pageIndex: 0, pageSize: 10 }); const renderPreviewCellValue = ({ rowIndex, columnId }) => { if (previewTransform.hasOwnProperty(rowIndex)) { @@ -100,11 +91,6 @@ export default function PreviewTransform({ display: ( ), diff --git a/public/pages/CreateTransform/components/ReviewDefinition/ReviewDefinition.tsx b/public/pages/CreateTransform/components/ReviewDefinition/ReviewDefinition.tsx index dc3d6a429..28b285682 100644 --- a/public/pages/CreateTransform/components/ReviewDefinition/ReviewDefinition.tsx +++ b/public/pages/CreateTransform/components/ReviewDefinition/ReviewDefinition.tsx @@ -46,17 +46,13 @@ export default class ReviewDefinition extends Component { render() { const { transformService, - notifications, - transformId, sourceIndex, fields, selectedGroupField, onGroupSelectionChange, selectedAggregations, - aggList, onAggregationSelectionChange, onRemoveTransformation, - previewTransform, onChangeStep, } = this.props; @@ -158,7 +154,6 @@ export default class ReviewDefinition extends Component { {...this.props} transformService={transformService} notifications={this.context.notifications} - transformId={transformId} sourceIndex={sourceIndex} fields={fields} onGroupSelectionChange={onGroupSelectionChange} diff --git a/public/pages/CreateTransform/components/ReviewSchedule/ReviewSchedule.tsx b/public/pages/CreateTransform/components/ReviewSchedule/ReviewSchedule.tsx index e9cd129c3..770aaac56 100644 --- a/public/pages/CreateTransform/components/ReviewSchedule/ReviewSchedule.tsx +++ b/public/pages/CreateTransform/components/ReviewSchedule/ReviewSchedule.tsx @@ -14,7 +14,7 @@ */ import React, { Component } from "react"; -import { EuiFlexGrid, EuiSpacer, EuiFlexItem, EuiText } from "@elastic/eui"; +import { EuiFlexGrid, EuiFlexItem, EuiText } from "@elastic/eui"; import { ContentPanel, ContentPanelActions } from "../../../../components/ContentPanel"; import { ModalConsumer } from "../../../../components/Modal"; diff --git a/public/pages/CreateTransform/components/Schedule/Schedule.tsx b/public/pages/CreateTransform/components/Schedule/Schedule.tsx index deb3cfe7c..92ae11b13 100644 --- a/public/pages/CreateTransform/components/Schedule/Schedule.tsx +++ b/public/pages/CreateTransform/components/Schedule/Schedule.tsx @@ -14,23 +14,14 @@ */ import React, { ChangeEvent, Component } from "react"; -import moment from "moment-timezone"; import { EuiSpacer, EuiCheckbox, - EuiRadioGroup, EuiFormRow, - EuiSelect, EuiFieldNumber, - EuiFlexGroup, - EuiFlexItem, - EuiTextArea, - EuiFormHelpText, - EuiText, EuiAccordion, EuiHorizontalRule, } from "@elastic/eui"; -import { DelayTimeunitOptions, ExecutionFrequencyDefinitionOptions, ScheduleIntervalTimeunitOptions } from "../../utils/constants"; import { ContentPanel } from "../../../../components/ContentPanel"; import { selectInterval } from "../../../Transforms/utils/metadataHelper"; diff --git a/public/pages/CreateTransform/components/TransformIndices/TransformIndices.tsx b/public/pages/CreateTransform/components/TransformIndices/TransformIndices.tsx index 0d9d6f310..6eed3a3eb 100644 --- a/public/pages/CreateTransform/components/TransformIndices/TransformIndices.tsx +++ b/public/pages/CreateTransform/components/TransformIndices/TransformIndices.tsx @@ -19,8 +19,6 @@ import { EuiFormRow, EuiComboBox, EuiCallOut, - EuiFacetButton, - EuiAvatar, EuiPopover, EuiFlexGroup, EuiFlexItem, @@ -41,8 +39,6 @@ import { CoreServicesContext } from "../../../../components/core_services"; interface TransformIndicesProps { indexService: IndexService; sourceIndex: { label: string; value?: IndexItem }[]; - //TODO: Uncomment the following line when multiple data filter is supported - // sourceIndexFilter: string[]; sourceIndexFilter: string; sourceIndexError: string; targetIndex: { label: string; value?: IndexItem }[]; @@ -61,7 +57,6 @@ interface TransformIndicesState { targetIndexOptions: { label: string; value?: IndexItem }[]; isPopoverOpen: boolean; selectFieldValue: string; - // dataFilters: string[]; dataFilters: string; } @@ -75,7 +70,6 @@ export default class TransformIndices extends Component this.onButtonClick()} data-test-subj="addFilter"> - Edit data filter - - ); - const clearIndexFilter = () => { onChangeSourceIndexFilter(""); }; diff --git a/public/pages/CreateTransform/components/TransformOptions/Panels/HistogramPanel/HistogramPanel.tsx b/public/pages/CreateTransform/components/TransformOptions/Panels/HistogramPanel/HistogramPanel.tsx index 3a8d61568..a8af94474 100644 --- a/public/pages/CreateTransform/components/TransformOptions/Panels/HistogramPanel/HistogramPanel.tsx +++ b/public/pages/CreateTransform/components/TransformOptions/Panels/HistogramPanel/HistogramPanel.tsx @@ -23,7 +23,7 @@ interface HistogramPanelProps { closePopover: () => void; } -export default function HistogramPanel({ name, handleGroupSelectionChange, aggList, closePopover }: HistogramPanelProps) { +export default function HistogramPanel({ name, handleGroupSelectionChange, closePopover }: HistogramPanelProps) { const [histogramInterval, setHistogramInterval] = useState(5); return ( diff --git a/public/pages/CreateTransform/containers/CreateTransform/CreateTransform.tsx b/public/pages/CreateTransform/containers/CreateTransform/CreateTransform.tsx index 1e61cea41..0d48fb62c 100644 --- a/public/pages/CreateTransform/containers/CreateTransform/CreateTransform.tsx +++ b/public/pages/CreateTransform/containers/CreateTransform/CreateTransform.tsx @@ -33,8 +33,6 @@ interface CreateTransformProps extends RouteComponentProps { hasSubmitted: boolean; description: string; sourceIndex: { label: string; value?: IndexItem }[]; - //TODO: Uncomment the following line when multiple data filter is supported - // sourceIndexFilter: string[]; sourceIndexFilter: string; sourceIndexError: string; targetIndex: { label: string; value?: IndexItem }[]; @@ -48,7 +46,6 @@ interface CreateTransformProps extends RouteComponentProps { hasAggregation: boolean; fields: FieldItem[]; fieldSelectedOption: string; - onFieldChange: () => void; beenWarned: boolean; } diff --git a/public/pages/CreateTransform/containers/CreateTransformForm/CreateTransformForm.tsx b/public/pages/CreateTransform/containers/CreateTransformForm/CreateTransformForm.tsx index a40bea814..e8b3220da 100644 --- a/public/pages/CreateTransform/containers/CreateTransformForm/CreateTransformForm.tsx +++ b/public/pages/CreateTransform/containers/CreateTransformForm/CreateTransformForm.tsx @@ -25,7 +25,6 @@ import CreateTransform from "../CreateTransform"; import CreateTransformStep2 from "../CreateTransformStep2"; import { FieldItem, - GroupItem, IndexItem, Transform, TRANSFORM_AGG_TYPE, @@ -73,12 +72,10 @@ interface CreateTransformFormState { allMappings: FieldItem[][]; fields: FieldItem[]; fieldSelectedOption: string; - selectedTerms: FieldItem[]; selectedGroupField: TransformGroupItem[]; selectedAggregations: any; aggList: TransformAggItem[]; - aggregationsError: string; selectedFields: FieldItem[]; jobEnabledByDefault: boolean; @@ -114,12 +111,11 @@ export default class CreateTransformForm extends Component => { + onGroupSelectionChange = async (selectedGroupField: TransformGroupItem[], aggItem: TransformAggItem): Promise => { const { aggList } = this.state; aggList.push(aggItem); this.updateGroup(); - let previewResponse = await this.previewTransform(this.state.transformJSON); + await this.previewTransform(this.state.transformJSON); this.setState({ selectedGroupField }); }; @@ -320,7 +316,7 @@ export default class CreateTransformForm extends Component { const { transformJSON, aggList } = this.state; let newJSON = transformJSON; - let tempGroupSelect: GroupItem[] = []; + let tempGroupSelect: TransformGroupItem[] = []; aggList.map((aggItem) => { if ( aggItem.type == TRANSFORM_AGG_TYPE.histogram || @@ -470,14 +466,11 @@ export default class CreateTransformForm extends Component void; previewTransform: any[]; - timestamp: EuiComboBoxOptionOption[]; - timezone: string; - timeunit: string; + interval: number; + intervalTimeunit: string; } export default class CreateTransformStep4 extends Component { diff --git a/public/pages/Transforms/components/Indices/Indices.tsx b/public/pages/Transforms/components/Indices/Indices.tsx index 5dcb31478..688c21e25 100644 --- a/public/pages/Transforms/components/Indices/Indices.tsx +++ b/public/pages/Transforms/components/Indices/Indices.tsx @@ -14,7 +14,7 @@ */ import React, { Component } from "react"; -import { EuiCodeEditor, EuiFlexGrid, EuiSpacer, EuiFlexItem, EuiText } from "@elastic/eui"; +import { EuiCodeEditor, EuiSpacer, EuiText } from "@elastic/eui"; import { ContentPanel } from "../../../../components/ContentPanel"; interface IndicesProps { diff --git a/public/pages/Transforms/components/Schedule/Schedule.tsx b/public/pages/Transforms/components/Schedule/Schedule.tsx index c80a1cdc8..46f5f4fe1 100644 --- a/public/pages/Transforms/components/Schedule/Schedule.tsx +++ b/public/pages/Transforms/components/Schedule/Schedule.tsx @@ -14,25 +14,17 @@ */ import React, { ChangeEvent, Component } from "react"; -import moment from "moment-timezone"; import { EuiSpacer, EuiCheckbox, EuiAccordion, EuiFormRow, - EuiSelect, EuiFieldNumber, - EuiFlexGroup, - EuiFlexItem, - EuiTextArea, - EuiText, } from "@elastic/eui"; // @ts-ignore import { htmlIdGenerator } from "@elastic/eui/lib/services"; -import { ScheduleIntervalTimeunitOptions } from "../../utils/constants"; import { ContentPanel } from "../../../../components/ContentPanel"; -import { selectCronExpression, selectInterval } from "../../utils/metadataHelper"; -import { ExecutionFrequencyDefinitionOptions } from "../../../CreateTransform/utils/constants"; +import { selectInterval } from "../../utils/metadataHelper"; interface ScheduleProps { transformId: string; @@ -66,17 +58,11 @@ export default class Schedule extends Component { pageSize, onEnabledChange, onPageChange, - schedule, - onScheduleChange, interval, intervalError, intervalTimeUnit, onIntervalChange, onIntervalTimeUnitChange, - cronExpression, - cronTimeZone, - onCronExpressionChange, - onCronTimeZoneChange, } = this.props; return ( diff --git a/public/pages/Transforms/containers/Transforms/TransformDetails.tsx b/public/pages/Transforms/containers/Transforms/TransformDetails.tsx index 3dc890ab6..159750d9c 100644 --- a/public/pages/Transforms/containers/Transforms/TransformDetails.tsx +++ b/public/pages/Transforms/containers/Transforms/TransformDetails.tsx @@ -27,7 +27,6 @@ import { EuiModalHeaderTitle, EuiModalBody, EuiCodeBlock, - EuiHealth, EuiContextMenuItem, EuiContextMenuPanel, EuiTextColor, @@ -40,7 +39,7 @@ import { CoreServicesContext } from "../../../../components/core_services"; import { BREADCRUMBS, ROUTES } from "../../../../utils/constants"; import queryString from "query-string"; import { getErrorMessage } from "../../../../utils/helpers"; -import { DimensionItem, MetricItem, RollupDimensionItem, TransformMetadata } from "../../../../../models/interfaces"; +import { DimensionItem, RollupDimensionItem, TransformMetadata } from "../../../../../models/interfaces"; import DeleteModal from "../../components/DeleteModal"; import GenerationInformation from "../../components/GeneralInformation"; import TransformStatus from "../../components/TransformStatus"; diff --git a/public/pages/Transforms/containers/Transforms/TransformSettings.tsx b/public/pages/Transforms/containers/Transforms/TransformSettings.tsx index 06cc91526..ee42491a6 100644 --- a/public/pages/Transforms/containers/Transforms/TransformSettings.tsx +++ b/public/pages/Transforms/containers/Transforms/TransformSettings.tsx @@ -158,7 +158,5 @@ export default class TransformSettings extends Component { const response = await this.previewTransform({ transform: this.props.transformJson.transform }); - console.log(response); - console.log("tada"); }; } From d1b67ffb10d3541a15e391084108684df48bbb87 Mon Sep 17 00:00:00 2001 From: Eric Lobdell Date: Tue, 25 May 2021 00:33:10 -0700 Subject: [PATCH 91/93] Tweaks to details page, delete flow, aggregations in review create --- .../ReviewDefinition/ReviewDefinition.tsx | 94 +++++++------------ .../components/DeleteModal/DeleteModal.tsx | 2 +- .../Transforms/TransformDetails.tsx | 17 ---- .../Transforms/TransformSettings.tsx | 8 +- .../containers/Transforms/Transforms.tsx | 7 +- server/services/TransformService.ts | 3 +- 6 files changed, 44 insertions(+), 87 deletions(-) diff --git a/public/pages/CreateTransform/components/ReviewDefinition/ReviewDefinition.tsx b/public/pages/CreateTransform/components/ReviewDefinition/ReviewDefinition.tsx index 28b285682..6adbb3fc2 100644 --- a/public/pages/CreateTransform/components/ReviewDefinition/ReviewDefinition.tsx +++ b/public/pages/CreateTransform/components/ReviewDefinition/ReviewDefinition.tsx @@ -17,7 +17,12 @@ import React, { Component } from "react"; import { EuiFlexGrid, EuiSpacer, EuiFlexItem, EuiText, EuiAccordion } from "@elastic/eui"; import { ContentPanel, ContentPanelActions } from "../../../../components/ContentPanel"; import { ModalConsumer } from "../../../../components/Modal"; -import { TransformGroupItem, DimensionItem, FieldItem, TransformAggItem } from "../../../../../models/interfaces"; +import { TransformGroupItem, + DimensionItem, + FieldItem, + TransformAggItem, + TRANSFORM_AGG_TYPE, + } from "../../../../../models/interfaces"; import DefineTransforms from "../DefineTransforms"; import { TransformService } from "../../../../services"; import { CoreStart } from "opensearch-dashboards/public"; @@ -48,74 +53,44 @@ export default class ReviewDefinition extends Component { transformService, sourceIndex, fields, - selectedGroupField, onGroupSelectionChange, selectedAggregations, onAggregationSelectionChange, onRemoveTransformation, onChangeStep, + aggList, } = this.props; - const groupItems = () => { - return selectedGroupField.map((group) => { - let parsedGroup = parseGroup(group); - return ( - - -
Group by {parsedGroup.aggregationMethod}
-
{parsedGroup.field.label}
-
-
- ); + const aggListItems = () => { + return aggList.map((item) => { + return (parseAggListItem(item)); }); }; - const parseGroup = (group: TransformGroupItem): DimensionItem => { - switch (true) { - case group.date_histogram != null: - return { - sequence: 0, - aggregationMethod: "date_histogram", - field: { - label: group.date_histogram?.source_field, - }, - interval: group.date_histogram?.interval, - }; - case group.histogram != null: - return { - sequence: 0, - aggregationMethod: "histogram", - field: { - label: group.histogram?.source_field, - }, - interval: group.histogram?.interval, - }; - case group.terms != null: - return { - sequence: 0, - aggregationMethod: "terms", - field: { - label: group.terms?.source_field, - }, - interval: null, - }; + const parseAggListItem = (item: any) => { + let title = ""; + let field = ""; + if ( + item.type == TRANSFORM_AGG_TYPE.histogram || + item.type == TRANSFORM_AGG_TYPE.terms || + item.type == TRANSFORM_AGG_TYPE.date_histogram + ) { + // is a group + title = "Group by " + item.type; + field = item.item[item.type].source_field; + } else { + // is an agg + title = item.type + "()"; + field = item.item[item.type].field; } - }; - - const aggItems = () => { - return Object.keys(selectedAggregations).map((key) => { - let aggregationType = Object.keys(selectedAggregations[key])[0]; - let sourceField = selectedAggregations[key][aggregationType].field; - - return ( - - -
{aggregationType}()
-
{sourceField}
-
-
- ); - }); + return ( + + +
{title}
+
{field}
+
+
+ ) }; return ( @@ -143,8 +118,7 @@ export default class ReviewDefinition extends Component { >
- {groupItems()} - {aggItems()} + {aggListItems()} diff --git a/public/pages/Transforms/components/DeleteModal/DeleteModal.tsx b/public/pages/Transforms/components/DeleteModal/DeleteModal.tsx index 1d6539e15..856ed9155 100644 --- a/public/pages/Transforms/components/DeleteModal/DeleteModal.tsx +++ b/public/pages/Transforms/components/DeleteModal/DeleteModal.tsx @@ -52,7 +52,7 @@ export default class DeleteModal extends Component - By deleting "{item}", all future scheduled executions will be canceled. However, your target index + By deleting {item}, all future scheduled executions will be canceled. However, your target index and data in it will remain intact. diff --git a/public/pages/Transforms/containers/Transforms/TransformDetails.tsx b/public/pages/Transforms/containers/Transforms/TransformDetails.tsx index 159750d9c..57b080a2d 100644 --- a/public/pages/Transforms/containers/Transforms/TransformDetails.tsx +++ b/public/pages/Transforms/containers/Transforms/TransformDetails.tsx @@ -267,18 +267,6 @@ export default class TransformDetails extends Component View JSON , - { - this.closePopover(); - this.showDeleteModal(); - }} - > - Duplicate job - , - - - View target index in Discover - - diff --git a/public/pages/Transforms/containers/Transforms/TransformSettings.tsx b/public/pages/Transforms/containers/Transforms/TransformSettings.tsx index ee42491a6..36f83c240 100644 --- a/public/pages/Transforms/containers/Transforms/TransformSettings.tsx +++ b/public/pages/Transforms/containers/Transforms/TransformSettings.tsx @@ -104,13 +104,15 @@ export default class TransformSettings extends Component { - console.log(aggregationsShown); return Object.keys(aggregationsShown).map((key) => { + let aggregationType = Object.keys(aggregationsShown[key])[0]; + let sourceField = aggregationsShown[key][aggregationType].field; + return ( -
{key}
-
{JSON.stringify(aggregationsShown[key])}
+
{aggregationType}()
+
{sourceField}
); diff --git a/public/pages/Transforms/containers/Transforms/Transforms.tsx b/public/pages/Transforms/containers/Transforms/Transforms.tsx index 96be48d2d..d90001884 100644 --- a/public/pages/Transforms/containers/Transforms/Transforms.tsx +++ b/public/pages/Transforms/containers/Transforms/Transforms.tsx @@ -351,8 +351,7 @@ export default class Transforms extends Component { - return "asd"; - // this.state.selectedItems.map((item: DocumentTransform) => { return item._id }).join(", "); + return this.state.selectedItems.map((item: DocumentTransform) => { return item._id }).join(", "); }; onSelectionChange = (selectedItems: DocumentTransform[]): void => { @@ -387,8 +386,7 @@ export default class Transforms extends Component Date: Tue, 25 May 2021 10:18:01 -0700 Subject: [PATCH 92/93] Correcting license and some imports Signed-off-by: Ravi Thaluru --- .../ConfigureTransform/ConfigureTransform.tsx | 16 ++--- .../components/ConfigureTransform/index.ts | 16 ++--- .../CreateTransformSteps.tsx | 17 ++--- .../components/CreateTransformSteps/index.ts | 16 ++--- .../DefineTransforms/DefineTransforms.tsx | 16 ++--- .../components/DefineTransforms/index.ts | 16 ++--- .../IndexFilterPopover/IndexFilterPopover.tsx | 16 ++--- .../components/IndexFilterPopover/index.ts | 16 ++--- .../JobNameAndIndices/JobNameAndIndices.tsx | 16 ++--- .../components/JobNameAndIndices/index.ts | 16 ++--- .../PreviewEmptyPrompt/PreviewEmptyPrompt.tsx | 16 ++--- .../components/PreviewEmptyPrompt/index.ts | 16 ++--- .../PreviewOptions/PreviewOptions.tsx | 21 ++---- .../components/PreviewOptions/index.ts | 16 ++--- .../PreviewTransform/PreviewTransform.tsx | 30 +++----- .../components/PreviewTransform/index.ts | 16 ++--- .../ReviewDefinition/ReviewDefinition.tsx | 31 +++----- .../components/ReviewDefinition/index.ts | 16 ++--- .../ReviewSchedule/ReviewSchedule.tsx | 16 ++--- .../components/ReviewSchedule/index.ts | 16 ++--- .../components/Schedule/Schedule.tsx | 25 ++----- .../components/Schedule/index.ts | 16 ++--- .../TransformIndices/TransformIndices.tsx | 16 ++--- .../components/TransformIndices/index.ts | 16 ++--- .../Panels/HistogramPanel/HistogramPanel.tsx | 16 ++--- .../Panels/HistogramPanel/index.ts | 16 ++--- .../PercentilePanel/PercentilePanel.tsx | 16 ++--- .../Panels/PercentilePanel/index.ts | 16 ++--- .../TransformOptions/TransformOptions.tsx | 16 ++--- .../components/TransformOptions/index.ts | 16 ++--- .../CreateTransform/CreateTransform.tsx | 16 ++--- .../containers/CreateTransform/index.ts | 16 ++--- .../CreateTransformForm.tsx | 22 +++--- .../containers/CreateTransformForm/index.ts | 16 ++--- .../CreateTransformStep2.tsx | 16 ++--- .../containers/CreateTransformStep2/index.ts | 16 ++--- .../CreateTransformStep3.tsx | 16 ++--- .../containers/CreateTransformStep3/index.ts | 16 ++--- .../CreateTransformStep4.tsx | 16 ++--- .../containers/CreateTransformStep4/index.ts | 16 ++--- public/pages/CreateTransform/index.ts | 16 ++--- .../pages/CreateTransform/utils/constants.ts | 16 ++--- public/pages/CreateTransform/utils/helpers.ts | 16 ++--- .../ConfigureTransform/Configure.tsx | 16 ++--- .../components/ConfigureTransform/index.ts | 16 ++--- .../components/DeleteModal/DeleteModal.tsx | 20 +++--- .../components/DeleteModal/index.ts | 16 ++--- .../components/ErrorModal/ErrorModal.tsx | 16 ++--- .../Transforms/components/ErrorModal/index.ts | 16 ++--- .../GeneralInformation/GeneralInformation.tsx | 26 ++----- .../components/GeneralInformation/index.ts | 16 ++--- .../Transforms/components/Indices/Indices.tsx | 16 ++--- .../Transforms/components/Indices/index.ts | 16 ++--- .../components/Schedule/Schedule.tsx | 24 ++----- .../Transforms/components/Schedule/index.ts | 16 ++--- .../TransformEmptyPrompt.tsx | 21 +++--- .../components/TransformEmptyPrompt/index.ts | 16 ++--- .../TransformStatus/TransformStatus.tsx | 18 ++--- .../components/TransformStatus/index.ts | 16 ++--- .../containers/Transforms/EditTransform.tsx | 16 ++--- .../Transforms/TransformDetails.tsx | 16 ++--- .../Transforms/TransformSettings.tsx | 22 +++--- .../containers/Transforms/Transforms.tsx | 22 +++--- .../Transforms/containers/Transforms/index.ts | 16 ++--- public/pages/Transforms/index.ts | 16 ++--- public/pages/Transforms/models/interfaces.ts | 18 ++--- public/pages/Transforms/utils/constants.tsx | 16 ++--- public/pages/Transforms/utils/helpers.ts | 16 ++--- .../pages/Transforms/utils/metadataHelper.tsx | 16 ++--- public/services/TransformService.ts | 18 ++--- server/routes/transforms.ts | 16 ++--- server/services/TransformService.ts | 72 ++++++++++--------- 72 files changed, 490 insertions(+), 813 deletions(-) diff --git a/public/pages/CreateTransform/components/ConfigureTransform/ConfigureTransform.tsx b/public/pages/CreateTransform/components/ConfigureTransform/ConfigureTransform.tsx index 4df0d5fd2..3ea600e0c 100644 --- a/public/pages/CreateTransform/components/ConfigureTransform/ConfigureTransform.tsx +++ b/public/pages/CreateTransform/components/ConfigureTransform/ConfigureTransform.tsx @@ -1,16 +1,12 @@ /* - * Copyright 2021 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 * - * Licensed under the Apache License, Version 2.0 (the "License"). - * You may not use this file except in compliance with the License. - * A copy of the License is located at + * The OpenSearch Contributors require contributions made to + * this file be licensed under the Apache-2.0 license or a + * compatible open source license. * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * or in the "license" file accompanying this file. This file is distributed - * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either - * express or implied. See the License for the specific language governing - * permissions and limitations under the License. + * Modifications Copyright OpenSearch Contributors. See + * GitHub history for details. */ import React, { ChangeEvent } from "react"; diff --git a/public/pages/CreateTransform/components/ConfigureTransform/index.ts b/public/pages/CreateTransform/components/ConfigureTransform/index.ts index b935263fd..a1cc22a27 100644 --- a/public/pages/CreateTransform/components/ConfigureTransform/index.ts +++ b/public/pages/CreateTransform/components/ConfigureTransform/index.ts @@ -1,16 +1,12 @@ /* - * Copyright 2021 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 * - * Licensed under the Apache License, Version 2.0 (the "License"). - * You may not use this file except in compliance with the License. - * A copy of the License is located at + * The OpenSearch Contributors require contributions made to + * this file be licensed under the Apache-2.0 license or a + * compatible open source license. * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * or in the "license" file accompanying this file. This file is distributed - * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either - * express or implied. See the License for the specific language governing - * permissions and limitations under the License. + * Modifications Copyright OpenSearch Contributors. See + * GitHub history for details. */ import ConfigureTransform from "./ConfigureTransform"; diff --git a/public/pages/CreateTransform/components/CreateTransformSteps/CreateTransformSteps.tsx b/public/pages/CreateTransform/components/CreateTransformSteps/CreateTransformSteps.tsx index 3b404ea58..ae5cb946c 100644 --- a/public/pages/CreateTransform/components/CreateTransformSteps/CreateTransformSteps.tsx +++ b/public/pages/CreateTransform/components/CreateTransformSteps/CreateTransformSteps.tsx @@ -1,17 +1,14 @@ /* - * Copyright 2021 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 * - * Licensed under the Apache License, Version 2.0 (the "License"). - * You may not use this file except in compliance with the License. - * A copy of the License is located at + * The OpenSearch Contributors require contributions made to + * this file be licensed under the Apache-2.0 license or a + * compatible open source license. * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * or in the "license" file accompanying this file. This file is distributed - * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either - * express or implied. See the License for the specific language governing - * permissions and limitations under the License. + * Modifications Copyright OpenSearch Contributors. See + * GitHub history for details. */ + import React from "react"; import { EuiSteps } from "@elastic/eui"; diff --git a/public/pages/CreateTransform/components/CreateTransformSteps/index.ts b/public/pages/CreateTransform/components/CreateTransformSteps/index.ts index fe481f23a..ee37e42c6 100644 --- a/public/pages/CreateTransform/components/CreateTransformSteps/index.ts +++ b/public/pages/CreateTransform/components/CreateTransformSteps/index.ts @@ -1,16 +1,12 @@ /* - * Copyright 2021 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 * - * Licensed under the Apache License, Version 2.0 (the "License"). - * You may not use this file except in compliance with the License. - * A copy of the License is located at + * The OpenSearch Contributors require contributions made to + * this file be licensed under the Apache-2.0 license or a + * compatible open source license. * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * or in the "license" file accompanying this file. This file is distributed - * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either - * express or implied. See the License for the specific language governing - * permissions and limitations under the License. + * Modifications Copyright OpenSearch Contributors. See + * GitHub history for details. */ import CreateTransformSteps from "./CreateTransformSteps"; diff --git a/public/pages/CreateTransform/components/DefineTransforms/DefineTransforms.tsx b/public/pages/CreateTransform/components/DefineTransforms/DefineTransforms.tsx index b5f79f2fa..4bfe7adfc 100644 --- a/public/pages/CreateTransform/components/DefineTransforms/DefineTransforms.tsx +++ b/public/pages/CreateTransform/components/DefineTransforms/DefineTransforms.tsx @@ -1,16 +1,12 @@ /* - * Copyright 2021 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 * - * Licensed under the Apache License, Version 2.0 (the "License"). - * You may not use this file except in compliance with the License. - * A copy of the License is located at + * The OpenSearch Contributors require contributions made to + * this file be licensed under the Apache-2.0 license or a + * compatible open source license. * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * or in the "license" file accompanying this file. This file is distributed - * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either - * express or implied. See the License for the specific language governing - * permissions and limitations under the License. + * Modifications Copyright OpenSearch Contributors. See + * GitHub history for details. */ import { EuiDataGrid, EuiDataGridColumn, EuiSpacer, EuiText, EuiToolTip } from "@elastic/eui"; diff --git a/public/pages/CreateTransform/components/DefineTransforms/index.ts b/public/pages/CreateTransform/components/DefineTransforms/index.ts index ac624d7f2..a3ba6f574 100644 --- a/public/pages/CreateTransform/components/DefineTransforms/index.ts +++ b/public/pages/CreateTransform/components/DefineTransforms/index.ts @@ -1,16 +1,12 @@ /* - * Copyright 2021 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 * - * Licensed under the Apache License, Version 2.0 (the "License"). - * You may not use this file except in compliance with the License. - * A copy of the License is located at + * The OpenSearch Contributors require contributions made to + * this file be licensed under the Apache-2.0 license or a + * compatible open source license. * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * or in the "license" file accompanying this file. This file is distributed - * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either - * express or implied. See the License for the specific language governing - * permissions and limitations under the License. + * Modifications Copyright OpenSearch Contributors. See + * GitHub history for details. */ import DefineTransforms from "./DefineTransforms"; diff --git a/public/pages/CreateTransform/components/IndexFilterPopover/IndexFilterPopover.tsx b/public/pages/CreateTransform/components/IndexFilterPopover/IndexFilterPopover.tsx index 6ed65cabb..4c8bc50fd 100644 --- a/public/pages/CreateTransform/components/IndexFilterPopover/IndexFilterPopover.tsx +++ b/public/pages/CreateTransform/components/IndexFilterPopover/IndexFilterPopover.tsx @@ -1,16 +1,12 @@ /* - * Copyright 2021 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 * - * Licensed under the Apache License, Version 2.0 (the "License"). - * You may not use this file except in compliance with the License. - * A copy of the License is located at + * The OpenSearch Contributors require contributions made to + * this file be licensed under the Apache-2.0 license or a + * compatible open source license. * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * or in the "license" file accompanying this file. This file is distributed - * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either - * express or implied. See the License for the specific language governing - * permissions and limitations under the License. + * Modifications Copyright OpenSearch Contributors. See + * GitHub history for details. */ import React, { ChangeEvent, useState } from "react"; diff --git a/public/pages/CreateTransform/components/IndexFilterPopover/index.ts b/public/pages/CreateTransform/components/IndexFilterPopover/index.ts index 38ae26bfa..ffacbd8a1 100644 --- a/public/pages/CreateTransform/components/IndexFilterPopover/index.ts +++ b/public/pages/CreateTransform/components/IndexFilterPopover/index.ts @@ -1,16 +1,12 @@ /* - * Copyright 2021 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 * - * Licensed under the Apache License, Version 2.0 (the "License"). - * You may not use this file except in compliance with the License. - * A copy of the License is located at + * The OpenSearch Contributors require contributions made to + * this file be licensed under the Apache-2.0 license or a + * compatible open source license. * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * or in the "license" file accompanying this file. This file is distributed - * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either - * express or implied. See the License for the specific language governing - * permissions and limitations under the License. + * Modifications Copyright OpenSearch Contributors. See + * GitHub history for details. */ import IndexFilterPopover from "./IndexFilterPopover"; diff --git a/public/pages/CreateTransform/components/JobNameAndIndices/JobNameAndIndices.tsx b/public/pages/CreateTransform/components/JobNameAndIndices/JobNameAndIndices.tsx index dd49d16df..b5c701d2d 100644 --- a/public/pages/CreateTransform/components/JobNameAndIndices/JobNameAndIndices.tsx +++ b/public/pages/CreateTransform/components/JobNameAndIndices/JobNameAndIndices.tsx @@ -1,16 +1,12 @@ /* - * Copyright 2021 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 * - * Licensed under the Apache License, Version 2.0 (the "License"). - * You may not use this file except in compliance with the License. - * A copy of the License is located at + * The OpenSearch Contributors require contributions made to + * this file be licensed under the Apache-2.0 license or a + * compatible open source license. * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * or in the "license" file accompanying this file. This file is distributed - * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either - * express or implied. See the License for the specific language governing - * permissions and limitations under the License. + * Modifications Copyright OpenSearch Contributors. See + * GitHub history for details. */ import React, { Component } from "react"; diff --git a/public/pages/CreateTransform/components/JobNameAndIndices/index.ts b/public/pages/CreateTransform/components/JobNameAndIndices/index.ts index aa4b17446..8f5747ed5 100644 --- a/public/pages/CreateTransform/components/JobNameAndIndices/index.ts +++ b/public/pages/CreateTransform/components/JobNameAndIndices/index.ts @@ -1,16 +1,12 @@ /* - * Copyright 2021 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 * - * Licensed under the Apache License, Version 2.0 (the "License"). - * You may not use this file except in compliance with the License. - * A copy of the License is located at + * The OpenSearch Contributors require contributions made to + * this file be licensed under the Apache-2.0 license or a + * compatible open source license. * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * or in the "license" file accompanying this file. This file is distributed - * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either - * express or implied. See the License for the specific language governing - * permissions and limitations under the License. + * Modifications Copyright OpenSearch Contributors. See + * GitHub history for details. */ import JobNameAndIndices from "./JobNameAndIndices"; diff --git a/public/pages/CreateTransform/components/PreviewEmptyPrompt/PreviewEmptyPrompt.tsx b/public/pages/CreateTransform/components/PreviewEmptyPrompt/PreviewEmptyPrompt.tsx index 1cc40cce8..d420c9d01 100644 --- a/public/pages/CreateTransform/components/PreviewEmptyPrompt/PreviewEmptyPrompt.tsx +++ b/public/pages/CreateTransform/components/PreviewEmptyPrompt/PreviewEmptyPrompt.tsx @@ -1,16 +1,12 @@ /* - * Copyright 2021 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 * - * Licensed under the Apache License, Version 2.0 (the "License"). - * You may not use this file except in compliance with the License. - * A copy of the License is located at + * The OpenSearch Contributors require contributions made to + * this file be licensed under the Apache-2.0 license or a + * compatible open source license. * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * or in the "license" file accompanying this file. This file is distributed - * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either - * express or implied. See the License for the specific language governing - * permissions and limitations under the License. + * Modifications Copyright OpenSearch Contributors. See + * GitHub history for details. */ import { EuiEmptyPrompt, EuiPanel, EuiText } from "@elastic/eui"; diff --git a/public/pages/CreateTransform/components/PreviewEmptyPrompt/index.ts b/public/pages/CreateTransform/components/PreviewEmptyPrompt/index.ts index a393f37d6..fe6165726 100644 --- a/public/pages/CreateTransform/components/PreviewEmptyPrompt/index.ts +++ b/public/pages/CreateTransform/components/PreviewEmptyPrompt/index.ts @@ -1,16 +1,12 @@ /* - * Copyright 2021 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 * - * Licensed under the Apache License, Version 2.0 (the "License"). - * You may not use this file except in compliance with the License. - * A copy of the License is located at + * The OpenSearch Contributors require contributions made to + * this file be licensed under the Apache-2.0 license or a + * compatible open source license. * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * or in the "license" file accompanying this file. This file is distributed - * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either - * express or implied. See the License for the specific language governing - * permissions and limitations under the License. + * Modifications Copyright OpenSearch Contributors. See + * GitHub history for details. */ import PreviewEmptyPrompt from "./PreviewEmptyPrompt"; diff --git a/public/pages/CreateTransform/components/PreviewOptions/PreviewOptions.tsx b/public/pages/CreateTransform/components/PreviewOptions/PreviewOptions.tsx index db0ced29a..e959b90a1 100644 --- a/public/pages/CreateTransform/components/PreviewOptions/PreviewOptions.tsx +++ b/public/pages/CreateTransform/components/PreviewOptions/PreviewOptions.tsx @@ -1,16 +1,12 @@ /* - * Copyright 2021 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 * - * Licensed under the Apache License, Version 2.0 (the "License"). - * You may not use this file except in compliance with the License. - * A copy of the License is located at + * The OpenSearch Contributors require contributions made to + * this file be licensed under the Apache-2.0 license or a + * compatible open source license. * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * or in the "license" file accompanying this file. This file is distributed - * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either - * express or implied. See the License for the specific language governing - * permissions and limitations under the License. + * Modifications Copyright OpenSearch Contributors. See + * GitHub history for details. */ import React from "react"; @@ -31,10 +27,7 @@ interface PreviewOptionsProps { onRemoveTransformation: (name: string) => void; } -export default function PreviewOptions({ - name, - onRemoveTransformation, -}: PreviewOptionsProps) { +export default function PreviewOptions({ name, onRemoveTransformation }: PreviewOptionsProps) { const [isPopoverOpen, setIsPopoverOpen] = useState(false); const closePopover = () => { diff --git a/public/pages/CreateTransform/components/PreviewOptions/index.ts b/public/pages/CreateTransform/components/PreviewOptions/index.ts index ead095700..e9c44f8b9 100644 --- a/public/pages/CreateTransform/components/PreviewOptions/index.ts +++ b/public/pages/CreateTransform/components/PreviewOptions/index.ts @@ -1,16 +1,12 @@ /* - * Copyright 2021 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 * - * Licensed under the Apache License, Version 2.0 (the "License"). - * You may not use this file except in compliance with the License. - * A copy of the License is located at + * The OpenSearch Contributors require contributions made to + * this file be licensed under the Apache-2.0 license or a + * compatible open source license. * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * or in the "license" file accompanying this file. This file is distributed - * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either - * express or implied. See the License for the specific language governing - * permissions and limitations under the License. + * Modifications Copyright OpenSearch Contributors. See + * GitHub history for details. */ import PreviewOptions from "./PreviewOptions"; diff --git a/public/pages/CreateTransform/components/PreviewTransform/PreviewTransform.tsx b/public/pages/CreateTransform/components/PreviewTransform/PreviewTransform.tsx index 3209330b8..de37f3e8a 100644 --- a/public/pages/CreateTransform/components/PreviewTransform/PreviewTransform.tsx +++ b/public/pages/CreateTransform/components/PreviewTransform/PreviewTransform.tsx @@ -1,16 +1,12 @@ /* - * Copyright 2021 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 * - * Licensed under the Apache License, Version 2.0 (the "License"). - * You may not use this file except in compliance with the License. - * A copy of the License is located at + * The OpenSearch Contributors require contributions made to + * this file be licensed under the Apache-2.0 license or a + * compatible open source license. * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * or in the "license" file accompanying this file. This file is distributed - * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either - * express or implied. See the License for the specific language governing - * permissions and limitations under the License. + * Modifications Copyright OpenSearch Contributors. See + * GitHub history for details. */ import React, { useState } from "react"; @@ -27,12 +23,7 @@ interface PreviewTransformProps { isReadOnly: boolean; } -export default function PreviewTransform({ - previewTransform, - aggList, - onRemoveTransformation, - isReadOnly, -}: PreviewTransformProps) { +export default function PreviewTransform({ previewTransform, aggList, onRemoveTransformation, isReadOnly }: PreviewTransformProps) { const [previewColumns, setPreviewColumns] = useState([]); const [visiblePreviewColumns, setVisiblePreviewColumns] = useState(() => previewColumns.map(({ id }) => id).slice(0, 5)); @@ -88,12 +79,7 @@ export default function PreviewTransform({ aggList.map((aggItem) => { tempCol.push({ id: aggItem.name, - display: ( - - ), + display: , actions: { showHide: false, showMoveLeft: false, diff --git a/public/pages/CreateTransform/components/PreviewTransform/index.ts b/public/pages/CreateTransform/components/PreviewTransform/index.ts index e08a3b2d8..d69eb9dd5 100644 --- a/public/pages/CreateTransform/components/PreviewTransform/index.ts +++ b/public/pages/CreateTransform/components/PreviewTransform/index.ts @@ -1,16 +1,12 @@ /* - * Copyright 2021 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 * - * Licensed under the Apache License, Version 2.0 (the "License"). - * You may not use this file except in compliance with the License. - * A copy of the License is located at + * The OpenSearch Contributors require contributions made to + * this file be licensed under the Apache-2.0 license or a + * compatible open source license. * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * or in the "license" file accompanying this file. This file is distributed - * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either - * express or implied. See the License for the specific language governing - * permissions and limitations under the License. + * Modifications Copyright OpenSearch Contributors. See + * GitHub history for details. */ import PreviewTransform from "./PreviewTransform"; diff --git a/public/pages/CreateTransform/components/ReviewDefinition/ReviewDefinition.tsx b/public/pages/CreateTransform/components/ReviewDefinition/ReviewDefinition.tsx index 6adbb3fc2..df4149a8d 100644 --- a/public/pages/CreateTransform/components/ReviewDefinition/ReviewDefinition.tsx +++ b/public/pages/CreateTransform/components/ReviewDefinition/ReviewDefinition.tsx @@ -1,28 +1,19 @@ /* - * Copyright 2021 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 * - * Licensed under the Apache License, Version 2.0 (the "License"). - * You may not use this file except in compliance with the License. - * A copy of the License is located at + * The OpenSearch Contributors require contributions made to + * this file be licensed under the Apache-2.0 license or a + * compatible open source license. * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * or in the "license" file accompanying this file. This file is distributed - * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either - * express or implied. See the License for the specific language governing - * permissions and limitations under the License. + * Modifications Copyright OpenSearch Contributors. See + * GitHub history for details. */ import React, { Component } from "react"; import { EuiFlexGrid, EuiSpacer, EuiFlexItem, EuiText, EuiAccordion } from "@elastic/eui"; import { ContentPanel, ContentPanelActions } from "../../../../components/ContentPanel"; import { ModalConsumer } from "../../../../components/Modal"; -import { TransformGroupItem, - DimensionItem, - FieldItem, - TransformAggItem, - TRANSFORM_AGG_TYPE, - } from "../../../../../models/interfaces"; +import { TransformGroupItem, FieldItem, TransformAggItem, TRANSFORM_AGG_TYPE } from "../../../../../models/interfaces"; import DefineTransforms from "../DefineTransforms"; import { TransformService } from "../../../../services"; import { CoreStart } from "opensearch-dashboards/public"; @@ -63,7 +54,7 @@ export default class ReviewDefinition extends Component { const aggListItems = () => { return aggList.map((item) => { - return (parseAggListItem(item)); + return parseAggListItem(item); }); }; @@ -90,7 +81,7 @@ export default class ReviewDefinition extends Component {
{field}
- ) + ); }; return ( @@ -117,9 +108,7 @@ export default class ReviewDefinition extends Component { titleSize="m" >
- - {aggListItems()} - + {aggListItems()}
diff --git a/public/pages/CreateTransform/components/ReviewDefinition/index.ts b/public/pages/CreateTransform/components/ReviewDefinition/index.ts index 7776389d4..6c9cb35a4 100644 --- a/public/pages/CreateTransform/components/ReviewDefinition/index.ts +++ b/public/pages/CreateTransform/components/ReviewDefinition/index.ts @@ -1,16 +1,12 @@ /* - * Copyright 2021 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 * - * Licensed under the Apache License, Version 2.0 (the "License"). - * You may not use this file except in compliance with the License. - * A copy of the License is located at + * The OpenSearch Contributors require contributions made to + * this file be licensed under the Apache-2.0 license or a + * compatible open source license. * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * or in the "license" file accompanying this file. This file is distributed - * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either - * express or implied. See the License for the specific language governing - * permissions and limitations under the License. + * Modifications Copyright OpenSearch Contributors. See + * GitHub history for details. */ import ReviewDefinition from "./ReviewDefinition"; diff --git a/public/pages/CreateTransform/components/ReviewSchedule/ReviewSchedule.tsx b/public/pages/CreateTransform/components/ReviewSchedule/ReviewSchedule.tsx index 770aaac56..b4232bf49 100644 --- a/public/pages/CreateTransform/components/ReviewSchedule/ReviewSchedule.tsx +++ b/public/pages/CreateTransform/components/ReviewSchedule/ReviewSchedule.tsx @@ -1,16 +1,12 @@ /* - * Copyright 2021 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 * - * Licensed under the Apache License, Version 2.0 (the "License"). - * You may not use this file except in compliance with the License. - * A copy of the License is located at + * The OpenSearch Contributors require contributions made to + * this file be licensed under the Apache-2.0 license or a + * compatible open source license. * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * or in the "license" file accompanying this file. This file is distributed - * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either - * express or implied. See the License for the specific language governing - * permissions and limitations under the License. + * Modifications Copyright OpenSearch Contributors. See + * GitHub history for details. */ import React, { Component } from "react"; diff --git a/public/pages/CreateTransform/components/ReviewSchedule/index.ts b/public/pages/CreateTransform/components/ReviewSchedule/index.ts index 516d7cc30..5071cc5f9 100644 --- a/public/pages/CreateTransform/components/ReviewSchedule/index.ts +++ b/public/pages/CreateTransform/components/ReviewSchedule/index.ts @@ -1,16 +1,12 @@ /* - * Copyright 2021 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 * - * Licensed under the Apache License, Version 2.0 (the "License"). - * You may not use this file except in compliance with the License. - * A copy of the License is located at + * The OpenSearch Contributors require contributions made to + * this file be licensed under the Apache-2.0 license or a + * compatible open source license. * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * or in the "license" file accompanying this file. This file is distributed - * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either - * express or implied. See the License for the specific language governing - * permissions and limitations under the License. + * Modifications Copyright OpenSearch Contributors. See + * GitHub history for details. */ import ReviewSchedule from "./ReviewSchedule"; diff --git a/public/pages/CreateTransform/components/Schedule/Schedule.tsx b/public/pages/CreateTransform/components/Schedule/Schedule.tsx index 92ae11b13..0fb8a10c5 100644 --- a/public/pages/CreateTransform/components/Schedule/Schedule.tsx +++ b/public/pages/CreateTransform/components/Schedule/Schedule.tsx @@ -1,27 +1,16 @@ /* - * Copyright 2020 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 * - * Licensed under the Apache License, Version 2.0 (the "License"). - * You may not use this file except in compliance with the License. - * A copy of the License is located at + * The OpenSearch Contributors require contributions made to + * this file be licensed under the Apache-2.0 license or a + * compatible open source license. * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * or in the "license" file accompanying this file. This file is distributed - * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either - * express or implied. See the License for the specific language governing - * permissions and limitations under the License. + * Modifications Copyright OpenSearch Contributors. See + * GitHub history for details. */ import React, { ChangeEvent, Component } from "react"; -import { - EuiSpacer, - EuiCheckbox, - EuiFormRow, - EuiFieldNumber, - EuiAccordion, - EuiHorizontalRule, -} from "@elastic/eui"; +import { EuiSpacer, EuiCheckbox, EuiFormRow, EuiFieldNumber, EuiAccordion, EuiHorizontalRule } from "@elastic/eui"; import { ContentPanel } from "../../../../components/ContentPanel"; import { selectInterval } from "../../../Transforms/utils/metadataHelper"; diff --git a/public/pages/CreateTransform/components/Schedule/index.ts b/public/pages/CreateTransform/components/Schedule/index.ts index f18d83cb2..876263cbe 100644 --- a/public/pages/CreateTransform/components/Schedule/index.ts +++ b/public/pages/CreateTransform/components/Schedule/index.ts @@ -1,16 +1,12 @@ /* - * Copyright 2021 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 * - * Licensed under the Apache License, Version 2.0 (the "License"). - * You may not use this file except in compliance with the License. - * A copy of the License is located at + * The OpenSearch Contributors require contributions made to + * this file be licensed under the Apache-2.0 license or a + * compatible open source license. * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * or in the "license" file accompanying this file. This file is distributed - * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either - * express or implied. See the License for the specific language governing - * permissions and limitations under the License. + * Modifications Copyright OpenSearch Contributors. See + * GitHub history for details. */ import Schedule from "./Schedule"; diff --git a/public/pages/CreateTransform/components/TransformIndices/TransformIndices.tsx b/public/pages/CreateTransform/components/TransformIndices/TransformIndices.tsx index 6eed3a3eb..18c293222 100644 --- a/public/pages/CreateTransform/components/TransformIndices/TransformIndices.tsx +++ b/public/pages/CreateTransform/components/TransformIndices/TransformIndices.tsx @@ -1,16 +1,12 @@ /* - * Copyright 2021 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 * - * Licensed under the Apache License, Version 2.0 (the "License"). - * You may not use this file except in compliance with the License. - * A copy of the License is located at + * The OpenSearch Contributors require contributions made to + * this file be licensed under the Apache-2.0 license or a + * compatible open source license. * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * or in the "license" file accompanying this file. This file is distributed - * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either - * express or implied. See the License for the specific language governing - * permissions and limitations under the License. + * Modifications Copyright OpenSearch Contributors. See + * GitHub history for details. */ import React, { Component, Fragment } from "react"; diff --git a/public/pages/CreateTransform/components/TransformIndices/index.ts b/public/pages/CreateTransform/components/TransformIndices/index.ts index ffee9e56f..dfd1191e3 100644 --- a/public/pages/CreateTransform/components/TransformIndices/index.ts +++ b/public/pages/CreateTransform/components/TransformIndices/index.ts @@ -1,16 +1,12 @@ /* - * Copyright 2021 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 * - * Licensed under the Apache License, Version 2.0 (the "License"). - * You may not use this file except in compliance with the License. - * A copy of the License is located at + * The OpenSearch Contributors require contributions made to + * this file be licensed under the Apache-2.0 license or a + * compatible open source license. * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * or in the "license" file accompanying this file. This file is distributed - * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either - * express or implied. See the License for the specific language governing - * permissions and limitations under the License. + * Modifications Copyright OpenSearch Contributors. See + * GitHub history for details. */ import TransformIndices from "./TransformIndices"; diff --git a/public/pages/CreateTransform/components/TransformOptions/Panels/HistogramPanel/HistogramPanel.tsx b/public/pages/CreateTransform/components/TransformOptions/Panels/HistogramPanel/HistogramPanel.tsx index a8af94474..5a95af60b 100644 --- a/public/pages/CreateTransform/components/TransformOptions/Panels/HistogramPanel/HistogramPanel.tsx +++ b/public/pages/CreateTransform/components/TransformOptions/Panels/HistogramPanel/HistogramPanel.tsx @@ -1,16 +1,12 @@ /* - * Copyright 2021 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 * - * Licensed under the Apache License, Version 2.0 (the "License"). - * You may not use this file except in compliance with the License. - * A copy of the License is located at + * The OpenSearch Contributors require contributions made to + * this file be licensed under the Apache-2.0 license or a + * compatible open source license. * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * or in the "license" file accompanying this file. This file is distributed - * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either - * express or implied. See the License for the specific language governing - * permissions and limitations under the License. + * Modifications Copyright OpenSearch Contributors. See + * GitHub history for details. */ import { GROUP_TYPES, TRANSFORM_AGG_TYPE, TransformAggItem, TransformGroupItem } from "../../../../../../../models/interfaces"; diff --git a/public/pages/CreateTransform/components/TransformOptions/Panels/HistogramPanel/index.ts b/public/pages/CreateTransform/components/TransformOptions/Panels/HistogramPanel/index.ts index 68f34c95f..a7b31d7ef 100644 --- a/public/pages/CreateTransform/components/TransformOptions/Panels/HistogramPanel/index.ts +++ b/public/pages/CreateTransform/components/TransformOptions/Panels/HistogramPanel/index.ts @@ -1,16 +1,12 @@ /* - * Copyright 2021 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 * - * Licensed under the Apache License, Version 2.0 (the "License"). - * You may not use this file except in compliance with the License. - * A copy of the License is located at + * The OpenSearch Contributors require contributions made to + * this file be licensed under the Apache-2.0 license or a + * compatible open source license. * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * or in the "license" file accompanying this file. This file is distributed - * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either - * express or implied. See the License for the specific language governing - * permissions and limitations under the License. + * Modifications Copyright OpenSearch Contributors. See + * GitHub history for details. */ import HistogramPanel from "./HistogramPanel"; diff --git a/public/pages/CreateTransform/components/TransformOptions/Panels/PercentilePanel/PercentilePanel.tsx b/public/pages/CreateTransform/components/TransformOptions/Panels/PercentilePanel/PercentilePanel.tsx index a01413566..b752df121 100644 --- a/public/pages/CreateTransform/components/TransformOptions/Panels/PercentilePanel/PercentilePanel.tsx +++ b/public/pages/CreateTransform/components/TransformOptions/Panels/PercentilePanel/PercentilePanel.tsx @@ -1,16 +1,12 @@ /* - * Copyright 2021 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 * - * Licensed under the Apache License, Version 2.0 (the "License"). - * You may not use this file except in compliance with the License. - * A copy of the License is located at + * The OpenSearch Contributors require contributions made to + * this file be licensed under the Apache-2.0 license or a + * compatible open source license. * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * or in the "license" file accompanying this file. This file is distributed - * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either - * express or implied. See the License for the specific language governing - * permissions and limitations under the License. + * Modifications Copyright OpenSearch Contributors. See + * GitHub history for details. */ import React, { useState } from "react"; diff --git a/public/pages/CreateTransform/components/TransformOptions/Panels/PercentilePanel/index.ts b/public/pages/CreateTransform/components/TransformOptions/Panels/PercentilePanel/index.ts index b4fd5ea91..fa3c3aab9 100644 --- a/public/pages/CreateTransform/components/TransformOptions/Panels/PercentilePanel/index.ts +++ b/public/pages/CreateTransform/components/TransformOptions/Panels/PercentilePanel/index.ts @@ -1,16 +1,12 @@ /* - * Copyright 2021 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 * - * Licensed under the Apache License, Version 2.0 (the "License"). - * You may not use this file except in compliance with the License. - * A copy of the License is located at + * The OpenSearch Contributors require contributions made to + * this file be licensed under the Apache-2.0 license or a + * compatible open source license. * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * or in the "license" file accompanying this file. This file is distributed - * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either - * express or implied. See the License for the specific language governing - * permissions and limitations under the License. + * Modifications Copyright OpenSearch Contributors. See + * GitHub history for details. */ import PercentilePanel from "./PercentilePanel"; diff --git a/public/pages/CreateTransform/components/TransformOptions/TransformOptions.tsx b/public/pages/CreateTransform/components/TransformOptions/TransformOptions.tsx index 9b7a8aa5d..016248796 100644 --- a/public/pages/CreateTransform/components/TransformOptions/TransformOptions.tsx +++ b/public/pages/CreateTransform/components/TransformOptions/TransformOptions.tsx @@ -1,16 +1,12 @@ /* - * Copyright 2021 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 * - * Licensed under the Apache License, Version 2.0 (the "License"). - * You may not use this file except in compliance with the License. - * A copy of the License is located at + * The OpenSearch Contributors require contributions made to + * this file be licensed under the Apache-2.0 license or a + * compatible open source license. * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * or in the "license" file accompanying this file. This file is distributed - * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either - * express or implied. See the License for the specific language governing - * permissions and limitations under the License. + * Modifications Copyright OpenSearch Contributors. See + * GitHub history for details. */ import React, { useState } from "react"; diff --git a/public/pages/CreateTransform/components/TransformOptions/index.ts b/public/pages/CreateTransform/components/TransformOptions/index.ts index 054d1acf1..61624f2e9 100644 --- a/public/pages/CreateTransform/components/TransformOptions/index.ts +++ b/public/pages/CreateTransform/components/TransformOptions/index.ts @@ -1,16 +1,12 @@ /* - * Copyright 2021 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 * - * Licensed under the Apache License, Version 2.0 (the "License"). - * You may not use this file except in compliance with the License. - * A copy of the License is located at + * The OpenSearch Contributors require contributions made to + * this file be licensed under the Apache-2.0 license or a + * compatible open source license. * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * or in the "license" file accompanying this file. This file is distributed - * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either - * express or implied. See the License for the specific language governing - * permissions and limitations under the License. + * Modifications Copyright OpenSearch Contributors. See + * GitHub history for details. */ import TransformOptions from "./TransformOptions"; diff --git a/public/pages/CreateTransform/containers/CreateTransform/CreateTransform.tsx b/public/pages/CreateTransform/containers/CreateTransform/CreateTransform.tsx index 0d48fb62c..baa2bebbf 100644 --- a/public/pages/CreateTransform/containers/CreateTransform/CreateTransform.tsx +++ b/public/pages/CreateTransform/containers/CreateTransform/CreateTransform.tsx @@ -1,16 +1,12 @@ /* - * Copyright 2021 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 * - * Licensed under the Apache License, Version 2.0 (the "License"). - * You may not use this file except in compliance with the License. - * A copy of the License is located at + * The OpenSearch Contributors require contributions made to + * this file be licensed under the Apache-2.0 license or a + * compatible open source license. * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * or in the "license" file accompanying this file. This file is distributed - * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either - * express or implied. See the License for the specific language governing - * permissions and limitations under the License. + * Modifications Copyright OpenSearch Contributors. See + * GitHub history for details. */ import React, { ChangeEvent, Component } from "react"; diff --git a/public/pages/CreateTransform/containers/CreateTransform/index.ts b/public/pages/CreateTransform/containers/CreateTransform/index.ts index b01f74a8a..25ab48891 100644 --- a/public/pages/CreateTransform/containers/CreateTransform/index.ts +++ b/public/pages/CreateTransform/containers/CreateTransform/index.ts @@ -1,16 +1,12 @@ /* - * Copyright 2021 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 * - * Licensed under the Apache License, Version 2.0 (the "License"). - * You may not use this file except in compliance with the License. - * A copy of the License is located at + * The OpenSearch Contributors require contributions made to + * this file be licensed under the Apache-2.0 license or a + * compatible open source license. * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * or in the "license" file accompanying this file. This file is distributed - * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either - * express or implied. See the License for the specific language governing - * permissions and limitations under the License. + * Modifications Copyright OpenSearch Contributors. See + * GitHub history for details. */ import CreateTransform from "./CreateTransform"; diff --git a/public/pages/CreateTransform/containers/CreateTransformForm/CreateTransformForm.tsx b/public/pages/CreateTransform/containers/CreateTransformForm/CreateTransformForm.tsx index e8b3220da..46dcf780b 100644 --- a/public/pages/CreateTransform/containers/CreateTransformForm/CreateTransformForm.tsx +++ b/public/pages/CreateTransform/containers/CreateTransformForm/CreateTransformForm.tsx @@ -1,16 +1,12 @@ /* - * Copyright 2021 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 * - * Licensed under the Apache License, Version 2.0 (the "License"). - * You may not use this file except in compliance with the License. - * A copy of the License is located at + * The OpenSearch Contributors require contributions made to + * this file be licensed under the Apache-2.0 license or a + * compatible open source license. * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * or in the "license" file accompanying this file. This file is distributed - * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either - * express or implied. See the License for the specific language governing - * permissions and limitations under the License. + * Modifications Copyright OpenSearch Contributors. See + * GitHub history for details. */ import React, { ChangeEvent, Component } from "react"; @@ -278,7 +274,7 @@ export default class CreateTransformForm extends Component { let newJSON = this.state.transformJSON; if (newFilter == "") { - newJSON.transform.hasOwnProperty("data_selection_query") && delete newJSON.transform.data_selection_query + newJSON.transform.hasOwnProperty("data_selection_query") && delete newJSON.transform.data_selection_query; } else { try { newJSON.transform.data_selection_query = JSON.parse(newFilter); @@ -500,9 +496,7 @@ export default class CreateTransformForm extends Component - By deleting {item}, all future scheduled executions will be canceled. However, your target index - and data in it will remain intact. + By deleting {item}, all future scheduled executions will be canceled. However, your target index and data in + it will remain intact. diff --git a/public/pages/Transforms/components/DeleteModal/index.ts b/public/pages/Transforms/components/DeleteModal/index.ts index 0d2c98428..526ba3f42 100644 --- a/public/pages/Transforms/components/DeleteModal/index.ts +++ b/public/pages/Transforms/components/DeleteModal/index.ts @@ -1,16 +1,12 @@ /* - * Copyright 2021 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 * - * Licensed under the Apache License, Version 2.0 (the "License"). - * You may not use this file except in compliance with the License. - * A copy of the License is located at + * The OpenSearch Contributors require contributions made to + * this file be licensed under the Apache-2.0 license or a + * compatible open source license. * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * or in the "license" file accompanying this file. This file is distributed - * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either - * express or implied. See the License for the specific language governing - * permissions and limitations under the License. + * Modifications Copyright OpenSearch Contributors. See + * GitHub history for details. */ import DeleteModal from "./DeleteModal"; diff --git a/public/pages/Transforms/components/ErrorModal/ErrorModal.tsx b/public/pages/Transforms/components/ErrorModal/ErrorModal.tsx index c6e511866..dea2ba6af 100644 --- a/public/pages/Transforms/components/ErrorModal/ErrorModal.tsx +++ b/public/pages/Transforms/components/ErrorModal/ErrorModal.tsx @@ -1,16 +1,12 @@ /* - * Copyright 2021 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 * - * Licensed under the Apache License, Version 2.0 (the "License"). - * You may not use this file except in compliance with the License. - * A copy of the License is located at + * The OpenSearch Contributors require contributions made to + * this file be licensed under the Apache-2.0 license or a + * compatible open source license. * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * or in the "license" file accompanying this file. This file is distributed - * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either - * express or implied. See the License for the specific language governing - * permissions and limitations under the License. + * Modifications Copyright OpenSearch Contributors. See + * GitHub history for details. */ import React from "react"; diff --git a/public/pages/Transforms/components/ErrorModal/index.ts b/public/pages/Transforms/components/ErrorModal/index.ts index a3ed8e5b5..59873aad3 100644 --- a/public/pages/Transforms/components/ErrorModal/index.ts +++ b/public/pages/Transforms/components/ErrorModal/index.ts @@ -1,16 +1,12 @@ /* - * Copyright 2021 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 * - * Licensed under the Apache License, Version 2.0 (the "License"). - * You may not use this file except in compliance with the License. - * A copy of the License is located at + * The OpenSearch Contributors require contributions made to + * this file be licensed under the Apache-2.0 license or a + * compatible open source license. * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * or in the "license" file accompanying this file. This file is distributed - * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either - * express or implied. See the License for the specific language governing - * permissions and limitations under the License. + * Modifications Copyright OpenSearch Contributors. See + * GitHub history for details. */ import ErrorModal from "./ErrorModal"; diff --git a/public/pages/Transforms/components/GeneralInformation/GeneralInformation.tsx b/public/pages/Transforms/components/GeneralInformation/GeneralInformation.tsx index 68227c402..6cb39b4d8 100644 --- a/public/pages/Transforms/components/GeneralInformation/GeneralInformation.tsx +++ b/public/pages/Transforms/components/GeneralInformation/GeneralInformation.tsx @@ -1,16 +1,12 @@ /* - * Copyright 2021 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 * - * Licensed under the Apache License, Version 2.0 (the "License"). - * You may not use this file except in compliance with the License. - * A copy of the License is located at + * The OpenSearch Contributors require contributions made to + * this file be licensed under the Apache-2.0 license or a + * compatible open source license. * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * or in the "license" file accompanying this file. This file is distributed - * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either - * express or implied. See the License for the specific language governing - * permissions and limitations under the License. + * Modifications Copyright OpenSearch Contributors. See + * GitHub history for details. */ import React, { Component } from "react"; @@ -37,15 +33,7 @@ export default class GenerationInformation extends Component { {metadata == null || metadata.transform_metadata == null ? "-" : metadata.transform_metadata.stats.search_time_in_millis} - +
diff --git a/public/pages/Transforms/components/TransformStatus/index.ts b/public/pages/Transforms/components/TransformStatus/index.ts index 8f2f8df70..1521fb1c3 100644 --- a/public/pages/Transforms/components/TransformStatus/index.ts +++ b/public/pages/Transforms/components/TransformStatus/index.ts @@ -1,16 +1,12 @@ /* - * Copyright 2021 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 * - * Licensed under the Apache License, Version 2.0 (the "License"). - * You may not use this file except in compliance with the License. - * A copy of the License is located at + * The OpenSearch Contributors require contributions made to + * this file be licensed under the Apache-2.0 license or a + * compatible open source license. * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * or in the "license" file accompanying this file. This file is distributed - * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either - * express or implied. See the License for the specific language governing - * permissions and limitations under the License. + * Modifications Copyright OpenSearch Contributors. See + * GitHub history for details. */ import TransformStatus from "./TransformStatus"; diff --git a/public/pages/Transforms/containers/Transforms/EditTransform.tsx b/public/pages/Transforms/containers/Transforms/EditTransform.tsx index 3f834f7eb..7b196e62a 100644 --- a/public/pages/Transforms/containers/Transforms/EditTransform.tsx +++ b/public/pages/Transforms/containers/Transforms/EditTransform.tsx @@ -1,16 +1,12 @@ /* - * Copyright 2021 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 * - * Licensed under the Apache License, Version 2.0 (the "License"). - * You may not use this file except in compliance with the License. - * A copy of the License is located at + * The OpenSearch Contributors require contributions made to + * this file be licensed under the Apache-2.0 license or a + * compatible open source license. * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * or in the "license" file accompanying this file. This file is distributed - * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either - * express or implied. See the License for the specific language governing - * permissions and limitations under the License. + * Modifications Copyright OpenSearch Contributors. See + * GitHub history for details. */ import { RouteComponentProps } from "react-router-dom"; diff --git a/public/pages/Transforms/containers/Transforms/TransformDetails.tsx b/public/pages/Transforms/containers/Transforms/TransformDetails.tsx index 57b080a2d..beb46f62d 100644 --- a/public/pages/Transforms/containers/Transforms/TransformDetails.tsx +++ b/public/pages/Transforms/containers/Transforms/TransformDetails.tsx @@ -1,16 +1,12 @@ /* - * Copyright 2021 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 * - * Licensed under the Apache License, Version 2.0 (the "License"). - * You may not use this file except in compliance with the License. - * A copy of the License is located at + * The OpenSearch Contributors require contributions made to + * this file be licensed under the Apache-2.0 license or a + * compatible open source license. * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * or in the "license" file accompanying this file. This file is distributed - * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either - * express or implied. See the License for the specific language governing - * permissions and limitations under the License. + * Modifications Copyright OpenSearch Contributors. See + * GitHub history for details. */ import { diff --git a/public/pages/Transforms/containers/Transforms/TransformSettings.tsx b/public/pages/Transforms/containers/Transforms/TransformSettings.tsx index 36f83c240..803f7b046 100644 --- a/public/pages/Transforms/containers/Transforms/TransformSettings.tsx +++ b/public/pages/Transforms/containers/Transforms/TransformSettings.tsx @@ -1,16 +1,12 @@ /* - * Copyright 2021 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 * - * Licensed under the Apache License, Version 2.0 (the "License"). - * You may not use this file except in compliance with the License. - * A copy of the License is located at + * The OpenSearch Contributors require contributions made to + * this file be licensed under the Apache-2.0 license or a + * compatible open source license. * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * or in the "license" file accompanying this file. This file is distributed - * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either - * express or implied. See the License for the specific language governing - * permissions and limitations under the License. + * Modifications Copyright OpenSearch Contributors. See + * GitHub history for details. */ import React, { Component } from "react"; @@ -19,9 +15,7 @@ import { EuiSpacer, EuiText, EuiAccordion, EuiFlexGrid, EuiFlexItem } from "@ela import { htmlIdGenerator } from "@elastic/eui/lib/services"; import { ContentPanel } from "../../../../components/ContentPanel"; import { TransformService } from "../../../../services"; -import { DimensionItem, FieldItem } from "../../../../../models/interfaces"; -import DefineTransforms from "../../../CreateTransform/components/DefineTransforms"; -import { compareFieldItem, parseFieldOptions } from "../../../CreateTransform/utils/helpers"; +import { DimensionItem } from "../../../../../models/interfaces"; import { getErrorMessage } from "../../../../utils/helpers"; import PreviewTransforms from "../../../CreateTransform/components/PreviewTransform"; @@ -159,6 +153,6 @@ export default class TransformSettings extends Component { - const response = await this.previewTransform({ transform: this.props.transformJson.transform }); + await this.previewTransform({ transform: this.props.transformJson.transform }); }; } diff --git a/public/pages/Transforms/containers/Transforms/Transforms.tsx b/public/pages/Transforms/containers/Transforms/Transforms.tsx index d90001884..d961dd282 100644 --- a/public/pages/Transforms/containers/Transforms/Transforms.tsx +++ b/public/pages/Transforms/containers/Transforms/Transforms.tsx @@ -1,16 +1,12 @@ /* - * Copyright 2021 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 * - * Licensed under the Apache License, Version 2.0 (the "License"). - * You may not use this file except in compliance with the License. - * A copy of the License is located at + * The OpenSearch Contributors require contributions made to + * this file be licensed under the Apache-2.0 license or a + * compatible open source license. * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * or in the "license" file accompanying this file. This file is distributed - * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either - * express or implied. See the License for the specific language governing - * permissions and limitations under the License. + * Modifications Copyright OpenSearch Contributors. See + * GitHub history for details. */ import { @@ -351,7 +347,11 @@ export default class Transforms extends Component { - return this.state.selectedItems.map((item: DocumentTransform) => { return item._id }).join(", "); + return this.state.selectedItems + .map((item: DocumentTransform) => { + return item._id; + }) + .join(", "); }; onSelectionChange = (selectedItems: DocumentTransform[]): void => { diff --git a/public/pages/Transforms/containers/Transforms/index.ts b/public/pages/Transforms/containers/Transforms/index.ts index 9ccbf849d..3755e0675 100644 --- a/public/pages/Transforms/containers/Transforms/index.ts +++ b/public/pages/Transforms/containers/Transforms/index.ts @@ -1,16 +1,12 @@ /* - * Copyright 2021 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 * - * Licensed under the Apache License, Version 2.0 (the "License"). - * You may not use this file except in compliance with the License. - * A copy of the License is located at + * The OpenSearch Contributors require contributions made to + * this file be licensed under the Apache-2.0 license or a + * compatible open source license. * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * or in the "license" file accompanying this file. This file is distributed - * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either - * express or implied. See the License for the specific language governing - * permissions and limitations under the License. + * Modifications Copyright OpenSearch Contributors. See + * GitHub history for details. */ import EditTransform from "./EditTransform"; diff --git a/public/pages/Transforms/index.ts b/public/pages/Transforms/index.ts index 7f224906b..60a2371ed 100644 --- a/public/pages/Transforms/index.ts +++ b/public/pages/Transforms/index.ts @@ -1,16 +1,12 @@ /* - * Copyright 2021 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 * - * Licensed under the Apache License, Version 2.0 (the "License"). - * You may not use this file except in compliance with the License. - * A copy of the License is located at + * The OpenSearch Contributors require contributions made to + * this file be licensed under the Apache-2.0 license or a + * compatible open source license. * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * or in the "license" file accompanying this file. This file is distributed - * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either - * express or implied. See the License for the specific language governing - * permissions and limitations under the License. + * Modifications Copyright OpenSearch Contributors. See + * GitHub history for details. */ import { EditTransform, Transforms } from "./containers/Transforms"; diff --git a/public/pages/Transforms/models/interfaces.ts b/public/pages/Transforms/models/interfaces.ts index 620afece3..253fe4544 100644 --- a/public/pages/Transforms/models/interfaces.ts +++ b/public/pages/Transforms/models/interfaces.ts @@ -1,20 +1,16 @@ /* - * Copyright 2021 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 * - * Licensed under the Apache License, Version 2.0 (the "License"). - * You may not use this file except in compliance with the License. - * A copy of the License is located at + * The OpenSearch Contributors require contributions made to + * this file be licensed under the Apache-2.0 license or a + * compatible open source license. * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * or in the "license" file accompanying this file. This file is distributed - * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either - * express or implied. See the License for the specific language governing - * permissions and limitations under the License. + * Modifications Copyright OpenSearch Contributors. See + * GitHub history for details. */ import { Direction } from "@elastic/eui"; -import {DocumentTransform} from "../../../../models/interfaces"; +import { DocumentTransform } from "../../../../models/interfaces"; export interface TransformQueryParams { from: number; diff --git a/public/pages/Transforms/utils/constants.tsx b/public/pages/Transforms/utils/constants.tsx index 2adea0dbe..c85a32ba4 100644 --- a/public/pages/Transforms/utils/constants.tsx +++ b/public/pages/Transforms/utils/constants.tsx @@ -1,16 +1,12 @@ /* - * Copyright 2021 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 * - * Licensed under the Apache License, Version 2.0 (the "License"). - * You may not use this file except in compliance with the License. - * A copy of the License is located at + * The OpenSearch Contributors require contributions made to + * this file be licensed under the Apache-2.0 license or a + * compatible open source license. * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * or in the "license" file accompanying this file. This file is distributed - * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either - * express or implied. See the License for the specific language governing - * permissions and limitations under the License. + * Modifications Copyright OpenSearch Contributors. See + * GitHub history for details. */ import { SortDirection } from "../../../utils/constants"; diff --git a/public/pages/Transforms/utils/helpers.ts b/public/pages/Transforms/utils/helpers.ts index 687e289ab..edb264777 100644 --- a/public/pages/Transforms/utils/helpers.ts +++ b/public/pages/Transforms/utils/helpers.ts @@ -1,16 +1,12 @@ /* - * Copyright 2021 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 * - * Licensed under the Apache License, Version 2.0 (the "License"). - * You may not use this file except in compliance with the License. - * A copy of the License is located at + * The OpenSearch Contributors require contributions made to + * this file be licensed under the Apache-2.0 license or a + * compatible open source license. * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * or in the "license" file accompanying this file. This file is distributed - * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either - * express or implied. See the License for the specific language governing - * permissions and limitations under the License. + * Modifications Copyright OpenSearch Contributors. See + * GitHub history for details. */ import queryString from "query-string"; diff --git a/public/pages/Transforms/utils/metadataHelper.tsx b/public/pages/Transforms/utils/metadataHelper.tsx index c1ce56a20..eb251bd52 100644 --- a/public/pages/Transforms/utils/metadataHelper.tsx +++ b/public/pages/Transforms/utils/metadataHelper.tsx @@ -1,16 +1,12 @@ /* - * Copyright 2021 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 * - * Licensed under the Apache License, Version 2.0 (the "License"). - * You may not use this file except in compliance with the License. - * A copy of the License is located at + * The OpenSearch Contributors require contributions made to + * this file be licensed under the Apache-2.0 license or a + * compatible open source license. * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * or in the "license" file accompanying this file. This file is distributed - * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either - * express or implied. See the License for the specific language governing - * permissions and limitations under the License. + * Modifications Copyright OpenSearch Contributors. See + * GitHub history for details. */ import { TransformMetadata } from "../../../../models/interfaces"; diff --git a/public/services/TransformService.ts b/public/services/TransformService.ts index 1c3a6710c..f360e9e51 100644 --- a/public/services/TransformService.ts +++ b/public/services/TransformService.ts @@ -1,21 +1,17 @@ /* - * Copyright 2021 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 * - * Licensed under the Apache License, Version 2.0 (the "License"). - * You may not use this file except in compliance with the License. - * A copy of the License is located at + * The OpenSearch Contributors require contributions made to + * this file be licensed under the Apache-2.0 license or a + * compatible open source license. * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * or in the "license" file accompanying this file. This file is distributed - * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either - * express or implied. See the License for the specific language governing - * permissions and limitations under the License. + * Modifications Copyright OpenSearch Contributors. See + * GitHub history for details. */ import { HttpSetup } from "opensearch-dashboards/public"; import { ServerResponse } from "../../server/models/types"; -import { GetTransformsResponse, PreviewTransformResponse, PutTransformResponse } from "../../server/models/interfaces"; +import { GetFieldsResponse, GetTransformsResponse, PreviewTransformResponse, PutTransformResponse } from "../../server/models/interfaces"; import { NODE_API } from "../../utils/constants"; import { DocumentTransform, Transform } from "../../models/interfaces"; diff --git a/server/routes/transforms.ts b/server/routes/transforms.ts index 88d9b5ad5..c98ad8072 100644 --- a/server/routes/transforms.ts +++ b/server/routes/transforms.ts @@ -1,16 +1,12 @@ /* - * Copyright 2021 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 * - * Licensed under the Apache License, Version 2.0 (the "License"). - * You may not use this file except in compliance with the License. - * A copy of the License is located at + * The OpenSearch Contributors require contributions made to + * this file be licensed under the Apache-2.0 license or a + * compatible open source license. * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * or in the "license" file accompanying this file. This file is distributed - * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either - * express or implied. See the License for the specific language governing - * permissions and limitations under the License. + * Modifications Copyright OpenSearch Contributors. See + * GitHub history for details. */ import { NodeServices } from "../models/interfaces"; diff --git a/server/services/TransformService.ts b/server/services/TransformService.ts index f065fb834..0495583a1 100644 --- a/server/services/TransformService.ts +++ b/server/services/TransformService.ts @@ -1,19 +1,21 @@ /* - * Copyright 2021 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 * - * Licensed under the Apache License, Version 2.0 (the "License"). - * You may not use this file except in compliance with the License. - * A copy of the License is located at + * The OpenSearch Contributors require contributions made to + * this file be licensed under the Apache-2.0 license or a + * compatible open source license. * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * or in the "license" file accompanying this file. This file is distributed - * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either - * express or implied. See the License for the specific language governing - * permissions and limitations under the License. + * Modifications Copyright OpenSearch Contributors. See + * GitHub history for details. */ -import { IClusterClient, IKibanaResponse, KibanaRequest, KibanaResponseFactory, RequestHandlerContext } from "kibana/server"; +import { + IClusterClient, + IOpenSearchDashboardsResponse, + OpenSearchDashboardsRequest, + OpenSearchDashboardsResponseFactory, + RequestHandlerContext, +} from "opensearch-dashboards/server"; import { ServerResponse } from "../models/types"; import { GetTransformsResponse, @@ -34,9 +36,9 @@ export default class TransformService { getTransforms = async ( context: RequestHandlerContext, - request: KibanaRequest, - response: KibanaResponseFactory - ): Promise>> => { + request: OpenSearchDashboardsRequest, + response: OpenSearchDashboardsResponseFactory + ): Promise>> => { try { const { from, size, search, sortDirection, sortField } = request.query as { from: number; @@ -121,9 +123,9 @@ export default class TransformService { getTransform = async ( context: RequestHandlerContext, - request: KibanaRequest, - response: KibanaResponseFactory - ): Promise>> => { + request: OpenSearchDashboardsRequest, + response: OpenSearchDashboardsResponseFactory + ): Promise>> => { try { const { id } = request.params as { id: string }; const params = { transformId: id }; @@ -181,9 +183,9 @@ export default class TransformService { startTransform = async ( context: RequestHandlerContext, - request: KibanaRequest, - response: KibanaResponseFactory - ): Promise>> => { + request: OpenSearchDashboardsRequest, + response: OpenSearchDashboardsResponseFactory + ): Promise>> => { try { const { id } = request.params as { id: string }; console.log("received " + JSON.stringify(request.params)); @@ -213,9 +215,9 @@ export default class TransformService { stopTransform = async ( context: RequestHandlerContext, - request: KibanaRequest, - response: KibanaResponseFactory - ): Promise>> => { + request: OpenSearchDashboardsRequest, + response: OpenSearchDashboardsResponseFactory + ): Promise>> => { try { const { id } = request.params as { id: string }; const params = { transformId: id }; @@ -244,9 +246,9 @@ export default class TransformService { deleteTransform = async ( context: RequestHandlerContext, - request: KibanaRequest, - response: KibanaResponseFactory - ): Promise>> => { + request: OpenSearchDashboardsRequest, + response: OpenSearchDashboardsResponseFactory + ): Promise>> => { try { const { id } = request.params as { id: string }; const params = { transformId: id }; @@ -274,9 +276,9 @@ export default class TransformService { putTransform = async ( context: RequestHandlerContext, - request: KibanaRequest, - response: KibanaResponseFactory - ): Promise>> => { + request: OpenSearchDashboardsRequest, + response: OpenSearchDashboardsResponseFactory + ): Promise>> => { try { const { id } = request.params as { id: string }; const { seqNo, primaryTerm } = request.query as { seqNo?: string; primaryTerm?: string }; @@ -314,9 +316,9 @@ export default class TransformService { searchSampleData = async ( context: RequestHandlerContext, - request: KibanaRequest, - response: KibanaResponseFactory - ): Promise>> => { + request: OpenSearchDashboardsRequest, + response: OpenSearchDashboardsResponseFactory + ): Promise>> => { try { const { from, size } = request.query as { from: number; @@ -370,9 +372,9 @@ export default class TransformService { previewTransform = async ( context: RequestHandlerContext, - request: KibanaRequest, - response: KibanaResponseFactory - ): Promise>> => { + request: OpenSearchDashboardsRequest, + response: OpenSearchDashboardsResponseFactory + ): Promise>> => { try { let params = { body: JSON.stringify(request.body), From 71a7401d6bbc5793526d3cabd90f40c05519e81d Mon Sep 17 00:00:00 2001 From: Ravi Thaluru Date: Tue, 25 May 2021 14:48:48 -0700 Subject: [PATCH 93/93] Cleanup unused code Signed-off-by: Ravi Thaluru --- .../BaseAggregationAndMetricSettings.tsx | 65 ++++++++++--------- .../HistogramAndMetrics.tsx | 51 ++++++++------- .../IndexFilterPopover/IndexFilterPopover.tsx | 7 -- .../components/Schedule/Schedule.tsx | 6 -- .../TransformIndices/TransformIndices.tsx | 12 ---- .../Panels/HistogramPanel/HistogramPanel.tsx | 3 +- .../TransformOptions/TransformOptions.tsx | 8 +-- .../components/Schedule/Schedule.tsx | 11 ---- .../TransformStatus/TransformStatus.tsx | 4 +- .../Transforms/TransformDetails.tsx | 4 +- .../Transforms/TransformSettings.tsx | 33 ++-------- .../containers/Transforms/Transforms.tsx | 2 +- 12 files changed, 72 insertions(+), 134 deletions(-) diff --git a/public/pages/Commons/BaseAggregationAndMetricSettings.tsx b/public/pages/Commons/BaseAggregationAndMetricSettings.tsx index a0468f000..7c433341c 100644 --- a/public/pages/Commons/BaseAggregationAndMetricSettings.tsx +++ b/public/pages/Commons/BaseAggregationAndMetricSettings.tsx @@ -1,3 +1,14 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * + * The OpenSearch Contributors require contributions made to + * this file be licensed under the Apache-2.0 license or a + * compatible open source license. + * + * Modifications Copyright OpenSearch Contributors. See + * GitHub history for details. + */ + /* * Copyright 2020 Amazon.com, Inc. or its affiliates. All Rights Reserved. * @@ -13,21 +24,13 @@ * permissions and limitations under the License. */ -import React, {Fragment} from "react"; +import React, { Fragment } from "react"; -import { - EuiFlexItem, - EuiText, - EuiBasicTable, - EuiTableFieldDataColumnType, - EuiPanel, - EuiFlexGroup, - EuiIcon, -} from "@elastic/eui"; +import { EuiFlexItem, EuiText, EuiBasicTable, EuiTableFieldDataColumnType, EuiPanel, EuiFlexGroup, EuiIcon } from "@elastic/eui"; -import {DimensionItem, MetricItem} from "../../../models/interfaces"; +import { DimensionItem, MetricItem } from "../../../models/interfaces"; -export const AGGREGATION_AND_METRIC_SETTINGS = 'Aggregation and metrics settings' +export const AGGREGATION_AND_METRIC_SETTINGS = "Aggregation and metrics settings"; export interface BaseAggregationAndMetricsState { from: number; @@ -40,8 +43,7 @@ export interface BaseAggregationAndMetricsState { dimensionSortDirection: string; } - -export const BaseAggregationColumns: Readonly>[] = [ +export const BaseAggregationColumns: Readonly>[] = [ { field: "sequence", name: "Sequence", @@ -71,7 +73,6 @@ export const BaseAggregationColumns: Readonly>[] = [ { field: "source_field", @@ -109,16 +110,17 @@ export const BaseMetricsColumns: Readonly -
No fields added for aggregation
- - ) +export function sequenceTableComponents(selectedDimensionField, items, columns, pagination, sorting, onChange) { + if (selectedDimensionField.length == 0) { + return ( + +
No fields added for aggregation
+
+ ); } - return ( + return ( + - ) + ); } export function additionalMetricsComponent(selectedMetrics) { @@ -149,21 +151,20 @@ export function additionalMetricsComponent(selectedMetrics) { - ) + ); } export function sourceFieldComponents(selectedMetrics, items, columns, pagination, sorting, onChange) { - - if(selectedMetrics.length == 0) { + if (selectedMetrics.length == 0) { return (
No fields added for metrics
- ) + ); } - return ( - + return ( + - - ) + + ); } diff --git a/public/pages/CreateRollup/components/HistogramAndMetrics/HistogramAndMetrics.tsx b/public/pages/CreateRollup/components/HistogramAndMetrics/HistogramAndMetrics.tsx index 56072fd2c..f8c076dc4 100644 --- a/public/pages/CreateRollup/components/HistogramAndMetrics/HistogramAndMetrics.tsx +++ b/public/pages/CreateRollup/components/HistogramAndMetrics/HistogramAndMetrics.tsx @@ -49,7 +49,10 @@ import { additionalMetricsComponent, AGGREGATION_AND_METRIC_SETTINGS, BaseAggregationAndMetricsState, - BaseAggregationColumns, BaseMetricsColumns, sequenceTableComponents, sourceFieldComponents + BaseAggregationColumns, + BaseMetricsColumns, + sequenceTableComponents, + sourceFieldComponents, } from "../../../Commons/BaseAggregationAndMetricSettings"; interface HistogramAndMetricsProps { @@ -69,7 +72,6 @@ interface HistogramAndMetricsState extends BaseAggregationAndMetricsState { dimensionsShown: DimensionItem[]; } - const _createFlowAggregateColumns: Readonly>[] = [ { field: "field.type", @@ -77,22 +79,22 @@ const _createFlowAggregateColumns: Readonly (type == undefined ? "-" : type), }, -] - -const _createFlowMetricsColumn: Readonly> = { - field: "all", - name: "All", - align: "center", - render: (all: boolean) => all && , - } +]; +const _createFlowMetricsColumn: Readonly> = { + field: "all", + name: "All", + align: "center", + render: (all: boolean) => all && , +}; -const aggregationColumns: Readonly>[] - = [...BaseAggregationColumns,..._createFlowAggregateColumns]; +const aggregationColumns: Readonly>[] = [ + ...BaseAggregationColumns, + ..._createFlowAggregateColumns, +]; // Adding 'all' column at 1st index. -const metricsColumns: EuiTableFieldDataColumnType[] = - [...BaseMetricsColumns].splice(1, 0, _createFlowMetricsColumn); +const metricsColumns: EuiTableFieldDataColumnType[] = [...BaseMetricsColumns].splice(1, 0, _createFlowMetricsColumn); export default class HistogramAndMetrics extends Component { constructor(props: HistogramAndMetricsProps) { @@ -256,22 +258,21 @@ export default class HistogramAndMetrics extends Component - { - sequenceTableComponents( selectedDimensionField, dimensionsShown, aggregationColumns, - dimensionPagination, dimensionSorting, this.onDimensionTableChange ) - } + {sequenceTableComponents( + selectedDimensionField, + dimensionsShown, + aggregationColumns, + dimensionPagination, + dimensionSorting, + this.onDimensionTableChange + )} - { - additionalMetricsComponent(selectedMetrics) - } + {additionalMetricsComponent(selectedMetrics)} - { - sourceFieldComponents(selectedMetrics, metricsShown, metricsColumns, pagination, - sorting, this.onTableChange) - } + {sourceFieldComponents(selectedMetrics, metricsShown, metricsColumns, pagination, sorting, this.onTableChange)}
diff --git a/public/pages/CreateTransform/components/IndexFilterPopover/IndexFilterPopover.tsx b/public/pages/CreateTransform/components/IndexFilterPopover/IndexFilterPopover.tsx index 4c8bc50fd..894ee742c 100644 --- a/public/pages/CreateTransform/components/IndexFilterPopover/IndexFilterPopover.tsx +++ b/public/pages/CreateTransform/components/IndexFilterPopover/IndexFilterPopover.tsx @@ -41,7 +41,6 @@ export default function IndexFilterPopover({ const [selectedField, setSelectedField] = useState(""); const [selectedOperator, setSelectedOperator] = useState(""); const [selectedValue, setSelectedValue] = useState(""); - const [isCustomEditorOpen, setIsCustomEditorOpen] = useState(false); const [queryDsl, setQueryDsl] = useState(sourceIndexFilter); const onChangeSelectedField = (e: ChangeEvent): void => { @@ -107,11 +106,6 @@ export default function IndexFilterPopover({ Edit data filter - {/**/} - {/* setIsCustomEditorOpen(!isCustomEditorOpen)}>*/} - {/* {isCustomEditorOpen ? "Edit filter values" : "Custom expression"}*/} - {/* */} - {/**/} @@ -127,7 +121,6 @@ export default function IndexFilterPopover({ onChangeSourceIndexFilter(queryDsl); closePopover(); }} - // isDisabled={!this.isFilterValid()} data-test-subj="saveFilter" > Save diff --git a/public/pages/CreateTransform/components/Schedule/Schedule.tsx b/public/pages/CreateTransform/components/Schedule/Schedule.tsx index 0fb8a10c5..45a455df7 100644 --- a/public/pages/CreateTransform/components/Schedule/Schedule.tsx +++ b/public/pages/CreateTransform/components/Schedule/Schedule.tsx @@ -63,12 +63,6 @@ export default class Schedule extends Component { {!isEdit} - {/* TODO: Removing transform execution frequency dropdown menu as only fix interval will be supported in P0. */} - {/**/} - {/* */} - {/**/} - {/**/} - {/* TODO: Replace with switch block when define by cron expressions is supported. */} {selectInterval(interval, intervalTimeunit, intervalError, onChangeIntervalTime, onChangeIntervalTimeunit)} diff --git a/public/pages/CreateTransform/components/TransformIndices/TransformIndices.tsx b/public/pages/CreateTransform/components/TransformIndices/TransformIndices.tsx index 18c293222..d6b6a97e0 100644 --- a/public/pages/CreateTransform/components/TransformIndices/TransformIndices.tsx +++ b/public/pages/CreateTransform/components/TransformIndices/TransformIndices.tsx @@ -132,15 +132,6 @@ export default class TransformIndices extends Component { - // const { dataFilters } = this.state; - // //debug - // console.log("to add: " + dataFilter + " existing filters: " + dataFilters); - // dataFilters.push(dataFilter); - // this.setState({ dataFilters }); - // this.closePopover(); - // }; - closePopover = () => this.setState({ isPopoverOpen: false }); render() { @@ -219,9 +210,6 @@ export default class TransformIndices extends Component - {/*{this.state.dataFilters.map((item) => (*/} - {/* {item}*/} - {/*))}*/} void; @@ -31,7 +32,7 @@ export default function HistogramPanel({ name, handleGroupSelectionChange, close - + diff --git a/public/pages/CreateTransform/components/TransformOptions/TransformOptions.tsx b/public/pages/CreateTransform/components/TransformOptions/TransformOptions.tsx index 016248796..8205f5ef5 100644 --- a/public/pages/CreateTransform/components/TransformOptions/TransformOptions.tsx +++ b/public/pages/CreateTransform/components/TransformOptions/TransformOptions.tsx @@ -49,8 +49,8 @@ export default function TransformOptions({ const isText = type == "text"; const [isPopoverOpen, setIsPopoverOpen] = useState(false); - const [groupSelection, setGroupSelection] = useState(selectedGroupField); - const [aggSelection, setAggSelection] = useState(selectedAggregations); + const [groupSelection] = useState(selectedGroupField); + const [aggSelection] = useState(selectedAggregations); const closePopover = () => { setIsPopoverOpen(false); @@ -178,10 +178,6 @@ export default function TransformOptions({ /> ), }, - // { - // id: 3, - // title: "Back", - // }, ]; const datePanels: EuiContextMenuPanelDescriptor[] = [ { diff --git a/public/pages/Transforms/components/Schedule/Schedule.tsx b/public/pages/Transforms/components/Schedule/Schedule.tsx index e22dd067e..b504f3152 100644 --- a/public/pages/Transforms/components/Schedule/Schedule.tsx +++ b/public/pages/Transforms/components/Schedule/Schedule.tsx @@ -66,17 +66,6 @@ export default class Schedule extends Component { /> - {/* TODO: Removing transform execution frequency dropdown menu as only fix interval will be supported in P0. */} - {/**/} - {/* */} - {/**/} - {/**/} - {/* TODO: Replace with switch block when define by cron expressions is supported. */} {selectInterval(interval, intervalTimeUnit, intervalError, onIntervalChange, onIntervalTimeUnitChange)} {/*{schedule == "fixed"*/} diff --git a/public/pages/Transforms/components/TransformStatus/TransformStatus.tsx b/public/pages/Transforms/components/TransformStatus/TransformStatus.tsx index 57bf21cd5..4558b42d3 100644 --- a/public/pages/Transforms/components/TransformStatus/TransformStatus.tsx +++ b/public/pages/Transforms/components/TransformStatus/TransformStatus.tsx @@ -61,8 +61,8 @@ export default class TransformStatus extends Component { - - + +
Page processed
diff --git a/public/pages/Transforms/containers/Transforms/TransformDetails.tsx b/public/pages/Transforms/containers/Transforms/TransformDetails.tsx index beb46f62d..06b5a5e90 100644 --- a/public/pages/Transforms/containers/Transforms/TransformDetails.tsx +++ b/public/pages/Transforms/containers/Transforms/TransformDetails.tsx @@ -363,13 +363,13 @@ export default class TransformDetails extends Component => { - // if (!srcIndex.length) return; - // try { - // const { rollupService } = this.props; - // const response = await rollupService.getMappings(srcIndex); - // if (response.ok) { - // let allMappings: FieldItem[][] = []; - // const mappings = response.response; - // //Push mappings array to allMappings 2D array first - // for (let index in mappings) { - // allMappings.push(parseFieldOptions("", mappings[index].mappings.properties)); - // } - // //Find intersect from all mappings - // const fields = allMappings.reduce((mappingA, mappingB) => - // mappingA.filter((itemA) => mappingB.some((itemB) => compareFieldItem(itemA, itemB))) - // ); - // this.setState({ mappings, fields, allMappings }); - // } else { - // this.context.notifications.toasts.addDanger(`Could not load fields: ${response.error}`); - // } - // } catch (err) { - // this.context.notifications.toasts.addDanger(getErrorMessage(err, "Could not load fields")); - // } - // }; - previewTransform = async (transform: any): Promise => { try { const { transformService } = this.props; @@ -85,9 +60,9 @@ export default class TransformSettings extends Component { console.log(groupsShown); - return groupsShown.map((group) => { + return groupsShown.map((group, index) => { return ( - +
Group by {group.aggregationMethod}
{group.field.label}
@@ -98,12 +73,12 @@ export default class TransformSettings extends Component { - return Object.keys(aggregationsShown).map((key) => { + return Object.keys(aggregationsShown).map((key, index) => { let aggregationType = Object.keys(aggregationsShown[key])[0]; let sourceField = aggregationsShown[key][aggregationType].field; return ( - +
{aggregationType}()
{sourceField}
diff --git a/public/pages/Transforms/containers/Transforms/Transforms.tsx b/public/pages/Transforms/containers/Transforms/Transforms.tsx index d961dd282..1f49f225c 100644 --- a/public/pages/Transforms/containers/Transforms/Transforms.tsx +++ b/public/pages/Transforms/containers/Transforms/Transforms.tsx @@ -391,7 +391,7 @@ export default class Transforms extends Component