stablekernel/aqueduct

"document" command fails when a multi-variable path is routed to a ManagedObjectController

Opened this issue · 3 comments

Using the "db" aqueduct template as the basis for this test, I have added an additional variable to the "model" path like so:

  @override
  Controller get entryPoint {
    final router = Router();

    router.route("/model/[:id/[:subId]]").link(() => ModelController(context));

    return router;
  }

I have also extended the ManagedObjectController with a class called ModelController which the route is now linked to.

class ModelController extends ManagedObjectController<Model> {
  ModelController(ManagedContext context) : super(context);

  @Operation.get('id', 'subId')
  Future<Response> getObjectWithIdAndSubId(
          @Bind.path('id') String id, @Bind.path('subId') String subId) =>
      getObject(id);
}

When running pub run aqueduct document, it fails with this output:

-- Aqueduct CLI Version: 4.0.0-b1
-- Aqueduct project version: 4.0.0-b1
*** Uncaught error
    Bad state: Invalid argument(s): Invalid context. The data model of 'context' does not contain 'ManagedObject<dynamic>'.
  **** Stacktrace
  * #0      new Query (package:aqueduct/src/db/query/query.dart:32:7)
  * #1      new ManagedObjectController (package:aqueduct/src/http/managed_object_controller.dart:44:14)
  * #2      new ModelController (package:openapi_test/model/model_controller.dart:6:45)
  * #3      OpenapiTestChannel.entryPoint.<anonymous closure> (package:openapi_test/channel.dart:37:54)
  * #4      Controller.link (package:aqueduct/src/http/controller.dart:110:34)
  * #5      OpenapiTestChannel.entryPoint (package:openapi_test/channel.dart:37:43)
  * #6      ApplicationChannel.documentAPI (package:aqueduct/src/application/channel.dart:166:18)
  * #7      Application.document (package:aqueduct/src/application/application.dart:167:38)
  * <asynchronous suspension>
  * #8      OpenAPIBuilder.execute (<data:application/dart>:17:1821)
  * #9      main (<data:application/dart>:13:35)
  * #10     _startIsolate.<anonymous closure> (dart:isolate-patch/isolate_patch.dart:297:32)
  * #11     _RawReceivePortImpl._handleMessage (dart:isolate-patch/isolate_patch.dart:168:12)
  ****

The failure seems to be in the documentOperations() method of the ManagedObjectController class. Particularly that it attempts to modify non-existent keys in the ops map under the assumption that all ops exist since it is a ManagedObjectController.

  @override
  Map<String, APIOperation> documentOperations(
      APIDocumentContext context, String route, APIPath path) {
    final ops = super.documentOperations(context, route, path);

    final entityName = _query.entity.name;

    if ((path.parameters
                ?.where((p) => p.location == APIParameterLocation.path)
                ?.length ??
            0) >
        0) {
      ops["get"].id = "get$entityName";
      ops["put"].id = "update$entityName";
      ops["delete"].id = "delete$entityName";
    } else {
      ops["get"].id = "get${entityName}s";
      ops["post"].id = "create$entityName";
    }

    return ops;
  }

I think the problem is that in your router you have subID, but in the controller you have subId.

The original issue was on a different codebase with different path vars. I will check this again and ensure the path var names are identical in both the original occurrence and the example reproduction.

I have confirmed that the same exception still occurs after correcting the "subID" to "subId". I have also updated the issue to reflect this.