Skip to content

Reduce boilerplate for actions that expose controller methods through the messenger #4582

Open
@MajorLift

Description

@MajorLift

If we want more parts of the system to be able to access more parts of controllers and services, then this means that the number of actions these objects will have to register will grow. Currently, creating an action that merely delegates to a method on the "messageable" requires three steps:

  • An action type must be defined
  • The action type must be added to the list of ${Controller}Actions or ${Service}Actions
  • A handler for the action must be registered in the controller which calls the method

If we can provide a way to group the methods that the developer wants to expose into a single type and register them as action handlers in bulk, then any time the developer wants to change the set of exposed methods, they only have to go to one place.

Acceptance Criteria

  • A function should exist that takes an object, and a list of methods on the object, and its messenger, and registers action handlers for the methods on the messenger
  • A utility type should exist that takes a list of method names and creates a type union of action types

(Original PR description)

Motivation

  • Currently, we manually enumerate each individual registerActionHandler call in controller constructors (example). We should have a DRY-er way of doing this.
  • Most handlers for controller internal actions are simply public controller class methods bound to the controller instance.

Proof of concept

  // TODO: Expand into base-controller utility function that batch registers action handlers.
  #registerActionHandlers() {
    const methodsExcludedFromMessenger = [
      'constructor',
      'messagingSystem',
      'setProvider',
      'provider',
      'ipfsGateway',
      'chainId',
    ];

    getKnownPropertyNames<keyof this>(Object.getPrototypeOf(this)).forEach(
      (method) => {
        if (
          ((key: keyof this): key is AssetsContractControllerMethodName =>
            !methodsExcludedFromMessenger.find((e) => e === key) &&
            typeof this[key] === 'function')(method)
        ) {
          this.messagingSystem.registerActionHandler(
            `${name}:${method}`,
            // TODO: Write a generic for-loop implementation that iterates over an input union type in tandem with the input array.
            // @ts-expect-error Both assigned argument and assignee parameter are using the entire union type for `method` instead of the type for the current element
            this[method].bind(this),
          );
        }
      },
    );
  }

https://github.com/MetaMask/core/pull/4397/files#diff-7ada7b099f6bcc6f3d4acd71f94b604636abd22cc556a76dbe7b3e610b1d9233R235-R262

Acceptance Criteria

  • MVP: Write a function that...
    • a) Accepts a list of public controller class fields and methods that should be excluded from being registered as action handlers.
      • constructor and messagingSystem should be included by default.
    • b) Iterates over a list of public controller methods, and binds them to the controller instance.
    • c) Registers these bound methods as action handlers for the corresponding internal messenger actions.
      • This iteration is correctly typed.
  • Stretch: Better handling for exceptions.
    • a) Accepts a map of action names and handlers which are exceptions to the general rule that each class method corresponds to an action handler.
    • b) Register these exceptions to the messagingSystem as well.

Metadata

Metadata

Assignees

Type

No type

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions