Skip to content
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
137 changes: 113 additions & 24 deletions src/Hyperledger.Aries/Storage/DefaultWalletRecordService.cs
Original file line number Diff line number Diff line change
Expand Up @@ -6,9 +6,11 @@
using Hyperledger.Aries.Agents;
using Hyperledger.Aries.Extensions;
using Hyperledger.Aries.Features.PresentProof;
using Hyperledger.Aries.Storage.Models;
using Hyperledger.Indy.NonSecretsApi;
using Hyperledger.Indy.WalletApi;
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;

namespace Hyperledger.Aries.Storage
{
Expand All @@ -32,47 +34,80 @@ public DefaultWalletRecordService()
}

/// <inheritdoc />
public virtual Task AddAsync<T>(Wallet wallet, T record)
where T : RecordBase, new()
public virtual Task AddAsync<T>(Wallet wallet, T record, Func<T, JObject>? encode = null) where T : RecordBase, new()
{
record.CreatedAtUtc = DateTime.UtcNow;

var properties = record
.GetType()
.GetProperties()
.Where(info => Attribute.IsDefined(info, typeof(RecordTagAttribute)));

foreach (var property in properties)
{
var value = property.GetValue(record);
record.SetTag(property.Name, value.ToString(), false);
}

var recordJson = encode is null
? record.ToJson(_jsonSettings)
: encode(record).ToString();

return NonSecrets.AddRecordAsync(wallet,
record.TypeName,
record.Id,
record.ToJson(_jsonSettings),
recordJson,
record.Tags.ToJson());
}

/// <inheritdoc />
public virtual async Task<List<T>> SearchAsync<T>(Wallet wallet, ISearchQuery query, SearchOptions options, int count, int skip)
where T : RecordBase, new()
public virtual async Task<List<T>> SearchAsync<T>(
Wallet wallet,
ISearchQuery? query = null,
SearchOptions? options = null,
int count = 10,
int skip = 0,
Func<JObject, T>? decode = null) where T : RecordBase, new()
{
using var search = await NonSecrets.OpenSearchAsync(
wallet,
new T().TypeName,
(query ?? SearchQuery.Empty).ToJson(),
(options ?? new SearchOptions()).ToJson()
);
);

if(skip > 0) {
await search.NextAsync(wallet, skip);
}

var result = JsonConvert.DeserializeObject<SearchResult>(await search.NextAsync(wallet, count), _jsonSettings);

return result.Records?
.Select(x =>
{
var record = JsonConvert.DeserializeObject<T>(x.Value, _jsonSettings);

foreach (var tag in x.Tags)
record.Tags[tag.Key] = tag.Value;

return record;
})
.ToList()
?? new List<T>();
var searchResultStr = await search.NextAsync(wallet, count);
var searchResult = JsonConvert.DeserializeObject<SearchResult>(searchResultStr, _jsonSettings);

if (searchResult?.Records is null)
{
return new List<T>();
}

var records = searchResult.Records.Select(searchItem =>
{
T record;
if (decode is null)
{
record = JsonConvert.DeserializeObject<T>(searchItem.Value, _jsonSettings)!;
}
else
{
var json = JObject.Parse(searchItem.Value);
record = decode(json);
}

foreach (var tag in searchItem.Tags)
record.Tags[tag.Key] = tag.Value;

return record;
});

return records.ToList();
}

/// <inheritdoc />
Expand All @@ -91,8 +126,27 @@ await NonSecrets.UpdateRecordTagsAsync(wallet,
record.Tags.ToJson(_jsonSettings));
}

public async Task Update<T>(Wallet wallet, T record, Func<T, JObject>? encode = null) where T : RecordBase
{
record.UpdatedAtUtc = DateTime.UtcNow;

var recordJson = encode is null
? record.ToJson(_jsonSettings)
: encode(record).ToString();

await NonSecrets.UpdateRecordValueAsync(wallet,
record.TypeName,
record.Id,
recordJson);

await NonSecrets.UpdateRecordTagsAsync(wallet,
record.TypeName,
record.Id,
record.Tags.ToJson(_jsonSettings));
}

