Skip to content

Commit

Permalink
feat(workflows): add review endpoint (#28)
Browse files Browse the repository at this point in the history
* feat: add review endpoint

Signed-off-by: ShikharBhatt <shikhar.bhatt@impactility.com>

* Revert "feat: add review endpoint"

This reverts commit e93196d.

* feat: add review endpoint

Signed-off-by: ShikharBhatt <shikhar.bhatt@impactility.com>

* fix: e2e tests

Signed-off-by: ShikharBhatt <shikhar.bhatt@impactility.com>

* refactor: OnlyRedirect is duplicate of RedirectUrl

Signed-off-by: John Henderson <jrhender@users.noreply.github.com>

---------

Signed-off-by: ShikharBhatt <shikhar.bhatt@impactility.com>
Signed-off-by: John Henderson <jrhender@users.noreply.github.com>
Co-authored-by: John Henderson <jrhender@users.noreply.github.com>
  • Loading branch information
ShikharBhatt and jrhender committed Jan 21, 2025
1 parent 4db0c92 commit 1f9e199
Show file tree
Hide file tree
Showing 7 changed files with 91 additions and 5 deletions.
27 changes: 27 additions & 0 deletions apps/vc-api/src/vc-api/vc-api.controller.ts
Original file line number Diff line number Diff line change
Expand Up @@ -514,4 +514,31 @@ export class VcApiController {
): Promise<ExchangeStepStateDto> {
return this.workflowService.getExchangeStep(localWorkflowId, localExchangeId, localStepId);
}

/**
* Add review for an exchange step
*
* @param localWorkflowId
* @param localExchangeId
* @param localStepId
* @param submissionReview
*/
@Post('/workflows/:localWorkflowId/exchanges/:localExchangeId/steps/:localStepId/review')
@ApiOperation({
description:
'Update an exchange step review\n' +
'A NON-STANDARD endpoint currently.\n'
})
@ApiBody({ type: SubmissionReviewDto })
@ApiCreatedResponse()
@ApiNotFoundResponse({ type: NotFoundErrorResponseDto })
@ApiNotFoundResponse({ type: BadRequestErrorResponseDto })
async addWorkflowStepReview(
@Param('localWorkflowId') localWorkflowId: string,
@Param('localExchangeId') localExchangeId: string,
@Param('localStepId') localStepId: string,
@Body() submissionReview: SubmissionReviewDto
) {
await this.workflowService.addReview(localWorkflowId, localExchangeId, localStepId, submissionReview);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -27,5 +27,5 @@ export class PresentationSubmissionFullDto {
@IsNotEmpty()
@ValidateNested()
@Type(() => VerifiablePresentationDto)
vp: VerifiablePresentationDto;
verifiablePresentation: VerifiablePresentationDto;
}
32 changes: 32 additions & 0 deletions apps/vc-api/src/vc-api/workflows/dtos/submission-review.dto.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
/*
* Copyright 2021 - 2023 Energy Web Foundation
* SPDX-License-Identifier: Apache-2.0
*/

import { Type } from 'class-transformer';
import { IsEnum, IsOptional, ValidateNested } from 'class-validator';
import { VerifiablePresentationDto } from '../../credentials/dtos/verifiable-presentation.dto';
import { ApiProperty, ApiPropertyOptional } from '@nestjs/swagger';

export enum ReviewResult {
approved = 'approved',
rejected = 'rejected'
}

export class SubmissionReviewDto {
@IsEnum(ReviewResult)
@ApiProperty({
description: 'The judgement made by the reviewer',
enum: ReviewResult,
enumName: 'ReviewResult'
})
result: ReviewResult;

@ValidateNested()
@IsOptional()
@Type(() => VerifiablePresentationDto)
@ApiPropertyOptional({
description: 'A reviewer may want to include credentials (wrapped in a VP) to the holder'
})
vp?: VerifiablePresentationDto;
}
27 changes: 27 additions & 0 deletions apps/vc-api/src/vc-api/workflows/workflow.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,8 @@ import { HttpService } from '@nestjs/axios';
import { validate } from 'class-validator';
import { ExchangeStepStateDto } from './dtos/exchange-step-state.dto';
import { API_DEFAULT_VERSION_PREFIX } from '../../setup';
import { SubmissionReviewDto } from './dtos/submission-review.dto';
import { IssuanceExchangeStep } from './types/issuance-exchange-step';

export class WorkflowService {
private readonly logger = new Logger(WorkflowService.name, { timestamp: true });
Expand Down Expand Up @@ -193,4 +195,29 @@ export class WorkflowService {
stepResponse: response
};
}

public async addReview(
localWorkflowId: string,
localExchangeId: string,
localStepId: string,
reviewDto: SubmissionReviewDto) {
const exchange = await this.exchangeRepository.findOneBy({
workflowId: localWorkflowId,
exchangeId: localExchangeId
});
if (exchange == null) {
throw new NotFoundException(`workflowId='${localWorkflowId} -> 'exchangeId='${localExchangeId}' does not exist`);
}

const stepInfo = exchange.getStep(localStepId);

if (stepInfo instanceof IssuanceExchangeStep) {
stepInfo.addVP(reviewDto.vp);
} else {
throw new BadRequestException('Only Issuance exchange step is supported');
}
const id = exchange.steps.findIndex((step) => step.stepId === localStepId);
exchange.steps[id] = stepInfo;
await this.exchangeRepository.save(exchange);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ import { VpRequestQueryType } from '../../../../src/vc-api/exchanges/types/vp-re
export class ResidentCardPresentation {
#workflowId = `b229a18f-db45-4b33-8d36-25d442467bab`;
#callbackUrl: string;
queryType = VpRequestQueryType.presentationDefinition;
queryType = VpRequestQueryType.didAuth;

constructor(callbackUrl: string) {
this.#callbackUrl = callbackUrl;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -147,7 +147,7 @@ export const residentCardWorkflowSuite = () => {
// As holder, start issuance exchange
// POST /workflows/{localWorkflowId}/exchanges/{localExchangeId}
const presentationExchangeEndpoint = getUrlPath(presentationExchangeId);
const presentationVpRequest = await walletClient.startExchange(
const presentationVpRequest = await walletClient.startWorkflowExchange(
presentationExchangeEndpoint,
presentationWorkflow.queryType
);
Expand All @@ -174,7 +174,7 @@ export const residentCardWorkflowSuite = () => {
const vp = await walletClient.provePresentation({ presentation, options: presentationOptions });

// Holder submits presentation
await walletClient.continueWorkflowExchange(presentationExchangeContinuationEndpoint, vp, 'empty');
await walletClient.continueWorkflowExchange(presentationExchangeContinuationEndpoint, vp, 'redirectUrl');
presentationCallbackScope.done();
});
};
2 changes: 1 addition & 1 deletion apps/vc-api/test/wallet-client.ts
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ const EXPECTED_RESPONSE_TYPE = {
VpRequest: 'vpRequest',
RedirectUrl: 'redirectUrl',
VerifiablePresentation: 'verifiablePresentation',
Empty: 'empty' // An empty response isn't clearly in the spec but there may be a use for it
Empty: 'empty', // An empty response isn't clearly in the spec but there may be a use for it,
} as const;
type ExpectedResponseType = (typeof EXPECTED_RESPONSE_TYPE)[keyof typeof EXPECTED_RESPONSE_TYPE];

Expand Down

0 comments on commit 1f9e199

Please sign in to comment.