added some additional apis for the frontend

This commit is contained in:
jasquat 2022-06-06 09:05:14 -04:00
parent f00dff8450
commit 49524e6fcd
6 changed files with 142 additions and 71 deletions

View File

@ -38,6 +38,19 @@ paths:
application/json:
schema:
$ref: "#/components/schemas/WorkflowSpecCategory"
/process-models/{process_model_id}:
get:
operationId: spiffworkflow_backend.routes.process_api_blueprint.process_model_show
summary: Returns a single process model
tags:
- Workflow Specification
responses:
'200':
description: Workflow spec.
content:
application/json:
schema:
$ref: "#/components/schemas/WorkflowSpec"
/workflow-specification:
# get:
# operationId: crc.api.workflow.all_specifications
@ -77,7 +90,7 @@ paths:
schema:
$ref: "#/components/schemas/WorkflowSpec"
responses:
"200":
"201":
description: Workflow specification created successfully.
content:
application/json:
@ -120,7 +133,7 @@ paths:
type: string
format: binary
responses:
"200":
"201":
description: Metadata about the uploaded file, but not the file content.
content:
application/json:
@ -152,7 +165,7 @@ paths:
tags:
- Workflow Specifications
responses:
"200":
"201":
description: Workflow generated successfully
content:
application/json:
@ -187,7 +200,7 @@ paths:
# responses:
# '204':
# description: The workflow specification has been removed.
/workflow-specification/{spec_id}/file/{file_name}:
/process-models/{spec_id}/file/{file_name}:
parameters:
- name: spec_id
in: path

View File

@ -1,9 +1,11 @@
"""File."""
import enum
from dataclasses import dataclass, field
from flask_bpmn.models.db import db
from marshmallow import INCLUDE
from marshmallow import Schema
from typing import Optional
from sqlalchemy import func
from sqlalchemy.orm import deferred # type: ignore
from sqlalchemy.orm import relationship
@ -85,38 +87,28 @@ CONTENT_TYPES = {
}
@dataclass
class File:
"""File."""
def __init__(self):
"""__init__."""
self.content_type = None
self.name = None
self.content_type = None
self.process_instance_id = None
self.irb_doc_code = None
self.type = None
self.document = {}
self.last_modified = None
self.size = None
self.data_store = {}
self.user_uid = None
self.archived = None
content_type: str
name: str
type: str
document: dict
last_modified: str
size: int
process_instance_id: Optional[int] = None
irb_doc_code: Optional[str] = None
data_store: Optional[dict] = field(default_factory=dict)
user_uid: Optional[str] = None
file_contents: Optional[str] = None
archived: bool = False
@classmethod
def from_file_system(
cls, file_name, file_type, content_type, last_modified, file_size
):
"""From_file_system."""
instance = cls()
instance.name = file_name
instance.content_type = content_type
instance.type = file_type.value
instance.document = {}
instance.last_modified = last_modified
instance.size = file_size
# fixme: How to track the user id?
instance.data_store = {}
instance = cls(name=file_name, content_type=content_type, type=file_type.value, document={}, last_modified=last_modified, size=file_size)
return instance
@ -141,6 +133,7 @@ class FileSchema(Schema):
"document",
"user_uid",
"url",
"file_contents",
]
unknown = INCLUDE

View File

@ -2,6 +2,11 @@
import marshmallow
from marshmallow import post_load
from marshmallow import Schema
from dataclasses import dataclass
from typing import List
import json
from spiffworkflow_backend.models.file import File
class ProcessModelInfo:
@ -21,6 +26,7 @@ class ProcessModelInfo:
process_group_id="",
display_order=0,
is_review=False,
files=None,
):
"""__init__."""
self.id = id # Sting unique id
@ -37,9 +43,12 @@ class ProcessModelInfo:
if libraries is None:
libraries = []
self.libraries = libraries
if files is None:
files = []
self.files = files
def __eq__(self, other):
"""__eq__."""
if not isinstance(other, ProcessModelInfo):
@ -69,6 +78,9 @@ class ProcessModelInfoSchema(Schema):
is_review = marshmallow.fields.Boolean(allow_none=True)
process_group_id = marshmallow.fields.String(allow_none=True)
libraries = marshmallow.fields.List(marshmallow.fields.String(), allow_none=True)
files = marshmallow.fields.List(
marshmallow.fields.Nested("FileSchema")
)
@post_load
def make_spec(self, data, **kwargs):

View File

@ -1,6 +1,8 @@
"""APIs for dealing with process groups, process models, and process instances."""
import connexion
import json
from flask import Blueprint
from flask import Response
from flask import g
from flask_bpmn.api.api_error import ApiError
@ -9,6 +11,7 @@ from spiffworkflow_backend.models.file import FileType
from spiffworkflow_backend.models.process_instance import ProcessInstanceApiSchema
from spiffworkflow_backend.models.process_model import ProcessModelInfoSchema
from spiffworkflow_backend.models.process_group import ProcessGroupSchema
from spiffworkflow_backend.models.file import File
from spiffworkflow_backend.services.process_instance_processor import (
ProcessInstanceProcessor,
)
@ -35,14 +38,15 @@ def add_process_model(body):
size = len(workflows)
spec.display_order = size
process_model_service.add_spec(spec)
return ProcessModelInfoSchema().dump(spec)
return Response(
json.dumps(ProcessModelInfoSchema().dump(spec)), status=201, mimetype="application/json"
)
def get_file(spec_id, file_name):
"""Get_file."""
workflow_spec_service = ProcessModelService()
workflow_spec = workflow_spec_service.get_spec(spec_id)
files = SpecFileService.get_files(workflow_spec, file_name)
process_model = ProcessModelService().get_spec(spec_id)
files = SpecFileService.get_files(process_model, file_name)
if len(files) == 0:
raise ApiError(
code="unknown file",
@ -50,21 +54,27 @@ def get_file(spec_id, file_name):
f" it does not exist in workflow {spec_id}.",
status_code=404,
)
return FileSchema().dump(files[0])
file = files[0]
file_contents = SpecFileService.get_data(process_model, file.name)
file.file_contents = file_contents
return FileSchema().dump(file)
def add_file(spec_id):
"""Add_file."""
workflow_spec_service = ProcessModelService()
workflow_spec = workflow_spec_service.get_spec(spec_id)
process_model = workflow_spec_service.get_spec(spec_id)
request_file = connexion.request.files["file"]
file = SpecFileService.add_file(
workflow_spec, request_file.filename, request_file.stream.read()
process_model, request_file.filename, request_file.stream.read()
)
if not process_model.primary_process_id and file.type == FileType.bpmn.value:
SpecFileService.set_primary_bpmn(process_model, file.name)
workflow_spec_service.update_spec(process_model)
return Response(
json.dumps(FileSchema().dump(file)), status=201, mimetype="application/json"
)
if not workflow_spec.primary_process_id and file.type == FileType.bpmn.value:
SpecFileService.set_primary_bpmn(workflow_spec, file.name)
workflow_spec_service.update_spec(workflow_spec)
return FileSchema().dump(file)
def create_process_instance(spec_id):
@ -79,13 +89,14 @@ def create_process_instance(spec_id):
workflow_api_model = ProcessInstanceService.processor_to_process_instance_api(
processor
)
return ProcessInstanceApiSchema().dump(workflow_api_model)
return Response(
json.dumps(ProcessInstanceApiSchema().dump(workflow_api_model)), status=201, mimetype="application/json"
)
def process_groups_list():
"""Process_groups_list."""
process_model_service = ProcessModelService()
process_groups = process_model_service.get_process_groups()
process_groups = ProcessModelService().get_process_groups()
return ProcessGroupSchema(many=True).dump(process_groups)
@ -93,3 +104,21 @@ def process_group_show(process_group_id):
"""Process_group_show."""
process_group = ProcessModelService().get_process_group(process_group_id)
return ProcessGroupSchema().dump(process_group)
def process_model_show(process_model_id):
"""Process_model_show."""
process_model = ProcessModelService().get_spec(process_model_id)
if process_model is None:
raise (
ApiError(
code="process_mode_cannot_be_found",
message=f"Process model cannot be found: {process_model_id}",
status_code=400,
)
)
files = SpecFileService.get_files(process_model, extension_filter="bpmn")
process_model.files = files
process_model_json = ProcessModelInfoSchema().dump(process_model)
return process_model_json

View File

@ -37,7 +37,7 @@ class SpecFileService(FileSystemService):
files.extend(SpecFileService._get_files(lib_path, file_name))
if extension_filter != "":
files = filter(lambda file: file.name.endswith(extension_filter), files)
files = list(filter(lambda file: file.name.endswith(extension_filter), files))
return files

View File

