Skip to content

Commit

Permalink
Add metadata field to SendMessageRequest and Message model (#399)
Browse files Browse the repository at this point in the history
* Add metadata field to SendMessageRequest model

- Added metadata field to CreateDraftRequest which is inherited by SendMessageRequest
- Added test cases for metadata in draft creation and message sending
- Updated docstring to include metadata field description

Fixes #394

Co-Authored-By: Aaron de Mello <[email protected]>

* Update test assertion to use keyword arguments

Co-Authored-By: Aaron de Mello <[email protected]>

* Update CHANGELOG.md for metadata field addition

Co-Authored-By: Aaron de Mello <[email protected]>

* Update changelog format to match version entry style

Co-Authored-By: Aaron de Mello <[email protected]>

* Add E2E example for metadata field functionality

Co-Authored-By: Aaron de Mello <[email protected]>

* Update metadata example to use local package instead of pip install

Co-Authored-By: Aaron de Mello <[email protected]>

* Added the metadata property to the Message model

---------

Co-authored-by: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
Co-authored-by: Aaron de Mello <[email protected]>
  • Loading branch information
devin-ai-integration[bot] and AaronDDM authored Dec 20, 2024
1 parent 5922f8c commit e0c4cf2
Show file tree
Hide file tree
Showing 7 changed files with 587 additions and 2 deletions.
3 changes: 2 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,10 @@ nylas-python Changelog
======================

Unreleased
--------------
----------------
* Add support for Scheduler APIs
* Fixed attachment download response handling
* Add metadata field support for drafts and messages through CreateDraftRequest model

v6.4.0
----------------
Expand Down
67 changes: 67 additions & 0 deletions examples/metadata_field_demo/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
# Metadata Field Example

This example demonstrates how to use metadata fields when creating drafts and sending messages using the Nylas Python SDK.

## Features

- Create drafts with custom metadata fields
- Send messages with custom metadata fields
- Error handling and environment variable configuration
- Clear output and status messages

## Prerequisites

1. A Nylas account with API access
2. Python 3.x installed
3. Local installation of the Nylas Python SDK (this repository)

## Setup

1. Install the SDK in development mode from the repository root:
```bash
cd /path/to/nylas-python
pip install -e .
```

2. Set your environment variables:
```bash
export NYLAS_API_KEY="your_api_key"
export NYLAS_GRANT_ID="your_grant_id"
export TEST_EMAIL="[email protected]" # Optional
```

3. Run the example from the repository root:
```bash
python examples/metadata_field_demo/metadata_example.py
```

## Example Output

```
Demonstrating Metadata Field Usage
=================================
1. Creating draft with metadata...
✓ Created draft with ID: draft-abc123
Request ID: req-xyz789
2. Sending message with metadata...
✓ Sent message with ID: msg-def456
Request ID: req-uvw321
Example completed successfully!
```

## Error Handling

The example includes proper error handling for:
- Missing environment variables
- API authentication errors
- Draft creation failures
- Message sending failures

## Documentation

For more information about the Nylas Python SDK and its features, visit:
- [Nylas Python SDK Documentation](https://developer.nylas.com/docs/sdks/python/)
- [Nylas API Reference](https://developer.nylas.com/docs/api/)
137 changes: 137 additions & 0 deletions examples/metadata_field_demo/metadata_example.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,137 @@
#!/usr/bin/env python3
"""
Nylas SDK Example: Using Metadata Fields with Drafts and Messages
This example demonstrates how to use metadata fields when creating drafts
and sending messages using the Nylas Python SDK.
Required Environment Variables:
NYLAS_API_KEY: Your Nylas API key
NYLAS_GRANT_ID: Your Nylas grant ID
TEST_EMAIL: Email address for sending test messages (optional)
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"
export TEST_EMAIL="[email protected]"
python examples/metadata_field_demo/metadata_example.py
"""

import os
import sys
from typing import Dict, Any, Optional

# Import from local nylas package
from nylas import Client
from nylas.models.errors import NylasApiError


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


def create_draft_with_metadata(
client: Client, grant_id: str, metadata: Dict[str, Any], recipient: str
) -> str:
"""Create a draft message with metadata fields."""
try:
draft_request = {
"subject": "Test Draft with Metadata",
"to": [{"email": recipient}],
"body": "This is a test draft with metadata fields.",
"metadata": metadata
}

draft, request_id = client.drafts.create(
identifier=grant_id,
request_body=draft_request
)
print(f"✓ Created draft with ID: {draft.id}")
print(f" Request ID: {request_id}")
return draft.id
except NylasApiError as e:
print(f"✗ Failed to create draft: {e}")
sys.exit(1)


def send_message_with_metadata(
client: Client, grant_id: str, metadata: Dict[str, Any], recipient: str
) -> str:
"""Send a message directly with metadata fields."""
try:
message_request = {
"subject": "Test Message with Metadata",
"to": [{"email": recipient}],
"body": "This is a test message with metadata fields.",
"metadata": metadata
}

message, request_id = client.messages.send(
identifier=grant_id,
request_body=message_request
)
print(f"✓ Sent message with ID: {message.id}")
print(f" Request ID: {request_id}")

return message.id
except NylasApiError as e:
print(f"✗ Failed to send message: {e}")
sys.exit(1)


def main():
"""Main function demonstrating metadata field usage."""
# Get required environment variables
api_key = get_env_or_exit("NYLAS_API_KEY")
grant_id = get_env_or_exit("NYLAS_GRANT_ID")
recipient = get_env_or_exit("TEST_EMAIL", required=False) or "[email protected]"

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

# Example metadata
metadata = {
"campaign_id": "example-123",
"user_id": "user-456",
"custom_field": "test-value"
}

print("\nDemonstrating Metadata Field Usage")
print("=================================")

# Create a draft with metadata
print("\n1. Creating draft with metadata...")
draft_id = create_draft_with_metadata(client, grant_id, metadata, recipient)

# Send a message with metadata
print("\n2. Sending message with metadata...")
message_id = send_message_with_metadata(client, grant_id, metadata, recipient)

print("\nExample completed successfully!")

# Get the draft and message to demonstrate metadata retrieval
draft = client.drafts.find(identifier=grant_id, draft_id=draft_id)
message = client.messages.find(identifier=grant_id, message_id=message_id)

print("\nRetrieved Draft Metadata:")
print("-------------------------")
print(draft.data)

print("\nRetrieved Message Metadata:")
print("---------------------------")
print(message.data)

if __name__ == "__main__":
main()
4 changes: 3 additions & 1 deletion nylas/models/drafts.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
from dataclasses import dataclass
from typing import List, get_type_hints
from typing import List, Dict, Any, get_type_hints

from dataclasses_json import dataclass_json
from typing_extensions import TypedDict, NotRequired
Expand Down Expand Up @@ -87,6 +87,7 @@ class CreateDraftRequest(TypedDict):
reply_to_message_id: The ID of the message that you are replying to.
tracking_options: Options for tracking opens, links, and thread replies.
custom_headers: Custom headers to add to the message.
metadata: A dictionary of key-value pairs storing additional data.
"""

body: NotRequired[str]
Expand All @@ -101,6 +102,7 @@ class CreateDraftRequest(TypedDict):
reply_to_message_id: NotRequired[str]
tracking_options: NotRequired[TrackingOptions]
custom_headers: NotRequired[List[CustomHeader]]
metadata: NotRequired[Dict[str, Any]]


UpdateDraftRequest = CreateDraftRequest
Expand Down
1 change: 1 addition & 0 deletions nylas/models/messages.py
Original file line number Diff line number Diff line change
Expand Up @@ -80,6 +80,7 @@ class Message:
date: Optional[int] = None
schedule_id: Optional[str] = None
send_at: Optional[int] = None
metadata: Optional[Dict[str, Any]] = None


# Need to use Functional typed dicts because "from" and "in" are Python
Expand Down
41 changes: 41 additions & 0 deletions tests/resources/test_drafts.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

from nylas.models.drafts import Draft
from nylas.resources.drafts import Drafts
from nylas.resources.messages import Messages


class TestDraft:
Expand Down Expand Up @@ -143,6 +144,27 @@ def test_create_draft(self, http_client_response):
overrides=None,
)

def test_create_draft_with_metadata(self, http_client_response):
drafts = Drafts(http_client_response)
request_body = {
"subject": "Hello from Nylas!",
"to": [{"name": "Jon Snow", "email": "[email protected]"}],
"cc": [{"name": "Arya Stark", "email": "[email protected]"}],
"body": "This is the body of my draft message.",
"metadata": {"custom_field": "value", "another_field": 123}
}

drafts.create(identifier="abc-123", request_body=request_body)

http_client_response._execute.assert_called_once_with(
"POST",
"/v3/grants/abc-123/drafts",
None,
None,
request_body,
overrides=None,
)

def test_create_draft_small_attachment(self, http_client_response):
drafts = Drafts(http_client_response)
request_body = {
Expand Down Expand Up @@ -349,6 +371,25 @@ def test_send_draft(self, http_client_response):
method="POST", path="/v3/grants/abc-123/drafts/draft-123", overrides=None
)

def test_send_message_with_metadata(self, http_client_response):
messages = Messages(http_client_response)
request_body = {
"subject": "Hello from Nylas!",
"to": [{"name": "Jon Snow", "email": "[email protected]"}],
"body": "This is the body of my message.",
"metadata": {"custom_field": "value", "another_field": 123}
}

messages.send(identifier="abc-123", request_body=request_body)

http_client_response._execute.assert_called_once_with(
method="POST",
path="/v3/grants/abc-123/messages/send",
request_body=request_body,
data=None,
overrides=None,
)

def test_send_draft_encoded_id(self, http_client_response):
drafts = Drafts(http_client_response)

Expand Down
Loading

0 comments on commit e0c4cf2

Please sign in to comment.