diff --git a/api/docker-compose.yml b/api/docker-compose.yml index 1226ca8a..74d46d07 100644 --- a/api/docker-compose.yml +++ b/api/docker-compose.yml @@ -1,7 +1,7 @@ version: '2' services: - templaterDb: - container_name: templaterDb + templaterdb: + container_name: templaterdb image: postgres:12 ports: - '${POSTGRES_HOST}:5432' @@ -13,25 +13,25 @@ services: volumes: - ./pgdata:/var/lib/postgresql/data - # gql: - # image: ${HASURA_VERSION} + gql: + image: ${HASURA_VERSION} + ports: + - '10022:8080' + depends_on: + - templaterdb + restart: always + environment: + HASURA_GRAPHQL_DATABASE_URL: postgres://${DB_USERNAME}:${DB_PASSWORD}@templaterdb:5432/${DB_NAME} + HASURA_GRAPHQL_ENABLE_CONSOLE: 'true' # set to "false" to disable console + HASURA_GRAPHQL_ENABLED_LOG_TYPES: startup, http-log, webhook-log, websocket-log, query-log + HASURA_GRAPHQL_ADMIN_SECRET: ${HASURA_ADMIN_PASS} + + # templater: + # image: samagragovernance/templater:latest # ports: - # - '10022:8080' + # - '${PORT}:3000' # depends_on: # - templaterdb # restart: always # environment: - # HASURA_GRAPHQL_DATABASE_URL: postgres://${DB_USERNAME}:${DB_PASSWORD}@templaterdb:5432/${DB_NAME} - # HASURA_GRAPHQL_ENABLE_CONSOLE: 'true' # set to "false" to disable console - # HASURA_GRAPHQL_ENABLED_LOG_TYPES: startup, http-log, webhook-log, websocket-log, query-log - # HASURA_GRAPHQL_ADMIN_SECRET: ${HASURA_ADMIN_PASS} - - templater: - image: samagragovernance/templater:latest - ports: - - '${PORT}:3000' - depends_on: - - templaterDb - restart: always - environment: - DATABASE_URL: ${DATABASE_URL} + # DATABASE_URL: ${DATABASE_URL} diff --git a/api/package.json b/api/package.json index 3d272182..d904dd33 100644 --- a/api/package.json +++ b/api/package.json @@ -5,6 +5,8 @@ "author": "", "private": true, "license": "UNLICENSED", + "main": "dist/index.js", + "typings": "dist/index.d.ts", "scripts": { "prebuild": "rimraf dist", "build": "nest build", @@ -34,7 +36,7 @@ "@nestjs/platform-fastify": "^8.2.6", "@nestjs/swagger": "^5.1.5", "@nestjs/terminus": "^8", - "@prisma/client": "3.5.0", + "@prisma/client": "^4.16.2", "@types/nunjucks": "^3.2.1", "ejs": "^3.1.6", "fastify-cors": "^6.0.3", @@ -43,13 +45,16 @@ "nunjucks": "^3.2.3", "passport-google-oauth20": "^2.0.0", "passport-jwt": "^4.0.0", - "prisma": "3.5.0", + "prisma": "^4.16.2", "reflect-metadata": "^0.1.13", "rimraf": "^3.0.2", "rxjs": "^7.2.0", "swagger-ui-express": "^4.1.6", "vm2": "^3.9.7" }, + "peerDependencies": { + "@nestjs/common": "^9.0.0" + }, "devDependencies": { "@compodoc/compodoc": "^1.1.18", "@nestjs/cli": "^8.0.0", diff --git a/api/prisma/migrations/20230702111112_init/migration.sql b/api/prisma/migrations/20230702111112_init/migration.sql new file mode 100644 index 00000000..7c7e6176 --- /dev/null +++ b/api/prisma/migrations/20230702111112_init/migration.sql @@ -0,0 +1,2 @@ +-- CreateIndex +CREATE INDEX "Template_tag_idx" ON "Template"("tag"); diff --git a/api/src/app.module.ts b/api/src/app.module.ts index 02fe9bc9..85a2a093 100644 --- a/api/src/app.module.ts +++ b/api/src/app.module.ts @@ -16,6 +16,8 @@ import { SingletonServiceModule } from './singletonService.module'; import { I18nController } from './core/i18n/i18n/i18n.controller'; import { SearchController } from './core/search/search.controller'; import { HealthModule } from './health/health.module'; +import { RenderController } from './core/render/render.controller'; +import { RenderService } from './core/render/render.service'; @Module({ imports: [SingletonServiceModule, HealthModule], @@ -26,6 +28,7 @@ import { HealthModule } from './health/health.module'; LambdaService, I18nController, SearchController, + RenderController, ], exports: [VMService], providers: [ @@ -39,6 +42,7 @@ import { HealthModule } from './health/health.module'; JinjaService, EjsService, VMService, + RenderService, ], }) export class AppModule {} diff --git a/api/src/core/render/render.controller.ts b/api/src/core/render/render.controller.ts new file mode 100644 index 00000000..8116f337 --- /dev/null +++ b/api/src/core/render/render.controller.ts @@ -0,0 +1,13 @@ +import { Body, Controller, Post } from '@nestjs/common'; +import { RenderService } from './render.service'; +import { RenderRes, RenderTestDTO } from './types'; + +@Controller('render') +export class RenderController { + constructor(private render: RenderService) {} + + @Post('/test') + async test(@Body() data: RenderTestDTO): Promise { + return this.render.renderTemplateTest(data); + } +} diff --git a/api/src/core/render/render.module.ts b/api/src/core/render/render.module.ts new file mode 100644 index 00000000..e31cfb4d --- /dev/null +++ b/api/src/core/render/render.module.ts @@ -0,0 +1,10 @@ +import { Module } from '@nestjs/common'; +import { RenderService } from './render.service'; +import { EngineModule } from '../../engines/engine.module'; + +@Module({ + imports: [EngineModule], + providers: [RenderService], + exports: [RenderService], +}) +export class RenderModule {} diff --git a/api/src/core/render/render.service.spec.ts b/api/src/core/render/render.service.spec.ts new file mode 100644 index 00000000..b5862746 --- /dev/null +++ b/api/src/core/render/render.service.spec.ts @@ -0,0 +1,18 @@ +import { Test, TestingModule } from '@nestjs/testing'; +import { RenderService } from './render.service'; + +describe('RenderService', () => { + let service: RenderService; + + beforeEach(async () => { + const module: TestingModule = await Test.createTestingModule({ + providers: [RenderService], + }).compile(); + + service = module.get(RenderService); + }); + + it('should be defined', () => { + expect(service).toBeDefined(); + }); +}); diff --git a/api/src/core/render/render.service.ts b/api/src/core/render/render.service.ts new file mode 100644 index 00000000..08ab8860 --- /dev/null +++ b/api/src/core/render/render.service.ts @@ -0,0 +1,118 @@ +import { Injectable } from '@nestjs/common'; +import { JinjaService } from '../../engines/jinja/jinja.service'; +import { JsTLService } from '../../engines/jstl/jstl.service'; +import { EjsService } from '../../engines/ejs/ejs.service'; +import * as types from './types'; + +@Injectable() +export class RenderService { + constructor( + private jinjaService: JinjaService, + private jstlService: JsTLService, + private ejsService: EjsService, + ) {} + + test() { + return 'test'; + } + + async renderTemplate(data: types.RenderReq): Promise { + const template = data.template; + const transformedData = data.data; + let processed; + switch (data.engineType) { + case types.TemplateType.JINJA: + processed = this.jinjaService.render(template.content, transformedData); + break; + + case types.TemplateType.JSTL: + processed = this.jstlService.render(template.content, transformedData); + break; + + case types.TemplateType.EJS: + processed = this.ejsService.render(template.content, transformedData); + break; + default: + throw 'Templates without template types not allowed'; + } + return { + processed, + templateType: data.engineType, + data: data.data, + templateBody: template.content, + meta: 'meta', + }; + } + + renderTemplateTest(data: types.RenderTestDTO): types.RenderRes { + let processed; + let transformedData; + try { + transformedData = JSON.parse(data.sampleData); + } catch (e) { + transformedData = data.sampleData; + } + switch (data.type) { + case types.TemplateType.JINJA: + processed = this.jinjaService.render(data.body, transformedData); + + case types.TemplateType.JSTL: + processed = this.jstlService.render(data.body, transformedData); + break; + + case types.TemplateType.EJS: + processed = this.ejsService.render(data.body, transformedData); + break; + default: + throw 'Templates without template types not allowed'; + } + return { + processed, + templateType: types.TemplateType.JSTL, + data: data.sampleData, + templateBody: data.body, + }; + } + + renderTemplateManyTest(data: types.RenderTestDTO): types.RenderRes { + const processed = []; + let transformedData; + try { + transformedData = JSON.parse(data.sampleData); + } catch (e) { + transformedData = data.sampleData; + } + for (let i = 0; i < transformedData.length; i++) { + switch (data.type) { + case types.TemplateType.JINJA: + processed.push({ + __index: transformedData[i].__index, + body: this.jinjaService.render(data.body, transformedData[i]), + }); + + case types.TemplateType.JSTL: + processed.push({ + __index: transformedData[i].__index, + body: this.jstlService.render(data.body, transformedData[i]), + }); + + break; + + case types.TemplateType.EJS: + processed.push({ + __index: transformedData[i].__index, + body: this.ejsService.render(data.body, transformedData[i]), + }); + break; + default: + throw 'Templates without template types not allowed'; + } + } + return { + processed, + templateType: types.TemplateType.JSTL, + data: data.sampleData, + templateBody: data.body, + }; + } +} diff --git a/api/src/core/render/types.ts b/api/src/core/render/types.ts new file mode 100644 index 00000000..2c4b930f --- /dev/null +++ b/api/src/core/render/types.ts @@ -0,0 +1,28 @@ +export enum TemplateType { + JSTL = 'JSTL', + EJS = 'EJS', + JINJA = 'JINJA', +} +export interface Template { + content: string; +} + +export interface RenderReq { + template: Template; + data: string | any; + engineType: string; +} + +export interface RenderRes { + processed: string | string[]; + templateType: TemplateType; + data: any; + templateBody: string; + meta?: any; +} + +export interface RenderTestDTO { + sampleData: any; + body: string; + type: TemplateType; +} diff --git a/api/src/core/template/template.controller.ts b/api/src/core/template/template.controller.ts index 51fb9cd8..407c1203 100644 --- a/api/src/core/template/template.controller.ts +++ b/api/src/core/template/template.controller.ts @@ -162,4 +162,4 @@ export class TemplateController { async getTemplate(@Param('id') id: string): Promise