jhthorsen / mojolicious-plugin-openapi

OpenAPI / Swagger plugin for Mojolicious
54 stars 44 forks source link

Array coersion doesn't work with $ref #251

Closed simon816 closed 3 months ago

simon816 commented 4 months ago

When a parameter's schema is an array type, it is incorrectly converted to a scalar if using $ref.

Version: 5.09, JSON::Validator: 5.14

Example app:

package Myapp;
use Mojo::Base "Mojolicious";

sub startup {
    my $app = shift;
    $app->plugin("OpenAPI" => {url => "data:///api.yaml"});
}

package Myapp::Controller::Array;
use Mojo::Base "Mojolicious::Controller";

sub arraytest {
  my $c = shift->openapi->valid_input or return;
  $c->render(openapi => $c->validation->output->{idlist});
}

Myapp->new->start;

__DATA__
@@ api.yaml
openapi: "3.0.0"
info:
  version: 1.0.0
  title: Array test
servers:
  - url: /
paths:
  /arraytest:
    get:
      x-mojo-to: Array#arraytest
      parameters:
        - name: idlist
          in: query
          schema:
            $ref: '#/components/schemas/IdList'
      responses:
        200:
          description: Test response
          content:
            application/json:    
              schema:
                $ref: '#/components/schemas/IdList'
components:
  schemas:
    IdList:
      type: array
      items:
        type: integer

When requesting /arraytest with one or more idlist parameters, it returns this error:

$ curl "localhost:3000/arraytest?idlist=1&idlist=2"
{"errors":[{"message":"Expected array - got string.","path":"\/idlist"}],"status":400}

If I inline the IdList schema like so:

      parameters:
        - name: idlist
          in: query
          schema:
            type: array
            items:
              type: integer

Then it works as expected:

$ curl "localhost:3000/arraytest?idlist=1&idlist=2"
[1,2]

I've identified the _coerce_arrays function is where the problem comes from:

https://github.com/jhthorsen/json-validator/blob/v5.14/lib/JSON/Validator/Schema/OpenAPIv2.pm#L226

It converts the idlist parameter to a scalar string.

One possible fix is the following:

my $schema = $param->{schema} || $param;
$schema = $self->get($schema->{'$ref'} =~ s!^#!!r) if $schema->{'$ref'};
my $schema_type = schema_type $schema;

I'm wondering whether $ref should even make it this far though, given the comment on https://github.com/jhthorsen/json-validator/blob/v5.14/lib/JSON/Validator/Schema/OpenAPIv2.pm#L287

Resolve all $ref before returning the data

It seems this doesn't do what it says here.

jhthorsen commented 3 months ago

As you pointed out, it seems like this issue is in JSON::Validator. Would you mind opening a PR with your change for that repo?