From d6607b1d9f3e219817a6874dfc8a15df95d3eee5 Mon Sep 17 00:00:00 2001 From: Arthur Simas Date: Fri, 24 Jun 2022 14:45:58 -0300 Subject: [PATCH] FEAT: connector manager --- Insomnia_2022-06-23.zip | Bin 0 -> 3774 bytes package-lock.json | 19 ++ package.json | 1 + src/app.module.ts | 10 + src/clients/connector/client.config.ts | 31 ++ src/clients/connector/client.service.ts | 266 ++++++++++++++++++ src/main.ts | 7 +- src/modules/auth/auth.controller.ts | 1 + src/modules/auth/dtos/login.ts | 12 + src/modules/connector/connector.controller.ts | 210 ++++++++++++++ src/modules/connector/dtos/add-tag.ts | 6 + .../connector/dtos/create-connector.ts | 9 + .../connector/dtos/delete-connector.ts | 6 + src/modules/connector/dtos/get-all.ts | 9 + src/modules/connector/dtos/remove-tag.ts | 6 + src/modules/connector/dtos/update.ts | 42 +++ src/modules/connector/dtos/upload-file.ts | 9 + swagger_empty.json | 17 ++ tsconfig.json | 3 +- 19 files changed, 662 insertions(+), 2 deletions(-) create mode 100644 Insomnia_2022-06-23.zip create mode 100644 src/clients/connector/client.config.ts create mode 100644 src/clients/connector/client.service.ts create mode 100644 src/modules/auth/dtos/login.ts create mode 100644 src/modules/connector/connector.controller.ts create mode 100644 src/modules/connector/dtos/add-tag.ts create mode 100644 src/modules/connector/dtos/create-connector.ts create mode 100644 src/modules/connector/dtos/delete-connector.ts create mode 100644 src/modules/connector/dtos/get-all.ts create mode 100644 src/modules/connector/dtos/remove-tag.ts create mode 100644 src/modules/connector/dtos/update.ts create mode 100644 src/modules/connector/dtos/upload-file.ts create mode 100644 swagger_empty.json diff --git a/Insomnia_2022-06-23.zip b/Insomnia_2022-06-23.zip new file mode 100644 index 0000000000000000000000000000000000000000..7103d04cb19c37a25ec7375fa59ebaecbdd84773 GIT binary patch literal 3774 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 v0Q}`dh$#gnC)NKl{X2D8{$BqD( + ConnectorManager.ProtoServices.ConnectorManagerWriteServices, + ); + + this.connectorServiceRead = + this.grpcClient.getService( + ConnectorManager.ProtoServices.ConnectorManagerReadServices, + ); + } + + async uploadConnector(uploadConnector) { + const connector: ConnectorManager.Entities.ConnectorCreateRequest = { + file: { + buffer: uploadConnector.file.buffer, + mimetypes: uploadConnector.file.mimetype, + }, + connector: JSON.stringify(uploadConnector.connector), + }; + const serviceBody: ConnectorManager.Messages.RegisterConnectorRequest = { + connector, + }; + + const observable = from( + this.connectorServiceWrite.RegisterConnector(serviceBody), + ); + + const result = await new Promise((resolve, reject) => { + observable.subscribe({ + next(x) { + resolve(x); + }, + error(err) { + console.log('Observable Error'); + reject(err); + }, + complete() { + console.log('done'); + }, + }); + }).catch((err) => { + console.log(err.details); + throw new HttpException( + err.details, + err.code === 6 ? HttpStatus.CONFLICT : 400, + ); + }); + + console.log('ConnectorClientService', 'Upload'); + + return result; + } + + async uploadFile(uploadFile) { + const body: ConnectorManager.Messages.UploadFileRequest = { + file: { + buffer: uploadFile.file.buffer, + mimetypes: uploadFile.file.mimetype, + }, + name: uploadFile.name, + }; + + const observable = from(this.connectorServiceWrite.UploadFile(body)); + + const result = await new Promise((resolve, reject) => { + observable.subscribe({ + next(x) { + resolve(x); + }, + error(err) { + console.log('Observable Error'); + reject(err); + }, + complete() { + console.log('done'); + }, + }); + }).catch((err) => { + console.log(err.details); + throw new HttpException( + err.details, + err.code === 6 ? HttpStatus.CONFLICT : 400, + ); + }); + + console.log('ConnectorClientService', 'Upload'); + + return result; + } + + async getAllConnectors(body) { + const observable = from( + this.connectorServiceRead.GetAllConnectors({ + search: body.search, + filters: JSON.stringify(body.filters), + }), + ); + const result = await new Promise((resolve, reject) => { + observable.subscribe({ + next(x) { + resolve(x); + }, + error(err) { + console.log('Observable Error'); + reject(err); + }, + complete() { + console.log('done'); + }, + }); + }).catch((err) => { + throw new HttpException( + err.details, + err.code === 6 ? HttpStatus.CONFLICT : 400, + ); + }); + + console.log('ConnectorClientService', 'getAllConnectors'); + + return result; + } + + async getConnectorDetails(plugin: string) { + const observable = from( + this.connectorServiceRead.GetConnectorDetails({ + plugin, + }), + ); + const result = await new Promise((resolve, reject) => { + observable.subscribe({ + next(x) { + resolve(x); + }, + error(err) { + console.log('Observable Error'); + reject(err); + }, + complete() { + console.log('done'); + }, + }); + }).catch((err) => { + throw new HttpException( + err.details, + err.code === 6 ? HttpStatus.CONFLICT : 400, + ); + }); + + console.log('ConnectorClientService', 'getAllConnectors'); + + return result; + } + + async getConnector(plugin: string) { + const observable = from( + this.connectorServiceRead.GetConnector({ + plugin, + }), + ); + const result = await new Promise((resolve, reject) => { + observable.subscribe({ + next(x) { + resolve(x); + }, + error(err) { + console.log('Observable Error'); + reject(err); + }, + complete() { + console.log('done'); + }, + }); + }).catch((err) => { + throw new HttpException( + err.details, + err.code === 6 ? HttpStatus.CONFLICT : 400, + ); + }); + + console.log('ConnectorClientService', 'getAllConnectors'); + + return result; + } + + async deleteConnector(plugin: string, version: string) { + const observable = from( + this.connectorServiceWrite.DeleteConnector({ + version, + plugin, + }), + ); + const result = await new Promise((resolve, reject) => { + observable.subscribe({ + next(x) { + resolve(x); + }, + error(err) { + console.log('Observable Error'); + reject(err); + }, + complete() { + console.log('done'); + }, + }); + }).catch((err) => { + throw new HttpException( + err.details, + err.code === 6 ? HttpStatus.CONFLICT : 400, + ); + }); + + console.log('ConnectorClientService', 'getAllConnectors'); + + return result; + } + + async updateConnector({ plugin, changes }) { + const observable = from( + this.connectorServiceWrite.UpdateConnector({ + plugin, + changes, + }), + ); + const result = await new Promise((resolve, reject) => { + observable.subscribe({ + next(x) { + resolve(x); + }, + error(err) { + console.log('Observable Error'); + reject(err); + }, + complete() { + console.log('done'); + }, + }); + }).catch((err) => { + throw new HttpException( + err.details, + err.code === 6 ? HttpStatus.CONFLICT : 400, + ); + }); + + console.log('ConnectorClientService', 'getAllConnectors'); + + return result; + } +} diff --git a/src/main.ts b/src/main.ts index fec1592..71690c3 100644 --- a/src/main.ts +++ b/src/main.ts @@ -3,6 +3,8 @@ import { DocumentBuilder, SwaggerModule } from '@nestjs/swagger'; import { writeFileSync } from 'fs'; import helmet from 'helmet'; +import documentEmpty from '../swagger_empty.json'; + import { AppModule } from './app.module'; async function bootstrap() { @@ -18,7 +20,7 @@ async function bootstrap() { app.use(helmet()); const config = new DocumentBuilder() - .setTitle('Maestro Grpc Documentation') + .setTitle('Maestro') .setDescription('Documentation for Maestro gateway') .setVersion('1.0') .addBearerAuth() @@ -29,6 +31,9 @@ async function bootstrap() { if (process.env.ENV != 'stg' && process.env.ENV != 'prd') SwaggerModule.setup('api', app, document); + const swaggerDoc = process.env.ENV === 'dev' ? document : documentEmpty; + SwaggerModule.setup('api', app, swaggerDoc); + await app.listen(3333); } bootstrap(); diff --git a/src/modules/auth/auth.controller.ts b/src/modules/auth/auth.controller.ts index 62709eb..97e1794 100644 --- a/src/modules/auth/auth.controller.ts +++ b/src/modules/auth/auth.controller.ts @@ -26,6 +26,7 @@ import { PermissionsClientService } from '../../clients/permissions/client.servi import { Permissions } from '../../authentication/permissions.enum'; import ErrorBuilder from '../../utils/ErrorBuilder'; +import { LoginDto } from './dtos/login'; @ApiTags('Auth') @Controller('auth') diff --git a/src/modules/auth/dtos/login.ts b/src/modules/auth/dtos/login.ts new file mode 100644 index 0000000..cae27fd --- /dev/null +++ b/src/modules/auth/dtos/login.ts @@ -0,0 +1,12 @@ +import { ApiProperty } from '@nestjs/swagger'; + +export class LoginDto { + @ApiProperty() + username: string; + + @ApiProperty() + password: string; + + @ApiProperty({ required: false }) + totp: string; +} diff --git a/src/modules/connector/connector.controller.ts b/src/modules/connector/connector.controller.ts new file mode 100644 index 0000000..e5bf810 --- /dev/null +++ b/src/modules/connector/connector.controller.ts @@ -0,0 +1,210 @@ +import { + Body, + Controller, + Delete, + Get, + HttpException, + Param, + Put, + Post, + Query, + UploadedFile, + UseInterceptors, +} from '@nestjs/common'; +import { FileInterceptor } from '@nestjs/platform-express'; +import { ApiBearerAuth, ApiConsumes, ApiTags } from '@nestjs/swagger'; +import { ConnectorClientService } from 'src/clients/connector/client.service'; +import { AddTagDto } from './dtos/add-tag'; +import { CreateConnectorDto } from './dtos/create-connector'; +import { DeleteConnectorDto } from './dtos/delete-connector'; +import { GetAllDto } from './dtos/get-all'; +import { RemoveTagDto } from './dtos/remove-tag'; +import { UpdateDto } from './dtos/update'; +import { UploadFileDto } from './dtos/upload-file'; + +@ApiTags('connectors') +@ApiBearerAuth() +@Controller('connectors') +export class ConnectorController { + constructor(private connectorClientService: ConnectorClientService) {} + + @Post() + @ApiConsumes('multipart/form-data') + @UseInterceptors(FileInterceptor('file')) + async uploadConnector( + @UploadedFile() file, + @Body() body: CreateConnectorDto, + ) { + console.log(`/upload`, 'Upload Connector Route'); + + const response = await this.connectorClientService.uploadConnector({ + file, + connector: JSON.parse(body.connector), + }); + + return response; + } + + @Post('/upload') + @ApiConsumes('multipart/form-data') + @UseInterceptors(FileInterceptor('file')) + async uploadFile(@UploadedFile() file, @Body() { name }: UploadFileDto) { + console.log(`/upload`, 'Upload Connector Route'); + + const response = await this.connectorClientService.uploadFile({ + file, + name, + }); + + return response; + } + + @Get() + async getAllConnectors(@Query() queries: GetAllDto) { + console.log(`/upload`, 'Upload Connector Route'); + + const { filters, search } = queries; + + const response: any = await this.connectorClientService.getAllConnectors({ + filters: filters || {}, + search: search || '', + }); + + const connectors = JSON.parse(response.connectors).connectors; + + return { + message: response.message, + connectors, + }; + } + + @Get('/:plugin') + async getConnector( + @Param('plugin') plugin: string, + @Query('version') version: string, + ) { + console.log(`/upload`, 'Upload Connector Route'); + + const pluginId = `${plugin}-${version}`; + + const response: any = await this.connectorClientService.getConnector( + pluginId, + ); + + return { + message: response.message || 'ok', + connector: { ...JSON.parse(response.connector) }, + }; + } + + @Get('/:plugin/details') + async getConnectorDetails( + @Param('plugin') plugin: string, + @Query('version') version: string, + ) { + console.log(`/upload`, 'Upload Connector Route'); + + const pluginId = `${plugin}-${version}`; + + const response: any = await this.connectorClientService.getConnectorDetails( + pluginId, + ); + + return { + message: response.message || 'ok', + connector: { ...JSON.parse(response.connector) }, + }; + } + + @Put('/:plugin') + @ApiConsumes('multipart/form-data') + async updateConnector( + @Param('plugin') plugin: string, + @Body() body: UpdateDto, + ) { + console.log(`/upload`, 'Upload Connector Route'); + + const changes = body; + + const response: any = await this.connectorClientService.updateConnector({ + plugin, + changes: JSON.stringify(changes), + }); + + return { + message: response.message || 'ok', + connector: { ...JSON.parse(response.connector) }, + }; + } + + @Put('/:plugin/add-tag') + async addTagOnConnector( + @Param('plugin') plugin: string, + @Body() body: AddTagDto, + ) { + console.log(`/upload`, 'Upload Connector Route'); + + const { tags } = body; + + if (!tags || !(typeof tags === 'object') || !tags.length) { + throw new HttpException('Not found tags attributes', 400); + } + + const changes = { plugin, tags }; + + const response: any = await this.connectorClientService.updateConnector({ + plugin, + changes: JSON.stringify(changes), + }); + + return { + message: response.message || 'ok', + connector: { ...JSON.parse(response.connector) }, + }; + } + + @Put('/:plugin/remove-tag') + async removeTagOnConnector( + @Param('plugin') plugin: string, + @Body() body: RemoveTagDto, + ) { + console.log(`/upload`, 'Upload Connector Route'); + + const { tags } = body; + const remove = tags; + + if (!remove || !(typeof remove === 'object') || !remove.length) { + throw new HttpException('Not found remove attribute', 400); + } + + const changes = { plugin, remove }; + + const response: any = await this.connectorClientService.updateConnector({ + plugin, + changes: JSON.stringify(changes), + }); + + return { + message: response.message || 'ok', + connector: { ...JSON.parse(response.connector) }, + }; + } + + @Delete('/:plugin') + async deleteConnector( + @Param('plugin') plugin: string, + @Body() { version }: DeleteConnectorDto, + ) { + console.log(`/upload`, 'Upload Connector Route'); + + const response: any = await this.connectorClientService.deleteConnector( + plugin, + version, + ); + + return { + message: response.message || 'ok', + connector: { ...JSON.parse(response.connector) }, + }; + } +} diff --git a/src/modules/connector/dtos/add-tag.ts b/src/modules/connector/dtos/add-tag.ts new file mode 100644 index 0000000..7c04a23 --- /dev/null +++ b/src/modules/connector/dtos/add-tag.ts @@ -0,0 +1,6 @@ +import { ApiProperty } from '@nestjs/swagger'; + +export class AddTagDto { + @ApiProperty() + tags: string[]; +} diff --git a/src/modules/connector/dtos/create-connector.ts b/src/modules/connector/dtos/create-connector.ts new file mode 100644 index 0000000..2cdc4d6 --- /dev/null +++ b/src/modules/connector/dtos/create-connector.ts @@ -0,0 +1,9 @@ +import { ApiProperty } from '@nestjs/swagger'; + +export class CreateConnectorDto { + @ApiProperty({ format: 'binary' }) + file: string; + + @ApiProperty() + connector: string; +} diff --git a/src/modules/connector/dtos/delete-connector.ts b/src/modules/connector/dtos/delete-connector.ts new file mode 100644 index 0000000..6db5547 --- /dev/null +++ b/src/modules/connector/dtos/delete-connector.ts @@ -0,0 +1,6 @@ +import { ApiProperty } from '@nestjs/swagger'; + +export class DeleteConnectorDto { + @ApiProperty() + version: string; +} diff --git a/src/modules/connector/dtos/get-all.ts b/src/modules/connector/dtos/get-all.ts new file mode 100644 index 0000000..f5e3469 --- /dev/null +++ b/src/modules/connector/dtos/get-all.ts @@ -0,0 +1,9 @@ +import { ApiProperty } from '@nestjs/swagger'; + +export class GetAllDto { + @ApiProperty() + search: string; + + @ApiProperty() + filters: object; +} diff --git a/src/modules/connector/dtos/remove-tag.ts b/src/modules/connector/dtos/remove-tag.ts new file mode 100644 index 0000000..9de619b --- /dev/null +++ b/src/modules/connector/dtos/remove-tag.ts @@ -0,0 +1,6 @@ +import { ApiProperty } from '@nestjs/swagger'; + +export class RemoveTagDto { + @ApiProperty() + tags: string[]; +} diff --git a/src/modules/connector/dtos/update.ts b/src/modules/connector/dtos/update.ts new file mode 100644 index 0000000..5262fde --- /dev/null +++ b/src/modules/connector/dtos/update.ts @@ -0,0 +1,42 @@ +import { ApiProperty } from '@nestjs/swagger'; + +export class UpdateDto { + @ApiProperty({ format: 'binary', required: false }) + file?: string; + + @ApiProperty() + version: string; + + @ApiProperty({ required: false }) + category?: string; + + @ApiProperty({ required: false }) + plugin?: string; + + @ApiProperty({ required: false }) + image?: string; + + @ApiProperty({ required: false }) + docs?: string; + + @ApiProperty({ required: false }) + name?: string; + + @ApiProperty({ + required: false, + description: 'skip_select_entities: boolean skip_select_columns: boolean;', + }) + options?: { + skip_select_entities: boolean; + skip_select_columns: boolean; + }; + + @ApiProperty({ + required: false, + type: 'array', + items: { + properties: {}, + }, + }) + connection_controls?: object[]; +} diff --git a/src/modules/connector/dtos/upload-file.ts b/src/modules/connector/dtos/upload-file.ts new file mode 100644 index 0000000..d167f3f --- /dev/null +++ b/src/modules/connector/dtos/upload-file.ts @@ -0,0 +1,9 @@ +import { ApiProperty } from '@nestjs/swagger'; + +export class UploadFileDto { + @ApiProperty({ format: 'binary' }) + file: string; + + @ApiProperty() + name: string; +} diff --git a/swagger_empty.json b/swagger_empty.json new file mode 100644 index 0000000..d0eb1b4 --- /dev/null +++ b/swagger_empty.json @@ -0,0 +1,17 @@ +{ + "openapi": "3.0.0", + "paths": { + }, + "info": { + "title": "404", + "description": "", + "version": "", + "contact": {} + }, + "tags": [], + "servers": [], + "components": { + + "schemas": {} + } +} diff --git a/tsconfig.json b/tsconfig.json index 3923786..f20e56f 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -17,6 +17,7 @@ "noImplicitAny": false, "strictBindCallApply": false, "forceConsistentCasingInFileNames": false, - "noFallthroughCasesInSwitch": false + "noFallthroughCasesInSwitch": false, + "resolveJsonModule": true } }