diff --git a/.devcontainer/Dockerfile b/.devcontainer/Dockerfile new file mode 100644 index 0000000..6f37bfa --- /dev/null +++ b/.devcontainer/Dockerfile @@ -0,0 +1,16 @@ +# See here for image contents: https://github.com/microsoft/vscode-dev-containers/tree/v0.194.0/containers/typescript-node/.devcontainer/base.Dockerfile + +# [Choice] Node.js version: 16, 14, 12 +ARG VARIANT="16-buster" +FROM mcr.microsoft.com/vscode/devcontainers/typescript-node:0-${VARIANT} + +# [Optional] Uncomment this section to install additional OS packages. +# RUN apt-get update && export DEBIAN_FRONTEND=noninteractive \ +# && apt-get -y install --no-install-recommends + +# [Optional] Uncomment if you want to install an additional version of node using nvm +# ARG EXTRA_NODE_VERSION=10 +# RUN su node -c "source /usr/local/share/nvm/nvm.sh && nvm install ${EXTRA_NODE_VERSION}" + +# [Optional] Uncomment if you want to install more global node packages +# RUN su node -c "npm install -g " diff --git a/.devcontainer/devcontainer.json b/.devcontainer/devcontainer.json new file mode 100644 index 0000000..74e2670 --- /dev/null +++ b/.devcontainer/devcontainer.json @@ -0,0 +1,31 @@ +// For format details, see https://aka.ms/devcontainer.json. For config options, see the README at: +// https://github.com/microsoft/vscode-dev-containers/tree/v0.194.0/containers/typescript-node +{ + "name": "Node.js & TypeScript", + "build": { + "dockerfile": "Dockerfile", + // Update 'VARIANT' to pick a Node version: 12, 14, 16 + "args": { + "VARIANT": "14" + } + }, + + // Set *default* container specific settings.json values on container create. + "settings": {}, + + + // Add the IDs of extensions you want installed when the container is created. + "extensions": [ + "dbaeumer.vscode-eslint" + ], + + // Use 'forwardPorts' to make a list of ports inside the container available locally. + "forwardPorts": [9000], + + // Use 'postCreateCommand' to run commands after the container is created. + // "postCreateCommand": "yarn install", + + // Comment out connect as root instead. More info: https://aka.ms/vscode-remote/containers/non-root. + "remoteUser": "node" + +} \ No newline at end of file diff --git a/src/api/openapi.yaml b/src/api/openapi.yaml index 605eef8..f10e322 100644 --- a/src/api/openapi.yaml +++ b/src/api/openapi.yaml @@ -108,6 +108,15 @@ paths: application/json: schema: $ref: '#/components/schemas/RequestDescription' + "404": + description: Returns when interaction for provided type(s) was not found. + content: + application/json: + schema: + type: object + properties: + message: + type: string x-swagger-router-controller: CredentialController operationId: requestPost /credential-issuance/offer: @@ -129,6 +138,15 @@ paths: application/json: schema: $ref: '#/components/schemas/RequestDescription' + "404": + description: Returns when interaction for provided type(s) was not found. + content: + application/json: + schema: + type: object + properties: + message: + type: string x-swagger-router-controller: CredentialController operationId: offerPost /credential-issuance/offer/custom: diff --git a/src/controller/callbackController.ts b/src/controller/callbackController.ts index 653a10f..20d9e42 100644 --- a/src/controller/callbackController.ts +++ b/src/controller/callbackController.ts @@ -1,48 +1,48 @@ -import { Request, Response } from 'express' -import { SdkAgentProvider } from '../sdk/sdkAgentProvider' -import { ErrorCode } from '@jolocom/sdk' -import { StatusCodes } from 'http-status-codes' -import { InteractionRequestHandler } from '../interaction/interactionRequestHandler' -import { injectable } from 'inversify' -// @ts-ignore -import { FlowType } from '@jolocom/sdk/js/interactionManager/types' - -/** - * The controller to handle all interactions callback requests. - */ -@injectable() -export class CallbackController { - constructor( - private readonly agentProvider: SdkAgentProvider, - private readonly interactionRequestHandler: InteractionRequestHandler, - ) {} - - /** - * An action method to process interactions callback request for all {@link FlowType} types. - * In response will be received encoded processed interaction message jwt. - * - * @param request The {@link Request} object representation. - * @param response The {@link Response} object representation. - * @return {Promise} - */ - public async callbackPost(request: Request, response: Response) { - const agent = await this.agentProvider.provide() - - try { - await agent.findInteraction(request.body.token) - } catch (error) { - if (error.message === ErrorCode.NoSuchInteraction) { - response.status(StatusCodes.NOT_FOUND).json({ - message: `Interaction with token '${request.body.token}' not found.` - }) - } - - throw (error) - } - - const token = await this.interactionRequestHandler.handle(request.body.token, agent) - - // TODO: Make common responce preparation and creation - response.json({ token: token.encode() }) - } -} +import { Request, Response } from 'express' +import { SdkAgentProvider } from '../sdk/sdkAgentProvider' +import { ErrorCode } from '@jolocom/sdk' +import { StatusCodes } from 'http-status-codes' +import { InteractionRequestHandler } from '../interaction/interactionRequestHandler' +import { injectable } from 'inversify' +// @ts-ignore +import { FlowType } from '@jolocom/sdk/js/interactionManager/types' + +/** + * The controller to handle all interactions callback requests. + */ +@injectable() +export class CallbackController { + constructor( + private readonly agentProvider: SdkAgentProvider, + private readonly interactionRequestHandler: InteractionRequestHandler, + ) {} + + /** + * An action method to process interactions callback request for all {@link FlowType} types. + * In response will be received encoded processed interaction message jwt. + * + * @param request The {@link Request} object representation. + * @param response The {@link Response} object representation. + * @return {Promise} + */ + public async callbackPost(request: Request, response: Response) { + const agent = await this.agentProvider.provide() + + try { + await agent.findInteraction(request.body.token) + } catch (error) { + if (error instanceof Error && error.message === ErrorCode.NoSuchInteraction) { + response.status(StatusCodes.NOT_FOUND).json({ + message: `Interaction with token '${request.body.token}' not found.` + }) + } + + throw (error) + } + + const token = await this.interactionRequestHandler.handle(request.body.token, agent) + + // TODO: Make common responce preparation and creation + response.json({ token: token.encode() }) + } +} diff --git a/src/controller/credentialController.ts b/src/controller/credentialController.ts index eb99b9c..ab3dd48 100644 --- a/src/controller/credentialController.ts +++ b/src/controller/credentialController.ts @@ -11,6 +11,8 @@ import { CredentialOfferFactory } from '../credential/offer/credentialOfferFacto import { ICredentialRequest } from '@jolocom/protocol-ts/dist/lib/interactionTokens' // @ts-ignore import { FlowType } from '@jolocom/sdk/js/interactionManager/types' +import { InvalidArgumentException } from '../exception/invalidArgumentException' +import { StatusCodes } from 'http-status-codes' /** * The controller to handle all requests related to the @@ -25,7 +27,7 @@ export class CredentialController { private readonly staticClaimsMetadataProvider: StaticClaimsMetadataProvider, private readonly staticCredentialOfferProvider: StaticCredentialOfferProvider, private readonly credentialOfferFactory: CredentialOfferFactory - ) {} + ) { } /** * An action method to receive the resource containing credential request information. @@ -36,19 +38,29 @@ export class CredentialController { */ public async requestPost(request: Request, response: Response) { // TODO: Refactor in favor of strategy pattern usage - const credentialRequirements: ICredentialRequest[] = request.body.types.map((type: string) => ({ - type: this.staticClaimsMetadataProvider.getByType(type).type, - // TODO: Define constraints definition place and provide - constraints: [], - })) - const agent = await this.agentProvider.provide() - const token = await agent.credRequestToken({ - credentialRequirements, - callbackURL: this.appConfig.sdk.callbackUrl, - }) - const requestDescription = await this.requestDescriptionFactory.create(token) + try { + const credentialRequirements: ICredentialRequest[] = request.body.types.map((type: string) => ({ + type: this.staticClaimsMetadataProvider.getByType(type).type, + // TODO: Define constraints definition place and provide + constraints: [], + })) - response.json(requestDescription.toJSON()) + const agent = await this.agentProvider.provide() + const token = await agent.credRequestToken({ + credentialRequirements, + callbackURL: this.appConfig.sdk.callbackUrl, + }) + + const requestDescription = await this.requestDescriptionFactory.create(token) + response.json(requestDescription.toJSON()) + } catch (error) { + if (error instanceof InvalidArgumentException) { + response.status(StatusCodes.NOT_FOUND).json({ + message: error.message + }) + } + throw error; + } } /** @@ -60,17 +72,27 @@ export class CredentialController { */ public async offerPost(request: Request, response: Response) { // TODO: Refactor in favor of strategy pattern usage - const offeredCredentials: CredentialOfferRequest[] = request.body.types.map( - (type: string) => this.staticCredentialOfferProvider.getByType(type) - ) - const agent = await this.agentProvider.provide() - const token = await agent.credOfferToken({ - offeredCredentials, - callbackURL: this.appConfig.sdk.callbackUrl - }) - const requestDescription = await this.requestDescriptionFactory.create(token) + try { + const offeredCredentials: CredentialOfferRequest[] = request.body.types.map( + (type: string) => this.staticCredentialOfferProvider.getByType(type) + ) + const agent = await this.agentProvider.provide() + const token = await agent.credOfferToken({ + offeredCredentials, + callbackURL: this.appConfig.sdk.callbackUrl + }) + const requestDescription = await this.requestDescriptionFactory.create(token) + + response.json(requestDescription.toJSON()) + } catch (error) { + if (error instanceof InvalidArgumentException) { + response.status(StatusCodes.NOT_FOUND).json({ + message: error.message + }) + } + throw error; + } - response.json(requestDescription.toJSON()) } /** @@ -93,5 +115,6 @@ export class CredentialController { const requestDescription = await this.requestDescriptionFactory.create(token) response.json(requestDescription.toJSON()) + } } diff --git a/src/credential/offer/staticCredentialOfferProvider.ts b/src/credential/offer/staticCredentialOfferProvider.ts index 2cd8792..a4563b4 100644 --- a/src/credential/offer/staticCredentialOfferProvider.ts +++ b/src/credential/offer/staticCredentialOfferProvider.ts @@ -1,6 +1,7 @@ import { injectable, multiInject } from 'inversify' import { TYPES } from '../../types' import { CredentialOffer } from '@jolocom/protocol-ts' +import { InvalidArgumentException } from '../../exception/invalidArgumentException' /** * This implementation is registry of all predefined (static) {@link CredentialOffer} instances. @@ -23,7 +24,7 @@ export class StaticCredentialOfferProvider { private assertExists(type: string) { if (!this.credentialOffers.some(credentialOffer => credentialOffer.type === type)) { - throw new Error(`Credential offer with type '${type}' not found.`) + throw new InvalidArgumentException(`Credential offer with type '${type}' not found.`) } } } diff --git a/src/credential/staticClaimsMetadataProvider.ts b/src/credential/staticClaimsMetadataProvider.ts index d24d962..fd4e3db 100644 --- a/src/credential/staticClaimsMetadataProvider.ts +++ b/src/credential/staticClaimsMetadataProvider.ts @@ -2,6 +2,7 @@ import { inject, injectable } from 'inversify' import { ClaimsMetadataMap } from './claimsMetadataMap' import { TYPES } from '../types' import { BaseMetadata } from 'cred-types-jolocom-core/types' +import { InvalidArgumentException } from '../exception/invalidArgumentException' /** * This implementation is providing service of {@link BaseMetadata}. @@ -24,7 +25,7 @@ export class StaticClaimsMetadataProvider { private assertExists(type: string) { if (!(type in this.claimsMetadataMap)) { - throw new Error(`Claims metadata with type '${type}' not found.`) + throw new InvalidArgumentException(`Claims metadata with type '${type}' not found.`) } } }