diff --git a/.github/ISSUE_TEMPLATE/config.yml b/.github/ISSUE_TEMPLATE/config.yml deleted file mode 100644 index 0b6947e..0000000 --- a/.github/ISSUE_TEMPLATE/config.yml +++ /dev/null @@ -1,8 +0,0 @@ -blank_issues_enabled: false -contact_links: - - name: Logic Apps Discussions - url: https://github.com/Azure/LogicApps/discussions - about: Ask general Logic Apps questions or discuss ideas - - name: Stack Overflow - url: https://stackoverflow.com/questions/tagged/azure-logic-apps - about: Ask questions about using the SDK on Stack Overflow diff --git a/.github/copilot-instructions.md b/.github/copilot-instructions.md index 10c93c7..4385ae5 100644 --- a/.github/copilot-instructions.md +++ b/.github/copilot-instructions.md @@ -2,7 +2,7 @@ ## Overview -This repository contains the Python SDK for Azure Logic Apps connectors. Code must follow Python best practices (PEP 8, PEP 484) and the team's conventions for async/await patterns, type safety, and API design. +This repository contains the Python SDK for Azure Connectors. Code must follow Python best practices (PEP 8, PEP 484) and the team's conventions for async/await patterns, type safety, and API design. ## Quick Reference: Coding Style Rules diff --git a/.github/skills/connection-setup/SKILL.md b/.github/skills/connection-setup/SKILL.md index ba01498..ac00e43 100644 --- a/.github/skills/connection-setup/SKILL.md +++ b/.github/skills/connection-setup/SKILL.md @@ -1,11 +1,11 @@ --- name: connection-setup -description: 'Create and configure Connector Gateway connections for SDK-supported connectors. USE WHEN: setting up a new connector connection, creating an Connector Gateway, authorizing OAuth consent, adding access policies, or configuring local.settings.json / app settings. Covers Office365, SharePoint, Teams, and any Microsoft.Web/connections connector. NOT FOR: general SDK usage, trigger registration, or code generation.' +description: 'Create and configure Connector Namespace connections for the Azure Connectors Python SDK. USE WHEN: setting up a new connector connection, creating a Connector Namespace, authorizing OAuth consent, adding access policies, or configuring local.settings.json / app settings. Covers Office365, SharePoint, Teams, and any Microsoft.Web/connections connector. Works with Azure Functions, Flask, FastAPI, Django, or any Python app using the SDK. NOT FOR: trigger registration (use trigger-registration skill), or code generation.' --- -# Connector Gateway Connection Setup +# Connector Namespace Connection Setup -Automates the end-to-end connection lifecycle for SDK-supported connectors, keeping the developer in VS Code. +Automates the end-to-end connection lifecycle for SDK-supported connectors. Connections created here can be used by any Python app via the `azure-connectors` package — Azure Functions, Flask, FastAPI, Django, scripts, etc. ## When to Use @@ -23,7 +23,7 @@ Automates the end-to-end connection lifecycle for SDK-supported connectors, keep ## Procedure -### Step 1: Create or Select Connector Gateway +### Step 1: Create or Select Connector Namespace Check for an existing Connector Namespace in the resource group: @@ -175,3 +175,16 @@ az rest --method GET --uri "$runtimeUrl/beta/me/joinedTeams" --resource "https:/ # Azure Blob — list datasets az rest --method GET --uri "$runtimeUrl/v2/datasets" --resource "https://apihub.azure.com" -o json ``` + +## Next Steps + +- **SDK usage:** Use the connection runtime URL with the SDK's typed async clients: + ```python + from azure.connectors.office365 import Office365Client + from azure.connectors.sdk import ManagedIdentityTokenProvider + + async with Office365Client(runtime_url, ManagedIdentityTokenProvider()) as client: + await client.send_email_async(to="...", subject="...", body="...") + ``` +- **Azure Functions triggers:** To register connector triggers (e.g., OnNewEmail, OnNewFile) for Azure Functions, use the [trigger-registration skill](../trigger-registration/SKILL.md). +- **Azure Functions signatures:** For a complete mapping of trigger operations to Azure Functions signatures, see [Operations to Functions Signature Match](https://github.com/Azure/azure-functions-connector-extension/blob/main/docs/operations-functions-match.md). diff --git a/.github/skills/trigger-registration/SKILL.md b/.github/skills/trigger-registration/SKILL.md index 19c9945..e020ee2 100644 --- a/.github/skills/trigger-registration/SKILL.md +++ b/.github/skills/trigger-registration/SKILL.md @@ -1,196 +1,240 @@ --- name: trigger-registration -description: 'Register Connector Gateway trigger configs for SDK-supported connectors. USE WHEN: setting up polling triggers (e.g., OnNewEmail, OnNewFile, OnUpdatedFile) that call back to an Azure Function when events occur, creating a Python Function App to receive trigger callbacks, or wiring post-deploy trigger config scripts. Covers function app scaffolding, trigger config creation, callback URL wiring, parameter discovery, and binary vs metadata payload handling. NOT FOR: connection setup (use connection-setup skill).' +description: 'Register Connector Namespace trigger configs for Azure connectors and scaffold Azure Functions to receive trigger callbacks. USE WHEN: setting up polling triggers (e.g., OnNewEmail, OnNewFile, OnUpdatedFile) that call back to an Azure Function, scaffolding a Function App project with ConnectorTrigger binding, wiring callback URLs, or troubleshooting trigger configs. Covers both typed SDK payloads and raw JSON generic triggers. NOT FOR: connection setup (use connection-setup skill).' --- -# Connector Gateway Trigger Registration +# Connector Namespace Trigger Registration -Registers polling trigger configs on a Connector Gateway so that connector events (new email, new file, etc.) call back to your Azure Function endpoint. Also covers scaffolding a Python Function App to receive trigger callbacks. +Registers polling trigger configs on a Connector Namespace so that connector events (new email, new file, etc.) call back to your application endpoint. Covers scaffolding a Python Function App with the ConnectorTrigger extension binding. ## When to Use -- Developer needs a connector trigger (e.g., "when a new file is created in OneDrive") -- Developer has an existing Connector Gateway connection (use the `connection-setup` skill first if not) -- Developer needs to wire up the callback URL from a deployed Azure Function -- Developer needs to understand binary vs metadata trigger payload shapes +- Developer needs a connector trigger (e.g., "when a new email arrives in Office365") +- Developer has an existing Connector Namespace connection (use the `connection-setup` skill first if not) +- Developer needs to scaffold a Python Function App with ConnectorTrigger in Azure Functions +- Developer needs to wire the callback URL from a deployed or local Function App +- Developer needs to understand which typed payload to use for a given trigger operation ## Prerequisites - Azure CLI installed and authenticated (`az login`) -- Connector Gateway with a connected connector (see `connection-setup` skill) -- The Connector Gateway must have a **system-assigned managed identity** enabled (required for trigger callback authentication) -- Deployed Azure Function App with an HTTP trigger function to receive callbacks -- **Supported regions** for Connector Gateway: `brazilsouth`, `centraluseuap`, `eastus2euap`, `centralusstage`, `eastusstage`. Only the Connector Gateway `location` must be in a supported region; the Function App can be in any region. -- Python 3.10+ with the `azure-connectors` package installed +- Connector Namespace with a connected connector (see `connection-setup` skill) +- The Connector Namespace must have a **system-assigned managed identity** enabled +- **Supported regions** for Connector Namespace: `westcentralus` +- A [supported Python version for Azure Functions](https://learn.microsoft.com/en-us/azure/azure-functions/supported-languages?tabs=isolated-process%2Cv4&pivots=programming-language-python#languages-by-runtime-version) with the `azure-connectors` package installed ## Key Concepts -### Trigger Config vs Connection +### ConnectorTrigger Extension Binding + +### Python Decorator and Package Rules + +Use this decision logic when scaffolding a Python connector trigger function: + +1. **Choose decorator by Python version:** + - Python 3.13 → `@app.connector_trigger` + - Python < 3.13 → `@app.generic_trigger(type="connectorTrigger")` + +2. **Choose packages by trigger operation:** + - Office 365 `OnNewEmail` → add `azurefunctions-extensions-connectors` (imports `azure-connectors` automatically) + - Any other connector with a typed SDK model → add `azure-connectors`, use typed class as parameter type + - No typed model available → use `str` as parameter type, no extra package needed beyond `azure-functions` + +3. **Base package:** `azure-functions` (use `>=2.2.0b4` only when using `@app.connector_trigger` decorator) + +### Extension Webhook Endpoint -Connections (managed by the `connection-setup` skill) authenticate your app to the connector API. Trigger configs tell the Connector Gateway to **poll** the connector for events and **POST callbacks** to your function when events occur. +The ConnectorTrigger extension registers a webhook route on the Function App: + +```text +POST /runtime/webhooks/connector?functionName={FunctionName}&code={connector_extension_key} +``` + +- `functionName` must exactly match the `@app.function_name(name="...")` value +- `connector_extension` is a system key auto-generated when the extension loads +- Locally (`func start`), the system key is not enforced + +### Trigger Config vs Connection ```text -Connector Gateway +Connector Namespace ├── connections/ -│ └── onedrive-test ← auth + runtime URL (connection-setup skill) +│ └── office365-conn ← auth + runtime URL (connection-setup skill) └── triggerConfigs/ - └── onedrive-newfile ← poll + callback config (THIS skill) + └── onnewemail-trigger ← poll + callback config (THIS skill) ``` -### Binary vs Metadata Triggers +## Scaffolding a Python Function App -Some connectors offer two variants of the same trigger: +### 1. Initialize with azd -| Variant | Example | Payload Shape | Body Field | -|---------|---------|---------------|------------| -| **File content (binary)** | `OnNewFileV2` | `{"body":""}` | String — base64-encoded file bytes | -| **Properties only (metadata)** | `OnNewFilesV2` | `{"body":{"value":[{...}]}}` | Object — array of typed metadata items | +```shell +azd init -t functions-quickstart-python-http-azd +``` + +### 2. Update host.json for preview extension bundle + +```json +{ + "version": "2.0", + "extensionBundle": { + "id": "Microsoft.Azure.Functions.ExtensionBundle.Preview", + "version": "[4.*, 5.0.0)" + } +} +``` + +### 3. Install packages -**Critical:** Both variants arrive with `Content-Type: application/json`. You cannot use content-type to distinguish them. Instead, parse the JSON and inspect whether `body` is a string or an object. +Add to `requirements.txt` (include packages based on your approach): -#### Identifying binary triggers +```text +# >=2.2.0b4 required for @app.connector_trigger decorator (Python 3.13+ only), no pinning required for generic trigger approaches +azure-functions>=2.2.0b4 + +# Currently only supports Office 365 OnNewEmail operation +azurefunctions-extensions-connectors -Binary triggers deliver raw file content as a base64-encoded string in the `body` field. Metadata triggers deliver structured data with a `body.value` array containing typed items. +# Required for @app.generic_trigger with typed SDK models or str payloads, don't include if using azurefunctions-extensions-connectors +azure-connectors +``` -Check the trigger operation name — triggers ending in a singular noun (e.g., `OnNewFile`, `OnUpdatedFile`) typically return binary content, while plural variants (e.g., `OnNewFiles`, `OnUpdatedFiles`) return metadata objects. +### 4. Create a ConnectorTrigger function -## Python Function App for Trigger Callbacks +#### With typed SDK payload (Office 365 email example) -### Scaffolding a New Project +```python +import azure.functions as func +import azurefunctions.extensions.connectors.office365 as office365 +import logging +from typing import List -Use `azd` with an HTTP trigger template to create a Python Function App that receives trigger callbacks: +app = func.FunctionApp() -1. **Initialize** with the Azure Functions Python quickstart: - ```shell - azd init -t functions-quickstart-python-http-azd - ``` +@app.function_name(name="OnNewEmail") +@app.connector_trigger(arg_name="emails") +def on_new_email(emails: List[office365.ClientReceiveMessage]) -> None: + logging.info("OnNewEmail trigger received") -2. **Create an HTTP trigger function** to receive callbacks. The Connector Gateway POSTs trigger payloads to your function's HTTP endpoint. + for email in emails: + logging.info(f"Subject: {email.subject}") + logging.info(f"From: {email.from_}") +``` -3. **Install the SDK**: +#### With connector trigger and raw JSON (when no typed SDK model exists) - ```shell - pip install azure-connectors - ``` +```python +import azure.functions as func +import json +import logging - Add to `requirements.txt`: +app = func.FunctionApp() - ```text - azure-connectors - ``` -4. **Build and deploy**: +@app.function_name(name="OnNewFile") +@app.connector_trigger(arg_name="payload") +def on_new_file(payload: str) -> None: + logging.info("OnNewFile trigger received") - ```shell - azd up - ``` + data = json.loads(payload) + body = data.get("body", {}) -### Example: HTTP Trigger Function for Email Callbacks + if isinstance(body, dict) and "value" in body: + # Metadata trigger — batch of items + for item in body["value"]: + logging.info(f"File: {item.get('name')}") + elif isinstance(body, str): + # Binary trigger — base64-encoded file content + import base64 + content = base64.b64decode(body) + logging.info(f"Received file content: {len(content)} bytes") +``` -Create an HTTP trigger function that receives Office 365 email trigger callbacks: +#### With generic trigger (when using Python <= 3.12) ```python import azure.functions as func import json import logging -from typing import Any - -from azure.connectors.office365 import TriggerBatchResponseGraphClientReceiveMessage app = func.FunctionApp() -@app.function_name(name="OnNewEmailReceived") -@app.route(route="triggers/email", methods=["POST"], auth_level=func.AuthLevel.FUNCTION) -async def on_new_email_received(req: func.HttpRequest) -> func.HttpResponse: - """Handle Office 365 email trigger callbacks from Connector Gateway.""" - logging.info("Received email trigger callback") +@app.function_name(name="OnNewFile") +@app.generic_trigger(arg_name="payload", type="connectorTrigger") +def on_new_file(payload: str) -> None: +... +``` - try: - body = req.get_json() +### 5. Run locally - # Parse the trigger payload using the SDK's typed class - payload = parse_trigger_payload(body, TriggerBatchResponseGraphClientReceiveMessage) +Start Azurite (required for `AzureWebJobsStorage`): - if payload and payload.value: - for email in payload.value: - logging.info(f"Subject: {email.subject}, From: {email.from_}") +```bash +npx azurite +``` + +Verify `local.settings.json`: - return func.HttpResponse("OK", status_code=200) - except Exception as e: - logging.error(f"Error processing trigger callback: {e}") - return func.HttpResponse(f"Error: {e}", status_code=500) +```json +{ + "IsEncrypted": false, + "Values": { + "AzureWebJobsStorage": "UseDevelopmentStorage=true", + "FUNCTIONS_WORKER_RUNTIME": "python" + } +} +``` +Start the Function App: -def parse_trigger_payload(body: dict[str, Any], payload_type: type) -> Any: - """Parse trigger callback body into typed payload.""" - if "body" in body and isinstance(body["body"], dict): - # Metadata trigger - body contains structured data - return payload_type(**body["body"]) if body["body"] else None - return None +```shell +func start ``` -The callback URL for this function would be: +The extension logs the webhook endpoint at startup: -```text -POST https://.azurewebsites.net/api/triggers/email?code= +``` +Connector endpoint: http://localhost:7071/runtime/webhooks/connector ``` -## Procedure +## Registering a Trigger Config ### Step 1: Get the Callback URL -Build the callback URL using your function's HTTP endpoint and function key: +#### Deployed Function App ```powershell $resourceGroup = "" $functionAppName = "" -$functionRoute = "triggers/email" # Your function's route +$functionName = "" # must match @app.function_name(name="...") -$functionKey = az functionapp keys list -g $resourceGroup -n $functionAppName --query "functionKeys.default" -o tsv -$callbackUrl = "https://$functionAppName.azurewebsites.net/api/$functionRoute?code=$functionKey" +$connectorExtensionKey = az functionapp keys list -g $resourceGroup -n $functionAppName --query "systemKeys.connector_extension" -o tsv +$callbackUrl = "https://$functionAppName.azurewebsites.net/runtime/webhooks/connector?functionName=$functionName&code=$connectorExtensionKey" ``` -> **Important:** The route must exactly match the `route` parameter in your `@app.route()` decorator. - -### Step 2: Get Trigger Parameters - -Each trigger operation requires specific parameters. Use the connector's list operations to discover folder IDs and other required values: - -> **Note:** Listing folders requires a data-plane call to the connection runtime URL. If you skipped access policies in the `connection-setup` skill (trigger-only flow), you must first add a local-dev access policy (`connection-setup` Step 5) to avoid 403 errors. +#### Local development (with dev tunnel) ```powershell -$runtimeUrl = "" # from connection-setup skill Step 4 - -# OneDrive - list folders -az rest --method GET ` - --uri "$runtimeUrl/datasets/default/folders" ` - --resource "https://apihub.azure.com" -o json - -# Office 365 - list mail folders -az rest --method GET ` - --uri "$runtimeUrl/Mail/Folders" ` - --resource "https://apihub.azure.com" -o json +$tunnelUrl = "" # e.g., https://-7071.uks1.devtunnels.ms +$functionName = "" +$callbackUrl = "$tunnelUrl/runtime/webhooks/connector?functionName=$functionName" ``` -### Step 3: Create Trigger Config +### Step 2: Create Trigger Config ```powershell $subscriptionId = "" $resourceGroup = "" -$gatewayName = "" -$gwId = "/subscriptions/$subscriptionId/resourceGroups/$resourceGroup/providers/Microsoft.Web/connectorGateways/$gatewayName" +$namespaceName = "" +$nsId = "/subscriptions/$subscriptionId/resourceGroups/$resourceGroup/providers/Microsoft.Web/connectorGateways/$namespaceName" -$triggerName = "" # e.g., "office365-newemail" -$connectionName = "" # e.g., "office365-test" +$triggerName = "" # e.g., "onnewemail-trigger" +$connectionName = "" # e.g., "office365-conn" $connectorName = "" # e.g., "office365" $operationName = "" # e.g., "OnNewEmailV3" -``` - -Build and send the PUT request. **Use `Invoke-WebRequest`** (not `az rest`) because `az rest` silently swallows error responses from this API: -```powershell $token = az account get-access-token ` --resource "https://management.core.windows.net/" ` --query "accessToken" -o tsv @@ -207,210 +251,164 @@ $body = @{ httpMethod = "Post" } parameters = @( - @{ name = "folderId"; value = "Inbox" } + # Office 365 OnNewEmailV3: + # @{ name = "folderPath"; value = "Inbox" } + + # OneDrive for Business OnNewFile: + # @{ name = "folderId"; value = "root" } ) } } | ConvertTo-Json -Depth 4 -$uri = "https://management.azure.com${gwId}/triggerConfigs/${triggerName}?api-version=2026-05-01-preview" +$uri = "https://management.azure.com${nsId}/triggerConfigs/${triggerName}?api-version=2026-05-01-preview" try { $response = Invoke-WebRequest -Uri $uri -Method PUT -Body $body ` -ContentType "application/json" ` -Headers @{ Authorization = "Bearer $token" } Write-Output "Status: $($response.StatusCode)" } catch { - Write-Output "Error: $($_.Exception.Response.StatusCode) $($_.Exception.Response.ReasonPhrase)" + Write-Output "Error: $($_.Exception.Response.StatusCode)" $_.ErrorDetails.Message } ``` -#### Alternative: Using az rest - -```powershell -$bodyFile = [System.IO.Path]::GetTempFileName() -$body | Out-File -FilePath $bodyFile -Encoding utf8 - -az rest --method PUT --url $uri --body "@$bodyFile" --headers "Content-Type=application/json" -Remove-Item $bodyFile -ErrorAction SilentlyContinue -``` - -Expected: HTTP 201 Created. - -### Step 4: Verify Trigger Config +### Step 3: Verify Trigger Config ```powershell az rest --method GET ` - --uri "https://management.azure.com${gwId}/triggerConfigs/${triggerName}?api-version=2026-05-01-preview" ` - --query "properties.{operation:operationName, state:state, hasCallback:notificationDetails.callbackUrl!=null}" ` + --uri "https://management.azure.com${nsId}/triggerConfigs/${triggerName}?api-version=2026-05-01-preview" ` + --query "properties.{operation:operationName, state:state, callback:notificationDetails.callbackUrl}" ` -o table ``` -Expected: `state = Enabled`, `hasCallback = True`. - -### Step 5: List All Trigger Configs +Expected: `state = Enabled`. -```powershell -az rest --method GET ` - --uri "https://management.azure.com${gwId}/triggerConfigs?api-version=2026-05-01-preview" ` - --query "value[].{name:name, operation:properties.operationName, state:properties.state}" ` - -o table -``` +### Step 4: Test the Trigger -### Step 6: Fire the Trigger +Trigger the connector event (e.g., send an email, upload a file). The Connector Namespace polls every 1-5 minutes. -Create content in the watched location to trigger the callback: +Watch Function App logs: ```powershell -# Example: send yourself an email to fire OnNewEmailV3 -# Or upload a file to OneDrive to fire OnNewFileV2 +func start # local +# or for deployed: +az functionapp log tail -g $resourceGroup -n $functionAppName ``` -The Connector Gateway polls the connector every 1-5 minutes. After polling detects the new content, it POSTs the trigger payload to your callback URL. +## Using the SDK for Actions (Beyond Triggers) -### Step 7: Verify Callback Received +The `azure-connectors` package provides typed async clients for calling connector actions directly from any Python application — Azure Functions, Flask, FastAPI, Django, scripts, etc. -Check function app logs: +### Example: Send an email -```powershell -az webapp log download -g $resourceGroup -n $functionAppName --log-file "$env:TEMP/func-logs.zip" -Expand-Archive -Path "$env:TEMP/func-logs.zip" -DestinationPath "$env:TEMP/func-logs" -Force -Get-ChildItem "$env:TEMP/func-logs" -Recurse -Filter "*.log" | - Sort-Object LastWriteTime -Descending | - Select-Object -First 3 | - ForEach-Object { Select-String -Path $_.FullName -Pattern "trigger callback" } -``` - -## API Schema Reference - -### TriggerConfig PUT Body +```python +import asyncio +from azure.connectors.office365 import Office365Client +from azure.connectors.sdk import ManagedIdentityTokenProvider + +async def main(): + # Connection runtime URL from Connector Namespace + connection_url = "https://..." + token_provider = ManagedIdentityTokenProvider() + + async with Office365Client(connection_url, token_provider) as client: + await client.send_email_async( + to="recipient@example.com", + subject="Hello from Python SDK", + body="

