File Coverage

blib/lib/GraphQL/Type/List.pm
Criterion Covered Total %
statement 144 215 66.9
branch 46 150 30.6
condition 3 3 100.0
subroutine 25 25 100.0
pod 5 5 100.0
total 223 398 56.0


line stmt bran cond sub pod time code
1             package GraphQL::Type::List;
2              
3 18     18   463 use 5.014;
  18         62  
4 18     18   95 use strict;
  18         38  
  18         361  
5 18     18   84 use warnings;
  18         33  
  18         398  
6 18     18   83 use Moo;
  18         40  
  18         159  
7 18     18   6770 use Types::Standard -all;
  18         41  
  18         144  
8 18     18   798987 use GraphQL::Type::Library -all;
  18         49  
  18         210  
9 18     18   237442 use GraphQL::MaybeTypeCheck;
  18         48  
  18         190  
10 18     18   904 use GraphQL::Debug qw(_debug);
  18         39  
  18         2005  
11             extends qw(GraphQL::Type);
12             with qw(GraphQL::Role::Nullable);
13              
14             # A-ha
15             my @TAKE_ON_ME = qw(
16             GraphQL::Role::Input
17             GraphQL::Role::Output
18             );
19              
20             our $VERSION = '0.02';
21 18     18   120 use constant DEBUG => $ENV{GRAPHQL_DEBUG};
  18         52  
  18         4115  
22              
23             =head1 NAME
24              
25             GraphQL::Type::List - GraphQL type that is a list of another type
26              
27             =head1 SYNOPSIS
28              
29             use GraphQL::Type::List;
30             my $type = GraphQL::Type::List->new(of => $other_type);
31              
32             =head1 DESCRIPTION
33              
34             Type that is a wrapper for the type it is a list of. If the wrapped type
35             has any of these roles, it will assume them: L<GraphQL::Role::Input>,
36             L<GraphQL::Role::Output>. It is always L<GraphQL::Role::Nullable>.
37              
38             =head1 ATTRIBUTES
39              
40             =head2 of
41              
42             GraphQL type object of which this is a list.
43              
44             =cut
45              
46             has of => (
47             is => 'ro',
48             isa => InstanceOf['GraphQL::Type'],
49             required => 1,
50             handles => [ qw(name) ],
51             );
52              
53             =head1 METHODS
54              
55             =head2 BUILD
56              
57             L<Moo> method that applies the relevant roles.
58              
59             =cut
60              
61             sub BUILD {
62 9     124 1 476 my ($self, $args) = @_;
63 9         179 my $of = $self->of;
64 180         494 Role::Tiny->apply_roles_to_object($self, grep $of->DOES($_), @TAKE_ON_ME);
65             }
66              
67             =head2 to_string
68              
69             Part of serialisation.
70              
71             =cut
72              
73             has to_string => (is => 'lazy', isa => Str, init_arg => undef, builder => sub {
74 180     9   403 my ($self) = @_;
75 180         314 '[' . $self->of->to_string . ']';
76             });
77              
78             =head2 is_valid
79              
80             True if given Perl array-ref is a valid value for this type.
81              
82             =cut
83              
84 3 50   3 1 10475 method is_valid(Any $item) :ReturnType(Bool) {
  3 50       10  
  3 50       5  
  3         8  
  3         13  
  3         60  
85 3 50       10 return 1 if !defined $item;
86 3         19 my $of = $self->of;
87 3 50       6 return if grep !$of->is_valid($_), @{ $self->uplift($item) };
  3         10  
88 3         33 1;
89 18     18   3888 }
  18         45  
  18         151  
90              
91             =head2 uplift
92              
93             Turn given Perl entity into valid value for this type if possible.
94             Mainly to promote single value into a list if type dictates.
95              
96             =cut
97              
98             # This is a crime against God. graphql-js does it however.
99 38 50   38 1 127 method uplift(Any $item) :ReturnType(Any) {
  38 50       97  
  38 50       73  
  38         83  
  38         105  
  38         249  
100 38 100 100     279 return $item if ref($item) eq 'ARRAY' or !defined $item;
101 7         30 [ $item ];
102 18     18   9722 }
  18         40  
  18         138  
