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
Showing
with 169 additions and 410 deletions
......@@ -27,10 +27,10 @@ export class ConnectionsService {
tenantId: string,
): Observable<EventDidcommConnectionsGetAll['data']> {
return this.natsClient
.send<EventDidcommConnectionsGetAll, EventDidcommConnectionsGetAllInput>(
EventDidcommConnectionsGetAll.token,
{ tenantId },
)
.send<
EventDidcommConnectionsGetAll,
EventDidcommConnectionsGetAllInput
>(EventDidcommConnectionsGetAll.token, { tenantId })
.pipe(map((result) => result.data));
}
......@@ -62,10 +62,10 @@ export class ConnectionsService {
idOrDid: string,
): Observable<EventDidcommConnectionsBlock['data']> {
return this.natsClient
.send<EventDidcommConnectionsBlock, EventDidcommConnectionsBlockInput>(
EventDidcommConnectionsBlock.token,
{ tenantId, idOrDid },
)
.send<
EventDidcommConnectionsBlock,
EventDidcommConnectionsBlockInput
>(EventDidcommConnectionsBlock.token, { tenantId, idOrDid })
.pipe(map((result) => result.data));
}
}
......@@ -7,7 +7,7 @@ export default {
moduleFileExtensions: ['js', 'ts'],
testEnvironment: 'node',
transform: {
'^.+\\.(ts|js)$': [
'^.+\\.(js|ts)$': [
'@swc/jest',
{
...swcConfig,
......@@ -31,10 +31,9 @@ export default {
: ['text-summary', 'html'],
coveragePathIgnorePatterns: [
'<rootDir>/node_modules/',
'<rootDir>/test/',
'<rootDir>/coverage/',
'<rootDir>/dist/',
'<rootDir>/**/test',
'__tests__',
'@types',
'.dto.(t|j)s',
'.enum.ts',
......
......@@ -16,8 +16,7 @@
"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",
"start": "nest start --watch --preserveWatchOutput",
"test": "jest",
"test:watch": "jest --watch",
"test:cov": "jest --coverage",
......@@ -25,53 +24,36 @@
"test:e2e": "jest --config ./test/jest.config.js"
},
"dependencies": {
"@elastic/ecs-winston-format": "^1.5.0",
"@nestjs/axios": "^3.0.1",
"@nestjs/common": "^10.2.8",
"@nestjs/common": "^10.2.10",
"@nestjs/config": "^3.1.1",
"@nestjs/core": "^10.2.8",
"@nestjs/core": "^10.2.10",
"@nestjs/mapped-types": "^2.0.4",
"@nestjs/microservices": "^10.2.8",
"@nestjs/microservices": "^10.2.10",
"@nestjs/platform-express": "^10.2.8",
"@nestjs/swagger": "^7.1.16",
"@nestjs/terminus": "^10.1.1",
"@prisma/client": "^5.6.0",
"@ocm/shared": "workspace:*",
"class-validator": "^0.14.0",
"class-transformer": "^0.5.1",
"express": "^4.17.3",
"joi": "^17.11.0",
"js-base64": "^3.7.2",
"jsonwebtoken": "^9.0.2",
"jwks-rsa": "^3.1.0",
"moment": "^2.29.4",
"nats": "^2.18.0",
"pg": "^8.11.3",
"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",
"@nestjs/testing": "^10.2.10",
"@swc/cli": "^0.1.62",
"@swc/core": "^1.3.96",
"@swc/jest": "^0.2.29",
"@types/express": "^4.17.21",
"@types/jest": "^29.5.8",
"@types/jsonwebtoken": "^9.0.5",
"@types/node": "^20.9.0",
"@types/supertest": "^2.0.16",
"@types/jest": "^29.5.9",
"@types/node": "^20.9.3",
"dotenv-cli": "^7.3.0",
"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.2"
}
}
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],
const moduleFixture = await Test.createTestingModule({
imports: [Application],
}).compile();
app = moduleFixture.createNestApplication();
await app.init();
});
it('should work', () => {
expect(true).toBe(true);
});
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 { TerminusModule } from '@nestjs/terminus';
import ExceptionHandler from './common/exception.handler.js';
import config from './config/config.js';
import validationSchema from './config/validation.js';
import HealthController from './health/health.controller.js';
import { AuthMiddleware } from './middleware/auth.middleware.js';
import PresentationProofsModule from './presentationProof/module.js';
@Module({
imports: [
TerminusModule,
ConfigModule.forRoot({
isGlobal: true,
load: [config],
validationSchema,
}),
PresentationProofsModule,
],
controllers: [HealthController],
providers: [
{
provide: APP_FILTER,
useClass: ExceptionHandler,
},
],
})
export default class AppModule implements NestModule {
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 type { ClientProvider } from '@nestjs/microservices';
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 { ProofsModule } from './proofs/proofs.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>) => {
const provider: Required<ClientProvider> = {
transport: Transport.NATS,
options: {
servers: config.url as string,
},
};
if ('user' in config && 'password' in config) {
provider.options.user = config.user as string;
provider.options.pass = config.password as string;
}
return provider;
},
},
],
}),
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;
},
}),
ProofsModule,
RouterModule.register([
{ module: HealthModule, path: '/health' },
{ module: ProofsModule, path: '/proofs' },
]),
],
})
export class Application {}
import type PresentationSubscriptionEndpointDto from '../presentationProof/entities/presentationSubscribeEndPoint.entity.js';
import { Inject, Injectable } from '@nestjs/common';
import { ClientProxy } from '@nestjs/microservices';
import { lastValueFrom } from 'rxjs';
import { ATTESTATION, Connection, NATSServices } from '../common/constants.js';
@Injectable()
export default class NatsClientService {
public constructor(
@Inject(NATSServices.SERVICE_NAME) private client: ClientProxy,
) {}
public getConnectionById(connectionId: string) {
const pattern = {
endpoint: `${Connection.NATS_ENDPOINT}/${Connection.GET_CONNECTION_By_ID}`,
};
const payload = { connectionId };
return lastValueFrom(this.client.send(pattern, payload));
}
public publishPresentation(data: PresentationSubscriptionEndpointDto) {
this.client.emit(
`${NATSServices.SERVICE_NAME}/${NATSServices.PRESENTATION_SUBSCRIBER_ENDPOINT}`,
data,
);
}
public getCredentialsTypeDetails(type: string) {
const pattern = {
endpoint: `${ATTESTATION.ATTESTATION_MANAGER_SERVICE}/${ATTESTATION.GET_MEMBERSHIP_CREDENTIALS_DETAILS}`,
};
const payload = { type };
return lastValueFrom(this.client.send(pattern, payload));
}
public makeConnectionTrusted(connectionId: string) {
const pattern = {
endpoint: `${Connection.NATS_ENDPOINT}/${Connection.MAKE_CONNECTION_TRUSTED}`,
};
const payload = { connectionId };
return lastValueFrom(this.client.send(pattern, payload));
}
}
// import { ClientProxy } from '@nestjs/microservices';
import NatsClientService from './nats.client';
describe('Check if the nats client is working', () => {
// let natsClient: NatsClientService;
// let client: ClientProxy;
// beforeEach(() => {
// natsClient = new NatsClientService(client);
// });
// jest.mock('rxjs', () => {
// const original = jest.requireActual('rxjs');
// return {
// ...original,
// lastValueFrom: () => new Promise((resolve, reject) => {
// resolve(true);
// }),
// };
// });
it('should be defined', () => {
expect(NatsClientService).toBeDefined();
});
// it('should call the offer membership credential endpoint', async () => {
// const data = {
// status: 'complete',
// connectionId: 'connectionId',
// theirLabel: 'theirLabel',
// participantId: 'participantId',
// participantDID: 'participantDID'
// };
// jest.spyOn(client, 'send').mockReturnValue(of(data));
// const response = await natsClient.OfferMembershipCredential(data);
// expect(response).toBeTruthy();
// });
});
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)),
);
}
public async get(url: string) {
return lastValueFrom(
this.httpService.get(url).pipe(map((response) => response.data)),
);
}
}
export enum LoggerConfig {
FILE_PATH = 'logs/log.json',
LOG_DIR = './logs',
}
export enum NATSServices {
SERVICE_NAME = 'PROOF_MANAGER_SERVICE',
PRESENTATION_SUBSCRIBER_ENDPOINT = 'PresentationSubscriberEndpoint',
}
export enum Abstraction {
NATS_ENDPOINT = 'SSI_ABSTRACTION_SERVICE',
PROOF_STATE_CHANGED = 'ProofStateChanged',
}
export enum Connection {
GET_CONNECTION_By_ID = 'getConnectionById',
NATS_ENDPOINT = 'CONNECTION_MANAGER_SERVICE',
MAKE_CONNECTION_TRUSTED = 'makeConnectionTrusted',
}
export enum ATTESTATION {
ATTESTATION_MANAGER_SERVICE = 'ATTESTATION_MANAGER_SERVICE',
GET_MEMBERSHIP_CREDENTIALS_DETAILS = 'getCredentialsTypeDetails',
CREDENTIAL_TYPE = 'principalMemberCredential',
}
export enum States {
RequestSent = 'request-sent',
PresentationReceived = 'presentation-received',
Done = 'done',
}
export const SERVICE_NAME = 'PROOF_MANAGER_SERVICE';
export const NATS_CLIENT = Symbol('NATS_CLIENT');
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.PROOF_MANAGER_URL,
nats: {
url: process.env.NATS_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,
},
agent: {
agentUrl: process.env.AGENT_URL,
didcommUrl: process.env.DIDCOMM_URL,
},
DATABASE: {
type: 'postgres',
port: 5432,
synchronize: false,
logging: false,
entities: [`${parentDirectory}/../**/**.model{.ts,.js}`],
},
ECSURL: process.env.ECSURL,
ACCEPT_PRESENTATION_CONFIG: process.env.ACCEPT_PRESENTATION_CONFIG,
});
export default config;
import { registerAs } from '@nestjs/config';
export const httpConfig = registerAs('http', () => ({
host: process.env.HOST || '0.0.0.0',
port: Number(process.env.PORT) || 3000,
}));
import { registerAs } from '@nestjs/config';
export const natsConfig = registerAs('nats', () => ({
url: process.env.NATS_URL || 'nats://localhost:4222',
user: process.env.NATS_USER,
password: process.env.NATS_PASSWORD,
monitoringUrl: process.env.NATS_MONITORING_URL || 'http://localhost:8222',
}));
import { registerAs } from '@nestjs/config';
export const ssiConfig = registerAs('ssi', () => ({
agentUrl: process.env.SSI_AGENT_URL || 'http://localhost:3010',
}));
import Joi from 'joi';
const validationSchema = Joi.object({
AGENT_URL: Joi.string().required(),
DATABASE_URL: Joi.string().required(),
NATS_URL: Joi.string().required(),
PORT: Joi.number().required(),
ACCEPT_PRESENTATION_CONFIG: Joi.string().required(),
USE_AUTH: Joi.string(),
OAUTH_CLIENT_ID: Joi.string(),
OAUTH_CLIENT_SECRET: Joi.string(),
OAUTH_TOKEN_URL: Joi.string(),
});
export const validationSchema = Joi.object({
HTTP_HOST: Joi.string(),
HTTP_PORT: Joi.number(),
NATS_URL: Joi.string().uri(),
NATS_USER: Joi.string().optional(),
NATS_PASSWORD: Joi.string().optional(),
NATS_MONITORING_URL: Joi.string().uri(),
export default validationSchema;
SSI_AGENT_URL: Joi.string().uri(),
});
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 {
public 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.js';
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 { MicroserviceOptions } from '@nestjs/microservices';
/* c8 ignore start */
import type { ConfigType } from '@nestjs/config';
import type { MicroserviceOptions, NatsOptions } from '@nestjs/microservices';
import { VersioningType } from '@nestjs/common';
import { ConfigService } from '@nestjs/config';
import { HttpAdapterHost, NestFactory } from '@nestjs/core';
import { Logger, VersioningType } from '@nestjs/common';
import { NestFactory } from '@nestjs/core';
import { Transport } from '@nestjs/microservices';
import { DocumentBuilder, SwaggerModule } from '@nestjs/swagger';
import AppModule from './app.module.js';
import AllExceptionsFilter from './utils/exceptionsFilter.js';
import logger from './utils/logger.js';
import { Application } from './application.js';
import { httpConfig } from './config/http.config.js';
import { natsConfig } from './config/nats.config.js';
const app = await NestFactory.create(AppModule);
const configService = app.get(ConfigService);
const app = await NestFactory.create(Application);
app.enableCors();
app.connectMicroservice<MicroserviceOptions>({
const { url, user, password } = app.get(natsConfig.KEY) as ConfigType<
typeof natsConfig
>;
const microserviceOptions: Required<NatsOptions> = {
transport: Transport.NATS,
options: {
servers: [configService.get('nats')?.url],
servers: [url],
},
});
};
if (user && password) {
microserviceOptions.options.user = user;
microserviceOptions.options.pass = password;
}
app.connectMicroservice<MicroserviceOptions>(microserviceOptions);
app.enableVersioning({
defaultVersion: ['1'],
......@@ -26,10 +38,9 @@ app.enableVersioning({
});
const swaggerConfig = new DocumentBuilder()
.setTitle('Gaia-x Proof Manager API')
.setDescription('API documentation for GAIA-X Proof Manager')
.setTitle('Gaia-X OCM Proof Manager API')
.setDescription('API documentation for Gaia-X OCM Proof Manager')
.setVersion('1.0')
.addServer(`localhost:${configService.get('PORT')}`)
.build();
const document = SwaggerModule.createDocument(app, swaggerConfig);
......@@ -37,9 +48,8 @@ const document = SwaggerModule.createDocument(app, swaggerConfig);
SwaggerModule.setup('/swagger', app, document);
await app.startAllMicroservices();
const httpAdapter = app.get(HttpAdapterHost);
app.useGlobalFilters(new AllExceptionsFilter(httpAdapter));
const { host, port } = app.get(httpConfig.KEY) as ConfigType<typeof httpConfig>;
await app.listen(port as number, host as string);
await app.listen(configService.get('PORT') || 3000, () => {
logger.info(`Listening on Port:${configService.get('PORT')}` || 3000);
});
Logger.log(`Application is running on: ${await app.getUrl()}`);
/* c8 ignore stop */