Skip to content

Commit

Permalink
infra client core
Browse files Browse the repository at this point in the history
  • Loading branch information
wermarter committed Mar 18, 2024
1 parent cd2fc23 commit 425427d
Show file tree
Hide file tree
Showing 20 changed files with 1,432 additions and 43 deletions.
5 changes: 3 additions & 2 deletions apps/browser-service/src/infra/pdf/module.ts
Original file line number Diff line number Diff line change
@@ -1,13 +1,14 @@
import { ModuleMetadata } from '@nestjs/common'
import { PdfServiceToken } from 'src/domain'
import { ClassConstructor } from 'class-transformer'

import { IPdfService, PdfServiceToken } from 'src/domain'
import { PdfService } from './service'

export const pdfMetadata: ModuleMetadata = {
providers: [
{
provide: PdfServiceToken,
useClass: PdfService,
useClass: PdfService satisfies ClassConstructor<IPdfService>,
},
],
}
5 changes: 3 additions & 2 deletions apps/browser-service/src/infra/puppeteer/module.ts
Original file line number Diff line number Diff line change
@@ -1,13 +1,14 @@
import { ModuleMetadata } from '@nestjs/common'
import { PuppeteerServiceToken } from 'src/domain'
import { ClassConstructor } from 'class-transformer'

import { IPuppeteerService, PuppeteerServiceToken } from 'src/domain'
import { PuppeteerService } from './service'

export const puppeteerMetadata: ModuleMetadata = {
providers: [
{
provide: PuppeteerServiceToken,
useClass: PuppeteerService,
useClass: PuppeteerService satisfies ClassConstructor<IPuppeteerService>,
},
],
}
34 changes: 13 additions & 21 deletions apps/browser-service/src/infra/puppeteer/service.ts
Original file line number Diff line number Diff line change
@@ -1,12 +1,7 @@
import { NodeEnv } from '@diut/common'
import {
Inject,
Injectable,
Logger,
OnModuleDestroy,
OnModuleInit,
} from '@nestjs/common'
import { Inject, Injectable } from '@nestjs/common'
import * as puppeteer from 'puppeteer-core'
import { AbstractClientService } from '@diut/nestjs-infra'

