mirror of
https://github.com/sartography/cr-connect-workflow.git
synced 2025-02-20 11:48:16 +00:00
1. Updating Personnel BPMN diagram to debug some issues.
2. Disabling the token timeout for now, to see if this corrects the issues Alex is having with lost work. 3. Raising more thoughtful error messages for unknown lookup options. 4. Providing better validation of default values and injecting the correct value for defaults related to enum lists of all types. 5. Bumping Spiffworkflow library which contains some better error messages and checks.
This commit is contained in:
parent
5e938c4b06
commit
b544334f45
14
Pipfile.lock
generated
14
Pipfile.lock
generated
@ -248,11 +248,11 @@
|
||||
},
|
||||
"flask-cors": {
|
||||
"hashes": [
|
||||
"sha256:72170423eb4612f0847318afff8c247b38bd516b7737adfc10d1c2cdbb382d16",
|
||||
"sha256:f4d97201660e6bbcff2d89d082b5b6d31abee04b1b3003ee073a6fd25ad1d69a"
|
||||
"sha256:6bcfc100288c5d1bcb1dbb854babd59beee622ffd321e444b05f24d6d58466b8",
|
||||
"sha256:cee4480aaee421ed029eaa788f4049e3e26d15b5affb6a880dade6bafad38324"
|
||||
],
|
||||
"index": "pypi",
|
||||
"version": "==3.0.8"
|
||||
"version": "==3.0.9"
|
||||
},
|
||||
"flask-mail": {
|
||||
"hashes": [
|
||||
@ -800,7 +800,7 @@
|
||||
},
|
||||
"spiffworkflow": {
|
||||
"git": "https://github.com/sartography/SpiffWorkflow.git",
|
||||
"ref": "0581d29db6fd150ebb5ac86ba114681e2e078396"
|
||||
"ref": "cfffae7d5bf8aa31fdbceb37595d3131ddccff24"
|
||||
},
|
||||
"sqlalchemy": {
|
||||
"hashes": [
|
||||
@ -971,10 +971,10 @@
|
||||
},
|
||||
"more-itertools": {
|
||||
"hashes": [
|
||||
"sha256:68c70cc7167bdf5c7c9d8f6954a7837089c6a36bf565383919bb595efb8a17e5",
|
||||
"sha256:b78134b2063dd214000685165d81c154522c3ee0a1c0d4d113c80361c234c5a2"
|
||||
"sha256:6f83822ae94818eae2612063a5101a7311e68ae8002005b5e05f03fd74a86a20",
|
||||
"sha256:9b30f12df9393f0d28af9210ff8efe48d10c94f73e5daf886f10c4b0b0b4f03c"
|
||||
],
|
||||
"version": "==8.4.0"
|
||||
"version": "==8.5.0"
|
||||
},
|
||||
"packaging": {
|
||||
"hashes": [
|
||||
|
@ -34,8 +34,8 @@ class UserModel(db.Model):
|
||||
"""
|
||||
hours = float(app.config['TOKEN_AUTH_TTL_HOURS'])
|
||||
payload = {
|
||||
'exp': datetime.datetime.utcnow() + datetime.timedelta(hours=hours, minutes=0, seconds=0),
|
||||
'iat': datetime.datetime.utcnow(),
|
||||
# 'exp': datetime.datetime.utcnow() + datetime.timedelta(hours=hours, minutes=0, seconds=0),
|
||||
# 'iat': datetime.datetime.utcnow(),
|
||||
'sub': self.uid
|
||||
}
|
||||
return jwt.encode(
|
||||
|
@ -213,7 +213,7 @@ Returns information specific to the protocol.
|
||||
"uid": "cah3us",
|
||||
"affiliation": "sponsored",
|
||||
"date_cached": "2020-08-04T19:32:10.075852+00:00"
|
||||
}
|
||||
},
|
||||
},
|
||||
"pi": {
|
||||
"PI": {
|
||||
|
@ -124,10 +124,11 @@ class LookupService(object):
|
||||
lookup_model = LookupFileModel(workflow_spec_id=workflow_model.workflow_spec_id,
|
||||
field_id=field_id,
|
||||
is_ldap=True)
|
||||
|
||||
else:
|
||||
raise ApiError("unknown_lookup_option",
|
||||
raise ApiError.from_task_spec("unknown_lookup_option",
|
||||
"Lookup supports using spreadsheet or LDAP options, "
|
||||
"and neither of those was provided.")
|
||||
"and neither of those was provided.", spec)
|
||||
db.session.add(lookup_model)
|
||||
db.session.commit()
|
||||
return lookup_model
|
||||
|
@ -130,29 +130,32 @@ class WorkflowService(object):
|
||||
# Assure field has valid properties
|
||||
WorkflowService.check_field_properties(field, task)
|
||||
|
||||
# First, assure that all expressions can fire, even if they aren't required fields.
|
||||
if field.has_property(Task.FIELD_PROP_HIDE_EXPRESSION):
|
||||
if WorkflowService.evaluate_property(Task.FIELD_PROP_HIDE_EXPRESSION, field, task):
|
||||
continue # Don't mess about with hidden fields.
|
||||
if field.has_property(Task.FIELD_PROP_REQUIRED_EXPRESSION):
|
||||
result = WorkflowService.evaluate_property(Task.FIELD_PROP_REQUIRED_EXPRESSION, field, task)
|
||||
if not result and required_only:
|
||||
continue # Don't complete fields that are not required.
|
||||
# Process the label of the field if it is dynamic.
|
||||
if field.has_property(Task.FIELD_PROP_LABEL_EXPRESSION):
|
||||
result = WorkflowService.evaluate_property(Task.FIELD_PROP_LABEL_EXPRESSION, field, task)
|
||||
field.label = result
|
||||
if field.has_property(Task.FIELD_PROP_VALUE_EXPRESSION):
|
||||
result = WorkflowService.evaluate_property(Task.FIELD_PROP_VALUE_EXPRESSION, field, task)
|
||||
field.default_value = result
|
||||
form_data[field.id] = field.default_value
|
||||
if required_only and (not field.has_validation(Task.FIELD_CONSTRAINT_REQUIRED) or
|
||||
field.get_validation(Task.FIELD_CONSTRAINT_REQUIRED).lower().strip() != "true"):
|
||||
if field.default_value:
|
||||
form_data[field.id] = field.default_value
|
||||
continue # Don't include any fields that aren't specifically marked as required.
|
||||
elif field.has_property("read_only") and field.get_property(Task.FIELD_PROP_READ_ONLY).lower().strip() == "true":
|
||||
if field.default_value:
|
||||
form_data[field.id] = field.default_value
|
||||
|
||||
# If the field is hidden, it should not produce a value.
|
||||
if field.has_property(Task.FIELD_PROP_HIDE_EXPRESSION):
|
||||
if WorkflowService.evaluate_property(Task.FIELD_PROP_HIDE_EXPRESSION, field, task):
|
||||
continue
|
||||
|
||||
# If there is a default value, set it.
|
||||
if field.default_value:
|
||||
form_data[field.id] = WorkflowService.get_default_value(field, task)
|
||||
|
||||
# If we are only populating required fields, and this isn't required. stop here.
|
||||
if required_only:
|
||||
if (not field.has_validation(Task.FIELD_CONSTRAINT_REQUIRED) or
|
||||
field.get_validation(Task.FIELD_CONSTRAINT_REQUIRED).lower().strip() != "true"):
|
||||
continue # Don't include any fields that aren't specifically marked as required.
|
||||
if field.has_property(Task.FIELD_PROP_REQUIRED_EXPRESSION):
|
||||
result = WorkflowService.evaluate_property(Task.FIELD_PROP_REQUIRED_EXPRESSION, field, task)
|
||||
if not result and required_only:
|
||||
continue # Don't complete fields that are not required.
|
||||
|
||||
# If it is read only, stop here.
|
||||
if field.has_property("read_only") and field.get_property(Task.FIELD_PROP_READ_ONLY).lower().strip() == "true":
|
||||
continue # Don't mess about with read only fields.
|
||||
|
||||
if field.has_property(Task.FIELD_PROP_REPEAT):
|
||||
@ -192,15 +195,63 @@ class WorkflowService(object):
|
||||
message = f"The field {field.id} contains an invalid expression. {e}"
|
||||
raise ApiError.from_task(f'invalid_{property_name}', message, task=task)
|
||||
|
||||
|
||||
@staticmethod
|
||||
def has_lookup(field):
|
||||
"""Returns true if this is a lookup field."""
|
||||
"""Note, this does not include enums based on task data, that
|
||||
is populated when the form is created, not as a lookup from a data table. """
|
||||
has_ldap_lookup = field.has_property(Task.FIELD_PROP_LDAP_LOOKUP)
|
||||
has_file_lookup = field.has_property(Task.FIELD_PROP_SPREADSHEET_NAME)
|
||||
return has_ldap_lookup or has_file_lookup
|
||||
|
||||
|
||||
@staticmethod
|
||||
def get_default_value(field, task):
|
||||
has_lookup = WorkflowService.has_lookup(field)
|
||||
|
||||
default = field.default_value
|
||||
# If there is a value expression, use that rather than the default value.
|
||||
if field.has_property(Task.FIELD_PROP_VALUE_EXPRESSION):
|
||||
result = WorkflowService.evaluate_property(Task.FIELD_PROP_VALUE_EXPRESSION, field, task)
|
||||
default = result
|
||||
|
||||
# If no default exists, return None
|
||||
if not default: return None
|
||||
|
||||
if field.type == "enum" and not has_lookup:
|
||||
default_option = next((obj for obj in field.options if obj.id == default), None)
|
||||
if not default_option:
|
||||
raise ApiError.from_task("invalid_default", "You specified a default value that does not exist in "
|
||||
"the enum options ", task)
|
||||
return {'value': default_option.id, 'label': default_option.name}
|
||||
elif field.type == "autocomplete" or field.type == "enum":
|
||||
lookup_model = LookupService.get_lookup_model(task, field)
|
||||
if field.has_property(Task.FIELD_PROP_LDAP_LOOKUP): # All ldap records get the same person.
|
||||
return None # There is no default value for ldap.
|
||||
elif lookup_model:
|
||||
data = db.session.query(LookupDataModel).\
|
||||
filter(LookupDataModel.lookup_file_model == lookup_model). \
|
||||
filter(LookupDataModel.value == default).\
|
||||
first()
|
||||
if not data:
|
||||
raise ApiError.from_task("invalid_default", "You specified a default value that does not exist in "
|
||||
"the enum options ", task)
|
||||
return {"value": data.value, "label": data.label, "data": data.data}
|
||||
else:
|
||||
raise ApiError.from_task("unknown_lookup_option", "The settings for this auto complete field "
|
||||
"are incorrect: %s " % field.id, task)
|
||||
elif field.type == "long":
|
||||
return int(default)
|
||||
elif field.type == 'boolean':
|
||||
return bool(default)
|
||||
else:
|
||||
return default
|
||||
|
||||
@staticmethod
|
||||
def get_random_data_for_field(field, task):
|
||||
has_ldap_lookup = field.has_property(Task.FIELD_PROP_LDAP_LOOKUP)
|
||||
has_file_lookup = field.has_property(Task.FIELD_PROP_SPREADSHEET_NAME)
|
||||
has_data_lookup = field.has_property(Task.FIELD_PROP_DATA_NAME)
|
||||
has_lookup = has_ldap_lookup or has_file_lookup or has_data_lookup
|
||||
"""Randomly populates the field, mainly concerned with getting enums correct, as
|
||||
the rest are pretty easy."""
|
||||
has_lookup = WorkflowService.has_lookup(field)
|
||||
|
||||
if field.type == "enum" and not has_lookup:
|
||||
# If it's a normal enum field with no lookup,
|
||||
@ -208,17 +259,10 @@ class WorkflowService(object):
|
||||
if len(field.options) > 0:
|
||||
random_choice = random.choice(field.options)
|
||||
if isinstance(random_choice, dict):
|
||||
return {
|
||||
'value': random_choice['id'],
|
||||
'label': random_choice['name']
|
||||
}
|
||||
return {'value': random_choice['id'], 'label': random_choice['name']}
|
||||
else:
|
||||
# fixme: why it is sometimes an EnumFormFieldOption, and other times not?
|
||||
# Assume it is an EnumFormFieldOption
|
||||
return {
|
||||
'value': random_choice.id,
|
||||
'label': random_choice.name
|
||||
}
|
||||
return {'value': random_choice.id, 'label': random_choice.name}
|
||||
else:
|
||||
raise ApiError.from_task("invalid_enum", "You specified an enumeration field (%s),"
|
||||
" with no options" % field.id, task)
|
||||
@ -226,19 +270,8 @@ class WorkflowService(object):
|
||||
# If it has a lookup, get the lookup model from the spreadsheet or task data, then return a random option
|
||||
# from the lookup model
|
||||
lookup_model = LookupService.get_lookup_model(task, field)
|
||||
if has_ldap_lookup: # All ldap records get the same person.
|
||||
return {
|
||||
"label": "dhf8r",
|
||||
"value": "Dan Funk",
|
||||
"data": {
|
||||
"uid": "dhf8r",
|
||||
"display_name": "Dan Funk",
|
||||
"given_name": "Dan",
|
||||
"email_address": "dhf8r@virginia.edu",
|
||||
"department": "Depertment of Psychocosmographictology",
|
||||
"affiliation": "Rousabout",
|
||||
"sponsor_type": "Staff"}
|
||||
}
|
||||
if field.has_property(Task.FIELD_PROP_LDAP_LOOKUP): # All ldap records get the same person.
|
||||
return WorkflowService._random_ldap_record()
|
||||
elif lookup_model:
|
||||
data = db.session.query(LookupDataModel).filter(
|
||||
LookupDataModel.lookup_file_model == lookup_model).limit(10).all()
|
||||
@ -260,8 +293,21 @@ class WorkflowService(object):
|
||||
else:
|
||||
return WorkflowService._random_string()
|
||||
|
||||
def __get_options(self):
|
||||
pass
|
||||
@staticmethod
|
||||
def _random_ldap_record():
|
||||
return {
|
||||
"label": "dhf8r",
|
||||
"value": "Dan Funk",
|
||||
"data": {
|
||||
"uid": "dhf8r",
|
||||
"display_name": "Dan Funk",
|
||||
"given_name": "Dan",
|
||||
"email_address": "dhf8r@virginia.edu",
|
||||
"department": "Department of Psychocosmographictology",
|
||||
"affiliation": "Roustabout",
|
||||
"sponsor_type": "Staff"}
|
||||
}
|
||||
|
||||
|
||||
@staticmethod
|
||||
def _random_string(string_length=10):
|
||||
|
Binary file not shown.
File diff suppressed because it is too large
Load Diff
@ -18,6 +18,11 @@
|
||||
</camunda:validation>
|
||||
</camunda:formField>
|
||||
<camunda:formField id="string_not_required" type="string" />
|
||||
<camunda:formField id="enum_with_default" label="My Enum" type="enum" defaultValue="maybe">
|
||||
<camunda:value id="yes" name="Yes" />
|
||||
<camunda:value id="no" name="No" />
|
||||
<camunda:value id="maybe" name="Maybe so, I don't know." />
|
||||
</camunda:formField>
|
||||
</camunda:formData>
|
||||
</bpmn:extensionElements>
|
||||
<bpmn:incoming>SequenceFlow_0lvudp8</bpmn:incoming>
|
||||
|
@ -125,3 +125,12 @@ class TestWorkflowSpecValidation(BaseTest):
|
||||
self.assertIsNotNone(final_data)
|
||||
self.assertIn('string_required', final_data)
|
||||
self.assertNotIn('string_not_required', final_data)
|
||||
|
||||
def test_enum_defaults_correctly_populated(self):
|
||||
self.load_example_data()
|
||||
spec_model = self.load_test_spec('required_fields')
|
||||
final_data = WorkflowService.test_spec(spec_model.id, required_only=True)
|
||||
self.assertIsNotNone(final_data)
|
||||
self.assertIn('enum_with_default', final_data)
|
||||
self.assertEquals('maybe', final_data['enum_with_default']['value'])
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user