Goffo.OracleFusion.Api.Core 3.4.1

dotnet add package Goffo.OracleFusion.Api.Core --version 3.4.1
                    
NuGet\Install-Package Goffo.OracleFusion.Api.Core -Version 3.4.1
                    
This command is intended to be used within the Package Manager Console in Visual Studio, as it uses the NuGet module's version of Install-Package.
<PackageReference Include="Goffo.OracleFusion.Api.Core" Version="3.4.1" />
                    
For projects that support PackageReference, copy this XML node into the project file to reference the package.
<PackageVersion Include="Goffo.OracleFusion.Api.Core" Version="3.4.1" />
                    
Directory.Packages.props
<PackageReference Include="Goffo.OracleFusion.Api.Core" />
                    
Project file
For projects that support Central Package Management (CPM), copy this XML node into the solution Directory.Packages.props file to version the package.
paket add Goffo.OracleFusion.Api.Core --version 3.4.1
                    
#r "nuget: Goffo.OracleFusion.Api.Core, 3.4.1"
                    
#r directive can be used in F# Interactive and Polyglot Notebooks. Copy this into the interactive tool or source code of the script to reference the package.
#:package Goffo.OracleFusion.Api.Core@3.4.1
                    
#:package directive can be used in C# file-based apps starting in .NET 10 preview 4. Copy this into a .cs file before any lines of code to reference the package.
#addin nuget:?package=Goffo.OracleFusion.Api.Core&version=3.4.1
                    
Install as a Cake Addin
#tool nuget:?package=Goffo.OracleFusion.Api.Core&version=3.4.1
                    
Install as a Cake Tool

Goffo.OracleFusion.Api.Core

Breaking Changes

v3.0.0.

1. The IFusionApi method PostAsync has been renamed to ExecuteAsync
2. The FusionApiEndpoints class property Post has been renamed to BaseUrl.  
    a. You will need to change Post to BaseUrl in appsettings.json or your in-line code specified options

How to use

Create an implementation for the IFusionApi<TRequest, TResponse>

TRequest is what will hold your request payload for the Oracle Fusion API.

TResponse is what will hold the return response from the Oracle Fusion API

Use the ITokenProvider to create an implementation to get a token

For multiple implementations you must create your own TokenHeaderHandler but you can use the AbstractTokenHeaderHandler as a base class and will need to pass your own Key/Name through the Base constructor to differentiate.

If using multiplt TokenProviders you must create you must inject each as a KeyedService

Examples

Appsettings.json - Single Implementation

{
  "FusionApiEndpoints": {
    "BaseUrl": "https://example.com/api/post"
  }
}

Appsettings.json - Multiple Implementations

{
  "FusionApiOneEndpoints": {
    "BaseUrl": "https://fusion-api-ONE-endpoint.com"
  },
  "FusionApiTwoEndpoints": {
    "BaseUrl": "https://fusion-api-TWO-endpoint.com"
  }
}

Code Example - Single Implementation

    internal class ApiExample
    {
        private readonly IServiceCollection _services;

        internal ApiExample()
        {
            // Dependency injection setup
            _services = new ServiceCollection();

            // Uses appsettings.json to configure the Fusion API endpoints
            // FusionHeaderHandler is your own class and you can inherit from the Abstracts:  AbstractTokenHeaderHandler or AbstractBasicAuthHeaderHandler
            _services.AddFusionApi<FusionApi, FusionRequest, FusionResponse, FusionHeaderHandler>();

            // Our configure in method
            // FusionHeaderHandler is your own class and you can inherit from the Abstracts:  AbstractTokenHeaderHandler or AbstractBasicAuthHeaderHandler
            _services.AddFusionApi<FusionApi, FusionRequest, FusionResponse, FusionHeaderHandler>((endpoints) =>
            {
                endpoints.BaseUrl = "https://api.example.com/fusion";
            });


            _services.AddHttpClient<ITokenProvider, TokenProvider>();
        }

        internal class FusionApi : IFusionApi<FusionRequest, FusionResponse>
        {
            // Inject HttpClient via constructor injection
            // This is setup in the AddFusionApi extension method
            private readonly HttpClient _client;
            private readonly ITokenProvider _tokenProvider;
            public FusionApi(HttpClient client, ITokenProvider tokenProvider)
            {
                _client = client;
                _tokenProvider = tokenProvider;
            }
            public async Task<FusionResponse> PostAsync(FusionRequest request, CancellationToken cancellationToken = default)
            {
                // Your code to handle the request and return a response...

                // For example you might need to get a token first
                Token token = await _tokenProvider.GetTokenAsync(cancellationToken);

                // Now you can make the actual API call
                var response = await _client.PostAsJsonAsync("https://api.example.com/fusion", request, cancellationToken);
                if (response.IsSuccessStatusCode)
                {
                    return await response.Content.ReadFromJsonAsync<FusionResponse>();
                }

                return new FusionResponse(IsSuccess: false);
            }
        }
        internal record FusionRequest(string Payload);
        internal record FusionResponse(bool IsSuccess);
        internal class TokenProvider : ITokenProvider
        {
            private readonly HttpClient _client;
            private Token _cachedToken = new("", "", 0, "");
            public TokenProvider(HttpClient client)
            {
                _client = client;
            }
            public async Task<Token> GetTokenAsync(CancellationToken cancellationToken = default)
            {
                // Your code to get a token....
                // You probably want to cache the token and handle expiration

                System.IdentityModel.Tokens.Jwt.JwtSecurityTokenHandler handler = new();
                System.IdentityModel.Tokens.Jwt.JwtSecurityToken securityToken = handler.ReadJwtToken(_cachedToken.AccessToken);
                object? tokenExp = null;
                DateTime expDateTime = DateTime.Now.AddDays(1);

                if (securityToken.Payload.TryGetValue("exp", out tokenExp))
                {
                    expDateTime = DateTimeOffset.FromUnixTimeSeconds(Convert.ToInt64(tokenExp)).LocalDateTime;
                    bool isValid = expDateTime < DateTime.Now.AddSeconds(-10);

                    if (isValid)
                    {
                        // Token is still valid, return cached token
                        return _cachedToken;
                    }
                }

                // You may want to make an API call
                Dictionary<string, string?> formUrlContent = new Dictionary<string, string?>
                {
                    { "grant_type", "client_credentials" },
                    { "scope", "your-scope" }
                };

                _client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Basic", Convert.ToBase64String(Encoding.UTF8.GetBytes("YourClientId:YourClientSecret")));
                var response = await _client.PostAsJsonAsync("https://api.example.com/fusion", new FormUrlEncodedContent(formUrlContent), cancellationToken);

                if (response.IsSuccessStatusCode)
                {
                    Token? token = await response.Content.ReadFromJsonAsync<Token>(cancellationToken);
                    _cachedToken = token ?? new Token(string.Empty, string.Empty, 0, string.Empty);
                    return token ?? new Token(string.Empty, string.Empty, 0, string.Empty);
                }

                throw new TokenException(response.StatusCode, response.ReasonPhrase, await response.Content.ReadAsStringAsync());
            }
        }
    }


