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              
2             use 5.014;
3 18     18   408 use strict;
  18         60  
4 18     18   91 use warnings;
  18         36  
  18         317  
5 18     18   72 use Moo;
  18         32  
  18         414  
6 18     18   83 use Types::Standard -all;
  18         31  
  18         114  
7 18     18   5700 use GraphQL::Type::Library -all;
  18         41  
  18         131  
8 18     18   722284 use GraphQL::MaybeTypeCheck;
  18         47  
  18         159  
9 18     18   214607 use GraphQL::Debug qw(_debug);
  18         47  
  18         169  
10 18     18   692 extends qw(GraphQL::Type);
  18         40  
  18         1813  
11             with qw(GraphQL::Role::Nullable);
12              
13             # A-ha
14             my @TAKE_ON_ME = qw(
15             GraphQL::Role::Input
16             GraphQL::Role::Output
17             );
18              
19             our $VERSION = '0.02';
20             use constant DEBUG => $ENV{GRAPHQL_DEBUG};
21 18     18   109  
  18         35  
  18         3740  
22             =head1 NAME
23              
24             GraphQL::Type::List - GraphQL type that is a list of another type
25              
26             =head1 SYNOPSIS
27              
28             use GraphQL::Type::List;
29             my $type = GraphQL::Type::List->new(of => $other_type);
30              
31             =head1 DESCRIPTION
32              
33             Type that is a wrapper for the type it is a list of. If the wrapped type
34             has any of these roles, it will assume them: L<GraphQL::Role::Input>,
35             L<GraphQL::Role::Output>. It is always L<GraphQL::Role::Nullable>.
36              
37             =head1 ATTRIBUTES
38              
39             =head2 of
40              
41             GraphQL type object of which this is a list.
42              
43             =cut
44              
45             has of => (
46             is => 'ro',
47             isa => InstanceOf['GraphQL::Type'],
48             required => 1,
49             handles => [ qw(name) ],
50             );
51              
52             =head1 METHODS
53              
54             =head2 BUILD
55              
56             L<Moo> method that applies the relevant roles.
57              
58             =cut
59              
60             my ($self, $args) = @_;
61             my $of = $self->of;
62 9     124 1 467 Role::Tiny->apply_roles_to_object($self, grep $of->DOES($_), @TAKE_ON_ME);
63 9         159 }
64 180         412  
65             =head2 to_string
66              
67             Part of serialisation.
68              
69             =cut
70              
71             has to_string => (is => 'lazy', isa => Str, init_arg => undef, builder => sub {
72             my ($self) = @_;
73             '[' . $self->of->to_string . ']';
74 180     9   376 });
75 180         257  
76             =head2 is_valid
77              
78             True if given Perl array-ref is a valid value for this type.
79              
80             =cut
81              
82             method is_valid(Any $item) :ReturnType(Bool) {
83             return 1 if !defined $item;
84 3 50   3 1 276 my $of = $self->of;
  3 50       9  
  3 50       5  
  3         8  
  3         9  
  3         24  
85 3 50       6 return if grep !$of->is_valid($_), @{ $self->uplift($item) };
86 3         9 1;
87 3 50       4 }
  3         8  
88 3         26  
89 18     18   3578 =head2 uplift
  18         37  
  18         150  
