File: openapi.pm

package info (click to toggle)
libopenapi-client-perl 1.07-1
  • links: PTS, VCS
  • area: main
  • in suites: bookworm, forky, sid, trixie
  • size: 176 kB
  • sloc: perl: 259; makefile: 6
file content (185 lines) | stat: -rw-r--r-- 5,995 bytes parent folder | download
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
package Mojolicious::Command::openapi;
use Mojo::Base 'Mojolicious::Command';

use OpenAPI::Client;
use Mojo::JSON qw(encode_json decode_json j);
use Mojo::Util qw(encode getopt);

use constant YAML    => eval 'require YAML::XS;1';
use constant REPLACE => $ENV{JSON_VALIDATOR_REPLACE} // 1;

sub _say  { length && say encode('UTF-8', $_) for @_ }
sub _warn { warn @_ }

has description => 'Perform Open API requests';
has usage       => sub { shift->extract_usage . "\n" };

has _client => undef;

sub run {
  my ($self, @args) = @_;
  my %ua;

  getopt \@args,
    'i|inactivity-timeout=i' => sub { $ua{inactivity_timeout} = $_[1] },
    'I|information'          => \my $info,
    'o|connect-timeout=i'    => sub { $ua{connect_timeout} = $_[1] },
    'p|parameter=s'          => \my %parameters,
    'c|content=s'            => \my $content,
    'S|response-size=i'      => sub { $ua{max_response_size} = $_[1] },
    'v|verbose'              => \my $verbose;

  # Read body from STDIN
  vec(my $r, fileno(STDIN), 1) = 1;
  $content //= !-t STDIN && select($r, undef, undef, 0) ? join '', <STDIN> : undef;

  my @client_args = (shift @args);
  my $op          = shift @args;
  my $selector    = shift @args // '';

  die $self->usage unless $client_args[0];

  if ($client_args[0] =~ m!^/! and !-e $client_args[0]) {
    $client_args[0] = Mojo::URL->new($client_args[0]);
    push @client_args, app => $self->app;
  }

  $self->_client(OpenAPI::Client->new(@client_args));
  return $self->_info($op) if $info;
  return $self->_list                  unless $op;
  die qq(Unknown operationId "$op".\n) unless $self->_client->can($op);

  $self->_client->ua->proxy->detect unless $ENV{OPENAPI_NO_PROXY};
  $self->_client->ua->$_($ua{$_}) for keys %ua;
  $self->_client->ua->on(
    start => sub {
      my ($ua, $tx) = @_;
      weaken $tx;
      $tx->res->content->on(body => sub { _warn _header($tx->req), _header($tx->res) }) if $verbose;
    }
  );

  my $tx = $self->_client->call($op => \%parameters, $content ? (json => decode_json $content) : ());
  if ($tx->error and $tx->error->{message} eq 'Invalid input') {
    _warn _header($tx->req), _header($tx->res) if $verbose;
  }

  return _json($tx->res->json, $selector) if !length $selector || $selector =~ m!^/!;
  return _say $tx->res->dom->find($selector)->each;
}

sub _header { $_[0]->build_start_line, $_[0]->headers->to_string, "\n\n" }

sub _info {
  my ($self, $op) = @_;
  local $YAML::XS::Boolean = 'JSON::PP';

  unless ($op) {
    my $op_spec = $self->_client->validator->bundle->data;
    return _say YAML ? YAML::XS::Dump($op_spec) : Mojo::Util::dumper($op_spec);
  }

  my ($schema, $op_spec) = ($self->_client->validator);
  for my $route ($schema->routes->each) {
    next if !$route->{operation_id} or $route->{operation_id} ne $op;
    $op_spec = $schema->get(['paths', @$route{qw(path method)}]);
  }

  return _warn qq(Could not find the given operationId "$op".\n) unless $op_spec;
  local $YAML::XS::Boolean = 'JSON::PP';
  return _say YAML ? YAML::XS::Dump($op_spec) : Mojo::Util::dumper($op_spec);
}

sub _json {
  return            unless defined(my $data = Mojo::JSON::Pointer->new(shift)->get(shift));
  return _say $data unless ref $data eq 'HASH' || ref $data eq 'ARRAY';
  _say Mojo::Util::decode('UTF-8', encode_json $data);
}

sub _list {
  my $self = shift;
  _warn "--- Operations for @{[$self->_client->base_url]}\n";
  $_->{operation_id} && _say $_->{operation_id} for $self->_client->validator->routes->each;
}

1;

=encoding utf8

=head1 NAME

Mojolicious::Command::openapi - Perform Open API requests

=head1 SYNOPSIS

  Usage: APPLICATION openapi SPECIFICATION OPERATION "{ARGUMENTS}" [SELECTOR|JSON-POINTER]

    # Fetch /api from myapp.pl and list available operationId
    ./myapp.pl openapi /api

    # Dump the whole specification or for an operationId
    ./myapp.pl openapi /api -I
    ./myapp.pl openapi /api -I addPet

    # Run an operation against a local application
    ./myapp.pl openapi /api listPets /pets/0

    # Run an operation against a local application, with body parameter
    ./myapp.pl openapi /api addPet -c '{"name":"pluto"}'
    echo '{"name":"pluto"} | ./myapp.pl openapi /api addPet

    # Run an operation with parameters
    mojo openapi spec.json listPets -p limit=10 -p type=dog

    # Run against local or online specifications
    mojo openapi /path/to/spec.json listPets
    mojo openapi http://service.example.com/api.json listPets

  Options:
    -h, --help                           Show this summary of available options
    -c, --content <content>              JSON content, with body parameter data
    -i, --inactivity-timeout <seconds>   Inactivity timeout, defaults to the
                                         value of MOJO_INACTIVITY_TIMEOUT or 20
    -I, --information [operationId]      Dump the specification about a given
                                         operationId or the whole spec.
                                         YAML::XS is preferred if available.
    -o, --connect-timeout <seconds>      Connect timeout, defaults to the value
                                         of MOJO_CONNECT_TIMEOUT or 10
    -p, --parameter <name=value>         Specify multiple header, path, or
                                         query parameter
    -S, --response-size <size>           Maximum response size in bytes,
                                         defaults to 2147483648 (2GB)
    -v, --verbose                        Print request and response headers to
                                         STDERR

=head1 DESCRIPTION

L<Mojolicious::Command::openapi> is a command line interface for
L<OpenAPI::Client>.

Not that this implementation is currently EXPERIMENTAL! Feedback is
appreciated.

=head1 ATTRIBUTES

=head2 description

  $str = $command->description;

=head2 usage

  $str = $command->usage;

=head1 METHODS

=head2 run

  $command->run(@ARGV);

Run this command.

=head1 SEE ALSO

L<OpenAPI::Client>.

=cut