diff --git a/docs/source/dev/adapter.md b/docs/source/dev/adapter.md index 6b0e6d4de..c5c6e9d4c 100644 --- a/docs/source/dev/adapter.md +++ b/docs/source/dev/adapter.md @@ -147,9 +147,9 @@ This generates: Below is an example of how to define a resource handler configuration for a `MotorAdapter`: ```python -from mxcubeweb.core.models.configmodels import AdapterResourceHandlerConfigModel +from mxcubeweb.core.models.configmodels import ResourceHandlerConfigModel -resource_handler_config = AdapterResourceHandlerConfigModel( +resource_handler_config = ResourceHandlerConfigModel( commands=[ "set_value", "get_value", diff --git a/mxcubeweb/app.py b/mxcubeweb/app.py index ab555efdb..df0cc0133 100644 --- a/mxcubeweb/app.py +++ b/mxcubeweb/app.py @@ -29,9 +29,10 @@ from mxcubeweb.core.components.samplechanger import SampleChanger from mxcubeweb.core.components.sampleview import SampleView from mxcubeweb.core.components.workflow import Workflow +from mxcubeweb.core.components.log import Log from mxcubeweb.core.models.configmodels import UIComponentModel from mxcubeweb.logging_handler import MX3LoggingHandler -from mxcubeweb.core.server.resource_handler import AdapterResourceHandlerFactory +from mxcubeweb.core.server.resource_handler import ResourceHandlerFactory removeLoggingHandlers() @@ -131,7 +132,7 @@ def init( # re-initializes the application for each test, we thus # need to remove all AdapterResourceHandlers from the # factory. - AdapterResourceHandlerFactory.unregister_all() + ResourceHandlerFactory.unregister_all() logging.getLogger("MX3.HWR").info("Starting MXCuBE-Web...") MXCUBEApplication.server = server @@ -165,6 +166,7 @@ def init( MXCUBEApplication.sample_view = SampleView(MXCUBEApplication, {}) MXCUBEApplication.workflow = Workflow(MXCUBEApplication, {}) MXCUBEApplication.harvester = Harvester(MXCUBEApplication, {}) + MXCUBEApplication.log = Log(MXCUBEApplication, {}) MXCUBEApplication.init_signal_handlers() # Install server-side UI state storage diff --git a/mxcubeweb/core/adapter/actuator_adapter.py b/mxcubeweb/core/adapter/actuator_adapter.py index d6f300dc0..69a83b187 100644 --- a/mxcubeweb/core/adapter/actuator_adapter.py +++ b/mxcubeweb/core/adapter/actuator_adapter.py @@ -8,10 +8,10 @@ FloatValueModel, HOActuatorValueChangeModel, ) -from mxcubeweb.core.models.configmodels import AdapterResourceHandlerConfigModel +from mxcubeweb.core.models.configmodels import ResourceHandlerConfigModel from mxcubeweb.core.util.networkutils import RateLimited -resource_handler_config = AdapterResourceHandlerConfigModel( +resource_handler_config = ResourceHandlerConfigModel( commands=["get_value", "set_value", "stop"], attributes=["data"] ) diff --git a/mxcubeweb/core/adapter/adapter_base.py b/mxcubeweb/core/adapter/adapter_base.py index 761085b44..35a13585a 100644 --- a/mxcubeweb/core/adapter/adapter_base.py +++ b/mxcubeweb/core/adapter/adapter_base.py @@ -16,12 +16,12 @@ HOActuatorModel, HOModel, ) -from mxcubeweb.core.models.configmodels import AdapterResourceHandlerConfigModel +from mxcubeweb.core.models.configmodels import ResourceHandlerConfigModel from mxcubeweb.core.server.resource_handler import ( - AdapterResourceHandlerFactory, + ResourceHandlerFactory, ) -default_resource_handler_config = AdapterResourceHandlerConfigModel( +default_resource_handler_config = ResourceHandlerConfigModel( commands=[ "set_value", "get_value", @@ -67,10 +67,10 @@ def __init__(self, ho, role, app, resource_handler_config=None): self.ADAPTER_DICT[cls_name][ho.name] = self if resource_handler_config: - AdapterResourceHandlerFactory.create_or_get( + ResourceHandlerFactory.create_or_get( name=cls_name, url_prefix="/mxcube/api/v0.1/hwobj/" + self._type.lower(), - adapter_dict=self.ADAPTER_DICT[cls_name], + handler_dict=self.ADAPTER_DICT[cls_name], app=self.app, exports=resource_handler_config.exports, commands=resource_handler_config.commands, @@ -251,7 +251,7 @@ def _exported_methods(self): # been explicitly configured to be exported configured_exported = self._ho.exported_attributes.keys() - rh = AdapterResourceHandlerFactory.get_handler(self.__class__.__name__.lower()) + rh = ResourceHandlerFactory.get_handler(self.__class__.__name__.lower()) if rh: for export in rh.commands: diff --git a/mxcubeweb/core/adapter/beamline_action_adapter.py b/mxcubeweb/core/adapter/beamline_action_adapter.py index 8ca2b7a91..f54d38224 100644 --- a/mxcubeweb/core/adapter/beamline_action_adapter.py +++ b/mxcubeweb/core/adapter/beamline_action_adapter.py @@ -19,9 +19,9 @@ HOActuatorValueChangeModel, NStateModel, ) -from mxcubeweb.core.models.configmodels import AdapterResourceHandlerConfigModel +from mxcubeweb.core.models.configmodels import ResourceHandlerConfigModel -resource_handler_config = AdapterResourceHandlerConfigModel( +resource_handler_config = ResourceHandlerConfigModel( commands=["stop", "run_action"], attributes=["data", "get_all_actions"] ) @@ -62,7 +62,7 @@ def __init__( ho: HardwareObject, role: str, app, - resource_handler_config: AdapterResourceHandlerConfigModel = resource_handler_config, # noqa: E501 + resource_handler_config: ResourceHandlerConfigModel = resource_handler_config, ): """ Args: diff --git a/mxcubeweb/core/adapter/detector_adapter.py b/mxcubeweb/core/adapter/detector_adapter.py index 0b1a5a186..8e84103cc 100644 --- a/mxcubeweb/core/adapter/detector_adapter.py +++ b/mxcubeweb/core/adapter/detector_adapter.py @@ -3,9 +3,9 @@ from mxcubecore.HardwareObjects.abstract import AbstractDetector from mxcubeweb.core.adapter.adapter_base import AdapterBase -from mxcubeweb.core.models.configmodels import AdapterResourceHandlerConfigModel +from mxcubeweb.core.models.configmodels import ResourceHandlerConfigModel -resource_handler_config = AdapterResourceHandlerConfigModel( +resource_handler_config = ResourceHandlerConfigModel( name="detector_test", url_prefix="/mxcube/api/v0.1/detectortest", attributes=["data"], diff --git a/mxcubeweb/core/adapter/energy_adapter.py b/mxcubeweb/core/adapter/energy_adapter.py index 8c14e0e5c..79c81f4ff 100644 --- a/mxcubeweb/core/adapter/energy_adapter.py +++ b/mxcubeweb/core/adapter/energy_adapter.py @@ -5,9 +5,9 @@ from mxcubeweb.core.adapter.actuator_adapter import ActuatorAdapter from mxcubeweb.core.adapter.wavelength_adapter import WavelengthAdapter -from mxcubeweb.core.models.configmodels import AdapterResourceHandlerConfigModel +from mxcubeweb.core.models.configmodels import ResourceHandlerConfigModel -resource_handler_config = AdapterResourceHandlerConfigModel( +resource_handler_config = ResourceHandlerConfigModel( commands=["get_value", "set_value", "stop", "get_resolution_limits_for_energy"], attributes=["data"], ) diff --git a/mxcubeweb/core/adapter/motor_adapter.py b/mxcubeweb/core/adapter/motor_adapter.py index 65de90911..f1cf79853 100644 --- a/mxcubeweb/core/adapter/motor_adapter.py +++ b/mxcubeweb/core/adapter/motor_adapter.py @@ -6,10 +6,10 @@ from mxcubeweb.core.models.adaptermodels import ( FloatValueModel, ) -from mxcubeweb.core.models.configmodels import AdapterResourceHandlerConfigModel +from mxcubeweb.core.models.configmodels import ResourceHandlerConfigModel from mxcubeweb.core.util.networkutils import RateLimited -resource_handler_config = AdapterResourceHandlerConfigModel( +resource_handler_config = ResourceHandlerConfigModel( url_prefix="/mxcube/api/v0.1/motor_test", commands=["set_value", "get_value", "stop"], attributes=["data"], diff --git a/mxcubeweb/core/adapter/nstate_adapter.py b/mxcubeweb/core/adapter/nstate_adapter.py index 0ae8029a7..aa4afcf54 100644 --- a/mxcubeweb/core/adapter/nstate_adapter.py +++ b/mxcubeweb/core/adapter/nstate_adapter.py @@ -13,9 +13,9 @@ NStateModel, StrValueModel, ) -from mxcubeweb.core.models.configmodels import AdapterResourceHandlerConfigModel +from mxcubeweb.core.models.configmodels import ResourceHandlerConfigModel -resource_handler_config = AdapterResourceHandlerConfigModel( +resource_handler_config = ResourceHandlerConfigModel( commands=["get_value", "set_value", "stop"], attributes=["data"] ) diff --git a/mxcubeweb/core/components/component_base.py b/mxcubeweb/core/components/component_base.py index 408513872..4e205a094 100644 --- a/mxcubeweb/core/components/component_base.py +++ b/mxcubeweb/core/components/component_base.py @@ -1,12 +1,30 @@ import importlib import logging +from mxcubeweb.core.server.resource_handler import ( + ResourceHandlerFactory, +) + class ComponentBase: - def __init__(self, app, config): + def __init__(self, app, config, resource_handler_config=None): self.app = app self.config = config + if resource_handler_config: + cls_name = self.__class__.__name__.lower() + + ResourceHandlerFactory.create_or_get( + name=cls_name, + url_prefix="/mxcube/api/v0.1/" + cls_name, + handler_dict={cls_name: self}, + app=self.app, + exports=resource_handler_config.exports, + commands=resource_handler_config.commands, + attributes=resource_handler_config.attributes, + handler_type="component", + ) + def import_component(config, package="", module=""): _module = "mxcubeweb.core" diff --git a/mxcubeweb/core/components/log.py b/mxcubeweb/core/components/log.py new file mode 100644 index 000000000..2ca9a3faa --- /dev/null +++ b/mxcubeweb/core/components/log.py @@ -0,0 +1,58 @@ +import json +import logging + +from mxcubeweb import logging_handler +from mxcubeweb.core.components.component_base import ComponentBase +from mxcubeweb.core.models.configmodels import ( + ResourceHandlerConfigModel, +) + +hwr_logger = logging.getLogger("MX3.HWR") + + +class Log(ComponentBase): + def __init__(self, app, config): + super().__init__( + app, + config, + resource_handler_config=ResourceHandlerConfigModel( + exports=[ + { + "attr": "log", + "method": "GET", + "url": "/", + "decorators": [app.server.restrict], + }, + { + "attr": "log_frontend_traceback", + "method": "POST", + "url": "/log_frontend_traceback", + "decorators": [app.server.restrict], + }, + ] + ), + ) + + def log(self): + """ + Retrieve log messages + """ + messages = [] + + for handler in logging.getLogger("MX3.HWR").handlers: + if isinstance(handler, logging_handler.MX3LoggingHandler): + messages = handler.buffer + + return messages + + def log_frontend_traceback(self, args): + """ + Logs a UI traceback to the UI logger + """ + logging.getLogger("MX3.UI").error("------ Start of UI trace back ------") + logging.getLogger("MX3.UI").error("Traceback: %s", args["stack"]) + logging.getLogger("MX3.UI").error( + "State: %s", json.dumps(args["state"], indent=4) + ) + logging.getLogger("MX3.UI").error("------ End of UI trace back ------") + return {} diff --git a/mxcubeweb/core/models/configmodels.py b/mxcubeweb/core/models/configmodels.py index 1199c8954..a4d3293f2 100644 --- a/mxcubeweb/core/models/configmodels.py +++ b/mxcubeweb/core/models/configmodels.py @@ -5,10 +5,7 @@ Literal, ) -from pydantic.v1 import ( - BaseModel, - Field, -) +from pydantic.v1 import BaseModel, Field class FlaskConfigModel(BaseModel): @@ -208,7 +205,7 @@ class AppConfigModel(BaseModel): sso: SSOConfigModel | None -class AdapterResourceHandlerConfigModel(BaseModel): +class ResourceHandlerConfigModel(BaseModel): """ Used to define which adapter properties and methods that are exported over HTTP. An endpoint for each method and/or property @@ -255,7 +252,7 @@ class AdapterResourceHandlerConfigModel(BaseModel): """ url_prefix: str = Field("", description="URL prefix") - exports: list[dict[str, str]] = Field( + exports: list[dict[str, str | list]] = Field( [], description=( "List of dictionaires specifying each of the exported attributes or" diff --git a/mxcubeweb/core/server/resource_handler.py b/mxcubeweb/core/server/resource_handler.py index cf4ecfc54..6e3be5848 100644 --- a/mxcubeweb/core/server/resource_handler.py +++ b/mxcubeweb/core/server/resource_handler.py @@ -62,7 +62,7 @@ def assert_valid_type_arguments(func): log = logging.getLogger("MX3.HWR") -class AdapterResourceHandlerFactory: +class ResourceHandlerFactory: _handlers: ClassVar[dict] = {} @classmethod @@ -70,11 +70,12 @@ def create_or_get( # noqa: PLR0913 cls, name: str, url_prefix: str, - adapter_dict: dict[str, object], + handler_dict: dict[str, object], app: object, exports: list[dict[str, str]], commands: list[str], attributes: list[str], + handler_type="adapter", ) -> object: """ Return existing handler if it exists, otherwise create and register a new one. @@ -82,8 +83,13 @@ def create_or_get( # noqa: PLR0913 if name in cls._handlers: return cls._handlers[name] - handler = AdapterResourceHandler( - name, url_prefix, adapter_dict, app, exports, commands, attributes + if handler_type == "component": + resource_handler_cls = ComponentResourceHandler + else: + resource_handler_cls = AdapterResourceHandler + + handler = resource_handler_cls( + name, url_prefix, handler_dict, app, exports, commands, attributes ) cls._handlers[name] = handler return handler @@ -114,14 +120,14 @@ def register_with_server(cls, flask_server): rh.register_blueprint(flask_server) -class AdapterResourceHandler: +class ResourceHandler: openapi_spec = OpenAPISpec("docs", "/apidocs", "1.0.0", "MXCuBE Adapter API") def __init__( # noqa: PLR0913 self, name: str, url_prefix: str, - adapter_dict: dict[str, object], + handler_dict: dict[str, object], app: object, exports: list[dict[str, str]], commands: list[str], @@ -133,14 +139,14 @@ def __init__( # noqa: PLR0913 Args: name: Name of the blueprint. url_prefix: URL prefix for the blueprint. - adapter_dict: Dictionary mapping object IDs to adapter objects. + handler_dict: Dictionary mapping object IDs to adapter objects. app: mxcube app object, providing acccess to mxcubecore and server exports: Predefined list of exported commands/attributes. commands: List of command names to export. attributes: List of attribute names to export. """ self._bp = Blueprint(name, name, url_prefix=url_prefix) - self._adapter_dict = adapter_dict + self._handler_dict = handler_dict self._server = app.server # Store the server object to access its decorators self._app = app self._url_prefix = url_prefix @@ -167,12 +173,7 @@ def _create_routes_for_exports(self) -> None: Creates Flask routes dynamically for each exported command or attribute. """ for export in self._exports: - route = ( # Dynamic route: //set_value - f"//{export['attr']}" - ) - decorators = export["decorators"] - http_method = export["method"] - + route = self._get_handler_object_route(export) # For the time being we enforce the usage of pydantic models, int, float or # str for arguments to ensure safe validation of input. We rely on that # the pydantic models used are well specified. We validate the actual data @@ -186,13 +187,13 @@ def _create_routes_for_exports(self) -> None: view_func = self._create_view_func(export) # Apply decorators to the view function - view_func = self._apply_decorators(view_func, decorators) + view_func = self._apply_decorators(view_func, export["decorators"]) # Register route self._bp.add_url_rule( route, view_func=view_func, - methods=[http_method], + methods=[export["method"]], endpoint=export["attr"], ) msg = ( @@ -215,7 +216,7 @@ def _apply_decorators( """ return reduce(lambda f, decorator: decorator(f), decorators, view_func) - def _create_view_func(self, export: dict[str, str]) -> Callable: # noqa: C901 + def _create_view_func(self, export: dict[str, str]) -> Callable: """ Creates a Flask view function for handling requests dynamically. @@ -227,80 +228,63 @@ def _create_view_func(self, export: dict[str, str]) -> Callable: # noqa: C901 Callable: The view function. """ - def _view_func(object_id: str, *args, **kwargs) -> any: # noqa: ARG001 - # Validate object id - if not valid_object_id(object_id): - msg = f"Invalid object id '{object_id}'" - log.error(msg) - return jsonify({"error": msg}), 400 - - # Check if the object_id exists in the adapter_dict - obj = self._app.mxcubecore.get_adapter(object_id) + def _validate_params_and_call_handler_func( + self, + export: dict[str, str], + handler_obj: object, + ) -> dict | Response: + """ + Validates parameters from the request and calls the handler function. + Args: + export: Export definition with method, attr, and decorators. + handler_obj: The handler object to call. + Returns: + The result of the handler function call or an error response. + """ - if not obj: - msg = f"Object '{object_id}' not found" - log.error(msg) - return jsonify({"error": msg}), 404 + # Get the method and its annotations + handler_func = getattr(handler_obj, export["attr"]) + annotations = handler_func.__annotations__ + http_method = export["method"] - # We ensure that the object has the desired method - if not hasattr(obj, export["attr"]): - return ( - jsonify( - { - "error": ( - f"Method '{export['attr']}' not found on object" - f" '{object_id}'" - ) - } - ), - 404, - ) + # Prepare data for all required arguments + validated_data = {} + param_data = self._extract_param_data(http_method) - # Get the method and its annotations - view_func = getattr(obj, export["attr"]) - annotations = view_func.__annotations__ - http_method = export["method"] - - # Prepare data for all required arguments - validated_data = {} - param_data = self._extract_param_data(http_method) - - # Validate parameters from the request - for param_name, param_type in annotations.items(): - if param_name == "return": # Skip the return annotation - continue - - try: - if param_data is not None: - validated_data[param_name] = self._validate_param_data( - param_name, param_type, param_data - ) - except (ValueError, TypeError) as ex: - log.error(str(ex)) # noqa: TRY400 - msg = f"Invalid input for '{param_name}'" - return ( - jsonify({"error": msg}), - 400, - ) + # Validate parameters from the request + for param_name, param_type in annotations.items(): + if param_name == "return": # Skip the return annotation + continue - # Call the view function with validated data try: - result = view_func(**validated_data) - except Exception: - msg = "Exception raised when calling view function" - log.exception(msg) - return jsonify({"error": msg}), 500 - else: - # Handle and serialize the result - return self._handle_view_result(result) + if param_data is not None: + validated_data[param_name] = self._validate_param_data( + param_name, param_type, param_data + ) + except (ValueError, TypeError) as ex: + log.error(str(ex)) # noqa: TRY400 + msg = f"Invalid input for '{param_name}'" + return ( + jsonify({"error": msg}), + 400, + ) - return _view_func + # Call the view function with validated data + try: + result = handler_func(**validated_data) + except Exception: + msg = "Exception raised when calling view function" + log.exception(msg) + return jsonify({"error": msg}), 500 + else: + # Handle and serialize the result + return self._handle_view_result(result) def _assert_valid_type_arguments(self, export): """ Ensures the method referenced in the export uses Pydantic arguments. """ - obj = next(iter(self._adapter_dict.values())) + obj = next(iter(self._handler_dict.values())) assert_valid_type_arguments(getattr(obj, export["attr"])) def _create_openapi_doc_for_view(self, route, export): @@ -310,7 +294,7 @@ def _create_openapi_doc_for_view(self, route, export): # Get the first adapter object, the signature are all the same (same class) so # any will do for documentation purpose http_method = export["method"] - obj = next(iter(self._adapter_dict.values())) + obj = next(iter(self._handler_dict.values())) view_func = getattr(obj, export["attr"]) annotations = view_func.__annotations__ @@ -511,3 +495,142 @@ def register_blueprint(self, parent_bp) -> None: f"Blueprint '{self._bp.name}' ({self._url_prefix}) registered with server." ) log.debug(msg) + + +class ComponentResourceHandler(ResourceHandler): + """ + AdapterResourceHandler is a Flask resource handler that dynamically creates routes + for hardware objects based on their attributes and methods. + It supports GET and PUT requests for attributes and commands respectively. + """ + + def __init__( # noqa: PLR0913 + self, + name: str, + url_prefix: str, + handler_dict: dict[str, object], + app: object, + exports: list[dict[str, str]], + commands: list[str], + attributes: list[str], + ) -> None: + super().__init__( + name, url_prefix, handler_dict, app, exports, commands, attributes + ) + + def _get_handler_object_route(self, export) -> str: + """ + Returns the base route for the resource handler. + """ + return f"{export['url']}" + + def _create_view_func(self, export: dict[str, str]) -> Callable: + """ + Creates a Flask view function for handling requests dynamically. + + Args: + route (str): URL route. + export (dict): Export definition with method, attr, and decorators. + + Returns: + Callable: The view function. + """ + + def _view_func(*args, **kwargs) -> any: # noqa: ARG001 + # Get component from handler_dict, there is only one handler object for + # components + component_obj = next(iter(self._handler_dict.values())) + component_name = next(iter(self._handler_dict.keys())) + + # We ensure that the component has the desired method + if not hasattr(component_obj, export["attr"]): + return ( + jsonify( + { + "error": ( + f"Method '{export['attr']}' not found on object" + f" '{component_name}'" + ) + } + ), + 404, + ) + + return self._validate_params_and_call_handler_func(export, component_obj) + + return _view_func + + +class AdapterResourceHandler(ResourceHandler): + """ + AdapterResourceHandler is a Flask resource handler that dynamically creates routes + for hardware objects based on their attributes and methods. + It supports GET and PUT requests for attributes and commands respectively. + """ + + def __init__( # noqa: PLR0913 + self, + name: str, + url_prefix: str, + handler_dict: dict[str, object], + app: object, + exports: list[dict[str, str]], + commands: list[str], + attributes: list[str], + ) -> None: + super().__init__( + name, url_prefix, handler_dict, app, exports, commands, attributes + ) + + def _get_handler_object_route(self, export) -> str: + """ + Returns the base route for the resource handler. + """ + # Dynamic route for handler object with id object_id, i.e: + # //set_value + return f"//{export['attr']}" + + def _create_view_func(self, export: dict[str, str]) -> Callable: + """ + Creates a Flask view function for handling requests dynamically. + + Args: + route (str): URL route. + export (dict): Export definition with method, attr, and decorators. + + Returns: + Callable: The view function. + """ + + def _view_func(object_id: str, *args, **kwargs) -> any: # noqa: ARG001 + # Validate object id + if not valid_object_id(object_id): + msg = f"Invalid object id '{object_id}'" + log.error(msg) + return jsonify({"error": msg}), 400 + + # Check if the object_id exists in the handler_dict + obj = self._app.mxcubecore.get_adapter(object_id) + + if not obj: + msg = f"Object '{object_id}' not found" + log.error(msg) + return jsonify({"error": msg}), 404 + + # We ensure that the object has the desired method + if not hasattr(obj, export["attr"]): + return ( + jsonify( + { + "error": ( + f"Method '{export['attr']}' not found on object" + f" '{object_id}'" + ) + } + ), + 404, + ) + + return self._validate_params_and_call_handler_func(export, obj) + + return _view_func diff --git a/mxcubeweb/routes/log.py b/mxcubeweb/routes/log.py deleted file mode 100644 index 98b995136..000000000 --- a/mxcubeweb/routes/log.py +++ /dev/null @@ -1,46 +0,0 @@ -import json -import logging - -from flask import ( - Blueprint, - jsonify, - make_response, - request, -) - -from mxcubeweb import logging_handler - - -def init_route(app, server, url_prefix): - bp = Blueprint("log", __name__, url_prefix=url_prefix) - - @bp.route("/", methods=["GET"]) - @server.restrict - def log(): - """ - Retrieve log messages - """ - messages = [] - - for handler in logging.getLogger("MX3.HWR").handlers: - if isinstance(handler, logging_handler.MX3LoggingHandler): - messages = handler.buffer - - return jsonify(messages) - - @server.restrict - @bp.route("/log_frontend_traceback", methods=["POST"]) - def log_front_end_traceback(): - """ - Logs a UI traceback to the UI logger - """ - args = request.get_json() - logging.getLogger("MX3.UI").error("------ Start of UI trace back ------") - logging.getLogger("MX3.UI").error("Traceback: %s " % args["stack"]) - logging.getLogger("MX3.UI").error( - "State: %s " % json.dumps(args["state"], indent=4) - ) - logging.getLogger("MX3.UI").error("------ End of UI trace back ------") - return make_response("", 200) - - return bp diff --git a/mxcubeweb/server.py b/mxcubeweb/server.py index 5b15df53b..03988cb8c 100644 --- a/mxcubeweb/server.py +++ b/mxcubeweb/server.py @@ -24,7 +24,7 @@ ) from mxcubeweb.core.server.csp import CSPMiddleware from mxcubeweb.core.server.limiter import init_limiter -from mxcubeweb.core.server.resource_handler import AdapterResourceHandlerFactory +from mxcubeweb.core.server.resource_handler import ResourceHandlerFactory from mxcubeweb.core.util import networkutils @@ -125,7 +125,6 @@ def register_routes(mxcube): ) from mxcubeweb.routes.harvester import init_route as init_harvester_route from mxcubeweb.routes.lims import init_route as init_lims_route - from mxcubeweb.routes.log import init_route as init_log_route from mxcubeweb.routes.login import init_route as init_login_route from mxcubeweb.routes.main import init_route as init_main_route from mxcubeweb.routes.queue import init_route as init_queue_route @@ -155,8 +154,6 @@ def register_routes(mxcube): Server._register_route(init_lims_route, mxcube, f"{url_root_prefix}/lims") - Server._register_route(init_log_route, mxcube, f"{url_root_prefix}/log") - Server._register_route(init_login_route, mxcube, f"{url_root_prefix}/login") Server._register_route(init_main_route, mxcube, f"{url_root_prefix}") @@ -165,6 +162,10 @@ def register_routes(mxcube): Server._register_route(init_ra_route, mxcube, f"{url_root_prefix}/ra") + Server._register_route( + init_workflow_route, mxcube, f"{url_root_prefix}/workflow" + ) + Server._register_route( init_sampleview_route, mxcube, @@ -177,15 +178,11 @@ def register_routes(mxcube): f"{url_root_prefix}/sample_changer", ) - Server._register_route( - init_workflow_route, mxcube, f"{url_root_prefix}/workflow" - ) - Server._register_route( init_harvester_route, mxcube, f"{url_root_prefix}/harvester" ) - AdapterResourceHandlerFactory.register_with_server(Server.flask) + ResourceHandlerFactory.register_with_server(Server.flask) @staticmethod def emit(*args, **kwargs): diff --git a/test/conftest.py b/test/conftest.py index b537eecb5..335ac0b7f 100644 --- a/test/conftest.py +++ b/test/conftest.py @@ -36,7 +36,7 @@ _SIO_TEST_CLIENT = None -from mxcubeweb.core.server.resource_handler import AdapterResourceHandlerFactory +from mxcubeweb.core.server.resource_handler import ResourceHandlerFactory @pytest.fixture(autouse=True) @@ -44,7 +44,7 @@ def cleanup_adapter_resource_handler(): yield # Teardown - AdapterResourceHandlerFactory.unregister_all() + ResourceHandlerFactory.unregister_all() def noop_register(*args, **kwargs): diff --git a/ui/src/reducers/logger.js b/ui/src/reducers/logger.js index c3992803a..08dd8f350 100644 --- a/ui/src/reducers/logger.js +++ b/ui/src/reducers/logger.js @@ -11,7 +11,7 @@ function loggerReducer(state = INITIAL_STATE, action = {}) { }; } case 'SET_INITIAL_STATE': { - return { ...state, logRecords: [...action.data.logger] }; + return { ...state, logRecords: [...action.data.logger.return] }; } default: { return state;