Expose a flag on the workflow model in the api to shown if it is using the latest spec. Added a soft_reset and hard_reset onto the workflow endpoint that will allow you to cause a hard or soft reset.
This commit is contained in:
parent
7b21b78987
commit
906bacff6a
13
crc/api.yml
13
crc/api.yml
|
@ -533,6 +533,19 @@ paths:
|
|||
get:
|
||||
operationId: crc.api.workflow.get_workflow
|
||||
summary: Detailed information for a specific workflow instance
|
||||
parameters:
|
||||
- name: soft_reset
|
||||
in: query
|
||||
required: false
|
||||
description: Set this to true to use the latest workflow specification to load minor modifications to the spec.
|
||||
schema:
|
||||
type: boolean
|
||||
- name: hard_reset
|
||||
in: query
|
||||
required: false
|
||||
description: Set this to true to reset the workflow
|
||||
schema:
|
||||
type: boolean
|
||||
tags:
|
||||
- Workflows and Tasks
|
||||
responses:
|
||||
|
|
|
@ -75,17 +75,18 @@ def __get_workflow_api_model(processor: WorkflowProcessor):
|
|||
next_task=None,
|
||||
user_tasks=user_tasks,
|
||||
workflow_spec_id=processor.workflow_spec_id,
|
||||
spec_version=processor.get_spec_version()
|
||||
spec_version=processor.get_spec_version(),
|
||||
is_latest_spec=processor.get_spec_version() == processor.get_latest_version_string(processor.workflow_spec_id)
|
||||
)
|
||||
if processor.next_task():
|
||||
workflow_api.next_task = Task.from_spiff(processor.next_task())
|
||||
return workflow_api
|
||||
|
||||
|
||||
def get_workflow(workflow_id):
|
||||
def get_workflow(workflow_id, soft_reset=False, hard_reset=False):
|
||||
schema = WorkflowApiSchema()
|
||||
workflow_model = session.query(WorkflowModel).filter_by(id=workflow_id).first()
|
||||
processor = WorkflowProcessor(workflow_model)
|
||||
processor = WorkflowProcessor(workflow_model, soft_reset=soft_reset, hard_reset=hard_reset)
|
||||
return schema.dump(__get_workflow_api_model(processor))
|
||||
|
||||
|
||||
|
|
|
@ -95,7 +95,7 @@ class TaskSchema(ma.Schema):
|
|||
|
||||
|
||||
class WorkflowApi(object):
|
||||
def __init__(self, id, status, user_tasks, last_task, next_task, workflow_spec_id, spec_version):
|
||||
def __init__(self, id, status, user_tasks, last_task, next_task, workflow_spec_id, spec_version, is_latest_spec):
|
||||
self.id = id
|
||||
self.status = status
|
||||
self.user_tasks = user_tasks
|
||||
|
@ -103,11 +103,15 @@ class WorkflowApi(object):
|
|||
self.next_task = next_task
|
||||
self.workflow_spec_id = workflow_spec_id
|
||||
self.spec_version = spec_version
|
||||
self.is_latest_spec = is_latest_spec
|
||||
|
||||
class WorkflowApiSchema(ma.Schema):
|
||||
class Meta:
|
||||
model = WorkflowApi
|
||||
fields = ["id", "status", "user_tasks", "last_task", "next_task", "workflow_spec_id", "spec_version"]
|
||||
fields = ["id", "status",
|
||||
"user_tasks", "last_task", "next_task",
|
||||
"workflow_spec_id",
|
||||
"spec_version", "is_latest_spec"]
|
||||
unknown = INCLUDE
|
||||
|
||||
status = EnumField(WorkflowStatus)
|
||||
|
|
|
@ -92,7 +92,7 @@ class WorkflowProcessor(object):
|
|||
STUDY_ID_KEY = "study_id"
|
||||
|
||||
def __init__(self, workflow_model: WorkflowModel, soft_reset=False, hard_reset=False):
|
||||
"""Create a Worflow Processor based on the serialized information available in the workflow model.
|
||||
"""Create a Workflow Processor based on the serialized information available in the workflow model.
|
||||
If soft_reset is set to true, it will try to use the latest version of the workflow specification.
|
||||
If hard_reset is set to true, it will create a new Workflow, but embed the data from the last
|
||||
completed task in the previous workflow.
|
||||
|
@ -118,14 +118,36 @@ class WorkflowProcessor(object):
|
|||
return parser
|
||||
|
||||
@staticmethod
|
||||
def __get_file_models_for_version(workflow_spec_id, version):
|
||||
"""Version is in the format v[VERSION] [FILE_ID_LIST]
|
||||
def get_latest_version_string(workflow_spec_id):
|
||||
"""Version is in the format v[VERSION] (FILE_ID_LIST)
|
||||
For example, a single bpmn file with only one version would be
|
||||
v1 [12] Where 12 is the id of the file that is used to create the
|
||||
v1 (12) Where 12 is the id of the file data model that is used to create the
|
||||
specification. If multiple files exist, they are added on in
|
||||
dot notation to both the version number and the file list. So
|
||||
a Spec that includes a BPMN, DMN, an a Word file all on the first
|
||||
version would be v1.1.1 [12,45,21]"""
|
||||
version would be v1.1.1 (12.45.21)"""
|
||||
|
||||
# this could potentially become expensive to load all the data in the data models.
|
||||
# in which case we might consider using a deferred loader for the actual data, but
|
||||
# trying not to pre-optimize.
|
||||
file_data_models = WorkflowProcessor.__get_latest_file_models(workflow_spec_id)
|
||||
major_version = 0 # The version of the primary file.
|
||||
minor_version = [] # The versions of the minor files if any.
|
||||
file_ids = []
|
||||
for file_data in file_data_models:
|
||||
file_ids.append(file_data.id)
|
||||
if file_data.file_model.primary:
|
||||
major_version = file_data.version
|
||||
else:
|
||||
minor_version.append(file_data.version)
|
||||
minor_version.insert(0, major_version) # Add major version to beginning.
|
||||
version = ".".join(str(x) for x in minor_version)
|
||||
files = ".".join(str(x) for x in file_ids)
|
||||
full_version = "v%s (%s)" % (version, files)
|
||||
return full_version
|
||||
|
||||
@staticmethod
|
||||
def __get_file_models_for_version(workflow_spec_id, version):
|
||||
file_id_strings = re.findall('\((.*)\)', version)[0].split(".")
|
||||
file_ids = [int(i) for i in file_id_strings]
|
||||
files = session.query(FileDataModel)\
|
||||
|
@ -148,21 +170,19 @@ class WorkflowProcessor(object):
|
|||
.order_by(FileModel.id)\
|
||||
.all()
|
||||
|
||||
|
||||
@staticmethod
|
||||
def get_spec(workflow_spec_id, version=None):
|
||||
"""Returns the requested version of the specification,
|
||||
or the lastest version if none is specified."""
|
||||
parser = WorkflowProcessor.get_parser()
|
||||
major_version = 0 # The version of the primary file.
|
||||
minor_version = [] # The versions of the minor files if any.
|
||||
file_ids = []
|
||||
process_id = None
|
||||
if version is None:
|
||||
file_data_models = WorkflowProcessor.__get_latest_file_models(workflow_spec_id)
|
||||
version = WorkflowProcessor.get_latest_version_string(workflow_spec_id)
|
||||
else:
|
||||
file_data_models = WorkflowProcessor.__get_file_models_for_version(workflow_spec_id, version)
|
||||
for file_data in file_data_models:
|
||||
file_ids.append(file_data.id)
|
||||
if file_data.file_model.type == FileType.bpmn:
|
||||
bpmn: ElementTree.Element = ElementTree.fromstring(file_data.data)
|
||||
if file_data.file_model.primary:
|
||||
|
@ -171,17 +191,10 @@ class WorkflowProcessor(object):
|
|||
elif file_data.file_model.type == FileType.dmn:
|
||||
dmn: ElementTree.Element = ElementTree.fromstring(file_data.data)
|
||||
parser.add_dmn_xml(dmn, filename=file_data.file_model.name)
|
||||
if file_data.file_model.primary:
|
||||
major_version = file_data.version
|
||||
else:
|
||||
minor_version.append(file_data.version)
|
||||
if process_id is None:
|
||||
raise(Exception("There is no primary BPMN model defined for workflow %s" % workflow_spec_id))
|
||||
minor_version.insert(0, major_version) # Add major version to beginning.
|
||||
spec = parser.get_spec(process_id)
|
||||
version = ".".join(str(x) for x in minor_version)
|
||||
files = ".".join(str(x) for x in file_ids)
|
||||
spec.description = "v%s (%s)" % (version, files)
|
||||
spec.description = version
|
||||
return spec
|
||||
|
||||
@staticmethod
|
||||
|
|
|
@ -25,8 +25,10 @@ class TestTasksApi(BaseTest):
|
|||
workflow = session.query(WorkflowModel).filter_by(study_id=study.id, workflow_spec_id=workflow_name).first()
|
||||
return workflow
|
||||
|
||||
def get_workflow_api(self, workflow):
|
||||
rv = self.app.get('/v1.0/workflow/%i' % workflow.id, content_type="application/json")
|
||||
def get_workflow_api(self, workflow, soft_reset=False, hard_reset=False):
|
||||
rv = self.app.get('/v1.0/workflow/%i?soft_reset=%s&hard_reset=%s' %
|
||||
(workflow.id, str(soft_reset), str(hard_reset)),
|
||||
content_type="application/json")
|
||||
json_data = json.loads(rv.get_data(as_text=True))
|
||||
workflow_api = WorkflowApiSchema().load(json_data)
|
||||
self.assertEqual(workflow.workflow_spec_id, workflow_api.workflow_spec_id)
|
||||
|
@ -210,6 +212,7 @@ class TestTasksApi(BaseTest):
|
|||
workflow = self.create_workflow('two_forms')
|
||||
workflow_api = self.get_workflow_api(workflow)
|
||||
self.complete_form(workflow, workflow_api.user_tasks[0], {"color": "blue"})
|
||||
self.assertTrue(workflow_api.is_latest_spec)
|
||||
|
||||
# Modify the specification, with a major change that alters the flow and can't be deserialized
|
||||
# effectively, if it uses the latest spec files.
|
||||
|
@ -218,4 +221,8 @@ class TestTasksApi(BaseTest):
|
|||
|
||||
workflow_api = self.get_workflow_api(workflow)
|
||||
self.assertTrue(workflow_api.spec_version.startswith("v1 "))
|
||||
self.assertTrue(workflow_api.latest_spec_version)
|
||||
self.assertFalse(workflow_api.is_latest_spec)
|
||||
|
||||
workflow_api = self.get_workflow_api(workflow, hard_reset=True)
|
||||
self.assertTrue(workflow_api.spec_version.startswith("v2 "))
|
||||
self.assertTrue(workflow_api.is_latest_spec)
|
||||
|
|
|
@ -319,4 +319,7 @@ class TestWorkflowProcessor(BaseTest):
|
|||
self.assertEquals("New Step", processor3.next_task().task_spec.description)
|
||||
self.assertEquals({"color": "blue"}, processor3.next_task().data)
|
||||
|
||||
|
||||
def test_get_latest_spec_version(self):
|
||||
workflow_spec_model = self.load_test_spec("two_forms")
|
||||
version = WorkflowProcessor.get_latest_version_string("two_forms")
|
||||
self.assertTrue(version.startswith("v1 "))
|
||||
|
|
Loading…
Reference in New Issue