APISchema

 view release on metacpan or  search on metacpan

lib/APISchema/Validator.pm  view on Meta::CPAN

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);

lib/Plack/Middleware/APISchema/RequestValidator.pm  view on Meta::CPAN


    $self->{router} //= do {
        my $generator = APISchema::Generator::Router::Simple->new;
        $generator->generate_router($self->schema);
    };
}

sub _resolve_status_code {
    my ($self, $validation_result) = @_;
    my $error_message = $validation_result->errors->{body}->{message} // '';
    return $error_message =~ m/Wrong content-type/ ? HTTP_UNSUPPORTED_MEDIA_TYPE : HTTP_UNPROCESSABLE_ENTITY;
}


1;

t/APISchema-Validator.t  view on Meta::CPAN

    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 {
        my $schema = _strict_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;
    };

    subtest 'invalid with wrong content type' => sub {
        my $schema = _strict_route t::test::fixtures::prepare_bmi, ['body'];
        my $validator = APISchema::Validator->for_request;
        my $content_type = 'application/x-www-form-urlencoded';
        my $result = $validator->validate('/endpoint' => {
            body => encode_json({weight => 50, height => 1.6}),
            content_type => $content_type,
        }, $schema);
        ok !$result->is_valid;
        is_deeply [ keys %{$result->errors} ], [ 'body' ];
        is_deeply [ map { $_->{message} } values %{$result->errors} ],
            [ ("Wrong content-type: $content_type") ];
    };

    subtest 'valid 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&height=1.6',
        }, $schema);
        ok $result->is_valid;
    };

t/APISchema-Validator.t  view on Meta::CPAN

    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 {
        my $schema = _strict_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;
    };

    subtest 'invalid with wrong content type' => sub {
        my $schema = _strict_route t::test::fixtures::prepare_bmi, ['body'];
        my $validator = APISchema::Validator->for_response;
        my $content_type = 'application/x-www-form-urlencoded';
        my $result = $validator->validate('/endpoint' => {
            body => encode_json({value => 19.5}),
            content_type => $content_type,
        }, $schema);
        ok !$result->is_valid;
        is_deeply [ keys %{$result->errors} ], [ 'body' ];
        is_deeply [ map { $_->{message} } values %{$result->errors} ],
            [ ("Wrong content-type: $content_type") ];
    };

    subtest 'valid header' => sub {
        my $schema = _simple_route t::test::fixtures::prepare_bmi, ['header'];
        my $validator = APISchema::Validator->for_response;
        my $result = $validator->validate('/endpoint' => {
            header => { value => 19.5 },
        }, $schema);
        ok $result->is_valid;
    };

t/Plack-App-APISchema-Document.t  view on Meta::CPAN


sub serve_document : Tests {
     my $schema = t::test::fixtures::prepare_bmi;
     my $app = Plack::App::APISchema::Document->new(schema => $schema)->to_app;

     subtest 'when valid request' => sub {
         test_psgi $app => sub {
             my $server = shift;
             my $res = $server->(GET '/');
             is $res->code, 200;
             is $res->header('content-type'), 'text/html; charset=utf-8';
             like $res->content, qr{<h3 id="toc_8"><a name="resource-figure"></a> <code>figure</code> : <code>object</code></h3>};
             done_testing;
         }
     };
}

sub mojibake : Tests {
    my $schema = t::test::fixtures::prepare_author;
    my $app = Plack::App::APISchema::Document->new(schema => $schema)->to_app;

     subtest 'when valid request' => sub {
         test_psgi $app => sub {
             my $server = shift;
             my $res = $server->(GET '/');
             is $res->code, 200;
             is $res->header('content-type'), 'text/html; charset=utf-8';
             like $res->content, qr{td>著者</td>};
             done_testing;
         }
     };
}

sub inheritable : Tests {
    my $schema = t::test::fixtures::prepare_bmi;
    my $app = t::test::InheritedDocument->new(schema => $schema)->to_app;

    subtest 'Document is inheritable' => sub {
        test_psgi $app => sub {
            my $server = shift;
            my $res = $server->(GET '/');
            is $res->code, 200;
            is $res->header('content-type'), 'text/html; charset=utf-8';
            like $res->content, qr{pink};
            done_testing;
        };
    };
}

t/Plack-App-APISchema-MockServer.t  view on Meta::CPAN


sub serve_document_bmi : Tests {
     my $schema = t::test::fixtures::prepare_bmi;
     my $app = Plack::App::APISchema::MockServer->new(schema => $schema)->to_app;

     subtest 'when valid request' => sub {
         test_psgi $app => sub {
             my $server = shift;
             my $res = $server->(POST '/bmi');
             is $res->code, 200;
             is $res->header('content-type'), 'application/json; charset=utf-8';
             is $res->content, q!{"value":19.5}!;
         }
     };

     subtest 'when invalid request' => sub {
         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',

t/Plack-App-APISchema-MockServer.t  view on Meta::CPAN

        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 => {

t/Plack-App-APISchema-MockServer.t  view on Meta::CPAN

            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;
        is $res->header('content-type'), 'application/json; charset=utf-8';
        is $res->content, q!{"author_name":"著者"}!;
    };
}

sub one_of : Tests {
    my $schema = t::test::fixtures::prepare_bmi;
    $schema->register_resource(maybe_bmi => {
        oneOf => [
            {
                type => 'object',

t/Plack-App-APISchema-MockServer.t  view on Meta::CPAN

            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;
        is $res->header('content-type'), 'application/json; charset=utf-8';
        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 => {

t/Plack-Middleware-APISchema-RequestValidator.t  view on Meta::CPAN

            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';
            my $res = $server->(
                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({

t/Plack-Middleware-APISchema-RequestValidator.t  view on Meta::CPAN

                    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;
        }
    };

    subtest 'when content-type is incorrect with strict content-type check' => sub {
        test_psgi $middleware => sub {
            my $server = shift;
            my $content_type = 'application/x-www-form-urlencoded';
            my $res = $server->(
                POST '/bmi_strict',
                Content_Type => $content_type,
                Content => encode_json({weight => 50, height => 1.6}),
            );
            is $res->code, HTTP_UNSUPPORTED_MEDIA_TYPE;
            cmp_deeply $res->content, json({
                body => { message => "Wrong content-type: $content_type" },
            });
            done_testing;
        }
    };

    subtest 'when valid request by parameter' => sub {
        test_psgi $middleware => sub {
            my $server = shift;
            my $res = $server->(
                POST '/bmi_by_parameter?weight=50&height=1.6',

t/Plack-Middleware-APISchema-ResponseValidator.t  view on Meta::CPAN

                    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});
            $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 => "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 = [];

t/Plack-Middleware-APISchema-ResponseValidator.t  view on Meta::CPAN

            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 => {

t/Plack-Middleware-APISchema-ResponseValidator.t  view on Meta::CPAN

                    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;
        }
    };

    subtest 'when content-type is incorrect with strict content-type check' => sub {
        test_psgi $middleware => sub {
            $content_type = 'application/x-www-form-urlencoded';
            $json = encode_json({value => 19.5});
            my $server = shift;
            my $res = $server->(POST '/bmi_strict');
            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: $content_type" },
            });
            done_testing;
        }
    };

    subtest 'other endpoints are not affected' => sub {
        test_psgi $middleware => sub {
            my $server = shift;
            my $res = $server->(GET '/other/');
            is $res->code, 200;



( run in 1.594 second using v1.01-cache-2.11-cpan-524268b4103 )