File Coverage

blib/lib/Docker/Registry/V2.pm
Criterion Covered Total %
statement 75 78 96.1
branch 6 8 75.0
condition n/a
subroutine 18 21 85.7
pod 0 7 0.0
total 99 114 86.8


line stmt bran cond sub pod time code
1             package Docker::Registry::Call::Repositories;
2 5     5   161080 use Moo;
  5         11554  
  5         26  
3 5     5   3440 use Types::Standard qw/Int Str/;
  5         74504  
  5         35  
4             has n => (is => 'ro', isa => Int);
5             has last => (is => 'ro', isa => Str);
6              
7             package Docker::Registry::Result::Repositories;
8 5     5   4030 use Moo;
  5         11  
  5         24  
9 5     5   1732 use Types::Standard qw/ArrayRef Str/;
  5         12  
  5         46  
10             has repositories => (is => 'ro', isa => ArrayRef[Str]);
11             has last => (is => 'ro', isa => Str);
12              
13             package Docker::Registry::Call::RepositoryTags;
14 5     5   3347 use Moo;
  5         11  
  5         21  
15 5     5   1699 use Types::Standard qw/Int Str/;
  5         13  
  5         29  
16             has repository => (is => 'ro', isa => Str, required => 1);
17             has n => (is => 'ro', isa => Int);
18             has last => (is => 'ro', isa => Str);
19              
20             package Docker::Registry::Result::RepositoryTags;
21 5     5   3181 use Moo;
  5         11  
  5         22  
22 5     5   1662 use Types::Standard qw/ArrayRef Str/;
  5         22  
  5         43  
23             has name => (is => 'ro', isa => Str, required => 1);
24             has tags => (is => 'ro', isa => ArrayRef[Str], required => 1);
25             has last => (is => 'ro', isa => Str);
26              
27             package Docker::Registry::V2;
28 5     5   3116 use Moo;
  5         19  
  5         23  
29 5     5   3529 use Docker::Registry::Types qw(DockerRegistryURI);
  5         18  
  5         48  
30 5     5   1755 use Types::Standard qw/Str ConsumerOf InstanceOf/;
  5         12  
  5         25  
31              
32             has url => (is => 'ro', coerce => 1, isa => DockerRegistryURI, required => 1);
33             has api_base => (is => 'ro', default => 'v2');
34              
35             has caller => (is => 'ro', isa => ConsumerOf['Docker::Registry::IO'], default => sub {
36             require Docker::Registry::IO::Simple;
37             Docker::Registry::IO::Simple->new;
38             });
39             has auth => (is => 'ro', isa => ConsumerOf['Docker::Registry::Auth'], lazy => 1, builder => 'build_auth' );
40             has request_builder => (is => 'ro', isa => InstanceOf['Docker::Registry::RequestBuilder'], lazy => 1, default => sub {
41             my $self = shift;
42             require Docker::Registry::RequestBuilder;
43             Docker::Registry::RequestBuilder->new(url => $self->url, api_base => $self->api_base);
44             });
45              
46             sub build_auth {
47 1     1 0 463 require Docker::Registry::Auth::None;
48 1         6 Docker::Registry::Auth::None->new;
49             };
50              
51 5     5   7197 use JSON::MaybeXS qw//;
  5         23477  
  5         953  
52             has _json => (is => 'ro', default => sub {
53             JSON::MaybeXS->new;
54             });
55             sub process_json_response {
56 14     14 0 45 my ($self, $response) = @_;
57 14 100       85 if ($response->status == 200) {
    50          
58 11         23 my $struct = eval {
59 11         184 $self->_json->decode($response->content);
60             };
61 11 50       40 if ($@) {
62 0         0 Docker::Registry::Exception->throw({ message => $@ });
63             }
64 11         44 my $pagination = $self->_parse_pagination_header($response);
65 11         285 return { %$struct, %$pagination };
66             } elsif ($response->status == 401) {
67 0         0 Docker::Registry::Exception::Unauthorized->throw({
68             message => $response->content,
69             status => $response->status,
70             });
71             } else {
72 3         69 Docker::Registry::Exception::HTTP->throw({
73             message => $response->content,
74             status => $response->status
75             });
76             }
77             }
78              
79 5     5   39 use URI;
  5         12  
  5         2152  
80             sub _parse_pagination_header {
81 11     11   30 my ($self, $response) = @_;
82              
83 11 100       240 return {} unless($response->headers->{link});
84              
85 1         25 my ($link) = ($response->headers->{link} =~ /<([^>]*)>/);
86 1         15 my $url = URI->new($link);
87 1         75 my %url_params = $url->query_form;
88              
89 1         75 return {last => $url_params{last}};
90             }
91              
92             sub repositories {
93 6     6 0 14857 my $self = shift;
94             # Inputs n, last
95             #
96             # GET /v2/_catalog
97             #
98             # Header next
99             # {
100             # "repositories": [
101             # ,
102             # ...
103             # ]
104             # }
105 6         14 my $call_class = 'Docker::Registry::Call::Repositories';
106 6         98 my $call = $call_class->new({ @_ });
107              
108 6         6028 my $request = $self->request_builder->build_request($call);
109              
110 6         16 my $scope = 'registry:catalog:*';
111 6         129 $request = $self->auth->authorize($request, $scope);
112 6         53 my $response = $self->caller->send_request($request);
113 6         8587 my $result_class = 'Docker::Registry::Result::Repositories';
114 6         32 my $result = $result_class->new($self->process_json_response($response));
115 3         6237 return $result;
116             }
117              
118             sub repository_tags {
119 8     8 0 11416 my $self = shift;
120              
121             # n, last
122             #GET /v2/$repository/tags/list
123             #
124             #{"name":"$repository","tags":["2017.09","latest"]}
125 8         21 my $call_class = 'Docker::Registry::Call::RepositoryTags';
126 8         140 my $call = $call_class->new({ @_ });
127              
128 8         13194 my $request = $self->request_builder->build_request($call);
129              
130 8         77 my $scope = sprintf 'repository:%s:%s', $call->repository, 'pull';
131 8         175 $request = $self->auth->authorize($request, $scope);
132 8         56 my $response = $self->caller->send_request($request);
133 8         3342 my $result_class = 'Docker::Registry::Result::RepositoryTags';
134 8         38 my $result = $result_class->new($self->process_json_response($response));
135 8         14115 return $result;
136             }
137              
138             sub is_registry {
139 0     0 0   my $self = shift;
140             # GET /v2
141             # if (200 or 401) and (header.Docker-Distribution-API-Version eq 'registry/2.0')
142             }
143              
144             # Actionable failure conditions, covered in detail in their relevant sections,
145             # are reported as part of 4xx responses, in a json response body. One or more
146             # errors will be returned in the following format:
147             # {
148             # "errors:" [{
149             # "code": ,
150             # "message": ,
151             # "detail":
152             # },
153             # ...
154             # ]
155             # }
156              
157             # ECR: returns 401 error body as "Not Authorized"
158       0 0   sub process_error {
159            
160             }
161              
162       0 0   sub request {
163            
164             }
165             1;