Skip to content

Commit 84f379f

Browse files
author
Jan Henning Thorsen
committed
Add support for "nullable" in OpenAPI 3.0, closes #106
1 parent 425e2ef commit 84f379f

File tree

4 files changed

+98
-9
lines changed

4 files changed

+98
-9
lines changed

Changes

+2-1
Original file line numberDiff line numberDiff line change
@@ -2,10 +2,11 @@ Revision history for perl distribution Mojolicious-Plugin-OpenAPI
22

33
2.12 Not Released
44
- Fix HEAD requests #105
5-
- Bump JSON::Validator to 3.06
5+
- Add support for "nullable" in OpenAPI 3.0 #106
66
- Improved handling of Accept header in OpenAPI v3 #104
77
Can now handle wildcards, such as application/* and */*, even though not
88
defined in the specification.
9+
- Bump JSON::Validator to 3.06
910

1011
2.11 2019-01-26T11:37:15+0900
1112
- Fix allowing regular requests with "openapi_cors_allowed_origins" #103

lib/JSON/Validator/OpenAPI/Mojolicious.pm

+24-7
Original file line numberDiff line numberDiff line change
@@ -296,6 +296,10 @@ sub _set_request_data {
296296
}
297297
}
298298

299+
sub _to_list {
300+
return ref $_[0] eq 'ARRAY' ? @{$_[0]} : $_[0] ? ($_[0]) : ();
301+
}
302+
299303
sub _validate_request_body {
300304
my ($self, $c, $body_schema) = @_;
301305
my $ct = $c->req->headers->content_type // '';
@@ -387,19 +391,32 @@ sub _validate_type_file {
387391
}
388392

389393
sub _validate_type_object {
390-
return shift->SUPER::_validate_type_object(@_) unless $_[0]->{validate_input};
391-
392394
my ($self, $data, $path, $schema) = @_;
393-
my $properties = $schema->{properties} || {};
394-
my $discriminator = $schema->{discriminator};
395-
my (%ro, @e);
395+
return shift->SUPER::_validate_type_object(@_) unless ref $data eq 'HASH';
396+
397+
# Support "nullable" in v3
398+
# "nullable" is the same as "type":["null", ...], which is supported by many
399+
# tools, even though not officially supported by OpenAPI.
400+
my %properties = %{$schema->{properties} || {}};
401+
local $schema->{properties} = \%properties;
402+
if ($self->version eq '3') {
403+
for my $key (keys %properties) {
404+
next unless $properties{$key}{nullable};
405+
$properties{$key} = {%{$properties{$key}}};
406+
$properties{$key}{type} = ['null', _to_list($properties{$key}{type})];
407+
}
408+
}
396409

397-
for my $p (keys %$properties) {
398-
next unless $properties->{$p}{readOnly};
410+
return shift->SUPER::_validate_type_object(@_) unless $self->{validate_input};
411+
412+
my (@e, %ro);
413+
for my $p (keys %properties) {
414+
next unless $properties{$p}{readOnly};
399415
push @e, JSON::Validator::E("$path/$p", "Read-only.") if exists $data->{$p};
400416
$ro{$p} = 1;
401417
}
402418

419+
my $discriminator = $schema->{discriminator};
403420
if ($discriminator and !$self->{inside_discriminator}) {
404421
my $name = $data->{$discriminator}
405422
or return JSON::Validator::E($path, "Discriminator $discriminator has no value.");

t/v3-nullable.t

+72
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,72 @@
1+
use Mojo::Base -strict;
2+
use Test::Mojo;
3+
use Test::More;
4+
5+
use Mojolicious::Lite;
6+
7+
plan skip_all => $@ unless eval 'use YAML::XS 0.67;1';
8+
9+
my %data = (id => 42);
10+
get '/nullable-data' => sub {
11+
my $c = shift->openapi->valid_input or return;
12+
$c->render(openapi => \%data);
13+
},
14+
'withNullable';
15+
16+
plugin OpenAPI => {url => 'data:///nullable.json', schema => 'v3'};
17+
18+
my $t = Test::Mojo->new;
19+
$t->get_ok('/nullable-data')->status_is(500);
20+
21+
$data{name} = undef;
22+
$t->get_ok('/nullable-data')->status_is(200);
23+
24+
$data{name} = 'batgirl';
25+
$t->get_ok('/nullable-data')->status_is(200);
26+
27+
done_testing;
28+
29+
__DATA__
30+
@@ nullable.json
31+
{
32+
"openapi": "3.0.0",
33+
"info": {
34+
"license": {
35+
"name": "MIT"
36+
},
37+
"title": "Swagger Petstore",
38+
"version": "1.0.0"
39+
},
40+
"servers": [
41+
{ "url": "http://petstore.swagger.io/v1" }
42+
],
43+
"paths": {
44+
"/nullable-data": {
45+
"get": {
46+
"operationId": "withNullable",
47+
"summary": "Dummy",
48+
"responses": {
49+
"200": {
50+
"description": "type:[null, string, ...] does the same",
51+
"content": {
52+
"application/json": {
53+
"schema": { "$ref": "#/components/schemas/WithNullable" }
54+
}
55+
}
56+
}
57+
}
58+
}
59+
}
60+
},
61+
"components": {
62+
"schemas": {
63+
"WithNullable": {
64+
"required": [ "id", "name" ],
65+
"properties": {
66+
"id": { "type": "integer", "format": "int64" },
67+
"name": { "type": "string", "nullable": true }
68+
}
69+
}
70+
}
71+
}
72+
}

t/v3.t

-1
Original file line numberDiff line numberDiff line change
@@ -68,7 +68,6 @@ $t->post_ok('/pets', {Cookie => 'debug=1'}, json => {id => 1, name => 'Supercow'
6868

6969
$t->post_ok('/pets', form => {id => 1, name => 'Supercow'})->status_is(201)->content_is('');
7070

71-
7271
done_testing;
7372

7473
__DATA__

0 commit comments

Comments
 (0)