From 6e26af105e32591ddb506c61553a62e9f0fbc561 Mon Sep 17 00:00:00 2001 From: Dzmitry Tamashevich Date: Mon, 3 May 2021 10:55:47 +0300 Subject: [PATCH 1/5] [Discover] migrate remaining context files from js to ts --- .../context/api/{_stubs.js => _stubs.ts} | 61 +++++----- .../api/{anchor.test.js => anchor.test.ts} | 70 +++++++----- .../context/api/{anchor.js => anchor.ts} | 25 +++- ...s.test.js => context.predecessors.test.ts} | 107 ++++++++++-------- ...ors.test.js => context.successors.test.ts} | 90 ++++++++------- .../angular/context/api/context.ts | 7 +- .../context/query/{actions.js => actions.tsx} | 80 ++++++++----- .../context/query/{index.js => index.ts} | 0 .../context/query/{state.js => state.ts} | 10 +- .../context/query_parameters/actions.test.ts | 45 +++++--- .../{actions.js => actions.ts} | 36 ++++-- .../query_parameters/{index.js => index.ts} | 0 .../application/angular/context_app_state.ts | 60 ++++++++++ 13 files changed, 368 insertions(+), 223 deletions(-) rename src/plugins/discover/public/application/angular/context/api/{_stubs.js => _stubs.ts} (58%) rename src/plugins/discover/public/application/angular/context/api/{anchor.test.js => anchor.test.ts} (75%) rename src/plugins/discover/public/application/angular/context/api/{anchor.js => anchor.ts} (65%) rename src/plugins/discover/public/application/angular/context/api/{context.predecessors.test.js => context.predecessors.test.ts} (80%) rename src/plugins/discover/public/application/angular/context/api/{context.successors.test.js => context.successors.test.ts} (83%) rename src/plugins/discover/public/application/angular/context/query/{actions.js => actions.tsx} (66%) rename src/plugins/discover/public/application/angular/context/query/{index.js => index.ts} (100%) rename src/plugins/discover/public/application/angular/context/query/{state.js => state.ts} (57%) rename src/plugins/discover/public/application/angular/context/query_parameters/{actions.js => actions.ts} (51%) rename src/plugins/discover/public/application/angular/context/query_parameters/{index.js => index.ts} (100%) create mode 100644 src/plugins/discover/public/application/angular/context_app_state.ts diff --git a/src/plugins/discover/public/application/angular/context/api/_stubs.js b/src/plugins/discover/public/application/angular/context/api/_stubs.ts similarity index 58% rename from src/plugins/discover/public/application/angular/context/api/_stubs.js rename to src/plugins/discover/public/application/angular/context/api/_stubs.ts index 6930e96a0d411..91cf34c44efcd 100644 --- a/src/plugins/discover/public/application/angular/context/api/_stubs.js +++ b/src/plugins/discover/public/application/angular/context/api/_stubs.ts @@ -9,8 +9,11 @@ import sinon from 'sinon'; import moment from 'moment'; +import { IndexPatternsContract } from '../../../../../../data/public'; +import { EsHitRecord, EsHitRecordList } from './context'; + export function createIndexPatternsStub() { - return { + return ({ get: sinon.spy((indexPatternId) => Promise.resolve({ id: indexPatternId, @@ -18,62 +21,58 @@ export function createIndexPatternsStub() { popularizeField: () => {}, }) ), - }; + } as unknown) as IndexPatternsContract; } /** * A stubbed search source with a `fetch` method that returns all of `_stubHits`. */ -export function createSearchSourceStub(hits, timeField) { - const searchSourceStub = { +export function createSearchSourceStub(hits: Array>, timeField?: string) { + const searchSourceStub: any = { _stubHits: hits, _stubTimeField: timeField, - _createStubHit: (timestamp, tiebreaker = 0) => ({ + _createStubHit: (timestamp: number, tiebreaker = 0) => ({ [searchSourceStub._stubTimeField]: timestamp, sort: [timestamp, tiebreaker], }), + setParent: sinon.spy(() => searchSourceStub), + setField: sinon.spy(() => searchSourceStub), + removeField: sinon.spy(() => searchSourceStub), + getField: sinon.spy((key) => { + const previousSetCall = searchSourceStub.setField.withArgs(key).lastCall; + return previousSetCall ? previousSetCall.args[1] : null; + }), + fetch: sinon.spy(() => + Promise.resolve({ + hits: { + hits: searchSourceStub._stubHits, + total: searchSourceStub._stubHits.length, + }, + }) + ), }; - - searchSourceStub.setParent = sinon.spy(() => searchSourceStub); - searchSourceStub.setField = sinon.spy(() => searchSourceStub); - searchSourceStub.removeField = sinon.spy(() => searchSourceStub); - - searchSourceStub.getField = sinon.spy((key) => { - const previousSetCall = searchSourceStub.setField.withArgs(key).lastCall; - return previousSetCall ? previousSetCall.args[1] : null; - }); - - searchSourceStub.fetch = sinon.spy(() => - Promise.resolve({ - hits: { - hits: searchSourceStub._stubHits, - total: searchSourceStub._stubHits.length, - }, - }) - ); - return searchSourceStub; } /** * A stubbed search source with a `fetch` method that returns a filtered set of `_stubHits`. */ -export function createContextSearchSourceStub(hits, timeField = '@timestamp') { - const searchSourceStub = createSearchSourceStub(hits, timeField); +export function createContextSearchSourceStub(hits: EsHitRecordList, timeFieldName = '@timestamp') { + const searchSourceStub = createSearchSourceStub(hits, timeFieldName); searchSourceStub.fetch = sinon.spy(() => { - const timeField = searchSourceStub._stubTimeField; + const timeField: keyof EsHitRecord = searchSourceStub._stubTimeField; const lastQuery = searchSourceStub.setField.withArgs('query').lastCall.args[1]; const timeRange = lastQuery.query.bool.must.constant_score.filter.range[timeField]; const lastSort = searchSourceStub.setField.withArgs('sort').lastCall.args[1]; const sortDirection = lastSort[0][timeField].order; const sortFunction = sortDirection === 'asc' - ? (first, second) => first[timeField] - second[timeField] - : (first, second) => second[timeField] - first[timeField]; + ? (first: any, second: any) => first[timeField] - second[timeField] + : (first: any, second: any) => second[timeField] - first[timeField]; const filteredHits = searchSourceStub._stubHits .filter( - (hit) => + (hit: EsHitRecord) => moment(hit[timeField]).isSameOrAfter(timeRange.gte) && moment(hit[timeField]).isSameOrBefore(timeRange.lte) ) @@ -87,5 +86,5 @@ export function createContextSearchSourceStub(hits, timeField = '@timestamp') { }); }); - return searchSourceStub; + return searchSourceStub as sinon.SinonStub; } diff --git a/src/plugins/discover/public/application/angular/context/api/anchor.test.js b/src/plugins/discover/public/application/angular/context/api/anchor.test.ts similarity index 75% rename from src/plugins/discover/public/application/angular/context/api/anchor.test.js rename to src/plugins/discover/public/application/angular/context/api/anchor.test.ts index 12b9b4ab28556..4698dc8380e3c 100644 --- a/src/plugins/discover/public/application/angular/context/api/anchor.test.js +++ b/src/plugins/discover/public/application/angular/context/api/anchor.test.ts @@ -6,24 +6,32 @@ * Side Public License, v 1. */ -import { createIndexPatternsStub, createSearchSourceStub } from './_stubs'; +import { Dictionary } from 'lodash'; -import { fetchAnchorProvider } from './anchor'; +import { SortDirection } from '../../../../../../data/public'; +import { createIndexPatternsStub, createSearchSourceStub } from './_stubs'; +import { AnchorHitRecord, fetchAnchorProvider } from './anchor'; describe('context app', function () { - describe('function fetchAnchor', function () { - let fetchAnchor; - let searchSourceStub; + let fetchAnchor: ( + indexPatternId: string, + anchorId: string, + sort: [Dictionary, { [key: string]: SortDirection }] + ) => Promise; + let searchSourceStub: any; + describe('function fetchAnchor', function () { beforeEach(() => { - searchSourceStub = createSearchSourceStub([{ _id: 'hit1' }]); + searchSourceStub = createSearchSourceStub([ + { _id: 'hit1', fields: [], sort: [], _source: {} }, + ]); fetchAnchor = fetchAnchorProvider(createIndexPatternsStub(), searchSourceStub); }); it('should use the `fetch` method of the SearchSource', function () { return fetchAnchor('INDEX_PATTERN_ID', 'id', [ - { '@timestamp': 'desc' }, - { _doc: 'desc' }, + { '@timestamp': SortDirection.desc }, + { _doc: SortDirection.desc }, ]).then(() => { expect(searchSourceStub.fetch.calledOnce).toBe(true); }); @@ -31,8 +39,8 @@ describe('context app', function () { it('should configure the SearchSource to not inherit from the implicit root', function () { return fetchAnchor('INDEX_PATTERN_ID', 'id', [ - { '@timestamp': 'desc' }, - { _doc: 'desc' }, + { '@timestamp': SortDirection.desc }, + { _doc: SortDirection.desc }, ]).then(() => { const setParentSpy = searchSourceStub.setParent; expect(setParentSpy.calledOnce).toBe(true); @@ -42,8 +50,8 @@ describe('context app', function () { it('should set the SearchSource index pattern', function () { return fetchAnchor('INDEX_PATTERN_ID', 'id', [ - { '@timestamp': 'desc' }, - { _doc: 'desc' }, + { '@timestamp': SortDirection.desc }, + { _doc: SortDirection.desc }, ]).then(() => { const setFieldSpy = searchSourceStub.setField; expect(setFieldSpy.firstCall.args[1].id).toEqual('INDEX_PATTERN_ID'); @@ -52,8 +60,8 @@ describe('context app', function () { it('should set the SearchSource version flag to true', function () { return fetchAnchor('INDEX_PATTERN_ID', 'id', [ - { '@timestamp': 'desc' }, - { _doc: 'desc' }, + { '@timestamp': SortDirection.desc }, + { _doc: SortDirection.desc }, ]).then(() => { const setVersionSpy = searchSourceStub.setField.withArgs('version'); expect(setVersionSpy.calledOnce).toBe(true); @@ -63,8 +71,8 @@ describe('context app', function () { it('should set the SearchSource size to 1', function () { return fetchAnchor('INDEX_PATTERN_ID', 'id', [ - { '@timestamp': 'desc' }, - { _doc: 'desc' }, + { '@timestamp': SortDirection.desc }, + { _doc: SortDirection.desc }, ]).then(() => { const setSizeSpy = searchSourceStub.setField.withArgs('size'); expect(setSizeSpy.calledOnce).toBe(true); @@ -74,8 +82,8 @@ describe('context app', function () { it('should set the SearchSource query to an ids query', function () { return fetchAnchor('INDEX_PATTERN_ID', 'id', [ - { '@timestamp': 'desc' }, - { _doc: 'desc' }, + { '@timestamp': SortDirection.desc }, + { _doc: SortDirection.desc }, ]).then(() => { const setQuerySpy = searchSourceStub.setField.withArgs('query'); expect(setQuerySpy.calledOnce).toBe(true); @@ -96,12 +104,15 @@ describe('context app', function () { it('should set the SearchSource sort order', function () { return fetchAnchor('INDEX_PATTERN_ID', 'id', [ - { '@timestamp': 'desc' }, - { _doc: 'desc' }, + { '@timestamp': SortDirection.desc }, + { _doc: SortDirection.desc }, ]).then(() => { const setSortSpy = searchSourceStub.setField.withArgs('sort'); expect(setSortSpy.calledOnce).toBe(true); - expect(setSortSpy.firstCall.args[1]).toEqual([{ '@timestamp': 'desc' }, { _doc: 'desc' }]); + expect(setSortSpy.firstCall.args[1]).toEqual([ + { '@timestamp': SortDirection.desc }, + { _doc: SortDirection.desc }, + ]); }); }); @@ -109,11 +120,11 @@ describe('context app', function () { searchSourceStub._stubHits = []; return fetchAnchor('INDEX_PATTERN_ID', 'id', [ - { '@timestamp': 'desc' }, - { _doc: 'desc' }, + { '@timestamp': SortDirection.desc }, + { _doc: SortDirection.desc }, ]).then( () => { - expect().fail('expected the promise to be rejected'); + fail('expected the promise to be rejected'); }, (error) => { expect(error).toBeInstanceOf(Error); @@ -125,8 +136,8 @@ describe('context app', function () { searchSourceStub._stubHits = [{ property1: 'value1' }, { property2: 'value2' }]; return fetchAnchor('INDEX_PATTERN_ID', 'id', [ - { '@timestamp': 'desc' }, - { _doc: 'desc' }, + { '@timestamp': SortDirection.desc }, + { _doc: SortDirection.desc }, ]).then((anchorDocument) => { expect(anchorDocument).toHaveProperty('property1', 'value1'); expect(anchorDocument).toHaveProperty('$$_isAnchor', true); @@ -135,9 +146,6 @@ describe('context app', function () { }); describe('useNewFields API', () => { - let fetchAnchor; - let searchSourceStub; - beforeEach(() => { searchSourceStub = createSearchSourceStub([{ _id: 'hit1' }]); fetchAnchor = fetchAnchorProvider(createIndexPatternsStub(), searchSourceStub, true); @@ -147,8 +155,8 @@ describe('context app', function () { searchSourceStub._stubHits = [{ property1: 'value1' }, { property2: 'value2' }]; return fetchAnchor('INDEX_PATTERN_ID', 'id', [ - { '@timestamp': 'desc' }, - { _doc: 'desc' }, + { '@timestamp': SortDirection.desc }, + { _doc: SortDirection.desc }, ]).then(() => { const setFieldsSpy = searchSourceStub.setField.withArgs('fields'); const removeFieldsSpy = searchSourceStub.removeField.withArgs('fieldsFromSource'); diff --git a/src/plugins/discover/public/application/angular/context/api/anchor.js b/src/plugins/discover/public/application/angular/context/api/anchor.ts similarity index 65% rename from src/plugins/discover/public/application/angular/context/api/anchor.js rename to src/plugins/discover/public/application/angular/context/api/anchor.ts index 83b611cb0d648..e3e76a74f1688 100644 --- a/src/plugins/discover/public/application/angular/context/api/anchor.js +++ b/src/plugins/discover/public/application/angular/context/api/anchor.ts @@ -6,11 +6,27 @@ * Side Public License, v 1. */ -import _ from 'lodash'; +import _, { Dictionary } from 'lodash'; import { i18n } from '@kbn/i18n'; -export function fetchAnchorProvider(indexPatterns, searchSource, useNewFieldsApi = false) { - return async function fetchAnchor(indexPatternId, anchorId, sort) { +import { ISearchSource, IndexPatternsContract, SortDirection } from '../../../../../../data/public'; +import { EsHitRecord } from './context'; + +export interface AnchorHitRecord extends EsHitRecord { + // eslint-disable-next-line @typescript-eslint/naming-convention + $$_isAnchor: boolean; +} + +export function fetchAnchorProvider( + indexPatterns: IndexPatternsContract, + searchSource: ISearchSource, + useNewFieldsApi: boolean = false +) { + return async function fetchAnchor( + indexPatternId: string, + anchorId: string, + sort: [Dictionary, { [key: string]: SortDirection }] + ): Promise { const indexPattern = await indexPatterns.get(indexPatternId); searchSource .setParent(undefined) @@ -46,7 +62,8 @@ export function fetchAnchorProvider(indexPatterns, searchSource, useNewFieldsApi return { ..._.get(response, ['hits', 'hits', 0]), + // eslint-disable-next-line @typescript-eslint/naming-convention $$_isAnchor: true, - }; + } as AnchorHitRecord; }; } diff --git a/src/plugins/discover/public/application/angular/context/api/context.predecessors.test.js b/src/plugins/discover/public/application/angular/context/api/context.predecessors.test.ts similarity index 80% rename from src/plugins/discover/public/application/angular/context/api/context.predecessors.test.js rename to src/plugins/discover/public/application/angular/context/api/context.predecessors.test.ts index 9f5e62da398d2..c75f09f8a9e5c 100644 --- a/src/plugins/discover/public/application/angular/context/api/context.predecessors.test.js +++ b/src/plugins/discover/public/application/angular/context/api/context.predecessors.test.ts @@ -9,8 +9,10 @@ import moment from 'moment'; import { get, last } from 'lodash'; import { createIndexPatternsStub, createContextSearchSourceStub } from './_stubs'; -import { fetchContextProvider } from './context'; -import { setServices } from '../../../../kibana_services'; +import { EsHitRecordList, fetchContextProvider } from './context'; +import { setServices, SortDirection } from '../../../../kibana_services'; +import { AnchorHitRecord } from './anchor'; +import { Query } from '../../../../../../data/public'; const MS_PER_DAY = 24 * 60 * 60 * 1000; const ANCHOR_TIMESTAMP = new Date(MS_PER_DAY).toJSON(); @@ -18,13 +20,28 @@ const ANCHOR_TIMESTAMP_3 = new Date(MS_PER_DAY * 3).toJSON(); const ANCHOR_TIMESTAMP_1000 = new Date(MS_PER_DAY * 1000).toJSON(); const ANCHOR_TIMESTAMP_3000 = new Date(MS_PER_DAY * 3000).toJSON(); +interface Timestamp { + format: string; + gte?: string; + lte?: string; +} + describe('context app', function () { - describe('function fetchPredecessors', function () { - let fetchPredecessors; - let mockSearchSource; + let fetchPredecessors: ( + indexPatternId: string, + timeField: string, + sortDir: SortDirection, + timeValIso: string, + timeValNr: number, + tieBreakerField: string, + tieBreakerValue: number, + size: number + ) => Promise; + let mockSearchSource: any; + describe('function fetchPredecessors', function () { beforeEach(() => { - mockSearchSource = createContextSearchSourceStub([], '@timestamp', MS_PER_DAY * 8); + mockSearchSource = createContextSearchSourceStub([], '@timestamp'); setServices({ data: { @@ -44,7 +61,7 @@ describe('context app', function () { timeValNr, tieBreakerField, tieBreakerValue, - size + size = 10 ) => { const anchor = { _source: { @@ -56,7 +73,7 @@ describe('context app', function () { return fetchContextProvider(createIndexPatternsStub()).fetchSurroundingDocs( 'predecessors', indexPatternId, - anchor, + anchor as AnchorHitRecord, timeField, tieBreakerField, sortDir, @@ -78,14 +95,13 @@ describe('context app', function () { return fetchPredecessors( 'INDEX_PATTERN_ID', '@timestamp', - 'desc', + SortDirection.desc, ANCHOR_TIMESTAMP_3000, MS_PER_DAY * 3000, '_doc', 0, - 3, - [] - ).then((hits) => { + 3 + ).then((hits: EsHitRecordList) => { expect(mockSearchSource.fetch.calledOnce).toBe(true); expect(hits).toEqual(mockSearchSource._stubHits.slice(0, 3)); }); @@ -103,17 +119,16 @@ describe('context app', function () { return fetchPredecessors( 'INDEX_PATTERN_ID', '@timestamp', - 'desc', + SortDirection.desc, ANCHOR_TIMESTAMP_3000, MS_PER_DAY * 3000, '_doc', 0, - 6, - [] - ).then((hits) => { - const intervals = mockSearchSource.setField.args - .filter(([property]) => property === 'query') - .map(([, { query }]) => + 6 + ).then((hits: EsHitRecordList) => { + const intervals: Timestamp[] = mockSearchSource.setField.args + .filter(([property]: string) => property === 'query') + .map(([, { query }]: [string, { query: Query }]) => get(query, ['bool', 'must', 'constant_score', 'filter', 'range', '@timestamp']) ); @@ -123,7 +138,7 @@ describe('context app', function () { // should have started at the given time expect(intervals[0].gte).toEqual(moment(MS_PER_DAY * 3000).toISOString()); // should have ended with a half-open interval - expect(Object.keys(last(intervals))).toEqual(['format', 'gte']); + expect(Object.keys(last(intervals) ?? {})).toEqual(['format', 'gte']); expect(intervals.length).toBeGreaterThan(1); expect(hits).toEqual(mockSearchSource._stubHits.slice(0, 3)); @@ -141,24 +156,23 @@ describe('context app', function () { return fetchPredecessors( 'INDEX_PATTERN_ID', '@timestamp', - 'desc', + SortDirection.desc, ANCHOR_TIMESTAMP_1000, MS_PER_DAY * 1000, '_doc', 0, - 3, - [] - ).then((hits) => { - const intervals = mockSearchSource.setField.args - .filter(([property]) => property === 'query') - .map(([, { query }]) => - get(query, ['bool', 'must', 'constant_score', 'filter', 'range', '@timestamp']) - ); + 3 + ).then((hits: EsHitRecordList) => { + const intervals: Timestamp[] = mockSearchSource.setField.args + .filter(([property]: string) => property === 'query') + .map(([, { query }]: [string, { query: Query }]) => { + return get(query, ['bool', 'must', 'constant_score', 'filter', 'range', '@timestamp']); + }); // should have started at the given time expect(intervals[0].gte).toEqual(moment(MS_PER_DAY * 1000).toISOString()); // should have stopped before reaching MS_PER_DAY * 1700 - expect(moment(last(intervals).lte).valueOf()).toBeLessThan(MS_PER_DAY * 1700); + expect(moment(last(intervals)?.lte).valueOf()).toBeLessThan(MS_PER_DAY * 1700); expect(intervals.length).toBeGreaterThan(1); expect(hits).toEqual(mockSearchSource._stubHits.slice(-3)); }); @@ -168,14 +182,13 @@ describe('context app', function () { return fetchPredecessors( 'INDEX_PATTERN_ID', '@timestamp', - 'desc', + SortDirection.desc, ANCHOR_TIMESTAMP_3, MS_PER_DAY * 3, '_doc', 0, - 3, - [] - ).then((hits) => { + 3 + ).then((hits: EsHitRecordList) => { expect(hits).toEqual([]); }); }); @@ -184,13 +197,12 @@ describe('context app', function () { return fetchPredecessors( 'INDEX_PATTERN_ID', '@timestamp', - 'desc', + SortDirection.desc, ANCHOR_TIMESTAMP_3, MS_PER_DAY * 3, '_doc', 0, - 3, - [] + 3 ).then(() => { const setParentSpy = mockSearchSource.setParent; expect(setParentSpy.alwaysCalledWith(undefined)).toBe(true); @@ -202,13 +214,12 @@ describe('context app', function () { return fetchPredecessors( 'INDEX_PATTERN_ID', '@timestamp', - 'desc', + SortDirection.desc, ANCHOR_TIMESTAMP, MS_PER_DAY, '_doc', 0, - 3, - [] + 3 ).then(() => { expect( mockSearchSource.setField.calledWith('sort', [ @@ -221,11 +232,8 @@ describe('context app', function () { }); describe('function fetchPredecessors with useNewFieldsApi set', function () { - let fetchPredecessors; - let mockSearchSource; - beforeEach(() => { - mockSearchSource = createContextSearchSourceStub([], '@timestamp', MS_PER_DAY * 8); + mockSearchSource = createContextSearchSourceStub([], '@timestamp'); setServices({ data: { @@ -245,7 +253,7 @@ describe('context app', function () { timeValNr, tieBreakerField, tieBreakerValue, - size + size = 10 ) => { const anchor = { _source: { @@ -257,7 +265,7 @@ describe('context app', function () { return fetchContextProvider(createIndexPatternsStub(), true).fetchSurroundingDocs( 'predecessors', indexPatternId, - anchor, + anchor as AnchorHitRecord, timeField, tieBreakerField, sortDir, @@ -279,14 +287,13 @@ describe('context app', function () { return fetchPredecessors( 'INDEX_PATTERN_ID', '@timestamp', - 'desc', + SortDirection.desc, ANCHOR_TIMESTAMP_3000, MS_PER_DAY * 3000, '_doc', 0, - 3, - [] - ).then((hits) => { + 3 + ).then((hits: EsHitRecordList) => { const setFieldsSpy = mockSearchSource.setField.withArgs('fields'); const removeFieldsSpy = mockSearchSource.removeField.withArgs('fieldsFromSource'); expect(mockSearchSource.fetch.calledOnce).toBe(true); diff --git a/src/plugins/discover/public/application/angular/context/api/context.successors.test.js b/src/plugins/discover/public/application/angular/context/api/context.successors.test.ts similarity index 83% rename from src/plugins/discover/public/application/angular/context/api/context.successors.test.js rename to src/plugins/discover/public/application/angular/context/api/context.successors.test.ts index 4936c937aa2fa..3c2026b5282e5 100644 --- a/src/plugins/discover/public/application/angular/context/api/context.successors.test.js +++ b/src/plugins/discover/public/application/angular/context/api/context.successors.test.ts @@ -10,20 +10,36 @@ import moment from 'moment'; import { get, last } from 'lodash'; import { createIndexPatternsStub, createContextSearchSourceStub } from './_stubs'; -import { setServices } from '../../../../kibana_services'; - -import { fetchContextProvider } from './context'; +import { setServices, SortDirection } from '../../../../kibana_services'; +import { Query } from '../../../../../../data/public'; +import { EsHitRecordList, fetchContextProvider } from './context'; +import { AnchorHitRecord } from './anchor'; const MS_PER_DAY = 24 * 60 * 60 * 1000; const ANCHOR_TIMESTAMP = new Date(MS_PER_DAY).toJSON(); const ANCHOR_TIMESTAMP_3 = new Date(MS_PER_DAY * 3).toJSON(); const ANCHOR_TIMESTAMP_3000 = new Date(MS_PER_DAY * 3000).toJSON(); +interface Timestamp { + format: string; + gte?: string; + lte?: string; +} + describe('context app', function () { - describe('function fetchSuccessors', function () { - let fetchSuccessors; - let mockSearchSource; + let fetchSuccessors: ( + indexPatternId: string, + timeField: string, + sortDir: SortDirection, + timeValIso: string, + timeValNr: number, + tieBreakerField: string, + tieBreakerValue: number, + size: number + ) => Promise; + let mockSearchSource: any; + describe('function fetchSuccessors', function () { beforeEach(() => { mockSearchSource = createContextSearchSourceStub([], '@timestamp'); @@ -57,7 +73,7 @@ describe('context app', function () { return fetchContextProvider(createIndexPatternsStub()).fetchSurroundingDocs( 'successors', indexPatternId, - anchor, + anchor as AnchorHitRecord, timeField, tieBreakerField, sortDir, @@ -79,13 +95,12 @@ describe('context app', function () { return fetchSuccessors( 'INDEX_PATTERN_ID', '@timestamp', - 'desc', + SortDirection.desc, ANCHOR_TIMESTAMP_3000, MS_PER_DAY * 3000, '_doc', 0, - 3, - [] + 3 ).then((hits) => { expect(mockSearchSource.fetch.calledOnce).toBe(true); expect(hits).toEqual(mockSearchSource._stubHits.slice(-3)); @@ -104,17 +119,16 @@ describe('context app', function () { return fetchSuccessors( 'INDEX_PATTERN_ID', '@timestamp', - 'desc', + SortDirection.desc, ANCHOR_TIMESTAMP_3000, MS_PER_DAY * 3000, '_doc', 0, - 6, - [] + 6 ).then((hits) => { - const intervals = mockSearchSource.setField.args - .filter(([property]) => property === 'query') - .map(([, { query }]) => + const intervals: Timestamp[] = mockSearchSource.setField.args + .filter(([property]: [string]) => property === 'query') + .map(([, { query }]: [string, { query: Query }]) => get(query, ['bool', 'must', 'constant_score', 'filter', 'range', '@timestamp']) ); @@ -124,7 +138,7 @@ describe('context app', function () { // should have started at the given time expect(intervals[0].lte).toEqual(moment(MS_PER_DAY * 3000).toISOString()); // should have ended with a half-open interval - expect(Object.keys(last(intervals))).toEqual(['format', 'lte']); + expect(Object.keys(last(intervals) ?? {})).toEqual(['format', 'lte']); expect(intervals.length).toBeGreaterThan(1); expect(hits).toEqual(mockSearchSource._stubHits.slice(-3)); @@ -144,24 +158,23 @@ describe('context app', function () { return fetchSuccessors( 'INDEX_PATTERN_ID', '@timestamp', - 'desc', + SortDirection.desc, ANCHOR_TIMESTAMP_3000, MS_PER_DAY * 3000, '_doc', 0, - 4, - [] + 4 ).then((hits) => { - const intervals = mockSearchSource.setField.args - .filter(([property]) => property === 'query') - .map(([, { query }]) => + const intervals: Timestamp[] = mockSearchSource.setField.args + .filter(([property]: [string]) => property === 'query') + .map(([, { query }]: [string, { query: Query }]) => get(query, ['bool', 'must', 'constant_score', 'filter', 'range', '@timestamp']) ); // should have started at the given time expect(intervals[0].lte).toEqual(moment(MS_PER_DAY * 3000).toISOString()); // should have stopped before reaching MS_PER_DAY * 2200 - expect(moment(last(intervals).gte).valueOf()).toBeGreaterThan(MS_PER_DAY * 2200); + expect(moment(last(intervals)?.gte).valueOf()).toBeGreaterThan(MS_PER_DAY * 2200); expect(intervals.length).toBeGreaterThan(1); expect(hits).toEqual(mockSearchSource._stubHits.slice(0, 4)); @@ -172,13 +185,12 @@ describe('context app', function () { return fetchSuccessors( 'INDEX_PATTERN_ID', '@timestamp', - 'desc', + SortDirection.desc, ANCHOR_TIMESTAMP_3, MS_PER_DAY * 3, '_doc', 0, - 3, - [] + 3 ).then((hits) => { expect(hits).toEqual([]); }); @@ -188,13 +200,12 @@ describe('context app', function () { return fetchSuccessors( 'INDEX_PATTERN_ID', '@timestamp', - 'desc', + SortDirection.desc, ANCHOR_TIMESTAMP_3, MS_PER_DAY * 3, '_doc', 0, - 3, - [] + 3 ).then(() => { const setParentSpy = mockSearchSource.setParent; expect(setParentSpy.alwaysCalledWith(undefined)).toBe(true); @@ -206,18 +217,17 @@ describe('context app', function () { return fetchSuccessors( 'INDEX_PATTERN_ID', '@timestamp', - 'desc', + SortDirection.desc, ANCHOR_TIMESTAMP, MS_PER_DAY, '_doc', 0, - 3, - [] + 3 ).then(() => { expect( mockSearchSource.setField.calledWith('sort', [ - { '@timestamp': { order: 'desc', format: 'strict_date_optional_time' } }, - { _doc: 'desc' }, + { '@timestamp': { order: SortDirection.desc, format: 'strict_date_optional_time' } }, + { _doc: SortDirection.desc }, ]) ).toBe(true); }); @@ -225,9 +235,6 @@ describe('context app', function () { }); describe('function fetchSuccessors with useNewFieldsApi set', function () { - let fetchSuccessors; - let mockSearchSource; - beforeEach(() => { mockSearchSource = createContextSearchSourceStub([], '@timestamp'); @@ -261,7 +268,7 @@ describe('context app', function () { return fetchContextProvider(createIndexPatternsStub(), true).fetchSurroundingDocs( 'successors', indexPatternId, - anchor, + anchor as AnchorHitRecord, timeField, tieBreakerField, sortDir, @@ -283,13 +290,12 @@ describe('context app', function () { return fetchSuccessors( 'INDEX_PATTERN_ID', '@timestamp', - 'desc', + SortDirection.desc, ANCHOR_TIMESTAMP_3000, MS_PER_DAY * 3000, '_doc', 0, - 3, - [] + 3 ).then((hits) => { expect(mockSearchSource.fetch.calledOnce).toBe(true); expect(hits).toEqual(mockSearchSource._stubHits.slice(-3)); diff --git a/src/plugins/discover/public/application/angular/context/api/context.ts b/src/plugins/discover/public/application/angular/context/api/context.ts index 820e37d754ef2..e341c1e78dbe0 100644 --- a/src/plugins/discover/public/application/angular/context/api/context.ts +++ b/src/plugins/discover/public/application/angular/context/api/context.ts @@ -14,6 +14,7 @@ import { generateIntervals } from './utils/generate_intervals'; import { getEsQuerySearchAfter } from './utils/get_es_query_search_after'; import { getEsQuerySort } from './utils/get_es_query_sort'; import { getServices } from '../../../../kibana_services'; +import { AnchorHitRecord } from './anchor'; export type SurrDocType = 'successors' | 'predecessors'; export interface EsHitRecord { @@ -39,7 +40,7 @@ function fetchContextProvider(indexPatterns: IndexPatternsContract, useNewFields * * @param {SurrDocType} type - `successors` or `predecessors` * @param {string} indexPatternId - * @param {EsHitRecord} anchor - anchor record + * @param {AnchorHitRecord} anchor - anchor record * @param {string} timeField - name of the timefield, that's sorted on * @param {string} tieBreakerField - name of the tie breaker, the 2nd sort field * @param {SortDirection} sortDir - direction of sorting @@ -50,13 +51,13 @@ function fetchContextProvider(indexPatterns: IndexPatternsContract, useNewFields async function fetchSurroundingDocs( type: SurrDocType, indexPatternId: string, - anchor: EsHitRecord, + anchor: AnchorHitRecord, timeField: string, tieBreakerField: string, sortDir: SortDirection, size: number, filters: Filter[] - ) { + ): Promise { if (typeof anchor !== 'object' || anchor === null || !size) { return []; } diff --git a/src/plugins/discover/public/application/angular/context/query/actions.js b/src/plugins/discover/public/application/angular/context/query/actions.tsx similarity index 66% rename from src/plugins/discover/public/application/angular/context/query/actions.js rename to src/plugins/discover/public/application/angular/context/query/actions.tsx index 493697ff38ff1..6fadafe3a348b 100644 --- a/src/plugins/discover/public/application/angular/context/query/actions.js +++ b/src/plugins/discover/public/application/angular/context/query/actions.tsx @@ -8,17 +8,26 @@ import _ from 'lodash'; import { i18n } from '@kbn/i18n'; -import React from 'react'; import { getServices } from '../../../../kibana_services'; -import { fetchAnchorProvider } from '../api/anchor'; -import { fetchContextProvider } from '../api/context'; +import { AnchorHitRecord, fetchAnchorProvider } from '../api/anchor'; +import { EsHitRecord, EsHitRecordList, fetchContextProvider, SurrDocType } from '../api/context'; import { getQueryParameterActions } from '../query_parameters'; -import { FAILURE_REASONS, LOADING_STATUS } from './index'; -import { MarkdownSimple } from '../../../../../../kibana_react/public'; import { SEARCH_FIELDS_FROM_SOURCE } from '../../../../../common'; +import { + ContextAppState, + FailureReason, + LoadingStatus, + LoadingStatusEntry, + LoadingStatusState, + QueryParameters, +} from '../../context_app_state'; + +interface DiscoverPromise extends PromiseConstructor { + try: (fn: () => Promise) => Promise; +} -export function QueryActionsProvider(Promise) { +export function QueryActionsProvider(Promise: DiscoverPromise) { const { filterManager, indexPatterns, data, uiSettings } = getServices(); const useNewFieldsApi = !uiSettings.get(SEARCH_FIELDS_FROM_SOURCE); const fetchAnchor = fetchAnchorProvider( @@ -32,24 +41,27 @@ export function QueryActionsProvider(Promise) { indexPatterns ); - const setFailedStatus = (state) => (subject, details = {}) => + const setFailedStatus = (state: ContextAppState) => ( + subject: keyof LoadingStatusState, + details: LoadingStatusEntry = {} + ) => (state.loadingStatus[subject] = { - status: LOADING_STATUS.FAILED, - reason: FAILURE_REASONS.UNKNOWN, + status: LoadingStatus.FAILED, + reason: FailureReason.UNKNOWN, ...details, }); - const setLoadedStatus = (state) => (subject) => + const setLoadedStatus = (state: ContextAppState) => (subject: keyof LoadingStatusState) => (state.loadingStatus[subject] = { - status: LOADING_STATUS.LOADED, + status: LoadingStatus.LOADED, }); - const setLoadingStatus = (state) => (subject) => + const setLoadingStatus = (state: ContextAppState) => (subject: keyof LoadingStatusState) => (state.loadingStatus[subject] = { - status: LOADING_STATUS.LOADING, + status: LoadingStatus.LOADING, }); - const fetchAnchorRow = (state) => () => { + const fetchAnchorRow = (state: ContextAppState) => () => { const { queryParameters: { indexPatternId, anchorId, sort, tieBreakerField }, } = state; @@ -57,7 +69,7 @@ export function QueryActionsProvider(Promise) { if (!tieBreakerField) { return Promise.reject( setFailedStatus(state)('anchor', { - reason: FAILURE_REASONS.INVALID_TIEBREAKER, + reason: FailureReason.INVALID_TIEBREAKER, }) ); } @@ -67,25 +79,25 @@ export function QueryActionsProvider(Promise) { return Promise.try(() => fetchAnchor(indexPatternId, anchorId, [_.fromPairs([sort]), { [tieBreakerField]: sort[1] }]) ).then( - (anchorDocument) => { + (anchorDocument: AnchorHitRecord) => { setLoadedStatus(state)('anchor'); state.rows.anchor = anchorDocument; return anchorDocument; }, - (error) => { + (error: Error) => { setFailedStatus(state)('anchor', { error }); getServices().toastNotifications.addDanger({ title: i18n.translate('discover.context.unableToLoadAnchorDocumentDescription', { defaultMessage: 'Unable to load the anchor document', }), - text: {error.message}, + text: error.message, }); throw error; } ); }; - const fetchSurroundingRows = (type, state) => { + const fetchSurroundingRows = (type: SurrDocType, state: ContextAppState) => { const { queryParameters: { indexPatternId, sort, tieBreakerField }, rows: { anchor }, @@ -100,7 +112,7 @@ export function QueryActionsProvider(Promise) { if (!tieBreakerField) { return Promise.reject( setFailedStatus(state)(type, { - reason: FAILURE_REASONS.INVALID_TIEBREAKER, + reason: FailureReason.INVALID_TIEBREAKER, }) ); } @@ -120,54 +132,62 @@ export function QueryActionsProvider(Promise) { filters ) ).then( - (documents) => { + (documents: EsHitRecordList) => { setLoadedStatus(state)(type); state.rows[type] = documents; return documents; }, - (error) => { + (error: Error) => { setFailedStatus(state)(type, { error }); getServices().toastNotifications.addDanger({ title: i18n.translate('discover.context.unableToLoadDocumentDescription', { defaultMessage: 'Unable to load documents', }), - text: {error.message}, + text: error.message, }); throw error; } ); }; - const fetchContextRows = (state) => () => + const fetchContextRows = (state: ContextAppState) => () => Promise.all([ fetchSurroundingRows('predecessors', state), fetchSurroundingRows('successors', state), ]); - const fetchAllRows = (state) => () => + const fetchAllRows = (state: ContextAppState) => () => Promise.try(fetchAnchorRow(state)).then(fetchContextRows(state)); - const fetchContextRowsWithNewQueryParameters = (state) => (queryParameters) => { + const fetchContextRowsWithNewQueryParameters = (state: ContextAppState) => ( + queryParameters: QueryParameters + ) => { setQueryParameters(state)(queryParameters); return fetchContextRows(state)(); }; - const fetchAllRowsWithNewQueryParameters = (state) => (queryParameters) => { + const fetchAllRowsWithNewQueryParameters = (state: ContextAppState) => ( + queryParameters: QueryParameters + ) => { setQueryParameters(state)(queryParameters); return fetchAllRows(state)(); }; - const fetchGivenPredecessorRows = (state) => (count) => { + const fetchGivenPredecessorRows = (state: ContextAppState) => (count: number) => { setPredecessorCount(state)(count); return fetchSurroundingRows('predecessors', state); }; - const fetchGivenSuccessorRows = (state) => (count) => { + const fetchGivenSuccessorRows = (state: ContextAppState) => (count: number) => { setSuccessorCount(state)(count); return fetchSurroundingRows('successors', state); }; - const setAllRows = (state) => (predecessorRows, anchorRow, successorRows) => + const setAllRows = (state: ContextAppState) => ( + predecessorRows: EsHitRecordList, + anchorRow: EsHitRecord, + successorRows: EsHitRecordList + ) => (state.rows.all = [ ...(predecessorRows || []), ...(anchorRow ? [anchorRow] : []), diff --git a/src/plugins/discover/public/application/angular/context/query/index.js b/src/plugins/discover/public/application/angular/context/query/index.ts similarity index 100% rename from src/plugins/discover/public/application/angular/context/query/index.js rename to src/plugins/discover/public/application/angular/context/query/index.ts diff --git a/src/plugins/discover/public/application/angular/context/query/state.js b/src/plugins/discover/public/application/angular/context/query/state.ts similarity index 57% rename from src/plugins/discover/public/application/angular/context/query/state.js rename to src/plugins/discover/public/application/angular/context/query/state.ts index c4f3fbb80cce4..fefadf9009185 100644 --- a/src/plugins/discover/public/application/angular/context/query/state.js +++ b/src/plugins/discover/public/application/angular/context/query/state.ts @@ -6,12 +6,12 @@ * Side Public License, v 1. */ -import { LOADING_STATUS } from './index'; +import { LoadingStatus, LoadingStatusState } from '../../context_app_state'; -export function createInitialLoadingStatusState() { +export function createInitialLoadingStatusState(): LoadingStatusState { return { - anchor: LOADING_STATUS.UNINITIALIZED, - predecessors: LOADING_STATUS.UNINITIALIZED, - successors: LOADING_STATUS.UNINITIALIZED, + anchor: LoadingStatus.UNINITIALIZED, + predecessors: LoadingStatus.UNINITIALIZED, + successors: LoadingStatus.UNINITIALIZED, }; } diff --git a/src/plugins/discover/public/application/angular/context/query_parameters/actions.test.ts b/src/plugins/discover/public/application/angular/context/query_parameters/actions.test.ts index 774a29721c426..fe00d11f0d3b6 100644 --- a/src/plugins/discover/public/application/angular/context/query_parameters/actions.test.ts +++ b/src/plugins/discover/public/application/angular/context/query_parameters/actions.test.ts @@ -8,18 +8,12 @@ // @ts-ignore import { getQueryParameterActions } from './actions'; -import { FilterManager } from '../../../../../../data/public'; +import { FilterManager, SortDirection } from '../../../../../../data/public'; import { coreMock } from '../../../../../../../core/public/mocks'; +import { ContextAppState, LoadingStatus, QueryParameters } from '../../context_app_state'; const setupMock = coreMock.createSetup(); -let state: { - queryParameters: { - defaultStepSize: number; - indexPatternId: string; - predecessorCount: number; - successorCount: number; - }; -}; +let state: ContextAppState; let filterManager: FilterManager; let filterManagerSpy: jest.SpyInstance; @@ -33,7 +27,25 @@ beforeEach(() => { indexPatternId: 'INDEX_PATTERN_ID', predecessorCount: 10, successorCount: 10, + anchorId: '', + columns: [], + filters: [], + sort: ['field', SortDirection.asc], + tieBreakerField: '', + }, + loadingStatus: { + anchor: LoadingStatus.UNINITIALIZED, + predecessors: LoadingStatus.UNINITIALIZED, + successors: LoadingStatus.UNINITIALIZED, }, + rows: { + all: [], + // eslint-disable-next-line @typescript-eslint/naming-convention + anchor: { $$_isAnchor: true, fields: [], sort: [], _source: [], _id: '' }, + predecessors: [], + successors: [], + }, + useNewFieldsApi: true, }; }); @@ -105,6 +117,7 @@ describe('context query_parameter actions', function () { const newState = { ...state, queryParameters: { + ...state.queryParameters, additionalParameter: 'ADDITIONAL_PARAMETER', }, }; @@ -113,11 +126,12 @@ describe('context query_parameter actions', function () { anchorId: 'ANCHOR_ID', columns: ['column'], defaultStepSize: 3, - filters: ['filter'], + filters: [], indexPatternId: 'INDEX_PATTERN', predecessorCount: 100, successorCount: 100, - sort: ['field'], + sort: ['field', SortDirection.asc], + tieBreakerField: '', }); expect(actualState).toEqual({ @@ -125,20 +139,21 @@ describe('context query_parameter actions', function () { anchorId: 'ANCHOR_ID', columns: ['column'], defaultStepSize: 3, - filters: ['filter'], + filters: [], indexPatternId: 'INDEX_PATTERN', predecessorCount: 100, successorCount: 100, - sort: ['field'], + sort: ['field', SortDirection.asc], + tieBreakerField: '', }); }); it('should ignore invalid properties', function () { const newState = { ...state }; - setQueryParameters(newState)({ + setQueryParameters(newState)(({ additionalParameter: 'ADDITIONAL_PARAMETER', - }); + } as unknown) as QueryParameters); expect(state.queryParameters).toEqual(newState.queryParameters); }); diff --git a/src/plugins/discover/public/application/angular/context/query_parameters/actions.js b/src/plugins/discover/public/application/angular/context/query_parameters/actions.ts similarity index 51% rename from src/plugins/discover/public/application/angular/context/query_parameters/actions.js rename to src/plugins/discover/public/application/angular/context/query_parameters/actions.ts index b308196cb8b04..d7507a35e0996 100644 --- a/src/plugins/discover/public/application/angular/context/query_parameters/actions.js +++ b/src/plugins/discover/public/application/angular/context/query_parameters/actions.ts @@ -7,34 +7,46 @@ */ import _ from 'lodash'; -import { esFilters } from '../../../../../../data/public'; +import { esFilters, Filter, IFieldType } from '../../../../../../data/public'; +import { FilterManager, IndexPatternsContract } from '../../../../../../data/public'; import { popularizeField } from '../../../helpers/popularize_field'; +import { ContextAppState, QueryParameters } from '../../context_app_state'; import { MAX_CONTEXT_SIZE, MIN_CONTEXT_SIZE, QUERY_PARAMETER_KEYS } from './constants'; -export function getQueryParameterActions(filterManager, indexPatterns) { - const setPredecessorCount = (state) => (predecessorCount) => - (state.queryParameters.predecessorCount = clamp( +export function getQueryParameterActions( + filterManager: FilterManager, + indexPatterns?: IndexPatternsContract +) { + const setPredecessorCount = (state: ContextAppState) => (predecessorCount: number) => { + return (state.queryParameters.predecessorCount = clamp( MIN_CONTEXT_SIZE, MAX_CONTEXT_SIZE, predecessorCount )); + }; - const setSuccessorCount = (state) => (successorCount) => - (state.queryParameters.successorCount = clamp( + const setSuccessorCount = (state: ContextAppState) => (successorCount: number) => { + return (state.queryParameters.successorCount = clamp( MIN_CONTEXT_SIZE, MAX_CONTEXT_SIZE, successorCount )); + }; - const setQueryParameters = (state) => (queryParameters) => - Object.assign(state.queryParameters, _.pick(queryParameters, QUERY_PARAMETER_KEYS)); + const setQueryParameters = (state: ContextAppState) => (queryParameters: QueryParameters) => { + return Object.assign(state.queryParameters, _.pick(queryParameters, QUERY_PARAMETER_KEYS)); + }; - const updateFilters = () => (filters) => { + const updateFilters = () => (filters: Filter[]) => { filterManager.setFilters(filters); }; - const addFilter = (state) => async (field, values, operation) => { + const addFilter = (state: ContextAppState) => async ( + field: string | IFieldType, + values: any, + operation: string + ) => { const indexPatternId = state.queryParameters.indexPatternId; const newFilters = esFilters.generateFilters( filterManager, @@ -46,7 +58,7 @@ export function getQueryParameterActions(filterManager, indexPatterns) { filterManager.addFilters(newFilters); if (indexPatterns) { const indexPattern = await indexPatterns.get(indexPatternId); - await popularizeField(indexPattern, field.name, indexPatterns); + await popularizeField(indexPattern, (field as IFieldType).name, indexPatterns); } }; @@ -59,6 +71,6 @@ export function getQueryParameterActions(filterManager, indexPatterns) { }; } -function clamp(minimum, maximum, value) { +function clamp(minimum: number, maximum: number, value: number) { return Math.max(Math.min(maximum, value), minimum); } diff --git a/src/plugins/discover/public/application/angular/context/query_parameters/index.js b/src/plugins/discover/public/application/angular/context/query_parameters/index.ts similarity index 100% rename from src/plugins/discover/public/application/angular/context/query_parameters/index.js rename to src/plugins/discover/public/application/angular/context/query_parameters/index.ts diff --git a/src/plugins/discover/public/application/angular/context_app_state.ts b/src/plugins/discover/public/application/angular/context_app_state.ts new file mode 100644 index 0000000000000..1593b2457019c --- /dev/null +++ b/src/plugins/discover/public/application/angular/context_app_state.ts @@ -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 + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +import { Filter } from '../../../../data/public'; +import { AnchorHitRecord } from './context/api/anchor'; +import { EsHitRecordList } from './context/api/context'; +import { SortDirection } from './context/api/utils/sorting'; + +export interface ContextAppState { + loadingStatus: LoadingStatusState; + queryParameters: QueryParameters; + rows: ContextRows; + useNewFieldsApi: boolean; +} + +export enum LoadingStatus { + FAILED = 'failed', + LOADED = 'loaded', + LOADING = 'loading', + UNINITIALIZED = 'uninitialized', +} +export enum FailureReason { + UNKNOWN = 'unknown', + INVALID_TIEBREAKER = 'invalid_tiebreaker', +} +export type LoadingStatusEntry = Partial<{ + status: LoadingStatus; + reason: FailureReason; + error: Error; +}>; + +export interface LoadingStatusState { + anchor: LoadingStatusEntry | LoadingStatus; + predecessors: LoadingStatusEntry | LoadingStatus; + successors: LoadingStatusEntry | LoadingStatus; +} + +export interface QueryParameters { + anchorId: string; + columns: string[]; + defaultStepSize: number; + filters: Filter[]; + indexPatternId: string; + predecessorCount: number; + successorCount: number; + sort: [string, SortDirection]; + tieBreakerField: string; +} + +interface ContextRows { + all: EsHitRecordList; + anchor: AnchorHitRecord; + predecessors: EsHitRecordList; + successors: EsHitRecordList; +} From fae2360d35339c393a65502b3c49eaf520b03252 Mon Sep 17 00:00:00 2001 From: Dzmitry Tamashevich Date: Mon, 3 May 2021 18:34:41 +0300 Subject: [PATCH 2/5] [Discover] get rid of any types --- .../application/angular/context/api/_stubs.ts | 22 ++++++++++++------- .../angular/context/api/anchor.test.ts | 4 +++- .../context/api/context.predecessors.test.ts | 4 ++-- .../context/api/context.successors.test.ts | 4 ++-- 4 files changed, 21 insertions(+), 13 deletions(-) diff --git a/src/plugins/discover/public/application/angular/context/api/_stubs.ts b/src/plugins/discover/public/application/angular/context/api/_stubs.ts index 91cf34c44efcd..3736065492e06 100644 --- a/src/plugins/discover/public/application/angular/context/api/_stubs.ts +++ b/src/plugins/discover/public/application/angular/context/api/_stubs.ts @@ -10,7 +10,13 @@ import sinon from 'sinon'; import moment from 'moment'; import { IndexPatternsContract } from '../../../../../../data/public'; -import { EsHitRecord, EsHitRecordList } from './context'; +import { EsHitRecordList } from './context'; + +type SortHit = { + [key in string]: number; // timeField name +} & { + sort: [number, number]; +}; export function createIndexPatternsStub() { return ({ @@ -27,7 +33,7 @@ export function createIndexPatternsStub() { /** * A stubbed search source with a `fetch` method that returns all of `_stubHits`. */ -export function createSearchSourceStub(hits: Array>, timeField?: string) { +export function createSearchSourceStub(hits: EsHitRecordList, timeField?: string) { const searchSourceStub: any = { _stubHits: hits, _stubTimeField: timeField, @@ -57,22 +63,22 @@ export function createSearchSourceStub(hits: Array>, timeFi /** * A stubbed search source with a `fetch` method that returns a filtered set of `_stubHits`. */ -export function createContextSearchSourceStub(hits: EsHitRecordList, timeFieldName = '@timestamp') { - const searchSourceStub = createSearchSourceStub(hits, timeFieldName); +export function createContextSearchSourceStub(timeFieldName: string) { + const searchSourceStub = createSearchSourceStub([], timeFieldName); searchSourceStub.fetch = sinon.spy(() => { - const timeField: keyof EsHitRecord = searchSourceStub._stubTimeField; + const timeField: keyof SortHit = searchSourceStub._stubTimeField; const lastQuery = searchSourceStub.setField.withArgs('query').lastCall.args[1]; const timeRange = lastQuery.query.bool.must.constant_score.filter.range[timeField]; const lastSort = searchSourceStub.setField.withArgs('sort').lastCall.args[1]; const sortDirection = lastSort[0][timeField].order; const sortFunction = sortDirection === 'asc' - ? (first: any, second: any) => first[timeField] - second[timeField] - : (first: any, second: any) => second[timeField] - first[timeField]; + ? (first: SortHit, second: SortHit) => first[timeField] - second[timeField] + : (first: SortHit, second: SortHit) => second[timeField] - first[timeField]; const filteredHits = searchSourceStub._stubHits .filter( - (hit: EsHitRecord) => + (hit: SortHit) => moment(hit[timeField]).isSameOrAfter(timeRange.gte) && moment(hit[timeField]).isSameOrBefore(timeRange.lte) ) diff --git a/src/plugins/discover/public/application/angular/context/api/anchor.test.ts b/src/plugins/discover/public/application/angular/context/api/anchor.test.ts index 4698dc8380e3c..080a3066c7be5 100644 --- a/src/plugins/discover/public/application/angular/context/api/anchor.test.ts +++ b/src/plugins/discover/public/application/angular/context/api/anchor.test.ts @@ -147,7 +147,9 @@ describe('context app', function () { describe('useNewFields API', () => { beforeEach(() => { - searchSourceStub = createSearchSourceStub([{ _id: 'hit1' }]); + searchSourceStub = createSearchSourceStub([ + { _id: 'hit1', fields: [], sort: [], _source: {} }, + ]); fetchAnchor = fetchAnchorProvider(createIndexPatternsStub(), searchSourceStub, true); }); diff --git a/src/plugins/discover/public/application/angular/context/api/context.predecessors.test.ts b/src/plugins/discover/public/application/angular/context/api/context.predecessors.test.ts index c75f09f8a9e5c..ec567b40f8eb0 100644 --- a/src/plugins/discover/public/application/angular/context/api/context.predecessors.test.ts +++ b/src/plugins/discover/public/application/angular/context/api/context.predecessors.test.ts @@ -41,7 +41,7 @@ describe('context app', function () { describe('function fetchPredecessors', function () { beforeEach(() => { - mockSearchSource = createContextSearchSourceStub([], '@timestamp'); + mockSearchSource = createContextSearchSourceStub('@timestamp'); setServices({ data: { @@ -233,7 +233,7 @@ describe('context app', function () { describe('function fetchPredecessors with useNewFieldsApi set', function () { beforeEach(() => { - mockSearchSource = createContextSearchSourceStub([], '@timestamp'); + mockSearchSource = createContextSearchSourceStub('@timestamp'); setServices({ data: { diff --git a/src/plugins/discover/public/application/angular/context/api/context.successors.test.ts b/src/plugins/discover/public/application/angular/context/api/context.successors.test.ts index 3c2026b5282e5..987c56514b77c 100644 --- a/src/plugins/discover/public/application/angular/context/api/context.successors.test.ts +++ b/src/plugins/discover/public/application/angular/context/api/context.successors.test.ts @@ -41,7 +41,7 @@ describe('context app', function () { describe('function fetchSuccessors', function () { beforeEach(() => { - mockSearchSource = createContextSearchSourceStub([], '@timestamp'); + mockSearchSource = createContextSearchSourceStub('@timestamp'); setServices({ data: { @@ -236,7 +236,7 @@ describe('context app', function () { describe('function fetchSuccessors with useNewFieldsApi set', function () { beforeEach(() => { - mockSearchSource = createContextSearchSourceStub([], '@timestamp'); + mockSearchSource = createContextSearchSourceStub('@timestamp'); setServices({ data: { From 5c9b64ab375cd564542d39a76ae78f7dfaace6c5 Mon Sep 17 00:00:00 2001 From: Dzmitry Tamashevich Date: Tue, 4 May 2021 11:58:37 +0300 Subject: [PATCH 3/5] [Discover] replace constants with enums, update imports --- .../angular/context/api/anchor.test.ts | 6 ++---- .../application/angular/context/api/anchor.ts | 14 +++++++++----- .../angular/context/query/actions.tsx | 14 ++++++++------ .../angular/context/query/index.ts | 1 - .../context/query_parameters/actions.test.ts | 1 - .../context/query_parameters/actions.ts | 6 +++--- .../public/application/angular/context_app.js | 12 +----------- .../application/angular/context_state.ts | 4 ++-- .../components/context_app/constants.ts | 19 ------------------- .../context_app/context_app_legacy.tsx | 10 +++++----- .../context_error_message.test.tsx | 11 +++++------ .../context_error_message.tsx | 7 +++---- 12 files changed, 38 insertions(+), 67 deletions(-) delete mode 100644 src/plugins/discover/public/application/components/context_app/constants.ts diff --git a/src/plugins/discover/public/application/angular/context/api/anchor.test.ts b/src/plugins/discover/public/application/angular/context/api/anchor.test.ts index 080a3066c7be5..5f63ac1592a7c 100644 --- a/src/plugins/discover/public/application/angular/context/api/anchor.test.ts +++ b/src/plugins/discover/public/application/angular/context/api/anchor.test.ts @@ -6,9 +6,7 @@ * Side Public License, v 1. */ -import { Dictionary } from 'lodash'; - -import { SortDirection } from '../../../../../../data/public'; +import { EsQuerySortValue, SortDirection } from '../../../../../../data/public'; import { createIndexPatternsStub, createSearchSourceStub } from './_stubs'; import { AnchorHitRecord, fetchAnchorProvider } from './anchor'; @@ -16,7 +14,7 @@ describe('context app', function () { let fetchAnchor: ( indexPatternId: string, anchorId: string, - sort: [Dictionary, { [key: string]: SortDirection }] + sort: EsQuerySortValue[] ) => Promise; let searchSourceStub: any; diff --git a/src/plugins/discover/public/application/angular/context/api/anchor.ts b/src/plugins/discover/public/application/angular/context/api/anchor.ts index e3e76a74f1688..da81ce525331a 100644 --- a/src/plugins/discover/public/application/angular/context/api/anchor.ts +++ b/src/plugins/discover/public/application/angular/context/api/anchor.ts @@ -6,10 +6,14 @@ * Side Public License, v 1. */ -import _, { Dictionary } from 'lodash'; +import { get } from 'lodash'; import { i18n } from '@kbn/i18n'; -import { ISearchSource, IndexPatternsContract, SortDirection } from '../../../../../../data/public'; +import { + ISearchSource, + IndexPatternsContract, + EsQuerySortValue, +} from '../../../../../../data/public'; import { EsHitRecord } from './context'; export interface AnchorHitRecord extends EsHitRecord { @@ -25,7 +29,7 @@ export function fetchAnchorProvider( return async function fetchAnchor( indexPatternId: string, anchorId: string, - sort: [Dictionary, { [key: string]: SortDirection }] + sort: EsQuerySortValue[] ): Promise { const indexPattern = await indexPatterns.get(indexPatternId); searchSource @@ -52,7 +56,7 @@ export function fetchAnchorProvider( } const response = await searchSource.fetch(); - if (_.get(response, ['hits', 'total'], 0) < 1) { + if (get(response, ['hits', 'total'], 0) < 1) { throw new Error( i18n.translate('discover.context.failedToLoadAnchorDocumentErrorDescription', { defaultMessage: 'Failed to load anchor document.', @@ -61,7 +65,7 @@ export function fetchAnchorProvider( } return { - ..._.get(response, ['hits', 'hits', 0]), + ...get(response, ['hits', 'hits', 0]), // eslint-disable-next-line @typescript-eslint/naming-convention $$_isAnchor: true, } as AnchorHitRecord; diff --git a/src/plugins/discover/public/application/angular/context/query/actions.tsx b/src/plugins/discover/public/application/angular/context/query/actions.tsx index 6fadafe3a348b..52c56d379d259 100644 --- a/src/plugins/discover/public/application/angular/context/query/actions.tsx +++ b/src/plugins/discover/public/application/angular/context/query/actions.tsx @@ -6,14 +6,16 @@ * Side Public License, v 1. */ -import _ from 'lodash'; +import React from 'react'; +import { fromPairs } from 'lodash'; import { i18n } from '@kbn/i18n'; -import { getServices } from '../../../../kibana_services'; +import { getServices } from '../../../../kibana_services'; +import { SEARCH_FIELDS_FROM_SOURCE } from '../../../../../common'; +import { MarkdownSimple, toMountPoint } from '../../../../../../kibana_react/public'; import { AnchorHitRecord, fetchAnchorProvider } from '../api/anchor'; import { EsHitRecord, EsHitRecordList, fetchContextProvider, SurrDocType } from '../api/context'; import { getQueryParameterActions } from '../query_parameters'; -import { SEARCH_FIELDS_FROM_SOURCE } from '../../../../../common'; import { ContextAppState, FailureReason, @@ -77,7 +79,7 @@ export function QueryActionsProvider(Promise: DiscoverPromise) { setLoadingStatus(state)('anchor'); return Promise.try(() => - fetchAnchor(indexPatternId, anchorId, [_.fromPairs([sort]), { [tieBreakerField]: sort[1] }]) + fetchAnchor(indexPatternId, anchorId, [fromPairs([sort]), { [tieBreakerField]: sort[1] }]) ).then( (anchorDocument: AnchorHitRecord) => { setLoadedStatus(state)('anchor'); @@ -90,7 +92,7 @@ export function QueryActionsProvider(Promise: DiscoverPromise) { title: i18n.translate('discover.context.unableToLoadAnchorDocumentDescription', { defaultMessage: 'Unable to load the anchor document', }), - text: error.message, + text: toMountPoint({error.message}), }); throw error; } @@ -143,7 +145,7 @@ export function QueryActionsProvider(Promise: DiscoverPromise) { title: i18n.translate('discover.context.unableToLoadDocumentDescription', { defaultMessage: 'Unable to load documents', }), - text: error.message, + text: toMountPoint({error.message}), }); throw error; } diff --git a/src/plugins/discover/public/application/angular/context/query/index.ts b/src/plugins/discover/public/application/angular/context/query/index.ts index a718cdb237774..70e3dc1600472 100644 --- a/src/plugins/discover/public/application/angular/context/query/index.ts +++ b/src/plugins/discover/public/application/angular/context/query/index.ts @@ -7,5 +7,4 @@ */ export { QueryActionsProvider } from './actions'; -export { FAILURE_REASONS, LOADING_STATUS } from '../../../components/context_app/constants'; export { createInitialLoadingStatusState } from './state'; diff --git a/src/plugins/discover/public/application/angular/context/query_parameters/actions.test.ts b/src/plugins/discover/public/application/angular/context/query_parameters/actions.test.ts index fe00d11f0d3b6..b54f11e9e6706 100644 --- a/src/plugins/discover/public/application/angular/context/query_parameters/actions.test.ts +++ b/src/plugins/discover/public/application/angular/context/query_parameters/actions.test.ts @@ -6,7 +6,6 @@ * Side Public License, v 1. */ -// @ts-ignore import { getQueryParameterActions } from './actions'; import { FilterManager, SortDirection } from '../../../../../../data/public'; import { coreMock } from '../../../../../../../core/public/mocks'; diff --git a/src/plugins/discover/public/application/angular/context/query_parameters/actions.ts b/src/plugins/discover/public/application/angular/context/query_parameters/actions.ts index d7507a35e0996..03372c4fa2ccf 100644 --- a/src/plugins/discover/public/application/angular/context/query_parameters/actions.ts +++ b/src/plugins/discover/public/application/angular/context/query_parameters/actions.ts @@ -6,12 +6,12 @@ * Side Public License, v 1. */ -import _ from 'lodash'; +import { pick } from 'lodash'; + import { esFilters, Filter, IFieldType } from '../../../../../../data/public'; import { FilterManager, IndexPatternsContract } from '../../../../../../data/public'; import { popularizeField } from '../../../helpers/popularize_field'; import { ContextAppState, QueryParameters } from '../../context_app_state'; - import { MAX_CONTEXT_SIZE, MIN_CONTEXT_SIZE, QUERY_PARAMETER_KEYS } from './constants'; export function getQueryParameterActions( @@ -35,7 +35,7 @@ export function getQueryParameterActions( }; const setQueryParameters = (state: ContextAppState) => (queryParameters: QueryParameters) => { - return Object.assign(state.queryParameters, _.pick(queryParameters, QUERY_PARAMETER_KEYS)); + return Object.assign(state.queryParameters, pick(queryParameters, QUERY_PARAMETER_KEYS)); }; const updateFilters = () => (filters: Filter[]) => { diff --git a/src/plugins/discover/public/application/angular/context_app.js b/src/plugins/discover/public/application/angular/context_app.js index 04406338f49ab..a90904fa2ccea 100644 --- a/src/plugins/discover/public/application/angular/context_app.js +++ b/src/plugins/discover/public/application/angular/context_app.js @@ -21,12 +21,7 @@ import { getQueryParameterActions, QUERY_PARAMETER_KEYS, } from './context/query_parameters'; -import { - createInitialLoadingStatusState, - FAILURE_REASONS, - LOADING_STATUS, - QueryActionsProvider, -} from './context/query'; +import { createInitialLoadingStatusState, QueryActionsProvider } from './context/query'; import { callAfterBindingsWorkaround } from './context/helpers/call_after_bindings_workaround'; getAngularModule().directive('contextApp', function ContextApp() { @@ -69,11 +64,6 @@ function ContextAppController($scope, Private) { (action) => (...args) => action(this.state)(...args) ); - this.constants = { - FAILURE_REASONS, - LOADING_STATUS, - }; - $scope.$watchGroup( [ () => this.state.rows.predecessors, diff --git a/src/plugins/discover/public/application/angular/context_state.ts b/src/plugins/discover/public/application/angular/context_state.ts index 0bae006ec1f6e..4a9a729e9229b 100644 --- a/src/plugins/discover/public/application/angular/context_state.ts +++ b/src/plugins/discover/public/application/angular/context_state.ts @@ -6,7 +6,7 @@ * Side Public License, v 1. */ -import _ from 'lodash'; +import { isEqual } from 'lodash'; import { History } from 'history'; import { NotificationsStart, IUiSettingsClient } from 'kibana/public'; import { @@ -247,7 +247,7 @@ function isEqualState(stateA: AppState | GlobalState, stateB: AppState | GlobalS const { filters: stateAFilters = [], ...stateAPartial } = stateA; const { filters: stateBFilters = [], ...stateBPartial } = stateB; return ( - _.isEqual(stateAPartial, stateBPartial) && + isEqual(stateAPartial, stateBPartial) && esFilters.compareFilters(stateAFilters, stateBFilters, esFilters.COMPARE_ALL_OPTIONS) ); } diff --git a/src/plugins/discover/public/application/components/context_app/constants.ts b/src/plugins/discover/public/application/components/context_app/constants.ts deleted file mode 100644 index a22aa69477ee9..0000000000000 --- a/src/plugins/discover/public/application/components/context_app/constants.ts +++ /dev/null @@ -1,19 +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 - * 2.0 and the Server Side Public License, v 1; you may not use this file except - * in compliance with, at your election, the Elastic License 2.0 or the Server - * Side Public License, v 1. - */ - -export const FAILURE_REASONS = { - UNKNOWN: 'unknown', - INVALID_TIEBREAKER: 'invalid_tiebreaker', -}; - -export const LOADING_STATUS = { - FAILED: 'failed', - LOADED: 'loaded', - LOADING: 'loading', - UNINITIALIZED: 'uninitialized', -}; diff --git a/src/plugins/discover/public/application/components/context_app/context_app_legacy.tsx b/src/plugins/discover/public/application/components/context_app/context_app_legacy.tsx index 5031f78c49fcc..55c2208105f13 100644 --- a/src/plugins/discover/public/application/components/context_app/context_app_legacy.tsx +++ b/src/plugins/discover/public/application/components/context_app/context_app_legacy.tsx @@ -15,7 +15,7 @@ import { DocTableLegacyProps, } from '../../angular/doc_table/create_doc_table_react'; import { IIndexPattern, IndexPatternField } from '../../../../../data/common/index_patterns'; -import { LOADING_STATUS } from './constants'; +import { LoadingStatus } from '../../angular/context_app_state'; import { ActionBar, ActionBarProps } from '../../angular/context/components/action_bar/action_bar'; import { TopNavMenuProps } from '../../../../../navigation/public'; @@ -45,13 +45,13 @@ const PREDECESSOR_TYPE = 'predecessors'; const SUCCESSOR_TYPE = 'successors'; function isLoading(status: string) { - return status !== LOADING_STATUS.LOADED && status !== LOADING_STATUS.FAILED; + return status !== LoadingStatus.LOADED && status !== LoadingStatus.FAILED; } export function ContextAppLegacy(renderProps: ContextAppProps) { const status = renderProps.status; - const isLoaded = status === LOADING_STATUS.LOADED; - const isFailed = status === LOADING_STATUS.FAILED; + const isLoaded = status === LoadingStatus.LOADED; + const isFailed = status === LoadingStatus.FAILED; const actionBarProps = (type: string) => { const { @@ -114,7 +114,7 @@ export function ContextAppLegacy(renderProps: ContextAppProps) { }; const loadingFeedback = () => { - if (status === LOADING_STATUS.UNINITIALIZED || status === LOADING_STATUS.LOADING) { + if (status === LoadingStatus.UNINITIALIZED || status === LoadingStatus.LOADING) { return ( diff --git a/src/plugins/discover/public/application/components/context_error_message/context_error_message.test.tsx b/src/plugins/discover/public/application/components/context_error_message/context_error_message.test.tsx index 3edd8f2514e63..e4b5e3b95c3f3 100644 --- a/src/plugins/discover/public/application/components/context_error_message/context_error_message.test.tsx +++ b/src/plugins/discover/public/application/components/context_error_message/context_error_message.test.tsx @@ -10,32 +10,31 @@ import React from 'react'; import { mountWithIntl } from '@kbn/test/jest'; import { ReactWrapper } from 'enzyme'; import { ContextErrorMessage } from './context_error_message'; -// @ts-ignore -import { FAILURE_REASONS, LOADING_STATUS } from '../../angular/context/query'; +import { FailureReason, LoadingStatus } from '../../angular/context_app_state'; import { findTestSubject } from '@elastic/eui/lib/test'; describe('loading spinner', function () { let component: ReactWrapper; it('ContextErrorMessage does not render on loading', () => { - component = mountWithIntl(); + component = mountWithIntl(); expect(findTestSubject(component, 'contextErrorMessageTitle').length).toBe(0); }); it('ContextErrorMessage does not render on success loading', () => { - component = mountWithIntl(); + component = mountWithIntl(); expect(findTestSubject(component, 'contextErrorMessageTitle').length).toBe(0); }); it('ContextErrorMessage renders just the title if the reason is not specifically handled', () => { - component = mountWithIntl(); + component = mountWithIntl(); expect(findTestSubject(component, 'contextErrorMessageTitle').length).toBe(1); expect(findTestSubject(component, 'contextErrorMessageBody').text()).toBe(''); }); it('ContextErrorMessage renders the reason for unknown errors', () => { component = mountWithIntl( - + ); expect(findTestSubject(component, 'contextErrorMessageTitle').length).toBe(1); expect(findTestSubject(component, 'contextErrorMessageBody').length).toBe(1); diff --git a/src/plugins/discover/public/application/components/context_error_message/context_error_message.tsx b/src/plugins/discover/public/application/components/context_error_message/context_error_message.tsx index 83cb6981d761e..85dc67e7029ee 100644 --- a/src/plugins/discover/public/application/components/context_error_message/context_error_message.tsx +++ b/src/plugins/discover/public/application/components/context_error_message/context_error_message.tsx @@ -9,8 +9,7 @@ import React from 'react'; import { EuiCallOut, EuiText } from '@elastic/eui'; import { FormattedMessage, I18nProvider } from '@kbn/i18n/react'; -// @ts-ignore -import { FAILURE_REASONS, LOADING_STATUS } from '../../angular/context/query'; +import { FailureReason, LoadingStatus } from '../../angular/context_app_state'; export interface ContextErrorMessageProps { /** @@ -24,7 +23,7 @@ export interface ContextErrorMessageProps { } export function ContextErrorMessage({ status, reason }: ContextErrorMessageProps) { - if (status !== LOADING_STATUS.FAILED) { + if (status !== LoadingStatus.FAILED) { return null; } return ( @@ -41,7 +40,7 @@ export function ContextErrorMessage({ status, reason }: ContextErrorMessageProps data-test-subj="contextErrorMessageTitle" > - {reason === FAILURE_REASONS.UNKNOWN && ( + {reason === FailureReason.UNKNOWN && ( Date: Tue, 4 May 2021 16:01:43 +0300 Subject: [PATCH 4/5] [Discover] use unknown instead of any, correct types --- .../angular/context/query_parameters/actions.ts | 16 +++++++++++----- 1 file changed, 11 insertions(+), 5 deletions(-) diff --git a/src/plugins/discover/public/application/angular/context/query_parameters/actions.ts b/src/plugins/discover/public/application/angular/context/query_parameters/actions.ts index 03372c4fa2ccf..02eab422b68a4 100644 --- a/src/plugins/discover/public/application/angular/context/query_parameters/actions.ts +++ b/src/plugins/discover/public/application/angular/context/query_parameters/actions.ts @@ -8,8 +8,13 @@ import { pick } from 'lodash'; -import { esFilters, Filter, IFieldType } from '../../../../../../data/public'; -import { FilterManager, IndexPatternsContract } from '../../../../../../data/public'; +import { + IndexPatternsContract, + FilterManager, + esFilters, + Filter, + IndexPatternField, +} from '../../../../../../data/public'; import { popularizeField } from '../../../helpers/popularize_field'; import { ContextAppState, QueryParameters } from '../../context_app_state'; import { MAX_CONTEXT_SIZE, MIN_CONTEXT_SIZE, QUERY_PARAMETER_KEYS } from './constants'; @@ -43,8 +48,8 @@ export function getQueryParameterActions( }; const addFilter = (state: ContextAppState) => async ( - field: string | IFieldType, - values: any, + field: IndexPatternField | string, + values: unknown, operation: string ) => { const indexPatternId = state.queryParameters.indexPatternId; @@ -58,7 +63,8 @@ export function getQueryParameterActions( filterManager.addFilters(newFilters); if (indexPatterns) { const indexPattern = await indexPatterns.get(indexPatternId); - await popularizeField(indexPattern, (field as IFieldType).name, indexPatterns); + const fieldName = typeof field === 'string' ? field : field.name; + await popularizeField(indexPattern, fieldName, indexPatterns); } }; From 902473aaa38c13ef08d99b0676d089e41f76931a Mon Sep 17 00:00:00 2001 From: Dzmitry Tamashevich Date: Wed, 5 May 2021 17:26:44 +0300 Subject: [PATCH 5/5] [Discover] skip any type for tests --- .../public/application/angular/context/api/_stubs.ts | 1 + .../application/angular/context/api/anchor.test.ts | 1 + .../angular/context/api/context.predecessors.test.ts | 10 ++++++---- .../angular/context/api/context.successors.test.ts | 10 ++++++---- 4 files changed, 14 insertions(+), 8 deletions(-) diff --git a/src/plugins/discover/public/application/angular/context/api/_stubs.ts b/src/plugins/discover/public/application/angular/context/api/_stubs.ts index 3736065492e06..241d0a621f245 100644 --- a/src/plugins/discover/public/application/angular/context/api/_stubs.ts +++ b/src/plugins/discover/public/application/angular/context/api/_stubs.ts @@ -34,6 +34,7 @@ export function createIndexPatternsStub() { * A stubbed search source with a `fetch` method that returns all of `_stubHits`. */ export function createSearchSourceStub(hits: EsHitRecordList, timeField?: string) { + // eslint-disable-next-line @typescript-eslint/no-explicit-any const searchSourceStub: any = { _stubHits: hits, _stubTimeField: timeField, diff --git a/src/plugins/discover/public/application/angular/context/api/anchor.test.ts b/src/plugins/discover/public/application/angular/context/api/anchor.test.ts index 5f63ac1592a7c..62c9a2a5e3b90 100644 --- a/src/plugins/discover/public/application/angular/context/api/anchor.test.ts +++ b/src/plugins/discover/public/application/angular/context/api/anchor.test.ts @@ -16,6 +16,7 @@ describe('context app', function () { anchorId: string, sort: EsQuerySortValue[] ) => Promise; + // eslint-disable-next-line @typescript-eslint/no-explicit-any let searchSourceStub: any; describe('function fetchAnchor', function () { diff --git a/src/plugins/discover/public/application/angular/context/api/context.predecessors.test.ts b/src/plugins/discover/public/application/angular/context/api/context.predecessors.test.ts index ec567b40f8eb0..dc097bc110e20 100644 --- a/src/plugins/discover/public/application/angular/context/api/context.predecessors.test.ts +++ b/src/plugins/discover/public/application/angular/context/api/context.predecessors.test.ts @@ -13,6 +13,7 @@ import { EsHitRecordList, fetchContextProvider } from './context'; import { setServices, SortDirection } from '../../../../kibana_services'; import { AnchorHitRecord } from './anchor'; import { Query } from '../../../../../../data/public'; +import { DiscoverServices } from '../../../../build_services'; const MS_PER_DAY = 24 * 60 * 60 * 1000; const ANCHOR_TIMESTAMP = new Date(MS_PER_DAY).toJSON(); @@ -37,13 +38,14 @@ describe('context app', function () { tieBreakerValue: number, size: number ) => Promise; + // eslint-disable-next-line @typescript-eslint/no-explicit-any let mockSearchSource: any; describe('function fetchPredecessors', function () { beforeEach(() => { mockSearchSource = createContextSearchSourceStub('@timestamp'); - setServices({ + setServices(({ data: { search: { searchSource: { @@ -51,7 +53,7 @@ describe('context app', function () { }, }, }, - }); + } as unknown) as DiscoverServices); fetchPredecessors = ( indexPatternId, @@ -235,7 +237,7 @@ describe('context app', function () { beforeEach(() => { mockSearchSource = createContextSearchSourceStub('@timestamp'); - setServices({ + setServices(({ data: { search: { searchSource: { @@ -243,7 +245,7 @@ describe('context app', function () { }, }, }, - }); + } as unknown) as DiscoverServices); fetchPredecessors = ( indexPatternId, diff --git a/src/plugins/discover/public/application/angular/context/api/context.successors.test.ts b/src/plugins/discover/public/application/angular/context/api/context.successors.test.ts index 987c56514b77c..f8fc7eb343206 100644 --- a/src/plugins/discover/public/application/angular/context/api/context.successors.test.ts +++ b/src/plugins/discover/public/application/angular/context/api/context.successors.test.ts @@ -14,6 +14,7 @@ import { setServices, SortDirection } from '../../../../kibana_services'; import { Query } from '../../../../../../data/public'; import { EsHitRecordList, fetchContextProvider } from './context'; import { AnchorHitRecord } from './anchor'; +import { DiscoverServices } from '../../../../build_services'; const MS_PER_DAY = 24 * 60 * 60 * 1000; const ANCHOR_TIMESTAMP = new Date(MS_PER_DAY).toJSON(); @@ -37,13 +38,14 @@ describe('context app', function () { tieBreakerValue: number, size: number ) => Promise; + // eslint-disable-next-line @typescript-eslint/no-explicit-any let mockSearchSource: any; describe('function fetchSuccessors', function () { beforeEach(() => { mockSearchSource = createContextSearchSourceStub('@timestamp'); - setServices({ + setServices(({ data: { search: { searchSource: { @@ -51,7 +53,7 @@ describe('context app', function () { }, }, }, - }); + } as unknown) as DiscoverServices); fetchSuccessors = ( indexPatternId, @@ -238,7 +240,7 @@ describe('context app', function () { beforeEach(() => { mockSearchSource = createContextSearchSourceStub('@timestamp'); - setServices({ + setServices(({ data: { search: { searchSource: { @@ -246,7 +248,7 @@ describe('context app', function () { }, }, }, - }); + } as unknown) as DiscoverServices); fetchSuccessors = ( indexPatternId,