mirror of
https://github.com/elastic/kibana.git
synced 2025-04-24 17:59:23 -04:00
[data view mgmt] add saved object relationships to data view management (#132385)
* add saved object relationships to data view management
This commit is contained in:
parent
d1b4465d0a
commit
606d9c2649
20 changed files with 402 additions and 17 deletions
|
@ -3,7 +3,7 @@
|
|||
"version": "kibana",
|
||||
"server": true,
|
||||
"ui": true,
|
||||
"requiredPlugins": ["management", "data", "urlForwarding", "dataViewFieldEditor", "dataViewEditor", "dataViews", "fieldFormats", "unifiedSearch"],
|
||||
"requiredPlugins": ["management", "data", "urlForwarding", "dataViewFieldEditor", "dataViewEditor", "dataViews", "fieldFormats", "unifiedSearch", "savedObjectsManagement"],
|
||||
"requiredBundles": ["kibanaReact", "kibanaUtils"],
|
||||
"optionalPlugins": ["spaces"],
|
||||
"owner": {
|
||||
|
|
|
@ -9,3 +9,4 @@
|
|||
export const TAB_INDEXED_FIELDS = 'indexedFields';
|
||||
export const TAB_SCRIPTED_FIELDS = 'scriptedFields';
|
||||
export const TAB_SOURCE_FILTERS = 'sourceFilters';
|
||||
export const TAB_RELATIONSHIPS = 'relationships';
|
||||
|
|
|
@ -21,7 +21,12 @@ import {
|
|||
import { i18n } from '@kbn/i18n';
|
||||
import { FormattedMessage } from '@kbn/i18n-react';
|
||||
import { DataView, DataViewField } from '@kbn/data-views-plugin/public';
|
||||
import { DATA_VIEW_SAVED_OBJECT_TYPE } from '@kbn/data-views-plugin/public';
|
||||
import { useKibana } from '@kbn/kibana-react-plugin/public';
|
||||
import {
|
||||
SavedObjectRelation,
|
||||
SavedObjectManagementTypeInfo,
|
||||
} from '@kbn/saved-objects-management-plugin/public';
|
||||
import { IndexPatternManagmentContext } from '../../types';
|
||||
import { Tabs } from './tabs';
|
||||
import { IndexHeader } from './index_header';
|
||||
|
@ -32,6 +37,10 @@ export interface EditIndexPatternProps extends RouteComponentProps {
|
|||
indexPattern: DataView;
|
||||
}
|
||||
|
||||
export interface SavedObjectRelationWithTitle extends SavedObjectRelation {
|
||||
title: string;
|
||||
}
|
||||
|
||||
const mappingAPILink = i18n.translate(
|
||||
'indexPatternManagement.editIndexPattern.timeFilterLabel.mappingAPILink',
|
||||
{
|
||||
|
@ -57,7 +66,7 @@ const securitySolution = 'security-solution';
|
|||
|
||||
export const EditIndexPattern = withRouter(
|
||||
({ indexPattern, history, location }: EditIndexPatternProps) => {
|
||||
const { uiSettings, overlays, chrome, dataViews } =
|
||||
const { uiSettings, overlays, chrome, dataViews, savedObjectsManagement } =
|
||||
useKibana<IndexPatternManagmentContext>().services;
|
||||
const [fields, setFields] = useState<DataViewField[]>(indexPattern.getNonScriptedFields());
|
||||
const [conflictedFields, setConflictedFields] = useState<DataViewField[]>(
|
||||
|
@ -65,6 +74,26 @@ export const EditIndexPattern = withRouter(
|
|||
);
|
||||
const [defaultIndex, setDefaultIndex] = useState<string>(uiSettings.get('defaultIndex'));
|
||||
const [tags, setTags] = useState<any[]>([]);
|
||||
const [relationships, setRelationships] = useState<SavedObjectRelationWithTitle[]>([]);
|
||||
const [allowedTypes, setAllowedTypes] = useState<SavedObjectManagementTypeInfo[]>([]);
|
||||
|
||||
useEffect(() => {
|
||||
savedObjectsManagement.getAllowedTypes().then((resp) => {
|
||||
setAllowedTypes(resp);
|
||||
});
|
||||
}, [savedObjectsManagement]);
|
||||
|
||||
useEffect(() => {
|
||||
if (allowedTypes.length === 0) {
|
||||
return;
|
||||
}
|
||||
const allowedAsString = allowedTypes.map((item) => item.name);
|
||||
savedObjectsManagement
|
||||
.getRelationships(DATA_VIEW_SAVED_OBJECT_TYPE, indexPattern.id!, allowedAsString)
|
||||
.then((resp) => {
|
||||
setRelationships(resp.relations.map((r) => ({ ...r, title: r.meta.title! })));
|
||||
});
|
||||
}, [savedObjectsManagement, indexPattern, allowedTypes]);
|
||||
|
||||
useEffect(() => {
|
||||
setFields(indexPattern.getNonScriptedFields());
|
||||
|
@ -200,6 +229,8 @@ export const EditIndexPattern = withRouter(
|
|||
indexPattern={indexPattern}
|
||||
saveIndexPattern={dataViews.updateSavedObject.bind(dataViews)}
|
||||
fields={fields}
|
||||
relationships={relationships}
|
||||
allowedTypes={allowedTypes}
|
||||
history={history}
|
||||
location={location}
|
||||
refreshFields={() => {
|
||||
|
|
|
@ -0,0 +1,38 @@
|
|||
/*
|
||||
* 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 { i18n } from '@kbn/i18n';
|
||||
|
||||
export const typeFieldName = i18n.translate(
|
||||
'indexPatternManagement.objectsTable.relationships.columnTypeName',
|
||||
{
|
||||
defaultMessage: 'Type',
|
||||
}
|
||||
);
|
||||
|
||||
export const typeFieldDescription = i18n.translate(
|
||||
'indexPatternManagement.objectsTable.relationships.columnTypeDescription',
|
||||
{ defaultMessage: 'Type of the saved object' }
|
||||
);
|
||||
|
||||
export const titleFieldName = i18n.translate(
|
||||
'indexPatternManagement.objectsTable.relationships.columnTitleName',
|
||||
{
|
||||
defaultMessage: 'Title',
|
||||
}
|
||||
);
|
||||
|
||||
export const titleFieldDescription = i18n.translate(
|
||||
'indexPatternManagement.objectsTable.relationships.columnTitleDescription',
|
||||
{ defaultMessage: 'Title of the saved object' }
|
||||
);
|
||||
|
||||
export const filterTitle = i18n.translate(
|
||||
'indexPatternManagement.objectsTable.relationships.search.filters.type.name',
|
||||
{ defaultMessage: 'Type' }
|
||||
);
|
|
@ -0,0 +1,9 @@
|
|||
/*
|
||||
* 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 { RelationshipsTable } from './relationships_table';
|
|
@ -0,0 +1,154 @@
|
|||
/*
|
||||
* 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 {
|
||||
EuiInMemoryTable,
|
||||
HorizontalAlignment,
|
||||
EuiText,
|
||||
EuiLink,
|
||||
EuiTableDataType,
|
||||
} from '@elastic/eui';
|
||||
import { CoreStart } from '@kbn/core/public';
|
||||
import { get } from 'lodash';
|
||||
import { RedirectAppLinks } from '@kbn/shared-ux-link-redirect-app';
|
||||
|
||||
import {
|
||||
SavedObjectRelation,
|
||||
SavedObjectManagementTypeInfo,
|
||||
SavedObjectsManagementPluginStart,
|
||||
} from '@kbn/saved-objects-management-plugin/public';
|
||||
|
||||
import { EuiToolTip, EuiIcon, SearchFilterConfig } from '@elastic/eui';
|
||||
import { IPM_APP_ID } from '../../../plugin';
|
||||
import {
|
||||
typeFieldName,
|
||||
typeFieldDescription,
|
||||
titleFieldName,
|
||||
titleFieldDescription,
|
||||
filterTitle,
|
||||
} from './i18n';
|
||||
|
||||
const canGoInApp = (
|
||||
savedObject: SavedObjectRelation,
|
||||
capabilities: CoreStart['application']['capabilities']
|
||||
) => {
|
||||
const { inAppUrl } = savedObject.meta;
|
||||
if (!inAppUrl) return false;
|
||||
if (!inAppUrl.uiCapabilitiesPath) return true;
|
||||
return Boolean(get(capabilities, inAppUrl.uiCapabilitiesPath));
|
||||
};
|
||||
|
||||
export const RelationshipsTable = ({
|
||||
basePath,
|
||||
capabilities,
|
||||
id,
|
||||
navigateToUrl,
|
||||
getDefaultTitle,
|
||||
getSavedObjectLabel,
|
||||
relationships,
|
||||
allowedTypes,
|
||||
}: {
|
||||
basePath: CoreStart['http']['basePath'];
|
||||
capabilities: CoreStart['application']['capabilities'];
|
||||
navigateToUrl: CoreStart['application']['navigateToUrl'];
|
||||
id: string;
|
||||
getDefaultTitle: SavedObjectsManagementPluginStart['getDefaultTitle'];
|
||||
getSavedObjectLabel: SavedObjectsManagementPluginStart['getSavedObjectLabel'];
|
||||
relationships: SavedObjectRelation[];
|
||||
allowedTypes: SavedObjectManagementTypeInfo[];
|
||||
}) => {
|
||||
const columns = [
|
||||
{
|
||||
field: 'type',
|
||||
name: typeFieldName,
|
||||
width: '50px',
|
||||
align: 'center' as HorizontalAlignment,
|
||||
description: typeFieldDescription,
|
||||
sortable: false,
|
||||
render: (type: string, object: SavedObjectRelation) => {
|
||||
const typeLabel = getSavedObjectLabel(type, allowedTypes);
|
||||
return (
|
||||
<EuiToolTip position="top" content={typeLabel}>
|
||||
<EuiIcon
|
||||
aria-label={typeLabel}
|
||||
type={object.meta.icon || 'apps'}
|
||||
size="s"
|
||||
data-test-subj="relationshipsObjectType"
|
||||
/>
|
||||
</EuiToolTip>
|
||||
);
|
||||
},
|
||||
},
|
||||
{
|
||||
field: 'title',
|
||||
name: titleFieldName,
|
||||
description: titleFieldDescription,
|
||||
dataType: 'string' as EuiTableDataType,
|
||||
sortable: false,
|
||||
render: (title: string, object: SavedObjectRelation) => {
|
||||
const path = object.meta.inAppUrl?.path || '';
|
||||
const showUrl = canGoInApp(object, capabilities);
|
||||
const titleDisplayed = title || getDefaultTitle(object);
|
||||
|
||||
return showUrl ? (
|
||||
<EuiLink href={basePath.prepend(path)} data-test-subj="relationshipsTitle">
|
||||
{titleDisplayed}
|
||||
</EuiLink>
|
||||
) : (
|
||||
<EuiText size="s" data-test-subj="relationshipsTitle">
|
||||
{titleDisplayed}
|
||||
</EuiText>
|
||||
);
|
||||
},
|
||||
},
|
||||
];
|
||||
|
||||
const filterTypesMap = new Map(
|
||||
relationships.map((relationship) => [
|
||||
relationship.type,
|
||||
{
|
||||
value: relationship.type,
|
||||
name: relationship.type,
|
||||
view: relationship.type,
|
||||
},
|
||||
])
|
||||
);
|
||||
|
||||
const search = {
|
||||
// query,
|
||||
// onChange: handleOnChange,
|
||||
box: {
|
||||
incremental: true,
|
||||
schema: true,
|
||||
},
|
||||
filters: [
|
||||
{
|
||||
type: 'field_value_selection',
|
||||
field: 'type',
|
||||
name: filterTitle,
|
||||
multiSelect: 'or',
|
||||
options: [...filterTypesMap.values()],
|
||||
},
|
||||
] as SearchFilterConfig[],
|
||||
};
|
||||
|
||||
return (
|
||||
<RedirectAppLinks currentAppId={IPM_APP_ID} navigateToUrl={navigateToUrl}>
|
||||
<EuiInMemoryTable<SavedObjectRelation>
|
||||
items={relationships}
|
||||
columns={columns}
|
||||
pagination={true}
|
||||
search={search}
|
||||
rowProps={() => ({
|
||||
'data-test-subj': `relationshipsTableRow`,
|
||||
})}
|
||||
/>
|
||||
</RedirectAppLinks>
|
||||
);
|
||||
};
|
|
@ -30,13 +30,23 @@ import {
|
|||
DataViewsPublicPluginStart,
|
||||
META_FIELDS,
|
||||
} from '@kbn/data-views-plugin/public';
|
||||
import {
|
||||
SavedObjectRelation,
|
||||
SavedObjectManagementTypeInfo,
|
||||
} from '@kbn/saved-objects-management-plugin/public';
|
||||
import { useKibana } from '@kbn/kibana-react-plugin/public';
|
||||
import { IndexPatternManagmentContext } from '../../../types';
|
||||
import { createEditIndexPatternPageStateContainer } from '../edit_index_pattern_state_container';
|
||||
import { TAB_INDEXED_FIELDS, TAB_SCRIPTED_FIELDS, TAB_SOURCE_FILTERS } from '../constants';
|
||||
import {
|
||||
TAB_INDEXED_FIELDS,
|
||||
TAB_SCRIPTED_FIELDS,
|
||||
TAB_SOURCE_FILTERS,
|
||||
TAB_RELATIONSHIPS,
|
||||
} from '../constants';
|
||||
import { SourceFiltersTable } from '../source_filters_table';
|
||||
import { IndexedFieldsTable } from '../indexed_fields_table';
|
||||
import { ScriptedFieldsTable } from '../scripted_fields_table';
|
||||
import { RelationshipsTable } from '../relationships_table';
|
||||
import { getTabs, getPath, convertToEuiFilterOptions } from './utils';
|
||||
import { getFieldInfo } from '../../utils';
|
||||
|
||||
|
@ -45,6 +55,8 @@ interface TabsProps extends Pick<RouteComponentProps, 'history' | 'location'> {
|
|||
fields: DataViewField[];
|
||||
saveIndexPattern: DataViewsPublicPluginStart['updateSavedObject'];
|
||||
refreshFields: () => void;
|
||||
relationships: SavedObjectRelation[];
|
||||
allowedTypes: SavedObjectManagementTypeInfo[];
|
||||
}
|
||||
|
||||
interface FilterItems {
|
||||
|
@ -131,9 +143,20 @@ export function Tabs({
|
|||
history,
|
||||
location,
|
||||
refreshFields,
|
||||
relationships,
|
||||
allowedTypes,
|
||||
}: TabsProps) {
|
||||
const { uiSettings, docLinks, dataViewFieldEditor, overlays, theme, dataViews } =
|
||||
useKibana<IndexPatternManagmentContext>().services;
|
||||
const {
|
||||
uiSettings,
|
||||
docLinks,
|
||||
dataViewFieldEditor,
|
||||
overlays,
|
||||
theme,
|
||||
dataViews,
|
||||
http,
|
||||
application,
|
||||
savedObjectsManagement,
|
||||
} = useKibana<IndexPatternManagmentContext>().services;
|
||||
const [fieldFilter, setFieldFilter] = useState<string>('');
|
||||
const [syncingStateFunc, setSyncingStateFunc] = useState<any>({
|
||||
getCurrentTab: () => TAB_INDEXED_FIELDS,
|
||||
|
@ -492,6 +515,22 @@ export function Tabs({
|
|||
/>
|
||||
</Fragment>
|
||||
);
|
||||
case TAB_RELATIONSHIPS:
|
||||
return (
|
||||
<Fragment>
|
||||
<EuiSpacer size="m" />
|
||||
<RelationshipsTable
|
||||
basePath={http.basePath}
|
||||
id={indexPattern.id!}
|
||||
capabilities={application.capabilities}
|
||||
relationships={relationships}
|
||||
allowedTypes={allowedTypes}
|
||||
navigateToUrl={application.navigateToUrl}
|
||||
getDefaultTitle={savedObjectsManagement.getDefaultTitle}
|
||||
getSavedObjectLabel={savedObjectsManagement.getSavedObjectLabel}
|
||||
/>
|
||||
</Fragment>
|
||||
);
|
||||
}
|
||||
},
|
||||
[
|
||||
|
@ -513,18 +552,25 @@ export function Tabs({
|
|||
overlays,
|
||||
theme,
|
||||
dataViews,
|
||||
http,
|
||||
application,
|
||||
savedObjectsManagement,
|
||||
allowedTypes,
|
||||
relationships,
|
||||
]
|
||||
);
|
||||
|
||||
const euiTabs: EuiTabbedContentTab[] = useMemo(
|
||||
() =>
|
||||
getTabs(indexPattern, fieldFilter).map((tab: Pick<EuiTabbedContentTab, 'name' | 'id'>) => {
|
||||
return {
|
||||
...tab,
|
||||
content: getContent(tab.id),
|
||||
};
|
||||
}),
|
||||
[fieldFilter, getContent, indexPattern]
|
||||
getTabs(indexPattern, fieldFilter, relationships.length).map(
|
||||
(tab: Pick<EuiTabbedContentTab, 'name' | 'id'>) => {
|
||||
return {
|
||||
...tab,
|
||||
content: getContent(tab.id),
|
||||
};
|
||||
}
|
||||
),
|
||||
[fieldFilter, getContent, indexPattern, relationships]
|
||||
);
|
||||
|
||||
const [selectedTabId, setSelectedTabId] = useState(euiTabs[0].id);
|
||||
|
|
|
@ -9,7 +9,12 @@
|
|||
import { Dictionary, countBy, defaults, uniq } from 'lodash';
|
||||
import { i18n } from '@kbn/i18n';
|
||||
import { DataView, DataViewField } from '@kbn/data-views-plugin/public';
|
||||
import { TAB_INDEXED_FIELDS, TAB_SCRIPTED_FIELDS, TAB_SOURCE_FILTERS } from '../constants';
|
||||
import {
|
||||
TAB_INDEXED_FIELDS,
|
||||
TAB_SCRIPTED_FIELDS,
|
||||
TAB_SOURCE_FILTERS,
|
||||
TAB_RELATIONSHIPS,
|
||||
} from '../constants';
|
||||
import { areScriptedFieldsEnabled } from '../../utils';
|
||||
|
||||
function filterByName(items: DataViewField[], filter: string) {
|
||||
|
@ -68,7 +73,7 @@ function getTitle(type: string, filteredCount: Dictionary<number>, totalCount: D
|
|||
return title + count;
|
||||
}
|
||||
|
||||
export function getTabs(indexPattern: DataView, fieldFilter: string) {
|
||||
export function getTabs(indexPattern: DataView, fieldFilter: string, relationshipCount = 0) {
|
||||
const totalCount = getCounts(indexPattern.fields.getAll(), indexPattern.getSourceFiltering());
|
||||
const filteredCount = getCounts(
|
||||
indexPattern.fields.getAll(),
|
||||
|
@ -98,6 +103,15 @@ export function getTabs(indexPattern: DataView, fieldFilter: string) {
|
|||
'data-test-subj': 'tab-sourceFilters',
|
||||
});
|
||||
|
||||
tabs.push({
|
||||
name: i18n.translate('indexPatternManagement.editIndexPattern.tabs.relationshipsHeader', {
|
||||
defaultMessage: 'Relationships ({count})',
|
||||
values: { count: relationshipCount },
|
||||
}),
|
||||
id: TAB_RELATIONSHIPS,
|
||||
'data-test-subj': 'tab-relationships',
|
||||
});
|
||||
|
||||
return tabs;
|
||||
}
|
||||
|
||||
|
|
|
@ -40,7 +40,16 @@ export async function mountManagementSection(
|
|||
) {
|
||||
const [
|
||||
{ application, chrome, uiSettings, notifications, overlays, http, docLinks, theme },
|
||||
{ data, dataViewFieldEditor, dataViewEditor, dataViews, fieldFormats, unifiedSearch, spaces },
|
||||
{
|
||||
data,
|
||||
dataViewFieldEditor,
|
||||
dataViewEditor,
|
||||
dataViews,
|
||||
fieldFormats,
|
||||
unifiedSearch,
|
||||
spaces,
|
||||
savedObjectsManagement,
|
||||
},
|
||||
indexPatternManagementStart,
|
||||
] = await getStartServices();
|
||||
const canSave = dataViews.getCanSaveSync();
|
||||
|
@ -68,6 +77,7 @@ export async function mountManagementSection(
|
|||
fieldFormats,
|
||||
spaces,
|
||||
theme,
|
||||
savedObjectsManagement,
|
||||
};
|
||||
|
||||
ReactDOM.render(
|
||||
|
|
|
@ -16,6 +16,7 @@ import { indexPatternFieldEditorPluginMock } from '@kbn/data-view-field-editor-p
|
|||
import { indexPatternEditorPluginMock } from '@kbn/data-view-editor-plugin/public/mocks';
|
||||
import { dataViewPluginMocks } from '@kbn/data-views-plugin/public/mocks';
|
||||
import { fieldFormatsServiceMock } from '@kbn/field-formats-plugin/public/mocks';
|
||||
import { savedObjectsManagementPluginMock } from '@kbn/saved-objects-management-plugin/public/mocks';
|
||||
import {
|
||||
IndexPatternManagementSetup,
|
||||
IndexPatternManagementStart,
|
||||
|
@ -63,6 +64,7 @@ const createIndexPatternManagmentContext = (): {
|
|||
const dataViewFieldEditor = indexPatternFieldEditorPluginMock.createStartContract();
|
||||
const dataViews = dataViewPluginMocks.createStartContract();
|
||||
const unifiedSearch = unifiedSearchPluginMock.createStartContract();
|
||||
const savedObjectsManagement = savedObjectsManagementPluginMock.createStartContract();
|
||||
|
||||
return {
|
||||
application,
|
||||
|
@ -83,6 +85,7 @@ const createIndexPatternManagmentContext = (): {
|
|||
indexPatternEditorPluginMock.createStartContract().IndexPatternEditorComponent,
|
||||
fieldFormats: fieldFormatsServiceMock.createStartContract(),
|
||||
theme,
|
||||
savedObjectsManagement,
|
||||
};
|
||||
};
|
||||
|
||||
|
|
|
@ -18,6 +18,7 @@ import { DataViewEditorStart } from '@kbn/data-view-editor-plugin/public';
|
|||
import { DataViewsPublicPluginStart } from '@kbn/data-views-plugin/public';
|
||||
import { SpacesPluginStart } from '@kbn/spaces-plugin/public';
|
||||
import { UnifiedSearchPublicPluginStart } from '@kbn/unified-search-plugin/public';
|
||||
import { SavedObjectsManagementPluginStart } from '@kbn/saved-objects-management-plugin/public';
|
||||
|
||||
export interface IndexPatternManagementSetupDependencies {
|
||||
management: ManagementSetup;
|
||||
|
@ -32,6 +33,7 @@ export interface IndexPatternManagementStartDependencies {
|
|||
fieldFormats: FieldFormatsStart;
|
||||
spaces?: SpacesPluginStart;
|
||||
unifiedSearch: UnifiedSearchPublicPluginStart;
|
||||
savedObjectsManagement: SavedObjectsManagementPluginStart;
|
||||
}
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/no-empty-interface
|
||||
|
@ -44,7 +46,7 @@ const sectionsHeader = i18n.translate('indexPatternManagement.dataView.sectionsH
|
|||
defaultMessage: 'Data Views',
|
||||
});
|
||||
|
||||
const IPM_APP_ID = 'dataViews';
|
||||
export const IPM_APP_ID = 'dataViews';
|
||||
|
||||
export class IndexPatternManagementPlugin
|
||||
implements
|
||||
|
|
|
@ -25,6 +25,7 @@ import { DataViewEditorStart } from '@kbn/data-view-editor-plugin/public';
|
|||
import { DataViewsPublicPluginStart } from '@kbn/data-views-plugin/public';
|
||||
import { FieldFormatsStart } from '@kbn/field-formats-plugin/public';
|
||||
import { SpacesPluginStart } from '@kbn/spaces-plugin/public';
|
||||
import { SavedObjectsManagementPluginStart } from '@kbn/saved-objects-management-plugin/public';
|
||||
import { IndexPatternManagementStart } from '.';
|
||||
|
||||
export interface IndexPatternManagmentContext {
|
||||
|
@ -46,6 +47,7 @@ export interface IndexPatternManagmentContext {
|
|||
fieldFormats: FieldFormatsStart;
|
||||
spaces?: SpacesPluginStart;
|
||||
theme: ThemeServiceStart;
|
||||
savedObjectsManagement: SavedObjectsManagementPluginStart;
|
||||
}
|
||||
|
||||
export type IndexPatternManagmentContextValue =
|
||||
|
|
|
@ -21,6 +21,7 @@
|
|||
{ "path": "../data_view_field_editor/tsconfig.json" },
|
||||
{ "path": "../data_view_editor/tsconfig.json" },
|
||||
{ "path": "../unified_search/tsconfig.json" },
|
||||
{ "path": "../saved_objects_management/tsconfig.json" },
|
||||
{ "path": "../../../x-pack/plugins/spaces/tsconfig.json" }
|
||||
]
|
||||
}
|
||||
|
|
|
@ -24,7 +24,12 @@ export type {
|
|||
export { SavedObjectsManagementAction } from './services';
|
||||
export type { ProcessedImportResponse, FailedImport } from './lib';
|
||||
export { processImportResponse } from './lib';
|
||||
export type { SavedObjectRelation, SavedObjectWithMetadata, SavedObjectMetadata } from './types';
|
||||
export type {
|
||||
SavedObjectRelation,
|
||||
SavedObjectWithMetadata,
|
||||
SavedObjectMetadata,
|
||||
SavedObjectManagementTypeInfo,
|
||||
} from './types';
|
||||
|
||||
export function plugin(initializerContext: PluginInitializerContext) {
|
||||
return new SavedObjectsManagementPlugin();
|
||||
|
|
|
@ -22,6 +22,10 @@ const createStartContractMock = (): jest.Mocked<SavedObjectsManagementPluginStar
|
|||
const mock = {
|
||||
actions: actionServiceMock.createStart(),
|
||||
columns: columnServiceMock.createStart(),
|
||||
getAllowedTypes: jest.fn(),
|
||||
getRelationships: jest.fn(),
|
||||
getSavedObjectLabel: jest.fn(),
|
||||
getDefaultTitle: jest.fn(),
|
||||
};
|
||||
return mock;
|
||||
};
|
||||
|
|
|
@ -23,6 +23,9 @@ import {
|
|||
SavedObjectsManagementColumnServiceStart,
|
||||
} from './services';
|
||||
|
||||
import { SavedObjectManagementTypeInfo, SavedObjectGetRelationshipsResponse } from './types';
|
||||
import { getAllowedTypes, getRelationships, getSavedObjectLabel, getDefaultTitle } from './lib';
|
||||
|
||||
export interface SavedObjectsManagementPluginSetup {
|
||||
actions: SavedObjectsManagementActionServiceSetup;
|
||||
columns: SavedObjectsManagementColumnServiceSetup;
|
||||
|
@ -31,6 +34,14 @@ export interface SavedObjectsManagementPluginSetup {
|
|||
export interface SavedObjectsManagementPluginStart {
|
||||
actions: SavedObjectsManagementActionServiceStart;
|
||||
columns: SavedObjectsManagementColumnServiceStart;
|
||||
getAllowedTypes: () => Promise<SavedObjectManagementTypeInfo[]>;
|
||||
getRelationships: (
|
||||
type: string,
|
||||
id: string,
|
||||
savedObjectTypes: string[]
|
||||
) => Promise<SavedObjectGetRelationshipsResponse>;
|
||||
getSavedObjectLabel: typeof getSavedObjectLabel;
|
||||
getDefaultTitle: typeof getDefaultTitle;
|
||||
}
|
||||
|
||||
export interface SetupDependencies {
|
||||
|
@ -109,6 +120,11 @@ export class SavedObjectsManagementPlugin
|
|||
return {
|
||||
actions: actionStart,
|
||||
columns: columnStart,
|
||||
getAllowedTypes: () => getAllowedTypes(_core.http),
|
||||
getRelationships: (type: string, id: string, savedObjectTypes: string[]) =>
|
||||
getRelationships(_core.http, type, id, savedObjectTypes),
|
||||
getSavedObjectLabel,
|
||||
getDefaultTitle,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
|
|
@ -13,4 +13,5 @@ export type {
|
|||
SavedObjectRelation,
|
||||
SavedObjectInvalidRelation,
|
||||
SavedObjectGetRelationshipsResponse,
|
||||
SavedObjectManagementTypeInfo,
|
||||
} from '../common';
|
||||
|
|
35
test/functional/apps/management/_data_view_relationships.ts
Normal file
35
test/functional/apps/management/_data_view_relationships.ts
Normal file
|
@ -0,0 +1,35 @@
|
|||
/*
|
||||
* 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 expect from '@kbn/expect';
|
||||
import { FtrProviderContext } from '../../ftr_provider_context';
|
||||
|
||||
export default function ({ getService, getPageObjects }: FtrProviderContext) {
|
||||
const kibanaServer = getService('kibanaServer');
|
||||
const browser = getService('browser');
|
||||
const PageObjects = getPageObjects(['common', 'home', 'settings', 'discover', 'header']);
|
||||
|
||||
describe('data view relationships', function describeIndexTests() {
|
||||
before(async function () {
|
||||
await browser.setWindowSize(1200, 800);
|
||||
await kibanaServer.importExport.load('test/functional/fixtures/kbn_archiver/discover');
|
||||
});
|
||||
|
||||
after(async () => {
|
||||
await kibanaServer.importExport.unload('test/functional/fixtures/kbn_archiver/discover');
|
||||
});
|
||||
|
||||
it('Render relationships tab and verify count', async function () {
|
||||
await PageObjects.settings.navigateTo();
|
||||
await PageObjects.settings.clickKibanaIndexPatterns();
|
||||
await PageObjects.settings.clickIndexPatternLogstash();
|
||||
await PageObjects.settings.clickRelationshipsTab();
|
||||
expect(parseInt(await PageObjects.settings.getRelationshipsTabCount(), 10)).to.be(1);
|
||||
});
|
||||
});
|
||||
}
|
|
@ -40,5 +40,6 @@ export default function ({ getService, loadTestFile }: FtrProviderContext) {
|
|||
loadTestFile(require.resolve('./_test_huge_fields'));
|
||||
loadTestFile(require.resolve('./_handle_alias'));
|
||||
loadTestFile(require.resolve('./_handle_version_conflict'));
|
||||
loadTestFile(require.resolve('./_data_view_relationships'));
|
||||
});
|
||||
}
|
||||
|
|
|
@ -244,6 +244,13 @@ export class SettingsPageObject extends FtrService {
|
|||
});
|
||||
}
|
||||
|
||||
async getRelationshipsTabCount() {
|
||||
return await this.retry.try(async () => {
|
||||
const text = await this.testSubjects.getVisibleText('tab-relationships');
|
||||
return text.split(' ')[1].replace(/\((.*)\)/, '$1');
|
||||
});
|
||||
}
|
||||
|
||||
async getFieldNames() {
|
||||
const fieldNameCells = await this.testSubjects.findAll('editIndexPattern > indexedFieldName');
|
||||
return await Promise.all(
|
||||
|
@ -562,6 +569,11 @@ export class SettingsPageObject extends FtrService {
|
|||
await this.testSubjects.click('tab-sourceFilters');
|
||||
}
|
||||
|
||||
async clickRelationshipsTab() {
|
||||
this.log.debug('click Relationships tab');
|
||||
await this.testSubjects.click('tab-relationships');
|
||||
}
|
||||
|
||||
async editScriptedField(name: string) {
|
||||
await this.filterField(name);
|
||||
await this.find.clickByCssSelector('.euiTableRowCell--hasActions button:first-child');
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue