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

Plugin types #40122

Closed
wants to merge 5 commits into from
Closed

Plugin types #40122

wants to merge 5 commits into from

Conversation

streamich
Copy link
Contributor

Summary

Adds EmptyPluginContracts default empty plugin contract type.

Checklist

Use strikethroughs to remove checklist items you don't feel are applicable to this PR.

For maintainers

@streamich streamich requested a review from mshustov July 2, 2019 09:08
@streamich streamich requested a review from a team as a code owner July 2, 2019 09:08
@streamich streamich added review release_note:skip Skip the PR/issue when compiling release notes Feature:New Platform Team:AppArch labels Jul 2, 2019
@elasticmachine
Copy link
Contributor

Pinging @elastic/kibana-app-arch

@streamich streamich added Team:Core Core services & architecture: plugins, logging, config, saved objects, http, ES client, i18n, etc and removed Feature:New Platform labels Jul 2, 2019
@elasticmachine
Copy link
Contributor

Pinging @elastic/kibana-platform

@elasticmachine
Copy link
Contributor

💚 Build Succeeded

@@ -168,8 +169,8 @@ export type PluginInitializer<
export class PluginWrapper<
TSetup = unknown,
TStart = unknown,
TPluginsSetup extends Record<PluginName, unknown> = Record<PluginName, unknown>,
TPluginsStart extends Record<PluginName, unknown> = Record<PluginName, unknown>
TPluginsSetup extends EmptyPluginContracts = EmptyPluginContracts,
Copy link
Contributor

@mshustov mshustov Jul 2, 2019

Choose a reason for hiding this comment

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

I just realised that extends EmptyPluginContracts refers to an empty interface, not object. with this change we have a problem that any real type like number, string, etc. satisfy this conditions. now I think we should rollback to Record<string, unknown> or use object instead

https://www.typescriptlang.org/play/#code/JYOwLgpgTgZghgYwgAgKIFsAOYCeAFAGwFcBzUAYQHtwpEwBnZAbwF8AoN3TFAQQB4AKgD5kAXmQDkEAB6QQAE0YZs+YmRBUadRgH5kAchiVK+5AC4DAIzhR9Abg5cUAITHJ+9MFFAkhd5AD0AchGlJw43MjkbvwgROiW0H6BwaHhkQAiMXysycj5KSHG6Sio2QBulMDyeYXWUI4RvPzCbpIycorIAEoQCJRQ8nye3iAkADTIRCAA1iCUAO4gInqGxqYW+vX2jZHOruI8Hl4+yUHI9SVR0Yex8YlQZ8GXTsgZWbc5LLX552mvqDKn0q1Vq53qQA

Copy link
Contributor Author

@streamich streamich Jul 2, 2019

Choose a reason for hiding this comment

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

@restrry

I'm not sure if that is something we should worry about, but if you want to fix that you can do:

type EmptyPluginContracts = {} & object;

or use a branded type:

type EmptyPluginContracts = {} & { __brandPluginContract?: 'yup' };

or use a branded type from some lib like utility-types:

type EmptyPluginContracts = Brand<{}, 'PluginContracts'>

Copy link
Contributor

@mshustov mshustov Jul 2, 2019

Choose a reason for hiding this comment

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

I'm not sure if that is something we should worry about

yes, right now it doesn't specify a type of a contract at all

TPluginsSetup extends object = {},

should be enough

Copy link
Contributor Author

Choose a reason for hiding this comment

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

That does not seem to work.

image

Copy link
Contributor

Choose a reason for hiding this comment

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

hm. it falls back to the empty interface as well. either options should work

TPluginsSetup extends object = object,
TPluginsSetup extends Record<string, unknown> = Record<string, unknown>,

Copy link
Contributor Author

Choose a reason for hiding this comment

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

@restrry

After playing with it for a bit, I think I have something that cover "all" 4 use cases:

  1. Unknown properties cannot be accessed.
  2. Plugin contract cannot be a primitive "number" type.
  3. If plugin contract is not specified, it still cannot be a primitive like "number".
  4. Plugin can have no contract.

image

Copy link
Contributor Author

@streamich streamich Jul 2, 2019

Choose a reason for hiding this comment

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

Ah, actually object/object this way seems to cover those cases too:

TPluginsSetup extends (object | void) = object

OK, I know why. Because type NonPrimitive = object, so my NotPrimitive<{}> is basically object. 😄

If you do that same thing with Record it does not work.

TPluginsSetup extends (Record<string, unknown> | void) = Record<string, unknown>

Copy link
Contributor Author

Choose a reason for hiding this comment

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

@restrry so, I'm gonna implement

TPluginsSetup extends (object | void) = object

if you are okay with that?

Copy link
Contributor

Choose a reason for hiding this comment

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

should be ok

@streamich streamich requested a review from a team as a code owner July 3, 2019 20:59
@@ -117,15 +117,17 @@ test('`setupPlugins` throws if plugins have circular optional dependency', async

test('`setupPlugins` ignores missing optional dependency', async () => {
const plugin = createPlugin('some-id', { optional: ['missing-dep'] });
jest.spyOn(plugin, 'setup').mockResolvedValue('test');
jest.spyOn(plugin, 'setup').mockResolvedValue({ foo: 'bar' });
Copy link
Contributor Author

@streamich streamich Jul 3, 2019

Choose a reason for hiding this comment

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

TypeScript found these "errors" in tests—plugin contract must be an object.

@elasticmachine
Copy link
Contributor

💚 Build Succeeded

Copy link
Member

@lukeelmers lukeelmers left a comment

Choose a reason for hiding this comment

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

LGTM! Thanks for doing this; I was noticing recently we were missing a way to have empty contracts.

I'll defer to someone on the platform team for the implementation on the core stuff, but it all makes sense to me.

@@ -20,7 +20,7 @@
import { PluginInitializerContext, CoreSetup, CoreStart, Plugin } from '../../../core/public';
import { ExpressionsService } from './expressions/expressions_service';

export class DataPublicPlugin implements Plugin<{}> {
export class DataPublicPlugin implements Plugin<ReturnType<DataPublicPlugin['setup']>, void> {
Copy link
Member

Choose a reason for hiding this comment

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

Just a note that I'm working on some conventions for the plugin files here which would include exporting a dedicated DataSetup interface that you could pass to this generic: #39554

Would be interested in your thoughts on that PR if you have time to review!

/**
* Custom window type for loading bundles. Do not extend global Window to avoid leaking these types.
* @internal
*/
export interface CoreWindow {
__kbnNonce__: string;
__kbnBundles__: {
[pluginBundleName: string]: UnknownPluginInitializer | undefined;
[pluginBundleName: string]: object | undefined;
Copy link
Contributor

Choose a reason for hiding this comment

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

Not sure it's a correct.__kbnBundles__ contains init functions, not objects.
https://github.com/restrry/kibana/blob/af295b43bdf27bcefd7ed293fa97e96a9656add0/src/core/public/plugins/plugin_loader.ts#L91-L99

Suggested change
[pluginBundleName: string]: object | undefined;
[pluginBundleName: string]: PluginInitializer | undefined;

Copy link
Contributor

@mshustov mshustov Jul 4, 2019

Choose a reason for hiding this comment

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

That actually illustrates another problem with the current code. any not primitive type is a valid one
2019-07-04_10-14-30

@streamich streamich closed this Aug 12, 2019
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
release_note:skip Skip the PR/issue when compiling release notes review Team:Core Core services & architecture: plugins, logging, config, saved objects, http, ES client, i18n, etc
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants