0

I'm using Google's My Business API via Google's API Python Client Library.

Without further ado, here is a complete code example:

from dotenv import load_dotenv
from google_auth_oauthlib.flow import InstalledAppFlow
from googleapiclient.discovery import build
from os.path import exists
from pprint import pprint
import os
import pickle

load_dotenv()

API_DEVELOPER_KEY = os.getenv('API_DEVELOPER_KEY')
API_SCOPE = os.getenv('API_SCOPE')
STORED_CLIENT_CREDENTIALS = os.getenv('STORED_CLIENT_CREDENTIALS')
GOOGLE_APPLICATION_CREDENTIALS = os.getenv('GOOGLE_APPLICATION_CREDENTIALS')


def get_google_credentials(path=STORED_CLIENT_CREDENTIALS):
    '''Loads stored credentials. Gets and stores new credentials if necessary.'''
    if exists(path):
        pickle_in = open(path, 'rb')
        credentials = pickle.load(pickle_in)
    else:
        flow = InstalledAppFlow.from_GOOGLE_APPLICATION_CREDENTIALS_file(
            GOOGLE_APPLICATION_CREDENTIALS_file=GOOGLE_APPLICATION_CREDENTIALS, scopes=API_SCOPE)
        flow.run_local_server()
        credentials = flow.credentials
        store_google_credentials(credentials)
    return credentials


def store_google_credentials(credentials, path=STORED_CLIENT_CREDENTIALS):
    '''Store credentials for future reuse to avoid authenticating every time.'''
    pickle_out = open(path, 'wb')
    pickle.dump(credentials, pickle_out)
    pickle_out.close()


def get_google_api_interface(credentials, service_name, service_version, service_discovery_url=None):
    '''Get a resource object with methods for interacting with Google's API.'''
    return build(service_name,
                 service_version,
                 credentials=credentials,
                 developerKey=API_DEVELOPER_KEY,
                 discoveryServiceUrl=service_discovery_url)


def extract_dict_key(dict, key):
    '''Utility to extract particular values from a dictionary by their key.'''
    return [d[key] for d in dict]


def transform_list_to_string(list, separator=' '):
    return separator.join(map(str, list))


def get_google_account_names():
    '''Get a list of all account names (unique ids).'''
    google = get_google_api_interface(
        get_google_credentials(),
        service_name='mybusinessaccountmanagement',
        service_version='v1',
        service_discovery_url='https://mybusinessaccountmanagement.googleapis.com/$discovery/rest?version=v1')
    accounts = google.accounts().list().execute()
    return extract_dict_key(accounts['accounts'], 'name')


def get_google_store_reviews(account_name):
    '''Get all store reviews for a specific account from Google My Business.'''
    google = get_google_api_interface(
        get_google_credentials(),
        service_name='mybusiness',
        service_version='v4',
        service_discovery_url='https://mybusiness.googleapis.com/$discovery/rest?version=v4')
    return google.accounts().locations().batchGetReviews(account_name).execute()


account_names = get_google_account_names()

pprint(account_names)

first_account_name = account_names[0]

pprint(get_google_store_reviews(first_account_name))

And here is the contents of .env:

API_DEVELOPER_KEY = ********
API_SCOPE = https://www.googleapis.com/auth/business.manage
STORED_CLIENT_CREDENTIALS = secrets/credentials.pickle
GOOGLE_APPLICATION_CREDENTIALS = secrets/client_secrets.json

My function get_google_account_names() works fine and returns the expected data:

['accounts/******************020',
 'accounts/******************098',
 'accounts/******************872',
 'accounts/******************021',
 'accounts/******************112']

I have tested and validated get_google_credentials() to ensure that CLIENT_CREDENTIALS and API_DEVELOPER_KEY are indeed loaded correctly and working.

Also, in .env, I'm setting the environment variable GOOGLE_APPLICATION_CREDENTIALS to the client_secret.json path, as required some methods in Google's Python Client Library.

My function get_google_store_reviews(), however, results in this error:

