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

feat(credential-manager): add credential requests module

parent 8e12be81
No related branches found
No related tags found
1 merge request!19Credential requests API. Refs #10
......@@ -12,6 +12,7 @@ import { natsConfig } from './config/nats.config.js';
import { ssiConfig } from './config/ssi.config.js';
import { validationSchema } from './config/validation.js';
import { CredentialOffersModule } from './credential-offers/credential-offers.module.js';
import { CredentialRequestsModule } from './credential-requests/credential-requests.module.js';
@Module({
imports: [
......@@ -59,10 +60,12 @@ import { CredentialOffersModule } from './credential-offers/credential-offers.mo
}),
CredentialOffersModule,
CredentialRequestsModule,
RouterModule.register([
{ module: HealthModule, path: '/health' },
{ module: CredentialOffersModule, path: '/credential-offers' },
{ module: CredentialRequestsModule, path: '/credential-requests' },
]),
],
})
......
import type { TestingModule } from '@nestjs/testing';
import type {
EventAnonCredsCredentialRequestGetAll,
EventAnonCredsCredentialRequestGetById,
} from '@ocm/shared';
import { Test } from '@nestjs/testing';
import { Subject, of, takeUntil } from 'rxjs';
import { NATS_CLIENT } from '../../common/constants.js';
import { CredentialRequestsController } from '../credential-requests.controller.js';
import { CredentialRequestsService } from '../credential-requests.service.js';
describe('CredentialRequestsController', () => {
const natsClientMock = {};
let controller: CredentialRequestsController;
let service: CredentialRequestsService;
beforeEach(async () => {
const module: TestingModule = await Test.createTestingModule({
controllers: [CredentialRequestsController],
providers: [
{ provide: NATS_CLIENT, useValue: natsClientMock },
CredentialRequestsService,
],
}).compile();
controller = module.get<CredentialRequestsController>(
CredentialRequestsController,
);
service = module.get<CredentialRequestsService>(CredentialRequestsService);
});
describe('find', () => {
it('should return a list of credential requests', (done) => {
const unsubscribe$ = new Subject<void>();
const tenantId = 'exampleTenantId';
const expectedResult: EventAnonCredsCredentialRequestGetAll['data'] = [];
jest
.spyOn(service, 'findCredentialRequests')
.mockReturnValueOnce(of(expectedResult));
controller
.find({ tenantId })
.pipe(takeUntil(unsubscribe$))
.subscribe((result) => {
expect(result).toStrictEqual(expectedResult);
unsubscribe$.next();
unsubscribe$.complete();
done();
});
});
});
describe('getById', () => {
it('should return a credential request', (done) => {
const unsubscribe$ = new Subject<void>();
const tenantId = 'exampleTenantId';
const credentialRequestId = 'exampleCredentialRequestId';
const expectedResult: EventAnonCredsCredentialRequestGetById['data'] = {
blinded_ms: {},
blinded_ms_correctness_proof: {},
cred_def_id: 'cred_def_id',
nonce: 'nonce',
entropy: 'entropy',
prover_did: 'prover_did',
};
jest
.spyOn(service, 'getCredentialRequestById')
.mockReturnValueOnce(of(expectedResult));
controller
.getById({ credentialRequestId }, { 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 { CredentialRequestsController } from '../credential-requests.controller.js';
import { CredentialRequestsModule } from '../credential-requests.module.js';
import { CredentialRequestsService } from '../credential-requests.service.js';
describe('CredentialRequestsModule', () => {
let credentialRequestsController: CredentialRequestsController;
let credentialRequestsService: CredentialRequestsService;
beforeEach(async () => {
const moduleRef = await Test.createTestingModule({
imports: [
ClientsModule.registerAsync({
isGlobal: true,
clients: [{ name: NATS_CLIENT, useFactory: () => ({}) }],
}),
CredentialRequestsModule,
],
}).compile();
credentialRequestsController = moduleRef.get<CredentialRequestsController>(
CredentialRequestsController,
);
credentialRequestsService = moduleRef.get<CredentialRequestsService>(
CredentialRequestsService,
);
});
it('should be defined', () => {
expect(credentialRequestsController).toBeDefined();
expect(credentialRequestsController).toBeInstanceOf(
CredentialRequestsController,
);
expect(credentialRequestsService).toBeDefined();
expect(credentialRequestsService).toBeInstanceOf(CredentialRequestsService);
});
});
import type { TestingModule } from '@nestjs/testing';
import { Test } from '@nestjs/testing';
import { EventAnonCredsCredentialRequestGetAll } from '@ocm/shared';
import { Subject, of, takeUntil } from 'rxjs';
import { NATS_CLIENT } from '../../common/constants.js';
import { CredentialRequestsService } from '../credential-requests.service.js';
describe('CredentialRequestsService', () => {
const natsClientMock = { send: jest.fn() };
let service: CredentialRequestsService;
beforeEach(async () => {
const module: TestingModule = await Test.createTestingModule({
providers: [
{ provide: NATS_CLIENT, useValue: natsClientMock },
CredentialRequestsService,
],
}).compile();
service = module.get<CredentialRequestsService>(CredentialRequestsService);
});
describe('findCredentialRequests', () => {
it('should call the natsClient send method with the correct arguments', (done) => {
const unsubscribe$ = new Subject<void>();
const tenantId = 'tenantId';
const expectedResult: EventAnonCredsCredentialRequestGetAll['data'] = [];
natsClientMock.send.mockReturnValueOnce(
of(new EventAnonCredsCredentialRequestGetAll(expectedResult, tenantId)),
);
service
.findCredentialRequests(tenantId)
.pipe(takeUntil(unsubscribe$))
.subscribe((result) => {
expect(natsClientMock.send).toHaveBeenCalledWith(
EventAnonCredsCredentialRequestGetAll.token,
{ tenantId },
);
expect(result).toStrictEqual(expectedResult);
unsubscribe$.next();
unsubscribe$.complete();
done();
});
});
});
describe('getCredentialRequestById', () => {
it('should call the natsClient send method with the correct arguments', (done) => {
const unsubscribe$ = new Subject<void>();
const tenantId = 'tenantId';
const credentialRequestId = 'credentialRequestId';
const expectedResult: EventAnonCredsCredentialRequestGetAll['data'] = [];
natsClientMock.send.mockReturnValueOnce(
of(new EventAnonCredsCredentialRequestGetAll(expectedResult, tenantId)),
);
service
.getCredentialRequestById(tenantId, credentialRequestId)
.pipe(takeUntil(unsubscribe$))
.subscribe((result) => {
expect(natsClientMock.send).toHaveBeenCalledWith(
EventAnonCredsCredentialRequestGetAll.token,
{ tenantId, credentialRequestId },
);
expect(result).toStrictEqual(expectedResult);
unsubscribe$.next();
unsubscribe$.complete();
done();
});
});
});
});
import {
Controller,
Get,
HttpStatus,
Param,
Query,
UseInterceptors,
UsePipes,
ValidationPipe,
} from '@nestjs/common';
import { ApiOperation, ApiResponse, ApiTags } from '@nestjs/swagger';
import { MultitenancyParams, ResponseFormatInterceptor } from '@ocm/shared';
import { CredentialRequestsService } from './credential-requests.service.js';
import { GetByIdParams } from './dto/get-by-id.dto.js';
@Controller()
@UsePipes(new ValidationPipe({ transform: true, whitelist: true }))
@UseInterceptors(ResponseFormatInterceptor)
@ApiTags('Credential Requests')
export class CredentialRequestsController {
public constructor(private readonly service: CredentialRequestsService) {}
@Get()
@ApiOperation({
summary: 'Fetch a list of credential requests',
description:
'This call provides a list of credential requests for a given tenant',
})
@ApiResponse({
status: HttpStatus.OK,
description: 'Credential requests fetched successfully',
content: {
'application/json': {
schema: {},
examples: {
'Credential requests fetched successfully': {
value: {
statusCode: 200,
message: 'Credential requests fetched successfully',
data: [
{
id: '71b784a3',
},
],
},
},
'Tenant not found': {
value: {
statusCode: 404,
message: 'Tenant not found',
data: null,
},
},
},
},
},
})
@ApiResponse({
status: HttpStatus.NOT_FOUND,
content: {
'application/json': {
schema: {},
examples: {
'Credential request not found': {
value: {
statusCode: 404,
message: 'Credential request not found',
data: null,
},
},
'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 find(@Query() { tenantId }: MultitenancyParams) {
return this.service.findCredentialRequests(tenantId);
}
@Get(':id')
@ApiOperation({
summary: 'Fetch a credential request by id',
description:
'This call provides a credential request for a given tenant by id',
})
@ApiResponse({
status: HttpStatus.OK,
description: 'Credential request fetched successfully',
content: {
'application/json': {
schema: {},
examples: {
'Credential request fetched successfully': {
value: {
statusCode: 200,
message: 'Credential request fetched successfully',
data: {
id: '71b784a3',
},
},
},
'Tenant not found': {
value: {
statusCode: 404,
message: 'Tenant not found',
data: null,
},
},
},
},
},
})
@ApiResponse({
status: HttpStatus.NOT_FOUND,
content: {
'application/json': {
schema: {},
examples: {
'Credential request not found': {
value: {
statusCode: 404,
message: 'Credential request not found',
data: null,
},
},
'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 getById(
@Param() { credentialRequestId }: GetByIdParams,
@Query() { tenantId }: MultitenancyParams,
) {
return this.service.getCredentialRequestById(tenantId, credentialRequestId);
}
}
import { Module } from '@nestjs/common';
import { CredentialRequestsController } from './credential-requests.controller.js';
import { CredentialRequestsService } from './credential-requests.service.js';
@Module({
providers: [CredentialRequestsService],
controllers: [CredentialRequestsController],
})
export class CredentialRequestsModule {}
import type {
EventAnonCredsCredentialRequestGetAllInput,
EventAnonCredsCredentialRequestGetById,
EventAnonCredsCredentialRequestGetByIdInput,
} from '@ocm/shared';
import { Inject, Injectable } from '@nestjs/common';
import { ClientProxy } from '@nestjs/microservices';
import { EventAnonCredsCredentialRequestGetAll } from '@ocm/shared';
import { map } from 'rxjs';
import { NATS_CLIENT } from '../common/constants.js';
@Injectable()
export class CredentialRequestsService {
public constructor(
@Inject(NATS_CLIENT) private readonly natsClient: ClientProxy,
) {}
public findCredentialRequests(tenantId: string) {
return this.natsClient
.send<
EventAnonCredsCredentialRequestGetAll,
EventAnonCredsCredentialRequestGetAllInput
>(EventAnonCredsCredentialRequestGetAll.token, { tenantId })
.pipe(map(({ data }) => data));
}
public getCredentialRequestById(
tenantId: string,
credentialRequestId: string,
) {
return this.natsClient
.send<
EventAnonCredsCredentialRequestGetById,
EventAnonCredsCredentialRequestGetByIdInput
>(EventAnonCredsCredentialRequestGetAll.token, {
tenantId,
credentialRequestId,
})
.pipe(map(({ data }) => data));
}
}
import { ApiProperty } from '@nestjs/swagger';
import { IsNotEmpty, IsString } from 'class-validator';
export class GetByIdParams {
@IsString()
@IsNotEmpty()
@ApiProperty({
description: 'The credential request ID to retrieve',
format: 'string',
})
public credentialRequestId: string;
}
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