File Coverage

blib/lib/Facebook/Graph.pm
Criterion Covered Total %
statement 77 165 46.6
branch 4 56 7.1
condition 0 6 0.0
subroutine 23 38 60.5
pod 19 19 100.0
total 123 284 43.3


line stmt bran cond sub pod time code
1             package Facebook::Graph;
2             $Facebook::Graph::VERSION = '1.1203';
3 4     4   4234 use Moo;
  4         37225  
  4         23  
4 4     4   7069 use MIME::Base64::URLSafe;
  4         5044  
  4         192  
5 4     4   950 use JSON;
  4         11855  
  4         30  
6 4     4   2199 use Facebook::Graph::AccessToken;
  4         14  
  4         136  
7 4     4   1363 use Facebook::Graph::Authorize;
  4         12  
  4         130  
8 4     4   1474 use Facebook::Graph::Query;
  4         11  
  4         125  
9 4     4   1500 use Facebook::Graph::Picture;
  4         11  
  4         102  
10 4     4   25 use Facebook::Graph::Request;
  4         7  
  4         67  
11 4     4   1493 use Facebook::Graph::Publish::Post;
  4         12  
  4         116  
12 4     4   1332 use Facebook::Graph::Publish::Photo;
  4         10  
  4         106  
13 4     4   1417 use Facebook::Graph::Publish::Checkin;
  4         10  
  4         99  
14 4     4   1452 use Facebook::Graph::Publish::Comment;
  4         11  
  4         93  
15 4     4   1413 use Facebook::Graph::Publish::RSVPMaybe;
  4         10  
  4         93  
16 4     4   1305 use Facebook::Graph::Publish::RSVPAttending;
  4         27  
  4         245  
17 4     4   1555 use Facebook::Graph::Publish::RSVPDeclined;
  4         11  
  4         97  
18 4     4   1373 use Facebook::Graph::Publish::PageTab;
  4         11  
  4         84  
19 4     4   1340 use Facebook::Graph::BatchRequests;
  4         13  
  4         101  
20 4     4   1601 use Facebook::Graph::Page::Feed;
  4         14  
  4         103  
21 4     4   27 use Ouch;
  4         10  
  4         4369  
22              
23             has app_id => (
24             is => 'ro',
25             );
26              
27             has secret => (
28             is => 'ro',
29             predicate => 'has_secret',
30             );
31              
32             has postback => (
33             is => 'rw',
34             );
35              
36             has access_token => (
37             is => 'rw',
38             predicate => 'has_access_token',
39             );
40              
41             sub parse_signed_request {
42 1     1 1 2016 my ($self, $signed_request) = @_;
43 1         410 require Digest::SHA;
44 1         2125 my ($encoded_sig, $payload) = split(/\./, $signed_request);
45              
46 1         6 my $sig = urlsafe_b64decode($encoded_sig);
47 1         25 my $data = JSON->new->decode(urlsafe_b64decode($payload));
48              
49 1 50       28 if (uc($data->{'algorithm'}) ne "HMAC-SHA256") {
50 0         0 ouch '500', 'Unknown algorithm. Expected HMAC-SHA256';
51             }
52              
53 1         25 my $expected_sig = Digest::SHA::hmac_sha256($payload, $self->secret);
54 1 50       4 if ($sig ne $expected_sig) {
55 0         0 ouch '500', 'Bad Signed JSON signature!';
56             }
57 1         23 return $data;
58             }
59              
60             sub request_access_token {
61 2     2 1 5857 my ($self, $code) = @_;
62 2         42 my $token = Facebook::Graph::AccessToken->new(
63             code => $code,
64             postback => $self->postback,
65             secret => $self->secret,
66             app_id => $self->app_id,
67             )->request;
68 2         1525 $self->access_token($token->token);
69 2         4567 return $token;
70             }
71              
72             sub request_extended_access_token {
73 0     0 1 0 my ($self, $access_token) = @_;
74              
75 0 0 0     0 die "request_extended_access_token requires an access_token" unless $access_token or $self->has_access_token;
76 0 0       0 $access_token = $access_token ? $access_token : $self->access_token;
77              
78 0         0 my $token = Facebook::Graph::AccessToken->new(
79             access_token => $access_token,
80             postback => $self->postback,
81             secret => $self->secret,
82             app_id => $self->app_id,
83             )->request;
84 0         0 $self->access_token($token->token);
85 0         0 return $token;
86             }
87              
88             sub convert_sessions {
89 0     0 1 0 my ($self, $sessions) = @_;
90 0         0 return Facebook::Graph::Session->new(
91             secret => $self->secret,
92             app_id => $self->app_id,
93             sessions => $sessions,
94             )
95             ->request
96             ->as_hashref;
97             }
98              
99             sub authorize {
100 0     0 1 0 my ($self) = @_;
101 0         0 return Facebook::Graph::Authorize->new(
102             app_id => $self->app_id,
103             postback => $self->postback,
104             );
105             }
106              
107             sub fetch {
108 0     0 1 0 my ($self, $object_name, %params) = @_;
109 0         0 return $self->query->find($object_name, %params)->request->as_hashref;
110             }
111              
112             sub request {
113 0     0 1 0 my ($self, $uri) = @_;
114 0         0 return Facebook::Graph::Request->new->get($uri);
115             }
116              
117             sub query {
118 1     1 1 2394 my ($self, %params) = @_;
119 1 50       6 if ($self->has_access_token) {
120 0         0 $params{access_token} = $self->access_token;
121             }
122 1 50       5 if ($self->has_secret) {
123 0         0 $params{secret} = $self->secret;
124             }
125 1         7 return Facebook::Graph::Query->new(%params);
126             }
127              
128             sub batch_requests {
129 0     0 1 0 my ($self, %params) = @_;
130 0         0 $params{access_token} = $self->access_token;
131 0         0 return Facebook::Graph::BatchRequests->new(%params);
132             }
133              
134             sub picture {
135 1     1 1 1990 my ($self, $object_name, %params) = @_;
136 1         2 $params{object_name} = $object_name;
137 1         9 return Facebook::Graph::Picture->new( %params );
138             }
139              
140             sub add_post {
141 0     0 1   my ($self, $object_name, %params) = @_;
142 0 0         if ($object_name) {
143 0           $params{object_name} = $object_name;
144             }
145 0 0         if ($self->has_access_token) {
146 0           $params{access_token} = $self->access_token;
147             }
148 0 0         if ($self->has_secret) {
149 0           $params{secret} = $self->secret;
150             }
151 0           return Facebook::Graph::Publish::Post->new( %params );
152             }
153              
154              
155             sub add_page_feed {
156 0     0 1   my ($self, %params) = @_;
157 0 0         if ($self->has_access_token) {
158 0           $params{access_token} = $self->access_token;
159             }
160 0 0         if ($self->has_secret) {
161 0           $params{secret} = $self->secret;
162             }
163 0           return Facebook::Graph::Page::Feed->new( %params );
164             };
165              
166              
167             sub add_photo {
168 0     0 1   my ($self, $object_name, %params) = @_;
169 0 0         if ($object_name) {
170 0           $params{object_name} = $object_name;
171             }
172 0 0         if ($self->has_access_token) {
173 0           $params{access_token} = $self->access_token;
174             }
175 0 0         if ($self->has_secret) {
176 0           $params{secret} = $self->secret;
177             }
178 0           return Facebook::Graph::Publish::Photo->new( %params );
179             }
180              
181             sub add_checkin {
182 0     0 1   my ($self, $object_name, %params) = @_;
183 0 0         if ($object_name) {
184 0           $params{object_name} = $object_name;
185             }
186 0 0         if ($self->has_access_token) {
187 0           $params{access_token} = $self->access_token;
188             }
189 0 0         if ($self->has_secret) {
190 0           $params{secret} = $self->secret;
191             }
192 0           return Facebook::Graph::Publish::Checkin->new( %params );
193             }
194              
195             sub add_comment {
196 0     0 1   my ($self, $object_name, %params) = @_;
197 0           $params{object_name} = $object_name;
198 0 0         if ($self->has_access_token) {
199 0           $params{access_token} = $self->access_token;
200             }
201 0 0         if ($self->has_secret) {
202 0           $params{secret} = $self->secret;
203             }
204 0           return Facebook::Graph::Publish::Comment->new( %params );
205             }
206              
207             sub add_page_tab {
208 0     0 1   my ($self, $object_name, $app_id, %params) = @_;
209              
210 0 0 0       die "page_id and app_id are required" unless $object_name and $app_id;
211              
212 0           $params{object_name} = $object_name;
213              
214 0 0         if ($self->has_access_token) {
215 0           $params{access_token} = $self->access_token;
216             }
217              
218 0 0         if ($self->has_secret) {
219 0           $params{secret} = $self->secret;
220             }
221              
222 0           $params{app_id} = $app_id;
223              
224 0           return Facebook::Graph::Publish::PageTab->new( %params );
225             }
226              
227              
228              
229             sub rsvp_maybe {
230 0     0 1   my ($self, $object_name, %params) = @_;
231 0           $params{object_name} = $object_name;
232 0 0         if ($self->has_access_token) {
233 0           $params{access_token} = $self->access_token;
234             }
235 0 0         if ($self->has_secret) {
236 0           $params{secret} = $self->secret;
237             }
238 0           return Facebook::Graph::Publish::RSVPMaybe->new( %params );
239             }
240              
241             sub rsvp_attending {
242 0     0 1   my ($self, $object_name, %params) = @_;
243 0           $params{object_name} = $object_name;
244 0 0         if ($self->has_access_token) {
245 0           $params{access_token} = $self->access_token;
246             }
247 0 0         if ($self->has_secret) {
248 0           $params{secret} = $self->secret;
249             }
250 0           return Facebook::Graph::Publish::RSVPAttending->new( %params );
251             }
252              
253             sub rsvp_declined {
254 0     0 1   my ($self, $object_name, %params) = @_;
255 0           $params{object_name} = $object_name;
256 0 0         if ($self->has_access_token) {
257 0           $params{access_token} = $self->access_token;
258             }
259 0 0         if ($self->has_secret) {
260 0           $params{secret} = $self->secret;
261             }
262 0           return Facebook::Graph::Publish::RSVPDeclined->new( %params );
263             }
264              
265              
266             1;
267              
268             =head1 NAME
269              
270             Facebook::Graph - A fast and easy way to integrate your apps with Facebook.
271              
272             =head1 VERSION
273              
274             version 1.1203
275              
276             =head1 SYNOPSIS
277              
278             my $fb = Facebook::Graph->new;
279             $fb->access_token($token);
280             my $sarah_bownds = $fb->fetch('767598108');
281             my $perl_page = $fb->fetch('16665510298');
282              
283             Or better yet:
284              
285             my $sarah_bownds = $fb->query
286             ->find('767598108')
287             ->include_metadata
288             ->select_fields(qw( id name picture ))
289             ->request
290             ->as_hashref;
291              
292             my $sarahs_picture_uri = $fb->picture('sarahbownds')->get_large->uri_as_string;
293              
294             You can also do asynchronous calls like this:
295              
296             my $sarah = $fb->query->find('767598108'); # making request in background here
297             # ... do stuff here ...
298             my $hashref = $sarah->as_hashref; # handling the response here
299              
300              
301             Or fetching a response from a URI you already have:
302              
303             my $hashref = $fb->request('https://graph.facebook.com/16665510298')->as_hashref;
304              
305              
306             =head2 Building A Privileged App
307              
308             my $fb = Facebook::Graph->new(
309             app_id => $facebook_application_id,
310             secret => $facebook_application_secret,
311             postback => 'https://www.yourapplication.com/facebook/oauth/postback',
312             );
313              
314             Get the user to authorize your app (only needed if you want to fetch non-public information or publish stuff):
315              
316             my $uri = $fb
317             ->authorize
318             ->extend_permissions(qw(offline_access publish_stream))
319             ->uri_as_string;
320              
321             # redirect the user's browser to $uri
322              
323             Handle the Facebook authorization code postback:
324              
325             my $q = Plack::Request->new($env);
326             $fb->request_access_token($q->query_param('code'));
327              
328             #now retrieve extended access token
329             $fb->request_extended_access_token; #extended access token now in $fb->access_token
330              
331             Or if you already had the access token:
332              
333             $fb->access_token($token);
334             $fb->request_extended_access_token;
335              
336             Or simply:
337              
338             $fb->request_extended_access_token($token);
339              
340             Get some info:
341              
342             my $user = $fb->fetch('me');
343             my $friends = $fb->fetch('me/friends');
344             my $perl_page = $fb->fetch('16665510298');
345              
346             Use a different version of the API:
347              
348             my $user = $fb->fetch('me', api_version => 'v2.5');
349              
350             =head1 DESCRIPTION
351              
352             This is a Perl interface to the Facebook Graph API L<http://developers.facebook.com/docs/api>. With this module you can currently query public Facebook data, query privileged Facebook data, and build a privileged Facebook application. See the TODO for all that this module cannot yet do.
353              
354             For example code, see L<Facebook::Graph::Cookbook>.
355              
356             B<WARNING:> The work on this module has only just begun because the Graph API itself isn't very new, and I'm only working on it as I have some tuits. Therefore things are potentially subject to change drastically with each release.
357              
358              
359             =head1 METHODS
360              
361             =head2 new ( [ params ] )
362              
363             The constructor.
364              
365             =head3 params
366              
367             A hash of base parameters, just so you don't have to pass them around. If you only want to do public queries then these params are not needed.
368              
369             =over
370              
371             =item access_token
372              
373             An access token string used to make Facebook requests as a privileged user. Required if you want to make privileged queries or perform privileged actions on Facebook objects.
374              
375             =item app_id
376              
377             The application id that you get from Facebook after registering (L<http://developers.facebook.com/setup/>) your application on their site. Required if you'll be calling the C<request_access_token>, C<convert_sessions>, or C<authorize> methods.
378              
379             =item secret
380              
381             The application secret that you get from Facebook after registering your application. Required if you'll be calling the C<request_access_token> or C<convert_sessions> methods.
382              
383             =item postback
384              
385             The URI that Facebook should post your authorization code back to. Required if you'll be calling the C<request_access_token> or C<authorize> methods.
386              
387             B<NOTE:> It must be a sub URI of the URI that you put in the Application Settings > Connect > Connect URL field of your application's profile on Facebook.
388              
389             =back
390              
391              
392             =head2 authorize ( )
393              
394             Creates a L<Facebook::Graph::Authorize> object, which can be used to get permissions from a user for your application.
395              
396              
397             =head2 request_access_token ( code )
398              
399             Creates a L<Facebook::Graph::AccessToken> object and fetches an access token from Facebook, which will allow everything you do with Facebook::Graph to work within user privileges rather than through the public interface. Returns a L<Facebook::Graph::AccessToken::Response> object, and also sets the C<access_token> property in the Facebook::Graph object.
400              
401             =head2 request_extended_access_token ( access_token )
402              
403             Note: access_token is optional. Creates a L<Facebook::Graph::AccessToken> object and fetches an (https://developers.facebook.com/docs/facebook-login/access-tokens/#extending) extended access token from Facebook.
404             This method accepts an optional access token. If you have called C<request_access_token> already on the Facebook::Graph object and C<access_token> is set, then you do not have to pass
405             in an access token. However, if you have an access token stored from a previous object, you will need to pass it in.
406              
407             =head3 code
408              
409             An authorization code string that you should have gotten by going through the C<authorize> process.
410              
411              
412             =head2 query ( )
413              
414             Creates a L<Facebook::Graph::Query> object, which can be used to fetch and search data from Facebook.
415              
416             =head2 batch_requests ( uri )
417              
418             Creates a L<Facebook::Graph::BatchRequests> object, which can be used to send multiple requests in a single HTTP request.
419              
420             =head2 request ( uri )
421              
422             Fetch a Facebook::Graph URI you already have.
423              
424             =head3 uri
425              
426             The URI to fetch. For example: https://graph.facebook.com/amazon
427              
428             =head2 fetch ( id )
429              
430             Returns a hash reference of an object from facebook. A quick way to grab an object from Facebook. These two statements are identical:
431              
432             my $sarah = $fb->fetch('767598108');
433              
434             my $sarah = $fb->query->find('767598108')->request->as_hashref;
435              
436             =head3 id
437              
438             An object id like C<16665510298> for the Perl page.
439              
440             =head2 picture ( id )
441              
442             Returns a L<Facebook::Graph::Picture> object, which can be used to generate the URLs of the pictures of any object on Facebook.
443              
444             =head3 id
445              
446             An object id like C<16665510298> for the Perl page.
447              
448              
449              
450             =head2 add_post ( [ id ] )
451              
452             Creates a L<Facebook::Graph::Publish::Post> object, which can be used to publish data to a user's feed/wall.
453              
454             =head2 add_page_feed ( )
455              
456             Creates a L<Facebook::Graph::Page::Feed> object, which can be used to add a post to a Facebook page.
457              
458             =head2 add_photo ( [ id ] )
459              
460             Creates a L<Facebook::Graph::Publish::Photo> object, which can be used to publish a photo to a user's feed/wall.
461              
462             =head3 id
463              
464             Optionally provide an object id to place it on. Requires that you have administrative access to that page/object.
465              
466              
467             =head2 add_checkin ( [ id ] )
468              
469             Creates a L<Facebook::Graph::Publish::Checkin> object, which can be used to publish a checkin to a location.
470              
471             =head3 id
472              
473             Optionally provide an user id to check in. Requires that you have administrative access to that user.
474              
475              
476             =head2 add_comment ( id )
477              
478             Creates a L<Facebook::Graph::Publish::Comment> object that you can use to comment on a note.
479              
480             =head3 id
481              
482             The id of the post you want to comment on.
483              
484              
485             =head2 add_page_tab ( page_id, app_id )
486              
487             Creates a L<Facebook::Graph::Publish::PageTab> object, which can be used to publish an app as a page tab.
488              
489             =head3 id
490              
491             Optionally provide an object id to place it on. Requires that you have administrative access to that page/object.
492              
493              
494              
495             =head2 rsvp_maybe ( id )
496              
497             RSVP as 'maybe' to an event.
498              
499             =head3 id
500              
501             The id of an event.
502              
503             =head2 rsvp_attending ( id )
504              
505             RSVP as 'attending' to an event.
506              
507             =head3 id
508              
509             The id of an event.
510              
511             =head2 rsvp_declined ( id )
512              
513             RSVP as 'declined' to an event.
514              
515             =head3 id
516              
517             The id of an event.
518              
519              
520              
521             =head2 convert_sessions ( sessions )
522              
523             A utility method to convert old sessions into access tokens that can be used with the Graph API. Returns an array reference of hash references of access tokens.
524              
525             [
526             {
527             "access_token": "xxxxxxxxxxxxxxxxxxxxxxxxxxxxx",
528             "expires": 1271649600,
529             },
530             ...
531             ]
532              
533             See also L<Facebook::Graph::Session>.
534              
535             =head3 sessions
536              
537             An array reference of session ids from the old Facebook API.
538              
539              
540             =head2 parse_signed_request ( $signed_request )
541              
542             Allows the decoding of signed requests for canvas applications to ensure data passed back from Facebook isn't tampered with. You can read more about this at L<http://developers.facebook.com/docs/authentication/canvas>.
543              
544             =head3 $signed_request
545              
546             A signature string passed from Facebook. To capture a signed request your app must be displayed within the Facebook canvas page and then you must pull the query parameter called C<signed_request> from the query string.
547              
548             B<NOTE:> To get this passed to your app you must enable it in your migration settings for your app (L<http://www.facebook.com/developers/>).
549              
550             =head1 EXCEPTIONS
551              
552             This module throws exceptions when it encounters a problem. It uses L<Ouch> to throw the exception, and the Exception typically takes 3 parts: code, message, and a data portion that is the URI that was originally requested. For example:
553              
554             eval { $fb->call_some_method };
555             if (kiss 500) {
556             say "error: ". $@->message;
557             say "uri: ".$@->data;
558             }
559             else {
560             throw $@; # rethrow the error
561             }
562              
563             =head1 CAVEATS
564              
565             The Facebook Graph API is a constantly moving target. As such some stuff that used to work, may stop working. Keep up to date with their changes here: L<https://developers.facebook.com/docs/apps/upgrading>
566              
567             If you were using any version of Facebook::Graph before 1.1000, then you may be used to doing things like creating events through this API, or using a person's username instead of their ID, or making queries without an access token. You can't do any of those things anymore, because as of the Facebook Graph v2.0 API, none of them is supported any longer.
568              
569              
570             =head1 PREREQS
571              
572             L<Moo>
573             L<JSON>
574             L<LWP::UserAgent>
575             L<LWP::Protocol::https>
576             L<URI>
577             L<DateTime>
578             L<DateTime::Format::Strptime>
579             L<MIME::Base64::URLSafe>
580             L<Ouch>
581              
582             =head2 Optional
583              
584             L<Digest::SHA> is used for signed requests. If you don't plan on using the signed request feature, then you do not need to install Digest::SHA.
585              
586             =head1 SUPPORT
587              
588             =over
589              
590             =item Repository
591              
592             L<http://github.com/rizen/Facebook-Graph>
593              
594             =item Bug Reports
595              
596             L<http://github.com/rizen/Facebook-Graph/issues>
597              
598             =back
599              
600              
601             =head1 SEE ALSO
602              
603             I highly recommend L<Facebook::OpenGraph>. I may even switch to it myself soon.
604              
605              
606             =head1 AUTHOR
607              
608             JT Smith <jt_at_plainblack_dot_com>
609              
610             =head1 LEGAL
611              
612             Facebook::Graph is Copyright 2010 - 2012 Plain Black Corporation (L<http://www.plainblack.com>) and is licensed under the same terms as Perl itself.
613              
614             =cut