File Coverage

blib/lib/WebService/Strava/Auth.pm
Criterion Covered Total %
statement 50 174 28.7
branch 0 74 0.0
condition 0 3 0.0
subroutine 22 30 73.3
pod n/a
total 72 281 25.6


line stmt bran cond sub pod time code
1             package WebService::Strava::Auth;
2              
3 5     5   2619 use v5.010;
  5         15  
  5         242  
4 5     5   26 use strict;
  5         6  
  5         165  
5 5     5   22 use warnings;
  5         5  
  5         181  
6 5     5   2589 use experimental 'say';
  5         17770  
  5         36  
7 5     5   1555 use Moo;
  5         17500  
  5         44  
8 5     5   3879 use Method::Signatures;
  5         56145  
  5         42  
9 5     5   5400 use Config::Tiny;
  5         4400  
  5         167  
10 5     5   3158 use LWP::Authen::OAuth2;
  5         330580  
  5         242  
11 5     5   51 use JSON qw(decode_json encode_json);
  5         18  
  5         38  
12 5     5   3594 use JSON::Parse 'valid_json';
  5         4187  
  5         338  
13 5     5   48 use Carp qw(croak);
  5         7  
  5         225  
14 5     5   28 use File::Basename;
  5         7  
  5         394  
15 5     5   2971 use File::MimeInfo::Magic;
  5         38981  
  5         378  
16 5     5   601 use Data::Dumper;
  5         4741  
  5         1400  