Sent from any Python app!

", + ) -```json -{ - "properties": { - "operationName": "OnNewEmailV3", - "connectionDetails": { - "connectorName": "office365", - "connectionName": "office365-test" - }, - "notificationDetails": { - "callbackUrl": "https://my-func.azurewebsites.net/api/triggers/email?code=", - "httpMethod": "Post" - }, - "parameters": [ - { "name": "folderId", "value": "Inbox" } - ] - } -} +asyncio.run(main()) ``` -### Property Names (Validated) - -| Property | Notes | -|----------|-------| -| `properties.connectionDetails` | **Not** `connectionName` at top level | -| `properties.connectionDetails.connectorName` | Required — the API connector name | -| `properties.connectionDetails.connectionName` | Required — the connection resource name | -| `properties.notificationDetails.callbackUrl` | **Missing = trigger has no target** — trigger provisions but never calls back | -| `properties.notificationDetails.httpMethod` | `"Post"` | -| `parameters[].name` | **Not** `parameterName` | -| `parameters[].value` | String value | - -### Common Errors - -| Error | Cause | Fix | -|-------|-------|-----| -| `Could not find member 'connectionName'` | Used `connectionName` instead of `connectionDetails` | Use `connectionDetails` with nested `connectorName` + `connectionName` | -| `Could not find member 'callbackUrl'` | Put `callbackUrl` at properties level | Wrap in `notificationDetails` | -| `Could not find member 'parameterName'` | Used `parameterName` in parameter array | Use `name` | -| `Cannot deserialize... into ConnectorGatewayOperationsParameter[]` | Parameters as object, not array | Use `[{"name":"...","value":"..."}]` array | -| `missing required property 'folderId'` | Required trigger parameter not provided | Add to parameters array | -| Trigger provisions but never fires callback | Missing `notificationDetails` or empty `notificationDetails.callbackUrl` | Add `notificationDetails` with a non-empty `callbackUrl` and `httpMethod` | -| `az rest` PUT returns no output/error | `az rest` swallows non-2xx responses silently | Use `Invoke-WebRequest` instead for PUT operations | - -## Handling Trigger Payloads in Python - -### Binary Content Triggers (e.g., OnNewFileV2) +### Example: List SharePoint items ```python -import base64 -import json -from typing import Any +from azure.connectors.sharepointonline import SharepointonlineClient +async def list_items(): + async with SharepointonlineClient(connection_url, token_provider) as client: + items = await client.get_items_async( + dataset="https://contoso.sharepoint.com/sites/MySite", + table="MyList" + ) + for item in items.get("value", []): + print(f"Item: {item.get('Title')}") +``` -def handle_binary_trigger(body: dict[str, Any]) -> bytes | None: - """Handle binary trigger payload (e.g., OnNewFileV2).""" - if "body" not in body: - return None - - body_value = body["body"] - - if isinstance(body_value, str): - # Binary trigger — body is base64-encoded file content. - # NOTE: The base64 string may be wrapped in extra quotes - # from the Logic Apps expression engine. Strip them. - base64_content = body_value.strip('"') +### Example: List Teams - if base64_content: - try: - return base64.b64decode(base64_content) - except Exception: - # Invalid base64 payload - return None +```python +from azure.connectors.teams import TeamsClient - return None +async def list_teams(): + async with TeamsClient(connection_url, token_provider) as client: + teams = await client.get_all_teams_async() + for team in teams.get("value", []): + print(f"Team: {team.get('displayName')}") ``` -### Metadata Triggers (e.g., OnNewFilesV2, OnNewEmailV3) +### Authentication Options ```python -from dataclasses import dataclass -from typing import Any +from azure.connectors.sdk import ManagedIdentityTokenProvider, ConnectionStringTokenProvider + +# System-assigned managed identity (recommended for Azure) +token_provider = ManagedIdentityTokenProvider() -from azure.connectors.office365 import TriggerBatchResponseGraphClientReceiveMessage +# User-assigned managed identity +token_provider = ManagedIdentityTokenProvider(client_id="your-client-id") +# Azure Identity credentials (DefaultAzureCredential, AzureCliCredential, etc.) +from azure.identity.aio import DefaultAzureCredential +credential = DefaultAzureCredential() +client = Office365Client(connection_url, credential) +``` -def handle_metadata_trigger(body: dict[str, Any]) -> list[Any]: - """Handle metadata trigger payload (e.g., OnNewEmailV3).""" - # Callback JSON shape: {"body":{"value":[{...item...}]}} - if "body" not in body: - return [] +### Validated Connectors - body_value = body["body"] +| Connector | Package | Status | +|-----------|---------|--------| +| Office 365 Outlook | `azure.connectors.office365` | ✅ Complete | +| SharePoint Online | `azure.connectors.sharepointonline` | ✅ Complete | +| Microsoft Teams | `azure.connectors.teams` | ✅ Complete | +| Azure Data Explorer | `azure.connectors.kusto` | ✅ Complete | +| MS Graph Groups & Users | `azure.connectors.msgraphgroupsanduser` | ✅ Complete | +| Office 365 Users | `azure.connectors.office365users` | ✅ Complete | +| Azure Blob Storage | `azure.connectors.azureblob` | ✅ Complete | +| IBM MQ | `azure.connectors.mq` | ✅ Complete | - if isinstance(body_value, dict) and "value" in body_value: - return body_value["value"] +> All SDKs support generating clients for any of the 1,000+ Azure managed connectors. - return [] +## Trigger Payload Shapes +The Connector Namespace delivers callbacks in two shapes depending on the connector's `splitOn` setting: -# Using SDK typed payload classes -def handle_email_trigger(body: dict[str, Any]) -> TriggerBatchResponseGraphClientReceiveMessage | None: - """Handle Office 365 email trigger with typed payload.""" - if "body" in body and isinstance(body["body"], dict): - return TriggerBatchResponseGraphClientReceiveMessage(**body["body"]) - return None -``` +| Shape | JSON Structure | When | +|-------|---------------|------| +| Batch | `{"body": {"value": [...items...]}}` | splitOn disabled (default) | +| Single-item | `{"body": {...item properties...}}` | splitOn enabled | -### Detecting the Variant at Runtime +The SDK's `TriggerCallbackPayload.from_json()` and `TriggerCallbackBody.from_dict()` methods normalize both shapes into a list, so consumers always iterate: ```python -from typing import Any +from azure.connectors.sdk import TriggerCallbackPayload +payload = TriggerCallbackPayload.from_json(raw_json) +for item in payload.body.value: + print(item) +``` -def detect_trigger_variant(body: dict[str, Any]) -> str: - """Detect whether this is a binary or metadata trigger payload.""" - if "body" not in body: - return "unknown" +## Common Errors - body_value = body["body"] +| Error | Cause | Fix | +|-------|-------|-----| +| `Could not find member 'connectionName'` | Used `connectionName` at top level | Wrap in `connectionDetails` object | +| `Could not find member 'callbackUrl'` | Put `callbackUrl` at properties level | Wrap in `notificationDetails` object | +| `Could not find member 'parameterName'` | Used `parameterName` in params array | Use `name` field instead | +| Trigger provisions but never fires | Missing `notificationDetails` or empty `callbackUrl` | Ensure `notificationDetails.callbackUrl` is set | +| `az rest` PUT returns no output | `az rest` swallows non-2xx responses | Use `Invoke-WebRequest` for PUT operations | - if isinstance(body_value, str): - # Binary content trigger (OnNewFileV2, OnUpdatedFileV2) - return "binary" - elif isinstance(body_value, dict): - # Metadata trigger (OnNewFilesV2, OnUpdatedFilesV2, OnNewEmailV3) - return "metadata" +## Reference - return "unknown" -``` -``` \ No newline at end of file +For a complete mapping of trigger operations to function signatures across .NET, Python, and TypeScript, see [Operations to Functions Signature Match](https://github.com/Azure/azure-functions-connector-extension/blob/main/docs/operations-functions-match.md). diff --git a/README.md b/README.md index e0b9a97..d0fb1d9 100644 --- a/README.md +++ b/README.md @@ -221,7 +221,7 @@ client = Office365Client(connection_url, token_provider, options) ## Project Structure -``` +```text azure-connectors/ ├── src/azure/connectors/ │ ├── sdk/ # Core SDK infrastructure @@ -261,6 +261,7 @@ for message in messages: ``` The `from_json` method handles: + - JSON string or dictionary input - Nested `body.value` payload structure - Field name conversion (camelCase → snake_case) @@ -271,8 +272,10 @@ This feature is particularly useful when building Azure Functions that process c ## Related Projects -- **[Connectors .NET SDK](https://github.com/Azure/Connectors-NET-SDK)** — .NET implementation of this SDK - **[Azure Functions Connector Extension](https://github.com/Azure/azure-functions-connector-extension)** - An Azure Functions trigger extension for receiving webhook callbacks from Connector Namespace managed connectors +- **[Connectors .NET SDK](https://github.com/Azure/Connectors-NET-SDK)** — .NET implementation of this SDK +- **[Connectors Node.js SDK](https://github.com/Azure/connectors-nodejs-sdk)** — Node.js implementation of this SDK + ## Contributing This project welcomes contributions and suggestions. Most contributions require you to agree to a Contributor License Agreement (CLA) declaring that you have the right to, and actually do, grant us the rights to use your contribution. For details, visit https://cla.opensource.microsoft.com. diff --git a/ROADMAP.md b/ROADMAP.md index 7db5433..dcb3391 100644 --- a/ROADMAP.md +++ b/ROADMAP.md @@ -150,7 +150,7 @@ This document tracks the development roadmap for the Azure Connectors Python SDK - [ ] **Trigger support** - [ ] Polling triggers (webhooks where supported) - - [ ] Connector Gateway integration + - [ ] Connector Namespace integration - [ ] Event Grid integration - [ ] **Batch operations** - [ ] Bulk send for email @@ -411,13 +411,13 @@ For each new connector, complete these steps: - **[M365 Agent POC](https://github.com/coreai-microsoft/m365-agent-poc)** — Working POC using today's GA connectors with Functions (LA Connector as trigger layer, Functions as code runner). Found v1/v2/v3 connector version surprises and webhook subscribe 404 issues. Analyzed internally. - **[Codeful Workflows SDK](https://github.com/AzureAD/logicapps-sdk)** (`logicapps-sdk`) — C# code-to-workflow compiler. Generates typed trigger classes for all 1000+ managed connectors. Compiles to workflow.json and delegates trigger lifecycle to Logic Apps runtime. Different approach from our direct-invocation SDK but shares swagger-to-trigger-model generation. Analyzed internally. - **BPM Runtime Trigger Architecture** — Analyzed the Logic Apps runtime trigger implementation: three trigger strategies (polling, pure webhook, notification hybrid), dual-job provisioning, class hierarchy, coordination protocol. Extraction plan and trigger taxonomy documentation available internally. -- **Connector Gateway API Design** — Control plane API design for `Microsoft.Web/connectorGateways` (internal document). +- **Connector Namespace API Design** — Control plane API design for `Microsoft.Web/connectorGateways` (internal document). ### Backlog -1. **Transition to Connector Gateway API** (`Microsoft.Web/connectorGateways`) - - The Connector Gateway resource exposes connections and triggers as data-plane resources without requiring Logic Apps. Available in First Release / TIP regions (as of 2026-03-05) but not yet GA. - - **Blocked on:** Connector Gateway reaching GA with stable API surface. +1. **Transition to Connector Namespace API** (`Microsoft.Web/connectorGateways`) + - The Connector Namespace resource exposes connections and triggers as data-plane resources without requiring Logic Apps. Available in First Release / TIP regions (as of 2026-03-05) but not yet GA. + - **Blocked on:** Connector Namespace reaching GA with stable API surface. - [ ] Monitor Connector Namespace API availability and documentation - [ ] Evaluate migration path from `Microsoft.Web/connections` direct access - [ ] Update SDK connection setup to support Connector Namespace resource type @@ -427,7 +427,7 @@ For each new connector, complete these steps: 1. **Multi-language trigger SDK** (Python, Node.js, Java) 1. **Connection setup automation** (VS Code extension / LSP) - - AI agents can already automate the full connection lifecycle via the `connection-setup` skill (`.github/skills/connection-setup/SKILL.md`): Connector Gateway creation, connection creation, OAuth consent link, access policies, and settings injection. + - AI agents can already automate the full connection lifecycle via the `connection-setup` skill (`.github/skills/connection-setup/SKILL.md`): Connector Namespace creation, connection creation, OAuth consent link, access policies, and settings injection. - **Next:** Build native VS Code extension support (or LSP ConnectionsService) for in-editor connection creation with UI, following the building blocks documented in the skill file. - [ ] LSP ConnectionsService: detect existing connections, guided creation flow - [ ] VS Code command palette: "Add Connector Connection" with connector picker @@ -446,7 +446,7 @@ For each new connector, complete these steps: |------------|--------|--------| | BPM generator PR merged to master | ⏳ Awaiting review | Can work from feature branch, but need master for stability | | OAuth connections for testing | ✅ Office365 works | Need connection setup for each new connector | -| Connector Gateway GA | ⬜ Not yet GA | Using `Microsoft.Web/connections` direct access until then | +| Connector Namespace GA | ⬜ Not yet GA | Using `Microsoft.Web/connections` direct access until then | | Functions extension for triggers | ⬜ Functions team owns | We provide SDK + connector-side delivery; they build the extension | | MWF sync cadence | ✅ Established 2026-03-13 | Cross-team alignment on Connectors for Functions | diff --git a/SUPPORT.md b/SUPPORT.md index a55d7be..4849490 100644 --- a/SUPPORT.md +++ b/SUPPORT.md @@ -8,9 +8,8 @@ For help and questions about using this project, please use the following resour - **Browse the [documentation](docs/)** - Start with [README.md](README.md) for quick start and overview - **Check [GitHub Issues](https://github.com/Azure/Connectors-Python-SDK/issues)** - Search for existing solutions or similar questions -- **Ask in [GitHub Discussions](https://github.com/Azure/LogicApps/discussions)** - Community Q&A for Logic Apps and Connectors - **Review the [samples](samples/)** - See working examples for Office 365, SharePoint, Teams, and Kusto -- **Stack Overflow** - Tag questions with `azure-connectors` or `azure-logic-apps` for Python SDK questions +- **Stack Overflow** - Tag questions with `azure-connectors` for Python SDK questions ## Microsoft Support Policy @@ -18,13 +17,11 @@ Support for this project is limited to the resources listed above. The product s If you encounter issues with the underlying Azure connector infrastructure (not the Python SDK): -- **Azure Logic Apps** - Use Azure Portal support for Logic Apps Standard - **API Management** - Use Azure Portal support for API Management - **Azure Functions** - Use Azure Portal support for Functions runtime ## Community Resources -- **[Azure Logic Apps Documentation](https://learn.microsoft.com/azure/logic-apps/)** - Official Logic Apps docs - **[Connector Reference](https://learn.microsoft.com/connectors/connector-reference/)** - Browse 1,000+ connectors - **[Azure Functions Python Guide](https://learn.microsoft.com/azure/azure-functions/functions-reference-python)** - Python Functions development @@ -39,5 +36,3 @@ See [SECURITY.md](SECURITY.md) for more information. ## Related Projects - **[Connectors .NET SDK](https://github.com/Azure/Connectors-NET-SDK)** - .NET implementation of this SDK -- **[Azure Logic Apps](https://github.com/Azure/LogicApps)** - Workflow engine that powers connectors - diff --git a/docs/connection-setup.md b/docs/connection-setup.md index f4034fc..f9bd18f 100644 --- a/docs/connection-setup.md +++ b/docs/connection-setup.md @@ -33,43 +33,43 @@ For Office365 connections, use a different test path: ## Connection Types -There are two ways to create connections, depending on whether you use the Connector Gateway: +There are two ways to create connections, depending on whether you use the Connector Namespace: -### Option A: Connector Gateway Connection (Recommended for Triggers) +### Option A: Connector Namespace Connection (Recommended for Triggers) -Connector Gateway connections are required for connector triggers (Connector Gateway manages the polling infrastructure). They also work for actions. +Connector Namespace connections are required for connector triggers (Connector Namespace manages the polling infrastructure). They also work for actions. -#### A1. Create an Connector Gateway +#### A1. Create a Connector Namespace ```powershell $subscriptionId = "" $resourceGroup = "" -$gatewayName = "" +$connectorNamespace = "" $location = "" # e.g., "brazilsouth" az rest --method PUT ` - --uri "https://management.azure.com/subscriptions/$subscriptionId/resourceGroups/$resourceGroup/providers/Microsoft.Web/connectorGateways/$gatewayName?api-version=2026-05-01-preview" ` + --uri "https://management.azure.com/subscriptions/$subscriptionId/resourceGroups/$resourceGroup/providers/Microsoft.Web/connectorGateways/$connectorNamespace?api-version=2026-05-01-preview" ` --body "{`"location`":`"$location`",`"properties`":{}}" ``` -#### A2. Create a Connection in the Connector Gateway +#### A2. Create a Connection in the Connector Namespace ```powershell $connectorName = "office365" # API connector name $connectionName = "office365-test" az rest --method PUT ` - --uri "https://management.azure.com/subscriptions/$subscriptionId/resourceGroups/$resourceGroup/providers/Microsoft.Web/connectorGateways/$gatewayName/connections/$connectionName?api-version=2026-05-01-preview" ` + --uri "https://management.azure.com/subscriptions/$subscriptionId/resourceGroups/$resourceGroup/providers/Microsoft.Web/connectorGateways/$connectorNamespace/connections/$connectionName?api-version=2026-05-01-preview" ` --body "{`"properties`":{`"connectorName`":`"$connectorName`"}}" ``` The connection is created in an **unauthenticated** state. You must complete OAuth consent. -#### A3. OAuth Consent via Connector Gateway Manager Portal +#### A3. OAuth Consent via Connector Namespace Manager Portal -1. Open the [Connector Gateway Manager Portal](https://nice-desert-04d03581e.2.azurestaticapps.net/) +1. Open the [Connector Namespace Manager Portal](https://nice-desert-04d03581e.2.azurestaticapps.net/) 2. Run the command shown on the portal to get an ARM token, paste it, and save -3. Select your Connector Gateway — the connections will appear +3. Select your Connector Namespace — the connections will appear 4. Click **Authorize** on the connection requiring consent 5. Complete the OAuth flow. Status changes from `Error` to `Connected`. @@ -79,12 +79,12 @@ The connection is created in an **unauthenticated** state. You must complete OAu After OAuth consent, the connection runtime URL is available: -- In the Connector Gateway Manager Portal: select the connection → copy the runtime URL +- In the Connector Namespace Manager Portal: select the connection → copy the runtime URL - Via ARM API: ```powershell $result = az rest --method GET ` - --uri "https://management.azure.com/subscriptions/$subscriptionId/resourceGroups/$resourceGroup/providers/Microsoft.Web/connectorGateways/$gatewayName/connections/$connectionName?api-version=2026-05-01-preview" ` + --uri "https://management.azure.com/subscriptions/$subscriptionId/resourceGroups/$resourceGroup/providers/Microsoft.Web/connectorGateways/$connectorNamespace/connections/$connectionName?api-version=2026-05-01-preview" ` -o json | ConvertFrom-Json $result.properties.connectionRuntimeUrl ``` @@ -99,7 +99,7 @@ $tenantId = "" $policyName = "functionapp-msi" az rest --method PUT ` - --uri "https://management.azure.com/subscriptions/$subscriptionId/resourceGroups/$resourceGroup/providers/Microsoft.Web/connectorGateways/$gatewayName/connections/$connectionName/accessPolicies/$policyName?api-version=2026-05-01-preview" ` + --uri "https://management.azure.com/subscriptions/$subscriptionId/resourceGroups/$resourceGroup/providers/Microsoft.Web/connectorGateways/$connectorNamespace/connections/$connectionName/accessPolicies/$policyName?api-version=2026-05-01-preview" ` --body "{`"properties`":{`"principal`":{`"type`":`"ActiveDirectory`",`"identity`":{`"objectId`":`"$msiObjectId`",`"tenantId`":`"$tenantId`"}}}}" ` --headers "Content-Type=application/json" ``` @@ -110,7 +110,7 @@ The same connection (and access policy) is used for **both triggers and actions* ### Option B: Standalone ARM Connection (Actions Only) -Standalone connections work for actions but do not support Connector Gateway triggers. +Standalone connections work for actions but do not support Connector Namespace triggers. #### Manual Steps (Option B — Standalone) diff --git a/samples/sample_connector_usage/sample_connector_usage_azureblob.py b/samples/sample_connector_usage/sample_connector_usage_azureblob.py index bed2936..8792f56 100644 --- a/samples/sample_connector_usage/sample_connector_usage_azureblob.py +++ b/samples/sample_connector_usage/sample_connector_usage_azureblob.py @@ -7,7 +7,7 @@ Prerequisites: 1. Azure subscription with Azure Blob Storage connection -2. Azure Blob Storage connection in Azure Logic Apps +2. Azure Blob Storage connection in Connector Namespaces 3. Connection runtime URL from Azure Portal Note: Azure Blob Storage uses key-based auth (accountName + accessKey), not OAuth. diff --git a/samples/sample_connector_usage/sample_connector_usage_kusto.py b/samples/sample_connector_usage/sample_connector_usage_kusto.py index 5e90534..59f1ea7 100644 --- a/samples/sample_connector_usage/sample_connector_usage_kusto.py +++ b/samples/sample_connector_usage/sample_connector_usage_kusto.py @@ -7,7 +7,7 @@ Prerequisites: 1. Azure subscription with Kusto (Azure Data Explorer) cluster -2. Kusto connection in Azure Logic Apps +2. Kusto connection in Connector Namespaces 3. Connection runtime URL from Azure Portal 4. Kusto cluster URL and database name diff --git a/samples/sample_connector_usage/sample_connector_usage_mq.py b/samples/sample_connector_usage/sample_connector_usage_mq.py index 69f6312..99f1a9a 100644 --- a/samples/sample_connector_usage/sample_connector_usage_mq.py +++ b/samples/sample_connector_usage/sample_connector_usage_mq.py @@ -7,7 +7,7 @@ Prerequisites: 1. Azure subscription with IBM MQ connection -2. IBM MQ connection in Azure Logic Apps +2. IBM MQ connection in Connector Namespaces 3. Connection runtime URL from Azure Portal Note: IBM MQ uses parameter-based auth (server, queue manager, channel, credentials). diff --git a/samples/sample_connector_usage/sample_connector_usage_msgraphgroupsanduser.py b/samples/sample_connector_usage/sample_connector_usage_msgraphgroupsanduser.py index d9bf263..9c81f75 100644 --- a/samples/sample_connector_usage/sample_connector_usage_msgraphgroupsanduser.py +++ b/samples/sample_connector_usage/sample_connector_usage_msgraphgroupsanduser.py @@ -7,7 +7,7 @@ Prerequisites: 1. Azure subscription with MS Graph Groups & Users connection -2. MS Graph connection in Azure Logic Apps +2. MS Graph connection in Connector Namespaces 3. Connection runtime URL from Azure Portal Installation: diff --git a/samples/sample_connector_usage/sample_connector_usage_office365.py b/samples/sample_connector_usage/sample_connector_usage_office365.py index c8c4d60..db06f4d 100644 --- a/samples/sample_connector_usage/sample_connector_usage_office365.py +++ b/samples/sample_connector_usage/sample_connector_usage_office365.py @@ -7,7 +7,7 @@ Prerequisites: 1. Azure subscription with Office365 connection -2. Office365 connection in Azure Logic Apps +2. Office365 connection in Connector Namespaces 3. Connection runtime URL from Azure Portal Installation: diff --git a/samples/sample_connector_usage/sample_connector_usage_office365users.py b/samples/sample_connector_usage/sample_connector_usage_office365users.py index dcf6eb3..9ea6a1c 100644 --- a/samples/sample_connector_usage/sample_connector_usage_office365users.py +++ b/samples/sample_connector_usage/sample_connector_usage_office365users.py @@ -7,7 +7,7 @@ Prerequisites: 1. Azure subscription with Office 365 Users connection -2. Office 365 Users connection in Azure Logic Apps +2. Office 365 Users connection in Connector Namespaces 3. Connection runtime URL from Azure Portal Installation: diff --git a/samples/sample_connector_usage/sample_connector_usage_sharepoint.py b/samples/sample_connector_usage/sample_connector_usage_sharepoint.py index c5ca743..e0e7c6b 100644 --- a/samples/sample_connector_usage/sample_connector_usage_sharepoint.py +++ b/samples/sample_connector_usage/sample_connector_usage_sharepoint.py @@ -7,7 +7,7 @@ Prerequisites: 1. Azure subscription with SharePoint Online connection -2. SharePoint Online connection in Azure Logic Apps +2. SharePoint Online connection in Connector Namespaces 3. Connection runtime URL from Azure Portal 4. SharePoint site URL diff --git a/samples/sample_connector_usage/sample_connector_usage_teams.py b/samples/sample_connector_usage/sample_connector_usage_teams.py index c903531..68d0c5d 100644 --- a/samples/sample_connector_usage/sample_connector_usage_teams.py +++ b/samples/sample_connector_usage/sample_connector_usage_teams.py @@ -5,7 +5,7 @@ Prerequisites: 1. Azure subscription with Teams connection -2. Teams connection in Azure Logic Apps +2. Teams connection in Connector Namespaces 3. Connection runtime URL from Azure Portal Installation: diff --git a/src/azure/__init__.py b/src/azure/__init__.py index 63397d5..639e28f 100644 --- a/src/azure/__init__.py +++ b/src/azure/__init__.py @@ -1,7 +1,7 @@ # Copyright (c) Microsoft Corporation. All rights reserved. """ -Azure namespace package. +Azure Connectors Python SDK package. """ __path__ = __import__('pkgutil').extend_path(__path__, __name__) diff --git a/src/azure/connectors/__init__.py b/src/azure/connectors/__init__.py index 595ba68..a7aa5ca 100644 --- a/src/azure/connectors/__init__.py +++ b/src/azure/connectors/__init__.py @@ -1,9 +1,9 @@ # Copyright (c) Microsoft Corporation. All rights reserved. """ -Azure Logic Apps Connector SDK for Python. +Azure Connectors SDK for Python. -This package provides infrastructure for calling Azure Logic Apps connectors +This package provides infrastructure for calling Azure Connectors from Python applications, including authentication, HTTP clients, and strongly-typed generated connector clients. """ diff --git a/src/azure/connectors/sdk/__init__.py b/src/azure/connectors/sdk/__init__.py index fbac4e5..97d72f2 100644 --- a/src/azure/connectors/sdk/__init__.py +++ b/src/azure/connectors/sdk/__init__.py @@ -1,9 +1,9 @@ # Copyright (c) Microsoft Corporation. All rights reserved. """ -Azure Logic Apps Connector SDK for Python. +Azure Connectors SDK for Python. -This package provides infrastructure for calling Azure Logic Apps connectors +This package provides infrastructure for calling Azure Connectors from Python applications, including authentication, HTTP clients, and strongly-typed generated connector clients. """ diff --git a/src/pyproject.toml b/src/pyproject.toml index 9c407c6..ae5c7b0 100644 --- a/src/pyproject.toml +++ b/src/pyproject.toml @@ -2,14 +2,13 @@ name = "azure-connectors" dynamic = ["version"] requires-python = ">=3.10" -description = "Azure Logic Apps Connector SDK for Python" +description = "Azure Connectors SDK for Python" readme = "README.md" license = { text = "MIT" } authors = [ - { name = "Azure Logic Apps team at Microsoft Corp.", email = "azurelogicapps@microsoft.com" } + { name = "Azure Connector Namespaces team at Microsoft Corp.", email = "azurelogicapps@microsoft.com" } ] -keywords = ["azure", "connectors", "logic-apps", - "python", "serverless"] +keywords = ["azure", "connectors", "python", "serverless"] classifiers = [ "Programming Language :: Python", "Programming Language :: Python :: 3", diff --git a/tests/README.md b/tests/README.md index 94431fa..0c7496a 100644 --- a/tests/README.md +++ b/tests/README.md @@ -1,6 +1,6 @@ # Unit Tests -This directory contains comprehensive unit tests for the Azure Logic Apps Connectors Python SDK. +This directory contains comprehensive unit tests for the Azure Connectors Python SDK. ## Test Structure diff --git a/tests/__init__.py b/tests/__init__.py index 8778690..7bf2e26 100644 --- a/tests/__init__.py +++ b/tests/__init__.py @@ -1,3 +1,3 @@ # Copyright (c) Microsoft Corporation. All rights reserved. -"""Unit tests for Azure Logic Apps Connector SDK.""" +"""Unit tests for Azure Connectors SDK."""