google-sheetsraku

Raku module fails to work with Google Sheets .clear API function


Apologies for the long question. I am working on the raku Net::Google::Sheets module.

Currently it does a good job of successfully using OAuth2::Client::Google and getting and putting a 2D Array from/to a Google Sheet.

I want to add a $sheet.clear method in line with the Google Sheets API.

Here is the relevant class in /lib/Net/Google/Sheets.rakumod:

class Sheet is export {
    has $.session;
    has $.id;
    has $.range;

    method url {
        "$sheet-base/{$!id}/values/{$!range}"
    }

    method clear {
        my $request = HTTP::Request.new(
            POST => ($.url ~ ':clear'),
            Authorization => "Bearer {$.session.token}",
            Content-length => 0,
        );

        $ua.request($request).decoded-content.&from-json;
    }
}

And here is the script that calls it:

#!/usr/bin/env raku 

use Net::Google::Sheets;

my $session = Session.new;
my %sheets = $session.sheets;
my $id = %sheets<AWS_EC2_Sizes_test>;

$sheet2.clear;            # <=== gives error

And here is the output and error that I am getting...

The requested URL <code>/v4/spreadsheets/{SheetId}/values/Sheet2%3Aclear</code> was not found on this server.  <ins>That’s all we know.</ins>

The Oauth access is working fine for read and write and the sheet ID is correct. I have also got a successful .clear action via the Google reference doc "Try It" box.

I suspect that there is some subtlety around gRPC that I am not following (maybe I need to encode the ':' colon character another way?).

Please can you help?


Thanks to @raiph answer, see below, the problem was that handing URI a string for the entire url means that it is all url-encoded and (as raiph points out) Google wants the colon : as is. The following code works fine:

   method uri( :$cmd ) {
        my $uri = URI.new: $sheet-url;

        my $path = "$sheet-path/{$!id}/values/{$!range}";
        $path ~= ":$cmd" if $cmd;
        $uri.path: $path;

        $uri;
    }

    method clear {
        my $cmd = 'clear';

        my $request = HTTP::Request.new(
            POST => $.uri( :$cmd ),
            Authorization => "Bearer {$.session.token}",
            Content-length => 0,
        );

        $ua.request($request).decoded-content.&from-json;
    }

Thanks for deconfusing me!


Solution

  • The following is not tested in situ with HTTP::Request and a call to a google sheets API endpoint, but I'm sufficiently confident it'll work to post it as an answer:

    my URI $uri .= new($.url) andthen .path(.path ~ ':clear');
    
    my $request = HTTP::Request.new(POST => $uri, ...
    

    A few notes about what I figured out on the way to the above solution might be useful for later readers: