File download from workflow data (#122)
This commit is contained in:
parent
a815863727
commit
4240946334
|
@ -1605,6 +1605,45 @@ paths:
|
||||||
schema:
|
schema:
|
||||||
$ref: "#/components/schemas/Workflow"
|
$ref: "#/components/schemas/Workflow"
|
||||||
|
|
||||||
|
/process-data-file-download/{modified_process_model_identifier}/{process_instance_id}/{process_data_identifier}:
|
||||||
|
parameters:
|
||||||
|
- name: modified_process_model_identifier
|
||||||
|
in: path
|
||||||
|
required: true
|
||||||
|
description: The modified id of an existing process model
|
||||||
|
schema:
|
||||||
|
type: string
|
||||||
|
- name: process_instance_id
|
||||||
|
in: path
|
||||||
|
required: true
|
||||||
|
description: The unique id of an existing process instance.
|
||||||
|
schema:
|
||||||
|
type: integer
|
||||||
|
- name: process_data_identifier
|
||||||
|
in: path
|
||||||
|
required: true
|
||||||
|
description: The identifier of the process data.
|
||||||
|
schema:
|
||||||
|
type: string
|
||||||
|
- name: index
|
||||||
|
in: query
|
||||||
|
required: false
|
||||||
|
description: The optional index of the value if key's value is an array
|
||||||
|
schema:
|
||||||
|
type: integer
|
||||||
|
get:
|
||||||
|
operationId: spiffworkflow_backend.routes.process_api_blueprint.process_data_file_download
|
||||||
|
summary: Download the file referneced in the process data value.
|
||||||
|
tags:
|
||||||
|
- Data Objects
|
||||||
|
responses:
|
||||||
|
"200":
|
||||||
|
description: Fetch succeeded.
|
||||||
|
content:
|
||||||
|
application/json:
|
||||||
|
schema:
|
||||||
|
$ref: "#/components/schemas/Workflow"
|
||||||
|
|
||||||
/send-event/{modified_process_model_identifier}/{process_instance_id}:
|
/send-event/{modified_process_model_identifier}/{process_instance_id}:
|
||||||
parameters:
|
parameters:
|
||||||
- name: modified_process_model_identifier
|
- name: modified_process_model_identifier
|
||||||
|
|
|
@ -1,7 +1,9 @@
|
||||||
"""APIs for dealing with process groups, process models, and process instances."""
|
"""APIs for dealing with process groups, process models, and process instances."""
|
||||||
|
import base64
|
||||||
import json
|
import json
|
||||||
from typing import Any
|
from typing import Any
|
||||||
from typing import Dict
|
from typing import Dict
|
||||||
|
from typing import Optional
|
||||||
|
|
||||||
import flask.wrappers
|
import flask.wrappers
|
||||||
from flask import Blueprint
|
from flask import Blueprint
|
||||||
|
@ -81,10 +83,12 @@ def process_list() -> Any:
|
||||||
return SpecReferenceSchema(many=True).dump(references)
|
return SpecReferenceSchema(many=True).dump(references)
|
||||||
|
|
||||||
|
|
||||||
def process_data_show(
|
def _process_data_fetcher(
|
||||||
process_instance_id: int,
|
process_instance_id: int,
|
||||||
process_data_identifier: str,
|
process_data_identifier: str,
|
||||||
modified_process_model_identifier: str,
|
modified_process_model_identifier: str,
|
||||||
|
download_file_data: bool,
|
||||||
|
index: Optional[int] = None,
|
||||||
) -> flask.wrappers.Response:
|
) -> flask.wrappers.Response:
|
||||||
"""Process_data_show."""
|
"""Process_data_show."""
|
||||||
process_instance = _find_process_instance_by_id_or_raise(process_instance_id)
|
process_instance = _find_process_instance_by_id_or_raise(process_instance_id)
|
||||||
|
@ -94,6 +98,26 @@ def process_data_show(
|
||||||
if process_data_identifier in all_process_data:
|
if process_data_identifier in all_process_data:
|
||||||
process_data_value = all_process_data[process_data_identifier]
|
process_data_value = all_process_data[process_data_identifier]
|
||||||
|
|
||||||
|
if process_data_value is not None and index is not None:
|
||||||
|
process_data_value = process_data_value[index]
|
||||||
|
|
||||||
|
if (
|
||||||
|
download_file_data
|
||||||
|
and isinstance(process_data_value, str)
|
||||||
|
and process_data_value.startswith("data:")
|
||||||
|
):
|
||||||
|
parts = process_data_value.split(";")
|
||||||
|
mimetype = parts[0][4:]
|
||||||
|
filename = parts[1]
|
||||||
|
base64_value = parts[2].split(",")[1]
|
||||||
|
file_contents = base64.b64decode(base64_value)
|
||||||
|
|
||||||
|
return Response(
|
||||||
|
file_contents,
|
||||||
|
mimetype=mimetype,
|
||||||
|
headers={"Content-disposition": f"attachment; filename={filename}"},
|
||||||
|
)
|
||||||
|
|
||||||
return make_response(
|
return make_response(
|
||||||
jsonify(
|
jsonify(
|
||||||
{
|
{
|
||||||
|
@ -105,6 +129,37 @@ def process_data_show(
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def process_data_show(
|
||||||
|
process_instance_id: int,
|
||||||
|
process_data_identifier: str,
|
||||||
|
modified_process_model_identifier: str,
|
||||||
|
) -> flask.wrappers.Response:
|
||||||
|
"""Process_data_show."""
|
||||||
|
return _process_data_fetcher(
|
||||||
|
process_instance_id,
|
||||||
|
process_data_identifier,
|
||||||
|
modified_process_model_identifier,
|
||||||
|
False,
|
||||||
|
None,
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def process_data_file_download(
|
||||||
|
process_instance_id: int,
|
||||||
|
process_data_identifier: str,
|
||||||
|
modified_process_model_identifier: str,
|
||||||
|
index: Optional[int] = None,
|
||||||
|
) -> flask.wrappers.Response:
|
||||||
|
"""Process_data_file_download."""
|
||||||
|
return _process_data_fetcher(
|
||||||
|
process_instance_id,
|
||||||
|
process_data_identifier,
|
||||||
|
modified_process_model_identifier,
|
||||||
|
True,
|
||||||
|
index,
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
# sample body:
|
# sample body:
|
||||||
# {"ref": "refs/heads/main", "repository": {"name": "sample-process-models",
|
# {"ref": "refs/heads/main", "repository": {"name": "sample-process-models",
|
||||||
# "full_name": "sartography/sample-process-models", "private": False .... }}
|
# "full_name": "sartography/sample-process-models", "private": False .... }}
|
||||||
|
|
|
@ -17,6 +17,7 @@ from flask import request
|
||||||
from werkzeug.wrappers import Response
|
from werkzeug.wrappers import Response
|
||||||
|
|
||||||
from spiffworkflow_backend.exceptions.api_error import ApiError
|
from spiffworkflow_backend.exceptions.api_error import ApiError
|
||||||
|
from spiffworkflow_backend.helpers.api_version import V1_API_PATH_PREFIX
|
||||||
from spiffworkflow_backend.models.user import UserModel
|
from spiffworkflow_backend.models.user import UserModel
|
||||||
from spiffworkflow_backend.services.authentication_service import AuthenticationService
|
from spiffworkflow_backend.services.authentication_service import AuthenticationService
|
||||||
from spiffworkflow_backend.services.authentication_service import (
|
from spiffworkflow_backend.services.authentication_service import (
|
||||||
|
@ -58,6 +59,10 @@ def verify_token(
|
||||||
if not token and "Authorization" in request.headers:
|
if not token and "Authorization" in request.headers:
|
||||||
token = request.headers["Authorization"].removeprefix("Bearer ")
|
token = request.headers["Authorization"].removeprefix("Bearer ")
|
||||||
|
|
||||||
|
if not token and "access_token" in request.cookies:
|
||||||
|
if request.path.startswith(f"{V1_API_PATH_PREFIX}/process-data-file-download/"):
|
||||||
|
token = request.cookies["access_token"]
|
||||||
|
|
||||||
# This should never be set here but just in case
|
# This should never be set here but just in case
|
||||||
_clear_auth_tokens_from_thread_local_data()
|
_clear_auth_tokens_from_thread_local_data()
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,51 @@
|
||||||
|
"""Markdown_file_download_link."""
|
||||||
|
from typing import Any
|
||||||
|
from urllib.parse import unquote
|
||||||
|
|
||||||
|
from flask import current_app
|
||||||
|
|
||||||
|
from spiffworkflow_backend.models.process_model import ProcessModelInfo
|
||||||
|
from spiffworkflow_backend.models.script_attributes_context import (
|
||||||
|
ScriptAttributesContext,
|
||||||
|
)
|
||||||
|
from spiffworkflow_backend.scripts.script import Script
|
||||||
|
|
||||||
|
|
||||||
|
class GetMarkdownFileDownloadLink(Script):
|
||||||
|
"""GetMarkdownFileDownloadLink."""
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def requires_privileged_permissions() -> bool:
|
||||||
|
"""We have deemed this function safe to run without elevated permissions."""
|
||||||
|
return False
|
||||||
|
|
||||||
|
def get_description(self) -> str:
|
||||||
|
"""Get_description."""
|
||||||
|
return """Returns a string which is a string in markdown format."""
|
||||||
|
|
||||||
|
def run(
|
||||||
|
self,
|
||||||
|
script_attributes_context: ScriptAttributesContext,
|
||||||
|
*_args: Any,
|
||||||
|
**kwargs: Any,
|
||||||
|
) -> Any:
|
||||||
|
"""Run."""
|
||||||
|
# example input:
|
||||||
|
# "data:application/pdf;name=Harmeet_1234.pdf;base64,JV...."
|
||||||
|
process_data_identifier = kwargs["key"]
|
||||||
|
parts = kwargs["file_data"].split(";")
|
||||||
|
file_index = kwargs["file_index"]
|
||||||
|
label = unquote(parts[1].split("=")[1])
|
||||||
|
process_model_identifier = script_attributes_context.process_model_identifier
|
||||||
|
modified_process_model_identifier = (
|
||||||
|
ProcessModelInfo.modify_process_identifier_for_path_param(
|
||||||
|
process_model_identifier
|
||||||
|
)
|
||||||
|
)
|
||||||
|
process_instance_id = script_attributes_context.process_instance_id
|
||||||
|
url = current_app.config["SPIFFWORKFLOW_BACKEND_URL"]
|
||||||
|
url += f"/v1.0/process-data-file-download/{modified_process_model_identifier}/"
|
||||||
|
f"{process_instance_id}/{process_data_identifier}?index={file_index}"
|
||||||
|
link = f"[{label}]({url})"
|
||||||
|
|
||||||
|
return link
|
Loading…
Reference in New Issue