Code Example - Mulitple Implementations


    internal class MulitApisExample
    {
        const string FusionApiOneEndpointsKey = "FusionApiOneEndpoints";
        const string FusionApiTwoEndpointsKey = "FusionApiTwoEndpoints";
        const string FusionApiOneTokenProviderKey = "FusionApiOneTokenProvider";
        const string FusionApiTwoTokenProviderKey = "FusionApiTwoTokenProvider";

        private readonly IServiceCollection _services;

        internal MulitApisExample()
        {
            // Dependency injection setup
            _services = new ServiceCollection();

            _services.AddKeyedTransient<ITokenProvider, TokenProviderOne>(FusionApiOneTokenProviderKey);
            _services.AddHttpClient<ITokenProvider, TokenProviderOne>();
            // Uses appsettings.json to configure the Fusion API endpoints
            _services.AddFusionApi<FusionApiOne, FusionRequestOne, FusionResponseOne, TokenHeaderHandlerOne>(FusionApiOneEndpointsKey);

            // Or configure in method
            //_services.AddFusionApi<FusionApiOne, FusionRequestOne, FusionResponseOne>((endpoints) =>
            //{
            //    endpoints.Post = "https://api.example.com/fusion-ONE";
            //}, FusionApiOneEndpointsKey);

            _services.AddKeyedTransient<ITokenProvider, TokenProviderTwo>(FusionApiTwoTokenProviderKey);
            _services.AddHttpClient<ITokenProvider, TokenProviderTwo>();
            // Uses appsettings.json to configure the Fusion API endpoints
            _services.AddFusionApi<FusionApiTwo, FusionRequestTwo, FusionResponseTwo, TokenHeaderHandlerTwo>(FusionApiTwoEndpointsKey);

            //// Or configure in method
            //_services.AddFusionApi<FusionApiOne, FusionRequestOne, FusionResponseOne>((endpoints) =>
            //{
            //    endpoints.Post = "https://api.example.com/fusion-TWO";
            //}, FusionApiTwoEndointsKey);
        }

        internal class FusionApiOne : IFusionApi<FusionRequestOne, FusionResponseOne>
        {
            // Inject HttpClient via constructor injection
            // This is setup in the AddFusionApi extension method
            private readonly HttpClient _client;
            private readonly ITokenProvider _tokenProvider;
            public FusionApiOne(HttpClient client, ITokenProvider tokenProvider)
            {
                _client = client;
                _tokenProvider = tokenProvider;
            }
            public async Task<FusionResponseOne> PostAsync(FusionRequestOne request, CancellationToken cancellationToken = default)
            {
                // Your code to handle the request and return a response...

                // For example you might need to get a token first
                Token token = await _tokenProvider.GetTokenAsync(cancellationToken);

                // use the token to set the Authorization header
                _client.DefaultRequestHeaders.Authorization = new System.Net.Http.Headers.AuthenticationHeaderValue(token.TokenType, token.AccessToken);

                // Now you can make the actual API call
                var response = await _client.PostAsJsonAsync("https://api.example.com/fusion", request, cancellationToken);
                
                if (response.IsSuccessStatusCode)
                {
                    return await response.Content.ReadFromJsonAsync<FusionResponseOne>(cancellationToken);
                }

                return new FusionResponseOne(IsSuccess: false);
            }
        }
        internal record FusionRequestOne(string Payload);
        internal record FusionResponseOne(bool IsSuccess);
        internal class TokenHeaderHandlerOne : AbstractTokenHeaderHandler
        {
            public TokenHeaderHandlerOne(IServiceProvider serviceProvider, string? tokenProviderKey = null) : base(serviceProvider, MulitApisExample.FusionApiOneTokenProviderKey)
            {
            }
        }
        internal class TokenProviderOne : ITokenProvider
        {
            private readonly HttpClient _client;
            private Token _cachedToken = new("", "", 0, "");
            public TokenProviderOne(HttpClient client)
            {
                _client = client;
            }
            public async Task<Token> GetTokenAsync(CancellationToken cancellationToken = default)
            {
                // Your code to get a token....
                // You probably want to cache the token and handle expiration

                System.IdentityModel.Tokens.Jwt.JwtSecurityTokenHandler handler = new();
                System.IdentityModel.Tokens.Jwt.JwtSecurityToken securityToken = handler.ReadJwtToken(_cachedToken.AccessToken);
                object? tokenExp = null;
                DateTime expDateTime = DateTime.Now.AddDays(1);

                if (securityToken.Payload.TryGetValue("exp", out tokenExp))
                {
                    expDateTime = DateTimeOffset.FromUnixTimeSeconds(Convert.ToInt64(tokenExp)).LocalDateTime;
                    bool isValid = expDateTime < DateTime.Now.AddSeconds(-10);

                    if (isValid)
                    {
                        // Token is still valid, return cached token
                        return _cachedToken;
                    }
                }

                // You may want to make an API call
                Dictionary<string, string?> formUrlContent = new Dictionary<string, string?>
                {
                    { "grant_type", "client_credentials" },
                    { "scope", "your-scope-ONE" }
                };

                _client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Basic", Convert.ToBase64String(Encoding.UTF8.GetBytes("YourClientIdONE:YourClientSecretONE")));
                var response = await _client.PostAsJsonAsync("https://api.example.com/fusion", new FormUrlEncodedContent(formUrlContent), cancellationToken);

                if (response.IsSuccessStatusCode)
                {
                    Token? token = await response.Content.ReadFromJsonAsync<Token>(cancellationToken);
                    _cachedToken = token ?? new Token(string.Empty, string.Empty, 0, string.Empty);
                    return token ?? new Token(string.Empty, string.Empty, 0, string.Empty);
                }

                throw new TokenException(response.StatusCode, response.ReasonPhrase, await response.Content.ReadAsStringAsync());
            }
        }

        internal class FusionApiTwo : IFusionApi<FusionRequestTwo, FusionResponseTwo>
        {
            // Inject HttpClient via constructor injection
            // This is setup in the AddFusionApi extension method
            private readonly HttpClient _client;
            private readonly ITokenProvider _tokenProvider;
            public FusionApiTwo(HttpClient client, ITokenProvider tokenProvider)
            {
                _client = client;
                _tokenProvider = tokenProvider;
            }
            public async Task<FusionResponseTwo> PostAsync(FusionRequestTwo request, CancellationToken cancellationToken = default)
            {
                // Your code to handle the request and return a response...

                // For example you might need to get a token first
                Token token = await _tokenProvider.GetTokenAsync(cancellationToken);

                // use the token to set the Authorization header
                _client.DefaultRequestHeaders.Authorization = new System.Net.Http.Headers.AuthenticationHeaderValue(token.TokenType, token.AccessToken);

                // Now you can make the actual API call
                var response = await _client.PostAsJsonAsync("https://api.example.com/fusion", request, cancellationToken);

                if (response.IsSuccessStatusCode)
                {
                    return await response.Content.ReadFromJsonAsync<FusionResponseTwo>(cancellationToken);
                }

                return new FusionResponseTwo(IsSuccess: false);
            }
        }
        internal record FusionRequestTwo(string Payload);
        internal record FusionResponseTwo(bool IsSuccess);
        internal class TokenHeaderHandlerTwo : AbstractTokenHeaderHandler
        {
            public TokenHeaderHandlerTwo(IServiceProvider serviceProvider, string? tokenProviderKey = null) : base(serviceProvider, MulitApisExample.FusionApiTwoTokenProviderKey)
            {
            }
        }
        internal class TokenProviderTwo : ITokenProvider
        {
            private readonly HttpClient _client;
            private Token _cachedToken = new("", "", 0, "");
            public TokenProviderTwo(HttpClient client)
            {
                _client = client;
            }
            public async Task<Token> GetTokenAsync(CancellationToken cancellationToken = default)
            {
                // Your code to get a token....
                // You probably want to cache the token and handle expiration

                System.IdentityModel.Tokens.Jwt.JwtSecurityTokenHandler handler = new();
                System.IdentityModel.Tokens.Jwt.JwtSecurityToken securityToken = handler.ReadJwtToken(_cachedToken.AccessToken);
                object? tokenExp = null;
                DateTime expDateTime = DateTime.Now.AddDays(1);

                if (securityToken.Payload.TryGetValue("exp", out tokenExp))
                {
                    expDateTime = DateTimeOffset.FromUnixTimeSeconds(Convert.ToInt64(tokenExp)).LocalDateTime;
                    bool isValid = expDateTime < DateTime.Now.AddSeconds(-10);

                    if (isValid)
                    {
                        // Token is still valid, return cached token
                        return _cachedToken;
                    }
                }

                // You may want to make an API call
                Dictionary<string, string?> formUrlContent = new Dictionary<string, string?>
                {
                    { "grant_type", "client_credentials" },
                    { "scope", "your-scope-ONE" }
                };

                _client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Basic", Convert.ToBase64String(Encoding.UTF8.GetBytes("YourClientIdONE:YourClientSecretONE")));
                var response = await _client.PostAsJsonAsync("https://api.example.com/fusion", new FormUrlEncodedContent(formUrlContent), cancellationToken);

                if (response.IsSuccessStatusCode)
                {
                    Token? token = await response.Content.ReadFromJsonAsync<Token>(cancellationToken);
                    _cachedToken = token ?? new Token(string.Empty, string.Empty, 0, string.Empty);
                    return token ?? new Token(string.Empty, string.Empty, 0, string.Empty);
                }

                throw new TokenException(response.StatusCode, response.ReasonPhrase, await response.Content.ReadAsStringAsync());
            }
        }
    }

