[Discover] Remove Angular (#109774)

- This PR removes a remaining Angular related code in Discover
This commit is contained in:
Matthias Wilhelm 2021-09-02 14:49:14 +02:00 committed by GitHub
parent 51b0f5aa15
commit 814cf7a4fc
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
91 changed files with 141 additions and 901 deletions

View file

@ -1,2 +0,0 @@
@import 'directives/index';
@import 'context/index';

View file

@ -1,95 +0,0 @@
# Discover Context App Implementation Notes
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 in response to 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**: Exports the actions, reducers and selectors related to the
query status and results.
**query_parameters**: Exports the actions, reducers and selectors related to
the parameters used to construct the query.
**components/action_bar**: Defines the `<context-action-bar>`
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()`, `fetchSuccessors()`, `fetchSurroundingDocs()` that
create and execute the queries for the preceeding and succeeding documents.
**api/utils**: Exports various functions used to create and transform
queries.

View file

@ -1,8 +0,0 @@
// Prefix all styles with "cxt" to avoid conflicts.
// Examples
// cxtChart
// cxtChart__legend
// cxtChart__legend--small
// cxtChart__legend-isLoading
@import 'components/action_bar/index';

View file

@ -1,15 +0,0 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License
* 2.0 and the Server Side Public License, v 1; you may not use this file except
* in compliance with, at your election, the Elastic License 2.0 or the Server
* Side Public License, v 1.
*/
import { getAngularModule } from '../../../../../kibana_services';
import { ActionBar } from './action_bar';
// eslint-disable-next-line @typescript-eslint/no-explicit-any
getAngularModule().directive('contextActionBar', function (reactDirective: any) {
return reactDirective(ActionBar);
});

View file

@ -1,9 +0,0 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License
* 2.0 and the Server Side Public License, v 1; you may not use this file except
* in compliance with, at your election, the Elastic License 2.0 or the Server
* Side Public License, v 1.
*/
import './action_bar_directive';

View file

@ -1,52 +0,0 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License
* 2.0 and the Server Side Public License, v 1; you may not use this file except
* in compliance with, at your election, the Elastic License 2.0 or the Server
* Side Public License, v 1.
*/
/**
* WHAT NEEDS THIS WORKAROUND?
* ===========================
* Any directive that meets all of the following criteria:
* - uses isolate scope bindings
* - sets `bindToController: true`
* - synchronously accesses the bound values in the controller constructor
*
*
*
* HOW DO I GET RID OF IT?
* =======================
* The quick band-aid solution:
* Wrap your constructor logic so it doesn't access bound values
* synchronously. This can have subtle bugs which is why I didn't
* just wrap all of the offenders in $timeout() and made this
* workaround instead.
*
* The more complete solution:
* Use the new component lifecycle methods, like `$onInit()`, to access
* bindings immediately after the constructor is called, which shouldn't
* have any observable effect outside of the constructor.
*
* NOTE: `$onInit()` is not dependency injected, if you need controller specific
* dependencies like `$scope` then you're probably using watchers and should
* take a look at the new one-way data flow facilities available to
* directives/components:
*
* https://docs.angularjs.org/guide/component#component-based-application-architecture
*
*/
export function callAfterBindingsWorkaround(constructor) {
return function InitAfterBindingsWrapper($injector, $attrs, $element, $scope, $transclude) {
this.$onInit = () => {
$injector.invoke(constructor, this, {
$attrs,
$element,
$scope,
$transclude,
});
};
};
}

View file

@ -1,4 +0,0 @@
.dscHistogram__header--partial {
font-weight: $euiFontWeightRegular;
min-width: $euiSize * 12;
}

View file

@ -1 +0,0 @@
@import 'histogram';

View file

@ -1,39 +0,0 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License
* 2.0 and the Server Side Public License, v 1; you may not use this file except
* in compliance with, at your election, the Elastic License 2.0 or the Server
* Side Public License, v 1.
*/
import { getAngularModule, getServices } from '../../kibana_services';
const services = getServices();
const { history: getHistory } = getServices();
const app = getAngularModule();
app.directive('discoverApp', function () {
return {
restrict: 'E',
controllerAs: 'discoverApp',
controller: discoverController,
};
});
function discoverController(_, $scope) {
const history = getHistory();
$scope.opts = {
history,
services,
navigateTo: (path) => {
$scope.$evalAsync(() => {
history.push(path);
});
},
};
$scope.$on('$destroy', () => {});
}

View file

@ -1,11 +0,0 @@
<discover-app>
<discover
angularRoute="angularRoute"
index-pattern="indexPattern"
opts="opts"
reset-query="resetQuery"
search-source="volatileSearchSource"
set-index-pattern="setIndexPattern"
>
</discover>
</discover-app>

View file

@ -1,7 +0,0 @@
<discover-app>
<discover
index-pattern="indexPattern"
opts="opts"
>
</discover>
</discover-app>

View file

@ -1,38 +0,0 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License
* 2.0 and the Server Side Public License, v 1; you may not use this file except
* in compliance with, at your election, the Elastic License 2.0 or the Server
* Side Public License, v 1.
*/
import React from 'react';
import { DocViewer } from '../components/doc_viewer/doc_viewer';
// eslint-disable-next-line @typescript-eslint/no-explicit-any
export function createDocViewerDirective(reactDirective: any) {
return reactDirective(
(props: React.ComponentProps<typeof DocViewer>) => {
return <DocViewer {...props} />;
},
[
'hit',
['indexPattern', { watchDepth: 'reference' }],
['filter', { watchDepth: 'reference' }],
['columns', { watchDepth: 'collection' }],
['onAddColumn', { watchDepth: 'reference' }],
['onRemoveColumn', { watchDepth: 'reference' }],
],
{
restrict: 'E',
scope: {
hit: '=',
indexPattern: '=',
filter: '=?',
columns: '=?',
onAddColumn: '=?',
onRemoveColumn: '=?',
},
}
);
}

View file

@ -1,142 +0,0 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License
* 2.0 and the Server Side Public License, v 1; you may not use this file except
* in compliance with, at your election, the Elastic License 2.0 or the Server
* Side Public License, v 1.
*/
// inner angular imports
// these are necessary to bootstrap the local angular.
// They can stay even after NP cutover
import '../index.scss';
import angular from 'angular';
// required for `ngSanitize` angular module
import 'angular-sanitize';
import { EuiIcon } from '@elastic/eui';
import { i18nDirective, i18nFilter, I18nProvider } from '@kbn/i18n/angular';
import { CoreStart, PluginInitializerContext } from 'kibana/public';
import { DataPublicPluginStart } from 'src/plugins/data/public';
import { Storage } from '../../../../kibana_utils/public';
import { NavigationPublicPluginStart as NavigationStart } from '../../../../navigation/public';
import { createContextAppLegacy } from '../components/context_app/context_app_legacy_directive';
import { createDiscoverGridDirective } from './create_discover_grid_directive';
import {
configureAppAngularModule,
PrivateProvider,
registerListenEventListener,
watchMultiDecorator,
} from '../../../../kibana_legacy/public';
import { PromiseServiceCreator } from './helpers';
import { DiscoverStartPlugins } from '../../plugin';
import { getScopedHistory } from '../../kibana_services';
/**
* returns the main inner angular module, it contains all the parts of Angular Discover
* needs to render, so in the end the current 'kibana' angular module is no longer necessary
*/
export function getInnerAngularModule(
name: string,
core: CoreStart,
deps: DiscoverStartPlugins,
context: PluginInitializerContext
) {
const module = initializeInnerAngularModule(name, core, deps.navigation, deps.data);
configureAppAngularModule(module, { core, env: context.env }, true, getScopedHistory);
return module;
}
/**
* returns a slimmer inner angular module for embeddable rendering
*/
export function getInnerAngularModuleEmbeddable(
name: string,
core: CoreStart,
deps: DiscoverStartPlugins
) {
return initializeInnerAngularModule(name, core, deps.navigation, deps.data, true);
}
let initialized = false;
export function initializeInnerAngularModule(
name = 'app/discover',
core: CoreStart,
navigation: NavigationStart,
data: DataPublicPluginStart,
embeddable = false
) {
if (!initialized) {
createLocalI18nModule();
createLocalPrivateModule();
createLocalPromiseModule();
createLocalStorageModule();
createDocTableModule();
initialized = true;
}
if (embeddable) {
return angular
.module(name, [
'ngSanitize',
'react',
'ui.bootstrap',
'discoverI18n',
'discoverPrivate',
'discoverDocTable',
'discoverPromise',
])
.config(watchMultiDecorator)
.directive('icon', (reactDirective) => reactDirective(EuiIcon));
}
return angular
.module(name, [
'ngSanitize',
'react',
'ui.bootstrap',
'discoverI18n',
'discoverPrivate',
'discoverPromise',
'discoverLocalStorageProvider',
'discoverDocTable',
])
.config(watchMultiDecorator)
.run(registerListenEventListener);
}
function createLocalPromiseModule() {
angular.module('discoverPromise', []).service('Promise', PromiseServiceCreator);
}
function createLocalPrivateModule() {
angular.module('discoverPrivate', []).provider('Private', PrivateProvider);
}
function createLocalI18nModule() {
angular
.module('discoverI18n', [])
.provider('i18n', I18nProvider)
.filter('i18n', i18nFilter)
.directive('i18nId', i18nDirective);
}
function createLocalStorageModule() {
angular
.module('discoverLocalStorageProvider', ['discoverPrivate'])
.service('localStorage', createLocalStorageService('localStorage'))
.service('sessionStorage', createLocalStorageService('sessionStorage'));
}
const createLocalStorageService = function (type: string) {
return function ($window: ng.IWindowService) {
return new Storage($window[type]);
};
};
function createDocTableModule() {
angular
.module('discoverDocTable', ['react'])
.directive('discoverGrid', createDiscoverGridDirective)
.directive('contextAppLegacy', createContextAppLegacy);
}

View file

@ -1,10 +0,0 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License
* 2.0 and the Server Side Public License, v 1; you may not use this file except
* in compliance with, at your election, the Elastic License 2.0 or the Server
* Side Public License, v 1.
*/
export { handleSourceColumnState } from './state_helpers';
export { PromiseServiceCreator } from './promises';

View file

@ -1,9 +0,0 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License
* 2.0 and the Server Side Public License, v 1; you may not use this file except
* in compliance with, at your election, the Elastic License 2.0 or the Server
* Side Public License, v 1.
*/
export function PromiseServiceCreator($q: unknown, $timeout: unknown): (fn: unknown) => unknown;

View file

@ -1,118 +0,0 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License
* 2.0 and the Server Side Public License, v 1; you may not use this file except
* in compliance with, at your election, the Elastic License 2.0 or the Server
* Side Public License, v 1.
*/
import _ from 'lodash';
export function PromiseServiceCreator($q, $timeout) {
function Promise(fn) {
if (typeof this === 'undefined')
throw new Error('Promise constructor must be called with "new"');
const defer = $q.defer();
try {
fn(defer.resolve, defer.reject);
} catch (e) {
defer.reject(e);
}
return defer.promise;
}
Promise.all = Promise.props = $q.all;
Promise.resolve = function (val) {
const defer = $q.defer();
defer.resolve(val);
return defer.promise;
};
Promise.reject = function (reason) {
const defer = $q.defer();
defer.reject(reason);
return defer.promise;
};
Promise.cast = $q.when;
Promise.delay = function (ms) {
return $timeout(_.noop, ms);
};
Promise.method = function (fn) {
return function () {
const args = Array.prototype.slice.call(arguments);
return Promise.try(fn, args, this);
};
};
Promise.nodeify = function (promise, cb) {
promise.then(function (val) {
cb(void 0, val);
}, cb);
};
Promise.map = function (arr, fn) {
return Promise.all(
arr.map(function (i, el, list) {
return Promise.try(fn, [i, el, list]);
})
);
};
Promise.each = function (arr, fn) {
const queue = arr.slice(0);
let i = 0;
return (function next() {
if (!queue.length) return arr;
return Promise.try(fn, [arr.shift(), i++]).then(next);
})();
};
Promise.is = function (obj) {
// $q doesn't create instances of any constructor, promises are just objects with a then function
// https://github.com/angular/angular.js/blob/58f5da86645990ef984353418cd1ed83213b111e/src/ng/q.js#L335
return obj && typeof obj.then === 'function';
};
Promise.halt = _.once(function () {
const promise = new Promise(() => {});
promise.then = _.constant(promise);
promise.catch = _.constant(promise);
return promise;
});
Promise.try = function (fn, args, ctx) {
if (typeof fn !== 'function') {
return Promise.reject(new TypeError('fn must be a function'));
}
let value;
if (Array.isArray(args)) {
try {
value = fn.apply(ctx, args);
} catch (e) {
return Promise.reject(e);
}
} else {
try {
value = fn.call(ctx, args);
} catch (e) {
return Promise.reject(e);
}
}
return Promise.resolve(value);
};
Promise.fromNode = function (takesCbFn) {
return new Promise(function (resolve, reject) {
takesCbFn(function (err, ...results) {
if (err) reject(err);
else if (results.length > 1) resolve(results);
else resolve(results[0]);
});
});
};
Promise.race = function (iterable) {
return new Promise((resolve, reject) => {
for (const i of iterable) {
Promise.resolve(i).then(resolve, reject);
}
});
};
return Promise;
}

View file

@ -1,13 +0,0 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License
* 2.0 and the Server Side Public License, v 1; you may not use this file except
* in compliance with, at your election, the Elastic License 2.0 or the Server
* Side Public License, v 1.
*/
// required for i18nIdDirective
import 'angular-sanitize';
import './doc';
import './context';

View file

@ -6,11 +6,10 @@
* Side Public License, v 1.
*/
import './index.scss';
import { renderApp as renderReactApp } from './index';
/**
* Here's where Discover's inner angular is mounted and rendered
* Here's where Discover is mounted and rendered
*/
export async function renderApp(moduleName: string, element: HTMLElement) {
const app = mountDiscoverApp(moduleName, element);

View file

@ -10,11 +10,8 @@ import React from 'react';
import { mountWithIntl } from '@kbn/test/jest';
import { ActionBar, ActionBarProps } from './action_bar';
import { findTestSubject } from '@elastic/eui/lib/test';
import {
MAX_CONTEXT_SIZE,
MIN_CONTEXT_SIZE,
} from '../../../../components/context_app/utils/constants';
import { SurrDocType } from '../../api/context';
import { MAX_CONTEXT_SIZE, MIN_CONTEXT_SIZE } from '../../utils/constants';
import { SurrDocType } from '../../services/context';
describe('Test Discover Context ActionBar for successor | predecessor records', () => {
[SurrDocType.SUCCESSORS, SurrDocType.PREDECESSORS].forEach((type) => {

View file

@ -6,6 +6,7 @@
* Side Public License, v 1.
*/
import './_action_bar.scss';
import React, { useState, useEffect } from 'react';
import { i18n } from '@kbn/i18n';
import { FormattedMessage, I18nProvider } from '@kbn/i18n/react';
@ -18,11 +19,8 @@ import {
EuiSpacer,
} from '@elastic/eui';
import { ActionBarWarning } from './action_bar_warning';
import { SurrDocType } from '../../api/context';
import {
MAX_CONTEXT_SIZE,
MIN_CONTEXT_SIZE,
} from '../../../../components/context_app/utils/constants';
import { SurrDocType } from '../../services/context';
import { MAX_CONTEXT_SIZE, MIN_CONTEXT_SIZE } from '../../utils/constants';
export interface ActionBarProps {
/**

View file

@ -9,7 +9,7 @@
import React from 'react';
import { FormattedMessage } from '@kbn/i18n/react';
import { EuiCallOut } from '@elastic/eui';
import { SurrDocType } from '../../api/context';
import { SurrDocType } from '../../services/context';
export function ActionBarWarning({ docCount, type }: { docCount: number; type: SurrDocType }) {
if (type === SurrDocType.PREDECESSORS) {

View file

@ -10,7 +10,7 @@ import React from 'react';
import { mountWithIntl } from '@kbn/test/jest';
import { ReactWrapper } from 'enzyme';
import { ContextErrorMessage } from './context_error_message';
import { FailureReason, LoadingStatus } from '../../angular/context_query_state';
import { FailureReason, LoadingStatus } from '../../services/context_query_state';
import { findTestSubject } from '@elastic/eui/lib/test';
describe('loading spinner', function () {

View file

@ -13,7 +13,7 @@ import {
FailureReason,
LoadingStatus,
LoadingStatusEntry,
} from '../../angular/context_query_state';
} from '../../services/context_query_state';
export interface ContextErrorMessageProps {
/**

View file

@ -1,4 +1,4 @@
@import '../../../../../../core/public/mixins';
@import 'src/core/public/mixins';
.dscDocsPage {
@include kibanaFullBodyHeight(54px); // action bar height

View file

@ -14,17 +14,17 @@ import { EuiText, EuiPageContent, EuiPage, EuiSpacer } from '@elastic/eui';
import { cloneDeep } from 'lodash';
import { esFilters, SortDirection } from '../../../../../data/public';
import { DOC_TABLE_LEGACY, SEARCH_FIELDS_FROM_SOURCE } from '../../../../common';
import { ContextErrorMessage } from '../context_error_message';
import { ContextErrorMessage } from './components/context_error_message';
import { IndexPattern, IndexPatternField } from '../../../../../data/common';
import { LoadingStatus } from '../../angular/context_query_state';
import { LoadingStatus } from './services/context_query_state';
import { getServices } from '../../../kibana_services';
import { AppState, isEqualFilters } from '../../angular/context_state';
import { AppState, isEqualFilters } from './services/context_state';
import { useDataGridColumns } from '../../helpers/use_data_grid_columns';
import { useContextAppState } from './use_context_app_state';
import { useContextAppFetch } from './use_context_app_fetch';
import { useContextAppState } from './utils/use_context_app_state';
import { useContextAppFetch } from './utils/use_context_app_fetch';
import { popularizeField } from '../../helpers/popularize_field';
import { ContextAppContent } from './context_app_content';
import { SurrDocType } from '../../angular/context/api/context';
import { SurrDocType } from './services/context';
import { DocViewFilterFn } from '../../doc_views/doc_views_types';
const ContextAppContentMemoized = memo(ContextAppContent);

View file

@ -9,17 +9,17 @@
import React from 'react';
import { mountWithIntl } from '@kbn/test/jest';
import { findTestSubject } from '@elastic/eui/lib/test';
import { ActionBar } from '../../angular/context/components/action_bar/action_bar';
import { AppState, GetStateReturn } from '../../angular/context_state';
import { ActionBar } from './components/action_bar/action_bar';
import { AppState, GetStateReturn } from './services/context_state';
import { SortDirection } from 'src/plugins/data/common';
import { EsHitRecordList } from '../../angular/context/api/context';
import { ContextAppContent, ContextAppContentProps } from './context_app_content';
import { getServices, setServices } from '../../../kibana_services';
import { LoadingStatus } from '../../angular/context_query_state';
import { LoadingStatus } from './services/context_query_state';
import { indexPatternMock } from '../../../__mocks__/index_pattern';
import { DiscoverGrid } from '../discover_grid/discover_grid';
import { DiscoverGrid } from '../../components/discover_grid/discover_grid';
import { discoverServiceMock } from '../../../__mocks__/services';
import { DocTableWrapper } from '../../apps/main/components/doc_table/doc_table_wrapper';
import { DocTableWrapper } from '../main/components/doc_table/doc_table_wrapper';
import { EsHitRecordList } from '../../types';
describe('ContextAppContent test', () => {
let hit;

View file

@ -12,15 +12,16 @@ import { EuiHorizontalRule, EuiText } from '@elastic/eui';
import { CONTEXT_STEP_SETTING, DOC_HIDE_TIME_COLUMN_SETTING } from '../../../../common';
import { IndexPattern } from '../../../../../data/common';
import { SortDirection } from '../../../../../data/public';
import { LoadingStatus } from '../../angular/context_query_state';
import { ActionBar } from '../../angular/context/components/action_bar/action_bar';
import { DiscoverGrid } from '../discover_grid/discover_grid';
import { LoadingStatus } from './services/context_query_state';
import { ActionBar } from './components/action_bar/action_bar';
import { DiscoverGrid } from '../../components/discover_grid/discover_grid';
import { DocViewFilterFn, ElasticSearchHit } from '../../doc_views/doc_views_types';
import { AppState } from '../../angular/context_state';
import { EsHitRecordList, SurrDocType } from '../../angular/context/api/context';
import { AppState } from './services/context_state';
import { SurrDocType } from './services/context';
import { DiscoverServices } from '../../../build_services';
import { MAX_CONTEXT_SIZE, MIN_CONTEXT_SIZE } from './utils/constants';
import { DocTableContext } from '../../apps/main/components/doc_table/doc_table_context';
import { DocTableContext } from '../main/components/doc_table/doc_table_context';
import { EsHitRecordList } from '../../types';
export interface ContextAppContentProps {
columns: string[];

View file

@ -9,7 +9,7 @@ import React, { useEffect } from 'react';
import { useParams } from 'react-router-dom';
import { i18n } from '@kbn/i18n';
import { DiscoverServices } from '../../../build_services';
import { ContextApp } from '../../components/context_app/context_app';
import { ContextApp } from './context_app';
import { getRootBreadcrumbs } from '../../helpers/breadcrumbs';
import { LoadingIndicator } from '../../components/common/loading_indicator';
import { useIndexPattern } from '../../helpers/use_index_pattern';

View file

@ -10,7 +10,7 @@ import sinon from 'sinon';
import moment from 'moment';
import { IndexPatternsContract } from '../../../../../../data/public';
import { EsHitRecordList } from './context';
import { EsHitRecordList } from '../../../types';
type SortHit = {
[key in string]: number; // timeField name

View file

@ -9,9 +9,9 @@
import { EsQuerySortValue, SortDirection } from '../../../../../../data/public';
import { createIndexPatternsStub, createSearchSourceStub } from './_stubs';
import { fetchAnchorProvider, updateSearchSource } from './anchor';
import { EsHitRecord, EsHitRecordList } from './context';
import { indexPatternMock } from '../../../../__mocks__/index_pattern';
import { savedSearchMock } from '../../../../__mocks__/saved_search';
import { EsHitRecord, EsHitRecordList } from '../../../types';
describe('context app', function () {
let fetchAnchor: (

View file

@ -15,7 +15,7 @@ import {
EsQuerySortValue,
IndexPattern,
} from '../../../../../../data/public';
import { EsHitRecord } from './context';
import { EsHitRecord } from '../../../types';
export function fetchAnchorProvider(
indexPatterns: IndexPatternsContract,

View file

@ -9,11 +9,11 @@
import moment from 'moment';
import { get, last } from 'lodash';
import { createIndexPatternsStub, createContextSearchSourceStub } from './_stubs';
import { EsHitRecordList, fetchContextProvider, SurrDocType } from './context';
import { fetchContextProvider, SurrDocType } from './context';
import { setServices, SortDirection } from '../../../../kibana_services';
import { EsHitRecord } from './context';
import { Query } from '../../../../../../data/public';
import { DiscoverServices } from '../../../../build_services';
import { EsHitRecord, EsHitRecordList } from '../../../types';
const MS_PER_DAY = 24 * 60 * 60 * 1000;
const ANCHOR_TIMESTAMP = new Date(MS_PER_DAY).toJSON();

View file

@ -12,9 +12,9 @@ import { get, last } from 'lodash';
import { createIndexPatternsStub, createContextSearchSourceStub } from './_stubs';
import { setServices, SortDirection } from '../../../../kibana_services';
import { Query } from '../../../../../../data/public';
import { EsHitRecordList, fetchContextProvider, SurrDocType } from './context';
import { EsHitRecord } from './context';
import { fetchContextProvider, SurrDocType } from './context';
import { DiscoverServices } from '../../../../build_services';
import { EsHitRecord, EsHitRecordList } from '../../../types';
const MS_PER_DAY = 24 * 60 * 60 * 1000;
const ANCHOR_TIMESTAMP = new Date(MS_PER_DAY).toJSON();

View file

@ -5,32 +5,21 @@
* in compliance with, at your election, the Elastic License 2.0 or the Server
* Side Public License, v 1.
*/
import type { estypes } from '@elastic/elasticsearch';
import { Filter, IndexPatternsContract, IndexPattern, SearchSource } from 'src/plugins/data/public';
import { Filter, IndexPattern, IndexPatternsContract, SearchSource } from 'src/plugins/data/public';
import { reverseSortDir, SortDirection } from './utils/sorting';
import { extractNanos, convertIsoToMillis } from './utils/date_conversion';
import { convertIsoToMillis, extractNanos } from './utils/date_conversion';
import { fetchHitsInInterval } from './utils/fetch_hits_in_interval';
import { generateIntervals } from './utils/generate_intervals';
import { getEsQuerySearchAfter } from './utils/get_es_query_search_after';
import { getEsQuerySort } from './utils/get_es_query_sort';
import { getServices } from '../../../../kibana_services';
import { EsHitRecord, EsHitRecordList } from '../../../types';
export enum SurrDocType {
SUCCESSORS = 'successors',
PREDECESSORS = 'predecessors',
}
export type EsHitRecord = Required<
Pick<estypes.SearchHit, '_id' | 'fields' | 'sort' | '_index' | '_version'>
> & {
_source?: Record<string, unknown>;
_score?: number;
isAnchor?: boolean;
};
export type EsHitRecordList = EsHitRecord[];
const DAY_MILLIS = 24 * 60 * 60 * 1000;
// look from 1 day up to 10000 days into the past and future

View file

@ -6,8 +6,7 @@
* Side Public License, v 1.
*/
import { EsHitRecord } from './context/api/context';
import { EsHitRecordList } from './context/api/context';
import { EsHitRecord, EsHitRecordList } from '../../../types';
export interface ContextFetchState {
/**

View file

@ -9,9 +9,9 @@
import { IUiSettingsClient } from 'kibana/public';
import { getState } from './context_state';
import { createBrowserHistory, History } from 'history';
import { FilterManager, Filter } from '../../../../data/public';
import { coreMock } from '../../../../../core/public/mocks';
import { SEARCH_FIELDS_FROM_SOURCE } from '../../../common';
import { FilterManager, Filter } from '../../../../../../data/public';
import { coreMock } from '../../../../../../../core/public/mocks';
import { SEARCH_FIELDS_FROM_SOURCE } from '../../../../../common';
const setupMock = coreMock.createSetup();

View file

@ -15,9 +15,9 @@ import {
syncStates,
withNotifyOnErrors,
ReduxLikeStateContainer,
} from '../../../../kibana_utils/public';
import { esFilters, FilterManager, Filter, SortDirection } from '../../../../data/public';
import { handleSourceColumnState } from './helpers';
} from '../../../../../../kibana_utils/public';
import { esFilters, FilterManager, Filter, SortDirection } from '../../../../../../data/public';
import { handleSourceColumnState } from '../../../helpers/state_helpers';
export interface AppState {
/**

View file

@ -8,9 +8,9 @@
import { ISearchSource, EsQuerySortValue, SortDirection } from '../../../../../../../data/public';
import { convertTimeValueToIso } from './date_conversion';
import { EsHitRecordList, EsHitRecord } from '../context';
import { IntervalValue } from './generate_intervals';
import { EsQuerySearchAfter } from './get_es_query_search_after';
import { EsHitRecord, EsHitRecordList } from '../../../../types';
interface RangeQuery {
format: string;

View file

@ -6,7 +6,8 @@
* Side Public License, v 1.
*/
import { SurrDocType, EsHitRecordList, EsHitRecord } from '../context';
import { SurrDocType } from '../context';
import { EsHitRecord, EsHitRecordList } from '../../../../types';
export type EsQuerySearchAfter = [string | number, string | number];

View file

@ -7,25 +7,25 @@
*/
import { act, renderHook } from '@testing-library/react-hooks';
import { setServices, getServices } from '../../../kibana_services';
import { SortDirection } from '../../../../../data/public';
import { createFilterManagerMock } from '../../../../../data/public/query/filter_manager/filter_manager.mock';
import { CONTEXT_TIE_BREAKER_FIELDS_SETTING } from '../../../../common';
import { DiscoverServices } from '../../../build_services';
import { indexPatternMock } from '../../../__mocks__/index_pattern';
import { indexPatternsMock } from '../../../__mocks__/index_patterns';
import { FailureReason, LoadingStatus } from '../../angular/context_query_state';
import { setServices, getServices } from '../../../../kibana_services';
import { SortDirection } from '../../../../../../data/public';
import { createFilterManagerMock } from '../../../../../../data/public/query/filter_manager/filter_manager.mock';
import { CONTEXT_TIE_BREAKER_FIELDS_SETTING } from '../../../../../common';
import { DiscoverServices } from '../../../../build_services';
import { indexPatternMock } from '../../../../__mocks__/index_pattern';
import { indexPatternsMock } from '../../../../__mocks__/index_patterns';
import { FailureReason, LoadingStatus } from '../services/context_query_state';
import { ContextAppFetchProps, useContextAppFetch } from './use_context_app_fetch';
import {
mockAnchorHit,
mockPredecessorHits,
mockSuccessorHits,
} from './__mocks__/use_context_app_fetch';
} from '../__mocks__/use_context_app_fetch';
const mockFilterManager = createFilterManagerMock();
jest.mock('../../angular/context/api/context', () => {
const originalModule = jest.requireActual('../../angular/context/api/context');
jest.mock('../services/context', () => {
const originalModule = jest.requireActual('../services/context');
return {
...originalModule,
fetchContextProvider: () => ({
@ -39,7 +39,7 @@ jest.mock('../../angular/context/api/context', () => {
};
});
jest.mock('../../angular/context/api/anchor', () => ({
jest.mock('../services/anchor', () => ({
fetchAnchorProvider: () => (indexPatternId: string) => {
if (!indexPatternId) {
throw new Error();

View file

@ -8,20 +8,21 @@
import React, { useCallback, useMemo, useState } from 'react';
import { i18n } from '@kbn/i18n';
import { fromPairs } from 'lodash';
import { CONTEXT_TIE_BREAKER_FIELDS_SETTING } from '../../../../common';
import { DiscoverServices } from '../../../build_services';
import { fetchAnchorProvider } from '../../angular/context/api/anchor';
import { EsHitRecord, fetchContextProvider, SurrDocType } from '../../angular/context/api/context';
import { MarkdownSimple, toMountPoint } from '../../../../../kibana_react/public';
import { IndexPattern, SortDirection } from '../../../../../data/public';
import { CONTEXT_TIE_BREAKER_FIELDS_SETTING } from '../../../../../common';
import { DiscoverServices } from '../../../../build_services';
import { fetchAnchorProvider } from '../services/anchor';
import { fetchContextProvider, SurrDocType } from '../services/context';
import { MarkdownSimple, toMountPoint } from '../../../../../../kibana_react/public';
import { IndexPattern, SortDirection } from '../../../../../../data/public';
import {
ContextFetchState,
FailureReason,
getInitialContextQueryState,
LoadingStatus,
} from '../../angular/context_query_state';
import { AppState } from '../../angular/context_state';
import { getFirstSortableField } from '../../angular/context/api/utils/sorting';
} from '../services/context_query_state';
import { AppState } from '../services/context_state';
import { getFirstSortableField } from '../services/utils/sorting';
import { EsHitRecord } from '../../../types';
const createError = (statusKey: string, reason: FailureReason, error?: Error) => ({
[statusKey]: { value: LoadingStatus.FAILED, error, reason },

View file

@ -8,10 +8,10 @@
import { useEffect, useMemo, useState } from 'react';
import { cloneDeep } from 'lodash';
import { CONTEXT_DEFAULT_SIZE_SETTING } from '../../../../common';
import { IndexPattern } from '../../../../../data/public';
import { DiscoverServices } from '../../../build_services';
import { AppState, getState } from '../../angular/context_state';
import { CONTEXT_DEFAULT_SIZE_SETTING } from '../../../../../common';
import { IndexPattern } from '../../../../../../data/public';
import { DiscoverServices } from '../../../../build_services';
import { AppState, getState } from '../services/context_state';
export function useContextAppState({
indexPattern,

View file

@ -13,11 +13,11 @@ import { mountWithIntl } from '@kbn/test/jest';
import { ReactWrapper } from 'enzyme';
import { findTestSubject } from '@elastic/eui/lib/test';
import { Doc, DocProps } from './doc';
import { SEARCH_FIELDS_FROM_SOURCE as mockSearchFieldsFromSource } from '../../../../common';
import { SEARCH_FIELDS_FROM_SOURCE as mockSearchFieldsFromSource } from '../../../../../common';
const mockSearchApi = jest.fn();
jest.mock('../../../kibana_services', () => {
jest.mock('../../../../kibana_services', () => {
// eslint-disable-next-line @typescript-eslint/no-explicit-any
let registry: any[] = [];

View file

@ -10,10 +10,10 @@ import React from 'react';
import { FormattedMessage, I18nProvider } from '@kbn/i18n/react';
import { EuiCallOut, EuiLink, EuiLoadingSpinner, EuiPageContent, EuiPage } from '@elastic/eui';
import { IndexPatternsContract } from 'src/plugins/data/public';
import { useEsDocSearch } from './use_es_doc_search';
import { getServices } from '../../../kibana_services';
import { DocViewer } from '../doc_viewer/doc_viewer';
import { ElasticRequestState } from './elastic_request_state';
import { getServices } from '../../../../kibana_services';
import { DocViewer } from '../../../components/doc_viewer/doc_viewer';
import { ElasticRequestState } from '../types';
import { useEsDocSearch } from '../../../services/use_es_doc_search';
export interface DocProps {
/**

View file

@ -9,7 +9,7 @@ import React, { useEffect } from 'react';
import { useLocation, useParams } from 'react-router-dom';
import { DiscoverServices } from '../../../build_services';
import { getRootBreadcrumbs } from '../../helpers/breadcrumbs';
import { Doc } from '../../components/doc/doc';
import { Doc } from './components/doc';
import { LoadingIndicator } from '../../components/common/loading_indicator';
import { useIndexPattern } from '../../helpers/use_index_pattern';

View file

@ -7,3 +7,7 @@
height: 100%;
width: 100%;
}
.dscHistogram__header--partial {
font-weight: $euiFontWeightRegular;
min-width: $euiSize * 12;
}

View file

@ -7,7 +7,6 @@
*/
import { Capabilities, IUiSettingsClient } from 'kibana/public';
import { SORT_DEFAULT_ORDER_SETTING } from '../../../../../../../common';
import { popularizeField } from '../../../../../../application/helpers/popularize_field';
import {
AppState as DiscoverState,
GetStateReturn as DiscoverGetStateReturn,
@ -15,8 +14,9 @@ import {
import {
AppState as ContextState,
GetStateReturn as ContextGetStateReturn,
} from '../../../../../../application/angular/context_state';
} from '../../../../context/services/context_state';
import { IndexPattern, IndexPatternsContract } from '../../../../../../../../data/public';
import { popularizeField } from '../../../../../helpers/popularize_field';
/**
* Helper function to provide a fallback to a single _source column if the given array of columns

View file

@ -99,13 +99,6 @@ describe('DiscoverFieldSearch', () => {
expect(badge.text()).toEqual('0');
});
test('missing switch appears with new fields api', () => {
const component = mountComponent({ ...defaultProps, useNewFieldsApi: true });
const btn = findTestSubject(component, 'toggleFieldFilterButton');
btn.simulate('click');
expect(findTestSubject(component, 'missingSwitch').exists()).toBeTruthy();
});
test('change in filters triggers onChange', () => {
const onChange = jest.fn();
const component = mountComponent({ ...defaultProps, ...{ onChange } });

View file

@ -53,18 +53,13 @@ export interface Props {
* types for the type filter
*/
types: string[];
/**
* use new fields api
*/
useNewFieldsApi?: boolean;
}
/**
* Component is Discover's side bar to search of available fields
* Additionally there's a button displayed that allows the user to show/hide more filter fields
*/
export function DiscoverFieldSearch({ onChange, value, types, useNewFieldsApi }: Props) {
export function DiscoverFieldSearch({ onChange, value, types }: Props) {
const searchPlaceholder = i18n.translate('discover.fieldChooser.searchPlaceHolder', {
defaultMessage: 'Search field names',
});
@ -92,12 +87,6 @@ export function DiscoverFieldSearch({ onChange, value, types, useNewFieldsApi }:
missing: true,
});
if (typeof value !== 'string') {
// at initial rendering value is undefined (angular related), this catches the warning
// should be removed once all is react
return null;
}
const filterBtnAriaLabel = isPopoverOpen
? i18n.translate('discover.fieldChooser.toggleFieldFilterButtonHideAriaLabel', {
defaultMessage: 'Hide field filter settings',

View file

@ -23,7 +23,7 @@ export interface DiscoverIndexPatternProps {
*/
onChangeIndexPattern: (id: string) => void;
/**
* currently selected index pattern, due to angular issues it's undefined at first rendering
* currently selected index pattern
*/
selectedIndexPattern: IndexPattern;
}

View file

@ -338,7 +338,6 @@ export function DiscoverSidebar({
onChange={onChangeFieldSearch}
value={fieldFilter.name}
types={fieldTypes}
useNewFieldsApi={useNewFieldsApi}
/>
</form>
</EuiFlexItem>

View file

@ -26,7 +26,7 @@ export function SkipBottomButton({ onClick }: SkipBottomButtonProps) {
// prevent the anchor to reload the page on click
event.preventDefault();
// The destinationId prop cannot be leveraged here as the table needs
// to be updated first (angular logic)
// to be updated firsts
onClick();
}}
className="dscSkipButton"

View file

@ -34,7 +34,7 @@ import { migrateLegacyQuery } from '../../../helpers/migrate_legacy_query';
import { DiscoverGridSettings } from '../../../components/discover_grid/types';
import { DISCOVER_APP_URL_GENERATOR, DiscoverUrlGeneratorState } from '../../../../url_generator';
import { SavedSearch } from '../../../../saved_searches';
import { handleSourceColumnState } from '../../../angular/helpers';
import { handleSourceColumnState } from '../../../helpers/state_helpers';
export interface AppState {
/**

View file

@ -1,18 +0,0 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License
* 2.0 and the Server Side Public License, v 1; you may not use this file except
* in compliance with, at your election, the Elastic License 2.0 or the Server
* Side Public License, v 1.
*/
import { ContextApp } from './context_app';
// eslint-disable-next-line @typescript-eslint/no-explicit-any
export function createContextAppLegacy(reactDirective: any) {
return reactDirective(ContextApp, [
['indexPattern', { watchDepth: 'reference' }],
['indexPatternId', { watchDepth: 'reference' }],
['anchorId', { watchDepth: 'reference' }],
]);
}

View file

@ -12,7 +12,7 @@ import themeDark from '@elastic/eui/dist/eui_theme_dark.json';
import themeLight from '@elastic/eui/dist/eui_theme_light.json';
import { i18n } from '@kbn/i18n';
import { DiscoverGridContext } from './discover_grid_context';
import { EsHitRecord } from '../../angular/context/api/context';
import { EsHitRecord } from '../../types';
/**
* Button to expand a given row
*/

View file

@ -21,7 +21,7 @@ import { ElasticSearchHit } from '../../doc_views/doc_views_types';
import { DiscoverGridContext } from './discover_grid_context';
import { JsonCodeEditor } from '../json_code_editor/json_code_editor';
import { defaultMonacoEditorWidth } from './constants';
import { EsHitRecord } from '../../angular/context/api/context';
import { EsHitRecord } from '../../types';
export const getRenderCellValueFn = (
indexPattern: IndexPattern,

View file

@ -15,7 +15,7 @@ interface Props {
}
/**
* Responsible for rendering a tab provided by a render function.
* So any other framework can be used (E.g. legacy Angular 3rd party plugin code)
* Any other framework can be used
* The provided `render` function is called with a reference to the
* component's `HTMLDivElement` as 1st arg and `renderProps` as 2nd arg
*/

View file

@ -61,7 +61,7 @@ export class DocViewerTab extends React.Component<Props, State> {
}
if (render) {
// doc view is provided by a render function, e.g. for legacy Angular code
// doc view is provided by a render function
return <DocViewRenderTab render={render} renderProps={renderProps} />;
}

View file

@ -15,8 +15,6 @@ import { FieldIcon, FieldIconProps } from '../../../../../kibana_react/public';
import { getFieldTypeName } from './field_type_name';
import { IndexPatternField } from '../../../../../data/public';
// properties fieldType and fieldName are provided in kbn_doc_view
// this should be changed when both components are deangularized
interface Props {
fieldName: string;
fieldType?: string;

View file

@ -9,7 +9,7 @@
import React from 'react';
import { mountWithIntl } from '@kbn/test/jest';
import { SourceViewer } from './source_viewer';
import * as hooks from '../doc/use_es_doc_search';
import * as hooks from '../../services/use_es_doc_search';
import * as useUiSettingHook from 'src/plugins/kibana_react/public/ui_settings/use_ui_setting';
import { EuiButton, EuiEmptyPrompt, EuiLoadingSpinner } from '@elastic/eui';
import { JsonCodeEditorCommon } from '../json_code_editor/json_code_editor_common';

View file

@ -12,11 +12,11 @@ import { FormattedMessage } from '@kbn/i18n/react';
import { monaco } from '@kbn/monaco';
import { EuiButton, EuiEmptyPrompt, EuiLoadingSpinner, EuiSpacer, EuiText } from '@elastic/eui';
import { i18n } from '@kbn/i18n';
import { useEsDocSearch } from '../doc/use_es_doc_search';
import { JSONCodeEditorCommonMemoized } from '../json_code_editor/json_code_editor_common';
import { ElasticRequestState } from '../doc/elastic_request_state';
import { getServices } from '../../../../public/kibana_services';
import { getServices } from '../../../kibana_services';
import { SEARCH_FIELDS_FROM_SOURCE } from '../../../../common';
import { ElasticRequestState } from '../../apps/doc/types';
import { useEsDocSearch } from '../../services/use_es_doc_search';
interface SourceViewerProps {
id: string;

View file

@ -38,7 +38,7 @@ import {
SORT_DEFAULT_ORDER_SETTING,
} from '../../../common';
import * as columnActions from '../apps/main/components/doc_table/actions/columns';
import { handleSourceColumnState } from '../angular/helpers';
import { handleSourceColumnState } from '../helpers/state_helpers';
import { DiscoverGridProps } from '../components/discover_grid/discover_grid';
import { DiscoverGridSettings } from '../components/discover_grid/types';
import { DocTableProps } from '../apps/main/components/doc_table/doc_table_wrapper';

View file

@ -8,10 +8,7 @@
import React from 'react';
import {
DiscoverGridEmbeddable,
DiscoverGridEmbeddableProps,
} from '../angular/create_discover_grid_directive';
import { DiscoverGridEmbeddable, DiscoverGridEmbeddableProps } from './saved_search_grid';
import { DiscoverDocTableEmbeddable } from '../apps/main/components/doc_table/create_doc_table_embeddable';
import { DocTableEmbeddableProps } from '../apps/main/components/doc_table/doc_table_embeddable';
import { SearchProps } from './saved_search_embeddable';

View file

@ -44,28 +44,3 @@ export function DiscoverGridEmbeddable(props: DiscoverGridEmbeddableProps) {
</I18nProvider>
);
}
/**
* this is just needed for the embeddable
*/
// eslint-disable-next-line @typescript-eslint/no-explicit-any
export function createDiscoverGridDirective(reactDirective: any) {
return reactDirective(DiscoverGridEmbeddable, [
['columns', { watchDepth: 'collection' }],
['indexPattern', { watchDepth: 'reference' }],
['isLoading', { watchDepth: 'reference' }],
['onAddColumn', { watchDepth: 'reference', wrapApply: false }],
['onFilter', { watchDepth: 'reference', wrapApply: false }],
['onRemoveColumn', { watchDepth: 'reference', wrapApply: false }],
['onSetColumns', { watchDepth: 'reference', wrapApply: false }],
['onSort', { watchDepth: 'reference', wrapApply: false }],
['rows', { watchDepth: 'collection' }],
['sampleSize', { watchDepth: 'reference' }],
['searchDescription', { watchDepth: 'reference' }],
['searchTitle', { watchDepth: 'reference' }],
['settings', { watchDepth: 'reference' }],
['showTimeCol', { watchDepth: 'value' }],
['sort', { watchDepth: 'value' }],
['className', { watchDepth: 'value' }],
]);
}

View file

@ -6,7 +6,6 @@
* Side Public License, v 1.
*/
import { auto } from 'angular';
import { i18n } from '@kbn/i18n';
import { UiActionsStart } from 'src/plugins/ui_actions/public';
import { getServices } from '../../kibana_services';
@ -30,8 +29,6 @@ interface StartServices {
export class SearchEmbeddableFactory
implements EmbeddableFactoryDefinition<SearchInput, SearchOutput, SavedSearchEmbeddable> {
public readonly type = SEARCH_EMBEDDABLE_TYPE;
private $injector: auto.IInjectorService | null;
private getInjector: () => Promise<auto.IInjectorService> | null;
public readonly savedObjectMetaData = {
name: i18n.translate('discover.savedSearch.savedObjectName', {
defaultMessage: 'Saved search',
@ -40,13 +37,7 @@ export class SearchEmbeddableFactory
getIconForSavedObject: () => 'discoverApp',
};
constructor(
private getStartServices: () => Promise<StartServices>,
getInjector: () => Promise<auto.IInjectorService>
) {
this.$injector = null;
this.getInjector = getInjector;
}
constructor(private getStartServices: () => Promise<StartServices>) {}
public canCreateNew() {
return false;
@ -67,10 +58,6 @@ export class SearchEmbeddableFactory
input: Partial<SearchInput> & { id: string; timeRange: TimeRange },
parent?: Container
): Promise<SavedSearchEmbeddable | ErrorEmbeddable> => {
if (!this.$injector) {
this.$injector = await this.getInjector();
}
const filterManager = getServices().filterManager;
const url = await getServices().getSavedSearchUrlById(savedObjectId);

View file

@ -6,9 +6,9 @@
* Side Public License, v 1.
*/
import { IUiSettingsClient } from 'src/core/public';
import { IUiSettingsClient } from 'kibana/public';
import { isEqual } from 'lodash';
import { SEARCH_FIELDS_FROM_SOURCE, DEFAULT_COLUMNS_SETTING } from '../../../../common';
import { SEARCH_FIELDS_FROM_SOURCE, DEFAULT_COLUMNS_SETTING } from '../../../common';
/**
* Makes sure the current state is not referencing the source column when using the fields api

View file

@ -11,7 +11,7 @@ import { useDataGridColumns } from './use_data_grid_columns';
import { indexPatternMock } from '../../__mocks__/index_pattern';
import { configMock } from '../../__mocks__/config';
import { indexPatternsMock } from '../../__mocks__/index_patterns';
import { AppState } from '../angular/context_state';
import { AppState } from '../apps/context/services/context_state';
import { Capabilities } from '../../../../../core/types';
describe('useDataGridColumns', () => {

View file

@ -17,7 +17,7 @@ import {
import {
AppState as ContextState,
GetStateReturn as ContextGetStateReturn,
} from '../angular/context_state';
} from '../apps/context/services/context_state';
import { getStateColumnActions } from '../apps/main/components/doc_table/actions/columns';
interface UseDataGridColumnsProps {

View file

@ -1 +0,0 @@
@import 'angular/index';

View file

@ -8,15 +8,15 @@
import { renderHook, act } from '@testing-library/react-hooks';
import { buildSearchBody, useEsDocSearch } from './use_es_doc_search';
import { DocProps } from './doc';
import { Observable } from 'rxjs';
import { SEARCH_FIELDS_FROM_SOURCE as mockSearchFieldsFromSource } from '../../../../common';
import { IndexPattern } from 'src/plugins/data/common';
import { ElasticRequestState } from './elastic_request_state';
import { DocProps } from '../apps/doc/components/doc';
import { ElasticRequestState } from '../apps/doc/types';
import { SEARCH_FIELDS_FROM_SOURCE as mockSearchFieldsFromSource } from '../../../common';
const mockSearchResult = new Observable();
jest.mock('../../../kibana_services', () => ({
jest.mock('../../kibana_services', () => ({
getServices: () => ({
data: {
search: {

View file

@ -8,11 +8,12 @@
import { useCallback, useEffect, useMemo, useState } from 'react';
import type { estypes } from '@elastic/elasticsearch';
import { getServices, IndexPattern } from '../../../kibana_services';
import { DocProps } from './doc';
import { ElasticSearchHit } from '../../doc_views/doc_views_types';
import { SEARCH_FIELDS_FROM_SOURCE } from '../../../../common';
import { ElasticRequestState } from './elastic_request_state';
import { IndexPattern } from '../../../../data/common';
import { DocProps } from '../apps/doc/components/doc';
import { ElasticRequestState } from '../apps/doc/types';
import { ElasticSearchHit } from '../doc_views/doc_views_types';
import { getServices } from '../../kibana_services';
import { SEARCH_FIELDS_FROM_SOURCE } from '../../../common';
type RequestBody = Pick<estypes.SearchRequest, 'body'>;

View file

@ -5,6 +5,7 @@
* in compliance with, at your election, the Elastic License 2.0 or the Server
* Side Public License, v 1.
*/
import type { estypes } from '@elastic/elasticsearch';
export enum FetchStatus {
UNINITIALIZED = 'uninitialized',
@ -13,3 +14,13 @@ export enum FetchStatus {
COMPLETE = 'complete',
ERROR = 'error',
}
export type EsHitRecord = Required<
Pick<estypes.SearchHit, '_id' | 'fields' | 'sort' | '_index' | '_version'>
> & {
_source?: Record<string, unknown>;
_score?: number;
// note that this a special property for Discover Context, to determine the anchor record
isAnchor?: boolean;
};
export type EsHitRecordList = EsHitRecord[];

View file

@ -8,7 +8,6 @@
import { History } from 'history';
import type { auto } from 'angular';
import {
Capabilities,
ChromeStart,
@ -59,19 +58,17 @@ export interface DiscoverServices {
toastNotifications: ToastsStart;
getSavedSearchById: (id?: string) => Promise<SavedSearch>;
getSavedSearchUrlById: (id: string) => Promise<string>;
getEmbeddableInjector: () => Promise<auto.IInjectorService>;
uiSettings: IUiSettingsClient;
trackUiMetric?: (metricType: UiCounterMetricType, eventName: string | string[]) => void;
indexPatternFieldEditor: IndexPatternFieldEditorStart;
http: HttpStart;
}
export async function buildServices(
export function buildServices(
core: CoreStart,
plugins: DiscoverStartPlugins,
context: PluginInitializerContext,
getEmbeddableInjector: () => Promise<auto.IInjectorService>
): Promise<DiscoverServices> {
context: PluginInitializerContext
): DiscoverServices {
const services = {
savedObjectsClient: core.savedObjects.client,
savedObjects: plugins.savedObjects,
@ -88,7 +85,6 @@ export async function buildServices(
docLinks: core.docLinks,
theme: plugins.charts.theme,
filterManager: plugins.data.query.filterManager,
getEmbeddableInjector,
getSavedSearchById: async (id?: string) => savedObjectService.get(id),
getSavedSearchUrlById: async (id: string) => savedObjectService.urlFor(id),
history: getHistory,

View file

@ -15,27 +15,9 @@ import { createGetterSetter } from '../../kibana_utils/public';
import { search } from '../../data/public';
import { DocViewsRegistry } from './application/doc_views/doc_views_registry';
let angularModule: ng.IModule | null = null;
let services: DiscoverServices | null = null;
let uiActions: UiActionsStart;
/**
* set bootstrapped inner angular module
*/
export function setAngularModule(module: ng.IModule) {
angularModule = module;
}
/**
* get boostrapped inner angular module
*/
export function getAngularModule(): ng.IModule {
if (!angularModule) {
throw new Error('Discover angular module not yet available');
}
return angularModule;
}
export function getServices(): DiscoverServices {
if (!services) {
throw new Error('Discover services are not yet available');

View file

@ -8,10 +8,8 @@
import { i18n } from '@kbn/i18n';
import React from 'react';
import angular, { auto } from 'angular';
import { BehaviorSubject } from 'rxjs';
import { filter, map } from 'rxjs/operators';
import {
AppMountParameters,
AppUpdater,
@ -40,14 +38,12 @@ import { DocViewerTable } from './application/components/table/table';
import {
setDocViewsRegistry,
setUrlTracker,
setAngularModule,
setServices,
setHeaderActionMenuMounter,
setUiActions,
setScopedHistory,
getScopedHistory,
syncHistoryLocations,
getServices,
} from './kibana_services';
import { createSavedSearchesLoader } from './saved_searches';
import { registerFeature } from './register_feature';
@ -78,7 +74,6 @@ export interface DiscoverSetup {
docViews: {
/**
* Add new doc view shown along with table view and json view in the details of each document in Discover.
* Both react and angular doc views are supported.
* @param docViewRaw
*/
addDocView(docViewRaw: DocViewInput | DocViewInputFn): void;
@ -121,7 +116,7 @@ export interface DiscoverStart {
savedSearchLoader: SavedObjectLoader;
/**
* @deprecated Use URL locator instead. URL generaotr will be removed.
* @deprecated Use URL locator instead. URL generator will be removed.
*/
readonly urlGenerator: undefined | UrlGeneratorContract<'DISCOVER_APP_URL_GENERATOR'>;
@ -189,13 +184,9 @@ export interface DiscoverStartPlugins {
indexPatternFieldEditor: IndexPatternFieldEditorStart;
}
const innerAngularName = 'app/discover';
const embeddableAngularName = 'app/discoverEmbeddable';
/**
* Contains Discover, one of the oldest parts of Kibana
* There are 2 kinds of Angular bootstrapped for rendering, additionally to the main Angular
* Discover provides embeddables, those contain a slimmer Angular
* Discover provides embeddables for Dashboards
*/
export class DiscoverPlugin
implements Plugin<DiscoverSetup, DiscoverStart, DiscoverSetupPlugins, DiscoverStartPlugins> {
@ -203,10 +194,7 @@ export class DiscoverPlugin
private appStateUpdater = new BehaviorSubject<AppUpdater>(() => ({}));
private docViewsRegistry: DocViewsRegistry | null = null;
private embeddableInjector: auto.IInjectorService | null = null;
private stopUrlTracking: (() => void) | undefined = undefined;
private servicesInitialized: boolean = false;
private innerAngularInitialized: boolean = false;
/**
* @deprecated
@ -214,13 +202,6 @@ export class DiscoverPlugin
private urlGenerator?: DiscoverStart['urlGenerator'];
private locator?: DiscoverAppLocator;
/**
* why are those functions public? they are needed for some mocha tests
* can be removed once all is Jest
*/
public initializeInnerAngular?: () => void;
public initializeServices?: () => Promise<{ core: CoreStart; plugins: DiscoverStartPlugins }>;
setup(
core: CoreSetup<DiscoverStartPlugins, DiscoverStart>,
plugins: DiscoverSetupPlugins
@ -326,12 +307,7 @@ export class DiscoverPlugin
defaultPath: '#/',
category: DEFAULT_APP_CATEGORIES.kibana,
mount: async (params: AppMountParameters) => {
if (!this.initializeServices) {
throw Error('Discover plugin method initializeServices is undefined');
}
if (!this.initializeInnerAngular) {
throw Error('Discover plugin method initializeInnerAngular is undefined');
}
const [, depsStart] = await core.getStartServices();
setScopedHistory(params.history);
setHeaderActionMenuMounter(params.setHeaderActionMenu);
syncHistoryLocations();
@ -341,17 +317,11 @@ export class DiscoverPlugin
const unlistenParentHistory = params.history.listen(() => {
window.dispatchEvent(new HashChangeEvent('hashchange'));
});
const {
plugins: { data: dataStart },
} = await this.initializeServices();
await this.initializeInnerAngular();
// make sure the index pattern list is up to date
await dataStart.indexPatterns.clearCache();
await depsStart.data.indexPatterns.clearCache();
const { renderApp } = await import('./application/application');
params.element.classList.add('dscAppWrapper');
const unmount = await renderApp(innerAngularName, params.element);
const unmount = await renderApp('discover', params.element);
return () => {
params.element.classList.remove('dscAppWrapper');
unlistenParentHistory();
@ -400,42 +370,13 @@ export class DiscoverPlugin
start(core: CoreStart, plugins: DiscoverStartPlugins) {
// we need to register the application service at setup, but to render it
// there are some start dependencies necessary, for this reason
// initializeInnerAngular + initializeServices are assigned at start and used
// initializeServices are assigned at start and used
// when the application/embeddable is mounted
this.initializeInnerAngular = async () => {
if (this.innerAngularInitialized) {
return;
}
// this is used by application mount and tests
const { getInnerAngularModule } = await import('./application/angular/get_inner_angular');
await plugins.kibanaLegacy.loadAngularBootstrap();
const module = getInnerAngularModule(
innerAngularName,
core,
plugins,
this.initializerContext
);
setAngularModule(module);
this.innerAngularInitialized = true;
};
setUiActions(plugins.uiActions);
this.initializeServices = async () => {
if (this.servicesInitialized) {
return { core, plugins };
}
const services = await buildServices(
core,
plugins,
this.initializerContext,
this.getEmbeddableInjector
);
setServices(services);
this.servicesInitialized = true;
return { core, plugins };
};
const services = buildServices(core, plugins, this.initializerContext);
setServices(services);
return {
urlGenerator: this.urlGenerator,
@ -453,14 +394,7 @@ export class DiscoverPlugin
}
}
/**
* register embeddable with a slimmer embeddable version of inner angular
*/
private registerEmbeddable(core: CoreSetup<DiscoverStartPlugins>, plugins: DiscoverSetupPlugins) {
if (!this.getEmbeddableInjector) {
throw Error('Discover plugin method getEmbeddableInjector is undefined');
}
const getStartServices = async () => {
const [coreStart, deps] = await core.getStartServices();
return {
@ -469,25 +403,7 @@ export class DiscoverPlugin
};
};
const factory = new SearchEmbeddableFactory(getStartServices, this.getEmbeddableInjector);
const factory = new SearchEmbeddableFactory(getStartServices);
plugins.embeddable.registerEmbeddableFactory(factory.type, factory);
}
private getEmbeddableInjector = async () => {
if (!this.embeddableInjector) {
if (!this.initializeServices) {
throw Error('Discover plugin getEmbeddableInjector: initializeServices is undefined');
}
const { core, plugins } = await this.initializeServices();
await getServices().kibanaLegacy.loadAngularBootstrap();
const { getInnerAngularModuleEmbeddable } = await import(
'./application/angular/get_inner_angular'
);
getInnerAngularModuleEmbeddable(embeddableAngularName, core, plugins);
const mountpoint = document.createElement('div');
this.embeddableInjector = angular.bootstrap(mountpoint, [embeddableAngularName]);
}
return this.embeddableInjector;
};
}