File Coverage

blib/lib/Facebook/Graph.pm
Criterion Covered Total %
statement 37 39 94.8
branch n/a
condition n/a
subroutine 13 13 100.0
pod n/a
total 50 52 96.1


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