Skip to content
Snippets Groups Projects
Commit 0cbfbcb4 authored by Steffen Schulze's avatar Steffen Schulze
Browse files

Merge branch 'feat/connection-manager' into 'main'

Refactor connection manager

See merge request eclipse/xfsc/ocm/ocm-engine!21
parents eab760e2 63c20727
No related branches found
No related tags found
No related merge requests found
Showing
with 780 additions and 344 deletions
{
"name": "@ocm/connection-manager",
"version": "0.0.1",
"description": "",
"author": "Sagar",
"version": "1.0.0",
"description": "Connection Manager for OCM",
"contributors": [
"Konstantin Tsabolov <konstantin.tsabolov@spherity.com>"
],
"private": true,
"license": "Apache-2.0",
"type": "module",
......@@ -11,67 +13,40 @@
"prebuild": "pnpm clean",
"build": "nest build",
"prebuild:production": "pnpm clean",
"build:production": "pnpm prisma:generate && nest build -p tsconfig.production.json",
"prisma:dbpush": "prisma db push --schema=./src/prisma/schema.prisma",
"prisma:generate": "prisma generate --schema=./src/prisma/schema.prisma",
"prisma:migrate": "prisma migrate deploy --schema=./src/prisma/schema.prisma",
"prisma:studio": "prisma studio",
"start": "nest start",
"start:dev": "nest start --watch --preserveWatchOutput",
"test": "jest",
"test:watch": "jest --watch",
"test:cov": "jest --coverage",
"test:debug": "node --inspect-brk -r tsconfig-paths/register -r ts-node/register node_modules/.bin/jest --runInBand",
"test:e2e": "jest --config ./test/jest.config.js"
"build:production": "nest build -p tsconfig.production.json",
"start": "nest start --watch --preserveWatchOutput",
"test": "jest"
},
"dependencies": {
"@elastic/ecs-winston-format": "^1.5.0",
"@nestjs/axios": "^3.0.1",
"@nestjs/common": "^10.2.8",
"@nestjs/common": "^10.3.0",
"@nestjs/config": "^3.1.1",
"@nestjs/core": "^10.2.8",
"@nestjs/mapped-types": "^2.0.4",
"@nestjs/microservices": "^10.2.8",
"@nestjs/platform-express": "^10.2.8",
"@nestjs/core": "^10.3.0",
"@nestjs/microservices": "^10.3.0",
"@nestjs/platform-express": "^10.3.0",
"@nestjs/schedule": "^4.0.0",
"@nestjs/swagger": "^7.1.16",
"@nestjs/terminus": "^10.1.1",
"@prisma/client": "^5.6.0",
"@nestjs/swagger": "^7.1.17",
"@ocm/shared": "workspace:*",
"class-transformer": "^0.5.1",
"class-validator": "^0.14.0",
"express": "^4.17.3",
"joi": "^17.11.0",
"jsonwebtoken": "^9.0.2",
"jwks-rsa": "^3.1.0",
"moment": "^2.29.4",
"nats": "^2.18.0",
"reflect-metadata": "^0.1.13",
"rxjs": "^7.8.1",
"winston": "^3.11.0",
"winston-elasticsearch": "^0.17.4"
"rxjs": "^7.8.1"
},
"devDependencies": {
"@jest/globals": "^29.7.0",
"@nestjs/cli": "^10.2.1",
"@nestjs/schematics": "^10.0.3",
"@nestjs/testing": "^10.2.8",
"@swc/cli": "^0.1.62",
"@swc/core": "^1.3.96",
"@nestjs/testing": "^10.3.0",
"@swc/cli": "^0.1.63",
"@swc/core": "^1.3.101",
"@swc/jest": "^0.2.29",
"@types/express": "^4.17.21",
"@types/jest": "27.0.2",
"@types/jsonwebtoken": "^9.0.5",
"@types/node": "^20.9.0",
"@types/simple-oauth2": "^5.0.7",
"@types/supertest": "^2.0.16",
"dotenv-cli": "^7.3.0",
"@types/jest": "29.5.11",
"@types/node": "^20.10.5",
"jest": "^29.7.0",
"node-mocks-http": "^1.13.0",
"prisma": "^5.6.0",
"rimraf": "^5.0.5",
"source-map-support": "^0.5.21",
"supertest": "^6.3.3",
"swagger-ui-express": "^5.0.0",
"ts-node": "^10.9.1",
"typescript": "^5.2.2"
"typescript": "^5.3.3"
}
}
import type { INestApplication } from '@nestjs/common';
import type { TestingModule } from '@nestjs/testing';
import { Test } from '@nestjs/testing';
import AppModule from './app.module.js';
import { Application } from '../application.js';
describe('App Module', () => {
describe('Application', () => {
let app: INestApplication;
beforeAll(async () => {
const moduleFixture: TestingModule = await Test.createTestingModule({
imports: [AppModule],
beforeEach(async () => {
const moduleRef = await Test.createTestingModule({
imports: [Application],
}).compile();
app = moduleFixture.createNestApplication();
app = moduleRef.createNestApplication();
await app.init();
});
it('should work', () => {
expect(true).toBe(true);
afterEach(async () => {
await app.close();
});
afterAll(async () => {
await app.close();
it('should be defined', () => {
expect(app).toBeDefined();
});
});
import type { MiddlewareConsumer, NestModule } from '@nestjs/common';
import { Module, RequestMethod } from '@nestjs/common';
import { ConfigModule } from '@nestjs/config';
import { APP_FILTER } from '@nestjs/core';
import { ScheduleModule } from '@nestjs/schedule';
import { TerminusModule } from '@nestjs/terminus';
import ExceptionHandler from './common/exception.handler.js';
import config from './config/config.js';
import validationSchema from './config/validation.js';
import ConnectionsModule from './connections/module.js';
import SchedulerService from './connections/scheduler/scheduler.service.js';
import HealthController from './health/health.controller.js';
import { AuthMiddleware } from './middleware/auth.middleware.js';
import PrismaModule from './prisma/prisma.module.js';
@Module({
imports: [
ScheduleModule.forRoot(),
TerminusModule,
ConfigModule.forRoot({
isGlobal: true,
load: [config],
validationSchema,
}),
PrismaModule,
ConnectionsModule,
],
controllers: [HealthController],
providers: [
{
provide: APP_FILTER,
useClass: ExceptionHandler,
},
SchedulerService,
],
})
export default class AppModule implements NestModule {
// eslint-disable-next-line class-methods-use-this
public configure(consumer: MiddlewareConsumer) {
// eslint-disable-line
consumer
.apply(AuthMiddleware)
.exclude({
path: 'v1/health',
method: RequestMethod.GET,
})
.forRoutes('*');
}
}
import type { ConfigType } from '@nestjs/config';
import { Module } from '@nestjs/common';
import { ConfigModule } from '@nestjs/config';
import { RouterModule } from '@nestjs/core';
import { ClientsModule, Transport } from '@nestjs/microservices';
import { HealthModule } from '@ocm/shared';
import { NATS_CLIENT } from './common/constants.js';
import { httpConfig } from './config/http.config.js';
import { natsConfig } from './config/nats.config.js';
import { ssiConfig } from './config/ssi.config.js';
import { validationSchema } from './config/validation.js';
import { ConnectionsModule } from './connections/connections.module.js';
import { InvitationsModule } from './invitations/invitations.module.js';
@Module({
imports: [
ConfigModule.forRoot({
isGlobal: true,
load: [httpConfig, natsConfig, ssiConfig],
cache: true,
expandVariables: true,
validationSchema,
validationOptions: {
allowUnknown: true,
abortEarly: true,
},
}),
ClientsModule.registerAsync({
isGlobal: true,
clients: [
{
name: NATS_CLIENT,
inject: [natsConfig.KEY],
useFactory: (config: ConfigType<typeof natsConfig>) => ({
transport: Transport.NATS,
options: {
url: config.url as string,
},
}),
},
],
}),
HealthModule.registerAsync({
inject: [natsConfig.KEY],
useFactory: (config: ConfigType<typeof natsConfig>) => {
const options: Parameters<typeof HealthModule.register>[0] = {};
if (config.monitoringUrl) {
options.nats = {
monitoringUrl: config.monitoringUrl as string,
};
}
return options;
},
}),
ConnectionsModule,
InvitationsModule,
RouterModule.register([
{ module: HealthModule, path: '/health' },
{ module: ConnectionsModule, path: '/connections' },
{ module: InvitationsModule, path: '/invitations' },
]),
],
})
export class Application {}
export default class ConfigClient {
/**
*
* If there is no Limit to check expire till date return false
* @returns Number to calculate end date
*/
public static checkExpireTill(): Date | false {
const days = 2;
const tillDate = new Date();
tillDate.setDate(tillDate.getDate() - days);
return tillDate;
}
public static getConnectionExpire(): Date | false {
const min = 30;
const connectionExpire = min * 60 * 1000;
const compareDateTime = new Date(new Date().getTime() - connectionExpire);
return compareDateTime;
}
}
import type ResponseType from '../common/response.js';
import type ConnectionSubscriptionEndpointDto from '../connections/entities/connectionSubscribeEndPoint.entity.js';
import { Inject, Injectable } from '@nestjs/common';
import { ClientProxy } from '@nestjs/microservices';
import { lastValueFrom } from 'rxjs';
import {
Attestation,
NATSServices,
Principal,
ProofManager,
} from '../common/constants.js';
import logger from '../utils/logger.js';
@Injectable()
export default class NatsClientService {
public constructor(
@Inject(NATSServices.SERVICE_NAME) private client: ClientProxy,
) {}
public sendConnectionStatusPrincipalManager(
status: string,
connectionId: string,
theirLabel: string,
participantDID: string,
theirDid: string,
) {
const pattern = {
endpoint: `${Principal.NATS_ENDPOINT}/${Principal.CONNECTION_COMPLETE_STATUS}`,
};
const payload = {
status,
connectionId,
theirLabel,
participantDID,
theirDid,
};
logger.info(`before nats call to principal manager ${payload}`);
return lastValueFrom(this.client.send<ResponseType>(pattern, payload));
}
public getIssueCredentials(connectionId: string) {
const pattern = {
endpoint: `${Attestation.NATS_ENDPOINT}/${Attestation.GET_ISSUE_CREDENTIALS}`,
};
const payload = {
connectionId,
};
return lastValueFrom(this.client.send<ResponseType>(pattern, payload));
}
public sendMembershipProofRequestToProofManager(connectionId: string) {
const pattern = {
endpoint: `${ProofManager.NATS_ENDPOINT}/${ProofManager.SEND_MEMBERSHIP_PROOF_REQUEST}`,
};
const payload = {
connectionId,
};
return lastValueFrom(this.client.send<ResponseType>(pattern, payload));
}
public getPresentProofs(connectionId: string) {
const pattern = {
endpoint: `${ProofManager.NATS_ENDPOINT}/${ProofManager.GET_PRESENT_PROOFS}`,
};
const payload = {
connectionId,
};
return lastValueFrom(this.client.send<ResponseType>(pattern, payload));
}
public publishConnection(data: ConnectionSubscriptionEndpointDto) {
this.client.emit(
`${NATSServices.SERVICE_NAME}/${NATSServices.CONNECTION_SUBSCRIBER_ENDPOINT}`,
data,
);
}
}
import { HttpService } from '@nestjs/axios';
import { Injectable } from '@nestjs/common';
import { lastValueFrom, map } from 'rxjs';
@Injectable()
export default class RestClientService {
public constructor(private readonly httpService: HttpService) {}
public async post(url: string, payload: object) {
return lastValueFrom(
this.httpService
.post(url, payload)
.pipe(map((response) => response.data)),
);
}
}
export enum NATSServices {
SERVICE_NAME = 'CONNECTION_MANAGER_SERVICE',
CONNECTION_SUBSCRIBER_ENDPOINT = 'ConnectionSubscriberEndpoint',
}
export enum LoggerConfig {
FILE_PATH = 'logs/log.json',
LOG_DIR = './logs',
}
export enum Abstraction {
NATS_ENDPOINT = 'SSI_ABSTRACTION_SERVICE',
CONNECTION_STATE_CHANGED = 'ConnectionStateChanged',
}
export enum Principal {
NATS_ENDPOINT = 'PRINCIPAL_MANAGER_SERVICE',
CONNECTION_COMPLETE_STATUS = 'connectionCompleteStatus',
}
export enum Attestation {
NATS_ENDPOINT = 'ATTESTATION_MANAGER_SERVICE',
GET_ISSUE_CREDENTIALS = 'getIssueCredentials',
}
export enum ProofManager {
NATS_ENDPOINT = 'PROOF_MANAGER_SERVICE',
GET_PRESENT_PROOFS = 'getPresentProofs',
SEND_MEMBERSHIP_PROOF_REQUEST = 'sendMembershipProofRequest',
}
export const RECEIVED_CONNECTION_ALIAS = 'connection-received';
export const SERVICE_NAME = 'SCHEMA_MANAGER_SERVICE';
export const NATS_CLIENT = Symbol('NATS_CLIENT');
import moment from 'moment';
const getDate = () => moment().format('MM-DD-YYYY, h:mm:ss a');
export default getDate;
import type ResponseType from './response.js';
import type { ArgumentsHost, ExceptionFilter } from '@nestjs/common';
import { Catch, HttpException, HttpStatus } from '@nestjs/common';
import { HttpAdapterHost } from '@nestjs/core';
@Catch()
export default class ExceptionHandler implements ExceptionFilter {
public constructor(private readonly httpAdapterHost: HttpAdapterHost) {}
// eslint-disable-next-line @typescript-eslint/no-explicit-any
public catch(exception: any, host: ArgumentsHost): void {
// In certain situations `httpAdapter` might not be available in the
// constructor method, thus we should resolve it here.
const { httpAdapter } = this.httpAdapterHost;
const ctx = host.switchToHttp();
const response = ctx.getResponse();
let statusCode = HttpStatus.INTERNAL_SERVER_ERROR;
let message =
exception.message.error || exception.message || 'Something went wrong!';
if (exception instanceof HttpException) {
const errorResponse: string | object = exception.getResponse();
statusCode = exception.getStatus();
message =
(typeof errorResponse === 'object' &&
Reflect.get(errorResponse, 'error')) ||
message;
}
const responseBody: ResponseType = {
statusCode,
message,
error: exception.message,
};
httpAdapter.reply(response, responseBody, statusCode);
}
}
export default interface ResponseType {
statusCode: number;
message: string;
data?: unknown;
error?: unknown;
}
import { fileURLToPath } from 'node:url';
const parentDirectory = fileURLToPath(new URL('..', import.meta.url));
const config = () => ({
PORT: Number(process.env.PORT),
APP_URL: process.env.CONNECTION_MANAGER_URL,
auth: {
useAuth: process.env.USE_AUTH || 'false',
clientId: process.env.OAUTH_CLIENT_ID,
clientSecret: process.env.OAUTH_CLIENT_SECRET,
tokenUrl: process.env.OAUTH_TOKEN_URL,
},
nats: {
url: process.env.NATS_URL,
},
database: {
type: 'postgres',
host: process.env.DB_HOST,
username: process.env.DB_USERNAME,
password: process.env.DB_PASSWORD,
database: process.env.DB_DATABASE,
port: 5432,
synchronize: false,
logging: false,
entities: [`${parentDirectory}/../**/**.model{.ts,.js}`],
DATABASE_URL: process.env.DATABASE_URL,
},
agent: {
agentUrl: process.env.AGENT_URL,
},
ECSURL: process.env.ECSURL,
});
export default config;
import { registerAs } from '@nestjs/config';
export const httpConfig = registerAs('http', () => ({
host: process.env.HTTP_HOST,
port: Number(process.env.HTTP_PORT),
}));
import { registerAs } from '@nestjs/config';
export const natsConfig = registerAs('nats', () => ({
url: process.env.NATS_URL,
monitoringUrl: process.env.NATS_MONITORING_URL,
}));
import { registerAs } from '@nestjs/config';
export const ssiConfig = registerAs('ssi', () => ({
agentUrl: process.env.SSI_AGENT_URL,
}));
import Joi from 'joi';
const validationSchema = Joi.object({
DATABASE_URL: Joi.string().required(),
NATS_URL: Joi.string().required(),
PORT: Joi.number().required(),
CONNECTION_MANAGER_URL: Joi.string().required(),
USE_AUTH: Joi.string(),
AGENT_URL: Joi.string().required(),
OAUTH_CLIENT_ID: Joi.string(),
OAUTH_CLIENT_SECRET: Joi.string(),
OAUTH_TOKEN_URL: Joi.string(),
});
export const validationSchema = Joi.object({
HTTP_HOST: Joi.string().default('0.0.0.0'),
HTTP_PORT: Joi.number().default(3000),
NATS_URL: Joi.string().uri().default('nats://localhost:4222'),
NATS_MONITORING_URL: Joi.string().uri().default('http://localhost:8222'),
export default validationSchema;
SSI_AGENT_URL: Joi.string().default('http://localhost:3010'),
});
import type { TestingModule } from '@nestjs/testing';
import type {
EventDidcommConnectionsCreateWithSelf,
EventDidcommConnectionsGetAll,
EventDidcommConnectionsGetById,
} from '@ocm/shared';
import { Test } from '@nestjs/testing';
import { Subject, of, takeUntil } from 'rxjs';
import { NATS_CLIENT } from '../../common/constants.js';
import { ConnectionsController } from '../connections.controller.js';
import { ConnectionsService } from '../connections.service.js';
describe('ConnectionsController', () => {
const natsClientMock = {};
let controller: ConnectionsController;
let service: ConnectionsService;
beforeEach(async () => {
const module: TestingModule = await Test.createTestingModule({
controllers: [ConnectionsController],
providers: [
{ provide: NATS_CLIENT, useValue: natsClientMock },
ConnectionsService,
],
}).compile();
controller = module.get<ConnectionsController>(ConnectionsController);
service = module.get<ConnectionsService>(ConnectionsService);
});
describe('getAll', () => {
it('should return a list of connections', (done) => {
const unsubscribe$ = new Subject<void>();
const tenantId = 'exampleTenantId';
const expectedResult: EventDidcommConnectionsGetAll['data'] = [];
jest
.spyOn(service, 'getAllConnections')
.mockReturnValue(of(expectedResult));
controller
.getAll({ tenantId })
.pipe(takeUntil(unsubscribe$))
.subscribe((result) => {
expect(result).toStrictEqual(expectedResult);
unsubscribe$.next();
unsubscribe$.complete();
done();
});
});
});
describe('getById', () => {
it('should return a connection', (done) => {
const unsubscribe$ = new Subject<void>();
const tenantId = 'exampleTenantId';
const connectionId = 'exampleConnectionId';
const expectedResult = {} as EventDidcommConnectionsGetById['data'];
jest
.spyOn(service, 'getConnectionById')
.mockReturnValue(of(expectedResult));
controller
.getById({ connectionId }, { tenantId })
.pipe(takeUntil(unsubscribe$))
.subscribe((result) => {
expect(result).toStrictEqual(expectedResult);
unsubscribe$.next();
unsubscribe$.complete();
done();
});
});
});
describe('createWithSelf', () => {
it('should return a connection', (done) => {
const unsubscribe$ = new Subject<void>();
const tenantId = 'exampleTenantId';
const expectedResult =
{} as EventDidcommConnectionsCreateWithSelf['data'];
jest
.spyOn(service, 'createConnectionWithSelf')
.mockReturnValue(of(expectedResult));
controller
.createWithSelf({ tenantId })
.pipe(takeUntil(unsubscribe$))
.subscribe((result) => {
expect(result).toStrictEqual(expectedResult);
unsubscribe$.next();
unsubscribe$.complete();
done();
});
});
});
describe('block', () => {
it('should return a connection', (done) => {
const unsubscribe$ = new Subject<void>();
const tenantId = 'exampleTenantId';
const idOrDid = 'exampleConnectionId';
const expectedResult = {} as EventDidcommConnectionsGetById['data'];
jest
.spyOn(service, 'blockConnection')
.mockReturnValue(of(expectedResult));
controller
.block({ idOrDid }, { tenantId })
.pipe(takeUntil(unsubscribe$))
.subscribe((result) => {
expect(result).toStrictEqual(expectedResult);
unsubscribe$.next();
unsubscribe$.complete();
done();
});
});
});
});
import { ClientsModule } from '@nestjs/microservices';
import { Test } from '@nestjs/testing';
import { NATS_CLIENT } from '../../common/constants.js';
import { ConnectionsController } from '../connections.controller.js';
import { ConnectionsModule } from '../connections.module.js';
import { ConnectionsService } from '../connections.service.js';
describe('ConnectionsModule', () => {
let connectionsController: ConnectionsController;
let connectionsService: ConnectionsService;
beforeEach(async () => {
const moduleRef = await Test.createTestingModule({
imports: [
ClientsModule.registerAsync({
isGlobal: true,
clients: [{ name: NATS_CLIENT, useFactory: () => ({}) }],
}),
ConnectionsModule,
],
}).compile();
connectionsController = moduleRef.get<ConnectionsController>(
ConnectionsController,
);
connectionsService = moduleRef.get<ConnectionsService>(ConnectionsService);
});
it('should be defined', () => {
expect(connectionsController).toBeDefined();
expect(connectionsController).toBeInstanceOf(ConnectionsController);
expect(connectionsService).toBeDefined();
expect(connectionsService).toBeInstanceOf(ConnectionsService);
});
});
import type { ClientProxy } from '@nestjs/microservices';
import type { TestingModule } from '@nestjs/testing';
import { Test } from '@nestjs/testing';
import {
EventDidcommConnectionsBlock,
EventDidcommConnectionsCreateWithSelf,
EventDidcommConnectionsGetAll,
EventDidcommConnectionsGetById,
} from '@ocm/shared';
import { Subject, of, takeUntil } from 'rxjs';
import { NATS_CLIENT } from '../../common/constants.js';
import { ConnectionsService } from '../connections.service.js';
describe('ConnectionsService', () => {
let service: ConnectionsService;
let natsClient: ClientProxy;
beforeEach(async () => {
const module: TestingModule = await Test.createTestingModule({
providers: [
ConnectionsService,
{
provide: NATS_CLIENT,
useValue: {
send: jest.fn(),
},
},
],
}).compile();
service = module.get<ConnectionsService>(ConnectionsService);
natsClient = module.get<ClientProxy>(NATS_CLIENT);
});
describe('getAllConnections', () => {
it('should return all connections', (done) => {
const unsubscribe$ = new Subject<void>();
const tenantId = 'exampleTenantId';
const expectedResult: EventDidcommConnectionsGetAll['data'] = [];
jest
.spyOn(natsClient, 'send')
.mockReturnValue(
of(new EventDidcommConnectionsGetAll(expectedResult, tenantId)),
);
service
.getAllConnections(tenantId)
.pipe(takeUntil(unsubscribe$))
.subscribe((result) => {
expect(natsClient.send).toHaveBeenCalledWith(
EventDidcommConnectionsGetAll.token,
{ tenantId },
);
expect(result).toStrictEqual(expectedResult);
unsubscribe$.next();
unsubscribe$.complete();
done();
});
});
});
describe('getConnectionById', () => {
it('should return a connection', (done) => {
const unsubscribe$ = new Subject<void>();
const tenantId = 'exampleTenantId';
const connectionId = 'exampleConnectionId';
const expectedResult = {} as EventDidcommConnectionsGetById['data'];
jest
.spyOn(natsClient, 'send')
.mockReturnValue(
of(new EventDidcommConnectionsGetById(expectedResult, tenantId)),
);
service
.getConnectionById(tenantId, connectionId)
.pipe(takeUntil(unsubscribe$))
.subscribe((result) => {
expect(natsClient.send).toHaveBeenCalledWith(
EventDidcommConnectionsGetById.token,
{ tenantId, id: connectionId },
);
expect(result).toStrictEqual(expectedResult);
unsubscribe$.next();
unsubscribe$.complete();
done();
});
});
});
describe('createConnectionWithSelf', () => {
it('should create a connection with self', (done) => {
const unsubscribe$ = new Subject<void>();
const tenantId = 'exampleTenantId';
const expectedResult =
{} as EventDidcommConnectionsCreateWithSelf['data'];
jest
.spyOn(natsClient, 'send')
.mockReturnValue(
of(
new EventDidcommConnectionsCreateWithSelf(expectedResult, tenantId),
),
);
service
.createConnectionWithSelf(tenantId)
.pipe(takeUntil(unsubscribe$))
.subscribe((result) => {
expect(natsClient.send).toHaveBeenCalledWith(
EventDidcommConnectionsCreateWithSelf.token,
{ tenantId },
);
expect(result).toStrictEqual(expectedResult);
unsubscribe$.next();
unsubscribe$.complete();
done();
});
});
});
describe('blockConnection', () => {
it('should block a connection', (done) => {
const unsubscribe$ = new Subject<void>();
const tenantId = 'exampleTenantId';
const idOrDid = 'exampleConnectionId';
const expectedResult = {} as EventDidcommConnectionsBlock['data'];
jest
.spyOn(natsClient, 'send')
.mockReturnValue(
of(new EventDidcommConnectionsBlock(expectedResult, tenantId)),
);
service
.blockConnection(tenantId, idOrDid)
.pipe(takeUntil(unsubscribe$))
.subscribe((result) => {
expect(natsClient.send).toHaveBeenCalledWith(
EventDidcommConnectionsBlock.token,
{ tenantId, idOrDid },
);
expect(result).toStrictEqual(expectedResult);
unsubscribe$.next();
unsubscribe$.complete();
done();
});
});
});
});
import {
Controller,
Get,
HttpStatus,
Param,
Post,
Query,
UseInterceptors,
UsePipes,
ValidationPipe,
} from '@nestjs/common';
import { ApiOperation, ApiResponse, ApiTags } from '@nestjs/swagger';
import { MultitenancyParams, ResponseFormatInterceptor } from '@ocm/shared';
import { ConnectionsService } from './connections.service.js';
import { BlockParams } from './dto/block.dto.js';
import { GetByIdParams } from './dto/get-by-id.dto.js';
@Controller()
@UsePipes(new ValidationPipe({ transform: true, whitelist: true }))
@UseInterceptors(ResponseFormatInterceptor)
@ApiTags('Connections')
export class ConnectionsController {
public constructor(private readonly service: ConnectionsService) {}
@Get()
@ApiOperation({
summary: 'Fetch a list of connections',
description: 'This call provides a list of connections for a given tenant',
})
@ApiResponse({
status: HttpStatus.OK,
description: 'Connections fetched successfully',
content: {
'application/json': {
schema: {},
examples: {
'Connections fetched successfully': {
value: {
statusCode: 200,
message: 'Connections fetched successfully',
data: [],
},
},
},
},
},
})
@ApiResponse({
status: HttpStatus.NOT_FOUND,
description: 'Tenant not found',
content: {
'application/json': {
schema: {},
examples: {
'Tenant not found': {
value: {
statusCode: 404,
message: 'Tenant not found',
data: null,
},
},
},
},
},
})
@ApiResponse({
status: HttpStatus.INTERNAL_SERVER_ERROR,
description: 'Internal server error',
content: {
'application/json': {
schema: {},
examples: {
'Internal server error': {
value: {
statusCode: 500,
message: 'Internal server error',
data: null,
},
},
},
},
},
})
public getAll(
@Query() { tenantId }: MultitenancyParams,
): ReturnType<ConnectionsService['getAllConnections']> {
return this.service.getAllConnections(tenantId);
}
@Get(':connectionId')
@ApiOperation({
summary: 'Fetch a connection by ID',
description:
'This call provides a connection for a given tenant and connection ID',
})
@ApiResponse({
status: HttpStatus.OK,
description: 'Connection fetched successfully',
content: {
'application/json': {
schema: {},
examples: {
'Connection fetched successfully': {
value: {
statusCode: 200,
message: 'Connection fetched successfully',
data: {},
},
},
},
},
},
})
@ApiResponse({
status: HttpStatus.NOT_FOUND,
description: 'Connection not found',
content: {
'application/json': {
schema: {},
examples: {
'Tenant not found': {
value: {
statusCode: 404,
message: 'Tenant not found',
data: null,
},
},
'Connection not found': {
value: {
statusCode: 404,
message: 'Connection not found',
data: null,
},
},
},
},
},
})
@ApiResponse({
status: HttpStatus.INTERNAL_SERVER_ERROR,
description: 'Internal server error',
content: {
'application/json': {
schema: {},
examples: {
'Internal server error': {
value: {
statusCode: 500,
message: 'Internal server error',
data: null,
},
},
},
},
},
})
public getById(
@Param() { connectionId }: GetByIdParams,
@Query() { tenantId }: MultitenancyParams,
): ReturnType<ConnectionsService['getConnectionById']> {
return this.service.getConnectionById(tenantId, connectionId);
}
@Post()
@ApiOperation({
summary: 'Create a connection',
description: 'This call creates a self connection for a given tenant',
})
@ApiResponse({
status: HttpStatus.CREATED,
description: 'Connection created successfully',
content: {
'application/json': {
schema: {},
examples: {
'Connection created successfully': {
value: {
statusCode: 201,
message: 'Connection created successfully',
data: {},
},
},
},
},
},
})
@ApiResponse({
status: HttpStatus.NOT_FOUND,
description: 'Tenant not found',
content: {
'application/json': {
schema: {},
examples: {
'Tenant not found': {
value: {
statusCode: 404,
message: 'Tenant not found',
data: null,
},
},
},
},
},
})
@ApiResponse({
status: HttpStatus.INTERNAL_SERVER_ERROR,
description: 'Internal server error',
content: {
'application/json': {
schema: {},
examples: {
'Internal server error': {
value: {
statusCode: 500,
message: 'Internal server error',
data: null,
},
},
},
},
},
})
public createWithSelf(
@Query() { tenantId }: MultitenancyParams,
): ReturnType<ConnectionsService['createConnectionWithSelf']> {
return this.service.createConnectionWithSelf(tenantId);
}
@Post(':idOrDid/block')
@ApiOperation({
summary: 'Block a connection',
description:
'This call blocks a connection for a given tenant and connection ID',
})
@ApiResponse({
status: HttpStatus.OK,
description: 'Connection blocked successfully',
content: {
'application/json': {
schema: {},
examples: {
'Connection blocked successfully': {
value: {
statusCode: 200,
message: 'Connection blocked successfully',
data: {},
},
},
},
},
},
})
@ApiResponse({
status: HttpStatus.NOT_FOUND,
description: 'Connection not found',
content: {
'application/json': {
schema: {},
examples: {
'Tenant not found': {
value: {
statusCode: 404,
message: 'Tenant not found',
data: null,
},
},
'Connection not found': {
value: {
statusCode: 404,
message: 'Connection not found',
data: null,
},
},
},
},
},
})
@ApiResponse({
status: HttpStatus.INTERNAL_SERVER_ERROR,
description: 'Internal server error',
content: {
'application/json': {
schema: {},
examples: {
'Internal server error': {
value: {
statusCode: 500,
message: 'Internal server error',
data: null,
},
},
},
},
},
})
public block(
@Param() { idOrDid }: BlockParams,
@Query() { tenantId }: MultitenancyParams,
): ReturnType<ConnectionsService['blockConnection']> {
return this.service.blockConnection(tenantId, idOrDid);
}
}
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