Skip to content

Commit 01922e7

Browse files
committed
First cut of IResourceWithAzureFunctionsConfig and removed WithReference
- We now scan all of the env variables and resolve references instead.
1 parent c3b4b4a commit 01922e7

File tree

11 files changed

+104
-87
lines changed

11 files changed

+104
-87
lines changed

src/Aspire.Hosting.Azure.EventHubs/AzureEventHubsResource.cs

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,8 @@ namespace Aspire.Hosting.Azure;
1616
public class AzureEventHubsResource(string name, Action<ResourceModuleConstruct> configureConstruct) :
1717
AzureConstructResource(name, configureConstruct),
1818
IResourceWithConnectionString,
19-
IResourceWithEndpoints
19+
IResourceWithEndpoints,
20+
IResourceWithAzureFunctionsConfig
2021
{
2122
internal List<(string Name, Action<IResourceBuilder<AzureEventHubsResource>, ResourceModuleConstruct, EventHub>? Configure)> Hubs { get; } = [];
2223

@@ -39,4 +40,16 @@ public class AzureEventHubsResource(string name, Action<ResourceModuleConstruct>
3940
IsEmulator
4041
? ReferenceExpression.Create($"Endpoint=sb://{EmulatorEndpoint.Property(EndpointProperty.Host)}:{EmulatorEndpoint.Property(EndpointProperty.Port)};SharedAccessKeyName=RootManageSharedAccessKey;SharedAccessKey=SAS_KEY_VALUE;UseDevelopmentEmulator=true;")
4142
: ReferenceExpression.Create($"{EventHubsEndpoint}");
43+
44+
void IResourceWithAzureFunctionsConfig.ApplyAzureFunctionsConfiguration(IDictionary<string, object> target, string connectionName)
45+
{
46+
if (IsEmulator)
47+
{
48+
target[connectionName] = ConnectionStringExpression;
49+
}
50+
else
51+
{
52+
target[$"{connectionName}__fullyQualifiedNamespace"] = EventHubsEndpoint;
53+
}
54+
}
4255
}

src/Aspire.Hosting.Azure.Functions/Aspire.Hosting.Azure.Functions.csproj

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,6 @@
1313
</PropertyGroup>
1414

1515
<ItemGroup>
16-
<ProjectReference Include="..\Aspire.Hosting.Azure.EventHubs\Aspire.Hosting.Azure.EventHubs.csproj" />
1716
<ProjectReference Include="..\Aspire.Hosting.Azure.Storage\Aspire.Hosting.Azure.Storage.csproj" />
1817
<ProjectReference Include="..\Aspire.Hosting.Azure\Aspire.Hosting.Azure.csproj" />
1918
</ItemGroup>

src/Aspire.Hosting.Azure.Functions/AzureFunctionsProjectResourceExtensions.cs

Lines changed: 30 additions & 79 deletions
Original file line numberDiff line numberDiff line change
@@ -37,8 +37,36 @@ public static class AzureFunctionsProjectResourceExtensions
3737
if (item.HostStorage == storage)
3838
{
3939
removeStorage = false;
40-
break;
4140
}
41+
42+
// Before the resource starts, we want to apply the azure functions specific environment variables.
43+
// we look at all of the environment variables and apply the configuration for any resources that implement IResourceWithAzureFunctionsConfig.
44+
item.Annotations.Add(new EnvironmentCallbackAnnotation(static context =>
45+
{
46+
var functionsConfigMapping = new Dictionary<string, IResourceWithAzureFunctionsConfig>();
47+
48+
foreach (var (_, val) in context.EnvironmentVariables)
49+
{
50+
var (name, config) = val switch
51+
{
52+
IResourceWithAzureFunctionsConfig c => (c.Name, c),
53+
ConnectionStringReference conn when conn.Resource is IResourceWithAzureFunctionsConfig c => (conn.ConnectionName ?? c.Name, c),
54+
_ => ("", null)
55+
};
56+
57+
if (config is not null)
58+
{
59+
functionsConfigMapping[name] = config;
60+
}
61+
}
62+
63+
foreach (var (name, config) in functionsConfigMapping)
64+
{
65+
config.ApplyAzureFunctionsConfiguration(context.EnvironmentVariables, name);
66+
}
67+
68+
return Task.CompletedTask;
69+
}));
4270
}
4371

4472
if (removeStorage)
@@ -52,7 +80,7 @@ public static class AzureFunctionsProjectResourceExtensions
5280

5381
resource.HostStorage = storage;
5482

55-
var functionsBuilder = builder.AddResource(resource)
83+
return builder.AddResource(resource)
5684
.WithArgs(context =>
5785
{
5886
var http = resource.GetEndpoint("http");
@@ -104,8 +132,6 @@ public static class AzureFunctionsProjectResourceExtensions
104132

105133
context.Writer.WriteEndObject();
106134
});
107-
108-
return functionsBuilder;
109135
}
110136

111137
/// <summary>
@@ -119,79 +145,4 @@ public static IResourceBuilder<AzureFunctionsProjectResource> WithHostStorage(th
119145
builder.Resource.HostStorage = storage.Resource;
120146
return builder;
121147
}
122-
123-
/// <remarks>
124-
/// This implementation demonstrates the approach of "teaching Aspire about Functions" where we take advantage of
125-
/// Aspire's ConnectionStringExpression to map to the connection strings format that Azure Functions understands.
126-
/// An alternative approach here is to "teach Functions about Aspire" where integrate the Aspire configuration
127-
/// model into the Azure Functions worker extensions.
128-
/// </remarks>
129-
#pragma warning disable RS0026 // Do not add multiple public overloads with optional parameters
130-
public static IResourceBuilder<AzureFunctionsProjectResource> WithReference(this IResourceBuilder<AzureFunctionsProjectResource> builder, IResourceBuilder<AzureQueueStorageResource> source, string? connectionName = null)
131-
#pragma warning restore RS0026 // Do not add multiple public overloads with optional parameters
132-
{
133-
return builder.WithEnvironment(context =>
134-
{
135-
connectionName ??= source.Resource.Name;
136-
if (source.Resource.Parent.IsEmulator)
137-
{
138-
context.EnvironmentVariables[connectionName] = source.Resource.Parent.GetEmulatorConnectionString();
139-
}
140-
else
141-
{
142-
context.EnvironmentVariables[$"{connectionName}__queueServiceUri"] = source.Resource.ConnectionStringExpression;
143-
}
144-
});
145-
}
146-
147-
/// <summary>
148-
///
149-
/// </summary>
150-
/// <param name="builder"></param>
151-
/// <param name="source"></param>
152-
/// <param name="connectionName"></param>
153-
/// <returns></returns>
154-
#pragma warning disable RS0026 // Do not add multiple public overloads with optional parameters
155-
public static IResourceBuilder<AzureFunctionsProjectResource> WithReference(this IResourceBuilder<AzureFunctionsProjectResource> builder, IResourceBuilder<AzureBlobStorageResource> source, string? connectionName = null)
156-
#pragma warning restore RS0026 // Do not add multiple public overloads with optional parameters
157-
{
158-
return builder.WithEnvironment(context =>
159-
{
160-
connectionName ??= source.Resource.Name;
161-
162-
if (source.Resource.Parent.IsEmulator)
163-
{
164-
context.EnvironmentVariables[connectionName] = source.Resource.Parent.GetEmulatorConnectionString();
165-
}
166-
else
167-
{
168-
context.EnvironmentVariables[$"{connectionName}__blobServiceUri"] = source.Resource.ConnectionStringExpression;
169-
}
170-
});
171-
}
172-
173-
/// <summary>
174-
///
175-
/// </summary>
176-
/// <param name="builder"></param>
177-
/// <param name="source"></param>
178-
/// <param name="connectionName"></param>
179-
/// <returns></returns>
180-
#pragma warning disable RS0026 // Do not add multiple public overloads with optional parameters
181-
public static IResourceBuilder<AzureFunctionsProjectResource> WithReference(this IResourceBuilder<AzureFunctionsProjectResource> builder, IResourceBuilder<AzureEventHubsResource> source, string? connectionName = null)
182-
#pragma warning restore RS0026 // Do not add multiple public overloads with optional parameters
183-
{
184-
return builder.WithEnvironment(context =>
185-
{
186-
connectionName ??= source.Resource.Name;
187-
if (source.Resource.IsEmulator)
188-
{
189-
context.EnvironmentVariables[connectionName] = source.Resource.ConnectionStringExpression;
190-
}
191-
else
192-
{
193-
context.EnvironmentVariables[$"{connectionName}__fullyQualifiedNamespace"] = source.Resource.ConnectionStringExpression;
194-
}
195-
});
196-
}
197148
}

src/Aspire.Hosting.Azure.Functions/PublicAPI.Unshipped.txt

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,3 @@ Aspire.Hosting.Azure.AzureFunctionsProjectResource.AzureFunctionsProjectResource
44
Aspire.Hosting.Azure.AzureFunctionsProjectResourceExtensions
55
static Aspire.Hosting.Azure.AzureFunctionsProjectResourceExtensions.AddAzureFunctionsProject<TProject>(this Aspire.Hosting.IDistributedApplicationBuilder! builder, string! name) -> Aspire.Hosting.ApplicationModel.IResourceBuilder<Aspire.Hosting.Azure.AzureFunctionsProjectResource!>!
66
static Aspire.Hosting.Azure.AzureFunctionsProjectResourceExtensions.WithHostStorage(this Aspire.Hosting.ApplicationModel.IResourceBuilder<Aspire.Hosting.Azure.AzureFunctionsProjectResource!>! builder, Aspire.Hosting.ApplicationModel.IResourceBuilder<Aspire.Hosting.Azure.AzureStorageResource!>! storage) -> Aspire.Hosting.ApplicationModel.IResourceBuilder<Aspire.Hosting.Azure.AzureFunctionsProjectResource!>!
7-
static Aspire.Hosting.Azure.AzureFunctionsProjectResourceExtensions.WithReference(this Aspire.Hosting.ApplicationModel.IResourceBuilder<Aspire.Hosting.Azure.AzureFunctionsProjectResource!>! builder, Aspire.Hosting.ApplicationModel.IResourceBuilder<Aspire.Hosting.Azure.AzureBlobStorageResource!>! source, string? connectionName = null) -> Aspire.Hosting.ApplicationModel.IResourceBuilder<Aspire.Hosting.Azure.AzureFunctionsProjectResource!>!
8-
static Aspire.Hosting.Azure.AzureFunctionsProjectResourceExtensions.WithReference(this Aspire.Hosting.ApplicationModel.IResourceBuilder<Aspire.Hosting.Azure.AzureFunctionsProjectResource!>! builder, Aspire.Hosting.ApplicationModel.IResourceBuilder<Aspire.Hosting.Azure.AzureEventHubsResource!>! source, string? connectionName = null) -> Aspire.Hosting.ApplicationModel.IResourceBuilder<Aspire.Hosting.Azure.AzureFunctionsProjectResource!>!
9-
static Aspire.Hosting.Azure.AzureFunctionsProjectResourceExtensions.WithReference(this Aspire.Hosting.ApplicationModel.IResourceBuilder<Aspire.Hosting.Azure.AzureFunctionsProjectResource!>! builder, Aspire.Hosting.ApplicationModel.IResourceBuilder<Aspire.Hosting.Azure.AzureQueueStorageResource!>! source, string? connectionName = null) -> Aspire.Hosting.ApplicationModel.IResourceBuilder<Aspire.Hosting.Azure.AzureFunctionsProjectResource!>!

src/Aspire.Hosting.Azure.Storage/AzureBlobStorageResource.cs

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,8 @@ namespace Aspire.Hosting.Azure;
1212
/// <param name="storage">The <see cref="AzureStorageResource"/> that the resource is stored in.</param>
1313
public class AzureBlobStorageResource(string name, AzureStorageResource storage) : Resource(name),
1414
IResourceWithConnectionString,
15-
IResourceWithParent<AzureStorageResource>
15+
IResourceWithParent<AzureStorageResource>,
16+
IResourceWithAzureFunctionsConfig
1617
{
1718
/// <summary>
1819
/// Gets the parent AzureStorageResource of this AzureBlobStorageResource.
@@ -24,4 +25,16 @@ public class AzureBlobStorageResource(string name, AzureStorageResource storage)
2425
/// </summary>
2526
public ReferenceExpression ConnectionStringExpression =>
2627
Parent.GetBlobConnectionString();
28+
29+
void IResourceWithAzureFunctionsConfig.ApplyAzureFunctionsConfiguration(IDictionary<string, object> target, string connectionName)
30+
{
31+
if (Parent.IsEmulator)
32+
{
33+
target[connectionName] = Parent.GetEmulatorConnectionString();
34+
}
35+
else
36+
{
37+
target[$"{connectionName}__blobServiceUri"] = Parent.BlobEndpoint;
38+
}
39+
}
2740
}

src/Aspire.Hosting.Azure.Storage/AzureQueueStorageResource.cs

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,8 @@ namespace Aspire.Hosting.Azure;
1212
/// <param name="storage">The <see cref="AzureStorageResource"/> that the resource is stored in.</param>
1313
public class AzureQueueStorageResource(string name, AzureStorageResource storage) : Resource(name),
1414
IResourceWithConnectionString,
15-
IResourceWithParent<AzureStorageResource>
15+
IResourceWithParent<AzureStorageResource>,
16+
IResourceWithAzureFunctionsConfig
1617
{
1718
/// <summary>
1819
/// Gets the parent AzureStorageResource of this AzureQueueStorageResource.
@@ -24,4 +25,16 @@ public class AzureQueueStorageResource(string name, AzureStorageResource storage
2425
/// </summary>
2526
public ReferenceExpression ConnectionStringExpression =>
2627
Parent.GetQueueConnectionString();
28+
29+
void IResourceWithAzureFunctionsConfig.ApplyAzureFunctionsConfiguration(IDictionary<string, object> target, string connectionName)
30+
{
31+
if (Parent.IsEmulator)
32+
{
33+
target[connectionName] = Parent.GetEmulatorConnectionString();
34+
}
35+
else
36+
{
37+
target[$"{connectionName}__queueServiceUri"] = Parent.QueueEndpoint;
38+
}
39+
}
2740
}
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
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 Aspire.Hosting.ApplicationModel;
5+
6+
namespace Aspire.Hosting.Azure;
7+
8+
/// <summary>
9+
/// Represents an resource that can provide configuration for Azure Functions.
10+
/// </summary>
11+
public interface IResourceWithAzureFunctionsConfig : IResource
12+
{
13+
/// <summary>
14+
/// Applies the Azure Functions configuration to the target dictionary.
15+
/// </summary>
16+
/// <param name="target"></param>
17+
/// <param name="connectionName"></param>
18+
void ApplyAzureFunctionsConfiguration(IDictionary<string, object> target, string connectionName);
19+
}
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,4 @@
11
#nullable enable
2+
Aspire.Hosting.Azure.IResourceWithAzureFunctionsConfig
3+
Aspire.Hosting.Azure.IResourceWithAzureFunctionsConfig.ApplyAzureFunctionsConfiguration(System.Collections.Generic.IDictionary<string!, object!>! target, string! connectionName) -> void
24
static Aspire.Hosting.AzureConstructResourceExtensions.ConfigureConstruct<T>(this Aspire.Hosting.ApplicationModel.IResourceBuilder<T!>! builder, System.Action<Aspire.Hosting.ResourceModuleConstruct!>! configure) -> Aspire.Hosting.ApplicationModel.IResourceBuilder<T!>!

src/Aspire.Hosting/ApplicationModel/ConnectionStringReference.cs

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,11 @@ public class ConnectionStringReference(IResourceWithConnectionString resource, b
1717
/// </summary>
1818
public bool Optional { get; } = optional;
1919

20+
/// <summary>
21+
/// The name of the connection key.
22+
/// </summary>
23+
public string? ConnectionName { get; set; }
24+
2025
string IManifestExpressionProvider.ValueExpression => Resource.ValueExpression;
2126

2227
IEnumerable<object> IValueWithReferences.References => [Resource];

src/Aspire.Hosting/PublicAPI.Unshipped.txt

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,8 @@ Aspire.Hosting.ApplicationModel.BeforeStartEvent
1515
Aspire.Hosting.ApplicationModel.BeforeStartEvent.BeforeStartEvent(System.IServiceProvider! services, Aspire.Hosting.ApplicationModel.DistributedApplicationModel! model) -> void
1616
Aspire.Hosting.ApplicationModel.BeforeStartEvent.Model.get -> Aspire.Hosting.ApplicationModel.DistributedApplicationModel!
1717
Aspire.Hosting.ApplicationModel.BeforeStartEvent.Services.get -> System.IServiceProvider!
18+
Aspire.Hosting.ApplicationModel.ConnectionStringReference.ConnectionName.get -> string?
19+
Aspire.Hosting.ApplicationModel.ConnectionStringReference.ConnectionName.set -> void
1820
Aspire.Hosting.ApplicationModel.ResourceNotificationService.ResourceNotificationService(Microsoft.Extensions.Logging.ILogger<Aspire.Hosting.ApplicationModel.ResourceNotificationService!>! logger, Microsoft.Extensions.Hosting.IHostApplicationLifetime! hostApplicationLifetime) -> void
1921
Aspire.Hosting.DistributedApplicationBuilder.Eventing.get -> Aspire.Hosting.Eventing.IDistributedApplicationEventing!
2022
Aspire.Hosting.Eventing.DistributedApplicationEventing

0 commit comments

Comments
 (0)