File Coverage

blib/lib/GraphQL/Client.pm
Criterion Covered Total %
statement 96 97 98.9
branch 24 30 80.0
condition 12 18 66.6
subroutine 26 26 100.0
pod 6 6 100.0
total 164 177 92.6


line stmt bran cond sub pod time code
1             package GraphQL::Client;
2             # ABSTRACT: A GraphQL client
3              
4 2     2   65013 use warnings;
  2         11  
  2         50  
5 2     2   8 use strict;
  2         3  
  2         34  
6              
7 2     2   652 use Module::Load qw(load);
  2         1633  
  2         9  
8 2     2   114 use Scalar::Util qw(reftype);
  2         3  
  2         71  
9 2     2   639 use namespace::clean;
  2         22657  
  2         9  
10              
11             our $VERSION = '0.605'; # VERSION
12              
13 1     1   5 sub _croak { require Carp; goto &Carp::croak }
  1         20  
14 2     2   7 sub _throw { GraphQL::Client::Error->throw(@_) }
15              
16             sub new {
17 6     6 1 6532 my $class = shift;
18 6         22 bless {@_}, $class;
19             }
20              
21             sub execute {
22 13     13 1 5362 my $self = shift;
23 13         22 my ($query, $variables, $operation_name, $options) = @_;
24              
25 13 100 100     55 if ((reftype($operation_name) || '') eq 'HASH') {
26 1         2 $options = $operation_name;
27 1         13 $operation_name = undef;
28             }
29              
30 13 100 66     45 my $request = {
    100          
31             query => $query,
32             ($variables && %$variables) ? (variables => $variables) : (),
33             $operation_name ? (operationName => $operation_name) : (),
34             };
35              
36 13         24 return $self->_handle_result($self->transport->execute($request, $options));
37             }
38              
39             sub _handle_result {
40 13     13   106 my $self = shift;
41 13         19 my ($result) = @_;
42              
43             my $handle_result = sub {
44 13     13   21 my $result = shift;
45 13         16 my $resp = $result->{response};
46 13 100       24 if (my $exception = $result->{error}) {
47 2         3 unshift @{$resp->{errors}}, {
  2         14  
48             message => "$exception",
49             };
50             }
51 13 100       28 if ($self->unpack) {
52 4 100       8 if ($resp->{errors}) {
53             _throw $resp->{errors}[0]{message}, {
54             type => 'graphql',
55             response => $resp,
56             details => $result->{details},
57 2         8 };
58             }
59 2         6 return $resp->{data};
60             }
61 9         42 return $resp;
62 13         37 };
63              
64 13 100       19 if (eval { $result->isa('Future') }) {
  13         66  
65             return $result->transform(
66             done => sub {
67 2     2   124 my $result = shift;
68 2         3 my $resp = eval { $handle_result->($result) };
  2         4  
69 2 50       5 if (my $err = $@) {
70 0         0 Future::Exception->throw("$err", $err->{type}, $err->{response}, $err->{details});
71             }
72 2         4 return $resp;
73             },
74 2         11 );
75             }
76             else {
77 11         19 return $handle_result->($result);
78             }
79             }
80              
81             sub url {
82 1     1 1 2 my $self = shift;
83 1         2 $self->{url};
84             }
85              
86             sub transport_class {
87 4     4 1 6 my $self = shift;
88 4         9 $self->{transport_class};
89             }
90              
91             sub transport {
92 16     16 1 53 my $self = shift;
93 16   66     55 $self->{transport} //= do {
94 3         6 my $class = $self->_autodetermine_transport_class;
95 3         4 eval { load $class };
  3         7  
96 3 100 66     257 if ((my $err = $@) || !$class->can('execute')) {
97 1   33     5 $err ||= "Loaded $class, but it doesn't look like a proper transport.\n";
98 1 50       3 warn $err if $ENV{GRAPHQL_CLIENT_DEBUG};
99 1         4 _croak "Failed to load transport for \"${class}\"";
100             }
101 2         8 $class->new(%$self);
102             };
103             }
104              
105             sub unpack {
106 13     13 1 14 my $self = shift;
107 13   100     33 $self->{unpack} //= 0;
108             }
109              
110             sub _url_protocol {
111 1     1   2 my $self = shift;
112              
113 1         2 my $url = $self->url;
114 1         5 my ($protocol) = $url =~ /^([^+:]+)/;
115              
116 1         3 return $protocol;
117             }
118              
119             sub _autodetermine_transport_class {
120 3     3   3 my $self = shift;
121              
122 3         6 my $class = $self->transport_class;
123 3 100       13 return _expand_class($class) if $class;
124              
125 1         2 my $protocol = $self->_url_protocol;
126 1 50       2 _croak 'Failed to determine transport from URL' if !$protocol;
127              
128 1         10 $class = lc($protocol);
129 1         3 $class =~ s/[^a-z]/_/g;
130              
131 1         2 return _expand_class($class);
132             }
133              
134             sub _expand_class {
135 3     3   7 my $class = shift;
136 3 50       10 $class = "GraphQL::Client::$class" unless $class =~ s/^\+//;
137 3         6 $class;
138             }
139              
140             {
141             package GraphQL::Client::Error;
142              
143 2     2   1668 use warnings;
  2         17  
  2         55  
144 2     2   9 use strict;
  2         2  
  2         67  
145              
146 2     2   14 use overload '""' => \&error, fallback => 1;
  2         3  
  2         13  
147              
148 2 50 50 2   3 sub new { bless {%{$_[2] || {}}, error => $_[1] || 'Something happened'}, $_[0] }
  2         26  
149              
150 3     3   338 sub error { "$_[0]->{error}" }
151 1     1   4 sub type { "$_[0]->{type}" }
152              
153             sub throw {
154 2     2   3 my $self = shift;
155 2 50       4 die $self if ref $self;
156 2         13 die $self->new(@_);
157             }
158             }
159              
160             1;
161              
162             __END__