Skip to content

Commit

Permalink
⚡ feat: bump minor version to add support for APS environment variabl…
Browse files Browse the repository at this point in the history
…es (#179)

* Add support for APS alternative environment variables

* Add unit tests and support for APS configuration and environment variables

* fix review feedback: updated to correct semantic verion 4.1.0
  • Loading branch information
MadhukarMoogala authored Nov 12, 2024
1 parent b5b4065 commit 10cbd08
Show file tree
Hide file tree
Showing 6 changed files with 274 additions and 6 deletions.
18 changes: 13 additions & 5 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,8 @@
### 4.1.0

* Add support for APS alternative environment variables `APS_CLIENT_ID` and `APS_CLIENT_SECRET`
* `FORGE_CLIENT_ID` and `FORGE_CLIENT_SECRET` will be deprecated in future!

### 4.0.1

* Add Documentation in the `Autodesk.Forge.Core` project.
Expand All @@ -18,18 +23,21 @@

* Migrate to .Net 5
* Support to return HttpStatusCode in HttpRequestException


### 1.0.0.0

* 1.0.0-beta4
* Support concurrency with `SemaphoreSlim`

* Support concurrency with `SemaphoreSlim`

* 1.0.0-beta3
* Configurable timeout

* Configurable timeout

* 1.0.0-beta2
* Fix NuGet package settings

* Fix NuGet package settings

* 1.0.0-beta1
* Support for `FORGE_CLIENT_ID` and `FORGE_CLIENT_SECRET` environment variables via `ForgeAlternativeConfigurationExtensions`

* Support for `FORGE_CLIENT_ID` and `FORGE_CLIENT_SECRET` environment variables via `ForgeAlternativeConfigurationExtensions`
2 changes: 1 addition & 1 deletion Directory.Build.props
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
<Project>
<PropertyGroup>
<Version>4.0.1</Version>
<Version>4.1.0</Version>
<TargetFramework>net8.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
</PropertyGroup>
Expand Down
61 changes: 61 additions & 0 deletions src/Autodesk.Forge.Core/LegacySampleConfigurationProvider.cs
Original file line number Diff line number Diff line change
Expand Up @@ -29,13 +29,30 @@ public static class ForgeAlternativeConfigurationExtensions
/// </summary>
/// <param name="configurationBuilder">The configuration builder.</param>
/// <returns>The configuration builder with Forge alternative environment variables added.</returns>
///
[Obsolete("Use AddAPSAlternativeEnvironmentVariables instead will be removed in future release")]
public static IConfigurationBuilder AddForgeAlternativeEnvironmentVariables(this IConfigurationBuilder configurationBuilder)
{
configurationBuilder.Add(new ForgeAlternativeConfigurationSource());
return configurationBuilder;
}

/// <summary>
/// Adds APS alternative environment variables to the configuration builder.
/// </summary>
/// <param name="configurationBuilder"></param>
/// <returns></returns>

public static IConfigurationBuilder AddAPSAlternativeEnvironmentVariables(this IConfigurationBuilder configurationBuilder)
{
configurationBuilder.Add(new APSAlternativeConfigurationSource());
return configurationBuilder;
}

}



/// <summary>
/// Represents a configuration source for loading Forge alternative configuration.
/// </summary>
Expand Down Expand Up @@ -74,5 +91,49 @@ public override void Load()
}
}
}



/// <summary>
/// Represents a configuration source for loading APS alternative configuration.
/// </summary>

public class APSAlternativeConfigurationSource : IConfigurationSource
{
/// <summary>
/// Build the APS Environment Configuration Provider
/// </summary>
/// <param name="builder"></param>
/// <returns></returns>
public IConfigurationProvider Build(IConfigurationBuilder builder)
{
return new APSAlternativeConfigurationProvider();
}
}

/// <summary>
/// Loads the APS alternative configuration from environment variables.
/// </summary>

public class APSAlternativeConfigurationProvider : ConfigurationProvider
{
/// <summary>
/// Loads the APS alternative configuration from environment variables.
/// </summary>
public override void Load()
{
var id = Environment.GetEnvironmentVariable("APS_CLIENT_ID");
if (!string.IsNullOrEmpty(id))
{
this.Data.Add("APS:ClientId", id);
}
var secret = Environment.GetEnvironmentVariable("APS_CLIENT_SECRET");
if (!string.IsNullOrEmpty(secret))
{
this.Data.Add("APS:ClientSecret", secret);
}
}

}
}

2 changes: 2 additions & 0 deletions src/Autodesk.Forge.Core/ServiceCollectionExtensions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@ public static IHttpClientBuilder AddForgeService(this IServiceCollection service
{
services.AddOptions();
services.Configure<ForgeConfiguration>(configuration.GetSection("Forge"));
services.Configure<ForgeConfiguration>(configuration.GetSection("APS"));
services.AddTransient<ForgeHandler>();
return services.AddHttpClient<ForgeService>()
.AddHttpMessageHandler<ForgeHandler>();
Expand All @@ -57,6 +58,7 @@ public static IHttpClientBuilder AddForgeService(this IServiceCollection service
{
services.AddOptions();
services.Configure<ForgeConfiguration>(configuration.GetSection("Forge"));
services.Configure<ForgeConfiguration>(configuration.GetSection("APS"));
services.AddTransient<ForgeHandler>();
return services.AddHttpClient<ForgeService>(user)
.AddHttpMessageHandler(() => new ForgeAgentHandler(user))
Expand Down
180 changes: 180 additions & 0 deletions tests/Autodesk.Forge.Core.Test/TestAPSConfiguration.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,180 @@
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Options;
using Moq;
using Moq.Protected;
using Newtonsoft.Json;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Xunit;

namespace Autodesk.Forge.Core.Test
{
public class TestAPSConfiguration
{
/// <summary>
/// Tests using APS__ClientId and APS__ClientSecret environment variables dotnet core style
/// </summary>
[Fact]
public void TestAPSConfigFromEnvironmentVariables_DoubleUnderscoreFormat()
{
Environment.SetEnvironmentVariable("APS__ClientId", "bla");
Environment.SetEnvironmentVariable("APS__ClientSecret", "blabla");
var configuration = new ConfigurationBuilder()
.AddEnvironmentVariables()
.Build();

var services = new ServiceCollection();
services.AddForgeService(configuration);
var serviceProvider = services.BuildServiceProvider();

var config = serviceProvider.GetRequiredService<IOptions<ForgeConfiguration>>();
Assert.Equal("bla", config.Value.ClientId);
Assert.Equal("blabla", config.Value.ClientSecret);
}

/// <summary>
/// Tests using APS_CLIENT_ID and APS_CLIENT_SECRET environment variables
/// </summary>

[Fact]
public void TestAPSConfigFromEnvironmentVariables_UnderscoreFormat()
{
Environment.SetEnvironmentVariable("APS_CLIENT_ID", "bla");
Environment.SetEnvironmentVariable("APS_CLIENT_SECRET", "blabla");
var configuration = new ConfigurationBuilder()
.AddAPSAlternativeEnvironmentVariables()
.Build();
var services = new ServiceCollection();
services.AddForgeService(configuration);
var serviceProvider = services.BuildServiceProvider();
var config = serviceProvider.GetRequiredService<IOptions<ForgeConfiguration>>();
Assert.Equal("bla", config.Value.ClientId);
Assert.Equal("blabla", config.Value.ClientSecret);

}
/// <summary>
/// Tests loading APS configuration values from JSON with ClientId and ClientSecret
/// </summary>

[Fact]
public void TestAPSConfigFromJson()
{
var json = @"
{
""APS"" : {
""ClientId"" : ""bla"",
""ClientSecret"" : ""blabla""
}
}";
var configuration = new ConfigurationBuilder()
.AddJsonStream(new MemoryStream(Encoding.UTF8.GetBytes(json)))
.Build();

var services = new ServiceCollection();
services.AddForgeService(configuration);
var serviceProvider = services.BuildServiceProvider();

var config = serviceProvider.GetRequiredService<IOptions<ForgeConfiguration>>();
Assert.Equal("bla", config.Value.ClientId);
Assert.Equal("blabla", config.Value.ClientSecret);
}

/// <summary>
/// Tests loading APS configuration values from JSON with additional agent configurations
/// </summary>

[Fact]
public void TestAPSConfigFromJsonWithAgents()
{
var json = @"
{
""APS"" : {
""ClientId"" : ""bla"",
""ClientSecret"" : ""blabla"",
""Agents"" : {
""user1"" : {
""ClientId"" : ""user1-bla"",
""ClientSecret"" : ""user1-blabla""
}
}
}
}";
var configuration = new ConfigurationBuilder()
.AddJsonStream(new MemoryStream(Encoding.UTF8.GetBytes(json)))
.Build();

var services = new ServiceCollection();
services.AddForgeService(configuration);
var serviceProvider = services.BuildServiceProvider();

var config = serviceProvider.GetRequiredService<IOptions<ForgeConfiguration>>();
Assert.Equal("bla", config.Value.ClientId);
Assert.Equal("blabla", config.Value.ClientSecret);
Assert.Equal("user1-bla", config.Value.Agents["user1"].ClientId);
Assert.Equal("user1-blabla", config.Value.Agents["user1"].ClientSecret);
}

/// <summary>
/// Tests APS configuration for user agent "user1" and checks proper handling of authentication and request headers.
/// </summary>
[Fact]
public async Task TestAPSUserAgent()
{
var json = @"
{
""APS"" : {
""ClientId"" : ""bla"",
""ClientSecret"" : ""blabla"",
""Agents"" : {
""user1"" : {
""ClientId"" : ""user1-bla"",
""ClientSecret"" : ""user1-blabla""
}
}
}
}";
var configuration = new ConfigurationBuilder()
.AddJsonStream(new MemoryStream(Encoding.UTF8.GetBytes(json)))
.Build();

var sink = new Mock<HttpMessageHandler>(MockBehavior.Strict);
var services = new ServiceCollection();
services.AddForgeService("user1", configuration).ConfigurePrimaryHttpMessageHandler(() => sink.Object);
var serviceProvider = services.BuildServiceProvider();
var config = serviceProvider.GetRequiredService<IOptions<ForgeConfiguration>>().Value;
var req = new HttpRequestMessage();
req.RequestUri = new Uri("http://example.com");
req.Options.Set(ForgeConfiguration.ScopeKey, "somescope");

string user = null;
sink.Protected().As<HttpMessageInvoker>().Setup(o => o.SendAsync(It.Is<HttpRequestMessage>(r => r.RequestUri == config.AuthenticationAddress), It.IsAny<CancellationToken>()))
.ReturnsAsync(new HttpResponseMessage()
{
Content = new StringContent(JsonConvert.SerializeObject(new Dictionary<string, string> { { "token_type", "Bearer" }, { "access_token", "blablabla" }, { "expires_in", "3" } })),
StatusCode = System.Net.HttpStatusCode.OK
});
sink.Protected().As<HttpMessageInvoker>().Setup(o => o.SendAsync(It.Is<HttpRequestMessage>(r => r.RequestUri == req.RequestUri), It.IsAny<CancellationToken>()))
.Callback<HttpRequestMessage, CancellationToken>((r, ct) =>
{
r.Options.TryGetValue(ForgeConfiguration.AgentKey, out user);
})
.ReturnsAsync(new HttpResponseMessage()
{
StatusCode = System.Net.HttpStatusCode.OK
});


var clientFactory = serviceProvider.GetRequiredService<IHttpClientFactory>();
var client = clientFactory.CreateClient("user1");
var resp = await client.SendAsync(req, CancellationToken.None);

sink.Protected().As<HttpMessageInvoker>().Verify(o => o.SendAsync(It.Is<HttpRequestMessage>(r => r.RequestUri == config.AuthenticationAddress), It.IsAny<CancellationToken>()), Times.Once());
sink.Protected().As<HttpMessageInvoker>().Verify(o => o.SendAsync(It.Is<HttpRequestMessage>(r => r.RequestUri == req.RequestUri), It.IsAny<CancellationToken>()), Times.Once());
Assert.Equal("user1", user);
}
}
}
17 changes: 17 additions & 0 deletions tests/Autodesk.Forge.Core.Test/TestForgeConfiguration.cs
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,23 @@ public void TestValuesFromLegacyEnvironment()
Assert.Equal("blabla", config.Value.ClientSecret);
}

[Fact]
public void TestValuesFromAPSEnvironment()
{
Environment.SetEnvironmentVariable("APS_CLIENT_ID", "bla");
Environment.SetEnvironmentVariable("APS_CLIENT_SECRET", "blabla");
var configuration = new ConfigurationBuilder()
.AddAPSAlternativeEnvironmentVariables()
.Build();
var services = new ServiceCollection();
services.AddForgeService(configuration);
var serviceProvider = services.BuildServiceProvider();
var config = serviceProvider.GetRequiredService<IOptions<ForgeConfiguration>>();
Assert.Equal("bla", config.Value.ClientId);
Assert.Equal("blabla",config.Value.ClientSecret);

}

[Fact]
public void TestValuesFromJson()
{
Expand Down

0 comments on commit 10cbd08

Please sign in to comment.