Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,10 @@
nylas-python Changelog
======================

Unreleased
----------------
* Added response headers to all responses from the Nylas API

v6.5.0
----------------
* Added support for Scheduler APIs
Expand Down
107 changes: 107 additions & 0 deletions examples/response_headers_demo/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,107 @@
# Response Headers Demo

This example demonstrates how to access and use response headers from various Nylas API responses. It shows how headers are available in different types of responses:

1. List responses (from methods like `list()`)
2. Single-item responses (from methods like `find()`)
3. Error responses (when API calls fail)

## What You'll Learn

- How to access response headers from successful API calls
- How to access headers from error responses
- Common headers you'll encounter in Nylas API responses
- How headers differ between list and single-item responses

## Headers Demonstrated

The example will show various headers that Nylas includes in responses, such as:

- `request-id`: Unique identifier for the API request
- `x-ratelimit-limit`: Your rate limit for the endpoint
- `x-ratelimit-remaining`: Remaining requests within the current window
- `x-ratelimit-reset`: When the rate limit window resets
- And more...

## Prerequisites

Before running this example, make sure you have:

1. A Nylas API key
2. A Nylas grant ID
3. Python 3.7 or later installed
4. The Nylas Python SDK installed

## Setup

1. First, install the SDK in development mode:
```bash
cd /path/to/nylas-python
pip install -e .
```

2. Set up your environment variables:
```bash
export NYLAS_API_KEY="your_api_key"
export NYLAS_GRANT_ID="your_grant_id"
```

## Running the Example

Run the example with:
```bash
python examples/response_headers_demo/response_headers_example.py
```

The script will:
1. Demonstrate headers from a list response by fetching messages
2. Show headers from a single-item response by fetching one message
3. Trigger and catch an error to show error response headers

## Example Output

You'll see output similar to this:

```
Demonstrating Response Headers
============================

Demonstrating List Response Headers
----------------------------------
✓ Successfully retrieved messages

Response Headers:
------------------------
request-id: req_abcd1234
x-ratelimit-limit: 1000
x-ratelimit-remaining: 999
...

Demonstrating Find Response Headers
----------------------------------
✓ Successfully retrieved single message

Response Headers:
------------------------
request-id: req_efgh5678
...

Demonstrating Error Response Headers
---------------------------------
✓ Successfully caught expected error
✗ Error Type: invalid_request
✗ Request ID: req_ijkl9012
✗ Status Code: 404

Error Response Headers:
------------------------
request-id: req_ijkl9012
...
```

## Error Handling

The example includes proper error handling and will show you how to:
- Catch `NylasApiError` exceptions
- Access error details and headers
- Handle different types of API errors gracefully
161 changes: 161 additions & 0 deletions examples/response_headers_demo/response_headers_example.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,161 @@
#!/usr/bin/env python3
"""
Nylas SDK Example: Response Headers Demo

This example demonstrates how to access and use response headers from various Nylas API
responses, including successful responses and error cases.

Required Environment Variables:
NYLAS_API_KEY: Your Nylas API key
NYLAS_GRANT_ID: Your Nylas grant ID

Usage:
First, install the SDK in development mode:
cd /path/to/nylas-python
pip install -e .

Then set environment variables and run:
export NYLAS_API_KEY="your_api_key"
export NYLAS_GRANT_ID="your_grant_id"
python examples/response_headers_demo/response_headers_example.py
"""

import os
import sys
from typing import Optional

from nylas import Client
from nylas.models.errors import NylasApiError


def get_env_or_exit(var_name: str) -> str:
"""Get an environment variable or exit if not found."""
value = os.getenv(var_name)
if not value:
print(f"Error: {var_name} environment variable is required")
sys.exit(1)
return value


def print_response_headers(headers: dict, prefix: str = "") -> None:
"""Helper function to print response headers."""
print(f"\n{prefix} Response Headers:")
print("------------------------")
for key, value in headers.items():
print(f"{key}: {value}")


def demonstrate_list_response_headers(client: Client, grant_id: str) -> None:
"""Demonstrate headers in list responses."""
print("\nDemonstrating List Response Headers")
print("----------------------------------")

try:
# List messages to get a ListResponse
messages = client.messages.list(identifier=grant_id)

print("✓ Successfully retrieved messages")
print_response_headers(messages.headers)
print(f"Total messages count: {len(messages.data)}")

except NylasApiError as e:
print("\nError occurred while listing messages:")
print(f"✗ Error Type: {e.type}")
print(f"✗ Provider Error: {e.provider_error}")
print(f"✗ Request ID: {e.request_id}")
print_response_headers(e.headers, "Error")


def demonstrate_list_response_headers_with_pagination(client: Client, grant_id: str) -> None:
"""Demonstrate headers in list responses with pagination."""
print("\nDemonstrating List Response Headers with Pagination")
print("--------------------------------------------------")

try:
# List messages to get a ListResponse
threads = client.threads.list(identifier=grant_id, query_params={"limit": 1})

print("✓ Successfully retrieved threads")
print_response_headers(threads.headers)
print(f"Total threads count: {len(threads.data)}")

