Skip to content

[OPIK-6264] [BE] feat: add environments CRUD API#6563

Merged
BorisTkachenko merged 2 commits intomainfrom
boryst/OPIK-6264-be-environments-crud
Apr 30, 2026
Merged

[OPIK-6264] [BE] feat: add environments CRUD API#6563
BorisTkachenko merged 2 commits intomainfrom
boryst/OPIK-6264-be-environments-crud

Conversation

@BorisTkachenko
Copy link
Copy Markdown
Contributor

Details

Adds a workspace-scoped Environments CRUD module to the backend so users can manage named environments (with description, color, position) via the private API. Introduces the environments MySQL table, JDBI DAO, transactional service, and Dropwizard resource, plus a configurable per-workspace cap (default 20) and the same audit/analytics/locking patterns used by other workspace entities.

  • New endpoints under /v1/private/environments: GET / (list, capped), GET /{id}, POST / (create, rate-limited), PATCH /{id} (partial update, rate-limited), POST /delete (idempotent batch delete).
  • New API records Environment.java and EnvironmentUpdate.java with JSON views (Public / Write), snake_case naming, and validation (name regex ^[A-Za-z0-9_-]+$, length caps 150/500/20).
  • EnvironmentService.java enforces the workspace limit under a LockService mutex, maps unique-constraint violations to 409 Conflict, emits an environment_created analytics event, and uses READ_ONLY / WRITE transaction templates.
  • EnvironmentDAO.java uses COALESCE for partial PATCH updates and scopes every query by workspace_id.
  • New Liquibase migration 000065_create_environments_table.sql creates environments with a (workspace_id, name) unique constraint.
  • New EnvironmentConfig.java wired into OpikConfiguration.java; exposes OPIK_ENVIRONMENT_MAX_PER_WORKSPACE (default 20) in config.yml.

Change checklist

  • User facing
  • Documentation update

Issues

  • Resolves #
  • OPIK-6264

AI-WATERMARK

AI-WATERMARK: no

Testing

  • New integration suite EnvironmentsResourceTest.java (~506 lines) covers create/get/list/update/batch-delete happy paths, workspace isolation, name uniqueness (409), workspace cap (409), validation errors (400), missing-id (404), and PATCH partial semantics.
  • Test client helper EnvironmentsResourceClient.java added for reuse.
  • Run: cd apps/opik-backend && mvn test -Dtest=EnvironmentsResourceTest.

Documentation

N/A — OpenAPI annotations on the resource cover the new endpoints; no separate docs added.

@BorisTkachenko BorisTkachenko self-assigned this Apr 30, 2026
@BorisTkachenko BorisTkachenko requested a review from a team as a code owner April 30, 2026 10:52
@github-actions github-actions Bot added java Pull requests that update Java code Backend tests Including test files, or tests related like configuration. labels Apr 30, 2026
@github-actions
Copy link
Copy Markdown
Contributor

github-actions Bot commented Apr 30, 2026

Backend Tests - Integration Group 5

147 tests   144 ✅  3m 0s ⏱️
 28 suites    3 💤
 28 files      0 ❌

Results for commit 5acf158.

♻️ This comment has been updated with latest results.

Copy link
Copy Markdown
Contributor

@LifeXplorer LifeXplorer left a comment

Choose a reason for hiding this comment

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

Nice work! Added few non-blocking comments

@ApiResponse(responseCode = "200", description = "Environments page", content = @Content(schema = @Schema(implementation = Environment.EnvironmentPage.class)))
})
@JsonView({Environment.View.Public.class})
public Response find() {
Copy link
Copy Markdown
Contributor

@LifeXplorer LifeXplorer Apr 30, 2026

Choose a reason for hiding this comment

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

find() pretends to paginate but doesn't

Environment.EnvironmentPage carries page/size/total/sortableBy, but:

  • EnvironmentsResource.find() accepts no @QueryParam (page, size, name, sorting, filters).
  • Service hardcodes page=1, total=environments.size().
  • DAO findAll has no LIMIT.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Yes, basically we want to return all environments. We are capping it at 20 max. I used page structure for consistency and potential future expansion.

@ApiResponse(responseCode = "409", description = "Conflict", content = @Content(schema = @Schema(implementation = ErrorMessage.class)))
})
@RateLimited
public Response update(@PathParam("id") @NotNull UUID id,
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

PATCH semantics around empty/null aren't nailed down.
EnvironmentUpdate uses @Size(max=500) on description (and @Size(max=20) on color) without @NotBlank — and EnvironmentDAO.update does description = COALESCE(:description, description). So:

  • null → "no change" ✓
  • "" → sets to empty string (passes validation, COALESCE doesn't treat "" as null in MySQL).

There's no way to clear description back to NULL. Decide the contract:

  • "Null means no-op, empty means clear" — document and add a test.
  • "Empty is a no-op" — reject empty with @pattern(regexp = ".+") or a custom not-blank-when-present.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Right now we can remove description by setting it to empty string, this is intended behavior. Not sure if we need to update it to NULL. There is no use case for it. If user wants to remove, FE will pass empty string and it will be effectively removed, DB will hold empty string.

@BorisTkachenko BorisTkachenko merged commit b1a55a2 into main Apr 30, 2026
65 checks passed
@BorisTkachenko BorisTkachenko deleted the boryst/OPIK-6264-be-environments-crud branch April 30, 2026 13:11
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Backend java Pull requests that update Java code tests Including test files, or tests related like configuration.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants