Skip to content

feat: add support for extending the framework with custom modules#832

Merged
marcinkrasowski merged 6 commits intomainfrom
feat/custom-modules
Mar 24, 2026
Merged

feat: add support for extending the framework with custom modules#832
marcinkrasowski merged 6 commits intomainfrom
feat/custom-modules

Conversation

@marcinkrasowski
Copy link
Copy Markdown
Collaborator

@marcinkrasowski marcinkrasowski commented Mar 23, 2026

Summary

  • Add createModule() factory to @o2s/framework that generates NestJS dynamic modules from config, enabling developers to define new base modules (e.g. documents, reports, warranties) beyond the core framework modules
  • Add custom-module Turbo generator that scaffolds a complete module package in packages/modules/
  • Include a working "documents" example module with mocked integration implementation and full documentation guide

Motivation

The O2S framework provides a fixed set of core modules (invoices, tickets, orders, etc.). Developers can extend existing modules but cannot add entirely new domain-specific modules that follow the same abstract-service/swappable-integration pattern. This change closes that gap.

How it works

Custom modules follow the same architecture as core modules:

  1. Module definition (packages/modules/<name>/) — contains the abstract service, normalized data model, REST controller, and barrel export. This is the reusable contract, analogous to what @o2s/framework provides for core modules.

  2. Integration implementation (packages/integrations/<integration>/src/modules/<name>/) — contains the concrete service implementation and data mappers, analogous to how the mocked integration implements core modules.

  3. Registration — custom modules are registered directly in app.module.ts using createModule(), following the same pattern as the SurveyJS module:

    const DocumentsModule = createModule('documents');
    export const DocumentsBaseModule = DocumentsModule.register({
        name: 'documents',
        service: Documents.Service,
        serviceImpl: Documents.MockedService,
        controller: Documents.Controller,
    });
  4. Block consumption — blocks import custom module services from @o2s/configs.integrations exactly like core services, using the same useExisting DI pattern.

Changes

Framework (packages/framework/)

  • New createModule() factory at src/utils/create-module.ts — generates a @Module({}) class with register() static method, mirroring core modules like InvoiceModule
  • Exported from @o2s/framework/modules alongside existing core modules

Example module (packages/modules/documents/)

  • New standalone package with abstract DocumentService, Document model, DocumentController, and full package config (tsconfig, eslint, prettier, vitest, turbo)

Mocked integration (packages/integrations/mocked/)

  • New documents/ module with MockedDocumentService implementation and mock data mapper
  • Imports abstract types from @o2s/modules.documents, exports concrete implementation
  • Added @o2s/modules.documents as dependency, updated turbo.json build ordering

Config (packages/configs/integrations/)

  • New documents.ts re-exporting Service, MockedService, Controller, Model from the mocked integration — follows the same pattern as core module configs like
    invoices.ts

API Harmonization (apps/api-harmonization/)

  • Registers DocumentsBaseModule via createModule() in app.module.ts

Generator (turbo/generators/)

  • New custom-module generator with 12 templates scaffolding a complete module package at packages/modules/<name>/
  • Generates: model, abstract service, controller, barrel export, package.json, tsconfig, eslint, prettier, gitignore, lint-staged, vitest, turbo configs

Documentation (apps/docs/)

  • New guide: guides/integrations/extending-framework-modules.md — complete walkthrough covering file structure, step-by-step manual setup, generator usage, multi-integration
    support, block consumption, and frontend SDK extension
  • Updated: guides/integrations/overview.md, guides/integrations/adding-new-integrations.md, guides/using-generators.md,
    main-components/harmonization-app/module-structure.md

Test plan

  • npm run build passes (all 51 tasks)
  • npm run lint passes for framework, mocked integration, configs
  • npm run test passes for framework (100% coverage) and mocked integration
  • Run npm run generate → select custom-module → verify package scaffolded correctly
  • Run npm run dev → verify GET /documents and GET /documents/:id endpoints respond with mock data
  • Verify a block can inject Documents.Service from @o2s/configs.integrations

…mework

Enable developers to define new base modules beyond the core modules using `createModule()` and `ApiConfig.customModules`.

Includes `registerCustomModules()` helper, example documents module in mocked integration, and documentation guide.

(cherry picked from commit accf9ab)
@marcinkrasowski marcinkrasowski self-assigned this Mar 23, 2026
@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented Mar 23, 2026

Walkthrough

Adds a createModule() factory and a new documents base module (package + mocked integration), wires the documents module into the NestJS AppModule, adds generator templates for custom modules, and updates documentation and configs to support custom framework modules.

Changes

Cohort / File(s) Summary
Framework Core
packages/framework/src/utils/create-module.ts, packages/framework/src/index.ts
Add createModule() factory and export related types (CustomModuleConfig, RegisterableModule) to public API.
Documents Module Package
packages/modules/documents/...
New module package: models, abstract DocumentService, DocumentController, barrel exports, package/tsconfig/lint/prettier/vitest/turbo configs, and .gitignore.
Mocked Documents Implementation
packages/integrations/mocked/src/modules/documents/..., packages/integrations/mocked/package.json
Mocked MockedDocumentService, documents.mapper.ts with filtering & pagination, integration barrel export (Documents), and added runtime dependency @o2s/modules.documents.
App Module Registration
apps/api-harmonization/src/app.module.ts
Register DocumentsBaseModule via createModule('documents') in NestJS imports and include Documents in integrations config imports.
Configs & Models
packages/configs/integrations/src/models/documents.ts, packages/configs/integrations/src/models/index.ts
Add Documents re-exports to integration models barrel to expose Service/MockedService/Controller/Model.
Generators & Templates
turbo/generators/config.ts, turbo/generators/templates/custom-module/...
Add custom-module Plop generator and templates (model, service, controller, index, package.json, tsconfig, lint/prettier/vitest/turbo configs, gitignore, etc.).
Documentation & Guides
apps/docs/docs/guides/integrations/..., apps/docs/docs/main-components/...
New "Extending framework modules" guide, generator docs, module-structure updates, and small integrations overview edit.
Misc / Changeset
packages/integrations/mocked/turbo.json, .changeset/social-mammals-beam.md, Medusa docs files
Turbo build deps updated to include the new module build; added changeset for minor bumps; minor MD table formatting fixes.

Sequence Diagram(s)

sequenceDiagram
    participant App as App Module
    participant Factory as createModule()
    participant DI as DI Container
    participant Abstract as DocumentService<br/>(abstract)
    participant Impl as MockedDocumentService
    participant Ctrl as DocumentController
    participant Client as HTTP Client

    App->>Factory: createModule('documents') / register(config)
    Factory-->>App: DynamicModule class
    App->>DI: import Module
    DI->>DI: Module.register(config) -> provide token config.service -> useClass config.serviceImpl
    DI->>Impl: instantiate MockedDocumentService
    DI->>Ctrl: instantiate DocumentController (inject DocumentService token)

    Client->>Ctrl: GET /documents
    Ctrl->>Abstract: call getDocumentList(query, authorization)
    Abstract->>Impl: resolved to MockedDocumentService
    Impl->>Impl: mapDocuments(query) -> produces Observable<Documents>
    Impl-->>Ctrl: return Observable
    Ctrl-->>Client: HTTP 200 + payload
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~22 minutes

Possibly related PRs

Suggested reviewers

  • michnowak

Poem

🐰 A tiny factory hops into the scene,
Modules sprout where only core had been,
Mocked documents hum in DI's glow,
Controllers listen, observables flow,
Hop, review, and ship — off we go! ✨

🚥 Pre-merge checks | ✅ 2 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (2 passed)
Check name Status Explanation
Title check ✅ Passed The PR title accurately and concisely summarizes the main change: adding support for extending the framework with custom modules via createModule().
Description check ✅ Passed The PR description is comprehensive and well-structured, covering what the PR does, motivation, implementation details, changes across multiple packages, and a complete test plan.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feat/custom-modules

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 2

🧹 Nitpick comments (3)
packages/integrations/mocked/src/modules/documents/documents.mapper.ts (1)

54-60: Consider throwing NotFoundException for proper HTTP 404 response.

The generic Error thrown here will propagate as a 500 Internal Server Error to clients. Per the relevant code snippet, MockedDocumentService.getDocument() calls this directly without wrapping, so the raw error reaches the controller.

Using NestJS's NotFoundException provides a proper 404 response with a meaningful error message.

♻️ Proposed fix
+import { NotFoundException } from '@nestjs/common';
 import * as Model from './documents.model';
 
 // ... MOCK_DOCUMENTS ...

 export const mapDocument = (id: string): Model.Document => {
     const document = MOCK_DOCUMENTS.find((doc) => doc.id === id);
     if (!document) {
-        throw new Error(`Document with id ${id} not found`);
+        throw new NotFoundException(`Document with id ${id} not found`);
     }
     return document;
 };
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@packages/integrations/mocked/src/modules/documents/documents.mapper.ts`
around lines 54 - 60, The mapDocument function currently throws a generic Error
when a document isn't found; change it to throw NestJS's NotFoundException so
controllers return HTTP 404s: import NotFoundException from `@nestjs/common` and
replace the throw in mapDocument (used by MockedDocumentService.getDocument)
with throw new NotFoundException(`Document with id ${id} not found`) so the
proper HTTP response and message are produced.
packages/configs/integrations/src/models/custom-modules.ts (1)

1-3: Consider making the custom modules config more flexible.

This file directly re-exports CustomModules from the mocked integration, which tightly couples the configs package to that specific integration. For a production setup, developers will need to replace this with their own custom modules config.

Consider adding a comment clarifying this is a default/example configuration that should be customized:

 import { CustomModules } from '@o2s/integrations.mocked/integration';
 
+// Default custom modules config from the mocked integration.
+// Replace with your own custom modules when using a different integration.
 export const CustomModulesConfig = CustomModules;
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@packages/configs/integrations/src/models/custom-modules.ts` around lines 1 -
3, Replace the direct re-export of CustomModules from the mocked integration by
making CustomModulesConfig explicitly a default/example placeholder and adding a
clarifying comment; update the top of the file that currently imports
CustomModules and exports CustomModulesConfig to instead document that
CustomModulesConfig is an example default (pointing to the mocked integration
symbol CustomModules) and should be replaced by consumers with their own modules
— keep the exported name CustomModulesConfig so downstream code keeps
compatibility but ensure the comment clearly states this file is a template for
production overrides.
packages/integrations/mocked/src/modules/documents/documents.service.mocked.ts (1)

15-21: Method signatures omit the authorization parameter from the abstract service.

The abstract DocumentService defines both methods with an optional authorization?: string parameter (see documents.service.ts lines 10-12), but this implementation omits them. While this works in TypeScript (optional params can be omitted in overrides), it would be clearer to include them for consistency, especially if a future non-mocked implementation needs authorization.

♻️ Suggested fix to include authorization parameter
-    getDocumentList(query: Model.GetDocumentListQuery): Observable<Model.Documents> {
+    getDocumentList(query: Model.GetDocumentListQuery, _authorization?: string): Observable<Model.Documents> {
         return of(mapDocuments(query)).pipe(responseDelay());
     }
 
-    getDocument(params: Model.GetDocumentParams): Observable<Model.Document> {
+    getDocument(params: Model.GetDocumentParams, _authorization?: string): Observable<Model.Document> {
         return of(mapDocument(params.id)).pipe(responseDelay());
     }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@packages/integrations/mocked/src/modules/documents/documents.service.mocked.ts`
around lines 15 - 21, The mocked service methods getDocumentList and getDocument
omit the optional authorization parameter present in the abstract
DocumentService; update the signatures of getDocumentList(query:
Model.GetDocumentListQuery, authorization?: string) and getDocument(params:
Model.GetDocumentParams, authorization?: string) in documents.service.mocked.ts
to include the optional authorization?: string parameter (you can ignore it
inside the mocked implementations or pass it through if needed) so the mock
matches the abstract service contract and remains consistent for future
non-mocked implementations.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@apps/docs/docs/guides/integrations/extending-framework-modules.md`:
- Around line 23-31: Update the fenced code block that shows the package
directory listing to include a language specifier for plain text (e.g., use
```text or ```plaintext) so it renders correctly; locate the fenced block
containing the tree starting with
"packages/integrations/mocked/src/modules/documents/" in the README/guide (the
block that lists documents.model.ts, documents.service.ts, etc.) and change the
opening fence from ``` to ```text (or ```plaintext).

In `@packages/framework/src/utils/create-module.ts`:
- Around line 18-42: The `@Global`() decorator on the CustomModule class is dead
code for dynamic modules; remove the decorator from the createModule factory so
globality is controlled only via the DynamicModule return value. Edit the
createModule function and delete the `@Global`() line applied to class
CustomModule (leave `@Module` and the static register(config: CustomModuleConfig):
DynamicModule implementation unchanged) and ensure the returned object still
sets global: config.global !== false.

---

Nitpick comments:
In `@packages/configs/integrations/src/models/custom-modules.ts`:
- Around line 1-3: Replace the direct re-export of CustomModules from the mocked
integration by making CustomModulesConfig explicitly a default/example
placeholder and adding a clarifying comment; update the top of the file that
currently imports CustomModules and exports CustomModulesConfig to instead
document that CustomModulesConfig is an example default (pointing to the mocked
integration symbol CustomModules) and should be replaced by consumers with their
own modules — keep the exported name CustomModulesConfig so downstream code
keeps compatibility but ensure the comment clearly states this file is a
template for production overrides.

In `@packages/integrations/mocked/src/modules/documents/documents.mapper.ts`:
- Around line 54-60: The mapDocument function currently throws a generic Error
when a document isn't found; change it to throw NestJS's NotFoundException so
controllers return HTTP 404s: import NotFoundException from `@nestjs/common` and
replace the throw in mapDocument (used by MockedDocumentService.getDocument)
with throw new NotFoundException(`Document with id ${id} not found`) so the
proper HTTP response and message are produced.

In
`@packages/integrations/mocked/src/modules/documents/documents.service.mocked.ts`:
- Around line 15-21: The mocked service methods getDocumentList and getDocument
omit the optional authorization parameter present in the abstract
DocumentService; update the signatures of getDocumentList(query:
Model.GetDocumentListQuery, authorization?: string) and getDocument(params:
Model.GetDocumentParams, authorization?: string) in documents.service.mocked.ts
to include the optional authorization?: string parameter (you can ignore it
inside the mocked implementations or pass it through if needed) so the mock
matches the abstract service contract and remains consistent for future
non-mocked implementations.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: c1905d1e-57b1-4881-84b2-23ef883e9f8f

📥 Commits

Reviewing files that changed from the base of the PR and between 5a71c75 and 16a6a76.

📒 Files selected for processing (22)
  • apps/api-harmonization/src/app.config.ts
  • apps/api-harmonization/src/app.module.ts
  • apps/docs/docs/guides/integrations/extending-framework-modules.md
  • apps/docs/docs/guides/integrations/overview.md
  • apps/docs/docs/integrations/commerce/medusa-js/how-to-setup.md
  • apps/docs/docs/integrations/commerce/medusa-js/usage.md
  • apps/docs/docs/main-components/harmonization-app/module-structure.md
  • packages/configs/integrations/src/models/custom-modules.ts
  • packages/configs/integrations/src/models/index.ts
  • packages/framework/src/api-config.ts
  • packages/framework/src/index.ts
  • packages/framework/src/utils/create-module.ts
  • packages/framework/src/utils/register-custom-modules.ts
  • packages/integrations/mocked/package.json
  • packages/integrations/mocked/src/integration.ts
  • packages/integrations/mocked/src/modules/documents/documents.controller.ts
  • packages/integrations/mocked/src/modules/documents/documents.mapper.ts
  • packages/integrations/mocked/src/modules/documents/documents.model.ts
  • packages/integrations/mocked/src/modules/documents/documents.service.mocked.ts
  • packages/integrations/mocked/src/modules/documents/documents.service.ts
  • packages/integrations/mocked/src/modules/documents/index.ts
  • packages/integrations/mocked/src/modules/index.ts

Comment thread apps/docs/docs/guides/integrations/extending-framework-modules.md
Comment thread packages/framework/src/utils/create-module.ts
@github-actions
Copy link
Copy Markdown
Contributor

github-actions Bot commented Mar 23, 2026

Coverage Report for packages/configs/vitest-config

Status Category Percentage Covered / Total
🔵 Lines 78.19% 1732 / 2215
🔵 Statements 77.14% 1822 / 2362
🔵 Functions 74.33% 524 / 705
🔵 Branches 65.79% 1148 / 1745
File CoverageNo changed files found.
Generated in workflow #559 for commit 544e729 by the Vitest Coverage Report Action

@michnowak
Copy link
Copy Markdown
Contributor

@marcinkrasowski Should we also add a changelist file for these changes?

@marcinkrasowski
Copy link
Copy Markdown
Collaborator Author

@marcinkrasowski Should we also add a changelist file for these changes?

added

Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🧹 Nitpick comments (3)
packages/modules/documents/src/documents.model.ts (1)

20-30: Consider adding class-validator decorators for runtime validation.

The request DTOs (GetDocumentListQuery, GetDocumentParams) define the shape but lack runtime validation. Adding decorators like @IsOptional(), @IsInt(), @IsString() would enable NestJS's ValidationPipe to enforce constraints.

♻️ Example with validation decorators
import { IsInt, IsOptional, IsString, IsIn } from 'class-validator';
import { Type } from 'class-transformer';

export class GetDocumentListQuery {
    `@IsOptional`()
    `@Type`(() => Number)
    `@IsInt`()
    offset?: number;

    `@IsOptional`()
    `@Type`(() => Number)
    `@IsInt`()
    limit?: number;

    `@IsOptional`()
    `@IsIn`(['CONTRACT', 'REPORT', 'POLICY', 'STATEMENT'])
    type?: DocumentType;
    // ...
}
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@packages/modules/documents/src/documents.model.ts` around lines 20 - 30, Add
runtime validation decorators to the DTOs so NestJS ValidationPipe can enforce
constraints: on GetDocumentListQuery annotate offset and limit with
`@IsOptional`(), `@Type`(() => Number) and `@IsInt`(); annotate search with
`@IsOptional`() and `@IsString`(); annotate type and status with `@IsOptional`() and
`@IsIn`([...]) using your DocumentType/DocumentStatus values; on GetDocumentParams
annotate id with `@IsString`() or `@IsUUID`() and `@IsNotEmpty`(); also import the
needed decorators from class-validator and Type from class-transformer and
update exports accordingly.
packages/integrations/mocked/src/modules/documents/documents.service.ts (1)

18-20: Consider wrapping synchronous errors in Observable error handling.

Per the context from documents.mapper.ts, mapDocument() throws a synchronous Error when the document isn't found. Since this occurs before of() creates the Observable, the error propagates as a thrown exception rather than an Observable error stream.

For consistency with RxJS patterns and to allow downstream error handling via catchError(), consider using defer():

♻️ Proposed refactor for consistent Observable error handling
-import { Observable, of } from 'rxjs';
+import { defer, Observable, of } from 'rxjs';
 getDocument(params: Model.GetDocumentParams): Observable<Model.Document> {
-    return of(mapDocument(params.id)).pipe(responseDelay());
+    return defer(() => of(mapDocument(params.id))).pipe(responseDelay());
 }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@packages/integrations/mocked/src/modules/documents/documents.service.ts`
around lines 18 - 20, getDocument currently calls mapDocument(params.id)
synchronously inside of(), so any synchronous Error thrown by mapDocument (from
documents.mapper.ts) escapes as a thrown exception; wrap the call in an
Observable factory (e.g., use RxJS defer or another lazy creator) so mapDocument
is executed when subscribed and any thrown error is emitted as an Observable
error stream, then apply responseDelay() as before; update the implementation in
getDocument to call defer(() =>
of(mapDocument(params.id))).pipe(responseDelay()) (or equivalent) so downstream
operators like catchError() can handle the error.
packages/modules/documents/src/documents.controller.ts (1)

14-20: Consider adding validation for query parameters.

The GetDocumentListQuery contains offset and limit parameters that arrive as strings from the query string. Without a ValidationPipe or ParseIntPipe, these may need explicit transformation to numbers.

If the framework uses a global validation pipe, this is already handled. Otherwise, consider adding class-validator decorators to the model or using @Query(new ValidationPipe({ transform: true })).

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@packages/modules/documents/src/documents.controller.ts` around lines 14 - 20,
The getDocumentList controller accepts Model.GetDocumentListQuery where
offset/limit arrive as strings; ensure they are validated/transformed to numbers
by either adding class-validator/class-transformer decorators to the
GetDocumentListQuery DTO (e.g., `@IsInt`(), `@Type`(() => Number) on offset/limit)
or by applying a ValidationPipe with transform enabled to the `@Query` (e.g.,
`@Query`(new ValidationPipe({ transform: true })) query:
Model.GetDocumentListQuery), or use ParseIntPipe on individual params; update
the getDocumentList signature and/or DTO so documentService.getDocumentList
always receives numeric offset/limit.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@turbo/generators/templates/custom-module/service.hbs`:
- Around line 10-18: The template's type names for collection endpoints are
inconsistent: update the return types and related model references used in the
abstract methods get{{pascalCase name}}List and get{{pascalCase name}} to use
the pluralized model name (e.g., change Model.{{pascalCase name}}List to
Model.{{pascalCase name}}s) so generated modules match the example Documents
pattern; locate the declarations of get{{pascalCase name}}List, get{{pascalCase
name}}, and any other occurrences of Model.{{pascalCase name}}List in the
template and replace them with the plural form Model.{{pascalCase name}}s (and
adjust any corresponding type names like response types) to align with REST
collection naming.

---

Nitpick comments:
In `@packages/integrations/mocked/src/modules/documents/documents.service.ts`:
- Around line 18-20: getDocument currently calls mapDocument(params.id)
synchronously inside of(), so any synchronous Error thrown by mapDocument (from
documents.mapper.ts) escapes as a thrown exception; wrap the call in an
Observable factory (e.g., use RxJS defer or another lazy creator) so mapDocument
is executed when subscribed and any thrown error is emitted as an Observable
error stream, then apply responseDelay() as before; update the implementation in
getDocument to call defer(() =>
of(mapDocument(params.id))).pipe(responseDelay()) (or equivalent) so downstream
operators like catchError() can handle the error.

In `@packages/modules/documents/src/documents.controller.ts`:
- Around line 14-20: The getDocumentList controller accepts
Model.GetDocumentListQuery where offset/limit arrive as strings; ensure they are
validated/transformed to numbers by either adding
class-validator/class-transformer decorators to the GetDocumentListQuery DTO
(e.g., `@IsInt`(), `@Type`(() => Number) on offset/limit) or by applying a
ValidationPipe with transform enabled to the `@Query` (e.g., `@Query`(new
ValidationPipe({ transform: true })) query: Model.GetDocumentListQuery), or use
ParseIntPipe on individual params; update the getDocumentList signature and/or
DTO so documentService.getDocumentList always receives numeric offset/limit.

In `@packages/modules/documents/src/documents.model.ts`:
- Around line 20-30: Add runtime validation decorators to the DTOs so NestJS
ValidationPipe can enforce constraints: on GetDocumentListQuery annotate offset
and limit with `@IsOptional`(), `@Type`(() => Number) and `@IsInt`(); annotate search
with `@IsOptional`() and `@IsString`(); annotate type and status with `@IsOptional`()
and `@IsIn`([...]) using your DocumentType/DocumentStatus values; on
GetDocumentParams annotate id with `@IsString`() or `@IsUUID`() and `@IsNotEmpty`();
also import the needed decorators from class-validator and Type from
class-transformer and update exports accordingly.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 31c23dc8-b813-426a-850f-89b52ce51aac

📥 Commits

Reviewing files that changed from the base of the PR and between 16a6a76 and 58d5807.

⛔ Files ignored due to path filters (1)
  • package-lock.json is excluded by !**/package-lock.json
📒 Files selected for processing (40)
  • .changeset/social-mammals-beam.md
  • apps/api-harmonization/src/app.module.ts
  • apps/docs/docs/guides/integrations/adding-new-integrations.md
  • apps/docs/docs/guides/integrations/extending-framework-modules.md
  • apps/docs/docs/guides/integrations/overview.md
  • apps/docs/docs/guides/using-generators.md
  • apps/docs/docs/main-components/harmonization-app/module-structure.md
  • packages/configs/integrations/src/models/documents.ts
  • packages/configs/integrations/src/models/index.ts
  • packages/framework/src/index.ts
  • packages/framework/src/utils/create-module.ts
  • packages/integrations/mocked/package.json
  • packages/integrations/mocked/src/modules/documents/documents.mapper.ts
  • packages/integrations/mocked/src/modules/documents/documents.service.ts
  • packages/integrations/mocked/src/modules/documents/index.ts
  • packages/modules/documents/.gitignore
  • packages/modules/documents/.prettierrc.mjs
  • packages/modules/documents/eslint.config.mjs
  • packages/modules/documents/lint-staged.config.mjs
  • packages/modules/documents/package.json
  • packages/modules/documents/src/documents.controller.ts
  • packages/modules/documents/src/documents.model.ts
  • packages/modules/documents/src/documents.service.ts
  • packages/modules/documents/src/index.ts
  • packages/modules/documents/tsconfig.json
  • packages/modules/documents/turbo.json
  • packages/modules/documents/vitest.config.mjs
  • turbo/generators/config.ts
  • turbo/generators/templates/custom-module/controller.hbs
  • turbo/generators/templates/custom-module/eslint.config.hbs
  • turbo/generators/templates/custom-module/gitignore.hbs
  • turbo/generators/templates/custom-module/index.hbs
  • turbo/generators/templates/custom-module/lint-staged.config.hbs
  • turbo/generators/templates/custom-module/model.hbs
  • turbo/generators/templates/custom-module/package.hbs
  • turbo/generators/templates/custom-module/prettierrc.hbs
  • turbo/generators/templates/custom-module/service.hbs
  • turbo/generators/templates/custom-module/tsconfig.hbs
  • turbo/generators/templates/custom-module/turbo.hbs
  • turbo/generators/templates/custom-module/vitestConfig.hbs
✅ Files skipped from review due to trivial changes (25)
  • apps/docs/docs/guides/integrations/adding-new-integrations.md
  • packages/modules/documents/lint-staged.config.mjs
  • packages/modules/documents/vitest.config.mjs
  • packages/configs/integrations/src/models/index.ts
  • packages/modules/documents/tsconfig.json
  • packages/modules/documents/.gitignore
  • apps/docs/docs/guides/integrations/overview.md
  • packages/modules/documents/src/index.ts
  • turbo/generators/templates/custom-module/gitignore.hbs
  • turbo/generators/templates/custom-module/turbo.hbs
  • turbo/generators/templates/custom-module/vitestConfig.hbs
  • turbo/generators/templates/custom-module/lint-staged.config.hbs
  • packages/configs/integrations/src/models/documents.ts
  • .changeset/social-mammals-beam.md
  • apps/docs/docs/main-components/harmonization-app/module-structure.md
  • apps/docs/docs/guides/using-generators.md
  • packages/modules/documents/turbo.json
  • turbo/generators/templates/custom-module/eslint.config.hbs
  • packages/modules/documents/.prettierrc.mjs
  • turbo/generators/templates/custom-module/tsconfig.hbs
  • packages/modules/documents/package.json
  • apps/docs/docs/guides/integrations/extending-framework-modules.md
  • packages/framework/src/utils/create-module.ts
  • turbo/generators/templates/custom-module/model.hbs
  • turbo/generators/templates/custom-module/package.hbs
🚧 Files skipped from review as they are similar to previous changes (5)
  • packages/integrations/mocked/package.json
  • packages/framework/src/index.ts
  • packages/integrations/mocked/src/modules/documents/index.ts
  • packages/integrations/mocked/src/modules/documents/documents.mapper.ts
  • apps/api-harmonization/src/app.module.ts

Comment thread turbo/generators/templates/custom-module/service.hbs
@vercel
Copy link
Copy Markdown

vercel Bot commented Mar 23, 2026

The latest updates on your projects. Learn more about Vercel for GitHub.

1 Skipped Deployment
Project Deployment Actions Updated (UTC)
o2s-docs Skipped Skipped Mar 23, 2026 4:11pm

Request Review

@marcinkrasowski marcinkrasowski merged commit 7ac16b0 into main Mar 24, 2026
15 checks passed
@marcinkrasowski marcinkrasowski deleted the feat/custom-modules branch March 24, 2026 09:42
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.

3 participants