0

i am currently trying to run an app script function when user connect there google account. when one row is finished typing i want the row information back to my server.so i came to a conclusion that app script is suitable for that.

what i have do step by step is

1) create a google console project

2) enable app script

3) enable google sheet api

4) download credentials.json

5) create a script

   <?php
require __DIR__ . '/vendor/autoload.php';

if (php_sapi_name() != 'cli') {
    throw new Exception('This application must be run on the command line.');
}

/**
 * Returns an authorized API client.
 * @return Google_Client the authorized client object
 */
function getClient()
{

    $client = new Google_Client();
    $client->setApplicationName('Google Apps Script API PHP Quickstart');
    $client->setScopes(['https://www.googleapis.com/auth/script.projects','https://www.googleapis.com/auth/script.scriptapp','https://www.googleapis.com/auth/spreadsheets','https://www.googleapis.com/auth/spreadsheets.readonly','https://www.googleapis.com/auth/script.external_request','https://www.googleapis.com/auth/script.deployments']);
    $client->setAuthConfig('credentials.json');
    $client->setAccessType('offline');
    $client->setPrompt('select_account consent');

    // Load previously authorized token from a file, if it exists.
    // The file token.json stores the user's access and refresh tokens, and is
    // created automatically when the authorization flow completes for the first
    // time.
    $tokenPath = 'token.json';
    if (file_exists($tokenPath)) {
        $accessToken = json_decode(file_get_contents($tokenPath), true);
        $client->setAccessToken($accessToken);
    }

    // If there is no previous token or it's expired.
    if ($client->isAccessTokenExpired()) {
        // Refresh the token if possible, else fetch a new one.
        if ($client->getRefreshToken()) {
            $client->fetchAccessTokenWithRefreshToken($client->getRefreshToken());
        } else {
            // Request authorization from the user.
            $authUrl = $client->createAuthUrl();
            printf("Open the following link in your browser:\n%s\n", $authUrl);
            print 'Enter verification code: ';
            $authCode = trim(fgets(STDIN));

            // Exchange authorization code for an access token.
            $accessToken = $client->fetchAccessTokenWithAuthCode($authCode);
            $client->setAccessToken($accessToken);

            // Check to see if there was an error.
            if (array_key_exists('error', $accessToken)) {
                throw new Exception(join(', ', $accessToken));
            }
        }
        // Save the token to a file.
        if (!file_exists(dirname($tokenPath))) {
            mkdir(dirname($tokenPath), 0700, true);
        }
        file_put_contents($tokenPath, json_encode($client->getAccessToken()));
    }
    return $client;
}


/**
 * Shows basic usage of the Apps Script API.
 *
 * Call the Apps Script API to create a new script project, upload files to the
 * project, and log the script's URL to the user.
 */
$client = getClient();
$service = new Google_Service_Script($client);

// Create a management request object.
$request = new Google_Service_Script_CreateProjectRequest();
$request->setTitle('console script 44400');
$response = $service->projects->create($request);

$scriptId = $response->getScriptId();

$code = <<<EOT
function customFunction(e) {
  var range = e.range;
  range.setNote('Last modified: ' + new Date());
  var ui = SpreadsheetApp.getUi();
  ui.alert('text');
}

ScriptApp.newTrigger('customFunction')
    .forSpreadsheet('xxxxxxxxxxxx')
    .onEdit()
    .create();
EOT;
$file1 = new Google_Service_Script_ScriptFile();
$file1->setName('hello 111100');
$file1->setType('SERVER_JS');
$file1->setSource($code);

$manifest = <<<EOT
{
  "timeZone": "America/New_York",
  "exceptionLogging": "CLOUD",
    "oauthScopes": [
    "https://www.googleapis.com/auth/spreadsheets.readonly",
    "https://www.googleapis.com/auth/userinfo.email",
    "https://www.googleapis.com/auth/script.scriptapp",
    "https://www.googleapis.com/auth/spreadsheets",
    "https://www.googleapis.com/auth/script.external_request",
    "https://www.googleapis.com/auth/drive",
    "https://www.googleapis.com/auth/drive.scripts"
  ]
}
EOT;
$file2 = new Google_Service_Script_ScriptFile();
$file2->setName('appsscript');
$file2->setType('JSON');
$file2->setSource($manifest);

$request = new Google_Service_Script_Content();
$request->setScriptId($scriptId);
$request->setFiles([$file1, $file2]);



$response = $service->projects->updateContent($scriptId, $request);
echo "https://script.google.com/d/" . $response->getScriptId() . "/edit\n";

  $request = new \Google_Service_Script_ExecutionRequest();
$request->setFunction('customFunction');
$response = $service->scripts->run($scriptId, $request);

I got the response as

  Fatal error: Uncaught Google_Service_Exception: {
  "error": {
    "code": 404,
    "message": "Requested entity was not found.",
    "errors": [
      {
        "message": "Requested entity was not found.",
        "domain": "global",
        "reason": "notFound"
      }
    ],
    "status": "NOT_FOUND"
  }
}

the error is due to

$request = new \Google_Service_Script_ExecutionRequest();
$request->setFunction('customFunction');
$response = $service->scripts->run($scriptId, $request);

i see similar errors on internet .but the solutions don't work for me. i pass all the necessary scopes

'https://www.googleapis.com/auth/script.projects','https://www.googleapis.com/auth/script.scriptapp','https://www.googleapis.com/auth/spreadsheets','https://www.googleapis.com/auth/spreadsheets.readonly','https://www.googleapis.com/auth/script.external_request','https://www.googleapis.com/auth/script.deployments'

between as a side note when i try to run the script manually in the script editor and try editing in the google sheet the above script work and the alert box shown. but this is not practical in case of user who connected there google account.we cannot guarantee that user run script manually to make things work.we need the api to run the script and make the trigger working.Please help

Edited version code added

$request1=new Google_Service_Script_Version();
$request1->setScriptId($scriptId);
$request1->setVersionNumber(1);
$service->projects_versions->create($scriptId,$request1);

added above script run API code but still get same error

  • Have you checked that script was deployed with new version after you added `customFunction` to it? – Kos Feb 12 '20 at 18:24
  • thanks for replying.in app script editor? –  Feb 12 '20 at 18:25
  • Does this answer your question? [404 error when deploying a Google Apps script via the REST API](https://stackoverflow.com/questions/55453933/404-error-when-deploying-a-google-apps-script-via-the-rest-api) – Kos Feb 12 '20 at 18:26
  • the point is i miss a version number.i can set via api right? –  Feb 12 '20 at 18:28
  • version code added but still same error happend –  Feb 12 '20 at 18:56
  • You're mixing up oAuth2 tokens with a service account, these are two different authentication flows. Just use `putenv('GOOGLE_APPLICATION_CREDENTIALS=/path/to/service-account.json')` & `$client->useApplicationDefaultCredentials()`. – Martin Zeitler Feb 12 '20 at 19:41
  • From the error message, how about clicking the save button on the script editor of Google Apps Script, and trying again? But if this was not the direct solution, I apologize. – Tanaike Feb 12 '20 at 23:07
  • @martin thanks for the reply. But I am not sure where I am mixing oauth2 token with a service account.can you please point out. Between my application need oauth flow. –  Feb 13 '20 at 03:00
  • @tanaike. Nope that is not possible. That needs to be automated by script api –  Feb 13 '20 at 03:01
  • Thank you for replying. I apologize for the inconvenience. I couldn't understand about `That needs to be automated by script api`. – Tanaike Feb 13 '20 at 04:55
  • Thanks for your reply.which means the script is run via an api request once user connected google account instead of the user go to scripts.google.com and press run button –  Feb 13 '20 at 07:20
  • @Tanaike did you have any thoughts on this? –  Feb 13 '20 at 12:36

1 Answers1

0

I've got the same problem and the solution is quite easy. You need to deploy your project as "API Exeutable". That is all. But for such deploying you have to set Google Cloud Platform (GCP) Project Number in Project Settings.