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

feat(ci,server,worker): implement schemata and items copy #1331

Merged
merged 35 commits into from
Jan 15, 2025

Conversation

nourbalaha
Copy link
Contributor

@nourbalaha nourbalaha commented Dec 3, 2024

Overview

This PR implements schemata and items copy

Summary by CodeRabbit

Release Notes

  • New Features

    • Added model copy functionality, allowing users to duplicate models with their associated schemas and items.
    • Introduced new API endpoint /models/{modelId}/copy for model duplication.
    • Implemented task runner support for copying collections across different contexts.
    • Added a new GitHub Actions workflow for building and pushing Docker images.
    • Introduced a new CopyController for handling copy operations via HTTP.
  • Improvements

    • Enhanced JSON serialization with consistent camelCase naming conventions.
    • Improved repository management with a more flexible container-based architecture.
    • Added robust error handling for copy operations.
  • Technical Updates

    • Updated GitHub Actions workflows for automated Docker image builds.
    • Refactored infrastructure code to support more modular repository management.
    • Established a new organizational structure for repositories with a dedicated Container for managing related functionalities.
  • Bug Fixes

    • Resolved potential issues with model and schema copying processes.

@nourbalaha nourbalaha self-assigned this Dec 3, 2024
Copy link
Contributor

coderabbitai bot commented Dec 3, 2024

Walkthrough

This pull request introduces a comprehensive feature for copying models in the ReEarth CMS system. The implementation spans multiple components, including server-side integration API, worker processes, and infrastructure layers. The changes enable users to duplicate models along with their schemas, items, and metadata while also adding new GitHub Actions workflows to support the build and deployment of the copier service. The feature introduces new endpoints, controllers, repositories, and test cases to ensure robust functionality across the system.

Changes

File Change Summary
.github/workflows/build_copier.yml New workflow for building and pushing Docker image for ReEarth CMS copier
.github/workflows/build_worker.yml Added docker_copier job for building copier Docker image
server/e2e/integration_model_test.go Added integration test for model copy functionality
server/internal/adapter/integration/model.go Implemented CopyModel method for model copying
server/internal/adapter/integration/server.gen.go Added CopyModel method to ServerInterface and its wrapper
server/internal/infrastructure/gcp/config.go Added CopierImage field to TaskConfig struct
server/internal/infrastructure/gcp/taskrunner.go Introduced copy method for handling copy tasks
server/internal/usecase/interactor/model.go Added comprehensive model copying logic with helper methods
worker/cmd/copier/main.go Implemented main entry point for copier service
worker/internal/adapter/http/copy.go Added copy controller for handling copy operations
worker/internal/infrastructure/mongo/copier.go Implemented MongoDB copier repository
worker/internal/usecase/repo/copier.go Defined Copier interface for copying operations
server/pkg/integrationapi/types.gen.go Added new request body structures for model copying functionality
worker/internal/usecase/interactor/usecase.go Updated Usecase struct to manage repositories instead of webhook
worker/internal/usecase/interactor/copier.go Added Copy method to Usecase for invoking copier functionality
worker/internal/infrastructure/mongo/copier_test.go Added tests for Copier functionality
worker/pkg/task/task.go Introduced Copy field in Payload for copy operations
worker/schemas/integration.yml Added new endpoint for copying models in OpenAPI specification

Sequence Diagram

sequenceDiagram
    participant Client
    participant ModelController
    participant ModelInteractor
    participant TaskRunner
    participant CopierWorker
    participant MongoDB

    Client->>ModelController: POST /models/{modelId}/copy
    ModelController->>ModelInteractor: Copy Model
    ModelInteractor->>ModelInteractor: Copy Schema
    ModelInteractor->>ModelInteractor: Copy Items
    ModelInteractor->>TaskRunner: Trigger Copy Event
    TaskRunner->>CopierWorker: Dispatch Copy Task
    CopierWorker->>MongoDB: Retrieve Documents
    CopierWorker->>MongoDB: Copy Documents
    MongoDB-->>CopierWorker: Confirm Copy
    CopierWorker-->>TaskRunner: Task Complete
    TaskRunner-->>ModelInteractor: Copy Successful
    ModelInteractor-->>ModelController: Return Copied Model
    ModelController-->>Client: 200 OK with Copied Model
Loading

Poem

🐰 Hop, hop, copy with glee!
A model's journey, now set free
From schema to item, with grace we clone
Our CMS magic has newly grown
Copying dreams, one click at a time 🚀

Finishing Touches

  • 📝 Generate Docstrings (Beta)

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 generate docstrings to generate docstrings for this PR. (Beta)
  • @coderabbitai resolve resolve all the CodeRabbit review comments.
  • @coderabbitai configuration to show the current CodeRabbit configuration for the repository.
  • @coderabbitai help to get help.

Other keywords and placeholders

  • Add @coderabbitai ignore anywhere in the PR description to prevent this PR from being reviewed.
  • Add @coderabbitai summary to generate the high-level summary at a specific location in the PR description.
  • Add @coderabbitai anywhere in the PR title to generate the title automatically.

CodeRabbit Configuration File (.coderabbit.yaml)

  • You can programmatically configure CodeRabbit by adding a .coderabbit.yaml file to the root of your repository.
  • Please see the configuration documentation for more information.
  • If your editor has YAML language server enabled, you can add the path at the top of this file to enable auto-completion and validation: # yaml-language-server: $schema=https://coderabbit.ai/integrations/schema.v2.json

Documentation and Community

  • Visit our Documentation 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.

@github-actions github-actions bot added the server label Dec 3, 2024
Copy link

netlify bot commented Dec 3, 2024

Deploy Preview for reearth-cms canceled.

Name Link
🔨 Latest commit ce70e17
🔍 Latest deploy log https://app.netlify.com/sites/reearth-cms/deploys/678751c10a318d0008c2b052

@nourbalaha nourbalaha changed the title feat(server): implement schemata and items copy feat(server, worker): implement schemata and items copy Dec 10, 2024
@nourbalaha nourbalaha changed the title feat(server, worker): implement schemata and items copy feat(server,worker): implement schemata and items copy Dec 10, 2024
@nourbalaha nourbalaha changed the title feat(server,worker): implement schemata and items copy feat(ci,server,worker): implement schemata and items copy Dec 11, 2024
Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 5

♻️ Duplicate comments (2)
server/internal/infrastructure/gcp/taskrunner.go (2)

167-174: 🛠️ Refactor suggestion

Extract hardcoded build arguments to configuration

Similar to the decompressAsset function, these build arguments should be configurable.

+type CopierConfig struct {
+    ThreadCount    string
+    GCLimit       string
+    ChunkSize     string
+    DiskLimit     string
+    SkipTop       bool
+    OldWindows    bool
+}

 type TaskConfig struct {
     // ... existing fields ...
+    CopierConfig *CopierConfig
 }

-Args: []string{
-    "-v",
-    "-n=192",
-    "-gc=5000",
-    "-chunk=1m",
-    "-disk-limit=20g",
-    "-skip-top",
-    "-old-windows",
-},
+args := []string{"-v"}
+if conf.CopierConfig != nil {
+    if conf.CopierConfig.ThreadCount != "" {
+        args = append(args, "-n="+conf.CopierConfig.ThreadCount)
+    }
+    // ... add other configurable arguments
+}

176-178: ⚠️ Potential issue

Add input validation for environment variables

The Copy payload fields are used directly in environment variables without validation. This could lead to injection vulnerabilities.

+func sanitizeEnvValue(value string) string {
+    // Remove any characters that could cause issues in environment variables
+    // This is a basic example - implement proper sanitization based on your requirements
+    return strings.Map(func(r rune) rune {
+        if unicode.IsLetter(r) || unicode.IsNumber(r) || r == '-' || r == '_' || r == '.' {
+            return r
+        }
+        return -1
+    }, value)
+}

 Env: []string{
-    "REEARTH_CMS_COPIER_COLLECTION=" + p.Copy.Collection,
-    "REEARTH_CMS_COPIER_FILTER=" + p.Copy.Filter,
-    "REEARTH_CMS_COPIER_CHANGES=" + p.Copy.Changes,
+    "REEARTH_CMS_COPIER_COLLECTION=" + sanitizeEnvValue(p.Copy.Collection),
+    "REEARTH_CMS_COPIER_FILTER=" + sanitizeEnvValue(p.Copy.Filter),
+    "REEARTH_CMS_COPIER_CHANGES=" + sanitizeEnvValue(p.Copy.Changes),
 },
🧹 Nitpick comments (5)
server/internal/infrastructure/gcp/taskrunner.go (1)

182-184: Consider making disk size configurable for copy operations

The copy operation uses a hardcoded defaultDiskSizeGb value. Consider making this configurable similar to the decompressor.

 Options: &cloudbuild.BuildOptions{
-    DiskSizeGb: defaultDiskSizeGb,
+    DiskSizeGb: conf.CopierDiskSizeGb,
 },
server/pkg/task/task.go (2)

64-68: Add documentation for Changes and Change types

The types lack documentation explaining their purpose and usage.

Add documentation:

+// Changes represents a map of field changes to be applied during copy operations
 type Changes map[string]Change
+
+// Change represents a single field modification with its type and new value
 type Change struct {
 	Type  ChangeType
 	Value string
 }

71-74: Consider adding validation for ChangeType values

The ChangeType constants should be validated when used.

Add validation:

 const (
 	ChangeTypeSet ChangeType = "set"
 	ChangeTypeNew ChangeType = "new"
 )
+
+// IsValid checks if the ChangeType is a valid value
+func (ct ChangeType) IsValid() bool {
+	switch ct {
+	case ChangeTypeSet, ChangeTypeNew:
+		return true
+	default:
+		return false
+	}
+}
.github/workflows/build_copier.yml (1)

7-10: Consider adding timeout to concurrency configuration

The concurrency configuration lacks a timeout, which could lead to stuck workflows.

Add timeout:

 concurrency:
   group: ${{ github.workflow }}-${{ github.event.workflow_run.head_branch }}
   cancel-in-progress: true
+  timeout-minutes: 60
server/internal/usecase/interactor/model.go (1)

415-439: Add retry mechanism for copyItems

The copyItems method should handle transient failures during the copy operation.

Add retry logic:

 func (i Model) copyItems(ctx context.Context, oldSchemaID, newSchemaID id.SchemaID, newModelID id.ModelID) error {
+	maxRetries := 3
+	var lastErr error
+	
+	for attempt := 1; attempt <= maxRetries; attempt++ {
 		collection := "item"
 		filter, err := json.Marshal(bson.M{"schema": oldSchemaID.String()})
 		if err != nil {
 			return err
 		}
 		changes, err := json.Marshal(task.Changes{
 			"id": {
 				Type:  task.ChangeTypeNew,
 				Value: "item",
 			},
 			"schema": {
 				Type:  task.ChangeTypeSet,
 				Value: newSchemaID.String(),
 			},
 			"model": {
 				Type:  task.ChangeTypeSet,
 				Value: newModelID.String(),
 			},
 		})
 		if err != nil {
 			return err
 		}
-		return i.triggerCopyEvent(ctx, collection, string(filter), string(changes))
+		if err := i.triggerCopyEvent(ctx, collection, string(filter), string(changes)); err != nil {
+			lastErr = err
+			log.Warnf("attempt %d failed: %v", attempt, err)
+			continue
+		}
+		return nil
+	}
+	return fmt.Errorf("failed after %d attempts: %v", maxRetries, lastErr)
 }
📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between d2f3636 and e80cf07.

📒 Files selected for processing (4)
  • .github/workflows/build_copier.yml (1 hunks)
  • server/internal/infrastructure/gcp/taskrunner.go (4 hunks)
  • server/internal/usecase/interactor/model.go (3 hunks)
  • server/pkg/task/task.go (2 hunks)
🧰 Additional context used
🪛 actionlint (1.7.4)
.github/workflows/build_copier.yml

31-31: shellcheck reported issue in this script: SC2236:style:4:9: Use -n instead of ! -z

(shellcheck)


78-78: shellcheck reported issue in this script: SC2129:style:15:1: Consider using { cmd1; cmd2; } >> file instead of individual redirects

(shellcheck)

🔇 Additional comments (3)
server/internal/infrastructure/gcp/taskrunner.go (1)

119-120: 🛠️ Refactor suggestion

Extract hardcoded build step arguments to configuration

The build step arguments contain multiple hardcoded values that should be configurable. This makes the application less flexible and harder to maintain.

+type DecompressorConfig struct {
+    ThreadCount    string
+    GCLimit       string
+    ChunkSize     string
+    DiskLimit     string
+    SkipTop       bool
+    OldWindows    bool
+}

 type TaskConfig struct {
     // ... existing fields ...
+    DecompressorConfig *DecompressorConfig
 }

