├── .dockerignore ├── src ├── utils │ ├── services │ │ ├── index.ts │ │ └── html-to-pdf │ │ │ ├── index.ts │ │ │ ├── base │ │ │ └── html-to-pdf.ts │ │ │ └── puppeteer │ │ │ ├── puppeteer.ts │ │ │ └── puppeteer.spec.ts │ ├── guards │ │ ├── index.ts │ │ └── api-key │ │ │ ├── api-key.guard.ts │ │ │ └── api-key.guard.spec.ts │ └── config.ts ├── html-to-pdf │ ├── dto │ │ ├── html-to-pdf-type.ts │ │ ├── response-type.ts │ │ ├── index.ts │ │ ├── pdf-format.ts │ │ ├── html-to-pdf.ts │ │ └── html-to-pdf.spec.ts │ ├── html-to-pdf.module.ts │ ├── html-to-pdf.module.spec.ts │ ├── html-to-pdf.service.ts │ ├── html-to-pdf.controller.ts │ ├── html-to-pdf.service.spec.ts │ └── html-to-pdf.controller.spec.ts ├── app.module.ts ├── app.module.spec.ts └── main.ts ├── .prettierrc ├── .env.example ├── Brew-Logo-Small.png ├── tsconfig.build.json ├── nest-cli.json ├── docs ├── api_reference.md ├── install_and_deploy.md ├── local_development.md └── environment_variables.md ├── test ├── jest-e2e.json └── html-to-pdf.e2e-spec.ts ├── sonar-project.properties ├── docker-compose.yml ├── Dockerfile ├── .gitignore ├── tsconfig.json ├── .github └── workflows │ ├── sonarcloud.yml │ ├── development.yml │ └── release.yml ├── .eslintrc.js ├── LICENSE ├── package.json ├── README.md └── CHANGELOG.md /.dockerignore: -------------------------------------------------------------------------------- 1 | node_modules -------------------------------------------------------------------------------- /src/utils/services/index.ts: -------------------------------------------------------------------------------- 1 | export * from './html-to-pdf'; 2 | -------------------------------------------------------------------------------- /src/utils/guards/index.ts: -------------------------------------------------------------------------------- 1 | export * from './api-key/api-key.guard'; 2 | -------------------------------------------------------------------------------- /.prettierrc: -------------------------------------------------------------------------------- 1 | { 2 | "singleQuote": true, 3 | "trailingComma": "all" 4 | } -------------------------------------------------------------------------------- /.env.example: -------------------------------------------------------------------------------- 1 | ENVIRONMENT=? 2 | API_KEY=? 3 | CORS=? 4 | PORT=? 5 | GLOBAL_PREFIX=? 6 | BODY_SIZE_LIMIT=? -------------------------------------------------------------------------------- /Brew-Logo-Small.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BrewInteractive/export-service-nestjs/HEAD/Brew-Logo-Small.png -------------------------------------------------------------------------------- /src/html-to-pdf/dto/html-to-pdf-type.ts: -------------------------------------------------------------------------------- 1 | export enum HtmlToPdfType { 2 | HTML = 'Html', 3 | URL = 'Url', 4 | } 5 | -------------------------------------------------------------------------------- /src/html-to-pdf/dto/response-type.ts: -------------------------------------------------------------------------------- 1 | export enum ResponseType { 2 | STREAM = 'Stream', 3 | BASE64 = 'Base64', 4 | } 5 | -------------------------------------------------------------------------------- /src/utils/services/html-to-pdf/index.ts: -------------------------------------------------------------------------------- 1 | export * from './base/html-to-pdf'; 2 | export * from './puppeteer/puppeteer'; 3 | -------------------------------------------------------------------------------- /tsconfig.build.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "./tsconfig.json", 3 | "exclude": ["node_modules", "test", "dist", "**/*spec.ts"] 4 | } 5 | -------------------------------------------------------------------------------- /nest-cli.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "https://json.schemastore.org/nest-cli", 3 | "collection": "@nestjs/schematics", 4 | "sourceRoot": "src" 5 | } 6 | -------------------------------------------------------------------------------- /src/html-to-pdf/dto/index.ts: -------------------------------------------------------------------------------- 1 | export * from './html-to-pdf'; 2 | export * from './html-to-pdf-type'; 3 | export * from './pdf-format'; 4 | export * from './response-type'; 5 | -------------------------------------------------------------------------------- /docs/api_reference.md: -------------------------------------------------------------------------------- 1 | # API Reference 2 | 3 | The API reference provides information on how to use the endpoints. It can be accessed via Swagger UI at the `{{BaseUrl}}/swagger` address. 4 | -------------------------------------------------------------------------------- /test/jest-e2e.json: -------------------------------------------------------------------------------- 1 | { 2 | "moduleFileExtensions": ["js", "json", "ts"], 3 | "rootDir": ".", 4 | "testEnvironment": "node", 5 | "testRegex": ".e2e-spec.ts$", 6 | "transform": { 7 | "^.+\\.(t|j)s$": "ts-jest" 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /src/utils/services/html-to-pdf/base/html-to-pdf.ts: -------------------------------------------------------------------------------- 1 | import { HtmlToPdf } from '../../../../html-to-pdf/dto'; 2 | 3 | export const HTML_TO_PDF_PROVIDE = 'HTML_TO_PDF'; 4 | 5 | export interface IHtmlToPdfService { 6 | generatePDFAsync(options: HtmlToPdf): Promise; 7 | } 8 | -------------------------------------------------------------------------------- /src/html-to-pdf/dto/pdf-format.ts: -------------------------------------------------------------------------------- 1 | export enum PdfFormat { 2 | LETTER = 'Letter', 3 | LEGAL = 'Legal', 4 | TABLOID = 'Tabloid', 5 | LEDGER = 'Ledger', 6 | A0 = 'A0', 7 | A1 = 'A1', 8 | A2 = 'A2', 9 | A3 = 'A3', 10 | A4 = 'A4', 11 | A5 = 'A5', 12 | A6 = 'A6', 13 | } 14 | -------------------------------------------------------------------------------- /sonar-project.properties: -------------------------------------------------------------------------------- 1 | sonar.organization=brewinteractive 2 | sonar.projectKey=BrewInteractive_export-service-nestjs 3 | sonar.sources=. 4 | sonar.tests=. 5 | sonar.test.inclusions=**/*.spec.ts 6 | 7 | sonar.javascript.lcov.reportPaths=./coverage/lcov.info 8 | sonar.exclusions=test/** 9 | sonar.coverage.exclusions=src/main.ts -------------------------------------------------------------------------------- /docker-compose.yml: -------------------------------------------------------------------------------- 1 | version: "3" 2 | services: 3 | serve: 4 | container_name: export-service 5 | # image: brewery/export-service:dev-latest 6 | build: 7 | context: . 8 | dockerfile: Dockerfile 9 | expose: 10 | - ${PORT} 11 | restart: always 12 | ports: 13 | - "${PORT}:${PORT}" 14 | env_file: 15 | - .env 16 | -------------------------------------------------------------------------------- /src/app.module.ts: -------------------------------------------------------------------------------- 1 | import { Module } from '@nestjs/common'; 2 | import { ConfigModule } from '@nestjs/config'; 3 | import { HtmlToPdfModule } from './html-to-pdf/html-to-pdf.module'; 4 | import config from './utils/config'; 5 | 6 | @Module({ 7 | imports: [ 8 | ConfigModule.forRoot({ 9 | isGlobal: true, 10 | load: [config], 11 | }), 12 | HtmlToPdfModule, 13 | ], 14 | }) 15 | export class AppModule {} 16 | -------------------------------------------------------------------------------- /src/app.module.spec.ts: -------------------------------------------------------------------------------- 1 | import { AppModule } from './app.module'; 2 | import { Test } from '@nestjs/testing'; 3 | 4 | describe('AppModule', () => { 5 | let appModule: AppModule; 6 | 7 | beforeEach(async () => { 8 | const app = await Test.createTestingModule({ 9 | imports: [AppModule], 10 | }).compile(); 11 | 12 | appModule = app.get(AppModule); 13 | }); 14 | 15 | it('Should be defined', () => { 16 | expect(appModule).toBeDefined(); 17 | }); 18 | }); 19 | -------------------------------------------------------------------------------- /Dockerfile: -------------------------------------------------------------------------------- 1 | FROM node:16-alpine AS build 2 | WORKDIR /app 3 | 4 | RUN apk add --no-cache chromium 5 | 6 | COPY . . 7 | 8 | RUN yarn install 9 | RUN yarn build 10 | 11 | FROM node:16-alpine 12 | WORKDIR /app 13 | 14 | COPY --from=build /app/dist ./dist 15 | COPY --from=build /app/package.json ./ 16 | COPY --from=build /app/node_modules ./node_modules 17 | 18 | 19 | ENV PUPPETEER_SKIP_CHROMIUM_DOWNLOAD=true \ 20 | PUPPETEER_EXECUTABLE_PATH=/usr/bin/chromium-browser 21 | 22 | RUN apk add --no-cache chromium 23 | 24 | CMD ["yarn", "start:prod"] -------------------------------------------------------------------------------- /src/html-to-pdf/html-to-pdf.module.ts: -------------------------------------------------------------------------------- 1 | import { Module } from '@nestjs/common'; 2 | import { HtmlToPdfController } from './html-to-pdf.controller'; 3 | import { HtmlToPdfService } from './html-to-pdf.service'; 4 | import { HTML_TO_PDF_PROVIDE, PuppeteerService } from '../utils/services'; 5 | 6 | @Module({ 7 | controllers: [HtmlToPdfController], 8 | providers: [ 9 | HtmlToPdfService, 10 | { 11 | useClass: PuppeteerService, 12 | provide: HTML_TO_PDF_PROVIDE, 13 | }, 14 | ], 15 | }) 16 | export class HtmlToPdfModule {} 17 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # compiled output 2 | /dist 3 | /node_modules 4 | 5 | # Logs 6 | logs 7 | *.log 8 | npm-debug.log* 9 | pnpm-debug.log* 10 | yarn-debug.log* 11 | yarn-error.log* 12 | lerna-debug.log* 13 | .env 14 | 15 | # OS 16 | .DS_Store 17 | 18 | # Tests 19 | /coverage 20 | /.nyc_output 21 | 22 | # IDEs and editors 23 | /.idea 24 | .project 25 | .classpath 26 | .c9/ 27 | *.launch 28 | .settings/ 29 | *.sublime-workspace 30 | 31 | # IDE - VSCode 32 | .vscode/* 33 | !.vscode/settings.json 34 | !.vscode/tasks.json 35 | !.vscode/launch.json 36 | !.vscode/extensions.json -------------------------------------------------------------------------------- /src/html-to-pdf/html-to-pdf.module.spec.ts: -------------------------------------------------------------------------------- 1 | import { HtmlToPdfModule } from './html-to-pdf.module'; 2 | import { Test } from '@nestjs/testing'; 3 | 4 | describe('HtmlToPdfModule', () => { 5 | let htmlToPdfModule: HtmlToPdfModule; 6 | 7 | beforeEach(async () => { 8 | const app = await Test.createTestingModule({ 9 | imports: [HtmlToPdfModule], 10 | }).compile(); 11 | 12 | htmlToPdfModule = app.get(HtmlToPdfModule); 13 | }); 14 | 15 | it('Should be defined', () => { 16 | expect(htmlToPdfModule).toBeDefined(); 17 | }); 18 | }); 19 | -------------------------------------------------------------------------------- /src/utils/config.ts: -------------------------------------------------------------------------------- 1 | export default () => ({ 2 | environment: process.env.ENVIRONMENT || 'dev', 3 | port: process.env.PORT || 3000, 4 | isDev: 5 | process.env.ENVIRONMENT === 'dev' || 6 | process.env.ENVIRONMENT === undefined || 7 | process.env.ENVIRONMENT === null || 8 | process.env.ENVIRONMENT == '', 9 | version: '1.0.0', 10 | apiKey: process.env.API_KEY, 11 | cors: process.env.CORS, 12 | puppeteer: { 13 | executablePath: process.env.PUPPETEER_EXECUTABLE_PATH, 14 | }, 15 | globalPrefix: process.env.GLOBAL_PREFIX, 16 | bodySizeLimit: process.env.BODY_SIZE_LIMIT || '5mb', 17 | }); 18 | -------------------------------------------------------------------------------- /src/html-to-pdf/html-to-pdf.service.ts: -------------------------------------------------------------------------------- 1 | import { Inject, Injectable } from '@nestjs/common'; 2 | import { HtmlToPdf } from './dto/html-to-pdf'; 3 | import { HTML_TO_PDF_PROVIDE, IHtmlToPdfService } from '../utils/services'; 4 | import { PdfFormat } from './dto'; 5 | 6 | @Injectable() 7 | export class HtmlToPdfService { 8 | constructor( 9 | @Inject(HTML_TO_PDF_PROVIDE) 10 | private readonly _htmlToPdfService: IHtmlToPdfService, 11 | ) {} 12 | 13 | public generatePDFAsync(options: HtmlToPdf) { 14 | if (!options.format) options.format = PdfFormat.A4; 15 | return this._htmlToPdfService.generatePDFAsync(options); 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /src/utils/guards/api-key/api-key.guard.ts: -------------------------------------------------------------------------------- 1 | import { CanActivate, ExecutionContext, Injectable } from '@nestjs/common'; 2 | import { Observable } from 'rxjs'; 3 | import config from '../../config'; 4 | 5 | @Injectable() 6 | export class ApiKeyGuard implements CanActivate { 7 | canActivate( 8 | context: ExecutionContext, 9 | ): boolean | Promise | Observable { 10 | if (config().apiKey === undefined || config().apiKey === null) return true; 11 | 12 | const request = context.switchToHttp().getRequest(); 13 | if (request?.headers['x-api-key'] === config().apiKey) return true; 14 | return false; 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "module": "commonjs", 4 | "declaration": true, 5 | "removeComments": true, 6 | "emitDecoratorMetadata": true, 7 | "experimentalDecorators": true, 8 | "allowSyntheticDefaultImports": true, 9 | "target": "es2017", 10 | "sourceMap": true, 11 | "outDir": "./dist", 12 | "baseUrl": "./", 13 | "incremental": true, 14 | "skipLibCheck": true, 15 | "strictNullChecks": false, 16 | "noImplicitAny": false, 17 | "strictBindCallApply": false, 18 | "forceConsistentCasingInFileNames": false, 19 | "noFallthroughCasesInSwitch": false, 20 | "resolveJsonModule": true 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /.github/workflows/sonarcloud.yml: -------------------------------------------------------------------------------- 1 | name: sonarcloud 2 | on: 3 | push: 4 | branches: 5 | - main 6 | pull_request: 7 | types: [opened, synchronize, reopened] 8 | 9 | jobs: 10 | sonarcloud: 11 | runs-on: ubuntu-latest 12 | steps: 13 | - uses: actions/checkout@v2 14 | with: 15 | fetch-depth: 0 16 | - name: Install dependencies 17 | run: yarn install 18 | - name: Test and coverage 19 | run: yarn test:cov 20 | - name: SonarCloud Scan 21 | uses: sonarsource/sonarcloud-github-action@master 22 | env: 23 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} 24 | SONAR_TOKEN: ${{ secrets.SONAR_TOKEN }} 25 | -------------------------------------------------------------------------------- /.eslintrc.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | parser: '@typescript-eslint/parser', 3 | parserOptions: { 4 | project: 'tsconfig.json', 5 | tsconfigRootDir : __dirname, 6 | sourceType: 'module', 7 | }, 8 | plugins: ['@typescript-eslint/eslint-plugin'], 9 | extends: [ 10 | 'plugin:@typescript-eslint/recommended', 11 | 'plugin:prettier/recommended', 12 | ], 13 | root: true, 14 | env: { 15 | node: true, 16 | jest: true, 17 | }, 18 | ignorePatterns: ['.eslintrc.js'], 19 | rules: { 20 | '@typescript-eslint/interface-name-prefix': 'off', 21 | '@typescript-eslint/explicit-function-return-type': 'off', 22 | '@typescript-eslint/explicit-module-boundary-types': 'off', 23 | '@typescript-eslint/no-explicit-any': 'off', 24 | }, 25 | }; 26 | -------------------------------------------------------------------------------- /.github/workflows/development.yml: -------------------------------------------------------------------------------- 1 | name: build to dev 2 | on: 3 | push: 4 | branches: 5 | - development 6 | workflow_dispatch: 7 | jobs: 8 | docker: 9 | runs-on: ubuntu-latest 10 | steps: 11 | - name: Set up QEMU 12 | uses: docker/setup-qemu-action@v2 13 | - name: Set up Docker Buildx 14 | uses: docker/setup-buildx-action@v2 15 | - name: Login to DockerHub 16 | uses: docker/login-action@v2 17 | with: 18 | username: ${{ secrets.DOCKERHUB_USERNAME }} 19 | password: ${{ secrets.DOCKERHUB_TOKEN }} 20 | - name: Build and push 21 | uses: docker/build-push-action@v3 22 | with: 23 | push: true 24 | tags: brewery/export-service:dev-latest 25 | platforms: linux/amd64,linux/arm64 26 | -------------------------------------------------------------------------------- /docs/install_and_deploy.md: -------------------------------------------------------------------------------- 1 | # Export Service Installation and Deployment 2 | 3 | To customize the service, review the [environment variables](https://github.com/BrewInteractive/export-service-nestjs/blob/main/docs/environment_variables.md) document. 4 | 5 | ## Deploying With Docker Compose 6 | 7 | By creating the `docker-compose.yml` file, it is possible to deploy the project with `docker` commands below. You can visit the [Docker Hub Repository](https://hub.docker.com/r/brewery/export-service/tags) to review the versions. 8 | 9 | ```yml 10 | version: "3" 11 | services: 12 | serve: 13 | container_name: export-service 14 | image: brewery/export-service:latest 15 | expose: 16 | - ${PORT} 17 | restart: always 18 | ports: 19 | - "${PORT}:${PORT}" 20 | env_file: 21 | - .env 22 | ``` -------------------------------------------------------------------------------- /docs/local_development.md: -------------------------------------------------------------------------------- 1 | # Local Development Instructions 2 | 3 | First, create your ".env" file. Then, you can follow the steps below. 4 | 5 | Install the npm packages for the service requirements. 6 | 7 | ```bash 8 | $ yarn install 9 | ``` 10 | 11 | You can run the project with one of the following commands. 12 | 13 | ```bash 14 | # development 15 | $ yarn start 16 | 17 | # watch mode 18 | $ yarn run start:dev 19 | 20 | # production mode 21 | $ yarn run start:prod 22 | ``` 23 | 24 | ## Running Tests 25 | 26 | There are unit and integration tests within the service, which are written using NestJS's built-in testing framework. You can run the tests using the following commands. 27 | 28 | ```bash 29 | # unit tests 30 | $ yarn test 31 | 32 | # e2e tests 33 | $ yarn run test:e2e 34 | 35 | # test coverage 36 | $ yarn run test:cov 37 | ``` -------------------------------------------------------------------------------- /.github/workflows/release.yml: -------------------------------------------------------------------------------- 1 | name: build to release 2 | on: 3 | push: 4 | tags: 5 | - v* 6 | workflow_dispatch: 7 | jobs: 8 | docker: 9 | runs-on: ubuntu-latest 10 | steps: 11 | - name: Git Checkout 12 | uses: actions/checkout@v3 13 | 14 | - name: Set env 15 | run: echo "RELEASE_VERSION=${GITHUB_REF#refs/*/}" >> $GITHUB_ENV 16 | 17 | - name: Set up QEMU 18 | uses: docker/setup-qemu-action@v2 19 | - name: Set up Docker Buildx 20 | uses: docker/setup-buildx-action@v2 21 | - name: Login to DockerHub 22 | uses: docker/login-action@v2 23 | with: 24 | username: ${{ secrets.DOCKERHUB_USERNAME }} 25 | password: ${{ secrets.DOCKERHUB_TOKEN }} 26 | - name: Build and push 27 | uses: docker/build-push-action@v3 28 | with: 29 | push: true 30 | tags: brewery/export-service:latest, brewery/export-service:${{ env.RELEASE_VERSION }} 31 | platforms: linux/amd64,linux/arm64 32 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2022 Brew Interactive 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /docs/environment_variables.md: -------------------------------------------------------------------------------- 1 | # Environment Variables 2 | 3 | | Variable Name | Description | Required | Default | 4 | | ----------------------- | ------------------------------------------------------------------------------------------------------- | -------- | -------- | 5 | | ENVIRONMENT | Specifies the environment name. If the environment name is given as `dev`, `Swagger` operates actively. | NO | dev | 6 | | CORS | Website endpoints can be defined for Cors safety. | NO | * | 7 | | PORT | It is determined which port will be deploy. | NO | 3000 | 8 | | GLOBAL_PREFIX | Allows to add additional pathname to the service end. | NO | - | 9 | | BODY_SIZE_LIMIT | Specifies the maximum size of the data that will come from the body during the request. | NO | 5mb | 10 | | API_KEY | It allows to add an api key control to the service for security during service use. | NO | - | -------------------------------------------------------------------------------- /src/html-to-pdf/dto/html-to-pdf.ts: -------------------------------------------------------------------------------- 1 | import { 2 | IsDefined, 3 | IsEnum, 4 | IsNotEmpty, 5 | IsString, 6 | Matches, 7 | ValidateIf, 8 | } from 'class-validator'; 9 | 10 | import { ApiPropertyOptional } from '@nestjs/swagger'; 11 | import { HtmlToPdfType } from './html-to-pdf-type'; 12 | import { PdfFormat } from './pdf-format'; 13 | import { ResponseType } from './response-type'; 14 | 15 | export class HtmlToPdf { 16 | @ApiPropertyOptional({ default: 'file-name' }) 17 | @IsNotEmpty() 18 | @IsString() 19 | @IsDefined() 20 | @Matches('^[a-zA-Z0-9_-]*$') 21 | fileName!: string; 22 | 23 | @ApiPropertyOptional({ default: 'Html' }) 24 | @IsNotEmpty() 25 | @IsString() 26 | @IsDefined() 27 | @IsEnum(HtmlToPdfType) 28 | type!: HtmlToPdfType; 29 | 30 | @ApiPropertyOptional({ default: '

Hello Word

' }) 31 | @ValidateIf((o) => o.type === HtmlToPdfType.HTML) 32 | @IsDefined() 33 | html?: string; 34 | 35 | @ApiPropertyOptional() 36 | @ValidateIf((o) => o.type === HtmlToPdfType.URL) 37 | @IsDefined() 38 | url?: string; 39 | 40 | @ApiPropertyOptional({ default: 'A4' }) 41 | @IsString() 42 | @IsDefined() 43 | @IsEnum(PdfFormat) 44 | format?: PdfFormat; 45 | 46 | @ApiPropertyOptional({ default: 'Stream' }) 47 | @IsString() 48 | @IsDefined() 49 | @IsEnum(ResponseType) 50 | responseType?: ResponseType; 51 | } 52 | -------------------------------------------------------------------------------- /src/html-to-pdf/html-to-pdf.controller.ts: -------------------------------------------------------------------------------- 1 | import { ApiKeyGuard } from '../utils/guards'; 2 | import { ApiSecurity } from '@nestjs/swagger'; 3 | import { HtmlToPdfService } from './html-to-pdf.service'; 4 | import { HtmlToPdf } from './dto/html-to-pdf'; 5 | import { Response } from 'express'; 6 | import { Controller, UseGuards, Post, Body, Res } from '@nestjs/common'; 7 | import { ResponseType } from './dto'; 8 | 9 | @Controller('html-to-pdf') 10 | @UseGuards(ApiKeyGuard) 11 | @ApiSecurity('ApiKey') 12 | export class HtmlToPdfController { 13 | constructor(private htmlToPdfService: HtmlToPdfService) {} 14 | 15 | @Post() 16 | async htmlToPdf(@Body() options: HtmlToPdf, @Res() response: Response) { 17 | const pdf = await this.htmlToPdfService.generatePDFAsync(options); 18 | if (options.responseType == ResponseType.BASE64) { 19 | response.writeHead(201, { 'Content-Type': 'application/json' }); 20 | response.write(JSON.stringify({ file: pdf })); 21 | response.end(); 22 | return { file: pdf }; 23 | } else { 24 | response.set({ 25 | 'Content-Type': 'application/pdf', 26 | 'Content-Disposition': `attachment; filename=${options.fileName}.pdf`, 27 | 'Content-Length': pdf.length, 28 | 'Cache-Control': 'no-cache, no-store, must-revalidate', 29 | Pragma: 'no-cache', 30 | Expires: 0, 31 | }); 32 | response.end(pdf); 33 | return pdf; 34 | } 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /src/main.ts: -------------------------------------------------------------------------------- 1 | import { NestFactory } from '@nestjs/core'; 2 | import { AppModule } from './app.module'; 3 | import { SwaggerModule, DocumentBuilder } from '@nestjs/swagger'; 4 | import config from './utils/config'; 5 | import { INestApplication, ValidationPipe } from '@nestjs/common'; 6 | import * as bodyParser from 'body-parser'; 7 | 8 | function initSwagger(app: INestApplication) { 9 | if (config().environment === 'dev') { 10 | const swaggerConfig = new DocumentBuilder() 11 | .setTitle('Export Service') 12 | .setVersion(config().version) 13 | .addSecurity('ApiKey', { 14 | type: 'apiKey', 15 | name: 'x-api-key', 16 | in: 'header', 17 | }) 18 | .build(); 19 | const document = SwaggerModule.createDocument(app, swaggerConfig); 20 | SwaggerModule.setup('swagger', app, document); 21 | } 22 | } 23 | 24 | function initValidationPipe(app: INestApplication) { 25 | app.useGlobalPipes(new ValidationPipe()); 26 | } 27 | 28 | function setGlobalPrefix(app: INestApplication) { 29 | const globalPrefix = config().globalPrefix; 30 | if (globalPrefix) app.setGlobalPrefix(globalPrefix); 31 | } 32 | 33 | async function bootstrap() { 34 | const app = await NestFactory.create(AppModule); 35 | 36 | app.enableCors({ 37 | origin: config().cors || true, 38 | credentials: true, 39 | }); 40 | 41 | app.use(bodyParser.json({ limit: config().bodySizeLimit })); 42 | 43 | setGlobalPrefix(app); 44 | initSwagger(app); 45 | initValidationPipe(app); 46 | await app.listen(config().port); 47 | } 48 | 49 | bootstrap(); 50 | -------------------------------------------------------------------------------- /src/html-to-pdf/dto/html-to-pdf.spec.ts: -------------------------------------------------------------------------------- 1 | import { HtmlToPdf } from './html-to-pdf'; 2 | import { HtmlToPdfType } from './html-to-pdf-type'; 3 | import { PdfFormat } from './pdf-format'; 4 | import { ResponseType } from './response-type'; 5 | 6 | describe('HtmlToPdf', () => { 7 | let htmlToPdf: HtmlToPdf; 8 | 9 | beforeEach(() => { 10 | htmlToPdf = new HtmlToPdf(); 11 | }); 12 | 13 | it('should be defined', () => { 14 | expect(htmlToPdf).toBeDefined(); 15 | }); 16 | 17 | it('should have a valid fileName', () => { 18 | htmlToPdf.fileName = 'valid_file_name'; 19 | expect(htmlToPdf.fileName).toMatch(/^[a-zA-Z0-9_-]*$/); 20 | }); 21 | 22 | it('should have a valid type', () => { 23 | htmlToPdf.type = HtmlToPdfType.HTML; 24 | expect(htmlToPdf.type).toEqual(HtmlToPdfType.HTML); 25 | }); 26 | 27 | it('should have a valid html when type is HTML', () => { 28 | htmlToPdf.type = HtmlToPdfType.HTML; 29 | htmlToPdf.html = '

Hello World

'; 30 | expect(htmlToPdf.html).toBeDefined(); 31 | }); 32 | 33 | it('should have a valid url when type is URL', () => { 34 | htmlToPdf.type = HtmlToPdfType.URL; 35 | htmlToPdf.url = 'http://example.com'; 36 | expect(htmlToPdf.url).toBeDefined(); 37 | }); 38 | 39 | it('should have a valid format', () => { 40 | htmlToPdf.format = PdfFormat.A4; 41 | expect(htmlToPdf.format).toEqual(PdfFormat.A4); 42 | }); 43 | 44 | it('should have a valid responseType', () => { 45 | htmlToPdf.responseType = ResponseType.STREAM; 46 | expect(htmlToPdf.responseType).toEqual(ResponseType.STREAM); 47 | }); 48 | }); 49 | -------------------------------------------------------------------------------- /src/utils/services/html-to-pdf/puppeteer/puppeteer.ts: -------------------------------------------------------------------------------- 1 | import { IHtmlToPdfService } from '../base/html-to-pdf'; 2 | import puppeteer from 'puppeteer'; 3 | import config from '../../../config'; 4 | import { 5 | HtmlToPdf, 6 | HtmlToPdfType, 7 | ResponseType, 8 | } from '../../../../html-to-pdf/dto'; 9 | 10 | export class PuppeteerService implements IHtmlToPdfService { 11 | generatePDFAsync(options: HtmlToPdf): Promise { 12 | if (options.type == HtmlToPdfType.HTML) return this._htmlToPdf(options); 13 | else return this._webUrlToPdf(options); 14 | } 15 | 16 | private async _htmlToPdf(options: HtmlToPdf): Promise { 17 | const browser = await puppeteer.launch({ 18 | headless: true, 19 | executablePath: config().puppeteer.executablePath, 20 | args: ['--no-sandbox', '--headless'], 21 | }); 22 | const page = await browser.newPage(); 23 | await page.setContent(options.html); 24 | const buffer = await page.pdf({ 25 | format: options.format, 26 | printBackground: true, 27 | }); 28 | await browser.close(); 29 | if (options.responseType == ResponseType.BASE64) 30 | return buffer.toString('base64'); 31 | return buffer; 32 | } 33 | 34 | private async _webUrlToPdf(options: HtmlToPdf): Promise { 35 | const browser = await puppeteer.launch({ 36 | headless: true, 37 | executablePath: config().puppeteer.executablePath, 38 | args: ['--no-sandbox', '--headless'], 39 | }); 40 | const page = await browser.newPage(); 41 | await page.goto(options.url, { 42 | waitUntil: 'load', 43 | }); 44 | const buffer = await page.pdf({ 45 | format: options.format, 46 | printBackground: true, 47 | }); 48 | await browser.close(); 49 | if (options.responseType == ResponseType.BASE64) 50 | return buffer.toString('base64'); 51 | return buffer; 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /src/utils/services/html-to-pdf/puppeteer/puppeteer.spec.ts: -------------------------------------------------------------------------------- 1 | import { PuppeteerService } from './puppeteer'; 2 | import { 3 | HtmlToPdf, 4 | HtmlToPdfType, 5 | PdfFormat, 6 | ResponseType, 7 | } from '../../../../html-to-pdf/dto'; 8 | 9 | describe('Puppeteer', () => { 10 | let puppeteerService: PuppeteerService; 11 | 12 | beforeEach(async () => { 13 | puppeteerService = new PuppeteerService(); 14 | }); 15 | 16 | it('Should be defined', () => { 17 | expect(puppeteerService).toBeDefined(); 18 | }); 19 | 20 | it('The sent HTML must be converted to PDF. Response type STREAM.', async () => { 21 | const options: HtmlToPdf = { 22 | html: '

Hello Word

', 23 | type: HtmlToPdfType.HTML, 24 | fileName: 'hello-word', 25 | format: PdfFormat.A3, 26 | }; 27 | expect( 28 | (await puppeteerService.generatePDFAsync(options)) instanceof Buffer, 29 | ).toEqual(true); 30 | }); 31 | 32 | it('The sent Website Url must be converted to PDF. Response type STREAM.', async () => { 33 | const options: HtmlToPdf = { 34 | url: 'https://google.com.tr', 35 | type: HtmlToPdfType.URL, 36 | fileName: 'hello-word', 37 | format: PdfFormat.A5, 38 | }; 39 | expect( 40 | (await puppeteerService.generatePDFAsync(options)) instanceof Buffer, 41 | ).toEqual(true); 42 | }); 43 | 44 | it('The sent HTML must be converted to PDF. Response type BASE64.', async () => { 45 | const options: HtmlToPdf = { 46 | html: '

Hello Word

', 47 | type: HtmlToPdfType.HTML, 48 | fileName: 'hello-word', 49 | responseType: ResponseType.BASE64, 50 | }; 51 | const response = await puppeteerService.generatePDFAsync(options); 52 | expect(typeof response).toEqual('string'); 53 | expect(response).not.toEqual(null); 54 | expect(response).not.toEqual(undefined); 55 | expect(response).not.toEqual(''); 56 | }); 57 | 58 | it('The sent Website Url must be converted to PDF. Response type BASE64.', async () => { 59 | const options: HtmlToPdf = { 60 | url: 'https://google.com.tr', 61 | type: HtmlToPdfType.URL, 62 | fileName: 'hello-word', 63 | responseType: ResponseType.BASE64, 64 | }; 65 | const response = await puppeteerService.generatePDFAsync(options); 66 | expect(typeof response).toEqual('string'); 67 | expect(response).not.toEqual(null); 68 | expect(response).not.toEqual(undefined); 69 | expect(response).not.toEqual(''); 70 | }); 71 | }); 72 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "export-service", 3 | "version": "1.1.1", 4 | "description": "", 5 | "author": "", 6 | "private": true, 7 | "license": "UNLICENSED", 8 | "scripts": { 9 | "prebuild": "rimraf dist", 10 | "build": "nest build", 11 | "format": "prettier --write \"src/**/*.ts\" \"test/**/*.ts\"", 12 | "start": "nest start", 13 | "start:dev": "nest start --watch", 14 | "start:debug": "nest start --debug --watch", 15 | "start:prod": "node dist/main", 16 | "lint": "eslint \"{src,apps,libs,test}/**/*.ts\" --fix", 17 | "test": "jest", 18 | "test:watch": "jest --watch", 19 | "test:cov": "jest --coverage", 20 | "test:debug": "node --inspect-brk -r tsconfig-paths/register -r ts-node/register node_modules/.bin/jest --runInBand", 21 | "test:e2e": "jest --config ./test/jest-e2e.json", 22 | "release": "standard-version" 23 | }, 24 | "dependencies": { 25 | "@golevelup/ts-jest": "^0.3.3", 26 | "@nestjs/common": "^8.0.0", 27 | "@nestjs/config": "^2.1.0", 28 | "@nestjs/core": "^8.0.0", 29 | "@nestjs/mapped-types": "*", 30 | "@nestjs/platform-express": "^8.0.0", 31 | "@nestjs/swagger": "^5.2.1", 32 | "class-transformer": "^0.5.1", 33 | "class-validator": "^0.13.2", 34 | "puppeteer": "^15.3.0", 35 | "reflect-metadata": "^0.1.13", 36 | "rimraf": "^3.0.2", 37 | "rxjs": "^7.2.0", 38 | "swagger-ui-express": "^4.4.0" 39 | }, 40 | "devDependencies": { 41 | "@faker-js/faker": "^8.3.1", 42 | "@nestjs/cli": "^8.0.0", 43 | "@nestjs/schematics": "^8.0.0", 44 | "@nestjs/testing": "^8.0.0", 45 | "@types/express": "^4.17.13", 46 | "@types/jest": "27.5.0", 47 | "@types/node": "^16.0.0", 48 | "@types/supertest": "^2.0.11", 49 | "@typescript-eslint/eslint-plugin": "^5.0.0", 50 | "@typescript-eslint/parser": "^5.0.0", 51 | "eslint": "^8.0.1", 52 | "eslint-config-prettier": "^8.3.0", 53 | "eslint-plugin-prettier": "^4.0.0", 54 | "jest": "28.0.3", 55 | "prettier": "^2.3.2", 56 | "source-map-support": "^0.5.20", 57 | "standard-version": "^9.5.0", 58 | "supertest": "^6.1.3", 59 | "ts-jest": "28.0.1", 60 | "ts-loader": "^9.2.3", 61 | "ts-node": "^10.0.0", 62 | "tsconfig-paths": "4.0.0", 63 | "typescript": "^4.3.5" 64 | }, 65 | "jest": { 66 | "moduleFileExtensions": [ 67 | "js", 68 | "json", 69 | "ts" 70 | ], 71 | "rootDir": "src", 72 | "testRegex": ".*\\.spec\\.ts$", 73 | "transform": { 74 | "^.+\\.(t|j)s$": "ts-jest" 75 | }, 76 | "collectCoverageFrom": [ 77 | "**/*.(t|j)s" 78 | ], 79 | "coverageDirectory": "../coverage", 80 | "testEnvironment": "node" 81 | } 82 | } 83 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 |

2 | Brew Logo 3 |

4 | 5 |

Export Service

6 | 7 |

Export Service is a Nest.js based rest api designed to provide export operations by Brew Interactive.

8 |

9 | 10 | 11 |

12 |

13 | Instagram 14 | Linkedin 15 | Twitter 16 |

17 | 18 | ## Purpose 19 | 20 | An export service simplifies the extraction and download of data from a system, allowing users to save information in various file formats for external use. 21 | 22 | ## Documents 23 | 24 | - [Export Service Installation and Deployment](https://github.com/BrewInteractive/export-service-nestjs/blob/main/docs/install_and_deploy.md) 25 | - [Deploying With Docker Compose](https://github.com/BrewInteractive/export-service-nestjs/blob/main/docs/install_and_deploy.md#deploying-with-docker-compose) 26 | - [Local Development Instructions](https://github.com/BrewInteractive/export-service-nestjs/blob/main/docs/local_development.md) 27 | - [Running Tests](https://github.com/BrewInteractive/export-service-nestjs/blob/main/docs/local_development.md#running-tests) 28 | - [Environment Variables](https://github.com/BrewInteractive/export-service-nestjs/blob/main/docs/environment_variables.md) 29 | - [API Reference](https://github.com/BrewInteractive/export-service-nestjs/blob/main/docs/api_reference.md) 30 | 31 | ## Conclusion 32 | 33 | These instructions will help you start, configure, test, and use the export-service-nestjs project. The project can be used in any project that requires export functions. 34 | 35 | ## License 36 | 37 | Export Service is [MIT licensed](LICENSE). 38 | -------------------------------------------------------------------------------- /src/utils/guards/api-key/api-key.guard.spec.ts: -------------------------------------------------------------------------------- 1 | import { Test, TestingModule } from '@nestjs/testing'; 2 | 3 | import { ApiKeyGuard } from './api-key.guard'; 4 | import config from '../../config'; 5 | import { faker } from '@faker-js/faker'; 6 | 7 | jest.mock('../../config', () => ({ 8 | __esModule: true, 9 | default: jest.fn(), 10 | })); 11 | 12 | describe('ApiKeyGuard', () => { 13 | let apiKeyGuard: ApiKeyGuard; 14 | 15 | beforeEach(async () => { 16 | const module: TestingModule = await Test.createTestingModule({ 17 | providers: [ApiKeyGuard], 18 | }).compile(); 19 | 20 | apiKeyGuard = module.get(ApiKeyGuard); 21 | }); 22 | 23 | it('Should be defined', () => { 24 | expect(apiKeyGuard).toBeDefined(); 25 | }); 26 | 27 | it('Should operate without an api key', async () => { 28 | (config as jest.Mock).mockImplementation(() => ({ 29 | apiKey: null, 30 | })); 31 | 32 | const context = { 33 | getClass: jest.fn(), 34 | getHandler: jest.fn(), 35 | switchToHttp: jest.fn(), 36 | } as any; 37 | 38 | expect(apiKeyGuard.canActivate(context)).toEqual(true); 39 | }); 40 | 41 | it('Api key should be approved', async () => { 42 | const apiKey = faker.string.alpha(); 43 | 44 | (config as jest.Mock).mockImplementation(() => ({ 45 | apiKey, 46 | })); 47 | 48 | const context = { 49 | getClass: jest.fn(), 50 | getHandler: jest.fn(), 51 | switchToHttp: jest.fn(() => ({ 52 | getRequest: jest.fn().mockReturnValue({ 53 | headers: { 54 | 'x-api-key': apiKey, 55 | }, 56 | }), 57 | })), 58 | } as any; 59 | 60 | expect(apiKeyGuard.canActivate(context)).toEqual(true); 61 | }); 62 | 63 | it('Api key should be not approved', async () => { 64 | (config as jest.Mock).mockImplementation(() => ({ 65 | apiKey: faker.string.alpha(8), 66 | })); 67 | 68 | const context = { 69 | getClass: jest.fn(), 70 | getHandler: jest.fn(), 71 | switchToHttp: jest.fn(() => ({ 72 | getRequest: jest.fn().mockReturnValue({ 73 | headers: { 74 | 'x-api-key': faker.string.alpha(4), 75 | }, 76 | }), 77 | })), 78 | } as any; 79 | 80 | expect(apiKeyGuard.canActivate(context)).toEqual(false); 81 | }); 82 | 83 | it('If the api key is not sent, it should be approved', async () => { 84 | (config as jest.Mock).mockImplementation(() => ({ 85 | apiKey: faker.string.alpha(), 86 | })); 87 | 88 | const context = { 89 | getClass: jest.fn(), 90 | getHandler: jest.fn(), 91 | switchToHttp: jest.fn(() => ({ 92 | getRequest: jest.fn().mockReturnValue({ 93 | headers: {}, 94 | }), 95 | })), 96 | } as any; 97 | 98 | expect(apiKeyGuard.canActivate(context)).toEqual(false); 99 | }); 100 | }); 101 | -------------------------------------------------------------------------------- /src/html-to-pdf/html-to-pdf.service.spec.ts: -------------------------------------------------------------------------------- 1 | import { HTML_TO_PDF_PROVIDE, PuppeteerService } from '../utils/services'; 2 | import { PdfFormat, ResponseType } from './dto'; 3 | import { Test, TestingModule } from '@nestjs/testing'; 4 | 5 | import { HtmlToPdf } from './dto/html-to-pdf'; 6 | import { HtmlToPdfService } from './html-to-pdf.service'; 7 | import { HtmlToPdfType } from './dto/html-to-pdf-type'; 8 | 9 | describe('HtmlToPdfService', () => { 10 | let service: HtmlToPdfService; 11 | 12 | beforeEach(async () => { 13 | const module: TestingModule = await Test.createTestingModule({ 14 | providers: [ 15 | HtmlToPdfService, 16 | { 17 | useClass: PuppeteerService, 18 | provide: HTML_TO_PDF_PROVIDE, 19 | }, 20 | ], 21 | }).compile(); 22 | 23 | service = module.get(HtmlToPdfService); 24 | }); 25 | 26 | it('Should be defined', () => { 27 | expect(service).toBeDefined(); 28 | }); 29 | 30 | it('The sent HTML must be converted to PDF. Response type STREAM.', async () => { 31 | const options: HtmlToPdf = { 32 | html: '

Hello Word

', 33 | type: HtmlToPdfType.HTML, 34 | fileName: 'hello-word', 35 | format: PdfFormat.A4, 36 | responseType: ResponseType.STREAM, 37 | }; 38 | expect((await service.generatePDFAsync(options)) instanceof Buffer).toEqual( 39 | true, 40 | ); 41 | }); 42 | 43 | it('The sent Website Url must be converted to PDF. Response type STREAM.', async () => { 44 | const options: HtmlToPdf = { 45 | url: 'https://google.com.tr', 46 | type: HtmlToPdfType.URL, 47 | fileName: 'hello-word', 48 | format: PdfFormat.A5, 49 | responseType: ResponseType.STREAM, 50 | }; 51 | expect((await service.generatePDFAsync(options)) instanceof Buffer).toEqual( 52 | true, 53 | ); 54 | }); 55 | 56 | it('The sent HTML must be converted to PDF. Response type BASE64.', async () => { 57 | const options: HtmlToPdf = { 58 | html: '

Hello Word

', 59 | type: HtmlToPdfType.HTML, 60 | fileName: 'hello-word', 61 | responseType: ResponseType.BASE64, 62 | }; 63 | const response = await service.generatePDFAsync(options); 64 | expect(typeof response).toEqual('string'); 65 | expect(response).not.toEqual(null); 66 | expect(response).not.toEqual(undefined); 67 | expect(response).not.toEqual(''); 68 | }); 69 | 70 | it('The sent Website Url must be converted to PDF. Response type BASE64.', async () => { 71 | const options: HtmlToPdf = { 72 | url: 'https://google.com.tr', 73 | type: HtmlToPdfType.URL, 74 | fileName: 'hello-word', 75 | format: PdfFormat.A5, 76 | responseType: ResponseType.BASE64, 77 | }; 78 | const response = await service.generatePDFAsync(options); 79 | expect(typeof response).toEqual('string'); 80 | expect(response).not.toEqual(null); 81 | expect(response).not.toEqual(undefined); 82 | expect(response).not.toEqual(''); 83 | }); 84 | }); 85 | -------------------------------------------------------------------------------- /src/html-to-pdf/html-to-pdf.controller.spec.ts: -------------------------------------------------------------------------------- 1 | import { Test, TestingModule } from '@nestjs/testing'; 2 | import { HtmlToPdfController } from './html-to-pdf.controller'; 3 | import { HtmlToPdfService } from './html-to-pdf.service'; 4 | import { HTML_TO_PDF_PROVIDE, PuppeteerService } from '../utils/services'; 5 | import { HtmlToPdf, HtmlToPdfType, PdfFormat, ResponseType } from './dto'; 6 | import { createMock } from '@golevelup/ts-jest'; 7 | import { Response } from 'express'; 8 | 9 | describe('HtmlToPdfController', () => { 10 | let controller: HtmlToPdfController; 11 | let service: HtmlToPdfService; 12 | 13 | const mockResponseObject = () => { 14 | return createMock({ 15 | json: jest.fn().mockReturnThis(), 16 | status: jest.fn().mockReturnThis(), 17 | }); 18 | }; 19 | 20 | beforeEach(async () => { 21 | const module: TestingModule = await Test.createTestingModule({ 22 | controllers: [HtmlToPdfController], 23 | providers: [ 24 | HtmlToPdfService, 25 | { 26 | useClass: PuppeteerService, 27 | provide: HTML_TO_PDF_PROVIDE, 28 | }, 29 | ], 30 | }).compile(); 31 | 32 | controller = module.get(HtmlToPdfController); 33 | service = module.get(HtmlToPdfService); 34 | }); 35 | 36 | it('Should be defined', () => { 37 | expect(controller).toBeDefined(); 38 | expect(service).toBeDefined(); 39 | }); 40 | 41 | it('The sent HTML must be converted to PDF. Response type STREAM.', async () => { 42 | const options: HtmlToPdf = { 43 | html: '

Hello Word

', 44 | type: HtmlToPdfType.HTML, 45 | fileName: 'hello-word', 46 | format: PdfFormat.A5, 47 | responseType: ResponseType.STREAM, 48 | }; 49 | expect( 50 | (await controller.htmlToPdf(options, mockResponseObject())) instanceof 51 | Buffer, 52 | ).toEqual(true); 53 | }); 54 | 55 | it('The sent Website Url must be converted to PDF. Response type STREAM.', async () => { 56 | const options: HtmlToPdf = { 57 | url: 'https://google.com.tr', 58 | type: HtmlToPdfType.URL, 59 | fileName: 'hello-word', 60 | format: PdfFormat.A4, 61 | responseType: ResponseType.STREAM, 62 | }; 63 | expect( 64 | (await controller.htmlToPdf(options, mockResponseObject())) instanceof 65 | Buffer, 66 | ).toEqual(true); 67 | }); 68 | 69 | it('The sent HTML must be converted to PDF. Response type BASE64.', async () => { 70 | const options: HtmlToPdf = { 71 | html: '

Hello Word

', 72 | type: HtmlToPdfType.HTML, 73 | fileName: 'hello-word', 74 | format: PdfFormat.A5, 75 | responseType: ResponseType.BASE64, 76 | }; 77 | const response = await controller.htmlToPdf(options, mockResponseObject()); 78 | expect(response).toHaveProperty('file'); 79 | }); 80 | 81 | it('The sent Website Url must be converted to PDF. Response type BASE64.', async () => { 82 | const options: HtmlToPdf = { 83 | url: 'https://google.com.tr', 84 | type: HtmlToPdfType.URL, 85 | fileName: 'hello-word', 86 | format: PdfFormat.A4, 87 | responseType: ResponseType.BASE64, 88 | }; 89 | const response = await controller.htmlToPdf(options, mockResponseObject()); 90 | expect(response).toHaveProperty('file'); 91 | }); 92 | }); 93 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Changelog 2 | 3 | All notable changes to this project will be documented in this file. See [standard-version](https://github.com/conventional-changelog/standard-version) for commit guidelines. 4 | 5 | ### [1.1.1](https://github.com/BrewInteractive/export-service-nestjs/compare/v1.1.0...v1.1.1) (2022-08-29) 6 | 7 | ## 1.1.0 (2022-08-20) 8 | 9 | 10 | ### Features 11 | 12 | * add body size limit property ([23ebb1b](https://github.com/BrewInteractive/export-service-nestjs/commit/23ebb1b34ce9c31c2a6bae2d5139987233543a37)) 13 | * add cors configuration ([f09e067](https://github.com/BrewInteractive/export-service-nestjs/commit/f09e06734092f8babcaa3ed484f8fd7455d70a24)) 14 | * add default values for swagger ([12ea1b2](https://github.com/BrewInteractive/export-service-nestjs/commit/12ea1b287f9eb0888664fdd6d163ab19320a05d4)) 15 | * add global prefix pathname property ([58d741c](https://github.com/BrewInteractive/export-service-nestjs/commit/58d741c62f0976e7a8a9eb89d9a3690dc612080a)) 16 | * add response type property for HTML TO PDF service ([8ebdb20](https://github.com/BrewInteractive/export-service-nestjs/commit/8ebdb209a1236d33a7b3487d0473550b35cc0fc8)) 17 | * create api key guard ([f2a47d8](https://github.com/BrewInteractive/export-service-nestjs/commit/f2a47d837c69b3ba9d91c4c44491a2bef3b15367)) 18 | * create release action ([7358d7e](https://github.com/BrewInteractive/export-service-nestjs/commit/7358d7e7a68ee15a1bfea7dffcd38c9e33ea2c7b)) 19 | * create service for convert HTML to PDF ([f47f8ab](https://github.com/BrewInteractive/export-service-nestjs/commit/f47f8ab3f3f638d5b51b59738c2058e85121e7d3)) 20 | * nestjs setup ([baf3b68](https://github.com/BrewInteractive/export-service-nestjs/commit/baf3b68c9a76b26e36b5b878b3ce69c63a197c62)) 21 | * project working port dynamic make ([eb7fc75](https://github.com/BrewInteractive/export-service-nestjs/commit/eb7fc7565e57a253fc3027a8e4240796d52a5680)) 22 | * swagger setup for nest app ([f967e35](https://github.com/BrewInteractive/export-service-nestjs/commit/f967e35f54c005a4a37bc627a163bb504f7b213f)) 23 | 24 | ## 1.0.0 (2022-08-08) 25 | 26 | 27 | ### Features 28 | 29 | * add body size limit property ([23ebb1b](https://github.com/BrewInteractive/export-service-nestjs/commit/23ebb1b34ce9c31c2a6bae2d5139987233543a37)) 30 | * add cors configuration ([f09e067](https://github.com/BrewInteractive/export-service-nestjs/commit/f09e06734092f8babcaa3ed484f8fd7455d70a24)) 31 | * add default values for swagger ([12ea1b2](https://github.com/BrewInteractive/export-service-nestjs/commit/12ea1b287f9eb0888664fdd6d163ab19320a05d4)) 32 | * add global prefix pathname property ([58d741c](https://github.com/BrewInteractive/export-service-nestjs/commit/58d741c62f0976e7a8a9eb89d9a3690dc612080a)) 33 | * create api key guard ([f2a47d8](https://github.com/BrewInteractive/export-service-nestjs/commit/f2a47d837c69b3ba9d91c4c44491a2bef3b15367)) 34 | * create release action ([7358d7e](https://github.com/BrewInteractive/export-service-nestjs/commit/7358d7e7a68ee15a1bfea7dffcd38c9e33ea2c7b)) 35 | * create service for convert HTML to PDF ([f47f8ab](https://github.com/BrewInteractive/export-service-nestjs/commit/f47f8ab3f3f638d5b51b59738c2058e85121e7d3)) 36 | * nestjs setup ([baf3b68](https://github.com/BrewInteractive/export-service-nestjs/commit/baf3b68c9a76b26e36b5b878b3ce69c63a197c62)) 37 | * project working port dynamic make ([eb7fc75](https://github.com/BrewInteractive/export-service-nestjs/commit/eb7fc7565e57a253fc3027a8e4240796d52a5680)) 38 | * swagger setup for nest app ([f967e35](https://github.com/BrewInteractive/export-service-nestjs/commit/f967e35f54c005a4a37bc627a163bb504f7b213f)) 39 | -------------------------------------------------------------------------------- /test/html-to-pdf.e2e-spec.ts: -------------------------------------------------------------------------------- 1 | import { Test, TestingModule } from '@nestjs/testing'; 2 | import { INestApplication, ValidationPipe } from '@nestjs/common'; 3 | import * as request from 'supertest'; 4 | import { AppModule } from './../src/app.module'; 5 | 6 | describe('Html To Pdf (e2e)', () => { 7 | let app: INestApplication; 8 | 9 | beforeEach(async () => { 10 | const moduleFixture: TestingModule = await Test.createTestingModule({ 11 | imports: [AppModule], 12 | }).compile(); 13 | 14 | app = moduleFixture.createNestApplication(); 15 | app.useGlobalPipes(new ValidationPipe()); 16 | await app.init(); 17 | }); 18 | 19 | it('When the body is sent empty, it should give BadRequest error.', () => { 20 | return request(app.getHttpServer()) 21 | .post('/html-to-pdf') 22 | .expect(400) 23 | .expect({ 24 | statusCode: 400, 25 | message: [ 26 | 'fileName should not be null or undefined', 27 | 'fileName must match ^[a-zA-Z0-9_-]*$ regular expression', 28 | 'fileName must be a string', 29 | 'fileName should not be empty', 30 | 'type should not be null or undefined', 31 | 'type must be a valid enum value', 32 | 'type must be a string', 33 | 'type should not be empty', 34 | 'format should not be null or undefined', 35 | 'format must be a valid enum value', 36 | 'format must be a string', 37 | 'responseType should not be null or undefined', 38 | 'responseType must be a valid enum value', 39 | 'responseType must be a string', 40 | ], 41 | error: 'Bad Request', 42 | }); 43 | }); 44 | 45 | it('The sent Website Url must be converted to PDF. Response type STREAM.', () => { 46 | return request(app.getHttpServer()) 47 | .post('/html-to-pdf') 48 | .send({ 49 | url: 'https://google.com.tr', 50 | type: 'url', 51 | fileName: 'hello-word', 52 | format: 'a4', 53 | responseType: 'stream', 54 | }) 55 | .expect(201) 56 | .expect('Content-Type', 'application/pdf'); 57 | }); 58 | 59 | it('The sent HTML must be converted to PDF. Response type STREAM.', () => { 60 | return request(app.getHttpServer()) 61 | .post('/html-to-pdf') 62 | .send({ 63 | html: '