17              
18             # ABSTRACT: A Strava Segment Object
19              
20             our $VERSION = '0.04'; # VERSION: Generated by DZP::OurPkg:Version
21              
22              
23             # Debugging hooks in case things go weird. (Thanks @pjf)
24              
25             around BUILDARGS => sub {
26             my $orig = shift;
27             my $class = shift;
28            
29             if ($WebService::Strava::DEBUG) {
30             warn "Building task with:\n";
31             warn Dumper(\@_), "\n";
32             }
33            
34             return $class->$orig(@_);
35             };
36              
37             has 'api_base' => (is => 'ro', default => sub { 'https://www.strava.com/api/v3' });
38             has 'config_file' => ( is => 'ro', default => sub { "$ENV{HOME}/.stravarc" } );
39             has 'config' => ( is => 'rw', lazy => 1, builder => 1 );
40             has 'scope' => ( is => 'ro', default => sub { "view_private,write" } );
41             has 'auth' => ( is => 'rw', lazy => 1, builder => 1, handles => [ qw( get post ) ] );
42              
43             # TODO: Potentially allow the config to be passed through instead of loaded.
44             #has 'client_id' => ( is => 'ro' );
45             #has 'client_secret' => ( is => 'ro' );
46             #has 'token_string' => ( is => 'rw' );
47              
48              
49 5 0   5   5560 method setup() {
  0     0      
  0            
50             # Request Client details if non existent
51 0 0         if (! $self->config->{auth}{client_id} ) {
52 0           $self->config->{auth}{client_id} = $self->prompt("Paste enter your client_id");
53             }
54            
55 0 0         if (! $self->config->{auth}{client_secret} ) {
56 0           $self->config->{auth}{client_secret} = $self->prompt("Paste enter your client_secret");
57             }
58            
59             # Build auth object - TODO: Write a strava authentication provider! Issue #1
60 0           my $oauth2 = LWP::Authen::OAuth2->new(
61             client_id => $self->{config}{auth}{client_id},
62             client_secret => $self->{config}{auth}{client_secret},
63             service_provider => "Strava",
64             redirect_uri => "http://127.0.0.1",
65             scope => $self->{scope},
66             );
67              
68             # Get authentican token string
69 0           say "Log into the Strava account and browse the following url";
70 0           my $url = $oauth2->authorization_url();
71 0           say $url;
72 0           my $code = $self->prompt("Paste code result here");
73 0           $oauth2->request_tokens(code => $code);
74 0           $self->config->{auth}{token_string} = $oauth2->token_string;
75 0           $self->config->write($self->{config_file});
76             }
77              
78 5 0   5   4327 method _build_config() {
  0     0      
  0            
79 0           my $config;
80 0 0         if ( -e $self->{config_file} ) {
81 0           $config = Config::Tiny->read( $self->{config_file} );
82 0 0 0       unless ($config->{auth}{client_id}
83             && $config->{auth}{client_secret}) {
84 0           die <<"END_DIE";
85             Cannot find user credentials in $self->{config_file}
86              
87             You'll need to have a $self->{config_file} file that looks like
88             the following:
89              
90             [auth]
91             client_id = xxxxx
92             client_secret = xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
93              
94             You can get these values by going to https://www.strava.com/settings/api
95              
96             Running 'strava --setup' or \$strava->auth->setup will run you through
97             setting up Oauth2.
98              
99             END_DIE
100             }
101             } else {
102 0           $config = Config::Tiny->new();
103             }
104 0           return $config;
105             }
106              
107 5 0   5   3565 method _build_auth() {
  0     0      
  0            
108 0           $self->config;
109 0 0         croak "Missing token, please re-run setup" unless $self->config->{auth}{token_string};
110              
111 0           my $oauth2 = LWP::Authen::OAuth2->new(
112             client_id => $self->{config}{auth}{client_id},
113             client_secret => $self->{config}{auth}{client_secret},
114             service_provider => "Strava",
115             token_string => $self->config->{auth}{token_string},
116             );
117 0           return $oauth2;
118             }
119              
120              
121 5 0   5   734405 method get_api($api_path) {
  0 0   0      
  0            
  0            
  0            
122 0           my $response = $self->auth->get($self->{api_base}.$api_path);
123 0           my $json = $response->decoded_content;
124 0 0         if (! valid_json($json) ) {
125 0           croak("Something went wrong, a JSON string wasn't returned");
126             }
127 0 0         if ($ENV{STRAVA_DEBUG}) {
128 0           say Dumper($json);
129             }
130 0           return decode_json($json);
131             }
132              
133              
134 5 0   5   14201 method delete_api($api_path) {
  0 0   0      
  0            
  0            
  0            
135 0           my $response = $self->auth->delete($self->{api_base}.$api_path);
136 0 0         if ($ENV{STRAVA_DEBUG}) {
137 0           say Dumper($response);
138             }
139 0 0         return $response->code == 204 ? 1 : 0;
140             }
141              
142              
143 5 0   5   17155 method post_api($api_path,$content) {
  0 0   0      
  0 0          
  0            
  0            
  0            
  0            
144 0           my $response = $self->auth->post(
145             $self->{api_base}.$api_path,
146             Content => encode_json($content),
147             );
148              
149 0           my $json = $response->decoded_content;
150 0 0         if (! valid_json($json) ) {
151 0           croak("Something went wrong, a JSON string wasn't returned");
152             }
153 0 0         if ($ENV{STRAVA_DEBUG}) {
154 0           say Dumper($json);
155             }
156 0           return decode_json($json);
157             }
158              
159              
160 5     5   71550 method uploads_api(
  0     0      
  0            
  0            
161 0           :$file,
  0            
  0            
162 0           :$type,
  0            
  0            
163 0           :$activity_type?,
  0            
  0            
164 0           :$name?,
  0            
  0            
165 0           :$description?,
  0            
  0            
166 0           :$private?,
  0            
  0            
167 0           :$trainer?,
  0            
  0            
168 0 0         :$external_id?,
  0 0          
  0            
  0            
  0            
169             ) {
170 0           my $filename = basename($file);
171 0           my $mimetype = mimetype($file);
172              
173 0           my $content = {
174             file => [
175             $file,
176             $filename,
177             Content_Type => $mimetype,
178             'Content-Transfer-Encoding' => 'binary',
179             ],
180             data_type => lc($type),
181             };
182              
183 0 0         $content->{activity_type} = lc($activity_type) if $activity_type;
184 0 0         $content->{name} = $name if $name;
185 0 0         $content->{description} = $description if $description;
186 0 0         $content->{private} = $private if $private;
187 0 0         $content->{trainer} = $trainer if $trainer;
188 0 0         $content->{external_id} = $external_id if $external_id;
189              
190 0           my $response = $self->auth->post(
191             $self->{api_base}.'/uploads',
192             Content_Type => 'multipart/form-data',
193             Content => $content,
194             );
195              
196 0           my $json = $response->decoded_content;
197 0 0         if (! valid_json($json) ) {
198 0           croak("Something went wrong, a JSON string wasn't returned");
199             }
200 0 0         if ($ENV{STRAVA_DEBUG}) {
201 0           say Dumper($json);
202             }
203 0           return decode_json($json);
204             }
205              
206 5 0   5   21430 method prompt($question,:$default) { # inspired from here: http://alvinalexander.com/perl/edu/articles/pl010005
  0 0   0      
  0 0          
  0            
  0            
  0            
  0            
  0            
  0            
207 0 0         if ($default) {
208 0           say $question, "[", $default, "]: ";
209             } else {
210 0           say $question, ": ";
211 0           $default = "";
212             }
213              
214 0           $| = 1; # flush
215 0           $_ = <STDIN>; # get input
216              
217 0           chomp;
218 0 0         if ("$default") {
219 0 0         return $_ ? $_ : $default; # return $_ if it has a value
220             } else {
221 0           return $_;
222             }
223             }
224              
225             1;
226              
227             __END__
228              
229             =pod
230              
231             =encoding UTF-8
232              
233             =head1 NAME
234              
235             WebService::Strava::Auth - A Strava Segment Object
236              
237             =head1 VERSION
238              
239             version 0.04
240              
241             =head1 SYNOPSIS
242              
243             my $auth = WebService::Strava::Auth->new(
244             ['config_file' => '/path/to/file'],
245             ['scope' => 'read']
246             );
247              
248             =head1 DESCRIPTION
249              
250             A thin wrapper around LWP::Authen::OAuth2 to provide a pre-authenticated Oauth2 object
251             as a helper for the rest of WebService::Strava.
252              
253             =head1 METHODS
254              
255             =head2 setup()
256              
257             $auth->setup();
258              
259             Runs through configuring Oauth2 authentication with the Strava API. You
260             will need your client_id and client_secret available here:
261              
262             https://www.strava.com/settings/api
263              
264             =head2 get_api
265              
266             $strava->auth->get_api($url);
267              
268             Mainly used for an internal shortcut, but will return a parsed
269             perl data structure of what the api returns.
270              
271             =head2 delete_api
272              
273             $strava->auth->delete_api($url);
274              
275             Mainly used for an internal shortcut, but will return true on
276             success or false on failure.
277              
278             =head2 post_api
279              
280             $strava->auth->post_api($url,$content);
281              
282             Mainly used for an internal shortcut, but will return a parsed
283             perl data structure of what the api returns. '$content' is expected
284             to be a plain perl data structure. The method will encode it to json.
285              
286             =head2 uploads_api
287              
288             $strava->auth->uploads_api(file => 'sample.gpx', type => 'gpx');
289              
290             Mainly used for an internal shortcut, but will return a parsed
291             perl data structure of what the api returns.
292              
293             =head1 AUTHOR
294              
295             Leon Wright < techman@cpan.org >
296              
297             =head1 COPYRIGHT AND LICENSE
298              
299             This software is copyright (c) 2014 by Leon Wright.
300              
301             This is free software; you can redistribute it and/or modify it under
302             the same terms as the Perl 5 programming language system itself.
303              
304             =cut