Home > Software design >  connect to google spreadsheet
connect to google spreadsheet

Time:07-06

i have a web app created using perl, and i have a table on a spreadsheet document, i want to connect my app to the spreadsheet, i tried the documentation but i can't get the token here is what i did:

sub authenticate {
    my $oauth2 = Net::Google::DataAPI::Auth::OAuth2->new(
        client_id     => 'my client id',
        client_secret => 'my secret code',
        scope         => ['http://spreadsheets.google.com/feeds/'],
    );

    my $url = $oauth2->authorize_url(access_type => 'offline', approval_prompt => 'force');
    use Data::Dumper;
    Core::Global::Logger::debug(Dumper($url));
    
    #you will need to put code here and receive token
    print "OAuth URL, get code: \n$url\n";
   
    use Term::Prompt;
    my $code = prompt('x', 'my code', '', ''); 
    my $token = $oauth2->get_access_token($code) or die;

    #save token for future use
    my $session = $token->session_freeze;
    store( $session, 'google_spreadsheet.session' );
}

CodePudding user response:

I was not able to make Net::Google::Spreadsheets::V4 work with a service account and using Net::Google::DataAPI::Auth::OAuth2 to get the access token. But you should be able to get the access token from WWW::Google::Cloud::Auth::ServiceAccount instead, and then use that token to authorize requests to the google drive rest api. The following worked for me:

use feature qw(say);
use strict;
use warnings;
use WWW::Google::Cloud::Auth::ServiceAccount;
use LWP::UserAgent;
use URI;
use HTTP::Request;
use Mojolicious::Lite;
use Data::Dumper;
use JSON;
use experimental qw(declared_refs refaliasing signatures);

{ 
    my @scopes = ('https://www.googleapis.com/auth/spreadsheets',
                  'https://www.googleapis.com/auth/drive');
    my $auth =  WWW::Google::Cloud::Auth::ServiceAccount->new(
        credentials_path => 'credentials.json',
        scope            => join ' ', @scopes 
    );

    get '/' => sub {
        my $c = shift;
        my $token = $auth->get_token();
        my ($return_code, $files) = get_all_spreadsheets($token);
        $c->render(template => 'index', return_code => $return_code);
    };
    app->start;
}

sub get_all_spreadsheets($token) {
    my $url = 'https://www.googleapis.com/drive/v3/files';
    my $query = 'mimeType="application/vnd.google-apps.spreadsheet"';
    my $params = {
        "q"                         => $query,
        "pageSize"                  => 1000,
        "supportsAllDrives"         => 1,
        "includeItemsFromAllDrives" => 1,
        "fields"                    => "kind,nextPageToken,"
                                       . "files(id,name,createdTime,modifiedTime)",
    };
    my $more_pages = 1;
    my $page_token = '';
    my $status_line;
    my @files;
    while ($more_pages) {
        $params->{pageToken} = $page_token if $page_token;
        my $result = send_google_drive_get_request($url, $params, $token);
        $status_line = $result->status_line;
        if (!$result->is_success) {
            return $status_line;
        }
        my $hash = decode_json($result->content);
        push @files, $hash->{files};
        if (exists $hash->{nextPageToken}) {
            $page_token = $hash->{nextPageToken};
        }
        else {
            $more_pages = 0;
        }
    }
    return $status_line, \@files;
}

sub send_google_drive_get_request( $url, $params, $token ) {
    my $uri = URI->new( $url );
    $uri->query_form($params);
    my $str = $uri->as_string();
    my @headers = get_headers($token);
    my $req = HTTP::Request->new(
        'GET',
        $uri->as_string(),
        \@headers,
    );
    my $ua = LWP::UserAgent->new();
    my $res = $ua->request($req);
    return $res;
}

sub get_headers($token) {
    return 'Accept-Encoding' => 'gzip, deflate',
        'Accept'          => '*/*',
        'Connection'      => 'keep-alive',
        "Authorization"   => "Bearer $token";
}

__DATA__

@@ index.html.ep
<!DOCTYPE html>
<html>
  <head><title>Testing access to google sheets...</title></head>
  <body>
    <h1>Return code = <%= $return_code %></h1>
  </body>
</html>

Edit:

To get the value of a cell for a given sheet with a given id, you can use the following url: https://sheets.googleapis.com/v4/spreadsheets/%s/values/%s where the first %s is replaced by the id of the sheet and the second %s represents the cell range to extract. Here is an example:

use feature qw(say);
use strict;
use warnings;
use WWW::Google::Cloud::Auth::ServiceAccount;
use LWP::UserAgent;
use URI;
use URI::Encode;
use HTTP::Request;
use Mojolicious::Lite;
use Data::Dumper;
use JSON;
use experimental qw(declared_refs refaliasing signatures);

{
    my @scopes = ('https://www.googleapis.com/auth/spreadsheets',
                  'https://www.googleapis.com/auth/drive');
    my $auth =  WWW::Google::Cloud::Auth::ServiceAccount->new(
        credentials_path => 'credentials.json',
        scope => join " ", @scopes
    );

    get '/' => sub {
        my $c = shift;
        my $token = $auth->get_token();
        my $sheet_id = '1FPyDuIPPzwUeLNpLbdI-RzfouKcm-2duOJ9Jio-Z-Qw';
        my $sheet_cell = 'B1';
        my ($return_code, $cell_value) = 
            read_spreadsheet_cell($token, $sheet_id, $sheet_cell);
        app->log->debug(app->dumper( { cell_value => $cell_value } ));
        $c->render(template => 'index', return_code => $return_code);
    };
    app->start;
}

sub read_spreadsheet_cell($token, $id, $cell) {
    my $encoder = URI::Encode->new();
    my $value_range = $encoder->encode(sprintf "'Sheet1'!%s", $cell);
    my $url = sprintf 'https://sheets.googleapis.com/v4/spreadsheets/%s/values/%s', 
         $id, $value_range;
    my $result = send_google_drive_get_request($url, $token);
    my $status_line = $result->status_line;
    if (!$result->is_success) {
        return $status_line;
    }
    my $result_hash = decode_json( $result->content );
    #app->log->debug(app->dumper( $result_hash ));
    return $status_line, $result_hash->{values}[0][0];
}

sub send_google_drive_get_request( $url, $token ) {
    my @headers = get_headers($token);
    my $req = HTTP::Request->new('GET', $url, \@headers);
    my $ua = LWP::UserAgent->new();
    my $res = $ua->request($req);
    return $res;
}

sub get_headers($token) {
    return
        'User-Agent'      => 'Mozilla/8.0',
        'Accept-Encoding' => 'gzip, deflate',
        'Accept'          => '*/*',
        'Connection'      => 'keep-alive',
        "Authorization"   => "Bearer $token";
}

__DATA__

@@ index.html.ep
<!DOCTYPE html>
<html>
  <head><title>Testing access to google sheets...</title></head>
  <body>
    <h1>Return code = <%= $return_code %></h1>
  </body>
</html>
  • Related