Hello Word

', 64 | type: 'html', 65 | fileName: 'hello-word', 66 | format: 'a4', 67 | responseType: 'stream', 68 | }) 69 | .expect(201) 70 | .expect('Content-Type', 'application/pdf'); 71 | }); 72 | 73 | it('The sent Website Url must be converted to PDF. Response type BASE64.', () => { 74 | return request(app.getHttpServer()) 75 | .post('/html-to-pdf') 76 | .send({ 77 | url: 'https://google.com.tr', 78 | type: 'url', 79 | fileName: 'hello-word', 80 | format: 'a4', 81 | responseType: 'base64', 82 | }) 83 | .expect(201) 84 | .expect('Content-Type', 'application/json') 85 | .then((response) => { 86 | console.log(response.body.file); 87 | expect(response.body).toHaveProperty('file'); 88 | expect(response.body.file).not.toEqual(null); 89 | expect(response.body.file).not.toEqual(undefined); 90 | expect(response.body.file).not.toEqual(''); 91 | }); 92 | }); 93 | 94 | it('The sent HTML must be converted to PDF. Response type BASE64.', () => { 95 | return request(app.getHttpServer()) 96 | .post('/html-to-pdf') 97 | .send({ 98 | html: '

Hello Word

', 99 | type: 'html', 100 | fileName: 'hello-word', 101 | format: 'a4', 102 | responseType: 'base64', 103 | }) 104 | .expect(201) 105 | .expect('Content-Type', 'application/json') 106 | .then((response) => { 107 | expect(response.body).toHaveProperty('file'); 108 | expect(response.body.file).not.toEqual(null); 109 | expect(response.body.file).not.toEqual(undefined); 110 | expect(response.body.file).not.toEqual(''); 111 | }); 112 | }); 113 | }); 114 | --------------------------------------------------------------------------------