google-api-python-client icon indicating copy to clipboard operation
google-api-python-client copied to clipboard

Photos mediaItems.search doesn't accept correct parameters

Open alexhayes opened this issue 3 years ago • 3 comments

TL/DR This is not a bug - see my comment.


When attempting to call the search method from the mediaItems resource for Google Photos API with any of the allowed parameters an exception is thrown as follows;

TypeError: Got an unexpected keyword argument albumId

I suspect this has something to do with the discovery URL which, for the method in question, does not define any parameters.

image

As shown, it does define a request with a $ref of SearchMediaItemsRequest, which in the discovery URL is as follows;

image

As far as I can ascertain however google-api-python-client doesn't appear to reference $ref anywhere, so I am assuming it takes the method signature only from parameters defined in the discovery URL.

~~Oddly enough however, when the Resource is created, there are some parameters set for the search method, but these appear to be derived from somewhere else (perhaps oath2.v2.json???) See below;~~

EDIT: It would appear these are set by _fix_up_parameters in discovery.py which happens to all methods.

PyCharm debug showing the incorrect parameters attached to the methodDesc

Given it appears the parameters in the discovery URL are incorrect, I'm unsure if this is a bug in google-api-python-client - if not, please direct me to where I should raise this.

NOTE: Calls without any parameters return the expected result (ie. media items).

Environment details

  • OS type and version: OSX 11.6.4
  • Python version: Python 3.10.0
  • pip version: pip 22.0.4
  • google-api-python-client version: 2.44.0

Steps to reproduce

  1. Retrieve a Resource with serviceName='photoslibrary', version='v1'
  2. Attempt to call service.mediaItems().search(albumId="xyz").execute()

Code example

import os

import typing

from google.auth.transport.requests import Request
from google.oauth2.credentials import Credentials
from google_auth_oauthlib.flow import InstalledAppFlow
from googleapiclient.discovery import build, Resource


def create_credentials(secrets_path: str, scopes: typing.List[str]) -> Credentials:
    path = os.path.splitext(secrets_path)[0]
    tokens_path = f'{path}-cached-token.json'

    if os.path.exists(tokens_path):
        credentials = Credentials.from_authorized_user_file(tokens_path, scopes)
    else:
        credentials = None

    # If there are no (valid) credentials available, let the user log in.
    if not credentials or not credentials.valid:
        if credentials and credentials.expired and credentials.refresh_token:
            credentials.refresh(Request())
        else:
            flow = InstalledAppFlow.from_client_secrets_file(secrets_path, scopes)
            credentials = flow.run_local_server(port=0)

        # Save the credentials for the next run
        with open(tokens_path, 'w') as token:
            token.write(credentials.to_json())
    
    return credentials


def create_photos_service(secrets_path: str) -> Resource:
    credentials = create_credentials(
        secrets_path,
        [
            'https://www.googleapis.com/auth/photoslibrary'
        ]
    )
    
    return build('photoslibrary', 'v1', credentials=credentials, static_discovery=False)


service = create_photos_service("client_secret.json")
media_items = service.mediaItems().search(albumId="xyz").execute()

Stack trace

/path/to/.direnv/python-3.10.0/bin/python /path/to/helpers.py
Traceback (most recent call last):
  File "/path/to/helpers.py", line 59, in <module>
    media_items = service.mediaItems().search(pageSize="xyz").execute()
  File "/path/to/.direnv/python-3.10.0/lib/python3.10/site-packages/googleapiclient/discovery.py", line 1019, in method
    raise TypeError('Got an unexpected keyword argument {}'.format(name))
TypeError: Got an unexpected keyword argument albumId

Process finished with exit code 1

alexhayes avatar Apr 16 '22 11:04 alexhayes

This can be closed as INVALID - it would appear that for methods that are HTTP POST you need to provide a body attribute with the values in it - as follows;

media_items = service.mediaItems().search(body=dict(albumId="xyz")).execute()

That seems somewhat unintuitive to me and I'm not able to find that documented - I just found it by chance as I was digging around StackOverflow (and yes, the issue template says "check SO", I did, but didn't find it initially).

As a consumer of an API, do I really care if it's a POST, GET, PATCH etc..? As an engineer I definitely want to know what happens under the hood, but it feels a bit off to be exposed to that by the interface signature. My assumption is there are good reasons.

If someone would like me to, I'm happy to document this via a PR if there is a particular area of the docs someone thinks is the best place to put it.

alexhayes avatar Apr 17 '22 01:04 alexhayes

Hi @alexhayes,

I agree this information can be difficult to find. In case you're still interested in updating the docs, PRs are welcome !

parthea avatar Apr 29 '22 14:04 parthea

This can be closed as INVALID - it would appear that for methods that are HTTP POST you need to provide a body attribute with the values in it - as follows;

media_items = service.mediaItems().search(body=dict(albumId="xyz")).execute()

That seems somewhat unintuitive to me and I'm not able to find that documented - I just found it by chance as I was digging around StackOverflow (and yes, the issue template says "check SO", I did, but didn't find it initially).

As a consumer of an API, do I really care if it's a POST, GET, PATCH etc..? As an engineer I definitely want to know what happens under the hood, but it feels a bit off to be exposed to that by the interface signature. My assumption is there are good reasons.

If someone would like me to, I'm happy to document this via a PR if there is a particular area of the docs someone thinks is the best place to put it.

I spent hours testing and surfing the internet until I found your comment, thanks !

Victorivus avatar Dec 18 '23 22:12 Victorivus