| line |
stmt |
bran |
cond |
sub |
pod |
time |
code |
|
1
|
|
|
|
|
|
|
package OpenAPI::Client; |
|
2
|
11
|
|
|
11
|
|
1432922
|
use Mojo::EventEmitter -base; |
|
|
11
|
|
|
|
|
4647
|
|
|
|
11
|
|
|
|
|
106
|
|
|
3
|
|
|
|
|
|
|
|
|
4
|
9
|
|
|
9
|
|
1571
|
use Carp (); |
|
|
9
|
|
|
|
|
17
|
|
|
|
9
|
|
|
|
|
159
|
|
|
5
|
9
|
|
|
9
|
|
3986
|
use JSON::Validator; |
|
|
9
|
|
|
|
|
2061133
|
|
|
|
9
|
|
|
|
|
80
|
|
|
6
|
9
|
|
|
9
|
|
394
|
use Mojo::UserAgent; |
|
|
9
|
|
|
|
|
23
|
|
|
|
9
|
|
|
|
|
115
|
|
|
7
|
9
|
|
|
9
|
|
227
|
use Mojo::Promise; |
|
|
9
|
|
|
|
|
18
|
|
|
|
9
|
|
|
|
|
108
|
|
|
8
|
9
|
|
|
9
|
|
223
|
use Scalar::Util qw(blessed); |
|
|
9
|
|
|
|
|
14
|
|
|
|
9
|
|
|
|
|
580
|
|
|
9
|
|
|
|
|
|
|
|
|
10
|
9
|
|
50
|
9
|
|
52
|
use constant DEBUG => $ENV{OPENAPI_CLIENT_DEBUG} || 0; |
|
|
9
|
|
|
|
|
19
|
|
|
|
9
|
|
|
|
|
22100
|
|
|
11
|
|
|
|
|
|
|
|
|
12
|
|
|
|
|
|
|
our $VERSION = '1.06'; |
|
13
|
|
|
|
|
|
|
|
|
14
|
|
|
|
|
|
|
has base_url => sub { |
|
15
|
|
|
|
|
|
|
my $self = shift; |
|
16
|
|
|
|
|
|
|
my $validator = $self->validator; |
|
17
|
|
|
|
|
|
|
my $url = $validator->can('base_url') ? $validator->base_url->clone : Mojo::URL->new; |
|
18
|
|
|
|
|
|
|
$url->scheme('http') unless $url->scheme; |
|
19
|
|
|
|
|
|
|
$url->host('localhost') unless $url->host; |
|
20
|
|
|
|
|
|
|
return $url; |
|
21
|
|
|
|
|
|
|
}; |
|
22
|
|
|
|
|
|
|
|
|
23
|
|
|
|
|
|
|
has ua => sub { Mojo::UserAgent->new }; |
|
24
|
|
|
|
|
|
|
|
|
25
|
|
|
|
|
|
|
sub call { |
|
26
|
7
|
|
|
7
|
1
|
17780
|
my ($self, $op) = (shift, shift); |
|
27
|
7
|
100
|
|
|
|
330
|
my $code = $self->can($op) or Carp::croak('[OpenAPI::Client] No such operationId'); |
|
28
|
6
|
|
|
|
|
30
|
return $self->$code(@_); |
|
29
|
|
|
|
|
|
|
} |
|
30
|
|
|
|
|
|
|
|
|
31
|
|
|
|
|
|
|
sub call_p { |
|
32
|
2
|
|
|
2
|
1
|
17825
|
my ($self, $op) = (shift, shift); |
|
33
|
2
|
100
|
|
|
|
56
|
my $code = $self->can("${op}_p") or return Mojo::Promise->reject('[OpenAPI::Client] No such operationId'); |
|
34
|
1
|
|
|
|
|
6
|
return $self->$code(@_); |
|
35
|
|
|
|
|
|
|
} |
|
36
|
|
|
|
|
|
|
|
|
37
|
|
|
|
|
|
|
sub new { |
|
38
|
24
|
|
|
24
|
1
|
1833616
|
my ($parent, $specification) = (shift, shift); |
|
39
|
24
|
50
|
|
|
|
117
|
my $attrs = @_ == 1 ? shift : {@_}; |
|
40
|
|
|
|
|
|
|
|
|
41
|
24
|
|
|
|
|
108
|
my $class = $parent->_url_to_class($specification); |
|
42
|
24
|
100
|
|
|
|
247
|
$parent->_generate_class($class, $specification, $attrs) unless $class->isa($parent); |
|
43
|
|
|
|
|
|
|
|
|
44
|
24
|
|
|
|
|
447
|
my $self = $class->SUPER::new($attrs); |
|
45
|
24
|
100
|
100
|
|
|
352
|
$self->base_url(Mojo::URL->new($self->{base_url})) if $self->{base_url} and !blessed $self->{base_url}; |
|
46
|
24
|
50
|
|
|
|
247
|
$self->ua->transactor->name('Mojo-OpenAPI (Perl)') unless $self->{ua}; |
|
47
|
|
|
|
|
|
|
|
|
48
|
24
|
100
|
|
|
|
793
|
if (my $app = delete $self->{app}) { |
|
49
|
9
|
|
|
|
|
52
|
$self->base_url->host(undef)->scheme(undef)->port(undef); |
|
50
|
9
|
|
|
|
|
125
|
$self->ua->server->app($app); |
|
51
|
|
|
|
|
|
|
} |
|
52
|
|
|
|
|
|
|
|
|
53
|
24
|
|
|
|
|
587
|
return $self; |
|
54
|
|
|
|
|
|
|
} |
|
55
|
|
|
|
|
|
|
|
|
56
|
0
|
|
|
0
|
1
|
0
|
sub validator { Carp::confess("validator() is not defined for $_[0]") } |
|
57
|
|
|
|
|
|
|
|
|
58
|
|
|
|
|
|
|
sub _generate_class { |
|
59
|
10
|
|
|
10
|
|
36
|
my ($parent, $class, $specification, $attrs) = @_; |
|
60
|
|
|
|
|
|
|
|
|
61
|
10
|
|
|
|
|
104
|
my $jv = JSON::Validator->new; |
|
62
|
10
|
|
50
|
|
|
257
|
$jv->coerce($attrs->{coerce} // 'booleans,numbers,strings'); |
|
63
|
10
|
100
|
|
|
|
432
|
$jv->store->ua->server->app($attrs->{app}) if $attrs->{app}; |
|
64
|
|
|
|
|
|
|
|
|
65
|
10
|
|
|
|
|
1002
|
my $schema = $jv->schema($specification)->schema; |
|
66
|
10
|
50
|
|
|
|
157972
|
die "Invalid schema: $specification has the following errors:\n", join "\n", @{$schema->errors} if @{$schema->errors}; |
|
|
0
|
|
|
|
|
0
|
|
|
|
10
|
|
|
|
|
47
|
|
|
67
|
|
|
|
|
|
|
|
|
68
|
8
|
50
|
|
8
|
|
97
|
eval <<"HERE" or Carp::confess("package $class: $@"); |
|
|
8
|
|
|
|
|
16
|
|
|
|
8
|
|
|
|
|
72
|
|
|
|
10
|
|
|
|
|
2153565
|
|
|
69
|
|
|
|
|
|
|
package $class; |
|
70
|
|
|
|
|
|
|
use Mojo::Base '$parent'; |
|
71
|
|
|
|
|
|
|
1; |
|
72
|
|
|
|
|
|
|
HERE |
|
73
|
|
|
|
|
|
|
|
|
74
|
10
|
|
|
69
|
|
109
|
Mojo::Util::monkey_patch($class => validator => sub {$schema}); |
|
|
69
|
|
|
69
|
|
1205
|
|
|
75
|
10
|
100
|
|
|
|
242
|
return unless $schema->can('routes'); # In case it is not an OpenAPI spec |
|
76
|
|
|
|
|
|
|
|
|
77
|
8
|
|
|
|
|
46
|
for my $route ($schema->routes->each) { |
|
78
|
13
|
50
|
|
|
|
2755
|
next unless $route->{operation_id}; |
|
79
|
13
|
|
|
|
|
23
|
warn "[$class] Add method $route->{operation_id}() for $route->{method} $route->{path}\n" if DEBUG; |
|
80
|
13
|
|
|
|
|
75
|
$class->_generate_method_bnb($route->{operation_id} => $route); |
|
81
|
13
|
|
|
|
|
263
|
$class->_generate_method_p("$route->{operation_id}_p" => $route); |
|
82
|
|
|
|
|
|
|
} |
|
83
|
|
|
|
|
|
|
} |
|
84
|
|
|
|
|
|
|
|
|
85
|
|
|
|
|
|
|
sub _generate_method_bnb { |
|
86
|
13
|
|
|
40
|
|
33
|
my ($class, $method_name, $route) = @_; |
|
87
|
|
|
|
|
|
|
|
|
88
|
|
|
|
|
|
|
Mojo::Util::monkey_patch $class => $method_name => sub { |
|
89
|
17
|
100
|
|
17
|
|
72199
|
my $cb = ref $_[-1] eq 'CODE' ? pop : undef; |
|
|
|
|
|
9
|
|
|
|
|
90
|
17
|
|
|
|
|
40
|
my $self = shift; |
|
91
|
17
|
|
|
|
|
99
|
my $tx = $self->_build_tx($route, @_); |
|
92
|
|
|
|
|
|
|
|
|
93
|
17
|
100
|
|
|
|
90
|
if ($tx->error) { |
|
94
|
9
|
100
|
|
|
|
187
|
return $tx unless $cb; |
|
95
|
1
|
|
|
1
|
|
19
|
Mojo::IOLoop->next_tick(sub { $self->$cb($tx) }); |
|
|
1
|
|
|
|
|
146
|
|
|
96
|
1
|
|
|
|
|
118
|
return $self; |
|
97
|
|
|
|
|
|
|
} |
|
98
|
|
|
|
|
|
|
|
|
99
|
8
|
50
|
|
|
|
289
|
return $self->ua->start($tx) unless $cb; |
|
100
|
0
|
|
|
0
|
|
0
|
$self->ua->start($tx, sub { $self->$cb($_[1]) }); |
|
|
0
|
|
|
|
|
0
|
|
|
101
|
0
|
|
|
|
|
0
|
return $self; |
|
102
|
13
|
|
|
|
|
79
|
}; |
|
103
|
|
|
|
|
|
|
} |
|
104
|
|
|
|
|
|
|
|
|
105
|
|
|
|
|
|
|
sub _generate_method_p { |
|
106
|
13
|
|
|
21
|
|
34
|
my ($class, $method_name, $route) = @_; |
|
107
|
|
|
|
|
|
|
|
|
108
|
|
|
|
|
|
|
Mojo::Util::monkey_patch $class => $method_name => sub { |
|
109
|
6
|
|
|
12
|
|
65613
|
my $self = shift; |
|
110
|
6
|
|
|
|
|
41
|
my $tx = $self->_build_tx($route, @_); |
|
111
|
|
|
|
|
|
|
|
|
112
|
6
|
100
|
|
|
|
31
|
return $self->ua->start_p($tx) unless my $err = $tx->error; |
|
113
|
1
|
50
|
|
|
|
22
|
return Mojo::Promise->new->reject($err->{message}) unless $err->{code}; |
|
114
|
1
|
50
|
33
|
|
|
3
|
return Mojo::Promise->new->reject('WebSocket handshake failed') if $tx->req->is_handshake && !$tx->is_websocket; |
|
115
|
1
|
|
|
|
|
63
|
return Mojo::Promise->new->resolve($tx); |
|
116
|
13
|
|
|
|
|
63
|
}; |
|
117
|
|
|
|
|
|
|
} |
|
118
|
|
|
|
|
|
|
|
|
119
|
|
|
|
|
|
|
sub _build_tx { |
|
120
|
23
|
|
|
35
|
|
82
|
my ($self, $route, $params, %content) = @_; |
|
121
|
23
|
|
|
|
|
89
|
my $v = $self->validator; |
|
122
|
23
|
|
|
|
|
119
|
my $url = $self->base_url->clone; |
|
123
|
23
|
|
|
|
|
1308
|
my ($tx, %headers); |
|
124
|
|
|
|
|
|
|
|
|
125
|
23
|
|
100
|
|
|
90
|
push @{$url->path}, map { local $_ = $_; s,\{([-\w]+)\},{$params->{$1}//''},ge; $_ } grep {length} split '/', |
|
|
37
|
|
|
|
|
79
|
|
|
|
37
|
|
|
|
|
157
|
|
|
|
10
|
|
|
|
|
26
|
|
|
|
10
|
|
|
|
|
81
|
|
|
|
37
|
|
|
|
|
132
|
|
|
|
60
|
|
|
|
|
2191
|
|
|
126
|
23
|
|
|
|
|
53
|
$route->{path}; |
|
127
|
|
|
|
|
|
|
|
|
128
|
|
|
|
|
|
|
my @errors = $self->validator->validate_request( |
|
129
|
|
|
|
|
|
|
[@$route{qw(method path)}], |
|
130
|
|
|
|
|
|
|
{ |
|
131
|
|
|
|
|
|
|
body => sub { |
|
132
|
8
|
|
|
18
|
|
2212
|
my ($name, $param) = @_; |
|
133
|
|
|
|
|
|
|
|
|
134
|
8
|
100
|
|
|
|
31
|
if (exists $params->{$name}) { |
|
135
|
4
|
|
|
|
|
12
|
$content{json} = $params->{$name}; |
|
136
|
|
|
|
|
|
|
} |
|
137
|
|
|
|
|
|
|
else { |
|
138
|
4
|
|
|
|
|
7
|
for ('body', sort keys %{$self->ua->transactor->generators}) { |
|
|
4
|
|
|
|
|
13
|
|
|
139
|
14
|
100
|
|
|
|
124
|
next unless exists $content{$_}; |
|
140
|
2
|
|
|
|
|
7
|
$params->{$name} = $content{$_}; |
|
141
|
2
|
|
|
|
|
5
|
last; |
|
142
|
|
|
|
|
|
|
} |
|
143
|
|
|
|
|
|
|
} |
|
144
|
|
|
|
|
|
|
|
|
145
|
8
|
|
|
|
|
36
|
return {exists => $params->{$name}, value => $params->{$name}}; |
|
146
|
|
|
|
|
|
|
}, |
|
147
|
|
|
|
|
|
|
formData => sub { |
|
148
|
8
|
|
|
18
|
|
2263
|
my ($name, $param) = @_; |
|
149
|
8
|
|
|
|
|
25
|
my $value = _param_as_array($name => $params); |
|
150
|
8
|
|
|
|
|
25
|
$content{form}{$name} = $params->{$name}; |
|
151
|
8
|
|
|
|
|
35
|
return {exists => !!@$value, value => $value}; |
|
152
|
|
|
|
|
|
|
}, |
|
153
|
|
|
|
|
|
|
header => sub { |
|
154
|
2
|
|
|
12
|
|
283
|
my ($name, $param) = @_; |
|
155
|
2
|
|
|
|
|
9
|
my $value = _param_as_array($name => $params); |
|
156
|
2
|
|
|
|
|
7
|
$headers{$name} = $value; |
|
157
|
2
|
|
|
|
|
10
|
return {exists => !!@$value, value => $value}; |
|
158
|
|
|
|
|
|
|
}, |
|
159
|
|
|
|
|
|
|
path => sub { |
|
160
|
10
|
|
|
20
|
|
2108
|
my ($name, $param) = @_; |
|
161
|
10
|
|
|
|
|
53
|
return {exists => exists $params->{$name}, value => $params->{$name}}; |
|
162
|
|
|
|
|
|
|
}, |
|
163
|
|
|
|
|
|
|
query => sub { |
|
164
|
18
|
|
|
28
|
|
3971
|
my ($name, $param) = @_; |
|
165
|
18
|
|
|
|
|
61
|
my $value = _param_as_array($name => $params); |
|
166
|
18
|
|
|
|
|
87
|
$url->query->param($name => _coerce_collection_format($value, $param)); |
|
167
|
18
|
|
|
|
|
917
|
return {exists => !!@$value, value => $value}; |
|
168
|
|
|
|
|
|
|
}, |
|
169
|
|
|
|
|
|
|
} |
|
170
|
23
|
|
|
|
|
110
|
); |
|
171
|
|
|
|
|
|
|
|
|
172
|
23
|
100
|
|
|
|
4957
|
if (@errors) { |
|
173
|
10
|
|
|
|
|
18
|
warn "[@{[ref $self]}] Validation for $route->{method} $url failed: @errors\n" if DEBUG; |
|
174
|
10
|
|
|
|
|
86
|
$tx = Mojo::Transaction::HTTP->new; |
|
175
|
10
|
|
|
|
|
90
|
$tx->req->method(uc $route->{method}); |
|
176
|
10
|
|
|
|
|
289
|
$tx->req->url($url); |
|
177
|
10
|
|
|
|
|
139
|
$tx->res->headers->content_type('application/json'); |
|
178
|
10
|
|
|
|
|
802
|
$tx->res->body(Mojo::JSON::encode_json({errors => \@errors})); |
|
179
|
10
|
|
|
|
|
2255
|
$tx->res->code(400)->message($tx->res->default_message); |
|
180
|
10
|
|
|
|
|
278
|
$tx->res->error({message => 'Invalid input', code => 400}); |
|
181
|
|
|
|
|
|
|
} |
|
182
|
|
|
|
|
|
|
else { |
|
183
|
13
|
|
|
|
|
29
|
warn "[@{[ref $self]}] Validation for $route->{method} $url was successful\n" if DEBUG; |
|
184
|
13
|
50
|
|
|
|
69
|
$tx = $self->ua->build_tx($route->{method}, $url, \%headers, defined $content{body} ? $content{body} : %content); |
|
185
|
|
|
|
|
|
|
} |
|
186
|
|
|
|
|
|
|
|
|
187
|
23
|
|
|
|
|
4489
|
$tx->req->env->{operationId} = $route->{operation_id}; |
|
188
|
23
|
|
|
|
|
383
|
$self->emit(after_build_tx => $tx); |
|
189
|
|
|
|
|
|
|
|
|
190
|
23
|
|
|
|
|
398
|
return $tx; |
|
191
|
|
|
|
|
|
|
} |
|
192
|
|
|
|
|
|
|
|
|
193
|
|
|
|
|
|
|
sub _coerce_collection_format { |
|
194
|
18
|
|
|
22
|
|
369
|
my ($value, $param) = @_; |
|
195
|
18
|
|
66
|
|
|
168
|
my $format = $param->{collectionFormat} || (+($param->{type} // '') eq 'array' ? 'csv' : ''); |
|
196
|
18
|
100
|
66
|
|
|
133
|
return $value if !$format or $format eq 'multi'; |
|
197
|
1
|
50
|
|
|
|
5
|
return join "|", @$value if $format eq 'pipes'; |
|
198
|
1
|
50
|
|
|
|
4
|
return join " ", @$value if $format eq 'ssv'; |
|
199
|
1
|
50
|
|
|
|
5
|
return join "\t", @$value if $format eq 'tsv'; |
|
200
|
1
|
|
|
|
|
9
|
return join ",", @$value; |
|
201
|
|
|
|
|
|
|
} |
|
202
|
|
|
|
|
|
|
|
|
203
|
|
|
|
|
|
|
sub _param_as_array { |
|
204
|
28
|
|
|
32
|
|
76
|
my ($name, $params) = @_; |
|
205
|
28
|
100
|
|
|
|
138
|
return !exists $params->{$name} ? [] : ref $params->{$name} eq 'ARRAY' ? $params->{$name} : [$params->{$name}]; |
|
|
|
100
|
|
|
|
|
|
|
206
|
|
|
|
|
|
|
} |
|
207
|
|
|
|
|
|
|
|
|
208
|
|
|
|
|
|
|
sub _url_to_class { |
|
209
|
24
|
|
|
24
|
|
107
|
my ($self, $package) = @_; |
|
210
|
|
|
|
|
|
|
|
|
211
|
24
|
|
|
|
|
124
|
$package =~ s!^\w+?://!!; |
|
212
|
24
|
|
|
|
|
857
|
$package =~ s!\W!_!g; |
|
213
|
24
|
50
|
|
|
|
779
|
$package = Mojo::Util::md5_sum($package) if length $package > 110; # 110 is a bit random, but it cannot be too long |
|
214
|
|
|
|
|
|
|
|
|
215
|
24
|
|
|
|
|
93
|
return "$self\::$package"; |
|
216
|
|
|
|
|
|
|
} |
|
217
|
|
|
|
|
|
|
|
|
218
|
|
|
|
|
|
|
1; |
|
219
|
|
|
|
|
|
|
|
|
220
|
|
|
|
|
|
|
=encoding utf8 |
|
221
|
|
|
|
|
|
|
|
|
222
|
|
|
|
|
|
|
=head1 NAME |
|
223
|
|
|
|
|
|
|
|
|
224
|
|
|
|
|
|
|
OpenAPI::Client - A client for talking to an Open API powered server |
|
225
|
|
|
|
|
|
|
|
|
226
|
|
|
|
|
|
|
=head1 DESCRIPTION |
|
227
|
|
|
|
|
|
|
|
|
228
|
|
|
|
|
|
|
L can generating classes that can talk to an Open API server. |
|
229
|
|
|
|
|
|
|
This is done by generating a custom class, based on a Open API specification, |
|
230
|
|
|
|
|
|
|
with methods that transform parameters into a HTTP request. |
|
231
|
|
|
|
|
|
|
|
|
232
|
|
|
|
|
|
|
The generated class will perform input validation, so invalid data won't be |
|
233
|
|
|
|
|
|
|
sent to the server. |
|
234
|
|
|
|
|
|
|
|
|
235
|
|
|
|
|
|
|
Note that this implementation is currently EXPERIMENTAL, but unlikely to change! |
|
236
|
|
|
|
|
|
|
Feedback is appreciated. |
|
237
|
|
|
|
|
|
|
|
|
238
|
|
|
|
|
|
|
=head1 SYNOPSIS |
|
239
|
|
|
|
|
|
|
|
|
240
|
|
|
|
|
|
|
=head2 Open API specification |
|
241
|
|
|
|
|
|
|
|
|
242
|
|
|
|
|
|
|
The specification given to L need to point to a valid OpenAPI document. |
|
243
|
|
|
|
|
|
|
This document can be OpenAPI v2.x or v3.x, and it can be in either JSON or YAML |
|
244
|
|
|
|
|
|
|
format. Example: |
|
245
|
|
|
|
|
|
|
|
|
246
|
|
|
|
|
|
|
openapi: 3.0.1 |
|
247
|
|
|
|
|
|
|
info: |
|
248
|
|
|
|
|
|
|
title: Swagger Petstore |
|
249
|
|
|
|
|
|
|
version: 1.0.0 |
|
250
|
|
|
|
|
|
|
servers: |
|
251
|
|
|
|
|
|
|
- url: http://petstore.swagger.io/v1 |
|
252
|
|
|
|
|
|
|
paths: |
|
253
|
|
|
|
|
|
|
/pets: |
|
254
|
|
|
|
|
|
|
get: |
|
255
|
|
|
|
|
|
|
operationId: listPets |
|
256
|
|
|
|
|
|
|
... |
|
257
|
|
|
|
|
|
|
|
|
258
|
|
|
|
|
|
|
C, C and the first item in C will be used to construct |
|
259
|
|
|
|
|
|
|
L. This can be altered at any time, if you need to send data to a |
|
260
|
|
|
|
|
|
|
custom endpoint. |
|
261
|
|
|
|
|
|
|
|
|
262
|
|
|
|
|
|
|
=head2 Client |
|
263
|
|
|
|
|
|
|
|
|
264
|
|
|
|
|
|
|
The OpenAPI API specification will be used to generate a sub-class of |
|
265
|
|
|
|
|
|
|
L where the "operationId", inside of each path definition, is |
|
266
|
|
|
|
|
|
|
used to generate methods: |
|
267
|
|
|
|
|
|
|
|
|
268
|
|
|
|
|
|
|
use OpenAPI::Client; |
|
269
|
|
|
|
|
|
|
$client = OpenAPI::Client->new("file:///path/to/api.json"); |
|
270
|
|
|
|
|
|
|
|
|
271
|
|
|
|
|
|
|
# Blocking |
|
272
|
|
|
|
|
|
|
$tx = $client->listPets; |
|
273
|
|
|
|
|
|
|
|
|
274
|
|
|
|
|
|
|
# Non-blocking |
|
275
|
|
|
|
|
|
|
$client = $client->listPets(sub { my ($client, $tx) = @_; }); |
|
276
|
|
|
|
|
|
|
|
|
277
|
|
|
|
|
|
|
# Promises |
|
278
|
|
|
|
|
|
|
$promise = $client->listPets_p->then(sub { my $tx = shift }); |
|
279
|
|
|
|
|
|
|
|
|
280
|
|
|
|
|
|
|
# With parameters |
|
281
|
|
|
|
|
|
|
$tx = $client->listPets({limit => 10}); |
|
282
|
|
|
|
|
|
|
|
|
283
|
|
|
|
|
|
|
See L for more information about what you can do with the |
|
284
|
|
|
|
|
|
|
C<$tx> object, but you often just want something like this: |
|
285
|
|
|
|
|
|
|
|
|
286
|
|
|
|
|
|
|
# Check for errors |
|
287
|
|
|
|
|
|
|
die $tx->error->{message} if $tx->error; |
|
288
|
|
|
|
|
|
|
|
|
289
|
|
|
|
|
|
|
# Extract data from the JSON responses |
|
290
|
|
|
|
|
|
|
say $tx->res->json->{pets}[0]{name}; |
|
291
|
|
|
|
|
|
|
|
|
292
|
|
|
|
|
|
|
Check out L, L and |
|
293
|
|
|
|
|
|
|
L for some of the most used methods in that class. |
|
294
|
|
|
|
|
|
|
|
|
295
|
|
|
|
|
|
|
=head1 CUSTOMIZATION |
|
296
|
|
|
|
|
|
|
|
|
297
|
|
|
|
|
|
|
=head2 Custom server URL |
|
298
|
|
|
|
|
|
|
|
|
299
|
|
|
|
|
|
|
If you want to request a different server than what is specified in the Open |
|
300
|
|
|
|
|
|
|
API document, you can change the L: |
|
301
|
|
|
|
|
|
|
|
|
302
|
|
|
|
|
|
|
# Pass on a Mojo::URL object to the constructor |
|
303
|
|
|
|
|
|
|
$base_url = Mojo::URL->new("http://example.com"); |
|
304
|
|
|
|
|
|
|
$client1 = OpenAPI::Client->new("file:///path/to/api.json", base_url => $base_url); |
|
305
|
|
|
|
|
|
|
|
|
306
|
|
|
|
|
|
|
# A plain string will be converted to a Mojo::URL object |
|
307
|
|
|
|
|
|
|
$client2 = OpenAPI::Client->new("file:///path/to/api.json", base_url => "http://example.com"); |
|
308
|
|
|
|
|
|
|
|
|
309
|
|
|
|
|
|
|
# Change the base_url after the client has been created |
|
310
|
|
|
|
|
|
|
$client3 = OpenAPI::Client->new("file:///path/to/api.json"); |
|
311
|
|
|
|
|
|
|
$client3->base_url->host("other.example.com"); |
|
312
|
|
|
|
|
|
|
|
|
313
|
|
|
|
|
|
|
=head2 Custom content |
|
314
|
|
|
|
|
|
|
|
|
315
|
|
|
|
|
|
|
You can send XML or any format you like, but this require you to add a new |
|
316
|
|
|
|
|
|
|
"generator": |
|
317
|
|
|
|
|
|
|
|
|
318
|
|
|
|
|
|
|
use Your::XML::Library "to_xml"; |
|
319
|
|
|
|
|
|
|
$client->ua->transactor->add_generator(xml => sub { |
|
320
|
|
|
|
|
|
|
my ($t, $tx, $data) = @_; |
|
321
|
|
|
|
|
|
|
$tx->req->body(to_xml $data); |
|
322
|
|
|
|
|
|
|
return $tx; |
|
323
|
|
|
|
|
|
|
}); |
|
324
|
|
|
|
|
|
|
|
|
325
|
|
|
|
|
|
|
$client->addHero({}, xml => {name => "Supergirl"}); |
|
326
|
|
|
|
|
|
|
|
|
327
|
|
|
|
|
|
|
See L for more details. |
|
328
|
|
|
|
|
|
|
|
|
329
|
|
|
|
|
|
|
=head1 EVENTS |
|
330
|
|
|
|
|
|
|
|
|
331
|
|
|
|
|
|
|
=head2 after_build_tx |
|
332
|
|
|
|
|
|
|
|
|
333
|
|
|
|
|
|
|
$client->on(after_build_tx => sub { my ($client, $tx) = @_ }) |
|
334
|
|
|
|
|
|
|
|
|
335
|
|
|
|
|
|
|
This event is emitted after a L object has been |
|
336
|
|
|
|
|
|
|
built, just before it is passed on to the L. Note that all validation has |
|
337
|
|
|
|
|
|
|
already been run, so alternating the C<$tx> too much, might cause an invalid |
|
338
|
|
|
|
|
|
|
request on the server side. |
|
339
|
|
|
|
|
|
|
|
|
340
|
|
|
|
|
|
|
A special L variable will be set, to reference the |
|
341
|
|
|
|
|
|
|
operationId: |
|
342
|
|
|
|
|
|
|
|
|
343
|
|
|
|
|
|
|
$tx->req->env->{operationId}; |
|
344
|
|
|
|
|
|
|
|
|
345
|
|
|
|
|
|
|
Note that this usage of C is currently EXPERIMENTAL: |
|
346
|
|
|
|
|
|
|
|
|
347
|
|
|
|
|
|
|
=head1 ATTRIBUTES |
|
348
|
|
|
|
|
|
|
|
|
349
|
|
|
|
|
|
|
=head2 base_url |
|
350
|
|
|
|
|
|
|
|
|
351
|
|
|
|
|
|
|
$base_url = $client->base_url; |
|
352
|
|
|
|
|
|
|
|
|
353
|
|
|
|
|
|
|
Returns a L object with the base URL to the API. The default value |
|
354
|
|
|
|
|
|
|
comes from C, C and C in the OpenAPI v2 specification |
|
355
|
|
|
|
|
|
|
or from C in the OpenAPI v3 specification. |
|
356
|
|
|
|
|
|
|
|
|
357
|
|
|
|
|
|
|
=head2 ua |
|
358
|
|
|
|
|
|
|
|
|
359
|
|
|
|
|
|
|
$ua = $client->ua; |
|
360
|
|
|
|
|
|
|
|
|
361
|
|
|
|
|
|
|
Returns a L object which is used to execute requests. |
|
362
|
|
|
|
|
|
|
|
|
363
|
|
|
|
|
|
|
=head1 METHODS |
|
364
|
|
|
|
|
|
|
|
|
365
|
|
|
|
|
|
|
=head2 call |
|
366
|
|
|
|
|
|
|
|
|
367
|
|
|
|
|
|
|
$tx = $client->call($operationId => \%params, %content); |
|
368
|
|
|
|
|
|
|
$client = $client->call($operationId => \%params, %content, sub { my ($client, $tx) = @_; }); |
|
369
|
|
|
|
|
|
|
|
|
370
|
|
|
|
|
|
|
Used to either call an C<$operationId> that has an "invalid name", such as |
|
371
|
|
|
|
|
|
|
"list pets" instead of "listPets" or to call an C<$operationId> that you are |
|
372
|
|
|
|
|
|
|
unsure is supported yet. If it is not, an exception will be thrown, |
|
373
|
|
|
|
|
|
|
matching text "No such operationId". |
|
374
|
|
|
|
|
|
|
|
|
375
|
|
|
|
|
|
|
C<$operationId> is the name of the resource defined in the |
|
376
|
|
|
|
|
|
|
L. |
|
377
|
|
|
|
|
|
|
|
|
378
|
|
|
|
|
|
|
C<$params> is optional, but must be a hash ref, where the keys should match a |
|
379
|
|
|
|
|
|
|
named parameter in the L. |
|
380
|
|
|
|
|
|
|
|
|
381
|
|
|
|
|
|
|
C<%content> is used for the body of the request, where the key need to be |
|
382
|
|
|
|
|
|
|
either "body" or a matching L. Example: |
|
383
|
|
|
|
|
|
|
|
|
384
|
|
|
|
|
|
|
$client->addHero({}, body => "Some data"); |
|
385
|
|
|
|
|
|
|
$client->addHero({}, json => {name => "Supergirl"}); |
|
386
|
|
|
|
|
|
|
|
|
387
|
|
|
|
|
|
|
C<$tx> is a L object. |
|
388
|
|
|
|
|
|
|
|
|
389
|
|
|
|
|
|
|
=head2 call_p |
|
390
|
|
|
|
|
|
|
|
|
391
|
|
|
|
|
|
|
$promise = $client->call_p($operationId => $params, %content); |
|
392
|
|
|
|
|
|
|
$promise->then(sub { my $tx = shift }); |
|
393
|
|
|
|
|
|
|
|
|
394
|
|
|
|
|
|
|
As L above, but returns a L object. |
|
395
|
|
|
|
|
|
|
|
|
396
|
|
|
|
|
|
|
=head2 new |
|
397
|
|
|
|
|
|
|
|
|
398
|
|
|
|
|
|
|
$client = OpenAPI::Client->new($specification, \%attributes); |
|
399
|
|
|
|
|
|
|
$client = OpenAPI::Client->new($specification, %attributes); |
|
400
|
|
|
|
|
|
|
|
|
401
|
|
|
|
|
|
|
Returns an object of a generated class, with methods generated from the Open |
|
402
|
|
|
|
|
|
|
API specification located at C<$specification>. See L |
|
403
|
|
|
|
|
|
|
for valid versions of C<$specification>. |
|
404
|
|
|
|
|
|
|
|
|
405
|
|
|
|
|
|
|
Note that the class is cached by perl, so loading a new specification from the |
|
406
|
|
|
|
|
|
|
same URL will not generate a new class. |
|
407
|
|
|
|
|
|
|
|
|
408
|
|
|
|
|
|
|
Extra C<%attributes>: |
|
409
|
|
|
|
|
|
|
|
|
410
|
|
|
|
|
|
|
=over 2 |
|
411
|
|
|
|
|
|
|
|
|
412
|
|
|
|
|
|
|
=item * app |
|
413
|
|
|
|
|
|
|
|
|
414
|
|
|
|
|
|
|
Specifying an C is useful when running against a local L |
|
415
|
|
|
|
|
|
|
instance. |
|
416
|
|
|
|
|
|
|
|
|
417
|
|
|
|
|
|
|
=item * coerce |
|
418
|
|
|
|
|
|
|
|
|
419
|
|
|
|
|
|
|
See L. Default to "booleans,numbers,strings". |
|
420
|
|
|
|
|
|
|
|
|
421
|
|
|
|
|
|
|
=back |
|
422
|
|
|
|
|
|
|
|
|
423
|
|
|
|
|
|
|
=head2 validator |
|
424
|
|
|
|
|
|
|
|
|
425
|
|
|
|
|
|
|
$validator = $client->validator; |
|
426
|
|
|
|
|
|
|
$validator = $class->validator; |
|
427
|
|
|
|
|
|
|
|
|
428
|
|
|
|
|
|
|
Returns a L object for a generated class. |
|
429
|
|
|
|
|
|
|
Note that this is a global variable, so changing the object will affect all |
|
430
|
|
|
|
|
|
|
instances returned by L. |
|
431
|
|
|
|
|
|
|
|
|
432
|
|
|
|
|
|
|
=head1 COPYRIGHT AND LICENSE |
|
433
|
|
|
|
|
|
|
|
|
434
|
|
|
|
|
|
|
Copyright (C) 2017-2021, Jan Henning Thorsen |
|
435
|
|
|
|
|
|
|
|
|
436
|
|
|
|
|
|
|
This program is free software, you can redistribute it and/or modify it under |
|
437
|
|
|
|
|
|
|
the terms of the Artistic License version 2.0. |
|
438
|
|
|
|
|
|
|
|
|
439
|
|
|
|
|
|
|
=head1 AUTHORS |
|
440
|
|
|
|
|
|
|
|
|
441
|
|
|
|
|
|
|
=head2 Project Founder |
|
442
|
|
|
|
|
|
|
|
|
443
|
|
|
|
|
|
|
Jan Henning Thorsen - C |
|
444
|
|
|
|
|
|
|
|
|
445
|
|
|
|
|
|
|
=head2 Contributors |
|
446
|
|
|
|
|
|
|
|
|
447
|
|
|
|
|
|
|
=over 2 |
|
448
|
|
|
|
|
|
|
|
|
449
|
|
|
|
|
|
|
|
|
450
|
|
|
|
|
|
|
=item * Clive Holloway |
|
451
|
|
|
|
|
|
|
|
|
452
|
|
|
|
|
|
|
=item * Ed J |
|
453
|
|
|
|
|
|
|
|
|
454
|
|
|
|
|
|
|
=item * Jan Henning Thorsen |
|
455
|
|
|
|
|
|
|
|
|
456
|
|
|
|
|
|
|
=item * Jan Henning Thorsen |
|
457
|
|
|
|
|
|
|
|
|
458
|
|
|
|
|
|
|
=item * Mohammad S Anwar |
|
459
|
|
|
|
|
|
|
|
|
460
|
|
|
|
|
|
|
=item * Reneeb |
|
461
|
|
|
|
|
|
|
|
|
462
|
|
|
|
|
|
|
=item * Roy Storey |
|
463
|
|
|
|
|
|
|
|
|
464
|
|
|
|
|
|
|
=item * Veesh Goldman |
|
465
|
|
|
|
|
|
|
|
|
466
|
|
|
|
|
|
|
=back |
|
467
|
|
|
|
|
|
|
|
|
468
|
|
|
|
|
|
|
=cut |