diff --git a/src/middlewared/middlewared/plugins/system.py b/src/middlewared/middlewared/plugins/system.py index 9c81ba2ab655..934f3b916b38 100644 --- a/src/middlewared/middlewared/plugins/system.py +++ b/src/middlewared/middlewared/plugins/system.py @@ -5,7 +5,8 @@ from middlewared.logger import CrashReporting from middlewared.schema import accepts, Bool, Dict, Int, IPAddr, List, Str from middlewared.service import ( - CallError, ConfigService, no_auth_required, job, pass_app, private, Service, throttle, ValidationErrors + CallError, ConfigService, no_auth_required, job, pass_app, private, rest_api_metadata, + Service, throttle, ValidationErrors ) import middlewared.sqlalchemy as sa from middlewared.utils import Popen, run, start_daemon_thread, sw_buildtime, sw_version, osc @@ -1303,6 +1304,7 @@ async def do_update(self, data): return await self.config() + @rest_api_metadata(extra_methods=['GET']) @accepts(Int('delay', default=3, validators=[Range(min=0)])) async def ui_restart(self, delay): """ diff --git a/src/middlewared/middlewared/restful.py b/src/middlewared/middlewared/restful.py index bd4b64872541..bc072137e703 100644 --- a/src/middlewared/middlewared/restful.py +++ b/src/middlewared/middlewared/restful.py @@ -142,6 +142,11 @@ async def register_resources(self): res_kwargs['post'] = methodname else: res_kwargs['get'] = methodname + for rest_method in map(str.lower, (method['extra_methods'] or [])): + assert rest_method in ('get',) + # Only allow get for now as that's the only use case we have for now NAS-110243 + res_kwargs[rest_method] = methodname + Resource(self, self.middleware, short_methodname, service['config'], parent=parent, **res_kwargs) await asyncio.sleep(0) # Force context switch diff --git a/src/middlewared/middlewared/service.py b/src/middlewared/middlewared/service.py index a87cf000a42a..1ed3ebb20f8e 100644 --- a/src/middlewared/middlewared/service.py +++ b/src/middlewared/middlewared/service.py @@ -175,6 +175,23 @@ def wrapper(fn): return wrapper +def rest_api_metadata(extra_methods=None): + """ + Allow having endpoints specify explicit rest methods. + + Explicit methods should be a list which specifies what methods the function should be available + at other then the default one it is already going to be. This is useful when we want to maintain + backwards compatibility with endpoints which were not expecting payload before but are now and users + still would like to consume them with previous method which would be GET whereas it's POST now. + """ + def wrapper(fn): + fn._rest_api_metadata = { + 'extra_methods': extra_methods, + } + return fn + return wrapper + + def periodic(interval, run_on_start=True): def wrapper(fn): fn._periodic = PeriodicTaskDescriptor(interval, run_on_start) @@ -969,6 +986,8 @@ def get_methods(self, service=None): 'no_auth_required': hasattr(method, '_no_auth_required'), 'filterable': hasattr(method, '_filterable'), 'pass_application': hasattr(method, '_pass_app'), + 'extra_methods': method._rest_api_metadata['extra_methods'] if hasattr( + method, '_rest_api_metadata') else None, 'require_websocket': hasattr(method, '_pass_app') and not method._pass_app['rest'], 'job': hasattr(method, '_job'), 'downloadable': hasattr(method, '_job') and 'output' in method._job['pipes'],