From de627d0009e9fad511fae17ec2c9aa3504e443b9 Mon Sep 17 00:00:00 2001 From: Aaron de Mello Date: Wed, 18 Jun 2025 10:40:55 -0400 Subject: [PATCH 1/2] feat: add support for include_hidden_folders query parameter in list folders endpoint --- CHANGELOG.md | 1 + .../include_hidden_folders_demo/README.md | 67 ++++++++++++ .../include_hidden_folders_example.py | 71 +++++++++++++ nylas/models/folders.py | 2 + tests/resources/test_folders.py | 100 ++++++++++++++---- 5 files changed, 222 insertions(+), 19 deletions(-) create mode 100644 examples/include_hidden_folders_demo/README.md create mode 100644 examples/include_hidden_folders_demo/include_hidden_folders_example.py diff --git a/CHANGELOG.md b/CHANGELOG.md index 81f7ecc..7cb790b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -10,6 +10,7 @@ Unreleased * Added `raw_mime` field to Message model for Base64url-encoded message data * Added TrackingOptions model for message tracking configuration * Maintained backwards compatibility for existing message functionality +* Added support for `include_hidden_folders` query parameter for listing folders (Microsoft only) v6.9.0 ---------------- diff --git a/examples/include_hidden_folders_demo/README.md b/examples/include_hidden_folders_demo/README.md new file mode 100644 index 0000000..dc1b99a --- /dev/null +++ b/examples/include_hidden_folders_demo/README.md @@ -0,0 +1,67 @@ +# Include Hidden Folders Example + +This example demonstrates how to use the `include_hidden_folders` query parameter when listing folders with the Nylas Python SDK. + +## Overview + +The `include_hidden_folders` parameter is Microsoft-specific and allows you to include hidden folders in the folder listing response. By default, this parameter is `False` and hidden folders are not included. + +## Prerequisites + +1. A Nylas application with Microsoft OAuth configured +2. A valid Nylas API key +3. A grant ID for a Microsoft account + +## Setup + +1. Set your environment variables: + ```bash + export NYLAS_API_KEY="your_api_key_here" + export NYLAS_GRANT_ID="your_grant_id_here" + export NYLAS_API_URI="https://api.us.nylas.com" # Optional, defaults to US + ``` + +2. Install the Nylas Python SDK: + ```bash + pip install nylas + ``` + +## Running the Example + +```bash +python include_hidden_folders_example.py +``` + +## Code Explanation + +The example demonstrates two scenarios: + +1. **Default behavior**: Lists folders without hidden folders + ```python + folders_response = nylas.folders.list( + identifier=grant_id, + query_params={"limit": 10} + ) + ``` + +2. **With hidden folders**: Lists folders including hidden folders (Microsoft only) + ```python + folders_with_hidden_response = nylas.folders.list( + identifier=grant_id, + query_params={ + "include_hidden_folders": True, + "limit": 10 + } + ) + ``` + +## Expected Output + +The example will show: +- List of regular folders +- List of folders including hidden ones (if any) +- Comparison showing how many additional hidden folders were found + +## Note + +This feature is **Microsoft-specific only**. For other providers (Google, IMAP), the `include_hidden_folders` parameter will be ignored. \ No newline at end of file diff --git a/examples/include_hidden_folders_demo/include_hidden_folders_example.py b/examples/include_hidden_folders_demo/include_hidden_folders_example.py new file mode 100644 index 0000000..39f2226 --- /dev/null +++ b/examples/include_hidden_folders_demo/include_hidden_folders_example.py @@ -0,0 +1,71 @@ +import os +from nylas import Client + + +def main(): + """ + This example demonstrates how to use the include_hidden_folders parameter + when listing folders with the Nylas SDK. + + The include_hidden_folders parameter is Microsoft-specific and when set to True, + it includes hidden folders in the response. + """ + + # Initialize the client + nylas = Client( + api_key=os.environ.get("NYLAS_API_KEY"), + api_uri=os.environ.get("NYLAS_API_URI", "https://api.us.nylas.com"), + ) + + # Get the grant ID from environment variable + grant_id = os.environ.get("NYLAS_GRANT_ID") + + if not grant_id: + print("Please set the NYLAS_GRANT_ID environment variable") + return + + try: + print("Listing folders without hidden folders (default behavior):") + print("=" * 60) + + # List folders without hidden folders (default) + folders_response = nylas.folders.list( + identifier=grant_id, query_params={"limit": 10} + ) + + for folder in folders_response.data: + print(f"- {folder.name} (ID: {folder.id})") + + print(f"\nTotal folders found: {len(folders_response.data)}") + + # Now list folders WITH hidden folders (Microsoft only) + print("\n\nListing folders with hidden folders included (Microsoft only):") + print("=" * 70) + + folders_with_hidden_response = nylas.folders.list( + identifier=grant_id, + query_params={"include_hidden_folders": True, "limit": 10}, + ) + + for folder in folders_with_hidden_response.data: + print(f"- {folder.name} (ID: {folder.id})") + + print( + f"\nTotal folders found (including hidden): {len(folders_with_hidden_response.data)}" + ) + + # Compare the counts + hidden_count = len(folders_with_hidden_response.data) - len( + folders_response.data + ) + if hidden_count > 0: + print(f"\nFound {hidden_count} additional hidden folder(s)") + else: + print("\nNo additional hidden folders found") + + except Exception as e: + print(f"Error: {e}") + + +if __name__ == "__main__": + main() diff --git a/nylas/models/folders.py b/nylas/models/folders.py index 205502d..9352387 100644 --- a/nylas/models/folders.py +++ b/nylas/models/folders.py @@ -85,6 +85,7 @@ class ListFolderQueryParams(ListQueryParams): Attributes: parent_id: (Microsoft and EWS only.) Use the ID of a folder to find all child folders it contains. + include_hidden_folders: (Microsoft only) When true, Nylas includes hidden folders in its response. select (NotRequired[str]): 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. limit (NotRequired[int]): The maximum number of objects to return. @@ -94,6 +95,7 @@ class ListFolderQueryParams(ListQueryParams): """ parent_id: NotRequired[str] + include_hidden_folders: NotRequired[bool] class FindFolderQueryParams(TypedDict): diff --git a/tests/resources/test_folders.py b/tests/resources/test_folders.py index da53dec..87332f0 100644 --- a/tests/resources/test_folders.py +++ b/tests/resources/test_folders.py @@ -58,26 +58,85 @@ def test_list_folders_with_query_params(self, http_client_list_response): overrides=None, ) + def test_list_folders_with_include_hidden_folders_param( + self, http_client_list_response + ): + folders = Folders(http_client_list_response) + + folders.list( + identifier="abc-123", query_params={"include_hidden_folders": True} + ) + + http_client_list_response._execute.assert_called_once_with( + "GET", + "/v3/grants/abc-123/folders", + None, + {"include_hidden_folders": True}, + None, + overrides=None, + ) + + def test_list_folders_with_include_hidden_folders_false( + self, http_client_list_response + ): + folders = Folders(http_client_list_response) + + folders.list( + identifier="abc-123", query_params={"include_hidden_folders": False} + ) + + http_client_list_response._execute.assert_called_once_with( + "GET", + "/v3/grants/abc-123/folders", + None, + {"include_hidden_folders": False}, + None, + overrides=None, + ) + + def test_list_folders_with_multiple_params_including_hidden_folders( + self, http_client_list_response + ): + folders = Folders(http_client_list_response) + + folders.list( + identifier="abc-123", + query_params={ + "limit": 20, + "parent_id": "parent-123", + "include_hidden_folders": True, + }, + ) + + http_client_list_response._execute.assert_called_once_with( + "GET", + "/v3/grants/abc-123/folders", + None, + {"limit": 20, "parent_id": "parent-123", "include_hidden_folders": True}, + None, + overrides=None, + ) + def test_list_folders_with_select_param(self, http_client_list_response): folders = Folders(http_client_list_response) # Set up mock response data http_client_list_response._execute.return_value = { "request_id": "abc-123", - "data": [{ - "id": "folder-123", - "name": "Important", - "total_count": 42, - "unread_count": 5 - }] + "data": [ + { + "id": "folder-123", + "name": "Important", + "total_count": 42, + "unread_count": 5, + } + ], } # Call the API method result = folders.list( identifier="abc-123", - query_params={ - "select": "id,name,total_count,unread_count" - } + query_params={"select": "id,name,total_count,unread_count"}, ) # Verify API call @@ -111,21 +170,24 @@ def test_find_folder_with_select_param(self, http_client_response): folders = Folders(http_client_response) # Set up mock response data - http_client_response._execute.return_value = ({ - "request_id": "abc-123", - "data": { - "id": "folder-123", - "name": "Important", - "total_count": 42, - "unread_count": 5 - } - }, {"X-Test-Header": "test"}) + http_client_response._execute.return_value = ( + { + "request_id": "abc-123", + "data": { + "id": "folder-123", + "name": "Important", + "total_count": 42, + "unread_count": 5, + }, + }, + {"X-Test-Header": "test"}, + ) # Call the API method result = folders.find( identifier="abc-123", folder_id="folder-123", - query_params={"select": "id,name,total_count,unread_count"} + query_params={"select": "id,name,total_count,unread_count"}, ) # Verify API call From 25cedf9382f0a63b0fc45b98cb025c9a1eb5c7f9 Mon Sep 17 00:00:00 2001 From: Samuel Xavier Date: Wed, 18 Jun 2025 20:39:32 -0300 Subject: [PATCH 2/2] Fix conflicts, synchronize tests --- tests/resources/test_folders.py | 18 +++++++++++------- 1 file changed, 11 insertions(+), 7 deletions(-) diff --git a/tests/resources/test_folders.py b/tests/resources/test_folders.py index f79f313..6da3cce 100644 --- a/tests/resources/test_folders.py +++ b/tests/resources/test_folders.py @@ -76,7 +76,6 @@ def test_list_folders_with_single_level_param(self, http_client_list_response): "GET", "/v3/grants/abc-123/folders", None, - {"include_hidden_folders": True}, {"single_level": True}, None, overrides=None, @@ -90,6 +89,15 @@ def test_list_folders_with_include_hidden_folders_false( folders.list( identifier="abc-123", query_params={"include_hidden_folders": False} ) + + http_client_list_response._execute.assert_called_once_with( + "GET", + "/v3/grants/abc-123/folders", + None, + {"include_hidden_folders": False}, + None, + overrides=None, + ) def test_list_folders_with_single_level_false(self, http_client_list_response): folders = Folders(http_client_list_response) @@ -100,7 +108,6 @@ def test_list_folders_with_single_level_false(self, http_client_list_response): "GET", "/v3/grants/abc-123/folders", None, - {"include_hidden_folders": False}, {"single_level": False}, None, overrides=None, @@ -109,8 +116,6 @@ def test_list_folders_with_single_level_false(self, http_client_list_response): def test_list_folders_with_multiple_params_including_hidden_folders( self, http_client_list_response ): - - def test_list_folders_with_combined_params(self, http_client_list_response): folders = Folders(http_client_list_response) folders.list( @@ -119,16 +124,15 @@ def test_list_folders_with_combined_params(self, http_client_list_response): "limit": 20, "parent_id": "parent-123", "include_hidden_folders": True, + "single_level": True, }, - query_params={"single_level": True, "parent_id": "parent-123", "limit": 10}, ) http_client_list_response._execute.assert_called_once_with( "GET", "/v3/grants/abc-123/folders", None, - {"limit": 20, "parent_id": "parent-123", "include_hidden_folders": True}, - {"single_level": True, "parent_id": "parent-123", "limit": 10}, + {"limit": 20, "parent_id": "parent-123", "include_hidden_folders": True, "single_level": True}, None, overrides=None, )