view release on metacpan or search on metacpan
1.34 2017-11-30T16:29:26Z
- Define prototype of type before calling it
1.33 2017-11-30T14:39:09Z
- Resolve anyOf, allOf and oneOf keywords when serving Mock Server and API Document
1.32 2017-11-30T06:08:51Z
- MockServer always returns bytes
1.31 2017-11-29T02:30:47Z
- MockServer support `response => {body => 'bmi', encoding => 'json'}` syntax
1.30 2017-11-20T17:00:38Z
- Empty input could pass validations unexpectedly
- Add . to lib
1.29 2017-11-20T01:34:11Z
- Handle boolean on generating document(1.28 was broken)
1.28 2017-11-17T09:49:53Z
- Handle boolean on generating document
1.27 2017-09-19T09:58:40Z
- Sort keys when encoding JSON
1.26 2017-08-30T02:20:58Z
- Expand `FETCH` method to `GET` and `HEAD`
1.25 2017-08-29T02:31:53Z
- You can inherit Plack::App::APISchema::Document and customize template
1.24 2016-06-10T10:01:57Z
- Fix document
eg/bmi.psgi view on Meta::CPAN
my $generator = APISchema::Generator::Markdown->new;
my $content = $generator->format_schema($schema);
[200, ['Content-Type' => 'text/plain; charset=utf-8;'], [$content]];
};
mount '/' => $app;
}
__END__
=encoding utf-8
=head1 NAME
BMI Calculator
=head1 NAME
Sample Application
=head1 HOW TO USE
lib/APISchema.pm view on Meta::CPAN
package APISchema;
use 5.014;
use strict;
use warnings;
our $VERSION = "1.37";
1;
__END__
=encoding utf-8
=head1 NAME
APISchema - Schema for API
=head1 SYNOPSIS
# bmi.def
resource figure => {
lib/APISchema/Generator/Markdown.pm view on Meta::CPAN
);
bless {
renderer => $renderer,
map {
( $_ => $renderer->build_file($_) );
} qw(index toc route resource request response
request_example response_example),
}, $class;
}
sub resolve_encoding ($) {
my ($resources) = @_;
$resources = { body => $resources } unless ref $resources;
my $encoding = $resources->{encoding} // { '' => 'auto' };
$encoding = { '' => $encoding } unless ref $encoding;
return { %$resources, encoding => $encoding };
}
sub format_schema {
my ($self, $schema) = @_;
my $renderer = $self->{renderer};
my $routes = $schema->get_routes;
my $resources = $schema->get_resources;
my $root = $schema->get_resource_root;
lib/APISchema/Generator/Markdown.pm view on Meta::CPAN
return $self->{index}->(
$renderer,
$schema,
$self->{toc}->(
$renderer,
$routes,
$resources,
),
join('', map {
my $route = $_;
my $req = resolve_encoding($route->request_resource);
my $request_resource = $route->canonical_request_resource($root);
my $codes = $route->responsible_codes;
my $default_code = $route->default_responsible_code;
my $response_resource = $route->canonical_response_resource($root, [
$default_code
]);
my $res = $_->response_resource;
$res = $_->responsible_code_is_specified
? { map { $_ => resolve_encoding($res->{$_}) } @$codes }
: { '' => resolve_encoding($res) };
$self->{route}->(
$renderer,
$route,
{
req => $self->{request_example}->(
$renderer,
$route,
APISchema::Generator::Markdown::ExampleFormatter->new(
resolver => $resolver,
lib/APISchema/Generator/Markdown.pm view on Meta::CPAN
?= $req->{description} // ''
? if (scalar grep { $req->{$_} } qw(header parameter body)) {
|Part|Resource|Content-Type|Encoding|
|----|--------|------------|--------|
? for (qw(header parameter)) {
? next unless $req->{$_};
|<?= $_ ?>|<?= type($req->{$_}) ?>|-|-|
? } # for
? if ($req->{body}) {
? for (sort keys %{$req->{encoding}}) {
|body|<?= type($req->{body}) ?>|<?= content_type($_) ?>|<?= content_type($req->{encoding}->{$_}) ?>|
? } # for
? } # $req->{body}
? } # scalar keys %$req
@@ response
? my ($route, $code, $res) = @_;
#### Response <?= http_status_code($code) ?>
?= $res->{description} // ''
? if (scalar grep { $res->{$_} } qw(header parameter body)) {
|Part|Resource|Content-Type|Encoding|
|----|--------|------------|--------|
? for (qw(header)) {
? next unless $res->{$_};
|<?= $_ ?>|<?= type($res->{$_}) ?>|-|-|
? } # for
? if ($res->{body}) {
? for (sort keys %{$res->{encoding}}) {
|body|<?= type($res->{body}) ?>|<?= content_type($_) ?>|<?= content_type($res->{encoding}->{$_}) ?>|
? } # for
? } # $res->{body}
? } # scalar keys %$res
@@ resource
? my ($r, $resource, $properties) = @_;
### <a name="<?= anchor(resource => $resource) ?>"></a> `<?= $resource->title ?>` : <?= type($resource->definition) ?>
```javascript
<?= pretty_json $r->example($resource->definition) ?>
```
lib/APISchema/Validator.pm view on Meta::CPAN
}
sub for_response {
my $class = shift;
return $class->_new(@_, fetch_resource_method => 'canonical_response_resource');
}
sub _valid_result { APISchema::Validator::Result->new_valid(@_) }
sub _error_result { APISchema::Validator::Result->new_error(@_) }
sub _resolve_encoding {
my ($content_type, $encoding_spec) = @_;
# TODO handle charset?
$content_type = $content_type =~ s/\s*;.*$//r;
$encoding_spec //= DEFAULT_ENCODING_SPEC;
if (ref $encoding_spec) {
$encoding_spec = $encoding_spec->{$content_type};
return ( undef, { message => "Wrong content-type: $content_type" } )
unless $encoding_spec;
}
my $method = $encoding_spec;
return ( undef, {
message => "Unknown decoding method: $method",
content_type => $content_type,
} )
unless APISchema::Validator::Decoder->new->can($method);
return ($method, undef);
}
sub _validate {
lib/APISchema/Validator.pm view on Meta::CPAN
or return $valid;
my $method = $self->fetch_resource_method;
my $resource_root = $schema->get_resource_root;
my $resource_spec = $route->$method(
$resource_root,
$target->{status_code} ? [ $target->{status_code} ] : [],
[ @target_keys ],
);
@target_keys = grep { $resource_spec->{$_} } @target_keys;
my $body_encoding = $resource_spec->{body} && do {
my ($enc, $err) = _resolve_encoding(
$target->{content_type} // '',
$resource_spec->{encoding},
);
if ($err) {
return _error_result(body => $err);
}
$enc;
};
my $encoding = {
body => $body_encoding,
parameter => 'url_parameter',
header => 'perl',
};
my $validator_class = $self->validator_class;
load_class $validator_class;
my $result = APISchema::Validator::Result->new;
$result->merge($_) for map {
my $field = $_;
my $err = _validate($validator_class, map { $_->{$field} } (
$encoding, $target, $resource_spec,
));
$err ? _error_result($field => {
%$err,
encoding => $encoding->{$_},
}) : _valid_result($field);
} @target_keys;
return $result;
}
1;
__END__
lib/Plack/App/APISchema/MockServer.pm view on Meta::CPAN
]);
my $resolver = APISchema::Generator::Markdown::ResourceResolver->new(schema => $root);
my $formatter = APISchema::Generator::Markdown::ExampleFormatter->new(
resolver => $resolver,
spec => $response_resource,
);
# TODO: serve all headers defined in example
# TODO: format body with encoding
return [$default_code, ['Content-Type' => 'application/json; charset=utf-8'], [encode_utf8($formatter->body)]];
}
sub router {
my ($self) = @_;
return $self->{router} if $self->{router};
my $generator = APISchema::Generator::Router::Simple->new;
$self->{router} = $generator->generate_router($self->schema);
t/APISchema-Validator.t view on Meta::CPAN
);
return $schema;
}
sub _forced_route ($$) {
my ($schema, $keys) = @_;
$keys = [qw(header parameter body)] unless defined $keys;
$schema->register_route(
route => '/endpoint',
request_resource => {
encoding => 'json',
map { $_ => 'figure' } @$keys
},
response_resource => {
encoding => 'json',
map { $_ => 'bmi' } @$keys
},
);
return $schema;
}
sub _invalid_encoding_route ($$) {
my ($schema, $keys) = @_;
$keys = [qw(header parameter body)] unless defined $keys;
$schema->register_route(
route => '/endpoint',
request_resource => {
encoding => 'hoge',
map { $_ => 'figure' } @$keys
},
response_resource => {
encoding => 'hoge',
map { $_ => 'bmi' } @$keys
},
);
return $schema;
}
sub _strict_route ($$) {
my ($schema, $keys) = @_;
$keys = [qw(header parameter body)] unless defined $keys;
$schema->register_route(
route => '/endpoint',
request_resource => {
encoding => { 'application/json' => 'json' },
map { $_ => 'figure' } @$keys
},
response_resource => {
encoding => { 'application/json' => 'json' },
map { $_ => 'bmi' } @$keys
},
);
return $schema;
}
sub validate_request : Tests {
subtest 'valid with emtpy schema' => sub {
my $schema = APISchema::Schema->new;
my $validator = APISchema::Validator->for_request;
t/APISchema-Validator.t view on Meta::CPAN
my $schema = _simple_route t::test::fixtures::prepare_bmi, ['body'];
my $validator = APISchema::Validator->for_request;
my $result = $validator->validate('/endpoint' => {
body => encode_json({weight => 50}),
content_type => 'application/json',
}, $schema);
ok !$result->is_valid;
is_deeply [ keys %{$result->errors} ], [ 'body' ];
is_deeply [ map { $_->{attribute} } values %{$result->errors} ],
[ ('Valiemon::Attributes::Required') ];
is_deeply [ map { $_->{encoding} } values %{$result->errors} ],
[ ('json') ];
};
subtest 'invalid without body' => sub {
for my $value ({}, '', undef) {
my $schema = _simple_route t::test::fixtures::prepare_bmi, ['body'];
my $validator = APISchema::Validator->for_request;
my $result = $validator->validate('/endpoint' => {
body => $value,
}, $schema);
t/APISchema-Validator.t view on Meta::CPAN
my $validator = APISchema::Validator->for_request;
my $result = $validator->validate('/endpoint' => {
parameter => $value,
}, $schema);
ok ! $result->is_valid;
is_deeply [ keys %{$result->errors} ], [ 'parameter' ];
}
};
subtest 'invalid with wrong encoding' => sub {
my $schema = _simple_route t::test::fixtures::prepare_bmi, ['body'];
my $validator = APISchema::Validator->for_request;
my $result = $validator->validate('/endpoint' => {
body => encode_json({weight => 50, height => 1.6}),
content_type => 'application/x-www-form-urlencoded',
}, $schema);
ok !$result->is_valid;
is_deeply [ keys %{$result->errors} ], [ 'body' ];
is_deeply [ map { $_->{attribute} } values %{$result->errors} ],
[ ('Valiemon::Attributes::Required') ];
is_deeply [ map { $_->{encoding} } values %{$result->errors} ],
[ ('url_parameter') ];
};
subtest 'invalid with invalid encoding' => sub {
my $schema = _invalid_encoding_route t::test::fixtures::prepare_bmi, ['body'];
my $validator = APISchema::Validator->for_request;
my $result = $validator->validate('/endpoint' => {
body => encode_json({weight => 50, height => 1.6}),
content_type => 'application/json',
}, $schema);
ok !$result->is_valid;
is_deeply [ keys %{$result->errors} ], [ 'body' ];
is_deeply [ map { $_->{message} } values %{$result->errors} ],
[ ('Unknown decoding method: hoge') ];
};
subtest 'valid with forced encoding' => sub {
my $schema = _forced_route t::test::fixtures::prepare_bmi, ['body'];
my $validator = APISchema::Validator->for_request;
my $result = $validator->validate('/endpoint' => {
body => encode_json({weight => 50, height => 1.6}),
content_type => 'application/x-www-form-urlencoded',
}, $schema);
ok $result->is_valid;
};
subtest 'valid with strict content-type check' => sub {
t/APISchema-Validator.t view on Meta::CPAN
subtest 'invalid parameter' => sub {
my $schema = _simple_route t::test::fixtures::prepare_bmi, ['parameter'];
my $validator = APISchema::Validator->for_request;
my $result = $validator->validate('/endpoint' => {
parameter => 'weight=50',
}, $schema);
ok !$result->is_valid;
is_deeply [ map { $_->{attribute} } values %{$result->errors} ],
[ ('Valiemon::Attributes::Required') ];
is_deeply [ map { $_->{encoding} } values %{$result->errors} ],
[ ('url_parameter') ];
};
subtest 'valid header' => sub {
my $schema = _simple_route t::test::fixtures::prepare_bmi, ['header'];
my $validator = APISchema::Validator->for_request;
my $result = $validator->validate('/endpoint' => {
header => { weight => 50, height => 1.6 },
}, $schema);
ok $result->is_valid;
t/APISchema-Validator.t view on Meta::CPAN
subtest 'invalid header' => sub {
my $schema = _simple_route t::test::fixtures::prepare_bmi, ['header'];
my $validator = APISchema::Validator->for_request;
my $result = $validator->validate('/endpoint' => {
header => { weight => 50 },
}, $schema);
ok !$result->is_valid;
is_deeply [ keys %{$result->errors} ], [ 'header' ];
is_deeply [ map { $_->{attribute} } values %{$result->errors} ],
[ ('Valiemon::Attributes::Required') ];
is_deeply [ map { $_->{encoding} } values %{$result->errors} ],
[ ('perl') ];
};
subtest 'all valid' => sub {
my $schema = _simple_route t::test::fixtures::prepare_bmi, ['body', 'parameter', 'header'];
my $validator = APISchema::Validator->for_request;
my $result = $validator->validate('/endpoint' => {
header => { weight => 50, height => 1.6 },
parameter => 'weight=50&height=1.6',
body => encode_json({weight => 50, height => 1.6}),
t/APISchema-Validator.t view on Meta::CPAN
parameter => 'weight=50',
body => encode_json({weight => 50}),
content_type => 'application/json',
}, $schema);
ok !$result->is_valid;
is scalar keys %{$result->errors}, 3;
is_deeply [ sort keys %{$result->errors} ],
[ qw(body header parameter) ];
is_deeply [ map { $_->{attribute} } values %{$result->errors} ],
[ ('Valiemon::Attributes::Required') x 3 ];
is_deeply [ sort map { $_->{encoding} } values %{$result->errors} ],
[ ('json', 'perl', 'url_parameter') ];
};
}
sub validate_response : Tests {
subtest 'valid with emtpy schema' => sub {
my $schema = APISchema::Schema->new;
my $validator = APISchema::Validator->for_response;
my $result = $validator->validate('/endpoint' => {
header => { foo => 'bar' },
t/APISchema-Validator.t view on Meta::CPAN
my $schema = _simple_route t::test::fixtures::prepare_bmi, ['body'];
my $validator = APISchema::Validator->for_response;
my $result = $validator->validate('/endpoint' => {
body => encode_json({hoge => 'foo'}),
content_type => 'application/json',
}, $schema);
ok !$result->is_valid;
is_deeply [ keys %{$result->errors} ], [ 'body' ];
is_deeply [ map { $_->{attribute} } values %{$result->errors} ],
[ ('Valiemon::Attributes::Required') ];
is_deeply [ map { $_->{encoding} } values %{$result->errors} ],
[ ('json') ];
};
subtest 'invalid with wrong encoding' => sub {
my $schema = _simple_route t::test::fixtures::prepare_bmi, ['body'];
my $validator = APISchema::Validator->for_response;
my $result = $validator->validate('/endpoint' => {
body => encode_json({value => 19.5}),
content_type => 'application/x-www-form-urlencoded',
}, $schema);
ok !$result->is_valid;
is_deeply [ keys %{$result->errors} ], [ 'body' ];
is_deeply [ map { $_->{attribute} } values %{$result->errors} ],
[ ('Valiemon::Attributes::Required') ];
is_deeply [ map { $_->{encoding} } values %{$result->errors} ],
[ ('url_parameter') ];
};
subtest 'invalid with invalid encoding' => sub {
my $schema = _invalid_encoding_route t::test::fixtures::prepare_bmi, ['body'];
my $validator = APISchema::Validator->for_response;
my $result = $validator->validate('/endpoint' => {
body => encode_json({value => 19.5}),
content_type => 'application/json',
}, $schema);
ok !$result->is_valid;
is_deeply [ keys %{$result->errors} ], [ 'body' ];
is_deeply [ map { $_->{message} } values %{$result->errors} ],
[ ('Unknown decoding method: hoge') ];
};
subtest 'valid with forced encoding' => sub {
my $schema = _forced_route t::test::fixtures::prepare_bmi, ['body'];
my $validator = APISchema::Validator->for_response;
my $result = $validator->validate('/endpoint' => {
body => encode_json({value => 19.5}),
content_type => 'application/x-www-form-urlencoded',
}, $schema);
ok $result->is_valid;
};
subtest 'valid with strict content-type check' => sub {
t/APISchema-Validator.t view on Meta::CPAN
subtest 'invalid header' => sub {
my $schema = _simple_route t::test::fixtures::prepare_bmi, ['header'];
my $validator = APISchema::Validator->for_response;
my $result = $validator->validate('/endpoint' => {
header => {},
}, $schema);
ok !$result->is_valid;
is_deeply [ keys %{$result->errors} ], [ 'header' ];
is_deeply [ map { $_->{attribute} } values %{$result->errors} ],
[ ('Valiemon::Attributes::Required') ];
is_deeply [ map { $_->{encoding} } values %{$result->errors} ],
[ ('perl') ];
};
subtest 'all valid' => sub {
my $schema = _simple_route t::test::fixtures::prepare_bmi, ['body', 'header'];
my $validator = APISchema::Validator->for_response;
my $result = $validator->validate('/endpoint' => {
header => { value => 19.5 },
body => encode_json({value => 19.5}),
content_type => 'application/json',
t/APISchema-Validator.t view on Meta::CPAN
header => {},
body => encode_json({}),
content_type => 'application/json',
}, $schema);
ok !$result->is_valid;
is scalar keys %{$result->errors}, 2;
is_deeply [ sort keys %{$result->errors} ],
[ qw(body header) ];
is_deeply [ map { $_->{attribute} } values %{$result->errors} ],
[ ('Valiemon::Attributes::Required') x 2 ];
is_deeply [ sort map { $_->{encoding} } values %{$result->errors} ],
[ ('json', 'perl') ];
};
subtest 'valid referenced resource' => sub {
my $schema = _forced_route t::test::fixtures::prepare_family, ['body'];
my $validator = APISchema::Validator->for_response;
my $result = $validator->validate('Children GET API' => {
body => encode_json([ {
name => 'Alice',
age => 16,
t/Plack-App-APISchema-MockServer.t view on Meta::CPAN
test_psgi $app => sub {
my $server = shift;
my $res = $server->(POST '/notfound');
is $res->code, 404;
is $res->header('content-type'), 'text/plain; charset=utf-8';
is $res->content, q!not found!;
}
};
}
sub when_encoding_is_specified : Tests {
my $schema = t::test::fixtures::prepare_bmi;
$schema->register_route(
method => 'POST',
route => '/bmi_force_json',
request_resource => {
encoding => 'json',
body => 'figure',
},
response_resource => {
encoding => 'json',
body => 'bmi',
},
);
my $app = Plack::App::APISchema::MockServer->new(schema => $schema)->to_app;
test_psgi $app => sub {
my $server = shift;
my $res = $server->(POST '/bmi_force_json');
is $res->code, 200;
is $res->header('content-type'), 'application/json; charset=utf-8';
is $res->content, q!{"value":19.5}!;
}
}
sub with_wide_character : Tests {
my $schema = t::test::fixtures::prepare_author;
$schema->register_route(
method => 'GET',
route => '/author',
response_resource => {
encoding => 'json',
body => 'author',
},
);
my $app = Plack::App::APISchema::MockServer->new(schema => $schema)->to_app;
test_psgi $app => sub {
my $server = shift;
my $res = $server->(GET '/author');
is $res->code, 200;
t/Plack-App-APISchema-MockServer.t view on Meta::CPAN
},
{
type => 'null',
},
],
});
$schema->register_route(
method => 'GET',
route => '/maybe_bmi',
response_resource => {
encoding => 'json',
body => 'maybe_bmi',
},
);
my $app = Plack::App::APISchema::MockServer->new(schema => $schema)->to_app;
test_psgi $app => sub {
my $server = shift;
my $res = $server->(GET '/maybe_bmi');
is $res->code, 200;
t/Plack-App-APISchema-MockServer.t view on Meta::CPAN
is $res->content, q!{"value":19.5}!;
};
}
sub status_201 : Tests {
my $schema = t::test::fixtures::prepare_bmi;
$schema->register_route(
method => 'PUT',
route => '/put_bmi',
request_resource => {
encoding => 'json',
body => 'figure',
},
response_resource => {
201 => {
encoding => 'json',
body => 'bmi',
},
},
);
my $app = Plack::App::APISchema::MockServer->new(schema => $schema)->to_app;
test_psgi $app => sub {
my $server = shift;
my $res = $server->(PUT '/put_bmi');
is $res->content, q!{"value":19.5}!;
t/Plack-Middleware-APISchema-RequestValidator.t view on Meta::CPAN
isa_ok $middleware->router, 'Router::Simple';
}
sub request_validator : Tests {
my $schema = t::test::fixtures::prepare_bmi;
$schema->register_route(
method => 'POST',
route => '/bmi_strict',
request_resource => {
encoding => { 'application/json' => 'json' },
body => 'figure',
},
);
$schema->register_route(
method => 'POST',
route => '/bmi_force_json',
request_resource => {
encoding => 'json',
body => 'figure',
},
);
$schema->register_route(
method => 'POST',
route => '/bmi_by_parameter',
request_resource => {
parameter => 'figure',
},
);
t/Plack-Middleware-APISchema-RequestValidator.t view on Meta::CPAN
POST '/bmi',
Content_Type => 'application/json',
Content => encode_json({}),
);
is $res->code, HTTP_UNPROCESSABLE_ENTITY;
cmp_deeply $res->content, json({
body => {
attribute => 'Valiemon::Attributes::Required',
position => '/$ref/required',
message => "Contents do not match resource 'figure'",
encoding => 'json',
actual => {},
expected => $schema->get_resource_by_name('figure')->definition,
},
});
done_testing;
}
};
subtest 'other endpoints are not affected' => sub {
test_psgi $middleware => sub {
t/Plack-Middleware-APISchema-RequestValidator.t view on Meta::CPAN
my $server = shift;
my $res = $server->(
POST '/bmi',
Content_Type => 'application/json',
Content => 'aaa',
);
is $res->code, HTTP_UNPROCESSABLE_ENTITY;
cmp_deeply $res->content, json({
body => {
message => "Failed to parse json",
encoding => 'json',
},
});
done_testing;
}
};
subtest 'when content-type is incorrect' => sub {
test_psgi $middleware => sub {
my $server = shift;
my $content_type = 'application/x-www-form-urlencoded';
t/Plack-Middleware-APISchema-RequestValidator.t view on Meta::CPAN
POST '/bmi',
Content_Type => $content_type,
Content => encode_json({weight => 50, height => 1.6}),
);
is $res->code, HTTP_UNPROCESSABLE_ENTITY;
cmp_deeply $res->content, json({
body => {
attribute => 'Valiemon::Attributes::Required',
position => '/$ref/required',
message => "Contents do not match resource 'figure'",
encoding => 'url_parameter',
actual => isa('HASH'),
# XXX: Hash order randomization
# actual => { "{\"weight\":50,\"height\":1.6}" => undef }
expected => $schema->get_resource_by_name('figure')->definition,
},
});
done_testing;
}
};
subtest 'when content-type is incorrect with forced encoding' => sub {
test_psgi $middleware => sub {
my $server = shift;
my $content_type = 'application/x-www-form-urlencoded';
my $res = $server->(
POST '/bmi_force_json',
Content_Type => $content_type,
Content => encode_json({weight => 50, height => 1.6}),
);
is $res->code, 200;
done_testing;
t/Plack-Middleware-APISchema-RequestValidator.t view on Meta::CPAN
my $server = shift;
my $res = $server->(
POST '/bmi_by_parameter?weight=50',
);
is $res->code, HTTP_UNPROCESSABLE_ENTITY;
cmp_deeply $res->content, json({
parameter => {
attribute => 'Valiemon::Attributes::Required',
position => '/$ref/required',
message => "Contents do not match resource 'figure'",
encoding => 'url_parameter',
actual => {
weight => 50,
},
expected => $schema->get_resource_by_name('figure')->definition,
},
});
done_testing;
}
};
t/Plack-Middleware-APISchema-RequestValidator.t view on Meta::CPAN
my $res = $server->(
POST '/bmi_by_header',
X_Weight => 50,
);
is $res->code, HTTP_UNPROCESSABLE_ENTITY;
cmp_deeply $res->content, json({
header => {
attribute => 'Valiemon::Attributes::Required',
position => '/$ref/required',
message => "Contents do not match resource 'figure_header'",
encoding => 'perl',
actual => {
content_length => 0,
x_weight => 50,
content_type => "application/x-www-form-urlencoded",
host => "localhost",
},
expected => $schema->get_resource_by_name('figure_header')->definition,
},
});
done_testing;
t/Plack-Middleware-APISchema-RequestValidator.t view on Meta::CPAN
last_name => [],
}),
);
is $res->code, HTTP_UNPROCESSABLE_ENTITY;
cmp_deeply $res->content, json({
body => {
actual => [],
attribute => 'Valiemon::Attributes::Type',
position => '/$ref/properties/last_name/type',
expected => $schema->get_resource_by_name('user')->definition->{properties}->{last_name},
encoding => 'json',
message => "Contents do not match resource 'user'",
},
});
done_testing;
};
};
}
t/Plack-Middleware-APISchema-ResponseValidator.t view on Meta::CPAN
isa_ok $middleware->router, 'Router::Simple';
}
sub response_validator : Tests {
my $schema = t::test::fixtures::prepare_bmi;
$schema->register_route(
method => 'POST',
route => '/bmi_strict',
response_resource => {
encoding => { 'application/json' => 'json' },
body => 'bmi',
},
);
$schema->register_route(
method => 'POST',
route => '/bmi_force_json',
response_resource => {
encoding => 'json',
body => 'bmi',
},
);
$schema->register_resource(bmi_header => {
type => 'object',
properties => {
'x_value' => { type => 'number' },
},
required => ['x_value'],
});
t/Plack-Middleware-APISchema-ResponseValidator.t view on Meta::CPAN
my $res = $server->(POST '/bmi');
is $res->code, 500;
is $res->header('X-Error-Cause'), 'Plack::Middleware::APISchema::ResponseValidator+Valiemon';
cmp_deeply $res->content, json({
body => {
attribute => "Valiemon::Attributes::Type",
position => '/$ref/properties/value/type',
expected => $schema->get_resource_by_name('bmi')->definition->{properties}->{value},
actual => 'aaa',
message => "Contents do not match resource 'bmi'",
encoding => 'json',
},
});
done_testing;
}
};
subtest 'when wrong content-type' => sub {
test_psgi $middleware => sub {
$content_type = 'text/plain';
$json = encode_json({value => 19.5});
t/Plack-Middleware-APISchema-ResponseValidator.t view on Meta::CPAN
$content_type = 'application/json';
$json = 'aaa';
$header = [];
my $server = shift;
my $res = $server->(POST '/bmi');
is $res->code, 500;
is $res->header('X-Error-Cause'), 'Plack::Middleware::APISchema::ResponseValidator+Valiemon';
cmp_deeply $res->content, json({
body => {
message => "Failed to parse json",
encoding => 'json',
},
});
done_testing;
}
};
subtest 'when content-type is incorrect' => sub {
test_psgi $middleware => sub {
$content_type = 'application/x-www-form-urlencoded';
$json = encode_json({value => 19.5});
$header = [];
my $server = shift;
my $res = $server->(POST '/bmi');
is $res->code, 500;
is $res->header('X-Error-Cause'), 'Plack::Middleware::APISchema::ResponseValidator+Valiemon';
cmp_deeply $res->content, json({
body => {
attribute => 'Valiemon::Attributes::Required',
position => '/$ref/required',
message => "Contents do not match resource 'bmi'",
encoding => 'url_parameter',
actual => {
'{"value":19.5}' => undef,
},
expected => $schema->get_resource_by_name('bmi')->definition,
},
});
done_testing;
}
};
subtest 'when content-type is incorrect with forced encoding' => sub {
test_psgi $middleware => sub {
$content_type = 'application/x-www-form-urlencoded';
$json = encode_json({value => 19.5});
$header = [];
my $server = shift;
my $res = $server->(POST '/bmi_force_json');
is $res->code, 200;
done_testing;
}
};
t/Plack-Middleware-APISchema-ResponseValidator.t view on Meta::CPAN
$header = [ 'X-Foo' => 1 ];
my $server = shift;
my $res = $server->(POST '/bmi_by_header');
is $res->code, 500;
is $res->header('X-Error-Cause'), 'Plack::Middleware::APISchema::ResponseValidator+Valiemon';
cmp_deeply $res->content, json({
header => {
attribute => "Valiemon::Attributes::Required",
position => '/$ref/required',
message => "Contents do not match resource 'bmi_header'",
encoding => 'perl',
expected => $schema->get_resource_by_name('bmi_header')->definition,
actual => {
content_type => "application/json",
"x_foo" => 1,
},
},
});
done_testing;
}
};
t/Plack-Middleware-APISchema-ResponseValidator.t view on Meta::CPAN
});
subtest 'Status 400 with invalid body' => sub {
test_psgi $middleware_ng => sub {
my $server = shift;
my $res = $server->(GET '/get');
is $res->code, 500;
is $res->header('X-Error-Cause'), 'Plack::Middleware::APISchema::ResponseValidator+Valiemon';
cmp_deeply $res->content, json({ body => {
message => 'Failed to parse json',
encoding => 'json',
} });
done_testing;
};
};
}
sub response_validator_with_utf8 : Tests {
my $schema = t::test::fixtures::prepare_user;
$schema->register_route(
method => 'GET',
t/Plack-Middleware-APISchema-ResponseValidator.t view on Meta::CPAN
my $server = shift;
my $res = $server->(GET '/user');
is $res->code, 500;
cmp_deeply $res->content, json({
body => {
attribute => "Valiemon::Attributes::Type",
position => '/$ref/properties/last_name/type',
expected => $schema->get_resource_by_name('user')->definition->{properties}->{last_name},
actual => [],
message => "Contents do not match resource 'user'",
encoding => 'json',
},
});
done_testing;
};
};
}
t/fixtures/status.def view on Meta::CPAN
};
GET '/get' => {
title => 'Get API',
description => 'Get something',
destination => {},
request => 'none',
response => {
200 => {
body => 'ok',
encoding => 'perl',
},
400 => {
body => 'error',
encoding => 'json',
},
},
};