mirror of
https://github.com/elastic/kibana.git
synced 2025-04-24 17:59:23 -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,
|
||||
EsSorting,
|
||||
RenderCellValue,
|
||||
SearchResponse7,
|
||||
UseDataGridReturnType,
|
||||
UseIndexDataReturnType,
|
||||
} from './types';
|
||||
|
|
|
@ -5,7 +5,6 @@
|
|||
*/
|
||||
|
||||
import { Dispatch, SetStateAction } from 'react';
|
||||
import { SearchResponse } from 'elasticsearch';
|
||||
|
||||
import { EuiDataGridPaginationProps, EuiDataGridSorting, EuiDataGridColumn } from '@elastic/eui';
|
||||
|
||||
|
@ -43,16 +42,6 @@ export type EsSorting = Dictionary<{
|
|||
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
|
||||
extends Pick<
|
||||
UseDataGridReturnType,
|
||||
|
|
|
@ -4,9 +4,10 @@
|
|||
* 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 { EsSorting, SearchResponse7, UseDataGridReturnType } from '../../components/data_grid';
|
||||
import { EsSorting, UseDataGridReturnType } from '../../components/data_grid';
|
||||
import { ml } from '../../services/ml_api_service';
|
||||
|
||||
import { isKeywordAndTextType } from '../common/fields';
|
||||
|
|
|
@ -22,9 +22,9 @@ import {
|
|||
useDataGrid,
|
||||
useRenderCellValue,
|
||||
EsSorting,
|
||||
SearchResponse7,
|
||||
UseIndexDataReturnType,
|
||||
} from '../../../../components/data_grid';
|
||||
import type { SearchResponse7 } from '../../../../../../common/types/es_client';
|
||||
import { extractErrorMessage } from '../../../../../../common/util/errors';
|
||||
import { INDEX_STATUS } from '../../../common/analytics';
|
||||
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'];
|
||||
|
||||
// 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,
|
||||
EsSorting,
|
||||
RenderCellValue,
|
||||
SearchResponse7,
|
||||
UseDataGridReturnType,
|
||||
UseIndexDataReturnType,
|
||||
INDEX_STATUS,
|
||||
|
|
|
@ -6,7 +6,7 @@
|
|||
|
||||
import { composeValidators, patternValidator } from '../../../../ml/public';
|
||||
|
||||
export type AggName = string;
|
||||
import { AggName } from '../../../common/types/aggregations';
|
||||
|
||||
export function isAggName(arg: any): arg is AggName {
|
||||
// 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.
|
||||
*/
|
||||
|
||||
import { PIVOT_SUPPORTED_AGGS } from '../../../common/types/pivot_aggs';
|
||||
|
||||
import {
|
||||
getPreviewRequestBody,
|
||||
getPreviewTransformRequestBody,
|
||||
PivotAggsConfig,
|
||||
PivotGroupByConfig,
|
||||
PIVOT_SUPPORTED_AGGS,
|
||||
PIVOT_SUPPORTED_GROUP_BY_AGGS,
|
||||
SimpleQuery,
|
||||
} from '../common';
|
||||
|
@ -35,7 +36,12 @@ describe('Transform: Data Grid', () => {
|
|||
aggName: 'the-agg-agg-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);
|
||||
|
||||
expect(pivotPreviewDevConsoleStatement).toBe(`POST _transform/_preview
|
||||
|
|
|
@ -4,12 +4,13 @@
|
|||
* 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 { PreviewRequestBody } from './transform';
|
||||
|
||||
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`;
|
||||
};
|
||||
|
||||
|
|
|
@ -5,10 +5,10 @@
|
|||
*/
|
||||
|
||||
import { Dictionary } from '../../../common/types/common';
|
||||
import { EsFieldName } from '../../../common/types/fields';
|
||||
|
||||
export type EsId = string;
|
||||
export type EsDocSource = Dictionary<any>;
|
||||
export type EsFieldName = string;
|
||||
|
||||
export interface EsDoc extends Dictionary<any> {
|
||||
_id: EsId;
|
||||
|
|
|
@ -4,7 +4,7 @@
|
|||
* you may not use this file except in compliance with the Elastic License.
|
||||
*/
|
||||
|
||||
export { AggName, isAggName } from './aggregations';
|
||||
export { isAggName } from './aggregations';
|
||||
export {
|
||||
getIndexDevConsoleStatement,
|
||||
getPivotPreviewDevConsoleStatement,
|
||||
|
@ -17,44 +17,28 @@ export {
|
|||
toggleSelectedField,
|
||||
EsDoc,
|
||||
EsDocSource,
|
||||
EsFieldName,
|
||||
} from './fields';
|
||||
export { DropDownLabel, DropDownOption, Label } from './dropdown';
|
||||
export {
|
||||
isTransformIdValid,
|
||||
refreshTransformList$,
|
||||
useRefreshTransformList,
|
||||
CreateRequestBody,
|
||||
PreviewRequestBody,
|
||||
TransformPivotConfig,
|
||||
IndexName,
|
||||
IndexPattern,
|
||||
REFRESH_TRANSFORM_LIST_STATE,
|
||||
} from './transform';
|
||||
export { TRANSFORM_LIST_COLUMN, TransformListAction, TransformListRow } from './transform_list';
|
||||
export {
|
||||
getTransformProgress,
|
||||
isCompletedBatchTransform,
|
||||
isTransformStats,
|
||||
TransformStats,
|
||||
TRANSFORM_MODE,
|
||||
} from './transform_stats';
|
||||
export { getTransformProgress, isCompletedBatchTransform } from './transform_stats';
|
||||
export { getDiscoverUrl } from './navigation';
|
||||
export { GetTransformsResponse, PreviewData, PreviewMappings } from './pivot_preview';
|
||||
export {
|
||||
getEsAggFromAggConfig,
|
||||
isPivotAggsConfigWithUiSupport,
|
||||
isPivotAggsConfigPercentiles,
|
||||
PERCENTILES_AGG_DEFAULT_PERCENTS,
|
||||
PivotAgg,
|
||||
PivotAggDict,
|
||||
PivotAggsConfig,
|
||||
PivotAggsConfigDict,
|
||||
PivotAggsConfigBase,
|
||||
PivotAggsConfigWithUiSupport,
|
||||
PivotAggsConfigWithUiSupportDict,
|
||||
pivotAggsFieldSupport,
|
||||
PIVOT_SUPPORTED_AGGS,
|
||||
} from './pivot_aggs';
|
||||
export {
|
||||
dateHistogramIntervalFormatRegex,
|
||||
|
@ -65,25 +49,19 @@ export {
|
|||
isGroupByHistogram,
|
||||
isGroupByTerms,
|
||||
pivotGroupByFieldSupport,
|
||||
DateHistogramAgg,
|
||||
GenericAgg,
|
||||
GroupByConfigWithInterval,
|
||||
GroupByConfigWithUiSupport,
|
||||
HistogramAgg,
|
||||
PivotGroupBy,
|
||||
PivotGroupByConfig,
|
||||
PivotGroupByDict,
|
||||
PivotGroupByConfigDict,
|
||||
PivotGroupByConfigWithUiSupportDict,
|
||||
PivotSupportedGroupByAggs,
|
||||
PivotSupportedGroupByAggsWithInterval,
|
||||
PIVOT_SUPPORTED_GROUP_BY_AGGS,
|
||||
TermsAgg,
|
||||
} from './pivot_group_by';
|
||||
export {
|
||||
defaultQuery,
|
||||
getPreviewRequestBody,
|
||||
getCreateRequestBody,
|
||||
getPreviewTransformRequestBody,
|
||||
getCreateTransformRequestBody,
|
||||
getPivotQuery,
|
||||
isDefaultQuery,
|
||||
isMatchAllQuery,
|
||||
|
|
|
@ -5,31 +5,22 @@
|
|||
*/
|
||||
|
||||
import { FC } from 'react';
|
||||
import { Dictionary } from '../../../common/types/common';
|
||||
|
||||
import { KBN_FIELD_TYPES } from '../../../../../../src/plugins/data/common';
|
||||
|
||||
import { AggName } from './aggregations';
|
||||
import { EsFieldName } from './fields';
|
||||
import type { AggName } from '../../../common/types/aggregations';
|
||||
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 { 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 {
|
||||
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 pivotAggsFieldSupport = {
|
||||
|
@ -69,16 +60,6 @@ export const pivotAggsFieldSupport = {
|
|||
[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
|
||||
*/
|
||||
|
|
|
@ -4,12 +4,12 @@
|
|||
* 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 { EsFieldName } from '../../../common/types/fields';
|
||||
import { GenericAgg } from '../../../common/types/pivot_group_by';
|
||||
import { KBN_FIELD_TYPES } from '../../../../../../src/plugins/data/common';
|
||||
|
||||
import { AggName } from './aggregations';
|
||||
import { EsFieldName } from './fields';
|
||||
|
||||
export enum PIVOT_SUPPORTED_GROUP_BY_AGGS {
|
||||
DATE_HISTOGRAM = 'date_histogram',
|
||||
HISTOGRAM = 'histogram',
|
||||
|
@ -106,31 +106,6 @@ export function isPivotGroupByConfigWithUiSupport(arg: any): arg is GroupByConfi
|
|||
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 {
|
||||
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.
|
||||
*/
|
||||
|
||||
import { PIVOT_SUPPORTED_AGGS } from '../../../common/types/pivot_aggs';
|
||||
|
||||
import { PivotGroupByConfig } from '../common';
|
||||
|
||||
import { StepDefineExposedState } from '../sections/create_transform/components/step_define';
|
||||
import { StepDetailsExposedState } from '../sections/create_transform/components/step_details/step_details_form';
|
||||
|
||||
import { PIVOT_SUPPORTED_GROUP_BY_AGGS } from './pivot_group_by';
|
||||
import { PivotAggsConfig, PIVOT_SUPPORTED_AGGS } from './pivot_aggs';
|
||||
import { PivotAggsConfig } from './pivot_aggs';
|
||||
import {
|
||||
defaultQuery,
|
||||
getPreviewRequestBody,
|
||||
getCreateRequestBody,
|
||||
getPreviewTransformRequestBody,
|
||||
getCreateTransformRequestBody,
|
||||
getPivotQuery,
|
||||
isDefaultQuery,
|
||||
isMatchAllQuery,
|
||||
|
@ -55,7 +57,7 @@ describe('Transform: Common', () => {
|
|||
});
|
||||
});
|
||||
|
||||
test('getPreviewRequestBody()', () => {
|
||||
test('getPreviewTransformRequestBody()', () => {
|
||||
const query = getPivotQuery('the-query');
|
||||
const groupBy: PivotGroupByConfig[] = [
|
||||
{
|
||||
|
@ -73,7 +75,7 @@ describe('Transform: Common', () => {
|
|||
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({
|
||||
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 groupBy: PivotGroupByConfig[] = [
|
||||
{
|
||||
|
@ -105,7 +107,7 @@ describe('Transform: Common', () => {
|
|||
dropDownName: 'the-agg-drop-down-name',
|
||||
},
|
||||
];
|
||||
const request = getPreviewRequestBody(
|
||||
const request = getPreviewTransformRequestBody(
|
||||
'the-index-pattern-title,the-other-title',
|
||||
query,
|
||||
groupBy,
|
||||
|
@ -124,7 +126,7 @@ describe('Transform: Common', () => {
|
|||
});
|
||||
});
|
||||
|
||||
test('getCreateRequestBody()', () => {
|
||||
test('getCreateTransformRequestBody()', () => {
|
||||
const groupBy: PivotGroupByConfig = {
|
||||
agg: PIVOT_SUPPORTED_GROUP_BY_AGGS.TERMS,
|
||||
field: 'the-group-by-field',
|
||||
|
@ -160,7 +162,7 @@ describe('Transform: Common', () => {
|
|||
valid: true,
|
||||
};
|
||||
|
||||
const request = getCreateRequestBody(
|
||||
const request = getCreateTransformRequestBody(
|
||||
'the-index-pattern-title',
|
||||
pivotState,
|
||||
transformDetailsState
|
||||
|
|
|
@ -4,15 +4,25 @@
|
|||
* 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 { SavedSearchQuery } from '../hooks/use_search_items';
|
||||
|
||||
import { StepDefineExposedState } from '../sections/create_transform/components/step_define';
|
||||
import { StepDetailsExposedState } from '../sections/create_transform/components/step_details/step_details_form';
|
||||
|
||||
import { IndexPattern } from '../../../../../../src/plugins/data/public';
|
||||
import type { SavedSearchQuery } from '../hooks/use_search_items';
|
||||
import type { StepDefineExposedState } from '../sections/create_transform/components/step_define';
|
||||
import type { StepDetailsExposedState } from '../sections/create_transform/components/step_details/step_details_form';
|
||||
|
||||
import {
|
||||
getEsAggFromAggConfig,
|
||||
|
@ -24,8 +34,6 @@ import {
|
|||
} from '../common';
|
||||
|
||||
import { PivotAggsConfig } from './pivot_aggs';
|
||||
import { DateHistogramAgg, HistogramAgg, TermsAgg } from './pivot_group_by';
|
||||
import { PreviewRequestBody, CreateRequestBody } from './transform';
|
||||
|
||||
export interface SimpleQuery {
|
||||
query_string: {
|
||||
|
@ -63,17 +71,18 @@ export function isDefaultQuery(query: PivotQuery): boolean {
|
|||
return isSimpleQuery(query) && query.query_string.query === '*';
|
||||
}
|
||||
|
||||
export function getPreviewRequestBody(
|
||||
export function getPreviewTransformRequestBody(
|
||||
indexPatternTitle: IndexPattern['title'],
|
||||
query: PivotQuery,
|
||||
groupBy: PivotGroupByConfig[],
|
||||
aggs: PivotAggsConfig[]
|
||||
): PreviewRequestBody {
|
||||
): PostTransformsPreviewRequestSchema {
|
||||
const index = indexPatternTitle.split(',').map((name: string) => name.trim());
|
||||
|
||||
const request: PreviewRequestBody = {
|
||||
const request: PostTransformsPreviewRequestSchema = {
|
||||
source: {
|
||||
index,
|
||||
...(!isDefaultQuery(query) && !isMatchAllQuery(query) ? { query } : {}),
|
||||
},
|
||||
pivot: {
|
||||
group_by: {},
|
||||
|
@ -81,10 +90,6 @@ export function getPreviewRequestBody(
|
|||
},
|
||||
};
|
||||
|
||||
if (!isDefaultQuery(query) && !isMatchAllQuery(query)) {
|
||||
request.source.query = query;
|
||||
}
|
||||
|
||||
groupBy.forEach((g) => {
|
||||
if (isGroupByTerms(g)) {
|
||||
const termsAgg: TermsAgg = {
|
||||
|
@ -125,37 +130,41 @@ export function getPreviewRequestBody(
|
|||
return request;
|
||||
}
|
||||
|
||||
export function getCreateRequestBody(
|
||||
export const getCreateTransformRequestBody = (
|
||||
indexPatternTitle: IndexPattern['title'],
|
||||
pivotState: StepDefineExposedState,
|
||||
transformDetailsState: StepDetailsExposedState
|
||||
): CreateRequestBody {
|
||||
const request: CreateRequestBody = {
|
||||
...getPreviewRequestBody(
|
||||
indexPatternTitle,
|
||||
getPivotQuery(pivotState.searchQuery),
|
||||
dictionaryToArray(pivotState.groupByList),
|
||||
dictionaryToArray(pivotState.aggList)
|
||||
),
|
||||
// conditionally add optional description
|
||||
...(transformDetailsState.transformDescription !== ''
|
||||
? { description: transformDetailsState.transformDescription }
|
||||
: {}),
|
||||
dest: {
|
||||
index: transformDetailsState.destinationIndex,
|
||||
},
|
||||
// conditionally add continuous mode config
|
||||
...(transformDetailsState.isContinuousModeEnabled
|
||||
? {
|
||||
sync: {
|
||||
time: {
|
||||
field: transformDetailsState.continuousModeDateField,
|
||||
delay: transformDetailsState.continuousModeDelay,
|
||||
},
|
||||
): PutTransformsRequestSchema => ({
|
||||
...getPreviewTransformRequestBody(
|
||||
indexPatternTitle,
|
||||
getPivotQuery(pivotState.searchQuery),
|
||||
dictionaryToArray(pivotState.groupByList),
|
||||
dictionaryToArray(pivotState.aggList)
|
||||
),
|
||||
// conditionally add optional description
|
||||
...(transformDetailsState.transformDescription !== ''
|
||||
? { description: transformDetailsState.transformDescription }
|
||||
: {}),
|
||||
dest: {
|
||||
index: transformDetailsState.destinationIndex,
|
||||
},
|
||||
// conditionally add continuous mode config
|
||||
...(transformDetailsState.isContinuousModeEnabled
|
||||
? {
|
||||
sync: {
|
||||
time: {
|
||||
field: transformDetailsState.continuousModeDateField,
|
||||
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 { Subscription } from 'rxjs';
|
||||
|
||||
import { TransformId } from '../../../common';
|
||||
|
||||
import { PivotAggDict } from './pivot_aggs';
|
||||
import { PivotGroupByDict } from './pivot_group_by';
|
||||
|
||||
export type IndexName = string;
|
||||
export type IndexPattern = string;
|
||||
import { TransformId } from '../../../common/types/transform';
|
||||
|
||||
// Transform name must contain lowercase alphanumeric (a-z and 0-9), hyphens or underscores;
|
||||
// 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);
|
||||
}
|
||||
|
||||
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 {
|
||||
ERROR = 'error',
|
||||
IDLE = 'idle',
|
||||
|
|
|
@ -6,9 +6,8 @@
|
|||
|
||||
import { EuiTableActionsColumnType } from '@elastic/eui';
|
||||
|
||||
import { TransformId } from '../../../common';
|
||||
import { TransformPivotConfig } from './transform';
|
||||
import { TransformStats } from './transform_stats';
|
||||
import { TransformId, TransformPivotConfig } from '../../../common/types/transform';
|
||||
import { TransformStats } from '../../../common/types/transform_stats';
|
||||
|
||||
// Used to pass on attribute names to table columns
|
||||
export enum TRANSFORM_LIST_COLUMN {
|
||||
|
|
|
@ -4,64 +4,10 @@
|
|||
* 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';
|
||||
|
||||
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) {
|
||||
if (isCompletedBatchTransform(item)) {
|
||||
return 100;
|
||||
|
|
|
@ -4,66 +4,161 @@
|
|||
* 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 = () => ({
|
||||
getTransforms(transformId?: TransformId): Promise<any> {
|
||||
return new Promise((resolve, reject) => {
|
||||
resolve([]);
|
||||
async getTransform(
|
||||
transformId: TransformId
|
||||
): 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) => {
|
||||
resolve([]);
|
||||
});
|
||||
async getEsIndices(): Promise<EsIndex[] | HttpFetchError> {
|
||||
return Promise.resolve([]);
|
||||
},
|
||||
createTransform(transformId: TransformId, transformConfig: any): Promise<any> {
|
||||
return new Promise((resolve, reject) => {
|
||||
resolve([]);
|
||||
});
|
||||
},
|
||||
deleteTransforms(transformsInfo: TransformEndpointRequest[]) {
|
||||
return new Promise((resolve, reject) => {
|
||||
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([]);
|
||||
});
|
||||
async getHistogramsForFields(
|
||||
indexPatternTitle: string,
|
||||
fields: FieldHistogramRequestConfig[],
|
||||
query: string | SavedSearchQuery,
|
||||
samplerShardSize = DEFAULT_SAMPLER_SHARD_SIZE
|
||||
): Promise<FieldHistogramsResponseSchema | HttpFetchError> {
|
||||
return Promise.resolve([]);
|
||||
},
|
||||
});
|
||||
|
||||
|
|
|
@ -6,20 +6,43 @@
|
|||
|
||||
import { useMemo } from 'react';
|
||||
|
||||
import { HttpFetchError } from 'kibana/public';
|
||||
|
||||
import { KBN_FIELD_TYPES } from '../../../../../../src/plugins/data/public';
|
||||
|
||||
import {
|
||||
TransformId,
|
||||
TransformEndpointRequest,
|
||||
TransformEndpointResult,
|
||||
DeleteTransformEndpointResult,
|
||||
} from '../../../common';
|
||||
import type { GetTransformsAuditMessagesResponseSchema } from '../../../common/api_schemas/audit_messages';
|
||||
import type {
|
||||
DeleteTransformsRequestSchema,
|
||||
DeleteTransformsResponseSchema,
|
||||
} from '../../../common/api_schemas/delete_transforms';
|
||||
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 { EsIndex } from '../../../common/types/es_index';
|
||||
import type { SearchResponse7 } from '../../../common/shared_imports';
|
||||
|
||||
import { useAppDependencies } from '../app_dependencies';
|
||||
import { GetTransformsResponse, PreviewRequestBody } from '../common';
|
||||
|
||||
import { EsIndex } from './use_api_types';
|
||||
import { SavedSearchQuery } from './use_search_items';
|
||||
|
||||
// Default sampler shard size used for field histograms
|
||||
|
@ -35,81 +58,146 @@ export const useApi = () => {
|
|||
|
||||
return useMemo(
|
||||
() => ({
|
||||
getTransforms(transformId?: TransformId): Promise<any> {
|
||||
const transformIdString = transformId !== undefined ? `/${transformId}` : '';
|
||||
return http.get(`${API_BASE_PATH}transforms${transformIdString}`);
|
||||
},
|
||||
getTransformsStats(transformId?: TransformId): Promise<any> {
|
||||
if (transformId !== undefined) {
|
||||
return http.get(`${API_BASE_PATH}transforms/${transformId}/_stats`);
|
||||
async getTransform(
|
||||
transformId: TransformId
|
||||
): Promise<GetTransformsResponseSchema | HttpFetchError> {
|
||||
try {
|
||||
return await http.get(`${API_BASE_PATH}transforms/${transformId}`);
|
||||
} catch (e) {
|
||||
return e;
|
||||
}
|
||||
|
||||
return http.get(`${API_BASE_PATH}transforms/_stats`);
|
||||
},
|
||||
createTransform(transformId: TransformId, transformConfig: any): Promise<any> {
|
||||
return http.put(`${API_BASE_PATH}transforms/${transformId}`, {
|
||||
body: JSON.stringify(transformConfig),
|
||||
});
|
||||
async getTransforms(): Promise<GetTransformsResponseSchema | HttpFetchError> {
|
||||
try {
|
||||
return await http.get(`${API_BASE_PATH}transforms`);
|
||||
} catch (e) {
|
||||
return e;
|
||||
}
|
||||
},
|
||||
updateTransform(transformId: TransformId, transformConfig: any): Promise<any> {
|
||||
return http.post(`${API_BASE_PATH}transforms/${transformId}/_update`, {
|
||||
body: JSON.stringify(transformConfig),
|
||||
});
|
||||
async getTransformStats(
|
||||
transformId: TransformId
|
||||
): Promise<GetTransformsStatsResponseSchema | HttpFetchError> {
|
||||
try {
|
||||
return await http.get(`${API_BASE_PATH}transforms/${transformId}/_stats`);
|
||||
} catch (e) {
|
||||
return e;
|
||||
}
|
||||
},
|
||||
deleteTransforms(
|
||||
transformsInfo: TransformEndpointRequest[],
|
||||
deleteDestIndex: boolean | undefined,
|
||||
deleteDestIndexPattern: boolean | undefined,
|
||||
forceDelete: boolean
|
||||
): Promise<DeleteTransformEndpointResult> {
|
||||
return http.post(`${API_BASE_PATH}delete_transforms`, {
|
||||
body: JSON.stringify({
|
||||
transformsInfo,
|
||||
deleteDestIndex,
|
||||
deleteDestIndexPattern,
|
||||
forceDelete,
|
||||
}),
|
||||
});
|
||||
async getTransformsStats(): Promise<GetTransformsStatsResponseSchema | HttpFetchError> {
|
||||
try {
|
||||
return await http.get(`${API_BASE_PATH}transforms/_stats`);
|
||||
} catch (e) {
|
||||
return e;
|
||||
}
|
||||
},
|
||||
getTransformsPreview(obj: PreviewRequestBody): Promise<GetTransformsResponse> {
|
||||
return http.post(`${API_BASE_PATH}transforms/_preview`, {
|
||||
body: JSON.stringify(obj),
|
||||
});
|
||||
async createTransform(
|
||||
transformId: TransformId,
|
||||
transformConfig: PutTransformsRequestSchema
|
||||
): Promise<PutTransformsResponseSchema | HttpFetchError> {
|
||||
try {
|
||||
return await http.put(`${API_BASE_PATH}transforms/${transformId}`, {
|
||||
body: JSON.stringify(transformConfig),
|
||||
});
|
||||
} catch (e) {
|
||||
return e;
|
||||
}
|
||||
},
|
||||
startTransforms(
|
||||
transformsInfo: TransformEndpointRequest[]
|
||||
): Promise<TransformEndpointResult> {
|
||||
return http.post(`${API_BASE_PATH}start_transforms`, {
|
||||
body: JSON.stringify(transformsInfo),
|
||||
});
|
||||
async updateTransform(
|
||||
transformId: TransformId,
|
||||
transformConfig: PostTransformsUpdateRequestSchema
|
||||
): Promise<PostTransformsUpdateResponseSchema | HttpFetchError> {
|
||||
try {
|
||||
return await http.post(`${API_BASE_PATH}transforms/${transformId}/_update`, {
|
||||
body: JSON.stringify(transformConfig),
|
||||
});
|
||||
} catch (e) {
|
||||
return e;
|
||||
}
|
||||
},
|
||||
stopTransforms(transformsInfo: TransformEndpointRequest[]): Promise<TransformEndpointResult> {
|
||||
return http.post(`${API_BASE_PATH}stop_transforms`, {
|
||||
body: JSON.stringify(transformsInfo),
|
||||
});
|
||||
async deleteTransforms(
|
||||
reqBody: DeleteTransformsRequestSchema
|
||||
): 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> {
|
||||
return http.get(`${API_BASE_PATH}transforms/${transformId}/messages`);
|
||||
async getTransformsPreview(
|
||||
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> {
|
||||
return http.post(`${API_BASE_PATH}es_search`, { body: JSON.stringify(payload) });
|
||||
async startTransforms(
|
||||
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[]> {
|
||||
return http.get(`/api/index_management/indices`);
|
||||
async stopTransforms(
|
||||
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,
|
||||
fields: FieldHistogramRequestConfig[],
|
||||
query: string | SavedSearchQuery,
|
||||
samplerShardSize = DEFAULT_SAMPLER_SHARD_SIZE
|
||||
) {
|
||||
return http.post(`${API_BASE_PATH}field_histograms/${indexPatternTitle}`, {
|
||||
body: JSON.stringify({
|
||||
query,
|
||||
fields,
|
||||
samplerShardSize,
|
||||
}),
|
||||
});
|
||||
): Promise<FieldHistogramsResponseSchema | HttpFetchError> {
|
||||
try {
|
||||
return await http.post(`${API_BASE_PATH}field_histograms/${indexPatternTitle}`, {
|
||||
body: JSON.stringify({
|
||||
query,
|
||||
fields,
|
||||
samplerShardSize,
|
||||
}),
|
||||
});
|
||||
} catch (e) {
|
||||
return e;
|
||||
}
|
||||
},
|
||||
}),
|
||||
[http]
|
||||
|
|
|
@ -7,11 +7,11 @@
|
|||
import React, { useCallback, useEffect, useState } from 'react';
|
||||
import { i18n } from '@kbn/i18n';
|
||||
import { toMountPoint } from '../../../../../../src/plugins/kibana_react/public';
|
||||
import {
|
||||
DeleteTransformEndpointResult,
|
||||
import type {
|
||||
DeleteTransformStatus,
|
||||
TransformEndpointRequest,
|
||||
} from '../../../common';
|
||||
DeleteTransformsRequestSchema,
|
||||
} from '../../../common/api_schemas/delete_transforms';
|
||||
import { isDeleteTransformsResponseSchema } from '../../../common/api_schemas/type_guards';
|
||||
import { extractErrorMessage } from '../../shared_imports';
|
||||
import { getErrorMessage } from '../../../common/utils/errors';
|
||||
import { useAppDependencies, useToastNotifications } from '../app_dependencies';
|
||||
|
@ -109,161 +109,10 @@ export const useDeleteTransforms = () => {
|
|||
const toastNotifications = useToastNotifications();
|
||||
const api = useApi();
|
||||
|
||||
return async (
|
||||
transforms: TransformListRow[],
|
||||
shouldDeleteDestIndex: boolean,
|
||||
shouldDeleteDestIndexPattern: boolean,
|
||||
shouldForceDelete = false
|
||||
) => {
|
||||
const transformsInfo: TransformEndpointRequest[] = transforms.map((tf) => ({
|
||||
id: tf.config.id,
|
||||
state: tf.stats.state,
|
||||
}));
|
||||
return async (reqBody: DeleteTransformsRequestSchema) => {
|
||||
const results = await api.deleteTransforms(reqBody);
|
||||
|
||||
try {
|
||||
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) {
|
||||
if (!isDeleteTransformsResponseSchema(results)) {
|
||||
toastNotifications.addDanger({
|
||||
title: i18n.translate('xpack.transform.transformList.deleteTransformGenericErrorMessage', {
|
||||
defaultMessage: 'An error occurred calling the API endpoint to delete transforms.',
|
||||
|
@ -272,10 +121,145 @@ export const useDeleteTransforms = () => {
|
|||
<ToastNotificationText
|
||||
previewTextLength={50}
|
||||
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.
|
||||
*/
|
||||
|
||||
import { HttpFetchError } from 'src/core/public';
|
||||
|
||||
import {
|
||||
TransformListRow,
|
||||
TransformStats,
|
||||
TRANSFORM_MODE,
|
||||
isTransformStats,
|
||||
TransformPivotConfig,
|
||||
refreshTransformList$,
|
||||
REFRESH_TRANSFORM_LIST_STATE,
|
||||
} from '../common';
|
||||
isGetTransformsResponseSchema,
|
||||
isGetTransformsStatsResponseSchema,
|
||||
} from '../../../common/api_schemas/type_guards';
|
||||
import { TRANSFORM_MODE } from '../../../common/constants';
|
||||
import { isTransformStats } from '../../../common/types/transform_stats';
|
||||
|
||||
import { TransformListRow, refreshTransformList$, REFRESH_TRANSFORM_LIST_STATE } from '../common';
|
||||
|
||||
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 const useGetTransforms = (
|
||||
setTransforms: React.Dispatch<React.SetStateAction<TransformListRow[]>>,
|
||||
setErrorMessage: React.Dispatch<
|
||||
React.SetStateAction<GetTransformsStatsResponseError | undefined>
|
||||
>,
|
||||
setErrorMessage: React.Dispatch<React.SetStateAction<HttpFetchError | undefined>>,
|
||||
setIsInitialized: React.Dispatch<React.SetStateAction<boolean>>,
|
||||
blockRefresh: boolean
|
||||
): GetTransforms => {
|
||||
|
@ -66,45 +38,57 @@ export const useGetTransforms = (
|
|||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
const transformConfigs: GetTransformsResponse = await api.getTransforms();
|
||||
const transformStats: GetTransformsStatsResponse = await api.getTransformsStats();
|
||||
const transformConfigs = await api.getTransforms();
|
||||
const transformStats = await api.getTransformsStats();
|
||||
|
||||
const tableRows = transformConfigs.transforms.reduce((reducedtableRows, config) => {
|
||||
const stats = isGetTransformsStatsResponseOk(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);
|
||||
} catch (e) {
|
||||
if (
|
||||
!isGetTransformsResponseSchema(transformConfigs) ||
|
||||
!isGetTransformsStatsResponseSchema(transformStats)
|
||||
) {
|
||||
// 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.
|
||||
refreshTransformList$.next(REFRESH_TRANSFORM_LIST_STATE.ERROR);
|
||||
refreshTransformList$.next(REFRESH_TRANSFORM_LIST_STATE.IDLE);
|
||||
setTransforms([]);
|
||||
setErrorMessage(e);
|
||||
|
||||
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--;
|
||||
|
||||
if (concurrentLoads > 0) {
|
||||
|
|
|
@ -8,6 +8,11 @@ import { useEffect } from 'react';
|
|||
|
||||
import { EuiDataGridColumn } from '@elastic/eui';
|
||||
|
||||
import {
|
||||
isEsSearchResponse,
|
||||
isFieldHistogramsResponseSchema,
|
||||
} from '../../../common/api_schemas/type_guards';
|
||||
|
||||
import {
|
||||
getFieldType,
|
||||
getDataGridSchemaFromKibanaFieldType,
|
||||
|
@ -16,7 +21,6 @@ import {
|
|||
useDataGrid,
|
||||
useRenderCellValue,
|
||||
EsSorting,
|
||||
SearchResponse7,
|
||||
UseIndexDataReturnType,
|
||||
INDEX_STATUS,
|
||||
} from '../../shared_imports';
|
||||
|
@ -29,8 +33,6 @@ import { useApi } from './use_api';
|
|||
|
||||
import { useToastNotifications } from '../app_dependencies';
|
||||
|
||||
type IndexSearchResponse = SearchResponse7;
|
||||
|
||||
export const useIndexData = (
|
||||
indexPattern: SearchItems['indexPattern'],
|
||||
query: PivotQuery
|
||||
|
@ -90,37 +92,39 @@ export const useIndexData = (
|
|||
},
|
||||
};
|
||||
|
||||
try {
|
||||
const resp: IndexSearchResponse = await api.esSearch(esSearchRequest);
|
||||
const resp = await api.esSearch(esSearchRequest);
|
||||
|
||||
const docs = resp.hits.hits.map((d) => d._source);
|
||||
|
||||
setRowCount(resp.hits.total.value);
|
||||
setTableItems(docs);
|
||||
setStatus(INDEX_STATUS.LOADED);
|
||||
} catch (e) {
|
||||
setErrorMessage(getErrorMessage(e));
|
||||
if (!isEsSearchResponse(resp)) {
|
||||
setErrorMessage(getErrorMessage(resp));
|
||||
setStatus(INDEX_STATUS.ERROR);
|
||||
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 () {
|
||||
try {
|
||||
const columnChartsData = await api.getHistogramsForFields(
|
||||
indexPattern.title,
|
||||
columns
|
||||
.filter((cT) => dataGrid.visibleColumns.includes(cT.id))
|
||||
.map((cT) => ({
|
||||
fieldName: cT.id,
|
||||
type: getFieldType(cT.schema),
|
||||
})),
|
||||
isDefaultQuery(query) ? matchAllQuery : query
|
||||
);
|
||||
setColumnCharts(columnChartsData);
|
||||
} catch (e) {
|
||||
showDataGridColumnChartErrorMessageToast(e, toastNotifications);
|
||||
const columnChartsData = await api.getHistogramsForFields(
|
||||
indexPattern.title,
|
||||
columns
|
||||
.filter((cT) => dataGrid.visibleColumns.includes(cT.id))
|
||||
.map((cT) => ({
|
||||
fieldName: cT.id,
|
||||
type: getFieldType(cT.schema),
|
||||
})),
|
||||
isDefaultQuery(query) ? matchAllQuery : query
|
||||
);
|
||||
|
||||
if (!isFieldHistogramsResponseSchema(columnChartsData)) {
|
||||
showDataGridColumnChartErrorMessageToast(columnChartsData, toastNotifications);
|
||||
return;
|
||||
}
|
||||
|
||||
setColumnCharts(columnChartsData);
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
|
|
|
@ -13,11 +13,13 @@ import { i18n } from '@kbn/i18n';
|
|||
|
||||
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 { formatHumanReadableDateTimeSeconds } from '../../shared_imports';
|
||||
import { getNestedProperty } from '../../../common/utils/object_utils';
|
||||
|
||||
import {
|
||||
formatHumanReadableDateTimeSeconds,
|
||||
multiColumnSortFactory,
|
||||
useDataGrid,
|
||||
RenderCellValue,
|
||||
|
@ -27,12 +29,11 @@ import {
|
|||
import { getErrorMessage } from '../../../common/utils/errors';
|
||||
|
||||
import {
|
||||
getPreviewRequestBody,
|
||||
getPreviewTransformRequestBody,
|
||||
PivotAggsConfigDict,
|
||||
PivotGroupByConfigDict,
|
||||
PivotGroupByConfig,
|
||||
PivotQuery,
|
||||
PreviewMappings,
|
||||
PivotAggsConfig,
|
||||
} from '../common';
|
||||
|
||||
|
@ -74,21 +75,23 @@ export const usePivotData = (
|
|||
aggs: PivotAggsConfigDict,
|
||||
groupBy: PivotGroupByConfigDict
|
||||
): UseIndexDataReturnType => {
|
||||
const [previewMappings, setPreviewMappings] = useState<PreviewMappings>({ properties: {} });
|
||||
const [previewMappingsProperties, setPreviewMappingsProperties] = useState<
|
||||
PreviewMappingsProperties
|
||||
>({});
|
||||
const api = useApi();
|
||||
|
||||
const aggsArr = useMemo(() => dictionaryToArray(aggs), [aggs]);
|
||||
const groupByArr = useMemo(() => dictionaryToArray(groupBy), [groupBy]);
|
||||
|
||||
// Filters mapping properties of type `object`, which get returned for nested field parents.
|
||||
const columnKeys = Object.keys(previewMappings.properties).filter(
|
||||
(key) => previewMappings.properties[key].type !== 'object'
|
||||
const columnKeys = Object.keys(previewMappingsProperties).filter(
|
||||
(key) => previewMappingsProperties[key].type !== 'object'
|
||||
);
|
||||
columnKeys.sort(sortColumns(groupByArr));
|
||||
|
||||
// EuiDataGrid State
|
||||
const columns: EuiDataGridColumn[] = columnKeys.map((id) => {
|
||||
const field = previewMappings.properties[id];
|
||||
const field = previewMappingsProperties[id];
|
||||
|
||||
// Built-in values are ['boolean', 'currency', 'datetime', 'numeric', 'json']
|
||||
// To fall back to the default string schema it needs to be undefined.
|
||||
|
@ -159,28 +162,35 @@ export const usePivotData = (
|
|||
setNoDataMessage('');
|
||||
setStatus(INDEX_STATUS.LOADING);
|
||||
|
||||
try {
|
||||
const previewRequest = getPreviewRequestBody(indexPatternTitle, query, groupByArr, aggsArr);
|
||||
const resp = await api.getTransformsPreview(previewRequest);
|
||||
setTableItems(resp.preview);
|
||||
setRowCount(resp.preview.length);
|
||||
setPreviewMappings(resp.generated_dest_index.mappings);
|
||||
setStatus(INDEX_STATUS.LOADED);
|
||||
const previewRequest = getPreviewTransformRequestBody(
|
||||
indexPatternTitle,
|
||||
query,
|
||||
groupByArr,
|
||||
aggsArr
|
||||
);
|
||||
const resp = await api.getTransformsPreview(previewRequest);
|
||||
|
||||
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.',
|
||||
})
|
||||
);
|
||||
}
|
||||
} catch (e) {
|
||||
setErrorMessage(getErrorMessage(e));
|
||||
if (!isPostTransformsPreviewResponseSchema(resp)) {
|
||||
setErrorMessage(getErrorMessage(resp));
|
||||
setTableItems([]);
|
||||
setRowCount(0);
|
||||
setPreviewMappings({ properties: {} });
|
||||
setPreviewMappingsProperties({});
|
||||
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 (
|
||||
[ES_FIELD_TYPES.DATE, ES_FIELD_TYPES.DATE_NANOS].includes(
|
||||
previewMappings.properties[columnId].type
|
||||
previewMappingsProperties[columnId].type
|
||||
)
|
||||
) {
|
||||
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;
|
||||
};
|
||||
}, [pageData, pagination.pageIndex, pagination.pageSize, previewMappings.properties]);
|
||||
}, [pageData, pagination.pageIndex, pagination.pageSize, previewMappingsProperties]);
|
||||
|
||||
return {
|
||||
...dataGrid,
|
||||
|
|
|
@ -4,25 +4,45 @@
|
|||
* you may not use this file except in compliance with the Elastic License.
|
||||
*/
|
||||
|
||||
import React from 'react';
|
||||
|
||||
import { i18n } from '@kbn/i18n';
|
||||
|
||||
import { TransformEndpointRequest, TransformEndpointResult } from '../../../common';
|
||||
import { toMountPoint } from '../../../../../../src/plugins/kibana_react/public';
|
||||
|
||||
import { useToastNotifications } from '../app_dependencies';
|
||||
import { TransformListRow, refreshTransformList$, REFRESH_TRANSFORM_LIST_STATE } from '../common';
|
||||
import type { StartTransformsRequestSchema } from '../../../common/api_schemas/start_transforms';
|
||||
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';
|
||||
|
||||
export const useStartTransforms = () => {
|
||||
const deps = useAppDependencies();
|
||||
const toastNotifications = useToastNotifications();
|
||||
const api = useApi();
|
||||
|
||||
return async (transforms: TransformListRow[]) => {
|
||||
const transformsInfo: TransformEndpointRequest[] = transforms.map((tf) => ({
|
||||
id: tf.config.id,
|
||||
state: tf.stats.state,
|
||||
}));
|
||||
const results: TransformEndpointResult = await api.startTransforms(transformsInfo);
|
||||
return async (transformsInfo: StartTransformsRequestSchema) => {
|
||||
const results = await api.startTransforms(transformsInfo);
|
||||
|
||||
if (!isStartTransformsResponseSchema(results)) {
|
||||
toastNotifications.addDanger({
|
||||
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) {
|
||||
// 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.
|
||||
*/
|
||||
|
||||
import React from 'react';
|
||||
|
||||
import { i18n } from '@kbn/i18n';
|
||||
|
||||
import { TransformEndpointRequest, TransformEndpointResult } from '../../../common';
|
||||
import { toMountPoint } from '../../../../../../src/plugins/kibana_react/public';
|
||||
|
||||
import { useToastNotifications } from '../app_dependencies';
|
||||
import { TransformListRow, refreshTransformList$, REFRESH_TRANSFORM_LIST_STATE } from '../common';
|
||||
import type { StopTransformsRequestSchema } from '../../../common/api_schemas/stop_transforms';
|
||||
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';
|
||||
|
||||
export const useStopTransforms = () => {
|
||||
const deps = useAppDependencies();
|
||||
const toastNotifications = useToastNotifications();
|
||||
const api = useApi();
|
||||
|
||||
return async (transforms: TransformListRow[]) => {
|
||||
const transformsInfo: TransformEndpointRequest[] = transforms.map((df) => ({
|
||||
id: df.config.id,
|
||||
state: df.stats.state,
|
||||
}));
|
||||
const results: TransformEndpointResult = await api.stopTransforms(transformsInfo);
|
||||
return async (transformsInfo: StopTransformsRequestSchema) => {
|
||||
const results = await api.stopTransforms(transformsInfo);
|
||||
|
||||
if (!isStopTransformsResponseSchema(results)) {
|
||||
toastNotifications.addDanger({
|
||||
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) {
|
||||
// hasOwnProperty check to ensure only properties on object itself, and not its prototypes
|
|
@ -6,7 +6,7 @@
|
|||
|
||||
import React, { createContext } from 'react';
|
||||
|
||||
import { Privileges } from '../../../../../common';
|
||||
import { Privileges } from '../../../../../common/types/privileges';
|
||||
|
||||
import { useRequest } from '../../../hooks';
|
||||
|
||||
|
|
|
@ -6,7 +6,7 @@
|
|||
|
||||
import { i18n } from '@kbn/i18n';
|
||||
|
||||
import { Privileges } from '../../../../../common';
|
||||
import { Privileges } from '../../../../../common/types/privileges';
|
||||
|
||||
export interface Capabilities {
|
||||
canGetTransform: boolean;
|
||||
|
|
|
@ -10,7 +10,7 @@ import { EuiPageContent } from '@elastic/eui';
|
|||
|
||||
import { FormattedMessage } from '@kbn/i18n/react';
|
||||
|
||||
import { MissingPrivileges } from '../../../../../common';
|
||||
import { MissingPrivileges } from '../../../../../common/types/privileges';
|
||||
|
||||
import { SectionLoading } from '../../../components';
|
||||
|
||||
|
|
|
@ -21,40 +21,20 @@ import {
|
|||
EuiTitle,
|
||||
} 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 { useDocumentationLinks } from '../../hooks/use_documentation_links';
|
||||
import { useSearchItems } from '../../hooks/use_search_items';
|
||||
|
||||
import { APP_CREATE_TRANSFORM_CLUSTER_PRIVILEGES } from '../../../../common/constants';
|
||||
|
||||
import { useAppDependencies } from '../../app_dependencies';
|
||||
import { TransformPivotConfig } from '../../common';
|
||||
import { breadcrumbService, docTitleService, BREADCRUMB_SECTION } from '../../services/navigation';
|
||||
import { PrivilegesWrapper } from '../../lib/authorization';
|
||||
|
||||
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 }>;
|
||||
export const CloneTransformSection: FC<Props> = ({ match }) => {
|
||||
// Set breadcrumb and page title
|
||||
|
@ -84,15 +64,15 @@ export const CloneTransformSection: FC<Props> = ({ match }) => {
|
|||
} = useSearchItems(undefined);
|
||||
|
||||
const fetchTransformConfig = async () => {
|
||||
try {
|
||||
const transformConfigs: GetTransformsResponse = await api.getTransforms(transformId);
|
||||
if (isGetTransformsResponseError(transformConfigs)) {
|
||||
setTransformConfig(undefined);
|
||||
setErrorMessage(transformConfigs.error.msg);
|
||||
setIsInitialized(true);
|
||||
return;
|
||||
}
|
||||
const transformConfigs = await api.getTransform(transformId);
|
||||
if (isHttpFetchError(transformConfigs)) {
|
||||
setTransformConfig(undefined);
|
||||
setErrorMessage(transformConfigs.message);
|
||||
setIsInitialized(true);
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
await loadIndexPatterns(savedObjectsClient, indexPatterns);
|
||||
const indexPatternTitle = Array.isArray(transformConfigs.transforms[0].source.index)
|
||||
? transformConfigs.transforms[0].source.index.join(',')
|
||||
|
|
|
@ -7,7 +7,10 @@
|
|||
import { shallow } from 'enzyme';
|
||||
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';
|
||||
|
||||
|
|
|
@ -10,8 +10,9 @@ import { i18n } from '@kbn/i18n';
|
|||
|
||||
import { EuiButtonIcon, EuiFlexGroup, EuiFlexItem, EuiPopover, EuiTextColor } from '@elastic/eui';
|
||||
|
||||
import { AggName } from '../../../../../../common/types/aggregations';
|
||||
|
||||
import {
|
||||
AggName,
|
||||
isPivotAggsConfigWithUiSupport,
|
||||
PivotAggsConfig,
|
||||
PivotAggsConfigWithUiSupportDict,
|
||||
|
|
|
@ -7,7 +7,9 @@
|
|||
import { shallow } from 'enzyme';
|
||||
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';
|
||||
|
||||
|
|
|
@ -8,8 +8,9 @@ import React, { Fragment } from 'react';
|
|||
|
||||
import { EuiPanel, EuiSpacer } from '@elastic/eui';
|
||||
|
||||
import { AggName } from '../../../../../../common/types/aggregations';
|
||||
|
||||
import {
|
||||
AggName,
|
||||
PivotAggsConfig,
|
||||
PivotAggsConfigDict,
|
||||
PivotAggsConfigWithUiSupportDict,
|
||||
|
|
|
@ -7,7 +7,9 @@
|
|||
import { shallow } from 'enzyme';
|
||||
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';
|
||||
|
||||
|
|
|
@ -8,7 +8,9 @@ import React, { Fragment } from 'react';
|
|||
|
||||
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 {
|
||||
list: PivotAggsConfigDict;
|
||||
|
|
|
@ -7,7 +7,10 @@
|
|||
import { shallow } from 'enzyme';
|
||||
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';
|
||||
|
||||
|
|
|
@ -20,10 +20,14 @@ import {
|
|||
|
||||
import { cloneDeep } from 'lodash';
|
||||
import { useUpdateEffect } from 'react-use';
|
||||
import { AggName } from '../../../../../../common/types/aggregations';
|
||||
import { dictionaryToArray } from '../../../../../../common/types/common';
|
||||
import {
|
||||
PivotSupportedAggs,
|
||||
PIVOT_SUPPORTED_AGGS,
|
||||
} from '../../../../../../common/types/pivot_aggs';
|
||||
|
||||
import {
|
||||
AggName,
|
||||
isAggName,
|
||||
isPivotAggsConfigPercentiles,
|
||||
isPivotAggsConfigWithUiSupport,
|
||||
|
@ -31,9 +35,8 @@ import {
|
|||
PERCENTILES_AGG_DEFAULT_PERCENTS,
|
||||
PivotAggsConfig,
|
||||
PivotAggsConfigWithUiSupportDict,
|
||||
PIVOT_SUPPORTED_AGGS,
|
||||
} 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';
|
||||
|
||||
interface Props {
|
||||
|
|
|
@ -10,8 +10,9 @@ import { i18n } from '@kbn/i18n';
|
|||
|
||||
import { EuiButtonIcon, EuiFlexGroup, EuiFlexItem, EuiPopover, EuiTextColor } from '@elastic/eui';
|
||||
|
||||
import { AggName } from '../../../../../../common/types/aggregations';
|
||||
|
||||
import {
|
||||
AggName,
|
||||
isGroupByDateHistogram,
|
||||
isGroupByHistogram,
|
||||
PivotGroupByConfig,
|
||||
|
|
|
@ -8,8 +8,9 @@ import React, { Fragment } from 'react';
|
|||
|
||||
import { EuiPanel, EuiSpacer } from '@elastic/eui';
|
||||
|
||||
import { AggName } from '../../../../../../common/types/aggregations';
|
||||
|
||||
import {
|
||||
AggName,
|
||||
PivotGroupByConfig,
|
||||
PivotGroupByConfigDict,
|
||||
PivotGroupByConfigWithUiSupportDict,
|
||||
|
|
|
@ -7,7 +7,9 @@
|
|||
import { shallow } from 'enzyme';
|
||||
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';
|
||||
|
||||
|
|
|
@ -18,10 +18,10 @@ import {
|
|||
EuiSpacer,
|
||||
} from '@elastic/eui';
|
||||
|
||||
import { AggName } from '../../../../../../common/types/aggregations';
|
||||
import { dictionaryToArray } from '../../../../../../common/types/common';
|
||||
|
||||
import {
|
||||
AggName,
|
||||
dateHistogramIntervalFormatRegex,
|
||||
getEsAggFromGroupByConfig,
|
||||
isGroupByDateHistogram,
|
||||
|
|
|
@ -30,6 +30,12 @@ import {
|
|||
|
||||
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 { getErrorMessage } from '../../../../../../common/utils/errors';
|
||||
|
@ -93,34 +99,28 @@ export const StepCreateForm: FC<Props> = React.memo(
|
|||
async function createTransform() {
|
||||
setLoading(true);
|
||||
|
||||
try {
|
||||
const resp = await api.createTransform(transformId, transformConfig);
|
||||
if (resp.errors !== undefined && Array.isArray(resp.errors)) {
|
||||
if (resp.errors.length === 1) {
|
||||
throw resp.errors[0];
|
||||
}
|
||||
const resp = await api.createTransform(transformId, transformConfig);
|
||||
|
||||
if (resp.errors.length > 1) {
|
||||
throw resp.errors;
|
||||
}
|
||||
if (!isPutTransformsResponseSchema(resp) || resp.errors.length > 0) {
|
||||
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({
|
||||
title: i18n.translate('xpack.transform.stepCreateForm.createTransformErrorMessage', {
|
||||
defaultMessage: 'An error occurred creating the transform {transformId}:',
|
||||
values: { transformId },
|
||||
}),
|
||||
text: toMountPoint(
|
||||
<ToastNotificationText overlays={deps.overlays} text={getErrorMessage(e)} />
|
||||
<ToastNotificationText
|
||||
overlays={deps.overlays}
|
||||
text={getErrorMessage(isPutTransformsResponseSchema(resp) ? respErrors : resp)}
|
||||
/>
|
||||
),
|
||||
});
|
||||
setCreated(false);
|
||||
|
@ -128,6 +128,15 @@ export const StepCreateForm: FC<Props> = React.memo(
|
|||
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) {
|
||||
createKibanaIndexPattern();
|
||||
}
|
||||
|
@ -138,37 +147,36 @@ export const StepCreateForm: FC<Props> = React.memo(
|
|||
async function startTransform() {
|
||||
setLoading(true);
|
||||
|
||||
try {
|
||||
const resp = await api.startTransforms([{ id: transformId }]);
|
||||
if (typeof resp === 'object' && resp !== null && resp[transformId]?.success === true) {
|
||||
toastNotifications.addSuccess(
|
||||
i18n.translate('xpack.transform.stepCreateForm.startTransformSuccessMessage', {
|
||||
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}:',
|
||||
const resp = await api.startTransforms([{ id: transformId }]);
|
||||
|
||||
if (isStartTransformsResponseSchema(resp) && resp[transformId]?.success === true) {
|
||||
toastNotifications.addSuccess(
|
||||
i18n.translate('xpack.transform.stepCreateForm.startTransformSuccessMessage', {
|
||||
defaultMessage: 'Request to start transform {transformId} acknowledged.',
|
||||
values: { transformId },
|
||||
}),
|
||||
text: toMountPoint(
|
||||
<ToastNotificationText overlays={deps.overlays} text={getErrorMessage(e)} />
|
||||
),
|
||||
});
|
||||
setStarted(false);
|
||||
})
|
||||
);
|
||||
setStarted(true);
|
||||
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() {
|
||||
|
@ -250,27 +258,30 @@ export const StepCreateForm: FC<Props> = React.memo(
|
|||
// wrapping in function so we can keep the interval id in local scope
|
||||
function startProgressBar() {
|
||||
const interval = setInterval(async () => {
|
||||
try {
|
||||
const stats = await api.getTransformsStats(transformId);
|
||||
if (stats && Array.isArray(stats.transforms) && stats.transforms.length > 0) {
|
||||
const percent =
|
||||
getTransformProgress({
|
||||
id: transformConfig.id,
|
||||
config: transformConfig,
|
||||
stats: stats.transforms[0],
|
||||
}) || 0;
|
||||
setProgressPercentComplete(percent);
|
||||
if (percent >= 100) {
|
||||
clearInterval(interval);
|
||||
}
|
||||
const stats = await api.getTransformStats(transformId);
|
||||
|
||||
if (
|
||||
isGetTransformsStatsResponseSchema(stats) &&
|
||||
Array.isArray(stats.transforms) &&
|
||||
stats.transforms.length > 0
|
||||
) {
|
||||
const percent =
|
||||
getTransformProgress({
|
||||
id: transformConfig.id,
|
||||
config: transformConfig,
|
||||
stats: stats.transforms[0],
|
||||
}) || 0;
|
||||
setProgressPercentComplete(percent);
|
||||
if (percent >= 100) {
|
||||
clearInterval(interval);
|
||||
}
|
||||
} catch (e) {
|
||||
} else {
|
||||
toastNotifications.addDanger({
|
||||
title: i18n.translate('xpack.transform.stepCreateForm.progressErrorMessage', {
|
||||
defaultMessage: 'An error occurred getting the progress percentage:',
|
||||
}),
|
||||
text: toMountPoint(
|
||||
<ToastNotificationText overlays={deps.overlays} text={getErrorMessage(e)} />
|
||||
<ToastNotificationText overlays={deps.overlays} text={getErrorMessage(stats)} />
|
||||
),
|
||||
});
|
||||
clearInterval(interval);
|
||||
|
|
|
@ -6,19 +6,21 @@
|
|||
|
||||
import { isEqual } from 'lodash';
|
||||
|
||||
import { Dictionary } from '../../../../../../../common/types/common';
|
||||
import { PivotSupportedAggs } from '../../../../../../../common/types/pivot_aggs';
|
||||
import { TransformPivotConfig } from '../../../../../../../common/types/transform';
|
||||
|
||||
import {
|
||||
matchAllQuery,
|
||||
PivotAggsConfig,
|
||||
PivotAggsConfigDict,
|
||||
PivotGroupByConfig,
|
||||
PivotGroupByConfigDict,
|
||||
TransformPivotConfig,
|
||||
PIVOT_SUPPORTED_GROUP_BY_AGGS,
|
||||
} from '../../../../../common';
|
||||
import { Dictionary } from '../../../../../../../common/types/common';
|
||||
|
||||
import { StepDefineExposedState } from './types';
|
||||
import { getAggConfigFromEsAgg, PivotSupportedAggs } from '../../../../../common/pivot_aggs';
|
||||
import { getAggConfigFromEsAgg } from '../../../../../common/pivot_aggs';
|
||||
|
||||
export function applyTransformConfigToDefineState(
|
||||
state: StepDefineExposedState,
|
||||
|
|
|
@ -10,6 +10,7 @@ import { FormattedMessage } from '@kbn/i18n/react';
|
|||
import { debounce } from 'lodash';
|
||||
import { useUpdateEffect } from 'react-use';
|
||||
import { i18n } from '@kbn/i18n';
|
||||
import { isEsSearchResponse } from '../../../../../../../../../common/api_schemas/type_guards';
|
||||
import { useApi } from '../../../../../../../hooks';
|
||||
import { CreateTransformWizardContext } from '../../../../wizard/wizard';
|
||||
import { FilterAggConfigTerm } from '../types';
|
||||
|
@ -55,22 +56,24 @@ export const FilterTermForm: FilterAggConfigTerm['aggTypeConfig']['FilterAggForm
|
|||
},
|
||||
};
|
||||
|
||||
try {
|
||||
const response = await api.esSearch(esSearchRequest);
|
||||
setOptions(
|
||||
response.aggregations.field_values.buckets.map(
|
||||
(value: { key: string; doc_count: number }) => ({ label: value.key })
|
||||
)
|
||||
);
|
||||
} catch (e) {
|
||||
const response = await api.esSearch(esSearchRequest);
|
||||
|
||||
setIsLoading(false);
|
||||
|
||||
if (!isEsSearchResponse(response)) {
|
||||
toastNotifications.addWarning(
|
||||
i18n.translate('xpack.transform.agg.popoverForm.filerAgg.term.errorFetchSuggestions', {
|
||||
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),
|
||||
[selectedField]
|
||||
);
|
||||
|
|
|
@ -5,11 +5,11 @@
|
|||
*/
|
||||
|
||||
import {
|
||||
PIVOT_SUPPORTED_AGGS,
|
||||
PivotAggsConfigBase,
|
||||
PivotAggsConfigWithUiBase,
|
||||
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';
|
||||
|
||||
/**
|
||||
|
|
|
@ -6,7 +6,8 @@
|
|||
|
||||
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(
|
||||
aggName: AggName,
|
||||
|
@ -36,7 +37,7 @@ export function getAggNameConflictToastMessages(
|
|||
// check the new aggName against existing aggs and groupbys
|
||||
const aggNameSplit = aggName.split('.');
|
||||
let aggNameCheck: string;
|
||||
aggNameSplit.forEach((aggNamePart) => {
|
||||
aggNameSplit.forEach((aggNamePart: string) => {
|
||||
aggNameCheck = aggNameCheck === undefined ? aggNamePart : `${aggNameCheck}.${aggNamePart}`;
|
||||
if (aggList[aggNameCheck] !== undefined || groupByList[aggNameCheck] !== undefined) {
|
||||
conflicts.push(
|
||||
|
|
|
@ -4,13 +4,15 @@
|
|||
* you may not use this file except in compliance with the Elastic License.
|
||||
*/
|
||||
|
||||
import { EsFieldName } from '../../../../../../../common/types/fields';
|
||||
import {
|
||||
EsFieldName,
|
||||
PERCENTILES_AGG_DEFAULT_PERCENTS,
|
||||
PivotSupportedAggs,
|
||||
PIVOT_SUPPORTED_AGGS,
|
||||
} from '../../../../../../../common/types/pivot_aggs';
|
||||
import {
|
||||
PERCENTILES_AGG_DEFAULT_PERCENTS,
|
||||
PivotAggsConfigWithUiSupport,
|
||||
} from '../../../../../common';
|
||||
import { PivotSupportedAggs } from '../../../../../common/pivot_aggs';
|
||||
import { getFilterAggConfig } from './filter_agg/config';
|
||||
|
||||
/**
|
||||
|
|
|
@ -4,11 +4,9 @@
|
|||
* you may not use this file except in compliance with the Elastic License.
|
||||
*/
|
||||
|
||||
import {
|
||||
EsFieldName,
|
||||
GroupByConfigWithUiSupport,
|
||||
PIVOT_SUPPORTED_GROUP_BY_AGGS,
|
||||
} from '../../../../../common';
|
||||
import { EsFieldName } from '../../../../../../../common/types/fields';
|
||||
|
||||
import { GroupByConfigWithUiSupport, PIVOT_SUPPORTED_GROUP_BY_AGGS } from '../../../../../common';
|
||||
|
||||
export function getDefaultGroupByConfig(
|
||||
aggName: string,
|
||||
|
|
|
@ -6,7 +6,9 @@
|
|||
|
||||
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 { 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 { PreviewRequestBody } from '../../../../../common';
|
||||
import { PostTransformsPreviewRequestSchema } from '../../../../../../../common/api_schemas/transforms';
|
||||
|
||||
import { StepDefineExposedState } from '../common';
|
||||
|
||||
export const useAdvancedPivotEditor = (
|
||||
defaults: StepDefineExposedState,
|
||||
previewRequest: PreviewRequestBody
|
||||
previewRequest: PostTransformsPreviewRequestSchema
|
||||
) => {
|
||||
const stringifiedPivotConfig = JSON.stringify(previewRequest.pivot, null, 2);
|
||||
|
||||
|
|
|
@ -6,13 +6,13 @@
|
|||
|
||||
import { useState } from 'react';
|
||||
|
||||
import { PreviewRequestBody } from '../../../../../common';
|
||||
import { PostTransformsPreviewRequestSchema } from '../../../../../../../common/api_schemas/transforms';
|
||||
|
||||
import { StepDefineExposedState } from '../common';
|
||||
|
||||
export const useAdvancedSourceEditor = (
|
||||
defaults: StepDefineExposedState,
|
||||
previewRequest: PreviewRequestBody
|
||||
previewRequest: PostTransformsPreviewRequestSchema
|
||||
) => {
|
||||
const stringifiedSourceConfig = JSON.stringify(previewRequest.source.query, null, 2);
|
||||
|
||||
|
|
|
@ -6,11 +6,11 @@
|
|||
|
||||
import { useCallback, useMemo, useState } from 'react';
|
||||
|
||||
import { AggName } from '../../../../../../../common/types/aggregations';
|
||||
import { dictionaryToArray } from '../../../../../../../common/types/common';
|
||||
|
||||
import { useToastNotifications } from '../../../../../app_dependencies';
|
||||
import {
|
||||
AggName,
|
||||
DropDownLabel,
|
||||
PivotAggsConfig,
|
||||
PivotAggsConfigDict,
|
||||
|
|
|
@ -6,7 +6,7 @@
|
|||
|
||||
import { useEffect } from 'react';
|
||||
|
||||
import { getPreviewRequestBody } from '../../../../../common';
|
||||
import { getPreviewTransformRequestBody } from '../../../../../common';
|
||||
|
||||
import { getDefaultStepDefineState } from '../common';
|
||||
|
||||
|
@ -26,7 +26,7 @@ export const useStepDefineForm = ({ overrides, onChange, searchItems }: StepDefi
|
|||
const searchBar = useSearchBar(defaults, indexPattern);
|
||||
const pivotConfig = usePivotConfig(defaults, indexPattern);
|
||||
|
||||
const previewRequest = getPreviewRequestBody(
|
||||
const previewRequest = getPreviewTransformRequestBody(
|
||||
indexPattern.title,
|
||||
searchBar.state.pivotQuery,
|
||||
pivotConfig.state.pivotGroupByArr,
|
||||
|
@ -41,7 +41,7 @@ export const useStepDefineForm = ({ overrides, onChange, searchItems }: StepDefi
|
|||
|
||||
useEffect(() => {
|
||||
if (!advancedSourceEditor.state.isAdvancedSourceEditorEnabled) {
|
||||
const previewRequestUpdate = getPreviewRequestBody(
|
||||
const previewRequestUpdate = getPreviewTransformRequestBody(
|
||||
indexPattern.title,
|
||||
searchBar.state.pivotQuery,
|
||||
pivotConfig.state.pivotGroupByArr,
|
||||
|
|
|
@ -15,10 +15,11 @@ import { coreMock } from '../../../../../../../../../src/core/public/mocks';
|
|||
import { dataPluginMock } from '../../../../../../../../../src/plugins/data/public/mocks';
|
||||
const startMock = coreMock.createStart();
|
||||
|
||||
import { PIVOT_SUPPORTED_AGGS } from '../../../../../../common/types/pivot_aggs';
|
||||
|
||||
import {
|
||||
PivotAggsConfigDict,
|
||||
PivotGroupByConfigDict,
|
||||
PIVOT_SUPPORTED_AGGS,
|
||||
PIVOT_SUPPORTED_GROUP_BY_AGGS,
|
||||
} from '../../../../common';
|
||||
import { SearchItems } from '../../../../hooks/use_search_items';
|
||||
|
|
|
@ -22,6 +22,9 @@ import {
|
|||
EuiText,
|
||||
} from '@elastic/eui';
|
||||
|
||||
import { PivotAggDict } from '../../../../../../common/types/pivot_aggs';
|
||||
import { PivotGroupByDict } from '../../../../../../common/types/pivot_group_by';
|
||||
|
||||
import { DataGrid } from '../../../../../shared_imports';
|
||||
|
||||
import {
|
||||
|
@ -30,10 +33,8 @@ import {
|
|||
} from '../../../../common/data_grid';
|
||||
|
||||
import {
|
||||
getPreviewRequestBody,
|
||||
PivotAggDict,
|
||||
getPreviewTransformRequestBody,
|
||||
PivotAggsConfigDict,
|
||||
PivotGroupByDict,
|
||||
PivotGroupByConfigDict,
|
||||
PivotSupportedGroupByAggs,
|
||||
PivotAggsConfig,
|
||||
|
@ -87,7 +88,7 @@ export const StepDefineForm: FC<StepDefineFormProps> = React.memo((props) => {
|
|||
toastNotifications,
|
||||
};
|
||||
|
||||
const previewRequest = getPreviewRequestBody(
|
||||
const previewRequest = getPreviewTransformRequestBody(
|
||||
indexPattern.title,
|
||||
pivotQuery,
|
||||
pivotGroupByArr,
|
||||
|
|
|
@ -7,10 +7,11 @@
|
|||
import React from 'react';
|
||||
import { render, wait } from '@testing-library/react';
|
||||
|
||||
import { PIVOT_SUPPORTED_AGGS } from '../../../../../../common/types/pivot_aggs';
|
||||
|
||||
import {
|
||||
PivotAggsConfig,
|
||||
PivotGroupByConfig,
|
||||
PIVOT_SUPPORTED_AGGS,
|
||||
PIVOT_SUPPORTED_GROUP_BY_AGGS,
|
||||
} from '../../../../common';
|
||||
import { SearchItems } from '../../../../hooks/use_search_items';
|
||||
|
|
|
@ -18,7 +18,7 @@ import { useToastNotifications } from '../../../../app_dependencies';
|
|||
import {
|
||||
getPivotQuery,
|
||||
getPivotPreviewDevConsoleStatement,
|
||||
getPreviewRequestBody,
|
||||
getPreviewTransformRequestBody,
|
||||
isDefaultQuery,
|
||||
isMatchAllQuery,
|
||||
} from '../../../../common';
|
||||
|
@ -44,7 +44,7 @@ export const StepDefineSummary: FC<Props> = ({
|
|||
const pivotGroupByArr = dictionaryToArray(groupByList);
|
||||
const pivotQuery = getPivotQuery(searchQuery);
|
||||
|
||||
const previewRequest = getPreviewRequestBody(
|
||||
const previewRequest = getPreviewTransformRequestBody(
|
||||
searchItems.indexPattern.title,
|
||||
pivotQuery,
|
||||
pivotGroupByArr,
|
||||
|
|
|
@ -11,24 +11,28 @@ import { i18n } from '@kbn/i18n';
|
|||
import { EuiLink, EuiSwitch, EuiFieldText, EuiForm, EuiFormRow, EuiSelect } from '@elastic/eui';
|
||||
|
||||
import { KBN_FIELD_TYPES } from '../../../../../../../../../src/plugins/data/common';
|
||||
|
||||
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 { getErrorMessage } from '../../../../../../common/utils/errors';
|
||||
|
||||
import { useAppDependencies, useToastNotifications } from '../../../../app_dependencies';
|
||||
import { ToastNotificationText } from '../../../../components';
|
||||
import { isHttpFetchError } from '../../../../common/request';
|
||||
import { useDocumentationLinks } from '../../../../hooks/use_documentation_links';
|
||||
import { SearchItems } from '../../../../hooks/use_search_items';
|
||||
import { useApi } from '../../../../hooks/use_api';
|
||||
import { StepDetailsTimeField } from './step_details_time_field';
|
||||
import {
|
||||
getPivotQuery,
|
||||
getPreviewRequestBody,
|
||||
getPreviewTransformRequestBody,
|
||||
isTransformIdValid,
|
||||
TransformPivotConfig,
|
||||
} from '../../../../common';
|
||||
import { EsIndexName, IndexPatternTitle } from './common';
|
||||
import { delayValidator } from '../../../../common/validators';
|
||||
|
@ -48,10 +52,12 @@ export interface StepDetailsExposedState {
|
|||
indexPatternDateField?: string | undefined;
|
||||
}
|
||||
|
||||
const defaultContinuousModeDelay = '60s';
|
||||
|
||||
export function getDefaultStepDetailsState(): StepDetailsExposedState {
|
||||
return {
|
||||
continuousModeDateField: '',
|
||||
continuousModeDelay: '60s',
|
||||
continuousModeDelay: defaultContinuousModeDelay,
|
||||
createIndexPattern: true,
|
||||
isContinuousModeEnabled: false,
|
||||
transformId: '',
|
||||
|
@ -72,7 +78,7 @@ export function applyTransformConfigToDetailsState(
|
|||
const time = transformConfig.sync?.time;
|
||||
if (time !== undefined) {
|
||||
state.continuousModeDateField = time.field;
|
||||
state.continuousModeDelay = time.delay;
|
||||
state.continuousModeDelay = time?.delay ?? defaultContinuousModeDelay;
|
||||
state.isContinuousModeEnabled = true;
|
||||
}
|
||||
}
|
||||
|
@ -137,19 +143,20 @@ export const StepDetailsForm: FC<Props> = React.memo(
|
|||
useEffect(() => {
|
||||
// use an IIFE to avoid returning a Promise to useEffect.
|
||||
(async function () {
|
||||
try {
|
||||
const { searchQuery, groupByList, aggList } = stepDefineState;
|
||||
const pivotAggsArr = dictionaryToArray(aggList);
|
||||
const pivotGroupByArr = dictionaryToArray(groupByList);
|
||||
const pivotQuery = getPivotQuery(searchQuery);
|
||||
const previewRequest = getPreviewRequestBody(
|
||||
searchItems.indexPattern.title,
|
||||
pivotQuery,
|
||||
pivotGroupByArr,
|
||||
pivotAggsArr
|
||||
);
|
||||
const { searchQuery, groupByList, aggList } = stepDefineState;
|
||||
const pivotAggsArr = dictionaryToArray(aggList);
|
||||
const pivotGroupByArr = dictionaryToArray(groupByList);
|
||||
const pivotQuery = getPivotQuery(searchQuery);
|
||||
const previewRequest = getPreviewTransformRequestBody(
|
||||
searchItems.indexPattern.title,
|
||||
pivotQuery,
|
||||
pivotGroupByArr,
|
||||
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 datetimeColumns: string[] = Object.keys(properties).filter(
|
||||
(col) => properties[col].type === 'date'
|
||||
|
@ -157,43 +164,46 @@ export const StepDetailsForm: FC<Props> = React.memo(
|
|||
|
||||
setPreviewDateColumns(datetimeColumns);
|
||||
setIndexPatternDateField(datetimeColumns[0]);
|
||||
} catch (e) {
|
||||
} else {
|
||||
toastNotifications.addDanger({
|
||||
title: i18n.translate('xpack.transform.stepDetailsForm.errorGettingTransformPreview', {
|
||||
defaultMessage: 'An error occurred getting transform preview',
|
||||
defaultMessage: 'An error occurred fetching the transform preview',
|
||||
}),
|
||||
text: toMountPoint(
|
||||
<ToastNotificationText overlays={deps.overlays} text={getErrorMessage(e)} />
|
||||
<ToastNotificationText
|
||||
overlays={deps.overlays}
|
||||
text={getErrorMessage(transformPreview)}
|
||||
/>
|
||||
),
|
||||
});
|
||||
}
|
||||
|
||||
try {
|
||||
setTransformIds(
|
||||
(await api.getTransforms()).transforms.map(
|
||||
(transform: TransformPivotConfig) => transform.id
|
||||
)
|
||||
);
|
||||
} catch (e) {
|
||||
const resp = await api.getTransforms();
|
||||
|
||||
if (isHttpFetchError(resp)) {
|
||||
toastNotifications.addDanger({
|
||||
title: i18n.translate('xpack.transform.stepDetailsForm.errorGettingTransformList', {
|
||||
defaultMessage: 'An error occurred getting the existing transform IDs:',
|
||||
}),
|
||||
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 {
|
||||
setIndexNames((await api.getIndices()).map((index) => index.name));
|
||||
} catch (e) {
|
||||
const indices = await api.getEsIndices();
|
||||
|
||||
if (isEsIndices(indices)) {
|
||||
setIndexNames(indices.map((index) => index.name));
|
||||
} else {
|
||||
toastNotifications.addDanger({
|
||||
title: i18n.translate('xpack.transform.stepDetailsForm.errorGettingIndexNames', {
|
||||
defaultMessage: 'An error occurred getting the existing index names:',
|
||||
}),
|
||||
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 { getCreateRequestBody, TransformPivotConfig } from '../../../../common';
|
||||
import { TransformPivotConfig } from '../../../../../../common/types/transform';
|
||||
|
||||
import { getCreateTransformRequestBody } from '../../../../common';
|
||||
import { SearchItems } from '../../../../hooks/use_search_items';
|
||||
|
||||
import {
|
||||
|
@ -149,7 +151,7 @@ export const Wizard: FC<WizardProps> = React.memo(({ cloneConfig, searchItems })
|
|||
}
|
||||
}, []);
|
||||
|
||||
const transformConfig = getCreateRequestBody(
|
||||
const transformConfig = getCreateTransformRequestBody(
|
||||
indexPattern.title,
|
||||
stepDefineState,
|
||||
stepDetailsState
|
||||
|
|
|
@ -7,7 +7,7 @@
|
|||
import React, { FC } from 'react';
|
||||
import { i18n } from '@kbn/i18n';
|
||||
import { EuiToolTip } from '@elastic/eui';
|
||||
import { TRANSFORM_STATE } from '../../../../../../common';
|
||||
import { TransformState, TRANSFORM_STATE } from '../../../../../../common/constants';
|
||||
import { createCapabilityFailureMessage } from '../../../../lib/authorization';
|
||||
import { TransformListRow } from '../../../../common';
|
||||
|
||||
|
@ -18,8 +18,8 @@ export const deleteActionNameText = i18n.translate(
|
|||
}
|
||||
);
|
||||
|
||||
const transformCanNotBeDeleted = (item: TransformListRow) =>
|
||||
![TRANSFORM_STATE.STOPPED, TRANSFORM_STATE.FAILED].includes(item.stats.state);
|
||||
const transformCanNotBeDeleted = (i: TransformListRow) =>
|
||||
!([TRANSFORM_STATE.STOPPED, TRANSFORM_STATE.FAILED] as TransformState[]).includes(i.stats.state);
|
||||
|
||||
export const isDeleteActionDisabled = (items: TransformListRow[], forceDisable: boolean) => {
|
||||
const disabled = items.some(transformCanNotBeDeleted);
|
||||
|
|
|
@ -6,7 +6,7 @@
|
|||
|
||||
import React, { useContext, useMemo, useState } from 'react';
|
||||
|
||||
import { TRANSFORM_STATE } from '../../../../../../common';
|
||||
import { TRANSFORM_STATE } from '../../../../../../common/constants';
|
||||
|
||||
import { TransformListAction, TransformListRow } from '../../../../common';
|
||||
import { useDeleteIndexAndTargetIndex, useDeleteTransforms } from '../../../../hooks';
|
||||
|
@ -55,7 +55,16 @@ export const useDeleteAction = (forceDisable: boolean) => {
|
|||
const forceDelete = isBulkAction
|
||||
? shouldForceDelete
|
||||
: 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[]) => {
|
||||
|
|
|
@ -6,7 +6,9 @@
|
|||
|
||||
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 { editActionNameText, EditActionName } from './edit_action_name';
|
||||
|
|
|
@ -8,7 +8,7 @@ import React, { FC, useContext } from 'react';
|
|||
import { i18n } from '@kbn/i18n';
|
||||
import { EuiToolTip } from '@elastic/eui';
|
||||
|
||||
import { TRANSFORM_STATE } from '../../../../../../common';
|
||||
import { TRANSFORM_STATE } from '../../../../../../common/constants';
|
||||
|
||||
import {
|
||||
createCapabilityFailureMessage,
|
||||
|
|
|
@ -6,7 +6,7 @@
|
|||
|
||||
import React, { useContext, useMemo, useState } from 'react';
|
||||
|
||||
import { TRANSFORM_STATE } from '../../../../../../common';
|
||||
import { TRANSFORM_STATE } from '../../../../../../common/constants';
|
||||
|
||||
import { AuthorizationContext } from '../../../../lib/authorization';
|
||||
import { TransformListAction, TransformListRow } from '../../../../common';
|
||||
|
@ -27,7 +27,7 @@ export const useStartAction = (forceDisable: boolean) => {
|
|||
|
||||
const startAndCloseModal = () => {
|
||||
setModalVisible(false);
|
||||
startTransforms(items);
|
||||
startTransforms(items.map((i) => ({ id: i.id })));
|
||||
};
|
||||
|
||||
const openModal = (newItems: TransformListRow[]) => {
|
||||
|
|
|
@ -8,7 +8,7 @@ import React, { FC, useContext } from 'react';
|
|||
import { i18n } from '@kbn/i18n';
|
||||
import { EuiToolTip } from '@elastic/eui';
|
||||
|
||||
import { TRANSFORM_STATE } from '../../../../../../common';
|
||||
import { TRANSFORM_STATE } from '../../../../../../common/constants';
|
||||
|
||||
import { TransformListRow } from '../../../../common';
|
||||
import {
|
||||
|
|
|
@ -6,7 +6,7 @@
|
|||
|
||||
import React, { useCallback, useContext, useMemo } from 'react';
|
||||
|
||||
import { TRANSFORM_STATE } from '../../../../../../common';
|
||||
import { TRANSFORM_STATE } from '../../../../../../common/constants';
|
||||
|
||||
import { AuthorizationContext } from '../../../../lib/authorization';
|
||||
import { TransformListAction, TransformListRow } from '../../../../common';
|
||||
|
@ -20,9 +20,10 @@ export const useStopAction = (forceDisable: boolean) => {
|
|||
|
||||
const stopTransforms = useStopTransforms();
|
||||
|
||||
const clickHandler = useCallback((item: TransformListRow) => stopTransforms([item]), [
|
||||
stopTransforms,
|
||||
]);
|
||||
const clickHandler = useCallback(
|
||||
(i: TransformListRow) => stopTransforms([{ id: i.id, state: i.stats.state }]),
|
||||
[stopTransforms]
|
||||
);
|
||||
|
||||
const action: TransformListAction = useMemo(
|
||||
() => ({
|
||||
|
|
|
@ -23,13 +23,12 @@ import {
|
|||
EuiTitle,
|
||||
} from '@elastic/eui';
|
||||
|
||||
import { isPostTransformsUpdateResponseSchema } from '../../../../../../common/api_schemas/type_guards';
|
||||
import { TransformPivotConfig } from '../../../../../../common/types/transform';
|
||||
|
||||
import { getErrorMessage } from '../../../../../../common/utils/errors';
|
||||
|
||||
import {
|
||||
refreshTransformList$,
|
||||
TransformPivotConfig,
|
||||
REFRESH_TRANSFORM_LIST_STATE,
|
||||
} from '../../../../common';
|
||||
import { refreshTransformList$, REFRESH_TRANSFORM_LIST_STATE } from '../../../../common';
|
||||
import { useToastNotifications } from '../../../../app_dependencies';
|
||||
|
||||
import { useApi } from '../../../../hooks/use_api';
|
||||
|
@ -58,19 +57,21 @@ export const EditTransformFlyout: FC<EditTransformFlyoutProps> = ({ closeFlyout,
|
|||
const requestConfig = applyFormFieldsToTransformConfig(config, state.formFields);
|
||||
const transformId = config.id;
|
||||
|
||||
try {
|
||||
await api.updateTransform(transformId, requestConfig);
|
||||
toastNotifications.addSuccess(
|
||||
i18n.translate('xpack.transform.transformList.editTransformSuccessMessage', {
|
||||
defaultMessage: 'Transform {transformId} updated.',
|
||||
values: { transformId },
|
||||
})
|
||||
);
|
||||
closeFlyout();
|
||||
refreshTransformList$.next(REFRESH_TRANSFORM_LIST_STATE.REFRESH);
|
||||
} catch (e) {
|
||||
setErrorMessage(getErrorMessage(e));
|
||||
const resp = await api.updateTransform(transformId, requestConfig);
|
||||
|
||||
if (!isPostTransformsUpdateResponseSchema(resp)) {
|
||||
setErrorMessage(getErrorMessage(resp));
|
||||
return;
|
||||
}
|
||||
|
||||
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;
|
||||
|
|
|
@ -4,7 +4,7 @@
|
|||
* you may not use this file except in compliance with the Elastic License.
|
||||
*/
|
||||
|
||||
import { TransformPivotConfig } from '../../../../common';
|
||||
import { TransformPivotConfig } from '../../../../../../common/types/transform';
|
||||
|
||||
import {
|
||||
applyFormFieldsToTransformConfig,
|
||||
|
@ -86,9 +86,7 @@ describe('Transform: applyFormFieldsToTransformConfig()', () => {
|
|||
});
|
||||
|
||||
test('should include previously nonexisting attributes', () => {
|
||||
const transformConfigMock = getTransformConfigMock();
|
||||
delete transformConfigMock.description;
|
||||
delete transformConfigMock.frequency;
|
||||
const { description, frequency, ...transformConfigMock } = getTransformConfigMock();
|
||||
|
||||
const updateConfig = applyFormFieldsToTransformConfig(transformConfigMock, {
|
||||
description: getDescriptionFieldMock('the-new-description'),
|
||||
|
|
|
@ -9,7 +9,8 @@ import { useReducer } from 'react';
|
|||
|
||||
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.
|
||||
// If no messages (empty array) get returned, the value is valid.
|
||||
|
@ -118,53 +119,35 @@ interface Action {
|
|||
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
|
||||
// request object suitable to be sent to the
|
||||
// transform update API endpoint.
|
||||
export const applyFormFieldsToTransformConfig = (
|
||||
config: TransformPivotConfig,
|
||||
{ description, docsPerSecond, frequency }: EditTransformFlyoutFieldsState
|
||||
): Partial<UpdateTransformPivotConfig> => {
|
||||
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;
|
||||
}
|
||||
|
||||
): PostTransformsUpdateRequestSchema => {
|
||||
// if the input field was left empty,
|
||||
// fall back to the default value of `null`
|
||||
// which will disable throttling.
|
||||
const docsPerSecondFormValue =
|
||||
docsPerSecond.value !== '' ? parseInt(docsPerSecond.value, 10) : 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
|
||||
|
|
|
@ -4,7 +4,7 @@
|
|||
* 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';
|
||||
|
||||
|
|
|
@ -9,11 +9,15 @@ import React, { useState } from 'react';
|
|||
import { EuiSpacer, EuiBasicTable } from '@elastic/eui';
|
||||
// @ts-ignore
|
||||
import { formatDate } from '@elastic/eui/lib/services/format';
|
||||
import { i18n } from '@kbn/i18n';
|
||||
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 { JobIcon } from '../../../../components/job_icon';
|
||||
import { TransformMessage } from '../../../../../../common/types/messages';
|
||||
import { useRefreshTransformList } from '../../../../common';
|
||||
|
||||
const TIME_FORMAT = 'YYYY-MM-DD HH:mm:ss';
|
||||
|
@ -36,25 +40,16 @@ export const ExpandedRowMessagesPane: React.FC<Props> = ({ transformId }) => {
|
|||
let concurrentLoads = 0;
|
||||
|
||||
return async function getMessages() {
|
||||
try {
|
||||
concurrentLoads++;
|
||||
concurrentLoads++;
|
||||
|
||||
if (concurrentLoads > 1) {
|
||||
return;
|
||||
}
|
||||
if (concurrentLoads > 1) {
|
||||
return;
|
||||
}
|
||||
|
||||
setIsLoading(true);
|
||||
const messagesResp = await api.getTransformAuditMessages(transformId);
|
||||
setIsLoading(false);
|
||||
setMessages(messagesResp as any[]);
|
||||
setIsLoading(true);
|
||||
const messagesResp = await api.getTransformAuditMessages(transformId);
|
||||
|
||||
concurrentLoads--;
|
||||
|
||||
if (concurrentLoads > 0) {
|
||||
concurrentLoads = 0;
|
||||
getMessages();
|
||||
}
|
||||
} catch (error) {
|
||||
if (!isGetTransformsAuditMessagesResponseSchema(messagesResp)) {
|
||||
setIsLoading(false);
|
||||
setErrorMessage(
|
||||
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 { TransformPivotConfig } from '../../../../../../common/types/transform';
|
||||
import { DataGrid } from '../../../../../shared_imports';
|
||||
|
||||
import { useToastNotifications } from '../../../../app_dependencies';
|
||||
import { getPivotQuery, TransformPivotConfig } from '../../../../common';
|
||||
import { getPivotQuery } from '../../../../common';
|
||||
import { usePivotData } from '../../../../hooks/use_pivot_data';
|
||||
import { SearchItems } from '../../../../hooks/use_search_items';
|
||||
|
||||
|
|
|
@ -23,7 +23,7 @@ import {
|
|||
EuiTitle,
|
||||
} from '@elastic/eui';
|
||||
|
||||
import { TransformId } from '../../../../../../common';
|
||||
import { TransformId } from '../../../../../../common/types/transform';
|
||||
|
||||
import {
|
||||
useRefreshTransformList,
|
||||
|
@ -189,7 +189,11 @@ export const TransformList: FC<Props> = ({
|
|||
</EuiButtonEmpty>
|
||||
</div>,
|
||||
<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} />
|
||||
</EuiButtonEmpty>
|
||||
</div>,
|
||||
|
|
|
@ -16,8 +16,8 @@ import {
|
|||
} from '@elastic/eui';
|
||||
import { i18n } from '@kbn/i18n';
|
||||
import { TermClause, FieldClause, Value } from './common';
|
||||
import { TRANSFORM_STATE } from '../../../../../../common';
|
||||
import { TRANSFORM_MODE, TransformListRow } from '../../../../common';
|
||||
import { TRANSFORM_MODE, TRANSFORM_STATE } from '../../../../../../common/constants';
|
||||
import { TransformListRow } from '../../../../common';
|
||||
import { getTaskStateBadge } from './use_columns';
|
||||
|
||||
const filters: SearchFilterConfig[] = [
|
||||
|
|
|
@ -7,9 +7,9 @@
|
|||
import React, { FC } from 'react';
|
||||
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';
|
||||
|
||||
|
|
|
@ -22,24 +22,21 @@ import {
|
|||
RIGHT_ALIGNMENT,
|
||||
} 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 {
|
||||
getTransformProgress,
|
||||
TransformListRow,
|
||||
TransformStats,
|
||||
TRANSFORM_LIST_COLUMN,
|
||||
} from '../../../../common';
|
||||
import { getTransformProgress, TransformListRow, TRANSFORM_LIST_COLUMN } from '../../../../common';
|
||||
import { useActions } from './use_actions';
|
||||
|
||||
enum STATE_COLOR {
|
||||
aborting = 'warning',
|
||||
failed = 'danger',
|
||||
indexing = 'primary',
|
||||
started = 'primary',
|
||||
stopped = 'hollow',
|
||||
stopping = 'hollow',
|
||||
}
|
||||
const STATE_COLOR = {
|
||||
aborting: 'warning',
|
||||
failed: 'danger',
|
||||
indexing: 'primary',
|
||||
started: 'primary',
|
||||
stopped: 'hollow',
|
||||
stopping: 'hollow',
|
||||
} as const;
|
||||
|
||||
export const getTaskStateBadge = (
|
||||
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