Skip to content

report: align OpenAPI schemas with actual API responses and introduce DTO service contracts - #342

Open
ol-zayatsm wants to merge 3 commits into
ts-factory:mainfrom
ol-zayatsm:reports_swagger_update
Open

report: align OpenAPI schemas with actual API responses and introduce DTO service contracts#342
ol-zayatsm wants to merge 3 commits into
ts-factory:mainfrom
ol-zayatsm:reports_swagger_update

Conversation

@ol-zayatsm

@ol-zayatsm ol-zayatsm commented Jul 3, 2026

Copy link
Copy Markdown
Contributor

Description

Summary

Update API v2 OpenAPI schema definitions for report endpoints and introduce
DTOs for report service responses to keep API documentation and service-layer
contracts aligned with current response payloads.

Changes

  • Add schema for the GET /report/{run_id}/configs endpoint.
  • Add schema for the GET /report/{run_id} endpoint.
  • Document the required config query parameter for report generation.
  • Add serializers for report configuration, generated report, and
    unprocessed iterations.
  • Add DTOs for report configurations, generated reports, and unprocessed
    iterations.
  • Return DTOs from report service methods instead of dictionaries and tuples.
  • Serialize DTO-backed service responses in report endpoints.
  • Document success (200) and error (400, 404) responses.
  • Keep OpenAPI definitions aligned with the current report service API.

@ol-zayatsm
ol-zayatsm force-pushed the reports_swagger_update branch from b4b7391 to 725b522 Compare July 4, 2026 16:18
@ol-zayatsm ol-zayatsm changed the title report: align OpenAPI schemas with actual API responses report: align OpenAPI schemas with actual API responses and introduce DTO service contracts Jul 4, 2026
response=ReportConfigListResponseSerializer,
description='Configurations were successfully retrieved',
),
},

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Missing 404 — self.get_object() can raise it if run_id doesn't exist.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Fixed.

class ReportRetrieveResponseSerializer(serializers.Serializer):
warnings = serializers.ListField(child=serializers.CharField())
config = ReportConfigSerializer()
content = serializers.ListField(child=serializers.DictField())

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

content has a fully known 4-level structure (test → args-vals → measurement → record), each with fixed fields — DictField() loses all of it in the generated schema. Consider proper nested serializers for all four levels.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Fixed.

@ol-zayatsm
ol-zayatsm marked this pull request as ready for review July 7, 2026 19:54
@ol-zayatsm
ol-zayatsm force-pushed the reports_swagger_update branch from 725b522 to 35826d7 Compare July 8, 2026 19:47
Comment thread bublik/core/report/services.py Outdated
exclude=['type', 'is_active', 'user', 'content'],
RunReportConfigDTO(
id=report_config.id,
created=report_config.created,

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

created wasn't in the old response — model_to_dict() silently drops it, since auto_now_add=True makes it non-editable and non-editable fields are skipped regardless of exclude. This PR is meant to align the schema with the actual response, not change it, so please remove created from RunReportConfigDTO (and the serializer) to keep the response as it was.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Fixed.

Comment thread bublik/core/report/dto.py
name: str
description: str
version: int
content: dict[str, Any]

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

nit: ReportConfigDTO doubles as both the response contract (only name/description/version are serialized) and the internal carrier of content used to build the report. Not a bug, but could be confusing later. If you want to keep the DTO pattern consistent with the rest of the PR, consider a small internal-only wrapper (e.g. ReportConfigContentDTO{config: ReportConfigDTO, content: dict}) instead of embedding content in the response DTO — up to you, not blocking.

@ol-zayatsm ol-zayatsm Jul 15, 2026

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Added.

@ol-zayatsm
ol-zayatsm force-pushed the reports_swagger_update branch from 35826d7 to 26a5fbf Compare July 15, 2026 10:59
Comment thread bublik/core/report/services.py Outdated
@ol-zayatsm
ol-zayatsm force-pushed the reports_swagger_update branch 2 times, most recently from eb3ac25 to ffda007 Compare July 16, 2026 16:25
Comment thread bublik/core/report/services.py Outdated

Returns:
Tuple of (config_obj, config_data, config_content)
ReportConfigDTO

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

nit: docstring says Returns: ReportConfigDTO, but the method now returns ReportConfigContentDTO (signature says so too). Please update the docstring to match.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Fixed.

"""
try:
report_config_obj = Config.objects.get(id=config_id)
except ObjectDoesNotExist as e:

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Non-numeric config (e.g. ?config=abc) raises a plain ValueError here, which isn't caught — it bubbles up as a 500 instead of 400. Please catch ValueError alongside ObjectDoesNotExist.

This is a behavior fix (500 → 400), unrelated to the DTO/schema refactor — please put it in its own commit before the introduce DTO-based service contracts commit, not mixed into it.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Fixed.

@ol-zayatsm
ol-zayatsm force-pushed the reports_swagger_update branch 3 times, most recently from 792ddde to 3876a39 Compare July 31, 2026 09:00
Comment thread bublik/core/report/services.py Outdated
from __future__ import annotations

from django.core.exceptions import ObjectDoesNotExist
from django.core.exceptions import ObjectDoesNotExist, ValidationError

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

ValidationError here is django.core.exceptions.ValidationError, not DRF's. It's not an APIException, so DRF's exception_handler returns None for it and this still falls through to a 500 (verified by running it through custom_exception_handler directly — status 500). Use from rest_framework.exceptions import ValidationError instead — same as views.py already does for the "config not passed" case.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Fixed.

@ol-zayatsm
ol-zayatsm force-pushed the reports_swagger_update branch from 3876a39 to d0e4d7e Compare August 6, 2026 15:27
ReportConfigContentDTO

Raises:
NotFoundError: if config not found

@ol-nata ol-nata Aug 24, 2026

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Raises: is missing ValidationError, which the method now also raises for an invalid config ID or content.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Fixed.

response=ReportConfigListResponseSerializer,
description='Configurations were successfully retrieved',
),
400: OpenApiResponse(

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

configs documents 400 for "Current run does not exist", but self.get_object() raises Http404 → DRF's default exception_handler converts it to exceptions.NotFound, i.e. actual status is 404 — same as retrieve correctly documents for the same situation. Please change this to 404 to match reality.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Fixed.

Values of the `config` query parameter that aren't valid integers
(e.g. `?config=abc` or `?config=1.5`) raised an unhandled `ValueError`
when used in the database lookup, surfacing as an opaque 500 response
instead of telling the client what was wrong. Catch `ValueError`
alongside `ObjectDoesNotExist` in `get_report_config` and raise a
`ValidationError` with a descriptive message, so an invalid config ID
returns a clear 400 error.

Signed-off-by: Mikhail Zayats <mikhail.zayats@oktetlabs.ru>
Replace dict-based service responses with typed DTOs to introduce
explicit and structured data contracts across service boundaries and
move serialization to API boundary to improve separation of
concerns between service layer and API layers.

Signed-off-by: Mikhail Zayats <mikhail.zayats@oktetlabs.ru>
Ensure consistency between OpenAPI schemas and API responses by
introducing explicit request/response serializers and binding them via
drf-spectacular.

Signed-off-by: Mikhail Zayats <mikhail.zayats@oktetlabs.ru>
@ol-zayatsm
ol-zayatsm force-pushed the reports_swagger_update branch from d0e4d7e to 0539514 Compare August 27, 2026 08:58
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.

2 participants