diff --git a/sdk/storage/Azure.Storage.Webjobs.Extensions.Blobs/README.md b/sdk/storage/Azure.Storage.Webjobs.Extensions.Blobs/README.md index c9a5565863c79..de913d33ae439 100644 --- a/sdk/storage/Azure.Storage.Webjobs.Extensions.Blobs/README.md +++ b/sdk/storage/Azure.Storage.Webjobs.Extensions.Blobs/README.md @@ -1,34 +1,159 @@ # Azure WebJobs Storage Blobs client library for .NET -TODO +This extension provides functionality for accessing Azure Storage Blobs in Azure Functions. ## Getting started ### Install the package +Install the Storage Blobs extension with [NuGet][nuget]: + +```Powershell +dotnet add package Azure.WebJobs.Extensions.Storage.Blobs +``` ### Prerequisites +You need an [Azure subscription][azure_sub] and a +[Storage Account][storage_account_docs] to use this package. + +To create a new Storage Account, you can use the [Azure Portal][storage_account_create_portal], +[Azure PowerShell][storage_account_create_ps], or the [Azure CLI][storage_account_create_cli]. +Here's an example using the Azure CLI: + +```Powershell +az storage account create --name --resource-group --location westus --sku Standard_LRS +``` ### Authenticate the client +In order for the extension to access Blobs, you will need the connection string which can be found in the [Azure Portal](https://portal.azure.com/) or by using the [Azure CLI](https://docs.microsoft.com/cli/azure) snippet below. + +```bash +az storage account show-connection-string -g -n +``` + +The connection string can be supplied through [AzureWebJobsStorage app setting](https://docs.microsoft.com/azure/azure-functions/functions-app-settings). ## Key concepts -TODO +### Using Blob binding + +Please follow the [input binding tutorial](https://docs.microsoft.com/azure/azure-functions/functions-bindings-storage-blob-input?tabs=csharp) and [output binding tutorial](https://docs.microsoft.com/azure/azure-functions/functions-bindings-storage-blob-output?tabs=csharp) to learn about using this extension for accessing Blobs. + +### Using Blob trigger + +Please follow the [tutorial](https://docs.microsoft.com/azure/azure-functions/functions-bindings-storage-blob-trigger?tabs=csharp) to learn about triggering an Azure Function when a blob is modified. ## Examples -TODO +### Binding to string + +```C# Snippet:BlobFunction_String +public static class BlobFunction_String +{ + [FunctionName("BlobFunction")] + public static void Run( + [BlobTrigger("sample-container/sample-blob-1")] string blobTriggerContent, + [Blob("sample-container/sample-blob-2")] string blobContent, + ILogger logger) + { + logger.LogInformation("Blob sample-container/sample-blob-1 has been updated with content: {content}", blobTriggerContent); + logger.LogInformation("Blob sample-container/sample-blob-2 has content: {content}", blobContent); + } +} +``` + +### Reading from stream + +```C# Snippet:BlobFunction_ReadStream +public static class BlobFunction_ReadStream +{ + [FunctionName("BlobFunction")] + public static void Run( + [BlobTrigger("sample-container/sample-blob-1")] Stream blobTriggerStream, + [Blob("sample-container/sample-blob-2", FileAccess.Read)] Stream blobStream, + ILogger logger) + { + using var blobTriggerStreamReader = new StreamReader(blobTriggerStream); + logger.LogInformation("Blob sample-container/sample-blob-1 has been updated with content: {content}", blobTriggerStreamReader.ReadToEnd()); + using var blobStreamReader = new StreamReader(blobStream); + logger.LogInformation("Blob sample-container/sample-blob-2 has content: {content}", blobStreamReader.ReadToEnd()); + } +} +``` + +### Writing to stream + +```C# Snippet:BlobFunction_WriteStream +public static class BlobFunction_WriteStream +{ + [FunctionName("BlobFunction")] + public static async Task Run( + [BlobTrigger("sample-container/sample-blob-1")] Stream blobTriggerStream, + [Blob("sample-container/sample-blob-2", FileAccess.Write)] Stream blobStream, + ILogger logger) + { + await blobTriggerStream.CopyToAsync(blobStream); + logger.LogInformation("Blob sample-container/sample-blob-1 has been copied to sample-container/sample-blob-2"); + } +} +``` + +### Binding to Azure Storage Blob SDK types + +```C# Snippet:BlobFunction_BlobBaseClient +public static class BlobFunction_BlobBaseClient +{ + [FunctionName("BlobFunction")] + public static async Task Run( + [BlobTrigger("sample-container/sample-blob-1")] BlobBaseClient blobTriggerClient, + [Blob("sample-container/sample-blob-2")] BlobBaseClient blobClient, + ILogger logger) + { + BlobProperties blobTriggerProperties = await blobTriggerClient.GetPropertiesAsync(); + logger.LogInformation("Blob sample-container/sample-blob-1 has been updated on: {datetime}", blobTriggerProperties.LastModified); + BlobProperties blobProperties = await blobClient.GetPropertiesAsync(); + logger.LogInformation("Blob sample-container/sample-blob-2 has been updated on: {datetime}", blobProperties.LastModified); + } +} +``` ## Troubleshooting -TODO +Please refer to [Monitor Azure Functions](https://docs.microsoft.com/azure/azure-functions/functions-monitoring) for troubleshooting guidance. ## Next steps -TODO +Read the [introduction to Azure Function](https://docs.microsoft.com/azure/azure-functions/functions-overview) or [creating an Azure Function guide](https://docs.microsoft.com/azure/azure-functions/functions-create-first-azure-function). ## Contributing -TODO \ No newline at end of file +See the [Storage CONTRIBUTING.md][storage_contrib] for details on building, +testing, and contributing to this library. + +This project welcomes contributions and suggestions. Most contributions require +you to agree to a Contributor License Agreement (CLA) declaring that you have +the right to, and actually do, grant us the rights to use your contribution. For +details, visit [cla.microsoft.com][cla]. + +This project has adopted the [Microsoft Open Source Code of Conduct][coc]. +For more information see the [Code of Conduct FAQ][coc_faq] +or contact [opencode@microsoft.com][coc_contact] with any +additional questions or comments. + +![Impressions](https://azure-sdk-impressions.azurewebsites.net/api/impressions/azure-sdk-for-net%2Fsdk%2Fstorage%2FAzure.Storage.Webjobs.Extensions.Blobs%2FREADME.png) + + +[nuget]: https://www.nuget.org/ +[storage_account_docs]: https://docs.microsoft.com/azure/storage/common/storage-account-overview +[storage_account_create_ps]: https://docs.microsoft.com/azure/storage/common/storage-quickstart-create-account?tabs=azure-powershell +[storage_account_create_cli]: https://docs.microsoft.com/azure/storage/common/storage-quickstart-create-account?tabs=azure-cli +[storage_account_create_portal]: https://docs.microsoft.com/azure/storage/common/storage-quickstart-create-account?tabs=azure-portal +[azure_sub]: https://azure.microsoft.com/free/ +[RequestFailedException]: https://github.com/Azure/azure-sdk-for-net/tree/master/sdk/core/Azure.Core/src/RequestFailedException.cs +[storage_contrib]: https://github.com/Azure/azure-sdk-for-net/blob/master/sdk/storage/CONTRIBUTING.md +[cla]: https://cla.microsoft.com +[coc]: https://opensource.microsoft.com/codeofconduct/ +[coc_faq]: https://opensource.microsoft.com/codeofconduct/faq/ +[coc_contact]: mailto:opencode@microsoft.com diff --git a/sdk/storage/Azure.Storage.Webjobs.Extensions.Blobs/samples/Azure.WebJobs.Extensions.Storage.Blobs.Samples.Tests.csproj b/sdk/storage/Azure.Storage.Webjobs.Extensions.Blobs/samples/Azure.WebJobs.Extensions.Storage.Blobs.Samples.Tests.csproj new file mode 100644 index 0000000000000..77c43e294735a --- /dev/null +++ b/sdk/storage/Azure.Storage.Webjobs.Extensions.Blobs/samples/Azure.WebJobs.Extensions.Storage.Blobs.Samples.Tests.csproj @@ -0,0 +1,14 @@ + + + $(RequiredTargetFrameworks) + + + + + + + + + + + diff --git a/sdk/storage/Azure.Storage.Webjobs.Extensions.Blobs/samples/BlobExtensionSamples.cs b/sdk/storage/Azure.Storage.Webjobs.Extensions.Blobs/samples/BlobExtensionSamples.cs new file mode 100644 index 0000000000000..e086ff3d89a98 --- /dev/null +++ b/sdk/storage/Azure.Storage.Webjobs.Extensions.Blobs/samples/BlobExtensionSamples.cs @@ -0,0 +1,115 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + + +using System; +using System.Collections.Generic; +using System.IO; +using System.Threading.Tasks; +using Azure.Storage.Blobs.Models; +using Azure.Storage.Blobs.Specialized; +using Azure.WebJobs.Extensions.Storage.Common.Tests; +using Microsoft.Azure.WebJobs; +using Microsoft.Extensions.Azure; +using Microsoft.Extensions.Hosting; +using Microsoft.Extensions.Logging; +using NUnit.Framework; + +namespace Azure.WebJobs.Extensions.Storage.Blobs.Samples.Tests +{ + public class BlobExtensionSamples + { + [TestCase(typeof(BlobFunction_String))] + [TestCase(typeof(BlobFunction_ReadStream))] + [TestCase(typeof(BlobFunction_WriteStream))] + [TestCase(typeof(BlobFunction_BlobBaseClient))] + public async Task Run_BlobFunction(Type programType) + { + var containerClient = AzuriteNUnitFixture.Instance.GetBlobServiceClient().GetBlobContainerClient("sample-container"); + await containerClient.CreateIfNotExistsAsync(); + await containerClient.GetBlockBlobClient("sample-blob").UploadTextAsync("content"); + await containerClient.GetBlockBlobClient("sample-blob-1").UploadTextAsync("content-1"); + await containerClient.GetBlockBlobClient("sample-blob-2").UploadTextAsync("content-2"); + await RunTrigger(programType); + } + + private async Task RunTrigger(Type programType) + { + await FunctionalTest.RunTriggerAsync(b => { + b.Services.AddAzureClients(builder => + { + builder.ConfigureDefaults(options => options.Transport = AzuriteNUnitFixture.Instance.GetTransport()); + }); + b.AddAzureStorageBlobs(); + }, programType, + settings: new Dictionary() { + // This takes precedence over env variables. + { "ConnectionStrings:AzureWebJobsStorage", AzuriteNUnitFixture.Instance.GetAzureAccount().ConnectionString } + }); + } + } + + #region Snippet:BlobFunction_String + public static class BlobFunction_String + { + [FunctionName("BlobFunction")] + public static void Run( + [BlobTrigger("sample-container/sample-blob-1")] string blobTriggerContent, + [Blob("sample-container/sample-blob-2")] string blobContent, + ILogger logger) + { + logger.LogInformation("Blob sample-container/sample-blob-1 has been updated with content: {content}", blobTriggerContent); + logger.LogInformation("Blob sample-container/sample-blob-2 has content: {content}", blobContent); + } + } + #endregion + + #region Snippet:BlobFunction_ReadStream + public static class BlobFunction_ReadStream + { + [FunctionName("BlobFunction")] + public static void Run( + [BlobTrigger("sample-container/sample-blob-1")] Stream blobTriggerStream, + [Blob("sample-container/sample-blob-2", FileAccess.Read)] Stream blobStream, + ILogger logger) + { + using var blobTriggerStreamReader = new StreamReader(blobTriggerStream); + logger.LogInformation("Blob sample-container/sample-blob-1 has been updated with content: {content}", blobTriggerStreamReader.ReadToEnd()); + using var blobStreamReader = new StreamReader(blobStream); + logger.LogInformation("Blob sample-container/sample-blob-2 has content: {content}", blobStreamReader.ReadToEnd()); + } + } + #endregion + + #region Snippet:BlobFunction_WriteStream + public static class BlobFunction_WriteStream + { + [FunctionName("BlobFunction")] + public static async Task Run( + [BlobTrigger("sample-container/sample-blob-1")] Stream blobTriggerStream, + [Blob("sample-container/sample-blob-2", FileAccess.Write)] Stream blobStream, + ILogger logger) + { + await blobTriggerStream.CopyToAsync(blobStream); + logger.LogInformation("Blob sample-container/sample-blob-1 has been copied to sample-container/sample-blob-2"); + } + } + #endregion + + #region Snippet:BlobFunction_BlobBaseClient + public static class BlobFunction_BlobBaseClient + { + [FunctionName("BlobFunction")] + public static async Task Run( + [BlobTrigger("sample-container/sample-blob-1")] BlobBaseClient blobTriggerClient, + [Blob("sample-container/sample-blob-2")] BlobBaseClient blobClient, + ILogger logger) + { + BlobProperties blobTriggerProperties = await blobTriggerClient.GetPropertiesAsync(); + logger.LogInformation("Blob sample-container/sample-blob-1 has been updated on: {datetime}", blobTriggerProperties.LastModified); + BlobProperties blobProperties = await blobClient.GetPropertiesAsync(); + logger.LogInformation("Blob sample-container/sample-blob-2 has been updated on: {datetime}", blobProperties.LastModified); + } + } + #endregion +} diff --git a/sdk/storage/Azure.Storage.Webjobs.Extensions.Common/README.md b/sdk/storage/Azure.Storage.Webjobs.Extensions.Common/README.md index 358e41d8b3738..22a73c4979ee9 100644 --- a/sdk/storage/Azure.Storage.Webjobs.Extensions.Common/README.md +++ b/sdk/storage/Azure.Storage.Webjobs.Extensions.Common/README.md @@ -1,34 +1,79 @@ # Azure WebJobs Storage Common client library for .NET -TODO +This package provides infrastruture shared by the other Azure WebJobs Storage libraries. ## Getting started ### Install the package +Install the common library with [NuGet][nuget]: + +```Powershell +dotnet add package Azure.WebJobs.Extensions.Storage.Common +``` ### Prerequisites +You need an [Azure subscription][azure_sub] and a +[Storage Account][storage_account_docs] to use this package. + +To create a new Storage Account, you can use the [Azure Portal][storage_account_create_portal], +[Azure PowerShell][storage_account_create_ps], or the [Azure CLI][storage_account_create_cli]. +Here's an example using the Azure CLI: + +```Powershell +az storage account create --name --resource-group --location westus --sku Standard_LRS +``` ### Authenticate the client +In order for the extensions to access Storage, you will need the connection string which can be found in the [Azure Portal](https://portal.azure.com/) or by using the [Azure CLI](https://docs.microsoft.com/cli/azure) snippet below. -## Key concepts +```bash +az storage account show-connection-string -g -n +``` -TODO +The connection string can be supplied through [AzureWebJobsStorage app setting](https://docs.microsoft.com/azure/azure-functions/functions-app-settings). -## Examples +## Key concepts -TODO +## Examples ## Troubleshooting -TODO +Please refer to [Monitor Azure Functions](https://docs.microsoft.com/azure/azure-functions/functions-monitoring) for troubleshooting guidance. ## Next steps -TODO +Read the [introduction to Azure Function](https://docs.microsoft.com/azure/azure-functions/functions-overview) or [creating an Azure Function guide](https://docs.microsoft.com/azure/azure-functions/functions-create-first-azure-function). ## Contributing -TODO \ No newline at end of file +See the [Storage CONTRIBUTING.md][storage_contrib] for details on building, +testing, and contributing to this library. + +This project welcomes contributions and suggestions. Most contributions require +you to agree to a Contributor License Agreement (CLA) declaring that you have +the right to, and actually do, grant us the rights to use your contribution. For +details, visit [cla.microsoft.com][cla]. + +This project has adopted the [Microsoft Open Source Code of Conduct][coc]. +For more information see the [Code of Conduct FAQ][coc_faq] +or contact [opencode@microsoft.com][coc_contact] with any +additional questions or comments. + +![Impressions](https://azure-sdk-impressions.azurewebsites.net/api/impressions/azure-sdk-for-net%2Fsdk%2Fstorage%2FAzure.Storage.Webjobs.Extensions.Blobs%2FREADME.png) + + +[nuget]: https://www.nuget.org/ +[storage_account_docs]: https://docs.microsoft.com/azure/storage/common/storage-account-overview +[storage_account_create_ps]: https://docs.microsoft.com/azure/storage/common/storage-quickstart-create-account?tabs=azure-powershell +[storage_account_create_cli]: https://docs.microsoft.com/azure/storage/common/storage-quickstart-create-account?tabs=azure-cli +[storage_account_create_portal]: https://docs.microsoft.com/azure/storage/common/storage-quickstart-create-account?tabs=azure-portal +[azure_sub]: https://azure.microsoft.com/free/ +[RequestFailedException]: https://github.com/Azure/azure-sdk-for-net/tree/master/sdk/core/Azure.Core/src/RequestFailedException.cs +[storage_contrib]: https://github.com/Azure/azure-sdk-for-net/blob/master/sdk/storage/CONTRIBUTING.md +[cla]: https://cla.microsoft.com +[coc]: https://opensource.microsoft.com/codeofconduct/ +[coc_faq]: https://opensource.microsoft.com/codeofconduct/faq/ +[coc_contact]: mailto:opencode@microsoft.com diff --git a/sdk/storage/Azure.Storage.Webjobs.Extensions.Queues/README.md b/sdk/storage/Azure.Storage.Webjobs.Extensions.Queues/README.md index bcb6fe3053928..3f0e7abee4e30 100644 --- a/sdk/storage/Azure.Storage.Webjobs.Extensions.Queues/README.md +++ b/sdk/storage/Azure.Storage.Webjobs.Extensions.Queues/README.md @@ -1,34 +1,205 @@ # Azure WebJobs Storage Queues client library for .NET -TODO +This extension provides functionality for accessing Azure Storage Queues in Azure Functions. ## Getting started ### Install the package +Install the Storage Queues extension with [NuGet][nuget]: + +```Powershell +dotnet add package Azure.WebJobs.Extensions.Storage.Queues +``` ### Prerequisites +You need an [Azure subscription][azure_sub] and a +[Storage Account][storage_account_docs] to use this package. + +To create a new Storage Account, you can use the [Azure Portal][storage_account_create_portal], +[Azure PowerShell][storage_account_create_ps], or the [Azure CLI][storage_account_create_cli]. +Here's an example using the Azure CLI: + +```Powershell +az storage account create --name --resource-group --location westus --sku Standard_LRS +``` ### Authenticate the client +In order for the extension to access Queues, you will need the connection string which can be found in the [Azure Portal](https://portal.azure.com/) or by using the [Azure CLI](https://docs.microsoft.com/cli/azure) snippet below. + +```bash +az storage account show-connection-string -g -n +``` + +The connection string can be supplied through [AzureWebJobsStorage app setting](https://docs.microsoft.com/azure/azure-functions/functions-app-settings). ## Key concepts -TODO +### Using Queue binding + +Please follow the [binding tutorial](https://docs.microsoft.com/azure/azure-functions/functions-bindings-storage-queue-output?tabs=csharp) to learn about using this extension for producing messages into queues in Azure Functions. + +### Using Queue trigger + +Please follow the [tutorial](https://docs.microsoft.com/azure/azure-functions/functions-bindings-storage-queue-trigger?tabs=csharp) to learn about how to listen to queues in Azure Functions. ## Examples -TODO +### Listening to queue + +#### Binding queue message to string + +```C# Snippet:QueueTriggerFunction_String +public static class QueueTriggerFunction_String +{ + [FunctionName("QueueTriggerFunction")] + public static void Run( + [QueueTrigger("sample-queue")] string message, + ILogger logger) + { + logger.LogInformation("Received message from sample-queue, content={content}", message); + } +} +``` + +#### Binding queue message to custom type + +```C# Snippet:QueueTriggerFunction_CustomObject +public static class QueueTriggerFunction_CustomObject +{ + public class CustomMessage + { + public string Content { get; set; } + } + + [FunctionName("QueueTriggerFunction")] + public static void Run( + [QueueTrigger("sample-queue")] CustomMessage message, + ILogger logger) + { + logger.LogInformation("Received message from sample-queue, content={content}", message.Content); + } +} +``` + +#### Binding queue message to JObject + +```C# Snippet:QueueTriggerFunction_JObject +public static class QueueTriggerFunction_JObject +{ + [FunctionName("QueueTriggerFunction")] + public static void Run( + [QueueTrigger("sample-queue")] JObject message, + ILogger logger) + { + logger.LogInformation("Received message from sample-queue, content={content}", message["content"]); + } +} +``` + +### Publishing messages to queue + +#### Publishing message as string + +```C# Snippet:QueueSenderFunction_String_Return +public static class QueueSenderFunction_String_Return +{ + [FunctionName("QueueFunction")] + [return: Queue("sample-queue-2")] + public static string Run( + [QueueTrigger("sample-queue-1")] string message, + ILogger logger) + { + logger.LogInformation("Received message from sample-queue-1, content={content}", message); + logger.LogInformation("Dispatching message to sample-queue-2"); + return message; + } +} +``` + +#### Publishing message as custom type through out parameter + +```C# Snippet:QueueSenderFunction_CustomObject_OutParamter +public static class QueueSenderFunction_CustomObject_OutParamter +{ + public class CustomMessage + { + public string Content { get; set; } + } + + [FunctionName("QueueFunction")] + public static void Run( + [QueueTrigger("sample-queue-1")] CustomMessage incomingMessage, + [Queue("sample-queue-2")] out CustomMessage outgoingMessage, + ILogger logger) + { + logger.LogInformation("Received message from sample-queue-1, content={content}", incomingMessage.Content); + logger.LogInformation("Dispatching message to sample-queue-2"); + outgoingMessage = incomingMessage; + } +} +``` + +#### Publishing message as custom type through collector + +```C# Snippet:QueueSenderFunction_CustomObject_Collector +public static class QueueSenderFunction_CustomObject_Collector +{ + public class CustomMessage + { + public string Content { get; set; } + } + + [FunctionName("QueueFunction")] + public static void Run( + [QueueTrigger("sample-queue-1")] CustomMessage incomingMessage, + [Queue("sample-queue-2")] ICollector collector, + ILogger logger) + { + logger.LogInformation("Received message from sample-queue-1, content={content}", incomingMessage.Content); + logger.LogInformation("Dispatching message to sample-queue-2"); + collector.Add(incomingMessage); + } +} +``` ## Troubleshooting -TODO +Please refer to [Monitor Azure Functions](https://docs.microsoft.com/azure/azure-functions/functions-monitoring) for troubleshooting guidance. ## Next steps -TODO +Read the [introduction to Azure Function](https://docs.microsoft.com/azure/azure-functions/functions-overview) or [creating an Azure Function guide](https://docs.microsoft.com/azure/azure-functions/functions-create-first-azure-function). ## Contributing -TODO \ No newline at end of file +See the [Storage CONTRIBUTING.md][storage_contrib] for details on building, +testing, and contributing to this library. + +This project welcomes contributions and suggestions. Most contributions require +you to agree to a Contributor License Agreement (CLA) declaring that you have +the right to, and actually do, grant us the rights to use your contribution. For +details, visit [cla.microsoft.com][cla]. + +This project has adopted the [Microsoft Open Source Code of Conduct][coc]. +For more information see the [Code of Conduct FAQ][coc_faq] +or contact [opencode@microsoft.com][coc_contact] with any +additional questions or comments. + +![Impressions](https://azure-sdk-impressions.azurewebsites.net/api/impressions/azure-sdk-for-net%2Fsdk%2Fstorage%2FAzure.Storage.Webjobs.Extensions.Blobs%2FREADME.png) + + +[nuget]: https://www.nuget.org/ +[storage_account_docs]: https://docs.microsoft.com/azure/storage/common/storage-account-overview +[storage_account_create_ps]: https://docs.microsoft.com/azure/storage/common/storage-quickstart-create-account?tabs=azure-powershell +[storage_account_create_cli]: https://docs.microsoft.com/azure/storage/common/storage-quickstart-create-account?tabs=azure-cli +[storage_account_create_portal]: https://docs.microsoft.com/azure/storage/common/storage-quickstart-create-account?tabs=azure-portal +[azure_sub]: https://azure.microsoft.com/free/ +[RequestFailedException]: https://github.com/Azure/azure-sdk-for-net/tree/master/sdk/core/Azure.Core/src/RequestFailedException.cs +[storage_contrib]: https://github.com/Azure/azure-sdk-for-net/blob/master/sdk/storage/CONTRIBUTING.md +[cla]: https://cla.microsoft.com +[coc]: https://opensource.microsoft.com/codeofconduct/ +[coc_faq]: https://opensource.microsoft.com/codeofconduct/faq/ +[coc_contact]: mailto:opencode@microsoft.com diff --git a/sdk/storage/Azure.Storage.Webjobs.Extensions.Queues/samples/Azure.WebJobs.Extensions.Storage.Queues.Samples.Tests.csproj b/sdk/storage/Azure.Storage.Webjobs.Extensions.Queues/samples/Azure.WebJobs.Extensions.Storage.Queues.Samples.Tests.csproj new file mode 100644 index 0000000000000..1932ed97f6d88 --- /dev/null +++ b/sdk/storage/Azure.Storage.Webjobs.Extensions.Queues/samples/Azure.WebJobs.Extensions.Storage.Queues.Samples.Tests.csproj @@ -0,0 +1,14 @@ + + + $(RequiredTargetFrameworks) + + + + + + + + + + + diff --git a/sdk/storage/Azure.Storage.Webjobs.Extensions.Queues/samples/QueueExtensionSamples.cs b/sdk/storage/Azure.Storage.Webjobs.Extensions.Queues/samples/QueueExtensionSamples.cs new file mode 100644 index 0000000000000..f1865f30c491c --- /dev/null +++ b/sdk/storage/Azure.Storage.Webjobs.Extensions.Queues/samples/QueueExtensionSamples.cs @@ -0,0 +1,156 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + + +using System; +using System.Collections.Generic; +using System.Threading.Tasks; +using Azure.WebJobs.Extensions.Storage.Common.Tests; +using Microsoft.Azure.WebJobs; +using Microsoft.Extensions.Azure; +using Microsoft.Extensions.Hosting; +using Microsoft.Extensions.Logging; +using Newtonsoft.Json.Linq; +using NUnit.Framework; + +namespace Azure.WebJobs.Extensions.Storage.Blobs.Samples.Tests +{ + public class BlobExtensionSamples + { + [TestCase(typeof(QueueTriggerFunction_String), "sample message")] + [TestCase(typeof(QueueTriggerFunction_CustomObject), "{ \"content\": \"sample message\"}")] + [TestCase(typeof(QueueTriggerFunction_JObject), "{ \"content\": \"sample message\"}")] + [TestCase(typeof(QueueSenderFunction_String_Return), "sample message")] + [TestCase(typeof(QueueSenderFunction_CustomObject_OutParamter), "{ \"content\": \"sample message\"}")] + [TestCase(typeof(QueueSenderFunction_CustomObject_Collector), "{ \"content\": \"sample message\"}")] + public async Task Run_QueueFunction(Type programType, string message) + { + var queueServiceClient = AzuriteNUnitFixture.Instance.GetQueueServiceClient(); + await queueServiceClient.GetQueueClient("sample-queue").DeleteIfExistsAsync(); + await queueServiceClient.GetQueueClient("sample-queue").CreateIfNotExistsAsync(); + await queueServiceClient.GetQueueClient("sample-queue").SendMessageAsync(message); + await queueServiceClient.GetQueueClient("sample-queue-1").DeleteIfExistsAsync(); + await queueServiceClient.GetQueueClient("sample-queue-1").CreateIfNotExistsAsync(); + await queueServiceClient.GetQueueClient("sample-queue-1").SendMessageAsync(message); + await queueServiceClient.GetQueueClient("sample-queue-2").CreateIfNotExistsAsync(); + await RunTrigger(programType); + } + + private async Task RunTrigger(Type programType) + { + await FunctionalTest.RunTriggerAsync(b => { + b.Services.AddAzureClients(builder => + { + builder.ConfigureDefaults(options => options.Transport = AzuriteNUnitFixture.Instance.GetTransport()); + }); + b.AddAzureStorageQueues(); + }, programType, + settings: new Dictionary() { + // This takes precedence over env variables. + { "ConnectionStrings:AzureWebJobsStorage", AzuriteNUnitFixture.Instance.GetAzureAccount().ConnectionString } + }); + } + } + + #region Snippet:QueueTriggerFunction_String + public static class QueueTriggerFunction_String + { + [FunctionName("QueueTriggerFunction")] + public static void Run( + [QueueTrigger("sample-queue")] string message, + ILogger logger) + { + logger.LogInformation("Received message from sample-queue, content={content}", message); + } + } + #endregion + + #region Snippet:QueueTriggerFunction_CustomObject + public static class QueueTriggerFunction_CustomObject + { + public class CustomMessage + { + public string Content { get; set; } + } + + [FunctionName("QueueTriggerFunction")] + public static void Run( + [QueueTrigger("sample-queue")] CustomMessage message, + ILogger logger) + { + logger.LogInformation("Received message from sample-queue, content={content}", message.Content); + } + } + #endregion + + #region Snippet:QueueTriggerFunction_JObject + public static class QueueTriggerFunction_JObject + { + [FunctionName("QueueTriggerFunction")] + public static void Run( + [QueueTrigger("sample-queue")] JObject message, + ILogger logger) + { + logger.LogInformation("Received message from sample-queue, content={content}", message["content"]); + } + } + #endregion + + #region Snippet:QueueSenderFunction_String_Return + public static class QueueSenderFunction_String_Return + { + [FunctionName("QueueFunction")] + [return: Queue("sample-queue-2")] + public static string Run( + [QueueTrigger("sample-queue-1")] string message, + ILogger logger) + { + logger.LogInformation("Received message from sample-queue-1, content={content}", message); + logger.LogInformation("Dispatching message to sample-queue-2"); + return message; + } + } + #endregion + + #region Snippet:QueueSenderFunction_CustomObject_OutParamter + public static class QueueSenderFunction_CustomObject_OutParamter + { + public class CustomMessage + { + public string Content { get; set; } + } + + [FunctionName("QueueFunction")] + public static void Run( + [QueueTrigger("sample-queue-1")] CustomMessage incomingMessage, + [Queue("sample-queue-2")] out CustomMessage outgoingMessage, + ILogger logger) + { + logger.LogInformation("Received message from sample-queue-1, content={content}", incomingMessage.Content); + logger.LogInformation("Dispatching message to sample-queue-2"); + outgoingMessage = incomingMessage; + } + } + #endregion + + #region Snippet:QueueSenderFunction_CustomObject_Collector + public static class QueueSenderFunction_CustomObject_Collector + { + public class CustomMessage + { + public string Content { get; set; } + } + + [FunctionName("QueueFunction")] + public static void Run( + [QueueTrigger("sample-queue-1")] CustomMessage incomingMessage, + [Queue("sample-queue-2")] ICollector collector, + ILogger logger) + { + logger.LogInformation("Received message from sample-queue-1, content={content}", incomingMessage.Content); + logger.LogInformation("Dispatching message to sample-queue-2"); + collector.Add(incomingMessage); + } + } + #endregion +} diff --git a/sdk/storage/Azure.Storage.Webjobs.Extensions.Samples/Azure.Webjobs.Extensions.Storage.Samples.csproj b/sdk/storage/Azure.Storage.Webjobs.Extensions.Samples/Azure.Webjobs.Extensions.Storage.Samples.csproj deleted file mode 100644 index 0e1163674f259..0000000000000 --- a/sdk/storage/Azure.Storage.Webjobs.Extensions.Samples/Azure.Webjobs.Extensions.Storage.Samples.csproj +++ /dev/null @@ -1,25 +0,0 @@ - - - - Exe - netcoreapp2.1 - - - - - - - - - - - - - - - - - - - - diff --git a/sdk/storage/Azure.Storage.Webjobs.Extensions.Samples/Filters/ErrorHandlerAttribute.cs b/sdk/storage/Azure.Storage.Webjobs.Extensions.Samples/Filters/ErrorHandlerAttribute.cs deleted file mode 100644 index e5a0b78d7259c..0000000000000 --- a/sdk/storage/Azure.Storage.Webjobs.Extensions.Samples/Filters/ErrorHandlerAttribute.cs +++ /dev/null @@ -1,31 +0,0 @@ -// Copyright (c) Microsoft Corporation. All rights reserved. -// Licensed under the MIT License. - -using System.Threading; -using System.Threading.Tasks; -using Microsoft.Azure.WebJobs.Host; -using Microsoft.Extensions.Logging; - -namespace SampleHost.Filters -{ - /// - /// Sample exception filter that shows how declarative error handling logic - /// can be integrated into the execution pipeline. - /// -#pragma warning disable CS0618 // Type or member is obsolete - public class ErrorHandlerAttribute : FunctionExceptionFilterAttribute -#pragma warning restore CS0618 // Type or member is obsolete - { -#pragma warning disable CS0618 // Type or member is obsolete - public override Task OnExceptionAsync(FunctionExceptionContext exceptionContext, CancellationToken cancellationToken) -#pragma warning restore CS0618 // Type or member is obsolete - { - // custom error handling logic could be written here - // (e.g. write a queue message, send a notification, etc.) - - exceptionContext.Logger.LogError($"ErrorHandler called. Function '{exceptionContext.FunctionName}:{exceptionContext.FunctionInstanceId} failed."); - - return Task.CompletedTask; - } - } -} diff --git a/sdk/storage/Azure.Storage.Webjobs.Extensions.Samples/Filters/WorkItemValidatorAttribute.cs b/sdk/storage/Azure.Storage.Webjobs.Extensions.Samples/Filters/WorkItemValidatorAttribute.cs deleted file mode 100644 index e31d9c0327a90..0000000000000 --- a/sdk/storage/Azure.Storage.Webjobs.Extensions.Samples/Filters/WorkItemValidatorAttribute.cs +++ /dev/null @@ -1,57 +0,0 @@ -// Copyright (c) Microsoft Corporation. All rights reserved. -// Licensed under the MIT License. - -using System.ComponentModel.DataAnnotations; -using System.Linq; -using System.Threading; -using System.Threading.Tasks; -using Microsoft.Azure.WebJobs.Host; -using Microsoft.Extensions.Logging; -using SampleHost.Models; - -namespace SampleHost.Filters -{ - /// - /// Sample invocation filter that demonstrates how declarative validation logic - /// can be integrated into the execution pipeline. - /// -#pragma warning disable CS0618 // Type or member is obsolete - public class WorkItemValidatorAttribute : FunctionInvocationFilterAttribute -#pragma warning restore CS0618 // Type or member is obsolete - { -#pragma warning disable CS0618 // Type or member is obsolete - public override Task OnExecutingAsync(FunctionExecutingContext executingContext, CancellationToken cancellationToken) -#pragma warning restore CS0618 // Type or member is obsolete - { - executingContext.Logger.LogInformation("WorkItemValidator executing..."); - - var workItem = executingContext.Arguments.First().Value as WorkItem; - string errorMessage = null; - if (!TryValidateWorkItem(workItem, out errorMessage)) - { - executingContext.Logger.LogError(errorMessage); - throw new ValidationException(errorMessage); - } - - return base.OnExecutingAsync(executingContext, cancellationToken); - } - - private static bool TryValidateWorkItem(WorkItem workItem, out string errorMessage) - { - errorMessage = null; - - if (string.IsNullOrEmpty(workItem.ID)) - { - errorMessage = "ID cannot be null or empty."; - return false; - } - if (workItem.Priority > 100 || workItem.Priority < 0) - { - errorMessage = "Priority must be between 0 and 100"; - return false; - } - - return true; - } - } -} diff --git a/sdk/storage/Azure.Storage.Webjobs.Extensions.Samples/Functions.cs b/sdk/storage/Azure.Storage.Webjobs.Extensions.Samples/Functions.cs deleted file mode 100644 index e05593d6fed5a..0000000000000 --- a/sdk/storage/Azure.Storage.Webjobs.Extensions.Samples/Functions.cs +++ /dev/null @@ -1,51 +0,0 @@ -// Copyright (c) Microsoft Corporation. All rights reserved. -// Licensed under the MIT License. - -using Microsoft.Azure.WebJobs; -using Microsoft.Extensions.Logging; -using Newtonsoft.Json.Linq; -using SampleHost.Filters; -using SampleHost.Models; - -namespace SampleHost -{ - [ErrorHandler] - public class Functions - { - private readonly ISampleServiceA _sampleServiceA; - private readonly ISampleServiceB _sampleServiceB; - - public Functions(ISampleServiceA sampleServiceA, ISampleServiceB sampleServiceB) - { - _sampleServiceA = sampleServiceA; - _sampleServiceB = sampleServiceB; - } - - [Singleton] - public void BlobTrigger( - [BlobTrigger("test")] string blob, ILogger logger) - { - _sampleServiceB.DoIt(); - logger.LogInformation("Processed blob: " + blob); - } - -#pragma warning disable CA1822 // Mark members as static - public void BlobPoisonBlobHandler( -#pragma warning restore CA1822 // Mark members as static - [QueueTrigger("webjobs-blobtrigger-poison")] JObject blobInfo, ILogger logger) - { - string container = (string)blobInfo["ContainerName"]; - string blobName = (string)blobInfo["BlobName"]; - - logger.LogInformation($"Poison blob: {container}/{blobName}"); - } - - [WorkItemValidator] - public void ProcessWorkItem( - [QueueTrigger("test")] WorkItem workItem, ILogger logger) - { - _sampleServiceA.DoIt(); - logger.LogInformation($"Processed work item {workItem.ID}"); - } - } -} diff --git a/sdk/storage/Azure.Storage.Webjobs.Extensions.Samples/Models/WorkItem.cs b/sdk/storage/Azure.Storage.Webjobs.Extensions.Samples/Models/WorkItem.cs deleted file mode 100644 index 52a44901aed3d..0000000000000 --- a/sdk/storage/Azure.Storage.Webjobs.Extensions.Samples/Models/WorkItem.cs +++ /dev/null @@ -1,14 +0,0 @@ -// Copyright (c) Microsoft Corporation. All rights reserved. -// Licensed under the MIT License. - -namespace SampleHost.Models -{ - public class WorkItem - { - public string ID { get; set; } - public int Priority { get; set; } - public string Region { get; set; } - public int Category { get; set; } - public string Description { get; set; } - } -} diff --git a/sdk/storage/Azure.Storage.Webjobs.Extensions.Samples/Program.cs b/sdk/storage/Azure.Storage.Webjobs.Extensions.Samples/Program.cs deleted file mode 100644 index d81954009b893..0000000000000 --- a/sdk/storage/Azure.Storage.Webjobs.Extensions.Samples/Program.cs +++ /dev/null @@ -1,55 +0,0 @@ -// Copyright (c) Microsoft Corporation. All rights reserved. -// Licensed under the MIT License. - -using System.Threading.Tasks; -using Microsoft.Extensions.Configuration; -using Microsoft.Extensions.DependencyInjection; -using Microsoft.Extensions.Hosting; -using Microsoft.Extensions.Logging; - -namespace SampleHost -{ - public static class Program - { - public static async Task Main(string[] args) - { - var builder = new HostBuilder() - .UseEnvironment("Development") - .ConfigureWebJobs(b => - { - b.AddAzureStorageCoreServices() - .AddAzureStorageBlobs().AddAzureStorageQueues(); - }) - .ConfigureAppConfiguration(b => - { - // Adding command line as a configuration source - b.AddCommandLine(args); - }) - .ConfigureLogging((context, b) => - { - b.SetMinimumLevel(LogLevel.Debug); - b.AddConsole(); - - // If this key exists in any config, use it to enable App Insights - string appInsightsKey = context.Configuration["APPINSIGHTS_INSTRUMENTATIONKEY"]; - if (!string.IsNullOrEmpty(appInsightsKey)) - { - b.AddApplicationInsightsWebJobs(o => o.InstrumentationKey = appInsightsKey); - } - }) - .ConfigureServices(services => - { - // add some sample services to demonstrate job class DI - services.AddSingleton(); - services.AddSingleton(); - }) - .UseConsoleLifetime(); - - var host = builder.Build(); - using (host) - { - await host.RunAsync(); - } - } - } -} diff --git a/sdk/storage/Azure.Storage.Webjobs.Extensions.Samples/Properties/AssemblyInfo.cs b/sdk/storage/Azure.Storage.Webjobs.Extensions.Samples/Properties/AssemblyInfo.cs deleted file mode 100644 index 9ef90ad6a1abc..0000000000000 --- a/sdk/storage/Azure.Storage.Webjobs.Extensions.Samples/Properties/AssemblyInfo.cs +++ /dev/null @@ -1,22 +0,0 @@ -// Copyright (c) Microsoft Corporation. All rights reserved. -// Licensed under the MIT License. - -using System.Reflection; -using System.Runtime.CompilerServices; -using System.Runtime.InteropServices; - - -// Setting ComVisible to false makes the types in this assembly not visible -// to COM components. If you need to access a type in this assembly from -// COM, set the ComVisible attribute to true on that type. -[assembly: ComVisible(false)] - -// The following GUID is for the ID of the typelib if this project is exposed to COM -[assembly: Guid("93429246-cce9-4eb0-b94d-68522862ba79")] - -// Version information for an assembly consists of the following four values: -// -// Major Version -// Minor Version -// Build Number -// Revision diff --git a/sdk/storage/Azure.Storage.Webjobs.Extensions.Samples/Services.cs b/sdk/storage/Azure.Storage.Webjobs.Extensions.Samples/Services.cs deleted file mode 100644 index be44e2d32a1ac..0000000000000 --- a/sdk/storage/Azure.Storage.Webjobs.Extensions.Samples/Services.cs +++ /dev/null @@ -1,54 +0,0 @@ -// Copyright (c) Microsoft Corporation. All rights reserved. -// Licensed under the MIT License. - -using Microsoft.Extensions.Logging; - -namespace SampleHost -{ - /// - /// Sample services used to demonstrate DI capabilities - /// -#pragma warning disable SA1649 // File name should match first type name - public interface ISampleServiceA -#pragma warning restore SA1649 // File name should match first type name - { - void DoIt(); - } - - public class SampleServiceA : ISampleServiceA - { - private readonly ILogger _logger; - - public SampleServiceA(ILogger logger) - { - _logger = logger; - } - - public void DoIt() - { - _logger.LogInformation("SampleServiceA.DoIt invoked!"); - } - } - - public interface ISampleServiceB - { - void DoIt(); - } - -#pragma warning disable SA1402 // File may only contain a single type - public class SampleServiceB : ISampleServiceB -#pragma warning restore SA1402 // File may only contain a single type - { - private readonly ILogger _logger; - - public SampleServiceB(ILogger logger) - { - _logger = logger; - } - - public void DoIt() - { - _logger.LogInformation("SampleServiceB.DoIt invoked!"); - } - } -} diff --git a/sdk/storage/Azure.Storage.Webjobs.Extensions.Samples/appsettings.json b/sdk/storage/Azure.Storage.Webjobs.Extensions.Samples/appsettings.json deleted file mode 100644 index 5188e1eedbacb..0000000000000 --- a/sdk/storage/Azure.Storage.Webjobs.Extensions.Samples/appsettings.json +++ /dev/null @@ -1,4 +0,0 @@ -{ - "ConnectionStrings": { - } -} \ No newline at end of file diff --git a/sdk/storage/Azure.Storage.Webjobs.Extensions.sln b/sdk/storage/Azure.Storage.Webjobs.Extensions.sln index 832c313889dab..ece2a96e33cbd 100644 --- a/sdk/storage/Azure.Storage.Webjobs.Extensions.sln +++ b/sdk/storage/Azure.Storage.Webjobs.Extensions.sln @@ -30,10 +30,12 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Azure.Storage.Common", "Azu EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Azure.WebJobs.Extensions.Storage.Scenario.Tests", "Azure.Storage.Webjobs.Extensions.Scenario.Tests\tests\Azure.WebJobs.Extensions.Storage.Scenario.Tests.csproj", "{F1997C00-7304-481F-8002-7AB0728DB6C1}" EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Azure.Webjobs.Extensions.Storage.Samples", "Azure.Storage.Webjobs.Extensions.Samples\Azure.Webjobs.Extensions.Storage.Samples.csproj", "{0425DF36-E0F1-419A-8B07-E01DE249D066}" -EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.Extensions.Azure", "..\extensions\Microsoft.Extensions.Azure\src\Microsoft.Extensions.Azure.csproj", "{CB002B93-3D25-42DD-B1A6-472C10B99BA7}" EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Azure.WebJobs.Extensions.Storage.Blobs.Samples.Tests", "Azure.Storage.Webjobs.Extensions.Blobs\samples\Azure.WebJobs.Extensions.Storage.Blobs.Samples.Tests.csproj", "{77860E78-4770-4C48-8C2B-AF6B42A8D067}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Azure.WebJobs.Extensions.Storage.Queues.Samples.Tests", "Azure.Storage.Webjobs.Extensions.Queues\samples\Azure.WebJobs.Extensions.Storage.Queues.Samples.Tests.csproj", "{A7A937E4-F4E1-47B7-8D25-AA5FB6BDE010}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -84,14 +86,18 @@ Global {F1997C00-7304-481F-8002-7AB0728DB6C1}.Debug|Any CPU.Build.0 = Debug|Any CPU {F1997C00-7304-481F-8002-7AB0728DB6C1}.Release|Any CPU.ActiveCfg = Release|Any CPU {F1997C00-7304-481F-8002-7AB0728DB6C1}.Release|Any CPU.Build.0 = Release|Any CPU - {0425DF36-E0F1-419A-8B07-E01DE249D066}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {0425DF36-E0F1-419A-8B07-E01DE249D066}.Debug|Any CPU.Build.0 = Debug|Any CPU - {0425DF36-E0F1-419A-8B07-E01DE249D066}.Release|Any CPU.ActiveCfg = Release|Any CPU - {0425DF36-E0F1-419A-8B07-E01DE249D066}.Release|Any CPU.Build.0 = Release|Any CPU {CB002B93-3D25-42DD-B1A6-472C10B99BA7}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {CB002B93-3D25-42DD-B1A6-472C10B99BA7}.Debug|Any CPU.Build.0 = Debug|Any CPU {CB002B93-3D25-42DD-B1A6-472C10B99BA7}.Release|Any CPU.ActiveCfg = Release|Any CPU {CB002B93-3D25-42DD-B1A6-472C10B99BA7}.Release|Any CPU.Build.0 = Release|Any CPU + {77860E78-4770-4C48-8C2B-AF6B42A8D067}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {77860E78-4770-4C48-8C2B-AF6B42A8D067}.Debug|Any CPU.Build.0 = Debug|Any CPU + {77860E78-4770-4C48-8C2B-AF6B42A8D067}.Release|Any CPU.ActiveCfg = Release|Any CPU + {77860E78-4770-4C48-8C2B-AF6B42A8D067}.Release|Any CPU.Build.0 = Release|Any CPU + {A7A937E4-F4E1-47B7-8D25-AA5FB6BDE010}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {A7A937E4-F4E1-47B7-8D25-AA5FB6BDE010}.Debug|Any CPU.Build.0 = Debug|Any CPU + {A7A937E4-F4E1-47B7-8D25-AA5FB6BDE010}.Release|Any CPU.ActiveCfg = Release|Any CPU + {A7A937E4-F4E1-47B7-8D25-AA5FB6BDE010}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE