Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
18 changes: 18 additions & 0 deletions .cfignore
Original file line number Diff line number Diff line change
@@ -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
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,2 +1,4 @@
bin/
obj/
.vs/
*.user
4 changes: 2 additions & 2 deletions Cook.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -5,11 +5,11 @@
<Description>cook project</Description>
<Nullable>enable</Nullable>
<ImplicitUsings>enable</ImplicitUsings>
<NoWarn>SA1402;SA1600;SA0001;SA1633</NoWarn>
<NoWarn>SA1101;SA1204;SA1402;SA1600;SA0001;SA1633</NoWarn>
</PropertyGroup>

<PropertyGroup>
<SteeltoeVersion>4.0.*-*</SteeltoeVersion>
<SteeltoeVersion>4.0.*</SteeltoeVersion>
</PropertyGroup>

<ItemGroup>
Expand Down
2 changes: 1 addition & 1 deletion Cook.slnx
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
<Solution>
<Project Path="cook.csproj" />
<Project Path="Cook.csproj" />
</Solution>
12 changes: 1 addition & 11 deletions Program.cs
Original file line number Diff line number Diff line change
Expand Up @@ -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();
8 changes: 4 additions & 4 deletions Properties/launchSettings.json
Original file line number Diff line number Diff line change
Expand Up @@ -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"
}
}
}
Expand Down
89 changes: 38 additions & 51 deletions ResourceService.cs
Original file line number Diff line number Diff line change
Expand Up @@ -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<ConfigServerClientOptions> 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<string> 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<string> 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<string, string>("grant_type", "client_credentials"),
new KeyValuePair<string, string>("client_id", clientId),
new KeyValuePair<string, string>("client_secret", clientSecret),
});
new KeyValuePair<string, string>("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<OAuth2TokenResponse>(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
}
}
7 changes: 0 additions & 7 deletions app.config

This file was deleted.

16 changes: 16 additions & 0 deletions appsettings.Development.json
Original file line number Diff line number Diff line change
@@ -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"
}
}
}
}
14 changes: 4 additions & 10 deletions appsettings.json
Original file line number Diff line number Diff line change
@@ -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"
}
}
}
4 changes: 3 additions & 1 deletion manifest.yml
Original file line number Diff line number Diff line change
Expand Up @@ -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"