diff --git a/apps/attestation-manager/.eslintrc.json b/apps/attestation-manager/.eslintrc.json new file mode 100644 index 0000000000000000000000000000000000000000..9d9c0db55bb1e91c5f2e7b64a02bc6bf69fc7cb5 --- /dev/null +++ b/apps/attestation-manager/.eslintrc.json @@ -0,0 +1,18 @@ +{ + "extends": ["../../.eslintrc.json"], + "ignorePatterns": ["!**/*"], + "overrides": [ + { + "files": ["*.ts", "*.tsx", "*.js", "*.jsx"], + "rules": {} + }, + { + "files": ["*.ts", "*.tsx"], + "rules": {} + }, + { + "files": ["*.js", "*.jsx"], + "rules": {} + } + ] +} diff --git a/apps/attestation-manager/deployment/Dockerfile b/apps/attestation-manager/deployment/Dockerfile new file mode 100644 index 0000000000000000000000000000000000000000..96750a73306698cfc0764db1b0b45ed468d90042 --- /dev/null +++ b/apps/attestation-manager/deployment/Dockerfile @@ -0,0 +1,15 @@ +FROM node:18.16.0-buster-slim + +RUN apt update -y && apt install python3 git make build-essential -y + +WORKDIR app + +COPY ./dist/apps/attestation-manager . +COPY package.json yarn.lock ./ + +RUN yarn install + + +EXPOSE 8883 + +CMD ["node", "main.js"] diff --git a/apps/attestation-manager/jest.config.ts b/apps/attestation-manager/jest.config.ts new file mode 100644 index 0000000000000000000000000000000000000000..18a7bc1cca8e8df5a07ea18aabbaebf20e93c204 --- /dev/null +++ b/apps/attestation-manager/jest.config.ts @@ -0,0 +1,11 @@ +/* eslint-disable */ +export default { + displayName: "attestation-manager", + preset: "../../jest.preset.js", + testEnvironment: "node", + transform: { + "^.+\\.[tj]s$": ["ts-jest", { tsconfig: "<rootDir>/tsconfig.spec.json" }], + }, + moduleFileExtensions: ["ts", "js", "html"], + coverageDirectory: "../../coverage/apps/attestation-manager", +}; diff --git a/apps/attestation-manager/project.json b/apps/attestation-manager/project.json new file mode 100644 index 0000000000000000000000000000000000000000..193c32f5168234cb3987d52c361801507db9e3f3 --- /dev/null +++ b/apps/attestation-manager/project.json @@ -0,0 +1,63 @@ +{ + "name": "attestation-manager", + "$schema": "../../node_modules/nx/schemas/project-schema.json", + "sourceRoot": "apps/attestation-manager/src", + "projectType": "application", + "targets": { + "build": { + "executor": "@nx/webpack:webpack", + "outputs": ["{options.outputPath}"], + "defaultConfiguration": "production", + "options": { + "target": "node", + "compiler": "tsc", + "outputPath": "dist/apps/attestation-manager", + "main": "apps/attestation-manager/src/main.ts", + "tsConfig": "apps/attestation-manager/tsconfig.app.json", + "isolatedConfig": true, + "webpackConfig": "apps/attestation-manager/webpack.config.js" + }, + "configurations": { + "development": {}, + "production": {} + } + }, + "serve": { + "executor": "@nx/js:node", + "defaultConfiguration": "development", + "options": { + "buildTarget": "attestation-manager:build" + }, + "configurations": { + "development": { + "buildTarget": "attestation-manager:build:development" + }, + "production": { + "buildTarget": "attestation-manager:build:production" + } + } + }, + "lint": { + "executor": "@nx/linter:eslint", + "outputs": ["{options.outputFile}"], + "options": { + "lintFilePatterns": ["apps/attestation-manager/**/*.ts"] + } + }, + "test": { + "executor": "@nx/jest:jest", + "outputs": ["{workspaceRoot}/coverage/{projectRoot}"], + "options": { + "jestConfig": "apps/attestation-manager/jest.config.ts", + "passWithNoTests": true + }, + "configurations": { + "ci": { + "ci": true, + "codeCoverage": true + } + } + } + }, + "tags": [] +} diff --git a/apps/attestation-manager/src/app/app.controller.ts b/apps/attestation-manager/src/app/app.controller.ts new file mode 100644 index 0000000000000000000000000000000000000000..3c778ceaf87f265b48165656ea5320f0ef4d7fb3 --- /dev/null +++ b/apps/attestation-manager/src/app/app.controller.ts @@ -0,0 +1,89 @@ +import { Body, Controller, Get, Logger } from "@nestjs/common"; + +import { AppService } from "./app.service"; +import { MessagePattern, RpcException } from "@nestjs/microservices"; +import { + CloudEventDto, + ConnectionEvent, + CreateCredentialDefinitionRequsetDto, + CreateInvitationResponseDto, + CreateSchemaRequestDto, + CredentialEvent, + GetConnectionRequestDto, + IssueCredentialRequestDto, + SchemaEvent, +} from "@ocm-engine/dtos"; +import { ProducerService } from "@ocm-engine/nats"; + +@Controller() +export class AppController { + private readonly logger: Logger = new Logger(AppController.name); + + constructor( + private readonly producerService: ProducerService, + private readonly appService: AppService, + ) {} + + @MessagePattern("schemas") + async create( + @Body() + payload: { + data: null | CreateSchemaRequestDto | CreateSchemaRequestDto; + type: SchemaEvent; + source: string; + }, + ): Promise<{ id: string }> { + this.logger.debug(JSON.stringify(payload, null, 2)); + + try { + const event = this.appService.toEvent(payload); + this.logger.debug(JSON.stringify(event, null, 2)); + await this.producerService.publish<typeof payload.data>( + payload.type, + event as CloudEventDto<typeof payload.data>, + ); + + return { id: event.id }; + } catch (e) { + this.logger.debug(JSON.stringify(e, null, 2)); + if (e instanceof Error) { + throw new RpcException(e.message); + } + + throw new RpcException("Internal server error"); + } + } + + @MessagePattern("credentials") + async createCredential( + @Body() + payload: { + data: + | null + | CreateCredentialDefinitionRequsetDto + | IssueCredentialRequestDto; + type: CredentialEvent; + source: string; + }, + ): Promise<{ id: string }> { + this.logger.debug(JSON.stringify(payload, null, 2)); + + try { + const event = this.appService.toEvent(payload); + this.logger.debug(JSON.stringify(event, null, 2)); + await this.producerService.publish<typeof payload.data>( + payload.type, + event as CloudEventDto<typeof payload.data>, + ); + + return { id: event.id }; + } catch (e) { + this.logger.debug(JSON.stringify(e, null, 2)); + if (e instanceof Error) { + throw new RpcException(e.message); + } + + throw new RpcException("Internal server error"); + } + } +} diff --git a/apps/attestation-manager/src/app/app.module.ts b/apps/attestation-manager/src/app/app.module.ts new file mode 100644 index 0000000000000000000000000000000000000000..5fbe1f9a3d38d356789cb2985914f5f1fe66386e --- /dev/null +++ b/apps/attestation-manager/src/app/app.module.ts @@ -0,0 +1,26 @@ +import { Module } from "@nestjs/common"; + +import { AppController } from "./app.controller"; +import { AppService } from "./app.service"; +import { ConfigModule } from "@nestjs/config"; +import { amConfig, amSchema, natsConfig, natsSchema } from "@ocm-engine/config"; +import { ProducerService } from "@ocm-engine/nats"; +import Joi from "joi"; + +const validationSchema = Joi.object({ + nats: natsSchema, + am: amSchema, +}); + +@Module({ + imports: [ + ConfigModule.forRoot({ + isGlobal: true, + load: [natsConfig, amConfig], + validationSchema, + }), + ], + controllers: [AppController], + providers: [ProducerService, AppService], +}) +export class AppModule {} diff --git a/apps/attestation-manager/src/app/app.service.ts b/apps/attestation-manager/src/app/app.service.ts new file mode 100644 index 0000000000000000000000000000000000000000..bb195327d59df12ccbb3cdb032316c8592f1da71 --- /dev/null +++ b/apps/attestation-manager/src/app/app.service.ts @@ -0,0 +1,37 @@ +import { Injectable } from "@nestjs/common"; +import { + ALL_EVENTS, + CloudEventDto, + ConnectionUnsupportedTypeError, + CreateCredentialDefinitionRequsetDto, + CreateSchemaRequestDto, + CredentialEvent, + IssueCredentialRequestDto, + SchemaEvent, +} from "@ocm-engine/dtos"; + +@Injectable() +export class AppService { + toEvent = (payload: { + data: + | null + | CreateSchemaRequestDto + | CreateSchemaRequestDto + | CreateCredentialDefinitionRequsetDto + | IssueCredentialRequestDto; + type: SchemaEvent | CredentialEvent; + source: string; + }) => { + if (ALL_EVENTS.includes(payload.type)) { + throw new ConnectionUnsupportedTypeError(); + } + + const event = new CloudEventDto<typeof payload.data>(); + event.subject = payload.type; + event.source = payload.source; + event.type = payload.type; + event.data = payload.data; + + return event; + }; +} diff --git a/apps/attestation-manager/src/main.ts b/apps/attestation-manager/src/main.ts new file mode 100644 index 0000000000000000000000000000000000000000..b9c9619988ba69898f38e48f5d5b77b109bcf043 --- /dev/null +++ b/apps/attestation-manager/src/main.ts @@ -0,0 +1,37 @@ +/** + * This is not a production server yet! + * This is only a minimal backend to get started. + */ + +import { Logger } from "@nestjs/common"; +import { NestFactory } from "@nestjs/core"; + +import { AppModule } from "./app/app.module"; + +import { MicroserviceOptions, Transport } from "@nestjs/microservices"; +import { ConfigService } from "@nestjs/config"; +import { IAttestationManagerConfig } from "@ocm-engine/config"; +async function bootstrap() { + const app = await NestFactory.create(AppModule); + + const configService = app.get(ConfigService); + const am = configService.get<IAttestationManagerConfig>("am")!; + + app.enableShutdownHooks(); + + const microservice = app.connectMicroservice<MicroserviceOptions>({ + transport: Transport.TCP, + options: { + host: am.host, + port: am.port, + }, + }); + + await app.startAllMicroservices(); + + app.enableShutdownHooks(); + + Logger.log("Application is running"); +} + +bootstrap(); diff --git a/apps/attestation-manager/tsconfig.app.json b/apps/attestation-manager/tsconfig.app.json new file mode 100644 index 0000000000000000000000000000000000000000..954f3ad1c11170724606b4b020297567c518a86b --- /dev/null +++ b/apps/attestation-manager/tsconfig.app.json @@ -0,0 +1,12 @@ +{ + "extends": "./tsconfig.json", + "compilerOptions": { + "outDir": "../../dist/out-tsc", + "module": "commonjs", + "types": ["node"], + "emitDecoratorMetadata": true, + "target": "es2015" + }, + "exclude": ["jest.config.ts", "src/**/*.spec.ts", "src/**/*.test.ts"], + "include": ["src/**/*.ts"] +} diff --git a/apps/attestation-manager/tsconfig.json b/apps/attestation-manager/tsconfig.json new file mode 100644 index 0000000000000000000000000000000000000000..c1e2dd4e8be6f4fe3dca35d044fd912ff41b1c18 --- /dev/null +++ b/apps/attestation-manager/tsconfig.json @@ -0,0 +1,16 @@ +{ + "extends": "../../tsconfig.base.json", + "files": [], + "include": [], + "references": [ + { + "path": "./tsconfig.app.json" + }, + { + "path": "./tsconfig.spec.json" + } + ], + "compilerOptions": { + "esModuleInterop": true + } +} diff --git a/apps/attestation-manager/tsconfig.spec.json b/apps/attestation-manager/tsconfig.spec.json new file mode 100644 index 0000000000000000000000000000000000000000..9b2a121d114b68dcdb5b834ebca032814b499a74 --- /dev/null +++ b/apps/attestation-manager/tsconfig.spec.json @@ -0,0 +1,14 @@ +{ + "extends": "./tsconfig.json", + "compilerOptions": { + "outDir": "../../dist/out-tsc", + "module": "commonjs", + "types": ["jest", "node"] + }, + "include": [ + "jest.config.ts", + "src/**/*.test.ts", + "src/**/*.spec.ts", + "src/**/*.d.ts" + ] +} diff --git a/apps/attestation-manager/webpack.config.js b/apps/attestation-manager/webpack.config.js new file mode 100644 index 0000000000000000000000000000000000000000..0ab513e830c33d6687ae9e14f62f69df7c0df36b --- /dev/null +++ b/apps/attestation-manager/webpack.config.js @@ -0,0 +1,8 @@ +const { composePlugins, withNx } = require("@nx/webpack"); + +// Nx plugins for webpack. +module.exports = composePlugins(withNx(), (config) => { + // Update the webpack config as needed here. + // e.g. `config.plugins.push(new MyPlugin())` + return config; +}); diff --git a/apps/connection-manager/src/main.ts b/apps/connection-manager/src/main.ts index 58d5133a4b4cc97a9208e1df050f3a4a01d85798..eb1af5cf3cff5730e41d42e222ec6a219d48f72b 100644 --- a/apps/connection-manager/src/main.ts +++ b/apps/connection-manager/src/main.ts @@ -9,21 +9,20 @@ import { NestFactory } from "@nestjs/core"; import { AppModule } from "./app/app.module"; import { MicroserviceOptions, Transport } from "@nestjs/microservices"; import { ConfigService } from "@nestjs/config"; -import { IConnectionManager, IGateway } from "@ocm-engine/config"; -import { DocumentBuilder, SwaggerModule } from "@nestjs/swagger"; +import { IConnectionManagerConfig } from "@ocm-engine/config"; async function bootstrap() { const app = await NestFactory.create(AppModule); const configService = app.get(ConfigService); - const gatewayConfig = configService.get<IConnectionManager>("cm")!; + const cmConfig = configService.get<IConnectionManagerConfig>("cm")!; app.enableShutdownHooks(); const microservice = app.connectMicroservice<MicroserviceOptions>({ transport: Transport.TCP, options: { - host: gatewayConfig.host, - port: gatewayConfig.port, + host: cmConfig.host, + port: cmConfig.port, }, }); diff --git a/apps/gateway/src/app/app.module.ts b/apps/gateway/src/app/app.module.ts index 5c54c0585730b619541655f16e5067d00dbb906e..cc91a8cec459825dd035e10bb6d2821339aa9e47 100644 --- a/apps/gateway/src/app/app.module.ts +++ b/apps/gateway/src/app/app.module.ts @@ -4,33 +4,41 @@ import { EventsGateway } from "./events.gateway"; import { ConfigModule } from "@nestjs/config"; import { AppController } from "./app.controller"; import { + amConfig, + amSchema, cmConfig, cmSchema, gatewayConfig, gatewaySchema, } from "@ocm-engine/config"; -import { ConnectionManagerClient } from "@ocm-engine/clients"; +import { + AttestationManagerClient, + ConnectionManagerClient, +} from "@ocm-engine/clients"; import Joi from "joi"; -import { ConnectionController } from "./connection.controller"; +import { ConnectionController } from "./managers/connection.controller"; import { APP_PIPE } from "@nestjs/core"; +import { AttestationController } from "./managers/attestation.controller"; const validationSchema = Joi.object({ gateway: gatewaySchema, cm: cmSchema, + am: amSchema, }); @Module({ imports: [ ConfigModule.forRoot({ isGlobal: true, - load: [gatewayConfig, cmConfig], + load: [gatewayConfig, cmConfig, amConfig], validationSchema, }), ], - controllers: [AppController, ConnectionController], + controllers: [AppController, ConnectionController, AttestationController], providers: [ EventsGateway, ConnectionManagerClient, + AttestationManagerClient, { provide: APP_PIPE, useValue: new ValidationPipe({ diff --git a/apps/gateway/src/app/managers/attestation.controller.ts b/apps/gateway/src/app/managers/attestation.controller.ts new file mode 100644 index 0000000000000000000000000000000000000000..f4f4d47b36a47d1db5ec9fface825d82157020c0 --- /dev/null +++ b/apps/gateway/src/app/managers/attestation.controller.ts @@ -0,0 +1,316 @@ +import { + BadRequestException, + Body, + Controller, + Get, + Param, + Post, + UseFilters, +} from "@nestjs/common"; +import { AllExceptionsHandler } from "../exception.handler"; +import { AttestationManagerClient } from "@ocm-engine/clients"; +import { + AcceptCredentialOfferRequestDto, + CONNECTION_ACCEPT, + CreateCredentialDefinitionRequsetDto, + CreateSchemaRequestDto, + CRED_DEF_CREATE, + CRED_ISSUE, + CRED_LIST, + CRED_OFFER_ACCEPT, + CRED_OFFER_LIST, + GatewayAcceptedResponseDto, + GetSchemaRequestDto, + IssueCredentialRequestDto, + SCHEMA_CREATE, + SCHEMA_GET, + SCHEMA_LIST, +} from "@ocm-engine/dtos"; +import { + ApiBadRequestResponse, + ApiInternalServerErrorResponse, + ApiOperation, + ApiResponse, +} from "@nestjs/swagger"; + +@UseFilters(AllExceptionsHandler) +@Controller("v1") +export class AttestationController { + constructor(private readonly amClient: AttestationManagerClient) {} + + @Post("/schemas") + @ApiResponse({ + status: 201, + description: + "Request is accepted for execution, the response id will match the event id received from the web socket", + type: GatewayAcceptedResponseDto, + }) + @ApiBadRequestResponse({ + status: 400, + description: + "Error in sending data to connection manager. This error shows that attestation manager could not convert request to event or attestation manager could not send the event to the broker.", + type: BadRequestException, + }) + @ApiInternalServerErrorResponse({ + status: 500, + description: "Unknown error", + }) + @ApiOperation({ + summary: "Create schema", + description: + "Method will create schema. The id of the response will be matched when you receive event from the websocket", + tags: ["Schema"], + }) + async createSchema(@Body() schemaDto: CreateSchemaRequestDto) { + return this.amClient.sendPayload<CreateSchemaRequestDto>({ + pattern: "schemas", + payload: { + source: "/schema", + data: schemaDto, + type: SCHEMA_CREATE, + }, + }); + } + + @Post("/schemas-by-id") + @ApiResponse({ + status: 201, + description: + "Request is accepted for execution, the response id will match the event id received from the web socket", + type: GatewayAcceptedResponseDto, + }) + @ApiBadRequestResponse({ + status: 400, + description: + "Error in sending data to connection manager. This error shows that attestation manager could not convert request to event or attestation manager could not send the event to the broker.", + type: BadRequestException, + }) + @ApiInternalServerErrorResponse({ + status: 500, + description: "Unknown error", + }) + @ApiOperation({ + summary: "Get schema by id", + description: + "Method will fetch specific schema or return null. The id of the response will be matched when you receive event from the websocket", + tags: ["Schema"], + }) + async getSchemaById(@Body() schemaRequestDto: GetSchemaRequestDto) { + return this.amClient.sendPayload<GetSchemaRequestDto>({ + pattern: "schemas", + payload: { + source: "/schemas-by-id", + data: schemaRequestDto, + type: SCHEMA_GET, + }, + }); + } + + @Get("/schemas") + @ApiResponse({ + status: 200, + description: + "Request is accepted for execution, the response id will match the event id received from the web socket", + type: GatewayAcceptedResponseDto, + }) + @ApiBadRequestResponse({ + status: 400, + description: + "Error in sending data to attestation manager. This error shows that attestation manager could not convert request to event or attestation manager could not send the event to the broker.", + type: BadRequestException, + }) + @ApiInternalServerErrorResponse({ + status: 500, + description: "Unknown error", + }) + @ApiOperation({ + summary: "List all schemas", + description: + "Method will fetch all schemas. The id of the response will be matched when you receive event from the websocket", + tags: ["Schema"], + }) + async listSchema() { + return this.amClient.sendPayload<null>({ + pattern: "schemas", + payload: { + source: "/schema", + data: null, + type: SCHEMA_LIST, + }, + }); + } + + @Post("credential/definition") + @ApiResponse({ + status: 201, + description: + "Request is accepted for execution, the response id will match the event id received from the web socket", + type: GatewayAcceptedResponseDto, + }) + @ApiBadRequestResponse({ + status: 400, + description: + "Error in sending data to attestation manager. This error shows that attestation manager could not convert request to event or attestation manager could not send the event to the broker.", + type: BadRequestException, + }) + @ApiInternalServerErrorResponse({ + status: 500, + description: "Unknown error", + }) + @ApiOperation({ + summary: "Create credential definition", + description: + "Method create credential definition. The id of the response will be matched when you receive event from the websocket", + tags: ["Credentials"], + }) + createCredentialDefinition( + @Body() credentialDefinitionDto: CreateCredentialDefinitionRequsetDto, + ) { + return this.amClient.sendPayload({ + pattern: "credentials", + payload: { + source: "/credential/definition", + data: credentialDefinitionDto, + type: CRED_DEF_CREATE, + }, + }); + } + + @Post("credential/issue") + @ApiResponse({ + status: 201, + description: + "Request is accepted for execution, the response id will match the event id received from the web socket", + type: GatewayAcceptedResponseDto, + }) + @ApiBadRequestResponse({ + status: 400, + description: + "Error in sending data to attestation manager. This error shows that attestation manager could not convert request to event or attestation manager could not send the event to the broker.", + type: BadRequestException, + }) + @ApiInternalServerErrorResponse({ + status: 500, + description: "Unknown error", + }) + @ApiOperation({ + summary: "Issue credential definition", + description: + "Method issue credential definition. The id of the response will be matched when you receive event from the websocket", + tags: ["Credentials"], + }) + issueCredential(@Body() issueCredentialDto: IssueCredentialRequestDto) { + return this.amClient.sendPayload({ + pattern: "credentials", + payload: { + source: "credential/issue", + data: issueCredentialDto, + type: CRED_ISSUE, + }, + }); + } + + @Get("/credentials") + @ApiResponse({ + status: 200, + description: + "Request is accepted for execution, the response id will match the event id received from the web socket", + type: GatewayAcceptedResponseDto, + }) + @ApiBadRequestResponse({ + status: 400, + description: + "Error in sending data to attestation manager. This error shows that attestation manager could not convert request to event or attestation manager could not send the event to the broker.", + type: BadRequestException, + }) + @ApiInternalServerErrorResponse({ + status: 500, + description: "Unknown error", + }) + @ApiOperation({ + summary: "List all credential", + description: + "Method list credential definition no filters applied. The id of the response will be matched when you receive event from the websocket", + tags: ["Credentials"], + }) + credentials() { + return this.amClient.sendPayload({ + pattern: "credentials", + payload: { + source: "credentials", + data: null, + type: CRED_LIST, + }, + }); + } + + @Get("/credential/offers") + @ApiResponse({ + status: 200, + description: + "Request is accepted for execution, the response id will match the event id received from the web socket", + type: GatewayAcceptedResponseDto, + }) + @ApiBadRequestResponse({ + status: 400, + description: + "Error in sending data to attestation manager. This error shows that attestation manager could not convert request to event or attestation manager could not send the event to the broker.", + type: BadRequestException, + }) + @ApiInternalServerErrorResponse({ + status: 500, + description: "Unknown error", + }) + @ApiOperation({ + summary: "List all credential offers", + description: + "Method list offers that are sent, but not accepted. The id of the response will be matched when you receive event from the websocket", + tags: ["Credentials"], + }) + getCredentialOffers() { + return this.amClient.sendPayload({ + pattern: "credentials", + payload: { + source: "credentials/offers", + data: null, + type: CRED_OFFER_LIST, + }, + }); + } + + @Post("/credential/offers/:credential_record_id/accept") + @ApiResponse({ + status: 200, + description: + "Request is accepted for execution, the response id will match the event id received from the web socket", + type: GatewayAcceptedResponseDto, + }) + @ApiBadRequestResponse({ + status: 400, + description: + "Error in sending data to attestation manager. This error shows that attestation manager could not convert request to event or attestation manager could not send the event to the broker.", + type: BadRequestException, + }) + @ApiInternalServerErrorResponse({ + status: 500, + description: "Unknown error", + }) + @ApiOperation({ + summary: "Accept credential offers", + description: + "Method list accept credential offer. The id of the response will be matched when you receive event from the websocket", + tags: ["Credentials"], + }) + acceptCredential(@Param("credential_record_id") credentialRecordId: string) { + const data = new AcceptCredentialOfferRequestDto(); + data.credentialRecordId = credentialRecordId; + return this.amClient.sendPayload({ + pattern: "credentials", + payload: { + source: "credential/:id/accept", + data, + type: CRED_OFFER_ACCEPT, + }, + }); + } +} diff --git a/apps/gateway/src/app/connection.controller.ts b/apps/gateway/src/app/managers/connection.controller.ts similarity index 98% rename from apps/gateway/src/app/connection.controller.ts rename to apps/gateway/src/app/managers/connection.controller.ts index 47c866f5abfa38362d6a326da8867a751753ca01..52c86c6940baa9896c050e026b81306b68733349 100644 --- a/apps/gateway/src/app/connection.controller.ts +++ b/apps/gateway/src/app/managers/connection.controller.ts @@ -17,7 +17,7 @@ import { GatewayAcceptedResponseDto, GetConnectionRequestDto, } from "@ocm-engine/dtos"; -import { AllExceptionsHandler } from "./exception.handler"; +import { AllExceptionsHandler } from "../exception.handler"; import { ApiBadRequestResponse, ApiBody, diff --git a/compose/docker-compose.yml b/compose/docker-compose.yml index 145594f59efdbdbf91121906e54552db86dd54d6..2a0c5fb5ac8a488ffac5bb6ce7a9b9a4777c8def 100644 --- a/compose/docker-compose.yml +++ b/compose/docker-compose.yml @@ -44,6 +44,20 @@ services: broker: condition: service_started + am-issuer: + container_name: am-issuer + build: + context: "../" + dockerfile: "./apps/attestation-manager/deployment/Dockerfile" + env_file: + - ./env/issuer.env + ports: + - "8883" + depends_on: + broker: + condition: service_started + + pg_db: image: 'postgres:latest' ports: diff --git a/compose/env/issuer.env b/compose/env/issuer.env index 813e39d527cdfcb9f1edeb617670fd4ac6d9fbff..aa7308f2eb4a20dcd44f71819f828b9083907c69 100644 --- a/compose/env/issuer.env +++ b/compose/env/issuer.env @@ -26,3 +26,6 @@ GATEWAY_HOST=gateway-issuer CONNECTION_SERVICE_TCP_PORT=8882 CONNECTION_SERVICE_HOST=cm-issuer + +CONNECTION_SERVICE_TCP_PORT=8883 +CONNECTION_SERVICE_HOST=am-issuer diff --git a/gateway-swagger.json b/gateway-swagger.json index 0b4ba37b7148b7f7927696fe6212187ebe1d2827..7abc420082b25e60f8ce051ad2a4245f400c9f58 100644 --- a/gateway-swagger.json +++ b/gateway-swagger.json @@ -1 +1 @@ -{"openapi":"3.0.0","paths":{"/api/v1/invitation":{"post":{"operationId":"ConnectionController_createInvitation","summary":"Create invitation for connection","description":"Method will create invitation url. The id of the response will be matched when you receive event from the websocket","tags":["Connections"],"parameters":[],"responses":{"201":{"description":"Request is accepted for execution, the response id will match the event id received from the web socket","content":{"application/json":{"schema":{"$ref":"#/components/schemas/GatewayAcceptedResponseDto"}}}},"400":{"description":"Error in sending data to connection manager. This error shows that connection manager could not convert request to event or connection manager could not send the event to the broker.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/BadRequestException"}}}},"500":{"description":"Unknown error"}}}},"/api/v1/invitation/accept":{"post":{"operationId":"ConnectionController_acceptInvitation","summary":"Accept invitation for connection","description":"Method will accept the invitation and will return connection thought the websocket. The id of the response will be matched when you receive event from the websocket","tags":["Connections"],"parameters":[],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateInvitationResponseDto"}}}},"responses":{"201":{"description":"Request is accepted for execution, the response id will match the event id received from the web socket","content":{"application/json":{"schema":{"$ref":"#/components/schemas/GatewayAcceptedResponseDto"}}}},"400":{"description":"Error in sending data to connection manager. This error shows that connection manager could not convert request to event or connection manager could not send the event to the broker.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/BadRequestException"}}}},"500":{"description":"Unknown error"}}}},"/api/v1/connections":{"get":{"operationId":"ConnectionController_list","summary":"List all connections","description":"The id of the response will be matched when you receive event from the websocket","tags":["Connections"],"parameters":[],"responses":{"200":{"description":"Request is accepted for execution, the response id will match the event id received from the web socket","content":{"application/json":{"schema":{"$ref":"#/components/schemas/GatewayAcceptedResponseDto"}}}},"400":{"description":"Error in sending data to connection manager. This error shows that connection manager could not convert request to event or connection manager could not send the event to the broker.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/BadRequestException"}}}},"500":{"description":"Unknown error"}}}},"/api/v1/connections/{id}":{"get":{"operationId":"ConnectionController_getById","summary":"Get connection by id","description":"The method will search for connection id, if not found null will be returned. The id of the response will be matched when you receive event from the websocket","tags":["Connections"],"parameters":[{"name":"id","required":true,"in":"path","schema":{"type":"string"}}],"responses":{"200":{"description":"Request is accepted for execution, the response id will match the event id received from the web socket","content":{"application/json":{"schema":{"$ref":"#/components/schemas/GatewayAcceptedResponseDto"}}}},"400":{"description":"Error in sending data to connection manager. This error shows that connection manager could not convert request to event or connection manager could not send the event to the broker.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/BadRequestException"}}}},"500":{"description":"Unknown error"}}}}},"info":{"title":"Gateway","description":"OCM ENGINE GATEWAY API","version":"1.0","contact":{}},"tags":[],"servers":[{"url":"http://0.0.0.0:8081"}],"components":{"schemas":{"CloudEventDto":{"type":"object","properties":{}},"GatewayAcceptedResponseDto":{"type":"object","properties":{"id":{"type":"string","example":"80633e6d-c606-4539-a3df-287fedd09253"}},"required":["id"]},"BadRequestException":{"type":"object","properties":{}},"CreateInvitationResponseDto":{"type":"object","properties":{"invitationUrl":{"type":"string","description":"A list of user's roles","example":"http://0.0.0.0:8001?oob=eyJAdHlwZSI6Imh0dHBzOi8vZGlkY29tbS5vcmcvb3V0LW9mLWJhbmQvMS4xL2ludml0YXRpb24iLCJAaWQiOiIzYWExNGIzNC04YTk5LTQxY2UtYTY3NC1jODUxYmVhMTIxMWEiLCJsYWJlbCI6IkRFeGNWYXNkX0FHRU5UXzQ1IiwiYWNjZXB0IjpbImRpZGNvbW0vYWlwMSIsImRpZGNvbW0vYWlwMjtlbnY9cmZjMTkiXSwiaGFuZHNoYWtlX3Byb3RvY29scyI6WyJodHRwczovL2RpZGNvbW0ub3JnL2RpZGV4Y2hhbmdlLzEuMCIsImh0dHBzOi8vZGlkY29tbS5vcmcvY29ubmVjdGlvbnMvMS4wIl0sInNlcnZpY2VzIjpbeyJpZCI6IiNpbmxpbmUtMCIsInNlcnZpY2VFbmRwb2ludCI6Imh0dHA6Ly8wLjAuMC4wOjgwMDEiLCJ0eXBlIjoiZGlkLWNvbW11bmljYXRpb24iLCJyZWNpcGllbnRLZXlzIjpbImRpZDprZXk6ejZNa3VFcHllc1pNa3k0a1BpQzhEOEplZERlcm55YTFuaTREMUF3ZmdnWWt6YmR4Il0sInJvdXRpbmdLZXlzIjpbXX1dfQ"}},"required":["invitationUrl"]}}}} \ No newline at end of file +{"openapi":"3.0.0","paths":{"/api/v1/invitation":{"post":{"operationId":"ConnectionController_createInvitation","summary":"Create invitation for connection","description":"Method will create invitation url. The id of the response will be matched when you receive event from the websocket","tags":["Connections"],"parameters":[],"responses":{"201":{"description":"Request is accepted for execution, the response id will match the event id received from the web socket","content":{"application/json":{"schema":{"$ref":"#/components/schemas/GatewayAcceptedResponseDto"}}}},"400":{"description":"Error in sending data to connection manager. This error shows that connection manager could not convert request to event or connection manager could not send the event to the broker.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/BadRequestException"}}}},"500":{"description":"Unknown error"}}}},"/api/v1/invitation/accept":{"post":{"operationId":"ConnectionController_acceptInvitation","summary":"Accept invitation for connection","description":"Method will accept the invitation and will return connection thought the websocket. The id of the response will be matched when you receive event from the websocket","tags":["Connections"],"parameters":[],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateInvitationResponseDto"}}}},"responses":{"201":{"description":"Request is accepted for execution, the response id will match the event id received from the web socket","content":{"application/json":{"schema":{"$ref":"#/components/schemas/GatewayAcceptedResponseDto"}}}},"400":{"description":"Error in sending data to connection manager. This error shows that connection manager could not convert request to event or connection manager could not send the event to the broker.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/BadRequestException"}}}},"500":{"description":"Unknown error"}}}},"/api/v1/connections":{"get":{"operationId":"ConnectionController_list","summary":"List all connections","description":"The id of the response will be matched when you receive event from the websocket","tags":["Connections"],"parameters":[],"responses":{"200":{"description":"Request is accepted for execution, the response id will match the event id received from the web socket","content":{"application/json":{"schema":{"$ref":"#/components/schemas/GatewayAcceptedResponseDto"}}}},"400":{"description":"Error in sending data to connection manager. This error shows that connection manager could not convert request to event or connection manager could not send the event to the broker.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/BadRequestException"}}}},"500":{"description":"Unknown error"}}}},"/api/v1/connections/{id}":{"get":{"operationId":"ConnectionController_getById","summary":"Get connection by id","description":"The method will search for connection id, if not found null will be returned. The id of the response will be matched when you receive event from the websocket","tags":["Connections"],"parameters":[{"name":"id","required":true,"in":"path","schema":{"type":"string"}}],"responses":{"200":{"description":"Request is accepted for execution, the response id will match the event id received from the web socket","content":{"application/json":{"schema":{"$ref":"#/components/schemas/GatewayAcceptedResponseDto"}}}},"400":{"description":"Error in sending data to connection manager. This error shows that connection manager could not convert request to event or connection manager could not send the event to the broker.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/BadRequestException"}}}},"500":{"description":"Unknown error"}}}},"/api/v1/schemas":{"post":{"operationId":"AttestationController_createSchema","summary":"Create schema","description":"Method will create schema. The id of the response will be matched when you receive event from the websocket","tags":["Schema"],"parameters":[],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateSchemaRequestDto"}}}},"responses":{"201":{"description":"Request is accepted for execution, the response id will match the event id received from the web socket","content":{"application/json":{"schema":{"$ref":"#/components/schemas/GatewayAcceptedResponseDto"}}}},"400":{"description":"Error in sending data to connection manager. This error shows that attestation manager could not convert request to event or attestation manager could not send the event to the broker.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/BadRequestException"}}}},"500":{"description":"Unknown error"}}},"get":{"operationId":"AttestationController_listSchema","summary":"List all schemas","description":"Method will fetch all schemas. The id of the response will be matched when you receive event from the websocket","tags":["Schema"],"parameters":[],"responses":{"200":{"description":"Request is accepted for execution, the response id will match the event id received from the web socket","content":{"application/json":{"schema":{"$ref":"#/components/schemas/GatewayAcceptedResponseDto"}}}},"400":{"description":"Error in sending data to attestation manager. This error shows that attestation manager could not convert request to event or attestation manager could not send the event to the broker.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/BadRequestException"}}}},"500":{"description":"Unknown error"}}}},"/api/v1/schemas-by-id":{"post":{"operationId":"AttestationController_getSchemaById","summary":"Get schema by id","description":"Method will fetch specific schema or return null. The id of the response will be matched when you receive event from the websocket","tags":["Schema"],"parameters":[],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/GetSchemaRequestDto"}}}},"responses":{"201":{"description":"Request is accepted for execution, the response id will match the event id received from the web socket","content":{"application/json":{"schema":{"$ref":"#/components/schemas/GatewayAcceptedResponseDto"}}}},"400":{"description":"Error in sending data to connection manager. This error shows that attestation manager could not convert request to event or attestation manager could not send the event to the broker.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/BadRequestException"}}}},"500":{"description":"Unknown error"}}}},"/api/v1/credential/definition":{"post":{"operationId":"AttestationController_createCredentialDefinition","summary":"Create credential definition","description":"Method create credential definition. The id of the response will be matched when you receive event from the websocket","tags":["Credentials"],"parameters":[],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateCredentialDefinitionRequsetDto"}}}},"responses":{"201":{"description":"Request is accepted for execution, the response id will match the event id received from the web socket","content":{"application/json":{"schema":{"$ref":"#/components/schemas/GatewayAcceptedResponseDto"}}}},"400":{"description":"Error in sending data to attestation manager. This error shows that attestation manager could not convert request to event or attestation manager could not send the event to the broker.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/BadRequestException"}}}},"500":{"description":"Unknown error"}}}},"/api/v1/credential/issue":{"post":{"operationId":"AttestationController_issueCredential","summary":"Issue credential definition","description":"Method issue credential definition. The id of the response will be matched when you receive event from the websocket","tags":["Credentials"],"parameters":[],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/IssueCredentialRequestDto"}}}},"responses":{"201":{"description":"Request is accepted for execution, the response id will match the event id received from the web socket","content":{"application/json":{"schema":{"$ref":"#/components/schemas/GatewayAcceptedResponseDto"}}}},"400":{"description":"Error in sending data to attestation manager. This error shows that attestation manager could not convert request to event or attestation manager could not send the event to the broker.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/BadRequestException"}}}},"500":{"description":"Unknown error"}}}},"/api/v1/credentials":{"get":{"operationId":"AttestationController_credentials","summary":"List all credential","description":"Method list credential definition no filters applied. The id of the response will be matched when you receive event from the websocket","tags":["Credentials"],"parameters":[],"responses":{"200":{"description":"Request is accepted for execution, the response id will match the event id received from the web socket","content":{"application/json":{"schema":{"$ref":"#/components/schemas/GatewayAcceptedResponseDto"}}}},"400":{"description":"Error in sending data to attestation manager. This error shows that attestation manager could not convert request to event or attestation manager could not send the event to the broker.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/BadRequestException"}}}},"500":{"description":"Unknown error"}}}},"/api/v1/credential/offers":{"get":{"operationId":"AttestationController_getCredentialOffers","summary":"List all credential offers","description":"Method list offers that are sent, but not accepted. The id of the response will be matched when you receive event from the websocket","tags":["Credentials"],"parameters":[],"responses":{"200":{"description":"Request is accepted for execution, the response id will match the event id received from the web socket","content":{"application/json":{"schema":{"$ref":"#/components/schemas/GatewayAcceptedResponseDto"}}}},"400":{"description":"Error in sending data to attestation manager. This error shows that attestation manager could not convert request to event or attestation manager could not send the event to the broker.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/BadRequestException"}}}},"500":{"description":"Unknown error"}}}},"/api/v1/credential/offers/{credential_record_id}/accept":{"post":{"operationId":"AttestationController_acceptCredential","summary":"Accept credential offers","description":"Method list accept credential offer. The id of the response will be matched when you receive event from the websocket","tags":["Credentials"],"parameters":[{"name":"credential_record_id","required":true,"in":"path","schema":{"type":"string"}}],"responses":{"200":{"description":"Request is accepted for execution, the response id will match the event id received from the web socket","content":{"application/json":{"schema":{"$ref":"#/components/schemas/GatewayAcceptedResponseDto"}}}},"201":{"description":""},"400":{"description":"Error in sending data to attestation manager. This error shows that attestation manager could not convert request to event or attestation manager could not send the event to the broker.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/BadRequestException"}}}},"500":{"description":"Unknown error"}}}}},"info":{"title":"Gateway","description":"OCM ENGINE GATEWAY API","version":"1.0","contact":{}},"tags":[],"servers":[{"url":"http://0.0.0.0:8081"}],"components":{"schemas":{"CloudEventDto":{"type":"object","properties":{}},"GatewayAcceptedResponseDto":{"type":"object","properties":{"id":{"type":"string","example":"80633e6d-c606-4539-a3df-287fedd09253"}},"required":["id"]},"BadRequestException":{"type":"object","properties":{}},"CreateInvitationResponseDto":{"type":"object","properties":{"invitationUrl":{"type":"string","description":"A list of user's roles","example":"http://0.0.0.0:8001?oob=eyJAdHlwZSI6Imh0dHBzOi8vZGlkY29tbS5vcmcvb3V0LW9mLWJhbmQvMS4xL2ludml0YXRpb24iLCJAaWQiOiIzYWExNGIzNC04YTk5LTQxY2UtYTY3NC1jODUxYmVhMTIxMWEiLCJsYWJlbCI6IkRFeGNWYXNkX0FHRU5UXzQ1IiwiYWNjZXB0IjpbImRpZGNvbW0vYWlwMSIsImRpZGNvbW0vYWlwMjtlbnY9cmZjMTkiXSwiaGFuZHNoYWtlX3Byb3RvY29scyI6WyJodHRwczovL2RpZGNvbW0ub3JnL2RpZGV4Y2hhbmdlLzEuMCIsImh0dHBzOi8vZGlkY29tbS5vcmcvY29ubmVjdGlvbnMvMS4wIl0sInNlcnZpY2VzIjpbeyJpZCI6IiNpbmxpbmUtMCIsInNlcnZpY2VFbmRwb2ludCI6Imh0dHA6Ly8wLjAuMC4wOjgwMDEiLCJ0eXBlIjoiZGlkLWNvbW11bmljYXRpb24iLCJyZWNpcGllbnRLZXlzIjpbImRpZDprZXk6ejZNa3VFcHllc1pNa3k0a1BpQzhEOEplZERlcm55YTFuaTREMUF3ZmdnWWt6YmR4Il0sInJvdXRpbmdLZXlzIjpbXX1dfQ"}},"required":["invitationUrl"]},"CreateSchemaRequestDto":{"type":"object","properties":{"name":{"type":"string","example":"my test schema"},"attributes":{"example":["first_name, last_name"],"type":"array","items":{"type":"string"}},"version":{"type":"string","example":"1.0.2","pattern":"/^(\\d+\\.)?(\\d+\\.)?(\\*|\\d+)$/"}},"required":["name","attributes","version"]},"GetSchemaRequestDto":{"type":"object","properties":{"schemaId":{"type":"string","example":"did:indy:LEDNGER:SXM76gQwRnjkgoz2oBnGjd/anoncreds/v0/SCHEMA/test schema/1.0.2"}},"required":["schemaId"]},"CreateCredentialDefinitionRequsetDto":{"type":"object","properties":{"schemaId":{"type":"string"},"tag":{"type":"string"}},"required":["schemaId","tag"]},"IssueCredentialAttributes":{"type":"object","properties":{"name":{"type":"string"},"value":{"type":"string"}},"required":["name","value"]},"IssueCredentialRequestDto":{"type":"object","properties":{"connectionId":{"type":"string"},"credentialDefinitionId":{"type":"string"},"attributes":{"type":"array","items":{"$ref":"#/components/schemas/IssueCredentialAttributes"}}},"required":["connectionId","credentialDefinitionId","attributes"]}}}} \ No newline at end of file diff --git a/libs/asker/src/asker-nats/agent.consumer.service.ts b/libs/asker/src/asker-nats/agent.consumer.service.ts index 751211a02ab6f56f815c3cb5e29cc61db094f3c5..1a333e3a5d238661a53c3087b36b7213cf98be2f 100644 --- a/libs/asker/src/asker-nats/agent.consumer.service.ts +++ b/libs/asker/src/asker-nats/agent.consumer.service.ts @@ -10,12 +10,25 @@ import { ConfigService } from "@nestjs/config"; import { IConfAgent } from "@ocm-engine/config"; import { GatewayClient } from "@ocm-engine/clients"; import { + AcceptCredentialOfferRequestDto, CONNECTION_ACCEPT, CONNECTION_CREATE, CONNECTION_GET, CONNECTION_LIST, + CreateCredentialDefinitionRequsetDto, CreateInvitationResponseDto, + CreateSchemaRequestDto, + CRED_DEF_CREATE, + CRED_ISSUE, + CRED_LIST, + CRED_OFFER_ACCEPT, + CRED_OFFER_LIST, GetConnectionRequestDto, + GetSchemaRequestDto, + IssueCredentialRequestDto, + SCHEMA_CREATE, + SCHEMA_GET, + SCHEMA_LIST, } from "@ocm-engine/dtos"; @Injectable() @@ -41,15 +54,15 @@ export class AgentConsumerService implements OnModuleInit, OnModuleDestroy { await this.consumerService.subscribe(async (event) => { this.logger.debug(JSON.stringify(event, null, 2)); let data; + let dto; switch (event.type) { case CONNECTION_CREATE: data = await this.agentService.createInvitation(); break; case CONNECTION_ACCEPT: - // eslint-disable-next-line no-case-declarations - const c = event.data as CreateInvitationResponseDto; - data = await this.agentService.acceptInvitation(c.invitationUrl); + dto = event.data as CreateInvitationResponseDto; + data = await this.agentService.acceptInvitation(dto.invitationUrl); break; case CONNECTION_LIST: @@ -57,9 +70,47 @@ export class AgentConsumerService implements OnModuleInit, OnModuleDestroy { break; case CONNECTION_GET: - // eslint-disable-next-line no-case-declarations - const g = event.data as GetConnectionRequestDto; - data = await this.agentService.getConnectionById(g.connectionId); + dto = event.data as GetConnectionRequestDto; + data = await this.agentService.getConnectionById(dto.connectionId); + break; + + case SCHEMA_CREATE: + dto = event.data as CreateSchemaRequestDto; + data = await this.agentService.createSchema(dto); + break; + case SCHEMA_LIST: + data = await this.agentService.fetchSchemas(); + break; + + case SCHEMA_GET: + dto = event.data as GetSchemaRequestDto; + data = await this.agentService.getSchemaById(dto.schemaId); + break; + case CRED_DEF_CREATE: + data = await this.agentService.createCredentialDefinition( + event.data as CreateCredentialDefinitionRequsetDto, + ); + break; + + case CRED_ISSUE: + data = await this.agentService.issueCredential( + event.data as IssueCredentialRequestDto, + ); + break; + + case CRED_LIST: + data = await this.agentService.credentials(); + break; + + case CRED_OFFER_LIST: + data = await this.agentService.credentialByStatedOfferReceived(); + break; + + case CRED_OFFER_ACCEPT: + dto = event.data as AcceptCredentialOfferRequestDto; + data = await this.agentService.acceptCredential( + dto.credentialRecordId, + ); } event.data = data; diff --git a/libs/asker/src/asker/agent.service.ts b/libs/asker/src/asker/agent.service.ts index 88013bdbc91c76abbeec755f5bee2c867e204890..d311a3eaeda17537995f89fa7a4c1e85470d1f52 100644 --- a/libs/asker/src/asker/agent.service.ts +++ b/libs/asker/src/asker/agent.service.ts @@ -63,6 +63,14 @@ export class AgentService { return this.asker.agent.connections.findById(id); }; + fetchSchemas = () => { + return this.asker.agent.modules.anoncreds.getCreatedSchemas({}); + }; + + getSchemaById = (schemaId: string) => { + return this.asker.agent.modules.anoncreds.getSchema(schemaId); + }; + createSchema = async (schema: CreateSchemaRequestDto) => { const dids = await this.asker.agent.dids.getCreatedDids({ method: "indy" }); @@ -82,6 +90,7 @@ export class AgentService { } const response = new CreateSchemaResponseDto(); + response.name = schemaResult.schemaState.schema.name; response.schemaId = schemaResult.schemaState.schemaId; response.issuerId = schemaResult.schemaState.schema.issuerId; diff --git a/libs/clients/src/index.ts b/libs/clients/src/index.ts index 8191daf640fdfb1a5726344f7023277e2696f268..76a58bf05b250a6fda72297b742165eddd0e54a9 100644 --- a/libs/clients/src/index.ts +++ b/libs/clients/src/index.ts @@ -1,2 +1,3 @@ export * from "./lib/gateway.client"; export * from "./lib/connection.manager.client"; +export * from "./lib/attestation.manager.client"; diff --git a/libs/clients/src/lib/attestation.manager.client.ts b/libs/clients/src/lib/attestation.manager.client.ts new file mode 100644 index 0000000000000000000000000000000000000000..4b66e730d589a90217df024762b7302ae285988b --- /dev/null +++ b/libs/clients/src/lib/attestation.manager.client.ts @@ -0,0 +1,66 @@ +import { + BadRequestException, + Injectable, + InternalServerErrorException, + Logger, +} from "@nestjs/common"; +import { + ClientProxy, + ClientProxyFactory, + Transport, +} from "@nestjs/microservices"; +import { ConfigService } from "@nestjs/config"; +import { + IAttestationManagerConfig, + IConnectionManagerConfig, +} from "@ocm-engine/config"; +import { lastValueFrom } from "rxjs"; + +@Injectable() +export class AttestationManagerClient { + private client: ClientProxy; + private cmConfig: IAttestationManagerConfig; + private readonly logger: Logger = new Logger(AttestationManagerClient.name); + + constructor(configService: ConfigService) { + this.cmConfig = configService.get<IConnectionManagerConfig>("am")!; + + this.client = ClientProxyFactory.create({ + transport: Transport.TCP, + options: { + host: this.cmConfig.host, + port: this.cmConfig.port, + }, + }); + } + + async sendPayload<T>({ + pattern, + payload, + }: { + pattern: string; + payload: { + data: T; + type: string; + source: string; + }; + }): Promise<{ + id: string; + }> { + this.logger.debug( + `sending payload to attestation manager ${JSON.stringify( + payload, + null, + 2, + )}`, + ); + + return lastValueFrom(this.client.send(pattern, payload)).catch((e) => { + if (e.message === "Internal server error") { + throw new InternalServerErrorException(); + } + + throw new BadRequestException(e.message); + }); + } +} diff --git a/libs/clients/src/lib/connection.manager.client.ts b/libs/clients/src/lib/connection.manager.client.ts index 04183ed7e8764a701b7f36546b8bf60acdd7d52a..3554fb1e95286ef0ab00a8f576af095d05d331c3 100644 --- a/libs/clients/src/lib/connection.manager.client.ts +++ b/libs/clients/src/lib/connection.manager.client.ts @@ -10,17 +10,17 @@ import { Transport, } from "@nestjs/microservices"; import { ConfigService } from "@nestjs/config"; -import { IConnectionManager } from "@ocm-engine/config"; +import { IConnectionManagerConfig } from "@ocm-engine/config"; import { lastValueFrom } from "rxjs"; @Injectable() export class ConnectionManagerClient { private client: ClientProxy; - private cmConfig: IConnectionManager; + private cmConfig: IConnectionManagerConfig; private readonly logger: Logger = new Logger(ConnectionManagerClient.name); constructor(configService: ConfigService) { - this.cmConfig = configService.get<IConnectionManager>("cm")!; + this.cmConfig = configService.get<IConnectionManagerConfig>("cm")!; this.client = ClientProxyFactory.create({ transport: Transport.TCP, diff --git a/libs/config/src/config/attestation.manager.ts b/libs/config/src/config/attestation.manager.ts new file mode 100644 index 0000000000000000000000000000000000000000..d7e28623b3d5a8b6d64d577c2885b80e22cd5d97 --- /dev/null +++ b/libs/config/src/config/attestation.manager.ts @@ -0,0 +1,11 @@ +import { registerAs } from "@nestjs/config"; +import * as process from "process"; +import { IAttestationManagerConfig } from "../interfaces/attestation.manager.config.interface"; + +export const amConfig = registerAs( + "am", + (): IAttestationManagerConfig => ({ + host: process.env["ATTESTATION_SERVICE_HOST"]!, + port: parseInt(process.env["ATTESTATION_SERVICE_TCP_PORT"]!), + }), +); diff --git a/libs/config/src/config/connection.manager.config.ts b/libs/config/src/config/connection.manager.config.ts index ea3c981b395d7098571f89f2952d2b0a104c2d3a..1ae947bd47c90b8473a3fa1186a268acc01a2152 100644 --- a/libs/config/src/config/connection.manager.config.ts +++ b/libs/config/src/config/connection.manager.config.ts @@ -1,10 +1,10 @@ import { registerAs } from "@nestjs/config"; import * as process from "process"; -import { IConnectionManager } from "../interfaces/connection.manager.interface"; +import { IConnectionManagerConfig } from "../interfaces/connection.manager.config.interface"; export const cmConfig = registerAs( "cm", - (): IConnectionManager => ({ + (): IConnectionManagerConfig => ({ host: process.env["CONNECTION_SERVICE_HOST"]!, port: parseInt(process.env["CONNECTION_SERVICE_TCP_PORT"]!), }), diff --git a/libs/config/src/index.ts b/libs/config/src/index.ts index 7bd3b78d59f22078020dd10873f83bd434a3e91e..6ecbe3f4fe991891573fd5590d29998ecfef8bbe 100644 --- a/libs/config/src/index.ts +++ b/libs/config/src/index.ts @@ -3,15 +3,18 @@ export * from "./config/agent.config"; export * from "./config/ledgers.config"; export * from "./config/gateway.config"; export * from "./config/connection.manager.config"; +export * from "./config/attestation.manager"; export * from "./interfaces/nats.config.interface"; export * from "./interfaces/agent.config.interface"; export * from "./interfaces/ledgers.config.interface"; export * from "./interfaces/gateway.config.interface"; -export * from "./interfaces/connection.manager.interface"; +export * from "./interfaces/connection.manager.config.interface"; +export * from "./interfaces/attestation.manager.config.interface"; export * from "./schemas/nats.schema"; export * from "./schemas/agent.schema"; export * from "./schemas/ledgers.schema"; export * from "./schemas/gateway.schema"; export * from "./schemas/connection.manager.schema"; +export * from "./schemas/attestation.manager.schema"; diff --git a/libs/config/src/interfaces/attestation.manager.config.interface.ts b/libs/config/src/interfaces/attestation.manager.config.interface.ts new file mode 100644 index 0000000000000000000000000000000000000000..1ad4a74b3b9dde7ed40c86f855f329a9b74572d6 --- /dev/null +++ b/libs/config/src/interfaces/attestation.manager.config.interface.ts @@ -0,0 +1,4 @@ +export interface IAttestationManagerConfig { + host: string; + port: number; +} diff --git a/libs/config/src/interfaces/connection.manager.config.interface.ts b/libs/config/src/interfaces/connection.manager.config.interface.ts new file mode 100644 index 0000000000000000000000000000000000000000..51b1dfa8e2253f58843ece847ba74345215eadad --- /dev/null +++ b/libs/config/src/interfaces/connection.manager.config.interface.ts @@ -0,0 +1,4 @@ +export interface IConnectionManagerConfig { + host: string; + port: number; +} diff --git a/libs/config/src/interfaces/connection.manager.interface.ts b/libs/config/src/interfaces/connection.manager.interface.ts deleted file mode 100644 index 80092b731d2d06aa5699f11f8c5a5d620e53052f..0000000000000000000000000000000000000000 --- a/libs/config/src/interfaces/connection.manager.interface.ts +++ /dev/null @@ -1,4 +0,0 @@ -export interface IConnectionManager { - host: string; - port: number; -} diff --git a/libs/config/src/schemas/attestation.manager.schema.ts b/libs/config/src/schemas/attestation.manager.schema.ts new file mode 100644 index 0000000000000000000000000000000000000000..da7bbf862353178dc93362a037dfe0a1c922073c --- /dev/null +++ b/libs/config/src/schemas/attestation.manager.schema.ts @@ -0,0 +1,6 @@ +import Joi from "joi"; + +export const amSchema = Joi.object({ + ATTESTATION_SERVICE_TCP_PORT: Joi.string().required(), + ATTESTATION_SERVICE_HOST: Joi.string().required(), +}); diff --git a/libs/dtos/src/dtos/requests/accept.credential.offer.request.dto.ts b/libs/dtos/src/dtos/requests/accept.credential.offer.request.dto.ts new file mode 100644 index 0000000000000000000000000000000000000000..baaa860e4e40458f64af76f576939d8e72eb8779 --- /dev/null +++ b/libs/dtos/src/dtos/requests/accept.credential.offer.request.dto.ts @@ -0,0 +1,8 @@ +import { IsNotEmpty, IsString } from "class-validator"; + +export class AcceptCredentialOfferRequestDto { + //@example cf8395a5-9a53-4e06-8a5d-04e0fc00ca04 + @IsNotEmpty() + @IsString() + credentialRecordId: string; +} diff --git a/libs/dtos/src/dtos/requests/create.schema.request.dto.ts b/libs/dtos/src/dtos/requests/create.schema.request.dto.ts index ef98564fec6fe6d07ea82424fdf970acfb7e0830..01d49ccdefddf83767d45a7e2de040ca7cd7d3e4 100644 --- a/libs/dtos/src/dtos/requests/create.schema.request.dto.ts +++ b/libs/dtos/src/dtos/requests/create.schema.request.dto.ts @@ -1,13 +1,16 @@ import { IsNotEmpty, IsString, Matches } from "class-validator"; export class CreateSchemaRequestDto { + //@example "my test schema" @IsNotEmpty() @IsString() name: string; + //@example ['first_name, last_name'] @IsNotEmpty() attributes: string[]; + //@example 1.0.2 @IsNotEmpty() @Matches(/^(\d+\.)?(\d+\.)?(\*|\d+)$/) version: string; diff --git a/libs/dtos/src/dtos/requests/get.schema.request.dto.ts b/libs/dtos/src/dtos/requests/get.schema.request.dto.ts new file mode 100644 index 0000000000000000000000000000000000000000..acdf35ca546ede92f0f5aa27097b892a9ba14bfc --- /dev/null +++ b/libs/dtos/src/dtos/requests/get.schema.request.dto.ts @@ -0,0 +1,8 @@ +import { IsNotEmpty, IsString } from "class-validator"; + +export class GetSchemaRequestDto { + //@example "did:indy:LEDNGER:SXM76gQwRnjkgoz2oBnGjd/anoncreds/v0/SCHEMA/test schema/1.0.2" + @IsNotEmpty() + @IsString() + schemaId: string; +} diff --git a/libs/dtos/src/events/types.ts b/libs/dtos/src/events/types.ts index 748a1aad40589f956aa91ad1c599849db62f5190..128a81f8437f29e263fb89fed01201f4319401e7 100644 --- a/libs/dtos/src/events/types.ts +++ b/libs/dtos/src/events/types.ts @@ -15,3 +15,37 @@ export const CONNECTION_EVENTS: ConnectionEvent[] = [ CONNECTION_LIST, CONNECTION_GET, ]; + +export type SchemaEvent = "schemas.create" | "schemas.get" | "schemas.list"; +export const SCHEMA_CREATE: SchemaEvent = "schemas.create"; +export const SCHEMA_GET: SchemaEvent = "schemas.get"; +export const SCHEMA_LIST: SchemaEvent = "schemas.list"; + +export const SCHEMA_EVENTS: SchemaEvent[] = [ + SCHEMA_CREATE, + SCHEMA_LIST, + SCHEMA_GET, +]; + +export type CredentialEvent = + | "credential.definition.create" + | "credential.issue" + | "credential.list" + | "credential.offer.list" + | "credential.offer.accept"; + +export const CRED_DEF_CREATE = "credential.definition.create"; +export const CRED_ISSUE = "credential.issue"; +export const CRED_LIST = "credential.list"; +export const CRED_OFFER_LIST = "credential.offer.list"; +export const CRED_OFFER_ACCEPT = "credential.offer.accept"; + +export const CRED_EVENTS: CredentialEvent[] = [ + CRED_DEF_CREATE, + CRED_ISSUE, + CRED_LIST, + CRED_OFFER_LIST, + CRED_OFFER_ACCEPT, +]; + +export const ALL_EVENTS = [...SCHEMA_EVENTS, ...CRED_EVENTS]; diff --git a/libs/dtos/src/index.ts b/libs/dtos/src/index.ts index 8ed6a97500170a5be088ed6c304ead573c514e8d..cf82a2c82268d0c6c5fddb9d53da6e2654418885 100644 --- a/libs/dtos/src/index.ts +++ b/libs/dtos/src/index.ts @@ -4,6 +4,8 @@ export * from "./dtos/requests/create.credential.definition.requset.dto"; export * from "./dtos/requests/issue.credential.request.dto"; export * from "./dtos/requests/issue.proof.request.dto"; export * from "./dtos/requests/get.connection.request.dto"; +export * from "./dtos/requests/get.schema.request.dto"; +export * from "./dtos/requests/accept.credential.offer.request.dto"; export * from "./dtos/responses/create.invitation.response.dto"; export * from "./dtos/responses/accept.invitation.response.dto";