Skip to content

Conversation

@thiagohora
Copy link
Contributor

@thiagohora thiagohora commented Nov 13, 2025

Details

This PR implements the complete CRUD functionality for workspace-level custom dashboards. The implementation includes:

Core Features

  • Create Dashboard: Create custom dashboards with name, description, and JSON configuration
  • Read Dashboard: Get dashboard by ID and list dashboards with pagination
  • Update Dashboard: Update dashboard fields with optimistic locking (version-based)
  • Delete Dashboard: Idempotent delete operation (returns 204 regardless of existence)

Key Components

Database Layer:

  • New dashboards table with UUIDv7 IDs, workspace scoping, and optimistic locking
  • JSON column for flexible dashboard configuration storage
  • Unique constraints on workspace+name and workspace+slug combinations
  • Custom JsonNodeColumnMapper for JDBI3 JSON handling

Domain Layer:

  • DashboardDAO: JDBI3 interface for database operations
  • DashboardService: Business logic including slug generation and validation
  • DashboardConfigValidator: JSON schema validation for dashboard configurations
  • SlugUtils: URL-safe slug generation with deduplication

API Layer:

  • RESTful endpoints under /v1/private/dashboards
  • Support for pagination, filtering by name, and sorting
  • OpenAPI/Swagger documentation
  • JsonView annotations for proper field visibility (read-only vs writable)

Testing:

  • Comprehensive resource tests following OptimizationsResourceTest pattern
  • DashboardResourceClient for test API interactions
  • 23 test cases covering all CRUD operations, validation, and edge cases
  • Test isolation with unique data generation

Technical Decisions

  1. Slug Generation: Automatic URL-safe slug generation from dashboard name with deduplication per workspace
  2. Optimistic Locking: Version field prevents concurrent update conflicts
  3. Idempotent DELETE: Returns 204 No Content for both existing and non-existing resources (REST best practice)
  4. JSON Schema Validation: Validates dashboard config against predefined schema (currently v1)
  5. Workspace Scoping: All operations are scoped to workspace for proper multi-tenancy

Change checklist

  • User facing
  • Documentation update

Issues

  • Resolves OPIK-3012

Testing

All 23 tests passing in DashboardsResourceTest:

Create Operations (7 tests)

  • ✅ Create dashboard with all fields
  • ✅ Create dashboard with minimal fields
  • ✅ Create dashboard with null description
  • ✅ Create multiple dashboards with unique names
  • ✅ Reject duplicate dashboard names in same workspace
  • ✅ Reject invalid dashboard name (empty, too long)
  • ✅ Reject invalid config (non-object JSON)

Read Operations (4 tests)

  • ✅ Get dashboard by ID
  • ✅ Get non-existent dashboard returns 404
  • ✅ List dashboards with pagination
  • ✅ Find dashboards by name

Update Operations (6 tests)

  • ✅ Update dashboard name and config
  • ✅ Update dashboard description to null
  • ✅ Update with optimistic locking (version check)
  • ✅ Reject concurrent updates (version mismatch)
  • ✅ Update non-existent dashboard returns 404
  • ✅ Reject duplicate name on update

Delete Operations (4 tests)

  • ✅ Delete existing dashboard
  • ✅ Delete non-existent dashboard returns 204
  • ✅ Delete dashboard from different workspace returns 204
  • ✅ Verify dashboard is actually deleted

Slug Generation (2 tests)

  • ✅ Generate URL-safe slugs from names
  • ✅ Deduplicate slugs with numeric suffixes

Run tests:

cd apps/opik-backend
mvn clean test -Dtest=DashboardsResourceTest

Documentation

No documentation updates required for this internal API. Future PRs will add:

  • Frontend integration
  • User-facing documentation
  • API client SDK updates

@thiagohora thiagohora force-pushed the thiagohora/OPIK-3012-workspace-dashboards-crud branch from a7352e4 to b762afc Compare November 13, 2025 14:17
@thiagohora thiagohora changed the title OPIK-3012: [BE] Add workspace dashboards CRUD functionality [OPIK-3012] [BE] Add workspace dashboards CRUD functionality Nov 13, 2025
@comet-ml comet-ml deleted a comment from github-actions bot Nov 13, 2025
@comet-ml comet-ml deleted a comment from github-actions bot Nov 13, 2025
@github-actions
Copy link
Contributor

github-actions bot commented Nov 13, 2025

Backend Tests Results

  296 files    296 suites   52m 11s ⏱️
5 487 tests 5 480 ✅ 7 💤 0 ❌
5 441 runs  5 434 ✅ 7 💤 0 ❌

Results for commit c9178c2.

♻️ This comment has been updated with latest results.

@github-actions
Copy link
Contributor

github-actions bot commented Nov 14, 2025

SDK E2E Tests Results

0 tests   0 ✅  0s ⏱️
0 suites  0 💤
0 files    0 ❌

Results for commit 57d9657.

♻️ This comment has been updated with latest results.