Traceback (most recent call last):
  File "/my-project-dir/my-script.py", line 88, in <module>
    pprint(get_google_store_reviews())
  File "/my-project-dir/my-script.py", line 76, in get_google_store_reviews
    google = get_google_api_interface(
  File "/my-project-dir/my-script.py", line 46, in get_google_api_interface
    return build(service_name,
  File "/my-project-dir/.venv/lib/python3.9/site-packages/googleapiclient/_helpers.py", line 131, in positional_wrapper
    return wrapped(*args, **kwargs)
  File "/my-project-dir/.venv/lib/python3.9/site-packages/googleapiclient/discovery.py", line 324, in build
    raise UnknownApiNameOrVersion("name: %s  version: %s" % (serviceName, version))
googleapiclient.errors.UnknownApiNameOrVersion: name: mybusiness  version: v4

I have also tried v1 of the Discovery Document with the same result.

Does anyone know what's going on here? It seems like the API mybusiness is not discoverable via the Discovery Document provided by Google, but I'm not sure how to verify my suspicion.

Note that this and this issue is related, but not exactly the same. The answers in those questions are old don't seem to be applicable anymore after recent changes by Google.

Update:

As a commenter pointed out, this API appears to be deprecated. That might explain the issues I'm having, however, Google's documentation states:

"Deprecated indicates that the version of the API will continue to function […]"

Furthermore, notice that even though the top-level accounts.locations is marked as deprecated, some other the underlying methods (including batchGetReviews) are not.

See screenshot for more details:

Google's Deprecation Dchedule Documentation

This issue has also been reported in GitHub.

leifericf
  • 2,324
  • 3
  • 26
  • 37
  • The client libraries are programmatically built from the [discovery doc](https://developers.google.com/discovery). It the api is not in the discovery doc then its not part of Google apis and you cant use it with that library. – Linda Lawton - DaImTo Mar 25 '22 at 09:44
  • Most of the Google apis for my business were [Deprecation](https://developers.google.com/my-business/content/sunset-dates) as far as i can see – Linda Lawton - DaImTo Mar 25 '22 at 09:46
  • Thank you for the pointers, @DaImTo! I'm specifically trying to get all reviews for many store locations via ```accounts.locations.batchGetReviews``` (https://developers.google.com/my-business/reference/rest/v4/accounts.locations/batchGetReviews) by following this and [this official guide from Google](https://developers.google.com/my-business/content/review-data#get_reviews_from_multiple_locations). I missed the fact that ```accounts.locations.*``` appears to be deprecated. The specific method ```batchGetReviews``` is not marked as deprecated. Very confusing and misleading docs. – leifericf Mar 25 '22 at 10:02
  • accounts.locations.* <-- Check the * It normally means everything under that. Meaning all methods under accounts.locations. accounts.locations.batchGetReviews falls under that. – Linda Lawton - DaImTo Mar 25 '22 at 10:15
  • Ah, my bad. There isn't actually a ```*``` in the deprecation message, I added it to indicate that it might be implied. I will update my question with some more info and a screenshot to show what I'm referring to. – leifericf Mar 25 '22 at 10:54
  • 1
    I think you should cross post this [issue forum](https://github.com/googleapis/google-api-python-client/issues) UnknownApiNameOrVersion means its not being built by your library. Ask over on their issue forum. That being said they cant build it if it was removed from discovery services – Linda Lawton - DaImTo Mar 25 '22 at 11:09
  • 1
    Thanks again, @DaImTo! I have sent opened a support ticket with Google and also created [this GitHub issue](https://github.com/googleapis/google-api-python-client/issues/1735) as you suggested. I'm leaving a link here, just in case someone else stumbles upon the same problem in the future. – leifericf Mar 25 '22 at 11:34

1 Answers1

1

Update:

Regarding the UnknownApiNameOrVersion error:

If you open the discovery link you are using (https://mybusiness.googleapis.com/$discovery/rest?version=v4), you will see that it now only says

{
  "error": {
  "code": 404,
  "message": "Method not found.",
  "status": "NOT_FOUND"
  }
}

Try using this link instead: https://developers.google.com/static/my-business/samples/mybusiness_google_rest_v4p9.json

Regarding correct method usage of the batchGetReviews method:

The method will require a request body containing a locationNames property.

vpgcloud
  • 1,294
  • 2
  • 10
  • 19
  • Thank you for noticing that! I have now changed my function ``get_google_store_reviews(account_name)`` to get reviews for one account only so that I can call it successively for each account, however, the original problem (`UnknownApiNameOrVersion`) still remains. – leifericf Mar 25 '22 at 13:12
  • @vpriesner This does not solves the problem, The issue is still the same. – Nagesh Singh Chauhan Jul 14 '23 at 03:37
  • 1
    @NageshSinghChauhan I updated my answer to reflect the API changes. – vpgcloud Jul 14 '23 at 10:19
  • Thank you, it solves the issue but I face some problems with request = service.accounts().locations().reviews().updateReply( name=f'accounts/{account_id}/locations/{location_id}/reviews/{review_id}', body={'comment': reply_text} ).execute() //404 not found – Nagesh Singh Chauhan Jul 17 '23 at 15:16
  • Please create a separate question for questions regarding review replies, this is not related. – vpgcloud Jul 17 '23 at 15:19