Skip to content

Commit

Permalink
Merge pull request #74 from Sphereon-Opensource/fix/ESSIFI-160_not_re…
Browse files Browse the repository at this point in the history
…turning_proper_JSON_paths

ESSIFI-160: json path for methods selectFrom, presentationFrom, and v…
  • Loading branch information
sksadjad authored Dec 3, 2021
2 parents b42a980 + 52f32ff commit e70e243
Show file tree
Hide file tree
Showing 14 changed files with 206 additions and 130 deletions.
10 changes: 10 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,4 +1,14 @@
# Release Notes
## v5.0.1 - 2021-12-03
Fixed json path issue in library responses.

- Updated:
- added verifiableCredential (list) to return params of evaluatePresentation
- added verifiableCredential (list) to return params of evaluateCredentials
- selectFrom response changed to have "verifiableCredential" instead of "selectableVerifiableCredential"
- json path of the verifiableCredentials, now start at presentation object root
- renamed matches in submissionRequirementMatches to vc_path

## v0.5.0 - 2021-11-29
Refactor verifiable presentation support using callbacks

Expand Down
5 changes: 3 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -453,6 +453,7 @@ interface EvaluationResults {
value?: PresentationSubmission;
warnings?: string[];
errors?: Error[];
verifiableCredential: VerifiableCredential[];
}
```

Expand Down Expand Up @@ -500,7 +501,7 @@ interface SelectResults {
/**
* All matched/selectable credentials
*/
selectableVerifiableCredentials?: VerifiableCredential[];
verifiableCredential?: VerifiableCredential[];
/**
* Following are indexes of the verifiableCredentials passed to the selectFrom method that have been selected.
*/
Expand All @@ -514,7 +515,7 @@ interface SubmissionRequirementMatch {
min?: number;
count?: number;
max?: number;
matches: string[];
vc_path: string[];
from?: string[];
from_nested?: SubmissionRequirementMatch[]; // VerifiableCredential Address
}
Expand Down
2 changes: 1 addition & 1 deletion lib/evaluation/core/selectResults.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ export interface SelectResults {
/**
* All matched/selectable credentials
*/
selectableVerifiableCredentials?: VerifiableCredential[];
verifiableCredential?: VerifiableCredential[];
/**
* Following are indexes of the verifiableCredentials passed to the selectFrom method that have been selected.
*/
Expand Down
2 changes: 1 addition & 1 deletion lib/evaluation/core/submissionRequirementMatch.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ export interface SubmissionRequirementMatch {
min?: number;
count?: number;
max?: number;
matches: string[];
vc_path: string[];
from?: string[];
from_nested?: SubmissionRequirementMatch[];
}
85 changes: 66 additions & 19 deletions lib/evaluation/evaluationClientWrapper.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,10 @@
import { PresentationDefinition, PresentationSubmission, Rules, SubmissionRequirement } from '@sphereon/pe-models';
import {
Descriptor,
PresentationDefinition,
PresentationSubmission,
Rules,
SubmissionRequirement,
} from '@sphereon/pe-models';
import jp from 'jsonpath';

import { Checked, Status } from '../ConstraintUtils';
Expand Down Expand Up @@ -52,7 +58,7 @@ export class EvaluationClientWrapper {
errors: errors,
matches: [...matchSubmissionRequirements],
areRequiredCredentialsPresent: Status.INFO,
selectableVerifiableCredentials: [...credentials],
verifiableCredential: [...credentials],
warnings,
};
} else {
Expand All @@ -68,22 +74,25 @@ export class EvaluationClientWrapper {
errors: errors,
matches: [...matchSubmissionRequirements],
areRequiredCredentialsPresent: Status.INFO,
selectableVerifiableCredentials: [...credentials],
verifiableCredential: [...credentials],
warnings,
};
}

this.fillSelectableCredentialsToVerifiableCredentialsMapping(selectResults, verifiableCredentials);
selectResults.areRequiredCredentialsPresent = this.determineAreRequiredCredentialsPresent(selectResults?.matches);
this.remapMatches(selectResults, verifiableCredentials);
selectResults.matches?.forEach((m) => {
this.updateSubmissionRequirementMatchPathToAlias(m, 'verifiableCredential');
});
return selectResults;
}

private remapMatches(selectResults: SelectResults, verifiableCredentials: VerifiableCredential[]) {
selectResults.matches?.forEach((srm) => {
srm.matches.forEach((match, index, matches) => {
srm.vc_path.forEach((match, index, matches) => {
const vc = jp.query(verifiableCredentials, match)[0];
const newIndex = selectResults.selectableVerifiableCredentials?.findIndex((svc) => svc.id === vc.id);
const newIndex = selectResults.verifiableCredential?.findIndex((svc) => svc.id === vc.id);
matches[index] = `$[${newIndex}]`;
});
srm.name;
Expand All @@ -93,7 +102,7 @@ export class EvaluationClientWrapper {
private extractMatches(matchSubmissionRequirements: SubmissionRequirementMatch[]): string[] {
const matches: string[] = [];
matchSubmissionRequirements.forEach((e) => {
matches.push(...e.matches);
matches.push(...e.vc_path);
if (e.from_nested) {
matches.push(...this.extractMatches(e.from_nested));
}
Expand All @@ -117,7 +126,7 @@ export class EvaluationClientWrapper {
submissionRequirementMatches.push(matchingDescriptors);
}
} else if (sr.from_nested) {
const srm: SubmissionRequirementMatch = { name: pd.name || pd.id, rule: sr.rule, from_nested: [], matches: [] };
const srm: SubmissionRequirementMatch = { name: pd.name || pd.id, rule: sr.rule, from_nested: [], vc_path: [] };
if (srm && srm.from_nested) {
sr.min ? (srm.min = sr.min) : undefined;
sr.max ? (srm.max = sr.max) : undefined;
Expand All @@ -142,7 +151,7 @@ export class EvaluationClientWrapper {
const submissionRequirementMatch: SubmissionRequirementMatch = {
name: idRes[0].value.name || idRes[0].value.id,
rule: Rules.All,
matches: sameIdVCs,
vc_path: sameIdVCs,
};
submissionRequirementMatches.push(submissionRequirementMatch);
}
Expand All @@ -155,15 +164,15 @@ export class EvaluationClientWrapper {
sr: SubmissionRequirement,
marked: HandlerCheckResult[]
): SubmissionRequirementMatch {
const srm: Partial<SubmissionRequirementMatch> = { rule: sr.rule, from: [], matches: [] };
const srm: Partial<SubmissionRequirementMatch> = { rule: sr.rule, from: [], vc_path: [] };
if (sr?.from) {
srm.from?.push(sr.from);
for (const m of marked) {
const inDesc = jp.query(pd, m.input_descriptor_path)[0];
srm.name = inDesc.name || inDesc.id;
if (m.payload.group.includes(sr.from)) {
if (srm.matches?.indexOf(m.verifiable_credential_path) === -1) {
srm.matches.push(m.verifiable_credential_path);
if (srm.vc_path?.indexOf(m.verifiable_credential_path) === -1) {
srm.vc_path.push(m.verifiable_credential_path);
}
}
}
Expand All @@ -178,7 +187,7 @@ export class EvaluationClientWrapper {
limitDisclosureSignatureSuites?: string[]
): EvaluationResults {
this._client.evaluate(pd, vcs, holderDids, limitDisclosureSignatureSuites);
const result: EvaluationResults = {};
const result: EvaluationResults = { verifiableCredential: [...vcs] };
result.warnings = this.formatNotInfo(Status.WARN);
result.errors = this.formatNotInfo(Status.ERROR);
if (this._client.presentationSubmission?.descriptor_map.length) {
Expand All @@ -190,8 +199,9 @@ export class EvaluationClientWrapper {
);
}
this._client.presentationSubmission.descriptor_map.splice(0, len); // cut the array and leave only the non-empty values
result.value = this._client.presentationSubmission;
result.value = JSON.parse(JSON.stringify(this._client.presentationSubmission));
}
this.updatePresentationSubmissionPathToAlias('verifiableCredential', result.value);
return result;
}

Expand Down Expand Up @@ -225,13 +235,15 @@ export class EvaluationClientWrapper {
);
const finalIdx = upIdx.filter((ui) => result[1].find((r) => r.verifiable_credential_path === ui[1]));
this.updatePresentationSubmission(finalIdx);
this.updatePresentationSubmissionPathToAlias('verifiableCredential');
return this._client.presentationSubmission;
}
const marked: HandlerCheckResult[] = this._client.results.filter(
(result) => result.evaluator === 'MarkForSubmissionEvaluation' && result.status !== Status.ERROR
);
const updatedIndexes = this.matchUserSelectedVcs(marked, vcs);
this.updatePresentationSubmission(updatedIndexes[1]);
this.updatePresentationSubmissionPathToAlias('verifiableCredential');
return this._client.presentationSubmission;
}

Expand Down Expand Up @@ -391,7 +403,7 @@ export class EvaluationClientWrapper {
verifiableCredentials: VerifiableCredential[]
) {
if (selectResults) {
selectResults.selectableVerifiableCredentials?.forEach((selectableCredential: VerifiableCredential) => {
selectResults.verifiableCredential?.forEach((selectableCredential: VerifiableCredential) => {
const foundIndex: number = verifiableCredentials.findIndex(
(verifiableCredential) => selectableCredential.id === verifiableCredential.id
);
Expand All @@ -408,15 +420,15 @@ export class EvaluationClientWrapper {
return Status.ERROR;
}
for (const m of matchSubmissionRequirements) {
if (m.matches.length == 0 && (!m.from_nested || m.from_nested.length == 0)) {
if (m.vc_path.length == 0 && (!m.from_nested || m.from_nested.length == 0)) {
return Status.ERROR;
} else if (m.count && m.matches.length < m.count && (!m.from_nested || !m.from_nested?.length)) {
} else if (m.count && m.vc_path.length < m.count && (!m.from_nested || !m.from_nested?.length)) {
return Status.ERROR;
} else if (m.count && (m.matches.length > m.count || (m.from_nested && m.from_nested?.length > m.count))) {
} else if (m.count && (m.vc_path.length > m.count || (m.from_nested && m.from_nested?.length > m.count))) {
status = Status.WARN;
} else if (m.min && m.matches.length < m.min && m.from_nested && !m.from_nested?.length) {
} else if (m.min && m.vc_path.length < m.min && m.from_nested && !m.from_nested?.length) {
return Status.ERROR;
} else if (m.max && (m.matches.length > m.max || (m.from_nested && m.from_nested?.length > m.max))) {
} else if (m.max && (m.vc_path.length > m.max || (m.from_nested && m.from_nested?.length > m.max))) {
status = Status.WARN;
} else if (m.from_nested) {
status = this.determineAreRequiredCredentialsPresent(m.from_nested);
Expand All @@ -427,4 +439,39 @@ export class EvaluationClientWrapper {
}
return status;
}

private updateSubmissionRequirementMatchPathToAlias(
submissionRequirementMatch: SubmissionRequirementMatch,
alias: string
) {
const vc_path: string[] = [];
submissionRequirementMatch.vc_path.forEach((m) => {
vc_path.push(m.replace('$', '$.' + alias));
});
submissionRequirementMatch.vc_path = vc_path;
if (submissionRequirementMatch.from_nested) {
submissionRequirementMatch.from_nested.forEach((f) => {
this.updateSubmissionRequirementMatchPathToAlias(f, alias);
});
}
}

private updatePresentationSubmissionPathToAlias(alias: string, presentationSubmission?: PresentationSubmission) {
if (presentationSubmission) {
presentationSubmission.descriptor_map.forEach((d) => {
this.replacePathWithAlias(d, alias);
});
} else {
this._client.presentationSubmission.descriptor_map.forEach((d) => {
this.replacePathWithAlias(d, alias);
});
}
}

private replacePathWithAlias(descriptor: Descriptor, alias: string) {
descriptor.path = descriptor.path.replace('$', '$.' + alias);
if (descriptor.path_nested) {
this.replacePathWithAlias(descriptor.path_nested, alias);
}
}
}
2 changes: 2 additions & 0 deletions lib/evaluation/evaluationResults.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,11 @@
import { PresentationSubmission } from '@sphereon/pe-models';

import { Checked } from '../ConstraintUtils';
import { VerifiableCredential } from '../types';

export interface EvaluationResults {
value?: PresentationSubmission;
warnings?: Checked[];
errors?: Checked[];
verifiableCredential: VerifiableCredential[];
}
10 changes: 5 additions & 5 deletions test/evaluation/EvaluationClientWrapperData.ts
Original file line number Diff line number Diff line change
Expand Up @@ -75,7 +75,7 @@ export class EvaluationClientWrapperData {
{
id: 'Educational transcripts',
format: 'ldp_vc',
path: '$[0]',
path: '$.verifiableCredential[0]',
},
],
};
Expand Down Expand Up @@ -172,7 +172,7 @@ export class EvaluationClientWrapperData {
{
format: 'ldp_vc',
id: '867bfe7a-5b91-46b2-9ba4-70028b8d9cc8',
path: '$[0]',
path: '$.verifiableCredential[0]',
},
],
}),
Expand All @@ -189,7 +189,7 @@ export class EvaluationClientWrapperData {
{
format: 'ldp_vc',
id: '867bfe7a-5b91-46b2-9ba4-70028b8d9cc8',
path: '$[0]',
path: '$.verifiableCredential[0]',
},
],
}),
Expand Down Expand Up @@ -225,7 +225,7 @@ export class EvaluationClientWrapperData {
public getSelectResults(): SelectResults {
return {
areRequiredCredentialsPresent: Status.INFO,
selectableVerifiableCredentials: [
verifiableCredential: [
{
id: 'CredentialID2021110405',
credentialStatus: {
Expand Down Expand Up @@ -253,7 +253,7 @@ export class EvaluationClientWrapperData {
{
name: 'test',
rule: Rules.All,
matches: ['$[0]'],
vc_path: ['$.verifiableCredential[0]'],
},
],
};
Expand Down
6 changes: 3 additions & 3 deletions test/evaluation/check-scenario-1.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -204,10 +204,10 @@ describe('1st scenario', () => {
);
expect(selectFromResult.matches?.length).toEqual(2);
expect(selectFromResult.matches).toEqual([
{ rule: 'all', matches: ['$[0]'], name: 'e73646de-43e2-4d72-ba4f-090d01c11eac' },
{ rule: 'all', matches: ['$[0]'], name: '867bfe7a-5b91-46b2-9ba4-70028b8d9cc8' },
{ rule: 'all', vc_path: ['$.verifiableCredential[0]'], name: 'e73646de-43e2-4d72-ba4f-090d01c11eac' },
{ rule: 'all', vc_path: ['$.verifiableCredential[0]'], name: '867bfe7a-5b91-46b2-9ba4-70028b8d9cc8' },
]);
expect(selectFromResult.selectableVerifiableCredentials?.length).toEqual(1);
expect(selectFromResult.verifiableCredential?.length).toEqual(1);

/**
* Base on the selectFrom result, now Alice knows what to send, so she will call the presentationFrom with the right VerifiableCredential (index #2)
Expand Down
4 changes: 2 additions & 2 deletions test/evaluation/core/submissionRequirementMatch.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,11 +7,11 @@ describe('submissionRequirementMatch', () => {
const submissionRequirementMatch: SubmissionRequirementMatch = {
name: 'test srm',
rule: Rules.All,
matches: ['$.verifiableCredential[1]'],
vc_path: ['$.verifiableCredential[1]'],
from: ['A'],
};
expect(submissionRequirementMatch.from).toContain('A');
expect(submissionRequirementMatch.rule).toBe(Rules.All);
expect(submissionRequirementMatch.matches[0]).toBe('$.verifiableCredential[1]');
expect(submissionRequirementMatch.vc_path[0]).toBe('$.verifiableCredential[1]');
});
});
Loading

0 comments on commit e70e243

Please sign in to comment.