-Args: []string{"-v", "-n=192", "-gc=5000", "-chunk=1m", "-disk-limit=20g", "-gzip-ext=" + conf.DecompressorGzipExt, "-skip-top", "-old-windows", src, dest},
+args := []string{"-v"}
+if conf.DecompressorConfig != nil {
+    if conf.DecompressorConfig.ThreadCount != "" {
+        args = append(args, "-n="+conf.DecompressorConfig.ThreadCount)
+    }
+    // ... add other configurable arguments
+}
+args = append(args, "-gzip-ext="+conf.DecompressorGzipExt, src, dest)

Likely invalid or redundant comment.

server/pkg/task/task.go (1)

48-52: 🛠️ Refactor suggestion

Consider enhancing CopyPayload validation

The CopyPayload struct contains critical fields for copy operations but lacks comprehensive validation.

Consider adding field-level validation:

 type CopyPayload struct {
 	Collection string
 	Filter     string
 	Changes    string
+
+	// Validate checks if the payload fields are valid
+	func (p *CopyPayload) Validate() error {
+		if p == nil {
+			return errors.New("payload cannot be nil")
+		}
+		if p.Collection == "" {
+			return errors.New("collection is required")
+		}
+		if p.Filter == "" {
+			return errors.New("filter is required")
+		}
+		if !json.Valid([]byte(p.Changes)) {
+			return errors.New("changes must be valid JSON")
+		}
+		return nil
+	}
 }

Likely invalid or redundant comment.

.github/workflows/build_copier.yml (1)

97-106: 🛠️ Refactor suggestion

Add resource limits to Docker build step

The Docker build step lacks resource constraints, which could impact CI/CD performance.

Add resource limits:

       uses: docker/build-push-action@v6
       with:
         context: ./worker
         file: ./worker/copier.Dockerfile
         platforms: ${{ steps.options.outputs.platforms }}
         push: true
         build-args: VERSION=${{ steps.options.outputs.version }}
         tags: ${{ steps.options.outputs.tags }}
         cache-from: type=gha
         cache-to: type=gha,mode=max
+        timeout-minutes: 60
+        memory: 6G
+        cpu-limit: 2

Likely invalid or redundant comment.

server/internal/usecase/interactor/model.go Outdated Show resolved Hide resolved
server/internal/usecase/interactor/model.go Show resolved Hide resolved
server/internal/usecase/interactor/model.go Show resolved Hide resolved
@nourbalaha nourbalaha force-pushed the feat-server/schemata-item-copy branch from 8fbdb8a to e80cf07 Compare January 6, 2025 13:39
Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 0

🧹 Nitpick comments (5)
server/internal/usecase/interactor/model_test.go (3)

540-575: LGTM! Consider adding test cleanup.

The test setup is thorough and follows best practices. However, consider adding cleanup of test data after each test case.

 func TestModel_Copy(t *testing.T) {
+    t.Cleanup(func() {
+        ctx := context.Background()
+        _ = db.Model.DeleteAll(ctx)
+        _ = db.Schema.DeleteAll(ctx)
+        _ = db.Project.DeleteAll(ctx)
+    })

576-629: Add test cases for additional scenarios.

The current test cases cover basic scenarios. Consider adding test cases for:

  1. Invalid project permissions
  2. Schema copy failures
  3. Metadata schema copy failures

Example test case:

+        {
+            name: "invalid project permissions",
+            param: interfaces.CopyModelParam{
+                ModelId: m.ID(),
+                Name:    lo.ToPtr("Copied Model"),
+            },
+            setupMock: func() {
+                mRunner.EXPECT().Run(ctx, gomock.Any()).Times(0)
+            },
+            wantErr: true,
+            validate: func(t *testing.T, got *model.Model) {
+                assert.Nil(t, got)
+            },
+            operator: &usecase.Operator{}, // operator without project permissions
+        },

631-644: Enhance test execution with parallel runs and error validation.

Consider these improvements:

  1. Run test cases in parallel for faster execution
  2. Validate error messages for better test coverage
 for _, tt := range tests {
+    tt := tt
     t.Run(tt.name, func(t *testing.T) {
+        t.Parallel()
         tt.setupMock()
         got, err := u.Copy(ctx, tt.param, op)
         if tt.wantErr {
             assert.Error(t, err)
+            if tt.expectedError != nil {
+                assert.ErrorIs(t, err, tt.expectedError)
+            }
             tt.validate(t, nil)
         } else {
server/e2e/integration_model_test.go (2)

79-121: Add tests for error responses and additional parameters.

While the happy path is well tested, consider adding:

  1. Tests for invalid request parameters
  2. Validation of error response structure
  3. Tests for additional request parameters (e.g., description, key)

Example test case:

+    // Test invalid request
+    e.POST(endpoint, oldModelId).
+        WithHeader("authorization", "Bearer "+secret).
+        WithJSON(map[string]interface{}{
+            "name": "", // Invalid empty name
+        }).
+        Expect().
+        Status(http.StatusBadRequest).
+        JSON().
+        Object().
+        HasValue("error", "invalid_request").
+        HasValue("message", "name cannot be empty")

122-140: Enhance schema validation coverage.

Consider adding more detailed schema validation:

  1. Validate individual field properties
  2. Check field types and configurations
  3. Verify schema metadata and settings

Example validation:

     copiedSchema.Value("fields").Array().Length().IsEqual(oldSchema.Value("fields").Array().Length().Raw())
+    // Validate first field properties
+    firstField := copiedSchema.Value("fields").Array().First().Object()
+    firstField.HasValue("type", "text")
+    firstField.HasValue("required", true)
+    firstField.Value("title").NotNull()
+    firstField.Value("key").NotNull()
📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between e80cf07 and 31914d9.

📒 Files selected for processing (5)
  • server/e2e/integration_model_test.go (1 hunks)
  • server/internal/usecase/interactor/model_test.go (2 hunks)
  • server/internal/usecase/interfaces/model.go (2 hunks)
  • server/pkg/schema/schema_test.go (1 hunks)
  • worker/cmd/copier/main.go (1 hunks)
🚧 Files skipped from review as they are similar to previous changes (3)
  • server/internal/usecase/interfaces/model.go
  • server/pkg/schema/schema_test.go
  • worker/cmd/copier/main.go
🔇 Additional comments (1)
server/e2e/integration_model_test.go (1)

60-78: LGTM! Authorization tests are comprehensive.

The authorization tests cover all necessary scenarios and follow the established pattern.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 4

♻️ Duplicate comments (4)
worker/internal/adapter/http/copy.go (1)

28-38: ⚠️ Potential issue

Add input validation and improve error handling

The current implementation lacks proper input validation and error handling, which could lead to security vulnerabilities.

  1. Add input validation:
 func (c *CopyController) Copy(ctx context.Context, input CopyInput) error {
+    if len(input.Filter) == 0 || len(input.Changes) == 0 {
+        return rerror.ErrInvalidRequestBy(fmt.Errorf("filter and changes cannot be empty"))
+    }
+    if len(input.Filter) > 1024*1024 || len(input.Changes) > 1024*1024 {
+        return rerror.ErrInvalidRequestBy(fmt.Errorf("input size exceeds limit"))
+    }
     var filter bson.M
     if err := json.Unmarshal([]byte(input.Filter), &filter); err != nil {
-        return rerror.ErrInternalBy(err)
+        return rerror.ErrInvalidRequestBy(fmt.Errorf("invalid filter format: %w", err))
     }
     var changes task.Changes
     if err := json.Unmarshal([]byte(input.Changes), &changes); err != nil {
-        return rerror.ErrInternalBy(err)
+        return rerror.ErrInvalidRequestBy(fmt.Errorf("invalid changes format: %w", err))
     }
+    if err := validateMongoFilter(filter); err != nil {
+        return rerror.ErrInvalidRequestBy(fmt.Errorf("invalid filter: %w", err))
+    }
     return c.usecase.Copy(ctx, filter, changes)
 }
worker/internal/infrastructure/mongo/copier_test.go (1)

25-61: 🛠️ Refactor suggestion

Enhance test coverage

The current test coverage is insufficient. Consider adding the following test cases:

  1. Error scenarios (invalid filter, changes)
  2. Verification of copied documents
  3. Edge cases (empty filter, null values)

Example additional test cases:

t.Run("Copy_InvalidFilter", func(t *testing.T) {
    err := w.Copy(ctx, bson.M{"$invalid": 1}, changes)
    assert.Error(t, err)
})

t.Run("Copy_VerifyCopiedDocs", func(t *testing.T) {
    err := w.Copy(ctx, filter, changes)
    assert.NoError(t, err)
    
    // Verify copied documents
    var copied []bson.M
    cursor, err := iCol.Find(ctx, bson.M{"schema": sid2.String()})
    assert.NoError(t, err)
    err = cursor.All(ctx, &copied)
    assert.NoError(t, err)
    assert.Len(t, copied, 2)
})
server/internal/usecase/interactor/model.go (2)

322-353: ⚠️ Potential issue

Add transaction rollback mechanism

The Copy method should handle cleanup if any step fails during the copy process.

Previous suggestion about implementing rollback logic is still valid. This is crucial for maintaining data consistency in case of failures during the copy process.


445-463: ⚠️ Potential issue

Enhance error handling and add timeout

The method needs better error handling and timeout management.

Previous suggestions about adding context timeout and validating taskPayload are still valid. Additionally:

 	if err := i.gateways.TaskRunner.Run(ctx, taskPayload.Payload()); err != nil {
-		return fmt.Errorf("failed to trigger copy event: %w", err)
+		return fmt.Errorf("failed to trigger copy event for collection %s: %w", collection, err)
 	}
🧹 Nitpick comments (9)
worker/internal/adapter/http/copy.go (1)

23-26: Add JSON validation tags to input struct

Consider adding validation tags to enforce input constraints at the struct level.

 type CopyInput struct {
-    Filter  string `json:"filter"`
-    Changes string `json:"changes"`
+    Filter  string `json:"filter" validate:"required,max=1048576"`
+    Changes string `json:"changes" validate:"required,max=1048576"`
 }
worker/internal/usecase/repo/copier.go (1)

10-12: Add interface documentation

The Copier interface would benefit from documentation explaining its purpose and the expected behavior of the Copy method.

+// Copier defines the interface for copying documents based on a filter and applying specified changes
 type Copier interface {
+    // Copy applies the specified changes to documents matching the filter
+    // Returns an error if the operation fails
     Copy(context.Context, bson.M, task.Changes) error
 }
worker/internal/usecase/interactor/copier.go (1)

10-12: Improve error handling

Consider wrapping the repository error with context to help with debugging and error tracking.

 func (u *Usecase) Copy(ctx context.Context, filter bson.M, changes task.Changes) error {
-    return u.repos.Copier.Copy(ctx, filter, changes)
+    if err := u.repos.Copier.Copy(ctx, filter, changes); err != nil {
+        return fmt.Errorf("failed to copy documents: %w", err)
+    }
+    return nil
 }
worker/internal/infrastructure/mongo/copier_test.go (2)

41-41: Remove debug print statement

The fmt.Print statement appears to be used for debugging and should be removed.

-    fmt.Print(res)

16-23: Add cleanup between tests

Add cleanup to prevent test data from affecting other tests.

 func TestCopier(t *testing.T) {
     db := mongotest.Connect(t)(t)
+    defer db.Drop(context.Background())
     w := NewCopier(db)
     w.SetCollection(db.Collection("item"))
worker/internal/infrastructure/mongo/copier.go (1)

16-17: Move batch size to configuration.

The TODO comment indicates that the batch size should be configurable. Consider moving this to the application's configuration system for better maintainability.

-// TODO: this should be taken from config
-var batchSize int32 = 1000
+type Config struct {
+    BatchSize int32 `env:"BATCH_SIZE" envDefault:"1000"`
+}
server/e2e/integration_model_test.go (1)

60-143: Add edge case tests for model copying.

The test suite should include additional cases:

  1. Copying with invalid name/key formats
  2. Copying non-existent models
  3. Copying with duplicate keys

Add these test cases:

// Test invalid name
e.POST(endpoint, oldModelId).
    WithHeader("authorization", "Bearer "+secret).
    WithJSON(map[string]interface{}{
        "name": "",
    }).
    Expect().
    Status(http.StatusBadRequest)

// Test invalid key
e.POST(endpoint, oldModelId).
    WithHeader("authorization", "Bearer "+secret).
    WithJSON(map[string]interface{}{
        "key": "invalid key",
    }).
    Expect().
    Status(http.StatusBadRequest)

// Test non-existent model
e.POST(endpoint, id.NewModelID()).
    WithHeader("authorization", "Bearer "+secret).
    WithJSON(map[string]interface{}{
        "name": "new name",
    }).
    Expect().
    Status(http.StatusNotFound)
server/schemas/integration.yml (1)

452-486: Enhance the endpoint documentation.

While the endpoint definition is well-structured, consider adding more detailed documentation to help API consumers:

  • Describe what gets copied (schema, items, metadata)
  • Explain the purpose of the optional key field
  • Document any constraints on the name field
  • Specify if the copy operation is asynchronous or synchronous
 '/models/{modelId}/copy':
   parameters:
     - $ref: '#/components/parameters/modelIdParam'
   post:
     operationId: CopyModel
-    summary: Copy schema and items of a selected model
+    summary: Create a copy of an existing model
+    description: |
+      Creates a new model by copying the schema and items from an existing model.
+      The operation copies:
+      - Model schema
+      - Model items
+      - Model metadata
+      
+      The name field is required and must be unique within the project.
+      The key field is optional and must be unique if provided.
+      
+      This is a synchronous operation that returns the newly created model.
     tags:
       - Models
server/internal/usecase/interactor/model.go (1)

355-379: Enhance model copy implementation

A few suggestions for improvement:

  1. Consider making the "Copy" suffix configurable or localizable.
  2. Add explicit project access validation before proceeding with the copy operation.
 func (i Model) copyModel(ctx context.Context, params interfaces.CopyModelParam, operator *usecase.Operator) (*model.Model, *model.Model, error) {
 	oldModel, err := i.repos.Model.FindByID(ctx, params.ModelId)
 	if err != nil {
 		return nil, nil, err
 	}
+	if !operator.IsMaintainingProject(oldModel.Project()) {
+		return nil, nil, interfaces.ErrOperationDenied
+	}
-	name := lo.ToPtr(oldModel.Name() + " Copy")
+	name := lo.ToPtr(oldModel.Name() + i18n.T(" Copy"))
📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 31914d9 and 43e3756.

📒 Files selected for processing (12)
  • server/e2e/integration_model_test.go (1 hunks)
  • server/internal/adapter/integration/model.go (1 hunks)
  • server/internal/adapter/integration/server.gen.go (7 hunks)
  • server/internal/usecase/interactor/model.go (3 hunks)
  • server/internal/usecase/interfaces/model.go (2 hunks)
  • server/pkg/integrationapi/types.gen.go (3 hunks)
  • server/schemas/integration.yml (2 hunks)
  • worker/internal/adapter/http/copy.go (1 hunks)
  • worker/internal/infrastructure/mongo/copier.go (1 hunks)
  • worker/internal/infrastructure/mongo/copier_test.go (1 hunks)
  • worker/internal/usecase/interactor/copier.go (1 hunks)
  • worker/internal/usecase/repo/copier.go (1 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
  • server/pkg/integrationapi/types.gen.go
🔇 Additional comments (6)
worker/internal/infrastructure/mongo/copier.go (1)

37-84: ⚠️ Potential issue

Add error handling for bulk write failures.

The bulk write operation doesn't handle partial failures. MongoDB's BulkWrite can fail for individual documents while succeeding for others.

 if len(bulkModels) > 0 {
-    _, err = r.c.BulkWrite(ctx, bulkModels)
+    result, err := r.c.BulkWrite(ctx, bulkModels, options.BulkWrite().SetOrdered(false))
     if err != nil {
+        if bulkErr, ok := err.(mongo.BulkWriteException); ok {
+            log.Errorf("partial bulk write failure: %d/%d operations failed",
+                len(bulkErr.WriteErrors), len(bulkModels))
+            return fmt.Errorf("partial bulk write failure: %w", err)
+        }
         return rerror.ErrInternalBy(err)
     }
+    if result.InsertedCount != int64(len(bulkModels)) {
+        return fmt.Errorf("expected to insert %d documents, but inserted %d",
+            len(bulkModels), result.InsertedCount)
+    }
 }

Likely invalid or redundant comment.

server/internal/adapter/integration/model.go (1)

115-145: 🛠️ Refactor suggestion

Improve error handling and add request validation.

The handler should validate the request body and provide more specific error responses.

 func (s *Server) CopyModel(ctx context.Context, request CopyModelRequestObject) (CopyModelResponseObject, error) {
     uc := adapter.Usecases(ctx)
     op := adapter.Operator(ctx)

+    if request.Body.Name != nil && *request.Body.Name == "" {
+        return CopyModel400Response{}, rerror.NewE(rerror.ErrInvalidParams, "name cannot be empty")
+    }
+
+    if request.Body.Key != nil && !id.NewKey(*request.Body.Key).IsValid() {
+        return CopyModel400Response{}, rerror.NewE(rerror.ErrInvalidParams, "invalid key format")
+    }

     m, err := uc.Model.Copy(ctx, interfaces.CopyModelParam{

Likely invalid or redundant comment.

server/schemas/integration.yml (1)

1861-1861: LGTM! Property rename follows JSON conventions.

The rename from TitleField to titleField follows camelCase convention which is more idiomatic in JSON/OpenAPI specifications.

server/internal/adapter/integration/server.gen.go (1)

78-80: LGTM! Generated code matches the API specification.

The generated code for the new CopyModel endpoint is consistent with other endpoints and correctly implements:

  • Interface method
  • Request/response types
  • Error handling
  • Authentication
  • Parameter binding

Also applies to: 513-529, 2278-2324, 3814-3816, 4365-4394

server/internal/usecase/interactor/model.go (2)

419-443: Improve items copy configuration and validation

The method could be enhanced in several ways:

  1. Make the collection name configurable.
  2. Validate the task payload before triggering the copy event.

Previous suggestion about validating taskPayload is still valid. Additionally:

-	collection := "item"
+	collection := i.config.Collections.Item // Add configuration for collection names

381-392: Add schema compatibility validation

The schema copy operation should validate compatibility and handle potential errors from the CopyFrom operation.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 0

♻️ Duplicate comments (4)
worker/internal/infrastructure/mongo/copier_test.go (1)

23-59: 🛠️ Refactor suggestion

Enhance test coverage and robustness.

The test needs significant improvements:

  1. Missing verification of the copied items.
  2. No cleanup of test resources.
  3. No error cases tested.
  4. Not running in parallel.

Here's a suggested improvement:

 func TestCopier_Copy(t *testing.T) {
+	t.Parallel()
 	ctx := context.Background()
 	db := mongotest.Connect(t)(t)
+	defer db.Drop(ctx)
 	w := NewCopier(db)
 	iCol := db.Collection("item")
 	w.SetCollection(iCol)

-	mid1 := id.NewModelID()
-	sid1 := id.NewSchemaID()
-	mid2 := id.NewModelID()
-	sid2 := id.NewSchemaID()
-	i1 := item.New().ID(id.NewItemID()).Schema(sid1).Model(mid1).Project(id.NewProjectID()).Thread(id.NewThreadID()).MustBuild()
-	i2 := item.New().ID(id.NewItemID()).Schema(sid1).Model(mid1).Project(id.NewProjectID()).Thread(id.NewThreadID()).MustBuild()
-
-	res, err := iCol.InsertMany(ctx, []any{i1, i2})
-	assert.NoError(t, err)
-	fmt.Print(res)
-
-	filter := bson.M{"schema": sid1.String()}
-	changes := task.Changes{
-		"id": {
-			Type:  task.ChangeTypeNew,
-			Value: "item",
-		},
-		"schema": {
-			Type:  task.ChangeTypeSet,
-			Value: sid2.String(),
-		},
-		"model": {
-			Type:  task.ChangeTypeSet,
-			Value: mid2.String(),
-		},
-	}
-
-	err = w.Copy(ctx, filter, changes)
-	assert.NoError(t, err)
+	tests := []struct {
+		name    string
+		setup   func(t *testing.T) (filter bson.M, changes task.Changes)
+		wantErr bool
+	}{
+		{
+			name: "success",
+			setup: func(t *testing.T) (bson.M, task.Changes) {
+				mid1 := id.NewModelID()
+				sid1 := id.NewSchemaID()
+				mid2 := id.NewModelID()
+				sid2 := id.NewSchemaID()
+				i1 := item.New().ID(id.NewItemID()).Schema(sid1).Model(mid1).Project(id.NewProjectID()).Thread(id.NewThreadID()).MustBuild()
+				i2 := item.New().ID(id.NewItemID()).Schema(sid1).Model(mid1).Project(id.NewProjectID()).Thread(id.NewThreadID()).MustBuild()
+
+				_, err := iCol.InsertMany(ctx, []any{i1, i2})
+				assert.NoError(t, err)
+
+				return bson.M{"schema": sid1.String()}, task.Changes{
+					"id":     {Type: task.ChangeTypeNew, Value: "item"},
+					"schema": {Type: task.ChangeTypeSet, Value: sid2.String()},
+					"model":  {Type: task.ChangeTypeSet, Value: mid2.String()},
+				}
+			},
+			wantErr: false,
+		},
+		{
+			name: "invalid_filter",
+			setup: func(t *testing.T) (bson.M, task.Changes) {
+				return bson.M{"invalid": "filter"}, task.Changes{}
+			},
+			wantErr: true,
+		},
+		{
+			name: "invalid_changes",
+			setup: func(t *testing.T) (bson.M, task.Changes) {
+				return bson.M{}, task.Changes{
+					"invalid": {Type: "invalid", Value: nil},
+				}
+			},
+			wantErr: true,
+		},
+	}
+
+	for _, tt := range tests {
+		tt := tt
+		t.Run(tt.name, func(t *testing.T) {
+			t.Parallel()
+			filter, changes := tt.setup(t)
+
+			err := w.Copy(ctx, filter, changes)
+			if tt.wantErr {
+				assert.Error(t, err)
+				return
+			}
+			assert.NoError(t, err)
+
+			// Verify copied items
+			var copied []item.Item
+			cursor, err := iCol.Find(ctx, changes)
+			assert.NoError(t, err)
+			err = cursor.All(ctx, &copied)
+			assert.NoError(t, err)
+			assert.NotEmpty(t, copied)
+		})
+	}
 }
server/internal/usecase/interactor/model.go (3)

322-353: 🛠️ Refactor suggestion

Add rollback mechanism for partial failures.

The Copy method should ensure proper cleanup if any step fails to maintain data consistency.

This is a duplicate of a previous review comment. The suggestion to implement rollback logic remains valid.


381-392: 🛠️ Refactor suggestion

Add workspace and project validation.

The method should validate that both schemas belong to the same workspace and project before copying.

This is a duplicate of a previous review comment. The suggestion to validate workspace consistency remains valid.


445-463: 🛠️ Refactor suggestion

Add context timeout and payload validation.

The method needs a timeout to prevent long-running operations and should validate the task payload.

This is a duplicate of previous review comments. The suggestions to add context timeout and validate task payload remain valid.

🧹 Nitpick comments (4)
worker/internal/infrastructure/mongo/copier_test.go (2)

16-21: Add cleanup and error handling.

While the test is functional, it could be improved by:

  1. Adding database cleanup using defer db.Drop(ctx).
  2. Adding error handling for database connection.
 func TestCopier_SetCollection(t *testing.T) {
+	ctx := context.Background()
 	db := mongotest.Connect(t)(t)
+	defer db.Drop(ctx)
+
 	w := NewCopier(db)
 	w.SetCollection(db.Collection("item"))
 	assert.Equal(t, "item", w.c.Name())
 }

39-39: Remove debug print statement.

The fmt.Print(res) statement appears to be left from debugging and should be removed.

server/internal/usecase/interactor/model.go (2)

394-417: Improve error handling for model updates.

Consider wrapping the model and schema saves in a transaction to ensure atomicity.

+	if err := i.repos.Schema.Save(ctx, newMetaSchema); err != nil {
+		return nil, fmt.Errorf("failed to save meta schema: %w", err)
+	}
 	newModel.SetMetadata(newMetaSchema.ID())
-	if err := i.repos.Model.Save(ctx, newModel); err != nil {
-		return nil, err
-	}
-	if err := i.repos.Schema.Save(ctx, newMetaSchema); err != nil {
-		return nil, err
+	if err := i.repos.Model.Save(ctx, newModel); err != nil {
+		return nil, fmt.Errorf("failed to update model with meta schema: %w", err)
 	}

419-443: Improve error handling and add constants.

The method needs better error handling and should use constants for collection names.

+const (
+	itemCollection = "item"
+	idField       = "id"
+	schemaField   = "schema"
+	modelIDField  = "modelid"
+)

 func (i Model) copyItems(ctx context.Context, oldSchemaID, newSchemaID id.SchemaID, newModelID id.ModelID) error {
-	collection := "item"
+	collection := itemCollection
 	filter, err := json.Marshal(bson.M{"schema": oldSchemaID.String()})
 	if err != nil {
-		return err
+		return fmt.Errorf("failed to marshal filter: %w", err)
 	}
📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 43e3756 and 0ac1161.

📒 Files selected for processing (5)
  • server/internal/usecase/interactor/model.go (3 hunks)
  • worker/cmd/copier/main.go (1 hunks)
  • worker/internal/infrastructure/mongo/container.go (1 hunks)
  • worker/internal/infrastructure/mongo/copier.go (1 hunks)
  • worker/internal/infrastructure/mongo/copier_test.go (1 hunks)
🚧 Files skipped from review as they are similar to previous changes (3)
  • worker/internal/infrastructure/mongo/container.go
  • worker/cmd/copier/main.go
  • worker/internal/infrastructure/mongo/copier.go
🔇 Additional comments (2)
worker/internal/infrastructure/mongo/copier_test.go (1)

1-14: LGTM!

The package declaration and imports are well-organized and all imports are being utilized.

server/internal/usecase/interactor/model.go (1)

355-379: Add name uniqueness validation.

The method should validate that the new name doesn't conflict with existing models in the project.

Run this script to check if the codebase has name uniqueness validation:

✅ Verification successful

Add name uniqueness validation.

The codebase has key uniqueness validation but lacks name uniqueness checks. While the copyModel method appends "Copy" to duplicate names, there's no guarantee of uniqueness:

  • Multiple copies could create models with the same name
  • Custom names provided via params.Name aren't validated for uniqueness
🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Search for name uniqueness validation in model-related files
rg -A 5 'func.*Create.*Model' | rg -A 5 'name.*unique|duplicate.*name'

Length of output: 72


Script:

#!/bin/bash
# Search for model validation and uniqueness checks
echo "=== Searching for model validation ==="
rg -g '!*.test.*' -g '!*test*' "func.*Validate.*Model|func.*Check.*Model" -A 5

echo -e "\n=== Searching for name uniqueness checks in repository layer ==="
ast-grep --pattern 'func ($VAR *ModelRepository) Create($ARGS) {
  $$$
}'

echo -e "\n=== Searching for database schema or constraints ==="
fd -e sql . | xargs cat

Length of output: 3138


Script:

#!/bin/bash
# Search for model repository implementation
echo "=== Searching for model repository implementation ==="
fd -t f "model.*repository.*go" -X cat

echo -e "\n=== Searching for model table schema ==="
fd -t f ".*model.*sql" -X cat

echo -e "\n=== Searching for Create method implementation ==="
ast-grep --pattern 'func ($VAR *Model) Create(ctx context.Context, $ARGS) ($RET, error) {
  $$$
}'

Length of output: 504


Script:

#!/bin/bash
echo "=== Searching for repository files ==="
rg -g "*.go" -l "type.*ModelRepository" 

echo -e "\n=== Searching for model creation implementation ==="
rg -g "*.go" -B 5 -A 10 "func.*Create.*Model.*Context"

echo -e "\n=== Searching for database schema files ==="
fd . -e sql -e ddl -e schema

Length of output: 4637

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 1

♻️ Duplicate comments (2)
worker/internal/infrastructure/mongo/container.go (2)

10-22: ⚠️ Potential issue

Add validation for required components

The New function should validate its input parameters to prevent potential nil pointer dereferences later.

Apply this diff to add validation:

 func New(ctx context.Context, webhook *Webhook, copier *Copier) (*repo.Container, error) {
+	if webhook == nil {
+		return nil, fmt.Errorf("webhook is required")
+	}
+	if copier == nil {
+		return nil, fmt.Errorf("copier is required")
+	}
+
 	r := &repo.Container{
 		Webhook: webhook,
 		Copier:  copier,
 	}

24-34: ⚠️ Potential issue

Add type assertion safety checks

The initialization should include type assertions to ensure the correct implementation types are provided.

Apply this diff to add type safety:

 func Init(r *repo.Container) error {
 	if r == nil {
 		return nil
 	}
 
 	if r.Webhook == nil && r.Copier == nil {
 		return errors.New("invalid repository container")
 	}
 
+	if r.Webhook != nil {
+		if _, ok := r.Webhook.(*Webhook); !ok {
+			return fmt.Errorf("invalid webhook implementation type")
+		}
+	}
+
+	if r.Copier != nil {
+		if _, ok := r.Copier.(*Copier); !ok {
+			return fmt.Errorf("invalid copier implementation type")
+		}
+	}
+
 	return nil
 }
🧹 Nitpick comments (1)
worker/internal/infrastructure/mongo/container.go (1)

1-34: Consider enhancing the container for schema and item copy operations

Given that this PR aims to implement schemata and items copy functionality, consider extending the container to include:

  1. Validation of copy operations (e.g., checking for circular dependencies)
  2. Rate limiting or batching for large copy operations
  3. Progress tracking for long-running copy operations
  4. Rollback mechanisms for failed copy operations

This would make the implementation more robust and production-ready.

📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 0ac1161 and cb5d2e2.

📒 Files selected for processing (3)
  • worker/cmd/copier/main.go (1 hunks)
  • worker/internal/app/main.go (4 hunks)
  • worker/internal/infrastructure/mongo/container.go (1 hunks)
🚧 Files skipped from review as they are similar to previous changes (2)
  • worker/cmd/copier/main.go
  • worker/internal/app/main.go
🔇 Additional comments (1)
worker/internal/infrastructure/mongo/container.go (1)

1-9: LGTM!

The package declaration and imports are clean and well-organized.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 1

♻️ Duplicate comments (4)
server/internal/usecase/interactor/model.go (4)

322-353: ⚠️ Potential issue

Implement rollback mechanism for partial failures.

The Copy method performs multiple operations but lacks cleanup mechanisms for partial failures. If an error occurs after creating the model but before copying items, the system could be left in an inconsistent state.

Apply the rollback mechanism as suggested in the previous review:

 func (i Model) Copy(ctx context.Context, params interfaces.CopyModelParam, operator *usecase.Operator) (*model.Model, error) {
 	return Run1(ctx, operator, i.repos, Usecase().Transaction(),
 		func(ctx context.Context) (*model.Model, error) {
+			// Create a cleanup function
+			var cleanup func() error
+			defer func() {
+				if cleanup != nil {
+					if err := cleanup(); err != nil {
+						log.Errorf("failed to cleanup after copy error: %v", err)
+					}
+				}
+			}()
+
 			// copy model
 			oldModel, newModel, err := i.copyModel(ctx, params, operator)
 			if err != nil {
 				return nil, err
 			}
+			cleanup = func() error {
+				return i.repos.Model.Remove(ctx, newModel.ID())
+			}

381-392: 🛠️ Refactor suggestion

Ensure workspace consistency and proper schema copying.

The method should validate workspace consistency and ensure proper deep copying of schema fields.

Apply the workspace validation as suggested in the previous review:

 func (i Model) copySchema(ctx context.Context, oldSchemaId, newSchemaId id.SchemaID) error {
 	oldSchema, err := i.repos.Schema.FindByID(ctx, oldSchemaId)
 	if err != nil {
 		return err
 	}
 	newSchema, err := i.repos.Schema.FindByID(ctx, newSchemaId)
 	if err != nil {
 		return err
 	}
+	if oldSchema.Workspace() != newSchema.Workspace() {
+		return fmt.Errorf("workspace mismatch: cannot copy schema across workspaces")
+	}
 	newSchema.CopyFrom(oldSchema)
 	return i.repos.Schema.Save(ctx, newSchema)
 }

419-443: 🛠️ Refactor suggestion

Validate task payload before execution.

The method should validate the task payload before triggering the copy event.

Apply the validation as suggested in the previous review:

 	taskPayload := task.CopyPayload{
 		Collection: collection,
 		Filter:     filter,
 		Changes:    changes,
 	}
+
+	if err := taskPayload.Validate(); err != nil {
+		return fmt.Errorf("invalid task payload: %w", err)
+	}

445-463: 🛠️ Refactor suggestion

Add context timeout to prevent long-running operations.

The method should have a timeout to prevent long-running copy operations.

Apply the timeout as suggested in the previous review:

 func (i Model) triggerCopyEvent(ctx context.Context, collection, filter, changes string) error {
+	ctx, cancel := context.WithTimeout(ctx, 30*time.Second)
+	defer cancel()
+
 	if i.gateways.TaskRunner == nil {
 		log.Infof("model: copy of %s skipped because task runner is not configured", collection)
 		return nil
 	}
📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 7382546 and 2d38a47.

📒 Files selected for processing (1)
  • server/internal/usecase/interactor/model.go (3 hunks)
⏰ Context from checks skipped due to timeout of 90000ms (1)
  • GitHub Check: ci-server / test
🔇 Additional comments (1)
server/internal/usecase/interactor/model.go (1)

394-417: LGTM with previous suggestions applied.

The implementation looks correct, but ensure to apply the workspace validation and atomic operation handling suggested in the previous review.

server/internal/usecase/interactor/model.go Show resolved Hide resolved
Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 0

♻️ Duplicate comments (1)
worker/internal/infrastructure/mongo/copier_test.go (1)

22-57: 🛠️ Refactor suggestion

Enhance test coverage and add data verification.

The test needs improvement in several areas:

  1. No verification of the copied data
  2. Missing cleanup after test execution
  3. No error cases tested

Apply this diff to improve the test:

 func TestCopier_Copy(t *testing.T) {
 	ctx := context.Background()
 	db := mongotest.Connect(t)(t)
+	defer db.Drop(ctx)
 	w := NewCopier(db)
 	iCol := db.Collection("item")
 	w.SetCollection(iCol)

 	mid1 := id.NewModelID()
 	sid1 := id.NewSchemaID()
 	mid2 := id.NewModelID()
 	sid2 := id.NewSchemaID()
 	i1 := item.New().ID(id.NewItemID()).Schema(sid1).Model(mid1).Project(id.NewProjectID()).Thread(id.NewThreadID()).MustBuild()
 	i2 := item.New().ID(id.NewItemID()).Schema(sid1).Model(mid1).Project(id.NewProjectID()).Thread(id.NewThreadID()).MustBuild()

 	_, err := iCol.InsertMany(ctx, []any{i1, i2})
 	assert.NoError(t, err)

 	filter := bson.M{"schema": sid1.String()}
 	changes := task.Changes{
 		"id": {
 			Type:  task.ChangeTypeNew,
 			Value: "item",
 		},
 		"schema": {
 			Type:  task.ChangeTypeSet,
 			Value: sid2.String(),
 		},
 		"model": {
 			Type:  task.ChangeTypeSet,
 			Value: mid2.String(),
 		},
 	}

 	err = w.Copy(ctx, filter, changes)
 	assert.NoError(t, err)
+
+	// Verify copied data
+	var results []bson.M
+	cursor, err := iCol.Find(ctx, bson.M{"schema": sid2.String()})
+	assert.NoError(t, err)
+	err = cursor.All(ctx, &results)
+	assert.NoError(t, err)
+	assert.Len(t, results, 2)
+	
+	// Test error cases
+	err = w.Copy(ctx, bson.M{"invalid": "filter"}, changes)
+	assert.Error(t, err)
+	
+	invalidChanges := task.Changes{
+		"invalid": {
+			Type:  task.ChangeTypeSet,
+			Value: "value",
+		},
+	}
+	err = w.Copy(ctx, filter, invalidChanges)
+	assert.Error(t, err)
 }
🧹 Nitpick comments (2)
worker/internal/infrastructure/mongo/copier_test.go (2)

15-20: Add cleanup after test execution.

While the test correctly verifies the collection name setting, it should clean up after execution to prevent interference with other tests.

 func TestCopier_SetCollection(t *testing.T) {
 	db := mongotest.Connect(t)(t)
+	defer db.Drop(context.Background())
 	w := NewCopier(db)
 	w.SetCollection(db.Collection("item"))
 	assert.Equal(t, "item", w.c.Name())
 }

59-98: Enhance test cases to verify generated ID values.

The test only verifies if ID generation succeeds but doesn't check the actual generated ID values. Add verification of the generated IDs to ensure they match the expected format for each type.

 func TestCopier_GenerateId(t *testing.T) {
 	tests := []struct {
 		name       string
 		input      string
 		expectOk   bool
+		expectType string
 	}{
 		{
 			name:     "Valid input - item",
 			input:    "item",
 			expectOk: true,
+			expectType: "item",
 		},
 		// ... other test cases ...
 	}

 	for _, tt := range tests {
 		t.Run(tt.name, func(t *testing.T) {
-			_, ok := generateId(tt.input)
+			id, ok := generateId(tt.input)
 			assert.Equal(t, tt.expectOk, ok)
+			if ok {
+				assert.Contains(t, id.String(), tt.expectType)
+			}
 		})
 	}
 }
📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between a798a8b and 90b5277.

📒 Files selected for processing (2)
  • worker/internal/infrastructure/mongo/common_test.go (1 hunks)
  • worker/internal/infrastructure/mongo/copier_test.go (1 hunks)
🔇 Additional comments (1)
worker/internal/infrastructure/mongo/common_test.go (1)

1-7: LGTM! Environment setup is correctly implemented.

The initialization of the test environment follows Go conventions and uses consistent naming.

@nourbalaha nourbalaha force-pushed the feat-server/schemata-item-copy branch 3 times, most recently from 2e0c104 to 90b5277 Compare January 15, 2025 06:11
@mimoham24 mimoham24 enabled auto-merge (squash) January 15, 2025 07:19
@nourbalaha nourbalaha disabled auto-merge January 15, 2025 07:59
@nourbalaha nourbalaha merged commit 61a5e68 into main Jan 15, 2025
22 checks passed
@nourbalaha nourbalaha deleted the feat-server/schemata-item-copy branch January 15, 2025 08:01
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants