mirror of
https://github.com/dadosfera/maestro.git
synced 2026-09-06 16:44:49 +00:00
Compare commits
7
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
6f092884c8 | ||
|
|
b2f909d332 | ||
|
|
0db4457ec0 | ||
|
|
d4254c7d8e | ||
|
|
4db15fceab | ||
|
|
a4f4335e43 | ||
|
|
6efc25ad5e |
@@ -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,
|
||||
|
||||
@@ -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: '',
|
||||
},
|
||||
],
|
||||
},
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -65,7 +65,7 @@ describe('ConnectionTestService catalog cache', () => {
|
||||
});
|
||||
});
|
||||
|
||||
it('maps cached columns and derives references from the engine allowlist', async () => {
|
||||
it('maps cached columns to the existing table metadata contract', async () => {
|
||||
connectionsApiService.proxy.mockResolvedValue({
|
||||
columns: [
|
||||
{
|
||||
@@ -73,16 +73,6 @@ describe('ConnectionTestService catalog cache', () => {
|
||||
data_type: 'bigint',
|
||||
is_primary_key: true,
|
||||
},
|
||||
{
|
||||
column_name: 'name',
|
||||
data_type: 'varchar',
|
||||
is_primary_key: false,
|
||||
},
|
||||
],
|
||||
});
|
||||
platformApiService.proxy.mockResolvedValue({
|
||||
allowed_datatypes: [
|
||||
{ engine: 'postgresql', allowed_datatypes: ['bigint', 'timestamp'] },
|
||||
],
|
||||
});
|
||||
|
||||
@@ -102,10 +92,13 @@ describe('ConnectionTestService catalog cache', () => {
|
||||
{
|
||||
table_name: 'customers',
|
||||
columns: [
|
||||
{ name: 'id', type: 'bigint', is_primary_key: true },
|
||||
{ name: 'name', type: 'varchar', is_primary_key: false },
|
||||
{
|
||||
name: 'id',
|
||||
type: 'bigint',
|
||||
is_primary_key: true,
|
||||
},
|
||||
],
|
||||
references: [{ name: 'id', type: 'bigint', is_primary_key: true }],
|
||||
references: [],
|
||||
},
|
||||
],
|
||||
});
|
||||
@@ -114,39 +107,6 @@ describe('ConnectionTestService catalog cache', () => {
|
||||
'/connection_catalog/config-id/schemas/public/tables/customers/columns',
|
||||
user,
|
||||
);
|
||||
expect(platformApiService.proxy).toHaveBeenCalledWith(
|
||||
'GET',
|
||||
'/jobs/jdbc/configs/allowed_datatypes',
|
||||
user,
|
||||
);
|
||||
});
|
||||
|
||||
it('returns empty references when the allowlist call fails', async () => {
|
||||
connectionsApiService.proxy.mockResolvedValue({
|
||||
columns: [{ column_name: 'id', data_type: 'bigint', is_primary_key: true }],
|
||||
});
|
||||
platformApiService.proxy.mockRejectedValue(new Error('platform down'));
|
||||
|
||||
await expect(
|
||||
service.getTableMetadata(
|
||||
{
|
||||
connection_id: 'config-id',
|
||||
plugin: 'postgresql',
|
||||
schema: 'public',
|
||||
table_list: ['customers'],
|
||||
},
|
||||
user,
|
||||
),
|
||||
).resolves.toEqual({
|
||||
operation_result: true,
|
||||
tables_metadata: [
|
||||
{
|
||||
table_name: 'customers',
|
||||
columns: [{ name: 'id', type: 'bigint', is_primary_key: true }],
|
||||
references: [],
|
||||
},
|
||||
],
|
||||
});
|
||||
});
|
||||
|
||||
it('submits a catalog refresh without holding the request open', async () => {
|
||||
|
||||
@@ -187,20 +187,6 @@ export class ConnectionTestService {
|
||||
body: GetTableMetadataReq,
|
||||
user: RequestUser,
|
||||
): Promise<GetTableMetadataRes> {
|
||||
// Columns eligible as the incremental reference field are the ones whose
|
||||
// data type is allowed for this engine (e.g. int/date/timestamp). The
|
||||
// allowlist is owned by the platform API, keyed by engine === plugin.
|
||||
const allowedByEngine = await this.platformApiService
|
||||
.proxy('GET', '/jobs/jdbc/configs/allowed_datatypes', user)
|
||||
.catch(() => null);
|
||||
const allowedDataTypes: string[] =
|
||||
allowedByEngine?.allowed_datatypes?.find(
|
||||
(datatypes) => datatypes.engine === body.plugin,
|
||||
)?.allowed_datatypes ?? [];
|
||||
const allowedSet = new Set(
|
||||
allowedDataTypes.map((type) => type.toLowerCase()),
|
||||
);
|
||||
|
||||
const tables_metadata = await Promise.all(
|
||||
body.table_list.map(async (table_name) => {
|
||||
const result = await this.connectionsApiService.proxy(
|
||||
@@ -210,18 +196,14 @@ export class ConnectionTestService {
|
||||
`/tables/${encodeURIComponent(table_name)}/columns`,
|
||||
user,
|
||||
);
|
||||
const columns = result.columns.map((column) => ({
|
||||
name: column.column_name,
|
||||
type: column.data_type,
|
||||
is_primary_key: column.is_primary_key,
|
||||
}));
|
||||
const references = columns.filter((column) =>
|
||||
allowedSet.has(String(column.type).toLowerCase()),
|
||||
);
|
||||
return {
|
||||
table_name,
|
||||
columns,
|
||||
references,
|
||||
columns: result.columns.map((column) => ({
|
||||
name: column.column_name,
|
||||
type: column.data_type,
|
||||
is_primary_key: column.is_primary_key,
|
||||
})),
|
||||
references: [],
|
||||
};
|
||||
}),
|
||||
);
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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',
|
||||
|
||||
Reference in New Issue
Block a user