Skip to content

Commit 5c90928

Browse files
aligneddevaligneddev
andauthored
Fix weather call and improve error logging (#30)
* apphost and urls * weather call fix * error logging --------- Co-authored-by: aligneddev <aligneddev@github.com>
1 parent e08f6ea commit 5c90928

File tree

5 files changed

+60
-15
lines changed

5 files changed

+60
-15
lines changed

src/BikeTracking.Api.Tests/Application/Rides/WeatherLookupServiceTests.cs

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -65,7 +65,9 @@ public async Task GetOrFetchAsync_CacheHit_DoesNotCallHttp()
6565
{
6666
Content = new StringContent("{}", Encoding.UTF8, "application/json"),
6767
});
68-
var factory = new StubHttpClientFactory(new HttpClient(handler));
68+
var factory = new StubHttpClientFactory(
69+
new HttpClient(handler) { BaseAddress = new Uri("https://api.open-meteo.com") }
70+
);
6971
var config = new ConfigurationBuilder().AddInMemoryCollection().Build();
7072

7173
var service = new OpenMeteoWeatherLookupService(
@@ -101,7 +103,9 @@ public async Task GetOrFetchAsync_SecondCall_UsesCacheAndCallsHttpOnce()
101103
),
102104
});
103105

104-
var factory = new StubHttpClientFactory(new HttpClient(handler));
106+
var factory = new StubHttpClientFactory(
107+
new HttpClient(handler) { BaseAddress = new Uri("https://api.open-meteo.com") }
108+
);
105109
var config = new ConfigurationBuilder().AddInMemoryCollection().Build();
106110

107111
var service = new OpenMeteoWeatherLookupService(
@@ -164,7 +168,9 @@ public async Task GetOrFetchAsync_AfterServiceRestart_UsesPersistedCacheWithoutH
164168
"application/json"
165169
),
166170
});
167-
var factory = new StubHttpClientFactory(new HttpClient(handler));
171+
var factory = new StubHttpClientFactory(
172+
new HttpClient(handler) { BaseAddress = new Uri("https://api.open-meteo.com") }
173+
);
168174
var config = new ConfigurationBuilder().AddInMemoryCollection().Build();
169175

170176
await using var restartedContext = CreateSqliteContext(connection);

src/BikeTracking.Api/Application/Rides/WeatherLookupService.cs

Lines changed: 8 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -101,45 +101,43 @@ ILogger<OpenMeteoWeatherLookupService> logger
101101
// Determine which API to call (forecast vs. archive)
102102
var daysDiff = (int)(DateTime.UtcNow.Date - dateTimeUtc.Date).TotalDays;
103103
var isHistorical = daysDiff > 92;
104-
var endpoint = isHistorical
105-
? "https://archive-api.open-meteo.com/v1/archive"
106-
: "https://api.open-meteo.com/v1/forecast";
104+
var clientName = isHistorical ? "OpenMeteoArchive" : "OpenMeteoForecast";
105+
var requestPath = isHistorical ? "/v1/archive" : "/v1/forecast";
107106

108107
var apiKey = configuration["WeatherLookup:ApiKey"];
109108
var apiKeyParam = string.IsNullOrWhiteSpace(apiKey)
110109
? string.Empty
111110
: $"&apikey={Uri.EscapeDataString(apiKey)}";
112111

113112
// Build query parameters
114-
var pastDaysParam = isHistorical ? "" : $"&past_days={Math.Min(daysDiff + 1, 92)}";
113+
// Note: past_days is mutually exclusive with start_date/end_date on the Open-Meteo API.
115114
var queryParams =
116115
$"?latitude={Uri.EscapeDataString(latitude.ToString(CultureInfo.InvariantCulture))}"
117116
+ $"&longitude={Uri.EscapeDataString(longitude.ToString(CultureInfo.InvariantCulture))}"
118117
+ $"&start_date={dateTimeUtc.Date:yyyy-MM-dd}"
119118
+ $"&end_date={dateTimeUtc.Date:yyyy-MM-dd}"
120-
+ $"{pastDaysParam}"
121119
+ "&hourly=temperature_2m,wind_speed_10m,wind_direction_10m,relative_humidity_2m,cloud_cover,precipitation,weather_code"
122120
+ "&temperature_unit=fahrenheit"
123121
+ "&wind_speed_unit=mph"
124122
+ "&timezone=auto"
125123
+ apiKeyParam;
126-
127124
try
128125
{
129-
var client = httpClientFactory.CreateClient("OpenMeteo");
126+
var client = httpClientFactory.CreateClient(clientName);
130127
using var response = await client.GetAsync(
131-
$"{endpoint}{queryParams}",
128+
$"{requestPath}{queryParams}",
132129
cancellationToken
133130
);
134131

135132
if (!response.IsSuccessStatusCode)
136133
{
137134
logger.LogWarning(
138-
"Open-Meteo lookup failed for rounded {LatitudeRounded},{LongitudeRounded} at {UtcHour} with status {StatusCode}",
135+
"Open-Meteo lookup failed for rounded {LatitudeRounded},{LongitudeRounded} at {UtcHour} with status {StatusCode}: {ResponseContent}",
139136
latRounded,
140137
lonRounded,
141138
dateTimeUtc,
142-
response.StatusCode
139+
response.StatusCode,
140+
await response.Content.ReadAsStringAsync(cancellationToken)
143141
);
144142

145143
// Cache failure for short period to avoid hammering API

src/BikeTracking.Api/Program.cs

Lines changed: 19 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,14 @@
1515
var connectionString =
1616
builder.Configuration.GetConnectionString("BikeTracking")
1717
?? "Data Source=biketracking.local.db";
18+
var eiaGasPriceBaseUrl =
19+
builder.Configuration["ExternalApis:EiaGasPriceBaseUrl"] ?? "https+http://eia-gas-price";
20+
var openMeteoForecastBaseUrl =
21+
builder.Configuration["ExternalApis:OpenMeteoForecastBaseUrl"]
22+
?? "https+http://open-meteo-forecast";
23+
var openMeteoArchiveBaseUrl =
24+
builder.Configuration["ExternalApis:OpenMeteoArchiveBaseUrl"]
25+
?? "https+http://open-meteo-archive";
1826

1927
builder.Services.Configure<IdentityOptions>(builder.Configuration.GetSection("Identity"));
2028
builder.Services.AddDbContext<BikeTrackingDbContext>(options =>
@@ -48,14 +56,23 @@
4856
"EiaGasPrice",
4957
client =>
5058
{
51-
client.BaseAddress = new Uri("https://api.eia.gov");
59+
client.BaseAddress = new Uri(eiaGasPriceBaseUrl);
5260
client.Timeout = TimeSpan.FromSeconds(10);
5361
}
5462
);
5563
builder.Services.AddHttpClient(
56-
"OpenMeteo",
64+
"OpenMeteoForecast",
5765
client =>
5866
{
67+
client.BaseAddress = new Uri(openMeteoForecastBaseUrl);
68+
client.Timeout = TimeSpan.FromSeconds(5);
69+
}
70+
);
71+
builder.Services.AddHttpClient(
72+
"OpenMeteoArchive",
73+
client =>
74+
{
75+
client.BaseAddress = new Uri(openMeteoArchiveBaseUrl);
5976
client.Timeout = TimeSpan.FromSeconds(5);
6077
}
6178
);

src/BikeTracking.AppHost/AppHost.cs

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

3+
var openMeteoForecast = builder.AddExternalService(
4+
"open-meteo-forecast",
5+
"https://api.open-meteo.com/"
6+
);
7+
var openMeteoArchive = builder.AddExternalService(
8+
"open-meteo-archive",
9+
"https://archive-api.open-meteo.com/"
10+
);
11+
var eiaGasPrice = builder.AddExternalService("eia-gas-price", "https://api.eia.gov/");
12+
13+
const string eiaGasPriceBaseUrl = "https+http://eia-gas-price";
14+
const string openMeteoForecastBaseUrl = "https+http://open-meteo-forecast";
15+
const string openMeteoArchiveBaseUrl = "https+http://open-meteo-archive";
16+
317
// Local SQL Server database for development
418
// var database = builder.AddSqlServer("sql").AddDatabase("biketracking");
519
// TODO https://aspire.dev/integrations/databases/sqlite/sqlite-get-started/?lang=csharp
@@ -10,6 +24,12 @@
1024
var apiService = builder
1125
.AddProject<Projects.BikeTracking_Api>("api")
1226
//.WithReference(database)
27+
.WithReference(openMeteoForecast)
28+
.WithReference(openMeteoArchive)
29+
.WithReference(eiaGasPrice)
30+
.WithEnvironment("ExternalApis__EiaGasPriceBaseUrl", eiaGasPriceBaseUrl)
31+
.WithEnvironment("ExternalApis__OpenMeteoForecastBaseUrl", openMeteoForecastBaseUrl)
32+
.WithEnvironment("ExternalApis__OpenMeteoArchiveBaseUrl", openMeteoArchiveBaseUrl)
1333
.WithHttpHealthCheck("/health")
1434
.WithExternalHttpEndpoints();
1535

src/BikeTracking.Frontend/playwright.config.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,10 @@ export default defineConfig({
4545
env: {
4646
ASPNETCORE_URLS: e2eApiUrl,
4747
PLAYWRIGHT_E2E: "1",
48+
ExternalApis__EiaGasPriceBaseUrl: "https://api.eia.gov",
49+
ExternalApis__OpenMeteoForecastBaseUrl: "https://api.open-meteo.com",
50+
ExternalApis__OpenMeteoArchiveBaseUrl:
51+
"https://archive-api.open-meteo.com",
4852
// Use a dedicated E2E database so test runs never touch the local dev DB.
4953
// ASP.NET Core maps ConnectionStrings__<name> to ConnectionStrings[name].
5054
ConnectionStrings__BikeTracking: `Data Source=${e2eDbPath}`,

0 commit comments

Comments
 (0)