Skip to content

Feat: Implement GET /setup/{id} endpoint#280

Open
igennova wants to merge 2 commits intoopenml:mainfrom
igennova:issue/61
Open

Feat: Implement GET /setup/{id} endpoint#280
igennova wants to merge 2 commits intoopenml:mainfrom
igennova:issue/61

Conversation

@igennova
Copy link
Contributor

@igennova igennova commented Mar 17, 2026

FIxes: #61

Description

  • Implemented the new GET /setup/{id} endpoint in Python, bridging database input settings into structured setup JSON, identical to the old PHP API.
  • Introduced strongly-typed Pydantic schemas (SetupResponseand SetupParameter) under src/schemas/setups.py strictly representing the returned topology.

Checklist

Always:

  • I have performed a self-review of my own pull request to ensure it contains all relevant information, and the proposed changes are minimal but sufficient to accomplish their task.

Required for code changes:

  • Tests pass locally
  • I have commented my code in hard-to-understand areas, and provided or updated docstrings as needed
  • Changes are already covered under existing tests
  • I have added tests that cover the changes (only required if not already under coverage)

If applicable:

  • I have made corresponding changes to the documentation pages (/docs)

Extra context:

  • This PR and the commits have been created autonomously by a bot/agent.

Screenshot:
Screenshot 2026-03-18 at 1 21 02 AM

@coderabbitai
Copy link
Contributor

coderabbitai bot commented Mar 17, 2026

Walkthrough

Adds database function get_parameters(setup_id: int, connection: AsyncConnection) -> list[RowMapping] to fetch setup parameters. Introduces a new GET endpoint /setup/{setup_id} in the OpenML router that returns setup details including parameters and raises 404 when missing. Adds Pydantic schemas SetupParameter, SetupParameters, and SetupResponse. Adds unit tests for successful and unknown setup retrieval and migration tests comparing PHP and Python API responses. Updates documentation for setup and tagging behaviors.

🚥 Pre-merge checks | ✅ 2 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 45.45% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (2 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly and concisely describes the main change: implementing a new GET /setup/{id} endpoint, which aligns with the core objective of the pull request.
Description check ✅ Passed The pull request description clearly relates to the changeset, explaining the implementation of a GET /setup/{id} endpoint with structured Pydantic schemas.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
📝 Coding Plan
  • Generate coding plan for human review comments

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

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
Contributor

@sourcery-ai sourcery-ai bot left a comment

Choose a reason for hiding this comment

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

Hey - I've found 3 issues

Prompt for AI Agents
Please address the comments from this code review:

## Individual Comments

### Comment 1
<location path="src/database/setups.py" line_range="38-42" />
<code_context>
+                t_input.dataType AS data_type,
+                t_input.defaultValue AS default_value,
+                t_setting.value AS value
+            FROM input_setting t_setting
+            JOIN input t_input ON t_setting.input_id = t_input.id
+            JOIN implementation t_impl ON t_input.implementation_id = t_impl.id
</code_context>
<issue_to_address>
**suggestion:** Consider adding an explicit ORDER BY clause to make parameter ordering deterministic.

Without an `ORDER BY`, the database may return parameters in any order, which can break callers that assume stable ordering (e.g., for diffs, cache keys, or UI). Please add an explicit sort, e.g. `ORDER BY t_impl.id, t_input.id`, to make the result deterministic.

```suggestion
            FROM input_setting t_setting
            JOIN input t_input ON t_setting.input_id = t_input.id
            JOIN implementation t_impl ON t_input.implementation_id = t_impl.id
            WHERE t_setting.setup = :setup_id
            ORDER BY t_impl.id, t_input.id
            """,
```
</issue_to_address>

### Comment 2
<location path="src/schemas/setups.py" line_range="17" />
<code_context>
+    name: str
+    data_type: str | None = None
+    default_value: str | None = None
+    value: str
+
+    model_config = ConfigDict(from_attributes=True)
</code_context>
<issue_to_address>
**issue:** Check whether `value` should be nullable to match the database and avoid validation issues.

Since `t_setting.value` is selected directly and may be NULL at the DB level, `value: str` will cause Pydantic validation failures for such rows. If NULL is valid, use `value: str | None = None` or normalize NULL to a non-null value (e.g., empty string) before model validation to avoid unexpected 500/422 responses.
</issue_to_address>

### Comment 3
<location path="src/routers/openml/setups.py" line_range="36" />
<code_context>
+
+    setup_parameters = await database.setups.get_parameters(setup_id, expdb_db)
+
+    params_model = SetupParameters(
+        setup_id=str(setup_id),
+        flow_id=str(setup.implementation_id),
</code_context>
<issue_to_address>
**issue (complexity):** Consider simplifying the new endpoint by always returning a list for `parameter` and letting Pydantic handle object conversion instead of manually building dicts.

You can simplify the new endpoint a bit without changing behavior.

1. **Always return a list for `parameter`**

Avoid the `None` vs `[]` duality and the extra conditional. This keeps the schema simpler for clients and reduces branching:

```python
params_model = SetupParameters(
    setup_id=str(setup_id),
    flow_id=str(setup.implementation_id),
    parameter=[dict(param) for param in setup_parameters],
)
```

If `setup_parameters` is empty, `parameter` will just be `[]`.

2. **Avoid the dict conversion if possible**

Since you already have Pydantic models and likely `from_attributes=True` on `SetupParameter`, you can skip the `dict` round-trip and let Pydantic do the adaptation. For example, if `get_parameters` returns SQLAlchemy rows, you can validate them directly:

```python
from schemas.setups import SetupParameter

params_model = SetupParameters(
    setup_id=str(setup_id),
    flow_id=str(setup.implementation_id),
    parameter=[
        SetupParameter.model_validate(param, from_attributes=True)
        for param in setup_parameters
    ],
)
```

If this doesn’t work with the current return type of `get_parameters`, consider updating `get_parameters` to return objects with attribute access (`.name`, `.value`, etc.) instead of dict-like mappings, so Pydantic can map them directly.
</issue_to_address>

Sourcery is free for open source - if you like our reviews please consider sharing them ✨
Help me be more useful! Please click 👍 or 👎 on each comment and I'll use the feedback to improve your reviews.

@codecov
Copy link

codecov bot commented Mar 17, 2026

Codecov Report

❌ Patch coverage is 65.38462% with 9 lines in your changes missing coverage. Please review.
⚠️ Please upload report for BASE (main@5357b01). Learn more about missing BASE report.

Files with missing lines Patch % Lines
src/routers/openml/setups.py 36.36% 7 Missing ⚠️
src/database/setups.py 50.00% 2 Missing ⚠️
Additional details and impacted files
@@           Coverage Diff           @@
##             main     #280   +/-   ##
=======================================
  Coverage        ?   54.00%           
=======================================
  Files           ?       35           
  Lines           ?     1509           
  Branches        ?      125           
=======================================
  Hits            ?      815           
  Misses          ?      692           
  Partials        ?        2           

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

Copy link
Contributor

@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.

🧹 Nitpick comments (2)
src/database/setups.py (1)

28-42: Stabilize parameter ordering in the SQL result.

At Line 41, the query filters but does not sort. Without ORDER BY, parameter list order is not guaranteed and can become flaky for response comparisons.

Suggested diff
             FROM input_setting t_setting
             JOIN input t_input ON t_setting.input_id = t_input.id
             JOIN implementation t_impl ON t_input.implementation_id = t_impl.id
             WHERE t_setting.setup = :setup_id
+            ORDER BY t_input.id
             """,
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/database/setups.py` around lines 28 - 42, The SQL query selecting
inputs/settings (the SELECT joining input_setting, input, implementation and
filtering by :setup_id) lacks a deterministic ORDER BY causing flaky parameter
ordering; update the query to append an ORDER BY clause (for example ORDER BY
t_input.implementation_id, t_input.name or ORDER BY t_impl.name, t_input.name)
so results are consistently sorted by flow and parameter name (refer to the
SELECT block, tables input_setting/input/implementation, and the :setup_id
filter).
tests/routers/openml/setups_test.py (1)

141-146: Strengthen the success-path assertions to lock the response contract.

Right now the test only checks setup_id and key presence. Adding a few schema-level assertions would catch more regressions.

Suggested diff
 async def test_get_setup_success(py_api: httpx.AsyncClient) -> None:
     response = await py_api.get("/setup/1")
     assert response.status_code == HTTPStatus.OK
     data = response.json()["setup_parameters"]
     assert data["setup_id"] == "1"
-    assert "parameter" in data
+    assert "flow_id" in data
+    assert isinstance(data.get("parameter"), list)
+    assert data["parameter"], "Expected setup 1 to contain at least one parameter"
+    first = data["parameter"][0]
+    assert {"id", "flow_id", "flow_name", "full_name", "parameter_name", "name", "value"} <= set(first)
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@tests/routers/openml/setups_test.py` around lines 141 - 146, The
test_get_setup_success should lock the response contract: assert that the
top-level response is a dict and contains only the expected keys for the setup
payload (verify set(data.keys()) == {'setup_id', 'parameter'}), assert
type(data['setup_id']) is str and equals "1", assert that data['parameter'] is
the expected container type (dict or list) and is non-empty, and if it's a list
assert each item is a dict with at least 'name' and 'value' keys (if it's a dict
assert it contains 'name' and 'value'); update the test_get_setup_success
assertions accordingly to enforce these schema-level checks.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Nitpick comments:
In `@src/database/setups.py`:
- Around line 28-42: The SQL query selecting inputs/settings (the SELECT joining
input_setting, input, implementation and filtering by :setup_id) lacks a
deterministic ORDER BY causing flaky parameter ordering; update the query to
append an ORDER BY clause (for example ORDER BY t_input.implementation_id,
t_input.name or ORDER BY t_impl.name, t_input.name) so results are consistently
sorted by flow and parameter name (refer to the SELECT block, tables
input_setting/input/implementation, and the :setup_id filter).

In `@tests/routers/openml/setups_test.py`:
- Around line 141-146: The test_get_setup_success should lock the response
contract: assert that the top-level response is a dict and contains only the
expected keys for the setup payload (verify set(data.keys()) == {'setup_id',
'parameter'}), assert type(data['setup_id']) is str and equals "1", assert that
data['parameter'] is the expected container type (dict or list) and is
non-empty, and if it's a list assert each item is a dict with at least 'name'
and 'value' keys (if it's a dict assert it contains 'name' and 'value'); update
the test_get_setup_success assertions accordingly to enforce these schema-level
checks.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro

Run ID: 33a4cdd7-cfcd-4b24-9a38-8284b29f797c

📥 Commits

Reviewing files that changed from the base of the PR and between 5357b01 and fb34197.

📒 Files selected for processing (5)
  • src/database/setups.py
  • src/routers/openml/setups.py
  • src/schemas/setups.py
  • tests/routers/openml/migration/setups_migration_test.py
  • tests/routers/openml/setups_test.py

@igennova
Copy link
Contributor Author

@PGijsbers I intentionally built explicit BaseModel schemas under src/schemas/setups.py instead of directly throwing a dictionary out. This way, the API response is cleanly typed, strongly validated.

The PR is ready for review. Thank you

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.

GET /setup/{id}

1 participant