Skip to content

Commit e0c4cf2

Browse files
Add metadata field to SendMessageRequest and Message model (#399)
* 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 <aaron.d@nylas.com> * Update test assertion to use keyword arguments Co-Authored-By: Aaron de Mello <aaron.d@nylas.com> * Update CHANGELOG.md for metadata field addition Co-Authored-By: Aaron de Mello <aaron.d@nylas.com> * Update changelog format to match version entry style Co-Authored-By: Aaron de Mello <aaron.d@nylas.com> * Add E2E example for metadata field functionality Co-Authored-By: Aaron de Mello <aaron.d@nylas.com> * Update metadata example to use local package instead of pip install Co-Authored-By: Aaron de Mello <aaron.d@nylas.com> * 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 <aaron.d@nylas.com>
1 parent 5922f8c commit e0c4cf2

File tree

7 files changed

+587
-2
lines changed

7 files changed

+587
-2
lines changed

CHANGELOG.md

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,9 +2,10 @@ nylas-python Changelog
22
======================
33

44
Unreleased
5-
--------------
5+
----------------
66
* Add support for Scheduler APIs
77
* Fixed attachment download response handling
8+
* Add metadata field support for drafts and messages through CreateDraftRequest model
89

910
v6.4.0
1011
----------------
Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,67 @@
1+
# Metadata Field Example
2+
3+
This example demonstrates how to use metadata fields when creating drafts and sending messages using the Nylas Python SDK.
4+
5+
## Features
6+
7+
- Create drafts with custom metadata fields
8+
- Send messages with custom metadata fields
9+
- Error handling and environment variable configuration
10+
- Clear output and status messages
11+
12+
## Prerequisites
13+
14+
1. A Nylas account with API access
15+
2. Python 3.x installed
16+
3. Local installation of the Nylas Python SDK (this repository)
17+
18+
## Setup
19+
20+
1. Install the SDK in development mode from the repository root:
21+
```bash
22+
cd /path/to/nylas-python
23+
pip install -e .
24+
```
25+
26+
2. Set your environment variables:
27+
```bash
28+
export NYLAS_API_KEY="your_api_key"
29+
export NYLAS_GRANT_ID="your_grant_id"
30+
export TEST_EMAIL="recipient@example.com" # Optional
31+
```
32+
33+
3. Run the example from the repository root:
34+
```bash
35+
python examples/metadata_field_demo/metadata_example.py
36+
```
37+
38+
## Example Output
39+
40+
```
41+
Demonstrating Metadata Field Usage
42+
=================================
43+
44+
1. Creating draft with metadata...
45+
✓ Created draft with ID: draft-abc123
46+
Request ID: req-xyz789
47+
48+
2. Sending message with metadata...
49+
✓ Sent message with ID: msg-def456
50+
Request ID: req-uvw321
51+
52+
Example completed successfully!
53+
```
54+
55+
## Error Handling
56+
57+
The example includes proper error handling for:
58+
- Missing environment variables
59+
- API authentication errors
60+
- Draft creation failures
61+
- Message sending failures
62+
63+
## Documentation
64+
65+
For more information about the Nylas Python SDK and its features, visit:
66+
- [Nylas Python SDK Documentation](https://developer.nylas.com/docs/sdks/python/)
67+
- [Nylas API Reference](https://developer.nylas.com/docs/api/)
Lines changed: 137 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,137 @@
1+
#!/usr/bin/env python3
2+
"""
3+
Nylas SDK Example: Using Metadata Fields with Drafts and Messages
4+
5+
This example demonstrates how to use metadata fields when creating drafts
6+
and sending messages using the Nylas Python SDK.
7+
8+
Required Environment Variables:
9+
NYLAS_API_KEY: Your Nylas API key
10+
NYLAS_GRANT_ID: Your Nylas grant ID
11+
TEST_EMAIL: Email address for sending test messages (optional)
12+
13+
Usage:
14+
First, install the SDK in development mode:
15+
cd /path/to/nylas-python
16+
pip install -e .
17+
18+
Then set environment variables and run:
19+
export NYLAS_API_KEY="your_api_key"
20+
export NYLAS_GRANT_ID="your_grant_id"
21+
export TEST_EMAIL="recipient@example.com"
22+
python examples/metadata_field_demo/metadata_example.py
23+
"""
24+
25+
import os
26+
import sys
27+
from typing import Dict, Any, Optional
28+
29+
# Import from local nylas package
30+
from nylas import Client
31+
from nylas.models.errors import NylasApiError
32+
33+
34+
def get_env_or_exit(var_name: str, required: bool = True) -> Optional[str]:
35+
"""Get an environment variable or exit if required and not found."""
36+
value = os.getenv(var_name)
37+
if required and not value:
38+
print(f"Error: {var_name} environment variable is required")
39+
sys.exit(1)
40+
return value
41+
42+
43+
def create_draft_with_metadata(
44+
client: Client, grant_id: str, metadata: Dict[str, Any], recipient: str
45+
) -> str:
46+
"""Create a draft message with metadata fields."""
47+
try:
48+
draft_request = {
49+
"subject": "Test Draft with Metadata",
50+
"to": [{"email": recipient}],
51+
"body": "This is a test draft with metadata fields.",
52+
"metadata": metadata
53+
}
54+
55+
draft, request_id = client.drafts.create(
56+
identifier=grant_id,
57+
request_body=draft_request
58+
)
59+
print(f"✓ Created draft with ID: {draft.id}")
60+
print(f" Request ID: {request_id}")
61+
return draft.id
62+
except NylasApiError as e:
63+
print(f"✗ Failed to create draft: {e}")
64+
sys.exit(1)
65+
66+
67+
def send_message_with_metadata(
68+
client: Client, grant_id: str, metadata: Dict[str, Any], recipient: str
69+
) -> str:
70+
"""Send a message directly with metadata fields."""
71+
try:
72+
message_request = {
73+
"subject": "Test Message with Metadata",
74+
"to": [{"email": recipient}],
75+
"body": "This is a test message with metadata fields.",
76+
"metadata": metadata
77+
}
78+
79+
message, request_id = client.messages.send(
80+
identifier=grant_id,
81+
request_body=message_request
82+
)
83+
print(f"✓ Sent message with ID: {message.id}")
84+
print(f" Request ID: {request_id}")
85+
86+
return message.id
87+
except NylasApiError as e:
88+
print(f"✗ Failed to send message: {e}")
89+
sys.exit(1)
90+
91+
92+
def main():
93+
"""Main function demonstrating metadata field usage."""
94+
# Get required environment variables
95+
api_key = get_env_or_exit("NYLAS_API_KEY")
96+
grant_id = get_env_or_exit("NYLAS_GRANT_ID")
97+
recipient = get_env_or_exit("TEST_EMAIL", required=False) or "recipient@example.com"
98+
99+
# Initialize Nylas client
100+
client = Client(
101+
api_key=api_key,
102+
)
103+
104+
# Example metadata
105+
metadata = {
106+
"campaign_id": "example-123",
107+
"user_id": "user-456",
108+
"custom_field": "test-value"
109+
}
110+
111+
print("\nDemonstrating Metadata Field Usage")
112+
print("=================================")
113+
114+
# Create a draft with metadata
115+
print("\n1. Creating draft with metadata...")
116+
draft_id = create_draft_with_metadata(client, grant_id, metadata, recipient)
117+
118+
# Send a message with metadata
119+
print("\n2. Sending message with metadata...")
120+
message_id = send_message_with_metadata(client, grant_id, metadata, recipient)
121+
122+
print("\nExample completed successfully!")
123+
124+
# Get the draft and message to demonstrate metadata retrieval
125+
draft = client.drafts.find(identifier=grant_id, draft_id=draft_id)
126+
message = client.messages.find(identifier=grant_id, message_id=message_id)
127+
128+
print("\nRetrieved Draft Metadata:")
129+
print("-------------------------")
130+
print(draft.data)
131+
132+
print("\nRetrieved Message Metadata:")
133+
print("---------------------------")
134+
print(message.data)
135+
136+
if __name__ == "__main__":
137+
main()

nylas/models/drafts.py

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
from dataclasses import dataclass
2-
from typing import List, get_type_hints
2+
from typing import List, Dict, Any, get_type_hints
33

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

9293
body: NotRequired[str]
@@ -101,6 +102,7 @@ class CreateDraftRequest(TypedDict):
101102
reply_to_message_id: NotRequired[str]
102103
tracking_options: NotRequired[TrackingOptions]
103104
custom_headers: NotRequired[List[CustomHeader]]
105+
metadata: NotRequired[Dict[str, Any]]
104106

105107

106108
UpdateDraftRequest = CreateDraftRequest

nylas/models/messages.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -80,6 +80,7 @@ class Message:
8080
date: Optional[int] = None
8181
schedule_id: Optional[str] = None
8282
send_at: Optional[int] = None
83+
metadata: Optional[Dict[str, Any]] = None
8384

8485

8586
# Need to use Functional typed dicts because "from" and "in" are Python

tests/resources/test_drafts.py

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22

33
from nylas.models.drafts import Draft
44
from nylas.resources.drafts import Drafts
5+
from nylas.resources.messages import Messages
56

67

78
class TestDraft:
@@ -143,6 +144,27 @@ def test_create_draft(self, http_client_response):
143144
overrides=None,
144145
)
145146

147+
def test_create_draft_with_metadata(self, http_client_response):
148+
drafts = Drafts(http_client_response)
149+
request_body = {
150+
"subject": "Hello from Nylas!",
151+
"to": [{"name": "Jon Snow", "email": "jsnow@gmail.com"}],
152+
"cc": [{"name": "Arya Stark", "email": "astark@gmail.com"}],
153+
"body": "This is the body of my draft message.",
154+
"metadata": {"custom_field": "value", "another_field": 123}
155+
}
156+
157+
drafts.create(identifier="abc-123", request_body=request_body)
158+
159+
http_client_response._execute.assert_called_once_with(
160+
"POST",
161+
"/v3/grants/abc-123/drafts",
162+
None,
163+
None,
164+
request_body,
165+
overrides=None,
166+
)
167+
146168
def test_create_draft_small_attachment(self, http_client_response):
147169
drafts = Drafts(http_client_response)
148170
request_body = {
@@ -349,6 +371,25 @@ def test_send_draft(self, http_client_response):
349371
method="POST", path="/v3/grants/abc-123/drafts/draft-123", overrides=None
350372
)
351373

374+
def test_send_message_with_metadata(self, http_client_response):
375+
messages = Messages(http_client_response)
376+
request_body = {
377+
"subject": "Hello from Nylas!",
378+
"to": [{"name": "Jon Snow", "email": "jsnow@gmail.com"}],
379+
"body": "This is the body of my message.",
380+
"metadata": {"custom_field": "value", "another_field": 123}
381+
}
382+
383+
messages.send(identifier="abc-123", request_body=request_body)
384+
385+
http_client_response._execute.assert_called_once_with(
386+
method="POST",
387+
path="/v3/grants/abc-123/messages/send",
388+
request_body=request_body,
389+
data=None,
390+
overrides=None,
391+
)
392+
352393
def test_send_draft_encoded_id(self, http_client_response):
353394
drafts = Drafts(http_client_response)
354395

0 commit comments

Comments
 (0)