diff --git a/api/src/cms/cms.module.ts b/api/src/cms/cms.module.ts index 0c23fe1..8cc23d5 100644 --- a/api/src/cms/cms.module.ts +++ b/api/src/cms/cms.module.ts @@ -6,13 +6,7 @@ * 2. All derivative works must include clear attribution to the original creator and software, Hexastack and Hexabot, in a prominent location (e.g., in the software's "About" section, documentation, and README file). */ -import { - forwardRef, - MiddlewareConsumer, - Module, - NestModule, - RequestMethod, -} from '@nestjs/common'; +import { forwardRef, Module } from '@nestjs/common'; import { MongooseModule } from '@nestjs/mongoose'; import { AttachmentModule } from '@/attachment/attachment.module'; @@ -21,7 +15,6 @@ import { ChatModule } from '@/chat/chat.module'; import { ContentTypeController } from './controllers/content-type.controller'; import { ContentController } from './controllers/content.controller'; import { MenuController } from './controllers/menu.controller'; -import { ContentMiddleWare } from './middlewares/content.middleware'; import { ContentTypeRepository } from './repositories/content-type.repository'; import { ContentRepository } from './repositories/content.repository'; import { MenuRepository } from './repositories/menu.repository'; @@ -55,13 +48,4 @@ import { AttachmentModel } from '../attachment/schemas/attachment.schema'; ], exports: [MenuService, ContentService, ContentTypeService], }) -export class CmsModule implements NestModule { - configure(consumer: MiddlewareConsumer) { - consumer - .apply(ContentMiddleWare) - .forRoutes( - { path: 'content', method: RequestMethod.POST }, - { path: 'content/:id', method: RequestMethod.PATCH }, - ); - } -} +export class CmsModule {} diff --git a/api/src/cms/controllers/content.controller.spec.ts b/api/src/cms/controllers/content.controller.spec.ts index fcf8919..e084843 100644 --- a/api/src/cms/controllers/content.controller.spec.ts +++ b/api/src/cms/controllers/content.controller.spec.ts @@ -35,7 +35,6 @@ import { import { ContentController } from './content.controller'; import { ContentCreateDto } from '../dto/content.dto'; -import { ContentTransformInterceptor } from '../interceptors/content.interceptor'; import { ContentTypeRepository } from '../repositories/content-type.repository'; import { ContentRepository } from '../repositories/content.repository'; import { ContentType, ContentTypeModel } from '../schemas/content-type.schema'; @@ -48,7 +47,6 @@ describe('ContentController', () => { let contentService: ContentService; let contentTypeService: ContentTypeService; let attachmentService: AttachmentService; - let transformInterceptor: ContentTransformInterceptor; let contentType: ContentType; let content: Content; let attachment: Attachment; @@ -74,7 +72,6 @@ describe('ContentController', () => { AttachmentService, ContentTypeRepository, AttachmentRepository, - ContentTransformInterceptor, EventEmitter2, ], }).compile(); @@ -82,9 +79,6 @@ describe('ContentController', () => { contentService = module.get(ContentService); attachmentService = module.get(AttachmentService); contentTypeService = module.get(ContentTypeService); - transformInterceptor = module.get( - ContentTransformInterceptor, - ); contentType = await contentTypeService.findOne({ name: 'Product' }); content = await contentService.findOne({ title: 'Jean', @@ -237,28 +231,6 @@ describe('ContentController', () => { }); }); - describe('filterDynamicFields', () => { - it('should flatten dynamic fields', () => { - const result = transformInterceptor.transformDynamicFields( - contentFixtures[0], - ); - - expect(result).toEqualPayload( - { - title: 'Jean', - status: true, - subtitle: 'Jean Droit Taille Normale', - image: { - payload: { - url: 'https://images-na.ssl-images-amazon.com/images/I/31DY09uzLDL._SX38_SY50_CR,0,0,38,50_.jpg', - }, - }, - }, - ['entity', 'rag', ...IGNORED_TEST_FIELDS], - ); - }); - }); - describe('count', () => { it('should return the number of contents', async () => { jest.spyOn(contentService, 'count'); diff --git a/api/src/cms/controllers/content.controller.ts b/api/src/cms/controllers/content.controller.ts index 4b2b6e6..b1e4ec3 100644 --- a/api/src/cms/controllers/content.controller.ts +++ b/api/src/cms/controllers/content.controller.ts @@ -40,7 +40,6 @@ import { SearchFilterPipe } from '@/utils/pipes/search-filter.pipe'; import { ContentTypeService } from './../services/content-type.service'; import { ContentService } from './../services/content.service'; import { ContentCreateDto, ContentUpdateDto } from '../dto/content.dto'; -import { ContentTransformInterceptor } from '../interceptors/content.interceptor'; import { ContentType } from '../schemas/content-type.schema'; import { Content, @@ -48,9 +47,8 @@ import { ContentPopulate, ContentStub, } from '../schemas/content.schema'; -import { preprocessDynamicFields } from '../utilities'; -@UseInterceptors(ContentTransformInterceptor, CsrfInterceptor) +@UseInterceptors(CsrfInterceptor) @Controller('content') export class ContentController extends BaseController< Content, @@ -116,8 +114,7 @@ export class ContentController extends BaseController< entity: contentType?.id, }, }); - const newContent = this.filterDynamicFields(contentDto, contentType); - return await this.contentService.create(newContent); + return await this.contentService.create(contentDto); } /** @@ -186,12 +183,22 @@ export class ContentController extends BaseController< }); } - const contentsDto = result.data.map((content) => { - content.entity = targetContentType; - const dto = preprocessDynamicFields(content); - // Match headers against entity fields - return this.filterDynamicFields(dto, contentType); - }); + const contentsDto = result.data.reduce( + (acc, { title, status, ...rest }) => [ + ...acc, + { + title, + status, + entity: targetContentType, + dynamicFields: Object.keys(rest) + .filter((key) => + contentType.fields.map((field) => field.name).includes(key), + ) + .reduce((filtered, key) => ({ ...filtered, [key]: rest[key] }), {}), + }, + ], + [], + ); // Create content return await this.contentService.createMany(contentsDto); diff --git a/api/src/cms/dto/content.dto.ts b/api/src/cms/dto/content.dto.ts index 1981bc2..69c6d4d 100644 --- a/api/src/cms/dto/content.dto.ts +++ b/api/src/cms/dto/content.dto.ts @@ -43,4 +43,8 @@ export class ContentUpdateDto { @IsBoolean() @IsOptional() status?: boolean; + + @ApiPropertyOptional({ description: 'Content dynamic fields', type: Object }) + @IsOptional() + dynamicFields?: Record; } diff --git a/api/src/cms/interceptors/content.interceptor.ts b/api/src/cms/interceptors/content.interceptor.ts deleted file mode 100644 index c93a438..0000000 --- a/api/src/cms/interceptors/content.interceptor.ts +++ /dev/null @@ -1,54 +0,0 @@ -/* - * Copyright © 2024 Hexastack. All rights reserved. - * - * Licensed under the GNU Affero General Public License v3.0 (AGPLv3) with the following additional terms: - * 1. The name "Hexabot" is a trademark of Hexastack. You may not use this name in derivative works without express written permission. - * 2. All derivative works must include clear attribution to the original creator and software, Hexastack and Hexabot, in a prominent location (e.g., in the software's "About" section, documentation, and README file). - */ - -import { - Injectable, - NestInterceptor, - ExecutionContext, - CallHandler, -} from '@nestjs/common'; -import { Observable, map } from 'rxjs'; - -import { Content } from '../schemas/content.schema'; - -@Injectable() -export class ContentTransformInterceptor - implements NestInterceptor -{ - /* - -This interceptor is designed to provide a flattened representation of the 'dynamicFields'. - -The incoming data contains a 'dynamicField' object, and the interceptor is expanding it, - extracting its content as separate entries. - -After the expansion, the 'dynamicFields' property is removed. - -The interceptor will be applied on each endpoint of this controller. - */ - transformDynamicFields(data) { - if (data.dynamicFields) { - Object.keys(data.dynamicFields).forEach((key) => { - data[key] = data.dynamicFields[key]; - }); - delete data.dynamicFields; - } - return data; - } - - intercept(context: ExecutionContext, next: CallHandler): Observable { - return next.handle().pipe( - map((data) => { - // If the data is not an array, the 'transformDynamicFields' method is applied once - if (!Array.isArray(data)) { - return this.transformDynamicFields(data); - } - - return data.map((content) => { - return this.transformDynamicFields(content); - }); - }), - ); - } -} diff --git a/api/src/cms/middlewares/content.middleware.ts b/api/src/cms/middlewares/content.middleware.ts deleted file mode 100644 index addaa8c..0000000 --- a/api/src/cms/middlewares/content.middleware.ts +++ /dev/null @@ -1,20 +0,0 @@ -/* - * Copyright © 2024 Hexastack. All rights reserved. - * - * Licensed under the GNU Affero General Public License v3.0 (AGPLv3) with the following additional terms: - * 1. The name "Hexabot" is a trademark of Hexastack. You may not use this name in derivative works without express written permission. - * 2. All derivative works must include clear attribution to the original creator and software, Hexastack and Hexabot, in a prominent location (e.g., in the software's "About" section, documentation, and README file). - */ - -import { Injectable, NestMiddleware } from '@nestjs/common'; -import { NextFunction, Request, Response } from 'express'; - -import { preprocessDynamicFields } from '../utilities'; - -@Injectable() -export class ContentMiddleWare implements NestMiddleware { - use(req: Request, _res: Response, next: NextFunction) { - req.body = preprocessDynamicFields(req.body); - next(); - } -} diff --git a/api/src/cms/utilities/index.ts b/api/src/cms/utilities/index.ts deleted file mode 100644 index 1459fa6..0000000 --- a/api/src/cms/utilities/index.ts +++ /dev/null @@ -1,24 +0,0 @@ -/* - * Copyright © 2024 Hexastack. All rights reserved. - * - * Licensed under the GNU Affero General Public License v3.0 (AGPLv3) with the following additional terms: - * 1. The name "Hexabot" is a trademark of Hexastack. You may not use this name in derivative works without express written permission. - * 2. All derivative works must include clear attribution to the original creator and software, Hexastack and Hexabot, in a prominent location (e.g., in the software's "About" section, documentation, and README file). - */ - -import { ContentCreateDto } from '../dto/content.dto'; - -export const preprocessDynamicFields = ( - content: Record, -) => { - const { _csrf, title, status, entity, ...dynamicFields } = content; - const processed: ContentCreateDto & { _csrf?: string } = { - _csrf: _csrf?.toString(), - entity: entity?.toString(), - status: !!status, - title: title?.toString(), - dynamicFields, - }; - - return processed; -};