Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Rules & rule-engine backend completed in swift #30

Merged
merged 8 commits into from
Dec 4, 2024

Conversation

Dracks
Copy link
Owner

@Dracks Dracks commented Nov 5, 2024

Summary by CodeRabbit

Release Notes

  • New Features

    • Expanded API capabilities with new endpoints for rule management, including create, update, delete, apply, and condition handling.
    • Introduced new models and enumerations to support complex rule and condition management.
    • Enhanced transaction processing with asynchronous job handling through a new job framework.
    • Added new operations for managing labels, including create, update, and delete functionalities.
    • Implemented a caching mechanism to improve performance for frequently accessed data.
    • Enhanced user-related API operations with improved error handling and input validation.
    • Introduced a Cache class for generic key-value storage with expiration management.
    • Added a ListWithCursor structure for pagination support in API responses.
  • Bug Fixes

    • Updated dependency versions to enhance stability and performance.
    • Corrected error handling logic in session and graph management APIs.
  • Documentation

    • Improved OpenAPI specifications to reflect new API operations and schema changes.
  • Refactor

    • Simplified the bank transaction model by removing unnecessary classes and enums, streamlining the structure.
    • Refactored service access to utilize dependency injection for better modularity and maintainability.
    • Enhanced method signatures across various services to remove redundant parameters, improving clarity.

Copy link

coderabbitai bot commented Nov 5, 2024

Caution

Review failed

The pull request is closed.

Walkthrough

The pull request introduces several updates across multiple files, primarily focusing on version increments for @typespec dependencies in package.yaml, and significant enhancements to the OpenAPI specification for the Mr Scrooge API. New endpoints for rule management are added, including operations for creating, updating, and deleting rules and conditions. Additionally, the Swift codebase sees the removal of several classes and enums related to bank transactions, while new classes and enumerations are introduced for a rules engine. Migration schemas are also updated to reflect these changes.

Changes

File Change Summary
package.yaml Updated @typespec dependencies from 0.60.x to 0.61.x for: @typespec/compiler, @typespec/http, @typespec/json-schema, @typespec/openapi3, @typespec/rest.
src/@typespec/openapi3/openapi.yaml Added new endpoints for /labels and /rules (create, update, delete), and operations for applying rules and adding conditions. Updated schemas for Condition and Rule.
src/swift-server/bank-transaction/bank-transaction.models.swift Removed enums ConditionalRelationType, FilterConditionals and classes Condition, Rule.
src/swift-server/migrations/initial-migration.swift Updated core_condition schema to include additional fields: rule_id, operation, value_str, value_double.
src/swift-server/routes.swift Removed ApiRule_list method; updated server URL reference.
src/swift-server/rules/rules.models.swift Introduced new enums ConditionalRelationType, ConditionOperation, and classes Condition, Rule, RuleLabelAction, RuleLabelPivot.
src/typespec/rules.tsp Replaced JsonSchema with OpenAPI3, removed Operation, added OperationString, OperationDouble, and introduced RuleParam, Rule, ConditionString, ConditionDouble. Updated ApiRule interface with new methods for rule and condition management.

Poem

🐇 In the meadow, changes bloom,
New rules and paths, dispelling gloom.
With conditions and operations bright,
Our API hops to new heights!
Dependencies updated, models refined,
A leap of progress, perfectly aligned! 🌼


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
🪧 Tips

Chat

There are 3 ways to chat with CodeRabbit:

  • Review comments: Directly reply to a review comment made by CodeRabbit. Example:
    • I pushed a fix in commit <commit_id>, 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.

Documentation and Community

  • Visit our Documentation for detailed information on how to use CodeRabbit.
  • Join our Discord Community to get help, request features, and share feedback.
  • Follow us on X/Twitter for updates and announcements.

@Dracks
Copy link
Owner Author

Dracks commented Nov 5, 2024

@coderabbitai review

Copy link

coderabbitai bot commented Nov 5, 2024

✅ Actions performed

Review triggered.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

Copy link

@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: 8

🧹 Outside diff range and nitpick comments (13)
src/swift-server/routes.swift (3)

107-111: Consider atomic transaction for condition management

For the ApiRule_addCondition endpoint, consider wrapping the implementation in a database transaction to ensure atomicity when adding conditions to rules. This prevents partial updates if something fails during the operation.

Example pattern to consider:

func ApiRule_addCondition(_ input: Operations.ApiRule_addCondition.Input) async throws
    -> Operations.ApiRule_addCondition.Output
{
    return try await request.db.transaction { db in
        // 1. Validate rule exists
        // 2. Create and save condition
        // 3. Associate condition with rule
        // 4. Return success response
    }
}

101-105: Consider validation middleware for rule application

The ApiRule_apply endpoint will likely need careful validation of the input data before applying rules. Consider adding a dedicated middleware for validation to keep the endpoint implementation clean.

Example pattern:

struct RuleValidationMiddleware: AsyncMiddleware {
    func respond(to request: Request, chainingTo next: AsyncResponder) async throws -> Response {
        // Validate rule exists and is active
        // Validate input data matches rule requirements
        return try await next.respond(to: request)
    }
}

89-93: Add logging for rule creation

For the ApiRule_create endpoint, consider adding logging to track rule creation events. This will be valuable for auditing and debugging purposes.

Example enhancement:

func ApiRule_create(_ input: Operations.ApiRule_create.Input) async throws
    -> Operations.ApiRule_create.Output
{
    // Implementation here
    request.logger.info("Rule created: \(rule.id) by \(request.auth.get()?.id ?? "unknown")")
}
src/swift-server/migrations/initial-migration.swift (1)

61-63: Consider adding validation for the operation field.

The operation field appears to be used for comparison operations, but there's no constraint ensuring valid values. Consider:

  1. Using an enum type if your database supports it
  2. Adding a check constraint for valid operations

Example of valid operations might include: "equals", "contains", "greater_than", "less_than", etc. This would prevent invalid operations from being stored.

Additionally, the flexible value storage pattern using value_str and value_float is a good approach for handling different data types, but consider documenting which operations are valid with which value types to prevent runtime errors.

src/@typespec/openapi3/openapi.yaml (4)

560-583: Add request body validation descriptions.

The RuleParam schema is used in the request body but lacks descriptions for its properties. This makes it harder for API consumers to understand the validation requirements.

Consider adding descriptions for each property in the RuleParam schema.


560-583: Add pagination support for GET /rules endpoint.

The GET /rules endpoint might return a large number of rules. Consider adding pagination support similar to other list endpoints in the API (e.g., /users).

Add cursor-based pagination parameters and update the response schema:

     get:
       operationId: ApiRule_list
-      parameters: []
+      parameters:
+        - name: cursor
+          in: query
+          required: false
+          schema:
+            type: string
+          explode: false
+        - name: limit
+          in: query
+          required: false
+          schema:
+            type: integer
+            format: uint16
+            default: 100
+          explode: false
       responses:
         '200':
           description: The request has succeeded.
           content:
             application/json:
               schema:
-                type: array
-                items:
-                  $ref: '#/components/schemas/Rule'
+                type: object
+                required:
+                  - results
+                properties:
+                  results:
+                    type: array
+                    items:
+                      $ref: '#/components/schemas/Rule'
+                  next:
+                    type: string

Line range hint 1088-1101: Add validation constraints for float values.

The ConditionFloat schema should include validation constraints for the float value to prevent issues with special values (NaN, Infinity).

         value:
           type: number
+          format: double
+          minimum: -1e308
+          maximum: 1e308

1454-1460: Add descriptions for operation enums.

The OperationFloat and OperationString enums lack descriptions for their values, making it unclear how each operation behaves.

Add descriptions for each enum value:

     OperationFloat:
       type: string
+      description: Numeric comparison operations
       enum:
-        - greater
-        - greaterEqual
-        - lower
-        - lowerEqual
+        - greater # Value must be greater than the condition value
+        - greaterEqual # Value must be greater than or equal to the condition value
+        - lower # Value must be lower than the condition value
+        - lowerEqual # Value must be lower than or equal to the condition value
     OperationString:
       type: string
+      description: String matching operations
       enum:
-        - contains
-        - prefix
-        - regularExpression
-        - suffix
+        - contains # Value must contain the condition string
+        - prefix # Value must start with the condition string
+        - regularExpression # Value must match the condition regular expression
+        - suffix # Value must end with the condition string

Also applies to: 1461-1467

src/typespec/rules.tsp (3)

15-20: Consider renaming 'relations' to 'relation' in 'RuleParam' for clarity

The property relations in RuleParam is of type ConditionalRelation, which suggests it represents a single relation rather than multiple. For clarity and to adhere to naming conventions, consider renaming it to relation.


35-39: Rename 'lower' to 'less' in 'OperationFloat' for consistency with mathematical terminology

In the OperationFloat enum, renaming lower to less and lowerEqual to lessEqual would align the terms with standard mathematical language, improving readability and understanding.

Apply this diff to make the changes:

 enum OperationFloat {
     greater,
     greaterEqual,
-    lower,
-    lowerEqual,
+    less,
+    lessEqual,
 }

28-31: Consider adding an 'equals' operation to 'OperationString'

The OperationString enum lacks an equals operation, which is commonly used for string comparisons. Adding this operation would enhance the flexibility of string conditions.

Apply this diff to include the equals operation:

 enum OperationString {
+    equals,
     contains,
     prefix,
     regularExpression,
     suffix,
 }
src/swift-server/rules/rules.models.swift (2)

4-7: Consider removing redundant string enum values.

SwiftLint suggests that string enum values can be omitted when they are equal to the enumcase name. This can help reduce verbosity and improve readability.

Apply this diff to remove the redundant string enum values:

 enum ConditionalRelationType: String, Codable {
-	case and = "and"
-	case notOr = "notOr"
+	case and
+	case notOr
 }
🧰 Tools
🪛 SwiftLint

[Warning] 5-5: String enum values can be omitted when they are equal to the enumcase name

(redundant_string_enum_value)


[Warning] 6-6: String enum values can be omitted when they are equal to the enumcase name

(redundant_string_enum_value)


90-101: Consider renaming the class to RuleLabelActionPivot for consistency.

The class name RuleLabelAction doesn't follow the naming convention used for the other pivot class RuleLabelPivot. For consistency and clarity, consider renaming it to RuleLabelActionPivot.

Apply this diff to rename the class:

