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: create deployment #512

Merged
merged 67 commits into from
Oct 24, 2024
Merged

feat: create deployment #512

merged 67 commits into from
Oct 24, 2024

Conversation

KaWaite
Copy link
Member

@KaWaite KaWaite commented Sep 18, 2024

Overview

Add deployment functionality to frontend and extend/fix backend deployment functionality.

What I've done

BE

  • Split local saving of files between assets/ and workflows/
  • Fixed Deployment saving
  • Add update and delete functionality to Deployments
  • Updated GQL to meet frontend needs

What I haven't done

  • Finalize debug run execution on the backend
  • Unable to test deployments successfully run on initiation

How I tested

Screenshot

Which point I want you to review particularly

Memo

Summary by CodeRabbit

Release Notes

  • New Features

    • Added support for YAML file processing with a new dependency.
    • Introduced new hooks and methods to enhance deployment management, including create, update, and delete functionalities.
    • New GraphQL queries and mutations for managing deployments and jobs.
    • Enhanced localization with new deployment-related messages in multiple languages.
    • Added a new method to retrieve deployment data associated with workspaces.
    • Simplified input structures for deployment and project management by consolidating file inputs.
    • Implemented a middleware for handling GraphQL requests with file uploads.
    • Introduced a new function for converting workflows into a GraphQL-compatible format.
  • Bug Fixes

    • Adjusted class name orders for improved visual styling across various components.
  • Documentation

    • Updated type definitions for better clarity and type safety in deployment, job, and workflow functionalities.
  • Chores

    • Integrated coverage checking into the CI pipeline to evaluate code coverage metrics.

Copy link
Contributor

coderabbitai bot commented Sep 18, 2024

Walkthrough

The pull request introduces several modifications across various components of the @reearth/flow-web project. Key changes include the addition of a new dependency for YAML processing in package.json, a shift in the NewRun component from project management to deployment management, and the introduction of new GraphQL queries and types related to deployments and jobs. Localization files have been updated with new entries for deployment-related messages in multiple languages, and TypeScript types have been refined for better clarity and safety.

Changes

File Path Change Summary
ui/package.json Added dependency: "yaml": "2.6.0"; updated "cesium" from "1.121.1" to "1.122.0".
ui/src/features/Runs/components/NewRun.tsx Transitioned from project management to deployment management; updated imports, state variables, and UI elements.
ui/src/lib/gql/deployment/useQueries.ts Introduced hooks and mutations for managing deployment-related GraphQL operations.
ui/src/lib/i18n/locales/*.json Added new entries for deployment-related messages in multiple languages.
ui/src/utils/engineWorkflowYaml/workflowYaml.test.ts Added unit tests for new workflow-related functions.
.github/workflows/ci_ui.yml Added step for coverage checks in CI configuration.
api/gql/deployment.graphql Updated input types and added new mutations for deployment management.
api/gql/project.graphql Updated input types for running projects to simplify the structure.
api/internal/adapter/gql/resolver_mutation_deployment.go Updated CreateDeployment method to remove Meta field and added new methods for updating and deleting deployments.
api/internal/infrastructure/memory/deployment.go Introduced Deployment struct for managing deployment data.
api/internal/usecase/interactor/deployment.go Updated Create, Update, and Delete methods for deployment management.
api/pkg/deployment/deployment.go Removed createdAt field and added setter for updatedAt.
ui/src/lib/gql/deployment/queries.graphql Defined fragments and mutations related to deployments and jobs.
ui/src/lib/gql/deployment/useApi.ts Introduced custom hook useDeployment for deployment operations.
ui/src/lib/gql/project/useApi.ts Added runProject function to execute a project.

Possibly related PRs

  • chore(ui): upgrade deps and fix issues around nodes #337: The changes in this PR involve updating the cesium dependency from "1.121.1" to "1.122.0", which directly relates to the version update mentioned in the main PR.
  • chore(ui): Deps upgrade #526: This PR also updates the cesium package, moving it from "1.120.0" to "1.121.1", which is relevant as it shows ongoing updates to the same dependency.
  • feat(ui): new logo + a little ui tweaks #562: This PR includes modifications to the FlowLogo component, which may indirectly relate to UI changes that could be affected by the dependency updates in the main PR, although it does not directly modify the same files or dependencies.

🐇 In the meadow, changes bloom,
With YAML now, there's more room!
Class names dance, and workflows flow,
Deployments sing, as new types grow.
In every language, joy we share,
A project bright, beyond compare! 🌼


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.

Copy link

netlify bot commented Sep 18, 2024

Deploy Preview for reearth-flow ready!

Name Link
🔨 Latest commit 395fa89
🔍 Latest deploy log https://app.netlify.com/sites/reearth-flow/deploys/6719b9016d3a6c00081d9d36
😎 Deploy Preview https://deploy-preview-512--reearth-flow.netlify.app
📱 Preview on mobile
Toggle QR Code...

QR Code

Use your smartphone camera to open QR code link.

To edit notification comments on pull requests, go to your Netlify site configuration.

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

🧹 Outside diff range and nitpick comments (8)
api/pkg/deployment/builder.go (1)

19-22: Document the timestamp initialization behavior

The automatic initialization of updatedAt to match createdAt should be documented to make the behavior explicit for future maintainers.

 func (b *DeploymentBuilder) Build() (*Deployment, error) {
 	if b.d.id.IsNil() {
 		return nil, ErrInvalidID
 	}
 
+	// Initialize updatedAt to match createdAt if not explicitly set
 	if b.d.updatedAt.IsZero() {
 		b.d.updatedAt = b.d.CreatedAt()
 	}
api/internal/adapter/gql/resolver_mutation_deployment.go (1)

Line range hint 1-64: Consider implementing transaction management for deployment operations.

The mutation resolvers could benefit from transaction management to ensure atomicity of operations, especially in a distributed system. This would prevent partial updates in case of failures during the operation.

For example, if the deployment update fails after partial processing, you might want to roll back any intermediate changes.

Consider wrapping the operations in transactions:

  1. Start transaction before ID conversion
  2. Commit on successful completion
  3. Rollback on any error
api/internal/infrastructure/mongo/deployment.go (2)

31-33: Enhance error handling in Init method

While index creation is good practice, consider wrapping the error with context details for better debugging.

 func (r *Deployment) Init(ctx context.Context) error {
-	return createIndexes(ctx, r.client, deploymentIndexes, deploymentUniqueIndexes)
+	if err := createIndexes(ctx, r.client, deploymentIndexes, deploymentUniqueIndexes); err != nil {
+		return fmt.Errorf("failed to create deployment indexes: %w", err)
+	}
+	return nil

90-95: Consider pre-allocating the workspace ID slice

The implementation is good, but consider pre-allocating the slice when filterByWorkspaces is true to avoid potential reallocation.

-	var f []accountdomain.WorkspaceID
+	var f []accountdomain.WorkspaceID
 	if filterByWorkspaces {
-		f = r.f.Readable
+		f = make([]accountdomain.WorkspaceID, len(r.f.Readable))
+		copy(f, r.f.Readable)
 	}
api/internal/adapter/gql/generated.go (1)

Line range hint 11612-11639: Consider consolidating duplicate file upload unmarshal logic.

The file upload unmarshal logic is duplicated across different input types. Consider extracting this into a reusable helper function to maintain consistency and reduce code duplication.

Example refactor:

func (ec *executionContext) unmarshalFileUpload(ctx context.Context, v interface{}, fieldName string) (*graphql.Upload, error) {
    ctx = graphql.WithPathContext(ctx, graphql.NewPathWithField(fieldName))
    return ec.unmarshalNUpload2githubᚗcomᚋ99designsᚋgqlgenᚋgraphqlᚐUpload(ctx, v)
}

Also applies to: 12219-12246

api/internal/usecase/interactor/deployment.go (3)

135-135: Implement version increment logic

The version is currently hardcoded, and there's a comment indicating that it needs to be incremented eventually.

Would you like assistance in implementing the versioning logic to automatically increment the deployment version?


100-143: Add unit tests for the Update method

The new Update method lacks corresponding unit tests. Implementing tests will ensure that updates to deployments work as expected and help catch potential issues early.

Would you like assistance in creating unit tests for the Update method?


145-178: Add unit tests for the Delete method

The new Delete method does not have accompanying unit tests. Adding tests will verify that deployments are deleted correctly and resources are cleaned up properly.

Would you like assistance in creating unit tests for the Delete method?

📜 Review details

Configuration used: .coderabbit.yaml
Review profile: CHILL

📥 Commits

Files that changed from the base of the PR and between 8bff54e and 0694944.

📒 Files selected for processing (10)
  • api/gql/deployment.graphql (3 hunks)
  • api/internal/adapter/gql/generated.go (28 hunks)
  • api/internal/adapter/gql/gqlmodel/models_gen.go (4 hunks)
  • api/internal/adapter/gql/resolver_mutation_deployment.go (2 hunks)
  • api/internal/infrastructure/mongo/deployment.go (2 hunks)
  • api/internal/infrastructure/mongo/mongodoc/deployment.go (0 hunks)
  • api/internal/usecase/interactor/deployment.go (4 hunks)
  • api/internal/usecase/interfaces/deployment.go (2 hunks)
  • api/pkg/deployment/builder.go (1 hunks)
  • api/pkg/deployment/deployment.go (2 hunks)
🔥 Files not summarized due to errors (1)
  • api/internal/adapter/gql/generated.go: Error: Server error: no LLM provider could handle the message
💤 Files with no reviewable changes (1)
  • api/internal/infrastructure/mongo/mongodoc/deployment.go
🚧 Files skipped from review as they are similar to previous changes (2)
  • api/internal/adapter/gql/gqlmodel/models_gen.go
  • api/internal/usecase/interfaces/deployment.go
🧰 Additional context used
🔇 Additional comments (17)
api/pkg/deployment/deployment.go (1)

37-37: LGTM! Verify ID timestamp implementation.

Using the ID's timestamp as the creation time is a good practice that ensures consistency and reduces redundant storage.

Let's verify the ID implementation:

✅ Verification successful

Let's try a different search strategy to locate the ID type and its implementation, as it might be in a different package or using a different name pattern.

This broader search should help us locate:

  1. Files containing ID type definitions
  2. Files containing Timestamp method implementations
  3. The actual implementation context for both

Let's check the actual implementation of the ID type since we found that it's using id.DeploymentID. We need to look at the base implementation to verify the Timestamp method.

This should help us find:

  1. The actual implementation of the ID types in the id package
  2. Any Timestamp method implementation across the codebase

I notice that the ID types are using the idx package from reearthx, which seems to be an external dependency. Let's check if we can find the Timestamp implementation in any imported packages.

This should help us understand:

  1. Which version of reearthx is being used
  2. How the idx package is imported and used
  3. Any timestamp-related functionality in the codebase

Based on the search results, I can now generate a final response. We've found sufficient evidence about the ID implementation and its usage:

The Timestamp implementation is verified and consistent across the codebase

The search results show that:

  1. The Timestamp() method is consistently used across multiple entities (Project, Asset, Deployment) to get creation time from their IDs
  2. The implementation is provided by the reearthx/idx package, which is a common dependency
  3. The pattern is used consistently in tests and production code
  4. Other entities in the codebase follow the same pattern for getting creation time from IDs

The change to use id.Timestamp() for CreatedAt() is safe and follows an established pattern in the codebase. This is a good practice that ensures consistency between ID and creation time while reducing redundant storage.

🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Description: Verify ID type implements Timestamp correctly
# Expected: Find ID type definition and its Timestamp method

# Search for ID type definition
echo "=== ID Type Definition ==="
ast-grep --pattern 'type ID'

# Search for Timestamp method implementation
echo -e "\n=== Timestamp Method Implementation ==="
ast-grep --pattern 'func ($_ ID) Timestamp() time.Time'

Length of output: 246


Script:

#!/bin/bash
# Let's first find all files that might contain ID-related code
echo "=== Files containing ID type or interface ==="
rg -l "type.*ID" --type go

echo -e "\n=== Files containing Timestamp method ==="
rg -l "Timestamp.*\(\).*time\.Time" --type go

echo -e "\n=== Detailed context for ID and Timestamp implementations ==="
rg "type.*ID|func.*Timestamp.*\(\).*time\.Time" -B 1 -A 2 --type go

Length of output: 8505


Script:

#!/bin/bash
# Let's check the contents of the id package
echo "=== Contents of files in id package ==="
fd -e go -x cat {} \; -x echo -e "\n---\n" \; id.go

echo -e "\n=== Search for Timestamp implementation in any ID-related type ==="
rg -B 2 -A 2 "func.*Timestamp" --type go

Length of output: 9085


Script:

#!/bin/bash
# Check for imports of reearthx package and its usage
echo "=== Check go.mod for reearthx version ==="
cat go.mod

echo -e "\n=== Search for idx package imports ==="
rg "github.com/reearth/reearthx/idx" -B 2 -A 2 --type go

echo -e "\n=== Search for any timestamp-related code ==="
rg -i "timestamp" -B 2 -A 2 --type go

Length of output: 4030

api/pkg/deployment/builder.go (1)

20-22: Verify the existence of CreatedAt() method

The code calls b.d.CreatedAt() but the AI summary indicates the CreatedAt method was removed. Please verify that the Deployment struct still has a CreatedAt() method defined.

✅ Verification successful

Based on the shell script results, I can now generate the final response since I have sufficient information to verify the concern.

CreatedAt() method exists and is properly accessible

The CreatedAt() method is correctly defined in api/pkg/deployment/deployment.go as a receiver method on the Deployment struct, and it returns a time.Time. The usage in builder.go to set updatedAt is valid and will work as expected.

🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Description: Check if CreatedAt() is defined on the Deployment struct
# Expected: Find the CreatedAt() method definition

# Search for CreatedAt method definition in deployment package
ast-grep --pattern 'func (d *Deployment) CreatedAt() time.Time {
  $$$
}'

# Backup: Search for any CreatedAt occurrences
rg -p "CreatedAt" api/pkg/deployment/

Length of output: 442

api/gql/deployment.graphql (4)

41-44: LGTM! Well-structured update mutation input.

The UpdateDeploymentInput type follows GraphQL best practices with clear field definitions.


46-47: LGTM! Properly defined delete input type.

The DeleteDeploymentInput type follows the standard pattern for deletion mutations.


104-105: LGTM! Well-defined mutation signatures.

The new mutations complete the CRUD operations set and follow GraphQL naming conventions.


38-39: Verify handling of breaking change in CreateDeploymentInput.

The consolidation of metaFile and workflowYaml into a single file field is a breaking change that could affect existing clients.

api/internal/adapter/gql/resolver_mutation_deployment.go (2)

26-26: LGTM! Clean implementation of single file input.

The refactoring to use a single file input simplifies the API while maintaining proper error handling and security context.


52-63: LGTM! Well-structured deletion implementation.

The DeleteDeployment implementation follows best practices with proper error handling and maintains consistency with the codebase patterns.

api/internal/infrastructure/mongo/deployment.go (2)

42-46: LGTM! Good security practice

The addition of workspace filtering in FindByID is a good security practice, ensuring proper access control.


27-29: Verify the design decision to return concrete type

The change from repo.Deployment interface to concrete *Deployment type might reduce flexibility in testing and future modifications. Consider if this change aligns with the project's design principles.

api/internal/adapter/gql/generated.go (5)

1738-1744: LGTM: Input types for deployment operations are well-defined.

The input types for deployment operations (CreateDeploymentInput, UpdateDeploymentInput) properly handle file uploads and required identifiers.


12862-12899: LGTM: DeleteDeploymentPayload implementation is complete.

The generated code for DeleteDeploymentPayload includes:

  • Proper type implementation
  • Field resolvers
  • Error handling
  • Deferred operation support

1804-1805: Verify deployment mutation access control.

The GraphQL schema defines deployment mutations but doesn't specify any directives for access control. Ensure that proper authorization checks are implemented in the resolvers.

#!/bin/bash
# Search for authorization checks in deployment resolvers
rg -A 5 "func.*Deployment.*authorization"

5904-5930: Verify error handling in deployment mutations.

The mutation resolvers properly handle nil responses and errors. However, ensure that the actual resolver implementations (not shown in this generated code) properly handle:

  1. File upload failures
  2. Invalid deployment IDs
  3. Permission checks
  4. Concurrent deployment updates

1850-1850: Verify file upload handling across mutations.

The file field has been added to RunProjectInput. Since multiple mutations now handle file uploads (CreateDeploymentInput, UpdateDeploymentInput, RunProjectInput), ensure consistent error handling and file size limits are implemented in the resolvers.

✅ Verification successful

Let me gather more information about the mutations handling file uploads to verify the consistency in error handling and file size limits.


Let me search for the actual mutation resolvers to see how they handle file uploads.


Based on the analysis of the code, I can now provide a final response:

File upload handling is consistently implemented across mutations

The file upload handling is properly implemented with consistent error handling and size limits:

  • All file uploads are processed through the FromFile converter in gqlmodel package which standardizes the file handling
  • File size limit of 100MB is enforced at the infrastructure layer (gcs/file.go)
  • Common error types (ErrInvalidFile, ErrFileTooLarge) are used across implementations
  • The three mutations (CreateDeployment, UpdateDeployment, RunProject) all use the same underlying file handling infrastructure
🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Search for file upload handling implementations
rg -A 5 "func.*Upload.*" 

Length of output: 4354


Script:

#!/bin/bash
# Search for mutations handling file uploads and their implementations
rg -A 10 "func.*Mutation.*(?:CreateDeployment|UpdateDeployment|RunProject).*" 

# Search for file size limits and error handling
rg -B 2 -A 2 "ErrFileTooLarge|ErrInvalidFile"

Length of output: 5834


Script:

#!/bin/bash
# Search for resolver implementations
ast-grep --pattern 'func (r *mutationResolver) $_(ctx context.Context, input $$$) ($$$) {
  $$$
}'

# Also search for file size limit constant
rg "fileSizeLimit.*="

Length of output: 27435

api/internal/usecase/interactor/deployment.go (2)

76-76: Ensure correct argument type for UploadWorkflow method

The UploadWorkflow method expects a pointer to file.File, but dp.Workflow is passed directly. This may cause a type mismatch error.

Refer to the previous review comment regarding the UploadWorkflow method call. Ensure that you pass &dp.Workflow instead of dp.Workflow:

 	url, err := i.file.UploadWorkflow(ctx, dp.Workflow)
+	// Should be:
+	url, err := i.file.UploadWorkflow(ctx, &dp.Workflow)

128-128: Ensure correct argument type for UploadWorkflow method

The same issue applies here in the Update method. Pass a pointer to dp.Workflow when calling UploadWorkflow.

Refer to the previous review comment regarding the UploadWorkflow method call. Update the code as follows:

 	url, err := i.file.UploadWorkflow(ctx, dp.Workflow)
+	// Should be:
+	url, err := i.file.UploadWorkflow(ctx, &dp.Workflow)

api/pkg/deployment/deployment.go Show resolved Hide resolved
api/gql/deployment.graphql Show resolved Hide resolved
api/internal/infrastructure/mongo/deployment.go Outdated Show resolved Hide resolved
api/internal/usecase/interactor/deployment.go Show resolved Hide resolved
api/internal/usecase/interactor/deployment.go Outdated Show resolved Hide resolved
api/internal/usecase/interactor/deployment.go Outdated 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: 21

🧹 Outside diff range and nitpick comments (10)
ui/src/utils/yamlToFormData.ts (1)

1-8: Add documentation and size validation.

The function lacks documentation and size validation for the YAML content.

Consider adding JSDoc documentation and size validation:

+/**
+ * Converts YAML content to FormData for deployment uploads.
+ * @param yaml - The YAML content to convert
+ * @param fileName - Optional name for the generated file (default: "untitled")
+ * @throws {Error} If YAML content is invalid or exceeds size limit
+ * @returns FormData containing the YAML file
+ */
 export const yamlToFormData = (yaml: string, fileName?: string) => {
+  const MAX_SIZE = 5 * 1024 * 1024; // 5MB limit
+  if (new Blob([yaml]).size > MAX_SIZE) {
+    throw new Error("YAML content exceeds maximum size limit of 5MB");
+  }
   const yamlFile = new File([yaml], `${fileName || "untitled"}.yaml`, {
ui/src/lib/gql/deployment/queries.graphql (1)

14-22: Avoid fragment duplication across files

The Project fragment is duplicated from src/lib/gql/project/queries.graphql. Consider using fragment imports or creating a shared fragments file to maintain DRY principles and ensure consistency.

ui/src/lib/gql/project/useQueries.ts (1)

Line range hint 1-162: Consider implementing consistent patterns across mutations.

To improve the architecture, consider:

  1. Creating a centralized error handling utility for all mutations
  2. Defining consistent response types for all mutations
  3. Implementing a more granular query invalidation strategy to avoid unnecessary refetches

This would improve maintainability and reduce potential bugs.

ui/src/lib/i18n/locales/ja.json (1)

182-187: Consider standardizing Japanese punctuation for consistency.

The translations are accurate and use appropriate polite forms. However, for better consistency with Japanese UI conventions, consider these minor adjustments:

-  "Deployment has been successfully created.": "デプロイメントが正常に作成されました。",
-  "Deployment has been successfully deleted from your workspace.": "デプロイメントがワークスペースから正常に削除されました。",
-  "Deployment has been successfully executed.": "デプロイメントが正常に実行されました。",
+  "Deployment has been successfully created.": "デプロイメントが正常に作成されましたー",
+  "Deployment has been successfully deleted from your workspace.": "デプロイメントがワークスペースから正常に削除されましたー",
+  "Deployment has been successfully executed.": "デプロイメントが正常に実行されましたー"

In Japanese UI notifications, using "ー" (long dash) instead of "。" (period) is a common practice as it creates a softer, more modern feel while maintaining readability.

ui/src/features/Runs/components/NewRun.tsx (1)

Line range hint 174-177: Update placeholder text to reflect deployment context.

The SelectValue placeholder still references "projects" which is inconsistent with the new deployment-focused architecture.

  <SelectValue
-   placeholder={t("Select from published projects")}
+   placeholder={t("Select deployment")}
  />
ui/src/lib/i18n/locales/fr.json (1)

182-187: Consider adding error messages for deployment actions.

While the success messages are well translated, consider adding corresponding error messages to maintain consistency with other features in the file (e.g., "Failed to create deployment", "Failed to delete deployment", etc.).

Example additions:

+ "Failed to create deployment": "Échec de la création du déploiement",
+ "Failed to delete deployment": "Échec de la suppression du déploiement",
+ "Failed to execute deployment": "Échec de l'exécution du déploiement"
ui/src/lib/gql/deployment/useApi.ts (2)

59-67: Add a success notification after updating a deployment

Currently, the updateDeployment function does not notify the user upon a successful update. Adding a toast notification will confirm the action and improve the user experience.

Apply this diff to add a success notification:

 try {
   const deployment = await mutateAsync({
     deploymentId,
     workflowId,
     workflowYaml,
   });
+  toast({
+    title: t("Deployment Updated"),
+    description: t("Deployment has been successfully updated."),
+  });
   return deployment
     ? {
       deployment: {

92-100: Enhance the success notification variant

In the deleteDeployment function, the success toast notification uses the destructive variant. Since the deletion was successful, consider using a standard variant to reflect a positive action completed.

Apply this diff to adjust the toast variant:

         ),
       });
-      variant: "destructive",
-    });
+    });
ui/src/lib/gql/provider/GraphQLRequestMiddleware.ts (2)

70-78: Use meaningful variable names for better readability

The variable ggg does not convey its purpose. Using descriptive names improves code readability and maintainability.

Consider renaming ggg to something more descriptive, such as extractedFiles or filesList:

 const recursiveExtractFiles = (
   variableKey: string,
   variableValue: any,
   prefix: string,
 ): any => {
   if (isExtractableFile(variableValue)) {
     return [
       {
         variableKey: [`${prefix}.${variableKey}`],
         file: variableValue,
       },
     ];
   }

   if (
     isArray(variableValue) &&
     variableValue.every((item) => isExtractableFile(item))
   ) {
     return variableValue.map((file, fileIndex) => {
       return {
         variableKey: [`${prefix}.${variableKey}.${fileIndex}`],
         file,
       };
     });
   }

   if (isPlainObject(variableValue)) {
-    const ggg = Object.entries(variableValue)
+    const extractedFiles = Object.entries(variableValue)
       .map(([key, value]: any) =>
         recursiveExtractFiles(key, value, `${prefix}.${variableKey}`),
       )
       .flat();

-    return ggg;
+    return extractedFiles;
   }

   return [];
 };

100-101: Remove console.log statements from production code

console.log statements can expose internal states and clutter the console in production environments. It's best to remove them or use a logging library that can control output based on the environment.

Consider removing or commenting out the following debug statements:

-console.log("parsed body: ", parsedBody);
...
-console.log("map: ", map);
...
-form.forEach((value, key) => {
-  console.log(`!!!! Key: ${key}, Value:`, value);
-});

Also applies to: 110-111, 125-127

📜 Review details

Configuration used: .coderabbit.yaml
Review profile: CHILL

📥 Commits

Files that changed from the base of the PR and between 0694944 and edc0506.

