Skip to content

Commit

Permalink
feat(rest): add share_workflow endpoint (reanahub#658)
Browse files Browse the repository at this point in the history
Adds a new endpoint to share a workflow with a user.

Closes reanahub/reana-client#680
  • Loading branch information
DaanRosendal committed Mar 20, 2024
1 parent 9fa4059 commit ae3584d
Show file tree
Hide file tree
Showing 2 changed files with 231 additions and 18 deletions.
105 changes: 105 additions & 0 deletions docs/openapi.json
Original file line number Diff line number Diff line change
Expand Up @@ -2972,6 +2972,111 @@
"summary": "Get the retention rules of a workflow."
}
},
"/api/workflows/{workflow_id_or_name}/share": {
"post": {
"description": "This resource shares a workflow with another user. This resource is expecting a workflow UUID and some parameters.",
"operationId": "share_workflow",
"parameters": [
{
"description": "The API access_token of workflow owner.",
"in": "query",
"name": "access_token",
"required": false,
"type": "string"
},
{
"description": "Required. Workflow UUID or name.",
"in": "path",
"name": "workflow_id_or_name",
"required": true,
"type": "string"
},
{
"description": "JSON object with details of the share.",
"in": "body",
"name": "share_details",
"required": true,
"schema": {
"properties": {
"message": {
"description": "Optional. Message to include when sharing the workflow.",
"type": "string"
},
"user_email_to_share_with": {
"description": "User to share the workflow with.",
"type": "string"
},
"valid_until": {
"description": "Optional. Date when access to the workflow will expire (format YYYY-MM-DD).",
"type": "string"
}
},
"required": [
"user_email_to_share_with"
],
"type": "object"
}
}
],
"produces": [
"application/json"
],
"responses": {
"200": {
"description": "Request succeeded. The workflow has been shared with the user.",
"examples": {
"application/json": {
"message": "The workflow has been shared with the user.",
"workflow_id": "cdcf48b1-c2f3-4693-8230-b066e088c6ac",
"workflow_name": "mytest.1"
}
},
"schema": {
"properties": {
"message": {
"type": "string"
},
"workflow_id": {
"type": "string"
},
"workflow_name": {
"type": "string"
}
},
"type": "object"
}
},
"400": {
"description": "Request failed. The incoming data seems malformed."
},
"404": {
"description": "Request failed. Workflow does not exist or user does not exist.",
"examples": {
"application/json": {
"message": "Workflow cdcf48b1-c2f3-4693-8230-b066e088c6ac does not exist"
}
}
},
"409": {
"description": "Request failed. The workflow is already shared with the user.",
"examples": {
"application/json": {
"message": "The workflow is already shared with the user."
}
}
},
"500": {
"description": "Request failed. Internal controller error.",
"examples": {
"application/json": {
"message": "Internal controller error."
}
}
}
},
"summary": "Share a workflow with another user."
}
},
"/api/workflows/{workflow_id_or_name}/specification": {
"get": {
"description": "This resource returns the REANA workflow specification used to start the workflow run. Resource is expecting a workflow UUID.",
Expand Down
144 changes: 126 additions & 18 deletions reana_server/rest/workflows.py
Original file line number Diff line number Diff line change
@@ -1,56 +1,53 @@
# -*- coding: utf-8 -*-
#
# This file is part of REANA.
# Copyright (C) 2018, 2019, 2020, 2021, 2022 CERN.
# Copyright (C) 2018, 2019, 2020, 2021, 2022, 2023 CERN.
#
# REANA is free software; you can redistribute it and/or modify it
# under the terms of the MIT License; see LICENSE file for more details.

"""Reana-Server workflow-functionality Flask-Blueprint."""
import os
import json
import logging
import os
import traceback

import requests
from bravado.exception import HTTPError
from flask import Blueprint, Response
from flask import jsonify, request, stream_with_context
from flask import Blueprint, Response, jsonify, request, stream_with_context
from jsonschema.exceptions import ValidationError

from reana_commons import workspace
from reana_commons.config import REANA_WORKFLOW_ENGINES
from reana_commons.errors import REANAQuotaExceededError, REANAValidationError
from reana_commons.specification import load_reana_spec
from reana_commons.validation.operational_options import validate_operational_options
from reana_commons.validation.utils import validate_workflow_name
from reana_commons.specification import load_reana_spec
from reana_db.database import Session
from reana_db.models import InteractiveSessionType, RunStatus
from reana_db.utils import _get_workflow_with_uuid_or_name
from webargs import fields, validate
from webargs.flaskparser import use_kwargs

from reana_server.api_client import current_rwc_api_client
from reana_server.config import REANA_HOSTNAME
from reana_server.decorators import check_quota, signin_required
from reana_server.deleter import Deleter, InOrOut
from reana_server.validation import (
validate_inputs,
validate_workspace_path,
validate_workflow,
)
from reana_server.utils import (
_fail_gitlab_commit_build_status,
RequestStreamWithLen,
_load_and_save_yadage_spec,
_fail_gitlab_commit_build_status,
_get_reana_yaml_from_gitlab,
prevent_disk_quota_excess,
publish_workflow_submission,
_load_and_save_yadage_spec,
clone_workflow,
get_quota_excess_message,
get_workspace_retention_rules,
is_uuid_v4,
prevent_disk_quota_excess,
publish_workflow_submission,
)
from reana_server.validation import (
validate_inputs,
validate_workflow,
validate_workspace_path,
)
from webargs import fields, validate
from webargs.flaskparser import use_kwargs

try:
from urllib import parse as urlparse
Expand Down Expand Up @@ -3251,3 +3248,114 @@ def prune_workspace(
except Exception as e:
logging.exception(str(e))
return jsonify({"message": str(e)}), 500


@blueprint.route("/workflows/<workflow_id_or_name>/share", methods=["POST"])
@signin_required()
def share_workflow(workflow_id_or_name, user):
r"""Share a workflow with another user.
---
post:
summary: Share a workflow with another user.
description: >-
This resource shares a workflow with another user.
This resource is expecting a workflow UUID and some parameters.
operationId: share_workflow
produces:
- application/json
parameters:
- name: access_token
in: query
description: The API access_token of workflow owner.
required: false
type: string
- name: workflow_id_or_name
in: path
description: Required. Workflow UUID or name.
required: true
type: string
- name: share_details
in: body
description: JSON object with details of the share.
required: true
schema:
type: object
properties:
user_email_to_share_with:
type: string
description: User to share the workflow with.
message:
type: string
description: Optional. Message to include when sharing the workflow.
valid_until:
type: string
description: Optional. Date when access to the workflow will expire (format YYYY-MM-DD).
required: [user_email_to_share_with]
responses:
200:
description: >-
Request succeeded. The workflow has been shared with the user.
schema:
type: object
properties:
message:
type: string
workflow_id:
type: string
workflow_name:
type: string
examples:
application/json:
{
"message": "The workflow has been shared with the user.",
"workflow_id": "cdcf48b1-c2f3-4693-8230-b066e088c6ac",
"workflow_name": "mytest.1"
}
400:
description: >-
Request failed. The incoming data seems malformed.
404:
description: >-
Request failed. Workflow does not exist or user does not exist.
examples:
application/json:
{
"message": "Workflow cdcf48b1-c2f3-4693-8230-b066e088c6ac does
not exist",
}
409:
description: >-
Request failed. The workflow is already shared with the user.
examples:
application/json:
{
"message": "The workflow is already shared with the user.",
}
500:
description: >-
Request failed. Internal controller error.
examples:
application/json:
{
"message": "Internal controller error.",
}
"""
try:
response, http_response = current_rwc_api_client.api.share_workflow(
workflow_id_or_name=workflow_id_or_name,
user=str(user.id_),
share_details=request.json,
).result()

return jsonify(response), 200
except HTTPError as e:
logging.exception(str(e))
return jsonify(e.response.json()), e.response.status_code
except ValueError as e:
# In case of invalid workflow name / UUID
logging.exception(str(e))
return jsonify({"message": str(e)}), 403
except Exception as e:
logging.exception(str(e))
return jsonify({"message": str(e)}), 500

0 comments on commit ae3584d

Please sign in to comment.