Skip to content
Snippets Groups Projects
Verified Commit 06496cbb authored by Konstantin Tsabolov's avatar Konstantin Tsabolov
Browse files

Merge branch 'main' into chore/move-docker-compose-to-root

parents 221e5385 ae8024d2
No related branches found
No related tags found
No related merge requests found
Showing
with 183 additions and 2734 deletions
import type {
EventDidcommConnectionsBlockInput,
EventDidcommConnectionsGetAllInput,
EventDidcommConnectionsGetByIdInput,
} from '@ocm/shared';
import type { Observable } from 'rxjs';
import { Inject, Injectable } from '@nestjs/common';
import { ClientProxy } from '@nestjs/microservices';
import {
EventDidcommConnectionsBlock,
EventDidcommConnectionsCreateWithSelf,
EventDidcommConnectionsGetAll,
EventDidcommConnectionsGetById,
} from '@ocm/shared';
import { map } from 'rxjs';
import { NATS_CLIENT } from '../common/constants.js';
@Injectable()
export class ConnectionsService {
public constructor(
@Inject(NATS_CLIENT) private readonly natsClient: ClientProxy,
) {}
public getAllConnections(
tenantId: string,
): Observable<EventDidcommConnectionsGetAll['data']> {
return this.natsClient
.send<
EventDidcommConnectionsGetAll,
EventDidcommConnectionsGetAllInput
>(EventDidcommConnectionsGetAll.token, { tenantId })
.pipe(map((result) => result.data));
}
public getConnectionById(
tenantId: string,
id: string,
): Observable<EventDidcommConnectionsGetById['data']> {
return this.natsClient
.send<
EventDidcommConnectionsGetById,
EventDidcommConnectionsGetByIdInput
>(EventDidcommConnectionsGetById.token, { tenantId, id })
.pipe(map((result) => result.data));
}
public createConnectionWithSelf(
tenantId: string,
): Observable<EventDidcommConnectionsCreateWithSelf['data']> {
return this.natsClient
.send<EventDidcommConnectionsCreateWithSelf>(
EventDidcommConnectionsCreateWithSelf.token,
{ tenantId },
)
.pipe(map((result) => result.data));
}
public blockConnection(
tenantId: string,
idOrDid: string,
): Observable<EventDidcommConnectionsBlock['data']> {
return this.natsClient
.send<
EventDidcommConnectionsBlock,
EventDidcommConnectionsBlockInput
>(EventDidcommConnectionsBlock.token, { tenantId, idOrDid })
.pipe(map((result) => result.data));
}
}
This diff is collapsed.
import { ApiProperty } from '@nestjs/swagger';
import { IsNotEmpty, IsString } from 'class-validator';
export class BlockParams {
@IsString()
@IsNotEmpty()
@ApiProperty({
description: 'The connection ID or DID',
example: '8d74c6ec-fa3e-4a09-91fb-5fd0062da835',
})
public idOrDid: string;
}
import { ApiProperty } from '@nestjs/swagger';
import { IsNotEmpty, IsString } from 'class-validator';
export class GetByIdParams {
@IsString()
@IsNotEmpty()
@ApiProperty({
description: 'The connection ID',
example: '71b784a3',
})
public connectionId: string;
}
import { ApiProperty } from '@nestjs/swagger';
export default class AcceptConnectionInvitationBody {
@ApiProperty()
public invitationUrl: string;
@ApiProperty()
public autoAcceptConnection: boolean;
}
import { IsString } from 'class-validator';
export default class InvitationDTO {
@IsString()
public serviceEndpoint?: string;
@IsString()
public ['@type']?: string;
@IsString()
public ['@id']?: string;
@IsString()
public label?: string;
@IsString()
public recipientKeys?: [string];
@IsString()
public routingKeys?: [];
}
import { ApiProperty } from '@nestjs/swagger';
import { IsString } from 'class-validator';
export default class ConnectionCreateInvitationDto {
@IsString()
@ApiProperty()
public autoAcceptConnection?: boolean;
@IsString()
@ApiProperty()
public alias?: string;
@IsString()
@ApiProperty()
public myLabel?: string;
@IsString()
@ApiProperty()
public myImageUrl?: string;
}
import { IsBooleanString, IsNotEmpty, IsString } from 'class-validator';
import InvitationDTO from './InvitationDto.entity.js';
export default class ConnectionStateDto {
@IsString()
public _tags?: string;
@IsString()
public metadata?: string;
@IsString()
public didDoc?: string;
@IsString()
public verkey?: string;
@IsString()
public createdAt?: string;
@IsString()
@IsNotEmpty()
public role: string;
@IsString()
@IsNotEmpty()
public state: string;
@IsString()
@IsNotEmpty()
public id: string;
@IsString()
@IsNotEmpty()
public did: string;
@IsString()
public theirDid: string;
@IsString()
public theirLabel: string;
@IsString()
public invitation: InvitationDTO;
@IsString()
public alias: string;
@IsBooleanString()
public multiUseInvitation?: boolean;
}
import { IsString, IsNotEmpty } from 'class-validator';
export default class ConnectionSubscriptionEndpointDto {
@IsString()
@IsNotEmpty()
public connectionId: string;
@IsString()
@IsNotEmpty()
public status: string;
}
import { IsString, IsNotEmpty, IsDate, IsBoolean } from 'class-validator';
import InvitationDTO from './InvitationDto.entity.js';
export default class ConnectionDto {
@IsString()
public id?: string;
@IsDate()
public connectionDate?: Date;
@IsDate()
public createdDate?: Date;
@IsDate()
public updatedDate?: Date;
@IsString()
@IsNotEmpty()
public participantDid: string;
@IsString()
@IsNotEmpty()
public status: string;
@IsString()
@IsNotEmpty()
public connectionId: string;
@IsString()
public theirDid: string;
@IsString()
public theirLabel: string;
@IsBoolean()
public isReceived: boolean;
@IsString()
public invitation?: InvitationDTO;
}
import ConnectionsModule from './module';
describe('Check if the module is working', () => {
it('should be defined', () => {
expect(ConnectionsModule).toBeDefined();
});
});
import { HttpModule } from '@nestjs/axios';
import { Module } from '@nestjs/common';
import { ClientsModule, Transport } from '@nestjs/microservices';
import NatsClientService from '../client/nats.client.js';
import RestClientService from '../client/rest.client.js';
import { NATSServices } from '../common/constants.js';
import config from '../config/config.js';
import PrismaService from '../prisma/prisma.service.js';
import ConnectionsController from './controller/controller.js';
import ConnectionsService from './services/service.js';
@Module({
imports: [
HttpModule,
ClientsModule.register([
{
name: NATSServices.SERVICE_NAME,
transport: Transport.NATS,
options: {
servers: [config().nats.url as string],
},
},
]),
],
controllers: [ConnectionsController],
providers: [
ConnectionsService,
PrismaService,
NatsClientService,
RestClientService,
],
})
export default class ConnectionsModule {}
import type { Prisma } from '@prisma/client';
import { Injectable } from '@nestjs/common';
import PrismaService from '../../prisma/prisma.service.js';
@Injectable()
export default class ConnectionRepository {
public constructor(private readonly prismaService: PrismaService) {}
public async createConnection(data: Prisma.ConnectionCreateInput) {
return this.prismaService.connection.create({ data });
}
public async createShortUrl(connectionUrl: string) {
return this.prismaService.shortUrlConnection.create({
data: { connectionUrl },
});
}
public async getShortUrl(id: string) {
return this.prismaService.shortUrlConnection.findUnique({
where: { id },
});
}
public async updateConnection(params: {
where: Prisma.ConnectionWhereUniqueInput;
data: Prisma.ConnectionUpdateInput;
}) {
const { where, data } = params;
return this.prismaService.connection.update({
data,
where,
});
}
public async updateManyConnection(params: {
where: Prisma.ConnectionWhereInput;
data: Prisma.ConnectionUpdateInput;
}) {
const { where, data } = params;
return this.prismaService.connection.updateMany({
data,
where,
});
}
public async findConnections(params: {
skip?: number;
take?: number;
cursor?: Prisma.ConnectionWhereUniqueInput;
where?: Prisma.ConnectionWhereInput;
orderBy?: Prisma.ConnectionOrderByWithRelationInput;
select?: Prisma.ConnectionSelect;
}) {
const { skip, take, cursor, where, orderBy, select } = params;
if (where) {
where.isActive = true;
}
return this.prismaService.$transaction([
this.prismaService.connection.count({ where }),
this.prismaService.connection.findMany({
skip,
take,
cursor,
where,
orderBy,
select,
}),
]);
}
public async findUniqueConnection(params: {
where: Prisma.ConnectionWhereUniqueInput;
}) {
const { where } = params;
return this.prismaService.connection.findUnique({
where,
});
}
public findByConnectionId(connectionId: string) {
const query = { where: { connectionId } };
return this.findUniqueConnection(query);
}
public findByConnectionByParticipantDID(participantDid: string) {
const query = { where: { participantDid } };
return this.findUniqueConnection(query);
}
}
import {
Controller,
Injectable,
InternalServerErrorException,
} from '@nestjs/common';
import { Cron, CronExpression } from '@nestjs/schedule';
import ConfigClient from '../../client/config.client.js';
import PrismaService from '../../prisma/prisma.service.js';
import logger from '../../utils/logger.js';
import ConnectionRepository from '../repository/connection.repository.js';
import ConnectionsService from '../services/service.js';
@Injectable()
@Controller()
export default class SchedulerService {
private connectionRepository;
public constructor(private readonly prismaService: PrismaService) {
this.connectionRepository = new ConnectionRepository(this.prismaService);
}
@Cron(CronExpression.EVERY_30_SECONDS)
public async expireNonCompleteConnection() {
const compareDateTime = ConfigClient.getConnectionExpire();
if (compareDateTime) {
const checkExpireTillDateTime = ConfigClient.checkExpireTill();
const query = {
where: {
AND: [
{
OR: [
{
status: ConnectionsService.status.INVITED,
},
{
status: ConnectionsService.status.REQUESTED,
},
{
status: ConnectionsService.status.RESPONDED,
},
],
},
{
isActive: true,
},
{
createdDate: {
lt: compareDateTime,
...(checkExpireTillDateTime && { gt: checkExpireTillDateTime }),
},
},
],
},
data: {
isActive: false,
},
};
const result =
await this.connectionRepository.updateManyConnection(query);
logger.info(JSON.stringify(result));
} else {
throw new InternalServerErrorException(
'Connection Expire period is mandatory',
);
}
}
@Cron(CronExpression.EVERY_30_SECONDS)
public async expireNonTrustedConnection() {
const compareDateTime = ConfigClient.getConnectionExpire();
if (compareDateTime) {
const checkExpireTillDateTime = ConfigClient.checkExpireTill();
const query = {
where: {
AND: [
{
status: ConnectionsService.status.COMPLETE,
},
{
isActive: true,
},
{
createdDate: {
lt: compareDateTime,
...(checkExpireTillDateTime && { gt: checkExpireTillDateTime }),
},
},
],
},
data: {
isActive: false,
},
};
const result =
await this.connectionRepository.updateManyConnection(query);
logger.info(JSON.stringify(result));
} else {
throw new InternalServerErrorException(
'Connection Expire period is mandatory',
);
}
}
}
/* eslint-disable @typescript-eslint/no-explicit-any */
import type ConnectionDto from '../entities/entity.js';
import type { TestingModule } from '@nestjs/testing';
import { HttpModule } from '@nestjs/axios';
import { ConfigModule } from '@nestjs/config';
import { ClientsModule, Transport } from '@nestjs/microservices';
import { Test } from '@nestjs/testing';
import NatsClientService from '../../client/nats.client.js';
import RestClientService from '../../client/rest.client.js';
import { NATSServices } from '../../common/constants.js';
import PrismaService from '../../prisma/prisma.service.js';
import ConnectionsService from './service.js';
describe('ConnectionsService', () => {
let service: ConnectionsService;
let prismaService: PrismaService;
let restClientService: RestClientService;
let natsClient: NatsClientService;
beforeEach(async () => {
const module: TestingModule = await Test.createTestingModule({
imports: [
ConfigModule,
HttpModule,
ClientsModule.register([
{
name: NATSServices.SERVICE_NAME,
transport: Transport.NATS,
},
]),
],
providers: [
ConnectionsService,
PrismaService,
NatsClientService,
RestClientService,
],
exports: [PrismaService],
}).compile();
prismaService = module.get<PrismaService>(PrismaService);
service = module.get<ConnectionsService>(ConnectionsService);
restClientService = module.get<RestClientService>(RestClientService);
natsClient = module.get<NatsClientService>(NatsClientService);
});
it('should be defined', () => {
expect(service).toBeDefined();
});
describe('find Connection function', () => {
it('find connection by connection Id', async () => {
const repositoryResult: any = {
id: '1a7f0b09-b20e-4971-b9b1-7adde7256bbc',
connectionId: '7b821264-2ae3-4459-b45f-19fa975d91f7',
participantId: '7780cd24-af13-423e-b1ff-ae944ab6fd71',
status: 'trusted',
participantDid: 'SU1SHqQiDcc6gDvqH8wwYF',
theirDid: 'Ax9xMqE89F9LStfGnTpDzg',
theirLabel: 'sagar@getnada.com',
createdDate: '2022-04-18T11:03:58.099Z',
updatedDate: '2022-04-18T11:05:10.004Z',
isActive: true,
};
const result: any = {
id: '1a7f0b09-b20e-4971-b9b1-7adde7256bbc',
connectionId: '7b821264-2ae3-4459-b45f-19fa975d91f7',
participantId: '7780cd24-af13-423e-b1ff-ae944ab6fd71',
status: 'trusted',
participantDid: 'SU1SHqQiDcc6gDvqH8wwYF',
theirDid: 'Ax9xMqE89F9LStfGnTpDzg',
theirLabel: 'sagar@getnada.com',
createdDate: '2022-04-18T11:03:58.099Z',
updatedDate: '2022-04-18T11:05:10.004Z',
isActive: true,
};
jest
.spyOn(prismaService.connection, 'findUnique')
.mockResolvedValueOnce(repositoryResult);
const res: any = await service.findConnections(
NaN,
-1,
false,
'',
'7b821264-2ae3-4459-b45f-19fa975d91f7',
);
expect(res).toStrictEqual(result);
});
it('find connection by participant DID', async () => {
const repositoryResult: any = {
id: '1a7f0b09-b20e-4971-b9b1-7adde7256bbc',
connectionId: '7b821264-2ae3-4459-b45f-19fa975d91f7',
participantId: '7780cd24-af13-423e-b1ff-ae944ab6fd71',
status: 'trusted',
participantDid: 'SU1SHqQiDcc6gDvqH8wwYF',
theirDid: 'Ax9xMqE89F9LStfGnTpDzg',
theirLabel: 'sagar@getnada.com',
createdDate: '2022-04-18T11:03:58.099Z',
updatedDate: '2022-04-18T11:05:10.004Z',
isActive: true,
};
const result: any = {
id: '1a7f0b09-b20e-4971-b9b1-7adde7256bbc',
connectionId: '7b821264-2ae3-4459-b45f-19fa975d91f7',
participantId: '7780cd24-af13-423e-b1ff-ae944ab6fd71',
status: 'trusted',
participantDid: 'SU1SHqQiDcc6gDvqH8wwYF',
theirDid: 'Ax9xMqE89F9LStfGnTpDzg',
theirLabel: 'sagar@getnada.com',
createdDate: '2022-04-18T11:03:58.099Z',
updatedDate: '2022-04-18T11:05:10.004Z',
isActive: true,
};
jest
.spyOn(prismaService.connection, 'findUnique')
.mockResolvedValueOnce(repositoryResult);
const res: any = await service.findConnections(
NaN,
-1,
'',
'',
'SU1SHqQiDcc6gDvqH8wwYF',
);
expect(res).toStrictEqual(result);
});
it('find connection by participant id', async () => {
const repositoryResult: any = [
3,
[
{
id: '977c7cd6-a1af-4a5a-bd51-03758d8db50f',
connectionId: '19694476-cc8e-42a3-a5ea-0b2503133348',
participantId: '13f412e2-2749-462a-a10a-54f25e326641',
status: 'invited',
participantDid: 'UVE8wxzGEYGjTWWcudc2nB',
theirDid: '',
theirLabel: '',
createdDate: '2022-04-22T12:15:59.365Z',
updatedDate: '2022-04-22T12:15:59.365Z',
isActive: true,
},
{
id: 'a453d1ae-f95d-4485-8a1a-3c4347450537',
connectionId: '24fcc8b7-3cfa-4d46-a14a-9b6297e81d7e',
participantId: '13f412e2-2749-462a-a10a-54f25e326641',
status: 'trusted',
participantDid: '3X5jtG9CJFgsDFyXjUDRNp',
theirDid: 'Us8ZgUGXZ5P7GTF8q5NEgh',
theirLabel: 'tango@vomoto.com',
createdDate: '2022-04-22T12:18:42.273Z',
updatedDate: '2022-04-22T12:23:09.183Z',
isActive: true,
},
{
id: 'ccde23e4-5a21-44d8-a90b-beeba526d5f4',
connectionId: 'ff468f45-7fe8-4964-abb4-d2dd90b6aed3',
participantId: '13f412e2-2749-462a-a10a-54f25e326641',
status: 'responded',
participantDid: 'WYcecJk6ZbWvoF2VD9xTey',
theirDid: '3NigtUWR68H3HPQiuwgfEk',
theirLabel: 'arnold@vomoto.com',
createdDate: '2022-04-22T12:16:03.614Z',
updatedDate: '2022-04-22T12:16:56.132Z',
isActive: true,
},
],
];
const result: any = [
3,
[
{
id: '977c7cd6-a1af-4a5a-bd51-03758d8db50f',
connectionId: '19694476-cc8e-42a3-a5ea-0b2503133348',
participantId: '13f412e2-2749-462a-a10a-54f25e326641',
status: 'invited',
participantDid: 'UVE8wxzGEYGjTWWcudc2nB',
theirDid: '',
theirLabel: '',
createdDate: '2022-04-22T12:15:59.365Z',
updatedDate: '2022-04-22T12:15:59.365Z',
isActive: true,
},
{
id: 'a453d1ae-f95d-4485-8a1a-3c4347450537',
connectionId: '24fcc8b7-3cfa-4d46-a14a-9b6297e81d7e',
participantId: '13f412e2-2749-462a-a10a-54f25e326641',
status: 'trusted',
participantDid: '3X5jtG9CJFgsDFyXjUDRNp',
theirDid: 'Us8ZgUGXZ5P7GTF8q5NEgh',
theirLabel: 'tango@vomoto.com',
createdDate: '2022-04-22T12:18:42.273Z',
updatedDate: '2022-04-22T12:23:09.183Z',
isActive: true,
},
{
id: 'ccde23e4-5a21-44d8-a90b-beeba526d5f4',
connectionId: 'ff468f45-7fe8-4964-abb4-d2dd90b6aed3',
participantId: '13f412e2-2749-462a-a10a-54f25e326641',
status: 'responded',
participantDid: 'WYcecJk6ZbWvoF2VD9xTey',
theirDid: '3NigtUWR68H3HPQiuwgfEk',
theirLabel: 'arnold@vomoto.com',
createdDate: '2022-04-22T12:16:03.614Z',
updatedDate: '2022-04-22T12:16:56.132Z',
isActive: true,
},
],
];
jest
.spyOn(prismaService, '$transaction')
.mockResolvedValueOnce(repositoryResult);
const res: any = await service.findConnections(NaN, -1, '', '', '');
expect(res).toStrictEqual(result);
});
it.skip('find connections by participant Id and status', async () => {
const repositoryResult = [
3,
[
{
id: '977c7cd6-a1af-4a5a-bd51-03758d8db50f',
connectionId: '19694476-cc8e-42a3-a5ea-0b2503133348',
participantId: '13f412e2-2749-462a-a10a-54f25e326641',
status: 'invited',
participantDid: 'UVE8wxzGEYGjTWWcudc2nB',
theirDid: '',
theirLabel: '',
createdDate: '2022-04-22T12:15:59.365Z',
updatedDate: '2022-04-22T12:15:59.365Z',
isActive: true,
},
{
id: 'a453d1ae-f95d-4485-8a1a-3c4347450537',
connectionId: '24fcc8b7-3cfa-4d46-a14a-9b6297e81d7e',
participantId: '13f412e2-2749-462a-a10a-54f25e326641',
status: 'trusted',
participantDid: '3X5jtG9CJFgsDFyXjUDRNp',
theirDid: 'Us8ZgUGXZ5P7GTF8q5NEgh',
theirLabel: 'tango@vomoto.com',
createdDate: '2022-04-22T12:18:42.273Z',
updatedDate: '2022-04-22T12:23:09.183Z',
isActive: true,
},
{
id: 'ccde23e4-5a21-44d8-a90b-beeba526d5f4',
connectionId: 'ff468f45-7fe8-4964-abb4-d2dd90b6aed3',
participantId: '13f412e2-2749-462a-a10a-54f25e326641',
status: 'responded',
participantDid: 'WYcecJk6ZbWvoF2VD9xTey',
theirDid: '3NigtUWR68H3HPQiuwgfEk',
theirLabel: 'arnold@vomoto.com',
createdDate: '2022-04-22T12:16:03.614Z',
updatedDate: '2022-04-22T12:16:56.132Z',
isActive: true,
},
],
];
const result = [
3,
[
{
id: '977c7cd6-a1af-4a5a-bd51-03758d8db50f',
connectionId: '19694476-cc8e-42a3-a5ea-0b2503133348',
participantId: '13f412e2-2749-462a-a10a-54f25e326641',
status: 'invited',
participantDid: 'UVE8wxzGEYGjTWWcudc2nB',
theirDid: '',
theirLabel: '',
createdDate: '2022-04-22T12:15:59.365Z',
updatedDate: '2022-04-22T12:15:59.365Z',
isActive: true,
},
{
id: 'a453d1ae-f95d-4485-8a1a-3c4347450537',
connectionId: '24fcc8b7-3cfa-4d46-a14a-9b6297e81d7e',
participantId: '13f412e2-2749-462a-a10a-54f25e326641',
status: 'trusted',
participantDid: '3X5jtG9CJFgsDFyXjUDRNp',
theirDid: 'Us8ZgUGXZ5P7GTF8q5NEgh',
theirLabel: 'tango@vomoto.com',
createdDate: '2022-04-22T12:18:42.273Z',
updatedDate: '2022-04-22T12:23:09.183Z',
isActive: true,
},
{
id: 'ccde23e4-5a21-44d8-a90b-beeba526d5f4',
connectionId: 'ff468f45-7fe8-4964-abb4-d2dd90b6aed3',
participantId: '13f412e2-2749-462a-a10a-54f25e326641',
status: 'responded',
participantDid: 'WYcecJk6ZbWvoF2VD9xTey',
theirDid: '3NigtUWR68H3HPQiuwgfEk',
theirLabel: 'arnold@vomoto.com',
createdDate: '2022-04-22T12:16:03.614Z',
updatedDate: '2022-04-22T12:16:56.132Z',
isActive: true,
},
],
];
jest
.spyOn(prismaService, '$transaction')
.mockResolvedValueOnce(repositoryResult);
const res = await service.findConnections(
-1,
-1,
'trusted,complete,responded,invited',
undefined,
'13f412e2-2749-462a-a10a-54f25e326641',
);
expect(res).toStrictEqual(result);
});
});
describe.skip('create invitation', () => {
it('Create invitation-> member flow', async () => {
const serviceResult: any = {
invitationUrl:
'http://localhost:4005?c_i=eyJAdHlwZSI6ImRpZDpzb3Y6QnpDYnNOWWhNcmpIaXFaRFRVQVNIZztzcGVjL2Nvbm5lY3Rpb25zLzEuMC9pbnZpdGF0aW9uIiwiQGlkIjoiYWMzYjE0NjktY2Y0Ni00M2ZjLWE4M2EtZGNmZjJjMDA1YjRlIiwibGFiZWwiOiJ0ZWNobmljYV9jb3JwIiwicmVjaXBpZW50S2V5cyI6WyI1bml1NWZmZmVnYkZlS2F3bU5OblRBTEpzaHB1cXpjRm5CUGpBOFFWU2dtWCJdLCJzZXJ2aWNlRW5kcG9pbnQiOiJodHRwOi8vMy4xMTEuNzcuMzg6NDAwNSIsInJvdXRpbmdLZXlzIjpbXX0',
invitation: {
'@type':
'did:sov:BzCbsNYhMrjHiqZDTUASHg;spec/connections/1.0/invitation',
'@id': 'ac3b1469-cf46-43fc-a83a-dcff2c005b4e',
label: 'technica_corp',
recipientKeys: ['5niu5fffegbFeKawmNNnTALJshpuqzcFnBPjA8QVSgmX'],
serviceEndpoint: 'http://localhost:4005',
routingKeys: [],
},
connection: {
_tags: {},
metadata: {},
id: 'c1d73d9e-6988-4c84-9ebc-068c265d2fb6',
createdAt: '2022-04-21T10:52:18.161Z',
did: '9nYw7CSdHPqXf6ayfA7Wo2',
didDoc: {
'@context': 'https://w3id.org/did/v1',
publicKey: [
{
id: '9nYw7CSdHPqXf6ayfA7Wo2#1',
controller: '9nYw7CSdHPqXf6ayfA7Wo2',
type: 'Ed25519VerificationKey2018',
publicKeyBase58: '5niu5fffegbFeKawmNNnTALJshpuqzcFnBPjA8QVSgmX',
},
],
service: [
{
id: '9nYw7CSdHPqXf6ayfA7Wo2#In7780cd24-af13-423e-b1ff-ae944ab6fd71dyAgentService',
serviceEndpoint: 'http://localhost:4005',
type: 'IndyAgent',
priority: 0,
recipientKeys: ['5niu5fffegbFeKawmNNnTALJshpuqzcFnBPjA8QVSgmX'],
routingKeys: [],
},
],
authentication: [
{
publicKey: '9nYw7CSdHPqXf6ayfA7Wo2#1',
type: 'Ed25519SignatureAuthentication2018',
},
],
id: '9nYw7CSdHPqXf6ayfA7Wo2',
},
verkey: '5niu5fffegbFeKawmNNnTALJshpuqzcFnBPjA8QVSgmX',
state: 'invited',
role: 'inviter',
alias: 'member',
autoAcceptConnection: true,
invitation: {
'@type': 'https://didcomm.org/connections/1.0/invitation',
'@id': 'ac3b1469-cf46-43fc-a83a-dcff2c005b4e',
label: 'technica_corp',
recipientKeys: ['5niu5fffegbFeKawmNNnTALJshpuqzcFnBPjA8QVSgmX'],
serviceEndpoint: 'http://localhost:4005',
routingKeys: [],
},
multiUseInvitation: false,
},
};
const agent: any = {
status: 200,
message: 'Agent Data',
data: {
service_endpoint: 'agent URL',
},
};
const result = serviceResult;
jest
.spyOn(natsClient, 'getAgentByParticipantId')
.mockResolvedValueOnce(agent);
jest
.spyOn(restClientService, 'post')
.mockResolvedValueOnce(serviceResult);
const res = await service.createInvitationURL(
{ autoAcceptConnection: true, alias: 'member' },
'participantId',
);
expect(res).toStrictEqual(result);
});
});
describe.skip('Create connection', () => {
it('should create', async () => {
const connectionObj: ConnectionDto = {
connectionId: '7b821264-2ae3-4459-b45f-19fa975d91f7',
status: 'complete',
participantDid: 'SU1SHqQiDcc6gDvqH8wwYF',
theirDid: 'Ax9xMqE89F9LStfGnTpDzg',
theirLabel: 'sagar@getnada.com',
};
const agent: any = {
status: 200,
message: 'Agent Data',
data: {
service_endpoint: 'agent URL',
},
};
const repositoryResult: any = {
id: '52d499e0-f76a-4b25-9c2a-f357bf6b73be',
connectionId: '7b821264-2ae3-4459-b45f-19fa975d91f7',
participantId: '',
status: 'complete',
participantDid: 'SU1SHqQiDcc6gDvqH8wwYF',
theirDid: 'Ax9xMqE89F9LStfGnTpDzg',
theirLabel: 'sagar@getnada.com',
createdDate: '2022-04-27T06:55:01.643Z',
updatedDate: '2022-04-27T06:55:01.643Z',
isActive: true,
};
const result = repositoryResult;
jest.spyOn(natsClient, 'getAgentByURL').mockResolvedValueOnce(agent);
jest
.spyOn(prismaService.connection, 'create')
.mockResolvedValueOnce(repositoryResult);
const res = await service.createConnections(connectionObj);
expect(res).toStrictEqual(result);
});
});
});
import type ConnectionCreateInvitationDto from '../entities/connectionCreateInvitationDto.entity.js';
import type ConnectionSubscriptionEndpointDto from '../entities/connectionSubscribeEndPoint.entity.js';
import type ConnectionDto from '../entities/entity.js';
import type { Connection, Prisma } from '@prisma/client';
import { Injectable } from '@nestjs/common';
import { ConfigService } from '@nestjs/config';
import NatsClientService from '../../client/nats.client.js';
import RestClientService from '../../client/rest.client.js';
import PrismaService from '../../prisma/prisma.service.js';
import logger from '../../utils/logger.js';
import pagination from '../../utils/pagination.js';
import ConnectionRepository from '../repository/connection.repository.js';
@Injectable()
export default class ConnectionsService {
private connectionRepository: ConnectionRepository;
public constructor(
private readonly prismaService: PrismaService,
private readonly natsClient: NatsClientService,
private readonly restClient: RestClientService,
private readonly configService: ConfigService,
) {
this.connectionRepository = new ConnectionRepository(this.prismaService);
}
public static readonly connectionAlias = {
MEMBER: 'member',
SUBSCRIBER: 'subscriber',
TRUST: 'trust',
};
public static readonly status = {
DEFAULT: 'invited',
INVITED: 'invited',
REQUESTED: 'requested',
RESPONDED: 'responded',
COMPLETE: 'complete',
TRUSTED: 'trusted',
};
public static readonly roles = {
INVITER: 'inviter',
INVITEE: 'invitee',
};
public async createConnections(connection: ConnectionDto) {
logger.info(
`connection service create connection connection?.invitation?.serviceEndpoint is ${connection?.invitation?.serviceEndpoint}`,
);
const insertData = {
...connection,
};
delete insertData.invitation;
return this.connectionRepository.createConnection({
...insertData,
});
}
public async sendConnectionStatusToPrincipal(
status: string,
connectionId: string,
theirLabel: string,
participantDID: string,
theirDid: string,
) {
try {
const response =
await this.natsClient.sendConnectionStatusPrincipalManager(
status,
connectionId,
theirLabel,
participantDID,
theirDid,
);
return response;
} catch (error: unknown) {
logger.error(String(error));
return error;
}
}
public async sendMembershipProofRequestToProofManager(connectionId: string) {
try {
const response =
await this.natsClient.sendMembershipProofRequestToProofManager(
connectionId,
);
return response;
} catch (error: unknown) {
logger.error(String(error));
return error;
}
}
public async updateStatusByConnectionId(connection: ConnectionDto) {
return this.connectionRepository.updateConnection({
where: { connectionId: connection.connectionId },
data: {
status: connection.status,
theirDid: connection.theirDid,
theirLabel: connection.theirLabel,
updatedDate: new Date(),
},
});
}
public getConnectionByID(connectionId: string) {
return this.connectionRepository.findByConnectionId(connectionId);
}
public getAgentUrl() {
return this.configService.get('agent');
}
public getAppUrl() {
return this.configService.get('APP_URL');
}
public async findConnections(
pageSize: number,
page: number,
status: string | false,
connectionId = '',
participantDid = '',
) {
let query: {
skip?: number;
take?: number;
cursor?: Prisma.ConnectionWhereUniqueInput;
where?: Prisma.ConnectionWhereInput;
orderBy?: Prisma.ConnectionOrderByWithRelationInput;
} = {};
if (connectionId) {
return this.connectionRepository.findByConnectionId(connectionId);
}
if (participantDid) {
return this.connectionRepository.findByConnectionByParticipantDID(
participantDid,
);
}
if (status) {
const statuses: string[] = status.split(',');
query.where = { status: { in: statuses } };
}
query = { ...query, ...pagination(pageSize, page) };
return this.connectionRepository.findConnections(query);
}
public async createInvitationURL(
connectionCreate: ConnectionCreateInvitationDto,
) {
const { agentUrl } = this.getAgentUrl();
const appUrl = this.getAppUrl();
const responseData = await this.restClient.post(
`${agentUrl}/connections/create-invitation`,
connectionCreate,
);
const shortRow = await this.connectionRepository.createShortUrl(
responseData.invitationUrl,
);
responseData.invitationUrlShort = `${appUrl}/v1/url/${shortRow.id}`;
return responseData;
}
public async findConnectionByShortUrlId(id: string) {
return this.connectionRepository.getShortUrl(id);
}
public async getConnectionInformationRequest(connectionId = '', did = '') {
try {
let connectionDetails: Connection | null = null;
if (connectionId) {
connectionDetails =
await this.connectionRepository.findByConnectionId(connectionId);
}
if (did && !connectionDetails) {
connectionDetails =
await this.connectionRepository.findByConnectionByParticipantDID(did);
}
if (!connectionDetails) {
return null;
}
const response = {
issueCredentials: [],
presentProofs: [],
};
const issueCredentials = await this.natsClient.getIssueCredentials(
connectionDetails.connectionId,
);
if (
issueCredentials &&
Array.isArray(issueCredentials) &&
!!issueCredentials[1]
) {
const [, issueCredentialsArr] = issueCredentials;
response.issueCredentials = issueCredentialsArr;
}
const presentProofs = await this.natsClient.getPresentProofs(
connectionDetails.connectionId,
);
if (presentProofs && Array.isArray(presentProofs) && !!presentProofs[1]) {
const [, presentProofsArr] = presentProofs;
response.presentProofs = presentProofsArr;
}
return response;
} catch (error) {
logger.error(JSON.stringify(error));
return error;
}
}
public async makeConnectionTrusted(connectionId: string) {
return this.connectionRepository.updateConnection({
where: { connectionId },
data: {
status: ConnectionsService.status.TRUSTED,
updatedDate: new Date(),
},
});
}
public publishConnectionSubscriberEndpoint(
data: ConnectionSubscriptionEndpointDto,
) {
this.natsClient.publishConnection(data);
}
public async acceptConnectionInvitation(
agentURL: string,
payload: {
invitationUrl: string;
autoAcceptConnection: boolean;
alias: string;
},
) {
return this.restClient.post(
`${agentURL}/connections/receive-invitation-url`,
payload,
);
}
public async getReceivedConnections() {
return this.connectionRepository.findConnections({
where: { isReceived: true },
select: { connectionId: true },
});
}
}
import type ResponseType from '../common/response.js';
import { Controller, Get, HttpStatus, Version } from '@nestjs/common';
import { ApiOperation, ApiResponse } from '@nestjs/swagger';
@Controller('health')
export default class HealthController {
private res: ResponseType;
@Version(['1'])
@Get()
@ApiOperation({
summary: 'Health check',
description:
'This call provides the capability to check the service is working and up. The call returns 200 Status Code and current server time in json body',
})
@ApiResponse({
status: HttpStatus.OK,
description: 'Service is up and running.',
content: {
'application/json': {
schema: {},
examples: {
'Service is up and running.': {
value: {
statusCode: 200,
message:
'Thu Jan 01 1970 00:00:00 GMT+0000 (Coordinated Universal Time)',
},
},
},
},
},
})
public getHealth() {
this.res = {
statusCode: HttpStatus.OK,
message: `${new Date()}`,
};
return this.res;
}
}
import type { TestingModule } from '@nestjs/testing';
import { HttpStatus } from '@nestjs/common';
import { Test } from '@nestjs/testing';
import HealthController from './health.controller';
describe('Health', () => {
let healthController: HealthController;
beforeEach(async () => {
const module: TestingModule = await Test.createTestingModule({
imports: [],
controllers: [HealthController],
providers: [],
}).compile();
healthController = module.get<HealthController>(HealthController);
});
it('should be defined', () => {
expect(healthController).toBeDefined();
});
it('should call getHealth', () => {
const response = healthController.getHealth();
expect(response.statusCode).toBe(HttpStatus.OK);
});
});
import type { TestingModule } from '@nestjs/testing';
import type {
EventDidcommConnectionsCreateInvitation,
EventDidcommConnectionsReceiveInvitationFromUrl,
} from '@ocm/shared';
import { Test } from '@nestjs/testing';
import { Subject, of, takeUntil } from 'rxjs';
import { NATS_CLIENT } from '../../common/constants.js';
import { InvitationsController } from '../invitations.controller.js';
import { InvitationsService } from '../invitations.service.js';
describe('InvitationsController', () => {
const natsClientMock = {};
let controller: InvitationsController;
let service: InvitationsService;
beforeEach(async () => {
const module: TestingModule = await Test.createTestingModule({
controllers: [InvitationsController],
providers: [
{ provide: NATS_CLIENT, useValue: natsClientMock },
InvitationsService,
],
}).compile();
controller = module.get<InvitationsController>(InvitationsController);
service = module.get<InvitationsService>(InvitationsService);
});
it('should be defined', () => {
expect(controller).toBeDefined();
});
describe('create', () => {
it('should return an invitation', (done) => {
const unsubscribe$ = new Subject<void>();
const tenantId = 'exampleTenantId';
const expectedResult: EventDidcommConnectionsCreateInvitation['data'] = {
invitationUrl: 'https://example.com/invitation',
};
jest
.spyOn(service, 'createInvitation')
.mockReturnValue(of(expectedResult));
controller
.createInvitation({ tenantId })
.pipe(takeUntil(unsubscribe$))
.subscribe((result) => {
expect(result).toStrictEqual(expectedResult);
unsubscribe$.next();
unsubscribe$.complete();
done();
});
});
});
describe('receive', () => {
it('should return a connection', (done) => {
const unsubscribe$ = new Subject<void>();
const tenantId = 'exampleTenantId';
const invitationUrl = 'https://example.com/invitation';
const expectedResult =
{} as EventDidcommConnectionsReceiveInvitationFromUrl['data'];
jest
.spyOn(service, 'receiveInvitationFromURL')
.mockReturnValue(of(expectedResult));
controller
.receiveInvitation({ tenantId }, { invitationUrl })
.pipe(takeUntil(unsubscribe$))
.subscribe((result) => {
expect(result).toStrictEqual(expectedResult);
unsubscribe$.next();
unsubscribe$.complete();
done();
});
});
});
});
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