Dancer-Plugin-Swagger/lib/Dancer/Plugin/Swagger/Path.pm
package Dancer::Plugin::Swagger::Path;
our $AUTHORITY = 'cpan:YANICK';
# ABSTRACT: Internal representation of a swagger path
$Dancer::Plugin::Swagger::Path::VERSION = '0.3.0';
use strict;
use warnings;
use Moo;
use MooseX::MungeHas 'is_ro';
use Carp;
use Hash::Merge;
use Clone 'clone';
use List::AllUtils qw/ first any none /;
use JSON;
use Class::Load qw/ load_class /;
has route => ( handles => [ 'pattern' ] );
has tags => ( predicate => 1 );
has method => sub {
eval { $_[0]->route->method }
or croak "no route or explicit method provided to path";
};
has path => sub {
dancer_pattern_to_swagger_path( $_[0]->route->pattern );
};
has responses => ( predicate => 1);
has description => ( predicate => 1 );
has parameters =>
lazy => 1,
default => sub { [] },
predicate => 1,
;
# TODO allow to pass a hashref instead of an arrayref
sub parameter {
my( $self, $param, $args ) = @_;
$args ||= {};
$args->{name} ||= $param;
my $p = first { $_->{name} eq $param } @{ $self->parameters };
push @{ $self->parameters || [] }, $p = { name => $param }
unless $p;
%$p = %{Hash::Merge::merge( $p, $args )};
}
sub dancer_pattern_to_swagger_path {
my $pattern = shift;
$pattern =~ s#(?<=/):(\w+)(?=/|$)#{$1}#g;
return $pattern;
}
sub add_to_doc {
my( $self, $doc ) = @_;
my $path = $self->path;
my $method = $self->method;
# already there
next if $doc->{paths}{$path}{$method};
my $m = $doc->{paths}{$path}{$method} ||= {};
$m->{description} = $self->description if $self->has_description;
$m->{parameters} = $self->parameters if $self->has_parameters;
$m->{tags} = $self->tags if $self->has_tags;
if( $self->has_responses ) {
$m->{responses} = clone $self->responses;
for my $r ( values %{$m->{responses}} ) {
delete $r->{template};
if( my $example = delete $r->{example} ) {
my $serializer = Dancer::engine('serializer');
die "Don't know content type for serializer ", ref $serializer
if none { $serializer->isa($_) } qw/ Dancer::Serializer::JSON Dancer::Serializer::YAML /;
$r->{examples}{$serializer->content_type} = $example;
}
}
}
}
sub validate_response {
my( $self, $code, $data, $strict ) = @_;
my $schema = $self->responses->{$code}{schema};
die "no schema found for return code $code for ", join ' ' , uc($self->method), $self->path
unless $schema or not $strict;
return unless $schema;
my $plugin = Dancer::Plugin::Swagger->instance;
$schema = {
definitions => $plugin->doc->{definitions},
properties => { response => $schema },
};
my $result = load_class('JSON::Schema::AsType')->new( schema => $schema)->validate_explain({ response => $data });
return unless $result;
die join "\n", map { "* " . $_ } @$result;
}
sub BUILD {
my $self = shift;
for my $param ( eval { @{ $self->route->{_params} } } ) {
$self->parameter( $param => {
in => 'path',
required => JSON::true,
type => "string",
} );
}
}
1;
__END__
=pod
=encoding UTF-8
=head1 NAME
Dancer::Plugin::Swagger::Path - Internal representation of a swagger path
=head1 VERSION
version 0.3.0
=head1 DESCRIPTION
Objects of this class are used by L<Dancer::Plugin::Swagger> to represent
a path in the Swagger document.
=head1 AUTHOR
Yanick Champoux <yanick@cpan.org>
=head1 COPYRIGHT AND LICENSE
This software is copyright (c) 2021, 2016, 2015 by Yanick Champoux.
This is free software; you can redistribute it and/or modify it under
the same terms as the Perl 5 programming language system itself.
=cut