Squashed 'SpiffWorkflow/' changes from 0e61be85c..11e4b4f96

11e4b4f96 fix two incorrectly names attributes in node parser
abec918a8 Merge pull request #291 from rachfop/fix-grammar
a597f9ce9 Fixes grammar, typos, and spellings
00ffaf067 Assure that when something goes wrong calling a service task that we get as much good information about the problem as possible.
c044b5646 Fix that dreadful unknown "KeyError" exception that was cropping up. Adding a bit of detail to the spiffworkflow exceptions when a duplicate process model is found. Disable the submit button on tasks after you click submit (avoid the double click and give users a better experience)

git-subtree-dir: SpiffWorkflow
git-subtree-split: 11e4b4f96f03a036bd29632f1560e347a4e69aae
This commit is contained in:
Dan 2023-02-14 16:51:09 -05:00
parent c237e218b2
commit fec5934d38
13 changed files with 135 additions and 137 deletions

View File

@ -7,7 +7,7 @@ Filtering Tasks
In our earlier example, all we did was check the lane a task was in and display In our earlier example, all we did was check the lane a task was in and display
it along with the task name and state. it along with the task name and state.
Lets take a look at a sample workflow with lanes: Let's take a look at a sample workflow with lanes:
.. figure:: figures/lanes.png .. figure:: figures/lanes.png
:scale: 30% :scale: 30%
@ -15,7 +15,7 @@ Lets take a look at a sample workflow with lanes:
Workflow with lanes Workflow with lanes
To get all of the tasks that are ready for the 'Customer' workflow, we could To get all the tasks that are ready for the 'Customer' workflow, we could
specify the lane when retrieving ready user tasks: specify the lane when retrieving ready user tasks:
.. code:: python .. code:: python
@ -50,14 +50,14 @@ Logging
Spiff provides several loggers: Spiff provides several loggers:
- the :code:`spiff` logger, which emits messages when a workflow is initialized and when tasks change state - the :code:`spiff` logger, which emits messages when a workflow is initialized and when tasks change state
- the :code:`spiff.metrics` logger, which emits messages containing the elapsed duration of tasks - the :code:`spiff.metrics` logger, which emits messages containing the elapsed duration of tasks
- the :code:`spiff.data` logger, which emits message when task or workflow data is updated. - the :code:`spiff.data` logger, which emits a message when task or workflow data is updated.
Log level :code:`INFO` will provide reasonably detailed information about state changes. Log level :code:`INFO` will provide reasonably detailed information about state changes.
As usual, log level :code:`DEBUG` will probably provide more logs than you really want As usual, log level :code:`DEBUG` will probably provide more logs than you really want
to see, but the logs will contain the task and task internal data. to see, but the logs will contain the task and task internal data.
Data can be included at any level less than :code:`INFO`. In our exmple application, Data can be included at any level less than :code:`INFO`. In our example application,
we define a custom log level we define a custom log level
.. code:: python .. code:: python
@ -76,7 +76,7 @@ Serialization
Serialization Changed in Version 1.1.7. Serialization Changed in Version 1.1.7.
Support for pre-1.1.7 serialization will be dropped in a future release. Support for pre-1.1.7 serialization will be dropped in a future release.
The old serialization method still works but it is deprecated. The old serialization method still works, but it is deprecated.
To migrate your system to the new version, see "Migrating between To migrate your system to the new version, see "Migrating between
serialization versions" below. serialization versions" below.
@ -131,7 +131,7 @@ To restore the workflow:
with open(args.restore) as state: with open(args.restore) as state:
wf = serializer.deserialize_json(state.read()) wf = serializer.deserialize_json(state.read())
The workflow serializer is designed to be flexible and modular and as such is a little complicated. It has The workflow serializer is designed to be flexible and modular, and as such is a little complicated. It has
two components: two components:
- a workflow spec converter (which handles workflow and task specs) - a workflow spec converter (which handles workflow and task specs)
@ -141,7 +141,7 @@ The default workflow spec converter likely to meet your needs, either on its own
:code:`UserTask` and :code:`BusinessRuleTask` in the :code:`camnuda` or :code:`spiff` and :code:`dmn` subpackages :code:`UserTask` and :code:`BusinessRuleTask` in the :code:`camnuda` or :code:`spiff` and :code:`dmn` subpackages
of this library, and all you'll need to do is add them to the list of task converters, as we did above. of this library, and all you'll need to do is add them to the list of task converters, as we did above.
However, he default data converter is very simple, adding only JSON-serializable conversions of :code:`datetime` However, the default data converter is very simple, adding only JSON-serializable conversions of :code:`datetime`
and :code:`timedelta` objects (we make these available in our default script engine) and UUIDs. If your and :code:`timedelta` objects (we make these available in our default script engine) and UUIDs. If your
workflow or task data contains objects that are not JSON-serializable, you'll need to extend ours, or extend workflow or task data contains objects that are not JSON-serializable, you'll need to extend ours, or extend
its base class to create one of your own. its base class to create one of your own.
@ -245,7 +245,7 @@ The code would then look more like this:
Because the serializer is highly customizable, we've made it possible for you to manage your own versions of the Because the serializer is highly customizable, we've made it possible for you to manage your own versions of the
serialization. You can do this by passing a version number into the serializer, which will be embedded in the serialization. You can do this by passing a version number into the serializer, which will be embedded in the
json of all workflows. This allow you to modify the serialization and customize it over time, and still manage json of all workflows. This allows you to modify the serialization and customize it over time, and still manage
the different forms as you make adjustments without leaving people behind. the different forms as you make adjustments without leaving people behind.
Versioned Serializer Versioned Serializer
@ -273,7 +273,7 @@ security reasons.
and :code:`exec`! If you have security concerns, you should definitely investigate and :code:`exec`! If you have security concerns, you should definitely investigate
replacing the default with your own implementation. replacing the default with your own implementation.
We'll cover a simple extension of custom script engine here. There is also an examples of We'll cover a simple extension of custom script engine here. There is also an example of
a similar engine based on `RestrictedPython <https://restrictedpython.readthedocs.io/en/latest/>`_ a similar engine based on `RestrictedPython <https://restrictedpython.readthedocs.io/en/latest/>`_
included alongside this example. included alongside this example.

