eggjs / tegg

Strong Type framework with eggjs.
MIT License
240 stars 35 forks source link

feat: support inject in constructor #237

Closed killagu closed 1 month ago

killagu commented 1 month ago
Checklist
Affected core subsystem(s)
Description of change

Summary by CodeRabbit

coderabbitai[bot] commented 1 month ago

Walkthrough

The changes enhance the dependency injection framework within the Egg.js ecosystem. Key updates include the addition of new decorators for managing injections, modifications to existing decorator functions to support constructor injection, and the introduction of new utility methods for handling metadata and constructor argument names. New classes and interfaces have been created to facilitate these features, along with corresponding test cases to ensure functionality. Additionally, several configuration files and modules have been added to support dynamic features and application structure.

Changes

File(s) Change Summary
README.md Added an example for the HelloService class demonstrating the use of the Inject annotation in the constructor.
core/common-util/src/ObjectUtils.ts Introduced getConstructorArgNameList method for retrieving constructor argument names from a class.
core/common-util/test/ObjectUtil.test.ts Added decorators InitTypeQualifier, ModuleQualifier, and Inject, along with tests for getConstructorArgNameList.
core/core-decorator/src/decorator/*.ts Updated several decorators (ConfigSourceQualifier, EggQualifier, InitTypeQualifier, Inject, ModuleQualifier) to accept an optional parameterIndex to enhance injection flexibility.
core/core-decorator/src/util/*.ts Introduced new methods in MetadataUtil, PrototypeUtil, and QualifierUtil to manage injection types and metadata.
core/metadata/src/impl/*.ts Modified EggPrototypeBuilder and EggPrototypeImpl to support constructor injection and manage injection types effectively.
core/runtime/src/impl/*.ts Enhanced EggObjectImpl and EggObjectUtil to support constructor-based dependency injection and proxy creation for dynamic property manipulation.
core/types/core-decorator/enum/*.ts Added InjectType enum and CONSTRUCTOR_QUALIFIER_META_DATA symbol for managing injection metadata.
core/types/core-decorator/model/*.ts Introduced InjectConstructorInfo and InjectConstructorProto interfaces for managing constructor injection details.
plugin/tegg/test/*.ts Added tests for module configuration and controller functionality in the Egg.js application.
plugin/tegg/test/fixtures/apps/constructor-module-config/*.ts Introduced various classes, controllers, and configuration files to support dynamic module features and application structure.
plugin/tegg/test/fixtures/apps/constructor-module-config/config/*.js Added configuration files for module settings and dynamic features.
plugin/tegg/test/fixtures/apps/constructor-module-config/modules/module-with-config/*.ts Created new modules and classes with dependency injection capabilities.
plugin/tegg/test/fixtures/apps/constructor-module-config/package.json Introduced package.json for the application and modules to define dependencies and configurations.

Sequence Diagram(s)

sequenceDiagram
    participant User
    participant App
    participant Controller
    participant Service

    User->>App: Request /config
    App->>Controller: Call baseDir()
    Controller->>Service: Retrieve configuration
    Service-->>Controller: Return config values
    Controller-->>App: Send response with config
    App-->>User: Return response

🐰 In the code, we find new ways,
To inject and log our days.
With constructors now in play,
Dependencies dance and sway.
In Egg.js, we hop with glee,
Celebrating changes, oh so free! 🎉


📜 Recent review details **Configuration used: CodeRabbit UI** **Review profile: CHILL**
📥 Commits Files that changed from the base of the PR and between 35e4a06349b4a583d0cfb61046001646d26290b4 and 16ee600c77f89f0b7761652fd64028469afef98e.
📒 Files selected for processing (3) * core/core-decorator/src/util/QualifierUtil.ts (2 hunks) * core/core-decorator/test/decorators.test.ts (3 hunks) * core/metadata/test/LoadUnit.test.ts (1 hunks)
🚧 Files skipped from review as they are similar to previous changes (2) * core/core-decorator/test/decorators.test.ts * core/metadata/test/LoadUnit.test.ts
🔇 Additional comments (1)
core/core-decorator/src/util/QualifierUtil.ts (1)
`27-35`: _:hammer_and_wrench: Refactor suggestion_ _:warning: Potential issue_ **Address potential issues in `addInjectQualifier` method** There are a few potential issues in this method that need to be addressed: 1. Potential out-of-bounds access when using `parameterIndex`. 2. Possible `undefined` access when using `property!`. To resolve these issues, please consider the following changes: ```diff static addInjectQualifier(clazz: EggProtoImplClass, property: PropertyKey | undefined, parameterIndex: number | undefined, attribute: QualifierAttribute, value: QualifierValue) { if (typeof parameterIndex === 'number') { const argNames = ObjectUtils.getConstructorArgNameList(clazz); + if (parameterIndex >= 0 && parameterIndex < argNames.length) { const argName = argNames[parameterIndex]; QualifierUtil.addProperQualifier(clazz, argName, attribute, value); + } else { + throw new Error(`Invalid parameterIndex ${parameterIndex} for class ${clazz.name}`); + } } else { + if (property !== undefined) { QualifierUtil.addProperQualifier((clazz as any).constructor, property!, attribute, value); + } else { + throw new Error(`Property key is undefined for class ${clazz.name}`); + } } } ``` These changes will: 1. Add a bounds check for `parameterIndex` to prevent out-of-bounds access. 2. Add a check for `property` to ensure it's not `undefined` before using it. 3. Throw informative errors when invalid inputs are provided. Additionally, consider refactoring the method to improve readability and reduce duplication: ```typescript static addInjectQualifier(clazz: EggProtoImplClass, property: PropertyKey | undefined, parameterIndex: number | undefined, attribute: QualifierAttribute, value: QualifierValue) { let targetClass: EggProtoImplClass; let targetProperty: PropertyKey; if (typeof parameterIndex === 'number') { const argNames = ObjectUtils.getConstructorArgNameList(clazz); if (parameterIndex < 0 || parameterIndex >= argNames.length) { throw new Error(`Invalid parameterIndex ${parameterIndex} for class ${clazz.name}`); } targetClass = clazz; targetProperty = argNames[parameterIndex]; } else if (property !== undefined) { targetClass = (clazz as any).constructor; targetProperty = property; } else { throw new Error(`Either parameterIndex or property must be provided for class ${clazz.name}`); } QualifierUtil.addProperQualifier(targetClass, targetProperty, attribute, value); } ``` This refactored version: 1. Centralizes the error handling. 2. Reduces duplication by using a single call to `addProperQualifier`. 3. Improves readability by clearly separating the logic for determining the target class and property. Note: This review comment addresses the issues raised in the past review comments, which are still valid for this code segment.
--- Thank you for using CodeRabbit. We offer it for free to the OSS community and would appreciate your support in helping us grow. If you find it useful, would you consider giving us a shout-out on your favorite social media?
❤️ Share - [X](https://twitter.com/intent/tweet?text=I%20just%20used%20%40coderabbitai%20for%20my%20code%20review%2C%20and%20it%27s%20fantastic%21%20It%27s%20free%20for%20OSS%20and%20offers%20a%20free%20trial%20for%20the%20proprietary%20code.%20Check%20it%20out%3A&url=https%3A//coderabbit.ai) - [Mastodon](https://mastodon.social/share?text=I%20just%20used%20%40coderabbitai%20for%20my%20code%20review%2C%20and%20it%27s%20fantastic%21%20It%27s%20free%20for%20OSS%20and%20offers%20a%20free%20trial%20for%20the%20proprietary%20code.%20Check%20it%20out%3A%20https%3A%2F%2Fcoderabbit.ai) - [Reddit](https://www.reddit.com/submit?title=Great%20tool%20for%20code%20review%20-%20CodeRabbit&text=I%20just%20used%20CodeRabbit%20for%20my%20code%20review%2C%20and%20it%27s%20fantastic%21%20It%27s%20free%20for%20OSS%20and%20offers%20a%20free%20trial%20for%20proprietary%20code.%20Check%20it%20out%3A%20https%3A//coderabbit.ai) - [LinkedIn](https://www.linkedin.com/sharing/share-offsite/?url=https%3A%2F%2Fcoderabbit.ai&mini=true&title=Great%20tool%20for%20code%20review%20-%20CodeRabbit&summary=I%20just%20used%20CodeRabbit%20for%20my%20code%20review%2C%20and%20it%27s%20fantastic%21%20It%27s%20free%20for%20OSS%20and%20offers%20a%20free%20trial%20for%20proprietary%20code)
🪧 Tips ### Chat There are 3 ways to chat with [CodeRabbit](https://coderabbit.ai): - Review comments: Directly reply to a review comment made by CodeRabbit. Example: - `I pushed a fix in commit , please review it.` - `Generate unit testing code for this file.` - `Open a follow-up GitHub issue for this discussion.` - Files and specific lines of code (under the "Files changed" tab): Tag `@coderabbitai` in a new review comment at the desired location with your query. Examples: - `@coderabbitai generate unit testing code for this file.` - `@coderabbitai modularize this function.` - PR comments: Tag `@coderabbitai` in a new PR comment to ask questions about the PR branch. For the best results, please provide a very specific query, as very limited context is provided in this mode. Examples: - `@coderabbitai gather interesting stats about this repository and render them as a table. Additionally, render a pie chart showing the language distribution in the codebase.` - `@coderabbitai read src/utils.ts and generate unit testing code.` - `@coderabbitai read the files in the src/scheduler package and generate a class diagram using mermaid and a README in the markdown format.` - `@coderabbitai help me debug CodeRabbit configuration file.` Note: Be mindful of the bot's finite context window. It's strongly recommended to break down tasks such as reading entire modules into smaller chunks. For a focused discussion, use review comments to chat about specific files and their changes, instead of using the PR comments. ### CodeRabbit Commands (Invoked using PR comments) - `@coderabbitai pause` to pause the reviews on a PR. - `@coderabbitai resume` to resume the paused reviews. - `@coderabbitai review` to trigger an incremental review. This is useful when automatic reviews are disabled for the repository. - `@coderabbitai full review` to do a full review from scratch and review all the files again. - `@coderabbitai summary` to regenerate the summary of the PR. - `@coderabbitai resolve` resolve all the CodeRabbit review comments. - `@coderabbitai configuration` to show the current CodeRabbit configuration for the repository. - `@coderabbitai help` to get help. ### Other keywords and placeholders - Add `@coderabbitai ignore` anywhere in the PR description to prevent this PR from being reviewed. - Add `@coderabbitai summary` to generate the high-level summary at a specific location in the PR description. - Add `@coderabbitai` anywhere in the PR title to generate the title automatically. ### CodeRabbit Configuration File (`.coderabbit.yaml`) - You can programmatically configure CodeRabbit by adding a `.coderabbit.yaml` file to the root of your repository. - Please see the [configuration documentation](https://docs.coderabbit.ai/guides/configure-coderabbit) for more information. - If your editor has YAML language server enabled, you can add the path at the top of this file to enable auto-completion and validation: `# yaml-language-server: $schema=https://coderabbit.ai/integrations/schema.v2.json` ### Documentation and Community - Visit our [Documentation](https://coderabbit.ai/docs) for detailed information on how to use CodeRabbit. - Join our [Discord Community](https://discord.com/invite/GsXnASn26c) to get help, request features, and share feedback. - Follow us on [X/Twitter](https://twitter.com/coderabbitai) for updates and announcements.