I am creating a web site to interact with Google Calendars and watching resources and I want to stop them, but I can't seem to do that, so Google sends the headers "X-Goog-Channel-Id" and "X-Goog-Resource-Id" with the webhook request which from the documentation seems like that's all that's needed to send back to stop them, but I just keep getting a:
Google\Service\Exception: {
"error": {
"errors": [
{
"domain": "global",
"reason": "notFound",
"message": "Channel '0PAA4Z9RXJYMA7YMAV6O' not found for project '309331158475'"
}
],
"code": 404,
"message": "Channel '0PAA4Z9RXJYMA7YMAV6O' not found for project '309331158475'"
}
}
But they should be found as that's what Google has just sent in the header of the webhook. What am I doing wrong?
$headers = getallheaders();
try{
$client = new Google_Client();
$client->setAccessToken(get_google_accesstoken());
$service = new Google_Service_Calendar($client);
$channel = new Google_Service_Calendar_Channel($service);
$channel->setId($headers['X-Goog-Channel-Id']);
$channel->setResourceId($headers['X-Goog-Resource-Id']);
$service->channels->stop($channel);
}catch(Exception $e){
echo $e->getMessage();
}
So the steps I have currently are registering the watch event for the calendar, all good here. Then when the calendar changes Google loads the URL /webhook/google/ on my site and just for concept on that page I have the code above to stop the webhook from happening again, but it shows the error.
I'm generating the watch event with the code below if that helps
$expire = time()+86400;
try {
$client = new Google_Client();
$client->setAccessToken(get_google_accesstoken());
$service = new Google_Service_Calendar($client);
$channel = new Google_Service_Calendar_Channel($client);
$channel->setId(generaterandomstring(20));
$optParams = array('ttl' => $expire);
$channel->setParams($optParams);
$channel->setType('web_hook');
$channel->setAddress($site_url.'/webhook/google/');
$watchEvent = $service->events->watch('email#mysite.com', $channel);
}catch(Exception $e) {
}
I'd guess it's because the channel has already expired.
The $expire = time()+86400 line makes it seem like you're making it expire in 86.4 seconds. Could it be that you're trying to stop the channel watch more than 86 seconds after it was created?
Related
I am using google API php client and getting below error. This code is working fine before a week. Current its not working and passing error. may be some deprecated by google after that its not working. Please help me out as soon as possible because its affect on live product and can't able to show review.
$http = new GuzzleHttp\Client([
'verify' => false
]);
$client = new Google_Client();
$client->setHttpClient($http);
$client->setApplicationName('Magic Minds WEB');
$client->setAuthConfigFile(CLIENT_SECRET_PATH);
$client->setRedirectUri(redirectUri);
$client->setScopes("https://www.googleapis.com/auth/business.manage");
$client->setAccessType("offline");
$client->setApprovalPrompt("force");
$mybusinessService = new Google_Service_MyBusiness($client);
$credentialsPath = tokenJson;
// Load previously authorized credentials from a file.
$accessToken = (array)json_decode(file_get_contents($credentialsPath));
$client->setAccessToken($accessToken);
// Refresh the token if it's expired.
if ($client->isAccessTokenExpired()) {
$client->refreshToken($client->getRefreshToken());
file_put_contents($credentialsPath, json_encode($client->getAccessToken()));
}
// For testing purposes, selects the very first account in the accounts array
$accounts = $mybusinessService->accounts;
// echo "<pre>";
//print_r($accounts);
$accountsList = $accounts->listAccounts()->getAccounts();
print_r($accountsList);
$account = $accountsList[2];
// For testing purposes, selects the very first location in the locations array
$locations = $mybusinessService->accounts_locations;
$locationsList = $locations->listAccountsLocations($account->name)->getLocations();
$location = $locationsList[0];
// Lists all reviews for the specified location
$reviews = $mybusinessService->accounts_locations_reviews;
$listReviewsResponse = $reviews->listAccountsLocationsReviews($location->name);
$reviewsList = $listReviewsResponse->getReviews();
Getting below error
Fatal error: Uncaught Google\Service\Exception: { "error": { "code": 400, "message": "Request contains an invalid argument.", "errors": [ { "message": "Request contains an invalid argument.", "domain": "global", "reason": "badRequest" } ], "status": "INVALID_ARGUMENT", "details": [ { "#type": "type.googleapis.com/google.mybusiness.v4.ValidationError", "errorDetails": [ { "message": "This API will soon be deprecated. Please migrate all the usages to My Business Account Management API - https://developers.google.com/my-business/reference/accountmanagement/rest" } ] } ] } } in /var/www/html/magicmind/magicmindsweb/backend/vendor/google/apiclient/src/Http/REST.php:128 Stack trace: #0 /var/www/html/magicmind/magicmindsweb/backend/vendor/google/apiclient/src/Http/REST.php(103): Google\Http\REST::decodeHttpResponse() #1 [internal function]:
efforts will be appreciated. Thanks in advance
Google is shutting down that api as stated in the error message. They have stated
Starting April 30, 2022, the following four API methods will return errors with increasing frequency, ramping up to 100% shut down within 30 days.
As today is June 07, 2022 You are past the 30 day grace period. So the error message you are getting is the result of that.
This API will soon be deprecated. Please migrate all the usages to My Business Account Management API - https://developers.google.com/my-business/reference/accountmanagement/rest"
I would just start to migrate to the new api as they have directed, This is not something you can fix as that API no longer exists.
See accounts management api
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
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.
I'm trying to create an automation script with php which adds a new row of information to a google sheet that I've shared. I've been scouring tutorials all morning trying to figure this out but each method is different and doesn't seem to be exactly what I need to do. Essentially this script will be triggers when a form is submitted on my site. Its going to take the form data and drop is into my google sheet.
I've created a service account under my project, which is stored inside the json file that was downloaded. Here's the script I have now. It's giving me the following error
{ "error": {
"code": 403,
"message": "The caller does not have permission",
"errors": [ {
"message": "The caller does not have permission",
"domain": "global",
"reason": "forbidden"
} ],
"status": "PERMISSION_DENIED"
}
and here's the code I'm running. (yes its incomplete because I can't get past the auth)
public function index()
{
$this->load->helper("url");
require APPPATH . 'third_party/GoogleAPI/vendor/autoload.php';
putenv('GOOGLE_APPLICATION_CREDENTIALS=' . APPPATH . 'third_party/GoogleAPI/bdgsheets.json');
$client = new Google_Client;
$client->useApplicationDefaultCredentials();
$client->setApplicationName("Is this working");
$client->setScopes(['https://www.googleapis.com/auth/drive','https://spreadsheets.google.com/feeds']);
$client->setAccessType('offline');
if ($client->isAccessTokenExpired()) {
$client->refreshTokenWithAssertion();
}
$accessToken = $client->fetchAccessTokenWithAssertion()["access_token"];
$sheets = new Google_Service_Sheets($client);
$data = [];
$current_row = 2;
$spreadsheet_id = "{my spreadsheet id here}";
$range = 'A1:H';
$rows = $sheets->spreadsheets_values->get($spreadsheet_id, $range, ['majorDimension' => 'ROWS']);
}
Thanks in advance for the help
Have you put the right scope when adding creating the access token. If you had used the scope value
"https://www.googleapis.com/auth/spreadsheets.readonly" instead of
"https://www.googleapis.com/auth/spreadsheets" you won't have write permission to the google sheet.
If you can share the code piece which creates the bdgsheets.json file it is easy to understand whether you have the "write" permission.
Also, try changing this piece of code
$client->setScopes(['https://www.googleapis.com/auth/drive','https://spreadsheets.google.com/feeds']);
to
$client->setScopes(['https://www.googleapis.com/auth/spreadsheets']);
and give it a shot
I'm implementing a cron fetching automatically my application installation stats on Google Play Store. So, I need to download a file from Google Cloud Storage and parse it. I made this code :
// Set scopes to authorize full control of Cloud Storage
$scopes = array("https://www.googleapis.com/auth/cloud-platform", "https://www.googleapis.com/auth/devstorage.full_control");
// Create new Google Client
$client = new Google_Client();
// Set path to ServiceAccount's credentials and link them to Google Client
putenv('GOOGLE_APPLICATION_CREDENTIALS=../../libs/GoogleAPICredentials/client_credentials_CPTS.json');
$client->useApplicationDefaultCredentials();
$client->setScopes($scopes);
// Create new Google Service Storage object to handle our repository
$service = new Google_Service_Storage($client);
// Get content of GCS repository
$request = $service->objects->listObjects("MY_BUCKET");
// Settings folders, files, buckets, names
$sourceBucket = "pubsite_prod_rev_BUCKET_IDENTIFIER";
$sourceObject = "installs_MY_APP_IDENTIFIER_" . $date->format("Ym") . "_overview.csv";
$localFile = "../files/temp/installs_MY_APP_IDENTIFIER_" . $date->format("Ym") . "_overview.csv";
$fullSrcObj = "stats/installs/" . $sourceObject;
$destinationBucket = "MY_BUCKET";
$destinationObject = $sourceObject;
// Create new Google Service Storage StorageObject object to handle a single object (file)
$postBody = new Google_Service_Storage_StorageObject($client);
try {
// Copy stats app .CSV file from orignal repository (Google Play Store) to our bucket
$response = $service->objects->copy($sourceBucket, $fullSrcObj, $destinationBucket, $destinationObject, $postBody);
// Get the new object
try {
$object = $service->objects->get($destinationBucket, $destinationObject);
}
catch (Google_Service_Exception $e) {
if ($e->getCode() == 404)
echo ("[<b><span style=\"color:#DA4F34\">ERROR</span></b>] <b><span style=\"color:#DA4F34\">". $e->getMessage() ."</span></b><br><br>");
throw $e;
}
// Download the new object on the MY_COMPAGNY server
$uri = sprintf('https://storage.googleapis.com/%s/%s?alt=media&generation=%s', $destinationBucket, $destinationObject, $object->generation);
$http = $client->authorize();
$response = $http->get($uri);
if ($response->getStatusCode() != 200) {
echo ("[<b><span style=\"color:#DA4F34\">ERROR</span></b>] <b><span style=\"color:#DA4F34\">An unhandled error append !</span></b><br><br>");
die();
}
// Save it into local file on MY_COMPAGNY server
file_put_contents($localFile, $response->getBody());
// Convert .CSV into php array
$csv = array_map('str_getcsv', file($localFile));
// Delete local file
unlink($localFile);
// Remove last line of CSV (Array) --> Empty - And remove first line of CSV (Array) --> Title line
array_shift($csv);
array_pop($csv);
// Insert each line into MY_COMPAGNY Database
$success = false;
foreach ($csv as $row) {
$success = insertAppInstallData(filter_var($row[0], FILTER_SANITIZE_STRING), (int)filter_var($row[$columnTotal], FILTER_SANITIZE_NUMBER_INT), (int)filter_var($row[$columnCurrent], FILTER_SANITIZE_NUMBER_INT))?true: $success;
}
if (!$success)
echo ("[<b><span style=\"color:#DA4F34\">ERROR</span></b>] <b><span style=\"color:#DA4F34\">DataBase insertion failure</span></b><br><br>");
}
catch (Google_Service_Exception $E) {
echo ("[<b><span style=\"color:#DA4F34\">ERROR</span></b>] <b><span style=\"color:#DA4F34\">".$E->getMessage()." - ".$E->getTraceAsString()."</span></b><br><br>");
}
With an user account, it's work well but with a Service account, I have a 403 error, Forbidden on the call to $service->objects->copy().
{
"error":
{
"errors": [
{
"domain": "global",
"reason": "forbidden",
"message": "Forbidden"
}],
"code": 403,
"message": "Forbidden"
}
}
I checks twice if there are good credentials, IAM authorizations, OAuth client, ... and it still dosent work.
I'm not on a Google Engine, I running on a personal Engine.
Someone have an idea ?
Have a good day and Happy New Year !
Ben.
The reason you are getting error 403 is because you are missing the domain wide delegation and the user impersonation step. Please refer to the documentation here to understand how you can access user data with service accounts.
The process is like this:
$scopes = array("https://www.googleapis.com/auth/cloud-platform", "https://www.googleapis.com/auth/devstorage.full_control");
$client = new Google_Client();
$client->useApplicationDefaultCredentials();
$client->addScope($scopes); //Set scopes to client object
$client->setSubject("user#domain.com"); // replace with the user account
You are able to make it work with a user account because the user already has access to the resource but not the service account, hence the error 403 forbidden. Unfortunately, the domain wide delegation process and user impersonation only works with G Suite accounts( formerly Google Apps ). Another alternative would be to share the resources with the service account but that is something that I havenĀ“t explored yet and not sure if it is possible. You can also refer to the PHP Client Lib documentation here. I hope this information is helpful.
I'm trying to set publish permissions for gmail to a pubsub topic in google cloud.
The application where I implemented this code is running in AWS.
It's a PHP application and I'm using version 2.0.0-RC7 of the google PHP api client.
In code, I implemented the flow as described in the documentation:
Create a topic (Works)
Create a subscription (works)
Grant publish rights to gmail (here I get stuck)
The first two actions are done with the same google client instance, that is authenticated with the service account credentials.
The code:
$scopes = [
'https://www.googleapis.com/auth/pubsub',
'https://mail.google.com',
];
$pushEndpoint = 'https://some.url/google_notifications/';
$client = new Google_Client();
$client->setScopes($scopes);
$client->setAuthConfig($serviceAccountInfo);
if ($client->isAccessTokenExpired()) {
$client->refreshTokenWithAssertion();
}
$service = new Google_Service_Pubsub($client);
// This part works
$topicObject = new Google_Service_Pubsub_Topic();
$topicObject->setName($this->getTopicName());
$service->projects_topics->create($this->getTopicName(), $topic);
// This part also works
$push = new Google_Service_Pubsub_PushConfig();
$push->setPushEndpoint($pushEndpoint);
$subscription = new Google_Service_Pubsub_Subscription();
$subscription->setName($this->getSubscriptionName());
$subscription->setTopic($this->getTopicName());
$subscription->setPushConfig($push);
$service->projects_subscriptions->create($this->getSubscriptionName(), $subscription);
// This part gives the error
$binding = new Google_Service_Pubsub_Binding();
$binding->setRole('roles/pubsub.publisher');
$binding->setMembers(['serviceAccount:gmail-api-push#system.gserviceaccount.com']);
$policy = new Google_Service_Pubsub_Policy();
$policy->setBindings([$binding]);
$setRequest = new Google_Service_Pubsub_SetIamPolicyRequest();
$setRequest->setPolicy($policy);
try {
$result = $service->projects_topics->setIamPolicy($this->getTopicName(), $setRequest);
var_dump($result);
} catch (\Exception $e) {
echo $e->getMessage();
}
The result is always:
{
"error": {
"code": 403,
"message": "User not authorized to perform this action.",
"errors": [
{
"message": "User not authorized to perform this action.",
"domain": "global",
"reason": "forbidden"
}
],
"status": "PERMISSION_DENIED"
}
}
Can someone tell me what I'm doing wrong ?
It's really annoying to set those permissions by hand all the time.
Thanks.
To use projects.topics.setIamPolicy method your service account must have a "Pub/Sub Admin" role. You can change a role of the account by visiting "IAM & admin" page for your project: https://console.cloud.google.com/iam-admin/iam