view release on metacpan or search on metacpan
- 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
README.md
cpanfile
eg/bmi.psgi
lib/APISchema.pm
lib/APISchema/DSL.pm
lib/APISchema/Generator/Markdown.pm
lib/APISchema/Generator/Markdown/ExampleFormatter.pm
lib/APISchema/Generator/Markdown/Formatter.pm
lib/APISchema/Generator/Markdown/ResourceResolver.pm
lib/APISchema/Generator/Router/Simple.pm
lib/APISchema/JSON.pm
lib/APISchema/Resource.pm
lib/APISchema/Route.pm
lib/APISchema/Schema.pm
lib/APISchema/Validator.pm
lib/APISchema/Validator/Decoder.pm
lib/APISchema/Validator/Result.pm
lib/Plack/App/APISchema/Document.pm
lib/Plack/App/APISchema/MockServer.pm
lib/Plack/Middleware/APISchema/RequestValidator.pm
lib/Plack/Middleware/APISchema/ResponseValidator.pm
minil.toml
script/generate_markdown_document.pl
t/APISchema-DSL.t
t/APISchema-Generator-Markdown-Formatter.t
t/APISchema-Generator-Markdown.t
t/APISchema-Generator-Router-Simple.t
t/APISchema-JSON.t
t/APISchema-Resource.t
t/APISchema-Route.t
t/APISchema-Schema.t
t/APISchema-Validator.t
t/APISchema.t
t/Plack-App-APISchema-Document.t
t/Plack-App-APISchema-MockServer.t
t/Plack-Middleware-APISchema-RequestValidator.t
t/Plack-Middleware-APISchema-ResponseValidator.t
t/fixtures/author.def
}
},
"runtime" : {
"requires" : {
"Class::Accessor::Lite" : "0",
"Class::Accessor::Lite::Lazy" : "0",
"Class::Load" : "0",
"HTML::Escape" : "0",
"HTTP::Message" : "0",
"Hash::Merge::Simple" : "0",
"JSON::Pointer" : "0",
"JSON::XS" : "0",
"List::MoreUtils" : "0",
"Path::Class" : "0",
"Plack" : "0",
"Router::Simple" : "0",
"Text::Markdown::Hoedown" : "0",
"Text::MicroTemplate" : "0",
"Text::MicroTemplate::DataSection" : "0",
"Text::MicroTemplate::Extended" : "0",
"URI::Escape" : "0",
"URL::Encode" : "0",
"Valiemon" : "0.04",
"perl" : "5.014"
}
},
"test" : {
"requires" : {
"HTTP::Request::Common" : "0",
"Path::Class" : "0",
"Test::Class" : "0",
"Test::Deep" : "0",
"Test::Deep::JSON" : "0",
"Test::Fatal" : "0",
"Test::More" : "0.98"
}
}
},
"provides" : {
"APISchema" : {
"file" : "lib/APISchema.pm",
"version" : "1.37"
},
},
"APISchema::Generator::Markdown::Formatter" : {
"file" : "lib/APISchema/Generator/Markdown/Formatter.pm"
},
"APISchema::Generator::Markdown::ResourceResolver" : {
"file" : "lib/APISchema/Generator/Markdown/ResourceResolver.pm"
},
"APISchema::Generator::Router::Simple" : {
"file" : "lib/APISchema/Generator/Router/Simple.pm"
},
"APISchema::JSON" : {
"file" : "lib/APISchema/JSON.pm"
},
"APISchema::Resource" : {
"file" : "lib/APISchema/Resource.pm"
},
"APISchema::Route" : {
"file" : "lib/APISchema/Route.pm"
},
"APISchema::Schema" : {
"file" : "lib/APISchema/Schema.pm"
},
}
},
"version" : "1.37",
"x_authority" : "cpan:HITODE",
"x_contributors" : [
"Mohammad S Anwar <mohammad.anwar@yahoo.com>",
"Takumi Akiyama <t.akiym@gmail.com>",
"Tomohiro Nishimura <tomohiro68@gmail.com>",
"aereal <aereal@aereal.org>"
],
"x_serialization_backend" : "JSON::PP version 2.27400"
}
---
abstract: 'Schema for API'
author:
- 'hitode909 <hitode909@gmail.com>'
build_requires:
HTTP::Request::Common: '0'
Path::Class: '0'
Test::Class: '0'
Test::Deep: '0'
Test::Deep::JSON: '0'
Test::Fatal: '0'
Test::More: '0.98'
configure_requires:
Module::Build::Tiny: '0.035'
dynamic_config: 0
generated_by: 'Minilla/v3.0.14, CPAN::Meta::Converter version 2.150010'
license: perl
meta-spec:
url: http://module-build.sourceforge.net/META-spec-v1.4.html
version: '1.4'
APISchema::Generator::Markdown:
file: lib/APISchema/Generator/Markdown.pm
APISchema::Generator::Markdown::ExampleFormatter:
file: lib/APISchema/Generator/Markdown/ExampleFormatter.pm
APISchema::Generator::Markdown::Formatter:
file: lib/APISchema/Generator/Markdown/Formatter.pm
APISchema::Generator::Markdown::ResourceResolver:
file: lib/APISchema/Generator/Markdown/ResourceResolver.pm
APISchema::Generator::Router::Simple:
file: lib/APISchema/Generator/Router/Simple.pm
APISchema::JSON:
file: lib/APISchema/JSON.pm
APISchema::Resource:
file: lib/APISchema/Resource.pm
APISchema::Route:
file: lib/APISchema/Route.pm
APISchema::Schema:
file: lib/APISchema/Schema.pm
APISchema::Validator:
file: lib/APISchema/Validator.pm
APISchema::Validator::Decoder:
file: lib/APISchema/Validator/Decoder.pm
file: lib/Plack/Middleware/APISchema/RequestValidator.pm
Plack::Middleware::APISchema::ResponseValidator:
file: lib/Plack/Middleware/APISchema/ResponseValidator.pm
requires:
Class::Accessor::Lite: '0'
Class::Accessor::Lite::Lazy: '0'
Class::Load: '0'
HTML::Escape: '0'
HTTP::Message: '0'
Hash::Merge::Simple: '0'
JSON::Pointer: '0'
JSON::XS: '0'
List::MoreUtils: '0'
Path::Class: '0'
Plack: '0'
Router::Simple: '0'
Text::Markdown::Hoedown: '0'
Text::MicroTemplate: '0'
Text::MicroTemplate::DataSection: '0'
Text::MicroTemplate::Extended: '0'
URI::Escape: '0'
URL::Encode: '0'
requires 'perl', '5.008001';
requires 'Router::Simple';
requires 'Plack';
requires 'JSON::XS';
requires 'JSON::Pointer';
requires 'Path::Class';
requires 'Class::Load';
requires 'Class::Accessor::Lite';
requires 'Class::Accessor::Lite::Lazy';
requires 'List::MoreUtils';
requires 'Hash::Merge::Simple';
requires 'URL::Encode';
requires 'HTML::Escape';
requires 'Text::MicroTemplate';
requires 'Text::MicroTemplate::Extended';
requires 'HTTP::Message';
requires 'Valiemon', '0.04';
requires 'URI::Escape';
on 'test' => sub {
requires 'Path::Class';
requires 'Test::More', '0.98';
requires 'Test::Class';
requires 'Test::Deep';
requires 'Test::Fatal';
requires 'Test::Deep::JSON';
requires 'HTTP::Request::Common';
};
eg/bmi.psgi view on Meta::CPAN
use lib glob '../modules/*/lib/';
use Plack::Builder;
use Plack::Request;
use APISchema::DSL;
use APISchema::Generator::Router::Simple;
use Plack::App::APISchema::Document;
use Plack::App::APISchema::MockServer;
use APISchema::Generator::Markdown;
use JSON qw(decode_json encode_json);
my $schema = APISchema::DSL::process {
include '../t/fixtures/bmi.def';
};
my $router = do {
my $generator = APISchema::Generator::Router::Simple->new;
$generator->generate_router($schema);
};
lib/APISchema/Generator/Markdown/Formatter.pm view on Meta::CPAN
use strict;
use warnings;
# core
use Exporter qw(import);
our @EXPORT = qw(type json pretty_json code restriction desc anchor method methods content_type http_status http_status_code);
# cpan
use HTTP::Status qw(status_message);
use URI::Escape qw(uri_escape_utf8);
use JSON::XS ();
my $JSON = JSON::XS->new->canonical(1);
use constant +{
RESTRICTIONS => [qw(required max_items min_items max_length min_length maximum minimum pattern)],
SHORT_DESCRIPTION_LENGTH => 100,
};
sub type ($); # type has recursive call
sub type ($) {
my $def = shift;
lib/APISchema/Generator/Markdown/Formatter.pm view on Meta::CPAN
sub json ($) {
my $x = shift;
if (ref $x eq 'SCALAR') {
if ($$x eq 1) {
$x = 'true';
} elsif ($$x eq 0) {
$x = 'false';
}
} elsif (ref $x) {
$x = $JSON->encode($x);
} else {
$x = $JSON->encode([$x]);
$x =~ s/^\[(.*)\]$/$1/;
}
return $x;
}
my $PRETTY_JSON = JSON::XS->new->canonical(1)->indent(1)->pretty(1);
sub pretty_json ($) {
my $x = shift;
if (ref $x) {
$x = $PRETTY_JSON->encode($x);
} else {
$x = $PRETTY_JSON->encode([$x]);
$x =~ s/^\[\s*(.*)\s*\]\n$/$1/;
}
return $x;
}
sub _code ($) {
my $text = shift;
return '' unless defined $text;
if ($text =~ /[`|]/) {
$text =~ s/[|]/|/g;
lib/APISchema/Generator/Markdown/ResourceResolver.pm view on Meta::CPAN
package APISchema::Generator::Markdown::ResourceResolver;
use 5.014;
use strict;
use warnings;
# cpan
use JSON::Pointer;
use Class::Accessor::Lite (
new => 1,
ro => [qw(schema)],
);
sub _foreach_properties($$&) {
my ($name_path, $definition, $callback) = @_;
return unless (ref $definition || '') eq 'HASH';
if ($definition->{items}) {
lib/APISchema/Generator/Markdown/ResourceResolver.pm view on Meta::CPAN
return '.' . join '.', @name_path;
}
sub _collect_properties {
my ($self, $path, $definition) = @_;
return {} unless (ref $definition || '') eq 'HASH';
my $ref = $definition->{'$ref'};
if ($ref) {
$ref = $ref =~ s/^#//r;
my $def = JSON::Pointer->get($self->schema, $ref);
return $self->_collect_properties($path, $def)
if $def && $ref !~ qr!^/resource/[^/]+$!;
$definition = +{
%$definition,
description => $definition->{description} // $def->{description},
};
}
my $result = { _property_name(@$path) => $definition };
lib/APISchema/Generator/Markdown/ResourceResolver.pm view on Meta::CPAN
my ($self, $path, $definition) = @_;
return ($definition->{example}, 1) if exists $definition->{example};
if (my $union = $definition->{oneOf} || $definition->{anyOf} || $definition->{allOf}) {
return ($self->_collect_example($path, $union->[0]), 1);
}
my $ref = $definition->{'$ref'};
if ($ref) {
$ref = $ref =~ s/^#//r;
my $def = JSON::Pointer->get($self->schema, $ref);
return ($self->_collect_example($path, $def), 1) if $def;
}
my %result;
my $type = $definition->{type} || '';
_foreach_properties($path, $definition, sub {
my ($example, $exists) = $self->_collect_example(@_);
unless ($exists) {
if (exists $_[1]->{default}) {
$example = $_[1]->{default};
lib/APISchema/JSON.pm view on Meta::CPAN
package APISchema::JSON;
use strict;
use warnings;
use Exporter 'import';
our @EXPORT = qw(encode_json_canonical);
use JSON::XS;
my $json = JSON::XS->new->utf8->canonical(1);
sub encode_json_canonical {
my ($value) = @_;
$json->encode($value);
}
1;
lib/APISchema/Validator/Decoder.pm view on Meta::CPAN
package APISchema::Validator::Decoder;
use strict;
use warnings;
# cpan
use JSON::XS qw(decode_json);
use URL::Encode qw(url_params_mixed);
use Class::Accessor::Lite ( new => 1 );
sub perl {
my ($self, $body) = @_;
return $body;
}
my $JSON = JSON::XS->new->utf8;
sub json {
my ($self, $body) = @_;
return $JSON->decode($body);
}
sub url_parameter {
my ($self, $body) = @_;
return undef unless defined $body;
return url_params_mixed($body, 1);
}
1;
lib/Plack/App/APISchema/MockServer.pm view on Meta::CPAN
package Plack::App::APISchema::MockServer;
use strict;
use warnings;
use parent qw(Plack::Component);
use Plack::Util::Accessor qw(schema);
use Plack::Request;
use Encode qw(encode_utf8);
use APISchema::JSON;
use APISchema::Generator::Router::Simple;
use APISchema::Generator::Markdown::ResourceResolver;
use APISchema::Generator::Markdown::ExampleFormatter;
sub call {
my ($self, $env) = @_;
my $req = Plack::Request->new($env);
lib/Plack/Middleware/APISchema/RequestValidator.pm view on Meta::CPAN
package Plack::Middleware::APISchema::RequestValidator;
use strict;
use warnings;
use parent qw(Plack::Middleware);
use HTTP::Status qw(:constants);
use Plack::Util::Accessor qw(schema validator);
use Plack::Request;
use APISchema::Generator::Router::Simple;
use APISchema::Validator;
use APISchema::JSON;
use constant DEFAULT_VALIDATOR_CLASS => 'Valiemon';
sub call {
my ($self, $env) = @_;
my $req = Plack::Request->new($env);
my ($matched, $route) = $self->router->routematch($env);
$matched or return $self->app->($env);
lib/Plack/Middleware/APISchema/ResponseValidator.pm view on Meta::CPAN
package Plack::Middleware::APISchema::ResponseValidator;
use strict;
use warnings;
use parent qw(Plack::Middleware);
use Plack::Util ();
use Plack::Util::Accessor qw(schema validator);
use Plack::Response;
use APISchema::Generator::Router::Simple;
use APISchema::Validator;
use APISchema::JSON;
use constant DEFAULT_VALIDATOR_CLASS => 'Valiemon';
sub call {
my ($self, $env) = @_;
Plack::Util::response_cb($self->app->($env), sub {
my $res = shift;
my ($matched, $route) = $self->router->routematch($env);
t/APISchema-JSON.t view on Meta::CPAN
package t::APISchema::JSON;
use lib '.';
use t::test;
sub _require : Test(startup => 1) {
my ($self) = @_;
BEGIN{ use_ok 'APISchema::JSON'; }
}
sub _encode_json_canonical : Tests {
is APISchema::JSON::encode_json_canonical({b => 2, c => 3, a => 1}), '{"a":1,"b":2,"c":3}', 'keys are sorted';
is APISchema::JSON::encode_json_canonical({nested => {b => 2, c => 3, a => 1}}), '{"nested":{"a":1,"b":2,"c":3}}', 'nested keys are sorted';
}
t/APISchema-Validator.t view on Meta::CPAN
package t::Plack::Middleware::APISchema::ResponseValidator;
use lib '.';
use t::test;
use t::test::fixtures;
use JSON::XS qw(encode_json);
sub _require : Test(startup => 1) {
use_ok 'APISchema::Validator';
}
sub instantiate : Tests {
subtest 'For request' => sub {
my $validator = APISchema::Validator->for_request;
isa_ok $validator, 'APISchema::Validator';
is $validator->validator_class, 'Valiemon';
t/Plack-Middleware-APISchema-RequestValidator.t view on Meta::CPAN
package t::Plack::Middleware::APISchema::RequestValidator;
use lib '.';
use t::test;
use t::test::fixtures;
use Plack::Test;
use HTTP::Request::Common;
use HTTP::Status qw(:constants);
use JSON::XS qw(encode_json);
sub _require : Test(startup => 1) {
my ($self) = @_;
use_ok 'Plack::Middleware::APISchema::RequestValidator';
}
sub instantiate : Tests {
my $schema = APISchema::Schema->new;
my $middleware = Plack::Middleware::APISchema::RequestValidator->new(schema => $schema);
t/Plack-Middleware-APISchema-RequestValidator.t view on Meta::CPAN
};
subtest 'other endpoints are not affected' => sub {
test_psgi $middleware => sub {
my $server = shift;
my $res = $server->(GET '/other/');
is $res->code, 200;
}
};
subtest 'when request is not a JSON' => sub {
test_psgi $middleware => sub {
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 => {
t/Plack-Middleware-APISchema-ResponseValidator.t view on Meta::CPAN
package t::Plack::Middleware::APISchema::ResponseValidator;
use lib '.';
use t::test;
use t::test::fixtures;
use Plack::Test;
use Plack::Request;
use HTTP::Request::Common;
use JSON::XS qw(encode_json);
sub _require : Test(startup => 1) {
my ($self) = @_;
use_ok 'Plack::Middleware::APISchema::ResponseValidator';
}
sub instantiate : Tests {
my $schema = APISchema::Schema->new;
my $middleware = Plack::Middleware::APISchema::ResponseValidator->new(schema => $schema);
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 => { message => "Wrong content-type: text/plain" },
});
done_testing;
}
};
subtest 'when response is not a JSON' => sub {
test_psgi $middleware => sub {
$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 => {
my $code = qq[
package $package;
use strict;
use warnings;
use parent qw(Test::Class);
use Test::More;
use Test::Fatal qw(lives_ok dies_ok exception);
use Test::Deep;
use Test::Deep::JSON;
END { $package->runtests }
];
eval $code;
die $@ if $@;
}
1;