mirror of
https://github.com/elastic/kibana.git
synced 2025-04-23 09:19:04 -04:00
[SOM] display invalid references in the relationship flyout (#88814)
* return invalid relations and display them in SOM * add FTR test
This commit is contained in:
parent
f0717a0a79
commit
84d49f1123
21 changed files with 2093 additions and 770 deletions
|
@ -6,4 +6,11 @@
|
|||
* Public License, v 1.
|
||||
*/
|
||||
|
||||
export { SavedObjectRelation, SavedObjectWithMetadata, SavedObjectMetadata } from './types';
|
||||
export {
|
||||
SavedObjectWithMetadata,
|
||||
SavedObjectMetadata,
|
||||
SavedObjectRelation,
|
||||
SavedObjectRelationKind,
|
||||
SavedObjectInvalidRelation,
|
||||
SavedObjectGetRelationshipsResponse,
|
||||
} from './types';
|
||||
|
|
|
@ -28,12 +28,26 @@ export type SavedObjectWithMetadata<T = unknown> = SavedObject<T> & {
|
|||
meta: SavedObjectMetadata;
|
||||
};
|
||||
|
||||
export type SavedObjectRelationKind = 'child' | 'parent';
|
||||
|
||||
/**
|
||||
* Represents a relation between two {@link SavedObject | saved object}
|
||||
*/
|
||||
export interface SavedObjectRelation {
|
||||
id: string;
|
||||
type: string;
|
||||
relationship: 'child' | 'parent';
|
||||
relationship: SavedObjectRelationKind;
|
||||
meta: SavedObjectMetadata;
|
||||
}
|
||||
|
||||
export interface SavedObjectInvalidRelation {
|
||||
id: string;
|
||||
type: string;
|
||||
relationship: SavedObjectRelationKind;
|
||||
error: string;
|
||||
}
|
||||
|
||||
export interface SavedObjectGetRelationshipsResponse {
|
||||
relations: SavedObjectRelation[];
|
||||
invalidRelations: SavedObjectInvalidRelation[];
|
||||
}
|
||||
|
|
|
@ -6,6 +6,7 @@
|
|||
* Public License, v 1.
|
||||
*/
|
||||
|
||||
import { SavedObjectGetRelationshipsResponse } from '../types';
|
||||
import { httpServiceMock } from '../../../../core/public/mocks';
|
||||
import { getRelationships } from './get_relationships';
|
||||
|
||||
|
@ -22,13 +23,17 @@ describe('getRelationships', () => {
|
|||
});
|
||||
|
||||
it('should handle successful responses', async () => {
|
||||
httpMock.get.mockResolvedValue([1, 2]);
|
||||
const serverResponse: SavedObjectGetRelationshipsResponse = {
|
||||
relations: [],
|
||||
invalidRelations: [],
|
||||
};
|
||||
httpMock.get.mockResolvedValue(serverResponse);
|
||||
|
||||
const response = await getRelationships(httpMock, 'dashboard', '1', [
|
||||
'search',
|
||||
'index-pattern',
|
||||
]);
|
||||
expect(response).toEqual([1, 2]);
|
||||
expect(response).toEqual(serverResponse);
|
||||
});
|
||||
|
||||
it('should handle errors', async () => {
|
||||
|
|
|
@ -8,19 +8,19 @@
|
|||
|
||||
import { HttpStart } from 'src/core/public';
|
||||
import { get } from 'lodash';
|
||||
import { SavedObjectRelation } from '../types';
|
||||
import { SavedObjectGetRelationshipsResponse } from '../types';
|
||||
|
||||
export async function getRelationships(
|
||||
http: HttpStart,
|
||||
type: string,
|
||||
id: string,
|
||||
savedObjectTypes: string[]
|
||||
): Promise<SavedObjectRelation[]> {
|
||||
): Promise<SavedObjectGetRelationshipsResponse> {
|
||||
const url = `/api/kibana/management/saved_objects/relationships/${encodeURIComponent(
|
||||
type
|
||||
)}/${encodeURIComponent(id)}`;
|
||||
try {
|
||||
return await http.get<SavedObjectRelation[]>(url, {
|
||||
return await http.get<SavedObjectGetRelationshipsResponse>(url, {
|
||||
query: {
|
||||
savedObjectTypes,
|
||||
},
|
||||
|
|
File diff suppressed because it is too large
Load diff
|
@ -25,36 +25,39 @@ describe('Relationships', () => {
|
|||
goInspectObject: () => {},
|
||||
canGoInApp: () => true,
|
||||
basePath: httpServiceMock.createSetupContract().basePath,
|
||||
getRelationships: jest.fn().mockImplementation(() => [
|
||||
{
|
||||
type: 'search',
|
||||
id: '1',
|
||||
relationship: 'parent',
|
||||
meta: {
|
||||
editUrl: '/management/kibana/objects/savedSearches/1',
|
||||
icon: 'search',
|
||||
inAppUrl: {
|
||||
path: '/app/discover#//1',
|
||||
uiCapabilitiesPath: 'discover.show',
|
||||
getRelationships: jest.fn().mockImplementation(() => ({
|
||||
relations: [
|
||||
{
|
||||
type: 'search',
|
||||
id: '1',
|
||||
relationship: 'parent',
|
||||
meta: {
|
||||
editUrl: '/management/kibana/objects/savedSearches/1',
|
||||
icon: 'search',
|
||||
inAppUrl: {
|
||||
path: '/app/discover#//1',
|
||||
uiCapabilitiesPath: 'discover.show',
|
||||
},
|
||||
title: 'My Search Title',
|
||||
},
|
||||
title: 'My Search Title',
|
||||
},
|
||||
},
|
||||
{
|
||||
type: 'visualization',
|
||||
id: '2',
|
||||
relationship: 'parent',
|
||||
meta: {
|
||||
editUrl: '/management/kibana/objects/savedVisualizations/2',
|
||||
icon: 'visualizeApp',
|
||||
inAppUrl: {
|
||||
path: '/app/visualize#/edit/2',
|
||||
uiCapabilitiesPath: 'visualize.show',
|
||||
{
|
||||
type: 'visualization',
|
||||
id: '2',
|
||||
relationship: 'parent',
|
||||
meta: {
|
||||
editUrl: '/management/kibana/objects/savedVisualizations/2',
|
||||
icon: 'visualizeApp',
|
||||
inAppUrl: {
|
||||
path: '/app/visualize#/edit/2',
|
||||
uiCapabilitiesPath: 'visualize.show',
|
||||
},
|
||||
title: 'My Visualization Title',
|
||||
},
|
||||
title: 'My Visualization Title',
|
||||
},
|
||||
},
|
||||
]),
|
||||
],
|
||||
invalidRelations: [],
|
||||
})),
|
||||
savedObject: {
|
||||
id: '1',
|
||||
type: 'index-pattern',
|
||||
|
@ -92,36 +95,39 @@ describe('Relationships', () => {
|
|||
goInspectObject: () => {},
|
||||
canGoInApp: () => true,
|
||||
basePath: httpServiceMock.createSetupContract().basePath,
|
||||
getRelationships: jest.fn().mockImplementation(() => [
|
||||
{
|
||||
type: 'index-pattern',
|
||||
id: '1',
|
||||
relationship: 'child',
|
||||
meta: {
|
||||
editUrl: '/management/kibana/indexPatterns/patterns/1',
|
||||
icon: 'indexPatternApp',
|
||||
inAppUrl: {
|
||||
path: '/app/management/kibana/indexPatterns/patterns/1',
|
||||
uiCapabilitiesPath: 'management.kibana.indexPatterns',
|
||||
getRelationships: jest.fn().mockImplementation(() => ({
|
||||
relations: [
|
||||
{
|
||||
type: 'index-pattern',
|
||||
id: '1',
|
||||
relationship: 'child',
|
||||
meta: {
|
||||
editUrl: '/management/kibana/indexPatterns/patterns/1',
|
||||
icon: 'indexPatternApp',
|
||||
inAppUrl: {
|
||||
path: '/app/management/kibana/indexPatterns/patterns/1',
|
||||
uiCapabilitiesPath: 'management.kibana.indexPatterns',
|
||||
},
|
||||
title: 'My Index Pattern',
|
||||
},
|
||||
title: 'My Index Pattern',
|
||||
},
|
||||
},
|
||||
{
|
||||
type: 'visualization',
|
||||
id: '2',
|
||||
relationship: 'parent',
|
||||
meta: {
|
||||
editUrl: '/management/kibana/objects/savedVisualizations/2',
|
||||
icon: 'visualizeApp',
|
||||
inAppUrl: {
|
||||
path: '/app/visualize#/edit/2',
|
||||
uiCapabilitiesPath: 'visualize.show',
|
||||
{
|
||||
type: 'visualization',
|
||||
id: '2',
|
||||
relationship: 'parent',
|
||||
meta: {
|
||||
editUrl: '/management/kibana/objects/savedVisualizations/2',
|
||||
icon: 'visualizeApp',
|
||||
inAppUrl: {
|
||||
path: '/app/visualize#/edit/2',
|
||||
uiCapabilitiesPath: 'visualize.show',
|
||||
},
|
||||
title: 'My Visualization Title',
|
||||
},
|
||||
title: 'My Visualization Title',
|
||||
},
|
||||
},
|
||||
]),
|
||||
],
|
||||
invalidRelations: [],
|
||||
})),
|
||||
savedObject: {
|
||||
id: '1',
|
||||
type: 'search',
|
||||
|
@ -159,36 +165,39 @@ describe('Relationships', () => {
|
|||
goInspectObject: () => {},
|
||||
canGoInApp: () => true,
|
||||
basePath: httpServiceMock.createSetupContract().basePath,
|
||||
getRelationships: jest.fn().mockImplementation(() => [
|
||||
{
|
||||
type: 'dashboard',
|
||||
id: '1',
|
||||
relationship: 'parent',
|
||||
meta: {
|
||||
editUrl: '/management/kibana/objects/savedDashboards/1',
|
||||
icon: 'dashboardApp',
|
||||
inAppUrl: {
|
||||
path: '/app/kibana#/dashboard/1',
|
||||
uiCapabilitiesPath: 'dashboard.show',
|
||||
getRelationships: jest.fn().mockImplementation(() => ({
|
||||
relations: [
|
||||
{
|
||||
type: 'dashboard',
|
||||
id: '1',
|
||||
relationship: 'parent',
|
||||
meta: {
|
||||
editUrl: '/management/kibana/objects/savedDashboards/1',
|
||||
icon: 'dashboardApp',
|
||||
inAppUrl: {
|
||||
path: '/app/kibana#/dashboard/1',
|
||||
uiCapabilitiesPath: 'dashboard.show',
|
||||
},
|
||||
title: 'My Dashboard 1',
|
||||
},
|
||||
title: 'My Dashboard 1',
|
||||
},
|
||||
},
|
||||
{
|
||||
type: 'dashboard',
|
||||
id: '2',
|
||||
relationship: 'parent',
|
||||
meta: {
|
||||
editUrl: '/management/kibana/objects/savedDashboards/2',
|
||||
icon: 'dashboardApp',
|
||||
inAppUrl: {
|
||||
path: '/app/kibana#/dashboard/2',
|
||||
uiCapabilitiesPath: 'dashboard.show',
|
||||
{
|
||||
type: 'dashboard',
|
||||
id: '2',
|
||||
relationship: 'parent',
|
||||
meta: {
|
||||
editUrl: '/management/kibana/objects/savedDashboards/2',
|
||||
icon: 'dashboardApp',
|
||||
inAppUrl: {
|
||||
path: '/app/kibana#/dashboard/2',
|
||||
uiCapabilitiesPath: 'dashboard.show',
|
||||
},
|
||||
title: 'My Dashboard 2',
|
||||
},
|
||||
title: 'My Dashboard 2',
|
||||
},
|
||||
},
|
||||
]),
|
||||
],
|
||||
invalidRelations: [],
|
||||
})),
|
||||
savedObject: {
|
||||
id: '1',
|
||||
type: 'visualization',
|
||||
|
@ -226,36 +235,39 @@ describe('Relationships', () => {
|
|||
goInspectObject: () => {},
|
||||
canGoInApp: () => true,
|
||||
basePath: httpServiceMock.createSetupContract().basePath,
|
||||
getRelationships: jest.fn().mockImplementation(() => [
|
||||
{
|
||||
type: 'visualization',
|
||||
id: '1',
|
||||
relationship: 'child',
|
||||
meta: {
|
||||
editUrl: '/management/kibana/objects/savedVisualizations/1',
|
||||
icon: 'visualizeApp',
|
||||
inAppUrl: {
|
||||
path: '/app/visualize#/edit/1',
|
||||
uiCapabilitiesPath: 'visualize.show',
|
||||
getRelationships: jest.fn().mockImplementation(() => ({
|
||||
relations: [
|
||||
{
|
||||
type: 'visualization',
|
||||
id: '1',
|
||||
relationship: 'child',
|
||||
meta: {
|
||||
editUrl: '/management/kibana/objects/savedVisualizations/1',
|
||||
icon: 'visualizeApp',
|
||||
inAppUrl: {
|
||||
path: '/app/visualize#/edit/1',
|
||||
uiCapabilitiesPath: 'visualize.show',
|
||||
},
|
||||
title: 'My Visualization Title 1',
|
||||
},
|
||||
title: 'My Visualization Title 1',
|
||||
},
|
||||
},
|
||||
{
|
||||
type: 'visualization',
|
||||
id: '2',
|
||||
relationship: 'child',
|
||||
meta: {
|
||||
editUrl: '/management/kibana/objects/savedVisualizations/2',
|
||||
icon: 'visualizeApp',
|
||||
inAppUrl: {
|
||||
path: '/app/visualize#/edit/2',
|
||||
uiCapabilitiesPath: 'visualize.show',
|
||||
{
|
||||
type: 'visualization',
|
||||
id: '2',
|
||||
relationship: 'child',
|
||||
meta: {
|
||||
editUrl: '/management/kibana/objects/savedVisualizations/2',
|
||||
icon: 'visualizeApp',
|
||||
inAppUrl: {
|
||||
path: '/app/visualize#/edit/2',
|
||||
uiCapabilitiesPath: 'visualize.show',
|
||||
},
|
||||
title: 'My Visualization Title 2',
|
||||
},
|
||||
title: 'My Visualization Title 2',
|
||||
},
|
||||
},
|
||||
]),
|
||||
],
|
||||
invalidRelations: [],
|
||||
})),
|
||||
savedObject: {
|
||||
id: '1',
|
||||
type: 'dashboard',
|
||||
|
@ -324,4 +336,49 @@ describe('Relationships', () => {
|
|||
expect(props.getRelationships).toHaveBeenCalled();
|
||||
expect(component).toMatchSnapshot();
|
||||
});
|
||||
|
||||
it('should render invalid relations', async () => {
|
||||
const props: RelationshipsProps = {
|
||||
goInspectObject: () => {},
|
||||
canGoInApp: () => true,
|
||||
basePath: httpServiceMock.createSetupContract().basePath,
|
||||
getRelationships: jest.fn().mockImplementation(() => ({
|
||||
relations: [],
|
||||
invalidRelations: [
|
||||
{
|
||||
id: '1',
|
||||
type: 'dashboard',
|
||||
relationship: 'child',
|
||||
error: 'Saved object [dashboard/1] not found',
|
||||
},
|
||||
],
|
||||
})),
|
||||
savedObject: {
|
||||
id: '1',
|
||||
type: 'index-pattern',
|
||||
attributes: {},
|
||||
references: [],
|
||||
meta: {
|
||||
title: 'MyIndexPattern*',
|
||||
icon: 'indexPatternApp',
|
||||
editUrl: '#/management/kibana/indexPatterns/patterns/1',
|
||||
inAppUrl: {
|
||||
path: '/management/kibana/indexPatterns/patterns/1',
|
||||
uiCapabilitiesPath: 'management.kibana.indexPatterns',
|
||||
},
|
||||
},
|
||||
},
|
||||
close: jest.fn(),
|
||||
};
|
||||
|
||||
const component = shallowWithI18nProvider(<Relationships {...props} />);
|
||||
|
||||
// Ensure all promises resolve
|
||||
await new Promise((resolve) => process.nextTick(resolve));
|
||||
// Ensure the state changes are reflected
|
||||
component.update();
|
||||
|
||||
expect(props.getRelationships).toHaveBeenCalled();
|
||||
expect(component).toMatchSnapshot();
|
||||
});
|
||||
});
|
||||
|
|
|
@ -26,11 +26,17 @@ import { i18n } from '@kbn/i18n';
|
|||
import { FormattedMessage } from '@kbn/i18n/react';
|
||||
import { IBasePath } from 'src/core/public';
|
||||
import { getDefaultTitle, getSavedObjectLabel } from '../../../lib';
|
||||
import { SavedObjectWithMetadata, SavedObjectRelation } from '../../../types';
|
||||
import {
|
||||
SavedObjectWithMetadata,
|
||||
SavedObjectRelationKind,
|
||||
SavedObjectRelation,
|
||||
SavedObjectInvalidRelation,
|
||||
SavedObjectGetRelationshipsResponse,
|
||||
} from '../../../types';
|
||||
|
||||
export interface RelationshipsProps {
|
||||
basePath: IBasePath;
|
||||
getRelationships: (type: string, id: string) => Promise<SavedObjectRelation[]>;
|
||||
getRelationships: (type: string, id: string) => Promise<SavedObjectGetRelationshipsResponse>;
|
||||
savedObject: SavedObjectWithMetadata;
|
||||
close: () => void;
|
||||
goInspectObject: (obj: SavedObjectWithMetadata) => void;
|
||||
|
@ -38,17 +44,47 @@ export interface RelationshipsProps {
|
|||
}
|
||||
|
||||
export interface RelationshipsState {
|
||||
relationships: SavedObjectRelation[];
|
||||
relations: SavedObjectRelation[];
|
||||
invalidRelations: SavedObjectInvalidRelation[];
|
||||
isLoading: boolean;
|
||||
error?: string;
|
||||
}
|
||||
|
||||
const relationshipColumn = {
|
||||
field: 'relationship',
|
||||
name: i18n.translate('savedObjectsManagement.objectsTable.relationships.columnRelationshipName', {
|
||||
defaultMessage: 'Direct relationship',
|
||||
}),
|
||||
dataType: 'string',
|
||||
sortable: false,
|
||||
width: '125px',
|
||||
'data-test-subj': 'directRelationship',
|
||||
render: (relationship: SavedObjectRelationKind) => {
|
||||
return (
|
||||
<EuiText size="s">
|
||||
{relationship === 'parent' ? (
|
||||
<FormattedMessage
|
||||
id="savedObjectsManagement.objectsTable.relationships.columnRelationship.parentAsValue"
|
||||
defaultMessage="Parent"
|
||||
/>
|
||||
) : (
|
||||
<FormattedMessage
|
||||
id="savedObjectsManagement.objectsTable.relationships.columnRelationship.childAsValue"
|
||||
defaultMessage="Child"
|
||||
/>
|
||||
)}
|
||||
</EuiText>
|
||||
);
|
||||
},
|
||||
};
|
||||
|
||||
export class Relationships extends Component<RelationshipsProps, RelationshipsState> {
|
||||
constructor(props: RelationshipsProps) {
|
||||
super(props);
|
||||
|
||||
this.state = {
|
||||
relationships: [],
|
||||
relations: [],
|
||||
invalidRelations: [],
|
||||
isLoading: false,
|
||||
error: undefined,
|
||||
};
|
||||
|
@ -70,8 +106,11 @@ export class Relationships extends Component<RelationshipsProps, RelationshipsSt
|
|||
this.setState({ isLoading: true });
|
||||
|
||||
try {
|
||||
const relationships = await getRelationships(savedObject.type, savedObject.id);
|
||||
this.setState({ relationships, isLoading: false, error: undefined });
|
||||
const { relations, invalidRelations } = await getRelationships(
|
||||
savedObject.type,
|
||||
savedObject.id
|
||||
);
|
||||
this.setState({ relations, invalidRelations, isLoading: false, error: undefined });
|
||||
} catch (err) {
|
||||
this.setState({ error: err.message, isLoading: false });
|
||||
}
|
||||
|
@ -99,9 +138,83 @@ export class Relationships extends Component<RelationshipsProps, RelationshipsSt
|
|||
);
|
||||
}
|
||||
|
||||
renderRelationships() {
|
||||
const { goInspectObject, savedObject, basePath } = this.props;
|
||||
const { relationships, isLoading, error } = this.state;
|
||||
renderInvalidRelationship() {
|
||||
const { invalidRelations } = this.state;
|
||||
if (!invalidRelations.length) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const columns = [
|
||||
{
|
||||
field: 'type',
|
||||
name: i18n.translate('savedObjectsManagement.objectsTable.relationships.columnTypeName', {
|
||||
defaultMessage: 'Type',
|
||||
}),
|
||||
width: '150px',
|
||||
description: i18n.translate(
|
||||
'savedObjectsManagement.objectsTable.relationships.columnTypeDescription',
|
||||
{ defaultMessage: 'Type of the saved object' }
|
||||
),
|
||||
sortable: false,
|
||||
'data-test-subj': 'relationshipsObjectType',
|
||||
},
|
||||
{
|
||||
field: 'id',
|
||||
name: i18n.translate('savedObjectsManagement.objectsTable.relationships.columnIdName', {
|
||||
defaultMessage: 'Id',
|
||||
}),
|
||||
width: '150px',
|
||||
description: i18n.translate(
|
||||
'savedObjectsManagement.objectsTable.relationships.columnIdDescription',
|
||||
{ defaultMessage: 'Id of the saved object' }
|
||||
),
|
||||
sortable: false,
|
||||
'data-test-subj': 'relationshipsObjectId',
|
||||
},
|
||||
relationshipColumn,
|
||||
{
|
||||
field: 'error',
|
||||
name: i18n.translate('savedObjectsManagement.objectsTable.relationships.columnErrorName', {
|
||||
defaultMessage: 'Error',
|
||||
}),
|
||||
description: i18n.translate(
|
||||
'savedObjectsManagement.objectsTable.relationships.columnErrorDescription',
|
||||
{ defaultMessage: 'Error encountered with the relation' }
|
||||
),
|
||||
sortable: false,
|
||||
'data-test-subj': 'relationshipsError',
|
||||
},
|
||||
];
|
||||
|
||||
return (
|
||||
<>
|
||||
<EuiCallOut
|
||||
color="warning"
|
||||
iconType="alert"
|
||||
title={i18n.translate(
|
||||
'savedObjectsManagement.objectsTable.relationships.invalidRelationShip',
|
||||
{
|
||||
defaultMessage: 'This saved object has some invalid relations.',
|
||||
}
|
||||
)}
|
||||
/>
|
||||
<EuiSpacer />
|
||||
<EuiInMemoryTable
|
||||
items={invalidRelations}
|
||||
columns={columns as any}
|
||||
pagination={true}
|
||||
rowProps={() => ({
|
||||
'data-test-subj': `invalidRelationshipsTableRow`,
|
||||
})}
|
||||
/>
|
||||
<EuiSpacer />
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
renderRelationshipsTable() {
|
||||
const { goInspectObject, basePath, savedObject } = this.props;
|
||||
const { relations, isLoading, error } = this.state;
|
||||
|
||||
if (error) {
|
||||
return this.renderError();
|
||||
|
@ -137,39 +250,7 @@ export class Relationships extends Component<RelationshipsProps, RelationshipsSt
|
|||
);
|
||||
},
|
||||
},
|
||||
{
|
||||
field: 'relationship',
|
||||
name: i18n.translate(
|
||||
'savedObjectsManagement.objectsTable.relationships.columnRelationshipName',
|
||||
{ defaultMessage: 'Direct relationship' }
|
||||
),
|
||||
dataType: 'string',
|
||||
sortable: false,
|
||||
width: '125px',
|
||||
'data-test-subj': 'directRelationship',
|
||||
render: (relationship: string) => {
|
||||
if (relationship === 'parent') {
|
||||
return (
|
||||
<EuiText size="s">
|
||||
<FormattedMessage
|
||||
id="savedObjectsManagement.objectsTable.relationships.columnRelationship.parentAsValue"
|
||||
defaultMessage="Parent"
|
||||
/>
|
||||
</EuiText>
|
||||
);
|
||||
}
|
||||
if (relationship === 'child') {
|
||||
return (
|
||||
<EuiText size="s">
|
||||
<FormattedMessage
|
||||
id="savedObjectsManagement.objectsTable.relationships.columnRelationship.childAsValue"
|
||||
defaultMessage="Child"
|
||||
/>
|
||||
</EuiText>
|
||||
);
|
||||
}
|
||||
},
|
||||
},
|
||||
relationshipColumn,
|
||||
{
|
||||
field: 'meta.title',
|
||||
name: i18n.translate('savedObjectsManagement.objectsTable.relationships.columnTitleName', {
|
||||
|
@ -224,7 +305,7 @@ export class Relationships extends Component<RelationshipsProps, RelationshipsSt
|
|||
];
|
||||
|
||||
const filterTypesMap = new Map(
|
||||
relationships.map((relationship) => [
|
||||
relations.map((relationship) => [
|
||||
relationship.type,
|
||||
{
|
||||
value: relationship.type,
|
||||
|
@ -277,7 +358,7 @@ export class Relationships extends Component<RelationshipsProps, RelationshipsSt
|
|||
};
|
||||
|
||||
return (
|
||||
<div>
|
||||
<>
|
||||
<EuiCallOut>
|
||||
<p>
|
||||
{i18n.translate(
|
||||
|
@ -296,7 +377,7 @@ export class Relationships extends Component<RelationshipsProps, RelationshipsSt
|
|||
</EuiCallOut>
|
||||
<EuiSpacer />
|
||||
<EuiInMemoryTable
|
||||
items={relationships}
|
||||
items={relations}
|
||||
columns={columns as any}
|
||||
pagination={true}
|
||||
search={search}
|
||||
|
@ -304,7 +385,7 @@ export class Relationships extends Component<RelationshipsProps, RelationshipsSt
|
|||
'data-test-subj': `relationshipsTableRow`,
|
||||
})}
|
||||
/>
|
||||
</div>
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -328,8 +409,10 @@ export class Relationships extends Component<RelationshipsProps, RelationshipsSt
|
|||
</h2>
|
||||
</EuiTitle>
|
||||
</EuiFlyoutHeader>
|
||||
|
||||
<EuiFlyoutBody>{this.renderRelationships()}</EuiFlyoutBody>
|
||||
<EuiFlyoutBody>
|
||||
{this.renderInvalidRelationship()}
|
||||
{this.renderRelationshipsTable()}
|
||||
</EuiFlyoutBody>
|
||||
</EuiFlyout>
|
||||
);
|
||||
}
|
||||
|
|
|
@ -6,4 +6,11 @@
|
|||
* Public License, v 1.
|
||||
*/
|
||||
|
||||
export { SavedObjectMetadata, SavedObjectWithMetadata, SavedObjectRelation } from '../common';
|
||||
export {
|
||||
SavedObjectMetadata,
|
||||
SavedObjectWithMetadata,
|
||||
SavedObjectRelationKind,
|
||||
SavedObjectRelation,
|
||||
SavedObjectInvalidRelation,
|
||||
SavedObjectGetRelationshipsResponse,
|
||||
} from '../common';
|
||||
|
|
|
@ -6,10 +6,35 @@
|
|||
* Public License, v 1.
|
||||
*/
|
||||
|
||||
import type { SavedObject, SavedObjectError } from 'src/core/types';
|
||||
import type { SavedObjectsFindResponse } from 'src/core/server';
|
||||
import { findRelationships } from './find_relationships';
|
||||
import { managementMock } from '../services/management.mock';
|
||||
import { savedObjectsClientMock } from '../../../../core/server/mocks';
|
||||
|
||||
const createObj = (parts: Partial<SavedObject<any>>): SavedObject<any> => ({
|
||||
id: 'id',
|
||||
type: 'type',
|
||||
attributes: {},
|
||||
references: [],
|
||||
...parts,
|
||||
});
|
||||
|
||||
const createFindResponse = (objs: SavedObject[]): SavedObjectsFindResponse => ({
|
||||
saved_objects: objs.map((obj) => ({ ...obj, score: 1 })),
|
||||
total: objs.length,
|
||||
per_page: 20,
|
||||
page: 1,
|
||||
});
|
||||
|
||||
const createError = (parts: Partial<SavedObjectError>): SavedObjectError => ({
|
||||
error: 'error',
|
||||
message: 'message',
|
||||
metadata: {},
|
||||
statusCode: 404,
|
||||
...parts,
|
||||
});
|
||||
|
||||
describe('findRelationships', () => {
|
||||
let savedObjectsClient: ReturnType<typeof savedObjectsClientMock.create>;
|
||||
let managementService: ReturnType<typeof managementMock.create>;
|
||||
|
@ -19,7 +44,7 @@ describe('findRelationships', () => {
|
|||
managementService = managementMock.create();
|
||||
});
|
||||
|
||||
it('returns the child and parent references of the object', async () => {
|
||||
it('calls the savedObjectClient APIs with the correct parameters', async () => {
|
||||
const type = 'dashboard';
|
||||
const id = 'some-id';
|
||||
const references = [
|
||||
|
@ -36,46 +61,35 @@ describe('findRelationships', () => {
|
|||
];
|
||||
const referenceTypes = ['some-type', 'another-type'];
|
||||
|
||||
savedObjectsClient.get.mockResolvedValue({
|
||||
id,
|
||||
type,
|
||||
attributes: {},
|
||||
references,
|
||||
});
|
||||
|
||||
savedObjectsClient.get.mockResolvedValue(
|
||||
createObj({
|
||||
id,
|
||||
type,
|
||||
references,
|
||||
})
|
||||
);
|
||||
savedObjectsClient.bulkGet.mockResolvedValue({
|
||||
saved_objects: [
|
||||
{
|
||||
createObj({
|
||||
type: 'some-type',
|
||||
id: 'ref-1',
|
||||
attributes: {},
|
||||
references: [],
|
||||
},
|
||||
{
|
||||
}),
|
||||
createObj({
|
||||
type: 'another-type',
|
||||
id: 'ref-2',
|
||||
attributes: {},
|
||||
references: [],
|
||||
},
|
||||
}),
|
||||
],
|
||||
});
|
||||
|
||||
savedObjectsClient.find.mockResolvedValue({
|
||||
saved_objects: [
|
||||
{
|
||||
savedObjectsClient.find.mockResolvedValue(
|
||||
createFindResponse([
|
||||
createObj({
|
||||
type: 'parent-type',
|
||||
id: 'parent-id',
|
||||
attributes: {},
|
||||
score: 1,
|
||||
references: [],
|
||||
},
|
||||
],
|
||||
total: 1,
|
||||
per_page: 20,
|
||||
page: 1,
|
||||
});
|
||||
}),
|
||||
])
|
||||
);
|
||||
|
||||
const relationships = await findRelationships({
|
||||
await findRelationships({
|
||||
type,
|
||||
id,
|
||||
size: 20,
|
||||
|
@ -101,8 +115,63 @@ describe('findRelationships', () => {
|
|||
perPage: 20,
|
||||
type: referenceTypes,
|
||||
});
|
||||
});
|
||||
|
||||
expect(relationships).toEqual([
|
||||
it('returns the child and parent references of the object', async () => {
|
||||
const type = 'dashboard';
|
||||
const id = 'some-id';
|
||||
const references = [
|
||||
{
|
||||
type: 'some-type',
|
||||
id: 'ref-1',
|
||||
name: 'ref 1',
|
||||
},
|
||||
{
|
||||
type: 'another-type',
|
||||
id: 'ref-2',
|
||||
name: 'ref 2',
|
||||
},
|
||||
];
|
||||
const referenceTypes = ['some-type', 'another-type'];
|
||||
|
||||
savedObjectsClient.get.mockResolvedValue(
|
||||
createObj({
|
||||
id,
|
||||
type,
|
||||
references,
|
||||
})
|
||||
);
|
||||
savedObjectsClient.bulkGet.mockResolvedValue({
|
||||
saved_objects: [
|
||||
createObj({
|
||||
type: 'some-type',
|
||||
id: 'ref-1',
|
||||
}),
|
||||
createObj({
|
||||
type: 'another-type',
|
||||
id: 'ref-2',
|
||||
}),
|
||||
],
|
||||
});
|
||||
savedObjectsClient.find.mockResolvedValue(
|
||||
createFindResponse([
|
||||
createObj({
|
||||
type: 'parent-type',
|
||||
id: 'parent-id',
|
||||
}),
|
||||
])
|
||||
);
|
||||
|
||||
const { relations, invalidRelations } = await findRelationships({
|
||||
type,
|
||||
id,
|
||||
size: 20,
|
||||
client: savedObjectsClient,
|
||||
referenceTypes,
|
||||
savedObjectsManagement: managementService,
|
||||
});
|
||||
|
||||
expect(relations).toEqual([
|
||||
{
|
||||
id: 'ref-1',
|
||||
relationship: 'child',
|
||||
|
@ -122,6 +191,70 @@ describe('findRelationships', () => {
|
|||
meta: expect.any(Object),
|
||||
},
|
||||
]);
|
||||
expect(invalidRelations).toHaveLength(0);
|
||||
});
|
||||
|
||||
it('returns the invalid relations', async () => {
|
||||
const type = 'dashboard';
|
||||
const id = 'some-id';
|
||||
const references = [
|
||||
{
|
||||
type: 'some-type',
|
||||
id: 'ref-1',
|
||||
name: 'ref 1',
|
||||
},
|
||||
{
|
||||
type: 'another-type',
|
||||
id: 'ref-2',
|
||||
name: 'ref 2',
|
||||
},
|
||||
];
|
||||
const referenceTypes = ['some-type', 'another-type'];
|
||||
|
||||
savedObjectsClient.get.mockResolvedValue(
|
||||
createObj({
|
||||
id,
|
||||
type,
|
||||
references,
|
||||
})
|
||||
);
|
||||
const ref1Error = createError({ message: 'Not found' });
|
||||
savedObjectsClient.bulkGet.mockResolvedValue({
|
||||
saved_objects: [
|
||||
createObj({
|
||||
type: 'some-type',
|
||||
id: 'ref-1',
|
||||
error: ref1Error,
|
||||
}),
|
||||
createObj({
|
||||
type: 'another-type',
|
||||
id: 'ref-2',
|
||||
}),
|
||||
],
|
||||
});
|
||||
savedObjectsClient.find.mockResolvedValue(createFindResponse([]));
|
||||
|
||||
const { relations, invalidRelations } = await findRelationships({
|
||||
type,
|
||||
id,
|
||||
size: 20,
|
||||
client: savedObjectsClient,
|
||||
referenceTypes,
|
||||
savedObjectsManagement: managementService,
|
||||
});
|
||||
|
||||
expect(relations).toEqual([
|
||||
{
|
||||
id: 'ref-2',
|
||||
relationship: 'child',
|
||||
type: 'another-type',
|
||||
meta: expect.any(Object),
|
||||
},
|
||||
]);
|
||||
|
||||
expect(invalidRelations).toEqual([
|
||||
{ type: 'some-type', id: 'ref-1', relationship: 'child', error: ref1Error.message },
|
||||
]);
|
||||
});
|
||||
|
||||
it('uses the management service to consolidate the relationship objects', async () => {
|
||||
|
@ -144,32 +277,24 @@ describe('findRelationships', () => {
|
|||
uiCapabilitiesPath: 'uiCapabilitiesPath',
|
||||
});
|
||||
|
||||
savedObjectsClient.get.mockResolvedValue({
|
||||
id,
|
||||
type,
|
||||
attributes: {},
|
||||
references,
|
||||
});
|
||||
|
||||
savedObjectsClient.get.mockResolvedValue(
|
||||
createObj({
|
||||
id,
|
||||
type,
|
||||
references,
|
||||
})
|
||||
);
|
||||
savedObjectsClient.bulkGet.mockResolvedValue({
|
||||
saved_objects: [
|
||||
{
|
||||
createObj({
|
||||
type: 'some-type',
|
||||
id: 'ref-1',
|
||||
attributes: {},
|
||||
references: [],
|
||||
},
|
||||
}),
|
||||
],
|
||||
});
|
||||
savedObjectsClient.find.mockResolvedValue(createFindResponse([]));
|
||||
|
||||
savedObjectsClient.find.mockResolvedValue({
|
||||
saved_objects: [],
|
||||
total: 0,
|
||||
per_page: 20,
|
||||
page: 1,
|
||||
});
|
||||
|
||||
const relationships = await findRelationships({
|
||||
const { relations } = await findRelationships({
|
||||
type,
|
||||
id,
|
||||
size: 20,
|
||||
|
@ -183,7 +308,7 @@ describe('findRelationships', () => {
|
|||
expect(managementService.getEditUrl).toHaveBeenCalledTimes(1);
|
||||
expect(managementService.getInAppUrl).toHaveBeenCalledTimes(1);
|
||||
|
||||
expect(relationships).toEqual([
|
||||
expect(relations).toEqual([
|
||||
{
|
||||
id: 'ref-1',
|
||||
relationship: 'child',
|
||||
|
|
|
@ -9,7 +9,11 @@
|
|||
import { SavedObjectsClientContract } from 'src/core/server';
|
||||
import { injectMetaAttributes } from './inject_meta_attributes';
|
||||
import { ISavedObjectsManagement } from '../services';
|
||||
import { SavedObjectRelation, SavedObjectWithMetadata } from '../types';
|
||||
import {
|
||||
SavedObjectInvalidRelation,
|
||||
SavedObjectWithMetadata,
|
||||
SavedObjectGetRelationshipsResponse,
|
||||
} from '../types';
|
||||
|
||||
export async function findRelationships({
|
||||
type,
|
||||
|
@ -25,17 +29,19 @@ export async function findRelationships({
|
|||
client: SavedObjectsClientContract;
|
||||
referenceTypes: string[];
|
||||
savedObjectsManagement: ISavedObjectsManagement;
|
||||
}): Promise<SavedObjectRelation[]> {
|
||||
}): Promise<SavedObjectGetRelationshipsResponse> {
|
||||
const { references = [] } = await client.get(type, id);
|
||||
|
||||
// Use a map to avoid duplicates, it does happen but have a different "name" in the reference
|
||||
const referencedToBulkGetOpts = new Map(
|
||||
references.map((ref) => [`${ref.type}:${ref.id}`, { id: ref.id, type: ref.type }])
|
||||
);
|
||||
const childrenReferences = [
|
||||
...new Map(
|
||||
references.map((ref) => [`${ref.type}:${ref.id}`, { id: ref.id, type: ref.type }])
|
||||
).values(),
|
||||
];
|
||||
|
||||
const [childReferencesResponse, parentReferencesResponse] = await Promise.all([
|
||||
referencedToBulkGetOpts.size > 0
|
||||
? client.bulkGet([...referencedToBulkGetOpts.values()])
|
||||
childrenReferences.length > 0
|
||||
? client.bulkGet(childrenReferences)
|
||||
: Promise.resolve({ saved_objects: [] }),
|
||||
client.find({
|
||||
hasReference: { type, id },
|
||||
|
@ -44,28 +50,37 @@ export async function findRelationships({
|
|||
}),
|
||||
]);
|
||||
|
||||
return childReferencesResponse.saved_objects
|
||||
.map((obj) => injectMetaAttributes(obj, savedObjectsManagement))
|
||||
.map(extractCommonProperties)
|
||||
.map(
|
||||
(obj) =>
|
||||
({
|
||||
...obj,
|
||||
relationship: 'child',
|
||||
} as SavedObjectRelation)
|
||||
)
|
||||
.concat(
|
||||
parentReferencesResponse.saved_objects
|
||||
.map((obj) => injectMetaAttributes(obj, savedObjectsManagement))
|
||||
.map(extractCommonProperties)
|
||||
.map(
|
||||
(obj) =>
|
||||
({
|
||||
...obj,
|
||||
relationship: 'parent',
|
||||
} as SavedObjectRelation)
|
||||
)
|
||||
);
|
||||
const invalidRelations: SavedObjectInvalidRelation[] = childReferencesResponse.saved_objects
|
||||
.filter((obj) => Boolean(obj.error))
|
||||
.map((obj) => ({
|
||||
id: obj.id,
|
||||
type: obj.type,
|
||||
relationship: 'child',
|
||||
error: obj.error!.message,
|
||||
}));
|
||||
|
||||
const relations = [
|
||||
...childReferencesResponse.saved_objects
|
||||
.filter((obj) => !obj.error)
|
||||
.map((obj) => injectMetaAttributes(obj, savedObjectsManagement))
|
||||
.map(extractCommonProperties)
|
||||
.map((obj) => ({
|
||||
...obj,
|
||||
relationship: 'child' as const,
|
||||
})),
|
||||
...parentReferencesResponse.saved_objects
|
||||
.map((obj) => injectMetaAttributes(obj, savedObjectsManagement))
|
||||
.map(extractCommonProperties)
|
||||
.map((obj) => ({
|
||||
...obj,
|
||||
relationship: 'parent' as const,
|
||||
})),
|
||||
];
|
||||
|
||||
return {
|
||||
relations,
|
||||
invalidRelations,
|
||||
};
|
||||
}
|
||||
|
||||
function extractCommonProperties(savedObject: SavedObjectWithMetadata) {
|
||||
|
|
|
@ -38,7 +38,7 @@ export const registerRelationshipsRoute = (
|
|||
? req.query.savedObjectTypes
|
||||
: [req.query.savedObjectTypes];
|
||||
|
||||
const relations = await findRelationships({
|
||||
const findRelationsResponse = await findRelationships({
|
||||
type,
|
||||
id,
|
||||
client,
|
||||
|
@ -48,7 +48,7 @@ export const registerRelationshipsRoute = (
|
|||
});
|
||||
|
||||
return res.ok({
|
||||
body: relations,
|
||||
body: findRelationsResponse,
|
||||
});
|
||||
})
|
||||
);
|
||||
|
|
|
@ -12,4 +12,11 @@ export interface SavedObjectsManagementPluginSetup {}
|
|||
// eslint-disable-next-line @typescript-eslint/no-empty-interface
|
||||
export interface SavedObjectsManagementPluginStart {}
|
||||
|
||||
export { SavedObjectMetadata, SavedObjectWithMetadata, SavedObjectRelation } from '../common';
|
||||
export {
|
||||
SavedObjectMetadata,
|
||||
SavedObjectWithMetadata,
|
||||
SavedObjectRelationKind,
|
||||
SavedObjectRelation,
|
||||
SavedObjectInvalidRelation,
|
||||
SavedObjectGetRelationshipsResponse,
|
||||
} from '../common';
|
||||
|
|
|
@ -14,23 +14,32 @@ export default function ({ getService }: FtrProviderContext) {
|
|||
const supertest = getService('supertest');
|
||||
const esArchiver = getService('esArchiver');
|
||||
|
||||
const responseSchema = schema.arrayOf(
|
||||
schema.object({
|
||||
id: schema.string(),
|
||||
type: schema.string(),
|
||||
relationship: schema.oneOf([schema.literal('parent'), schema.literal('child')]),
|
||||
meta: schema.object({
|
||||
title: schema.string(),
|
||||
icon: schema.string(),
|
||||
editUrl: schema.string(),
|
||||
inAppUrl: schema.object({
|
||||
path: schema.string(),
|
||||
uiCapabilitiesPath: schema.string(),
|
||||
}),
|
||||
namespaceType: schema.string(),
|
||||
const relationSchema = schema.object({
|
||||
id: schema.string(),
|
||||
type: schema.string(),
|
||||
relationship: schema.oneOf([schema.literal('parent'), schema.literal('child')]),
|
||||
meta: schema.object({
|
||||
title: schema.string(),
|
||||
icon: schema.string(),
|
||||
editUrl: schema.string(),
|
||||
inAppUrl: schema.object({
|
||||
path: schema.string(),
|
||||
uiCapabilitiesPath: schema.string(),
|
||||
}),
|
||||
})
|
||||
);
|
||||
namespaceType: schema.string(),
|
||||
}),
|
||||
});
|
||||
const invalidRelationSchema = schema.object({
|
||||
id: schema.string(),
|
||||
type: schema.string(),
|
||||
relationship: schema.oneOf([schema.literal('parent'), schema.literal('child')]),
|
||||
error: schema.string(),
|
||||
});
|
||||
|
||||
const responseSchema = schema.object({
|
||||
relations: schema.arrayOf(relationSchema),
|
||||
invalidRelations: schema.arrayOf(invalidRelationSchema),
|
||||
});
|
||||
|
||||
describe('relationships', () => {
|
||||
before(async () => {
|
||||
|
@ -64,7 +73,7 @@ export default function ({ getService }: FtrProviderContext) {
|
|||
.get(relationshipsUrl('search', '960372e0-3224-11e8-a572-ffca06da1357'))
|
||||
.expect(200);
|
||||
|
||||
expect(resp.body).to.eql([
|
||||
expect(resp.body.relations).to.eql([
|
||||
{
|
||||
id: '8963ca30-3224-11e8-a572-ffca06da1357',
|
||||
type: 'index-pattern',
|
||||
|
@ -108,7 +117,7 @@ export default function ({ getService }: FtrProviderContext) {
|
|||
)
|
||||
.expect(200);
|
||||
|
||||
expect(resp.body).to.eql([
|
||||
expect(resp.body.relations).to.eql([
|
||||
{
|
||||
id: '8963ca30-3224-11e8-a572-ffca06da1357',
|
||||
type: 'index-pattern',
|
||||
|
@ -145,8 +154,7 @@ export default function ({ getService }: FtrProviderContext) {
|
|||
]);
|
||||
});
|
||||
|
||||
// TODO: https://github.com/elastic/kibana/issues/19713 causes this test to fail.
|
||||
it.skip('should return 404 if search finds no results', async () => {
|
||||
it('should return 404 if search finds no results', async () => {
|
||||
await supertest
|
||||
.get(relationshipsUrl('search', 'xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx'))
|
||||
.expect(404);
|
||||
|
@ -169,7 +177,7 @@ export default function ({ getService }: FtrProviderContext) {
|
|||
.get(relationshipsUrl('dashboard', 'b70c7ae0-3224-11e8-a572-ffca06da1357'))
|
||||
.expect(200);
|
||||
|
||||
expect(resp.body).to.eql([
|
||||
expect(resp.body.relations).to.eql([
|
||||
{
|
||||
id: 'add810b0-3224-11e8-a572-ffca06da1357',
|
||||
type: 'visualization',
|
||||
|
@ -210,7 +218,7 @@ export default function ({ getService }: FtrProviderContext) {
|
|||
.get(relationshipsUrl('dashboard', 'b70c7ae0-3224-11e8-a572-ffca06da1357', ['search']))
|
||||
.expect(200);
|
||||
|
||||
expect(resp.body).to.eql([
|
||||
expect(resp.body.relations).to.eql([
|
||||
{
|
||||
id: 'add810b0-3224-11e8-a572-ffca06da1357',
|
||||
type: 'visualization',
|
||||
|
@ -246,8 +254,7 @@ export default function ({ getService }: FtrProviderContext) {
|
|||
]);
|
||||
});
|
||||
|
||||
// TODO: https://github.com/elastic/kibana/issues/19713 causes this test to fail.
|
||||
it.skip('should return 404 if dashboard finds no results', async () => {
|
||||
it('should return 404 if dashboard finds no results', async () => {
|
||||
await supertest
|
||||
.get(relationshipsUrl('dashboard', 'xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx'))
|
||||
.expect(404);
|
||||
|
@ -270,7 +277,7 @@ export default function ({ getService }: FtrProviderContext) {
|
|||
.get(relationshipsUrl('visualization', 'a42c0580-3224-11e8-a572-ffca06da1357'))
|
||||
.expect(200);
|
||||
|
||||
expect(resp.body).to.eql([
|
||||
expect(resp.body.relations).to.eql([
|
||||
{
|
||||
id: '960372e0-3224-11e8-a572-ffca06da1357',
|
||||
type: 'search',
|
||||
|
@ -313,7 +320,7 @@ export default function ({ getService }: FtrProviderContext) {
|
|||
)
|
||||
.expect(200);
|
||||
|
||||
expect(resp.body).to.eql([
|
||||
expect(resp.body.relations).to.eql([
|
||||
{
|
||||
id: '960372e0-3224-11e8-a572-ffca06da1357',
|
||||
type: 'search',
|
||||
|
@ -356,7 +363,7 @@ export default function ({ getService }: FtrProviderContext) {
|
|||
.get(relationshipsUrl('index-pattern', '8963ca30-3224-11e8-a572-ffca06da1357'))
|
||||
.expect(200);
|
||||
|
||||
expect(resp.body).to.eql([
|
||||
expect(resp.body.relations).to.eql([
|
||||
{
|
||||
id: '960372e0-3224-11e8-a572-ffca06da1357',
|
||||
type: 'search',
|
||||
|
@ -399,7 +406,7 @@ export default function ({ getService }: FtrProviderContext) {
|
|||
)
|
||||
.expect(200);
|
||||
|
||||
expect(resp.body).to.eql([
|
||||
expect(resp.body.relations).to.eql([
|
||||
{
|
||||
id: '960372e0-3224-11e8-a572-ffca06da1357',
|
||||
type: 'search',
|
||||
|
@ -425,5 +432,48 @@ export default function ({ getService }: FtrProviderContext) {
|
|||
.expect(404);
|
||||
});
|
||||
});
|
||||
|
||||
describe('invalid references', () => {
|
||||
it('should validate the response schema', async () => {
|
||||
const resp = await supertest.get(relationshipsUrl('dashboard', 'invalid-refs')).expect(200);
|
||||
|
||||
expect(() => {
|
||||
responseSchema.validate(resp.body);
|
||||
}).not.to.throwError();
|
||||
});
|
||||
|
||||
it('should return the invalid relations', async () => {
|
||||
const resp = await supertest.get(relationshipsUrl('dashboard', 'invalid-refs')).expect(200);
|
||||
|
||||
expect(resp.body).to.eql({
|
||||
invalidRelations: [
|
||||
{
|
||||
error: 'Saved object [visualization/invalid-vis] not found',
|
||||
id: 'invalid-vis',
|
||||
relationship: 'child',
|
||||
type: 'visualization',
|
||||
},
|
||||
],
|
||||
relations: [
|
||||
{
|
||||
id: 'add810b0-3224-11e8-a572-ffca06da1357',
|
||||
meta: {
|
||||
editUrl:
|
||||
'/management/kibana/objects/savedVisualizations/add810b0-3224-11e8-a572-ffca06da1357',
|
||||
icon: 'visualizeApp',
|
||||
inAppUrl: {
|
||||
path: '/app/visualize#/edit/add810b0-3224-11e8-a572-ffca06da1357',
|
||||
uiCapabilitiesPath: 'visualize.show',
|
||||
},
|
||||
namespaceType: 'single',
|
||||
title: 'Visualization',
|
||||
},
|
||||
relationship: 'child',
|
||||
type: 'visualization',
|
||||
},
|
||||
],
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
}
|
||||
|
|
|
@ -0,0 +1,190 @@
|
|||
{
|
||||
"type": "doc",
|
||||
"value": {
|
||||
"index": ".kibana",
|
||||
"id": "timelion-sheet:190f3e90-2ec3-11e8-ba48-69fc4e41e1f6",
|
||||
"source": {
|
||||
"type": "timelion-sheet",
|
||||
"updated_at": "2018-03-23T17:53:30.872Z",
|
||||
"timelion-sheet": {
|
||||
"title": "New TimeLion Sheet",
|
||||
"hits": 0,
|
||||
"description": "",
|
||||
"timelion_sheet": [
|
||||
".es(*)"
|
||||
],
|
||||
"timelion_interval": "auto",
|
||||
"timelion_chart_height": 275,
|
||||
"timelion_columns": 2,
|
||||
"timelion_rows": 2,
|
||||
"version": 1
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
{
|
||||
"type": "doc",
|
||||
"value": {
|
||||
"index": ".kibana",
|
||||
"id": "index-pattern:8963ca30-3224-11e8-a572-ffca06da1357",
|
||||
"source": {
|
||||
"type": "index-pattern",
|
||||
"updated_at": "2018-03-28T01:08:34.290Z",
|
||||
"index-pattern": {
|
||||
"title": "saved_objects*",
|
||||
"fields": "[{\"name\":\"_id\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":false},{\"name\":\"_index\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":false},{\"name\":\"_score\",\"type\":\"number\",\"count\":0,\"scripted\":false,\"searchable\":false,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"_source\",\"type\":\"_source\",\"count\":0,\"scripted\":false,\"searchable\":false,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"_type\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":false},{\"name\":\"id\",\"type\":\"number\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true}]"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
{
|
||||
"type": "doc",
|
||||
"value": {
|
||||
"index": ".kibana",
|
||||
"id": "config:7.0.0-alpha1",
|
||||
"source": {
|
||||
"type": "config",
|
||||
"updated_at": "2018-03-28T01:08:39.248Z",
|
||||
"config": {
|
||||
"buildNum": 8467,
|
||||
"telemetry:optIn": false,
|
||||
"defaultIndex": "8963ca30-3224-11e8-a572-ffca06da1357"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
{
|
||||
"type": "doc",
|
||||
"value": {
|
||||
"index": ".kibana",
|
||||
"id": "search:960372e0-3224-11e8-a572-ffca06da1357",
|
||||
"source": {
|
||||
"type": "search",
|
||||
"updated_at": "2018-03-28T01:08:55.182Z",
|
||||
"search": {
|
||||
"title": "OneRecord",
|
||||
"description": "",
|
||||
"hits": 0,
|
||||
"columns": [
|
||||
"_source"
|
||||
],
|
||||
"sort": [
|
||||
"_score",
|
||||
"desc"
|
||||
],
|
||||
"version": 1,
|
||||
"kibanaSavedObjectMeta": {
|
||||
"searchSourceJSON": "{\"index\":\"8963ca30-3224-11e8-a572-ffca06da1357\",\"highlightAll\":true,\"version\":true,\"query\":{\"query\":\"id:3\",\"language\":\"lucene\"},\"filter\":[]}"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
{
|
||||
"type": "doc",
|
||||
"value": {
|
||||
"index": ".kibana",
|
||||
"id": "visualization:a42c0580-3224-11e8-a572-ffca06da1357",
|
||||
"source": {
|
||||
"type": "visualization",
|
||||
"updated_at": "2018-03-28T01:09:18.936Z",
|
||||
"visualization": {
|
||||
"title": "VisualizationFromSavedSearch",
|
||||
"visState": "{\"title\":\"VisualizationFromSavedSearch\",\"type\":\"table\",\"params\":{\"perPage\":10,\"showPartialRows\":false,\"showMeticsAtAllLevels\":false,\"sort\":{\"columnIndex\":null,\"direction\":null},\"showTotal\":false,\"totalFunc\":\"sum\"},\"aggs\":[{\"id\":\"1\",\"enabled\":true,\"type\":\"count\",\"schema\":\"metric\",\"params\":{}}]}",
|
||||
"uiStateJSON": "{\"vis\":{\"params\":{\"sort\":{\"columnIndex\":null,\"direction\":null}}}}",
|
||||
"description": "",
|
||||
"savedSearchId": "960372e0-3224-11e8-a572-ffca06da1357",
|
||||
"version": 1,
|
||||
"kibanaSavedObjectMeta": {
|
||||
"searchSourceJSON": "{\"filter\":[],\"query\":{\"query\":\"\",\"language\":\"lucene\"}}"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
{
|
||||
"type": "doc",
|
||||
"value": {
|
||||
"index": ".kibana",
|
||||
"id": "visualization:add810b0-3224-11e8-a572-ffca06da1357",
|
||||
"source": {
|
||||
"type": "visualization",
|
||||
"updated_at": "2018-03-28T01:09:35.163Z",
|
||||
"visualization": {
|
||||
"title": "Visualization",
|
||||
"visState": "{\"title\":\"Visualization\",\"type\":\"metric\",\"params\":{\"addTooltip\":true,\"addLegend\":false,\"type\":\"metric\",\"metric\":{\"percentageMode\":false,\"useRanges\":false,\"colorSchema\":\"Green to Red\",\"metricColorMode\":\"None\",\"colorsRange\":[{\"from\":0,\"to\":10000}],\"labels\":{\"show\":true},\"invertColors\":false,\"style\":{\"bgFill\":\"#000\",\"bgColor\":false,\"labelColor\":false,\"subText\":\"\",\"fontSize\":60}}},\"aggs\":[{\"id\":\"1\",\"enabled\":true,\"type\":\"count\",\"schema\":\"metric\",\"params\":{}}]}",
|
||||
"uiStateJSON": "{}",
|
||||
"description": "",
|
||||
"version": 1,
|
||||
"kibanaSavedObjectMeta": {
|
||||
"searchSourceJSON": "{\"index\":\"8963ca30-3224-11e8-a572-ffca06da1357\",\"filter\":[],\"query\":{\"query\":\"\",\"language\":\"lucene\"}}"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
{
|
||||
"type": "doc",
|
||||
"value": {
|
||||
"index": ".kibana",
|
||||
"id": "dashboard:b70c7ae0-3224-11e8-a572-ffca06da1357",
|
||||
"source": {
|
||||
"type": "dashboard",
|
||||
"updated_at": "2018-03-28T01:09:50.606Z",
|
||||
"dashboard": {
|
||||
"title": "Dashboard",
|
||||
"hits": 0,
|
||||
"description": "",
|
||||
"panelsJSON": "[{\"gridData\":{\"w\":24,\"h\":15,\"x\":0,\"y\":0,\"i\":\"1\"},\"version\":\"7.0.0-alpha1\",\"panelIndex\":\"1\",\"type\":\"visualization\",\"id\":\"add810b0-3224-11e8-a572-ffca06da1357\",\"embeddableConfig\":{}},{\"gridData\":{\"w\":24,\"h\":15,\"x\":24,\"y\":0,\"i\":\"2\"},\"version\":\"7.0.0-alpha1\",\"panelIndex\":\"2\",\"type\":\"visualization\",\"id\":\"a42c0580-3224-11e8-a572-ffca06da1357\",\"embeddableConfig\":{}}]",
|
||||
"optionsJSON": "{\"darkTheme\":false,\"useMargins\":true,\"hidePanelTitles\":false}",
|
||||
"version": 1,
|
||||
"timeRestore": false,
|
||||
"kibanaSavedObjectMeta": {
|
||||
"searchSourceJSON": "{\"query\":{\"query\":\"\",\"language\":\"lucene\"},\"filter\":[],\"highlightAll\":true,\"version\":true}"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
{
|
||||
"type": "doc",
|
||||
"value": {
|
||||
"index": ".kibana",
|
||||
"id": "dashboard:invalid-refs",
|
||||
"source": {
|
||||
"type": "dashboard",
|
||||
"updated_at": "2018-03-28T01:09:50.606Z",
|
||||
"dashboard": {
|
||||
"title": "Dashboard",
|
||||
"hits": 0,
|
||||
"description": "",
|
||||
"panelsJSON": "[]",
|
||||
"optionsJSON": "{}",
|
||||
"version": 1,
|
||||
"timeRestore": false,
|
||||
"kibanaSavedObjectMeta": {
|
||||
"searchSourceJSON": "{}"
|
||||
}
|
||||
},
|
||||
"references": [
|
||||
{
|
||||
"type":"visualization",
|
||||
"id": "add810b0-3224-11e8-a572-ffca06da1357",
|
||||
"name": "valid-ref"
|
||||
},
|
||||
{
|
||||
"type":"visualization",
|
||||
"id": "invalid-vis",
|
||||
"name": "missing-ref"
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
Binary file not shown.
|
@ -12,6 +12,20 @@
|
|||
"mappings": {
|
||||
"dynamic": "strict",
|
||||
"properties": {
|
||||
"references": {
|
||||
"properties": {
|
||||
"id": {
|
||||
"type": "keyword"
|
||||
},
|
||||
"name": {
|
||||
"type": "keyword"
|
||||
},
|
||||
"type": {
|
||||
"type": "keyword"
|
||||
}
|
||||
},
|
||||
"type": "nested"
|
||||
},
|
||||
"config": {
|
||||
"dynamic": "true",
|
||||
"properties": {
|
||||
|
@ -280,4 +294,4 @@
|
|||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -12,5 +12,6 @@ export default function savedObjectsManagementApp({ loadTestFile }: FtrProviderC
|
|||
describe('saved objects management', function savedObjectsManagementAppTestSuite() {
|
||||
this.tags('ciGroup7');
|
||||
loadTestFile(require.resolve('./edit_saved_object'));
|
||||
loadTestFile(require.resolve('./show_relationships'));
|
||||
});
|
||||
}
|
||||
|
|
|
@ -0,0 +1,52 @@
|
|||
/*
|
||||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||
* or more contributor license agreements. Licensed under the Elastic License
|
||||
* and the Server Side Public License, v 1; you may not use this file except in
|
||||
* compliance with, at your election, the Elastic License or the Server Side
|
||||
* Public License, v 1.
|
||||
*/
|
||||
|
||||
import expect from '@kbn/expect';
|
||||
import { FtrProviderContext } from '../../ftr_provider_context';
|
||||
|
||||
export default function ({ getPageObjects, getService }: FtrProviderContext) {
|
||||
const esArchiver = getService('esArchiver');
|
||||
const PageObjects = getPageObjects(['common', 'settings', 'savedObjects']);
|
||||
|
||||
describe('saved objects relationships flyout', () => {
|
||||
beforeEach(async () => {
|
||||
await esArchiver.load('saved_objects_management/show_relationships');
|
||||
});
|
||||
|
||||
afterEach(async () => {
|
||||
await esArchiver.unload('saved_objects_management/show_relationships');
|
||||
});
|
||||
|
||||
it('displays the invalid references', async () => {
|
||||
await PageObjects.settings.navigateTo();
|
||||
await PageObjects.settings.clickKibanaSavedObjects();
|
||||
|
||||
const objects = await PageObjects.savedObjects.getRowTitles();
|
||||
expect(objects.includes('Dashboard with missing refs')).to.be(true);
|
||||
|
||||
await PageObjects.savedObjects.clickRelationshipsByTitle('Dashboard with missing refs');
|
||||
|
||||
const invalidRelations = await PageObjects.savedObjects.getInvalidRelations();
|
||||
|
||||
expect(invalidRelations).to.eql([
|
||||
{
|
||||
error: 'Saved object [visualization/missing-vis-ref] not found',
|
||||
id: 'missing-vis-ref',
|
||||
relationship: 'Child',
|
||||
type: 'visualization',
|
||||
},
|
||||
{
|
||||
error: 'Saved object [dashboard/missing-dashboard-ref] not found',
|
||||
id: 'missing-dashboard-ref',
|
||||
relationship: 'Child',
|
||||
type: 'dashboard',
|
||||
},
|
||||
]);
|
||||
});
|
||||
});
|
||||
}
|
|
@ -0,0 +1,36 @@
|
|||
{
|
||||
"type": "doc",
|
||||
"value": {
|
||||
"index": ".kibana",
|
||||
"type": "doc",
|
||||
"id": "dashboard:dash-with-missing-refs",
|
||||
"source": {
|
||||
"dashboard": {
|
||||
"title": "Dashboard with missing refs",
|
||||
"hits": 0,
|
||||
"description": "",
|
||||
"panelsJSON": "[]",
|
||||
"optionsJSON": "{}",
|
||||
"version": 1,
|
||||
"timeRestore": false,
|
||||
"kibanaSavedObjectMeta": {
|
||||
"searchSourceJSON": "{}"
|
||||
}
|
||||
},
|
||||
"type": "dashboard",
|
||||
"references": [
|
||||
{
|
||||
"type": "visualization",
|
||||
"id": "missing-vis-ref",
|
||||
"name": "some missing ref"
|
||||
},
|
||||
{
|
||||
"type": "dashboard",
|
||||
"id": "missing-dashboard-ref",
|
||||
"name": "some other missing ref"
|
||||
}
|
||||
],
|
||||
"updated_at": "2019-01-22T19:32:47.232Z"
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,473 @@
|
|||
{
|
||||
"type": "index",
|
||||
"value": {
|
||||
"index": ".kibana",
|
||||
"settings": {
|
||||
"index": {
|
||||
"number_of_shards": "1",
|
||||
"auto_expand_replicas": "0-1",
|
||||
"number_of_replicas": "0"
|
||||
}
|
||||
},
|
||||
"mappings": {
|
||||
"dynamic": "strict",
|
||||
"properties": {
|
||||
"references": {
|
||||
"properties": {
|
||||
"id": {
|
||||
"type": "keyword"
|
||||
},
|
||||
"name": {
|
||||
"type": "keyword"
|
||||
},
|
||||
"type": {
|
||||
"type": "keyword"
|
||||
}
|
||||
},
|
||||
"type": "nested"
|
||||
},
|
||||
"apm-telemetry": {
|
||||
"properties": {
|
||||
"has_any_services": {
|
||||
"type": "boolean"
|
||||
},
|
||||
"services_per_agent": {
|
||||
"properties": {
|
||||
"go": {
|
||||
"type": "long",
|
||||
"null_value": 0
|
||||
},
|
||||
"java": {
|
||||
"type": "long",
|
||||
"null_value": 0
|
||||
},
|
||||
"js-base": {
|
||||
"type": "long",
|
||||
"null_value": 0
|
||||
},
|
||||
"nodejs": {
|
||||
"type": "long",
|
||||
"null_value": 0
|
||||
},
|
||||
"python": {
|
||||
"type": "long",
|
||||
"null_value": 0
|
||||
},
|
||||
"ruby": {
|
||||
"type": "long",
|
||||
"null_value": 0
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"canvas-workpad": {
|
||||
"dynamic": "false",
|
||||
"properties": {
|
||||
"@created": {
|
||||
"type": "date"
|
||||
},
|
||||
"@timestamp": {
|
||||
"type": "date"
|
||||
},
|
||||
"id": {
|
||||
"type": "text",
|
||||
"index": false
|
||||
},
|
||||
"name": {
|
||||
"type": "text",
|
||||
"fields": {
|
||||
"keyword": {
|
||||
"type": "keyword"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"config": {
|
||||
"dynamic": "true",
|
||||
"properties": {
|
||||
"accessibility:disableAnimations": {
|
||||
"type": "boolean"
|
||||
},
|
||||
"buildNum": {
|
||||
"type": "keyword"
|
||||
},
|
||||
"dateFormat:tz": {
|
||||
"type": "text",
|
||||
"fields": {
|
||||
"keyword": {
|
||||
"type": "keyword",
|
||||
"ignore_above": 256
|
||||
}
|
||||
}
|
||||
},
|
||||
"defaultIndex": {
|
||||
"type": "text",
|
||||
"fields": {
|
||||
"keyword": {
|
||||
"type": "keyword",
|
||||
"ignore_above": 256
|
||||
}
|
||||
}
|
||||
},
|
||||
"telemetry:optIn": {
|
||||
"type": "boolean"
|
||||
}
|
||||
}
|
||||
},
|
||||
"dashboard": {
|
||||
"properties": {
|
||||
"description": {
|
||||
"type": "text"
|
||||
},
|
||||
"hits": {
|
||||
"type": "integer"
|
||||
},
|
||||
"kibanaSavedObjectMeta": {
|
||||
"properties": {
|
||||
"searchSourceJSON": {
|
||||
"type": "text"
|
||||
}
|
||||
}
|
||||
},
|
||||
"optionsJSON": {
|
||||
"type": "text"
|
||||
},
|
||||
"panelsJSON": {
|
||||
"type": "text"
|
||||
},
|
||||
"refreshInterval": {
|
||||
"properties": {
|
||||
"display": {
|
||||
"type": "keyword"
|
||||
},
|
||||
"pause": {
|
||||
"type": "boolean"
|
||||
},
|
||||
"section": {
|
||||
"type": "integer"
|
||||
},
|
||||
"value": {
|
||||
"type": "integer"
|
||||
}
|
||||
}
|
||||
},
|
||||
"timeFrom": {
|
||||
"type": "keyword"
|
||||
},
|
||||
"timeRestore": {
|
||||
"type": "boolean"
|
||||
},
|
||||
"timeTo": {
|
||||
"type": "keyword"
|
||||
},
|
||||
"title": {
|
||||
"type": "text"
|
||||
},
|
||||
"uiStateJSON": {
|
||||
"type": "text"
|
||||
},
|
||||
"version": {
|
||||
"type": "integer"
|
||||
}
|
||||
}
|
||||
},
|
||||
"map": {
|
||||
"properties": {
|
||||
"bounds": {
|
||||
"type": "geo_shape",
|
||||
"tree": "quadtree"
|
||||
},
|
||||
"description": {
|
||||
"type": "text"
|
||||
},
|
||||
"layerListJSON": {
|
||||
"type": "text"
|
||||
},
|
||||
"mapStateJSON": {
|
||||
"type": "text"
|
||||
},
|
||||
"title": {
|
||||
"type": "text"
|
||||
},
|
||||
"uiStateJSON": {
|
||||
"type": "text"
|
||||
},
|
||||
"version": {
|
||||
"type": "integer"
|
||||
}
|
||||
}
|
||||
},
|
||||
"graph-workspace": {
|
||||
"properties": {
|
||||
"description": {
|
||||
"type": "text"
|
||||
},
|
||||
"kibanaSavedObjectMeta": {
|
||||
"properties": {
|
||||
"searchSourceJSON": {
|
||||
"type": "text"
|
||||
}
|
||||
}
|
||||
},
|
||||
"numLinks": {
|
||||
"type": "integer"
|
||||
},
|
||||
"numVertices": {
|
||||
"type": "integer"
|
||||
},
|
||||
"title": {
|
||||
"type": "text"
|
||||
},
|
||||
"version": {
|
||||
"type": "integer"
|
||||
},
|
||||
"wsState": {
|
||||
"type": "text"
|
||||
}
|
||||
}
|
||||
},
|
||||
"index-pattern": {
|
||||
"properties": {
|
||||
"fieldFormatMap": {
|
||||
"type": "text"
|
||||
},
|
||||
"fields": {
|
||||
"type": "text"
|
||||
},
|
||||
"intervalName": {
|
||||
"type": "keyword"
|
||||
},
|
||||
"notExpandable": {
|
||||
"type": "boolean"
|
||||
},
|
||||
"sourceFilters": {
|
||||
"type": "text"
|
||||
},
|
||||
"timeFieldName": {
|
||||
"type": "keyword"
|
||||
},
|
||||
"title": {
|
||||
"type": "text"
|
||||
},
|
||||
"type": {
|
||||
"type": "keyword"
|
||||
},
|
||||
"typeMeta": {
|
||||
"type": "keyword"
|
||||
}
|
||||
}
|
||||
},
|
||||
"kql-telemetry": {
|
||||
"properties": {
|
||||
"optInCount": {
|
||||
"type": "long"
|
||||
},
|
||||
"optOutCount": {
|
||||
"type": "long"
|
||||
}
|
||||
}
|
||||
},
|
||||
"migrationVersion": {
|
||||
"dynamic": "true",
|
||||
"properties": {
|
||||
"index-pattern": {
|
||||
"type": "text",
|
||||
"fields": {
|
||||
"keyword": {
|
||||
"type": "keyword",
|
||||
"ignore_above": 256
|
||||
}
|
||||
}
|
||||
},
|
||||
"space": {
|
||||
"type": "text",
|
||||
"fields": {
|
||||
"keyword": {
|
||||
"type": "keyword",
|
||||
"ignore_above": 256
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"namespace": {
|
||||
"type": "keyword"
|
||||
},
|
||||
"search": {
|
||||
"properties": {
|
||||
"columns": {
|
||||
"type": "keyword"
|
||||
},
|
||||
"description": {
|
||||
"type": "text"
|
||||
},
|
||||
"hits": {
|
||||
"type": "integer"
|
||||
},
|
||||
"kibanaSavedObjectMeta": {
|
||||
"properties": {
|
||||
"searchSourceJSON": {
|
||||
"type": "text"
|
||||
}
|
||||
}
|
||||
},
|
||||
"sort": {
|
||||
"type": "keyword"
|
||||
},
|
||||
"title": {
|
||||
"type": "text"
|
||||
},
|
||||
"version": {
|
||||
"type": "integer"
|
||||
}
|
||||
}
|
||||
},
|
||||
"server": {
|
||||
"properties": {
|
||||
"uuid": {
|
||||
"type": "keyword"
|
||||
}
|
||||
}
|
||||
},
|
||||
"space": {
|
||||
"properties": {
|
||||
"_reserved": {
|
||||
"type": "boolean"
|
||||
},
|
||||
"color": {
|
||||
"type": "keyword"
|
||||
},
|
||||
"description": {
|
||||
"type": "text"
|
||||
},
|
||||
"disabledFeatures": {
|
||||
"type": "keyword"
|
||||
},
|
||||
"initials": {
|
||||
"type": "keyword"
|
||||
},
|
||||
"name": {
|
||||
"type": "text",
|
||||
"fields": {
|
||||
"keyword": {
|
||||
"type": "keyword",
|
||||
"ignore_above": 2048
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"spaceId": {
|
||||
"type": "keyword"
|
||||
},
|
||||
"telemetry": {
|
||||
"properties": {
|
||||
"enabled": {
|
||||
"type": "boolean"
|
||||
}
|
||||
}
|
||||
},
|
||||
"timelion-sheet": {
|
||||
"properties": {
|
||||
"description": {
|
||||
"type": "text"
|
||||
},
|
||||
"hits": {
|
||||
"type": "integer"
|
||||
},
|
||||
"kibanaSavedObjectMeta": {
|
||||
"properties": {
|
||||
"searchSourceJSON": {
|
||||
"type": "text"
|
||||
}
|
||||
}
|
||||
},
|
||||
"timelion_chart_height": {
|
||||
"type": "integer"
|
||||
},
|
||||
"timelion_columns": {
|
||||
"type": "integer"
|
||||
},
|
||||
"timelion_interval": {
|
||||
"type": "keyword"
|
||||
},
|
||||
"timelion_other_interval": {
|
||||
"type": "keyword"
|
||||
},
|
||||
"timelion_rows": {
|
||||
"type": "integer"
|
||||
},
|
||||
"timelion_sheet": {
|
||||
"type": "text"
|
||||
},
|
||||
"title": {
|
||||
"type": "text"
|
||||
},
|
||||
"version": {
|
||||
"type": "integer"
|
||||
}
|
||||
}
|
||||
},
|
||||
"type": {
|
||||
"type": "keyword"
|
||||
},
|
||||
"updated_at": {
|
||||
"type": "date"
|
||||
},
|
||||
"url": {
|
||||
"properties": {
|
||||
"accessCount": {
|
||||
"type": "long"
|
||||
},
|
||||
"accessDate": {
|
||||
"type": "date"
|
||||
},
|
||||
"createDate": {
|
||||
"type": "date"
|
||||
},
|
||||
"url": {
|
||||
"type": "text",
|
||||
"fields": {
|
||||
"keyword": {
|
||||
"type": "keyword",
|
||||
"ignore_above": 2048
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"visualization": {
|
||||
"properties": {
|
||||
"description": {
|
||||
"type": "text"
|
||||
},
|
||||
"kibanaSavedObjectMeta": {
|
||||
"properties": {
|
||||
"searchSourceJSON": {
|
||||
"type": "text"
|
||||
}
|
||||
}
|
||||
},
|
||||
"savedSearchId": {
|
||||
"type": "keyword"
|
||||
},
|
||||
"title": {
|
||||
"type": "text"
|
||||
},
|
||||
"uiStateJSON": {
|
||||
"type": "text"
|
||||
},
|
||||
"version": {
|
||||
"type": "integer"
|
||||
},
|
||||
"visState": {
|
||||
"type": "text"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -257,6 +257,22 @@ export function SavedObjectsPageProvider({ getService, getPageObjects }: FtrProv
|
|||
});
|
||||
}
|
||||
|
||||
async getInvalidRelations() {
|
||||
const rows = await testSubjects.findAll('invalidRelationshipsTableRow');
|
||||
return mapAsync(rows, async (row) => {
|
||||
const objectType = await row.findByTestSubject('relationshipsObjectType');
|
||||
const objectId = await row.findByTestSubject('relationshipsObjectId');
|
||||
const relationship = await row.findByTestSubject('directRelationship');
|
||||
const error = await row.findByTestSubject('relationshipsError');
|
||||
return {
|
||||
type: await objectType.getVisibleText(),
|
||||
id: await objectId.getVisibleText(),
|
||||
relationship: await relationship.getVisibleText(),
|
||||
error: await error.getVisibleText(),
|
||||
};
|
||||
});
|
||||
}
|
||||
|
||||
async getTableSummary() {
|
||||
const table = await testSubjects.find('savedObjectsTable');
|
||||
const $ = await table.parseDomContent();
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue