Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Show event context #9198

Merged
merged 118 commits into from
Feb 22, 2017
Merged
Show file tree
Hide file tree
Changes from 110 commits
Commits
Show all changes
118 commits
Select commit Hold shift + click to select a range
f80a169
Add app to display discover entry context
weltenwort Nov 18, 2016
05aedd8
Extract multi-transclude workaround into directive
weltenwort Nov 21, 2016
2e422f7
Add a generic local-navigation directive
weltenwort Nov 21, 2016
89b31e9
Use `<local-navigation>` for context size controls
weltenwort Nov 21, 2016
3227016
Wire up the context app state to the url
weltenwort Nov 21, 2016
ef78a3c
Add basic link from discover to the context app
weltenwort Nov 21, 2016
be355d8
Change context view breadcrumbs
weltenwort Nov 22, 2016
b2db703
Return promises from the reload and init actions
weltenwort Nov 22, 2016
c3d7459
Make the context size display editable
weltenwort Nov 22, 2016
9e5986c
Always sort on the index-pattern's time field
weltenwort Nov 23, 2016
d64f938
Improve doc and context link styling in docTable
weltenwort Nov 23, 2016
2acc123
Fix font-awesome class name typo
weltenwort Nov 23, 2016
4991bd8
Hide context link for non-time-based index patterns
weltenwort Nov 23, 2016
1bb189d
Merge branch 'master' into discover-context-app
weltenwort Dec 5, 2016
e10f458
Add setting to configure the default context size
weltenwort Dec 5, 2016
123d6b0
Add setting to configure the context size step
weltenwort Dec 5, 2016
dbaed4f
Enforce a minimal context size of 0
weltenwort Dec 5, 2016
840278f
Reimplement the local nav without multi-transclusion
weltenwort Dec 6, 2016
4730c77
Add visual indicator for first initialization
weltenwort Dec 6, 2016
2054287
Merge commit '10f7880' into discover-context-app
weltenwort Dec 13, 2016
a9c96aa
Improve loading ui and behaviour, refactor state
weltenwort Dec 13, 2016
a2b1456
Merge branch 'master' into discover-context-app
weltenwort Dec 13, 2016
bb1b056
Remove plus icons from "load more" buttons
weltenwort Dec 13, 2016
36c9b17
Merge branch 'master' into discover-context-app
weltenwort Dec 13, 2016
72e7faf
Merge branch 'master' into discover-context-app
weltenwort Dec 14, 2016
366962a
Fix AppState synchronization
weltenwort Dec 15, 2016
a98d967
Merge branch 'master' into discover-context-app
weltenwort Dec 15, 2016
778d3ce
Fix linting errors
weltenwort Dec 15, 2016
46d8472
Fix more linting errors
weltenwort Dec 15, 2016
3ae8a5d
Make anchor highlighting more prominent
weltenwort Dec 16, 2016
ddda2ff
Remove obsolete isInitialized from state
weltenwort Dec 16, 2016
84d0a3d
Replace history entry when persisting state
weltenwort Dec 16, 2016
491e212
Merge branch 'master' into discover-context-app
weltenwort Dec 16, 2016
89f3af2
Update to new kui css classes
weltenwort Dec 16, 2016
170b6e6
Merge branch 'master' into discover-context-app
weltenwort Dec 19, 2016
cfd0573
Change the design of the "load more" buttons
weltenwort Dec 19, 2016
3ed6941
Stop abusing the kuiLocalMenu styles for sizePicker
weltenwort Dec 19, 2016
d15b35c
Fix imports of removed style file
weltenwort Dec 19, 2016
c2d2240
Use more popular redux terminology
weltenwort Dec 20, 2016
ed354e0
Split state management into separate files
weltenwort Dec 20, 2016
62f7342
Add helpers to bind selectors to state
weltenwort Dec 20, 2016
5472730
Factor out the size picker into a separate directive
weltenwort Dec 20, 2016
b7a789d
Make "load more" buttons full-width
weltenwort Dec 20, 2016
34abd9c
Merge branch 'master' into discover-context-app
weltenwort Jan 2, 2017
e9cdb7a
Use tabs in the LocalNav
weltenwort Jan 2, 2017
00d1bbe
Use yellow outline for anchor highlighting
weltenwort Jan 2, 2017
d3a37cb
Use kui classes for links in discover detail links
weltenwort Jan 2, 2017
d4daca4
Merge branch 'master' into discover-context-app
weltenwort Jan 5, 2017
6ac96e2
Merge branch 'master' into discover-context-app
weltenwort Jan 6, 2017
2cbbf62
Add implementation notes
weltenwort Jan 6, 2017
ce2d015
Remove superfluous directives
weltenwort Jan 6, 2017
a7d32ac
Factor out loading button component
weltenwort Jan 6, 2017
fbb61c1
Add directory structure to notes
weltenwort Jan 6, 2017
82f88c4
Add unit tests
weltenwort Jan 10, 2017
2f31979
Use chai instead of expect.js in tests
weltenwort Jan 11, 2017
5cc91aa
Add more unit tests
weltenwort Jan 11, 2017
b80be8b
Merge branch 'master' into discover-context-app
weltenwort Jan 11, 2017
02440e8
Remove redux concepts in response to code review
weltenwort Jan 12, 2017
cb4d95f
Improve Context Log highlighting and UI. (#1)
cjcenizal Jan 12, 2017
2e5b192
Add missing successor loading status
weltenwort Jan 12, 2017
5c50c1d
Factor out initial queryParameters state creation
weltenwort Jan 16, 2017
43472f5
Add basic acceptance tests (WIP)
weltenwort Jan 17, 2017
5272a76
Add some context size tests
weltenwort Jan 18, 2017
2756db1
Remove selectors in reaction to review feedback
weltenwort Jan 18, 2017
de6983f
Bind SizePickerController to a proper name
weltenwort Jan 19, 2017
cbe3602
Merge branch 'master' into discover-context-app
weltenwort Jan 20, 2017
29e1fa8
Replace object spread syntax with Object.assign
weltenwort Jan 20, 2017
19df62f
Rename setCount to onChangeCount
weltenwort Jan 20, 2017
9e4dcea
Use Promise.try to catch more errors
weltenwort Jan 20, 2017
e0046e2
Track loading errors and notify the user about them
weltenwort Jan 20, 2017
1b7fb54
Disable size picker inputs until anchor is loaded
weltenwort Jan 20, 2017
cdae37c
Fix ContextPage class name
weltenwort Jan 23, 2017
9d411ea
Explicitly reset the view value in debounced input
weltenwort Jan 24, 2017
826910e
Replace uses of chai with expect.js
weltenwort Jan 25, 2017
0566459
Add docstrings and annotations
weltenwort Jan 25, 2017
4b275b9
Remove the need to separately pass the anchor uid
weltenwort Jan 25, 2017
61d7683
Re-use `KbnUrl` to encode the url components
weltenwort Jan 26, 2017
c0021f2
Preserve previous discover state in breadcrumb url
weltenwort Jan 26, 2017
a916f49
Start to remove duplication from PageObjects
weltenwort Jan 26, 2017
f07926b
Merge branch 'master' into discover-context-app
weltenwort Jan 27, 2017
05cc080
Merge branch 'master' into discover-context-app
weltenwort Jan 31, 2017
ef3573e
Update implementation notes
weltenwort Jan 31, 2017
183991e
Introduce loading status constants, separate state
weltenwort Feb 1, 2017
8997be7
Remove obsolete border work-around
weltenwort Feb 1, 2017
61f0423
Split query and query_parameters files
weltenwort Feb 1, 2017
6af8043
Extract route template into separate html file
weltenwort Feb 1, 2017
d8a7a4f
Replace _.spread with argument spread syntax
weltenwort Feb 2, 2017
a0040cc
Use new kui error info panel
weltenwort Feb 2, 2017
d23f2e4
Inline reversing the results
weltenwort Feb 2, 2017
e8862c7
Add a link to Discover to the error message
weltenwort Feb 2, 2017
0102c18
Rename disabled property of the loading button
weltenwort Feb 2, 2017
9ef0b69
Merge branch 'master' into discover-context-app
weltenwort Feb 3, 2017
9af7cb6
Show end-of-data warnings to the user
weltenwort Feb 3, 2017
f8dcd35
Rename query to queryBody where appropriate
weltenwort Feb 6, 2017
41ea1bf
Remove Discover breadcrumb
weltenwort Feb 6, 2017
0a4fa98
Merge branch 'master' into discover-context-app
weltenwort Feb 8, 2017
a3572d0
Get the config options lazily
weltenwort Feb 8, 2017
ce4c07f
Limit the context size to Elasticsearch's limit
weltenwort Feb 9, 2017
938e841
Increase the size picker width
weltenwort Feb 9, 2017
06245b9
Move the size picker to the left
weltenwort Feb 9, 2017
44c1e8c
Add comment about ngModel getter/setter
weltenwort Feb 10, 2017
519a3a4
Don't disable load-more buttons on failure
weltenwort Feb 10, 2017
3856851
Revert button styling of links in discover
weltenwort Feb 10, 2017
61be5c1
Adjust header content
weltenwort Feb 10, 2017
912df52
Merge branch 'master' into discover-context-app
weltenwort Feb 10, 2017
21c9098
Fix function names in unit tests
weltenwort Feb 10, 2017
c6ac75b
Add test for navigation from Discover to Context View
weltenwort Feb 14, 2017
9706068
Move updating the configuration into before block
weltenwort Feb 14, 2017
9b8bf37
Add query_parameter action tests
weltenwort Feb 14, 2017
8998279
Merge branch 'master' into discover-context-app
weltenwort Feb 15, 2017
ad1c856
Switch loading button and size picker locations
weltenwort Feb 16, 2017
d8f99e7
Rename entries to documents
weltenwort Feb 16, 2017
8a5b60e
Display loading step size on button
weltenwort Feb 16, 2017
50205b8
Merge branch 'master' into discover-context-app
weltenwort Feb 16, 2017
cb2bae6
Fix anchor highlight render bug in chrome
weltenwort Feb 16, 2017
12ce61b
Improve test case wording
weltenwort Feb 21, 2017
2784b0c
Merge branch 'master' into discover-context-app
weltenwort Feb 22, 2017
62bf542
Update notes to match the dir structure
weltenwort Feb 22, 2017
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion docs/management/advanced-options.asciidoc
Original file line number Diff line number Diff line change
Expand Up @@ -78,7 +78,6 @@ Markdown.
`notifications:lifetime:error`:: Specifies the duration in milliseconds for error notification displays. The default value is 300000. Set this field to `Infinity` to disable error notifications.
`notifications:lifetime:warning`:: Specifies the duration in milliseconds for warning notification displays. The default value is 10000. Set this field to `Infinity` to disable warning notifications.
`notifications:lifetime:info`:: Specifies the duration in milliseconds for information notification displays. The default value is 5000. Set this field to `Infinity` to disable information notifications.

`timelion:showTutorial`:: Set this property to `true` to show the Timelion tutorial to users when they first open Timelion.
`timelion:es.timefield`:: Default field containing a timestamp when using the `.es()` query.
`timelion:es.default_index`:: Default index when using the `.es()` query.
Expand All @@ -89,3 +88,5 @@ Markdown.
`timelion:graphite.url`:: [experimental] Used with graphite queries, this it the URL of your host
`timelion:quandl.key`:: [experimental] Used with quandl queries, this is your API key from www.quandl.com
`state:storeInSessionStorage`:: [experimental] Kibana tracks UI state in the URL, which can lead to problems when there is a lot of information there and the URL gets very long. Enabling this will store parts of the state in your browser session instead, to keep the URL shorter.
`context:defaultSize`:: Specifies the initial number of surrounding entries to display in the context view. The default value is 5.
`context:step`:: Specifies the number to increment or decrement the context size by when using the buttons in the context view. The default value is 5.
97 changes: 97 additions & 0 deletions src/core_plugins/kibana/public/context/NOTES.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,97 @@
# Discover Context App Implementation Notes
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This file needs to be updated to reflect the current principles and directory structure.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes I will remove it, since most of what it describes is no longer true.


The implementation of this app is intended to exhibit certain desirable
properties by adhering to a set of *principles*. This document aims to explain
those and the *concepts* employed to achieve that.


## Principles

**Single Source of Truth**: A good user experience depends on the UI displaying
consistent information across the whole page. To achieve this, there should
always be a single source of truth for the application's state. In this
application this is the `ContextAppController::state` object.

**Unidirectional Data Flow**: While a single state promotes rendering
consistency, it does little to make the state changes easier to reason about.
To avoid having state mutations scattered all over the code, this app
implements a unidirectional data flow architecture. That means that the state
is treated as immutable throughout the application except for actions, which
may modify it to cause angular to re-render and watches to trigger.

**Unit-Testability**: Creating unit tests for large parts of the UI code is
made easy by expressing the as much of the logic as possible as
side-effect-free functions. The only place where side-effects are allowed are
actions. Due to the nature of AngularJS a certain amount of impure code must be
employed in some cases, e.g. when dealing with the isolate scope bindings in
`ContextAppController`.

**Loose Coupling**: An attempt was made to couple the parts that make up this
app as loosely as possible. This means using pure functions whenever possible
and isolating the angular directives diligently. To that end, the app has been
implemented as the independent `ContextApp` directive in [app.js](./app.js). It
does not access the Kibana `AppState` directly but communicates only via its
directive properties. The binding of these attributes to the state and thereby
to the route is performed by the `CreateAppRouteController`in
[index.js](./index.js). Similarly, the `SizePicker` directive only communicates
with its parent via the passed properties.


## Concepts

To adhere to the principles mentioned above, this app borrows some concepts
from the redux architecture that forms a ciruclar unidirectional data flow:

```

|* create initial state
v
+->+
| v
| |* state
| v
| |* angular templates render state
| v
| |* angular calls actions in response to user action/system events
| v
| |* actions modify state
| v
+--+

```

**State**: The state is the single source of truth at
`ContextAppController::state` and may only be modified by actions.

**Action**: Actions are functions that are called inreponse user or system
actions and may modified the state the are bound to via their closure.


## Directory Structure

**index.js**: Defines the route and renders the `<context-app>` directive,
binding it to the `AppState`.

**app.js**: Defines the `<context-app>` directive, that is at the root of the
application. Creates the store, reducer and bound actions/selectors.

**query.js**: Exports the actions, reducers and selectors related to the
query status and results.

**query_parameters.js**: Exports the actions, reducers and selectors related to
the parameters used to construct the query.

**components/loading_button**: Defines the `<context-loading-button>`
directive including its respective styles.

**components/size_picker**: Defines the `<context-size-picker>`
directive including its respective styles.

**api/anchor.js**: Exports `fetchAnchor()` that creates and executes the
query for the anchor document.

**api/context.js**: Exports `fetchPredecessors()` and `fetchSuccessors()` that
create and execute the queries for the preceeding and succeeding documents.

**api/utils**: Exports various functions used to create and transform
queries.
80 changes: 80 additions & 0 deletions src/core_plugins/kibana/public/context/api/__tests__/anchor.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
import expect from 'expect.js';
import sinon from 'sinon';

import { fetchAnchor } from 'plugins/kibana/context/api/anchor';


Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

FYI the AirBnB style guide advises against multiple consecutive blank lines (I can't find the rule but they note it in an issue comment: airbnb/javascript#1048 (comment)). I don't mind if you leave the whitespace the way it is now because the linter can fix it on it's own, but I just wanted to let you know. :)

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I've been using the following rules:

  • Group third-party and local imports using single blank lines, sort alphabetically by filename within the groups.
  • Separate the import section from other code by two blank lines.
  • Separate the export section from other code by two blank lines.

This results in:

// third-party/global imports
// ...

// local imports
// ...


// code
// ...
// ...


// exports
// ...

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for the explanation.

describe('context app', function () {
describe('function fetchAnchor', function () {
it('should use the `search` api to query the given index', function () {
const indexPatternStub = createIndexPatternStub('index1');
const esStub = createEsStub(['hit1']);

return fetchAnchor(esStub, indexPatternStub, 'UID', { '@timestamp': 'desc' })
.then(() => {
expect(esStub.search.calledOnce).to.be(true);
expect(esStub.search.firstCall.args[0]).to.have.property('index', 'index1');
});
});

it('should include computed fields in the query', function () {
const indexPatternStub = createIndexPatternStub('index1');
const esStub = createEsStub(['hit1']);

return fetchAnchor(esStub, indexPatternStub, 'UID', { '@timestamp': 'desc' })
.then(() => {
expect(esStub.search.calledOnce).to.be(true);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't think we should assert this here, since the previous test is covering it, and it doesn't reflect this test's description.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I added this to avoid a hard-to-understand error for the following expectation if the spy was not called at all. I'll remove it.

expect(esStub.search.firstCall.args[0].body).to.have.keys([
'script_fields', 'docvalue_fields', 'stored_fields']);
});
});

it('should reject with an error when no hits were found', function () {
const indexPatternStub = createIndexPatternStub('index1');
const esStub = createEsStub([]);

return fetchAnchor(esStub, indexPatternStub, 'UID', { '@timestamp': 'desc' })
.then(
() => {
expect().fail('expected the promise to be rejected');
},
(error) => {
expect(error).to.be.an(Error);
}
);
});

it('should return the first hit after adding an anchor marker', function () {
const indexPatternStub = createIndexPatternStub('index1');
const esStub = createEsStub([{ property1: 'value1' }, {}]);

return fetchAnchor(esStub, indexPatternStub, 'UID', { '@timestamp': 'desc' })
.then((anchorDocument) => {
expect(anchorDocument).to.have.property('property1', 'value1');
expect(anchorDocument).to.have.property('$$_isAnchor', true);
});
});
});
});


function createIndexPatternStub(indices) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can we move these helpers to the top of the file? I find that structure to be more readable, and AirBnB's rules will cause the linter to complain because of no-use-before-define.

Copy link
Member Author

@weltenwort weltenwort Jan 31, 2017

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm used to applying the pattern suggested in Robert C. Martin's "Clean Code" in regards to ordering: It should be easy to read by starting with the highlights and getting more detailed the further the reader progresses through the file.

return {
getComputedFields: sinon.stub()
.returns({}),
toIndexList: sinon.stub()
.returns(indices),
};
}

function createEsStub(hits) {
return {
search: sinon.stub()
.returns({
hits: {
hits,
total: hits.length,
},
}),
};
}
31 changes: 31 additions & 0 deletions src/core_plugins/kibana/public/context/api/anchor.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
import _ from 'lodash';

import { addComputedFields } from './utils/fields';
import { createAnchorQueryBody } from './utils/queries';


async function fetchAnchor(es, indexPattern, uid, sort) {
const indices = await indexPattern.toIndexList();
const queryBody = addComputedFields(indexPattern, createAnchorQueryBody(uid, sort));
const response = await es.search({
index: indices,
body: queryBody,
});

if (_.get(response, ['hits', 'total'], 0) < 1) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What if the ES client returns an error? We should handle that scenario gracefully. I usually display a user friendly error message with the notifier and log the actual error.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Right, but I will handle that on a level closer to the UI. This function is not concerned with user interaction.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I would at least generally translate the ES errors into something more generic at the service level, so that clients of the service don't end up coupling themselves to the ES client.

throw new Error('Failed to load anchor document.');
}

return Object.assign(
{},
response.hits.hits[0],
{
$$_isAnchor: true,
},
);
}


export {
fetchAnchor,
};
47 changes: 47 additions & 0 deletions src/core_plugins/kibana/public/context/api/context.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
import _ from 'lodash';

import { addComputedFields } from './utils/fields';
import { getDocumentUid } from './utils/ids';
import { createSuccessorsQueryBody } from './utils/queries.js';
import { reverseQuerySort } from './utils/sorting';


async function fetchSuccessors(es, indexPattern, anchorDocument, sort, size) {
const successorsQueryBody = prepareQueryBody(indexPattern, anchorDocument, sort, size);
const results = await performQuery(es, indexPattern, successorsQueryBody);
return results;
}

async function fetchPredecessors(es, indexPattern, anchorDocument, sort, size) {
const successorsQueryBody = prepareQueryBody(indexPattern, anchorDocument, sort, size);
const predecessorsQueryBody = reverseQuerySort(successorsQueryBody);
const reversedResults = await performQuery(es, indexPattern, predecessorsQueryBody);
const results = reversedResults.slice().reverse();
return results;
}


function prepareQueryBody(indexPattern, anchorDocument, sort, size) {
const successorsQueryBody = addComputedFields(
indexPattern,
createSuccessorsQueryBody(anchorDocument.sort, sort, size)
);
return successorsQueryBody;
}

async function performQuery(es, indexPattern, queryBody) {
const indices = await indexPattern.toIndexList();

const response = await es.search({
index: indices,
body: queryBody,
});
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same comment as in anchor.js: we should catch and handle errors from the ES client here.


return _.get(response, ['hits', 'hits'], []);
}


export {
fetchPredecessors,
fetchSuccessors,
};
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
import expect from 'expect.js';

import { addComputedFields } from 'plugins/kibana/context/api/utils/fields';


describe('context app', function () {
describe('function addComputedFields', function () {
it('should add the `script_fields` property defined in the given index pattern', function () {
const getComputedFields = () => ({
scriptFields: {
sourcefield1: {
script: '_source.field1',
},
}
});

const query = addComputedFields({ getComputedFields }, {});
expect(query).to.have.property('script_fields');
expect(query.script_fields).to.eql(getComputedFields().scriptFields);
});

it('should add the `docvalue_fields` property defined in the given index pattern', function () {
const getComputedFields = () => ({
docvalueFields: ['field1'],
});

const query = addComputedFields({ getComputedFields }, {});
expect(query).to.have.property('docvalue_fields');
expect(query.docvalue_fields).to.eql(getComputedFields().docvalueFields);
});

it('should add the `stored_fields` property defined in the given index pattern', function () {
const getComputedFields = () => ({
storedFields: ['field1'],
});

const query = addComputedFields({ getComputedFields }, {});
expect(query).to.have.property('stored_fields');
expect(query.stored_fields).to.eql(getComputedFields().storedFields);
});

it('should preserve other properties of the query', function () {
const getComputedFields = () => ({});

const query = addComputedFields({ getComputedFields }, { property1: 'value1' });
expect(query).to.have.property('property1', 'value1');
});
});
});
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
import expect from 'expect.js';

import {
createAnchorQueryBody,
createSuccessorsQueryBody,
} from 'plugins/kibana/context/api/utils/queries';


describe('context app', function () {
describe('function createAnchorQueryBody', function () {
it('should return a search definition that searches the given uid', function () {
const query = createAnchorQueryBody('UID', { '@timestamp': 'desc' });
expect(query.query.terms._uid[0]).to.eql('UID');
});

it('should return a search definition that sorts by the given criteria and uid', function () {
const query = createAnchorQueryBody('UID', { '@timestamp': 'desc' });
expect(query.sort).to.eql([
{ '@timestamp': 'desc' },
{ _uid: 'asc' },
]);
});
});

describe('function createSuccessorsQueryBody', function () {
it('should return a search definition that includes the given size', function () {
const query = createSuccessorsQueryBody([0, 'UID'], { '@timestamp' : 'desc' }, 10);
expect(query).to.have.property('size', 10);
});

it('should return a search definition that sorts by the given criteria and uid', function () {
const query = createSuccessorsQueryBody([0, 'UID'], { '@timestamp' : 'desc' }, 10);
expect(query).to.have.property('sort');
expect(query.sort).to.eql([
{ '@timestamp': 'desc' },
{ _uid: 'asc' },
]);
});

it('should return a search definition that searches after the given uid', function () {
const query = createSuccessorsQueryBody([0, 'UID'], { '@timestamp' : 'desc' }, 10);
expect(query).to.have.property('search_after');
expect(query.search_after).to.eql([0, 'UID']);
});
});
});
Loading