Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions .github/skills/trigger-registration/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -116,10 +116,10 @@ There is no `ConnectorTrigger` template yet. Use `azd` with an HTTP trigger temp

### Example: ConnectorTrigger Function

Use the `[ConnectorTrigger]` attribute with SDK typed payloads for POCO binding. Payload types are in the `Microsoft.Azure.Connectors.DirectClient.<Connector>` namespace:
Use the `[ConnectorTrigger]` attribute with SDK typed payloads for POCO binding. Payload types are in the `Microsoft.Azure.Connectors.Sdk.<Connector>` namespace:

```csharp
using Microsoft.Azure.Connectors.DirectClient.Office365;
using Microsoft.Azure.Connectors.Sdk.Office365;
using Microsoft.Azure.Functions.Worker;
using Microsoft.Extensions.Logging;

Expand Down
10 changes: 10 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,9 +7,19 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0

## [Unreleased]

### Changed

- **Breaking:** Generated connector clients now inherit from `ConnectorClientBase` instead of implementing `IDisposable` directly (#88)
- **Breaking:** Per-connector exception types (e.g., `Office365ConnectorException`, `TeamsConnectorException`) replaced with unified `ConnectorException` base type with `ConnectorName`, `Operation`, `StatusCode`, and `ResponseBody` properties (#88)
- **Breaking:** Generated client constructors accept a new optional `ConnectorClientOptions` parameter for configuring retry policy, timeout, and exponential backoff — the `HttpClient` parameter moved from position 3 to position 4 (#88)
- Generated clients now use SDK infrastructure (`ConnectorHttpClient`) for authentication, retry with exponential backoff, OpenTelemetry instrumentation, and SSRF-protected URL resolution (#88)

### Added

- Azure Monitor Logs (`azuremonitorlogs`) generated typed client for querying Log Analytics workspaces and Application Insights — includes QueryData, QueryDataV2, VisualizeQuery, VisualizeQueryV2 operations with dynamic schema support for query results
- `TokenCredentialTokenProvider` adapter — wraps any `Azure.Core.TokenCredential` as an `ITokenProvider` for the SDK's HTTP pipeline (#88)
- `ConnectorException` — unified exception type for all connector API failures (#88)
- `ConnectorClientBase` now provides `CallConnectorAsync`, `ResolveUrl`, shared JSON options, and convenience constructors accepting `connectionRuntimeUrl` + `TokenCredential` (#88)

### Removed

Expand Down
2 changes: 1 addition & 1 deletion GENERATION.md
Original file line number Diff line number Diff line change
Expand Up @@ -118,7 +118,7 @@ LogicAppsCompiler.exe <output-directory> unused --managedConnectors
using System.Text.Json.Serialization;
// ... other usings

namespace Microsoft.Azure.Connectors.DirectClient.Office365;
namespace Microsoft.Azure.Connectors.Sdk.Office365;

#region Types

Expand Down
25 changes: 23 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -75,14 +75,26 @@ Copy the generated `*Extensions.cs` files to your project.
### 3. Use the Typed Client

```csharp
using Microsoft.Azure.Connectors.DirectClient.Office365;
using System;
using Microsoft.Azure.Connectors.Sdk;
using Microsoft.Azure.Connectors.Sdk.Office365;

Comment thread
daviburg marked this conversation as resolved.
// Get connection runtime URL from Azure Portal
var connectionRuntimeUrl = "https://...";

// Create client (uses DefaultAzureCredential by default)
// Create client with default settings (uses DefaultAzureCredential)
using var client = new Office365Client(connectionRuntimeUrl);

// Or configure retry, timeout, and backoff
using var clientWithOptions = new Office365Client(
connectionRuntimeUrl,
options: new ConnectorClientOptions
{
MaxRetryAttempts = 5,
Timeout = TimeSpan.FromSeconds(60),
UseExponentialBackoff = true
});
Comment thread
daviburg marked this conversation as resolved.

// Call typed operations
await client.SendEmailAsync(new SendEmailInput
{
Expand All @@ -96,12 +108,21 @@ var categories = await client.GetOutlookCategoryNamesAsync();

## SDK Components

### Client Base

| Component | Description |
|-----------|-------------|
| `ConnectorClientBase` | Abstract base class for all generated clients — provides authentication, retry, OTel tracing, JSON serialization, and SSRF-protected URL resolution |
| `ConnectorClientOptions` | Configuration for retry count, timeout, exponential backoff, and initial retry delay |
| `ConnectorException` | Unified exception for connector API failures with `ConnectorName`, `Operation`, `StatusCode`, and `ResponseBody` |

### Authentication

| Provider | Use Case |
|----------|----------|
| `ManagedIdentityTokenProvider` | Azure-hosted apps (App Service, Functions) |
| `ConnectionStringTokenProvider` | Local development with API keys |
| `TokenCredentialTokenProvider` | Wraps any `Azure.Core.TokenCredential` (DefaultAzureCredential, etc.) |

### HTTP

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
//------------------------------------------------------------
// Copyright (c) Microsoft Corporation. All rights reserved.
//------------------------------------------------------------

using global::Azure.Core;

namespace Microsoft.Azure.Connectors.Sdk.Authentication
{
/// <summary>
/// Adapts an <see cref="TokenCredential"/> (from Azure.Core) to the SDK's
/// <see cref="ITokenProvider"/> interface, enabling generated clients to accept
/// any Azure credential (DefaultAzureCredential, ManagedIdentityCredential, etc.).
/// </summary>
public class TokenCredentialTokenProvider : ITokenProvider
{
private readonly TokenCredential _credential;

/// <summary>
/// Initializes a new instance of the <see cref="TokenCredentialTokenProvider"/> class.
/// </summary>
/// <param name="credential">The Azure credential to wrap.</param>
public TokenCredentialTokenProvider(TokenCredential credential)
{
ArgumentNullException.ThrowIfNull(credential);
this._credential = credential;
}

/// <inheritdoc />
public async Task<string> GetAccessTokenAsync(string[] scopes, CancellationToken cancellationToken = default)
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

General suggestion: I recommend always having CancellationToken cancellationToken be a required parameter with very few exceptional circumstances. Allowing it to be unused by the caller can introduce unexpected hangs, soft-locks, and degraded performance.

This suggestion is speaking with my own .NET experience and preferences, and it is not aligned with the Azure SDK .NET guidelines.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I paid the significant cost of updating thousands of lines of the Azure MCP server code to plumb through CancellationToken parameters during our drive to release a remote HTTP mode.
It took me
a lot
of time

{
ArgumentNullException.ThrowIfNull(scopes);

if (scopes.Length == 0)
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nit: checking the length, but not the contents of the array. Do we care about null or empty string array elements?

{
throw new ArgumentException(message: "At least one scope must be provided.", paramName: nameof(scopes));
}

var context = new TokenRequestContext(scopes);
var token = await this._credential
.GetTokenAsync(context, cancellationToken)
.ConfigureAwait(continueOnCapturedContext: false);
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why include ConfigureAwait(false)?


return token.Token;
}
}
}
Loading
Loading