Skip to content

Commit

Permalink
[Discover] Migrate remaining context files from js to ts (#99019) (#9…
Browse files Browse the repository at this point in the history
…9457)

* [Discover] migrate remaining context files from js to ts

* [Discover] get rid of any types

* [Discover] replace constants with enums, update imports

* [Discover] use unknown instead of any, correct types

* [Discover] skip any type for tests

Co-authored-by: Kibana Machine <[email protected]>

Co-authored-by: Kibana Machine <[email protected]>
  • Loading branch information
dimaanj and kibanamachine authored May 6, 2021
1 parent a5d698b commit c06ba92
Show file tree
Hide file tree
Showing 20 changed files with 477 additions and 341 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -9,71 +9,77 @@
import sinon from 'sinon';
import moment from 'moment';

import { IndexPatternsContract } from '../../../../../../data/public';
import { EsHitRecordList } from './context';

type SortHit = {
[key in string]: number; // timeField name
} & {
sort: [number, number];
};

export function createIndexPatternsStub() {
return {
return ({
get: sinon.spy((indexPatternId) =>
Promise.resolve({
id: indexPatternId,
isTimeNanosBased: () => false,
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: EsHitRecordList, timeField?: string) {
// eslint-disable-next-line @typescript-eslint/no-explicit-any
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(timeFieldName: string) {
const searchSourceStub = createSearchSourceStub([], timeFieldName);

searchSourceStub.fetch = sinon.spy(() => {
const timeField = 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, second) => first[timeField] - second[timeField]
: (first, second) => 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) =>
(hit: SortHit) =>
moment(hit[timeField]).isSameOrAfter(timeRange.gte) &&
moment(hit[timeField]).isSameOrBefore(timeRange.lte)
)
Expand All @@ -87,5 +93,5 @@ export function createContextSearchSourceStub(hits, timeField = '@timestamp') {
});
});

return searchSourceStub;
return searchSourceStub as sinon.SinonStub;
}
Original file line number Diff line number Diff line change
Expand Up @@ -6,33 +6,40 @@
* Side Public License, v 1.
*/

import { EsQuerySortValue, SortDirection } from '../../../../../../data/public';
import { createIndexPatternsStub, createSearchSourceStub } from './_stubs';

import { fetchAnchorProvider } from './anchor';
import { AnchorHitRecord, fetchAnchorProvider } from './anchor';

describe('context app', function () {
describe('function fetchAnchor', function () {
let fetchAnchor;
let searchSourceStub;
let fetchAnchor: (
indexPatternId: string,
anchorId: string,
sort: EsQuerySortValue[]
) => Promise<AnchorHitRecord>;
// eslint-disable-next-line @typescript-eslint/no-explicit-any
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);
});
});

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);
Expand All @@ -42,8 +49,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');
Expand All @@ -52,8 +59,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);
Expand All @@ -63,8 +70,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);
Expand All @@ -74,8 +81,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);
Expand All @@ -96,24 +103,27 @@ 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 },
]);
});
});

it('should reject with an error when no hits were found', 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);
Expand All @@ -125,8 +135,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);
Expand All @@ -135,20 +145,19 @@ describe('context app', function () {
});

describe('useNewFields API', () => {
let fetchAnchor;
let searchSourceStub;

beforeEach(() => {
searchSourceStub = createSearchSourceStub([{ _id: 'hit1' }]);
searchSourceStub = createSearchSourceStub([
{ _id: 'hit1', fields: [], sort: [], _source: {} },
]);
fetchAnchor = fetchAnchorProvider(createIndexPatternsStub(), searchSourceStub, true);
});

it('should request fields if useNewFieldsApi set', 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');
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,11 +6,31 @@
* Side Public License, v 1.
*/

import _ from 'lodash';
import { get } from 'lodash';
import { i18n } from '@kbn/i18n';

export function fetchAnchorProvider(indexPatterns, searchSource, useNewFieldsApi = false) {
return async function fetchAnchor(indexPatternId, anchorId, sort) {
import {
ISearchSource,
IndexPatternsContract,
EsQuerySortValue,
} 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: EsQuerySortValue[]
): Promise<AnchorHitRecord> {
const indexPattern = await indexPatterns.get(indexPatternId);
searchSource
.setParent(undefined)
Expand All @@ -36,7 +56,7 @@ export function fetchAnchorProvider(indexPatterns, searchSource, useNewFieldsApi
}
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.',
Expand All @@ -45,8 +65,9 @@ export function fetchAnchorProvider(indexPatterns, searchSource, useNewFieldsApi
}

return {
..._.get(response, ['hits', 'hits', 0]),
...get(response, ['hits', 'hits', 0]),
// eslint-disable-next-line @typescript-eslint/naming-convention
$$_isAnchor: true,
};
} as AnchorHitRecord;
};
}
Loading

0 comments on commit c06ba92

Please sign in to comment.