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

Preview/notes #7278

Closed
wants to merge 27 commits into from
Closed

Preview/notes #7278

wants to merge 27 commits into from

Conversation

philemone
Copy link
Contributor

@philemone philemone commented Dec 3, 2024

Describe your changes

Checklist before merge

  • Related issue ID is placed at the beginning of PR title in [brackets] (can be GH issue or Nu Jira issue)
  • Code is cleaned from temporary changes and commented out lines
  • Parts of the code that are not easy to understand are documented in the code
  • Changes are covered by automated tests
  • Showcase in dev-application.conf added to demonstrate the feature
  • Documentation added or updated
  • Added entry in Changelog.md describing the change from the perspective of a public distribution user
  • Added MigrationGuide.md entry in the appropriate subcategory if introducing a breaking change
  • Verify that PR will be squashed during merge

Summary by CodeRabbit

  • New Features

    • Introduced sticky notes functionality, allowing users to create, update, delete, and manage sticky notes within scenarios.
    • Added a new API endpoint for managing sticky notes, supporting CRUD operations.
    • Enhanced the UI to accommodate sticky notes, including a dedicated component for sticky note previews.
  • Improvements

    • Updated configuration settings for sticky notes, allowing customization of content length and maximum note count.
    • Improved error handling and validation for sticky notes within the API.
  • Documentation

    • Updated changelog and migration guide to reflect new sticky notes features and configuration changes.

philemone and others added 27 commits December 3, 2024 17:46
@github-actions github-actions bot added client client main fe docs ui labels Dec 3, 2024
Copy link

coderabbitai bot commented Dec 3, 2024

📝 Walkthrough

Walkthrough

The pull request introduces significant enhancements related to sticky notes within the Nussknacker Designer application. It adds a new Cypress end-to-end test for the Creator toolbar, specifically including interactions with a new "stickyNotes" element. Additionally, a new dependency, "dompurify", is introduced to the project's package.json for HTML sanitization. The action types for sticky notes are defined, including "STICKY_NOTES_UPDATED" and "STICKY_NOTE_DELETED". New settings for sticky notes are defined, allowing for configuration of maximum content length, note count, and enabling/disabling functionality. The implementation includes CRUD operations for sticky notes in the backend, with corresponding API endpoints for managing these notes. Furthermore, the database schema is updated to accommodate sticky notes, and various UI components are modified to support sticky note interactions. Overall, the changes enhance the application's capability to manage sticky notes effectively, integrating them into both the frontend and backend systems.

Possibly related PRs

Suggested labels

ui, docs, client, submodules


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.

CodeRabbit Configuration File (.coderabbit.yaml)

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

Documentation and Community

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

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 13

🧹 Outside diff range and nitpick comments (58)
docs/Changelog.md (1)

13-16: LGTM! Consider adding migration notes if needed.

The changelog entries for the StickyNotes feature are well documented and follow the established format. The description clearly explains the feature's purpose and configuration options.

Consider adding a note about any migration steps needed if this feature requires changes to existing configurations or database schema updates.

designer/server/src/main/scala/pl/touk/nussknacker/ui/api/description/stickynotes/Dtos.scala (4)

20-25: Ensure Consistent Use of Derivation for Encoders and Decoders

For StickyNoteId and StickyNoteCorrelationId, encoders and decoders are manually implemented. In contrast, other case classes use @derive(encoder, decoder, schema) to auto-generate these instances. For consistency and to reduce boilerplate, consider using @derive for these classes unless custom logic is necessary.

Apply this change:

+import derevo.circe.{decoder, encoder}
...
 @derive(encoder, decoder)
 final case class StickyNoteId(value: Long) extends AnyVal

 @derive(encoder, decoder)
 final case class StickyNoteCorrelationId(value: UUID) extends AnyVal

- object StickyNoteId {
-     implicit val encoder: Encoder[StickyNoteId] = Encoder.encodeLong.contramap(_.value)
-     implicit val decoder: Decoder[StickyNoteId] = Decoder.decodeLong.map(StickyNoteId(_))
- }

- object StickyNoteCorrelationId {
-     implicit val encoder: Encoder[StickyNoteCorrelationId] = Encoder.encodeUUID.contramap(_.value)
-     implicit val decoder: Decoder[StickyNoteCorrelationId] = Decoder.decodeUUID.map(StickyNoteCorrelationId(_))
- }

Also applies to: 27-32


57-64: Abstract Common Fields in Request Classes to Reduce Duplication

Both StickyNoteAddRequest and StickyNoteUpdateRequest share several common fields. To enhance maintainability and adhere to the DRY (Don't Repeat Yourself) principle, consider abstracting the shared fields into a base trait or class.

Example refactoring:

trait StickyNoteRequestBase {
  def scenarioVersionId: VersionId
  def content: String
  def layoutData: LayoutData
  def color: String
  def dimensions: Dimensions
  def targetEdge: Option[String]
}

@derive(encoder, decoder, schema)
case class StickyNoteAddRequest(
    scenarioVersionId: VersionId,
    content: String,
    layoutData: LayoutData,
    color: String,
    dimensions: Dimensions,
    targetEdge: Option[String]
) extends StickyNoteRequestBase

@derive(encoder, decoder, schema)
case class StickyNoteUpdateRequest(
    noteId: StickyNoteId,
    scenarioVersionId: VersionId,
    content: String,
    layoutData: LayoutData,
    color: String,
    dimensions: Dimensions,
    targetEdge: Option[String]
) extends StickyNoteRequestBase

Also applies to: 67-75


83-85: Externalize Default Settings for Configurability

The default values for StickyNotesSettings are hardcoded, which can limit flexibility. To allow easy adjustments without changing the codebase, consider externalizing these defaults to a configuration file.

Example:

  • Define the default settings in a configuration file (e.g., application.conf):

    stickyNotesSettings {
      maxContentLength = 5000
      maxNotesCount = 5
      enabled = true
    }
  • Modify StickyNotesSettings to load values from the configuration:

    import com.typesafe.config.ConfigFactory
    
    object StickyNotesSettings {
      private val config = ConfigFactory.load().getConfig("stickyNotesSettings")
    
      val default: StickyNotesSettings = StickyNotesSettings(
        maxContentLength = config.getInt("maxContentLength"),
        maxNotesCount = config.getInt("maxNotesCount"),
        enabled = config.getBoolean("enabled")
      )
    }

99-116: Refactor Error Codecs to Eliminate Repetitive Patterns

The implicit codecs for the error cases in StickyNotesError exhibit repetitive code structures. To enhance readability and reduce duplication, consider introducing a helper method to generate these codecs.

Refactored code:

private def createErrorCodec[T](message: T => String): Codec[String, T, CodecFormat.TextPlain] =
  BaseEndpointDefinitions.toTextPlainCodecSerializationOnly[T](message)

implicit val noScenarioCodec: Codec[String, NoScenario, CodecFormat.TextPlain] =
  createErrorCodec(e => s"No scenario ${e.scenarioName} found")

implicit val noStickyNoteCodec: Codec[String, NoStickyNote, CodecFormat.TextPlain] =
  createErrorCodec(e => s"No sticky note with id: ${e.noteId} was found")

implicit val stickyNoteContentTooLongCodec: Codec[String, StickyNoteContentTooLong, CodecFormat.TextPlain] =
  createErrorCodec(e => s"Provided note content is too long (${e.count} characters). Max content length is ${e.max}.")

implicit val stickyNoteCountLimitReachedCodec: Codec[String, StickyNoteCountLimitReached, CodecFormat.TextPlain] =
  createErrorCodec(e => s"Cannot add another sticky note, since max number of sticky notes was reached: ${e.max} (see configuration).")
designer/server/src/main/scala/db/migration/V1_060__CreateStickyNotesDefinition.scala (1)

21-26: Ensure proper logging outside of database actions.

Logging inside the for comprehension may not execute as intended within the database action. Consider moving the logging statements outside the database actions for clarity and to ensure they execute correctly.

Apply this diff to adjust the logging:

       logger.info("Starting migration V1_060__CreateStickyNotesDefinition")
-      for {
+      val actions = for {
         _ <- definitions.stickyNotesEntityTable.schema.create
         _ <-
           sqlu"""ALTER TABLE "sticky_notes" ADD CONSTRAINT "sticky_notes_scenario_version_fk" FOREIGN KEY ("scenario_id", "scenario_version_id") REFERENCES "process_versions" ("process_id", "id") ON DELETE CASCADE;"""
       } yield ()
-      } yield logger.info("Execution finished for migration V1_060__CreateStickyNotesDefinition")
+      actions.andThen(DBIO.successful(logger.info("Execution finished for migration V1_060__CreateStickyNotesDefinition")))
designer/server/src/main/scala/pl/touk/nussknacker/ui/db/entity/StickyNotesEntityFactory.scala (2)

77-80: Handle JSON parsing errors more gracefully.

Throwing exceptions upon JSON parsing failures can lead to unhandled exceptions. Consider handling errors more gracefully by logging the error and providing a default value or informative message.

Apply this diff to improve error handling:

         jsonStr =>
           parser.parse(jsonStr).flatMap(Decoder[LayoutData].decodeJson) match {
             case Right(layoutData) => layoutData
-            case Left(error)       => throw error
+            case Left(error) =>
+              logger.error("Failed to parse LayoutData JSON", error)
+              throw new RuntimeException("Invalid LayoutData format", error)
           }

86-89: Handle JSON parsing errors more gracefully.

Similar to layoutData, improve error handling for dimensions to avoid unhandled exceptions.

Apply this diff to improve error handling:

         jsonStr =>
           parser.parse(jsonStr).flatMap(Decoder[Dimensions].decodeJson) match {
             case Right(dimensions) => dimensions
-            case Left(error)       => throw error
+            case Left(error) =>
+              logger.error("Failed to parse Dimensions JSON", error)
+              throw new RuntimeException("Invalid Dimensions format", error)
           }
designer/client/src/components/graph/EspNode/stickyNote.ts (1)

96-96: Use the defined default color constant instead of hard-coded value.

Replace the hard-coded color "#eae672" with the STICKY_NOTE_DEFAULT_COLOR constant for consistency and ease of maintenance.

Apply this diff to use the constant:

       fill: "#eae672",
+      // fill: STICKY_NOTE_DEFAULT_COLOR,
+      fill: STICKY_NOTE_DEFAULT_COLOR,
designer/client/src/components/graph/EspNode/stickyNoteElements.ts (1)

17-17: Remove unused parameter processDefinitionData.

The parameter processDefinitionData is not used within the makeStickyNoteElement function. Consider removing it to clean up the code.

Apply this diff to remove the unused parameter:

-export function makeStickyNoteElement(
-    processDefinitionData: ProcessDefinitionData,
+export function makeStickyNoteElement(
     theme: Theme,
 ): (stickyNote: StickyNote) => ModelWithTool {
designer/server/src/main/scala/pl/touk/nussknacker/ui/process/repository/stickynotes/DbStickyNotesRepository.scala (2)

34-64: Refactor duplicated query logic in findStickyNotes and countStickyNotes

The methods findStickyNotes and countStickyNotes have similar query logic. To adhere to the DRY (Don't Repeat Yourself) principle, consider extracting the common parts into a private helper method or function.


103-110: Simplify error handling in addStickyNote method

In the addStickyNote method, the insert operation checks for the number of affected rows and throws an IllegalStateException for unexpected cases. Since the database constraints should ensure data integrity, consider simplifying this logic or handling failures without throwing generic exceptions.

designer/server/src/main/scala/pl/touk/nussknacker/ui/api/description/StickyNotesApiEndpoints.scala (1)

170-215: Use jsonBody instead of plainBody for error responses

In the error variants such as stickyNoteContentTooLongExample, you are using plainBody to serialize error responses. For consistency and better client handling, consider using jsonBody to return error details in JSON format.

designer/server/src/main/scala/pl/touk/nussknacker/ui/api/StickyNotesApiHttpService.scala (2)

114-124: Check user authorization before fetching sticky note

In the deleteStickyNote method, consider checking user authorization before fetching the sticky note from the database. This can improve performance by avoiding unnecessary database access when the user lacks the necessary permissions.


205-228: Simplify error handling in updateStickyNote method

The updateStickyNote method includes nested error handling with EitherT monads. To enhance readability, consider simplifying the error handling logic or refactoring the method to reduce complexity.

designer/server/src/test/scala/pl/touk/nussknacker/test/utils/domain/ScenarioHelper.scala (2)

85-90: Add explicit return type for updateScenario method

For clarity and maintainability, consider adding an explicit return type to the updateScenario method. This practice enhances code readability and helps with future modifications.


92-97: Add explicit return type for addStickyNote method

Similarly, consider specifying the return type for the addStickyNote method to improve code clarity and prevent potential misunderstandings about the method's return value.

designer/client/src/components/graph/Graph.tsx (2)

495-495: Use strict equality operator for content comparison

In the content comparison, replace == with === to ensure strict equality checking without type coercion. This prevents unexpected behavior if types differ.


478-484: Consider centralizing sticky note dimension constraints

The dimension constraints for sticky notes are applied in multiple places. To adhere to the DRY principle, consider centralizing these constraints in a utility function or constant to ensure consistency and ease of maintenance.

designer/client/src/types/stickyNote.ts (2)

1-1: Add JSDoc documentation for exported constant

Consider adding documentation to describe the purpose and usage of this exported constant.

+/** Constant representing the type identifier for sticky notes in the system */
 export const StickyNoteType = "StickyNote";

2-4: Add input validation and documentation for createStickyNoteId function

The function should validate that noteId is non-negative and include JSDoc documentation explaining its purpose and parameters.

+/**
+ * Creates a unique identifier for a sticky note by combining the type and note ID
+ * @param noteId - The numeric identifier of the sticky note (must be non-negative)
+ * @returns A string in the format "StickyNote_<noteId>"
+ * @throws {Error} If noteId is negative
+ */
 export function createStickyNoteId(noteId: number) {
+    if (noteId < 0) {
+        throw new Error("Sticky note ID must be non-negative");
+    }
     return StickyNoteType + "_" + noteId;
 }
designer/server/src/main/scala/db/migration/hsql/V1_060__CreateStickyNotes.scala (1)

6-8: Add Scaladoc documentation for the migration class

Consider adding documentation to describe the purpose of this migration class and its relationship to sticky notes functionality.

+/**
+ * HSQLDB-specific implementation of sticky notes migration.
+ * Creates necessary database structures for storing sticky notes in HSQLDB.
+ */
 class V1_060__CreateStickyNotes extends V1_060__CreateStickyNotesDefinition {
   override protected lazy val profile: JdbcProfile = HsqldbProfile
 }
designer/server/src/main/scala/db/migration/postgres/V1_060__CreateStickyNotes.scala (1)

6-8: Add Scaladoc documentation for the PostgreSQL migration class

Consider adding documentation to describe the purpose of this migration class and its relationship to sticky notes functionality.

+/**
+ * PostgreSQL-specific implementation of sticky notes migration.
+ * Creates necessary database structures for storing sticky notes in PostgreSQL.
+ */
 class V1_060__CreateStickyNotes extends V1_060__CreateStickyNotesDefinition {
   override protected lazy val profile: JdbcProfile = PostgresProfile
 }
designer/client/src/types/component.ts (1)

8-8: Add JSDoc comment to document the disabled property.

The optional disabled property is well-typed and allows for dynamic component disabling. Consider adding documentation to explain when/why a component would be disabled.

+    /** Function that determines if the component should be disabled. Return true to disable the component. */
     disabled?: () => boolean;
designer/client/src/common/StickyNote.ts (1)

3-3: Consider using readonly properties for immutability.

The Dimensions type could benefit from being readonly to prevent accidental modifications.

-export type Dimensions = { width: number; height: number };
+export type Dimensions = { readonly width: number; readonly height: number };
designer/server/src/main/scala/pl/touk/nussknacker/ui/api/description/stickynotes/StickyNoteEvent.scala (1)

6-7: Consider adding custom error handling for JSON decoding.

While the current implementation is correct, consider adding custom error handling for invalid event types during JSON decoding to provide more meaningful error messages.

implicit val typeDecoder: Decoder[StickyNoteEvent.Value] = Decoder.decodeString.emap { str =>
  StickyNoteEvent.values.find(_.toString == str).toRight(s"Invalid StickyNoteEvent: $str")
}
designer/client/src/components/toolbars/creator/StickyNoteComponent.tsx (1)

4-4: Add type annotation for better type safety.

The noteModel constant would benefit from explicit typing to ensure type safety and improve code maintainability.

-const noteModel = { id: "StickyNoteToAdd", type: StickyNoteType, isDisabled: false };
+const noteModel: { id: string; type: typeof StickyNoteType; isDisabled: boolean } = {
+  id: "StickyNoteToAdd",
+  type: StickyNoteType,
+  isDisabled: false,
+};
designer/client/src/assets/json/nodeAttributes.json (1)

41-43: Consider maintaining alphabetical order for node types

The StickyNote node type addition is correct and follows the established pattern. However, consider maintaining alphabetical order for better maintainability and easier navigation.

Consider moving the StickyNote entry to maintain alphabetical order with other node types:

     "Split": {
         "name": "Split"
     },
+    "StickyNote": {
+        "name": "StickyNote"
+    },
     "Switch": {
         "name": "Choice"
     },
-    "StickyNote": {
-        "name": "StickyNote"
-    },
designer/client/cypress/e2e/creatorToolbar.cy.ts (1)

29-29: Consider expanding sticky notes test coverage

While the basic toolbar interaction test is good, consider adding more comprehensive test cases for sticky notes functionality:

  1. Test sticky note creation
  2. Verify maximum notes limit
  3. Test content length restrictions
  4. Verify persistence of sticky notes
  5. Test deletion functionality

Example test structure:

it("should handle sticky notes operations", () => {
    // Test creation
    cy.contains(/^stickyNotes$/i).click();
    cy.get("[data-testid=add-sticky-note]").click();
    
    // Test content
    cy.get("[data-testid=sticky-note-content]")
        .type("Test note content")
        .should("have.value", "Test note content");
    
    // Test persistence
    cy.reload();
    cy.get("[data-testid=sticky-note-content]")
        .should("have.value", "Test note content");
    
    // Test deletion
    cy.get("[data-testid=delete-sticky-note]").click();
    cy.get("[data-testid=sticky-note-content]").should("not.exist");
});
designer/client/src/containers/theme/helpers.ts (1)

19-26: Consider enhancing type safety and documentation

While the implementation is solid, consider these improvements:

  1. Add JSDoc documentation to explain the function's purpose and parameters
  2. Consider using a more specific type for the color parameter (e.g., CSS color string)

Here's a suggested improvement:

+/**
+ * Gets the background color for a sticky note, with validation and fallback
+ * @param theme - The current theme object
+ * @param color - A valid CSS color string
+ * @returns An augmented color object from the theme's palette
+ */
-export function getStickyNoteBackgroundColor(theme: Theme, color: string) {
+export function getStickyNoteBackgroundColor(theme: Theme, color: CSSColorString) {
designer/client/src/actions/notificationActions.tsx (1)

15-15: Remove TODO comment from production code

Development notes/TODOs should be tracked in issues rather than in the production codebase.

Would you like me to create a GitHub issue to track this discussion about the error function's completeness?

designer/client/src/components/graph/GraphPartialsInTS/cellUtils.ts (1)

16-21: Consider adding error handling for invalid noteId.

The function handles the happy path well with proper null checks and immutable copies. However, consider adding error logging when noteId exists but no matching note is found, as this could indicate data inconsistency.

 export function getStickyNoteCopyFromCell(stickyNotes: StickyNote[], el: dia.Cell): StickyNote | undefined {
     const noteId = el.get("noteId");
     if (!isStickyNoteElement(el) || !noteId) return undefined;
     const stickyNote = stickyNotes.find((note) => note.noteId == noteId);
+    if (noteId && !stickyNote) {
+        console.warn(`Sticky note with id ${noteId} not found in available notes`);
+    }
     return stickyNote ? cloneDeep(stickyNote) : undefined;
 }
designer/client/src/components/graph/StickyNoteElement.ts (2)

17-18: Consider adding JSDoc documentation

The StickyNoteElement factory function would benefit from JSDoc documentation explaining its purpose and parameters.

+/**
+ * Creates a new sticky note element type using JointJS's dia.Element.define
+ * @param defaults - Optional positioning, sizing, and attribute defaults
+ * @param protoProps - Optional prototype properties including markup
+ * @returns A new JointJS element type for sticky notes
+ */
 export const StickyNoteElement = (defaults?: StickyNoteDefaults, protoProps?: StickyNoteProtoProps) =>
     dia.Element.define("stickyNote.StickyNoteElement", defaults, protoProps);

38-45: Add keyboard event type and consider additional shortcuts

The keyboard event handling could be improved:

-    selectAll: function (evt) {
+    selectAll: function (evt: KeyboardEvent) {
         if (evt.code === "KeyA") {
             if (evt.ctrlKey || evt.metaKey) {
                 evt.preventDefault();
                 evt.target.select();
             }
         }
+        // Consider adding Escape key handler to blur the textarea
+        if (evt.code === "Escape") {
+            (evt.target as HTMLTextAreaElement).blur();
+        }
     },
designer/client/src/actions/nk/assignSettings.ts (1)

42-42: Document the settings integration

Add JSDoc comment to explain the sticky notes feature configuration.

+    /** Configuration for sticky notes feature including content length and count limits */
     stickyNotesSettings: StickyNotesSettings;
designer/client/src/components/graph/node-modal/node/FragmentContent.tsx (1)

17-17: Add TypeScript types and error handling for sticky notes

While the implementation is functionally correct, consider these improvements:

  1. Add TypeScript type for the stickyNotes prop
  2. Add error handling or fallback for when sticky notes data is unavailable

Consider adding types and error handling:

+ import { StickyNote } from "../../../../types/stickyNote";

- const stickyNotes = useSelector(getStickyNotes);
+ const stickyNotes: StickyNote[] = useSelector(getStickyNotes) ?? [];

  <FragmentGraphPreview
    processCounts={fragmentCounts}
    scenario={fragmentContent}
-   stickyNotes={stickyNotes}
+   stickyNotes={stickyNotes}
    nodeIdPrefixForFragmentTests={getFragmentNodesPrefix(fragmentContent)}
  />

Also applies to: 44-44

designer/server/src/test/scala/pl/touk/nussknacker/test/base/it/WithSimplifiedConfigScenarioHelper.scala (1)

44-50: Add test coverage and error handling documentation

While the implementation is clean and follows the existing pattern, consider:

  1. Adding test cases demonstrating the usage of these new methods
  2. Documenting possible error scenarios and how they're handled

Consider adding ScalaDoc with error handling details:

+ /**
+  * Updates the scenario with a new version
+  * @param scenarioName name of the scenario to update
+  * @param newVersion new version of the scenario
+  * @return ProcessUpdated with update details
+  * @throws ProcessNotFoundError if scenario doesn't exist
+  */
  def updateScenario(scenarioName: ProcessName, newVersion: CanonicalProcess): ProcessUpdated = {
    rawScenarioHelper.updateScenario(scenarioName, newVersion)
  }

+ /**
+  * Adds a sticky note to the scenario
+  * @param scenarioName name of the scenario
+  * @param request sticky note details
+  * @return correlation ID of the created sticky note
+  * @throws ProcessNotFoundError if scenario doesn't exist
+  */
  def addStickyNote(scenarioName: ProcessName, request: StickyNoteAddRequest): StickyNoteCorrelationId = {
    rawScenarioHelper.addStickyNote(scenarioName, request)
  }
designer/client/src/components/StickyNotePreview.tsx (2)

9-11: Consider documenting magic numbers

While these constants are used for visual effects, adding comments explaining the visual impact of these specific values would improve maintainability.

+// Scale factor for the preview state of sticky notes
 const PREVIEW_SCALE = 0.9;
+// Rotation angle (in degrees) when the note is active
 const ACTIVE_ROTATION = 2;
+// Scale factor for inactive state
 const INACTIVE_SCALE = 1.5;

13-62: Add accessibility attributes to interactive elements

The component might be interactive but lacks accessibility attributes. Consider adding appropriate ARIA attributes and keyboard interactions.

 export function StickyNotePreview({ isActive, isOver }: { isActive?: boolean; isOver?: boolean }): JSX.Element {
     // ... existing code ...
     return (
-        <div className={cx(colors, nodeStyles)}>
+        <div 
+            className={cx(colors, nodeStyles)}
+            role="button"
+            tabIndex={0}
+            aria-label="Sticky note preview"
+            aria-pressed={isActive}
+        >
             <div className={cx(imageStyles, colors)}>
                 <PreloadedIcon src={stickyNoteIconSrc} />
             </div>
         </div>
     );
 }
designer/client/src/components/graph/GraphPartialsInTS/applyCellChanges.ts (1)

46-57: Replace console statements with proper logging

Consider using a proper logging service instead of direct console calls. Also, the error handling block could be extracted into a separate function for better maintainability.

+const addToolsToStickyNote = (model: ModelWithTool, paper: dia.Paper) => {
+    const view = model.model.findView(paper);
+    if (!view) {
+        logger.warn(`View not found for stickyNote model: ${model.model.id}`);
+        return;
+    }
+    view.addTools(model.tools);
+};

-    newStickyNotesModelsWithTools.forEach((m) => {
-        try {
-            const view = m.model.findView(paper);
-            if (!view) {
-                console.warn(`View not found for stickyNote model: ${m.model.id}`);
-                return;
-            }
-            view.addTools(m.tools);
-        } catch (error) {
-            console.error(`Failed to add tools to stickyNote view:`, error);
-        }
-    });
+    newStickyNotesModelsWithTools.forEach((model) => {
+        try {
+            addToolsToStickyNote(model, paper);
+        } catch (error) {
+            logger.error(`Failed to add tools to stickyNote view:`, error);
+        }
+    });
designer/client/src/components/graph/types.ts (2)

77-79: Consider grouping related events

The Events enum is growing large. Consider grouping related events (like cell events) into separate enums or using a namespace pattern for better organization.

-export enum Events {
+export namespace Events {
+  export enum Cell {
+    RESIZED = "cellCustom:resized",
+    CONTENT_UPDATED = "cellCustom:contentUpdated",
+    DELETED = "cellCustom:deleted",
+    // ... other cell events
+  }
+  
+  export enum Link {
+    CONNECT = "link:connect",
+    // ... other link events
+  }
+  
+  export enum Blank {
+    POINTERCLICK = "blank:pointerclick",
+    // ... other blank events
+  }
+}

Line range hint 19-41: Consider splitting ScenarioGraphProps into smaller interfaces

The ScenarioGraphProps type is growing with many responsibilities. Consider splitting it into smaller, focused interfaces for better maintainability.

interface StickyNoteActions {
    stickyNoteAdded: typeof stickyNoteAdded;
    stickyNoteUpdated: typeof stickyNoteUpdated;
    stickyNoteDeleted: typeof stickyNoteDeleted;
}

interface NodeActions {
    nodesConnected: typeof nodesConnected;
    nodesDisconnected: typeof nodesDisconnected;
    nodeAdded: typeof nodeAdded;
    // ... other node actions
}

interface ScenarioGraphProps extends StickyNoteActions, NodeActions {
    // ... remaining props
}
designer/server/src/test/scala/pl/touk/nussknacker/ui/api/StickyNotesApiHttpServiceBusinessSpec.scala (1)

42-43: Enhance test data helper method.

The stickyNoteToAdd helper method uses hardcoded values which might not cover all edge cases. Consider:

  • Adding parameters for content and color
  • Including validation boundary cases
  • Adding documentation
-  private def stickyNoteToAdd(versionId: VersionId): StickyNoteAddRequest =
-    StickyNoteAddRequest(versionId, "", LayoutData(0, 1), "#aabbcc", Dimensions(300, 200), None)
+  /** Creates a StickyNoteAddRequest with customizable parameters for testing
+   * @param versionId The version ID of the scenario
+   * @param content Note content (defaults to empty string)
+   * @param color Note color in hex format (defaults to "#aabbcc")
+   * @return StickyNoteAddRequest instance
+   */
+  private def stickyNoteToAdd(
+    versionId: VersionId,
+    content: String = "",
+    color: String = "#aabbcc"
+  ): StickyNoteAddRequest =
+    StickyNoteAddRequest(
+      versionId,
+      content,
+      LayoutData(0, 1),
+      color,
+      Dimensions(300, 200),
+      None
+    )
designer/client/src/components/toolbars/creator/ToolboxComponentGroup.tsx (1)

76-82: Consider enhancing type safety for the disabled prop.

While the implementation is functional, consider these improvements:

  • Add TypeScript type guard for the disabled function
  • Add unit tests for the disabled state
-                    disabled={component.disabled ? component.disabled() : false}
+                    disabled={typeof component.disabled === 'function' ? component.disabled() : false}
designer/server/src/main/scala/pl/touk/nussknacker/ui/config/FeatureTogglesConfig.scala (2)

32-33: Consider grouping related configuration fields together

The stickyNotesSettings field seems arbitrarily placed. Consider grouping it with other UI-related settings like commentSettings and deploymentCommentSettings for better code organization.

  final case class FeatureTogglesConfig(
    development: Boolean,
    metrics: Option[MetricsSettings],
    remoteEnvironment: Option[HttpRemoteEnvironmentConfig],
    counts: Option[Config],
    environmentAlert: Option[EnvironmentAlert],
    commentSettings: Option[CommentSettings],
    deploymentCommentSettings: Option[DeploymentCommentSettings],
+   stickyNotesSettings: StickyNotesSettings,
    scenarioLabelConfig: Option[ScenarioLabelConfig],
    scenarioStateTimeout: Option[FiniteDuration],
    surveySettings: Option[SurveySettings],
    tabs: Option[List[TopTab]],
    intervalTimeSettings: IntervalTimeSettings,
    testDataSettings: TestDataSettings,
    enableConfigEndpoint: Boolean,
    redirectAfterArchive: Boolean,
-   componentDefinitionExtractionMode: ComponentDefinitionExtractionMode,
-   stickyNotesSettings: StickyNotesSettings
+   componentDefinitionExtractionMode: ComponentDefinitionExtractionMode
  )

61-62: Add documentation for the default settings behavior

Consider adding a comment explaining the default behavior when stickyNotesSettings is not provided in the configuration.

+   // If stickyNotesSettings is not provided in config, use default settings
+   // Default settings can be found in StickyNotesSettings.default
    val stickyNotesSettings =
      config.getAs[StickyNotesSettings]("stickyNotesSettings").getOrElse(StickyNotesSettings.default)
designer/client/src/components/toolbars/creator/ToolBox.tsx (1)

124-128: Verify memoization dependencies and add type annotations

The memoization implementation looks good, but consider these improvements:

  1. Add type annotation for stickyNoteToolGroup
  2. Verify if t (translation function) is needed in the dependency array
-   const stickyNoteToolGroup = useMemo(() => stickyNoteComponentGroup(pristine), [pristine, props, t]);
+   const stickyNoteToolGroup = useMemo(() => stickyNoteComponentGroup(pristine), [pristine]);

-   const groups = useMemo(() => {
+   const groups: ComponentGroup[] = useMemo(() => {
        const allComponentGroups = stickyNotesSettings.enabled ? concat(componentGroups, stickyNoteToolGroup) : componentGroups;
        return allComponentGroups.map(filterComponentsByLabel(filters)).filter((g) => g.components.length > 0);
    }, [componentGroups, filters, stickyNoteToolGroup, stickyNotesSettings]);
designer/client/src/reducers/selectors/graph.ts (2)

75-77: Consider grouping related selectors and adding explicit return type

The sticky notes selector implementation is correct, but consider these improvements:

  1. Group it with other UI-related selectors for better code organization
  2. Add explicit return type annotation for better type safety
-export const getStickyNotes = createSelector([getGraph, getStickyNotesSettings], (g, settings) =>
-    settings.enabled ? g.stickyNotes || ([] as StickyNote[]) : ([] as StickyNote[]),
-);
+export const getStickyNotes = createSelector(
+    [getGraph, getStickyNotesSettings],
+    (g, settings): StickyNote[] => settings.enabled ? g.stickyNotes || [] : [],
+);

11-12: Group related imports together

Consider grouping the sticky notes related imports with other feature-specific imports for better code organization.

import { ProcessCounts } from "../graph";
import { RootState } from "../index";
import { getProcessState } from "./scenarioState";
import { TestFormParameters } from "../../common/TestResultUtils";
+// Feature-specific imports
import { StickyNote } from "../../common/StickyNote";
import { getStickyNotesSettings } from "./settings";
designer/server/src/main/scala/pl/touk/nussknacker/ui/api/SettingsResources.scala (1)

46-47: Consider adding validation for stickyNotesSettings

While the configuration is correctly passed through, consider adding validation to ensure stickyNotesSettings is properly initialized with valid values.

// Add validation similar to DeploymentCommentSettings
object StickyNotesSettings {
  def create(
      maxContentLength: Int,
      maxNotesCount: Int,
      enabled: Boolean
  ): Validated[InvalidStickyNotesSettingsError, StickyNotesSettings] = {
    Validated.cond(
      maxContentLength > 0 && maxNotesCount > 0,
      new StickyNotesSettings(maxContentLength, maxNotesCount, enabled),
      InvalidStickyNotesSettingsError("maxContentLength and maxNotesCount must be positive")
    )
  }
}
designer/client/src/components/graph/NodeDescriptionPopover.tsx (1)

123-123: Enhance the comment for better clarity

While the logic is correct, the comment could be more descriptive.

-        if (isStickyNoteElement(view.model)) return; //Dont use it for stickyNotes
+        if (isStickyNoteElement(view.model)) return; // Skip description popover for sticky notes as they have their own display mechanism
designer/client/src/components/graph/graphStyledWrapper.ts (1)

184-216: Consider enhancing the sticky note styles

A few suggestions to improve the styling:

  1. Add transitions for smoother interactions
  2. Extract magic numbers into theme spacing units
  3. Consider adding hover states for better UX

Consider applying these improvements:

 ".sticky-note-markdown": {
     width: "100%",
     height: "100%",
-    paddingLeft: "10px",
-    paddingRight: "10px",
+    paddingLeft: theme.spacing(1.25),
+    paddingRight: theme.spacing(1.25),
+    transition: theme.transitions.create(['background-color', 'box-shadow']),
 },
designer/client/src/reducers/graph/utils.ts (1)

82-93: Consider optimizing layout updates

The current implementation creates new arrays for both layout and sticky notes. For large datasets, this could be optimized.

Consider using Set or Map for O(1) lookups:

 export function removeStickyNoteFromLayout(state: GraphState, stickyNoteId: number): { layout: NodePosition[]; stickyNotes: StickyNote[] } {
     const { layout } = state;
     const stickyNoteLayoutId = createStickyNoteId(stickyNoteId);
+    const layoutMap = new Map(layout.map(l => [l.id, l]));
+    layoutMap.delete(stickyNoteLayoutId);
     const updatedStickyNotes = state.stickyNotes.filter((n) => n.noteId !== stickyNoteId);
-    const updatedLayout = updatedStickyNotes.map((stickyNote) => {
-        return { id: createStickyNoteId(stickyNote.noteId), position: stickyNote.layoutData };
-    });
+    const updatedLayout = Array.from(layoutMap.values());
     return {
         stickyNotes: [...updatedStickyNotes],
-        layout: [...layout.filter((l) => l.id !== stickyNoteLayoutId), ...updatedLayout],
+        layout: updatedLayout,
     };
 }
designer/client/src/http/HttpService.ts (2)

721-738: Add validation for sticky note content length.

The updateStickyNote method should validate the content length before making the API call to ensure it meets any backend restrictions.

Consider adding validation:

updateStickyNote(scenarioName: string, scenarioVersionId: number, stickyNote: StickyNote) {
+   const maxContentLength = 1000; // Get from config
+   if (stickyNote.content && stickyNote.content.length > maxContentLength) {
+       return Promise.reject(new Error(`Content exceeds maximum length of ${maxContentLength} characters`));
+   }
    const promise = api.put(...);
    ...
}

740-746: Consider adding caching for sticky notes.

The getStickyNotes method could benefit from caching to reduce API calls, especially if sticky notes are frequently accessed but rarely updated.

Consider implementing a caching mechanism with a short TTL to improve performance.

docs-internal/api/nu-designer-openapi.yaml (2)

2530-2843: REST API endpoints for sticky notes look good but could use additional documentation.

The implementation follows REST conventions and includes proper authentication, authorization, and error handling. However, consider the following improvements:

  1. Add description for why scenarioVersionId is required in the GET endpoint
  2. Document specific validation error scenarios in the 400 response examples, such as:
    • Maximum content length exceeded
    • Maximum notes count reached
    • Invalid color format
    • Invalid dimensions

Example documentation to add for the GET endpoint's scenarioVersionId parameter:

      - name: scenarioVersionId
        in: query
        required: true
+       description: Version ID of the scenario to retrieve sticky notes from. This ensures notes are retrieved for the correct version as they may differ between versions.
        schema:
          type: integer
          format: int64

7182-7267: Schema definitions could be enhanced with additional constraints.

The schema definitions are well-structured but could benefit from additional validation constraints:

  1. Add format validation for the color field (e.g., hex color)
  2. Add min/max constraints for dimensions
  3. Add maxLength constraint for content field

Example improvements:

    StickyNote:
      properties:
        content:
          type: string
+         maxLength: 5000
        color:
          type: string
+         pattern: '^#[0-9A-Fa-f]{6}$'
    Dimensions:
      properties:
        width:
          type: integer
          format: int64
+         minimum: 50
+         maximum: 1000
        height:
          type: integer
          format: int64
+         minimum: 50
+         maximum: 1000

Also applies to: 5492-5504

docs/MigrationGuide.md (1)

7-14: Consider adding version compatibility matrix

The migration guide would benefit from having a compatibility matrix at the top showing which versions are compatible with different Flink versions, Scala versions, and other major dependencies. This would help users quickly determine the right upgrade path.

📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL

📥 Commits

Reviewing files that changed from the base of the PR and between 5a4bbe5 and 63c7064.

⛔ Files ignored due to path filters (3)
  • designer/client/cypress/e2e/__image_snapshots__/electron/Linux/Creator toolbar should allow collapse (persist) and filtering #0.png is excluded by !**/*.png
  • designer/client/package-lock.json is excluded by !**/package-lock.json
  • designer/server/src/main/resources/web/static/assets/components/StickyNote.svg is excluded by !**/*.svg
📒 Files selected for processing (59)
  • designer/client/cypress/e2e/creatorToolbar.cy.ts (1 hunks)
  • designer/client/package.json (1 hunks)
  • designer/client/src/actions/actionTypes.ts (1 hunks)
  • designer/client/src/actions/nk/assignSettings.ts (1 hunks)
  • designer/client/src/actions/nk/process.ts (3 hunks)
  • designer/client/src/actions/notificationActions.tsx (1 hunks)
  • designer/client/src/assets/json/nodeAttributes.json (1 hunks)
  • designer/client/src/common/StickyNote.ts (1 hunks)
  • designer/client/src/components/ComponentPreview.tsx (2 hunks)
  • designer/client/src/components/StickyNotePreview.tsx (1 hunks)
  • designer/client/src/components/graph/EspNode/stickyNote.ts (1 hunks)
  • designer/client/src/components/graph/EspNode/stickyNoteElements.ts (1 hunks)
  • designer/client/src/components/graph/Graph.tsx (13 hunks)
  • designer/client/src/components/graph/GraphPartialsInTS/applyCellChanges.ts (2 hunks)
  • designer/client/src/components/graph/GraphPartialsInTS/cellUtils.ts (2 hunks)
  • designer/client/src/components/graph/NodeDescriptionPopover.tsx (2 hunks)
  • designer/client/src/components/graph/ProcessGraph.tsx (4 hunks)
  • designer/client/src/components/graph/StickyNoteElement.ts (1 hunks)
  • designer/client/src/components/graph/fragmentGraph.tsx (1 hunks)
  • designer/client/src/components/graph/graphStyledWrapper.ts (2 hunks)
  • designer/client/src/components/graph/node-modal/node/FragmentContent.tsx (3 hunks)
  • designer/client/src/components/graph/types.ts (3 hunks)
  • designer/client/src/components/graph/utils/graphUtils.ts (1 hunks)
  • designer/client/src/components/toolbars/creator/ComponentIcon.tsx (2 hunks)
  • designer/client/src/components/toolbars/creator/StickyNoteComponent.tsx (1 hunks)
  • designer/client/src/components/toolbars/creator/ToolBox.tsx (2 hunks)
  • designer/client/src/components/toolbars/creator/ToolboxComponentGroup.tsx (1 hunks)
  • designer/client/src/containers/theme/helpers.ts (2 hunks)
  • designer/client/src/http/HttpService.ts (3 hunks)
  • designer/client/src/reducers/graph/reducer.ts (2 hunks)
  • designer/client/src/reducers/graph/types.ts (2 hunks)
  • designer/client/src/reducers/graph/utils.ts (3 hunks)
  • designer/client/src/reducers/selectors/graph.ts (2 hunks)
  • designer/client/src/reducers/selectors/settings.ts (1 hunks)
  • designer/client/src/types/component.ts (1 hunks)
  • designer/client/src/types/node.ts (1 hunks)
  • designer/client/src/types/stickyNote.ts (1 hunks)
  • designer/server/src/main/resources/defaultDesignerConfig.conf (1 hunks)
  • designer/server/src/main/scala/db/migration/V1_060__CreateStickyNotesDefinition.scala (1 hunks)
  • designer/server/src/main/scala/db/migration/hsql/V1_060__CreateStickyNotes.scala (1 hunks)
  • designer/server/src/main/scala/db/migration/postgres/V1_060__CreateStickyNotes.scala (1 hunks)
  • designer/server/src/main/scala/pl/touk/nussknacker/ui/api/SettingsResources.scala (3 hunks)
  • designer/server/src/main/scala/pl/touk/nussknacker/ui/api/StickyNotesApiHttpService.scala (1 hunks)
  • designer/server/src/main/scala/pl/touk/nussknacker/ui/api/description/StickyNotesApiEndpoints.scala (1 hunks)
  • designer/server/src/main/scala/pl/touk/nussknacker/ui/api/description/stickynotes/Dtos.scala (1 hunks)
  • designer/server/src/main/scala/pl/touk/nussknacker/ui/api/description/stickynotes/StickyNoteEvent.scala (1 hunks)
  • designer/server/src/main/scala/pl/touk/nussknacker/ui/config/FeatureTogglesConfig.scala (4 hunks)
  • designer/server/src/main/scala/pl/touk/nussknacker/ui/db/NuTables.scala (1 hunks)
  • designer/server/src/main/scala/pl/touk/nussknacker/ui/db/entity/StickyNotesEntityFactory.scala (1 hunks)
  • designer/server/src/main/scala/pl/touk/nussknacker/ui/process/repository/stickynotes/DbStickyNotesRepository.scala (1 hunks)
  • designer/server/src/main/scala/pl/touk/nussknacker/ui/process/repository/stickynotes/StickyNotesRepository.scala (1 hunks)
  • designer/server/src/main/scala/pl/touk/nussknacker/ui/server/AkkaHttpBasedRouteProvider.scala (4 hunks)
  • designer/server/src/test/resources/config/common-designer.conf (1 hunks)
  • designer/server/src/test/scala/pl/touk/nussknacker/test/base/it/WithSimplifiedConfigScenarioHelper.scala (2 hunks)
  • designer/server/src/test/scala/pl/touk/nussknacker/test/utils/domain/ScenarioHelper.scala (4 hunks)
  • designer/server/src/test/scala/pl/touk/nussknacker/ui/api/StickyNotesApiHttpServiceBusinessSpec.scala (1 hunks)
  • docs-internal/api/nu-designer-openapi.yaml (3 hunks)
  • docs/Changelog.md (1 hunks)
  • docs/MigrationGuide.md (1 hunks)
🧰 Additional context used
📓 Learnings (1)
designer/client/src/components/graph/Graph.tsx (1)
Learnt from: philemone
PR: TouK/nussknacker#7149
File: designer/client/src/components/graph/Graph.tsx:530-536
Timestamp: 2024-11-20T10:37:32.855Z
Learning: There is backend validation for sticky note content length, so frontend validation in the `updateStickyNote` method in `Graph.tsx` is unnecessary.
🔇 Additional comments (37)
designer/server/src/main/scala/pl/touk/nussknacker/ui/process/repository/stickynotes/StickyNotesRepository.scala (1)

48-48: ⚠️ Potential issue

Remove unnecessary trailing comma in method parameters.

The trailing comma after scenarioVersionId in the updateStickyNote method may cause a syntax error.

Apply this diff to fix the syntax error:

       scenarioVersionId: VersionId
-  )(implicit user: LoggedUser): DB[Int]
+  )(implicit user: LoggedUser): DB[Int]

Likely invalid or redundant comment.

designer/client/src/components/graph/EspNode/stickyNote.ts (1)

67-71: Ensure proper sanitization and error handling in Markdown parsing.

While using DOMPurify and marked is good for sanitization, consider handling errors in a user-friendly way and ensure all potential security concerns are addressed.

Run the following script to check for any vulnerabilities in the usage of marked and DOMPurify:

✅ Verification successful

Let me try a different approach to analyze the code and its dependencies.


Let me check the renderer configuration and any additional security settings.


Current implementation has proper security measures in place

The code implements adequate security measures:

  • Uses DOMPurify to sanitize HTML output with specific allowance for "target" attribute
  • Custom renderer properly handles links with security attributes (target="_blank" and rel="noopener noreferrer")
  • Error handling provides user feedback without exposing sensitive information
  • Markdown parsing is isolated to sticky note content only
  • No other instances of marked/DOMPurify usage that could create inconsistencies
🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Description: Check for known vulnerabilities in 'marked' and 'dompurify' packages.

# Check 'marked' package for vulnerabilities
npm audit --json | jq '.advisories[] | select(.module_name=="marked")'

# Check 'dompurify' package for vulnerabilities
npm audit --json | jq '.advisories[] | select(.module_name=="dompurify")'

Length of output: 962


Script:

#!/bin/bash
# Check how marked and DOMPurify are imported and configured
rg -A 5 "import.*\{?(marked|DOMPurify)\}?" designer/client/src/

# Look for any custom renderer configurations
ast-grep --pattern 'const renderer = new marked.Renderer()'

# Check for any security-related configurations
rg -B 2 -A 5 "DOMPurify.sanitize"

Length of output: 1894


Script:

#!/bin/bash
# Check the renderer configuration and any security-related settings
rg -B 10 -A 10 "const renderer = new marked.Renderer\(\)" designer/client/src/components/graph/EspNode/stickyNote.ts

# Look for any marked configuration
rg "marked\.setOptions|marked\.defaults" designer/client/src/

# Check if there are any other uses of marked or DOMPurify in the codebase
rg "marked\.parse|DOMPurify\.sanitize" designer/client/src/

Length of output: 1027

designer/server/src/main/scala/db/migration/hsql/V1_060__CreateStickyNotes.scala (1)

1-8: Verify migration script compatibility

The migration script introduces sticky notes functionality. We should verify its compatibility with existing data and rollback capabilities.

designer/server/src/main/scala/db/migration/postgres/V1_060__CreateStickyNotes.scala (1)

1-8: Verify PR checklist requirements

Based on the PR objectives, please ensure:

  1. Documentation is added to _MigrationGuide.md_ as this is a database schema change
  2. An entry is added to _Changelog.md_ describing the sticky notes feature
  3. The changes are covered by automated tests
designer/client/src/common/StickyNote.ts (1)

5-15: 🛠️ Refactor suggestion

Consider type improvements for better type safety.

A few suggestions to enhance type safety and validation:

  1. Use Date type for editedAt
  2. Add color type validation
  3. Consider adding content length validation based on maxContentLength setting
 export interface StickyNote {
     id?: string;
     noteId: number;
     content: string;
     layoutData: LayoutData;
     dimensions: Dimensions;
-    color: string;
+    color: `#${string}` | string;
     targetEdge?: string;
     editedBy: string;
-    editedAt: string;
+    editedAt: Date;
 }

Let's verify if there are any color validation patterns used elsewhere:

designer/server/src/main/scala/pl/touk/nussknacker/ui/db/NuTables.scala (1)

14-15: Verify initialization order of entity factories.

The addition of StickyNotesEntityFactory follows the established pattern. However, let's verify there are no initialization order dependencies between the entity factories.

✅ Verification successful

Let me gather more specific information about these entity factories.


Let me check for any initialization methods or dependencies between these entity factories.


Based on the analysis of the codebase, I can now generate the final response:

No initialization order concerns with StickyNotesEntityFactory

The entity factories in this codebase follow a simple trait-based composition pattern where:

  • All entity factories extend BaseEntityFactory
  • None of the factories have initialization methods that could cause order dependencies
  • StickyNotesEntityFactory only has a dependency on ProcessVersionEntityFactory's table, which is properly handled through the trait composition in NuTables
🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Search for dependencies between entity factories
rg -A 5 "extends.*EntityFactory.*with.*EntityFactory" --type scala

# Search for initialization order requirements
rg "override.*initialize" --type scala

Length of output: 526


Script:

#!/bin/bash
# Search for EntityFactory trait/class definitions and implementations
ast-grep --pattern 'trait $_EntityFactory {
  $$$
}'

# Also check for any abstract or concrete methods in these factories
rg "EntityFactory" -B 2 -A 5 --type scala

Length of output: 54715


Script:

#!/bin/bash
# Search for any initialization methods in entity factories
ast-grep --pattern 'trait $_EntityFactory {
  $$$
  def initialize($$$) {
    $$$
  }
  $$$
}'

# Check for any dependencies between tables in StickyNotesEntityFactory
rg -A 10 "trait StickyNotesEntityFactory" --type scala

Length of output: 1709

designer/server/src/main/scala/pl/touk/nussknacker/ui/api/description/stickynotes/StickyNoteEvent.scala (1)

5-13: LGTM! Well-structured enumeration for sticky note events.

The implementation follows Scala best practices with clear, descriptive event types that cover all necessary CRUD operations.

designer/client/src/reducers/graph/types.ts (1)

17-17: LGTM! Well-placed type addition.

The optional stickyNotes array is appropriately typed and placed within the GraphState interface. The optional typing ensures backward compatibility.

designer/client/src/components/graph/fragmentGraph.tsx (1)

7-24: LGTM! Clean implementation of sticky notes support

The changes properly integrate sticky notes into the FragmentGraphPreview component while maintaining type safety and following React best practices. The use of Pick utility type ensures proper prop typing, and the props are correctly forwarded to the GraphWrapped component.

designer/client/src/actions/actionTypes.ts (1)

13-14: LGTM! Action types properly defined for sticky notes feature

The new action types follow the existing naming convention and clearly represent their intended operations.

designer/client/src/actions/notificationActions.tsx (1)

16-20: 🛠️ Refactor suggestion

Improve error handling implementation

Several concerns with the current implementation:

  1. The error message logic could be more explicit
  2. Type safety could be improved
  3. The function's behavior change might affect existing usages

Consider this improved implementation:

-export function error(message: string, error?: string, showErrorText?: boolean): Action {
+type ErrorMessage = {
+    displayMessage: string;
+    technicalDetails?: string;
+    showTechnicalDetails?: boolean;
+};
+
+export function error(params: string | ErrorMessage): Action {
+    const errorDetails = typeof params === 'string' 
+        ? { displayMessage: params }
+        : params;
+
     return Notifications.error({
         autoDismiss: 10,
-        children: <Notification type={"error"} icon={<InfoOutlinedIcon />} message={showErrorText && error ? error : message} />,
+        children: <Notification 
+            type={"error"} 
+            icon={<InfoOutlinedIcon />} 
+            message={errorDetails.showTechnicalDetails && errorDetails.technicalDetails 
+                ? errorDetails.technicalDetails 
+                : errorDetails.displayMessage
+            } 
+        />,
     });
 }

Let's verify existing usages to ensure backward compatibility:

designer/client/src/components/graph/GraphPartialsInTS/cellUtils.ts (1)

12-14: LGTM! Well-implemented type guard function.

The type guard follows TypeScript best practices and provides a clear, single-responsibility check for sticky note elements.

designer/client/src/types/node.ts (1)

3-3: LGTM! Clean type extension.

The addition of StickyNoteType to the union type is clean and maintains type safety. The import is properly scoped.

Also applies to: 5-5

designer/server/src/test/resources/config/common-designer.conf (1)

57-61: Consider increasing maxNotesCount and documenting limits.

The sticky notes configuration looks good overall, but:

  1. The limit of 5 notes might be too restrictive for complex scenarios
  2. The configuration values should be documented to explain their impact

Consider:

  1. Increasing maxNotesCount to a more practical limit (e.g., 20)
  2. Adding comments to explain the rationale behind these limits
 stickyNotesSettings: {
+  # Maximum length of content in a single sticky note
   maxContentLength: 5000,
+  # Maximum number of sticky notes per scenario
-  maxNotesCount: 5,
+  maxNotesCount: 20,
+  # Feature toggle for sticky notes functionality
   enabled: true
 }
designer/client/src/components/graph/StickyNoteElement.ts (1)

6-15: LGTM: Well-structured interfaces for sticky note configuration

The interfaces StickyNoteDefaults and StickyNoteProtoProps provide clear type definitions for configuring sticky notes. The optional properties in StickyNoteDefaults allow for flexible initialization.

designer/client/src/reducers/selectors/settings.ts (1)

17-17: LGTM: Consistent selector implementation

The getStickyNotesSettings selector follows the established pattern and is correctly implemented using createSelector.

designer/client/src/actions/nk/assignSettings.ts (1)

42-49: Verify settings validation and error handling

Ensure that the settings are properly validated and enforced across the stack:

  1. Backend API validation
  2. Frontend error handling
  3. User feedback for limit violations
✅ Verification successful

Settings validation and error handling are properly implemented

The codebase shows comprehensive validation and error handling for sticky notes settings:

  1. Backend validation in StickyNotesApiHttpService.scala:

    • Enforces maxNotesCount limit
    • Validates content length against maxContentLength
    • Returns appropriate error types (StickyNoteContentTooLong)
  2. Frontend integration:

    • Settings are properly propagated through Redux selectors
    • Feature toggle respects the enabled flag
    • Default settings are defined in backend (StickyNotesSettings.default)
🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Search for settings validation in backend
rg "StickyNotesSettings.*validate" --type scala

# Search for error handling in frontend
rg "maxNotesCount.*error" --type ts
rg "maxContentLength.*error" --type ts

Length of output: 128


Script:

#!/bin/bash
# Search for sticky notes settings usage and validation patterns
rg -A 5 "StickyNotesSettings" --type ts --type scala

# Search for any validation or error handling related to sticky notes
rg -A 5 "stickyNotes.*validation|stickyNotes.*error" --type ts --type scala

# Look for API endpoints or handlers related to sticky notes
ast-grep --pattern 'class $_ {
  $$$
  stickyNotes$_($$$) {
    $$$
  }
  $$$
}'

Length of output: 12800

designer/client/src/components/toolbars/creator/ComponentIcon.tsx (1)

26-29: LGTM! Sticky note icon implementation follows existing patterns

The implementation correctly follows the established patterns:

  • Uses memoized preloading via preloadBeImage
  • Maintains consistency with existing icon path structure
  • Exports necessary functions for reuse
designer/server/src/test/scala/pl/touk/nussknacker/test/base/it/WithSimplifiedConfigScenarioHelper.scala (1)

48-50: Verify sticky note validation

Ensure that the rawScenarioHelper.addStickyNote method validates the sticky note content according to the configured limits.

✅ Verification successful

Sticky note validation is properly implemented

The validation is already implemented in the StickyNotesApiHttpService. The service performs two validations before adding a sticky note:

  1. Content length validation through validateStickyNoteContent which checks against maxContentLength (default: 5000 characters)
  2. Notes count validation through validateStickyNotesCount which checks against maxNotesCount (default: 5 notes)

These validations are enforced before any sticky note is added to the system, with proper error types defined in StickyNotesError.

🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Search for sticky note validation logic
rg -A 5 "StickyNoteAddRequest.*validate"

Length of output: 42


Script:

#!/bin/bash
# Let's first find all files related to StickyNoteAddRequest
rg -l "StickyNoteAddRequest"

Length of output: 651


Script:

#!/bin/bash
# Let's also search for any validation related to sticky notes
rg -A 5 "StickyNote.*validat"

Length of output: 31


Script:

#!/bin/bash
# Let's look at the implementation of StickyNoteAddRequest
ast-grep --pattern 'case class StickyNoteAddRequest'

Length of output: 54


Script:

#!/bin/bash
# Let's examine the content of the Dtos file which likely contains the StickyNoteAddRequest definition
cat "designer/server/src/main/scala/pl/touk/nussknacker/ui/api/description/stickynotes/Dtos.scala"

Length of output: 4535


Script:

#!/bin/bash
# Let's also check the service implementation
cat "designer/server/src/main/scala/pl/touk/nussknacker/ui/api/StickyNotesApiHttpService.scala"

Length of output: 7864

designer/client/src/components/ComponentPreview.tsx (1)

10-11: LGTM: Import statements are properly organized.

The new imports are correctly placed and follow the project's import organization pattern.

designer/client/src/components/graph/ProcessGraph.tsx (3)

87-89: LGTM: Action creators properly integrated

The sticky note actions are correctly bound to dispatch, maintaining consistency with other actions.


104-104: LGTM: GraphWrapped component properly configured

The stickyNotes prop is correctly passed to GraphWrapped component.


49-54: 🛠️ Refactor suggestion

Verify drop handler's error handling

The drop handler now handles sticky notes differently from regular nodes, but it lacks error handling for invalid positions or failed sticky note creation.

Consider adding error handling:

 if (item?.type === StickyNoteType) {
+    try {
         graph.current.addStickyNote(scenario.name, scenario.processVersionId, mapValues(nodeInputRelOffset, Math.round));
+    } catch (error) {
+        console.error("Failed to add sticky note:", error);
+        // Consider showing user feedback
+    }
 } else {
designer/client/src/components/graph/utils/graphUtils.ts (1)

130-130: LGTM: Improved event handling flexibility

The addition of the preventDefault parameter with a sensible default value improves the flexibility of the event handler while maintaining backward compatibility.

Also applies to: 133-133

designer/server/src/main/scala/pl/touk/nussknacker/ui/api/SettingsResources.scala (2)

10-10: LGTM: Import statement is correctly placed

The import for StickyNotesSettings follows the package structure and naming conventions.


139-139: LGTM: Case class field addition is properly placed

The stickyNotesSettings field is correctly added to the ToggleFeaturesOptions case class.

designer/client/src/components/graph/NodeDescriptionPopover.tsx (1)

8-8: LGTM: Import statement is correctly placed

The import for isStickyNoteElement follows the project's import conventions.

designer/server/src/main/resources/defaultDesignerConfig.conf (1)

213-217: 🛠️ Refactor suggestion

Review and adjust the sticky notes limits

A few concerns about the configuration:

  1. Remove the unnecessary comma after maxContentLength
  2. Consider documenting the reasoning behind these limits
  3. The maxNotesCount of 5 might be too restrictive for complex scenarios
stickyNotesSettings: {
-  maxContentLength: 5000,
+  maxContentLength: 5000  # Maximum characters per note
-  maxNotesCount: 5
+  maxNotesCount: 10      # Maximum notes per scenario
  enabled: true
}
✅ Verification successful

Let me gather more information about sticky notes usage in the codebase to better understand the context and validate the limits.


Let me gather more information about sticky notes usage patterns to better understand if the limits are reasonable.


Based on the previous findings and the lack of specific usage patterns in tests and documentation, I can now make a final assessment about the sticky notes configuration.

Review and adjust the sticky notes configuration

  1. The comma after maxContentLength is indeed a syntax error and should be removed
  2. The maxContentLength of 5000 characters is reasonable as no existing sticky notes exceed this limit
  3. The maxNotesCount of 5 is a reasonable default since:
    • It helps maintain UI cleanliness
    • No evidence was found of use cases requiring more notes
    • Users can still create multiple notes across different scenarios
stickyNotesSettings: {
-  maxContentLength: 5000,
+  maxContentLength: 5000  # Maximum characters per sticky note
  maxNotesCount: 5  # Maximum notes per scenario
  enabled: true
}
🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Check if there are any existing sticky notes in the codebase that might exceed these limits
rg -l "stickyNote.*content.*" | while read -r file; do
  echo "Checking $file for content length..."
  rg -U "content[\"']:\s*[\"']([^\"']*)[\"']" "$file" | awk '{if (length($0) > 5000) print "Warning: Content exceeds 5000 chars in " FILENAME}'
done

Length of output: 1518


Script:

#!/bin/bash
# Check sticky notes related code for any hints about usage patterns
echo "=== Checking sticky notes implementation ==="
rg -A 5 "class.*StickyNote" 

echo -e "\n=== Checking for any existing sticky note count limits ==="
rg -i "maxNotes|notesCount|stickyNotes?.length"

echo -e "\n=== Checking for any sticky note content validation ==="
rg -i "content.*length|maxContent|contentLength"

Length of output: 82109


Script:

#!/bin/bash
# Check for any sticky notes related tests or usage patterns
echo "=== Checking sticky notes test cases ==="
rg -A 5 "stickyNote.*test" 

echo -e "\n=== Checking sticky notes documentation ==="
rg -A 5 "stickyNote.*description|stickyNote.*doc"

echo -e "\n=== Checking sticky notes UI components ==="
rg -A 5 "class.*StickyNote.*Component|const.*StickyNote.*Component"

Length of output: 447

designer/client/src/http/HttpService.ts (1)

695-707: Consider making the default color configurable.

The default sticky note color is hardcoded. As indicated by the TODO comment, consider making this configurable through application settings.

designer/client/src/reducers/graph/reducer.ts (2)

19-19: LGTM: Clean import additions

The new utility imports for sticky notes management follow the existing import structure and naming conventions.

Also applies to: 23-24


244-255: LGTM: Well-structured reducer cases for sticky notes

The implementation follows Redux best practices:

  • Maintains immutability using spread operator
  • Delegates complex operations to utility functions
  • Consistent with existing reducer patterns
designer/server/src/main/scala/pl/touk/nussknacker/ui/server/AkkaHttpBasedRouteProvider.scala (4)

72-72: LGTM: Clean import addition

The import for DbStickyNotesRepository follows the existing import structure.


171-171: LGTM: Repository initialization

The sticky notes repository initialization follows the established pattern of other repositories in the codebase.


402-409: LGTM: Well-structured service initialization

The StickyNotesApiHttpService initialization:

  • Follows dependency injection pattern
  • Includes all necessary dependencies
  • Properly integrates with authentication and authorization
  • Uses feature toggle configuration

603-603: LGTM: Proper service registration

The sticky notes service is correctly registered in the NuDesignerApi service list.

docs/MigrationGuide.md (2)

Line range hint 1-24: LGTM: Clear version organization

The migration guide is well-organized with clear version sections and subsections for different types of changes (Configuration, API, etc.). This makes it easy for users to find relevant upgrade information.


Line range hint 1-1000: Verify all version links are valid

The guide contains many links to pull requests and issues. We should verify these are all valid and accessible.

✅ Verification successful

The current script is not working correctly because it's picking up CSS/JS files and other irrelevant matches. Let me generate a more focused script to check the PR links.

This script will:

  1. Only look in the MigrationGuide.md file
  2. Extract PR numbers from markdown links in the format [[FIX] Admin/Services parameters work #1234]
  3. Check if each PR exists using GitHub CLI

This should give us cleaner results to verify the PR links.


The script executed successfully and showed that the GitHub CLI was able to access all the PR links in the migration guide. Since we were able to verify the PR links and no errors were reported (no "PR X not found" messages), this indicates all PR references in the migration guide are valid.

All PR links in the migration guide are valid and accessible

🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Description: Check if all PR/issue links in the migration guide are accessible
# Extract PR/issue numbers and verify they exist in the repository

# Find all PR links and verify
echo "Checking PR links..."
rg -o '#\d{4}' | while read -r match; do
  number=$(echo "$match" | tr -d '#')
  echo "Checking PR $number"
  gh pr view "$number" >/dev/null 2>&1 || echo "PR $number not found"
done

Length of output: 131070


Script:

#!/bin/bash
# Extract only PR numbers from markdown links in the migration guide
echo "Checking PR links..."
rg -o '\[#(\d+)\]' docs/MigrationGuide.md | while read -r match; do
  number=$(echo "$match" | tr -d '[]#')
  echo "Checking PR $number"
  gh pr view "$number" >/dev/null 2>&1 || echo "PR $number not found"
done

Length of output: 57402

Comment on lines +118 to +126
actionResult <- stickyNotesTable.filter(_.id === id).result.headOption.flatMap {
case None =>
DBIOAction.failed(
new NoSuchElementException(s"Trying to update record (id=${id.value}) which is not present in the database")
)
case Some(latestEvent) =>
val newEvent = updateAction(latestEvent)
stickyNotesTable += newEvent
}
Copy link

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

Handle non-existent sticky note ID gracefully in updateStickyNote

In the updateStickyNote method, when a sticky note with the given ID does not exist, a NoSuchElementException is thrown. Consider returning a more descriptive error message or an appropriate error response to the caller to improve error handling.

Comment on lines +1 to +8
package db.migration.postgres

import db.migration.V1_060__CreateStickyNotesDefinition
import slick.jdbc.{JdbcProfile, PostgresProfile}

class V1_060__CreateStickyNotes extends V1_060__CreateStickyNotesDefinition {
override protected lazy val profile: JdbcProfile = PostgresProfile
}
Copy link

Choose a reason for hiding this comment

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

💡 Codebase verification

Consider adding an index for sticky notes queries by scenario version

Based on the table structure and foreign key relationship, I recommend adding an index on (scenario_id, scenario_version_id) columns since:

  1. These columns form a composite foreign key to process_versions table
  2. Sticky notes will likely be queried by scenario version, making this a frequent access pattern
  3. No existing index is present for this query pattern

Add this index definition after the foreign key constraint:

CREATE INDEX idx_sticky_notes_scenario_version ON sticky_notes(scenario_id, scenario_version_id);
🔗 Analysis chain

Consider database-specific optimizations

While the current implementation provides a clean abstraction through profiles, consider if any PostgreSQL-specific optimizations (like indexes or constraints) should be added for better performance.

🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Check if there are any database-specific optimizations in other migrations
rg -g "*/postgres/*.scala" "CREATE INDEX|CLUSTER|VACUUM|ANALYZE"

# Look for the base migration definition to understand the table structure
ast-grep --pattern 'trait V1_060__CreateStickyNotesDefinition {
  $$$
}'

Length of output: 290


Script:

#!/bin/bash
# Let's try a different approach to find the base definition file
fd "V1_060__CreateStickyNotesDefinition.scala"

# Look for any existing Postgres-specific optimizations in migration files
fd "V1.*__.*.scala" --exec grep -l "CREATE INDEX\|CLUSTER\|VACUUM\|ANALYZE" {} \;

# Check the content of the base definition file to understand the table structure
fd "V1_060__CreateStickyNotesDefinition.scala" --exec cat {} \;

Length of output: 3400


Script:

#!/bin/bash
# Let's check other migration files for index patterns specifically on foreign keys and frequently queried columns
fd "V1.*__.*.scala" --exec grep -A 5 -B 5 "CREATE INDEX\|ALTER TABLE.*ADD.*FOREIGN KEY" {} \;

# Let's also look for any existing sticky notes related queries to understand access patterns
rg -g "*.scala" "sticky_notes.*WHERE\|JOIN.*sticky_notes"

Length of output: 1212

Comment on lines +5 to +19
export const stickyNoteComponentGroup = (pristine: boolean) => {
return [
{
components: [
{
node: noteModel,
label: "sticky note",
componentId: StickyNoteType + "_" + pristine,
disabled: () => !pristine,
},
],
name: "stickyNotes",
} as ComponentGroup,
];
};
Copy link

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

Consider improving type safety and component structure.

A few suggestions to enhance the implementation:

  1. Use proper typing instead of type assertion
  2. Consider extracting string literals to constants
  3. Add prop types validation
+const COMPONENT_NAME = "stickyNotes";
+const COMPONENT_LABEL = "sticky note";

-export const stickyNoteComponentGroup = (pristine: boolean) => {
+export const stickyNoteComponentGroup = (pristine: boolean): ComponentGroup[] => {
     return [
         {
             components: [
                 {
                     node: noteModel,
-                    label: "sticky note",
+                    label: COMPONENT_LABEL,
                     componentId: StickyNoteType + "_" + pristine,
                     disabled: () => !pristine,
                 },
             ],
-            name: "stickyNotes",
+            name: COMPONENT_NAME,
-        } as ComponentGroup,
+        },
     ];
 };
📝 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 const stickyNoteComponentGroup = (pristine: boolean) => {
return [
{
components: [
{
node: noteModel,
label: "sticky note",
componentId: StickyNoteType + "_" + pristine,
disabled: () => !pristine,
},
],
name: "stickyNotes",
} as ComponentGroup,
];
};
const COMPONENT_NAME = "stickyNotes";
const COMPONENT_LABEL = "sticky note";
export const stickyNoteComponentGroup = (pristine: boolean): ComponentGroup[] => {
return [
{
components: [
{
node: noteModel,
label: COMPONENT_LABEL,
componentId: StickyNoteType + "_" + pristine,
disabled: () => !pristine,
},
],
name: COMPONENT_NAME,
},
];
};

@@ -1,6 +1,7 @@
import { Layout, RefreshData } from "../../actions/nk";
Copy link

Choose a reason for hiding this comment

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

💡 Codebase verification

Based on the analysis of the codebase, I can now generate a final response regarding the PR requirements checklist:

PR requirements need attention

  1. No test files were found for the sticky notes functionality. Tests should be added to cover:
    • Sticky note creation, update, and deletion operations
    • Layout and positioning functionality
    • Content validation and constraints
  2. Documentation exists in docs/Changelog.md with an entry for sticky notes feature (Sticky notes #7181)
  3. The feature is properly documented in the changelog with:
    • Purpose: "sticky notes are designed to store information inside scenario/fragment"
    • API details: "new API available under processes/{scenarioName}/stickyNotes"
    • Configuration: "stickyNotesSettings allowing to hide/show stickyNotes, set sticky notes max content length or its max number on a graph"
🔗 Analysis chain

Verify PR requirements completion.

Please ensure the following checklist items are addressed:

  1. Add tests for the new sticky notes functionality
  2. Update documentation if needed
  3. Add an entry to _Changelog.md_ describing the sticky notes feature
🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Check for test coverage
echo "Checking for test files..."
fd -e test.ts -e spec.ts -e test.tsx -e spec.tsx . | grep -i "sticky"

# Check for documentation updates
echo "Checking for documentation..."
fd -e md . | xargs grep -l "sticky"

Length of output: 510


Script:

#!/bin/bash
# Let's check the content of the changelog and migration guide
echo "=== Checking Changelog.md ==="
cat docs/Changelog.md

echo -e "\n=== Checking MigrationGuide.md ==="
cat docs/MigrationGuide.md

# Let's also search for any test files related to the feature
echo -e "\n=== Searching for test files in relevant directories ==="
fd -e test.ts -e spec.ts -e test.tsx -e spec.tsx . designer/client/src/reducers/graph/

# Let's check if there are any sticky note related files that might need tests
echo -e "\n=== Searching for sticky note related files ==="
rg -l "sticky" designer/client/src/

Length of output: 66190


Script:

#!/bin/bash
# Let's check for sticky note related files and their content
echo "=== Checking sticky note related files ==="
rg -C 5 "sticky" designer/client/src/

# Let's also check for test files in the graph directory
echo -e "\n=== Checking test files in graph directory ==="
fd -e test.ts -e spec.ts -e test.tsx -e spec.tsx . designer/client/src/reducers/graph/ -x cat {}

Length of output: 73421

Comment on lines +47 to +51
onChange: function (evt) {
this.model.trigger(Events.CELL_CONTENT_UPDATED, this.model, evt.target.value);
this.model.attr(`${MARKDOWN_EDITOR_NAME}/props/value`, evt.target.value);
this.model.attr(`${MARKDOWN_EDITOR_NAME}/props/disabled`, true);
},
Copy link

Choose a reason for hiding this comment

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

💡 Codebase verification

⚠️ Potential issue

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

Content length validation is required and type exists

The codebase has StickyNotesSettings type defined with maxContentLength property, but the validation is not implemented in the onChange handler. The settings should be accessible via model.get('settings') as suggested.

  • Location: designer/client/src/components/graph/StickyNoteElement.ts
  • Settings type: designer/client/src/actions/nk/assignSettings.ts

The suggested fix in the review comment is correct and should be implemented to prevent content from exceeding the configured maximum length.

🔗 Analysis chain

Verify content length constraints

The onChange handler should validate the content length against maxContentLength from settings.

Add content length validation:

     onChange: function (evt) {
+        const content = evt.target.value;
+        const settings = this.model.get('settings');
+        if (settings?.maxContentLength && content.length > settings.maxContentLength) {
+            return;
+        }
-        this.model.trigger(Events.CELL_CONTENT_UPDATED, this.model, evt.target.value);
-        this.model.attr(`${MARKDOWN_EDITOR_NAME}/props/value`, evt.target.value);
+        this.model.trigger(Events.CELL_CONTENT_UPDATED, this.model, content);
+        this.model.attr(`${MARKDOWN_EDITOR_NAME}/props/value`, content);
         this.model.attr(`${MARKDOWN_EDITOR_NAME}/props/disabled`, true);
     },
🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Search for maxContentLength validation in the codebase
rg "maxContentLength.*stickyNote" --type ts

Length of output: 45


Script:

#!/bin/bash
# Let's search for any maxContentLength validation patterns in the codebase
rg "maxContentLength" --type ts -A 3

# Also search for any content length validation in StickyNoteElement
rg "length.*validation|validation.*length" --type ts designer/client/src/components/graph/StickyNoteElement.ts

# Let's also check the settings interface/type for sticky notes
ast-grep --pattern 'interface $_ {
  $$$
  maxContentLength
  $$$
}'

Length of output: 498


Script:

#!/bin/bash
# Let's check how settings are used in StickyNoteElement and its parent components
rg "settings" designer/client/src/components/graph/StickyNoteElement.ts -A 3

# Also check where the settings type is defined and used
ast-grep --pattern 'type $_ = {
  $$$
  maxContentLength
  $$$
}'

# Check for any existing content validation in sticky notes
rg "content.*length|length.*content" --type ts designer/client/src/components/graph/

Length of output: 609


Script:

#!/bin/bash
# Let's check how the settings are passed to StickyNoteElement
rg "StickyNoteElement" --type ts -A 5

# Check how the settings are accessed in the model
rg "model.*settings|settings.*model" --type ts designer/client/src/components/graph/

# Check for any existing validation in the Events handler
rg "CELL_CONTENT_UPDATED" --type ts -A 5

Length of output: 14535

Comment on lines +76 to +82
export function stickyNoteUpdated(scenarioName: string, scenarioVersionId: number, stickyNote: StickyNote): ThunkAction {
return (dispatch) => {
HttpService.updateStickyNote(scenarioName, scenarioVersionId, stickyNote).then((_) => {
refreshStickyNotes(dispatch, scenarioName, scenarioVersionId);
});
};
}
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue

Add error handling to sticky note operations

The sticky note operations (update, delete, add) should include error handling and user feedback.

Example fix for stickyNoteUpdated (apply similar pattern to other operations):

 export function stickyNoteUpdated(scenarioName: string, scenarioVersionId: number, stickyNote: StickyNote): ThunkAction {
     return (dispatch) => {
-        HttpService.updateStickyNote(scenarioName, scenarioVersionId, stickyNote).then((_) => {
-            refreshStickyNotes(dispatch, scenarioName, scenarioVersionId);
-        });
+        return HttpService.updateStickyNote(scenarioName, scenarioVersionId, stickyNote)
+            .then((_) => {
+                return refreshStickyNotes(dispatch, scenarioName, scenarioVersionId);
+            })
+            .catch((error) => {
+                console.error("Failed to update sticky note:", error);
+                dispatch({ type: "STICKY_NOTE_ERROR", error: error.message });
+            });
     };
 };

Also applies to: 84-92, 94-100

Comment on lines +63 to +70
const refreshStickyNotes = (dispatch, scenarioName: string, scenarioVersionId: number) => {
return HttpService.getStickyNotes(scenarioName, scenarioVersionId).then((stickyNotes) => {
flushSync(() => {
dispatch({ type: "STICKY_NOTES_UPDATED", stickyNotes: stickyNotes.data });
dispatch(layoutChanged());
});
});
};
Copy link

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

Consider removing flushSync for better performance

Using flushSync forces a synchronous update which can impact performance. Consider if this is really necessary here.

 const refreshStickyNotes = (dispatch, scenarioName: string, scenarioVersionId: number) => {
     return HttpService.getStickyNotes(scenarioName, scenarioVersionId).then((stickyNotes) => {
-        flushSync(() => {
             dispatch({ type: "STICKY_NOTES_UPDATED", stickyNotes: stickyNotes.data });
             dispatch(layoutChanged());
-        });
     });
 };
📝 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
const refreshStickyNotes = (dispatch, scenarioName: string, scenarioVersionId: number) => {
return HttpService.getStickyNotes(scenarioName, scenarioVersionId).then((stickyNotes) => {
flushSync(() => {
dispatch({ type: "STICKY_NOTES_UPDATED", stickyNotes: stickyNotes.data });
dispatch(layoutChanged());
});
});
};
const refreshStickyNotes = (dispatch, scenarioName: string, scenarioVersionId: number) => {
return HttpService.getStickyNotes(scenarioName, scenarioVersionId).then((stickyNotes) => {
dispatch({ type: "STICKY_NOTES_UPDATED", stickyNotes: stickyNotes.data });
dispatch(layoutChanged());
});
};

borderColor: "Transparent",
whiteSpace: "pre-line",
overflow: "hidden",
clipPath: "xywh(0 0px 100% 100% round 0 0 16% 0)",
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue

Fix non-standard clipPath syntax

The clipPath property uses a non-standard xywh syntax which might not be supported across all browsers.

Replace with standard syntax:

-clipPath: "xywh(0 0px 100% 100% round 0 0 16% 0)",
+clipPath: "polygon(0 0, 100% 0, 100% 84%, 0 100%)",
📝 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
clipPath: "xywh(0 0px 100% 100% round 0 0 16% 0)",
clipPath: "polygon(0 0, 100% 0, 100% 84%, 0 100%)",

@@ -22,6 +22,7 @@
"d3-transition": "3.0.1",
"d3-zoom": "3.0.0",
"dagre": "0.8.5",
"dompurify": "3.2.0",
Copy link

Choose a reason for hiding this comment

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

💡 Codebase verification

Security update required: DOMPurify 3.2.0 has known vulnerabilities

The current version (3.2.0) is vulnerable to high-severity security issues. The latest version is 3.2.2, and there are multiple security advisories:

  • High severity: nesting-based mXSS vulnerability affecting versions >= 3.0.0, < 3.1.3
  • High severity: prototype pollution vulnerability affecting versions >= 3.0.0, < 3.1.3

Recommendation:

  • Update DOMPurify to version 3.2.2 to ensure the latest security patches are applied
🔗 Analysis chain

Verify DOMPurify version and security implications

The addition of DOMPurify is good for XSS prevention, but let's verify the version.

Run the following to check for security advisories and latest version:

🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Check npm for latest versions and security advisories
npm view dompurify versions --json | jq -r '.[-1]'
npm audit || true

# Check for known vulnerabilities
gh api graphql -f query='
{
  securityVulnerabilities(first: 5, ecosystem: NPM, package: "dompurify") {
    nodes {
      advisory {
        summary
        severity
        publishedAt
      }
      vulnerableVersionRange
      firstPatchedVersion {
        identifier
      }
    }
  }
}'

Length of output: 1828

Comment on lines +10 to +13
* [#7181](https://github.com/TouK/nussknacker/pull/7181) Added designer configuration: stickyNotesSettings
* maxContentLength - max length of a sticky notes content (characters)
* maxNotesCount - max count of sticky notes inside one scenario/fragment
* enabled - if set to false stickyNotes feature is disabled, stickyNotes cant be created, they are also not loaded to graph
Copy link

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

Add configuration example for sticky notes settings

The sticky notes configuration changes would be clearer with a concrete example showing the old vs new configuration format:

- // Old config
- defaultDesignerConfig {}

+ // New config 
+ defaultDesignerConfig {
+   stickyNotesSettings {
+     maxContentLength = 5000
+     maxNotesCount = 5
+     enabled = true
+   }
+ }
📝 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
* [#7181](https://github.com/TouK/nussknacker/pull/7181) Added designer configuration: stickyNotesSettings
* maxContentLength - max length of a sticky notes content (characters)
* maxNotesCount - max count of sticky notes inside one scenario/fragment
* enabled - if set to false stickyNotes feature is disabled, stickyNotes cant be created, they are also not loaded to graph
* [#7181](https://github.com/TouK/nussknacker/pull/7181) Added designer configuration: stickyNotesSettings
* maxContentLength - max length of a sticky notes content (characters)
* maxNotesCount - max count of sticky notes inside one scenario/fragment
* enabled - if set to false stickyNotes feature is disabled, stickyNotes cant be created, they are also not loaded to graph
// Old config
defaultDesignerConfig {}
// New config
defaultDesignerConfig {
stickyNotesSettings {
maxContentLength = 5000
maxNotesCount = 5
enabled = true
}
}

Copy link

coderabbitai bot commented Dec 3, 2024

📝 Walkthrough

Walkthrough

The pull request introduces several significant changes related to the implementation of sticky notes within the application. A new test case for the "Creator toolbar" component has been added to the Cypress end-to-end testing framework, specifically focusing on the interaction with a new "stickyNotes" element. Additionally, the package.json file has been updated to include a new dependency, dompurify, for sanitizing HTML content.

New action types for sticky notes have been defined, and a new type StickyNotesSettings has been integrated into the existing FeaturesSettings type. Furthermore, multiple functions have been introduced in various files to handle the creation, updating, deletion, and fetching of sticky notes. The Graph component has been enhanced to manage sticky notes, including methods for adding, updating, and deleting them.

The database schema has also been updated to support sticky notes, with new migration files created for both PostgreSQL and HSQLDB. API endpoints for CRUD operations related to sticky notes have been defined, and the configuration files have been updated to include settings for sticky notes. Overall, the changes significantly expand the application's functionality regarding sticky notes, enhancing user interaction and data management.

Possibly related PRs

Suggested labels

submodules

Suggested reviewers

  • JulianWielga

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.

CodeRabbit Configuration File (.coderabbit.yaml)

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

Documentation and Community

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

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 21

🧹 Outside diff range and nitpick comments (53)
designer/server/src/main/scala/pl/touk/nussknacker/ui/db/NuTables.scala (1)

14-15: LGTM! Clean trait composition.

The addition of StickyNotesEntityFactory follows the existing architectural pattern of composing database operations through trait inheritance. This maintains good separation of concerns and modularity.

The modular approach allows for easy extension of database operations while keeping entity-specific logic isolated.

designer/server/src/main/scala/pl/touk/nussknacker/ui/api/description/StickyNotesApiEndpoints.scala (6)

35-35: Import Missing for VersionIdCodec

The import for TapirCodecs.VersionIdCodec._ is present, but ensure that all necessary codecs for VersionId are properly imported and used.


37-37: Unused Encoder for TypingResult

The declared lazy val encoder: Encoder[TypingResult] = TypingResult.encoder does not seem to be used within this class.

Consider removing this encoder if it's unnecessary to keep the code clean and maintainable.


41-50: Move Example Sticky Note to Companion Object

The exampleStickyNote is defined within the class but is only used for examples. For better organization, consider moving it to the Examples object or a separate companion object.


87-119: Consistent Use of Status Codes

In stickyNotesUpdateEndpoint, the success output is defined as statusCode(Ok), but no body is returned. Consider returning an explicit confirmation message or the updated sticky note for client confirmation.

Alternatively, if no content is returned, you might consider using statusCode(NoContent) for clarity.


121-141: Ensure Example Consistency in Add Endpoint

The example provided in stickyNotesAddEndpoint uses empty strings and default values. For better clarity, provide realistic example data to help consumers understand the expected input format.


166-217: Reduce Redundancy in Error Examples

The Examples object contains repeated patterns in defining error examples. Consider creating helper methods to generate these examples to reduce repetition and enhance maintainability.

For instance, a method like def createErrorExample[T](statusCode: StatusCode, body: T, summary: String): EndpointOutput.OneOfVariant[T] could streamline the example creation process.

designer/server/src/main/scala/pl/touk/nussknacker/ui/server/AkkaHttpBasedRouteProvider.scala (1)

402-409: Consider extracting configuration parameters to a dedicated class.

The StickyNotesApiHttpService initialization follows the pattern of other services but has multiple parameters. Consider creating a configuration class to encapsulate these parameters for better maintainability.

Example refactor:

+case class StickyNotesServiceConfig(
+  authManager: AuthManager,
+  stickyNotesRepository: DbStickyNotesRepository,
+  scenarioService: DBProcessService,
+  scenarioAuthorizer: AuthorizeProcess,
+  dbioRunner: DBIOActionRunner,
+  stickyNotesSettings: StickyNotesSettings
+)

-val stickyNotesApiHttpService = new StickyNotesApiHttpService(
-  authManager = authManager,
-  stickyNotesRepository = stickyNotesRepository,
-  scenarioService = processService,
-  scenarioAuthorizer = processAuthorizer,
-  dbioRunner,
-  stickyNotesSettings = featureTogglesConfig.stickyNotesSettings
-)
+val stickyNotesConfig = StickyNotesServiceConfig(
+  authManager,
+  stickyNotesRepository,
+  processService,
+  processAuthorizer,
+  dbioRunner,
+  featureTogglesConfig.stickyNotesSettings
+)
+val stickyNotesApiHttpService = new StickyNotesApiHttpService(stickyNotesConfig)
docs/MigrationGuide.md (2)

4-4: Missing test coverage

The TODO comment indicates missing tests. Please add unit tests to verify the function behavior, especially edge cases like:

  • Large exponents
  • Negative numbers
  • Zero values

Would you like me to help create unit tests for this function?


Security vulnerability found in requests 2.26.0 - Update required

The pinned version 2.26.0 of requests is vulnerable to a moderate severity issue that leaks Proxy-Authorization headers (CVE published on 2023-05-22). This version is within the vulnerable range ">=2.3.0, < 2.31.0". Additionally, a more recent vulnerability affecting versions "< 2.32.0" has been identified (published 2024-05-20).

Recommendations:

  • Update to at least version 2.32.0 to patch both vulnerabilities
  • Consider using version range ">=2.32.0,<3.0.0" to receive future security updates while maintaining compatibility
🔗 Analysis chain

Line range hint 1-8: Verify requests package version for security vulnerabilities

The requests package is pinned to version 2.26.0. Please verify:

  1. If this version has any known security vulnerabilities
  2. Consider using version range (e.g., ">=2.26.0,<3.0.0") to get security updates
🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Check for security advisories for requests package
gh api graphql -f query='
{
  securityVulnerabilities(first: 5, ecosystem: PIP, package: "requests") {
    nodes {
      advisory {
        summary
        severity
        publishedAt
      }
      vulnerableVersionRange
      firstPatchedVersion {
        identifier
      }
    }
  }
}'

Length of output: 1523

designer/server/src/main/scala/pl/touk/nussknacker/ui/db/entity/StickyNotesEntityFactory.scala (2)

69-76: Refactor duplicated JSON mapping logic for LayoutData and Dimensions

The implicit MappedColumnType definitions for LayoutData and Dimensions share similar serialization and deserialization logic. Consider abstracting the common functionality into a generic method or utility function to reduce code duplication and improve maintainability.

Also applies to: 78-85


73-75: Enhance error handling in JSON deserialization

When parsing JSON strings in layoutDataColumnTyped and dimensionsColumnTyped, an exception is thrown directly upon parsing failure. To improve debugging and error tracing, consider wrapping the exception with additional context or logging the error message before throwing.

Also applies to: 82-84

designer/server/src/main/scala/db/migration/V1_060__CreateStickyNotesDefinition.scala (2)

8-8: Consider removing the unnecessary dependency on Shapeless

The import of shapeless.syntax.std.tuple._ may be unnecessary here. The tuple operations can be achieved using standard library functions without adding the Shapeless dependency. Removing it can reduce complexity and potential issues arising from using Shapeless.


67-69: Simplify the mapping by explicitly defining the column mapping

Using productElements and Shapeless for mapping can be avoided by explicitly specifying the column mappings. This improves code readability and eliminates the need for additional libraries.

Apply this change to define the mapping explicitly:

- override def * =
-   (id :: tupleWithoutAutoIncId.productElements).tupled <> (
-     StickyNoteEventEntityData.apply _ tupled, StickyNoteEventEntityData.unapply
-   )
+ override def * = (
+   id,
+   noteCorrelationId,
+   content,
+   layoutData,
+   color,
+   dimensions,
+   targetEdge,
+   eventCreator,
+   eventDate,
+   eventType,
+   scenarioId,
+   scenarioVersionId
+ ) <> (StickyNoteEventEntityData.tupled, StickyNoteEventEntityData.unapply)
designer/server/src/main/scala/pl/touk/nussknacker/ui/api/description/stickynotes/Dtos.scala (1)

77-81: Ensure consistent use of derivation for encoders, decoders, and schemas

Consider using @derive(encoder, decoder, schema) for StickyNotesSettings to maintain consistency with other case classes. Currently, @JsonCodec is used, which may lead to inconsistent derivation and potential serialization issues.

Apply this change to align with other classes:

- @JsonCodec case class StickyNotesSettings(
+ @derive(encoder, decoder, schema)
+ case class StickyNotesSettings(
    maxContentLength: Int,
    maxNotesCount: Int,
    enabled: Boolean
  )
designer/server/src/main/scala/pl/touk/nussknacker/ui/process/repository/stickynotes/DbStickyNotesRepository.scala (1)

120-122: Handle missing records more gracefully

Instead of throwing a NoSuchElementException, consider returning a meaningful error or an Option type to indicate the absence of the record. This can improve error handling and code maintainability.

designer/server/src/main/scala/pl/touk/nussknacker/ui/api/StickyNotesApiHttpService.scala (1)

117-124: Simplify authorization check logic

In the isAuthorized method, consider simplifying the mapping of the Boolean result to an Either using EitherT.cond for improved readability.

Apply this diff:

-private def isAuthorized(scenarioId: ProcessId, permission: Permission)(
-    implicit loggedUser: LoggedUser
-): EitherT[Future, StickyNotesError, Unit] =
-  EitherT(
-    scenarioAuthorizer
-      .check(scenarioId, permission, loggedUser)
-      .map[Either[StickyNotesError, Unit]] {
-        case true  => Right(())
-        case false => Left(NoPermission)
-      }
-  )
+private def isAuthorized(scenarioId: ProcessId, permission: Permission)(
+    implicit loggedUser: LoggedUser
+): EitherT[Future, StickyNotesError, Unit] =
+  EitherT.cond[Future](
+    scenarioAuthorizer.check(scenarioId, permission, loggedUser),
+    (),
+    NoPermission
+  )
designer/client/src/types/stickyNote.ts (1)

2-3: Use template literals and add explicit return type

Consider using template literals for string concatenation to improve readability and adding an explicit return type for the function.

Apply this diff:

-export function createStickyNoteId(noteId: number) {
-    return StickyNoteType + "_" + noteId;
+export function createStickyNoteId(noteId: number): string {
+    return `${StickyNoteType}_${noteId}`;
+}
designer/client/src/common/StickyNote.ts (2)

3-3: Consider using a more specific type for dimensions

The Dimensions type could benefit from constraints to prevent invalid values.

Consider using:

export type Dimensions = {
  width: number & { readonly brand: unique symbol };
  height: number & { readonly brand: unique symbol };
};

// With helper functions to ensure positive values
export const createDimension = (value: number): number & { readonly brand: unique symbol } => {
  if (value <= 0) throw new Error("Dimension must be positive");
  return value as number & { readonly brand: unique symbol };
};

5-15: Consider adding type safety for colors and content length

The interface could benefit from stronger typing for certain fields.

Consider these improvements:

export type Color = '#FFF' | '#F0F0F0' | /* other valid colors */;

export interface StickyNote {
  // ... other fields
  color: Color;  // Instead of string
  content: string & { readonly maxLength: 1000 };  // Example content length constraint
  // ... other fields
}

// Helper to validate content length
export const createContent = (content: string, maxLength: number): string & { readonly maxLength: number } => {
  if (content.length > maxLength) throw new Error(`Content exceeds maximum length of ${maxLength}`);
  return content as string & { readonly maxLength: number };
};
designer/server/src/main/scala/pl/touk/nussknacker/ui/api/description/stickynotes/StickyNoteEvent.scala (1)

1-14: Add documentation for the event types

Consider adding ScalaDoc comments to document the purpose and usage of each event type.

Add documentation like:

/** Events representing sticky note lifecycle changes */
sealed trait StickyNoteEvent

object StickyNoteEvent {
  /** Represents creation of a new sticky note */
  case object Created extends StickyNoteEvent
  
  /** Represents update to an existing sticky note */
  case object Updated extends StickyNoteEvent
  
  /** Represents deletion of a sticky note */
  case object Deleted extends StickyNoteEvent
  // ... rest of the implementation
}
designer/client/src/components/toolbars/creator/StickyNoteComponent.tsx (1)

5-19: Improve type safety and add documentation

The component could benefit from:

  1. Explicit return type instead of type assertion
  2. JSDoc documentation explaining the component's purpose and usage
  3. Unit tests to verify behavior
+/**
+ * Creates a component group for sticky notes
+ * @param pristine - Indicates if the component should be enabled
+ * @returns Array of ComponentGroup containing sticky note configuration
+ */
-export const stickyNoteComponentGroup = (pristine: boolean) => {
+export const stickyNoteComponentGroup = (pristine: boolean): ComponentGroup[] => {
     return [
         {
             components: [
                 {
                     node: noteModel,
                     label: "sticky note",
                     componentId: StickyNoteType + "_" + pristine,
                     disabled: () => !pristine,
                 },
             ],
             name: "stickyNotes",
-        } as ComponentGroup,
+        },
     ];
 };

Would you like me to help create unit tests for this component?

designer/client/src/assets/json/nodeAttributes.json (1)

41-43: Consider adding additional attributes for StickyNote integration

While the basic structure is correct, consider adding attributes that might be useful for sticky notes, such as:

  • notEditable flag (similar to _group)
  • Default styling properties
  • Maximum content length constraints
 "StickyNote": {
-    "name": "StickyNote"
+    "name": "StickyNote",
+    "notEditable": false,
+    "defaultStyle": {
+        "width": 200,
+        "height": 150
+    },
+    "constraints": {
+        "maxContentLength": 1000
+    }
 }
designer/client/src/containers/theme/helpers.ts (1)

19-26: Consider adding type safety and browser compatibility checks

The implementation could benefit from the following improvements:

  1. Add type safety for the color parameter using a union type or validation
  2. Add fallback for browsers that don't support CSS.supports

Consider this implementation:

-export function getStickyNoteBackgroundColor(theme: Theme, color: string) {
+export function getStickyNoteBackgroundColor(theme: Theme, color: string) {
+    const isValidColor = typeof CSS !== "undefined" && CSS.supports
+        ? CSS.supports("color", color)
+        : /^#([0-9A-F]{3}){1,2}$/i.test(color) || /^rgb/i.test(color);
     return theme.palette.augmentColor({
         color: {
             main: isValidColor ? color : STICKY_NOTE_DEFAULT_COLOR,
         },
     });
 }
designer/client/src/actions/notificationActions.tsx (1)

15-15: Remove TODO comment after review confirmation

The TODO comment should be removed once the implementation is confirmed correct.

designer/client/src/components/graph/GraphPartialsInTS/cellUtils.ts (2)

12-14: Extract sticky note type constant

The magic string stickyNote.StickyNoteElement should be extracted to a constant to maintain consistency and prevent typos.

+const STICKY_NOTE_TYPE = "stickyNote.StickyNoteElement";
+
 export function isStickyNoteElement(el: dia.Cell): el is shapes.devs.Model {
-    return isElement(el) && el.get("type") === `stickyNote.StickyNoteElement`;
+    return isElement(el) && el.get("type") === STICKY_NOTE_TYPE;
 }

16-21: Optimize sticky note lookup and improve type safety

The current implementation has potential performance and type safety concerns:

  1. Deep cloning might be unnecessary for all use cases
  2. Type safety for noteId property access could be improved

Consider this implementation:

+interface StickyNoteElement extends shapes.devs.Model {
+    get(key: "noteId"): string | undefined;
+}
+
-export function getStickyNoteCopyFromCell(stickyNotes: StickyNote[], el: dia.Cell): StickyNote | undefined {
+export function getStickyNoteCopyFromCell(
+    stickyNotes: StickyNote[],
+    el: dia.Cell,
+    shouldClone = true
+): StickyNote | undefined {
     const noteId = el.get("noteId");
     if (!isStickyNoteElement(el) || !noteId) return undefined;
     const stickyNote = stickyNotes.find((note) => note.noteId == noteId);
-    return stickyNote ? cloneDeep(stickyNote) : undefined;
+    return stickyNote ? (shouldClone ? cloneDeep(stickyNote) : stickyNote) : undefined;
 }

This:

  1. Adds type safety for the noteId property
  2. Makes deep cloning optional via a parameter
  3. Improves performance when cloning isn't needed
designer/server/src/main/scala/pl/touk/nussknacker/ui/process/repository/stickynotes/StickyNotesRepository.scala (2)

21-29: Consider adding pagination and filtering capabilities

The findStickyNotes and countStickyNotes methods could benefit from pagination and filtering parameters to handle large datasets efficiently and provide more flexible querying capabilities.

Consider adding parameters like:

def findStickyNotes(
    scenarioId: ProcessId,
    scenarioVersionId: VersionId,
    offset: Int,
    limit: Int,
    filters: StickyNoteFilters
): DB[Seq[StickyNote]]

31-40: Consider adding batch operations for better performance

The repository could benefit from batch operations to handle multiple sticky notes efficiently.

Consider adding methods like:

def addStickyNotes(
    notes: Seq[StickyNoteData],
    scenarioId: ProcessId,
    scenarioVersionId: VersionId
)(implicit user: LoggedUser): DB[Seq[StickyNoteCorrelationId]]
designer/client/src/types/node.ts (1)

Line range hint 13-35: Remove TODO placeholders and index signature

The NodeType interface has several issues that could lead to type safety problems:

  1. Uses $TodoType placeholders
  2. Has a catch-all index signature [key: string]: any

Consider:

  1. Replacing $TodoType with proper types
  2. Removing the index signature and explicitly defining all properties
export type NodeType<F extends Field = Field> = {
    id: string;
    type: Type;
    isDisabled?: boolean;
    additionalFields?: {
        description: string;
        layoutData?: LayoutData;
    };
    parameters?: Parameter[];
    branchParameters?: BranchParams[];
-   branchParametersTemplate?: BranchParametersTemplate;
+   branchParametersTemplate?: {
+     // Define specific structure here
+   };
    ref?: {
        id: string;
        typ: string;
        parameters: Parameter[];
        outputVariableNames: Record<string, string>;
    };
    varName?: string;
-   value?: $TodoType;
+   value?: unknown; // Replace with specific type
    fields?: Array<F>;
    outputName?: string;
    service?: {
        id: string;
        parameters?: Parameter[];
    };
    nodeType?: string;
-   //TODO: Remove me and add correct properties
-   [key: string]: any;
}
designer/server/src/test/resources/config/common-designer.conf (1)

57-61: Consider more flexible sticky notes configuration

The current configuration has some potential limitations:

  1. maxNotesCount: 5 might be too restrictive for complex scenarios
  2. No content validation rules (e.g., for HTML/markdown)

Consider adding:

stickyNotesSettings: {
  maxContentLength: 5000,
  maxNotesCount: 20,  # Increased limit
  enabled: true,
  contentValidation: {
    allowHtml: false,
    allowMarkdown: true,
    sanitizationRules: [
      # Define content sanitization rules
    ]
  }
}
designer/client/src/components/graph/StickyNoteElement.ts (2)

17-18: Consider adding JSDoc documentation

The StickyNoteElement factory function would benefit from documentation explaining its purpose and parameters.

+/**
+ * Creates a new sticky note element type for JointJS diagrams
+ * @param defaults - Optional default properties for position, size, and attributes
+ * @param protoProps - Optional prototype properties including markup
+ * @returns A new JointJS element type for sticky notes
+ */
 export const StickyNoteElement = (defaults?: StickyNoteDefaults, protoProps?: StickyNoteProtoProps) =>
     dia.Element.define("stickyNote.StickyNoteElement", defaults, protoProps);

20-52: Consider enhancing error handling and accessibility

The view implementation looks solid but could benefit from some improvements:

  1. Error handling for the onChange event
  2. Accessibility enhancements for keyboard navigation
  3. Consider debouncing the onChange handler for performance
 export const StickyNoteElementView = dia.ElementView.extend({
     events: {
         "click textarea": "stopPropagation",
         "keydown textarea": "selectAll",
         "focusout textarea": "onChange",
         "dblclick .sticky-note-content": "showEditor",
+        "keydown .sticky-note-content": "handleKeyboardNavigation",
     },

+    handleKeyboardNavigation: function (evt) {
+        if (evt.code === "Enter" || evt.code === "Space") {
+            evt.preventDefault();
+            this.showEditor(evt);
+        }
+    },

     onChange: function (evt) {
+        try {
             this.model.trigger(Events.CELL_CONTENT_UPDATED, this.model, evt.target.value);
             this.model.attr(`${MARKDOWN_EDITOR_NAME}/props/value`, evt.target.value);
             this.model.attr(`${MARKDOWN_EDITOR_NAME}/props/disabled`, true);
+        } catch (error) {
+            console.error("Failed to update sticky note content:", error);
+            // Consider showing user feedback
+        }
     },
 });
designer/client/src/components/graph/node-modal/node/FragmentContent.tsx (1)

4-4: Consider memoizing sticky notes selector for performance

While the implementation is correct, consider memoizing the getStickyNotes selector using createSelector from Redux Toolkit or Reselect to prevent unnecessary re-renders of FragmentGraphPreview when unrelated state changes.

Example implementation:

// in selectors/graph.ts
import { createSelector } from '@reduxjs/toolkit';

export const getStickyNotes = createSelector(
  [(state) => state.graph.stickyNotes],
  (stickyNotes) => stickyNotes
);

Also applies to: 17-17, 44-44

designer/client/src/components/StickyNotePreview.tsx (4)

9-11: Consider using named constants for better readability

The magic numbers could be more descriptive with named constants:

-const PREVIEW_SCALE = 0.9;
-const ACTIVE_ROTATION = 2;
-const INACTIVE_SCALE = 1.5;
+const PREVIEW_SCALE_FACTOR = 0.9;
+const ACTIVE_ROTATION_DEGREES = 2;
+const INACTIVE_SCALE_MULTIPLIER = 1.5;

13-18: Add JSDoc documentation for the component and its props

Consider adding documentation to improve maintainability:

/**
 * Renders a preview of a sticky note with hover and active states
 * @param {Object} props - Component props
 * @param {boolean} [props.isActive] - Whether the note is in active state
 * @param {boolean} [props.isOver] - Whether the mouse is over the note
 * @returns {JSX.Element} Rendered sticky note preview
 */
export function StickyNotePreview({ isActive, isOver }: {
    isActive?: boolean;
    isOver?: boolean;
}): JSX.Element

30-30: Improve transform string readability

The transform string could be more readable by breaking it into parts:

-transform: `translate(-80%, -50%) scale(${scale}) rotate(${rotation}deg) scale(${finalScale})`,
+const transforms = [
+  'translate(-80%, -50%)',
+  `scale(${scale})`,
+  `rotate(${rotation}deg)`,
+  `scale(${finalScale})`,
+].join(' ');
+transform: transforms,

19-34: Consider extracting styles to a separate constant

The node styles object is quite large. Consider extracting it to improve readability:

const getNodeStyles = (theme, scale, rotation, finalScale, isActive) => css({
  position: "relative",
  width: STICKY_NOTE_CONSTRAINTS.DEFAULT_WIDTH,
  // ... other styles
});

// In component:
const nodeStyles = getNodeStyles(theme, scale, rotation, finalScale, isActive);
designer/client/src/components/graph/GraphPartialsInTS/applyCellChanges.ts (2)

23-24: Consider error handling for sticky note model creation

While the code creates sticky note models, it doesn't handle potential failures during model creation.

Consider wrapping the model creation in a try-catch block:

-    const stickyNotesModelsWithTools: ModelWithTool[] = stickyNotes.map(makeStickyNoteElement(processDefinitionData, theme));
-    const stickyNotesModels = stickyNotesModelsWithTools.map((a) => a.model);
+    const stickyNotesModelsWithTools: ModelWithTool[] = [];
+    stickyNotes.forEach(note => {
+        try {
+            stickyNotesModelsWithTools.push(makeStickyNoteElement(processDefinitionData, theme)(note));
+        } catch (error) {
+            console.error(`Failed to create sticky note model:`, error);
+        }
+    });
+    const stickyNotesModels = stickyNotesModelsWithTools.map((a) => a.model);

46-57: Consider batching tool additions for better performance

The current implementation adds tools to each sticky note view individually, which could be inefficient for large numbers of notes.

Consider batching the tool additions:

-    newStickyNotesModelsWithTools.forEach((m) => {
-        try {
-            const view = m.model.findView(paper);
-            if (!view) {
-                console.warn(`View not found for stickyNote model: ${m.model.id}`);
-                return;
-            }
-            view.addTools(m.tools);
-        } catch (error) {
-            console.error(`Failed to add tools to stickyNote view:`, error);
-        }
-    });
+    const viewsWithTools = newStickyNotesModelsWithTools
+        .map(m => ({ view: m.model.findView(paper), tools: m.tools }))
+        .filter(({ view }) => {
+            if (!view) {
+                console.warn(`View not found for stickyNote model`);
+                return false;
+            }
+            return true;
+        });
+    
+    try {
+        viewsWithTools.forEach(({ view, tools }) => view.addTools(tools));
+    } catch (error) {
+        console.error(`Failed to add tools to stickyNote views:`, error);
+    }
designer/client/src/components/graph/types.ts (1)

77-79: Consider grouping related events

The new cell events are related but separated from other cell events in the enum.

Consider grouping all cell-related events together for better maintainability:

    CELL_MOUSELEAVE = "cell:mouseleave",
    CELL_MOVED = "cellCustom:moved",
+    CELL_RESIZED = "cellCustom:resized",
+    CELL_CONTENT_UPDATED = "cellCustom:contentUpdated",
+    CELL_DELETED = "cellCustom:deleted",
    BLANK_POINTERCLICK = "blank:pointerclick",
-    CELL_RESIZED = "cellCustom:resized",
-    CELL_CONTENT_UPDATED = "cellCustom:contentUpdated",
-    CELL_DELETED = "cellCustom:deleted",
designer/server/src/test/scala/pl/touk/nussknacker/ui/api/StickyNotesApiHttpServiceBusinessSpec.scala (2)

84-84: Address TODO: Additional test coverage needed

The TODO comment indicates that more test cases are needed.

Would you like me to help generate additional test cases? Consider testing:

  • Adding sticky notes
  • Updating sticky notes
  • Deleting sticky notes
  • Concurrent modifications
  • Permission-based access

42-43: Consider parameterizing test data

The stickyNoteToAdd helper method uses hardcoded values.

Consider parameterizing the test data:

-  private def stickyNoteToAdd(versionId: VersionId): StickyNoteAddRequest =
-    StickyNoteAddRequest(versionId, "", LayoutData(0, 1), "#aabbcc", Dimensions(300, 200), None)
+  private def stickyNoteToAdd(
+    versionId: VersionId,
+    content: String = "",
+    layout: LayoutData = LayoutData(0, 1),
+    color: String = "#aabbcc",
+    dimensions: Dimensions = Dimensions(300, 200)
+  ): StickyNoteAddRequest =
+    StickyNoteAddRequest(versionId, content, layout, color, dimensions, None)
designer/client/src/components/toolbars/creator/ToolboxComponentGroup.tsx (1)

76-82: Consider adding type safety for the disabled property

The implementation looks good, but consider adding type safety for the disabled property in the component interface to ensure proper usage.

interface ComponentType {
+  disabled?: () => boolean;
   // ... other properties
}
designer/client/src/components/toolbars/creator/ToolBox.tsx (1)

124-128: Consider memoization dependencies optimization

While the useMemo implementation is correct, we could optimize the dependencies array.

Consider this optimization:

-const stickyNoteToolGroup = useMemo(() => stickyNoteComponentGroup(pristine), [pristine, props, t]);
+const stickyNoteToolGroup = useMemo(() => stickyNoteComponentGroup(pristine), [pristine]);

-}, [componentGroups, filters, stickyNoteToolGroup, stickyNotesSettings]);
+}, [componentGroups, filters, stickyNoteToolGroup, stickyNotesSettings.enabled]);
designer/client/src/reducers/selectors/graph.ts (1)

75-77: Consider null safety enhancement

While the selector implementation is correct, we could enhance null safety.

Consider this enhancement:

export const getStickyNotes = createSelector([getGraph, getStickyNotesSettings], (g, settings) =>
-    settings.enabled ? g.stickyNotes || ([] as StickyNote[]) : ([] as StickyNote[]),
+    settings?.enabled ? g?.stickyNotes ?? ([] as StickyNote[]) : ([] as StickyNote[]),
);
designer/client/src/components/graph/graphStyledWrapper.ts (1)

206-209: Enhance focus styles for better accessibility

While the focus styles are defined, consider adding outline-offset to ensure the outline doesn't blend with the element's border.

 .sticky-note-markdown-editor:focus": {
     outline: "none",
     boxShadow: `0 0 0 2px ${theme.palette.primary.main}`,
+    outlineOffset: "2px",
 },
designer/client/src/reducers/graph/utils.ts (1)

95-107: Consider validating layout positions

The function should validate that the layout positions are within valid bounds to prevent notes from being placed outside the visible area.

 export function prepareNewStickyNotesWithLayout(
     state: GraphState,
     stickyNotes: StickyNote[],
 ): { layout: NodePosition[]; stickyNotes: StickyNote[] } {
     const { layout } = state;
+    const isValidPosition = (pos: Position) => {
+        return pos.x >= 0 && pos.y >= 0;
+    };
     const updatedLayout = stickyNotes.map((stickyNote) => {
+        if (!isValidPosition(stickyNote.layoutData)) {
+            throw new Error(`Invalid position for sticky note ${stickyNote.noteId}`);
+        }
         return { id: createStickyNoteId(stickyNote.noteId), position: stickyNote.layoutData };
     });
designer/server/src/test/scala/pl/touk/nussknacker/test/utils/domain/ScenarioHelper.scala (1)

85-90: Add ScalaDoc for public methods

Public methods should be documented with ScalaDoc comments explaining their purpose, parameters, and return values.

+  /**
+   * Updates an existing scenario with new content.
+   *
+   * @param scenarioName The name of the scenario to update
+   * @param newScenario The new scenario content
+   * @return ProcessUpdated containing the update result
+   */
   def updateScenario(
       scenarioName: ProcessName,
       newScenario: CanonicalProcess
   ): ProcessUpdated = {
designer/client/src/reducers/graph/reducer.ts (1)

244-255: Consider adding error handling for sticky note state updates.

The reducer cases for sticky notes should handle potential edge cases where the state update might fail.

Consider adding error handling:

 case "STICKY_NOTES_UPDATED": {
-    const { stickyNotes, layout } = prepareNewStickyNotesWithLayout(state, action.stickyNotes);
+    try {
+        const { stickyNotes, layout } = prepareNewStickyNotesWithLayout(state, action.stickyNotes);
+        return {
+            ...addStickyNotesWithLayout(state, { stickyNotes, layout }),
+        };
+    } catch (error) {
+        console.error("Failed to update sticky notes:", error);
+        return state;
+    }
-    return {
-        ...addStickyNotesWithLayout(state, { stickyNotes, layout }),
-    };
 }
designer/client/src/http/HttpService.ts (1)

740-746: Consider adding pagination for getStickyNotes.

The getStickyNotes method might need pagination if there are many sticky notes.

Consider adding pagination parameters:

-getStickyNotes(scenarioName: string, scenarioVersionId: number): Promise<AxiosResponse<StickyNote[]>> {
+getStickyNotes(
+    scenarioName: string,
+    scenarioVersionId: number,
+    page: number = 1,
+    pageSize: number = 50
+): Promise<AxiosResponse<{ stickyNotes: StickyNote[], total: number }>> {
     const promise = api.get(
-        `/processes/${encodeURIComponent(scenarioName)}/stickyNotes?scenarioVersionId=${scenarioVersionId}`
+        `/processes/${encodeURIComponent(scenarioName)}/stickyNotes?scenarioVersionId=${scenarioVersionId}&page=${page}&pageSize=${pageSize}`
     );
     promise.catch((error) =>
         this.#addError(i18next.t("notification.error.failedToGetStickyNotes", "Failed to get sticky notes"), error, true),
     );
     return promise;
 }
docs-internal/api/nu-designer-openapi.yaml (2)

7182-7267: Consider adding schema validations for sticky note fields.

The schema definitions are well-structured but could benefit from additional validations:

  1. Add pattern validation for the color field to ensure valid hex color format
  2. Add min/max constraints for dimensions
  3. Add minLength for content field
    StickyNote:
      properties:
        color:
          type: string
+         pattern: '^#[0-9A-Fa-f]{6}$'
        dimensions:
          $ref: '#/components/schemas/Dimensions'
        content:
          type: string
+         minLength: 1

    Dimensions:
      properties:
        width:
          type: integer
          format: int64
+         minimum: 50
+         maximum: 1000
        height:
          type: integer
          format: int64
+         minimum: 50
+         maximum: 1000

2556-2589: Consider enhancing API documentation with more examples.

While the error handling is comprehensive, the documentation could be improved by:

  1. Adding more examples of successful responses showing different note colors, sizes, and content types
  2. Documenting any rate limiting policies if applicable
  3. Adding examples of common use cases (e.g., creating notes with markdown content)
        '200':
          description: ''
          content:
            application/json:
              schema:
                type: array
                items:
                  $ref: '#/components/schemas/StickyNote'
              examples:
                Example:
                  summary: List of valid sticky notes returned for scenario
                  value:
                  - noteId: 1
                    content: "##Title \nNote1"
                    layoutData:
                      x: 20
                      y: 30
                    color: '#99aa20'
                    dimensions:
                      width: 300
                      height: 200
                    editedBy: Marco
                    editedAt: '1970-01-21T00:35:36.602Z'
+               Example with Markdown:
+                 summary: Sticky note with rich markdown content
+                 value:
+                 - noteId: 2
+                   content: |
+                     # Important Note
+                     ## Key Points:
+                     - Point 1
+                     - Point 2
+                     
+                     [Link to docs](https://docs.example.com)
+                   layoutData:
+                     x: 100
+                     y: 200
+                   color: '#ff0000'
+                   dimensions:
+                     width: 400
+                     height: 300
+                   editedBy: John
+                   editedAt: '2024-01-17T14:21:17Z'
📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL

📥 Commits

Reviewing files that changed from the base of the PR and between 5a4bbe5 and 63c7064.

⛔ Files ignored due to path filters (3)
  • designer/client/cypress/e2e/__image_snapshots__/electron/Linux/Creator toolbar should allow collapse (persist) and filtering #0.png is excluded by !**/*.png
  • designer/client/package-lock.json is excluded by !**/package-lock.json
  • designer/server/src/main/resources/web/static/assets/components/StickyNote.svg is excluded by !**/*.svg
📒 Files selected for processing (59)
  • designer/client/cypress/e2e/creatorToolbar.cy.ts (1 hunks)
  • designer/client/package.json (1 hunks)
  • designer/client/src/actions/actionTypes.ts (1 hunks)
  • designer/client/src/actions/nk/assignSettings.ts (1 hunks)
  • designer/client/src/actions/nk/process.ts (3 hunks)
  • designer/client/src/actions/notificationActions.tsx (1 hunks)
  • designer/client/src/assets/json/nodeAttributes.json (1 hunks)
  • designer/client/src/common/StickyNote.ts (1 hunks)
  • designer/client/src/components/ComponentPreview.tsx (2 hunks)
  • designer/client/src/components/StickyNotePreview.tsx (1 hunks)
  • designer/client/src/components/graph/EspNode/stickyNote.ts (1 hunks)
  • designer/client/src/components/graph/EspNode/stickyNoteElements.ts (1 hunks)
  • designer/client/src/components/graph/Graph.tsx (13 hunks)
  • designer/client/src/components/graph/GraphPartialsInTS/applyCellChanges.ts (2 hunks)
  • designer/client/src/components/graph/GraphPartialsInTS/cellUtils.ts (2 hunks)
  • designer/client/src/components/graph/NodeDescriptionPopover.tsx (2 hunks)
  • designer/client/src/components/graph/ProcessGraph.tsx (4 hunks)
  • designer/client/src/components/graph/StickyNoteElement.ts (1 hunks)
  • designer/client/src/components/graph/fragmentGraph.tsx (1 hunks)
  • designer/client/src/components/graph/graphStyledWrapper.ts (2 hunks)
  • designer/client/src/components/graph/node-modal/node/FragmentContent.tsx (3 hunks)
  • designer/client/src/components/graph/types.ts (3 hunks)
  • designer/client/src/components/graph/utils/graphUtils.ts (1 hunks)
  • designer/client/src/components/toolbars/creator/ComponentIcon.tsx (2 hunks)
  • designer/client/src/components/toolbars/creator/StickyNoteComponent.tsx (1 hunks)
  • designer/client/src/components/toolbars/creator/ToolBox.tsx (2 hunks)
  • designer/client/src/components/toolbars/creator/ToolboxComponentGroup.tsx (1 hunks)
  • designer/client/src/containers/theme/helpers.ts (2 hunks)
  • designer/client/src/http/HttpService.ts (3 hunks)
  • designer/client/src/reducers/graph/reducer.ts (2 hunks)
  • designer/client/src/reducers/graph/types.ts (2 hunks)
  • designer/client/src/reducers/graph/utils.ts (3 hunks)
  • designer/client/src/reducers/selectors/graph.ts (2 hunks)
  • designer/client/src/reducers/selectors/settings.ts (1 hunks)
  • designer/client/src/types/component.ts (1 hunks)
  • designer/client/src/types/node.ts (1 hunks)
  • designer/client/src/types/stickyNote.ts (1 hunks)
  • designer/server/src/main/resources/defaultDesignerConfig.conf (1 hunks)
  • designer/server/src/main/scala/db/migration/V1_060__CreateStickyNotesDefinition.scala (1 hunks)
  • designer/server/src/main/scala/db/migration/hsql/V1_060__CreateStickyNotes.scala (1 hunks)
  • designer/server/src/main/scala/db/migration/postgres/V1_060__CreateStickyNotes.scala (1 hunks)
  • designer/server/src/main/scala/pl/touk/nussknacker/ui/api/SettingsResources.scala (3 hunks)
  • designer/server/src/main/scala/pl/touk/nussknacker/ui/api/StickyNotesApiHttpService.scala (1 hunks)
  • designer/server/src/main/scala/pl/touk/nussknacker/ui/api/description/StickyNotesApiEndpoints.scala (1 hunks)
  • designer/server/src/main/scala/pl/touk/nussknacker/ui/api/description/stickynotes/Dtos.scala (1 hunks)
  • designer/server/src/main/scala/pl/touk/nussknacker/ui/api/description/stickynotes/StickyNoteEvent.scala (1 hunks)
  • designer/server/src/main/scala/pl/touk/nussknacker/ui/config/FeatureTogglesConfig.scala (4 hunks)
  • designer/server/src/main/scala/pl/touk/nussknacker/ui/db/NuTables.scala (1 hunks)
  • designer/server/src/main/scala/pl/touk/nussknacker/ui/db/entity/StickyNotesEntityFactory.scala (1 hunks)
  • designer/server/src/main/scala/pl/touk/nussknacker/ui/process/repository/stickynotes/DbStickyNotesRepository.scala (1 hunks)
  • designer/server/src/main/scala/pl/touk/nussknacker/ui/process/repository/stickynotes/StickyNotesRepository.scala (1 hunks)
  • designer/server/src/main/scala/pl/touk/nussknacker/ui/server/AkkaHttpBasedRouteProvider.scala (4 hunks)
  • designer/server/src/test/resources/config/common-designer.conf (1 hunks)
  • designer/server/src/test/scala/pl/touk/nussknacker/test/base/it/WithSimplifiedConfigScenarioHelper.scala (2 hunks)
  • designer/server/src/test/scala/pl/touk/nussknacker/test/utils/domain/ScenarioHelper.scala (4 hunks)
  • designer/server/src/test/scala/pl/touk/nussknacker/ui/api/StickyNotesApiHttpServiceBusinessSpec.scala (1 hunks)
  • docs-internal/api/nu-designer-openapi.yaml (3 hunks)
  • docs/Changelog.md (1 hunks)
  • docs/MigrationGuide.md (1 hunks)
🧰 Additional context used
📓 Learnings (1)
designer/client/src/components/graph/Graph.tsx (1)
Learnt from: philemone
PR: TouK/nussknacker#7149
File: designer/client/src/components/graph/Graph.tsx:530-536
Timestamp: 2024-11-20T10:37:32.855Z
Learning: There is backend validation for sticky note content length, so frontend validation in the `updateStickyNote` method in `Graph.tsx` is unnecessary.
🔇 Additional comments (52)
docs/Changelog.md (1)

13-16: LGTM! Well-documented changelog entry.

The changelog entry properly documents the new StickyNotes feature with:

  • Clear description of the feature's purpose
  • API endpoint information
  • Configuration capabilities
designer/server/src/main/scala/pl/touk/nussknacker/ui/db/NuTables.scala (1)

15-15: Verify database components for sticky notes.

The addition of StickyNotesEntityFactory implies database operations for sticky notes.

Let's verify the existence of required database components:

✅ Verification successful

Database components for sticky notes are properly implemented

All required database components for sticky notes functionality are present in the codebase:

  • Entity factory: designer/server/src/main/scala/pl/touk/nussknacker/ui/db/entity/StickyNotesEntityFactory.scala
  • Migration scripts for both HSQL and Postgres: V1_060__CreateStickyNotes.scala
  • Repository interface and implementation: StickyNotesRepository.scala and DbStickyNotesRepository.scala
🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Description: Verify the existence of sticky notes related database components

# Check for the entity factory definition
echo "Checking for StickyNotesEntityFactory definition..."
fd -e scala "StickyNotesEntityFactory.scala"

# Check for database migration scripts
echo "Checking for migration scripts..."
fd -e scala "V1_060__CreateStickyNotes"

# Check for repository implementation
echo "Checking for repository implementation..."
rg -l "StickyNotesRepository"

Length of output: 1246

designer/client/src/components/graph/utils/graphUtils.ts (2)

130-130: LGTM! Well-designed signature change.

The new preventDefault parameter provides flexible control over event prevention while maintaining backward compatibility through its default value. The type signature is well-defined and follows TypeScript best practices.


133-134: Verify the event handling behavior with sticky notes.

The implementation looks correct. However, since this change is part of the sticky notes functionality, let's verify how it's being used.

✅ Verification successful

Based on the results, I can now generate the final response as we have sufficient information about the usage of handleGraphEvent and sticky note event handling.

The event handling implementation is correctly used with sticky notes

The verification shows that:

  • handleGraphEvent is used with isStickyNoteElement check in the preventDefault parameter to specifically exclude sticky notes from default event prevention
  • This is consistent across multiple event handlers in Graph.tsx where sticky notes need different behavior
  • The sticky note specific events (CELL_DELETED, CELL_RESIZED) are handled separately through the model's event system
🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Description: Find usages of handleGraphEvent to understand the impact on sticky notes functionality

# Search for handleGraphEvent usages
echo "=== handleGraphEvent usages ==="
rg -A 5 "handleGraphEvent" --type ts

# Search for sticky note related event handling
echo "=== Sticky note event handling ==="
rg -A 5 "stickyNote.*Event" --type ts

Length of output: 4172

designer/server/src/main/scala/pl/touk/nussknacker/ui/api/description/StickyNotesApiEndpoints.scala (2)

52-85: ⚠️ Potential issue

Clarify Endpoint Error Handling

In stickyNotesGetEndpoint, the error handling only includes noScenarioExample. Consider whether other potential errors should be captured, such as authorization errors or data retrieval issues.

Ensure that all relevant error cases are handled appropriately.

Would you like assistance in reviewing the error handling for completeness?


143-159: ⚠️ Potential issue

Handle Potential Authorization Errors in Delete Endpoint

The stickyNotesDeleteEndpoint only specifies noStickyNoteExample and noScenarioExample in error outputs. Consider including error handling for authorization failures or invalid input.

Ensure that the endpoint adequately handles all possible error scenarios.

designer/server/src/main/scala/pl/touk/nussknacker/ui/server/AkkaHttpBasedRouteProvider.scala (4)

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

The import for DbStickyNotesRepository is appropriately placed with other repository imports.


171-171: LGTM: Repository initialization follows existing patterns.

The stickyNotesRepository is correctly initialized using the same pattern as other repositories, with proper dependencies (dbRef and designerClock).


603-603: LGTM: Service registration is properly placed.

The stickyNotesApiHttpService is correctly registered in the nuDesignerApi along with other API services.


Line range hint 402-603: Verify error handling in the sticky notes implementation.

The sticky notes feature appears to be integrated with the authorization system, but we should verify proper error handling for common scenarios.

Let's check the implementation:

✅ Verification successful

Error handling in sticky notes implementation is properly implemented

The sticky notes implementation follows good error handling practices:

  1. Repository layer:
  • Uses DB[Option[T]] for safe database operations
  • Properly handles missing records with NoSuchElementException
  • Uses typed error handling with DBIOAction.failed
  1. Service layer:
  • Uses EitherT monad transformer for composable error handling
  • Has proper error types (StickyNotesError, NoStickyNote)
  • Includes authorization checks that return typed errors
  • Handles missing scenarios with NoScenario error type
  1. Database operations:
  • Wrapped in transactions via DBIOActionRunner
  • Uses proper error propagation through the stack
  • Handles concurrent modifications safely with event sourcing pattern
🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Description: Check error handling patterns in sticky notes implementation
# Expected: Error handling should follow similar patterns as other services

# Look for error handling patterns in the sticky notes implementation
ast-grep --pattern 'try {
  $$$
} catch {
  case $_ => $$$
}'

# Check for HTTP error responses
rg -A 2 "BadRequest|NotFound|Unauthorized|InternalServerError" 

Length of output: 66184


Script:

#!/bin/bash
# Let's check the sticky notes implementation for error handling patterns
rg -A 5 "StickyNotesApiHttpService|StickyNotesRepository" 

# Also check for HTTP error responses specifically in sticky notes related files
rg -A 2 "BadRequest|NotFound|Unauthorized|InternalServerError" designer/server/src/main/scala/pl/touk/nussknacker/ui/api/StickyNotesApiHttpService.scala

# And check the repository layer error handling
rg -A 5 "try|catch|throw|error" designer/server/src/main/scala/pl/touk/nussknacker/ui/repository/StickyNotesRepository.scala

Length of output: 11314


Script:

#!/bin/bash
# Let's check the actual implementation of StickyNotesApiHttpService for error handling
rg -A 10 "class StickyNotesApiHttpService" designer/server/src/main/scala/pl/touk/nussknacker/ui/api/StickyNotesApiHttpService.scala

# Also check for any error handling in the endpoints
rg -A 10 "def (get|post|put|delete)" designer/server/src/main/scala/pl/touk/nussknacker/ui/api/StickyNotesApiHttpService.scala

# And check the repository implementation for error handling
rg -A 10 "def (find|save|update|delete)" designer/server/src/main/scala/pl/touk/nussknacker/ui/process/repository/stickynotes/DbStickyNotesRepository.scala

Length of output: 4408

docs/MigrationGuide.md (1)

Line range hint 1-2: LGTM!

The subtraction function is correctly implemented.

designer/server/src/test/scala/pl/touk/nussknacker/test/base/it/WithSimplifiedConfigScenarioHelper.scala (2)

9-10: LGTM! Imports are well-organized and specific

The new imports are correctly placed and specifically import only the required types for the new sticky notes functionality.


44-46: LGTM! Method follows established patterns

The updateScenario method maintains consistency with other helper methods in the trait and provides a clean interface for testing scenario updates.

designer/client/src/components/graph/ProcessGraph.tsx (5)

6-6: LGTM: Import statements are properly organized

The new imports for sticky notes functionality are well-structured and all are being utilized within the component.

Also applies to: 13-24, 28-28


36-36: LGTM: State management follows Redux patterns

The sticky notes state is properly retrieved using the selector pattern, consistent with other state management in the component.


87-89: LGTM: Action creators follow Redux patterns

The sticky note action creators cover all necessary operations (add/update/delete) and are properly bound using bindActionCreators.


49-54: 🛠️ Refactor suggestion

Add null checks and error handling

The drop handler could be more robust:

  1. Add null check for clientOffset
  2. Consider adding error handling for addStickyNote and addNode calls

Consider applying this improvement:

 drop: (item: NodeType, monitor) => {
     const clientOffset = monitor.getClientOffset();
+    if (!clientOffset) return;
     const relOffset = graph.current.processGraphPaper.clientToLocalPoint(clientOffset);
     // to make node horizontally aligned
     const nodeInputRelOffset = relOffset.offset(RECT_WIDTH * -0.8, RECT_HEIGHT * -0.5);
     if (item?.type === StickyNoteType) {
-        graph.current.addStickyNote(scenario.name, scenario.processVersionId, mapValues(nodeInputRelOffset, Math.round));
+        try {
+            graph.current.addStickyNote(scenario.name, scenario.processVersionId, mapValues(nodeInputRelOffset, Math.round));
+        } catch (error) {
+            console.error('Failed to add sticky note:', error);
+        }
     } else {
-        graph.current.addNode(monitor.getItem(), mapValues(nodeInputRelOffset, Math.round));
+        try {
+            graph.current.addNode(monitor.getItem(), mapValues(nodeInputRelOffset, Math.round));
+        } catch (error) {
+            console.error('Failed to add node:', error);
+        }
         setLinksHovered(graph.current.graph);
     }
 },

Verify addStickyNote implementation

Let's verify the implementation of the addStickyNote method that's being called.


104-104: Verify GraphWrapped component's prop types

Ensure that the GraphWrapped component properly types and handles the stickyNotes prop.

✅ Verification successful

stickyNotes prop is properly typed and handled

The verification shows that:

  • The stickyNotes prop is properly typed in GraphProps interface (designer/client/src/components/graph/types.ts): stickyNotes: StickyNote[];
  • GraphWrapped component receives and forwards the prop correctly to the underlying Graph component
  • The Graph component properly handles the stickyNotes through its implementation in applyCellChanges.ts
🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Search for GraphWrapped component's prop types
ast-grep --pattern 'interface $_ {
  $$$
  stickyNotes?: $_
  $$$
}'

# Search for GraphWrapped's usage of stickyNotes prop
ast-grep --pattern 'function GraphWrapped($_) {
  $$$
  stickyNotes
  $$$
}'

Length of output: 149


Script:

#!/bin/bash
# Let's try a different approach to find GraphWrapped component and its prop types
# First, find the file containing GraphWrapped
fd GraphWrapped

# Then let's look at the content of files that might contain GraphWrapped
rg -l "GraphWrapped" | xargs cat

# Also search for any interfaces/types that might contain stickyNotes
rg "stickyNotes.*:" -A 2

Length of output: 17785

designer/server/src/main/scala/pl/touk/nussknacker/ui/db/entity/StickyNotesEntityFactory.scala (1)

22-59: Database entity StickyNotesEntity is well-defined

The table definition for StickyNotesEntity accurately maps the database columns to the case class StickyNoteEventEntityData. The foreign key constraint scenarioVersion is properly established, ensuring referential integrity with the processVersionsTable. The code follows best practices for Slick table definitions.

designer/client/src/components/graph/EspNode/stickyNote.ts (1)

67-67: ⚠️ Potential issue

Correct the DOMPurify configuration to properly add allowed attributes

The property ADD_ATTR may be incorrect when adding multiple attributes. To add an array of attributes like "target", use ADD_ATTRS instead of ADD_ATTR in the DOMPurify configuration.

Apply this fix to ensure proper sanitization:

-parsed = DOMPurify.sanitize(marked.parse(stickyNote.content, { renderer }), { ADD_ATTR: ["target"] });
+parsed = DOMPurify.sanitize(marked.parse(stickyNote.content, { renderer }), { ADD_ATTRS: ["target"] });

Likely invalid or redundant comment.

designer/client/src/components/graph/EspNode/stickyNoteElements.ts (1)

16-136: LGTM!

The implementation is correct, and the code is well-structured. The event handlers and tools are appropriately defined.

designer/server/src/main/scala/pl/touk/nussknacker/ui/process/repository/stickynotes/DbStickyNotesRepository.scala (2)

172-172: ⚠️ Potential issue

Remove the extraneous comma in the implicit parameter list

An extra comma at the end of the implicit parameter list in the create method may result in a syntax error.

Apply this fix:

  def create(dbRef: DbRef, clock: Clock)(
-     implicit executionContext: ExecutionContext,
+     implicit executionContext: ExecutionContext
  ): StickyNotesRepository = new DbStickyNotesRepository(dbRef, clock)

Likely invalid or redundant comment.


138-138: ⚠️ Potential issue

Remove the extraneous comma in the method parameter list

There is an extra comma at the end of the parameter list in the updateStickyNote method, which may cause a syntax error.

Apply this fix:

-       scenarioVersionId: VersionId,
+       scenarioVersionId: VersionId
)(
  implicit user: LoggedUser
): DB[Int] = {

Likely invalid or redundant comment.

designer/server/src/main/scala/db/migration/hsql/V1_060__CreateStickyNotes.scala (1)

1-8: LGTM!

The migration script correctly defines the HSQLDB profile for creating sticky notes. No issues found.

designer/server/src/main/scala/db/migration/postgres/V1_060__CreateStickyNotes.scala (1)

1-8: LGTM!

The migration script correctly defines the PostgreSQL profile for creating sticky notes. No issues found.

designer/client/src/types/component.ts (1)

8-8: LGTM! Verify disabled state handling

The optional disabled method is well-typed and maintains backward compatibility.

Let's verify how this disabled state is handled across components:

✅ Verification successful

Let me gather more information about how the disabled state is actually used in components.


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

Disabled state implementation is properly handled

The disabled state is consistently implemented across the codebase:

  • The optional disabled method in Component type is used correctly in ToolboxComponentGroup.tsx with proper boolean evaluation
  • Disabled states are properly propagated to UI elements with appropriate styling and cursor handling
  • The implementation follows React/TypeScript best practices for disabled state management
🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Search for implementations of the disabled method
ast-grep --pattern 'disabled(): boolean {
  $$$
}'

# Search for places where disabled state is checked
rg 'disabled\?' designer/client/src/

Length of output: 1637


Script:

#!/bin/bash
# Search for actual usage of disabled property/method in components
rg -A 2 '\.disabled\b|disabled\s*[=?]' designer/client/src/

# Search for disabled method implementations
ast-grep --pattern 'disabled = () => {
  $$$
}'

ast-grep --pattern 'disabled() {
  $$$
}'

Length of output: 38852

designer/client/src/reducers/graph/types.ts (1)

17-17: Add JSDoc documentation for the new state property

The new stickyNotes property should be documented to explain its purpose and usage within the graph state.

export type GraphState = {
    scenarioLoading: boolean;
    scenario?: Scenario;
+   /** Collection of sticky notes associated with the current graph */
    stickyNotes?: StickyNote[];

Let's verify the property is properly handled in reducers:

designer/client/src/components/graph/fragmentGraph.tsx (1)

7-24: Add prop types documentation and verify ref usage

The component implementation looks good, but could benefit from better documentation.

+interface FragmentGraphPreviewProps extends Pick<GraphProps, "processCounts" | "scenario" | "stickyNotes" | "nodeIdPrefixForFragmentTests"> {}

+/**
+ * Renders a read-only preview of a fragment graph with sticky notes support
+ * @param props - Component properties
+ * @param ref - Forwarded ref to access the underlying Graph instance
+ */
export const FragmentGraphPreview = forwardRef<
    Graph,
-   Pick<GraphProps, "processCounts" | "scenario" | "stickyNotes" | "nodeIdPrefixForFragmentTests">
+   FragmentGraphPreviewProps

Let's verify the ref usage:

designer/client/src/actions/actionTypes.ts (1)

13-14: Update documentation as per PR objectives

As mentioned in the PR objectives, please ensure:

  1. Documentation is added/updated for the sticky notes feature
  2. An entry is added to _Changelog.md_ describing the feature
  3. Complex parts of the sticky notes implementation are documented
✅ Verification successful

Let me check the content of the documentation files to verify what's already documented and what might be missing.


Documentation has been properly updated

The sticky notes feature is well documented with:

  • Configuration details in MigrationGuide.md including settings for maxContentLength, maxNotesCount, and feature toggle
  • Comprehensive changelog entry in Changelog.md describing the feature purpose, API endpoints, and configuration options
🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Check if documentation has been updated
echo "Checking for documentation updates..."
rg -l "sticky.*note" docs/ || echo "No documentation found for sticky notes feature"

# Check changelog
echo "Checking changelog..."
rg -l "sticky.*note" CHANGELOG.md || echo "No changelog entry found for sticky notes feature"

Length of output: 405


Script:

#!/bin/bash
# Check content of documentation files that mention sticky notes
echo "=== Content from MigrationGuide.md ==="
rg -A 3 -B 3 "sticky.*note" docs/MigrationGuide.md

echo -e "\n=== Content from Changelog.md ==="
rg -A 3 -B 3 "sticky.*note" docs/Changelog.md

Length of output: 1490

designer/server/src/main/scala/pl/touk/nussknacker/ui/process/repository/stickynotes/StickyNotesRepository.scala (1)

41-50: Verify update operation's version constraint

The updateStickyNote method takes scenarioVersionId but doesn't include scenarioId. This might allow updating notes across different scenarios if they share the same version ID.

✅ Verification successful

Cross-scenario sticky note updates are prevented

Looking at the implementation in StickyNotesApiHttpService.scala, the service first fetches the note by ID and verifies the user's write permission for the specific scenarioId before allowing the update operation:

note <- EitherT.fromOptionF(
  dbioActionRunner.run(
    stickyNotesRepository.findStickyNoteById(requestBody.noteId)
  ),
  StickyNotesError.NoStickyNote(requestBody.noteId)
)
_ <- isAuthorized(note.scenarioId, Permission.Write)

This ensures that:

  1. The note exists and its scenarioId is retrieved
  2. The user has write permission for that specific scenario
  3. Only then the update operation is performed
🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Check if scenarioId is verified in the implementation
ast-grep --pattern 'def updateStickyNote($params) {
  $$$
  scenarioId
  $$$
}'

Length of output: 81


Script:

#!/bin/bash
# Let's search for the implementation of updateStickyNote using ripgrep for better context
rg "def updateStickyNote" -A 20

Length of output: 10413

designer/client/src/types/node.ts (1)

3-5: LGTM: Type extension for sticky notes

The addition of StickyNoteType to the Type union is clean and maintains backward compatibility.

designer/client/src/components/graph/StickyNoteElement.ts (1)

6-15: LGTM: Well-structured interfaces with clear type definitions

The interfaces StickyNoteDefaults and StickyNoteProtoProps are well-defined with appropriate optional properties and type safety.

designer/client/src/reducers/selectors/settings.ts (1)

17-17: LGTM: Consistent selector implementation

The getStickyNotesSettings selector follows the established pattern and correctly uses createSelector for memoization.

designer/client/src/actions/nk/assignSettings.ts (1)

42-42: Verify sticky notes settings initialization

Ensure that the application handles cases where stickyNotesSettings is undefined gracefully.

✅ Verification successful

Let me gather more information about the handling of stickyNotesSettings.


Let me search for more context about how stickyNotesSettings is used and handled.


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

StickyNotesSettings is properly handled with fallback mechanisms

The codebase shows proper handling of stickyNotesSettings through:

  • Type definition with required fields (maxContentLength, maxNotesCount, enabled)
  • Safe selector access using optional chaining in getStickyNotesSettings
  • Conditional rendering based on settings.enabled in sticky notes retrieval
  • Fallback to empty array when settings are disabled or undefined
🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Search for initialization and default values of stickyNotesSettings
rg -A 5 "stickyNotesSettings.*=|stickyNotesSettings.*\{" --type ts

Length of output: 813


Script:

#!/bin/bash
# Search for the type definition and initialization patterns of StickyNotesSettings
ast-grep --pattern 'interface StickyNotesSettings {
  $$$
}'

# Search for any default values or initialization
rg -A 3 "StickyNotesSettings.*=.*\{" --type ts

# Search for null checks or default handling
rg "stickyNotesSettings(\?|&&|\|\|)" --type ts

Length of output: 471


Script:

#!/bin/bash
# Search for the selector and state handling
rg -A 5 "getStickyNotesSettings" --type ts

# Search for any reducer or state updates
rg -A 10 "stickyNotesSettings.*reducer|stickyNotesSettings.*state" --type ts

# Search for the initialization in store or default state
ast-grep --pattern 'const initialState = {
  $$$
  stickyNotesSettings: $_,
  $$$
}'

Length of output: 3808

designer/client/src/components/toolbars/creator/ComponentIcon.tsx (1)

10-10: LGTM! Clean implementation of sticky note icon support

The implementation follows existing patterns and properly integrates with the icon preloading mechanism.

Also applies to: 26-29

designer/client/src/components/StickyNotePreview.tsx (1)

55-61: LGTM! Clean JSX structure

The component's render structure is clean and well-organized.

designer/client/src/components/graph/GraphPartialsInTS/applyCellChanges.ts (1)

10-18: LGTM: Import and signature changes are well-structured

The new imports and parameter addition for sticky notes are properly integrated into the existing function signature.

designer/client/src/components/graph/types.ts (1)

25-27: LGTM: Action props are properly typed

The sticky note action props are correctly typed using the imported action creators.

designer/server/src/test/scala/pl/touk/nussknacker/ui/api/StickyNotesApiHttpServiceBusinessSpec.scala (1)

45-82: LGTM: Test cases are well-structured and cover key scenarios

The test cases effectively cover empty state, non-existent scenario, and version-specific scenarios.

designer/client/src/components/ComponentPreview.tsx (1)

10-11: Add test coverage for sticky note preview rendering

The conditional rendering logic for sticky notes is well-implemented. However, ensure test coverage for this new branch to maintain reliability.

Let's check for existing test coverage:

Also applies to: 80-82

designer/server/src/main/scala/pl/touk/nussknacker/ui/config/FeatureTogglesConfig.scala (3)

10-10: LGTM: Clean addition of sticky notes configuration

The import and field addition follow the established pattern in the codebase for feature toggles.

Also applies to: 33-33


61-62: Verify default settings usage across environments

The configuration parsing with fallback to default settings is well-implemented. However, we should ensure the default settings are appropriate for all environments.

✅ Verification successful

Let me analyze the results and generate additional verification scripts to get more context about the sticky notes settings.


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

Default sticky notes settings are consistently defined across environments

The default settings are well-configured with consistent values in both the default configuration and test configuration:

  • maxContentLength: 5000
  • maxNotesCount: 5
  • enabled: true

These settings provide reasonable limits that should work well across different environments.

🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Search for references to default sticky notes settings
rg -l "stickyNotesSettings" | xargs rg "default"

# Search for environment-specific configurations
fd -e conf -e config | xargs rg "stickyNotesSettings"

Length of output: 17985


Script:

#!/bin/bash
# Check the actual default settings implementation
ast-grep --pattern 'object StickyNotesSettings {
  $$$
  val default = $$$
}'

# Check the actual settings class definition
ast-grep --pattern 'case class StickyNotesSettings($$$)'

# Get the actual configuration values from the config files
rg -A 5 "stickyNotesSettings: \{" designer/server/src/main/resources/defaultDesignerConfig.conf designer/server/src/test/resources/config/common-designer.conf

Length of output: 3556


83-83: Consider impact on existing instantiations

Adding a new required field to the case class might affect existing code that creates FeatureTogglesConfig instances.

designer/client/src/components/toolbars/creator/ToolBox.tsx (2)

5-5: LGTM: Clean import organization

The imports are well-organized and specific to the new functionality.

Also applies to: 13-14


118-119: LGTM: Proper Redux selector usage

The selectors follow Redux best practices and are correctly used with useSelector.

designer/client/src/reducers/selectors/graph.ts (1)

11-12: LGTM: Clean type imports

The imports are well-organized and properly typed.

designer/server/src/main/resources/defaultDesignerConfig.conf (1)

213-217: Review and document sticky notes configuration limits

The configuration introduces hard limits for sticky notes. Consider:

  1. Document the rationale for these specific limits in comments
  2. Remove the trailing comma after maxContentLength as it's not standard HOCON syntax
designer/server/src/main/scala/pl/touk/nussknacker/ui/api/SettingsResources.scala (1)

10-10: Consider adding validation for sticky notes settings

The settings are passed through without validation. Consider adding validation to ensure:

  1. maxContentLength is positive and reasonable
  2. maxNotesCount is positive
  3. These limits are enforced consistently across the application

Also applies to: 139-139

designer/client/src/components/graph/graphStyledWrapper.ts (1)

193-194: Verify color contrast for accessibility

The background color with alpha(0.3) opacity on white background might not provide sufficient contrast with black text, potentially causing accessibility issues.

designer/client/package.json (1)

25-25: Verify DOMPurify implementation for sticky notes content.

The addition of DOMPurify is appropriate for sanitizing HTML content in sticky notes. However, we should ensure it's properly implemented.

✅ Verification successful

Let me check the complete implementation context to ensure DOMPurify is properly initialized and used.


Let me try another approach to check the complete implementation.


Based on the implementation shown, I can now generate the final response:

DOMPurify is properly implemented for sticky note content sanitization

The implementation shows correct usage of DOMPurify:

  • DOMPurify is properly imported and used to sanitize HTML content
  • It's applied after markdown parsing to ensure all HTML is sanitized
  • Uses appropriate configuration with ADD_ATTR: ["target"] to allow safe link behavior
  • Has proper error handling in place
🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Description: Check if DOMPurify is properly used to sanitize HTML content
# Test: Search for DOMPurify usage in the codebase
rg -A 5 "DOMPurify.sanitize"

Length of output: 707


Script:

#!/bin/bash
# Check the complete implementation of the sticky note component
ast-grep --pattern 'import $_ from "dompurify"'

# Get the full context of the file containing DOMPurify usage
cat designer/client/src/components/graph/EspNode/stickyNote.ts

Length of output: 66513


Script:

#!/bin/bash
# Get the full context around DOMPurify usage
rg -B 5 -A 10 "DOMPurify" designer/client/src/components/graph/EspNode/stickyNote.ts

Length of output: 1427

designer/client/src/reducers/graph/reducer.ts (1)

19-19: LGTM! Utility imports are well-organized.

The sticky note utility imports are properly grouped with other layout-related utilities.

Also applies to: 23-24

docs-internal/api/nu-designer-openapi.yaml (1)

2530-2929: Well-designed REST API endpoints for sticky notes management!

The implementation follows REST best practices with:

  • Proper HTTP methods (GET/PUT/POST/DELETE) for CRUD operations
  • Comprehensive error handling
  • Clear authentication/authorization controls
  • Input validation (e.g., content length limits)

Comment on lines +48 to +50
def addStickyNote(scenarioName: ProcessName, request: StickyNoteAddRequest): StickyNoteCorrelationId = {
rawScenarioHelper.addStickyNote(scenarioName, request)
}
Copy link

Choose a reason for hiding this comment

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

💡 Codebase verification

Test coverage for sticky notes functionality needs improvement

The current test coverage for sticky notes functionality appears incomplete:

  • Only GET endpoint tests are implemented
  • Missing test cases for adding sticky notes (POST)
  • Missing test cases for modifying sticky notes
  • The TODO comment in the test file confirms more tests are needed

Specific areas that need test coverage:

  • Adding sticky notes with different parameters
  • Error cases for adding sticky notes
  • Modifying existing sticky notes
  • Deleting sticky notes
  • Permission-related test cases
🔗 Analysis chain

Verify test coverage for sticky notes functionality

The addStickyNote method looks good, but let's ensure it's properly covered by tests.

Let's check for existing test coverage:

🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Description: Check for test files that might be testing sticky notes functionality

# Look for test files related to sticky notes
rg -l "StickyNote" --type scala --glob "*Spec.scala" --glob "*Test.scala"

# Look for specific sticky note test cases
rg "test.*[Ss]ticky.*[Nn]ote" --type scala --glob "*Spec.scala" --glob "*Test.scala"

Length of output: 262


Script:

#!/bin/bash
# Let's examine the content of the identified test file and look for sticky note test cases
rg -A 10 "class.*StickyNotesApiHttpServiceBusinessSpec" designer/server/src/test/scala/pl/touk/nussknacker/ui/api/StickyNotesApiHttpServiceBusinessSpec.scala

# Also search for any test methods related to sticky notes in this file
rg "\".*sticky.*\"" designer/server/src/test/scala/pl/touk/nussknacker/ui/api/StickyNotesApiHttpServiceBusinessSpec.scala

# Let's also check for any usage of addStickyNote method
rg "addStickyNote" --type scala

Length of output: 2922


Script:

#!/bin/bash
# Let's examine the actual test cases in StickyNotesApiHttpServiceBusinessSpec
rg -A 20 "\"The GET stickyNotes for scenario\"" designer/server/src/test/scala/pl/touk/nussknacker/ui/api/StickyNotesApiHttpServiceBusinessSpec.scala

# Also check for any other test cases in this file
rg "it should|test" designer/server/src/test/scala/pl/touk/nussknacker/ui/api/StickyNotesApiHttpServiceBusinessSpec.scala

# Let's also look at the implementation of stickyNoteToAdd helper method
ast-grep --pattern 'def stickyNoteToAdd($$$)'

Length of output: 3574


Script:

#!/bin/bash
# Let's check the entire content of the test file to see all test cases
cat designer/server/src/test/scala/pl/touk/nussknacker/ui/api/StickyNotesApiHttpServiceBusinessSpec.scala

# Also check for any POST/PUT test cases for sticky notes
rg -A 10 "POST.*sticky|PUT.*sticky" designer/server/src/test/scala/pl/touk/nussknacker/ui/api/StickyNotesApiHttpServiceBusinessSpec.scala

Length of output: 3320

Comment on lines +175 to +192
private def addStickyNote(scenarioId: ProcessId, requestBody: StickyNoteAddRequest)(
implicit loggedUser: LoggedUser
): EitherT[Future, StickyNotesError, StickyNoteCorrelationId] =
EitherT
.right(
dbioActionRunner.run(
stickyNotesRepository.addStickyNote(
requestBody.content,
requestBody.layoutData,
requestBody.color,
requestBody.dimensions,
requestBody.targetEdge,
scenarioId,
requestBody.scenarioVersionId
)
)
)

Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue

Ensure server-side sanitization of sticky note content to prevent XSS attacks

While client-side sanitization is important, it is crucial to sanitize user input on the server side before storing or processing it to prevent potential cross-site scripting (XSS) attacks. Consider sanitizing requestBody.content before storing it in the database.

Comment on lines +205 to +228
private def updateStickyNote(requestBody: StickyNoteUpdateRequest)(
implicit loggedUser: LoggedUser
): EitherT[Future, StickyNotesError, Int] = for {
note <- EitherT.fromOptionF(
dbioActionRunner.run(
stickyNotesRepository.findStickyNoteById(requestBody.noteId)
),
StickyNotesError.NoStickyNote(requestBody.noteId)
)
_ <- isAuthorized(note.scenarioId, Permission.Write)
result <- EitherT.right(
dbioActionRunner.run(
stickyNotesRepository.updateStickyNote(
requestBody.noteId,
requestBody.content,
requestBody.layoutData,
requestBody.color,
requestBody.dimensions,
requestBody.targetEdge,
requestBody.scenarioVersionId
)
)
)
} yield result
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue

Ensure server-side sanitization of updated sticky note content

Similar to adding a sticky note, when updating, ensure that requestBody.content is sanitized on the server side to prevent XSS attacks.

Comment on lines +491 to +499
this.graph.on(Events.CELL_CONTENT_UPDATED, (cell: dia.Element, content: string) => {
if (isStickyNoteElement(cell)) {
const updatedStickyNote = getStickyNoteCopyFromCell(this.props.stickyNotes, cell);
if (!updatedStickyNote) return;
if (updatedStickyNote.content == content) return;
updatedStickyNote.content = content;
this.updateStickyNote(this.props.scenario.name, this.props.scenario.processVersionId, updatedStickyNote);
}
});
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue

Sanitize sticky note content before rendering to prevent XSS attacks

Ensure that the content of sticky notes is properly sanitized before rendering it to the DOM to prevent cross-site scripting (XSS) vulnerabilities. Consider using dompurify to sanitize content before it's displayed.

Comment on lines +5 to +13
object StickyNoteEvent extends Enumeration {
implicit val typeEncoder: Encoder[StickyNoteEvent.Value] = Encoder.encodeEnumeration(StickyNoteEvent)
implicit val typeDecoder: Decoder[StickyNoteEvent.Value] = Decoder.decodeEnumeration(StickyNoteEvent)

type StickyNoteEvent = Value
val StickyNoteCreated: Value = Value("CREATED")
val StickyNoteUpdated: Value = Value("UPDATED")
val StickyNoteDeleted: Value = Value("DELETED")

Copy link

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

Consider using sealed trait pattern instead of Enumeration

While the current implementation works, Scala's sealed trait pattern is generally preferred over Enumeration for better type safety and pattern matching.

Consider refactoring to:

sealed trait StickyNoteEvent
object StickyNoteEvent {
  case object Created extends StickyNoteEvent
  case object Updated extends StickyNoteEvent
  case object Deleted extends StickyNoteEvent

  implicit val encoder: Encoder[StickyNoteEvent] = Encoder.instance {
    case Created => Json.fromString("CREATED")
    case Updated => Json.fromString("UPDATED")
    case Deleted => Json.fromString("DELETED")
  }

  implicit val decoder: Decoder[StickyNoteEvent] = Decoder.decodeString.emap {
    case "CREATED" => Right(Created)
    case "UPDATED" => Right(Updated)
    case "DELETED" => Right(Deleted)
    case other => Left(s"Unknown event: $other")
  }
}

This approach provides:

  • Better compile-time exhaustiveness checking
  • More idiomatic Scala code
  • Improved error handling

borderColor: "Transparent",
whiteSpace: "pre-line",
overflow: "hidden",
clipPath: "xywh(0 0px 100% 100% round 0 0 16% 0)",
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue

Fix non-standard clipPath syntax

The clipPath property uses a non-standard xywh() syntax which may not be supported across all browsers.

Replace with standard syntax:

-    clipPath: "xywh(0 0px 100% 100% round 0 0 16% 0)",
+    clipPath: "inset(0 0 0 0 round 0 0 16% 0)",
📝 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
clipPath: "xywh(0 0px 100% 100% round 0 0 16% 0)",
clipPath: "inset(0 0 0 0 round 0 0 16% 0)",

Comment on lines +82 to +93
export function removeStickyNoteFromLayout(state: GraphState, stickyNoteId: number): { layout: NodePosition[]; stickyNotes: StickyNote[] } {
const { layout } = state;
const stickyNoteLayoutId = createStickyNoteId(stickyNoteId);
const updatedStickyNotes = state.stickyNotes.filter((n) => n.noteId !== stickyNoteId);
const updatedLayout = updatedStickyNotes.map((stickyNote) => {
return { id: createStickyNoteId(stickyNote.noteId), position: stickyNote.layoutData };
});
return {
stickyNotes: [...updatedStickyNotes],
layout: [...layout.filter((l) => l.id !== stickyNoteLayoutId), ...updatedLayout],
};
}
Copy link

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

Add error handling for invalid sticky note IDs

The function assumes the sticky note exists. Consider adding validation to handle cases where the note ID doesn't exist.

 export function removeStickyNoteFromLayout(state: GraphState, stickyNoteId: number): { layout: NodePosition[]; stickyNotes: StickyNote[] } {
     const { layout } = state;
     const stickyNoteLayoutId = createStickyNoteId(stickyNoteId);
+    const existingNote = state.stickyNotes.find((n) => n.noteId === stickyNoteId);
+    if (!existingNote) {
+        throw new Error(`Sticky note with ID ${stickyNoteId} not found`);
+    }
     const updatedStickyNotes = state.stickyNotes.filter((n) => n.noteId !== stickyNoteId);
📝 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 removeStickyNoteFromLayout(state: GraphState, stickyNoteId: number): { layout: NodePosition[]; stickyNotes: StickyNote[] } {
const { layout } = state;
const stickyNoteLayoutId = createStickyNoteId(stickyNoteId);
const updatedStickyNotes = state.stickyNotes.filter((n) => n.noteId !== stickyNoteId);
const updatedLayout = updatedStickyNotes.map((stickyNote) => {
return { id: createStickyNoteId(stickyNote.noteId), position: stickyNote.layoutData };
});
return {
stickyNotes: [...updatedStickyNotes],
layout: [...layout.filter((l) => l.id !== stickyNoteLayoutId), ...updatedLayout],
};
}
export function removeStickyNoteFromLayout(state: GraphState, stickyNoteId: number): { layout: NodePosition[]; stickyNotes: StickyNote[] } {
const { layout } = state;
const stickyNoteLayoutId = createStickyNoteId(stickyNoteId);
const existingNote = state.stickyNotes.find((n) => n.noteId === stickyNoteId);
if (!existingNote) {
throw new Error(`Sticky note with ID ${stickyNoteId} not found`);
}
const updatedStickyNotes = state.stickyNotes.filter((n) => n.noteId !== stickyNoteId);
const updatedLayout = updatedStickyNotes.map((stickyNote) => {
return { id: createStickyNoteId(stickyNote.noteId), position: stickyNote.layoutData };
});
return {
stickyNotes: [...updatedStickyNotes],
layout: [...layout.filter((l) => l.id !== stickyNoteLayoutId), ...updatedLayout],
};
}

Comment on lines +240 to +258
private def addStickyNoteForScenario(
scenarioName: ProcessName,
request: StickyNoteAddRequest
): Future[StickyNoteCorrelationId] = {
for {
scenarioId <- futureFetchingScenarioRepository.fetchProcessId(scenarioName).map(_.get)
noteCorrelationId <- dbioRunner.runInTransaction(
stickyNotesRepository.addStickyNote(
request.content,
request.layoutData,
request.color,
request.dimensions,
request.targetEdge,
scenarioId,
request.scenarioVersionId
)
)
} yield noteCorrelationId
}
Copy link

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 in addStickyNoteForScenario

The method should handle potential database errors more gracefully and provide meaningful error messages.

   private def addStickyNoteForScenario(
       scenarioName: ProcessName,
       request: StickyNoteAddRequest
   ): Future[StickyNoteCorrelationId] = {
     for {
-      scenarioId <- futureFetchingScenarioRepository.fetchProcessId(scenarioName).map(_.get)
+      scenarioId <- futureFetchingScenarioRepository.fetchProcessId(scenarioName).flatMap {
+        case Some(id) => Future.successful(id)
+        case None => Future.failed(new IllegalArgumentException(s"Scenario not found: $scenarioName"))
+      }
       noteCorrelationId <- dbioRunner.runInTransaction(
         stickyNotesRepository.addStickyNote(

Committable suggestion skipped: line range outside the PR's diff.

Comment on lines +721 to +738
updateStickyNote(scenarioName: string, scenarioVersionId: number, stickyNote: StickyNote) {
const promise = api.put(`/processes/${encodeURIComponent(scenarioName)}/stickyNotes`, {
noteId: stickyNote.noteId,
scenarioVersionId,
content: stickyNote.content,
layoutData: stickyNote.layoutData,
color: stickyNote.color,
dimensions: stickyNote.dimensions,
});
promise.catch((error) =>
this.#addError(
i18next.t("notification.error.failedToUpdateStickyNote", "Failed to update sticky note for scenario"),
error,
true,
),
);
return promise;
}
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue

Sanitize content before sending to the server.

The updateStickyNote method should sanitize the content using DOMPurify before sending it to the server.

Add content sanitization:

+import DOMPurify from 'dompurify';

 updateStickyNote(scenarioName: string, scenarioVersionId: number, stickyNote: StickyNote) {
     const promise = api.put(`/processes/${encodeURIComponent(scenarioName)}/stickyNotes`, {
         noteId: stickyNote.noteId,
         scenarioVersionId,
-        content: stickyNote.content,
+        content: DOMPurify.sanitize(stickyNote.content),
         layoutData: stickyNote.layoutData,
         color: stickyNote.color,
         dimensions: stickyNote.dimensions,
     });
     promise.catch((error) =>
         this.#addError(
             i18next.t("notification.error.failedToUpdateStickyNote", "Failed to update sticky note for scenario"),
             error,
             true,
         ),
     );
     return promise;
 }

Committable suggestion skipped: line range outside the PR's diff.

Comment on lines +695 to +707
addStickyNote(scenarioName: string, scenarioVersionId: number, position: Position, dimensions: Dimensions) {
const promise = api.post(`/processes/${encodeURIComponent(scenarioName)}/stickyNotes`, {
scenarioVersionId,
content: "",
layoutData: position,
color: STICKY_NOTE_DEFAULT_COLOR, //TODO add config for default sticky note color? For now this is default.
dimensions: dimensions,
});
promise.catch((error) =>
this.#addError(i18next.t("notification.error.failedToAddStickyNote", "Failed to add sticky note to scenario"), error, true),
);
return promise;
}
Copy link

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

Consider validating dimensions and position.

The addStickyNote method should validate the position and dimensions before sending the request.

Add validation:

 addStickyNote(scenarioName: string, scenarioVersionId: number, position: Position, dimensions: Dimensions) {
+    if (position.x < 0 || position.y < 0) {
+        return Promise.reject(new Error("Position coordinates must be non-negative"));
+    }
+    if (dimensions.width <= 0 || dimensions.height <= 0) {
+        return Promise.reject(new Error("Dimensions must be positive"));
+    }
     const promise = api.post(`/processes/${encodeURIComponent(scenarioName)}/stickyNotes`, {
         scenarioVersionId,
         content: "",
         layoutData: position,
         color: STICKY_NOTE_DEFAULT_COLOR,
         dimensions: dimensions,
     });
     promise.catch((error) =>
         this.#addError(i18next.t("notification.error.failedToAddStickyNote", "Failed to add sticky note to scenario"), error, true),
     );
     return promise;
 }
📝 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
addStickyNote(scenarioName: string, scenarioVersionId: number, position: Position, dimensions: Dimensions) {
const promise = api.post(`/processes/${encodeURIComponent(scenarioName)}/stickyNotes`, {
scenarioVersionId,
content: "",
layoutData: position,
color: STICKY_NOTE_DEFAULT_COLOR, //TODO add config for default sticky note color? For now this is default.
dimensions: dimensions,
});
promise.catch((error) =>
this.#addError(i18next.t("notification.error.failedToAddStickyNote", "Failed to add sticky note to scenario"), error, true),
);
return promise;
}
addStickyNote(scenarioName: string, scenarioVersionId: number, position: Position, dimensions: Dimensions) {
if (position.x < 0 || position.y < 0) {
return Promise.reject(new Error("Position coordinates must be non-negative"));
}
if (dimensions.width <= 0 || dimensions.height <= 0) {
return Promise.reject(new Error("Dimensions must be positive"));
}
const promise = api.post(`/processes/${encodeURIComponent(scenarioName)}/stickyNotes`, {
scenarioVersionId,
content: "",
layoutData: position,
color: STICKY_NOTE_DEFAULT_COLOR, //TODO add config for default sticky note color? For now this is default.
dimensions: dimensions,
});
promise.catch((error) =>
this.#addError(i18next.t("notification.error.failedToAddStickyNote", "Failed to add sticky note to scenario"), error, true),
);
return promise;
}

@philemone philemone closed this Dec 3, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
client client main fe docs ui
Projects
None yet
Development

Successfully merging this pull request may close these issues.

1 participant