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