90              
91             Turn given Perl entity into valid value for this type if possible.
92             Mainly to promote single value into a list if type dictates.
93              
94             =cut
95              
96             # This is a crime against God. graphql-js does it however.
97             method uplift(Any $item) :ReturnType(Any) {
98             return $item if ref($item) eq 'ARRAY' or !defined $item;
99 38 50   38 1 87 [ $item ];
  38 50       63  
  38 50       44  
  38         58  
  38         71  
  38         197  
100 38 100 100     164 }
101 7         26  
102 18     18   8831 method graphql_to_perl(Any $item) :ReturnType(Maybe[ArrayRef]) {
  18         39  
  18         112  
103             return $item if !defined $item;
104 27 50   27 1 56 $item = $self->uplift($item);
  27 50       58  
  27 50       33  
  27         45  
  27         54  
  27         149  
105 27 100       87 my $of = $self->of;
106 18         55 my $i = 0;
107 18         64 my @errors;
108 18         22 my @values = map {
109 18         20 my $value = eval { $of->graphql_to_perl($_) };
110             push @errors, qq{In element #$i: $@} if $@;
111 18         30 $i++;
  28         33  
  28         57  
112 28 100       127 $value;
113 28         33 } @$item;
114 28         60 die @errors if @errors;
115             \@values;
116 18 100       40 }
117 16         44  
118 18     18   8458 method perl_to_graphql(Any $item) :ReturnType(Maybe[ArrayRef]) {
  18         40  
  18         78  
119             return $item if !defined $item;
120 2 50   2 1 6 $item = $self->uplift($item);
  2 50       4  
  2 50       4  
  2         4  
  2         5  
  2         12  
121 2 50       6 my $of = $self->of;
122 2         3 my $i = 0;
123 2         15 my @errors;
124 2         4 my @values = map {
125 2         3 my $value = eval { $of->perl_to_graphql($_) };
126             push @errors, qq{In element #$i: $@} if $@;
127 2         4 $i++;
  2         4  
  2         6  
128 2 50       6 $value;
129 2         3 } @$item;
130 2         5 die @errors if @errors;
131             \@values;
132 2 50       5 }
133 2         9  
134 18     18   8409 method _complete_value(
  18         38  
  18         92  
135             HashRef $context,
136             ArrayRef[HashRef] $nodes,
137             HashRef $info,
138             ArrayRef $path,
139             ArrayRef $result,
140             ) {
141             # TODO promise stuff
142 180 50   180   356 my $item_type = $self->of;
  180 50       335  
  180 50       1199  
  180 50       2015  
  180 50       1055  
  180 100       1259  
  180 0       966  
  180         519  
  180         241  
  180         268  
143             my $index = 0;
144 180         636 my @errors;
145 176         261 my @completed = map GraphQL::Execution::_complete_value_catching_error(
146 176         488 $context,
147 0         0 $item_type,
148             $nodes,
149             $info,
150             [ @$path, $index++ ],
151             $_,
152             ), @$result;
153             DEBUG and _debug("List._complete_value(done)", \@completed);
154             (grep is_Promise($_), @completed)
155 0         0 ? _promise_for_list($context, \@completed)
156 0 0       0 : _merge_list(\@completed);
157             }
158              
159             fun _merge_list(
160             ArrayRef[ExecutionPartialResult] $list,
161             ) :ReturnType(ExecutionPartialResult) {
162             DEBUG and _debug("List._merge_list", $list);
163 172 50   172   1191 my @errors = map @{ $_->{errors} || [] }, @$list;
  172 50       332  
  172 0       285  
  172 0       442  
  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         918  
164 0         0 my @data = map $_->{data}, @$list;
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  172         2813  
165 0 50       0 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  
  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         239  
  172         299  
166 18         10430 +{ data => \@data, @errors ? (errors => \@errors) : () };
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  344         1016  
167 18         41 }
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  172         415  
168 18 50       66  
  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       220  
    0          
    100          
169 18     18   31059 fun _promise_for_list(
  18         49  
  18         95  
170             HashRef $context,
171             ArrayRef $list,
172             ) :ReturnType(Promise) {
173             DEBUG and _debug('_promise_for_list', $list);
174 12 50   12   38 die "Given a promise in list but no PromiseCode given\n"
  12 50       92  
  12 50       74  
  12 50       13  
  12         32  
  12         41  
175 8         1353 if !$context->{promise_code};
176             return $context->{promise_code}{all}->(@$list)->then(sub {
177 8 50       44 DEBUG and _debug('_promise_for_list(all)', @_);
178             _merge_list([ map $_->[0], @_ ]);
179 124     8   356 });
180 124         511 }
181 124         54343  
182 12     18   247 =head2 name
  12         31  
  12         25  
183              
184             The C<name> of the type this object is a list of.
185              
186             =cut
187              
188             __PACKAGE__->meta->make_immutable();
189              
190             1;