def from_dict(doc: Dict, validate: bool = True) -> File: """Get file parameter instance from a dictionary serialization. Parameters ---------- doc: dict Dictionary serialization for file parameter declaration. validate: bool, default=True Validate the serialized object if True. Returns ------- flowserv.model.parameter.files.File Raises ------ flowserv.error.InvalidParameterError """ if validate: util.validate_doc(doc, mandatory=pd.MANDATORY, optional=pd.OPTIONAL + ['target'], exception=err.InvalidParameterError) if doc[pd.TYPE] != PARA_FILE: raise ValueError("invalid type '{}'".format(doc[pd.TYPE])) return File(name=doc[pd.NAME], index=doc[pd.INDEX], label=doc.get(pd.LABEL), help=doc.get(pd.HELP), default=doc.get(pd.DEFAULT), required=doc[pd.REQUIRED], group=doc.get(pd.GROUP), target=doc.get('target'))
def from_dict(cls, doc, validate=True): """Get an instance of the sort column from the dictionary serialization. Raises an error if the given dictionary does not contain the expected elements as generated by the to_dict() method of the class. Parameters ---------- doc: dict Dictionary serialization of a column object validate: bool, default=True Validate the serialization if True. Returns ------- flowserv.model.template.schema.SortColumn Raises ------ flowserv.error.InvalidTemplateError """ # Validate the serialization dictionary if validate: try: util.validate_doc( doc, mandatory=['name'], optional=['sortDesc'] ) except ValueError as ex: raise err.InvalidTemplateError(str(ex)) sort_desc = None if 'sortDesc' in doc: sort_desc = doc['sortDesc'] # Return instance of the column object return cls(column_id=doc['name'], sort_desc=sort_desc)
def from_dict(cls, doc, validate=True): """Create object instance from dictionary serialization. Parameters ---------- doc: dict Dictionary serialization for a workflow output file. validate: bool, default=True Validate the serialization if True. Returns ------- flowserv.model.template.files.WorkflowOutputFile Raises ------ ValueError """ if validate: # Validate the document if the respective flag is True. util.validate_doc( doc=doc, mandatory=['source'], optional=['key', 'title', 'caption', 'widget', 'format']) return cls(source=doc['source'], key=doc.get('key'), title=doc.get('title'), caption=doc.get('caption'), format=doc.get('format'), widget=doc.get('widget'))
def validate_workflow_handle(doc): """Validate serialization of a workflow handle. Here we distinguish between handles that have optional elements (description and instructions) and those that have not. Parameters ---------- doc: dict Workflow handle serialization. Raises ------ ValueError """ # Note: The parameter groups element is optional but it should be contained # in all local test cases. That is the reason why it is in the list of # mandatory elements here. mandatory = ['id', 'name', 'parameters', 'parameterGroups'] util.validate_doc(doc=doc, mandatory=mandatory, optional=[ 'description', 'instructions', 'groups', 'postproc', 'outputs' ]) # Validate the post-processing run handle if present if 'postproc' in doc: postproc = doc['postproc'] validate_run_handle(doc=postproc, state=postproc['state'])
def from_dict(doc: Dict, validate: Optional[bool] = True) -> Actor: """Get an actor parameter instance from a given dictionary serialization. Parameters ---------- doc: dict Dictionary serialization for string parameter delaration. validate: bool, default=True Validate the serialized object if True. Returns ------- flowserv.model.parameter.string.String Raises ------ flowserv.error.InvalidParameterError """ if validate: util.validate_doc(doc, mandatory=pd.MANDATORY, optional=pd.OPTIONAL, exception=err.InvalidParameterError) if doc[pd.TYPE] != PARA_ACTOR: raise ValueError("invalid type '{}'".format(doc[pd.TYPE])) return Actor(name=doc[pd.NAME], index=doc[pd.INDEX], label=doc.get(pd.LABEL), help=doc.get(pd.HELP), default=doc.get(pd.DEFAULT), required=doc[pd.REQUIRED], group=doc.get(pd.GROUP))
def is_fh(value: Any) -> bool: """Check whether an argument value is a serialization of an input file. Expects a dictionary with the following schema: {'type': '$file', 'value': {'fileId': 'string', 'targetPath': 'string'}} The target path is optional. Parameters ---------- value: any User provided argument value. Returns ------- bool """ if not isinstance(value, dict): return False try: util.validate_doc(value, mandatory=['type', 'value']) assert value['type'] == '$file' util.validate_doc( value['value'], mandatory=['fileId'], optional=['targetPath'] ) return True except ValueError: pass return False
def validate_file_handle(doc): """Validate serialization of a file handle. Parameters ---------- doc: dict File handle serialization Raises ------ ValueError """ util.validate_doc(doc=doc, mandatory=['id', 'name', 'createdAt', 'size'])
def validate_run_descriptor(doc): """Validate serialization of run descriptor. Parameters ---------- doc: dict Run handle serialization Raises ------ ValueError """ util.validate_doc(doc=doc, mandatory=['id', 'state', 'createdAt'])
def validate_reset_request(doc): """Validate serialization of a user password reset request. Parameters ---------- doc: dict Reset request response serialization Raises ------ ValueError """ util.validate_doc(doc=doc, mandatory=['requestId'])
def validate_para_module(doc): """Validate serialization of a workflow parameter module handle. Parameters ---------- doc: dict Workflow parameter module handle serialization Raises ------ ValueError """ util.validate_doc(doc=doc, mandatory=['name', 'title', 'index'])
def cast(self, value: Any) -> Dict: """Convert the given value into a record. Returns a dictionary that is a mapping of filed identifier to the converted values returned by the respective parameter declaration. Expects a list of dictionaries containing two elements: 'name' and 'value'. The name identifies the record field and the value is the argument value for that field. Raises an error if the value is not a list, if any of the dictionaries are not well-formed, if required fields are not present in the given list, or if the respective parameter declaration for a record fields raised an exception during cast. Parameters ---------- value: any User-provided value for a template parameter. Returns ------- sting Raises ------ flowserv.error.InvalidArgumentError """ if not isinstance(value, list): raise err.InvalidArgumentError('invalid argument type') result = dict() # Cast all given values using their respective parameter declaration. for obj in value: util.validate_doc(obj, mandatory=['name', 'value'], exception=err.InvalidArgumentError) name = obj['name'] if name not in self.fields: raise err.InvalidArgumentError( "unknown argument '{}'".format(name)) result[name] = self.fields[name].cast(obj['value']) # Add default values for missing fields. for key, para in self.fields.items(): if key not in result: if para.default is not None: result[key] = para.cast(para.default) elif para.required: raise err.InvalidArgumentError( "missing field '{}'".format(key)) return result
def validate_user_listing(doc): """Validate serialization of a user listing. Parameters ---------- doc: dict Serialization for listing of user descriptors Raises ------ ValueError """ util.validate_doc(doc=doc, mandatory=['users']) for user in doc['users']: util.validate_doc(doc=user, mandatory=['id', 'username'])
def validate_parameter(doc): """Validate serialization of a workflow parameter. Parameters ---------- doc: dict Parameter serialization Raises ------ ValueError """ util.validate_doc(doc=doc, mandatory=pd.MANDATORY, optional=pd.OPTIONAL + ['target', 'values', 'range'])
def validate_group_listing(doc): """Validate serialization of a workflow group listing. Parameters ---------- doc: dict Listing of workflow group descriptor serializations Raises ------ ValueError """ util.validate_doc(doc=doc, mandatory=['groups']) for g in doc['groups']: util.validate_doc(doc=g, mandatory=['id', 'name', 'workflow'])
def validate_run_listing(doc): """Validate serialization of a workflow run listing. Parameters ---------- doc: dict Serialization for listing of workflow run descriptors Raises ------ ValueError """ util.validate_doc(doc=doc, mandatory=['runs']) for r in doc['runs']: validate_run_descriptor(doc=r)
def test_validate_doc(): """Test document validation with custom error classes.""" doc = {'name': 'A', 'value': 1} assert util.validate_doc(doc, mandatory=['name', 'value']) assert util.validate_doc(doc, mandatory=['name'], optional=['value']) assert util.validate_doc(doc, mandatory=['name', 'value'], optional=['id']) with pytest.raises(ValueError): util.validate_doc(doc, mandatory=['name']) with pytest.raises(ValueError): util.validate_doc(doc, mandatory=['name', 'value', 'id']) with pytest.raises(err.InvalidArgumentError): util.validate_doc(doc, mandatory=['name'], exception=err.InvalidArgumentError)
def jsonbody(request, mandatory=None, optional=None) -> Dict: """Get Json object from the body of an API request. Validates the object based on the given (optional) lists of mandatory and optional labels. Returns the JSON object (dictionary). Raises an error if an invalid request or body is given. Parameters ---------- request: flask.request HTTP request mandatory: list(string) List of mandatory labels for the dictionary serialization optional: list(string), optional List of optional labels for the dictionary serialization Returns ------- dict Raises ------ robflask.error.InvalidRequest """ try: return validate_doc(request.json, mandatory=mandatory, optional=optional) except (AttributeError, TypeError, ValueError) as ex: raise err.InvalidRequestError(str(ex))
def validate_group_handle(doc): """Validate serialization of a workflow group handle. Parameters ---------- doc: dict Workflow group handle serialization Raises ------ ValueError """ util.validate_doc( doc=doc, mandatory=['id', 'name', 'workflow', 'members', 'parameters', 'files'], optional=['runs', 'config'])
def validate_workflow_listing(doc): """Validate serialization of a workflow descriptor listing. Parameters ---------- doc: dict Serialization for listing of workflow descriptors Raises ------ ValueError """ util.validate_doc(doc=doc, mandatory=['workflows']) for wf in doc['workflows']: util.validate_doc(doc=wf, mandatory=['id', 'name'], optional=['description', 'instructions'])
def from_dict(doc: Dict, validate: Optional[bool] = True) -> Select: """Get select parameter instance from a dictionary serialization. Parameters ---------- doc: dict Dictionary serialization for select parameter declaration. validate: bool, default=True Validate the serialized object if True. Returns ------- flowserv.model.parameter.enum.Select Raises ------ flowserv.error.InvalidParameterError """ if validate: util.validate_doc( doc, mandatory=pd.MANDATORY + ['values'], optional=pd.OPTIONAL, exception=err.InvalidParameterError ) for val in doc['values']: util.validate_doc( val, mandatory=['name', 'value'], optional=['isDefault'], exception=err.InvalidParameterError ) if doc[pd.TYPE] != PARA_SELECT: raise ValueError("invalid type '{}'".format(doc[pd.TYPE])) return Select( name=doc[pd.NAME], index=doc[pd.INDEX], label=doc.get(pd.LABEL), help=doc.get(pd.HELP), default=doc.get(pd.DEFAULT), required=doc[pd.REQUIRED], group=doc.get(pd.GROUP), values=doc['values'] )
def from_dict(doc: Dict, validate: Optional[bool] = True) -> Numeric: """Get numeric parameter instance from a dictionary serialization. Parameters ---------- doc: dict Dictionary serialization for numeric parameter. validate: bool, default=True Validate the serialized object if True. Returns ------- flowserv.model.parameter.numeric.Numeric Raises ------ flowserv.error.InvalidParameterError """ if validate: try: util.validate_doc(doc, mandatory=pd.MANDATORY, optional=pd.OPTIONAL + ['range']) constraint = None if 'range' in doc: constraint = RangeConstraint.from_string(doc['range']) except (ValueError, TypeError) as ex: raise err.InvalidParameterError(str(ex)) try: constraint = None if 'range' in doc: constraint = RangeConstraint.from_string(doc['range']) except (ValueError, TypeError) as ex: raise err.InvalidParameterError(str(ex)) return Numeric(dtype=doc[pd.TYPE], name=doc[pd.NAME], index=doc[pd.INDEX], label=doc.get(pd.LABEL), help=doc.get(pd.HELP), default=doc.get(pd.DEFAULT), required=doc[pd.REQUIRED], group=doc.get(pd.GROUP), constraint=constraint)
def validate_file_listing(doc, count): """Validate serialization of a file listing. The count parameter gives the expected number of files in the listing. Parameters ---------- doc: dict Listing of file handle serializations count: int Expected number of files in the listing Raises ------ ValueError """ util.validate_doc(doc=doc, mandatory=['files']) assert len(doc['files']) == count for fh in doc['files']: validate_file_handle(fh)
def validate_run_handle(doc, state): """Validate serialization of a run handle. Parameters ---------- doc: dict Run handle serialization state: string Expected run state Raises ------ ValueError """ labels = ['id', 'workflowId', 'state', 'createdAt', 'arguments'] if state == st.STATE_RUNNING: labels.append('startedAt') elif state in [st.STATE_ERROR, st.STATE_CANCELED]: labels.append('startedAt') labels.append('finishedAt') labels.append('messages') elif state == st.STATE_SUCCESS: labels.append('startedAt') labels.append('finishedAt') labels.append('files') util.validate_doc( doc=doc, mandatory=labels, optional=['parameters', 'groupId'] ) if 'parameters' in doc: for p in doc['parameters']: validate_parameter(p) assert doc['state'] == state if state == st.STATE_SUCCESS: for r in doc['files']: util.validate_doc( doc=r, mandatory=['id', 'name'], optional=['title', 'caption', 'mimeType', 'widget', 'format'] )
def validate_user_handle(doc, login, inactive=False): """Validate serialization of a user handle. Serialization depends on whether the user is currently logged in or not. Parameters ---------- doc: dict User handle serialization login: bool Flag indicating whether the handle is for a user that is logged in inactive: bool, optional Flag indicating whether the user account has been activated yet Raises ------ ValueError """ mandatory = ['id', 'username'] if login: mandatory.append('token') util.validate_doc(doc=doc, mandatory=mandatory)
def from_dict(cls, doc, validate=True): """Create object instance from dictionary serialization. Parameters ---------- doc: dict Dictionary serialization for parameter group handles validate: bool, default=True Validate the serialization if True. Returns ------- flowserv.model.parameter.base.ParameterGroup Raises ------ ValueError """ if validate: util.validate_doc(doc, mandatory=['name', 'title', 'index']) return cls(name=doc['name'], title=doc['title'], index=doc['index'])
def from_dict(doc: Dict, validate: Optional[bool] = True) -> List: """Get list parameter instance from a dictionary serialization. Parameters ---------- doc: dict Dictionary serialization for a list parameter declaration. validate: bool, default=True Validate the serialized object if True. Returns ------- flowserv.model.parameter.list.List Raises ------ flowserv.error.InvalidParameterError """ if validate: util.validate_doc( doc, mandatory=pd.MANDATORY + ['para'], optional=pd.OPTIONAL, exception=err.InvalidParameterError ) if doc[pd.TYPE] != PARA_LIST: raise ValueError("invalid type '{}'".format(doc[pd.TYPE])) # Deserialize parameter declaration. Import the deserializer here to # avoid cyclic dependencies. from flowserv.model.parameter.factory import ParameterDeserializer as deserializer return Array( name=doc[pd.NAME], para=deserializer.from_dict(doc['para'], validate=validate), index=doc[pd.INDEX], label=doc.get(pd.LABEL), help=doc.get(pd.HELP), default=doc.get(pd.DEFAULT), required=doc[pd.REQUIRED], group=doc.get(pd.GROUP) )
def from_dict(doc: Dict, validate: Optional[bool] = True) -> Record: """Get record parameter instance from a given dictionary serialization. Parameters ---------- doc: dict Dictionary serialization for record parameter declaration. validate: bool, default=True Validate the serialized object if True. Returns ------- flowserv.model.parameter.record.Record Raises ------ flowserv.error.InvalidParameterError """ if validate: util.validate_doc(doc, mandatory=pd.MANDATORY + ['fields'], optional=pd.OPTIONAL, exception=err.InvalidParameterError) if doc[pd.TYPE] != PARA_RECORD: raise ValueError("invalid type '{}'".format(doc[pd.TYPE])) # Deserialize parameter declarations for record fields. Import the # deserializer here to avoid cyclic dependencies. from flowserv.model.parameter.factory import ParameterDeserializer as deserializer fields = list() for obj in doc['fields']: fields.append(deserializer.from_dict(obj, validate=validate)) return Record(name=doc[pd.NAME], fields=fields, index=doc[pd.INDEX], label=doc.get(pd.LABEL), help=doc.get(pd.HELP), default=doc.get(pd.DEFAULT), required=doc[pd.REQUIRED], group=doc.get(pd.GROUP))
def from_dict(cls, doc, validate=True): """Get an instance of the column from the dictionary serialization. Raises an error if the given dictionary does not contain the expected elements as generated by the to_dict() method of the class. Parameters ---------- doc: dict Dictionary serialization of a column object validate: bool, default=True Validate the serialization if True. Returns ------- flowserv.model.template.schema.ResultColumn Raises ------ flowserv.error.InvalidTemplateError """ # Validate the serialization dictionary. if validate: try: util.validate_doc( doc, mandatory=['name', 'label', 'dtype'], optional=['path', 'required'] ) except ValueError as ex: raise err.InvalidTemplateError(str(ex)) # Return instance of the column object return cls( column_id=doc['name'], name=doc['label'], dtype=doc['dtype'], path=doc.get('path'), required=doc.get('required') )
def cancel_run(run_id): """Get handle for a given run. The user has to be a member of the run submission in order to be authorized to access the run. Parameters ---------- run_id: string Unique run identifier Returns ------- flask.response_class Raises ------ flowserv.error.UnauthenticatedAccessError flowserv.error.UnauthorizedAccessError flowserv.error.UnknownWorkflowGroupError """ # Get the access token first to raise an error immediately if no token is # present (to avoid unnecessarily instantiating the service API). token = ACCESS_TOKEN(request) # If the body contains a Json object verify that the object has the # mandatory element 'reason' reason = None if request.json: try: obj = util.validate_doc(request.json, mandatory=['reason']) reason = obj['reason'] except ValueError as ex: raise err.InvalidRequestError(str(ex)) from robflask.service import service with service(access_token=token) as api: # Authentication of the user from the expected api_token in the header # will fail if no token is given or if the user is not logged in. r = api.runs().cancel_run(run_id=run_id, reason=reason) return make_response(jsonify(r), 200)
def validate_ranking(doc): """Validate serialization of a workflow evaluation ranking. Parameters ---------- doc: dict Ranking serialization Raises ------ ValueError """ util.validate_doc(doc=doc, mandatory=['schema', 'ranking'], optional=['postproc', 'outputs']) # Schema columns for col in doc['schema']: util.validate_doc(doc=col, mandatory=['name', 'label', 'dtype']) # Run results for entry in doc['ranking']: util.validate_doc(doc=entry, mandatory=['run', 'group', 'results']) util.validate_doc( doc=entry['run'], mandatory=['id', 'createdAt', 'startedAt', 'finishedAt']) util.validate_doc(doc=entry['group'], mandatory=['id', 'name']) for result in entry['results']: util.validate_doc(doc=result, mandatory=['name', 'value'])