aiogoogle

repository·master·Indexed 42 days ago

https://github.com/omarryhan/aiogoogle

An asynchronous Python client for non-blocking access to Google's public APIs, including Gmail, Drive, Calendar, Sheets, Docs, Maps, YouTube, and Translate. It provides a Pythonic wrapper for the Google Discovery Service, supporting multiple authentication methods such as OAuth2, OpenID Connect, Service Accounts, and API keys.

Tokens
7.2K
Snippets
26
Records
31
Agent score
51%

What's inside aiogoogle

  1. Overview of Aiogoogle

    master
    Aiogoogle is an asynchronous Python client for accessing Google's public APIs. It provides async support for a wide range of services, allowing developers to integrate Google features into asynchronous applications without blocking the event loop. Supported APIs include Google Calendar, Drive, Gmail, Sheets, Docs, Maps, YouTube, Translate, and many others.
  2. How the Discovery Service and API structure works

    master

    Aiogoogle is a Pythonic wrapper for Google's Discovery Service. Instead of hardcoding every API, it downloads machine-readable specifications (discovery documents) to build an API object dynamically.

    API Hierarchy

    • Resources: Represent entities (e.g., videos, url). Resources can be nested (e.g., youtube_v3.videos.comments).
    • Methods: The actual actions you can perform on a resource (e.g., list, get, insert).

    Crucial Rule: You cannot call a resource directly to get data; you must call a method associated with that resource.

  3. Handle API Pagination with `full_res=True`

    master

    When an API response contains multiple pages of data, you can use the full_res=True argument in .as_user() (or .as_api_key()). This returns an asynchronous iterator that allows you to loop through all pages of results.

    Instead of a single dictionary, the returned object is an AsyncIterable of pages.

    async def list_files():
        async with Aiogoogle(user_creds=user_creds) as aiogoogle:
            drive_v3 = await aiogoogle.discover('drive', 'v3')
            # full_res=True enables pagination
            full_res = await aiogoogle.as_user(
                drive_v3.files.list(),
                full_res=True
            )
        # Iterate through each page asynchronously
        async for page in full_res:
            for file in page['files']:
                print(file['name'])
    
    asyncio.run(list_files())
  4. Browse Google Discovery Documents Manually

    master

    Before writing code, you can manually inspect the structure of a Google API by accessing its discovery document. This helps you identify resources, methods, and required parameters.

    1. Use the following URL pattern in your browser: https://www.googleapis.com/discovery/v1/apis/{api}/{version}/rest Example for YouTube v3: https://www.googleapis.com/discovery/v1/apis/youtube/v3/rest
    2. Use a JSON viewer extension to navigate the structure.
    3. Follow the hierarchy: resources $\rightarrow$ [resource_name] $\rightarrow$ methods $\rightarrow$ [method_name] $\rightarrow$ parameters to understand how to construct your aiogoogle calls.
  5. Integrate OpenID Connect for user authentication

    master

    Use OpenID Connect (OIDC) if you need to identify the user (e.g., for 'Social Sign-in'). OIDC is a superset of OAuth2; it provides the same access tokens but also returns an ID token containing user 'claims' (like sub, email, etc.).

    1. Generate Authorization URL

    Use aiogoogle.openid_connect.authorization_url. You should provide a nonce to prevent replay attacks.

    2. Handle the Callback

    Exchange the grant code for UserCreds and then use aiogoogle.openid_connect.get_user_info to retrieve the user's identity claims.

    Note: Use the sub claim to uniquely identify users. Do not rely on email for unique identification, as emails can change.

    # 1. Generate URL with nonce
    uri = aiogoogle.openid_connect.authorization_url(
        client_creds={
            'client_id': '...',
            'client_secret': '...',
            'redirect_uri': '...',
            'scopes': ['...']
        },
        nonce='random_nonce_value'
    )
    
    # 2. Handle callback
    full_user_creds = await aiogoogle.openid_connect.build_user_creds(
        grant=request.args.get('code'),
        client_creds=CLIENT_CREDS,
        nonce='random_nonce_value',
        verify=False
    )
    
    # 3. Get user identity claims
    full_user_info = await aiogoogle.openid_connect.get_user_info(full_user_creds)
    # full_user_info contains claims like 'sub', 'email', etc.
  6. Authenticate using GOOGLE_APPLICATION_CREDENTIALS environment variable

    master

    You can point the GOOGLE_APPLICATION_CREDENTIALS environment variable to your JSON key file location. To make Aiogoogle load these credentials, you must call await aiogoogle.service_account_manager.detect_default_creds_source() before making API calls.

    export GOOGLE_APPLICATION_CREDENTIALS="location_of_key_file.json"
    import asyncio
    from aiogoogle import Aiogoogle
    from aiogoogle.auth.creds import ServiceAccountCreds
    
    creds = ServiceAccountCreds(scopes=["https://www.googleapis.com/auth/cloud-platform"])
    
    async def list_storage_buckets(project_id=creds["project_id"]):
        aiogoogle = Aiogoogle(service_account_creds=creds)
    
        # Load the service account key from the environment variable
        await aiogoogle.service_account_manager.detect_default_creds_source()
    
        async with aiogoogle:
            storage = await aiogoogle.discover("storage", "v1")
            res = await aiogoogle.as_service_account(
                storage.buckets.list(project=creds["project_id"])
            )
            print(res)
    
    if __name__ == "__main__":
        asyncio.run(list_storage_buckets())
  7. Authenticate using an API Key for public data

    master

    For accessing public data where no user authorization is required, you can use a simple API key. Pass the api_key string to the Aiogoogle constructor and use aiogoogle.as_api_key() to execute calls.

    async with Aiogoogle(api_key='YOUR_API_KEY') as aiogoogle:
        google_api = await aiogoogle.discover('google_api', 'v1')
        result = await aiogoogle.as_api_key(
            google_api.foo.bar(baz='foo')
        )
  8. Set up Google Cloud project and credentials

    master

    Before using aiogoogle, you must configure your environment in the Google Cloud Console:

    1. Create a project: Use the Google APIs and Services dashboard.
    2. Enable an API: Select the specific API you need (e.g., Gmail, Drive, Calendar) in the API Library.
    3. Create credentials: Use the Credentials wizard to generate your client_id and client_secret.
    4. Explore APIs: Use Google's APIs Explorer to find the specific methods you need.
  9. Authenticate using a Service Account key file

    master

    You can authenticate using a user-managed service account JSON key file by passing its contents to ServiceAccountCreds. This is useful for applications that need to perform actions on their own. When using this method, use aiogoogle.as_service_account() to execute the discovered API calls.

    import json
    import asyncio
    from aiogoogle import Aiogoogle
    from aiogoogle.auth.creds import ServiceAccountCreds
    
    # Load the JSON key file
    service_account_key = json.load(open('test_service_account.json'))
    
    # Initialize credentials with required scopes
    creds = ServiceAccountCreds(
        scopes=[
            "https://www.googleapis.com/auth/devstorage.read_only",
            "https://www.googleapis.com/auth/devstorage.read_write",
        ],
        **service_account_key
    )
    
    async def list_storage_buckets(project_id=creds["project_id"]):
        async with Aiogoogle(service_account_creds=creds) as aiogoogle:
            storage = await aiogoogle.discover("storage", "v1")
            # Use as_service_account to wrap the API call
            res = await aiogoogle.as_service_account(
                storage.buckets.list(project=creds["project_id"])
            )
            print(res)
    
    if __name__ == "__main__":
        asyncio.run(list_storage_buckets())
  10. Programmatically browse and inspect an API

    master

    Once you have discovered an API using await aiogoogle.discover(name, version), you can inspect its structure programmatically:

    1. List Resources: Access api_object['resources'].
    2. Access a Resource: Use dot notation (e.g., api_object.resource_name).
    3. Check Available Methods: Use resource.methods_available.
    4. Inspect a Method: Access the method object (e.g., resource.method_name) to see:
      • description: A string describing the method.
      • optional_parameters: A list of parameter names.
      • required_parameters: A list of parameter names.
      • parameters: A dictionary containing detailed metadata (type, description, location) for each parameter.
    # Example: Inspecting a method
    url_resource = url_shortener.url
    print(url_resource.methods_available)  # ['get', 'insert', 'list']
    
    list_url = url_resource.list
    print(list_url.optional_parameters)    # ['projection', 'start_token', ...]
    print(list_url.parameters['start_token'])
  11. Download and Upload files to Google Drive

    master

    Use the Google Drive API via aiogoogle to manage files.

    • To download: Use files.get() with download_file=path and alt='media' inside an .as_user() call.
    • To upload: Use files.create() with the upload_file=path parameter.
    • To upload via stream: Use files.create() with the pipe_from=iterable parameter, where iterable is an AsyncIterable yielding chunks.
    # Download
    await aiogoogle.as_user(
        drive_v3.files.get(fileId=file_id, download_file=path, alt='media'),
    )
    
    # Upload file
    await aiogoogle.as_user(
        drive_v3.files.create(upload_file=path)
    )
    
    # Upload via AsyncIterable
    async def yield_file():
        yield b'chunk1'
        yield b'chunk2'
    
    await aiogoogle.as_user(
        drive_v3.files.create(pipe_from=yield_file())
    )