|
1 | 1 | # Copyright (c) Microsoft Corporation. All rights reserved. |
2 | 2 |
|
3 | 3 | """ |
4 | | -Sample program demonstrating how to use the Connector SDK with generated connector clients. |
| 4 | +Office365 Connector SDK Sample |
5 | 5 |
|
6 | | -This sample shows the usage pattern. For actual usage, you need to: |
7 | | -1. Generate connector code using LogicAppsCompiler CLI (see GENERATION.md) |
8 | | -2. Install the SDK: pip install <TBD> |
9 | | -3. Use the generated typed client classes |
| 6 | +This sample demonstrates how to use the Office365 connector SDK. |
| 7 | +
|
| 8 | +Prerequisites: |
| 9 | +1. Azure subscription with Office365 connection |
| 10 | +2. Office365 connection in Azure Logic Apps |
| 11 | +3. Connection runtime URL from Azure Portal |
| 12 | +
|
| 13 | +Installation: |
| 14 | + pip install <TBD> |
| 15 | +
|
| 16 | +Usage: |
| 17 | + Set environment variable: |
| 18 | + $env:OFFICE365_CONNECTION_URL = "https://[region].azure-apihub.net/apim/office365/[connection-id]" |
| 19 | + |
| 20 | + python sample_connector_usage_office365.py |
10 | 21 | """ |
11 | 22 |
|
12 | 23 | import asyncio |
13 | 24 | import os |
14 | | -from typing import Optional |
| 25 | +from azure.identity.aio import DefaultAzureCredential |
| 26 | +from azure.connectors import ConnectorException |
| 27 | +from azure.connectors.office365 import ( |
| 28 | + Office365Client, |
| 29 | + ClientSendHtmlMessage, |
| 30 | +) |
| 31 | + |
| 32 | +# Connection runtime URL format: |
| 33 | +# https://[region].azure-apihub.net/apim/office365/[connection-id] |
| 34 | +CONNECTION_RUNTIME_URL = os.environ.get( |
| 35 | + "OFFICE365_CONNECTION_URL", |
| 36 | + "" |
| 37 | +) |
| 38 | + |
| 39 | + |
| 40 | +async def example_1_get_outlook_categories(): |
| 41 | + """Example 1: Get Outlook category names.""" |
| 42 | + print("\n=== Example 1: Get Outlook Category Names ===") |
| 43 | + |
| 44 | + credential = DefaultAzureCredential() |
| 45 | + |
| 46 | + async with Office365Client(CONNECTION_RUNTIME_URL, credential) as client: |
| 47 | + try: |
| 48 | + categories = await client.get_outlook_category_names_async() |
| 49 | + |
| 50 | + if categories and 'value' in categories: |
| 51 | + print(f"Found {len(categories['value'])} Outlook categories:") |
| 52 | + for category in categories['value'][:5]: |
| 53 | + display_name = category.get('DisplayName', 'Unknown') |
| 54 | + color = category.get('Color', 'N/A') |
| 55 | + print(f" - {display_name} (Color: {color})") |
| 56 | + else: |
| 57 | + print("No categories found or unexpected response format.") |
| 58 | + |
| 59 | + except ConnectorException as ex: |
| 60 | + print(f"Connector error: {ex}") |
| 61 | + except Exception as ex: |
| 62 | + print(f"Error: {ex}") |
| 63 | + |
| 64 | + |
| 65 | +async def example_2_send_email(): |
| 66 | + """Example 2: Send an HTML email.""" |
| 67 | + print("\n=== Example 2: Send Email ===") |
| 68 | + |
| 69 | + to_address = os.environ.get("TEST_EMAIL_TO", "victoriahall@microsoft.com") |
| 70 | + |
| 71 | + credential = DefaultAzureCredential() |
| 72 | + |
| 73 | + async with Office365Client(CONNECTION_RUNTIME_URL, credential) as client: |
| 74 | + try: |
| 75 | + email = ClientSendHtmlMessage( |
| 76 | + to=to_address, |
| 77 | + subject="Test Email from Office365 Connector SDK", |
| 78 | + body="<p>This is a test email sent from the <strong>Python Office365 Connector SDK</strong>.</p>", |
| 79 | + ) |
| 80 | + |
| 81 | + await client.send_email_async(email) |
| 82 | + print(f"Email sent successfully to {to_address}") |
| 83 | + print("Note: Set TEST_EMAIL_TO environment variable to send to a real address") |
| 84 | + |
| 85 | + except ConnectorException as ex: |
| 86 | + print(f"Connector error: {ex}") |
| 87 | + except Exception as ex: |
| 88 | + print(f"Error: {ex}") |
| 89 | + |
| 90 | + |
| 91 | +async def example_3_get_emails(): |
| 92 | + """Example 3: Get emails from inbox.""" |
| 93 | + print("\n=== Example 3: Get Emails from Inbox ===") |
| 94 | + |
| 95 | + credential = DefaultAzureCredential() |
| 96 | + |
| 97 | + async with Office365Client(CONNECTION_RUNTIME_URL, credential) as client: |
| 98 | + try: |
| 99 | + emails = await client.get_emails_async() |
| 100 | + |
| 101 | + if emails and 'value' in emails: |
| 102 | + print(f"Found {len(emails['value'])} emails:") |
| 103 | + for email in emails['value']: |
| 104 | + subject = email.get('Subject', 'No Subject') |
| 105 | + from_addr = email.get('From', 'Unknown') |
| 106 | + received = email.get('DateTimeReceived', 'Unknown') |
| 107 | + print(f" - {subject}") |
| 108 | + print(f" From: {from_addr}") |
| 109 | + print(f" Received: {received}") |
| 110 | + else: |
| 111 | + print("No emails found or unexpected response format.") |
| 112 | + |
| 113 | + except ConnectorException as ex: |
| 114 | + print(f"Connector error: {ex}") |
| 115 | + except Exception as ex: |
| 116 | + print(f"Error: {ex}") |
| 117 | + |
| 118 | + |
| 119 | +async def example_4_draft_and_send_email(): |
| 120 | + """Example 4: Draft an email and then send it.""" |
| 121 | + print("\n=== Example 4: Draft and Send Email ===") |
| 122 | + |
| 123 | + to_address = os.environ.get("TEST_EMAIL_TO", "victoriahall@microsoft.com") |
| 124 | + |
| 125 | + credential = DefaultAzureCredential() |
| 126 | + |
| 127 | + async with Office365Client(CONNECTION_RUNTIME_URL, credential) as client: |
| 128 | + try: |
| 129 | + draft = ClientSendHtmlMessage( |
| 130 | + to=to_address, |
| 131 | + subject="Draft Email from SDK", |
| 132 | + body="<p>This email was created as a draft first.</p>", |
| 133 | + ) |
| 134 | + |
| 135 | + draft_response = await client.draft_email_async(draft) |
| 136 | + print(f"Draft created successfully") |
| 137 | + |
| 138 | + if draft_response and 'Id' in draft_response: |
| 139 | + message_id = draft_response['Id'] |
| 140 | + print(f"Draft message ID: {message_id}") |
| 141 | + |
| 142 | + await client.send_draft_email_async(message_id=message_id) |
| 143 | + print(f"Draft email sent successfully to {to_address}") |
| 144 | + else: |
| 145 | + print("Draft created but no ID returned.") |
| 146 | + |
| 147 | + except ConnectorException as ex: |
| 148 | + print(f"Connector error: {ex}") |
| 149 | + except Exception as ex: |
| 150 | + print(f"Error: {ex}") |
| 151 | + |
| 152 | + |
| 153 | +async def example_5_error_handling(): |
| 154 | + """Example 5: Demonstrate error handling.""" |
| 155 | + print("\n=== Example 5: Error Handling ===") |
| 156 | + |
| 157 | + credential = DefaultAzureCredential() |
| 158 | + |
| 159 | + async with Office365Client(CONNECTION_RUNTIME_URL, credential) as client: |
| 160 | + try: |
| 161 | + # Attempt to get an email with an invalid ID |
| 162 | + invalid_message_id = "invalid-message-id-12345" |
| 163 | + email = await client.get_email_async(message_id=invalid_message_id) |
| 164 | + print(f"Unexpected success: {email}") |
| 165 | + |
| 166 | + except ConnectorException as ex: |
| 167 | + print(f"Expected error caught:") |
| 168 | + print(f" Message: {ex}") |
| 169 | + except Exception as ex: |
| 170 | + print(f"Unexpected error type: {type(ex).__name__}") |
| 171 | + print(f" Message: {ex}") |
15 | 172 |
|
16 | 173 |
|
17 | 174 | async def main(): |
18 | | - """Entry point for the sample application.""" |
19 | | - print("Azure Logic Apps Connector SDK - Sample Usage (Python)") |
20 | | - print("======================================================") |
21 | | - print() |
22 | | - |
23 | | - # Example 1: SDK Runtime Components |
24 | | - print("Example 1: SDK Runtime Components") |
25 | | - print("----------------------------------") |
26 | | - print("The SDK provides runtime infrastructure for generated connector clients:") |
27 | | - print() |
28 | | - print(" Authentication:") |
29 | | - print(" - ManagedIdentityTokenProvider: For Azure-hosted apps") |
30 | | - print(" - ConnectionStringTokenProvider: For local development") |
31 | | - print() |
32 | | - print(" HTTP:") |
33 | | - print(" - ConnectorHttpClient: Async HTTP client with aiohttp") |
34 | | - print(" - Automatic retry and error handling") |
35 | | - print() |
36 | | - print(" Base Classes:") |
37 | | - print(" - ConnectorClientBase: Base class for all generated clients") |
38 | | - print(" - ConnectorClientOptions: Configuration options") |
39 | | - print() |
40 | | - |
41 | | - # Example 2: Token Provider Usage |
42 | | - print("Example 2: Token Provider Usage") |
43 | | - print("-------------------------------") |
44 | | - |
45 | | - try: |
46 | | - # Import the SDK (demonstrates that it's installed) |
47 | | - from azure_workflows_connectors_sdk import ( |
48 | | - ManagedIdentityTokenProvider, |
49 | | - ConnectionStringTokenProvider, |
50 | | - ) |
51 | | - |
52 | | - # Managed Identity for Azure-hosted scenarios |
53 | | - print(" Managed Identity (for Azure-hosted apps):") |
54 | | - print(" from azure_workflows_connectors_sdk import ManagedIdentityTokenProvider") |
55 | | - print(" token_provider = ManagedIdentityTokenProvider()") |
56 | | - print(" token = await token_provider.get_access_token_async(scopes)") |
57 | | - print() |
58 | | - |
59 | | - # Connection String for local development |
60 | | - api_key = os.environ.get("CONNECTOR_API_KEY", "demo-key") |
61 | | - connection_token_provider = ConnectionStringTokenProvider(api_key) |
62 | | - print(" Connection String (for local development):") |
63 | | - print(" token_provider = ConnectionStringTokenProvider(api_key)") |
64 | | - print(f" Created with key: {api_key[:min(4, len(api_key))]}...") |
65 | | - except ImportError: |
66 | | - print(" Note: <TBD> not installed") |
67 | | - print(" Run: pip install <TBD>") |
68 | | - except Exception as ex: |
69 | | - print(f" Error: {ex}") |
70 | | - |
71 | | - print() |
72 | | - |
73 | | - # Example 3: Generated Client Usage Pattern |
74 | | - print("Example 3: Generated Client Usage Pattern") |
75 | | - print("------------------------------------------") |
76 | | - print("After generating connector code with LogicAppsCompiler CLI:") |
77 | | - print() |
78 | | - print(" # Using generated Office365Client (from office365_client.py)") |
79 | | - print(" from azure_workflows_connectors_sdk.generated.office365_client import (") |
80 | | - print(" Office365Client, ClientSendHtmlMessage") |
81 | | - print(" )") |
82 | | - print(" from azure_workflows_connectors_sdk import ManagedIdentityTokenProvider") |
83 | | - print() |
84 | | - print(" # Create the client with connection runtime URL") |
85 | | - print(" connection_runtime_url = 'https://...' # From Azure Portal") |
86 | | - print(" token_provider = ManagedIdentityTokenProvider()") |
87 | | - print() |
88 | | - print(" async with Office365Client(connection_runtime_url, token_provider) as client:") |
89 | | - print(" # Call typed operations with request dataclasses") |
90 | | - print(" email = ClientSendHtmlMessage(") |
91 | | - print(" to='recipient@example.com',") |
92 | | - print(" subject='Hello from SDK',") |
93 | | - print(" body='<p>Email body</p>',") |
94 | | - print(" )") |
95 | | - print(" await client.send_email_v2_async(email)") |
96 | | - print() |
97 | | - print(" categories = await client.get_outlook_category_names_async()") |
98 | | - print() |
99 | | - |
100 | | - # Example 4: Generation Instructions |
101 | | - print("Example 4: How to Generate Connector Code") |
102 | | - print("------------------------------------------") |
103 | | - print("Use the LogicAppsCompiler CLI from the BPM repository:") |
104 | | - print() |
105 | | - print(" # Build the generator") |
106 | | - print(" dotnet build .\\src\\tools\\CodefulSdkGenerator\\LogicAppsCompiler.Cli -c Release") |
107 | | - print() |
108 | | - print(" # Generate Office365 connector (Python)") |
109 | | - print(" cd src\\tools\\CodefulSdkGenerator\\LogicAppsCompiler.Cli\\bin\\Release") |
110 | | - print(' .\\LogicAppsCompiler.exe "..\\..\\..\\..\\..\\..\\PythonSDK\\src\\azure_workflows_connectors_sdk\\generated" --pythonDirectClient --connectors=office365') |
111 | | - print() |
112 | | - print("See PythonSDK/GENERATION.md for complete documentation.") |
113 | | - print() |
114 | | - |
115 | | - # Example 5: Integration with Azure Functions |
116 | | - print("Example 5: Azure Functions Integration (Python)") |
117 | | - print("------------------------------------------------") |
118 | | - print("The generated clients work well with Azure Functions:") |
119 | | - print() |
120 | | - print(" import azure.functions as func") |
121 | | - print(" from azure_workflows_connectors_sdk.generated.office365_client import Office365Client") |
122 | | - print(" from azure_workflows_connectors_sdk import ManagedIdentityTokenProvider") |
123 | | - print() |
124 | | - print(" app = func.FunctionApp()") |
125 | | - print() |
126 | | - print(" @app.route(route='send-email', auth_level=func.AuthLevel.FUNCTION)") |
127 | | - print(" async def send_email(req: func.HttpRequest) -> func.HttpResponse:") |
128 | | - print(" connection_url = os.environ['CONNECTION_RUNTIME_URL']") |
129 | | - print(" token_provider = ManagedIdentityTokenProvider()") |
130 | | - print() |
131 | | - print(" async with Office365Client(connection_url, token_provider) as client:") |
132 | | - print(" email = ClientSendHtmlMessage(") |
133 | | - print(" to=req.params.get('to'),") |
134 | | - print(" subject='Hello from Azure Function',") |
135 | | - print(" body='<p>Sent from Python!</p>',") |
136 | | - print(" )") |
137 | | - print(" await client.send_email_v2_async(email)") |
138 | | - print() |
139 | | - print(" return func.HttpResponse('Email sent!', status_code=200)") |
140 | | - print() |
141 | | - |
142 | | - # Example 6: Async Context Manager Pattern |
143 | | - print("Example 6: Async Context Manager Pattern") |
144 | | - print("-----------------------------------------") |
145 | | - print("All generated clients support async context managers:") |
146 | | - print() |
147 | | - print(" # Automatic cleanup with context manager") |
148 | | - print(" async with Office365Client(url, token_provider) as client:") |
149 | | - print(" email = ClientSendHtmlMessage(to='...', subject='...', body='...')") |
150 | | - print(" await client.send_email_v2_async(email)") |
151 | | - print(" # Client automatically closed after exiting context") |
152 | | - print() |
153 | | - print(" # Manual lifecycle management") |
154 | | - print(" client = Office365Client(url, token_provider)") |
155 | | - print(" try:") |
156 | | - print(" email = ClientSendHtmlMessage(to='...', subject='...', body='...')") |
157 | | - print(" await client.send_email_v2_async(email)") |
158 | | - print(" finally:") |
159 | | - print(" await client.close()") |
160 | | - print() |
161 | | - |
162 | | - # Example 7: Error Handling |
163 | | - print("Example 7: Error Handling") |
164 | | - print("-------------------------") |
165 | | - print("The SDK provides structured error handling:") |
166 | | - print() |
167 | | - print(" from azure_workflows_connectors_sdk import ConnectorException") |
168 | | - print(" from azure_workflows_connectors_sdk.generated.office365_client import (") |
169 | | - print(" Office365Client, ClientSendHtmlMessage") |
170 | | - print(" )") |
171 | | - print() |
172 | | - print(" try:") |
173 | | - print(" async with Office365Client(url, token_provider) as client:") |
174 | | - print(" email = ClientSendHtmlMessage(to='...', subject='...', body='...')") |
175 | | - print(" await client.send_email_v2_async(email)") |
176 | | - print(" except ConnectorException as ex:") |
177 | | - print(" print(f'Connector error: {ex.message}')") |
178 | | - print(" print(f'Status code: {ex.status_code}')") |
179 | | - print(" print(f'Error body: {ex.error_body}')") |
180 | | - print() |
181 | | - |
182 | | - print("Sample completed successfully!") |
183 | | - print() |
184 | | - print("Next steps:") |
185 | | - print(" 1. Run LogicAppsCompiler CLI to generate connector code") |
186 | | - print(" 2. Install the SDK: pip install <TBD>") |
187 | | - print(" 3. Use typed clients with the connection runtime URL from Azure Portal") |
188 | | - print(" 4. Deploy to Azure Functions or run locally") |
| 175 | + """Run all examples.""" |
| 176 | + print("Office365 Connector SDK - Sample Usage") |
| 177 | + print("=" * 50) |
| 178 | + print() |
| 179 | + |
| 180 | + await example_1_get_outlook_categories() |
| 181 | + await example_2_send_email() |
| 182 | + await example_3_get_emails() |
| 183 | + await example_4_draft_and_send_email() |
| 184 | + await example_5_error_handling() |
| 185 | + |
| 186 | + print("\n" + "=" * 50) |
| 187 | + print("Sample completed!") |
189 | 188 |
|
190 | 189 |
|
191 | 190 | if __name__ == "__main__": |
|
0 commit comments