Microservices automation framework - the "main" function to drive external APIs.
Automata allows a Pharo developer to write automation scripts for external microservices (or APIs like twitter, google, or any) in Pharo. The project main idea is inspired by Seaside
WATask class which allows coding of a complex communication in workflow based style.
The concept is some kind of a state machine which drives external services via (REST) API and changes it's state based on events recieved back. The state machine is represented by a Smalltalk code and "waitForEvent:" method which saves current execution state and resumes it upon a recieving of an event.
Automata state machine can be hooked to any event system by inheriting
AMTaskManager and implementing
As an example implementaion see
AMManualTaskManager class which implements a FIFO queue for pending tasks and manual event triggering.
Metacello new baseline: #Automata; repository: 'github://mikefilonov/automata'; load.
You may find an full-fledged example for external service automation here: https://github.com/mikefilonov/automata-example
The following is an example of Automata only and uses
AMManualTaskManager implementation to run tasks. Note that in scope of this example by term "event" we mean an explicit call to "doNext" method.
automata := AMManualTaskManager new. automata do: [ :task | Transcript show: 'Hello'. task wait: nil. Transcript show: 'World!'. ]. automata do: [ :task | Transcript show: 'Goodbye'. task wait: nil. Transcript show: 'Cruel'. task wait: nil. Transcript show: 'World :('. task wait: nil. ]. automata start. automata doNext. automata doNext. automata doNext. automata doNext. automata stop.
A task manager (AMTaskManager subclasses) is used to track execution of many tasks. In order to start a new task
doTask: method is used. The method accepts one argument a AMTask subclass instance to run.
automata doTask: ProcessNewMailTask new. "a class I made up to show the point of running subclasses of AMTask"
do: method is a shorthand for adding a
AMDeligateTask instance by providing a one-argument code block.
automata do: [ :task | task waitForEvent: [ :e | e selector = #ToDoItemAdded and: [ e list = 'planned' ] ]. ]
The code is equavalent to the following:
automata doTask: (AMDeligateTask new taskBlock: [ :task | task waitForEvent: [ :e | e selector = #ToDoItemAdded and: [ e list = 'planned' ] ]. ]; yourself)
Automata task saves it's state using
wait: method called in a Task code. This method accepts 1 argument (condition) which is not restricted to any class by the framework but defined by a concrete imoplementation of an event system. The argument purpose is to define a resume condition of the stop point. The intended way to use it is one-argument block which accepts an event and returns
false depending on if the event matches the expected description.
waitForEvent: is an helper method which assumes that condition is to be a block with one argument (suposedly an event) and returns the argument for which the block answered
event := task waitForEvent: [:event | event selector = #FileUploaded]. Transcript show: (event at: #filename).
AMTaskManager callback protocol
To implement an new automata manager for an event system create a new AMTaskManager subclass and re-implement
addPendingTask: aTask withState: cc withCondition: condition
aTaskis a AMTask instance which represents the code flow and can be used to store some flow-specific properties like "task name".
ccis a Continuation which should be used to resume the task.
conditionis a condition to resume the flow. Defined by the concrete implementation.
This method is called whenever a task reaches
wait: method. In a response a task manager is supposed to save the task in a "pending tasks" collection and resume it when event matching
condition is recieved.
The framework does not provide a default implenentation to store and resume tasks though. This means the task manager implementation must have some kind of a queue for tasks and event processor which runs through conditions and put matched
cc to execution loop queue.
Resuming a pending task
In order to resume a saved task it's latest
cc should be put to an execution loop queue:
automata executionLoop enqueue: cc
By default Automata implements single-threaded execution loop in which runable tasks are queued and exected in FIFO order.
For more implementation details please refer to RESTAnnouncer project at https://github.com/mikefilonov/restannouncer
Quick start example: https://github.com/mikefilonov/automata-example