Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[ContentManagement] Fix Visualize List search and CRUD operations via CM #165485

Merged
merged 24 commits into from
Sep 13, 2023
Merged
Show file tree
Hide file tree
Changes from 18 commits
Commits
Show all changes
24 commits
Select commit Hold shift + click to select a range
1432cce
:recycle: Refactor client to factory fn
dej611 Sep 1, 2023
8663aef
:sparkles: Create lens client
dej611 Sep 1, 2023
5b43067
:wrench: Add client prop to type service
dej611 Sep 1, 2023
93b422b
:white_check_mark: Add unit and functional tests
dej611 Sep 1, 2023
d4c38d2
:bug: Fixes after manual testing
dej611 Sep 1, 2023
7dc072b
:sparkles: Add new methods to better interact with listing page
dej611 Sep 1, 2023
9507501
:label: Fix type
dej611 Sep 1, 2023
9bb387b
:label: More type fixes
dej611 Sep 1, 2023
f337a34
:white_check_mark: Fix tests
dej611 Sep 1, 2023
dd26a3d
Merge branch 'main' into fix/163246-2
dej611 Sep 1, 2023
4202269
:recycle: Refactor types and storage
dej611 Sep 4, 2023
b52dc99
:white_check_mark: Fix test
dej611 Sep 4, 2023
4b808e0
Merge remote-tracking branch 'upstream/main' into fix/163246-2
dej611 Sep 4, 2023
5d08c70
Merge branch 'fix/163246-2' of https://github.com/dej611/kibana into …
dej611 Sep 4, 2023
1fb3457
:recycle: Remove unused options
dej611 Sep 4, 2023
016edec
Merge branch 'main' into fix/163246-2
stratoula Sep 5, 2023
e2d9b92
Update src/plugins/content_management/common/rpc/msearch.ts
dej611 Sep 5, 2023
3bd5cc0
:white_check_mark: Fix tests
dej611 Sep 5, 2023
7870b82
Merge remote-tracking branch 'upstream/main' into fix/163246-2
dej611 Sep 8, 2023
d9b0427
:ok_hand: Refactor based on feedback
dej611 Sep 8, 2023
6c1c096
:bug: Make update overwrite an optional prop owned by each plugin
dej611 Sep 8, 2023
2bdff27
Merge branch 'main' into fix/163246-2
dej611 Sep 11, 2023
d8bd1fc
Merge branch 'main' into fix/163246-2
dej611 Sep 12, 2023
255e766
Merge remote-tracking branch 'upstream/main' into fix/163246-2
dej611 Sep 13, 2023
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions packages/kbn-content-management-utils/src/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -161,6 +161,8 @@ export interface SavedObjectSearchOptions {

/** Saved Object update options - Pick and Omit to customize */
export interface SavedObjectUpdateOptions<Attributes = unknown> {
/** Overwrite existing documents (defaults to false) */
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

why is this needed ? in the old times we used this as we didnt have the update operation, but now we have full crud operation set.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is used by all visualization types but maps right now. I had to introduce it to maps in order to make it work within the metadata editing in visualize Listing page.
Other visualizations were using the CreateOptions type on Update in order to validate/pass the overwrite flag.
I can see how this can be removed, but I would like to move it in a follow up PR unless it's strictly required.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

here is some context where a related bug in Lens was fixed #160116 (comment)

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'd like a more complete explanation of this which might be best done over zoom since its modifying api types.

overwrite?: boolean;
/** Array of referenced saved objects. */
references?: Reference[];
version?: string;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,13 @@ const deleteVisualization = async (id: string) => {
};

const search = async (query: SearchQuery = {}, options?: VisualizationSearchQuery) => {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Does the return type of this method account for that it could return maps and lens objects?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not specifically the types of those, but all adhere to a generic interface defined https://github.com/elastic/kibana/pull/165485/files#diff-72eb8e94b67c0c56b5744ac732c6164a66b68e6e4a6fb1857da4d361acb6b149R52

Having the exact types returned by this method, from my understanding, requires either a type cast or a full refactor of the architecture, as types are registered dynamically to Visualizations and there's no static way to detect the correct type.

if (options && options.types && options.types.length > 1) {
const { types } = options;
return getContentManagement().client.mSearch<VisualizationSearchOut['hits'][number]>({
contentTypes: types.map((type) => ({ contentTypeId: type })),
query,
});
}
return getContentManagement().client.search<VisualizationSearchIn, VisualizationSearchOut>({
contentTypeId: 'visualization',
query,
Expand Down
10 changes: 9 additions & 1 deletion src/plugins/visualizations/public/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,15 @@ export { getVisSchemas } from './vis_schemas';
/** @public types */
export type { VisualizationsSetup, VisualizationsStart };
export { VisGroups } from './vis_types/vis_groups_enum';
export type { BaseVisType, VisTypeAlias, VisTypeDefinition, Schema, ISchemas } from './vis_types';
export type {
BaseVisType,
VisTypeAlias,
VisTypeDefinition,
Schema,
ISchemas,
VisualizationClient,
SerializableAttributes,
} from './vis_types';
export type { Vis, SerializedVis, SerializedVisData, VisData } from './vis';
export type VisualizeEmbeddableFactoryContract = PublicContract<VisualizeEmbeddableFactory>;
export type VisualizeEmbeddableContract = PublicContract<VisualizeEmbeddable>;
Expand Down
1 change: 1 addition & 0 deletions src/plugins/visualizations/public/plugin.ts
Original file line number Diff line number Diff line change
Expand Up @@ -333,6 +333,7 @@ export class VisualizationsPlugin
unifiedSearch: pluginsStart.unifiedSearch,
serverless: pluginsStart.serverless,
noDataPage: pluginsStart.noDataPage,
contentManagement: pluginsStart.contentManagement,
};

params.element.classList.add('visAppWrapper');
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,12 +9,29 @@
import type { SavedObjectsTaggingApi } from '@kbn/saved-objects-tagging-oss-plugin/public';
import type { OverlayStart } from '@kbn/core-overlays-browser';

import { ContentManagementPublicStart } from '@kbn/content-management-plugin/public';
import { extractReferences } from '../saved_visualization_references';
import { visualizationsClient } from '../../content_management';
import { TypesStart } from '../../vis_types';

interface UpdateBasicSoAttributesDependencies {
savedObjectsTagging?: SavedObjectsTaggingApi;
overlays: OverlayStart;
typesService: TypesStart;
contentManagement: ContentManagementPublicStart;
}

function getClientForType(
type: string,
typesService: TypesStart,
contentManagement: ContentManagementPublicStart
) {
const visAliases = typesService.getAliases();
return (
visAliases
.find((v) => v.appExtensions?.visualizations.docTypes.includes(type))
?.appExtensions?.visualizations.client(contentManagement) || visualizationsClient
);
}

export const updateBasicSoAttributes = async (
Expand All @@ -27,7 +44,9 @@ export const updateBasicSoAttributes = async (
},
dependencies: UpdateBasicSoAttributesDependencies
) => {
const so = await visualizationsClient.get(soId);
const client = getClientForType(type, dependencies.typesService, dependencies.contentManagement);

const so = await client.get(soId);
const extractedReferences = extractReferences({
attributes: so.item.attributes,
references: so.item.references,
Expand All @@ -48,7 +67,7 @@ export const updateBasicSoAttributes = async (
);
}

return await visualizationsClient.update({
return await client.update({
id: soId,
data: {
...attributes,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,17 +12,17 @@ import {
injectSearchSourceReferences,
SerializedSearchSourceFields,
} from '@kbn/data-plugin/public';
import { SerializableRecord } from '@kbn/utility-types';
import { SavedVisState, VisSavedObject } from '../../types';

import { extractTimeSeriesReferences, injectTimeSeriesReferences } from './timeseries_references';
import { extractControlsReferences, injectControlsReferences } from './controls_references';
import type { SerializableAttributes } from '../../vis_types/vis_type_alias_registry';

export function extractReferences({
attributes,
references = [],
}: {
attributes: SerializableRecord;
attributes: SerializableAttributes;
references: SavedObjectReference[];
}) {
const updatedAttributes = { ...attributes };
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,7 @@ jest.mock('../services', () => ({
update: mockUpdateContent,
get: mockGetContent,
search: mockFindContent,
mSearch: mockFindContent,
},
})),
}));
Expand Down Expand Up @@ -358,10 +359,11 @@ describe('saved_visualize_utils', () => {
expect(mockFindContent.mock.calls).toMatchObject([
[
{
options: {
types: ['bazdoc', 'etc', 'visualization'],
searchFields: ['baz', 'bing', 'title^3', 'description'],
},
contentTypes: [
{ contentTypeId: 'bazdoc' },
{ contentTypeId: 'etc' },
{ contentTypeId: 'visualization' },
],
},
],
]);
Expand Down Expand Up @@ -395,10 +397,12 @@ describe('saved_visualize_utils', () => {
expect(mockFindContent.mock.calls).toMatchObject([
[
{
options: {
types: ['bazdoc', 'bar', 'visualization', 'foo'],
searchFields: ['baz', 'bing', 'barfield', 'foofield', 'title^3', 'description'],
},
contentTypes: [
{ contentTypeId: 'bazdoc' },
{ contentTypeId: 'bar' },
{ contentTypeId: 'visualization' },
{ contentTypeId: 'foo' },
],
},
],
]);
Expand Down
1 change: 1 addition & 0 deletions src/plugins/visualizations/public/vis_types/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,3 +11,4 @@ export { Schemas } from './schemas';
export { VisGroups } from './vis_groups_enum';
export { BaseVisType } from './base_vis_type';
export type { VisTypeDefinition, ISchemas, Schema } from './types';
export type { VisualizationClient, SerializableAttributes } from './vis_type_alias_registry';
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,13 @@
* Side Public License, v 1.
*/

import { SearchQuery } from '@kbn/content-management-plugin/common';
import { ContentManagementPublicStart } from '@kbn/content-management-plugin/public';
import {
ContentManagementCrudTypes,
SavedObjectCreateOptions,
SavedObjectUpdateOptions,
} from '@kbn/content-management-utils';
import type { SimpleSavedObject } from '@kbn/core/public';
import { BaseVisType } from './base_vis_type';

Expand All @@ -27,9 +34,49 @@ export interface VisualizationListItem {
type?: BaseVisType | string;
}

export interface SerializableAttributes {
[key: string]: unknown;
}

export type GenericVisualizationCrudTypes<
ContentType extends string,
Attr extends SerializableAttributes
> = ContentManagementCrudTypes<
ContentType,
Attr,
Pick<SavedObjectCreateOptions, 'overwrite' | 'references'>,
Pick<SavedObjectUpdateOptions, 'overwrite' | 'references'>,
object
>;

export interface VisualizationClient<
ContentType extends string = string,
Attr extends SerializableAttributes = SerializableAttributes
> {
get: (id: string) => Promise<GenericVisualizationCrudTypes<ContentType, Attr>['GetOut']>;
create: (
visualization: Omit<
GenericVisualizationCrudTypes<ContentType, Attr>['CreateIn'],
'contentTypeId'
>
) => Promise<GenericVisualizationCrudTypes<ContentType, Attr>['CreateOut']>;
update: (
visualization: Omit<
GenericVisualizationCrudTypes<ContentType, Attr>['UpdateIn'],
'contentTypeId'
>
) => Promise<GenericVisualizationCrudTypes<ContentType, Attr>['UpdateOut']>;
delete: (id: string) => Promise<GenericVisualizationCrudTypes<ContentType, Attr>['DeleteOut']>;
search: (
query: SearchQuery,
options?: object
) => Promise<GenericVisualizationCrudTypes<ContentType, Attr>['SearchOut']>;
}

export interface VisualizationsAppExtension {
docTypes: string[];
searchFields?: string[];
client: (contentManagement: ContentManagementPublicStart) => VisualizationClient;
toListItem: (savedObject: SimpleSavedObject<any>) => VisualizationListItem;
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -109,6 +109,7 @@ const useTableListViewProps = (
overlays,
toastNotifications,
visualizeCapabilities,
contentManagement,
},
} = useKibana<VisualizeServices>();

Expand Down Expand Up @@ -176,11 +177,16 @@ const useTableListViewProps = (
description: args.description ?? '',
tags: args.tags,
},
{ overlays, savedObjectsTagging }
{
overlays,
savedObjectsTagging,
typesService: getTypes(),
contentManagement,
}
);
}
},
[overlays, savedObjectsTagging]
[overlays, savedObjectsTagging, contentManagement]
);

const contentEditorValidators: OpenContentEditorParams['customValidators'] = useMemo(
Expand Down
2 changes: 2 additions & 0 deletions src/plugins/visualizations/public/visualize_app/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@ import type { SavedObjectsTaggingApi } from '@kbn/saved-objects-tagging-oss-plug
import type { SavedSearch, SavedSearchPublicPluginStart } from '@kbn/saved-search-plugin/public';
import type { ServerlessPluginStart } from '@kbn/serverless/public';
import type { NoDataPagePluginStart } from '@kbn/no-data-page-plugin/public';
import { ContentManagementPublicStart } from '@kbn/content-management-plugin/public';
import type {
Vis,
VisualizeEmbeddableContract,
Expand Down Expand Up @@ -119,6 +120,7 @@ export interface VisualizeServices extends CoreStart {
unifiedSearch: UnifiedSearchPublicPluginStart;
serverless?: ServerlessPluginStart;
noDataPage?: NoDataPagePluginStart;
contentManagement: ContentManagementPublicStart;
}

export interface VisInstance {
Expand Down
12 changes: 4 additions & 8 deletions test/functional/apps/dashboard/group4/dashboard_listing.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,8 +14,6 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) {
const PageObjects = getPageObjects(['dashboard', 'header', 'common']);
const browser = getService('browser');
const listingTable = getService('listingTable');
const testSubjects = getService('testSubjects');
const retry = getService('retry');
const dashboardAddPanel = getService('dashboardAddPanel');

describe('dashboard listing page', function describeIndexTests() {
Expand Down Expand Up @@ -217,12 +215,10 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) {

await PageObjects.dashboard.gotoDashboardLandingPage();
await listingTable.searchForItemWithName(`${dashboardName}-editMetaData`);
await testSubjects.click('inspect-action');
await testSubjects.setValue('nameInput', 'new title');
await testSubjects.setValue('descriptionInput', 'new description');
await retry.try(async () => {
await testSubjects.click('saveButton');
await testSubjects.missingOrFail('flyoutTitle');
await listingTable.inspectVisualization();
await listingTable.editVisualizationDetails({
title: 'new title',
description: 'new description',
});

await listingTable.searchAndExpectItemsCount('dashboard', 'new title', 1);
Expand Down
17 changes: 17 additions & 0 deletions test/functional/apps/visualize/group3/_visualize_listing.ts
Original file line number Diff line number Diff line change
Expand Up @@ -84,5 +84,22 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) {
await listingTable.expectItemsCount('visualize', 0);
});
});

describe('Edit', () => {
before(async () => {
await PageObjects.visualize.gotoVisualizationLandingPage();
});

it('should edit the title and description of a visualization', async () => {
await listingTable.searchForItemWithName('Hello');
await listingTable.inspectVisualization();
await listingTable.editVisualizationDetails({
title: 'new title',
description: 'new description',
});
await listingTable.searchForItemWithName('new title');
await listingTable.expectItemsCount('visualize', 1);
});
});
});
}
29 changes: 29 additions & 0 deletions test/functional/services/listing_table.ts
Original file line number Diff line number Diff line change
Expand Up @@ -154,6 +154,35 @@ export class ListingTableService extends FtrService {
return visualizationNames;
}

/**
* Open the inspect flyout
*/
public async inspectVisualization(index: number = 0) {
const inspectButtons = await this.testSubjects.findAll('inspect-action');
await inspectButtons[index].click();
}

/**
* Edit Visualization title and description in the flyout
*/
public async editVisualizationDetails(
{ title, description }: { title?: string; description?: string } = {},
shouldSave: boolean = true
) {
if (title) {
await this.testSubjects.setValue('nameInput', title);
}
if (description) {
await this.testSubjects.setValue('descriptionInput', description);
}
if (shouldSave) {
await this.retry.try(async () => {
await this.testSubjects.click('saveButton');
await this.testSubjects.missingOrFail('flyoutTitle');
});
}
}

/**
* Returns items count on landing page
*/
Expand Down
1 change: 1 addition & 0 deletions x-pack/plugins/lens/common/content_management/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ export type {
LensSearchIn,
LensSearchOut,
LensSearchQuery,
LensCrudTypes,
} from './latest';

export * as LensV1 from './v1';
Original file line number Diff line number Diff line change
Expand Up @@ -22,5 +22,5 @@ export type {
LensSearchIn,
LensSearchOut,
LensSearchQuery,
Reference,
LensCrudTypes,
} from './types';
Loading