API-Google
view release on metacpan or search on metacpan
received the program in object code or executable form alone.)
Source code for a work means the preferred form of the work for making
modifications to it. For an executable file, complete source code means
all the source code for all modules it contains; but, as a special
exception, it need not include source code for modules which are standard
libraries that accompany the operating system on which the executable
file runs, or for standard header files or definitions files that
accompany that operating system.
4. You may not copy, modify, sublicense, distribute or transfer the
Program except as expressly provided under this General Public License.
Any attempt otherwise to copy, modify, sublicense, distribute or transfer
the Program is void, and will automatically terminate your rights to use
the Program under this License. However, parties who have received
copies, or rights to use copies, from you under this General Public
License will not have their licenses terminated so long as such parties
remain in full compliance.
5. By copying, distributing or modifying the Program (or any work based
on the Program) you indicate your acceptance of this license to do so,
and all its terms and conditions.
6. Each time you redistribute the Program (or any work based on the
Program), the recipient automatically receives a license from the original
licensor to copy, distribute or modify the Program subject to these
terms and conditions. You may not impose any further restrictions on the
recipients' exercise of the rights granted herein.
7. The Free Software Foundation may publish revised and/or new versions
of the General Public License from time to time. Such new versions will
be similar in spirit to the present version, but may differ in detail to
address new problems or concerns.
Each version is given a distinguishing version number. If the Program
specifies a version number of the license which applies to it and "any
may not charge a fee for this Package itself. However, you may distribute this
Package in aggregate with other (possibly commercial) programs as part of a
larger (possibly commercial) software distribution provided that you do not
advertise this Package as a product of your own.
6. The scripts and library files supplied as input to or produced as output
from the programs of this Package do not automatically fall under the copyright
of this Package, but belong to whomever generated them, and may be sold
commercially, and may be aggregated with this Package.
7. C or perl subroutines supplied by you and linked into this Package shall not
be considered part of this Package.
8. The name of the Copyright Holder may not be used to endorse or promote
products derived from this software without specific prior written permission.
9. THIS PACKAGE IS PROVIDED "AS IS" AND WITHOUT ANY EXPRESS OR IMPLIED
WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF
MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
The End
if (-e $filename) {
say "File $filename exists";
input_if_not_exists(['gapi/client_id', 'gapi/client_secret']);
runserver();
} else {
say "JSON file $filename with API tokens not found. Creating new file...";
setup();
runserver();
}
sub setup {
my $oauth = {};
say "Obtain app client_id and client_secret from http://console.developers.google.com/";
print "client_id: ";
chomp ($oauth->{client_id} = <STDIN>);
print "client_secret: ";
chomp ($oauth->{client_secret} = <STDIN>);
my $tokensfile = Config::JSON->create($filename);
$tokensfile->set('gapi/client_id', $oauth->{client_id});
$tokensfile->set('gapi/client_secret', $oauth->{client_secret});
say 'OAuth details was updated!';
# Remove comment for Mojolicious::Plugin::JSONConfig compatibility
tie my @array, 'Tie::File', $filename or die $!;
shift @array;
untie @array;
};
sub input_if_not_exists {
my $fields = shift;
my $config = Config::JSON->new($filename);
for my $i (@$fields) {
if (!defined $config->get($i) ) {
print "$i: ";
chomp (my $val = <STDIN>);
$config->set($i, $val);
}
}
}
sub runserver {
my $port = empty_port(3000);
say "Starting web server. Before authorization don't forget to set redirect_uri to http://127.0.0.1:$port/";
$ENV{'GOAUTH_TOKENSFILE'} = $filename;
Mojolicious::Commands->start_app('API::Google::Server', 'daemon', '-l', 'http://*:'.$port);
}
__END__
=pod
lib/API/Google.pm view on Meta::CPAN
# ABSTRACT: Perl library for easy access to Google services via their API
use strict;
use warnings;
use Mojo::UserAgent;
use Config::JSON;
use Data::Dumper;
sub new {
my ($class, $params) = @_;
my $h = {};
if ($params->{tokensfile}) {
$h->{tokensfile} = Config::JSON->new($params->{tokensfile});
} else {
die 'no json file specified!';
}
$h->{ua} = Mojo::UserAgent->new();
$h->{debug} = $params->{debug};
$h->{max_refresh_attempts} = $params->{max_refresh_attempts} || 5;
return bless $h, $class;
}
sub refresh_access_token {
my ($self, $params) = @_;
warn "Attempt to refresh access_token with params: ".Dumper $params if $self->{debug};
$params->{grant_type} = 'refresh_token';
$self->{ua}->post('https://www.googleapis.com/oauth2/v4/token' => form => $params)->res->json; # tokens
};
sub client_id {
shift->{tokensfile}->get('gapi/client_id');
}
sub ua {
shift->{ua};
}
sub client_secret {
shift->{tokensfile}->get('gapi/client_secret');
}
sub refresh_access_token_silent {
my ($self, $user) = @_;
my $tokens = $self->refresh_access_token({
client_id => $self->client_id,
client_secret => $self->client_secret,
refresh_token => $self->get_refresh_token_from_storage($user)
});
warn "New tokens got" if $self->{debug};
my $res = {};
$res->{old} = $self->get_access_token_from_storage($user);
warn Dumper $tokens if $self->{debug};
if ($tokens->{access_token}) {
$self->set_access_token_to_storage($user, $tokens->{access_token});
}
$res->{new} = $self->get_access_token_from_storage($user);
return $res;
};
sub get_refresh_token_from_storage {
my ($self, $user) = @_;
warn "get_refresh_token_from_storage(".$user.")" if $self->{debug};
return $self->{tokensfile}->get('gapi/tokens/'.$user.'/refresh_token');
};
sub get_access_token_from_storage {
my ($self, $user) = @_;
$self->{tokensfile}->get('gapi/tokens/'.$user.'/access_token');
};
sub set_access_token_to_storage {
my ($self, $user, $token) = @_;
$self->{tokensfile}->set('gapi/tokens/'.$user.'/access_token', $token);
};
sub build_headers {
my ($self, $user) = @_;
my $t = $self->get_access_token_from_storage($user);
my $headers = {};
$headers->{'Authorization'} = 'Bearer '.$t;
return $headers;
}
sub build_http_transaction {
my ($self, $params) = @_;
warn "build_http_transaction() params : ".Dumper $params if $self->{debug};
my $headers = $self->build_headers($params->{user});
my $http_method = $params->{method};
my $tx;
if ($http_method eq 'get' || $http_method eq 'delete') {
$tx = $self->{ua}->build_tx(uc $http_method => $params->{route} => $headers);
lib/API/Google.pm view on Meta::CPAN
} else {
die 'wrong http_method on no payload if using POST';
}
return $tx;
}
sub api_query {
my ($self, $params, $payload) = @_;
warn "api_query() params : ".Dumper $params if $self->{debug};
$payload = { payload => $payload };
%$params = (%$params, %$payload);
my $tx = $self->build_http_transaction($params);
my $res = $self->{ua}->start($tx)->res->json;
lib/API/Google/GCal.pm view on Meta::CPAN
$API::Google::GCal::VERSION = '0.12';
use parent 'API::Google';
# use base 'API::Google';
# ABSTRACT: Google Calendar API client
sub new {
my ($class, $params) = @_;
my $self = API::Google->new($params);
$self->{api_base} = 'https://www.googleapis.com/calendar/v3';
bless $self, $class;
return $self; # just for clearance
}
sub get_calendars {
my ($self, $user, $fields) = @_;
my $res = $self->api_query({
method => 'get',
route => $self->{api_base}.'/users/me/calendarList',
user => $user
});
if ($fields) {
my @a;
for my $item (@{$res->{items}}) {
push @a, { map { $_ => $item->{$_} } grep { exists $item->{$_} } @$fields };
}
return \@a;
} else {
return $res;
}
}
sub get_calendar_id_by_name {
my ($self, $user, $name) = @_;
my $all = $self->get_calendars($user, ['id', 'summary']); # arr ref
my @n = grep { $_->{'summary'} eq $name } @$all;
my $full_id = $n[0]->{id};
return $full_id;
}
sub add_event {
my ($self, $user, $calendar_id, $event_data) = @_;
$self->api_query({
method => 'post',
route => $self->{api_base}.'/calendars/'.$calendar_id.'/events',
user => $user
}, $event_data);
}
sub busy_time_ranges {
my ($self, $params) = @_;
$self->api_query({
method => 'post',
route => $self->{api_base}.'/freeBusy',
user => $params->{user}
}, {
timeMin => $params->{dt_start},
timeMax => $params->{dt_end},
timeZone => $params->{timeZone},
items => [{ 'id' => $params->{calendarId} }]
});
};
sub events_list {
my ($self, $params) = @_;
if (!defined $params->{calendarId}) { die "No calendarId provided as parameter"}
if (!defined $params->{user}) { die "No user provided as parameter"}
my $res = $self->api_query({
method => 'get',
route => $self->{api_base}.'/calendars/'.$params->{calendarId}.'/events',
user => $params->{user}
});
lib/API/Google/Server.pm view on Meta::CPAN
use Data::Dumper;
use Config::JSON;
use Tie::File;
use Crypt::JWT qw(decode_jwt);
use feature 'say';
use Mojo::Util 'getopt';
use Mojolicious::Plugin::OAuth2;
# use Mojo::JWT;
# sub return_json_filename {
# use Cwd;
# my $cwd = getcwd;
# opendir my $dir, $cwd or die "Cannot open directory: $!";
# my @files = readdir $dir;
# my @j = grep { $_ =~ /\w+.json/ } @files;
# return $j[0];
# }
# my $f = return_json_filename();
lib/API/Google/Server.pm view on Meta::CPAN
google => {
key => $config->get('gapi/client_id'), # $config->{gapi}{client_id},
secret => $config->get('gapi/client_secret'), #$config->{gapi}{client_secret},
authorize_url => 'https://accounts.google.com/o/oauth2/v2/auth?response_type=code',
token_url => 'https://www.googleapis.com/oauth2/v4/token'
}
};
helper get_new_tokens => sub {
my ($c,$auth_code) = @_;
my $hash = {};
$hash->{code} = $c->param('code');
$hash->{redirect_uri} = $c->url_for->to_abs->to_string;
$hash->{client_id} = $config->get('gapi/client_id');
$hash->{client_secret} = $config->get('gapi/client_secret');
$hash->{grant_type} = 'authorization_code';
my $tokens = $c->ua->post('https://www.googleapis.com/oauth2/v4/token' => form => $hash)->res->json;
return $tokens;
};
lib/API/Google/Server.pm view on Meta::CPAN
# =method get_all_google_jwk_keys
# Get all Google JWK keys for validation of JSON Web Token
# Check https://jwt.io/ and https://developers.google.com/identity/protocols/OpenIDConnect#validatinganidtoken for more details
# return arrayref
# =cut
# helper get_all_google_jwk_keys => sub {
# my $c = shift;
# my $certs = $c->ua->get('https://www.googleapis.com/oauth2/v3/certs')->res->json;
# # return $certs;
# my @keys = @{$certs->{keys}};
# return \@keys;
# # return $certs->{keys}[1];
# };
# =method get_google_jwk_key_by_kid
lib/API/Google/Server.pm view on Meta::CPAN
# $c->get_google_cert_by_kid($kid,$crts) # $kid - string, $crts - arrayref
# Example of usage:
# $c->get_google_cert_by_kid($header->{kid},$crts)
# =cut
# helper get_google_jwk_key_by_kid => sub {
# my ($c, $kid, $jwks_arrayref) = @_;
# if (!defined $jwks_arrayref) {
# warn 'get_google_cert_by_kid(): $ctrs is not defined, obtaining from Google...';
# $jwks_arrayref = $c->ua->get('https://www.googleapis.com/oauth2/v3/certs')->res->json->{keys};
# }
# # my @keys = @{$crts->{keys}};
# my @keys = @$jwks_arrayref;
# my $size = scalar @keys;
lib/API/Google/Server.pm view on Meta::CPAN
# }
# }
# warn 'JWK key with particular kid not found';
# return undef;
# };
helper get_email => sub {
my ($c, $access_token) = @_;
my %h = (
'Authorization' => 'Bearer '.$access_token
);
$c->ua->get('https://www.googleapis.com/auth/plus.profile.emails.read' => form => \%h)->res->json;
};
get "/" => sub {
my $c = shift;
app->log->info("Will store tokens at".$config->getFilename ($config->pathToFile));
if ($c->param('code')) {
app->log->info("Authorization code was retrieved: ".$c->param('code'));
my $tokens = $c->get_new_tokens($c->param('code'));
app->log->info("App got new tokens: ".Dumper $tokens);
if ($tokens) {
my $user_data;
( run in 0.580 second using v1.01-cache-2.11-cpan-88abd93f124 )