Code Example - Custom API Inteface

// Create your own interface
// Optional to inherit from IFusionApi<TRequest, TResponse>
// Create an implementation for the interface

public interface ICustomFusionApi : IFusionApi<FusionRequest, FusionResponse>
{
}

Can register the custom interface and implementation the same way as examples above

services.AddCustomFusionApi<ICustomFusionApi, CustomFusionApi>();

Example Setup for Multiple API Endpoints

App.cs

public class App
{
    private readonly IHost _host;

    public App()
    {
        _host = Host.CreateDefaultBuilder()
            .ConfigureServices((context, services) =>
            {
                // Add Token Provider (if required)
                services.AddHttpClient<ITokenProvider, TokenProvider>();

                // Uses the Default Endpoint Key for appsettings.json => "FusionApiEndpoints"
                // FusionHeaderHandler is your own class and you can inherit from the Abstracts:  AbstractTokenHeaderHandler or AbstractBasicAuthHeaderHandler
                services.AddFusionApi<FusionGetApi, FusionGetRequest, FusionGetResponse, FusionHeaderHandler>();
                services.AddFusionApi<FusionPostApi, FusionPostRequest, FusionPostResponse, FusionHeaderHandler>();

            }).Build();

        FusionGetApi = _host.Services.GetRequiredService<IFusionApi<FusionGetRequest, FusionGetResponse>>();
        FusionPostApi = _host.Services.GetRequiredService<IFusionApi<FusionPostRequest, FusionPostResponse>>();

    }

