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

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions Directory.Packages.props
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
<ManagePackageVersionsCentrally>true</ManagePackageVersionsCentrally>
<CentralPackageTransitivePinningEnabled>true</CentralPackageTransitivePinningEnabled>
<AzureProvisiongVersion>1.0.0</AzureProvisiongVersion>
<AzureProvisiongStorageVersion>1.0.1</AzureProvisiongStorageVersion>
<!-- The Npgsql version used when using Npgsql EF Core on net8. The major versions need to match between Npgsql and EF Core. -->
<Npgsql8Version>8.0.6</Npgsql8Version>
</PropertyGroup>
Expand Down Expand Up @@ -49,7 +50,7 @@
<PackageVersion Include="Azure.Provisioning.ServiceBus" Version="$(AzureProvisiongVersion)" />
<PackageVersion Include="Azure.Provisioning.SignalR" Version="$(AzureProvisiongVersion)" />
<PackageVersion Include="Azure.Provisioning.Sql" Version="$(AzureProvisiongVersion)" />
<PackageVersion Include="Azure.Provisioning.Storage" Version="$(AzureProvisiongVersion)" />
<PackageVersion Include="Azure.Provisioning.Storage" Version="$(AzureProvisiongStorageVersion)" />
<PackageVersion Include="Azure.Provisioning.WebPubSub" Version="$(AzureProvisiongVersion)" />
<PackageVersion Include="Azure.ResourceManager.Authorization" Version="1.1.4" />
<PackageVersion Include="Azure.ResourceManager.KeyVault" Version="1.3.2" />
Expand Down Expand Up @@ -146,7 +147,6 @@
<PackageVersion Include="Microsoft.Azure.Functions.Worker.Sdk" Version="2.0.2" />
<PackageVersion Include="Microsoft.ApplicationInsights.WorkerService" Version="2.23.0" />
<!-- Pinned versions for Component Governance - Remove when root dependencies are updated -->
<PackageVersion Include="Azure.Core" Version="1.46.0" />
<PackageVersion Include="Azure.Identity" Version="1.13.2" />
<!-- https://github.com/Azure/azure-cosmos-dotnet-v3/pull/3313 -->
<PackageVersion Include="Newtonsoft.Json" Version="13.0.3" />
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,8 +14,8 @@

// Add service defaults & Aspire client integrations.
builder.AddServiceDefaults();
builder.AddAzureQueueClient("queue");
builder.AddAzureBlobClient("blob");
builder.AddAzureQueueClient("queues");
builder.AddAzureBlobClient("blobs");
builder.AddAzureEventHubProducerClient("myhub");
#if !SKIP_UNSTABLE_EMULATORS
builder.AddAzureServiceBusClient("messaging");
Expand All @@ -24,10 +24,16 @@

var app = builder.Build();

app.MapGet("/", async (HttpClient client) =>
{
var stream = await client.GetStreamAsync("http://funcapp/api/injected-resources");
return Results.Stream(stream, "application/json");
});

app.MapGet("/publish/asq", async (QueueServiceClient client, CancellationToken cancellationToken) =>
{
var queue = client.GetQueueClient("queue");
await queue.CreateIfNotExistsAsync(cancellationToken: cancellationToken);
var queue = client.GetQueueClient("myqueue1");

var data = Convert.ToBase64String(Encoding.UTF8.GetBytes("Hello, World!"));
await queue.SendMessageAsync(data, cancellationToken: cancellationToken);
return Results.Ok("Message sent to Azure Storage Queue.");
Expand All @@ -41,15 +47,14 @@ static string RandomString(int length)

app.MapGet("/publish/blob", async (BlobServiceClient client, CancellationToken cancellationToken, int length = 20) =>
{
var container = client.GetBlobContainerClient("blobs");
await container.CreateIfNotExistsAsync(cancellationToken: cancellationToken);
var container = client.GetBlobContainerClient("myblobcontainer");

var entry = new { Id = Guid.NewGuid(), Text = RandomString(length) };
var blob = container.GetBlobClient(entry.Id.ToString());

await blob.UploadAsync(new BinaryData(entry));

return Results.Ok("String uploaded to Azure Storage Blobs.");
return Results.Ok($"String uploaded to Azure Storage Blobs {container.Uri}.");
});

app.MapGet("/publish/eventhubs", async (EventHubProducerClient client, CancellationToken cancellationToken, int length = 20) =>
Expand Down Expand Up @@ -80,12 +85,6 @@ static string RandomString(int length)
});
#endif

app.MapGet("/", async (HttpClient client) =>
{
var stream = await client.GetStreamAsync("http://funcapp/api/injected-resources");
return Results.Stream(stream, "application/json");
});

app.MapDefaultEndpoints();

app.Run();
Expand Down
Original file line number Diff line number Diff line change
@@ -1,9 +1,12 @@
var builder = DistributedApplication.CreateBuilder(args);

var storage = builder.AddAzureStorage("storage").RunAsEmulator();
var queue = storage.AddQueues("queue");
var blob = storage.AddBlobs("blob");
var myBlobContainer = blob.AddBlobContainer("myblobcontainer");

var queues = storage.AddQueues("queues");
var myQueue = queues.AddQueue("myqueue1");

var blobs = storage.AddBlobs("blobs");
var myBlobContainer = blobs.AddBlobContainer("myblobcontainer");

var eventHub = builder.AddAzureEventHubs("eventhubs")
.RunAsEmulator()
Expand All @@ -22,22 +25,23 @@
var funcApp = builder.AddAzureFunctionsProject<Projects.AzureFunctionsEndToEnd_Functions>("funcapp")
.WithExternalHttpEndpoints()
.WithReference(eventHub).WaitFor(eventHub)
.WithReference(myBlobContainer).WaitFor(myBlobContainer)
#if !SKIP_UNSTABLE_EMULATORS
.WithReference(serviceBus).WaitFor(serviceBus)
.WithReference(cosmosDb).WaitFor(cosmosDb)
#endif
.WithReference(blob)
.WithReference(queue);
.WithReference(blobs)
.WithReference(myBlobContainer).WaitFor(myBlobContainer)
.WithReference(queues)
.WithReference(myQueue).WaitFor(myQueue);

builder.AddProject<Projects.AzureFunctionsEndToEnd_ApiService>("apiservice")
.WithReference(eventHub).WaitFor(eventHub)
#if !SKIP_UNSTABLE_EMULATORS
.WithReference(serviceBus).WaitFor(serviceBus)
.WithReference(cosmosDb).WaitFor(cosmosDb)
#endif
.WithReference(queue)
.WithReference(blob)
.WithReference(queues)
.WithReference(blobs)
.WithReference(funcApp);

builder.Build().Run();
Original file line number Diff line number Diff line change
Expand Up @@ -4,16 +4,16 @@

namespace AzureFunctionsEndToEnd.Functions;

public class MyAzureBlobTrigger(ILogger<MyAzureBlobTrigger> logger, BlobContainerClient containerClient)
public class MyAzureBlobTrigger(BlobContainerClient containerClient, ILogger<MyAzureBlobTrigger> logger)
{
[Function(nameof(MyAzureBlobTrigger))]
[BlobOutput("test-files/{name}.txt", Connection = "blob")]
public async Task<string> RunAsync([BlobTrigger("blobs/{name}", Connection = "blob")] string triggerString, FunctionContext context)
[BlobOutput("test-files/{name}.txt", Connection = "blobs")]
public async Task<string> RunAsync([BlobTrigger("myblobcontainer/{name}", Connection = "blobs")] string triggerString, FunctionContext context)
{
var blobName = (string)context.BindingContext.BindingData["name"]!;
await containerClient.UploadBlobAsync(blobName, new BinaryData(triggerString));
_ = await containerClient.GetAccountInfoAsync();

logger.LogInformation("C# blob trigger function invoked for 'blobs/{source}' with {message}...", blobName, triggerString);
logger.LogInformation("C# blob trigger function invoked for 'myblobcontainer/{source}' with {message}...", blobName, triggerString);
return triggerString.ToUpper();
}
}
Original file line number Diff line number Diff line change
@@ -1,14 +1,17 @@
using Azure.Storage.Queues;
using Azure.Storage.Queues.Models;
using Microsoft.Azure.Functions.Worker;
using Microsoft.Extensions.Logging;

namespace AzureFunctionsEndToEnd.Functions;

public class MyAzureQueueTrigger(ILogger<MyAzureQueueTrigger> logger)
public class MyAzureQueueTrigger(QueueClient queueClient, ILogger<MyAzureQueueTrigger> logger)
{
[Function(nameof(MyAzureQueueTrigger))]
public void Run([QueueTrigger("queue", Connection = "queue")] QueueMessage message)
public void Run([QueueTrigger("myqueue1", Connection = "queues")] QueueMessage message)
{
_ = queueClient.GetProperties();

logger.LogInformation("C# Queue trigger function processed: {Text}", message.MessageText);
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ public class MyHttpTrigger(
#endif
EventHubProducerClient eventHubProducerClient,
QueueServiceClient queueServiceClient,
QueueClient queueClient,
BlobServiceClient blobServiceClient,
BlobContainerClient blobContainerClient)
{
Expand All @@ -35,6 +36,7 @@ public IResult Run([HttpTrigger(AuthorizationLevel.Anonymous, "get", "post")] Ht
#endif
stringBuilder.AppendLine(CultureInfo.InvariantCulture, $"Aspire-injected EventHubProducerClient namespace: {eventHubProducerClient.FullyQualifiedNamespace}");
stringBuilder.AppendLine(CultureInfo.InvariantCulture, $"Aspire-injected QueueServiceClient URI: {queueServiceClient.Uri}");
stringBuilder.AppendLine(CultureInfo.InvariantCulture, $"Aspire-injected QueueClient URI: {queueClient.Uri}");
stringBuilder.AppendLine(CultureInfo.InvariantCulture, $"Aspire-injected BlobServiceClient URI: {blobServiceClient.Uri}");
stringBuilder.AppendLine(CultureInfo.InvariantCulture, $"Aspire-injected BlobContainerClient URI: {blobContainerClient.Uri}");
return Results.Text(stringBuilder.ToString());
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,13 @@
var builder = FunctionsApplication.CreateBuilder(args);

builder.AddServiceDefaults();
builder.AddAzureQueueClient("queue");
builder.AddAzureBlobClient("blob");

builder.AddAzureQueueClient("queues");
builder.AddAzureQueue("myqueue1");

builder.AddAzureBlobClient("blobs");
builder.AddAzureBlobContainerClient("myblobcontainer");

builder.AddAzureEventHubProducerClient("myhub");
#if !SKIP_UNSTABLE_EMULATORS
builder.AddAzureServiceBusClient("messaging");
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,31 +12,61 @@
builder.AddKeyedAzureBlobContainerClient("foocontainer");

builder.AddAzureQueueClient("queues");
builder.AddKeyedAzureQueue("myqueue");

var app = builder.Build();

app.MapDefaultEndpoints();

app.MapGet("/", async (BlobServiceClient bsc, QueueServiceClient qsc, [FromKeyedServices("foocontainer")] BlobContainerClient keyedContainerClient1) =>
app.MapGet("/", (HttpContext context) =>
{
var request = context.Request;
var scheme = request.Scheme;
var host = request.Host;

var endpointDataSource = context.RequestServices.GetRequiredService<EndpointDataSource>();
var urls = endpointDataSource.Endpoints
.OfType<RouteEndpoint>()
.Select(e => $"{scheme}://{host}{e.RoutePattern.RawText}");

var html = "<html><body><ul>" +
string.Join("", urls.Select(url => $"<li><a href=\"{url}\">{url}</a></li>")) +
"</ul></body></html>";

context.Response.ContentType = "text/html";
return context.Response.WriteAsync(html);
});

app.MapGet("/blobs", async (BlobServiceClient bsc, [FromKeyedServices("foocontainer")] BlobContainerClient bcc) =>
{
var blobNames = new List<string>();
var blobNameAndContent = Guid.NewGuid().ToString();

await keyedContainerClient1.UploadBlobAsync(blobNameAndContent, new BinaryData(blobNameAndContent));
await bcc.UploadBlobAsync(blobNameAndContent, new BinaryData(blobNameAndContent));

var directContainerClient = bsc.GetBlobContainerClient(blobContainerName: "test-container-1");
await directContainerClient.UploadBlobAsync(blobNameAndContent, new BinaryData(blobNameAndContent));

await ReadBlobsAsync(directContainerClient, blobNames);
await ReadBlobsAsync(keyedContainerClient1, blobNames);

var queue = qsc.GetQueueClient("myqueue");
await queue.CreateIfNotExistsAsync();
await queue.SendMessageAsync("Hello, world!");
await ReadBlobsAsync(bcc, blobNames);

return blobNames;
});

app.MapGet("/queues", async (QueueServiceClient qsc, [FromKeyedServices("myqueue")] QueueClient qc) =>
{
const string text = "Hello, World!";
List<string> messages = [$"Sent: {text}"];

var queue = qsc.GetQueueClient("my-queue");
await queue.SendMessageAsync(text);

var msg = await qc.ReceiveMessageAsync();
messages.Add($"Received: {msg.Value.Body}");

return messages;
});

app.Run();

static async Task ReadBlobsAsync(BlobContainerClient containerClient, List<string> output)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
blobs.AddBlobContainer("mycontainer2", blobContainerName: "test-container-2");

var queues = storage.AddQueues("queues");
var myqueue = queues.AddQueue("myqueue", queueName: "my-queue");

var storage2 = builder.AddAzureStorage("storage2").RunAsEmulator(container =>
{
Expand All @@ -25,7 +26,8 @@
.WithExternalHttpEndpoints()
.WithReference(blobs).WaitFor(blobs)
.WithReference(blobContainer2).WaitFor(blobContainer2)
.WithReference(queues).WaitFor(queues);
.WithReference(queues).WaitFor(queues)
.WithReference(myqueue).WaitFor(myqueue);

#if !SKIP_DASHBOARD_REFERENCE
// This project is only added in playground projects to support development/debugging
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,11 @@

<ItemGroup>
<ProjectReference Include="..\Aspire.Hosting.Azure\Aspire.Hosting.Azure.csproj" />
</ItemGroup>

<ItemGroup>
<PackageReference Include="AspNetCore.HealthChecks.Azure.Storage.Blobs" />
<PackageReference Include="AspNetCore.HealthChecks.Azure.Storage.Queues" />
<PackageReference Include="Azure.Provisioning" />
<PackageReference Include="Azure.Provisioning.Storage" />
</ItemGroup>
Expand Down
56 changes: 56 additions & 0 deletions src/Aspire.Hosting.Azure.Storage/AzureQueueStorageQueueResource.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.

using System.Diagnostics.CodeAnalysis;
using System.Runtime.CompilerServices;
using Aspire.Hosting.ApplicationModel;
using Aspire.Hosting.Azure;
using Azure.Provisioning;

namespace Aspire.Hosting;

/// <summary>
/// A resource that represents an Azure Storage queue.
/// </summary>
/// <param name="name">The name of the resource.</param>
/// <param name="queueName">The name of the queue.</param>
/// <param name="parent">The <see cref="AzureQueueStorageResource"/> that the resource is stored in.</param>
public class AzureQueueStorageQueueResource(string name, string queueName, AzureQueueStorageResource parent) : Resource(name),
IResourceWithConnectionString,
IResourceWithParent<AzureQueueStorageResource>
{
/// <summary>
/// Gets the queue name.
/// </summary>
public string QueueName { get; } = ThrowIfNullOrEmpty(queueName);

/// <summary>
/// Gets the connection string template for the manifest for the Azure Storage queue resource.
/// </summary>
public ReferenceExpression ConnectionStringExpression => Parent.GetConnectionString(QueueName);

/// <summary>
/// Gets the parent <see cref="AzureQueueStorageResource"/> of this <see cref="AzureQueueStorageQueueResource"/>.
/// </summary>
public AzureQueueStorageResource Parent => parent ?? throw new ArgumentNullException(nameof(parent));

/// <summary>
/// Converts the current instance to a provisioning entity.
/// </summary>
/// <returns>A <see cref="global::Azure.Provisioning.Storage.StorageQueue"/> instance.</returns>
internal global::Azure.Provisioning.Storage.StorageQueue ToProvisioningEntity()
{
global::Azure.Provisioning.Storage.StorageQueue queue = new(Infrastructure.NormalizeBicepIdentifier(Name))
{
Name = QueueName
};

return queue;
}

private static string ThrowIfNullOrEmpty([NotNull] string? argument, [CallerArgumentExpression(nameof(argument))] string? paramName = null)
{
ArgumentException.ThrowIfNullOrEmpty(argument, paramName);
return argument;
}
}
Loading