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

Merge branch 'feat/schemas-and-credentialdefs' into 'main'

Schemas and credential definitions. Refs #9

See merge request eclipse/xfsc/ocm/ocm-engine!17
parents 45595e8d 12faceed
No related branches found
No related tags found
No related merge requests found
Showing
with 818 additions and 173 deletions
......@@ -24,15 +24,13 @@
"test:e2e": "jest --config ./test/jest.config.js"
},
"dependencies": {
"@nestjs/axios": "^3.0.1",
"@nestjs/common": "^10.2.10",
"@nestjs/config": "^3.1.1",
"@nestjs/core": "^10.2.10",
"@nestjs/microservices": "^10.2.10",
"@nestjs/platform-express": "^10.2.8",
"@nestjs/swagger": "^7.1.16",
"@nestjs/terminus": "^10.1.1",
"axios": "^1.6.2",
"@ocm/shared": "workspace:*",
"class-transformer": "^0.5.1",
"class-validator": "^0.14.0",
"express": "^4.17.3",
......
import type { INestApplication } from '@nestjs/common';
import { Test } from '@nestjs/testing';
import { Application } from '../application.js';
describe('Application', () => {
let app: INestApplication;
beforeAll(async () => {
const moduleFixture = await Test.createTestingModule({
imports: [Application],
}).compile();
app = moduleFixture.createNestApplication();
await app.init();
});
afterAll(async () => {
await app.close();
});
it('should be defined', () => {
expect(app).toBeDefined();
});
});
import type { ConfigType } from '@nestjs/config';
import { Module } from '@nestjs/common';
import { ConfigModule } from '@nestjs/config';
import { RouterModule } from '@nestjs/core';
import { HealthModule } from '@ocm/shared';
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 { HealthModule } from './health/health.module.js';
@Module({
imports: [
......@@ -20,7 +23,23 @@ import { HealthModule } from './health/health.module.js';
abortEarly: true,
},
}),
HealthModule,
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;
},
}),
RouterModule.register([{ module: HealthModule, path: '/health' }]),
],
})
export default class AppModule {}
export class Application {}
import type { HealthIndicatorFunction } from '@nestjs/terminus';
import { Controller, Get } from '@nestjs/common';
import { ConfigService } from '@nestjs/config';
import {
HealthCheck,
HealthCheckService,
HttpHealthIndicator,
} from '@nestjs/terminus';
@Controller('health')
export class HealthController {
public constructor(
private readonly config: ConfigService,
private readonly health: HealthCheckService,
private readonly http: HttpHealthIndicator,
) {}
@Get()
@HealthCheck()
public check() {
const healthIndicators: HealthIndicatorFunction[] = [];
const natsMonitoringUrl = this.config.get('nats.monitoringUrl');
if (typeof natsMonitoringUrl === 'string') {
healthIndicators.push(() =>
this.http.pingCheck('nats', natsMonitoringUrl),
);
} else {
healthIndicators.push(() => ({ nats: { status: 'down' } }));
}
return this.health.check(healthIndicators);
}
}
import type { ConfigType } from '@nestjs/config';
import { HttpModule } from '@nestjs/axios';
import { Module } from '@nestjs/common';
import { ClientsModule, Transport } from '@nestjs/microservices';
import { TerminusModule } from '@nestjs/terminus';
import { SERVICE_NAME } from '../common/constants.js';
import { natsConfig } from '../config/nats.config.js';
import { HealthController } from './health.controller.js';
@Module({
imports: [
TerminusModule,
HttpModule,
ClientsModule.registerAsync({
clients: [
{
name: SERVICE_NAME,
inject: [natsConfig.KEY],
useFactory: (config: ConfigType<typeof natsConfig>) => ({
transport: Transport.NATS,
options: {
servers: [config.url as string],
},
}),
},
],
}),
],
controllers: [HealthController],
})
export class HealthModule {}
......@@ -6,9 +6,9 @@ import { NestFactory } from '@nestjs/core';
import { Transport } from '@nestjs/microservices';
import { DocumentBuilder, SwaggerModule } from '@nestjs/swagger';
import AppModule from './app.module.js';
import { Application } from './application.js';
const app = await NestFactory.create(AppModule);
const app = await NestFactory.create(Application);
const configService = app.get(ConfigService);
app.enableCors();
......
......@@ -24,16 +24,13 @@
"test:e2e": "jest --config ./test/jest.config.js"
},
"dependencies": {
"@nestjs/axios": "^3.0.1",
"@nestjs/common": "^10.2.10",
"@nestjs/config": "^3.1.1",
"@nestjs/core": "^10.2.10",
"@nestjs/microservices": "^10.2.10",
"@nestjs/platform-express": "^10.2.8",
"@nestjs/swagger": "^7.1.16",
"@nestjs/terminus": "^10.1.1",
"@ocm/shared": "workspace:*",
"axios": "^1.6.2",
"class-transformer": "^0.5.1",
"class-validator": "^0.14.0",
"express": "^4.17.3",
......
......@@ -2,14 +2,16 @@ 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 { HealthModule } from './health/health.module.js';
import { CredentialDefinitionsModule } from './credential-definitions/credential-definitions.module.js';
import { SchemasModule } from './schemas/schemas.module.js';
@Module({
......@@ -42,8 +44,29 @@ import { SchemasModule } from './schemas/schemas.module.js';
],
}),
HealthModule,
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;
},
}),
SchemasModule,
CredentialDefinitionsModule,
RouterModule.register([
{ module: HealthModule, path: '/health' },
{ module: SchemasModule, path: '/schemas' },
{ module: CredentialDefinitionsModule, path: '/credential-definitions' },
]),
],
})
export class Application {}
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, ResponseFormatInterceptor } from '@ocm/shared';
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;
}
import type { HealthIndicatorFunction } from '@nestjs/terminus';
import { Controller, Get } from '@nestjs/common';
import { ConfigService } from '@nestjs/config';
import {
HealthCheck,
HealthCheckService,
HttpHealthIndicator,
} from '@nestjs/terminus';
@Controller('health')
export class HealthController {
public constructor(
private readonly config: ConfigService,
private readonly health: HealthCheckService,
private readonly http: HttpHealthIndicator,
) {}
@Get()
@HealthCheck()
public check() {
const healthIndicators: HealthIndicatorFunction[] = [];
const natsMonitoringUrl = this.config.get('nats.monitoringUrl');
if (typeof natsMonitoringUrl === 'string') {
healthIndicators.push(() =>
this.http.pingCheck('nats', natsMonitoringUrl),
);
} else {
healthIndicators.push(() => ({ nats: { status: 'down' } }));
}
return this.health.check(healthIndicators);
}
}
import type { ConfigType } from '@nestjs/config';
import { HttpModule } from '@nestjs/axios';
import { Module } from '@nestjs/common';
import { ClientsModule, Transport } from '@nestjs/microservices';
import { TerminusModule } from '@nestjs/terminus';
import { SERVICE_NAME } from '../common/constants.js';
import { natsConfig } from '../config/nats.config.js';
import { HealthController } from './health.controller.js';
@Module({
imports: [
TerminusModule,
HttpModule,
ClientsModule.registerAsync({
clients: [
{
name: SERVICE_NAME,
inject: [natsConfig.KEY],
useFactory: (config: ConfigType<typeof natsConfig>) => ({
transport: Transport.NATS,
options: {
servers: [config.url as string],
},
}),
},
],
}),
],
controllers: [HealthController],
})
export class HealthModule {}
export interface Response<T = unknown> {
statusCode: number;
message: string;
data?: T;
error?: unknown;
}
......@@ -26,8 +26,8 @@ app.enableVersioning({
});
const swaggerConfig = new DocumentBuilder()
.setTitle('Gaia-X OCM Credential Manager API')
.setDescription('API documentation for Gaia-X OCM Credential Manager')
.setTitle('Gaia-X OCM Schema Manager API')
.setDescription('API documentation for Gaia-X OCM Schema Manager')
.setVersion('1.0')
.build();
......
import type { TestingModule } from '@nestjs/testing';
import type { EventAnonCredsSchemasRegisterInput } from '@ocm/shared';
import { Test } from '@nestjs/testing';
import {
......@@ -31,23 +32,20 @@ describe('SchemasService', () => {
describe('getAll', () => {
it('should return the data from NATS client', (done) => {
const unsubscribe$ = new Subject<void>();
const payload = {
tenantId: 'mocked tenantId',
endpoint: EventAnonCredsSchemasGetAll.token,
};
const tenantId = 'mocked tenantId';
const expectedResult: EventAnonCredsSchemasGetAll['data'] = [];
natsClientMock.send.mockReturnValueOnce(
of(new EventAnonCredsSchemasGetAll([], payload.tenantId)),
of(new EventAnonCredsSchemasGetAll([], tenantId)),
);
service
.getAll(payload)
.getAll(tenantId)
.pipe(takeUntil(unsubscribe$))
.subscribe((result) => {
expect(natsClientMock.send).toHaveBeenCalledWith(
{ endpoint: EventAnonCredsSchemasGetAll.token },
payload,
EventAnonCredsSchemasGetAll.token,
{ tenantId },
);
expect(result).toStrictEqual(expectedResult);
......@@ -63,10 +61,8 @@ describe('SchemasService', () => {
describe('getById', () => {
it('should return the data from NATS client', (done) => {
const unsubscribe$ = new Subject<void>();
const payload = {
tenantId: 'mocked tenantId',
schemaId: 'mocked id',
};
const tenantId = 'mocked tenantId';
const schemaId = 'mocked id';
const expectedResult: EventAnonCredsSchemasGetById['data'] = {
issuerId: 'mocked issuerDid',
name: 'mocked name',
......@@ -75,16 +71,16 @@ describe('SchemasService', () => {
};
natsClientMock.send.mockReturnValueOnce(
of(new EventAnonCredsSchemasGetById(expectedResult, payload.tenantId)),
of(new EventAnonCredsSchemasGetById(expectedResult, tenantId)),
);
service
.getById(payload)
.getById(tenantId, schemaId)
.pipe(takeUntil(unsubscribe$))
.subscribe((result) => {
expect(natsClientMock.send).toHaveBeenCalledWith(
{ endpoint: EventAnonCredsSchemasGetById.token },
payload,
EventAnonCredsSchemasGetById.token,
{ tenantId, schemaId },
);
expect(result).toStrictEqual(expectedResult);
......@@ -100,31 +96,31 @@ describe('SchemasService', () => {
describe('register', () => {
it('should return the data from NATS client', (done) => {
const unsubscribe$ = new Subject<void>();
const payload = {
tenantId: 'mocked tenantId',
const tenantId = 'mocked tenantId';
const payload: Omit<EventAnonCredsSchemasRegisterInput, 'tenantId'> = {
issuerDid: 'mocked issuerDid',
name: 'mocked name',
version: '1.0.0',
attributeNames: ['mocked attribute1', 'mocked attribute2'],
};
const expectedResult: EventAnonCredsSchemasRegister['data'] = {
issuerId: 'mocked issuerDid',
issuerId: 'mocked issuerId',
name: 'mocked name',
version: '1.0.0',
attrNames: ['mocked attribute1', 'mocked attribute2'],
};
natsClientMock.send.mockReturnValueOnce(
of(new EventAnonCredsSchemasRegister(expectedResult, payload.tenantId)),
of(new EventAnonCredsSchemasRegister(expectedResult, tenantId)),
);
service
.register(payload)
.register(tenantId, payload)
.pipe(takeUntil(unsubscribe$))
.subscribe((result) => {
expect(natsClientMock.send).toHaveBeenCalledWith(
{ endpoint: EventAnonCredsSchemasRegister.token },
payload,
EventAnonCredsSchemasRegister.token,
{ ...payload, tenantId },
);
expect(result).toStrictEqual(expectedResult);
......
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