jhthorsen / mojolicious-plugin-openapi

OpenAPI / Swagger plugin for Mojolicious

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

Generated v3 spec generates validation errors on editor.swagger.io

HEM42 opened this issue · comments

Hi,

I noticed that the generated spec generates validation errors on editor.swagger.io, using your own example converted to OpenAPIv3, see code at the end

There is multiple problems, A few i would be able to submit a PR on :

Definitions belongs under {components}{schemas}, not responses.
Also swagger.io complains about the extra basePath and host in the spec

diff --git a/lib/Mojolicious/Plugin/OpenAPI.pm b/lib/Mojolicious/Plugin/OpenAPI.pm
index 8df0dce..96d885c 100644
--- a/lib/Mojolicious/Plugin/OpenAPI.pm
+++ b/lib/Mojolicious/Plugin/OpenAPI.pm
@@ -72,12 +72,12 @@ sub _add_default_response {

   my $ref
     = $self->validator->version ge '3'
-    ? ($schema_data->{components}{responses}{$name} ||= $self->_default_schema)
+    ? ($schema_data->{components}{schemas}{$name} ||= $self->_default_schema)
     : ($schema_data->{definitions}{$name} ||= $self->_default_schema);

   my %schema
     = $self->validator->version ge '3'
-    ? ('$ref' => "#/components/responses/$name")
+    ? ('$ref' => "#/components/schemas/$name")
     : ('$ref' => "#/definitions/$name");

   tie %schema, 'JSON::Validator::Ref', $ref, $schema{'$ref'}, $schema{'$ref'};
diff --git a/lib/Mojolicious/Plugin/OpenAPI/SpecRenderer.pm b/lib/Mojolicious/Plugin/OpenAPI/SpecRenderer.pm
index 24db353..f2c868c 100644
--- a/lib/Mojolicious/Plugin/OpenAPI/SpecRenderer.pm
+++ b/lib/Mojolicious/Plugin/OpenAPI/SpecRenderer.pm
@@ -82,8 +82,12 @@ sub _render_spec {
   my $format = $c->stash('format') || 'json';
   my $spec   = $self->{bundled} ||= $self->validator->bundle;

-  local $spec->{basePath} = $c->url_for($spec->{basePath});
-  local $spec->{host}     = $c->req->url->to_abs->host_port;
+  if ( $self->validator->version ge '3' ) {
+    delete $spec->{basePath};
+  } else {
+    local $spec->{dbasePath} = $c->url_for($spec->{basePath});
+    local $spec->{host}     = $c->req->url->to_abs->host_port;
+  }

   return $c->render(json => $spec) unless $format eq 'html';
   return $c->render(

But the last issue here, I was not able to fix, and I hope you can assist on this.

The default responses, even if the ref is added correctly at line 83 in lib/Mojolicious/Plugin/OpenAPI.pm

tie %schema, 'JSON::Validator::Ref', $ref, $schema{'$ref'}, $schema{'$ref'};

Its still rendered as following, under definitions:, which dont belong in v3 spec

  "definitions": {
    "__components_responses_DefaultResponse": {
      "properties": {
        "errors": {
          "items": {
            "properties": {
              "message": {
                "type": "string"
              },
              "path": {
                "type": "string"
              }
            },
            "required": [
              "message"
            ],
            "type": "object"
          },
          "type": "array"
        }
      },
      "required": [
        "errors"
      ],
      "type": "object"
    }
  },

Following is the code used

use Mojolicious::Lite;

# Will be moved under "basePath", resulting in "POST /api/echo"
post "/echo" => sub {
  my $c = shift->openapi->valid_input or return;
  my $data = {body => $c->validation->param("body")};
  $c->render(openapi => $data);
}, "echo";

# Load specification and start web server
plugin OpenAPI => {url => "data:///spec.json", schema => "v3"};
app->start;

__DATA__
@@ spec.json
{
  "openapi": "3.0.0",
  "info": {
    "title": "Echo Service",
    "version": "0.8"
  },
  "paths": {
    "/echo": {
      "post": {
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "type": "object"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Echo response",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                }
              }
            }
          }
        },
        "x-mojo-name": "echo"
      }
    }
  },
  "servers": [
    {
      "url": "/api"
    }
  ]
}

Thanks for reporting in!

...A few i would be able to submit a PR on