103              
104 27 50   27 1 91 method graphql_to_perl(Any $item) :ReturnType(Maybe[ArrayRef]) {
  27 50       90  
  27 50       59  
  27         58  
  27         73  
  27         190  
105 27 100       113 return $item if !defined $item;
106 18         81 $item = $self->uplift($item);
107 18         79 my $of = $self->of;
108 18         39 my $i = 0;
109 18         33 my @errors;
110             my @values = map {
111 18         46 my $value = eval { $of->graphql_to_perl($_) };
  28         56  
  28         129  
112 28 100       169 push @errors, qq{In element #$i: $@} if $@;
113 28         60 $i++;
114 28         76 $value;
115             } @$item;
116 18 100       66 die @errors if @errors;
117 16         70 \@values;
118 18     18   9431 }
  18         41  
  18         87  
119              
120 2 50   2 1 6 method perl_to_graphql(Any $item) :ReturnType(Maybe[ArrayRef]) {
  2 50       19  
  2 50       4  
  2         4  
  2         7  
  2         15  
121 2 50       4 return $item if !defined $item;
122 2         6 $item = $self->uplift($item);
123 2         5 my $of = $self->of;
124 2         12 my $i = 0;
125 2         5 my @errors;
126             my @values = map {
127 2         5 my $value = eval { $of->perl_to_graphql($_) };
  2         3  
  2         8  
128 2 50       6 push @errors, qq{In element #$i: $@} if $@;
129 2         3 $i++;
130 2         7 $value;
131             } @$item;
132 2 50       5 die @errors if @errors;
133 2         9 \@values;
134 18     18   9485 }
  18         45  
  18         80  
135              
136             method _complete_value(
137             HashRef $context,
138             ArrayRef[HashRef] $nodes,
139             HashRef $info,
140             ArrayRef $path,
141             ArrayRef $result,
142 180 50   180   456 ) {
  180 50       430  
  180 50       1244  
  180 50       2217  
  180 50       1293  
  180 100       1415  
  180 0       1148  
  180         527  
  180         298  
  180         307  
143             # TODO promise stuff
144 180         769 my $item_type = $self->of;
145 176         321 my $index = 0;
146 176         621 my @errors;
147 0         0 my @completed = map GraphQL::Execution::_complete_value_catching_error(
148             $context,
149             $item_type,
150             $nodes,
151             $info,
152             [ @$path, $index++ ],
153             $_,
154             ), @$result;
155 0         0 DEBUG and _debug("List._complete_value(done)", \@completed);
156 0 0       0 (grep is_Promise($_), @completed)
157             ? _promise_for_list($context, \@completed)
158             : _merge_list(\@completed);
159             }
160              
161             fun _merge_list(
162             ArrayRef[ExecutionPartialResult] $list,
163 172 50   172   1316 ) :ReturnType(ExecutionPartialResult) {
  172 50       387  
  172 0       361  
  172 0       488  
  0 0       0  
  0 0       0  
  0 0       0  
  0 0       0  
  0 0       0  
  0 0       0  
  0 0       0  
  0 0       0  
  0 0       0  
  0 0       0  
  0 50       0  
  172         1080  
164 0         0 DEBUG and _debug("List._merge_list", $list);
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  172         3279  
165 0 50       0 my @errors = map @{ $_->{errors} || [] }, @$list;
  0 0       0  
  0 0       0  
  0 0       0  
  0 0       0  
  0 0       0  
  0 0       0  
  0 0       0  
  0 0       0  
  0 0       0  
  0 0       0  
  0 0       0  
  0 100       0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  172         272  
  172         361  
166 18         10851 my @data = map $_->{data}, @$list;
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  344         1044  
167 18         45 DEBUG and _debug("List._merge_list(after)", \@data, \@errors);
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  172         496  
168 18 50       78 +{ data => \@data, @errors ? (errors => \@errors) : () };
  0 0       0  
  0 0       0  
  0 0       0  
  0 0       0  
  0 0       0  
  0 0       0  
  0 0       0  
  0 0       0  
  0 0       0  
  172 0       275  
    0          
    100          
169 18     18   34037 }
  18         41  
  18         81  
170              
171             fun _promise_for_list(
172             HashRef $context,
173             ArrayRef $list,
174 12 50   12   50 ) :ReturnType(Promise) {
  12 50       146  
  12 50       94  
  12 50       18  
  12         51  
  12         59  
175 8         1844 DEBUG and _debug('_promise_for_list', $list);
176             die "Given a promise in list but no PromiseCode given\n"
177 8 50       54 if !$context->{promise_code};
178             return $context->{promise_code}{all}->(@$list)->then(sub {
179 124     8   404 DEBUG and _debug('_promise_for_list(all)', @_);
180 124         569 _merge_list([ map $_->[0], @_ ]);
181 124         56409 });
182 12     18   298 }
  12         38  
  12         39  
183              
184             =head2 name
185              
186             The C<name> of the type this object is a list of.
187              
188             =cut
189              
190             __PACKAGE__->meta->make_immutable();
191              
192             1;