except NylasApiError as e:
print("\nError occurred while listing threads:")
print(f"✗ Error Type: {e.type}")
print(f"✗ Provider Error: {e.provider_error}")
print(f"✗ Request ID: {e.request_id}")
print_response_headers(e.headers, "Error")


def demonstrate_find_response_headers(client: Client, grant_id: str) -> None:
"""Demonstrate headers in find/single-item responses."""
print("\nDemonstrating Find Response Headers")
print("----------------------------------")

try:
# Get the first message to demonstrate single-item response
messages = client.messages.list(identifier=grant_id)
if not messages.data:
print("No messages found to demonstrate find response")
return

message_id = messages.data[0].id
message = client.messages.find(identifier=grant_id, message_id=message_id)

print("✓ Successfully retrieved single message")
print_response_headers(message.headers)

except NylasApiError as e:
print("\nError occurred while finding message:")
print(f"✗ Error Type: {e.type}")
print(f"✗ Provider Error: {e.provider_error}")
print(f"✗ Request ID: {e.request_id}")
print_response_headers(e.headers, "Error")


def demonstrate_error_response_headers(client: Client, grant_id: str) -> None:
"""Demonstrate headers in error responses."""
print("\nDemonstrating Error Response Headers")
print("---------------------------------")

try:
# Attempt to find a non-existent message
message = client.messages.find(
identifier=grant_id,
message_id="non-existent-id-123"
)

except NylasApiError as e:
print("✓ Successfully caught expected error")
print(f"✗ Error Type: {e.type}")
print(f"✗ Provider Error: {e.provider_error}")
print(f"✗ Request ID: {e.request_id}")
print(f"✗ Status Code: {e.status_code}")
print_response_headers(e.headers, "Error")


def main():
"""Main function demonstrating response headers."""
# Get required environment variables
api_key = get_env_or_exit("NYLAS_API_KEY")
grant_id = get_env_or_exit("NYLAS_GRANT_ID")

# Initialize Nylas client
client = Client(
api_key=api_key,
)

print("\nDemonstrating Response Headers")
print("============================")

# Demonstrate different types of responses and their headers
demonstrate_list_response_headers(client, grant_id)
demonstrate_list_response_headers_with_pagination(client, grant_id)
demonstrate_find_response_headers(client, grant_id)
demonstrate_error_response_headers(client, grant_id)

print("\nExample completed!")


if __name__ == "__main__":
main()
28 changes: 16 additions & 12 deletions nylas/handler/api_resources.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,11 +16,11 @@ def list(
request_body=None,
overrides=None,
) -> ListResponse:
response_json = self._http_client._execute(
response_json, response_headers = self._http_client._execute(
"GET", path, headers, query_params, request_body, overrides=overrides
)

return ListResponse.from_dict(response_json, response_type)
return ListResponse.from_dict(response_json, response_type, response_headers)


class FindableApiResource(Resource):
Expand All @@ -33,11 +33,11 @@ def find(
request_body=None,
overrides=None,
) -> Response:
response_json = self._http_client._execute(
response_json, response_headers = self._http_client._execute(
"GET", path, headers, query_params, request_body, overrides=overrides
)

return Response.from_dict(response_json, response_type)
return Response.from_dict(response_json, response_type, response_headers)


class CreatableApiResource(Resource):
Expand All @@ -50,11 +50,11 @@ def create(
request_body=None,
overrides=None,
) -> Response:
response_json = self._http_client._execute(
response_json, response_headers = self._http_client._execute(
"POST", path, headers, query_params, request_body, overrides=overrides
)

return Response.from_dict(response_json, response_type)
return Response.from_dict(response_json, response_type, response_headers)


class UpdatableApiResource(Resource):
Expand All @@ -68,11 +68,11 @@ def update(
method="PUT",
overrides=None,
):
response_json = self._http_client._execute(
response_json, response_headers = self._http_client._execute(
method, path, headers, query_params, request_body, overrides=overrides
)

return Response.from_dict(response_json, response_type)
return Response.from_dict(response_json, response_type, response_headers)


class UpdatablePatchApiResource(Resource):
Expand All @@ -86,11 +86,11 @@ def patch(
method="PATCH",
overrides=None,
):
response_json = self._http_client._execute(
response_json, response_headers = self._http_client._execute(
method, path, headers, query_params, request_body, overrides=overrides
)

return Response.from_dict(response_json, response_type)
return Response.from_dict(response_json, response_type, response_headers)


class DestroyableApiResource(Resource):
Expand All @@ -106,7 +106,11 @@ def destroy(
if response_type is None:
response_type = DeleteResponse

response_json = self._http_client._execute(
response_json, response_headers = self._http_client._execute(
"DELETE", path, headers, query_params, request_body, overrides=overrides
)
return response_type.from_dict(response_json)

# Check if the response type is a dataclass_json class
if hasattr(response_type, "from_dict") and not hasattr(response_type, "headers"):
return response_type.from_dict(response_json)
return response_type.from_dict(response_json, headers=response_headers)
Loading