From e1cfc6e1a8bbf5ae8c7ed8aa9a894dc468828f1d Mon Sep 17 00:00:00 2001 From: viniciusgadea Date: Tue, 21 Jul 2026 11:21:22 -0300 Subject: [PATCH 1/3] 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 +} From a93fbfbbd9ff0e7143f62c5303d5cf480b2164df Mon Sep 17 00:00:00 2001 From: viniciusgadea Date: Tue, 21 Jul 2026 16:32:38 -0300 Subject: [PATCH 2/3] FEAT: remove custom properties endpoints and DTOs from catalog management --- docsfera.json | 124 ---------------------- package-lock.json | 8 +- package.json | 2 +- src/modules/catalog/catalog.controller.ts | 44 -------- src/modules/catalog/catalog.service.ts | 16 --- src/modules/catalog/dtos/index.ts | 17 +-- 6 files changed, 6 insertions(+), 205 deletions(-) diff --git a/docsfera.json b/docsfera.json index 4fd0289..08320c6 100644 --- a/docsfera.json +++ b/docsfera.json @@ -5578,106 +5578,6 @@ "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}": { @@ -10993,30 +10893,6 @@ "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": { diff --git a/package-lock.json b/package-lock.json index eb4a4c9..0ce8dc9 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.12", + "@dadosfera/protospack-v2": "^3.40.0-beta.13", "@grpc/grpc-js": "^1.9.3", "@grpc/proto-loader": "^0.7.9", "@nestjs/cli": "^9.5.0", @@ -1735,9 +1735,9 @@ } }, "node_modules/@dadosfera/protospack-v2": { - "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==", + "version": "3.40.0-beta.13", + "resolved": "https://dadosfera-611330257153.d.codeartifact.us-east-1.amazonaws.com/npm/dadosfera-npm/@dadosfera/protospack-v2/-/protospack-v2-3.40.0-beta.13.tgz", + "integrity": "sha512-XYrq29fG0yddQAid6wnuD1n2X9NsXQiHpItngfLL0bXZZBP0ufgiotiEKmOsDtb3Z+apzGJVK9dJ9YduvmVzlQ==", "license": "ISC", "dependencies": { "@grpc/grpc-js": "^1.9.3", diff --git a/package.json b/package.json index 346950d..7d0c1dd 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.12", + "@dadosfera/protospack-v2": "^3.40.0-beta.13", "@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 f8a9c95..e5d7d00 100644 --- a/src/modules/catalog/catalog.controller.ts +++ b/src/modules/catalog/catalog.controller.ts @@ -37,7 +37,6 @@ import { PackTheMetadata } from 'src/utils/PackTheMetadata'; import { RequestUser, User } from 'src/decorators/user.decorator'; import { BatchRemoveRlsRulesRequest, - CustomPropertyDefinitionDto, GetDatasetCatalogTaskRes, ICatalogAllRequest, ICatalogAllResponse, @@ -289,49 +288,6 @@ export class CatalogController { 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, diff --git a/src/modules/catalog/catalog.service.ts b/src/modules/catalog/catalog.service.ts index 6f38e41..e6e9d18 100644 --- a/src/modules/catalog/catalog.service.ts +++ b/src/modules/catalog/catalog.service.ts @@ -35,11 +35,9 @@ 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'; @@ -126,20 +124,6 @@ class CatalogService implements OnModuleInit { 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 aa87f21..00c641b 100644 --- a/src/modules/catalog/dtos/index.ts +++ b/src/modules/catalog/dtos/index.ts @@ -203,21 +203,6 @@ 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() @@ -233,7 +218,7 @@ export class IUpdateDataRequest { @ApiPropertyOptional() docs?: string; @ApiPropertyOptional() - custom_properties?: Record; + custom_properties?: Record; } export class IUpdateCertificationStatusRequest { From cffda86eab35de3680877c7200ba2f8025c20bac Mon Sep 17 00:00:00 2001 From: viniciusgadea Date: Wed, 22 Jul 2026 10:51:53 -0300 Subject: [PATCH 3/3] FEAT: add CustomPropertyDto and update IUpdateDataRequest to use custom properties array --- docsfera.json | 30 +++++++++++++++++++++++++++++- src/modules/catalog/dtos/index.ts | 20 ++++++++++++++++++-- 2 files changed, 47 insertions(+), 3 deletions(-) diff --git a/docsfera.json b/docsfera.json index 08320c6..d758028 100644 --- a/docsfera.json +++ b/docsfera.json @@ -11087,6 +11087,31 @@ "docs" ] }, + "CustomPropertyDto": { + "type": "object", + "properties": { + "key": { + "type": "string" + }, + "value": { + "type": "string" + }, + "type": { + "type": "string", + "enum": [ + "text", + "number", + "date", + "boolean" + ] + } + }, + "required": [ + "key", + "value", + "type" + ] + }, "IUpdateDataRequest": { "type": "object", "properties": { @@ -11117,7 +11142,10 @@ "type": "string" }, "custom_properties": { - "type": "object" + "type": "array", + "items": { + "$ref": "#/components/schemas/CustomPropertyDto" + } } }, "required": [ diff --git a/src/modules/catalog/dtos/index.ts b/src/modules/catalog/dtos/index.ts index 00c641b..aa2ee07 100644 --- a/src/modules/catalog/dtos/index.ts +++ b/src/modules/catalog/dtos/index.ts @@ -204,6 +204,22 @@ export class IData { } +export enum CustomPropertyType { + TEXT = 'text', + NUMBER = 'number', + DATE = 'date', + BOOLEAN = 'boolean', +} + +export class CustomPropertyDto { + @ApiProperty() + key: string; + @ApiProperty() + value: string; + @ApiProperty({ enum: CustomPropertyType }) + type: CustomPropertyType; +} + export class IUpdateDataRequest { @ApiProperty() name: string; @@ -217,8 +233,8 @@ export class IUpdateDataRequest { share_type?: DataAssetShareType; @ApiPropertyOptional() docs?: string; - @ApiPropertyOptional() - custom_properties?: Record; + @ApiPropertyOptional({ type: [CustomPropertyDto] }) + custom_properties?: CustomPropertyDto[]; } export class IUpdateCertificationStatusRequest {