@ -38,9 +38,9 @@ def test_add_new_process_model(app, client: FlaskClient, with_bpmn_file_cleanup)
# def test_get_process_model(self):
#
# load_test_spec('random_fact')
# rv = client.get('/v1.0/workflow-specification/random_fact', headers=logged_in_headers())
# assert_success(rv)
# json_data = json.loads(rv.get_data(as_text=True))
# response = client.get('/v1.0/workflow-specification/random_fact', headers=logged_in_headers())
# assert_success(response)
# json_data = json.loads(response.get_data(as_text=True))
# api_spec = WorkflowSpecInfoSchema().load(json_data)
#
# fs_spec = process_model_service.get_spec('random_fact')
@ -54,31 +54,31 @@ def test_get_workflow_from_workflow_spec(
"""Test_get_workflow_from_workflow_spec."""
user = find_or_create_user()
spec = load_test_spec(app, "hello_world")
rv = client.post(
response = client.post(
f"/v1.0/workflow-specification/{spec.id}", headers=logged_in_headers(user)
)
assert rv.status_code == 200
assert "hello_world" == rv.json["process_model_identifier"]
# assert('Task_GetName' == rv.json['next_task']['name'])
assert response.status_code == 201
assert "hello_world" == response.json["process_model_identifier"]
# assert('Task_GetName' == response.json['next_task']['name'])
def test_get_process_groups_when_none(app, client: FlaskClient, with_bpmn_file_cleanup):
user = find_or_create_user()
rv = client.get(
response = client.get(
"/v1.0/process-groups", headers=logged_in_headers(user)
)
assert rv.status_code == 200
assert rv.json == []
assert response.status_code == 200
assert response.json == []
def test_get_process_groups_when_there_are_some(app, client: FlaskClient, with_bpmn_file_cleanup):
user = find_or_create_user()
load_test_spec(app, "hello_world")
rv = client.get(
response = client.get(
"/v1.0/process-groups", headers=logged_in_headers(user)
)
assert rv.status_code == 200
assert len(rv.json) == 1
assert response.status_code == 200
assert len(response.json) == 1
def test_get_process_group_when_found(app, client: FlaskClient, with_bpmn_file_cleanup):
@ -86,12 +86,36 @@ def test_get_process_group_when_found(app, client: FlaskClient, with_bpmn_file_c
test_process_group_id = "group_id1"
process_model_dir_name = "hello_world"
load_test_spec(app, process_model_dir_name, process_group_id=test_process_group_id)
rv = client.get(
f"/v1.0/process-group/{test_process_group_id}", headers=logged_in_headers(user)
response = client.get(
f"/v1.0/process-groups/{test_process_group_id}", headers=logged_in_headers(user)
)
assert rv.status_code == 200
assert rv.json["id"] == test_process_group_id
assert rv.json["process_models"][0]["id"] == process_model_dir_name
assert response.status_code == 200
assert response.json["id"] == test_process_group_id
assert response.json["process_models"][0]["id"] == process_model_dir_name
def test_get_process_model_when_found(app, client: FlaskClient, with_bpmn_file_cleanup):
user = find_or_create_user()
test_process_group_id = "group_id1"
process_model_dir_name = "hello_world"
load_test_spec(app, process_model_dir_name, process_group_id=test_process_group_id)
response = client.get(
f"/v1.0/process-models/{process_model_dir_name}", headers=logged_in_headers(user)
)
assert response.status_code == 200
assert response.json["id"] == process_model_dir_name
assert len(response.json["files"]) == 1
assert response.json["files"][0]["name"] == "hello_world.bpmn"
def test_get_process_model_when_not_found(app, client: FlaskClient, with_bpmn_file_cleanup):
user = find_or_create_user()
process_model_dir_name = "THIS_NO_EXISTS"
response = client.get(
f"/v1.0/process-models/{process_model_dir_name}", headers=logged_in_headers(user)
)
assert response.status_code == 400
assert response.json["code"] == "process_mode_cannot_be_found"
def create_process_model(app, client: FlaskClient):
@ -117,13 +141,13 @@ def create_process_model(app, client: FlaskClient):
primary_file_name="",
)
user = find_or_create_user()
rv = client.post(
response = client.post(
"/v1.0/workflow-specification",
content_type="application/json",
data=json.dumps(ProcessModelInfoSchema().dump(spec)),
headers=logged_in_headers(user),
)
assert rv.status_code == 200
assert response.status_code == 201
fs_spec = process_model_service.get_spec("make_cookies")
assert spec.display_name == fs_spec.display_name
@ -136,7 +160,7 @@ def create_spec_file(app, client: FlaskClient):
spec = load_test_spec(app, "random_fact")
data = {"file": (io.BytesIO(b"abcdef"), "random_fact.svg")}
user = find_or_create_user()
rv = client.post(
response = client.post(
"/v1.0/workflow-specification/%s/file" % spec.id,
data=data,
follow_redirects=True,
@ -144,16 +168,16 @@ def create_spec_file(app, client: FlaskClient):
headers=logged_in_headers(user),
)
assert rv.status_code == 200
assert rv.get_data() is not None
file = json.loads(rv.get_data(as_text=True))
assert response.status_code == 201
assert response.get_data() is not None
file = json.loads(response.get_data(as_text=True))
assert FileType.svg.value == file["type"]
assert "image/svg+xml" == file["content_type"]
rv = client.get(
response = client.get(
f"/v1.0/workflow-specification/{spec.id}/file/random_fact.svg",
headers=logged_in_headers(user),
)
assert rv.status_code == 200
file2 = json.loads(rv.get_data(as_text=True))
assert response.status_code == 200
file2 = json.loads(response.get_data(as_text=True))
assert file == file2