Compare commits

...
Author SHA1 Message Date
viniciusgadea ba609a9494 FEAT: add data_preview field to data asset creation in CatalogService 2026-08-25 17:14:22 -03:00
viniciusgadea 8916dfc46c FEAT: update @dadosfera/protospack-v2 dependency to version 3.40.0-beta.19 2026-08-25 17:05:34 -03:00
viniciusgadea cf4e0aea22 FEAT: update protospack-v2 dependency to version 3.40.0-beta.18 and refactor UpdateDataAsset method in CatalogService 2026-08-25 14:06:25 -03:00
viniciusgadea 9d57e69e74 Merge remote-tracking branch 'origin/beta' into feat/migration-nimbus-data-beta
# Conflicts:
#	docsfera.json
#	src/modules/catalog/dtos/index.ts
2026-08-25 12:08:47 -03:00
viniciusgadea 4db15fceab FEAT: add request body and DTOs for updating columns metadata in catalog 2026-08-25 09:36:02 -03:00
viniciusgadea a4f4335e43 FEAT: update columns metadata endpoint to accept batch updates and add error handling for missing columns 2026-08-25 09:05:46 -03:00
RafaelandWOZCODE bf0314f5b1 FIX: trigger release for /auth/me permission seqids (PR #513)
PR #513 merged to beta but no semantic-release ran: its commits used
conventional-commits prefixes (feat(auth):, fix(auth):) which the
.releaserc.json eslint preset does not recognise, so commit-analyzer
found no release-worthy change. This empty FIX: commit matches the
eslint preset's releaseRules (tag FIX -> patch) to cut a beta release
that includes the /auth/me permission-seqids change, so stg can deploy it.

Co-Authored-By: WOZCODE <contact@withwoz.com>
2026-08-24 19:18:59 -03:00
Rafael Santana c97180ac95 Merge pull request #513 from dadosfera/feat/auth-me-orchest-identity
feat(auth): return the user's permission seqids from /auth/me
2026-08-24 17:00:15 -03:00
RafaelandWOZCODE cac36f2c60 refactor(auth): /auth/me returns raw permission seqids
Return payload.permissions verbatim (numeric seqids) instead of
translating them to claim strings. Consumers own the seqid->meaning
mapping. Drops permission-claims.ts entirely; UserDTO.permissions is
now number[].

Co-Authored-By: WOZCODE <contact@withwoz.com>
2026-08-24 16:56:23 -03:00
RafaelandWOZCODE cd21fd0b7b refactor(auth): /auth/me returns permissions only (drop roles/modules)
Keep Maestro a pure identity provider: /auth/me exposes the user's
permission claim strings and nothing consumer-specific. Consumers derive
whatever meaning they need (roles, module access, groups) from the claim
vocabulary — claims are already namespaced group:action.

- UserDTO: drop roles[]/modules[], keep permissions[].
- Helper shrinks to a generic seqid->claim translation
  (orchest-identity.ts -> permission-claims.ts, translateSeqidsToClaims).
- api-key branch: permissions: [] only.

The roles/modules derivation moves entirely to the consumer (Orchest's
auth-server adapter).

Co-Authored-By: WOZCODE <contact@withwoz.com>
2026-08-24 16:30:33 -03:00
RafaelandWOZCODE a5a685ee3f fix(auth): derive Orchest identity from numeric seqids (JWT carries seqids not claim strings)
The JWT `permissions` claim is an array of numeric seqids at runtime
(see authentication.guard.ts / authentication.decorator.ts), not claim
strings. deriveOrchestIdentity previously matched claim strings against
this numeric array, so roles[]/modules[] were always empty for every
real user.

- deriveOrchestIdentity now takes number[] | undefined and matches
  seqids sourced from PERMISSIONS_GROUPS (permissions.enum.ts) instead
  of hand-copied literals.
- permissions is translated back to claim strings via a full
  seqid->claim catalog built once from PERMISSIONS_GROUPS; unknown
  seqids are dropped (auth-server ignores permissions[] in v1).
- auth.controller.ts's api-key branch literal is now annotated
  `: UserDTO` so tsc enforces the three fields there.
- Both spec files re-fixtured with numeric seqid inputs, including a
  mixed admin+module case and an exact claim-string translation
  assertion.

Co-Authored-By: WOZCODE <contact@withwoz.com>
2026-08-24 16:03:20 -03:00
Rafael a16fefe691 feat(auth): return permissions/roles/modules from /auth/me (all branches) 2026-08-24 15:45:09 -03:00
Rafael 9c57485031 feat(auth): pure helper deriving Orchest identity from permissions 2026-08-24 15:39:40 -03:00
viniciusgadea 6efc25ad5e FEAT: add endpoint to update column description in catalog 2026-08-20 08:26:56 -03:00
marcos.rodrigues 7051b21d86 Merge pull request #509 from dadosfera/release/2026-08-12
FIX: require collect module in endpoints
2026-08-12 15:06:04 -03:00
marcos.rodrigues 0c4888ccdb FIX: require collect module in endpoints 2026-08-12 15:02:37 -03:00
marcos.rodrigues e1b0e88bd8 Merge pull request #507 from dadosfera/bugfix/catalog-module-main
Bugfix/catalog module main
2026-08-12 09:59:08 -03:00
marcos.rodrigues 8a92da470c Merge pull request #506 from dadosfera/bugfix/catalog-module-main
FIX: skip nimbus update when customer haven't catalog module
2026-08-07 16:27:19 -03:00
11 changed files with 350 additions and 10 deletions
+252 -1
View File
@@ -4705,6 +4705,9 @@
"Platform API"
],
"security": [
{
"access-token": []
},
{
"access-token": []
}
@@ -4761,6 +4764,9 @@
"Platform API"
],
"security": [
{
"access-token": []
},
{
"access-token": []
}
@@ -6017,6 +6023,57 @@
"access-token": []
}
]
},
"patch": {
"operationId": "CatalogController_updateColumnsMetadata",
"parameters": [
{
"name": "dadosfera-lang",
"in": "header",
"required": false,
"schema": {
"enum": [
"pt-br",
"en-us"
],
"type": "string"
}
},
{
"name": "id",
"required": true,
"in": "path",
"schema": {
"type": "string"
}
}
],
"requestBody": {
"required": true,
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/UpdateColumnsMetadataRequest"
}
}
}
},
"responses": {
"200": {
"description": ""
}
},
"tags": [
"Catalog"
],
"security": [
{
"access-token": []
},
{
"access-token": []
}
]
}
},
"/catalog/data-asset/{id}/preview": {
@@ -7456,6 +7513,94 @@
]
}
},
"/connection-test/refresh-catalog": {
"post": {
"operationId": "ConnectionTestController_refreshCatalog",
"parameters": [],
"requestBody": {
"required": true,
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/RefreshCatalogReq"
}
}
}
},
"responses": {
"200": {
"description": "",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/RefreshCatalogRes"
}
}
}
},
"202": {
"description": "",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/RefreshCatalogRes"
}
}
}
}
},
"tags": [
"Connection Test"
],
"security": [
{
"access-token": []
},
{
"access-token": []
}
]
}
},
"/connection-test/refresh-catalog/status": {
"post": {
"operationId": "ConnectionTestController_refreshCatalogStatus",
"parameters": [],
"requestBody": {
"required": true,
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/RefreshCatalogStatusReq"
}
}
}
},
"responses": {
"200": {
"description": "",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/RefreshCatalogRes"
}
}
}
}
},
"tags": [
"Connection Test"
],
"security": [
{
"access-token": []
},
{
"access-token": []
}
]
}
},
"/oauth/hubspot": {
"get": {
"operationId": "OauthController_oauthHubspot",
@@ -11216,6 +11361,35 @@
"columns_metadata"
]
},
"ColumnDescriptionDto": {
"type": "object",
"properties": {
"column_name": {
"type": "string"
},
"description": {
"type": "string"
}
},
"required": [
"column_name",
"description"
]
},
"UpdateColumnsMetadataRequest": {
"type": "object",
"properties": {
"columns": {
"type": "array",
"items": {
"$ref": "#/components/schemas/ColumnDescriptionDto"
}
}
},
"required": [
"columns"
]
},
"IData": {
"type": "object",
"properties": {
@@ -11812,11 +11986,15 @@
},
"type": {
"type": "string"
},
"is_primary_key": {
"type": "boolean"
}
},
"required": [
"name",
"type"
"type",
"is_primary_key"
]
},
"TableMetadataDto": {
@@ -11862,6 +12040,79 @@
"tables_metadata"
]
},
"RefreshCatalogReq": {
"type": "object",
"properties": {
"connection_id": {
"type": "string"
},
"plugin": {
"type": "string",
"enum": [
"oracle",
"mysql",
"postgresql",
"sqlserver"
]
}
},
"required": [
"connection_id",
"plugin"
]
},
"RefreshCatalogRes": {
"type": "object",
"properties": {
"operation_result": {
"type": "boolean"
},
"status": {
"type": "string"
},
"session_id": {
"type": "string"
},
"date": {
"type": "string"
}
},
"required": [
"operation_result",
"status",
"session_id",
"date"
]
},
"RefreshCatalogStatusReq": {
"type": "object",
"properties": {
"connection_id": {
"type": "string"
},
"plugin": {
"type": "string",
"enum": [
"oracle",
"mysql",
"postgresql",
"sqlserver"
]
},
"session_id": {
"type": "string"
},
"date": {
"type": "string"
}
},
"required": [
"connection_id",
"plugin",
"session_id",
"date"
]
},
"INote": {
"type": "object",
"properties": {
+5 -4
View File
@@ -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.14",
"@dadosfera/protospack-v2": "^3.40.0-beta.19",
"@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.14",
"resolved": "https://dadosfera-611330257153.d.codeartifact.us-east-1.amazonaws.com/npm/dadosfera-npm/@dadosfera/protospack-v2/-/protospack-v2-3.40.0-beta.14.tgz",
"integrity": "sha512-pv3pxq0x1XcBgf3ajD6QOFRLOduh8iEozKFA3AKlIW4gid+gT4iL0GcU2M+O7h0QFeO4JIzRZe/nEMN82nqk7A==",
"version": "3.40.0-beta.19",
"resolved": "https://dadosfera-611330257153.d.codeartifact.us-east-1.amazonaws.com/npm/dadosfera-npm/@dadosfera/protospack-v2/-/protospack-v2-3.40.0-beta.19.tgz",
"integrity": "sha512-RT7BYWHD2aD945FXzDXHa/xM+sD2B2Ypepy/W/WS+RwKYTSMCfuUXGzK2lXcd9HP9+U+Zjfh90Lkktl++8enjg==",
"license": "ISC",
"dependencies": {
"@grpc/grpc-js": "^1.9.3",
"rxjs": "^7.5.5"
+1 -1
View File
@@ -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.14",
"@dadosfera/protospack-v2": "^3.40.0-beta.19",
"@grpc/grpc-js": "^1.9.3",
"@grpc/proto-loader": "^0.7.9",
"@nestjs/cli": "^9.5.0",
+4 -2
View File
@@ -37,6 +37,7 @@ import {
RequireAllPermissions,
} from 'src/decorators/authentication.decorator';
import { AuthClientService } from './auth.service';
import { UserDTO } from './dtos/login';
import { DadosferaLogger } from '@dadosfera/dadosfera-logs';
import { GrpcToHttpExceptionFilter } from '../../error/grpc-to-http-exception.filter';
import { RequestUser, User } from 'src/decorators/user.decorator';
@@ -486,7 +487,7 @@ export class AuthController {
this.logger.info('Authenticating via X-Api-key header');
const { api_key } = await this.apiKeyService.get(apiKey);
const userDto = {
const userDto: UserDTO = {
id: api_key.user_id,
name: api_key.username,
email: api_key.username,
@@ -494,7 +495,8 @@ export class AuthController {
id: api_key.customer_id,
name: api_key.customer_name,
tier: api_key.customer_tier,
}
},
permissions: [],
};
return res.status(200).json(userDto);
+3
View File
@@ -447,6 +447,9 @@ export class AuthClientService implements OnModuleInit {
name: payload.customer_name,
tier: payload.customer_tier,
},
// Raw permission seqids from the JWT. Consumers own the seqid->meaning
// mapping (e.g. Orchest's auth-server); Maestro reports them as-is.
permissions: payload.permissions ?? [],
};
return userDto;
+2 -1
View File
@@ -152,5 +152,6 @@ export type UserDTO = {
id: string,
name: string,
tier: string,
}
},
permissions: number[],
}
+38
View File
@@ -9,6 +9,7 @@ import {
Inject,
NotFoundException,
Param,
Patch,
Post,
Put,
Query,
@@ -51,6 +52,7 @@ import {
IUpdateDataRequest,
TriggerCatalogReq,
TriggerCatalogRes,
UpdateColumnsMetadataRequest,
} from './dtos';
import { GrpcToHttpExceptionFilter } from 'src/error/grpc-to-http-exception.filter';
import { Language } from 'src/decorators/language.decorator';
@@ -448,6 +450,42 @@ export class CatalogController {
return { columns_metadata };
}
@Patch('data-asset/:id/columns-metadata')
@RequireSomePermission(
PERMISSIONS_GROUPS.CATALOG.permissions.UPDATE,
PERMISSIONS_GROUPS.CATALOG.permissions.DATA_MANAGER,
)
async updateColumnsMetadata(
@User() user: RequestUser,
@Language() language: LanguageEnum,
@Param('id') id: string,
@Body(new ValidationPipe()) body: UpdateColumnsMetadataRequest,
): Promise<{ success: boolean }> {
const { customer_name, customer_id, user_id, username } = user;
this.logger.info(`/catalog - update columns metadata`, {
user_id,
customer_name,
columns_count: body.columns.length,
});
const metadata = PackTheMetadata({
customer_name,
customer_id,
user_id,
username,
language,
});
await this.catalogService.updateColumnsDescriptions(
id,
body.columns,
metadata,
);
return { success: true };
}
@Get('data-asset/:id/preview')
@RequireSomePermission(
PERMISSIONS_GROUPS.CATALOG.permissions.GET,
+12
View File
@@ -467,6 +467,16 @@ class CatalogService implements OnModuleInit {
return result;
}
async updateColumnsDescriptions(
id: string,
columns: { column_name: string; description: string }[],
metadata: Metadata,
) {
await lastValueFrom(
this.catalogWriteService.UpdateColumnDescriptions({ id, columns }, metadata),
);
}
async createDataDocs(body: CreateDataDocsDTO, metadata: Metadata) {
if (body.asset_type === 'table' || body.asset_type === 'view') {
return this.createDataDocsViaNimbus(body);
@@ -832,6 +842,8 @@ class CatalogService implements OnModuleInit {
data_asset_id: table_metadata_id.toString(),
customer_name: customer_name,
data_asset_type: 'dataset',
column_metadata: [],
data_preview: '',
},
],
},
+24 -1
View File
@@ -1,10 +1,13 @@
import { ApiProperty, ApiPropertyOptional, PickType } from '@nestjs/swagger';
import {
ArrayNotEmpty,
IsArray,
IsEnum,
IsNotEmpty,
IsOptional,
IsString,
ValidateNested,
} from 'class-validator';
import { Type } from 'class-transformer';
import { CreateDataAssetRequest } from '@dadosfera/protospack-v2/dist/lib/Catalog/interfaces/messages';
export enum DataAssetShareType {
@@ -247,6 +250,26 @@ export class IUpdateCertificationStatusRequest {
certification_status: CertificationStatus;
}
export class ColumnDescriptionDto {
@ApiProperty()
@IsString()
@IsNotEmpty()
column_name: string;
@ApiProperty()
@IsString()
description: string;
}
export class UpdateColumnsMetadataRequest {
@ApiProperty({ type: [ColumnDescriptionDto] })
@IsArray()
@ArrayNotEmpty()
@ValidateNested({ each: true })
@Type(() => ColumnDescriptionDto)
columns: ColumnDescriptionDto[];
}
export class ICreateDataAsset implements CreateDataAssetRequest {
@ApiProperty()
display_name: string;
+8
View File
@@ -315,6 +315,14 @@ export function EnrichErrorCode(code: string) {
'Tente realizar a ação novamente. Caso o erro persista, entre em contato com o suporte',
code,
};
case ErrorCodes.CATALOG.COLUMN_NOT_FOUND:
return {
statusCode: HttpStatus.NOT_FOUND,
error: 'Coluna não encontrada',
message:
'Uma ou mais colunas informadas não existem neste ativo. Verifique os nomes e tente novamente.',
code,
};
case ErrorCodes.CATALOG.PREVIEW_TOO_BIG:
return {
statusCode: HttpStatus.INTERNAL_SERVER_ERROR,
+1
View File
@@ -74,6 +74,7 @@ const CATALOG = {
DATA_ASSET_NOT_FOUND: 'CATALOG.DATA_ASSET_NOT_FOUND',
PREVIEW_TOO_BIG: 'CATALOG.PREVIEW_TOO_BIG',
METADATA_TOO_BIG: 'CATALOG.METADATA_TOO_BIG',
COLUMN_NOT_FOUND: 'CATALOG.COLUMN_NOT_FOUND',
};
const IDENTITY_PROVIDER = {
INVALID_RESPONSE: 'IDENTITY_PROVIDER.INVALID_RESPONSE',