magpie.ui.utils =============== .. py:module:: magpie.ui.utils Attributes ---------- .. autoapisummary:: magpie.ui.utils.CONTENT_TYPE_JSON magpie.ui.utils.LOGGER Classes ------- .. autoapisummary:: magpie.ui.utils.UserGroupStatus magpie.ui.utils.BaseViews magpie.ui.utils.AdminRequests Functions --------- .. autoapisummary:: magpie.ui.utils.get_exception_info magpie.ui.utils.get_request_info magpie.ui.utils.get_logged_user magpie.ui.utils.get_constant magpie.ui.utils.mask_credentials magpie.ui.utils.get_header magpie.ui.utils.get_json magpie.ui.utils.get_logger magpie.ui.utils.get_magpie_url magpie.ui.utils.check_response magpie.ui.utils.request_api magpie.ui.utils.redirect_error magpie.ui.utils.handle_errors Module Contents --------------- .. py:function:: get_exception_info(response: Union[pyramid.httpexceptions.HTTPException, pyramid.request.Request, pyramid.response.Response], content: Optional[magpie.typedefs.JSON] = None, exception_details: bool = False) -> magpie.typedefs.JSON Obtains additional exception content details about the :paramref:`response` according to available information. .. py:function:: get_request_info(request: Union[pyramid.request.Request, pyramid.httpexceptions.HTTPException], default_message: Optional[magpie.typedefs.Str] = None, exception_details: bool = False) -> magpie.typedefs.JSON Obtains additional content details about the :paramref:`request` according to available information. .. py:function:: get_logged_user(request: pyramid.request.Request) -> Optional[magpie.models.User] .. py:function:: get_constant(constant_name: magpie.typedefs.Str, settings_container: Optional[magpie.typedefs.AnySettingsContainer] = None, settings_name: Optional[magpie.typedefs.Str] = None, default_value: Optional[magpie.typedefs.SettingValue] = None, raise_not_set: bool = True, raise_missing: bool = True, print_missing: bool = False, empty_missing: bool = False) -> magpie.typedefs.SettingValue Search in order for matched value of :paramref:`constant_name`: 1. search in :py:data:`MAGPIE_CONSTANTS` 2. search in settings if specified 3. search alternative setting names (see below) 4. search in :mod:`magpie.constants` definitions 5. search in environment variables Parameter :paramref:`constant_name` is expected to have the format ``MAGPIE_[VARIABLE_NAME]`` although any value can be passed to retrieve generic settings from all above-mentioned search locations. If :paramref:`settings_name` is provided as alternative name, it is used as is to search for results if :paramref:`constant_name` was not found. Otherwise, ``magpie.[variable_name]`` is used for additional search when the format ``MAGPIE_[VARIABLE_NAME]`` was used for :paramref:`constant_name` (i.e.: ``MAGPIE_ADMIN_USER`` will also search for ``magpie.admin_user`` and so on for corresponding constants). :param constant_name: key to search for a value :param settings_container: WSGI application settings container (if not provided, uses found one in current thread) :param settings_name: alternative name for `settings` if specified :param default_value: default value to be returned if not found anywhere, and exception raises are disabled. :param raise_not_set: raise an exception if the found key is ``None``, search until last case if others are ``None`` :param raise_missing: raise exception if key is not found anywhere :param print_missing: print message if key is not found anywhere, return ``None`` :param empty_missing: consider an empty value for an existing key as if it was missing (i.e.: as if not set). :returns: found value or `default_value` :raises ValueError: if resulting value is invalid based on options (by default raise missing/empty/``None`` value) :raises LookupError: if no appropriate value could be found from all search locations (according to options) .. py:class:: UserGroupStatus Bases: :py:obj:`magpie.utils.FlexibleNameEnum` Supported statuses of user-group relationships. .. py:attribute:: ALL :value: 'all' .. py:attribute:: ACTIVE :value: 'active' .. py:attribute:: PENDING :value: 'pending' .. py:method:: allowed() -> List[magpie.typedefs.Str] :classmethod: Returns all supported representation values that can be mapped to a valid status. .. py:function:: mask_credentials(container: magpie.typedefs.JSON, redact: magpie.typedefs.Str = '[REDACTED]', flags: Optional[List[magpie.typedefs.Str]] = None, parent: Optional[magpie.typedefs.Str] = None) -> magpie.typedefs.JSON Masks away any credential matched against :paramref:`flags` recursively from JSON :paramref:`container`. Matched credential entries are replaced by :paramref:`redact`. List items are all replaced by the same :paramref:`redact` when their :paramref:`parent` field name is matched. :param container: JSON container to mask. If starting with a list on top-level, first level children will not be masked unless parent is provided. :param redact: string by which to replace flagged fields. :param flags: field names (partial matches) to flag for masking. :param parent: reference to contained elements if in a listing format rather than mapping. :return: masked credentials JSON container. .. py:data:: CONTENT_TYPE_JSON :value: 'application/json' .. py:function:: get_header(header_name: magpie.typedefs.Str, header_container: magpie.typedefs.AnyHeadersType, default: (Optional[magpie.typedefs.Str], Optional[Union[magpie.typedefs.Str, List[magpie.typedefs.Str]]], bool) = None, split: Optional[Union[magpie.typedefs.Str, List[magpie.typedefs.Str]]] = None, multi: bool = False, pop: bool = False) -> Optional[Union[magpie.typedefs.Str, List[magpie.typedefs.Str]]] Retrieves ``header_name`` by fuzzy match (independently of upper/lower-case and underscore/dash) from various framework implementations of ``Headers``. If ``split`` is specified, the matched ``header_name`` is first split with it and the first item is returned. This allows to parse complex headers (e.g.: ``text/plain; charset=UTF-8`` to ``text/plain`` with ``split=';'``). :param header_name: Header to find. :param header_container: Where to look for `header_name`. :param default: Value to returned if `header_container` is invalid or `header_name` could not be found. :param split: Character(s) to use to split the *found* `header_name`. :param multi: Return extracted header as array of multiple values or return a single value on fist match. :param pop: Remove the header from the container if found. :returns: Found header value(s) if applicable. .. py:function:: get_json(request_or_response: Union[magpie.typedefs.AnyRequestType, magpie.typedefs.AnyResponseType]) -> magpie.typedefs.JSON Retrieves the 'JSON' body of a response using the property/callable according to the response's implementation. .. py:function:: get_logger(name: magpie.typedefs.Str, level: Optional[int] = None, force_stdout: bool = None, message_format: Optional[magpie.typedefs.Str] = None, datetime_format: Optional[magpie.typedefs.Str] = None) -> logging.Logger Immediately sets the logger level to avoid duplicate log outputs from the `root logger` and `this logger` when `level` is ``logging.NOTSET``. .. py:function:: get_magpie_url(container: Optional[magpie.typedefs.AnySettingsContainer] = None) -> magpie.typedefs.Str Obtains the configured Magpie URL entrypoint based on the various combinations of supported configuration settings. .. seealso:: Documentation section :ref:`config_app_settings` for available setting combinations. :param container: container that provides access to application settings. :return: resolved Magpie URL .. py:data:: LOGGER .. py:function:: check_response(response: magpie.typedefs.AnyResponseType) -> magpie.typedefs.AnyResponseType :returns: response if the HTTP status code is successful. :raises HTTPError: (of appropriate type) if the response corresponds to an HTTP error code .. py:function:: request_api(request: pyramid.request.Request, path: magpie.typedefs.Str, method: magpie.typedefs.Str = 'GET', data: Optional[Union[magpie.typedefs.JSON, magpie.typedefs.Str]] = None, headers: Optional[magpie.typedefs.HeadersType] = None, cookies: Optional[magpie.typedefs.CookiesType] = None) -> magpie.typedefs.AnyResponseType Use a pyramid sub-request to request Magpie API routes via the UI. This avoids max retries and closed connections when using 1 worker (eg: during tests). Some information is retrieved from :paramref:`request` to pass down to the sub-request (eg: cookies). If they are passed as argument, corresponding values will override the ones found in :paramref:`request`. All sub-requests to the API are assumed to be :py:data:`magpie.common.CONTENT_TYPE_JSON` unless explicitly overridden with :paramref:`headers`. Headers are also looked for additional ``Set-Cookie`` header in case they need to be passed down to :paramref:`cookies`. :param request: incoming Magpie UI request that requires sub-request to Magpie API, to retrieve required details. :param path: local Magpie API path (relative to root without URL). :param method: HTTP method to send the API sub-request. :param data: JSON dictionary or literal string content of the request body. :param headers: override headers to employ for the API sub-request. Defaults to JSON Accept & Content-Type headers. :param cookies: Override cookies to employ for the API sub-request. Defaults to current logged user. For empty cookies (no user), explicitly provide an empty dictionary. .. py:function:: redirect_error(request: pyramid.request.Request, code: int = None, content: Optional[magpie.typedefs.JSON] = None) -> magpie.typedefs.AnyResponseType Redirects the contents to be rendered by the UI 'error' page. :param request: incoming request that resulted into some kind of error. :param code: explicit HTTP status code for the error response, extracted from contents if otherwise available. :param content: any body content provided as error response from the API. .. py:function:: handle_errors(func: Callable) -> Callable Decorator that encapsulates the operation in a try/except block, and redirects the response to the UI error page with API error contents. In worst case scenario where the operation cannot figure out what to do with the exception response, raise the most basic :class:`HTTPInternalServerError` that can be formulated from available details. .. seealso:: :func:`redirect_error` .. py:class:: BaseViews(request) Bases: :py:obj:`object` Base methods for Magpie UI pages. .. py:attribute:: MAGPIE_FIXED_GROUP_MEMBERSHIPS :value: [] Special :term:`Group` memberships that cannot be edited. .. py:attribute:: MAGPIE_FIXED_GROUP_EDITS :value: [] Special :term:`Group` details that cannot be edited. .. py:attribute:: MAGPIE_FIXED_USERS :value: [] Special :term:`User` details that cannot be edited. .. py:attribute:: MAGPIE_FIXED_USERS_REFS :value: [] Special :term:`User` that cannot have any relationship edited. This includes both :term:`Group` memberships and :term:`Permission` references. .. py:attribute:: MAGPIE_USER_PWD_LOCKED :value: [] Special :term:`User` that *could* self-edit themselves, but is disabled since conflicting with other policies. .. py:attribute:: MAGPIE_USER_PWD_DISABLED :value: [] Special :term:`User` where password cannot be edited (managed by `Magpie` configuration settings). .. py:attribute:: MAGPIE_ANONYMOUS_GROUP :value: None Reference to :py:data:`magpie.constants.MAGPIE_ANONYMOUS_GROUP` for convenience in UI pages. .. py:method:: add_template_data(data: Optional[Dict[magpie.typedefs.Str, Any]] = None) -> Dict[magpie.typedefs.Str, Any] Adds required template data for the 'heading' mako template applied to every UI page. .. py:method:: render(template: magpie.typedefs.Str, data: Optional[Dict[magpie.typedefs.Str, Any]] = None) -> pyramid.response.Response Render the response with an explicit Mako template reference. Views that are decorated by :func:`pyramid.view.view_config` or registered by :meth:`pyramid.config.Configurator.add_view` with a ``renderer`` parameter do not require to call this function as it is auto-resolved with the submitted :paramref:`data`. .. py:class:: AdminRequests(request) Bases: :py:obj:`BaseViews` Regroups multiple administration-level operations to be dispatched to the API requests. .. py:method:: create_user_default_template_data(data) Generates all the default values for the various fields employed for display purposes of the user creation form. :param data: any template data that should override the defaults. :return: updated template data with defaults and overridden values. .. seealso: :meth:`create_user` .. py:method:: get_admin_session() -> magpie.typedefs.CookiesType Temporarily login as default administrator to execute an elevated operation that the current user cannot make. .. warning:: Cookies *MUST NOT* be preserved or memorized, to avoid user gaining restricted access. This is intended only for basic operations such as validating information. Care must be taken such information retrieved this way do not provide a way of non-administrator to indirectly infer some otherwise protected information. User sparingly. :returns: Cookies of the administrator login. .. py:method:: get_all_groups(first_default_group=None) .. py:method:: get_group_info(group_name: magpie.typedefs.Str) -> magpie.typedefs.JSON .. py:method:: get_group_users(group_name: magpie.typedefs.Str, user_group_status: magpie.models.UserGroupStatus = UserGroupStatus.ACTIVE) -> List[magpie.typedefs.Str] .. py:method:: update_group_info(group_name: magpie.typedefs.Str, group_info: magpie.typedefs.JSON) -> magpie.typedefs.JSON .. py:method:: delete_group(group_name: magpie.typedefs.Str) -> magpie.typedefs.JSON .. py:method:: get_user_groups(user_name: magpie.typedefs.Str, user_group_status: magpie.models.UserGroupStatus = UserGroupStatus.ACTIVE) -> List[magpie.typedefs.Str] .. py:method:: get_user_names() Obtains all user names. .. py:method:: get_user_statuses(status: Union[magpie.typedefs.Str, int] = 0) -> List[magpie.typedefs.Str] Obtains all user names that have the corresponding status value. .. py:method:: get_user_emails() .. py:method:: get_user_details(status: Optional[Union[str, int]] = None, cookies: Optional[magpie.typedefs.CookiesType] = None) -> List[magpie.typedefs.JSON] Obtains all user details, optionally filtered to by corresponding status value. Employ this method to avoid multiple requests fetching individual information. .. seealso:: - :meth:`get_user_emails` - :meth:`get_user_names` - :meth:`get_user_statuses` .. py:method:: get_resource_types() :return: dictionary of all resources as {id: 'resource_type'} :rtype: dict .. py:method:: flatten_tree_resource(resource_node, resource_dict) :staticmethod: :param resource_node: any-level dictionary composing the resources tree :param resource_dict: reference of flattened dictionary across levels :return: flattened dictionary `resource_dict` of all {id: 'resource_type'} :rtype: dict .. py:method:: get_services(cur_svc_type) .. py:method:: get_service_data(service_name) .. py:method:: get_service_types() .. py:method:: update_service_name(old_service_name, new_service_name, service_push) .. py:method:: update_service_url(service_name, new_service_url, service_push) .. py:method:: create_user(data) Processes the user creation form with fields input data. All the fields are pre- and post-validated according to expected behaviour by the API. Pre-validations attempt *soft* checks to detect as many potential errors such that they can all be simultaneously displayed on the form, to avoid back-and-forth erroneous submissions by the user. Post-validations are the *hard* checks imposed by the API, which include some of the pre-checks. Whenever some failure occurs, returned data will contain ``is_error`` with ``True`` or ``False`` accordingly. Following successful request without error, the :term:`User` will be created. It is up to the calling function to redirect the response and further process the returned data as needed. :param data: initial templated data overrides according to who is initiation the user creation. :return: updated template data with any relevant error messages and statuses if applicable. :raises HTTPException: any unhandled or unknown HTTP error received from the API. .. seealso: :meth:`create_user_default_template_data`