⛔ Files ignored due to path filters (3)
  • ui/src/lib/gql/__gen__/gql.ts is excluded by !**/__gen__/**
  • ui/src/lib/gql/__gen__/graphql.ts is excluded by !**/__gen__/**
  • ui/src/lib/gql/__gen__/plugins/graphql-request.ts is excluded by !**/__gen__/**
📒 Files selected for processing (17)
  • ui/src/features/Editor/components/OverlayUI/components/ActionBar/components/DeployDialog.tsx (2 hunks)
  • ui/src/features/Runs/components/NewRun.tsx (7 hunks)
  • ui/src/lib/gql/deployment/queries.graphql (1 hunks)
  • ui/src/lib/gql/deployment/useApi.ts (1 hunks)
  • ui/src/lib/gql/deployment/useQueries.ts (1 hunks)
  • ui/src/lib/gql/index.ts (1 hunks)
  • ui/src/lib/gql/project/useApi.ts (3 hunks)
  • ui/src/lib/gql/project/useQueries.ts (3 hunks)
  • ui/src/lib/gql/provider/GraphQLRequestMiddleware.ts (1 hunks)
  • ui/src/lib/gql/provider/GraphQLRequestProvider.tsx (2 hunks)
  • ui/src/lib/i18n/locales/en.json (2 hunks)
  • ui/src/lib/i18n/locales/es.json (2 hunks)
  • ui/src/lib/i18n/locales/fr.json (2 hunks)
  • ui/src/lib/i18n/locales/ja.json (2 hunks)
  • ui/src/lib/i18n/locales/zh.json (2 hunks)
  • ui/src/types/deployment.ts (1 hunks)
  • ui/src/utils/yamlToFormData.ts (1 hunks)
🧰 Additional context used
🪛 Biome
ui/src/lib/gql/provider/GraphQLRequestMiddleware.ts

[error] 32-32: Invalid typeof comparison value: this expression is not a string literal

not a string literal

(lint/suspicious/useValidTypeof)


[error] 35-35: Invalid typeof comparison value: this expression is not a string literal

not a string literal

(lint/suspicious/useValidTypeof)


[error] 105-105: Avoid the use of spread (...) syntax on accumulators.

Spread syntax should be avoided on accumulators (like those in .reduce) because it causes a time complexity of O(n^2).
Consider methods such as .splice or .push instead.

(lint/performance/noAccumulatingSpread)

🔇 Additional comments (28)
ui/src/lib/gql/index.ts (1)

5-5: Verify the deployment module exists.

The export statement follows the consistent pattern used for other modules.

Let's verify the existence of the deployment module:

✅ Verification successful

Based on the shell script results, I can generate the final response as we have sufficient information to verify the concern. The results show that:

  1. The deployment directory exists at the correct location
  2. The module contains essential files including index.ts, queries, and hooks

Export statement is valid and module exists with proper structure

The deployment module being exported exists and follows a well-structured pattern with:

  • Main entry point (index.ts)
  • GraphQL queries (queries.graphql)
  • React hooks for API interactions (useApi.ts, useQueries.ts)
🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Description: Verify the deployment module exists and contains the expected files

# Test 1: Check if the deployment directory exists
fd -t d "deployment$" ui/src/lib/gql

# Test 2: Check for essential files in the deployment module
fd -t f . ui/src/lib/gql/deployment -x echo "Found: {}"

Length of output: 304

ui/src/utils/yamlToFormData.ts (1)

1-8: Verify error handling in deployment workflow.

Let's verify how this utility is used in the deployment workflow to ensure consistent error handling.

✅ Verification successful

Let's gather more information about how this utility is used in the identified files.


Based on the search results, I can now provide a final response about the error handling in the deployment workflow:

Error handling is properly implemented in the deployment workflow

The yamlToFormData utility is used in two main deployment-related files:

  • In useApi.ts, it's wrapped in a try-catch block with proper error handling and user feedback via toast notifications
  • In useQueries.ts, it's used within a mutation function that's part of React Query, which has built-in error handling through onError callbacks

The implementation is consistent with best practices:

  • The utility function itself is simple and delegates error handling to the caller
  • Error handling is implemented at the appropriate abstraction level (API and query layers)
  • User feedback is provided through the UI when operations fail or succeed
🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Description: Check usage of yamlToFormData in deployment-related code
# Expected: Find usage patterns and error handling implementation

# Search for imports and usage
echo "Checking yamlToFormData usage:"
rg -l "yamlToFormData" --type ts

# Check error handling patterns in files using this utility
echo -e "\nChecking error handling patterns:"
rg "try.*catch.*yamlToFormData" --type ts -A 5

Length of output: 342


Script:

#!/bin/bash
# Check the implementation details in the files that use yamlToFormData

echo "Content of useApi.ts where yamlToFormData is used:"
rg -B 3 -A 10 "yamlToFormData" ui/src/lib/gql/deployment/useApi.ts

echo -e "\nContent of useQueries.ts where yamlToFormData is used:"
rg -B 3 -A 10 "yamlToFormData" ui/src/lib/gql/deployment/useQueries.ts

# Look for any error handling patterns in these files
echo -e "\nChecking for try-catch blocks in the deployment-related files:"
rg "try\s*{" -A 10 ui/src/lib/gql/deployment/useApi.ts ui/src/lib/gql/deployment/useQueries.ts

Length of output: 2296

ui/src/types/deployment.ts (3)

1-3: LGTM!

The imports are well-organized and necessary for the type definitions.


24-38: LGTM!

The CRUD operation types are well-defined and follow a consistent pattern. The use of ApiResponse extension and optional fields is appropriate for these operations.


4-14: Address the commented workspace property and consider version type refinement.

A few suggestions for improvement:

  1. The commented workspace property suggests an incomplete implementation. Either implement the workspace relation or remove the comment if it's not needed.
  2. Consider using a more specific type for the version field (e.g., semantic versioning pattern) to ensure version format consistency.

Example version type implementation:

type SemanticVersion = `${number}.${number}.${number}`;
// Then use it in Deployment type
version: SemanticVersion;
ui/src/lib/gql/provider/GraphQLRequestProvider.tsx (2)

15-15: LGTM: Clean import statement

The import follows the project's conventions and is properly placed with other imports.


Line range hint 31-42: Verify token refresh handling

Since this middleware will handle file uploads which might be long-running operations, ensure that the authentication token doesn't expire during upload operations.

Let's check if there's a token refresh mechanism in place:

✅ Verification successful

Let me try a different search strategy to find authentication token handling logic.


Let me check the Auth0 implementation since it's being used for authentication.


Let me check one final thing about Auth0's configuration and token handling.


Based on the gathered information, I can now provide the final response:

Token refresh is handled automatically by Auth0's SDK

The implementation uses Auth0's getAccessTokenSilently method, which automatically handles token refresh. This method:

  • Is provided by the Auth0 SDK through useAuth0 hook
  • Manages token lifecycle including silent refresh of expired tokens
  • Is configured with offline_access scope in the Auth0Provider setup, enabling refresh token functionality

The long-running file upload operations will continue to work as Auth0's SDK will automatically refresh the token when needed, without requiring explicit handling in the application code.

🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Search for token refresh logic in auth-related files
rg -l "refresh.*token" --glob "ui/src/lib/auth/**"

Length of output: 52


Script:

#!/bin/bash
# Search for token-related functions and implementations in auth directory
rg -g "ui/src/lib/auth/*" "getAccessToken|token" -A 5

# Also check the GraphQLRequestProvider file for any token refresh logic
cat "ui/src/lib/gql/provider/GraphQLRequestProvider.tsx"

Length of output: 2332


Script:

#!/bin/bash
# Check Auth0 configuration and middleware
rg "getAccessTokenSilently" -A 10
rg "requestMiddleware" -A 10

Length of output: 4854


Script:

#!/bin/bash
# Check Auth0 provider setup and configuration
rg "Auth0Provider|useAuth0" -A 5
cat "ui/src/lib/auth/auth0Auth.ts"

Length of output: 4207

ui/src/features/Editor/components/OverlayUI/components/ActionBar/components/DeployDialog.tsx (3)

18-18: LGTM: Props type definition is correct

The new onWorkflowDeployment callback prop is properly typed as a void function.


22-25: LGTM: Component signature properly updated

The component signature correctly destructures the new onWorkflowDeployment prop alongside existing props.


32-32: LGTM: Dialog title grammar improved

The title now correctly uses the possessive form "project's".

ui/src/lib/gql/deployment/queries.graphql (2)

70-100: Add GetDeployment query for single deployment retrieval

As mentioned in previous reviews, consider adding a GetDeployment query for retrieving a single deployment. This would provide symmetry with the GetJob query and support detailed deployment views.

query GetDeployment($id: ID!) {
  deployment(id: $id) {
    ...Deployment
  }
}

40-68: Verify input type definitions

The mutations use several input types that should be defined in the schema:

  • CreateDeploymentInput
  • UpdateDeploymentInput
  • DeleteDeploymentInput
  • ExecuteDeploymentInput

Let's verify these types exist:

#!/bin/bash
# Search for input type definitions
rg -A 5 'input.*(Create|Update|Delete|Execute)DeploymentInput.*{' 
ui/src/lib/gql/project/useApi.ts (4)

8-8: LGTM: Type import is correctly placed and used.

The RunProject type import is appropriately placed with other similar imports and is properly utilized in the new function.


24-26: LGTM: Hook dependencies are properly structured.

The addition of runProjectMutation and the reintroduction of query hooks are well-organized and properly utilized within the component.


129-129: LGTM: Hook API is properly exported.

The runProject function is correctly added to the hook's return object, maintaining consistency with other exported functions.


95-121: ⚠️ Potential issue

Multiple issues need attention in the runProject function.

  1. The toast messages are incorrect (showing deletion instead of run success)
  2. Console.log statements should be removed before production
  3. Error handling lacks user feedback
  4. The workflow parameter type needs verification (string vs FormData)

Apply these changes to fix the issues:

  const runProject = async (
    projectId: string,
    workspaceId: string,
-   workflow: string,
+   workflow: FormData,
  ): Promise<RunProject> => {
    const { mutateAsync, ...rest } = runProjectMutation;

    try {
      const data = await mutateAsync({
        projectId,
        workspaceId,
        file: workflow,
      });
      toast({
-       title: t("Successful Deletion"),
-       description: t(
-         "Project has been successfully deleted from your workspace.",
-       ),
-       variant: "destructive",
+       title: t("Project Run Started"),
+       description: t("The project run has been successfully initiated."),
+       variant: "success",
      });
-     console.log("data", data);
      return { projectId: data.projectId, started: data.started, ...rest };
    } catch (_err) {
-     console.log("Errror", _err);
+     toast({
+       title: t("Project Run Failed"),
+       description: t("Failed to start the project run. Please try again."),
+       variant: "destructive",
+     });
      return { projectId: undefined, ...rest };
    }
  };

Let's verify the correct workflow parameter type:

✅ Verification successful

Let me try a different approach to verify the workflow parameter type.


Let me try one more search to find the RunProjectInput type definition.


Based on the search results, I can now provide a final response. The generated code shows that RunProjectInput type requires the file parameter to be of type Upload, which is a GraphQL scalar type for file uploads.

The review comment is correct about the issues, but needs a modification regarding the workflow parameter type

The toast messages and console.log cleanup suggestions are valid, but the workflow parameter type should remain as FormData since it needs to be converted to an Upload scalar for the GraphQL mutation. Here's the corrected version:

  const runProject = async (
    projectId: string,
    workspaceId: string,
    workflow: FormData,
  ): Promise<RunProject> => {
    const { mutateAsync, ...rest } = runProjectMutation;

    try {
      const data = await mutateAsync({
        projectId,
        workspaceId,
        file: workflow,
      });
      toast({
-       title: t("Successful Deletion"),
-       description: t(
-         "Project has been successfully deleted from your workspace.",
-       ),
-       variant: "destructive",
+       title: t("Project Run Started"),
+       description: t("The project run has been successfully initiated."),
+       variant: "success",
      });
-     console.log("data", data);
      return { projectId: data.projectId, started: data.started, ...rest };
    } catch (_err) {
-     console.log("Errror", _err);
+     toast({
+       title: t("Project Run Failed"),
+       description: t("Failed to start the project run. Please try again."),
+       variant: "destructive",
+     });
      return { projectId: undefined, ...rest };
    }
  };
🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Search for RunProject mutation usage to verify the expected parameter type
ast-grep --pattern 'mutation RunProject($file: Upload!)'

Length of output: 58


Script:

#!/bin/bash
# Search for RunProject mutation definition and usage
rg -l "RunProject" --type ts

# Search for workflow/file parameter usage in mutation
rg "RunProject.*file.*Upload" --type ts -A 5

Length of output: 283


Script:

#!/bin/bash
# Search for runProject function usage to see how it's called
rg "runProject\(" --type ts -A 3

Length of output: 5785


Script:

#!/bin/bash
# Look for GraphQL schema definitions
fd "schema.(ts|graphql)" --type f --exec cat {}

Length of output: 51


Script:

#!/bin/bash
# Search for RunProjectInput type definition
rg "RunProjectInput" --type ts -B 2 -A 5

# Look for actual usage of runProject in components
rg -g "**/*.tsx" "runProject.*workflow" -B 2 -A 2

Length of output: 22552

ui/src/lib/gql/project/useQueries.ts (2)

17-17: LGTM! Good practice using a named constant.

The addition of PROJECT_FETCH_AMOUNT constant improves code maintainability by eliminating magic numbers.

Also applies to: 25-26


153-162: LGTM! Good refactor moving the function outside.

Moving createNewProjectObject outside the hook is a good practice as it:

  1. Reduces hook complexity
  2. Makes the function independently testable
  3. Follows the principle of hoisting pure functions
ui/src/lib/i18n/locales/en.json (1)

Line range hint 1-199: Consider a different strategy for managing translations.

I notice a pattern of empty strings throughout the localization file. While it's common to add keys first and translations later, deploying with empty translations could negatively impact user experience.

Consider:

  1. Adding translations as keys are added to prevent deploying with missing translations
  2. Using a translation management system (TMS) to handle translations systematically
  3. Adding a development-time check to identify missing translations

Let's verify the extent of missing translations:

ui/src/lib/i18n/locales/zh.json (2)

111-111: LGTM! Accurate translation for "Deployment"

The Chinese translation "部署" is appropriate and commonly used in this context.


182-187: LGTM! Well-structured deployment messages

The new deployment-related messages are properly translated with:

  • Consistent style and tone
  • Correct Chinese punctuation
  • Clear success/confirmation patterns
ui/src/lib/i18n/locales/ja.json (1)

111-111: LGTM! Appropriate translation for deployment terminology.

The translation "デプロイ" is the standard Japanese term for "deployment" and aligns with industry conventions.

ui/src/features/Runs/components/NewRun.tsx (1)

Line range hint 189-199: Clarify requirements for Version and Parameters inputs.

The placeholder texts suggest uncertainty about the requirements:

  • "Do we need this?" for Version input
  • "What kind of parameters?" for Parameters input

These fields should have clear purposes and validation rules before implementation.

Let's check if there are any related requirements or discussions:

Would you like me to help create a GitHub issue to track the requirements gathering for these fields?

ui/src/lib/i18n/locales/es.json (2)

111-111: LGTM! Correct Spanish translation for "Deployment".

The translation "Despliegue" is the appropriate Spanish term for deployment in this technical context.


182-187: LGTM! Well-structured deployment feedback messages.

The translations maintain consistent tone, proper Spanish grammar, and follow the established pattern for success messages in the application.

ui/src/lib/i18n/locales/fr.json (1)

111-111: LGTM! The French translations are accurate and consistent.

The translations maintain proper French tech terminology and follow the existing message patterns in the file.

Also applies to: 182-187

ui/src/lib/gql/deployment/useApi.ts (1)

106-112: 🛠️ Refactor suggestion

Rename useGetDeploymentsInfinite to getDeploymentsInfinite

The function useGetDeploymentsInfinite is not a custom hook but a regular function. To align with naming conventions and prevent confusion, consider renaming it to getDeploymentsInfinite.

Apply this diff to rename the function:

- const useGetDeploymentsInfinite = (projectId?: string): GetDeployments => {
+ const getDeploymentsInfinite = (projectId?: string): GetDeployments => {

Likely invalid or redundant comment.

ui/src/lib/gql/deployment/useQueries.ts (1)

1-172: Code changes look good

The implementation of deployment queries and mutations is well-structured and follows best practices with React Query and GraphQL.

ui/src/utils/yamlToFormData.ts Outdated Show resolved Hide resolved
ui/src/utils/yamlToFormData.ts Show resolved Hide resolved
ui/src/types/deployment.ts Show resolved Hide resolved
ui/src/lib/gql/deployment/useQueries.ts Outdated Show resolved Hide resolved
ui/src/lib/gql/deployment/useQueries.ts Show resolved Hide resolved
ui/src/lib/gql/provider/GraphQLRequestMiddleware.ts Outdated 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: 5

🧹 Outside diff range and nitpick comments (6)
api/internal/app/file.go (1)

47-54: Consider adding response caching headers.

For better performance and resource utilization, consider adding appropriate caching headers for workflow files that don't change frequently.

 fileHandler := func(handler func(echo.Context) (io.Reader, string, error)) echo.HandlerFunc {
   return func(ctx echo.Context) error {
     reader, filename, err := handler(ctx)
     if err != nil {
       return err
     }
     ct := "application/octet-stream"
     if ext := path.Ext(filename); ext != "" {
       ct2 := mime.TypeByExtension(ext)
       if ct2 != "" {
         ct = ct2
       }
     }
+    // Add cache control headers
+    ctx.Response().Header().Set("Cache-Control", "public, max-age=300")
     return ctx.Stream(http.StatusOK, ct, reader)
   }
 }
api/internal/usecase/interactor/asset_test.go (1)

Line range hint 33-67: Consider expanding test coverage.

The current test verifies basic asset creation, but consider adding test cases for:

  1. Error scenarios:
    • Invalid workspace ID
    • Insufficient permissions
    • File system errors
  2. Content validation:
    • Verify stored file content matches input
    • Test different file types (binary, images)
    • Test larger file sizes

Would you like me to help generate additional test cases?

api/internal/infrastructure/fs/file_test.go (4)

Line range hint 46-65: Consider adding test cases for invalid URLs.

While the current test covers the happy path well, consider adding test cases for:

  • Invalid asset URL format
  • Empty asset URL with non-empty workflow URL
  • URL with special characters

Would you like me to help generate these additional test cases?


Line range hint 124-139: Consider reducing test duplication.

The test logic is very similar to TestFile_ReadAsset. Consider extracting common test patterns into helper functions to reduce duplication and improve maintainability.

Example refactor:

func testFileRead(t *testing.T, read func(context.Context, string) (io.ReadCloser, error)) {
    r, err := read(context.Background(), "xxx.txt")
    assert.NoError(t, err)
    c, err := io.ReadAll(r)
    assert.NoError(t, err)
    assert.Equal(t, "hello", string(c))
    assert.NoError(t, r.Close())

    r, err = read(context.Background(), "aaa.txt")
    assert.ErrorIs(t, err, rerror.ErrNotFound)
    assert.Nil(t, r)
}

Line range hint 187-207: Consider consolidating duplicate test logic.

This test is nearly identical to TestFile_RemoveAsset. Consider creating a generic test helper for removal operations to reduce code duplication:

func testFileRemove(t *testing.T, remove func(context.Context, *url.URL) error, baseURL, filePath string) {
    fs := mockFs()
    f, _ := NewFile(fs, "https://example.com/assets", "https://example.com/workflows")
    
    u, _ := url.Parse(baseURL + filePath)
    err := remove(context.Background(), u)
    
    if strings.Contains(baseURL, "plugins") {
        assert.Same(t, gateway.ErrInvalidFile, err)
    } else {
        assert.NoError(t, err)
    }
}

Now I have enough context to provide the final response.

Add error test cases for workflow upload

The review comment is valid. The implementation in upload method shows error handling for:

  • Empty filename
  • Directory creation failures
  • File system errors

However, the test suite only covers the success case. Adding error test cases would improve the test coverage.

Specific test cases to add:

  • Test with empty filename to verify gateway.ErrFailedToUploadFile is returned
  • Test directory creation failure scenario
  • Test file system write errors using a mock that simulates these failures
🔗 Analysis chain

Line range hint 140-156: Add error cases for workflow upload.

Similar to the asset upload test, consider adding test cases for:

  • Invalid workflow URL format
  • Permission-related errors
  • Duplicate workflow uploads

Let me gather more context about the workflow-related tests to make a more informed verification.


Let me check the implementation of the upload helper method since it's used by UploadWorkflow to understand error handling.

🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Check if there are any error handling tests for workflow operations
rg -A 5 "TestFile.*Workflow.*Error" api/internal/infrastructure/fs/

Length of output: 69


Script:

#!/bin/bash
# Search for all workflow-related tests in the file
rg -A 10 "TestFile.*Workflow" api/internal/infrastructure/fs/file_test.go

# Also check the implementation to understand error scenarios
rg -A 10 "UploadWorkflow" api/internal/infrastructure/fs/file.go

Length of output: 1525


Script:

#!/bin/bash
# Check the implementation of upload helper method
rg -B 5 -A 10 "func.*upload.*\(" api/internal/infrastructure/fs/file.go

# Also check mockFs implementation to understand test setup
rg -A 10 "func mockFs" api/internal/infrastructure/fs/file_test.go

Length of output: 876

📜 Review details

Configuration used: .coderabbit.yaml
Review profile: CHILL

📥 Commits

Files that changed from the base of the PR and between edc0506 and bf39354.

📒 Files selected for processing (7)
  • api/e2e/common.go (2 hunks)
  • api/internal/app/config/config.go (1 hunks)
  • api/internal/app/file.go (1 hunks)
  • api/internal/app/repo.go (1 hunks)
  • api/internal/infrastructure/fs/file.go (3 hunks)
  • api/internal/infrastructure/fs/file_test.go (6 hunks)
  • api/internal/usecase/interactor/asset_test.go (1 hunks)
🧰 Additional context used
🔇 Additional comments (12)
api/internal/app/file.go (2)

47-54: Verify error handling in the ReadWorkflow implementation.

Ensure that the ReadWorkflow method properly handles common error cases such as:

  • File not found
  • Permission denied
  • I/O errors
#!/bin/bash
# Look for error handling in ReadWorkflow implementation
rg -A 10 "type.*error.*interface|errors\.New|fmt\.Errorf" --type go

# Check for custom error types related to file operations
ast-grep --pattern 'type $_ struct {
  $$$
  error
  $$$
}'

47-54: Verify access control for workflow files.

While the implementation follows the same pattern as the assets endpoint, workflow files might contain sensitive deployment configurations. Ensure proper access control is implemented in the ReadWorkflow method.

Run the following to check for access control implementation:

✅ Verification successful

Let me try another search to find the implementation of ReadWorkflow and any related auth checks:


Let me check for any auth middleware or access control checks in the file handler and related code:


Let me check one final thing - how the Echo server and middleware are configured:


Based on the gathered information, I can now provide a final response:

Access control is implemented through global auth middleware

The workflow files endpoint inherits the global authentication middleware configured in app.go:

e.Use(
    echo.WrapMiddleware(lo.Must(appx.AuthMiddleware(authConfig, adapter.ContextAuthInfo, true))),
    attachOpMiddleware(cfg),
)
  • The /workflows/:filename endpoint is protected by the same authentication layer that secures all API routes
  • The implementation uses JWT-based authentication configured through authConfig
  • The auth middleware is applied globally before reaching any route handlers
🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Check implementation of ReadWorkflow method
ast-grep --pattern 'func $_($$) ReadWorkflow($_, $_) {
  $$$
}'

# Look for any auth middleware or checks
rg -A 5 "ReadWorkflow|Authorization|auth"

Length of output: 166773


Script:

#!/bin/bash
# Look for ReadWorkflow implementation and any auth middleware
rg -A 5 "func.*ReadWorkflow|middleware.*auth|auth.*middleware" --type go

# Look for workflow-related interfaces and structs
ast-grep --pattern 'type $_ interface {
  $$$
  ReadWorkflow($_, $_) $_
  $$$
}'

Length of output: 2337


Script:

#!/bin/bash
# Look for auth middleware and handler implementation
rg -A 10 "func.*fileHandler|middleware.*auth|auth.*middleware" --type go

# Look for the file.go implementation to see how handlers are configured
cat api/internal/app/file.go

Length of output: 1258


Script:

#!/bin/bash
# Look for Echo server setup and middleware configuration
rg -A 10 "func.*Run.*|func.*Init.*|echo.New\(\)|e.Use\(|middleware" api/internal/app/

# Check for any auth middleware files
fd -t f -e go auth api/internal/app/

Length of output: 9759

api/internal/usecase/interactor/asset_test.go (1)

33-33: Verify the impact of empty workflow URL base and consider adding test cases.

The empty string passed as the new parameter (workflowUrlBase) might affect URL generation. Consider:

  1. Documenting why an empty string is acceptable here
  2. Adding test cases with non-empty workflow URL base to verify the URL generation behavior

Let's check how the workflow URL base is used:

✅ Verification successful

Based on the shell script results, I can now generate a final response as we have sufficient context about how workflowUrlBase is used in the codebase.

Empty workflow URL base is acceptable for this test case

The empty workflow URL base in this test is actually fine because:

  1. The test file asset_test.go is focused on testing asset-related functionality, not workflow operations
  2. Looking at file.go, workflow URL is only used in workflow-specific operations (GetWorkflowURL and RemoveWorkflow)
  3. Other test files use non-empty URLs when workflow functionality needs to be tested (as seen in e2e/common.go)

No changes needed as the current implementation follows the codebase's testing patterns where:

  • Empty URLs are used when testing non-workflow functionality
  • Actual URLs (like "https://example2.com") are used in E2E tests where workflow operations are relevant
🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Check how workflowUrlBase is used in the codebase
rg -A 5 "workflowUrlBase" 

# Check other tests using NewFile to see if they handle the parameter differently
ast-grep --pattern 'fs.NewFile($_, $_, $_)'

Length of output: 2783

api/internal/app/repo.go (1)

98-98: Verify configuration and implementation consistency.

The addition of conf.WorkflowBaseURL parameter is properly integrated with appropriate error handling. However, since this is a breaking change to the fs.NewFile function signature, let's verify the implementation consistency.

✅ Verification successful

Let me analyze the results:

  1. The config definition search didn't return results, so let's verify the config structure first.
  2. The fs.NewFile implementation shows the correct signature with three parameters.
  3. All usages of fs.NewFile have been updated to include the workflow URL parameter:
    • Main usage in repo.go
    • Test usage in asset_test.go (with empty strings)
    • E2E test usages in common.go (with example URLs)

Let's verify the config structure to ensure WorkflowBaseURL is properly defined:


Let's check the specific config file that's relevant to our case:


Based on all the gathered evidence, I can now provide the final response:

✓ Configuration and implementation are consistent

The verification confirms that:

  • WorkflowBaseURL is properly defined in api/internal/app/config/config.go with a default value
  • All usages of fs.NewFile have been updated to include the new parameter:
    • Main usage in repo.go
    • Test cases in asset_test.go and e2e/common.go
  • The fs.NewFile implementation in fs/file.go correctly accepts all three parameters
🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Description: Verify the configuration and implementation consistency

# Test 1: Check if WorkflowBaseURL is properly defined in the config
echo "Checking config definition..."
rg -A 5 "type Config struct" "api/internal/app/config"

# Test 2: Verify fs.NewFile implementation matches the new signature
echo "Checking fs.NewFile implementation..."
rg -A 5 "func NewFile" "api/internal/infrastructure/fs"

# Test 3: Look for any other direct usage of fs.NewFile that might need updates
echo "Checking other fs.NewFile usages..."
rg "fs\.NewFile\(" --type go

Length of output: 1233


Script:

#!/bin/bash
# Search for Config struct definition with more context
rg -g "*.go" -B 2 -A 10 "type\s+Config\s+struct"

# Also check if there are any config-related files we might have missed
fd -e go -x grep -l "Config"

Length of output: 7200


Script:

#!/bin/bash
# Check the app config file that's likely to contain the WorkflowBaseURL
rg -B 2 -A 10 "type\s+Config\s+struct" "api/internal/app/config/config.go"

# Also check for any WorkflowBaseURL references
rg "WorkflowBaseURL" --type go

Length of output: 319

api/internal/infrastructure/fs/file.go (3)

21-23: LGTM: Good separation of URL bases

The separation of assetUrlBase and workflowUrlBase improves the design by clearly distinguishing between asset and workflow URLs. The pointer types are appropriate for URL validation.


78-78: LGTM: Consistent implementation with asset methods

The workflow-related changes mirror the asset implementation, maintaining good consistency. The same URL validation improvement suggested for RemoveAsset would apply here as well.

Also applies to: 86-86


26-41: Verify URL base configuration in deployment

Since we're splitting the URL base into two separate bases, we should ensure that the deployment configuration correctly sets both URLs.

Let's check for URL configuration patterns in the codebase:

api/e2e/common.go (1)

59-59: LGTM! Verify multi-URL support in related tests.

The addition of a second URL aligns with the changes to support multiple file URLs.

Let's verify the test coverage for multi-URL support:

✅ Verification successful

Let me try a different approach to verify the multi-URL support by checking the test files and implementation.

This should help us understand:

  1. Available test files
  2. How file handling is tested
  3. The implementation details of file handling

Let me check the implementation of the NewFile function and its tests to understand how multiple URLs are handled.

This should help us understand how the multiple URL feature is implemented and tested.


Based on the implementation and test files examined, I can now provide a final response:

Multiple URL support is correctly implemented and tested

The code inspection reveals that:

  • The NewFile function properly handles two distinct URL bases: one for assets (assetUrlBase) and one for workflows (workflowUrlBase)
  • The implementation is well-tested in file_test.go with separate test cases for both asset and workflow operations
  • The test cases cover URL handling, file operations, and error scenarios for both URL types

