oarepo-fsm 2.0.4
OArepo FSM library for record state transitions
License: MIT
Keywords: fsm fsm-library oarepo records state-management

..
    Copyright (C) 2020 CESNET.

    oarepo-fsm is free software; you can redistribute it and/or modify it
    under the terms of the MIT License; see LICENSE file for more details.

============
 oarepo-fsm
============

.. image:: https://img.shields.io/travis/oarepo/oarepo-fsm.svg
        :target: https://travis-ci.org/oarepo/oarepo-fsm

.. image:: https://img.shields.io/coveralls/oarepo/oarepo-fsm.svg
        :target: https://coveralls.io/r/oarepo/oarepo-fsm

.. image:: https://img.shields.io/github/tag/oarepo/oarepo-fsm.svg
        :target: https://github.com/oarepo/oarepo-fsm/releases

.. image:: https://img.shields.io/pypi/dm/oarepo-fsm.svg
        :target: https://pypi.python.org/pypi/oarepo-fsm

.. image:: https://img.shields.io/github/license/oarepo/oarepo-fsm.svg
        :target: https://github.com/oarepo/oarepo-fsm/blob/master/LICENSE

OArepo FSM  library for record state transitions built on top of the https://pypi.org/project/sqlalchemy-fsm/ library.


Quickstart
----------

Run the following commands to bootstrap your environment ::

    git clone https://github.com/oarepo/oarepo-fsm
    cd oarepo-fsm
    pip install -e .[devel]


Configuration
-------------

Check that correct record_class is being used on the RECORDS_REST_ENDPOINT's item_route ::

    item_route='/records/<pid(recid,record_class="yourapp.models:RecordModelFSM"):pid_value>',

To automatically add a link to the FSM endpoint to your record links, use the following ``links_factory_imp`` in
your **RECORDS_REST_ENDPOINTS** config ::

    links_factory_imp='oarepo_fsm.links:record_fsm_links_factory',

If you wish to activate FSM on a certain Record enpoints only, put in your config ::

    OAREPO_FSM_ENABLED_REST_ENDPOINTS = ['recid']

Where **recid** is the prefix key into your **RECORDS_REST_ENDPOINTS** configuration.
This library activates FSM on all endpoints using `record_class` inherited from `FSMMixin` otherwise.

Usage
-----

In order to use this library, you need to define a Record
model in your app, that inherits from a **FSMMixin** column ::

    from invenio_records import Record
    from oarepo_fsm.mixins import FSMMixin

    class RecordModelFSM(FSMMixin, Record):
    ...

To define FSM transitions on this class, create methods decorated with **@transition(..)** e.g. ::

    @transition(
        src=['open', 'archived'],
        dest='published',
        required=['id'],
        permissions=[editor_permission],
        commit_record=True)
    def publish(self, **kwargs):
        print('record published')

Where decorator parameters mean:

  - **src**: record must be in one of the source states before transition could happen
  - **dest**: target state of the transition
  - **required**: a list of required ``**kwargs`` that must be passed to the ``@transition`` decorated function
  - **permissions**: currently logged user must have at least one of the permissions to execute the transition
  - **commit_record**: should the changes made in a record be commited after the function returns?

A transition-decorated function can optionally return a custom flask Response or a JSON-serializable
dict to be provided to user in a JSON response.

"state" field name
------------------

To change state field name, set ::

    class MyRecord(FSMMixin, ...):
        STATE_FIELD = 'administrative_props.state'


Note that the field name might be nested. If the container element does not exist,
it will be created on the first transition.

Note: if you use your own state field, do not use marshamllow mixin, json schema
or included mappings. Write/use your own.

REST API Usage
--------------

To get current record state and possible transitions (only transitions that you have permission to invoke will be returned) ::

    GET <record_rest_item_endpoint>
    >>>
    {
        metadata: {
            state: <current state of the record>
            ... other record metadata
        }
        links: {
            self: ...,
            "transitions": {
                <fsm_transition1_name>: <transition_url>,
                <fsm_transition2_name>: <transition_url>,
            },
            ...
        }
    }

To invoke a specific transition transition, do ::

    POST <record_rest_endpoint>/<fsm_transition_name>


Further documentation is available on
https://oarepo-fsm.readthedocs.io/


Permission factories
--------------------

Sometimes access to records should be governed by the state of the record. For example,
if the record is in ``state=editing``, any editor can make changes. If it is ``state=approving``,
only the curator can modify the record.

On REST level, modification permissions are governed by permission factories ::

    from invenio_records_rest.utils import allow_all, deny_all
    RECORDS_REST_ENDPOINTS = dict(
        recid=dict(
           create_permission_factory_imp=deny_all,
           delete_permission_factory_imp=deny_all,
           update_permission_factory_imp=deny_all,
           read_permission_factory_imp=allow_all,
       )
    )

This library provides the following factories and helpers:

   * ``transition_required(*transitions)`` allows user if
     he is entitled to perform any of the transitions (
     method names) on the current record
   * ``states_required(*states, state_field="state"`` allows
     anyone if the record is in any of the states mentioned
   * ``require_all(*perms_or_factories)`` allows user only if all
     permissions allow. Use it with states_required as follows ::

        require_all(
            states_required('editing'),
            editing_user_permission_factory
        )

     where editing_user_permission_factory is a permission factory allowing only
     editing users.
   * ``require_any(*perms_or_factories)`` allows user if any of
     the permissions allow. Example ::

        require_any(
            require_all(
                states_required('editing'),
                editing_user_permission_factory
            ),
            require_all(
                states_required('editing', 'approving),
                curator_user_permission_factory
            ),
        )


..
    Copyright (C) 2020 CESNET.

    oarepo-fsm is free software; you can redistribute it and/or modify it
    under the terms of the MIT License; see LICENSE file for more details.

Changes
=======

Version 0.1.0 (released TBD)

- Initial public release.