diff --git a/app/sep/apps/backup_mongo/models.py b/app/sep/apps/backup_mongo/models.py index 72711a9651..584ad1caa3 100644 --- a/app/sep/apps/backup_mongo/models.py +++ b/app/sep/apps/backup_mongo/models.py @@ -24,7 +24,6 @@ from app.core.models import BaseCaseInsensitiveModel from app.core.utils.fields import EmptyStrToNone, EnumFieldMixin, NonEmptyStr from app.inventory.models import ServiceTypeEnum -from app.sep.apps.framework import BaseTaskResponse from app.sep.apps.framework.form_dsl import ( Choices, FieldWidget, @@ -34,6 +33,7 @@ Ui, ) from app.sep.apps.framework.rules import F +from app.sep.apps.shared.backups.responses import BackupTaskBase from app.tasks.models import TaskHistoryStatusEnum OWNER = "BACKUP_MONGO" @@ -537,15 +537,6 @@ class BackupDerivedTaskSummary(BaseModel): status: TaskHistoryStatusEnum | None = None -class BackupTaskBase(BaseTaskResponse): - """Carry the backup_mongo-specific fields shared across its responses. - - :param hostname: The target hostname for the task execution. - """ - - hostname: str | None = None - - class BackupTaskResponse(BackupTaskBase): """Represent a backup task API response. diff --git a/app/sep/apps/backup_pg/models.py b/app/sep/apps/backup_pg/models.py index 407795fd65..4b875d752c 100644 --- a/app/sep/apps/backup_pg/models.py +++ b/app/sep/apps/backup_pg/models.py @@ -29,13 +29,13 @@ from app.core.utils.fields import EmptyStrToNone, EnumFieldMixin, NonEmptyStr from app.core.utils.pydantic import blank_str_values_to_none from app.inventory.models import ServiceTypeEnum -from app.sep.apps.framework import BaseTaskResponse from app.sep.apps.framework.form_dsl import ( Choices, ServiceRef, TaskFormModel, Ui, ) +from app.sep.apps.shared.backups.responses import BackupTaskBase OWNER = "BACKUP_PG" @@ -221,15 +221,6 @@ def _blank_to_none(cls, data: Any) -> Any: return blank_str_values_to_none(data) -class BackupTaskBase(BaseTaskResponse): - """Carry the backup_pg-specific fields shared across its API responses. - - :param hostname: The Nomad executor target the task runs on. - """ - - hostname: str | None = None - - class BackupTaskResponse(BackupTaskBase): """Represent a pgBackRest backup task API response. diff --git a/app/sep/apps/mysql_backups/app.py b/app/sep/apps/mysql_backups/app.py index 2210fe6c05..0f11a3d2b0 100644 --- a/app/sep/apps/mysql_backups/app.py +++ b/app/sep/apps/mysql_backups/app.py @@ -43,7 +43,7 @@ from app.sep.apps.mysql_backups.deps import build_mysql_backups_api_task_response from app.sep.apps.mysql_backups.models import ( BackupCreate, - BackupResponse, + BackupTaskResponse, OWNER, ) from app.sep.apps.mysql_backups.restore.app import app as restore_app @@ -64,7 +64,7 @@ description="Run XtraBackup, Mydumper, and Binlog backups against MySQL hosts.", owner=OWNER, create_model=BackupCreate, - response_model=BackupResponse, + response_model=BackupTaskResponse, views=mysql_backups_views, task_spec_builder=build_backup_spec, response_builder=build_mysql_backups_api_task_response, diff --git a/app/sep/apps/mysql_backups/deps.py b/app/sep/apps/mysql_backups/deps.py index 74a97a0b21..c69446a716 100644 --- a/app/sep/apps/mysql_backups/deps.py +++ b/app/sep/apps/mysql_backups/deps.py @@ -30,7 +30,7 @@ ) from app.sep.apps.mysql_backups.models import ( BackupCreate, - BackupResponse, + BackupTaskResponse, BackupType, OWNER, ) @@ -156,8 +156,8 @@ def build_mysql_backups_api_task_response( *, last_executed_at: datetime | None = None, context: dict[str, str] | None = None, -) -> BackupResponse: - """Build a ``BackupResponse`` for the JSON API. +) -> BackupTaskResponse: + """Build a ``BackupTaskResponse`` for the JSON API. :param task: The backups task retrieved from the Tasks API. :type task: Task @@ -170,7 +170,6 @@ def build_mysql_backups_api_task_response( usernames; falls back to the raw id when the map lacks an entry. :type context: dict[str, str] | None :return: A validated backup task API response object. - :rtype: BackupResponse """ mapping = context or {} hostname = None @@ -178,7 +177,7 @@ def build_mysql_backups_api_task_response( meta = task.data.get("meta") or {} hostname = meta.get("target") return build_default_task_response( - BackupResponse, + BackupTaskResponse, task, status, last_executed_at=last_executed_at, diff --git a/app/sep/apps/mysql_backups/models.py b/app/sep/apps/mysql_backups/models.py index 3d16b46437..dc01760b01 100644 --- a/app/sep/apps/mysql_backups/models.py +++ b/app/sep/apps/mysql_backups/models.py @@ -33,7 +33,6 @@ NonEmptyStr, ) from app.inventory.models import ServiceTypeEnum -from app.sep.apps.framework import BaseTaskResponse from app.sep.apps.framework.form_dsl import ( Choices, Forbidden, @@ -51,6 +50,7 @@ not_, truthy, ) +from app.sep.apps.shared.backups.responses import BackupTaskBase OWNER = "BACKUPS" @@ -644,19 +644,10 @@ class BackupConfig(BaseCaseInsensitiveModel): server_list: list[BackupConfigServer] -class BackupTaskBase(BaseTaskResponse): - """Carry the mysql_backups-specific fields shared across its responses. +class BackupTaskResponse(BackupTaskBase): + """Represent a backup task API response. :param backup_type: The backup type recorded in task config. """ backup_type: BackupType | None = None - - -class BackupResponse(BackupTaskBase): - """Represent a backup task API response. - - :param hostname: The executor hostname target. - """ - - hostname: str | None = None diff --git a/app/sep/apps/shared/backups/__init__.py b/app/sep/apps/shared/backups/__init__.py index 636a76aeff..9b0f71b34e 100644 --- a/app/sep/apps/shared/backups/__init__.py +++ b/app/sep/apps/shared/backups/__init__.py @@ -17,5 +17,6 @@ from app.sep.apps.shared.backups.columns import BACKUP_TYPE_COLUMN from app.sep.apps.shared.backups.edit_form import parse_server_list_config +from app.sep.apps.shared.backups.responses import BackupTaskBase -__all__ = ["BACKUP_TYPE_COLUMN", "parse_server_list_config"] +__all__ = ["BACKUP_TYPE_COLUMN", "BackupTaskBase", "parse_server_list_config"] diff --git a/app/sep/apps/shared/backups/responses.py b/app/sep/apps/shared/backups/responses.py new file mode 100644 index 0000000000..2c161b1b5c --- /dev/null +++ b/app/sep/apps/shared/backups/responses.py @@ -0,0 +1,27 @@ +# Copyright (C) 2026 Percona LLC +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU Affero General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Affero General Public License for more details. +# +# You should have received a copy of the GNU Affero General Public License +# along with this program. If not, see . + +"""Define backup-family shared API response bases.""" + +from app.sep.apps.framework import BaseTaskResponse + + +class BackupTaskBase(BaseTaskResponse): + """Carry the backup-family fields shared across its API responses. + + :param hostname: The Nomad executor target the task runs on. + """ + + hostname: str | None = None diff --git a/frontend/packages/api/specs/sep.json b/frontend/packages/api/specs/sep.json index 1da5a3db46..f2d4df92e8 100644 --- a/frontend/packages/api/specs/sep.json +++ b/frontend/packages/api/specs/sep.json @@ -9631,8 +9631,8 @@ "title": "BackupCreate", "type": "object" }, - "mysql_backups__BackupResponse": { - "description": "Represent a backup task API response.\n\n:param hostname: The executor hostname target.", + "mysql_backups__BackupTaskResponse": { + "description": "Represent a backup task API response.\n\n:param backup_type: The backup type recorded in task config.", "properties": { "alert_on_fail": { "title": "Alert On Fail", @@ -9807,7 +9807,7 @@ "alert_on_fail", "anonymized_entities" ], - "title": "BackupResponse", + "title": "BackupTaskResponse", "type": "object" }, "mysql_backups__BackupType": { @@ -9831,11 +9831,11 @@ "title": "CompressionAlgorithm", "type": "string" }, - "mysql_backups__PaginatedResponse_BackupResponse_": { + "mysql_backups__PaginatedResponse_BackupTaskResponse_": { "properties": { "items": { "items": { - "$ref": "#/components/schemas/mysql_backups__BackupResponse" + "$ref": "#/components/schemas/mysql_backups__BackupTaskResponse" }, "title": "Items", "type": "array" @@ -9859,7 +9859,7 @@ "offset", "limit" ], - "title": "PaginatedResponse[BackupResponse]", + "title": "PaginatedResponse[BackupTaskResponse]", "type": "object" }, "mysql_backups__PaginatedResponse_RestoresResponse_": { @@ -15976,7 +15976,7 @@ "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/mysql_backups__PaginatedResponse_BackupResponse_" + "$ref": "#/components/schemas/mysql_backups__PaginatedResponse_BackupTaskResponse_" } } }, @@ -16435,7 +16435,7 @@ "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/mysql_backups__BackupResponse" + "$ref": "#/components/schemas/mysql_backups__BackupTaskResponse" } } }, diff --git a/frontend/packages/api/src/generated/sep.ts b/frontend/packages/api/src/generated/sep.ts index 6b088e6ffd..3ea1cbab5f 100644 --- a/frontend/packages/api/src/generated/sep.ts +++ b/frontend/packages/api/src/generated/sep.ts @@ -7743,12 +7743,12 @@ export interface components { xtrabackup_verify: boolean; }; /** - * BackupResponse + * BackupTaskResponse * @description Represent a backup task API response. * - * :param hostname: The executor hostname target. + * :param backup_type: The backup type recorded in task config. */ - mysql_backups__BackupResponse: { + mysql_backups__BackupTaskResponse: { /** Alert On Fail */ alert_on_fail: boolean; /** Anonymize Mask */ @@ -7798,10 +7798,10 @@ export interface components { * @enum {string} */ mysql_backups__CompressionAlgorithm: 'zstd' | 'lz4' | 'gzip' | 'quicklz'; - /** PaginatedResponse[BackupResponse] */ - mysql_backups__PaginatedResponse_BackupResponse_: { + /** PaginatedResponse[BackupTaskResponse] */ + mysql_backups__PaginatedResponse_BackupTaskResponse_: { /** Items */ - items: components['schemas']['mysql_backups__BackupResponse'][]; + items: components['schemas']['mysql_backups__BackupTaskResponse'][]; /** Limit */ limit: number; /** Offset */ @@ -11512,7 +11512,7 @@ export interface operations { [name: string]: unknown; }; content: { - 'application/json': components['schemas']['mysql_backups__PaginatedResponse_BackupResponse_']; + 'application/json': components['schemas']['mysql_backups__PaginatedResponse_BackupTaskResponse_']; }; }; /** @description Validation Error */ @@ -11814,7 +11814,7 @@ export interface operations { [name: string]: unknown; }; content: { - 'application/json': components['schemas']['mysql_backups__BackupResponse']; + 'application/json': components['schemas']['mysql_backups__BackupTaskResponse']; }; }; /** @description Validation Error */ diff --git a/tests/app/sep/apps/mysql_backups/test_models.py b/tests/app/sep/apps/mysql_backups/test_models.py index abfb9ec7ba..5b8bbd68fc 100644 --- a/tests/app/sep/apps/mysql_backups/test_models.py +++ b/tests/app/sep/apps/mysql_backups/test_models.py @@ -21,7 +21,7 @@ from app.sep.apps.framework import BaseTaskResponse from app.sep.apps.mysql_backups.models import ( BackupConfigAll, - BackupResponse, + BackupTaskResponse, BackupType, ) from app.tasks.models import TaskBackendEnum @@ -113,12 +113,12 @@ def test_none_raises_validation_error(self): BackupConfigAll.model_validate({"UPLOAD_QUIET": None}) -class TestBackupResponseModel: - """Tests for ``BackupResponse`` rebased onto ``BaseTaskResponse``.""" +class TestBackupTaskResponseModel: + """Verify ``BackupTaskResponse`` is rebased onto ``BaseTaskResponse``.""" def test_exposes_inherited_task_response_surface(self) -> None: """Carry the shared anonymization and connectivity surface from the base.""" - response = BackupResponse( + response = BackupTaskResponse( name="mysql-backup", owner="BACKUPS", backend=TaskBackendEnum.PROXY, diff --git a/tests/app/sep/snapshots/openapi/mysql_backups.json b/tests/app/sep/snapshots/openapi/mysql_backups.json index 668e010007..5aad996650 100644 --- a/tests/app/sep/snapshots/openapi/mysql_backups.json +++ b/tests/app/sep/snapshots/openapi/mysql_backups.json @@ -4004,8 +4004,8 @@ "title": "BackupCreate", "type": "object" }, - "mysql_backups__BackupResponse": { - "description": "Represent a backup task API response.\n\n:param hostname: The executor hostname target.", + "mysql_backups__BackupTaskResponse": { + "description": "Represent a backup task API response.\n\n:param backup_type: The backup type recorded in task config.", "properties": { "alert_on_fail": { "title": "Alert On Fail", @@ -4180,7 +4180,7 @@ "alert_on_fail", "anonymized_entities" ], - "title": "BackupResponse", + "title": "BackupTaskResponse", "type": "object" }, "mysql_backups__BackupType": { @@ -4204,11 +4204,11 @@ "title": "CompressionAlgorithm", "type": "string" }, - "mysql_backups__PaginatedResponse_BackupResponse_": { + "mysql_backups__PaginatedResponse_BackupTaskResponse_": { "properties": { "items": { "items": { - "$ref": "#/components/schemas/mysql_backups__BackupResponse" + "$ref": "#/components/schemas/mysql_backups__BackupTaskResponse" }, "title": "Items", "type": "array" @@ -4232,7 +4232,7 @@ "offset", "limit" ], - "title": "PaginatedResponse[BackupResponse]", + "title": "PaginatedResponse[BackupTaskResponse]", "type": "object" }, "mysql_backups__UploadProvider": { @@ -4297,7 +4297,7 @@ "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/mysql_backups__PaginatedResponse_BackupResponse_" + "$ref": "#/components/schemas/mysql_backups__PaginatedResponse_BackupTaskResponse_" } } }, @@ -4445,7 +4445,7 @@ "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/mysql_backups__BackupResponse" + "$ref": "#/components/schemas/mysql_backups__BackupTaskResponse" } } },