def get_workflow_logs(workflow_id_or_name): # noqa r"""Get workflow logs from a workflow engine. --- get: summary: Returns logs of a specific workflow from a workflow engine. description: >- This resource is expecting a workflow UUID and a filename to return its outputs. operationId: get_workflow_logs produces: - application/json parameters: - name: user in: query description: Required. UUID of workflow owner. required: true type: string - name: workflow_id_or_name in: path description: Required. Workflow UUID or name. required: true type: string - name: steps in: body description: Steps of a workflow. required: false schema: type: array description: List of step names to get logs for. items: type: string description: Step name. responses: 200: description: >- Request succeeded. Info about workflow, including the status is returned. schema: type: object properties: workflow_id: type: string workflow_name: type: string logs: type: string user: type: string examples: application/json: { "workflow_id": "256b25f4-4cfb-4684-b7a8-73872ef455a1", "workflow_name": "mytest-1", "logs": "{'workflow_logs': string, 'job_logs': { '256b25f4-4cfb-4684-b7a8-73872ef455a2': string, '256b25f4-4cfb-4684-b7a8-73872ef455a3': string, }, 'engine_specific': object, }", "user": "******" } 400: description: >- Request failed. The incoming data specification seems malformed. 404: description: >- Request failed. User does not exist. examples: application/json: { "message": "User 00000000-0000-0000-0000-000000000000 does not exist" } 500: description: >- Request failed. Internal controller error. examples: application/json: { "message": "Internal workflow controller error." } """ try: user_uuid = request.args["user"] workflow = _get_workflow_with_uuid_or_name(workflow_id_or_name, user_uuid) if not str(workflow.owner_id) == user_uuid: return ( jsonify({ "message": "User {} is not allowed to access workflow {}".format( user_uuid, workflow_id_or_name) }), 403, ) steps = None if request.json: steps = request.json if steps: workflow_logs = { "workflow_logs": None, "job_logs": build_workflow_logs(workflow, steps), "engine_specific": None, } else: workflow_logs = { "workflow_logs": workflow.logs, "job_logs": build_workflow_logs(workflow), "engine_specific": workflow.engine_specific, } return ( jsonify({ "workflow_id": workflow.id_, "workflow_name": get_workflow_name(workflow), "logs": json.dumps(workflow_logs), "user": user_uuid, }), 200, ) except ValueError: return ( jsonify({ "message": "REANA_WORKON is set to {0}, but " "that workflow does not exist. " "Please set your REANA_WORKON environment " "variable appropriately.".format(workflow_id_or_name) }), 404, ) except KeyError as e: return jsonify({"message": str(e)}), 400 except Exception as e: return jsonify({"message": str(e)}), 500
def get_workflow_status(workflow_id_or_name): # noqa r"""Get workflow status. --- get: summary: Get workflow status. description: >- This resource reports the status of workflow. operationId: get_workflow_status produces: - application/json parameters: - name: user in: query description: Required. UUID of workflow owner. required: true type: string - name: workflow_id_or_name in: path description: Required. Workflow UUID or name. required: true type: string responses: 200: description: >- Request succeeded. Info about workflow, including the status is returned. schema: type: object properties: id: type: string name: type: string created: type: string status: type: string user: type: string logs: type: string progress: type: object examples: application/json: { "id": "256b25f4-4cfb-4684-b7a8-73872ef455a1", "name": "mytest-1", "created": "2018-06-13T09:47:35.66097", "status": "running", "user": "******" } 400: description: >- Request failed. The incoming data specification seems malformed. examples: application/json: { "message": "Malformed request." } 403: description: >- Request failed. User is not allowed to access workflow. examples: application/json: { "message": "User 00000000-0000-0000-0000-000000000000 is not allowed to access workflow 256b25f4-4cfb-4684-b7a8-73872ef455a1" } 404: description: >- Request failed. Either User or Workflow does not exist. examples: application/json: { "message": "User 00000000-0000-0000-0000-000000000000 does not exist" } application/json: { "message": "Workflow 256b25f4-4cfb-4684-b7a8-73872ef455a1 does not exist" } 500: description: >- Request failed. Internal controller error. """ try: user_uuid = request.args["user"] workflow = _get_workflow_with_uuid_or_name(workflow_id_or_name, user_uuid) workflow_logs = build_workflow_logs(workflow) if not str(workflow.owner_id) == user_uuid: return ( jsonify({ "message": "User {} is not allowed to access workflow {}".format( user_uuid, workflow_id_or_name) }), 403, ) return ( jsonify({ "id": workflow.id_, "name": get_workflow_name(workflow), "created": workflow.created.strftime(WORKFLOW_TIME_FORMAT), "status": workflow.status.name, "progress": get_workflow_progress(workflow), "user": user_uuid, "logs": json.dumps(workflow_logs), }), 200, ) except ValueError: return ( jsonify({ "message": "REANA_WORKON is set to {0}, but " "that workflow does not exist. " "Please set your REANA_WORKON environment " "variable appropriately.".format(workflow_id_or_name) }), 404, ) except KeyError as e: return jsonify({"message": str(e)}), 400 except Exception as e: return jsonify({"message": str(e)}), 500
def set_workflow_status(workflow_id_or_name): # noqa r"""Set workflow status. --- put: summary: Set workflow status. description: >- This resource sets the status of workflow. operationId: set_workflow_status produces: - application/json parameters: - name: user in: query description: Required. UUID of workflow owner. required: true type: string - name: workflow_id_or_name in: path description: Required. Workflow UUID or name. required: true type: string - name: status in: query description: Required. New status. required: true type: string enum: - start - stop - deleted - name: parameters in: body description: >- Optional. Additional input parameters and operational options for workflow execution. Possible parameters are `CACHE=on/off`, passed to disable caching of results in serial workflows, `all_runs=True/False` deletes all runs of a given workflow if status is set to deleted, `workspace=True/False` which deletes the workspace of a workflow and finally `hard_delete=True` which removes completely the workflow data from the database and the workspace from the shared filesystem. required: false schema: type: object properties: CACHE: type: string all_runs: type: boolean workspace: type: boolean hard_delete: type: boolean responses: 200: description: >- Request succeeded. Info about workflow, including the status is returned. schema: type: object properties: message: type: string workflow_id: type: string workflow_name: type: string status: type: string user: type: string examples: application/json: { "message": "Workflow successfully launched", "workflow_id": "256b25f4-4cfb-4684-b7a8-73872ef455a1", "workflow_name": "mytest-1", "status": "running", "user": "******" } 400: description: >- Request failed. The incoming data specification seems malformed. examples: application/json: { "message": "Malformed request." } 403: description: >- Request failed. User is not allowed to access workflow. examples: application/json: { "message": "User 00000000-0000-0000-0000-000000000000 is not allowed to access workflow 256b25f4-4cfb-4684-b7a8-73872ef455a1" } 404: description: >- Request failed. Either User or Workflow does not exist. examples: application/json: { "message": "User 00000000-0000-0000-0000-000000000000 does not exist" } application/json: { "message": "Workflow 256b25f4-4cfb-4684-b7a8-73872ef455a1 does not exist" } 409: description: >- Request failed. The workflow could not be started due to a conflict. examples: application/json: { "message": "Workflow 256b25f4-4cfb-4684-b7a8-73872ef455a1 could not be started because it is already running." } 500: description: >- Request failed. Internal controller error. 501: description: >- Request failed. The specified status change is not implemented. examples: application/json: { "message": "Status resume is not supported yet." } 502: description: >- Request failed. Connection to a third party system has failed. examples: application/json: { "message": "Connection to database timed out, please retry." } """ try: user_uuid = request.args["user"] workflow = _get_workflow_with_uuid_or_name(workflow_id_or_name, user_uuid) status = request.args.get("status") if not (status in STATUSES): return ( jsonify({ "message": "Status {0} is not one of: {1}".format( status, ", ".join(STATUSES)) }), 400, ) if not str(workflow.owner_id) == user_uuid: return ( jsonify({ "message": "User {} is not allowed to access workflow {}".format( user_uuid, workflow_id_or_name) }), 403, ) parameters = {} if request.json: parameters = request.json if status == START: start_workflow(workflow, parameters) return ( jsonify({ "message": "Workflow successfully launched", "workflow_id": str(workflow.id_), "workflow_name": get_workflow_name(workflow), "status": workflow.status.name, "user": str(workflow.owner_id), }), 200, ) elif status == DELETED: all_runs = True if request.json.get("all_runs") else False hard_delete = True if request.json.get("hard_delete") else False workspace = True if hard_delete or request.json.get( "workspace") else False return delete_workflow(workflow, all_runs, hard_delete, workspace) if status == STOP: stop_workflow(workflow) return ( jsonify({ "message": "Workflow successfully stopped", "workflow_id": workflow.id_, "workflow_name": get_workflow_name(workflow), "status": workflow.status.name, "user": str(workflow.owner_id), }), 200, ) else: raise NotImplementedError( "Status {} is not supported yet".format(status)) except ValueError: return ( jsonify({ "message": "REANA_WORKON is set to {0}, but " "that workflow does not exist. " "Please set your REANA_WORKON environment " "variable appropriately.".format(workflow_id_or_name) }), 404, ) except REANAWorkflowControllerError as e: return jsonify({"message": str(e)}), 409 except REANAWorkflowStatusError as e: return jsonify({"message": str(e)}), 404 except KeyError as e: return jsonify({"message": str(e)}), 400 except NotImplementedError as e: return jsonify({"message": str(e)}), 501 except REANAExternalCallError as e: return jsonify({"message": str(e)}), 502 except Exception as e: return jsonify({"message": str(e)}), 500
def get_workflows(paginate=None): # noqa r"""Get all workflows. --- get: summary: Returns all workflows. description: >- This resource is expecting a user UUID. The information related to all workflows for a given user will be served as JSON operationId: get_workflows produces: - application/json parameters: - name: user in: query description: Required. UUID of workflow owner. required: true type: string - name: type in: query description: Required. Type of workflows. required: true type: string - name: verbose in: query description: Optional flag to show more information. required: false type: boolean - name: search in: query description: Filter workflows by name. required: false type: string - name: sort in: query description: Sort workflows by creation date (asc, desc). required: false type: string - name: status in: query description: Filter workflows by list of statuses. required: false type: array items: type: string - name: page in: query description: Results page number (pagination). required: false type: integer - name: size in: query description: Number of results per page (pagination). required: false type: integer responses: 200: description: >- Requests succeeded. The response contains the current workflows for a given user. schema: type: object properties: total: type: integer items: type: array items: type: object properties: id: type: string name: type: string status: type: string size: type: object properties: raw: type: number human_readable: type: string user: type: string created: type: string progress: type: object examples: application/json: [ { "id": "256b25f4-4cfb-4684-b7a8-73872ef455a1", "name": "mytest.1", "status": "running", "size":{ "raw": 10490000, "human_readable": "10 MB" }, "user": "******", "created": "2018-06-13T09:47:35.66097", }, { "id": "3c9b117c-d40a-49e3-a6de-5f89fcada5a3", "name": "mytest.2", "status": "finished", "size":{ "raw": 12580000, "human_readable": "12 MB" }, "user": "******", "created": "2018-06-13T09:47:35.66097", }, { "id": "72e3ee4f-9cd3-4dc7-906c-24511d9f5ee3", "name": "mytest.3", "status": "created", "size":{ "raw": 184320, "human_readable": "180 KB" }, "user": "******", "created": "2018-06-13T09:47:35.66097", }, { "id": "c4c0a1a6-beef-46c7-be04-bf4b3beca5a1", "name": "mytest.4", "status": "created", "size": { "raw": 1074000000, "human_readable": "1 GB" }, "user": "******", "created": "2018-06-13T09:47:35.66097", } ] 400: description: >- Request failed. The incoming data specification seems malformed. 404: description: >- Request failed. User does not exist. examples: application/json: { "message": "User 00000000-0000-0000-0000-000000000000 does not exist" } 500: description: >- Request failed. Internal controller error. examples: application/json: { "message": "Internal workflow controller error." } """ try: user_uuid = request.args["user"] user = User.query.filter(User.id_ == user_uuid).first() type_ = request.args.get("type", "batch") verbose = json.loads(request.args.get("verbose", "false").lower()) sort = request.args.get("sort", "desc") search = request.args.get("search", "") status_list = request.args.get("status", "") if not user: return jsonify( {"message": "User {} does not exist".format(user_uuid)}), 404 workflows = [] query = user.workflows if search: query = query.filter(Workflow.name.ilike("%{}%".format(search))) if status_list: workflow_status = [ RunStatus[status] for status in status_list.split(",") ] query = query.filter(Workflow.status.in_(workflow_status)) if sort not in ["asc", "desc"]: sort = "desc" column_sorted = getattr(Workflow.created, sort)() pagination_dict = paginate(query.order_by(column_sorted)) for workflow in pagination_dict["items"]: workflow_response = { "id": workflow.id_, "name": get_workflow_name(workflow), "status": workflow.status.name, "user": user_uuid, "created": workflow.created.strftime(WORKFLOW_TIME_FORMAT), "progress": get_workflow_progress(workflow), } if type_ == "interactive" or verbose: int_session = workflow.sessions.first() if int_session: workflow_response["session_type"] = int_session.type_.name workflow_response["session_uri"] = int_session.path workflow_response[ "session_status"] = int_session.status.name # Skip workflow if type is interactive and there is no session elif type_ == "interactive": continue empty_disk_usage = { "human_readable": "", "raw": -1, } if verbose: workflow_response["size"] = (workflow.get_quota_usage().get( "disk", {}).get("usage", empty_disk_usage)) else: workflow_response["size"] = empty_disk_usage workflows.append(workflow_response) pagination_dict["items"] = workflows pagination_dict["user_has_workflows"] = user.workflows.first( ) is not None return jsonify(pagination_dict), 200 except (ValueError, KeyError): return jsonify({"message": "Malformed request."}), 400 except json.JSONDecodeError: return jsonify({"message": "Your request contains not valid JSON."}), 400 except Exception as e: return jsonify({"message": str(e)}), 500
def get_workflow_parameters(workflow_id_or_name): # noqa r"""Get workflow input parameters. --- get: summary: Get workflow parameters. description: >- This resource reports the input parameters of workflow. operationId: get_workflow_parameters produces: - application/json parameters: - name: user in: query description: Required. UUID of workflow owner. required: true type: string - name: workflow_id_or_name in: path description: Required. Workflow UUID or name. required: true type: string responses: 200: description: >- Request succeeded. Workflow input parameters, including the status are returned. schema: type: object properties: id: type: string name: type: string type: type: string parameters: type: object examples: application/json: { 'id': 'dd4e93cf-e6d0-4714-a601-301ed97eec60', 'name': 'workflow.24', 'type': 'serial', 'parameters': {'helloworld': 'code/helloworld.py', 'inputfile': 'data/names.txt', 'outputfile': 'results/greetings.txt', 'sleeptime': 2} } 400: description: >- Request failed. The incoming data specification seems malformed. examples: application/json: { "message": "Malformed request." } 403: description: >- Request failed. User is not allowed to access workflow. examples: application/json: { "message": "User 00000000-0000-0000-0000-000000000000 is not allowed to access workflow 256b25f4-4cfb-4684-b7a8-73872ef455a1" } 404: description: >- Request failed. Either User or Workflow does not exist. examples: application/json: { "message": "User 00000000-0000-0000-0000-000000000000 does not exist" } application/json: { "message": "Workflow 256b25f4-4cfb-4684-b7a8-73872ef455a1 does not exist" } 500: description: >- Request failed. Internal controller error. """ try: user_uuid = request.args["user"] workflow = _get_workflow_with_uuid_or_name(workflow_id_or_name, user_uuid) if not str(workflow.owner_id) == user_uuid: return ( jsonify({ "message": "User {} is not allowed to access workflow {}".format( user_uuid, workflow_id_or_name) }), 403, ) workflow_parameters = workflow.get_input_parameters() return ( jsonify({ "id": workflow.id_, "name": get_workflow_name(workflow), "type": workflow.reana_specification["workflow"]["type"], "parameters": workflow_parameters, }), 200, ) except ValueError: return ( jsonify({ "message": "REANA_WORKON is set to {0}, but " "that workflow does not exist. " "Please set your REANA_WORKON environment " "variable appropriately.".format(workflow_id_or_name) }), 404, ) except KeyError as e: return jsonify({"message": str(e)}), 400 except Exception as e: return jsonify({"message": str(e)}), 500
def create_workflow(): # noqa r"""Create workflow and its workspace. --- post: summary: Create workflow and its workspace. description: >- This resource expects all necessary data to represent a workflow so it is stored in database and its workspace is created. operationId: create_workflow produces: - application/json parameters: - name: user in: query description: Required. UUID of workflow owner. required: true type: string - name: workflow in: body description: >- JSON object including workflow parameters and workflow specification in JSON format (`yadageschemas.load()` output) with necessary data to instantiate a yadage workflow. required: true schema: type: object properties: operational_options: type: object description: Operational options. reana_specification: type: object description: >- Workflow specification in JSON format. workflow_name: type: string description: Workflow name. If empty name will be generated. git_data: type: object description: >- GitLab data. required: [reana_specification, workflow_name, operational_options] responses: 201: description: >- Request succeeded. The workflow has been created along with its workspace schema: type: object properties: message: type: string workflow_id: type: string workflow_name: type: string examples: application/json: { "message": "Workflow workspace has been created.", "workflow_id": "cdcf48b1-c2f3-4693-8230-b066e088c6ac", "workflow_name": "mytest-1" } 400: description: >- Request failed. The incoming data specification seems malformed 404: description: >- Request failed. User does not exist. examples: application/json: { "message": "User 00000000-0000-0000-0000-000000000000 does not exist" } """ try: user_uuid = request.args["user"] user = User.query.filter(User.id_ == user_uuid).first() if not user: return ( jsonify({ "message": "User with id:{} does not exist".format(user_uuid) }), 404, ) workflow_uuid = str(uuid4()) # Use name prefix user specified or use default name prefix # Actual name is prefix + autoincremented run_number. workflow_name = request.json.get("workflow_name", "") if workflow_name == "": workflow_name = DEFAULT_NAME_FOR_WORKFLOWS else: try: workflow_name.encode("ascii") except UnicodeEncodeError: # `workflow_name` contains something else than just ASCII. raise REANAWorkflowNameError( "Workflow name {} is not valid.".format(workflow_name)) git_ref = "" git_repo = "" if "git_data" in request.json: git_data = request.json["git_data"] git_ref = git_data["git_commit_sha"] git_repo = git_data["git_url"] # add spec and params to DB as JSON workflow = Workflow( id_=workflow_uuid, name=workflow_name, owner_id=request.args["user"], reana_specification=request.json["reana_specification"], operational_options=request.json.get("operational_options", {}), type_=request.json["reana_specification"]["workflow"]["type"], logs="", git_ref=git_ref, git_repo=git_repo, ) Session.add(workflow) Session.object_session(workflow).commit() if git_ref: create_workflow_workspace( workflow.workspace_path, user_id=user.id_, git_url=git_data["git_url"], git_branch=git_data["git_branch"], git_ref=git_ref, ) else: create_workflow_workspace(workflow.workspace_path) return ( jsonify({ "message": "Workflow workspace created", "workflow_id": workflow.id_, "workflow_name": get_workflow_name(workflow), }), 201, ) except (REANAWorkflowNameError, KeyError) as e: return jsonify({"message": str(e)}), 400 except Exception as e: return jsonify({"message": str(e)}), 500
def move_files(workflow_id_or_name): # noqa r"""Move files within workspace. --- put: summary: Move files within workspace. description: >- This resource moves files within the workspace. Resource is expecting a workflow UUID. operationId: move_files consumes: - application/json produces: - application/json parameters: - name: workflow_id_or_name in: path description: Required. Analysis UUID or name. required: true type: string - name: source in: query description: Required. Source file(s). required: true type: string - name: target in: query description: Required. Target file(s). required: true type: string - name: user in: query description: Required. UUID of workflow owner.. required: true type: string responses: 200: description: >- Request succeeded. Message about successfully moved files is returned. schema: type: object properties: message: type: string workflow_id: type: string workflow_name: type: string examples: application/json: { "message": "Files were successfully moved", "workflow_id": "256b25f4-4cfb-4684-b7a8-73872ef455a1", "workflow_name": "mytest.1", } 400: description: >- Request failed. The incoming payload seems malformed. examples: application/json: { "message": "Malformed request." } 403: description: >- Request failed. User is not allowed to access workflow. examples: application/json: { "message": "User 00000000-0000-0000-0000-000000000000 is not allowed to access workflow 256b25f4-4cfb-4684-b7a8-73872ef455a1" } 404: description: >- Request failed. Either User or Workflow does not exist. examples: application/json: { "message": "Workflow 256b25f4-4cfb-4684-b7a8-73872ef455a1 does not exist" } 500: description: >- Request failed. Internal controller error. """ try: user_uuid = request.args["user"] workflow = _get_workflow_with_uuid_or_name(workflow_id_or_name, user_uuid) source = request.args["source"] target = request.args["target"] if workflow.status == "running": return ( jsonify({"message": "Workflow is running, files can not be " "moved"}), 400, ) if not str(workflow.owner_id) == user_uuid: return ( jsonify( { "message": "User {} is not allowed to access workflow {}".format( user_uuid, workflow_id_or_name ) } ), 403, ) mv_files(source, target, workflow) message = "File(s) {} were successfully moved".format(source) return ( jsonify( { "message": message, "workflow_id": workflow.id_, "workflow_name": get_workflow_name(workflow), } ), 200, ) except ValueError: return ( jsonify( { "message": "REANA_WORKON is set to {0}, but " "that workflow does not exist. " "Please set your REANA_WORKON environment " "variable appropriately.".format(workflow_id_or_name) } ), 404, ) except REANAWorkflowControllerError as e: return jsonify({"message": str(e)}), 409 except KeyError as e: return jsonify({"message": str(e)}), 400 except NotImplementedError as e: return jsonify({"message": str(e)}), 501 except Exception as e: return jsonify({"message": str(e)}), 500
def get_workflows(): # noqa r"""Get all workflows. --- get: summary: Returns all workflows. description: >- This resource is expecting a user UUID. The information related to all workflows for a given user will be served as JSON operationId: get_workflows produces: - application/json parameters: - name: user in: query description: Required. UUID of workflow owner. required: true type: string - name: type in: query description: Required. Type of workflows. required: true type: string - name: verbose in: query description: Optional flag to show more information. required: false type: boolean - name: block_size in: query description: Size format, either 'b' (bytes) or 'k' (kilobytes). required: false type: string responses: 200: description: >- Requests succeeded. The response contains the current workflows for a given user. schema: type: array items: type: object properties: id: type: string name: type: string status: type: string size: type: string user: type: string created: type: string progress: type: object examples: application/json: [ { "id": "256b25f4-4cfb-4684-b7a8-73872ef455a1", "name": "mytest.1", "status": "running", "size": "10M", "user": "******", "created": "2018-06-13T09:47:35.66097", }, { "id": "3c9b117c-d40a-49e3-a6de-5f89fcada5a3", "name": "mytest.2", "status": "finished", "size": "12M", "user": "******", "created": "2018-06-13T09:47:35.66097", }, { "id": "72e3ee4f-9cd3-4dc7-906c-24511d9f5ee3", "name": "mytest.3", "status": "created", "size": "180K", "user": "******", "created": "2018-06-13T09:47:35.66097", }, { "id": "c4c0a1a6-beef-46c7-be04-bf4b3beca5a1", "name": "mytest.4", "status": "created", "size": "1G", "user": "******", "created": "2018-06-13T09:47:35.66097", } ] 400: description: >- Request failed. The incoming data specification seems malformed. 404: description: >- Request failed. User does not exist. examples: application/json: { "message": "User 00000000-0000-0000-0000-000000000000 does not exist" } 500: description: >- Request failed. Internal controller error. examples: application/json: { "message": "Internal workflow controller error." } """ try: user_uuid = request.args["user"] user = User.query.filter(User.id_ == user_uuid).first() type = request.args.get("type", "batch") verbose = json.loads(request.args.get("verbose", "false").lower()) block_size = request.args.get("block_size") if not user: return jsonify({"message": "User {} does not exist".format(user)}), 404 workflows = [] for workflow in user.workflows: workflow_response = { "id": workflow.id_, "name": get_workflow_name(workflow), "status": workflow.status.name, "user": user_uuid, "created": workflow.created.strftime(WORKFLOW_TIME_FORMAT), "progress": get_workflow_progress(workflow), "size": "-", } if type == "interactive": if (not workflow.interactive_session or not workflow.interactive_session_name or not workflow.interactive_session_type): continue else: workflow_response[ "session_type"] = workflow.interactive_session_type workflow_response[ "session_uri"] = workflow.interactive_session if verbose: reana_fs = fs.open_fs(SHARED_VOLUME_PATH) if reana_fs.exists(workflow.workspace_path): absolute_workspace_path = reana_fs.getospath( workflow.workspace_path) disk_usage_info = get_workspace_disk_usage( absolute_workspace_path, block_size=block_size) if disk_usage_info: workflow_response["size"] = disk_usage_info[-1]["size"] else: workflow_response["size"] = "0K" workflows.append(workflow_response) return jsonify(workflows), 200 except ValueError: return jsonify({"message": "Malformed request."}), 400 except KeyError: return jsonify({"message": "Malformed request."}), 400 except json.JSONDecodeError: return jsonify({"message": "Your request contains not valid JSON."}), 400 except Exception as e: return jsonify({"message": str(e)}), 500