refactor(service_api): dep-inject the dataset create payload with @model_validate (#41651)

This commit is contained in:
Eddy ZHANG authored and GitHub committed 2026-09-03 03:52:41 +00:00
1 parent 59021616d4
commit fc7193cf04
2 files changed
+32 -8

No files matched your search

@@ -504,10 +504,9 @@ class DatasetListApi(DatasetApiResource):
)
@cloud_edition_billing_rate_limit_check("knowledge", "dataset")
@with_session
def post(self, session: Session, tenant_id):
@model_validate(DatasetCreatePayload)
def post(self, payload: DatasetCreatePayload, session: Session, tenant_id):
"""Resource for creating datasets."""
payload = DatasetCreatePayload.model_validate(service_api_ns.payload or {})
embedding_model_provider = payload.embedding_model_provider
embedding_model = payload.embedding_model
if embedding_model_provider and embedding_model:
@@ -7,12 +7,13 @@ persisted through the shared SQLite session fixture.
import uuid
from datetime import UTC, datetime
from inspect import unwrap
from types import SimpleNamespace
from unittest.mock import MagicMock, patch
import pytest
from flask import Flask, request
from sqlalchemy.orm import Session
from werkzeug.exceptions import Forbidden, NotFound
from werkzeug.exceptions import Forbidden, NotFound, UnprocessableEntity
import services
from controllers.service_api.dataset.error import DatasetInUseError, DatasetNameDuplicateError, InvalidActionError
@@ -269,7 +270,7 @@ class TestDatasetListApiPost:
tenant: Tenant,
controller_session: Session,
) -> None:
from controllers.service_api.dataset.dataset import DatasetListApi
from controllers.service_api.dataset.dataset import DatasetCreatePayload, DatasetListApi
mock_dataset_svc.create_empty_dataset.return_value = make_dataset(
controller_session, tenant, account, name="New Dataset"
@@ -281,7 +282,10 @@ class TestDatasetListApiPost:
json={"name": "New Dataset"},
):
api = DatasetListApi()
response, status = unwrap(api.post)(api, controller_session, tenant_id=tenant.id)
# `post` is wrapped in @model_validate, so the unwrapped view expects
# the validated model where the decorator would have injected it.
validated_payload = DatasetCreatePayload.model_validate(request.get_json() or {})
response, status = unwrap(api.post)(api, validated_payload, controller_session, tenant_id=tenant.id)
assert status == 200
assert_dataset_detail_shape(response)
@@ -297,7 +301,7 @@ class TestDatasetListApiPost:
tenant: Tenant,
controller_session: Session,
) -> None:
from controllers.service_api.dataset.dataset import DatasetListApi
from controllers.service_api.dataset.dataset import DatasetCreatePayload, DatasetListApi
mock_dataset_svc.create_empty_dataset.side_effect = services.errors.dataset.DatasetNameDuplicateError()
@@ -307,8 +311,29 @@ class TestDatasetListApiPost:
json={"name": "Existing Dataset"},
):
api = DatasetListApi()
validated_payload = DatasetCreatePayload.model_validate(request.get_json() or {})
with pytest.raises(DatasetNameDuplicateError):
unwrap(api.post)(api, controller_session, tenant_id=tenant.id)
unwrap(api.post)(api, validated_payload, controller_session, tenant_id=tenant.id)
@pytest.mark.usefixtures("account")
@patch("controllers.service_api.wraps.FeatureService")
@patch("controllers.service_api.wraps.validate_and_get_api_token")
def test_invalid_body_is_rejected_before_the_handler_runs(
self,
mock_validate_token: MagicMock,
mock_feature_svc: MagicMock,
app: Flask,
tenant: Tenant,
) -> None:
"""The tests above unwrap the view, so this is what covers the decorator."""
from controllers.service_api.dataset.dataset import DatasetListApi
mock_validate_token.return_value = SimpleNamespace(tenant_id=tenant.id)
mock_feature_svc.get_knowledge_rate_limit.return_value = SimpleNamespace(enabled=False)
with app.test_request_context("/datasets", method="POST", json={}):
with pytest.raises(UnprocessableEntity):
DatasetListApi().post(tenant_id=tenant.id)
# ---------------------------------------------------------------------------