def update_group(self, group_id, name=None, members=None): """Update the name and/or list of members for a workflow group. Parameters ---------- group_id: string Unique group identifier name: string, optional Unique user identifier members: list(string), optional List of user identifier for group members Returns ------- flowserv.model.base.GroupObject Raises ------ flowserv.error.ConstraintViolationError flowserv.error.UnknownUserError flowserv.error.UnknownWorkflowGroupError """ # Get group handle. This will raise an error if the group is unknown. group = self.get_group(group_id) # If name and members are None we simply return the group handle. if name is None and members is None: return group if name is not None and name is not group.name: constraint.validate_name(name) group.name = name if members is not None: group.members = list() for user_id in members: group.members.append(self.users.get_user(user_id, active=True)) return group
def unique_name(name, existing_names): """Ensure that the workflow name in the project metadata is not empty, not longer than 512 character, and unique. Parameters ---------- name: string Workflow name in manifest or given by user. existing_names: set Set of names for existing projects. Raises ------ flowserv.error.ConstraintViolationError """ # Validate that the name is not empty and not too long. constraint.validate_name(name) # Ensure that the name is unique. if name in existing_names: # Find a a unique name that matches the template name (int) name_templ = name + ' ({})' count = 1 while name_templ.format(count) in existing_names: count += 1 name = name_templ.format(count) # Re-validate that the name is not too long. constraint.validate_name(name) return name
def upload_file(self, group_id: str, file: IOHandle, name: str): """Upload a new file for a workflow group. This will create a copy of the given file in the file store that is associated with the group. The file will be places in a unique folder inside the groups upload folder. Raises an error if the given file name is invalid. Parameters ---------- group_id: string Unique group identifier file: flowserv.model.files.base.IOHandle File object (e.g., uploaded via HTTP request) name: string Name of the file Returns ------- flowserv.model.base.UploadFile Raises ------ flowserv.error.ConstraintViolationError flowserv.error.UnknownWorkflowGroupError """ # Get the group object to ensure that the group exists. group = self.get_group(group_id) # Ensure that the given file name is valid constraint.validate_name(name) # Create a new unique identifier for the file and save the file object # to the new file path. file_id = util.get_unique_identifier() uploaddir = self.fs.group_uploaddir( workflow_id=group.workflow_id, group_id=group.group_id ) # Get file size. file_size = file.size() # Attempt to guess the Mime type for the uploaded file from the file # name. mime_type, _ = mimetypes.guess_type(url=name) self.fs.store_files(files=[(file, file_id)], dst=uploaddir) # Insert information into database and return handle for uploaded file. fileobj = UploadFile( file_id=file_id, created_at=util.utc_now(), key=os.path.join(uploaddir, file_id), name=name, mime_type=mime_type, size=file_size ) group.uploads.append(fileobj) return fileobj
def update_workflow(self, workflow_id, name=None, description=None, instructions=None): """Update name, description, and instructions for a given workflow. Raises an error if the given workflow does not exist or if the name is not unique. Parameters ---------- workflow_id: string Unique workflow identifier name: string, optional Unique workflow name description: string, optional Optional short description for display in workflow listings instructions: string, optional Text containing detailed instructions for workflow execution Returns ------- flowserv.model.base.WorkflowObject Raises ------ flowserv.error.ConstraintViolationError flowserv.error.UnknownWorkflowError """ # Get the workflow from the database. This will raise an error if the # workflow does not exist. workflow = self.get_workflow(workflow_id) # Update workflow properties. if name is not None: # Ensure that the name is a valid name. constraint.validate_name(name) # Ensure that the name is unique. wf = self.session\ .query(WorkflowObject)\ .filter(WorkflowObject.name == name)\ .one_or_none() if wf is not None and wf.workflow_id != workflow_id: msg = "workflow '{}' exists".format(name) raise err.ConstraintViolationError(msg) workflow.name = name if description is not None: workflow.description = description if instructions is not None: workflow.instructions = instructions return workflow
def create_group(self, workflow_id: str, name: str, parameters: List[Parameter], workflow_spec: Dict, user_id: Optional[str] = None, members: List[str] = None, engine_config: Optional[Dict] = None, identifier: Optional[str] = None): """Create a new group for a given workflow. Within each workflow, the names of groups are expected to be unique. The workflow group may define additional parameters for the template. The full (modifued or original) parameter list is stored with the group together with the workflow specification. A group may have a list of users that are members. Membership can be used to control which users are allowed to execute the associated workflow and to upload/view files. The user that creates the group, identified by user_id parameter, is always part of the initial list of group members. If a list of members is given it is ensured that each identifier in the list references an existing user. Parameters ---------- workflow_id: string Unique workflow identifier name: string Group name user_id: string Unique identifier of the user that created the group parameters: list(flowserv.model.parameter.base.Parameter) List of workflow template parameter declarations that may be specific to the group workflow_spec: dict Workflow specification members: list(string), optional Optional list of user identifiers for other group members engine_config: dict, default=None Optional configuration settings that will be used as the default when running a workflow. identifier: string, default=None Optional user-provided group identifier. Returns ------- flowserv.model.base.GroupObject Raises ------ flowserv.error.ConstraintViolationError flowserv.error.UnknownUserError """ # Validate the given group identifier. This will raise a ValueError # if the identifier is invalid. validate_identifier(identifier) # Ensure that the given name is valid and unique for the workflow constraint.validate_name(name) # Ensure that the user identifier is not None. if user_id is None: raise err.UnknownUserError('none') group = self.session.query(GroupObject)\ .filter(GroupObject.name == name)\ .filter(GroupObject.workflow_id == workflow_id)\ .one_or_none() if group is not None: msg = "group '{}' exists".format(name) raise err.ConstraintViolationError(msg) # Create the group object identifier = identifier if identifier else unique_identifier() group = GroupObject(group_id=identifier, name=name, workflow_id=workflow_id, owner_id=user_id, parameters=parameters, workflow_spec=workflow_spec, engine_config=engine_config) # Create a set of member identifier that contains the identifier of # the group owner. Ensure that all group members exist. This will also # ensure that the group owner exists. member_set = set() if members is None else set(members) if user_id is not None and user_id not in member_set: member_set.add(user_id) for member_id in member_set: group.members.append(self.users.get_user(member_id, active=True)) # Enter group information into the database. self.session.add(group) return group
def test_valid_names(name): """Test no exception for valid names.""" validate_name(name)
def test_invalid_names(name): """Test exception for invalid names.""" with pytest.raises(err.ConstraintViolationError): validate_name(name)