Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions backend/src/app.module.ts
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@ import { DatabaseModule } from './shared/database/database.module.js';
import { GetHelloUseCase } from './use-cases-app/get-hello.use.case.js';
import { ThrottlerModule, ThrottlerGuard } from '@nestjs/throttler';
import { SharedJobsModule } from './entities/shared-jobs/shared-jobs.module.js';
import { TableCategoriesModule } from './entities/table-categories/table-categories.module.js';

@Module({
imports: [
Expand Down Expand Up @@ -79,6 +80,7 @@ import { SharedJobsModule } from './entities/shared-jobs/shared-jobs.module.js';
DemoDataModule,
LoggingModule,
SharedJobsModule,
TableCategoriesModule,
],
controllers: [AppController],
providers: [
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,8 @@ import { ITableWidgetsRepository } from '../../entities/widget/repository/table-
import { TableWidgetEntity } from '../../entities/widget/table-widget.entity.js';
import { IDatabaseContext } from '../database-context.interface.js';
import { TableCategoriesEntity } from '../../entities/table-categories/table-categories.entity.js';
import { ITableCategoriesCustomRepository } from '../../entities/table-categories/repository/table-categories-repository.interface.js';
import { ConnectionPropertiesEntity } from '../../entities/connection-properties/connection-properties.entity.js';

export interface IGlobalDatabaseContext extends IDatabaseContext {
userRepository: Repository<UserEntity> & IUserRepository;
Expand All @@ -59,7 +61,7 @@ export interface IGlobalDatabaseContext extends IDatabaseContext {
passwordResetRepository: IPasswordResetRepository;
emailChangeRepository: IEmailChangeRepository;
userInvitationRepository: IUserInvitationRepository;
connectionPropertiesRepository: IConnectionPropertiesRepository;
connectionPropertiesRepository: Repository<ConnectionPropertiesEntity> & IConnectionPropertiesRepository;
customFieldsRepository: ICustomFieldsRepository;
tableLogsRepository: ITableLogsRepository;
userActionRepository: IUserActionRepository;
Expand All @@ -80,5 +82,5 @@ export interface IGlobalDatabaseContext extends IDatabaseContext {
companyTabTitleRepository: Repository<CompanyTabTitleEntity>;
tableFiltersRepository: Repository<TableFiltersEntity> & ITableFiltersCustomRepository;
aiResponsesToUserRepository: Repository<AiResponsesToUserEntity> & IAiResponsesToUserRepository;
tableCategoriesRepository: Repository<TableCategoriesEntity>;
tableCategoriesRepository: Repository<TableCategoriesEntity> & ITableCategoriesCustomRepository;
}
14 changes: 9 additions & 5 deletions backend/src/common/application/global-database-context.ts
Original file line number Diff line number Diff line change
Expand Up @@ -88,6 +88,8 @@ import { IAiResponsesToUserRepository } from '../../entities/ai/ai-data-entities
import { AiResponsesToUserEntity } from '../../entities/ai/ai-data-entities/ai-reponses-to-user/ai-responses-to-user.entity.js';
import { aiResponsesToUserRepositoryExtension } from '../../entities/ai/ai-data-entities/ai-reponses-to-user/ai-reponses-to-user-repository.extension.js';
import { TableCategoriesEntity } from '../../entities/table-categories/table-categories.entity.js';
import { ITableCategoriesCustomRepository } from '../../entities/table-categories/repository/table-categories-repository.interface.js';
import { tableCategoriesCustomRepositoryExtension } from '../../entities/table-categories/repository/table-categories-repository.extension.js';

@Injectable({ scope: Scope.REQUEST })
export class GlobalDatabaseContext implements IGlobalDatabaseContext {
Expand All @@ -104,7 +106,7 @@ export class GlobalDatabaseContext implements IGlobalDatabaseContext {
private _passwordResetRepository: IPasswordResetRepository;
private _emailChangeRepository: IEmailChangeRepository;
private _userInvitationRepository: IUserInvitationRepository;
private _connectionPropertiesRepository: IConnectionPropertiesRepository;
private _connectionPropertiesRepository: Repository<ConnectionPropertiesEntity> & IConnectionPropertiesRepository;
private _customFieldsRepository: ICustomFieldsRepository;
private _tableLogsRepository: ITableLogsRepository;
private _userActionRepository: IUserActionRepository;
Expand All @@ -125,7 +127,7 @@ export class GlobalDatabaseContext implements IGlobalDatabaseContext {
private _companyTabTitleRepository: Repository<CompanyTabTitleEntity>;
private _tableFiltersRepository: Repository<TableFiltersEntity> & ITableFiltersCustomRepository;
private _aiResponsesToUserRepository: Repository<AiResponsesToUserEntity> & IAiResponsesToUserRepository;
private _tableCategoriesRepository: Repository<TableCategoriesEntity>;
private _tableCategoriesRepository: Repository<TableCategoriesEntity> & ITableCategoriesCustomRepository;

public constructor(
@Inject(BaseType.DATA_SOURCE)
Expand Down Expand Up @@ -211,7 +213,9 @@ export class GlobalDatabaseContext implements IGlobalDatabaseContext {
this._aiResponsesToUserRepository = this.appDataSource
.getRepository(AiResponsesToUserEntity)
.extend(aiResponsesToUserRepositoryExtension);
this._tableCategoriesRepository = this.appDataSource.getRepository(TableCategoriesEntity);
this._tableCategoriesRepository = this.appDataSource
.getRepository(TableCategoriesEntity)
.extend(tableCategoriesCustomRepositoryExtension);
}

public get userRepository(): Repository<UserEntity> & IUserRepository {
Expand Down Expand Up @@ -258,7 +262,7 @@ export class GlobalDatabaseContext implements IGlobalDatabaseContext {
return this._userInvitationRepository;
}

public get connectionPropertiesRepository(): IConnectionPropertiesRepository {
public get connectionPropertiesRepository(): Repository<ConnectionPropertiesEntity> & IConnectionPropertiesRepository {
return this._connectionPropertiesRepository;
}

Expand Down Expand Up @@ -342,7 +346,7 @@ export class GlobalDatabaseContext implements IGlobalDatabaseContext {
return this._aiResponsesToUserRepository;
}

public get tableCategoriesRepository(): Repository<TableCategoriesEntity> {
public get tableCategoriesRepository(): Repository<TableCategoriesEntity> & ITableCategoriesCustomRepository {
return this._tableCategoriesRepository;
}

Expand Down
3 changes: 3 additions & 0 deletions backend/src/common/data-injection.tokens.ts
Original file line number Diff line number Diff line change
Expand Up @@ -167,4 +167,7 @@ export enum UseCaseType {
FIND_TABLE_FILTER_BY_ID = 'FIND_TABLE_FILTER_BY_ID',
DELETE_TABLE_FILTER_BY_ID = 'DELETE_TABLE_FILTER_BY_ID',
UPDATE_TABLE_FILTER_BY_ID = 'UPDATE_TABLE_FILTER_BY_ID',

CREATE_UPDATE_TABLE_CATEGORIES = 'CREATE_UPDATE_TABLE_CATEGORIES',
FIND_TABLE_CATEGORIES = 'FIND_TABLE_CATEGORIES',
}
Original file line number Diff line number Diff line change
Expand Up @@ -15,5 +15,7 @@ export class CreateConnectionPropertiesDs {
table_categories: Array<{
category_name: string;
tables: Array<string>;
category_color: string;
category_id: string;
}>;
}
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,8 @@ export class CreateConnectionPropertiesUseCase
const newCategory = this._dbContext.tableCategoriesRepository.create({
category_name: category.category_name,
tables: category.tables,
category_color: category.category_color,
category_id: category.category_id,
});
newCategory.connection_properties = createdConnectionProperties;
return newCategory;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,8 @@ export class UpdateConnectionPropertiesUseCase
const newCategory = this._dbContext.tableCategoriesRepository.create({
category_name: category.category_name,
tables: category.tables,
category_color: category.category_color,
category_id: category.category_id,
});
newCategory.connection_properties = updatedProperties;
return newCategory;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,9 +20,10 @@ export function buildFoundConnectionPropertiesDs(
table_categories: connectionProperties.table_categories
? connectionProperties.table_categories.map((c) => {
return {
id: c.id,
category_id: c.category_id,
category_name: c.category_name,
tables: c.tables,
category_color: c.category_color,
};
})
: [],
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import { Messages } from '../../../exceptions/text/messages.js';
import { HttpStatus } from '@nestjs/common';
import { ConnectionEntity } from '../../connection/connection.entity.js';
import { getDataAccessObject } from '@rocketadmin/shared-code/dist/src/data-access-layer/shared/create-data-access-object.js';
import { validateTableCategories } from '../../table-categories/utils/validate-table-categories.util.js';

export async function validateCreateConnectionPropertiesDs(
createConnectionProperties: CreateConnectionPropertiesDs,
Expand Down Expand Up @@ -43,6 +44,8 @@ export async function validateCreateConnectionPropertiesDs(
errors.push(Messages.CANT_CATEGORIZE_HIDDEN_TABLE(table));
}
}

await validateTableCategories(table_categories, connection);
}

if (errors.length > 0) {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
export class TableCategoryDS {
category_name: string;
category_color: string;
category_id: string;
tables: Array<string>;
}

export class CreateOrUpdateTableCategoriesDS {
connectionId: string;
master_password: string;
table_categories: Array<TableCategoryDS>;
}
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { ApiProperty } from '@nestjs/swagger';
import { IsArray, IsNotEmpty, IsString, MaxLength, MinLength } from 'class-validator';
import { IsArray, IsNotEmpty, IsOptional, IsString, MaxLength, MinLength } from 'class-validator';

export class CreateTableCategoryDto {
@ApiProperty({ type: String, maxLength: 255, example: 'Category 1' })
Expand All @@ -9,9 +9,24 @@ export class CreateTableCategoryDto {
@MaxLength(255)
category_name: string;

@ApiProperty({ type: String, maxLength: 255, example: '#FF5733' })
@IsOptional()
@IsString()
@IsNotEmpty()
@MinLength(1)
@MaxLength(255)
category_color: string;

@ApiProperty({ type: String, example: ['table1', 'table2'], isArray: true })
@IsNotEmpty()
@IsArray()
@IsString({ each: true })
tables: string[];

@ApiProperty({ type: String, maxLength: 255, example: 'cat-001' })
@IsString()
@IsNotEmpty()
@MinLength(1)
@MaxLength(255)
category_id: string;
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,14 @@ import { ApiProperty } from '@nestjs/swagger';

export class FoundTableCategoryRo {
@ApiProperty({ type: String })
id: string;
category_id: string;

@ApiProperty({ type: String })
category_name: string;

@ApiProperty({ type: String })
category_color: string;

@ApiProperty({ isArray: true, type: String })
tables: Array<string>;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
import { TableCategoriesEntity } from '../table-categories.entity.js';
import { ITableCategoriesCustomRepository } from './table-categories-repository.interface.js';

export const tableCategoriesCustomRepositoryExtension: ITableCategoriesCustomRepository = {
async findTableCategoriesForConnection(connectionId: string): Promise<Array<TableCategoriesEntity>> {
return this.createQueryBuilder('table_categories')
.leftJoin('table_categories.connection_properties', 'connection_properties')
.leftJoin('connection_properties.connection', 'connection')
.where('connection.id = :connectionId', { connectionId: connectionId })
.getMany();
},
};
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
import { TableCategoriesEntity } from '../table-categories.entity.js';

export interface ITableCategoriesCustomRepository {
findTableCategoriesForConnection(connectionId: string): Promise<Array<TableCategoriesEntity>>;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
import { Body, Controller, Get, Inject, Injectable, Put, UseGuards, UseInterceptors } from '@nestjs/common';
import { ApiBearerAuth, ApiBody, ApiOperation, ApiParam, ApiResponse, ApiTags } from '@nestjs/swagger';
import { UseCaseType } from '../../common/data-injection.tokens.js';
import { MasterPassword } from '../../decorators/master-password.decorator.js';
import { SlugUuid } from '../../decorators/slug-uuid.decorator.js';
import { ConnectionEditGuard } from '../../guards/connection-edit.guard.js';
import { ConnectionReadGuard } from '../../guards/connection-read.guard.js';
import { SentryInterceptor } from '../../interceptors/sentry.interceptor.js';
import { CreateOrUpdateTableCategoriesDS } from './data-sctructures/create-or-update-table-categories.ds.js';
import { CreateTableCategoryDto } from './dto/create-table-category.dto.js';
import { FoundTableCategoryRo } from './dto/found-table-category.ro.js';
import { ICreateTableCategories, IFindTableCategories } from './use-cases/table-categories-use-cases.interface.js';

@UseInterceptors(SentryInterceptor)
@Controller('table-categories')
@ApiBearerAuth()
@ApiTags('Table categories')
@Injectable()
export class TableCategoriesController {
constructor(
@Inject(UseCaseType.CREATE_UPDATE_TABLE_CATEGORIES)
private readonly createTableCategoriesUseCase: ICreateTableCategories,
@Inject(UseCaseType.FIND_TABLE_CATEGORIES)
private readonly findTableCategoriesUseCase: IFindTableCategories,
) {}

@ApiOperation({ summary: 'Add new table categories' })
@ApiBody({ type: CreateTableCategoryDto, isArray: true })
@ApiResponse({
status: 200,
description: 'Table categories created.',
type: FoundTableCategoryRo,
isArray: true,
})
@ApiParam({ name: 'connectionId', required: true })
@UseGuards(ConnectionEditGuard)
@Put('/:connectionId')
async createTableCategories(
@SlugUuid('connectionId') connectionId: string,
@MasterPassword() masterPwd: string,
@Body() requestBody: CreateTableCategoryDto[],
): Promise<Array<FoundTableCategoryRo>> {
const inputData: CreateOrUpdateTableCategoriesDS = {
connectionId,
master_password: masterPwd,
table_categories: requestBody,
};
return await this.createTableCategoriesUseCase.execute(inputData);
}

@ApiOperation({ summary: 'Find table categories' })
@ApiResponse({
status: 200,
description: 'Table categories found.',
type: FoundTableCategoryRo,
isArray: true,
})
@ApiParam({ name: 'connectionId', required: true })
@UseGuards(ConnectionReadGuard)
@Get('/:connectionId')
async findTableCategories(@SlugUuid('connectionId') connectionId: string): Promise<Array<FoundTableCategoryRo>> {
return await this.findTableCategoriesUseCase.execute(connectionId);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,12 @@ export class TableCategoriesEntity {
@Column({ type: 'varchar', length: 255 })
category_name: string;

@Column({ type: 'varchar', length: 255, nullable: false })
category_id: string;

@Column({ type: 'varchar', length: 255, default: null, nullable: true })
category_color: string;

@Column('varchar', { array: true, default: null })
tables: string[];

Expand Down
39 changes: 39 additions & 0 deletions backend/src/entities/table-categories/table-categories.module.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
import { TypeOrmModule } from '@nestjs/typeorm';
import { GlobalDatabaseContext } from '../../common/application/global-database-context.js';
import { BaseType, UseCaseType } from '../../common/data-injection.tokens.js';
import { MiddlewareConsumer, Module, RequestMethod } from '@nestjs/common';
import { AuthMiddleware } from '../../authorization/auth.middleware.js';
import { TableCategoriesController } from './table-categories.controller.js';
import { FindTableCategoriesUseCase } from './use-cases/find-table-categories.use.case.js';
import { CreateOrUpdateTableCategoriesUseCase } from './use-cases/create-or-update-table-categories.use.case.js';
import { UserEntity } from '../user/user.entity.js';
import { LogOutEntity } from '../log-out/log-out.entity.js';

@Module({
imports: [TypeOrmModule.forFeature([UserEntity, LogOutEntity])],
providers: [
{
provide: BaseType.GLOBAL_DB_CONTEXT,
useClass: GlobalDatabaseContext,
},
{
provide: UseCaseType.FIND_TABLE_CATEGORIES,
useClass: FindTableCategoriesUseCase,
},
{
provide: UseCaseType.CREATE_UPDATE_TABLE_CATEGORIES,
useClass: CreateOrUpdateTableCategoriesUseCase,
},
],
controllers: [TableCategoriesController],
})
export class TableCategoriesModule {
public configure(consumer: MiddlewareConsumer): any {
consumer
.apply(AuthMiddleware)
.forRoutes(
{ path: '/table-categories/:connectionId/', method: RequestMethod.GET },
{ path: '/table-categories/:connectionId/', method: RequestMethod.PUT },
);
}
}
Loading