/
Response.pm
300 lines (211 loc) · 7.72 KB
/
Response.pm
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
package Facebook::OpenGraph::Response;
use strict;
use warnings;
use 5.008001;
use Carp qw(croak);
use JSON 2 ();
sub new {
my $class = shift;
my $args = shift || +{};
return bless +{
json => $args->{json} || JSON->new->utf8,
headers => $args->{headers},
code => $args->{code},
message => $args->{message},
content => $args->{content},
req_headers => $args->{req_headers} || q{},
req_content => $args->{req_content} || q{},
}, $class;
}
# accessors
sub code { shift->{code} }
sub headers { shift->{headers} }
sub message { shift->{message} }
sub content { shift->{content} }
sub req_headers { shift->{req_headers} }
sub req_content { shift->{req_content} }
sub json { shift->{json} }
sub etag { shift->header('etag') }
sub api_version {
my $self = shift;
return $self->header('facebook-api-version');
}
sub is_api_version_eq_or_later_than {
my ($self, $comparing_version) = @_;
croak 'comparing version is not given.' unless $comparing_version;
(my $comp_major, my $comp_minor)
= $comparing_version =~ m/ (\d+) \. (\d+ )/x;
(my $response_major, my $response_minor)
= $self->api_version =~ m/ (\d+) \. (\d+ )/x;
return $comp_major <= $response_major && $comp_minor <= $response_minor;
}
sub is_api_version_eq_or_older_than {
my ($self, $comparing_version) = @_;
croak 'comparing version is not given.' unless $comparing_version;
(my $comp_major, my $comp_minor)
= $comparing_version =~ m/ (\d+) \. (\d+ )/x;
(my $response_major, my $response_minor)
= $self->api_version =~ m/ (\d+) \. (\d+ )/x;
return $response_major <= $comp_major && $response_minor <= $comp_minor;
}
sub header {
my ($self, $key) = @_;
croak 'header field name is not given' unless $key;
$self->{header} ||= do {
my $ref = +{};
while (my ($k, $v) = splice @{ $self->headers }, 0, 2) {
$ref->{$k} = $v;
}
$ref;
};
return $self->{header}->{$key};
}
sub is_success {
my $self = shift;
# code 2XX or 304
# 304 is returned when you use ETag and the data is not changed
return substr($self->code, 0, 1) == 2 || $self->code == 304;
}
# Using the Graph API > Handling Errors
# https://developers.facebook.com/docs/graph-api/using-graph-api/
sub error_string {
my $self = shift;
# When error occurs, response should be given in a form of below:
#{
# "error": {
# "message": "Message describing the error",
# "type": "OAuthException",
# "code": 190 ,
# "error_subcode": 460
# }
#}
my $error = eval { $self->as_hashref->{error}; };
my $err_str = q{};
if ($@ || !$error) {
$err_str = $self->message;
}
else {
# sometimes error_subcode is not given
$err_str = sprintf(
'%s:%s %s:%s',
$error->{code},
$error->{error_subcode} || '-',
$error->{type},
$error->{message},
);
}
return $err_str;
}
sub as_json {
my $self = shift;
my $content = $self->content;
if ($content =~ m{\A (true|false) \z}xms) {
# On v2.0 and older version, some endpoints return plain text saying
# 'true' or 'false' to indicate result, so make it JSON formatted for
# our convinience. The key is named "success" so its format matches with
# other endpoints that return {"success": "(true|false)"}.
# From v2.1 they always return in form of {"success": "(true|false)"}.
# See https://developers.facebook.com/docs/apps/changelog#v2_1_changes
$content = sprintf('{"success" : "%s"}', $1);
};
return $content; # content is JSON formatted
}
sub as_hashref {
my $self = shift;
# just in case content is not properly formatted
my $hash_ref = eval { $self->json->decode( $self->as_json ); };
croak $@ if $@;
return $hash_ref;
}
# Indicates whether the data is modified.
# It should be used when you request with ETag.
# https://developers.facebook.com/docs/reference/ads-api/etags-reference/
sub is_modified {
my $self = shift;
my $not_modified = $self->code == 304 && $self->message eq 'Not Modified';
return !$not_modified;
}
1;
__END__
=head1 NAME
Facebook::OpenGraph::Response - Response object for Facebook::OpenGraph.
=head1 SYNOPSIS
my $res = Facebook::OpenGraph::Response->new(+{
code => $http_status_code,
message => $http_status_message,
headers => $response_headers,
content => $response_content,
req_headers => $req_headers,
req_content => $req_content,
json => JSON->new->utf8,
});
=head1 DESCRIPTION
This handles response object for Facebook::OpenGraph.
=head1 METHODS
=head2 Class Methods
=head3 C<< Facebook::OpenGraph::Response->new(\%args) >>
Creates and returns a new Facebook::OpenGraph::Response object.
I<%args> can contain...
=over 4
=item * code
HTTP status code
=item * message
HTTP status message
=item * headers
Response headers
=item * content
Response body
=item * req_headers
Stringified request headers
=item * req_content
Request content
=item * json
JSON object
=back
=head2 Instance Methods
=head3 C<< $res->code >>
Returns HTTP status code
=head3 C<< $res->message >>
Returns HTTP status message
=head3 C<< $res->content >>
Returns response body
=head3 C<< $res->req_headers >>
Returns request header. This is especially useful for debugging. You must
install later version of Furl to enable this or otherwise empty string will be
returned. Also you have to specify Furl::HTTP->new(capture_request => 1) option.
=head3 C<< $res->req_content >>
Returns request body. This is especially useful for debugging. You must install
later version of Furl to enable this or otherwise empty string will be returned.
Also you have to specify Furl::HTTP->new(capture_request => 1) option.
=head3 C<< $res->etag >>
Returns ETag value that is given as a part of response headers.
=head3 C<< $res->header($field_name) >>
Returns specified header field value.
my $res = $fb->request('GET', 'go.hagiwara');
my $etag = $res->header('etag'); # "a376a57cb3a4bd3a3c6a53fca06b0fd5badee50b"
=head3 C<< $res->api_version >>
By checking facebook-api-version header value, it returns API version that
current API call actually experienced. This may differ from the one you
specified. See
L<https://developers.facebook.com/docs/apps/changelog#v2_1>
=head3 C<< $res->is_api_version_eq_or_older_than($comparing_version) >>
Compare $comparing_version with the facebook-api-version header value and
returns TRUE when facebook-api-version is older than the given version.
=head3 C<< $res->is_api_version_eq_or_later_than($comparing_version) >>
Compare $comparing_version with the facebook-api-version header value and
returns TRUE when facebook-api-version is newer than the given version.
=head3 C<< $res->is_success >>
Returns if status is 2XX or 304. 304 is added to handle $fb->fetch_with_etag();
=head3 C<< $res->error_string >>
Returns error string.
=head3 C<< $res->as_json >>
Returns response content as JSON string. Most of the time the response content
itself is JSON formatted so it basically returns response content without doing
anything. When Graph API returns plain text just saying 'true' or 'false,' it
turns the content into JSON format like '{"success" : "(true|false)"}' so you
can handle it in the same way as other cases.
=head3 C<< $res->as_hashref >>
Returns response content in hash reference.
=head3 C<< $res->is_modified >>
Returns if target object is modified. This method is called in
$fb->fetch_with_etag().