jhthorsen / mojolicious-plugin-openapi

OpenAPI / Swagger plugin for Mojolicious
53 stars 41 forks source link

Can't call method "validator" on an undefined value #233

Closed Ovid closed 2 years ago

Ovid commented 2 years ago

I'm trying to take the Mojolicious:Lite 'echo' example from the docs and play around with turning that into a well-structured mojo app, but I'm getting the following error:

Can't call method "validator" on an undefined value at .../Mojolicious/Plugin/OpenAPI.pm line 200.

I've stripped out all other code to reduce this to a minimal use case.

My main class:

package My::Example;

use Mojo::Base 'Mojolicious', -signatures;

sub startup ($app) {
    $app->plugin( OpenAPI => { url => $app->home->rel_file('data/openapi.yaml') } );

    my $r = $app->routes;
    $r->post('/echo')->to('echo#echo');
    $app->secrets( ['Shut up, Mojo'] );
    $app->start;
}

1;

My controller class:

package My::Example::Controller::Echo;
use Mojo::Base 'Mojolicious::Controller', -signatures;

sub echo ($self) {
    $self = $self->app->openapi->valid_input or return;
    # XXX the line above is where we blow up

    # generate some data
    my $data = { body => $self->req->json };

    $self->render( openapi => $data );
}

1;

My OpenAPI spec:

swagger: 2.0
basePath: /api
schemes:
  - http
info:
  title: Quizzes
  version: 1.0
paths:
  /echo:
    post:
      operationId: echo
      parameters:
        - in: body
          name: body
          schema:
            type: object
      responses:
        200:
          description: Echo response
          schema:
            type: object

And the test that is generating the error:

#!/usr/bin/env perl

use Test::Mojo;
use Test::More;

my $t = Test::Mojo->new('My::Example');

# Post a JSON document
$t->post_ok(
    '/echo' => json => { event => 'full moon' },
    'We can send echo dasta'
  )    #
  ->status_is( 200, '... and get an OK status' )    #
  ->json_is(
    '/body' => { event => 'full moon' },
    '... the data should be in the body'
  )                                                 #
  ->header_is(
    'Content-Type',
    'application/json;charset=UTF-8',
    '... and it should be a JSON content-type'
  );                                                #

done_testing;

That test worked fine when I was using a Lite app. I've searched like mad all through the docs and even tests to figure out what I'm doing wrong, but to no avail. Help?

Ovid commented 2 years ago

Note that I have a basePath: /api in the spec, even though my test calls /echo.

Changing that to /api/echo in the test gets me:

t/basic.t .. [2022-03-12 15:00:12.65159] [10099] [trace] [Txd4wWPlecBL] POST "/api/echo"
[2022-03-12 15:00:12.65195] [10099] [trace] [Txd4wWPlecBL] Template "echo.html.ep" not found
[2022-03-12 15:00:12.65244] [10099] [error] [Txd4wWPlecBL] Route without action and nothing to render at /Users/ovid/perl5/perlbrew/perls/perl-5.26.3/lib/site_perl/5.26.3/Mojolicious.pm line 125.

[2022-03-12 15:00:12.65269] [10099] [trace] [Txd4wWPlecBL] 500 Internal Server Error (0.001104s, 905.797/s)

I've also tried changing my main class:

    # $r->post('/echo')->to('echo#echo');
    $r->post('/api/echo')->to('echo#echo');

But I get the same error (but since I have a basePath:, it wasn't actually necessary to do that in the Lite app). There seems to be very little documentation about using this plugin with a full Mojo app.

If I remove the basePath: /api from the config, I get the "Route without action" error.

jhthorsen commented 2 years ago

This is incorrect:

$self->app->openapi->valid_input

it should be:

$self->openapi->valid_input

The helper can’t be called on the app, since you can load the plugin multiple times, allowing each route work on a different spec and a different validator instance.

Ovid commented 2 years ago

@jhthorsen: Thank you for that explanation. It's very clear.

Unfortunately, I get the same error with $self->openapi->valid_input.

jhthorsen commented 2 years ago

When I look at your code closer I find more issues, but it boils down to the helpers not being called inside an OpenAPI powered action.

The SYNOPSIS is too simple and it has created so much confusion for users that I consider getting rid of it. Please read one of the following (or both) documents instead:

Just to give you some pointers: (They are documented in the guides above)

Feel free to hang around in #perl-openapi and I'll try to guide you if you have further questions after reading the guides, but because of the timezone I might not be available right away.

jhthorsen commented 2 years ago

I tried to make it a bit more clear in the documentation now that you should read the guides: https://metacpan.org/release/JHTHORSEN/Mojolicious-Plugin-OpenAPI-5.04/view/lib/Mojolicious/Plugin/OpenAPI.pm#SYNOPSIS