diff --git a/src/app.module.ts b/src/app.module.ts index 575f6d8..d9b13dc 100644 --- a/src/app.module.ts +++ b/src/app.module.ts @@ -43,6 +43,7 @@ import { PermissionsModule } from './modules/permissions/permissions.module'; import { DadosferaLogger } from 'dadosfera-logs'; import { TermsOfUseModule } from './modules/termsOfUse/termsOfUse.module'; import { ConnectionModule } from './modules/connection/connection.module'; +import { ConnectionTestModule } from './modules/connection-test/connection-test.module'; const inputClient = new InputsClientConfiguration(); const outputClient = new OutputsClientConfiguration(); @@ -111,6 +112,8 @@ const transformationClient = new TransformationsClientConfiguration(); ...pipelineClient.config(), }, ]), + + ConnectionTestModule, ], }) export class AppModule {} diff --git a/src/modules/connection-test/connection-test-client.config.ts b/src/modules/connection-test/connection-test-client.config.ts new file mode 100644 index 0000000..7971bfb --- /dev/null +++ b/src/modules/connection-test/connection-test-client.config.ts @@ -0,0 +1,30 @@ +import { credentials } from '@grpc/grpc-js'; +import { + ClientProviderOptions, + GrpcOptions, + Transport, +} from '@nestjs/microservices'; +import { ConnectionTest } from 'protospack-v2'; + +export class ConnectionTestClientConfiguration { + private config: GrpcOptions = { + transport: Transport.GRPC, + options: { + url: process.env.INFACTORY_URL, + package: [ConnectionTest.ProtoPackages.ReadPackage], + credentials: + process.env.ENV === 'local' ? undefined : credentials.createSsl(), + protoPath: [ConnectionTest.ProtoPaths.ReadFilePath], + loader: { + keepCase: true, + enums: String, + objects: true, + arrays: true, + }, + }, + }; + providerOptions: ClientProviderOptions = { + name: 'ConnectionTestGrpcClient', + ...this.config, + }; +} diff --git a/src/modules/connection-test/connection-test.controller.spec.ts b/src/modules/connection-test/connection-test.controller.spec.ts new file mode 100644 index 0000000..efe3fe0 --- /dev/null +++ b/src/modules/connection-test/connection-test.controller.spec.ts @@ -0,0 +1,18 @@ +import { Test, TestingModule } from '@nestjs/testing'; +import { ConnectionTestController } from './connection-test.controller'; + +describe('ConnectionTestController', () => { + let controller: ConnectionTestController; + + beforeEach(async () => { + const module: TestingModule = await Test.createTestingModule({ + controllers: [ConnectionTestController], + }).compile(); + + controller = module.get(ConnectionTestController); + }); + + it('should be defined', () => { + expect(controller).toBeDefined(); + }); +}); diff --git a/src/modules/connection-test/connection-test.controller.ts b/src/modules/connection-test/connection-test.controller.ts new file mode 100644 index 0000000..95ed102 --- /dev/null +++ b/src/modules/connection-test/connection-test.controller.ts @@ -0,0 +1,125 @@ +import { + Body, + Controller, + HttpCode, + HttpStatus, + Inject, + Post, + ValidationPipe, +} from '@nestjs/common'; +import { ApiOkResponse, ApiTags } from '@nestjs/swagger'; +import { User, RequestUser } from 'src/authentication/user.decorator'; +import { ConnectionTestService } from './connection-test.service'; +import { + ConnectionTestPingRes, + ConnectionTestPingReq, + ConnectionTestCredentialsReq, + ConnectionTestCredentialsRes, + ConnectionTestListSchemasReq, + ConnectionTestListSchemasRes, + ConnectionTestListTablesReq, + ConnectionTestListTablesRes, + GetTableMetadataRes, + GetTableMetadataReq, +} from './dto/connection-test'; +import { DadosferaLogger } from 'dadosfera-logs'; +import { Authenticated } from 'src/authentication/authentication.decorator'; + +@ApiTags('Connection Test') +@Controller('connection-test') +@Authenticated() +export class ConnectionTestController { + logger: any; + constructor( + private connectionTestService: ConnectionTestService, + @Inject(DadosferaLogger) + dadosferaLogger: DadosferaLogger, + ) { + this.logger = dadosferaLogger.logger; + } + + @Post('ping') + @ApiOkResponse({ type: ConnectionTestPingRes }) + @HttpCode(HttpStatus.OK) + async connectionTestPing( + @User() user: RequestUser, + @Body(new ValidationPipe()) body: ConnectionTestPingReq, + ) { + this.logger.info('/connection-test/ping', { + user: user.user_id, + customer: user.customer_name, + }); + return this.connectionTestService.connectionTestPing( + body, + user.customer_name, + ); + } + + @Post('credentials') + @ApiOkResponse({ type: ConnectionTestCredentialsRes }) + @HttpCode(HttpStatus.OK) + async connectionTestCredentials( + @User() user: RequestUser, + @Body(new ValidationPipe()) body: ConnectionTestCredentialsReq, + ) { + this.logger.info('/connection-test/credentials', { + user: user.user_id, + customer: user.customer_name, + }); + return this.connectionTestService.connectionTestCredentials( + body, + user.customer_name, + ); + } + + @Post('schemas') + @ApiOkResponse({ type: ConnectionTestListSchemasRes }) + @HttpCode(HttpStatus.OK) + async connectionTestListSchemas( + @User() user: RequestUser, + @Body(new ValidationPipe()) body: ConnectionTestListSchemasReq, + ) { + this.logger.info('/connection-test/schemas', { + user: user.user_id, + customer: user.customer_name, + }); + return this.connectionTestService.connectionTestListSchemas( + body, + user.customer_name, + ); + } + + @Post('tables') + @ApiOkResponse({ type: ConnectionTestListTablesRes }) + @HttpCode(HttpStatus.OK) + async connectionTestListTables( + @User() user: RequestUser, + @Body(new ValidationPipe()) body: ConnectionTestListTablesReq, + ) { + this.logger.info('/connection-test/tables', { + user: user.user_id, + customer: user.customer_name, + }); + return this.connectionTestService.connectionTestListTables( + body, + user.customer_name, + ); + } + + @Post('table-metadata') + @ApiOkResponse({ type: GetTableMetadataRes }) + @HttpCode(HttpStatus.OK) + async getTableMetadata( + @User() user: RequestUser, + @Body(new ValidationPipe()) body: GetTableMetadataReq, + ) { + this.logger.info('/connection-test/tables', { + user: user.user_id, + customer: user.customer_name, + }); + return this.connectionTestService.getTableMetadata( + body, + user.customer_name, + ); + } +} diff --git a/src/modules/connection-test/connection-test.module.ts b/src/modules/connection-test/connection-test.module.ts new file mode 100644 index 0000000..687df06 --- /dev/null +++ b/src/modules/connection-test/connection-test.module.ts @@ -0,0 +1,13 @@ +import { Module } from '@nestjs/common'; +import { ConnectionTestController } from './connection-test.controller'; +import { ConnectionTestService } from './connection-test.service'; +import { DadosferaLogger } from 'dadosfera-logs'; +import { ClientsModule } from '@nestjs/microservices'; +import { ConnectionTestClientConfiguration } from './connection-test-client.config'; +const client = new ConnectionTestClientConfiguration(); +@Module({ + controllers: [ConnectionTestController], + providers: [ConnectionTestService, DadosferaLogger], + imports: [ClientsModule.register([client.providerOptions])], +}) +export class ConnectionTestModule {} diff --git a/src/modules/connection-test/connection-test.service.spec.ts b/src/modules/connection-test/connection-test.service.spec.ts new file mode 100644 index 0000000..3b44f65 --- /dev/null +++ b/src/modules/connection-test/connection-test.service.spec.ts @@ -0,0 +1,18 @@ +import { Test, TestingModule } from '@nestjs/testing'; +import { ConnectionTestService } from './connection-test.service'; + +describe('ConnectionTestService', () => { + let service: ConnectionTestService; + + beforeEach(async () => { + const module: TestingModule = await Test.createTestingModule({ + providers: [ConnectionTestService], + }).compile(); + + service = module.get(ConnectionTestService); + }); + + it('should be defined', () => { + expect(service).toBeDefined(); + }); +}); diff --git a/src/modules/connection-test/connection-test.service.ts b/src/modules/connection-test/connection-test.service.ts new file mode 100644 index 0000000..bb29cf4 --- /dev/null +++ b/src/modules/connection-test/connection-test.service.ts @@ -0,0 +1,105 @@ +import { Inject, Injectable } from '@nestjs/common'; +import { ClientGrpc } from '@nestjs/microservices'; +import { ConnectionTest } from 'protospack-v2'; +import { lastValueFrom } from 'rxjs'; +import { + ConnectionTestCredentialsReq, + ConnectionTestCredentialsRes, + ConnectionTestListSchemasReq, + ConnectionTestListSchemasRes, + ConnectionTestListTablesReq, + ConnectionTestListTablesRes, + ConnectionTestPingReq, + ConnectionTestPingRes, + GetTableMetadataReq, + GetTableMetadataRes, +} from './dto/connection-test'; + +@Injectable() +export class ConnectionTestService { + private connectionTestReadClient: ConnectionTest.ReadService.ConnectionTestReadServices; + constructor( + @Inject('ConnectionTestGrpcClient') private readonly grpcClient: ClientGrpc, + ) { + this.connectionTestReadClient = + grpcClient.getService( + ConnectionTest.ProtoServices.ConnectionTestReadServices, + ); + } + + async connectionTestPing( + body: ConnectionTestPingReq, + customer_name: string, + ): Promise { + const { endpoint, plugin, port, network_config_id } = body; + return lastValueFrom( + this.connectionTestReadClient.ConnectionTestPing({ + customer_name, + endpoint, + network_config_id, + plugin, + port, + }), + ); + } + async connectionTestCredentials( + body: ConnectionTestCredentialsReq, + customer_name: string, + ): Promise { + const { connector_id, plugin } = body; + //CREATE CONNECTION + const connection_id = ''; + /***********************/ + const { operation_result } = await lastValueFrom( + this.connectionTestReadClient.ConnectionTestCredentials({ + connection_id, + customer_name, + plugin, + }), + ); + return { operation_result, connection_id }; + } + async connectionTestListSchemas( + body: ConnectionTestListSchemasReq, + customer_name: string, + ): Promise { + const { connection_id, plugin } = body; + return lastValueFrom( + this.connectionTestReadClient.ListSchemas({ + connection_id, + customer_name, + plugin, + }), + ); + } + async connectionTestListTables( + body: ConnectionTestListTablesReq, + customer_name: string, + ): Promise { + const { connection_id, plugin, schema } = body; + return lastValueFrom( + this.connectionTestReadClient.ListTables({ + connection_id, + customer_name, + plugin, + schema, + }), + ); + } + + async getTableMetadata( + body: GetTableMetadataReq, + customer_name: string, + ): Promise { + const { schema, plugin, table_list, connection_id } = body; + return lastValueFrom( + this.connectionTestReadClient.GetTableMetadata({ + connection_id, + customer_name, + plugin, + schema, + table_list, + }), + ); + } +} diff --git a/src/modules/connection-test/dto/connection-test.ts b/src/modules/connection-test/dto/connection-test.ts new file mode 100644 index 0000000..7deadc0 --- /dev/null +++ b/src/modules/connection-test/dto/connection-test.ts @@ -0,0 +1,120 @@ +import { ApiProperty, ApiPropertyOptional, OmitType } from '@nestjs/swagger'; +import { IsString, IsObject, IsOptional } from 'class-validator'; +import { DatabaseConnectionPropertiesDto } from 'src/modules/connection/dtos/connection'; +import { CreateConnectionDto } from 'src/modules/connection/dtos/create-connection'; +export class ColumnDto { + @ApiProperty() + name: string; + @ApiProperty() + type: string; +} +export class TableMetadataDto { + @ApiProperty() + table_name: string; + @ApiProperty({ type: [ColumnDto] }) + columns: ColumnDto[]; +} + +export class ConnectionTestPingReq { + @ApiProperty() + @IsString() + plugin: string; + + @ApiProperty() + @IsString() + endpoint: string; + + @ApiProperty() + @IsString() + port: string; + + @ApiPropertyOptional() + @IsOptional() + @IsString() + network_config_id?: string; +} + +export class ConnectionTestPingRes { + @ApiProperty() + operation_result: boolean; +} + +export class ConnectionTestCredentialsReq extends OmitType( + CreateConnectionDto, + ['properties'], +) { + @ApiProperty() + @IsString() + plugin: string; + @ApiProperty() + @IsObject() + properties: DatabaseConnectionPropertiesDto; +} + +export class ConnectionTestCredentialsRes { + @ApiProperty() + operation_result: boolean; + @ApiProperty() + connection_id: string; +} + +export class ConnectionTestListSchemasReq { + @ApiProperty() + @IsString() + plugin: string; + + @ApiProperty() + @IsString() + connection_id: string; +} + +export class ConnectionTestListSchemasRes { + @ApiProperty() + operation_result: boolean; + @ApiProperty() + schema_list: string[]; +} + +export class ConnectionTestListTablesReq { + @ApiProperty() + @IsString() + plugin: string; + + @ApiProperty() + @IsString() + connection_id: string; + + @ApiProperty() + @IsString() + schema: string; +} + +export class ConnectionTestListTablesRes { + @ApiProperty() + operation_result: boolean; + @ApiProperty() + table_list: string[]; +} + +export class GetTableMetadataReq { + @ApiProperty() + @IsString() + connection_id: string; + + @ApiProperty() + @IsString() + plugin: string; + + @ApiProperty() + @IsString() + schema: string; + + @ApiProperty() + @IsString({ each: true }) + table_list: string[]; +} + +export class GetTableMetadataRes { + @ApiProperty({ type: [TableMetadataDto] }) + table_metadata: TableMetadataDto[]; +} diff --git a/src/modules/connection/dtos/connection.ts b/src/modules/connection/dtos/connection.ts new file mode 100644 index 0000000..d68872c --- /dev/null +++ b/src/modules/connection/dtos/connection.ts @@ -0,0 +1,18 @@ +import { ApiProperty } from '@nestjs/swagger'; + +export class DatabaseConnectionPropertiesDto { + credentials_type: 'basic_auth'; + @ApiProperty() + plugin: string; + @ApiProperty() + host: string; + @ApiProperty() + port: string; + @ApiProperty() + username: string; + @ApiProperty() + @ApiProperty() + password: string; + @ApiProperty() + database: string; +} diff --git a/src/modules/connection/dtos/create-connection.ts b/src/modules/connection/dtos/create-connection.ts index f5db7b2..00e75be 100644 --- a/src/modules/connection/dtos/create-connection.ts +++ b/src/modules/connection/dtos/create-connection.ts @@ -1,4 +1,4 @@ -import { ApiProperty } from '@nestjs/swagger'; +import { ApiProperty, ApiPropertyOptional } from '@nestjs/swagger'; import { IsString, IsObject } from 'class-validator'; export class CreateConnectionDto { @@ -14,9 +14,9 @@ export class CreateConnectionDto { @IsString() description: string; - @ApiProperty() + @ApiPropertyOptional() @IsString() - network_config_id: string; + network_config_id?: string; @ApiProperty() @IsString()