Skip to content

Commit 705b252

Browse files
committed
x
1 parent d947490 commit 705b252

9 files changed

+193
-47
lines changed

examples-Aspire/AspireApp1.AppHost/Program.cs

+4-1
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
using AspireApp1.AppHost;
2+
13
var builder = DistributedApplication.CreateBuilder(args);
24

35
//IResourceBuilder<ProjectResource> apiService = builder.AddProject<Projects.AspireApp1_ApiService>("apiservice");
@@ -9,7 +11,8 @@
911
var wiremock = builder
1012
.AddWireMock("apiservice", WireMockServerArguments.DefaultPort)
1113
.WithMappingsPath(mappingsPath)
12-
.WithReadStaticMappings();
14+
.WithReadStaticMappings()
15+
.WithApiMappingBuilder(WeatherForecastApiMock.BuildAsync);
1316

1417
builder.AddProject<Projects.AspireApp1_Web>("webfrontend")
1518
.WithExternalHttpEndpoints()
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
using WireMock.Client.Builders;
2+
3+
namespace AspireApp1.AppHost;
4+
5+
internal class WeatherForecastApiMock
6+
{
7+
public static async Task BuildAsync(AdminApiMappingBuilder builder)
8+
{
9+
var summaries = new[]
10+
{
11+
"Freezing", "Bracing", "Chilly", "Cool", "Mild", "Warm", "Balmy", "Hot", "Sweltering", "Scorching"
12+
};
13+
14+
builder.Given(b => b
15+
.WithRequest(request => request
16+
.UsingGet()
17+
.WithPath("/weatherforecast2")
18+
)
19+
.WithResponse(response => response
20+
.WithHeaders(h => h.Add("Content-Type", "application/json"))
21+
.WithBodyAsJson(() => Enumerable.Range(1, 5).Select(index =>
22+
new WeatherForecast
23+
(
24+
DateOnly.FromDateTime(DateTime.Now.AddDays(index)),
25+
Random.Shared.Next(-20, 55),
26+
summaries[Random.Shared.Next(summaries.Length)]
27+
))
28+
.ToArray())
29+
)
30+
);
31+
32+
await builder.BuildAndPostAsync();
33+
}
34+
}
35+
36+
internal record WeatherForecast(DateOnly Date, int TemperatureC, string? Summary);

examples-Aspire/AspireApp1.Web/Components/Pages/Weather.razor

+47-12
Original file line numberDiff line numberDiff line change
@@ -1,18 +1,15 @@
11
@page "/weather"
2-
@attribute [StreamRendering(true)]
3-
@attribute [OutputCache(Duration = 5)]
2+
@attribute [StreamRendering]
3+
@* @attribute [OutputCache(Duration = 5)] *@
44

55
@inject WeatherApiClient WeatherApi
6+
@inject WeatherApiClient2 WeatherApi2
67

78
<PageTitle>Weather</PageTitle>
89

9-
<h1>Weather</h1>
10+
<h1>Weather 1</h1>
1011

11-
<p>This component demonstrates showing data loaded from a backend API service.</p>
12-
13-
<p>HttpClient.BaseAddress = </p><pre>@WeatherApi.GetBaseAddress()</pre>
14-
15-
@if (forecasts == null)
12+
@if (forecasts1 == null)
1613
{
1714
<p><em>Loading...</em></p>
1815
}
@@ -28,7 +25,7 @@ else
2825
</tr>
2926
</thead>
3027
<tbody>
31-
@foreach (var forecast in forecasts)
28+
@foreach (var forecast in forecasts1)
3229
{
3330
<tr>
3431
<td>@forecast.Date.ToShortDateString()</td>
@@ -41,11 +38,49 @@ else
4138
</table>
4239
}
4340

41+
<h1>Weather 2</h1>
42+
43+
@if (forecasts2 == null)
44+
{
45+
<p><em>Loading...</em></p>
46+
}
47+
else
48+
{
49+
<table class="table">
50+
<thead>
51+
<tr>
52+
<th>Date</th>
53+
<th>Temp. (C)</th>
54+
<th>Temp. (F)</th>
55+
<th>Summary</th>
56+
</tr>
57+
</thead>
58+
<tbody>
59+
@foreach (var forecast in forecasts2)
60+
{
61+
<tr>
62+
<td>@forecast.Date.ToShortDateString()</td>
63+
<td>@forecast.TemperatureC</td>
64+
<td>@forecast.TemperatureF</td>
65+
<td>@forecast.Summary</td>
66+
</tr>
67+
}
68+
</tbody>
69+
</table>
70+
}
71+
4472
@code {
45-
private WeatherForecast[]? forecasts;
73+
private WeatherForecast[]? forecasts1;
74+
private WeatherForecast[]? forecasts2;
4675

4776
protected override async Task OnInitializedAsync()
4877
{
49-
forecasts = await WeatherApi.GetWeatherAsync();
78+
var forecastsTask1 = WeatherApi.GetWeatherAsync();
79+
var forecastsTask2 = WeatherApi2.GetWeatherAsync();
80+
81+
await Task.WhenAll(forecastsTask1, forecastsTask2);
82+
83+
forecasts1 = await forecastsTask1;
84+
forecasts2 = await forecastsTask2;
5085
}
51-
}
86+
}

examples-Aspire/AspireApp1.Web/Program.cs

+6
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,12 @@
1818
// Learn more about service discovery scheme resolution at https://aka.ms/dotnet/sdschemes.
1919
client.BaseAddress = new("https+http://apiservice");
2020
});
21+
builder.Services.AddHttpClient<WeatherApiClient2>(client =>
22+
{
23+
// This URL uses "https+http://" to indicate HTTPS is preferred over HTTP.
24+
// Learn more about service discovery scheme resolution at https://aka.ms/dotnet/sdschemes.
25+
client.BaseAddress = new("https+http://apiservice");
26+
});
2127

2228
var app = builder.Build();
2329

examples-Aspire/AspireApp1.Web/WeatherApiClient.cs

+28
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,34 @@ public async Task<WeatherForecast[]> GetWeatherAsync(int maxItems = 10, Cancella
2828
}
2929
}
3030

31+
public class WeatherApiClient2(HttpClient httpClient)
32+
{
33+
public string GetBaseAddress()
34+
{
35+
return httpClient.BaseAddress?.ToString() ?? "???";
36+
}
37+
38+
public async Task<WeatherForecast[]> GetWeatherAsync(int maxItems = 10, CancellationToken cancellationToken = default)
39+
{
40+
List<WeatherForecast>? forecasts = null;
41+
42+
await foreach (var forecast in httpClient.GetFromJsonAsAsyncEnumerable<WeatherForecast>("/weatherforecast2", cancellationToken))
43+
{
44+
if (forecasts?.Count >= maxItems)
45+
{
46+
break;
47+
}
48+
if (forecast is not null)
49+
{
50+
forecasts ??= [];
51+
forecasts.Add(forecast);
52+
}
53+
}
54+
55+
return forecasts?.ToArray() ?? [];
56+
}
57+
}
58+
3159
public record WeatherForecast(DateOnly Date, int TemperatureC, string? Summary)
3260
{
3361
public int TemperatureF => 32 + (int)(TemperatureC / 0.5556);

src/WireMock.Net.Aspire/WireMockServerArguments.cs

+7-24
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11
using System;
22
using System.Collections.Generic;
33
using System.Linq;
4+
using System.Threading.Tasks;
5+
using WireMock.Client.Builders;
46

57
// ReSharper disable once CheckNamespace
68
namespace Aspire.Hosting;
@@ -11,7 +13,6 @@ namespace Aspire.Hosting;
1113
public class WireMockServerArguments
1214
{
1315
internal const int HttpContainerPort = 80;
14-
internal const int HttpsContainerPort = 443;
1516

1617
/// <summary>
1718
/// The default HTTP port where WireMock.Net is listening.
@@ -26,17 +27,6 @@ public class WireMockServerArguments
2627
/// </summary>
2728
public int? HttpPort { get; set; }
2829

29-
/// <summary>
30-
/// The HTTPS port where WireMock.Net is listening.
31-
/// If not defined, .NET Aspire automatically assigns a random port.
32-
/// </summary>
33-
public int? HttpsPort { get; set; }
34-
35-
/// <summary>
36-
/// Also listen on HTTPS URL.
37-
/// </summary>
38-
public bool UseHttps { get; set; }
39-
4030
/// <summary>
4131
/// The admin username.
4232
/// </summary>
@@ -71,6 +61,11 @@ public class WireMockServerArguments
7161
/// </summary>
7262
public bool HasBasicAuthentication => !string.IsNullOrEmpty(AdminUsername) && !string.IsNullOrEmpty(AdminPassword);
7363

64+
/// <summary>
65+
/// Optional delegate that will be invoked to configure the WireMock.Net resource using the <see cref="AdminApiMappingBuilder"/>.
66+
/// </summary>
67+
public Func<AdminApiMappingBuilder, Task>? ApiMappingBuilder { get; set; }
68+
7469
/// <summary>
7570
/// Converts the current instance's properties to an array of command-line arguments for starting the WireMock.Net server.
7671
/// </summary>
@@ -79,18 +74,6 @@ public string[] GetArgs()
7974
{
8075
var args = new Dictionary<string, string>();
8176

82-
Add(args, "--Urls", () =>
83-
{
84-
var urls = $"http://*:{HttpContainerPort}";
85-
if (UseHttps || HttpsPort > 0)
86-
{
87-
urls += $", https://*:{HttpsContainerPort}";
88-
}
89-
90-
return urls;
91-
}
92-
);
93-
9477
Add(args, "--WireMockLogger", DefaultLogger);
9578

9679
if (HasBasicAuthentication)

src/WireMock.Net.Aspire/WireMockServerBuilderExtensions.cs

+19-10
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,10 @@
11
using System;
2+
using System.Threading.Tasks;
23
using Aspire.Hosting.ApplicationModel;
4+
using Aspire.Hosting.Lifecycle;
35
using Stef.Validation;
6+
using WireMock.Client.Builders;
7+
using WireMock.Net.Aspire;
48

59
// ReSharper disable once CheckNamespace
610
namespace Aspire.Hosting;
@@ -53,11 +57,6 @@ public static IResourceBuilder<WireMockServerResource> AddWireMock(this IDistrib
5357
.WithEnvironment(ctx => ctx.EnvironmentVariables.Add("DOTNET_USE_POLLING_FILE_WATCHER", "1")) // https://khalidabuhakmeh.com/aspnet-docker-gotchas-and-workarounds#configuration-reloads-and-filesystemwatcher
5458
.WithHttpEndpoint(port: arguments.HttpPort, targetPort: WireMockServerArguments.HttpContainerPort);
5559

56-
if (arguments.UseHttps || arguments.HttpsPort > 0)
57-
{
58-
resourceBuilder = resourceBuilder.WithHttpsEndpoint(port: arguments.HttpsPort, targetPort: WireMockServerArguments.HttpsContainerPort);
59-
}
60-
6160
if (!string.IsNullOrEmpty(arguments.MappingsPath))
6261
{
6362
resourceBuilder = resourceBuilder.WithBindMount(arguments.MappingsPath, DefaultLinuxMappingsPath);
@@ -152,9 +151,19 @@ public static IResourceBuilder<WireMockServerResource> WithAdminUserNameAndPassw
152151
return wiremock;
153152
}
154153

155-
//public static IResourceBuilder<WireMockServerResource> UseHttps(this IResourceBuilder<WireMockServerResource> wiremock)
156-
//{
157-
// Guard.NotNull(wiremock).Resource.Arguments.UseHttps = true;
158-
// return wiremock;
159-
//}
154+
/// <summary>
155+
/// Use WireMock Client's AdminApiMappingBuilder to configure the WireMock.Net resource.
156+
/// </summary>
157+
/// <param name="wiremock">The <see cref="IResourceBuilder{WireMockServerResource}"/>.</param>
158+
/// <param name="configure">Delegate that will be invoked to configure the WireMock.Net resource.</param>
159+
/// <returns></returns>
160+
public static IResourceBuilder<WireMockServerResource> WithApiMappingBuilder(this IResourceBuilder<WireMockServerResource> wiremock, Func<AdminApiMappingBuilder, Task> configure)
161+
{
162+
Guard.NotNull(wiremock);
163+
164+
wiremock.ApplicationBuilder.Services.TryAddLifecycleHook<WireMockServerMappingBuilderHook>();
165+
wiremock.Resource.Arguments.ApiMappingBuilder = configure;
166+
167+
return wiremock;
168+
}
160169
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
using System;
2+
using System.Linq;
3+
using System.Threading;
4+
using System.Threading.Tasks;
5+
using Aspire.Hosting.ApplicationModel;
6+
using Aspire.Hosting.Lifecycle;
7+
using RestEase;
8+
using WireMock.Client;
9+
using WireMock.Client.Extensions;
10+
11+
namespace WireMock.Net.Aspire;
12+
13+
internal class WireMockServerMappingBuilderHook : IDistributedApplicationLifecycleHook
14+
{
15+
public Task AfterResourcesCreatedAsync(DistributedApplicationModel appModel, CancellationToken cancellationToken)
16+
{
17+
var wireMockInstances = appModel.Resources
18+
.OfType<WireMockServerResource>()
19+
.ToArray();
20+
21+
if (!wireMockInstances.Any())
22+
{
23+
return Task.CompletedTask;
24+
}
25+
26+
foreach (var wireMockInstance in wireMockInstances)
27+
{
28+
if (wireMockInstance.PrimaryEndpoint.IsAllocated)
29+
{
30+
var adminApi = RestClient.For<IWireMockAdminApi>(new Uri(wireMockInstance.PrimaryEndpoint.Url, UriKind.Absolute));
31+
var mappingBuilder = adminApi.GetMappingBuilder();
32+
wireMockInstance.Arguments.ApiMappingBuilder?.Invoke(mappingBuilder);
33+
}
34+
}
35+
36+
return Task.CompletedTask;
37+
}
38+
}

src/WireMock.Net.Aspire/WireMockServerResource.cs

+8
Original file line numberDiff line numberDiff line change
@@ -8,8 +8,16 @@ namespace Aspire.Hosting.ApplicationModel;
88
/// </summary>
99
public class WireMockServerResource : ContainerResource, IResourceWithServiceDiscovery
1010
{
11+
private const string PrimaryEndpointName = "http";
12+
private EndpointReference? _primaryEndpoint;
13+
1114
internal WireMockServerArguments Arguments { get; }
1215

16+
/// <summary>
17+
/// Gets the primary endpoint for the server.
18+
/// </summary>
19+
public EndpointReference PrimaryEndpoint => _primaryEndpoint ??= new(this, PrimaryEndpointName);
20+
1321
/// <summary>
1422
/// Initializes a new instance of the <see cref="WireMockServerResource"/> class.
1523
/// </summary>

0 commit comments

Comments
 (0)