View File

@ -31,7 +31,7 @@ We'll include examples of all of these types in this section.
Transactions Transactions
^^^^^^^^^^^^ ^^^^^^^^^^^^
We also need to introduce the concept of a Transaction, bceause certain events We also need to introduce the concept of a Transaction because certain events
can only be used in that context. A Transaction is essentially a subprocess, but can only be used in that context. A Transaction is essentially a subprocess, but
it must fully complete before it affects its outer workflow. it must fully complete before it affects its outer workflow.
@ -147,7 +147,7 @@ this tutorial.
We ask the Employee to verify that they were able to retrieve the product; if they We ask the Employee to verify that they were able to retrieve the product; if they
were unable to do so, then we generate an Error End Event, which we will handle were unable to do so, then we generate an Error End Event, which we will handle
with an Interrupting Error Boundary Event (Error events are *always* Interrupting). with an Interrupting Error Boundary Event (Error events are *always* interrupting).
If the product is unavailable, our Manager will notify the customer, issue a refund, If the product is unavailable, our Manager will notify the customer, issue a refund,
and cancel the order. and cancel the order.
@ -161,7 +161,7 @@ Event, you'll have to use Escalation, because BPMN does not allow Intermediate E
and that Error Events cannot be Non-Interrupting. and that Error Events cannot be Non-Interrupting.
In our example, we'll assume that if we failed to ship the product, we can try again later, In our example, we'll assume that if we failed to ship the product, we can try again later,
so we will not end the Subprocess (Escalation events can be either Interrupting or so, we will not end the Subprocess (Escalation events can be either Interrupting or
Non-Interrupting). Non-Interrupting).
However, we still want to notify our customer of a delay, so we use a Non-Interrupting However, we still want to notify our customer of a delay, so we use a Non-Interrupting

View File

@ -23,7 +23,7 @@ Exclusive Gateway
Exclusive gateways are used when exactly one alternative can be selected. Exclusive gateways are used when exactly one alternative can be selected.
Suppose our products are T-shirts and we offer product C in several colors. After Suppose our products are T-shirts and we offer product C in several colors. After
the user selects a product, we check to see it if is customizable. Our default the user selects a product, we check to see it if is customizable. Our default
branch will be 'Not Customizable', but we'll direct the user to a second form branch will be 'Not Customizable', but we'll direct the user to a second form
if they select 'C'; our condition for choosing this branch is a simple python if they select 'C'; our condition for choosing this branch is a simple python
expression. expression.

View File

@ -28,7 +28,7 @@ selections in a collection.
Selecting more than one product Selecting more than one product
We'll also need to update our element docmentation to display all products. We'll also need to update our element documentation to display all products.
.. figure:: figures/documentation_multi.png .. figure:: figures/documentation_multi.png
:scale: 30% :scale: 30%

View File

@ -17,7 +17,7 @@ instead of the `run.py <https://github.com/sartography/spiff-example-clie/blob/m
Camunda's BPMN editor does not handle data objects in the expected way. You can create data object Camunda's BPMN editor does not handle data objects in the expected way. You can create data object
references, but there is no way to re-use data objects. references, but there is no way to re-use data objects.
It also does not support Message Correlations, and the inteface for generating a message payload doesn't work It also does not support Message Correlations, and the interface for generating a message payload doesn't work
well in a Python environment. well in a Python environment.
We have extended BPMN.js to correct some of these issues. The examples in this section were created using our We have extended BPMN.js to correct some of these issues. The examples in this section were created using our
@ -59,7 +59,7 @@ the 'Enter Payment Info' has been completed.
Configuring Messages Configuring Messages
^^^^^^^^^^^^^^^^^^^^ ^^^^^^^^^^^^^^^^^^^^
Messages are handled slightly differently in Spiff Message Events. On an Message Throw Event or Send Task, Messages are handled slightly differently in Spiff Message Events. On a Message Throw Event or Send Task,
we define a payload, which is simply a bit of python code that will be evaluated against the task data and we define a payload, which is simply a bit of python code that will be evaluated against the task data and
sent along with the message. In the corresponding Message Catch Event or Receive Task, we define a sent along with the message. In the corresponding Message Catch Event or Receive Task, we define a
variable name where we'll store the result. variable name where we'll store the result.

View File

@ -58,7 +58,7 @@ will create specs for all executable processes found in every file supplied. Th
the specified process. Both search recursively for subprocesses; the only difference is the specified process. Both search recursively for subprocesses; the only difference is
the latter method limits the search start to the specified process. the latter method limits the search start to the specified process.
Our examples are pretty simple and we're not loading any extraneous stuff, so we'll Our examples are pretty simple, and we're not loading any extraneous stuff, so we'll
just always load everything. If your entire workflow is contained in your top-level just always load everything. If your entire workflow is contained in your top-level
process, you can omit the :code:`subprocess` argument, but if your workflow contains process, you can omit the :code:`subprocess` argument, but if your workflow contains
call activities, you'll need to use one of these methods to find the models for any call activities, you'll need to use one of these methods to find the models for any
@ -91,7 +91,7 @@ We create a mapping of task type to handler, which we'll pass to our workflow ru
This might not be a step you would need to do in an application you build, since This might not be a step you would need to do in an application you build, since
you would likely have only one set of task specs that need to be parsed, handled, and you would likely have only one set of task specs that need to be parsed, handled, and
serialized; however our `run` method is an awful lot of code to maintain in two separate serialized; however, our `run` method is an awful lot of code to maintain in two separate
files. files.
Running a Workflow Running a Workflow
@ -180,10 +180,10 @@ Examining the Workflow State
---------------------------- ----------------------------
When this application is run and we want to present steps to the user, we'll need When this application is run and we want to present steps to the user, we'll need
to be able to examine the workflow and task states and associated data. We'll cover to be able to examine the workflow and task states and associated data. We'll cover
the basics of this in this section. the basics of this in this section.
The code below is a simple method for displaying information about a task. We use The code below is a simple method for displaying information about a task. We use
this in two ways this in two ways
- presenting a list of tasks to a user (in this case the state will always be ready, so we won't include it) - presenting a list of tasks to a user (in this case the state will always be ready, so we won't include it)
@ -233,7 +233,7 @@ We'll print information about our task as described above, as well as a dump of
We can get a list of all tasks regardless of type or state with :code:`workflow.get_tasks()`. We can get a list of all tasks regardless of type or state with :code:`workflow.get_tasks()`.
The actual list of tasks will get quite long (some tasks are expanded internally by Spiff into The actual list of tasks will get quite long (some tasks are expanded internally by Spiff into
multiple tasks, and all gateways and events are also treated as "tasks"). So we're filtering multiple tasks, and all gateways and events are also treated as "tasks"). So we're filtering
the tasks to only display the ones that would have salience to a user here. the tasks to only display the ones that would have salience to a user here.
We'll further filter those tasks for :code:`READY` and :code:`WAITING` tasks for a more We'll further filter those tasks for :code:`READY` and :code:`WAITING` tasks for a more

View File

@ -171,7 +171,7 @@ Our :code:`select_option` function simply repeats the prompt until the user
enters a value contained in the option list. enters a value contained in the option list.
For other fields, we'll just store whatever the user enters, although in the case For other fields, we'll just store whatever the user enters, although in the case
where they data type was specified to be a :code:`long`, we'll convert it to a where the data type was specified to be a :code:`long`, we'll convert it to a
number. number.
Finally, we need to explicitly store the user-provided response in a variable Finally, we need to explicitly store the user-provided response in a variable
@ -219,4 +219,3 @@ The template string can be obtained from :code:`task.task_spec.documentation`.
As noted above, our template class comes from Jinja. We render the template As noted above, our template class comes from Jinja. We render the template
using the task data, which is just a dictionary. using the task data, which is just a dictionary.

View File

@ -64,7 +64,7 @@ The following example also has one task, represented by the rectangle with curve
The sequence flow is represented with a solid line connector. When the node at The sequence flow is represented with a solid line connector. When the node at
the tail of a sequence flow completes, the node at the arrowhead is enabled to start. the tail of a sequence flow completes, the node at the arrowhead is enabled to start.
A More Complicated Workflow A More Complicated Workflow
@ -78,7 +78,7 @@ A More Complicated Workflow
In this example, the diamond shape is called a gateway. It represents a branch In this example, the diamond shape is called a gateway. It represents a branch
point in our flow. This gateway is an exclusive data-based gateway (also point in our flow. This gateway is an exclusive data-based gateway (also
called an XOR gateway). With an exclusive gateway, you must take one path or called an XOR gateway). With an exclusive gateway, you must take one path or
the other based on some data condition. BPMN has other gateway types. the other based on some data condition. BPMN has other gateway types.
@ -122,4 +122,3 @@ attached to will be cancelled if the event is received) or Non-Interrupting (in
which case the task will continue). In both cases, flows may emanate from the which case the task will continue). In both cases, flows may emanate from the
Boundary Event, which will trigger those paths if the events occur while the task Boundary Event, which will trigger those paths if the events occur while the task
is being executed. is being executed.

View File

@ -4,7 +4,7 @@ Implementing Custom Tasks
Introduction Introduction
------------ ------------
In this second tutorial we are going to implement our own task, and In this second tutorial, we are going to implement our own task, and
use serialization and deserialization to store and restore it. use serialization and deserialization to store and restore it.
If you haven't already, you should complete the first If you haven't already, you should complete the first