view release on metacpan or search on metacpan
- Determine the status code from the detail of failure (aereal++)
1.02 2015-10-06T07:12:24Z
- Prettify the output of examples (aereal++)
1.01 2015-09-04T09:32:37Z
- Recognize HTTP PATCH method (aereal++)
1.00 2015-08-27T10:48:03Z
- Support schema which contains unicode characters
- Enable `use utf8` in DSL file
0.03 2015-08-18T02:06:45Z
- Validation error has more details. expected: expected schema, actual: actual input data
0.02 2015-03-16T02:35:36Z
- Add a status_code parameter to P::MW::AS::RequestValidator
0.01 2015-02-18T10:41:05Z
- original version
lib/APISchema/DSL.pm view on Meta::CPAN
sub process (&) {
my $dsl = shift;
my $schema = APISchema::Schema->new;
local $_directive->{include} = sub {
my ($file) = @_;
-r $_[0] or Carp::croak(sprintf 'No such file: %s', $file);
my $content = file($file)->slurp;
my $with_utf8 = "use utf8;\n" . $content;
eval $with_utf8;
Carp::croak($@) if $@;
};
local $_directive->{title} = sub {
$schema->title(@_);
};
local $_directive->{description} = sub {
$schema->description(@_);
};
my @filters;
lib/APISchema/Generator/Markdown/ExampleFormatter.pm view on Meta::CPAN
package APISchema::Generator::Markdown::ExampleFormatter;
use 5.014;
use strict;
use warnings;
# lib
use APISchema::Generator::Markdown::Formatter qw(json);
# cpan
use URI::Escape qw(uri_escape_utf8);
use Class::Accessor::Lite (
new => 1,
ro => [qw(resolver spec)],
);
sub example {
my $self = shift;
return $self->resolver->example(@_);
}
lib/APISchema/Generator/Markdown/ExampleFormatter.pm view on Meta::CPAN
my $parameter = $self->spec->{parameter} or return '';
my $resource = $parameter->definition or return '';
my $example = $self->example($resource);
return '' unless defined $example;
return '' unless (ref $example) eq 'HASH';
return '' unless scalar keys %$example;
return '?' . join '&', map {
# TODO multiple values?
sprintf '%s=%s', map { uri_escape_utf8 $_ } $_, $example->{$_};
} sort keys %$example;
}
sub body {
my ($self) = @_;
my $body = $self->spec->{body} or return '';
my $resource = $body->definition or return '';
my $example = $self->example($resource);
return '' unless defined $example;
lib/APISchema/Generator/Markdown/Formatter.pm view on Meta::CPAN
use 5.014;
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
lib/APISchema/Generator/Markdown/Formatter.pm view on Meta::CPAN
sub code ($;$) {
my ($text, $exists) = @_;
return $exists ? '`null`' : '' unless defined $text;
return _code json $text;
}
sub anchor ($$) {
my ($label, $obj) = @_;
my $name = ref $obj ? $obj->title : $obj;
return sprintf '%s-%s', $label, uri_escape_utf8($name);
}
sub restriction ($) {
my $def = shift;
return '' unless (ref $def) eq 'HASH';
my @result = ();
for my $r (sort @{+RESTRICTIONS}) {
next unless defined $def->{$r};
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
# 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);
}
lib/Plack/App/APISchema/Document.pm view on Meta::CPAN
package Plack::App::APISchema::Document;
use strict;
use warnings;
use parent qw(Plack::Component);
use Plack::Util::Accessor qw(schema);
use Text::Markdown::Hoedown qw(markdown);
use Text::MicroTemplate qw(encoded_string);
use Text::MicroTemplate::DataSection qw(render_mt);
use Encode qw(encode_utf8);
use APISchema::Generator::Markdown;
sub call {
my ($self, $env) = @_;
my $generator = APISchema::Generator::Markdown->new;
my $markdown = $generator->format_schema($self->schema);
my $body = markdown(
lib/Plack/App/APISchema/Document.pm view on Meta::CPAN
| Text::Markdown::Hoedown::HOEDOWN_EXT_AUTOLINK
| Text::Markdown::Hoedown::HOEDOWN_EXT_FENCED_CODE
| Text::Markdown::Hoedown::HOEDOWN_EXT_NO_INTRA_EMPHASIS
)
);
my $renderer = Text::MicroTemplate::DataSection->new(package => ref $self);
my $title = $self->schema->title || '';
my $html = $renderer->render_mt('template.mt', $title, $body);
return [200, ['Content-Type' => 'text/html; charset=utf-8'], [encode_utf8 $html]];
}
1;
__DATA__
@@ template.mt
? my ($title, $body) = @_;
<!DOCTYPE html>
<html>
<head>
<title><?= $title ?></title>
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) = @_;
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);
}
script/generate_markdown_document.pl view on Meta::CPAN
#!/usr/bin/env perl
use strict;
use warnings;
use Encode qw(encode_utf8);
BEGIN {
# cpan
use Path::Class qw(file);
my $Root = file(__FILE__)->dir->parent->resolve->absolute;
unshift @INC, $Root->subdir('lib').q();
}
# lib
use APISchema::DSL;
script/generate_markdown_document.pl view on Meta::CPAN
<file> API definition file.
EOM
exit;
}
my $schema = APISchema::DSL::process {
include $ARGV[0];
};
my $generator = APISchema::Generator::Markdown->new;
print encode_utf8 $generator->format_schema($schema);
t/APISchema-DSL.t view on Meta::CPAN
package t::APISchema::Generator::Router::Simple;
use lib '.';
use t::test;
use Encode qw(decode_utf8);
sub _require : Test(startup => 1) {
my ($self) = @_;
BEGIN{ use_ok 'APISchema::DSL'; }
}
sub no_global : Tests {
dies_ok {
filter {};
t/APISchema-DSL.t view on Meta::CPAN
};
}
sub with_unicode : Tests {
my $schema = APISchema::DSL::process {
include 't/fixtures/user.def';
};
isa_ok $schema, 'APISchema::Schema';
is $schema->title, decode_utf8('ã¦ã¼ã¶ã¼');
is $schema->description, decode_utf8('ã¦ã¼ã¶ã¼ã®å®ç¾©');
cmp_deeply $schema->get_resource_by_name('user')->{definition}, {
type => 'object',
description => decode_utf8('ã¦ã¼ã¶ã¼'),
properties => {
first_name => {
type => 'string',
description => decode_utf8('å§'),
example => decode_utf8('å°é£¼'),
},
last_name => {
type => 'string',
description => decode_utf8('å'),
example => decode_utf8('å¼¾'),
},
},
required => ['first_name', 'last_name'],
};
}
t/APISchema-Generator-Markdown.t view on Meta::CPAN
package t::APISchema::Generator::Markdown;
use lib '.';
use t::test;
use t::test::fixtures;
use utf8;
use APISchema::DSL;
sub _require : Test(startup => 1) {
my ($self) = @_;
use_ok 'APISchema::Generator::Markdown';
}
sub instantiate : Tests {
t/APISchema-Generator-Markdown.t view on Meta::CPAN
};
};
my $generator = APISchema::Generator::Markdown->new;
my $markdown = $generator->format_schema($schema);
like $markdown, qr{\Q- [Fetch](#route-Fetch) - `GET`, `HEAD` /\E}, 'FETCH expanded to GET and HEAD';
};
}
sub generate_utf8 : Tests {
subtest 'Simple' => sub {
my $schema = t::test::fixtures::prepare_user;
my $generator = APISchema::Generator::Markdown->new;
my $markdown = $generator->format_schema($schema);
like $markdown, qr!{\n "first_name" : "å°é£¼",\n "last_name" : "å¼¾"\n}!;
like $markdown, qr!\Q|`.last_name` |`string` | |`"å¼¾"` | |å |\E!;
};
}
t/Plack-Middleware-APISchema-RequestValidator.t view on Meta::CPAN
my $res = $server->(
POST '/bmi',
Content_Type => 'application/json',
Content => encode_json({weight => 50, height => 1.6}),
);
is $res->code, 200;
done_testing;
}
};
subtest 'when valid utf8 request' => sub {
test_psgi $middleware => sub {
my $server = shift;
my $res = $server->(
POST '/bmi',
Content_Type => 'application/json; charset=UTF-8',
Content => encode_json({weight => 50, height => 1.6}),
);
is $res->code, 200;
done_testing;
}
t/Plack-Middleware-APISchema-RequestValidator.t view on Meta::CPAN
host => "localhost",
},
expected => $schema->get_resource_by_name('figure_header')->definition,
},
});
done_testing;
}
};
}
sub request_validator_with_utf8 : Tests {
my $schema = t::test::fixtures::prepare_user;
$schema->register_route(
method => 'POST',
route => '/user',
request_resource => {
body => 'user',
},
);
my $middleware = Plack::Middleware::APISchema::RequestValidator->new(schema => $schema);
t/Plack-Middleware-APISchema-ResponseValidator.t view on Meta::CPAN
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',
route => '/user',
response_resource => {
body => 'user',
},
);
my $middleware = Plack::Middleware::APISchema::ResponseValidator->new(schema => $schema);
$middleware->wrap(sub {
[200, [ 'Content-Type' => 'application/json; charset=utf-8' ], [ encode_json({ first_name => 'Bill', last_name => []}) ] ]
});
subtest 'invalid response with utf8' => sub {
test_psgi $middleware => sub {
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 => [],