diff --git a/src/Hyperledger.Aries/Storage/DefaultWalletRecordService.cs b/src/Hyperledger.Aries/Storage/DefaultWalletRecordService.cs index f9b3af8e..83a9425a 100644 --- a/src/Hyperledger.Aries/Storage/DefaultWalletRecordService.cs +++ b/src/Hyperledger.Aries/Storage/DefaultWalletRecordService.cs @@ -10,7 +10,6 @@ using Hyperledger.Indy.NonSecretsApi; using Hyperledger.Indy.WalletApi; using Newtonsoft.Json; -using Newtonsoft.Json.Linq; namespace Hyperledger.Aries.Storage { @@ -34,7 +33,7 @@ public DefaultWalletRecordService() } /// - public virtual Task AddAsync(Wallet wallet, T record, Func? encode = null) where T : RecordBase, new() + public virtual Task AddAsync(Wallet wallet, T record) where T : RecordBase, new() { record.CreatedAtUtc = DateTime.UtcNow; @@ -49,9 +48,7 @@ public DefaultWalletRecordService() record.SetTag(property.Name, value.ToString(), false); } - var recordJson = encode is null - ? record.ToJson(_jsonSettings) - : encode(record).ToString(); + var recordJson = record.ToJson(_jsonSettings); return NonSecrets.AddRecordAsync(wallet, record.TypeName, @@ -66,8 +63,7 @@ public virtual async Task> SearchAsync( ISearchQuery? query = null, SearchOptions? options = null, int count = 10, - int skip = 0, - Func? decode = null) where T : RecordBase, new() + int skip = 0) where T : RecordBase, new() { using var search = await NonSecrets.OpenSearchAsync( wallet, @@ -90,16 +86,7 @@ public virtual async Task> SearchAsync( var records = searchResult.Records.Select(searchItem => { - T record; - if (decode is null) - { - record = JsonConvert.DeserializeObject(searchItem.Value, _jsonSettings)!; - } - else - { - var json = JObject.Parse(searchItem.Value); - record = decode(json); - } + var record = JsonConvert.DeserializeObject(searchItem.Value, _jsonSettings)!; foreach (var tag in searchItem.Tags) record.Tags[tag.Key] = tag.Value; @@ -126,13 +113,11 @@ await NonSecrets.UpdateRecordTagsAsync(wallet, record.Tags.ToJson(_jsonSettings)); } - public async Task Update(Wallet wallet, T record, Func? encode = null) where T : RecordBase + public async Task Update(Wallet wallet, T record) where T : RecordBase { record.UpdatedAtUtc = DateTime.UtcNow; - var recordJson = encode is null - ? record.ToJson(_jsonSettings) - : encode(record).ToString(); + var recordJson = record.ToJson(_jsonSettings); await NonSecrets.UpdateRecordValueAsync(wallet, record.TypeName, @@ -146,7 +131,7 @@ await NonSecrets.UpdateRecordTagsAsync(wallet, } /// - public async Task GetAsync(Wallet wallet, string id, Func? decode = null) where T : RecordBase, new() + public async Task GetAsync(Wallet wallet, string id) where T : RecordBase, new() { try { @@ -162,16 +147,7 @@ await NonSecrets.UpdateRecordTagsAsync(wallet, var item = JsonConvert.DeserializeObject(searchItemJson, _jsonSettings)!; - T record; - if (decode is null) - { - record = JsonConvert.DeserializeObject(item.Value, _jsonSettings)!; - } - else - { - var json = JObject.Parse(item.Value); - record = decode(json); - } + var record = JsonConvert.DeserializeObject(item.Value, _jsonSettings)!; foreach (var tag in item.Tags) record.Tags[tag.Key] = tag.Value; @@ -190,7 +166,7 @@ record = decode(json); try { var record = await GetAsync(wallet, id); - var typeName = record.TypeName; + var typeName = record!.TypeName; await NonSecrets.DeleteRecordTagsAsync( wallet: wallet, diff --git a/src/Hyperledger.Aries/Storage/IWalletRecordService.cs b/src/Hyperledger.Aries/Storage/IWalletRecordService.cs index 3adde782..eb3886bc 100644 --- a/src/Hyperledger.Aries/Storage/IWalletRecordService.cs +++ b/src/Hyperledger.Aries/Storage/IWalletRecordService.cs @@ -1,8 +1,6 @@ -using System; -using System.Collections.Generic; +using System.Collections.Generic; using System.Threading.Tasks; using Hyperledger.Indy.WalletApi; -using Newtonsoft.Json.Linq; namespace Hyperledger.Aries.Storage { @@ -17,9 +15,8 @@ public interface IWalletRecordService /// The record async. /// Wallet. /// Record. - /// The func for encoding the record to JSON format /// The 1st type parameter. - Task AddAsync(Wallet wallet, T record, Func? encode = null) where T : RecordBase, new(); + Task AddAsync(Wallet wallet, T record) where T : RecordBase, new(); /// /// Searches the records async. @@ -30,15 +27,13 @@ public interface IWalletRecordService /// Options. /// The number of items to return /// The number of items to skip - /// Func for decoding the JSON to the record /// The 1st type parameter. Task> SearchAsync( Wallet wallet, ISearchQuery? query = null, SearchOptions? options = null, int count = 10, - int skip = 0, - Func? decode = null) where T : RecordBase, new(); + int skip = 0) where T : RecordBase, new(); /// /// Updates the record async. @@ -54,8 +49,7 @@ Task> SearchAsync( /// The record async. /// Wallet. /// Credential record. - /// The func for encoding the record to JSON format - Task Update(Wallet wallet, T record, Func? encode = null) where T : RecordBase; + Task Update(Wallet wallet, T record) where T : RecordBase; /// /// Gets the record async. @@ -63,9 +57,8 @@ Task> SearchAsync( /// The record async. /// Wallet. /// Identifier. - /// Func for decoding the JSON to the record /// The 1st type parameter. - Task GetAsync(Wallet wallet, string id, Func? decode = null) where T : RecordBase, new(); + Task GetAsync(Wallet wallet, string id) where T : RecordBase, new(); /// /// Deletes the record async. diff --git a/src/WalletFramework.MdocVc/MdocRecord.cs b/src/WalletFramework.MdocVc/MdocRecord.cs index 4c00f983..6fa74d57 100644 --- a/src/WalletFramework.MdocVc/MdocRecord.cs +++ b/src/WalletFramework.MdocVc/MdocRecord.cs @@ -2,6 +2,7 @@ using Hyperledger.Aries.Storage.Models; using Hyperledger.Aries.Storage.Models.Interfaces; using LanguageExt; +using Newtonsoft.Json; using Newtonsoft.Json.Linq; using WalletFramework.Core.Credentials; using WalletFramework.Core.Functional; @@ -11,6 +12,7 @@ namespace WalletFramework.MdocVc; +[JsonConverter(typeof(MdocRecordJsonConverter))] public sealed class MdocRecord : RecordBase, ICredential { public CredentialId CredentialId @@ -20,12 +22,11 @@ public CredentialId CredentialId .UnwrapOrThrow(new InvalidOperationException("The Id is corrupt")); private set => Id = value; } - + + [RecordTag] public DocType DocType => Mdoc.DocType; + public Mdoc Mdoc { get; } - - [RecordTag] - public DocType DocType => Mdoc.DocType; - + public Option> Displays { get; } public override string TypeName => "WF.MdocRecord"; @@ -46,36 +47,35 @@ public MdocRecord() public static implicit operator Mdoc(MdocRecord record) => record.Mdoc; } -public static class MdocRecordFun +public class MdocRecordJsonConverter : JsonConverter { - public const string MdocJsonKey = "mdoc"; - private const string MdocDisplaysJsonKey = "displays"; - - public static JObject EncodeToJson(this MdocRecord record) + public override MdocRecord ReadJson( + JsonReader reader, + Type objectType, + MdocRecord? existingValue, + bool hasExistingValue, + JsonSerializer serializer) { - var result = new JObject - { - {nameof(RecordBase.Id), record.Id}, - {MdocJsonKey, record.Mdoc.Encode()} - }; - - record.Displays.IfSome(displays => - { - var displaysJson = new JArray(); - foreach (var display in displays) - { - displaysJson.Add(display.EncodeToJson()); - } - result.Add(MdocDisplaysJsonKey, displaysJson); - }); + var json = JObject.Load(reader); + return DecodeFromJson(json); + } - return result; + public override void WriteJson(JsonWriter writer, MdocRecord? value, JsonSerializer serializer) + { + var json = value!.EncodeToJson(); + json.WriteTo(writer); } - +} + +public static class MdocRecordFun +{ + private const string MdocDisplaysJsonKey = "displays"; + public const string MdocJsonKey = "mdoc"; + public static MdocRecord DecodeFromJson(JObject json) { var id = json[nameof(RecordBase.Id)]!.ToString(); - + var mdocStr = json[MdocJsonKey]!.ToString(); var mdoc = Mdoc .ValidMdoc(mdocStr) @@ -86,7 +86,7 @@ from jToken in json.GetByKey(MdocDisplaysJsonKey).ToOption() from jArray in jToken.ToJArray().ToOption() from mdocDisplays in MdocDisplayFun.DecodeFromJson(jArray) select mdocDisplays; - + var result = new MdocRecord(mdoc, displays) { Id = id @@ -95,5 +95,27 @@ from mdocDisplays in MdocDisplayFun.DecodeFromJson(jArray) return result; } + public static JObject EncodeToJson(this MdocRecord record) + { + var result = new JObject + { + { nameof(RecordBase.Id), record.Id }, + { MdocJsonKey, record.Mdoc.Encode() } + }; + + record.Displays.IfSome(displays => + { + var displaysJson = new JArray(); + foreach (var display in displays) + { + displaysJson.Add(display.EncodeToJson()); + } + + result.Add(MdocDisplaysJsonKey, displaysJson); + }); + + return result; + } + public static MdocRecord ToRecord(this Mdoc mdoc, Option> displays) => new(mdoc, displays); } diff --git a/src/WalletFramework.Oid4Vc/Oid4Vci/AuthFlow/Implementations/AuthFlowSessionStorage.cs b/src/WalletFramework.Oid4Vc/Oid4Vci/AuthFlow/Implementations/AuthFlowSessionStorage.cs index ad77eebb..db8466f9 100644 --- a/src/WalletFramework.Oid4Vc/Oid4Vci/AuthFlow/Implementations/AuthFlowSessionStorage.cs +++ b/src/WalletFramework.Oid4Vc/Oid4Vci/AuthFlow/Implementations/AuthFlowSessionStorage.cs @@ -33,7 +33,7 @@ public async Task StoreAsync( authorizationCodeParameters, authFlowSessionState); - await _recordService.AddAsync(agentContext.Wallet, record, AuthFlowSessionRecordFun.EncodeToJson); + await _recordService.AddAsync(agentContext.Wallet, record); return record.Id; } @@ -41,7 +41,7 @@ public async Task StoreAsync( /// public async Task GetAsync(IAgentContext context, AuthFlowSessionState authFlowSessionState) { - var record = await _recordService.GetAsync(context.Wallet, authFlowSessionState, AuthFlowSessionRecordFun.DecodeFromJson); + var record = await _recordService.GetAsync(context.Wallet, authFlowSessionState); return record!; } diff --git a/src/WalletFramework.Oid4Vc/Oid4Vci/AuthFlow/Records/AuthFlowSessionRecord.cs b/src/WalletFramework.Oid4Vc/Oid4Vci/AuthFlow/Records/AuthFlowSessionRecord.cs index 3e4f5a78..4ca5b51a 100644 --- a/src/WalletFramework.Oid4Vc/Oid4Vci/AuthFlow/Records/AuthFlowSessionRecord.cs +++ b/src/WalletFramework.Oid4Vc/Oid4Vci/AuthFlow/Records/AuthFlowSessionRecord.cs @@ -9,6 +9,7 @@ namespace WalletFramework.Oid4Vc.Oid4Vci.AuthFlow.Records; /// /// Represents the authorization session record. Used during the VCI Authorization Code Flow to hold session relevant information. /// +[JsonConverter(typeof(AuthFlowSessionRecordConverter))] public sealed class AuthFlowSessionRecord : RecordBase { /// @@ -69,6 +70,26 @@ public AuthFlowSessionRecord( } } +public class AuthFlowSessionRecordConverter : JsonConverter +{ + public override void WriteJson(JsonWriter writer, AuthFlowSessionRecord? value, JsonSerializer serializer) + { + var json = value!.EncodeToJson(); + json.WriteTo(writer); + } + + public override AuthFlowSessionRecord ReadJson( + JsonReader reader, + Type objectType, + AuthFlowSessionRecord? existingValue, + bool hasExistingValue, + JsonSerializer serializer) + { + var json = JObject.Load(reader); + return AuthFlowSessionRecordFun.DecodeFromJson(json); + } +} + public static class AuthFlowSessionRecordFun { private const string AuthorizationDataJsonKey = "authorization_data"; diff --git a/src/WalletFramework.Oid4Vc/Oid4Vci/Implementations/MdocStorage.cs b/src/WalletFramework.Oid4Vc/Oid4Vci/Implementations/MdocStorage.cs index c28fa2fa..329444a6 100644 --- a/src/WalletFramework.Oid4Vc/Oid4Vci/Implementations/MdocStorage.cs +++ b/src/WalletFramework.Oid4Vc/Oid4Vci/Implementations/MdocStorage.cs @@ -22,14 +22,14 @@ public MdocStorage(IAgentProvider agentProvider, IWalletRecordService recordServ public async Task Add(MdocRecord record) { var context = await _agentProvider.GetContextAsync(); - await _recordService.AddAsync(context.Wallet, record, MdocRecordFun.EncodeToJson); + await _recordService.AddAsync(context.Wallet, record); return Unit.Default; } public async Task> Get(CredentialId id) { var context = await _agentProvider.GetContextAsync(); - return await _recordService.GetAsync(context.Wallet, id, MdocRecordFun.DecodeFromJson); + return await _recordService.GetAsync(context.Wallet, id); } public async Task>> List( @@ -38,13 +38,12 @@ public async Task>> List( int skip = 0) { var context = await _agentProvider.GetContextAsync(); - var list = await _recordService.SearchAsync( + var list = await _recordService.SearchAsync( context.Wallet, query.ToNullable(), null, count, - skip, - MdocRecordFun.DecodeFromJson); + skip); if (list.Count == 0) return Option>.None; @@ -55,7 +54,7 @@ public async Task>> List( public async Task Update(MdocRecord record) { var context = await _agentProvider.GetContextAsync(); - await _recordService.Update(context.Wallet, record, MdocRecordFun.EncodeToJson); + await _recordService.Update(context.Wallet, record); return Unit.Default; } diff --git a/test/Hyperledger.Aries.Tests/Routing/RoutingInboxHandlerTests.cs b/test/Hyperledger.Aries.Tests/Routing/RoutingInboxHandlerTests.cs index 4f833337..4a499712 100644 --- a/test/Hyperledger.Aries.Tests/Routing/RoutingInboxHandlerTests.cs +++ b/test/Hyperledger.Aries.Tests/Routing/RoutingInboxHandlerTests.cs @@ -48,7 +48,7 @@ public async Task CreateInboxRecordAsync() CreateInboxResponseMessage agentMessage = (CreateInboxResponseMessage)await routingInboxHandler.ProcessAsync(agentContext.Object, unpackedMessage); walletService.Verify(w => w.CreateWalletAsync(It.Is(wc => wc.Id == agentMessage.InboxId), It.Is(wc => wc.Key == agentMessage.InboxKey))); - recordService.Verify(m => m.AddAsync(agentContext.Object.Wallet, It.Is(i => i.Tags.Count == 0), It.IsAny?>()), Times.Once()); + recordService.Verify(m => m.AddAsync(agentContext.Object.Wallet, It.Is(i => i.Tags.Count == 0)), Times.Once()); recordService.Verify(m => m.UpdateAsync(agentContext.Object.Wallet, It.Is(c => c.GetTag("InboxId") == agentMessage.InboxId))); } @@ -74,7 +74,7 @@ public async Task CreateInboxRecordWithMetadataAsync() agentMessage.InboxKey.Should().HaveLength(44); walletService.Verify(w => w.CreateWalletAsync(It.Is(wc => wc.Id == agentMessage.InboxId), It.Is(wc => wc.Key == agentMessage.InboxKey))); - recordService.Verify(m => m.AddAsync(agentContext.Object.Wallet, It.Is(i => i.GetTag(key) == value), It.IsAny?>()), Times.Once()); + recordService.Verify(m => m.AddAsync(agentContext.Object.Wallet, It.Is(i => i.GetTag(key) == value)), Times.Once()); recordService.Verify(m => m.UpdateAsync(agentContext.Object.Wallet, It.Is(c => c.GetTag("InboxId") == agentMessage.InboxId))); } diff --git a/test/WalletFramework.MdocVc.Tests/MdocRecordTests.cs b/test/WalletFramework.MdocVc.Tests/MdocRecordTests.cs index cb988ae0..c2455492 100644 --- a/test/WalletFramework.MdocVc.Tests/MdocRecordTests.cs +++ b/test/WalletFramework.MdocVc.Tests/MdocRecordTests.cs @@ -2,6 +2,7 @@ using Hyperledger.Aries.Storage; using LanguageExt; using Newtonsoft.Json; +using Newtonsoft.Json.Linq; using WalletFramework.Core.Functional; using WalletFramework.MdocLib; using Xunit; @@ -17,7 +18,7 @@ public void Can_Encode_To_Json() var mdoc = Mdoc.ValidMdoc(encodedMdoc).UnwrapOrThrow(new InvalidOperationException("Mdoc sample is corrupt")); var record = mdoc.ToRecord(Option>.None); - var sut = record.EncodeToJson(); + var sut = JObject.FromObject(record); sut[nameof(RecordBase.Id)]!.ToString().Should().Be(record.Id); sut[MdocRecordFun.MdocJsonKey]!.ToString().Should().Be(encodedMdoc); @@ -28,7 +29,7 @@ public void Can_Decode_From_Json() { var json = MdocVcSamples.MdocRecordJson; - var sut = MdocRecordFun.DecodeFromJson(json); + var sut = json.ToObject()!; sut.Mdoc.DocType.ToString().Should().Be(MdocLib.Tests.Samples.DocType); } diff --git a/test/WalletFramework.Oid4Vc.Tests/Oid4Vci/AuthFlow/AuthFlowSessionRecordTests.cs b/test/WalletFramework.Oid4Vc.Tests/Oid4Vci/AuthFlow/AuthFlowSessionRecordTests.cs index 0a338831..8ab7392e 100644 --- a/test/WalletFramework.Oid4Vc.Tests/Oid4Vci/AuthFlow/AuthFlowSessionRecordTests.cs +++ b/test/WalletFramework.Oid4Vc.Tests/Oid4Vci/AuthFlow/AuthFlowSessionRecordTests.cs @@ -1,6 +1,5 @@ using FluentAssertions; using Hyperledger.Aries.Storage; -using Newtonsoft.Json; using Newtonsoft.Json.Linq; using WalletFramework.Core.Functional; using WalletFramework.Core.Uri; @@ -53,7 +52,7 @@ public void Can_Encode_To_Json() var record = new AuthFlowSessionRecord(authorizationData, authorizationCodeParameters, sessionId); // Act - var recordSut = record.EncodeToJson(); + var recordSut = JObject.FromObject(record); var tagsSut = JObject.FromObject(record.Tags); // Assert @@ -68,7 +67,7 @@ public void Can_Decode_From_Json() var json = AuthFlowSamples.AuthFlowSessionRecordJson; // Act - var record = AuthFlowSessionRecordFun.DecodeFromJson(json); + var record = json.ToObject(); // Assert record.Should().NotBeNull();