Skip to content

feat: add endpoint to find table categories with associated tables#1597

Merged
Artuomka merged 3 commits into
mainfrom
backend_table_folders_improvement
Feb 12, 2026
Merged

feat: add endpoint to find table categories with associated tables#1597
Artuomka merged 3 commits into
mainfrom
backend_table_folders_improvement

Conversation

@Artuomka

Copy link
Copy Markdown
Collaborator

No description provided.

Copilot AI review requested due to automatic review settings February 12, 2026 14:27
@Artuomka Artuomka enabled auto-merge February 12, 2026 14:27

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Adds a new GET /table-categories/v2/:connectionId endpoint that returns table categories along with the resolved tables (including permissions/display metadata), and validates it via a new SaaS E2E test.

Changes:

  • Added GET /table-categories/v2/:connectionId controller route + DI wiring for a new “find categories with tables” use case.
  • Implemented FindTableCategoriesWithTablesUseCase to fetch tables, apply permissions, enrich with display names, and group by categories (plus an “All tables” bucket).
  • Added a new response DTO and an E2E test covering the new endpoint.

Reviewed changes

Copilot reviewed 7 out of 7 changed files in this pull request and generated 9 comments.

Show a summary per file
File Description
backend/test/ava-tests/saas-tests/table-categories-e2e.test.ts Adds E2E coverage for the new v2 endpoint response shape and contents.
backend/src/entities/table-categories/use-cases/table-categories-use-cases.interface.ts Introduces a new use case interface for fetching categories with tables.
backend/src/entities/table-categories/use-cases/find-table-categories-with-tables.use.case.ts New use case implementing the endpoint behavior (tables + permissions + grouping).
backend/src/entities/table-categories/table-categories.module.ts Wires new use case provider and applies auth middleware to the new route.
backend/src/entities/table-categories/table-categories.controller.ts Exposes GET /table-categories/v2/:connectionId and calls the new use case.
backend/src/entities/table-categories/dto/found-table-categories-with-tables.ro.ts Adds response model for “categories with tables” endpoint.
backend/src/common/data-injection.tokens.ts Adds a new UseCaseType token for the use case wiring.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

}

protected async implementation(inputData: FindTablesDs): Promise<Array<FoundTableCategoriesWithTablesRo>> {
const { connectionId, masterPwd, userId } = inputData;

Copilot AI Feb 12, 2026

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

FindTablesDs includes hiddenTablesOption, but this use case ignores it (it destructures only connectionId, masterPwd, userId) and always filters out hidden tables. Either honor hiddenTablesOption (including the same permission/guard behavior as the existing find-tables use cases) or remove the field from this endpoint’s input to avoid misleading callers.

Suggested change
const { connectionId, masterPwd, userId } = inputData;
const { connectionId, masterPwd, userId, hiddenTablesOption } = inputData;
if (typeof hiddenTablesOption !== 'undefined' && hiddenTablesOption !== null) {
// This use case intentionally always excludes hidden tables.
// To avoid misleading callers, explicitly reject requests that try to
// control hidden table visibility via `hiddenTablesOption`.
throw new HttpException(
{
message: 'The hiddenTablesOption parameter is not supported for this endpoint.',
},
HttpStatus.BAD_REQUEST,
);
}

Copilot uses AI. Check for mistakes.
Comment on lines +119 to +123
private async addDisplayNamesForTables(
connectionId: string,
tablesObjArr: Array<ITableAndViewPermissionData>,
): Promise<Array<FoundTableDs>> {
const tableSettings = await this._dbContext.tableSettingsRepository.findTableSettingsInConnectionPure(connectionId);

Copilot AI Feb 12, 2026

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This use case duplicates substantial logic from the existing table listing use cases (e.g., display-name enrichment and permissions calculation). Keeping multiple copies increases the risk of behavior drift when one copy changes. Consider extracting these helpers into a shared service/util or reusing the existing implementation where possible.

Copilot uses AI. Check for mistakes.
Comment on lines +11 to +12
@ApiProperty({ type: String })
category_color: string;

Copilot AI Feb 12, 2026

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

category_color is returned as null for the "All tables" category (see use case), but this response model declares it as non-nullable string. Update the DTO type to string | null and mark it nullable in the Swagger decorator.

Suggested change
@ApiProperty({ type: String })
category_color: string;
@ApiProperty({ type: String, nullable: true })
category_color: string | null;

Copilot uses AI. Check for mistakes.
Comment on lines +90 to +94
const foundTableCategories =
await this._dbContext.tableCategoriesRepository.findTableCategoriesForConnection(connectionId);

const sortedTables = tablesRO.sort((tableRO1, tableRO2) => {
const name1 = tableRO1.display_name || tableRO1.table;

Copilot AI Feb 12, 2026

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

findTableCategoriesForConnection() has no ORDER BY, so category order in the response can be nondeterministic. To keep the API output stable (and avoid flaky tests), sort the returned categories (e.g., by category_name/category_id) or add an orderBy in the repository query.

Copilot uses AI. Check for mistakes.
Comment on lines +375 to +385
t.is(findTableCategoriesWithTablesRO[1].category_name, 'Category 1');
t.is(findTableCategoriesWithTablesRO[1].category_color, '#FF5733');
t.is(findTableCategoriesWithTablesRO[1].category_id, 'cat-001');
t.is(findTableCategoriesWithTablesRO[1].tables.length, 1);
t.is(findTableCategoriesWithTablesRO[1].tables[0].table, firstTestTableName);

t.is(findTableCategoriesWithTablesRO[2].category_name, 'Category 2');
t.is(findTableCategoriesWithTablesRO[2].category_color, '#33FF57');
t.is(findTableCategoriesWithTablesRO[2].category_id, 'cat-002');
t.is(findTableCategoriesWithTablesRO[2].tables.length, 1);
t.is(findTableCategoriesWithTablesRO[2].tables[0].table, secondTestTableName);

Copilot AI Feb 12, 2026

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This test assumes a stable ordering of categories in the response (using indexes [1] and [2]). Since the underlying repository query doesn’t specify an ORDER BY, DB return order can be nondeterministic and make the test flaky. Prefer asserting by locating categories via category_id/category_name (e.g., find()) instead of relying on array positions (other than the special "All tables" element).

Suggested change
t.is(findTableCategoriesWithTablesRO[1].category_name, 'Category 1');
t.is(findTableCategoriesWithTablesRO[1].category_color, '#FF5733');
t.is(findTableCategoriesWithTablesRO[1].category_id, 'cat-001');
t.is(findTableCategoriesWithTablesRO[1].tables.length, 1);
t.is(findTableCategoriesWithTablesRO[1].tables[0].table, firstTestTableName);
t.is(findTableCategoriesWithTablesRO[2].category_name, 'Category 2');
t.is(findTableCategoriesWithTablesRO[2].category_color, '#33FF57');
t.is(findTableCategoriesWithTablesRO[2].category_id, 'cat-002');
t.is(findTableCategoriesWithTablesRO[2].tables.length, 1);
t.is(findTableCategoriesWithTablesRO[2].tables[0].table, secondTestTableName);
const category1 = findTableCategoriesWithTablesRO.find(
(category) => category.category_id === 'cat-001',
);
t.truthy(category1);
t.is(category1.category_name, 'Category 1');
t.is(category1.category_color, '#FF5733');
t.is(category1.category_id, 'cat-001');
t.is(category1.tables.length, 1);
t.is(category1.tables[0].table, firstTestTableName);
const category2 = findTableCategoriesWithTablesRO.find(
(category) => category.category_id === 'cat-002',
);
t.truthy(category2);
t.is(category2.category_name, 'Category 2');
t.is(category2.category_color, '#33FF57');
t.is(category2.category_id, 'cat-002');
t.is(category2.tables.length, 1);
t.is(category2.tables[0].table, secondTestTableName);

Copilot uses AI. Check for mistakes.
Comment thread backend/src/entities/table-categories/table-categories.controller.ts Outdated
@ApiParam({ name: 'connectionId', required: true })
@UseGuards(TablesReceiveGuard)
@Get('/v2/:connectionId')
async findTableCategoriesWithTAbles(

Copilot AI Feb 12, 2026

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Method name findTableCategoriesWithTAbles has inconsistent casing (capital "A" in the middle), which makes the API surface harder to scan and search. Rename to standard lowerCamelCase (e.g., findTableCategoriesWithTables).

Suggested change
async findTableCategoriesWithTAbles(
async findTableCategoriesWithTables(

Copilot uses AI. Check for mistakes.
Comment on lines +5 to +6
@ApiProperty({ type: String })
category_id: string;

Copilot AI Feb 12, 2026

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

category_id is returned as null for the "All tables" category (see use case), but this response model declares it as non-nullable string. Update the DTO type to string | null and mark it nullable in the Swagger decorator.

Suggested change
@ApiProperty({ type: String })
category_id: string;
@ApiProperty({ type: String, nullable: true })
category_id: string | null;

Copilot uses AI. Check for mistakes.
Artuomka and others added 2 commits February 12, 2026 16:33
@Artuomka Artuomka disabled auto-merge February 12, 2026 14:35
@Artuomka Artuomka merged commit 7a1008f into main Feb 12, 2026
16 checks passed
@Artuomka Artuomka deleted the backend_table_folders_improvement branch February 12, 2026 14:36
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants