Compare commits

..
Author SHA1 Message Date
RafaelandClaude b736cddf07 FIX: Update ValidationPipe for class-validator 0.14.0+ compatibility
This fix addresses a breaking change introduced in class-validator 0.14.0
where the default for forbidUnknownValues changed from false to true.

Issue:
- POST /connections was returning 400 "an unknown value was passed to the
  validate function" errors
- This occurred because CreateConnectionDto and UpdateConnectionDto have no
  validation decorators, causing class-validator 0.14.0+ to treat them as
  "unknown values"
- Extra fields (like connector_version) in request payloads would fail
  validation

Solution:
- Set forbidUnknownValues: false to allow DTOs without validation decorators
- Set whitelist: true to automatically strip extra properties not defined
  in DTOs
- This maintains backward compatibility while adding security by removing
  unexpected fields

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2026-01-21 14:43:43 -03:00
Marcos Rodrigues Silva 007f3911ff Merge pull request #429 from dadosfera/beta
Beta
2026-01-05 17:43:34 -03:00
9 changed files with 21 additions and 185 deletions
+7 -67
View File
@@ -5461,66 +5461,6 @@
]
}
},
"/customers/{id}/organization-info": {
"get": {
"operationId": "CustomersController_getOrganizationInfo",
"parameters": [
{
"name": "id",
"required": true,
"in": "path",
"schema": {
"type": "string"
}
}
],
"responses": {
"200": {
"description": "Organization information"
}
},
"tags": [
"Customers"
],
"security": [
{
"access-token": []
},
{
"access-token": []
}
]
},
"put": {
"operationId": "CustomersController_updateOrganizationInfo",
"parameters": [
{
"name": "id",
"required": true,
"in": "path",
"schema": {
"type": "string"
}
}
],
"responses": {
"200": {
"description": "Organization information updated"
}
},
"tags": [
"Customers"
],
"security": [
{
"access-token": []
},
{
"access-token": []
}
]
}
},
"/catalog/data-asset/share/{id}": {
"get": {
"operationId": "ShareController_getShareDataAsset",
@@ -8229,7 +8169,7 @@
"companyName": {
"type": "string"
},
"personalSite": {
"companySite": {
"type": "string"
},
"mfaStatus": {
@@ -8264,7 +8204,7 @@
"hierarchy",
"bio",
"companyName",
"personalSite",
"companySite",
"mfaStatus",
"createdAt",
"updatedAt",
@@ -8404,7 +8344,7 @@
"companyName": {
"type": "string"
},
"personalSite": {
"companySite": {
"type": "string"
},
"customer": {
@@ -8436,7 +8376,7 @@
"hierarchy",
"bio",
"companyName",
"personalSite",
"companySite",
"mfaStatus",
"createdAt",
"updatedAt",
@@ -8580,7 +8520,7 @@
"bio": {
"type": "string"
},
"personalSite": {
"companySite": {
"type": "string"
},
"companyName": {
@@ -8632,7 +8572,7 @@
"companyName": {
"type": "string"
},
"personalSite": {
"companySite": {
"type": "string"
},
"mfaStatus": {
@@ -8660,7 +8600,7 @@
"hierarchy",
"bio",
"companyName",
"personalSite",
"companySite",
"mfaStatus",
"createdAt",
"updatedAt",
+5 -4
View File
@@ -17,7 +17,7 @@
"@aws-sdk/signature-v4": "^3.370.0",
"@dadosfera/dadosfera-logs": "^1.0.0-beta.4",
"@dadosfera/protospack": "2.5.3",
"@dadosfera/protospack-v2": "^3.38.0-beta.25",
"@dadosfera/protospack-v2": "3.38.0-beta.19",
"@grpc/grpc-js": "^1.9.3",
"@grpc/proto-loader": "^0.7.9",
"@nestjs/cli": "^9.5.0",
@@ -1744,9 +1744,10 @@
}
},
"node_modules/@dadosfera/protospack-v2": {
"version": "3.38.0-beta.25",
"resolved": "https://dadosfera-611330257153.d.codeartifact.us-east-1.amazonaws.com/npm/dadosfera-npm/@dadosfera/protospack-v2/-/protospack-v2-3.38.0-beta.25.tgz",
"integrity": "sha512-OsOl9hJezmtWlVy+2vl3qbWP5yHmLktJ01r5aL475lcfKJtzFPZieUT9ES87C1Emrqe21E6IM/BkJK2rm/aUiA==",
"version": "3.38.0-beta.19",
"resolved": "https://dadosfera-611330257153.d.codeartifact.us-east-1.amazonaws.com/npm/dadosfera-npm/@dadosfera/protospack-v2/-/protospack-v2-3.38.0-beta.19.tgz",
"integrity": "sha512-qevGunl1IKv4POa2hhXNaDpqlh/bvnq5Hxo6534OHYPZ//S+XcRZgTrxFnylK3+uoROM8epF3Mey5GwynxY7Tw==",
"license": "ISC",
"dependencies": {
"@grpc/grpc-js": "^1.9.3",
"rxjs": "^7.5.5"
+1 -1
View File
@@ -35,7 +35,7 @@
"@aws-sdk/signature-v4": "^3.370.0",
"@dadosfera/dadosfera-logs": "^1.0.0-beta.4",
"@dadosfera/protospack": "2.5.3",
"@dadosfera/protospack-v2": "^3.38.0-beta.25",
"@dadosfera/protospack-v2": "3.38.0-beta.19",
"@grpc/grpc-js": "^1.9.3",
"@grpc/proto-loader": "^0.7.9",
"@nestjs/cli": "^9.5.0",
@@ -136,32 +136,4 @@ export class CustomersController {
const result = await this.customersService.getAccessDashboardUrl(user.customer_name, metadata);
return result;
}
@Get(':id/organization-info')
@Authenticated()
@RequireAllPermissions(PERMISSIONS_GROUPS.USERS.permissions.ADMIN)
@ApiOkResponse({ description: 'Organization information' })
async getOrganizationInfo(@Param('id') id: string) {
this.logger.info('getOrganizationInfo', { id });
return this.customersService.getOrganizationInfo(id);
}
@Put(':id/organization-info')
@Authenticated()
@RequireAllPermissions(PERMISSIONS_GROUPS.USERS.permissions.ADMIN)
@HttpCode(HttpStatus.OK)
@ApiOkResponse({ description: 'Organization information updated' })
async updateOrganizationInfo(
@Param('id') id: string,
@Body() body: {
companyName: string;
companySite: string;
domain: string;
cnpj: string;
description: string;
},
) {
return this.customersService.updateOrganizationInfo(id, body);
}
}
+1 -54
View File
@@ -223,57 +223,4 @@ export class CustomersService implements OnModuleInit {
})
)
}
async updateOrganizationInfo(
customerId: string,
data: {
companyName: string;
companySite: string;
domain: string;
cnpj: string;
description: string;
},
) {
try {
const result = await lastValueFrom(
this.customerService.OrganizationUpdate({
customerId,
companyName: data.companyName || '',
companySite: data.companySite || '',
domain: data.domain || '',
cnpj: data.cnpj || '',
description: data.description || '',
}),
);
return result;
} catch (err) {
if (err.details === ErrorCodes.CUSTOMER.NOT_FOUND)
throw new HttpException(err.details, HttpStatus.NOT_FOUND);
else throw err;
}
}
async getOrganizationInfo(customerId: string) {
try {
const customerResponse = await lastValueFrom(
this.customerService.CustomerFindOneById({ id: customerId })
);
const customer = customerResponse.customer;
return {
companyName: customer.companyName || '',
companySite: customer.companySite || '',
domain: customer.domain || '',
cnpj: customer.cnpj || '',
description: customer.description || ''
};
} catch (err) {
if (err.details === ErrorCodes.CUSTOMER.NOT_FOUND)
throw new HttpException(err.details, HttpStatus.NOT_FOUND);
else throw err;
}
}
}
}
@@ -1,27 +0,0 @@
import { ApiProperty, ApiPropertyOptional } from '@nestjs/swagger';
export class OrganizationUpdateRequest {
@ApiProperty()
name: string;
@ApiPropertyOptional()
companySite: string;
@ApiProperty()
domain: string;
@ApiPropertyOptional()
info: string;
@ApiPropertyOptional()
cnpj: string;
}
export class OrganizationResponse {
@ApiProperty()
name: string;
@ApiPropertyOptional()
companySite: string;
@ApiProperty()
domain: string;
@ApiPropertyOptional()
info: string;
@ApiPropertyOptional()
cnpj: string;
}
+2 -2
View File
@@ -43,7 +43,7 @@ export class User {
@ApiProperty()
companyName?: string;
@ApiProperty()
personalSite?: string;
companySite?: string;
@ApiPropertyOptional()
customer?: Customer;
@ApiProperty()
@@ -117,7 +117,7 @@ export class UpdateUserReq {
@ApiPropertyOptional()
bio?: string;
@ApiPropertyOptional()
personalSite?: string;
companySite?: string;
@ApiPropertyOptional()
companyName?: string;
@ApiPropertyOptional()
+1 -1
View File
@@ -162,7 +162,7 @@ export class UsersService implements OnModuleInit {
name: updateUserDTO.name,
bio: updateUserDTO.bio,
companyName: updateUserDTO.companyName,
personalSite: updateUserDTO.personalSite,
companySite: updateUserDTO.companySite,
customerId,
id,
metabaseUserId: undefined,
+4 -1
View File
@@ -14,7 +14,10 @@ export class ValidationPipe implements PipeTransform<any> {
return value;
}
const object = plainToInstance(metatype, value);
const errors = await validate(object);
const errors = await validate(object, {
forbidUnknownValues: false,
whitelist: true,
});
if (errors.length > 0) {
const errorMessages = errors.map((err) => err.constraints);
throw new BadRequestException(errorMessages);