Skip to content

Commit

Permalink
Merge pull request #9 from whippet71/main
Browse files Browse the repository at this point in the history
Fixes #8 - Allow CurrencyExchange section to be either a single item or an array, and add element for InstructedAmount
  • Loading branch information
RobinTTY authored Feb 19, 2024
2 parents 8addcc5 + 4fcc657 commit dbe0724
Show file tree
Hide file tree
Showing 5 changed files with 91 additions and 9 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -10,20 +10,48 @@ namespace RobinTTY.NordigenApiClient.Tests.Serialization;
internal class TransactionTests
{
/// <summary>
/// Tests the correct deserialization of transactions.
/// Tests the correct deserialization of transactions with a single embedded currency exchange object.
/// </summary>
[Test]
public void DeserializeTransaction()
public void DeserializeTransactionWithSingleCurrencyExchange()
{
const string json =
"{ \"transactionId\": \"AB123456789\", \"entryReference\": \"123456789\", \"bookingDate\": \"2023-03-20\", \"bookingDateTime\": \"2023-03-20T00:00:00+00:00\", \"transactionAmount\": { \"amount\": \"-33.06\", \"currency\": \"GBP\" }, \"currencyExchange\": { \"sourceCurrency\": \"USD\", \"exchangeRate\": \"1.20961887\", \"unitCurrency\": \"USD\", \"targetCurrency\": \"GBP\", \"instructedAmount\": { \"amount\": \"-33.06\", \"currency\": \"GBP\" } }, \"remittanceInformationUnstructured\": \"my reference here\", \"additionalInformation\": \"123456789\", \"proprietaryBankTransactionCode\": \"OTHER_PURCHASE\", \"merchantCategoryCode\": \"5045\", \"internalTransactionId\": \"abcdef\" }";

var options = new JsonSerializerOptions
{
Converters = {new CultureSpecificDecimalConverter()}
};
var transaction = JsonSerializer.Deserialize<Transaction>(json, options);

Assert.Multiple(() =>
{
Assert.That(transaction!.CurrencyExchange, Is.Not.Null);
Assert.That(transaction.CurrencyExchange!.First().ExchangeRate, Is.EqualTo(1.20961887));
Assert.That(transaction.CurrencyExchange!.First().InstructedAmount!.Amount, Is.EqualTo(-33.06));
Assert.That(transaction.CurrencyExchange!.First().InstructedAmount!.Currency, Is.EqualTo("GBP"));
Assert.That(transaction.CurrencyExchange!.First().SourceCurrency, Is.EqualTo("USD"));
Assert.That(transaction.CurrencyExchange!.First().TargetCurrency, Is.EqualTo("GBP"));
Assert.That(transaction.CurrencyExchange!.First().UnitCurrency, Is.EqualTo("USD"));
Assert.That(transaction.CurrencyExchange!.First().QuotationDate, Is.Null);
});
}

/// <summary>
/// Tests the correct deserialization of transactions with multiple embedded currency exchange objects.
/// </summary>
[Test]
public void DeserializeTransactionWithMultipleCurrencyExchange()
{
const string json =
"{ \"transactionId\": \"AB123456789\", \"entryReference\": \"123456789\", \"bookingDate\": \"2023-03-20\", \"bookingDateTime\": \"2023-03-20T00:00:00+00:00\", \"transactionAmount\": { \"amount\": \"-33.06\", \"currency\": \"GBP\" }, \"currencyExchange\":[{\"sourceCurrency\":\"USD\",\"exchangeRate\":\"1.20961887\",\"unitCurrency\":\"USD\",\"targetCurrency\":\"GBP\"}], \"remittanceInformationUnstructured\": \"my reference here\", \"additionalInformation\": \"123456789\", \"proprietaryBankTransactionCode\": \"OTHER_PURCHASE\", \"merchantCategoryCode\": \"5045\", \"internalTransactionId\": \"abcdef\" }";

// We need the culture specific decimal converter here, since it it accepting strings
var options = new JsonSerializerOptions
{
Converters = {new JsonWebTokenConverter(), new GuidConverter(), new CultureSpecificDecimalConverter()}
Converters = { new CultureSpecificDecimalConverter() }
};
var transaction = JsonSerializer.Deserialize<Transaction>(json, options);

Assert.Multiple(() =>
{
Assert.That(transaction!.CurrencyExchange, Is.Not.Null);
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
using System.Text.Json;
using System.Text.Json.Serialization;

namespace RobinTTY.NordigenApiClient.JsonConverters;

internal class SingleOrArrayConverter<TCollection, TItem> : JsonConverter<TCollection>
where TCollection : class, ICollection<TItem>, new()
{
public override TCollection? Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
{
switch (reader.TokenType)
{
case JsonTokenType.Null:
return null;
case JsonTokenType.StartArray:
var list = new TCollection();
while (reader.Read())
{
if (reader.TokenType == JsonTokenType.EndArray) break;
var listItem = JsonSerializer.Deserialize<TItem>(ref reader, options);
if (listItem != null) list.Add(listItem);
}
return list;
default:
var item = JsonSerializer.Deserialize<TItem>(ref reader, options);
return item != null ? new TCollection {item} : null;
}
}

public override void Write(Utf8JsonWriter writer, TCollection value, JsonSerializerOptions options)
{
if (value.Count == 1)
JsonSerializer.Serialize(writer, value.First(), options);
else
{
writer.WriteStartArray();
foreach (var item in value)
JsonSerializer.Serialize(writer, item, options);
writer.WriteEndArray();
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -30,4 +30,4 @@ public AmountCurrencyPair(decimal amount, string currency)
Amount = amount;
Currency = currency;
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,12 @@ namespace RobinTTY.NordigenApiClient.Models.Responses;
/// </summary>
public class CurrencyExchange
{
/// <summary>
/// The instructed amount including details about the currency the amount is denominated in.
/// </summary>
[JsonPropertyName("instructedAmount")]
public AmountCurrencyPair? InstructedAmount { get; }

/// <summary>
/// Currency from which an amount is to be converted in a currency conversion. ISO 4217 Alpha 3 currency code (e.g.
/// "USD").
Expand Down Expand Up @@ -51,6 +57,9 @@ public class CurrencyExchange
/// <summary>
/// Creates a new instance of <see cref="CurrencyExchange" />.
/// </summary>
/// <param name="instructedAmount">
/// The currency and amount as instructed by the debtor, if the actual settlement is of a different currency or amount
/// </param>
/// <param name="sourceCurrency">
/// Currency from which an amount is to be converted in a currency conversion. ISO 4217 Alpha
/// 3 currency code (e.g. "USD").
Expand All @@ -70,9 +79,10 @@ public class CurrencyExchange
/// </param>
/// <param name="quotationDate">Date at which an exchange rate is quoted.</param>
/// <param name="contractIdentification">Unique identification to unambiguously identify the foreign exchange contract.</param>
public CurrencyExchange(string sourceCurrency, string targetCurrency, string unitCurrency, decimal exchangeRate,
public CurrencyExchange(AmountCurrencyPair? instructedAmount, string sourceCurrency, string targetCurrency, string unitCurrency, decimal exchangeRate,
DateTime? quotationDate, string? contractIdentification)
{
InstructedAmount = instructedAmount;
SourceCurrency = sourceCurrency;
TargetCurrency = targetCurrency;
UnitCurrency = unitCurrency;
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
using System.Text.Json.Serialization;
using RobinTTY.NordigenApiClient.JsonConverters;
using System.Text.Json.Serialization;

namespace RobinTTY.NordigenApiClient.Models.Responses;

Expand Down Expand Up @@ -218,7 +219,8 @@ public class Transaction
/// Array of the report exchange rate.
/// </summary>
[JsonPropertyName("currencyExchange")]
public IEnumerable<CurrencyExchange>? CurrencyExchange { get; }
[JsonConverter(typeof(SingleOrArrayConverter<List<CurrencyExchange>, CurrencyExchange>))]
public List<CurrencyExchange>? CurrencyExchange { get; }

/// <summary>
/// The identification of the transaction as used for reference by the financial institution.
Expand Down Expand Up @@ -343,7 +345,7 @@ public Transaction(string? transactionId, string? debtorName, MinimalBankAccount
DateTime? valueDateTime, string? remittanceInformationStructured,
IEnumerable<string>? remittanceInformationStructuredArray, string? additionalInformation,
string? additionalInformationStructured, Balance? balanceAfterTransaction, string? checkId,
IEnumerable<CurrencyExchange>? currencyExchange, string? entryReference, string? internalTransactionId,
List<CurrencyExchange>? currencyExchange, string? entryReference, string? internalTransactionId,
string? merchantCategoryCode, DateTime? bookingDateTime)
{
TransactionId = transactionId;
Expand Down

0 comments on commit dbe0724

Please sign in to comment.