fix(api): stop returning 204 with response body and add CI check (#36489)

This commit is contained in:
chariri
2026-05-22 01:20:34 +09:00
committed by GitHub
parent f19702f76c
commit ea5e487d3c
29 changed files with 46 additions and 46 deletions

View File

@ -47,6 +47,10 @@ jobs:
if: steps.changed-files.outputs.any_changed == 'true'
run: uv run --directory api --dev lint-imports
- name: Run Response Contract Linter
if: steps.changed-files.outputs.any_changed == 'true'
run: uv run --project api --dev python api/dev/lint_response_contracts.py --fail-on-mismatch
- name: Run Type Checks
if: steps.changed-files.outputs.any_changed == 'true'
run: make type-check-core

View File

@ -146,7 +146,7 @@ class BaseApiKeyResource(Resource):
db.session.execute(delete(ApiToken).where(ApiToken.id == api_key_id))
db.session.commit()
return {"result": "success"}, 204
return "", 204
@console_ns.route("/apps/<uuid:resource_id>/api-keys")

View File

@ -269,12 +269,12 @@ class AnnotationApi(Resource):
"message": "annotation_ids are required if the parameter is provided.",
}, 400
result = AppAnnotationService.delete_app_annotations_in_batch(str(app_id), annotation_ids)
return result, 204
AppAnnotationService.delete_app_annotations_in_batch(str(app_id), annotation_ids)
return "", 204
# If no annotation_ids are provided, handle clearing all annotations
else:
AppAnnotationService.clear_all_annotations(str(app_id))
return {"result": "success"}, 204
return "", 204
@console_ns.route("/apps/<uuid:app_id>/annotations/export")
@ -335,7 +335,7 @@ class AnnotationUpdateDeleteApi(Resource):
@edit_permission_required
def delete(self, app_id: UUID, annotation_id: UUID):
AppAnnotationService.delete_app_annotation(str(app_id), str(annotation_id))
return {"result": "success"}, 204
return "", 204
@console_ns.route("/apps/<uuid:app_id>/annotations/batch-import")

View File

@ -633,7 +633,7 @@ class AppApi(Resource):
app_service = AppService()
app_service.delete_app(app_model)
return {"result": "success"}, 204
return "", 204
@console_ns.route("/apps/<uuid:app_id>/copy")

View File

@ -29,9 +29,6 @@ from fields.conversation_fields import (
from fields.conversation_fields import (
ConversationWithSummaryPagination as ConversationWithSummaryPaginationResponse,
)
from fields.conversation_fields import (
ResultResponse,
)
from libs.datetime_utils import naive_utc_now, parse_time_range
from libs.login import current_account_with_tenant, login_required
from models import Conversation, EndUser, Message, MessageAnnotation
@ -77,7 +74,6 @@ register_schema_models(
ConversationMessageDetailResponse,
ConversationWithSummaryPaginationResponse,
ConversationDetailResponse,
ResultResponse,
CompletionConversationQuery,
ChatConversationQuery,
)
@ -194,7 +190,7 @@ class CompletionConversationDetailApi(Resource):
except ConversationNotExistsError:
raise NotFound("Conversation Not Exists.")
return ResultResponse(result="success").model_dump(mode="json"), 204
return "", 204
@console_ns.route("/apps/<uuid:app_id>/chat-conversations")
@ -347,7 +343,7 @@ class ChatConversationDetailApi(Resource):
except ConversationNotExistsError:
raise NotFound("Conversation Not Exists.")
return ResultResponse(result="success").model_dump(mode="json"), 204
return "", 204
def _get_conversation(app_model, conversation_id):

View File

@ -128,6 +128,6 @@ class TraceAppConfigApi(Resource):
result = OpsService.delete_tracing_app_config(app_id=app_model.id, tracing_provider=args.tracing_provider)
if not result:
raise TracingConfigNotExist()
return {"result": "success"}, 204
return "", 204
except Exception as e:
raise BadRequest(str(e))

View File

@ -311,7 +311,7 @@ class WorkflowCommentDetailApi(Resource):
user_id=current_user.id,
)
return {"result": "success"}, 204
return "", 204
@console_ns.route("/apps/<uuid:app_id>/workflow/comments/<string:comment_id>/resolve")
@ -431,7 +431,7 @@ class WorkflowCommentReplyDetailApi(Resource):
user_id=current_user.id,
)
return {"result": "success"}, 204
return "", 204
@console_ns.route("/apps/<uuid:app_id>/workflow/comments/mention-users")

View File

@ -93,4 +93,4 @@ class ApiKeyAuthDataSourceBindingDelete(Resource):
ApiKeyAuthService.delete_provider_auth(current_tenant_id, binding_id)
return {"result": "success"}, 204
return "", 204

View File

@ -535,7 +535,7 @@ class DatasetApi(Resource):
try:
if DatasetService.delete_dataset(dataset_id_str, current_user):
DatasetPermissionService.clear_partial_member_list(dataset_id_str)
return {"result": "success"}, 204
return "", 204
else:
raise NotFound("Dataset not found.")
except services.errors.dataset.DatasetInUseError:
@ -873,7 +873,7 @@ class DatasetApiDeleteApi(Resource):
db.session.delete(key)
db.session.commit()
return {"result": "success"}, 204
return "", 204
@console_ns.route("/datasets/<uuid:dataset_id>/api-keys/<string:status>")

View File

@ -504,7 +504,7 @@ class DatasetDocumentListApi(Resource):
except services.errors.document.DocumentIndexingError:
raise DocumentIndexingError("Cannot delete document during indexing.")
return {"result": "success"}, 204
return "", 204
@console_ns.route("/datasets/init")
@ -966,7 +966,7 @@ class DocumentApi(DocumentResource):
except services.errors.document.DocumentIndexingError:
raise DocumentIndexingError("Cannot delete document during indexing.")
return {"result": "success"}, 204
return "", 204
@console_ns.route("/datasets/<uuid:dataset_id>/documents/<uuid:document_id>/download")
@ -1204,7 +1204,7 @@ class DocumentPauseApi(DocumentResource):
except services.errors.document.DocumentIndexingError:
raise DocumentIndexingError("Cannot pause completed document.")
return {"result": "success"}, 204
return "", 204
@console_ns.route("/datasets/<uuid:dataset_id>/documents/<uuid:document_id>/processing/resume")
@ -1236,7 +1236,7 @@ class DocumentRecoverApi(DocumentResource):
except services.errors.document.DocumentIndexingError:
raise DocumentIndexingError("Document is not in paused status.")
return {"result": "success"}, 204
return "", 204
@console_ns.route("/datasets/<uuid:dataset_id>/retry")
@ -1279,7 +1279,7 @@ class DocumentRetryApi(DocumentResource):
# retry document
DocumentService.retry_document(dataset_id, retry_documents)
return {"result": "success"}, 204
return "", 204
@console_ns.route("/datasets/<uuid:dataset_id>/documents/<uuid:document_id>/rename")

View File

@ -251,7 +251,7 @@ class DatasetDocumentSegmentListApi(Resource):
except services.errors.account.NoPermissionError as e:
raise Forbidden(str(e))
SegmentService.delete_segments(segment_ids, document, dataset)
return {"result": "success"}, 204
return "", 204
@console_ns.route("/datasets/<uuid:dataset_id>/documents/<uuid:document_id>/segment/<string:action>")
@ -467,7 +467,7 @@ class DatasetDocumentSegmentUpdateApi(Resource):
except services.errors.account.NoPermissionError as e:
raise Forbidden(str(e))
SegmentService.delete_segment(segment, document, dataset)
return {"result": "success"}, 204
return "", 204
@console_ns.route(
@ -754,7 +754,7 @@ class ChildChunkUpdateApi(Resource):
SegmentService.delete_child_chunk(child_chunk, dataset)
except ChildChunkDeleteIndexServiceError as e:
raise ChildChunkDeleteIndexError(str(e))
return {"result": "success"}, 204
return "", 204
@setup_required
@login_required

View File

@ -218,7 +218,7 @@ class ExternalApiTemplateApi(Resource):
raise Forbidden()
ExternalDatasetService.delete_external_knowledge_api(current_tenant_id, external_knowledge_api_id)
return {"result": "success"}, 204
return "", 204
@console_ns.route("/datasets/external-knowledge-api/<uuid:external_knowledge_api_id>/use-check")

View File

@ -105,7 +105,7 @@ class ConversationApi(InstalledAppResource):
except ConversationNotExistsError:
raise NotFound("Conversation Not Exists.")
return ResultResponse(result="success").model_dump(mode="json"), 204
return "", 204
@console_ns.route(

View File

@ -270,7 +270,7 @@ class InstalledAppApi(InstalledAppResource):
db.session.delete(installed_app)
db.session.commit()
return {"result": "success", "message": "App uninstalled successfully"}, 204
return "", 204
@console_ns.response(200, "Success", console_ns.models[SimpleResultMessageResponse.__name__])
def patch(self, installed_app):

View File

@ -76,4 +76,4 @@ class SavedMessageApi(InstalledAppResource):
SavedMessageService.delete(app_model, current_user, message_id)
return ResultResponse(result="success").model_dump(mode="json"), 204
return "", 204

View File

@ -204,4 +204,4 @@ class APIBasedExtensionDetailAPI(Resource):
APIBasedExtensionService.delete(extension_data_from_db)
return {"result": "success"}, 204
return "", 204

View File

@ -194,7 +194,7 @@ class ModelProviderCredentialApi(Resource):
tenant_id=current_tenant_id, provider=provider, credential_id=args.credential_id
)
return {"result": "success"}, 204
return "", 204
@console_ns.route("/workspaces/current/model-providers/<path:provider>/credentials/switch")

View File

@ -259,7 +259,7 @@ class ModelProviderModelApi(Resource):
tenant_id=tenant_id, provider=provider, model=args.model, model_type=args.model_type
)
return {"result": "success"}, 204
return "", 204
@console_ns.route("/workspaces/current/model-providers/<path:provider>/models/credentials")
@ -395,7 +395,7 @@ class ModelProviderModelCredentialApi(Resource):
credential_id=args.credential_id,
)
return {"result": "success"}, 204
return "", 204
@console_ns.route("/workspaces/current/model-providers/<path:provider>/models/credentials/switch")

View File

@ -136,7 +136,7 @@ class ConversationApi(WebApiResource):
ConversationService.delete(app_model, conversation_id, end_user)
except ConversationNotExistsError:
raise NotFound("Conversation Not Exists.")
return ResultResponse(result="success").model_dump(mode="json"), 204
return "", 204
@web_ns.route("/conversations/<uuid:c_id>/name")

View File

@ -112,4 +112,4 @@ class SavedMessageApi(WebApiResource):
SavedMessageService.delete(app_model, end_user, message_id)
return ResultResponse(result="success").model_dump(mode="json"), 204
return "", 204

View File

@ -128,7 +128,7 @@ class TestConversationApi:
body, status = result
assert status == 204
assert body["result"] == "success"
assert body == ""
def test_delete_not_found(self, app: Flask, chat_app, user):
api = conversation_module.ConversationApi()

View File

@ -81,7 +81,7 @@ class TestConversationApi:
result, status = ConversationApi().delete(_chat_app(), _end_user(), c_id)
assert status == 204
assert result["result"] == "success"
assert result == ""
@patch("controllers.web.conversation.ConversationService.delete", side_effect=ConversationNotExistsError())
def test_delete_not_found(self, mock_delete: MagicMock, app: Flask) -> None:

View File

@ -873,7 +873,7 @@ class TestDatasetApiDelete:
result, status = method(api, dataset_id)
assert status == 204
assert result == {"result": "success"}
assert result == ""
def test_delete_forbidden_no_permission(self, app: Flask):
api = DatasetApi()
@ -1687,7 +1687,7 @@ class TestDatasetApiDeleteApi:
response, status = method(api, "api-key-id")
assert status == 204
assert response["result"] == "success"
assert response == ""
def test_delete_key_not_found(self, app: Flask):
api = DatasetApiDeleteApi()

View File

@ -940,7 +940,7 @@ class TestChildChunkUpdateApi:
response, status = method(api, "ds-1", "doc-1", "seg-1", "cc-1")
assert status == 204
assert response["result"] == "success"
assert response == ""
def test_delete_child_chunk_index_error(self, app: Flask):
api = ChildChunkUpdateApi()

View File

@ -327,7 +327,7 @@ class TestInstalledAppApi:
resp, status = method(installed_app)
assert status == 204
assert resp["result"] == "success"
assert resp == ""
def test_delete_owned_by_current_tenant(self, tenant_id: str):
api = module.InstalledAppApi()

View File

@ -141,7 +141,7 @@ class TestSavedMessageApi:
delete_mock.assert_called_once()
assert status == 204
assert result == {"result": "success"}
assert result == ""
def test_delete_not_completion_app(self):
api = module.SavedMessageApi()

View File

@ -242,5 +242,5 @@ def test_api_based_extension_detail_delete_removes_extension(app: Flask, monkeyp
response, status = APIBasedExtensionDetailAPI().delete(extension_id)
delete_mock.assert_called_once_with(existing_extension)
assert response == {"result": "success"}
assert status == 204
assert response == ""

View File

@ -179,8 +179,8 @@ class TestModelProviderCredentialApi:
):
result, status = method(api, provider="openai")
assert result["result"] == "success"
assert status == 204
assert result == ""
class TestModelProviderCredentialSwitchApi:

View File

@ -94,4 +94,4 @@ class TestSavedMessageApi:
result, status = SavedMessageApi().delete(_completion_app(), _end_user(), msg_id)
assert status == 204
assert result["result"] == "success"
assert result == ""