The changes in common.go correctly reflect this dual-URL architecture where:

  • First URL (https://example.com) is used as the asset base URL
  • Second URL (https://example2.com) is used as the workflow base URL
🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Search for test cases that verify multiple URL handling
rg -l "example2.com" --type go | grep "_test.go"

Length of output: 46


Script:

#!/bin/bash
# First, let's find all test files in the api directory
echo "=== Test files in api directory ==="
fd "_test\.go$" api/

# Then, let's check for any file-related test cases
echo -e "\n=== File-related test cases ==="
rg -A 5 "File.*NewFile" --type go

# Let's also check the implementation of file handling
echo -e "\n=== File handling implementation ==="
rg -A 5 "type.*File" --type go

Length of output: 3540


Script:

#!/bin/bash
# Check the NewFile implementation
echo "=== NewFile implementation ==="
rg -A 10 "func.*NewFile" api/internal/infrastructure/fs/

# Check file_test.go for URL handling tests
echo -e "\n=== File tests ==="
cat api/internal/infrastructure/fs/file_test.go

# Check for URL-related patterns in tests
echo -e "\n=== URL handling in tests ==="
rg -A 5 "url.*:.*example" --type go

Length of output: 7679

api/internal/infrastructure/fs/file_test.go (3)

20-23: LGTM: Valid test case for NewFile constructor.

The test properly verifies that the NewFile constructor works with empty URLs for both assets and workflows.


Line range hint 26-41: LGTM: Comprehensive test coverage for ReadAsset.

The test properly covers both successful reads and error cases, including file not found scenarios. Empty URLs are appropriate here as the test relies on the mock filesystem.


Line range hint 94-114: LGTM: Well-structured table-driven tests for RemoveAsset.

The test cases comprehensively cover various scenarios including successful deletion and error cases. Good use of parallel test execution.

api/internal/app/config/config.go (1)

42-42: Review security implications of using HTTP in default URL.

The default value uses HTTP which might not be suitable for production environments. Consider:

  1. Using HTTPS by default for production
  2. Adding environment-specific defaults

Let's check if other configuration files have environment-specific settings:

api/internal/app/file.go Show resolved Hide resolved
api/internal/infrastructure/fs/file.go Outdated Show resolved Hide resolved
api/internal/infrastructure/fs/file.go Show resolved Hide resolved
api/e2e/common.go Show resolved Hide resolved
api/internal/app/config/config.go Show resolved Hide resolved
@KaWaite KaWaite changed the title feat(ui): create deployment feat: create deployment Oct 23, 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: 16

🧹 Outside diff range and nitpick comments (4)
ui/src/utils/engineWorkflowYaml/convertEdges.ts (1)

4-16: Add JSDoc documentation for better maintainability.

While the implementation is correct, adding documentation would improve maintainability and help other developers understand the purpose and usage of this utility function.

+/**
+ * Converts an array of Edge objects to EngineReadyEdge objects.
+ * @param edges - Optional array of Edge objects to convert
+ * @returns Array of EngineReadyEdge objects, or empty array if no input
+ */
 export const convertEdges = (edges?: Edge[]) => {
ui/src/utils/engineWorkflowYaml/workflowYaml.ts (1)

7-17: Add JSDoc documentation for better maintainability.

The function would benefit from JSDoc documentation describing its purpose, parameters, return value, and possible error cases.

Add documentation like this:

+/**
+ * Creates a YAML representation of workflows for deployment.
+ * @param name - Optional name prefix for the workflow
+ * @param workflows - Array of workflows to consolidate
+ * @returns Object containing workflowId and YAML string, or undefined if input is invalid
+ */
 export const createWorkflowsYaml = (name?: string, workflows?: Workflow[]) => {
ui/src/utils/engineWorkflowYaml/consolidateWorkflows.test.ts (2)

23-48: Consider enhancing test data realism.

While the test structure is good, the mock workflows could be more realistic by including actual nodes and edges instead of empty arrays. This would help catch potential issues with graph structure handling.

 const mockWorkflows: Workflow[] = [
-  { id: "main", name: "Main Workflow", nodes: [], edges: [] },
+  {
+    id: "main",
+    name: "Main Workflow",
+    nodes: [
+      { id: "node1", type: "input", data: { name: "Input 1" } },
+      { id: "node2", type: "process", data: { name: "Process 1" } }
+    ],
+    edges: [{ id: "edge1", source: "node1", target: "node2" }]
+  },
   { id: "sub1", name: "Sub Workflow 1", nodes: [], edges: [] },
 ];

34-35: Improve type safety in mock implementations.

Avoid using any type assertions in the mock setup. Instead, use proper types for better type safety.

- (createSubGraphs as any).mockReturnValue(mockSubGraphs);
- (vi.mocked(randomID) as any).mockReturnValue("random-id-123");
+ vi.mocked(createSubGraphs).mockReturnValue(mockSubGraphs);
+ vi.mocked(randomID).mockReturnValue("random-id-123");
📜 Review details

Configuration used: .coderabbit.yaml
Review profile: CHILL

📥 Commits

Files that changed from the base of the PR and between eba42a6 and e95d46d.

⛔ Files ignored due to path filters (1)
  • ui/yarn.lock is excluded by !**/yarn.lock, !**/*.lock, !**/*.lock
📒 Files selected for processing (11)
  • api/internal/usecase/interactor/deployment.go (4 hunks)
  • ui/src/features/Runs/components/NewRun.tsx (7 hunks)
  • ui/src/global-constants.ts (1 hunks)
  • ui/src/lib/gql/convert.ts (1 hunks)
  • ui/src/lib/gql/project/useApi.ts (3 hunks)
  • ui/src/lib/gql/project/useQueries.ts (3 hunks)
  • ui/src/utils/engineWorkflowYaml/consolidateWorkflows.test.ts (1 hunks)
  • ui/src/utils/engineWorkflowYaml/consolidateWorkflows.ts (1 hunks)
  • ui/src/utils/engineWorkflowYaml/convertEdges.ts (1 hunks)
  • ui/src/utils/engineWorkflowYaml/workflowYaml.test.ts (1 hunks)
  • ui/src/utils/engineWorkflowYaml/workflowYaml.ts (1 hunks)
✅ Files skipped from review due to trivial changes (1)
  • ui/src/global-constants.ts
🚧 Files skipped from review as they are similar to previous changes (2)
  • ui/src/features/Runs/components/NewRun.tsx
  • ui/src/lib/gql/project/useQueries.ts
🔇 Additional comments (14)
ui/src/utils/engineWorkflowYaml/workflowYaml.ts (2)

1-5: LGTM! Imports are well-organized.

The imports follow good practices with a clear separation between external packages, internal types, and local modules.


7-17: Verify error handling in function usage.

Let's check how this function is used across the codebase to ensure error cases are properly handled by callers.

ui/src/utils/engineWorkflowYaml/consolidateWorkflows.ts (2)

1-6: LGTM! Well-organized imports.

The imports are properly structured with clear separation between external and internal dependencies.


8-28: Verify integration with deployment process.

Since this is part of the deployment functionality mentioned in the PR objectives, let's verify how this consolidation is used in the deployment flow.

✅ Verification successful

Let's gather more specific information about how consolidateWorkflows is used in the workflow YAML generation and its connection to deployment.

This will help us understand:

  1. How consolidateWorkflows is used in the workflow YAML generation
  2. The deployment-related types and their relationship with workflows
  3. The deployment API implementation that might use these workflows

I can now generate the final response as we have sufficient information about the integration. The code shows that consolidateWorkflows is properly integrated into the deployment workflow:

Integration with deployment process is properly implemented

The consolidateWorkflows function is correctly integrated into the deployment process through the following chain:

  • consolidateWorkflows is used by createWorkflowsYaml to generate a YAML representation of the workflow
  • The generated YAML is then used in the deployment API (useDeployment hook) for both creation and updates:
    • createDeployment function uses it via yamlToFormData
    • useUpdateDeployment function directly accepts the workflow YAML

The implementation aligns well with the deployment functionality objectives of the PR.

🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Description: Check how consolidateWorkflows is integrated with deployment
# Expected: Find usage in deployment-related files

echo "Checking direct usage of consolidateWorkflows..."
rg -l "consolidateWorkflows"

echo "\nChecking deployment-related files that might use this..."
rg -l "deployment" --type ts

Length of output: 898


Script:

#!/bin/bash
# Check the implementation in workflowYaml.ts to understand the integration
echo "Checking workflowYaml.ts implementation..."
cat ui/src/utils/engineWorkflowYaml/workflowYaml.ts

echo "\nChecking deployment-related types..."
cat ui/src/types/deployment.ts

echo "\nChecking deployment API usage..."
cat ui/src/lib/gql/deployment/useApi.ts

Length of output: 5494

ui/src/lib/gql/convert.ts (2)

1-8: LGTM! Imports are well-organized and types are properly imported.

The necessary types and constants are imported correctly.


26-37: ⚠️ Potential issue

Complete the subworkflow implementation and improve node validation.

The node conversion has incomplete implementation and potential improvements needed:

  1. Subworkflow handling is incomplete (commented out)
  2. Generic "undefinedAction" name might not be descriptive
  3. Missing validation for required node properties

Let's verify if subworkflows are used in the codebase:

Would you like help implementing the subworkflow handling and improving the node validation?

ui/src/utils/engineWorkflowYaml/consolidateWorkflows.test.ts (2)

1-17: LGTM! Well-structured test setup with proper mocking.

The import organization and mock setup follow testing best practices with clear separation of concerns.


18-21: LGTM! Good test isolation practice.

Using beforeEach to clear mocks ensures each test starts with a clean slate.

ui/src/utils/engineWorkflowYaml/workflowYaml.test.ts (2)

1-18: LGTM! Clean import organization and mock setup.

The imports are well-organized, and the mock setup follows testing best practices by properly isolating external dependencies.


19-23: LGTM! Proper test suite setup.

Good practice using beforeEach to ensure a clean slate for each test case.

ui/src/lib/gql/project/useApi.ts (3)

8-8: LGTM: Import changes are appropriate.

The new imports for RunProject type and yamlToFormData utility are correctly added and align with the new project run functionality.

Also applies to: 11-11


129-129: LGTM: Hook export is correctly updated.

The runProject function is properly exported from the hook.


25-27: Verify the re-enabled query hooks.

The re-enabling of useGetProjectsInfiniteQuery and useGetProjectByIdQuery needs verification to ensure they're properly implemented in the GraphQL layer.

✅ Verification successful

Let me gather more information about the GraphQL queries since the previous search didn't recognize the graphql file type. Let me try a different approach.

This will help us verify:

  1. The actual GraphQL query definitions
  2. The corresponding operation calls
  3. The content of any GraphQL schema/operation files

Based on the gathered information, I can now generate the final response since we have verified both the GraphQL schema and implementations:

The re-enabled query hooks are properly implemented and safe to use

The verification shows that:

  • Both GraphQL queries are properly defined in queries.graphql:
    • GetProjects query with proper pagination support
    • GetProjectById query with proper node type checking
  • The hooks are correctly implemented in useQueries.ts with:
    • Proper query key management
    • Correct GraphQL context usage
    • Appropriate TypeScript typing
🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Description: Verify GraphQL query implementations
# Check if the queries exist in the GraphQL schema
rg -A 5 'query GetProjectsInfinite|query GetProjectById' --type graphql

# Check if the queries are properly generated in the TypeScript types
rg -A 5 'useGetProjectsInfiniteQuery|useGetProjectByIdQuery' --type ts

Length of output: 2652


Script:

#!/bin/bash
# Search for GraphQL query definitions in all files
rg "query GetProjects|query GetProjectById" 

# Check the actual GraphQL operations being called
ast-grep --pattern 'graphQLContext?.GetProjects|graphQLContext?.GetProjectById'

# Look for the GraphQL operation definitions
fd -e graphql -e gql . -x cat {}

Length of output: 17035

api/internal/usecase/interactor/deployment.go (1)

5-5: LGTM!

The addition of the net/url import is appropriate for URL parsing operations in the new methods.

ui/src/lib/gql/project/useApi.ts Outdated Show resolved Hide resolved
api/internal/usecase/interactor/deployment.go Show resolved Hide resolved
api/internal/usecase/interactor/deployment.go Show resolved Hide resolved
api/internal/usecase/interactor/deployment.go Show resolved Hide resolved
api/internal/usecase/interactor/deployment.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: 13

🧹 Outside diff range and nitpick comments (6)
ui/src/lib/gql/provider/GraphQLRequestMiddleware.ts (1)

48-54: Improve variable naming and type safety

The variable name 'ggg' is non-descriptive and should be renamed to better reflect its purpose.

Apply this diff to improve readability and type safety:

-    const ggg = Object.entries(variableValue)
+    const extractedFiles = Object.entries(variableValue)
       .map(([key, value]: any) =>
         recursiveExtractFiles(key, value, `${prefix}.${variableKey}`),
       )
       .flat();
 
-    return ggg;
+    return extractedFiles;
api/internal/infrastructure/mongo/deployment.go (1)

27-33: Document Init error handling requirements.

The Init method is crucial for setting up MongoDB indexes. Consider documenting:

  1. When Init should be called (app startup?)
  2. How to handle Init failures (retry strategy?)
  3. Impact of missing indexes on query performance
api/internal/infrastructure/fs/file.go (1)

169-176: Add documentation for validateURL helper

While the implementation is correct, consider adding a comment explaining the purpose and validation rules of this helper function.

+// validateURL checks if the given URL matches the base URL's scheme, host,
+// and if it's in the correct path hierarchy. Returns false if either URL is nil.
 func (f *fileRepo) validateURL(u *url.URL, base *url.URL) bool {
ui/src/lib/gql/deployment/useQueries.ts (2)

21-21: Consider making DEPLOYMENT_FETCH_RATE configurable.

The fetch rate for deployments is hardcoded. Consider making this configurable through environment variables or configuration to allow for different pagination sizes in different environments.


151-153: Make the filter's purpose more explicit.

The isDefined filter's purpose isn't immediately clear. Consider adding a type predicate or comment explaining what it's filtering out.

 const deployments: Deployment[] = nodes
-  .filter(isDefined)
+  // Filter out null or undefined nodes that might come from the GraphQL response
+  .filter((node): node is NonNullable<typeof node> => isDefined(node))
   .map((deployment) => createNewDeploymentObject(deployment));
ui/src/lib/gql/deployment/useApi.ts (1)

119-119: Remove console.log statement from production code

The console.log statement on line 119 appears to be left over from debugging. It's recommended to remove it to keep the codebase clean.

Apply this diff to remove the console log:

-         console.log("input", input);
📜 Review details

Configuration used: .coderabbit.yaml
Review profile: CHILL

📥 Commits

Files that changed from the base of the PR and between e95d46d and ef5be90.

📒 Files selected for processing (10)
  • api/internal/infrastructure/fs/file.go (4 hunks)
  • api/internal/infrastructure/memory/deployment.go (1 hunks)
  • api/internal/infrastructure/mongo/deployment.go (2 hunks)
  • api/internal/usecase/interactor/deployment.go (4 hunks)
  • ui/src/lib/gql/deployment/useApi.ts (1 hunks)
  • ui/src/lib/gql/deployment/useQueries.ts (1 hunks)
  • ui/src/lib/gql/provider/GraphQLRequestMiddleware.ts (1 hunks)
  • ui/src/lib/yjs/useYjsStore.ts (4 hunks)
  • ui/src/utils/yamlToFormData.test.ts (1 hunks)
  • ui/src/utils/yamlToFormData.ts (1 hunks)
🚧 Files skipped from review as they are similar to previous changes (2)
  • api/internal/infrastructure/memory/deployment.go
  • ui/src/utils/yamlToFormData.ts
🧰 Additional context used
🪛 Biome
ui/src/lib/gql/provider/GraphQLRequestMiddleware.ts

[error] 9-9: Invalid typeof comparison value: this expression is not a string literal

not a string literal

(lint/suspicious/useValidTypeof)


[error] 12-12: Invalid typeof comparison value: this expression is not a string literal

not a string literal

(lint/suspicious/useValidTypeof)


[error] 81-81: Avoid the use of spread (...) syntax on accumulators.

Spread syntax should be avoided on accumulators (like those in .reduce) because it causes a time complexity of O(n^2).
Consider methods such as .splice or .push instead.

(lint/performance/noAccumulatingSpread)

🔇 Additional comments (18)
ui/src/utils/yamlToFormData.test.ts (1)

1-5: LGTM: Clean and minimal imports

The test setup is properly configured with the necessary imports.

ui/src/lib/gql/provider/GraphQLRequestMiddleware.ts (1)

88-91: Add file validation before upload

The current implementation doesn't validate file size or type before appending to FormData, which could lead to security issues or performance problems with large files.

Consider adding file validation:

   for (let index = 0; index < files.length; index++) {
     const file = files[index];
+    if (file.file instanceof File) {
+      const maxSize = 50 * 1024 * 1024; // 50MB example limit
+      if (file.file.size > maxSize) {
+        throw new Error(`File ${file.file.name} exceeds size limit`);
+      }
+      // Add allowed mime type validation if needed
+    }
     form.append(index.toString(), file.file);
   }

Let's verify if there are any file size limits defined elsewhere in the codebase:

api/internal/infrastructure/mongo/deployment.go (2)

17-20: LGTM! Index definitions are now consistent.

The index definitions look good with "workspaceid" being used consistently throughout the codebase. The unique index on "id" ensures document uniqueness.


42-46: Verify workspace filtering behavior.

The method now includes workspace-based access control. Let's verify this works as expected.

✅ Verification successful

Let me try a different approach to verify the workspace filtering behavior.

These commands will help us:

  1. Find the implementation of findOne to verify workspace filtering logic
  2. Check all find-related methods to ensure consistent workspace filtering
  3. Look for the workspace filter construction logic

Let me check one more aspect to ensure the workspace filtering is properly integrated with the MongoDB queries.


Based on the gathered evidence, I can now provide a final response:

Workspace filtering is properly implemented and verified

The implementation shows a robust workspace-based access control system:

  • The findOne method correctly accepts and applies the filterByWorkspaces parameter
  • When enabled, it uses r.f.Readable workspace IDs for filtering
  • The NewDeploymentConsumer implementation properly filters results based on workspace access using slices.Contains(workspaces, d.Workspace())
  • This pattern is consistently implemented across different repository types (Project, Deployment, Workflow)
🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Description: Check if workspace filtering is consistently applied across all find methods

# Test: Look for other find methods to ensure they also apply workspace filtering
rg -A 5 'func.*find.*workspace' --type go

# Test: Check for potential bypass of workspace filtering
ast-grep --pattern 'func ($r *Deployment) find($ctx, $filter) {
  $$$
  if !$r.f.Can$_($$$) {
    $$$
  }
  $$$
}'

Length of output: 160


Script:

#!/bin/bash
# Check for findOne method implementation and its workspace filtering
ast-grep --pattern 'func ($r *Deployment) findOne($ctx context.Context, $filter bson.M, $workspaces bool) ($result *deployment.Deployment, $err error) {
  $$$
}'

# Look for all find-related methods in the Deployment struct
rg -B 2 -A 10 "func \(\*Deployment\) (Find|find)" --type go

# Check for workspace filter construction
rg -B 2 -A 5 "filterByWorkspaces" --type go

Length of output: 2611


Script:

#!/bin/bash
# Check the DeploymentConsumer implementation to verify workspace filtering
rg -B 2 -A 10 "NewDeploymentConsumer" --type go

# Look for the MongoDB client implementation to see how filters are applied
rg -B 2 -A 10 "func \(\*Client\) FindOne" --type go

Length of output: 4112

api/internal/infrastructure/fs/file.go (5)

21-23: LGTM: Good separation of asset and workflow URL bases

The split into separate URL bases for assets and workflows improves separation of concerns and maintainability.


26-41: Improve error handling in constructor

The error handling could be more informative by:

  1. Distinguishing between asset and workflow URL parsing failures
  2. Including the original error and URL in the message for better debugging

This was previously flagged in an earlier review. Consider applying the suggested improvement:

 func NewFile(fs afero.Fs, assetUrlBase string, workflowUrlBase string) (gateway.File, error) {
 	var err error
 	aurlb, err := url.Parse(assetUrlBase)
 	if err != nil {
-		return nil, errors.New("invalid base URL")
+		return nil, fmt.Errorf("invalid asset base URL %q: %w", assetUrlBase, err)
 	}
 	wurlb, err := url.Parse(workflowUrlBase)
 	if err != nil {
-		return nil, errors.New("invalid base URL")
+		return nil, fmt.Errorf("invalid workflow base URL %q: %w", workflowUrlBase, err)
 	}

54-54: LGTM: Proper URL handling for asset operations

The changes correctly use the asset-specific URL base and include proper URL validation.

Also applies to: 62-62


78-78: LGTM: Proper URL handling for workflow operations

The changes correctly use the workflow-specific URL base and include proper URL validation.

Also applies to: 86-86


169-176: Verify path sanitization for security

The URL validation looks good, but let's verify that all file paths are properly sanitized before use.

✅ Verification successful

Let me gather more context about how the file paths are constructed and used before making the final assessment.


After reviewing the complete implementation, I can now provide the final response:

URL validation and path sanitization are properly implemented

The code implements multiple layers of security for file operations:

  • All filenames are sanitized using sanitize.Path before any file operations
  • File paths are properly constructed using filepath.Join
  • URL validation ensures requests stay within intended base paths
  • New files use randomly generated IDs with controlled extensions
  • Directory traversal is prevented through both URL validation and path sanitization
🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Description: Check for consistent use of sanitize.Path before file operations
# Expect: All file operations should be preceded by sanitize.Path

# Search for file operations
rg -A 2 'f\.fs\.(Open|Create|RemoveAll)' 

# Search for sanitize.Path usage
rg 'sanitize\.Path'

Length of output: 1690


Script:

#!/bin/bash
# Let's examine the full context of file operations and path handling

# Check the full file implementation to understand the flow
cat api/internal/infrastructure/fs/file.go

# Also check for any path manipulation functions
rg -A 3 "filepath\.(Join|Clean|Dir|Base)"

Length of output: 9050

ui/src/lib/yjs/useYjsStore.ts (4)

7-11: LGTM: Imports are well-organized and necessary.

The new imports are properly organized and all are utilized within the component.


27-28: LGTM: Hooks are properly initialized.

The hooks are correctly placed at the top level and are essential for the deployment functionality.


157-157: LGTM: Return value properly updated.

The handleWorkflowDeployment function is correctly exposed in the return object.


134-139: Verify deployment integration.

Let's verify the integration of deployment functionality across the codebase.

✅ Verification successful

Let's gather more specific information about the deployment integration.

The previous results showed the existence of deployment-related files, but we need to verify the actual implementation and integration points. This will help us understand if the deployment functionality is properly integrated across the codebase.


Based on the comprehensive results from the verification scripts, I can now provide a final response about the deployment integration.

Deployment integration is properly implemented

The deployment functionality is well-integrated across the codebase with:

  • Complete GraphQL mutation definitions for create, update, delete, and execute operations
  • Proper type definitions and interfaces in ui/src/types/deployment.ts
  • Centralized deployment hooks in ui/src/lib/gql/deployment/useApi.ts
  • Consistent usage in components like NewRun and useYjsStore
  • Full GraphQL schema support with proper input types and payloads

The createDeployment call in useYjsStore.ts is correctly implemented and integrated with the rest of the deployment system.

🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Description: Verify deployment integration

# Test 1: Check for deployment-related GraphQL mutations
echo "Checking for deployment GraphQL mutations..."
rg -l "mutation.*[Dd]eployment" --type ts

# Test 2: Check for deployment error handling
echo "Checking for deployment error handling..."
rg "catch.*[Dd]eployment" --type ts

# Test 3: Check for deployment loading states
echo "Checking for deployment loading states..."
rg "isDeploying|loading.*[Dd]eployment" --type ts

# Test 4: Check for deployment tests
echo "Checking for deployment tests..."
rg -l "test.*[Dd]eployment" --type test

Length of output: 718


Script:

#!/bin/bash
# Let's examine the deployment-related GraphQL mutations and their usage

# Check the implementation of createDeployment
echo "Checking createDeployment implementation and usage..."
rg "createDeployment" -A 5 --type ts

# Check the GraphQL mutation definition
echo "Checking deployment mutation definition..."
rg "mutation.*[Dd]eployment.*{" -A 10 --type ts

# Check for deployment-related types and interfaces
echo "Checking deployment types..."
rg "type.*Deployment|interface.*Deployment" -A 5 --type ts

# Check for deployment-related hooks and components
echo "Checking deployment hooks and components..."
rg "use.*Deployment|component.*Deployment" --type ts

Length of output: 84274

api/internal/usecase/interactor/deployment.go (1)

5-5: LGTM: URL package import is appropriate

The addition of the net/url package is well-justified for URL parsing operations used in the new deployment management functionality.

ui/src/lib/gql/deployment/useQueries.ts (2)

27-39: ⚠️ Potential issue

Fix useCallback dependencies and add null handling.

Two issues need attention:

  1. The empty dependencies array in useCallback is incorrect as the function uses the deployment parameter.
  2. The project?.name access could be undefined, but the type doesn't reflect this possibility.

Apply this diff to fix the issues:

 const createNewDeploymentObject = useCallback(
   (deployment: DeploymentFragment): Deployment => ({
     id: deployment.id,
     workspaceId: deployment.workspaceId,
     projectId: deployment.projectId,
-    projectName: deployment.project?.name,
+    projectName: deployment.project?.name ?? null,
     workflowUrl: deployment.workflowUrl,
     version: deployment.version,
     createdAt: deployment.createdAt,
     updatedAt: deployment.updatedAt,
   }),
-  [],
+  [deployment],
 );

Likely invalid or redundant comment.


41-69: Add file validation in createDeploymentMutation.

The mutation accepts a FormData file without validating its presence or format. Consider adding validation before making the GraphQL call.

Run this script to check if there's any existing file validation:

ui/src/lib/gql/deployment/useApi.ts (2)

28-51: Function createDeployment is implemented correctly

The function createDeployment correctly handles deployment creation, including form data conversion and user feedback upon success.


106-112: Function useGetDeploymentsInfinite is implemented correctly

The function useGetDeploymentsInfinite correctly retrieves paginated deployment data using an infinite query.

ui/src/utils/yamlToFormData.test.ts Outdated Show resolved Hide resolved
ui/src/utils/yamlToFormData.test.ts Outdated Show resolved Hide resolved
ui/src/utils/yamlToFormData.test.ts Outdated Show resolved Hide resolved
Comment on lines +118 to +140
const handleWorkflowDeployment = useCallback(async () => {
const { workflowId, yamlWorkflow } =
createWorkflowsYaml(
currentProject?.name,
rawWorkflows.map((w): Workflow => {
if (!w) return { id: "", name: "", nodes: [], edges: [] };
const id = w.id instanceof Y.Text ? fromYjsText(w.id) : "";
const name = w.name instanceof Y.Text ? fromYjsText(w.name) : "";
const n = w.nodes as Node[];
const e = w.edges as Edge[];
return { id, name, nodes: n, edges: e };
}),
) ?? {};

if (!yamlWorkflow || !currentProject || !workflowId) return;

await createDeployment(
currentProject.workspaceId,
currentProject.id,
workflowId,
yamlWorkflow,
);
}, [rawWorkflows, currentProject, createDeployment]);
Copy link
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue

Several improvements needed in handleWorkflowDeployment.

While the basic implementation is sound, there are several areas that need attention:

  1. Error handling is missing for the createDeployment call
  2. Type assertions for nodes and edges could be unsafe
  3. No loading state management for deployment in progress
  4. The currentProject?.name parameter is passed but not used in the returned object

Consider applying these improvements:

  const handleWorkflowDeployment = useCallback(async () => {
+   try {
+     setIsDeploying?.(true);
      const { workflowId, yamlWorkflow } =
        createWorkflowsYaml(
-         currentProject?.name,
          rawWorkflows.map((w): Workflow => {
            if (!w) return { id: "", name: "", nodes: [], edges: [] };
            const id = w.id instanceof Y.Text ? fromYjsText(w.id) : "";
            const name = w.name instanceof Y.Text ? fromYjsText(w.name) : "";
-           const n = w.nodes as Node[];
-           const e = w.edges as Edge[];
+           const n = Array.isArray(w.nodes) ? w.nodes : [];
+           const e = Array.isArray(w.edges) ? w.edges : [];
            return { id, name, nodes: n, edges: e };
          }),
        ) ?? {};

      if (!yamlWorkflow || !currentProject || !workflowId) return;

      await createDeployment(
        currentProject.workspaceId,
        currentProject.id,
        workflowId,
        yamlWorkflow,
      );
+   } catch (error) {
+     console.error('Deployment failed:', error);
+     // Handle error appropriately (e.g., show toast notification)
+   } finally {
+     setIsDeploying?.(false);
+   }
-  }, [rawWorkflows, currentProject, createDeployment]);
+  }, [rawWorkflows, currentProject, createDeployment, setIsDeploying]);

Also, consider adding a loading state:

const [isDeploying, setIsDeploying] = useState(false);

Comment on lines 52 to 56
func (i *Deployment) Create(ctx context.Context, dp interfaces.CreateDeploymentParam, operator *usecase.Operator) (result *deployment.Deployment, err error) {
// if err := i.CanWriteWorkspace(p.Workspace, operator); err != nil {
// fmt.Println("HERE0", err.Error())
// return nil, err
// }
Copy link
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue

Security Issue: Workspace permission check is disabled

The workspace permission check is commented out, which could lead to unauthorized access to workspace resources. This is a critical security concern that needs to be addressed.

Apply this diff to restore the security check:

-// if err := i.CanWriteWorkspace(p.Workspace, operator); err != nil {
-// 	fmt.Println("HERE0", err.Error())
-// 	return nil, err
-// }
+if err := i.CanWriteWorkspace(dp.Workspace, operator); err != nil {
+	return nil, err
+}
📝 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 (i *Deployment) Create(ctx context.Context, dp interfaces.CreateDeploymentParam, operator *usecase.Operator) (result *deployment.Deployment, err error) {
// if err := i.CanWriteWorkspace(p.Workspace, operator); err != nil {
// fmt.Println("HERE0", err.Error())
// return nil, err
// }
func (i *Deployment) Create(ctx context.Context, dp interfaces.CreateDeploymentParam, operator *usecase.Operator) (result *deployment.Deployment, err error) {
if err := i.CanWriteWorkspace(dp.Workspace, operator); err != nil {
return nil, err
}

ui/src/lib/gql/deployment/useQueries.ts Show resolved Hide resolved
ui/src/lib/gql/deployment/useQueries.ts Outdated Show resolved Hide resolved
ui/src/lib/gql/deployment/useQueries.ts 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: 10

🧹 Outside diff range and nitpick comments (3)
ui/src/lib/gql/job/useQueries.ts (1)

43-43: Make the enabled condition more explicit

The current condition doesn't check for empty string.

-enabled: !!workspaceId,
+enabled: typeof workspaceId === 'string' && workspaceId.length > 0,
api/internal/usecase/interactor/deployment.go (2)

Line range hint 52-97: Enhance error handling in Create method

While the basic structure is sound, there are several areas where error handling could be improved:

  1. The transaction rollback isn't explicitly handled on error paths
  2. Resource cleanup isn't guaranteed if workflow upload fails

Consider wrapping the workflow upload in a defer cleanup handler:

 func (i *Deployment) Create(ctx context.Context, dp interfaces.CreateDeploymentParam, operator *usecase.Operator) (result *deployment.Deployment, err error) {
+    var workflowURL *url.URL
+    defer func() {
+        if err != nil && workflowURL != nil {
+            _ = i.file.RemoveWorkflow(context.Background(), workflowURL)
+        }
+    }()
     
     // ... existing code ...
     
     url, err := i.file.UploadWorkflow(ctx, dp.Workflow)
     if err != nil {
         return nil, fmt.Errorf("failed to upload workflow: %w", err)
     }
+    workflowURL = url

Line range hint 52-176: Consider implementing middleware for common patterns

The code shows repetitive patterns across methods for:

  1. Transaction handling
  2. Error handling
  3. Resource cleanup
  4. Permission checks

This could be simplified using middleware functions.

Consider implementing these middleware functions:

// Transaction middleware
func (i *Deployment) withTransaction(ctx context.Context, fn func(ctx context.Context) error) error {
    tx, err := i.transaction.Begin(ctx)
    if err != nil {
        return err
    }
    defer tx.End(tx.Context())
    
    if err := fn(tx.Context()); err != nil {
        return err
    }
    
    tx.Commit()
    return nil
}

// Permission middleware
func (i *Deployment) withWorkspacePermission(ctx context.Context, wsID accountdomain.WorkspaceID, operator *usecase.Operator, fn func(ctx context.Context) error) error {
    if err := i.CanWriteWorkspace(wsID, operator); err != nil {
        return err
    }
    return fn(ctx)
}

This would simplify the methods and reduce code duplication while maintaining the same functionality.

📜 Review details

Configuration used: .coderabbit.yaml
Review profile: CHILL

📥 Commits

Files that changed from the base of the PR and between ef5be90 and 20d2a83.

📒 Files selected for processing (10)
  • api/internal/usecase/interactor/deployment.go (4 hunks)
  • ui/src/lib/gql/deployment/useQueries.ts (1 hunks)
  • ui/src/lib/gql/job/useQueries.ts (1 hunks)
  • ui/src/lib/gql/project/useApi.ts (3 hunks)
  • ui/src/lib/i18n/locales/en.json (3 hunks)
  • ui/src/lib/i18n/locales/es.json (3 hunks)
  • ui/src/lib/i18n/locales/fr.json (3 hunks)
  • ui/src/lib/i18n/locales/ja.json (3 hunks)
  • ui/src/lib/i18n/locales/zh.json (3 hunks)
  • ui/src/utils/yamlToFormData.test.ts (1 hunks)
🚧 Files skipped from review as they are similar to previous changes (6)
  • ui/src/lib/gql/deployment/useQueries.ts
  • ui/src/lib/gql/project/useApi.ts
  • ui/src/lib/i18n/locales/es.json
  • ui/src/lib/i18n/locales/ja.json
  • ui/src/lib/i18n/locales/zh.json
  • ui/src/utils/yamlToFormData.test.ts
🧰 Additional context used
🪛 Biome
ui/src/lib/gql/job/useQueries.ts

[error] 75-75: Useless case clause.

because the default clause is present:

Unsafe fix: Remove the useless case.

(lint/complexity/noUselessSwitchCase)

🔇 Additional comments (3)
ui/src/lib/i18n/locales/en.json (1)

193-194: Verify the usage of debug run messages.

Since the PR objectives mention that debug run execution is not finalized, let's verify if these messages are actually being used in the codebase.

✅ Verification successful

Let me try a different search strategy to verify the usage of these messages.

The previous search failed due to unrecognized file type. This new script:

  1. Searches more broadly without file type restrictions
  2. Includes potential translation function usage patterns (common in i18n implementations)
  3. Uses a shorter search string to catch potential variations

Let's examine the content of useApi.ts to see how these messages are being used.


Debug run messages are actively used in the codebase

The messages are properly used in ui/src/lib/gql/project/useApi.ts as toast notifications when a debug run is started. The translation keys are correctly implemented and used with the t() translation function, which aligns with the PR objectives of implementing debug run functionality.

🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Description: Check if the debug run messages are used in the codebase

# Test: Search for usage of debug run messages
echo "Searching for debug run message usage..."
rg -l "Debug run started" --type ts --type tsx
rg -l "Debug run has been successfully started for the project" --type ts --type tsx

Length of output: 282


Script:

#!/bin/bash
# Description: Check if the debug run messages are used in the codebase using different search patterns

# Search in all files (not limiting to specific extensions)
echo "Searching for debug run message usage..."
rg -l "Debug run" 

# Also search for potential translation key usage patterns
echo -e "\nSearching for translation key usage patterns..."
rg -l "t\(['\"]Debug run started" 
rg -l "t\(`Debug run started" 

Length of output: 580


Script:

#!/bin/bash
# Check the usage context in useApi.ts
echo "Checking usage context in useApi.ts..."
rg "Debug run" -C 5 ui/src/lib/gql/project/useApi.ts

Length of output: 512

ui/src/lib/i18n/locales/fr.json (2)

49-57: LGTM: Deployment workflow translations are accurate and consistent

The translations maintain consistent terminology and are grammatically correct.


184-189: LGTM: Deployment status messages are well translated

The translations are accurate, maintain proper French grammar, and follow correct capitalization rules.

Comment on lines +67 to +79
function toJobStatus(status: GraphqlJobStatus): JobStatus {
switch (status) {
case "RUNNING":
return "running";
case "COMPLETED":
return "completed";
case "FAILED":
return "failed";
case "PENDING":
default:
return "pending";
}
}
Copy link
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue

Remove redundant PENDING case in switch statement

The PENDING case is redundant as it's already handled by the default case.

 function toJobStatus(status: GraphqlJobStatus): JobStatus {
   switch (status) {
     case "RUNNING":
       return "running";
     case "COMPLETED":
       return "completed";
     case "FAILED":
       return "failed";
-    case "PENDING":
     default:
       return "pending";
   }
 }
📝 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
function toJobStatus(status: GraphqlJobStatus): JobStatus {
switch (status) {
case "RUNNING":
return "running";
case "COMPLETED":
return "completed";
case "FAILED":
return "failed";
case "PENDING":
default:
return "pending";
}
}
function toJobStatus(status: GraphqlJobStatus): JobStatus {
switch (status) {
case "RUNNING":
return "running";
case "COMPLETED":
return "completed";
case "FAILED":
return "failed";
default:
return "pending";
}
}
🧰 Tools
🪛 Biome

[error] 75-75: Useless case clause.

because the default clause is present:

Unsafe fix: Remove the useless case.

(lint/complexity/noUselessSwitchCase)

GetJob = "getJob",
}

const JOBS_FETCH_RATE = 15;
Copy link
Contributor

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

Consider making JOBS_FETCH_RATE configurable

Consider moving this constant to a configuration file or environment variable to allow for different fetch rates in different environments without code changes.

-const JOBS_FETCH_RATE = 15;
+import { config } from "../config";
+const JOBS_FETCH_RATE = config.jobsFetchRate ?? 15;

Committable suggestion was skipped due to low confidence.

Comment on lines +19 to +49
const useGetJobsInfiniteQuery = (workspaceId?: string) =>
useInfiniteQuery({
queryKey: [JobQueryKeys.GetJobs, workspaceId],
initialPageParam: null,
queryFn: async ({ pageParam }) => {
const data = await graphQLContext?.GetJobs({
workspaceId: workspaceId ?? "",
pagination: {
first: JOBS_FETCH_RATE,
after: pageParam,
},
});
if (!data) return;
const {
jobs: {
nodes,
pageInfo: { endCursor, hasNextPage },
},
} = data;
const jobs: Job[] = nodes
.filter(isDefined)
.map((job) => createNewJobObject(job));
return { jobs, endCursor, hasNextPage };
},
enabled: !!workspaceId,
getNextPageParam: (lastPage) => {
if (!lastPage) return undefined;
const { endCursor, hasNextPage } = lastPage;
return hasNextPage ? endCursor : undefined;
},
});
Copy link
Contributor

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 and type safety in useGetJobsInfiniteQuery

Consider adding error type definition and handling network errors explicitly.

 useInfiniteQuery({
   queryKey: [JobQueryKeys.GetJobs, workspaceId],
   initialPageParam: null,
+  retry: 3,
   queryFn: async ({ pageParam }) => {
-    const data = await graphQLContext?.GetJobs({
-      workspaceId: workspaceId ?? "",
+    if (!graphQLContext) {
+      throw new Error("GraphQL context is not initialized");
+    }
+    const data = await graphQLContext.GetJobs({
+      workspaceId: workspaceId,
       pagination: {

Committable suggestion was skipped due to low confidence.

Comment on lines +81 to +90
export function createNewJobObject(job: JobFragment): Job {
return {
id: job.id,
deploymentId: job.deploymentId,
workspaceId: job.workspaceId,
status: toJobStatus(job.status),
startedAt: job.startedAt,
completedAt: job.completedAt,
};
}
Copy link
Contributor

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

Add input validation and handle nullable fields in createNewJobObject

Consider adding validation for required fields and proper handling of nullable dates.

 export function createNewJobObject(job: JobFragment): Job {
+  if (!job.id || !job.workspaceId) {
+    throw new Error("Invalid job data: missing required fields");
+  }
   return {
     id: job.id,
     deploymentId: job.deploymentId,
     workspaceId: job.workspaceId,
     status: toJobStatus(job.status),
-    startedAt: job.startedAt,
-    completedAt: job.completedAt,
+    startedAt: job.startedAt ?? null,
+    completedAt: job.completedAt ?? null,
   };
 }
📝 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
export function createNewJobObject(job: JobFragment): Job {
return {
id: job.id,
deploymentId: job.deploymentId,
workspaceId: job.workspaceId,
status: toJobStatus(job.status),
startedAt: job.startedAt,
completedAt: job.completedAt,
};
}
export function createNewJobObject(job: JobFragment): Job {
if (!job.id || !job.workspaceId) {
throw new Error("Invalid job data: missing required fields");
}
return {
id: job.id,
deploymentId: job.deploymentId,
workspaceId: job.workspaceId,
status: toJobStatus(job.status),
startedAt: job.startedAt ?? null,
completedAt: job.completedAt ?? null,
};
}

Comment on lines +98 to +141
func (i *Deployment) Update(ctx context.Context, dp interfaces.UpdateDeploymentParam, operator *usecase.Operator) (_ *deployment.Deployment, err error) {
tx, err := i.transaction.Begin(ctx)
if err != nil {
return
}

ctx = tx.Context()
defer func() {
if err2 := tx.End(ctx); err == nil && err2 != nil {
err = err2
}
}()

d, err := i.deploymentRepo.FindByID(ctx, dp.ID)
if err != nil {
return nil, err
}
if err := i.CanWriteWorkspace(d.Workspace(), operator); err != nil {
return nil, err
}

if dp.Workflow != nil {
if url, _ := url.Parse(d.WorkflowUrl()); url != nil {
if err := i.file.RemoveWorkflow(ctx, url); err != nil {
return nil, err
}
}

url, err := i.file.UploadWorkflow(ctx, dp.Workflow)
if err != nil {
return nil, err
}
d.SetWorkflowUrl(url.String())
}

// d.SetVersion() // version is hardcoded for now but will need to be incremented here eventually

if err := i.deploymentRepo.Save(ctx, d); err != nil {
return nil, err
}

tx.Commit()
return d, nil
}
Copy link
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue

Add validation for workflow content

The method should validate the workflow content before attempting to upload it.

Add validation before processing the workflow:

 if dp.Workflow != nil {
+    if len(dp.Workflow.Content) == 0 {
+        return nil, fmt.Errorf("workflow content cannot be empty")
+    }
+
     if url, _ := url.Parse(d.WorkflowUrl()); url != nil {

Additionally, consider adding a transaction middleware to handle rollback consistently across all methods.

Consider implementing a transaction middleware that can be reused across all methods:

func (i *Deployment) withTransaction(ctx context.Context, fn func(ctx context.Context) error) error {
    tx, err := i.transaction.Begin(ctx)
    if err != nil {
        return err
    }
    
    ctx = tx.Context()
    defer func() {
        if err2 := tx.End(ctx); err == nil && err2 != nil {
            err = err2
        }
    }()
    
    if err := fn(ctx); err != nil {
        return err
    }
    
    tx.Commit()
    return nil
}

Comment on lines +143 to +176
func (i *Deployment) Delete(ctx context.Context, deploymentID id.DeploymentID, operator *usecase.Operator) (err error) {
tx, err := i.transaction.Begin(ctx)
if err != nil {
return
}

ctx = tx.Context()
defer func() {
if err2 := tx.End(ctx); err == nil && err2 != nil {
err = err2
}
}()

dep, err := i.deploymentRepo.FindByID(ctx, deploymentID)
if err != nil {
return err
}
if err := i.CanWriteWorkspace(dep.Workspace(), operator); err != nil {
return err
}

if url, _ := url.Parse(dep.WorkflowUrl()); url != nil {
if err := i.file.RemoveWorkflow(ctx, url); err != nil {
return err
}
}

if err := i.deploymentRepo.Remove(ctx, deploymentID); err != nil {
return err
}

tx.Commit()
return nil
}
Copy link
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue

Add deployment existence check before deletion

The method should verify that the deployment exists and is in a deletable state before proceeding with deletion.

Add state validation before deletion:

 dep, err := i.deploymentRepo.FindByID(ctx, deploymentID)
 if err != nil {
     return err
 }
+
+// Check if deployment has any active jobs
+jobs, err := i.jobRepo.FindByDeployment(ctx, deploymentID)
+if err != nil {
+    return fmt.Errorf("failed to check deployment jobs: %w", err)
+}
+for _, j := range jobs {
+    if j.Status() == job.StatusRunning || j.Status() == job.StatusPending {
+        return fmt.Errorf("cannot delete deployment with active jobs")
+    }
+}

Committable suggestion was skipped due to low confidence.

@@ -46,14 +46,15 @@
"Canvas Navigation": "Canvas Navigation",
"Actions list": "",
"Dashboard": "Dashboard",
"Deploy project workflow": "",
"Deploy project's workflow": "",
Copy link
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue

Resolve duplicate deployment keys with inconsistent wording.

There are two similar but differently worded keys for deployment:

  • "Deploy project's workflow"
  • "Deploy project workflow"

This could lead to confusion and maintenance issues. Consider consolidating these into a single key.

Choose one consistent format, preferably:

-  "Deploy project's workflow": "",
-  "Deploy project workflow": "",
+  "Deploy project workflow": "",

Also applies to: 57-57

Comment on lines +193 to +194
"Debug run started": "",
"Debug run has been successfully started for the project.": "",
Copy link
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue

Add missing debug run notification messages.

The debug run notification messages are currently empty. These messages are important for providing feedback during debug operations.

Suggested translations:

-  "Debug run started": "",
-  "Debug run has been successfully started for the project.": "",
+  "Debug run started": "Debug run started",
+  "Debug run has been successfully started for the project.": "Debug run has been successfully started for the project."
📝 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
"Debug run started": "",
"Debug run has been successfully started for the project.": "",
"Debug run started": "Debug run started",
"Debug run has been successfully started for the project.": "Debug run has been successfully started for the project."

Comment on lines +112 to +114
"Deployment": "Déploiement",
"Select from published projects": "Sélectionner parmi les projets publiés",
"Unnamed project": "Projet sans nom",
Copy link
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue

Update "Unnamed project" translation for consistency

To maintain consistency with the shift from project to deployment terminology:

-  "Unnamed project": "Projet sans nom",
+  "Unnamed project": "Déploiement sans nom",
📝 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
"Deployment": "Déploiement",
"Select from published projects": "Sélectionner parmi les projets publiés",
"Unnamed project": "Projet sans nom",
"Deployment": "Déploiement",
"Select from published projects": "Sélectionner parmi les projets publiés",
"Unnamed project": "Déploiement sans nom",

Comment on lines +193 to +194
"Debug run started": "Déboguer l",
"Debug run has been successfully started for the project.": "Le débogage a été démarré avec succès pour le projet.",
Copy link
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue

Fix incomplete and incorrect debug run translation

The debug run translation has two issues:

  1. Line 193 has an incomplete translation ending with "l"
  2. The translation doesn't accurately reflect the English meaning
-  "Debug run started": "Déboguer l",
+  "Debug run started": "Débogage démarré",
📝 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
"Debug run started": "Déboguer l",
"Debug run has been successfully started for the project.": "Le débogage a été démarré avec succès pour le projet.",
"Debug run started": "Débogage démarré",
"Debug run has been successfully started for the project.": "Le débogage a été démarré avec succès pour le projet.",

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: 2

📜 Review details

Configuration used: .coderabbit.yaml
Review profile: CHILL

📥 Commits

Files that changed from the base of the PR and between 20d2a83 and 395fa89.

⛔ Files ignored due to path filters (1)
  • ui/yarn.lock is excluded by !**/yarn.lock, !**/*.lock, !**/*.lock
📒 Files selected for processing (6)
  • ui/package.json (2 hunks)
  • ui/src/routeTree.gen.ts (8 hunks)
  • ui/src/routes/workspaces_.$workspaceId.lazy.tsx (1 hunks)
  • ui/src/routes/workspaces_.$workspaceId_.projects.$projectId.lazy.tsx (1 hunks)
  • ui/src/routes/workspaces_.$workspaceId_.runs.$tab.lazy.tsx (1 hunks)
  • ui/src/routes/workspaces_.$workspaceId_.settings.$tab.lazy.tsx (1 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
  • ui/package.json
🔇 Additional comments (4)
ui/src/routes/workspaces_.$workspaceId.lazy.tsx (1)

6-6: ⚠️ Breaking Change: Route path modification requires migration strategy

The change in route pattern from /workspaces/$workspaceId to /workspaces_/$workspaceId is a breaking change that will affect existing users. Consider implementing the following:

  1. Add route redirects from the old pattern to maintain backward compatibility
  2. Update all internal navigation links to use the new pattern
  3. Document this change in release notes
  4. Consider implementing a gradual migration strategy

Consider implementing a route constants file to centralize route definitions and make future changes easier to manage. This would help avoid similar breaking changes in the future.

ui/src/routes/workspaces_.$workspaceId_.settings.$tab.lazy.tsx (1)

7-7: LGTM, but verify URL handling.

The route path update aligns with the standardization of using underscores in route definitions across the application.

Let's verify the consistency of this pattern across other route files and check for any potential breaking changes:

✅ Verification successful

Based on the results, I can now generate the final response since we have sufficient information from the executed shell scripts.

Route pattern is consistently implemented across workspace routes

The verification shows that all workspace-related routes in the codebase consistently use the underscore pattern (workspaces_/$workspaceId_). This includes:

  • Projects route: /workspaces_/$workspaceId_/projects/$projectId
  • Runs route: /workspaces_/$workspaceId_/runs/$tab
  • Base workspace route: /workspaces_/$workspaceId
  • Settings route (current change): /workspaces_/$workspaceId_/settings/$tab

The change aligns with the existing pattern, and there are no remaining instances of the old slash-based format (/workspaces/$workspaceId).

🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Description: Check route patterns across all route files
# Expected: All workspace-related routes should follow the same underscore pattern

# Search for route definitions in all route files
rg -A 1 "createLazyFileRoute\(" "ui/src/routes/"

# Search for any remaining slash-based workspace routes that might need updating
rg "/workspaces/\$workspaceId" "ui/src/routes/"

Length of output: 941

ui/src/routes/workspaces_.$workspaceId_.projects.$projectId.lazy.tsx (1)

13-13: Breaking Change: URL structure modification.

The change in route path from /workspaces/$workspaceId/projects/$projectId to /workspaces_/$workspaceId_/projects/$projectId is a breaking change that could affect:

  • Existing bookmarks
  • External links to specific projects
  • Internal navigation

Please ensure:

  1. This change is documented in release notes
  2. Proper redirects are in place for old URLs
  3. All internal navigation uses the new URL structure
ui/src/routeTree.gen.ts (1)

21-30: Verify the source of route generation changes.

This is an auto-generated file by TanStack Router. The systematic changes in route IDs (adding underscores) while maintaining the original paths suggest an intentional modification in the router configuration or route generation process.

Please ensure that:

  1. These changes were made through the appropriate router configuration
  2. The discrepancy between IDs (with underscores) and paths (without underscores) is intentional
  3. The route handling logic in the application correctly handles these modified route IDs

Let's verify the router configuration and related files:

Also applies to: 42-79, 93-172, 207-226

✅ Verification successful

Note: I adjusted the paths to include the ui/ prefix since the file under review is in ui/src/routeTree.gen.ts. This should help us locate the router configuration and related files.


This will help us understand the router setup and configuration that generates these routes with underscores.


This will help us understand if the underscore modifications are coming from the router's Vite plugin configuration or the route file structure itself.


Route structure changes are correctly generated and intentional

The underscore modifications in the route IDs are intentional and correct. This is evidenced by:

  1. The presence of matching route files in ui/src/routes/ with the same underscore pattern:

    • workspaces_.$workspaceId.lazy.tsx
    • workspaces_.$workspaceId_.projects.$projectId.lazy.tsx
    • workspaces_.$workspaceId_.runs.$tab.lazy.tsx
    • workspaces_.$workspaceId_.settings.$tab.lazy.tsx
  2. The TanStack Router Vite plugin is properly configured in vite.config.ts and is responsible for generating the routeTree.gen.ts file based on these route files.

The discrepancy between route IDs (with underscores) and actual URL paths (without underscores) is a standard TanStack Router convention for file-based routing, where underscores in filenames are used to structure routes without affecting the final URL paths.

🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Check for router configuration files and route components
echo "Checking for router configuration files..."
fd -e ts -e tsx "router|route" src/

# Check for any potential route handling code
echo "Checking for route handling code..."
rg -l "useRoute|useNavigate|useParams" src/

Length of output: 396


Script:

#!/bin/bash
# Let's first check the repository structure to understand where the UI code resides
ls -la

# Then check for router configuration files and route components in the UI directory
echo "Checking for router configuration files..."
fd -e ts -e tsx "router|route" ui/src/

# Check for any potential route handling code
echo "Checking for route handling code..."
rg -l "useRoute|useNavigate|useParams" ui/src/

# Look for TanStack Router configuration
echo "Checking for TanStack Router configuration..."
rg -l "@tanstack/router" ui/

Length of output: 2189


Script:

#!/bin/bash
# Let's check the root route configuration and router setup
echo "Checking root route configuration..."
cat ui/src/routes/__root.tsx

# Let's also check the package.json for TanStack Router version and configuration
echo "Checking package.json for router configuration..."
cat ui/package.json | grep -A 5 -B 5 "@tanstack/router"

# Check if there are any route generation scripts or configs
echo "Checking for route generation configs..."
fd -e js -e ts -e json "route|router" ui/ --exclude "routeTree.gen.ts"

Length of output: 2594


Script:

#!/bin/bash
# Let's check the vite config since TanStack Router uses a Vite plugin
echo "Checking Vite configuration..."
cat ui/vite.config.ts

# Let's also check for any route files in the routes directory
echo "Checking route files..."
ls -la ui/src/routes/

Length of output: 3379

Comment on lines +6 to +8
export const Route = createLazyFileRoute(
"/workspaces_/$workspaceId_/runs/$tab",
)({
Copy link
Contributor

Choose a reason for hiding this comment

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

💡 Codebase verification

Route path changes are part of a coordinated update, but one inconsistency found

The route path changes are consistently implemented across all route definitions, but there's one inconsistency in ui/src/features/Dashboard/components/MainSection/hooks.ts where an old-style route path is still being used:

const navigate = useNavigate({ from: "/workspaces/$workspaceId" });

This should be updated to use the new format with underscores:

const navigate = useNavigate({ from: "/workspaces_/$workspaceId_" });
🔗 Analysis chain

Route path modification needs documentation update.

The route path change aligns with the broader routing structure updates. However, this change in URL structure might affect existing bookmarks or direct URL access.

Let's verify the consistency of this change across the codebase:


Let me try a different approach to verify the routing changes.

This script will help us:

  1. Find all route definitions to verify consistency in the new routing pattern
  2. Check for any direct URL references in components that might need updating
  3. Look for programmatic navigation calls that might be affected

Let's analyze the results to determine if the route changes are consistent and if there are any places that need attention.


Let me try one final verification to check for any component references with a different approach:

This will help us find any remaining references to workspace routes in the codebase, regardless of file type, to ensure we haven't missed any potential places that need updating.

🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Description: Check for any inconsistencies in workspace route patterns
# and find potential documentation that needs updating

# Check for any remaining old-style workspace routes
echo "Checking for any remaining old-style workspace routes..."
rg "/workspaces/\$workspaceId/" --type ts

# Look for potential documentation files that might need updating
echo "Checking for documentation files that might need updating..."
fd -e md -e mdx | xargs rg -l "/workspaces/[^_]"

Length of output: 347


Script:

#!/bin/bash
# Check for all route definitions and URL patterns in the codebase
echo "Checking all route patterns in the codebase..."
rg "createLazyFileRoute|createFileRoute" -A 2 --type ts

echo "Checking for any URL references in the codebase..."
rg "to=\"/workspaces" --type tsx
rg "path=\"/workspaces" --type tsx
rg "navigate\(\"/workspaces" --type ts

Length of output: 4005


Script:

#!/bin/bash
# Check for any URL references without file type restrictions
echo "Checking for URL references in components..."
rg "to=\"/workspaces"
rg "path=\"/workspaces"
rg "navigate\(\"/workspaces"

# Check for any string literals containing workspace paths
echo "Checking for string literals with workspace paths..."
rg "'/workspaces"
rg "\"/workspaces"

Length of output: 5689

@@ -10,7 +10,7 @@ import { useFullscreen, useShortcuts } from "@flow/hooks";
// import { useShortcut } from "@flow/hooks/useShortcut";

export const Route = createLazyFileRoute(
"/workspaces/$workspaceId/projects/$projectId",
"/workspaces_/$workspaceId_/projects/$projectId",
Copy link
Contributor

Choose a reason for hiding this comment

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

💡 Codebase verification

Route path inconsistency detected between route definitions and navigation code

Based on the verification results, there is a clear inconsistency between route definitions and actual usage in the codebase:

  1. Route definitions in routeTree.gen.ts and route files use underscores: /workspaces_/$workspaceId_/...
  2. But all navigation code and links in the application use paths without underscores: /workspaces/${workspaceId}/...

Examples of inconsistent usage:

  • features/WorkspaceSettings/index.tsx: /workspaces/${workspaceId}/settings/${t}
  • features/TopNavigation/index.tsx: /workspaces/${currentWorkspace?.id}
  • features/Runs/index.tsx: /workspaces/${currentWorkspace?.id}/runs/${tab}
  • And many more similar instances

This mismatch between route definitions and navigation code will cause routing failures in the application. The changes need to be synchronized - either all paths should use underscores or none should.

🔗 Analysis chain

Verify consistent URL structure across the application.

The route path has been updated to use underscores around the workspaceId parameter. Let's verify this pattern is consistently applied across all routes.


Let me generate another script to check for path references with the correct file type specification:

The previous results showed that the route definitions are consistent with underscores, but we need to verify path references in the codebase and navigation usage to ensure full consistency. This new script will help us gather that information.


Let me try one final verification with a modified script:

This script uses a different approach to search for workspace paths and also examines the content of route files directly. This will help us make a final determination about the URL structure consistency.

🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Description: Check for inconsistent route patterns across the application
# Expected: All workspace routes should follow the same pattern with underscores

echo "Checking for route definitions:"
rg -A 1 "createLazyFileRoute|createFileRoute" "ui/src/routes"

echo -e "\nChecking for direct path references:"
rg "/workspaces[/_]" --type ts --type tsx

Length of output: 2219


Script:

#!/bin/bash
# Description: Check for route patterns and path references with correct file type
# First, let's check all route files
echo "All route files in the repository:"
fd ".*\.(tsx|ts)$" ui/src/routes

echo -e "\nChecking for path references:"
# Using a more specific pattern to catch various forms of workspace paths
rg -t typescript -t tsx "/workspaces[^\"']*" 

# Check for any navigation or link components that might use these paths
echo -e "\nChecking for navigation/link usage:"
ast-grep --pattern 'navigate($$$"/workspaces$$$")' 
ast-grep --pattern '<Link $$$to="/workspaces$$$"$$$>'

Length of output: 756


Script:

#!/bin/bash
# Description: Final check for workspace path references with correct syntax
echo "Checking for path references in TypeScript/TSX files:"
rg --type-add 'ts:*.{ts,tsx}' -t ts '/workspaces[^"]*'

echo -e "\nChecking route file contents:"
for file in $(fd '.*\.(tsx|ts)$' ui/src/routes); do
  echo -e "\n=== $file ==="
  head -n 20 "$file"
done

Length of output: 12124

@KaWaite KaWaite merged commit 005f304 into main Oct 24, 2024
18 checks passed
@KaWaite KaWaite deleted the ui-convert-workflow branch October 24, 2024 05:34
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