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

Sticky notes #7181

Closed
wants to merge 27 commits into from
Closed

Sticky notes #7181

wants to merge 27 commits into from

Conversation

philemone
Copy link
Contributor

@philemone philemone commented Nov 20, 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

Release Notes

  • New Features

    • Introduced sticky notes functionality, allowing users to create, update, and delete sticky notes within scenarios.
    • Added a dedicated API for managing sticky notes, including endpoints for retrieving, adding, updating, and deleting sticky notes.
    • Enhanced user interface with a new StickyNote component and improved toolbox for easier access to sticky notes.
    • Implemented new settings for sticky notes, including maximum content length and note count limits.
    • Integrated sticky notes management into the existing graph component, enhancing usability and interactivity.
    • Added support for sticky notes in the configuration, allowing users to enable or disable the feature.
    • Introduced a new section in the configuration for sticky notes settings.
  • Bug Fixes

    • Resolved issues related to the visibility and interaction of sticky notes in the graphical interface.
  • Documentation

    • Updated API documentation to include new sticky notes endpoints and their functionalities.
    • Revised migration guide to reflect changes in sticky notes configuration and management.

Copy link

coderabbitai bot commented Nov 20, 2024

📝 Walkthrough
📝 Walkthrough

Walkthrough

The pull request introduces extensive modifications to the Nussknacker application, primarily focusing on the implementation and management of sticky notes. Key changes include the addition of a new dependency, dompurify, to enhance security against XSS vulnerabilities. The ActionTypes type definition is updated to include new action types for sticky notes, facilitating their management within the application state.

Several new functions are introduced in the process.ts file to handle CRUD operations for sticky notes, including fetching, updating, adding, and deleting notes. A new StickyNote type and related interfaces are defined to standardize sticky note data structures. The UI components are modified to support rendering and interaction with sticky notes, including conditional rendering in the ComponentPreview and a new StickyNotePreview component.

Database schema migrations are implemented to create a sticky_notes table, and corresponding API endpoints are added to manage sticky notes via HTTP requests. Configuration files are updated to include settings for sticky notes, enhancing the application’s configurability. Overall, these changes significantly expand the application's capabilities related to sticky notes, integrating them into both the frontend and backend systems.

Possibly related PRs

Suggested labels

submodules

Suggested reviewers

  • JulianWielga
  • arkadius

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.

@github-actions github-actions bot added client client main fe docs ui labels Nov 20, 2024
@philemone
Copy link
Contributor Author

@coderabbitai review

Copy link

coderabbitai bot commented Nov 20, 2024

✅ Actions performed

Review triggered.

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

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 16

🧹 Outside diff range and nitpick comments (72)
designer/client/src/types/stickyNote.ts (1)

1-1: Consider using a const assertion for better type safety

The constant looks good, but you could make it more type-safe by using a const assertion:

-export const StickyNoteType = "StickyNote";
+export const StickyNoteType = "StickyNote" as const;
designer/client/src/common/StickyNote.ts (1)

3-3: Add JSDoc documentation for the Dimensions type.

Consider adding documentation to explain the purpose and usage of this type, including any constraints on width and height values.

+/** Represents the dimensions of a sticky note in pixels */
 export type Dimensions = { width: number; height: number };
designer/server/src/main/scala/pl/touk/nussknacker/ui/api/description/stickynotes/StickyNoteEvent.scala (1)

5-13: Add ScalaDoc documentation for better maintainability.

The implementation looks good, but consider adding documentation to explain:

  • The purpose of this enumeration
  • When each event type is triggered
  • How these events integrate with the sticky notes feature
+/**
+ * Represents events that can occur during sticky note lifecycle.
+ * Used for tracking state changes and triggering appropriate actions.
+ */
 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
+  /** Emitted when a new sticky note is created */
   val StickyNoteCreated: Value = Value("CREATED")
+  /** Emitted when an existing sticky note is modified */
   val StickyNoteUpdated: Value = Value("UPDATED")
+  /** Emitted when a sticky note is removed */
   val StickyNoteDeleted: Value = Value("DELETED")
designer/client/src/reducers/graph/types.ts (1)

17-17: Consider adding JSDoc documentation

The addition of stickyNotes to GraphState looks good. Consider adding JSDoc documentation to describe the purpose and usage of this state property, especially since it's an optional field.

+    /** Collection of sticky notes associated with the current graph/scenario */
     stickyNotes?: StickyNote[];
designer/client/src/components/graph/fragmentGraph.tsx (2)

7-24: LGTM! Consider adding prop documentation.

The implementation correctly integrates sticky notes functionality while maintaining type safety. Consider adding JSDoc comments to document the props, especially the new stickyNotes prop, to improve maintainability.

/**
 * Renders a preview of a fragment graph with sticky notes support.
 * @param props.processCounts - Process execution counts
 * @param props.scenario - Scenario configuration
 * @param props.stickyNotes - Collection of sticky notes for the graph
 * @param props.nodeIdPrefixForFragmentTests - Prefix for node IDs in fragment tests
 */
export const FragmentGraphPreview = forwardRef<...

9-9: Consider making stickyNotes explicitly optional.

Since this is integrating a new feature, consider marking stickyNotes as optional in the type definition to ensure backward compatibility and gradual adoption.

-    Pick<GraphProps, "processCounts" | "scenario" | "stickyNotes" | "nodeIdPrefixForFragmentTests">
+    Pick<GraphProps, "processCounts" | "scenario" | "nodeIdPrefixForFragmentTests"> & { stickyNotes?: GraphProps["stickyNotes"] }
designer/client/src/containers/theme/helpers.ts (2)

19-26: Add type safety and documentation improvements

The function implementation is solid, but could benefit from some TypeScript best practices:

