mirror of
https://github.com/dadosfera/maestro.git
synced 2026-11-10 21:39:08 +00:00
Compare commits
6
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
b0ec305763 | ||
|
|
4543c1ad05 | ||
|
|
ec39b82843 | ||
|
|
20d3532c0f | ||
|
|
9457fcc85e | ||
|
|
1400df0ab9 |
@@ -83,7 +83,7 @@ describe('ConnectionTestService catalog cache', () => {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
it('maps cached columns to the existing table metadata contract', async () => {
|
it('maps cached columns and derives references from the engine allowlist', async () => {
|
||||||
connectionsApiService.proxy.mockResolvedValue({
|
connectionsApiService.proxy.mockResolvedValue({
|
||||||
columns: [
|
columns: [
|
||||||
{
|
{
|
||||||
@@ -91,6 +91,16 @@ describe('ConnectionTestService catalog cache', () => {
|
|||||||
data_type: 'bigint',
|
data_type: 'bigint',
|
||||||
is_primary_key: true,
|
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'] },
|
||||||
],
|
],
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -110,13 +120,10 @@ describe('ConnectionTestService catalog cache', () => {
|
|||||||
{
|
{
|
||||||
table_name: 'customers',
|
table_name: 'customers',
|
||||||
columns: [
|
columns: [
|
||||||
{
|
{ name: 'id', type: 'bigint', is_primary_key: true },
|
||||||
name: 'id',
|
{ name: 'name', type: 'varchar', is_primary_key: false },
|
||||||
type: 'bigint',
|
|
||||||
is_primary_key: true,
|
|
||||||
},
|
|
||||||
],
|
],
|
||||||
references: [],
|
references: [{ name: 'id', type: 'bigint', is_primary_key: true }],
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
});
|
});
|
||||||
@@ -125,6 +132,39 @@ describe('ConnectionTestService catalog cache', () => {
|
|||||||
'/connection_catalog/config-id/schemas/public/tables/customers/columns',
|
'/connection_catalog/config-id/schemas/public/tables/customers/columns',
|
||||||
user,
|
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 () => {
|
it('submits a catalog refresh without holding the request open', async () => {
|
||||||
|
|||||||
@@ -213,6 +213,20 @@ export class ConnectionTestService {
|
|||||||
body: GetTableMetadataReq,
|
body: GetTableMetadataReq,
|
||||||
user: RequestUser,
|
user: RequestUser,
|
||||||
): Promise<GetTableMetadataRes> {
|
): 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(
|
const tables_metadata = await Promise.all(
|
||||||
body.table_list.map(async (table_name) => {
|
body.table_list.map(async (table_name) => {
|
||||||
const result = await this.connectionsApiService.proxy(
|
const result = await this.connectionsApiService.proxy(
|
||||||
@@ -222,14 +236,18 @@ export class ConnectionTestService {
|
|||||||
`/tables/${encodeURIComponent(table_name)}/columns`,
|
`/tables/${encodeURIComponent(table_name)}/columns`,
|
||||||
user,
|
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 {
|
return {
|
||||||
table_name,
|
table_name,
|
||||||
columns: result.columns.map((column) => ({
|
columns,
|
||||||
name: column.column_name,
|
references,
|
||||||
type: column.data_type,
|
|
||||||
is_primary_key: column.is_primary_key,
|
|
||||||
})),
|
|
||||||
references: [],
|
|
||||||
};
|
};
|
||||||
}),
|
}),
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -3,11 +3,15 @@ import {
|
|||||||
CdcTable,
|
CdcTable,
|
||||||
} from '@dadosfera/protospack-v2/dist/lib/Input/interfaces/entities';
|
} from '@dadosfera/protospack-v2/dist/lib/Input/interfaces/entities';
|
||||||
|
|
||||||
/** What a caller knows about a CDC table before it is stored. */
|
/** What a caller knows about a CDC table before it is stored. The source
|
||||||
|
* table is named `table_name` on the platform-facing bodies and `name` on
|
||||||
|
* the create DTO (CdcTableReq); either works — deriving one from the other
|
||||||
|
* happens only here. */
|
||||||
export interface CdcTableInput {
|
export interface CdcTableInput {
|
||||||
// Optional on the create DTO (CdcTableReq); the platform validates it.
|
// Optional on the create DTO (CdcTableReq); the platform validates it.
|
||||||
table_schema?: string;
|
table_schema?: string;
|
||||||
table_name: string;
|
table_name?: string;
|
||||||
|
name?: string;
|
||||||
primary_keys?: string[];
|
primary_keys?: string[];
|
||||||
iceberg_table_name?: string;
|
iceberg_table_name?: string;
|
||||||
iceberg_qualify_table_name?: string;
|
iceberg_qualify_table_name?: string;
|
||||||
@@ -25,10 +29,11 @@ export interface CdcTableInput {
|
|||||||
* `name ?? table_name`).
|
* `name ?? table_name`).
|
||||||
*/
|
*/
|
||||||
export function toCdcTable(table: CdcTableInput): CdcTable {
|
export function toCdcTable(table: CdcTableInput): CdcTable {
|
||||||
|
const tableName = table.table_name ?? table.name;
|
||||||
return {
|
return {
|
||||||
table_schema: table.table_schema,
|
table_schema: table.table_schema,
|
||||||
table_name: table.table_name,
|
table_name: tableName,
|
||||||
name: table.table_name,
|
name: tableName,
|
||||||
primary_keys: table.primary_keys ?? [],
|
primary_keys: table.primary_keys ?? [],
|
||||||
iceberg_table_name: table.iceberg_table_name,
|
iceberg_table_name: table.iceberg_table_name,
|
||||||
iceberg_qualify_table_name: table.iceberg_qualify_table_name,
|
iceberg_qualify_table_name: table.iceberg_qualify_table_name,
|
||||||
|
|||||||
@@ -196,9 +196,7 @@ export class InputsService {
|
|||||||
name: body.name,
|
name: body.name,
|
||||||
plugin: body.plugin,
|
plugin: body.plugin,
|
||||||
read_only: body.read_only ?? true,
|
read_only: body.read_only ?? true,
|
||||||
tables: body.tables.map((t) =>
|
tables: body.tables.map(toCdcTable),
|
||||||
toCdcTable({ ...t, table_name: t.name }),
|
|
||||||
),
|
|
||||||
destination: body.destination,
|
destination: body.destination,
|
||||||
},
|
},
|
||||||
info,
|
info,
|
||||||
|
|||||||
@@ -30,7 +30,7 @@ interface PlatformPipeline {
|
|||||||
*/
|
*/
|
||||||
@Injectable()
|
@Injectable()
|
||||||
export class PipelineTablesService {
|
export class PipelineTablesService {
|
||||||
private logger: DadosferaLogger['logger'];
|
logger: DadosferaLogger;
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
private readonly platformApiService: PlatformApiService,
|
private readonly platformApiService: PlatformApiService,
|
||||||
|
|||||||
Reference in New Issue
Block a user