-
Notifications
You must be signed in to change notification settings - Fork 7
measurement: align OpenAPI schemas with actual API responses and introduce DTO service contracts #355
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
measurement: align OpenAPI schemas with actual API responses and introduce DTO service contracts #355
Changes from all commits
c7813db
dec6bc2
423c7f4
9c79988
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,31 @@ | ||
| # SPDX-License-Identifier: Apache-2.0 | ||
| # Copyright (C) 2026 OKTET Labs Ltd. All rights reserved. | ||
|
|
||
| from __future__ import annotations | ||
|
|
||
| from dataclasses import dataclass | ||
| from typing import TYPE_CHECKING, Any | ||
|
|
||
|
|
||
| if TYPE_CHECKING: | ||
| from datetime import datetime | ||
|
|
||
|
|
||
| @dataclass | ||
| class MeasurementChartDTO: | ||
| id: int | str | ||
| title: str | None | ||
| subtitle: str | ||
| axis_x: dict[str, Any] | ||
| axis_y: dict[str, Any] | ||
| dataset: list[list[Any]] | ||
|
|
||
|
|
||
| @dataclass | ||
| class MeasurementDTO: | ||
| run_id: int | ||
| result_id: int | ||
| start: datetime | ||
| test_name: str | ||
| parameters_list: list[str] | ||
| measurement_series_charts: list[MeasurementChartDTO] |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,96 @@ | ||
| # SPDX-License-Identifier: Apache-2.0 | ||
| # Copyright (C) 2026 OKTET Labs Ltd. All rights reserved. | ||
|
|
||
| from drf_spectacular.utils import OpenApiResponse, extend_schema, extend_schema_view | ||
|
|
||
| from bublik.interfaces.api_v2.errors.serializers import ErrorResponseSerializer | ||
| from bublik.interfaces.api_v2.measurement.serializers import ( | ||
| MeasurementByResultSerializer, | ||
| MeasurementChartSerializer, | ||
| MeasurementListResponseSerializer, | ||
| MeasurementRequestBodySerializer, | ||
| ) | ||
|
|
||
|
|
||
| MEASUREMENT_TAG = 'Measurements' | ||
|
|
||
|
|
||
| measurement_viewset_schema = extend_schema_view( | ||
| list=extend_schema( | ||
| summary='List measurements', | ||
| description=""" | ||
| Return a list of available measurements, | ||
| each represented by its defining set of metadata. | ||
| """, | ||
| responses={ | ||
| 200: OpenApiResponse( | ||
| response=MeasurementListResponseSerializer(many=True), | ||
| description='Measurements were successfully retrieved', | ||
| ) | ||
| }, | ||
| tags=[MEASUREMENT_TAG], | ||
| ), | ||
| trend_charts=extend_schema( | ||
| summary='Get measurement trend charts', | ||
| description=""" | ||
| Build and return measurement trend charts for the specified | ||
| test result IDs. | ||
| """, | ||
| request=MeasurementRequestBodySerializer, | ||
| responses={ | ||
| 200: OpenApiResponse( | ||
| response=MeasurementChartSerializer(many=True), | ||
| description='Measurement trend charts were successfully retrieved', | ||
| ), | ||
| 400: OpenApiResponse( | ||
| response=ErrorResponseSerializer, | ||
| description='Result IDs were not provided', | ||
| ), | ||
| }, | ||
| tags=[MEASUREMENT_TAG], | ||
| ), | ||
| by_result_ids=extend_schema( | ||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Same as
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Fixed. |
||
| summary='Get measurements by result IDs', | ||
| description=""" | ||
| Return measurement data, test parameters, and chart series | ||
| for the specified test result IDs. | ||
| """, | ||
| request=MeasurementRequestBodySerializer, | ||
| responses={ | ||
| 200: OpenApiResponse( | ||
| response=MeasurementByResultSerializer(many=True), | ||
| description='Measurement data for result IDs were successfully retrieved', | ||
| ), | ||
| 400: OpenApiResponse( | ||
| response=ErrorResponseSerializer, | ||
| description='Result IDs were not provided', | ||
| ), | ||
| 404: OpenApiResponse( | ||
| response=ErrorResponseSerializer, | ||
| description='One of the specified results was not found', | ||
| ), | ||
| }, | ||
| tags=[MEASUREMENT_TAG], | ||
| ), | ||
| retrieve=extend_schema( | ||
| summary='Get measurement', | ||
| description=""" | ||
| Return the set of metadata that describes a measurement, identified by its ID. | ||
| """, | ||
| responses={ | ||
| 200: OpenApiResponse( | ||
| response=MeasurementListResponseSerializer, | ||
| description='Measurement details were successfully retrieved', | ||
| ), | ||
| 400: OpenApiResponse( | ||
| response=ErrorResponseSerializer, | ||
| description='Measurement ID was not provided or is invalid', | ||
| ), | ||
| 404: OpenApiResponse( | ||
| response=ErrorResponseSerializer, | ||
| description='Measurement was not found', | ||
| ), | ||
| }, | ||
| tags=[MEASUREMENT_TAG], | ||
| ), | ||
| ) | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,40 @@ | ||
| # SPDX-License-Identifier: Apache-2.0 | ||
| # Copyright (C) 2026 OKTET Labs Ltd. All rights reserved. | ||
|
|
||
| from rest_framework import serializers | ||
|
|
||
|
|
||
| class MeasurementChartSerializer(serializers.Serializer): | ||
| id = serializers.JSONField() | ||
| title = serializers.CharField(allow_null=True) | ||
| subtitle = serializers.CharField() | ||
| axis_x = serializers.DictField() | ||
| axis_y = serializers.DictField() | ||
| dataset = serializers.ListField(child=serializers.ListField(child=serializers.JSONField())) | ||
|
|
||
|
|
||
| class MeasurementByResultSerializer(serializers.Serializer): | ||
| run_id = serializers.IntegerField() | ||
| result_id = serializers.IntegerField() | ||
| start = serializers.DateTimeField() | ||
| test_name = serializers.CharField() | ||
| parameters_list = serializers.ListField(child=serializers.CharField()) | ||
| measurement_series_charts = MeasurementChartSerializer(many=True) | ||
|
|
||
|
|
||
| class MeasurementMetasSerializer(serializers.Serializer): | ||
| name = serializers.CharField() | ||
| type = serializers.CharField() | ||
| value = serializers.CharField() | ||
| comment = serializers.CharField(allow_null=True) | ||
|
|
||
|
|
||
| class MeasurementListResponseSerializer(serializers.Serializer): | ||
| metas = MeasurementMetasSerializer(many=True) | ||
|
|
||
|
|
||
| class MeasurementRequestBodySerializer(serializers.Serializer): | ||
| result_ids = serializers.ListField( | ||
| child=serializers.IntegerField(), | ||
| allow_empty=False, | ||
| ) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
No
request=specified — drf-spectacular falls back to the viewset's defaultserializer_class(MeasurementSerializer, i.e.metas) as the request body schema, which doesn't match what the view actually reads (result_ids). Please add an explicitrequest=(e.g.inline_serializerwithresult_ids).There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Fixed.