From c3152c13bc2264800350b672118dca94a4287201 Mon Sep 17 00:00:00 2001 From: Ernesto Puerta Date: Fri, 24 Sep 2021 17:46:42 +0200 Subject: [PATCH] mgr/dashboard: replace string version with class * APIVersion: * Moved to a separate file * Added doctests * Added sentinel values: * DEFAULT = 1.0 * EXPERIMENTAL = 0.1 * NONE = 0.0 * Added to_mime_type() helper method * Controllers.__init__: * Added type hints * Replaced string versions with APIVersions * Feedback controller: * Replaced with EXPERIMENTAL (probably it should be NONE) Fixes: https://tracker.ceph.com/issues/52480 Signed-off-by: Ernesto Puerta Conflicts: src/pybind/mgr/dashboard/controllers/__init__.py - Remove the current changes and keep the incoming new changes src/pybind/mgr/dashboard/controllers/crush_rule.py - Changes related to the versioning like importing the APIVersion src/pybind/mgr/dashboard/controllers/docs.py - Changes related to the versioning like importing the APIVersion src/pybind/mgr/dashboard/controllers/feedback.py - Deleted the file since feedback module isn't backported to pacific src/pybind/mgr/dashboard/controllers/host.py - Changes related to the versioning like importing the APIVersion src/pybind/mgr/dashboard/openapi.yaml - Generated a new openapi yaml file src/pybind/mgr/dashboard/tests/__init__.py - Changes related to the versioning like importing the APIVersion src/pybind/mgr/dashboard/tests/test_docs.py - Changes related to the versioning like importing the APIVersion src/pybind/mgr/dashboard/tests/test_host.py - Changes related to the versioning like importing the APIVersion src/pybind/mgr/dashboard/tests/test_tools.py - Changes related to the versioning like importing the APIVersion src/pybind/mgr/dashboard/tests/test_versioning.py - Changes related to the versioning like importing the APIVersion src/pybind/mgr/dashboard/controllers/crush_rule.py - Removed the MethodMap decorator which updates the version of the enpoint to 2.0 because those changes which caused that version updating were not backported to pacific --- src/pybind/mgr/dashboard/__init__.py | 27 ------- .../mgr/dashboard/controllers/_version.py | 75 +++++++++++++++++ .../mgr/dashboard/controllers/crush_rule.py | 1 - src/pybind/mgr/dashboard/controllers/docs.py | 30 +++---- .../mgr/dashboard/controllers/feedback.py | 58 ------------- src/pybind/mgr/dashboard/controllers/host.py | 4 +- src/pybind/mgr/dashboard/openapi.yaml | 81 ------------------- src/pybind/mgr/dashboard/tests/__init__.py | 24 +++--- src/pybind/mgr/dashboard/tests/test_docs.py | 10 +-- src/pybind/mgr/dashboard/tests/test_host.py | 23 +++--- src/pybind/mgr/dashboard/tests/test_tools.py | 13 +-- .../mgr/dashboard/tests/test_versioning.py | 37 +++++---- 12 files changed, 148 insertions(+), 235 deletions(-) create mode 100644 src/pybind/mgr/dashboard/controllers/_version.py delete mode 100644 src/pybind/mgr/dashboard/controllers/feedback.py diff --git a/src/pybind/mgr/dashboard/__init__.py b/src/pybind/mgr/dashboard/__init__.py index b760ae661cd98..feefda948163c 100644 --- a/src/pybind/mgr/dashboard/__init__.py +++ b/src/pybind/mgr/dashboard/__init__.py @@ -6,36 +6,9 @@ ceph dashboard module from __future__ import absolute_import import os -import re -from typing import NamedTuple import cherrypy -DEFAULT_API_VERSION = '1.0' - - -class APIVersion(NamedTuple): - major: int = 1 - minor: int = 0 - - @classmethod - def from_string(cls, version_string): - result = re.match(r'application/vnd\.ceph\.api\.v(\d+)\.(\d+)\+json', version_string) - if result: - return cls(*(int(s) for s in result.groups())) - return None - - def __str__(self): - return f'{self.major}.{self.minor}' - - def supports(self, client_version): - return self.major == client_version.major and client_version.minor <= self.minor - - @staticmethod - def to_tuple(version: str): - return APIVersion(int(version.split('.')[0]), int(version.split('.')[1])) - - if 'COVERAGE_ENABLED' in os.environ: import coverage # pylint: disable=import-error __cov = coverage.Coverage(config_file="{}/.coveragerc".format(os.path.dirname(__file__)), diff --git a/src/pybind/mgr/dashboard/controllers/_version.py b/src/pybind/mgr/dashboard/controllers/_version.py new file mode 100644 index 0000000000000..3e7331c88ac78 --- /dev/null +++ b/src/pybind/mgr/dashboard/controllers/_version.py @@ -0,0 +1,75 @@ +import re +from typing import NamedTuple + + +class APIVersion(NamedTuple): + """ + >>> APIVersion(1,0) + APIVersion(major=1, minor=0) + + >>> APIVersion._make([1,0]) + APIVersion(major=1, minor=0) + + >>> f'{APIVersion(1, 0)!r}' + 'APIVersion(major=1, minor=0)' + """ + major: int + minor: int + + DEFAULT = ... # type: ignore + EXPERIMENTAL = ... # type: ignore + NONE = ... # type: ignore + + __MIME_TYPE_REGEX = re.compile( # type: ignore + r'^application/vnd\.ceph\.api\.v(\d+\.\d+)\+json$') + + @classmethod + def from_string(cls, version_string: str) -> 'APIVersion': + """ + >>> APIVersion.from_string("1.0") + APIVersion(major=1, minor=0) + """ + return cls._make(int(s) for s in version_string.split('.')) + + @classmethod + def from_mime_type(cls, mime_type: str) -> 'APIVersion': + """ + >>> APIVersion.from_mime_type('application/vnd.ceph.api.v1.0+json') + APIVersion(major=1, minor=0) + + """ + return cls.from_string(cls.__MIME_TYPE_REGEX.match(mime_type).group(1)) + + def __str__(self): + """ + >>> f'{APIVersion(1, 0)}' + '1.0' + """ + return f'{self.major}.{self.minor}' + + def to_mime_type(self, subtype='json'): + """ + >>> APIVersion(1, 0).to_mime_type(subtype='xml') + 'application/vnd.ceph.api.v1.0+xml' + """ + return f'application/vnd.ceph.api.v{self!s}+{subtype}' + + def supports(self, client_version: "APIVersion") -> bool: + """ + >>> APIVersion(1, 1).supports(APIVersion(1, 0)) + True + + >>> APIVersion(1, 0).supports(APIVersion(1, 1)) + False + + >>> APIVersion(2, 0).supports(APIVersion(1, 1)) + False + """ + return (self.major == client_version.major + and client_version.minor <= self.minor) + + +# Sentinel Values +APIVersion.DEFAULT = APIVersion(1, 0) # type: ignore +APIVersion.EXPERIMENTAL = APIVersion(0, 1) # type: ignore +APIVersion.NONE = APIVersion(0, 0) # type: ignore diff --git a/src/pybind/mgr/dashboard/controllers/crush_rule.py b/src/pybind/mgr/dashboard/controllers/crush_rule.py index bd00d8feaf408..0310ab1c3d985 100644 --- a/src/pybind/mgr/dashboard/controllers/crush_rule.py +++ b/src/pybind/mgr/dashboard/controllers/crush_rule.py @@ -8,7 +8,6 @@ from .. import mgr from ..security import Scope from ..services.ceph_service import CephService from . import APIDoc, APIRouter, Endpoint, EndpointDoc, ReadPermission, RESTController, UIRouter -from ._version import APIVersion LIST_SCHEMA = { "rule_id": (int, 'Rule ID'), diff --git a/src/pybind/mgr/dashboard/controllers/docs.py b/src/pybind/mgr/dashboard/controllers/docs.py index 22aafeec6c351..da040f86f89ac 100644 --- a/src/pybind/mgr/dashboard/controllers/docs.py +++ b/src/pybind/mgr/dashboard/controllers/docs.py @@ -2,11 +2,11 @@ from __future__ import absolute_import import logging -from typing import Any, Dict, List, Union +from typing import Any, Dict, List, Optional, Union import cherrypy -from .. import DEFAULT_API_VERSION, mgr +from .. import mgr from ..api.doc import Schema, SchemaInput, SchemaType from . import ENDPOINT_MAP, BaseController, Endpoint, Router from ._version import APIVersion @@ -186,7 +186,8 @@ class Docs(BaseController): return schema.as_dict() @classmethod - def _gen_responses(cls, method, resp_object=None, version=None): + def _gen_responses(cls, method, resp_object=None, + version: Optional[APIVersion] = None): resp: Dict[str, Dict[str, Union[str, Any]]] = { '400': { "description": "Operation exception. Please check the " @@ -206,37 +207,38 @@ class Docs(BaseController): } if not version: - version = DEFAULT_API_VERSION + version = APIVersion.DEFAULT if method.lower() == 'get': resp['200'] = {'description': "OK", - 'content': {'application/vnd.ceph.api.v{}+json'.format(version): + 'content': {version.to_mime_type(): {'type': 'object'}}} if method.lower() == 'post': resp['201'] = {'description': "Resource created.", - 'content': {'application/vnd.ceph.api.v{}+json'.format(version): + 'content': {version.to_mime_type(): {'type': 'object'}}} if method.lower() == 'put': resp['200'] = {'description': "Resource updated.", - 'content': {'application/vnd.ceph.api.v{}+json'.format(version): + 'content': {version.to_mime_type(): {'type': 'object'}}} if method.lower() == 'delete': resp['204'] = {'description': "Resource deleted.", - 'content': {'application/vnd.ceph.api.v{}+json'.format(version): + 'content': {version.to_mime_type(): {'type': 'object'}}} if method.lower() in ['post', 'put', 'delete']: resp['202'] = {'description': "Operation is still executing." " Please check the task queue.", - 'content': {'application/vnd.ceph.api.v{}+json'.format(version): + 'content': {version.to_mime_type(): {'type': 'object'}}} if resp_object: for status_code, response_body in resp_object.items(): if status_code in resp: - resp[status_code].update({ - 'content': { - 'application/vnd.ceph.api.v{}+json'.format(version): { - 'schema': cls._gen_schema_for_content(response_body)}}}) + resp[status_code].update( + {'content': + {version.to_mime_type(): + {'schema': cls._gen_schema_for_content(response_body)} + }}) return resp @@ -288,7 +290,7 @@ class Docs(BaseController): func = endpoint.func summary = '' - version = '' + version = None resp = {} p_info = [] diff --git a/src/pybind/mgr/dashboard/controllers/feedback.py b/src/pybind/mgr/dashboard/controllers/feedback.py deleted file mode 100644 index e1a9eb31d6c20..0000000000000 --- a/src/pybind/mgr/dashboard/controllers/feedback.py +++ /dev/null @@ -1,58 +0,0 @@ -# # -*- coding: utf-8 -*- - -from ..exceptions import DashboardException -from ..model.feedback import Feedback -from ..rest_client import RequestException -from ..security import Scope -from ..services import feedback -from . import APIDoc, APIRouter, RESTController - - -@APIRouter('/feedback', Scope.CONFIG_OPT) -@APIDoc("Feedback API", "Report") -class FeedbackController(RESTController): - issueAPIkey = None - - def __init__(self): # pragma: no cover - super().__init__() - self.tracker_client = feedback.CephTrackerClient() - - def create(self, project, tracker, subject, description): - """ - Create an issue. - :param project: The affected ceph component. - :param tracker: The tracker type. - :param subject: The title of the issue. - :param description: The description of the issue. - """ - try: - new_issue = Feedback(Feedback.Project[project].value, - Feedback.TrackerType[tracker].value, subject, description) - except KeyError: - raise DashboardException(msg=f'{"Invalid arguments"}', component='feedback') - try: - return self.tracker_client.create_issue(new_issue) - except RequestException as error: - if error.status_code == 401: - raise DashboardException(msg=f'{"Invalid API key"}', - http_status_code=error.status_code, - component='feedback') - raise error - except Exception: - raise DashboardException(msg=f'{"API key not set"}', - http_status_code=401, - component='feedback') - - def get(self, issue_number): - """ - Fetch issue details. - :param issueAPI: The issue tracker API access key. - """ - try: - return self.tracker_client.get_issues(issue_number) - except RequestException as error: - if error.status_code == 404: - raise DashboardException(msg=f'Issue {issue_number} not found', - http_status_code=error.status_code, - component='feedback') - raise error diff --git a/src/pybind/mgr/dashboard/controllers/host.py b/src/pybind/mgr/dashboard/controllers/host.py index b4d12c0b9312a..53e73b654af2c 100644 --- a/src/pybind/mgr/dashboard/controllers/host.py +++ b/src/pybind/mgr/dashboard/controllers/host.py @@ -295,7 +295,7 @@ class Host(RESTController): 'status': (str, 'Host Status'), }, responses={200: None, 204: None}) - @RESTController.MethodMap(version='0.1') + @RESTController.MethodMap(version=APIVersion.EXPERIMENTAL) def create(self, hostname: str, addr: Optional[str] = None, labels: Optional[List[str]] = None, @@ -408,7 +408,7 @@ class Host(RESTController): 'force': (bool, 'Force Enter Maintenance') }, responses={200: None, 204: None}) - @RESTController.MethodMap(version='0.1') + @RESTController.MethodMap(version=APIVersion.EXPERIMENTAL) def set(self, hostname: str, update_labels: bool = False, labels: List[str] = None, maintenance: bool = False, force: bool = False): diff --git a/src/pybind/mgr/dashboard/openapi.yaml b/src/pybind/mgr/dashboard/openapi.yaml index c923b3441a6c3..921dadb0471d0 100644 --- a/src/pybind/mgr/dashboard/openapi.yaml +++ b/src/pybind/mgr/dashboard/openapi.yaml @@ -2656,85 +2656,6 @@ paths: summary: Get List Of Features tags: - FeatureTogglesEndpoint - /api/feedback: - post: - description: "\n Create an issue.\n :param project: The affected\ - \ ceph component.\n :param tracker: The tracker type.\n :param\ - \ subject: The title of the issue.\n :param description: The description\ - \ of the issue.\n " - parameters: [] - requestBody: - content: - application/json: - schema: - properties: - description: - type: string - project: - type: string - subject: - type: string - tracker: - type: string - required: - - project - - tracker - - subject - - description - type: object - responses: - '201': - content: - application/vnd.ceph.api.v1.0+json: - type: object - description: Resource created. - '202': - content: - application/vnd.ceph.api.v1.0+json: - type: object - description: Operation is still executing. Please check the task queue. - '400': - description: Operation exception. Please check the response body for details. - '401': - description: Unauthenticated access. Please login first. - '403': - description: Unauthorized access. Please check your permissions. - '500': - description: Unexpected error. Please check the response body for the stack - trace. - security: - - jwt: [] - tags: - - Report - /api/feedback/{issue_number}: - get: - description: "\n Fetch issue details.\n :param issueAPI: The issue\ - \ tracker API access key.\n " - parameters: - - in: path - name: issue_number - required: true - schema: - type: integer - responses: - '200': - content: - application/vnd.ceph.api.v1.0+json: - type: object - description: OK - '400': - description: Operation exception. Please check the response body for details. - '401': - description: Unauthenticated access. Please login first. - '403': - description: Unauthorized access. Please check your permissions. - '500': - description: Unexpected error. Please check the response body for the stack - trace. - security: - - jwt: [] - tags: - - Report /api/grafana/dashboards: post: parameters: [] @@ -10452,8 +10373,6 @@ tags: name: RbdSnapshot - description: RBD Trash Management API name: RbdTrash -- description: Feedback API - name: Report - description: RGW Management API name: Rgw - description: RGW Bucket Management API diff --git a/src/pybind/mgr/dashboard/tests/__init__.py b/src/pybind/mgr/dashboard/tests/__init__.py index a09fe6a9adb0c..cda7660093977 100644 --- a/src/pybind/mgr/dashboard/tests/__init__.py +++ b/src/pybind/mgr/dashboard/tests/__init__.py @@ -14,7 +14,7 @@ from cherrypy.test import helper from mgr_module import HandleCommandResult from pyfakefs import fake_filesystem -from .. import DEFAULT_API_VERSION, mgr +from .. import mgr from ..controllers import generate_controller_routes, json_error_page from ..controllers._version import APIVersion from ..module import Module @@ -154,18 +154,18 @@ class ControllerTestCase(helper.CPWebCase): if cls._request_logging: cherrypy.config.update({'tools.request_logging.on': False}) - def _request(self, url, method, data=None, headers=None, version=DEFAULT_API_VERSION): + def _request(self, url, method, data=None, headers=None, version=APIVersion.DEFAULT): if not data: b = None if version: - h = [('Accept', 'application/vnd.ceph.api.v{}+json'.format(version)), + h = [('Accept', version.to_mime_type()), ('Content-Length', '0')] else: h = None else: b = json.dumps(data) if version is not None: - h = [('Accept', 'application/vnd.ceph.api.v{}+json'.format(version)), + h = [('Accept', version.to_mime_type()), ('Content-Type', 'application/json'), ('Content-Length', str(len(b)))] @@ -177,19 +177,19 @@ class ControllerTestCase(helper.CPWebCase): h = headers self.getPage(url, method=method, body=b, headers=h) - def _get(self, url, headers=None, version=DEFAULT_API_VERSION): + def _get(self, url, headers=None, version=APIVersion.DEFAULT): self._request(url, 'GET', headers=headers, version=version) - def _post(self, url, data=None, version=DEFAULT_API_VERSION): + def _post(self, url, data=None, version=APIVersion.DEFAULT): self._request(url, 'POST', data, version=version) - def _delete(self, url, data=None, version=DEFAULT_API_VERSION): + def _delete(self, url, data=None, version=APIVersion.DEFAULT): self._request(url, 'DELETE', data, version=version) - def _put(self, url, data=None, version=DEFAULT_API_VERSION): + def _put(self, url, data=None, version=APIVersion.DEFAULT): self._request(url, 'PUT', data, version=version) - def _task_request(self, method, url, data, timeout, version=DEFAULT_API_VERSION): + def _task_request(self, method, url, data, timeout, version=APIVersion.DEFAULT): self._request(url, method, data, version=version) if self.status != '202 Accepted': logger.info("task finished immediately") @@ -255,13 +255,13 @@ class ControllerTestCase(helper.CPWebCase): self.status = 500 self.body = json.dumps(thread.res_task['exception']) - def _task_post(self, url, data=None, timeout=60, version=DEFAULT_API_VERSION): + def _task_post(self, url, data=None, timeout=60, version=APIVersion.DEFAULT): self._task_request('POST', url, data, timeout, version=version) - def _task_delete(self, url, timeout=60, version=DEFAULT_API_VERSION): + def _task_delete(self, url, timeout=60, version=APIVersion.DEFAULT): self._task_request('DELETE', url, None, timeout, version=version) - def _task_put(self, url, data=None, timeout=60, version=DEFAULT_API_VERSION): + def _task_put(self, url, data=None, timeout=60, version=APIVersion.DEFAULT): self._task_request('PUT', url, data, timeout, version=version) def json_body(self): diff --git a/src/pybind/mgr/dashboard/tests/test_docs.py b/src/pybind/mgr/dashboard/tests/test_docs.py index 1e6abbc0c8cda..d0529ab8d8233 100644 --- a/src/pybind/mgr/dashboard/tests/test_docs.py +++ b/src/pybind/mgr/dashboard/tests/test_docs.py @@ -30,11 +30,11 @@ class DecoratedController(RESTController): }, ) @Endpoint(json_response=False) - @RESTController.Resource('PUT', version='0.1') + @RESTController.Resource('PUT', version=APIVersion(0, 1)) def decorated_func(self, parameter): pass - @RESTController.MethodMap(version='0.1') + @RESTController.MethodMap(version=APIVersion(0, 1)) def list(self): pass @@ -87,7 +87,7 @@ class DocsTest(ControllerTestCase): expected_response_content = { '200': { - 'application/vnd.ceph.api.v0.1+json': { + APIVersion(0, 1).to_mime_type(): { 'schema': {'type': 'array', 'items': {'type': 'object', 'properties': { 'my_prop': { @@ -95,7 +95,7 @@ class DocsTest(ControllerTestCase): 'description': '200 property desc.'}}}, 'required': ['my_prop']}}}, '202': { - 'application/vnd.ceph.api.v0.1+json': { + APIVersion(0, 1).to_mime_type(): { 'schema': {'type': 'object', 'properties': {'my_prop': { 'type': 'string', @@ -111,7 +111,7 @@ class DocsTest(ControllerTestCase): def test_gen_method_paths(self): outcome = Docs().gen_paths(False)['/api/doctest/']['get'] - self.assertEqual({'application/vnd.ceph.api.v0.1+json': {'type': 'object'}}, + self.assertEqual({APIVersion(0, 1).to_mime_type(): {'type': 'object'}}, outcome['responses']['200']['content']) def test_gen_paths_all(self): diff --git a/src/pybind/mgr/dashboard/tests/test_host.py b/src/pybind/mgr/dashboard/tests/test_host.py index 003d955adc95e..8b1e03b0ce317 100644 --- a/src/pybind/mgr/dashboard/tests/test_host.py +++ b/src/pybind/mgr/dashboard/tests/test_host.py @@ -136,7 +136,7 @@ class HostControllerTest(ControllerTestCase): 'labels': 'mon', 'status': 'maintenance' } - self._post(self.URL_HOST, payload, version='0.1') + self._post(self.URL_HOST, payload, version=APIVersion(0, 1)) self.assertStatus(201) mock_add_host.assert_called() @@ -150,7 +150,7 @@ class HostControllerTest(ControllerTestCase): fake_client.hosts.add_label = mock.Mock() payload = {'update_labels': True, 'labels': ['bbb', 'ccc']} - self._put('{}/node0'.format(self.URL_HOST), payload, version='0.1') + self._put('{}/node0'.format(self.URL_HOST), payload, version=APIVersion(0, 1)) self.assertStatus(200) self.assertHeader('Content-Type', 'application/vnd.ceph.api.v0.1+json') @@ -158,8 +158,9 @@ class HostControllerTest(ControllerTestCase): fake_client.hosts.add_label.assert_called_once_with('node0', 'ccc') # return 400 if type other than List[str] - self._put('{}/node0'.format(self.URL_HOST), {'update_labels': True, - 'labels': 'ddd'}, version='0.1') + self._put('{}/node0'.format(self.URL_HOST), + {'update_labels': True, 'labels': 'ddd'}, + version=APIVersion(0, 1)) self.assertStatus(400) def test_host_maintenance(self): @@ -170,25 +171,29 @@ class HostControllerTest(ControllerTestCase): ] with patch_orch(True, hosts=orch_hosts): # enter maintenance mode - self._put('{}/node0'.format(self.URL_HOST), {'maintenance': True}, version='0.1') + self._put('{}/node0'.format(self.URL_HOST), {'maintenance': True}, + version=APIVersion(0, 1)) self.assertStatus(200) self.assertHeader('Content-Type', 'application/vnd.ceph.api.v0.1+json') # force enter maintenance mode self._put('{}/node1'.format(self.URL_HOST), {'maintenance': True, 'force': True}, - version='0.1') + version=APIVersion(0, 1)) self.assertStatus(200) # exit maintenance mode - self._put('{}/node0'.format(self.URL_HOST), {'maintenance': True}, version='0.1') + self._put('{}/node0'.format(self.URL_HOST), {'maintenance': True}, + version=APIVersion(0, 1)) self.assertStatus(200) - self._put('{}/node1'.format(self.URL_HOST), {'maintenance': True}, version='0.1') + self._put('{}/node1'.format(self.URL_HOST), {'maintenance': True}, + version=APIVersion(0, 1)) self.assertStatus(200) # maintenance without orchestrator service with patch_orch(False): - self._put('{}/node0'.format(self.URL_HOST), {'maintenance': True}, version='0.1') + self._put('{}/node0'.format(self.URL_HOST), {'maintenance': True}, + version=APIVersion(0, 1)) self.assertStatus(503) @mock.patch('dashboard.controllers.host.time') diff --git a/src/pybind/mgr/dashboard/tests/test_tools.py b/src/pybind/mgr/dashboard/tests/test_tools.py index 4edb458b955d7..f1e4ae7e157f4 100644 --- a/src/pybind/mgr/dashboard/tests/test_tools.py +++ b/src/pybind/mgr/dashboard/tests/test_tools.py @@ -11,7 +11,6 @@ try: except ImportError: from unittest.mock import patch -from .. import DEFAULT_API_VERSION from ..controllers import APIRouter, BaseController, Proxy, RESTController, Router from ..controllers._version import APIVersion from ..services.exception import handle_rados_error @@ -88,8 +87,7 @@ class RESTControllerTest(ControllerTestCase): self.assertStatus(204) self._get("/foo") self.assertStatus('200 OK') - self.assertHeader('Content-Type', - 'application/vnd.ceph.api.v{}+json'.format(DEFAULT_API_VERSION)) + self.assertHeader('Content-Type', APIVersion.DEFAULT.to_mime_type()) self.assertBody('[]') def test_fill(self): @@ -100,19 +98,16 @@ class RESTControllerTest(ControllerTestCase): self._post("/foo", data) self.assertJsonBody(data) self.assertStatus(201) - self.assertHeader('Content-Type', - 'application/vnd.ceph.api.v{}+json'.format(DEFAULT_API_VERSION)) + self.assertHeader('Content-Type', APIVersion.DEFAULT.to_mime_type()) self._get("/foo") self.assertStatus('200 OK') - self.assertHeader('Content-Type', - 'application/vnd.ceph.api.v{}+json'.format(DEFAULT_API_VERSION)) + self.assertHeader('Content-Type', APIVersion.DEFAULT.to_mime_type()) self.assertJsonBody([data] * 5) self._put('/foo/0', {'newdata': 'newdata'}) self.assertStatus('200 OK') - self.assertHeader('Content-Type', - 'application/vnd.ceph.api.v{}+json'.format(DEFAULT_API_VERSION)) + self.assertHeader('Content-Type', APIVersion.DEFAULT.to_mime_type()) self.assertJsonBody({'newdata': 'newdata', 'key': '0'}) def test_not_implemented(self): diff --git a/src/pybind/mgr/dashboard/tests/test_versioning.py b/src/pybind/mgr/dashboard/tests/test_versioning.py index ae153e3e18d9a..48e6394c2cf04 100644 --- a/src/pybind/mgr/dashboard/tests/test_versioning.py +++ b/src/pybind/mgr/dashboard/tests/test_versioning.py @@ -13,22 +13,22 @@ from . import ControllerTestCase # pylint: disable=no-name-in-module class VTest(RESTController): RESOURCE_ID = "vid" - @RESTController.MethodMap(version="0.1") + @RESTController.MethodMap(version=APIVersion(0, 1)) def list(self): return {'version': ""} def get(self): return {'version': ""} - @RESTController.Collection('GET', version="1.0") + @RESTController.Collection('GET', version=APIVersion(1, 0)) def vmethod(self): return {'version': '1.0'} - @RESTController.Collection('GET', version="1.1") + @RESTController.Collection('GET', version=APIVersion(1, 1)) def vmethodv1_1(self): return {'version': '1.1'} - @RESTController.Collection('GET', version="2.0") + @RESTController.Collection('GET', version=APIVersion(2, 0)) def vmethodv2(self): return {'version': '2.0'} @@ -40,37 +40,40 @@ class RESTVersioningTest(ControllerTestCase, unittest.TestCase): def test_list(self): for (version, expected_status) in [ - ("0.1", 200), - ("2.0", 415) + ((0, 1), 200), + ((2, 0), 415) ]: with self.subTest(version=version): - self._get('/test/api/vtest', version=version) + self._get('/test/api/vtest', version=APIVersion._make(version)) self.assertStatus(expected_status) def test_v1(self): for (version, expected_status) in [ - ("1.0", 200), - ("2.0", 415) + ((1, 0), 200), + ((2, 0), 415) ]: with self.subTest(version=version): - self._get('/test/api/vtest/vmethod', version=version) + self._get('/test/api/vtest/vmethod', + version=APIVersion._make(version)) self.assertStatus(expected_status) def test_v2(self): for (version, expected_status) in [ - ("2.0", 200), - ("1.0", 415) + ((2, 0), 200), + ((1, 0), 415) ]: with self.subTest(version=version): - self._get('/test/api/vtest/vmethodv2', version=version) + self._get('/test/api/vtest/vmethodv2', + version=APIVersion._make(version)) self.assertStatus(expected_status) def test_backward_compatibility(self): for (version, expected_status) in [ - ("1.1", 200), - ("1.0", 200), - ("2.0", 415) + ((1, 1), 200), + ((1, 0), 200), + ((2, 0), 415) ]: with self.subTest(version=version): - self._get('/test/api/vtest/vmethodv1_1', version=version) + self._get('/test/api/vtest/vmethodv1_1', + version=APIVersion._make(version)) self.assertStatus(expected_status) -- 2.47.3