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

[content mgmt / maps] Saved Object wrapper for Content Management API #155680

Merged
merged 36 commits into from
May 16, 2023

Conversation

mattkime
Copy link
Contributor

@mattkime mattkime commented Apr 25, 2023

Summary

Abstract class for implementing content storage for saved objects.

Follow up to #155342

The maps content management storage class for maps has been reduced to -

export class MapsStorage extends SOContentStorage<MapCrudTypes> {
  constructor() {
    super({
      savedObjectType: CONTENT_ID,
      cmServicesDefinition,
      searchArgsToSOFindOptions, // converts map search args to saved object api args
      enableMSearch: true,
    });
  }
}

enableMSearch?: boolean;
}

export abstract class SOContentStorage<Types extends CMCrudTypes>
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 class is where the work happens.

total: response.total,
},
export class MapsStorage extends SOContentStorage<MapCrudTypes> {
constructor() {
Copy link
Contributor Author

@mattkime mattkime Apr 25, 2023

Choose a reason for hiding this comment

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

This is the result. Expressing your SO / content mgmt api integration in a very concise number of lines of code. Easier to see if you look at the file view - https://github.com/elastic/kibana/blob/861b8af7545082b2aae79205be6c3e98ad480d55/x-pack/plugins/maps/server/content_management/maps_storage.ts


// todo - talk to anton about a better way to implement this
if (enableMSearch) {
this.mSearch = {
Copy link
Contributor Author

Choose a reason for hiding this comment

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

@Dosant We should find a better api for this. I'd prefer to use some sort of boolean to determine whether mSearch is enabled. As best I can tell this is largely a reconfiguration of code used elsewhere

Copy link
Contributor

Choose a reason for hiding this comment

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

will improve. #155718
hopefully non blocking

@mattkime mattkime changed the title Content mgmt so content storage [content mgmt / maps] Saved Object wrapper for Content Management API Apr 25, 2023
@mattkime mattkime added Team:DataDiscovery Discover, search (e.g. data plugin and KQL), data views, saved searches. For ES|QL, use Team:ES|QL. Feature:Maps Feature:Content Management User generated content (saved objects) management release_note:skip Skip the PR/issue when compiling release notes labels Apr 25, 2023
@mattkime mattkime marked this pull request as ready for review April 25, 2023 21:02
@mattkime mattkime requested review from a team as code owners April 25, 2023 21:02
@elasticmachine
Copy link
Contributor

Pinging @elastic/kibana-data-discovery (Team:DataDiscovery)

Copy link
Contributor

@sebelga sebelga left a comment

Choose a reason for hiding this comment

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

Thanks for putting this PR together. I keep having some concerns about trying to fit it all under one package in terms of interfaces and schema.

At the very least I think that the utility package should follow the same versioning folder mechanism that the one I put for maps. It will be versioned based on change in the underlying SO server client.

We need to be able to version and up/down grade all those common interfaces (options, SO meta...) and schema somehow.

Let's take an example: in the SavedObjectSearchOptions interface there is an namespaces field which is a string[]

  • How can we make it evolve to { name: string; group: string }?
  • How is the up transform going to occur when the UI will still send a list of string and the server has been upgraded to accept an object?

It seems that we will have to also send the request version to the utility that will in turn return the correct schema... This starts to get complex (in my head at least 😄 )

@mattkime
Copy link
Contributor Author

@sebelga

At the very least I think that the utility package should follow the same versioning folder mechanism that the one I put for maps. It will be versioned based on change in the underlying SO server client.

This confuses me a bit. If there's an underlying change in the SO server client then all SO CM integrations will need to work with it. I don't think the callee's (in this case the SO server client) need to be versioned in the same way that the arguments need to. If there's something I'm missing here, I suspect the code in this PR would have the same weaknesses as the maps CM integration code.

We need to be able to version and up/down grade all those common interfaces

Oh, perhaps I'm misunderstanding and this is a restatement of a previous concern - that the various Option types are too closely tied to the actual SO api. If thats the concern, then I think this is addressed by the existing transform code. I think its okay to have arguments that map to SO api arguments as long as they're well defined AND we could transform them. If a map of arguments was being tossed into the SO api without further definition that would be clearly wrong.

Let's take an example...

We have two options. Either we can inspect the items in the namespace array and convert the strings to objects OR we could add an namespaceObjects argument and we could handle the two types appropriately. Perhaps I'm repeating a familiar debate regarding how to transform apis.

A big part of my reason for arguing against using args that are purposefully different from SO args is that I suspect it creates unnecessary work. Implementing the CM api can be very easy, thinking about how to abstract every call into the SO api is much more work. I do think it may eventually be necessary to change these calls but its possible that we could delay it
a very long time.


I'd like to state any outstanding concerns as plainly as possible -

  1. The cm options map too directly to SO apis.
  2. Versioning of the whole SOContentManagement class.

Is this correct?

@sebelga
Copy link
Contributor

sebelga commented Apr 27, 2023

@mattkime

If you are 100% sure you got a solution for the concerns I raised about being able to version any object that comes in for any SO, then I trust your judgment.

From where I am standing I see it even more complex now than before to handle it. But with that said... we still don't have the final verdict if we even have to worry about it (e.g. if we have a blue/green upgrade we won't have to do any kind of transform in the storage class).

Otherwise your abstraction might start looking like:

async create(ctx, data, options) {
    const transforms = ctx.utils.getTransforms(this.cmServicesDefinition);
    const soClient = await savedObjectClientFromRequest(ctx);
   
    // NEW: we need a new definition for the shared schemas
    const transformsForAbstraction = ctx.utils.getTransforms(<some-definition>);

    // Validate data & UP transform them to the latest version
    const { value: dataToLatest, error: dataError } = transforms.create.in.data.up<
      Types['Attributes'],
      Types['Attributes']
    >(data);
    if (dataError) {
      throw Boom.badRequest(`Invalid data. ${dataError.message}`);
    }
    
    // NEW: Add this block to handle BWC for **shared** option schema
    // But do we really need it, what if the options are completely different for one specific SO type??
    const { value: optionsToLatest, error: optionsError } = transformsForAbstraction.create.in.options.up(options);
    if (optionsError) {
      throw Boom.badRequest(`Invalid options. ${optionsError.message}`);
    }
    
    //  What do we do here?? Do we do it or is it behind a if condition?
    const { value: optionsToLatest, error: optionsError } = transforms.create.in.options.up<
      Types['CreateOptions'],
      Types['CreateOptions']
    >(optionsToLatest1);
    if (optionsError) {
      throw Boom.badRequest(`Invalid options. ${optionsError.message}`);
    }

    const createOptions = this.createArgsToSoCreateOptions(optionsToLatest);
}

As you can see it starts to get very complex and IMO pretty hard to maintain.

Copy link
Contributor

@sebelga sebelga left a comment

Choose a reason for hiding this comment

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

Approving to unblock the work. It is still unclear to me how we will migrate the shared options object in the abstraction as mentioned in my previous comments.

As we don't have yet a way to test a server upgrade and a UI doing request from an older version we can revisit later. But I do think at some point we'll have to address this problem 😊 and my personal take is that abstracting too early until all unknown are known might create more problem down the road.

Copy link
Contributor

@jughosta jughosta left a comment

Choose a reason for hiding this comment

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

Code changes LGTM 👍

Comment on lines 91 to 103
const hasReference: SavedObjectsFindOptions['hasReference'] = included
? included.map((id) => ({
id,
type: 'tag',
}))
: undefined;

const hasNoReference: SavedObjectsFindOptions['hasNoReference'] = excluded
? excluded.map((id) => ({
id,
type: 'tag',
}))
: undefined;
Copy link
Contributor

Choose a reason for hiding this comment

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

Can we use tagsToFindOptions here too?

Copy link
Contributor

@nreese nreese left a comment

Choose a reason for hiding this comment

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

kibana-gis changes LGTM
code review only

@mattkime mattkime enabled auto-merge (squash) May 16, 2023 13:44
@kibana-ci
Copy link
Collaborator

💚 Build Succeeded

Metrics [docs]

Public APIs missing comments

Total count of every public API that lacks a comment. Target amount is 0. Run node scripts/build_api_docs --plugin [yourplugin] --stats comments for more detailed information.

id before after diff
@kbn/content-management-utils 57 112 +55
Unknown metric groups

API count

id before after diff
@kbn/content-management-utils 106 177 +71

ESLint disabled line counts

id before after diff
enterpriseSearch 19 21 +2
securitySolution 400 404 +4
total +6

Total ESLint disabled count

id before after diff
enterpriseSearch 20 22 +2
securitySolution 480 484 +4
total +6

History

To update your PR or re-run it, just comment with:
@elasticmachine merge upstream

cc @mattkime

@mattkime mattkime merged commit a4a2e86 into elastic:main May 16, 2023
@kibanamachine kibanamachine added v8.9.0 backport:skip This commit does not require backporting labels May 16, 2023
jasonrhodes pushed a commit that referenced this pull request May 17, 2023
…#155680)

## Summary

Abstract class for implementing content storage for saved objects. 

---------

Co-authored-by: kibanamachine <[email protected]>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
backport:skip This commit does not require backporting Feature:Content Management User generated content (saved objects) management Feature:Maps release_note:skip Skip the PR/issue when compiling release notes Team:DataDiscovery Discover, search (e.g. data plugin and KQL), data views, saved searches. For ES|QL, use Team:ES|QL. v8.9.0
Projects
None yet
Development

Successfully merging this pull request may close these issues.

8 participants