Skip to content

Commit

Permalink
[ML] Transforms: Data grid fixes. (#59538)
Browse files Browse the repository at this point in the history
- Fixes data grid schemas to avoid crashing the page.
- Fixes date formatting.
- Uses data grid for preview table in transform list.
  • Loading branch information
walterra authored Mar 10, 2020
1 parent b56cd41 commit ec1f46b
Show file tree
Hide file tree
Showing 20 changed files with 547 additions and 538 deletions.
70 changes: 70 additions & 0 deletions x-pack/plugins/transform/common/utils/object_utils.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
/*
* 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 { getNestedProperty } from './object_utils';

describe('object_utils', () => {
test('getNestedProperty()', () => {
const testObj = {
the: {
nested: {
value: 'the-nested-value',
},
},
};

const falseyObj = {
the: {
nested: {
value: false,
},
other_nested: {
value: 0,
},
},
};

const test1 = getNestedProperty(testObj, 'the');
expect(typeof test1).toBe('object');
expect(Object.keys(test1)).toStrictEqual(['nested']);

const test2 = getNestedProperty(testObj, 'the$');
expect(typeof test2).toBe('undefined');

const test3 = getNestedProperty(testObj, 'the$', 'the-default-value');
expect(typeof test3).toBe('string');
expect(test3).toBe('the-default-value');

const test4 = getNestedProperty(testObj, 'the.neSted');
expect(typeof test4).toBe('undefined');

const test5 = getNestedProperty(testObj, 'the.nested');
expect(typeof test5).toBe('object');
expect(Object.keys(test5)).toStrictEqual(['value']);

const test6 = getNestedProperty(testObj, 'the.nested.vaLue');
expect(typeof test6).toBe('undefined');

const test7 = getNestedProperty(testObj, 'the.nested.value');
expect(typeof test7).toBe('string');
expect(test7).toBe('the-nested-value');

const test8 = getNestedProperty(testObj, 'the.nested.value.doesntExist');
expect(typeof test8).toBe('undefined');

const test9 = getNestedProperty(testObj, 'the.nested.value.doesntExist', 'the-default-value');
expect(typeof test9).toBe('string');
expect(test9).toBe('the-default-value');

const test10 = getNestedProperty(falseyObj, 'the.nested.value');
expect(typeof test10).toBe('boolean');
expect(test10).toBe(false);

const test11 = getNestedProperty(falseyObj, 'the.other_nested.value');
expect(typeof test11).toBe('number');
expect(test11).toBe(0);
});
});
6 changes: 5 additions & 1 deletion x-pack/plugins/transform/common/utils/object_utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,5 +11,9 @@ export const getNestedProperty = (
accessor: string,
defaultValue?: any
) => {
return accessor.split('.').reduce((o, i) => o?.[i], obj) || defaultValue;
const value = accessor.split('.').reduce((o, i) => o?.[i], obj);

if (value === undefined) return defaultValue;

return value;
};
Original file line number Diff line number Diff line change
@@ -0,0 +1,117 @@
/*
* 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 { EuiDataGridSorting } from '@elastic/eui';

import {
getPreviewRequestBody,
PivotAggsConfig,
PivotGroupByConfig,
PIVOT_SUPPORTED_AGGS,
PIVOT_SUPPORTED_GROUP_BY_AGGS,
SimpleQuery,
} from '../../common';

import { multiColumnSortFactory, getPivotPreviewDevConsoleStatement } from './common';

describe('Transform: Define Pivot Common', () => {
test('multiColumnSortFactory()', () => {
const data = [
{ s: 'a', n: 1 },
{ s: 'a', n: 2 },
{ s: 'b', n: 3 },
{ s: 'b', n: 4 },
];

const sortingColumns1: EuiDataGridSorting['columns'] = [{ id: 's', direction: 'desc' }];
const multiColumnSort1 = multiColumnSortFactory(sortingColumns1);
data.sort(multiColumnSort1);

expect(data).toStrictEqual([
{ s: 'b', n: 3 },
{ s: 'b', n: 4 },
{ s: 'a', n: 1 },
{ s: 'a', n: 2 },
]);

const sortingColumns2: EuiDataGridSorting['columns'] = [
{ id: 's', direction: 'asc' },
{ id: 'n', direction: 'desc' },
];
const multiColumnSort2 = multiColumnSortFactory(sortingColumns2);
data.sort(multiColumnSort2);

expect(data).toStrictEqual([
{ s: 'a', n: 2 },
{ s: 'a', n: 1 },
{ s: 'b', n: 4 },
{ s: 'b', n: 3 },
]);

const sortingColumns3: EuiDataGridSorting['columns'] = [
{ id: 'n', direction: 'desc' },
{ id: 's', direction: 'desc' },
];
const multiColumnSort3 = multiColumnSortFactory(sortingColumns3);
data.sort(multiColumnSort3);

expect(data).toStrictEqual([
{ s: 'b', n: 4 },
{ s: 'b', n: 3 },
{ s: 'a', n: 2 },
{ s: 'a', n: 1 },
]);
});

test('getPivotPreviewDevConsoleStatement()', () => {
const query: SimpleQuery = {
query_string: {
query: '*',
default_operator: 'AND',
},
};
const groupBy: PivotGroupByConfig = {
agg: PIVOT_SUPPORTED_GROUP_BY_AGGS.TERMS,
field: 'the-group-by-field',
aggName: 'the-group-by-agg-name',
dropDownName: 'the-group-by-drop-down-name',
};
const agg: PivotAggsConfig = {
agg: PIVOT_SUPPORTED_AGGS.AVG,
field: 'the-agg-field',
aggName: 'the-agg-agg-name',
dropDownName: 'the-agg-drop-down-name',
};
const request = getPreviewRequestBody('the-index-pattern-title', query, [groupBy], [agg]);
const pivotPreviewDevConsoleStatement = getPivotPreviewDevConsoleStatement(request);

expect(pivotPreviewDevConsoleStatement).toBe(`POST _transform/_preview
{
"source": {
"index": [
"the-index-pattern-title"
]
},
"pivot": {
"group_by": {
"the-group-by-agg-name": {
"terms": {
"field": "the-group-by-field"
}
}
},
"aggregations": {
"the-agg-agg-name": {
"avg": {
"field": "the-agg-field"
}
}
}
}
}
`);
});
});
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
/*
* 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 { EuiDataGridSorting } from '@elastic/eui';

import { getNestedProperty } from '../../../../common/utils/object_utils';

import { PreviewRequestBody } from '../../common';

/**
* Helper to sort an array of objects based on an EuiDataGrid sorting configuration.
* `sortFn()` is recursive to support sorting on multiple columns.
*
* @param sortingColumns - The EUI data grid sorting configuration
* @returns The sorting function which can be used with an array's sort() function.
*/
export const multiColumnSortFactory = (sortingColumns: EuiDataGridSorting['columns']) => {
const isString = (arg: any): arg is string => {
return typeof arg === 'string';
};

const sortFn = (a: any, b: any, sortingColumnIndex = 0): number => {
const sort = sortingColumns[sortingColumnIndex];
const aValue = getNestedProperty(a, sort.id, null);
const bValue = getNestedProperty(b, sort.id, null);

if (typeof aValue === 'number' && typeof bValue === 'number') {
if (aValue < bValue) {
return sort.direction === 'asc' ? -1 : 1;
}
if (aValue > bValue) {
return sort.direction === 'asc' ? 1 : -1;
}
}

if (isString(aValue) && isString(bValue)) {
if (aValue.localeCompare(bValue) === -1) {
return sort.direction === 'asc' ? -1 : 1;
}
if (aValue.localeCompare(bValue) === 1) {
return sort.direction === 'asc' ? 1 : -1;
}
}

if (sortingColumnIndex + 1 < sortingColumns.length) {
return sortFn(a, b, sortingColumnIndex + 1);
}

return 0;
};

return sortFn;
};

export const getPivotPreviewDevConsoleStatement = (request: PreviewRequestBody) => {
return `POST _transform/_preview\n${JSON.stringify(request, null, 2)}\n`;
};
Original file line number Diff line number Diff line change
@@ -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 { PivotPreview } from './pivot_preview';
Original file line number Diff line number Diff line change
Expand Up @@ -8,20 +8,19 @@ import React from 'react';
import { render, wait } from '@testing-library/react';
import '@testing-library/jest-dom/extend-expect';

import { Providers } from '../../../../app_dependencies.mock';
import { Providers } from '../../app_dependencies.mock';
import {
getPivotQuery,
PivotAggsConfig,
PivotGroupByConfig,
PIVOT_SUPPORTED_AGGS,
PIVOT_SUPPORTED_GROUP_BY_AGGS,
} from '../../../../common';
import { SearchItems } from '../../../../hooks/use_search_items';
} from '../../common';

import { PivotPreview } from './pivot_preview';

jest.mock('ui/new_platform');
jest.mock('../../../../../shared_imports');
jest.mock('../../../shared_imports');

describe('Transform: <PivotPreview />', () => {
// Using the async/await wait()/done() pattern to avoid act() errors.
Expand All @@ -42,10 +41,7 @@ describe('Transform: <PivotPreview />', () => {
const props = {
aggs: { 'the-agg-name': agg },
groupBy: { 'the-group-by-name': groupBy },
indexPattern: {
title: 'the-index-pattern-title',
fields: [] as any[],
} as SearchItems['indexPattern'],
indexPatternTitle: 'the-index-pattern-title',
query: getPivotQuery('the-query'),
};

Expand Down
Loading

0 comments on commit ec1f46b

Please sign in to comment.