diff --git a/src/RobinTTY.NordigenApiClient.Tests/Serialization/TransactionTests.cs b/src/RobinTTY.NordigenApiClient.Tests/Serialization/TransactionTests.cs index 291e11f..431eba6 100644 --- a/src/RobinTTY.NordigenApiClient.Tests/Serialization/TransactionTests.cs +++ b/src/RobinTTY.NordigenApiClient.Tests/Serialization/TransactionTests.cs @@ -10,20 +10,48 @@ namespace RobinTTY.NordigenApiClient.Tests.Serialization; internal class TransactionTests { /// - /// Tests the correct deserialization of transactions. + /// Tests the correct deserialization of transactions with a single embedded currency exchange object. /// [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(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); + }); + } + + /// + /// Tests the correct deserialization of transactions with multiple embedded currency exchange objects. + /// + [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(json, options); + Assert.Multiple(() => { Assert.That(transaction!.CurrencyExchange, Is.Not.Null); diff --git a/src/RobinTTY.NordigenApiClient/JsonConverters/SingleOrArrayConverter.cs b/src/RobinTTY.NordigenApiClient/JsonConverters/SingleOrArrayConverter.cs new file mode 100644 index 0000000..7feca25 --- /dev/null +++ b/src/RobinTTY.NordigenApiClient/JsonConverters/SingleOrArrayConverter.cs @@ -0,0 +1,42 @@ +using System.Text.Json; +using System.Text.Json.Serialization; + +namespace RobinTTY.NordigenApiClient.JsonConverters; + +internal class SingleOrArrayConverter : JsonConverter + where TCollection : class, ICollection, 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(ref reader, options); + if (listItem != null) list.Add(listItem); + } + return list; + default: + var item = JsonSerializer.Deserialize(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(); + } + } +} \ No newline at end of file diff --git a/src/RobinTTY.NordigenApiClient/Models/Responses/AmountCurrencyPair.cs b/src/RobinTTY.NordigenApiClient/Models/Responses/AmountCurrencyPair.cs index cf30676..5458c5e 100644 --- a/src/RobinTTY.NordigenApiClient/Models/Responses/AmountCurrencyPair.cs +++ b/src/RobinTTY.NordigenApiClient/Models/Responses/AmountCurrencyPair.cs @@ -30,4 +30,4 @@ public AmountCurrencyPair(decimal amount, string currency) Amount = amount; Currency = currency; } -} +} \ No newline at end of file diff --git a/src/RobinTTY.NordigenApiClient/Models/Responses/CurrencyExchange.cs b/src/RobinTTY.NordigenApiClient/Models/Responses/CurrencyExchange.cs index 57e581a..a3aae10 100644 --- a/src/RobinTTY.NordigenApiClient/Models/Responses/CurrencyExchange.cs +++ b/src/RobinTTY.NordigenApiClient/Models/Responses/CurrencyExchange.cs @@ -7,6 +7,12 @@ namespace RobinTTY.NordigenApiClient.Models.Responses; /// public class CurrencyExchange { + /// + /// The instructed amount including details about the currency the amount is denominated in. + /// + [JsonPropertyName("instructedAmount")] + public AmountCurrencyPair? InstructedAmount { get; } + /// /// Currency from which an amount is to be converted in a currency conversion. ISO 4217 Alpha 3 currency code (e.g. /// "USD"). @@ -51,6 +57,9 @@ public class CurrencyExchange /// /// Creates a new instance of . /// + /// + /// The currency and amount as instructed by the debtor, if the actual settlement is of a different currency or amount + /// /// /// Currency from which an amount is to be converted in a currency conversion. ISO 4217 Alpha /// 3 currency code (e.g. "USD"). @@ -70,9 +79,10 @@ public class CurrencyExchange /// /// Date at which an exchange rate is quoted. /// Unique identification to unambiguously identify the foreign exchange contract. - 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; diff --git a/src/RobinTTY.NordigenApiClient/Models/Responses/Transaction.cs b/src/RobinTTY.NordigenApiClient/Models/Responses/Transaction.cs index 907afbe..e614dee 100644 --- a/src/RobinTTY.NordigenApiClient/Models/Responses/Transaction.cs +++ b/src/RobinTTY.NordigenApiClient/Models/Responses/Transaction.cs @@ -1,4 +1,5 @@ -using System.Text.Json.Serialization; +using RobinTTY.NordigenApiClient.JsonConverters; +using System.Text.Json.Serialization; namespace RobinTTY.NordigenApiClient.Models.Responses; @@ -218,7 +219,8 @@ public class Transaction /// Array of the report exchange rate. /// [JsonPropertyName("currencyExchange")] - public IEnumerable? CurrencyExchange { get; } + [JsonConverter(typeof(SingleOrArrayConverter, CurrencyExchange>))] + public List? CurrencyExchange { get; } /// /// The identification of the transaction as used for reference by the financial institution. @@ -343,7 +345,7 @@ public Transaction(string? transactionId, string? debtorName, MinimalBankAccount DateTime? valueDateTime, string? remittanceInformationStructured, IEnumerable? remittanceInformationStructuredArray, string? additionalInformation, string? additionalInformationStructured, Balance? balanceAfterTransaction, string? checkId, - IEnumerable? currencyExchange, string? entryReference, string? internalTransactionId, + List? currencyExchange, string? entryReference, string? internalTransactionId, string? merchantCategoryCode, DateTime? bookingDateTime) { TransactionId = transactionId;