Skip to content

Comments

Migrate POST /setup/untag endpoint (#65)#246

Open
igennova wants to merge 1 commit intoopenml:mainfrom
igennova:issue/65
Open

Migrate POST /setup/untag endpoint (#65)#246
igennova wants to merge 1 commit intoopenml:mainfrom
igennova:issue/65

Conversation

@igennova
Copy link

Fixes #65

This PR migrates the POST /setup/untag endpoint from the legacy PHP API to the new FastAPI backend. It is the first endpoint migrated under the Setup Epic (#60).

Changes:

  • Added database.setups with [untag()] helper.
  • Created [routers/openml/setups.py] containing the POST /untag routing logic.
  • Implemented legacy error code parity (472, 475, 476, and 103 for auth).

API Response

Screenshot 2026-02-21 at 2 16 16 AM

@coderabbitai
Copy link
Contributor

coderabbitai bot commented Feb 20, 2026

Walkthrough

This pull request implements the POST /setup/untag endpoint for the OpenML API. It adds three database utility functions in src/database/setups.py to support setup queries and tag removal. A new router module in src/routers/openml/setups.py exposes the untag endpoint with authentication verification, ownership enforcement, and error handling. The router is registered in src/main.py. A parameterized test file validates the endpoint behavior across multiple scenarios including different user roles and setup states.

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed The title 'Migrate POST /setup/untag endpoint (#65)' clearly describes the main change - migrating a specific endpoint from PHP to FastAPI.
Description check ✅ Passed The description is related to the changeset, explaining the migration of the POST /setup/untag endpoint, changes made, and error code parity implementation.
Linked Issues check ✅ Passed The PR addresses issue #65 by implementing the POST /setup/untag endpoint migration with database utilities, routing logic, and legacy error code parity as specified.
Out of Scope Changes check ✅ Passed All changes (database utilities, router setup, endpoint implementation, and tests) are directly related to migrating the POST /setup/untag endpoint as stated in the linked issue.

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

✨ Finishing Touches
  • 📝 Generate docstrings (stacked PR)
  • 📝 Generate docstrings (commit on current branch)
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment

Tip

Issue Planner is now in beta. Read the docs and try it out! Share your feedback on Discord.


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 left some high level feedback:

  • The error responses for codes 472/475/476 are being constructed inline in untag_setup; consider extracting small helper functions similar to create_authentication_failed_error() so the legacy error shapes are centralized and easier to keep consistent across future migrated endpoints.
  • In untag_setup, the dependency-injected parameters (user, expdb_db) are annotated with | None and given = None defaults even though Depends(...) guarantees they are present; tightening these types (and dropping the None defaults) will make the function signature clearer and help static checkers.
Prompt for AI Agents
Please address the comments from this code review:

## Overall Comments
- The error responses for codes 472/475/476 are being constructed inline in `untag_setup`; consider extracting small helper functions similar to `create_authentication_failed_error()` so the legacy error shapes are centralized and easier to keep consistent across future migrated endpoints.
- In `untag_setup`, the dependency-injected parameters (`user`, `expdb_db`) are annotated with `| None` and given `= None` defaults even though `Depends(...)` guarantees they are present; tightening these types (and dropping the `None` defaults) will make the function signature clearer and help static checkers.

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.

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.

Actionable comments posted: 1

🧹 Nitpick comments (2)
tests/routers/openml/migration/setups_migration_test.py (1)

31-35: Silent pre-tag failure can mask test-ordering issues.

The pre-tag result is discarded. If setup_tag has a UNIQUE(id, tag) constraint and a previous test run left the tag in place (e.g., after a crash mid-test), the pre-tag silently fails but the leftover tag satisfies the next test — acceptable in practice, but noting it here for awareness.

Consider asserting the pre-tag succeeds to make state prerequisites explicit:

-        php_api.post(
-            "/setup/tag",
-            data={"api_key": ApiKey.SOME_USER, "tag": tag, "setup_id": setup_id},
-        )
+        pre_tag = php_api.post(
+            "/setup/tag",
+            data={"api_key": ApiKey.SOME_USER, "tag": tag, "setup_id": setup_id},
+        )
+        assert pre_tag.status_code in (HTTPStatus.OK, HTTPStatus.PRECONDITION_FAILED)
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@tests/routers/openml/migration/setups_migration_test.py` around lines 31 -
35, The pre-tag POST call to "/setup/tag" (invoked when setup_id == 1 via
php_api.post with ApiKey.SOME_USER, tag, and setup_id) currently discards the
response so a silent failure can hide leftover state; update the test to capture
the response from php_api.post("/setup/tag", ...) and assert it indicates
success (e.g., assert status code is 200 or response.json()["ok"] is True) so
the precondition is explicitly verified before continuing.
src/routers/openml/setups.py (1)

27-27: expdb_db type annotation is Connection, not Connection | None — minor annotation mismatch with = None default.

FastAPI always injects the dependency so None is never used at runtime, but static type checkers will flag this.

🔧 Suggested fix
-    expdb_db: Annotated[Connection, Depends(expdb_connection)] = None,
+    expdb_db: Annotated[Connection, Depends(expdb_connection)],
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/routers/openml/setups.py` at line 27, The parameter expdb_db in the
function signature is annotated as Connection but given a default of None, which
mismatches static typing; update the signature for the parameter expdb_db to use
an optional type (e.g., Connection | None or Optional[Connection]) and add the
corresponding import if needed, or remove the "= None" default and rely on
FastAPI's dependency injection; refer to the expdb_db parameter and
expdb_connection dependency in the setup function to locate the change.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@tests/routers/openml/migration/setups_migration_test.py`:
- Around line 15-19: The parameterized test for "api_key" omits invalid/missing
keys so the router branch that triggers create_authentication_failed_error
(error code 103) is never exercised; update the pytest.mark.parametrize list for
"api_key" to include ApiKey.INVALID (and/or None) and add corresponding id(s)
(e.g., "invalid key" or "no key") so the test runs the authentication-failure
path and asserts the 103 response in the test that uses ApiKey.ADMIN /
ApiKey.SOME_USER / ApiKey.OWNER_USER currently.

---

Nitpick comments:
In `@src/routers/openml/setups.py`:
- Line 27: The parameter expdb_db in the function signature is annotated as
Connection but given a default of None, which mismatches static typing; update
the signature for the parameter expdb_db to use an optional type (e.g.,
Connection | None or Optional[Connection]) and add the corresponding import if
needed, or remove the "= None" default and rely on FastAPI's dependency
injection; refer to the expdb_db parameter and expdb_connection dependency in
the setup function to locate the change.

In `@tests/routers/openml/migration/setups_migration_test.py`:
- Around line 31-35: The pre-tag POST call to "/setup/tag" (invoked when
setup_id == 1 via php_api.post with ApiKey.SOME_USER, tag, and setup_id)
currently discards the response so a silent failure can hide leftover state;
update the test to capture the response from php_api.post("/setup/tag", ...) and
assert it indicates success (e.g., assert status code is 200 or
response.json()["ok"] is True) so the precondition is explicitly verified before
continuing.

Comment on lines +15 to +19
@pytest.mark.parametrize(
"api_key",
[ApiKey.ADMIN, ApiKey.SOME_USER, ApiKey.OWNER_USER],
ids=["Administrator", "regular user", "possible owner"],
)
Copy link
Contributor

@coderabbitai coderabbitai bot Feb 20, 2026

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

Authentication failure (error code 103) is not covered by migration tests.

ApiKey.INVALID (or an absent key) is excluded from the api_key parametrize list, so the create_authentication_failed_error path in the router is never exercised here. Consider adding ApiKey.INVALID or None as a case to validate parity for the 103 response.

✅ Suggested addition
 `@pytest.mark.parametrize`(
     "api_key",
-    [ApiKey.ADMIN, ApiKey.SOME_USER, ApiKey.OWNER_USER],
-    ids=["Administrator", "regular user", "possible owner"],
+    [ApiKey.ADMIN, ApiKey.SOME_USER, ApiKey.OWNER_USER, ApiKey.INVALID],
+    ids=["Administrator", "regular user", "possible owner", "invalid key"],
 )
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
@pytest.mark.parametrize(
"api_key",
[ApiKey.ADMIN, ApiKey.SOME_USER, ApiKey.OWNER_USER],
ids=["Administrator", "regular user", "possible owner"],
)
`@pytest.mark.parametrize`(
"api_key",
[ApiKey.ADMIN, ApiKey.SOME_USER, ApiKey.OWNER_USER, ApiKey.INVALID],
ids=["Administrator", "regular user", "possible owner", "invalid key"],
)
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@tests/routers/openml/migration/setups_migration_test.py` around lines 15 -
19, The parameterized test for "api_key" omits invalid/missing keys so the
router branch that triggers create_authentication_failed_error (error code 103)
is never exercised; update the pytest.mark.parametrize list for "api_key" to
include ApiKey.INVALID (and/or None) and add corresponding id(s) (e.g., "invalid
key" or "no key") so the test runs the authentication-failure path and asserts
the 103 response in the test that uses ApiKey.ADMIN / ApiKey.SOME_USER /
ApiKey.OWNER_USER currently.

Copy link
Author

@igennova igennova Feb 20, 2026

Choose a reason for hiding this comment

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

I intentionally omitted ApiKey.INVALID from the migration test parameters because the legacy PHP API has a known bug where it returns raw XML instead of JSON when authentication fails for setup queries (Code 103). Because our migration test framework strictly relies on original.json() == new.json() to prove parity, testing the invalid key pathway directly crashes Pytest with a JSONDecodeError.

  • We will be able to test the 103 Authentication failure fully and properly in standard Python unit tests once POST /setup/tag is migrated natively in my follow up PR and we no longer have to rely on php_api to stage the data.

Copy link
Contributor

Choose a reason for hiding this comment

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

@igennova, thank you for the detailed explanation! That makes perfect sense—if the legacy PHP API returns raw XML for Code 103 errors while the new API returns JSON, the migration test would indeed fail at the original.json() deserialization step before any comparison could happen.

Your plan to test authentication failure properly in standard unit tests once POST /setup/tag is migrated is the right approach. At that point, you won't need php_api to stage the data, and you'll be able to validate the 103 error path cleanly.

One optional suggestion: consider adding a brief comment in the test file or a TODO note explaining why ApiKey.INVALID is excluded from the parameterization, so future maintainers understand the constraint.


✏️ Learnings added
Learnt from: igennova
Repo: openml/server-api PR: 246
File: tests/routers/openml/migration/setups_migration_test.py:15-19
Timestamp: 2026-02-20T21:20:18.622Z
Learning: Migration tests for OpenML API endpoints that rely on php_api for data staging cannot test authentication failure paths (Code 103) when the legacy PHP API returns XML instead of JSON. These paths should be tested in standard Python unit tests after dependent endpoints are migrated.

Note: Learnings are effective only in the context of similar code segments. To apply general coding guidelines, please configure review instructions. You can manage existing learnings in the app.

@igennova
Copy link
Author

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

POST /setup/untag

1 participant