diff --git a/CHANGELOG.md b/CHANGELOG.md index 07e7c65..5547e88 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,10 @@ nylas-python Changelog ====================== +Unreleased +---------------- +* Added support for `list_import_events` + v6.7.0 ---------------- * Added support for `select` query parameter in list calendars, list events, and list messages. diff --git a/examples/import_events_demo/README.md b/examples/import_events_demo/README.md new file mode 100644 index 0000000..cb52af0 --- /dev/null +++ b/examples/import_events_demo/README.md @@ -0,0 +1,68 @@ +# Import Events Demo + +This example demonstrates the usage of the `list_import_events` method in the Nylas SDK. This method returns a list of recurring events, recurring event exceptions, and single events from a specified calendar within a given time frame. It's particularly useful when you want to import, store, and synchronize events from a calendar to your application. + +## Features Demonstrated + +1. **Basic Usage**: Shows how to use `list_import_events` with required parameters. +2. **Time Filtering**: Demonstrates filtering events by start and end time. +3. **Pagination**: Shows how to handle paginated results with `limit` and `page_token`. +4. **Field Selection**: Demonstrates how to use the `select` parameter to request only specific fields. +5. **Multiple Scenarios**: Shows various parameter combinations for different use cases. + +## Setup + +1. Create a `.env` file in the root directory with your Nylas API credentials: + ``` + NYLAS_API_KEY=your_api_key_here + NYLAS_GRANT_ID=your_grant_id_here + ``` + +2. Install the required dependencies: + ```bash + pip install nylas python-dotenv + ``` + +## Running the Example + +Run the example script: +```bash +python examples/import_events_demo/import_events_example.py +``` + +The script will demonstrate different ways to use the `list_import_events` method with various parameters. + +## Example Output + +The script will show output similar to this: +``` +=== Import Events Demo === + +Basic import (primary calendar): +Event - Title: Team Meeting, ID: abc123... + +Time-filtered import (Jan 1, 2023 - Dec 31, 2023): +Event - Title: Annual Review, ID: def456... + +Limited results with field selection (only id, title and when): +Event - Title: Client Call, ID: ghi789... +``` + +## Benefits of Using Import Events + +1. **Efficient Syncing**: Easily synchronize calendar events to your application or database. +2. **Better Performance**: Using time filters and limiting results can improve performance. +3. **Selective Data**: Using the select parameter allows you to request only the fields you need. + +## Available Parameters + +The `list_import_events` method accepts the following parameters: + +- `calendar_id` (required): Specify the calendar ID to import events from. You can use "primary" for the user's primary calendar. +- `start`: Filter for events starting at or after this Unix timestamp. +- `end`: Filter for events ending at or before this Unix timestamp. +- `select`: Comma-separated list of fields to return in the response. +- `limit`: Maximum number of objects to return (defaults to 50, max 200). +- `page_token`: Token for retrieving the next page of results. + +For more information, refer to the [Nylas API documentation](https://developer.nylas.com/). \ No newline at end of file diff --git a/examples/import_events_demo/import_events_example.py b/examples/import_events_demo/import_events_example.py new file mode 100644 index 0000000..da347e6 --- /dev/null +++ b/examples/import_events_demo/import_events_example.py @@ -0,0 +1,197 @@ +#!/usr/bin/env python3 +""" +Nylas SDK Example: Using Import Events + +This example demonstrates how to use the 'list_import_events' method to import and +synchronize events from a calendar within a given time frame. + +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/import_events_demo/import_events_example.py +""" + +import os +import sys +import json +import time +from datetime import datetime, timedelta +from nylas import Client + + +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_data(data: list, title: str) -> None: + """Pretty print the data with a title.""" + print(f"\n{title}:") + for item in data: + # Convert to dict and pretty print + item_dict = item.to_dict() + print(json.dumps(item_dict, indent=2)) + + +def demonstrate_basic_import(client: Client, grant_id: str) -> None: + """Demonstrate basic usage of list_import_events with primary calendar.""" + print("\n=== Basic Import Events ===") + + print("\nFetching events from primary calendar:") + events = client.events.list_import_events( + identifier=grant_id, + query_params={"calendar_id": "primary", "limit": 2} + ) + print_data(events.data, "Basic import events") + + +def demonstrate_time_filtered_import(client: Client, grant_id: str) -> None: + """Demonstrate import events with time filtering.""" + print("\n=== Time Filtered Import Events ===") + + # Get timestamps for a one-month period + now = int(time.time()) + one_month_ago = now - (30 * 24 * 60 * 60) # 30 days ago + one_month_future = now + (30 * 24 * 60 * 60) # 30 days in future + + # Format dates for display + from_date = datetime.fromtimestamp(one_month_ago).strftime("%Y-%m-%d") + to_date = datetime.fromtimestamp(one_month_future).strftime("%Y-%m-%d") + + print(f"\nFetching events from {from_date} to {to_date}:") + events = client.events.list_import_events( + identifier=grant_id, + query_params={ + "calendar_id": "primary", + "start": one_month_ago, + "end": one_month_future + } + ) + print_data(events.data, f"Events from {from_date} to {to_date}") + + +def demonstrate_limit(client: Client, grant_id: str) -> None: + """Demonstrate import events with limit parameter.""" + print("\n=== Import Events with Max Results ===") + + print("\nFetching events with limit=5:") + events = client.events.list_import_events( + identifier=grant_id, + query_params={ + "calendar_id": "primary", + "limit": 5 + } + ) + print_data(events.data, "Events with limit=5") + + +def demonstrate_field_selection(client: Client, grant_id: str) -> None: + """Demonstrate import events with field selection.""" + print("\n=== Import Events with Field Selection ===") + + print("\nFetching events with select parameter (only id, title, and when):") + events = client.events.list_import_events( + identifier=grant_id, + query_params={ + "calendar_id": "primary", + "limit": 2, + "select": "id,title,when" + } + ) + print_data(events.data, "Events with selected fields only") + + +def demonstrate_pagination(client: Client, grant_id: str) -> None: + """Demonstrate pagination for import events.""" + print("\n=== Import Events with Pagination ===") + + # First page + print("\nFetching first page of events (limit=3):") + first_page = client.events.list_import_events( + identifier=grant_id, + query_params={ + "calendar_id": "primary", + "limit": 3 + } + ) + print_data(first_page.data, "First page of events") + + # If there's a next page, fetch it + if hasattr(first_page, 'next_cursor') and first_page.next_cursor: + print("\nFetching second page of events:") + second_page = client.events.list_import_events( + identifier=grant_id, + query_params={ + "calendar_id": "primary", + "limit": 3, + "page_token": first_page.next_cursor + } + ) + print_data(second_page.data, "Second page of events") + else: + print("\nNo second page available - not enough events to paginate") + + +def demonstrate_full_example(client: Client, grant_id: str) -> None: + """Demonstrate a full example with all parameters.""" + print("\n=== Full Import Events Example ===") + + # Get timestamps for the current year + now = datetime.now() + start_of_year = datetime(now.year, 1, 1).timestamp() + end_of_year = datetime(now.year, 12, 31, 23, 59, 59).timestamp() + + print(f"\nFetching events for {now.year} with all parameters:") + events = client.events.list_import_events( + identifier=grant_id, + query_params={ + "calendar_id": "primary", + "limit": 10, + "start": int(start_of_year), + "end": int(end_of_year), + "select": "id,title,description,when,participants,location" + } + ) + print_data(events.data, f"Events for {now.year} with all parameters") + + +def main(): + """Main function demonstrating the import events method.""" + # 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 Import Events Functionality") + print("========================================") + + # Demonstrate different ways to use list_import_events + demonstrate_basic_import(client, grant_id) + demonstrate_time_filtered_import(client, grant_id) + demonstrate_limit(client, grant_id) + demonstrate_field_selection(client, grant_id) + demonstrate_pagination(client, grant_id) + demonstrate_full_example(client, grant_id) + + print("\nExample completed!") + + +if __name__ == "__main__": + main() \ No newline at end of file diff --git a/nylas/models/events.py b/nylas/models/events.py index a16069c..c95c667 100644 --- a/nylas/models/events.py +++ b/nylas/models/events.py @@ -804,3 +804,25 @@ class SendRsvpRequest(TypedDict): """ status: SendRsvpStatus + + +class ListImportEventsQueryParams(ListQueryParams): + """ + Interface representing the query parameters for listing imported events. + + Attributes: + calendar_id: Specify calendar ID to import events to. "primary" is a supported value + indicating the user's primary calendar. + start: Filter for events that start at or after the specified time, in Unix timestamp format. + end: Filter for events that end at or before the specified time, in Unix timestamp format. + select: Comma-separated list of fields to return in the response. + This allows you to receive only the portion of object data that you're interested in. + page_token: An identifier that specifies which page of data to return. + This value should be taken from a ListResponse object's next_cursor parameter. + """ + + calendar_id: str + start: NotRequired[int] + end: NotRequired[int] + select: NotRequired[str] + page_token: NotRequired[str] diff --git a/nylas/resources/events.py b/nylas/resources/events.py index 95f05bd..3b9223a 100644 --- a/nylas/resources/events.py +++ b/nylas/resources/events.py @@ -12,6 +12,7 @@ CreateEventRequest, FindEventQueryParams, ListEventQueryParams, + ListImportEventsQueryParams, CreateEventQueryParams, UpdateEventQueryParams, DestroyEventQueryParams, @@ -64,6 +65,34 @@ def list( overrides=overrides, ) + def list_import_events( + self, + identifier: str, + query_params: ListImportEventsQueryParams, + overrides: RequestOverrides = None, + ) -> ListResponse[Event]: + """ + Returns a list of recurring events, recurring event exceptions, and + single events from the specified calendar within a given time frame. + This is useful when you want to import, store, and synchronize events + from the time frame to your application + + Args: + identifier: The identifier of the Grant to act upon. + query_params: The query parameters to include in the request. + overrides: The request overrides to use for the request. + + Returns: + The list of imported Events. + """ + + return super().list( + path=f"/v3/grants/{identifier}/events/import", + response_type=Event, + query_params=query_params, + overrides=overrides, + ) + def find( self, identifier: str, diff --git a/tests/resources/test_events.py b/tests/resources/test_events.py index a66f412..686fe17 100644 --- a/tests/resources/test_events.py +++ b/tests/resources/test_events.py @@ -175,6 +175,116 @@ def test_list_events_with_select_param(self, http_client_list_response): # The actual response validation is handled by the mock in conftest.py assert result is not None + def test_list_import_events(self, http_client_list_response): + events = Events(http_client=http_client_list_response) + events.list_import_events( + identifier="grant-123", + query_params={"calendar_id": "primary"}, + ) + + http_client_list_response._execute.assert_called_once_with( + "GET", + "/v3/grants/grant-123/events/import", + None, + {"calendar_id": "primary"}, + None, + overrides=None, + ) + + def test_list_import_events_with_select_param(self, http_client_list_response): + events = Events(http_client=http_client_list_response) + events.list_import_events( + identifier="grant-123", + query_params={"calendar_id": "primary", "select": "id,title,participants"}, + ) + + http_client_list_response._execute.assert_called_once_with( + "GET", + "/v3/grants/grant-123/events/import", + None, + {"calendar_id": "primary", "select": "id,title,participants"}, + None, + overrides=None, + ) + + def test_list_import_events_with_limit(self, http_client_list_response): + events = Events(http_client=http_client_list_response) + events.list_import_events( + identifier="grant-123", + query_params={"calendar_id": "primary", "limit": 100}, + ) + + http_client_list_response._execute.assert_called_once_with( + "GET", + "/v3/grants/grant-123/events/import", + None, + {"calendar_id": "primary", "limit": 100}, + None, + overrides=None, + ) + + def test_list_import_events_with_time_filters(self, http_client_list_response): + events = Events(http_client=http_client_list_response) + # Using Unix timestamps for Jan 1, 2023 and Dec 31, 2023 + start_time = 1672531200 # Jan 1, 2023 + end_time = 1704067199 # Dec 31, 2023 + + events.list_import_events( + identifier="grant-123", + query_params={ + "calendar_id": "primary", + "start": start_time, + "end": end_time + }, + ) + + http_client_list_response._execute.assert_called_once_with( + "GET", + "/v3/grants/grant-123/events/import", + None, + { + "calendar_id": "primary", + "start": start_time, + "end": end_time + }, + None, + overrides=None, + ) + + def test_list_import_events_with_all_params(self, http_client_list_response): + events = Events(http_client=http_client_list_response) + # Using Unix timestamps for Jan 1, 2023 and Dec 31, 2023 + start_time = 1672531200 # Jan 1, 2023 + end_time = 1704067199 # Dec 31, 2023 + + events.list_import_events( + identifier="grant-123", + query_params={ + "calendar_id": "primary", + "limit": 50, + "start": start_time, + "end": end_time, + "select": "id,title,participants,when", + "page_token": "next-page-token-123" + }, + ) + + http_client_list_response._execute.assert_called_once_with( + "GET", + "/v3/grants/grant-123/events/import", + None, + { + "calendar_id": "primary", + "limit": 50, + "start": start_time, + "end": end_time, + "select": "id,title,participants,when", + "page_token": "next-page-token-123" + }, + None, + overrides=None, + ) + def test_find_event(self, http_client_response): events = Events(http_client_response)