diff --git a/crc/models/api_models.py b/crc/models/api_models.py index c430879a..b964c519 100644 --- a/crc/models/api_models.py +++ b/crc/models/api_models.py @@ -27,8 +27,19 @@ class Task(object): # Field Types - FIELD_TYPE_FILE = "file" + FIELD_TYPE_STRING = "string" + FIELD_TYPE_LONG = "long" + FIELD_TYPE_BOOLEAN = "boolean" + FIELD_TYPE_DATE = "date" + FIELD_TYPE_ENUM = "enum" + FIELD_TYPE_TEXTAREA = "textarea" # textarea: Multiple lines of text FIELD_TYPE_AUTO_COMPLETE = "autocomplete" + FIELD_TYPE_FILE = "file" + FIELD_TYPE_FILES = "files" # files: Multiple files + FIELD_TYPE_TEL = "tel" # tel: Phone number + FIELD_TYPE_EMAIL = "email" # email: Email address + FIELD_TYPE_URL = "url" # url: Website address + FIELD_PROP_AUTO_COMPLETE_MAX = "autocomplete_num" # Not used directly, passed in from the front end. # Required field @@ -77,8 +88,6 @@ class Task(object): FIELD_PROP_HELP = "help" - - ########################################################################## def __init__(self, id, name, title, type, state, lane, form, documentation, data, @@ -103,6 +112,11 @@ class Task(object): def valid_property_names(cls): return [value for name, value in vars(cls).items() if name.startswith('FIELD_PROP')] + @classmethod + def valid_field_types(cls): + return [value for name, value in vars(cls).items() if name.startswith('FIELD_TYPE')] + + class OptionSchema(ma.Schema): class Meta: fields = ["id", "name", "data"] diff --git a/crc/services/workflow_service.py b/crc/services/workflow_service.py index 60a0630e..9bb89c05 100755 --- a/crc/services/workflow_service.py +++ b/crc/services/workflow_service.py @@ -210,6 +210,7 @@ class WorkflowService(object): task_name = task.get_name()) # Assure field has valid properties WorkflowService.check_field_properties(field, task) + WorkflowService.check_field_type(field, task) # Process the label of the field if it is dynamic. if field.has_property(Task.FIELD_PROP_LABEL_EXPRESSION): @@ -301,6 +302,14 @@ class WorkflowService(object): f'The field {field.id} contains an unsupported ' f'property: {name}', task=task) + @staticmethod + def check_field_type(field, task): + """Assures that the field type is valid.""" + valid_types = Task.valid_field_types() + if field.type not in valid_types: + raise ApiError.from_task("invalid_field_type", + f'The field {field.id} has an unknown field type ' + f'{field.type}, valid types include {valid_types}', task=task) @staticmethod def post_process_form(task): diff --git a/tests/data/invalid_custom_field/invalid_custom_field.bpmn b/tests/data/invalid_custom_field/invalid_custom_field.bpmn new file mode 100644 index 00000000..6c13a74b --- /dev/null +++ b/tests/data/invalid_custom_field/invalid_custom_field.bpmn @@ -0,0 +1,43 @@ + + + + + SequenceFlow_12ulmn8 + + + SequenceFlow_06786ls + + + + + + + + SequenceFlow_12ulmn8 + SequenceFlow_06786ls + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/tests/workflow/test_workflow_spec_validation_api.py b/tests/workflow/test_workflow_spec_validation_api.py index 2aa16e99..c0e8937f 100644 --- a/tests/workflow/test_workflow_spec_validation_api.py +++ b/tests/workflow/test_workflow_spec_validation_api.py @@ -148,6 +148,12 @@ class TestWorkflowSpecValidation(BaseTest): self.assertIn('enum_with_default', final_data) self.assertEqual('maybe', final_data['enum_with_default']['value']) + def test_invalid_custom_field(self): + self.load_example_data() + errors = self.validate_workflow("invalid_custom_field") + self.assertEqual(1, len(errors)) + self.assertEqual("invalid_field_type", errors[0]['code']) + @patch('crc.services.study_service.StudyService._get_study_status') def test_disabled_spec_validation(self, mock_status): """A disabled workflow spec should fail validation"""