-final class RuleLabelAction: Model, @unchecked Sendable {
+final class RuleLabelActionPivot: Model, @unchecked Sendable {
📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL

📥 Commits

Reviewing files that changed from the base of the PR and between 64ac5c9 and b2bf84a.

⛔ Files ignored due to path filters (2)
  • package-lock.json is excluded by !**/package-lock.json
  • pnpm-lock.yaml is excluded by !**/pnpm-lock.yaml
📒 Files selected for processing (7)
  • package.yaml (1 hunks)
  • src/@typespec/openapi3/openapi.yaml (5 hunks)
  • src/swift-server/bank-transaction/bank-transaction.models.swift (0 hunks)
  • src/swift-server/migrations/initial-migration.swift (1 hunks)
  • src/swift-server/routes.swift (1 hunks)
  • src/swift-server/rules/rules.models.swift (1 hunks)
  • src/typespec/rules.tsp (2 hunks)
💤 Files with no reviewable changes (1)
  • src/swift-server/bank-transaction/bank-transaction.models.swift
✅ Files skipped from review due to trivial changes (1)
  • package.yaml
🧰 Additional context used
🪛 SwiftLint
src/swift-server/rules/rules.models.swift

[Warning] 5-5: String enum values can be omitted when they are equal to the enumcase name

(redundant_string_enum_value)


[Warning] 6-6: String enum values can be omitted when they are equal to the enumcase name

(redundant_string_enum_value)

🔇 Additional comments (7)
src/typespec/rules.tsp (4)

22-26: Verify that 'Rule' model correctly extends 'RuleParam'

The Rule model uses the spread operator ...RuleParam;. Confirm that this correctly includes all properties from RuleParam and that any required properties are appropriately handled.


54-58: Ensure all condition types are accounted for in 'Condition' union

Currently, the Condition union includes ConditionFloat and ConditionString. Consider whether conditions for other data types (e.g., integers, dates) are required for the rules engine.

Run the following script to check for other condition models in the codebase:

#!/bin/bash
# Description: Search for other 'Condition' models that might need inclusion.

# Test: Find all models starting with 'Condition' that are not 'ConditionString' or 'ConditionFloat'.
# Expect: No additional models or identify any that should be included.

rg -A 2 '^model Condition\w+' --glob '!src/typespec/rules.tsp' | rg -v 'ConditionString|ConditionFloat'

79-79: ⚠️ Potential issue

Missing '@path condId: UUID' parameter in 'deleteCondition' method

The method deleteCondition has a route that includes {condId}, but the parameter @path condId: UUID is missing in the method signature. This will result in an error due to the unresolved path parameter.

Apply this diff to fix the method signature:

 @delete @route("{ruleId}/condition/{condId}") deleteCondition(@path ruleId: UUID): Date;
+@delete @route("{ruleId}/condition/{condId}") deleteCondition(@path ruleId: UUID, @path condId: UUID): Date;

Likely invalid or redundant comment.


71-72: Verify the usage of spread operator '...RuleParam' in method parameters

In the create and update methods, ...RuleParam is used to spread properties into the method parameters. Ensure that this usage is valid in the TypeSpec language and functions as intended.

Run the following script to verify the correct usage of ...RuleParam:

✅ Verification successful

Spread operator usage in RuleParam is valid and consistent with the codebase

The spread operator usage in create(...RuleParam) and update(@path ruleId: UUID, ...RuleParam) is valid and follows established patterns in the TypeSpec codebase. This is evidenced by:

  • RuleParam is properly defined as a model with required properties
  • Similar spread operator patterns are used consistently across the codebase in other TypeSpec files:
    • login(...UserCredentials)
    • create(...CreateLabel)
    • create(...GraphParam)
    • create(...UserGroup)
    • updateMe(...UpdateMyProfile)
🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Description: Check for correct usage of '...RuleParam' in method definitions.

# Test: Search for method definitions using '...RuleParam' and verify syntax.
# Expect: Method definitions should correctly use '...RuleParam' without syntax errors.

ast-grep --pattern $'interface $_ {
  $$$
  $_ (...) ...RuleParam : $_;
  $$$
}'

Length of output: 105


Script:

#!/bin/bash
# Let's try different approaches to verify the spread operator usage

# Search for RuleParam definition
rg "RuleParam" -A 5

# Search for spread operator usage in .tsp files
rg "\.\.\." --type-add 'tsp:*.tsp' --type tsp -A 2

# Look for similar parameter spreading patterns in other TypeSpec files
fd -e tsp -x cat {} | grep -A 2 "@\(post\|put\)"

Length of output: 9018

src/swift-server/rules/rules.models.swift (3)

20-49: LGTM!

The Condition class is well-structured and follows best practices:

  • Proper use of Fluent's Model and Content protocols
  • Correct usage of Fluent's property wrappers like @ID, @Parent, and @Field
  • Initializers with default values and required properties

51-87: LGTM!

The Rule class is well-designed and adheres to best practices:

  • Proper use of Fluent's Model and Content protocols
  • Correct usage of Fluent's property wrappers like @ID, @Parent, @Children, @Enum, and @OptionalParent
  • Initializer with default values and required properties
  • Establishes parent-child relationships with Condition and self-referential relationships

105-116: LGTM!

The RuleLabelPivot class is well-structured and follows best practices:

  • Proper use of Fluent's Model protocol
  • Correct usage of Fluent's property wrappers like @ID and @Parent
  • Establishes relationships with Rule and LabelTransaction

src/swift-server/migrations/initial-migration.swift Outdated Show resolved Hide resolved
src/@typespec/openapi3/openapi.yaml Outdated Show resolved Hide resolved
src/@typespec/openapi3/openapi.yaml Outdated Show resolved Hide resolved
src/@typespec/openapi3/openapi.yaml Outdated Show resolved Hide resolved
src/@typespec/openapi3/openapi.yaml Outdated Show resolved Hide resolved
src/typespec/rules.tsp Outdated Show resolved Hide resolved
src/swift-server/rules/rules.models.swift Outdated Show resolved Hide resolved
@Dracks Dracks force-pushed the 3.0.0/rule-engine branch from b2bf84a to ad623a6 Compare November 7, 2024 18:49
@Dracks
Copy link
Owner Author

Dracks commented Nov 7, 2024

@coderabbitai review

Copy link

coderabbitai bot commented Nov 7, 2024

✅ Actions performed

Review triggered.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

@Dracks
Copy link
Owner Author

Dracks commented Nov 7, 2024

@coderabbitai configuration

Copy link

coderabbitai bot commented Nov 7, 2024

CodeRabbit configuration
language: en-US
tone_instructions: ''
early_access: false
enable_free_tier: true
reviews:
  profile: chill
  request_changes_workflow: false
  high_level_summary: true
  high_level_summary_placeholder: '@coderabbitai summary'
  auto_title_placeholder: '@coderabbitai'
  review_status: true
  commit_status: true
  poem: true
  collapse_walkthrough: false
  sequence_diagrams: true
  changed_files_summary: true
  labeling_instructions: []
  path_filters: []
  path_instructions: []
  abort_on_close: true
  auto_review:
    enabled: true
    auto_incremental_review: true
    ignore_title_keywords: []
    labels: []
    drafts: true
    base_branches: []
  tools:
    shellcheck:
      enabled: true
    ruff:
      enabled: true
    markdownlint:
      enabled: true
    github-checks:
      enabled: true
      timeout_ms: 90000
    languagetool:
      enabled: true
      enabled_only: false
      level: default
    biome:
      enabled: true
    hadolint:
      enabled: true
    swiftlint:
      enabled: true
    phpstan:
      enabled: true
      level: default
    golangci-lint:
      enabled: true
    yamllint:
      enabled: true
    gitleaks:
      enabled: true
    checkov:
      enabled: true
    detekt:
      enabled: true
    eslint:
      enabled: true
    rubocop:
      enabled: true
    buf:
      enabled: true
    regal:
      enabled: true
    actionlint:
      enabled: true
    pmd:
      enabled: true
    cppcheck:
      enabled: true
    semgrep:
      enabled: true
    circleci:
      enabled: true
chat:
  auto_reply: true
knowledge_base:
  opt_out: false
  learnings:
    scope: auto
  issues:
    scope: auto
  jira:
    project_keys: []
  linear:
    team_keys: []
  pull_requests:
    scope: auto

Copy link

@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: 19

🧹 Outside diff range and nitpick comments (34)
src/swift-server/rules/rules.service.swift (3)

1-3: Consider adopting protocol-based design for better testability.

Extract the rules service interface into a protocol to facilitate mock injection in tests and improve modularity.

 import Fluent

+protocol RulesServiceProtocol {
+    func applyRules(on db: Database, for transaction: TransactionSummary) async throws
+}
+
-class RulesService {
+class RulesService: RulesServiceProtocol {

4-7: Add input validation and consider pagination for large rule sets.

The current implementation has several potential issues:

  1. Missing validation for transaction properties
  2. No pagination for rule queries which could impact performance with large rule sets
  3. No specific error handling for database failures

Consider implementing:

  1. Input validation
  2. Pagination using Fluent's paginate() method
  3. Custom error types for better error handling
 func applyRules(on db: Database, for transaction: TransactionSummary) async throws {
+    guard let groupOwnerId = transaction.groupOwnerId else {
+        throw RuleServiceError.invalidTransaction
+    }
+    
     let rules = try await Rule.query(on: db).filter(
-        \.$groupOwner.$id == transaction.groupOwnerId
+        \.$groupOwner.$id == groupOwnerId
     ).with(\.$conditions).all()

22-23: Remove or implement the commented code.

The commented code rules.extends(rule.children) suggests an unimplemented feature for rule inheritance or extension.

Would you like me to help implement the rule extension feature or create an issue to track this?

src/swift-server-tests/factories.swift (1)

66-69: Consider parameterizing the operation in RuleConditionFactory.

The current implementation hardcodes .contains as the operation, which might limit test coverage. Consider adding a parameter to allow testing different operations.

Here's a suggested improvement:

 class RuleConditionFactory: AbstractFactory<Condition> {
+    private var defaultOperation: Condition.Operation = .contains
+    
+    func withOperation(_ operation: Condition.Operation) -> RuleConditionFactory {
+        defaultOperation = operation
+        return self
+    }
+
     override func create(id: Int) -> Condition {
-        return .init(ruleId: UUID(), operation: .contains)
+        return .init(ruleId: UUID(), operation: defaultOperation)
     }
 }

This allows for more flexible testing:

let containsCondition = RuleConditionFactory().build()
let startsWithCondition = RuleConditionFactory().withOperation(.startsWith).build()
src/typespec/rules.tsp (5)

10-14: Consider renaming 'notAnd' for clarity

The enum value 'notAnd' is not a standard logical operator name and might be confusing. Consider using 'nand' or 'nor' if that's the intended logic, or add documentation to explain the behavior.


15-20: Add documentation for the RuleParam model

The model would benefit from documentation explaining:

  • The purpose of optional parentRule (hierarchical rules?)
  • The relationship between groupOwnerId and rules
  • How relations affects the rule evaluation

28-40: Add documentation for operation enums

Please add documentation for each operation to specify:

  • Expected behavior for string operations (e.g., case sensitivity for contains/prefix/suffix)
  • Format requirements for regularExpression
  • Precision handling for float comparisons

42-58: Consider adding validation constraints

The condition models could benefit from additional validation:

  • Add pattern validation for regularExpression values in ConditionString
  • Consider adding min/max bounds for float values if applicable

73-74: Clarify the purpose of Date return type

The apply and delete operations return a Date type. Please add documentation to explain what this timestamp represents (e.g., last execution time, deletion time).

src/swift-server/user/user.service.swift (1)

33-36: LGTM! Consider applying similar formatting to other methods.

The vertical alignment of parameters improves readability of this long method signature.

For consistency, consider applying similar formatting to other methods with multiple parameters, such as validateGroupId. Example:

-	func validateGroupId(on db: Database, groupId: String, forUserId userId: UUID) async throws
+	func validateGroupId(
+		on db: Database,
+		groupId: String,
+		forUserId userId: UUID
+	) async throws
src/swift-server-tests/importers/importer-n26.tests.swift (1)

49-49: LGTM! Consider additional error scenarios.

The queue parameter addition maintains consistency with the successful case. While the basic error handling is tested, consider adding test cases for queue-specific error scenarios (e.g., queue unavailability or processing failures).

Consider adding test cases like:

func testN26ImportWithQueueFailure() async throws {
    // Mock queue failure scenario
    let failingQueue = MockQueue(shouldFail: true)
    // Test error handling
}
src/swift-server/importer/importer.service.swift (1)

17-18: Add documentation for the queue parameter

Consider adding a documentation comment explaining the purpose and usage of the queue parameter.

+/// Creates a new file import operation
+/// - Parameters:
+///   - db: The database connection
+///   - queue: Queue for processing the import asynchronously
+///   - groupOwnerId: The ID of the group owner
+///   - key: The import key
+///   - fileName: Name of the file being imported
+///   - filePath: Path to the file being imported
+/// - Returns: The created FileImportReport
 func createFileImport(
   on db: Database, withQueue queue: Queue, groupOwnerId: UUID, key: String,
   fileName: String, filePath: String
 )
Package.swift (1)

17-19: Consider pinning the queues dependency version.

While updating Vapor to 4.106.0 is good, the queues dependency at 1.16.1 might be critical for the rule-engine. Consider pinning to a specific version range for better stability.

-.package(url: "https://github.com/vapor/queues.git", from: "1.16.1"),
+.package(url: "https://github.com/vapor/queues.git", .upToNextMinor(from: "1.16.1")),
src/swift-server-tests/importers/importer-helper.swift (1)

127-132: Consider enhancing error handling and documentation.

The implementation is correct and follows the established pattern of the getDb() method. However, there are a few potential improvements:

Consider applying these enhancements:

+    /// Returns the queue instance from the application.
+    /// - Throws: TestError if the application instance is not initialized.
+    /// - Returns: The queue instance for processing async jobs.
     func getQueue() throws -> Queue {
         guard let app = app else {
-            throw TestError()
+            throw TestError(message: "Application not initialized when accessing queue")
         }
         return app.queues.queue
     }

The changes would:

  1. Add documentation explaining the method's purpose and error conditions
  2. Provide a more descriptive error message for debugging
src/swift-server-tests/profile.tests.swift (3)

Line range hint 8-25: Enhance user list validation.

While the basic admin access test is good, consider adding assertions to validate the actual user data returned, not just the count.

 XCTAssertEqual(data.results.count, 2)
+// Verify user properties
+XCTAssertTrue(data.results.contains { $0.username == testAdmin.username })
+XCTAssertTrue(data.results.contains { $0.username == testUser.username })

Line range hint 27-43: Add edge cases to profile update tests.

The authorization and update tests are well structured, but consider adding tests for:

  1. Partial updates (updating only some fields)
  2. Invalid email format
  3. Username uniqueness constraints
  4. Password complexity rules

Also applies to: 45-63, 65-106


Line range hint 108-116: Critical: Implement missing test methods.

Several crucial test methods are currently empty:

  • testUpdateWithInvalidDefaultGroupId: Validates group ID constraints
  • testCreateUser: Tests user creation flow
  • testCreateUserAsAdmin: Verifies admin-specific creation capabilities
  • testDeleteUser: Ensures proper user deletion
  • testDeleteUserAsAdmin: Validates admin deletion privileges

These tests are essential for ensuring the reliability of user management operations.

Would you like me to help generate the implementation for these test methods?

src/swift-server-tests/importers/importer-commerz-bank.swift (1)

31-32: Consider adding queue-specific test scenarios.

While the basic import functionality is well tested, consider adding test cases that specifically verify the queue behavior:

  • Test queue processing order
  • Test concurrent imports
  • Test queue error handling

Would you like me to help create these additional test scenarios?

src/swift-server/user/user-api-impl.swift (1)

Line range hint 8-144: Consider architectural improvements for better maintainability and security.

  1. The unimplemented endpoints (ApiUser_create, ApiUser_deleteGroup, ApiUser_addGroup, ApiUser_delete) should be prioritized for implementation to ensure API completeness.

  2. The admin permission check is repeated across endpoints. Consider extracting it to a middleware:

struct AdminMiddleware: AsyncMiddleware {
    func respond(to request: Request, chainingTo next: AsyncResponder) async throws -> Response {
        guard try await getUser(fromRequest: request).isAdmin else {
            return .unauthorized(.init(body: .json(.init(
                message: "Admin access required",
                code: ApiError.API10014.rawValue))))
        }
        return try await next.respond(to: request)
    }
}
  1. Consider adding:
    • Rate limiting for API endpoints
    • Request validation middleware
    • API versioning support
    • Comprehensive error handling middleware

Would you like me to provide implementation details for any of these suggestions?

src/swift-server/importer/importers/new-import.service.swift (1)

Line range hint 64-156: Consider transaction atomicity with queue-based processing

The introduction of queue-based processing raises some architectural considerations:

  • What happens if the transaction is saved but the queued job fails?
  • Is there a need for transaction rollback in case of queue failures?
  • How are queued jobs monitored and retried?

Consider documenting the failure handling strategy and ensuring proper error recovery mechanisms are in place.

src/swift-server-tests/importers/importer-caixa-enginyers.tests.swift (1)

25-26: Consider documenting queue configuration and resource implications.

The introduction of queue management suggests a shift towards asynchronous processing for imports. Consider:

  1. Documenting the queue configuration and its impact on system resources
  2. Adding comments about expected queue behavior in the test files
  3. Including error handling tests for queue-related scenarios

This will help maintain the system's reliability as import volumes grow.

Also applies to: 74-75

src/swift-server-tests/rules.tests.swift (2)

8-24: Add documentation and improve type safety.

Consider the following improvements:

  1. Add documentation explaining the purpose of the struct and its relationship with ConditionOperation.
  2. Add validation to ensure the operation type matches the value type (e.g., string operations with string values).
+/// Represents a condition in a rule with either string or float value.
+/// The value type must match the operation type (string operations with string values,
+/// numeric operations with float values).
 struct BaseCondition {
     let operation: ConditionOperation
     let valueStr: String?
     let valueFloat: Double?
 
     init(operation: ConditionOperation, valueStr: String) {
+        precondition(operation.isStringOperation, "Operation must be a string operation")
         self.operation = operation
         self.valueStr = valueStr
         self.valueFloat = nil
     }
 
     init(operation: ConditionOperation, valueFloat: Double) {
+        precondition(operation.isNumericOperation, "Operation must be a numeric operation")
         self.operation = operation
         self.valueStr = nil
         self.valueFloat = valueFloat
     }
 }

57-88: Refactor test data creation for better maintainability.

The createBasicRules method has repetitive code that could be simplified using a data-driven approach.

 private func createBasicRules() async throws {
     let db = app!.db
-
-    // String operations
-    let _ = try await createBasicRule(
-        on: db, for: testGroup, with: .init(operation: .prefix, valueStr: "needle"),
-        toApply: labels[0])
+    let testCases = [
+        (operation: .prefix, value: "needle" as Any, labelIndex: 0),
+        (operation: .regularExpression, value: "needle", labelIndex: 1),
+        // ... other cases
+    ]
+    
+    for testCase in testCases {
+        let condition = testCase.value is String
+            ? BaseCondition(operation: testCase.operation, valueStr: testCase.value as! String)
+            : BaseCondition(operation: testCase.operation, valueFloat: testCase.value as! Double)
+        
+        try await createBasicRule(
+            on: db,
+            for: testGroup,
+            with: condition,
+            toApply: labels[testCase.labelIndex]
+        )
+    }
src/swift-server/routes.swift (1)

83-123: Add documentation for rule-engine endpoints.

Consider adding documentation comments for each rule-engine endpoint to describe:

  • Expected input parameters and their constraints
  • Possible error conditions and responses
  • Example usage

Example format:

/// Creates a new rule with the specified parameters
/// - Parameter input: The rule creation parameters
/// - Throws: `ValidationError` if the rule parameters are invalid
/// - Returns: The created rule details
func ApiRule_create(_ input: Operations.ApiRule_create.Input) async throws
src/swift-server-tests/app-test.swift (1)

46-49: Consider adding documentation for the factory properties

The factory properties are well-structured and appropriately declared as constants. Consider adding documentation comments to describe their testing purposes and usage patterns.

+    /// Factory for creating test Rule instances
     let ruleFactory = RuleFactory()
+    /// Factory for creating test RuleCondition instances
     let conditionFactory = RuleConditionFactory()
+    /// Factory for creating test BankTransaction instances
     let transactionFactory = BankTransactionFactory()
+    /// Factory for creating test Label instances
     let labelFactory = LabelFactory()
src/swift-server/migrations/initial-migration.swift (2)

63-64: Address the TODO comment about enums.

The comment suggests using enums for link_reason. This would provide type safety and prevent invalid values.

Would you like me to help implement the enum for link_reason?


67-74: Enhance schema with index and type safety.

Two suggestions for improvement:

  1. Add an index on parent_id for better hierarchical query performance
  2. Consider using an enum for conditions_relation instead of string
 try await database.schema("core_rule")
   .id()
   .field("group_owner_id", .uuid, .required, .references("user_groups", "id"))
   .field("name", .string, .required)
   .field("conditions_relation", .string, .required)
   .field("parent_id", .uuid, .references("core_rule", "id"))
+  .index("parent_id")
   .create()
src/swift-server-tests/importers/importer-service.tests.swift (2)

41-42: Consider adding queue-specific test cases.

While the withQueue parameter has been consistently added across all test methods, consider adding specific test cases to verify:

  • Queue failure scenarios
  • Queue timeout scenarios
  • Concurrent import handling

Also applies to: 68-69, 112-113, 143-144, 148-149, 177-178


177-178: Enhance error handling for queued operations.

With the introduction of queue-based processing, ensure that:

  1. Errors are properly propagated through the queue
  2. Failed imports don't block the queue
  3. Error states are consistently reported in the status reports
src/swift-server-tests/bank-transactions.tests.swift (3)

11-33: LGTM! Consider adding documentation for test data setup.

The test data creation is well-structured with good variety in dates and group ownership. The factory pattern is properly implemented.

Consider adding a comment block explaining the test data setup strategy, particularly the significance of the different dates (2022/2023) and group ownership patterns.


Line range hint 200-209: LGTM! Consider adding negative test cases.

The test setup is thorough with pre-attached labels.

Consider adding test cases for error scenarios, such as:

  • Unlinking a non-existent label
  • Unlinking from a transaction that doesn't exist
  • Unlinking a label that wasn't attached

Line range hint 1-338: Consider implementing a shared test fixture setup.

The test class shows consistent use of the factory pattern across all test methods. To reduce code duplication and improve maintainability, consider:

  1. Moving common transaction creation logic to a shared setup method
  2. Using test fixtures for common test data scenarios
  3. Implementing helper methods for frequently used assertions
src/@typespec/openapi3/openapi.yaml (2)

Line range hint 1085-1505: Add schema descriptions for better API documentation.

The new schemas lack descriptions that would help API consumers understand their purpose and usage. Consider adding descriptions for:

  • Condition, ConditionFloat, ConditionString: Explain their role in rule evaluation
  • OperationFloat, OperationString: Document the behavior of each operation
  • RuleParam: Describe required fields and their impact

Example addition:

    OperationFloat:
      type: string
+     description: Defines comparison operations for numeric values
      enum:
        - greater
        - greaterEqual
        - lower
        - lowerEqual

560-732: Consider rate limiting and validation for rule operations.

The rule engine endpoints could potentially impact system performance if misused. Consider:

  1. Implementing rate limiting for rule creation and application
  2. Adding validation for:
    • Maximum number of conditions per rule
    • Maximum number of rules per group
    • Complexity of regular expressions in string conditions
  3. Adding appropriate caching headers for GET operations
📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL

📥 Commits

Reviewing files that changed from the base of the PR and between b2bf84a and ad623a6.

⛔ Files ignored due to path filters (2)
  • package-lock.json is excluded by !**/package-lock.json
  • pnpm-lock.yaml is excluded by !**/pnpm-lock.yaml
📒 Files selected for processing (29)
  • Package.resolved (9 hunks)
  • Package.swift (3 hunks)
  • package.yaml (1 hunks)
  • src/@typespec/openapi3/openapi.yaml (5 hunks)
  • src/swift-server-tests/app-test.swift (4 hunks)
  • src/swift-server-tests/bank-transactions.tests.swift (6 hunks)
  • src/swift-server-tests/factories.swift (1 hunks)
  • src/swift-server-tests/importers/importer-caixa-enginyers.tests.swift (2 hunks)
  • src/swift-server-tests/importers/importer-commerz-bank.swift (1 hunks)
  • src/swift-server-tests/importers/importer-helper.swift (2 hunks)
  • src/swift-server-tests/importers/importer-n26.tests.swift (2 hunks)
  • src/swift-server-tests/importers/importer-service.tests.swift (6 hunks)
  • src/swift-server-tests/profile.tests.swift (1 hunks)
  • src/swift-server-tests/rules.tests.swift (1 hunks)
  • src/swift-server/bank-transaction/bank-transaction.models.swift (0 hunks)
  • src/swift-server/bank-transaction/bank-transaction.service.swift (2 hunks)
  • src/swift-server/configure.swift (3 hunks)
  • src/swift-server/importer/importer-api-impl.swift (1 hunks)
  • src/swift-server/importer/importer.service.swift (2 hunks)
  • src/swift-server/importer/importers/new-import.service.swift (4 hunks)
  • src/swift-server/migrations/initial-migration.swift (2 hunks)
  • src/swift-server/routes.swift (2 hunks)
  • src/swift-server/rules/new-transaction.job.swift (1 hunks)
  • src/swift-server/rules/rules.models.swift (1 hunks)
  • src/swift-server/rules/rules.service.swift (1 hunks)
  • src/swift-server/user/create-user.command.swift (1 hunks)
  • src/swift-server/user/user-api-impl.swift (1 hunks)
  • src/swift-server/user/user.service.swift (1 hunks)
  • src/typespec/rules.tsp (1 hunks)
💤 Files with no reviewable changes (1)
  • src/swift-server/bank-transaction/bank-transaction.models.swift
✅ Files skipped from review due to trivial changes (1)
  • src/swift-server/user/create-user.command.swift
🚧 Files skipped from review as they are similar to previous changes (2)
  • package.yaml
  • src/swift-server/rules/rules.models.swift
🧰 Additional context used
📓 Learnings (1)
src/typespec/rules.tsp (1)
Learnt from: Dracks
PR: Dracks/mr-scrooge#30
File: src/typespec/rules.tsp:49-52
Timestamp: 2024-11-07T16:01:46.093Z
Learning: In `src/typespec/rules.tsp`, the `ConditionString` model does not have a `field` property.
🪛 SwiftLint
src/swift-server-tests/app-test.swift

[Warning] 66-66: Prefer at least one space after slashes for comments

(comment_spacing)

🔇 Additional comments (48)
src/swift-server/rules/new-transaction.job.swift (1)

12-25: Verify job registration in application configuration.

Ensure this job is properly registered in the application's queue configuration.

✅ Verification successful

Job is properly registered in application configuration

The NewTransactionJob is correctly registered in the application's queue configuration:

  • Registered via app.queues.add(NewTransactionJob()) in configure.swift
  • Queue system is properly configured with Fluent driver in production
  • In-process jobs and scheduled jobs are started appropriately
🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Search for NewTransactionJob registration in configuration files
echo "Searching for job registration..."
rg -l "NewTransactionJob" --type swift

# Search for queue configuration
echo -e "\nSearching for queue configuration..."
rg "queues\.use|configureQueues" --type swift

Length of output: 607


Script:

#!/bin/bash
# Check the registration of NewTransactionJob in configure.swift
echo "Checking job registration in configure.swift..."
rg -A 5 -B 5 "NewTransactionJob" src/swift-server/configure.swift

# Check queue configuration and job registration in app-test.swift
echo -e "\nChecking test configuration..."
rg -A 5 -B 5 "NewTransactionJob|queues\.use" src/swift-server-tests/app-test.swift

Length of output: 1039

src/swift-server-tests/factories.swift (1)

60-64: LGTM! RuleFactory implementation follows established patterns.

The implementation is clean and consistent with other factories in the file.

src/typespec/rules.tsp (3)

1-6: LGTM: Imports and namespace setup are correct

The necessary TypeSpec modules are properly imported and the namespace is correctly configured.


22-26: LGTM: Rule model is well-structured

The model correctly extends RuleParam and includes an array of conditions.


67-80: Verify OpenAPI documentation completeness

Let's ensure all operations are properly documented in the OpenAPI specification.

✅ Verification successful

Let me verify the OpenAPI documentation by checking the content of the OpenAPI spec files.


Let me do one final check to verify the completeness of the OpenAPI spec against the TypeSpec definitions.


OpenAPI documentation is complete and consistent

All TypeSpec operations for the rules API are properly documented in the OpenAPI specification:

  • List operation (GET /rules)
  • Create operation (POST /rules)
  • Update operation (PUT /rules/{ruleId})
  • Apply operation (POST /rules/{ruleId}/apply)
  • Delete operation (DELETE /rules/{ruleId})
  • Add condition (POST /rules/{ruleId}/condition)
  • Update condition (PUT /rules/{ruleId}/condition/{condId})
  • Delete condition (DELETE /rules/{ruleId}/condition/{condId})
🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Description: Check if all API operations are documented in OpenAPI spec
# Expected: Each operation should have a corresponding path in the OpenAPI spec

# Look for the OpenAPI spec file
echo "Searching for OpenAPI specification files..."
fd -e yaml -e json --exec grep -l "openapi:" {}

# Search for API operations in TypeSpec
echo -e "\nAPI operations defined in TypeSpec:"
rg "@(post|put|delete|get)" "src/typespec/rules.tsp"

Length of output: 991


Script:

#!/bin/bash
# Check the content of OpenAPI spec files for the rule endpoints

# First, let's check the main OpenAPI spec
echo "Content of src/swift-server/openapi.yaml for rule endpoints:"
rg -A 5 "/rules" "src/swift-server/openapi.yaml"

# Also check if there are any rule-related paths
echo -e "\nAll rule-related paths in OpenAPI spec:"
rg "^\s+/.*rule" "src/swift-server/openapi.yaml"

# Check for operation IDs related to rules
echo -e "\nOperation IDs related to rules:"
rg "operationId:.*rule" "src/swift-server/openapi.yaml"

Length of output: 1232


Script:

#!/bin/bash
# Compare TypeSpec operations with OpenAPI spec paths

# Get all HTTP methods and paths from TypeSpec
echo "TypeSpec operations:"
rg -o "@(post|put|delete|get).*@route\([^)]*\)" "src/typespec/rules.tsp"

# Get all operations from OpenAPI spec
echo -e "\nOpenAPI spec operations:"
rg "^\s+(get|post|put|delete):" "src/swift-server/openapi.yaml" -A 1

Length of output: 2278

src/swift-server-tests/importers/importer-n26.tests.swift (1)

23-23: LGTM! Consider adding queue-specific test cases.

The addition of the queue parameter is consistent with the broader changes across importers. While the basic functionality is well-tested, consider adding specific test cases to verify queue-related behavior.

Let's verify if other importers have been updated consistently:

✅ Verification successful

Queue parameter has been consistently implemented across all importers

The verification shows that the withQueue: getQueue() parameter is consistently used across all importer test files:

  • importer-n26.tests.swift
  • importer-commerz-bank.swift
  • importer-caixa-enginyers.tests.swift
  • importer-service.tests.swift

The implementation is uniform and follows the same pattern in all importers, confirming that the change is part of a broader, consistent update across the codebase.

🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Description: Check if all importer tests use the new queue parameter
# Expected: All importers should use withQueue parameter

rg -l "importFromFile.*withQueue:" "src/swift-server-tests/importers/"

Length of output: 70


Script:

#!/bin/bash
# Let's try a broader search to understand the importer test patterns
rg -A 2 "importFromFile" "src/swift-server-tests/importers/"

Length of output: 4118


Script:

#!/bin/bash
# Let's also check for any queue-related patterns in the importers directory
rg -A 2 "getQueue\(\)" "src/swift-server-tests/importers/"

Length of output: 5102


Script:

#!/bin/bash
# List all files in the importers directory to ensure we're searching in the right place
fd . "src/swift-server-tests/importers/"

Length of output: 346

src/swift-server/configure.swift (3)

6-6: LGTM: Import statement is correctly placed.

The addition of QueuesFluentDriver import aligns with the new queue functionality being implemented.


63-63: LGTM: Queue driver configuration.

The Fluent queue driver setup is correctly configured.


43-43: Verify migration order dependencies.

While the addition of JobModelMigration is correct, please ensure there are no dependencies between JobModelMigration and InitialMigration that would require a specific order.

✅ Verification successful

Let me generate another script to check the JobModelMigration content since it wasn't found in the previous search.


Based on the search results, I can now generate the final response:

Migration order appears safe to proceed

The JobModelMigration is added between SessionRecord.migration and InitialMigration. From the extensive search of the codebase:

  • No jobs-related tables were found in InitialMigration
  • No foreign key dependencies were found between jobs tables and other tables
  • The job system appears to be a new, independent feature
🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Description: Check for potential migration dependencies
# Look for references between migrations and shared table names

# Search for table names and foreign key references in both migrations
rg -A 10 "create|addForeignKey|references" --glob "**/*migration*.swift"

Length of output: 14735


Script:

#!/bin/bash
# Search for JobModelMigration file and its content
rg -l "JobModelMigration" 
fd "JobModelMigration" 

Length of output: 81


Script:

#!/bin/bash
# Search for job-related migrations and tables
rg -A 10 "create|addForeignKey|references" --glob "**/migrations/*job*.swift"

# Search for any file containing JobModelMigration class definition
ast-grep --pattern 'struct JobModelMigration {
  $$$
}'

Length of output: 286


Script:

#!/bin/bash
# Try to find the job migration file with a broader search
fd -e swift -x cat {} | rg -A 10 -B 2 "JobModelMigration"

# Also search for any jobs table creation
rg -A 10 "schema.*jobs?" 

Length of output: 547

src/swift-server/importer/importer.service.swift (2)

2-2: LGTM: Queue import added for async processing support

The addition of the Queues import is appropriate for the new queue-based processing functionality.


17-18: Verify all callers of createFileImport are updated

The implementation correctly integrates queue-based processing, but this is a breaking change that requires updates to all callers.

Also applies to: 21-22

✅ Verification successful

Based on the ripgrep results, I can generate the final response as we have found all the callers of createFileImport:

All callers of createFileImport are properly updated

The only caller of createFileImport in importer-api-impl.swift is already passing the new queue parameter correctly with the expected withQueue label. No further updates are needed.

🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Search for all calls to createFileImport to ensure they include the new queue parameter
rg -A 3 "createFileImport\(" --type swift

Length of output: 798

Package.swift (3)

35-35: Major version bump for swift-format requires attention.

The swift-format dependency has been updated from 510.1.0 to 600.0.0, which is a major version bump. This could introduce breaking changes in code formatting.

#!/bin/bash
# Description: Check for swift-format configuration file
# Expected: Find format configuration to ensure compatibility

# Search for swift-format configuration
fd -e swift-format.json -e swift-format.yaml

55-57: Verify queue integration with rule-engine.

The addition of QueuesFluentDriver and XCTQueues suggests background job processing for the rule-engine. Ensure proper test coverage for asynchronous rule processing.

Also applies to: 77-77

✅ Verification successful

Queue integration with rule-engine is properly implemented and tested

The codebase shows a well-structured integration between queues and rule-engine:

  • NewTransactionJob processes rules for new transactions asynchronously
  • Comprehensive test coverage in rules.tests.swift verifies rule application and job dispatching
  • Queue configuration is properly set up in configure.swift with Fluent driver
🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Description: Check for rule-engine related test files
# Expected: Find test files covering rule processing jobs

# Search for rule-engine related test files
rg -l "class.*Test.*Rule" "src/swift-server-tests"

Length of output: 50


Script:

#!/bin/bash
# Let's broaden our search to find any rule engine related files and tests
rg -l "rule.*engine|RuleEngine" 

# Also search for queue-related tests
rg -l "class.*Test.*Queue|func.*test.*Queue"

# Check for any background job processing related code
rg -l "background.*job|BackgroundJob|JobQueue"

Length of output: 137


Script:

#!/bin/bash
# Let's check the todo.md file that mentioned rule engine
cat todo.md

# Let's also check for any queue-related implementations
rg -l "Queue|queue"

# And check Package.swift for more context about the queues integration
cat Package.swift

Length of output: 6041


Script:

#!/bin/bash
# Let's check the implementation of rules and queue processing
cat src/swift-server/rules/new-transaction.job.swift

# Check the test file for rules
cat src/swift-server-tests/rules.tests.swift

# Check how queues are configured
cat src/swift-server/configure.swift

Length of output: 6973


13-14: Verify OpenAPI version compatibility with the rule-engine endpoints.

The OpenAPI dependencies have been updated to newer versions (generator: 1.4.0, runtime: 1.6.0). While these updates are good, we should ensure the new rule-engine endpoints are properly defined in the OpenAPI spec.

✅ Verification successful

OpenAPI specification and rule-engine endpoints are properly defined and compatible with the updated dependencies.

The OpenAPI specification (openapi.yaml) includes comprehensive rule-engine endpoints under the /rules path with proper operations for:

  • Listing rules (GET /rules)
  • Creating rules (POST /rules)
  • Updating rules (PUT /rules/{ruleId})
  • Deleting rules (DELETE /rules/{ruleId})
  • Adding conditions (POST /rules/{ruleId}/condition)
  • Updating conditions (PUT /rules/{ruleId}/condition/{condId})
  • Deleting conditions (DELETE /rules/{ruleId}/condition/{condId})
  • Applying rules (POST /rules/{ruleId}/apply)

The endpoints are well-defined with appropriate request/response schemas and error handling. The updated OpenAPI dependencies (generator: 1.4.0, runtime: 1.6.0) are compatible with these endpoints and their implementation in the rules service.

🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Description: Check if rule-engine endpoints are defined in OpenAPI spec
# Expected: Find rule-engine related endpoint definitions

# Search for rule-engine endpoints in OpenAPI spec
rg -A 5 "paths:.*rule" "src/swift-server/openapi.yaml"

Length of output: 140


Script:

#!/bin/bash
# Let's first check if the OpenAPI spec file exists and its location
fd -e yaml -e yml | grep -i "openapi"

# Also search for any rule-engine related files to understand the context
fd -e swift | grep -i "rule"

Length of output: 354


Script:

#!/bin/bash
# Let's examine the OpenAPI spec file content
cat src/swift-server/openapi.yaml

# Also check the rules service implementation to understand the API requirements
cat src/swift-server/rules/rules.service.swift

Length of output: 42186

src/swift-server-tests/importers/importer-helper.swift (1)

2-2: LGTM!

The addition of the Queues import is appropriate for the new queue functionality being added.

src/swift-server-tests/profile.tests.swift (1)

Line range hint 1-7: LGTM! Appropriate test framework imports.

The imports and class declaration are well-structured, with necessary testing frameworks and database access through Fluent.

src/swift-server/importer/importer-api-impl.swift (1)

99-100: LGTM! Consider enhancing error handling and documentation.

The addition of queue-based processing aligns well with the new rule-engine architecture. However, consider the following suggestions:

  1. Ensure proper error handling for queue-related failures
  2. Document the queuing behavior, particularly:
    • What happens if the queue is unavailable?
    • Are there any timeout considerations?
    • How are failed imports retried?

Let's verify the error handling implementation:

src/swift-server-tests/importers/importer-commerz-bank.swift (1)

31-32: LGTM! Verify queue configuration.

The addition of queue management through withQueue parameter is a good improvement for handling asynchronous imports.

Let's verify the queue configuration across other importers:

✅ Verification successful

Queue implementation is consistent across importers

The verification shows that the withQueue: getQueue() pattern is consistently implemented across all importer test files:

  • All importFromFile calls include the withQueue parameter
  • The getQueue() method is defined in importer-helper.swift
  • The pattern is used uniformly in all test files (caixa-enginyers, n26, commerz-bank, and importer-service)
🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Description: Check for consistent queue usage across importers
# Expected: All importers should use the same queue pattern

# Search for importFromFile calls in test files
rg -t swift "importFromFile.*withQueue:" "src/swift-server-tests"

Length of output: 65


Script:

#!/bin/bash
# Let's try a broader search to understand the context better

# 1. First, find all test files related to importers
echo "=== Importer Test Files ==="
fd "importer.*\.swift$" src/swift-server-tests

# 2. Then look for importFromFile usage pattern
echo -e "\n=== ImportFromFile Usage ==="
rg "importFromFile" src/swift-server-tests

# 3. Check for queue-related methods
echo -e "\n=== Queue Usage Patterns ==="
rg "getQueue|withQueue" src/swift-server-tests

# 4. Look at the base test class
echo -e "\n=== Base Test Class Content ==="
fd "BaseImporterTests" src/swift-server-tests --exec cat {}

Length of output: 4291

src/swift-server/importer/importers/new-import.service.swift (3)

3-3: LGTM: Queue import added appropriately

The Queues import is correctly placed with other framework imports and supports the new queue-based transaction processing.


119-119: Document the queue usage in transaction processing

While the queue parameter is consistently passed to bankTransactionService.addTransaction, it's unclear what type of jobs are being queued and how they relate to the rule engine implementation.

Please add documentation explaining:

  • The purpose of the queue in transaction processing
  • What jobs are being queued
  • How this integrates with the new rule engine
#!/bin/bash
# Check the bankTransactionService implementation to understand queue usage
ast-grep --pattern 'func addTransaction(on: Database, withQueue: Queue, transaction: BankTransaction)'

Also applies to: 132-133


64-65: Verify all callers of importFromFile are updated

The signature change is breaking and requires updates to all calling code. The AI summary mentions test files were updated, but let's verify all callers are covered.

✅ Verification successful

Let me try a different approach to find the callers and the method definition. The previous search might have failed due to various reasons like case sensitivity or incomplete pattern. The new search includes more specific parameters and uses both ripgrep and ast-grep to get a comprehensive view.


Let me try a different approach by first locating the file and understanding the complete context, then searching for any files that might be using this method, including test files. This will help us verify the impact of the signature change.


We found several files that reference importFromFile. Let's examine the main service file and a test file to verify if they're using the updated signature with the queue parameter.


All callers have been properly updated with the new queue parameter

The verification shows that all callers of importFromFile are already using the updated signature with the withQueue parameter:

  1. In FileImportService.swift, the main service calls it correctly:
try await uploadImportService.importFromFile(on: db, withQueue: queue, groupOwnerId: groupOwnerId, key: key, fileName: fileName, filePath: filePath)
  1. In importer-service.tests.swift, all test cases use the updated signature:
try await importerService.importFromFile(on: db, withQueue: getQueue(), groupOwnerId: groupOwnerId, key: "test-account", fileName: "test-file.csv", filePath: "whatever")

The breaking change has been properly propagated to all calling code.

🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Search for all importFromFile method calls
rg -A 2 "importFromFile\(" 

Length of output: 104


Script:

#!/bin/bash
# Search for importFromFile method calls with better pattern matching
rg -A 2 "importFromFile\(.*db.*fileName.*filePath"

# Also search for the method definition to understand its full context
ast-grep --pattern 'func importFromFile($$$) {'

Length of output: 102


Script:

#!/bin/bash
# Try to find the file and method definition first
fd "new-import.service.swift" --exec cat {}

# Search for any files containing 'importFromFile' to cast a wider net
rg -l "importFromFile"

# Look for test files that might be using this method
fd -e swift -e test --exec rg -l "importFromFile" {}

Length of output: 5052


Script:

#!/bin/bash
# Let's check the content of importer.service.swift as it's likely the main caller
cat src/swift-server/importer/importer.service.swift

# Also check one of the test files to see the test usage
cat src/swift-server-tests/importers/importer-service.tests.swift

Length of output: 9727

src/swift-server-tests/importers/importer-caixa-enginyers.tests.swift (2)

74-75: LGTM! Consistent implementation across import types.

The queue management implementation is consistently applied across both account and credit card imports, which is a good practice.


25-26: Verify consistent queue usage across import tests.

The addition of queue management looks good. Let's verify that this pattern is consistently applied across all importer tests.

✅ Verification successful

This will help us see all test files and the context around importFromFile calls to verify queue parameter usage.


Queue parameter is consistently used across all importer tests

The verification shows that all importFromFile calls in the importer test files (importer-service.tests.swift, importer-n26.tests.swift, importer-caixa-enginyers.tests.swift, and importer-commerz-bank.swift) consistently include the withQueue: getQueue() parameter. The changes are properly aligned with the codebase's patterns.

🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Description: Check for consistent usage of withQueue parameter across import tests
# Expect: All importFromFile calls should include withQueue parameter

# Search for importFromFile calls without withQueue parameter
rg "importFromFile\([^)]*\)" --type swift "src/swift-server-tests" -A 3 | rg -v "withQueue"

Length of output: 89


Script:

#!/bin/bash
# Let's try a different approach to find importFromFile calls
# First, let's find all files that might contain import tests
fd ".*tests\.swift$" "src/swift-server-tests/importers"

# Then let's look for all importFromFile patterns with more context
rg "importFromFile" --type swift "src/swift-server-tests/importers" -B 2 -A 2

Length of output: 6449

src/swift-server-tests/rules.tests.swift (1)

1-7: LGTM! All necessary imports are present.

The imports cover all required frameworks for testing, database operations, and queue management.

src/swift-server/routes.swift (1)

146-146: Verify the server URL change impact.

The server URL configuration has changed from using server1() to Server1.url(). This might be a breaking change if other parts of the codebase still reference the old method.

✅ Verification successful

Server URL change appears to be an isolated update

The change from server1() to Server1.url() appears to be an isolated update, as:

  • No other references to the old Servers.server1() method exist in the codebase
  • The new Servers.Server1.url() is only used in this single location
  • This suggests the change is part of a coordinated API update and not a breaking change
🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Search for any remaining references to the old server URL method
rg "Servers\.server1\(\)" --type swift

Length of output: 40


Script:

#!/bin/bash
# Let's search for any references to both old and new server URL patterns
# to understand the usage patterns better
rg "Servers\.(server1|Server1)" --type swift -A 2

# Also search for the Servers enum/class definition
ast-grep --pattern 'enum Servers {
  $$$
}'

# And search for any other server URL related patterns
rg "\.url\(\)" --type swift -A 2

Length of output: 564

src/swift-server-tests/app-test.swift (4)

3-3: LGTM: XCTQueues import added for queue testing support

The import is correctly placed and aligns with the new queue testing functionality being introduced.


64-65: LGTM: Queue configuration added for async testing

The queue configuration is correctly set up for async testing using the test driver.


Line range hint 91-97: LGTM: Label factory usage is well-implemented

The label creation logic is clean and properly handles multiple groups. The use of functional programming patterns with proper error handling is commendable.


66-66: Fix comment spacing and clarify NewTransactionJob status

The commented code suggests incomplete implementation. Either implement the NewTransactionJob or remove the comment if it's no longer needed.

Also, fix the comment spacing to comply with SwiftLint:

-//app.queues.add(NewTransactionJob())
+// app.queues.add(NewTransactionJob())
✅ Verification successful

Keep the commented NewTransactionJob line with proper spacing

The NewTransactionJob is properly implemented in src/swift-server/rules/new-transaction.job.swift. The commented line appears in a test setup context where it's intentionally disabled while using the async test queue driver. The comment should be kept with proper spacing as a reference for future testing scenarios.

Fix the spacing as suggested:

-//app.queues.add(NewTransactionJob())
+// app.queues.add(NewTransactionJob())
🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Check if NewTransactionJob is implemented elsewhere in the codebase
rg "class NewTransactionJob" -A 5

Length of output: 635


Script:

#!/bin/bash
# Check if NewTransactionJob is used/imported in the test file
rg -l "NewTransactionJob" src/swift-server-tests/app-test.swift

# Check the context around the commented line in the test file
rg -B 5 -A 5 "//app.queues.add\(NewTransactionJob\(\)\)" src/swift-server-tests/app-test.swift

Length of output: 607

🧰 Tools
🪛 SwiftLint

[Warning] 66-66: Prefer at least one space after slashes for comments

(comment_spacing)

src/swift-server/bank-transaction/bank-transaction.service.swift (2)

2-2: LGTM: Import statement correctly added for queue functionality.

The addition of the Queues import aligns with the new asynchronous transaction processing architecture.


120-124: Verify NewTransactionJob implementation.

The code dispatches to NewTransactionJob, but we should verify:

  1. The job class exists and is properly implemented
  2. The job can handle the provided transaction data
  3. Error handling is in place within the job processing
✅ Verification successful

Let me gather more information about the NewTransactionJob implementation.


Let me check the TransactionSummary type and rules service implementation since they are key components.


NewTransactionJob implementation is properly structured and handles the transaction data correctly

The verification confirms:

  1. NewTransactionJob exists and properly implements AsyncJob protocol with required dequeue and error methods
  2. TransactionSummary payload matches the dispatched data structure with id, movementName, value, and groupOwnerId
  3. Error handling is in place with a dedicated error method
  4. The job processes transactions through RulesService which has proper implementation for applying rules and handling database operations

The implementation is sound and correctly processes the transaction data passed from the bank-transaction service.

🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Search for NewTransactionJob implementation
echo "Searching for NewTransactionJob implementation..."
rg -l "class NewTransactionJob"

# Check for proper job protocol conformance
echo "Checking for proper Job protocol implementation..."
ast-grep --pattern 'class NewTransactionJob: Job {
  $$$
  func dequeue($_) $$$
  $$$
}'

Length of output: 393


Script:

#!/bin/bash
# Get the full implementation of NewTransactionJob
echo "Getting NewTransactionJob implementation..."
rg -A 20 "class NewTransactionJob" src/swift-server/rules/new-transaction.job.swift

# Check initialization and property handling
echo "Checking initialization and properties..."
ast-grep --pattern 'struct NewTransactionJob.Payload {
  $$$
}'

# Check error handling
echo "Checking error handling in dequeue..."
rg -B 2 -A 5 "catch" src/swift-server/rules/new-transaction.job.swift

Length of output: 981


Script:

#!/bin/bash
# Check TransactionSummary definition
echo "Checking TransactionSummary definition..."
rg -A 10 "struct TransactionSummary" 

# Check rulesService implementation
echo "Checking rulesService implementation..."
rg -A 10 "class RulesService" 

# Check applyRules method
echo "Checking applyRules method..."
ast-grep --pattern 'func applyRules(on $_: $_,$_) $$$'

Length of output: 2149

src/swift-server/migrations/initial-migration.swift (2)

76-83: Uncomment the index and document operations.

  1. The commented index should be enabled for better query performance
  2. The operation field values should be documented for maintainability
 try await database.schema("core_condition")
   .id()
   .field("rule_id", .uuid, .required, .references("core_rule", "id"))
   .field("operation", .string, .required)
   .field("value_str", .string)
   .field("value_float", .float)
-  // .index("rule_id")
+  .index("rule_id")
   .create()

Consider adding a comment documenting the valid operations and when to use value_str vs value_float.


190-195: LGTM! Proper cleanup order.

The deletion order correctly respects foreign key constraints, ensuring clean removal of all schemas.

src/swift-server-tests/importers/importer-service.tests.swift (3)

Line range hint 1-200: Well-structured test suite with comprehensive coverage.

The test suite maintains good organization and covers essential scenarios including:

  • Happy path
  • Error cases
  • Duplicate handling
  • Invalid data scenarios

The addition of queue processing maintains the existing test patterns while enhancing the functionality.


106-108: Review concurrent transaction handling.

With the introduction of queue-based processing, there's a potential for race conditions between transaction additions and imports. Consider:

  1. Adding explicit transaction ordering assertions
  2. Implementing queue synchronization in test scenarios
  3. Adding test cases for concurrent import attempts
#!/bin/bash
# Search for transaction ordering and synchronization mechanisms
rg -t swift "class BankTransactionService" -A 20
rg -t swift "func addTransaction.*withQueue" -A 10

20-21: Verify queue implementation in BaseImporterTests.

The addition of withQueue parameter suggests a shift to queue-based processing. Ensure that getQueue() is properly implemented in BaseImporterTests and that it provides a test-appropriate queue instance.

✅ Verification successful

Let me gather more specific information about the test file and queue implementation.


Let me search for the getQueue implementation since it's not visible in the base class.


Queue implementation is correctly set up for testing

The getQueue() method is properly implemented in BaseImporterTests through importer-helper.swift. It returns the application's queue instance (app.queues.queue) which is appropriate for testing purposes. The implementation is supported by the necessary imports (XCTQueues) and follows testing best practices.

🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Search for getQueue implementation in BaseImporterTests
rg -t swift "class BaseImporterTests" -A 10
rg -t swift "func getQueue" -A 5

Length of output: 3238


Script:

#!/bin/bash
# Search for the test file content and BaseImporterTests
rg -t swift "class BaseImporterTests" -A 20
rg -t swift "import.*Queue" 
cat src/swift-server-tests/importers/importer-service.tests.swift

Length of output: 9736


Script:

#!/bin/bash
# Search for getQueue implementation in test files
rg -t swift "func getQueue" -A 5 src/swift-server-tests/
rg -t swift "import.*XCTQueues" -A 10 src/swift-server-tests/

Length of output: 1883

src/swift-server-tests/bank-transactions.tests.swift (4)

112-115: LGTM! Factory usage is clean and consistent.

The transaction creation with group ownership assignment is well-implemented.


Line range hint 154-158: LGTM! Test setup is comprehensive.

The sequence creation for testing label linking is well-structured and provides good test coverage.


Line range hint 246-252: LGTM! Test setup is appropriate for comment testing.

The transaction sequence creation is well-implemented for testing comment functionality.


Line range hint 291-297: LGTM! Consider adding validation for empty strings.

The test setup with initial comments is well-implemented.

Consider verifying the handling of empty strings vs nil comments:

Package.resolved (5)

2-2: LGTM: Hash update reflects dependency changes

The originHash update is consistent with the dependency changes in this PR.


54-55: Core dependency updates look appropriate

The version updates for core dependencies are consistent and compatible:

  • fluent: 4.11.0 → 4.12.0
  • fluent-postgres-driver: 2.9.2 → 2.10.0
  • fluent-sqlite-driver: 4.7.4 → 4.8.0
  • vapor: 4.105.2 → 4.106.3

These are minor version bumps that maintain backward compatibility.

Also applies to: 72-73, 81-82, 441-443


378-379: OpenAPI updates are consistent

The OpenAPI-related dependencies have been updated appropriately:

  • swift-openapi-generator: 1.3.1 → 1.4.0
  • swift-openapi-runtime: 1.5.0 → 1.6.0

These updates align with the AI summary mentioning OpenAPI specification enhancements.

Also applies to: 388-388


279-280: Major version update requires attention

The Swift toolchain-related updates include breaking changes:

  • swift-format: 510.1.0 → 600.0.0
  • swift-syntax: 510.0.3 → 600.0.1

These are major version bumps that may require code adjustments.

Let's check for potential impact:

#!/bin/bash
# Check for Swift format configuration
echo "Checking for .swift-format configuration..."
fd -t f ".swift-format"

# Check for custom syntax rules
echo "Checking for custom syntax rules..."
rg -l "SwiftSyntax|SwiftFormat"

Also applies to: 414-415


139-147: Verify queue implementation requirements

The addition of vapor-queues-fluent-driver and its dependencies suggests implementation of background job processing. This aligns with the rule-engine implementation, but using a non-versioned branch (main) could lead to stability issues.

Let's check the implementation requirements:

Consider:

  1. Pinning to a specific version instead of using the main branch
  2. Implementing retry mechanisms for failed jobs
  3. Setting appropriate job timeouts

Also applies to: 446-451

src/@typespec/openapi3/openapi.yaml (3)

629-634: Standardize delete operation response.

The delete operation returns a Date type while other delete operations in the API (e.g., /users/{id}) return a boolean. This inconsistency could confuse API consumers.


668-687: Add missing request body for condition creation.

The POST endpoint for adding conditions lacks a request body definition. This is required to specify the condition details.


668-732: Add error responses for condition operations.

The condition endpoints are missing error responses for common failure cases (400, 404).

src/swift-server/rules/new-transaction.job.swift Outdated Show resolved Hide resolved
src/swift-server/rules/rules.service.swift Outdated Show resolved Hide resolved
src/swift-server/rules/rules.service.swift Outdated Show resolved Hide resolved
src/swift-server/migrations/initial-migration.swift Outdated Show resolved Hide resolved
src/@typespec/openapi3/openapi.yaml Outdated Show resolved Hide resolved
@Dracks Dracks force-pushed the 3.0.0/rule-engine branch 4 times, most recently from 1351f08 to e0d603b Compare November 17, 2024 20:54
@Dracks Dracks force-pushed the 3.0.0/rule-engine branch 3 times, most recently from 7eb8a66 to 215bdf2 Compare November 22, 2024 16:54
@Dracks Dracks force-pushed the 3.0.0/rule-engine branch 4 times, most recently from 2fd3d71 to 3cf1970 Compare December 1, 2024 09:04
@Dracks Dracks force-pushed the 3.0.0/rule-engine branch from 3cf1970 to de46f21 Compare December 1, 2024 20:23
@Dracks Dracks changed the title Working on the new rule-engine Rules & rule-engine backend completed in swift Dec 1, 2024
@Dracks Dracks marked this pull request as ready for review December 1, 2024 20:24
@Dracks
Copy link
Owner Author

Dracks commented Dec 1, 2024

@CodeRabbit review

Copy link

coderabbitai bot commented Dec 1, 2024

✅ Actions performed

Review triggered.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

@Dracks
Copy link
Owner Author

Dracks commented Dec 1, 2024

@CodeRabbit review

Copy link

coderabbitai bot commented Dec 1, 2024

✅ Actions performed

Review triggered.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

Copy link

@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: 11

🧹 Outside diff range and nitpick comments (48)
src/swift-server/label/label.service.swift (3)

Line range hint 6-10: Consider adding validation before saving the label.

While the method correctly handles saving and relationship loading, consider adding validation for:

  • Required fields
  • Group ownership permissions
  • Duplicate label names within the same group

Example validation approach:

 func createLabel(label: Label) async throws -> Label {
+    try await validateLabel(label)
     try await label.save(on: db)
     try await label.$groupOwner.load(on: db)
     return label
 }
+
+private func validateLabel(_ label: Label) async throws {
+    guard let groupId = label.$groupOwner.id else {
+        throw Abort(.badRequest, reason: "Group owner is required")
+    }
+    // Add additional validation logic
+}

Line range hint 19-24: Validate groupIds parameter.

The method accepts groupIds without validation. Consider adding checks for:

  • Empty array
  • Invalid UUIDs
  • User's access rights to these groups

Example validation:

 func getAll(pageQuery: PageQuery = .init(), groupIds: [UUID]) async throws
     -> ListWithCursor<Label>
 {
+    guard !groupIds.isEmpty else {
+        throw Abort(.badRequest, reason: "At least one group ID is required")
+    }
+    try await validateGroupAccess(groupIds)
     var query = Label.query(on: db)
         .filter(\.$groupOwner.$id ~~ groupIds)

Line range hint 4-41: Consider defining a protocol for the label service.

To improve testability and maintain a clear contract, consider extracting the service interface into a protocol:

protocol LabelServiceProtocol {
    func createLabel(label: Label) async throws -> Label
    func getAll(pageQuery: PageQuery, groupIds: [UUID]) async throws -> ListWithCursor<Label>
}

class LabelService: ServiceWithDb, LabelServiceProtocol {
    // Current implementation
}

This would:

  • Make the service contract explicit
  • Facilitate mock creation for testing
  • Enable easier dependency injection
src/swift-server/bank-transaction/bank-transaction-api-impl.swift (6)

Line range hint 27-30: Avoid force unwrapping in response construction.

Using force unwrapping (!) could lead to runtime crashes if the data is unexpectedly nil.

Consider using safe unwrapping:

- bankTransaction: transaction.list.first!,
- labelIds: labelIds[transactionId]!
+ bankTransaction: transaction.list.first ?? BankTransaction(),  // Add appropriate default
+ labelIds: labelIds[transactionId] ?? []

Line range hint 87-90: Similar force unwrapping issue in unlinkLabel response.

The same force unwrapping issue exists in this method's response construction.

Apply similar safe unwrapping:

- bankTransaction: transaction.list.first!,
- labelIds: labelIds[transactionId]!
+ bankTransaction: transaction.list.first ?? BankTransaction(),
+ labelIds: labelIds[transactionId] ?? []

Line range hint 128-129: Unsafe UUID parsing in group ID mapping.

Force unwrapping UUIDs could crash if invalid strings are provided.

Consider using safe transformation:

- inputGroups = inputGroupsStr.map { UUID(uuidString: $0)! }
+ inputGroups = inputGroupsStr.compactMap { UUID(uuidString: $0) }

Line range hint 130-134: Group validation logic could be simplified.

The current validation approach using filter is less readable.

Consider using set operations for clearer intent:

- if !groupIds.filter({ return !validGroupsIds.contains($0) }).isEmpty {
+ let invalidGroups = Set(groupIds).subtracting(Set(validGroupsIds))
+ if !invalidGroups.isEmpty {

Line range hint 193-196: Force unwrapping in comment endpoint response.

Similar force unwrapping issues in response construction.

Apply safe unwrapping:

- bankTransaction: transaction.list.first!,
- labelIds: labelIds[transactionId]!
+ bankTransaction: transaction.list.first ?? BankTransaction(),
+ labelIds: labelIds[transactionId] ?? []

Line range hint 1-199: Consider extracting common response patterns.

The file has several repeated patterns that could be extracted into helper methods:

  1. Transaction response construction
  2. Error response construction
  3. Group validation logic

This would improve maintainability and reduce the chance of inconsistencies.

Consider creating helper methods like:

private func createTransactionResponse(
    transaction: BankTransaction, 
    transactionId: UUID, 
    labelIds: [UUID]
) -> Components.Schemas.BankTransaction {
    // Common response construction logic
}

private func validateGroups(_ inputGroups: [UUID], against validGroups: [UUID]) throws {
    // Common group validation logic
}
src/swift-server-tests/app-test.swift (2)

42-42: Consider adding documentation for the new properties

The factory properties are well-structured as constants and follow a consistent naming pattern. However, adding documentation comments would help explain their purpose in the test setup.

Consider adding documentation like this:

+ /// Group used for additional test scenarios
 var testGroup3: UserGroup!

+ /// Factory for creating test rules
 let ruleFactory = RuleFactory()
+ /// Factory for creating test rule conditions
 let conditionFactory = RuleConditionFactory()
+ /// Factory for creating test bank transactions
 let transactionFactory = BankTransactionFactory()
+ /// Factory for creating test labels
 let labelFactory = LabelFactory()

Also applies to: 47-50


Line range hint 94-98: Consider using map for more idiomatic Swift code

The current implementation using forEach could be more concise.

Consider this more idiomatic approach:

-labelFactory.createSequence(8) {
-  $0.$groupOwner.id = testGroupId2
-  return $0
-}.forEach { label in
-  labels.append(label)
-}
+labels.append(contentsOf: labelFactory.createSequence(8) {
+  $0.$groupOwner.id = testGroupId2
+  return $0
+})
src/swift-server/importer/importers/new-import.service.swift (3)

131-131: Fix inconsistent indentation

The indentation at line 131 is inconsistent with Swift style guidelines.

-							transaction: transaction
+                            transaction: transaction

Line range hint 93-136: Consider refactoring complex transaction processing logic

The transaction processing logic contains multiple nested conditions and repeated patterns. Consider extracting the logic into smaller, more focused methods for better maintainability.

Suggested refactor:

private func handleExistingTransaction(
    transaction: BankTransaction,
    status: FileImportReport,
    statusTransaction: FileImportRow,
    previousState: inout FileImportRow?,
    previous: inout BankTransaction?,
    discarting: inout Bool,
    on db: Database
) async throws {
    if discarting {
        try await handleDiscardedTransaction(status: status, statusTransaction: statusTransaction, previousState: previousState, on: db)
    } else {
        previous = transaction
        previousState = statusTransaction
        discarting = true
    }
}

private func handleDiscardedTransaction(
    status: FileImportReport,
    statusTransaction: FileImportRow,
    previousState: FileImportRow?,
    on db: Database
) async throws {
    let msg = "Repeated row, not inserted"
    status.status = .warn
    statusTransaction.message = msg
    if let previousStateValidated = previousState {
        previousStateValidated.message = msg
        try await previousStateValidated.save(on: db)
    }
    try await statusTransaction.save(on: db)
}

Line range hint 138-152: Enhance error handling specificity

The catch block could be more specific about the types of errors it handles. Consider:

  1. Adding specific catch blocks for known error types
  2. Improving error logging with more context
do {
    // existing code
} catch let error as Exception {
    status.description = String(describing: error)
    let jsonData = try JSONSerialization.data(
        withJSONObject: error.allContext, options: [])
    status.context = String(data: jsonData, encoding: .utf8)
    status.status = .error
} catch {
    status.description = "Unexpected error: \(error)"
    status.status = .error
    // Consider logging the error stack trace in development
    #if DEBUG
    status.context = String(describing: error)
    #endif
}
src/swift-server/importer/importer.service.swift (2)

Line range hint 36-75: Consider using a more robust date handling approach

The current implementation uses RFC1123 date format parsing which can be brittle. Consider using a more standardized approach for cursor date handling.

Consider using ISO8601 format instead:

-        if let dateString = cursorData["created"],
-            let date = Date(rfc1123: dateString)
+        if let dateString = cursorData["created"],
+            let date = ISO8601DateFormatter().date(from: dateString)

Line range hint 76-87: Consider wrapping delete operations in a transaction

The cascading delete operations should be wrapped in a transaction to ensure data consistency in case of failures.

Consider wrapping the delete operations in a transaction:

 func delete(groupIds: [UUID], importId: UUID) async throws {
     let exists = try await FileImportReport.query(on: db).filter(\.$id == importId)
         .filter(\.$groupOwnerId ~~ groupIds).count()
     if exists > 0 {
+        try await db.transaction { transaction in
             try await FileImportRow.query(on: db)
                 .filter(\.$report.$id == importId)
                 .delete()
             try await FileImportReport.query(on: db)
                 .filter(\.$id == importId)
                 .delete()
+        }
     }
 }
src/swift-server/importer/importer-api-impl.swift (2)

Line range hint 81-104: Enhance file upload security and validation

The file upload implementation could benefit from additional security measures:

  1. File size validation is missing
  2. File extension handling is basic
  3. Temporary file path could be made more secure

Consider adding these improvements:

+        // Add file size validation
+        guard upload.file.data.readableBytes < 50_000_000 else { // 50MB limit
+            throw Abort(.payloadTooLarge)
+        }
+
+        // Validate file extension
+        let allowedExtensions = ["csv", "xlsx", "xls"]
+        guard let ext = upload.file.extension,
+              allowedExtensions.contains(ext.lowercased()) else {
+            throw Abort(.badRequest, reason: "Unsupported file type")
+        }
+
         let tmpDir = NSTemporaryDirectory()
-        let filePath = "\(tmpDir)/mr-scrooge-\(UUID().uuidString).\(upload.file.extension ?? "unknown")"
+        let sanitizedFilename = UUID().uuidString
+        let filePath = (tmpDir as NSString).appendingPathComponent(sanitizedFilename)

Line range hint 109-146: Add validation for required fields in FileImport schema

The schema initialization should validate required fields before accessing them.

Consider adding validation:

 init(file: FileImportReport) {
+        guard let fileId = file.id else {
+            throw Abort(.internalServerError, reason: "File import missing ID")
+        }
-        id = file.id!.uuidString
+        id = fileId.uuidString
src/typespec/labels.tsp (1)

39-41: Ensure consistent error handling in the update method

The update method currently returns Label | NotFound | InvalidUUID. Since updates might also involve invalid groupOwnerId, consider adding InvalidGroupOwnerId to the return types for consistency with the create method, if applicable.

Consider modifying the return type as follows:

-      ): Label | NotFound | InvalidUUID;
+      ): Label | NotFound | InvalidGroupOwnerId | InvalidUUID;
src/swift-server-macrosMacros/app-dependency.swift (1)

25-26: Fix the typo in the error message

There is a misspelling in the error message "Missing anotation". It should be "Missing annotation".

Apply this diff to correct the typo:

-    node: node.root, message: "Missing anotation" as! DiagnosticMessage)
+    node: node.root, message: "Missing annotation" as! DiagnosticMessage)
src/swift-server-macrosMacros/basic-errors.swift (2)

17-18: Provide more descriptive error messages for invalid parameters

The error "First parameter should be an string" could be more descriptive. Consider specifying the expected type and the usage context to aid in debugging.

For example:

throw CustomError.message("The first parameter must be a string literal representing the error message.")

24-25: Clarify the error message for invalid code expressions

The message "Invalid code" is generic. Providing more context about what constitutes a valid code expression can help users understand and fix the issue.

Consider revising the error message:

throw CustomError.message("Expected a member access expression (e.g., ErrorCode.someValue) as the second parameter.")
src/swift-server/core/cache.swift (1)

62-68: Conform to Equatable for cleaner equality checks

Rather than overriding isEqual(_:), consider conforming WrappedKey to Equatable for better integration with Swift's equality protocols.

Modify the class declaration and implement ==:

fileprivate final class WrappedKey: NSObject, Equatable {
    static func == (lhs: WrappedKey, rhs: WrappedKey) -> Bool {
        return lhs.key == rhs.key
    }
}

Update isEqual(_:) accordingly:

override func isEqual(_ object: Any?) -> Bool {
    guard let otherKey = object as? WrappedKey else {
        return false
    }
    return self == otherKey
}
src/swift-server/label/label-api-impl.swift (2)

24-25: Improve error message grammar

The error message "GroupOwner ID is not an UUID" has a grammatical error. It should be "GroupOwner ID is not a valid UUID" for clarity.

Apply this diff to correct the message:

                         .init(
-                            message: "GroupOwner ID is not an UUID",
+                            message: "GroupOwner ID is not a valid UUID",
                             code: ApiError.API10025.rawValue))))

64-74: Provide meaningful implementations or detailed responses for unimplemented endpoints

The ApiLabels_update and ApiLabels_delete methods currently return a 501 status code with an undocumented payload. This may not provide sufficient information to API consumers.

Consider implementing these methods or returning a structured error response:

 func ApiLabels_update(_ input: Operations.ApiLabels_update.Input) async throws
     -> Operations.ApiLabels_update.Output
 {
-    return .undocumented(statusCode: 501, UndocumentedPayload())
+    return .notImplemented(
+        .init(
+            body: .json(
+                .init(
+                    message: "The update label functionality is not yet implemented",
+                    code: ApiError.API501.rawValue))))
 }

 func ApiLabels_delete(_ input: Operations.ApiLabels_delete.Input) async throws
     -> Operations.ApiLabels_delete.Output
 {
-    return .undocumented(statusCode: 501, UndocumentedPayload())
+    return .notImplemented(
+        .init(
+            body: .json(
+                .init(
+                    message: "The delete label functionality is not yet implemented",
+                    code: ApiError.API501.rawValue))))
 }

Ensure that ApiError.API501 is properly defined in your error handling.

src/swift-server/core/validations.swift (2)

19-21: Avoid using cryptic enum cases

The enum case .notUuid may not clearly convey its purpose. Consider renaming it to .invalidUUID for better readability and understanding.

Apply this diff to rename the enum case:

 enum GroupOwnerIdState {
-    case notUuid
+    case invalidUUID
     case notOwned(validGroups: [UUID])
     case ok(id: UUID)
 }

And update its usages accordingly:

 guard let groupOwnerId = UUID(uuidString: obj.groupOwnerId) else {
-    return .notUuid
+    return .invalidUUID
 }

48-49: Consolidate enum cases for clarity

In ParentRuleState, both .notUuid and .notFound represent error states. Consider providing more descriptive error information or consolidating these cases if appropriate.

src/typespec/rules.tsp (2)

55-59: Consider adding a discriminator to the Condition union for better OpenAPI generation

When using @oneOf unions, adding a discriminator helps code generators and API consumers to distinguish between types.

Define a discriminator field:

@oneOf
@discriminator("conditionType")
union Condition {
  double: ConditionDouble,
  string: ConditionString,
}

Ensure that each variant includes the conditionType property with a unique value.


104-115: Remove duplicate NotFound response in apply method

The apply method in ApiRule interface includes NotFound response twice. This is redundant.

Apply this diff to remove the duplicate:

 @post @route("{ruleId}/apply") apply(@path ruleId: UUID):
     | NotFound
     | InvalidGroupOwnerId
     | InvalidUUID
-    | NotFound
     | Date;
src/swift-server/rules/rule-engine.swift (2)

25-29: Simplify condition evaluation logic

In checkConditionals, the logic for .notAnd can be simplified for better readability.

Apply this diff to simplify the condition:

 case .or:
     return conditions.contains { $0(transaction) }
 case .notAnd:
-    return conditions.map { $0(transaction) }.filter { $0 }.isEmpty
+    return !conditions.allSatisfy { $0(transaction) }

49-52: Handle missing parent rules gracefully

When a parent rule is not found, the engine throws an exception. Consider handling this scenario more gracefully, possibly by logging an error and skipping the rule.

Modify the code to handle missing parents without throwing, if appropriate.

src/swift-server/rules/rules.service.swift (1)

208-210: Provide detailed feedback on group owner ID mismatch

In the addLabel method, when the group owner IDs do not match, returning invalidOwnerId might not give enough context to the client.

Consider returning an error with a message explaining the mismatch.

src/swift-server/rules/rule-api-impl.swift (2)

37-78: Consider refactoring repetitive validation logic into helper functions

The validation switch cases for groupOwner (lines 37-57) and parentRule (lines 59-78) share similar structures. Refactoring these into helper functions can reduce code duplication and enhance maintainability.


101-108: Correct grammatical error: Change "an UUID" to "a UUID" in error messages

The error messages currently use "an UUID," which is grammatically incorrect. It should be "a UUID" because "UUID" starts with a consonant sound.

Apply the following changes:

- message: "Rule ID should be an UUID",
+ message: "Rule ID should be a UUID",

(This change applies to all instances where "an UUID" appears in the error messages.)

Also applies to: 230-236, 263-270, 271-278, 306-309, 311-314, 338-340, 341-344, 372-374, 375-378

src/swift-server-tests/rules.tests.swift (1)

503-506: Use XCTFail() instead of XCTAssertTrue(false) to indicate test failures

In your test cases, replace XCTAssertTrue(false) with XCTFail() to clearly indicate a test failure. This provides better clarity and more informative test results.

Apply this change:

-case .ConditionDouble:
-    XCTAssertTrue(false)
-case .none:
-    XCTAssertTrue(false)
+case .ConditionDouble:
+    XCTFail("Unexpected ConditionDouble case")
+case .none:
+    XCTFail("Condition is nil")

(This applies to the relevant cases in your switch statements.)

Also applies to: 562-565, 621-624

src/swift-server-macrosMacros/entrypoint.swift (1)

9-9: Rename swift_macrosPlugin to SwiftMacrosPlugin to follow Swift naming conventions

Swift naming conventions recommend using UpperCamelCase for type names. Consider renaming the struct to SwiftMacrosPlugin for consistency.

src/swift-server/core/cursor-handler.swift (1)

Line range hint 33-36: Prevent potential crash when data is empty

The force unwrap data.last! could crash if data is empty and hasMore is true. Also consider validating that limit is greater than 0.

Consider this safer implementation:

 func getListWithCursor<T>(data: [T], generateCursor: (T) -> String) -> ListWithCursor<T> {
+    guard limit > 0 else {
+        return ListWithCursor(list: [], next: nil)
+    }
     let hasMore = data.count >= limit
-    return ListWithCursor(list: data, next: hasMore ? generateCursor(data.last!) : nil)
+    return ListWithCursor(list: data, next: hasMore ? data.last.map(generateCursor) : nil)
 }
.coderabbit.yaml (2)

28-78: Optimize tool configuration for Swift project

The configuration enables many linting tools that aren't relevant for a Swift project. Consider disabling unused tools to optimize the review process and reduce noise.

    swiftlint:
      enabled: true
-    phpstan:
-      enabled: true
-    golangci-lint:
-      enabled: true
-    rubocop:
-      enabled: true

82-92: Consider adding environment variable configuration

Since the project uses environment variables (e.g., queue worker count in configure.swift), consider adding environment variable configuration to the knowledge base scope.

  knowledge_base:
    opt_out: false
    learnings:
      scope: auto
+   environment:
+     scope: auto
+     variables:
+       - QUEUE_WORKERS
    issues:
      scope: auto
src/swift-server/configure.swift (2)

75-75: Remove or implement commented code

The commented line for scheduled jobs should either be implemented or removed if not needed.

- //try app.queues.startScheduledJobs()
+ // TODO: Implement scheduled jobs if needed
+ // try app.queues.startScheduledJobs()

Line range hint 76-80: Enhance error handling with logging

The catch block could benefit from structured logging to aid debugging in production.

  } catch {
-     print(error)
+     app.logger.error("Failed to configure application: \(error)")
+     if app.environment == .development {
+         print("Development details: \(error)")
+     }
      throw error
  }
src/swift-server/core/error.swift (2)

50-57: Enhance error messages for rule-related errors

The error messages for rule-related errors could be more specific and actionable:

  .E10014: ErrorInfo(
-     message: "Retrieving an String from a condition when it doesn't have it"),
+     message: "Invalid condition: Expected string value but none found"),
  .E10015: ErrorInfo(
-     message: "Retrieving the Double from a condition when it doesn't have it"),
+     message: "Invalid condition: Expected numeric value but none found"),
  .E10016: ErrorInfo(
      message: "Rule parent cannot be found by the ID",
-     additionalInfo: "this can be because the parent rule is with another groupOwnerId"
+     additionalInfo: "Verify that the parent rule exists and belongs to the same group (groupOwnerId mismatch)"
  ),

8-19: Add documentation for API error codes

Consider adding documentation comments to describe the scenarios where each API error code is used, especially for the newly added codes (API10020-API10050).