    public IFusionApi<FusionGetRequest, FusionGetResponse> FusionGetApi { get; init; }
    public IFusionApi<FusionPostRequest, FusionPostResponse> FusionPostApi { get; init; }
}

Program.cs
using Goffo.OracleFusion.Api.Core.DemoTwo.Console;
using Goffo.OracleFusion.Api.Core.DemoTwo.Console.Get;
using Goffo.OracleFusion.Api.Core.DemoTwo.Console.Post;

try
{
    Console.WriteLine("Starting App...");
    Console.WriteLine();

    var app = new App();

    var getResponse = await app.FusionGetApi.ExecuteAsync(new FusionGetRequest());
    var postResponse = await app.FusionPostApi.ExecuteAsync(new FusionPostRequest());

    Console.WriteLine($"GET Response: {getResponse.Message}");
    Console.WriteLine();
    Console.WriteLine($"POST Response: {postResponse.Message}");

    Console.WriteLine();
    Console.WriteLine("DONE!");
}
catch (Exception ex)
{
    Console.WriteLine(ex);
}
Product Compatible and additional computed target framework versions.
.NET net9.0 is compatible.  net9.0-android was computed.  net9.0-browser was computed.  net9.0-ios was computed.  net9.0-maccatalyst was computed.  net9.0-macos was computed.  net9.0-tvos was computed.  net9.0-windows was computed.  net10.0 is compatible.  net10.0-android was computed.  net10.0-browser was computed.  net10.0-ios was computed.  net10.0-maccatalyst was computed.  net10.0-macos was computed.  net10.0-tvos was computed.  net10.0-windows was computed. 
Compatible target framework(s)
Included target framework(s) (in package)
Learn more about Target Frameworks and .NET Standard.

NuGet packages (2)

Showing the top 2 NuGet packages that depend on Goffo.OracleFusion.Api.Core:

Package Downloads
Goffo.OracleFusion.Api.OAuth2

Provides helper method for OAuth2

Goffo.OracleFusion.Api.BasicAuth

Extension to add Basic Auth

GitHub repositories

This package is not used by any popular GitHub repositories.

Version Downloads Last Updated
3.4.1 105 1/28/2026
3.4.0 80 1/28/2026
3.3.1 83 1/27/2026
3.3.0 103 1/23/2026
3.2.0 88 1/23/2026
3.1.0 85 1/23/2026
3.0.0 99 1/23/2026
2.0.0 302 11/21/2025
1.4.0 310 11/21/2025
1.3.0 187 10/6/2025
1.2.1 268 9/19/2025
1.2.0 239 9/19/2025
1.1.0 232 7/7/2025
1.0.0 137 7/5/2025

Needed to added Scheme 'Basic' to the AuthenticationHeaderValue for the AbstractBasicAuthHeaderHandler