Move ensureDefaultIndexPattern into data plugin (#63100)

* Move ensure_default_index_pattern into data plugin

* Update docs to include ensureDefaultIndexPattern

* Fix translations

* Move helper into index_patterns service

* Update docs

* Remove mock

* Add mock

Co-authored-by: Elastic Machine <elasticmachine@users.noreply.github.com>
This commit is contained in:
Daniil Suleiman 2020-04-24 16:20:32 +03:00 committed by GitHub
parent 0dcefe8dbc
commit 9bda6bde83
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
16 changed files with 133 additions and 134 deletions

View file

@ -59,11 +59,7 @@ export const [getUrlTracker, setUrlTracker] = createGetterSetter<{
export const getHistory = _.once(() => createHashHistory());
export const { getRequestInspectorStats, getResponseInspectorStats, tabifyAggResponse } = search;
export {
unhashUrl,
redirectWhenMissing,
ensureDefaultIndexPattern,
} from '../../../../../plugins/kibana_utils/public';
export { unhashUrl, redirectWhenMissing } from '../../../../../plugins/kibana_utils/public';
export {
formatMsg,
formatStack,

View file

@ -49,7 +49,6 @@ import {
subscribeWithScope,
tabifyAggResponse,
getAngularModule,
ensureDefaultIndexPattern,
redirectWhenMissing,
} from '../../kibana_services';
@ -118,7 +117,7 @@ app.config($routeProvider => {
savedObjects: function($route, Promise) {
const history = getHistory();
const savedSearchId = $route.current.params.id;
return ensureDefaultIndexPattern(core, data, history).then(() => {
return data.indexPatterns.ensureDefaultIndexPattern(history).then(() => {
const { appStateContainer } = getState({ history });
const { index } = appStateContainer.getState();
return Promise.props({

View file

@ -149,6 +149,7 @@ exports[`CreateIndexPatternWizard renders time field step when step is set to 2
indexPatternsService={
Object {
"clearCache": [MockFunction],
"ensureDefaultIndexPattern": [MockFunction],
"get": [MockFunction],
"make": [Function],
}

View file

@ -28,7 +28,6 @@ import { initDashboardAppDirective } from './dashboard_app';
import { createDashboardEditUrl, DashboardConstants } from '../dashboard_constants';
import {
createKbnUrlStateStorage,
ensureDefaultIndexPattern,
redirectWhenMissing,
InvalidJSONProperty,
SavedObjectNotFound,
@ -138,7 +137,7 @@ export function initDashboardApp(app, deps) {
},
resolve: {
dash: function($route, history) {
return ensureDefaultIndexPattern(deps.core, deps.data, history).then(() => {
return deps.data.indexPatterns.ensureDefaultIndexPattern(history).then(() => {
const savedObjectsClient = deps.savedObjectsClient;
const title = $route.current.params.title;
if (title) {
@ -173,7 +172,8 @@ export function initDashboardApp(app, deps) {
requireUICapability: 'dashboard.createNew',
resolve: {
dash: history =>
ensureDefaultIndexPattern(deps.core, deps.data, history)
deps.data.indexPatterns
.ensureDefaultIndexPattern(history)
.then(() => deps.savedDashboards.get())
.catch(
redirectWhenMissing({
@ -194,7 +194,8 @@ export function initDashboardApp(app, deps) {
dash: function($route, history) {
const id = $route.current.params.id;
return ensureDefaultIndexPattern(deps.core, deps.data, history)
return deps.data.indexPatterns
.ensureDefaultIndexPattern(history)
.then(() => deps.savedDashboards.get(id))
.then(savedDashboard => {
deps.chrome.recentlyAccessed.add(

View file

@ -0,0 +1,98 @@
/*
* Licensed to Elasticsearch B.V. under one or more contributor
* license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright
* ownership. Elasticsearch B.V. licenses this file to you under
* the Apache License, Version 2.0 (the "License"); you may
* not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
import { contains } from 'lodash';
import React from 'react';
import { History } from 'history';
import { i18n } from '@kbn/i18n';
import { EuiCallOut } from '@elastic/eui';
import { CoreStart } from 'kibana/public';
import { toMountPoint } from '../../../../kibana_react/public';
import { IndexPatternsContract } from './index_patterns';
export type EnsureDefaultIndexPattern = (history: History) => Promise<unknown> | undefined;
export const createEnsureDefaultIndexPattern = (core: CoreStart) => {
let bannerId: string;
let timeoutId: NodeJS.Timeout | undefined;
/**
* Checks whether a default index pattern is set and exists and defines
* one otherwise.
*
* If there are no index patterns, redirect to management page and show
* banner. In this case the promise returned from this function will never
* resolve to wait for the URL change to happen.
*/
return async function ensureDefaultIndexPattern(this: IndexPatternsContract, history: History) {
const patterns = await this.getIds();
let defaultId = core.uiSettings.get('defaultIndex');
let defined = !!defaultId;
const exists = contains(patterns, defaultId);
if (defined && !exists) {
core.uiSettings.remove('defaultIndex');
defaultId = defined = false;
}
if (defined) {
return;
}
// If there is any index pattern created, set the first as default
if (patterns.length >= 1) {
defaultId = patterns[0];
core.uiSettings.set('defaultIndex', defaultId);
} else {
const canManageIndexPatterns = core.application.capabilities.management.kibana.index_patterns;
const redirectTarget = canManageIndexPatterns ? '/management/kibana/index_pattern' : '/home';
if (timeoutId) {
clearTimeout(timeoutId);
}
// Avoid being hostile to new users who don't have an index pattern setup yet
// give them a friendly info message instead of a terse error message
bannerId = core.overlays.banners.replace(
bannerId,
toMountPoint(
<EuiCallOut
color="warning"
iconType="iInCircle"
title={i18n.translate('data.indexPatterns.ensureDefaultIndexPattern.bannerLabel', {
defaultMessage:
"In order to visualize and explore data in Kibana, you'll need to create an index pattern to retrieve data from Elasticsearch.",
})}
/>
)
);
// hide the message after the user has had a chance to acknowledge it -- so it doesn't permanently stick around
timeoutId = setTimeout(() => {
core.overlays.banners.remove(bannerId);
timeoutId = undefined;
}, 15000);
history.push(redirectTarget);
// return never-resolving promise to stop resolving and wait for the url change
return new Promise(() => {});
}
};
};

View file

@ -21,9 +21,9 @@
import { IndexPatternsService } from './index_patterns';
import {
SavedObjectsClientContract,
IUiSettingsClient,
HttpSetup,
SavedObjectsFindResponsePublic,
CoreStart,
} from 'kibana/public';
jest.mock('./index_pattern', () => {
@ -61,10 +61,10 @@ describe('IndexPatterns', () => {
}) as Promise<SavedObjectsFindResponsePublic<any>>
);
const uiSettings = {} as IUiSettingsClient;
const core = {} as CoreStart;
const http = {} as HttpSetup;
indexPatterns = new IndexPatternsService(uiSettings, savedObjectsClient, http);
indexPatterns = new IndexPatternsService(core, savedObjectsClient, http);
});
test('does cache gets for the same id', async () => {

View file

@ -22,11 +22,16 @@ import {
SimpleSavedObject,
IUiSettingsClient,
HttpStart,
CoreStart,
} from 'src/core/public';
import { createIndexPatternCache } from './_pattern_cache';
import { IndexPattern } from './index_pattern';
import { IndexPatternsApiClient, GetFieldsOptions } from './index_patterns_api_client';
import {
createEnsureDefaultIndexPattern,
EnsureDefaultIndexPattern,
} from './ensure_default_index_pattern';
const indexPatternCache = createIndexPatternCache();
@ -37,15 +42,13 @@ export class IndexPatternsService {
private savedObjectsClient: SavedObjectsClientContract;
private savedObjectsCache?: Array<SimpleSavedObject<Record<string, any>>> | null;
private apiClient: IndexPatternsApiClient;
ensureDefaultIndexPattern: EnsureDefaultIndexPattern;
constructor(
config: IUiSettingsClient,
savedObjectsClient: SavedObjectsClientContract,
http: HttpStart
) {
constructor(core: CoreStart, savedObjectsClient: SavedObjectsClientContract, http: HttpStart) {
this.apiClient = new IndexPatternsApiClient(http);
this.config = config;
this.config = core.uiSettings;
this.savedObjectsClient = savedObjectsClient;
this.ensureDefaultIndexPattern = createEnsureDefaultIndexPattern(core);
}
private async refreshSavedObjectsCache() {

View file

@ -57,6 +57,7 @@ const createStartContract = (): Start => {
SearchBar: jest.fn(),
},
indexPatterns: ({
ensureDefaultIndexPattern: jest.fn(),
make: () => ({
fieldsFetcher: {
fetchForWildcard: jest.fn(),

View file

@ -160,7 +160,7 @@ export class DataPublicPlugin implements Plugin<DataPublicPluginSetup, DataPubli
const fieldFormats = this.fieldFormatsService.start();
setFieldFormats(fieldFormats);
const indexPatterns = new IndexPatternsService(uiSettings, savedObjects.client, http);
const indexPatterns = new IndexPatternsService(core, savedObjects.client, http);
setIndexPatterns(indexPatterns);
const query = this.queryService.start(savedObjects);

View file

@ -1,98 +0,0 @@
/*
* Licensed to Elasticsearch B.V. under one or more contributor
* license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright
* ownership. Elasticsearch B.V. licenses this file to you under
* the Apache License, Version 2.0 (the "License"); you may
* not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
import { contains } from 'lodash';
import React from 'react';
import { History } from 'history';
import { i18n } from '@kbn/i18n';
import { EuiCallOut } from '@elastic/eui';
import { CoreStart } from 'kibana/public';
import { DataPublicPluginStart } from 'src/plugins/data/public';
import { toMountPoint } from '../../../kibana_react/public';
let bannerId: string;
let timeoutId: NodeJS.Timeout | undefined;
/**
* Checks whether a default index pattern is set and exists and defines
* one otherwise.
*
* If there are no index patterns, redirect to management page and show
* banner. In this case the promise returned from this function will never
* resolve to wait for the URL change to happen.
*/
export async function ensureDefaultIndexPattern(
core: CoreStart,
data: DataPublicPluginStart,
history: History
) {
const patterns = await data.indexPatterns.getIds();
let defaultId = core.uiSettings.get('defaultIndex');
let defined = !!defaultId;
const exists = contains(patterns, defaultId);
if (defined && !exists) {
core.uiSettings.remove('defaultIndex');
defaultId = defined = false;
}
if (defined) {
return;
}
// If there is any index pattern created, set the first as default
if (patterns.length >= 1) {
defaultId = patterns[0];
core.uiSettings.set('defaultIndex', defaultId);
} else {
const canManageIndexPatterns = core.application.capabilities.management.kibana.index_patterns;
const redirectTarget = canManageIndexPatterns ? '/management/kibana/index_pattern' : '/home';
if (timeoutId) {
clearTimeout(timeoutId);
}
// Avoid being hostile to new users who don't have an index pattern setup yet
// give them a friendly info message instead of a terse error message
bannerId = core.overlays.banners.replace(
bannerId,
toMountPoint(
<EuiCallOut
color="warning"
iconType="iInCircle"
title={i18n.translate('kibana_utils.indexPattern.bannerLabel', {
defaultMessage:
"In order to visualize and explore data in Kibana, you'll need to create an index pattern to retrieve data from Elasticsearch.",
})}
/>
)
);
// hide the message after the user has had a chance to acknowledge it -- so it doesn't permanently stick around
timeoutId = setTimeout(() => {
core.overlays.banners.remove(bannerId);
timeoutId = undefined;
}, 15000);
history.push(redirectTarget);
// return never-resolving promise to stop resolving and wait for the url change
return new Promise(() => {});
}
}

View file

@ -19,4 +19,3 @@
export { removeQueryParam } from './remove_query_param';
export { redirectWhenMissing } from './redirect_when_missing';
export { ensureDefaultIndexPattern } from './ensure_default_index_pattern';

View file

@ -74,7 +74,7 @@ export {
StartSyncStateFnType,
StopSyncStateFnType,
} from './state_sync';
export { removeQueryParam, redirectWhenMissing, ensureDefaultIndexPattern } from './history';
export { removeQueryParam, redirectWhenMissing } from './history';
export { applyDiff } from './state_management/utils/diff_object';
/** dummy plugin, we just want kibanaUtils to have its own bundle */

View file

@ -238,6 +238,7 @@ exports[`SavedObjectsTable import should show the flyout 1`] = `
indexPatterns={
Object {
"clearCache": [MockFunction],
"ensureDefaultIndexPattern": [MockFunction],
"get": [MockFunction],
"make": [Function],
}

View file

@ -21,11 +21,7 @@ import { find } from 'lodash';
import { i18n } from '@kbn/i18n';
import { createHashHistory } from 'history';
import {
createKbnUrlStateStorage,
redirectWhenMissing,
ensureDefaultIndexPattern,
} from '../../../kibana_utils/public';
import { createKbnUrlStateStorage, redirectWhenMissing } from '../../../kibana_utils/public';
import { createSavedSearchesLoader } from '../../../discover/public';
import editorTemplate from './editor/editor.html';
@ -127,7 +123,7 @@ export function initVisualizeApp(app, deps) {
controllerAs: 'listingController',
resolve: {
createNewVis: () => false,
hasDefaultIndex: history => ensureDefaultIndexPattern(deps.core, deps.data, history),
hasDefaultIndex: history => deps.data.indexPatterns.ensureDefaultIndexPattern(history),
},
})
.when(VisualizeConstants.WIZARD_STEP_1_PAGE_PATH, {
@ -138,7 +134,7 @@ export function initVisualizeApp(app, deps) {
controllerAs: 'listingController',
resolve: {
createNewVis: () => true,
hasDefaultIndex: history => ensureDefaultIndexPattern(deps.core, deps.data, history),
hasDefaultIndex: history => deps.data.indexPatterns.ensureDefaultIndexPattern(history),
},
})
.when(VisualizeConstants.CREATE_PATH, {
@ -147,7 +143,7 @@ export function initVisualizeApp(app, deps) {
k7Breadcrumbs: getCreateBreadcrumbs,
resolve: {
resolved: function($route, history) {
const { core, data, savedVisualizations, visualizations, toastNotifications } = deps;
const { data, savedVisualizations, visualizations, toastNotifications } = deps;
const visTypes = visualizations.all();
const visType = find(visTypes, { name: $route.current.params.type });
const shouldHaveIndex = visType.requiresSearch && visType.options.showIndexSelection;
@ -164,7 +160,8 @@ export function initVisualizeApp(app, deps) {
);
}
return ensureDefaultIndexPattern(core, data, history)
return data.indexPatterns
.ensureDefaultIndexPattern(history)
.then(() => savedVisualizations.get($route.current.params))
.then(getResolvedResults(deps))
.catch(
@ -183,9 +180,10 @@ export function initVisualizeApp(app, deps) {
k7Breadcrumbs: getEditBreadcrumbs,
resolve: {
resolved: function($route, history) {
const { chrome, core, data, savedVisualizations, toastNotifications } = deps;
const { chrome, data, savedVisualizations, toastNotifications } = deps;
return ensureDefaultIndexPattern(core, data, history)
return data.indexPatterns
.ensureDefaultIndexPattern(history)
.then(() => savedVisualizations.get($route.current.params.id))
.then(savedVis => {
chrome.recentlyAccessed.add(savedVis.getFullPath(), savedVis.title, savedVis.id);

View file

@ -671,6 +671,7 @@
"data.functions.esaggs.inspector.dataRequest.description": "このリクエストは Elasticsearch にクエリし、ビジュアライゼーション用のデータを取得します。",
"data.functions.esaggs.inspector.dataRequest.title": "データ",
"data.indexPatterns.fetchFieldErrorTitle": "インデックスパターンのフィールド取得中にエラーが発生 {title} (ID: {id})",
"data.indexPatterns.ensureDefaultIndexPattern.bannerLabel": "Kibanaでデータの可視化と閲覧を行うには、Elasticsearchからデータを取得するためのインデックスパターンの作成が必要です。",
"data.indexPatterns.unableWriteLabel": "インデックスパターンを書き込めません!このインデックスパターンへの最新の変更を取得するには、ページを更新してください。",
"data.indexPatterns.unknownFieldErrorMessage": "インデックスパターン「{title}」のフィールド「{name}」が不明なフィールドタイプを使用しています。",
"data.indexPatterns.unknownFieldHeader": "不明なフィールドタイプ {type}",
@ -2428,7 +2429,6 @@
"kibana_legacy.paginate.size.allDropDownOptionLabel": "すべて",
"kibana_utils.defaultFeedbackMessage": "フィードバックがありますか?{link} で問題を報告してください。",
"kibana_utils.history.savedObjectIsMissingNotificationMessage": "保存されたオブジェクトがありません",
"kibana_utils.indexPattern.bannerLabel": "Kibanaでデータの可視化と閲覧を行うには、Elasticsearchからデータを取得するためのインデックスパターンの作成が必要です。",
"kibana_utils.stateManagement.stateHash.unableToRestoreUrlErrorMessage": "URL を完全に復元できません。共有機能を使用していることを確認してください。",
"kibana_utils.stateManagement.stateHash.unableToStoreHistoryInSessionErrorMessage": "セッションがいっぱいで安全に削除できるアイテムが見つからないため、Kibana は履歴アイテムを保存できません。\n\nこれは大抵新規タブに移動することで解決されますが、より大きな問題が原因である可能性もあります。このメッセージが定期的に表示される場合は、{gitHubIssuesUrl} で問題を報告してください。",
"kibana-react.dualRangeControl.mustSetBothErrorMessage": "下と上の値の両方を設定する必要があります",

View file

@ -671,6 +671,7 @@
"data.functions.esaggs.inspector.dataRequest.description": "此请求将查询 Elasticsearch 以获取用于可视化的数据。",
"data.functions.esaggs.inspector.dataRequest.title": "数据",
"data.indexPatterns.fetchFieldErrorTitle": "提取索引模式 {title} (ID: {id}) 的字段时出错",
"data.indexPatterns.ensureDefaultIndexPattern.bannerLabel": "若要在 Kibana 中可视化和浏览数据,您需要创建索引模式,以从 Elasticsearch 检索数据。",
"data.indexPatterns.unableWriteLabel": "无法写入索引模式!请刷新页面以获取此索引模式的最新更改。",
"data.indexPatterns.unknownFieldErrorMessage": "indexPattern “{title}” 中的字段 “{name}” 使用未知字段类型。",
"data.indexPatterns.unknownFieldHeader": "未知字段类型 {type}",
@ -2429,7 +2430,6 @@
"kibana_legacy.paginate.size.allDropDownOptionLabel": "全部",
"kibana_utils.defaultFeedbackMessage": "想反馈?请在 {link} 中创建问题。",
"kibana_utils.history.savedObjectIsMissingNotificationMessage": "已保存对象缺失",
"kibana_utils.indexPattern.bannerLabel": "若要在 Kibana 中可视化和浏览数据,您需要创建索引模式,以从 Elasticsearch 检索数据。",
"kibana_utils.stateManagement.stateHash.unableToRestoreUrlErrorMessage": "无法完全还原 URL请确保使用共享功能。",
"kibana_utils.stateManagement.stateHash.unableToStoreHistoryInSessionErrorMessage": "Kibana 无法将历史记录项存储在您的会话中,因为其已满,另外,似乎没有任何可安全删除的项目。\n\n通常这可以通过移到全新的选项卡来解决但这种情况可能是由更大的问题造成。如果您定期看到这个消息请在 {gitHubIssuesUrl} 报告问题。",
"kibana-react.dualRangeControl.mustSetBothErrorMessage": "下限值和上限值都须设置",