Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: changes OdataRepository implementation to obtain context service authentication token. #258

Merged
merged 1 commit into from
Jul 10, 2024
Merged
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
4 changes: 2 additions & 2 deletions src/Liquid.Core/Liquid.Core.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@
<Copyright>Avanade 2019</Copyright>
<PackageProjectUrl>https://github.com/Avanade/Liquid-Application-Framework</PackageProjectUrl>
<PackageIcon>logo.png</PackageIcon>
<Version>8.0.0-beta-06</Version>
<Version>8.0.0-beta-07</Version>
<GenerateDocumentationFile>true</GenerateDocumentationFile>
<ProjectGuid>{C33A89FC-4F4D-4274-8D0F-29456BA8F76B}</ProjectGuid>
<IsPackable>true</IsPackable>
Expand All @@ -28,7 +28,7 @@
<PackageReference Include="Microsoft.Extensions.Logging.Abstractions" Version="8.0.1" />
<PackageReference Include="System.Memory.Data" Version="8.0.0" />
<PackageReference Include="System.Net.Http" Version="4.3.4" />
<PackageReference Include="System.Text.Json" Version="8.0.3" />
<PackageReference Include="System.Text.Json" Version="8.0.4" />
<PackageReference Include="Microsoft.Extensions.Caching.Abstractions" Version="8.0.0" />
<PackageReference Include="Microsoft.Extensions.Hosting.Abstractions" Version="8.0.0" />
<PackageReference Include="System.Text.RegularExpressions" Version="4.3.1" />
Expand Down

This file was deleted.

Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
using Liquid.Core.Entities;
using Liquid.Core.Extensions.DependencyInjection;
using Liquid.Core.Implementations;
using Liquid.Core.Interfaces;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
Expand Down Expand Up @@ -30,6 +31,7 @@ public static IServiceCollection AddLiquidOdataRepository<TEntity, TIdentifier>(
where TEntity : LiquidEntity<TIdentifier>, new()
{
services.TryAddSingleton<IODataClientFactory, ODataClientFactory>();
services.TryAddSingleton<ILiquidContext, LiquidContext>();

services.AddOptions<ODataOptions>()
.Configure<IConfiguration>((settings, configuration) =>
Expand Down
3 changes: 1 addition & 2 deletions src/Liquid.Repository.OData/IODataClientFactory.cs
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,6 @@ public interface IODataClientFactory
/// Create an OData client.
/// </summary>
/// <param name="entityName">The entity name.</param>
/// <param name="token">Authorization token.</param>
IODataClient CreateODataClientAsync(string entityName, string token);
IODataClient CreateODataClientAsync(string entityName);
}
}
2 changes: 1 addition & 1 deletion src/Liquid.Repository.OData/Liquid.Repository.OData.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@
<Copyright>Avanade 2019</Copyright>
<PackageProjectUrl>https://github.com/Avanade/Liquid-Application-Framework</PackageProjectUrl>
<PackageIcon>logo.png</PackageIcon>
<Version>8.0.0-beta-02</Version>
<Version>8.0.0-beta-03</Version>
<GenerateDocumentationFile>true</GenerateDocumentationFile>
<IsPackable>true</IsPackable>
<DebugType>Full</DebugType>
Expand Down
18 changes: 15 additions & 3 deletions src/Liquid.Repository.OData/ODataClientFactory.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
using Microsoft.Extensions.Options;
using Liquid.Core.Interfaces;
using Microsoft.Extensions.Options;
using Simple.OData.Client;

namespace Liquid.Repository.OData
Expand All @@ -7,20 +8,31 @@
public class ODataClientFactory : IODataClientFactory
{
private readonly IOptions<ODataOptions> _options;
private readonly ILiquidContext _context;

/// <summary>
/// Initialize a new instance of <see cref="ODataClientFactory"/>
/// </summary>
/// <param name="options"></param>
public ODataClientFactory(IOptions<ODataOptions> options)
/// <param name="context"></param>
public ODataClientFactory(IOptions<ODataOptions> options, ILiquidContext context)
{
_options = options ?? throw new ArgumentNullException(nameof(options));
_context = context ?? throw new ArgumentNullException(nameof(context));
}

///<inheritdoc/>
public IODataClient CreateODataClientAsync(string entityName, string token)
public IODataClient CreateODataClientAsync(string entityName)
{
var token = _context.Get("OdataToken").ToString();

if (string.IsNullOrEmpty(token))
{
throw new InvalidOperationException("Token is required to perform this operation. The 'OdataToken' variable" +
" must be declared in the LiquidContext service.");
}

var settings = _options?.Value?.Settings.FirstOrDefault(x => x.EntityName == entityName);

Check warning on line 35 in src/Liquid.Repository.OData/ODataClientFactory.cs

View workflow job for this annotation

GitHub Actions / call-reusable-build-workflow / build

"Find" method should be used instead of the "FirstOrDefault" extension method. (https://rules.sonarsource.com/csharp/RSPEC-6602)

if (settings == null)
throw new ArgumentOutOfRangeException(nameof(entityName));
Expand All @@ -38,7 +50,7 @@
/// <returns></returns>
private static ODataClientSettings GetODataSettings(ODataSettings? settings, string token)
{
var odataSettings = new ODataClientSettings(new Uri(settings.BaseUrl));

Check warning on line 53 in src/Liquid.Repository.OData/ODataClientFactory.cs

View workflow job for this annotation

GitHub Actions / call-reusable-build-workflow / build

Dereference of a possibly null reference.

odataSettings.BeforeRequest = (message) =>
{
Expand All @@ -47,11 +59,11 @@

if (!settings.ValidateCert)
{
var handler = new HttpClientHandler();

Check warning on line 62 in src/Liquid.Repository.OData/ODataClientFactory.cs

View workflow job for this annotation

GitHub Actions / call-reusable-build-workflow / build

Remove the unused local variable 'handler'. (https://rules.sonarsource.com/csharp/RSPEC-1481)

odataSettings.OnApplyClientHandler = (handler) =>
{
handler.ServerCertificateCustomValidationCallback +=

Check warning on line 66 in src/Liquid.Repository.OData/ODataClientFactory.cs

View workflow job for this annotation

GitHub Actions / call-reusable-build-workflow / build

Enable server certificate validation on this SSL/TLS connection (https://rules.sonarsource.com/csharp/RSPEC-4830)
(sender, certificate, chain, errors) =>
{
return true;
Expand Down
53 changes: 7 additions & 46 deletions src/Liquid.Repository.OData/ODataRepository.cs
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
using Liquid.Core.Entities;
using Liquid.Core.Interfaces;
using Simple.OData.Client;
using System.Linq.Expressions;

namespace Liquid.Repository.OData
Expand All @@ -13,7 +12,6 @@

private readonly IODataClientFactory _clientFactory;
private readonly string _entityName;
private string _token;

/// <summary>
/// Initialize a new instance of <see cref="ODataRepository{TEntity, TIdentifier}"/>
Expand All @@ -27,88 +25,51 @@
_entityName = entityName ?? throw new ArgumentNullException(nameof(entityName));
}

/// <summary>
/// Set the token to perform operations.
/// </summary>
/// <param name="token">Token to be set.</param>
public void SetToken(string token)
{
_token = token;
}

///<inheritdoc/>
public async Task AddAsync(TEntity entity)
{
if (string.IsNullOrEmpty(_token))
{
throw new InvalidOperationException("Token is required to perform this operation.");
}

var client = _clientFactory.CreateODataClientAsync(_entityName, _token);
{
var client = _clientFactory.CreateODataClientAsync(_entityName);

await client.For<TEntity>().Set(entity).InsertEntryAsync();
}

///<inheritdoc/>
public async Task<IEnumerable<TEntity>> FindAllAsync()
{
if (string.IsNullOrEmpty(_token))
{
throw new InvalidOperationException("Token is required to perform this operation.");
}

var client = _clientFactory.CreateODataClientAsync(_entityName, _token);
var client = _clientFactory.CreateODataClientAsync(_entityName);

return await client.For<TEntity>().FindEntriesAsync();
}

///<inheritdoc/>
public async Task<TEntity> FindByIdAsync(TIdentifier id)
{
if (string.IsNullOrEmpty(_token))
{
throw new InvalidOperationException("Token is required to perform this operation.");
}
var client = _clientFactory.CreateODataClientAsync(_entityName, _token);
var client = _clientFactory.CreateODataClientAsync(_entityName);

return await client.For<TEntity>().Key(id).FindEntryAsync();

Check warning on line 50 in src/Liquid.Repository.OData/ODataRepository.cs

View workflow job for this annotation

GitHub Actions / call-reusable-build-workflow / build

Possible null reference argument for parameter 'entryKey' in 'IBoundClient<TEntity> IFluentClient<TEntity, IBoundClient<TEntity>>.Key(params object[] entryKey)'.

Check warning on line 50 in src/Liquid.Repository.OData/ODataRepository.cs

View workflow job for this annotation

GitHub Actions / call-reusable-build-workflow / build

Review this call, which partially matches an overload without 'params'. The partial match is 'Simple.OData.Client.IBoundClient<TEntity> Simple.OData.Client.IFluentClient<TEntity, Simple.OData.Client.IBoundClient<TEntity>>.Key(IEnumerable<object> entryKey)'. (https://rules.sonarsource.com/csharp/RSPEC-3220)

Check warning on line 50 in src/Liquid.Repository.OData/ODataRepository.cs

View workflow job for this annotation

GitHub Actions / call-reusable-build-workflow / build

Possible null reference argument for parameter 'entryKey' in 'IBoundClient<TEntity> IFluentClient<TEntity, IBoundClient<TEntity>>.Key(params object[] entryKey)'.
}

///<inheritdoc/>
public async Task RemoveByIdAsync(TIdentifier id)
{
if (string.IsNullOrEmpty(_token))
{
throw new InvalidOperationException("Token is required to perform this operation.");
}

var client = _clientFactory.CreateODataClientAsync(_entityName, _token);
var client = _clientFactory.CreateODataClientAsync(_entityName);

await client.For<TEntity>().Key(id).DeleteEntryAsync();

Check warning on line 58 in src/Liquid.Repository.OData/ODataRepository.cs

View workflow job for this annotation

GitHub Actions / call-reusable-build-workflow / build

Possible null reference argument for parameter 'entryKey' in 'IBoundClient<TEntity> IFluentClient<TEntity, IBoundClient<TEntity>>.Key(params object[] entryKey)'.

Check warning on line 58 in src/Liquid.Repository.OData/ODataRepository.cs

View workflow job for this annotation

GitHub Actions / call-reusable-build-workflow / build

Review this call, which partially matches an overload without 'params'. The partial match is 'Simple.OData.Client.IBoundClient<TEntity> Simple.OData.Client.IFluentClient<TEntity, Simple.OData.Client.IBoundClient<TEntity>>.Key(IEnumerable<object> entryKey)'. (https://rules.sonarsource.com/csharp/RSPEC-3220)

Check warning on line 58 in src/Liquid.Repository.OData/ODataRepository.cs

View workflow job for this annotation

GitHub Actions / call-reusable-build-workflow / build

Possible null reference argument for parameter 'entryKey' in 'IBoundClient<TEntity> IFluentClient<TEntity, IBoundClient<TEntity>>.Key(params object[] entryKey)'.
}

///<inheritdoc/>
public async Task UpdateAsync(TEntity entity)
{
if (string.IsNullOrEmpty(_token))
{
throw new InvalidOperationException("Token is required to perform this operation.");
}

var client = _clientFactory.CreateODataClientAsync(_entityName, _token);
var client = _clientFactory.CreateODataClientAsync(_entityName);

await client.For<TEntity>().Set(entity).UpdateEntryAsync();
}

///<inheritdoc/>
public async Task<IEnumerable<TEntity>> WhereAsync(Expression<Func<TEntity, bool>> whereClause)
{
if (string.IsNullOrEmpty(_token))
{
throw new InvalidOperationException("Token is required to perform this operation.");
}

var client = _clientFactory.CreateODataClientAsync(_entityName, _token);
var client = _clientFactory.CreateODataClientAsync(_entityName);

return await client.For<TEntity>().Filter(whereClause).FindEntriesAsync();
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
using Liquid.Core.Interfaces;
using Liquid.Core.Implementations;
using Liquid.Core.Interfaces;
using Liquid.Repository.OData.Extensions;
using Liquid.Repository.OData.Tests.Mock;
using Microsoft.Extensions.Configuration;
Expand Down
49 changes: 43 additions & 6 deletions test/Liquid.Repository.OData.Tests/ODataClientFactoryTests.cs
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
using Liquid.Core.Interfaces;
using Microsoft.Extensions.Options;
using NSubstitute;

Expand All @@ -7,6 +8,7 @@ public class ODataClientFactoryTests
{
private IODataClientFactory _sut;
private IOptions<ODataOptions> _options;
private ILiquidContext _context;

public ODataClientFactoryTests()
{
Expand All @@ -26,26 +28,29 @@ public ODataClientFactoryTests()

_options.Value.Returns(settings);

_sut = new ODataClientFactory(_options);
_context = Substitute.For<ILiquidContext>();
_context.Get("OdataToken").Returns("token");

_sut = new ODataClientFactory(_options, _context);
}


[Fact]
public void ODataClientFactory_WhenEntityNameIsNotFound_ThrowException()
{
Assert.Throws<ArgumentOutOfRangeException>(() => _sut.CreateODataClientAsync("TestEntities2", "token"));
Assert.Throws<ArgumentOutOfRangeException>(() => _sut.CreateODataClientAsync("TestEntities2"));
}

[Fact]
public void ODataClientFactory_WhenEntityNameIsNull_ThrowException()
{
Assert.Throws<ArgumentOutOfRangeException>(() => _sut.CreateODataClientAsync(null, "token"));
Assert.Throws<ArgumentOutOfRangeException>(() => _sut.CreateODataClientAsync(null));
}

[Fact]
public void OdataClientFactory_WhenValidateCertIsFalse_ReturnClient()
{
var client = _sut.CreateODataClientAsync("TestEntities", "token");
var client = _sut.CreateODataClientAsync("TestEntities");

Assert.NotNull(client);
}
Expand All @@ -65,12 +70,44 @@ public void OdataClientFactory_WhenValidateCertIsTrue_ReturnClient()
Settings = new List<ODataSettings>() { settings }
});

var sut = new ODataClientFactory(_options);
var sut = new ODataClientFactory(_options, _context);

var client = sut.CreateODataClientAsync("TestEntities", "token");
var client = sut.CreateODataClientAsync("TestEntities");

Assert.NotNull(client);
}

[Fact]
public void OdataClientFactory_WhenTokenIsNotSet_ThrowException()
{
_context.Get("OdataToken").Returns("");

Assert.Throws<InvalidOperationException>(() => _sut.CreateODataClientAsync("TestEntities"));
}
[Fact]
public void OdataClientFactory_WhenTokenIsNull_ThrowException()
{
var context = Substitute.For<ILiquidContext>();

var sut = new ODataClientFactory(_options, context);

Assert.Throws<NullReferenceException>(() => sut.CreateODataClientAsync("TestEntities"));
}

[Fact]
public void OdataClientFactory_WhenOptionsIsNull_ThrowException()
{
_options = null;

Assert.Throws<ArgumentNullException>(() => new ODataClientFactory(_options, _context));
}

[Fact]
public void OdataClientFactory_WhenContextIsNull_ThrowException()
{
_context = null;

Assert.Throws<ArgumentNullException>(() => new ODataClientFactory(_options, _context));
}
}
}
Loading
Loading