From e36c6a2010f9f4ad15a1cb1bc04d8e19f6c154fa Mon Sep 17 00:00:00 2001 From: Marco Donadoni Date: Thu, 22 Aug 2024 14:50:51 +0200 Subject: [PATCH] fix(rest): clean up workflow sharing code after changes (#552) --- docs/openapi.json | 85 +----- reana_workflow_controller/config.py | 1 + reana_workflow_controller/factory.py | 13 +- reana_workflow_controller/rest/workflows.py | 322 ++++++-------------- requirements.txt | 2 +- setup.py | 4 +- tests/test_views.py | 58 ++-- 7 files changed, 151 insertions(+), 334 deletions(-) diff --git a/docs/openapi.json b/docs/openapi.json index 8ce8d276..200aac6f 100644 --- a/docs/openapi.json +++ b/docs/openapi.json @@ -98,14 +98,14 @@ "type": "boolean" }, { - "description": "Optional argument to list workflows shared by the specified user(s).", + "description": "Optional argument to list workflows shared by the specified user.", "in": "query", "name": "shared_by", "required": false, "type": "string" }, { - "description": "Optional argument to list workflows shared with the specified user(s).", + "description": "Optional argument to list workflows shared with the specified user.", "in": "query", "name": "shared_with", "required": false, @@ -195,7 +195,10 @@ "type": "object" }, "shared_with": { - "type": "string" + "items": { + "type": "string" + }, + "type": "array" }, "size": { "properties": { @@ -1133,7 +1136,7 @@ { "description": "Required. UUID of workflow owner.", "in": "query", - "name": "user_id", + "name": "user", "required": true, "type": "string" }, @@ -1190,38 +1193,6 @@ "type": "object" } }, - "401": { - "description": "Request failed. User not signed in.", - "examples": { - "application/json": { - "message": "User not signed in." - } - }, - "schema": { - "properties": { - "message": { - "type": "string" - } - }, - "type": "object" - } - }, - "403": { - "description": "Request failed. Credentials are invalid or revoked.", - "examples": { - "application/json": { - "message": "Token not valid." - } - }, - "schema": { - "properties": { - "message": { - "type": "string" - } - }, - "type": "object" - } - }, "404": { "description": "Request failed. Workflow does not exist.", "examples": { @@ -1500,7 +1471,7 @@ { "description": "Required. UUID of workflow owner.", "in": "query", - "name": "user_id", + "name": "user", "required": true, "type": "string" }, @@ -1551,20 +1522,11 @@ "description": "Request failed. The incoming data specification seems malformed.", "examples": { "application/json": { - "errors": [ - "Missing data for required field." - ], "message": "Malformed request." } }, "schema": { "properties": { - "errors": { - "items": { - "type": "string" - }, - "type": "array" - }, "message": { "type": "string" } @@ -1576,9 +1538,7 @@ "description": "Request failed. User is not allowed to unshare the workflow.", "examples": { "application/json": { - "errors": [ - "User is not allowed to unshare the workflow." - ] + "message": "User is not allowed to unshare the workflow." } }, "schema": { @@ -1594,20 +1554,11 @@ "description": "Request failed. Workflow does not exist or user does not exist.", "examples": { "application/json": { - "errors": [ - "Workflow cdcf48b1-c2f3-4693-8230-b066e088c6ac does not exist" - ], "message": "Workflow cdcf48b1-c2f3-4693-8230-b066e088c6ac does not exist" } }, "schema": { "properties": { - "errors": { - "items": { - "type": "string" - }, - "type": "array" - }, "message": { "type": "string" } @@ -1619,20 +1570,11 @@ "description": "Request failed. The workflow is not shared with the user.", "examples": { "application/json": { - "errors": [ - "The workflow is not shared with the user." - ], "message": "The workflow is not shared with the user." } }, "schema": { "properties": { - "errors": { - "items": { - "type": "string" - }, - "type": "array" - }, "message": { "type": "string" } @@ -1644,20 +1586,11 @@ "description": "Request failed. Internal controller error.", "examples": { "application/json": { - "errors": [ - "Internal controller error." - ], "message": "Internal controller error." } }, "schema": { "properties": { - "errors": { - "items": { - "type": "string" - }, - "type": "array" - }, "message": { "type": "string" } diff --git a/reana_workflow_controller/config.py b/reana_workflow_controller/config.py index 62859127..56d5ebdd 100644 --- a/reana_workflow_controller/config.py +++ b/reana_workflow_controller/config.py @@ -277,3 +277,4 @@ def _parse_interactive_sessions_environments(env_var): """Prefixes that can be removed from container image references to generate valid image aliases.""" MAX_WORKFLOW_SHARING_MESSAGE_LENGTH = 5000 +"""Maximum length of the user-provided message when sharing a workflow.""" diff --git a/reana_workflow_controller/factory.py b/reana_workflow_controller/factory.py index d728651b..a193da71 100644 --- a/reana_workflow_controller/factory.py +++ b/reana_workflow_controller/factory.py @@ -33,10 +33,15 @@ def handle_args_validation_error(error: UnprocessableEntity): exception = getattr(error, "exc", None) if isinstance(exception, ValidationError): validation_messages = [] - for field, messages in exception.normalized_messages().items(): - validation_messages.append( - "Field '{}': {}".format(field, ", ".join(messages)) - ) + # this is slightly different from r-server error handler due to different + # versions of webargs/marshmallow + # the format of normalized_messages() is: + # {"json": {"field name": ["error 1", "error 2"]}} + for field_messages in exception.normalized_messages().values(): + for field, messages in field_messages.items(): + validation_messages.append( + "Field '{}': {}".format(field, ", ".join(messages)) + ) error_message = ". ".join(validation_messages) return jsonify({"message": error_message}), 400 diff --git a/reana_workflow_controller/rest/workflows.py b/reana_workflow_controller/rest/workflows.py index ef048ebb..1ee83fa1 100644 --- a/reana_workflow_controller/rest/workflows.py +++ b/reana_workflow_controller/rest/workflows.py @@ -13,13 +13,13 @@ import logging import re from typing import Optional -from uuid import UUID, uuid4 +from uuid import uuid4 from flask import Blueprint, jsonify, request -from sqlalchemy import and_, nullslast, or_ +from sqlalchemy import and_, nullslast, or_, select from sqlalchemy.orm import aliased from sqlalchemy.exc import IntegrityError -from webargs import fields +from webargs import fields, validate from webargs.flaskparser import use_args, use_kwargs from reana_commons.config import WORKFLOW_TIME_FORMAT from reana_db.database import Session @@ -71,7 +71,7 @@ "workflow_id_or_name": fields.String(), "shared": fields.Bool(missing=False), "shared_by": fields.String(), - "shared_with": fields.DelimitedList(fields.String()), + "shared_with": fields.String(), }, location="query", ) @@ -153,12 +153,12 @@ def get_workflows(args, paginate=None): # noqa type: boolean - name: shared_by in: query - description: Optional argument to list workflows shared by the specified user(s). + description: Optional argument to list workflows shared by the specified user. required: false type: string - name: shared_with in: query - description: Optional argument to list workflows shared with the specified user(s). + description: Optional argument to list workflows shared with the specified user. required: false type: string responses: @@ -201,7 +201,9 @@ def get_workflows(args, paginate=None): # noqa owner_email: type: string shared_with: - type: string + type: array + items: + type: string examples: application/json: [ @@ -289,85 +291,46 @@ def get_workflows(args, paginate=None): # noqa shared_by: Optional[str] = args.get("shared_by") shared_with: Optional[str] = args.get("shared_with") + if shared_by and shared_with: + message = "You cannot filter by shared_by and shared_with at the same time." + return (jsonify({"message": message}), 400) + try: - if shared_by and shared_with: - return ( - jsonify( - { - "message": "You cannot filter by shared_by and shared_with at the same time." - } - ), - 400, - ) user = User.query.filter(User.id_ == user_uuid).first() if not user: return jsonify({"message": "User {} does not exist".format(user_uuid)}), 404 - workflows = [] - - if not shared and not shared_with and not shared_by: - # default case: retrieve owned workflows - query = user.workflows - else: - if shared or shared_by: - # retrieve owned workflows and unowned workflows shared by others - workflows_shared_with_user = Session.query( - user.workflows_shared_with_me.subquery().c.id_ + # default case: retrieve owned workflows + query = user.workflows + if shared_with: + if shared_with == "nobody": + # retrieve owned unshared workflows + query = user.workflows.filter( + Workflow.id_.notin_(select(UserWorkflow.workflow_id)) ) - query = Session.query(Workflow).filter( - or_( - Workflow.owner_id == user.id_, - Workflow.id_.in_(workflows_shared_with_user), - ) + elif shared_with == "anybody": + # retrieve exclusively owned shared workflows + query = user.workflows.filter( + Workflow.id_.in_(select(UserWorkflow.workflow_id)) ) - if shared_by and not shared: - if shared_by == "anybody": - # retrieve unowned workflows shared by anyone - query = query.filter(Workflow.id_.in_(workflows_shared_with_user)) - else: - # retrieve unowned workflows shared by specific user - shared_by_user = ( - Session.query(User).filter(User.email == shared_by).first() - ) - if not shared_by_user: - return ( - jsonify( - { - "message": f"User with email '{shared_by}' does not exist." - } - ), - 404, - ) - query = query.filter(Workflow.owner_id == shared_by_user.id_) - if shared_with: - # starting point: retrieve owned workflows - query = user.workflows - - first_shared_with = shared_with[0] - workflows_shared_by_user = Session.query(Workflow.id_).join( - UserWorkflow, - and_( - Workflow.id_ == UserWorkflow.workflow_id, - Workflow.owner_id == user.id_, - ), + else: + # retrieve owned workflows shared with specific user + query = user.workflows.filter( + Workflow.users_it_is_shared_with.any(User.email == shared_with) ) - - if first_shared_with == "nobody": - # retrieve owned unshared workflows - query = query.filter(Workflow.id_.notin_(workflows_shared_by_user)) - elif first_shared_with == "anybody": - # retrieve exclusively owned shared workflows - query = query.filter(Workflow.id_.in_(workflows_shared_by_user)) - else: - # retrieve owned workflows shared with specific users - shared_with_users = Session.query(User.id_).filter( - User.email.in_(shared_with) - ) - shared_with_workflows_ids = Session.query( - UserWorkflow.workflow_id - ).filter(UserWorkflow.user_id.in_(shared_with_users)) - query = query.filter(Workflow.id_.in_(shared_with_workflows_ids)) + elif shared_by: + if shared_by == "anybody": + # retrieve unowned workflows shared by anyone + query = user.workflows_shared_with_me + else: + # retrieve unowned workflows shared by specific user + query = user.workflows_shared_with_me.filter( + Workflow.owner.has(User.email == shared_by) + ) + elif shared: + # retrieve all workflows, owned and shared with user + query = user.workflows.union_all(user.workflows_shared_with_me) if search: search = json.loads(search) @@ -404,31 +367,18 @@ def get_workflows(args, paginate=None): # noqa Session.query(User.id_, User.email).filter(User.id_.in_(owner_ids)).all() ) + workflows = [] for workflow in pagination_dict["items"]: - owner_email = owners.get(workflow.owner_id, "-") - - if owner_email == user.email: - owner_email = "-" - - if shared or shared_with: - shared_with_users = ( - Session.query(User.email) - .join( - UserWorkflow, - and_( - User.id_ == UserWorkflow.user_id, - UserWorkflow.workflow_id == workflow.id_, - ), + owner_email = owners[workflow.owner_id] + if workflow.owner_id == user.id_: + shared_with = [ + user.email + for user in workflow.users_it_is_shared_with.with_entities( + User.email ) - .all() - ) - else: - shared_with_users = None - - if shared_with_users and owner_email == "-": - shared_with_emails = [user[0] for user in shared_with_users] + ] else: - shared_with_emails = "-" + shared_with = [] workflow_response = { "id": workflow.id_, @@ -441,9 +391,7 @@ def get_workflows(args, paginate=None): # noqa workflow, include_progress=include_progress ), "owner_email": owner_email, - "shared_with": ( - ",".join(shared_with_emails) if shared_with_emails != "-" else "-" - ), + "shared_with": shared_with, } if type_ == "interactive" or verbose: int_session = workflow.sessions.first() @@ -1037,7 +985,26 @@ def get_workflow_retention_rules(workflow_id_or_name: str, user: str): @blueprint.route("/workflows//share", methods=["POST"]) @use_kwargs({"user": fields.Str(required=True)}, location="query") -def share_workflow(workflow_id_or_name: str, user: str): +@use_kwargs( + { + "user_email_to_share_with": fields.Str(required=True), + "message": fields.Str( + validate=validate.Length( + max=MAX_WORKFLOW_SHARING_MESSAGE_LENGTH, + error="Message is too long. Please keep it under {max} characters.", + ) + ), + "valid_until": fields.Date( + error_messages={ + "invalid": "Date format is not valid. Please use YYYY-MM-DD format." + } + ), + }, + location="json", +) +def share_workflow( + workflow_id_or_name: str, user: str, user_email_to_share_with: str, **kwargs +): r"""Share a workflow with other users. --- @@ -1125,6 +1092,9 @@ def share_workflow(workflow_id_or_name: str, user: str): "message": "Internal controller error.", } """ + message = kwargs.get("message") + valid_until = kwargs.get("valid_until") + try: sharer = User.query.filter(User.id_ == user).first() if not sharer: @@ -1133,11 +1103,6 @@ def share_workflow(workflow_id_or_name: str, user: str): 404, ) - share_details = request.json - user_email_to_share_with = share_details.get("user_email_to_share_with") - message = share_details.get("message", None) - valid_until = share_details.get("valid_until", None) - if sharer.email == user_email_to_share_with: raise ValueError("Unable to share a workflow with yourself.") @@ -1157,24 +1122,8 @@ def share_workflow(workflow_id_or_name: str, user: str): 404, ) - if valid_until: - try: - datetime.date.fromisoformat(valid_until) - except ValueError as e: - raise ValueError( - f"Date format is not valid ({str(e)}). Please use YYYY-MM-DD format." - ) - - # check if date is in the future - if datetime.date.fromisoformat(valid_until) < datetime.date.today(): - raise ValueError("The 'valid_until' date cannot be in the past.") - - if message and len(message) > MAX_WORKFLOW_SHARING_MESSAGE_LENGTH: - raise ValueError( - "Message is too long. Please keep it under " - + str(MAX_WORKFLOW_SHARING_MESSAGE_LENGTH) - + " characters." - ) + if valid_until and valid_until < datetime.date.today(): + raise ValueError("The 'valid_until' date cannot be in the past.") workflow = _get_workflow_with_uuid_or_name(workflow_id_or_name, sharer.id_) @@ -1216,13 +1165,13 @@ def share_workflow(workflow_id_or_name: str, user: str): @blueprint.route("/workflows//unshare", methods=["POST"]) @use_kwargs( { - "user_id": fields.Str(required=True), + "user": fields.Str(required=True), "user_email_to_unshare_with": fields.Str(required=True), }, location="query", ) def unshare_workflow( - workflow_id_or_name: str, user_id: str, user_email_to_unshare_with: str + workflow_id_or_name: str, user: str, user_email_to_unshare_with: str ): r"""Unshare a workflow with other users. @@ -1235,7 +1184,7 @@ def unshare_workflow( produces: - application/json parameters: - - name: user_id + - name: user in: query description: Required. UUID of workflow owner. required: true @@ -1279,15 +1228,10 @@ def unshare_workflow( properties: message: type: string - errors: - type: array - items: - type: string examples: application/json: { "message": "Malformed request.", - "errors": ["Missing data for required field."] } 403: description: >- @@ -1300,7 +1244,7 @@ def unshare_workflow( examples: application/json: { - "errors": ["User is not allowed to unshare the workflow."] + "message": "User is not allowed to unshare the workflow." } 404: description: >- @@ -1310,17 +1254,11 @@ def unshare_workflow( properties: message: type: string - errors: - type: array - items: - type: string examples: application/json: { "message": "Workflow cdcf48b1-c2f3-4693-8230-b066e088c6ac does - not exist", - "errors": ["Workflow cdcf48b1-c2f3-4693-8230-b066e088c6ac does - not exist"] + not exist" } 409: description: >- @@ -1330,15 +1268,10 @@ def unshare_workflow( properties: message: type: string - errors: - type: array - items: - type: string examples: application/json: { - "message": "The workflow is not shared with the user.", - "errors": ["The workflow is not shared with the user."] + "message": "The workflow is not shared with the user." } 500: description: >- @@ -1348,35 +1281,21 @@ def unshare_workflow( properties: message: type: string - errors: - type: array - items: - type: string examples: application/json: { - "message": "Internal controller error.", - "errors": ["Internal controller error."] + "message": "Internal controller error." } """ try: - user = User.query.filter(User.id_ == user_id).first() - if not user: + sharer = User.query.filter(User.id_ == user).first() + if not sharer: return ( - jsonify({"message": f"User with id '{user_id}' does not exist."}), + jsonify({"message": f"User with id '{sharer}' does not exist."}), 404, ) - if ( - re.match( - r"\b[A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\.[A-Z|a-z]{2,7}\b", - user_email_to_unshare_with, - ) - is None - ): - raise ValueError(f"User email '{user_email_to_unshare_with}' is not valid.") - - if user.email == user_email_to_unshare_with: + if sharer.email == user_email_to_unshare_with: raise ValueError("Unable to unshare a workflow with yourself.") user_to_unshare_with = ( @@ -1384,16 +1303,10 @@ def unshare_workflow( ) if not user_to_unshare_with: - return ( - jsonify( - { - "message": f"User with email '{user_email_to_unshare_with}' does not exist." - } - ), - 404, - ) + message = f"User with email '{user_email_to_unshare_with}' does not exist." + return jsonify({"message": message}), 404 - workflow = _get_workflow_with_uuid_or_name(workflow_id_or_name, str(user_id)) + workflow = _get_workflow_with_uuid_or_name(workflow_id_or_name, str(sharer.id_)) existing_share = ( Session.query(UserWorkflow) @@ -1402,14 +1315,8 @@ def unshare_workflow( ) if not existing_share: - return ( - jsonify( - { - "message": f"{workflow.get_full_workflow_name()} is not shared with {user_email_to_unshare_with}." - } - ), - 409, - ) + message = f"{workflow.get_full_workflow_name()} is not shared with {user_email_to_unshare_with}." + return (jsonify({"message": message}), 409) Session.delete(existing_share) Session.commit() @@ -1430,15 +1337,10 @@ def unshare_workflow( @blueprint.route("/workflows//share-status", methods=["GET"]) -@use_kwargs( - { - "user_id": fields.Str(required=True), - }, - location="query", -) +@use_kwargs({"user": fields.Str(required=True)}, location="query") def get_workflow_share_status( workflow_id_or_name: str, - user_id: str, + user: str, ): r"""Get the share status of a workflow. @@ -1451,7 +1353,7 @@ def get_workflow_share_status( produces: - application/json parameters: - - name: user_id + - name: user in: query description: Required. UUID of workflow owner. required: true @@ -1494,32 +1396,6 @@ def get_workflow_share_status( } ] } - 401: - description: >- - Request failed. User not signed in. - schema: - type: object - properties: - message: - type: string - examples: - application/json: - { - "message": "User not signed in." - } - 403: - description: >- - Request failed. Credentials are invalid or revoked. - schema: - type: object - properties: - message: - type: string - examples: - application/json: - { - "message": "Token not valid." - } 404: description: >- Request failed. Workflow does not exist. @@ -1548,13 +1424,13 @@ def get_workflow_share_status( } """ try: - workflow = _get_workflow_with_uuid_or_name(workflow_id_or_name, user_id) + workflow = _get_workflow_with_uuid_or_name(workflow_id_or_name, user) shared_with = ( Session.query(UserWorkflow) .filter_by(workflow_id=workflow.id_) .join(User, User.id_ == UserWorkflow.user_id) - .add_columns(User.email, UserWorkflow.valid_until) + .with_entities(User.email, UserWorkflow.valid_until) .all() ) @@ -1563,9 +1439,11 @@ def get_workflow_share_status( "workflow_name": workflow.get_full_workflow_name(), "shared_with": [ { - "user_email": share[1], + "user_email": share.email, "valid_until": ( - share[2].strftime("%Y-%m-%dT%H:%M:%S") if share[2] else None + share.valid_until.strftime(WORKFLOW_TIME_FORMAT) + if share.valid_until + else None ), } for share in shared_with diff --git a/requirements.txt b/requirements.txt index 8a99509e..5ec6cec7 100644 --- a/requirements.txt +++ b/requirements.txt @@ -55,7 +55,7 @@ python-dateutil==2.9.0.post0 # via arrow, bravado, bravado-core, kubernetes pytz==2024.1 # via bravado-core pyyaml==6.0.1 # via bravado, bravado-core, kubernetes, reana-commons, swagger-spec-validator reana-commons[kubernetes]==0.95.0a3 # via reana-db, reana-workflow-controller (setup.py) -reana-db==0.95.0a3 # via reana-workflow-controller (setup.py) +reana-db==0.95.0a4 # via reana-workflow-controller (setup.py) referencing==0.35.1 # via jsonschema, jsonschema-specifications requests==2.32.3 # via bravado, bravado-core, kubernetes, reana-workflow-controller (setup.py), requests-oauthlib requests-oauthlib==2.0.0 # via kubernetes diff --git a/setup.py b/setup.py index 9d18ed57..cf4a2b8c 100644 --- a/setup.py +++ b/setup.py @@ -33,7 +33,7 @@ "sphinxcontrib-redoc>=1.5.1", ], "tests": [ - "pytest-reana>=0.95.0a2,<0.96.0", + "pytest-reana>=0.95.0a4,<0.96.0", ], } @@ -51,7 +51,7 @@ "marshmallow>2.13.0,<3.0.0", # same upper pin as reana-server "packaging>=18.0", "reana-commons[kubernetes] @ git+https://github.com/reanahub/reana-commons.git@0.95.0a4", - "reana-db>=0.95.0a3,<0.96.0", + "reana-db>=0.95.0a4,<0.96.0", "requests>=2.25.0", "sqlalchemy-utils>=0.31.0", "uwsgi-tools>=1.1.1", diff --git a/tests/test_views.py b/tests/test_views.py index c9b13e2b..06553c69 100644 --- a/tests/test_views.py +++ b/tests/test_views.py @@ -71,8 +71,8 @@ def test_get_workflows(app, session, user0, cwl_workflow_with_name): "progress": response_data[0]["progress"], "size": {"raw": -1, "human_readable": ""}, "launcher_url": None, - "owner_email": "-", - "shared_with": "-", + "owner_email": user0.email, + "shared_with": [], } ] @@ -176,8 +176,8 @@ def test_get_workflows_shared( response_data = json.loads(res.get_data(as_text=True))["items"] assert len(response_data) == 1 assert response_data[0]["id"] == str(workflow.id_) - assert response_data[0]["shared_with"] == user2.email - assert response_data[0]["owner_email"] == "-" + assert response_data[0]["shared_with"] == [user2.email] + assert response_data[0]["owner_email"] == user1.email def test_get_workflows_shared_by( @@ -245,7 +245,7 @@ def test_get_workflows_shared_with( response_data = json.loads(res.get_data(as_text=True))["items"] assert len(response_data) == 1 assert response_data[0]["id"] == str(workflow.id_) - assert response_data[0]["shared_with"] == user2.email + assert response_data[0]["shared_with"] == [user2.email] def test_get_workflows_shared_by_and_shared_with( @@ -261,7 +261,7 @@ def test_get_workflows_shared_by_and_shared_with( workflow_id_or_name=str(workflow.id_), ), query_string={ - "user_id": str(user1.id_), + "user": str(user1.id_), "user_email_to_share_with": user2.email, }, ) @@ -812,7 +812,7 @@ def test_get_workflow_status_unauthorized( content_type="application/json", data=json.dumps(cwl_workflow_with_name), ) - assert res.status_code == 500 + assert res.status_code == 404 def test_get_workflow_status_unknown_workflow(app, user0, cwl_workflow_with_name): @@ -1027,7 +1027,7 @@ def test_set_workflow_status_unauthorized( query_string={"user": random_user_uuid, "status": payload}, content_type="application/json", ) - assert res.status_code == 500 + assert res.status_code == 404 def test_set_workflow_status_unknown_workflow( @@ -1230,7 +1230,7 @@ def test_get_workflow_logs_unauthorized( query_string={"user": random_user_uuid}, content_type="application/json", ) - assert res.status_code == 500 + assert res.status_code == 404 def test_start_input_parameters( @@ -1785,7 +1785,7 @@ def test_get_workflow_retention_rules_invalid_user(app, sample_serial_workflow_i ), query_string={"user": uuid.uuid4()}, ) - assert res.status_code == 500 + assert res.status_code == 404 def test_share_workflow( @@ -1937,7 +1937,7 @@ def test_share_workflow_with_invalid_date_format( workflow = sample_serial_workflow_in_db_owned_by_user1 share_details = { "user_email_to_share_with": user2.email, - "valid_until": "2023/12/31", # Invalid format + "valid_until": "invalid date", # Invalid format } with app.test_client() as client: res = client.post( @@ -1955,7 +1955,7 @@ def test_share_workflow_with_invalid_date_format( response_data = res.get_json() assert ( response_data["message"] - == "Date format is not valid (Invalid isoformat string: '2023/12/31'). Please use YYYY-MM-DD format." + == "Field 'valid_until': Date format is not valid. Please use YYYY-MM-DD format." ) @@ -2108,7 +2108,7 @@ def test_share_workflow_with_long_message( response_data = res.get_json() assert ( response_data["message"] - == "Message is too long. Please keep it under 5000 characters." + == "Field 'message': Message is too long. Please keep it under 5000 characters." ) @@ -2185,7 +2185,7 @@ def test_unshare_workflow( workflow_id_or_name=str(workflow.id_), ), query_string={ - "user_id": str(user1.id_), + "user": str(user1.id_), "user_email_to_unshare_with": user2.email, }, ) @@ -2209,7 +2209,7 @@ def test_unshare_workflow_not_shared( workflow_id_or_name=str(workflow.id_), ), query_string={ - "user_id": str(user1.id_), + "user": str(user1.id_), "user_email_to_unshare_with": user2.email, }, ) @@ -2233,7 +2233,7 @@ def test_unshare_workflow_with_self( workflow_id_or_name=str(workflow.id_), ), query_string={ - "user_id": str(user1.id_), + "user": str(user1.id_), "user_email_to_unshare_with": user1.email, }, ) @@ -2268,15 +2268,15 @@ def test_unshare_workflow_with_invalid_email( workflow_id_or_name=str(workflow.id_), ), query_string={ - "user_id": str(user1.id_), + "user": str(user1.id_), "user_email_to_unshare_with": invalid_email, }, ) - assert res.status_code == 400 + assert res.status_code == 404 response_data = res.get_json() assert ( response_data["message"] - == f"User email '{invalid_email}' is not valid." + == f"User with email '{invalid_email}' does not exist." ) @@ -2306,7 +2306,7 @@ def test_unshare_workflow_with_valid_email_but_unexisting_user( workflow_id_or_name=str(workflow.id_), ), query_string={ - "user_id": str(user1.id_), + "user": str(user1.id_), "user_email_to_unshare_with": valid_email, }, ) @@ -2328,7 +2328,7 @@ def test_unshare_non_existent_workflow(app, user1, user2): workflow_id_or_name=non_existent_workflow_id, ), query_string={ - "user_id": str(user1.id_), + "user": str(user1.id_), "user_email_to_unshare_with": user2.email, }, ) @@ -2353,7 +2353,7 @@ def test_unshare_workflow_already_unshared( workflow_id_or_name=str(workflow.id_), ), query_string={ - "user_id": str(user1.id_), + "user": str(user1.id_), "user_email_to_unshare_with": user2.email, }, ) @@ -2393,7 +2393,7 @@ def test_unshare_multiple_workflows( workflow_id_or_name=str(workflow1.id_), ), query_string={ - "user_id": str(user1.id_), + "user": str(user1.id_), "user_email_to_unshare_with": user2.email, }, ) @@ -2423,7 +2423,7 @@ def test_unshare_multiple_workflows( workflow_id_or_name=str(workflow2.id_), ), query_string={ - "user_id": str(user1.id_), + "user": str(user1.id_), "user_email_to_unshare_with": user2.email, }, ) @@ -2466,7 +2466,7 @@ def test_unshare_workflow_with_message_and_valid_until( workflow_id_or_name=str(workflow.id_), ), query_string={ - "user_id": str(user1.id_), + "user": str(user1.id_), "user_email_to_unshare_with": user2.email, }, ) @@ -2501,7 +2501,7 @@ def test_get_workflow_share_status( workflow_id_or_name=str(workflow.id_), ), query_string={ - "user_id": str(user1.id_), + "user": str(user1.id_), "user_email_to_check": user2.email, }, ) @@ -2523,7 +2523,7 @@ def test_get_workflow_share_status_not_shared( workflow_id_or_name=str(workflow.id_), ), query_string={ - "user_id": str(user1.id_), + "user": str(user1.id_), }, ) assert res.status_code == 200 @@ -2553,7 +2553,7 @@ def test_get_workflow_share_status_valid_until_not_set( workflow_id_or_name=str(workflow.id_), ), query_string={ - "user_id": str(user1.id_), + "user": str(user1.id_), }, ) assert res.status_code == 200 @@ -2590,7 +2590,7 @@ def test_get_workflow_share_status_valid_until_set( workflow_id_or_name=str(workflow.id_), ), query_string={ - "user_id": str(user1.id_), + "user": str(user1.id_), }, ) assert res.status_code == 200