mirror of
https://github.com/elastic/kibana.git
synced 2025-04-24 17:59:23 -04:00
Format epoch date strings to ensure consistent date values (#31865)
This PR fixes multiple issues around timestamp fields sometimes being returned as a number (`1551211325000`) instead of a UTC ISO8601 string (`2019-02-26T20:02:05.000Z`). The fix was introduced at the GraphQL layer so that any GraphQL datetime field could take advantage of the fix by simply using the new `Date` type. - Added a new [GraphQLScalarType](https://www.apollographql.com/docs/graphql-tools/scalars.html#Date-as-a-scalar) for converting epoch date values to UTC ISO8601 before sending to the client. - Updated all `timestamp` schema types to use the new `Date` type so that datetime fields will be returned in a consistent type/format. - Created `KibanaConfigContext` React Context for accessing Kibana Advanced Settings from custom components - Created HoC `PreferenceFormattedDate` for formatting dates according to Kibana Advanced Settings - Updated Timeline to use new `PreferenceFormattedDate` - Updated HostsTable's date to be `FormattedRelative` and added tool tip for viewing actual date value - Added generated files to `.gitattributes` so that they're [collapsed by default](https://thoughtbot.com/blog/github-diff-supression) when reviewing in GitHub. Includes fixes for: https://github.com/elastic/ingest-dev/issues/199 https://github.com/elastic/ingest-dev/issues/280 https://github.com/elastic/ingest-dev/issues/282 https://github.com/elastic/ingest-dev/issues/303
This commit is contained in:
parent
f63a8ca3df
commit
e9c1360dab
33 changed files with 543 additions and 197 deletions
6
x-pack/plugins/secops/.gitattributes
vendored
Normal file
6
x-pack/plugins/secops/.gitattributes
vendored
Normal file
|
@ -0,0 +1,6 @@
|
|||
# Auto-collapse generated files in GitHub
|
||||
# https://help.github.com/en/articles/customizing-how-changed-files-appear-on-github
|
||||
x-pack/plugins/secops/public/graphql/types.ts linguist-generated=true
|
||||
x-pack/plugins/secops/server/graphql/types.ts linguist-generated=true
|
||||
x-pack/plugins/secops/public/graphql/introspection.json linguist-generated=true
|
||||
*.test.tsx.snap linguist-generated=true
|
|
@ -16,6 +16,7 @@ import euiLightVars from '@elastic/eui/dist/eui_theme_light.json';
|
|||
import { I18nContext } from 'ui/i18n';
|
||||
|
||||
import { ErrorToast } from '../components/error_toast';
|
||||
import { KibanaConfigContext } from '../components/formatted_date';
|
||||
import { AppFrontendLibs } from '../lib/lib';
|
||||
import { PageRouter } from '../routes';
|
||||
import { store } from '../store';
|
||||
|
@ -34,7 +35,9 @@ export const startApp = async (libs: AppFrontendLibs) => {
|
|||
darkMode: libs.framework.darkMode,
|
||||
})}
|
||||
>
|
||||
<PageRouter history={history} />
|
||||
<KibanaConfigContext.Provider value={libs.framework}>
|
||||
<PageRouter history={history} />
|
||||
</KibanaConfigContext.Provider>
|
||||
</ThemeProvider>
|
||||
<ErrorToast />
|
||||
</ApolloProvider>
|
||||
|
|
|
@ -0,0 +1,71 @@
|
|||
/*
|
||||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||
* or more contributor license agreements. Licensed under the Elastic License;
|
||||
* you may not use this file except in compliance with the Elastic License.
|
||||
*/
|
||||
|
||||
import { mount } from 'enzyme';
|
||||
import moment from 'moment-timezone';
|
||||
import * as React from 'react';
|
||||
|
||||
import { KibanaConfigContext, PreferenceFormattedDate } from '.';
|
||||
import { AppTestingFrameworkAdapter } from '../../lib/adapters/framework/testing_framework_adapter';
|
||||
import { mockFrameworks } from '../../mock';
|
||||
|
||||
describe('PreferenceFormattedDate', () => {
|
||||
describe('rendering', () => {
|
||||
const isoDateString = '2019-02-25T22:27:05.000Z';
|
||||
const configFormattedDateString = (
|
||||
dateString: string,
|
||||
config: Partial<AppTestingFrameworkAdapter>
|
||||
): string =>
|
||||
moment
|
||||
.tz(
|
||||
dateString,
|
||||
config.dateFormatTz! === 'Browser' ? config.timezone! : config.dateFormatTz!
|
||||
)
|
||||
.format(config.dateFormat);
|
||||
|
||||
test('it renders the UTC ISO8601 date string supplied when no configuration exists', () => {
|
||||
const wrapper = mount(
|
||||
<KibanaConfigContext.Provider value={{}}>
|
||||
<PreferenceFormattedDate value={isoDateString} />
|
||||
</KibanaConfigContext.Provider>
|
||||
);
|
||||
expect(wrapper.text()).toEqual(isoDateString);
|
||||
});
|
||||
|
||||
test('it renders the UTC ISO8601 date supplied when the default configuration exists', () => {
|
||||
const wrapper = mount(
|
||||
<KibanaConfigContext.Provider value={mockFrameworks.default_UTC}>
|
||||
<PreferenceFormattedDate value={isoDateString} />
|
||||
</KibanaConfigContext.Provider>
|
||||
);
|
||||
expect(wrapper.text()).toEqual(
|
||||
configFormattedDateString(isoDateString, mockFrameworks.default_UTC)
|
||||
);
|
||||
});
|
||||
|
||||
test('it renders the correct tz when the default browser configuration exists', () => {
|
||||
const wrapper = mount(
|
||||
<KibanaConfigContext.Provider value={mockFrameworks.default_browser}>
|
||||
<PreferenceFormattedDate value={isoDateString} />
|
||||
</KibanaConfigContext.Provider>
|
||||
);
|
||||
expect(wrapper.text()).toEqual(
|
||||
configFormattedDateString(isoDateString, mockFrameworks.default_browser)
|
||||
);
|
||||
});
|
||||
|
||||
test('it renders the correct tz when a non-UTC configuration exists', () => {
|
||||
const wrapper = mount(
|
||||
<KibanaConfigContext.Provider value={mockFrameworks.default_MT}>
|
||||
<PreferenceFormattedDate value={isoDateString} />
|
||||
</KibanaConfigContext.Provider>
|
||||
);
|
||||
expect(wrapper.text()).toEqual(
|
||||
configFormattedDateString(isoDateString, mockFrameworks.default_MT)
|
||||
);
|
||||
});
|
||||
});
|
||||
});
|
|
@ -0,0 +1,25 @@
|
|||
/*
|
||||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||
* or more contributor license agreements. Licensed under the Elastic License;
|
||||
* you may not use this file except in compliance with the Elastic License.
|
||||
*/
|
||||
|
||||
import moment from 'moment-timezone';
|
||||
import * as React from 'react';
|
||||
import { pure } from 'recompose';
|
||||
|
||||
import { AppKibanaFrameworkAdapter } from '../../lib/adapters/framework/kibana_framework_adapter';
|
||||
|
||||
export const KibanaConfigContext = React.createContext<Partial<AppKibanaFrameworkAdapter>>({});
|
||||
|
||||
export const PreferenceFormattedDate = pure<{ value: Date | string }>(({ value }) => (
|
||||
<KibanaConfigContext.Consumer>
|
||||
{(config: Partial<AppKibanaFrameworkAdapter>) => {
|
||||
return config && config.dateFormat && config.dateFormatTz && config.timezone
|
||||
? moment
|
||||
.tz(value, config.dateFormatTz === 'Browser' ? config.timezone : config.dateFormatTz)
|
||||
.format(config.dateFormat)
|
||||
: moment.utc(value).toISOString();
|
||||
}}
|
||||
</KibanaConfigContext.Consumer>
|
||||
));
|
|
@ -4,7 +4,7 @@
|
|||
* you may not use this file except in compliance with the Elastic License.
|
||||
*/
|
||||
|
||||
import { EuiBadge, EuiLink } from '@elastic/eui';
|
||||
import { EuiBadge, EuiLink, EuiToolTip } from '@elastic/eui';
|
||||
import { FormattedRelative } from '@kbn/i18n/react';
|
||||
import { get, has, isNil } from 'lodash/fp';
|
||||
import React from 'react';
|
||||
|
@ -165,8 +165,10 @@ const getAuthenticationColumns = (startDate: number): Array<Columns<Authenticati
|
|||
truncateText: false,
|
||||
hideForMobile: false,
|
||||
render: ({ node }) =>
|
||||
has('lastFailure.timestamp', node) ? (
|
||||
<FormattedRelative value={new Date(node.lastFailure!.timestamp!)} />
|
||||
has('lastFailure.timestamp', node) && node.lastFailure!.timestamp != null ? (
|
||||
<EuiToolTip position="bottom" content={node.lastFailure!.timestamp!}>
|
||||
<FormattedRelative value={new Date(node.lastFailure!.timestamp!)} />
|
||||
</EuiToolTip>
|
||||
) : (
|
||||
getEmptyTagValue()
|
||||
),
|
||||
|
@ -273,7 +275,9 @@ const getAuthenticationColumns = (startDate: number): Array<Columns<Authenticati
|
|||
hideForMobile: false,
|
||||
render: ({ node }) =>
|
||||
has('lastSuccess.timestamp', node) ? (
|
||||
<FormattedRelative value={new Date(node.lastSuccess!.timestamp!)} />
|
||||
<EuiToolTip position="bottom" content={node.lastSuccess!.timestamp!}>
|
||||
<FormattedRelative value={new Date(node.lastSuccess!.timestamp!)} />
|
||||
</EuiToolTip>
|
||||
) : (
|
||||
getEmptyTagValue()
|
||||
),
|
||||
|
|
|
@ -1,48 +1,58 @@
|
|||
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
||||
|
||||
exports[`Load More Table Component rendering it renders the default Hosts table 1`] = `
|
||||
<Connect(pure(Component))
|
||||
data={
|
||||
Array [
|
||||
Object {
|
||||
"cursor": Object {
|
||||
"value": "98966fa2013c396155c460d35c0902be",
|
||||
},
|
||||
"node": Object {
|
||||
"_id": "cPsuhGcB0WOhS6qyTKC0",
|
||||
"firstSeen": "2018-12-06T15:40:53.319Z",
|
||||
"host": Object {
|
||||
"name": "elrond.elstc.co",
|
||||
"os": Object {
|
||||
"name": "Ubuntu",
|
||||
"version": "18.04.1 LTS (Bionic Beaver)",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
Object {
|
||||
"cursor": Object {
|
||||
"value": "aa7ca589f1b8220002f2fc61c64cfbf1",
|
||||
},
|
||||
"node": Object {
|
||||
"_id": "KwQDiWcB0WOhS6qyXmrW",
|
||||
"firstSeen": "2018-12-07T14:12:38.560Z",
|
||||
"host": Object {
|
||||
"name": "siem-kibana",
|
||||
"os": Object {
|
||||
"name": "Debian GNU/Linux",
|
||||
"version": "9 (stretch)",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
]
|
||||
<ContextProvider
|
||||
value={
|
||||
Object {
|
||||
"dateFormat": "MMM D, YYYY @ HH:mm:ss.SSS",
|
||||
"dateFormatTz": "UTC",
|
||||
"timezone": "UTC",
|
||||
}
|
||||
}
|
||||
hasNextPage={true}
|
||||
loadMore={[MockFunction]}
|
||||
loading={false}
|
||||
nextCursor="aa7ca589f1b8220002f2fc61c64cfbf1"
|
||||
totalCount={4}
|
||||
type="page"
|
||||
/>
|
||||
>
|
||||
<Connect(pure(Component))
|
||||
data={
|
||||
Array [
|
||||
Object {
|
||||
"cursor": Object {
|
||||
"value": "98966fa2013c396155c460d35c0902be",
|
||||
},
|
||||
"node": Object {
|
||||
"_id": "cPsuhGcB0WOhS6qyTKC0",
|
||||
"firstSeen": "2018-12-06T15:40:53.319Z",
|
||||
"host": Object {
|
||||
"name": "elrond.elstc.co",
|
||||
"os": Object {
|
||||
"name": "Ubuntu",
|
||||
"version": "18.04.1 LTS (Bionic Beaver)",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
Object {
|
||||
"cursor": Object {
|
||||
"value": "aa7ca589f1b8220002f2fc61c64cfbf1",
|
||||
},
|
||||
"node": Object {
|
||||
"_id": "KwQDiWcB0WOhS6qyXmrW",
|
||||
"firstSeen": "2018-12-07T14:12:38.560Z",
|
||||
"host": Object {
|
||||
"name": "siem-kibana",
|
||||
"os": Object {
|
||||
"name": "Debian GNU/Linux",
|
||||
"version": "9 (stretch)",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
]
|
||||
}
|
||||
hasNextPage={true}
|
||||
loadMore={[MockFunction]}
|
||||
loading={false}
|
||||
nextCursor="aa7ca589f1b8220002f2fc61c64cfbf1"
|
||||
totalCount={4}
|
||||
type="page"
|
||||
/>
|
||||
</ContextProvider>
|
||||
`;
|
||||
|
|
|
@ -11,8 +11,9 @@ import { getOr } from 'lodash/fp';
|
|||
import * as React from 'react';
|
||||
import { Provider as ReduxStoreProvider } from 'react-redux';
|
||||
|
||||
import { mockGlobalState } from '../../../../mock';
|
||||
import { mockFrameworks, mockGlobalState } from '../../../../mock';
|
||||
import { createStore, hostsModel, State } from '../../../../store';
|
||||
import { KibanaConfigContext } from '../../../formatted_date';
|
||||
import { HostsTable } from './index';
|
||||
import { mockData } from './mock';
|
||||
|
||||
|
@ -30,15 +31,17 @@ describe('Load More Table Component', () => {
|
|||
test('it renders the default Hosts table', () => {
|
||||
const wrapper = shallow(
|
||||
<ReduxStoreProvider store={store}>
|
||||
<HostsTable
|
||||
loading={false}
|
||||
data={mockData.Hosts.edges}
|
||||
totalCount={mockData.Hosts.totalCount}
|
||||
hasNextPage={getOr(false, 'hasNextPage', mockData.Hosts.pageInfo)!}
|
||||
nextCursor={getOr(null, 'endCursor.value', mockData.Hosts.pageInfo)!}
|
||||
loadMore={loadMore}
|
||||
type={hostsModel.HostsType.page}
|
||||
/>
|
||||
<KibanaConfigContext.Provider value={mockFrameworks.default_UTC}>
|
||||
<HostsTable
|
||||
loading={false}
|
||||
data={mockData.Hosts.edges}
|
||||
totalCount={mockData.Hosts.totalCount}
|
||||
hasNextPage={getOr(false, 'hasNextPage', mockData.Hosts.pageInfo)!}
|
||||
nextCursor={getOr(null, 'endCursor.value', mockData.Hosts.pageInfo)!}
|
||||
loadMore={loadMore}
|
||||
type={hostsModel.HostsType.page}
|
||||
/>
|
||||
</KibanaConfigContext.Provider>
|
||||
</ReduxStoreProvider>
|
||||
);
|
||||
|
||||
|
|
|
@ -4,13 +4,14 @@
|
|||
* you may not use this file except in compliance with the Elastic License.
|
||||
*/
|
||||
|
||||
import { EuiBadge, EuiLink } from '@elastic/eui';
|
||||
import { EuiBadge, EuiLink, EuiToolTip } from '@elastic/eui';
|
||||
import { get, isNil } from 'lodash/fp';
|
||||
import React from 'react';
|
||||
import { connect } from 'react-redux';
|
||||
import { pure } from 'recompose';
|
||||
import { ActionCreator } from 'typescript-fsa';
|
||||
|
||||
import { FormattedRelative } from '@kbn/i18n/react';
|
||||
import { HostsEdges } from '../../../../graphql/types';
|
||||
import { hostsActions, hostsModel, hostsSelectors, State } from '../../../../store';
|
||||
import { DragEffects, DraggableWrapper } from '../../../drag_and_drop/draggable_wrapper';
|
||||
|
@ -163,7 +164,14 @@ const getHostsColumns = (): Array<Columns<HostsEdges>> => [
|
|||
name: i18n.FIRST_SEEN,
|
||||
truncateText: false,
|
||||
hideForMobile: false,
|
||||
render: ({ node }) => defaultToEmptyTag(node.firstSeen),
|
||||
render: ({ node }) =>
|
||||
node.firstSeen && node.firstSeen !== '' ? (
|
||||
<EuiToolTip position="bottom" content={node.firstSeen}>
|
||||
<FormattedRelative value={node.firstSeen} />
|
||||
</EuiToolTip>
|
||||
) : (
|
||||
defaultToEmptyTag(node.firstSeen)
|
||||
),
|
||||
},
|
||||
{
|
||||
name: i18n.OS,
|
||||
|
|
|
@ -10,6 +10,7 @@ import { pure } from 'recompose';
|
|||
import { Ecs } from '../../../../../server/graphql/types';
|
||||
import { getMappedEcsValue } from '../../../../lib/ecs';
|
||||
import { getOrEmptyTag } from '../../../empty_value';
|
||||
import { PreferenceFormattedDate } from '../../../formatted_date';
|
||||
import { LocalizedDateTooltip } from '../../../localized_date_tooltip';
|
||||
|
||||
export const FormattedField = pure<{ data: Ecs; fieldName: string; fieldType: string }>(
|
||||
|
@ -18,7 +19,9 @@ export const FormattedField = pure<{ data: Ecs; fieldName: string; fieldType: st
|
|||
const maybeDate = moment(new Date(value!));
|
||||
|
||||
return fieldType === 'date' && value != null && maybeDate.isValid() ? (
|
||||
<LocalizedDateTooltip date={maybeDate.toDate()}>{value}</LocalizedDateTooltip>
|
||||
<LocalizedDateTooltip date={maybeDate.toDate()}>
|
||||
<PreferenceFormattedDate value={value} />
|
||||
</LocalizedDateTooltip>
|
||||
) : (
|
||||
getOrEmptyTag(fieldName, data)
|
||||
);
|
||||
|
|
|
@ -12,14 +12,17 @@ import { DragDropContext } from 'react-beautiful-dnd';
|
|||
import { Provider as ReduxStoreProvider } from 'react-redux';
|
||||
import { ThemeProvider } from 'styled-components';
|
||||
|
||||
import moment from 'moment-timezone';
|
||||
import { plainColumnRenderer } from '.';
|
||||
import { Ecs } from '../../../../graphql/types';
|
||||
import { getAllFieldsInSchemaByMappedName, virtualEcsSchema } from '../../../../lib/ecs';
|
||||
import { mockEcsData } from '../../../../mock';
|
||||
import { mockEcsData, mockFrameworks } from '../../../../mock';
|
||||
import { createStore } from '../../../../store';
|
||||
import { getEmptyValue } from '../../../empty_value';
|
||||
import { KibanaConfigContext } from '../../../formatted_date';
|
||||
|
||||
const allFieldsInSchemaByName = getAllFieldsInSchemaByMappedName(virtualEcsSchema);
|
||||
const mockFramework = mockFrameworks.default_UTC;
|
||||
|
||||
describe('plain_column_renderer', () => {
|
||||
let mockDatum: Ecs;
|
||||
|
@ -55,11 +58,13 @@ describe('plain_column_renderer', () => {
|
|||
);
|
||||
const wrapper = mount(
|
||||
<ThemeProvider theme={() => ({ eui: euiDarkVars, darkMode: true })}>
|
||||
<ReduxStoreProvider store={store}>
|
||||
<DragDropContext onDragEnd={noop}>
|
||||
<span>{column}</span>
|
||||
</DragDropContext>
|
||||
</ReduxStoreProvider>
|
||||
<KibanaConfigContext.Provider value={mockFramework}>
|
||||
<ReduxStoreProvider store={store}>
|
||||
<DragDropContext onDragEnd={noop}>
|
||||
<span>{column}</span>
|
||||
</DragDropContext>
|
||||
</ReduxStoreProvider>
|
||||
</KibanaConfigContext.Provider>
|
||||
</ThemeProvider>
|
||||
);
|
||||
expect(wrapper.text()).toEqual('Access');
|
||||
|
@ -73,11 +78,13 @@ describe('plain_column_renderer', () => {
|
|||
);
|
||||
const wrapper = mount(
|
||||
<ThemeProvider theme={() => ({ eui: euiDarkVars, darkMode: true })}>
|
||||
<ReduxStoreProvider store={store}>
|
||||
<DragDropContext onDragEnd={noop}>
|
||||
<span>{column}</span>
|
||||
</DragDropContext>
|
||||
</ReduxStoreProvider>
|
||||
<KibanaConfigContext.Provider value={mockFramework}>
|
||||
<ReduxStoreProvider store={store}>
|
||||
<DragDropContext onDragEnd={noop}>
|
||||
<span>{column}</span>
|
||||
</DragDropContext>
|
||||
</ReduxStoreProvider>
|
||||
</KibanaConfigContext.Provider>
|
||||
</ThemeProvider>
|
||||
);
|
||||
expect(wrapper.text()).toEqual('192.168.0.3');
|
||||
|
@ -91,11 +98,13 @@ describe('plain_column_renderer', () => {
|
|||
);
|
||||
const wrapper = mount(
|
||||
<ThemeProvider theme={() => ({ eui: euiDarkVars, darkMode: true })}>
|
||||
<ReduxStoreProvider store={store}>
|
||||
<DragDropContext onDragEnd={noop}>
|
||||
<span>{column}</span>
|
||||
</DragDropContext>
|
||||
</ReduxStoreProvider>
|
||||
<KibanaConfigContext.Provider value={mockFramework}>
|
||||
<ReduxStoreProvider store={store}>
|
||||
<DragDropContext onDragEnd={noop}>
|
||||
<span>{column}</span>
|
||||
</DragDropContext>
|
||||
</ReduxStoreProvider>
|
||||
</KibanaConfigContext.Provider>
|
||||
</ThemeProvider>
|
||||
);
|
||||
expect(wrapper.text()).toEqual('1');
|
||||
|
@ -109,11 +118,13 @@ describe('plain_column_renderer', () => {
|
|||
);
|
||||
const wrapper = mount(
|
||||
<ThemeProvider theme={() => ({ eui: euiDarkVars, darkMode: true })}>
|
||||
<ReduxStoreProvider store={store}>
|
||||
<DragDropContext onDragEnd={noop}>
|
||||
<span>{column}</span>
|
||||
</DragDropContext>
|
||||
</ReduxStoreProvider>
|
||||
<KibanaConfigContext.Provider value={mockFramework}>
|
||||
<ReduxStoreProvider store={store}>
|
||||
<DragDropContext onDragEnd={noop}>
|
||||
<span>{column}</span>
|
||||
</DragDropContext>
|
||||
</ReduxStoreProvider>
|
||||
</KibanaConfigContext.Provider>
|
||||
</ThemeProvider>
|
||||
);
|
||||
expect(wrapper.text()).toEqual('xx');
|
||||
|
@ -127,11 +138,13 @@ describe('plain_column_renderer', () => {
|
|||
);
|
||||
const wrapper = mount(
|
||||
<ThemeProvider theme={() => ({ eui: euiDarkVars, darkMode: true })}>
|
||||
<ReduxStoreProvider store={store}>
|
||||
<DragDropContext onDragEnd={noop}>
|
||||
<span>{column}</span>
|
||||
</DragDropContext>
|
||||
</ReduxStoreProvider>
|
||||
<KibanaConfigContext.Provider value={mockFramework}>
|
||||
<ReduxStoreProvider store={store}>
|
||||
<DragDropContext onDragEnd={noop}>
|
||||
<span>{column}</span>
|
||||
</DragDropContext>
|
||||
</ReduxStoreProvider>
|
||||
</KibanaConfigContext.Provider>
|
||||
</ThemeProvider>
|
||||
);
|
||||
expect(wrapper.text()).toEqual('3');
|
||||
|
@ -145,17 +158,19 @@ describe('plain_column_renderer', () => {
|
|||
);
|
||||
const wrapper = mount(
|
||||
<ThemeProvider theme={() => ({ eui: euiDarkVars, darkMode: true })}>
|
||||
<ReduxStoreProvider store={store}>
|
||||
<DragDropContext onDragEnd={noop}>
|
||||
<span>{column}</span>
|
||||
</DragDropContext>
|
||||
</ReduxStoreProvider>
|
||||
<KibanaConfigContext.Provider value={mockFramework}>
|
||||
<ReduxStoreProvider store={store}>
|
||||
<DragDropContext onDragEnd={noop}>
|
||||
<span>{column}</span>
|
||||
</DragDropContext>
|
||||
</ReduxStoreProvider>
|
||||
</KibanaConfigContext.Provider>
|
||||
</ThemeProvider>
|
||||
);
|
||||
expect(wrapper.text()).toEqual('192.168.0.1');
|
||||
});
|
||||
|
||||
test('should return the (unformatted) time if timestamp has a valid value', () => {
|
||||
test('should return the time formatted as per Kibana advanced settings if timestamp has a valid value', () => {
|
||||
const column = plainColumnRenderer.renderColumn(
|
||||
'timestamp',
|
||||
mockDatum,
|
||||
|
@ -163,14 +178,18 @@ describe('plain_column_renderer', () => {
|
|||
);
|
||||
const wrapper = mount(
|
||||
<ThemeProvider theme={() => ({ eui: euiDarkVars, darkMode: true })}>
|
||||
<ReduxStoreProvider store={store}>
|
||||
<DragDropContext onDragEnd={noop}>
|
||||
<span>{column}</span>
|
||||
</DragDropContext>
|
||||
</ReduxStoreProvider>
|
||||
<KibanaConfigContext.Provider value={mockFramework}>
|
||||
<ReduxStoreProvider store={store}>
|
||||
<DragDropContext onDragEnd={noop}>
|
||||
<span>{column}</span>
|
||||
</DragDropContext>
|
||||
</ReduxStoreProvider>
|
||||
</KibanaConfigContext.Provider>
|
||||
</ThemeProvider>
|
||||
);
|
||||
expect(wrapper.text()).toEqual(mockDatum.timestamp!);
|
||||
expect(wrapper.text()).toEqual(
|
||||
moment.tz(mockDatum.timestamp!, mockFramework.dateFormatTz!).format(mockFramework.dateFormat)
|
||||
);
|
||||
});
|
||||
|
||||
test('should return the value of event.action if event.action has a valid value', () => {
|
||||
|
@ -181,11 +200,13 @@ describe('plain_column_renderer', () => {
|
|||
);
|
||||
const wrapper = mount(
|
||||
<ThemeProvider theme={() => ({ eui: euiDarkVars, darkMode: true })}>
|
||||
<ReduxStoreProvider store={store}>
|
||||
<DragDropContext onDragEnd={noop}>
|
||||
<span>{column}</span>
|
||||
</DragDropContext>
|
||||
</ReduxStoreProvider>
|
||||
<KibanaConfigContext.Provider value={mockFramework}>
|
||||
<ReduxStoreProvider store={store}>
|
||||
<DragDropContext onDragEnd={noop}>
|
||||
<span>{column}</span>
|
||||
</DragDropContext>
|
||||
</ReduxStoreProvider>
|
||||
</KibanaConfigContext.Provider>
|
||||
</ThemeProvider>
|
||||
);
|
||||
expect(wrapper.text()).toEqual('Action');
|
||||
|
@ -199,11 +220,13 @@ describe('plain_column_renderer', () => {
|
|||
);
|
||||
const wrapper = mount(
|
||||
<ThemeProvider theme={() => ({ eui: euiDarkVars, darkMode: true })}>
|
||||
<ReduxStoreProvider store={store}>
|
||||
<DragDropContext onDragEnd={noop}>
|
||||
<span>{column}</span>
|
||||
</DragDropContext>
|
||||
</ReduxStoreProvider>
|
||||
<KibanaConfigContext.Provider value={mockFramework}>
|
||||
<ReduxStoreProvider store={store}>
|
||||
<DragDropContext onDragEnd={noop}>
|
||||
<span>{column}</span>
|
||||
</DragDropContext>
|
||||
</ReduxStoreProvider>
|
||||
</KibanaConfigContext.Provider>
|
||||
</ThemeProvider>
|
||||
);
|
||||
expect(wrapper.text()).toEqual('john.dee');
|
||||
|
@ -218,11 +241,13 @@ describe('plain_column_renderer', () => {
|
|||
);
|
||||
const wrapper = mount(
|
||||
<ThemeProvider theme={() => ({ eui: euiDarkVars, darkMode: true })}>
|
||||
<ReduxStoreProvider store={store}>
|
||||
<DragDropContext onDragEnd={noop}>
|
||||
<span>{emptyColumn}</span>
|
||||
</DragDropContext>
|
||||
</ReduxStoreProvider>
|
||||
<KibanaConfigContext.Provider value={mockFramework}>
|
||||
<ReduxStoreProvider store={store}>
|
||||
<DragDropContext onDragEnd={noop}>
|
||||
<span>{emptyColumn}</span>
|
||||
</DragDropContext>
|
||||
</ReduxStoreProvider>
|
||||
</KibanaConfigContext.Provider>
|
||||
</ThemeProvider>
|
||||
);
|
||||
expect(wrapper.text()).toEqual(getEmptyValue());
|
||||
|
@ -237,11 +262,13 @@ describe('plain_column_renderer', () => {
|
|||
);
|
||||
const wrapper = mount(
|
||||
<ThemeProvider theme={() => ({ eui: euiDarkVars, darkMode: true })}>
|
||||
<ReduxStoreProvider store={store}>
|
||||
<DragDropContext onDragEnd={noop}>
|
||||
<span>{emptyColumn}</span>
|
||||
</DragDropContext>
|
||||
</ReduxStoreProvider>
|
||||
<KibanaConfigContext.Provider value={mockFramework}>
|
||||
<ReduxStoreProvider store={store}>
|
||||
<DragDropContext onDragEnd={noop}>
|
||||
<span>{emptyColumn}</span>
|
||||
</DragDropContext>
|
||||
</ReduxStoreProvider>
|
||||
</KibanaConfigContext.Provider>
|
||||
</ThemeProvider>
|
||||
);
|
||||
expect(wrapper.text()).toEqual(getEmptyValue());
|
||||
|
@ -256,11 +283,13 @@ describe('plain_column_renderer', () => {
|
|||
);
|
||||
const wrapper = mount(
|
||||
<ThemeProvider theme={() => ({ eui: euiDarkVars, darkMode: true })}>
|
||||
<ReduxStoreProvider store={store}>
|
||||
<DragDropContext onDragEnd={noop}>
|
||||
<span>{emptyColumn}</span>
|
||||
</DragDropContext>
|
||||
</ReduxStoreProvider>
|
||||
<KibanaConfigContext.Provider value={mockFramework}>
|
||||
<ReduxStoreProvider store={store}>
|
||||
<DragDropContext onDragEnd={noop}>
|
||||
<span>{emptyColumn}</span>
|
||||
</DragDropContext>
|
||||
</ReduxStoreProvider>
|
||||
</KibanaConfigContext.Provider>
|
||||
</ThemeProvider>
|
||||
);
|
||||
expect(wrapper.text()).toEqual(getEmptyValue());
|
||||
|
@ -275,11 +304,13 @@ describe('plain_column_renderer', () => {
|
|||
);
|
||||
const wrapper = mount(
|
||||
<ThemeProvider theme={() => ({ eui: euiDarkVars, darkMode: true })}>
|
||||
<ReduxStoreProvider store={store}>
|
||||
<DragDropContext onDragEnd={noop}>
|
||||
<span>{emptyColumn}</span>
|
||||
</DragDropContext>
|
||||
</ReduxStoreProvider>
|
||||
<KibanaConfigContext.Provider value={mockFramework}>
|
||||
<ReduxStoreProvider store={store}>
|
||||
<DragDropContext onDragEnd={noop}>
|
||||
<span>{emptyColumn}</span>
|
||||
</DragDropContext>
|
||||
</ReduxStoreProvider>
|
||||
</KibanaConfigContext.Provider>
|
||||
</ThemeProvider>
|
||||
);
|
||||
expect(wrapper.text()).toEqual(getEmptyValue());
|
||||
|
@ -294,11 +325,13 @@ describe('plain_column_renderer', () => {
|
|||
);
|
||||
const wrapper = mount(
|
||||
<ThemeProvider theme={() => ({ eui: euiDarkVars, darkMode: true })}>
|
||||
<ReduxStoreProvider store={store}>
|
||||
<DragDropContext onDragEnd={noop}>
|
||||
<span>{emptyColumn}</span>
|
||||
</DragDropContext>
|
||||
</ReduxStoreProvider>
|
||||
<KibanaConfigContext.Provider value={mockFramework}>
|
||||
<ReduxStoreProvider store={store}>
|
||||
<DragDropContext onDragEnd={noop}>
|
||||
<span>{emptyColumn}</span>
|
||||
</DragDropContext>
|
||||
</ReduxStoreProvider>
|
||||
</KibanaConfigContext.Provider>
|
||||
</ThemeProvider>
|
||||
);
|
||||
expect(wrapper.text()).toEqual(getEmptyValue());
|
||||
|
@ -313,11 +346,13 @@ describe('plain_column_renderer', () => {
|
|||
);
|
||||
const wrapper = mount(
|
||||
<ThemeProvider theme={() => ({ eui: euiDarkVars, darkMode: true })}>
|
||||
<ReduxStoreProvider store={store}>
|
||||
<DragDropContext onDragEnd={noop}>
|
||||
<span>{emptyColumn}</span>
|
||||
</DragDropContext>
|
||||
</ReduxStoreProvider>
|
||||
<KibanaConfigContext.Provider value={mockFramework}>
|
||||
<ReduxStoreProvider store={store}>
|
||||
<DragDropContext onDragEnd={noop}>
|
||||
<span>{emptyColumn}</span>
|
||||
</DragDropContext>
|
||||
</ReduxStoreProvider>
|
||||
</KibanaConfigContext.Provider>
|
||||
</ThemeProvider>
|
||||
);
|
||||
expect(wrapper.text()).toEqual(getEmptyValue());
|
||||
|
@ -332,11 +367,13 @@ describe('plain_column_renderer', () => {
|
|||
);
|
||||
const wrapper = mount(
|
||||
<ThemeProvider theme={() => ({ eui: euiDarkVars, darkMode: true })}>
|
||||
<ReduxStoreProvider store={store}>
|
||||
<DragDropContext onDragEnd={noop}>
|
||||
<span>{emptyColumn}</span>
|
||||
</DragDropContext>
|
||||
</ReduxStoreProvider>
|
||||
<KibanaConfigContext.Provider value={mockFramework}>
|
||||
<ReduxStoreProvider store={store}>
|
||||
<DragDropContext onDragEnd={noop}>
|
||||
<span>{emptyColumn}</span>
|
||||
</DragDropContext>
|
||||
</ReduxStoreProvider>
|
||||
</KibanaConfigContext.Provider>
|
||||
</ThemeProvider>
|
||||
);
|
||||
expect(wrapper.text()).toEqual(getEmptyValue());
|
||||
|
@ -351,11 +388,13 @@ describe('plain_column_renderer', () => {
|
|||
);
|
||||
const wrapper = mount(
|
||||
<ThemeProvider theme={() => ({ eui: euiDarkVars, darkMode: true })}>
|
||||
<ReduxStoreProvider store={store}>
|
||||
<DragDropContext onDragEnd={noop}>
|
||||
<span>{emptyColumn}</span>
|
||||
</DragDropContext>
|
||||
</ReduxStoreProvider>
|
||||
<KibanaConfigContext.Provider value={mockFramework}>
|
||||
<ReduxStoreProvider store={store}>
|
||||
<DragDropContext onDragEnd={noop}>
|
||||
<span>{emptyColumn}</span>
|
||||
</DragDropContext>
|
||||
</ReduxStoreProvider>
|
||||
</KibanaConfigContext.Provider>
|
||||
</ThemeProvider>
|
||||
);
|
||||
expect(wrapper.text()).toEqual(getEmptyValue());
|
||||
|
|
|
@ -1110,7 +1110,7 @@
|
|||
"name": "timestamp",
|
||||
"description": "",
|
||||
"args": [],
|
||||
"type": { "kind": "SCALAR", "name": "String", "ofType": null },
|
||||
"type": { "kind": "SCALAR", "name": "Date", "ofType": null },
|
||||
"isDeprecated": false,
|
||||
"deprecationReason": null
|
||||
},
|
||||
|
@ -1136,6 +1136,16 @@
|
|||
"enumValues": null,
|
||||
"possibleTypes": null
|
||||
},
|
||||
{
|
||||
"kind": "SCALAR",
|
||||
"name": "Date",
|
||||
"description": "",
|
||||
"fields": null,
|
||||
"inputFields": null,
|
||||
"interfaces": null,
|
||||
"enumValues": null,
|
||||
"possibleTypes": null
|
||||
},
|
||||
{
|
||||
"kind": "OBJECT",
|
||||
"name": "SourceEcsFields",
|
||||
|
@ -1635,7 +1645,7 @@
|
|||
"name": "timestamp",
|
||||
"description": "",
|
||||
"args": [],
|
||||
"type": { "kind": "SCALAR", "name": "String", "ofType": null },
|
||||
"type": { "kind": "SCALAR", "name": "Date", "ofType": null },
|
||||
"isDeprecated": false,
|
||||
"deprecationReason": null
|
||||
},
|
||||
|
@ -1978,7 +1988,7 @@
|
|||
"name": "firstSeen",
|
||||
"description": "",
|
||||
"args": [],
|
||||
"type": { "kind": "SCALAR", "name": "String", "ofType": null },
|
||||
"type": { "kind": "SCALAR", "name": "Date", "ofType": null },
|
||||
"isDeprecated": false,
|
||||
"deprecationReason": null
|
||||
},
|
||||
|
@ -1994,7 +2004,7 @@
|
|||
"name": "lastBeat",
|
||||
"description": "",
|
||||
"args": [],
|
||||
"type": { "kind": "SCALAR", "name": "String", "ofType": null },
|
||||
"type": { "kind": "SCALAR", "name": "Date", "ofType": null },
|
||||
"isDeprecated": false,
|
||||
"deprecationReason": null
|
||||
}
|
||||
|
@ -2154,7 +2164,7 @@
|
|||
"name": "timestamp",
|
||||
"description": "",
|
||||
"args": [],
|
||||
"type": { "kind": "SCALAR", "name": "String", "ofType": null },
|
||||
"type": { "kind": "SCALAR", "name": "Date", "ofType": null },
|
||||
"isDeprecated": false,
|
||||
"deprecationReason": null
|
||||
},
|
||||
|
|
|
@ -9,6 +9,12 @@
|
|||
// START: Typescript template
|
||||
// ====================================================
|
||||
|
||||
// ====================================================
|
||||
// Scalars
|
||||
// ====================================================
|
||||
|
||||
export type Date = any;
|
||||
|
||||
// ====================================================
|
||||
// Types
|
||||
// ====================================================
|
||||
|
@ -144,7 +150,7 @@ export interface UserEcsFields {
|
|||
}
|
||||
|
||||
export interface LastSourceHost {
|
||||
timestamp?: string | null;
|
||||
timestamp?: Date | null;
|
||||
|
||||
source?: SourceEcsFields | null;
|
||||
|
||||
|
@ -240,7 +246,7 @@ export interface Ecs {
|
|||
|
||||
suricata?: SuricataEcsFields | null;
|
||||
|
||||
timestamp?: string | null;
|
||||
timestamp?: Date | null;
|
||||
|
||||
user?: UserEcsFields | null;
|
||||
}
|
||||
|
@ -310,11 +316,11 @@ export interface HostsEdges {
|
|||
export interface HostItem {
|
||||
_id?: string | null;
|
||||
|
||||
firstSeen?: string | null;
|
||||
firstSeen?: Date | null;
|
||||
|
||||
host?: HostEcsFields | null;
|
||||
|
||||
lastBeat?: string | null;
|
||||
lastBeat?: Date | null;
|
||||
}
|
||||
|
||||
export interface NetworkTopNFlowData {
|
||||
|
@ -334,7 +340,7 @@ export interface NetworkTopNFlowEdges {
|
|||
export interface NetworkTopNFlowItem {
|
||||
_id?: string | null;
|
||||
|
||||
timestamp?: string | null;
|
||||
timestamp?: Date | null;
|
||||
|
||||
source?: TopNFlowItem | null;
|
||||
|
||||
|
@ -612,7 +618,7 @@ export namespace GetAuthenticationsQuery {
|
|||
export type LastSuccess = {
|
||||
__typename?: 'LastSourceHost';
|
||||
|
||||
timestamp?: string | null;
|
||||
timestamp?: Date | null;
|
||||
|
||||
source?: _Source | null;
|
||||
|
||||
|
@ -636,7 +642,7 @@ export namespace GetAuthenticationsQuery {
|
|||
export type LastFailure = {
|
||||
__typename?: 'LastSourceHost';
|
||||
|
||||
timestamp?: string | null;
|
||||
timestamp?: Date | null;
|
||||
|
||||
source?: __Source | null;
|
||||
|
||||
|
@ -740,7 +746,7 @@ export namespace GetEventsQuery {
|
|||
|
||||
_index?: string | null;
|
||||
|
||||
timestamp?: string | null;
|
||||
timestamp?: Date | null;
|
||||
|
||||
event?: Event | null;
|
||||
|
||||
|
@ -873,9 +879,9 @@ export namespace GetHostSummaryQuery {
|
|||
|
||||
_id?: string | null;
|
||||
|
||||
firstSeen?: string | null;
|
||||
firstSeen?: Date | null;
|
||||
|
||||
lastBeat?: string | null;
|
||||
lastBeat?: Date | null;
|
||||
|
||||
host?: Host | null;
|
||||
};
|
||||
|
@ -976,9 +982,9 @@ export namespace GetHostsTableQuery {
|
|||
|
||||
_id?: string | null;
|
||||
|
||||
firstSeen?: string | null;
|
||||
firstSeen?: Date | null;
|
||||
|
||||
lastBeat?: string | null;
|
||||
lastBeat?: Date | null;
|
||||
|
||||
host?: Host | null;
|
||||
};
|
||||
|
@ -1311,7 +1317,7 @@ export namespace GetTimelineQuery {
|
|||
|
||||
_index?: string | null;
|
||||
|
||||
timestamp?: string | null;
|
||||
timestamp?: Date | null;
|
||||
|
||||
event?: Event | null;
|
||||
|
||||
|
|
|
@ -24,6 +24,7 @@ const BREADCRUMBS_ELEMENT_ID = 'react-secops-breadcrumbs';
|
|||
|
||||
export class AppKibanaFrameworkAdapter implements AppFrameworkAdapter {
|
||||
public dateFormat?: string;
|
||||
public dateFormatTz?: string;
|
||||
public darkMode?: boolean;
|
||||
public kbnVersion?: string;
|
||||
public scaledDateFormat?: string;
|
||||
|
@ -130,6 +131,7 @@ export class AppKibanaFrameworkAdapter implements AppFrameworkAdapter {
|
|||
this.timezone = Private(this.timezoneProvider)();
|
||||
this.kbnVersion = kbnVersion;
|
||||
this.dateFormat = config.get('dateFormat');
|
||||
this.dateFormatTz = config.get('dateFormat:tz');
|
||||
try {
|
||||
this.darkMode = config.get('theme:darkMode');
|
||||
} catch (e) {
|
||||
|
|
|
@ -9,6 +9,7 @@ import { AppFrameworkAdapter } from '../../lib';
|
|||
export class AppTestingFrameworkAdapter implements AppFrameworkAdapter {
|
||||
public appState?: object;
|
||||
public dateFormat?: string;
|
||||
public dateFormatTz?: string;
|
||||
public kbnVersion?: string;
|
||||
public scaledDateFormat?: string;
|
||||
public timezone?: string;
|
||||
|
|
|
@ -23,6 +23,7 @@ export type AppApolloClient = ApolloClient<NormalizedCacheObject>;
|
|||
export interface AppFrameworkAdapter {
|
||||
appState?: object;
|
||||
dateFormat?: string;
|
||||
dateFormatTz?: string;
|
||||
darkMode?: boolean;
|
||||
kbnVersion?: string;
|
||||
scaledDateFormat?: string;
|
||||
|
|
|
@ -5,5 +5,6 @@
|
|||
*/
|
||||
|
||||
export * from './global_state';
|
||||
export * from './mock_ecs';
|
||||
export * from './index_pattern';
|
||||
export * from './kibana_config';
|
||||
export * from './mock_ecs';
|
||||
|
|
30
x-pack/plugins/secops/public/mock/kibana_config.ts
Normal file
30
x-pack/plugins/secops/public/mock/kibana_config.ts
Normal file
|
@ -0,0 +1,30 @@
|
|||
/*
|
||||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||
* or more contributor license agreements. Licensed under the Elastic License;
|
||||
* you may not use this file except in compliance with the Elastic License.
|
||||
*/
|
||||
|
||||
import { AppTestingFrameworkAdapter } from '../lib/adapters/framework/testing_framework_adapter';
|
||||
|
||||
export const mockFrameworks: Readonly<Record<string, Partial<AppTestingFrameworkAdapter>>> = {
|
||||
default_browser: {
|
||||
dateFormat: 'MMM D, YYYY @ HH:mm:ss.SSS',
|
||||
dateFormatTz: 'Browser',
|
||||
timezone: 'America/Denver',
|
||||
},
|
||||
default_ET: {
|
||||
dateFormat: 'MMM D, YYYY @ HH:mm:ss.SSS',
|
||||
dateFormatTz: 'America/New_York',
|
||||
timezone: 'America/New_York',
|
||||
},
|
||||
default_MT: {
|
||||
dateFormat: 'MMM D, YYYY @ HH:mm:ss.SSS',
|
||||
dateFormatTz: 'America/Denver',
|
||||
timezone: 'America/Denver',
|
||||
},
|
||||
default_UTC: {
|
||||
dateFormat: 'MMM D, YYYY @ HH:mm:ss.SSS',
|
||||
dateFormatTz: 'UTC',
|
||||
timezone: 'UTC',
|
||||
},
|
||||
};
|
|
@ -8,7 +8,7 @@ import gql from 'graphql-tag';
|
|||
|
||||
export const authenticationsSchema = gql`
|
||||
type LastSourceHost {
|
||||
timestamp: String
|
||||
timestamp: Date
|
||||
source: SourceEcsFields
|
||||
host: HostEcsFields
|
||||
}
|
||||
|
|
|
@ -11,6 +11,7 @@ import { rootSchema } from '../../../common/graphql/root/schema.gql';
|
|||
import { sharedSchema } from '../../../common/graphql/shared';
|
||||
import { Logger } from '../../utils/logger';
|
||||
import { ecsSchema } from '../ecs';
|
||||
import { dateSchema } from '../scalar_date';
|
||||
import { sourceStatusSchema } from '../source_status/schema.gql';
|
||||
import { sourcesSchema } from '../sources/schema.gql';
|
||||
import { getAuthenticationsQueryMock, mockAuthenticationsData } from './authentications.mock';
|
||||
|
@ -103,6 +104,7 @@ describe('Test Source Schema', () => {
|
|||
sourceStatusSchema,
|
||||
ecsSchema,
|
||||
authenticationsSchema,
|
||||
dateSchema,
|
||||
];
|
||||
const mockSchema = makeExecutableSchema({ typeDefs });
|
||||
|
||||
|
|
|
@ -116,7 +116,7 @@ export const ecsSchema = gql`
|
|||
host: HostEcsFields
|
||||
source: SourceEcsFields
|
||||
suricata: SuricataEcsFields
|
||||
timestamp: String
|
||||
timestamp: Date
|
||||
user: UserEcsFields
|
||||
}
|
||||
|
||||
|
|
|
@ -11,6 +11,7 @@ import { rootSchema } from '../../../common/graphql/root/schema.gql';
|
|||
import { sharedSchema } from '../../../common/graphql/shared';
|
||||
import { Logger } from '../../utils/logger';
|
||||
import { ecsSchema } from '../ecs';
|
||||
import { dateSchema } from '../scalar_date';
|
||||
import { sourceStatusSchema } from '../source_status/schema.gql';
|
||||
import { sourcesSchema } from '../sources/schema.gql';
|
||||
import { getEventsQueryMock, mockEventsData } from './events.mock';
|
||||
|
@ -133,6 +134,7 @@ describe('Test Source Schema', () => {
|
|||
sourceStatusSchema,
|
||||
ecsSchema,
|
||||
eventsSchema,
|
||||
dateSchema,
|
||||
];
|
||||
const mockSchema = makeExecutableSchema({ typeDefs });
|
||||
|
||||
|
|
|
@ -9,9 +9,9 @@ import gql from 'graphql-tag';
|
|||
export const hostsSchema = gql`
|
||||
type HostItem {
|
||||
_id: String
|
||||
firstSeen: String
|
||||
firstSeen: Date
|
||||
host: HostEcsFields
|
||||
lastBeat: String
|
||||
lastBeat: Date
|
||||
}
|
||||
|
||||
type HostsEdges {
|
||||
|
|
|
@ -11,6 +11,7 @@ import { rootSchema } from '../../../common/graphql/root/schema.gql';
|
|||
import { sharedSchema } from '../../../common/graphql/shared';
|
||||
import { Logger } from '../../utils/logger';
|
||||
import { ecsSchema } from '../ecs';
|
||||
import { dateSchema } from '../scalar_date';
|
||||
import { sourceStatusSchema } from '../source_status/schema.gql';
|
||||
import { sourcesSchema } from '../sources/schema.gql';
|
||||
import { getHostsQueryMock, mockHostsData } from './hosts.mock';
|
||||
|
@ -87,6 +88,7 @@ describe('Test Source Schema', () => {
|
|||
sourceStatusSchema,
|
||||
ecsSchema,
|
||||
hostsSchema,
|
||||
dateSchema,
|
||||
];
|
||||
const mockSchema = makeExecutableSchema({ typeDefs });
|
||||
|
||||
|
|
|
@ -14,15 +14,17 @@ import { ecsSchema } from './ecs';
|
|||
import { eventsSchema } from './events';
|
||||
import { hostsSchema } from './hosts';
|
||||
import { networkTopNFlowSchema } from './network_top_n_flow';
|
||||
import { dateSchema } from './scalar_date';
|
||||
import { sourceStatusSchema } from './source_status';
|
||||
import { sourcesSchema } from './sources';
|
||||
import { uncommonProcessesSchema } from './uncommon_processes';
|
||||
import { whoAmISchema } from './who_am_i';
|
||||
|
||||
export const schemas = [
|
||||
ecsSchema,
|
||||
authenticationsSchema,
|
||||
ecsSchema,
|
||||
eventsSchema,
|
||||
dateSchema,
|
||||
hostsSchema,
|
||||
networkTopNFlowSchema,
|
||||
rootSchema,
|
||||
|
|
|
@ -15,7 +15,7 @@ export const networkTopNFlowSchema = gql`
|
|||
|
||||
type NetworkTopNFlowItem {
|
||||
_id: String
|
||||
timestamp: String
|
||||
timestamp: Date
|
||||
source: TopNFlowItem
|
||||
destination: TopNFlowItem
|
||||
client: TopNFlowItem
|
||||
|
|
|
@ -11,6 +11,7 @@ import { rootSchema } from '../../../common/graphql/root/schema.gql';
|
|||
import { sharedSchema } from '../../../common/graphql/shared';
|
||||
import { Logger } from '../../utils/logger';
|
||||
import { ecsSchema } from '../ecs';
|
||||
import { dateSchema } from '../scalar_date';
|
||||
import { sourceStatusSchema } from '../source_status/schema.gql';
|
||||
import { sourcesSchema } from '../sources/schema.gql';
|
||||
import { NetworkTopNFlowDirection, NetworkTopNFlowType } from '../types';
|
||||
|
@ -107,6 +108,7 @@ describe('Test Source Schema', () => {
|
|||
sourceStatusSchema,
|
||||
ecsSchema,
|
||||
networkTopNFlowSchema,
|
||||
dateSchema,
|
||||
];
|
||||
const mockSchema = makeExecutableSchema({ typeDefs });
|
||||
|
||||
|
|
|
@ -0,0 +1,8 @@
|
|||
/*
|
||||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||
* or more contributor license agreements. Licensed under the Elastic License;
|
||||
* you may not use this file except in compliance with the Elastic License.
|
||||
*/
|
||||
|
||||
export { createScalarDateResolvers } from './resolvers';
|
||||
export { dateSchema } from './schema.gql';
|
|
@ -0,0 +1,53 @@
|
|||
/*
|
||||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||
* or more contributor license agreements. Licensed under the Elastic License;
|
||||
* you may not use this file except in compliance with the Elastic License.
|
||||
*/
|
||||
import { IntValueNode, StringValueNode } from 'graphql';
|
||||
import { dateScalar } from './resolvers';
|
||||
|
||||
describe('Test ScalarDate Resolver', () => {
|
||||
describe('#serialize', () => {
|
||||
test('Make sure that an epoch date number is serialized', () => {
|
||||
const date = dateScalar.serialize(1514782800000);
|
||||
expect(date).toEqual('2018-01-01T05:00:00.000Z');
|
||||
});
|
||||
|
||||
test('Make sure that a date string is serialized', () => {
|
||||
const date = dateScalar.serialize('2018-01-01T05:00:00.000Z');
|
||||
expect(date).toEqual('2018-01-01T05:00:00.000Z');
|
||||
});
|
||||
});
|
||||
|
||||
describe('#parseValue', () => {
|
||||
test('Make sure that an epoch date number passes through parseValue', () => {
|
||||
const date = dateScalar.parseValue(1514782800000);
|
||||
expect(date).toEqual(1514782800000);
|
||||
});
|
||||
|
||||
test('Make sure that a date string passes through parseValue', () => {
|
||||
const date = dateScalar.parseValue('2018-01-01T05:00:00.000Z');
|
||||
expect(date).toEqual('2018-01-01T05:00:00.000Z');
|
||||
});
|
||||
});
|
||||
|
||||
describe('#parseLiteral', () => {
|
||||
test('Make sure that an epoch date string passes through parseLiteral', () => {
|
||||
const valueNode: IntValueNode = {
|
||||
kind: 'IntValue',
|
||||
value: '1514782800000',
|
||||
};
|
||||
const date = dateScalar.parseLiteral(valueNode);
|
||||
expect(date).toEqual(1514782800000);
|
||||
});
|
||||
|
||||
test('Make sure that a date string passes through parseLiteral', () => {
|
||||
const valueNode: StringValueNode = {
|
||||
kind: 'StringValue',
|
||||
value: '2018-01-01T05:00:00.000Z',
|
||||
};
|
||||
const date = dateScalar.parseLiteral(valueNode);
|
||||
expect(date).toEqual('2018-01-01T05:00:00.000Z');
|
||||
});
|
||||
});
|
||||
});
|
|
@ -0,0 +1,32 @@
|
|||
/*
|
||||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||
* or more contributor license agreements. Licensed under the Elastic License;
|
||||
* you may not use this file except in compliance with the Elastic License.
|
||||
*/
|
||||
|
||||
import { GraphQLScalarType, Kind } from 'graphql';
|
||||
|
||||
export const dateScalar = new GraphQLScalarType({
|
||||
name: 'Date',
|
||||
description:
|
||||
'Represents a Date for either an ES formatted date string or epoch string ISO8601 formatted',
|
||||
serialize(value): string {
|
||||
return Number.isNaN(Date.parse(value)) ? new Date(value).toISOString() : value;
|
||||
},
|
||||
parseValue(value) {
|
||||
return value;
|
||||
},
|
||||
parseLiteral(ast) {
|
||||
switch (ast.kind) {
|
||||
case Kind.INT:
|
||||
return parseInt(ast.value, 10);
|
||||
case Kind.STRING:
|
||||
return ast.value;
|
||||
}
|
||||
return null;
|
||||
},
|
||||
});
|
||||
|
||||
export const createScalarDateResolvers = () => ({
|
||||
Date: dateScalar,
|
||||
});
|
|
@ -0,0 +1,11 @@
|
|||
/*
|
||||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||
* or more contributor license agreements. Licensed under the Elastic License;
|
||||
* you may not use this file except in compliance with the Elastic License.
|
||||
*/
|
||||
|
||||
import gql from 'graphql-tag';
|
||||
|
||||
export const dateSchema = gql`
|
||||
scalar Date
|
||||
`;
|
|
@ -38,6 +38,12 @@ export type SubscriptionResolver<Result, Parent = any, Context = any, Args = nev
|
|||
// START: Typescript template
|
||||
// ====================================================
|
||||
|
||||
// ====================================================
|
||||
// Scalars
|
||||
// ====================================================
|
||||
|
||||
export type Date = any;
|
||||
|
||||
// ====================================================
|
||||
// Types
|
||||
// ====================================================
|
||||
|
@ -173,7 +179,7 @@ export interface UserEcsFields {
|
|||
}
|
||||
|
||||
export interface LastSourceHost {
|
||||
timestamp?: string | null;
|
||||
timestamp?: Date | null;
|
||||
|
||||
source?: SourceEcsFields | null;
|
||||
|
||||
|
@ -269,7 +275,7 @@ export interface Ecs {
|
|||
|
||||
suricata?: SuricataEcsFields | null;
|
||||
|
||||
timestamp?: string | null;
|
||||
timestamp?: Date | null;
|
||||
|
||||
user?: UserEcsFields | null;
|
||||
}
|
||||
|
@ -339,11 +345,11 @@ export interface HostsEdges {
|
|||
export interface HostItem {
|
||||
_id?: string | null;
|
||||
|
||||
firstSeen?: string | null;
|
||||
firstSeen?: Date | null;
|
||||
|
||||
host?: HostEcsFields | null;
|
||||
|
||||
lastBeat?: string | null;
|
||||
lastBeat?: Date | null;
|
||||
}
|
||||
|
||||
export interface NetworkTopNFlowData {
|
||||
|
@ -363,7 +369,7 @@ export interface NetworkTopNFlowEdges {
|
|||
export interface NetworkTopNFlowItem {
|
||||
_id?: string | null;
|
||||
|
||||
timestamp?: string | null;
|
||||
timestamp?: Date | null;
|
||||
|
||||
source?: TopNFlowItem | null;
|
||||
|
||||
|
@ -1059,7 +1065,7 @@ export namespace UserEcsFieldsResolvers {
|
|||
|
||||
export namespace LastSourceHostResolvers {
|
||||
export interface Resolvers<Context = SecOpsContext, TypeParent = LastSourceHost> {
|
||||
timestamp?: TimestampResolver<string | null, TypeParent, Context>;
|
||||
timestamp?: TimestampResolver<Date | null, TypeParent, Context>;
|
||||
|
||||
source?: SourceResolver<SourceEcsFields | null, TypeParent, Context>;
|
||||
|
||||
|
@ -1067,7 +1073,7 @@ export namespace LastSourceHostResolvers {
|
|||
}
|
||||
|
||||
export type TimestampResolver<
|
||||
R = string | null,
|
||||
R = Date | null,
|
||||
Parent = LastSourceHost,
|
||||
Context = SecOpsContext
|
||||
> = Resolver<R, Parent, Context>;
|
||||
|
@ -1337,7 +1343,7 @@ export namespace EcsResolvers {
|
|||
|
||||
suricata?: SuricataResolver<SuricataEcsFields | null, TypeParent, Context>;
|
||||
|
||||
timestamp?: TimestampResolver<string | null, TypeParent, Context>;
|
||||
timestamp?: TimestampResolver<Date | null, TypeParent, Context>;
|
||||
|
||||
user?: UserResolver<UserEcsFields | null, TypeParent, Context>;
|
||||
}
|
||||
|
@ -1382,11 +1388,11 @@ export namespace EcsResolvers {
|
|||
Parent = Ecs,
|
||||
Context = SecOpsContext
|
||||
> = Resolver<R, Parent, Context>;
|
||||
export type TimestampResolver<
|
||||
R = string | null,
|
||||
Parent = Ecs,
|
||||
Context = SecOpsContext
|
||||
> = Resolver<R, Parent, Context>;
|
||||
export type TimestampResolver<R = Date | null, Parent = Ecs, Context = SecOpsContext> = Resolver<
|
||||
R,
|
||||
Parent,
|
||||
Context
|
||||
>;
|
||||
export type UserResolver<
|
||||
R = UserEcsFields | null,
|
||||
Parent = Ecs,
|
||||
|
@ -1599,11 +1605,11 @@ export namespace HostItemResolvers {
|
|||
export interface Resolvers<Context = SecOpsContext, TypeParent = HostItem> {
|
||||
_id?: IdResolver<string | null, TypeParent, Context>;
|
||||
|
||||
firstSeen?: FirstSeenResolver<string | null, TypeParent, Context>;
|
||||
firstSeen?: FirstSeenResolver<Date | null, TypeParent, Context>;
|
||||
|
||||
host?: HostResolver<HostEcsFields | null, TypeParent, Context>;
|
||||
|
||||
lastBeat?: LastBeatResolver<string | null, TypeParent, Context>;
|
||||
lastBeat?: LastBeatResolver<Date | null, TypeParent, Context>;
|
||||
}
|
||||
|
||||
export type IdResolver<R = string | null, Parent = HostItem, Context = SecOpsContext> = Resolver<
|
||||
|
@ -1612,7 +1618,7 @@ export namespace HostItemResolvers {
|
|||
Context
|
||||
>;
|
||||
export type FirstSeenResolver<
|
||||
R = string | null,
|
||||
R = Date | null,
|
||||
Parent = HostItem,
|
||||
Context = SecOpsContext
|
||||
> = Resolver<R, Parent, Context>;
|
||||
|
@ -1622,7 +1628,7 @@ export namespace HostItemResolvers {
|
|||
Context = SecOpsContext
|
||||
> = Resolver<R, Parent, Context>;
|
||||
export type LastBeatResolver<
|
||||
R = string | null,
|
||||
R = Date | null,
|
||||
Parent = HostItem,
|
||||
Context = SecOpsContext
|
||||
> = Resolver<R, Parent, Context>;
|
||||
|
@ -1677,7 +1683,7 @@ export namespace NetworkTopNFlowItemResolvers {
|
|||
export interface Resolvers<Context = SecOpsContext, TypeParent = NetworkTopNFlowItem> {
|
||||
_id?: IdResolver<string | null, TypeParent, Context>;
|
||||
|
||||
timestamp?: TimestampResolver<string | null, TypeParent, Context>;
|
||||
timestamp?: TimestampResolver<Date | null, TypeParent, Context>;
|
||||
|
||||
source?: SourceResolver<TopNFlowItem | null, TypeParent, Context>;
|
||||
|
||||
|
@ -1696,7 +1702,7 @@ export namespace NetworkTopNFlowItemResolvers {
|
|||
Context = SecOpsContext
|
||||
> = Resolver<R, Parent, Context>;
|
||||
export type TimestampResolver<
|
||||
R = string | null,
|
||||
R = Date | null,
|
||||
Parent = NetworkTopNFlowItem,
|
||||
Context = SecOpsContext
|
||||
> = Resolver<R, Parent, Context>;
|
||||
|
|
|
@ -11,6 +11,7 @@ import { rootSchema } from '../../../common/graphql/root/schema.gql';
|
|||
import { sharedSchema } from '../../../common/graphql/shared';
|
||||
import { Logger } from '../../utils/logger';
|
||||
import { ecsSchema } from '../ecs';
|
||||
import { dateSchema } from '../scalar_date';
|
||||
import { sourceStatusSchema } from '../source_status/schema.gql';
|
||||
import { sourcesSchema } from '../sources/schema.gql';
|
||||
import { uncommonProcessesSchema } from './schema.gql';
|
||||
|
@ -94,6 +95,7 @@ describe('Test Source Schema', () => {
|
|||
sourceStatusSchema,
|
||||
ecsSchema,
|
||||
uncommonProcessesSchema,
|
||||
dateSchema,
|
||||
];
|
||||
const mockSchema = makeExecutableSchema({ typeDefs });
|
||||
|
||||
|
|
|
@ -11,6 +11,7 @@ import { createAuthenticationsResolvers } from './graphql/authentications';
|
|||
import { createEventsResolvers } from './graphql/events';
|
||||
import { createHostsResolvers } from './graphql/hosts';
|
||||
import { createNetworkTopNFlowResolvers } from './graphql/network_top_n_flow';
|
||||
import { createScalarDateResolvers } from './graphql/scalar_date';
|
||||
import { createSourceStatusResolvers } from './graphql/source_status';
|
||||
import { createSourcesResolvers } from './graphql/sources';
|
||||
import { createUncommonProcessesResolvers } from './graphql/uncommon_processes';
|
||||
|
@ -27,14 +28,14 @@ export const initServer = (libs: AppBackendLibs, config: Config) => {
|
|||
const { logger, mocking } = config;
|
||||
const schema = makeExecutableSchema({
|
||||
resolvers: [
|
||||
createUncommonProcessesResolvers(libs) as IResolvers,
|
||||
createSourceStatusResolvers(libs) as IResolvers,
|
||||
createSourcesResolvers(libs) as IResolvers,
|
||||
createAuthenticationsResolvers(libs) as IResolvers,
|
||||
createEventsResolvers(libs) as IResolvers,
|
||||
createHostsResolvers(libs) as IResolvers,
|
||||
createSourcesResolvers(libs) as IResolvers,
|
||||
createNetworkTopNFlowResolvers(libs) as IResolvers,
|
||||
createScalarDateResolvers() as IResolvers,
|
||||
createSourcesResolvers(libs) as IResolvers,
|
||||
createSourceStatusResolvers(libs) as IResolvers,
|
||||
createUncommonProcessesResolvers(libs) as IResolvers,
|
||||
createWhoAmIResolvers() as IResolvers,
|
||||
],
|
||||
typeDefs: schemas,
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue