Skip to content

Update samples for Azure Connectors SDK 0.14 - #72

Merged
David Burg (daviburg) merged 8 commits into
mainfrom
feature/sdk-0.14-samples
Sep 1, 2026
Merged

Update samples for Azure Connectors SDK 0.14#72
David Burg (daviburg) merged 8 commits into
mainfrom
feature/sdk-0.14-samples

Conversation

@daviburg

@daviburg David Burg (daviburg) commented Aug 19, 2026

Copy link
Copy Markdown
Member

Summary

  • Update Azure.Connectors.Sdk from 0.13.0-preview.1 to 0.14.0-preview.1.
  • Add Azure Functions samples for 12 connected typed clients that previously had no sample coverage: Azure Automation, Azure Digital Twins, Azure Virtual Machines, Azure Key Vault, Microsoft Bookings, Microsoft 365 Groups, Microsoft 365 Groups Mail, OneNote, Planner, Power BI, Shifts, and Microsoft To Do.
  • Demonstrate a top-level external action for every newly sampled client except Microsoft Bookings; internal discovery helpers are otherwise retained only to obtain valid action inputs.
  • Demonstrate two safe Teams operations added in SDK 0.14: GetTeamAsync and ListTeamMembersAsync.
  • Demonstrate the SDK 0.14 Azure Queues message contract through Messages.QueueMessagesList.QueueMessage and the renamed NextVisibleTime C# property.
  • Expand the configuration template and connector inventory to 36 sampled connectors.

SDK 0.14 contract coverage

  • Planner compiles and runs against AsyncPageable<GetTaskResponseV2>, covering the 0.14 versioned type rename.
  • Microsoft 365 Groups and Microsoft 365 Groups Mail enumerate every page returned by ListGroupsAsync and ListConversationsAsync so sample responses are not truncated.
  • Microsoft Bookings is discovery-only because SDK 0.14 exposes appointment operations as Connector Namespace triggers rather than public client action methods; the sample lists booking pages used as the required SMTP address trigger input.
  • Azure Queues uses POST to receive and hide messages, then projects both NextVisibleTime and the PopReceipt required to delete each message.
  • Teams exercises two newly generated public operations using an ID returned by GetAllTeamsAsync.
  • Azure Automation creates a real runbook job through CreateJobAsync and reads it through GetStatusOfJobAsync.
  • Azure VM, Microsoft 365 Groups Mail, Power BI, and Shifts use discovery results to call VirtualMachineGetAsync, ListConversationsAsync, GetScorecardsAsync, and ListShiftsCrossTeamAsync.
  • Mutating VM power operations and Key Vault secret-value reads are deliberately excluded.

Validation

  • dotnet test Connectors-NET-Samples.sln --configuration Release: 76 passed, 0 failed.
  • dotnet format Connectors-NET-Samples.sln --verify-no-changes --no-restore: passed.
  • Published and deployed SDK 0.14 samples to the BTS4 validation Function App. Final active deployment: 0eba74f59c844a83a156440f6940c5b6.
  • Verified 35 successful generated-client operations through the recreated sdk-test-gateway-prod Connector Namespace.
  • Top-level action validation used identifiers returned by the authenticated connector or existing BTS4 resources; no environment identifiers are embedded in source.
  • Azure Queues message validation created a temporary queue and message, asserted the nested response and nextVisibleTime, and verified cleanup left no temporary queue.
  • Azure Automation validation created a free test account and published no-op runbook in the SDK test resource group, then started and inspected the job through the generated client.
  • All 30 namespace connections are connected. Both Dataverse trigger configs remain enabled and provisioned.

Follow-up findings

  • Azure Data Factory is not added as a sample because SDK 0.14 generates x-ms-api-version=2017-09-01-preview, which Azure rejects with InvalidApiVersionParameter. Tracked by Azure/Connectors-NET-SDK#256.
  • boxmcpserver is registered but has no generated typed client or DI extension. Tracked by Azure/Connectors-NET-SDK#257.
  • CloudConvert has a generated client but no AddCloudconvertClient DI extension. Tracked by Azure/Connectors-NET-SDK#258.
  • Consumer Excel Online has no parameter-free discovery operation; every operation requires a provisioned document library and workbook. Excel Online (Business) remains sampled separately because it has a distinct discovery and authentication contract.

Trigger note

  • dataverse-developer-onnewitems polls and reports Skipped/Accepted when no matching row is available.
  • dataverse-diagnostic-onnewitems reaches Dataverse but receives HTTP 403 because its authenticated Dataverse user lacks prvReadAccount; this is an environment permission issue rather than an SDK regression.

Authored with Dobby.

Co-authored-by: Dobby <dobby@microsoft.com>
@daviburg
David Burg (daviburg) requested a review from a team as a code owner August 19, 2026 01:42
Copilot AI lite review requested due to automatic review settings August 19, 2026 01:42

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Pull request overview

Updates the DirectConnector sample project to consume the latest Azure.Connectors.Sdk preview package, keeping the sample aligned with current generated connector clients.

Changes:

  • Bumped Azure.Connectors.Sdk from 0.13.0-preview.1 to 0.14.0-preview.1 in the DirectConnector project.

💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.

@daviburg David Burg (daviburg) self-assigned this Aug 19, 2026
Co-authored-by: Dobby <dobby@microsoft.com>
Copilot AI review requested due to automatic review settings August 19, 2026 02:49
@daviburg David Burg (daviburg) changed the title Bump Azure Connectors SDK to 0.14.0-preview.1 Update samples for Azure Connectors SDK 0.14 Aug 19, 2026
Co-authored-by: Dobby <dobby@microsoft.com>

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Pull request overview

Copilot reviewed 23 out of 23 changed files in this pull request and generated no new comments.

Suppressed comments (17)

DirectConnector.Tests/AdditionalConnectorFunctionsTests.cs:44

  • These test methods return an un-awaited Task while the mocked client is declared with using var. The client can be disposed before the async assertion completes. Make the test async and await the assertion.
    [TestMethod]
    public Task AzureDigitalTwinsListModelsAsync_WithValidResponse_ReturnsOk()
    {
        using var client = AdditionalConnectorFunctionsTests.CreateClient(
            (uri, credential, options) => new AzureDigitalTwinsClient(uri, credential, options),
            "{\"value\":[]}");
        var functions = new AzureDigitalTwinsFunctions(TestHelpers.CreateNullLogger<AzureDigitalTwinsFunctions>(), client);
        return AdditionalConnectorFunctionsTests.AssertSuccessAsync(functions.ListModelsAsync);
    }

DirectConnector.Tests/AdditionalConnectorFunctionsTests.cs:54

  • These test methods return an un-awaited Task while the mocked client is declared with using var. The client can be disposed before the async assertion completes. Make the test async and await the assertion.
    [TestMethod]
    public Task AzureVMListSubscriptionsAsync_WithValidResponse_ReturnsOk()
    {
        using var client = AdditionalConnectorFunctionsTests.CreateClient(
            (uri, credential, options) => new AzureVMClient(uri, credential, options),
            "{\"value\":[]}");
        var functions = new AzureVMFunctions(TestHelpers.CreateNullLogger<AzureVMFunctions>(), client);
        return AdditionalConnectorFunctionsTests.AssertSuccessAsync(functions.ListSubscriptionsAsync);
    }

DirectConnector.Tests/AdditionalConnectorFunctionsTests.cs:64

  • These test methods return an un-awaited Task while the mocked client is declared with using var. The client can be disposed before the async assertion completes. Make the test async and await the assertion.
    [TestMethod]
    public Task KeyVaultListSecretsAsync_WithValidResponse_ReturnsOk()
    {
        using var client = AdditionalConnectorFunctionsTests.CreateClient(
            (uri, credential, options) => new KeyVaultClient(uri, credential, options),
            "{\"value\":[]}");
        var functions = new KeyVaultFunctions(TestHelpers.CreateNullLogger<KeyVaultFunctions>(), client);
        return AdditionalConnectorFunctionsTests.AssertSuccessAsync(functions.ListSecretsAsync);
    }

DirectConnector.Tests/AdditionalConnectorFunctionsTests.cs:74

  • These test methods return an un-awaited Task while the mocked client is declared with using var. The client can be disposed before the async assertion completes. Make the test async and await the assertion.
    [TestMethod]
    public Task MicrosoftBookingsListBusinessesAsync_WithValidResponse_ReturnsOk()
    {
        using var client = AdditionalConnectorFunctionsTests.CreateClient(
            (uri, credential, options) => new MicrosoftBookingsClient(uri, credential, options),
            "{\"value\":[]}");
        var functions = new MicrosoftBookingsFunctions(TestHelpers.CreateNullLogger<MicrosoftBookingsFunctions>(), client);
        return AdditionalConnectorFunctionsTests.AssertSuccessAsync(functions.ListBusinessesAsync);
    }

DirectConnector.Tests/AdditionalConnectorFunctionsTests.cs:84

  • These test methods return an un-awaited Task while the mocked client is declared with using var. The client can be disposed before the async assertion completes. Make the test async and await the assertion.
    [TestMethod]
    public Task Office365GroupsListGroupsAsync_WithValidResponse_ReturnsOk()
    {
        using var client = AdditionalConnectorFunctionsTests.CreateClient(
            (uri, credential, options) => new Office365GroupsClient(uri, credential, options),
            "{\"value\":[]}");
        var functions = new Office365GroupsFunctions(TestHelpers.CreateNullLogger<Office365GroupsFunctions>(), client);
        return AdditionalConnectorFunctionsTests.AssertSuccessAsync(functions.ListGroupsAsync);
    }

DirectConnector.Tests/AdditionalConnectorFunctionsTests.cs:94

  • These test methods return an un-awaited Task while the mocked client is declared with using var. The client can be disposed before the async assertion completes. Make the test async and await the assertion.
    [TestMethod]
    public Task Office365GroupsMailListGroupsAsync_WithValidResponse_ReturnsOk()
    {
        using var client = AdditionalConnectorFunctionsTests.CreateClient(
            (uri, credential, options) => new Office365GroupsMailClient(uri, credential, options),
            "{\"value\":[]}");
        var functions = new Office365GroupsMailFunctions(TestHelpers.CreateNullLogger<Office365GroupsMailFunctions>(), client);
        return AdditionalConnectorFunctionsTests.AssertSuccessAsync(functions.ListGroupsAsync);
    }

DirectConnector.Tests/AdditionalConnectorFunctionsTests.cs:104

  • These test methods return an un-awaited Task while the mocked client is declared with using var. The client can be disposed before the async assertion completes. Make the test async and await the assertion.
    [TestMethod]
    public Task OnenoteListNotebooksAsync_WithValidResponse_ReturnsOk()
    {
        using var client = AdditionalConnectorFunctionsTests.CreateClient(
            (uri, credential, options) => new OnenoteClient(uri, credential, options),
            "[]");
        var functions = new OnenoteFunctions(TestHelpers.CreateNullLogger<OnenoteFunctions>(), client);
        return AdditionalConnectorFunctionsTests.AssertSuccessAsync(functions.ListNotebooksAsync);
    }

DirectConnector.Tests/AdditionalConnectorFunctionsTests.cs:114

  • These test methods return an un-awaited Task while the mocked client is declared with using var. The client can be disposed before the async assertion completes. Make the test async and await the assertion.
    [TestMethod]
    public Task PowerBIListWorkspacesAsync_WithValidResponse_ReturnsOk()
    {
        using var client = AdditionalConnectorFunctionsTests.CreateClient(
            (uri, credential, options) => new PowerBIClient(uri, credential, options),
            "{\"value\":[]}");
        var functions = new PowerBIFunctions(TestHelpers.CreateNullLogger<PowerBIFunctions>(), client);
        return AdditionalConnectorFunctionsTests.AssertSuccessAsync(functions.ListWorkspacesAsync);
    }

DirectConnector.Tests/AdditionalConnectorFunctionsTests.cs:124

  • These test methods return an un-awaited Task while the mocked client is declared with using var. The client can be disposed before the async assertion completes. Make the test async and await the assertion.
    [TestMethod]
    public Task ShiftsListTeamsAsync_WithValidResponse_ReturnsOk()
    {
        using var client = AdditionalConnectorFunctionsTests.CreateClient(
            (uri, credential, options) => new ShiftsClient(uri, credential, options),
            "{\"value\":[]}");
        var functions = new ShiftsFunctions(TestHelpers.CreateNullLogger<ShiftsFunctions>(), client);
        return AdditionalConnectorFunctionsTests.AssertSuccessAsync(functions.ListTeamsAsync);
    }

DirectConnector.Tests/AdditionalConnectorFunctionsTests.cs:134

  • These test methods return an un-awaited Task while the mocked client is declared with using var. The client can be disposed before the async assertion completes. Make the test async and await the assertion.
    [TestMethod]
    public Task TodoListTaskListsAsync_WithValidResponse_ReturnsOk()
    {
        using var client = AdditionalConnectorFunctionsTests.CreateClient(
            (uri, credential, options) => new TodoClient(uri, credential, options),
            "[]");
        var functions = new TodoFunctions(TestHelpers.CreateNullLogger<TodoFunctions>(), client);
        return AdditionalConnectorFunctionsTests.AssertSuccessAsync(functions.ListTaskListsAsync);
    }

DirectConnector.Tests/AdditionalConnectorFunctionsTests.cs:34

  • These test methods return an un-awaited Task while the mocked client is declared with using var. The client (and underlying HttpClient pipeline) can be disposed before AssertSuccessAsync(...) completes, leading to flaky failures. Make the test async and await the assertion so the using scope lives for the whole async operation.

This issue also appears in the following locations of the same file:

  • line 36
  • line 46
  • line 56
  • line 66
  • line 76
  • ...and 5 more
    [TestMethod]
    public Task AzureAutomationListSubscriptionsAsync_WithValidResponse_ReturnsOk()
    {
        using var client = AdditionalConnectorFunctionsTests.CreateClient(
            (uri, credential, options) => new AzureAutomationClient(uri, credential, options),
            "{\"value\":[]}");
        var functions = new AzureAutomationFunctions(TestHelpers.CreateNullLogger<AzureAutomationFunctions>(), client);
        return AdditionalConnectorFunctionsTests.AssertSuccessAsync(functions.ListSubscriptionsAsync);
    }

DirectConnector/ConnectorFunctionExecutor.cs:24

  • This file breaks the repo’s established await/ConfigureAwait formatting: the awaited call and .ConfigureAwait(...) are on the same line. Other functions consistently use a line break before .ConfigureAwait(...) (e.g., DirectConnector/AzureTablesFunctions.cs:43-45).
        {
            var value = await operation().ConfigureAwait(continueOnCapturedContext: false);
            var response = request.CreateResponse(HttpStatusCode.OK);

DirectConnector/AzureAutomationFunctions.cs:40

  • To match the codebase’s multi-line async-enumeration style (see DirectConnector/AzureLogAnalyticsFunctions.cs:53-56), split the await foreach source so the method call starts on its own line.
                await foreach (var subscription in this._client.SubscriptionsListAsync(cancellationToken)
                    .ConfigureAwait(continueOnCapturedContext: false))

DirectConnector/AzureVMFunctions.cs:40

  • To match the codebase’s multi-line async-enumeration style (see DirectConnector/AzureLogAnalyticsFunctions.cs:53-56), split the await foreach source so the method call starts on its own line.
                await foreach (var subscription in this._client.SubscriptionsListAsync(cancellationToken)
                    .ConfigureAwait(continueOnCapturedContext: false))

DirectConnector/AzureDigitalTwinsFunctions.cs:40

  • To match the codebase’s multi-line async-enumeration style (see DirectConnector/AzureLogAnalyticsFunctions.cs:53-56), split the await foreach source so the method call starts on its own line.
                await foreach (var model in this._client.ListModelsAsync(cancellationToken: cancellationToken)
                    .ConfigureAwait(continueOnCapturedContext: false))

DirectConnector.Tests/AzureQueuesFunctionsTests.cs:176

  • This test is meant to validate the SDK 0.14 NextVisibleTime projection, but it only asserts that the JSON contains the property name. If NextVisibleTime deserializes as null, the test would still pass. Parse the response JSON and assert the nextVisibleTime value is not null.
        Assert.AreEqual(HttpStatusCode.OK, response.StatusCode);
        var body = ((MockHttpResponseData)response).GetBodyAsString();
        Assert.IsTrue(body.Contains("SDK 0.14 validation", StringComparison.Ordinal));
        Assert.IsTrue(body.Contains("nextVisibleTime", StringComparison.Ordinal));
    }

DirectConnector/TeamsFunctions.cs:140

  • For consistency with the other operation lambda in this file, pass the cancellation token as a named argument. This avoids accidentally binding to the wrong overload if the generated client signature changes.
                request,
                this._logger,
                operationName: "GetTeam",
                operation: () => this._teamsClient.GetTeamAsync(teamId, cancellationToken),
                cancellationToken)

Copilot AI review requested due to automatic review settings August 19, 2026 02:53

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Pull request overview

Copilot reviewed 23 out of 23 changed files in this pull request and generated no new comments.

Suppressed comments (11)

DirectConnector.Tests/AdditionalConnectorFunctionsTests.cs:43

  • This test returns the Task from AssertSuccessAsync while the mocked connector client is wrapped in using var. The using scope ends as soon as the test method returns, so the client can be disposed before the async assertion runs, causing flaky failures. Make the test async Task and await the assertion before leaving the using scope.
        using var client = AdditionalConnectorFunctionsTests.CreateClient(
            (uri, credential, options) => new AzureDigitalTwinsClient(uri, credential, options),
            "{\"value\":[]}");
        var functions = new AzureDigitalTwinsFunctions(TestHelpers.CreateNullLogger<AzureDigitalTwinsFunctions>(), client);
        return AdditionalConnectorFunctionsTests.AssertSuccessAsync(functions.ListModelsAsync);

DirectConnector.Tests/AdditionalConnectorFunctionsTests.cs:53

  • This test returns the Task from AssertSuccessAsync while the mocked connector client is wrapped in using var. The using scope ends as soon as the test method returns, so the client can be disposed before the async assertion runs, causing flaky failures. Make the test async Task and await the assertion before leaving the using scope.
        using var client = AdditionalConnectorFunctionsTests.CreateClient(
            (uri, credential, options) => new AzureVMClient(uri, credential, options),
            "{\"value\":[]}");
        var functions = new AzureVMFunctions(TestHelpers.CreateNullLogger<AzureVMFunctions>(), client);
        return AdditionalConnectorFunctionsTests.AssertSuccessAsync(functions.ListSubscriptionsAsync);

DirectConnector.Tests/AdditionalConnectorFunctionsTests.cs:63

  • This test returns the Task from AssertSuccessAsync while the mocked connector client is wrapped in using var. The using scope ends as soon as the test method returns, so the client can be disposed before the async assertion runs, causing flaky failures. Make the test async Task and await the assertion before leaving the using scope.
        using var client = AdditionalConnectorFunctionsTests.CreateClient(
            (uri, credential, options) => new KeyVaultClient(uri, credential, options),
            "{\"value\":[]}");
        var functions = new KeyVaultFunctions(TestHelpers.CreateNullLogger<KeyVaultFunctions>(), client);
        return AdditionalConnectorFunctionsTests.AssertSuccessAsync(functions.ListSecretsAsync);

DirectConnector.Tests/AdditionalConnectorFunctionsTests.cs:73

  • This test returns the Task from AssertSuccessAsync while the mocked connector client is wrapped in using var. The using scope ends as soon as the test method returns, so the client can be disposed before the async assertion runs, causing flaky failures. Make the test async Task and await the assertion before leaving the using scope.
        using var client = AdditionalConnectorFunctionsTests.CreateClient(
            (uri, credential, options) => new MicrosoftBookingsClient(uri, credential, options),
            "{\"value\":[]}");
        var functions = new MicrosoftBookingsFunctions(TestHelpers.CreateNullLogger<MicrosoftBookingsFunctions>(), client);
        return AdditionalConnectorFunctionsTests.AssertSuccessAsync(functions.ListBusinessesAsync);

DirectConnector.Tests/AdditionalConnectorFunctionsTests.cs:83

  • This test returns the Task from AssertSuccessAsync while the mocked connector client is wrapped in using var. The using scope ends as soon as the test method returns, so the client can be disposed before the async assertion runs, causing flaky failures. Make the test async Task and await the assertion before leaving the using scope.
        using var client = AdditionalConnectorFunctionsTests.CreateClient(
            (uri, credential, options) => new Office365GroupsClient(uri, credential, options),
            "{\"value\":[]}");
        var functions = new Office365GroupsFunctions(TestHelpers.CreateNullLogger<Office365GroupsFunctions>(), client);
        return AdditionalConnectorFunctionsTests.AssertSuccessAsync(functions.ListGroupsAsync);

DirectConnector.Tests/AdditionalConnectorFunctionsTests.cs:93

  • This test returns the Task from AssertSuccessAsync while the mocked connector client is wrapped in using var. The using scope ends as soon as the test method returns, so the client can be disposed before the async assertion runs, causing flaky failures. Make the test async Task and await the assertion before leaving the using scope.
        using var client = AdditionalConnectorFunctionsTests.CreateClient(
            (uri, credential, options) => new Office365GroupsMailClient(uri, credential, options),
            "{\"value\":[]}");
        var functions = new Office365GroupsMailFunctions(TestHelpers.CreateNullLogger<Office365GroupsMailFunctions>(), client);
        return AdditionalConnectorFunctionsTests.AssertSuccessAsync(functions.ListGroupsAsync);

DirectConnector.Tests/AdditionalConnectorFunctionsTests.cs:103

  • This test returns the Task from AssertSuccessAsync while the mocked connector client is wrapped in using var. The using scope ends as soon as the test method returns, so the client can be disposed before the async assertion runs, causing flaky failures. Make the test async Task and await the assertion before leaving the using scope.
        using var client = AdditionalConnectorFunctionsTests.CreateClient(
            (uri, credential, options) => new OnenoteClient(uri, credential, options),
            "[]");
        var functions = new OnenoteFunctions(TestHelpers.CreateNullLogger<OnenoteFunctions>(), client);
        return AdditionalConnectorFunctionsTests.AssertSuccessAsync(functions.ListNotebooksAsync);

DirectConnector.Tests/AdditionalConnectorFunctionsTests.cs:113

  • This test returns the Task from AssertSuccessAsync while the mocked connector client is wrapped in using var. The using scope ends as soon as the test method returns, so the client can be disposed before the async assertion runs, causing flaky failures. Make the test async Task and await the assertion before leaving the using scope.
        using var client = AdditionalConnectorFunctionsTests.CreateClient(
            (uri, credential, options) => new PowerBIClient(uri, credential, options),
            "{\"value\":[]}");
        var functions = new PowerBIFunctions(TestHelpers.CreateNullLogger<PowerBIFunctions>(), client);
        return AdditionalConnectorFunctionsTests.AssertSuccessAsync(functions.ListWorkspacesAsync);

DirectConnector.Tests/AdditionalConnectorFunctionsTests.cs:123

  • This test returns the Task from AssertSuccessAsync while the mocked connector client is wrapped in using var. The using scope ends as soon as the test method returns, so the client can be disposed before the async assertion runs, causing flaky failures. Make the test async Task and await the assertion before leaving the using scope.
        using var client = AdditionalConnectorFunctionsTests.CreateClient(
            (uri, credential, options) => new ShiftsClient(uri, credential, options),
            "{\"value\":[]}");
        var functions = new ShiftsFunctions(TestHelpers.CreateNullLogger<ShiftsFunctions>(), client);
        return AdditionalConnectorFunctionsTests.AssertSuccessAsync(functions.ListTeamsAsync);

DirectConnector.Tests/AdditionalConnectorFunctionsTests.cs:133

  • This test returns the Task from AssertSuccessAsync while the mocked connector client is wrapped in using var. The using scope ends as soon as the test method returns, so the client can be disposed before the async assertion runs, causing flaky failures. Make the test async Task and await the assertion before leaving the using scope.
        using var client = AdditionalConnectorFunctionsTests.CreateClient(
            (uri, credential, options) => new TodoClient(uri, credential, options),
            "[]");
        var functions = new TodoFunctions(TestHelpers.CreateNullLogger<TodoFunctions>(), client);
        return AdditionalConnectorFunctionsTests.AssertSuccessAsync(functions.ListTaskListsAsync);

DirectConnector.Tests/AdditionalConnectorFunctionsTests.cs:34

  • This test returns the Task from AssertSuccessAsync while the mocked connector client is wrapped in using var. The using scope ends as soon as the test method returns, so the client can be disposed before the async assertion runs, causing flaky failures. Make the test async Task and await the assertion before leaving the using scope.

This issue also appears in the following locations of the same file:

  • line 39
  • line 49
  • line 59
  • line 69
  • line 79
  • ...and 5 more
        using var client = AdditionalConnectorFunctionsTests.CreateClient(
            (uri, credential, options) => new AzureAutomationClient(uri, credential, options),
            "{\"value\":[]}");
        var functions = new AzureAutomationFunctions(TestHelpers.CreateNullLogger<AzureAutomationFunctions>(), client);
        return AdditionalConnectorFunctionsTests.AssertSuccessAsync(functions.ListSubscriptionsAsync);

Comment thread DirectConnector/AzureAutomationFunctions.cs
Comment thread DirectConnector/AzureVMFunctions.cs
Co-authored-by: Dobby <dobby@microsoft.com>
Copilot AI review requested due to automatic review settings August 19, 2026 05:20

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Pull request overview

Copilot reviewed 23 out of 23 changed files in this pull request and generated no new comments.

Suppressed comments (4)

DirectConnector/PlannerFunctions.cs:94

  • PlannerListMyTasksAsync handles ConnectorException, but any other non-fatal exception (e.g., serialization failures) will currently bubble out and return an unstructured 500. Catching non-fatal exceptions here keeps error responses consistent with other samples.
        catch (ConnectorException ex)
        {
            this._logger.LogError(ex, "PlannerListMyTasks failed with status '{StatusCode}'.", ex.Status);

            var response = request.CreateResponse(HttpStatusCode.BadGateway);

DirectConnector/AzureAutomationFunctions.cs:124

  • CreateBadRequestAsync is used by both GetJobStatusAsync (needs jobId) and CreateJobAsync (needs runbookName), but the returned error message only mentions 'jobId', which is misleading for the create-job route.
        var response = request.CreateResponse(System.Net.HttpStatusCode.BadRequest);
        await response
            .WriteAsJsonAsync(
                new { success = false, error = "Query parameters 'subscriptionId', 'resourceGroup', 'automationAccount', and 'jobId' are required." },
                cancellationToken)

DirectConnector/PlannerFunctions.cs:62

  • PlannerListGroupsAsync handles ConnectorException, but any other non-fatal exception will currently bubble out of the function and return an unstructured 500. Other functions in this repo typically catch non-fatal exceptions and return a consistent JSON error response.

This issue also appears on line 90 of the same file.

        catch (ConnectorException ex)
        {
            this._logger.LogError(ex, "PlannerListGroups failed with status '{StatusCode}'.", ex.Status);

            var response = request.CreateResponse(HttpStatusCode.BadGateway);

DirectConnector/AzureQueuesFunctions.cs:191

  • AzureQueuesGetMessagesAsync catches ConnectorException but not other non-fatal exceptions. For consistency with the other AzureQueuesFunctions endpoints (and to avoid unhandled exceptions returning non-JSON 500s), add the same non-fatal exception handling here.
        catch (ConnectorException ex)
        {
            this._logger.LogError(ex, "AzureQueuesGetMessages failed with status '{StatusCode}'.", ex.Status);

            var response = request.CreateResponse(HttpStatusCode.BadGateway);

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Pull request overview

Copilot reviewed 23 out of 23 changed files in this pull request and generated no new comments.

Suppressed comments (14)

DirectConnector/PlannerFunctions.cs:90

  • Only connector failures are normalized here; non-fatal failures from asynchronous paging or response serialization escape without the structured 500 response used by the shared executor (ConnectorFunctionExecutor.cs:42-51). Add the standard non-fatal exception path.
        catch (ConnectorException ex)

DirectConnector/AzureAutomationFunctions.cs:15

  • This summary is inaccurate because CreateJobAsync below starts a runbook job, which is a mutating operation rather than read-only discovery. Update the class documentation so consumers do not assume every endpoint is side-effect free.
/// Azure Functions demonstrating read-only Azure Automation discovery.

DirectConnector/AzureAutomationFunctions.cs:124

  • This shared response is also used by CreateJobAsync, but that endpoint requires runbookName, not jobId. A request missing runbookName therefore receives incorrect remediation guidance; pass an operation-specific message or use a message valid for both callers.
                new { success = false, error = "Query parameters 'subscriptionId', 'resourceGroup', 'automationAccount', and 'jobId' are required." },

DirectConnector.Tests/AzureQueuesFunctionsTests.cs:175

  • Checking only the property name still passes when deserialization leaves NextVisibleTime null, so this does not verify the SDK 0.14 rename that the test targets. Assert that the projected property is non-null (or equals the expected timestamp).
        Assert.IsTrue(body.Contains("nextVisibleTime", StringComparison.Ordinal));

DirectConnector/PlannerFunctions.cs:62

  • Only connector failures are normalized here; non-fatal failures from enumeration or response serialization escape the function without the structured 500 response used by the shared executor (ConnectorFunctionExecutor.cs:42-51). Add the standard non-fatal exception path for consistent endpoint behavior.

This issue also appears on line 90 of the same file.

        catch (ConnectorException ex)

DirectConnector/AzureQueuesFunctions.cs:191

  • This new endpoint omits the non-fatal exception handling used by both existing methods in this class (AzureQueuesFunctions.cs:65-75 and 125-135). Deserialization or response-writing failures will therefore bypass the endpoint's structured error contract; add the same 500 path here.
        catch (ConnectorException ex)

DirectConnector/ShiftsFunctions.cs:52

  • The page size is an unexplained magic number. Introduce a named constant (for example, DefaultPageSize) so the sample documents the intent and has one place to tune it.
                pageSize: 20,

DirectConnector/Office365GroupsFunctions.cs:47

  • This loop unconditionally stops after the first page, so groups beyond the first 20 are silently inaccessible and no continuation token is returned. Iterate all pages (as the Planner sample does) or expose continuation-token input/output.
                    break;

DirectConnector/Office365GroupsMailFunctions.cs:65

  • This endpoint silently discards every conversation page after the first and does not return a continuation token, so groups with more than 20 conversations cannot be fully listed. Iterate all pages or expose continuation-token input/output.
                    break;

DirectConnector/ConnectorFunctionExecutor.cs:23

  • The awaited call keeps ConfigureAwait on the invocation line, unlike the multi-line chaining used immediately below at ConnectorFunctionExecutor.cs:25-27. Split the continuation configuration onto the chained line.
            var value = await operation().ConfigureAwait(continueOnCapturedContext: false);

DirectConnector/AzureAutomationFunctions.cs:41

  • The async member invocation remains on the same line as this._client, unlike the established chaining at Office365GroupsFunctions.cs:39-43. Start SubscriptionsListAsync on the chained line.
                await foreach (var subscription in this._client.SubscriptionsListAsync(cancellationToken)
                    .ConfigureAwait(continueOnCapturedContext: false))

DirectConnector/AzureVMFunctions.cs:40

  • The async member invocation remains on the same line as this._client, unlike the established chaining at Office365GroupsFunctions.cs:39-43. Start SubscriptionsListAsync on the chained line.
                await foreach (var subscription in this._client.SubscriptionsListAsync(cancellationToken)
                    .ConfigureAwait(continueOnCapturedContext: false))

DirectConnector.Tests/AdditionalConnectorFunctionsTests.cs:248

  • The awaited operation is invoked on the same line as await, unlike the multi-line pattern already used at AdditionalConnectorFunctionsTests.cs:222-224. Move the invocation arguments onto chained lines before ConfigureAwait.
        var response = await operation(TestHelpers.CreateRequest(), CancellationToken.None)
            .ConfigureAwait(continueOnCapturedContext: false);

DirectConnector/AzureDigitalTwinsFunctions.cs:40

  • The async member invocation remains on the same line as this._client, unlike the established chaining at Office365GroupsFunctions.cs:39-43. Start ListModelsAsync on the chained line.
                await foreach (var model in this._client.ListModelsAsync(cancellationToken: cancellationToken)
                    .ConfigureAwait(continueOnCapturedContext: false))

@swapnil-nagar

Copy link
Copy Markdown

PR 72 Review Findings

PR: Azure/Connectors-NET-Samples#72

Reviewed head: 9f8024fb798bf02afed4e8a1c2fca59a828ca773

Recommended disposition: Request changes

Review Body

[Dobby] Code review: REQUEST_CHANGES

I found two issues on head 9f8024fb798bf02afed4e8a1c2fca59a828ca773: the Azure Automation
create-job validation response names the wrong required parameter, and the Azure Queues SDK 0.14 test does not
verify that NextVisibleTime is populated. Details and suggested fixes are included in the inline comments below.

Inline Comment 1

File: DirectConnector/AzureAutomationFunctions.cs

Place on the error message in CreateBadRequestAsync near line 123.

CreateJobAsync validates runbookName, but this shared response says that jobId is required. A request missing
runbookName therefore receives an incorrect error message. Please either pass the endpoint-specific parameter
name into this helper or give CreateJobAsync a response that names runbookName.

Automation Resolution

Make the bad-request response endpoint-specific:

  • CreateJobAsync should name runbookName.
  • GetJobStatusAsync should name jobId.
  • Add tests that exercise both validation paths and assert the relevant error text.

Inline Comment 2

File: DirectConnector.Tests/AzureQueuesFunctionsTests.cs

Place on the body.Contains("nextVisibleTime", ...) assertion near line 176.

This assertion passes when the response contains "nextVisibleTime": null, so it does not verify that SDK 0.14
mapped TimeNextVisible into NextVisibleTime. Please parse the response JSON and assert the value is non-null
and equals the expected timestamp.

Azure Queues Resolution

Replace the property-name check with a value assertion. For example:

using var document = JsonDocument.Parse(body);
var nextVisibleTime = document.RootElement
    .GetProperty("messages")[0]
    .GetProperty("nextVisibleTime")
    .GetString();

Assert.AreEqual("2026-08-19T03:00:00Z", nextVisibleTime);

Steps For The PR Owner

  1. Make the Azure Automation validation message endpoint-specific so each endpoint names its actual required
    parameter.
  2. Add or update tests for the Automation create-job and get-job-status validation paths.
  3. Parse the Azure Queues response JSON and assert that nextVisibleTime equals the mocked timestamp.
  4. Run the focused Automation and Azure Queues tests.
  5. Run the complete test suite and formatting check.
  6. Push the fixes and re-request review.

Validation Context

  • The PR head was unchanged when this review was finalized.
  • All current GitHub checks were successful.
  • Both existing review threads were resolved; these are new findings.
  • No review was posted automatically because the authenticated GitHub EMU account received HTTP 403 when writing
    to the public repository.
  • A trustworthy local test run was unavailable because the configured restore source could not resolve
    Azure.Connectors.Sdk; CI remains the executable validation evidence.

Co-authored-by: Dobby <dobby@microsoft.com>
Copilot AI review requested due to automatic review settings August 28, 2026 15:00
@daviburg

Copy link
Copy Markdown
Member Author

[Dobby] Addressed the requested changes from the review findings in commit 4d7e9b7.

  • Azure Automation now passes requiredParameterName: "jobId" from GetJobStatusAsync and requiredParameterName: "runbookName" from CreateJobAsync. The shared helper uses '{requiredParameterName}' in its error response. Two regression tests parse the JSON response and assert each endpoint's exact error text.
  • Azure Queues now parses the sample response and reads .GetProperty("nextVisibleTime").GetDateTimeOffset(), then compares it with new DateTimeOffset(2026, 8, 19, 3, 0, 0, TimeSpan.Zero).
  • Validation passed: focused Automation/Queues tests, 25 passed; full Release suite, 75 passed; dotnet format --verify-no-changes --no-restore, passed.

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Pull request overview

Copilot reviewed 23 out of 23 changed files in this pull request and generated no new comments.

Suppressed comments (3)

Previously missed (3) — in code that hasn't changed since the last review.

DirectConnector/Office365GroupsFunctions.cs:47

  • The SDK method returns items across all pages, but this unconditional break silently limits the endpoint to the first page (20 groups). Because the response exposes no continuation token, later groups are unreachable; enumerate all pages or return paging state.
                {
                    groups.AddRange(page.Values);
                    break;

DirectConnector/Office365GroupsMailFunctions.cs:65

  • The SDK method returns conversations across all pages, but this unconditional break discards every continuation after the first page. Since callers receive no continuation token, conversations beyond the first page cannot be retrieved; enumerate all pages or expose paging state.
                {
                    conversations.AddRange(page.Values);
                    break;

DirectConnector/AzureAutomationFunctions.cs:15

  • This class is not read-only: CreateJobAsync starts a runbook job. Update the summary so consumers are not misled about the sample's side effects.
/// Azure Functions demonstrating read-only Azure Automation discovery.

@swapnil-nagar

Copy link
Copy Markdown

Findings

P1: paged results are silently truncated. Both group and conversation endpoints stop after the first requested 20-item page without returning a continuation token. Later records are unreachable. Remove break to auto-page, or expose continuation state. Office365GroupsFunctions.cs#L39-L47, Office365GroupsMailFunctions.cs#L56-L65

P2: tests dispose connector clients before returned tasks complete. The success tests use using var client and return an unawaited task, causing disposal when the test method returns. Make them async Task and await AssertSuccessAsync. AdditionalConnectorFunctionsTests.cs#L28-L34

P2: Azure Automation is incorrectly described as read-only. The class exposes CreateJobAsync, which starts a runbook job. This documentation could mislead users about side effects. AzureAutomationFunctions.cs#L14-L17

Co-authored-by: Dobby <dobby@microsoft.com>
Copilot AI review requested due to automatic review settings August 31, 2026 21:28
@daviburg

Copy link
Copy Markdown
Member Author

[Dobby] Addressed all findings from this review in commit a40b6d7.

  • Removed both page-loop break statements. Each loop now keeps executing after groups.AddRange(page.Values); or conversations.AddRange(page.Values);. The updated tests return two @odata.nextLink pages and assert that both records are present and both mocked responses were consumed.
  • Converted all 17 affected success tests to public async Task and now await AdditionalConnectorFunctionsTests.AssertSuccessAsync(...).ConfigureAwait(continueOnCapturedContext: false); before each using var client scope ends.
  • Updated the Automation summary to Azure Functions demonstrating Azure Automation discovery and runbook job operations. and explicitly notes that the create-job endpoint starts a runbook job.
  • Validation passed: focused AdditionalConnectorFunctionsTests, 20 passed; full Release suite, 75 passed; dotnet format --verify-no-changes --no-restore, passed.

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Pull request overview

Copilot reviewed 23 out of 23 changed files in this pull request and generated 5 comments.

Suppressed comments (1)

DirectConnector/AzureQueuesFunctions.cs:174

  • GetMessagesAsync hides each returned message, but this projection drops PopReceipt, which the SDK model identifies as the token required to delete that message. Return it so callers can complete the receive/delete workflow rather than having every message reappear after its visibility timeout.
                    message.MessageId,
                    message.MessageText,
                    message.DequeueCount,
                    nextVisibleTime = message.NextVisibleTime,
                });

Comment thread DirectConnector/AzureQueuesFunctions.cs Outdated
Comment thread DirectConnector/ConnectorFunctionExecutor.cs Outdated
Comment thread DirectConnector/AzureAutomationFunctions.cs Outdated
Comment thread DirectConnector/AzureVMFunctions.cs Outdated
Comment thread DirectConnector/AzureDigitalTwinsFunctions.cs Outdated
Co-authored-by: Dobby <dobby@microsoft.com>
Copilot AI review requested due to automatic review settings August 31, 2026 21:38

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Pull request overview

Copilot reviewed 23 out of 23 changed files in this pull request and generated 1 comment.

Comment thread DirectConnector/MicrosoftBookingsFunctions.cs
Co-authored-by: Dobby <dobby@microsoft.com>
Copilot AI review requested due to automatic review settings August 31, 2026 21:45

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Pull request overview

Copilot reviewed 23 out of 23 changed files in this pull request and generated no new comments.

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

[P2] Handle non-connector failures in Azure Queues
AzureQueuesFunctions.cs:183

AzureQueuesGetMessagesAsync only catches ConnectorException. Malformed successful JSON raises JsonException, escaping the structured { success: false } contract. Add the same non-fatal fallback used by adjacent handlers or use ConnectorFunctionExecutor.

[P2] Handle non-connector failures in Planner endpoints
PlannerFunctions.cs:52 and PlannerFunctions.cs:90

Both handlers have the same gap. Deserialization or local serialization failures become unstructured host errors instead of the sample’s standard JSON 500 response.

@daviburg
David Burg (daviburg) merged commit fff4369 into main Sep 1, 2026
9 checks passed
@daviburg
David Burg (daviburg) deleted the feature/sdk-0.14-samples branch September 1, 2026 20:08
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants