From 5d15f0dc33b594d28413e80f724716cef673a9e1 Mon Sep 17 00:00:00 2001 From: David Burg Date: Wed, 13 May 2026 22:41:00 -0700 Subject: [PATCH] feat: add batch-5 connector samples (AzureQueues, AzureTables, DocumentDB, EventHubs, ExcelOnlineBusiness, Outlook, ServiceBus, WordOnlineBusiness) - Add 8 new HTTP-triggered Azure Functions demonstrating each connector - Register all 8 clients in Program.cs - Fix pre-existing API compat issues: Teams ChannelId casing, MsGraph parameter name casing, SharePoint ListFolder API changes --- DirectConnector/AzureQueuesFunctions.cs | 136 ++++++++++++++++++ DirectConnector/AzureTablesFunctions.cs | 136 ++++++++++++++++++ DirectConnector/DocumentDbFunctions.cs | 136 ++++++++++++++++++ DirectConnector/EventHubsFunctions.cs | 136 ++++++++++++++++++ .../ExcelOnlineBusinessFunctions.cs | 136 ++++++++++++++++++ DirectConnector/MsGraphFunctions.cs | 2 +- DirectConnector/OutlookFunctions.cs | 124 ++++++++++++++++ DirectConnector/Program.cs | 8 ++ DirectConnector/ServiceBusFunctions.cs | 124 ++++++++++++++++ DirectConnector/SharePointFunctions.cs | 31 ++-- DirectConnector/TeamsFunctions.cs | 4 +- .../WordOnlineBusinessFunctions.cs | 124 ++++++++++++++++ 12 files changed, 1080 insertions(+), 17 deletions(-) create mode 100644 DirectConnector/AzureQueuesFunctions.cs create mode 100644 DirectConnector/AzureTablesFunctions.cs create mode 100644 DirectConnector/DocumentDbFunctions.cs create mode 100644 DirectConnector/EventHubsFunctions.cs create mode 100644 DirectConnector/ExcelOnlineBusinessFunctions.cs create mode 100644 DirectConnector/OutlookFunctions.cs create mode 100644 DirectConnector/ServiceBusFunctions.cs create mode 100644 DirectConnector/WordOnlineBusinessFunctions.cs diff --git a/DirectConnector/AzureQueuesFunctions.cs b/DirectConnector/AzureQueuesFunctions.cs new file mode 100644 index 0000000..dc2964f --- /dev/null +++ b/DirectConnector/AzureQueuesFunctions.cs @@ -0,0 +1,136 @@ +//------------------------------------------------------------ +// Copyright (c) Microsoft Corporation. All rights reserved. +//------------------------------------------------------------ + +using System.Net; +using Azure.Connectors.Sdk; +using Azure.Connectors.Sdk.Azurequeues; +using Microsoft.Azure.Functions.Worker; +using Microsoft.Azure.Functions.Worker.Http; +using Microsoft.Extensions.Logging; + +namespace DirectConnector; + +/// +/// Azure Functions demonstrating Azure Queues operations using the generated +/// from the Azure Connectors SDK. +/// +public class AzureQueuesFunctions +{ + private readonly ILogger _logger; + private readonly AzurequeuesClient _azureQueuesClient; + + public AzureQueuesFunctions( + ILogger logger, + AzurequeuesClient azureQueuesClient) + { + this._logger = logger; + this._azureQueuesClient = azureQueuesClient; + } + + /// + /// Gets the list of Azure Storage accounts accessible via the connection. + /// + [Function("AzureQueuesGetStorageAccounts")] + public async Task AzureQueuesGetStorageAccountsAsync( + [HttpTrigger(AuthorizationLevel.Function, "get", Route = "azurequeues/accounts")] HttpRequestData request, + CancellationToken cancellationToken) + { + this._logger.LogInformation("AzureQueuesGetStorageAccounts: Using generated AzurequeuesClient from SDK."); + + try + { + var accounts = await this._azureQueuesClient + .GetStorageAccountsAsync(cancellationToken: cancellationToken) + .ConfigureAwait(continueOnCapturedContext: false); + + var response = request.CreateResponse(HttpStatusCode.OK); + await response + .WriteAsJsonAsync(new { success = true, accounts }, cancellationToken) + .ConfigureAwait(continueOnCapturedContext: false); + + return response; + } + catch (ConnectorException ex) + { + this._logger.LogError(ex, "AzureQueuesGetStorageAccounts failed with status '{StatusCode}'.", ex.Status); + + var errorResponse = request.CreateResponse(HttpStatusCode.BadGateway); + await errorResponse + .WriteAsJsonAsync(new { success = false, error = ex.Message, statusCode = ex.Status, details = ex.ResponseBody }, cancellationToken) + .ConfigureAwait(continueOnCapturedContext: false); + + return errorResponse; + } + catch (Exception ex) when (!ex.IsFatal()) + { + this._logger.LogError(ex, "Error in AzureQueuesGetStorageAccounts."); + + var errorResponse = request.CreateResponse(HttpStatusCode.InternalServerError); + await errorResponse + .WriteAsJsonAsync(new { success = false, error = ex.Message }) + .ConfigureAwait(continueOnCapturedContext: false); + + return errorResponse; + } + } + + /// + /// Lists queues in the specified Azure Storage account. + /// Route: GET /azurequeues/queues?storageAccount={storageAccountNameOrQueueEndpoint} + /// + [Function("AzureQueuesListQueues")] + public async Task AzureQueuesListQueuesAsync( + [HttpTrigger(AuthorizationLevel.Function, "get", Route = "azurequeues/queues")] HttpRequestData request, + CancellationToken cancellationToken) + { + this._logger.LogInformation("AzureQueuesListQueues: Using generated AzurequeuesClient from SDK."); + + var storageAccount = System.Web.HttpUtility.ParseQueryString(request.Url.Query)["storageAccount"]; + if (string.IsNullOrWhiteSpace(storageAccount)) + { + var badRequest = request.CreateResponse(HttpStatusCode.BadRequest); + await badRequest + .WriteAsJsonAsync(new { success = false, error = "Missing required query parameter 'storageAccount'." }, cancellationToken) + .ConfigureAwait(continueOnCapturedContext: false); + + return badRequest; + } + + try + { + var queues = await this._azureQueuesClient + .ListQueuesAsync(storageAccountNameOrQueueEndpoint: storageAccount, cancellationToken: cancellationToken) + .ConfigureAwait(continueOnCapturedContext: false); + + var response = request.CreateResponse(HttpStatusCode.OK); + await response + .WriteAsJsonAsync(new { success = true, queues }, cancellationToken) + .ConfigureAwait(continueOnCapturedContext: false); + + return response; + } + catch (ConnectorException ex) + { + this._logger.LogError(ex, "AzureQueuesListQueues failed with status '{StatusCode}'.", ex.Status); + + var errorResponse = request.CreateResponse(HttpStatusCode.BadGateway); + await errorResponse + .WriteAsJsonAsync(new { success = false, error = ex.Message, statusCode = ex.Status, details = ex.ResponseBody }, cancellationToken) + .ConfigureAwait(continueOnCapturedContext: false); + + return errorResponse; + } + catch (Exception ex) when (!ex.IsFatal()) + { + this._logger.LogError(ex, "Error in AzureQueuesListQueues."); + + var errorResponse = request.CreateResponse(HttpStatusCode.InternalServerError); + await errorResponse + .WriteAsJsonAsync(new { success = false, error = ex.Message }) + .ConfigureAwait(continueOnCapturedContext: false); + + return errorResponse; + } + } +} diff --git a/DirectConnector/AzureTablesFunctions.cs b/DirectConnector/AzureTablesFunctions.cs new file mode 100644 index 0000000..d64c41d --- /dev/null +++ b/DirectConnector/AzureTablesFunctions.cs @@ -0,0 +1,136 @@ +//------------------------------------------------------------ +// Copyright (c) Microsoft Corporation. All rights reserved. +//------------------------------------------------------------ + +using System.Net; +using Azure.Connectors.Sdk; +using Azure.Connectors.Sdk.Azuretables; +using Microsoft.Azure.Functions.Worker; +using Microsoft.Azure.Functions.Worker.Http; +using Microsoft.Extensions.Logging; + +namespace DirectConnector; + +/// +/// Azure Functions demonstrating Azure Tables operations using the generated +/// from the Azure Connectors SDK. +/// +public class AzureTablesFunctions +{ + private readonly ILogger _logger; + private readonly AzuretablesClient _azureTablesClient; + + public AzureTablesFunctions( + ILogger logger, + AzuretablesClient azureTablesClient) + { + this._logger = logger; + this._azureTablesClient = azureTablesClient; + } + + /// + /// Gets the list of Azure Storage accounts accessible via the connection. + /// + [Function("AzureTablesGetStorageAccounts")] + public async Task AzureTablesGetStorageAccountsAsync( + [HttpTrigger(AuthorizationLevel.Function, "get", Route = "azuretables/accounts")] HttpRequestData request, + CancellationToken cancellationToken) + { + this._logger.LogInformation("AzureTablesGetStorageAccounts: Using generated AzuretablesClient from SDK."); + + try + { + var accounts = await this._azureTablesClient + .GetStorageAccountsAsync(cancellationToken: cancellationToken) + .ConfigureAwait(continueOnCapturedContext: false); + + var response = request.CreateResponse(HttpStatusCode.OK); + await response + .WriteAsJsonAsync(new { success = true, accounts }, cancellationToken) + .ConfigureAwait(continueOnCapturedContext: false); + + return response; + } + catch (ConnectorException ex) + { + this._logger.LogError(ex, "AzureTablesGetStorageAccounts failed with status '{StatusCode}'.", ex.Status); + + var errorResponse = request.CreateResponse(HttpStatusCode.BadGateway); + await errorResponse + .WriteAsJsonAsync(new { success = false, error = ex.Message, statusCode = ex.Status, details = ex.ResponseBody }, cancellationToken) + .ConfigureAwait(continueOnCapturedContext: false); + + return errorResponse; + } + catch (Exception ex) when (!ex.IsFatal()) + { + this._logger.LogError(ex, "Error in AzureTablesGetStorageAccounts."); + + var errorResponse = request.CreateResponse(HttpStatusCode.InternalServerError); + await errorResponse + .WriteAsJsonAsync(new { success = false, error = ex.Message }) + .ConfigureAwait(continueOnCapturedContext: false); + + return errorResponse; + } + } + + /// + /// Lists tables in the specified Azure Storage account. + /// Route: GET /azuretables/tables?storageAccount={storageAccountNameOrTableEndpoint} + /// + [Function("AzureTablesGetTables")] + public async Task AzureTablesGetTablesAsync( + [HttpTrigger(AuthorizationLevel.Function, "get", Route = "azuretables/tables")] HttpRequestData request, + CancellationToken cancellationToken) + { + this._logger.LogInformation("AzureTablesGetTables: Using generated AzuretablesClient from SDK."); + + var storageAccount = System.Web.HttpUtility.ParseQueryString(request.Url.Query)["storageAccount"]; + if (string.IsNullOrWhiteSpace(storageAccount)) + { + var badRequest = request.CreateResponse(HttpStatusCode.BadRequest); + await badRequest + .WriteAsJsonAsync(new { success = false, error = "Missing required query parameter 'storageAccount'." }, cancellationToken) + .ConfigureAwait(continueOnCapturedContext: false); + + return badRequest; + } + + try + { + var tables = await this._azureTablesClient + .GetTablesAsync(storageAccountNameOrTableEndpoint: storageAccount, cancellationToken: cancellationToken) + .ConfigureAwait(continueOnCapturedContext: false); + + var response = request.CreateResponse(HttpStatusCode.OK); + await response + .WriteAsJsonAsync(new { success = true, tables }, cancellationToken) + .ConfigureAwait(continueOnCapturedContext: false); + + return response; + } + catch (ConnectorException ex) + { + this._logger.LogError(ex, "AzureTablesGetTables failed with status '{StatusCode}'.", ex.Status); + + var errorResponse = request.CreateResponse(HttpStatusCode.BadGateway); + await errorResponse + .WriteAsJsonAsync(new { success = false, error = ex.Message, statusCode = ex.Status, details = ex.ResponseBody }, cancellationToken) + .ConfigureAwait(continueOnCapturedContext: false); + + return errorResponse; + } + catch (Exception ex) when (!ex.IsFatal()) + { + this._logger.LogError(ex, "Error in AzureTablesGetTables."); + + var errorResponse = request.CreateResponse(HttpStatusCode.InternalServerError); + await errorResponse + .WriteAsJsonAsync(new { success = false, error = ex.Message }) + .ConfigureAwait(continueOnCapturedContext: false); + + return errorResponse; + } + } +} diff --git a/DirectConnector/DocumentDbFunctions.cs b/DirectConnector/DocumentDbFunctions.cs new file mode 100644 index 0000000..64a8c86 --- /dev/null +++ b/DirectConnector/DocumentDbFunctions.cs @@ -0,0 +1,136 @@ +//------------------------------------------------------------ +// Copyright (c) Microsoft Corporation. All rights reserved. +//------------------------------------------------------------ + +using System.Net; +using Azure.Connectors.Sdk; +using Azure.Connectors.Sdk.Documentdb; +using Microsoft.Azure.Functions.Worker; +using Microsoft.Azure.Functions.Worker.Http; +using Microsoft.Extensions.Logging; + +namespace DirectConnector; + +/// +/// Azure Functions demonstrating Azure Cosmos DB (DocumentDB) operations using the generated +/// from the Azure Connectors SDK. +/// +public class DocumentDbFunctions +{ + private readonly ILogger _logger; + private readonly DocumentdbClient _documentDbClient; + + public DocumentDbFunctions( + ILogger logger, + DocumentdbClient documentDbClient) + { + this._logger = logger; + this._documentDbClient = documentDbClient; + } + + /// + /// Gets the list of Cosmos DB accounts accessible via the connection. + /// + [Function("DocumentDbGetAccounts")] + public async Task DocumentDbGetAccountsAsync( + [HttpTrigger(AuthorizationLevel.Function, "get", Route = "documentdb/accounts")] HttpRequestData request, + CancellationToken cancellationToken) + { + this._logger.LogInformation("DocumentDbGetAccounts: Using generated DocumentdbClient from SDK."); + + try + { + var accounts = await this._documentDbClient + .GetCosmosDbAccountsAsync(cancellationToken: cancellationToken) + .ConfigureAwait(continueOnCapturedContext: false); + + var response = request.CreateResponse(HttpStatusCode.OK); + await response + .WriteAsJsonAsync(new { success = true, accounts }, cancellationToken) + .ConfigureAwait(continueOnCapturedContext: false); + + return response; + } + catch (ConnectorException ex) + { + this._logger.LogError(ex, "DocumentDbGetAccounts failed with status '{StatusCode}'.", ex.Status); + + var errorResponse = request.CreateResponse(HttpStatusCode.BadGateway); + await errorResponse + .WriteAsJsonAsync(new { success = false, error = ex.Message, statusCode = ex.Status, details = ex.ResponseBody }, cancellationToken) + .ConfigureAwait(continueOnCapturedContext: false); + + return errorResponse; + } + catch (Exception ex) when (!ex.IsFatal()) + { + this._logger.LogError(ex, "Error in DocumentDbGetAccounts."); + + var errorResponse = request.CreateResponse(HttpStatusCode.InternalServerError); + await errorResponse + .WriteAsJsonAsync(new { success = false, error = ex.Message }) + .ConfigureAwait(continueOnCapturedContext: false); + + return errorResponse; + } + } + + /// + /// Lists databases in the specified Cosmos DB account. + /// Route: GET /documentdb/databases?account={azureCosmosDBAccountName} + /// + [Function("DocumentDbGetDatabases")] + public async Task DocumentDbGetDatabasesAsync( + [HttpTrigger(AuthorizationLevel.Function, "get", Route = "documentdb/databases")] HttpRequestData request, + CancellationToken cancellationToken) + { + this._logger.LogInformation("DocumentDbGetDatabases: Using generated DocumentdbClient from SDK."); + + var account = System.Web.HttpUtility.ParseQueryString(request.Url.Query)["account"]; + if (string.IsNullOrWhiteSpace(account)) + { + var badRequest = request.CreateResponse(HttpStatusCode.BadRequest); + await badRequest + .WriteAsJsonAsync(new { success = false, error = "Missing required query parameter 'account'." }, cancellationToken) + .ConfigureAwait(continueOnCapturedContext: false); + + return badRequest; + } + + try + { + var databases = await this._documentDbClient + .GetDatabasesAsync(azureCosmosDBAccountName: account, cancellationToken: cancellationToken) + .ConfigureAwait(continueOnCapturedContext: false); + + var response = request.CreateResponse(HttpStatusCode.OK); + await response + .WriteAsJsonAsync(new { success = true, databases }, cancellationToken) + .ConfigureAwait(continueOnCapturedContext: false); + + return response; + } + catch (ConnectorException ex) + { + this._logger.LogError(ex, "DocumentDbGetDatabases failed with status '{StatusCode}'.", ex.Status); + + var errorResponse = request.CreateResponse(HttpStatusCode.BadGateway); + await errorResponse + .WriteAsJsonAsync(new { success = false, error = ex.Message, statusCode = ex.Status, details = ex.ResponseBody }, cancellationToken) + .ConfigureAwait(continueOnCapturedContext: false); + + return errorResponse; + } + catch (Exception ex) when (!ex.IsFatal()) + { + this._logger.LogError(ex, "Error in DocumentDbGetDatabases."); + + var errorResponse = request.CreateResponse(HttpStatusCode.InternalServerError); + await errorResponse + .WriteAsJsonAsync(new { success = false, error = ex.Message }) + .ConfigureAwait(continueOnCapturedContext: false); + + return errorResponse; + } + } +} diff --git a/DirectConnector/EventHubsFunctions.cs b/DirectConnector/EventHubsFunctions.cs new file mode 100644 index 0000000..005595e --- /dev/null +++ b/DirectConnector/EventHubsFunctions.cs @@ -0,0 +1,136 @@ +//------------------------------------------------------------ +// Copyright (c) Microsoft Corporation. All rights reserved. +//------------------------------------------------------------ + +using System.Net; +using Azure.Connectors.Sdk; +using Azure.Connectors.Sdk.Eventhubs; +using Microsoft.Azure.Functions.Worker; +using Microsoft.Azure.Functions.Worker.Http; +using Microsoft.Extensions.Logging; + +namespace DirectConnector; + +/// +/// Azure Functions demonstrating Azure Event Hubs operations using the generated +/// from the Azure Connectors SDK. +/// +public class EventHubsFunctions +{ + private readonly ILogger _logger; + private readonly EventhubsClient _eventHubsClient; + + public EventHubsFunctions( + ILogger logger, + EventhubsClient eventHubsClient) + { + this._logger = logger; + this._eventHubsClient = eventHubsClient; + } + + /// + /// Gets the list of Event Hubs accessible via the connection. + /// + [Function("EventHubsGetEventHubs")] + public async Task EventHubsGetEventHubsAsync( + [HttpTrigger(AuthorizationLevel.Function, "get", Route = "eventhubs/list")] HttpRequestData request, + CancellationToken cancellationToken) + { + this._logger.LogInformation("EventHubsGetEventHubs: Using generated EventhubsClient from SDK."); + + try + { + var eventHubs = await this._eventHubsClient + .GetEventHubsAsync(cancellationToken: cancellationToken) + .ConfigureAwait(continueOnCapturedContext: false); + + var response = request.CreateResponse(HttpStatusCode.OK); + await response + .WriteAsJsonAsync(new { success = true, eventHubs }, cancellationToken) + .ConfigureAwait(continueOnCapturedContext: false); + + return response; + } + catch (ConnectorException ex) + { + this._logger.LogError(ex, "EventHubsGetEventHubs failed with status '{StatusCode}'.", ex.Status); + + var errorResponse = request.CreateResponse(HttpStatusCode.BadGateway); + await errorResponse + .WriteAsJsonAsync(new { success = false, error = ex.Message, statusCode = ex.Status, details = ex.ResponseBody }, cancellationToken) + .ConfigureAwait(continueOnCapturedContext: false); + + return errorResponse; + } + catch (Exception ex) when (!ex.IsFatal()) + { + this._logger.LogError(ex, "Error in EventHubsGetEventHubs."); + + var errorResponse = request.CreateResponse(HttpStatusCode.InternalServerError); + await errorResponse + .WriteAsJsonAsync(new { success = false, error = ex.Message }) + .ConfigureAwait(continueOnCapturedContext: false); + + return errorResponse; + } + } + + /// + /// Gets the consumer groups for the specified Event Hub. + /// Route: GET /eventhubs/consumergroups?eventHub={eventHubName} + /// + [Function("EventHubsGetConsumerGroups")] + public async Task EventHubsGetConsumerGroupsAsync( + [HttpTrigger(AuthorizationLevel.Function, "get", Route = "eventhubs/consumergroups")] HttpRequestData request, + CancellationToken cancellationToken) + { + this._logger.LogInformation("EventHubsGetConsumerGroups: Using generated EventhubsClient from SDK."); + + var eventHubName = System.Web.HttpUtility.ParseQueryString(request.Url.Query)["eventHub"]; + if (string.IsNullOrWhiteSpace(eventHubName)) + { + var badRequest = request.CreateResponse(HttpStatusCode.BadRequest); + await badRequest + .WriteAsJsonAsync(new { success = false, error = "Missing required query parameter 'eventHub'." }, cancellationToken) + .ConfigureAwait(continueOnCapturedContext: false); + + return badRequest; + } + + try + { + var consumerGroups = await this._eventHubsClient + .GetConsumerGroupsAsync(theEventHubName: eventHubName, cancellationToken: cancellationToken) + .ConfigureAwait(continueOnCapturedContext: false); + + var response = request.CreateResponse(HttpStatusCode.OK); + await response + .WriteAsJsonAsync(new { success = true, consumerGroups }, cancellationToken) + .ConfigureAwait(continueOnCapturedContext: false); + + return response; + } + catch (ConnectorException ex) + { + this._logger.LogError(ex, "EventHubsGetConsumerGroups failed with status '{StatusCode}'.", ex.Status); + + var errorResponse = request.CreateResponse(HttpStatusCode.BadGateway); + await errorResponse + .WriteAsJsonAsync(new { success = false, error = ex.Message, statusCode = ex.Status, details = ex.ResponseBody }, cancellationToken) + .ConfigureAwait(continueOnCapturedContext: false); + + return errorResponse; + } + catch (Exception ex) when (!ex.IsFatal()) + { + this._logger.LogError(ex, "Error in EventHubsGetConsumerGroups."); + + var errorResponse = request.CreateResponse(HttpStatusCode.InternalServerError); + await errorResponse + .WriteAsJsonAsync(new { success = false, error = ex.Message }) + .ConfigureAwait(continueOnCapturedContext: false); + + return errorResponse; + } + } +} diff --git a/DirectConnector/ExcelOnlineBusinessFunctions.cs b/DirectConnector/ExcelOnlineBusinessFunctions.cs new file mode 100644 index 0000000..5852196 --- /dev/null +++ b/DirectConnector/ExcelOnlineBusinessFunctions.cs @@ -0,0 +1,136 @@ +//------------------------------------------------------------ +// Copyright (c) Microsoft Corporation. All rights reserved. +//------------------------------------------------------------ + +using System.Net; +using Azure.Connectors.Sdk; +using Azure.Connectors.Sdk.ExcelOnlineBusiness; +using Microsoft.Azure.Functions.Worker; +using Microsoft.Azure.Functions.Worker.Http; +using Microsoft.Extensions.Logging; + +namespace DirectConnector; + +/// +/// Azure Functions demonstrating Excel Online (Business) operations using the generated +/// from the Azure Connectors SDK. +/// +public class ExcelOnlineBusinessFunctions +{ + private readonly ILogger _logger; + private readonly ExcelOnlineBusinessClient _excelOnlineBusinessClient; + + public ExcelOnlineBusinessFunctions( + ILogger logger, + ExcelOnlineBusinessClient excelOnlineBusinessClient) + { + this._logger = logger; + this._excelOnlineBusinessClient = excelOnlineBusinessClient; + } + + /// + /// Gets the list of sources (drives/sites) accessible via the connection. + /// + [Function("ExcelOnlineBusinessGetSources")] + public async Task ExcelOnlineBusinessGetSourcesAsync( + [HttpTrigger(AuthorizationLevel.Function, "get", Route = "excelonlinebusiness/sources")] HttpRequestData request, + CancellationToken cancellationToken) + { + this._logger.LogInformation("ExcelOnlineBusinessGetSources: Using generated ExcelOnlineBusinessClient from SDK."); + + try + { + var sources = await this._excelOnlineBusinessClient + .GetSourcesAsync(cancellationToken: cancellationToken) + .ConfigureAwait(continueOnCapturedContext: false); + + var response = request.CreateResponse(HttpStatusCode.OK); + await response + .WriteAsJsonAsync(new { success = true, sources }, cancellationToken) + .ConfigureAwait(continueOnCapturedContext: false); + + return response; + } + catch (ConnectorException ex) + { + this._logger.LogError(ex, "ExcelOnlineBusinessGetSources failed with status '{StatusCode}'.", ex.Status); + + var errorResponse = request.CreateResponse(HttpStatusCode.BadGateway); + await errorResponse + .WriteAsJsonAsync(new { success = false, error = ex.Message, statusCode = ex.Status, details = ex.ResponseBody }, cancellationToken) + .ConfigureAwait(continueOnCapturedContext: false); + + return errorResponse; + } + catch (Exception ex) when (!ex.IsFatal()) + { + this._logger.LogError(ex, "Error in ExcelOnlineBusinessGetSources."); + + var errorResponse = request.CreateResponse(HttpStatusCode.InternalServerError); + await errorResponse + .WriteAsJsonAsync(new { success = false, error = ex.Message }) + .ConfigureAwait(continueOnCapturedContext: false); + + return errorResponse; + } + } + + /// + /// Gets the list of drives for the specified location (source). + /// Route: GET /excelonlinebusiness/drives?location={location} + /// + [Function("ExcelOnlineBusinessGetDrives")] + public async Task ExcelOnlineBusinessGetDrivesAsync( + [HttpTrigger(AuthorizationLevel.Function, "get", Route = "excelonlinebusiness/drives")] HttpRequestData request, + CancellationToken cancellationToken) + { + this._logger.LogInformation("ExcelOnlineBusinessGetDrives: Using generated ExcelOnlineBusinessClient from SDK."); + + var location = System.Web.HttpUtility.ParseQueryString(request.Url.Query)["location"]; + if (string.IsNullOrWhiteSpace(location)) + { + var badRequest = request.CreateResponse(HttpStatusCode.BadRequest); + await badRequest + .WriteAsJsonAsync(new { success = false, error = "Missing required query parameter 'location'." }, cancellationToken) + .ConfigureAwait(continueOnCapturedContext: false); + + return badRequest; + } + + try + { + var drives = await this._excelOnlineBusinessClient + .GetDrivesAsync(location: location, cancellationToken: cancellationToken) + .ConfigureAwait(continueOnCapturedContext: false); + + var response = request.CreateResponse(HttpStatusCode.OK); + await response + .WriteAsJsonAsync(new { success = true, drives }, cancellationToken) + .ConfigureAwait(continueOnCapturedContext: false); + + return response; + } + catch (ConnectorException ex) + { + this._logger.LogError(ex, "ExcelOnlineBusinessGetDrives failed with status '{StatusCode}'.", ex.Status); + + var errorResponse = request.CreateResponse(HttpStatusCode.BadGateway); + await errorResponse + .WriteAsJsonAsync(new { success = false, error = ex.Message, statusCode = ex.Status, details = ex.ResponseBody }, cancellationToken) + .ConfigureAwait(continueOnCapturedContext: false); + + return errorResponse; + } + catch (Exception ex) when (!ex.IsFatal()) + { + this._logger.LogError(ex, "Error in ExcelOnlineBusinessGetDrives."); + + var errorResponse = request.CreateResponse(HttpStatusCode.InternalServerError); + await errorResponse + .WriteAsJsonAsync(new { success = false, error = ex.Message }) + .ConfigureAwait(continueOnCapturedContext: false); + + return errorResponse; + } + } +} diff --git a/DirectConnector/MsGraphFunctions.cs b/DirectConnector/MsGraphFunctions.cs index 837e1a7..29ae844 100644 --- a/DirectConnector/MsGraphFunctions.cs +++ b/DirectConnector/MsGraphFunctions.cs @@ -187,7 +187,7 @@ await badRequest try { var group = await this._msGraphClient - .GetGroupPropertiesAsync(objectIDOfTheMicrosoftEntraIDGroup: groupId, cancellationToken: cancellationToken) + .GetGroupPropertiesAsync(objectIdOfTheMicrosoftEntraIdGroup: groupId, cancellationToken: cancellationToken) .ConfigureAwait(continueOnCapturedContext: false); var response = request.CreateResponse(HttpStatusCode.OK); diff --git a/DirectConnector/OutlookFunctions.cs b/DirectConnector/OutlookFunctions.cs new file mode 100644 index 0000000..83adf0b --- /dev/null +++ b/DirectConnector/OutlookFunctions.cs @@ -0,0 +1,124 @@ +//------------------------------------------------------------ +// Copyright (c) Microsoft Corporation. All rights reserved. +//------------------------------------------------------------ + +using System.Net; +using Azure.Connectors.Sdk; +using Azure.Connectors.Sdk.Outlook; +using Microsoft.Azure.Functions.Worker; +using Microsoft.Azure.Functions.Worker.Http; +using Microsoft.Extensions.Logging; + +namespace DirectConnector; + +/// +/// Azure Functions demonstrating Outlook operations using the generated +/// from the Azure Connectors SDK. +/// +public class OutlookFunctions +{ + private readonly ILogger _logger; + private readonly OutlookClient _outlookClient; + + public OutlookFunctions( + ILogger logger, + OutlookClient outlookClient) + { + this._logger = logger; + this._outlookClient = outlookClient; + } + + /// + /// Gets the list of calendars for the authenticated user. + /// + [Function("OutlookGetCalendars")] + public async Task OutlookGetCalendarsAsync( + [HttpTrigger(AuthorizationLevel.Function, "get", Route = "outlook/calendars")] HttpRequestData request, + CancellationToken cancellationToken) + { + this._logger.LogInformation("OutlookGetCalendars: Using generated OutlookClient from SDK."); + + try + { + var calendars = await this._outlookClient + .CalendarGetTablesAsync(cancellationToken: cancellationToken) + .ConfigureAwait(continueOnCapturedContext: false); + + var response = request.CreateResponse(HttpStatusCode.OK); + await response + .WriteAsJsonAsync(new { success = true, calendars }, cancellationToken) + .ConfigureAwait(continueOnCapturedContext: false); + + return response; + } + catch (ConnectorException ex) + { + this._logger.LogError(ex, "OutlookGetCalendars failed with status '{StatusCode}'.", ex.Status); + + var errorResponse = request.CreateResponse(HttpStatusCode.BadGateway); + await errorResponse + .WriteAsJsonAsync(new { success = false, error = ex.Message, statusCode = ex.Status, details = ex.ResponseBody }, cancellationToken) + .ConfigureAwait(continueOnCapturedContext: false); + + return errorResponse; + } + catch (Exception ex) when (!ex.IsFatal()) + { + this._logger.LogError(ex, "Error in OutlookGetCalendars."); + + var errorResponse = request.CreateResponse(HttpStatusCode.InternalServerError); + await errorResponse + .WriteAsJsonAsync(new { success = false, error = ex.Message }) + .ConfigureAwait(continueOnCapturedContext: false); + + return errorResponse; + } + } + + /// + /// Gets the list of contact folders for the authenticated user. + /// + [Function("OutlookGetContactFolders")] + public async Task OutlookGetContactFoldersAsync( + [HttpTrigger(AuthorizationLevel.Function, "get", Route = "outlook/contacts")] HttpRequestData request, + CancellationToken cancellationToken) + { + this._logger.LogInformation("OutlookGetContactFolders: Using generated OutlookClient from SDK."); + + try + { + var folders = await this._outlookClient + .ContactGetTablesAsync(cancellationToken: cancellationToken) + .ConfigureAwait(continueOnCapturedContext: false); + + var response = request.CreateResponse(HttpStatusCode.OK); + await response + .WriteAsJsonAsync(new { success = true, folders }, cancellationToken) + .ConfigureAwait(continueOnCapturedContext: false); + + return response; + } + catch (ConnectorException ex) + { + this._logger.LogError(ex, "OutlookGetContactFolders failed with status '{StatusCode}'.", ex.Status); + + var errorResponse = request.CreateResponse(HttpStatusCode.BadGateway); + await errorResponse + .WriteAsJsonAsync(new { success = false, error = ex.Message, statusCode = ex.Status, details = ex.ResponseBody }, cancellationToken) + .ConfigureAwait(continueOnCapturedContext: false); + + return errorResponse; + } + catch (Exception ex) when (!ex.IsFatal()) + { + this._logger.LogError(ex, "Error in OutlookGetContactFolders."); + + var errorResponse = request.CreateResponse(HttpStatusCode.InternalServerError); + await errorResponse + .WriteAsJsonAsync(new { success = false, error = ex.Message }) + .ConfigureAwait(continueOnCapturedContext: false); + + return errorResponse; + } + } +} diff --git a/DirectConnector/Program.cs b/DirectConnector/Program.cs index 54076db..4f5b2d9 100644 --- a/DirectConnector/Program.cs +++ b/DirectConnector/Program.cs @@ -40,6 +40,14 @@ services.AddYammerClient(configuration.GetSection("Connectors:Yammer")); services.AddWdatpClient(configuration.GetSection("Connectors:Wdatp")); services.AddUniversalPrintClient(configuration.GetSection("Connectors:UniversalPrint")); + services.AddAzureQueuesClient(configuration.GetSection("Connectors:AzureQueues")); + services.AddAzureTablesClient(configuration.GetSection("Connectors:AzureTables")); + services.AddDocumentDbClient(configuration.GetSection("Connectors:DocumentDB")); + services.AddEventHubsClient(configuration.GetSection("Connectors:EventHubs")); + services.AddExcelOnlineBusinessClient(configuration.GetSection("Connectors:ExcelOnlineBusiness")); + services.AddOutlookClient(configuration.GetSection("Connectors:Outlook")); + services.AddServiceBusClient(configuration.GetSection("Connectors:ServiceBus")); + services.AddWordOnlineBusinessClient(configuration.GetSection("Connectors:WordOnlineBusiness")); }) .Build(); diff --git a/DirectConnector/ServiceBusFunctions.cs b/DirectConnector/ServiceBusFunctions.cs new file mode 100644 index 0000000..202df20 --- /dev/null +++ b/DirectConnector/ServiceBusFunctions.cs @@ -0,0 +1,124 @@ +//------------------------------------------------------------ +// Copyright (c) Microsoft Corporation. All rights reserved. +//------------------------------------------------------------ + +using System.Net; +using Azure.Connectors.Sdk; +using Azure.Connectors.Sdk.Servicebus; +using Microsoft.Azure.Functions.Worker; +using Microsoft.Azure.Functions.Worker.Http; +using Microsoft.Extensions.Logging; + +namespace DirectConnector; + +/// +/// Azure Functions demonstrating Azure Service Bus operations using the generated +/// from the Azure Connectors SDK. +/// +public class ServiceBusFunctions +{ + private readonly ILogger _logger; + private readonly ServicebusClient _serviceBusClient; + + public ServiceBusFunctions( + ILogger logger, + ServicebusClient serviceBusClient) + { + this._logger = logger; + this._serviceBusClient = serviceBusClient; + } + + /// + /// Gets the list of queues in the Service Bus namespace. + /// + [Function("ServiceBusGetQueues")] + public async Task ServiceBusGetQueuesAsync( + [HttpTrigger(AuthorizationLevel.Function, "get", Route = "servicebus/queues")] HttpRequestData request, + CancellationToken cancellationToken) + { + this._logger.LogInformation("ServiceBusGetQueues: Using generated ServicebusClient from SDK."); + + try + { + var queues = await this._serviceBusClient + .GetQueuesAsync(cancellationToken: cancellationToken) + .ConfigureAwait(continueOnCapturedContext: false); + + var response = request.CreateResponse(HttpStatusCode.OK); + await response + .WriteAsJsonAsync(new { success = true, queues }, cancellationToken) + .ConfigureAwait(continueOnCapturedContext: false); + + return response; + } + catch (ConnectorException ex) + { + this._logger.LogError(ex, "ServiceBusGetQueues failed with status '{StatusCode}'.", ex.Status); + + var errorResponse = request.CreateResponse(HttpStatusCode.BadGateway); + await errorResponse + .WriteAsJsonAsync(new { success = false, error = ex.Message, statusCode = ex.Status, details = ex.ResponseBody }, cancellationToken) + .ConfigureAwait(continueOnCapturedContext: false); + + return errorResponse; + } + catch (Exception ex) when (!ex.IsFatal()) + { + this._logger.LogError(ex, "Error in ServiceBusGetQueues."); + + var errorResponse = request.CreateResponse(HttpStatusCode.InternalServerError); + await errorResponse + .WriteAsJsonAsync(new { success = false, error = ex.Message }) + .ConfigureAwait(continueOnCapturedContext: false); + + return errorResponse; + } + } + + /// + /// Gets the list of topics in the Service Bus namespace. + /// + [Function("ServiceBusGetTopics")] + public async Task ServiceBusGetTopicsAsync( + [HttpTrigger(AuthorizationLevel.Function, "get", Route = "servicebus/topics")] HttpRequestData request, + CancellationToken cancellationToken) + { + this._logger.LogInformation("ServiceBusGetTopics: Using generated ServicebusClient from SDK."); + + try + { + var topics = await this._serviceBusClient + .GetTopicsAsync(cancellationToken: cancellationToken) + .ConfigureAwait(continueOnCapturedContext: false); + + var response = request.CreateResponse(HttpStatusCode.OK); + await response + .WriteAsJsonAsync(new { success = true, topics }, cancellationToken) + .ConfigureAwait(continueOnCapturedContext: false); + + return response; + } + catch (ConnectorException ex) + { + this._logger.LogError(ex, "ServiceBusGetTopics failed with status '{StatusCode}'.", ex.Status); + + var errorResponse = request.CreateResponse(HttpStatusCode.BadGateway); + await errorResponse + .WriteAsJsonAsync(new { success = false, error = ex.Message, statusCode = ex.Status, details = ex.ResponseBody }, cancellationToken) + .ConfigureAwait(continueOnCapturedContext: false); + + return errorResponse; + } + catch (Exception ex) when (!ex.IsFatal()) + { + this._logger.LogError(ex, "Error in ServiceBusGetTopics."); + + var errorResponse = request.CreateResponse(HttpStatusCode.InternalServerError); + await errorResponse + .WriteAsJsonAsync(new { success = false, error = ex.Message }) + .ConfigureAwait(continueOnCapturedContext: false); + + return errorResponse; + } + } +} diff --git a/DirectConnector/SharePointFunctions.cs b/DirectConnector/SharePointFunctions.cs index fb59313..79eafc6 100644 --- a/DirectConnector/SharePointFunctions.cs +++ b/DirectConnector/SharePointFunctions.cs @@ -152,14 +152,14 @@ await badRequest { var folderId = request.Query["folder"]; - // NOTE: ListRootFolderAsync vs ListFolderAsync demonstrates + // NOTE: GetFolderMetadataAsync vs GetFolderMetadataByPathAsync demonstrates // two overloads with the same return type but different parameter sets. - var files = string.IsNullOrEmpty(folderId) + var file = string.IsNullOrEmpty(folderId) ? await this._sharePointClient - .ListRootFolderAsync(siteAddress, cancellationToken) + .GetFolderMetadataAsync(siteAddress, "/", cancellationToken) .ConfigureAwait(continueOnCapturedContext: false) : await this._sharePointClient - .ListFolderAsync(siteAddress, folderId, cancellationToken) + .GetFolderMetadataByPathAsync(siteAddress, folderId, cancellationToken) .ConfigureAwait(continueOnCapturedContext: false); var response = request.CreateResponse(HttpStatusCode.OK); @@ -169,17 +169,20 @@ await response success = true, site = siteAddress, folder = string.IsNullOrEmpty(folderId) ? "(root)" : folderId, - count = files?.Count ?? 0, - files = (files ?? Enumerable.Empty()).Select(file => new + count = file is null ? 0 : 1, + files = file is null ? [] : new[] { - id = file.Id, - name = file.Name, - displayName = file.DisplayName, - path = file.Path, - size = file.Size, - mediaType = file.MediaType, - isFolder = file.IsFolder - }) + new + { + id = file.Id, + name = file.Name, + displayName = file.DisplayName, + path = file.Path, + size = file.Size, + mediaType = file.MediaType, + isFolder = file.IsFolder + } + } }) .ConfigureAwait(continueOnCapturedContext: false); diff --git a/DirectConnector/TeamsFunctions.cs b/DirectConnector/TeamsFunctions.cs index f72a3ce..470c956 100644 --- a/DirectConnector/TeamsFunctions.cs +++ b/DirectConnector/TeamsFunctions.cs @@ -150,7 +150,7 @@ await response count = result?.ChannelList?.Count ?? 0, channels = result?.ChannelList?.Select(channel => new { - id = channel.ChannelID, + id = channel.ChannelId, displayName = channel.DisplayName, description = channel.DescriptionOfChannel, membershipType = channel.TheTypeOfTheChannel @@ -357,7 +357,7 @@ await response { success = true, message = "Message posted to Teams channel via generated TeamsClient from SDK.", - messageId = result?.MessageID, + messageId = result?.MessageId, messageLink = result?.MessageLink, timestamp = DateTime.UtcNow }) diff --git a/DirectConnector/WordOnlineBusinessFunctions.cs b/DirectConnector/WordOnlineBusinessFunctions.cs new file mode 100644 index 0000000..fef39d5 --- /dev/null +++ b/DirectConnector/WordOnlineBusinessFunctions.cs @@ -0,0 +1,124 @@ +//------------------------------------------------------------ +// Copyright (c) Microsoft Corporation. All rights reserved. +//------------------------------------------------------------ + +using System.Net; +using Azure.Connectors.Sdk; +using Azure.Connectors.Sdk.WordOnlineBusiness; +using Microsoft.Azure.Functions.Worker; +using Microsoft.Azure.Functions.Worker.Http; +using Microsoft.Extensions.Logging; + +namespace DirectConnector; + +/// +/// Azure Functions demonstrating Word Online (Business) operations using the generated +/// from the Azure Connectors SDK. +/// +public class WordOnlineBusinessFunctions +{ + private readonly ILogger _logger; + private readonly WordOnlineBusinessClient _wordOnlineBusinessClient; + + public WordOnlineBusinessFunctions( + ILogger logger, + WordOnlineBusinessClient wordOnlineBusinessClient) + { + this._logger = logger; + this._wordOnlineBusinessClient = wordOnlineBusinessClient; + } + + /// + /// Gets the list of sources accessible via the connection. + /// + [Function("WordOnlineBusinessGetSources")] + public async Task WordOnlineBusinessGetSourcesAsync( + [HttpTrigger(AuthorizationLevel.Function, "get", Route = "wordonlinebusiness/sources")] HttpRequestData request, + CancellationToken cancellationToken) + { + this._logger.LogInformation("WordOnlineBusinessGetSources: Using generated WordOnlineBusinessClient from SDK."); + + try + { + var sources = await this._wordOnlineBusinessClient + .GetSourcesAsync(cancellationToken: cancellationToken) + .ConfigureAwait(continueOnCapturedContext: false); + + var response = request.CreateResponse(HttpStatusCode.OK); + await response + .WriteAsJsonAsync(new { success = true, sources }, cancellationToken) + .ConfigureAwait(continueOnCapturedContext: false); + + return response; + } + catch (ConnectorException ex) + { + this._logger.LogError(ex, "WordOnlineBusinessGetSources failed with status '{StatusCode}'.", ex.Status); + + var errorResponse = request.CreateResponse(HttpStatusCode.BadGateway); + await errorResponse + .WriteAsJsonAsync(new { success = false, error = ex.Message, statusCode = ex.Status, details = ex.ResponseBody }, cancellationToken) + .ConfigureAwait(continueOnCapturedContext: false); + + return errorResponse; + } + catch (Exception ex) when (!ex.IsFatal()) + { + this._logger.LogError(ex, "Error in WordOnlineBusinessGetSources."); + + var errorResponse = request.CreateResponse(HttpStatusCode.InternalServerError); + await errorResponse + .WriteAsJsonAsync(new { success = false, error = ex.Message }) + .ConfigureAwait(continueOnCapturedContext: false); + + return errorResponse; + } + } + + /// + /// Gets the list of drives accessible via the connection. + /// + [Function("WordOnlineBusinessGetDrives")] + public async Task WordOnlineBusinessGetDrivesAsync( + [HttpTrigger(AuthorizationLevel.Function, "get", Route = "wordonlinebusiness/drives")] HttpRequestData request, + CancellationToken cancellationToken) + { + this._logger.LogInformation("WordOnlineBusinessGetDrives: Using generated WordOnlineBusinessClient from SDK."); + + try + { + var drives = await this._wordOnlineBusinessClient + .GetDrivesAsync(cancellationToken: cancellationToken) + .ConfigureAwait(continueOnCapturedContext: false); + + var response = request.CreateResponse(HttpStatusCode.OK); + await response + .WriteAsJsonAsync(new { success = true, drives }, cancellationToken) + .ConfigureAwait(continueOnCapturedContext: false); + + return response; + } + catch (ConnectorException ex) + { + this._logger.LogError(ex, "WordOnlineBusinessGetDrives failed with status '{StatusCode}'.", ex.Status); + + var errorResponse = request.CreateResponse(HttpStatusCode.BadGateway); + await errorResponse + .WriteAsJsonAsync(new { success = false, error = ex.Message, statusCode = ex.Status, details = ex.ResponseBody }, cancellationToken) + .ConfigureAwait(continueOnCapturedContext: false); + + return errorResponse; + } + catch (Exception ex) when (!ex.IsFatal()) + { + this._logger.LogError(ex, "Error in WordOnlineBusinessGetDrives."); + + var errorResponse = request.CreateResponse(HttpStatusCode.InternalServerError); + await errorResponse + .WriteAsJsonAsync(new { success = false, error = ex.Message }) + .ConfigureAwait(continueOnCapturedContext: false); + + return errorResponse; + } + } +}