From e1cfc6e1a8bbf5ae8c7ed8aa9a894dc468828f1d Mon Sep 17 00:00:00 2001 From: viniciusgadea Date: Tue, 21 Jul 2026 11:21:22 -0300 Subject: [PATCH] FEAT: add custom properties endpoints and DTOs for catalog management --- docsfera.json | 169 ++++++++++++++++++++++ package-lock.json | 9 +- package.json | 2 +- src/modules/catalog/catalog.controller.ts | 63 +++++++- src/modules/catalog/catalog.service.ts | 20 +++ src/modules/catalog/dtos/index.ts | 27 +++- 6 files changed, 282 insertions(+), 8 deletions(-) diff --git a/docsfera.json b/docsfera.json index e0aea4c..4fd0289 100644 --- a/docsfera.json +++ b/docsfera.json @@ -5538,6 +5538,148 @@ ] } }, + "/catalog/custom-properties": { + "get": { + "operationId": "CatalogController_getCustomPropertyDefinitions", + "parameters": [ + { + "name": "dadosfera-lang", + "in": "header", + "required": false, + "schema": { + "enum": [ + "pt-br", + "en-us" + ], + "type": "string" + } + } + ], + "responses": { + "200": { + "description": "", + "content": { + "application/json": { + "schema": { + "type": "object" + } + } + } + } + }, + "tags": [ + "Catalog" + ], + "security": [ + { + "access-token": [] + }, + { + "access-token": [] + } + ] + }, + "post": { + "operationId": "CatalogController_upsertCustomPropertyDefinition", + "parameters": [ + { + "name": "dadosfera-lang", + "in": "header", + "required": false, + "schema": { + "enum": [ + "pt-br", + "en-us" + ], + "type": "string" + } + } + ], + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/CustomPropertyDefinitionDto" + } + } + } + }, + "responses": { + "201": { + "description": "", + "content": { + "application/json": { + "schema": { + "type": "object" + } + } + } + } + }, + "tags": [ + "Catalog" + ], + "security": [ + { + "access-token": [] + }, + { + "access-token": [] + } + ] + } + }, + "/catalog/custom-properties/{key}": { + "delete": { + "operationId": "CatalogController_deleteCustomPropertyDefinition", + "parameters": [ + { + "name": "dadosfera-lang", + "in": "header", + "required": false, + "schema": { + "enum": [ + "pt-br", + "en-us" + ], + "type": "string" + } + }, + { + "name": "key", + "required": true, + "in": "path", + "schema": { + "type": "string" + } + } + ], + "responses": { + "200": { + "description": "", + "content": { + "application/json": { + "schema": { + "type": "object" + } + } + } + } + }, + "tags": [ + "Catalog" + ], + "security": [ + { + "access-token": [] + }, + { + "access-token": [] + } + ] + } + }, "/catalog/data-asset/{id}": { "get": { "operationId": "CatalogController_getDataAsset", @@ -10851,6 +10993,30 @@ "total" ] }, + "CustomPropertyDefinitionDto": { + "type": "object", + "properties": { + "key": { + "type": "string" + }, + "name": { + "type": "string" + }, + "type": { + "type": "string", + "enum": [ + "text", + "number", + "boolean", + "date" + ] + } + }, + "required": [ + "name", + "type" + ] + }, "IColumnMetadata": { "type": "object", "properties": { @@ -11073,6 +11239,9 @@ }, "docs": { "type": "string" + }, + "custom_properties": { + "type": "object" } }, "required": [ diff --git a/package-lock.json b/package-lock.json index dcb4ea9..eb4a4c9 100644 --- a/package-lock.json +++ b/package-lock.json @@ -16,7 +16,7 @@ "@aws-sdk/lib-dynamodb": "^3.414.0", "@aws-sdk/signature-v4": "^3.370.0", "@dadosfera/dadosfera-logs": "^1.0.0-beta.4", - "@dadosfera/protospack-v2": "^3.40.0-beta.10", + "@dadosfera/protospack-v2": "^3.40.0-beta.12", "@grpc/grpc-js": "^1.9.3", "@grpc/proto-loader": "^0.7.9", "@nestjs/cli": "^9.5.0", @@ -1735,9 +1735,10 @@ } }, "node_modules/@dadosfera/protospack-v2": { - "version": "3.40.0-beta.10", - "resolved": "https://dadosfera-611330257153.d.codeartifact.us-east-1.amazonaws.com/npm/dadosfera-npm/@dadosfera/protospack-v2/-/protospack-v2-3.40.0-beta.10.tgz", - "integrity": "sha512-F45dSEIKG+gwwDMYHayA242bFwhFTJbZm26KesQbGhf4I45ur6hYZD8dK0voNuVQInLMQhtm6+7th6/jJ8xpTQ==", + "version": "3.40.0-beta.12", + "resolved": "https://dadosfera-611330257153.d.codeartifact.us-east-1.amazonaws.com/npm/dadosfera-npm/@dadosfera/protospack-v2/-/protospack-v2-3.40.0-beta.12.tgz", + "integrity": "sha512-nmam/ZJK5ecZlrh/yrMw+C0cHy3bH4NXthKt5csePX9Y/Sgri7tPwPScz9QHQh2Ux2eLflhIcFI6lW2jvOrGNQ==", + "license": "ISC", "dependencies": { "@grpc/grpc-js": "^1.9.3", "rxjs": "^7.5.5" diff --git a/package.json b/package.json index fdbc61e..346950d 100644 --- a/package.json +++ b/package.json @@ -34,7 +34,7 @@ "@aws-sdk/lib-dynamodb": "^3.414.0", "@aws-sdk/signature-v4": "^3.370.0", "@dadosfera/dadosfera-logs": "^1.0.0-beta.4", - "@dadosfera/protospack-v2": "^3.40.0-beta.10", + "@dadosfera/protospack-v2": "^3.40.0-beta.12", "@grpc/grpc-js": "^1.9.3", "@grpc/proto-loader": "^0.7.9", "@nestjs/cli": "^9.5.0", diff --git a/src/modules/catalog/catalog.controller.ts b/src/modules/catalog/catalog.controller.ts index 308a082..f8a9c95 100644 --- a/src/modules/catalog/catalog.controller.ts +++ b/src/modules/catalog/catalog.controller.ts @@ -37,6 +37,7 @@ import { PackTheMetadata } from 'src/utils/PackTheMetadata'; import { RequestUser, User } from 'src/decorators/user.decorator'; import { BatchRemoveRlsRulesRequest, + CustomPropertyDefinitionDto, GetDatasetCatalogTaskRes, ICatalogAllRequest, ICatalogAllResponse, @@ -271,6 +272,66 @@ export class CatalogController { } + @Get('custom-properties') + @RequireSomePermission( + PERMISSIONS_GROUPS.CATALOG.permissions.GET, + PERMISSIONS_GROUPS.CATALOG.permissions.DATA_MANAGER, + ) + async getCustomPropertyDefinitions(@User() user: RequestUser) { + const { customer_id, customer_name, user_id, username } = user; + const metadata = PackTheMetadata({ + customer_id, + customer_name, + user_id, + username, + }); + + return this.catalogService.getCustomPropertyDefinitions(metadata); + } + + @Post('custom-properties') + @RequireAllPermissions(PERMISSIONS_GROUPS.CATALOG.permissions.DATA_MANAGER) + async upsertCustomPropertyDefinition( + @User() user: RequestUser, + @Body() definition: CustomPropertyDefinitionDto, + ) { + const { customer_id, customer_name, user_id, username } = user; + const metadata = PackTheMetadata({ + customer_id, + customer_name, + user_id, + username, + }); + + return this.catalogService.upsertCustomPropertyDefinition( + { + definition: { + key: definition.key || '', + name: definition.name, + type: definition.type, + }, + }, + metadata, + ); + } + + @Delete('custom-properties/:key') + @RequireAllPermissions(PERMISSIONS_GROUPS.CATALOG.permissions.DATA_MANAGER) + async deleteCustomPropertyDefinition( + @User() user: RequestUser, + @Param('key') key: string, + ) { + const { customer_id, customer_name, user_id, username } = user; + const metadata = PackTheMetadata({ + customer_id, + customer_name, + user_id, + username, + }); + + return this.catalogService.deleteCustomPropertyDefinition({ key }, metadata); + } + @Get('data-asset/:id') @RequireSomePermission( PERMISSIONS_GROUPS.CATALOG.permissions.GET, @@ -1002,4 +1063,4 @@ export class CatalogController { this.logger.error(error.message); } } -} \ No newline at end of file +} diff --git a/src/modules/catalog/catalog.service.ts b/src/modules/catalog/catalog.service.ts index f0fced8..6f38e41 100644 --- a/src/modules/catalog/catalog.service.ts +++ b/src/modules/catalog/catalog.service.ts @@ -35,9 +35,11 @@ import { } from './dtos'; import { AddRlsRuleRequest, + DeleteCustomPropertyDefinitionRequest, GetNimbusDashboardsRequest, GetRlsRulesRequest, PiiMetadata, + UpsertCustomPropertyDefinitionRequest, } from '@dadosfera/protospack-v2/dist/lib/Catalog/interfaces/messages'; import { TypeParser } from 'src/utils/FileParser/parser-types'; import { ParserBuilder } from 'src/utils/FileParser/parser.builder'; @@ -120,6 +122,24 @@ class CatalogService implements OnModuleInit { } } + async getCustomPropertyDefinitions(metadata: Metadata) { + return lastValueFrom(this.catalogReadService.GetCustomPropertyDefinitions({}, metadata)); + } + + async upsertCustomPropertyDefinition( + data: UpsertCustomPropertyDefinitionRequest, + metadata: Metadata, + ) { + return lastValueFrom(this.catalogWriteService.UpsertCustomPropertyDefinition(data, metadata)); + } + + async deleteCustomPropertyDefinition( + data: DeleteCustomPropertyDefinitionRequest, + metadata: Metadata, + ) { + return lastValueFrom(this.catalogWriteService.DeleteCustomPropertyDefinition(data, metadata)); + } + async createDataAsset(data: Messages.CreateDataAssetRequest, metadata) { this.logger.info('CatalogService - Manage Data assets permissions'); if (!data.embed) data.embed = undefined; diff --git a/src/modules/catalog/dtos/index.ts b/src/modules/catalog/dtos/index.ts index 770b01c..aa87f21 100644 --- a/src/modules/catalog/dtos/index.ts +++ b/src/modules/catalog/dtos/index.ts @@ -1,5 +1,10 @@ import { ApiProperty, ApiPropertyOptional, PickType } from '@nestjs/swagger'; -import { IsEnum } from 'class-validator'; +import { + IsEnum, + IsNotEmpty, + IsOptional, + IsString, +} from 'class-validator'; import { CreateDataAssetRequest } from '@dadosfera/protospack-v2/dist/lib/Catalog/interfaces/messages'; export enum DataAssetShareType { @@ -198,6 +203,22 @@ export class IData { day_opening: number; } +export type CustomPropertyType = 'text' | 'number' | 'boolean' | 'date'; + +export class CustomPropertyDefinitionDto { + @ApiPropertyOptional() + @IsOptional() + @IsString() + key?: string; + @ApiProperty() + @IsString() + @IsNotEmpty() + name: string; + @ApiProperty({ enum: ['text', 'number', 'boolean', 'date'] }) + @IsEnum(['text', 'number', 'boolean', 'date']) + type: CustomPropertyType; +} + export class IUpdateDataRequest { @ApiProperty() name: string; @@ -211,6 +232,8 @@ export class IUpdateDataRequest { share_type?: DataAssetShareType; @ApiPropertyOptional() docs?: string; + @ApiPropertyOptional() + custom_properties?: Record; } export class IUpdateCertificationStatusRequest { @@ -372,4 +395,4 @@ export type CreateDataDocsDTO = { docs: string; asset_type: string; -} \ No newline at end of file +}