Skip to content
Snippets Groups Projects

Compare revisions

Changes are shown as if the source revision was being merged into the target revision. Learn more about comparing revisions.

Source

Select target project
No results found

Target

Select target project
  • eclipse/xfsc/ocm/ocm-engine
  • zdravko61/ocm-engine
  • mjuergenscg/ocm-engine
  • tsabolov/ocm-engine
  • mikesell/ocm-engine
5 results
Show changes
Commits on Source (12)
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);
}
}