+ /**
+  * Returns an augmented color for sticky note background based on theme
+  * @param theme - Material-UI theme object
+  * @param color - CSS color string
+  * @returns Augmented color object from theme palette
+  */
- export function getStickyNoteBackgroundColor(theme: Theme, color: string) {
+ export function getStickyNoteBackgroundColor(theme: Theme, color: string): ReturnType<Theme['palette']['augmentColor']> {

20-20: Consider caching color validation results

The CSS.supports call could be expensive if called frequently. Consider caching results for commonly used colors if this function is called often in your application.

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

15-15: Remove or convert TODO into documentation

The TODO comment should be resolved before merging. If these changes are intentional and correct, consider converting this into proper documentation explaining the rationale behind the parameters and logic.


16-19: Improve error message handling logic

The current ternary operation could be made more explicit and safer. Consider restructuring the logic to handle edge cases more clearly.

-        children: <Notification type={"error"} icon={<InfoOutlinedIcon />} message={showErrorText && error ? error : message} />,
+        children: <Notification 
+            type={"error"} 
+            icon={<InfoOutlinedIcon />} 
+            message={showErrorText && error !== undefined ? error : message} 
+        />,

Also, consider adding JSDoc documentation to clarify the purpose of each parameter:

/**
 * Displays an error notification
 * @param message - The default message to display
 * @param error - Optional technical error details
 * @param showErrorText - When true and error is provided, shows technical error instead of message
 */
export function error(message: string, error?: string, showErrorText?: boolean): Action
designer/client/src/actions/actionTypes.ts (1)

15-16: Consider additional improvements for completeness.

A few suggestions to enhance the implementation:

  1. Consider adding a STICKY_NOTE_CREATED action type if sticky notes can be created separately from updates
  2. Consider grouping related action types together (e.g., moving all sticky note actions adjacent to each other) for better maintainability
designer/client/src/components/graph/GraphPartialsInTS/cellUtils.ts (2)

12-14: Consider extracting the magic string to a constant.

The function is well-implemented with proper type guarding. However, the string "stickyNote.StickyNoteElement" could be extracted to a named constant for better maintainability.

+const STICKY_NOTE_ELEMENT_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_ELEMENT_TYPE;
 }

16-21: Consider using strict equality for noteId comparison.

The function is well-implemented with proper type safety checks and immutability through deep cloning. However, consider using strict equality (===) instead of loose equality (==) for the noteId comparison to prevent potential type coercion issues.

-    const stickyNote = stickyNotes.find((note) => note.noteId == noteId);
+    const stickyNote = stickyNotes.find((note) => note.noteId === noteId);
designer/client/src/components/stickyNotes/StickyNotesPanel.tsx (5)

11-14: Consider adding prop type validation

While TypeScript provides type safety through ToolbarPanelProps, consider adding runtime prop validation using prop-types for additional safety, especially for required props.


14-14: Consider extracting the note model configuration

The hardcoded note model could be moved to a constants file or configuration object for better maintainability and reusability.

+// In a separate constants file
+export const DEFAULT_STICKY_NOTE = {
+  id: "StickyNoteToAdd",
+  type: StickyNoteType,
+  isDisabled: false,
+};

-const noteModel = { id: "StickyNoteToAdd", type: StickyNoteType, isDisabled: false };
+const noteModel = DEFAULT_STICKY_NOTE;

21-26: Consider enhancing the user experience with a tooltip

The disabled state when pristine is false should provide feedback to users about why the tool is unavailable.

 <Tool
   nodeModel={noteModel}
   label={t("stickyNotes.tool.label", "sticky note")}
   key={StickyNoteType + "_" + pristine}
   disabled={!pristine}
+  tooltip={!pristine ? t("stickyNotes.tool.disabled.tooltip", "Save your changes first to add sticky notes") : undefined}
 />

24-24: Improve key construction for better maintainability

The key construction using string concatenation could be made more maintainable.

-key={StickyNoteType + "_" + pristine}
+key={`${StickyNoteType}_${pristine}`}

31-32: Consider direct return instead of fragment

Since there's only one child element being returned, the fragment wrapper is unnecessary.

-return <>{stickyNoteTool}</>;
+return stickyNoteTool;
designer/client/src/types/node.ts (1)

5-5: Consider adding JSDoc documentation for the Type union.

The type modification correctly extends the union type to support sticky notes. However, since this is a core type that other components will rely on, consider adding JSDoc documentation to explain the purpose and valid values of this type.

+/**
+ * Represents the type of a node in the designer.
+ * - "FragmentInput": Represents a fragment input node
+ * - StickyNoteType: Represents a sticky note node
+ * - string: Allows for other custom node types
+ */
type Type = "FragmentInput" | typeof StickyNoteType | string;
designer/client/src/components/graph/StickyNoteElement.ts (1)

6-15: Add JSDoc documentation for interfaces.

While the interfaces are well-structured, adding JSDoc documentation would improve code maintainability and help other developers understand the purpose and usage of these interfaces.

+/**
+ * Default properties for configuring a sticky note element.
+ */
 interface StickyNoteDefaults {
+    /** Position coordinates for the sticky note */
     position?: { x: number; y: number };
+    /** Dimensions of the sticky note */
     size?: { width: number; height: number };
+    /** Additional attributes for styling and behavior */
     attrs?: Record<string, unknown>;
 }

+/**
+ * Properties for the sticky note prototype including markup and additional configurations.
+ */
 interface StickyNoteProtoProps {
+    /** Array of markup nodes defining the sticky note's structure */
     markup: (dia.MarkupNodeJSON | MarkupNodeJSON)[];
+    /** Additional prototype properties */
     [key: string]: unknown;
 }
designer/client/src/components/toolbars/creator/ComponentIcon.tsx (2)

26-27: Consider adding path validation and documentation

While the implementation follows the existing pattern, consider:

  1. Adding runtime validation to ensure the SVG assets exist for all StickyNoteType values
  2. Adding JSDoc comments to document the purpose and usage of these exported constants
+/** Path to the SVG asset for sticky note icons */
 export const stickyNoteIconSrc = `/assets/components/${StickyNoteType}.svg`;
+/** Preloaded sticky note icon reference, null if loading fails */
 export const stickyNoteIcon: string | null = preloadBeImage(stickyNoteIconSrc);

26-27: Consider centralizing asset path management

Since the application handles various icon types (components, sticky notes), consider creating a centralized asset path management system. This could:

  1. Provide type-safe path construction
  2. Centralize path validation
  3. Make it easier to change asset organization in the future

Example approach:

// assets/paths.ts
export const AssetPaths = {
  stickyNote: (type: StickyNoteType) => `/assets/components/${type}.svg`,
  component: (type: string) => `/assets/components/${type}.svg`,
} as const;
designer/client/src/components/graph/node-modal/node/FragmentContent.tsx (2)

17-17: Consider adding type annotation for better type safety.

While the code works, adding a type annotation for stickyNotes would improve type safety and code documentation.

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

Line range hint 1-49: Well-structured integration of sticky notes feature.

The implementation follows good practices by:

  • Maintaining separation of concerns
  • Following established Redux patterns
  • Integrating cleanly with existing component structure
designer/server/src/test/scala/pl/touk/nussknacker/test/base/it/WithSimplifiedConfigScenarioHelper.scala (1)

44-50: Consider adding ScalaDoc for the new methods.

While the methods are straightforward, adding ScalaDoc comments would help other developers understand:

  • The expected behavior of these methods in test scenarios
  • Any specific requirements for the input parameters
  • The significance of the return types

Example documentation:

/** Updates the scenario with a new version and returns the update result.
  * @param scenarioName name of the scenario to update
  * @param newVersion new version of the scenario
  * @return update result containing version and modification details
  */
def updateScenario(scenarioName: ProcessName, newVersion: CanonicalProcess): ProcessUpdated

/** Adds a sticky note to the specified scenario.
  * @param scenarioName name of the scenario to add the sticky note to
  * @param request details of the sticky note to add
  * @return correlation ID for tracking the sticky note
  */
def addStickyNote(scenarioName: ProcessName, request: StickyNoteAddRequest): StickyNoteCorrelationId
designer/client/src/components/StickyNotePreview.tsx (2)

9-11: Consider extracting props interface.

For better reusability and maintainability, consider extracting the props interface.

+interface StickyNotePreviewProps {
+    isActive?: boolean;
+    isOver?: boolean;
+}
+
-export function StickyNotePreview({ isActive, isOver }: { isActive?: boolean; isOver?: boolean }): JSX.Element {
+export function StickyNotePreview({ isActive, isOver }: StickyNotePreviewProps): JSX.Element {

19-53: Extract magic numbers in styles to constants.

The styling is well-structured, but there are some magic numbers that should be constants for better maintainability.

+const SHADOW_OPACITY = 0.5;
+const BORDER_WIDTH = 0.5;
+const TRANSFORM_ORIGIN_X = "80%";
+const TRANSFORM_ORIGIN_Y = "50%";
+const STYLE_OPACITY = 0.5;
+
 const nodeStyles = css({
     // ...
-    filter: `drop-shadow(0 4px 8px ${alpha(theme.palette.common.black, 0.5)})`,
-    borderWidth: 0.5,
+    filter: `drop-shadow(0 4px 8px ${alpha(theme.palette.common.black, SHADOW_OPACITY)})`,
+    borderWidth: BORDER_WIDTH,
     borderStyle: "solid",
-    transformOrigin: "80% 50%",
+    transformOrigin: `${TRANSFORM_ORIGIN_X} ${TRANSFORM_ORIGIN_Y}`,
     // ...
 });

 const colors = css({
-    opacity: 0.5,
+    opacity: STYLE_OPACITY,
     // ...
 });
designer/client/src/components/graph/GraphPartialsInTS/applyCellChanges.ts (2)

23-24: Consider using a more descriptive variable name

The variable name 'a' in the map function could be more descriptive. Consider using 'modelWithTool' or 'stickyNoteWithTool' instead.

-    const stickyNotesModels = stickyNotesModelsWithTools.map((a) => a.model);
+    const stickyNotesModels = stickyNotesModelsWithTools.map((modelWithTool) => modelWithTool.model);

46-57: Consider enhancing error handling

While the current error handling is functional, consider these improvements:

  1. Replace console logging with proper error tracking/monitoring
  2. Add more specific error messages including sticky note details
  3. Consider implementing an error recovery mechanism
     newStickyNotesModelsWithTools.forEach((m) => {
         try {
             const view = m.model.findView(paper);
             if (!view) {
-                console.warn(`View not found for stickyNote model: ${m.model.id}`);
+                const details = `id: ${m.model.id}, position: ${JSON.stringify(m.model.position)}`;
+                logger.warn(`Failed to find view for sticky note: ${details}`);
                 return;
             }
             view.addTools(m.tools);
         } catch (error) {
-            console.error(`Failed to add tools to stickyNote view:`, error);
+            logger.error(`Failed to add tools to sticky note view`, {
+                stickyNoteId: m.model.id,
+                error: error instanceof Error ? error.message : String(error)
+            });
+            // Consider implementing recovery mechanism here
         }
     });
designer/client/src/components/graph/types.ts (1)

49-49: LGTM: Consider documenting readonly behavior

The addition of stickyNotes to FragmentGraphProps is consistent with the type structure. Since FragmentGraphProps is readonly, consider documenting whether sticky notes should be immutable in fragment contexts.

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

42-43: Document the helper method and consider parameterizing coordinates.

Consider adding documentation to explain the purpose of the helper method and the significance of the default values used.

+  /**
+   * Creates a sticky note request with default values for testing.
+   * @param versionId The version ID of the scenario
+   * @return A sticky note request with predefined layout and styling
+   */
   private def stickyNoteToAdd(versionId: VersionId): StickyNoteAddRequest =
     StickyNoteAddRequest(versionId, "", LayoutData(0, 1), "#aabbcc", Dimensions(300, 200), None)

69-82: Consider adding assertion messages for better test clarity.

While the test logic is correct, adding descriptive messages to assertions would improve test failure debugging.

   .when()
   .basicAuthAllPermUser()
   .get(s"$nuDesignerHttpAddress/api/processes/$exampleScenarioName/stickyNotes?scenarioVersionId=1")
   .Then()
-  .statusCode(200)
+  .statusCode(200, "Should return success status for valid version request")
-  .equalsJsonBody("[]")
+  .equalsJsonBody("[]", "Should return empty array for notes from different version")
designer/client/src/components/ComponentPreview.tsx (2)

80-82: Add explicit type checking for better robustness.

While optional chaining prevents crashes, consider adding explicit type checking for better error handling:

-    return node?.type === StickyNoteType ? (
+    if (!node) {
+        return null; // or an appropriate fallback UI
+    }
+    return node.type === StickyNoteType ? (

80-82: Consider extracting default rendering logic into a separate component.

The component handles two distinct rendering paths. Consider extracting the default rendering logic into a separate component (e.g., DefaultComponentPreview) to improve maintainability and readability:

function DefaultComponentPreview({ node, isActive, isOver, colors, nodeStyles, ...props }) {
  return (
    <div className={cx(colors, nodeStyles)}>
      {/* existing default rendering logic */}
    </div>
  );
}

Then simplify the main component:

return node?.type === StickyNoteType 
  ? <StickyNotePreview isActive={isActive} isOver={isOver} />
  : <DefaultComponentPreview node={node} isActive={isActive} isOver={isOver} colors={colors} nodeStyles={nodeStyles} />;
designer/client/src/components/graph/ProcessGraph.tsx (1)

49-54: Consider extracting drop handlers for better maintainability.

While the logic is correct, consider extracting the handlers into separate functions to improve readability and maintainability.

-            if (item?.type === StickyNoteType) {
-                graph.current.addStickyNote(scenario.name, scenario.processVersionId, mapValues(nodeInputRelOffset, Math.round));
-            } else {
-                graph.current.addNode(monitor.getItem(), mapValues(nodeInputRelOffset, Math.round));
-                setLinksHovered(graph.current.graph);
-            }
+            const position = mapValues(nodeInputRelOffset, Math.round);
+            handleItemDrop(item, position);
+
+            function handleItemDrop(item: NodeType, position: any) {
+                if (item?.type === StickyNoteType) {
+                    graph.current.addStickyNote(scenario.name, scenario.processVersionId, position);
+                    return;
+                }
+                graph.current.addNode(item, position);
+                setLinksHovered(graph.current.graph);
+            }
designer/client/src/components/graph/utils/graphUtils.ts (1)

130-134: LGTM! Consider adding JSDoc documentation.

The addition of the preventDefault parameter with a default value is a good approach to make the event handling more flexible. This change maintains backward compatibility while allowing fine-grained control over event prevention.

Consider adding JSDoc documentation to explain the purpose of the preventDefault parameter:

/**
 * Creates an event handler for graph interactions
 * @param touchEvent Handler for touch events
 * @param mouseEvent Handler for mouse events
 * @param preventDefault Function to determine if default event behavior should be prevented
 * @returns Combined event handler function
 */
designer/server/src/main/scala/pl/touk/nussknacker/ui/config/FeatureTogglesConfig.scala (1)

61-62: Consider adding configuration validation

The configuration parsing looks good, using the idiomatic Scala approach with getAs and getOrElse. However, consider adding validation for the sticky notes settings to ensure all required fields are properly configured.

Example validation approach:

val stickyNotesSettings = config.getAs[StickyNotesSettings]("stickyNotesSettings") match {
  case Some(settings) => validateStickyNotesSettings(settings)
  case None => StickyNotesSettings.default
}
designer/client/src/actions/nk/process.ts (1)

63-105: Consider adding loading states

The sticky note operations would benefit from loading states to improve user experience during API calls.

Consider dispatching loading actions before API calls and clearing them after completion:

// Example for fetchStickyNotesForScenario
dispatch({ type: "STICKY_NOTES_LOADING", loading: true });
// API call
// ... then in finally block:
dispatch({ type: "STICKY_NOTES_LOADING", loading: false });
designer/client/src/components/graph/NodeDescriptionPopover.tsx (2)

123-123: Enhance code clarity with better documentation

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

Consider this improvement:

-        if (isStickyNoteElement(view.model)) return; //Dont use it for stickyNotes
+        // Skip description popover for sticky notes as they handle their own content display
+        if (isStickyNoteElement(view.model)) {
+            return;
+        }

Line range hint 123-128: Consider extracting sticky note behavior into a separate component

The current approach of excluding sticky notes from the description popover is correct. However, as the sticky notes feature grows, consider creating a dedicated StickyNotePopover component to handle sticky note-specific interactions. This would:

  • Separate concerns more clearly
  • Make the code more maintainable
  • Allow for easier extension of sticky note-specific features
designer/client/src/components/graph/graphStyledWrapper.ts (1)

186-211: Consider UX improvements for the sticky note editor.

The current implementation could benefit from the following enhancements:

  1. Add hover state styles for better interactivity
  2. Include smooth transitions for state changes
  3. Consider adding a subtle box-shadow for depth

Here's a suggested implementation:

 .sticky-note-markdown-editor: {
+    transition: "all 0.2s ease-in-out",
+    boxShadow: `0 1px 3px ${alpha(theme.palette.common.black, 0.1)}`,
+    "&:hover": {
+        backgroundColor: alpha(theme.palette.common.white, 0.4),
+        boxShadow: `0 2px 4px ${alpha(theme.palette.common.black, 0.15)}`,
+    },
     // ... existing styles
 }
designer/client/src/reducers/graph/utils.ts (2)

85-96: Consider defensive programming and performance optimizations

The function could benefit from the following improvements:

  1. Add null checks for state.stickyNotes to handle undefined cases
  2. Optimize to avoid multiple iterations over the arrays
 export function removeStickyNoteFromLayout(state: GraphState, stickyNoteId: number): { layout: NodePosition[]; stickyNotes: StickyNote[] } {
-    const { layout } = state;
+    const { layout, stickyNotes = [] } = 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 };
-    });
+    const updatedStickyNotes = stickyNotes.filter((n) => n.noteId !== stickyNoteId);
+    const filteredLayout = layout.filter((l) => l.id !== stickyNoteLayoutId);
+    const newLayoutEntries = updatedStickyNotes.map((stickyNote) => ({
+        id: createStickyNoteId(stickyNote.noteId),
+        position: stickyNote.layoutData,
+    }));
     return {
-        stickyNotes: [...updatedStickyNotes],
-        layout: [...layout.filter((l) => l.id !== stickyNoteLayoutId), ...updatedLayout],
+        stickyNotes: updatedStickyNotes,
+        layout: [...filteredLayout, ...newLayoutEntries],
     };
 }

Line range hint 85-135: Consider implementing a StickyNoteManager class

The current implementation spreads sticky note management across multiple functions. Consider encapsulating this functionality in a dedicated StickyNoteManager class to:

  1. Centralize validation logic
  2. Manage state consistency
  3. Handle ID generation and uniqueness
  4. Provide a cleaner API for sticky note operations

This would improve maintainability and make the code more testable.

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

85-97: Consider adding documentation for test utility methods.

The methods are well-structured, but since they're part of a test utility class, consider adding ScalaDoc comments to explain their testing purpose and usage examples.

Example documentation:

/** Updates a test scenario and waits for completion.
  * @param scenarioName name of the scenario to update
  * @param newScenario new scenario content
  * @return ProcessUpdated containing update results
  */
def updateScenario(...

/** Adds a sticky note to a test scenario and waits for completion.
  * @param scenarioName name of the scenario to add the note to
  * @param request sticky note details
  * @return correlation ID of the created note
  */
def addStickyNote(...

222-258: Consider improving error handling for missing scenarios.

Both helper methods use .map(_.get) on the optional scenario ID, which could throw if the scenario doesn't exist. Consider using more explicit error handling.

Example improvement:

-      scenarioId <- futureFetchingScenarioRepository.fetchProcessId(scenarioName).map(_.get)
+      scenarioId <- futureFetchingScenarioRepository.fetchProcessId(scenarioName).flatMap {
+        case Some(id) => Future.successful(id)
+        case None => Future.failed(new NoSuchElementException(s"Scenario not found: $scenarioName"))
+      }
designer/server/src/main/scala/pl/touk/nussknacker/ui/server/AkkaHttpBasedRouteProvider.scala (1)

72-72: Well-designed integration of sticky notes functionality.

The implementation follows a clean architecture pattern:

  • Repository layer for data access
  • Service layer for business logic
  • API layer for HTTP endpoints
  • Proper integration into the existing route structure

This modular approach ensures good separation of concerns and maintainability.

Also applies to: 171-171, 402-409, 603-603

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

695-707: Consider making the default sticky note color configurable.

The default color is hardcoded. Consider moving it to a configuration object for better maintainability and customization.

-            color: STICKY_NOTE_DEFAULT_COLOR, //TODO add config for default sticky note color? For now this is default.
+            color: config.stickyNotes.defaultColor ?? STICKY_NOTE_DEFAULT_COLOR,

713-713: Use i18next interpolation instead of template literal.

For consistency with other error messages, use i18next interpolation for the stickyNoteId.

-                i18next.t("notification.error.failedToDeleteStickyNote", `Failed to delete sticky note with id: ${stickyNoteId}`),
+                i18next.t("notification.error.failedToDeleteStickyNote", "Failed to delete sticky note with id: {{stickyNoteId}}", { stickyNoteId }),

695-746: Add input validation and JSDoc comments.

Consider these improvements:

  1. Add input validation for required fields
  2. Add JSDoc comments to document parameters, return types, and possible errors

Example JSDoc and validation:

/**
 * Adds a sticky note to a scenario
 * @param scenarioName - The name of the scenario
 * @param scenarioVersionId - The version ID of the scenario
 * @param position - The position of the sticky note
 * @param dimensions - The dimensions of the sticky note
 * @returns Promise<AxiosResponse<StickyNote>>
 * @throws {Error} When required parameters are missing
 */
addStickyNote(scenarioName: string, scenarioVersionId: number, position: Position, dimensions: Dimensions) {
    if (!scenarioName || !scenarioVersionId || !position || !dimensions) {
        throw new Error("Missing required parameters");
    }
    // ... rest of the method
}
docs/configuration/DesignerConfiguration.md (1)

593-593: Documentation enhancement needed for sticky notes panel.

The addition of the sticky-notes-panel configuration is well-placed and follows the existing pattern. However, consider enhancing the documentation by adding:

  • A description of the sticky notes feature and its purpose
  • Available configuration options specific to sticky notes
  • Any limitations or constraints users should be aware of
docs-internal/api/nu-designer-openapi.yaml (2)

5501-5513: Well-designed schema definitions for sticky notes functionality.

The schema definitions are well-structured with:

  • Clear separation between core entity (StickyNote) and request DTOs
  • Proper required field annotations
  • Consistent use of types
  • Comprehensive property definitions

A few suggestions to enhance the implementation:

  1. Consider adding validation constraints for:
    • Color format (hex color code)
    • Dimensions (minimum/maximum values)
    • Content length limits
    Dimensions:
      title: Dimensions
      type: object
      required:
      - width
      - height
      properties:
        width:
          type: integer
          format: int64
+         minimum: 50
+         maximum: 1000
        height:
          type: integer
          format: int64
+         minimum: 50
+         maximum: 1000

    StickyNote:
      title: StickyNote
      type: object
      required:
      - noteId
      - content
      - layoutData
      - color
      - dimensions
      - editedBy
      - editedAt
      properties:
        color:
          type: string
+         pattern: '^#[0-9A-Fa-f]{6}$'
        content:
          type: string
+         maxLength: 5000

Also applies to: 7191-7276


Line range hint 2530-7276: Consider adding rate limiting and pagination for sticky notes endpoints.

For better API scalability and performance:

  1. Add rate limiting headers to prevent abuse
  2. Implement pagination for GET endpoint to handle large numbers of sticky notes
  3. Consider adding bulk operations endpoints for efficiency
designer/server/src/main/scala/pl/touk/nussknacker/ui/process/repository/stickynotes/StickyNotesRepository.scala (2)

21-49: Add Scaladoc comments to public methods in the trait

Consider adding Scaladoc comments to each of the public methods in the StickyNotesRepository trait to improve code documentation and help other developers understand their purpose and usage.


21-49: Ensure consistent parameter ordering across methods

To improve readability and maintainability, consider ordering the parameters consistently across all methods. For example, placing scenarioId and scenarioVersionId parameters before other parameters in each method.

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

66-69: Consider removing the dependency on Shapeless for tuple mapping

The use of shapeless.syntax.std.tuple._ and productElements introduces an additional dependency on Shapeless, which may be unnecessary. You can achieve the same result using standard Scala features, simplifying the code and reducing dependencies.

You can modify the mapping as follows:

-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
+  )

This change eliminates the need for tupleWithoutAutoIncId and the Shapeless import.


25-25: Define foreign key constraints using Slick's table definitions

Instead of executing raw SQL to add foreign key constraints, you can define the foreign key relationship directly in the Slick table definition. This approach enhances type safety and maintainability.

Modify the StickyNotesEntity class to include the foreign key constraint:

def scenario = foreignKey(
  "sticky_notes_scenario_version_fk",
  (scenarioId, scenarioVersionId),
  processVersionsTable
)(
  r => (r.processId, r.id),
  onDelete = ForeignKeyAction.Cascade
)

Ensure you have processVersionsTable defined as a TableQuery of the corresponding table, and import or define any necessary references.


39-50: Consider adding indexes to improve query performance

Adding indexes to columns that are frequently queried can significantly enhance database performance. Columns like noteCorrelationId, scenarioId, and scenarioVersionId are good candidates for indexing.

Include index definitions in the StickyNotesEntity class:

// After column definitions
def idxNoteCorrelationId = index("idx_sticky_notes_note_correlation_id", noteCorrelationId)
def idxScenarioId = index("idx_sticky_notes_scenario_id", scenarioId)
def idxScenarioVersionId = index("idx_sticky_notes_scenario_version_id", scenarioVersionId)

These indexes will help optimize queries that filter or join on these columns.


41-44: Use appropriate data types for structured data

Fields such as layoutData, color, and dimensions appear to hold structured or complex data. Storing them as plain strings may limit querying capabilities and data validation.

If these fields contain JSON data or other structured formats, consider using appropriate database column types:

  • For JSON data, use a JSON or JSONB column type if your database supports it.
  • Define custom column types or Slick mappings for complex structures.

For example, if dimensions is a JSON object representing width and height:

// Define a case class for dimensions
case class Dimensions(width: Int, height: Int)

// Use an implicit mapping for the custom type
implicit val dimensionsColumnType = MappedColumnType.base[Dimensions, String](
  dimensions => write(dimensions),    // Convert Dimensions to JSON string
  jsonStr => read[Dimensions](jsonStr) // Convert JSON string to Dimensions
)

// Then define the column
def dimensions = column[Dimensions]("dimensions", NotNull)

This approach ensures data integrity and eases future data manipulation.

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

38-41: Consider using Int instead of Long for dimension fields

In the Dimensions case class, the width and height fields are defined as Long. Unless you anticipate extremely large values for dimensions, using Int might be more appropriate and efficient.

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

67-71: Enhance Error Handling for Markdown Parsing

In the catch block, consider providing more descriptive feedback to the user when markdown parsing fails. Instead of a generic message, you could display a user-friendly error indicating what went wrong and how to fix it. This improves the user experience by guiding users to correct their input.


83-83: Consider Externalizing the SVG Path for Reusability

The stickyNotePath constant defines an SVG path used for the sticky note shape. If this path might be reused in other components or shapes, consider moving it to a shared constants file to promote reusability and maintainability.

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

25-46: Optimize by reusing the sticky note background color

The function getStickyNoteBackgroundColor(theme, stickyNote.color) is called multiple times within the attrs object. To improve performance and enhance readability, consider computing the background color once and reusing it in the attributes.

Apply this refactor:

+ const stickyNoteBackgroundColor = getStickyNoteBackgroundColor(theme, stickyNote.color);

 attrs: {
     size: {
         width: stickyNote.dimensions.width,
         height: stickyNote.dimensions.height,
     },
     body: {
-        fill: getStickyNoteBackgroundColor(theme, stickyNote.color).main,
+        fill: stickyNoteBackgroundColor.main,
         opacity: 1,
     },
     foreignObject: {
         width: stickyNote.dimensions.width,
         height: stickyNote.dimensions.height - ICON_SIZE - CONTENT_PADDING * 4,
-        color: theme.palette.getContrastText(getStickyNoteBackgroundColor(theme, stickyNote.color).main),
+        color: theme.palette.getContrastText(stickyNoteBackgroundColor.main),
     },
     icon: {
         xlinkHref: stickyNoteIcon,
         opacity: 1,
-        color: theme.palette.getContrastText(getStickyNoteBackgroundColor(theme, stickyNote.color).main),
+        color: theme.palette.getContrastText(stickyNoteBackgroundColor.main),
     },
     border: {
-        stroke: getStickyNoteBackgroundColor(theme, stickyNote.color).dark,
+        stroke: stickyNoteBackgroundColor.dark,
         strokeWidth: 1,
     },
 },
designer/server/src/main/scala/pl/touk/nussknacker/ui/process/repository/stickynotes/DbStickyNotesRepository.scala (2)

88-94: Improve exception messages for clarity

The exception messages in lines 88 and 92 use phrases like "This is odd," which may not provide sufficient detail for debugging. Consider making the messages more descriptive.

Consider applying this diff:

 case 0 => DBIOAction.failed(new IllegalStateException(
-  s"This is odd, no sticky note was added"
+  s"Failed to add sticky note: no records were inserted"
 ))

 case n =>
   DBIOAction.failed(new IllegalStateException(
-    s"This is odd, more than one sticky note were added (added $n records)."
+    s"Failed to add sticky note: unexpected number of records inserted ($n)"
   ))

34-48: Add comments to explain complex query logic in findStickyNotes

The query in the findStickyNotes method is complex and could benefit from inline comments explaining the logic at each step, improving maintainability and readability.

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

60-60: Typo in endpoint summary: 'nodes' should be 'notes'

The summary currently reads "Returns sticky nodes for given scenario with version" but it should be "Returns sticky notes for given scenario with version".


146-146: Consistent terminology in endpoint summary

In the summary, "Deletes stickyNote by given noteId" should be "Deletes sticky note by given noteId" for consistency and readability.


171-171: Use jsonBody instead of plainBody for error responses

In the error responses at lines 171, 183, 195, and 207, plainBody is used to define the error outputs. For better consistency and to provide structured error information, consider using jsonBody instead of plainBody. This will return the error responses in JSON format, which is more suitable for API clients expecting JSON responses.

Also applies to: 183-183, 195-195, 207-207

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

46-104: Refactor duplicate logic in endpoint handlers

The logic for fetching scenarioId and checking authorization is repeated across the endpoint handlers for getting, adding, updating, and deleting sticky notes. Consider abstracting this common code into a helper method or middleware to reduce duplication and enhance maintainability.

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

470-489: Consider externalizing sticky note size constraints to configuration

The maximum and minimum dimensions for sticky notes are hardcoded within the CELL_RESIZED event handler. Moving these constraints to a configuration file or constants module will enhance maintainability and make future adjustments easier.

Would you like assistance in refactoring this code to externalize the size constraints?


495-495: Use strict equality for string comparison

To prevent potential type coercion issues, it's advisable to use strict equality === when comparing strings in TypeScript.

Apply this diff to update the comparison:

-if (updatedStickyNote.content == content) return;
+if (updatedStickyNote.content === content) return;
designer/client/src/reducers/graph/reducer.ts (2)

249-254: Simplify redundant object spread in return statement

The return statement spreads the result of addStickyNotesWithLayout into a new object, which is unnecessary since addStickyNotesWithLayout already returns a new state object. You can simplify the return statement by returning the result directly.

Apply this diff to simplify the return statement:

-    return {
-        ...addStickyNotesWithLayout(state, { stickyNotes, layout }),
-    };
+    return addStickyNotesWithLayout(state, { stickyNotes, layout });

255-260: Simplify redundant object spread in return statement

Similar to the previous case, the return statement here unnecessarily spreads the result of addStickyNotesWithLayout into a new object. Simplify the return statement by returning the result directly.

Apply this diff:

-    return {
-        ...addStickyNotesWithLayout(state, { stickyNotes, layout }),
-    };
+    return addStickyNotesWithLayout(state, { stickyNotes, layout });
📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL

📥 Commits

Reviewing files that changed from the base of the PR and between 14a9a75 and 894ecb3.

⛔ Files ignored due to path filters (2)
  • 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 (56)
  • designer/client/package.json (1 hunks)
  • designer/client/src/actions/actionTypes.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 (14 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/stickyNotes/StickyNotesPanel.tsx (1 hunks)
  • designer/client/src/components/toolbarSettings/TOOLBAR_COMPONENTS_MAP.ts (2 hunks)
  • designer/client/src/components/toolbarSettings/defaultToolbarsConfig.ts (1 hunks)
  • designer/client/src/components/toolbars/creator/ComponentIcon.tsx (2 hunks)
  • designer/client/src/components/toolbars/creator/ToolBox.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/types/node.ts (1 hunks)
  • designer/client/src/types/stickyNote.ts (1 hunks)
  • designer/server/src/main/resources/defaultDesignerConfig.conf (2 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/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/config/scenariotoolbar/ToolbarPanelConfig.scala (1 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 (2 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/ProcessesResourcesSpec.scala (2 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/configuration/DesignerConfiguration.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 (82)
designer/server/src/main/scala/db/migration/hsql/V1_060__CreateStickyNotes.scala (2)

1-5: LGTM! Clean organization of imports and package structure.

The imports are minimal and properly scoped, including only the necessary components for the HSQL-specific migration.


6-8: LGTM! Clean implementation with proper lazy loading.

The implementation follows best practices:

  • Extends the base definition trait for sticky notes
  • Uses lazy loading for the JDBC profile
  • Properly overrides the profile for HSQLDB specifically

Please ensure corresponding migration files exist for other supported database types (e.g., PostgreSQL) to maintain database compatibility across different environments.

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

1-5: LGTM! Clean and well-organized imports

The package declaration and imports are appropriate for a PostgreSQL-specific migration implementation.


6-8: LGTM! Well-structured migration class

The implementation follows best practices:

  • Proper extension of the base definition
  • Correct use of lazy initialization for the profile
  • Clean separation of PostgreSQL-specific configuration

Please ensure that:

  1. The base definition in V1_060__CreateStickyNotesDefinition includes appropriate indexes for foreign key columns
  2. The migration has been tested with actual PostgreSQL database to verify the schema changes
designer/client/src/common/StickyNote.ts (1)

6-8: Consider consolidating ID fields.

Having both id (optional string) and noteId (required number) could lead to confusion. Consider using a single ID field or documenting the specific use case for each.

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

14-15: LGTM! Clean integration of sticky notes functionality.

The addition of StickyNotesEntityFactory follows the established pattern of trait composition and maintains separation of concerns in the database layer.


14-15: Verify database migration ordering.

Please ensure that the database migrations (V1_060__CreateStickyNotes) are applied before this change is deployed to prevent runtime issues.

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

9-12: Verify the completeness of event types.

The current set of events covers Create, Update, and Delete operations. Please verify if any additional events are needed for the complete sticky notes feature, such as:

  • Events for bulk operations
  • Events for state changes (e.g., visibility, pinning)
  • Events for user interactions
designer/client/src/reducers/graph/types.ts (1)

4-4: Verify the StickyNote type definition

The import looks good, but please ensure that the StickyNote type in ../../common/StickyNote is properly defined with all necessary properties for the sticky notes feature.

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

41-43: LGTM! The StickyNote entry follows consistent patterns.

The new entry maintains the established structure and naming conventions of the configuration file.

Please ensure that all related changes (UI components, actions, and state management) properly reference this node type configuration.

designer/client/src/containers/theme/helpers.ts (1)

4-4: LGTM!

The import statement is correctly formatted and necessary for the new functionality.

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

15-16: LGTM! The new action types are well-defined.

The added action types follow the existing naming conventions and represent clear, atomic operations for sticky note management.

designer/client/src/components/toolbarSettings/TOOLBAR_COMPONENTS_MAP.ts (2)

11-11: LGTM! Clean import statement.

The import follows the established pattern and maintains consistency with the project structure.


21-21: LGTM! Please verify toolbar configuration.

The sticky notes panel integration looks good. Since this is a draft PR, please ensure that:

  1. The corresponding toolbar configuration includes this new panel identifier
  2. The panel's position in the toolbar layout has been tested
designer/client/src/components/graph/GraphPartialsInTS/cellUtils.ts (1)

2-3: LGTM! Imports are appropriate for the new functionality.

The addition of StickyNote type and cloneDeep from lodash are well-suited for handling sticky note operations.

designer/client/src/components/stickyNotes/StickyNotesPanel.tsx (2)

1-10: LGTM: Imports are well-organized and necessary

All imports are being used and serve clear purposes:

  • React core functionality
  • State management (Redux)
  • Internationalization
  • Type definitions and UI components

16-30: Verify the necessity of useMemo for this component

The useMemo usage here might be premature optimization:

  1. The component's render tree seems relatively simple
  2. The dependencies (pristine, props, t) might change frequently enough to negate the benefits

Consider benchmarking with and without useMemo to ensure it provides meaningful performance benefits.

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

3-3: LGTM! Clean import statement.

The import follows TypeScript best practices and maintains good module organization.

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

37-37: LGTM! Panel placement is appropriate.

The sticky-notes-panel is correctly placed in the topLeft toolbar section alongside other tool panels.


58-61: Verify sticky notes limits and consider adding validation messages.

The limits seem reasonable, but please verify:

  1. Is 5000 characters sufficient for your use case? Consider documenting the rationale.
  2. Is the limit of 5 notes per process adequate?

Consider adding validation messages to be displayed when these limits are exceeded, similar to how scenarioLabelSettings includes validationMessage.

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

1-5: LGTM! Imports and type definitions are well-organized.

The imports are appropriate for the sticky note functionality, and the type alias helps maintain type compatibility.


17-18: LGTM! Element definition follows JointJS patterns.

The implementation is concise and properly typed, following JointJS best practices for custom element definition.

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

10-10: LGTM! Import statement is properly placed

The import for StickyNoteType is correctly organized with other type imports and follows the project's import structure.

designer/client/src/components/graph/node-modal/node/FragmentContent.tsx (2)

4-4: LGTM! Import changes are well-organized.

The getStickyNotes selector import follows the established pattern and is correctly placed with other selector imports.


44-44: Verify FragmentGraphPreview prop types and documentation.

Ensure that:

  1. The stickyNotes prop is properly typed in the FragmentGraphPreview component.
  2. The component's documentation is updated to reflect this new prop.
designer/server/src/test/scala/pl/touk/nussknacker/test/base/it/WithSimplifiedConfigScenarioHelper.scala (3)

9-10: LGTM! Clean and well-scoped imports.

The new imports are properly organized and explicitly specify the required types.


44-46: LGTM! Consider adding test coverage verification.

The method follows the established pattern of delegating to rawScenarioHelper. Since this is a test helper method, ensure there are test cases that verify both successful and failed scenario updates using this method.


48-50: LGTM! Consider comprehensive test scenarios.

The method is well-structured and consistent with the trait's design. Since this is introducing sticky note functionality, ensure test coverage includes:

  • Adding sticky notes to different types of scenarios (saved, deployed, archived)
  • Handling of invalid requests
  • Correlation ID usage in subsequent operations
designer/client/src/components/StickyNotePreview.tsx (2)

1-8: LGTM! Imports are well-organized and necessary.

All imports are being used appropriately in the component.


55-62: LGTM! Clean and efficient render structure.

The component's render method is well-structured and makes good use of className composition.

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

10-11: LGTM: Clean import additions

The new imports are properly typed and necessary for the sticky notes functionality.


13-19: LGTM: Well-typed function signature update

The addition of the stickyNotes parameter is properly typed and maintains the function's type safety.


Line range hint 30-44: LGTM: Robust cell management implementation

The cell management logic properly handles the addition, removal, and updates of cells while maintaining clean separation between different cell types.

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

10-12: LGTM: Imports are well-organized

The new imports for sticky note actions and types are properly structured and align with the feature requirements.

Also applies to: 17-17


25-27: LGTM: Props structure is consistent

The new sticky note properties follow the established pattern for action handlers and state management.

Please verify that the StickyNote type definition in ../../common/StickyNote includes all necessary properties for the graph operations.

Also applies to: 31-31


77-79: Consider adding creation event for completeness

The new cell events cover modification operations well. Consider adding a CELL_CREATED = "cellCustom:created" event to maintain symmetry with the full lifecycle of sticky notes.

Also, verify that these events align with all the sticky note operations in the UI implementation.

designer/server/src/main/scala/pl/touk/nussknacker/ui/config/scenariotoolbar/ToolbarPanelConfig.scala (2)

34-34: LGTM! Clean addition of the sticky notes panel type.

The new StickyNotesPanel value follows the established pattern and is correctly integrated into the enumeration. The implementation maintains type safety and will work with existing JSON serialization.


34-34: Verify frontend compatibility.

Please ensure that the panel type string "sticky-notes-panel" matches the identifier used in the frontend components, particularly in the defaultToolbarsConfig.ts and TOOLBAR_COMPONENTS_MAP mentioned in the summary.

designer/client/src/components/toolbarSettings/defaultToolbarsConfig.ts (2)

74-80: LGTM! The sticky notes panel integration looks good.

The addition of sticky-notes-panel follows the existing pattern and maintains consistency with other panel definitions in the TopLeft toolbar section.


77-77: Verify sticky-notes-panel registration

Please ensure that the sticky-notes-panel ID is properly registered in the panel system and that the corresponding component is correctly imported and available.

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

1-21: LGTM! Well-organized imports.

The imports are clearly organized and properly scoped for the test requirements.


24-33: LGTM! Well-structured test specification class.

The class appropriately combines testing traits for HTTP testing, configuration, and assertions.


84-84: Expand test coverage for comprehensive validation.

The TODO indicates more tests are needed. Consider adding test cases for:

  1. Successful sticky note creation and retrieval
  2. Invalid version ID handling
  3. Permission checks
  4. Content validation
  5. Multiple notes handling

Would you like me to help generate additional test cases?

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

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

The new imports for sticky note functionality are correctly placed and follow project conventions.

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

15-15: LGTM! Good practice making the styled component reusable.

Making StyledToolbox exportable promotes code reuse and maintains styling consistency across components. This change aligns well with the sticky notes feature implementation.

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

6-6: LGTM! Import changes are well-organized.

The new imports for sticky notes functionality are logically grouped and include all necessary dependencies.

Also applies to: 13-24, 28-28


36-36: LGTM! State management is properly implemented.

The sticky notes state is correctly retrieved using the selector and integrated into the component.


87-89: LGTM! Action creators are properly bound.

The sticky note action creators are correctly integrated into the bindActionCreators call.


104-104: LGTM! Props are properly passed down.

The stickyNotes prop is correctly passed to the GraphWrapped component. Please verify that the GraphWrapped component is updated to handle this new prop appropriately.

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

10-10: LGTM: Import statement is correctly placed

The import for StickyNotesSettings follows the project's import organization pattern.


32-33: Verify field ordering in case class

The new stickyNotesSettings field is added at the end of the case class, which is good for backward compatibility. However, consider grouping it with related settings fields for better organization.


83-83: LGTM: Constructor parameter correctly added

The stickyNotesSettings parameter is properly added to the constructor call, maintaining consistency with the case class definition.

designer/client/src/actions/nk/process.ts (2)

9-11: LGTM: Imports are appropriate for the new functionality

The new imports are well-organized and necessary for the sticky notes feature implementation.


23-23: LGTM: Appropriate placement of sticky notes fetch

The sticky notes fetch is correctly placed after fetching process details and before displaying the process.

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

11-11: LGTM! Clean import addition.

The import follows the established pattern and provides proper typing for the sticky notes feature.


74-74: LGTM! Well-implemented selector.

The getStickyNotes selector is properly implemented with:

  • Memoization using createSelector
  • Appropriate fallback to empty array
  • Correct typing with StickyNote[]
designer/client/src/components/graph/NodeDescriptionPopover.tsx (1)

8-8: LGTM: Clean import addition

The import statement for isStickyNoteElement is well-placed and follows proper conventions.

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

124-124: LGTM! Toolbar panel placement is well-structured.

The sticky notes panel is logically placed in the topLeft section, between search/tips and creator panels. The configuration follows the existing pattern and the hidden property correctly handles archived scenarios.


214-217: Verify the sticky notes limits.

Please confirm if these limits are appropriate for your use case:

  • maxContentLength: 5000 characters per note
  • maxNotesCount: 5 notes total

Consider:

  • Are these limits sufficient for typical user scenarios?
  • Have these values been tested with real-world usage patterns?
  • Should these limits be configurable via environment variables like other settings?
designer/client/src/components/graph/graphStyledWrapper.ts (1)

2-2: LGTM: Import changes are appropriate.

The new imports for alpha and stickyNotePath are correctly added and necessary for the sticky notes feature implementation.

Also applies to: 5-5

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

8-9: LGTM: Imports are appropriate

The new imports are specific and necessary for the sticky notes functionality.

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

13-13: LGTM! Imports are properly organized.

The new imports are necessary for the sticky notes functionality and scenario management.

Also applies to: 19-23, 26-26


48-48: LGTM! Repository field follows established patterns.

The stickyNotesRepository is properly initialized and follows the same patterns as other repository fields in the class.

designer/client/package.json (2)

24-24: Good choice of DOMPurify for XSS prevention!

Adding DOMPurify is a crucial security measure for sanitizing user-generated HTML content in sticky notes, protecting against XSS attacks.


24-24: Verify DOMPurify version for updates

Please verify if version 3.2.0 is the latest stable version and check for any security advisories or patches.

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

402-409: LGTM! Well-structured service initialization.

The StickyNotesApiHttpService is properly initialized with all necessary dependencies and follows the established pattern of other services in the codebase.

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

34-34: LGTM!

The import statement is well-organized and correctly imports the necessary types for sticky notes functionality.


740-746: LGTM!

The getStickyNotes method is well-implemented with proper typing, error handling, and query parameter usage.

designer/server/src/test/scala/pl/touk/nussknacker/ui/api/ProcessesResourcesSpec.scala (2)

55-55: LGTM!

The import statement for StickyNotesPanel is correctly placed and follows the existing import organization pattern.


1229-1230: LGTM!

The test case correctly verifies the toolbar configuration with the addition of CreatorPanel and StickyNotesPanel, maintaining proper test coverage for the toolbar functionality.

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

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

The endpoints follow REST best practices with proper HTTP methods:

  • GET for retrieval
  • POST for creation
  • PUT for updates
  • DELETE for removal

The error handling is comprehensive, covering:

  • 400 for invalid requests
  • 401 for authentication issues
  • 403 for authorization issues
  • 404 for not found scenarios
  • 501 for unsupported features
designer/server/src/main/scala/pl/touk/nussknacker/ui/process/repository/stickynotes/StickyNotesRepository.scala (1)

43-44: ⚠️ Potential issue

Remove unnecessary trailing comma in method parameters

There is a trailing comma after scenarioVersionId: VersionId in the parameter list of the updateStickyNote method, which could cause a syntax error.

Apply this diff to fix the issue:

 def updateStickyNote(
     noteId: StickyNoteId,
     content: String,
     layoutData: LayoutData,
     color: String,
     dimensions: Dimensions,
     targetEdge: Option[String],
-    scenarioVersionId: VersionId,
+    scenarioVersionId: VersionId
 )(implicit user: LoggedUser): DB[Int]

Likely invalid or redundant comment.

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

27-64: Database table and entity mapping are correctly defined

The StickyNotesEntity class accurately defines the database table schema for sticky notes, with all columns appropriately typed and the foreign key constraint correctly established with processVersionsTable.


96-112: The entity data class and conversion method are properly implemented

The StickyNoteEventEntityData case class correctly represents the entity data, and the toStickyNote method accurately converts it into a StickyNote object, ensuring consistency between the database layer and the application layer.

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

75-75: Review the Use of a Disabled Textarea

The textarea element is rendered but disabled:

<textarea @selector="markdown-editor" class="sticky-note-markdown-editor" name="markdown-editor" autocomplete="off" disabled="disabled"></textarea>

If user input is not intended here, consider removing the textarea to simplify the DOM and avoid confusion. If it will be enabled in future implementations, you might want to add a comment explaining its current purpose.


55-62: ⚠️ Potential issue

Verify Security of Custom Renderer Functions with DOMPurify

While overriding the link and image methods in the marked renderer, ensure that DOMPurify effectively sanitizes all user-provided content, especially the href attribute. This is crucial to prevent XSS attacks through malicious links or image sources. Consider specifying additional DOMPurify configurations if necessary.

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

94-95: Verify the event mapping for 'touchcancel'

In the documentEvents, the 'touchcancel' event is mapped to onPointerUp, while 'touchend' is mapped to onPointerUpCustom. Please verify if this is intentional. For consistent behavior across touch devices, consider mapping 'touchcancel' to onPointerUpCustom if appropriate.


60-62: Ensure proper handling of the 'CELL_DELETED' event

The removeButtonTool triggers the Events.CELL_DELETED when the sticky note is removed. Confirm that this event is correctly handled elsewhere in the application to manage cleanup processes related to the sticky note deletion.


71-71: Confirm the SVG path data for the resize handle

The d attribute for the resize handle path is set to "M 4 0 L 4 4 L 0 4 L 0 5 L 5 5 L 5 0 L 4 0". Ensure that this SVG path renders the desired icon consistently across different browsers and devices.


133-133: Validate the setting of sticky note content

The line stickyNoteModel.attr(\${MARKDOWN_EDITOR_NAME}/props/value`, stickyNote.content);sets the content of the sticky note. Verify thatMARKDOWN_EDITOR_NAME` correctly references the intended attribute path and that the content renders properly within the sticky note.

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

156-156: ⚠️ Potential issue

Remove unnecessary trailing comma in parameter list

There's a trailing comma at the end of the parameter list in line 156, which may cause a syntax error.

Apply this diff to fix the syntax error:

-      implicit executionContext: ExecutionContext,
+      implicit executionContext: ExecutionContext
  ): StickyNotesRepository = new DbStickyNotesRepository(dbRef, clock)

Likely invalid or redundant comment.


122-122: ⚠️ Potential issue

Remove unnecessary trailing comma in parameter list

There's a trailing comma at the end of the parameter list in line 122, which may cause a syntax error.

Apply this diff to fix the syntax error:

-      scenarioVersionId: VersionId,
+      scenarioVersionId: VersionId
)(

Likely invalid or redundant comment.

Copy link
Contributor

github-actions bot commented Nov 20, 2024

created: #7276
⚠️ Be careful! Snapshot changes are not necessarily the cause of the error. Check the logs.

@philemone philemone force-pushed the stickyNotes branch 2 times, most recently from b991b11 to 99de706 Compare November 20, 2024 13:54
@github-actions github-actions bot removed the docs label Nov 20, 2024
@philemone philemone changed the title [Draft] Sticky notes Sticky notes Nov 20, 2024
@philemone philemone marked this pull request as ready for review November 20, 2024 17:08
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: 20

🧹 Outside diff range and nitpick comments (35)
designer/client/src/types/component.ts (1)

8-8: Consider adding JSDoc documentation.

Since this property is part of a larger sticky notes feature and is used across multiple components, adding documentation would help other developers understand its purpose and usage.

+    /**
+     * Function that determines if the component should be disabled.
+     * Used primarily for controlling interactive states of sticky note components.
+     * @returns {boolean} True if the component should be disabled, false otherwise
+     */
     disabled?: () => boolean;
designer/client/src/components/toolbars/creator/StickyNoteComponent.tsx (1)

4-4: Add type annotation and documentation for the note model.

The noteModel constant would benefit from:

  1. An explicit type annotation
  2. JSDoc documentation explaining its purpose and usage
  3. Consider using a more unique ID generation mechanism

Consider applying these improvements:

+/** Model representing a sticky note template for the toolbox.
+ * Used as a prototype for creating new sticky notes.
+ */
+const noteModel: {
+    id: string;
+    type: typeof StickyNoteType;
+    isDisabled: boolean;
+} = { id: "StickyNoteToAdd", type: StickyNoteType, isDisabled: false };
designer/client/src/containers/theme/helpers.ts (2)

4-4: Consider relocating shared constants to a dedicated constants file

The STICKY_NOTE_DEFAULT_COLOR constant is imported from a component file but used in theme helpers. This creates a dependency from the theme layer on the component layer, which could make the codebase harder to maintain.

Consider moving shared constants to a dedicated constants file (e.g., src/constants/theme.ts) to maintain better separation of concerns.


19-26: Add parameter validation and documentation

The function would benefit from proper JSDoc documentation and input validation.

Consider adding documentation and validation:

+/**
+ * Generates a background color for sticky notes based on the provided color or falls back to default
+ * @param theme - The MUI theme object
+ * @param color - The desired sticky note color (must be a valid CSS color)
+ * @returns An augmented color object from the theme palette
+ */
 export function getStickyNoteBackgroundColor(theme: Theme, color: string) {
+    if (!theme || !color) {
+        throw new Error('Theme and color parameters are required');
+    }
     const isValidColor = CSS.supports("color", color);
     return theme.palette.augmentColor({
         color: {
             main: isValidColor ? color : STICKY_NOTE_DEFAULT_COLOR,
         },
     });
 }
designer/client/src/components/graph/GraphPartialsInTS/cellUtils.ts (2)

2-2: Consider using absolute imports for better maintainability

The relative import with multiple parent directory traversals (../../../) can make the code harder to maintain. Consider configuring TypeScript path aliases to use absolute imports instead.

Example configuration in tsconfig.json:

{
  "compilerOptions": {
    "baseUrl": ".",
    "paths": {
      "@common/*": ["src/common/*"]
    }
  }
}

Then the import could be simplified to:

import { StickyNote } from "@common/StickyNote";

12-14: Extract string literal into a named constant

The string literal "stickyNote.StickyNoteElement" should be extracted into a named constant for better maintainability and reusability.

+const STICKY_NOTE_ELEMENT_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_ELEMENT_TYPE;
 }
designer/server/src/main/scala/pl/touk/nussknacker/ui/process/repository/stickynotes/StickyNotesRepository.scala (3)

17-20: Add documentation for the trait and clock usage.

Consider adding ScalaDoc to:

  1. Document the trait's purpose and responsibility
  2. Explain how the clock field is used (e.g., for audit trails, timestamps)
  3. Specify any threading or synchronization requirements for implementations
+/**
+ * Repository trait for managing sticky notes in scenarios.
+ * Provides CRUD operations for sticky notes with proper audit trailing.
+ *
+ * @note The clock field is used for tracking creation and modification timestamps
+ *       and should be consistent across the application.
+ */
 trait StickyNotesRepository {

21-30: Consider adding pagination support for findStickyNotes.

For scenarios with many sticky notes, fetching all notes at once could impact performance. Consider:

  1. Adding pagination parameters (offset/limit or cursor-based)
  2. Returning a paginated response type
 def findStickyNotes(
     scenarioId: ProcessId,
-    scenarioVersionId: VersionId
+    scenarioVersionId: VersionId,
+    offset: Int,
+    limit: Int
 ): DB[Seq[StickyNote]]

51-54: Consider implementing soft delete.

Instead of hard deleting sticky notes, consider implementing soft delete to:

  1. Maintain audit history
  2. Enable potential recovery
  3. Track deletion patterns
+  /**
+   * Marks a sticky note as deleted without removing it from the database.
+   * @return Number of affected rows (should be 1 if successful)
+   */
+  def softDeleteStickyNote(
+      noteId: StickyNoteId,
+      deletedAt: java.time.Instant = clock.instant()
+  )(implicit user: LoggedUser): DB[Int]
designer/client/src/reducers/selectors/settings.ts (1)

17-17: Consider adding explicit return type for better type safety.

The implementation follows the established pattern and is consistent with other feature settings selectors. However, consider adding an explicit return type for better maintainability and type safety.

-export const getStickyNotesSettings = createSelector(getFeatureSettings, (s) => s?.stickyNotesSettings);
+export const getStickyNotesSettings = createSelector(getFeatureSettings, (s): StickyNotesSettings | undefined => s?.stickyNotesSettings);
designer/client/src/components/toolbars/creator/ToolboxComponentGroup.tsx (2)

72-78: Consider optimizing the disabled state calculation

The disabled prop calculation could be optimized to prevent unnecessary function calls on each render.

Consider memoizing the disabled state:

const elements = useMemo(
    () =>
        componentGroup.components.map((component) => {
+           const isDisabled = useMemo(
+               () => component.disabled?.() ?? false,
+               [component.disabled]
+           );
            return (
                <Tool
                    nodeModel={component.node}
                    label={component.label}
                    key={component.componentId}
                    highlights={highlights}
-                   disabled={component.disabled ? component.disabled() : false}
+                   disabled={isDisabled}
                />
            );
        }),
    [highlights, componentGroup.components],
);

72-78: Document the disabled state logic

Please add documentation explaining when and why a tool would be disabled, as this seems to be part of the sticky notes feature implementation.

Add a comment block above the Tool component:

/**
 * Renders a Tool component with optional disabled state.
 * The disabled state is determined by the component's disabled function,
 * which is typically used to prevent certain tools (e.g., sticky notes)
 * from being used under specific conditions.
 */
designer/server/src/main/scala/pl/touk/nussknacker/ui/db/entity/StickyNotesEntityFactory.scala (1)

96-112: Consider adding input validation and documenting field mapping

The toStickyNote conversion drops several fields (noteCorrelationId, eventType, scenarioId, scenarioVersionId). While this might be intentional, it should be documented. Also, consider adding input validation for critical fields.

Example validation implementation:

final case class StickyNoteEventEntityData(
    id: StickyNoteId,
    noteCorrelationId: StickyNoteCorrelationId,
    content: String,
    layoutData: LayoutData,
    color: String,
    dimensions: Dimensions,
    targetEdge: Option[String],
    eventCreator: String,
    eventDate: Timestamp,
    eventType: StickyNoteEvent,
    scenarioId: ProcessId,
    scenarioVersionId: VersionId
) {
  require(content.nonEmpty, "Content cannot be empty")
  require(content.length <= 1000, "Content exceeds maximum length")
  require(color.matches("^#[0-9A-Fa-f]{6}$") || color.matches("^[a-zA-Z]+$"), "Invalid color format")
  
  /** Converts to StickyNote DTO, excluding audit and correlation fields */
  def toStickyNote: StickyNote =
    StickyNote(id, content, layoutData, color, dimensions, targetEdge, eventCreator, eventDate.toInstant)
}
designer/server/src/main/scala/pl/touk/nussknacker/ui/api/description/stickynotes/Dtos.scala (3)

44-54: Consider adding validation for color format.

The color field in StickyNote should validate the color format (e.g., hex color code).

Consider adding a color validator:

object StickyNote {
  private def validateColor(color: String): Boolean = 
    color.matches("^#([A-Fa-f0-9]{6}|[A-Fa-f0-9]{3})$")
    
  def apply(noteId: StickyNoteId, content: String, layoutData: LayoutData, 
            color: String, dimensions: Dimensions, targetEdge: Option[String],
            editedBy: String, editedAt: Instant): StickyNote = {
    require(validateColor(color), s"Invalid color format: $color")
    new StickyNote(noteId, content, layoutData, color, dimensions, 
                   targetEdge, editedBy, editedAt)
  }
}

91-117: Add documentation and fix formatting inconsistencies.

The error handling is comprehensive but would benefit from documentation and consistent formatting.

Consider these improvements:

 object StickyNotesError {
+  /** Thrown when the specified scenario doesn't exist */
   final case class NoScenario(scenarioName: ProcessName) extends StickyNotesError
+  
+  /** Thrown when user doesn't have required permissions */
   final case object NoPermission extends StickyNotesError with CustomAuthorizationError
+  
+  /** Thrown when note content exceeds maximum length */
   final case class StickyNoteContentTooLong(count: Int, max: Int) extends StickyNotesError
-  final case class StickyNoteCountLimitReached(max: Int)          extends StickyNotesError
-  final case class NoStickyNote(noteId: StickyNoteId)             extends StickyNotesError
+  
+  /** Thrown when scenario reaches maximum allowed notes */
+  final case class StickyNoteCountLimitReached(max: Int) extends StickyNotesError
+  
+  /** Thrown when specified sticky note doesn't exist */
+  final case class NoStickyNote(noteId: StickyNoteId) extends StickyNotesError

1-119: Overall architecture looks good with room for improvements.

The DTOs and error handling are well-structured. Consider these architectural improvements:

  1. Add a validation layer to enforce business rules (dimensions, colors, etc.)
  2. Consider using refined types for validated values
  3. Add comprehensive ScalaDoc documentation
  4. Consider adding property-based tests for JSON serialization/deserialization
designer/client/src/components/graph/EspNode/stickyNote.ts (3)

10-17: Consider reducing maximum dimensions

The maximum dimensions (3000x3000) seem excessive and could potentially impact performance or be used for DOM-based attacks. Consider reducing these to more reasonable values (e.g., 800x600) to prevent abuse while maintaining usability.

 export const STICKY_NOTE_CONSTRAINTS = {
     MIN_WIDTH: 100,
-    MAX_WIDTH: 3000,
+    MAX_WIDTH: 800,
     DEFAULT_WIDTH: 300,
     MIN_HEIGHT: 100,
-    MAX_HEIGHT: 3000,
+    MAX_HEIGHT: 600,
     DEFAULT_HEIGHT: 250,
 } as const;

64-81: Improve error handling and clarify disabled state

Two suggestions for improvement:

  1. The error message could be more user-friendly and include the actual error details
  2. The disabled textarea might be confusing - consider adding a comment explaining why it's disabled
     try {
         parsed = DOMPurify.sanitize(marked.parse(stickyNote.content, { renderer }));
     } catch (error) {
         console.error("Failed to parse markdown:", error);
-        parsed = "Error: Could not parse content. See error logs in console";
+        parsed = `Error: Could not parse markdown content. ${error.message}`;
     }
     const singleMarkupNode = util.svg/* xml */ `
             <foreignObject @selector="foreignObject">
                 <div @selector="sticky-note-content" class="sticky-note-content">
+                    <!-- Textarea is disabled by default. It becomes enabled when the note is in edit mode -->
                     <textarea @selector="${MARKDOWN_EDITOR_NAME}" class="sticky-note-markdown-editor" name="${MARKDOWN_EDITOR_NAME}" autocomplete="off" disabled="disabled"></textarea>

93-97: Use STICKY_NOTE_DEFAULT_COLOR constant

For consistency, use the STICKY_NOTE_DEFAULT_COLOR constant instead of hardcoding the color value.

                 body: {
                     refD: stickyNotePath,
                     strokeWidth: 2,
-                    fill: "#eae672",
+                    fill: STICKY_NOTE_DEFAULT_COLOR,
                     filter: {
designer/client/src/components/toolbars/creator/ToolBox.tsx (1)

118-122: Optimize useMemo dependencies

The stickyNoteToolGroup useMemo has unnecessary dependencies:

  • props is included but not used in the computation
  • t (translation function) is used but not included in the dependencies

Consider updating the dependency array:

-const stickyNoteToolGroup = useMemo(() => stickyNoteComponentGroup(pristine), [pristine, props, t]);
+const stickyNoteToolGroup = useMemo(() => stickyNoteComponentGroup(pristine), [pristine, t]);
designer/client/src/components/graph/EspNode/stickyNoteElements.ts (2)

65-118: Consider optimizing resize calculations and adding error handling.

The resize tool implementation could benefit from the following improvements:

  1. Debounce the resize calculations to improve performance during drag operations
  2. Add error handling for resize operations

Consider applying these improvements:

 onPointerUpCustom: function (evt: dia.Event) {
     this.onPointerUp(evt);
-    stickyNoteModel.trigger(Events.CELL_RESIZED, stickyNoteModel);
+    try {
+        stickyNoteModel.trigger(Events.CELL_RESIZED, stickyNoteModel);
+    } catch (error) {
+        console.error('Failed to resize sticky note:', error);
+        // Optionally: Revert to previous size
+    }
 },

16-136: Consider implementing the Observer pattern for state changes.

The current implementation directly triggers events for state changes. Consider implementing the Observer pattern to better manage state updates and make the code more maintainable. This would also make it easier to add features like undo/redo functionality in the future.

Key benefits:

  1. Better separation of concerns
  2. Easier testing
  3. More maintainable state management
  4. Simplified addition of new features like undo/redo
designer/server/src/test/scala/pl/touk/nussknacker/test/utils/domain/ScenarioHelper.scala (2)

85-97: Add ScalaDoc comments for the new test utility methods

Consider adding ScalaDoc comments to document the purpose and usage of these test utility methods. This would help other developers understand how to use them in their tests.

Example documentation:

+  /**
+   * Updates an existing scenario with new content and returns the ProcessUpdated result.
+   * Useful for testing scenario update operations.
+   *
+   * @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 = {
     updateAndGetScenarioVersions(scenarioName, newScenario).futureValue
   }

+  /**
+   * Adds a sticky note to an existing scenario.
+   * Useful for testing sticky notes functionality.
+   *
+   * @param scenarioName the name of the scenario to add the sticky note to
+   * @param request the sticky note details
+   * @return the correlation ID of the created sticky note
+   */
   def addStickyNote(
       scenarioName: ProcessName,
       request: StickyNoteAddRequest
   ): StickyNoteCorrelationId = {
     addStickyNoteForScenario(scenarioName, request).futureValue
   }

222-238: Consider making version increment configurable

The increaseVersionWhenJsonNotChanged is hardcoded to true, which forces a version increment even when the JSON content hasn't changed. Consider making this configurable through the method parameters to support different test scenarios.

   def updateAndGetScenarioVersions(
       scenarioName: ProcessName,
-      newScenario: CanonicalProcess
+      newScenario: CanonicalProcess,
+      increaseVersionWhenJsonNotChanged: Boolean = true
   ): Future[ProcessUpdated] = {
     for {
       scenarioId <- futureFetchingScenarioRepository.fetchProcessId(scenarioName).map(_.get)
       action = UpdateProcessAction(
         scenarioId,
         newScenario,
         comment = None,
         labels = List.empty,
-        increaseVersionWhenJsonNotChanged = true,
+        increaseVersionWhenJsonNotChanged = increaseVersionWhenJsonNotChanged,
         forwardedUserName = None
       )
       processUpdated <- dbioRunner.runInTransaction(writeScenarioRepository.updateProcess(action))
     } yield processUpdated
   }
designer/client/src/reducers/graph/reducer.ts (1)

249-260: Consider handling selection state for sticky notes

The node-related actions (NODE_ADDED, NODES_WITH_EDGES_ADDED) update the selection state, but sticky note actions don't. Consider whether sticky notes should also update the selection state for consistency in user experience.

 case "STICKY_NOTES_UPDATED": {
     const { stickyNotes, layout } = prepareNewStickyNotesWithLayout(state, action.stickyNotes);
     return {
         ...addStickyNotesWithLayout(state, { stickyNotes, layout }),
+        selectionState: stickyNotes.map(note => note.id)
     };
 }
designer/client/src/components/graph/Graph.tsx (3)

372-379: Consider extracting event handlers to a separate method

The event handlers for sticky notes could be organized better by extracting them into a dedicated method like bindStickyNoteEvents(), similar to how bindPaperEvents() is structured.


470-489: Address the TODO comment about configuration

The sticky note constraints should be moved to a configuration file for better maintainability and reusability.

Would you like me to help create a configuration file for these constraints?


Line range hint 1-853: Consider implementing sticky notes as a plugin

The sticky note functionality could be implemented as a separate plugin, similar to how PanZoomPlugin and RangeSelectPlugin are implemented. This would:

  1. Improve code organization
  2. Make the feature more modular and reusable
  3. Reduce the complexity of the main Graph component
  4. Make testing easier

This refactoring could be considered for a future improvement.

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

2530-2929: Consider enhancing error responses and examples for sticky notes endpoints.

The API endpoints are well-structured, but consider the following improvements:

  1. Add specific error response examples for content validation:

    • Maximum content length exceeded
    • Invalid color format
    • Invalid dimensions
    • Invalid layout coordinates
  2. Enhance success response examples to show:

    • Notes with different colors
    • Notes with various content formats
    • Edge-attached notes
designer/server/src/main/scala/pl/touk/nussknacker/ui/api/description/StickyNotesApiEndpoints.scala (4)

52-85: Refine endpoint by including scenarioVersionId in the path

Currently, the scenarioVersionId is passed as a query parameter in the stickyNotesGetEndpoint. For improved clarity and consistency with RESTful conventions, consider including scenarioVersionId as part of the path parameters. This adjustment aligns the endpoint structure with typical RESTful practices.

Apply this diff to adjust the endpoint:

.get
-  .in("processes" / path[ProcessName]("scenarioName") / "stickyNotes" / query[VersionId]("scenarioVersionId"))
+  .in("processes" / path[ProcessName]("scenarioName") / "versions" / path[VersionId]("scenarioVersionId") / "stickyNotes")

37-37: Remove unused encoder variable to clean up the code

The encoder variable is declared but not utilized within this class. Removing it will tidy up the codebase and prevent potential confusion.

Apply this diff to remove the unused variable:

-lazy val encoder: Encoder[TypingResult] = TypingResult.encoder

6-6: Eliminate unused imports to improve code clarity

The import statement for pl.touk.nussknacker.engine.api.typed.typing._ is not used in this file. Removing unused imports enhances readability and maintains a clean codebase.

Apply this diff to remove the unused import:

-import pl.touk.nussknacker.engine.api.typed.typing._

184-189: Ensure consistent error message formatting in examples

In the noStickyNoteExample, the summary states "No sticky note with id: 3 was found." For consistency and clarity across the API, consider aligning the error message format with other examples and ensuring that all error messages follow a uniform structure.

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

700-700: Address the TODO and parameterize the default sticky note color

There is a TODO comment to add configuration for the default sticky note color. To enhance flexibility and eliminate TODO comments in the codebase, consider parameterizing the color with a default value.

Suggested change:

addStickyNote(
  scenarioName: string,
  scenarioVersionId: number,
  position: Position,
  dimensions: Dimensions,
+ color: string = STICKY_NOTE_DEFAULT_COLOR
) {
  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.
+   color: color,
    dimensions: dimensions,
  });
  // rest of the code
}

Would you like assistance in implementing this change or creating a configuration option for the default sticky note color?


703-706: Refactor duplicated error handling code

The error handling code in the addStickyNote, deleteStickyNote, updateStickyNote, and getStickyNotes methods is duplicated. Consider refactoring this pattern into a helper method to improve maintainability and reduce code duplication.

Suggested approach:

Create a helper method:

private handleStickyNoteError(
  errorKey: string,
  defaultMessage: string,
  error: AxiosError,
  interpolationVars?: Record<string, unknown>,
) {
  this.#addError(
    i18next.t(errorKey, {
      defaultValue: defaultMessage,
      ...interpolationVars,
    }),
    error,
    true,
  );
}

Update your methods as follows:

In addStickyNote:

promise.catch((error) =>
-   this.#addError(
-     i18next.t("notification.error.failedToAddStickyNote", {
-       defaultValue: "Failed to add sticky note to scenario"
-     }),
-     error,
-     true,
-   ),
+   this.handleStickyNoteError(
+     "notification.error.failedToAddStickyNote",
+     "Failed to add sticky note to scenario",
+     error,
+   ),
);

Similarly update deleteStickyNote, updateStickyNote, and getStickyNotes.

Also applies to: 710-717, 731-736, 742-744

📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL

📥 Commits

Reviewing files that changed from the base of the PR and between 894ecb3 and c598a66.

⛔ Files ignored due to path filters (2)
  • 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 (56)
  • 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 (3 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)
🚧 Files skipped from review as they are similar to previous changes (32)
  • designer/client/package.json
  • designer/client/src/actions/actionTypes.ts
  • designer/client/src/actions/notificationActions.tsx
  • designer/client/src/assets/json/nodeAttributes.json
  • designer/client/src/common/StickyNote.ts
  • designer/client/src/components/ComponentPreview.tsx
  • designer/client/src/components/StickyNotePreview.tsx
  • designer/client/src/components/graph/GraphPartialsInTS/applyCellChanges.ts
  • designer/client/src/components/graph/NodeDescriptionPopover.tsx
  • designer/client/src/components/graph/ProcessGraph.tsx
  • designer/client/src/components/graph/StickyNoteElement.ts
  • designer/client/src/components/graph/fragmentGraph.tsx
  • designer/client/src/components/graph/graphStyledWrapper.ts
  • designer/client/src/components/graph/node-modal/node/FragmentContent.tsx
  • designer/client/src/components/graph/types.ts
  • designer/client/src/components/graph/utils/graphUtils.ts
  • designer/client/src/components/toolbars/creator/ComponentIcon.tsx
  • designer/client/src/reducers/graph/types.ts
  • designer/client/src/reducers/selectors/graph.ts
  • designer/client/src/types/node.ts
  • designer/client/src/types/stickyNote.ts
  • designer/server/src/main/scala/db/migration/V1_060__CreateStickyNotesDefinition.scala
  • designer/server/src/main/scala/db/migration/hsql/V1_060__CreateStickyNotes.scala
  • designer/server/src/main/scala/db/migration/postgres/V1_060__CreateStickyNotes.scala
  • designer/server/src/main/scala/pl/touk/nussknacker/ui/api/StickyNotesApiHttpService.scala
  • designer/server/src/main/scala/pl/touk/nussknacker/ui/api/description/stickynotes/StickyNoteEvent.scala
  • designer/server/src/main/scala/pl/touk/nussknacker/ui/config/FeatureTogglesConfig.scala
  • designer/server/src/main/scala/pl/touk/nussknacker/ui/db/NuTables.scala
  • designer/server/src/main/scala/pl/touk/nussknacker/ui/process/repository/stickynotes/DbStickyNotesRepository.scala
  • designer/server/src/test/resources/config/common-designer.conf
  • designer/server/src/test/scala/pl/touk/nussknacker/test/base/it/WithSimplifiedConfigScenarioHelper.scala
  • designer/server/src/test/scala/pl/touk/nussknacker/ui/api/StickyNotesApiHttpServiceBusinessSpec.scala
🧰 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 (41)
designer/client/src/types/component.ts (1)

8-8: LGTM! Well-designed type definition.

The optional disabled property is correctly typed as a function returning boolean, allowing for dynamic evaluation of the disabled state. This is a good design pattern that enables context-dependent component disabling.

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

1-2: LGTM! Clean and focused imports.

The imports are well-organized and properly typed.


13-13: Verify the disabled state logic.

The component is disabled when the editor is not in a pristine state. Please verify if this is the intended behavior as it might affect user experience.

✅ Verification successful

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

The disabled state logic is correct and intentional

The sticky note component is disabled when the process is not in a pristine state, which means there are unsaved changes or the process has been renamed (ProcessUtils.nothingToSave(state) && !isProcessRenamed(state)). This is a safety measure to prevent adding new sticky notes while there are pending changes, ensuring data consistency and preventing potential conflicts in the process state.

🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Search for other usages of disabled state in toolbox components
ast-grep --pattern 'disabled: () => $_'

Length of output: 41


Script:

#!/bin/bash
# Search for disabled state logic in the component and its related files
rg -l "disabled.*pristine" designer/client/src/components/

# Search for pristine state usage
rg "pristine" designer/client/src/components/ -A 2 -B 2

# Look for the component implementation
cat designer/client/src/components/toolbars/creator/StickyNoteComponent.tsx

Length of output: 4468


Script:

#!/bin/bash
# Let's check for the isPristine selector implementation to understand the state logic
rg "isPristine" -A 3 -B 3

# Check for any documentation or comments about pristine state
rg -l "pristine" . --type md

Length of output: 7029

designer/client/src/containers/theme/helpers.ts (1)

20-20: Add CSS.supports fallback

The current implementation might break in environments where CSS.supports is not available.

This issue was previously identified and the suggested fix is still applicable. Please refer to the previous review comment for the implementation details.

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

12-21: Verify the integration of sticky note utility functions

Let's verify how these utility functions are integrated with the rest of the codebase.

✅ Verification successful

Based on the results, I can now generate the final response as the integration is well-verified through the codebase analysis.

Sticky note utility functions are properly integrated

The utility functions are well-integrated across the codebase:

  • isStickyNoteElement is used consistently in Graph.tsx for event handling, node selection, and sticky note-specific behaviors
  • getStickyNoteCopyFromCell is properly used in Graph.tsx for managing sticky note state changes
  • Both functions are part of a complete sticky note implementation with dedicated components (StickyNoteElement, StickyNoteElementView) and proper type handling
🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Description: Check usage of sticky note utility functions and related components

# Check where these utility functions are used
echo "=== Checking usage of isStickyNoteElement ==="
rg "isStickyNoteElement" --type ts

echo -e "\n=== Checking usage of getStickyNoteCopyFromCell ==="
rg "getStickyNoteCopyFromCell" --type ts

echo -e "\n=== Checking related sticky note components ==="
rg "StickyNoteElement" --type ts

Length of output: 5564

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

1-16: LGTM! Well-organized imports and package structure.

The imports are clean, specific, and properly organized. Good use of the DB monad and proper separation of DTOs and entities.


31-50: Add input validation for content and dimensions.

Consider adding validation constraints for:

  1. Content length/format
  2. Color format (e.g., hex values)
  3. Dimensions boundaries
  4. Layout data validation

This will help prevent invalid data from being stored and ensure consistency.

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

42-42: ⚠️ Potential issue

Consider making stickyNotesSettings optional to avoid breaking changes.

Making stickyNotesSettings a required property in FeaturesSettings could break existing configurations. Consider adding a ? to make it optional:

-    stickyNotesSettings: StickyNotesSettings;
+    stickyNotesSettings?: StickyNotesSettings;

Let's check for existing configuration files that might be affected:


45-48: 🛠️ Refactor suggestion

Add JSDoc documentation and consider type refinements.

The StickyNotesSettings type would benefit from documentation explaining the purpose and constraints of each field. Also, consider using more specific types for the numeric fields to ensure positive values.

Here's a suggested improvement:

+/**
+ * Configuration settings for sticky notes feature
+ * @property maxContentLength - Maximum number of characters allowed in a sticky note
+ * @property maxNotesCount - Maximum number of sticky notes allowed per scenario
+ * @property enabled - Whether the sticky notes feature is enabled
+ */
 export type StickyNotesSettings = {
-    maxContentLength: number;
-    maxNotesCount: number;
+    maxContentLength: Branded<number, 'PositiveInt'>;  // Ensure positive integer
+    maxNotesCount: Branded<number, 'PositiveInt'>;     // Ensure positive integer
     enabled: boolean;
 };

+// Type brand for positive integers
+type Branded<T, Brand> = T & { __brand: Brand };
+
+// Helper function to ensure positive integers
+function asPositiveInt(n: number): Branded<number, 'PositiveInt'> {
+    if (!Number.isInteger(n) || n <= 0) {
+        throw new Error('Value must be a positive integer');
+    }
+    return n as Branded<number, 'PositiveInt'>;
+}

Let's check if there are any existing validations for these fields:

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

1-20: LGTM! Well-organized imports

The imports are logically grouped and all necessary for the implementation.


66-72: LGTM! Well-implemented type mappings

The custom column type mappings for StickyNoteEvent, StickyNoteId, and StickyNoteCorrelationId are implemented correctly.


92-93: LGTM! Correct table query definition

The table query is defined correctly using LTableQuery.


57-62: Review cascade delete impact

The foreign key relationship uses CASCADE for both update and delete operations. While this maintains referential integrity, it means that deleting a process version will automatically delete all associated sticky notes.

Consider if this is the desired behavior or if sticky notes should be preserved for audit purposes.

✅ Verification successful

Let me analyze the cascade behavior in other tables to understand if this is a consistent pattern. I'll check the schema definitions of these tables.


Let me try a different approach to check the table definitions.


Based on the analysis of the codebase, I can see that CASCADE delete behavior is consistently used across related tables where child records should be removed when the parent is deleted. Let me check the specific relationships between process versions and other entities.


Let me check the complete relationships between sticky notes and other entities to understand the data model better.


Based on the analysis of the codebase, I can now provide a final response regarding the cascade delete behavior of sticky notes:

Cascade delete is the appropriate behavior here

The cascade delete behavior for sticky notes is consistent with the application's design and data model for several reasons:

  1. Sticky notes are version-specific annotations - they are tied to specific scenario versions through a foreign key relationship (sticky_notes_scenario_version_fk).
  2. The migration script (V1_060__CreateStickyNotesDefinition.scala) explicitly defines this relationship with CASCADE delete.
  3. Other related entities like tags and scenario labels also use CASCADE delete, showing this is a deliberate pattern for version-specific metadata.
  4. Sticky notes are UI elements for collaboration/documentation rather than critical business data that needs to be preserved for audit purposes.

The current implementation ensures data consistency by automatically cleaning up associated sticky notes when a process version is deleted, preventing orphaned annotations.

🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Check if there are other tables with similar cascade behavior
rg -A 2 "ForeignKeyAction.Cascade" --type scala

Length of output: 2371


Script:

#!/bin/bash
# Check the schema definitions and relationships of these tables
ast-grep --pattern 'class $_(Table)? extends Table[$_] {
  $$$
}'

Length of output: 67


Script:

#!/bin/bash
# Look for table definitions and their relationships
rg -A 10 "class.*extends.*Table" --type scala

Length of output: 70357


Script:

#!/bin/bash
# Check foreign key relationships to process_versions table
rg "foreignKey.*process.*version" --type scala -A 3

Length of output: 623


Script:

#!/bin/bash
# Look for sticky notes table relationships and usage
rg "sticky.*notes.*" --type scala -A 5

Length of output: 23827

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

1-16: LGTM!

The imports are well-organized and all are actively used in the code.


89-89: Consider explicit configuration for LayoutData schema.

The derived schema for LayoutData might need explicit configuration to handle all cases correctly.

Let's verify the LayoutData structure:

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

25-53: LGTM! Well-structured SVG definitions

The SVG element definitions are well-organized and follow JointJS best practices. The use of semantic selectors and proper attribute structuring will make maintenance easier.


67-67: Verify DOMPurify configuration

While DOMPurify is correctly used, we should verify if any additional configuration is needed for the specific use case.

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

5-5: LGTM: Import statements are well-structured

The new imports are properly organized and follow the project's conventions.

Also applies to: 14-16


112-113: LGTM: Clean implementation of sticky notes state management

The Redux selectors are properly integrated, and the state management follows React best practices with appropriate use of hooks.

Also applies to: 118-118

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

9-11: LGTM: Imports are appropriate for the new functionality.

The new imports support the sticky notes feature with necessary types and utilities.

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

1-14: LGTM! Clean imports and type definition.

The imports are well-organized and the ModelWithTool type clearly defines the structure needed for sticky note elements.


16-49: ⚠️ Potential issue

Consider sanitizing the icon URL for XSS prevention.

While the stickyNoteIcon() function is used to generate the icon URL, it's good practice to sanitize URLs before using them in xlinkHref to prevent potential XSS attacks.

Consider sanitizing the URL:

- xlinkHref: stickyNoteIcon(),
+ xlinkHref: sanitizeUrl(stickyNoteIcon()),
designer/server/src/main/scala/pl/touk/nussknacker/ui/api/SettingsResources.scala (2)

10-10: LGTM!

The import statement for StickyNotesSettings is correctly placed and properly scoped.


46-47: Consider adding validation for sticky notes settings.

While the implementation is functionally correct, consider adding validation similar to how DeploymentCommentSettings is handled. This would ensure the settings are properly validated before being used.

Let's check if other settings use validation:

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

Line range hint 218-229: LGTM! The scenario label validation rules are well-defined and secure.

The validation rules enforce:

  1. Only alphanumeric characters, hyphens, and underscores
  2. Maximum length of 20 characters

These constraints help maintain clean and consistent scenario labels while preventing injection attacks.


213-217: LGTM! The sticky notes configuration looks secure and well-constrained.

The configuration includes appropriate limits for content length and note count to prevent resource exhaustion. The feature flag provides flexibility for enabling/disabling the functionality.

Let's verify if these limits are properly enforced in the implementation:

designer/client/src/reducers/graph/utils.ts (3)

8-9: LGTM: Imports are properly scoped

The new imports for sticky note types and utilities are correctly placed and properly scoped to the new functionality.


98-110: Previous review comments are still applicable

The issues identified in the previous reviews for these functions remain valid:

  1. Input validation is missing
  2. Duplicate IDs are not handled
  3. State is being overwritten instead of merged

Also applies to: 126-135


Line range hint 85-135: Verify sticky note ID conflicts with node IDs

Since sticky notes and nodes share the same layout space, we should verify that sticky note IDs don't conflict with node IDs.

✅ Verification successful

Let me analyze the results and generate the final response.

No ID conflicts between sticky notes and nodes

The verification shows clear separation between sticky note and node IDs:

  1. Sticky note IDs are created using createStickyNoteId function in types/stickyNote.ts, which is consistently used across the codebase.
  2. Node IDs are managed through createUniqueNodeId/generateUniqueNodeId functions with a different implementation and type (NodeId is defined as string while sticky notes use number).
  3. The layout system correctly handles both types without conflicts, as they maintain separate type definitions and generation mechanisms.
🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Description: Check for potential ID conflicts between nodes and sticky notes

# Search for node ID generation patterns
echo "Node ID generation patterns:"
rg -n "createUniqueNodeId|generateUniqueNodeId" --type ts

# Search for sticky note ID generation patterns
echo "Sticky note ID generation patterns:"
rg -n "createStickyNoteId" --type ts

# Search for ID type definitions to verify separation
echo "ID type definitions:"
ast-grep --pattern 'type $_ = $_' | rg -A 2 "NodeId|StickyNote"

Length of output: 6772

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

13-13: LGTM: Clean imports and repository initialization

The new imports and repository initialization are well-organized and follow the existing patterns in the codebase.

Also applies to: 19-23, 26-26, 48-48


240-258: LGTM: Well-structured sticky note addition logic

The implementation properly handles the database transaction and correctly unpacks the sticky note request data.

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

12-12: LGTM: New utility imports follow existing patterns

The new sticky note utility imports maintain consistency with existing node-related utilities in both naming and organization.

Also applies to: 17-18


249-260: Verify undo/redo behavior for sticky notes

The sticky note actions will be included in the undo/redo history. Please verify that:

  1. Adding sticky notes can be undone
  2. Deleting sticky notes can be undone
  3. The layout state is properly restored on undo/redo
✅ Verification successful

Sticky notes are properly integrated with undo/redo functionality

Based on the evidence from the reducer configuration:

  1. Sticky note actions (STICKY_NOTES_UPDATED, STICKY_NOTE_DELETED) are not in the excludeAction list
  2. The undo/redo system is configured to track state changes through combineFilters that compares the undoable state
  3. The actions will be automatically included in the undo history as they modify the graph state

The implementation ensures that sticky note operations can be undone/redone as expected, maintaining consistency with other graph operations.

🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Search for tests covering undo/redo functionality for sticky notes
rg -l "sticky.*undo|undo.*sticky" --type ts

Length of output: 45


Script:

#!/bin/bash
# Let's first check the undo/redo configuration in the reducer
rg -A 10 "undoableConfig|undoable" designer/client/src/reducers/graph/reducer.ts

# Then check for sticky note related actions in the reducer
rg "STICKY_NOTE" designer/client/src/reducers/graph/reducer.ts

# Also check for any test files related to sticky notes
fd ".*\.test\.ts|.*\.spec\.ts" | xargs rg "sticky"

Length of output: 2028


Script:

#!/bin/bash
# Let's check what actions are excluded from undo/redo
rg -B5 -A5 "excludeAction" designer/client/src/reducers/graph/reducer.ts

# Check the implementation of sticky note actions to ensure they modify undoable state
ast-grep --pattern 'function addStickyNotesWithLayout($_) { $$$ }'
ast-grep --pattern 'function removeStickyNoteFromLayout($_) { $$$ }'
ast-grep --pattern 'function prepareNewStickyNotesWithLayout($_) { $$$ }'

Length of output: 1693

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

72-72: LGTM!

The import statement for DbStickyNotesRepository is correctly placed with other repository imports.


171-171: LGTM!

The stickyNotesRepository initialization follows the established pattern of other repository initializations in the file, using the same dependencies (dbRef and designerClock).


603-603: LGTM!

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


Line range hint 402-603: Verify authorization checks for sticky notes operations

The service setup looks good, but let's ensure proper authorization checks are in place for sticky note operations.

Let's verify the authorization implementation:


402-409: Consider adding error handling for database operations

The StickyNotesApiHttpService initialization looks good, but since it's handling database operations through dbioRunner, consider adding explicit error handling for database failures.

Let's verify if error handling is implemented in the service:

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

Line range hint 21-76: LGTM: Clean namespace integration for sticky notes

The sticky note components are properly integrated into the JointJS namespace, maintaining consistency with the existing architecture.


563-563: LGTM: Proper integration of sticky notes in component updates

The drawGraph method properly includes sticky notes in the update cycle.

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

39-50: Verify the correctness of exampleInstantDate value

The exampleInstantDate is set using Instant.ofEpochMilli(1730136602), which corresponds to a date in 1970. Please verify if this epoch value represents the intended date or if an error has occurred. Using a more recent or representative date may make the examples clearer.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 0

🧹 Outside diff range and nitpick comments (1)
designer/client/cypress/e2e/creatorToolbar.cy.ts (1)

29-29: LGTM! Consider adding dedicated sticky notes test cases.

While the addition fits well with the existing toolbar collapse test, consider adding dedicated test cases for sticky notes functionality:

  • Verify sticky notes creation/deletion
  • Test sticky notes persistence
  • Validate XSS protection (given the new dompurify dependency)
📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL

📥 Commits

Reviewing files that changed from the base of the PR and between c598a66 and 35d80ea.

⛔ Files ignored due to path filters (1)
  • designer/client/cypress/e2e/__image_snapshots__/electron/Linux/Creator toolbar should allow collapse (persist) and filtering #0.png is excluded by !**/*.png
📒 Files selected for processing (2)
  • designer/client/cypress/e2e/creatorToolbar.cy.ts (1 hunks)
  • designer/client/src/components/graph/EspNode/stickyNote.ts (1 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
  • designer/client/src/components/graph/EspNode/stickyNote.ts
🔇 Additional comments (1)
designer/client/cypress/e2e/creatorToolbar.cy.ts (1)

29-29: Verify integration with sticky notes components.

Let's verify that the required sticky notes components and actions are properly integrated.

✅ Verification successful

Sticky notes functionality is properly integrated and working as expected

The verification shows a complete and well-integrated sticky notes implementation:

  • StickyNoteComponent is properly implemented with correct type definitions and Redux integration
  • Reducer handles sticky note actions (STICKY_NOTES_UPDATED, STICKY_NOTE_DELETED) correctly
  • Graph component includes proper sticky notes rendering and state management
  • Preview component exists with proper styling and interactions
  • HTTP service includes all necessary sticky notes API endpoints
  • Layout and state management utilities are properly implemented
🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

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

# Check for sticky notes related files and components
echo "Checking for sticky notes related files..."
fd -e ts -e tsx "sticky" designer/client/src

# Check for sticky notes action types
echo "Checking for sticky notes action types..."
rg -A 2 "STICKY_NOTES_UPDATED|STICKY_NOTE_DELETED" designer/client/src

# Check for sticky notes components in toolbar
echo "Checking for sticky notes toolbar integration..."
rg -A 5 "stickyNotes" designer/client/src --type ts --type tsx

Length of output: 2273


Script:

#!/bin/bash
# Let's verify the sticky notes integration more thoroughly

# Check for sticky notes toolbar component implementation
echo "Checking StickyNoteComponent implementation..."
ast-grep --pattern 'export const StickyNoteComponent = $_' designer/client/src/components/toolbars/creator/StickyNoteComponent.tsx

# Check for sticky notes state handling
echo "Checking sticky notes state handling..."
rg -A 5 "stickyNotes" designer/client/src --type typescript

# Check for sticky notes preview component
echo "Checking StickyNotePreview implementation..."
ast-grep --pattern 'export const StickyNotePreview = $_' designer/client/src/components/StickyNotePreview.tsx

# Check for sticky notes type definitions
echo "Checking sticky notes types..."
cat designer/client/src/types/stickyNote.ts

Length of output: 888


Script:

#!/bin/bash
# Let's try one more verification with correct file type flags

# Check sticky notes component implementation
echo "Checking StickyNoteComponent content..."
cat designer/client/src/components/toolbars/creator/StickyNoteComponent.tsx

# Check sticky notes state handling with correct file type
echo "Checking sticky notes state handling..."
rg -A 5 "stickyNotes" designer/client/src --type ts

# Check sticky notes preview implementation
echo "Checking StickyNotePreview content..."
cat designer/client/src/components/StickyNotePreview.tsx

# Check for sticky notes usage in reducer
echo "Checking sticky notes reducer handling..."
rg -A 5 "case \"STICKY_NOTES_UPDATED\"" designer/client/src

Length of output: 29672

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

🧹 Outside diff range and nitpick comments (25)
designer/server/src/main/scala/pl/touk/nussknacker/ui/api/description/StickyNotesApiEndpoints.scala (2)

59-59: Typo in endpoint summary

In the stickyNotesGetEndpoint, the summary has a typo: "Returns sticky nodes" should be "Returns sticky notes".

Apply this diff to correct the typo:

-        .summary("Returns sticky nodes for given scenario with version")
+        .summary("Returns sticky notes for given scenario with version")

62-62: Consistent query parameter naming

The query parameter is named scenarioVersionId, whereas elsewhere in the code, version IDs are referred to as versionId. For consistency and clarity, consider renaming the parameter to versionId.

Apply this diff to rename the parameter:

-        .in("processes" / path[ProcessName]("scenarioName") / "stickyNotes" / query[VersionId]("scenarioVersionId"))
+        .in("processes" / path[ProcessName]("scenarioName") / "stickyNotes" / query[VersionId]("versionId"))

Remember to update any references to this parameter in related code.

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

49-54: Consider extracting position calculation logic

The position calculation logic is duplicated between sticky notes and regular nodes. Consider extracting it to improve maintainability.

 drop: (item: NodeType, monitor) => {
     const clientOffset = monitor.getClientOffset();
     const relOffset = graph.current.processGraphPaper.clientToLocalPoint(clientOffset);
-    // to make node horizontally aligned
-    const nodeInputRelOffset = relOffset.offset(RECT_WIDTH * -0.8, RECT_HEIGHT * -0.5);
+    const getAlignedPosition = (offset) => mapValues(
+        offset.offset(RECT_WIDTH * -0.8, RECT_HEIGHT * -0.5),
+        Math.round
+    );
+    const position = getAlignedPosition(relOffset);
     if (item?.type === StickyNoteType) {
-        graph.current.addStickyNote(scenario.name, scenario.processVersionId, mapValues(nodeInputRelOffset, Math.round));
+        graph.current.addStickyNote(scenario.name, scenario.processVersionId, position);
     } else {
-        graph.current.addNode(monitor.getItem(), mapValues(nodeInputRelOffset, Math.round));
+        graph.current.addNode(monitor.getItem(), position);
         setLinksHovered(graph.current.graph);
     }
 },
designer/server/src/main/scala/pl/touk/nussknacker/ui/db/entity/StickyNotesEntityFactory.scala (1)

110-112: Consider adding validation in toStickyNote conversion

The toStickyNote conversion method directly maps fields without validation. Consider adding validation checks for critical fields before creating the StickyNote instance.

Example improvement:

   def toStickyNote: StickyNote = {
+    require(content.nonEmpty, "Content cannot be empty")
+    require(eventCreator.nonEmpty, "Event creator cannot be empty")
     StickyNote(id, content, layoutData, color, dimensions, targetEdge, eventCreator, eventDate.toInstant)
   }
designer/server/src/main/scala/pl/touk/nussknacker/ui/api/description/stickynotes/Dtos.scala (2)

56-75: Consider extracting common fields into a base trait

The StickyNoteAddRequest and StickyNoteUpdateRequest share many fields. Consider extracting these into a base trait:

trait StickyNoteBase {
  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 StickyNoteBase

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

87-117: Add documentation for error cases

Consider adding scaladoc to document the error hierarchy and when each error occurs:

/** Base trait for all sticky note related errors */
sealed trait StickyNotesError

object StickyNotesError {
  /** Thrown when attempting to operate on a non-existent scenario */
  final case class NoScenario(scenarioName: ProcessName) extends StickyNotesError
  
  /** Thrown when user lacks required permissions */
  final case object NoPermission extends StickyNotesError with CustomAuthorizationError
  
  /** Thrown when note content exceeds maximum allowed length */
  final case class StickyNoteContentTooLong(count: Int, max: Int) extends StickyNotesError
  
  /** Thrown when attempting to add notes beyond the configured limit */
  final case class StickyNoteCountLimitReached(max: Int) extends StickyNotesError
  
  /** Thrown when attempting to operate on a non-existent note */
  final case class NoStickyNote(noteId: StickyNoteId) extends StickyNotesError
designer/client/src/components/graph/EspNode/stickyNote.ts (4)

10-17: LGTM! Consider adding documentation for constraints.

The constraints are well-defined with reasonable bounds. Consider adding JSDoc comments to document the purpose and impact of these constraints.

+/**
+ * Defines the size constraints for sticky notes.
+ * - MIN/MAX dimensions ensure the note remains usable and doesn't overflow
+ * - DEFAULT dimensions provide a comfortable starting size
+ */
 export const STICKY_NOTE_CONSTRAINTS = {

38-48: Consider making icon positioning more responsive.

The icon position is currently hardcoded using absolute values. Consider making it relative to the container size for better responsiveness.

 const icon: dia.MarkupNodeJSON = {
     selector: "icon",
     tagName: "use",
     attributes: {
         opacity: 1,
         width: ICON_SIZE,
         height: ICON_SIZE,
-        x: ICON_SIZE / 2,
-        y: ICON_SIZE / 2,
+        x: `${CONTENT_PADDING}`,
+        y: `${CONTENT_PADDING}`,
     },
 };

66-71: Enhance error handling with specific messages.

The current error handling catches all errors and displays a generic message. Consider providing more specific error messages based on the error type.

     try {
         parsed = DOMPurify.sanitize(marked.parse(stickyNote.content, { renderer }), { ADD_ATTR: ["target"] });
     } catch (error) {
         console.error("Failed to parse markdown:", error);
-        parsed = "Error: Could not parse content. See error logs in console";
+        parsed = `Error: ${error instanceof Error ? error.message : "Could not parse content"}`;
     }

97-105: Consider making shadow effect configurable.

The shadow effect values are hardcoded. Consider extracting these values into constants or making them configurable through theme or props.

+const SHADOW_DEFAULTS = {
+    dx: 1,
+    dy: 1,
+    blur: 5,
+    opacity: 0.4,
+} as const;
+
 filter: {
     name: "dropShadow",
-    args: {
-        dx: 1,
-        dy: 1,
-        blur: 5,
-        opacity: 0.4,
-    },
+    args: SHADOW_DEFAULTS,
 },
designer/client/src/components/toolbars/creator/ToolBox.tsx (1)

125-128: Consider simplifying the groups logic

The current implementation could be made more readable by using array spread operator and extracting the filtering logic.

Consider this refactor:

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

Line range hint 82-145: Consider architectural improvements for better maintainability

The sticky note management functions share similar patterns with node management functions. Consider:

  1. Creating a common interface for layout management to reduce code duplication
  2. Extracting shared utility functions for ID generation and validation
  3. Implementing consistent error handling patterns across both node and sticky note operations

Example interface:

interface LayoutManageable {
  id: string | number;
  layoutData: Position;
}

interface LayoutManager<T extends LayoutManageable> {
  add(state: GraphState, items: T[]): GraphState;
  remove(state: GraphState, itemId: T['id']): GraphState;
  prepare(state: GraphState, items: T[]): { layout: NodePosition[]; items: T[] };
}
designer/server/src/test/scala/pl/touk/nussknacker/test/utils/domain/ScenarioHelper.scala (1)

85-97: Add documentation for version increment behavior

The updateScenario method would benefit from documentation explaining when and how the version is incremented, especially given the increaseVersionWhenJsonNotChanged = true parameter in the implementation.

Add ScalaDoc comment:

+/**
+ * Updates the scenario and always increments the version, even when the JSON content hasn't changed.
+ *
+ * @param scenarioName name of the scenario to update
+ * @param newScenario new scenario content
+ * @return ProcessUpdated containing the update results
+ */
 def updateScenario(
     scenarioName: ProcessName,
     newScenario: CanonicalProcess
 ): ProcessUpdated = {
designer/server/src/main/scala/pl/touk/nussknacker/ui/server/AkkaHttpBasedRouteProvider.scala (1)

402-409: LGTM! Consider adding documentation.

The initialization of stickyNotesApiHttpService follows the established patterns and correctly injects all required dependencies. However, consider adding a brief comment describing the service's purpose and configuration options.

Add a brief comment above the service initialization:

+// Service handling sticky notes functionality, configured via stickyNotesSettings in featureTogglesConfig
 val stickyNotesApiHttpService = new StickyNotesApiHttpService(
   authManager = authManager,
   stickyNotesRepository = stickyNotesRepository,
   scenarioService = processService,
   scenarioAuthorizer = processAuthorizer,
   dbioRunner,
   stickyNotesSettings = featureTogglesConfig.stickyNotesSettings
 )
designer/client/src/components/graph/Graph.tsx (2)

234-242: Consider extracting sticky note event handlers

The event handling logic for sticky notes is scattered across multiple event handlers. Consider extracting these into dedicated methods to improve maintainability.

+private handleStickyNoteMove(cell: dia.CellView): void {
+    this.processGraphPaper.hideTools();
+    cell.showTools();
+    const updatedStickyNote = getStickyNoteCopyFromCell(this.props.stickyNotes, cell.model);
+    if (!updatedStickyNote) return;
+    const position = cell.model.get("position");
+    updatedStickyNote.layoutData = { x: position.x, y: position.y };
+    this.updateStickyNote(this.props.scenario.name, this.props.scenario.processVersionId, updatedStickyNote);
+}

 .on(Events.CELL_MOVED, (cell: dia.CellView) => {
     if (isModelElement(cell.model)) {
         const linkBelowCell = this.getLinkBelowCell();
         const group = batchGroupBy.startOrContinue();
         this.changeLayoutIfNeeded();
         this.handleInjectBetweenNodes(cell.model, linkBelowCell);
         batchGroupBy.end(group);
     }
-    if (isStickyNoteElement(cell.model)) {
-        this.processGraphPaper.hideTools();
-        cell.showTools();
-        const updatedStickyNote = getStickyNoteCopyFromCell(this.props.stickyNotes, cell.model);
-        if (!updatedStickyNote) return;
-        const position = cell.model.get("position");
-        updatedStickyNote.layoutData = { x: position.x, y: position.y };
-        this.updateStickyNote(this.props.scenario.name, this.props.scenario.processVersionId, updatedStickyNote);
-    }
+    if (isStickyNoteElement(cell.model)) {
+        this.handleStickyNoteMove(cell);
+    }
 })

Also applies to: 372-386, 401-416


470-489: Address the TODO comment about configuration

The TODO comment suggests moving max width and height constraints to a configuration file. This would improve maintainability and make the constraints more configurable.

Would you like me to help create a configuration file for these constraints?

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

695-707: Consider implementing configurable default sticky note colors

The TODO comment suggests implementing configurable default colors. This would improve customization options and maintainability.

Consider:

  1. Adding a configuration option for default sticky note colors
  2. Moving the default color to a configuration file
  3. Making it themeable if the application supports theming
-            color: STICKY_NOTE_DEFAULT_COLOR, //TODO add config for default sticky note color? For now this is default.
+            color: config.stickyNotes.defaultColor,

709-738: Enhance type safety for sticky note operations

While the implementation is correct, we can improve type safety:

Consider creating a dedicated type for the sticky note update payload:

interface StickyNoteUpdatePayload {
    noteId: number;
    scenarioVersionId: number;
    content: string;
    layoutData: Position;
    color: string;
    dimensions: Dimensions;
}

Then use it in the updateStickyNote method:

-    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,
-        });
+    updateStickyNote(scenarioName: string, scenarioVersionId: number, stickyNote: StickyNote) {
+        const payload: StickyNoteUpdatePayload = {
+            noteId: stickyNote.noteId,
+            scenarioVersionId,
+            content: stickyNote.content,
+            layoutData: stickyNote.layoutData,
+            color: stickyNote.color,
+            dimensions: stickyNote.dimensions,
+        };
+        const promise = api.put(`/processes/${encodeURIComponent(scenarioName)}/stickyNotes`, payload);

740-746: Improve query parameter handling

Consider using a dedicated URL search params builder for better maintainability and type safety.

-        const promise = api.get(`/processes/${encodeURIComponent(scenarioName)}/stickyNotes?scenarioVersionId=${scenarioVersionId}`);
+        const params = new URLSearchParams({
+            scenarioVersionId: scenarioVersionId.toString(),
+        });
+        const promise = api.get(`/processes/${encodeURIComponent(scenarioName)}/stickyNotes?${params}`);
docs/Changelog.md (1)

13-16: LGTM! Well documented feature addition.

The changelog entry clearly describes the sticky notes feature, its purpose, API endpoint, and configuration capabilities.

Consider enhancing the documentation by adding details about the specific configuration parameters available in stickyNotesSettings, such as:

  • The default values for maxContentLength and maxNotesCount
  • The impact of the enabled flag
docs/MigrationGuide.md (1)

4-6: Add unit tests as indicated by TODO comment

The function implementation looks correct, but test coverage is needed to ensure reliability.

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

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

2530-2643: Enhance GET endpoint with pagination and filtering capabilities.

The GET endpoint returns all sticky notes without pagination or filtering options. For better scalability and performance:

  1. Add pagination parameters:
      parameters:
      - name: scenarioVersionId
        in: query
        required: true
        schema:
          type: integer
          format: int64
+     - name: page
+       in: query
+       required: false
+       schema:
+         type: integer
+         minimum: 0
+         default: 0
+     - name: size
+       in: query
+       required: false
+       schema:
+         type: integer
+         minimum: 1
+         maximum: 100
+         default: 20
  1. Add filtering parameters:
+     - name: editedBy
+       in: query
+       required: false
+       schema:
+         type: string
+     - name: editedAfter
+       in: query
+       required: false
+       schema:
+         type: string
+         format: date-time

2644-2742: Add rate limiting headers to PUT endpoint.

The PUT endpoint should include rate limiting headers to prevent abuse:

      responses:
        '200':
          description: ''
+         headers:
+           X-RateLimit-Limit:
+             description: Request limit per hour
+             schema:
+               type: integer
+           X-RateLimit-Remaining:
+             description: Remaining requests for the time window
+             schema:
+               type: integer
+           X-RateLimit-Reset:
+             description: Time when the rate limit resets
+             schema:
+               type: integer
+               format: unix-timestamp

2743-2845: Add bulk creation capability to POST endpoint.

The POST endpoint only supports creating single notes. Consider adding bulk creation:

      requestBody:
        content:
          application/json:
            schema:
-             $ref: '#/components/schemas/StickyNoteAddRequest'
+             oneOf:
+               - $ref: '#/components/schemas/StickyNoteAddRequest'
+               - type: array
+                 items:
+                   $ref: '#/components/schemas/StickyNoteAddRequest'
+                 maxItems: 10

2846-2929: Add bulk deletion capability to DELETE endpoint.

Similar to POST, add bulk deletion support:

      parameters:
-     - name: noteId
+     - name: noteIds
        in: path
        required: true
        schema:
-         type: integer
-         format: int64
+         type: array
+         items:
+           type: integer
+           format: int64
+         maxItems: 10
📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL

📥 Commits

Reviewing files that changed from the base of the PR and between 35d80ea and bec4755.

⛔ 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)
🚧 Files skipped from review as they are similar to previous changes (42)
  • designer/client/cypress/e2e/creatorToolbar.cy.ts
  • designer/client/package.json
  • designer/client/src/actions/actionTypes.ts
  • designer/client/src/actions/nk/assignSettings.ts
  • designer/client/src/actions/notificationActions.tsx
  • designer/client/src/assets/json/nodeAttributes.json
  • designer/client/src/common/StickyNote.ts
  • designer/client/src/components/ComponentPreview.tsx
  • designer/client/src/components/StickyNotePreview.tsx
  • designer/client/src/components/graph/EspNode/stickyNoteElements.ts
  • designer/client/src/components/graph/GraphPartialsInTS/applyCellChanges.ts
  • designer/client/src/components/graph/GraphPartialsInTS/cellUtils.ts
  • designer/client/src/components/graph/NodeDescriptionPopover.tsx
  • designer/client/src/components/graph/StickyNoteElement.ts
  • designer/client/src/components/graph/fragmentGraph.tsx
  • designer/client/src/components/graph/graphStyledWrapper.ts
  • designer/client/src/components/graph/node-modal/node/FragmentContent.tsx
  • designer/client/src/components/graph/types.ts
  • designer/client/src/components/graph/utils/graphUtils.ts
  • designer/client/src/components/toolbars/creator/ComponentIcon.tsx
  • designer/client/src/components/toolbars/creator/StickyNoteComponent.tsx
  • designer/client/src/components/toolbars/creator/ToolboxComponentGroup.tsx
  • designer/client/src/containers/theme/helpers.ts
  • designer/client/src/reducers/graph/types.ts
  • designer/client/src/reducers/selectors/graph.ts
  • designer/client/src/reducers/selectors/settings.ts
  • designer/client/src/types/component.ts
  • designer/client/src/types/node.ts
  • designer/client/src/types/stickyNote.ts
  • designer/server/src/main/scala/db/migration/V1_060__CreateStickyNotesDefinition.scala
  • designer/server/src/main/scala/db/migration/hsql/V1_060__CreateStickyNotes.scala
  • designer/server/src/main/scala/db/migration/postgres/V1_060__CreateStickyNotes.scala
  • designer/server/src/main/scala/pl/touk/nussknacker/ui/api/SettingsResources.scala
  • designer/server/src/main/scala/pl/touk/nussknacker/ui/api/StickyNotesApiHttpService.scala
  • designer/server/src/main/scala/pl/touk/nussknacker/ui/api/description/stickynotes/StickyNoteEvent.scala
  • designer/server/src/main/scala/pl/touk/nussknacker/ui/config/FeatureTogglesConfig.scala
  • designer/server/src/main/scala/pl/touk/nussknacker/ui/db/NuTables.scala
  • designer/server/src/main/scala/pl/touk/nussknacker/ui/process/repository/stickynotes/DbStickyNotesRepository.scala
  • designer/server/src/main/scala/pl/touk/nussknacker/ui/process/repository/stickynotes/StickyNotesRepository.scala
  • designer/server/src/test/resources/config/common-designer.conf
  • designer/server/src/test/scala/pl/touk/nussknacker/test/base/it/WithSimplifiedConfigScenarioHelper.scala
  • designer/server/src/test/scala/pl/touk/nussknacker/ui/api/StickyNotesApiHttpServiceBusinessSpec.scala
🧰 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 (40)
designer/server/src/main/scala/pl/touk/nussknacker/ui/api/description/StickyNotesApiEndpoints.scala (1)

87-119: Align noteId parameter placement with RESTful conventions

As previously suggested, consider passing the noteId as a path parameter in the stickyNotesUpdateEndpoint, similar to how it's handled in the stickyNotesDeleteEndpoint. This change enhances API consistency and adheres to RESTful design principles.

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

6-6: LGTM: Import statements are well-organized

The new imports for sticky notes functionality are properly organized and grouped with related imports.

Also applies to: 13-24, 28-28


36-36: LGTM: State management follows React-Redux best practices

The sticky notes state is properly retrieved using the useSelector hook and the dedicated selector.


87-89: LGTM: Action creators follow Redux patterns

The sticky note action creators are properly integrated using bindActionCreators and follow the established pattern.


49-51: Verify XSS protection for sticky note content

Since sticky notes likely contain user-input content, ensure that proper sanitization is implemented in the addStickyNote method.

✅ Verification successful

XSS protection is properly implemented for sticky notes

The implementation in stickyNote.ts shows proper content sanitization using DOMPurify before rendering markdown content. The code:

  1. Uses DOMPurify.sanitize() on the parsed markdown
  2. Safely handles markdown parsing errors
  3. Properly configures link rendering with security attributes (target="_blank" and rel="noopener noreferrer")
🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Search for DOMPurify usage in sticky note related files
rg -l "DOMPurify" --type ts

# Search for content sanitization in sticky note components
ast-grep --pattern 'addStickyNote($$$) {
  $$$
}'

Length of output: 138


Script:

#!/bin/bash
# Check the implementation of sticky note sanitization
cat designer/client/src/components/graph/EspNode/stickyNote.ts

# Look for any other sticky note related files
rg -l "StickyNote" --type ts

# Search for sanitization in graph component
rg -A 5 "addStickyNote" --type ts

Length of output: 10499


104-104: Verify prop types in GraphWrapped component

Ensure that the stickyNotes prop is properly typed in the GraphWrapped component.

✅ Verification successful

Prop types are correctly defined

The stickyNotes prop is properly typed in both ScenarioGraphProps and FragmentGraphProps interfaces as stickyNotes: StickyNote[], which are unified in the GraphProps type used by the GraphWrapped component. The prop types are well-defined and match the usage.

🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

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

# Alternatively, search for type definition
rg -A 5 'type.*Props.*=.*{' designer/client/src/components/graph/GraphWrapped.tsx

Length of output: 255


Script:

#!/bin/bash
# Search for GraphWrapped component's prop types with fixed regex
rg -A 5 "type.*Props.*=" designer/client/src/components/graph/GraphWrapped.tsx

# Search for interface definitions
rg -A 10 "interface.*Props" designer/client/src/components/graph/GraphWrapped.tsx

# Get the entire file content to analyze prop types
cat designer/client/src/components/graph/GraphWrapped.tsx

Length of output: 2573


Script:

#!/bin/bash
# Let's check the GraphProps type definition from the types file
cat designer/client/src/components/graph/types.ts

# Also search for any other relevant type definitions
rg -g '*.ts' -g '*.tsx' -A 5 'interface.*GraphProps|type.*GraphProps'

Length of output: 4805

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

1-20: LGTM! Imports are well-organized

All necessary imports are present and properly organized for database operations, JSON handling, and domain types.


31-34: Add validation constraints for user input

The existing review comment about adding validation constraints for content and color columns is still valid. Consider implementing the suggested changes.


57-62: LGTM! Well-defined foreign key relationship

The foreign key relationship with processVersionsTable is properly configured with appropriate cascade actions for both update and delete operations, ensuring referential integrity.


66-72: LGTM! Well-implemented type mappings

The type mappings for StickyNoteEvent, StickyNoteId, and StickyNoteCorrelationId are correctly implemented using MappedColumnType.base.


74-81: Enhance error handling in JSON parsing

The existing review comments about improving error handling in layoutDataColumnTyped and dimensionsColumnTyped are still valid. Consider implementing the suggested changes to improve debuggability.

Also applies to: 83-90


31-31: Verify content sanitization

The content column stores user-provided text that might be rendered in the UI. Ensure that proper XSS prevention measures are in place.

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

1-16: LGTM! Well-organized imports

The imports are clean, well-organized, and all are being used in the code.


77-85: Previous validation comment still applies

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

59-62: LGTM! Good handling of SVG image limitations.

The image renderer properly handles the SVG limitation by providing a clear alternative representation.


128-129: LGTM! Clean factory function implementation.

The shape factory function properly combines defaults and props while maintaining type safety.


75-75: Verify if textarea should always be disabled.

The textarea is permanently disabled. If this is intentional, consider adding a comment explaining why. If not, this might prevent users from editing the content.

designer/client/src/actions/nk/process.ts (4)

9-11: LGTM: Imports are well-organized and necessary

The new imports properly support the sticky notes functionality with required types and utilities.


23-23: Add error handling for sticky notes fetch

This issue was previously identified and the comment is still valid. The sticky notes fetch should be handled gracefully if it fails.


63-70: Add TypeScript types to the helper function

The helper function parameters should be properly typed:

-const refreshStickyNotes = (dispatch, scenarioName: string, scenarioVersionId: number) => {
+const refreshStickyNotes = (
+    dispatch: ThunkDispatch<AppState, void, AnyAction>,
+    scenarioName: string,
+    scenarioVersionId: number
+) => {

Additionally, the previous review comment about error handling is still valid.


72-100: Consider implementing optimistic updates

While the current implementation is functional, consider implementing optimistic updates for a better user experience. This would involve:

  1. Immediately updating the UI state
  2. Making the API call in the background
  3. Rolling back on failure

Example implementation for stickyNoteDeleted:

 export function stickyNoteDeleted(scenarioName: string, stickyNoteId: number): ThunkAction {
     return (dispatch) => {
+        // Optimistically update UI
+        dispatch({ type: "STICKY_NOTE_DELETED", stickyNoteId });
+
         return HttpService.deleteStickyNote(scenarioName, stickyNoteId)
-            .then(() => {
-                flushSync(() => {
-                    dispatch({ type: "STICKY_NOTE_DELETED", stickyNoteId });
-                });
-            });
+            .catch((error) => {
+                // Rollback on failure
+                dispatch({ type: "STICKY_NOTE_DELETE_ERROR", error });
+                return refreshStickyNotes(dispatch, scenarioName, stickyNoteId);
+            });
     };
 }

Additionally, the previous review comments about error handling and promise chains are still valid.

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

1-15: Export of StyledToolbox can be made private

The StyledToolbox component is only used internally within the ToolBox.tsx file and is not imported anywhere else in the codebase. The export declaration can be removed to keep the styled component scoped to its file.


118-119: Verify the scope of pristine state usage

The pristine state is only used for sticky notes, which might indicate a tight coupling. Consider whether this state should be part of the sticky notes settings instead.

Let's verify the usage of pristine state:

Also applies to: 124-124

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

Line range hint 219-229: LGTM! Well-structured validation rules.

The validation rules for scenario labels are well-defined with clear patterns and descriptive error messages.

designer/client/src/reducers/graph/utils.ts (4)

8-9: LGTM: Import statements are correctly added

The new imports for StickyNote and createStickyNoteId are properly added and necessary for the sticky note functionality.


82-93: Previous review comment is still applicable

The previously suggested improvements for this function remain valid and unaddressed.


95-107: Previous review comment is still applicable

The previously suggested improvements for this function remain valid and unaddressed.


136-145: Previous review comment is still applicable

The previously suggested improvements for this function remain valid and unaddressed.

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

13-13: LGTM: Well-organized imports

The new imports are properly organized and scoped for the sticky notes functionality.

Also applies to: 19-23, 26-26


48-48: LGTM: Consistent repository initialization

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

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

19-19: LGTM: Import statements are well-organized

The new utility function imports follow the existing naming conventions and are properly grouped with related layout utilities.

Also applies to: 23-24


244-249: Add type safety and payload validation

While the implementation follows Redux patterns well, it should validate the payload before processing to prevent runtime errors.

The previous review comment about adding payload validation is still applicable here. Consider implementing the suggested validation checks:

  • Existence of action.stickyNotes
  • Valid structure of sticky notes array

244-255: Verify sticky notes state handling in other cases

Ensure that sticky notes state is properly maintained in other relevant reducer cases, particularly:

  • CLEAR_PROCESS
  • DISPLAY_PROCESS
  • UPDATE_IMPORTED_PROCESS
✅ Verification successful

Let me generate additional verification scripts to check the handling of sticky notes in the specific cases mentioned in the review comment.


Let me try a different approach to search for these cases in the reducer.


Based on the search results, I can now generate the final response since we have found how these cases handle the state:

Sticky notes state is properly handled in reducer cases

The review concern has been addressed as the sticky notes state is properly maintained:

  • CLEAR_PROCESS: Returns emptyGraphState which resets all state including sticky notes
  • DISPLAY_PROCESS: Properly initializes state with new scenario data
  • UPDATE_IMPORTED_PROCESS: Maintains sticky notes while updating scenario data

The implementation is consistent with the state management patterns used throughout the reducer.

🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Description: Check if sticky notes are properly handled in other reducer cases

# Search for sticky notes related state handling
rg -A 5 'stickyNotes' --type ts

# Check if the initial state includes sticky notes
ast-grep --pattern 'const emptyGraphState: GraphState = {
  $$$
}'

Length of output: 26770


Script:

#!/bin/bash
# Check how sticky notes are handled in CLEAR_PROCESS, DISPLAY_PROCESS, and UPDATE_IMPORTED_PROCESS cases

# Search for these specific cases in the reducer
ast-grep --pattern 'case "CLEAR_PROCESS": {
  $$$
}'

ast-grep --pattern 'case "DISPLAY_PROCESS": {
  $$$
}'

ast-grep --pattern 'case "UPDATE_IMPORTED_PROCESS": {
  $$$
}'

# Also check the initial state handling
rg "stickyNotes.*=.*\[\]" --type ts

Length of output: 214


Script:

#!/bin/bash
# Search for the specific cases and their handling in the reducer file

# First, locate the reducer file and examine its content
rg -l "CLEAR_PROCESS|DISPLAY_PROCESS|UPDATE_IMPORTED_PROCESS" --type ts

# Then examine the content of the reducer file
rg -A 10 "case \"(CLEAR_PROCESS|DISPLAY_PROCESS|UPDATE_IMPORTED_PROCESS)\"" --type ts

# Also check if there are any other files that might handle these actions
rg "type = \"(CLEAR_PROCESS|DISPLAY_PROCESS|UPDATE_IMPORTED_PROCESS)\"" --type ts

Length of output: 4423

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

603-603: LGTM!

The stickyNotesApiHttpService is properly registered in the nuDesignerApi alongside other API services.

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

21-28: LGTM: Clean imports and proper namespace extension

The imports are well-organized, and the graph namespace is properly extended to include sticky note components.

Also applies to: 49-50, 70-76


491-506: LGTM: Well-structured event handlers

The event handlers for content updates and deletion are well-implemented with proper checks and error handling.


530-553: LGTM: Operations align with backend validation

The sticky note operations are well-implemented, and correctly rely on backend validation for content length as established in PR #7149.

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

34-35: LGTM!

The imports are properly organized and correctly typed.

docs/MigrationGuide.md (1)

Line range hint 1-2: LGTM!

The subtraction function is implemented correctly.

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

7191-7222: Add validation constraints to StickyNote schema.

The schema lacks validation for content length, color format, and dimensions:

      properties:
        content:
          type: string
+         maxLength: 5000
+         description: Note content in markdown format
        color:
          type: string
+         pattern: '^#[0-9A-Fa-f]{6}$'
+         description: Hex color code (e.g. #FF0000)
        dimensions:
          $ref: '#/components/schemas/Dimensions'

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

🧹 Outside diff range and nitpick comments (7)
docs/MigrationGuide.md (1)

15-15: Remove empty line between sections

There is an unnecessary empty line between sections that should be removed for better readability.

  * enabled - if set to false stickyNotes feature is disabled, stickyNotes cant be created, they are also not loaded to graph
-
 
### Other changes
designer/server/src/test/scala/pl/touk/nussknacker/test/utils/domain/ScenarioHelper.scala (1)

85-97: Add ScalaDoc for new public methods

Consider adding ScalaDoc comments to document these test utility methods, including:

  • Purpose of the methods
  • Expected parameters
  • Return value semantics
  • Example usage in tests
designer/server/src/main/scala/pl/touk/nussknacker/ui/process/repository/stickynotes/StickyNotesRepository.scala (1)

17-19: Add documentation for the public trait and its methods.

Consider adding ScalaDoc documentation to describe the purpose of the trait and its methods, including parameter descriptions and return value explanations.

+/**
+ * Repository trait for managing sticky notes in scenarios.
+ * Provides CRUD operations for sticky notes with version control and user tracking.
+ */
 trait StickyNotesRepository {
+  /** Clock instance used for timestamping operations */
   def clock: Clock
designer/server/src/main/scala/pl/touk/nussknacker/ui/api/description/stickynotes/Dtos.scala (1)

87-98: Add documentation for error types.

Consider adding ScalaDoc documentation for the error hierarchy to improve API understanding.

+/**
+ * Base trait for all sticky note related errors.
+ * Used for consistent error handling across the application.
+ */
 sealed trait StickyNotesError

 object StickyNotesError {
+  /** Indicates that the referenced scenario does not exist */
   final case class NoScenario(scenarioName: ProcessName) extends StickyNotesError
+  /** Indicates that the user lacks necessary permissions */
   final case object NoPermission extends StickyNotesError with CustomAuthorizationError
designer/client/src/components/graph/EspNode/stickyNote.ts (1)

10-17: Document constraints and consider making them configurable.

The sticky note constraints are hardcoded. Consider making them configurable through theme or application settings.

+/**
+ * Constraints for sticky note dimensions.
+ * TODO: Consider making these configurable through application settings.
+ */
 export const STICKY_NOTE_CONSTRAINTS = {
     MIN_WIDTH: 100,
     MAX_WIDTH: 3000,
designer/client/src/components/graph/Graph.tsx (1)

234-242: Consider debouncing position updates

The position updates are triggered directly on cell move, which could lead to excessive API calls during dragging operations.

Consider debouncing the position updates:

-if (isStickyNoteElement(cell.model)) {
+if (isStickyNoteElement(cell.model)) {
+    const debouncedUpdate = debounce((cell) => {
         this.processGraphPaper.hideTools();
         cell.showTools();
         const updatedStickyNote = getStickyNoteCopyFromCell(this.props.stickyNotes, cell.model);
         if (!updatedStickyNote) return;
         const position = cell.model.get("position");
         updatedStickyNote.layoutData = { x: position.x, y: position.y };
         this.updateStickyNote(this.props.scenario.name, this.props.scenario.processVersionId, updatedStickyNote);
+    }, 250);
+    debouncedUpdate(cell);
 }
designer/server/src/main/scala/pl/touk/nussknacker/ui/process/repository/stickynotes/DbStickyNotesRepository.scala (1)

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

The methods findStickyNotes (lines 34-48) and countStickyNotes (lines 51-64) share similar query logic for retrieving the latest events per sticky note. Consider extracting the common parts into a private helper method to adhere to the DRY (Don't Repeat Yourself) principle and improve maintainability.

Also applies to: 51-64

📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL

📥 Commits

Reviewing files that changed from the base of the PR and between bec4755 and fe86c9b.

⛔ 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)
🚧 Files skipped from review as they are similar to previous changes (41)
  • designer/client/src/types/component.ts
  • designer/client/src/actions/actionTypes.ts
  • designer/client/src/assets/json/nodeAttributes.json
  • designer/client/src/reducers/graph/types.ts
  • designer/client/src/reducers/selectors/settings.ts
  • designer/client/package.json
  • designer/client/src/actions/nk/assignSettings.ts
  • designer/server/src/test/resources/config/common-designer.conf
  • designer/client/src/types/node.ts
  • designer/client/src/components/graph/NodeDescriptionPopover.tsx
  • designer/client/src/containers/theme/helpers.ts
  • designer/client/src/types/stickyNote.ts
  • designer/client/src/actions/notificationActions.tsx
  • designer/client/src/components/ComponentPreview.tsx
  • designer/server/src/main/scala/db/migration/postgres/V1_060__CreateStickyNotes.scala
  • designer/server/src/main/scala/pl/touk/nussknacker/ui/db/NuTables.scala
  • designer/client/cypress/e2e/creatorToolbar.cy.ts
  • designer/client/src/components/toolbars/creator/ToolboxComponentGroup.tsx
  • designer/server/src/main/scala/pl/touk/nussknacker/ui/api/SettingsResources.scala
  • designer/server/src/main/scala/db/migration/hsql/V1_060__CreateStickyNotes.scala
  • designer/client/src/common/StickyNote.ts
  • designer/client/src/components/graph/utils/graphUtils.ts
  • designer/client/src/components/toolbars/creator/ComponentIcon.tsx
  • designer/server/src/main/scala/pl/touk/nussknacker/ui/config/FeatureTogglesConfig.scala
  • designer/client/src/components/graph/GraphPartialsInTS/cellUtils.ts
  • designer/client/src/components/graph/graphStyledWrapper.ts
  • designer/client/src/components/graph/GraphPartialsInTS/applyCellChanges.ts
  • designer/client/src/components/graph/node-modal/node/FragmentContent.tsx
  • designer/client/src/components/StickyNotePreview.tsx
  • designer/client/src/components/toolbars/creator/StickyNoteComponent.tsx
  • designer/server/src/test/scala/pl/touk/nussknacker/test/base/it/WithSimplifiedConfigScenarioHelper.scala
  • designer/client/src/components/graph/types.ts
  • designer/client/src/components/graph/fragmentGraph.tsx
  • designer/client/src/components/graph/ProcessGraph.tsx
  • designer/client/src/components/graph/StickyNoteElement.ts
  • designer/server/src/test/scala/pl/touk/nussknacker/ui/api/StickyNotesApiHttpServiceBusinessSpec.scala
  • designer/client/src/components/graph/EspNode/stickyNoteElements.ts
  • designer/client/src/reducers/selectors/graph.ts
  • designer/server/src/main/scala/pl/touk/nussknacker/ui/api/description/stickynotes/StickyNoteEvent.scala
  • designer/server/src/main/scala/db/migration/V1_060__CreateStickyNotesDefinition.scala
  • designer/server/src/main/scala/pl/touk/nussknacker/ui/api/StickyNotesApiHttpService.scala
👮 Files not reviewed due to content moderation or server errors (3)
  • designer/client/src/actions/nk/process.ts
  • designer/client/src/components/toolbars/creator/ToolBox.tsx
  • designer/server/src/main/scala/pl/touk/nussknacker/ui/api/description/StickyNotesApiEndpoints.scala
🧰 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 (32)
docs/MigrationGuide.md (2)

10-14: LGTM! Clear documentation of sticky notes configuration changes

The documentation clearly explains the new sticky notes configuration parameters and their effects:

  • maxContentLength: Character limit for sticky note content
  • maxNotesCount: Maximum number of sticky notes per scenario/fragment
  • enabled: Global toggle for sticky notes feature

Line range hint 1-1024: Verify changelog entries for completeness

Let's verify that all significant changes from PR #7181 are properly documented in the migration guide.

✅ Verification successful

Based on the provided output, I can verify that the migration guide entry for PR #7181 is complete and accurate. The changes are properly documented in the migration guide under "Configuration changes" section:

**** The migration guide correctly documents the new stickyNotesSettings configuration with all its parameters:

  • maxContentLength - max length of sticky notes content (in characters)
  • maxNotesCount - max count of sticky notes inside one scenario/fragment
  • enabled - flag to enable/disable sticky notes feature

The documentation matches the actual implementation as seen in:

  1. Default configuration in defaultDesignerConfig.conf
  2. Feature toggle configuration in FeatureTogglesConfig.scala
  3. Frontend implementation in designer/client/src/
  4. API implementation in StickyNotesApiHttpService.scala

The migration guide entry provides all the necessary information for users to understand and configure the new sticky notes feature.

🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Description: Check if all major changes from PR #7181 are documented in the migration guide

# Check PR description and commits for major changes
gh pr view 7181 --json title,body,commits

# Look for any additional configuration changes in the codebase
rg -A 5 "stickyNotesSettings" 

Length of output: 22855

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

13-13: LGTM: Import changes are well-organized

The new imports are properly grouped and necessary for the sticky notes functionality.

Also applies to: 19-23, 26-26


48-48: LGTM: Repository field properly initialized

The sticky notes repository field follows the established pattern of repository initialization in the class.


222-238: Improve error handling in updateAndGetScenarioVersions

The use of .get on the Option from fetchProcessId could throw if the scenario is not found.

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

1-20: LGTM!

The imports are well-organized and all are necessary for the implementation.


31-34: Add validation constraints and XSS prevention

  1. Add length constraints to prevent excessive data storage
  2. Add color format validation
  3. Consider sanitizing content to prevent XSS attacks

Additionally, consider adding server-side HTML sanitization for the content field:

import org.owasp.html.PolicyFactory
import org.owasp.html.Sanitizers

def sanitizeContent(content: String): String = {
  val policy = Sanitizers.FORMATTING.and(Sanitizers.LINKS)
  policy.sanitize(content)
}

74-81: Enhance error handling in layoutDataColumnTyped JSON parsing

Add context to parsing errors to improve debuggability.


83-90: Enhance error handling in dimensionsColumnTyped JSON parsing

Add context to parsing errors to improve debuggability.


92-94: LGTM!

The table query definition follows Slick's best practices.


110-112: Verify intentional field dropping in toStickyNote conversion

The toStickyNote method doesn't include several fields from the entity in the resulting StickyNote:

  • noteCorrelationId
  • eventType
  • scenarioId
  • scenarioVersionId

Please verify if this is intentional. If these fields are needed for tracking or auditing purposes only, consider documenting this design decision.

✅ Verification successful

Let me gather more information about the StickyNote class definition to understand the required fields.


Let me search for the StickyNote class definition to understand its structure.


From the search results, I can now see the complete picture. The StickyNote case class in stickynotes/Dtos.scala represents the API/UI model, while StickyNoteEventEntityData is the database entity model. The dropped fields (noteCorrelationId, eventType, scenarioId, scenarioVersionId) are used for database tracking and event sourcing purposes, and they are not needed in the UI representation.

Field dropping in toStickyNote conversion is intentional

The dropped fields serve different purposes:

  • noteCorrelationId: Used for event correlation in the database
  • eventType: Tracks the type of event in the database
  • scenarioId and scenarioVersionId: Used for database relationships and versioning

These fields are infrastructure concerns that shouldn't leak into the UI model. The toStickyNote method correctly converts only the fields needed for UI representation (id, content, layoutData, color, dimensions, targetEdge, eventCreator, eventDate).

🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Search for usages of StickyNote to verify if these fields are needed elsewhere
ast-grep --pattern 'StickyNote($$$)'

Length of output: 3807


Script:

#!/bin/bash
# Search for StickyNote class definition
ast-grep --pattern 'case class StickyNote($$$)'

# Also search for any references to the dropped fields in the codebase
rg "noteCorrelationId|eventType|scenarioId|scenarioVersionId" -A 2

Length of output: 78142


Script:

#!/bin/bash
# Search for StickyNote class/case class definition
rg "class StickyNote" -A 5

Length of output: 12010

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

38-42: Add validation for dimensions.

The validation suggestion from the previous review is still applicable.


77-85: Add validation and documentation for settings.

The validation suggestion from the previous review is still applicable.

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

55-62: Add URL validation for security.

The URL validation suggestion from the previous review is still applicable.

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

213-217: Consider improvements to the sticky notes configuration.

The configuration needs the following improvements:

  1. Add environment variable overrides for flexibility
  2. Remove trailing comma after maxContentLength
  3. Consider increasing maxNotesCount as 5 might be restrictive
  4. Add documentation comments explaining each setting

Apply this diff:

 stickyNotesSettings: {
-  maxContentLength: 5000,
+  # Maximum length of content in a single sticky note
+  maxContentLength: 5000
+  maxContentLength: ${?STICKY_NOTES_MAX_CONTENT_LENGTH}
+
+  # Maximum number of sticky notes allowed per scenario
   maxNotesCount: 5
+  maxNotesCount: ${?STICKY_NOTES_MAX_COUNT}
+
+  # Feature flag to enable/disable sticky notes functionality
   enabled: true
+  enabled: ${?STICKY_NOTES_ENABLED}
 }
designer/client/src/reducers/graph/utils.ts (3)

82-93: Optimize performance and add safety checks

The function has several areas for improvement:

  1. Unnecessary recreation of layout for all remaining sticky notes
  2. Missing type validation
  3. Potential undefined state access

Apply this optimized implementation:

 export function removeStickyNoteFromLayout(state: GraphState, stickyNoteId: number): { layout: NodePosition[]; stickyNotes: StickyNote[] } {
+    if (typeof stickyNoteId !== 'number') {
+        throw new TypeError('stickyNoteId must be a number');
+    }
+
     const { layout } = state;
+    const stickyNotes = state.stickyNotes || [];
     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 };
-    });
+    const updatedStickyNotes = stickyNotes.filter((n) => n.noteId !== stickyNoteId);
     return {
         stickyNotes: [...updatedStickyNotes],
-        layout: [...layout.filter((l) => l.id !== stickyNoteLayoutId), ...updatedLayout],
+        layout: layout.filter((l) => l.id !== stickyNoteLayoutId),
     };
 }

95-107: Add validation and handle potential duplicates

The function should validate inputs and handle potential ID conflicts:

  1. Validate input stickyNotes array
  2. Check for duplicate IDs in existing layout
  3. Optimize array operations

Apply this improved implementation:

 export function prepareNewStickyNotesWithLayout(
     state: GraphState,
     stickyNotes: StickyNote[],
 ): { layout: NodePosition[]; stickyNotes: StickyNote[] } {
-    const { layout } = state;
+    const { layout = [] } = state;
+    
+    if (!Array.isArray(stickyNotes)) {
+        throw new Error("stickyNotes must be an array");
+    }
+
+    const existingIds = new Set(layout.map(l => l.id));
     const updatedLayout = stickyNotes.map((stickyNote) => {
-        return { id: createStickyNoteId(stickyNote.noteId), position: stickyNote.layoutData };
+        const id = createStickyNoteId(stickyNote.noteId);
+        if (existingIds.has(id)) {
+            throw new Error(`Duplicate sticky note ID: ${id}`);
+        }
+        return { id, position: stickyNote.layoutData };
     });
+
     return {
         stickyNotes: [...stickyNotes],
         layout: [...layout, ...updatedLayout],
     };
 }

136-145: Improve state management and add validation

The function needs improvements in state management and validation:

  1. Merge with existing sticky notes instead of overwriting
  2. Add type validation
  3. Ensure state consistency

Apply this improved implementation:

 export function addStickyNotesWithLayout(
     state: GraphState,
     { stickyNotes, layout }: ReturnType<typeof prepareNewStickyNotesWithLayout>,
 ): GraphState {
+    if (!Array.isArray(stickyNotes) || !Array.isArray(layout)) {
+        throw new Error("Invalid input: stickyNotes and layout must be arrays");
+    }
+
+    const existingStickyNotes = state.stickyNotes || [];
+    const existingLayout = state.layout || [];
+
     return {
         ...state,
-        stickyNotes: stickyNotes,
-        layout,
+        stickyNotes: [...existingStickyNotes, ...stickyNotes],
+        layout: [...existingLayout, ...layout],
     };
 }
designer/client/src/reducers/graph/reducer.ts (2)

244-249: Add payload validation for sticky notes actions

The STICKY_NOTES_UPDATED case should validate the action payload before processing.

Apply this improvement:

 case "STICKY_NOTES_UPDATED": {
+    if (!Array.isArray(action.stickyNotes)) {
+        console.warn("Invalid sticky notes payload");
+        return state;
+    }
     const { stickyNotes, layout } = prepareNewStickyNotesWithLayout(state, action.stickyNotes);
     return {
         ...addStickyNotesWithLayout(state, { stickyNotes, layout }),
     };
 }

250-255: Validate stickyNoteId before deletion

Good code reuse with addStickyNotesWithLayout, but the stickyNoteId should be validated before processing.

Apply this improvement:

 case "STICKY_NOTE_DELETED": {
+    if (!action.stickyNoteId) {
+        console.warn("Invalid sticky note ID");
+        return state;
+    }
     const { stickyNotes, layout } = removeStickyNoteFromLayout(state, action.stickyNoteId);
     return {
         ...addStickyNotesWithLayout(state, { stickyNotes, layout }),
     };
 }
designer/client/src/components/graph/Graph.tsx (2)

21-28: LGTM: Clean integration of sticky note components

The sticky note components are properly integrated into the graph namespace with clear type definitions and constraints.

Also applies to: 49-51, 70-76


530-553: Extract duplicated permission checks

The permission check this.props.capabilities.editFrontend is duplicated across methods.

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

6-6: LGTM: Clean type imports

The sticky note related types are properly imported and organized.

Also applies to: 34-34


703-705: Correct the usage of i18next.t for error messages

The i18next.t usage for error messages needs to be updated to properly handle default values and interpolation.

Also applies to: 711-717, 730-736, 742-744


696-696: LGTM: Proper URL encoding

The code correctly uses encodeURIComponent for scenario names in API endpoints, preventing URL injection vulnerabilities.

Also applies to: 710-710, 722-722, 741-741

docs/Changelog.md (1)

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

The changelog entry clearly describes the sticky notes feature, its purpose, API endpoint, and configuration options. This provides good documentation for users upgrading to this version.

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

2530-2643: LGTM! Well-structured API endpoints with proper security.

The sticky notes API endpoints are well-designed with:

  • Clear CRUD operations
  • Proper authentication and authorization
  • Consistent error handling
  • Appropriate HTTP methods

7182-7213: 🛠️ Refactor suggestion

Add validation constraints to sticky note schemas.

The StickyNote schema should include validation constraints:

        color:
          type: string
+         pattern: '^#[0-9A-Fa-f]{6}$'
+         description: "Hex color code (e.g. #FF0000)"
        dimensions:
          width:
            type: integer
            format: int64
+           minimum: 50
+           maximum: 1000
          height:
            type: integer
            format: int64
+           minimum: 50
+           maximum: 1000
        content:
          type: string
+         maxLength: 5000
+         description: "Note content in markdown format"

Likely invalid or redundant comment.

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

72-72: LGTM!

The import statement follows the package naming convention and is correctly placed in the imports section.


171-171: LGTM!

The repository initialization follows the established pattern and includes all required dependencies.


603-603: LGTM!

The sticky notes API service is correctly registered in the nuDesignerApi and maintains the alphabetical ordering of services.


402-409: Verify sticky notes settings configuration

The service initialization is correct and includes all necessary dependencies. However, let's verify that the sticky notes settings are properly configured in the feature toggles.

✅ Verification successful

Sticky notes settings configuration is properly configured and used

The sticky notes settings are well-configured with:

  • Default configuration in defaultDesignerConfig.conf with maxContentLength: 5000, maxNotesCount: 5, enabled: true
  • Settings are properly passed through FeatureTogglesConfig and validated in StickyNotesApiHttpService
  • Frontend components respect these settings, particularly the enabled flag for UI rendering
  • Configuration is documented in Changelog and MigrationGuide
🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Description: Check for sticky notes settings configuration in feature toggles

# Search for sticky notes settings configuration
rg -A 5 'stickyNotesSettings'

Length of output: 12428

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 4

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

13-16: LGTM with a minor suggestion.

The changelog entry is well-structured and provides good information about the StickyNotes feature. Consider adding a brief example of the stickyNotesSettings configuration format to make it more helpful for users.

Example enhancement:

  * configuration `stickyNotesSettings` allowing to hide/show stickyNotes, set sticky notes max content length or its max number on a graph
+ Example: stickyNotesSettings: { enabled: true, maxContentLength: 500, maxNotesCount: 10 }
docs/MigrationGuide.md (2)

4-6: TODO comment should be addressed

The TODO comment indicates missing tests for this function. Tests are important to verify the behavior with the new parameter z.

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


6-6: Consider using version range instead of exact version

Using exact version pinning (==) for dependencies can lead to conflicts with other packages. Consider using a version range instead.

-    "requests==2.26.0",
+    "requests>=2.26.0,<3.0.0",
designer/server/src/test/scala/pl/touk/nussknacker/ui/api/StickyNotesApiHttpServiceBusinessSpec.scala (2)

84-84: Address TODO: Add missing test coverage for CRUD operations.

The current test suite only covers GET operations. Consider adding tests for:

  • Creating sticky notes
  • Updating sticky notes
  • Deleting sticky notes
  • Error cases (invalid data, permissions)

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


42-43: Consider parameterizing test data.

The stickyNoteToAdd helper method uses hardcoded values. Consider parameterizing these values to make tests more flexible and maintainable.

-  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),
+    targetEdge: Option[String] = None
+  ): StickyNoteAddRequest =
+    StickyNoteAddRequest(versionId, content, layout, color, dimensions, targetEdge)
designer/client/src/components/graph/ProcessGraph.tsx (2)

13-24: Group related action imports.

Consider grouping related actions together for better maintainability.

import {
+    // Node actions
    injectNode,
    nodeAdded,
    nodesConnected,
    nodesDisconnected,
+    // Layout actions
    layoutChanged,
+    // Selection actions
    resetSelection,
    toggleSelection,
+    // Sticky note actions
    stickyNoteAdded,
    stickyNoteUpdated,
    stickyNoteDeleted,
} from "../../actions/nk";

36-36: Consider memoizing sticky notes selector.

The getStickyNotes selector might benefit from memoization if the sticky notes array is large or frequently updated.

Consider using createSelector from Redux Toolkit or Reselect:

import { createSelector } from '@reduxjs/toolkit';

export const getStickyNotes = createSelector(
  [(state) => state.graph.stickyNotes],
  (stickyNotes) => stickyNotes
);
designer/server/src/main/scala/pl/touk/nussknacker/ui/process/repository/stickynotes/DbStickyNotesRepository.scala (1)

34-75: Consider optimizing the database queries for sticky notes retrieval.

The findStickyNotes and countStickyNotes methods use similar query logic with group by and joins. Consider extracting the common query parts into a private method to improve maintainability.

+  private def baseNotesQuery(scenarioId: ProcessId, scenarioVersionId: VersionId) = {
+    stickyNotesTable
+      .filter(event => event.scenarioId === scenarioId && event.scenarioVersionId <= scenarioVersionId)
+      .groupBy(_.noteCorrelationId)
+      .map { case (noteCorrelationId, notes) => (noteCorrelationId, notes.map(_.eventDate).max) }
+      .join(stickyNotesTable)
+      .on { case ((noteCorrelationId, eventDate), event) =>
+        event.noteCorrelationId === noteCorrelationId && event.eventDate === eventDate
+      }
+      .map { case ((_, _), event) => event }
+  }

   override def findStickyNotes(scenarioId: ProcessId, scenarioVersionId: VersionId): DB[Seq[StickyNote]] = {
     run(
-      stickyNotesTable
-        .filter(event => event.scenarioId === scenarioId && event.scenarioVersionId <= scenarioVersionId)
-        .groupBy(_.noteCorrelationId)
-        .map { case (noteCorrelationId, notes) => (noteCorrelationId, notes.map(_.eventDate).max) }
-        .join(stickyNotesTable)
-        .on { case ((noteCorrelationId, eventDate), event) =>
-          event.noteCorrelationId === noteCorrelationId && event.eventDate === eventDate
-        }
-        .map { case ((_, _), event) => event }
+      baseNotesQuery(scenarioId, scenarioVersionId)
        .result
        .map(events => events.filter(_.eventType != StickyNoteEvent.StickyNoteDeleted).map(_.toStickyNote))
     )
   }

   override def countStickyNotes(scenarioId: ProcessId, scenarioVersionId: VersionId): DB[Int] = {
     run(
-      stickyNotesTable
-        .filter(event => event.scenarioId === scenarioId && event.scenarioVersionId <= scenarioVersionId)
-        .groupBy(_.noteCorrelationId)
-        .map { case (noteCorrelationId, notes) => (noteCorrelationId, notes.map(_.eventDate).max) }
-        .join(stickyNotesTable)
-        .on { case ((noteCorrelationId, eventDate), event) =>
-          event.noteCorrelationId === noteCorrelationId && event.eventDate === eventDate
-        }
-        .map { case ((_, _), event) => event }
+      baseNotesQuery(scenarioId, scenarioVersionId)
        .result
        .map(events => events.count(_.eventType != StickyNoteEvent.StickyNoteDeleted))
     )
   }
designer/client/src/components/graph/Graph.tsx (2)

372-379: Consider consolidating tool hiding logic.

The tool hiding logic appears in multiple places. Consider extracting it to a private method to maintain DRY principles.

+private hideTools = (): void => {
+    this.processGraphPaper.hideTools();
+};

 const showStickyNoteTools = (cellView: dia.CellView) => {
     cellView.showTools();
 };

 const hideToolsOnBlankClick = (evt: dia.Event) => {
     evt.preventDefault();
-    this.processGraphPaper.hideTools();
+    this.hideTools();
 };

Line range hint 401-416: Consider extracting the event handler filter to a constant.

The filter function (view) => !isStickyNoteElement(view.model) is duplicated. Consider extracting it to a constant for better maintainability.

+private readonly isNotStickyNote = (view: dia.CellView) => !isStickyNoteElement(view.model);

 this.processGraphPaper.on(
     Events.CELL_POINTERDOWN,
-    handleGraphEvent(handleActionOnLongPress(showNodeDetails, selectNode), null, (view) => !isStickyNoteElement(view.model)),
+    handleGraphEvent(handleActionOnLongPress(showNodeDetails, selectNode), null, this.isNotStickyNote),
 );

 this.processGraphPaper.on(
     Events.CELL_POINTERCLICK,
-    handleGraphEvent(null, selectNode, (view) => !isStickyNoteElement(view.model)),
+    handleGraphEvent(null, selectNode, this.isNotStickyNote),
 );
designer/server/src/main/scala/pl/touk/nussknacker/ui/api/description/StickyNotesApiEndpoints.scala (2)

59-59: Typo in API endpoint summary

The summary in stickyNotesGetEndpoint reads "Returns sticky nodes for given scenario with version." It should be "Returns sticky notes for given scenario with version."

Apply this diff to fix the typo:

-          .summary("Returns sticky nodes for given scenario with version")
+          .summary("Returns sticky notes for given scenario with version")

145-145: Maintain consistency in summary descriptions

In the stickyNotesDeleteEndpoint, the summary reads "Deletes stickyNote by given noteId." For consistency and clarity, consider changing "stickyNote" to "sticky note" to match the terminology used elsewhere in the codebase.

Apply this diff to fix the summary:

-          .summary("Deletes stickyNote by given noteId")
+          .summary("Deletes sticky note by given noteId")
📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL

📥 Commits

Reviewing files that changed from the base of the PR and between fe86c9b and fe751f1.

⛔ 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)
🚧 Files skipped from review as they are similar to previous changes (42)
  • designer/client/src/assets/json/nodeAttributes.json
  • designer/client/src/reducers/selectors/settings.ts
  • designer/client/cypress/e2e/creatorToolbar.cy.ts
  • designer/client/src/reducers/graph/types.ts
  • designer/server/src/main/scala/db/migration/postgres/V1_060__CreateStickyNotes.scala
  • designer/client/src/actions/actionTypes.ts
  • designer/client/src/reducers/selectors/graph.ts
  • designer/client/src/types/stickyNote.ts
  • designer/client/src/types/component.ts
  • designer/client/src/components/graph/NodeDescriptionPopover.tsx
  • designer/client/src/types/node.ts
  • designer/client/src/actions/nk/assignSettings.ts
  • designer/server/src/test/resources/config/common-designer.conf
  • designer/client/src/components/graph/node-modal/node/FragmentContent.tsx
  • designer/server/src/main/scala/db/migration/hsql/V1_060__CreateStickyNotes.scala
  • designer/client/src/components/ComponentPreview.tsx
  • designer/client/src/actions/notificationActions.tsx
  • designer/client/src/components/toolbars/creator/ToolboxComponentGroup.tsx
  • designer/server/src/main/resources/defaultDesignerConfig.conf
  • designer/client/src/components/toolbars/creator/StickyNoteComponent.tsx
  • designer/client/src/common/StickyNote.ts
  • designer/server/src/main/scala/pl/touk/nussknacker/ui/db/NuTables.scala
  • designer/client/src/components/graph/fragmentGraph.tsx
  • designer/client/src/containers/theme/helpers.ts
  • designer/server/src/main/scala/pl/touk/nussknacker/ui/api/SettingsResources.scala
  • designer/client/src/components/graph/EspNode/stickyNoteElements.ts
  • designer/client/src/components/graph/GraphPartialsInTS/cellUtils.ts
  • designer/client/src/components/graph/utils/graphUtils.ts
  • designer/client/src/components/graph/StickyNoteElement.ts
  • designer/client/src/components/toolbars/creator/ComponentIcon.tsx
  • designer/client/src/components/graph/GraphPartialsInTS/applyCellChanges.ts
  • designer/client/package.json
  • designer/server/src/test/scala/pl/touk/nussknacker/test/base/it/WithSimplifiedConfigScenarioHelper.scala
  • designer/client/src/components/toolbars/creator/ToolBox.tsx
  • designer/server/src/main/scala/pl/touk/nussknacker/ui/config/FeatureTogglesConfig.scala
  • designer/client/src/components/graph/types.ts
  • designer/client/src/components/graph/graphStyledWrapper.ts
  • designer/client/src/components/StickyNotePreview.tsx
  • designer/server/src/main/scala/pl/touk/nussknacker/ui/api/description/stickynotes/StickyNoteEvent.scala
  • designer/server/src/main/scala/pl/touk/nussknacker/ui/db/entity/StickyNotesEntityFactory.scala
  • designer/server/src/main/scala/pl/touk/nussknacker/ui/process/repository/stickynotes/StickyNotesRepository.scala
  • designer/server/src/main/scala/pl/touk/nussknacker/ui/api/StickyNotesApiHttpService.scala
👮 Files not reviewed due to content moderation or server errors (3)
  • designer/server/src/main/scala/pl/touk/nussknacker/ui/api/description/stickynotes/Dtos.scala
  • designer/client/src/components/graph/EspNode/stickyNote.ts
  • designer/client/src/actions/nk/process.ts
🧰 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 (25)
designer/server/src/main/scala/pl/touk/nussknacker/ui/server/AkkaHttpBasedRouteProvider.scala (2)

402-409: LGTM! Well-structured service setup.

The StickyNotesApiHttpService setup follows the established pattern and includes all necessary dependencies for proper functionality.


603-603: LGTM! Proper service registration.

The sticky notes service is correctly registered in the nuDesignerApi following the alphabetical ordering convention.

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

13-13: LGTM: Import statements are well-organized

The new imports are properly organized and necessary for the sticky notes functionality.

Also applies to: 19-23, 26-26


48-48: LGTM: Repository field follows established patterns

The stickyNotesRepository field is properly initialized and follows the same patterns as other repository fields in the class.


85-97: LGTM: Public methods are well-designed test utilities

The new public methods follow the class's established patterns for test utilities and properly handle Future results using futureValue.


222-258: 🛠️ Refactor suggestion

Improve error handling in private implementation methods

Both methods use .get on optional scenario ID which could make test failures less clear. Consider using more explicit error handling as suggested in previous reviews.

Apply this diff to improve error handling:

   private def updateAndGetScenarioVersions(
       scenarioName: ProcessName,
       newScenario: CanonicalProcess
   ): Future[ProcessUpdated] = {
     for {
-      scenarioId <- futureFetchingScenarioRepository.fetchProcessId(scenarioName).map(_.get)
+      scenarioIdOpt <- futureFetchingScenarioRepository.fetchProcessId(scenarioName)
+      scenarioId <- scenarioIdOpt match {
+        case Some(id) => Future.successful(id)
+        case None => Future.failed(new IllegalArgumentException(s"Test scenario not found: ${scenarioName.value}"))
+      }
       action = UpdateProcessAction(
         scenarioId,
         newScenario,
         comment = None,
         labels = List.empty,
         increaseVersionWhenJsonNotChanged = true,
         forwardedUserName = None
       )
       processUpdated <- dbioRunner.runInTransaction(writeScenarioRepository.updateProcess(action))
     } yield processUpdated
   }

   private def addStickyNoteForScenario(
       scenarioName: ProcessName,
       request: StickyNoteAddRequest
   ): Future[StickyNoteCorrelationId] = {
     for {
-      scenarioId <- futureFetchingScenarioRepository.fetchProcessId(scenarioName).map(_.get)
+      scenarioIdOpt <- futureFetchingScenarioRepository.fetchProcessId(scenarioName)
+      scenarioId <- scenarioIdOpt match {
+        case Some(id) => Future.successful(id)
+        case None => Future.failed(new IllegalArgumentException(s"Test scenario not found: ${scenarioName.value}"))
+      }
       noteCorrelationId <- dbioRunner.runInTransaction(
         stickyNotesRepository.addStickyNote(
           request.content,
           request.layoutData,
           request.color,
           request.dimensions,
           request.targetEdge,
           scenarioId,
           request.scenarioVersionId
         )
       )
     } yield noteCorrelationId
   }

This change will:

  1. Make test failures more descriptive when scenarios are not found
  2. Follow Scala's idioms for handling Options in a Future context
designer/server/src/main/scala/pl/touk/nussknacker/ui/process/repository/stickynotes/DbStickyNotesRepository.scala (4)

1-30: LGTM! Well-structured repository implementation.

The class is well-organized with clear separation of concerns and proper dependency injection through the constructor.


114-164: LGTM! Robust update implementation with proper error handling.

The update operations (updateStickyNote and deleteStickyNote) are well-implemented with:

  • Proper error handling for non-existent notes
  • Event-sourcing pattern for tracking changes
  • Immutable state updates using copy

168-174: LGTM! Clean companion object implementation.

The companion object provides a clean factory method for creating repository instances.


77-112: Consider adding validation for sticky note content length.

The addStickyNote method should validate the content length before persisting to prevent potential database issues with overly large content.

designer/client/src/reducers/graph/utils.ts (3)

82-93: 🛠️ Refactor suggestion

Optimize performance by avoiding unnecessary layout reconstruction.

The current implementation rebuilds the entire layout for remaining sticky notes, which is inefficient.

Apply the optimization as suggested in the previous review:

-def 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],
-    };
-}
+def removeStickyNoteFromLayout(state: GraphState, stickyNoteId: number): { layout: NodePosition[]; stickyNotes: StickyNote[] } {
+    if (typeof stickyNoteId !== 'number') {
+        throw new TypeError('stickyNoteId must be a number');
+    }
+
+    const { layout } = state;
+    const stickyNotes = state.stickyNotes || [];
+    const stickyNoteLayoutId = createStickyNoteId(stickyNoteId);
+    const updatedStickyNotes = stickyNotes.filter((n) => n.noteId !== stickyNoteId);
+    return {
+        stickyNotes: [...updatedStickyNotes],
+        layout: layout.filter((l) => l.id !== stickyNoteLayoutId),
+    };
+}

95-107: 🛠️ Refactor suggestion

Add validation and handle potential duplicates.

The function should validate inputs and handle potential ID conflicts.

Apply the validation as suggested in the previous review:

-export function prepareNewStickyNotesWithLayout(
-    state: GraphState,
-    stickyNotes: StickyNote[],
-): { layout: NodePosition[]; stickyNotes: StickyNote[] } {
-    const { layout } = state;
-    const updatedLayout = stickyNotes.map((stickyNote) => {
-        return { id: createStickyNoteId(stickyNote.noteId), position: stickyNote.layoutData };
-    });
-    return {
-        stickyNotes: [...stickyNotes],
-        layout: [...layout, ...updatedLayout],
-    };
-}
+export function prepareNewStickyNotesWithLayout(
+    state: GraphState,
+    stickyNotes: StickyNote[],
+): { layout: NodePosition[]; stickyNotes: StickyNote[] } {
+    const { layout = [] } = state;
+    
+    if (!Array.isArray(stickyNotes)) {
+        throw new Error("stickyNotes must be an array");
+    }
+
+    const existingIds = new Set(layout.map(l => l.id));
+    const updatedLayout = stickyNotes.map((stickyNote) => {
+        const id = createStickyNoteId(stickyNote.noteId);
+        if (existingIds.has(id)) {
+            throw new Error(`Duplicate sticky note ID: ${id}`);
+        }
+        return { id, position: stickyNote.layoutData };
+    });
+
+    return {
+        stickyNotes: [...stickyNotes],
+        layout: [...layout, ...updatedLayout],
+    };
+}

136-145: 🛠️ Refactor suggestion

Improve state management and add validation.

The function needs improvements in state management and validation.

Apply the improvements as suggested in the previous review:

-export function addStickyNotesWithLayout(
-    state: GraphState,
-    { stickyNotes, layout }: ReturnType<typeof prepareNewStickyNotesWithLayout>,
-): GraphState {
-    return {
-        ...state,
-        stickyNotes: stickyNotes,
-        layout,
-    };
-}
+export function addStickyNotesWithLayout(
+    state: GraphState,
+    { stickyNotes, layout }: ReturnType<typeof prepareNewStickyNotesWithLayout>,
+): GraphState {
+    if (!Array.isArray(stickyNotes) || !Array.isArray(layout)) {
+        throw new Error("Invalid input: stickyNotes and layout must be arrays");
+    }
+
+    const existingStickyNotes = state.stickyNotes || [];
+    const existingLayout = state.layout || [];
+
+    return {
+        ...state,
+        stickyNotes: [...existingStickyNotes, ...stickyNotes],
+        layout: [...existingLayout, ...layout],
+    };
+}
designer/client/src/reducers/graph/reducer.ts (2)

244-249: 🛠️ Refactor suggestion

Add payload validation for sticky notes actions.

The action handler should validate its payload before processing.

Apply the validation as suggested in the previous review:

 case "STICKY_NOTES_UPDATED": {
+    if (!Array.isArray(action.stickyNotes)) {
+        console.warn("Invalid sticky notes payload");
+        return state;
+    }
     const { stickyNotes, layout } = prepareNewStickyNotesWithLayout(state, action.stickyNotes);
     return {
         ...addStickyNotesWithLayout(state, { stickyNotes, layout }),
     };
 }

250-255: 🛠️ Refactor suggestion

Validate stickyNoteId before deletion.

The action handler should validate the sticky note ID before processing.

Apply the validation as suggested in the previous review:

 case "STICKY_NOTE_DELETED": {
+    if (!action.stickyNoteId) {
+        console.warn("Invalid sticky note ID");
+        return state;
+    }
     const { stickyNotes, layout } = removeStickyNoteFromLayout(state, action.stickyNoteId);
     return {
         ...addStickyNotesWithLayout(state, { stickyNotes, layout }),
     };
 }
designer/client/src/components/graph/Graph.tsx (4)

Line range hint 21-77: LGTM! Clean namespace extension for sticky notes.

The namespace is properly extended to include sticky note components while maintaining the existing shapes.


234-242: LGTM! Proper handling of sticky note movement.

The code correctly:

  1. Hides tools during movement
  2. Shows tools for the moved note
  3. Updates position in backend

470-489: Move size constraints to configuration.

The size constraints are hardcoded in the resize handler. Consider moving them to a configuration file for better maintainability.


530-553: 🛠️ Refactor suggestion

Extract permission check to a private method.

The permission check this.props.capabilities.editFrontend is duplicated across methods. Consider extracting it to a private method.

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

695-707: 🛠️ Refactor suggestion

Improve error message handling.

The error messages should use i18next's options object for better localization support.

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

7182-7213: 🛠️ Refactor suggestion

Add validation constraints to sticky note schemas.

Consider adding OpenAPI constraints to improve validation.


7214-7239: ⚠️ Potential issue

Add validation to prevent XSS in note content.

The content field accepts any string without sanitization, which could lead to XSS attacks.

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

34-34: Incorrect import path

The import statement import stickynotes.Dtos._ lacks the full package path, which may lead to import errors or ambiguity. To ensure clarity and prevent potential issues, specify the full package path.

Apply this diff to correct the import:

-  import stickynotes.Dtos._
+  import pl.touk.nussknacker.ui.api.description.stickynotes.Dtos._

39-39: Incorrect epoch conversion for example date

The exampleInstantDate uses Instant.ofEpochMilli(1730136602), which corresponds to a date in 1970. If the intention is to represent the date "2024-10-29T18:10:02Z", you should use Instant.ofEpochSecond(1730136602).

Apply this diff to fix the date:

-  private val exampleInstantDate = Instant.ofEpochMilli(1730136602)
+  private val exampleInstantDate = Instant.ofEpochSecond(1730136602)

93-107: Align noteId parameter placement with RESTful conventions

In the stickyNotesUpdateEndpoint, the noteId is currently provided in the request body as part of StickyNoteUpdateRequest. For consistency and adherence to RESTful API design principles, consider passing the noteId as a path parameter, similar to how it's handled in the stickyNotesDeleteEndpoint. This change enhances API intuitiveness and consistency.

Apply this diff to adjust the endpoint:

.put
-          .in("processes" / path[ProcessName]("scenarioName") / "stickyNotes")
+          .in("processes" / path[ProcessName]("scenarioName") / "stickyNotes" / path[StickyNoteId]("noteId"))
           .in(
-            jsonBody[StickyNoteUpdateRequest]
+            jsonBody[StickyNoteUpdateRequestWithoutId]
               .example(
-                StickyNoteUpdateRequest(
-                  StickyNoteId(1),
+                StickyNoteUpdateRequestWithoutId(
                   VersionId(1),
                   // other fields...

You'll need to create a new DTO StickyNoteUpdateRequestWithoutId that excludes noteId, as it will now be obtained from the path.

@philemone philemone closed this Dec 3, 2024
This was referenced 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