package WebService::Strava::Auth;
use v5.010;
use strict;
use warnings;
use Config::Tiny;
use LWP::Authen::OAuth2;
use JSON qw(decode_json encode_json);
use JSON::Parse 'valid_json';
use Carp qw(croak);
use File::Basename;
use File::MimeInfo::Magic;
use Data::Dumper;
use Method::Signatures 20140224;
use Moo;
use experimental 'say';
use namespace::clean;
# ABSTRACT: A Strava Segment Object
our $VERSION = '0.06'; # VERSION: Generated by DZP::OurPkg:Version
# Debugging hooks in case things go weird. (Thanks @pjf)
around BUILDARGS => sub {
my $orig = shift;
my $class = shift;
if ($WebService::Strava::DEBUG) {
warn "Building task with:\n";
warn Dumper(\@_), "\n";
}
return $class->$orig(@_);
};
has 'api_base' => (is => 'ro', default => sub { 'https://www.strava.com/api/v3' });
has 'config_file' => ( is => 'ro', default => sub { "$ENV{HOME}/.stravarc" } );
has 'config' => ( is => 'rw', lazy => 1, builder => 1 );
has 'scope' => ( is => 'ro', default => sub { "view_private,write" } );
has 'auth' => ( is => 'rw', lazy => 1, builder => 1, handles => [ qw( get post ) ] );
# TODO: Potentially allow the config to be passed through instead of loaded.
#has 'client_id' => ( is => 'ro' );
#has 'client_secret' => ( is => 'ro' );
#has 'token_string' => ( is => 'rw' );
method setup() {
# Request Client details if non existent
if (! $self->config->{auth}{client_id} ) {
$self->config->{auth}{client_id} = $self->prompt("Paste enter your client_id");
}
if (! $self->config->{auth}{client_secret} ) {
$self->config->{auth}{client_secret} = $self->prompt("Paste enter your client_secret");
}
# Build auth object - TODO: Write a strava authentication provider! Issue #1
my $oauth2 = LWP::Authen::OAuth2->new(
client_id => $self->{config}{auth}{client_id},
client_secret => $self->{config}{auth}{client_secret},
service_provider => "Strava",
redirect_uri => "http://127.0.0.1",
scope => $self->{scope},
);
# Get authentican token string
say "Log into the Strava account and browse the following url";
my $url = $oauth2->authorization_url();
say $url;
my $code = $self->prompt("Paste code result here");
$oauth2->request_tokens(code => $code);
$self->config->{auth}{token_string} = $oauth2->token_string;
$self->config->write($self->{config_file});
}
method _build_config() {
my $config;
if ( -e $self->{config_file} ) {
$config = Config::Tiny->read( $self->{config_file} );
unless ($config->{auth}{client_id}
&& $config->{auth}{client_secret}) {
die <<"END_DIE";
Cannot find user credentials in $self->{config_file}
You'll need to have a $self->{config_file} file that looks like
the following:
[auth]
client_id = xxxxx
client_secret = xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
You can get these values by going to https://www.strava.com/settings/api
Running 'strava --setup' or \$strava->auth->setup will run you through
setting up Oauth2.
END_DIE
}
} else {
$config = Config::Tiny->new();
}
return $config;
}
method _build_auth() {
$self->config;
croak "Missing token, please re-run setup" unless $self->config->{auth}{token_string};
my $oauth2 = LWP::Authen::OAuth2->new(
client_id => $self->{config}{auth}{client_id},
client_secret => $self->{config}{auth}{client_secret},
service_provider => "Strava",
token_string => $self->config->{auth}{token_string},
);
return $oauth2;
}
method get_api($api_path) {
my $response = $self->auth->get($self->{api_base}.$api_path);
my $json = $response->decoded_content;
if (! valid_json($json) ) {
croak("Something went wrong, a JSON string wasn't returned");
}
if ($ENV{STRAVA_DEBUG}) {
say Dumper($json);
}
return decode_json($json);
}
method delete_api($api_path) {
my $response = $self->auth->delete($self->{api_base}.$api_path);
if ($ENV{STRAVA_DEBUG}) {
say Dumper($response);
}
return $response->code == 204 ? 1 : 0;
}
method post_api($api_path,$content) {
my $response = $self->auth->post(
$self->{api_base}.$api_path,
Content => encode_json($content),
);
my $json = $response->decoded_content;
if (! valid_json($json) ) {
croak("Something went wrong, a JSON string wasn't returned");
}
if ($ENV{STRAVA_DEBUG}) {
say Dumper($json);
}
return decode_json($json);
}
method uploads_api(
:$file,
:$type,
:$activity_type?,
:$name?,
:$description?,
:$private?,
:$trainer?,
:$external_id?,
) {
my $filename = basename($file);
my $mimetype = mimetype($file);
my $content = {
file => [
$file,
$filename,
Content_Type => $mimetype,
'Content-Transfer-Encoding' => 'binary',
],
data_type => lc($type),
};
$content->{activity_type} = lc($activity_type) if $activity_type;
$content->{name} = $name if $name;
$content->{description} = $description if $description;
$content->{private} = $private if $private;
$content->{trainer} = $trainer if $trainer;
$content->{external_id} = $external_id if $external_id;
my $response = $self->auth->post(
$self->{api_base}.'/uploads',
Content_Type => 'multipart/form-data',
Content => $content,
);
my $json = $response->decoded_content;
if (! valid_json($json) ) {
croak("Something went wrong, a JSON string wasn't returned");
}
if ($ENV{STRAVA_DEBUG}) {
say Dumper($json);
}
return decode_json($json);
}
method prompt($question,:$default) { # inspired from here: http://alvinalexander.com/perl/edu/articles/pl010005
if ($default) {
say $question, "[", $default, "]: ";
} else {
say $question, ": ";
$default = "";
}
$| = 1; # flush
$_ = <STDIN>; # get input
chomp;
if ("$default") {
return $_ ? $_ : $default; # return $_ if it has a value
} else {
return $_;
}
}
1;
__END__
=pod
=encoding UTF-8
=head1 NAME
WebService::Strava::Auth - A Strava Segment Object
=head1 VERSION
version 0.06
=head1 SYNOPSIS
my $auth = WebService::Strava::Auth->new(
['config_file' => '/path/to/file'],
['scope' => 'read']
);
=head1 DESCRIPTION
A thin wrapper around LWP::Authen::OAuth2 to provide a pre-authenticated Oauth2 object
as a helper for the rest of WebService::Strava.
=head1 METHODS
=head2 setup()
$auth->setup();
Runs through configuring Oauth2 authentication with the Strava API. You
will need your client_id and client_secret available here:
https://www.strava.com/settings/api
=head2 get_api
$strava->auth->get_api($url);
Mainly used for an internal shortcut, but will return a parsed
perl data structure of what the api returns.
=head2 delete_api
$strava->auth->delete_api($url);
Mainly used for an internal shortcut, but will return true on
success or false on failure.
=head2 post_api
$strava->auth->post_api($url,$content);
Mainly used for an internal shortcut, but will return a parsed
perl data structure of what the api returns. '$content' is expected
to be a plain perl data structure. The method will encode it to json.
=head2 uploads_api
$strava->auth->uploads_api(file => 'sample.gpx', type => 'gpx');
Mainly used for an internal shortcut, but will return a parsed
perl data structure of what the api returns.
=head1 AUTHOR
Leon Wright < techman@cpan.org >
=head1 COPYRIGHT AND LICENSE
This software is copyright (c) 2014 by Leon Wright.
This is free software; you can redistribute it and/or modify it under
the same terms as the Perl 5 programming language system itself.
=cut