Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: add id and type to match interface #164

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
26 changes: 26 additions & 0 deletions lib/evaluation/core/submissionRequirementMatch.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,32 @@
import { Rules } from '@sphereon/pex-models';

export enum SubmissionRequirementMatchType {
/**
* Match for a submission_requirements entry in the presentation definition. If the match type
* is `SubmissionRequirement` the {@link SubmissionRequirementMatch.id} property refers to the index
* of the `submission_requirements` entry in the presentation definition.
*
* If the match is a nested match result, this match type refers to the nested index. E.g. a presentation
* definition has three `submission_requirements` entries where the second submission requirement (index 1)
* has two `from_nested` `submission_requirements` entries and this match refers to the second (index 1) of
* this from nested, the {@link SubmissionRequirementMatch.id} property of the outer match refers to the outer index
* in the `submission_requirements` entries, and the nested {@link SubmissionRequirementMatch.id} refers to index of the
* `from_nested` entries. This can go multiple layers deep.
*/
SubmissionRequirement = 'SubmissionRequirement',

/**
* Match for an input_descriptors entry in the presentation definition. This type will be used
* if no submission_requirements are present in the presentation definition. If the match type
* is `InputDescriptor` the {@link SubmissionRequirementMatch.id} property refers to the `id`
* of the `input_descriptors` entry in the presentation definition.
*/
InputDescriptor = 'InputDescriptor',
}

export interface SubmissionRequirementMatch {
type: SubmissionRequirementMatchType;
id: string | number;
name?: string;
rule: Rules;
min?: number;
Expand Down
69 changes: 39 additions & 30 deletions lib/evaluation/evaluationClientWrapper.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,14 @@ import {
import { JsonPathUtils, ObjectUtils } from '../utils';
import { getVpFormatForVcFormat } from '../utils/formatMap';

import { EvaluationResults, HandlerCheckResult, PresentationEvaluationResults, SelectResults, SubmissionRequirementMatch } from './core';
import {
EvaluationResults,
HandlerCheckResult,
PresentationEvaluationResults,
SelectResults,
SubmissionRequirementMatch,
SubmissionRequirementMatchType,
} from './core';
import { EvaluationClient } from './evaluationClient';

interface SubmissionSatisfiesSubmissionRequirementResult {
Expand Down Expand Up @@ -252,13 +259,17 @@ export class EvaluationClientWrapper {
marked: HandlerCheckResult[],
): SubmissionRequirementMatch[] {
const submissionRequirementMatches: SubmissionRequirementMatch[] = [];
for (const sr of submissionRequirements) {
for (const [srIndex, sr] of Object.entries(submissionRequirements)) {
// Create a default SubmissionRequirementMatch object
const srm: SubmissionRequirementMatch = {
name: pd.name || pd.id,
rule: sr.rule,
vc_path: [],

name: sr.name,
type: SubmissionRequirementMatchType.SubmissionRequirement,
id: Number(srIndex),
};

if (sr.from) {
srm.from = sr.from;
}
Expand All @@ -268,12 +279,9 @@ export class EvaluationClientWrapper {
sr.count ? (srm.count = sr.count) : undefined;

if (sr.from) {
const matchingDescriptors = this.mapMatchingDescriptors(pd, sr, marked);
if (matchingDescriptors) {
srm.vc_path.push(...matchingDescriptors.vc_path);
srm.name = matchingDescriptors.name;
submissionRequirementMatches.push(srm);
}
const matchingVcPaths = this.getMatchingVcPathsForSubmissionRequirement(pd, sr, marked);
srm.vc_path.push(...matchingVcPaths);
submissionRequirementMatches.push(srm);
} else if (sr.from_nested) {
// Recursive call to matchSubmissionRequirements for nested requirements
try {
Expand All @@ -298,42 +306,43 @@ export class EvaluationClientWrapper {
continue;
}
for (const vcPath of sameIdVcs) {
const idRes = JsonPathUtils.extractInputField(pd, [idPath]);
if (idRes.length) {
const inputDescriptorResults = JsonPathUtils.extractInputField<InputDescriptorV1 | InputDescriptorV2>(pd, [idPath]);
if (inputDescriptorResults.length) {
const inputDescriptor = inputDescriptorResults[0].value;
submissionRequirementMatches.push({
name: (idRes[0].value as InputDescriptorV1 | InputDescriptorV2).name || (idRes[0].value as InputDescriptorV1 | InputDescriptorV2).id,
name: inputDescriptor.name || inputDescriptor.id,
rule: Rules.All,
vc_path: [vcPath],

type: SubmissionRequirementMatchType.InputDescriptor,
id: inputDescriptor.id,
});
}
}
}
return this.removeDuplicateSubmissionRequirementMatches(submissionRequirementMatches);
}

private mapMatchingDescriptors(
private getMatchingVcPathsForSubmissionRequirement(
pd: IInternalPresentationDefinition,
sr: SubmissionRequirement,
marked: HandlerCheckResult[],
): SubmissionRequirementMatch {
const srm: Partial<SubmissionRequirementMatch> = { rule: sr.rule, vc_path: [] };
if (sr?.from) {
srm.from = sr.from;
// updating the srm.name everytime and since we have only one, we're sending the last one
for (const m of marked) {
const inDesc: InputDescriptorV2 = jp.query(pd, m.input_descriptor_path)[0];
if (inDesc.group && inDesc.group.indexOf(sr.from) === -1) {
continue;
}
srm.name = inDesc.name || inDesc.id;
if (m.payload.group.includes(sr.from)) {
if (srm.vc_path?.indexOf(m.verifiable_credential_path) === -1) {
srm.vc_path.push(m.verifiable_credential_path);
}
}
): string[] {
const vcPaths = new Set<string>();

if (!sr.from) return Array.from(vcPaths);

for (const m of marked) {
const inputDescriptor: InputDescriptorV2 = jp.query(pd, m.input_descriptor_path)[0];
if (inputDescriptor.group && inputDescriptor.group.indexOf(sr.from) === -1) {
continue;
}
if (m.payload.group.includes(sr.from)) {
vcPaths.add(m.verifiable_credential_path);
}
}
return srm as SubmissionRequirementMatch;

return Array.from(vcPaths);
}

public evaluate(
Expand Down
4 changes: 2 additions & 2 deletions lib/utils/jsonPathUtils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@ export class JsonPathUtils {
}
result: [ { value: 19, path: [ '$', 'details', 'information', 0, 'age' ] } ]
*/
public static extractInputField(obj: InputFieldType, paths: string[]): { value: unknown; path: PathComponent[] }[] {
public static extractInputField<PathValue = unknown>(obj: InputFieldType, paths: string[]): { value: PathValue; path: PathComponent[] }[] {
let result: { value: unknown; path: PathComponent[] }[] = [];
if (paths) {
for (const path of paths) {
Expand All @@ -58,7 +58,7 @@ export class JsonPathUtils {
}
}
}
return result;
return result as { value: PathValue; path: PathComponent[] }[];
}

public static changePropertyNameRecursively(
Expand Down
Loading
Loading