_initControllers(plugin) {
    // @deprecated - warn about using the obsolete "controllers" object
    if (!_.isEmpty(plugin.instance.controllers)) {
      plugin.printDeprecation(
        'Defining controllers using the "controllers" object is deprecated. You should use the "api" object instead.',
      );
    }

    for (const controller of Object.keys(plugin.instance.controllers)) {
      debug(
        "[%s][%s] starting controller registration",
        plugin.name,
        controller,
      );

      const methodsList = getMethods(plugin.instance);
      const controllerName = `${plugin.name}/${controller}`;
      const definition = plugin.instance.controllers[controller];
      const errorControllerPrefix = `Unable to inject controller "${controller}" from plugin "${plugin.name}":`;

      if (!isPlainObject(definition)) {
        // @todo deprecate all those errors and use plugin.assert.invalid_controller_definition
        throw controllerError.get(
          "invalid_description",
          errorControllerPrefix,
          typeof definition,
        );
      }

      for (const action of Object.keys(definition)) {
        debug(
          "[%s][%s][%s] starting action controller registration",
          plugin.name,
          controller,
          action,
        );

        if (
          typeof definition[action] !== "function" &&
          typeof plugin.instance[definition[action]] !== "function"
        ) {
          const suggestion =
            typeof definition[action] === "string"
              ? didYouMean(definition[action], methodsList)
              : "";

          throw controllerError.get(
            "invalid_action",
            errorControllerPrefix,
            controller,
            action,
            suggestion,
          );
        }

        let apiController = this.controllers.get(controllerName);

        if (!apiController) {
          apiController = new BaseController();
          this.controllers.set(controllerName, apiController);
        }

        if (typeof definition[action] === "function") {
          apiController._addAction(action, definition[action]);
        } else {
          apiController._addAction(
            action,
            plugin.instance[definition[action]].bind(plugin.instance),
          );
        }
      }
    }

    const httpVerbs = [
      "get",
      "head",
      "post",
      "put",
      "delete",
      "patch",
      "options",
    ];
    const routeProperties = ["verb", "url", "controller", "action", "path"];
    const controllerNames = Object.keys(plugin.instance.controllers);

    // @deprecated - warn about using the obsolete "routes" object
    if (!_.isEmpty(plugin.instance.routes)) {
      plugin.printDeprecation(
        'Defining routes using the "routes" object is deprecated. You should use the "api" object instead.',
      );
    }

    for (const route of plugin.instance.routes || []) {
      const controller = `${plugin.name}/${route.controller}`;
      const errorRoutePrefix = `Unable to inject API route "${JSON.stringify(
        route,
      )}" from plugin "${plugin.name}":`;

      for (const key of Object.keys(route)) {
        if (routeProperties.indexOf(key) === -1) {
          throw controllerError.get(
            "unexpected_route_property",
            errorRoutePrefix,
            key,
            didYouMean(key, routeProperties),
          );
        }

        if (
          typeof route[key] !== "string" ||
          (route[key].length === 0 && key !== "url")
        ) {
          throw controllerError.get(
            "invalid_route_property",
            errorRoutePrefix,
            key,
          );
        }
      }

      const apiController = this.controllers.get(controller);

      if (!apiController) {
        throw controllerError.get(
          "undefined_controller",
          errorRoutePrefix,
          route.controller,
          didYouMean(route.controller, controllerNames),
        );
      }

      if (!apiController._isAction(route.action)) {
        const actionNames = Array.from(apiController._actions);
        throw controllerError.get(
          "undefined_action",
          errorRoutePrefix,
          route.action,
          didYouMean(route.action, actionNames),
        );
      }

      if (httpVerbs.indexOf(route.verb.toLowerCase()) === -1) {
        throw controllerError.get(
          "unsupported_verb",
          errorRoutePrefix,
          httpVerbs.join(", "),
          didYouMean(route.verb, httpVerbs),
        );
      }

      // @deprecated route.url is deprecated
      if (route.url) {
        plugin.printDeprecation(
          'Usage of "url" property for routes is deprecated. Use "path" instead.',
        );
      }
      route.path = route.path || route.url;

      debug(
        '[%s] binding HTTP route "%s" to controller "%s"',
        plugin.name,
        route.path,
        route.controller,
      );

      // @deprecated "/_plugin" prefix is deprecated for plugin routes
      this.routes.push({
        action: route.action,
        controller,
        path: `/_plugin/${plugin.name}${route.path}`,
        verb: route.verb,
      });

      const routePath =
        route.path.charAt(0) === "/"
          ? `/_${route.path}`
          : `/_/${plugin.name}/${route.path}`;

      this.routes.push({
        action: route.action,
        controller,
        path: routePath,
        verb: route.verb,
      });
    }
  }
Init plugin controllers

@param {object} plugin
@returns {boolean}