diff --git a/.cfignore b/.cfignore new file mode 100644 index 0000000..a9d96f9 --- /dev/null +++ b/.cfignore @@ -0,0 +1,18 @@ +# DotNet +bin/ +obj/ + +# user-specific state +*.user + +# VS Code +.vscode/ +*.code-workspace + +# Visual Studio +.vs/ + +# Common files that don't need to be pushed +manifest*.yml +*.md +launchSettings.json diff --git a/.gitignore b/.gitignore index cd42ee3..547a964 100644 --- a/.gitignore +++ b/.gitignore @@ -1,2 +1,4 @@ bin/ obj/ +.vs/ +*.user diff --git a/Cook.csproj b/Cook.csproj index 0a95df4..30c332a 100644 --- a/Cook.csproj +++ b/Cook.csproj @@ -5,11 +5,11 @@ cook project enable enable - SA1402;SA1600;SA0001;SA1633 + SA1101;SA1204;SA1402;SA1600;SA0001;SA1633 - 4.0.*-* + 4.0.* diff --git a/Cook.slnx b/Cook.slnx index a8b1ea3..d463273 100644 --- a/Cook.slnx +++ b/Cook.slnx @@ -1,3 +1,3 @@ - + diff --git a/Program.cs b/Program.cs index 380bf6e..8570a98 100644 --- a/Program.cs +++ b/Program.cs @@ -22,16 +22,6 @@ return secretMenu; }); -app.MapGet("/restaurant/dessert-menu", async (Cook.ResourceService resourceService) => -{ - try - { - return await resourceService.GetContentAsync("dessert.json"); - } - catch (Exception ex) - { - return $"Error fetching resource: {ex.Message}"; - } -}); +app.MapGet("/restaurant/dessert-menu", async (Cook.ResourceService resourceService) => await resourceService.GetContentAsync("dessert.json")); app.Run(); diff --git a/Properties/launchSettings.json b/Properties/launchSettings.json index 9025d31..cda6b0e 100644 --- a/Properties/launchSettings.json +++ b/Properties/launchSettings.json @@ -5,20 +5,20 @@ "commandName": "Project", "dotnetRunMessages": true, "launchBrowser": true, - "launchUrl": "swagger", + "launchUrl": "restaurant", "applicationUrl": "http://localhost:8080", "environmentVariables": { - "ASPNETCORE_ENVIRONMENT": "Development" + "ASPNETCORE_ENVIRONMENT": "development" } }, "https": { "commandName": "Project", "dotnetRunMessages": true, "launchBrowser": true, - "launchUrl": "swagger", + "launchUrl": "restaurant", "applicationUrl": "https://localhost:7053;http://localhost:8080", "environmentVariables": { - "ASPNETCORE_ENVIRONMENT": "Development" + "ASPNETCORE_ENVIRONMENT": "development" } } } diff --git a/ResourceService.cs b/ResourceService.cs index bb65f8b..d2d0cfb 100644 --- a/ResourceService.cs +++ b/ResourceService.cs @@ -3,97 +3,84 @@ namespace Cook; using System.Net.Http.Headers; using System.Text.Json; using System.Text.Json.Serialization; +using Microsoft.Extensions.Options; +using Steeltoe.Common; +using Steeltoe.Configuration.ConfigServer; -public class ResourceService +public class ResourceService(IOptionsSnapshot configServerOptions, HttpClient httpClient) { - private readonly IConfiguration configuration; - private readonly HttpClient httpClient; private OAuth2TokenResponse? cachedToken; - public ResourceService(IConfiguration configuration, HttpClient httpClient) - { - this.configuration = configuration; - this.httpClient = httpClient; - } - public async Task GetContentAsync(string name) { - try - { - var configServerUri = this.configuration["spring:cloud:config:uri"]; + var configServerUri = configServerOptions.Value.Uri; + var appName = configServerOptions.Value.Name; + var profile = configServerOptions.Value.Environment; + var label = configServerOptions.Value.Label ?? "main"; - var appName = this.configuration["spring:application:name"] ?? "application"; - var profile = this.configuration["spring:cloud:config:env"] ?? "default"; - var label = this.configuration["spring:cloud:config:label"] ?? "main"; + var request = new HttpRequestMessage(HttpMethod.Get, $"{configServerUri}/{appName}/{profile}/{label}/{name}"); - var accessToken = await this.GetOrRefreshAccessTokenAsync(); - - var request = new HttpRequestMessage(HttpMethod.Get, $"{configServerUri}/{appName}/{profile}/{label}/{name}"); + if (Platform.IsCloudFoundry) + { + var accessToken = await GetOrRefreshAccessTokenAsync(); request.Headers.Authorization = new AuthenticationHeaderValue("Bearer", accessToken); - - var response = await this.httpClient.SendAsync(request); - if (response.IsSuccessStatusCode) - { - return await response.Content.ReadAsStringAsync(); - } - else - { - throw new HttpRequestException($"Failed to fetch resource: {response.StatusCode} - {response.ReasonPhrase}"); - } } - catch (Exception ex) + + var response = await httpClient.SendAsync(request); + if (response.IsSuccessStatusCode) { - throw new InvalidOperationException($"Error fetching resource: {ex.Message}", ex); + return await response.Content.ReadAsStringAsync(); } + + throw new HttpRequestException($"Failed to fetch resource: {response.StatusCode} - {response.ReasonPhrase}"); } private async Task GetOrRefreshAccessTokenAsync() { - if (this.cachedToken != null && DateTime.UtcNow < this.cachedToken.ExpirationTime) + if (cachedToken != null && DateTime.UtcNow < cachedToken.ExpirationTime) { - return this.cachedToken.AccessToken; + return cachedToken.AccessToken; } - var clientId = this.configuration["spring:cloud:config:clientId"]; - var clientSecret = this.configuration["spring:cloud:config:clientSecret"]; - var accessTokenUri = this.configuration["spring:cloud:config:accessTokenUri"]; + var clientId = configServerOptions.Value.ClientId; + var clientSecret = configServerOptions.Value.ClientSecret; + var accessTokenUri = configServerOptions.Value.AccessTokenUri; if (string.IsNullOrEmpty(clientId) || string.IsNullOrEmpty(clientSecret) || string.IsNullOrEmpty(accessTokenUri)) { throw new InvalidOperationException("OAuth2 credentials not configured"); } - var tokenRequest = new FormUrlEncodedContent(new[] - { + var tokenRequest = new FormUrlEncodedContent([ new KeyValuePair("grant_type", "client_credentials"), new KeyValuePair("client_id", clientId), - new KeyValuePair("client_secret", clientSecret), - }); + new KeyValuePair("client_secret", clientSecret) + ]); - var tokenResponse = await this.httpClient.PostAsync(accessTokenUri, tokenRequest); + var tokenResponse = await httpClient.PostAsync(accessTokenUri, tokenRequest); if (!tokenResponse.IsSuccessStatusCode) { throw new HttpRequestException($"Failed to get access token: {tokenResponse.StatusCode} - {tokenResponse.ReasonPhrase}"); } - this.cachedToken = this.ExtractTokenData(await tokenResponse.Content.ReadAsStringAsync()); + cachedToken = ExtractTokenData(await tokenResponse.Content.ReadAsStringAsync()); - return this.cachedToken.AccessToken; + return cachedToken.AccessToken; } - private OAuth2TokenResponse ExtractTokenData(string tokenResponse) + private static OAuth2TokenResponse ExtractTokenData(string tokenResponse) { return JsonSerializer.Deserialize(tokenResponse) ?? new OAuth2TokenResponse(); } -} -public class OAuth2TokenResponse -{ - [JsonPropertyName("access_token")] - public string AccessToken { get; set; } = string.Empty; + private class OAuth2TokenResponse + { + [JsonPropertyName("access_token")] + public string AccessToken { get; init; } = string.Empty; - [JsonPropertyName("expires_in")] - public int ExpiresIn { get; set; } = 3600; + [JsonPropertyName("expires_in")] + public int ExpiresIn { get; init; } = 3600; - public DateTime ExpirationTime => DateTime.UtcNow.AddSeconds(this.ExpiresIn - 10); // Subtract 10 seconds for safety + public DateTime ExpirationTime => DateTime.UtcNow.AddSeconds(ExpiresIn - 10); // Subtract 10 seconds for safety + } } diff --git a/app.config b/app.config deleted file mode 100644 index ea5ce6b..0000000 --- a/app.config +++ /dev/null @@ -1,7 +0,0 @@ - - - - - - - diff --git a/appsettings.Development.json b/appsettings.Development.json new file mode 100644 index 0000000..5ed5153 --- /dev/null +++ b/appsettings.Development.json @@ -0,0 +1,16 @@ +{ + "$schema": "https://steeltoe.io/schema/latest/schema.json", + "Logging": { + "LogLevel": { + "Default": "Information", + "Microsoft.AspNetCore": "Warning" + } + }, + "Spring": { + "Cloud": { + "Config": { + "Uri": "http://localhost:8888" + } + } + } +} diff --git a/appsettings.json b/appsettings.json index 56a7121..5b4d310 100644 --- a/appsettings.json +++ b/appsettings.json @@ -1,20 +1,14 @@ { + "$schema": "https://steeltoe.io/schema/latest/schema.json", "Logging": { "LogLevel": { "Default": "Information", "Microsoft.AspNetCore": "Warning" } }, - "spring": { - "application": { - "name": "cook" - }, - "cloud": { - "config": { - "uri": "http://localhost:8888", - "env": "development", - "label": "main" - } + "Spring": { + "Application": { + "Name": "cook" } } } diff --git a/manifest.yml b/manifest.yml index 9897363..9e35f29 100644 --- a/manifest.yml +++ b/manifest.yml @@ -6,4 +6,6 @@ applications: services: - cook-config-server env: - SPRING__CLOUD__CONFIG__ENV: production + ASPNETCORE_ENVIRONMENT: production + DOTNET_CLI_TELEMETRY_OPTOUT: "true" + DOTNET_NOLOGO: "true" \ No newline at end of file