Skip to content

Conversation

@maximiliani
Copy link
Member

@maximiliani maximiliani commented Dec 13, 2024

Relationship management API

see issue #109

Why?

While creating a more complex use case of FAIR-DOs, I needed to create connected FAIR-DOs. The Typed PID Maker couldn't do it, therefore I initially planned to write a (highly disposable) Python script. Until I saw this open issue...

In issue #109 an idea of managing relationships of already existing FAIR-DOs was introduced. This was not quite what I needed for my use case, because I had to create a not small amount of FAIR-DOs (>2500 per data source --> 3*2500 = 7.500 FAIR-DOs). This made it undesirable to first create some half-finished FAIR-DOs, just to extend them later... Therefore, I extended the existing /pit/pid endpoint to create connected FAIR-DOs from a list of PIDRecords with "fantasy PIDs" just for referencing. These "fantasy PIDs" are mapped to freshly-generated PIDs and simply replaced in the value fields of all the records in the same request.

What?

  • create POST /api/v1/pit/pids endpoint for creating connected FAIR-DOS
    • accept a list of PIDRecords with "fantasy PIDs" that are referenced in other PIDRecords values and simply replace them
    • OpenAPI v3 documentation
    • add implementation for createPIDs() method
    • return mapping between fictionary (aka. user-provided) PIDs to real Handle PIDs
    • Testing

See future ideas in issue #109

Summary by CodeRabbit

  • New Features

    • Added batch creation endpoint for Persistent Identifier (PID) records, allowing multiple related PIDs to be created in a single request.
    • Introduced mapping of user-supplied temporary PIDs to actual generated PIDs in batch responses.
    • Added support for dry-run validation mode for batch PID creation.
  • Bug Fixes

    • Improved validation and error handling for duplicate or invalid PIDs in batch submissions.
    • Enhanced rollback logic to prevent partial persistence on batch failures.
  • Documentation

    • Updated configuration comments for clarity and corrected minor typos.
    • Improved JavaDoc comments for new and existing endpoints.
  • Refactor

    • Unified configuration file formatting and assignment style.
    • Reformatted and cleaned up REST resource implementations for consistency.
  • Tests

    • Added extensive integration and unit tests for PID creation, connection logic, batch processing, and error scenarios.
    • Introduced utility builders for PIDs and PID records to facilitate robust testing.
  • Chores

    • Added license headers to source files.
    • Updated .gitignore to exclude new test data and sensitive files.

@maximiliani maximiliani changed the title relationsship management API Relationship management API Dec 13, 2024
@Pfeil
Copy link
Member

Pfeil commented Dec 18, 2024

Note: bidirectional relations should be properly defined. Some bidirectional relations have different relation types. For example, "hasMetadata" and "isMetadataFor". Should / can we (reliably) support these cases?

@maximiliani maximiliani changed the title Relationship management API Create interconnected FAIR-DOs Jan 13, 2025
@maximiliani
Copy link
Member Author

Note: bidirectional relations should be properly defined. Some bidirectional relations have different relation types. For example, "hasMetadata" and "isMetadataFor". Should / can we (reliably) support these cases?

Probably, this would be useful at some point in time. For now, due to time constraints and a lack of a concrete use case, I moved these ideas to the original issue (#109).

@Pfeil Pfeil added this to the 3.0.0 milestone Jan 26, 2025
@Pfeil Pfeil added the enhancement New feature or request label Jan 26, 2025
@maximiliani maximiliani changed the base branch from master to dev-v3 January 31, 2025 09:06
@Pfeil
Copy link
Member

Pfeil commented Feb 13, 2025

Thanks for contributing! Besides the CI failing, can you give an example here? To which degree does this work? Does the value need to fully match a placeholder or will "value where $insert_pid_here" work? I think this should be reflected in the documentation. I'll try to do a review in the next days.

Copy link
Member

@Pfeil Pfeil left a comment

Choose a reason for hiding this comment

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

Just small comments, a proper/full review will come at a later point :)

Pfeil and others added 2 commits June 11, 2025 10:43
@coderabbitai
Copy link

coderabbitai bot commented Jul 15, 2025

Important

Review skipped

Auto reviews are disabled on base/target branches other than the default branch.

Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Walkthrough

This update introduces comprehensive support for batch creation of PID records, including new API endpoints and internal logic for mapping, validating, and registering multiple connected records in a single request. It adds cloning and deep-copy capabilities to core domain classes, introduces new builder utilities and extensive tests for connected PID scenarios, and refines configuration and style consistency.

Changes

Files / Groups Change Summary
.gitignore Added ignore rules for test_prefix_data/ and config/**/*.bin, config/**/*.pem; minor formatting fix.
config/application-default.properties Standardized property assignment style; disabled RabbitMQ health check; minor comment corrections.
src/main/java/edu/kit/datamanager/pit/common/RecordValidationException.java Added license header; reformatted; corrected a comment typo.
src/main/java/edu/kit/datamanager/pit/domain/PIDRecord.java Added Cloneable implementation, deep clone() and setEntries() methods, license header, and formatting improvements.
src/main/java/edu/kit/datamanager/pit/domain/PIDRecordEntry.java Added Cloneable implementation, clone() method, and license header.
src/main/java/edu/kit/datamanager/pit/web/BatchRecordResponse.java Introduced new record class for batch PID responses with Javadoc.
src/main/java/edu/kit/datamanager/pit/web/ITypingRestResource.java Added batch creation endpoint (createPIDs), improved docs, reordered methods, and reformatted interface.
src/main/java/edu/kit/datamanager/pit/web/impl/TypingRESTResourceImpl.java Major refactor: implemented batch PID creation, mapping, validation, rollback, helper methods, and improved error handling/logging.
src/test/java/edu/kit/datamanager/pit/web/ConnectedPIDsTest.java Added comprehensive tests for connected PID batch creation, validation, rollback, and builder utilities.
src/test/java/edu/kit/datamanager/pit/web/PIDBuilder.java Introduced deterministic PID string builder utility for testing with various prefix/suffix options.
src/test/java/edu/kit/datamanager/pit/web/PIDRecordBuilder.java Introduced builder for crafting complex PIDRecord test instances, including connection logic and invalid scenarios.

Sequence Diagram(s)

sequenceDiagram
    participant Client
    participant RESTController
    participant TypingRESTResourceImpl
    participant PIDService
    participant Database

    Client->>RESTController: POST /api/v1/pit/pids (List<PIDRecord>)
    RESTController->>TypingRESTResourceImpl: createPIDs(records, dryrun, ...)
    TypingRESTResourceImpl->>TypingRESTResourceImpl: generatePIDMapping(records, dryrun)
    TypingRESTResourceImpl->>TypingRESTResourceImpl: applyMappingsToRecordsAndValidate(records, mappings, prefix)
    alt dryrun
        TypingRESTResourceImpl-->>RESTController: BatchRecordResponse (HTTP 200)
    else registration
        loop for each record
            TypingRESTResourceImpl->>PIDService: registerPID(record)
            PIDService->>Database: persist(record)
        end
        alt all succeed
            TypingRESTResourceImpl-->>RESTController: BatchRecordResponse (HTTP 201)
        else any fail
            TypingRESTResourceImpl->>PIDService: rollback successful registrations
            TypingRESTResourceImpl-->>RESTController: Error response (HTTP 500)
        end
    end
Loading

Poem

🐇
A batch of PIDs, all linked in a chain,
Builders and records, tested again and again.
Cloning and mapping, connections anew,
Rollbacks on trouble, the code hops through.
With tests and diagrams, the rabbits delight—
Batch creation now works just right!


Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ 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.
    • Explain this complex logic.
    • 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 explain this code block.
    • @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 explain its main purpose.
    • @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.

Support

Need help? Create a ticket on our support page for assistance with any issues or questions.

Note: Be mindful of the bot's finite context window. It's strongly recommended to break down tasks such as reading entire modules into smaller chunks. For a focused discussion, use review comments to chat about specific files and their changes, instead of using the PR comments.

CodeRabbit Commands (Invoked using PR comments)

  • @coderabbitai pause to pause the reviews on a PR.
  • @coderabbitai resume to resume the paused reviews.
  • @coderabbitai review to trigger an incremental review. This is useful when automatic reviews are disabled for the repository.
  • @coderabbitai full review to do a full review from scratch and review all the files again.
  • @coderabbitai summary to regenerate the summary of the PR.
  • @coderabbitai generate docstrings to generate docstrings for this PR.
  • @coderabbitai generate sequence diagram to generate a sequence diagram of the changes in this 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.

Signed-off-by: Maximilian Inckmann <[email protected]>
@maximiliani maximiliani marked this pull request as ready for review July 15, 2025 15:43
@maximiliani maximiliani requested a review from Pfeil July 15, 2025 15:43
@maximiliani
Copy link
Member Author

@coderabbitai review

@coderabbitai
Copy link

coderabbitai bot commented Jul 15, 2025

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

🧹 Nitpick comments (9)
src/test/java/edu/kit/datamanager/pit/web/PIDBuilder.java (2)

116-132: Consider using SHA-256 instead of SHA-1.

While SHA-1 is sufficient for generating test UUIDs, using SHA-256 aligns with modern security practices and eliminates the static analysis warning.

 private static UUID generateUUID(String seed) {
     try {
-        MessageDigest md = MessageDigest.getInstance("SHA-1");
+        MessageDigest md = MessageDigest.getInstance("SHA-256");
         byte[] hash = md.digest(seed.getBytes(StandardCharsets.UTF_8));
         long msb = 0;
         long lsb = 0;
         for (int i = 0; i < 8; i++) {
             msb = (msb << 8) | (hash[i] & 0xff);
         }
         for (int i = 8; i < 16; i++) {
             lsb = (lsb << 8) | (hash[i] & 0xff);
         }
         return new UUID(msb, lsb);
     } catch (NoSuchAlgorithmException e) {
         throw new RuntimeException(e);
     }
 }

174-182: Consider removing the confusing clone(PIDBuilder) method.

Having both clone() and clone(PIDBuilder) methods creates confusion. The clone(PIDBuilder) method is actually a copy constructor pattern and should be renamed or removed in favor of using the standard clone() method.

Either rename it to clarify its purpose:

-public PIDBuilder clone(PIDBuilder builder) {
+public PIDBuilder copyFrom(PIDBuilder builder) {
     this.seed = builder.seed;
     this.random = new Random(seed);
     this.prefix = builder.prefix;
     this.suffix = builder.suffix;
     return this;
 }

Or remove it entirely and use the standard clone pattern:

// Usage: PIDBuilder copy = original.clone();
src/main/java/edu/kit/datamanager/pit/domain/PIDRecordEntry.java (1)

27-38: Simplify the clone() implementation.

The manual field assignments are redundant since super.clone() already copies all fields, and String fields are immutable.

 @Override
 public PIDRecordEntry clone() {
     try {
-        PIDRecordEntry clone = (PIDRecordEntry) super.clone();
-        clone.setKey(this.key);
-        clone.setName(this.name);
-        clone.setValue(this.value);
-        return clone;
+        return (PIDRecordEntry) super.clone();
     } catch (CloneNotSupportedException e) {
         throw new AssertionError();
     }
 }
src/main/java/edu/kit/datamanager/pit/web/BatchRecordResponse.java (2)

26-26: Consider using consistent terminology: "fictitious" instead of "fictionary".

The term "fictionary" appears to be non-standard English. Consider using "fictitious" or stick with "fantasy" as used elsewhere in the codebase for consistency.


29-31: Use standard Javadoc @param tags instead of "Arguments:" section.

For better IDE integration and Javadoc generation, consider using standard parameter documentation.

Replace the Arguments section with:

- * Arguments:
- * - pidRecords: List of PIDRecord objects representing the processed records. (List<PIDRecord>)
- * - mapping: Map where keys are user-provided identifiers (fictionary) and values are the corresponding real record Handle PIDs. (Map<String, String>)
+ * @param pidRecords List of PIDRecord objects representing the processed records
+ * @param mapping Map where keys are user-provided identifiers (fictitious) and values are the corresponding real record Handle PIDs
src/main/java/edu/kit/datamanager/pit/domain/PIDRecord.java (1)

88-91: Document that setEntries bypasses validation.

The setEntries method allows direct manipulation of the entries map, bypassing the validation logic in addEntry. Consider adding Javadoc to warn about this.

+    /**
+     * Sets the entries map directly. Use with caution as this bypasses validation.
+     * Primarily intended for cloning and testing purposes.
+     * 
+     * @param entries the entries map to set
+     */
     public void setEntries(Map<String, List<PIDRecordEntry>> entries) {
         this.entries = entries;
     }
src/test/java/edu/kit/datamanager/pit/web/PIDRecordBuilder.java (1)

502-509: Random string generation may produce problematic characters.

The generateRandomString method can generate control characters, surrogates, and other non-printable characters that might cause issues in tests or systems that process these strings.

Consider limiting the character range to printable ASCII or alphanumeric characters:

 private String generateRandomString(int length) {
     StringBuilder result = new StringBuilder();
     for (int i = 0; i < length; i++) {
-        char c = (char) random.nextInt(Character.MAX_VALUE);
+        // Generate printable ASCII characters (space to ~)
+        char c = (char) (random.nextInt(94) + 32);
         result.append(c);
     }
     return result.toString();
 }
src/main/java/edu/kit/datamanager/pit/web/impl/TypingRESTResourceImpl.java (1)

178-186: Consider tracking rollback failures for operational monitoring.

The rollback logic logs errors but doesn't track which PIDs failed to rollback. This could lead to orphaned PIDs in the system.

Consider collecting rollback failures and including them in the response or metrics:

         if (!failedRecords.isEmpty()) {
+            List<String> rollbackFailures = new ArrayList<>();
             for (PIDRecord successfulRecord : validatedRecords) {
                 try {
                     LOG.debug("Rolling back PID creation for record with PID {}.", successfulRecord.getPid());
                     this.typingService.deletePid(successfulRecord.getPid());
                 } catch (Exception e) {
                     LOG.error("Could not rollback PID creation for record with PID {}. Error: {}", successfulRecord.getPid(), e.getMessage());
+                    rollbackFailures.add(successfulRecord.getPid());
                 }
             }
+            if (!rollbackFailures.isEmpty()) {
+                LOG.error("Failed to rollback {} PIDs: {}", rollbackFailures.size(), rollbackFailures);
+            }
src/test/java/edu/kit/datamanager/pit/web/ConnectedPIDsTest.java (1)

231-232: Use a more specific exception type.

Instead of catching generic Exception.class, consider catching a more specific exception type that nullRecord().build() is expected to throw.

-        assertThrows(Exception.class, nullBuilder::build, "nullRecord should throw exception when built");
+        assertThrows(IllegalStateException.class, nullBuilder::build, "nullRecord should throw exception when built");
📜 Review details

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

📥 Commits

Reviewing files that changed from the base of the PR and between acbb333 and 4e168d7.

📒 Files selected for processing (11)
  • .gitignore (1 hunks)
  • config/application-default.properties (2 hunks)
  • src/main/java/edu/kit/datamanager/pit/common/RecordValidationException.java (1 hunks)
  • src/main/java/edu/kit/datamanager/pit/domain/PIDRecord.java (13 hunks)
  • src/main/java/edu/kit/datamanager/pit/domain/PIDRecordEntry.java (1 hunks)
  • src/main/java/edu/kit/datamanager/pit/web/BatchRecordResponse.java (1 hunks)
  • src/main/java/edu/kit/datamanager/pit/web/ITypingRestResource.java (6 hunks)
  • src/main/java/edu/kit/datamanager/pit/web/impl/TypingRESTResourceImpl.java (1 hunks)
  • src/test/java/edu/kit/datamanager/pit/web/ConnectedPIDsTest.java (1 hunks)
  • src/test/java/edu/kit/datamanager/pit/web/PIDBuilder.java (1 hunks)
  • src/test/java/edu/kit/datamanager/pit/web/PIDRecordBuilder.java (1 hunks)
🧰 Additional context used
🧬 Code Graph Analysis (1)
src/main/java/edu/kit/datamanager/pit/common/RecordValidationException.java (1)
src/main/java/edu/kit/datamanager/pit/domain/PIDRecord.java (1)
  • PIDRecord (34-288)
🪛 ast-grep (0.38.6)
src/test/java/edu/kit/datamanager/pit/web/PIDBuilder.java

[warning] 117-117: Detected SHA1 hash algorithm which is considered insecure. SHA1 is not collision resistant and is therefore not suitable as a cryptographic signature. Instead, use PBKDF2 for password hashing or SHA256 or SHA512 for other hash function applications.
Context: MessageDigest.getInstance("SHA-1")
Note: [CWE-328] Use of Weak Hash. [REFERENCES]
- https://owasp.org/Top10/A02_2021-Cryptographic_Failures

(use-of-sha1-java)

🔇 Additional comments (18)
.gitignore (1)

140-142: LGTM!

The additions appropriately exclude test data and sensitive credential files from version control.

src/main/java/edu/kit/datamanager/pit/common/RecordValidationException.java (1)

1-54: LGTM!

The formatting improvements and license header addition enhance code consistency.

config/application-default.properties (1)

134-139: LGTM!

The property format standardization and RabbitMQ health check configuration are appropriate.

src/main/java/edu/kit/datamanager/pit/domain/PIDRecord.java (1)

270-288: Well-implemented deep cloning logic.

The clone method properly performs deep copying of the entries map and all PIDRecordEntry objects, ensuring true independence between cloned instances.

src/main/java/edu/kit/datamanager/pit/web/ITypingRestResource.java (2)

54-55: Use consistent terminology throughout the documentation.

The documentation uses both "imaginary" and "fictionary" to describe the temporary PIDs. Please use one term consistently.

Also applies to: 71-71, 74-74, 83-83

Likely an incorrect or invalid review comment.


48-104: Well-designed batch creation endpoint.

The new createPIDs method provides a clean interface for batch PID creation with proper support for dry-run validation and comprehensive error handling.

src/test/java/edu/kit/datamanager/pit/web/PIDRecordBuilder.java (1)

25-67: Excellent documentation and test utility design.

The builder class is very well documented with clear examples and comprehensive feature descriptions. The deterministic approach using seeds is perfect for reproducible tests.

src/main/java/edu/kit/datamanager/pit/web/impl/TypingRESTResourceImpl.java (1)

115-134: Excellent timing instrumentation for performance monitoring.

The detailed timing logs for mapping, validation, and registration phases will be valuable for performance analysis and optimization.

Also applies to: 171-177

src/test/java/edu/kit/datamanager/pit/web/ConnectedPIDsTest.java (10)

1-50: LGTM! Well-structured test class setup.

The test class is properly configured with appropriate Spring Boot test annotations and profile settings for integration testing.


68-81: Good practice: Test setup verification.

The checkTestSetup() method ensures the test environment is properly initialized before running other tests, which helps catch configuration issues early.


83-162: Comprehensive test coverage for PIDBuilder.

The test thoroughly exercises all PIDBuilder methods including edge cases. Note that direct field access (line 96) suggests package-private visibility, which is acceptable for test builders.


247-251: Potential test flakiness with deterministic assumption.

The test assumes that builders with the same seed will produce identical results (line 250). This might not hold true if there's any non-deterministic behavior in the builder implementation.

Consider verifying that the PIDRecordBuilder implementation is truly deterministic when using the same seed, or adjust the test expectations accordingly.


258-309: Well-designed connection functionality tests.

The tests properly validate both the addConnection instance method and connectRecordBuilders static method, including error cases and custom key configurations.


311-458: Excellent API endpoint test coverage.

These tests comprehensively validate the /api/v1/pit/pids endpoint behavior for:

  • Valid connected records creation
  • Single record creation
  • Empty list rejection
  • Dry-run mode without persistence
  • Invalid JSON format handling
  • Unsupported media type rejection

The combination of HTTP response assertions and database state verification ensures proper endpoint functionality.


460-588: Thorough edge case testing.

These tests effectively validate critical scenarios:

  • Circular references between records are handled correctly
  • Duplicate temporary PIDs are properly rejected
  • Records with incomplete profiles fail validation
  • Large batches (200 records) are processed successfully
  • External references are preserved

The edge case coverage helps ensure robustness of the batch PID creation feature.


668-987: Comprehensive coverage of builder patterns and failure scenarios.

The remaining tests excellently cover:

  • PIDBuilder edge cases with various prefix/suffix combinations
  • Invalid configuration handling
  • Builder method chaining behavior
  • Multiple rollback scenarios ensuring atomicity
  • Batch operations with duplicate PIDs

The rollback tests are particularly valuable as they ensure the batch creation operation maintains data integrity by preventing partial success states.


45-988: Outstanding test implementation for the batch PID creation feature.

This test class provides exceptional coverage of the new /api/v1/pit/pids endpoint with:

  • Comprehensive builder pattern testing
  • Thorough API endpoint validation
  • Edge case and error scenario coverage
  • Transactional rollback verification
  • Performance testing with large batches

The test structure follows best practices with clear naming, proper assertions, and good use of test data builders.


663-663: Action required: Configure Java 21+ or replace getFirst() usage

The List.getFirst() call requires Java 21 and above. Your Gradle build file doesn’t explicitly enforce a minimum JDK version, so invoking getFirst() may break on older JDKs.

• File: src/test/java/edu/kit/datamanager/pit/web/ConnectedPIDsTest.java (line 663)
String storedPid = knownPidsDao.findAll().getFirst().getPid();

Consider one of the following:
– For Java < 21 compatibility, switch to get(0):

- String storedPid = knownPidsDao.findAll().getFirst().getPid();
+ String storedPid = knownPidsDao.findAll().get(0).getPid();

– To keep getFirst(), configure a Java 21 toolchain in your build.gradle:

java {
  toolchain {
    languageVersion = JavaLanguageVersion.of(21)
  }
}

Signed-off-by: Maximilian Inckmann <[email protected]>
Signed-off-by: Maximilian Inckmann <[email protected]>
@Pfeil Pfeil changed the title Create interconnected FAIR-DOs API for creating interconnected FAIR-DOs (a FAIR-DO graph) in a single request Jul 16, 2025
Copy link
Member

@Pfeil Pfeil left a comment

Choose a reason for hiding this comment

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

Overall very nice. Just some minor comments we should discuss or address. You did some other nice refactors, too: thanks for putting so much work into it.

Signed-off-by: Maximilian Inckmann <[email protected]>
@maximiliani maximiliani requested a review from Pfeil July 16, 2025 18:55
Copy link
Member

@Pfeil Pfeil left a comment

Choose a reason for hiding this comment

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

Nice, thanks a lot!

@Pfeil Pfeil merged commit fd4478c into kit-data-manager:dev-v3 Jul 17, 2025
4 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

enhancement New feature or request

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants