diff --git a/.gitignore b/.gitignore index 6584902..43f83f7 100644 --- a/.gitignore +++ b/.gitignore @@ -1,7 +1,7 @@ # Node.js Tools for Visual Studio .ntvs_analysis.dat node_modules/ -nestserver-*.exe +nestserver-* .vscode dist/ yarn-error.log diff --git a/cobigen-nest-server/.npmignore b/cobigen-nest-server/.npmignore index 7ecacc1..46d8078 100644 --- a/cobigen-nest-server/.npmignore +++ b/cobigen-nest-server/.npmignore @@ -3,5 +3,4 @@ node_modules/ .vscode dist/ -nestserver-linux-1.0.7 -nestserver-macos-1.0.7 \ No newline at end of file +nestserver-* \ No newline at end of file diff --git a/cobigen-nest-server/.prettierrc b/cobigen-nest-server/linux/.prettierrc similarity index 100% rename from cobigen-nest-server/.prettierrc rename to cobigen-nest-server/linux/.prettierrc diff --git a/cobigen-nest-server/ExternalProcessContract.yml b/cobigen-nest-server/linux/ExternalProcessContract.yml similarity index 100% rename from cobigen-nest-server/ExternalProcessContract.yml rename to cobigen-nest-server/linux/ExternalProcessContract.yml diff --git a/cobigen-nest-server/nest-cli.json b/cobigen-nest-server/linux/nest-cli.json similarity index 100% rename from cobigen-nest-server/nest-cli.json rename to cobigen-nest-server/linux/nest-cli.json diff --git a/cobigen-nest-server/nodemon-debug.json b/cobigen-nest-server/linux/nodemon-debug.json similarity index 100% rename from cobigen-nest-server/nodemon-debug.json rename to cobigen-nest-server/linux/nodemon-debug.json diff --git a/cobigen-nest-server/nodemon.json b/cobigen-nest-server/linux/nodemon.json similarity index 100% rename from cobigen-nest-server/nodemon.json rename to cobigen-nest-server/linux/nodemon.json diff --git a/cobigen-nest-server/linux/package.json b/cobigen-nest-server/linux/package.json new file mode 100644 index 0000000..ead1f86 --- /dev/null +++ b/cobigen-nest-server/linux/package.json @@ -0,0 +1,64 @@ +{ + "name": "@devonfw/cobigen-nestserver-linux", + "bin": "dist/main.js", + "version": "1.2.0", + "description": "Nestjs server to implement the input reader and merger for CobiGen", + "author": "CobiGen Team", + "license": "MIT", + "scripts":{ + "build": "tsc -p tsconfig.build.json", + "format": "prettier --write \"src/**/*.ts\"", + "start": "ts-node -r tsconfig-paths/register src/main.ts", + "start:dev": "nodemon", + "start:debug": "nodemon --config nodemon-debug.json", + "prestart:prod": "rimraf dist && npm run build", + "start:prod": "node dist/main.js", + "lint": "tslint -p tsconfig.json -c tslint.json", + "test": "jest", + "test:watch": "jest --watch", + "test:cov": "jest --coverage", + "test:debug": "node --inspect-brk -r tsconfig-paths/register -r ts-node/register node_modules/.bin/jest --runInBand", + "test:e2e": "jest --config ./test/jest-e2e.json" + }, + "dependencies": { + "@devonfw/ts-merger": "^2.2.10", + "@nestjs/common": "^5.4.0", + "@nestjs/core": "^5.4.0", + "js-beautify": "^1.9.0", + "pkg": "^4.4.9", + "reflect-metadata": "^0.1.12", + "rimraf": "^2.6.2", + "rxjs": "^6.2.2", + "typescript": "^3.0.1" + }, + "devDependencies": { + "@nestjs/testing": "^5.1.0", + "@types/express": "^4.16.0", + "@types/jest": "^23.3.1", + "@types/node": "^10.7.1", + "@types/supertest": "^2.0.5", + "jest": "^23.5.0", + "nodemon": "^1.18.3", + "prettier": "^1.14.2", + "supertest": "^3.1.0", + "ts-jest": "^23.1.3", + "ts-loader": "^4.4.2", + "ts-node": "^7.0.1", + "tsconfig-paths": "^3.5.0", + "tslint": "5.11.0" + }, + "jest": { + "moduleFileExtensions": [ + "js", + "json", + "ts" + ], + "rootDir": "src", + "testRegex": ".spec.ts$", + "transform": { + "^.+\\.(t|j)s$": "ts-jest" + }, + "coverageDirectory": "../coverage", + "testEnvironment": "node" + } +} diff --git a/cobigen-nest-server/src/app.controller.spec.ts b/cobigen-nest-server/linux/src/app.controller.spec.ts similarity index 100% rename from cobigen-nest-server/src/app.controller.spec.ts rename to cobigen-nest-server/linux/src/app.controller.spec.ts diff --git a/cobigen-nest-server/src/app.controller.ts b/cobigen-nest-server/linux/src/app.controller.ts similarity index 100% rename from cobigen-nest-server/src/app.controller.ts rename to cobigen-nest-server/linux/src/app.controller.ts diff --git a/cobigen-nest-server/src/app.module.ts b/cobigen-nest-server/linux/src/app.module.ts similarity index 100% rename from cobigen-nest-server/src/app.module.ts rename to cobigen-nest-server/linux/src/app.module.ts diff --git a/cobigen-nest-server/src/app.service.ts b/cobigen-nest-server/linux/src/app.service.ts similarity index 100% rename from cobigen-nest-server/src/app.service.ts rename to cobigen-nest-server/linux/src/app.service.ts diff --git a/cobigen-nest-server/src/config/properties.ts b/cobigen-nest-server/linux/src/config/properties.ts similarity index 100% rename from cobigen-nest-server/src/config/properties.ts rename to cobigen-nest-server/linux/src/config/properties.ts diff --git a/cobigen-nest-server/src/main.ts b/cobigen-nest-server/linux/src/main.ts similarity index 100% rename from cobigen-nest-server/src/main.ts rename to cobigen-nest-server/linux/src/main.ts diff --git a/cobigen-nest-server/src/processmanagement/etos/input-file.eto.ts b/cobigen-nest-server/linux/src/processmanagement/etos/input-file.eto.ts similarity index 100% rename from cobigen-nest-server/src/processmanagement/etos/input-file.eto.ts rename to cobigen-nest-server/linux/src/processmanagement/etos/input-file.eto.ts diff --git a/cobigen-nest-server/src/processmanagement/etos/merger.eto.ts b/cobigen-nest-server/linux/src/processmanagement/etos/merger.eto.ts similarity index 100% rename from cobigen-nest-server/src/processmanagement/etos/merger.eto.ts rename to cobigen-nest-server/linux/src/processmanagement/etos/merger.eto.ts diff --git a/cobigen-nest-server/src/processmanagement/etos/model.eto.ts b/cobigen-nest-server/linux/src/processmanagement/etos/model.eto.ts similarity index 100% rename from cobigen-nest-server/src/processmanagement/etos/model.eto.ts rename to cobigen-nest-server/linux/src/processmanagement/etos/model.eto.ts diff --git a/cobigen-nest-server/src/processmanagement/inputreader/input-reader.ts b/cobigen-nest-server/linux/src/processmanagement/inputreader/input-reader.ts similarity index 98% rename from cobigen-nest-server/src/processmanagement/inputreader/input-reader.ts rename to cobigen-nest-server/linux/src/processmanagement/inputreader/input-reader.ts index 70ee361..8d4ff91 100644 --- a/cobigen-nest-server/src/processmanagement/inputreader/input-reader.ts +++ b/cobigen-nest-server/linux/src/processmanagement/inputreader/input-reader.ts @@ -1,10 +1,11 @@ import { ModelEto } from './../etos/model.eto'; import { InputFileEto } from '../etos/input-file.eto'; +import * as tsm from '@devonfw/ts-merger'; + export class InputReader { readonly content; public async getInputObjects(inputFile:InputFileEto, removeEmptyFields : boolean) { - const tsm = require('@devonfw/ts-merger'); const parsedFile = tsm.readFile(inputFile.content); // Extending the model diff --git a/cobigen-nest-server/src/processmanagement/processmanagement.controller.spec.ts b/cobigen-nest-server/linux/src/processmanagement/processmanagement.controller.spec.ts similarity index 100% rename from cobigen-nest-server/src/processmanagement/processmanagement.controller.spec.ts rename to cobigen-nest-server/linux/src/processmanagement/processmanagement.controller.spec.ts diff --git a/cobigen-nest-server/src/processmanagement/processmanagement.controller.ts b/cobigen-nest-server/linux/src/processmanagement/processmanagement.controller.ts similarity index 97% rename from cobigen-nest-server/src/processmanagement/processmanagement.controller.ts rename to cobigen-nest-server/linux/src/processmanagement/processmanagement.controller.ts index bdf06cb..7c21aab 100644 --- a/cobigen-nest-server/src/processmanagement/processmanagement.controller.ts +++ b/cobigen-nest-server/linux/src/processmanagement/processmanagement.controller.ts @@ -2,6 +2,7 @@ import { Controller, Get, Post, Body } from '@nestjs/common'; import { InputFileEto } from './etos/input-file.eto'; import { MergerEto } from './etos/merger.eto'; import { InputReader } from './inputreader/input-reader'; +import * as tsm from '@devonfw/ts-merger'; const config = require('../../package.json'); @@ -39,7 +40,6 @@ export class ProcessmanagementController { const baseContent: string = merger.baseContent.replace(/\\n/gm, '\n'); const patchOverrides: boolean = merger.patchOverrides; - const tsm = require('@devonfw/ts-merger'); const mergedCode: string = tsm.merge( baseContent, patchContent, diff --git a/cobigen-nest-server/test/beautifyrequest.e2e-spec.ts b/cobigen-nest-server/linux/test/beautifyrequest.e2e-spec.ts similarity index 100% rename from cobigen-nest-server/test/beautifyrequest.e2e-spec.ts rename to cobigen-nest-server/linux/test/beautifyrequest.e2e-spec.ts diff --git a/cobigen-nest-server/test/inputreadingrequest.e2e-spec.ts b/cobigen-nest-server/linux/test/inputreadingrequest.e2e-spec.ts similarity index 100% rename from cobigen-nest-server/test/inputreadingrequest.e2e-spec.ts rename to cobigen-nest-server/linux/test/inputreadingrequest.e2e-spec.ts diff --git a/cobigen-nest-server/test/jest-e2e.json b/cobigen-nest-server/linux/test/jest-e2e.json similarity index 100% rename from cobigen-nest-server/test/jest-e2e.json rename to cobigen-nest-server/linux/test/jest-e2e.json diff --git a/cobigen-nest-server/test/mergerequest.e2e-spec.ts b/cobigen-nest-server/linux/test/mergerequest.e2e-spec.ts similarity index 100% rename from cobigen-nest-server/test/mergerequest.e2e-spec.ts rename to cobigen-nest-server/linux/test/mergerequest.e2e-spec.ts diff --git a/cobigen-nest-server/test/simplerequests.e2e-spec.ts b/cobigen-nest-server/linux/test/simplerequests.e2e-spec.ts similarity index 100% rename from cobigen-nest-server/test/simplerequests.e2e-spec.ts rename to cobigen-nest-server/linux/test/simplerequests.e2e-spec.ts diff --git a/cobigen-nest-server/tsconfig.build.json b/cobigen-nest-server/linux/tsconfig.build.json similarity index 100% rename from cobigen-nest-server/tsconfig.build.json rename to cobigen-nest-server/linux/tsconfig.build.json diff --git a/cobigen-nest-server/tsconfig.json b/cobigen-nest-server/linux/tsconfig.json similarity index 100% rename from cobigen-nest-server/tsconfig.json rename to cobigen-nest-server/linux/tsconfig.json diff --git a/cobigen-nest-server/tslint.json b/cobigen-nest-server/linux/tslint.json similarity index 100% rename from cobigen-nest-server/tslint.json rename to cobigen-nest-server/linux/tslint.json diff --git a/cobigen-nest-server/macos/.prettierrc b/cobigen-nest-server/macos/.prettierrc new file mode 100644 index 0000000..dcb7279 --- /dev/null +++ b/cobigen-nest-server/macos/.prettierrc @@ -0,0 +1,4 @@ +{ + "singleQuote": true, + "trailingComma": "all" +} \ No newline at end of file diff --git a/cobigen-nest-server/macos/ExternalProcessContract.yml b/cobigen-nest-server/macos/ExternalProcessContract.yml new file mode 100644 index 0000000..e6ef89b --- /dev/null +++ b/cobigen-nest-server/macos/ExternalProcessContract.yml @@ -0,0 +1,175 @@ +openapi: 3.0.0 +servers: + - url: 'https://localhost:5000/' + description: nest server +info: + title: External process communication + description: Specification of communication between external process and CobiGen + version: 1.0.0 + x-rootpackage: com.devonfw.poc + +paths: + /processmanagement/tsplugin/isConnectionReady/: + get: + description: Needed for CobiGen to know that the server is ready to receive requests. Also we need it to differentiate plug-ins. + responses: + '200': + description: Returns server version + content: + text/plain: + schema: + type: string + + /processmanagement/tsplugin/isValidInput/: + post: + description: Returns true or false whether it is a valid input for generation. It can be a simple file extension check or more complex logic. + requestBody: + $ref: '#/components/requestBodies/InputFile' + operationId: isValidInput + responses: + '200': + description: Returns true or false whether it is a valid input for generation + content: + text/plain: + schema: + type: boolean + '404': + description: Not found + + /processmanagement/tsplugin/getInputModel/: + post: + description: Requesting a key-value model of the input file in JSON format. + requestBody: + $ref: '#/components/requestBodies/InputFile' + operationId: getInputObjects + responses: + '200': + description: Returns a model in JSON format + content: + application/json: + schema: + $ref: '#/components/schemas/Model' + '404': + description: Not found + + /processmanagement/tsplugin/merge/: + post: + description: Merges two files and returns the result + requestBody: + $ref: '#/components/requestBodies/Merger' + operationId: merge + responses: + '200': + description: Returns merge file as a string + content: + text/plain: + schema: + type: string + '404': + description: Not found + +components: + schemas: + Model: + x-component: processmanagement + description: Data model of the input file + type: object + properties: + path: + type: string + description: Input file path. It can also be just the file name. + uniqueItems: true + inputObject: + $ref: '#/components/schemas/InputObject' + InputObject: + x-component: processmanagement + description: Input object containing needed information for the templates + type: object + properties: + Fields: + $ref: '#/components/schemas/Fields' + Imports: + $ref: '#/components/schemas/Imports' + Fields: + x-component: processmanagement + description: Not really needed, just used as an example for the model. + type: array + items: + type: object + properties: + Field: + $ref: '#/components/schemas/Field' + Field: + x-component: processmanagement + description: Not really needed, just used as an example for the model. + type: object + properties: + fieldName: + type: string + fieldType: + type: string + Imports: + x-component: processmanagement + description: Not really needed, just used as an example for the model. + type: array + items: + type: object + properties: + Import: + $ref: '#/components/schemas/Import' + Import: + x-component: processmanagement + description: Not really needed, just used as an example for the model. + type: object + properties: + importValue: + type: string + InputFile: + x-component: processmanagement + description: Input file + type: object + properties: + filename: + type: string + content: + type: string + charset: + type: string + Merger: + x-component: processmanagement + description: Merger properties. baseContent is the current user's file. patchContent is CobiGen freshly generated file. patchOverrides is set to true when in case of conflict, we want to override using patch. + type: object + properties: + baseContent: + type: string + patchContent: + type: string + patchOverrides: + type: boolean + + + requestBodies: + Model: + description: Data model of the file in JSON format + content: + application/json: + schema: + $ref: '#/components/schemas/Model' + required: true + + InputFile: + description: Input file for CobiGen to parse + content: + application/json: + schema: + $ref: '#/components/schemas/InputFile' + required: true + + Merger: + description: baseContent is the current user's file. patchContent is CobiGen freshly generated file. patchOverrides is set to true when in case of conflict, we want to override using patch. + content: + application/json: + schema: + $ref: '#/components/schemas/Merger' + required: true + diff --git a/cobigen-nest-server/macos/nest-cli.json b/cobigen-nest-server/macos/nest-cli.json new file mode 100644 index 0000000..0dde5f8 --- /dev/null +++ b/cobigen-nest-server/macos/nest-cli.json @@ -0,0 +1,5 @@ +{ + "language": "ts", + "collection": "@nestjs/schematics", + "sourceRoot": "src" +} diff --git a/cobigen-nest-server/macos/nodemon-debug.json b/cobigen-nest-server/macos/nodemon-debug.json new file mode 100644 index 0000000..052b023 --- /dev/null +++ b/cobigen-nest-server/macos/nodemon-debug.json @@ -0,0 +1,6 @@ +{ + "watch": ["src"], + "ext": "ts", + "ignore": ["src/**/*.spec.ts"], + "exec": "node --inspect-brk -r ts-node/register -r tsconfig-paths/register src/main.ts" +} diff --git a/cobigen-nest-server/macos/nodemon.json b/cobigen-nest-server/macos/nodemon.json new file mode 100644 index 0000000..583bb42 --- /dev/null +++ b/cobigen-nest-server/macos/nodemon.json @@ -0,0 +1,6 @@ +{ + "watch": ["src"], + "ext": "ts", + "ignore": ["src/**/*.spec.ts"], + "exec": "ts-node -r tsconfig-paths/register src/main.ts" +} diff --git a/cobigen-nest-server/macos/package.json b/cobigen-nest-server/macos/package.json new file mode 100644 index 0000000..32057b0 --- /dev/null +++ b/cobigen-nest-server/macos/package.json @@ -0,0 +1,64 @@ +{ + "name": "@devonfw/cobigen-nestserver-macos", + "bin": "dist/main.js", + "version": "1.2.0", + "description": "Nestjs server to implement the input reader and merger for CobiGen", + "author": "CobiGen Team", + "license": "MIT", + "scripts":{ + "build": "tsc -p tsconfig.build.json", + "format": "prettier --write \"src/**/*.ts\"", + "start": "ts-node -r tsconfig-paths/register src/main.ts", + "start:dev": "nodemon", + "start:debug": "nodemon --config nodemon-debug.json", + "prestart:prod": "rimraf dist && npm run build", + "start:prod": "node dist/main.js", + "lint": "tslint -p tsconfig.json -c tslint.json", + "test": "jest", + "test:watch": "jest --watch", + "test:cov": "jest --coverage", + "test:debug": "node --inspect-brk -r tsconfig-paths/register -r ts-node/register node_modules/.bin/jest --runInBand", + "test:e2e": "jest --config ./test/jest-e2e.json" + }, + "dependencies": { + "@devonfw/ts-merger": "^2.2.10", + "@nestjs/common": "^5.4.0", + "@nestjs/core": "^5.4.0", + "js-beautify": "^1.9.0", + "pkg": "^4.4.9", + "reflect-metadata": "^0.1.12", + "rimraf": "^2.6.2", + "rxjs": "^6.2.2", + "typescript": "^3.0.1" + }, + "devDependencies": { + "@nestjs/testing": "^5.1.0", + "@types/express": "^4.16.0", + "@types/jest": "^23.3.1", + "@types/node": "^10.7.1", + "@types/supertest": "^2.0.5", + "jest": "^23.5.0", + "nodemon": "^1.18.3", + "prettier": "^1.14.2", + "supertest": "^3.1.0", + "ts-jest": "^23.1.3", + "ts-loader": "^4.4.2", + "ts-node": "^7.0.1", + "tsconfig-paths": "^3.5.0", + "tslint": "5.11.0" + }, + "jest": { + "moduleFileExtensions": [ + "js", + "json", + "ts" + ], + "rootDir": "src", + "testRegex": ".spec.ts$", + "transform": { + "^.+\\.(t|j)s$": "ts-jest" + }, + "coverageDirectory": "../coverage", + "testEnvironment": "node" + } +} diff --git a/cobigen-nest-server/macos/src/app.controller.spec.ts b/cobigen-nest-server/macos/src/app.controller.spec.ts new file mode 100644 index 0000000..d22f389 --- /dev/null +++ b/cobigen-nest-server/macos/src/app.controller.spec.ts @@ -0,0 +1,22 @@ +import { Test, TestingModule } from '@nestjs/testing'; +import { AppController } from './app.controller'; +import { AppService } from './app.service'; + +describe('AppController', () => { + let appController: AppController; + + beforeEach(async () => { + const app: TestingModule = await Test.createTestingModule({ + controllers: [AppController], + providers: [AppService], + }).compile(); + + appController = app.get(AppController); + }); + + describe('root', () => { + it('should return "Hello World!"', () => { + expect(appController.getHello()).toBe('Hello World!'); + }); + }); +}); diff --git a/cobigen-nest-server/macos/src/app.controller.ts b/cobigen-nest-server/macos/src/app.controller.ts new file mode 100644 index 0000000..cce879e --- /dev/null +++ b/cobigen-nest-server/macos/src/app.controller.ts @@ -0,0 +1,12 @@ +import { Controller, Get } from '@nestjs/common'; +import { AppService } from './app.service'; + +@Controller() +export class AppController { + constructor(private readonly appService: AppService) {} + + @Get() + getHello(): string { + return this.appService.getHello(); + } +} diff --git a/cobigen-nest-server/macos/src/app.module.ts b/cobigen-nest-server/macos/src/app.module.ts new file mode 100644 index 0000000..4a957bd --- /dev/null +++ b/cobigen-nest-server/macos/src/app.module.ts @@ -0,0 +1,16 @@ +import { Module, NestModule, MiddlewareConsumer } from '@nestjs/common'; +import { AppController } from './app.controller'; +import { AppService } from './app.service'; +import { ProcessmanagementController } from './processmanagement/processmanagement.controller'; +import { resetTimeout } from './main'; + +@Module({ + imports: [], + controllers: [AppController, ProcessmanagementController], + providers: [AppService], +}) +export class AppModule implements NestModule { + configure(consumer: MiddlewareConsumer) { + consumer.apply(resetTimeout).forRoutes(ProcessmanagementController); + } +} diff --git a/cobigen-nest-server/macos/src/app.service.ts b/cobigen-nest-server/macos/src/app.service.ts new file mode 100644 index 0000000..927d7cc --- /dev/null +++ b/cobigen-nest-server/macos/src/app.service.ts @@ -0,0 +1,8 @@ +import { Injectable } from '@nestjs/common'; + +@Injectable() +export class AppService { + getHello(): string { + return 'Hello World!'; + } +} diff --git a/cobigen-nest-server/macos/src/config/properties.ts b/cobigen-nest-server/macos/src/config/properties.ts new file mode 100644 index 0000000..68f9f89 --- /dev/null +++ b/cobigen-nest-server/macos/src/config/properties.ts @@ -0,0 +1,6 @@ +const properties = { + // 15 minutes timeout + timeout: 15 * 60 * 1000, +}; + +export default properties; diff --git a/cobigen-nest-server/macos/src/main.ts b/cobigen-nest-server/macos/src/main.ts new file mode 100644 index 0000000..66f07a1 --- /dev/null +++ b/cobigen-nest-server/macos/src/main.ts @@ -0,0 +1,64 @@ +import { NestFactory } from '@nestjs/core'; +import { AppModule } from './app.module'; +import { Request, Response } from 'express'; +import properties from './config/properties'; +import * as bodyParser from 'body-parser'; + +// We need these variables for resetting the timeout +let counter: number = 0; +let time: NodeJS.Timeout; +// Will hold the server +let server; + +/** + * This function resets the current timeout. + * We don't want to close the server if we have just received a new request. + * @export + * @param {Request} req the current request + * @param {Response} res the result of the request + * @param {*} next to pass control to the next middleware function. Otherwise, the request will be left hanging. + */ +export function resetTimeout(req: Request, res: Response, next) { + counter++; + + if (counter % 2 === 0) { + // We need to start again the timeout + time = setTimeout(() => { + if (server === undefined) { + // If no server is found, let's just exit + process.exit(); + } else { + // Gracefully close the server + server.close(); + } + }, properties.timeout); + // The request should be passed to the controller + next(); + } else { + // Let's clear the timeout and then set it again + clearTimeout(time); + resetTimeout(req, res, next); + } +} + +async function bootstrap() { + const app = await NestFactory.create(AppModule); + // Port is passed as argument when creating the process + let port: string = process.argv[2]; + // We need to parse long JSON files on requests + app.use(bodyParser.json({ limit: '12mb' })); + app.use(bodyParser.urlencoded({ limit: '12mb', extended: true })); + + if (port === undefined) { + // Default port when no parameter was passed + port = '5000'; + } + + server = await app.listen(port); + + // We set the timeout, so that the server gets closed after some time of inactivity + time = setTimeout(() => { + server.close(); + }, properties.timeout); +} +bootstrap(); diff --git a/cobigen-nest-server/macos/src/processmanagement/etos/input-file.eto.ts b/cobigen-nest-server/macos/src/processmanagement/etos/input-file.eto.ts new file mode 100644 index 0000000..f417361 --- /dev/null +++ b/cobigen-nest-server/macos/src/processmanagement/etos/input-file.eto.ts @@ -0,0 +1,19 @@ +/** + * Input file transfer object + * + * OpenAPI spec version: 1.0 + * + * + * NOTE: This class is auto generated by CobiGen. + */ +export class InputFileEto { + readonly filename: string; + readonly content: string; + readonly charset: string; + + public constructor(filename: string, content: string, charset: string) { + this.filename = filename; + this.content = content; + this.charset = charset; + } +} diff --git a/cobigen-nest-server/macos/src/processmanagement/etos/merger.eto.ts b/cobigen-nest-server/macos/src/processmanagement/etos/merger.eto.ts new file mode 100644 index 0000000..67874da --- /dev/null +++ b/cobigen-nest-server/macos/src/processmanagement/etos/merger.eto.ts @@ -0,0 +1,25 @@ +/** + * Api Documentation + * Api Documentation + * + * OpenAPI spec version: 1.0 + * + * + * NOTE: This class is auto generated by CobiGen. + */ + +export class MergerEto { + readonly baseContent: string; + readonly patchContent: string; + readonly patchOverrides: boolean; + + public constructor( + baseContent: string, + patchContent: string, + patchOverrides: boolean, + ) { + this.baseContent = baseContent; + this.patchContent = patchContent; + this.patchOverrides = patchOverrides; + } +} diff --git a/cobigen-nest-server/macos/src/processmanagement/etos/model.eto.ts b/cobigen-nest-server/macos/src/processmanagement/etos/model.eto.ts new file mode 100644 index 0000000..4d2fe82 --- /dev/null +++ b/cobigen-nest-server/macos/src/processmanagement/etos/model.eto.ts @@ -0,0 +1,16 @@ +/** + * Api Documentation + * Api Documentation + * + * OpenAPI spec version: 1.0 + * + * + * NOTE: This class is auto generated by CobiGen. + */ +export class ModelEto { + readonly input: string; + + public constructor(input: string) { + this.input = input; + } +} diff --git a/cobigen-nest-server/macos/src/processmanagement/inputreader/input-reader.ts b/cobigen-nest-server/macos/src/processmanagement/inputreader/input-reader.ts new file mode 100644 index 0000000..8d4ff91 --- /dev/null +++ b/cobigen-nest-server/macos/src/processmanagement/inputreader/input-reader.ts @@ -0,0 +1,90 @@ +import { ModelEto } from './../etos/model.eto'; +import { InputFileEto } from '../etos/input-file.eto'; +import * as tsm from '@devonfw/ts-merger'; + +export class InputReader { + readonly content; + + public async getInputObjects(inputFile:InputFileEto, removeEmptyFields : boolean) { + const parsedFile = tsm.readFile(inputFile.content); + + // Extending the model + parsedFile.path = inputFile.filename; + // identifier and type fields are extended with the entity's type + + // Setting the import dictionary + var entityModuleMapper: { [id: string] : String; } = {}; + parsedFile.getImports().forEach( (i) => { + let module = i.getModule(); + i.getNamed().forEach ((name) =>{ + entityModuleMapper[String(name)] = module; + }) + }); + + // Traversing the parsed file object and extending each decorator + this.traverse(parsedFile,entityModuleMapper); + + if (!removeEmptyFields) + { + return new ModelEto(JSON.stringify(parsedFile)); + } + + // Removing the empty fields + const parsedFileString: string = this.removeEmptyFields(parsedFile) + + return new ModelEto(JSON.stringify(JSON.parse(parsedFileString))); + } + + private removeEmptyFields(parsedFile): string { + var regExp = /"\w+":(""|\[\]|{}),?("module":null)?/gi + var trailingCommasRegExp = /\,(?=\s*?[\}\]])/g; + + let parsedFileString: string = JSON.stringify(parsedFile); + + while (parsedFileString.search(regExp)> -1) + { + parsedFileString = parsedFileString.replace(regExp, ""); + parsedFileString = parsedFileString.replace(trailingCommasRegExp, ""); + } + return parsedFileString; + } + + + private traverse(o,entityModuleMapper) { + for (var i in o) { + if(o[i] && o[i]!=null){ + if(typeof o[i].getType === 'function'){ + let name = o[i].getType(); + let module = entityModuleMapper[String(name)]; + if (typeof module == 'undefined') + { + module = null; + } + o[i].setType( + { + "name" : name, + "module": module + } + ); + } + if (typeof o[i].getIsCallExpression === 'function'){ + let identifier = o[i].getIdentifier(); + if (identifier != '') + { + o[i].setIdentifier( + { + "name" : identifier, + "module": entityModuleMapper[String(identifier)] + } + ); + } + } + } + if (o[i] !== null && typeof(o[i])=="object") { + //going one step down in the object tree!! + this.traverse(o[i],entityModuleMapper); + } + + } +} +} diff --git a/cobigen-nest-server/macos/src/processmanagement/processmanagement.controller.spec.ts b/cobigen-nest-server/macos/src/processmanagement/processmanagement.controller.spec.ts new file mode 100644 index 0000000..89bd885 --- /dev/null +++ b/cobigen-nest-server/macos/src/processmanagement/processmanagement.controller.spec.ts @@ -0,0 +1,18 @@ +import { Test, TestingModule } from '@nestjs/testing'; +import { ProcessmanagementController } from './processmanagement.controller'; + +describe('Processmanagement Controller', () => { + let controller: ProcessmanagementController; + + beforeEach(async () => { + const module: TestingModule = await Test.createTestingModule({ + controllers: [ProcessmanagementController], + }).compile(); + + controller = module.get(ProcessmanagementController); + }); + + it('should be defined', () => { + expect(controller).toBeDefined(); + }); +}); diff --git a/cobigen-nest-server/macos/src/processmanagement/processmanagement.controller.ts b/cobigen-nest-server/macos/src/processmanagement/processmanagement.controller.ts new file mode 100644 index 0000000..7c21aab --- /dev/null +++ b/cobigen-nest-server/macos/src/processmanagement/processmanagement.controller.ts @@ -0,0 +1,61 @@ +import { Controller, Get, Post, Body } from '@nestjs/common'; +import { InputFileEto } from './etos/input-file.eto'; +import { MergerEto } from './etos/merger.eto'; +import { InputReader } from './inputreader/input-reader'; +import * as tsm from '@devonfw/ts-merger'; + +const config = require('../../package.json'); + +@Controller('processmanagement') +export class ProcessmanagementController { + @Get('tsplugin/isConnectionReady') + isConnectionReady() { + return config.version; + } + + @Post('tsplugin/isValidInput') + isValidInput(@Body() inputFile: InputFileEto) { + const filename: string = inputFile.filename.toLowerCase(); + + const validExtensions = ['.nest', '.ts', 'js']; + for (const extension of validExtensions) { + if (filename.includes(extension)) { + return true; + } + } + return false; + } + + @Post('/tsplugin/getInputModel') + async getInputModel(@Body() inputFile: InputFileEto) { + const inputReader: InputReader = new InputReader(); + const model = await inputReader.getInputObjects(inputFile, true); + + return model.input; + } + + @Post('/tsplugin/merge') + merge(@Body() merger: MergerEto) { + const patchContent: string = merger.patchContent.replace(/\\n/gm, '\n'); + const baseContent: string = merger.baseContent.replace(/\\n/gm, '\n'); + const patchOverrides: boolean = merger.patchOverrides; + + const mergedCode: string = tsm.merge( + baseContent, + patchContent, + patchOverrides, + ); + + return mergedCode; + } + + @Post('/tsplugin/beautify') + beautify(@Body() file: InputFileEto) { + const content: string = file.content.replace(/\\n/gm, '\n'); + + const beautify = require('js-beautify'); + const beautifiedCode: string = beautify(content); + + return beautifiedCode; + } +} diff --git a/cobigen-nest-server/macos/test/beautifyrequest.e2e-spec.ts b/cobigen-nest-server/macos/test/beautifyrequest.e2e-spec.ts new file mode 100644 index 0000000..400da87 --- /dev/null +++ b/cobigen-nest-server/macos/test/beautifyrequest.e2e-spec.ts @@ -0,0 +1,30 @@ +import { Test, TestingModule } from '@nestjs/testing'; +import * as request from 'supertest'; +import { AppModule } from '../src/app.module'; +import { InputFileEto } from '../src/processmanagement/etos/input-file.eto'; + +describe('Testing basic requests to the server', () => { + let app; + + beforeEach(async () => { + const moduleFixture: TestingModule = await Test.createTestingModule({ + imports: [AppModule], + }).compile(); + + app = moduleFixture.createNestApplication(); + await app.init(); + }); + + it('processmanagement/tsplugin/beautify (POST) should return beautified code', () => { + const content = `class a { + b(c:any):void; + }`; + const file: InputFileEto = new InputFileEto('', content, ''); + + return request(app.getHttpServer()) + .post('/processmanagement/tsplugin/beautify') + .send(file) + .expect(201) + .expect('class a {\n b(c: any): void;\n}'); + }); +}); diff --git a/cobigen-nest-server/macos/test/inputreadingrequest.e2e-spec.ts b/cobigen-nest-server/macos/test/inputreadingrequest.e2e-spec.ts new file mode 100644 index 0000000..08edac3 --- /dev/null +++ b/cobigen-nest-server/macos/test/inputreadingrequest.e2e-spec.ts @@ -0,0 +1,24 @@ +import { Test, TestingModule } from '@nestjs/testing'; +import * as request from 'supertest'; +import { AppModule } from '../src/app.module'; + +describe('Testing input reader request to the server', () => { + let app; + + beforeEach(async () => { + const moduleFixture: TestingModule = await Test.createTestingModule({ + imports: [AppModule], + }).compile(); + + app = moduleFixture.createNestApplication(); + await app.init(); + }); + + it('processmanagement/tsplugin/getInputModel (POST) should return true', () => { + return request(app.getHttpServer()) + .post('processmanagement/tsplugin/getInputModel') + .send({ content: 'class a {}' }) + .expect(201) + .toBeNonEmptyString(); + }); +}); diff --git a/cobigen-nest-server/macos/test/jest-e2e.json b/cobigen-nest-server/macos/test/jest-e2e.json new file mode 100644 index 0000000..e9d912f --- /dev/null +++ b/cobigen-nest-server/macos/test/jest-e2e.json @@ -0,0 +1,9 @@ +{ + "moduleFileExtensions": ["js", "json", "ts"], + "rootDir": ".", + "testEnvironment": "node", + "testRegex": ".e2e-spec.ts$", + "transform": { + "^.+\\.(t|j)s$": "ts-jest" + } +} diff --git a/cobigen-nest-server/macos/test/mergerequest.e2e-spec.ts b/cobigen-nest-server/macos/test/mergerequest.e2e-spec.ts new file mode 100644 index 0000000..9b56d46 --- /dev/null +++ b/cobigen-nest-server/macos/test/mergerequest.e2e-spec.ts @@ -0,0 +1,49 @@ +import { Test, TestingModule } from '@nestjs/testing'; +import * as request from 'supertest'; +import { AppModule } from '../src/app.module'; +import { MergerEto } from '../src/processmanagement/etos/merger.eto'; + +describe('Testing basic requests to the server', () => { + let app; + + beforeEach(async () => { + const moduleFixture: TestingModule = await Test.createTestingModule({ + imports: [AppModule], + }).compile(); + + app = moduleFixture.createNestApplication(); + await app.init(); + }); + + it('processmanagement/tsplugin/merge (POST) should return merged values without overriding base', () => { + const base = `class a { + async b(c:any):void{} + }`; + const patch = `class a { + b(c:any):number{} + }`; + const merger: MergerEto = new MergerEto(base, patch, false); + + return request(app.getHttpServer()) + .post('/processmanagement/tsplugin/merge') + .send(merger) + .expect(201) + .expect('\nclass a {\nasync b(c: any): void\n{ \n\n}\n\n\n}\n'); + }); + + it('processmanagement/tsplugin/merge (POST) should return merged values overriding base', () => { + const base = `interface a { + b(c:any):void; + }`; + const patch = `interface a { + b(c:any):number; + }`; + const merger: MergerEto = new MergerEto(base, patch, true); + + return request(app.getHttpServer()) + .post('/processmanagement/tsplugin/merge') + .send(merger) + .expect(201) + .expect('\ninterface a {\nb(c: any): number;\n\n\n}\n'); + }); +}); diff --git a/cobigen-nest-server/macos/test/simplerequests.e2e-spec.ts b/cobigen-nest-server/macos/test/simplerequests.e2e-spec.ts new file mode 100644 index 0000000..0aa28aa --- /dev/null +++ b/cobigen-nest-server/macos/test/simplerequests.e2e-spec.ts @@ -0,0 +1,31 @@ +import { Test, TestingModule } from '@nestjs/testing'; +import * as request from 'supertest'; +import { AppModule } from '../src/app.module'; + +describe('Testing basic requests to the server', () => { + let app; + + beforeEach(async () => { + const moduleFixture: TestingModule = await Test.createTestingModule({ + imports: [AppModule], + }).compile(); + + app = moduleFixture.createNestApplication(); + await app.init(); + }); + + it('/ (GET)', () => { + return request(app.getHttpServer()) + .get('/') + .expect(200) + .expect('Hello World!'); + }); + + it('processmanagement/isValidInput (POST) should return true', () => { + return request(app.getHttpServer()) + .post('/processmanagement/isValidInput') + .send({ path: 'bla.nest' }) + .expect(201) + .expect('true'); + }); +}); diff --git a/cobigen-nest-server/macos/tsconfig.build.json b/cobigen-nest-server/macos/tsconfig.build.json new file mode 100644 index 0000000..c180ac8 --- /dev/null +++ b/cobigen-nest-server/macos/tsconfig.build.json @@ -0,0 +1,4 @@ +{ + "extends": "./tsconfig.json", + "exclude": ["node_modules", "test", "**/*spec.ts"] +} diff --git a/cobigen-nest-server/macos/tsconfig.json b/cobigen-nest-server/macos/tsconfig.json new file mode 100644 index 0000000..44a5e2e --- /dev/null +++ b/cobigen-nest-server/macos/tsconfig.json @@ -0,0 +1,14 @@ +{ + "compilerOptions": { + "module": "commonjs", + "declaration": true, + "removeComments": true, + "emitDecoratorMetadata": true, + "experimentalDecorators": true, + "target": "es6", + "sourceMap": true, + "outDir": "./dist", + "baseUrl": "./" + }, + "exclude": ["node_modules"] +} diff --git a/cobigen-nest-server/macos/tslint.json b/cobigen-nest-server/macos/tslint.json new file mode 100644 index 0000000..5651b2f --- /dev/null +++ b/cobigen-nest-server/macos/tslint.json @@ -0,0 +1,18 @@ +{ + "defaultSeverity": "error", + "extends": ["tslint:recommended"], + "jsRules": { + "no-unused-expression": true + }, + "rules": { + "quotemark": [true, "single"], + "member-access": [false], + "ordered-imports": [false], + "max-line-length": [true, 150], + "member-ordering": [false], + "interface-name": [false], + "arrow-parens": false, + "object-literal-sort-keys": false + }, + "rulesDirectory": [] +} diff --git a/cobigen-nest-server/package.json b/cobigen-nest-server/package.json index f5ae7c4..d2a429f 100644 --- a/cobigen-nest-server/package.json +++ b/cobigen-nest-server/package.json @@ -1,64 +1,14 @@ { - "name": "@devonfw/cobigen-nestserver", - "bin": "dist/main.js", - "version": "1.1.8", - "description": "Nestjs server to implement the input reader and merger for CobiGen", - "author": "CobiGen Team", - "license": "MIT", + "private": true, + "workspaces": [ + "windows", + "linux", + "macos" + ], "scripts": { - "build": "tsc -p tsconfig.build.json", - "format": "prettier --write \"src/**/*.ts\"", - "start": "ts-node -r tsconfig-paths/register src/main.ts", - "start:dev": "nodemon", - "start:debug": "nodemon --config nodemon-debug.json", - "prestart:prod": "rimraf dist && npm run build", - "start:prod": "node dist/main.js", - "lint": "tslint -p tsconfig.json -c tslint.json", - "test": "jest", - "test:watch": "jest --watch", - "test:cov": "jest --coverage", - "test:debug": "node --inspect-brk -r tsconfig-paths/register -r ts-node/register node_modules/.bin/jest --runInBand", - "test:e2e": "jest --config ./test/jest-e2e.json" - }, - "dependencies": { - "@devonfw/ts-merger": "^2.2.10", - "@nestjs/common": "^5.4.0", - "@nestjs/core": "^5.4.0", - "js-beautify": "^1.9.0", - "pkg": "^4.4.8", - "reflect-metadata": "^0.1.12", - "rimraf": "^2.6.2", - "rxjs": "^6.2.2", - "typescript": "^3.0.1" - }, - "devDependencies": { - "@nestjs/testing": "^5.1.0", - "@types/express": "^4.16.0", - "@types/jest": "^23.3.1", - "@types/node": "^10.7.1", - "@types/supertest": "^2.0.5", - "jest": "^23.5.0", - "nodemon": "^1.18.3", - "prettier": "^1.14.2", - "supertest": "^3.1.0", - "ts-jest": "^23.1.3", - "ts-loader": "^4.4.2", - "ts-node": "^7.0.1", - "tsconfig-paths": "^3.5.0", - "tslint": "5.11.0" - }, - "jest": { - "moduleFileExtensions": [ - "js", - "json", - "ts" - ], - "rootDir": "src", - "testRegex": ".spec.ts$", - "transform": { - "^.+\\.(t|j)s$": "ts-jest" - }, - "coverageDirectory": "../coverage", - "testEnvironment": "node" + "prepare":"npm run build-windows && npm run build-linux && npm run build-macos", + "build-windows":"pkg --targets latest-win-x64 --output windows/nestserver-1.2.0.exe dist/main.js", + "build-linux":"pkg --targets latest-linux-x64 --output linux/nestserver-linux-1.2.0 dist/main.js", + "build-macos":"pkg --targets latest-macos-x64 --output macos/nestserver-macos-1.2.0 dist/main.js" } } diff --git a/cobigen-nest-server/windows/.prettierrc b/cobigen-nest-server/windows/.prettierrc new file mode 100644 index 0000000..dcb7279 --- /dev/null +++ b/cobigen-nest-server/windows/.prettierrc @@ -0,0 +1,4 @@ +{ + "singleQuote": true, + "trailingComma": "all" +} \ No newline at end of file diff --git a/cobigen-nest-server/windows/ExternalProcessContract.yml b/cobigen-nest-server/windows/ExternalProcessContract.yml new file mode 100644 index 0000000..e6ef89b --- /dev/null +++ b/cobigen-nest-server/windows/ExternalProcessContract.yml @@ -0,0 +1,175 @@ +openapi: 3.0.0 +servers: + - url: 'https://localhost:5000/' + description: nest server +info: + title: External process communication + description: Specification of communication between external process and CobiGen + version: 1.0.0 + x-rootpackage: com.devonfw.poc + +paths: + /processmanagement/tsplugin/isConnectionReady/: + get: + description: Needed for CobiGen to know that the server is ready to receive requests. Also we need it to differentiate plug-ins. + responses: + '200': + description: Returns server version + content: + text/plain: + schema: + type: string + + /processmanagement/tsplugin/isValidInput/: + post: + description: Returns true or false whether it is a valid input for generation. It can be a simple file extension check or more complex logic. + requestBody: + $ref: '#/components/requestBodies/InputFile' + operationId: isValidInput + responses: + '200': + description: Returns true or false whether it is a valid input for generation + content: + text/plain: + schema: + type: boolean + '404': + description: Not found + + /processmanagement/tsplugin/getInputModel/: + post: + description: Requesting a key-value model of the input file in JSON format. + requestBody: + $ref: '#/components/requestBodies/InputFile' + operationId: getInputObjects + responses: + '200': + description: Returns a model in JSON format + content: + application/json: + schema: + $ref: '#/components/schemas/Model' + '404': + description: Not found + + /processmanagement/tsplugin/merge/: + post: + description: Merges two files and returns the result + requestBody: + $ref: '#/components/requestBodies/Merger' + operationId: merge + responses: + '200': + description: Returns merge file as a string + content: + text/plain: + schema: + type: string + '404': + description: Not found + +components: + schemas: + Model: + x-component: processmanagement + description: Data model of the input file + type: object + properties: + path: + type: string + description: Input file path. It can also be just the file name. + uniqueItems: true + inputObject: + $ref: '#/components/schemas/InputObject' + InputObject: + x-component: processmanagement + description: Input object containing needed information for the templates + type: object + properties: + Fields: + $ref: '#/components/schemas/Fields' + Imports: + $ref: '#/components/schemas/Imports' + Fields: + x-component: processmanagement + description: Not really needed, just used as an example for the model. + type: array + items: + type: object + properties: + Field: + $ref: '#/components/schemas/Field' + Field: + x-component: processmanagement + description: Not really needed, just used as an example for the model. + type: object + properties: + fieldName: + type: string + fieldType: + type: string + Imports: + x-component: processmanagement + description: Not really needed, just used as an example for the model. + type: array + items: + type: object + properties: + Import: + $ref: '#/components/schemas/Import' + Import: + x-component: processmanagement + description: Not really needed, just used as an example for the model. + type: object + properties: + importValue: + type: string + InputFile: + x-component: processmanagement + description: Input file + type: object + properties: + filename: + type: string + content: + type: string + charset: + type: string + Merger: + x-component: processmanagement + description: Merger properties. baseContent is the current user's file. patchContent is CobiGen freshly generated file. patchOverrides is set to true when in case of conflict, we want to override using patch. + type: object + properties: + baseContent: + type: string + patchContent: + type: string + patchOverrides: + type: boolean + + + requestBodies: + Model: + description: Data model of the file in JSON format + content: + application/json: + schema: + $ref: '#/components/schemas/Model' + required: true + + InputFile: + description: Input file for CobiGen to parse + content: + application/json: + schema: + $ref: '#/components/schemas/InputFile' + required: true + + Merger: + description: baseContent is the current user's file. patchContent is CobiGen freshly generated file. patchOverrides is set to true when in case of conflict, we want to override using patch. + content: + application/json: + schema: + $ref: '#/components/schemas/Merger' + required: true + diff --git a/cobigen-nest-server/windows/nest-cli.json b/cobigen-nest-server/windows/nest-cli.json new file mode 100644 index 0000000..0dde5f8 --- /dev/null +++ b/cobigen-nest-server/windows/nest-cli.json @@ -0,0 +1,5 @@ +{ + "language": "ts", + "collection": "@nestjs/schematics", + "sourceRoot": "src" +} diff --git a/cobigen-nest-server/windows/nodemon-debug.json b/cobigen-nest-server/windows/nodemon-debug.json new file mode 100644 index 0000000..052b023 --- /dev/null +++ b/cobigen-nest-server/windows/nodemon-debug.json @@ -0,0 +1,6 @@ +{ + "watch": ["src"], + "ext": "ts", + "ignore": ["src/**/*.spec.ts"], + "exec": "node --inspect-brk -r ts-node/register -r tsconfig-paths/register src/main.ts" +} diff --git a/cobigen-nest-server/windows/nodemon.json b/cobigen-nest-server/windows/nodemon.json new file mode 100644 index 0000000..583bb42 --- /dev/null +++ b/cobigen-nest-server/windows/nodemon.json @@ -0,0 +1,6 @@ +{ + "watch": ["src"], + "ext": "ts", + "ignore": ["src/**/*.spec.ts"], + "exec": "ts-node -r tsconfig-paths/register src/main.ts" +} diff --git a/cobigen-nest-server/windows/package.json b/cobigen-nest-server/windows/package.json new file mode 100644 index 0000000..39eb9fe --- /dev/null +++ b/cobigen-nest-server/windows/package.json @@ -0,0 +1,64 @@ +{ + "name": "@devonfw/cobigen-nestserver", + "bin": "dist/main.js", + "version": "1.2.0", + "description": "Nestjs server to implement the input reader and merger for CobiGen", + "author": "CobiGen Team", + "license": "MIT", + "scripts":{ + "build": "tsc -p tsconfig.build.json", + "format": "prettier --write \"src/**/*.ts\"", + "start": "ts-node -r tsconfig-paths/register src/main.ts", + "start:dev": "nodemon", + "start:debug": "nodemon --config nodemon-debug.json", + "prestart:prod": "rimraf dist && npm run build", + "start:prod": "node dist/main.js", + "lint": "tslint -p tsconfig.json -c tslint.json", + "test": "jest", + "test:watch": "jest --watch", + "test:cov": "jest --coverage", + "test:debug": "node --inspect-brk -r tsconfig-paths/register -r ts-node/register node_modules/.bin/jest --runInBand", + "test:e2e": "jest --config ./test/jest-e2e.json" + }, + "dependencies": { + "@devonfw/ts-merger": "^2.2.10", + "@nestjs/common": "^5.4.0", + "@nestjs/core": "^5.4.0", + "js-beautify": "^1.9.0", + "pkg": "^4.4.9", + "reflect-metadata": "^0.1.12", + "rimraf": "^2.6.2", + "rxjs": "^6.2.2", + "typescript": "^3.0.1" + }, + "devDependencies": { + "@nestjs/testing": "^5.1.0", + "@types/express": "^4.16.0", + "@types/jest": "^23.3.1", + "@types/node": "^10.7.1", + "@types/supertest": "^2.0.5", + "jest": "^23.5.0", + "nodemon": "^1.18.3", + "prettier": "^1.14.2", + "supertest": "^3.1.0", + "ts-jest": "^23.1.3", + "ts-loader": "^4.4.2", + "ts-node": "^7.0.1", + "tsconfig-paths": "^3.5.0", + "tslint": "5.11.0" + }, + "jest": { + "moduleFileExtensions": [ + "js", + "json", + "ts" + ], + "rootDir": "src", + "testRegex": ".spec.ts$", + "transform": { + "^.+\\.(t|j)s$": "ts-jest" + }, + "coverageDirectory": "../coverage", + "testEnvironment": "node" + } +} diff --git a/cobigen-nest-server/windows/src/app.controller.spec.ts b/cobigen-nest-server/windows/src/app.controller.spec.ts new file mode 100644 index 0000000..d22f389 --- /dev/null +++ b/cobigen-nest-server/windows/src/app.controller.spec.ts @@ -0,0 +1,22 @@ +import { Test, TestingModule } from '@nestjs/testing'; +import { AppController } from './app.controller'; +import { AppService } from './app.service'; + +describe('AppController', () => { + let appController: AppController; + + beforeEach(async () => { + const app: TestingModule = await Test.createTestingModule({ + controllers: [AppController], + providers: [AppService], + }).compile(); + + appController = app.get(AppController); + }); + + describe('root', () => { + it('should return "Hello World!"', () => { + expect(appController.getHello()).toBe('Hello World!'); + }); + }); +}); diff --git a/cobigen-nest-server/windows/src/app.controller.ts b/cobigen-nest-server/windows/src/app.controller.ts new file mode 100644 index 0000000..cce879e --- /dev/null +++ b/cobigen-nest-server/windows/src/app.controller.ts @@ -0,0 +1,12 @@ +import { Controller, Get } from '@nestjs/common'; +import { AppService } from './app.service'; + +@Controller() +export class AppController { + constructor(private readonly appService: AppService) {} + + @Get() + getHello(): string { + return this.appService.getHello(); + } +} diff --git a/cobigen-nest-server/windows/src/app.module.ts b/cobigen-nest-server/windows/src/app.module.ts new file mode 100644 index 0000000..4a957bd --- /dev/null +++ b/cobigen-nest-server/windows/src/app.module.ts @@ -0,0 +1,16 @@ +import { Module, NestModule, MiddlewareConsumer } from '@nestjs/common'; +import { AppController } from './app.controller'; +import { AppService } from './app.service'; +import { ProcessmanagementController } from './processmanagement/processmanagement.controller'; +import { resetTimeout } from './main'; + +@Module({ + imports: [], + controllers: [AppController, ProcessmanagementController], + providers: [AppService], +}) +export class AppModule implements NestModule { + configure(consumer: MiddlewareConsumer) { + consumer.apply(resetTimeout).forRoutes(ProcessmanagementController); + } +} diff --git a/cobigen-nest-server/windows/src/app.service.ts b/cobigen-nest-server/windows/src/app.service.ts new file mode 100644 index 0000000..927d7cc --- /dev/null +++ b/cobigen-nest-server/windows/src/app.service.ts @@ -0,0 +1,8 @@ +import { Injectable } from '@nestjs/common'; + +@Injectable() +export class AppService { + getHello(): string { + return 'Hello World!'; + } +} diff --git a/cobigen-nest-server/windows/src/config/properties.ts b/cobigen-nest-server/windows/src/config/properties.ts new file mode 100644 index 0000000..68f9f89 --- /dev/null +++ b/cobigen-nest-server/windows/src/config/properties.ts @@ -0,0 +1,6 @@ +const properties = { + // 15 minutes timeout + timeout: 15 * 60 * 1000, +}; + +export default properties; diff --git a/cobigen-nest-server/windows/src/main.ts b/cobigen-nest-server/windows/src/main.ts new file mode 100644 index 0000000..66f07a1 --- /dev/null +++ b/cobigen-nest-server/windows/src/main.ts @@ -0,0 +1,64 @@ +import { NestFactory } from '@nestjs/core'; +import { AppModule } from './app.module'; +import { Request, Response } from 'express'; +import properties from './config/properties'; +import * as bodyParser from 'body-parser'; + +// We need these variables for resetting the timeout +let counter: number = 0; +let time: NodeJS.Timeout; +// Will hold the server +let server; + +/** + * This function resets the current timeout. + * We don't want to close the server if we have just received a new request. + * @export + * @param {Request} req the current request + * @param {Response} res the result of the request + * @param {*} next to pass control to the next middleware function. Otherwise, the request will be left hanging. + */ +export function resetTimeout(req: Request, res: Response, next) { + counter++; + + if (counter % 2 === 0) { + // We need to start again the timeout + time = setTimeout(() => { + if (server === undefined) { + // If no server is found, let's just exit + process.exit(); + } else { + // Gracefully close the server + server.close(); + } + }, properties.timeout); + // The request should be passed to the controller + next(); + } else { + // Let's clear the timeout and then set it again + clearTimeout(time); + resetTimeout(req, res, next); + } +} + +async function bootstrap() { + const app = await NestFactory.create(AppModule); + // Port is passed as argument when creating the process + let port: string = process.argv[2]; + // We need to parse long JSON files on requests + app.use(bodyParser.json({ limit: '12mb' })); + app.use(bodyParser.urlencoded({ limit: '12mb', extended: true })); + + if (port === undefined) { + // Default port when no parameter was passed + port = '5000'; + } + + server = await app.listen(port); + + // We set the timeout, so that the server gets closed after some time of inactivity + time = setTimeout(() => { + server.close(); + }, properties.timeout); +} +bootstrap(); diff --git a/cobigen-nest-server/windows/src/processmanagement/etos/input-file.eto.ts b/cobigen-nest-server/windows/src/processmanagement/etos/input-file.eto.ts new file mode 100644 index 0000000..f417361 --- /dev/null +++ b/cobigen-nest-server/windows/src/processmanagement/etos/input-file.eto.ts @@ -0,0 +1,19 @@ +/** + * Input file transfer object + * + * OpenAPI spec version: 1.0 + * + * + * NOTE: This class is auto generated by CobiGen. + */ +export class InputFileEto { + readonly filename: string; + readonly content: string; + readonly charset: string; + + public constructor(filename: string, content: string, charset: string) { + this.filename = filename; + this.content = content; + this.charset = charset; + } +} diff --git a/cobigen-nest-server/windows/src/processmanagement/etos/merger.eto.ts b/cobigen-nest-server/windows/src/processmanagement/etos/merger.eto.ts new file mode 100644 index 0000000..67874da --- /dev/null +++ b/cobigen-nest-server/windows/src/processmanagement/etos/merger.eto.ts @@ -0,0 +1,25 @@ +/** + * Api Documentation + * Api Documentation + * + * OpenAPI spec version: 1.0 + * + * + * NOTE: This class is auto generated by CobiGen. + */ + +export class MergerEto { + readonly baseContent: string; + readonly patchContent: string; + readonly patchOverrides: boolean; + + public constructor( + baseContent: string, + patchContent: string, + patchOverrides: boolean, + ) { + this.baseContent = baseContent; + this.patchContent = patchContent; + this.patchOverrides = patchOverrides; + } +} diff --git a/cobigen-nest-server/windows/src/processmanagement/etos/model.eto.ts b/cobigen-nest-server/windows/src/processmanagement/etos/model.eto.ts new file mode 100644 index 0000000..4d2fe82 --- /dev/null +++ b/cobigen-nest-server/windows/src/processmanagement/etos/model.eto.ts @@ -0,0 +1,16 @@ +/** + * Api Documentation + * Api Documentation + * + * OpenAPI spec version: 1.0 + * + * + * NOTE: This class is auto generated by CobiGen. + */ +export class ModelEto { + readonly input: string; + + public constructor(input: string) { + this.input = input; + } +} diff --git a/cobigen-nest-server/windows/src/processmanagement/inputreader/input-reader.ts b/cobigen-nest-server/windows/src/processmanagement/inputreader/input-reader.ts new file mode 100644 index 0000000..8d4ff91 --- /dev/null +++ b/cobigen-nest-server/windows/src/processmanagement/inputreader/input-reader.ts @@ -0,0 +1,90 @@ +import { ModelEto } from './../etos/model.eto'; +import { InputFileEto } from '../etos/input-file.eto'; +import * as tsm from '@devonfw/ts-merger'; + +export class InputReader { + readonly content; + + public async getInputObjects(inputFile:InputFileEto, removeEmptyFields : boolean) { + const parsedFile = tsm.readFile(inputFile.content); + + // Extending the model + parsedFile.path = inputFile.filename; + // identifier and type fields are extended with the entity's type + + // Setting the import dictionary + var entityModuleMapper: { [id: string] : String; } = {}; + parsedFile.getImports().forEach( (i) => { + let module = i.getModule(); + i.getNamed().forEach ((name) =>{ + entityModuleMapper[String(name)] = module; + }) + }); + + // Traversing the parsed file object and extending each decorator + this.traverse(parsedFile,entityModuleMapper); + + if (!removeEmptyFields) + { + return new ModelEto(JSON.stringify(parsedFile)); + } + + // Removing the empty fields + const parsedFileString: string = this.removeEmptyFields(parsedFile) + + return new ModelEto(JSON.stringify(JSON.parse(parsedFileString))); + } + + private removeEmptyFields(parsedFile): string { + var regExp = /"\w+":(""|\[\]|{}),?("module":null)?/gi + var trailingCommasRegExp = /\,(?=\s*?[\}\]])/g; + + let parsedFileString: string = JSON.stringify(parsedFile); + + while (parsedFileString.search(regExp)> -1) + { + parsedFileString = parsedFileString.replace(regExp, ""); + parsedFileString = parsedFileString.replace(trailingCommasRegExp, ""); + } + return parsedFileString; + } + + + private traverse(o,entityModuleMapper) { + for (var i in o) { + if(o[i] && o[i]!=null){ + if(typeof o[i].getType === 'function'){ + let name = o[i].getType(); + let module = entityModuleMapper[String(name)]; + if (typeof module == 'undefined') + { + module = null; + } + o[i].setType( + { + "name" : name, + "module": module + } + ); + } + if (typeof o[i].getIsCallExpression === 'function'){ + let identifier = o[i].getIdentifier(); + if (identifier != '') + { + o[i].setIdentifier( + { + "name" : identifier, + "module": entityModuleMapper[String(identifier)] + } + ); + } + } + } + if (o[i] !== null && typeof(o[i])=="object") { + //going one step down in the object tree!! + this.traverse(o[i],entityModuleMapper); + } + + } +} +} diff --git a/cobigen-nest-server/windows/src/processmanagement/processmanagement.controller.spec.ts b/cobigen-nest-server/windows/src/processmanagement/processmanagement.controller.spec.ts new file mode 100644 index 0000000..89bd885 --- /dev/null +++ b/cobigen-nest-server/windows/src/processmanagement/processmanagement.controller.spec.ts @@ -0,0 +1,18 @@ +import { Test, TestingModule } from '@nestjs/testing'; +import { ProcessmanagementController } from './processmanagement.controller'; + +describe('Processmanagement Controller', () => { + let controller: ProcessmanagementController; + + beforeEach(async () => { + const module: TestingModule = await Test.createTestingModule({ + controllers: [ProcessmanagementController], + }).compile(); + + controller = module.get(ProcessmanagementController); + }); + + it('should be defined', () => { + expect(controller).toBeDefined(); + }); +}); diff --git a/cobigen-nest-server/windows/src/processmanagement/processmanagement.controller.ts b/cobigen-nest-server/windows/src/processmanagement/processmanagement.controller.ts new file mode 100644 index 0000000..7c21aab --- /dev/null +++ b/cobigen-nest-server/windows/src/processmanagement/processmanagement.controller.ts @@ -0,0 +1,61 @@ +import { Controller, Get, Post, Body } from '@nestjs/common'; +import { InputFileEto } from './etos/input-file.eto'; +import { MergerEto } from './etos/merger.eto'; +import { InputReader } from './inputreader/input-reader'; +import * as tsm from '@devonfw/ts-merger'; + +const config = require('../../package.json'); + +@Controller('processmanagement') +export class ProcessmanagementController { + @Get('tsplugin/isConnectionReady') + isConnectionReady() { + return config.version; + } + + @Post('tsplugin/isValidInput') + isValidInput(@Body() inputFile: InputFileEto) { + const filename: string = inputFile.filename.toLowerCase(); + + const validExtensions = ['.nest', '.ts', 'js']; + for (const extension of validExtensions) { + if (filename.includes(extension)) { + return true; + } + } + return false; + } + + @Post('/tsplugin/getInputModel') + async getInputModel(@Body() inputFile: InputFileEto) { + const inputReader: InputReader = new InputReader(); + const model = await inputReader.getInputObjects(inputFile, true); + + return model.input; + } + + @Post('/tsplugin/merge') + merge(@Body() merger: MergerEto) { + const patchContent: string = merger.patchContent.replace(/\\n/gm, '\n'); + const baseContent: string = merger.baseContent.replace(/\\n/gm, '\n'); + const patchOverrides: boolean = merger.patchOverrides; + + const mergedCode: string = tsm.merge( + baseContent, + patchContent, + patchOverrides, + ); + + return mergedCode; + } + + @Post('/tsplugin/beautify') + beautify(@Body() file: InputFileEto) { + const content: string = file.content.replace(/\\n/gm, '\n'); + + const beautify = require('js-beautify'); + const beautifiedCode: string = beautify(content); + + return beautifiedCode; + } +} diff --git a/cobigen-nest-server/windows/test/beautifyrequest.e2e-spec.ts b/cobigen-nest-server/windows/test/beautifyrequest.e2e-spec.ts new file mode 100644 index 0000000..400da87 --- /dev/null +++ b/cobigen-nest-server/windows/test/beautifyrequest.e2e-spec.ts @@ -0,0 +1,30 @@ +import { Test, TestingModule } from '@nestjs/testing'; +import * as request from 'supertest'; +import { AppModule } from '../src/app.module'; +import { InputFileEto } from '../src/processmanagement/etos/input-file.eto'; + +describe('Testing basic requests to the server', () => { + let app; + + beforeEach(async () => { + const moduleFixture: TestingModule = await Test.createTestingModule({ + imports: [AppModule], + }).compile(); + + app = moduleFixture.createNestApplication(); + await app.init(); + }); + + it('processmanagement/tsplugin/beautify (POST) should return beautified code', () => { + const content = `class a { + b(c:any):void; + }`; + const file: InputFileEto = new InputFileEto('', content, ''); + + return request(app.getHttpServer()) + .post('/processmanagement/tsplugin/beautify') + .send(file) + .expect(201) + .expect('class a {\n b(c: any): void;\n}'); + }); +}); diff --git a/cobigen-nest-server/windows/test/inputreadingrequest.e2e-spec.ts b/cobigen-nest-server/windows/test/inputreadingrequest.e2e-spec.ts new file mode 100644 index 0000000..08edac3 --- /dev/null +++ b/cobigen-nest-server/windows/test/inputreadingrequest.e2e-spec.ts @@ -0,0 +1,24 @@ +import { Test, TestingModule } from '@nestjs/testing'; +import * as request from 'supertest'; +import { AppModule } from '../src/app.module'; + +describe('Testing input reader request to the server', () => { + let app; + + beforeEach(async () => { + const moduleFixture: TestingModule = await Test.createTestingModule({ + imports: [AppModule], + }).compile(); + + app = moduleFixture.createNestApplication(); + await app.init(); + }); + + it('processmanagement/tsplugin/getInputModel (POST) should return true', () => { + return request(app.getHttpServer()) + .post('processmanagement/tsplugin/getInputModel') + .send({ content: 'class a {}' }) + .expect(201) + .toBeNonEmptyString(); + }); +}); diff --git a/cobigen-nest-server/windows/test/jest-e2e.json b/cobigen-nest-server/windows/test/jest-e2e.json new file mode 100644 index 0000000..e9d912f --- /dev/null +++ b/cobigen-nest-server/windows/test/jest-e2e.json @@ -0,0 +1,9 @@ +{ + "moduleFileExtensions": ["js", "json", "ts"], + "rootDir": ".", + "testEnvironment": "node", + "testRegex": ".e2e-spec.ts$", + "transform": { + "^.+\\.(t|j)s$": "ts-jest" + } +} diff --git a/cobigen-nest-server/windows/test/mergerequest.e2e-spec.ts b/cobigen-nest-server/windows/test/mergerequest.e2e-spec.ts new file mode 100644 index 0000000..9b56d46 --- /dev/null +++ b/cobigen-nest-server/windows/test/mergerequest.e2e-spec.ts @@ -0,0 +1,49 @@ +import { Test, TestingModule } from '@nestjs/testing'; +import * as request from 'supertest'; +import { AppModule } from '../src/app.module'; +import { MergerEto } from '../src/processmanagement/etos/merger.eto'; + +describe('Testing basic requests to the server', () => { + let app; + + beforeEach(async () => { + const moduleFixture: TestingModule = await Test.createTestingModule({ + imports: [AppModule], + }).compile(); + + app = moduleFixture.createNestApplication(); + await app.init(); + }); + + it('processmanagement/tsplugin/merge (POST) should return merged values without overriding base', () => { + const base = `class a { + async b(c:any):void{} + }`; + const patch = `class a { + b(c:any):number{} + }`; + const merger: MergerEto = new MergerEto(base, patch, false); + + return request(app.getHttpServer()) + .post('/processmanagement/tsplugin/merge') + .send(merger) + .expect(201) + .expect('\nclass a {\nasync b(c: any): void\n{ \n\n}\n\n\n}\n'); + }); + + it('processmanagement/tsplugin/merge (POST) should return merged values overriding base', () => { + const base = `interface a { + b(c:any):void; + }`; + const patch = `interface a { + b(c:any):number; + }`; + const merger: MergerEto = new MergerEto(base, patch, true); + + return request(app.getHttpServer()) + .post('/processmanagement/tsplugin/merge') + .send(merger) + .expect(201) + .expect('\ninterface a {\nb(c: any): number;\n\n\n}\n'); + }); +}); diff --git a/cobigen-nest-server/windows/test/simplerequests.e2e-spec.ts b/cobigen-nest-server/windows/test/simplerequests.e2e-spec.ts new file mode 100644 index 0000000..0aa28aa --- /dev/null +++ b/cobigen-nest-server/windows/test/simplerequests.e2e-spec.ts @@ -0,0 +1,31 @@ +import { Test, TestingModule } from '@nestjs/testing'; +import * as request from 'supertest'; +import { AppModule } from '../src/app.module'; + +describe('Testing basic requests to the server', () => { + let app; + + beforeEach(async () => { + const moduleFixture: TestingModule = await Test.createTestingModule({ + imports: [AppModule], + }).compile(); + + app = moduleFixture.createNestApplication(); + await app.init(); + }); + + it('/ (GET)', () => { + return request(app.getHttpServer()) + .get('/') + .expect(200) + .expect('Hello World!'); + }); + + it('processmanagement/isValidInput (POST) should return true', () => { + return request(app.getHttpServer()) + .post('/processmanagement/isValidInput') + .send({ path: 'bla.nest' }) + .expect(201) + .expect('true'); + }); +}); diff --git a/cobigen-nest-server/windows/tsconfig.build.json b/cobigen-nest-server/windows/tsconfig.build.json new file mode 100644 index 0000000..c180ac8 --- /dev/null +++ b/cobigen-nest-server/windows/tsconfig.build.json @@ -0,0 +1,4 @@ +{ + "extends": "./tsconfig.json", + "exclude": ["node_modules", "test", "**/*spec.ts"] +} diff --git a/cobigen-nest-server/windows/tsconfig.json b/cobigen-nest-server/windows/tsconfig.json new file mode 100644 index 0000000..44a5e2e --- /dev/null +++ b/cobigen-nest-server/windows/tsconfig.json @@ -0,0 +1,14 @@ +{ + "compilerOptions": { + "module": "commonjs", + "declaration": true, + "removeComments": true, + "emitDecoratorMetadata": true, + "experimentalDecorators": true, + "target": "es6", + "sourceMap": true, + "outDir": "./dist", + "baseUrl": "./" + }, + "exclude": ["node_modules"] +} diff --git a/cobigen-nest-server/windows/tslint.json b/cobigen-nest-server/windows/tslint.json new file mode 100644 index 0000000..5651b2f --- /dev/null +++ b/cobigen-nest-server/windows/tslint.json @@ -0,0 +1,18 @@ +{ + "defaultSeverity": "error", + "extends": ["tslint:recommended"], + "jsRules": { + "no-unused-expression": true + }, + "rules": { + "quotemark": [true, "single"], + "member-access": [false], + "ordered-imports": [false], + "max-line-length": [true, 150], + "member-ordering": [false], + "interface-name": [false], + "arrow-parens": false, + "object-literal-sort-keys": false + }, + "rulesDirectory": [] +} diff --git a/cobigen-nest-server/yarn.lock b/cobigen-nest-server/yarn.lock index 6669797..9bab9ed 100644 --- a/cobigen-nest-server/yarn.lock +++ b/cobigen-nest-server/yarn.lock @@ -3458,10 +3458,10 @@ pkg-dir@^2.0.0: dependencies: find-up "^2.1.0" -pkg-fetch@^2.6.7: - version "2.6.7" - resolved "https://registry.yarnpkg.com/pkg-fetch/-/pkg-fetch-2.6.7.tgz#4625b1583e5b32e0771f35c1dc56306e52f1a3db" - integrity sha512-hQ+M53fphA300EkIvNoIxEdFltWcsDOqHZlaFzWJ78wIuFTIT7OrnXUQMAGC6qLkXq7eWCMU/4pTWsFvprUZMg== +pkg-fetch@^2.6.9: + version "2.6.9" + resolved "https://registry.yarnpkg.com/pkg-fetch/-/pkg-fetch-2.6.9.tgz#c18c5fa9604c57a3df3d9630afb64e176bc8732d" + integrity sha512-EnVR8LRILXBvaNP+wJOSY02c3+qDDfyEyR+aqAHLhcc9PBnbxFT9UZ1+If49goPQzQPn26TzF//fc6KXZ0aXEg== dependencies: "@babel/runtime" "^7.9.2" byline "^5.0.0" @@ -3475,10 +3475,10 @@ pkg-fetch@^2.6.7: semver "^6.3.0" unique-temp-dir "^1.0.0" -pkg@^4.4.8: - version "4.4.8" - resolved "https://registry.yarnpkg.com/pkg/-/pkg-4.4.8.tgz#145fb81f31eebfb90d2010dd2c4b663ca0db4009" - integrity sha512-Fqqv0iaX48U3CFZxd6Dq6JKe7BrAWbgRAqMJkz/m8W3H5cqJ6suvsUWe5AJPRlN/AhbBYXBJ0XG9QlYPTXcVFA== +pkg@^4.4.9: + version "4.4.9" + resolved "https://registry.yarnpkg.com/pkg/-/pkg-4.4.9.tgz#be04f8d03795772b7c4394724ae7252d7c2a4519" + integrity sha512-FK4GqHtcCY2PPPVaKViU0NyRzpo6gCS7tPKN5b7AkElqjAOCH1bsRKgohEnxThr6DWfTGByGqba2YHGR/BqbmA== dependencies: "@babel/parser" "^7.9.4" "@babel/runtime" "^7.9.2" @@ -3489,7 +3489,7 @@ pkg@^4.4.8: into-stream "^5.1.1" minimist "^1.2.5" multistream "^2.1.1" - pkg-fetch "^2.6.7" + pkg-fetch "^2.6.9" progress "^2.0.3" resolve "^1.15.1" stream-meter "^1.0.4"