/// <inheritdoc />
public virtual async Task<T> GetAsync<T>(Wallet wallet, string id) where T : RecordBase, new()
public async Task<T?> GetAsync<T>(Wallet wallet, string id, Func<JObject, T>? decode = null) where T : RecordBase, new()
{
try
{
Expand All @@ -106,9 +160,18 @@ await NonSecrets.UpdateRecordTagsAsync(wallet,
return null;
}

var item = JsonConvert.DeserializeObject<SearchItem>(searchItemJson, _jsonSettings);
var item = JsonConvert.DeserializeObject<SearchItem>(searchItemJson, _jsonSettings)!;

var record = JsonConvert.DeserializeObject<T>(item.Value, _jsonSettings);
T record;
if (decode is null)
{
record = JsonConvert.DeserializeObject<T>(item.Value, _jsonSettings)!;
}
else
{
var json = JObject.Parse(item.Value);
record = decode(json);
}

foreach (var tag in item.Tags)
record.Tags[tag.Key] = tag.Value;
Expand All @@ -127,7 +190,7 @@ await NonSecrets.UpdateRecordTagsAsync(wallet,
try
{
var record = await GetAsync<T>(wallet, id);
var typeName = new T().TypeName;
var typeName = record.TypeName;

await NonSecrets.DeleteRecordTagsAsync(
wallet: wallet,
Expand All @@ -147,5 +210,31 @@ await NonSecrets.DeleteRecordAsync(
return false;
}
}

/// <inheritdoc />
public async Task<bool> Delete(Wallet wallet, RecordBase record)
{
try
{
var typeName = record.TypeName;

await NonSecrets.DeleteRecordTagsAsync(
wallet: wallet,
type: typeName,
id: record.Id,
tagsJson: record.Tags.Select(x => x.Key).ToArray().ToJson());
await NonSecrets.DeleteRecordAsync(
wallet: wallet,
type: typeName,
id: record.Id);

return true;
}
catch (Exception e)
{
Debug.WriteLine($"Couldn't delete record: {e}");
return false;
}
}
}
}
40 changes: 34 additions & 6 deletions src/Hyperledger.Aries/Storage/IWalletRecordService.cs
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
using System.Collections.Generic;
using System;
using System.Collections.Generic;
using System.Threading.Tasks;
using Hyperledger.Indy.WalletApi;
using Newtonsoft.Json.Linq;

namespace Hyperledger.Aries.Storage
{
Expand All @@ -15,20 +17,28 @@ public interface IWalletRecordService
/// <returns>The record async.</returns>
/// <param name="wallet">Wallet.</param>
/// <param name="record">Record.</param>
/// <param name="encode">The func for encoding the record to JSON format</param>
/// <typeparam name="T">The 1st type parameter.</typeparam>
Task AddAsync<T>(Wallet wallet, T record) where T : RecordBase, new();

Task AddAsync<T>(Wallet wallet, T record, Func<T, JObject>? encode = null) where T : RecordBase, new();
/// <summary>
/// Searchs the records async.
/// Searches the records async.
/// </summary>
/// <returns>The records async.</returns>
/// <param name="wallet">Wallet.</param>
/// <param name="query">Query.</param>
/// <param name="options">Options.</param>
/// <param name="count">The number of items to return</param>
/// <param name="skip">The number of items to skip</param>
/// <param name="decode">Func for decoding the JSON to the record</param>
/// <typeparam name="T">The 1st type parameter.</typeparam>
Task<List<T>> SearchAsync<T>(Wallet wallet, ISearchQuery query = null, SearchOptions options = null, int count = 10, int skip = 0) where T : RecordBase, new();
Task<List<T>> SearchAsync<T>(
Wallet wallet,
ISearchQuery? query = null,
SearchOptions? options = null,
int count = 10,
int skip = 0,
Func<JObject, T>? decode = null) where T : RecordBase, new();

/// <summary>
/// Updates the record async.
Expand All @@ -37,15 +47,25 @@ public interface IWalletRecordService
/// <param name="wallet">Wallet.</param>
/// <param name="record">Credential record.</param>
Task UpdateAsync(Wallet wallet, RecordBase record);

/// <summary>
/// Updates the record async.
/// </summary>
/// <returns>The record async.</returns>
/// <param name="wallet">Wallet.</param>
/// <param name="record">Credential record.</param>
/// <param name="encode">The func for encoding the record to JSON format</param>
Task Update<T>(Wallet wallet, T record, Func<T, JObject>? encode = null) where T : RecordBase;

/// <summary>
/// Gets the record async.
/// </summary>
/// <returns>The record async.</returns>
/// <param name="wallet">Wallet.</param>
/// <param name="id">Identifier.</param>
/// <param name="decode">Func for decoding the JSON to the record</param>
/// <typeparam name="T">The 1st type parameter.</typeparam>
Task<T> GetAsync<T>(Wallet wallet, string id) where T : RecordBase, new();
Task<T?> GetAsync<T>(Wallet wallet, string id, Func<JObject, T>? decode = null) where T : RecordBase, new();

/// <summary>
/// Deletes the record async.
Expand All @@ -55,5 +75,13 @@ public interface IWalletRecordService
/// <param name="id">Record Identifier.</param>
/// <returns>Boolean status indicating if the removal succeed</returns>
Task<bool> DeleteAsync<T>(Wallet wallet, string id) where T : RecordBase, new();

/// <summary>
/// Deletes the record async.
/// </summary>
/// <param name="wallet">Wallet.</param>
/// <param name="record"></param>
/// <returns>Boolean status indicating if the removal succeed</returns>
Task<bool> Delete(Wallet wallet, RecordBase record);
}
}
11 changes: 11 additions & 0 deletions src/Hyperledger.Aries/Storage/Models/RecordTagAttribute.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
using System;

namespace Hyperledger.Aries.Storage.Models
{
/// <summary>
/// Defines an attribute to be also saved as a tag in the record
/// </summary>
public class RecordTagAttribute : Attribute
{
}
}
2 changes: 1 addition & 1 deletion src/Hyperledger.Aries/Storage/Records/RecordBase.cs
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ public DateTime? UpdatedAtUtc
/// <summary>Gets or sets the tags.</summary>
/// <value>The tags.</value>
[JsonIgnore]
protected internal Dictionary<string, string> Tags { get; set; } = new();
public Dictionary<string, string> Tags { get; set; } = new();

/// <summary>
/// Get and set the schema version of a wallet record
Expand Down
46 changes: 46 additions & 0 deletions src/WalletFramework.Core/Colors/Color.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
using System.Drawing;
using LanguageExt;

namespace WalletFramework.Core.Colors;

public readonly struct Color
{
private System.Drawing.Color Value { get; }

private Color(System.Drawing.Color value)
{
Value = value;
}

public override string ToString() => Value.ToHex();

public System.Drawing.Color ToSystemColor() => Value;

public static implicit operator System.Drawing.Color(Color color) => color.Value;

public static implicit operator Color(System.Drawing.Color systemColor) => new(systemColor);

public static implicit operator string(Color color) => color.ToString();

public static Option<Color> OptionColor(string hexStr)
{
try
{
var colorConverter = new ColorConverter();
var systemColor = (System.Drawing.Color)colorConverter.ConvertFromString(hexStr);
return systemColor.ToFrameworkColor();
}
catch (Exception)
{
return Option<Color>.None;
}
}
}

public static class ColorFun
{
public static string ToHex(this System.Drawing.Color systemColor) =>
$"#{systemColor.R:X2}{systemColor.G:X2}{systemColor.B:X2}";

public static Color ToFrameworkColor(this System.Drawing.Color systemColor) => systemColor;
}
32 changes: 32 additions & 0 deletions src/WalletFramework.Core/Credentials/CredentialId.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
using WalletFramework.Core.Credentials.Errors;
using WalletFramework.Core.Functional;

namespace WalletFramework.Core.Credentials;

public readonly struct CredentialId
{
private string Value { get; }

private CredentialId(string value)
{
Value = value;
}

public override string ToString() => Value;

public static implicit operator string(CredentialId credentialId) => credentialId.Value;

public static CredentialId CreateCredentialId()
{
var id = Guid.NewGuid().ToString();
return new CredentialId(id);
}

public static Validation<CredentialId> ValidCredentialId(string id)
{
var isValid = Guid.TryParse(id, out _);
return isValid
? new CredentialId(id)
: new CredentialIdError(id);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
using WalletFramework.Core.Functional;

namespace WalletFramework.Core.Credentials.Errors;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

namespace


public record CredentialIdError(string Value) : Error($"The CredentialId is not a valid GUID, value is: {Value}");
Loading
Loading