Yes, please do submit a PR, with updated/new tests. (Please note that the PR should also be run through perltidy)

Definitions belongs under ./components/schemas, not ./responses.

Could you please provide a reference to the documentation? It seems very random that I would place it under "responses". Or would you happen to know if the spec has changed? If so, I should probably update the bundled spec in JSON::Validator

extra basePath and host in the spec

Aiai. Not cool. Please fix that in the PR as well.

The default responses, ..., Its still rendered as following, under definitions:, which dont belong in

v3 spec
Is "definitions" not allowed in the v3 spec? This is an issue with how JSON::Validator works, so it probably needs fixing in both modules.

I'm sorry I haven't paid much attention to the v3 spec. The reason is simply that I don't use it myself. Any contribution to make it work better is greatly appreciated!

Thanks for reporting in!

...A few i would be able to submit a PR on

Yes, please do submit a PR, with updated/new tests. (Please note that the PR should also be run through perltidy)

extra basePath and host in the spec

Aiai. Not cool. Please fix that in the PR as well.

Will work on that

Definitions belongs under ./components/schemas, not ./responses.

Could you please provide a reference to the documentation? It seems very random that I would place it under "responses". Or would you happen to know if the spec has changed? If so, I should probably update the bundled spec in JSON::Validator

You are right in this, I should have read the spec (I simply converted my v2 spec, and I didn't pay attention.

So reponses is actual the correct placement, so no real issue here, now that I look at it, it complains about the structure, which I will try to fix as well.

The default responses, ..., Its still rendered as following, under definitions:, which dont belong in v3 spec
Is "definitions" not allowed in the v3 spec? This is an issue with how JSON::Validator works, so it probably needs fixing in both modules.

I'm sorry I haven't paid much attention to the v3 spec. The reason is simply that I don't use it myself. Any contribution to make it work better is greatly appreciated!

Not sure that is not allowed. But its not a part fixed fields in the spec : https://swagger.io/specification/#oasObject

Since the responses is actually build in Mojolicious::Plugin::OpenAPI, but not used, since JSON::Validator creates the "definitions".

Since you mentioned JSON::Validator, I looked at it, and found this passage in the doc for bundle()

    Used to create a new schema, where there are no "$ref" pointing to
    external resources. This means that all the "$ref" that are found, will be
    moved into the "definitions" key, in the returning $schema.

I guess its here the problem lies, that JSON::Validator also creates the "definitions", even if the $ref is #/components/responses/DefaultResponse

Im uncertain on how to fix that, but will have a look there as well. If you have a good idea on where exactly I could look

it comes in as

\ {
    $ref   "#/components/responses/DefaultResponse"
} (tied to JSON::Validator::Ref)

but comes out as

\ {
  $ref   "#/definitions/__components_responses_DefaultResponse"
} (tied to JSON::Validator::Ref)

I guess one difference from v2 to v3 is that the it went from simple structured hash (definitions names as keys), to a more complex structure with components, type, and then the actual "definition" as keys

Definitions belongs under ./components/schemas, not ./responses.

Could you please provide a reference to the documentation? It seems very random that I would place it under "responses". Or would you happen to know if the spec has changed? If so, I should probably update the bundled spec in JSON::Validator

You are right in this, I should have read the spec (I simply converted my v2 spec, and I didn't pay attention.

So reponses is actual the correct placement, so no real issue here, now that I look at it, it complains about the structure, which I will try to fix as well.

Worth mentioning, my initial assumption was right on this (wrong placement). The type of object you create for the response, is in fact a Schema object, have a look at

https://swagger.io/specification/#componentsObject

There is a Response object defined, but has a different structure.

Doesn't look like the spec has changed for this

So for a final change to make the ref paths generated correctly for v3, which the latest J::V release makes possible by overriding generate_definitions_path() in JSON::Validator::OpenAPI::Mojolicious

Im not quite what the right approach could be, both works for all tests; first one takes version into account, so no changes for version 2:

https://gist.github.com/HEM42/2f4d9cfbc67d451d0c543bb95a0bacc0

Second approach, handles both v2 and v3 in a similar fashion:

https://gist.github.com/HEM42/986d0ce0bf81a879c4e1cf3120b2c3ff

If one of these approaches can be agreed upon, Ill happy make the PR for it :)

#152 was merged, this issue can now be closed