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
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
using System.Text;
using OneOf;
using WalletFramework.Oid4Vc.Oid4Vp.Dcql.CredentialQueries;
using WalletFramework.Oid4Vc.Oid4Vp.Dcql.Models;
using WalletFramework.Oid4Vc.Oid4Vp.Models;
using WalletFramework.Oid4Vc.Oid4Vp.PresentationExchange.Models;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,15 +4,16 @@
using OneOf;
using WalletFramework.Core.Credentials.Abstractions;
using WalletFramework.Core.Functional;
using WalletFramework.Core.Functional.Errors;
using WalletFramework.Core.Json;
using WalletFramework.MdocLib;
using WalletFramework.SdJwtVc.Models;
using WalletFramework.Oid4Vc.Oid4Vp.Models;
using WalletFramework.Core.Functional.Errors;
using WalletFramework.Oid4Vc.Credential;
using static WalletFramework.Oid4Vc.Oid4Vp.Dcql.Models.CredentialQueryConstants;
using WalletFramework.Oid4Vc.Oid4Vp.Dcql.Models;
using WalletFramework.Oid4Vc.Oid4Vp.Models;
using WalletFramework.SdJwtVc.Models;
using static WalletFramework.Oid4Vc.Oid4Vp.Dcql.CredentialQueries.CredentialQueryConstants;

namespace WalletFramework.Oid4Vc.Oid4Vp.Dcql.Models;
namespace WalletFramework.Oid4Vc.Oid4Vp.Dcql.CredentialQueries;

/// <summary>
/// The credential query.
Expand All @@ -38,10 +39,11 @@ public class CredentialQuery
public string Format { get; set; } = null!;

/// <summary>
/// This MUST be a string identifying the Credential in the response.
/// This MUST be a CredentialQueryId identifying the Credential in the response.
/// </summary>
[JsonProperty(IdJsonKey)]
public string? Id { get; set; } = null!;
[JsonConverter(typeof(CredentialQueryIdJsonConverter))]
public CredentialQueryId Id { get; set; } = null!;

/// <summary>
/// Represents a collection, where each value contains a collection of identifiers for elements in claims that
Expand All @@ -54,15 +56,8 @@ public static Validation<CredentialQuery> FromJObject(JObject json)
{
var id = json.GetByKey(IdJsonKey)
.OnSuccess(token => token.ToJValue())
.OnSuccess(value =>
{
if (string.IsNullOrWhiteSpace(value.Value?.ToString()))
{
return new StringIsNullOrWhitespaceError<CredentialQuery>();
}

return ValidationFun.Valid(value.Value.ToString());
}).ToOption();
.OnSuccess(value => CredentialQueryId.Create(value.Value?.ToString() ?? string.Empty))
.ToOption();

var format = json.GetByKey(FormatJsonKey)
.OnSuccess(token => token.ToJValue())
Expand Down Expand Up @@ -102,7 +97,7 @@ public static Validation<CredentialQuery> FromJObject(JObject json)
}

private static CredentialQuery Create(
Option<string> id,
Option<CredentialQueryId> id,
string format,
CredentialMetaQuery meta,
Option<IEnumerable<ClaimQuery>> claims,
Expand Down Expand Up @@ -188,9 +183,10 @@ public static Option<PresentationCandidate> FindMatchingCandidate(
if (groupedCandidates.Any())
{
return new PresentationCandidate(
credentialQuery.Id,
credentialQuery.Id.AsString(),
groupedCandidates,
disclosures.ToList());
disclosures.ToList()
);
}
}

Expand All @@ -206,7 +202,7 @@ public static Option<PresentationCandidate> FindMatchingCandidate(
.ToArray();

return credentialsWhereTypeMatches.Any()
? new PresentationCandidate(credentialQuery.Id, allCandidates, Option<List<ClaimQuery>>.None)
? new PresentationCandidate(credentialQuery.Id.AsString(), allCandidates, Option<List<ClaimQuery>>.None)
: Option<PresentationCandidate>.None;
}

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
using WalletFramework.Core.Functional;
using WalletFramework.Core.Functional.Errors;

namespace WalletFramework.Oid4Vc.Oid4Vp.Dcql.CredentialQueries;