@thiagohora thiagohora marked this pull request as ready for review November 14, 2025 10:40
@thiagohora thiagohora requested a review from a team as a code owner November 14, 2025 10:40
Copilot AI review requested due to automatic review settings November 14, 2025 10:40
Copy link
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull Request Overview

This PR implements complete CRUD functionality for workspace-level custom dashboards in the Opik backend. The feature enables users to create, read, update, and delete custom dashboards with JSON-based configurations, including automatic slug generation, optimistic locking for concurrent updates, and workspace-scoped operations.

Key changes:

  • New database schema with dashboards table supporting JSON configurations and workspace multi-tenancy
  • RESTful API endpoints for all CRUD operations with proper validation and error handling
  • Business logic layer with slug generation utilities and optimistic concurrency control
  • Comprehensive test suite with 23 test cases covering all operations and edge cases

Reviewed Changes

Copilot reviewed 10 out of 10 changed files in this pull request and generated 5 comments.

Show a summary per file
File Description
000034_create_dashboards_table.sql Liquibase migration creating dashboards table with UUIDv7 IDs, JSON config storage, and unique constraints
JsonNodeColumnMapper.java JDBI3 column mapper for serializing/deserializing JSON columns to Jackson JsonNode objects
SlugUtils.java Utility class for generating URL-safe slugs from dashboard names with Unicode normalization and deduplication
DashboardDAO.java JDBI3 data access interface with parameterized queries for database operations
DashboardService.java Service layer implementing business logic including slug generation, validation, and transaction management
DashboardsResource.java REST API controller with OpenAPI documentation for all CRUD endpoints
Dashboard.java Domain model with JsonView annotations for field visibility control
DashboardUpdate.java DTO for dashboard update operations with validation constraints
DashboardResourceClient.java Test client utility for making API requests in integration tests
DashboardsResourceTest.java Comprehensive test suite with 23 tests covering all CRUD operations and edge cases

- Add MAX_SLUG_LENGTH constant (150) to match database constraint
- Remove DROP TABLE IF EXISTS from main migration changeset
- Make ObjectMapper static final in JsonNodeColumnMapper for performance
- Add retry logic with proper for loop for slug race conditions
- Remove unused podamFactory field from DashboardResourceClient
* Utility class for generating URL-safe slugs from dashboard names.
*/
@UtilityClass
public class SlugUtils {
Copy link
Member

@andrescrz andrescrz Nov 14, 2025

Choose a reason for hiding this comment

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

I'd try to use a stable and license compatible library to avoid re-inventing the while. I believe this is an option:

But there would be more library options.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Do you believe we really need a lib for it?

Copy link
Member

@andrescrz andrescrz Nov 17, 2025

Choose a reason for hiding this comment

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

It's a matter of avoiding maintaining, testing etc. our own code, if we can get it for free from a well-known and stable library etc.

Also to avoid bugs, side cases etc. as this implementation might have not considered all characters in regex etc.

But this is not a blocker.

- Remove optimistic locking (lastUpdatedAt from DashboardUpdate)
- Consolidate update + refresh into single service method that returns Dashboard
- Remove retry logic from update method
- Rename 'search' query parameter to 'name' with proper documentation
- Change ORDER BY from last_updated_at to id DESC
- Replace ObjectMapper with JsonUtils in JsonNodeColumnMapper and test client
- Remove unnecessary @nonnull annotations from SlugUtils
- Update API documentation to clarify partial update support
- Removed 'updateDashboardWithTimestampCheck' test that relied on lastUpdatedAt field
- All 20 remaining tests pass successfully
- Changed path from /v1/private/workspaces/{workspaceId}/dashboards to /v1/private/dashboards
- Removed unused workspaceIdPath parameters from all endpoint methods
- Updated test client RESOURCE_PATH constant and all API calls
- WorkspaceId is now resolved exclusively from authentication context (RequestContext)
- All 20 tests pass successfully
@thiagohora thiagohora requested a review from andrescrz November 17, 2025 09:12
andrescrz
andrescrz previously approved these changes Nov 17, 2025
Copy link
Member

@andrescrz andrescrz left a comment

Choose a reason for hiding this comment

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

LGTM, you can move forward.

I strongly recommend changing the update method to PATCH. It doesn't look like a PUT.

My secondary commendation is using a library for generating slugs, but not a blocker.

The rest if minor an optional.

* Utility class for generating URL-safe slugs from dashboard names.
*/
@UtilityClass
public class SlugUtils {
Copy link
Member

@andrescrz andrescrz Nov 17, 2025

Choose a reason for hiding this comment

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

It's a matter of avoiding maintaining, testing etc. our own code, if we can get it for free from a well-known and stable library etc.

Also to avoid bugs, side cases etc. as this implementation might have not considered all characters in regex etc.

But this is not a blocker.

@thiagohora thiagohora merged commit 9ef4d95 into main Nov 17, 2025
16 checks passed
@thiagohora thiagohora deleted the thiagohora/OPIK-3012-workspace-dashboards-crud branch November 17, 2025 15:09
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants