Skip to content

Commit

Permalink
Merge pull request #12 from alkem-io/develop
Browse files Browse the repository at this point in the history
Release v0.1.1
  • Loading branch information
valentinyanakiev authored Jun 24, 2024
2 parents d3a5e8f + 52c5ea6 commit a678e86
Show file tree
Hide file tree
Showing 16 changed files with 103 additions and 26 deletions.
4 changes: 2 additions & 2 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "file-service",
"version": "0.1.0",
"version": "0.1.1",
"description": "A file serving microservice for the Alkemio platform",
"main": "main.ts",
"scripts": {
Expand Down
9 changes: 0 additions & 9 deletions src/app.controller.ts

This file was deleted.

4 changes: 2 additions & 2 deletions src/app.module.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,10 @@ import { WinstonModule } from 'nest-winston';
import { Module } from '@nestjs/common';
import { ConfigModule } from '@nestjs/config';
import { WinstonConfigService } from './config';
import { AppController } from './app.controller';
import configuration from './config/configuration';
import { FileModule } from './services/file-reader';
import { BaseExceptionFilterProvider } from './core/filters';
import { HealthModule } from './services/health';

@Module({
imports: [
Expand All @@ -18,8 +18,8 @@ import { BaseExceptionFilterProvider } from './core/filters';
useClass: WinstonConfigService,
}),
FileModule,
HealthModule,
],
controllers: [AppController],
providers: [BaseExceptionFilterProvider],
})
export class AppModule {}
12 changes: 11 additions & 1 deletion src/main.ts
Original file line number Diff line number Diff line change
Expand Up @@ -29,8 +29,18 @@ const isProd = process.env.NODE_ENV === 'production';
const logger = app.get(WINSTON_MODULE_NEST_PROVIDER);
app.useLogger(logger);

app.enableCors({
origin: '*',
allowedHeaders: [
'Origin,X-Requested-With',
'Content-Type,Accept',
'Authorization',
],
methods: ['GET', 'HEAD', 'PUT', 'PATCH', 'POST', 'DELETE'],
});

await app.register(fastifyCookie);
await app.register(helmet);
await app.register(helmet, { contentSecurityPolicy: false });

const configService: ConfigService<ConfigType, true> = app.get(ConfigService);
const port = configService.get('settings.application.port', { infer: true });
Expand Down
42 changes: 31 additions & 11 deletions src/services/file-reader/file.adapter.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,19 +3,20 @@ import { firstValueFrom, map, timeInterval, timeout } from 'rxjs';
import { Inject, Injectable, LoggerService } from '@nestjs/common';
import { ConfigService } from '@nestjs/config';
import {
ClientProxy,
ClientProxyFactory,
RmqOptions,
Transport,
} from '@nestjs/microservices';
import { ConfigType } from 'src/config';
import { FileMessagePatternEnum } from './file.message.pattern.enum';
import { FileInfoInputData } from './inputs';
import { FileInfoOutputData } from './outputs';
import { FileInfoOutputData, HealthCheckOutputData } from './outputs';
import { ClientRMQ } from '@nestjs/microservices/client/client-rmq';
import { RMQConnectionError } from './types';

@Injectable()
export class FileAdapterService {
private readonly client: ClientProxy | undefined;
private readonly client: ClientRMQ | undefined;
private readonly timeoutMs: number;

constructor(
Expand Down Expand Up @@ -49,14 +50,29 @@ export class FileAdapterService {
'Client proxy successfully connected to RabbitMQ',
);
})
.catch(this.logger.error);
.catch(({ err }: RMQConnectionError) =>
this.logger.error(err, err.stack),
);

this.timeoutMs = this.configService.get(
'settings.application.response_timeout',
{ infer: true },
);
}

/**
* Is there a healthy connection to the queue
*/
public async isConnected(): Promise<boolean> {
return this.sendWithResponse<HealthCheckOutputData, string>(
FileMessagePatternEnum.HEALTH_CHECK,
'healthy?',
{ timeoutMs: 3000 },
)
.then((resp) => resp.healthy)
.catch(() => false);
}

/**
* Give information about a file. Access of the requester to that file and metadata.
* This method sends a request to the queue and waits for a response.
Expand All @@ -79,16 +95,20 @@ export class FileAdapterService {
* Each consumer needs to manually handle failures, returning the proper type.
* @param pattern
* @param data
* @param options
* @throws Error
*/
private sendWithResponse = async <TResult, TInput>(
pattern: FileMessagePatternEnum,
data: TInput,
options?: { timeoutMs?: number },
): Promise<TResult | never> => {
if (!this.client) {
throw new Error(`Connection was not established. Send failed.`);
}

const timeoutMs = options?.timeoutMs ?? this.timeoutMs;

const result$ = this.client.send<TResult, TInput>(pattern, data).pipe(
timeInterval(),
map((x) => {
Expand All @@ -100,17 +120,17 @@ export class FileAdapterService {
});
return x.value;
}),
timeout({ each: this.timeoutMs }),
timeout({ each: timeoutMs }),
);

return firstValueFrom(result$).catch((err) => {
return firstValueFrom(result$).catch(({ err }: RMQConnectionError) => {
this.logger.error(
err?.message ?? err,
err?.stack,
err.message,
err.stack,
JSON.stringify({
pattern,
data,
timeout: this.timeoutMs,
timeout: timeoutMs,
}),
);

Expand All @@ -129,7 +149,7 @@ const authQueueClientProxyFactory = (
queue: string;
},
logger: LoggerService,
): ClientProxy | undefined => {
): ClientRMQ | undefined => {
const { host, port, user, password, heartbeat: _heartbeat, queue } = config;
const heartbeat =
process.env.NODE_ENV === 'production' ? _heartbeat : _heartbeat * 3;
Expand All @@ -153,7 +173,7 @@ const authQueueClientProxyFactory = (
noAck: true,
},
};
return ClientProxyFactory.create(options);
return ClientProxyFactory.create(options) as ClientRMQ;
} catch (err) {
logger.error(`Could not connect to RabbitMQ: ${err}`);
return undefined;
Expand Down
1 change: 1 addition & 0 deletions src/services/file-reader/file.message.pattern.enum.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
export enum FileMessagePatternEnum {
FILE_INFO = 'file-info',
HEALTH_CHECK = 'health-check',
}
4 changes: 4 additions & 0 deletions src/services/file-reader/file.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,10 @@ export class FileService {
this.logger.verbose?.(`Serving files from ${this.storagePath}`);
}

public isConnected(): Promise<boolean> {
return this.adapter.isConnected();
}

public async fileInfo(
docId: string,
auth: {
Expand Down
3 changes: 3 additions & 0 deletions src/services/file-reader/outputs/base.output.data.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
export class BaseOutputData {
constructor(public event: string) {}
}
7 changes: 7 additions & 0 deletions src/services/file-reader/outputs/health.check.output.data.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
import { BaseOutputData } from './base.output.data';

export class HealthCheckOutputData extends BaseOutputData {
constructor(public healthy: boolean) {
super('health-check-output');
}
}
1 change: 1 addition & 0 deletions src/services/file-reader/outputs/index.ts
Original file line number Diff line number Diff line change
@@ -1 +1,2 @@
export * from './file.info.output.data';
export * from './health.check.output.data';
1 change: 1 addition & 0 deletions src/services/file-reader/types/index.ts
Original file line number Diff line number Diff line change
@@ -1,2 +1,3 @@
export * from './document.data';
export * from './file.info.error.code';
export * from './rmq.connection.error';
14 changes: 14 additions & 0 deletions src/services/file-reader/types/rmq.connection.error.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
export type RMQConnectionError = {
err: {
stack: string;
message: string;
};
url: {
protocol: string;
hostname: string;
username: string;
password: string;
port: number;
heartbeat: number;
};
};
15 changes: 15 additions & 0 deletions src/services/health/health.controller.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
import { Controller, Get, HttpException, HttpStatus } from '@nestjs/common';
import { FileService } from '../file-reader';

@Controller('/health')
export class HealthController {
constructor(private readonly fileService: FileService) {}
@Get('/')
public async getHello(): Promise<string> {
if (!(await this.fileService.isConnected())) {
throw new HttpException('unhealthy!', HttpStatus.INTERNAL_SERVER_ERROR);
}

return 'healthy!';
}
}
9 changes: 9 additions & 0 deletions src/services/health/health.module.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
import { Module } from '@nestjs/common';
import { FileModule } from '../file-reader';
import { HealthController } from './health.controller';

@Module({
imports: [FileModule],
controllers: [HealthController],
})
export class HealthModule {}
1 change: 1 addition & 0 deletions src/services/health/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export * from './health.module';

0 comments on commit a678e86

Please sign in to comment.