Files
racalmas/tools/sync_cms/lib/GoogleCalendarApi.pm

234 lines
6.6 KiB
Perl

package GoogleCalendarApi;
use strict;
use warnings;
use JSON;
use JSON::WebToken;
use LWP::UserAgent;
use HTML::Entities;
use URI::Escape;
use Data::Dumper;
use DateTime;
sub new {
my $class = shift;
my $params = shift;
my $self={};
for my $attr ('calendarId','debug'){
$self->{$attr}=$params->{$attr} if defined $params->{$attr};
}
my $instance=bless $self, $class;
if ((defined $params->{serviceAccount}) && (defined $params->{privateKey})){
$instance->login($params->{serviceAccount}, $params->{privateKey});
}
return $instance;
}
sub setCalendar{
my $self=shift;
my $calendarId=shift;
$self->{calendarId}=$calendarId;
}
sub getBasicUrl{
my $self=shift;
return 'https://www.googleapis.com/calendar/v3/calendars/'.encode_entities($self->{calendarId});
}
#https://developers.google.com/google-apps/calendar/v3/reference/events/list
#returns {
# 'timeZone' => 'Europe/Berlin',
# 'description' => "Radioprogramm von Pi Radio f\x{fc}r 88vier.de",
# 'defaultReminders' => [],
# 'accessRole' => 'owner',
# 'etag' => '"1415821582086000"',
# 'kind' => 'calendar#events',
# 'summary' => '88vier.de Pi Radio (Programm)',
# 'updated' => '2014-11-12T19:46:22.086Z',
# 'items' => [...]
# }
sub getEvents{
my $self=shift;
my $params=shift;
my $url='/events?';
for my $param ('iCalUID','alwaysIncludeEmail','maxAttendees','maxResults','orderBy','pageToken','privateExtendedProperty',
'q','sharedExtendedProperty','showDeleted','showHiddenInvitations','singleEvents','syncToken','timeZone'
){
$url.='&'.$param.'='.uri_escape($params->{$param}) if defined $params->{$param};
}
for my $param ('timeMin','timeMax','updatedMin'){
$url.='&'.$param.'='.uri_escape($self->formatDateTime($params->{$param})) if defined $params->{$param};
}
my $result=$self->httpRequest('GET', $url);
return $result;
}
# sleep 0.25 seconds to prevent hitting the 5.0 requests/second/user rate
sub sleep{
my $this=shift;
my $duration=shift || 0.25;
select(undef, undef, undef, $duration);
}
#https://developers.google.com/google-apps/calendar/v3/reference/events/delete
sub deleteEvent{
my $self=shift;
my $eventId=shift;
my $url='/events/'.$eventId;
#DELETE https://www.googleapis.com/calendar/v3/calendars/calendarId/events/eventId
my $result=$self->httpRequest('DELETE', $url);
$self->sleep();
return $result;
}
#https://developers.google.com/google-apps/calendar/v3/reference/events/insert
sub insertEvent{
my $self=shift;
my $params=shift;
my $event={
start => {
dateTime => $self->formatDateTime($params->{start})
},
end => {
dateTime => $self->formatDateTime($params->{end})
},
summary => $params->{summary}||'',
description => $params->{description}||'',
location => $params->{location}||'',
status => $params->{confirmed}||'confirmed'
};
$event= encode_json $event;
#POST https://www.googleapis.com/calendar/v3/calendars/calendarId/events
my $url='/events';
my $result=$self->httpRequest('POST', $url, $event);
$self->sleep();
return $result;
}
# send a HTTP request
sub httpRequest{
my $self=shift;
my $method=shift;
my $url=shift;
my $content=shift||'';
print STDERR "$method ".$url."\n" if $self->{debug};
die ("missing url") unless defined $url;
die ("calendarId not set") unless defined $self->{calendarId};
die ("not logged in ") unless defined $self->{api};
#prepend basic url including calendar id
$url=$self->getBasicUrl().$url;
print STDERR "$method ".$url."\n" if $self->{debug};
my $response=undef;
if($method eq 'GET'){
$response = $self->{api}->get($url);
}elsif(($method eq 'POST')||($method eq 'PUT')){
print STDERR $content."\n" if $self->{debug};
my $request = HTTP::Request->new( $method, $url );
$request->header( 'Content-Type' => 'application/json' );
$request->content( $content );
$response=$self->{api}->request( $request );
}elsif($method eq 'DELETE'){
$response = $self->{api}->delete($url);
}
if($response->is_success) {
my $content = $response->content;
return {} if $content eq '';
return decode_json($content);
} else {
print "ERROR:\n";
print "Code: ".$response->code."\n";
print "Message: ".$response->message."\n";
print $response->content."\n";
die;
}
}
# write datetime object to string
sub formatDateTime{
my $self=shift;
my $dt=shift;
my $datetime= $dt->format_cldr("yyyy-MM-ddTHH:mm:ssZZZZZ");
print STDERR "$dt -> $datetime\n" if $self->{debug};
return $datetime;
}
# parse datetime from string to object
sub getDateTime{
my $self=shift;
my $datetime=shift;
my $timezone=shift;
return if((!defined $datetime) or ($datetime eq ''));
my @l=split /[\-\;T\s\:\+\.]/,$datetime;
$datetime=DateTime->new(
year => $l[0],
month => $l[1],
day => $l[2],
hour => $l[3],
minute => $l[4],
second => $l[5],
time_zone => $timezone
);
return $datetime;
}
# login with serviceAccount and webToken (from privateKey)
sub login{
my $self=shift;
my $serviceAccount=shift;
my $privateKey=shift;
# https://developers.google.com/accounts/docs/OAuth2ServiceAccount
my $time = time;
#create JSON Web Token
my $jwt = JSON::WebToken->encode(
{
iss => $serviceAccount,
scope => 'https://www.googleapis.com/auth/calendar',
aud => 'https://accounts.google.com/o/oauth2/token',
exp => $time + 3600,
iat => $time,
},
$privateKey,
'RS256',
{typ => 'JWT'}
);
#send JSON web token to authentication service
$self->{auth} = LWP::UserAgent->new();
my $response = $self->{auth}->post(
'https://accounts.google.com/o/oauth2/token',
{
grant_type => encode_entities('urn:ietf:params:oauth:grant-type:jwt-bearer'),
assertion => $jwt
}
);
die($response->code, "\n", $response->content, "\n") unless $response->is_success();
my $data= decode_json($response->content);
#create a new user agent and set token to bearer
$self->{api} = LWP::UserAgent->new();
$self->{api}->default_header(Authorization => 'Bearer ' . $data->{access_token});
print STDERR "login successful\n" if $self->{debug};
return $data;
}
1;