public record CredentialQueryId
{
private readonly string _value;

public string AsString() => _value;

private CredentialQueryId(string value) => _value = value;

public static implicit operator string(CredentialQueryId credentialQueryId) => credentialQueryId._value;

public static Validation<CredentialQueryId> Create(string value) =>
string.IsNullOrWhiteSpace(value)
? new StringIsNullOrWhitespaceError<CredentialQueryId>()
: new CredentialQueryId(value);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
using Newtonsoft.Json;
using WalletFramework.Core.Functional;

namespace WalletFramework.Oid4Vc.Oid4Vp.Dcql.CredentialQueries;

public class CredentialQueryIdJsonConverter : JsonConverter<CredentialQueryId>
{
public override void WriteJson(JsonWriter writer, CredentialQueryId? value, JsonSerializer serializer)
{
writer.WriteValue(value?.AsString());
}

public override CredentialQueryId ReadJson(JsonReader reader, Type objectType, CredentialQueryId? existingValue, bool hasExistingValue, JsonSerializer serializer)
{
if (reader.Value is string str)
{
var result = CredentialQueryId.Create(str);
return result.Match(
success => success,
errors => throw new JsonSerializationException(
$"Failed to deserialize CredentialQueryId: {string.Join(", ", errors.Select(e => e.Message))}")
);
}
else
{
throw new JsonSerializationException($"Expected string but got {reader.TokenType}");
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
using WalletFramework.Core.Functional;

namespace WalletFramework.Oid4Vc.Oid4Vp.Dcql.CredentialQueries;

public class CredentialQueryIdListJsonConverter : JsonConverter<IReadOnlyList<CredentialQueryId>>
{
public override void WriteJson(JsonWriter writer, IReadOnlyList<CredentialQueryId>? value, JsonSerializer serializer)
{
writer.WriteStartArray();
foreach (var id in value!)
{
writer.WriteValue(id.AsString());
}
writer.WriteEndArray();
}

public override IReadOnlyList<CredentialQueryId> ReadJson(JsonReader reader, Type objectType, IReadOnlyList<CredentialQueryId>? existingValue, bool hasExistingValue, JsonSerializer serializer)
{
var array = JArray.Load(reader);
var result = array.TraverseAll(token => CredentialQueryId.Create(token.ToString()));

return result.Match(
list => list.ToArray(),
errors => throw new JsonSerializationException(
$"Failed to deserialize CredentialQueryId list: {string.Join(", ", errors.SelectMany(e => e.Message))}")
);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
using WalletFramework.Core.Functional;

namespace WalletFramework.Oid4Vc.Oid4Vp.Dcql.CredentialSets;

public class CredentialSetJsonConverter : JsonConverter<IReadOnlyList<CredentialSetOption>>
{
public override void WriteJson(JsonWriter writer, IReadOnlyList<CredentialSetOption>? value, JsonSerializer serializer)
{
writer.WriteStartArray();
foreach (var option in value!)
{
writer.WriteStartArray();
foreach (var id in option.Ids)
{
writer.WriteValue(id.AsString());
}
writer.WriteEndArray();
}
writer.WriteEndArray();
}

public override IReadOnlyList<CredentialSetOption> ReadJson(JsonReader reader, Type objectType, IReadOnlyList<CredentialSetOption>? existingValue, bool hasExistingValue, JsonSerializer serializer)
{
var array = JArray.Load(reader);
var result = array
.Select(inner => inner.ToObject<List<string>>() ?? [])
.TraverseAll(CredentialSetOption.FromStrings);

return result.Match(
list => list.ToList(),
errors => throw new JsonSerializationException(
$"Failed to deserialize CredentialSetOption list: {string.Join(", ", errors.SelectMany(e => e.Message))}")
);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
using System.Globalization;
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
using WalletFramework.Core.Functional;
using WalletFramework.Core.Json;
using WalletFramework.Oid4Vc.Oid4Vp.Dcql.CredentialQueries;

namespace WalletFramework.Oid4Vc.Oid4Vp.Dcql.CredentialSets;

public record CredentialSetOption(
[property: JsonConverter(typeof(CredentialQueryIdListJsonConverter))]
IReadOnlyList<CredentialQueryId> Ids)
{
public static Validation<CredentialSetOption> FromJArray(JArray array) =>
from jValues in array.TraverseAll(token => token.ToJValue())
from queryIds in FromStrings(jValues.Select(value => value.ToString(CultureInfo.InvariantCulture)))
select queryIds;

public static Validation<CredentialSetOption> FromStrings(IEnumerable<string> strings) =>
from queryIds in strings.TraverseAll(CredentialQueryId.Create)
select new CredentialSetOption(queryIds.ToArray());
}
Original file line number Diff line number Diff line change
Expand Up @@ -5,34 +5,35 @@
using WalletFramework.Core.Functional.Errors;
using WalletFramework.Core.Json;
using WalletFramework.Oid4Vc.RelyingPartyAuthentication.RegistrationCertificate;
using static WalletFramework.Oid4Vc.Oid4Vp.Dcql.Models.CredentialSetQueryFun;
using static WalletFramework.Oid4Vc.Oid4Vp.Dcql.CredentialSets.CredentialSetQueryConstants;

namespace WalletFramework.Oid4Vc.Oid4Vp.Dcql.Models;
namespace WalletFramework.Oid4Vc.Oid4Vp.Dcql.CredentialSets;

/// <summary>
/// The credential set query.
/// </summary>
public class CredentialSetQuery
public record CredentialSetQuery
{
/// <summary>
/// Specifies the purpose of the query.
/// </summary>
[JsonProperty(PurposeJsonKey)]
[JsonConverter(typeof(PurposeConverter))]
public Purpose[]? Purpose { get; set; }
public Purpose[]? Purpose { get; init; }

/// <summary>
/// Indicates whether this set of Credentials is required to satisfy the particular use case at the Verifier.
/// </summary>
[JsonProperty("required")]
public bool Required { get; set; }
public bool Required { get; init; } = true;

/// <summary>
/// Represents a collection, where each value is a list of Credential query identifiers representing one set Credentials that satisfies the use case.
/// </summary>
[JsonProperty(OptionsJsonKey)]
public string[][]? Options { get; set; }

[JsonConverter(typeof(CredentialSetJsonConverter))]
public List<CredentialSetOption> Options { get; private init; } = null!;

public static Validation<CredentialSetQuery> FromJObject(JObject json)
{
var purpose = json.GetByKey(PurposeJsonKey)
Expand Down Expand Up @@ -67,43 +68,39 @@ public static Validation<CredentialSetQuery> FromJObject(JObject json)
})
.ToOption();

var options = json.GetByKey(OptionsJsonKey)
.OnSuccess(token => token.ToJArray())
.OnSuccess(array => array.TraverseAll(jToken => jToken.ToJArray()))
.OnSuccess(array => array.TraverseAll(innerArray =>
var optionsValidation =
from jToken in json.GetByKey(OptionsJsonKey)
from jArray in jToken.ToJArray()
from options in jArray.TraverseAll(token =>
{
return innerArray.TraverseAll(x => x.ToJValue())
.OnSuccess(values => values.Select(value =>
{
if (string.IsNullOrWhiteSpace(value.Value?.ToString()))
{
return new StringIsNullOrWhitespaceError<CredentialSetQuery>();
}

return ValidationFun.Valid(value.Value.ToString());
}))
.OnSuccess(values => values.TraverseAll(x => x));
}))
.ToOption();
return
from array in token.ToJArray()
from option in CredentialSetOption.FromJArray(array)
select option;
})
select options;

return ValidationFun.Valid(Create)
.Apply(purpose)
.Apply(required)
.Apply(options);
.Apply(optionsValidation);
}

private static CredentialSetQuery Create(
Option<IEnumerable<Purpose>> purpose,
Option<bool> required,
Option<IEnumerable<IEnumerable<string>>> options) => new()
IEnumerable<CredentialSetOption> options)
{
Purpose = purpose.ToNullable()?.ToArray(),
Required = required.ToNullable() ?? false,
Options = options.ToNullable()?.Select(x => x.ToArray()).ToArray()
};
return new CredentialSetQuery
{
Purpose = purpose.ToNullable()?.ToArray(),
Required = required.ToNullable() ?? false,
Options = options.ToList()
};
}
}

public static class CredentialSetQueryFun
public static class CredentialSetQueryConstants
{
public const string PurposeJsonKey = "purpose";
public const string RequiredJsonKey = "required";
Expand Down
Loading
Loading