Skip to content
Snippets Groups Projects
Commit d286abc5 authored by Zdravko Iliev's avatar Zdravko Iliev
Browse files

feat: implement proof micro service

parent 946d0526
No related branches found
No related tags found
1 merge request!14feat: implement proof micro service
Pipeline #62779 passed with stage
in 1 minute and 25 seconds
Showing
with 442 additions and 137 deletions
AGENT_PEER_URL="http://localhost:4000"
AGENT_NAME=DExcVasd_AGENT_45
AGENT_KEY=HwNJroKHTSSj3XvE7ZAnuKiTn2C4QkFvxEqfm5rzhNrh
AGENT_DID_SEED=200000000000000000000000TCuste21
LEDGERS="BCOVRIN_TEST"
IDUNION_KEY=
IDUNION_KEY=#add if you are using IDUNION as a ledger
AGENT_PEER_URL="http://localhost:4000"
AGENT_NAME=EXAMPLE_AGENT_45
AGENT_KEY=EXAMPLE_AGENT_45_KEY
AGENT_DID_SEED=200000000000000000000000ExampleT21 #random string min 32 chars
AGENT_DB_HOST=0.0.0.0:5432
AGENT_DB_USER=postgres
AGENT_DB_PASS=postgres
PORT=3001
AGENT_PORT=3001
AGENT_CONSUMER_NAME=agent_1
AGENT_IS_REST=false
AGENT_MAX_MESSAGES=10
AGENT_RETE_LIMIT=5
NATS_SERVERS=0.0.0.0:4222
NATS_STREAM_NAME=ssi
NATS_SUBJECTS="connections.*,proofs.*,credentials.*"
AGENT_CONSUMER_NAME=agent
AGENT_IS_REST=true
NATS_STREAM_NAME=ssi_stream
NATS_SUBJECTS="connections.*,proofs.*,credentials.*,schemas.*"
GATEWAY_HTTP_PORT=8081
GATEWAY_TCP_PORT=8881
GATEWAY_SOCKET_EVENT_NAME=message
GATEWAY_MESSAGE_PATTERN=webhook
GATEWAY_HOST=0.0.0.0
CONNECTION_SERVICE_TCP_PORT=8882
CONNECTION_SERVICE_HOST=0.0.0.0
ATTESTATION_SERVICE_TCP_PORT=8883
ATTESTATION_SERVICE_HOST=0.0.0.0
PROOF_SERVICE_TCP_PORT=8884
PROOF_SERVICE_HOST=0.0.0.0
# OcmEngine
# Ocm Engine
<a alt="Nx logo" href="https://nx.dev" target="_blank" rel="noreferrer"><img src="https://raw.githubusercontent.com/nrwl/nx/master/images/nx-logo.png" width="45"></a>
**This workspace has been generated by [Nx, a Smart, fast and extensible build system.](https://nx.dev)**
## Development server
Run `nx serve agent` for a dev server. Navigate to http://localhost:4200/. The app will automatically reload if you change any of the source files.
## Understand this workspace
Run `nx graph` to see a diagram of the dependencies of the projects.
## Remote caching
Run `npx nx connect-to-nx-cloud` to enable [remote caching](https://nx.app) and make CI faster.
## Further help
Visit the [Nx Documentation](https://nx.dev) to learn more.
import { Body, Controller, Get, Logger } from "@nestjs/common";
import { Body, Controller, Logger } from "@nestjs/common";
import { AppService } from "./app.service";
import { MessagePattern, RpcException } from "@nestjs/microservices";
import {
CloudEventDto,
ConnectionEvent,
CreateCredentialDefinitionRequsetDto,
CreateInvitationResponseDto,
CreateSchemaRequestDto,
CredentialEvent,
GetConnectionRequestDto,
GatewayAcceptedResponseDto,
IssueCredentialRequestDto,
makeEvent,
SchemaEvent,
} from "@ocm-engine/dtos";
import { ProducerService } from "@ocm-engine/nats";
......@@ -19,10 +17,7 @@ import { ProducerService } from "@ocm-engine/nats";
export class AppController {
private readonly logger: Logger = new Logger(AppController.name);
constructor(
private readonly producerService: ProducerService,
private readonly appService: AppService,
) {}
constructor(private readonly producerService: ProducerService) {}
@MessagePattern("schemas")
async create(
......@@ -36,14 +31,17 @@ export class AppController {
this.logger.debug(JSON.stringify(payload, null, 2));
try {
const event = this.appService.toEvent(payload);
const event = makeEvent(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 };
const response = new GatewayAcceptedResponseDto();
response.id = event.id;
return response;
} catch (e) {
this.logger.debug(JSON.stringify(e, null, 2));
if (e instanceof Error) {
......@@ -69,14 +67,17 @@ export class AppController {
this.logger.debug(JSON.stringify(payload, null, 2));
try {
const event = this.appService.toEvent(payload);
const event = makeEvent(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 };
const response = new GatewayAcceptedResponseDto();
response.id = event.id;
return response;
} catch (e) {
this.logger.debug(JSON.stringify(e, null, 2));
if (e instanceof Error) {
......
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";
......@@ -21,6 +20,6 @@ const validationSchema = Joi.object({
}),
],
controllers: [AppController],
providers: [ProducerService, AppService],
providers: [ProducerService],
})
export class AppModule {}
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;
};
}
......@@ -31,7 +31,7 @@ async function bootstrap() {
app.enableShutdownHooks();
Logger.log("Application is running");
Logger.log(`Application is running ${am.host}:${am.port} TCP`);
}
bootstrap();
......@@ -2,21 +2,20 @@ import { Body, Controller, Logger } from "@nestjs/common";
import { ProducerService } from "@ocm-engine/nats";
import { MessagePattern, RpcException } from "@nestjs/microservices";
import { AppService } from "./app.service";
import {
CloudEventDto,
ConnectionEvent,
CreateInvitationResponseDto,
GatewayAcceptedResponseDto,
GetConnectionRequestDto,
makeEvent,
} from "@ocm-engine/dtos";
@Controller()
export class AppController {
private readonly logger: Logger = new Logger(AppController.name);
constructor(
private readonly producerService: ProducerService,
private readonly appService: AppService,
) {}
constructor(private readonly producerService: ProducerService) {}
@MessagePattern("connections")
async create(
......@@ -30,14 +29,18 @@ export class AppController {
this.logger.debug(JSON.stringify(payload, null, 2));
try {
const event = this.appService.toEvent(payload);
const event = makeEvent(payload);
this.logger.debug(JSON.stringify(event, null, 2));
await this.producerService.publish<typeof payload.data>(
payload.type,
event,
event as CloudEventDto<typeof payload.data>,
);
return { id: event.id };
const response = new GatewayAcceptedResponseDto();
response.id = event.id;
return response;
} catch (e) {
this.logger.debug(JSON.stringify(e, null, 2));
if (e instanceof Error) {
......
......@@ -5,7 +5,6 @@ import { ProducerService } from "@ocm-engine/nats";
import { ConfigModule } from "@nestjs/config";
import { cmConfig, cmSchema, natsConfig, natsSchema } from "@ocm-engine/config";
import Joi from "joi";
import { AppService } from "./app.service";
const validationSchema = Joi.object({
nats: natsSchema,
......@@ -21,6 +20,6 @@ const validationSchema = Joi.object({
}),
],
controllers: [AppController],
providers: [ProducerService, AppService],
providers: [ProducerService],
})
export class AppModule {}
import { Injectable } from "@nestjs/common";
import {
CloudEventDto,
CONNECTION_EVENTS,
ConnectionEvent,
ConnectionUnsupportedTypeError,
CreateInvitationResponseDto,
GetConnectionRequestDto,
} from "@ocm-engine/dtos";
@Injectable()
export class AppService {
toEvent = (payload: {
data: null | CreateInvitationResponseDto | GetConnectionRequestDto;
type: ConnectionEvent;
source: string;
}) => {
if (!CONNECTION_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;
};
}
......@@ -10,35 +10,46 @@ import {
cmSchema,
gatewayConfig,
gatewaySchema,
pmConfig,
pmSchema,
} from "@ocm-engine/config";
import {
AttestationManagerClient,
ConnectionManagerClient,
ProofManagerClient,
} from "@ocm-engine/clients";
import Joi from "joi";
import { ConnectionController } from "./managers/connection.controller";
import { APP_PIPE } from "@nestjs/core";
import { AttestationController } from "./managers/attestation.controller";
import { ProofController } from "./managers/proof.controller";
const validationSchema = Joi.object({
gateway: gatewaySchema,
cm: cmSchema,
am: amSchema,
pm: pmSchema,
});
@Module({
imports: [
ConfigModule.forRoot({
isGlobal: true,
load: [gatewayConfig, cmConfig, amConfig],
load: [gatewayConfig, cmConfig, amConfig, pmConfig],
validationSchema,
}),
],
controllers: [AppController, ConnectionController, AttestationController],
controllers: [
AppController,
ConnectionController,
AttestationController,
ProofController,
],
providers: [
EventsGateway,
ConnectionManagerClient,
AttestationManagerClient,
ProofManagerClient,
{
provide: APP_PIPE,
useValue: new ValidationPipe({
......
......@@ -11,7 +11,6 @@ import { AllExceptionsHandler } from "../exception.handler";
import { AttestationManagerClient } from "@ocm-engine/clients";
import {
AcceptCredentialOfferRequestDto,
CONNECTION_ACCEPT,
CreateCredentialDefinitionRequsetDto,
CreateSchemaRequestDto,
CRED_DEF_CREATE,
......@@ -140,7 +139,7 @@ export class AttestationController {
});
}
@Post("credential/definition")
@Post("credentials/definition")
@ApiResponse({
status: 201,
description:
......@@ -176,7 +175,7 @@ export class AttestationController {
});
}
@Post("credential/issue")
@Post("credentials/issue")
@ApiResponse({
status: 201,
description:
......@@ -194,9 +193,9 @@ export class AttestationController {
description: "Unknown error",
})
@ApiOperation({
summary: "Issue credential definition",
summary: "Issue credential",
description:
"Method issue credential definition. The id of the response will be matched when you receive event from the websocket",
"Method issue credential, it will create an offer and send it to specified receiver (connectionId). The id of the response will be matched when you receive event from the websocket",
tags: ["Credentials"],
})
issueCredential(@Body() issueCredentialDto: IssueCredentialRequestDto) {
......@@ -244,7 +243,7 @@ export class AttestationController {
});
}
@Get("/credential/offers")
@Get("/credentials/offers")
@ApiResponse({
status: 200,
description:
......@@ -262,10 +261,10 @@ export class AttestationController {
description: "Unknown error",
})
@ApiOperation({
summary: "List all credential offers",
summary: "List unaccepted 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"],
"Method list offers that are received, but not accepted. The id of the response will be matched when you receive event from the websocket",
tags: ["Credentials Offers"],
})
getCredentialOffers() {
return this.amClient.sendPayload({
......@@ -278,7 +277,7 @@ export class AttestationController {
});
}
@Post("/credential/offers/:credential_record_id/accept")
@Post("/credentials/offers/:credential_record_id/accept")
@ApiResponse({
status: 200,
description:
......@@ -299,7 +298,7 @@ export class AttestationController {
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"],
tags: ["Credentials Offers"],
})
acceptCredential(@Param("credential_record_id") credentialRecordId: string) {
const data = new AcceptCredentialOfferRequestDto();
......
import {
Controller,
BadRequestException,
Body,
Post,
Controller,
Get,
Param,
BadRequestException,
Post,
UseFilters,
} from "@nestjs/common";
import { ConnectionManagerClient } from "@ocm-engine/clients";
......@@ -20,11 +20,9 @@ import {
import { AllExceptionsHandler } from "../exception.handler";
import {
ApiBadRequestResponse,
ApiBody,
ApiInternalServerErrorResponse,
ApiOperation,
ApiResponse,
ApiTags,
} from "@nestjs/swagger";
@UseFilters(AllExceptionsHandler)
......@@ -32,7 +30,7 @@ import {
export class ConnectionController {
constructor(private readonly cmClient: ConnectionManagerClient) {}
@Post("/invitation")
@Post("/invitations")
@ApiResponse({
status: 201,
description:
......@@ -66,7 +64,7 @@ export class ConnectionController {
});
}
@Post("/invitation/accept")
@Post("/invitations/accept")
@ApiResponse({
status: 201,
description:
......
import {
BadRequestException,
Body,
Controller,
Get,
Param,
Post,
UseFilters,
} from "@nestjs/common";
import {
AcceptProofRequestDto,
GatewayAcceptedResponseDto,
GetSchemaRequestDto,
IssueProofRequestDto,
PROOF_ACCEPT,
PROOF_ISSUE,
PROOF_LIST,
} from "@ocm-engine/dtos";
import { AllExceptionsHandler } from "../exception.handler";
import { ProofManagerClient } from "@ocm-engine/clients";
import {
ApiBadRequestResponse,
ApiInternalServerErrorResponse,
ApiOperation,
ApiResponse,
} from "@nestjs/swagger";
@UseFilters(AllExceptionsHandler)
@Controller("v1")
export class ProofController {
constructor(private readonly pmClient: ProofManagerClient) {}
@Get("/credentials/proof")
@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 proof manager. This error shows that proof manager could not convert request to event or proof manager could not send the event to the broker.",
type: BadRequestException,
})
@ApiInternalServerErrorResponse({
status: 500,
description: "Unknown error",
})
@ApiOperation({
summary: "List received unaccepted proofs",
description:
"Method list all received unaccepted proofs. Status - request-receive. The id of the response will be matched when you receive event from the websocket",
tags: ["Credentials Proof"],
})
proofs() {
return this.pmClient.sendPayload<GetSchemaRequestDto>({
pattern: "proofs",
payload: {
source: "/credential/proofs",
data: null,
type: PROOF_LIST,
},
});
}
@Post("/credentials/proof/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 proof manager. This error shows that proof manager could not convert request to event or proof manager could not send the event to the broker.",
type: BadRequestException,
})
@ApiInternalServerErrorResponse({
status: 500,
description: "Unknown error",
})
@ApiOperation({
summary: "Issue proof for credential",
description:
"Method will issue proof. If connection id is not passed, the proof will be OOB. The id of the response will be matched when you receive event from the websocket",
tags: ["Credentials Proof"],
})
issueProof(@Body() issueProofDto: IssueProofRequestDto) {
return this.pmClient.sendPayload<IssueProofRequestDto>({
pattern: "proofs",
payload: {
source: "/credentials/proof/issue",
data: issueProofDto,
type: PROOF_ISSUE,
},
});
}
@Post(`/credentials/proof/:proof_record_id/accept`)
@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 proof manager. This error shows that proof manager could not convert request to event or proof manager could not send the event to the broker.",
type: BadRequestException,
})
@ApiInternalServerErrorResponse({
status: 500,
description: "Unknown error",
})
@ApiOperation({
summary: "Accept credential proof",
description:
"Method accept credential proof. The id of the response will be matched when you receive event from the websocket",
tags: ["Credentials Proof"],
})
acceptProof(@Param("proof_record_id") proofRecordId: string) {
const data = new AcceptProofRequestDto();
data.proofRecordId = proofRecordId;
return this.pmClient.sendPayload<AcceptProofRequestDto>({
pattern: "proofs",
payload: {
source: "/credentials/proofs/:id/accept",
data,
type: PROOF_ACCEPT,
},
});
}
}
{
"extends": ["../../.eslintrc.json"],
"ignorePatterns": ["!**/*"],
"overrides": [
{
"files": ["*.ts", "*.tsx", "*.js", "*.jsx"],
"rules": {}
},
{
"files": ["*.ts", "*.tsx"],
"rules": {}
},
{
"files": ["*.js", "*.jsx"],
"rules": {}
}
]
}
FROM node:18.16.0-buster-slim
RUN apt update -y && apt install python3 git make build-essential -y
WORKDIR app
COPY ./dist/apps/proof-manager .
COPY package.json yarn.lock ./
RUN yarn install
EXPOSE 8882
CMD ["node", "main.js"]
/* eslint-disable */
export default {
displayName: "proof-manager",
preset: "../../jest.preset.js",
testEnvironment: "node",
transform: {
"^.+\\.[tj]s$": ["ts-jest", { tsconfig: "<rootDir>/tsconfig.spec.json" }],
},
moduleFileExtensions: ["ts", "js", "html"],
coverageDirectory: "../../coverage/apps/proof-manager",
};
{
"name": "proof-manager",
"$schema": "../../node_modules/nx/schemas/project-schema.json",
"sourceRoot": "apps/proof-manager/src",
"projectType": "application",
"targets": {
"build": {
"executor": "@nx/webpack:webpack",
"outputs": ["{options.outputPath}"],
"defaultConfiguration": "production",
"options": {
"target": "node",
"compiler": "tsc",
"outputPath": "dist/apps/proof-manager",
"main": "apps/proof-manager/src/main.ts",
"tsConfig": "apps/proof-manager/tsconfig.app.json",
"isolatedConfig": true,
"webpackConfig": "apps/proof-manager/webpack.config.js"
},
"configurations": {
"development": {},
"production": {}
}
},
"serve": {
"executor": "@nx/js:node",
"defaultConfiguration": "development",
"options": {
"buildTarget": "proof-manager:build"
},
"configurations": {
"development": {
"buildTarget": "proof-manager:build:development"
},
"production": {
"buildTarget": "proof-manager:build:production"
}
}
},
"lint": {
"executor": "@nx/linter:eslint",
"outputs": ["{options.outputFile}"],
"options": {
"lintFilePatterns": ["apps/proof-manager/**/*.ts"]
}
},
"test": {
"executor": "@nx/jest:jest",
"outputs": ["{workspaceRoot}/coverage/{projectRoot}"],
"options": {
"jestConfig": "apps/proof-manager/jest.config.ts",
"passWithNoTests": true
},
"configurations": {
"ci": {
"ci": true,
"codeCoverage": true
}
}
}
},
"tags": []
}
import { Body, Controller, Logger } from "@nestjs/common";
import { ProducerService } from "@ocm-engine/nats";
import { MessagePattern, RpcException } from "@nestjs/microservices";
import {
CloudEventDto,
GatewayAcceptedResponseDto,
makeEvent,
ProofEvent,
} from "@ocm-engine/dtos";
@Controller()
export class AppController {
private readonly logger: Logger = new Logger(AppController.name);
constructor(private readonly producerService: ProducerService) {}
@MessagePattern("proofs")
async create(
@Body()
payload: {
data: null;
type: ProofEvent;
source: string;
},
): Promise<GatewayAcceptedResponseDto> {
this.logger.debug(JSON.stringify(payload, null, 2));
try {
const event = makeEvent(payload);
this.logger.debug(JSON.stringify(event, null, 2));
await this.producerService.publish<typeof payload.data>(
payload.type,
event as CloudEventDto<typeof payload.data>,
);
const response = new GatewayAcceptedResponseDto();
response.id = event.id;
return response;
} 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");
}
}
}
import { Module } from "@nestjs/common";
import { AppController } from "./app.controller";
import Joi from "joi";
import { natsConfig, natsSchema, pmConfig, pmSchema } from "@ocm-engine/config";
import { ConfigModule } from "@nestjs/config";
import { ProducerService } from "@ocm-engine/nats";
const validationSchema = Joi.object({
nats: natsSchema,
pm: pmSchema,
});
@Module({
imports: [
ConfigModule.forRoot({
isGlobal: true,
load: [natsConfig, pmConfig],
validationSchema,
}),
],
controllers: [AppController],
providers: [ProducerService],
})
export class AppModule {}
/**
* 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 { ConfigService } from "@nestjs/config";
import { MicroserviceOptions, Transport } from "@nestjs/microservices";
import { IProofManagerConfig } from "@ocm-engine/config";
async function bootstrap() {
const app = await NestFactory.create(AppModule);
const configService = app.get(ConfigService);
const pm = configService.get<IProofManagerConfig>("pm")!;
app.enableShutdownHooks();
const microservice = app.connectMicroservice<MicroserviceOptions>({
transport: Transport.TCP,
options: {
host: pm.host,
port: pm.port,
},
});
await app.startAllMicroservices();
app.enableShutdownHooks();
Logger.log(`Application is running ${pm.host}:${pm.port}`);
}
bootstrap();
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment