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

feat: implement cred defs in schemas manager

parent 9b4e0454
No related branches found
No related tags found
1 merge request!17Schemas and credential definitions. Refs #9
import type { CreateCredentialDefinitionPayload } from '../dto/create-credential-definition.dto.js';
import type { TestingModule } from '@nestjs/testing';
import type {
EventAnonCredsCredentialDefinitionsGetAll,
EventAnonCredsCredentialDefinitionsGetById,
EventAnonCredsCredentialDefinitionsRegister,
} from '@ocm/shared';
import { Test } from '@nestjs/testing';
import { Subject, of, takeUntil } from 'rxjs';
import { NATS_CLIENT } from '../../common/constants.js';
import { CredentialDefinitionsController } from '../credential-definitions.controller.js';
import { CredentialDefinitionsService } from '../credential-definitions.service.js';
describe('CredentialDefinitionsController', () => {
const natsClientMock = {};
let controller: CredentialDefinitionsController;
let service: CredentialDefinitionsService;
beforeEach(async () => {
const module: TestingModule = await Test.createTestingModule({
controllers: [CredentialDefinitionsController],
providers: [
{ provide: NATS_CLIENT, useValue: natsClientMock },
CredentialDefinitionsService,
],
}).compile();
controller = module.get<CredentialDefinitionsController>(
CredentialDefinitionsController,
);
service = module.get<CredentialDefinitionsService>(
CredentialDefinitionsService,
);
});
describe('find', () => {
it('should return a list of credential definitions', (done) => {
const unsubscribe$ = new Subject<void>();
const tenantId = 'exampleTenantId';
const expectedResult: EventAnonCredsCredentialDefinitionsGetAll['data'] =
[];
jest
.spyOn(service, 'findCredentialDefinitions')
.mockReturnValueOnce(of(expectedResult));
controller
.find({ tenantId })
.pipe(takeUntil(unsubscribe$))
.subscribe((result) => {
expect(result).toStrictEqual(expectedResult);
unsubscribe$.next();
unsubscribe$.complete();
done();
});
});
});
describe('get', () => {
it('should return a credential definition', (done) => {
const unsubscribe$ = new Subject<void>();
const tenantId = 'exampleTenantId';
const credentialDefinitionId = 'exampleCredentialDefinitionId';
const expectedResult: EventAnonCredsCredentialDefinitionsGetById['data'] =
{
credentialDefinitionId: 'exampleCredentialDefinitionId',
issuerId: 'exampleIssuerId',
schemaId: 'exampleSchemaId',
tag: 'exampleTag',
type: 'CL',
value: {
primary: {},
revocation: {},
},
};
jest
.spyOn(service, 'getCredentialDefinitionById')
.mockReturnValueOnce(of(expectedResult));
controller
.get({ tenantId }, credentialDefinitionId)
.pipe(takeUntil(unsubscribe$))
.subscribe((result) => {
expect(result).toStrictEqual(expectedResult);
unsubscribe$.next();
unsubscribe$.complete();
done();
});
});
});
describe('register', () => {
it('should return a credential definition', (done) => {
const unsubscribe$ = new Subject<void>();
const tenantId = 'exampleTenantId';
const payload: CreateCredentialDefinitionPayload = {
schemaId: 'exampleSchemaId',
tag: 'exampleTag',
};
const expectedResult: EventAnonCredsCredentialDefinitionsRegister['data'] =
{
credentialDefinitionId: 'exampleCredentialDefinitionId',
issuerId: 'exampleIssuerId',
schemaId: 'exampleSchemaId',
tag: 'exampleTag',
type: 'CL',
value: {
primary: {},
revocation: {},
},
};
jest
.spyOn(service, 'registerCredentialDefinition')
.mockReturnValueOnce(of(expectedResult));
controller
.register({ tenantId }, payload)
.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 { CredentialDefinitionsController } from '../credential-definitions.controller.js';
import { CredentialDefinitionsModule } from '../credential-definitions.module.js';
import { CredentialDefinitionsService } from '../credential-definitions.service.js';
describe('CredentialDefinitionsModule', () => {
let credentialDefinitionsModule: CredentialDefinitionsModule;
beforeEach(async () => {
const moduleRef = await Test.createTestingModule({
imports: [
ClientsModule.registerAsync({
isGlobal: true,
clients: [{ name: NATS_CLIENT, useFactory: () => ({}) }],
}),
CredentialDefinitionsModule,
],
controllers: [CredentialDefinitionsController],
providers: [CredentialDefinitionsService],
}).compile();
credentialDefinitionsModule = moduleRef.get<CredentialDefinitionsModule>(
CredentialDefinitionsModule,
);
});
it('should be defined', () => {
expect(credentialDefinitionsModule).toBeDefined();
});
});
import type { TestingModule } from '@nestjs/testing';
import { Test } from '@nestjs/testing';
import {
EventAnonCredsCredentialDefinitionsGetAll,
EventAnonCredsCredentialDefinitionsGetById,
EventAnonCredsCredentialDefinitionsRegister,
} from '@ocm/shared';
import { Subject, of, takeUntil } from 'rxjs';
import { NATS_CLIENT } from '../../common/constants.js';
import { CredentialDefinitionsService } from '../credential-definitions.service.js';
describe('CredentialDefinitionsService', () => {
let service: CredentialDefinitionsService;
const natsClientMock = { send: jest.fn() };
beforeEach(async () => {
const module: TestingModule = await Test.createTestingModule({
providers: [
{ provide: NATS_CLIENT, useValue: natsClientMock },
CredentialDefinitionsService,
],
}).compile();
service = module.get<CredentialDefinitionsService>(
CredentialDefinitionsService,
);
jest.resetAllMocks();
});
describe('findCredentialDefinitions', () => {
it('should call natsClient.send with the correct pattern and payload', (done) => {
const unsubscribe$ = new Subject<void>();
const tenantId = 'testTenantId';
const expectedResult: EventAnonCredsCredentialDefinitionsGetAll['data'] =
[];
natsClientMock.send.mockReturnValueOnce(
of(new EventAnonCredsCredentialDefinitionsGetAll([], tenantId)),
);
service
.findCredentialDefinitions(tenantId)
.pipe(takeUntil(unsubscribe$))
.subscribe((result) => {
expect(natsClientMock.send).toHaveBeenCalledWith(
EventAnonCredsCredentialDefinitionsGetAll.token,
{ tenantId },
);
expect(result).toStrictEqual(expectedResult);
unsubscribe$.next();
unsubscribe$.complete();
done();
});
});
});
describe('getCredentialDefinitionById', () => {
it('should call natsClient.send with the correct pattern and payload', (done) => {
const unsubscribe$ = new Subject<void>();
const tenantId = 'testTenantId';
const credentialDefinitionId = 'testCredentialDefinitionId';
const expectedResult: EventAnonCredsCredentialDefinitionsGetById['data'] =
{
credentialDefinitionId: 'testCredentialDefinitionId',
issuerId: 'testIssuerId',
schemaId: 'testSchemaId',
tag: 'testTag',
type: 'CL',
value: {
primary: {},
revocation: {},
},
};
natsClientMock.send.mockReturnValueOnce(
of(
new EventAnonCredsCredentialDefinitionsGetById(
expectedResult,
tenantId,
),
),
);
service
.getCredentialDefinitionById(tenantId, credentialDefinitionId)
.pipe(takeUntil(unsubscribe$))
.subscribe((result) => {
expect(natsClientMock.send).toHaveBeenCalledWith(
EventAnonCredsCredentialDefinitionsGetById.token,
{ tenantId, credentialDefinitionId },
);
expect(result).toStrictEqual(expectedResult);
unsubscribe$.next();
unsubscribe$.complete();
done();
});
});
});
describe('createCredentialDefinition', () => {
it('should call natsClient.send with the correct pattern and payload', (done) => {
const unsubscribe$ = new Subject<void>();
const tenantId = 'testTenantId';
const payload = { test: 'payload' };
const expectedResult: EventAnonCredsCredentialDefinitionsRegister['data'] =
{
credentialDefinitionId: 'testCredentialDefinitionId',
issuerId: 'testIssuerId',
schemaId: 'testSchemaId',
tag: 'testTag',
type: 'CL',
value: {
primary: {},
revocation: {},
},
};
natsClientMock.send.mockReturnValueOnce(
of(
new EventAnonCredsCredentialDefinitionsRegister(
expectedResult,
tenantId,
),
),
);
service
.registerCredentialDefinition(tenantId, payload)
.pipe(takeUntil(unsubscribe$))
.subscribe(() => {
expect(natsClientMock.send).toHaveBeenCalledWith(
EventAnonCredsCredentialDefinitionsRegister.token,
{ tenantId, payload },
);
unsubscribe$.next();
unsubscribe$.complete();
done();
});
});
});
});
import {
Body,
Controller,
Get,
HttpStatus,
Param,
Post,
Query,
UseInterceptors,
UsePipes,
ValidationPipe,
} from '@nestjs/common';
import { ApiOperation, ApiResponse, ApiTags } from '@nestjs/swagger';
import { MultitenancyParams } from '@ocm/shared';
import { ResponseFormatInterceptor } from '../common/response-format.interceptor.js';
import { CredentialDefinitionsService } from './credential-definitions.service.js';
import { CreateCredentialDefinitionPayload } from './dto/create-credential-definition.dto.js';
@Controller()
@UsePipes(new ValidationPipe({ transform: true, whitelist: true }))
@UseInterceptors(ResponseFormatInterceptor)
@ApiTags('Credential Definitions')
export class CredentialDefinitionsController {
public constructor(private readonly service: CredentialDefinitionsService) {}
@Get()
@ApiOperation({
summary: 'Fetch a list of credential definitions',
description:
'This call provides a list of credential definitions for a given tenant',
})
@ApiResponse({
status: HttpStatus.OK,
description: 'Credential definitions fetched successfully',
content: {
'application/json': {
schema: {},
examples: {
'Credential definitions fetched successfully': {
value: {
statusCode: 200,
message: 'Credential definitions fetched successfully',
data: [
{
id: '71b784a3',
},
],
},
},
},
},
},
})
@ApiResponse({
status: HttpStatus.NOT_FOUND,
description: 'Tenant not found',
content: {
'application/json': {
schema: {},
examples: {
'Tenant not found': {
value: {
statusCode: 404,
message: 'Tenant not found',
error: 'Not Found',
},
},
},
},
},
})
@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',
error: 'Internal Server Error',
},
},
},
},
},
})
public find(
@Query() { tenantId }: MultitenancyParams,
): ReturnType<CredentialDefinitionsService['findCredentialDefinitions']> {
return this.service.findCredentialDefinitions(tenantId);
}
@Get(':credentialDefinitionId')
@ApiOperation({
summary: 'Fetch a credential definition by ID',
description:
'This call provides a credential definition for a given tenant',
})
@ApiResponse({
status: HttpStatus.OK,
description: 'Credential definition fetched successfully',
content: {
'application/json': {
schema: {},
examples: {
'Credential definition fetched successfully': {
value: {
statusCode: 200,
message: 'Credential definition fetched successfully',
data: {
id: '71b784a3',
},
},
},
},
},
},
})
@ApiResponse({
status: HttpStatus.NOT_FOUND,
description: 'Credential definition not found',
content: {
'application/json': {
schema: {},
examples: {
'Credential definition not found': {
value: {
statusCode: 404,
message: 'Credential definition not found',
error: 'Not Found',
},
},
},
},
},
})
@ApiResponse({
status: HttpStatus.NOT_FOUND,
description: 'Tenant not found',
content: {
'application/json': {
schema: {},
examples: {
'Tenant not found': {
value: {
statusCode: 404,
message: 'Tenant not found',
error: 'Not Found',
},
},
},
},
},
})
@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',
error: 'Internal Server Error',
},
},
},
},
},
})
public get(
@Query() { tenantId }: MultitenancyParams,
@Param('credentialDefinitionId') credentialDefinitionId: string,
): ReturnType<CredentialDefinitionsService['getCredentialDefinitionById']> {
return this.service.getCredentialDefinitionById(
tenantId,
credentialDefinitionId,
);
}
@Post()
@ApiOperation({
summary: 'Create a credential definition',
description:
'This call allows you to create a credential definition for a given tenant',
})
@ApiResponse({
status: HttpStatus.CREATED,
description: 'Credential definition created successfully',
content: {
'application/json': {
schema: {},
examples: {
'Credential definition created successfully': {
value: {
statusCode: 201,
message: 'Credential definition created successfully',
data: {
id: '71b784a3',
},
},
},
},
},
},
})
@ApiResponse({
status: HttpStatus.NOT_FOUND,
description: 'Tenant not found',
content: {
'application/json': {
schema: {},
examples: {
'Tenant not found': {
value: {
statusCode: 404,
message: 'Tenant not found',
error: 'Not Found',
},
},
},
},
},
})
@ApiResponse({
status: HttpStatus.BAD_REQUEST,
description: 'Invalid request',
content: {
'application/json': {
schema: {},
examples: {
'Invalid request': {
value: {
statusCode: 400,
message: 'Invalid request',
error: 'Bad Request',
},
},
},
},
},
})
@ApiResponse({
status: HttpStatus.CONFLICT,
description: 'Credential definition already exists',
content: {
'application/json': {
schema: {},
examples: {
'Credential definition already exists': {
value: {
statusCode: 409,
message: 'Credential definition already exists',
error: 'Conflict',
},
},
},
},
},
})
@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',
error: 'Internal Server Error',
},
},
},
},
},
})
public register(
@Query() { tenantId }: MultitenancyParams,
@Body() payload: CreateCredentialDefinitionPayload,
): ReturnType<CredentialDefinitionsService['registerCredentialDefinition']> {
return this.service.registerCredentialDefinition(tenantId, payload);
}
}
import { Module } from '@nestjs/common';
import { CredentialDefinitionsController } from './credential-definitions.controller.js';
import { CredentialDefinitionsService } from './credential-definitions.service.js';
@Module({
providers: [CredentialDefinitionsService],
controllers: [CredentialDefinitionsController],
})
export class CredentialDefinitionsModule {}
import type { EventAnonCredsCredentialDefinitionsGetAllInput } from '@ocm/shared';
import type { Observable } from 'rxjs';
import { Inject, Injectable } from '@nestjs/common';
import { ClientProxy } from '@nestjs/microservices';
import {
EventAnonCredsCredentialDefinitionsGetAll,
EventAnonCredsCredentialDefinitionsGetById,
EventAnonCredsCredentialDefinitionsRegister,
} from '@ocm/shared';
import { map } from 'rxjs';
import { NATS_CLIENT } from '../common/constants.js';
@Injectable()
export class CredentialDefinitionsService {
public constructor(
@Inject(NATS_CLIENT) private readonly natsClient: ClientProxy,
) {}
public findCredentialDefinitions(
tenantId: string,
): Observable<EventAnonCredsCredentialDefinitionsGetAll['data']> {
return this.natsClient
.send<
EventAnonCredsCredentialDefinitionsGetAll,
EventAnonCredsCredentialDefinitionsGetAllInput
>(EventAnonCredsCredentialDefinitionsGetAll.token, { tenantId })
.pipe(map((result) => result.data));
}
public getCredentialDefinitionById(
tenantId: string,
credentialDefinitionId: string,
): Observable<EventAnonCredsCredentialDefinitionsGetById['data']> {
return this.natsClient
.send(EventAnonCredsCredentialDefinitionsGetById.token, {
tenantId,
credentialDefinitionId,
})
.pipe(map((result) => result.data));
}
public registerCredentialDefinition(
tenantId: string,
payload: unknown,
): Observable<EventAnonCredsCredentialDefinitionsRegister['data']> {
return this.natsClient
.send(EventAnonCredsCredentialDefinitionsRegister.token, {
tenantId,
payload,
})
.pipe(map((result) => result.data));
}
}
import { ApiProperty } from '@nestjs/swagger';
import { IsNotEmpty, IsString } from 'class-validator';
export class CreateCredentialDefinitionPayload {
@IsString()
@IsNotEmpty()
@ApiProperty()
public schemaId: string;
@IsString()
@IsNotEmpty()
@ApiProperty()
public tag: 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