import {
AppConfig,
Expand All @@ -19,28 +14,20 @@ import { EPuppeteerInitFailed, IPuppeteerService } from 'src/domain'

@Injectable()
export class PuppeteerService
implements OnModuleInit, OnModuleDestroy, IPuppeteerService
extends AbstractClientService
implements IPuppeteerService
{
private logger = new Logger(PuppeteerService.name)
private browser: puppeteer.Browser

constructor(
@Inject(loadAppConfig.KEY) private readonly appConfig: AppConfig,
@Inject(loadPuppeteerConfig.KEY)
private readonly puppeteerConfig: PuppeteerConfig,
) {}

async onModuleInit() {
// TODO: implement retry and throw error
await this.init()
}

async onModuleDestroy() {
this.logger.log('Closing browser on module destroy...')
this.browser && (await this.browser.close())
) {
super({ name: PuppeteerService.name })
}

private async init() {
async initialize() {
const options: puppeteer.PuppeteerLaunchOptions = {
executablePath: this.puppeteerConfig.CHROMIUM_PATH,
args: chromeArgs,
Expand All @@ -52,7 +39,12 @@ export class PuppeteerService
}

this.browser = await puppeteer.launch(options)
this.logger.log('Browser connected!')
}

async terminate() {
if (this.browser) {
await this.browser.close()
}
}

getBrowser() {
Expand Down
1 change: 0 additions & 1 deletion apps/hcdc-access-service/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,6 @@
"ejs": "^3.1.9",
"json-templates": "^5.0.0",
"lodash": "^4.17.21",
"minio": "^7.1.3",
"mongodb": "^6.3.0",
"mongoose": "^8.1.2",
"passport": "^0.6.0",
Expand Down
12 changes: 9 additions & 3 deletions apps/hcdc-access-service/src/app/sample/use-case/print.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,8 @@ import {
BrowserServiceToken,
EEntityNotFound,
IAuthContext,
IStorageService,
StorageServiceToken,
assertPermission,
} from 'src/domain'
import {
Expand All @@ -40,6 +42,8 @@ export class SamplePrintUseCase {
private readonly authContext: IAuthContext,
@Inject(BrowserServiceToken)
private readonly browserService: BrowserServiceClient,
@Inject(StorageServiceToken)
private readonly storageService: IStorageService,
private readonly moduleRef: ModuleRef,
private readonly sampleAssertExistsUseCase: SampleAssertExistsUseCase,
private readonly sampleTypeAssertExistsUseCase: SampleTypeAssertExistsUseCase,
Expand Down Expand Up @@ -109,7 +113,9 @@ export class SamplePrintUseCase {
strategy = await this.moduleRef.resolve(SamplePrintFormChungStrategy)
break
default:
throw new EEntityNotFound(`PrintForm id=${printOptions.printFormId}`)
throw new EEntityNotFound(
`PrintTemplate=${printForm.template} id=${printOptions.printFormId}`,
)
}

samplePrintContext.setStrategy(strategy)
Expand All @@ -122,9 +128,9 @@ export class SamplePrintUseCase {
for (let i = 0; i < input.length; i++) {
const printRequest = await printContexts[i].execute(input[i])
subscriber.next({
htmlContent: `<p>${JSON.stringify(printRequest)}</p>`,
htmlContent: `<p>${JSON.stringify(printRequest, null, 2)}</p>`,
pageFormat: PageFormat.A4,
pageOrientation: PageOrientation.Landscape,
pageOrientation: PageOrientation.Portrait,
})
}
})().then(() => subscriber.complete())
Expand Down
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
export const StorageServiceToken = Symbol('StorageService')

export interface IStorageService {}
export interface IStorageService {
upload(input: { bucket: string; key: string; buffer: Buffer }): Promise<void>
}
24 changes: 16 additions & 8 deletions apps/hcdc-access-service/src/infra/minio/module.ts
Original file line number Diff line number Diff line change
@@ -1,27 +1,35 @@
import { ConfigModule, MinioModule, MinioService } from '@diut/nestjs-infra'
import {
AwsS3ClientModule,
AwsS3ClientService,
ConfigModule,
} from '@diut/nestjs-infra'
import { ModuleMetadata } from '@nestjs/common'
import { ClassConstructor } from 'class-transformer'

import { IStorageService, StorageServiceToken } from 'src/domain'
import { MinioConfig, loadMinioConfig } from 'src/config'

export const minioMetadata: ModuleMetadata = {
imports: [
MinioModule.forRootAsync({
AwsS3ClientModule.registerAsync({
imports: [ConfigModule.forFeature(loadMinioConfig)],
inject: [loadMinioConfig.KEY],
useFactory: async (minioConfig: MinioConfig) => ({
endPoint: minioConfig.MINIO_ENDPOINT,
port: minioConfig.MINIO_PORT,
useSSL: false,
accessKey: minioConfig.MINIO_ACCESS_KEY,
secretKey: minioConfig.MINIO_SECRET_KEY,
connectionId: 'minio',
endpoint: `http://${minioConfig.MINIO_ENDPOINT}:${minioConfig.MINIO_PORT}`,
credentials: {
accessKeyId: minioConfig.MINIO_ACCESS_KEY,
secretAccessKey: minioConfig.MINIO_SECRET_KEY,
},
forcePathStyle: true,
}),
}),
],
providers: [
{
provide: StorageServiceToken,
useClass: MinioService satisfies IStorageService,
useExisting:
AwsS3ClientService satisfies ClassConstructor<IStorageService>,
},
],
}
8 changes: 8 additions & 0 deletions libs/nestjs-infra/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,9 @@
"@nestjs/core": "^10.3.3"
},
"dependencies": {
"@aws-sdk/client-s3": "^3.535.0",
"@aws-sdk/s3-request-presigner": "^3.535.0",
"@aws-sdk/util-format-url": "^3.535.0",
"@diut/common": "workspace:*",
"@nestjs/common": "^10.3.3",
"@nestjs/microservices": "^10.3.3",
Expand All @@ -37,6 +40,10 @@
"@opentelemetry/sdk-node": "^0.45.1",
"@opentelemetry/sdk-trace-base": "^1.21.0",
"@opentelemetry/semantic-conventions": "^1.21.0",
"@smithy/hash-node": "^2.2.0",
"@smithy/protocol-http": "^3.3.0",
"@smithy/url-parser": "^2.2.0",
"async": "^3.2.5",
"class-transformer": "^0.5.1",
"class-validator": "^0.14.1",
"cookie-parser": "^1.4.6",
Expand All @@ -54,6 +61,7 @@
"winston-loki": "^6.0.8"
},
"devDependencies": {
"@types/async": "^3.2.24",
"@types/cookie-parser": "^1.4.6",
"@types/express": "^4.17.21",
"@types/lodash": "^4.14.202",
Expand Down
2 changes: 2 additions & 0 deletions libs/nestjs-infra/src/client/aws-s3/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
export * from './module'
export * from './service'
6 changes: 6 additions & 0 deletions libs/nestjs-infra/src/client/aws-s3/module-builder.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
import { ConfigurableModuleBuilder } from '@nestjs/common'

import { AwsS3ClientOptions } from './service'

export const { ConfigurableModuleClass, MODULE_OPTIONS_TOKEN } =
new ConfigurableModuleBuilder<AwsS3ClientOptions>().build()
10 changes: 10 additions & 0 deletions libs/nestjs-infra/src/client/aws-s3/module.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
import { Module } from '@nestjs/common'

import { ConfigurableModuleClass } from './module-builder'
import { AwsS3ClientService } from './service'

@Module({
providers: [AwsS3ClientService],
exports: [AwsS3ClientService],
})
export class AwsS3ClientModule extends ConfigurableModuleClass {}
133 changes: 133 additions & 0 deletions libs/nestjs-infra/src/client/aws-s3/service.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,133 @@
import {
DeleteObjectCommand,
GetObjectCommand,
PutObjectCommand,
S3Client,
S3ClientConfigType,
} from '@aws-sdk/client-s3'
import { Inject, Injectable } from '@nestjs/common'
import { Readable, Stream } from 'stream'
import { parseUrl } from '@smithy/url-parser'
import { S3RequestPresigner } from '@aws-sdk/s3-request-presigner'
import { Hash } from '@smithy/hash-node'
import { HttpRequest } from '@smithy/protocol-http'
import { formatUrl } from '@aws-sdk/util-format-url'

import { AbstractClientService } from '../service'
import { MODULE_OPTIONS_TOKEN } from './module-builder'

export type AwsS3ClientOptions = S3ClientConfigType & {
connectionId?: string
}

@Injectable()
export class AwsS3ClientService<
TBuckets extends string = string,
> extends AbstractClientService {
private client: S3Client

constructor(
@Inject(MODULE_OPTIONS_TOKEN)
private readonly clientOptions: AwsS3ClientOptions,
) {
super({
name: AwsS3ClientService.name,
connectionId: clientOptions.connectionId,
})
}

initialize() {
this.client = new S3Client(this.clientOptions)
}

terminate() {
this.client.destroy()
}

async upload(input: { bucket: string; key: string; buffer: Buffer }) {
await this.client.send(
new PutObjectCommand({
Bucket: input.bucket,
Key: input.key,
Body: input.buffer,
}),
)
}

async getPresignedUrl(key: string) {
const credentials = this.clientOptions.credentials
const region = this.clientOptions.region
if (!credentials || !region) {
throw new Error('Missing credentials or region required for signing URL')
}

const s3ObjectUrl = parseUrl(
`https://${process.env.S3_BUCKET}.s3.${region}.amazonaws.com/${key}`,
)

const presigner = new S3RequestPresigner({
credentials,
region,
sha256: Hash.bind(null, 'sha256'),
})

const url = await presigner.presign(new HttpRequest(s3ObjectUrl))

return formatUrl(url)
}

async readToStream(input: { key: string; bucket: string }) {
const response = await this.client.send(
new GetObjectCommand({
Bucket: input.bucket,
Key: input.key,
}),
)

return response.Body as Readable
}

async readToBuffer(key: string, bucket: string) {
const buffer = await new Promise<Buffer>((resolve, reject) => {
const getObjectCommand = new GetObjectCommand({
Bucket: bucket,
Key: key,
})

this.client
.send(getObjectCommand)
.then((response) => {
// Store all of data chunks returned from the response data stream
// into an array then use Array#join() to use the returned contents as a String
const responseDataChunks: any[] = []
const responseStream = response.Body as Stream

// Handle an error while streaming the response body
responseStream.once('error', (err) => reject(err))

// Attach a 'data' listener to add the chunks of data to our array
// Each chunk is a Buffer instance
responseStream.on('data', (chunk) => responseDataChunks.push(chunk))

// Once the stream has no more data, join the chunks into a Buffer
responseStream.once('end', () =>
resolve(Buffer.concat(responseDataChunks)),
)
})
.catch((err) => {
reject(err)
})
})

return buffer
}

async deleteByKey(input: { key: string; bucket: string }) {
await this.client.send(
new DeleteObjectCommand({
Bucket: input.bucket,
Key: input.key,
}),
)
}
}
3 changes: 3 additions & 0 deletions libs/nestjs-infra/src/client/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
export * from './minio'
export * from './aws-s3'
export * from './service'
File renamed without changes.
File renamed without changes.
Loading

0 comments on commit 425427d

Please sign in to comment.