Skip to content

Commit d45e21e

Browse files
committed
AzureStorage auto create queues
Addendum to #5167
1 parent a4f8c03 commit d45e21e

File tree

33 files changed

+735
-65
lines changed

33 files changed

+735
-65
lines changed

playground/AzureFunctionsEndToEnd/AzureFunctionsEndToEnd.ApiService/Program.cs

Lines changed: 12 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -14,8 +14,8 @@
1414

1515
// Add service defaults & Aspire client integrations.
1616
builder.AddServiceDefaults();
17-
builder.AddAzureQueueClient("queue");
18-
builder.AddAzureBlobClient("blob");
17+
builder.AddAzureQueueClient("queues");
18+
builder.AddAzureBlobClient("blobs");
1919
builder.AddAzureEventHubProducerClient("myhub");
2020
#if !SKIP_UNSTABLE_EMULATORS
2121
builder.AddAzureServiceBusClient("messaging");
@@ -24,10 +24,16 @@
2424

2525
var app = builder.Build();
2626

27+
app.MapGet("/", async (HttpClient client) =>
28+
{
29+
var stream = await client.GetStreamAsync("http://funcapp/api/injected-resources");
30+
return Results.Stream(stream, "application/json");
31+
});
32+
2733
app.MapGet("/publish/asq", async (QueueServiceClient client, CancellationToken cancellationToken) =>
2834
{
29-
var queue = client.GetQueueClient("queue");
30-
await queue.CreateIfNotExistsAsync(cancellationToken: cancellationToken);
35+
var queue = client.GetQueueClient("myqueue1");
36+
3137
var data = Convert.ToBase64String(Encoding.UTF8.GetBytes("Hello, World!"));
3238
await queue.SendMessageAsync(data, cancellationToken: cancellationToken);
3339
return Results.Ok("Message sent to Azure Storage Queue.");
@@ -41,15 +47,14 @@ static string RandomString(int length)
4147

4248
app.MapGet("/publish/blob", async (BlobServiceClient client, CancellationToken cancellationToken, int length = 20) =>
4349
{
44-
var container = client.GetBlobContainerClient("blobs");
45-
await container.CreateIfNotExistsAsync(cancellationToken: cancellationToken);
50+
var container = client.GetBlobContainerClient("myblobcontainer");
4651

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

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

52-
return Results.Ok("String uploaded to Azure Storage Blobs.");
57+
return Results.Ok($"String uploaded to Azure Storage Blobs {container.Uri}.");
5358
});
5459

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

83-
app.MapGet("/", async (HttpClient client) =>
84-
{
85-
var stream = await client.GetStreamAsync("http://funcapp/api/injected-resources");
86-
return Results.Stream(stream, "application/json");
87-
});
88-
8988
app.MapDefaultEndpoints();
9089

9190
app.Run();

playground/AzureFunctionsEndToEnd/AzureFunctionsEndToEnd.AppHost/Program.cs

Lines changed: 12 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,12 @@
11
var builder = DistributedApplication.CreateBuilder(args);
22

33
var storage = builder.AddAzureStorage("storage").RunAsEmulator();
4-
var queue = storage.AddQueues("queue");
5-
var blob = storage.AddBlobs("blob");
6-
var myBlobContainer = blob.AddBlobContainer("myblobcontainer");
4+
5+
var queues = storage.AddQueues("queues");
6+
var myQueue = queues.AddQueue("myqueue1");
7+
8+
var blobs = storage.AddBlobs("blobs");
9+
var myBlobContainer = blobs.AddBlobContainer("myblobcontainer");
710

811
var eventHub = builder.AddAzureEventHubs("eventhubs")
912
.RunAsEmulator()
@@ -22,22 +25,23 @@
2225
var funcApp = builder.AddAzureFunctionsProject<Projects.AzureFunctionsEndToEnd_Functions>("funcapp")
2326
.WithExternalHttpEndpoints()
2427
.WithReference(eventHub).WaitFor(eventHub)
25-
.WithReference(myBlobContainer).WaitFor(myBlobContainer)
2628
#if !SKIP_UNSTABLE_EMULATORS
2729
.WithReference(serviceBus).WaitFor(serviceBus)
2830
.WithReference(cosmosDb).WaitFor(cosmosDb)
2931
#endif
30-
.WithReference(blob)
31-
.WithReference(queue);
32+
.WithReference(blobs)
33+
.WithReference(myBlobContainer).WaitFor(myBlobContainer)
34+
.WithReference(queues)
35+
.WithReference(myQueue).WaitFor(myQueue);
3236

3337
builder.AddProject<Projects.AzureFunctionsEndToEnd_ApiService>("apiservice")
3438
.WithReference(eventHub).WaitFor(eventHub)
3539
#if !SKIP_UNSTABLE_EMULATORS
3640
.WithReference(serviceBus).WaitFor(serviceBus)
3741
.WithReference(cosmosDb).WaitFor(cosmosDb)
3842
#endif
39-
.WithReference(queue)
40-
.WithReference(blob)
43+
.WithReference(queues)
44+
.WithReference(blobs)
4145
.WithReference(funcApp);
4246

4347
builder.Build().Run();

playground/AzureFunctionsEndToEnd/AzureFunctionsEndToEnd.Functions/MyAzureBlobTrigger.cs

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -4,16 +4,16 @@
44

55
namespace AzureFunctionsEndToEnd.Functions;
66

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

16-
logger.LogInformation("C# blob trigger function invoked for 'blobs/{source}' with {message}...", blobName, triggerString);
16+
logger.LogInformation("C# blob trigger function invoked for 'myblobcontainer/{source}' with {message}...", blobName, triggerString);
1717
return triggerString.ToUpper();
1818
}
1919
}

playground/AzureFunctionsEndToEnd/AzureFunctionsEndToEnd.Functions/MyAzureQueueTrigger.cs

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,17 @@
1+
using Azure.Storage.Queues;
12
using Azure.Storage.Queues.Models;
23
using Microsoft.Azure.Functions.Worker;
34
using Microsoft.Extensions.Logging;
45

56
namespace AzureFunctionsEndToEnd.Functions;
67

7-
public class MyAzureQueueTrigger(ILogger<MyAzureQueueTrigger> logger)
8+
public class MyAzureQueueTrigger(QueueClient queueClient, ILogger<MyAzureQueueTrigger> logger)
89
{
910
[Function(nameof(MyAzureQueueTrigger))]
10-
public void Run([QueueTrigger("queue", Connection = "queue")] QueueMessage message)
11+
public void Run([QueueTrigger("myqueue1", Connection = "queues")] QueueMessage message)
1112
{
13+
_ = queueClient.GetProperties();
14+
1215
logger.LogInformation("C# Queue trigger function processed: {Text}", message.MessageText);
1316
}
1417
}

playground/AzureFunctionsEndToEnd/AzureFunctionsEndToEnd.Functions/MyHttpTrigger.cs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ public class MyHttpTrigger(
2222
#endif
2323
EventHubProducerClient eventHubProducerClient,
2424
QueueServiceClient queueServiceClient,
25+
QueueClient queueClient,
2526
BlobServiceClient blobServiceClient,
2627
BlobContainerClient blobContainerClient)
2728
{
@@ -35,6 +36,7 @@ public IResult Run([HttpTrigger(AuthorizationLevel.Anonymous, "get", "post")] Ht
3536
#endif
3637
stringBuilder.AppendLine(CultureInfo.InvariantCulture, $"Aspire-injected EventHubProducerClient namespace: {eventHubProducerClient.FullyQualifiedNamespace}");
3738
stringBuilder.AppendLine(CultureInfo.InvariantCulture, $"Aspire-injected QueueServiceClient URI: {queueServiceClient.Uri}");
39+
stringBuilder.AppendLine(CultureInfo.InvariantCulture, $"Aspire-injected QueueClient URI: {queueClient.Uri}");
3840
stringBuilder.AppendLine(CultureInfo.InvariantCulture, $"Aspire-injected BlobServiceClient URI: {blobServiceClient.Uri}");
3941
stringBuilder.AppendLine(CultureInfo.InvariantCulture, $"Aspire-injected BlobContainerClient URI: {blobContainerClient.Uri}");
4042
return Results.Text(stringBuilder.ToString());

playground/AzureFunctionsEndToEnd/AzureFunctionsEndToEnd.Functions/Program.cs

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,9 +4,13 @@
44
var builder = FunctionsApplication.CreateBuilder(args);
55

66
builder.AddServiceDefaults();
7-
builder.AddAzureQueueClient("queue");
8-
builder.AddAzureBlobClient("blob");
7+
8+
builder.AddAzureQueueClient("queues");
9+
builder.AddAzureQueue("myqueue1");
10+
11+
builder.AddAzureBlobClient("blobs");
912
builder.AddAzureBlobContainerClient("myblobcontainer");
13+
1014
builder.AddAzureEventHubProducerClient("myhub");
1115
#if !SKIP_UNSTABLE_EMULATORS
1216
builder.AddAzureServiceBusClient("messaging");

playground/AzureStorageEndToEnd/AzureStorageEndToEnd.ApiService/Program.cs

Lines changed: 37 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -12,31 +12,61 @@
1212
builder.AddKeyedAzureBlobContainerClient("foocontainer");
1313

1414
builder.AddAzureQueueClient("queues");
15+
builder.AddKeyedAzureQueue("myqueue");
1516

1617
var app = builder.Build();
1718

1819
app.MapDefaultEndpoints();
1920

20-
app.MapGet("/", async (BlobServiceClient bsc, QueueServiceClient qsc, [FromKeyedServices("foocontainer")] BlobContainerClient keyedContainerClient1) =>
21+
app.MapGet("/", (HttpContext context) =>
22+
{
23+
var request = context.Request;
24+
var scheme = request.Scheme;
25+
var host = request.Host;
26+
27+
var endpointDataSource = context.RequestServices.GetRequiredService<EndpointDataSource>();
28+
var urls = endpointDataSource.Endpoints
29+
.OfType<RouteEndpoint>()
30+
.Select(e => $"{scheme}://{host}{e.RoutePattern.RawText}");
31+
32+
var html = "<html><body><ul>" +
33+
string.Join("", urls.Select(url => $"<li><a href=\"{url}\">{url}</a></li>")) +
34+
"</ul></body></html>";
35+
36+
context.Response.ContentType = "text/html";
37+
return context.Response.WriteAsync(html);
38+
});
39+
40+
app.MapGet("/blobs", async (BlobServiceClient bsc, [FromKeyedServices("foocontainer")] BlobContainerClient bcc) =>
2141
{
2242
var blobNames = new List<string>();
2343
var blobNameAndContent = Guid.NewGuid().ToString();
2444

25-
await keyedContainerClient1.UploadBlobAsync(blobNameAndContent, new BinaryData(blobNameAndContent));
45+
await bcc.UploadBlobAsync(blobNameAndContent, new BinaryData(blobNameAndContent));
2646

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

3050
await ReadBlobsAsync(directContainerClient, blobNames);
31-
await ReadBlobsAsync(keyedContainerClient1, blobNames);
32-
33-
var queue = qsc.GetQueueClient("myqueue");
34-
await queue.CreateIfNotExistsAsync();
35-
await queue.SendMessageAsync("Hello, world!");
51+
await ReadBlobsAsync(bcc, blobNames);
3652

3753
return blobNames;
3854
});
3955

56+
app.MapGet("/queues", async (QueueServiceClient qsc, [FromKeyedServices("myqueue")] QueueClient qc) =>
57+
{
58+
const string text = "Hello, World!";
59+
List<string> messages = [$"Sent: {text}"];
60+
61+
var queue = qsc.GetQueueClient("my-queue");
62+
await queue.SendMessageAsync(text);
63+
64+
var msg = await qc.ReceiveMessageAsync();
65+
messages.Add($"Received: {msg.Value.Body}");
66+
67+
return messages;
68+
});
69+
4070
app.Run();
4171

4272
static async Task ReadBlobsAsync(BlobContainerClient containerClient, List<string> output)

playground/AzureStorageEndToEnd/AzureStorageEndToEnd.AppHost/Program.cs

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313
blobs.AddBlobContainer("mycontainer2", blobContainerName: "test-container-2");
1414

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

1718
var storage2 = builder.AddAzureStorage("storage2").RunAsEmulator(container =>
1819
{
@@ -25,7 +26,8 @@
2526
.WithExternalHttpEndpoints()
2627
.WithReference(blobs).WaitFor(blobs)
2728
.WithReference(blobContainer2).WaitFor(blobContainer2)
28-
.WithReference(queues).WaitFor(queues);
29+
.WithReference(queues).WaitFor(queues)
30+
.WithReference(myqueue).WaitFor(myqueue);
2931

3032
#if !SKIP_DASHBOARD_REFERENCE
3133
// This project is only added in playground projects to support development/debugging

src/Aspire.Hosting.Azure.Storage/Aspire.Hosting.Azure.Storage.csproj

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,11 @@
1414

1515
<ItemGroup>
1616
<ProjectReference Include="..\Aspire.Hosting.Azure\Aspire.Hosting.Azure.csproj" />
17+
</ItemGroup>
18+
19+
<ItemGroup>
1720
<PackageReference Include="AspNetCore.HealthChecks.Azure.Storage.Blobs" />
21+
<PackageReference Include="AspNetCore.HealthChecks.Azure.Storage.Queues" />
1822
<PackageReference Include="Azure.Provisioning" />
1923
<PackageReference Include="Azure.Provisioning.Storage" />
2024
</ItemGroup>
Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
// Licensed to the .NET Foundation under one or more agreements.
2+
// The .NET Foundation licenses this file to you under the MIT license.
3+
4+
using System.Diagnostics.CodeAnalysis;
5+
using System.Runtime.CompilerServices;
6+
using Aspire.Hosting.ApplicationModel;
7+
using Aspire.Hosting.Azure;
8+
using Azure.Provisioning;
9+
10+
namespace Aspire.Hosting;
11+
12+
/// <summary>
13+
/// A resource that represents an Azure Blob Storage queue.
14+
/// </summary>
15+
/// <param name="name">The name of the resource.</param>
16+
/// <param name="queueName">The name of the queue.</param>
17+
/// <param name="parent">The <see cref="AzureQueueStorageResource"/> that the resource is stored in.</param>
18+
public class AzureQueueStorageQueueResource(string name, string queueName, AzureQueueStorageResource parent) : Resource(name),
19+
IResourceWithConnectionString,
20+
IResourceWithParent<AzureQueueStorageResource>
21+
{
22+
/// <summary>
23+
/// Gets the queue name.
24+
/// </summary>
25+
public string QueueName { get; } = ThrowIfNullOrEmpty(queueName);
26+
27+
/// <summary>
28+
/// Gets the connection string template for the manifest for the Azure Blob Storage container resource.
29+
/// </summary>
30+
public ReferenceExpression ConnectionStringExpression => Parent.GetConnectionString(QueueName);
31+
32+
/// <summary>
33+
/// Gets the parent <see cref="AzureQueueStorageResource"/> of this <see cref="AzureQueueStorageQueueResource"/>.
34+
/// </summary>
35+
public AzureQueueStorageResource Parent => parent ?? throw new ArgumentNullException(nameof(parent));
36+
37+
/// <summary>
38+
/// Converts the current instance to a provisioning entity.
39+
/// </summary>
40+
/// <returns>A <see cref="global::Azure.Provisioning.Storage.StorageQueue"/> instance.</returns>
41+
internal global::Azure.Provisioning.Storage.StorageQueue ToProvisioningEntity()
42+
{
43+
global::Azure.Provisioning.Storage.StorageQueue queue = new(Infrastructure.NormalizeBicepIdentifier(Name))
44+
{
45+
Name = QueueName
46+
};
47+
48+
return queue;
49+
}
50+
51+
private static string ThrowIfNullOrEmpty([NotNull] string? argument, [CallerArgumentExpression(nameof(argument))] string? paramName = null)
52+
{
53+
ArgumentException.ThrowIfNullOrEmpty(argument, paramName);
54+
return argument;
55+
}
56+
}

0 commit comments

Comments
 (0)