Authorization Requests spec defines new authorization_details
parameter. The authorization server have to somehow compare this parameter to decide whether client wants less, more or the same access.
My way to achieve this is to except two JSON arrays, but I feel my algorithm is not clear and not efficient.
My code looks as below:
public static Dictionary<string, JObject> ReadAuthorizationDetailsArray(this JArray jArray)
{
return jArray
.Where(p => p is JObject)
.ToDictionary(k => ((JObject)k).Property("type")!.Value.ToString(), v => (JObject)v);
}
public static JArray ExceptAuthorizationDetailArrays(this JArray firstArray, JArray secondArray)
{
Func<JValue, string> getJsonValueIdentifier = (JValue jValue)
=> $"{Regex.Replace(jValue.Path, "\\[\\d+\\]", string.Empty)}:{jValue.Value?.ToString()}";
var firstAuthorizationDetails = firstArray.ReadAuthorizationDetailsArray();
var secondAuthorizationDetails = secondArray.ReadAuthorizationDetailsArray();
foreach (var firstAuthorizationDetail in firstAuthorizationDetails)
{
if (!secondAuthorizationDetails.ContainsKey(firstAuthorizationDetail.Key))
continue;
var secondAuthorizationDetailObject = secondAuthorizationDetails[firstAuthorizationDetail.Key];
var firstAuthorizationDetailValuesIdentifiers = firstAuthorizationDetail.Value.Descendants()
.Where(p => p is JValue)
.ToDictionary(k => getJsonValueIdentifier((JValue)k), v => v);
var secondAuthorizationDetailValuesIdentifiers = secondAuthorizationDetailObject.Descendants()
.Where(p => p is JValue)
.Select(p => getJsonValueIdentifier((JValue)p));
var exceptedIdentifiers = firstAuthorizationDetailValuesIdentifiers.Keys.Except(secondAuthorizationDetailValuesIdentifiers);
if (!exceptedIdentifiers.Any())
{
firstArray.Remove(firstAuthorizationDetail.Value);
continue;
}
var toRemove = firstAuthorizationDetailValuesIdentifiers.ExceptBy(exceptedIdentifiers, k => k.Key).Where(p => p.Key != $".type:{p.Value}");
foreach (var removable in toRemove.Select(p => p.Value))
{
if (removable.Parent is JProperty) removable.Parent.Remove();
else removable.Remove();
}
var emptyDescendants = firstAuthorizationDetail.Value.Descendants().Where(p => (p is JObject || p is JArray) && (p is null || !p.Any())).ToList();
foreach (var descendant in emptyDescendants)
{
if (descendant.Parent is JProperty prop)
{
if (prop.Value is null || ((prop.Value is JObject || prop.Value is JArray) && !prop.Value.Any())) descendant.Parent.Remove();
}
else descendant.Remove();
}
}
return firstArray;
}
When firstArray looks as below:
var thirtyJson = @"[
{
""type"": ""payment_initiation"",
""actions"": [
""initiate"",
""status"",
""cancel""
],
""locations"": [
""https://example.com/payments""
],
""instructedAmount"": {
""currency"": ""EUR"",
""amount"": 123.50
},
""creditorName"": ""Merchant A"",
""creditorAccount"": {
""iban"": ""DE02100100109307118603""
},
""remittanceInformationUnstructured"": ""Ref Number Merchant""
},
{
""type"": ""account_information"",
""actions"": [
""list_accounts"",
""read_balances"",
""read_transactions"",
""write_accounts""
],
""locations"": [
""https://example.com/accounts""
]
}
]";
And the secondArray looks like it:
var fourtyJson = @"[
{
""type"":""account_information"",
""actions"":[
""list_accounts"",
""read_balances"",
""read_transactions""
],
""locations"":[
""https://example.com/accounts""
]
}
]";
The valid result is:
[
{
"type": "payment_initiation",
"actions": [
"initiate",
"status",
"cancel"
],
"locations": [
"https://example.com/payments"
],
"instructedAmount": {
"currency": "EUR",
"amount": 123.5
},
"creditorName": "Merchant A",
"creditorAccount": {
"iban": "DE02100100109307118603"
},
"remittanceInformationUnstructured": "Ref Number Merchant"
},
{
"type": "account_information",
"actions": [
"write_accounts"
]
}
]
We can describe this operation like: result = firstArray - secondArray
. So result is JSON that contains all firstArray items that are not contained in secondArray. What's more, type
property is never removed (unless compared objects with the same type
are the equals. Then we remove the entire object from firstArray.)
The regex, getting JSON descendants many times, and these nested loops... I feel it's as bad as possible. What shall I refactor in above code to make it very efficient and more readable?
secondAuthorizationDetailValuesIdentifiers
are not helpful to the Gentle Reader. Take advantage of the context that has been built up by the time we declare a new local variable. Global variables have high documentation burden, but not so with locals. If Tolstoy found that a three-word identifier sufficed for him ("War and Peace"), perhaps it will for you, as well. Five words is stretching it. \$\endgroup\$