mirror of
https://github.com/elastic/kibana.git
synced 2025-04-25 02:09:32 -04:00
[ML] Transforms: API schemas and integration tests (#75164)
- Adds schema definitions to transform API endpoints and adds API integration tests. - The type definitions based on the schema definitions can be used on the client side too. - Adds apidoc documentation.
This commit is contained in:
parent
cbcd1ebd32
commit
dd1822047c
127 changed files with 3110 additions and 1374 deletions
7
x-pack/plugins/ml/common/index.ts
Normal file
7
x-pack/plugins/ml/common/index.ts
Normal file
|
@ -0,0 +1,7 @@
|
||||||
|
/*
|
||||||
|
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||||
|
* or more contributor license agreements. Licensed under the Elastic License;
|
||||||
|
* you may not use this file except in compliance with the Elastic License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
export { SearchResponse7 } from './types/es_client';
|
25
x-pack/plugins/ml/common/types/es_client.ts
Normal file
25
x-pack/plugins/ml/common/types/es_client.ts
Normal file
|
@ -0,0 +1,25 @@
|
||||||
|
/*
|
||||||
|
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||||
|
* or more contributor license agreements. Licensed under the Elastic License;
|
||||||
|
* you may not use this file except in compliance with the Elastic License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
import { SearchResponse, ShardsResponse } from 'elasticsearch';
|
||||||
|
|
||||||
|
// The types specified in `@types/elasticsearch` are out of date and still have `total: number`.
|
||||||
|
interface SearchResponse7Hits<T> {
|
||||||
|
hits: SearchResponse<T>['hits']['hits'];
|
||||||
|
max_score: number;
|
||||||
|
total: {
|
||||||
|
value: number;
|
||||||
|
relation: string;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
export interface SearchResponse7<T = any> {
|
||||||
|
took: number;
|
||||||
|
timed_out: boolean;
|
||||||
|
_scroll_id?: string;
|
||||||
|
_shards: ShardsResponse;
|
||||||
|
hits: SearchResponse7Hits<T>;
|
||||||
|
aggregations?: any;
|
||||||
|
}
|
|
@ -19,7 +19,6 @@ export {
|
||||||
DataGridItem,
|
DataGridItem,
|
||||||
EsSorting,
|
EsSorting,
|
||||||
RenderCellValue,
|
RenderCellValue,
|
||||||
SearchResponse7,
|
|
||||||
UseDataGridReturnType,
|
UseDataGridReturnType,
|
||||||
UseIndexDataReturnType,
|
UseIndexDataReturnType,
|
||||||
} from './types';
|
} from './types';
|
||||||
|
|
|
@ -5,7 +5,6 @@
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import { Dispatch, SetStateAction } from 'react';
|
import { Dispatch, SetStateAction } from 'react';
|
||||||
import { SearchResponse } from 'elasticsearch';
|
|
||||||
|
|
||||||
import { EuiDataGridPaginationProps, EuiDataGridSorting, EuiDataGridColumn } from '@elastic/eui';
|
import { EuiDataGridPaginationProps, EuiDataGridSorting, EuiDataGridColumn } from '@elastic/eui';
|
||||||
|
|
||||||
|
@ -43,16 +42,6 @@ export type EsSorting = Dictionary<{
|
||||||
order: 'asc' | 'desc';
|
order: 'asc' | 'desc';
|
||||||
}>;
|
}>;
|
||||||
|
|
||||||
// The types specified in `@types/elasticsearch` are out of date and still have `total: number`.
|
|
||||||
export interface SearchResponse7 extends SearchResponse<any> {
|
|
||||||
hits: SearchResponse<any>['hits'] & {
|
|
||||||
total: {
|
|
||||||
value: number;
|
|
||||||
relation: string;
|
|
||||||
};
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface UseIndexDataReturnType
|
export interface UseIndexDataReturnType
|
||||||
extends Pick<
|
extends Pick<
|
||||||
UseDataGridReturnType,
|
UseDataGridReturnType,
|
||||||
|
|
|
@ -4,9 +4,10 @@
|
||||||
* you may not use this file except in compliance with the Elastic License.
|
* you may not use this file except in compliance with the Elastic License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
import type { SearchResponse7 } from '../../../../common/types/es_client';
|
||||||
import { extractErrorMessage } from '../../../../common/util/errors';
|
import { extractErrorMessage } from '../../../../common/util/errors';
|
||||||
|
|
||||||
import { EsSorting, SearchResponse7, UseDataGridReturnType } from '../../components/data_grid';
|
import { EsSorting, UseDataGridReturnType } from '../../components/data_grid';
|
||||||
import { ml } from '../../services/ml_api_service';
|
import { ml } from '../../services/ml_api_service';
|
||||||
|
|
||||||
import { isKeywordAndTextType } from '../common/fields';
|
import { isKeywordAndTextType } from '../common/fields';
|
||||||
|
|
|
@ -22,9 +22,9 @@ import {
|
||||||
useDataGrid,
|
useDataGrid,
|
||||||
useRenderCellValue,
|
useRenderCellValue,
|
||||||
EsSorting,
|
EsSorting,
|
||||||
SearchResponse7,
|
|
||||||
UseIndexDataReturnType,
|
UseIndexDataReturnType,
|
||||||
} from '../../../../components/data_grid';
|
} from '../../../../components/data_grid';
|
||||||
|
import type { SearchResponse7 } from '../../../../../../common/types/es_client';
|
||||||
import { extractErrorMessage } from '../../../../../../common/util/errors';
|
import { extractErrorMessage } from '../../../../../../common/util/errors';
|
||||||
import { INDEX_STATUS } from '../../../common/analytics';
|
import { INDEX_STATUS } from '../../../common/analytics';
|
||||||
import { ml } from '../../../../services/ml_api_service';
|
import { ml } from '../../../../services/ml_api_service';
|
||||||
|
|
|
@ -0,0 +1,9 @@
|
||||||
|
/*
|
||||||
|
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||||
|
* or more contributor license agreements. Licensed under the Elastic License;
|
||||||
|
* you may not use this file except in compliance with the Elastic License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
import { TransformMessage } from '../types/messages';
|
||||||
|
|
||||||
|
export type GetTransformsAuditMessagesResponseSchema = TransformMessage[];
|
48
x-pack/plugins/transform/common/api_schemas/common.ts
Normal file
48
x-pack/plugins/transform/common/api_schemas/common.ts
Normal file
|
@ -0,0 +1,48 @@
|
||||||
|
/*
|
||||||
|
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||||
|
* or more contributor license agreements. Licensed under the Elastic License;
|
||||||
|
* you may not use this file except in compliance with the Elastic License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
import { schema, TypeOf } from '@kbn/config-schema';
|
||||||
|
|
||||||
|
import { TRANSFORM_STATE } from '../constants';
|
||||||
|
|
||||||
|
export const transformIdsSchema = schema.arrayOf(
|
||||||
|
schema.object({
|
||||||
|
id: schema.string(),
|
||||||
|
})
|
||||||
|
);
|
||||||
|
|
||||||
|
export type TransformIdsSchema = TypeOf<typeof transformIdsSchema>;
|
||||||
|
|
||||||
|
export const transformStateSchema = schema.oneOf([
|
||||||
|
schema.literal(TRANSFORM_STATE.ABORTING),
|
||||||
|
schema.literal(TRANSFORM_STATE.FAILED),
|
||||||
|
schema.literal(TRANSFORM_STATE.INDEXING),
|
||||||
|
schema.literal(TRANSFORM_STATE.STARTED),
|
||||||
|
schema.literal(TRANSFORM_STATE.STOPPED),
|
||||||
|
schema.literal(TRANSFORM_STATE.STOPPING),
|
||||||
|
]);
|
||||||
|
|
||||||
|
export const indexPatternTitleSchema = schema.object({
|
||||||
|
/** Title of the index pattern for which to return stats. */
|
||||||
|
indexPatternTitle: schema.string(),
|
||||||
|
});
|
||||||
|
|
||||||
|
export type IndexPatternTitleSchema = TypeOf<typeof indexPatternTitleSchema>;
|
||||||
|
|
||||||
|
export const transformIdParamSchema = schema.object({
|
||||||
|
transformId: schema.string(),
|
||||||
|
});
|
||||||
|
|
||||||
|
export type TransformIdParamSchema = TypeOf<typeof transformIdParamSchema>;
|
||||||
|
|
||||||
|
export interface ResponseStatus {
|
||||||
|
success: boolean;
|
||||||
|
error?: any;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface CommonResponseStatusSchema {
|
||||||
|
[key: string]: ResponseStatus;
|
||||||
|
}
|
|
@ -0,0 +1,37 @@
|
||||||
|
/*
|
||||||
|
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||||
|
* or more contributor license agreements. Licensed under the Elastic License;
|
||||||
|
* you may not use this file except in compliance with the Elastic License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
import { schema, TypeOf } from '@kbn/config-schema';
|
||||||
|
|
||||||
|
import { transformStateSchema, ResponseStatus } from './common';
|
||||||
|
|
||||||
|
export const deleteTransformsRequestSchema = schema.object({
|
||||||
|
/**
|
||||||
|
* Delete Transform & Destination Index
|
||||||
|
*/
|
||||||
|
transformsInfo: schema.arrayOf(
|
||||||
|
schema.object({
|
||||||
|
id: schema.string(),
|
||||||
|
state: transformStateSchema,
|
||||||
|
})
|
||||||
|
),
|
||||||
|
deleteDestIndex: schema.maybe(schema.boolean()),
|
||||||
|
deleteDestIndexPattern: schema.maybe(schema.boolean()),
|
||||||
|
forceDelete: schema.maybe(schema.boolean()),
|
||||||
|
});
|
||||||
|
|
||||||
|
export type DeleteTransformsRequestSchema = TypeOf<typeof deleteTransformsRequestSchema>;
|
||||||
|
|
||||||
|
export interface DeleteTransformStatus {
|
||||||
|
transformDeleted: ResponseStatus;
|
||||||
|
destIndexDeleted?: ResponseStatus;
|
||||||
|
destIndexPatternDeleted?: ResponseStatus;
|
||||||
|
destinationIndex?: string | undefined;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface DeleteTransformsResponseSchema {
|
||||||
|
[key: string]: DeleteTransformStatus;
|
||||||
|
}
|
|
@ -0,0 +1,19 @@
|
||||||
|
/*
|
||||||
|
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||||
|
* or more contributor license agreements. Licensed under the Elastic License;
|
||||||
|
* you may not use this file except in compliance with the Elastic License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
import { schema, TypeOf } from '@kbn/config-schema';
|
||||||
|
|
||||||
|
export const fieldHistogramsRequestSchema = schema.object({
|
||||||
|
/** Query to match documents in the index. */
|
||||||
|
query: schema.any(),
|
||||||
|
/** The fields to return histogram data. */
|
||||||
|
fields: schema.arrayOf(schema.any()),
|
||||||
|
/** Number of documents to be collected in the sample processed on each shard, or -1 for no sampling. */
|
||||||
|
samplerShardSize: schema.number(),
|
||||||
|
});
|
||||||
|
|
||||||
|
export type FieldHistogramsRequestSchema = TypeOf<typeof fieldHistogramsRequestSchema>;
|
||||||
|
export type FieldHistogramsResponseSchema = any[];
|
|
@ -0,0 +1,13 @@
|
||||||
|
/*
|
||||||
|
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||||
|
* or more contributor license agreements. Licensed under the Elastic License;
|
||||||
|
* you may not use this file except in compliance with the Elastic License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
import { TypeOf } from '@kbn/config-schema';
|
||||||
|
|
||||||
|
import { transformIdsSchema, CommonResponseStatusSchema } from './common';
|
||||||
|
|
||||||
|
export const startTransformsRequestSchema = transformIdsSchema;
|
||||||
|
export type StartTransformsRequestSchema = TypeOf<typeof startTransformsRequestSchema>;
|
||||||
|
export type StartTransformsResponseSchema = CommonResponseStatusSchema;
|
|
@ -0,0 +1,19 @@
|
||||||
|
/*
|
||||||
|
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||||
|
* or more contributor license agreements. Licensed under the Elastic License;
|
||||||
|
* you may not use this file except in compliance with the Elastic License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
import { schema, TypeOf } from '@kbn/config-schema';
|
||||||
|
|
||||||
|
import { transformStateSchema, CommonResponseStatusSchema } from './common';
|
||||||
|
|
||||||
|
export const stopTransformsRequestSchema = schema.arrayOf(
|
||||||
|
schema.object({
|
||||||
|
id: schema.string(),
|
||||||
|
state: transformStateSchema,
|
||||||
|
})
|
||||||
|
);
|
||||||
|
|
||||||
|
export type StopTransformsRequestSchema = TypeOf<typeof stopTransformsRequestSchema>;
|
||||||
|
export type StopTransformsResponseSchema = CommonResponseStatusSchema;
|
127
x-pack/plugins/transform/common/api_schemas/transforms.ts
Normal file
127
x-pack/plugins/transform/common/api_schemas/transforms.ts
Normal file
|
@ -0,0 +1,127 @@
|
||||||
|
/*
|
||||||
|
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||||
|
* or more contributor license agreements. Licensed under the Elastic License;
|
||||||
|
* you may not use this file except in compliance with the Elastic License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
import { schema, TypeOf } from '@kbn/config-schema';
|
||||||
|
|
||||||
|
import type { ES_FIELD_TYPES } from '../../../../../src/plugins/data/common';
|
||||||
|
|
||||||
|
import type { Dictionary } from '../types/common';
|
||||||
|
import type { PivotAggDict } from '../types/pivot_aggs';
|
||||||
|
import type { PivotGroupByDict } from '../types/pivot_group_by';
|
||||||
|
import type { TransformId, TransformPivotConfig } from '../types/transform';
|
||||||
|
|
||||||
|
import { transformStateSchema } from './common';
|
||||||
|
|
||||||
|
// GET transforms
|
||||||
|
export const getTransformsRequestSchema = schema.arrayOf(
|
||||||
|
schema.object({
|
||||||
|
id: schema.string(),
|
||||||
|
state: transformStateSchema,
|
||||||
|
})
|
||||||
|
);
|
||||||
|
|
||||||
|
export type GetTransformsRequestSchema = TypeOf<typeof getTransformsRequestSchema>;
|
||||||
|
|
||||||
|
export interface GetTransformsResponseSchema {
|
||||||
|
count: number;
|
||||||
|
transforms: TransformPivotConfig[];
|
||||||
|
}
|
||||||
|
|
||||||
|
// schemas shared by parts of the preview, create and update endpoint
|
||||||
|
export const destSchema = schema.object({
|
||||||
|
index: schema.string(),
|
||||||
|
pipeline: schema.maybe(schema.string()),
|
||||||
|
});
|
||||||
|
export const pivotSchema = schema.object({
|
||||||
|
group_by: schema.any(),
|
||||||
|
aggregations: schema.any(),
|
||||||
|
});
|
||||||
|
export const settingsSchema = schema.object({
|
||||||
|
max_page_search_size: schema.maybe(schema.number()),
|
||||||
|
// The default value is null, which disables throttling.
|
||||||
|
docs_per_second: schema.maybe(schema.nullable(schema.number())),
|
||||||
|
});
|
||||||
|
export const sourceSchema = schema.object({
|
||||||
|
index: schema.oneOf([schema.string(), schema.arrayOf(schema.string())]),
|
||||||
|
query: schema.maybe(schema.recordOf(schema.string(), schema.any())),
|
||||||
|
});
|
||||||
|
export const syncSchema = schema.object({
|
||||||
|
time: schema.object({
|
||||||
|
delay: schema.maybe(schema.string()),
|
||||||
|
field: schema.string(),
|
||||||
|
}),
|
||||||
|
});
|
||||||
|
|
||||||
|
// PUT transforms/{transformId}
|
||||||
|
export const putTransformsRequestSchema = schema.object({
|
||||||
|
description: schema.maybe(schema.string()),
|
||||||
|
dest: destSchema,
|
||||||
|
frequency: schema.maybe(schema.string()),
|
||||||
|
pivot: pivotSchema,
|
||||||
|
settings: schema.maybe(settingsSchema),
|
||||||
|
source: sourceSchema,
|
||||||
|
sync: schema.maybe(syncSchema),
|
||||||
|
});
|
||||||
|
|
||||||
|
export interface PutTransformsRequestSchema extends TypeOf<typeof putTransformsRequestSchema> {
|
||||||
|
pivot: {
|
||||||
|
group_by: PivotGroupByDict;
|
||||||
|
aggregations: PivotAggDict;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
interface TransformCreated {
|
||||||
|
transform: TransformId;
|
||||||
|
}
|
||||||
|
interface TransformCreatedError {
|
||||||
|
id: TransformId;
|
||||||
|
error: any;
|
||||||
|
}
|
||||||
|
export interface PutTransformsResponseSchema {
|
||||||
|
transformsCreated: TransformCreated[];
|
||||||
|
errors: TransformCreatedError[];
|
||||||
|
}
|
||||||
|
|
||||||
|
// POST transforms/_preview
|
||||||
|
export const postTransformsPreviewRequestSchema = schema.object({
|
||||||
|
pivot: pivotSchema,
|
||||||
|
source: sourceSchema,
|
||||||
|
});
|
||||||
|
|
||||||
|
export interface PostTransformsPreviewRequestSchema
|
||||||
|
extends TypeOf<typeof postTransformsPreviewRequestSchema> {
|
||||||
|
pivot: {
|
||||||
|
group_by: PivotGroupByDict;
|
||||||
|
aggregations: PivotAggDict;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
interface EsMappingType {
|
||||||
|
type: ES_FIELD_TYPES;
|
||||||
|
}
|
||||||
|
|
||||||
|
export type PreviewItem = Dictionary<any>;
|
||||||
|
export type PreviewData = PreviewItem[];
|
||||||
|
export type PreviewMappingsProperties = Dictionary<EsMappingType>;
|
||||||
|
|
||||||
|
export interface PostTransformsPreviewResponseSchema {
|
||||||
|
generated_dest_index: {
|
||||||
|
mappings: {
|
||||||
|
_meta: {
|
||||||
|
_transform: {
|
||||||
|
transform: string;
|
||||||
|
version: { create: string };
|
||||||
|
creation_date_in_millis: number;
|
||||||
|
};
|
||||||
|
created_by: string;
|
||||||
|
};
|
||||||
|
properties: PreviewMappingsProperties;
|
||||||
|
};
|
||||||
|
settings: { index: { number_of_shards: string; auto_expand_replicas: string } };
|
||||||
|
aliases: Record<string, any>;
|
||||||
|
};
|
||||||
|
preview: PreviewData;
|
||||||
|
}
|
|
@ -0,0 +1,21 @@
|
||||||
|
/*
|
||||||
|
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||||
|
* or more contributor license agreements. Licensed under the Elastic License;
|
||||||
|
* you may not use this file except in compliance with the Elastic License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
import { TypeOf } from '@kbn/config-schema';
|
||||||
|
|
||||||
|
import { TransformStats } from '../types/transform_stats';
|
||||||
|
|
||||||
|
import { getTransformsRequestSchema } from './transforms';
|
||||||
|
|
||||||
|
export const getTransformsStatsRequestSchema = getTransformsRequestSchema;
|
||||||
|
|
||||||
|
export type GetTransformsRequestSchema = TypeOf<typeof getTransformsStatsRequestSchema>;
|
||||||
|
|
||||||
|
export interface GetTransformsStatsResponseSchema {
|
||||||
|
node_failures?: object;
|
||||||
|
count: number;
|
||||||
|
transforms: TransformStats[];
|
||||||
|
}
|
114
x-pack/plugins/transform/common/api_schemas/type_guards.ts
Normal file
114
x-pack/plugins/transform/common/api_schemas/type_guards.ts
Normal file
|
@ -0,0 +1,114 @@
|
||||||
|
/*
|
||||||
|
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||||
|
* or more contributor license agreements. Licensed under the Elastic License;
|
||||||
|
* you may not use this file except in compliance with the Elastic License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
import type { SearchResponse7 } from '../../../ml/common';
|
||||||
|
|
||||||
|
import type { EsIndex } from '../types/es_index';
|
||||||
|
|
||||||
|
// To be able to use the type guards on the client side, we need to make sure we don't import
|
||||||
|
// the code of '@kbn/config-schema' but just its types, otherwise the client side code will
|
||||||
|
// fail to build.
|
||||||
|
import type { FieldHistogramsResponseSchema } from './field_histograms';
|
||||||
|
import type { GetTransformsAuditMessagesResponseSchema } from './audit_messages';
|
||||||
|
import type { DeleteTransformsResponseSchema } from './delete_transforms';
|
||||||
|
import type { StartTransformsResponseSchema } from './start_transforms';
|
||||||
|
import type { StopTransformsResponseSchema } from './stop_transforms';
|
||||||
|
import type {
|
||||||
|
GetTransformsResponseSchema,
|
||||||
|
PostTransformsPreviewResponseSchema,
|
||||||
|
PutTransformsResponseSchema,
|
||||||
|
} from './transforms';
|
||||||
|
import type { GetTransformsStatsResponseSchema } from './transforms_stats';
|
||||||
|
import type { PostTransformsUpdateResponseSchema } from './update_transforms';
|
||||||
|
|
||||||
|
const isBasicObject = (arg: any) => {
|
||||||
|
return typeof arg === 'object' && arg !== null;
|
||||||
|
};
|
||||||
|
|
||||||
|
const isGenericResponseSchema = <T>(arg: any): arg is T => {
|
||||||
|
return (
|
||||||
|
isBasicObject(arg) &&
|
||||||
|
{}.hasOwnProperty.call(arg, 'count') &&
|
||||||
|
{}.hasOwnProperty.call(arg, 'transforms') &&
|
||||||
|
Array.isArray(arg.transforms)
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export const isGetTransformsResponseSchema = (arg: any): arg is GetTransformsResponseSchema => {
|
||||||
|
return isGenericResponseSchema<GetTransformsResponseSchema>(arg);
|
||||||
|
};
|
||||||
|
|
||||||
|
export const isGetTransformsStatsResponseSchema = (
|
||||||
|
arg: any
|
||||||
|
): arg is GetTransformsStatsResponseSchema => {
|
||||||
|
return isGenericResponseSchema<GetTransformsStatsResponseSchema>(arg);
|
||||||
|
};
|
||||||
|
|
||||||
|
export const isDeleteTransformsResponseSchema = (
|
||||||
|
arg: any
|
||||||
|
): arg is DeleteTransformsResponseSchema => {
|
||||||
|
return (
|
||||||
|
isBasicObject(arg) &&
|
||||||
|
Object.values(arg).every((d) => ({}.hasOwnProperty.call(d, 'transformDeleted')))
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export const isEsIndices = (arg: any): arg is EsIndex[] => {
|
||||||
|
return Array.isArray(arg);
|
||||||
|
};
|
||||||
|
|
||||||
|
export const isEsSearchResponse = (arg: any): arg is SearchResponse7 => {
|
||||||
|
return isBasicObject(arg) && {}.hasOwnProperty.call(arg, 'hits');
|
||||||
|
};
|
||||||
|
|
||||||
|
export const isFieldHistogramsResponseSchema = (arg: any): arg is FieldHistogramsResponseSchema => {
|
||||||
|
return Array.isArray(arg);
|
||||||
|
};
|
||||||
|
|
||||||
|
export const isGetTransformsAuditMessagesResponseSchema = (
|
||||||
|
arg: any
|
||||||
|
): arg is GetTransformsAuditMessagesResponseSchema => {
|
||||||
|
return Array.isArray(arg);
|
||||||
|
};
|
||||||
|
|
||||||
|
export const isPostTransformsPreviewResponseSchema = (
|
||||||
|
arg: any
|
||||||
|
): arg is PostTransformsPreviewResponseSchema => {
|
||||||
|
return (
|
||||||
|
isBasicObject(arg) &&
|
||||||
|
{}.hasOwnProperty.call(arg, 'generated_dest_index') &&
|
||||||
|
{}.hasOwnProperty.call(arg, 'preview') &&
|
||||||
|
typeof arg.generated_dest_index !== undefined &&
|
||||||
|
Array.isArray(arg.preview)
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export const isPostTransformsUpdateResponseSchema = (
|
||||||
|
arg: any
|
||||||
|
): arg is PostTransformsUpdateResponseSchema => {
|
||||||
|
return isBasicObject(arg) && {}.hasOwnProperty.call(arg, 'id') && typeof arg.id === 'string';
|
||||||
|
};
|
||||||
|
|
||||||
|
export const isPutTransformsResponseSchema = (arg: any): arg is PutTransformsResponseSchema => {
|
||||||
|
return (
|
||||||
|
isBasicObject(arg) &&
|
||||||
|
{}.hasOwnProperty.call(arg, 'transformsCreated') &&
|
||||||
|
{}.hasOwnProperty.call(arg, 'errors') &&
|
||||||
|
Array.isArray(arg.transformsCreated) &&
|
||||||
|
Array.isArray(arg.errors)
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
const isGenericSuccessResponseSchema = (arg: any) =>
|
||||||
|
isBasicObject(arg) && Object.values(arg).every((d) => ({}.hasOwnProperty.call(d, 'success')));
|
||||||
|
|
||||||
|
export const isStartTransformsResponseSchema = (arg: any): arg is StartTransformsResponseSchema => {
|
||||||
|
return isGenericSuccessResponseSchema(arg);
|
||||||
|
};
|
||||||
|
|
||||||
|
export const isStopTransformsResponseSchema = (arg: any): arg is StopTransformsResponseSchema => {
|
||||||
|
return isGenericSuccessResponseSchema(arg);
|
||||||
|
};
|
|
@ -0,0 +1,24 @@
|
||||||
|
/*
|
||||||
|
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||||
|
* or more contributor license agreements. Licensed under the Elastic License;
|
||||||
|
* you may not use this file except in compliance with the Elastic License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
import { schema, TypeOf } from '@kbn/config-schema';
|
||||||
|
|
||||||
|
import { TransformPivotConfig } from '../types/transform';
|
||||||
|
|
||||||
|
import { destSchema, settingsSchema, sourceSchema, syncSchema } from './transforms';
|
||||||
|
|
||||||
|
// POST _transform/{transform_id}/_update
|
||||||
|
export const postTransformsUpdateRequestSchema = schema.object({
|
||||||
|
description: schema.maybe(schema.string()),
|
||||||
|
dest: schema.maybe(destSchema),
|
||||||
|
frequency: schema.maybe(schema.string()),
|
||||||
|
settings: schema.maybe(settingsSchema),
|
||||||
|
source: schema.maybe(sourceSchema),
|
||||||
|
sync: schema.maybe(syncSchema),
|
||||||
|
});
|
||||||
|
|
||||||
|
export type PostTransformsUpdateRequestSchema = TypeOf<typeof postTransformsUpdateRequestSchema>;
|
||||||
|
export type PostTransformsUpdateResponseSchema = TransformPivotConfig;
|
|
@ -75,3 +75,24 @@ export const APP_CREATE_TRANSFORM_CLUSTER_PRIVILEGES = [
|
||||||
];
|
];
|
||||||
|
|
||||||
export const APP_INDEX_PRIVILEGES = ['monitor'];
|
export const APP_INDEX_PRIVILEGES = ['monitor'];
|
||||||
|
|
||||||
|
// reflects https://github.com/elastic/elasticsearch/blob/master/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/dataframe/transforms/DataFrameTransformStats.java#L243
|
||||||
|
export const TRANSFORM_STATE = {
|
||||||
|
ABORTING: 'aborting',
|
||||||
|
FAILED: 'failed',
|
||||||
|
INDEXING: 'indexing',
|
||||||
|
STARTED: 'started',
|
||||||
|
STOPPED: 'stopped',
|
||||||
|
STOPPING: 'stopping',
|
||||||
|
} as const;
|
||||||
|
|
||||||
|
const transformStates = Object.values(TRANSFORM_STATE);
|
||||||
|
export type TransformState = typeof transformStates[number];
|
||||||
|
|
||||||
|
export const TRANSFORM_MODE = {
|
||||||
|
BATCH: 'batch',
|
||||||
|
CONTINUOUS: 'continuous',
|
||||||
|
} as const;
|
||||||
|
|
||||||
|
const transformModes = Object.values(TRANSFORM_MODE);
|
||||||
|
export type TransformMode = typeof transformModes[number];
|
||||||
|
|
|
@ -1,58 +0,0 @@
|
||||||
/*
|
|
||||||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
|
||||||
* or more contributor license agreements. Licensed under the Elastic License;
|
|
||||||
* you may not use this file except in compliance with the Elastic License.
|
|
||||||
*/
|
|
||||||
|
|
||||||
export interface MissingPrivileges {
|
|
||||||
[key: string]: string[] | undefined;
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface Privileges {
|
|
||||||
hasAllPrivileges: boolean;
|
|
||||||
missingPrivileges: MissingPrivileges;
|
|
||||||
}
|
|
||||||
|
|
||||||
export type TransformId = string;
|
|
||||||
|
|
||||||
// reflects https://github.com/elastic/elasticsearch/blob/master/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/dataframe/transforms/DataFrameTransformStats.java#L243
|
|
||||||
export enum TRANSFORM_STATE {
|
|
||||||
ABORTING = 'aborting',
|
|
||||||
FAILED = 'failed',
|
|
||||||
INDEXING = 'indexing',
|
|
||||||
STARTED = 'started',
|
|
||||||
STOPPED = 'stopped',
|
|
||||||
STOPPING = 'stopping',
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface TransformEndpointRequest {
|
|
||||||
id: TransformId;
|
|
||||||
state?: TRANSFORM_STATE;
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface ResultData {
|
|
||||||
success: boolean;
|
|
||||||
error?: any;
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface TransformEndpointResult {
|
|
||||||
[key: string]: ResultData;
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface DeleteTransformEndpointRequest {
|
|
||||||
transformsInfo: TransformEndpointRequest[];
|
|
||||||
deleteDestIndex?: boolean;
|
|
||||||
deleteDestIndexPattern?: boolean;
|
|
||||||
forceDelete?: boolean;
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface DeleteTransformStatus {
|
|
||||||
transformDeleted: ResultData;
|
|
||||||
destIndexDeleted?: ResultData;
|
|
||||||
destIndexPatternDeleted?: ResultData;
|
|
||||||
destinationIndex?: string | undefined;
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface DeleteTransformEndpointResult {
|
|
||||||
[key: string]: DeleteTransformStatus;
|
|
||||||
}
|
|
7
x-pack/plugins/transform/common/shared_imports.ts
Normal file
7
x-pack/plugins/transform/common/shared_imports.ts
Normal file
|
@ -0,0 +1,7 @@
|
||||||
|
/*
|
||||||
|
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||||
|
* or more contributor license agreements. Licensed under the Elastic License;
|
||||||
|
* you may not use this file except in compliance with the Elastic License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
export type { SearchResponse7 } from '../../ml/common';
|
7
x-pack/plugins/transform/common/types/aggregations.ts
Normal file
7
x-pack/plugins/transform/common/types/aggregations.ts
Normal file
|
@ -0,0 +1,7 @@
|
||||||
|
/*
|
||||||
|
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||||
|
* or more contributor license agreements. Licensed under the Elastic License;
|
||||||
|
* you may not use this file except in compliance with the Elastic License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
export type AggName = string;
|
7
x-pack/plugins/transform/common/types/fields.ts
Normal file
7
x-pack/plugins/transform/common/types/fields.ts
Normal file
|
@ -0,0 +1,7 @@
|
||||||
|
/*
|
||||||
|
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||||
|
* or more contributor license agreements. Licensed under the Elastic License;
|
||||||
|
* you may not use this file except in compliance with the Elastic License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
export type EsFieldName = string;
|
31
x-pack/plugins/transform/common/types/pivot_aggs.ts
Normal file
31
x-pack/plugins/transform/common/types/pivot_aggs.ts
Normal file
|
@ -0,0 +1,31 @@
|
||||||
|
/*
|
||||||
|
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||||
|
* or more contributor license agreements. Licensed under the Elastic License;
|
||||||
|
* you may not use this file except in compliance with the Elastic License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
import { AggName } from './aggregations';
|
||||||
|
import { EsFieldName } from './fields';
|
||||||
|
|
||||||
|
export const PIVOT_SUPPORTED_AGGS = {
|
||||||
|
AVG: 'avg',
|
||||||
|
CARDINALITY: 'cardinality',
|
||||||
|
MAX: 'max',
|
||||||
|
MIN: 'min',
|
||||||
|
PERCENTILES: 'percentiles',
|
||||||
|
SUM: 'sum',
|
||||||
|
VALUE_COUNT: 'value_count',
|
||||||
|
FILTER: 'filter',
|
||||||
|
} as const;
|
||||||
|
|
||||||
|
export type PivotSupportedAggs = typeof PIVOT_SUPPORTED_AGGS[keyof typeof PIVOT_SUPPORTED_AGGS];
|
||||||
|
|
||||||
|
export type PivotAgg = {
|
||||||
|
[key in PivotSupportedAggs]?: {
|
||||||
|
field: EsFieldName;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
export type PivotAggDict = {
|
||||||
|
[key in AggName]: PivotAgg;
|
||||||
|
};
|
33
x-pack/plugins/transform/common/types/pivot_group_by.ts
Normal file
33
x-pack/plugins/transform/common/types/pivot_group_by.ts
Normal file
|
@ -0,0 +1,33 @@
|
||||||
|
/*
|
||||||
|
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||||
|
* or more contributor license agreements. Licensed under the Elastic License;
|
||||||
|
* you may not use this file except in compliance with the Elastic License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
import { Dictionary } from './common';
|
||||||
|
import { EsFieldName } from './fields';
|
||||||
|
|
||||||
|
export type GenericAgg = object;
|
||||||
|
|
||||||
|
export interface TermsAgg {
|
||||||
|
terms: {
|
||||||
|
field: EsFieldName;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface HistogramAgg {
|
||||||
|
histogram: {
|
||||||
|
field: EsFieldName;
|
||||||
|
interval: string;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface DateHistogramAgg {
|
||||||
|
date_histogram: {
|
||||||
|
field: EsFieldName;
|
||||||
|
calendar_interval: string;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
export type PivotGroupBy = GenericAgg | TermsAgg | HistogramAgg | DateHistogramAgg;
|
||||||
|
export type PivotGroupByDict = Dictionary<PivotGroupBy>;
|
14
x-pack/plugins/transform/common/types/privileges.ts
Normal file
14
x-pack/plugins/transform/common/types/privileges.ts
Normal file
|
@ -0,0 +1,14 @@
|
||||||
|
/*
|
||||||
|
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||||
|
* or more contributor license agreements. Licensed under the Elastic License;
|
||||||
|
* you may not use this file except in compliance with the Elastic License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
export interface MissingPrivileges {
|
||||||
|
[key: string]: string[] | undefined;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface Privileges {
|
||||||
|
hasAllPrivileges: boolean;
|
||||||
|
missingPrivileges: MissingPrivileges;
|
||||||
|
}
|
17
x-pack/plugins/transform/common/types/transform.ts
Normal file
17
x-pack/plugins/transform/common/types/transform.ts
Normal file
|
@ -0,0 +1,17 @@
|
||||||
|
/*
|
||||||
|
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||||
|
* or more contributor license agreements. Licensed under the Elastic License;
|
||||||
|
* you may not use this file except in compliance with the Elastic License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
import type { PutTransformsRequestSchema } from '../api_schemas/transforms';
|
||||||
|
|
||||||
|
export type IndexName = string;
|
||||||
|
export type IndexPattern = string;
|
||||||
|
export type TransformId = string;
|
||||||
|
|
||||||
|
export interface TransformPivotConfig extends PutTransformsRequestSchema {
|
||||||
|
id: TransformId;
|
||||||
|
create_time?: number;
|
||||||
|
version?: string;
|
||||||
|
}
|
62
x-pack/plugins/transform/common/types/transform_stats.ts
Normal file
62
x-pack/plugins/transform/common/types/transform_stats.ts
Normal file
|
@ -0,0 +1,62 @@
|
||||||
|
/*
|
||||||
|
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||||
|
* or more contributor license agreements. Licensed under the Elastic License;
|
||||||
|
* you may not use this file except in compliance with the Elastic License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
import { TransformState, TRANSFORM_STATE } from '../constants';
|
||||||
|
import { TransformId } from './transform';
|
||||||
|
|
||||||
|
export interface TransformStats {
|
||||||
|
id: TransformId;
|
||||||
|
checkpointing: {
|
||||||
|
last: {
|
||||||
|
checkpoint: number;
|
||||||
|
timestamp_millis?: number;
|
||||||
|
};
|
||||||
|
next?: {
|
||||||
|
checkpoint: number;
|
||||||
|
checkpoint_progress?: {
|
||||||
|
total_docs: number;
|
||||||
|
docs_remaining: number;
|
||||||
|
percent_complete: number;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
operations_behind: number;
|
||||||
|
};
|
||||||
|
node?: {
|
||||||
|
id: string;
|
||||||
|
name: string;
|
||||||
|
ephemeral_id: string;
|
||||||
|
transport_address: string;
|
||||||
|
attributes: Record<string, any>;
|
||||||
|
};
|
||||||
|
stats: {
|
||||||
|
documents_indexed: number;
|
||||||
|
documents_processed: number;
|
||||||
|
index_failures: number;
|
||||||
|
index_time_in_ms: number;
|
||||||
|
index_total: number;
|
||||||
|
pages_processed: number;
|
||||||
|
search_failures: number;
|
||||||
|
search_time_in_ms: number;
|
||||||
|
search_total: number;
|
||||||
|
trigger_count: number;
|
||||||
|
processing_time_in_ms: number;
|
||||||
|
processing_total: number;
|
||||||
|
exponential_avg_checkpoint_duration_ms: number;
|
||||||
|
exponential_avg_documents_indexed: number;
|
||||||
|
exponential_avg_documents_processed: number;
|
||||||
|
};
|
||||||
|
reason?: string;
|
||||||
|
state: TransformState;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function isTransformStats(arg: any): arg is TransformStats {
|
||||||
|
return (
|
||||||
|
typeof arg === 'object' &&
|
||||||
|
arg !== null &&
|
||||||
|
{}.hasOwnProperty.call(arg, 'state') &&
|
||||||
|
Object.values(TRANSFORM_STATE).includes(arg.state)
|
||||||
|
);
|
||||||
|
}
|
|
@ -23,7 +23,6 @@ export {
|
||||||
DataGrid,
|
DataGrid,
|
||||||
EsSorting,
|
EsSorting,
|
||||||
RenderCellValue,
|
RenderCellValue,
|
||||||
SearchResponse7,
|
|
||||||
UseDataGridReturnType,
|
UseDataGridReturnType,
|
||||||
UseIndexDataReturnType,
|
UseIndexDataReturnType,
|
||||||
INDEX_STATUS,
|
INDEX_STATUS,
|
||||||
|
|
|
@ -6,7 +6,7 @@
|
||||||
|
|
||||||
import { composeValidators, patternValidator } from '../../../../ml/public';
|
import { composeValidators, patternValidator } from '../../../../ml/public';
|
||||||
|
|
||||||
export type AggName = string;
|
import { AggName } from '../../../common/types/aggregations';
|
||||||
|
|
||||||
export function isAggName(arg: any): arg is AggName {
|
export function isAggName(arg: any): arg is AggName {
|
||||||
// allow all characters except `[]>` and must not start or end with a space.
|
// allow all characters except `[]>` and must not start or end with a space.
|
||||||
|
|
|
@ -4,11 +4,12 @@
|
||||||
* you may not use this file except in compliance with the Elastic License.
|
* you may not use this file except in compliance with the Elastic License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
import { PIVOT_SUPPORTED_AGGS } from '../../../common/types/pivot_aggs';
|
||||||
|
|
||||||
import {
|
import {
|
||||||
getPreviewRequestBody,
|
getPreviewTransformRequestBody,
|
||||||
PivotAggsConfig,
|
PivotAggsConfig,
|
||||||
PivotGroupByConfig,
|
PivotGroupByConfig,
|
||||||
PIVOT_SUPPORTED_AGGS,
|
|
||||||
PIVOT_SUPPORTED_GROUP_BY_AGGS,
|
PIVOT_SUPPORTED_GROUP_BY_AGGS,
|
||||||
SimpleQuery,
|
SimpleQuery,
|
||||||
} from '../common';
|
} from '../common';
|
||||||
|
@ -35,7 +36,12 @@ describe('Transform: Data Grid', () => {
|
||||||
aggName: 'the-agg-agg-name',
|
aggName: 'the-agg-agg-name',
|
||||||
dropDownName: 'the-agg-drop-down-name',
|
dropDownName: 'the-agg-drop-down-name',
|
||||||
};
|
};
|
||||||
const request = getPreviewRequestBody('the-index-pattern-title', query, [groupBy], [agg]);
|
const request = getPreviewTransformRequestBody(
|
||||||
|
'the-index-pattern-title',
|
||||||
|
query,
|
||||||
|
[groupBy],
|
||||||
|
[agg]
|
||||||
|
);
|
||||||
const pivotPreviewDevConsoleStatement = getPivotPreviewDevConsoleStatement(request);
|
const pivotPreviewDevConsoleStatement = getPivotPreviewDevConsoleStatement(request);
|
||||||
|
|
||||||
expect(pivotPreviewDevConsoleStatement).toBe(`POST _transform/_preview
|
expect(pivotPreviewDevConsoleStatement).toBe(`POST _transform/_preview
|
||||||
|
|
|
@ -4,12 +4,13 @@
|
||||||
* you may not use this file except in compliance with the Elastic License.
|
* you may not use this file except in compliance with the Elastic License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
import type { PostTransformsPreviewRequestSchema } from '../../../common/api_schemas/transforms';
|
||||||
|
|
||||||
import { PivotQuery } from './request';
|
import { PivotQuery } from './request';
|
||||||
import { PreviewRequestBody } from './transform';
|
|
||||||
|
|
||||||
export const INIT_MAX_COLUMNS = 20;
|
export const INIT_MAX_COLUMNS = 20;
|
||||||
|
|
||||||
export const getPivotPreviewDevConsoleStatement = (request: PreviewRequestBody) => {
|
export const getPivotPreviewDevConsoleStatement = (request: PostTransformsPreviewRequestSchema) => {
|
||||||
return `POST _transform/_preview\n${JSON.stringify(request, null, 2)}\n`;
|
return `POST _transform/_preview\n${JSON.stringify(request, null, 2)}\n`;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -5,10 +5,10 @@
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import { Dictionary } from '../../../common/types/common';
|
import { Dictionary } from '../../../common/types/common';
|
||||||
|
import { EsFieldName } from '../../../common/types/fields';
|
||||||
|
|
||||||
export type EsId = string;
|
export type EsId = string;
|
||||||
export type EsDocSource = Dictionary<any>;
|
export type EsDocSource = Dictionary<any>;
|
||||||
export type EsFieldName = string;
|
|
||||||
|
|
||||||
export interface EsDoc extends Dictionary<any> {
|
export interface EsDoc extends Dictionary<any> {
|
||||||
_id: EsId;
|
_id: EsId;
|
||||||
|
|
|
@ -4,7 +4,7 @@
|
||||||
* you may not use this file except in compliance with the Elastic License.
|
* you may not use this file except in compliance with the Elastic License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
export { AggName, isAggName } from './aggregations';
|
export { isAggName } from './aggregations';
|
||||||
export {
|
export {
|
||||||
getIndexDevConsoleStatement,
|
getIndexDevConsoleStatement,
|
||||||
getPivotPreviewDevConsoleStatement,
|
getPivotPreviewDevConsoleStatement,
|
||||||
|
@ -17,44 +17,28 @@ export {
|
||||||
toggleSelectedField,
|
toggleSelectedField,
|
||||||
EsDoc,
|
EsDoc,
|
||||||
EsDocSource,
|
EsDocSource,
|
||||||
EsFieldName,
|
|
||||||
} from './fields';
|
} from './fields';
|
||||||
export { DropDownLabel, DropDownOption, Label } from './dropdown';
|
export { DropDownLabel, DropDownOption, Label } from './dropdown';
|
||||||
export {
|
export {
|
||||||
isTransformIdValid,
|
isTransformIdValid,
|
||||||
refreshTransformList$,
|
refreshTransformList$,
|
||||||
useRefreshTransformList,
|
useRefreshTransformList,
|
||||||
CreateRequestBody,
|
|
||||||
PreviewRequestBody,
|
|
||||||
TransformPivotConfig,
|
|
||||||
IndexName,
|
|
||||||
IndexPattern,
|
|
||||||
REFRESH_TRANSFORM_LIST_STATE,
|
REFRESH_TRANSFORM_LIST_STATE,
|
||||||
} from './transform';
|
} from './transform';
|
||||||
export { TRANSFORM_LIST_COLUMN, TransformListAction, TransformListRow } from './transform_list';
|
export { TRANSFORM_LIST_COLUMN, TransformListAction, TransformListRow } from './transform_list';
|
||||||
export {
|
export { getTransformProgress, isCompletedBatchTransform } from './transform_stats';
|
||||||
getTransformProgress,
|
|
||||||
isCompletedBatchTransform,
|
|
||||||
isTransformStats,
|
|
||||||
TransformStats,
|
|
||||||
TRANSFORM_MODE,
|
|
||||||
} from './transform_stats';
|
|
||||||
export { getDiscoverUrl } from './navigation';
|
export { getDiscoverUrl } from './navigation';
|
||||||
export { GetTransformsResponse, PreviewData, PreviewMappings } from './pivot_preview';
|
|
||||||
export {
|
export {
|
||||||
getEsAggFromAggConfig,
|
getEsAggFromAggConfig,
|
||||||
isPivotAggsConfigWithUiSupport,
|
isPivotAggsConfigWithUiSupport,
|
||||||
isPivotAggsConfigPercentiles,
|
isPivotAggsConfigPercentiles,
|
||||||
PERCENTILES_AGG_DEFAULT_PERCENTS,
|
PERCENTILES_AGG_DEFAULT_PERCENTS,
|
||||||
PivotAgg,
|
|
||||||
PivotAggDict,
|
|
||||||
PivotAggsConfig,
|
PivotAggsConfig,
|
||||||
PivotAggsConfigDict,
|
PivotAggsConfigDict,
|
||||||
PivotAggsConfigBase,
|
PivotAggsConfigBase,
|
||||||
PivotAggsConfigWithUiSupport,
|
PivotAggsConfigWithUiSupport,
|
||||||
PivotAggsConfigWithUiSupportDict,
|
PivotAggsConfigWithUiSupportDict,
|
||||||
pivotAggsFieldSupport,
|
pivotAggsFieldSupport,
|
||||||
PIVOT_SUPPORTED_AGGS,
|
|
||||||
} from './pivot_aggs';
|
} from './pivot_aggs';
|
||||||
export {
|
export {
|
||||||
dateHistogramIntervalFormatRegex,
|
dateHistogramIntervalFormatRegex,
|
||||||
|
@ -65,25 +49,19 @@ export {
|
||||||
isGroupByHistogram,
|
isGroupByHistogram,
|
||||||
isGroupByTerms,
|
isGroupByTerms,
|
||||||
pivotGroupByFieldSupport,
|
pivotGroupByFieldSupport,
|
||||||
DateHistogramAgg,
|
|
||||||
GenericAgg,
|
|
||||||
GroupByConfigWithInterval,
|
GroupByConfigWithInterval,
|
||||||
GroupByConfigWithUiSupport,
|
GroupByConfigWithUiSupport,
|
||||||
HistogramAgg,
|
|
||||||
PivotGroupBy,
|
|
||||||
PivotGroupByConfig,
|
PivotGroupByConfig,
|
||||||
PivotGroupByDict,
|
|
||||||
PivotGroupByConfigDict,
|
PivotGroupByConfigDict,
|
||||||
PivotGroupByConfigWithUiSupportDict,
|
PivotGroupByConfigWithUiSupportDict,
|
||||||
PivotSupportedGroupByAggs,
|
PivotSupportedGroupByAggs,
|
||||||
PivotSupportedGroupByAggsWithInterval,
|
PivotSupportedGroupByAggsWithInterval,
|
||||||
PIVOT_SUPPORTED_GROUP_BY_AGGS,
|
PIVOT_SUPPORTED_GROUP_BY_AGGS,
|
||||||
TermsAgg,
|
|
||||||
} from './pivot_group_by';
|
} from './pivot_group_by';
|
||||||
export {
|
export {
|
||||||
defaultQuery,
|
defaultQuery,
|
||||||
getPreviewRequestBody,
|
getPreviewTransformRequestBody,
|
||||||
getCreateRequestBody,
|
getCreateTransformRequestBody,
|
||||||
getPivotQuery,
|
getPivotQuery,
|
||||||
isDefaultQuery,
|
isDefaultQuery,
|
||||||
isMatchAllQuery,
|
isMatchAllQuery,
|
||||||
|
|
|
@ -5,31 +5,22 @@
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import { FC } from 'react';
|
import { FC } from 'react';
|
||||||
import { Dictionary } from '../../../common/types/common';
|
|
||||||
import { KBN_FIELD_TYPES } from '../../../../../../src/plugins/data/common';
|
import { KBN_FIELD_TYPES } from '../../../../../../src/plugins/data/common';
|
||||||
|
|
||||||
import { AggName } from './aggregations';
|
import type { AggName } from '../../../common/types/aggregations';
|
||||||
import { EsFieldName } from './fields';
|
import type { Dictionary } from '../../../common/types/common';
|
||||||
|
import type { EsFieldName } from '../../../common/types/fields';
|
||||||
|
import type { PivotAgg, PivotSupportedAggs } from '../../../common/types/pivot_aggs';
|
||||||
|
import { PIVOT_SUPPORTED_AGGS } from '../../../common/types/pivot_aggs';
|
||||||
|
|
||||||
import { getAggFormConfig } from '../sections/create_transform/components/step_define/common/get_agg_form_config';
|
import { getAggFormConfig } from '../sections/create_transform/components/step_define/common/get_agg_form_config';
|
||||||
import { PivotAggsConfigFilter } from '../sections/create_transform/components/step_define/common/filter_agg/types';
|
import { PivotAggsConfigFilter } from '../sections/create_transform/components/step_define/common/filter_agg/types';
|
||||||
|
|
||||||
export type PivotSupportedAggs = typeof PIVOT_SUPPORTED_AGGS[keyof typeof PIVOT_SUPPORTED_AGGS];
|
|
||||||
|
|
||||||
export function isPivotSupportedAggs(arg: any): arg is PivotSupportedAggs {
|
export function isPivotSupportedAggs(arg: any): arg is PivotSupportedAggs {
|
||||||
return Object.values(PIVOT_SUPPORTED_AGGS).includes(arg);
|
return Object.values(PIVOT_SUPPORTED_AGGS).includes(arg);
|
||||||
}
|
}
|
||||||
|
|
||||||
export const PIVOT_SUPPORTED_AGGS = {
|
|
||||||
AVG: 'avg',
|
|
||||||
CARDINALITY: 'cardinality',
|
|
||||||
MAX: 'max',
|
|
||||||
MIN: 'min',
|
|
||||||
PERCENTILES: 'percentiles',
|
|
||||||
SUM: 'sum',
|
|
||||||
VALUE_COUNT: 'value_count',
|
|
||||||
FILTER: 'filter',
|
|
||||||
} as const;
|
|
||||||
|
|
||||||
export const PERCENTILES_AGG_DEFAULT_PERCENTS = [1, 5, 25, 50, 75, 95, 99];
|
export const PERCENTILES_AGG_DEFAULT_PERCENTS = [1, 5, 25, 50, 75, 95, 99];
|
||||||
|
|
||||||
export const pivotAggsFieldSupport = {
|
export const pivotAggsFieldSupport = {
|
||||||
|
@ -69,16 +60,6 @@ export const pivotAggsFieldSupport = {
|
||||||
[KBN_FIELD_TYPES.CONFLICT]: [PIVOT_SUPPORTED_AGGS.VALUE_COUNT, PIVOT_SUPPORTED_AGGS.FILTER],
|
[KBN_FIELD_TYPES.CONFLICT]: [PIVOT_SUPPORTED_AGGS.VALUE_COUNT, PIVOT_SUPPORTED_AGGS.FILTER],
|
||||||
};
|
};
|
||||||
|
|
||||||
export type PivotAgg = {
|
|
||||||
[key in PivotSupportedAggs]?: {
|
|
||||||
field: EsFieldName;
|
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
||||||
export type PivotAggDict = {
|
|
||||||
[key in AggName]: PivotAgg;
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The maximum level of sub-aggregations
|
* The maximum level of sub-aggregations
|
||||||
*/
|
*/
|
||||||
|
|
|
@ -4,12 +4,12 @@
|
||||||
* you may not use this file except in compliance with the Elastic License.
|
* you may not use this file except in compliance with the Elastic License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
import { AggName } from '../../../common/types/aggregations';
|
||||||
import { Dictionary } from '../../../common/types/common';
|
import { Dictionary } from '../../../common/types/common';
|
||||||
|
import { EsFieldName } from '../../../common/types/fields';
|
||||||
|
import { GenericAgg } from '../../../common/types/pivot_group_by';
|
||||||
import { KBN_FIELD_TYPES } from '../../../../../../src/plugins/data/common';
|
import { KBN_FIELD_TYPES } from '../../../../../../src/plugins/data/common';
|
||||||
|
|
||||||
import { AggName } from './aggregations';
|
|
||||||
import { EsFieldName } from './fields';
|
|
||||||
|
|
||||||
export enum PIVOT_SUPPORTED_GROUP_BY_AGGS {
|
export enum PIVOT_SUPPORTED_GROUP_BY_AGGS {
|
||||||
DATE_HISTOGRAM = 'date_histogram',
|
DATE_HISTOGRAM = 'date_histogram',
|
||||||
HISTOGRAM = 'histogram',
|
HISTOGRAM = 'histogram',
|
||||||
|
@ -106,31 +106,6 @@ export function isPivotGroupByConfigWithUiSupport(arg: any): arg is GroupByConfi
|
||||||
return isGroupByDateHistogram(arg) || isGroupByHistogram(arg) || isGroupByTerms(arg);
|
return isGroupByDateHistogram(arg) || isGroupByHistogram(arg) || isGroupByTerms(arg);
|
||||||
}
|
}
|
||||||
|
|
||||||
export type GenericAgg = object;
|
|
||||||
|
|
||||||
export interface TermsAgg {
|
|
||||||
terms: {
|
|
||||||
field: EsFieldName;
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface HistogramAgg {
|
|
||||||
histogram: {
|
|
||||||
field: EsFieldName;
|
|
||||||
interval: string;
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface DateHistogramAgg {
|
|
||||||
date_histogram: {
|
|
||||||
field: EsFieldName;
|
|
||||||
calendar_interval: string;
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
export type PivotGroupBy = GenericAgg | TermsAgg | HistogramAgg | DateHistogramAgg;
|
|
||||||
export type PivotGroupByDict = Dictionary<PivotGroupBy>;
|
|
||||||
|
|
||||||
export function getEsAggFromGroupByConfig(groupByConfig: GroupByConfigBase): GenericAgg {
|
export function getEsAggFromGroupByConfig(groupByConfig: GroupByConfigBase): GenericAgg {
|
||||||
const { agg, aggName, dropDownName, ...esAgg } = groupByConfig;
|
const { agg, aggName, dropDownName, ...esAgg } = groupByConfig;
|
||||||
|
|
||||||
|
|
|
@ -1,29 +0,0 @@
|
||||||
/*
|
|
||||||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
|
||||||
* or more contributor license agreements. Licensed under the Elastic License;
|
|
||||||
* you may not use this file except in compliance with the Elastic License.
|
|
||||||
*/
|
|
||||||
|
|
||||||
import { ES_FIELD_TYPES } from '../../../../../../src/plugins/data/public';
|
|
||||||
|
|
||||||
import { Dictionary } from '../../../common/types/common';
|
|
||||||
|
|
||||||
interface EsMappingType {
|
|
||||||
type: ES_FIELD_TYPES;
|
|
||||||
}
|
|
||||||
|
|
||||||
export type PreviewItem = Dictionary<any>;
|
|
||||||
export type PreviewData = PreviewItem[];
|
|
||||||
export interface PreviewMappings {
|
|
||||||
properties: Dictionary<EsMappingType>;
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface GetTransformsResponse {
|
|
||||||
preview: PreviewData;
|
|
||||||
generated_dest_index: {
|
|
||||||
mappings: PreviewMappings;
|
|
||||||
// Not in use yet
|
|
||||||
aliases: any;
|
|
||||||
settings: any;
|
|
||||||
};
|
|
||||||
}
|
|
|
@ -4,17 +4,19 @@
|
||||||
* you may not use this file except in compliance with the Elastic License.
|
* you may not use this file except in compliance with the Elastic License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
import { PIVOT_SUPPORTED_AGGS } from '../../../common/types/pivot_aggs';
|
||||||
|
|
||||||
import { PivotGroupByConfig } from '../common';
|
import { PivotGroupByConfig } from '../common';
|
||||||
|
|
||||||
import { StepDefineExposedState } from '../sections/create_transform/components/step_define';
|
import { StepDefineExposedState } from '../sections/create_transform/components/step_define';
|
||||||
import { StepDetailsExposedState } from '../sections/create_transform/components/step_details/step_details_form';
|
import { StepDetailsExposedState } from '../sections/create_transform/components/step_details/step_details_form';
|
||||||
|
|
||||||
import { PIVOT_SUPPORTED_GROUP_BY_AGGS } from './pivot_group_by';
|
import { PIVOT_SUPPORTED_GROUP_BY_AGGS } from './pivot_group_by';
|
||||||
import { PivotAggsConfig, PIVOT_SUPPORTED_AGGS } from './pivot_aggs';
|
import { PivotAggsConfig } from './pivot_aggs';
|
||||||
import {
|
import {
|
||||||
defaultQuery,
|
defaultQuery,
|
||||||
getPreviewRequestBody,
|
getPreviewTransformRequestBody,
|
||||||
getCreateRequestBody,
|
getCreateTransformRequestBody,
|
||||||
getPivotQuery,
|
getPivotQuery,
|
||||||
isDefaultQuery,
|
isDefaultQuery,
|
||||||
isMatchAllQuery,
|
isMatchAllQuery,
|
||||||
|
@ -55,7 +57,7 @@ describe('Transform: Common', () => {
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
test('getPreviewRequestBody()', () => {
|
test('getPreviewTransformRequestBody()', () => {
|
||||||
const query = getPivotQuery('the-query');
|
const query = getPivotQuery('the-query');
|
||||||
const groupBy: PivotGroupByConfig[] = [
|
const groupBy: PivotGroupByConfig[] = [
|
||||||
{
|
{
|
||||||
|
@ -73,7 +75,7 @@ describe('Transform: Common', () => {
|
||||||
dropDownName: 'the-agg-drop-down-name',
|
dropDownName: 'the-agg-drop-down-name',
|
||||||
},
|
},
|
||||||
];
|
];
|
||||||
const request = getPreviewRequestBody('the-index-pattern-title', query, groupBy, aggs);
|
const request = getPreviewTransformRequestBody('the-index-pattern-title', query, groupBy, aggs);
|
||||||
|
|
||||||
expect(request).toEqual({
|
expect(request).toEqual({
|
||||||
pivot: {
|
pivot: {
|
||||||
|
@ -87,7 +89,7 @@ describe('Transform: Common', () => {
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
test('getPreviewRequestBody() with comma-separated index pattern', () => {
|
test('getPreviewTransformRequestBody() with comma-separated index pattern', () => {
|
||||||
const query = getPivotQuery('the-query');
|
const query = getPivotQuery('the-query');
|
||||||
const groupBy: PivotGroupByConfig[] = [
|
const groupBy: PivotGroupByConfig[] = [
|
||||||
{
|
{
|
||||||
|
@ -105,7 +107,7 @@ describe('Transform: Common', () => {
|
||||||
dropDownName: 'the-agg-drop-down-name',
|
dropDownName: 'the-agg-drop-down-name',
|
||||||
},
|
},
|
||||||
];
|
];
|
||||||
const request = getPreviewRequestBody(
|
const request = getPreviewTransformRequestBody(
|
||||||
'the-index-pattern-title,the-other-title',
|
'the-index-pattern-title,the-other-title',
|
||||||
query,
|
query,
|
||||||
groupBy,
|
groupBy,
|
||||||
|
@ -124,7 +126,7 @@ describe('Transform: Common', () => {
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
test('getCreateRequestBody()', () => {
|
test('getCreateTransformRequestBody()', () => {
|
||||||
const groupBy: PivotGroupByConfig = {
|
const groupBy: PivotGroupByConfig = {
|
||||||
agg: PIVOT_SUPPORTED_GROUP_BY_AGGS.TERMS,
|
agg: PIVOT_SUPPORTED_GROUP_BY_AGGS.TERMS,
|
||||||
field: 'the-group-by-field',
|
field: 'the-group-by-field',
|
||||||
|
@ -160,7 +162,7 @@ describe('Transform: Common', () => {
|
||||||
valid: true,
|
valid: true,
|
||||||
};
|
};
|
||||||
|
|
||||||
const request = getCreateRequestBody(
|
const request = getCreateTransformRequestBody(
|
||||||
'the-index-pattern-title',
|
'the-index-pattern-title',
|
||||||
pivotState,
|
pivotState,
|
||||||
transformDetailsState
|
transformDetailsState
|
||||||
|
|
|
@ -4,15 +4,25 @@
|
||||||
* you may not use this file except in compliance with the Elastic License.
|
* you may not use this file except in compliance with the Elastic License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import { DefaultOperator } from 'elasticsearch';
|
import type { DefaultOperator } from 'elasticsearch';
|
||||||
|
|
||||||
|
import { HttpFetchError } from '../../../../../../src/core/public';
|
||||||
|
import type { IndexPattern } from '../../../../../../src/plugins/data/public';
|
||||||
|
|
||||||
|
import type {
|
||||||
|
PostTransformsPreviewRequestSchema,
|
||||||
|
PutTransformsRequestSchema,
|
||||||
|
} from '../../../common/api_schemas/transforms';
|
||||||
|
import type {
|
||||||
|
DateHistogramAgg,
|
||||||
|
HistogramAgg,
|
||||||
|
TermsAgg,
|
||||||
|
} from '../../../common/types/pivot_group_by';
|
||||||
import { dictionaryToArray } from '../../../common/types/common';
|
import { dictionaryToArray } from '../../../common/types/common';
|
||||||
import { SavedSearchQuery } from '../hooks/use_search_items';
|
|
||||||
|
|
||||||
import { StepDefineExposedState } from '../sections/create_transform/components/step_define';
|
import type { SavedSearchQuery } from '../hooks/use_search_items';
|
||||||
import { StepDetailsExposedState } from '../sections/create_transform/components/step_details/step_details_form';
|
import type { StepDefineExposedState } from '../sections/create_transform/components/step_define';
|
||||||
|
import type { StepDetailsExposedState } from '../sections/create_transform/components/step_details/step_details_form';
|
||||||
import { IndexPattern } from '../../../../../../src/plugins/data/public';
|
|
||||||
|
|
||||||
import {
|
import {
|
||||||
getEsAggFromAggConfig,
|
getEsAggFromAggConfig,
|
||||||
|
@ -24,8 +34,6 @@ import {
|
||||||
} from '../common';
|
} from '../common';
|
||||||
|
|
||||||
import { PivotAggsConfig } from './pivot_aggs';
|
import { PivotAggsConfig } from './pivot_aggs';
|
||||||
import { DateHistogramAgg, HistogramAgg, TermsAgg } from './pivot_group_by';
|
|
||||||
import { PreviewRequestBody, CreateRequestBody } from './transform';
|
|
||||||
|
|
||||||
export interface SimpleQuery {
|
export interface SimpleQuery {
|
||||||
query_string: {
|
query_string: {
|
||||||
|
@ -63,17 +71,18 @@ export function isDefaultQuery(query: PivotQuery): boolean {
|
||||||
return isSimpleQuery(query) && query.query_string.query === '*';
|
return isSimpleQuery(query) && query.query_string.query === '*';
|
||||||
}
|
}
|
||||||
|
|
||||||
export function getPreviewRequestBody(
|
export function getPreviewTransformRequestBody(
|
||||||
indexPatternTitle: IndexPattern['title'],
|
indexPatternTitle: IndexPattern['title'],
|
||||||
query: PivotQuery,
|
query: PivotQuery,
|
||||||
groupBy: PivotGroupByConfig[],
|
groupBy: PivotGroupByConfig[],
|
||||||
aggs: PivotAggsConfig[]
|
aggs: PivotAggsConfig[]
|
||||||
): PreviewRequestBody {
|
): PostTransformsPreviewRequestSchema {
|
||||||
const index = indexPatternTitle.split(',').map((name: string) => name.trim());
|
const index = indexPatternTitle.split(',').map((name: string) => name.trim());
|
||||||
|
|
||||||
const request: PreviewRequestBody = {
|
const request: PostTransformsPreviewRequestSchema = {
|
||||||
source: {
|
source: {
|
||||||
index,
|
index,
|
||||||
|
...(!isDefaultQuery(query) && !isMatchAllQuery(query) ? { query } : {}),
|
||||||
},
|
},
|
||||||
pivot: {
|
pivot: {
|
||||||
group_by: {},
|
group_by: {},
|
||||||
|
@ -81,10 +90,6 @@ export function getPreviewRequestBody(
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
if (!isDefaultQuery(query) && !isMatchAllQuery(query)) {
|
|
||||||
request.source.query = query;
|
|
||||||
}
|
|
||||||
|
|
||||||
groupBy.forEach((g) => {
|
groupBy.forEach((g) => {
|
||||||
if (isGroupByTerms(g)) {
|
if (isGroupByTerms(g)) {
|
||||||
const termsAgg: TermsAgg = {
|
const termsAgg: TermsAgg = {
|
||||||
|
@ -125,37 +130,41 @@ export function getPreviewRequestBody(
|
||||||
return request;
|
return request;
|
||||||
}
|
}
|
||||||
|
|
||||||
export function getCreateRequestBody(
|
export const getCreateTransformRequestBody = (
|
||||||
indexPatternTitle: IndexPattern['title'],
|
indexPatternTitle: IndexPattern['title'],
|
||||||
pivotState: StepDefineExposedState,
|
pivotState: StepDefineExposedState,
|
||||||
transformDetailsState: StepDetailsExposedState
|
transformDetailsState: StepDetailsExposedState
|
||||||
): CreateRequestBody {
|
): PutTransformsRequestSchema => ({
|
||||||
const request: CreateRequestBody = {
|
...getPreviewTransformRequestBody(
|
||||||
...getPreviewRequestBody(
|
indexPatternTitle,
|
||||||
indexPatternTitle,
|
getPivotQuery(pivotState.searchQuery),
|
||||||
getPivotQuery(pivotState.searchQuery),
|
dictionaryToArray(pivotState.groupByList),
|
||||||
dictionaryToArray(pivotState.groupByList),
|
dictionaryToArray(pivotState.aggList)
|
||||||
dictionaryToArray(pivotState.aggList)
|
),
|
||||||
),
|
// conditionally add optional description
|
||||||
// conditionally add optional description
|
...(transformDetailsState.transformDescription !== ''
|
||||||
...(transformDetailsState.transformDescription !== ''
|
? { description: transformDetailsState.transformDescription }
|
||||||
? { description: transformDetailsState.transformDescription }
|
: {}),
|
||||||
: {}),
|
dest: {
|
||||||
dest: {
|
index: transformDetailsState.destinationIndex,
|
||||||
index: transformDetailsState.destinationIndex,
|
},
|
||||||
},
|
// conditionally add continuous mode config
|
||||||
// conditionally add continuous mode config
|
...(transformDetailsState.isContinuousModeEnabled
|
||||||
...(transformDetailsState.isContinuousModeEnabled
|
? {
|
||||||
? {
|
sync: {
|
||||||
sync: {
|
time: {
|
||||||
time: {
|
field: transformDetailsState.continuousModeDateField,
|
||||||
field: transformDetailsState.continuousModeDateField,
|
delay: transformDetailsState.continuousModeDelay,
|
||||||
delay: transformDetailsState.continuousModeDelay,
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
}
|
},
|
||||||
: {}),
|
}
|
||||||
};
|
: {}),
|
||||||
|
});
|
||||||
|
|
||||||
return request;
|
export function isHttpFetchError(error: any): error is HttpFetchError {
|
||||||
|
return (
|
||||||
|
error instanceof HttpFetchError &&
|
||||||
|
typeof error.name === 'string' &&
|
||||||
|
typeof error.message !== 'undefined'
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
|
@ -9,13 +9,7 @@ import { BehaviorSubject } from 'rxjs';
|
||||||
import { filter, distinctUntilChanged } from 'rxjs/operators';
|
import { filter, distinctUntilChanged } from 'rxjs/operators';
|
||||||
import { Subscription } from 'rxjs';
|
import { Subscription } from 'rxjs';
|
||||||
|
|
||||||
import { TransformId } from '../../../common';
|
import { TransformId } from '../../../common/types/transform';
|
||||||
|
|
||||||
import { PivotAggDict } from './pivot_aggs';
|
|
||||||
import { PivotGroupByDict } from './pivot_group_by';
|
|
||||||
|
|
||||||
export type IndexName = string;
|
|
||||||
export type IndexPattern = string;
|
|
||||||
|
|
||||||
// Transform name must contain lowercase alphanumeric (a-z and 0-9), hyphens or underscores;
|
// Transform name must contain lowercase alphanumeric (a-z and 0-9), hyphens or underscores;
|
||||||
// It must also start and end with an alphanumeric character.
|
// It must also start and end with an alphanumeric character.
|
||||||
|
@ -23,41 +17,6 @@ export function isTransformIdValid(transformId: TransformId) {
|
||||||
return /^[a-z0-9\-\_]+$/g.test(transformId) && !/^([_-].*)?(.*[_-])?$/g.test(transformId);
|
return /^[a-z0-9\-\_]+$/g.test(transformId) && !/^([_-].*)?(.*[_-])?$/g.test(transformId);
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface PreviewRequestBody {
|
|
||||||
pivot: {
|
|
||||||
group_by: PivotGroupByDict;
|
|
||||||
aggregations: PivotAggDict;
|
|
||||||
};
|
|
||||||
source: {
|
|
||||||
index: IndexPattern | IndexPattern[];
|
|
||||||
query?: any;
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface CreateRequestBody extends PreviewRequestBody {
|
|
||||||
description?: string;
|
|
||||||
dest: {
|
|
||||||
index: IndexName;
|
|
||||||
};
|
|
||||||
frequency?: string;
|
|
||||||
settings?: {
|
|
||||||
max_page_search_size?: number;
|
|
||||||
docs_per_second?: number;
|
|
||||||
};
|
|
||||||
sync?: {
|
|
||||||
time: {
|
|
||||||
field: string;
|
|
||||||
delay: string;
|
|
||||||
};
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface TransformPivotConfig extends CreateRequestBody {
|
|
||||||
id: TransformId;
|
|
||||||
create_time?: number;
|
|
||||||
version?: string;
|
|
||||||
}
|
|
||||||
|
|
||||||
export enum REFRESH_TRANSFORM_LIST_STATE {
|
export enum REFRESH_TRANSFORM_LIST_STATE {
|
||||||
ERROR = 'error',
|
ERROR = 'error',
|
||||||
IDLE = 'idle',
|
IDLE = 'idle',
|
||||||
|
|
|
@ -6,9 +6,8 @@
|
||||||
|
|
||||||
import { EuiTableActionsColumnType } from '@elastic/eui';
|
import { EuiTableActionsColumnType } from '@elastic/eui';
|
||||||
|
|
||||||
import { TransformId } from '../../../common';
|
import { TransformId, TransformPivotConfig } from '../../../common/types/transform';
|
||||||
import { TransformPivotConfig } from './transform';
|
import { TransformStats } from '../../../common/types/transform_stats';
|
||||||
import { TransformStats } from './transform_stats';
|
|
||||||
|
|
||||||
// Used to pass on attribute names to table columns
|
// Used to pass on attribute names to table columns
|
||||||
export enum TRANSFORM_LIST_COLUMN {
|
export enum TRANSFORM_LIST_COLUMN {
|
||||||
|
|
|
@ -4,64 +4,10 @@
|
||||||
* you may not use this file except in compliance with the Elastic License.
|
* you may not use this file except in compliance with the Elastic License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import { TransformId, TRANSFORM_STATE } from '../../../common';
|
import { TRANSFORM_STATE } from '../../../common/constants';
|
||||||
|
|
||||||
import { TransformListRow } from './transform_list';
|
import { TransformListRow } from './transform_list';
|
||||||
|
|
||||||
export enum TRANSFORM_MODE {
|
|
||||||
BATCH = 'batch',
|
|
||||||
CONTINUOUS = 'continuous',
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface TransformStats {
|
|
||||||
id: TransformId;
|
|
||||||
checkpointing: {
|
|
||||||
last: {
|
|
||||||
checkpoint: number;
|
|
||||||
timestamp_millis?: number;
|
|
||||||
};
|
|
||||||
next?: {
|
|
||||||
checkpoint: number;
|
|
||||||
checkpoint_progress?: {
|
|
||||||
total_docs: number;
|
|
||||||
docs_remaining: number;
|
|
||||||
percent_complete: number;
|
|
||||||
};
|
|
||||||
};
|
|
||||||
operations_behind: number;
|
|
||||||
};
|
|
||||||
node?: {
|
|
||||||
id: string;
|
|
||||||
name: string;
|
|
||||||
ephemeral_id: string;
|
|
||||||
transport_address: string;
|
|
||||||
attributes: Record<string, any>;
|
|
||||||
};
|
|
||||||
stats: {
|
|
||||||
documents_indexed: number;
|
|
||||||
documents_processed: number;
|
|
||||||
index_failures: number;
|
|
||||||
index_time_in_ms: number;
|
|
||||||
index_total: number;
|
|
||||||
pages_processed: number;
|
|
||||||
search_failures: number;
|
|
||||||
search_time_in_ms: number;
|
|
||||||
search_total: number;
|
|
||||||
trigger_count: number;
|
|
||||||
};
|
|
||||||
reason?: string;
|
|
||||||
state: TRANSFORM_STATE;
|
|
||||||
}
|
|
||||||
|
|
||||||
export function isTransformStats(arg: any): arg is TransformStats {
|
|
||||||
return (
|
|
||||||
typeof arg === 'object' &&
|
|
||||||
arg !== null &&
|
|
||||||
{}.hasOwnProperty.call(arg, 'state') &&
|
|
||||||
Object.values(TRANSFORM_STATE).includes(arg.state)
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
export function getTransformProgress(item: TransformListRow) {
|
export function getTransformProgress(item: TransformListRow) {
|
||||||
if (isCompletedBatchTransform(item)) {
|
if (isCompletedBatchTransform(item)) {
|
||||||
return 100;
|
return 100;
|
||||||
|
|
|
@ -4,66 +4,161 @@
|
||||||
* you may not use this file except in compliance with the Elastic License.
|
* you may not use this file except in compliance with the Elastic License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import { TransformId, TransformEndpointRequest } from '../../../../common';
|
import { HttpFetchError } from 'kibana/public';
|
||||||
|
|
||||||
import { PreviewRequestBody } from '../../common';
|
import { KBN_FIELD_TYPES } from '../../../../../../../src/plugins/data/public';
|
||||||
|
|
||||||
|
import { TransformId } from '../../../../common/types/transform';
|
||||||
|
import type { FieldHistogramsResponseSchema } from '../../../../common/api_schemas/field_histograms';
|
||||||
|
import type { GetTransformsAuditMessagesResponseSchema } from '../../../../common/api_schemas/audit_messages';
|
||||||
|
import type {
|
||||||
|
DeleteTransformsRequestSchema,
|
||||||
|
DeleteTransformsResponseSchema,
|
||||||
|
} from '../../../../common/api_schemas/delete_transforms';
|
||||||
|
import type {
|
||||||
|
StartTransformsRequestSchema,
|
||||||
|
StartTransformsResponseSchema,
|
||||||
|
} from '../../../../common/api_schemas/start_transforms';
|
||||||
|
import type {
|
||||||
|
StopTransformsRequestSchema,
|
||||||
|
StopTransformsResponseSchema,
|
||||||
|
} from '../../../../common/api_schemas/stop_transforms';
|
||||||
|
import type {
|
||||||
|
GetTransformsResponseSchema,
|
||||||
|
PostTransformsPreviewRequestSchema,
|
||||||
|
PostTransformsPreviewResponseSchema,
|
||||||
|
PutTransformsRequestSchema,
|
||||||
|
PutTransformsResponseSchema,
|
||||||
|
} from '../../../../common/api_schemas/transforms';
|
||||||
|
import type { GetTransformsStatsResponseSchema } from '../../../../common/api_schemas/transforms_stats';
|
||||||
|
import type {
|
||||||
|
PostTransformsUpdateRequestSchema,
|
||||||
|
PostTransformsUpdateResponseSchema,
|
||||||
|
} from '../../../../common/api_schemas/update_transforms';
|
||||||
|
|
||||||
|
import type { SearchResponse7 } from '../../../../common/shared_imports';
|
||||||
|
import { EsIndex } from '../../../../common/types/es_index';
|
||||||
|
|
||||||
|
import type { SavedSearchQuery } from '../use_search_items';
|
||||||
|
|
||||||
|
// Default sampler shard size used for field histograms
|
||||||
|
export const DEFAULT_SAMPLER_SHARD_SIZE = 5000;
|
||||||
|
|
||||||
|
export interface FieldHistogramRequestConfig {
|
||||||
|
fieldName: string;
|
||||||
|
type?: KBN_FIELD_TYPES;
|
||||||
|
}
|
||||||
|
|
||||||
const apiFactory = () => ({
|
const apiFactory = () => ({
|
||||||
getTransforms(transformId?: TransformId): Promise<any> {
|
async getTransform(
|
||||||
return new Promise((resolve, reject) => {
|
transformId: TransformId
|
||||||
resolve([]);
|
): Promise<GetTransformsResponseSchema | HttpFetchError> {
|
||||||
|
return Promise.resolve({ count: 0, transforms: [] });
|
||||||
|
},
|
||||||
|
async getTransforms(): Promise<GetTransformsResponseSchema | HttpFetchError> {
|
||||||
|
return Promise.resolve({ count: 0, transforms: [] });
|
||||||
|
},
|
||||||
|
async getTransformStats(
|
||||||
|
transformId: TransformId
|
||||||
|
): Promise<GetTransformsStatsResponseSchema | HttpFetchError> {
|
||||||
|
return Promise.resolve({ count: 0, transforms: [] });
|
||||||
|
},
|
||||||
|
async getTransformsStats(): Promise<GetTransformsStatsResponseSchema | HttpFetchError> {
|
||||||
|
return Promise.resolve({ count: 0, transforms: [] });
|
||||||
|
},
|
||||||
|
async createTransform(
|
||||||
|
transformId: TransformId,
|
||||||
|
transformConfig: PutTransformsRequestSchema
|
||||||
|
): Promise<PutTransformsResponseSchema | HttpFetchError> {
|
||||||
|
return Promise.resolve({ transformsCreated: [], errors: [] });
|
||||||
|
},
|
||||||
|
async updateTransform(
|
||||||
|
transformId: TransformId,
|
||||||
|
transformConfig: PostTransformsUpdateRequestSchema
|
||||||
|
): Promise<PostTransformsUpdateResponseSchema | HttpFetchError> {
|
||||||
|
return Promise.resolve({
|
||||||
|
id: 'the-test-id',
|
||||||
|
source: { index: ['the-index-name'], query: { match_all: {} } },
|
||||||
|
dest: { index: 'user-the-destination-index-name' },
|
||||||
|
frequency: '10m',
|
||||||
|
pivot: {
|
||||||
|
group_by: { the_group: { terms: { field: 'the-group-by-field' } } },
|
||||||
|
aggregations: { the_agg: { value_count: { field: 'the-agg-field' } } },
|
||||||
|
},
|
||||||
|
description: 'the-description',
|
||||||
|
settings: { docs_per_second: null },
|
||||||
|
version: '8.0.0',
|
||||||
|
create_time: 1598860879097,
|
||||||
|
});
|
||||||
|
},
|
||||||
|
async deleteTransforms(
|
||||||
|
reqBody: DeleteTransformsRequestSchema
|
||||||
|
): Promise<DeleteTransformsResponseSchema | HttpFetchError> {
|
||||||
|
return Promise.resolve({});
|
||||||
|
},
|
||||||
|
async getTransformsPreview(
|
||||||
|
obj: PostTransformsPreviewRequestSchema
|
||||||
|
): Promise<PostTransformsPreviewResponseSchema | HttpFetchError> {
|
||||||
|
return Promise.resolve({
|
||||||
|
generated_dest_index: {
|
||||||
|
mappings: {
|
||||||
|
_meta: {
|
||||||
|
_transform: {
|
||||||
|
transform: 'the-transform',
|
||||||
|
version: { create: 'the-version' },
|
||||||
|
creation_date_in_millis: 0,
|
||||||
|
},
|
||||||
|
created_by: 'mock',
|
||||||
|
},
|
||||||
|
properties: {},
|
||||||
|
},
|
||||||
|
settings: { index: { number_of_shards: '1', auto_expand_replicas: '0-1' } },
|
||||||
|
aliases: {},
|
||||||
|
},
|
||||||
|
preview: [],
|
||||||
|
});
|
||||||
|
},
|
||||||
|
async startTransforms(
|
||||||
|
reqBody: StartTransformsRequestSchema
|
||||||
|
): Promise<StartTransformsResponseSchema | HttpFetchError> {
|
||||||
|
return Promise.resolve({});
|
||||||
|
},
|
||||||
|
async stopTransforms(
|
||||||
|
transformsInfo: StopTransformsRequestSchema
|
||||||
|
): Promise<StopTransformsResponseSchema | HttpFetchError> {
|
||||||
|
return Promise.resolve({});
|
||||||
|
},
|
||||||
|
async getTransformAuditMessages(
|
||||||
|
transformId: TransformId
|
||||||
|
): Promise<GetTransformsAuditMessagesResponseSchema | HttpFetchError> {
|
||||||
|
return Promise.resolve([]);
|
||||||
|
},
|
||||||
|
async esSearch(payload: any): Promise<SearchResponse7 | HttpFetchError> {
|
||||||
|
return Promise.resolve({
|
||||||
|
hits: {
|
||||||
|
hits: [],
|
||||||
|
total: {
|
||||||
|
value: 0,
|
||||||
|
relation: 'the-relation',
|
||||||
|
},
|
||||||
|
max_score: 0,
|
||||||
|
},
|
||||||
|
timed_out: false,
|
||||||
|
took: 10,
|
||||||
|
_shards: { total: 1, successful: 1, failed: 0, skipped: 0 },
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
getTransformsStats(transformId?: TransformId): Promise<any> {
|
|
||||||
if (transformId !== undefined) {
|
|
||||||
return new Promise((resolve, reject) => {
|
|
||||||
resolve([]);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
return new Promise((resolve, reject) => {
|
async getEsIndices(): Promise<EsIndex[] | HttpFetchError> {
|
||||||
resolve([]);
|
return Promise.resolve([]);
|
||||||
});
|
|
||||||
},
|
},
|
||||||
createTransform(transformId: TransformId, transformConfig: any): Promise<any> {
|
async getHistogramsForFields(
|
||||||
return new Promise((resolve, reject) => {
|
indexPatternTitle: string,
|
||||||
resolve([]);
|
fields: FieldHistogramRequestConfig[],
|
||||||
});
|
query: string | SavedSearchQuery,
|
||||||
},
|
samplerShardSize = DEFAULT_SAMPLER_SHARD_SIZE
|
||||||
deleteTransforms(transformsInfo: TransformEndpointRequest[]) {
|
): Promise<FieldHistogramsResponseSchema | HttpFetchError> {
|
||||||
return new Promise((resolve, reject) => {
|
return Promise.resolve([]);
|
||||||
resolve([]);
|
|
||||||
});
|
|
||||||
},
|
|
||||||
getTransformsPreview(obj: PreviewRequestBody): Promise<any> {
|
|
||||||
return new Promise((resolve, reject) => {
|
|
||||||
resolve([]);
|
|
||||||
});
|
|
||||||
},
|
|
||||||
startTransforms(transformsInfo: TransformEndpointRequest[]) {
|
|
||||||
return new Promise((resolve, reject) => {
|
|
||||||
resolve([]);
|
|
||||||
});
|
|
||||||
},
|
|
||||||
stopTransforms(transformsInfo: TransformEndpointRequest[]) {
|
|
||||||
return new Promise((resolve, reject) => {
|
|
||||||
resolve([]);
|
|
||||||
});
|
|
||||||
},
|
|
||||||
getTransformAuditMessages(transformId: TransformId): Promise<any> {
|
|
||||||
return new Promise((resolve, reject) => {
|
|
||||||
resolve([]);
|
|
||||||
});
|
|
||||||
},
|
|
||||||
esSearch(payload: any) {
|
|
||||||
return new Promise((resolve, reject) => {
|
|
||||||
resolve([]);
|
|
||||||
});
|
|
||||||
},
|
|
||||||
getIndices() {
|
|
||||||
return new Promise((resolve, reject) => {
|
|
||||||
resolve([]);
|
|
||||||
});
|
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
|
@ -6,20 +6,43 @@
|
||||||
|
|
||||||
import { useMemo } from 'react';
|
import { useMemo } from 'react';
|
||||||
|
|
||||||
|
import { HttpFetchError } from 'kibana/public';
|
||||||
|
|
||||||
import { KBN_FIELD_TYPES } from '../../../../../../src/plugins/data/public';
|
import { KBN_FIELD_TYPES } from '../../../../../../src/plugins/data/public';
|
||||||
|
|
||||||
import {
|
import type { GetTransformsAuditMessagesResponseSchema } from '../../../common/api_schemas/audit_messages';
|
||||||
TransformId,
|
import type {
|
||||||
TransformEndpointRequest,
|
DeleteTransformsRequestSchema,
|
||||||
TransformEndpointResult,
|
DeleteTransformsResponseSchema,
|
||||||
DeleteTransformEndpointResult,
|
} from '../../../common/api_schemas/delete_transforms';
|
||||||
} from '../../../common';
|
import type { FieldHistogramsResponseSchema } from '../../../common/api_schemas/field_histograms';
|
||||||
|
import type {
|
||||||
|
StartTransformsRequestSchema,
|
||||||
|
StartTransformsResponseSchema,
|
||||||
|
} from '../../../common/api_schemas/start_transforms';
|
||||||
|
import type {
|
||||||
|
StopTransformsRequestSchema,
|
||||||
|
StopTransformsResponseSchema,
|
||||||
|
} from '../../../common/api_schemas/stop_transforms';
|
||||||
|
import type {
|
||||||
|
GetTransformsResponseSchema,
|
||||||
|
PostTransformsPreviewRequestSchema,
|
||||||
|
PostTransformsPreviewResponseSchema,
|
||||||
|
PutTransformsRequestSchema,
|
||||||
|
PutTransformsResponseSchema,
|
||||||
|
} from '../../../common/api_schemas/transforms';
|
||||||
|
import type {
|
||||||
|
PostTransformsUpdateRequestSchema,
|
||||||
|
PostTransformsUpdateResponseSchema,
|
||||||
|
} from '../../../common/api_schemas/update_transforms';
|
||||||
|
import type { GetTransformsStatsResponseSchema } from '../../../common/api_schemas/transforms_stats';
|
||||||
|
import { TransformId } from '../../../common/types/transform';
|
||||||
import { API_BASE_PATH } from '../../../common/constants';
|
import { API_BASE_PATH } from '../../../common/constants';
|
||||||
|
import { EsIndex } from '../../../common/types/es_index';
|
||||||
|
import type { SearchResponse7 } from '../../../common/shared_imports';
|
||||||
|
|
||||||
import { useAppDependencies } from '../app_dependencies';
|
import { useAppDependencies } from '../app_dependencies';
|
||||||
import { GetTransformsResponse, PreviewRequestBody } from '../common';
|
|
||||||
|
|
||||||
import { EsIndex } from './use_api_types';
|
|
||||||
import { SavedSearchQuery } from './use_search_items';
|
import { SavedSearchQuery } from './use_search_items';
|
||||||
|
|
||||||
// Default sampler shard size used for field histograms
|
// Default sampler shard size used for field histograms
|
||||||
|
@ -35,81 +58,146 @@ export const useApi = () => {
|
||||||
|
|
||||||
return useMemo(
|
return useMemo(
|
||||||
() => ({
|
() => ({
|
||||||
getTransforms(transformId?: TransformId): Promise<any> {
|
async getTransform(
|
||||||
const transformIdString = transformId !== undefined ? `/${transformId}` : '';
|
transformId: TransformId
|
||||||
return http.get(`${API_BASE_PATH}transforms${transformIdString}`);
|
): Promise<GetTransformsResponseSchema | HttpFetchError> {
|
||||||
},
|
try {
|
||||||
getTransformsStats(transformId?: TransformId): Promise<any> {
|
return await http.get(`${API_BASE_PATH}transforms/${transformId}`);
|
||||||
if (transformId !== undefined) {
|
} catch (e) {
|
||||||
return http.get(`${API_BASE_PATH}transforms/${transformId}/_stats`);
|
return e;
|
||||||
}
|
}
|
||||||
|
|
||||||
return http.get(`${API_BASE_PATH}transforms/_stats`);
|
|
||||||
},
|
},
|
||||||
createTransform(transformId: TransformId, transformConfig: any): Promise<any> {
|
async getTransforms(): Promise<GetTransformsResponseSchema | HttpFetchError> {
|
||||||
return http.put(`${API_BASE_PATH}transforms/${transformId}`, {
|
try {
|
||||||
body: JSON.stringify(transformConfig),
|
return await http.get(`${API_BASE_PATH}transforms`);
|
||||||
});
|
} catch (e) {
|
||||||
|
return e;
|
||||||
|
}
|
||||||
},
|
},
|
||||||
updateTransform(transformId: TransformId, transformConfig: any): Promise<any> {
|
async getTransformStats(
|
||||||
return http.post(`${API_BASE_PATH}transforms/${transformId}/_update`, {
|
transformId: TransformId
|
||||||
body: JSON.stringify(transformConfig),
|
): Promise<GetTransformsStatsResponseSchema | HttpFetchError> {
|
||||||
});
|
try {
|
||||||
|
return await http.get(`${API_BASE_PATH}transforms/${transformId}/_stats`);
|
||||||
|
} catch (e) {
|
||||||
|
return e;
|
||||||
|
}
|
||||||
},
|
},
|
||||||
deleteTransforms(
|
async getTransformsStats(): Promise<GetTransformsStatsResponseSchema | HttpFetchError> {
|
||||||
transformsInfo: TransformEndpointRequest[],
|
try {
|
||||||
deleteDestIndex: boolean | undefined,
|
return await http.get(`${API_BASE_PATH}transforms/_stats`);
|
||||||
deleteDestIndexPattern: boolean | undefined,
|
} catch (e) {
|
||||||
forceDelete: boolean
|
return e;
|
||||||
): Promise<DeleteTransformEndpointResult> {
|
}
|
||||||
return http.post(`${API_BASE_PATH}delete_transforms`, {
|
|
||||||
body: JSON.stringify({
|
|
||||||
transformsInfo,
|
|
||||||
deleteDestIndex,
|
|
||||||
deleteDestIndexPattern,
|
|
||||||
forceDelete,
|
|
||||||
}),
|
|
||||||
});
|
|
||||||
},
|
},
|
||||||
getTransformsPreview(obj: PreviewRequestBody): Promise<GetTransformsResponse> {
|
async createTransform(
|
||||||
return http.post(`${API_BASE_PATH}transforms/_preview`, {
|
transformId: TransformId,
|
||||||
body: JSON.stringify(obj),
|
transformConfig: PutTransformsRequestSchema
|
||||||
});
|
): Promise<PutTransformsResponseSchema | HttpFetchError> {
|
||||||
|
try {
|
||||||
|
return await http.put(`${API_BASE_PATH}transforms/${transformId}`, {
|
||||||
|
body: JSON.stringify(transformConfig),
|
||||||
|
});
|
||||||
|
} catch (e) {
|
||||||
|
return e;
|
||||||
|
}
|
||||||
},
|
},
|
||||||
startTransforms(
|
async updateTransform(
|
||||||
transformsInfo: TransformEndpointRequest[]
|
transformId: TransformId,
|
||||||
): Promise<TransformEndpointResult> {
|
transformConfig: PostTransformsUpdateRequestSchema
|
||||||
return http.post(`${API_BASE_PATH}start_transforms`, {
|
): Promise<PostTransformsUpdateResponseSchema | HttpFetchError> {
|
||||||
body: JSON.stringify(transformsInfo),
|
try {
|
||||||
});
|
return await http.post(`${API_BASE_PATH}transforms/${transformId}/_update`, {
|
||||||
|
body: JSON.stringify(transformConfig),
|
||||||
|
});
|
||||||
|
} catch (e) {
|
||||||
|
return e;
|
||||||
|
}
|
||||||
},
|
},
|
||||||
stopTransforms(transformsInfo: TransformEndpointRequest[]): Promise<TransformEndpointResult> {
|
async deleteTransforms(
|
||||||
return http.post(`${API_BASE_PATH}stop_transforms`, {
|
reqBody: DeleteTransformsRequestSchema
|
||||||
body: JSON.stringify(transformsInfo),
|
): Promise<DeleteTransformsResponseSchema | HttpFetchError> {
|
||||||
});
|
try {
|
||||||
|
return await http.post(`${API_BASE_PATH}delete_transforms`, {
|
||||||
|
body: JSON.stringify(reqBody),
|
||||||
|
});
|
||||||
|
} catch (e) {
|
||||||
|
return e;
|
||||||
|
}
|
||||||
},
|
},
|
||||||
getTransformAuditMessages(transformId: TransformId): Promise<any> {
|
async getTransformsPreview(
|
||||||
return http.get(`${API_BASE_PATH}transforms/${transformId}/messages`);
|
obj: PostTransformsPreviewRequestSchema
|
||||||
|
): Promise<PostTransformsPreviewResponseSchema | HttpFetchError> {
|
||||||
|
try {
|
||||||
|
return await http.post(`${API_BASE_PATH}transforms/_preview`, {
|
||||||
|
body: JSON.stringify(obj),
|
||||||
|
});
|
||||||
|
} catch (e) {
|
||||||
|
return e;
|
||||||
|
}
|
||||||
},
|
},
|
||||||
esSearch(payload: any): Promise<any> {
|
async startTransforms(
|
||||||
return http.post(`${API_BASE_PATH}es_search`, { body: JSON.stringify(payload) });
|
reqBody: StartTransformsRequestSchema
|
||||||
|
): Promise<StartTransformsResponseSchema | HttpFetchError> {
|
||||||
|
try {
|
||||||
|
return await http.post(`${API_BASE_PATH}start_transforms`, {
|
||||||
|
body: JSON.stringify(reqBody),
|
||||||
|
});
|
||||||
|
} catch (e) {
|
||||||
|
return e;
|
||||||
|
}
|
||||||
},
|
},
|
||||||
getIndices(): Promise<EsIndex[]> {
|
async stopTransforms(
|
||||||
return http.get(`/api/index_management/indices`);
|
transformsInfo: StopTransformsRequestSchema
|
||||||
|
): Promise<StopTransformsResponseSchema | HttpFetchError> {
|
||||||
|
try {
|
||||||
|
return await http.post(`${API_BASE_PATH}stop_transforms`, {
|
||||||
|
body: JSON.stringify(transformsInfo),
|
||||||
|
});
|
||||||
|
} catch (e) {
|
||||||
|
return e;
|
||||||
|
}
|
||||||
},
|
},
|
||||||
getHistogramsForFields(
|
async getTransformAuditMessages(
|
||||||
|
transformId: TransformId
|
||||||
|
): Promise<GetTransformsAuditMessagesResponseSchema | HttpFetchError> {
|
||||||
|
try {
|
||||||
|
return await http.get(`${API_BASE_PATH}transforms/${transformId}/messages`);
|
||||||
|
} catch (e) {
|
||||||
|
return e;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
async esSearch(payload: any): Promise<SearchResponse7 | HttpFetchError> {
|
||||||
|
try {
|
||||||
|
return await http.post(`${API_BASE_PATH}es_search`, { body: JSON.stringify(payload) });
|
||||||
|
} catch (e) {
|
||||||
|
return e;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
async getEsIndices(): Promise<EsIndex[] | HttpFetchError> {
|
||||||
|
try {
|
||||||
|
return await http.get(`/api/index_management/indices`);
|
||||||
|
} catch (e) {
|
||||||
|
return e;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
async getHistogramsForFields(
|
||||||
indexPatternTitle: string,
|
indexPatternTitle: string,
|
||||||
fields: FieldHistogramRequestConfig[],
|
fields: FieldHistogramRequestConfig[],
|
||||||
query: string | SavedSearchQuery,
|
query: string | SavedSearchQuery,
|
||||||
samplerShardSize = DEFAULT_SAMPLER_SHARD_SIZE
|
samplerShardSize = DEFAULT_SAMPLER_SHARD_SIZE
|
||||||
) {
|
): Promise<FieldHistogramsResponseSchema | HttpFetchError> {
|
||||||
return http.post(`${API_BASE_PATH}field_histograms/${indexPatternTitle}`, {
|
try {
|
||||||
body: JSON.stringify({
|
return await http.post(`${API_BASE_PATH}field_histograms/${indexPatternTitle}`, {
|
||||||
query,
|
body: JSON.stringify({
|
||||||
fields,
|
query,
|
||||||
samplerShardSize,
|
fields,
|
||||||
}),
|
samplerShardSize,
|
||||||
});
|
}),
|
||||||
|
});
|
||||||
|
} catch (e) {
|
||||||
|
return e;
|
||||||
|
}
|
||||||
},
|
},
|
||||||
}),
|
}),
|
||||||
[http]
|
[http]
|
||||||
|
|
|
@ -7,11 +7,11 @@
|
||||||
import React, { useCallback, useEffect, useState } from 'react';
|
import React, { useCallback, useEffect, useState } from 'react';
|
||||||
import { i18n } from '@kbn/i18n';
|
import { i18n } from '@kbn/i18n';
|
||||||
import { toMountPoint } from '../../../../../../src/plugins/kibana_react/public';
|
import { toMountPoint } from '../../../../../../src/plugins/kibana_react/public';
|
||||||
import {
|
import type {
|
||||||
DeleteTransformEndpointResult,
|
|
||||||
DeleteTransformStatus,
|
DeleteTransformStatus,
|
||||||
TransformEndpointRequest,
|
DeleteTransformsRequestSchema,
|
||||||
} from '../../../common';
|
} from '../../../common/api_schemas/delete_transforms';
|
||||||
|
import { isDeleteTransformsResponseSchema } from '../../../common/api_schemas/type_guards';
|
||||||
import { extractErrorMessage } from '../../shared_imports';
|
import { extractErrorMessage } from '../../shared_imports';
|
||||||
import { getErrorMessage } from '../../../common/utils/errors';
|
import { getErrorMessage } from '../../../common/utils/errors';
|
||||||
import { useAppDependencies, useToastNotifications } from '../app_dependencies';
|
import { useAppDependencies, useToastNotifications } from '../app_dependencies';
|
||||||
|
@ -109,161 +109,10 @@ export const useDeleteTransforms = () => {
|
||||||
const toastNotifications = useToastNotifications();
|
const toastNotifications = useToastNotifications();
|
||||||
const api = useApi();
|
const api = useApi();
|
||||||
|
|
||||||
return async (
|
return async (reqBody: DeleteTransformsRequestSchema) => {
|
||||||
transforms: TransformListRow[],
|
const results = await api.deleteTransforms(reqBody);
|
||||||
shouldDeleteDestIndex: boolean,
|
|
||||||
shouldDeleteDestIndexPattern: boolean,
|
|
||||||
shouldForceDelete = false
|
|
||||||
) => {
|
|
||||||
const transformsInfo: TransformEndpointRequest[] = transforms.map((tf) => ({
|
|
||||||
id: tf.config.id,
|
|
||||||
state: tf.stats.state,
|
|
||||||
}));
|
|
||||||
|
|
||||||
try {
|
if (!isDeleteTransformsResponseSchema(results)) {
|
||||||
const results: DeleteTransformEndpointResult = await api.deleteTransforms(
|
|
||||||
transformsInfo,
|
|
||||||
shouldDeleteDestIndex,
|
|
||||||
shouldDeleteDestIndexPattern,
|
|
||||||
shouldForceDelete
|
|
||||||
);
|
|
||||||
const isBulk = Object.keys(results).length > 1;
|
|
||||||
const successCount: Record<SuccessCountField, number> = {
|
|
||||||
transformDeleted: 0,
|
|
||||||
destIndexDeleted: 0,
|
|
||||||
destIndexPatternDeleted: 0,
|
|
||||||
};
|
|
||||||
for (const transformId in results) {
|
|
||||||
// hasOwnProperty check to ensure only properties on object itself, and not its prototypes
|
|
||||||
if (results.hasOwnProperty(transformId)) {
|
|
||||||
const status = results[transformId];
|
|
||||||
const destinationIndex = status.destinationIndex;
|
|
||||||
|
|
||||||
// if we are only deleting one transform, show the success toast messages
|
|
||||||
if (!isBulk && status.transformDeleted) {
|
|
||||||
if (status.transformDeleted?.success) {
|
|
||||||
toastNotifications.addSuccess(
|
|
||||||
i18n.translate('xpack.transform.transformList.deleteTransformSuccessMessage', {
|
|
||||||
defaultMessage: 'Request to delete transform {transformId} acknowledged.',
|
|
||||||
values: { transformId },
|
|
||||||
})
|
|
||||||
);
|
|
||||||
}
|
|
||||||
if (status.destIndexDeleted?.success) {
|
|
||||||
toastNotifications.addSuccess(
|
|
||||||
i18n.translate(
|
|
||||||
'xpack.transform.deleteTransform.deleteAnalyticsWithIndexSuccessMessage',
|
|
||||||
{
|
|
||||||
defaultMessage:
|
|
||||||
'Request to delete destination index {destinationIndex} acknowledged.',
|
|
||||||
values: { destinationIndex },
|
|
||||||
}
|
|
||||||
)
|
|
||||||
);
|
|
||||||
}
|
|
||||||
if (status.destIndexPatternDeleted?.success) {
|
|
||||||
toastNotifications.addSuccess(
|
|
||||||
i18n.translate(
|
|
||||||
'xpack.transform.deleteTransform.deleteAnalyticsWithIndexPatternSuccessMessage',
|
|
||||||
{
|
|
||||||
defaultMessage:
|
|
||||||
'Request to delete index pattern {destinationIndex} acknowledged.',
|
|
||||||
values: { destinationIndex },
|
|
||||||
}
|
|
||||||
)
|
|
||||||
);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
(Object.keys(successCount) as SuccessCountField[]).forEach((key) => {
|
|
||||||
if (status[key]?.success) {
|
|
||||||
successCount[key] = successCount[key] + 1;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
if (status.transformDeleted?.error) {
|
|
||||||
const error = extractErrorMessage(status.transformDeleted.error);
|
|
||||||
toastNotifications.addDanger({
|
|
||||||
title: i18n.translate('xpack.transform.transformList.deleteTransformErrorMessage', {
|
|
||||||
defaultMessage: 'An error occurred deleting the transform {transformId}',
|
|
||||||
values: { transformId },
|
|
||||||
}),
|
|
||||||
text: toMountPoint(
|
|
||||||
<ToastNotificationText previewTextLength={50} overlays={overlays} text={error} />
|
|
||||||
),
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
if (status.destIndexDeleted?.error) {
|
|
||||||
const error = extractErrorMessage(status.destIndexDeleted.error);
|
|
||||||
toastNotifications.addDanger({
|
|
||||||
title: i18n.translate(
|
|
||||||
'xpack.transform.deleteTransform.deleteAnalyticsWithIndexErrorMessage',
|
|
||||||
{
|
|
||||||
defaultMessage: 'An error occurred deleting destination index {destinationIndex}',
|
|
||||||
values: { destinationIndex },
|
|
||||||
}
|
|
||||||
),
|
|
||||||
text: toMountPoint(
|
|
||||||
<ToastNotificationText previewTextLength={50} overlays={overlays} text={error} />
|
|
||||||
),
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
if (status.destIndexPatternDeleted?.error) {
|
|
||||||
const error = extractErrorMessage(status.destIndexPatternDeleted.error);
|
|
||||||
toastNotifications.addDanger({
|
|
||||||
title: i18n.translate(
|
|
||||||
'xpack.transform.deleteTransform.deleteAnalyticsWithIndexPatternErrorMessage',
|
|
||||||
{
|
|
||||||
defaultMessage: 'An error occurred deleting index pattern {destinationIndex}',
|
|
||||||
values: { destinationIndex },
|
|
||||||
}
|
|
||||||
),
|
|
||||||
text: toMountPoint(
|
|
||||||
<ToastNotificationText previewTextLength={50} overlays={overlays} text={error} />
|
|
||||||
),
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// if we are deleting multiple transforms, combine the success messages
|
|
||||||
if (isBulk) {
|
|
||||||
if (successCount.transformDeleted > 0) {
|
|
||||||
toastNotifications.addSuccess(
|
|
||||||
i18n.translate('xpack.transform.transformList.bulkDeleteTransformSuccessMessage', {
|
|
||||||
defaultMessage:
|
|
||||||
'Successfully deleted {count} {count, plural, one {transform} other {transforms}}.',
|
|
||||||
values: { count: successCount.transformDeleted },
|
|
||||||
})
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (successCount.destIndexDeleted > 0) {
|
|
||||||
toastNotifications.addSuccess(
|
|
||||||
i18n.translate('xpack.transform.transformList.bulkDeleteDestIndexSuccessMessage', {
|
|
||||||
defaultMessage:
|
|
||||||
'Successfully deleted {count} destination {count, plural, one {index} other {indices}}.',
|
|
||||||
values: { count: successCount.destIndexDeleted },
|
|
||||||
})
|
|
||||||
);
|
|
||||||
}
|
|
||||||
if (successCount.destIndexPatternDeleted > 0) {
|
|
||||||
toastNotifications.addSuccess(
|
|
||||||
i18n.translate(
|
|
||||||
'xpack.transform.transformList.bulkDeleteDestIndexPatternSuccessMessage',
|
|
||||||
{
|
|
||||||
defaultMessage:
|
|
||||||
'Successfully deleted {count} destination index {count, plural, one {pattern} other {patterns}}.',
|
|
||||||
values: { count: successCount.destIndexPatternDeleted },
|
|
||||||
}
|
|
||||||
)
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
refreshTransformList$.next(REFRESH_TRANSFORM_LIST_STATE.REFRESH);
|
|
||||||
} catch (e) {
|
|
||||||
toastNotifications.addDanger({
|
toastNotifications.addDanger({
|
||||||
title: i18n.translate('xpack.transform.transformList.deleteTransformGenericErrorMessage', {
|
title: i18n.translate('xpack.transform.transformList.deleteTransformGenericErrorMessage', {
|
||||||
defaultMessage: 'An error occurred calling the API endpoint to delete transforms.',
|
defaultMessage: 'An error occurred calling the API endpoint to delete transforms.',
|
||||||
|
@ -272,10 +121,145 @@ export const useDeleteTransforms = () => {
|
||||||
<ToastNotificationText
|
<ToastNotificationText
|
||||||
previewTextLength={50}
|
previewTextLength={50}
|
||||||
overlays={overlays}
|
overlays={overlays}
|
||||||
text={getErrorMessage(e)}
|
text={getErrorMessage(results)}
|
||||||
/>
|
/>
|
||||||
),
|
),
|
||||||
});
|
});
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const isBulk = Object.keys(results).length > 1;
|
||||||
|
const successCount: Record<SuccessCountField, number> = {
|
||||||
|
transformDeleted: 0,
|
||||||
|
destIndexDeleted: 0,
|
||||||
|
destIndexPatternDeleted: 0,
|
||||||
|
};
|
||||||
|
for (const transformId in results) {
|
||||||
|
// hasOwnProperty check to ensure only properties on object itself, and not its prototypes
|
||||||
|
if (results.hasOwnProperty(transformId)) {
|
||||||
|
const status = results[transformId];
|
||||||
|
const destinationIndex = status.destinationIndex;
|
||||||
|
|
||||||
|
// if we are only deleting one transform, show the success toast messages
|
||||||
|
if (!isBulk && status.transformDeleted) {
|
||||||
|
if (status.transformDeleted?.success) {
|
||||||
|
toastNotifications.addSuccess(
|
||||||
|
i18n.translate('xpack.transform.transformList.deleteTransformSuccessMessage', {
|
||||||
|
defaultMessage: 'Request to delete transform {transformId} acknowledged.',
|
||||||
|
values: { transformId },
|
||||||
|
})
|
||||||
|
);
|
||||||
|
}
|
||||||
|
if (status.destIndexDeleted?.success) {
|
||||||
|
toastNotifications.addSuccess(
|
||||||
|
i18n.translate(
|
||||||
|
'xpack.transform.deleteTransform.deleteAnalyticsWithIndexSuccessMessage',
|
||||||
|
{
|
||||||
|
defaultMessage:
|
||||||
|
'Request to delete destination index {destinationIndex} acknowledged.',
|
||||||
|
values: { destinationIndex },
|
||||||
|
}
|
||||||
|
)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
if (status.destIndexPatternDeleted?.success) {
|
||||||
|
toastNotifications.addSuccess(
|
||||||
|
i18n.translate(
|
||||||
|
'xpack.transform.deleteTransform.deleteAnalyticsWithIndexPatternSuccessMessage',
|
||||||
|
{
|
||||||
|
defaultMessage:
|
||||||
|
'Request to delete index pattern {destinationIndex} acknowledged.',
|
||||||
|
values: { destinationIndex },
|
||||||
|
}
|
||||||
|
)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
(Object.keys(successCount) as SuccessCountField[]).forEach((key) => {
|
||||||
|
if (status[key]?.success) {
|
||||||
|
successCount[key] = successCount[key] + 1;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
if (status.transformDeleted?.error) {
|
||||||
|
const error = extractErrorMessage(status.transformDeleted.error);
|
||||||
|
toastNotifications.addDanger({
|
||||||
|
title: i18n.translate('xpack.transform.transformList.deleteTransformErrorMessage', {
|
||||||
|
defaultMessage: 'An error occurred deleting the transform {transformId}',
|
||||||
|
values: { transformId },
|
||||||
|
}),
|
||||||
|
text: toMountPoint(
|
||||||
|
<ToastNotificationText previewTextLength={50} overlays={overlays} text={error} />
|
||||||
|
),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
if (status.destIndexDeleted?.error) {
|
||||||
|
const error = extractErrorMessage(status.destIndexDeleted.error);
|
||||||
|
toastNotifications.addDanger({
|
||||||
|
title: i18n.translate(
|
||||||
|
'xpack.transform.deleteTransform.deleteAnalyticsWithIndexErrorMessage',
|
||||||
|
{
|
||||||
|
defaultMessage: 'An error occurred deleting destination index {destinationIndex}',
|
||||||
|
values: { destinationIndex },
|
||||||
|
}
|
||||||
|
),
|
||||||
|
text: toMountPoint(
|
||||||
|
<ToastNotificationText previewTextLength={50} overlays={overlays} text={error} />
|
||||||
|
),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
if (status.destIndexPatternDeleted?.error) {
|
||||||
|
const error = extractErrorMessage(status.destIndexPatternDeleted.error);
|
||||||
|
toastNotifications.addDanger({
|
||||||
|
title: i18n.translate(
|
||||||
|
'xpack.transform.deleteTransform.deleteAnalyticsWithIndexPatternErrorMessage',
|
||||||
|
{
|
||||||
|
defaultMessage: 'An error occurred deleting index pattern {destinationIndex}',
|
||||||
|
values: { destinationIndex },
|
||||||
|
}
|
||||||
|
),
|
||||||
|
text: toMountPoint(
|
||||||
|
<ToastNotificationText previewTextLength={50} overlays={overlays} text={error} />
|
||||||
|
),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// if we are deleting multiple transforms, combine the success messages
|
||||||
|
if (isBulk) {
|
||||||
|
if (successCount.transformDeleted > 0) {
|
||||||
|
toastNotifications.addSuccess(
|
||||||
|
i18n.translate('xpack.transform.transformList.bulkDeleteTransformSuccessMessage', {
|
||||||
|
defaultMessage:
|
||||||
|
'Successfully deleted {count} {count, plural, one {transform} other {transforms}}.',
|
||||||
|
values: { count: successCount.transformDeleted },
|
||||||
|
})
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (successCount.destIndexDeleted > 0) {
|
||||||
|
toastNotifications.addSuccess(
|
||||||
|
i18n.translate('xpack.transform.transformList.bulkDeleteDestIndexSuccessMessage', {
|
||||||
|
defaultMessage:
|
||||||
|
'Successfully deleted {count} destination {count, plural, one {index} other {indices}}.',
|
||||||
|
values: { count: successCount.destIndexDeleted },
|
||||||
|
})
|
||||||
|
);
|
||||||
|
}
|
||||||
|
if (successCount.destIndexPatternDeleted > 0) {
|
||||||
|
toastNotifications.addSuccess(
|
||||||
|
i18n.translate('xpack.transform.transformList.bulkDeleteDestIndexPatternSuccessMessage', {
|
||||||
|
defaultMessage:
|
||||||
|
'Successfully deleted {count} destination index {count, plural, one {pattern} other {patterns}}.',
|
||||||
|
values: { count: successCount.destIndexPatternDeleted },
|
||||||
|
})
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
refreshTransformList$.next(REFRESH_TRANSFORM_LIST_STATE.REFRESH);
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
|
@ -4,52 +4,24 @@
|
||||||
* you may not use this file except in compliance with the Elastic License.
|
* you may not use this file except in compliance with the Elastic License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
import { HttpFetchError } from 'src/core/public';
|
||||||
|
|
||||||
import {
|
import {
|
||||||
TransformListRow,
|
isGetTransformsResponseSchema,
|
||||||
TransformStats,
|
isGetTransformsStatsResponseSchema,
|
||||||
TRANSFORM_MODE,
|
} from '../../../common/api_schemas/type_guards';
|
||||||
isTransformStats,
|
import { TRANSFORM_MODE } from '../../../common/constants';
|
||||||
TransformPivotConfig,
|
import { isTransformStats } from '../../../common/types/transform_stats';
|
||||||
refreshTransformList$,
|
|
||||||
REFRESH_TRANSFORM_LIST_STATE,
|
import { TransformListRow, refreshTransformList$, REFRESH_TRANSFORM_LIST_STATE } from '../common';
|
||||||
} from '../common';
|
|
||||||
|
|
||||||
import { useApi } from './use_api';
|
import { useApi } from './use_api';
|
||||||
|
|
||||||
interface GetTransformsResponse {
|
|
||||||
count: number;
|
|
||||||
transforms: TransformPivotConfig[];
|
|
||||||
}
|
|
||||||
|
|
||||||
interface GetTransformsStatsResponseOk {
|
|
||||||
node_failures?: object;
|
|
||||||
count: number;
|
|
||||||
transforms: TransformStats[];
|
|
||||||
}
|
|
||||||
|
|
||||||
const isGetTransformsStatsResponseOk = (arg: any): arg is GetTransformsStatsResponseOk => {
|
|
||||||
return (
|
|
||||||
{}.hasOwnProperty.call(arg, 'count') &&
|
|
||||||
{}.hasOwnProperty.call(arg, 'transforms') &&
|
|
||||||
Array.isArray(arg.transforms)
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
interface GetTransformsStatsResponseError {
|
|
||||||
statusCode: number;
|
|
||||||
error: string;
|
|
||||||
message: string;
|
|
||||||
}
|
|
||||||
|
|
||||||
type GetTransformsStatsResponse = GetTransformsStatsResponseOk | GetTransformsStatsResponseError;
|
|
||||||
|
|
||||||
export type GetTransforms = (forceRefresh?: boolean) => void;
|
export type GetTransforms = (forceRefresh?: boolean) => void;
|
||||||
|
|
||||||
export const useGetTransforms = (
|
export const useGetTransforms = (
|
||||||
setTransforms: React.Dispatch<React.SetStateAction<TransformListRow[]>>,
|
setTransforms: React.Dispatch<React.SetStateAction<TransformListRow[]>>,
|
||||||
setErrorMessage: React.Dispatch<
|
setErrorMessage: React.Dispatch<React.SetStateAction<HttpFetchError | undefined>>,
|
||||||
React.SetStateAction<GetTransformsStatsResponseError | undefined>
|
|
||||||
>,
|
|
||||||
setIsInitialized: React.Dispatch<React.SetStateAction<boolean>>,
|
setIsInitialized: React.Dispatch<React.SetStateAction<boolean>>,
|
||||||
blockRefresh: boolean
|
blockRefresh: boolean
|
||||||
): GetTransforms => {
|
): GetTransforms => {
|
||||||
|
@ -66,45 +38,57 @@ export const useGetTransforms = (
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
try {
|
const transformConfigs = await api.getTransforms();
|
||||||
const transformConfigs: GetTransformsResponse = await api.getTransforms();
|
const transformStats = await api.getTransformsStats();
|
||||||
const transformStats: GetTransformsStatsResponse = await api.getTransformsStats();
|
|
||||||
|
|
||||||
const tableRows = transformConfigs.transforms.reduce((reducedtableRows, config) => {
|
if (
|
||||||
const stats = isGetTransformsStatsResponseOk(transformStats)
|
!isGetTransformsResponseSchema(transformConfigs) ||
|
||||||
? transformStats.transforms.find((d) => config.id === d.id)
|
!isGetTransformsStatsResponseSchema(transformStats)
|
||||||
: undefined;
|
) {
|
||||||
|
|
||||||
// A newly created transform might not have corresponding stats yet.
|
|
||||||
// If that's the case we just skip the transform and don't add it to the transform list yet.
|
|
||||||
if (!isTransformStats(stats)) {
|
|
||||||
return reducedtableRows;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Table with expandable rows requires `id` on the outer most level
|
|
||||||
reducedtableRows.push({
|
|
||||||
id: config.id,
|
|
||||||
config,
|
|
||||||
mode:
|
|
||||||
typeof config.sync !== 'undefined' ? TRANSFORM_MODE.CONTINUOUS : TRANSFORM_MODE.BATCH,
|
|
||||||
stats,
|
|
||||||
});
|
|
||||||
return reducedtableRows;
|
|
||||||
}, [] as TransformListRow[]);
|
|
||||||
|
|
||||||
setTransforms(tableRows);
|
|
||||||
setErrorMessage(undefined);
|
|
||||||
setIsInitialized(true);
|
|
||||||
refreshTransformList$.next(REFRESH_TRANSFORM_LIST_STATE.IDLE);
|
|
||||||
} catch (e) {
|
|
||||||
// An error is followed immediately by setting the state to idle.
|
// An error is followed immediately by setting the state to idle.
|
||||||
// This way we're able to treat ERROR as a one-time-event like REFRESH.
|
// This way we're able to treat ERROR as a one-time-event like REFRESH.
|
||||||
refreshTransformList$.next(REFRESH_TRANSFORM_LIST_STATE.ERROR);
|
refreshTransformList$.next(REFRESH_TRANSFORM_LIST_STATE.ERROR);
|
||||||
refreshTransformList$.next(REFRESH_TRANSFORM_LIST_STATE.IDLE);
|
refreshTransformList$.next(REFRESH_TRANSFORM_LIST_STATE.IDLE);
|
||||||
setTransforms([]);
|
setTransforms([]);
|
||||||
setErrorMessage(e);
|
|
||||||
setIsInitialized(true);
|
setIsInitialized(true);
|
||||||
|
|
||||||
|
if (!isGetTransformsResponseSchema(transformConfigs)) {
|
||||||
|
setErrorMessage(transformConfigs);
|
||||||
|
} else if (!isGetTransformsStatsResponseSchema(transformStats)) {
|
||||||
|
setErrorMessage(transformStats);
|
||||||
|
}
|
||||||
|
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const tableRows = transformConfigs.transforms.reduce((reducedtableRows, config) => {
|
||||||
|
const stats = isGetTransformsStatsResponseSchema(transformStats)
|
||||||
|
? transformStats.transforms.find((d) => config.id === d.id)
|
||||||
|
: undefined;
|
||||||
|
|
||||||
|
// A newly created transform might not have corresponding stats yet.
|
||||||
|
// If that's the case we just skip the transform and don't add it to the transform list yet.
|
||||||
|
if (!isTransformStats(stats)) {
|
||||||
|
return reducedtableRows;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Table with expandable rows requires `id` on the outer most level
|
||||||
|
reducedtableRows.push({
|
||||||
|
id: config.id,
|
||||||
|
config,
|
||||||
|
mode:
|
||||||
|
typeof config.sync !== 'undefined' ? TRANSFORM_MODE.CONTINUOUS : TRANSFORM_MODE.BATCH,
|
||||||
|
stats,
|
||||||
|
});
|
||||||
|
return reducedtableRows;
|
||||||
|
}, [] as TransformListRow[]);
|
||||||
|
|
||||||
|
setTransforms(tableRows);
|
||||||
|
setErrorMessage(undefined);
|
||||||
|
setIsInitialized(true);
|
||||||
|
refreshTransformList$.next(REFRESH_TRANSFORM_LIST_STATE.IDLE);
|
||||||
|
|
||||||
concurrentLoads--;
|
concurrentLoads--;
|
||||||
|
|
||||||
if (concurrentLoads > 0) {
|
if (concurrentLoads > 0) {
|
||||||
|
|
|
@ -8,6 +8,11 @@ import { useEffect } from 'react';
|
||||||
|
|
||||||
import { EuiDataGridColumn } from '@elastic/eui';
|
import { EuiDataGridColumn } from '@elastic/eui';
|
||||||
|
|
||||||
|
import {
|
||||||
|
isEsSearchResponse,
|
||||||
|
isFieldHistogramsResponseSchema,
|
||||||
|
} from '../../../common/api_schemas/type_guards';
|
||||||
|
|
||||||
import {
|
import {
|
||||||
getFieldType,
|
getFieldType,
|
||||||
getDataGridSchemaFromKibanaFieldType,
|
getDataGridSchemaFromKibanaFieldType,
|
||||||
|
@ -16,7 +21,6 @@ import {
|
||||||
useDataGrid,
|
useDataGrid,
|
||||||
useRenderCellValue,
|
useRenderCellValue,
|
||||||
EsSorting,
|
EsSorting,
|
||||||
SearchResponse7,
|
|
||||||
UseIndexDataReturnType,
|
UseIndexDataReturnType,
|
||||||
INDEX_STATUS,
|
INDEX_STATUS,
|
||||||
} from '../../shared_imports';
|
} from '../../shared_imports';
|
||||||
|
@ -29,8 +33,6 @@ import { useApi } from './use_api';
|
||||||
|
|
||||||
import { useToastNotifications } from '../app_dependencies';
|
import { useToastNotifications } from '../app_dependencies';
|
||||||
|
|
||||||
type IndexSearchResponse = SearchResponse7;
|
|
||||||
|
|
||||||
export const useIndexData = (
|
export const useIndexData = (
|
||||||
indexPattern: SearchItems['indexPattern'],
|
indexPattern: SearchItems['indexPattern'],
|
||||||
query: PivotQuery
|
query: PivotQuery
|
||||||
|
@ -90,37 +92,39 @@ export const useIndexData = (
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
try {
|
const resp = await api.esSearch(esSearchRequest);
|
||||||
const resp: IndexSearchResponse = await api.esSearch(esSearchRequest);
|
|
||||||
|
|
||||||
const docs = resp.hits.hits.map((d) => d._source);
|
if (!isEsSearchResponse(resp)) {
|
||||||
|
setErrorMessage(getErrorMessage(resp));
|
||||||
setRowCount(resp.hits.total.value);
|
|
||||||
setTableItems(docs);
|
|
||||||
setStatus(INDEX_STATUS.LOADED);
|
|
||||||
} catch (e) {
|
|
||||||
setErrorMessage(getErrorMessage(e));
|
|
||||||
setStatus(INDEX_STATUS.ERROR);
|
setStatus(INDEX_STATUS.ERROR);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const docs = resp.hits.hits.map((d) => d._source);
|
||||||
|
|
||||||
|
setRowCount(resp.hits.total.value);
|
||||||
|
setTableItems(docs);
|
||||||
|
setStatus(INDEX_STATUS.LOADED);
|
||||||
};
|
};
|
||||||
|
|
||||||
const fetchColumnChartsData = async function () {
|
const fetchColumnChartsData = async function () {
|
||||||
try {
|
const columnChartsData = await api.getHistogramsForFields(
|
||||||
const columnChartsData = await api.getHistogramsForFields(
|
indexPattern.title,
|
||||||
indexPattern.title,
|
columns
|
||||||
columns
|
.filter((cT) => dataGrid.visibleColumns.includes(cT.id))
|
||||||
.filter((cT) => dataGrid.visibleColumns.includes(cT.id))
|
.map((cT) => ({
|
||||||
.map((cT) => ({
|
fieldName: cT.id,
|
||||||
fieldName: cT.id,
|
type: getFieldType(cT.schema),
|
||||||
type: getFieldType(cT.schema),
|
})),
|
||||||
})),
|
isDefaultQuery(query) ? matchAllQuery : query
|
||||||
isDefaultQuery(query) ? matchAllQuery : query
|
);
|
||||||
);
|
|
||||||
setColumnCharts(columnChartsData);
|
if (!isFieldHistogramsResponseSchema(columnChartsData)) {
|
||||||
} catch (e) {
|
showDataGridColumnChartErrorMessageToast(columnChartsData, toastNotifications);
|
||||||
showDataGridColumnChartErrorMessageToast(e, toastNotifications);
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
setColumnCharts(columnChartsData);
|
||||||
};
|
};
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
|
|
|
@ -13,11 +13,13 @@ import { i18n } from '@kbn/i18n';
|
||||||
|
|
||||||
import { ES_FIELD_TYPES } from '../../../../../../src/plugins/data/common';
|
import { ES_FIELD_TYPES } from '../../../../../../src/plugins/data/common';
|
||||||
|
|
||||||
|
import type { PreviewMappingsProperties } from '../../../common/api_schemas/transforms';
|
||||||
|
import { isPostTransformsPreviewResponseSchema } from '../../../common/api_schemas/type_guards';
|
||||||
import { dictionaryToArray } from '../../../common/types/common';
|
import { dictionaryToArray } from '../../../common/types/common';
|
||||||
import { formatHumanReadableDateTimeSeconds } from '../../shared_imports';
|
|
||||||
import { getNestedProperty } from '../../../common/utils/object_utils';
|
import { getNestedProperty } from '../../../common/utils/object_utils';
|
||||||
|
|
||||||
import {
|
import {
|
||||||
|
formatHumanReadableDateTimeSeconds,
|
||||||
multiColumnSortFactory,
|
multiColumnSortFactory,
|
||||||
useDataGrid,
|
useDataGrid,
|
||||||
RenderCellValue,
|
RenderCellValue,
|
||||||
|
@ -27,12 +29,11 @@ import {
|
||||||
import { getErrorMessage } from '../../../common/utils/errors';
|
import { getErrorMessage } from '../../../common/utils/errors';
|
||||||
|
|
||||||
import {
|
import {
|
||||||
getPreviewRequestBody,
|
getPreviewTransformRequestBody,
|
||||||
PivotAggsConfigDict,
|
PivotAggsConfigDict,
|
||||||
PivotGroupByConfigDict,
|
PivotGroupByConfigDict,
|
||||||
PivotGroupByConfig,
|
PivotGroupByConfig,
|
||||||
PivotQuery,
|
PivotQuery,
|
||||||
PreviewMappings,
|
|
||||||
PivotAggsConfig,
|
PivotAggsConfig,
|
||||||
} from '../common';
|
} from '../common';
|
||||||
|
|
||||||
|
@ -74,21 +75,23 @@ export const usePivotData = (
|
||||||
aggs: PivotAggsConfigDict,
|
aggs: PivotAggsConfigDict,
|
||||||
groupBy: PivotGroupByConfigDict
|
groupBy: PivotGroupByConfigDict
|
||||||
): UseIndexDataReturnType => {
|
): UseIndexDataReturnType => {
|
||||||
const [previewMappings, setPreviewMappings] = useState<PreviewMappings>({ properties: {} });
|
const [previewMappingsProperties, setPreviewMappingsProperties] = useState<
|
||||||
|
PreviewMappingsProperties
|
||||||
|
>({});
|
||||||
const api = useApi();
|
const api = useApi();
|
||||||
|
|
||||||
const aggsArr = useMemo(() => dictionaryToArray(aggs), [aggs]);
|
const aggsArr = useMemo(() => dictionaryToArray(aggs), [aggs]);
|
||||||
const groupByArr = useMemo(() => dictionaryToArray(groupBy), [groupBy]);
|
const groupByArr = useMemo(() => dictionaryToArray(groupBy), [groupBy]);
|
||||||
|
|
||||||
// Filters mapping properties of type `object`, which get returned for nested field parents.
|
// Filters mapping properties of type `object`, which get returned for nested field parents.
|
||||||
const columnKeys = Object.keys(previewMappings.properties).filter(
|
const columnKeys = Object.keys(previewMappingsProperties).filter(
|
||||||
(key) => previewMappings.properties[key].type !== 'object'
|
(key) => previewMappingsProperties[key].type !== 'object'
|
||||||
);
|
);
|
||||||
columnKeys.sort(sortColumns(groupByArr));
|
columnKeys.sort(sortColumns(groupByArr));
|
||||||
|
|
||||||
// EuiDataGrid State
|
// EuiDataGrid State
|
||||||
const columns: EuiDataGridColumn[] = columnKeys.map((id) => {
|
const columns: EuiDataGridColumn[] = columnKeys.map((id) => {
|
||||||
const field = previewMappings.properties[id];
|
const field = previewMappingsProperties[id];
|
||||||
|
|
||||||
// Built-in values are ['boolean', 'currency', 'datetime', 'numeric', 'json']
|
// Built-in values are ['boolean', 'currency', 'datetime', 'numeric', 'json']
|
||||||
// To fall back to the default string schema it needs to be undefined.
|
// To fall back to the default string schema it needs to be undefined.
|
||||||
|
@ -159,28 +162,35 @@ export const usePivotData = (
|
||||||
setNoDataMessage('');
|
setNoDataMessage('');
|
||||||
setStatus(INDEX_STATUS.LOADING);
|
setStatus(INDEX_STATUS.LOADING);
|
||||||
|
|
||||||
try {
|
const previewRequest = getPreviewTransformRequestBody(
|
||||||
const previewRequest = getPreviewRequestBody(indexPatternTitle, query, groupByArr, aggsArr);
|
indexPatternTitle,
|
||||||
const resp = await api.getTransformsPreview(previewRequest);
|
query,
|
||||||
setTableItems(resp.preview);
|
groupByArr,
|
||||||
setRowCount(resp.preview.length);
|
aggsArr
|
||||||
setPreviewMappings(resp.generated_dest_index.mappings);
|
);
|
||||||
setStatus(INDEX_STATUS.LOADED);
|
const resp = await api.getTransformsPreview(previewRequest);
|
||||||
|
|
||||||
if (resp.preview.length === 0) {
|
if (!isPostTransformsPreviewResponseSchema(resp)) {
|
||||||
setNoDataMessage(
|
setErrorMessage(getErrorMessage(resp));
|
||||||
i18n.translate('xpack.transform.pivotPreview.PivotPreviewNoDataCalloutBody', {
|
|
||||||
defaultMessage:
|
|
||||||
'The preview request did not return any data. Please ensure the optional query returns data and that values exist for the field used by group-by and aggregation fields.',
|
|
||||||
})
|
|
||||||
);
|
|
||||||
}
|
|
||||||
} catch (e) {
|
|
||||||
setErrorMessage(getErrorMessage(e));
|
|
||||||
setTableItems([]);
|
setTableItems([]);
|
||||||
setRowCount(0);
|
setRowCount(0);
|
||||||
setPreviewMappings({ properties: {} });
|
setPreviewMappingsProperties({});
|
||||||
setStatus(INDEX_STATUS.ERROR);
|
setStatus(INDEX_STATUS.ERROR);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
setTableItems(resp.preview);
|
||||||
|
setRowCount(resp.preview.length);
|
||||||
|
setPreviewMappingsProperties(resp.generated_dest_index.mappings.properties);
|
||||||
|
setStatus(INDEX_STATUS.LOADED);
|
||||||
|
|
||||||
|
if (resp.preview.length === 0) {
|
||||||
|
setNoDataMessage(
|
||||||
|
i18n.translate('xpack.transform.pivotPreview.PivotPreviewNoDataCalloutBody', {
|
||||||
|
defaultMessage:
|
||||||
|
'The preview request did not return any data. Please ensure the optional query returns data and that values exist for the field used by group-by and aggregation fields.',
|
||||||
|
})
|
||||||
|
);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -236,19 +246,19 @@ export const usePivotData = (
|
||||||
|
|
||||||
if (
|
if (
|
||||||
[ES_FIELD_TYPES.DATE, ES_FIELD_TYPES.DATE_NANOS].includes(
|
[ES_FIELD_TYPES.DATE, ES_FIELD_TYPES.DATE_NANOS].includes(
|
||||||
previewMappings.properties[columnId].type
|
previewMappingsProperties[columnId].type
|
||||||
)
|
)
|
||||||
) {
|
) {
|
||||||
return formatHumanReadableDateTimeSeconds(moment(cellValue).unix() * 1000);
|
return formatHumanReadableDateTimeSeconds(moment(cellValue).unix() * 1000);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (previewMappings.properties[columnId].type === ES_FIELD_TYPES.BOOLEAN) {
|
if (previewMappingsProperties[columnId].type === ES_FIELD_TYPES.BOOLEAN) {
|
||||||
return cellValue ? 'true' : 'false';
|
return cellValue ? 'true' : 'false';
|
||||||
}
|
}
|
||||||
|
|
||||||
return cellValue;
|
return cellValue;
|
||||||
};
|
};
|
||||||
}, [pageData, pagination.pageIndex, pagination.pageSize, previewMappings.properties]);
|
}, [pageData, pagination.pageIndex, pagination.pageSize, previewMappingsProperties]);
|
||||||
|
|
||||||
return {
|
return {
|
||||||
...dataGrid,
|
...dataGrid,
|
||||||
|
|
|
@ -4,25 +4,45 @@
|
||||||
* you may not use this file except in compliance with the Elastic License.
|
* you may not use this file except in compliance with the Elastic License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
import React from 'react';
|
||||||
|
|
||||||
import { i18n } from '@kbn/i18n';
|
import { i18n } from '@kbn/i18n';
|
||||||
|
|
||||||
import { TransformEndpointRequest, TransformEndpointResult } from '../../../common';
|
import { toMountPoint } from '../../../../../../src/plugins/kibana_react/public';
|
||||||
|
|
||||||
import { useToastNotifications } from '../app_dependencies';
|
import type { StartTransformsRequestSchema } from '../../../common/api_schemas/start_transforms';
|
||||||
import { TransformListRow, refreshTransformList$, REFRESH_TRANSFORM_LIST_STATE } from '../common';
|
import { isStartTransformsResponseSchema } from '../../../common/api_schemas/type_guards';
|
||||||
|
|
||||||
|
import { getErrorMessage } from '../../../common/utils/errors';
|
||||||
|
|
||||||
|
import { useAppDependencies, useToastNotifications } from '../app_dependencies';
|
||||||
|
import { refreshTransformList$, REFRESH_TRANSFORM_LIST_STATE } from '../common';
|
||||||
|
import { ToastNotificationText } from '../components';
|
||||||
|
|
||||||
import { useApi } from './use_api';
|
import { useApi } from './use_api';
|
||||||
|
|
||||||
export const useStartTransforms = () => {
|
export const useStartTransforms = () => {
|
||||||
|
const deps = useAppDependencies();
|
||||||
const toastNotifications = useToastNotifications();
|
const toastNotifications = useToastNotifications();
|
||||||
const api = useApi();
|
const api = useApi();
|
||||||
|
|
||||||
return async (transforms: TransformListRow[]) => {
|
return async (transformsInfo: StartTransformsRequestSchema) => {
|
||||||
const transformsInfo: TransformEndpointRequest[] = transforms.map((tf) => ({
|
const results = await api.startTransforms(transformsInfo);
|
||||||
id: tf.config.id,
|
|
||||||
state: tf.stats.state,
|
if (!isStartTransformsResponseSchema(results)) {
|
||||||
}));
|
toastNotifications.addDanger({
|
||||||
const results: TransformEndpointResult = await api.startTransforms(transformsInfo);
|
title: i18n.translate(
|
||||||
|
'xpack.transform.stepCreateForm.startTransformResponseSchemaErrorMessage',
|
||||||
|
{
|
||||||
|
defaultMessage: 'An error occurred calling the start transforms request.',
|
||||||
|
}
|
||||||
|
),
|
||||||
|
text: toMountPoint(
|
||||||
|
<ToastNotificationText overlays={deps.overlays} text={getErrorMessage(results)} />
|
||||||
|
),
|
||||||
|
});
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
for (const transformId in results) {
|
for (const transformId in results) {
|
||||||
// hasOwnProperty check to ensure only properties on object itself, and not its prototypes
|
// hasOwnProperty check to ensure only properties on object itself, and not its prototypes
|
|
@ -4,25 +4,45 @@
|
||||||
* you may not use this file except in compliance with the Elastic License.
|
* you may not use this file except in compliance with the Elastic License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
import React from 'react';
|
||||||
|
|
||||||
import { i18n } from '@kbn/i18n';
|
import { i18n } from '@kbn/i18n';
|
||||||
|
|
||||||
import { TransformEndpointRequest, TransformEndpointResult } from '../../../common';
|
import { toMountPoint } from '../../../../../../src/plugins/kibana_react/public';
|
||||||
|
|
||||||
import { useToastNotifications } from '../app_dependencies';
|
import type { StopTransformsRequestSchema } from '../../../common/api_schemas/stop_transforms';
|
||||||
import { TransformListRow, refreshTransformList$, REFRESH_TRANSFORM_LIST_STATE } from '../common';
|
import { isStopTransformsResponseSchema } from '../../../common/api_schemas/type_guards';
|
||||||
|
|
||||||
|
import { getErrorMessage } from '../../../common/utils/errors';
|
||||||
|
|
||||||
|
import { useAppDependencies, useToastNotifications } from '../app_dependencies';
|
||||||
|
import { refreshTransformList$, REFRESH_TRANSFORM_LIST_STATE } from '../common';
|
||||||
|
import { ToastNotificationText } from '../components';
|
||||||
|
|
||||||
import { useApi } from './use_api';
|
import { useApi } from './use_api';
|
||||||
|
|
||||||
export const useStopTransforms = () => {
|
export const useStopTransforms = () => {
|
||||||
|
const deps = useAppDependencies();
|
||||||
const toastNotifications = useToastNotifications();
|
const toastNotifications = useToastNotifications();
|
||||||
const api = useApi();
|
const api = useApi();
|
||||||
|
|
||||||
return async (transforms: TransformListRow[]) => {
|
return async (transformsInfo: StopTransformsRequestSchema) => {
|
||||||
const transformsInfo: TransformEndpointRequest[] = transforms.map((df) => ({
|
const results = await api.stopTransforms(transformsInfo);
|
||||||
id: df.config.id,
|
|
||||||
state: df.stats.state,
|
if (!isStopTransformsResponseSchema(results)) {
|
||||||
}));
|
toastNotifications.addDanger({
|
||||||
const results: TransformEndpointResult = await api.stopTransforms(transformsInfo);
|
title: i18n.translate(
|
||||||
|
'xpack.transform.transformList.stopTransformResponseSchemaErrorMessage',
|
||||||
|
{
|
||||||
|
defaultMessage: 'An error occurred called the stop transforms request.',
|
||||||
|
}
|
||||||
|
),
|
||||||
|
text: toMountPoint(
|
||||||
|
<ToastNotificationText overlays={deps.overlays} text={getErrorMessage(results)} />
|
||||||
|
),
|
||||||
|
});
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
for (const transformId in results) {
|
for (const transformId in results) {
|
||||||
// hasOwnProperty check to ensure only properties on object itself, and not its prototypes
|
// hasOwnProperty check to ensure only properties on object itself, and not its prototypes
|
|
@ -6,7 +6,7 @@
|
||||||
|
|
||||||
import React, { createContext } from 'react';
|
import React, { createContext } from 'react';
|
||||||
|
|
||||||
import { Privileges } from '../../../../../common';
|
import { Privileges } from '../../../../../common/types/privileges';
|
||||||
|
|
||||||
import { useRequest } from '../../../hooks';
|
import { useRequest } from '../../../hooks';
|
||||||
|
|
||||||
|
|
|
@ -6,7 +6,7 @@
|
||||||
|
|
||||||
import { i18n } from '@kbn/i18n';
|
import { i18n } from '@kbn/i18n';
|
||||||
|
|
||||||
import { Privileges } from '../../../../../common';
|
import { Privileges } from '../../../../../common/types/privileges';
|
||||||
|
|
||||||
export interface Capabilities {
|
export interface Capabilities {
|
||||||
canGetTransform: boolean;
|
canGetTransform: boolean;
|
||||||
|
|
|
@ -10,7 +10,7 @@ import { EuiPageContent } from '@elastic/eui';
|
||||||
|
|
||||||
import { FormattedMessage } from '@kbn/i18n/react';
|
import { FormattedMessage } from '@kbn/i18n/react';
|
||||||
|
|
||||||
import { MissingPrivileges } from '../../../../../common';
|
import { MissingPrivileges } from '../../../../../common/types/privileges';
|
||||||
|
|
||||||
import { SectionLoading } from '../../../components';
|
import { SectionLoading } from '../../../components';
|
||||||
|
|
||||||
|
|
|
@ -21,40 +21,20 @@ import {
|
||||||
EuiTitle,
|
EuiTitle,
|
||||||
} from '@elastic/eui';
|
} from '@elastic/eui';
|
||||||
|
|
||||||
|
import { APP_CREATE_TRANSFORM_CLUSTER_PRIVILEGES } from '../../../../common/constants';
|
||||||
|
import { TransformPivotConfig } from '../../../../common/types/transform';
|
||||||
|
|
||||||
|
import { isHttpFetchError } from '../../common/request';
|
||||||
import { useApi } from '../../hooks/use_api';
|
import { useApi } from '../../hooks/use_api';
|
||||||
import { useDocumentationLinks } from '../../hooks/use_documentation_links';
|
import { useDocumentationLinks } from '../../hooks/use_documentation_links';
|
||||||
import { useSearchItems } from '../../hooks/use_search_items';
|
import { useSearchItems } from '../../hooks/use_search_items';
|
||||||
|
|
||||||
import { APP_CREATE_TRANSFORM_CLUSTER_PRIVILEGES } from '../../../../common/constants';
|
|
||||||
|
|
||||||
import { useAppDependencies } from '../../app_dependencies';
|
import { useAppDependencies } from '../../app_dependencies';
|
||||||
import { TransformPivotConfig } from '../../common';
|
|
||||||
import { breadcrumbService, docTitleService, BREADCRUMB_SECTION } from '../../services/navigation';
|
import { breadcrumbService, docTitleService, BREADCRUMB_SECTION } from '../../services/navigation';
|
||||||
import { PrivilegesWrapper } from '../../lib/authorization';
|
import { PrivilegesWrapper } from '../../lib/authorization';
|
||||||
|
|
||||||
import { Wizard } from '../create_transform/components/wizard';
|
import { Wizard } from '../create_transform/components/wizard';
|
||||||
|
|
||||||
interface GetTransformsResponseOk {
|
|
||||||
count: number;
|
|
||||||
transforms: TransformPivotConfig[];
|
|
||||||
}
|
|
||||||
|
|
||||||
interface GetTransformsResponseError {
|
|
||||||
error: {
|
|
||||||
msg: string;
|
|
||||||
path: string;
|
|
||||||
query: any;
|
|
||||||
statusCode: number;
|
|
||||||
response: string;
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
function isGetTransformsResponseError(arg: any): arg is GetTransformsResponseError {
|
|
||||||
return arg.error !== undefined;
|
|
||||||
}
|
|
||||||
|
|
||||||
type GetTransformsResponse = GetTransformsResponseOk | GetTransformsResponseError;
|
|
||||||
|
|
||||||
type Props = RouteComponentProps<{ transformId: string }>;
|
type Props = RouteComponentProps<{ transformId: string }>;
|
||||||
export const CloneTransformSection: FC<Props> = ({ match }) => {
|
export const CloneTransformSection: FC<Props> = ({ match }) => {
|
||||||
// Set breadcrumb and page title
|
// Set breadcrumb and page title
|
||||||
|
@ -84,15 +64,15 @@ export const CloneTransformSection: FC<Props> = ({ match }) => {
|
||||||
} = useSearchItems(undefined);
|
} = useSearchItems(undefined);
|
||||||
|
|
||||||
const fetchTransformConfig = async () => {
|
const fetchTransformConfig = async () => {
|
||||||
try {
|
const transformConfigs = await api.getTransform(transformId);
|
||||||
const transformConfigs: GetTransformsResponse = await api.getTransforms(transformId);
|
if (isHttpFetchError(transformConfigs)) {
|
||||||
if (isGetTransformsResponseError(transformConfigs)) {
|
setTransformConfig(undefined);
|
||||||
setTransformConfig(undefined);
|
setErrorMessage(transformConfigs.message);
|
||||||
setErrorMessage(transformConfigs.error.msg);
|
setIsInitialized(true);
|
||||||
setIsInitialized(true);
|
return;
|
||||||
return;
|
}
|
||||||
}
|
|
||||||
|
|
||||||
|
try {
|
||||||
await loadIndexPatterns(savedObjectsClient, indexPatterns);
|
await loadIndexPatterns(savedObjectsClient, indexPatterns);
|
||||||
const indexPatternTitle = Array.isArray(transformConfigs.transforms[0].source.index)
|
const indexPatternTitle = Array.isArray(transformConfigs.transforms[0].source.index)
|
||||||
? transformConfigs.transforms[0].source.index.join(',')
|
? transformConfigs.transforms[0].source.index.join(',')
|
||||||
|
|
|
@ -7,7 +7,10 @@
|
||||||
import { shallow } from 'enzyme';
|
import { shallow } from 'enzyme';
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
|
|
||||||
import { AggName, PivotAggsConfig, PIVOT_SUPPORTED_AGGS } from '../../../../common';
|
import { AggName } from '../../../../../../common/types/aggregations';
|
||||||
|
import { PIVOT_SUPPORTED_AGGS } from '../../../../../../common/types/pivot_aggs';
|
||||||
|
|
||||||
|
import { PivotAggsConfig } from '../../../../common';
|
||||||
|
|
||||||
import { AggLabelForm } from './agg_label_form';
|
import { AggLabelForm } from './agg_label_form';
|
||||||
|
|
||||||
|
|
|
@ -10,8 +10,9 @@ import { i18n } from '@kbn/i18n';
|
||||||
|
|
||||||
import { EuiButtonIcon, EuiFlexGroup, EuiFlexItem, EuiPopover, EuiTextColor } from '@elastic/eui';
|
import { EuiButtonIcon, EuiFlexGroup, EuiFlexItem, EuiPopover, EuiTextColor } from '@elastic/eui';
|
||||||
|
|
||||||
|
import { AggName } from '../../../../../../common/types/aggregations';
|
||||||
|
|
||||||
import {
|
import {
|
||||||
AggName,
|
|
||||||
isPivotAggsConfigWithUiSupport,
|
isPivotAggsConfigWithUiSupport,
|
||||||
PivotAggsConfig,
|
PivotAggsConfig,
|
||||||
PivotAggsConfigWithUiSupportDict,
|
PivotAggsConfigWithUiSupportDict,
|
||||||
|
|
|
@ -7,7 +7,9 @@
|
||||||
import { shallow } from 'enzyme';
|
import { shallow } from 'enzyme';
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
|
|
||||||
import { PivotAggsConfig, PIVOT_SUPPORTED_AGGS } from '../../../../common';
|
import { PIVOT_SUPPORTED_AGGS } from '../../../../../../common/types/pivot_aggs';
|
||||||
|
|
||||||
|
import { PivotAggsConfig } from '../../../../common';
|
||||||
|
|
||||||
import { AggListForm, AggListProps } from './list_form';
|
import { AggListForm, AggListProps } from './list_form';
|
||||||
|
|
||||||
|
|
|
@ -8,8 +8,9 @@ import React, { Fragment } from 'react';
|
||||||
|
|
||||||
import { EuiPanel, EuiSpacer } from '@elastic/eui';
|
import { EuiPanel, EuiSpacer } from '@elastic/eui';
|
||||||
|
|
||||||
|
import { AggName } from '../../../../../../common/types/aggregations';
|
||||||
|
|
||||||
import {
|
import {
|
||||||
AggName,
|
|
||||||
PivotAggsConfig,
|
PivotAggsConfig,
|
||||||
PivotAggsConfigDict,
|
PivotAggsConfigDict,
|
||||||
PivotAggsConfigWithUiSupportDict,
|
PivotAggsConfigWithUiSupportDict,
|
||||||
|
|
|
@ -7,7 +7,9 @@
|
||||||
import { shallow } from 'enzyme';
|
import { shallow } from 'enzyme';
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
|
|
||||||
import { PivotAggsConfig, PIVOT_SUPPORTED_AGGS } from '../../../../common';
|
import { PIVOT_SUPPORTED_AGGS } from '../../../../../../common/types/pivot_aggs';
|
||||||
|
|
||||||
|
import { PivotAggsConfig } from '../../../../common';
|
||||||
|
|
||||||
import { AggListSummary, AggListSummaryProps } from './list_summary';
|
import { AggListSummary, AggListSummaryProps } from './list_summary';
|
||||||
|
|
||||||
|
|
|
@ -8,7 +8,9 @@ import React, { Fragment } from 'react';
|
||||||
|
|
||||||
import { EuiForm, EuiPanel, EuiSpacer } from '@elastic/eui';
|
import { EuiForm, EuiPanel, EuiSpacer } from '@elastic/eui';
|
||||||
|
|
||||||
import { AggName, PivotAggsConfigDict } from '../../../../common';
|
import { AggName } from '../../../../../../common/types/aggregations';
|
||||||
|
|
||||||
|
import { PivotAggsConfigDict } from '../../../../common';
|
||||||
|
|
||||||
export interface AggListSummaryProps {
|
export interface AggListSummaryProps {
|
||||||
list: PivotAggsConfigDict;
|
list: PivotAggsConfigDict;
|
||||||
|
|
|
@ -7,7 +7,10 @@
|
||||||
import { shallow } from 'enzyme';
|
import { shallow } from 'enzyme';
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
|
|
||||||
import { AggName, PIVOT_SUPPORTED_AGGS, PivotAggsConfig } from '../../../../common';
|
import { AggName } from '../../../../../../common/types/aggregations';
|
||||||
|
import { PIVOT_SUPPORTED_AGGS } from '../../../../../../common/types/pivot_aggs';
|
||||||
|
|
||||||
|
import { PivotAggsConfig } from '../../../../common';
|
||||||
|
|
||||||
import { PopoverForm } from './popover_form';
|
import { PopoverForm } from './popover_form';
|
||||||
|
|
||||||
|
|
|
@ -20,10 +20,14 @@ import {
|
||||||
|
|
||||||
import { cloneDeep } from 'lodash';
|
import { cloneDeep } from 'lodash';
|
||||||
import { useUpdateEffect } from 'react-use';
|
import { useUpdateEffect } from 'react-use';
|
||||||
|
import { AggName } from '../../../../../../common/types/aggregations';
|
||||||
import { dictionaryToArray } from '../../../../../../common/types/common';
|
import { dictionaryToArray } from '../../../../../../common/types/common';
|
||||||
|
import {
|
||||||
|
PivotSupportedAggs,
|
||||||
|
PIVOT_SUPPORTED_AGGS,
|
||||||
|
} from '../../../../../../common/types/pivot_aggs';
|
||||||
|
|
||||||
import {
|
import {
|
||||||
AggName,
|
|
||||||
isAggName,
|
isAggName,
|
||||||
isPivotAggsConfigPercentiles,
|
isPivotAggsConfigPercentiles,
|
||||||
isPivotAggsConfigWithUiSupport,
|
isPivotAggsConfigWithUiSupport,
|
||||||
|
@ -31,9 +35,8 @@ import {
|
||||||
PERCENTILES_AGG_DEFAULT_PERCENTS,
|
PERCENTILES_AGG_DEFAULT_PERCENTS,
|
||||||
PivotAggsConfig,
|
PivotAggsConfig,
|
||||||
PivotAggsConfigWithUiSupportDict,
|
PivotAggsConfigWithUiSupportDict,
|
||||||
PIVOT_SUPPORTED_AGGS,
|
|
||||||
} from '../../../../common';
|
} from '../../../../common';
|
||||||
import { isPivotAggsWithExtendedForm, PivotSupportedAggs } from '../../../../common/pivot_aggs';
|
import { isPivotAggsWithExtendedForm } from '../../../../common/pivot_aggs';
|
||||||
import { getAggFormConfig } from '../step_define/common/get_agg_form_config';
|
import { getAggFormConfig } from '../step_define/common/get_agg_form_config';
|
||||||
|
|
||||||
interface Props {
|
interface Props {
|
||||||
|
|
|
@ -10,8 +10,9 @@ import { i18n } from '@kbn/i18n';
|
||||||
|
|
||||||
import { EuiButtonIcon, EuiFlexGroup, EuiFlexItem, EuiPopover, EuiTextColor } from '@elastic/eui';
|
import { EuiButtonIcon, EuiFlexGroup, EuiFlexItem, EuiPopover, EuiTextColor } from '@elastic/eui';
|
||||||
|
|
||||||
|
import { AggName } from '../../../../../../common/types/aggregations';
|
||||||
|
|
||||||
import {
|
import {
|
||||||
AggName,
|
|
||||||
isGroupByDateHistogram,
|
isGroupByDateHistogram,
|
||||||
isGroupByHistogram,
|
isGroupByHistogram,
|
||||||
PivotGroupByConfig,
|
PivotGroupByConfig,
|
||||||
|
|
|
@ -8,8 +8,9 @@ import React, { Fragment } from 'react';
|
||||||
|
|
||||||
import { EuiPanel, EuiSpacer } from '@elastic/eui';
|
import { EuiPanel, EuiSpacer } from '@elastic/eui';
|
||||||
|
|
||||||
|
import { AggName } from '../../../../../../common/types/aggregations';
|
||||||
|
|
||||||
import {
|
import {
|
||||||
AggName,
|
|
||||||
PivotGroupByConfig,
|
PivotGroupByConfig,
|
||||||
PivotGroupByConfigDict,
|
PivotGroupByConfigDict,
|
||||||
PivotGroupByConfigWithUiSupportDict,
|
PivotGroupByConfigWithUiSupportDict,
|
||||||
|
|
|
@ -7,7 +7,9 @@
|
||||||
import { shallow } from 'enzyme';
|
import { shallow } from 'enzyme';
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
|
|
||||||
import { AggName, PIVOT_SUPPORTED_GROUP_BY_AGGS, PivotGroupByConfig } from '../../../../common';
|
import { AggName } from '../../../../../../common/types/aggregations';
|
||||||
|
|
||||||
|
import { PIVOT_SUPPORTED_GROUP_BY_AGGS, PivotGroupByConfig } from '../../../../common';
|
||||||
|
|
||||||
import { isIntervalValid, PopoverForm } from './popover_form';
|
import { isIntervalValid, PopoverForm } from './popover_form';
|
||||||
|
|
||||||
|
|
|
@ -18,10 +18,10 @@ import {
|
||||||
EuiSpacer,
|
EuiSpacer,
|
||||||
} from '@elastic/eui';
|
} from '@elastic/eui';
|
||||||
|
|
||||||
|
import { AggName } from '../../../../../../common/types/aggregations';
|
||||||
import { dictionaryToArray } from '../../../../../../common/types/common';
|
import { dictionaryToArray } from '../../../../../../common/types/common';
|
||||||
|
|
||||||
import {
|
import {
|
||||||
AggName,
|
|
||||||
dateHistogramIntervalFormatRegex,
|
dateHistogramIntervalFormatRegex,
|
||||||
getEsAggFromGroupByConfig,
|
getEsAggFromGroupByConfig,
|
||||||
isGroupByDateHistogram,
|
isGroupByDateHistogram,
|
||||||
|
|
|
@ -30,6 +30,12 @@ import {
|
||||||
|
|
||||||
import { toMountPoint } from '../../../../../../../../../src/plugins/kibana_react/public';
|
import { toMountPoint } from '../../../../../../../../../src/plugins/kibana_react/public';
|
||||||
|
|
||||||
|
import type { PutTransformsResponseSchema } from '../../../../../../common/api_schemas/transforms';
|
||||||
|
import {
|
||||||
|
isGetTransformsStatsResponseSchema,
|
||||||
|
isPutTransformsResponseSchema,
|
||||||
|
isStartTransformsResponseSchema,
|
||||||
|
} from '../../../../../../common/api_schemas/type_guards';
|
||||||
import { PROGRESS_REFRESH_INTERVAL_MS } from '../../../../../../common/constants';
|
import { PROGRESS_REFRESH_INTERVAL_MS } from '../../../../../../common/constants';
|
||||||
|
|
||||||
import { getErrorMessage } from '../../../../../../common/utils/errors';
|
import { getErrorMessage } from '../../../../../../common/utils/errors';
|
||||||
|
@ -93,34 +99,28 @@ export const StepCreateForm: FC<Props> = React.memo(
|
||||||
async function createTransform() {
|
async function createTransform() {
|
||||||
setLoading(true);
|
setLoading(true);
|
||||||
|
|
||||||
try {
|
const resp = await api.createTransform(transformId, transformConfig);
|
||||||
const resp = await api.createTransform(transformId, transformConfig);
|
|
||||||
if (resp.errors !== undefined && Array.isArray(resp.errors)) {
|
|
||||||
if (resp.errors.length === 1) {
|
|
||||||
throw resp.errors[0];
|
|
||||||
}
|
|
||||||
|
|
||||||
if (resp.errors.length > 1) {
|
if (!isPutTransformsResponseSchema(resp) || resp.errors.length > 0) {
|
||||||
throw resp.errors;
|
let respErrors:
|
||||||
}
|
| PutTransformsResponseSchema['errors']
|
||||||
|
| PutTransformsResponseSchema['errors'][number]
|
||||||
|
| undefined;
|
||||||
|
|
||||||
|
if (isPutTransformsResponseSchema(resp) && resp.errors.length > 0) {
|
||||||
|
respErrors = resp.errors.length === 1 ? resp.errors[0] : resp.errors;
|
||||||
}
|
}
|
||||||
|
|
||||||
toastNotifications.addSuccess(
|
|
||||||
i18n.translate('xpack.transform.stepCreateForm.createTransformSuccessMessage', {
|
|
||||||
defaultMessage: 'Request to create transform {transformId} acknowledged.',
|
|
||||||
values: { transformId },
|
|
||||||
})
|
|
||||||
);
|
|
||||||
setCreated(true);
|
|
||||||
setLoading(false);
|
|
||||||
} catch (e) {
|
|
||||||
toastNotifications.addDanger({
|
toastNotifications.addDanger({
|
||||||
title: i18n.translate('xpack.transform.stepCreateForm.createTransformErrorMessage', {
|
title: i18n.translate('xpack.transform.stepCreateForm.createTransformErrorMessage', {
|
||||||
defaultMessage: 'An error occurred creating the transform {transformId}:',
|
defaultMessage: 'An error occurred creating the transform {transformId}:',
|
||||||
values: { transformId },
|
values: { transformId },
|
||||||
}),
|
}),
|
||||||
text: toMountPoint(
|
text: toMountPoint(
|
||||||
<ToastNotificationText overlays={deps.overlays} text={getErrorMessage(e)} />
|
<ToastNotificationText
|
||||||
|
overlays={deps.overlays}
|
||||||
|
text={getErrorMessage(isPutTransformsResponseSchema(resp) ? respErrors : resp)}
|
||||||
|
/>
|
||||||
),
|
),
|
||||||
});
|
});
|
||||||
setCreated(false);
|
setCreated(false);
|
||||||
|
@ -128,6 +128,15 @@ export const StepCreateForm: FC<Props> = React.memo(
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
toastNotifications.addSuccess(
|
||||||
|
i18n.translate('xpack.transform.stepCreateForm.createTransformSuccessMessage', {
|
||||||
|
defaultMessage: 'Request to create transform {transformId} acknowledged.',
|
||||||
|
values: { transformId },
|
||||||
|
})
|
||||||
|
);
|
||||||
|
setCreated(true);
|
||||||
|
setLoading(false);
|
||||||
|
|
||||||
if (createIndexPattern) {
|
if (createIndexPattern) {
|
||||||
createKibanaIndexPattern();
|
createKibanaIndexPattern();
|
||||||
}
|
}
|
||||||
|
@ -138,37 +147,36 @@ export const StepCreateForm: FC<Props> = React.memo(
|
||||||
async function startTransform() {
|
async function startTransform() {
|
||||||
setLoading(true);
|
setLoading(true);
|
||||||
|
|
||||||
try {
|
const resp = await api.startTransforms([{ id: transformId }]);
|
||||||
const resp = await api.startTransforms([{ id: transformId }]);
|
|
||||||
if (typeof resp === 'object' && resp !== null && resp[transformId]?.success === true) {
|
if (isStartTransformsResponseSchema(resp) && resp[transformId]?.success === true) {
|
||||||
toastNotifications.addSuccess(
|
toastNotifications.addSuccess(
|
||||||
i18n.translate('xpack.transform.stepCreateForm.startTransformSuccessMessage', {
|
i18n.translate('xpack.transform.stepCreateForm.startTransformSuccessMessage', {
|
||||||
defaultMessage: 'Request to start transform {transformId} acknowledged.',
|
defaultMessage: 'Request to start transform {transformId} acknowledged.',
|
||||||
values: { transformId },
|
|
||||||
})
|
|
||||||
);
|
|
||||||
setStarted(true);
|
|
||||||
setLoading(false);
|
|
||||||
} else {
|
|
||||||
const errorMessage =
|
|
||||||
typeof resp === 'object' && resp !== null && resp[transformId]?.success === false
|
|
||||||
? resp[transformId].error
|
|
||||||
: resp;
|
|
||||||
throw new Error(errorMessage);
|
|
||||||
}
|
|
||||||
} catch (e) {
|
|
||||||
toastNotifications.addDanger({
|
|
||||||
title: i18n.translate('xpack.transform.stepCreateForm.startTransformErrorMessage', {
|
|
||||||
defaultMessage: 'An error occurred starting the transform {transformId}:',
|
|
||||||
values: { transformId },
|
values: { transformId },
|
||||||
}),
|
})
|
||||||
text: toMountPoint(
|
);
|
||||||
<ToastNotificationText overlays={deps.overlays} text={getErrorMessage(e)} />
|
setStarted(true);
|
||||||
),
|
|
||||||
});
|
|
||||||
setStarted(false);
|
|
||||||
setLoading(false);
|
setLoading(false);
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const errorMessage =
|
||||||
|
isStartTransformsResponseSchema(resp) && resp[transformId]?.success === false
|
||||||
|
? resp[transformId].error
|
||||||
|
: resp;
|
||||||
|
|
||||||
|
toastNotifications.addDanger({
|
||||||
|
title: i18n.translate('xpack.transform.stepCreateForm.startTransformErrorMessage', {
|
||||||
|
defaultMessage: 'An error occurred starting the transform {transformId}:',
|
||||||
|
values: { transformId },
|
||||||
|
}),
|
||||||
|
text: toMountPoint(
|
||||||
|
<ToastNotificationText overlays={deps.overlays} text={getErrorMessage(errorMessage)} />
|
||||||
|
),
|
||||||
|
});
|
||||||
|
setStarted(false);
|
||||||
|
setLoading(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
async function createAndStartTransform() {
|
async function createAndStartTransform() {
|
||||||
|
@ -250,27 +258,30 @@ export const StepCreateForm: FC<Props> = React.memo(
|
||||||
// wrapping in function so we can keep the interval id in local scope
|
// wrapping in function so we can keep the interval id in local scope
|
||||||
function startProgressBar() {
|
function startProgressBar() {
|
||||||
const interval = setInterval(async () => {
|
const interval = setInterval(async () => {
|
||||||
try {
|
const stats = await api.getTransformStats(transformId);
|
||||||
const stats = await api.getTransformsStats(transformId);
|
|
||||||
if (stats && Array.isArray(stats.transforms) && stats.transforms.length > 0) {
|
if (
|
||||||
const percent =
|
isGetTransformsStatsResponseSchema(stats) &&
|
||||||
getTransformProgress({
|
Array.isArray(stats.transforms) &&
|
||||||
id: transformConfig.id,
|
stats.transforms.length > 0
|
||||||
config: transformConfig,
|
) {
|
||||||
stats: stats.transforms[0],
|
const percent =
|
||||||
}) || 0;
|
getTransformProgress({
|
||||||
setProgressPercentComplete(percent);
|
id: transformConfig.id,
|
||||||
if (percent >= 100) {
|
config: transformConfig,
|
||||||
clearInterval(interval);
|
stats: stats.transforms[0],
|
||||||
}
|
}) || 0;
|
||||||
|
setProgressPercentComplete(percent);
|
||||||
|
if (percent >= 100) {
|
||||||
|
clearInterval(interval);
|
||||||
}
|
}
|
||||||
} catch (e) {
|
} else {
|
||||||
toastNotifications.addDanger({
|
toastNotifications.addDanger({
|
||||||
title: i18n.translate('xpack.transform.stepCreateForm.progressErrorMessage', {
|
title: i18n.translate('xpack.transform.stepCreateForm.progressErrorMessage', {
|
||||||
defaultMessage: 'An error occurred getting the progress percentage:',
|
defaultMessage: 'An error occurred getting the progress percentage:',
|
||||||
}),
|
}),
|
||||||
text: toMountPoint(
|
text: toMountPoint(
|
||||||
<ToastNotificationText overlays={deps.overlays} text={getErrorMessage(e)} />
|
<ToastNotificationText overlays={deps.overlays} text={getErrorMessage(stats)} />
|
||||||
),
|
),
|
||||||
});
|
});
|
||||||
clearInterval(interval);
|
clearInterval(interval);
|
||||||
|
|
|
@ -6,19 +6,21 @@
|
||||||
|
|
||||||
import { isEqual } from 'lodash';
|
import { isEqual } from 'lodash';
|
||||||
|
|
||||||
|
import { Dictionary } from '../../../../../../../common/types/common';
|
||||||
|
import { PivotSupportedAggs } from '../../../../../../../common/types/pivot_aggs';
|
||||||
|
import { TransformPivotConfig } from '../../../../../../../common/types/transform';
|
||||||
|
|
||||||
import {
|
import {
|
||||||
matchAllQuery,
|
matchAllQuery,
|
||||||
PivotAggsConfig,
|
PivotAggsConfig,
|
||||||
PivotAggsConfigDict,
|
PivotAggsConfigDict,
|
||||||
PivotGroupByConfig,
|
PivotGroupByConfig,
|
||||||
PivotGroupByConfigDict,
|
PivotGroupByConfigDict,
|
||||||
TransformPivotConfig,
|
|
||||||
PIVOT_SUPPORTED_GROUP_BY_AGGS,
|
PIVOT_SUPPORTED_GROUP_BY_AGGS,
|
||||||
} from '../../../../../common';
|
} from '../../../../../common';
|
||||||
import { Dictionary } from '../../../../../../../common/types/common';
|
|
||||||
|
|
||||||
import { StepDefineExposedState } from './types';
|
import { StepDefineExposedState } from './types';
|
||||||
import { getAggConfigFromEsAgg, PivotSupportedAggs } from '../../../../../common/pivot_aggs';
|
import { getAggConfigFromEsAgg } from '../../../../../common/pivot_aggs';
|
||||||
|
|
||||||
export function applyTransformConfigToDefineState(
|
export function applyTransformConfigToDefineState(
|
||||||
state: StepDefineExposedState,
|
state: StepDefineExposedState,
|
||||||
|
|
|
@ -10,6 +10,7 @@ import { FormattedMessage } from '@kbn/i18n/react';
|
||||||
import { debounce } from 'lodash';
|
import { debounce } from 'lodash';
|
||||||
import { useUpdateEffect } from 'react-use';
|
import { useUpdateEffect } from 'react-use';
|
||||||
import { i18n } from '@kbn/i18n';
|
import { i18n } from '@kbn/i18n';
|
||||||
|
import { isEsSearchResponse } from '../../../../../../../../../common/api_schemas/type_guards';
|
||||||
import { useApi } from '../../../../../../../hooks';
|
import { useApi } from '../../../../../../../hooks';
|
||||||
import { CreateTransformWizardContext } from '../../../../wizard/wizard';
|
import { CreateTransformWizardContext } from '../../../../wizard/wizard';
|
||||||
import { FilterAggConfigTerm } from '../types';
|
import { FilterAggConfigTerm } from '../types';
|
||||||
|
@ -55,22 +56,24 @@ export const FilterTermForm: FilterAggConfigTerm['aggTypeConfig']['FilterAggForm
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
try {
|
const response = await api.esSearch(esSearchRequest);
|
||||||
const response = await api.esSearch(esSearchRequest);
|
|
||||||
setOptions(
|
setIsLoading(false);
|
||||||
response.aggregations.field_values.buckets.map(
|
|
||||||
(value: { key: string; doc_count: number }) => ({ label: value.key })
|
if (!isEsSearchResponse(response)) {
|
||||||
)
|
|
||||||
);
|
|
||||||
} catch (e) {
|
|
||||||
toastNotifications.addWarning(
|
toastNotifications.addWarning(
|
||||||
i18n.translate('xpack.transform.agg.popoverForm.filerAgg.term.errorFetchSuggestions', {
|
i18n.translate('xpack.transform.agg.popoverForm.filerAgg.term.errorFetchSuggestions', {
|
||||||
defaultMessage: 'Unable to fetch suggestions',
|
defaultMessage: 'Unable to fetch suggestions',
|
||||||
})
|
})
|
||||||
);
|
);
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
setIsLoading(false);
|
setOptions(
|
||||||
|
response.aggregations.field_values.buckets.map(
|
||||||
|
(value: { key: string; doc_count: number }) => ({ label: value.key })
|
||||||
|
)
|
||||||
|
);
|
||||||
}, 600),
|
}, 600),
|
||||||
[selectedField]
|
[selectedField]
|
||||||
);
|
);
|
||||||
|
|
|
@ -5,11 +5,11 @@
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import {
|
import {
|
||||||
PIVOT_SUPPORTED_AGGS,
|
|
||||||
PivotAggsConfigBase,
|
|
||||||
PivotAggsConfigWithUiBase,
|
|
||||||
PivotSupportedAggs,
|
PivotSupportedAggs,
|
||||||
} from '../../../../../common/pivot_aggs';
|
PIVOT_SUPPORTED_AGGS,
|
||||||
|
} from '../../../../../../../common/types/pivot_aggs';
|
||||||
|
|
||||||
|
import { PivotAggsConfigBase, PivotAggsConfigWithUiBase } from '../../../../../common/pivot_aggs';
|
||||||
import { getFilterAggConfig } from './filter_agg/config';
|
import { getFilterAggConfig } from './filter_agg/config';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -6,7 +6,8 @@
|
||||||
|
|
||||||
import { i18n } from '@kbn/i18n';
|
import { i18n } from '@kbn/i18n';
|
||||||
|
|
||||||
import { AggName, PivotAggsConfigDict, PivotGroupByConfigDict } from '../../../../../common';
|
import { AggName } from '../../../../../../../common/types/aggregations';
|
||||||
|
import { PivotAggsConfigDict, PivotGroupByConfigDict } from '../../../../../common';
|
||||||
|
|
||||||
export function getAggNameConflictToastMessages(
|
export function getAggNameConflictToastMessages(
|
||||||
aggName: AggName,
|
aggName: AggName,
|
||||||
|
@ -36,7 +37,7 @@ export function getAggNameConflictToastMessages(
|
||||||
// check the new aggName against existing aggs and groupbys
|
// check the new aggName against existing aggs and groupbys
|
||||||
const aggNameSplit = aggName.split('.');
|
const aggNameSplit = aggName.split('.');
|
||||||
let aggNameCheck: string;
|
let aggNameCheck: string;
|
||||||
aggNameSplit.forEach((aggNamePart) => {
|
aggNameSplit.forEach((aggNamePart: string) => {
|
||||||
aggNameCheck = aggNameCheck === undefined ? aggNamePart : `${aggNameCheck}.${aggNamePart}`;
|
aggNameCheck = aggNameCheck === undefined ? aggNamePart : `${aggNameCheck}.${aggNamePart}`;
|
||||||
if (aggList[aggNameCheck] !== undefined || groupByList[aggNameCheck] !== undefined) {
|
if (aggList[aggNameCheck] !== undefined || groupByList[aggNameCheck] !== undefined) {
|
||||||
conflicts.push(
|
conflicts.push(
|
||||||
|
|
|
@ -4,13 +4,15 @@
|
||||||
* you may not use this file except in compliance with the Elastic License.
|
* you may not use this file except in compliance with the Elastic License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
import { EsFieldName } from '../../../../../../../common/types/fields';
|
||||||
import {
|
import {
|
||||||
EsFieldName,
|
PivotSupportedAggs,
|
||||||
PERCENTILES_AGG_DEFAULT_PERCENTS,
|
|
||||||
PIVOT_SUPPORTED_AGGS,
|
PIVOT_SUPPORTED_AGGS,
|
||||||
|
} from '../../../../../../../common/types/pivot_aggs';
|
||||||
|
import {
|
||||||
|
PERCENTILES_AGG_DEFAULT_PERCENTS,
|
||||||
PivotAggsConfigWithUiSupport,
|
PivotAggsConfigWithUiSupport,
|
||||||
} from '../../../../../common';
|
} from '../../../../../common';
|
||||||
import { PivotSupportedAggs } from '../../../../../common/pivot_aggs';
|
|
||||||
import { getFilterAggConfig } from './filter_agg/config';
|
import { getFilterAggConfig } from './filter_agg/config';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -4,11 +4,9 @@
|
||||||
* you may not use this file except in compliance with the Elastic License.
|
* you may not use this file except in compliance with the Elastic License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import {
|
import { EsFieldName } from '../../../../../../../common/types/fields';
|
||||||
EsFieldName,
|
|
||||||
GroupByConfigWithUiSupport,
|
import { GroupByConfigWithUiSupport, PIVOT_SUPPORTED_GROUP_BY_AGGS } from '../../../../../common';
|
||||||
PIVOT_SUPPORTED_GROUP_BY_AGGS,
|
|
||||||
} from '../../../../../common';
|
|
||||||
|
|
||||||
export function getDefaultGroupByConfig(
|
export function getDefaultGroupByConfig(
|
||||||
aggName: string,
|
aggName: string,
|
||||||
|
|
|
@ -6,7 +6,9 @@
|
||||||
|
|
||||||
import { KBN_FIELD_TYPES } from '../../../../../../../../../../src/plugins/data/public';
|
import { KBN_FIELD_TYPES } from '../../../../../../../../../../src/plugins/data/public';
|
||||||
|
|
||||||
import { EsFieldName, PivotAggsConfigDict, PivotGroupByConfigDict } from '../../../../../common';
|
import { EsFieldName } from '../../../../../../../common/types/fields';
|
||||||
|
|
||||||
|
import { PivotAggsConfigDict, PivotGroupByConfigDict } from '../../../../../common';
|
||||||
import { SavedSearchQuery } from '../../../../../hooks/use_search_items';
|
import { SavedSearchQuery } from '../../../../../hooks/use_search_items';
|
||||||
|
|
||||||
import { QUERY_LANGUAGE } from './constants';
|
import { QUERY_LANGUAGE } from './constants';
|
||||||
|
|
|
@ -8,13 +8,13 @@ import { useEffect, useState } from 'react';
|
||||||
|
|
||||||
import { useXJsonMode } from '../../../../../../../../../../src/plugins/es_ui_shared/static/ace_x_json/hooks';
|
import { useXJsonMode } from '../../../../../../../../../../src/plugins/es_ui_shared/static/ace_x_json/hooks';
|
||||||
|
|
||||||
import { PreviewRequestBody } from '../../../../../common';
|
import { PostTransformsPreviewRequestSchema } from '../../../../../../../common/api_schemas/transforms';
|
||||||
|
|
||||||
import { StepDefineExposedState } from '../common';
|
import { StepDefineExposedState } from '../common';
|
||||||
|
|
||||||
export const useAdvancedPivotEditor = (
|
export const useAdvancedPivotEditor = (
|
||||||
defaults: StepDefineExposedState,
|
defaults: StepDefineExposedState,
|
||||||
previewRequest: PreviewRequestBody
|
previewRequest: PostTransformsPreviewRequestSchema
|
||||||
) => {
|
) => {
|
||||||
const stringifiedPivotConfig = JSON.stringify(previewRequest.pivot, null, 2);
|
const stringifiedPivotConfig = JSON.stringify(previewRequest.pivot, null, 2);
|
||||||
|
|
||||||
|
|
|
@ -6,13 +6,13 @@
|
||||||
|
|
||||||
import { useState } from 'react';
|
import { useState } from 'react';
|
||||||
|
|
||||||
import { PreviewRequestBody } from '../../../../../common';
|
import { PostTransformsPreviewRequestSchema } from '../../../../../../../common/api_schemas/transforms';
|
||||||
|
|
||||||
import { StepDefineExposedState } from '../common';
|
import { StepDefineExposedState } from '../common';
|
||||||
|
|
||||||
export const useAdvancedSourceEditor = (
|
export const useAdvancedSourceEditor = (
|
||||||
defaults: StepDefineExposedState,
|
defaults: StepDefineExposedState,
|
||||||
previewRequest: PreviewRequestBody
|
previewRequest: PostTransformsPreviewRequestSchema
|
||||||
) => {
|
) => {
|
||||||
const stringifiedSourceConfig = JSON.stringify(previewRequest.source.query, null, 2);
|
const stringifiedSourceConfig = JSON.stringify(previewRequest.source.query, null, 2);
|
||||||
|
|
||||||
|
|
|
@ -6,11 +6,11 @@
|
||||||
|
|
||||||
import { useCallback, useMemo, useState } from 'react';
|
import { useCallback, useMemo, useState } from 'react';
|
||||||
|
|
||||||
|
import { AggName } from '../../../../../../../common/types/aggregations';
|
||||||
import { dictionaryToArray } from '../../../../../../../common/types/common';
|
import { dictionaryToArray } from '../../../../../../../common/types/common';
|
||||||
|
|
||||||
import { useToastNotifications } from '../../../../../app_dependencies';
|
import { useToastNotifications } from '../../../../../app_dependencies';
|
||||||
import {
|
import {
|
||||||
AggName,
|
|
||||||
DropDownLabel,
|
DropDownLabel,
|
||||||
PivotAggsConfig,
|
PivotAggsConfig,
|
||||||
PivotAggsConfigDict,
|
PivotAggsConfigDict,
|
||||||
|
|
|
@ -6,7 +6,7 @@
|
||||||
|
|
||||||
import { useEffect } from 'react';
|
import { useEffect } from 'react';
|
||||||
|
|
||||||
import { getPreviewRequestBody } from '../../../../../common';
|
import { getPreviewTransformRequestBody } from '../../../../../common';
|
||||||
|
|
||||||
import { getDefaultStepDefineState } from '../common';
|
import { getDefaultStepDefineState } from '../common';
|
||||||
|
|
||||||
|
@ -26,7 +26,7 @@ export const useStepDefineForm = ({ overrides, onChange, searchItems }: StepDefi
|
||||||
const searchBar = useSearchBar(defaults, indexPattern);
|
const searchBar = useSearchBar(defaults, indexPattern);
|
||||||
const pivotConfig = usePivotConfig(defaults, indexPattern);
|
const pivotConfig = usePivotConfig(defaults, indexPattern);
|
||||||
|
|
||||||
const previewRequest = getPreviewRequestBody(
|
const previewRequest = getPreviewTransformRequestBody(
|
||||||
indexPattern.title,
|
indexPattern.title,
|
||||||
searchBar.state.pivotQuery,
|
searchBar.state.pivotQuery,
|
||||||
pivotConfig.state.pivotGroupByArr,
|
pivotConfig.state.pivotGroupByArr,
|
||||||
|
@ -41,7 +41,7 @@ export const useStepDefineForm = ({ overrides, onChange, searchItems }: StepDefi
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (!advancedSourceEditor.state.isAdvancedSourceEditorEnabled) {
|
if (!advancedSourceEditor.state.isAdvancedSourceEditorEnabled) {
|
||||||
const previewRequestUpdate = getPreviewRequestBody(
|
const previewRequestUpdate = getPreviewTransformRequestBody(
|
||||||
indexPattern.title,
|
indexPattern.title,
|
||||||
searchBar.state.pivotQuery,
|
searchBar.state.pivotQuery,
|
||||||
pivotConfig.state.pivotGroupByArr,
|
pivotConfig.state.pivotGroupByArr,
|
||||||
|
|
|
@ -15,10 +15,11 @@ import { coreMock } from '../../../../../../../../../src/core/public/mocks';
|
||||||
import { dataPluginMock } from '../../../../../../../../../src/plugins/data/public/mocks';
|
import { dataPluginMock } from '../../../../../../../../../src/plugins/data/public/mocks';
|
||||||
const startMock = coreMock.createStart();
|
const startMock = coreMock.createStart();
|
||||||
|
|
||||||
|
import { PIVOT_SUPPORTED_AGGS } from '../../../../../../common/types/pivot_aggs';
|
||||||
|
|
||||||
import {
|
import {
|
||||||
PivotAggsConfigDict,
|
PivotAggsConfigDict,
|
||||||
PivotGroupByConfigDict,
|
PivotGroupByConfigDict,
|
||||||
PIVOT_SUPPORTED_AGGS,
|
|
||||||
PIVOT_SUPPORTED_GROUP_BY_AGGS,
|
PIVOT_SUPPORTED_GROUP_BY_AGGS,
|
||||||
} from '../../../../common';
|
} from '../../../../common';
|
||||||
import { SearchItems } from '../../../../hooks/use_search_items';
|
import { SearchItems } from '../../../../hooks/use_search_items';
|
||||||
|
|
|
@ -22,6 +22,9 @@ import {
|
||||||
EuiText,
|
EuiText,
|
||||||
} from '@elastic/eui';
|
} from '@elastic/eui';
|
||||||
|
|
||||||
|
import { PivotAggDict } from '../../../../../../common/types/pivot_aggs';
|
||||||
|
import { PivotGroupByDict } from '../../../../../../common/types/pivot_group_by';
|
||||||
|
|
||||||
import { DataGrid } from '../../../../../shared_imports';
|
import { DataGrid } from '../../../../../shared_imports';
|
||||||
|
|
||||||
import {
|
import {
|
||||||
|
@ -30,10 +33,8 @@ import {
|
||||||
} from '../../../../common/data_grid';
|
} from '../../../../common/data_grid';
|
||||||
|
|
||||||
import {
|
import {
|
||||||
getPreviewRequestBody,
|
getPreviewTransformRequestBody,
|
||||||
PivotAggDict,
|
|
||||||
PivotAggsConfigDict,
|
PivotAggsConfigDict,
|
||||||
PivotGroupByDict,
|
|
||||||
PivotGroupByConfigDict,
|
PivotGroupByConfigDict,
|
||||||
PivotSupportedGroupByAggs,
|
PivotSupportedGroupByAggs,
|
||||||
PivotAggsConfig,
|
PivotAggsConfig,
|
||||||
|
@ -87,7 +88,7 @@ export const StepDefineForm: FC<StepDefineFormProps> = React.memo((props) => {
|
||||||
toastNotifications,
|
toastNotifications,
|
||||||
};
|
};
|
||||||
|
|
||||||
const previewRequest = getPreviewRequestBody(
|
const previewRequest = getPreviewTransformRequestBody(
|
||||||
indexPattern.title,
|
indexPattern.title,
|
||||||
pivotQuery,
|
pivotQuery,
|
||||||
pivotGroupByArr,
|
pivotGroupByArr,
|
||||||
|
|
|
@ -7,10 +7,11 @@
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import { render, wait } from '@testing-library/react';
|
import { render, wait } from '@testing-library/react';
|
||||||
|
|
||||||
|
import { PIVOT_SUPPORTED_AGGS } from '../../../../../../common/types/pivot_aggs';
|
||||||
|
|
||||||
import {
|
import {
|
||||||
PivotAggsConfig,
|
PivotAggsConfig,
|
||||||
PivotGroupByConfig,
|
PivotGroupByConfig,
|
||||||
PIVOT_SUPPORTED_AGGS,
|
|
||||||
PIVOT_SUPPORTED_GROUP_BY_AGGS,
|
PIVOT_SUPPORTED_GROUP_BY_AGGS,
|
||||||
} from '../../../../common';
|
} from '../../../../common';
|
||||||
import { SearchItems } from '../../../../hooks/use_search_items';
|
import { SearchItems } from '../../../../hooks/use_search_items';
|
||||||
|
|
|
@ -18,7 +18,7 @@ import { useToastNotifications } from '../../../../app_dependencies';
|
||||||
import {
|
import {
|
||||||
getPivotQuery,
|
getPivotQuery,
|
||||||
getPivotPreviewDevConsoleStatement,
|
getPivotPreviewDevConsoleStatement,
|
||||||
getPreviewRequestBody,
|
getPreviewTransformRequestBody,
|
||||||
isDefaultQuery,
|
isDefaultQuery,
|
||||||
isMatchAllQuery,
|
isMatchAllQuery,
|
||||||
} from '../../../../common';
|
} from '../../../../common';
|
||||||
|
@ -44,7 +44,7 @@ export const StepDefineSummary: FC<Props> = ({
|
||||||
const pivotGroupByArr = dictionaryToArray(groupByList);
|
const pivotGroupByArr = dictionaryToArray(groupByList);
|
||||||
const pivotQuery = getPivotQuery(searchQuery);
|
const pivotQuery = getPivotQuery(searchQuery);
|
||||||
|
|
||||||
const previewRequest = getPreviewRequestBody(
|
const previewRequest = getPreviewTransformRequestBody(
|
||||||
searchItems.indexPattern.title,
|
searchItems.indexPattern.title,
|
||||||
pivotQuery,
|
pivotQuery,
|
||||||
pivotGroupByArr,
|
pivotGroupByArr,
|
||||||
|
|
|
@ -11,24 +11,28 @@ import { i18n } from '@kbn/i18n';
|
||||||
import { EuiLink, EuiSwitch, EuiFieldText, EuiForm, EuiFormRow, EuiSelect } from '@elastic/eui';
|
import { EuiLink, EuiSwitch, EuiFieldText, EuiForm, EuiFormRow, EuiSelect } from '@elastic/eui';
|
||||||
|
|
||||||
import { KBN_FIELD_TYPES } from '../../../../../../../../../src/plugins/data/common';
|
import { KBN_FIELD_TYPES } from '../../../../../../../../../src/plugins/data/common';
|
||||||
|
|
||||||
import { toMountPoint } from '../../../../../../../../../src/plugins/kibana_react/public';
|
import { toMountPoint } from '../../../../../../../../../src/plugins/kibana_react/public';
|
||||||
import { TransformId } from '../../../../../../common';
|
|
||||||
|
import {
|
||||||
|
isEsIndices,
|
||||||
|
isPostTransformsPreviewResponseSchema,
|
||||||
|
} from '../../../../../../common/api_schemas/type_guards';
|
||||||
|
import { TransformId, TransformPivotConfig } from '../../../../../../common/types/transform';
|
||||||
import { isValidIndexName } from '../../../../../../common/utils/es_utils';
|
import { isValidIndexName } from '../../../../../../common/utils/es_utils';
|
||||||
|
|
||||||
import { getErrorMessage } from '../../../../../../common/utils/errors';
|
import { getErrorMessage } from '../../../../../../common/utils/errors';
|
||||||
|
|
||||||
import { useAppDependencies, useToastNotifications } from '../../../../app_dependencies';
|
import { useAppDependencies, useToastNotifications } from '../../../../app_dependencies';
|
||||||
import { ToastNotificationText } from '../../../../components';
|
import { ToastNotificationText } from '../../../../components';
|
||||||
|
import { isHttpFetchError } from '../../../../common/request';
|
||||||
import { useDocumentationLinks } from '../../../../hooks/use_documentation_links';
|
import { useDocumentationLinks } from '../../../../hooks/use_documentation_links';
|
||||||
import { SearchItems } from '../../../../hooks/use_search_items';
|
import { SearchItems } from '../../../../hooks/use_search_items';
|
||||||
import { useApi } from '../../../../hooks/use_api';
|
import { useApi } from '../../../../hooks/use_api';
|
||||||
import { StepDetailsTimeField } from './step_details_time_field';
|
import { StepDetailsTimeField } from './step_details_time_field';
|
||||||
import {
|
import {
|
||||||
getPivotQuery,
|
getPivotQuery,
|
||||||
getPreviewRequestBody,
|
getPreviewTransformRequestBody,
|
||||||
isTransformIdValid,
|
isTransformIdValid,
|
||||||
TransformPivotConfig,
|
|
||||||
} from '../../../../common';
|
} from '../../../../common';
|
||||||
import { EsIndexName, IndexPatternTitle } from './common';
|
import { EsIndexName, IndexPatternTitle } from './common';
|
||||||
import { delayValidator } from '../../../../common/validators';
|
import { delayValidator } from '../../../../common/validators';
|
||||||
|
@ -48,10 +52,12 @@ export interface StepDetailsExposedState {
|
||||||
indexPatternDateField?: string | undefined;
|
indexPatternDateField?: string | undefined;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const defaultContinuousModeDelay = '60s';
|
||||||
|
|
||||||
export function getDefaultStepDetailsState(): StepDetailsExposedState {
|
export function getDefaultStepDetailsState(): StepDetailsExposedState {
|
||||||
return {
|
return {
|
||||||
continuousModeDateField: '',
|
continuousModeDateField: '',
|
||||||
continuousModeDelay: '60s',
|
continuousModeDelay: defaultContinuousModeDelay,
|
||||||
createIndexPattern: true,
|
createIndexPattern: true,
|
||||||
isContinuousModeEnabled: false,
|
isContinuousModeEnabled: false,
|
||||||
transformId: '',
|
transformId: '',
|
||||||
|
@ -72,7 +78,7 @@ export function applyTransformConfigToDetailsState(
|
||||||
const time = transformConfig.sync?.time;
|
const time = transformConfig.sync?.time;
|
||||||
if (time !== undefined) {
|
if (time !== undefined) {
|
||||||
state.continuousModeDateField = time.field;
|
state.continuousModeDateField = time.field;
|
||||||
state.continuousModeDelay = time.delay;
|
state.continuousModeDelay = time?.delay ?? defaultContinuousModeDelay;
|
||||||
state.isContinuousModeEnabled = true;
|
state.isContinuousModeEnabled = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -137,19 +143,20 @@ export const StepDetailsForm: FC<Props> = React.memo(
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
// use an IIFE to avoid returning a Promise to useEffect.
|
// use an IIFE to avoid returning a Promise to useEffect.
|
||||||
(async function () {
|
(async function () {
|
||||||
try {
|
const { searchQuery, groupByList, aggList } = stepDefineState;
|
||||||
const { searchQuery, groupByList, aggList } = stepDefineState;
|
const pivotAggsArr = dictionaryToArray(aggList);
|
||||||
const pivotAggsArr = dictionaryToArray(aggList);
|
const pivotGroupByArr = dictionaryToArray(groupByList);
|
||||||
const pivotGroupByArr = dictionaryToArray(groupByList);
|
const pivotQuery = getPivotQuery(searchQuery);
|
||||||
const pivotQuery = getPivotQuery(searchQuery);
|
const previewRequest = getPreviewTransformRequestBody(
|
||||||
const previewRequest = getPreviewRequestBody(
|
searchItems.indexPattern.title,
|
||||||
searchItems.indexPattern.title,
|
pivotQuery,
|
||||||
pivotQuery,
|
pivotGroupByArr,
|
||||||
pivotGroupByArr,
|
pivotAggsArr
|
||||||
pivotAggsArr
|
);
|
||||||
);
|
|
||||||
|
|
||||||
const transformPreview = await api.getTransformsPreview(previewRequest);
|
const transformPreview = await api.getTransformsPreview(previewRequest);
|
||||||
|
|
||||||
|
if (isPostTransformsPreviewResponseSchema(transformPreview)) {
|
||||||
const properties = transformPreview.generated_dest_index.mappings.properties;
|
const properties = transformPreview.generated_dest_index.mappings.properties;
|
||||||
const datetimeColumns: string[] = Object.keys(properties).filter(
|
const datetimeColumns: string[] = Object.keys(properties).filter(
|
||||||
(col) => properties[col].type === 'date'
|
(col) => properties[col].type === 'date'
|
||||||
|
@ -157,43 +164,46 @@ export const StepDetailsForm: FC<Props> = React.memo(
|
||||||
|
|
||||||
setPreviewDateColumns(datetimeColumns);
|
setPreviewDateColumns(datetimeColumns);
|
||||||
setIndexPatternDateField(datetimeColumns[0]);
|
setIndexPatternDateField(datetimeColumns[0]);
|
||||||
} catch (e) {
|
} else {
|
||||||
toastNotifications.addDanger({
|
toastNotifications.addDanger({
|
||||||
title: i18n.translate('xpack.transform.stepDetailsForm.errorGettingTransformPreview', {
|
title: i18n.translate('xpack.transform.stepDetailsForm.errorGettingTransformPreview', {
|
||||||
defaultMessage: 'An error occurred getting transform preview',
|
defaultMessage: 'An error occurred fetching the transform preview',
|
||||||
}),
|
}),
|
||||||
text: toMountPoint(
|
text: toMountPoint(
|
||||||
<ToastNotificationText overlays={deps.overlays} text={getErrorMessage(e)} />
|
<ToastNotificationText
|
||||||
|
overlays={deps.overlays}
|
||||||
|
text={getErrorMessage(transformPreview)}
|
||||||
|
/>
|
||||||
),
|
),
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
try {
|
const resp = await api.getTransforms();
|
||||||
setTransformIds(
|
|
||||||
(await api.getTransforms()).transforms.map(
|
if (isHttpFetchError(resp)) {
|
||||||
(transform: TransformPivotConfig) => transform.id
|
|
||||||
)
|
|
||||||
);
|
|
||||||
} catch (e) {
|
|
||||||
toastNotifications.addDanger({
|
toastNotifications.addDanger({
|
||||||
title: i18n.translate('xpack.transform.stepDetailsForm.errorGettingTransformList', {
|
title: i18n.translate('xpack.transform.stepDetailsForm.errorGettingTransformList', {
|
||||||
defaultMessage: 'An error occurred getting the existing transform IDs:',
|
defaultMessage: 'An error occurred getting the existing transform IDs:',
|
||||||
}),
|
}),
|
||||||
text: toMountPoint(
|
text: toMountPoint(
|
||||||
<ToastNotificationText overlays={deps.overlays} text={getErrorMessage(e)} />
|
<ToastNotificationText overlays={deps.overlays} text={getErrorMessage(resp)} />
|
||||||
),
|
),
|
||||||
});
|
});
|
||||||
|
} else {
|
||||||
|
setTransformIds(resp.transforms.map((transform: TransformPivotConfig) => transform.id));
|
||||||
}
|
}
|
||||||
|
|
||||||
try {
|
const indices = await api.getEsIndices();
|
||||||
setIndexNames((await api.getIndices()).map((index) => index.name));
|
|
||||||
} catch (e) {
|
if (isEsIndices(indices)) {
|
||||||
|
setIndexNames(indices.map((index) => index.name));
|
||||||
|
} else {
|
||||||
toastNotifications.addDanger({
|
toastNotifications.addDanger({
|
||||||
title: i18n.translate('xpack.transform.stepDetailsForm.errorGettingIndexNames', {
|
title: i18n.translate('xpack.transform.stepDetailsForm.errorGettingIndexNames', {
|
||||||
defaultMessage: 'An error occurred getting the existing index names:',
|
defaultMessage: 'An error occurred getting the existing index names:',
|
||||||
}),
|
}),
|
||||||
text: toMountPoint(
|
text: toMountPoint(
|
||||||
<ToastNotificationText overlays={deps.overlays} text={getErrorMessage(e)} />
|
<ToastNotificationText overlays={deps.overlays} text={getErrorMessage(indices)} />
|
||||||
),
|
),
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
|
@ -10,7 +10,9 @@ import { i18n } from '@kbn/i18n';
|
||||||
|
|
||||||
import { EuiSteps, EuiStepStatus } from '@elastic/eui';
|
import { EuiSteps, EuiStepStatus } from '@elastic/eui';
|
||||||
|
|
||||||
import { getCreateRequestBody, TransformPivotConfig } from '../../../../common';
|
import { TransformPivotConfig } from '../../../../../../common/types/transform';
|
||||||
|
|
||||||
|
import { getCreateTransformRequestBody } from '../../../../common';
|
||||||
import { SearchItems } from '../../../../hooks/use_search_items';
|
import { SearchItems } from '../../../../hooks/use_search_items';
|
||||||
|
|
||||||
import {
|
import {
|
||||||
|
@ -149,7 +151,7 @@ export const Wizard: FC<WizardProps> = React.memo(({ cloneConfig, searchItems })
|
||||||
}
|
}
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
const transformConfig = getCreateRequestBody(
|
const transformConfig = getCreateTransformRequestBody(
|
||||||
indexPattern.title,
|
indexPattern.title,
|
||||||
stepDefineState,
|
stepDefineState,
|
||||||
stepDetailsState
|
stepDetailsState
|
||||||
|
|
|
@ -7,7 +7,7 @@
|
||||||
import React, { FC } from 'react';
|
import React, { FC } from 'react';
|
||||||
import { i18n } from '@kbn/i18n';
|
import { i18n } from '@kbn/i18n';
|
||||||
import { EuiToolTip } from '@elastic/eui';
|
import { EuiToolTip } from '@elastic/eui';
|
||||||
import { TRANSFORM_STATE } from '../../../../../../common';
|
import { TransformState, TRANSFORM_STATE } from '../../../../../../common/constants';
|
||||||
import { createCapabilityFailureMessage } from '../../../../lib/authorization';
|
import { createCapabilityFailureMessage } from '../../../../lib/authorization';
|
||||||
import { TransformListRow } from '../../../../common';
|
import { TransformListRow } from '../../../../common';
|
||||||
|
|
||||||
|
@ -18,8 +18,8 @@ export const deleteActionNameText = i18n.translate(
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
const transformCanNotBeDeleted = (item: TransformListRow) =>
|
const transformCanNotBeDeleted = (i: TransformListRow) =>
|
||||||
![TRANSFORM_STATE.STOPPED, TRANSFORM_STATE.FAILED].includes(item.stats.state);
|
!([TRANSFORM_STATE.STOPPED, TRANSFORM_STATE.FAILED] as TransformState[]).includes(i.stats.state);
|
||||||
|
|
||||||
export const isDeleteActionDisabled = (items: TransformListRow[], forceDisable: boolean) => {
|
export const isDeleteActionDisabled = (items: TransformListRow[], forceDisable: boolean) => {
|
||||||
const disabled = items.some(transformCanNotBeDeleted);
|
const disabled = items.some(transformCanNotBeDeleted);
|
||||||
|
|
|
@ -6,7 +6,7 @@
|
||||||
|
|
||||||
import React, { useContext, useMemo, useState } from 'react';
|
import React, { useContext, useMemo, useState } from 'react';
|
||||||
|
|
||||||
import { TRANSFORM_STATE } from '../../../../../../common';
|
import { TRANSFORM_STATE } from '../../../../../../common/constants';
|
||||||
|
|
||||||
import { TransformListAction, TransformListRow } from '../../../../common';
|
import { TransformListAction, TransformListRow } from '../../../../common';
|
||||||
import { useDeleteIndexAndTargetIndex, useDeleteTransforms } from '../../../../hooks';
|
import { useDeleteIndexAndTargetIndex, useDeleteTransforms } from '../../../../hooks';
|
||||||
|
@ -55,7 +55,16 @@ export const useDeleteAction = (forceDisable: boolean) => {
|
||||||
const forceDelete = isBulkAction
|
const forceDelete = isBulkAction
|
||||||
? shouldForceDelete
|
? shouldForceDelete
|
||||||
: items[0] && items[0] && items[0].stats.state === TRANSFORM_STATE.FAILED;
|
: items[0] && items[0] && items[0].stats.state === TRANSFORM_STATE.FAILED;
|
||||||
deleteTransforms(items, shouldDeleteDestIndex, shouldDeleteDestIndexPattern, forceDelete);
|
|
||||||
|
deleteTransforms({
|
||||||
|
transformsInfo: items.map((i) => ({
|
||||||
|
id: i.config.id,
|
||||||
|
state: i.stats.state,
|
||||||
|
})),
|
||||||
|
deleteDestIndex: shouldDeleteDestIndex,
|
||||||
|
deleteDestIndexPattern: shouldDeleteDestIndexPattern,
|
||||||
|
forceDelete,
|
||||||
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
const openModal = (newItems: TransformListRow[]) => {
|
const openModal = (newItems: TransformListRow[]) => {
|
||||||
|
|
|
@ -6,7 +6,9 @@
|
||||||
|
|
||||||
import React, { useContext, useMemo, useState } from 'react';
|
import React, { useContext, useMemo, useState } from 'react';
|
||||||
|
|
||||||
import { TransformListAction, TransformListRow, TransformPivotConfig } from '../../../../common';
|
import { TransformPivotConfig } from '../../../../../../common/types/transform';
|
||||||
|
|
||||||
|
import { TransformListAction, TransformListRow } from '../../../../common';
|
||||||
import { AuthorizationContext } from '../../../../lib/authorization';
|
import { AuthorizationContext } from '../../../../lib/authorization';
|
||||||
|
|
||||||
import { editActionNameText, EditActionName } from './edit_action_name';
|
import { editActionNameText, EditActionName } from './edit_action_name';
|
||||||
|
|
|
@ -8,7 +8,7 @@ import React, { FC, useContext } from 'react';
|
||||||
import { i18n } from '@kbn/i18n';
|
import { i18n } from '@kbn/i18n';
|
||||||
import { EuiToolTip } from '@elastic/eui';
|
import { EuiToolTip } from '@elastic/eui';
|
||||||
|
|
||||||
import { TRANSFORM_STATE } from '../../../../../../common';
|
import { TRANSFORM_STATE } from '../../../../../../common/constants';
|
||||||
|
|
||||||
import {
|
import {
|
||||||
createCapabilityFailureMessage,
|
createCapabilityFailureMessage,
|
||||||
|
|
|
@ -6,7 +6,7 @@
|
||||||
|
|
||||||
import React, { useContext, useMemo, useState } from 'react';
|
import React, { useContext, useMemo, useState } from 'react';
|
||||||
|
|
||||||
import { TRANSFORM_STATE } from '../../../../../../common';
|
import { TRANSFORM_STATE } from '../../../../../../common/constants';
|
||||||
|
|
||||||
import { AuthorizationContext } from '../../../../lib/authorization';
|
import { AuthorizationContext } from '../../../../lib/authorization';
|
||||||
import { TransformListAction, TransformListRow } from '../../../../common';
|
import { TransformListAction, TransformListRow } from '../../../../common';
|
||||||
|
@ -27,7 +27,7 @@ export const useStartAction = (forceDisable: boolean) => {
|
||||||
|
|
||||||
const startAndCloseModal = () => {
|
const startAndCloseModal = () => {
|
||||||
setModalVisible(false);
|
setModalVisible(false);
|
||||||
startTransforms(items);
|
startTransforms(items.map((i) => ({ id: i.id })));
|
||||||
};
|
};
|
||||||
|
|
||||||
const openModal = (newItems: TransformListRow[]) => {
|
const openModal = (newItems: TransformListRow[]) => {
|
||||||
|
|
|
@ -8,7 +8,7 @@ import React, { FC, useContext } from 'react';
|
||||||
import { i18n } from '@kbn/i18n';
|
import { i18n } from '@kbn/i18n';
|
||||||
import { EuiToolTip } from '@elastic/eui';
|
import { EuiToolTip } from '@elastic/eui';
|
||||||
|
|
||||||
import { TRANSFORM_STATE } from '../../../../../../common';
|
import { TRANSFORM_STATE } from '../../../../../../common/constants';
|
||||||
|
|
||||||
import { TransformListRow } from '../../../../common';
|
import { TransformListRow } from '../../../../common';
|
||||||
import {
|
import {
|
||||||
|
|
|
@ -6,7 +6,7 @@
|
||||||
|
|
||||||
import React, { useCallback, useContext, useMemo } from 'react';
|
import React, { useCallback, useContext, useMemo } from 'react';
|
||||||
|
|
||||||
import { TRANSFORM_STATE } from '../../../../../../common';
|
import { TRANSFORM_STATE } from '../../../../../../common/constants';
|
||||||
|
|
||||||
import { AuthorizationContext } from '../../../../lib/authorization';
|
import { AuthorizationContext } from '../../../../lib/authorization';
|
||||||
import { TransformListAction, TransformListRow } from '../../../../common';
|
import { TransformListAction, TransformListRow } from '../../../../common';
|
||||||
|
@ -20,9 +20,10 @@ export const useStopAction = (forceDisable: boolean) => {
|
||||||
|
|
||||||
const stopTransforms = useStopTransforms();
|
const stopTransforms = useStopTransforms();
|
||||||
|
|
||||||
const clickHandler = useCallback((item: TransformListRow) => stopTransforms([item]), [
|
const clickHandler = useCallback(
|
||||||
stopTransforms,
|
(i: TransformListRow) => stopTransforms([{ id: i.id, state: i.stats.state }]),
|
||||||
]);
|
[stopTransforms]
|
||||||
|
);
|
||||||
|
|
||||||
const action: TransformListAction = useMemo(
|
const action: TransformListAction = useMemo(
|
||||||
() => ({
|
() => ({
|
||||||
|
|
|
@ -23,13 +23,12 @@ import {
|
||||||
EuiTitle,
|
EuiTitle,
|
||||||
} from '@elastic/eui';
|
} from '@elastic/eui';
|
||||||
|
|
||||||
|
import { isPostTransformsUpdateResponseSchema } from '../../../../../../common/api_schemas/type_guards';
|
||||||
|
import { TransformPivotConfig } from '../../../../../../common/types/transform';
|
||||||
|
|
||||||
import { getErrorMessage } from '../../../../../../common/utils/errors';
|
import { getErrorMessage } from '../../../../../../common/utils/errors';
|
||||||
|
|
||||||
import {
|
import { refreshTransformList$, REFRESH_TRANSFORM_LIST_STATE } from '../../../../common';
|
||||||
refreshTransformList$,
|
|
||||||
TransformPivotConfig,
|
|
||||||
REFRESH_TRANSFORM_LIST_STATE,
|
|
||||||
} from '../../../../common';
|
|
||||||
import { useToastNotifications } from '../../../../app_dependencies';
|
import { useToastNotifications } from '../../../../app_dependencies';
|
||||||
|
|
||||||
import { useApi } from '../../../../hooks/use_api';
|
import { useApi } from '../../../../hooks/use_api';
|
||||||
|
@ -58,19 +57,21 @@ export const EditTransformFlyout: FC<EditTransformFlyoutProps> = ({ closeFlyout,
|
||||||
const requestConfig = applyFormFieldsToTransformConfig(config, state.formFields);
|
const requestConfig = applyFormFieldsToTransformConfig(config, state.formFields);
|
||||||
const transformId = config.id;
|
const transformId = config.id;
|
||||||
|
|
||||||
try {
|
const resp = await api.updateTransform(transformId, requestConfig);
|
||||||
await api.updateTransform(transformId, requestConfig);
|
|
||||||
toastNotifications.addSuccess(
|
if (!isPostTransformsUpdateResponseSchema(resp)) {
|
||||||
i18n.translate('xpack.transform.transformList.editTransformSuccessMessage', {
|
setErrorMessage(getErrorMessage(resp));
|
||||||
defaultMessage: 'Transform {transformId} updated.',
|
return;
|
||||||
values: { transformId },
|
|
||||||
})
|
|
||||||
);
|
|
||||||
closeFlyout();
|
|
||||||
refreshTransformList$.next(REFRESH_TRANSFORM_LIST_STATE.REFRESH);
|
|
||||||
} catch (e) {
|
|
||||||
setErrorMessage(getErrorMessage(e));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
toastNotifications.addSuccess(
|
||||||
|
i18n.translate('xpack.transform.transformList.editTransformSuccessMessage', {
|
||||||
|
defaultMessage: 'Transform {transformId} updated.',
|
||||||
|
values: { transformId },
|
||||||
|
})
|
||||||
|
);
|
||||||
|
closeFlyout();
|
||||||
|
refreshTransformList$.next(REFRESH_TRANSFORM_LIST_STATE.REFRESH);
|
||||||
}
|
}
|
||||||
|
|
||||||
const isUpdateButtonDisabled = !state.isFormValid || !state.isFormTouched;
|
const isUpdateButtonDisabled = !state.isFormValid || !state.isFormTouched;
|
||||||
|
|
|
@ -4,7 +4,7 @@
|
||||||
* you may not use this file except in compliance with the Elastic License.
|
* you may not use this file except in compliance with the Elastic License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import { TransformPivotConfig } from '../../../../common';
|
import { TransformPivotConfig } from '../../../../../../common/types/transform';
|
||||||
|
|
||||||
import {
|
import {
|
||||||
applyFormFieldsToTransformConfig,
|
applyFormFieldsToTransformConfig,
|
||||||
|
@ -86,9 +86,7 @@ describe('Transform: applyFormFieldsToTransformConfig()', () => {
|
||||||
});
|
});
|
||||||
|
|
||||||
test('should include previously nonexisting attributes', () => {
|
test('should include previously nonexisting attributes', () => {
|
||||||
const transformConfigMock = getTransformConfigMock();
|
const { description, frequency, ...transformConfigMock } = getTransformConfigMock();
|
||||||
delete transformConfigMock.description;
|
|
||||||
delete transformConfigMock.frequency;
|
|
||||||
|
|
||||||
const updateConfig = applyFormFieldsToTransformConfig(transformConfigMock, {
|
const updateConfig = applyFormFieldsToTransformConfig(transformConfigMock, {
|
||||||
description: getDescriptionFieldMock('the-new-description'),
|
description: getDescriptionFieldMock('the-new-description'),
|
||||||
|
|
|
@ -9,7 +9,8 @@ import { useReducer } from 'react';
|
||||||
|
|
||||||
import { i18n } from '@kbn/i18n';
|
import { i18n } from '@kbn/i18n';
|
||||||
|
|
||||||
import { TransformPivotConfig } from '../../../../common';
|
import { PostTransformsUpdateRequestSchema } from '../../../../../../common/api_schemas/update_transforms';
|
||||||
|
import { TransformPivotConfig } from '../../../../../../common/types/transform';
|
||||||
|
|
||||||
// A Validator function takes in a value to check and returns an array of error messages.
|
// A Validator function takes in a value to check and returns an array of error messages.
|
||||||
// If no messages (empty array) get returned, the value is valid.
|
// If no messages (empty array) get returned, the value is valid.
|
||||||
|
@ -118,53 +119,35 @@ interface Action {
|
||||||
value: string;
|
value: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Some attributes can have a value of `null` to trigger
|
|
||||||
// a reset to the default value, or in the case of `docs_per_second`
|
|
||||||
// `null` is used to disable throttling.
|
|
||||||
interface UpdateTransformPivotConfig {
|
|
||||||
description: string;
|
|
||||||
frequency: string;
|
|
||||||
settings: {
|
|
||||||
docs_per_second: number | null;
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
// Takes in the form configuration and returns a
|
// Takes in the form configuration and returns a
|
||||||
// request object suitable to be sent to the
|
// request object suitable to be sent to the
|
||||||
// transform update API endpoint.
|
// transform update API endpoint.
|
||||||
export const applyFormFieldsToTransformConfig = (
|
export const applyFormFieldsToTransformConfig = (
|
||||||
config: TransformPivotConfig,
|
config: TransformPivotConfig,
|
||||||
{ description, docsPerSecond, frequency }: EditTransformFlyoutFieldsState
|
{ description, docsPerSecond, frequency }: EditTransformFlyoutFieldsState
|
||||||
): Partial<UpdateTransformPivotConfig> => {
|
): PostTransformsUpdateRequestSchema => {
|
||||||
const updateConfig: Partial<UpdateTransformPivotConfig> = {};
|
|
||||||
|
|
||||||
// set the values only if they changed from the default
|
|
||||||
// and actually differ from the previous value.
|
|
||||||
if (
|
|
||||||
!(config.frequency === undefined && frequency.value === '') &&
|
|
||||||
config.frequency !== frequency.value
|
|
||||||
) {
|
|
||||||
updateConfig.frequency = frequency.value;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (
|
|
||||||
!(config.description === undefined && description.value === '') &&
|
|
||||||
config.description !== description.value
|
|
||||||
) {
|
|
||||||
updateConfig.description = description.value;
|
|
||||||
}
|
|
||||||
|
|
||||||
// if the input field was left empty,
|
// if the input field was left empty,
|
||||||
// fall back to the default value of `null`
|
// fall back to the default value of `null`
|
||||||
// which will disable throttling.
|
// which will disable throttling.
|
||||||
const docsPerSecondFormValue =
|
const docsPerSecondFormValue =
|
||||||
docsPerSecond.value !== '' ? parseInt(docsPerSecond.value, 10) : null;
|
docsPerSecond.value !== '' ? parseInt(docsPerSecond.value, 10) : null;
|
||||||
const docsPerSecondConfigValue = config.settings?.docs_per_second ?? null;
|
const docsPerSecondConfigValue = config.settings?.docs_per_second ?? null;
|
||||||
if (docsPerSecondFormValue !== docsPerSecondConfigValue) {
|
|
||||||
updateConfig.settings = { docs_per_second: docsPerSecondFormValue };
|
|
||||||
}
|
|
||||||
|
|
||||||
return updateConfig;
|
return {
|
||||||
|
// set the values only if they changed from the default
|
||||||
|
// and actually differ from the previous value.
|
||||||
|
...(!(config.frequency === undefined && frequency.value === '') &&
|
||||||
|
config.frequency !== frequency.value
|
||||||
|
? { frequency: frequency.value }
|
||||||
|
: {}),
|
||||||
|
...(!(config.description === undefined && description.value === '') &&
|
||||||
|
config.description !== description.value
|
||||||
|
? { description: description.value }
|
||||||
|
: {}),
|
||||||
|
...(docsPerSecondFormValue !== docsPerSecondConfigValue
|
||||||
|
? { settings: { docs_per_second: docsPerSecondFormValue } }
|
||||||
|
: {}),
|
||||||
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
// Takes in a transform configuration and returns
|
// Takes in a transform configuration and returns
|
||||||
|
|
|
@ -4,7 +4,7 @@
|
||||||
* you may not use this file except in compliance with the Elastic License.
|
* you may not use this file except in compliance with the Elastic License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import { TRANSFORM_STATE } from '../../../../../../common';
|
import { TRANSFORM_STATE } from '../../../../../../common/constants';
|
||||||
|
|
||||||
import mockTransformListRow from '../../../../common/__mocks__/transform_list_row.json';
|
import mockTransformListRow from '../../../../common/__mocks__/transform_list_row.json';
|
||||||
|
|
||||||
|
|
|
@ -9,11 +9,15 @@ import React, { useState } from 'react';
|
||||||
import { EuiSpacer, EuiBasicTable } from '@elastic/eui';
|
import { EuiSpacer, EuiBasicTable } from '@elastic/eui';
|
||||||
// @ts-ignore
|
// @ts-ignore
|
||||||
import { formatDate } from '@elastic/eui/lib/services/format';
|
import { formatDate } from '@elastic/eui/lib/services/format';
|
||||||
import { i18n } from '@kbn/i18n';
|
|
||||||
import theme from '@elastic/eui/dist/eui_theme_light.json';
|
import theme from '@elastic/eui/dist/eui_theme_light.json';
|
||||||
|
|
||||||
|
import { i18n } from '@kbn/i18n';
|
||||||
|
|
||||||
|
import { isGetTransformsAuditMessagesResponseSchema } from '../../../../../../common/api_schemas/type_guards';
|
||||||
|
import { TransformMessage } from '../../../../../../common/types/messages';
|
||||||
|
|
||||||
import { useApi } from '../../../../hooks/use_api';
|
import { useApi } from '../../../../hooks/use_api';
|
||||||
import { JobIcon } from '../../../../components/job_icon';
|
import { JobIcon } from '../../../../components/job_icon';
|
||||||
import { TransformMessage } from '../../../../../../common/types/messages';
|
|
||||||
import { useRefreshTransformList } from '../../../../common';
|
import { useRefreshTransformList } from '../../../../common';
|
||||||
|
|
||||||
const TIME_FORMAT = 'YYYY-MM-DD HH:mm:ss';
|
const TIME_FORMAT = 'YYYY-MM-DD HH:mm:ss';
|
||||||
|
@ -36,25 +40,16 @@ export const ExpandedRowMessagesPane: React.FC<Props> = ({ transformId }) => {
|
||||||
let concurrentLoads = 0;
|
let concurrentLoads = 0;
|
||||||
|
|
||||||
return async function getMessages() {
|
return async function getMessages() {
|
||||||
try {
|
concurrentLoads++;
|
||||||
concurrentLoads++;
|
|
||||||
|
|
||||||
if (concurrentLoads > 1) {
|
if (concurrentLoads > 1) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
setIsLoading(true);
|
setIsLoading(true);
|
||||||
const messagesResp = await api.getTransformAuditMessages(transformId);
|
const messagesResp = await api.getTransformAuditMessages(transformId);
|
||||||
setIsLoading(false);
|
|
||||||
setMessages(messagesResp as any[]);
|
|
||||||
|
|
||||||
concurrentLoads--;
|
if (!isGetTransformsAuditMessagesResponseSchema(messagesResp)) {
|
||||||
|
|
||||||
if (concurrentLoads > 0) {
|
|
||||||
concurrentLoads = 0;
|
|
||||||
getMessages();
|
|
||||||
}
|
|
||||||
} catch (error) {
|
|
||||||
setIsLoading(false);
|
setIsLoading(false);
|
||||||
setErrorMessage(
|
setErrorMessage(
|
||||||
i18n.translate(
|
i18n.translate(
|
||||||
|
@ -64,6 +59,17 @@ export const ExpandedRowMessagesPane: React.FC<Props> = ({ transformId }) => {
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
);
|
);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
setIsLoading(false);
|
||||||
|
setMessages(messagesResp as any[]);
|
||||||
|
|
||||||
|
concurrentLoads--;
|
||||||
|
|
||||||
|
if (concurrentLoads > 0) {
|
||||||
|
concurrentLoads = 0;
|
||||||
|
getMessages();
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
|
@ -6,10 +6,11 @@
|
||||||
|
|
||||||
import React, { useMemo, FC } from 'react';
|
import React, { useMemo, FC } from 'react';
|
||||||
|
|
||||||
|
import { TransformPivotConfig } from '../../../../../../common/types/transform';
|
||||||
import { DataGrid } from '../../../../../shared_imports';
|
import { DataGrid } from '../../../../../shared_imports';
|
||||||
|
|
||||||
import { useToastNotifications } from '../../../../app_dependencies';
|
import { useToastNotifications } from '../../../../app_dependencies';
|
||||||
import { getPivotQuery, TransformPivotConfig } from '../../../../common';
|
import { getPivotQuery } from '../../../../common';
|
||||||
import { usePivotData } from '../../../../hooks/use_pivot_data';
|
import { usePivotData } from '../../../../hooks/use_pivot_data';
|
||||||
import { SearchItems } from '../../../../hooks/use_search_items';
|
import { SearchItems } from '../../../../hooks/use_search_items';
|
||||||
|
|
||||||
|
|
|
@ -23,7 +23,7 @@ import {
|
||||||
EuiTitle,
|
EuiTitle,
|
||||||
} from '@elastic/eui';
|
} from '@elastic/eui';
|
||||||
|
|
||||||
import { TransformId } from '../../../../../../common';
|
import { TransformId } from '../../../../../../common/types/transform';
|
||||||
|
|
||||||
import {
|
import {
|
||||||
useRefreshTransformList,
|
useRefreshTransformList,
|
||||||
|
@ -189,7 +189,11 @@ export const TransformList: FC<Props> = ({
|
||||||
</EuiButtonEmpty>
|
</EuiButtonEmpty>
|
||||||
</div>,
|
</div>,
|
||||||
<div key="stopAction" className="transform__BulkActionItem">
|
<div key="stopAction" className="transform__BulkActionItem">
|
||||||
<EuiButtonEmpty onClick={() => stopTransforms(transformSelection)}>
|
<EuiButtonEmpty
|
||||||
|
onClick={() =>
|
||||||
|
stopTransforms(transformSelection.map((t) => ({ id: t.id, state: t.stats.state })))
|
||||||
|
}
|
||||||
|
>
|
||||||
<StopActionName items={transformSelection} />
|
<StopActionName items={transformSelection} />
|
||||||
</EuiButtonEmpty>
|
</EuiButtonEmpty>
|
||||||
</div>,
|
</div>,
|
||||||
|
|
|
@ -16,8 +16,8 @@ import {
|
||||||
} from '@elastic/eui';
|
} from '@elastic/eui';
|
||||||
import { i18n } from '@kbn/i18n';
|
import { i18n } from '@kbn/i18n';
|
||||||
import { TermClause, FieldClause, Value } from './common';
|
import { TermClause, FieldClause, Value } from './common';
|
||||||
import { TRANSFORM_STATE } from '../../../../../../common';
|
import { TRANSFORM_MODE, TRANSFORM_STATE } from '../../../../../../common/constants';
|
||||||
import { TRANSFORM_MODE, TransformListRow } from '../../../../common';
|
import { TransformListRow } from '../../../../common';
|
||||||
import { getTaskStateBadge } from './use_columns';
|
import { getTaskStateBadge } from './use_columns';
|
||||||
|
|
||||||
const filters: SearchFilterConfig[] = [
|
const filters: SearchFilterConfig[] = [
|
||||||
|
|
|
@ -7,9 +7,9 @@
|
||||||
import React, { FC } from 'react';
|
import React, { FC } from 'react';
|
||||||
import { i18n } from '@kbn/i18n';
|
import { i18n } from '@kbn/i18n';
|
||||||
|
|
||||||
import { TRANSFORM_STATE } from '../../../../../../common';
|
import { TRANSFORM_MODE, TRANSFORM_STATE } from '../../../../../../common/constants';
|
||||||
|
|
||||||
import { TRANSFORM_MODE, TransformListRow } from '../../../../common';
|
import { TransformListRow } from '../../../../common';
|
||||||
|
|
||||||
import { StatsBar, TransformStatsBarStats } from '../stats_bar';
|
import { StatsBar, TransformStatsBarStats } from '../stats_bar';
|
||||||
|
|
||||||
|
|
|
@ -22,24 +22,21 @@ import {
|
||||||
RIGHT_ALIGNMENT,
|
RIGHT_ALIGNMENT,
|
||||||
} from '@elastic/eui';
|
} from '@elastic/eui';
|
||||||
|
|
||||||
import { TransformId, TRANSFORM_STATE } from '../../../../../../common';
|
import { TransformId } from '../../../../../../common/types/transform';
|
||||||
|
import { TransformStats } from '../../../../../../common/types/transform_stats';
|
||||||
|
import { TRANSFORM_STATE } from '../../../../../../common/constants';
|
||||||
|
|
||||||
import {
|
import { getTransformProgress, TransformListRow, TRANSFORM_LIST_COLUMN } from '../../../../common';
|
||||||
getTransformProgress,
|
|
||||||
TransformListRow,
|
|
||||||
TransformStats,
|
|
||||||
TRANSFORM_LIST_COLUMN,
|
|
||||||
} from '../../../../common';
|
|
||||||
import { useActions } from './use_actions';
|
import { useActions } from './use_actions';
|
||||||
|
|
||||||
enum STATE_COLOR {
|
const STATE_COLOR = {
|
||||||
aborting = 'warning',
|
aborting: 'warning',
|
||||||
failed = 'danger',
|
failed: 'danger',
|
||||||
indexing = 'primary',
|
indexing: 'primary',
|
||||||
started = 'primary',
|
started: 'primary',
|
||||||
stopped = 'hollow',
|
stopped: 'hollow',
|
||||||
stopping = 'hollow',
|
stopping: 'hollow',
|
||||||
}
|
} as const;
|
||||||
|
|
||||||
export const getTaskStateBadge = (
|
export const getTaskStateBadge = (
|
||||||
state: TransformStats['state'],
|
state: TransformStats['state'],
|
||||||
|
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Add table
Add a link
Reference in a new issue