feat: enforce single tool type with port configuration
- Replace interfaces array with single interface_type string (web/terminal) - Add requires_port boolean to indicate port/tunnel needs - Create Alembic migration for database schema change - Update backend model, API validation, and seed data - Update frontend types and tool workshop UI with dropdown - Add conditional port field rendering based on interface type - Update all frontend and backend tests OpenSpec change: enforce-single-tool-type-with-port-config Quality gates: frontend typecheck PASS, lint PASS, tests 37/37 PASS
This commit is contained in:
@@ -111,6 +111,37 @@ The system SHALL provide a dashboard overview.
|
|||||||
- Recent activity
|
- Recent activity
|
||||||
- Quick action buttons
|
- Quick action buttons
|
||||||
|
|
||||||
|
### Requirement: Tool Interface Type Dropdown
|
||||||
|
The tool workshop SHALL provide a dropdown for selecting a single interface type.
|
||||||
|
|
||||||
|
#### Scenario: Interface type dropdown
|
||||||
|
- GIVEN the tool workshop page
|
||||||
|
- WHEN a user creates or edits a tool type
|
||||||
|
- THEN the interface type field is a dropdown (not checkboxes)
|
||||||
|
- AND the options are "web" and "terminal"
|
||||||
|
- AND only one option can be selected
|
||||||
|
|
||||||
|
### Requirement: Conditional Port Fields
|
||||||
|
The tool workshop SHALL conditionally show or hide port-related fields based on the selected interface type.
|
||||||
|
|
||||||
|
#### Scenario: Web tool shows port fields
|
||||||
|
- GIVEN a tool type with interface type "web"
|
||||||
|
- WHEN the user views the tool editor
|
||||||
|
- THEN the Default Port field is visible and required
|
||||||
|
- AND port-related config fields are shown
|
||||||
|
|
||||||
|
#### Scenario: Terminal tool hides port fields
|
||||||
|
- GIVEN a tool type with interface type "terminal"
|
||||||
|
- WHEN the user views the tool editor
|
||||||
|
- THEN the Default Port field is hidden
|
||||||
|
- AND port-related config fields are hidden or disabled
|
||||||
|
|
||||||
|
#### Scenario: Changing interface type updates visibility
|
||||||
|
- GIVEN a user changes interface type from "web" to "terminal"
|
||||||
|
- WHEN the change is applied
|
||||||
|
- THEN port fields are immediately hidden
|
||||||
|
- AND any port value is preserved but not validated
|
||||||
|
|
||||||
## Dependencies
|
## Dependencies
|
||||||
|
|
||||||
- React 18+
|
- React 18+
|
||||||
|
|||||||
@@ -0,0 +1,50 @@
|
|||||||
|
## ADDED Requirements
|
||||||
|
|
||||||
|
### Requirement: Port Configuration Visibility
|
||||||
|
The system SHALL control whether port configuration is relevant for a tool type.
|
||||||
|
|
||||||
|
#### Scenario: Web tool requires port
|
||||||
|
- GIVEN a tool type with `requires_port` = true
|
||||||
|
- WHEN the tool type is displayed in the UI
|
||||||
|
- THEN port configuration fields are shown
|
||||||
|
- AND default_port is validated as required
|
||||||
|
|
||||||
|
#### Scenario: Terminal tool does not require port
|
||||||
|
- GIVEN a tool type with `requires_port` = false
|
||||||
|
- WHEN the tool type is displayed in the UI
|
||||||
|
- THEN port configuration fields are hidden
|
||||||
|
- AND default_port validation is skipped
|
||||||
|
- AND port_override in tool configs is not shown
|
||||||
|
|
||||||
|
### Requirement: Port Validation Based on requires_port
|
||||||
|
The API SHALL validate port fields conditionally based on requires_port.
|
||||||
|
|
||||||
|
#### Scenario: Validate port for web tools
|
||||||
|
- GIVEN a tool type with `requires_port` = true
|
||||||
|
- WHEN creating or updating without a default_port
|
||||||
|
- THEN the system returns 400 Bad Request
|
||||||
|
|
||||||
|
#### Scenario: Skip port validation for terminal tools
|
||||||
|
- GIVEN a tool type with `requires_port` = false
|
||||||
|
- WHEN creating or updating without a default_port
|
||||||
|
- THEN the request succeeds
|
||||||
|
- AND default_port defaults to 0 or null
|
||||||
|
|
||||||
|
### Requirement: UI Conditional Rendering
|
||||||
|
The frontend SHALL conditionally render port-related UI elements.
|
||||||
|
|
||||||
|
#### Scenario: Hide port in tool list
|
||||||
|
- GIVEN a terminal tool type
|
||||||
|
- WHEN displayed in the tool workshop list
|
||||||
|
- THEN port information is not shown
|
||||||
|
|
||||||
|
#### Scenario: Hide port in editor
|
||||||
|
- GIVEN a terminal tool type being edited
|
||||||
|
- WHEN the editor form is rendered
|
||||||
|
- THEN the Default Port field is hidden
|
||||||
|
- AND the readiness probe fields are shown (still relevant)
|
||||||
|
|
||||||
|
#### Scenario: Show port for web tools
|
||||||
|
- GIVEN a web tool type being edited
|
||||||
|
- WHEN the editor form is rendered
|
||||||
|
- THEN the Default Port field is visible and required
|
||||||
@@ -0,0 +1,39 @@
|
|||||||
|
## ADDED Requirements
|
||||||
|
|
||||||
|
### Requirement: Single Interface Type Enforcement
|
||||||
|
The system SHALL enforce that each tool type has exactly one interface type.
|
||||||
|
|
||||||
|
#### Scenario: Create with single interface
|
||||||
|
- GIVEN a tool type creation request with `interface_type` = "web"
|
||||||
|
- WHEN the request is processed
|
||||||
|
- THEN the tool type is created successfully
|
||||||
|
- AND the interface type is stored as a single string
|
||||||
|
|
||||||
|
#### Scenario: Reject multiple interfaces
|
||||||
|
- GIVEN a legacy request with `interfaces` array
|
||||||
|
- WHEN the request is processed
|
||||||
|
- THEN the system returns 400 Bad Request
|
||||||
|
- AND the error message indicates that `interface_type` (string) should be used instead
|
||||||
|
|
||||||
|
### Requirement: Interface Type Validation
|
||||||
|
The system SHALL validate that interface_type is one of the allowed values.
|
||||||
|
|
||||||
|
#### Scenario: Valid interface types
|
||||||
|
- GIVEN interface_type values "web" or "terminal"
|
||||||
|
- WHEN a tool type is created or updated
|
||||||
|
- THEN the request is accepted
|
||||||
|
|
||||||
|
#### Scenario: Invalid interface type
|
||||||
|
- GIVEN interface_type value "ssh"
|
||||||
|
- WHEN a tool type is created or updated
|
||||||
|
- THEN the system returns 400 Bad Request
|
||||||
|
|
||||||
|
### Requirement: Data Migration
|
||||||
|
The system SHALL migrate existing tool types from interfaces array to single interface_type.
|
||||||
|
|
||||||
|
#### Scenario: Migrate existing records
|
||||||
|
- GIVEN existing tool types with interfaces = ["web"] or ["terminal"]
|
||||||
|
- WHEN the migration runs
|
||||||
|
- THEN each record gets interface_type = interfaces[0]
|
||||||
|
- AND requires_port is set based on the interface type
|
||||||
|
- AND the old interfaces column is removed
|
||||||
@@ -11,7 +11,12 @@ The system SHALL provide a `ToolType` model to store tool definitions.
|
|||||||
- `name`: unique string (e.g., "code-server")
|
- `name`: unique string (e.g., "code-server")
|
||||||
- `display_name`: human-readable string (e.g., "VS Code Server")
|
- `display_name`: human-readable string (e.g., "VS Code Server")
|
||||||
- `description`: optional text
|
- `description`: optional text
|
||||||
|
- `category`: string (e.g., "editor", "notebook")
|
||||||
|
- `interface_type`: single string — "web" or "terminal"
|
||||||
|
- `requires_port`: boolean indicating if port/tunnel configuration is needed
|
||||||
- `compose_template`: Docker Compose YAML string
|
- `compose_template`: Docker Compose YAML string
|
||||||
|
- `dockerfile_template`: Dockerfile string
|
||||||
|
- `definition_type`: string — "compose" or "dockerfile"
|
||||||
- `required_variables`: list of required template variables
|
- `required_variables`: list of required template variables
|
||||||
- `is_builtin`: boolean flag for system-defined types
|
- `is_builtin`: boolean flag for system-defined types
|
||||||
- `created_at`/`updated_at`: timestamps
|
- `created_at`/`updated_at`: timestamps
|
||||||
@@ -30,7 +35,9 @@ The system SHALL provide REST API endpoints for tool type management.
|
|||||||
- GIVEN an admin user
|
- GIVEN an admin user
|
||||||
- WHEN they POST /api/tool-types with valid data
|
- WHEN they POST /api/tool-types with valid data
|
||||||
- THEN the system creates a new tool type
|
- THEN the system creates a new tool type
|
||||||
- AND validates the compose template YAML
|
- AND validates `interface_type` is "web" or "terminal"
|
||||||
|
- AND validates `requires_port` is boolean
|
||||||
|
- AND validates the compose template YAML (if definition_type is "compose")
|
||||||
- AND validates all required variables are present in template
|
- AND validates all required variables are present in template
|
||||||
- AND returns 201 Created with the new tool type
|
- AND returns 201 Created with the new tool type
|
||||||
|
|
||||||
@@ -44,6 +51,7 @@ The system SHALL provide REST API endpoints for tool type management.
|
|||||||
- GIVEN an admin user
|
- GIVEN an admin user
|
||||||
- WHEN they PUT /api/tool-types/{id} with valid data
|
- WHEN they PUT /api/tool-types/{id} with valid data
|
||||||
- THEN the system updates the tool type
|
- THEN the system updates the tool type
|
||||||
|
- AND validates `interface_type` is "web" or "terminal" if provided
|
||||||
- AND re-validates the compose template
|
- AND re-validates the compose template
|
||||||
- AND returns 200 OK with updated tool type
|
- AND returns 200 OK with updated tool type
|
||||||
|
|
||||||
@@ -98,3 +106,33 @@ The system SHALL validate Docker Compose templates.
|
|||||||
- THEN the system SHALL require:
|
- THEN the system SHALL require:
|
||||||
- `services` key present
|
- `services` key present
|
||||||
- At least one service defined
|
- At least one service defined
|
||||||
|
|
||||||
|
### Requirement: Port requirement indication
|
||||||
|
The system SHALL allow tool types to indicate whether they require port configuration.
|
||||||
|
|
||||||
|
#### Scenario: Web tool requires port
|
||||||
|
- GIVEN a tool type with `interface_type` = "web"
|
||||||
|
- WHEN the tool type is created or updated
|
||||||
|
- THEN `requires_port` SHALL default to true
|
||||||
|
- AND port-related configuration is shown in the UI
|
||||||
|
|
||||||
|
#### Scenario: Terminal tool does not require port
|
||||||
|
- GIVEN a tool type with `interface_type` = "terminal"
|
||||||
|
- WHEN the tool type is created or updated
|
||||||
|
- THEN `requires_port` SHALL default to false
|
||||||
|
- AND port-related configuration is hidden in the UI
|
||||||
|
|
||||||
|
### Requirement: Single interface validation
|
||||||
|
The system SHALL enforce that each tool type has exactly one interface type.
|
||||||
|
|
||||||
|
#### Scenario: Invalid interface type
|
||||||
|
- GIVEN a tool type creation request with `interface_type` = "invalid"
|
||||||
|
- WHEN the request is processed
|
||||||
|
- THEN the system returns 400 Bad Request
|
||||||
|
- AND the error message indicates valid values are "web" or "terminal"
|
||||||
|
|
||||||
|
#### Scenario: Missing interface type
|
||||||
|
- GIVEN a tool type creation request without `interface_type`
|
||||||
|
- WHEN the request is processed
|
||||||
|
- THEN the system returns 400 Bad Request
|
||||||
|
- AND the error message indicates interface_type is required
|
||||||
|
|||||||
Reference in New Issue
Block a user