+ /// API Error codes for the rules engine
+ /// - API10020-API10029: Rule validation errors
+ /// - API10030-API10039: Rule execution errors
+ /// - API10040-API10049: Rule configuration errors
+ /// - API10050: General rule engine error
 enum ApiError: String, StringEnumType {
src/swift-server/core/demo-data.command.swift (1)

58-77: Consider enhancing error handling and using constants

The implementation could benefit from the following improvements:

  1. Add error handling for label creation failures
  2. Consider using constants for label names and condition values
  3. Consider capturing and logging the created rule IDs for verification

Here's a suggested improvement:

+ private enum DemoLabels {
+     static let income = "income"
+     static let expenses = "expenses"
+     static let aral = "aral"
+ }

+ private enum DemoConditions {
+     static let zeroValue = 0.0
+     static let aralKeyword = "aral"
+ }

 private func addRulesAndLabels(app: Application, groupOwnerId: UUID) async throws {
-    let income = Label(groupOwnerId: groupOwnerId, name: "income")
+    let income = Label(groupOwnerId: groupOwnerId, name: DemoLabels.income)
     try await income.save(on: app.db)

-    let expenses = Label(groupOwnerId: groupOwnerId, name: "expenses")
+    let expenses = Label(groupOwnerId: groupOwnerId, name: DemoLabels.expenses)
     try await expenses.save(on: app.db)

-    let aral = Label(groupOwnerId: groupOwnerId, name: "aral")
+    let aral = Label(groupOwnerId: groupOwnerId, name: DemoLabels.aral)
     try await aral.save(on: app.db)

+    // Create rules and store IDs for verification
+    let rules = try await [
-    let _ = try await Rule.createRule(
+        Rule.createRule(
-        on: app.db, for: groupOwnerId, with: .init(.less, valueDouble: 0),
+        on: app.db, for: groupOwnerId, with: .init(.less, valueDouble: DemoConditions.zeroValue),
         toApply: expenses),
-    let _ = try await Rule.createRule(
+        Rule.createRule(
-        on: app.db, for: groupOwnerId, with: .init(.greater, valueDouble: 0),
+        on: app.db, for: groupOwnerId, with: .init(.greater, valueDouble: DemoConditions.zeroValue),
         toApply: income),
-    let _ = try await Rule.createRule(
+        Rule.createRule(
-        on: app.db, for: groupOwnerId, with: .init(.contains, valueStr: "aral"),
+        on: app.db, for: groupOwnerId, with: .init(.contains, valueStr: DemoConditions.aralKeyword),
         toApply: aral)
+    ]
+
+    app.logger.info("Created demo rules with IDs: \(rules.map { $0.id?.uuidString ?? "nil" })")
 }
src/@typespec/openapi3/openapi.yaml (1)

786-791: Standardize error response descriptions.

The description "Bad request, usually when providing an invalid string as UUID" should be more generic to cover all validation errors.

           description: >-
-            Bad request, usually when providing an invalid string as UUID
+            Bad Request - The request was invalid or cannot be served. The exact error is explained in the error message.

Also applies to: 824-829, 870-875, 915-920

src/swift-server/bank-transaction/bank-transaction.service.swift (2)

4-4: LGTM! Good architectural improvement.

The inheritance from ServiceWithQueueAndDb enhances the service layer by providing standardized database and queue capabilities.


Line range hint 28-63: Remove commented out code.

The commented line .with(\.$labels) should be removed if it's no longer needed. Keeping commented-out code can lead to confusion and maintenance issues.

- // .with(\.$labels)
src/swift-server/graph/graph.service.swift (1)

161-161: LGTM! Consistent architectural improvement.

The inheritance from ServiceWithDb aligns with the service layer standardization across the application.

📜 Review details

Configuration used: .coderabbit.yaml
Review profile: CHILL

📥 Commits

Reviewing files that changed from the base of the PR and between ad623a6 and de46f21.

📒 Files selected for processing (57)
  • .coderabbit.yaml (1 hunks)
  • Package.resolved (8 hunks)
  • Package.swift (4 hunks)
  • src/@typespec/openapi3/openapi.yaml (11 hunks)
  • src/cli/cli.module.ts (0 hunks)
  • src/cli/config/cli-config.module.ts (0 hunks)
  • src/cli/config/cli-config.ts (0 hunks)
  • src/cli/demo-data.service.ts (0 hunks)
  • src/cli/demo.command.ts (0 hunks)
  • src/cli/logs.command.ts (0 hunks)
  • src/cli/migrations.command.ts (0 hunks)
  • src/swift-server-macros/entrypoint.swift (1 hunks)
  • src/swift-server-macrosMacros/app-dependency.swift (1 hunks)
  • src/swift-server-macrosMacros/basic-errors.swift (1 hunks)
  • src/swift-server-macrosMacros/entrypoint.swift (1 hunks)
  • src/swift-server-tests/app-test.swift (5 hunks)
  • src/swift-server-tests/importers/importer-caixa-enginyers.tests.swift (2 hunks)
  • src/swift-server-tests/importers/importer-commerz-bank.swift (1 hunks)
  • src/swift-server-tests/importers/importer-helper.swift (3 hunks)
  • src/swift-server-tests/importers/importer-n26.tests.swift (2 hunks)
  • src/swift-server-tests/importers/importer-service.tests.swift (8 hunks)
  • src/swift-server-tests/rules.tests.swift (1 hunks)
  • src/swift-server/bank-transaction/bank-transaction-api-impl.swift (9 hunks)
  • src/swift-server/bank-transaction/bank-transaction.service.swift (8 hunks)
  • src/swift-server/configure.swift (3 hunks)
  • src/swift-server/core/cache.swift (1 hunks)
  • src/swift-server/core/cursor-handler.swift (1 hunks)
  • src/swift-server/core/demo-data.command.swift (1 hunks)
  • src/swift-server/core/error.swift (2 hunks)
  • src/swift-server/core/services.swift (1 hunks)
  • src/swift-server/core/validations.swift (1 hunks)
  • src/swift-server/graph/graph-api-impl.swift (4 hunks)
  • src/swift-server/graph/graph.service.swift (6 hunks)
  • src/swift-server/importer/importer-api-impl.swift (5 hunks)
  • src/swift-server/importer/importer.service.swift (2 hunks)
  • src/swift-server/importer/importers/new-import.service.swift (5 hunks)
  • src/swift-server/label/label-api-impl.swift (3 hunks)
  • src/swift-server/label/label.service.swift (2 hunks)
  • src/swift-server/migrations/initial-migration.swift (2 hunks)
  • src/swift-server/routes.swift (1 hunks)
  • src/swift-server/rules/new-transaction.job.swift (1 hunks)
  • src/swift-server/rules/rule-api-impl.swift (1 hunks)
  • src/swift-server/rules/rule-engine.swift (1 hunks)
  • src/swift-server/rules/rules.models.swift (1 hunks)
  • src/swift-server/rules/rules.service.swift (1 hunks)
  • src/swift-server/session/session-api-impl.swift (1 hunks)
  • src/swift-server/user/user-api-impl.swift (3 hunks)
  • src/swift-server/user/user.service.swift (3 hunks)
  • src/typespec/bank-transactions.tsp (2 hunks)
  • src/typespec/global.tsp (2 hunks)
  • src/typespec/graphs.tsp (1 hunks)
  • src/typespec/imports.tsp (2 hunks)
  • src/typespec/labels.tsp (2 hunks)
  • src/typespec/profile.tsp (2 hunks)
  • src/typespec/rules.tsp (1 hunks)
  • src/typespec/session.tsp (2 hunks)
  • todo.md (3 hunks)
💤 Files with no reviewable changes (7)
  • src/cli/config/cli-config.ts
  • src/cli/config/cli-config.module.ts
  • src/cli/cli.module.ts
  • src/cli/demo.command.ts
  • src/cli/demo-data.service.ts
  • src/cli/migrations.command.ts
  • src/cli/logs.command.ts
✅ Files skipped from review due to trivial changes (1)
  • src/typespec/imports.tsp
🚧 Files skipped from review as they are similar to previous changes (12)
  • src/swift-server/routes.swift
  • src/swift-server-tests/importers/importer-helper.swift
  • src/swift-server-tests/importers/importer-n26.tests.swift
  • src/swift-server-tests/importers/importer-commerz-bank.swift
  • src/swift-server-tests/importers/importer-service.tests.swift
  • src/swift-server-tests/importers/importer-caixa-enginyers.tests.swift
  • src/swift-server/user/user-api-impl.swift
  • src/swift-server/rules/new-transaction.job.swift
  • src/swift-server/user/user.service.swift
  • src/swift-server/rules/rules.models.swift
  • src/swift-server/migrations/initial-migration.swift
  • Package.resolved
🧰 Additional context used
📓 Learnings (1)
src/typespec/rules.tsp (1)
Learnt from: Dracks
PR: Dracks/mr-scrooge#30
File: src/typespec/rules.tsp:49-52
Timestamp: 2024-11-12T09:47:03.059Z
Learning: In `src/typespec/rules.tsp`, the `ConditionString` model does not have a `field` property.
🔇 Additional comments (48)
src/swift-server/label/label.service.swift (2)

4-5: LGTM! Service refactoring aligns with the codebase architecture.

The conversion from struct to class with ServiceWithDb inheritance follows the established pattern for service classes, improving consistency across the codebase.


Line range hint 25-41: LGTM! Well-implemented cursor-based pagination.

The implementation includes:

  • Proper cursor parsing and handling
  • Efficient sorting and limiting
  • Relationship eager loading
  • Clean cursor generation for the next page
src/swift-server/bank-transaction/bank-transaction-api-impl.swift (3)

10-10: Service access pattern improvement looks good.

The transition to application-scoped service access is a good architectural improvement for dependency injection and service lifetime management.

Also applies to: 19-19, 24-24


68-68: Service access changes are consistent.

The changes maintain consistency with the new service access pattern across the codebase.

Also applies to: 77-77, 82-82


153-153: Service access changes maintain consistency.

The changes align with the new service access pattern established across the codebase.

Also applies to: 174-174, 188-188

src/swift-server-tests/app-test.swift (3)

3-3: LGTM: Import statement properly added

The XCTQueues import is appropriately placed with other test-related imports and is necessary for the queue testing functionality being introduced.


72-72: LGTM: Test group setup is consistent

The creation and attachment of testGroup3 follows the established pattern and is properly integrated into the test setup.

Also applies to: 81-81


64-68: Verify the status of the commented NewTransactionJob

The queue configuration is correct for testing, but there's a commented line that might need attention:

//app.queues.add(NewTransactionJob())
✅ Verification successful

The commented NewTransactionJob line in tests is intentional and correct

The commented line in the test file is appropriate because:

  1. NewTransactionJob is properly registered in the main application configuration (configure.swift)
  2. The test environment specifically overrides the queue driver to .asyncTest for testing purposes
  3. Not registering the job in tests allows for better isolation and control of the test environment
🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Check if NewTransactionJob is used elsewhere in the codebase
rg "NewTransactionJob" --type swift

Length of output: 368


Script:

#!/bin/bash
# Check the implementation and usage context of NewTransactionJob
ast-grep --pattern 'class NewTransactionJob $_' -A 20

Length of output: 55


Script:

#!/bin/bash
# Let's try to get the content of the job file and its usage
cat src/swift-server/rules/new-transaction.job.swift
echo "---"
cat src/swift-server/configure.swift

Length of output: 3064

src/swift-server/importer/importers/new-import.service.swift (3)

65-66: LGTM: Queue parameter addition aligns with architectural changes

The method signature change from Database to Queue parameter aligns well with the new queue-based architecture.


38-41: Verify error handling for bankTransactionService initialization

The service is now properly initialized using dependency injection via the Application context. However, ensure that app.bankTransactionService access is properly handled.

✅ Verification successful

Let me analyze the results and generate a final response.

Access to bankTransactionService appears safe and well-handled

The bankTransactionService is properly registered as a service dependency in core/services.swift and is consistently accessed through the Application context across the codebase. The service access pattern follows the established dependency injection pattern, and there's no evidence of unsafe access or missing error handling in the initialization path.

🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Check how bankTransactionService is registered and if there's error handling
rg -A 5 "bankTransactionService" "src/swift-server"

Length of output: 9981


3-3: Verify consistent service architecture across the codebase

The change to inherit from ServiceWithQueueAndDb indicates an architectural shift towards queue-based processing. This is a good practice for handling potentially long-running import operations.

Also applies to: 34-34

✅ Verification successful

Let me gather more information to verify the architectural consistency.


Architectural change is consistent with the codebase

The addition of the Queues import and inheritance from ServiceWithQueueAndDb is verified to be consistent with the existing architecture. Both FileImportService and NewImportService in the importer module follow the same pattern, using queue-based processing and the standardized service base class. The remaining direct database usage in the codebase is primarily in models and utility functions, which is appropriate.

🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Check if other similar services follow the same pattern
ast-grep --pattern 'class $SERVICE_NAME: ServiceWithQueueAndDb {
  $$$
}'

# Check for any remaining direct Database usage
rg "on\s+db:" "src/swift-server"

Length of output: 1454


Script:

#!/bin/bash
# Check for other service classes in the importer directory
ast-grep --pattern 'class $SERVICE_NAME {
  $$$
}'  src/swift-server/importer

# Check for Queue usage patterns
rg "Queue" --type swift src/swift-server/importer -A 2

Length of output: 1788

src/swift-server/importer/importer.service.swift (1)

2-2: LGTM: Improved service structure with proper dependency injection

The refactoring to inherit from ServiceWithQueueAndDb and the structured initialization with proper dependency injection is a good improvement. The parser initialization in the constructor ensures all dependencies are properly set up at creation time.

Also applies to: 5-5, 13-20

Package.swift (2)

27-29: Security Consideration: Using 'main' branch for 'vapor-queues-fluent-driver'

Using the main branch for dependencies can lead to unexpected behavior or security issues. Please consider pinning vapor-queues-fluent-driver to a specific version or commit hash to ensure stability.


36-36: Verify the version specification for swift-format

The version 600.0.0 for swift-format may not correspond to a stable release. Ensure that this version is correct and consider pinning to a specific stable release to maintain compatibility.

Run the following script to list available versions of swift-format:

src/swift-server/core/services.swift (4)

6-11: Proper initialization in ServiceWithDb

The ServiceWithDb class correctly initializes the database connection, providing a solid base for services that require database access.


13-20: Correct inheritance and initialization in ServiceWithQueueAndDb

The ServiceWithQueueAndDb class appropriately extends ServiceWithDb and initializes both the queue and database, ensuring that services inheriting from it have access to both dependencies.


22-49: Effective use of StorageKey protocol for services

By extending each service to conform to StorageKey, you facilitate dependency injection and service retrieval, enhancing modularity and testability.


50-71: Consistent application of @ServiceDependency

The Application extension uses @ServiceDependency consistently for all services, promoting clean and manageable access to services throughout the application.

src/typespec/rules.tsp (2)

49-52: [Duplicate Comment] Ensure consistency in condition models

The ConditionDouble model does not include a field property, similar to the ConditionString model.


23-26: Ensure correct merging of RuleParam into Rule

When extending RuleParam in Rule, confirm that properties are correctly merged and there are no conflicts.

No action needed here if the merging is intentional and tested.

src/swift-server/rules/rules.service.swift (1)

258-263: Check for side effects when detaching all pivots

In removeLabelActionPivot, detaching all pivots without condition may have unintended consequences. Ensure this is the desired behavior.

Consider adding checks or logging to confirm that only intended pivots are detached.

src/swift-server/rules/rule-api-impl.swift (1)

217-221: Verify if ApiRule_apply endpoint should be implemented

The ApiRule_apply function currently returns a 501 Not Implemented status. Confirm whether this endpoint is intended for future implementation or should be removed if not needed.

src/swift-server-tests/rules.tests.swift (2)

25-25: Replace force unwrapping with safe alternatives

Using force unwrapping (!) can lead to runtime crashes if the value is nil. Use try label.requireID() or optional binding to safely unwrap the id.

Also applies to: 133-135


138-140: Add descriptive messages and constants for magic numbers in assertions

Consider adding descriptive messages to your XCTAssertEqual statements to clarify the purpose of each test. Defining magic numbers as constants improves readability and maintainability.

Also applies to: 152-175

src/swift-server-macros/entrypoint.swift (1)

3-3: Verify module naming for external macros

The module name "swift_macrosMacros" appears in the external macro declarations. Ensure that this module name is correct and consistent with your project's naming conventions.

Also applies to: 9-9, 13-13

src/swift-server/core/cursor-handler.swift (1)

38-41: LGTM: Clean generic struct implementation

The ListWithCursor<T> structure is well-designed and follows Swift naming conventions.

src/typespec/session.tsp (2)

56-56: LGTM: Consistent error type usage

The change from InvalidGroupId to InvalidUUID aligns with the global error handling approach.


33-37: Verify password validation logic

Please ensure that the password validation logic in the implementation enforces:

  1. Old password verification when newPassword is provided
  2. Both password and newPassword are required together for password changes
✅ Verification successful

Password validation logic is properly implemented

The implementation in session-api-impl.swift correctly enforces the password validation requirements:

  1. When newPassword is provided, it verifies the old password using user.verifyPassword(pwd: password)
  2. Both password and newPassword are required together, with appropriate error responses:
    • Returns API10019 if password is missing when newPassword is provided
    • Returns API10020 if the provided password doesn't match the current password
🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Search for password validation in the implementation
rg -A 5 "func.*updateMe.*UpdateMyProfile" src/swift-server/

Length of output: 61


Script:

#!/bin/bash
# Search for updateMe function implementation with broader pattern
rg -A 10 "updateMe.*\{" src/swift-server/

# Search for password validation patterns
rg -A 5 "password.*validation|validatePassword" src/swift-server/

# Search for files containing updateMe implementation
fd -e swift -x grep -l "updateMe"

Length of output: 195


Script:

#!/bin/bash
# Get the implementation of updateMe from the identified file
cat src/swift-server/session/session-api-impl.swift

Length of output: 3224

src/typespec/graphs.tsp (2)

71-74: LGTM: Improved response type clarity

The block structure for the status code and return type is more readable and follows TypeSpec best practices.


80-84: LGTM: Better union type formatting

The vertical layout of union types improves readability without changing functionality.

src/typespec/bank-transactions.tsp (2)

38-38: LGTM: Parameter formatting improvement

The spacing around the default value assignment improves readability while maintaining the same functionality.


58-61: LGTM: Method signature formatting improvement

The multi-line formatting of the comment method improves readability while maintaining the same functionality.

Let's verify the error handling after LabelGroupIdMismatch model removal:

src/typespec/profile.tsp (2)

16-19: LGTM: Model definitions formatting improvements

The improved indentation in model definitions enhances code readability.

Also applies to: 23-25, 29-30


47-50: LGTM: List method formatting improvement

The multi-line parameter formatting improves readability.

todo.md (2)

63-63: LGTM: Architectural improvement with dependency injection

The completion of moving services to dependency injection is a significant architectural improvement that will enhance testability and maintainability.


40-40: Verify rules engine implementation completeness

The todo list indicates that rule application is complete but CRUD operations are pending. This suggests a potential gap in the rules engine implementation.

Consider implementing the CRUD operations before marking the rules engine as complete to ensure full functionality.

Also applies to: 49-51

src/swift-server/configure.swift (1)

71-75: Consider enhancing queue configuration robustness.

Several improvements could make the queue configuration more production-ready:

  1. The worker count is hardcoded. Consider making it configurable via environment variables.
  2. Starting jobs immediately after configuration might need error handling or retry logic.
  3. The TODO comment suggests missing configuration - this should be addressed before production deployment.
src/swift-server/session/session-api-impl.swift (1)

65-68: LGTM! Service access pattern improved

The change to access userGroupService through request.application follows better dependency injection practices and aligns with the application's architecture.

src/swift-server/graph/graph-api-impl.swift (4)

32-32: LGTM! Consistent service access pattern

Service access moved to application context, improving dependency management.


60-63: LGTM! Pagination parameters properly handled

Service call properly handles pagination with limit and cursor while using the application context.


105-106: LGTM! Clear parameter passing

Update operation clearly passes all required parameters while using application context.


147-148: LGTM! Proper error handling maintained

Delete operation maintains proper error handling while using application context.

src/swift-server/core/demo-data.command.swift (1)

52-53: LGTM! Demo data generation enhanced

Added rules and labels generation to complete the demo data setup.

src/@typespec/openapi3/openapi.yaml (2)

1355-1397: LGTM! Well-structured condition schemas.

The condition schemas are well-designed with:

  • Clear separation between string and double operations
  • Proper required field definitions
  • Good use of oneOf for polymorphic types

780-785: 🛠️ Refactor suggestion

Standardize response type for rule application.

The /rules/{ruleId}/apply endpoint returns a Date type while other similar operations return boolean. Consider standardizing this for consistency.

             schema:
-                $ref: '#/components/schemas/Date'
+                type: boolean

Likely invalid or redundant comment.

src/swift-server/bank-transaction/bank-transaction.service.swift (1)

98-107: Previous review comment about error handling is still applicable.

The concerns about error handling and safe unwrapping of transaction.id remain valid.

src/swift-server/graph/graph.service.swift (2)

186-192: Good implementation of database parameter.

The optional database parameter with fallback to self.db is a clean way to support both transaction and non-transaction contexts.


530-533: LGTM! Well-structured transaction handling.

The implementation correctly:

  • Uses transactions for atomic operations
  • Maintains referential integrity in deletion order
  • Handles errors appropriately

src/swift-server/importer/importer.service.swift Outdated Show resolved Hide resolved
Package.swift Outdated Show resolved Hide resolved
src/typespec/labels.tsp Show resolved Hide resolved
src/swift-server-macrosMacros/app-dependency.swift Outdated Show resolved Hide resolved
src/swift-server/label/label-api-impl.swift Outdated Show resolved Hide resolved
src/swift-server/core/validations.swift Outdated Show resolved Hide resolved
src/swift-server/rules/rules.service.swift Outdated Show resolved Hide resolved
src/typespec/global.tsp Show resolved Hide resolved
src/typespec/profile.tsp Show resolved Hide resolved
@Dracks Dracks force-pushed the 3.0.0/rule-engine branch from 9a1b717 to 021672d Compare December 1, 2024 21:12
Copy link

@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: 4

🧹 Outside diff range and nitpick comments (15)
src/swift-server-tests/session.tests.swift (1)

Line range hint 103-123: LGTM! Well-structured test case.

The test follows good testing practices with clear arrange-act-assert pattern and proper validation of error responses.

Consider these improvements:

  1. Add a comment explaining what error code "API10017" represents
  2. Add more test cases with different invalid UUID formats:
 func testUpdateMeInvalidUUID() async throws {
+    // Test cases for various invalid UUID formats
+    let invalidUUIDs = [
+        "1111111",           // Too short
+        "not-a-uuid",        // Invalid characters
+        "123e4567-e89b-12d3-a456-42665544",  // Incomplete UUID
+    ]
+    
     let app = try getApp()
     let headers = try await app.getHeaders(
         forUser: .init(username: testUser.username, password: "test-password"))
 
+    // API10017: Invalid UUID format error
+    for invalidUUID in invalidUUIDs {
         let newUserInfo = Components.Schemas.UpdateMyProfile(
             username: testUser.username,
             email: "[email protected]",
             firstName: "First name 2",
             lastName: "Last Name 2",
-            defaultGroupId: "1111111"
+            defaultGroupId: invalidUUID
         )
 
         let response = try await app.sendRequest(
             .PUT, "/api/session/me", body: newUserInfo,
             headers: headers)
 
         XCTAssertEqual(response.status, .badRequest)
         let error = try response.content.decode(Components.Schemas._Error.self)
         XCTAssertEqual(error.code, "API10017")
+    }
 }
src/swift-server-tests/graphs.tests.swift (1)

Line range hint 379-379: Remove outdated TODO comment.

The comment "Todo add a test to try to update a not existing graph" can be removed since the test case is already implemented in the testUpdateNonExistingGraph method below.

.github/workflows/test-swift.yml (1)

Line range hint 19-21: Consider adding test coverage reporting

While parallel testing is good for performance, adding test coverage reporting would provide better visibility into test quality.

Consider adding a step for test coverage:

      - name: Run tests with coverage
        run: |
          swift test --enable-code-coverage --parallel
          xcrun llvm-cov export -format="lcov" .build/debug/mr-scroogePackageTests.xctest/Contents/MacOS/mr-scroogePackageTests > coverage.lcov
      - name: Upload coverage
        uses: codecov/codecov-action@v3
        with:
          files: ./coverage.lcov
src/swift-server/graph/graph-api-impl.swift (2)

32-32: LGTM: Improved service access pattern

Accessing the service through request.application context is a better architectural approach as it:

  • Makes dependencies explicit and manageable
  • Facilitates testing through dependency injection
  • Ensures consistent service instance usage across requests

60-63: Consider adding input validation for pagination parameters

While the service access change is good, consider adding validation for the pagination limit to ensure it doesn't exceed reasonable bounds. This can prevent potential performance issues with very large limit values.

+ let validLimit = min(input.query.limit ?? 100, 1000) // Add reasonable upper bound
  let data = try await request.application.graphService.getGraphs(
    pageQuery: .init(
-     limit: input.query.limit ?? 100,
+     limit: validLimit,
      cursor: input.query.cursor),
    groupsId: validGroupsId, graphsIds: graphIds)
.coderabbit.yaml (4)

27-28: Fix indentation and trailing spaces.

There are formatting issues in the base branches configuration:

  • Line 27 has trailing spaces
  • Line 28 uses 8 spaces for indentation instead of the expected 6 spaces

Apply this diff to fix the formatting:

-    base_branches: 
-        - "mr-scrooge-3.0.0"
+    base_branches:
+      - "mr-scrooge-3.0.0"
🧰 Tools
🪛 yamllint (1.35.1)

[error] 27-27: trailing spaces

(trailing-spaces)


[warning] 28-28: wrong indentation: expected 6 but found 8

(indentation)


Line range hint 531-538: Improve error response description.

The error description for status code 403 is too specific to group ownership. Consider making it more generic as this status code could be used for other authorization scenarios.

Apply this diff:

-          description: Forbidden, usually when using a group that the user doesn't have access to
+          description: Forbidden - The user does not have the necessary permissions
🧰 Tools
🪛 yamllint (1.35.1)

[error] 27-27: trailing spaces

(trailing-spaces)


[warning] 28-28: wrong indentation: expected 6 but found 8

(indentation)


Line range hint 770-806: Inconsistent error response schema for 404.

The error response for status code 404 uses the same schema reference twice in the anyOf array, which is redundant.

Apply this diff:

-                anyOf:
-                  - $ref: '#/components/schemas/Error'
-                  - $ref: '#/components/schemas/Error'
+                $ref: '#/components/schemas/Error'
🧰 Tools
🪛 yamllint (1.35.1)

[error] 27-27: trailing spaces

(trailing-spaces)


[warning] 28-28: wrong indentation: expected 6 but found 8

(indentation)


Line range hint 1787-1806: Consider adding validation constraints for Rule schema.

The Rule schema could benefit from additional constraints:

  • Minimum length for name
  • Non-empty array validation for conditions and labelIds

Apply this diff:

       required:
         - id
         - groupOwnerId
         - name
         - relations
         - conditions
         - labelIds
       properties:
         id:
           $ref: '#/components/schemas/UUID'
         parentRuleId:
           $ref: '#/components/schemas/UUID'
         groupOwnerId:
           $ref: '#/components/schemas/UUID'
         name:
           type: string
+          minLength: 1
         relations:
           $ref: '#/components/schemas/ConditionalRelation'
         conditions:
           type: array
           items:
             $ref: '#/components/schemas/Condition'
+          minItems: 1
         labelIds:
           type: array
           items:
             $ref: '#/components/schemas/UUID'
+          minItems: 1
🧰 Tools
🪛 yamllint (1.35.1)

[error] 27-27: trailing spaces

(trailing-spaces)


[warning] 28-28: wrong indentation: expected 6 but found 8

(indentation)

src/swift-server/label/label-api-impl.swift (1)

64-68: Add documentation for unimplemented endpoint

While returning 501 is appropriate for unimplemented endpoints, consider:

  1. Adding a TODO comment explaining the planned implementation
  2. Documenting when this endpoint will be implemented

Would you like help implementing this endpoint or creating a GitHub issue to track this task?

src/swift-server/core/validations.swift (1)

105-107: Use Logging Framework Instead of Print Statements

The print statement used for logging an error message when a rule is corrupted may not be suitable for production code. Consider using a logging framework to handle log messages appropriately.

Apply this diff to replace print with a logger:

-            print(
+            logger.error(
                 "Rule \(try parentRule.requireID()) is corrupted as it points to a parent that is not found for the user group \(userGroup)."
             )

Ensure that logger is properly initialized and configured.

src/@typespec/openapi3/openapi.yaml (1)

809-849: Add request body validation for condition creation.

The condition creation endpoint should validate the request body structure. Consider adding:

  • Required fields validation
  • Value type validation based on the condition type

Add these validations to the request body schema:

       requestBody:
         required: true
         content:
           application/json:
             schema:
               type: object
+              required:
+                - condition
               properties:
                 condition:
                   $ref: '#/components/schemas/ConditionParams'
-              required:
-                - condition
src/swift-server/rules/rule-api-impl.swift (3)

92-93: Refactor repeated user and group ID retrieval into a helper function

The retrieval of the user and validGroupsIds is repeated across multiple API methods. Refactoring these lines into a shared helper function can enhance code maintainability and reduce duplication.

Apply this diff to refactor the code:

-    let user = try await getUser(fromRequest: request)
-    let validGroupsIds = try user.groups.map { return try $0.requireID() }
+    let (user, validGroupsIds) = try await getUserAndGroupIDs()

Define the helper function outside of the API methods:

func getUserAndGroupIDs() async throws -> (User, [UUID]) {
    let user = try await getUser(fromRequest: request)
    let validGroupsIds = try user.groups.map { try $0.requireID() }
    return (user, validGroupsIds)
}

Also applies to: 185-186, 226-227, 260-261, 303-304, 334-335, 368-369


188-191: Create a utility function for UUID parsing and error handling

Parsing UUIDs from path parameters and handling invalid UUID errors are performed in multiple methods. Consider creating a utility function to parse UUIDs and handle errors consistently, reducing code duplication.

Apply this diff to refactor the code:

-    guard let ruleId = UUID(uuidString: input.path.ruleId) else {
-        return #BasicBadRequest(
-            msg: "Rule ID should be an UUID", code: ApiError.API10033)
-    }
+    let ruleId = try parseUUID(input.path.ruleId, fieldName: "Rule ID", errorCode: ApiError.API10033)

Define the utility function:

func parseUUID(_ uuidString: String, fieldName: String, errorCode: ApiError) throws -> UUID {
    guard let uuid = UUID(uuidString: uuidString) else {
        throw #BasicBadRequest(msg: "\(fieldName) should be a UUID", code: errorCode)
    }
    return uuid
}

Also applies to: 230-236, 263-270, 306-309, 337-340, 371-374


217-221: Implement the ApiRule_apply method or remove the placeholder

The ApiRule_apply method currently returns a 501 Not Implemented status, indicating that it's a placeholder. If this endpoint is intended to be implemented, consider adding the necessary logic. Otherwise, if it's not required at this time, you might remove it to avoid exposing unimplemented endpoints.

Would you like assistance in implementing this method? I can help draft the implementation or open a GitHub issue to track this task.

📜 Review details

Configuration used: .coderabbit.yaml
Review profile: CHILL

📥 Commits

Reviewing files that changed from the base of the PR and between de46f21 and 021672d.

📒 Files selected for processing (13)
  • .coderabbit.yaml (1 hunks)
  • .github/workflows/test-swift.yml (1 hunks)
  • Package.swift (4 hunks)
  • src/@typespec/openapi3/openapi.yaml (14 hunks)
  • src/swift-server-macrosMacros/app-dependency.swift (1 hunks)
  • src/swift-server-tests/graphs.tests.swift (1 hunks)
  • src/swift-server-tests/profile.tests.swift (2 hunks)
  • src/swift-server-tests/session.tests.swift (1 hunks)
  • src/swift-server/core/validations.swift (1 hunks)
  • src/swift-server/graph/graph-api-impl.swift (6 hunks)
  • src/swift-server/label/label-api-impl.swift (3 hunks)
  • src/swift-server/rules/rule-api-impl.swift (1 hunks)
  • src/typespec/global.tsp (2 hunks)
🚧 Files skipped from review as they are similar to previous changes (4)
  • src/typespec/global.tsp
  • src/swift-server-tests/profile.tests.swift
  • src/swift-server-macrosMacros/app-dependency.swift
  • Package.swift
🧰 Additional context used
🪛 yamllint (1.35.1)
.coderabbit.yaml

[error] 27-27: trailing spaces

(trailing-spaces)


[warning] 28-28: wrong indentation: expected 6 but found 8

(indentation)

🔇 Additional comments (13)
src/swift-server-tests/graphs.tests.swift (1)

Line range hint 192-196: LGTM! Appropriate status code change for permission-based rejection.

The change from .unauthorized to .forbidden is correct. HTTP 403 (Forbidden) is the appropriate status code when an authenticated user attempts to perform an action they don't have permission for, such as creating a graph for another owner's group. The error code "API10004" is also properly verified.

src/swift-server/graph/graph-api-impl.swift (2)

Line range hint 20-31: LGTM: Correct HTTP status code for authorization failure

The change from .unauthorized (401) to .forbidden (403) is semantically correct. The 403 status code better represents the scenario where an authenticated user attempts to access a resource they don't have permission for.


105-106: LGTM: Consistent service access with proper authorization

The service method calls properly include the user parameter, ensuring authorization checks at the service level. This implements defense in depth by validating permissions at both the API and service layers.

Also applies to: 147-148

.coderabbit.yaml (2)

29-79: Comprehensive tool configuration looks good.

The configuration includes a wide range of code quality tools that will help maintain high code standards:

  • Security: gitleaks, checkov
  • Linting: shellcheck, ruff, eslint, swiftlint, etc.
  • Code quality: markdownlint, hadolint, phpstan, etc.

Line range hint 1355-1397: Well-structured condition schemas.

The condition schemas are well-designed with:

  • Clear separation between string and double operations
  • Proper type discrimination using oneOf
  • Reusable parameter schemas
🧰 Tools
🪛 yamllint (1.35.1)

[error] 27-27: trailing spaces

(trailing-spaces)


[warning] 28-28: wrong indentation: expected 6 but found 8

(indentation)

src/swift-server/label/label-api-impl.swift (4)

9-40: LGTM! Good improvements to validation and error handling

The changes demonstrate several improvements:

  • Proper encapsulation of validation logic using TransformerAndValidator
  • Clear error messages with specific error codes
  • Correct use of HTTP status codes
  • Better service access pattern using dependency injection

51-51: LGTM! Consistent use of dependency injection

The change to use request.application.labelService aligns with the pattern used in other methods, improving consistency and testability.


70-74: Add documentation for unimplemented endpoint

Similar to the update endpoint, consider adding documentation about the implementation timeline and requirements.


77-77: Verify InputWithUserGroup protocol requirements

Let's ensure CreateLabel properly implements all requirements of the InputWithUserGroup protocol.

✅ Verification successful

Based on the search results from previous scripts, I can generate the final response:

Protocol conformance verified successfully

The InputWithUserGroup protocol requires only one property groupOwnerId: String { get }, and the CreateLabel type satisfies this requirement as evidenced by:

  • The protocol definition in src/swift-server/core/validations.swift
  • Usage in tests showing CreateLabel being instantiated with a groupOwnerId parameter
  • Similar conformance pattern used by other types like RuleParam
🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Search for InputWithUserGroup protocol definition and its requirements
ast-grep --pattern 'protocol InputWithUserGroup {
  $$$
}'

# Search for CreateLabel structure to verify it has all required properties
ast-grep --pattern 'struct CreateLabel {
  $$$
}'

Length of output: 112


Script:

#!/bin/bash
# Let's try a broader search for InputWithUserGroup
rg "InputWithUserGroup" -A 5 -B 5

# Search for CreateLabel in Components.Schemas namespace
rg "Components\.Schemas\.CreateLabel" -A 5 -B 5

Length of output: 5184


Script:

#!/bin/bash
# Let's find the CreateLabel struct definition to verify it has groupOwnerId
rg "struct CreateLabel" -A 10 -B 2

# Also search for any other references to CreateLabel properties
rg "CreateLabel\." -A 2 -B 2

Length of output: 67

src/swift-server/core/validations.swift (1)

84-110: Confirmed Fix for Potential Infinite Loop in Parent Rule Traversal

The updated code includes a visitedRuleIds set within the while true loop to prevent infinite loops due to cyclical parent relationships. This effectively addresses the previously identified issue.

src/@typespec/openapi3/openapi.yaml (3)

551-615: LGTM! Label management endpoints are well-defined.

The new PUT and DELETE operations for labels follow RESTful conventions and include appropriate error handling.


Line range hint 1355-1397: LGTM! Well-structured schema definitions for rules and conditions.

The polymorphic condition types and operations are well-defined using OpenAPI's oneOf discriminator. The separation between Double and String operations is clean and appropriate.

Also applies to: 1765-1775


770-786: 🛠️ Refactor suggestion

Standardize success response for rule application.

The /rules/{ruleId}/apply endpoint returns a Date type while similar action endpoints return boolean. Consider standardizing this for consistency.

-                $ref: '#/components/schemas/Date'
+                type: boolean

Likely invalid or redundant comment.

.github/workflows/test-swift.yml Show resolved Hide resolved
src/swift-server/core/validations.swift Show resolved Hide resolved
Comment on lines +172 to +173
'403':
description: Forbidden, usually when using a group that the user doesn't have access to
Copy link

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

Standardize error response descriptions.

Several endpoints have inconsistent descriptions for 400 error responses. Consider standardizing them across all endpoints:

Apply this change to all 400 responses:

-          description: Bad request, usually when providing an invalid string as UUID
+          description: Bad Request - Invalid input parameters or validation errors

This provides a more generic and consistent description that covers all possible validation errors, not just UUID formatting issues.

Also applies to: 251-252, 531-538, 567-572, 602-607, 661-666, 703-708, 786-791, 824-829, 870-875, 915-920, 950-955, 990-995

src/swift-server/rules/rule-api-impl.swift Show resolved Hide resolved
Copy link

@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: 0

🧹 Outside diff range and nitpick comments (2)
src/swift-server/migrations/initial-migration.swift (2)

78-89: Consider adding a cycle prevention constraint for hierarchical rules.

The parent_id relationship allows for hierarchical rules, but there's no built-in protection against circular references. Consider adding a trigger or application-level validation to prevent cycles in the rule hierarchy.

Would you like help implementing a cycle prevention solution?


Line range hint 185-186: Document the datetime handling decision.

The comment references an external Discord discussion about datetime handling. Consider documenting the decision and implementation details in the codebase for future reference.

-			// when problems look at the answer of Gwynne
-			// https://discord.com/channels/431917998102675485/684159753189982218/1282405735455522950
+			// DateTime handling: Using required datetime field as discussed in
+			// https://discord.com/channels/431917998102675485/684159753189982218/1282405735455522950
+			// Key points:
+			// 1. [Add the key points from the discussion]
+			// 2. [Add any implementation details]
📜 Review details

Configuration used: .coderabbit.yaml
Review profile: CHILL

📥 Commits

Reviewing files that changed from the base of the PR and between 021672d and 35ab8da.

📒 Files selected for processing (1)
  • src/swift-server/migrations/initial-migration.swift (6 hunks)
🔇 Additional comments (6)
src/swift-server/migrations/initial-migration.swift (6)

51-54: LGTM! Index addition will improve query performance.

The addition of an index on group_owner_id for the core_bank_transaction table is a good performance optimization for queries filtering by group ownership.


136-139: LGTM! Consistent indexing strategy.

The addition of an index on group_owner_id for the graph_graph table maintains consistency with the indexing strategy used across other tables.


193-196: LGTM! Performance optimization with appropriate indexes.

The addition of indexes on group_owner_id and report_id will improve query performance for import-related operations.

Also applies to: 214-217


229-234: LGTM! Proper deletion order.

The revert method correctly orders the table deletions to respect foreign key constraints, ensuring clean removal of the schema.


63-73: 🛠️ Refactor suggestion

Add an index on label_id for better query performance.

Since this is a junction table, adding an index on label_id would improve query performance for operations filtering or joining on labels.


62-71: Address the enum implementation for link_reason.

There are two comments indicating that enums should be used for the link_reason field. Consider implementing this using one of these approaches:

  1. Use a CHECK constraint to enforce valid values
  2. Create an enum type in the database
  3. Handle the validation at the application level

Would you like help implementing the enum solution?

Copy link

@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: 5

🧹 Outside diff range and nitpick comments (7)
src/swift-server/core/error.swift (2)

50-58: Error messages are clear and well-documented.

The new error messages are descriptive and provide good context, especially E10016's additional security context. Consider adding similar contextual information for E10017 as it also relates to data integrity.

 .E10017: ErrorInfo(
-    message: "Import from file returned an ID that was not found in the DB"),
+    message: "Import from file returned an ID that was not found in the DB",
+    additionalInfo: "This might indicate data corruption or synchronization issues"
+),

Line range hint 1-58: Consider adding explicit mapping between ErrorCode and ApiError.

While the separation between internal ErrorCode and external ApiError is good practice, consider adding explicit mapping between them to ensure consistent error translation from internal to API errors. This would help maintain error handling consistency across the application.

Example approach:

extension ErrorCode {
    var apiError: ApiError {
        switch self {
        case .E10014: return .API10021
        // ... other mappings
        }
    }
}
src/swift-server/bank-transaction/bank-transaction.service.swift (4)

Line range hint 79-89: Consider extracting duplicate detection criteria into a reusable function.

The duplicate detection logic uses multiple criteria that might be reused elsewhere. Consider extracting these conditions into a separate function for better maintainability and reusability.

+private func applyDuplicateDetectionCriteria(
+    query: QueryBuilder<BankTransaction>,
+    transaction: BankTransaction
+) -> QueryBuilder<BankTransaction> {
+    query
+        .filter(\.$groupOwner.$id == transaction.$groupOwner.id)
+        .filter(\.$_date == transaction.date.toString())
+        .filter(\.$details == transaction.details)
+        .filter(\.$kind == transaction.kind)
+        .filter(\.$movementName == transaction.movementName)
+        .filter(\.$value == transaction.value)
+}

 func existsSimilar(transaction: BankTransaction) async throws -> Bool {
-    let similarCount = try await BankTransaction.query(on: db)
-        .filter(\.$groupOwner.$id == transaction.$groupOwner.id)
-        .filter(\.$_date == transaction.date.toString())
-        .filter(\.$details == transaction.details)
-        .filter(\.$kind == transaction.kind)
-        .filter(\.$movementName == transaction.movementName)
-        .filter(\.$value == transaction.value)
-        .count()
+    let similarCount = try await applyDuplicateDetectionCriteria(
+        query: BankTransaction.query(on: db),
+        transaction: transaction
+    ).count()
     return similarCount > 0
 }

92-96: Consider enhancing error handling and logging for batch operations.

The batch insert could benefit from more detailed error handling and logging, especially for partial failures.

 func insertBatch(movements: [BankTransaction]) -> EventLoopFuture<Void> {
-    return movements.create(on: db).map { _ in
-        self.logger.info("Insert batch", metadata: ["sql": "Bulk insert"])
+    return movements.create(on: db).flatMapAlways { result in
+        switch result {
+        case .success:
+            self.logger.info("Insert batch successful", metadata: [
+                "count": "\(movements.count)",
+                "sql": "Bulk insert"
+            ])
+        case .failure(let error):
+            self.logger.error("Insert batch failed", metadata: [
+                "error": "\(error)",
+                "count": "\(movements.count)"
+            ])
+        }
+        return result
     }
 }

Line range hint 120-191: Enhance guard statement messages for better debugging.

Consider adding more descriptive error messages in guard statements to aid in debugging.

-        guard let transaction = transaction else {
+        guard let transaction = transaction else {
+            logger.debug("Transaction not found or not accessible", metadata: [
+                "transactionId": "\(transactionId)",
+                "groupIds": "\(groupIds)"
+            ])
             return .transactionNotFound
         }

Line range hint 192-195: Fix enum naming to follow Swift conventions.

The enum name setCommentReturn should follow Swift's naming convention for types (PascalCase).

-    enum setCommentReturn {
+    enum SetCommentReturn {
         case ok
         case notFound
     }
src/swift-server/rules/rules.service.swift (1)

12-12: Consider injecting RuleEngine dependency.

Instead of creating a RuleEngine instance directly, consider injecting it through the constructor for better testability and flexibility.

-    let ruleEngine = RuleEngine()
+    let ruleEngine: RuleEngine
+    
+    init(db: Database, ruleEngine: RuleEngine = RuleEngine()) {
+        super.init(db: db)
+        self.ruleEngine = ruleEngine
+    }
📜 Review details

Configuration used: .coderabbit.yaml
Review profile: CHILL

📥 Commits

Reviewing files that changed from the base of the PR and between 35ab8da and 4f16cc6.

📒 Files selected for processing (4)
  • src/swift-server/bank-transaction/bank-transaction.service.swift (8 hunks)
  • src/swift-server/core/error.swift (2 hunks)
  • src/swift-server/importer/importer.service.swift (2 hunks)
  • src/swift-server/rules/rules.service.swift (1 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
  • src/swift-server/importer/importer.service.swift
🧰 Additional context used
📓 Learnings (2)
src/swift-server/rules/rules.service.swift (1)
Learnt from: Dracks
PR: Dracks/mr-scrooge#30
File: src/swift-server/rules/rules.service.swift:0-0
Timestamp: 2024-12-01T21:23:33.848Z
Learning: In `src/swift-server/rules/rules.service.swift`, the `applyRules` method has been completely rewritten, rendering previous comments about error handling and performance improvements no longer applicable.
src/swift-server/bank-transaction/bank-transaction.service.swift (1)
Learnt from: Dracks
PR: Dracks/mr-scrooge#30
File: src/swift-server/bank-transaction/bank-transaction.service.swift:0-0
Timestamp: 2024-12-04T17:01:23.715Z
Learning: Prefer using the `requireID()` method instead of force unwrapping optionals in the code.
🔇 Additional comments (8)
src/swift-server/core/error.swift (2)

1-5: LGTM! New error codes follow consistent naming pattern.

The additional error codes (E10014-E10017) maintain the existing sequential naming convention and are well-organized.


8-19: Verify the necessity of adding 30 new API error codes.

While the new error codes follow the existing pattern, adding such a large range of new codes (API10021-API10050) seems excessive. Let's verify their intended usage across the codebase.

✅ Verification successful

The addition of new API error codes (API10021-API10050) is justified

The search results show that all the newly added error codes (API10021-API10050) are actively used in the codebase, primarily in the rules and label API implementations. Each error code serves a specific purpose:

  • Multiple UUID validation errors (API10033, API10041-API10047) for Rule IDs, Condition IDs, and Label IDs
  • Resource not found errors (API10048 for condition/label, API10050 for rule)
  • Various validation and processing errors in rule-api-impl.swift (API10021-API10040)
  • Label-specific error in label-api-impl.swift (API10025)
🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Description: Check usage of new API error codes in the codebase

# Search for any usage of the new API error codes
echo "Searching for usage of new API error codes (API10021-API10050)..."
rg "API100(2[1-9]|[34][0-9]|50)" --type swift

# Count total API errors actually used
echo "Counting total API errors used in the codebase..."
rg "API\d{5}" --type swift

Length of output: 10186

src/swift-server/bank-transaction/bank-transaction.service.swift (4)

4-6: LGTM! Good architectural improvement.

The inheritance from ServiceWithQueueAndDb provides a clean way to access both database and queue functionality, following good dependency injection principles.


Line range hint 8-24: LGTM! Clean implementation of active labels retrieval.

The method effectively retrieves and maps active labels to transactions while properly handling relationships and filtering.


98-109: Consider adding error handling for queue operations.

While the use of requireID() is good, consider implementing error handling for queue operations and transaction rollback if queue dispatch fails.


Line range hint 28-75: Clean up commented code if no longer needed.

There's a commented line that might indicate incomplete work:

// .with(\.$labels)

Consider either removing this comment if it's no longer needed or implementing the functionality if it's still required.

src/swift-server/rules/rules.service.swift (2)

77-105: LGTM! Well-implemented deletion logic.

The deletion implementation is robust with:

  • Proper transaction handling
  • Child rule validation
  • Cleanup of related entities

263-273: LGTM! Well-implemented pivot handling.

The helper method properly handles the pivot relationship by:

  • Updating the link reason before detachment
  • Using proper transaction context

Comment on lines +195 to +224
func addLabel(
labelId: UUID,
toRule ruleId: UUID,
for validGroupsIds: [UUID]
) async throws -> AddLabelState {
let rule = try await Rule.query(on: db).filter(
\.$groupOwner.$id ~~ validGroupsIds
).filter(\.$id == ruleId).first()
guard let rule else {
return .notFound
}

let label = try await Label.query(on: db).filter(\.$id == labelId).first()
guard let label else {
return .notFound
}

if rule.$groupOwner.id != label.$groupOwner.id {
return .invalidOwnerId
}

let labelAttached = try await rule.$labels.isAttached(to: label, on: db)
if !labelAttached {
try await rule.$labels.attach(label, on: db)
}

try await rule.$conditions.load(on: db)
try await rule.$labels.load(on: db)
return .ok(rule: rule)
}
Copy link

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

Wrap label addition in transaction.

While label removal uses a transaction, the label addition method should also use one for consistency and data integrity.

     func addLabel(
         labelId: UUID,
         toRule ruleId: UUID,
         for validGroupsIds: [UUID]
     ) async throws -> AddLabelState {
+        try await db.transaction { database in
-            let rule = try await Rule.query(on: db)...
+            let rule = try await Rule.query(on: database)...
             // ... rest of the implementation using database instead of db
+        }
     }

Committable suggestion skipped: line range outside the PR's diff.

Comment on lines +20 to +24
let cursorData = try self.cursorHandler.parse(cursor)
if let idString = cursorData["id"], let id = UUID(uuidString: idString) {
query = query.filter(\.$id < id)
}
}
Copy link

@coderabbitai coderabbitai bot Dec 4, 2024

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

Enhance error handling for cursor parsing.

The cursor parsing could fail silently if the cursor string is malformed. Consider adding proper error handling and validation.

-            let cursorData = try self.cursorHandler.parse(cursor)
-            if let idString = cursorData["id"], let id = UUID(uuidString: idString) {
-                query = query.filter(\.$id < id)
-            }
+            do {
+                let cursorData = try self.cursorHandler.parse(cursor)
+                guard let idString = cursorData["id"],
+                      let id = UUID(uuidString: idString) else {
+                    throw Abort(.badRequest, reason: "Invalid cursor format")
+                }
+                query = query.filter(\.$id < id)
+            } catch {
+                throw Abort(.badRequest, reason: "Failed to parse cursor: \(error)")
+            }
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
let cursorData = try self.cursorHandler.parse(cursor)
if let idString = cursorData["id"], let id = UUID(uuidString: idString) {
query = query.filter(\.$id < id)
}
}
do {
let cursorData = try self.cursorHandler.parse(cursor)
guard let idString = cursorData["id"],
let id = UUID(uuidString: idString) else {
throw Abort(.badRequest, reason: "Invalid cursor format")
}
query = query.filter(\.$id < id)
} catch {
throw Abort(.badRequest, reason: "Failed to parse cursor: \(error)")
}
}

Copy link
Owner Author

Choose a reason for hiding this comment

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

the idea is that a cursor string that's malformed is a non-existand cursor string.

Copy link

Choose a reason for hiding this comment

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

@Dracks, understood. Thank you for the clarification.


✏️ Learnings added
Learnt from: Dracks
PR: Dracks/mr-scrooge#30
File: src/swift-server/rules/rules.service.swift:20-24
Timestamp: 2024-12-04T17:16:57.895Z
Learning: In the Swift file `src/swift-server/rules/rules.service.swift`, within the `getAll` method, if the cursor string is malformed, it should be treated as a nonexistent cursor. The pagination should proceed without throwing an error, as if no cursor was provided.

Note: Learnings are effective only in the context of similar code segments. To apply general coding guidelines, please configure review instructions. You can manage existing learnings in the app.

Comment on lines +36 to +45
func create(rule: RuleParam) async throws -> Rule {
let newRule = Rule(
groupOwnerId: rule.groupOwnerId, name: rule.name,
conditionsRelation: rule.relations, parentId: rule.parentId)
try await newRule.save(on: db)
try await newRule.$children.load(on: db)
try await newRule.$conditions.load(on: db)
try await newRule.$labels.load(on: db)
return newRule
}
Copy link

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

Wrap creation in transaction and validate parent rule.

The rule creation process should validate the parent rule's existence and wrap all operations in a transaction for data consistency.

     func create(rule: RuleParam) async throws -> Rule {
+        return try await db.transaction { database in
+            if let parentId = rule.parentId {
+                guard let _ = try await Rule.find(parentId, on: database) else {
+                    throw Abort(.badRequest, reason: "Parent rule not found")
+                }
+            }
             let newRule = Rule(
                 groupOwnerId: rule.groupOwnerId, name: rule.name,
                 conditionsRelation: rule.relations, parentId: rule.parentId)
-            try await newRule.save(on: db)
-            try await newRule.$children.load(on: db)
-            try await newRule.$conditions.load(on: db)
-            try await newRule.$labels.load(on: db)
+            try await newRule.save(on: database)
+            try await newRule.$children.load(on: database)
+            try await newRule.$conditions.load(on: database)
+            try await newRule.$labels.load(on: database)
             return newRule
+        }
     }
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
func create(rule: RuleParam) async throws -> Rule {
let newRule = Rule(
groupOwnerId: rule.groupOwnerId, name: rule.name,
conditionsRelation: rule.relations, parentId: rule.parentId)
try await newRule.save(on: db)
try await newRule.$children.load(on: db)
try await newRule.$conditions.load(on: db)
try await newRule.$labels.load(on: db)
return newRule
}
func create(rule: RuleParam) async throws -> Rule {
return try await db.transaction { database in
if let parentId = rule.parentId {
guard let _ = try await Rule.find(parentId, on: database) else {
throw Abort(.badRequest, reason: "Parent rule not found")
}
}
let newRule = Rule(
groupOwnerId: rule.groupOwnerId, name: rule.name,
conditionsRelation: rule.relations, parentId: rule.parentId)
try await newRule.save(on: database)
try await newRule.$children.load(on: database)
try await newRule.$conditions.load(on: database)
try await newRule.$labels.load(on: database)
return newRule
}
}

Comment on lines +113 to +188
func addCondition(
condition: Rule.BaseCondition, forRule ruleId: UUID, for validGroupsIds: [UUID]
) async throws -> AddCodingState {
let rule = try await Rule.query(on: db).filter(
\.$groupOwner.$id ~~ validGroupsIds
).filter(\.$id == ruleId).with(\.$children).first()
guard let rule else {
return .notFound
}

try await rule.$conditions.create(
.init(condition, for: try rule.requireID()), on: db)
try await rule.$conditions.load(on: db)
try await rule.$labels.load(on: db)
return .ok(rule: rule)
}

func updateCondition(
_ condId: UUID, condition updatedCondition: Rule.BaseCondition,
forRule ruleId: UUID,
for validGroupsIds: [UUID]
) async throws -> AddCodingState {
let rule = try await Rule.query(on: db).filter(
\.$groupOwner.$id ~~ validGroupsIds
).filter(\.$id == ruleId).with(\.$children).first()
guard let rule else {
return .notFound
}

let condition = try await Condition.query(on: db).filter(\.$id == condId).filter(
\.$rule.$id == ruleId
).first()

guard let condition else {
return .notFound
}
condition.operation = updatedCondition.operation
condition.valueStr = updatedCondition.valueStr
condition.valueDouble = updatedCondition.valueDouble
try await condition.save(on: db)

try await rule.$conditions.load(on: db)
try await rule.$labels.load(on: db)
return .ok(rule: rule)
}

enum DeleteConditionState {
case deleted(rule: Rule)
case notFound
}

func deleteCondition(
_ condId: UUID,
forRule ruleId: UUID,
for validGroupsIds: [UUID]
) async throws -> DeleteConditionState {
let rule = try await Rule.query(on: db).filter(
\.$groupOwner.$id ~~ validGroupsIds
).filter(\.$id == ruleId).with(\.$children).first()
guard let rule else {
return .notFound
}

let condition = try await Condition.query(on: db).filter(\.$id == condId).filter(
\.$rule.$id == ruleId
).first()

guard let condition else {
return .notFound
}
try await condition.delete(on: db)

try await rule.$conditions.load(on: db)
try await rule.$labels.load(on: db)
return .deleted(rule: rule)
}
Copy link

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

Refactor condition management methods for better maintainability.

  1. The condition management methods share similar rule querying logic that could be extracted.
  2. Operations should be wrapped in transactions.
+    private func findRule(id: UUID, in validGroupIds: [UUID], on database: Database) async throws -> Rule? {
+        return try await Rule.query(on: database)
+            .filter(\.$groupOwner.$id ~~ validGroupIds)
+            .filter(\.$id == id)
+            .with(\.$children)
+            .first()
+    }

     func addCondition(
         condition: Rule.BaseCondition, forRule ruleId: UUID, for validGroupsIds: [UUID]
     ) async throws -> AddCodingState {
+        try await db.transaction { database in
-            let rule = try await Rule.query(on: db)...
+            guard let rule = try await findRule(id: ruleId, in: validGroupsIds, on: database) else {
+                return .notFound
+            }
             // ... rest of the implementation
+        }
     }

Committable suggestion skipped: line range outside the PR's diff.

Comment on lines +52 to +70
func updateRule(withId ruleId: UUID, rule inputRule: RuleParam, for validGroupsIds: [UUID])
async throws -> UpdateRuleState
{
let rule = try await Rule.query(on: db).filter(
\.$groupOwner.$id ~~ validGroupsIds
).filter(\.$id == ruleId).first()
guard let rule else {
return .notFound
}
rule.name = inputRule.name
rule.conditionsRelation = inputRule.relations
rule.$groupOwner.id = inputRule.groupOwnerId
rule.$parent.id = inputRule.parentId
try await rule.save(on: db)
try await rule.$children.load(on: db)
try await rule.$conditions.load(on: db)
try await rule.$labels.load(on: db)
return .ok(rule: rule)
}
Copy link

@coderabbitai coderabbitai bot Dec 4, 2024

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

Add transaction and parent rule validation to update method.

Similar to the create method, the update operation should validate the new parent rule and use a transaction.

     func updateRule(withId ruleId: UUID, rule inputRule: RuleParam, for validGroupsIds: [UUID])
         async throws -> UpdateRuleState
     {
+        return try await db.transaction { database in
             let rule = try await Rule.query(on: db).filter(
                 \.$groupOwner.$id ~~ validGroupsIds
             ).filter(\.$id == ruleId).first()
             guard let rule else {
                 return .notFound
             }
+            if let parentId = inputRule.parentId {
+                guard parentId != ruleId,
+                      let _ = try await Rule.find(parentId, on: database) else {
+                    throw Abort(.badRequest, reason: "Invalid parent rule")
+                }
+            }
             rule.name = inputRule.name
             rule.conditionsRelation = inputRule.relations
             rule.$groupOwner.id = inputRule.groupOwnerId
             rule.$parent.id = inputRule.parentId
-            try await rule.save(on: db)
-            try await rule.$children.load(on: db)
-            try await rule.$conditions.load(on: db)
-            try await rule.$labels.load(on: db)
+            try await rule.save(on: database)
+            try await rule.$children.load(on: database)
+            try await rule.$conditions.load(on: database)
+            try await rule.$labels.load(on: database)
             return .ok(rule: rule)
+        }
     }
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
func updateRule(withId ruleId: UUID, rule inputRule: RuleParam, for validGroupsIds: [UUID])
async throws -> UpdateRuleState
{
let rule = try await Rule.query(on: db).filter(
\.$groupOwner.$id ~~ validGroupsIds
).filter(\.$id == ruleId).first()
guard let rule else {
return .notFound
}
rule.name = inputRule.name
rule.conditionsRelation = inputRule.relations
rule.$groupOwner.id = inputRule.groupOwnerId
rule.$parent.id = inputRule.parentId
try await rule.save(on: db)
try await rule.$children.load(on: db)
try await rule.$conditions.load(on: db)
try await rule.$labels.load(on: db)
return .ok(rule: rule)
}
func updateRule(withId ruleId: UUID, rule inputRule: RuleParam, for validGroupsIds: [UUID])
async throws -> UpdateRuleState
{
return try await db.transaction { database in
let rule = try await Rule.query(on: db).filter(
\.$groupOwner.$id ~~ validGroupsIds
).filter(\.$id == ruleId).first()
guard let rule else {
return .notFound
}
if let parentId = inputRule.parentId {
guard parentId != ruleId,
let _ = try await Rule.find(parentId, on: database) else {
throw Abort(.badRequest, reason: "Invalid parent rule")
}
}
rule.name = inputRule.name
rule.conditionsRelation = inputRule.relations
rule.$groupOwner.id = inputRule.groupOwnerId
rule.$parent.id = inputRule.parentId
try await rule.save(on: database)
try await rule.$children.load(on: database)
try await rule.$conditions.load(on: database)
try await rule.$labels.load(on: database)
return .ok(rule: rule)
}
}

Copy link
Owner Author

Choose a reason for hiding this comment

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

The parent rule is already check in the api-impl. I don't think a transaction here is useful.

Copy link

Choose a reason for hiding this comment

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

@Dracks, understood. Thank you for clarifying that the parent rule validation is already handled in the API implementation, and wrapping the updateRule method in a transaction is not necessary. I appreciate the explanation.


✏️ Learnings added
Learnt from: Dracks
PR: Dracks/mr-scrooge#30
File: src/swift-server/rules/rules.service.swift:52-70
Timestamp: 2024-12-04T17:22:25.530Z
Learning: In the `RuleService`'s `updateRule` method in `src/swift-server/rules/rules.service.swift`, performing parent rule validation is unnecessary because it is already done in the API implementation (`src/swift-server/rules/rule-api-impl.swift`).

Learnt from: Dracks
PR: Dracks/mr-scrooge#30
File: src/swift-server/rules/rules.service.swift:52-70
Timestamp: 2024-12-04T17:22:25.530Z
Learning: Wrapping the `updateRule` method in a transaction within `src/swift-server/rules/rules.service.swift` is not required as per the current design.

Note: Learnings are effective only in the context of similar code segments. To apply general coding guidelines, please configure review instructions. You can manage existing learnings in the app.

@Dracks Dracks merged commit 63dee13 into mr-scrooge-3.0.0 Dec 4, 2024
0 of 4 checks passed
@Dracks Dracks deleted the 3.0.0/rule-engine branch December 4, 2024 17:24
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.

1 participant