-
Notifications
You must be signed in to change notification settings - Fork 8.3k
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
Restructure SavedObject types internal representation #56378
Restructure SavedObject types internal representation #56378
Conversation
it('Fails if duplicate mappings are defined', () => { | ||
const options = mockOptions(); | ||
options.savedObjectMappings = [ | ||
{ | ||
pluginId: 'aaa', | ||
properties: { amap: { type: 'text' } }, | ||
}, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The registry already ensure types are only registered once. Boths tests and Map implementation are ensuring this, the test was obsolete.
export type SavedObjectMigrationFn = ( | ||
doc: RawSavedObjectDoc, | ||
log: SavedObjectsMigrationLogger | ||
) => RawSavedObjectDoc; | ||
|
||
export const schemaMock = { | ||
create: createSchemaMock, | ||
}; | ||
export interface SavedObjectMigrationMap { | ||
[version: string]: SavedObjectMigrationFn; | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is undocumented for now as SavedObjectsType
is still an internal type.
mock.isHidden.mockReturnValue(false); | ||
mock.isNamespaceAgnostic.mockImplementation((type: string) => type === 'global'); | ||
|
||
return mock as any; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I couldn't find how to properly type the mock. TS keep being unhappy about the types
private field on SavedObjectTypeRegistry
. I didn't want to introduce an interface ONLY to have proper mock typing, so I force-casted.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🤷♂ we've done in this in other places. You don't necessarily have to actually export the interface, just use it for the mock and export the mock function. It will be type compatible while still making sure the mock is the correct type.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Issue is not about returning the interface, it's that my mock doesn't respect the jest.Mocked<SavedObjectTypeRegistry>
type, as it's does not have the readonly types
property. Changing the mock type to jest.Mocked<PublicMethodsOf<SavedObjectTypeRegistry>>
fails when using the mock with
Argument of type 'Mocked<Pick<SavedObjectTypeRegistry, "getType" | "registerType" | "getAllTypes" | "isNamespaceAgnostic" | "isHidden" | "getIndex">>' is not assignable to parameter of type 'SavedObjectTypeRegistry'.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The only problem here is that internal mocks aren't type-safe and might be out of sync when SavedObjectTypeRegistry
updates the interface. #54906 postponed, probably adding a separate interface is not such a bad idea.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Added the ISavedObjectTypeRegistry
interface
const converted = convertLegacyTypes(uiExports, legacyConfig); | ||
expect(converted).toMatchSnapshot(); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I usually hate snapshot testing, however the structure generated is just too big to manually assert or even using inline snapshots.
src/core/server/server.api.md
Outdated
// @public (undocumented) | ||
export interface SavedObjectsComplexFieldMapping { | ||
// (undocumented) | ||
dynamic?: string; | ||
// (undocumented) | ||
properties: SavedObjectsMappingProperties; | ||
// (undocumented) | ||
type?: string; | ||
} | ||
|
||
// @public (undocumented) | ||
export interface SavedObjectsCoreFieldMapping { | ||
// (undocumented) | ||
enabled?: boolean; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
All these types are made public because they are SavedObjectMapping
underlying types. However we are closer to ES definitions than SO here, so I'm not sure documenting them got much value.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can we link to elasticsearch docs describing the format of the mappings? https://www.elastic.co/guide/en/elasticsearch/reference/current/mapping-types.html
Is it possible to add proper typings for in the future? For example, type
is not just a string, but subset of strings.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can we link to elasticsearch docs describing the format of the mappings?
Yea (thanks for the link). However these type are wrongly defined as public
(as I picked the commits from my 'mappings' PR), and are not ATM exposed in any API. I would like to keep all documentation in the next PR where we expose registerType
. Would declaring them back to internal
for now be acceptable?
Is it possible to add proper typings for in the future?
We do want that. However I would really like to avoid redefining all direct ES types, and I think these types are not defined in the ES library we are using. Maybe this could be resolved when using the new es
node library?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
However I would really like to avoid redefining all direct ES types, and I think these types are not defined in the ES library we are using. Maybe this could be resolved when using the new es node library?
I didn't find them in elasticsearch-js
either. @delvedor are there any plans to add them?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Hello! The new js client does not have type definitions for Elasticsearch's request and response bodies, only for URL and query parameters.
It does offer a solution for fixing this through generics, see the typescript documentation.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Added link to the documentation. The generic does not answer our needs though, as in our part of the SO code, we have no way to be aware of the actual SO type we are working on.
We will have to improve the typings on our side I guess, but this should be done in another PR (We need improvement on pretty much everything in the src/core/server/saved_objects
subfolders...)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We need improvement on pretty much everything in the src/core/server/saved_objects subfolders...
Would you mind creating a list of follow-up tasks?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Sure, will add this as a condition to merge this PR.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Created #56856
const savedObjectTypes = convertLegacyTypes(uiExports, config); | ||
const typeRegistry = new SavedObjectTypeRegistry(); | ||
savedObjectTypes.forEach(type => typeRegistry.registerType(type)); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Adapted the ES-archiver to use new format. #55860 still need to be addressed.
const schema = new SavedObjectsSchema(kbnServer.uiExports.savedObjectSchemas); | ||
const schema = new SavedObjectsSchema(convertTypesToLegacySchema(typeRegistry.getAllTypes())); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I wanted to totally get rid of SavedObjectSchema
, however the legacy service is still exposing it. Had to keep compatibility by converting back the types to the old schema format.
// as we use this Schema solely to interact with Tasks, we | ||
// can initialise it with solely the Tasks schema | ||
const serializer = new SavedObjectsSerializer(new SavedObjectsSchema(savedObjectSchemas)); | ||
return new TaskManager({ | ||
taskManagerId: core.uuid.getInstanceUuid(), | ||
config, | ||
savedObjectsRepository, | ||
serializer, | ||
serializer: savedObjectsSerializer, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@elastic/kibana-alerting-services the serializer now requires an internal core type to be instantiated. I exposed an API from core to retrieve a configured serializer, and adapted your call to use it instead.
The createTaskManager
method no longer relies on any legacy API, however I did not feel confident on impacting too much you codebase, so I kept the call as is.
You should be good to remove the registerLegacyAPI
trick in x-pack/legacy/plugins/task_manager/server/index.ts
and x-pack/plugins/task_manager/server/plugin.ts
once this lands.
@joshdover @restrry I think I addressed all your current comments. PTAL |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Haven't worked through all the changes, but wanted to give the feedback I have so long.
}; | ||
export type SavedObjectMigrationFn = ( | ||
doc: RawSavedObjectDoc, | ||
log: SavedObjectsMigrationLogger |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Similar to the SavedObjectsMigrationLogger
we could make breaking changes and refactor the existing migrations, but since many migrations are written in javascript and a single uncaught exception can prevent kibana from starting up, this is risky. So I think it's better we keep the API and refactor it post 8.0.0.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Code LGTM, just took a look at the modified test where KibanaApp is Codeowner.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The changes to operations team files LGTM
Could you explain when the On startup? Linked to migrations? Also, we probably want |
That would be core's We are talking about savedObjects |
* A map of {@link SavedObjectMigrationFn | migration functions} to be used for a given type. | ||
* The map's keys must be valid semver versions. | ||
* | ||
* Migrations will be executed in order, starting from the lowest matching an higher version that the document |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Nit suggestion:
For a given document, only migrations with a higher version number than that of the document will be applied. Migrations are executed in order, starting from the lowest version and ending with the highest one.
public isNamespaceAgnostic(type: string) { | ||
return this.types.get(type)?.namespaceAgnostic ?? false; | ||
} | ||
|
||
public isHidden(type: string) { | ||
return this.types.get(type)?.hidden ?? false; | ||
} | ||
|
||
public getIndex(type: string) { | ||
return this.types.get(type)?.indexPattern; | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Maybe you can add a comment to the code?
…bjects-structure-change
💚 Build SucceededHistory
To update your PR or re-run it, just comment with: |
* adapt types and tests to prepare for new NP api * rename and export public types * update generated doc * first implementation of registerMappings * adapt es archiver to convert legacy mappings * update generated doc * fix more tests * add unit tests * add legacy-compat unit test * add documentation and examples * Introduce SavedObjectTypeRegistry and SavedObjectType types * add and fix tests * expose createSerializer API and fix usages * remove registerMappings API, add internal registerType * revert changes to migration guide * adapt ES-archiver migrator creation * export serializer-related types * update generated doc * add and use convertTypesToLegacySchema * remove / move to internal some mapping types * fix forEach closure context * add missing docs * fix core path * some nits * fix so_mixin tests * fix integration tests * fix integration tests for real * add documentation on serializer + restructure files and types * nit * add and use the ISavedObjectTypeRegistry interface * Add documentation, deprecates migrationLogger#warning * better typing for SavedObjectsRawDoc._source * nits * update generated doc * remove exposition of SavedObjectsTypeMappingDefinitions, update doc * creates so internal contracts mocks * improve documentation
* adapt types and tests to prepare for new NP api * rename and export public types * update generated doc * first implementation of registerMappings * adapt es archiver to convert legacy mappings * update generated doc * fix more tests * add unit tests * add legacy-compat unit test * add documentation and examples * Introduce SavedObjectTypeRegistry and SavedObjectType types * add and fix tests * expose createSerializer API and fix usages * remove registerMappings API, add internal registerType * revert changes to migration guide * adapt ES-archiver migrator creation * export serializer-related types * update generated doc * add and use convertTypesToLegacySchema * remove / move to internal some mapping types * fix forEach closure context * add missing docs * fix core path * some nits * fix so_mixin tests * fix integration tests * fix integration tests for real * add documentation on serializer + restructure files and types * nit * add and use the ISavedObjectTypeRegistry interface * Add documentation, deprecates migrationLogger#warning * better typing for SavedObjectsRawDoc._source * nits * update generated doc * remove exposition of SavedObjectsTypeMappingDefinitions, update doc * creates so internal contracts mocks * improve documentation
Summary
Fix #55857
Main goal of the PR is to introduce the new
SavedObjectType
type that is an aggregation of current SOschema
,mappings
andmigrations
(it will also handlevalidations
) later but this is outside the scope of this PR)Old legacy SO types registration are converted to the new format during the service
setup
phase.PR also introduce the new
SavedObjectTypeRegistry
registry, that superseed the responsibilities of the now deprecatedSavedObjectsSchema
. It keep the list of all registered types, and is propagated alongs the SO internal services, such as theKibanaMigrator
. This avoid passingsavedObjectSchemas
,savedObjectsMappings
andsavedObjectsMigrations
everywhere and greatly simplifies a lot of internal APIs and calls.Other things this PR does:
SavedObjectsSchema
to be able to instanciate their ownSavedObjectSerializer
. as the serializer now relies on aSavedObjectTypeRegistry
, and as I did not want the registry to be accessible, I added acreateSerializer
API toSavedObjectStart
to be able to get a ready-to-use serializer.SavedObjectSerializer
andSavedObjectType
are exported and referencing themregisterType
API to saved object internal setup contract. This is only here for testing for now, and can only be made public once Adapt ES-Archiver to use savedObject types defined in the new platform #55860 is resolved.LegacyConfig
usages in theSavedObject
codebase. As legacy types are converted during the setup phase, the legacy config is no longer needed on lower levels.Things this PR does not:
validations
(legacy validations are still used, but are not included in the newSavedObjectType
structureChecklist
Use
strikethroughsto remove checklist items you don't feel are applicable to this PR.[ ] This was checked for cross-browser compatibility, including a check against IE11[ ] Any text added follows EUI's writing guidelines, uses sentence case text and includes i18n support[ ] This was checked for keyboard-only and screenreader accessibilityFor maintainers