Skip to content
Snippets Groups Projects

WS-543: Addressed issues found in calibration ingestion testing

Merged Janet Goldstein requested to merge WS-543-fix-manifest-issues-2021-07-22 into main
1 file
+ 42
16
Compare changes
  • Side-by-side
  • Inline
@@ -6,21 +6,35 @@ reacts to certain events by triggering actions and going into another state.
"""
import abc
import json
from typing import Optional
class State(abc.ABC):
"""
A state that a machine could reside in.
A state has a suite of transitions to other states. When an event comes in, we match against it; if we find a
matching pattern, we perform that transition to another state.
"""
@abc.abstractmethod
def matches(self, other: "State") -> bool:
"""
This is most likely implemented by doing a string-equality test.
:param other: the other state to compare to
:return: true if we and the other state match
"""
pass
def __init__(self, transitions: list["TransitionIF"]):
# We have a bit of a chicken-and-egg problem here, in that the State needs Transitions to be initialized but
# the Transition needs States to be initialized. Going from prototype to production here will mean breaking
# this cycle, possibly by introducing a builder of some kind, but for now we can just pretend that they are
# built successfully somehow.
self.transitions = transitions
def on_event(self, event: dict) -> Optional["State"]:
# Locate the first matching transition
matching_transition = None
for transition in self.transitions:
if transition.matches(event):
matching_transition = transition
break
# take this transition
if matching_transition is not None:
return matching_transition.take()
class Action(abc.ABC):
@@ -34,6 +48,7 @@ class Action(abc.ABC):
- StartWorkflow(workflow_name, additional_args) that starts a workflow with the
provided name, the event and additional arguments
"""
@abc.abstractmethod
def execute(self):
pass
@@ -56,20 +71,21 @@ class TransitionIF(abc.ABC):
"""
A transition between states
"""
def __init__(self, from_state: State, to_state: State, pattern: Pattern, action: Action):
self.from_state, self.to_state = from_state, to_state
self.pattern = pattern
self.action = action
@abc.abstractmethod
def matches(self, state: State, event: dict) -> bool:
def matches(self, event: dict) -> bool:
"""
True if this transition is applicable in the supplied state and matches the supplied event.
:param state: state to check against
:param event: event to match against
:return: true if everything matches
"""
return self.from_state.matches(state) and self.pattern.matches(event)
return self.pattern.matches(event)
@abc.abstractmethod
def take(self) -> State:
@@ -88,6 +104,7 @@ class MealyMachine:
I am a state machine for a given capability. I am responsible for handling events
and transitioning to other states.
"""
def __init__(self):
self.transitions = []
self.current_state: State = None
@@ -122,6 +139,7 @@ class CapabilityInfoForMachines:
This is a demonstration of the sort of query I expect we'll use to locate executions
that are active and need to be acted on in response to an event of some kind.
"""
def find_requests_matching_transition(self, event: dict) -> list["CapabilityExecution"]:
"""
The concept here is to let the database do the heavy lifting and actually tell us
@@ -131,7 +149,8 @@ class CapabilityInfoForMachines:
:param event: the event to check
:return: a list of matching capability executions
"""
return self.session.query("""
return self.session.query(
"""
SELECT *
FROM transitions t
JOIN machines m ON t.machine_id = m.id
@@ -139,30 +158,36 @@ class CapabilityInfoForMachines:
JOIN capability_requests cr on cr.capability_name = c.name
JOIN capability_executions ce on cr.capability_request_id = ce.capability_request_id
WHERE %(event)s @? t.pattern AND ce.state = t.from_state
""", {"event": json.dumps(event)})
""",
{"event": json.dumps(event)},
)
def build_tables(self):
"""
This is just a demonstration method to hold some SQL to demo the tables I have
in mind for this system.
"""
self.session.execute("""
self.session.execute(
"""
CREATE TABLE machines(id serial primary key);
CREATE TABLE actions(id serial primary key, action_type varchar, action_arguments json);
CREATE TABLE transitions (
id serial primary key,
machine_id integer references(machines),
machine_id integer references machines(id),
from_state varchar,
to_state varchar,
pattern jsonpath,
action_id integer references(actions)
action_id integer references actions(id)
);
""")
"""
)
class CapabilityExecution:
machine: MealyMachine = None
def process(self, event):
self.machine.on_event(event)
@@ -173,6 +198,7 @@ class CapabilityServiceMachineMananger:
machines alive during the execution of the program. The idea here is to be more
efficient and more event-driven.
"""
def __init__(self):
self.info = CapabilityInfoForMachines()
Loading