Removing dependency on flask-bpmn and taking it out of SpiffArena

Slightly updating the APIError code for recent updates to SpiffWorkflow's error refactoring.
This commit is contained in:
Dan 2023-01-17 12:56:06 -05:00
parent 994620bc5f
commit 28a28e353b
77 changed files with 462 additions and 169 deletions

View File

@ -1,7 +1,7 @@
"""Grabs tickets from csv and makes process instances."""
import csv
from flask_bpmn.models.db import db
from spiffworkflow_backend.models.db import db
from spiffworkflow_backend import get_hacked_up_app_for_script
from spiffworkflow_backend.models.process_instance import ProcessInstanceModel

View File

@ -5,7 +5,7 @@ def main():
"""Use main to avoid global namespace."""
import csv
from flask_bpmn.models.db import db
from spiffworkflow_backend.models.db import db
from spiffworkflow_backend.models.process_instance import ProcessInstanceModel
from spiffworkflow_backend.models.user import UserModel

View File

@ -5,8 +5,9 @@ import shutil
import pytest
from flask.app import Flask
from flask.testing import FlaskClient
from flask_bpmn.models.db import db
from flask_bpmn.models.db import SpiffworkflowBaseDBModel
from spiffworkflow_backend.models.db import db
from spiffworkflow_backend.models.db import SpiffworkflowBaseDBModel
from tests.spiffworkflow_backend.helpers.base_test import BaseTest
from spiffworkflow_backend.models.human_task_user import HumanTaskUserModel

View File

@ -610,37 +610,6 @@ python-versions = "*"
bcrypt = ">=3.1.1"
Flask = "*"
[[package]]
name = "flask-bpmn"
version = "0.0.0"
description = "Flask Bpmn"
category = "main"
optional = false
python-versions = "^3.7"
develop = false
[package.dependencies]
click = "^8.0.1"
flask = "*"
flask-admin = "*"
flask-bcrypt = "*"
flask-cors = "*"
flask-mail = "*"
flask-marshmallow = "*"
flask-migrate = "*"
flask-restful = "*"
greenlet = "^2.0.1"
sentry-sdk = "*"
sphinx-autoapi = "^2.0.0"
spiffworkflow = "*"
werkzeug = "*"
[package.source]
type = "git"
url = "https://github.com/sartography/flask-bpmn"
reference = "main"
resolved_reference = "c18306300f4312b8d36e0197fd6b62399180d0b1"
[[package]]
name = "Flask-Cors"
version = "3.0.10"
@ -1776,7 +1745,7 @@ description = "A workflow framework and BPMN/DMN Processor"
category = "main"
optional = false
python-versions = "*"
develop = false
develop = true
[package.dependencies]
celery = "*"
@ -1784,10 +1753,8 @@ configparser = "*"
lxml = "*"
[package.source]
type = "git"
url = "https://github.com/sartography/SpiffWorkflow"
reference = "main"
resolved_reference = "be26100bcbef8026e26312c665dae42faf476485"
type = "directory"
url = "../../SpiffWorkflow"
[[package]]
name = "SQLAlchemy"
@ -2158,7 +2125,7 @@ testing = ["flake8 (<5)", "func-timeout", "jaraco.functools", "jaraco.itertools"
[metadata]
lock-version = "1.1"
python-versions = ">=3.9,<3.12"
content-hash = "8592e94ba80b7d0338a9c003ca4d0e189b5f470d97391438ddc1fc9050febedb"
content-hash = "f646836512a18f2a0e9ba4979926e004abd17790da5178e06e87092fdf23c35f"
[metadata.files]
alabaster = [
@ -2421,7 +2388,6 @@ Flask-Bcrypt = [
{file = "Flask-Bcrypt-1.0.1.tar.gz", hash = "sha256:f07b66b811417ea64eb188ae6455b0b708a793d966e1a80ceec4a23bc42a4369"},
{file = "Flask_Bcrypt-1.0.1-py3-none-any.whl", hash = "sha256:062fd991dc9118d05ac0583675507b9fe4670e44416c97e0e6819d03d01f808a"},
]
flask-bpmn = []
Flask-Cors = [
{file = "Flask-Cors-3.0.10.tar.gz", hash = "sha256:b60839393f3b84a0f3746f6cdca56c1ad7426aa738b70d6c61375857823181de"},
{file = "Flask_Cors-3.0.10-py2.py3-none-any.whl", hash = "sha256:74efc975af1194fc7891ff5cd85b0f7478be4f7f59fe158102e91abb72bb4438"},
@ -2499,6 +2465,7 @@ greenlet = [
{file = "greenlet-2.0.1-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d5b0ff9878333823226d270417f24f4d06f235cb3e54d1103b71ea537a6a86ce"},
{file = "greenlet-2.0.1-cp37-cp37m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:be9e0fb2ada7e5124f5282d6381903183ecc73ea019568d6d63d33f25b2a9000"},
{file = "greenlet-2.0.1-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0b493db84d124805865adc587532ebad30efa68f79ad68f11b336e0a51ec86c2"},
{file = "greenlet-2.0.1-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:0459d94f73265744fee4c2d5ec44c6f34aa8a31017e6e9de770f7bcf29710be9"},
{file = "greenlet-2.0.1-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:a20d33124935d27b80e6fdacbd34205732660e0a1d35d8b10b3328179a2b51a1"},
{file = "greenlet-2.0.1-cp37-cp37m-win32.whl", hash = "sha256:ea688d11707d30e212e0110a1aac7f7f3f542a259235d396f88be68b649e47d1"},
{file = "greenlet-2.0.1-cp37-cp37m-win_amd64.whl", hash = "sha256:afe07421c969e259e9403c3bb658968702bc3b78ec0b6fde3ae1e73440529c23"},
@ -2507,6 +2474,7 @@ greenlet = [
{file = "greenlet-2.0.1-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:659f167f419a4609bc0516fb18ea69ed39dbb25594934bd2dd4d0401660e8a1e"},
{file = "greenlet-2.0.1-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:356e4519d4dfa766d50ecc498544b44c0249b6de66426041d7f8b751de4d6b48"},
{file = "greenlet-2.0.1-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:811e1d37d60b47cb8126e0a929b58c046251f28117cb16fcd371eed61f66b764"},
{file = "greenlet-2.0.1-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:d38ffd0e81ba8ef347d2be0772e899c289b59ff150ebbbbe05dc61b1246eb4e0"},
{file = "greenlet-2.0.1-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:0109af1138afbfb8ae647e31a2b1ab030f58b21dd8528c27beaeb0093b7938a9"},
{file = "greenlet-2.0.1-cp38-cp38-win32.whl", hash = "sha256:88c8d517e78acdf7df8a2134a3c4b964415b575d2840a2746ddb1cc6175f8608"},
{file = "greenlet-2.0.1-cp38-cp38-win_amd64.whl", hash = "sha256:d6ee1aa7ab36475035eb48c01efae87d37936a8173fc4d7b10bb02c2d75dd8f6"},
@ -2515,6 +2483,7 @@ greenlet = [
{file = "greenlet-2.0.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:505138d4fa69462447a562a7c2ef723c6025ba12ac04478bc1ce2fcc279a2db5"},
{file = "greenlet-2.0.1-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:cce1e90dd302f45716a7715517c6aa0468af0bf38e814ad4eab58e88fc09f7f7"},
{file = "greenlet-2.0.1-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9e9744c657d896c7b580455e739899e492a4a452e2dd4d2b3e459f6b244a638d"},
{file = "greenlet-2.0.1-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:662e8f7cad915ba75d8017b3e601afc01ef20deeeabf281bd00369de196d7726"},
{file = "greenlet-2.0.1-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:41b825d65f31e394b523c84db84f9383a2f7eefc13d987f308f4663794d2687e"},
{file = "greenlet-2.0.1-cp39-cp39-win32.whl", hash = "sha256:db38f80540083ea33bdab614a9d28bcec4b54daa5aff1668d7827a9fc769ae0a"},
{file = "greenlet-2.0.1-cp39-cp39-win_amd64.whl", hash = "sha256:b23d2a46d53210b498e5b701a1913697671988f4bf8e10f935433f6e7c332fb6"},
@ -2813,10 +2782,7 @@ orjson = [
{file = "orjson-3.8.0-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:b68a42a31f8429728183c21fb440c21de1b62e5378d0d73f280e2d894ef8942e"},
{file = "orjson-3.8.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:ff13410ddbdda5d4197a4a4c09969cb78c722a67550f0a63c02c07aadc624833"},
{file = "orjson-3.8.0-cp310-none-win_amd64.whl", hash = "sha256:2d81e6e56bbea44be0222fb53f7b255b4e7426290516771592738ca01dbd053b"},
{file = "orjson-3.8.0-cp311-cp311-macosx_10_7_x86_64.whl", hash = "sha256:200eae21c33f1f8b02a11f5d88d76950cd6fd986d88f1afe497a8ae2627c49aa"},
{file = "orjson-3.8.0-cp311-cp311-macosx_10_9_x86_64.macosx_11_0_arm64.macosx_10_9_universal2.whl", hash = "sha256:9529990f3eab54b976d327360aa1ff244a4b12cb5e4c5b3712fcdd96e8fe56d4"},
{file = "orjson-3.8.0-cp311-cp311-manylinux_2_28_x86_64.whl", hash = "sha256:e2defd9527651ad39ec20ae03c812adf47ef7662bdd6bc07dabb10888d70dc62"},
{file = "orjson-3.8.0-cp311-none-win_amd64.whl", hash = "sha256:b21c7af0ff6228ca7105f54f0800636eb49201133e15ddb80ac20c1ce973ef07"},
{file = "orjson-3.8.0-cp37-cp37m-macosx_10_7_x86_64.whl", hash = "sha256:9e6ac22cec72d5b39035b566e4b86c74b84866f12b5b0b6541506a080fb67d6d"},
{file = "orjson-3.8.0-cp37-cp37m-macosx_10_9_x86_64.macosx_11_0_arm64.macosx_10_9_universal2.whl", hash = "sha256:e2f4a5542f50e3d336a18cb224fc757245ca66b1fd0b70b5dd4471b8ff5f2b0e"},
{file = "orjson-3.8.0-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e1418feeb8b698b9224b1f024555895169d481604d5d884498c1838d7412794c"},

View File

@ -27,12 +27,10 @@ flask-marshmallow = "*"
flask-migrate = "*"
flask-restful = "*"
werkzeug = "*"
SpiffWorkflow = {git = "https://github.com/sartography/SpiffWorkflow", rev = "main"}
#SpiffWorkflow = {develop = true, path = "../SpiffWorkflow" }
#SpiffWorkflow = {git = "https://github.com/sartography/SpiffWorkflow", rev = "main"}
SpiffWorkflow = {develop = true, path = "/home/dan/code/workflow/SpiffWorkflow" }
sentry-sdk = "^1.10"
sphinx-autoapi = "^2.0"
flask-bpmn = {git = "https://github.com/sartography/flask-bpmn", rev = "main"}
# flask-bpmn = {develop = true, path = "../flask-bpmn"}
mysql-connector-python = "*"
pytest-flask = "^1.2.0"
pytest-flask-sqlalchemy = "^1.1.0"

View File

@ -9,9 +9,9 @@ import sqlalchemy
from apscheduler.schedulers.background import BackgroundScheduler # type: ignore
from apscheduler.schedulers.base import BaseScheduler # type: ignore
from flask.json.provider import DefaultJSONProvider
from flask_bpmn.api.api_error import api_error_blueprint
from flask_bpmn.models.db import db
from flask_bpmn.models.db import migrate
from spiffworkflow_backend.exceptions.api_error import api_error_blueprint
from spiffworkflow_backend.models.db import db
from spiffworkflow_backend.models.db import migrate
from flask_cors import CORS # type: ignore
from flask_mail import Mail # type: ignore
from werkzeug.exceptions import NotFound

View File

@ -16,12 +16,19 @@ users:
email: malala@spiffworkflow.org
password: malala
preferred_username: Malala
oskar:
service: local_open_id
email: oskar@spiffworkflow.org
password: oskar
preferred_username: Oskar
groups:
admin:
users:
[
admin@spiffworkflow.org,
oskar@spiffworkflow.org
]
Education:
users:
@ -88,3 +95,11 @@ permissions:
users: []
allowed_permissions: [create, read]
uri: /process-instances/misc:category_number_one:process-model-with-form/*
# Anyone can see their own user groups.
groups-everybody:
groups: [everybody]
users: []
allowed_permissions: [create, read]
uri: /v1.0/user-groups/for-current-user

View File

@ -0,0 +1,206 @@
"""API Error functionality."""
from __future__ import annotations
import json
from dataclasses import dataclass
from dataclasses import field
from typing import Any
import flask.wrappers
import sentry_sdk
from flask import Blueprint
from flask import current_app
from flask import g
from flask import jsonify
from flask import make_response
from sentry_sdk import capture_exception
from sentry_sdk import set_tag
from SpiffWorkflow.exceptions import WorkflowException, WorkflowTaskException # type: ignore
from SpiffWorkflow.specs.base import TaskSpec # type: ignore
from SpiffWorkflow.task import Task # type: ignore
api_error_blueprint = Blueprint("api_error_blueprint", __name__)
@dataclass
class ApiError(Exception):
"""ApiError Class to help handle exceptions."""
error_code: str
message: str
error_line: str = ""
error_type: str = ""
file_name: str = ""
line_number: int = 0
offset: int = 0
sentry_link: str | None = None
status_code: int = 400
tag: str = ""
task_data: dict | str | None = field(default_factory=dict)
task_id: str = ""
task_name: str = ""
task_trace: dict | None = field(default_factory=dict)
def __str__(self) -> str:
"""Instructions to print instance as a string."""
msg = "ApiError: % s. " % self.message
if self.task_name:
msg += f"Error in task '{self.task_name}' ({self.task_id}). "
if self.line_number:
msg += "Error is on line %i. " % self.line_number
if self.file_name:
msg += "In file %s. " % self.file_name
return msg
@classmethod
def from_task(
cls,
error_code: str,
message: str,
task: Task,
status_code: int = 400,
line_number: int = 0,
offset: int = 0,
error_type: str = "",
error_line: str = "",
task_trace: dict | None = None,
) -> ApiError:
"""Constructs an API Error with details pulled from the current task."""
instance = cls(error_code, message, status_code=status_code)
instance.task_id = task.task_spec.name or ""
instance.task_name = task.task_spec.description or ""
instance.file_name = task.workflow.spec.file or ""
instance.line_number = line_number
instance.offset = offset
instance.error_type = error_type
instance.error_line = error_line
if task_trace:
instance.task_trace = task_trace
else:
instance.task_trace = WorkflowTaskException.get_task_trace(task)
# spiffworkflow is doing something weird where task ends up referenced in the data in some cases.
if "task" in task.data:
task.data.pop("task")
# Assure that there is nothing in the json data that can't be serialized.
instance.task_data = ApiError.remove_unserializeable_from_dict(task.data)
return instance
@staticmethod
def remove_unserializeable_from_dict(my_dict: dict) -> dict:
"""Removes unserializeable from dict."""
keys_to_delete = []
for key, value in my_dict.items():
if not ApiError.is_jsonable(value):
keys_to_delete.append(key)
for key in keys_to_delete:
del my_dict[key]
return my_dict
@staticmethod
def is_jsonable(x: Any) -> bool:
"""Attempts a json.dump on given input and returns false if it cannot."""
try:
json.dumps(x)
return True
except (TypeError, OverflowError, ValueError):
return False
@classmethod
def from_task_spec(
cls,
code: str,
message: str,
task_spec: TaskSpec,
status_code: int = 400,
) -> ApiError:
"""Constructs an API Error with details pulled from the current task."""
instance = cls(code, message, status_code=status_code)
instance.task_id = task_spec.name or ""
instance.task_name = task_spec.description or ""
if task_spec._wf_spec:
instance.file_name = task_spec._wf_spec.file
return instance
@classmethod
def from_workflow_exception(
cls,
error_code: str,
message: str,
exp: WorkflowException,
) -> ApiError:
"""Deals with workflow exceptions.
We catch a lot of workflow exception errors,
so consolidating the error_code, and doing the best things
we can with the data we have.
"""
if isinstance(exp, WorkflowTaskException):
return ApiError.from_task(
error_code,
message,
exp.task,
line_number=exp.line_number,
offset=exp.offset,
error_type=exp.error_type,
error_line=exp.error_line,
task_trace=exp.task_trace,
)
else:
return ApiError.from_task_spec(error_code, message, exp.sender)
def set_user_sentry_context() -> None:
"""Set_user_sentry_context."""
try:
username = g.user.username
except Exception:
username = "Unknown"
# This is for sentry logging into Slack
sentry_sdk.set_context("User", {"user": username})
set_tag("username", username)
@api_error_blueprint.app_errorhandler(Exception)
def handle_exception(exception: Exception) -> flask.wrappers.Response:
"""Handles unexpected exceptions."""
set_user_sentry_context()
sentry_link = None
if not isinstance(exception, ApiError) or exception.error_code != "invalid_token":
id = capture_exception(exception)
if isinstance(exception, ApiError):
current_app.logger.info(
f"Sending ApiError exception to sentry: {exception} with error code {exception.error_code}")
organization_slug = current_app.config.get("SENTRY_ORGANIZATION_SLUG")
project_slug = current_app.config.get("SENTRY_PROJECT_SLUG")
if organization_slug and project_slug:
sentry_link = (
f"https://sentry.io/{organization_slug}/{project_slug}/events/{id}"
)
# !!!NOTE!!!: do this after sentry stuff since calling logger.exception
# seems to break the sentry sdk context where we no longer get back
# an event id or send out tags like username
current_app.logger.exception(exception)
# set api_exception like this to avoid confusing mypy
# and what type the object is
api_exception = None
if isinstance(exception, ApiError):
api_exception = exception
else:
api_exception = ApiError(
error_code="internal_server_error",
message=f"{exception.__class__.__name__}",
sentry_link=sentry_link,
status_code=500,
)
return make_response(jsonify(api_exception), api_exception.status_code)

View File

@ -2,7 +2,7 @@
import time
import sqlalchemy
from flask_bpmn.models.db import db
from spiffworkflow_backend.models.db import db
def try_to_connect(start_time: float) -> None:

View File

@ -8,7 +8,7 @@ avoid circular imports
"""
from flask_bpmn.models.db import add_listeners
from spiffworkflow_backend.models.db import add_listeners
# must load these before UserModel and GroupModel for relationships
from spiffworkflow_backend.models.user_group_assignment import (

View File

@ -0,0 +1,85 @@
"""Db."""
from __future__ import annotations
import enum
import time
from typing import Any
from flask_migrate import Migrate # type: ignore
from flask_sqlalchemy import SQLAlchemy # type: ignore
from sqlalchemy import event # type: ignore
from sqlalchemy.engine.base import Connection # type: ignore
from sqlalchemy.orm.mapper import Mapper # type: ignore
db = SQLAlchemy()
migrate = Migrate()
class SpiffworkflowBaseDBModel(db.Model): # type: ignore
"""SpiffworkflowBaseDBModel."""
__abstract__ = True
@classmethod
def _all_subclasses(cls) -> list[type[SpiffworkflowBaseDBModel]]:
"""Get all subclasses of cls, descending.
So, if A is a subclass of B is a subclass of cls, this
will include A and B.
(Does not include cls)
"""
children = cls.__subclasses__()
result = []
while children:
next = children.pop()
subclasses = next.__subclasses__()
result.append(next)
# check subclasses of subclasses SpiffworkflowBaseDBModel. i guess we only go down to grandchildren, which seems cool.
for subclass in subclasses:
children.append(subclass)
return result
def validate_enum_field(
self, key: str, value: Any, enum_variable: enum.EnumMeta
) -> Any:
"""Validate_enum_field."""
try:
m_type = getattr(enum_variable, value, None)
except Exception as e:
raise ValueError(
f"{self.__class__.__name__}: invalid {key}: {value}"
) from e
if m_type is None:
raise ValueError(f"{self.__class__.__name__}: invalid {key}: {value}")
return m_type.value
def update_created_modified_on_create_listener(
mapper: Mapper, _connection: Connection, target: SpiffworkflowBaseDBModel
) -> None:
"""Event listener that runs before a record is updated, and sets the create/modified field accordingly."""
if "created_at_in_seconds" in mapper.columns.keys():
target.created_at_in_seconds = round(time.time())
if "updated_at_in_seconds" in mapper.columns.keys():
target.updated_at_in_seconds = round(time.time())
def update_modified_on_update_listener(
mapper: Mapper, _connection: Connection, target: SpiffworkflowBaseDBModel
) -> None:
"""Event listener that runs before a record is updated, and sets the modified field accordingly."""
if "updated_at_in_seconds" in mapper.columns.keys():
if db.session.is_modified(target, include_collections=False):
target.updated_at_in_seconds = round(time.time())
def add_listeners() -> None:
"""Adds the listeners to all subclasses.
This should be called after importing all subclasses
"""
for cls in SpiffworkflowBaseDBModel._all_subclasses():
event.listen(cls, "before_insert", update_created_modified_on_create_listener)
event.listen(cls, "before_update", update_modified_on_update_listener)

View File

@ -3,8 +3,7 @@ from __future__ import annotations
from typing import TYPE_CHECKING
from flask_bpmn.models.db import db
from flask_bpmn.models.group import FlaskBpmnGroupModel
from spiffworkflow_backend.models.db import db, SpiffworkflowBaseDBModel
from sqlalchemy.orm import relationship
if TYPE_CHECKING:
@ -18,12 +17,14 @@ class GroupNotFoundError(Exception):
"""GroupNotFoundError."""
class GroupModel(FlaskBpmnGroupModel):
class GroupModel(SpiffworkflowBaseDBModel):
"""GroupModel."""
__tablename__ = "group"
__table_args__ = {"extend_existing": True}
id = db.Column(db.Integer, primary_key=True)
name = db.Column(db.String(255))
identifier = db.Column(db.String(255))
user_group_assignments = relationship("UserGroupAssignmentModel", cascade="delete")

View File

@ -4,8 +4,9 @@ from __future__ import annotations
from dataclasses import dataclass
from typing import TYPE_CHECKING
from flask_bpmn.models.db import db
from flask_bpmn.models.db import SpiffworkflowBaseDBModel
from spiffworkflow_backend.models.db import db
from spiffworkflow_backend.models.db import SpiffworkflowBaseDBModel
from sqlalchemy import ForeignKey
from sqlalchemy.orm import relationship

View File

@ -3,8 +3,9 @@ from __future__ import annotations
from dataclasses import dataclass
from flask_bpmn.models.db import db
from flask_bpmn.models.db import SpiffworkflowBaseDBModel
from spiffworkflow_backend.models.db import db
from spiffworkflow_backend.models.db import SpiffworkflowBaseDBModel
from sqlalchemy import ForeignKey
from spiffworkflow_backend.models.human_task import HumanTaskModel

View File

@ -2,8 +2,9 @@
from dataclasses import dataclass
from typing import TYPE_CHECKING
from flask_bpmn.models.db import db
from flask_bpmn.models.db import SpiffworkflowBaseDBModel
from spiffworkflow_backend.models.db import db
from spiffworkflow_backend.models.db import SpiffworkflowBaseDBModel
from sqlalchemy import ForeignKey
from sqlalchemy.orm import relationship

View File

@ -1,8 +1,9 @@
"""Message_correlation_message_instance."""
from dataclasses import dataclass
from flask_bpmn.models.db import db
from flask_bpmn.models.db import SpiffworkflowBaseDBModel
from spiffworkflow_backend.models.db import db
from spiffworkflow_backend.models.db import SpiffworkflowBaseDBModel
from sqlalchemy import ForeignKey
from spiffworkflow_backend.models.message_correlation import MessageCorrelationModel

View File

@ -1,6 +1,7 @@
"""Message_correlation_property."""
from flask_bpmn.models.db import db
from flask_bpmn.models.db import SpiffworkflowBaseDBModel
from spiffworkflow_backend.models.db import db
from spiffworkflow_backend.models.db import SpiffworkflowBaseDBModel
from sqlalchemy import ForeignKey
from spiffworkflow_backend.models.message_model import MessageModel

View File

@ -5,8 +5,9 @@ from typing import Any
from typing import Optional
from typing import TYPE_CHECKING
from flask_bpmn.models.db import db
from flask_bpmn.models.db import SpiffworkflowBaseDBModel
from spiffworkflow_backend.models.db import db
from spiffworkflow_backend.models.db import SpiffworkflowBaseDBModel
from sqlalchemy import ForeignKey
from sqlalchemy.event import listens_for
from sqlalchemy.orm import relationship

View File

@ -1,6 +1,7 @@
"""Message_model."""
from flask_bpmn.models.db import db
from flask_bpmn.models.db import SpiffworkflowBaseDBModel
from spiffworkflow_backend.models.db import db
from spiffworkflow_backend.models.db import SpiffworkflowBaseDBModel
class MessageModel(SpiffworkflowBaseDBModel):

View File

@ -1,6 +1,7 @@
"""Message_correlation_property."""
from flask_bpmn.models.db import db
from flask_bpmn.models.db import SpiffworkflowBaseDBModel
from spiffworkflow_backend.models.db import db
from spiffworkflow_backend.models.db import SpiffworkflowBaseDBModel
from sqlalchemy import ForeignKey
from spiffworkflow_backend.models.message_model import MessageModel

View File

@ -2,8 +2,9 @@
import enum
from typing import Any
from flask_bpmn.models.db import db
from flask_bpmn.models.db import SpiffworkflowBaseDBModel
from spiffworkflow_backend.models.db import db
from spiffworkflow_backend.models.db import SpiffworkflowBaseDBModel
from sqlalchemy import ForeignKey
from sqlalchemy.orm import validates

View File

@ -3,8 +3,9 @@ import re
from dataclasses import dataclass
from typing import Optional
from flask_bpmn.models.db import db
from flask_bpmn.models.db import SpiffworkflowBaseDBModel
from spiffworkflow_backend.models.db import db
from spiffworkflow_backend.models.db import SpiffworkflowBaseDBModel
from sqlalchemy.orm import validates

View File

@ -1,8 +1,9 @@
"""Principal."""
from dataclasses import dataclass
from flask_bpmn.models.db import db
from flask_bpmn.models.db import SpiffworkflowBaseDBModel
from spiffworkflow_backend.models.db import db
from spiffworkflow_backend.models.db import SpiffworkflowBaseDBModel
from sqlalchemy import ForeignKey
from sqlalchemy.orm import relationship
from sqlalchemy.schema import CheckConstraint

View File

@ -5,8 +5,9 @@ from typing import Any
from typing import cast
import marshmallow
from flask_bpmn.models.db import db
from flask_bpmn.models.db import SpiffworkflowBaseDBModel
from spiffworkflow_backend.models.db import db
from spiffworkflow_backend.models.db import SpiffworkflowBaseDBModel
from marshmallow import INCLUDE
from marshmallow import Schema
from marshmallow_enum import EnumField # type: ignore

View File

@ -1,8 +1,9 @@
"""Process_instance_metadata."""
from dataclasses import dataclass
from flask_bpmn.models.db import db
from flask_bpmn.models.db import SpiffworkflowBaseDBModel
from spiffworkflow_backend.models.db import db
from spiffworkflow_backend.models.db import SpiffworkflowBaseDBModel
from sqlalchemy import ForeignKey
from spiffworkflow_backend.models.process_instance import ProcessInstanceModel

View File

@ -7,8 +7,9 @@ from typing import cast
from typing import Optional
from typing import TypedDict
from flask_bpmn.models.db import db
from flask_bpmn.models.db import SpiffworkflowBaseDBModel
from spiffworkflow_backend.models.db import db
from spiffworkflow_backend.models.db import SpiffworkflowBaseDBModel
from sqlalchemy import ForeignKey
from sqlalchemy.orm import deferred
from sqlalchemy.orm import relationship

View File

@ -1,8 +1,9 @@
"""Refresh_token."""
from dataclasses import dataclass
from flask_bpmn.models.db import db
from flask_bpmn.models.db import SpiffworkflowBaseDBModel
from spiffworkflow_backend.models.db import db
from spiffworkflow_backend.models.db import SpiffworkflowBaseDBModel
from sqlalchemy import ForeignKey
# from sqlalchemy.orm import relationship

View File

@ -1,8 +1,9 @@
"""Secret_model."""
from dataclasses import dataclass
from flask_bpmn.models.db import db
from flask_bpmn.models.db import SpiffworkflowBaseDBModel
from spiffworkflow_backend.models.db import db
from spiffworkflow_backend.models.db import SpiffworkflowBaseDBModel
from marshmallow import Schema
from sqlalchemy import ForeignKey

View File

@ -1,8 +1,9 @@
"""Message_model."""
from dataclasses import dataclass
from flask_bpmn.models.db import db
from flask_bpmn.models.db import SpiffworkflowBaseDBModel
from spiffworkflow_backend.models.db import db
from spiffworkflow_backend.models.db import SpiffworkflowBaseDBModel
from flask_marshmallow import Schema # type: ignore
from marshmallow import INCLUDE
from sqlalchemy import UniqueConstraint

View File

@ -2,8 +2,9 @@
from dataclasses import dataclass
from typing import Optional
from flask_bpmn.models.db import db
from flask_bpmn.models.db import SpiffworkflowBaseDBModel
from spiffworkflow_backend.models.db import db
from spiffworkflow_backend.models.db import SpiffworkflowBaseDBModel
@dataclass

View File

@ -1,8 +1,9 @@
"""Spiff_step_details."""
from dataclasses import dataclass
from flask_bpmn.models.db import db
from flask_bpmn.models.db import SpiffworkflowBaseDBModel
from spiffworkflow_backend.models.db import db
from spiffworkflow_backend.models.db import SpiffworkflowBaseDBModel
from sqlalchemy import ForeignKey
from sqlalchemy.orm import deferred

View File

@ -6,8 +6,9 @@ from dataclasses import dataclass
import jwt
import marshmallow
from flask import current_app
from flask_bpmn.models.db import db
from flask_bpmn.models.db import SpiffworkflowBaseDBModel
from spiffworkflow_backend.models.db import db
from spiffworkflow_backend.models.db import SpiffworkflowBaseDBModel
from marshmallow import Schema
from sqlalchemy.orm import relationship

View File

@ -1,6 +1,7 @@
"""UserGroupAssignment."""
from flask_bpmn.models.db import db
from flask_bpmn.models.db import SpiffworkflowBaseDBModel
from spiffworkflow_backend.models.db import db
from spiffworkflow_backend.models.db import SpiffworkflowBaseDBModel
from sqlalchemy import ForeignKey
from sqlalchemy.orm import relationship

View File

@ -1,6 +1,7 @@
"""UserGroupAssignment."""
from flask_bpmn.models.db import db
from flask_bpmn.models.db import SpiffworkflowBaseDBModel
from spiffworkflow_backend.models.db import db
from spiffworkflow_backend.models.db import SpiffworkflowBaseDBModel
from sqlalchemy import ForeignKey
from sqlalchemy.orm import relationship

View File

@ -9,7 +9,7 @@ from flask import g
from flask import jsonify
from flask import make_response
from flask.wrappers import Response
from flask_bpmn.api.api_error import ApiError
from spiffworkflow_backend.exceptions.api_error import ApiError
from spiffworkflow_backend.models.message_correlation import MessageCorrelationModel
from spiffworkflow_backend.models.message_instance import MessageInstanceModel

View File

@ -11,8 +11,8 @@ from flask import jsonify
from flask import make_response
from flask import request
from flask.wrappers import Response
from flask_bpmn.api.api_error import ApiError
from flask_bpmn.models.db import db
from spiffworkflow_backend.exceptions.api_error import ApiError
from spiffworkflow_backend.models.db import db
from spiffworkflow_backend.exceptions.process_entity_not_found_error import (
ProcessEntityNotFoundError,

View File

@ -8,7 +8,7 @@ from flask import g
from flask import jsonify
from flask import make_response
from flask.wrappers import Response
from flask_bpmn.api.api_error import ApiError
from spiffworkflow_backend.exceptions.api_error import ApiError
from spiffworkflow_backend.exceptions.process_entity_not_found_error import (
ProcessEntityNotFoundError,

View File

@ -11,8 +11,8 @@ from flask import jsonify
from flask import make_response
from flask import request
from flask.wrappers import Response
from flask_bpmn.api.api_error import ApiError
from flask_bpmn.models.db import db
from spiffworkflow_backend.exceptions.api_error import ApiError
from spiffworkflow_backend.models.db import db
from SpiffWorkflow.task import TaskState # type: ignore
from sqlalchemy import and_
from sqlalchemy import or_

View File

@ -14,7 +14,7 @@ from flask import g
from flask import jsonify
from flask import make_response
from flask.wrappers import Response
from flask_bpmn.api.api_error import ApiError
from spiffworkflow_backend.exceptions.api_error import ApiError
from werkzeug.datastructures import FileStorage
from spiffworkflow_backend.interfaces import IdToProcessGroupMapping
@ -519,7 +519,7 @@ def _create_or_update_process_model_file(
ApiError(
error_code="process_model_file_invalid",
message=(
f"Invalid Process model file cannot be save: {request_file.name}."
f"Invalid Process model file: {request_file.filename}."
f" Received error: {str(exception)}"
),
status_code=400,

View File

@ -10,7 +10,7 @@ from flask import current_app
from flask import jsonify
from flask import make_response
from flask.wrappers import Response
from flask_bpmn.api.api_error import ApiError
from spiffworkflow_backend.exceptions.api_error import ApiError
from lxml import etree # type: ignore
from lxml.builder import ElementMaker # type: ignore

View File

@ -15,8 +15,8 @@ from flask import g
from flask import jsonify
from flask import make_response
from flask.wrappers import Response
from flask_bpmn.api.api_error import ApiError
from flask_bpmn.models.db import db
from spiffworkflow_backend.exceptions.api_error import ApiError
from spiffworkflow_backend.models.db import db
from SpiffWorkflow.task import Task as SpiffTask # type: ignore
from SpiffWorkflow.task import TaskState
from sqlalchemy import and_

View File

@ -14,7 +14,7 @@ from flask import current_app
from flask import g
from flask import redirect
from flask import request
from flask_bpmn.api.api_error import ApiError
from spiffworkflow_backend.exceptions.api_error import ApiError
from werkzeug.wrappers import Response
from spiffworkflow_backend.models.user import UserModel

View File

@ -7,8 +7,8 @@ import flask.wrappers
from flask import Blueprint
from flask import request
from flask import Response
from flask_bpmn.api.api_error import ApiError
from flask_bpmn.models.db import db
from spiffworkflow_backend.exceptions.api_error import ApiError
from spiffworkflow_backend.models.db import db
from sqlalchemy.exc import IntegrityError
from spiffworkflow_backend.models.group import GroupModel

View File

@ -2,7 +2,7 @@
from time import time
from typing import Any
from flask_bpmn.models.db import db
from spiffworkflow_backend.models.db import db
from sqlalchemy import or_
from spiffworkflow_backend.models.process_instance import ProcessInstanceModel

View File

@ -3,7 +3,7 @@ from datetime import datetime
from typing import Any
import pytz
from flask_bpmn.api.api_error import ApiError
from spiffworkflow_backend.exceptions.api_error import ApiError
from spiffworkflow_backend.models.script_attributes_context import (
ScriptAttributesContext,

View File

@ -1,7 +1,7 @@
"""Save process instance metadata."""
from typing import Any
from flask_bpmn.models.db import db
from spiffworkflow_backend.models.db import db
from spiffworkflow_backend.models.process_instance_metadata import (
ProcessInstanceMetadataModel,

View File

@ -8,7 +8,7 @@ from abc import abstractmethod
from typing import Any
from typing import Callable
from flask_bpmn.api.api_error import ApiError
from spiffworkflow_backend.exceptions.api_error import ApiError
from spiffworkflow_backend.models.process_instance import ProcessInstanceModel
from spiffworkflow_backend.models.process_instance import ProcessInstanceNotFoundError

View File

@ -2,7 +2,7 @@
import time
from flask import current_app
from flask_bpmn.models.db import db
from spiffworkflow_backend.models.db import db
from tests.spiffworkflow_backend.helpers.base_test import BaseTest
from spiffworkflow_backend.models.process_instance import ProcessInstanceModel

View File

@ -9,8 +9,8 @@ import jwt
import requests
from flask import current_app
from flask import redirect
from flask_bpmn.api.api_error import ApiError
from flask_bpmn.models.db import db
from spiffworkflow_backend.exceptions.api_error import ApiError
from spiffworkflow_backend.models.db import db
from werkzeug.wrappers import Response
from spiffworkflow_backend.models.refresh_token import RefreshTokenModel

View File

@ -17,8 +17,8 @@ from flask import current_app
from flask import g
from flask import request
from flask import scaffold
from flask_bpmn.api.api_error import ApiError
from flask_bpmn.models.db import db
from spiffworkflow_backend.exceptions.api_error import ApiError
from spiffworkflow_backend.models.db import db
from SpiffWorkflow.task import Task as SpiffTask # type: ignore
from sqlalchemy import or_
from sqlalchemy import text

View File

@ -1,6 +1,6 @@
"""Data_setup_service."""
from flask import current_app
from flask_bpmn.models.db import db
from spiffworkflow_backend.models.db import db
from spiffworkflow_backend.services.process_model_service import ProcessModelService
from spiffworkflow_backend.services.spec_file_service import SpecFileService

View File

@ -5,8 +5,8 @@ from typing import Union
from flask import current_app
from flask import g
from flask.wrappers import Response
from flask_bpmn.api.api_error import ApiError
from flask_bpmn.models.db import db
from spiffworkflow_backend.exceptions.api_error import ApiError
from spiffworkflow_backend.models.db import db
from spiffworkflow_backend.models.message_model import MessageModel
from spiffworkflow_backend.models.message_triggerable_process_model import (

View File

@ -8,7 +8,7 @@ from typing import Optional
import pytz
from flask import current_app
from flask_bpmn.api.api_error import ApiError
from spiffworkflow_backend.exceptions.api_error import ApiError
from spiffworkflow_backend.models.file import CONTENT_TYPES
from spiffworkflow_backend.models.file import File

View File

@ -1,7 +1,7 @@
"""Group_service."""
from typing import Optional
from flask_bpmn.models.db import db
from spiffworkflow_backend.models.db import db
from spiffworkflow_backend.models.group import GroupModel
from spiffworkflow_backend.models.user import UserModel

View File

@ -7,7 +7,7 @@ from typing import Optional
from flask import g
from flask.app import Flask
from flask_bpmn.models.db import db
from spiffworkflow_backend.models.db import db
from spiffworkflow_backend.models.spiff_logging import SpiffLoggingModel

View File

@ -2,7 +2,7 @@
from typing import Any
from typing import Optional
from flask_bpmn.models.db import db
from spiffworkflow_backend.models.db import db
from sqlalchemy import and_
from sqlalchemy import or_
from sqlalchemy import select

View File

@ -22,11 +22,11 @@ from uuid import UUID
import dateparser
import pytz
from flask import current_app
from flask_bpmn.api.api_error import ApiError
from flask_bpmn.models.db import db
from spiffworkflow_backend.exceptions.api_error import ApiError
from spiffworkflow_backend.models.db import db
from lxml import etree # type: ignore
from RestrictedPython import safe_globals # type: ignore
from SpiffWorkflow.bpmn.exceptions import WorkflowTaskExecException # type: ignore
from SpiffWorkflow.exceptions import WorkflowTaskException # type: ignore
from SpiffWorkflow.bpmn.parser.ValidationException import ValidationException # type: ignore
from SpiffWorkflow.bpmn.PythonScriptEngine import Box # type: ignore
from SpiffWorkflow.bpmn.PythonScriptEngine import PythonScriptEngine
@ -222,7 +222,7 @@ class CustomBpmnScriptEngine(PythonScriptEngine): # type: ignore
% (expression, str(exception)),
) from exception
else:
raise WorkflowTaskExecException(
raise WorkflowTaskException(
task,
"Error evaluating expression '%s', %s"
% (expression, str(exception)),
@ -240,7 +240,7 @@ class CustomBpmnScriptEngine(PythonScriptEngine): # type: ignore
except WorkflowException as e:
raise e
except Exception as e:
raise WorkflowTaskExecException(task, f" {script}, {e}", e) from e
raise WorkflowTaskException(task, f" {script}, {e}", e) from e
def call_service(
self,
@ -1284,7 +1284,7 @@ class ProcessInstanceProcessor:
handler.bulk_insert_logs() # type: ignore
db.session.commit()
except WorkflowTaskExecException as we:
except WorkflowTaskException as we:
raise ApiError.from_workflow_exception("task_error", str(we), we) from we
finally:
@ -1304,7 +1304,7 @@ class ProcessInstanceProcessor:
bpmn_process_instance.catch(CancelEventDefinition())
# Due to this being static, can't save granular step details in this case
bpmn_process_instance.do_engine_steps()
except WorkflowTaskExecException as we:
except WorkflowTaskException as we:
raise ApiError.from_workflow_exception("task_error", str(we), we) from we
def user_defined_task_data(self, task_data: dict) -> dict:

View File

@ -6,8 +6,9 @@ from typing import Optional
from typing import Type
import sqlalchemy
from flask_bpmn.api.api_error import ApiError
from flask_bpmn.models.db import SpiffworkflowBaseDBModel
from spiffworkflow_backend.exceptions.api_error import ApiError
from spiffworkflow_backend.models.db import SpiffworkflowBaseDBModel
from sqlalchemy import and_
from sqlalchemy import func
from sqlalchemy import or_

View File

@ -5,8 +5,8 @@ from typing import List
from typing import Optional
from flask import current_app
from flask_bpmn.api.api_error import ApiError
from flask_bpmn.models.db import db
from spiffworkflow_backend.exceptions.api_error import ApiError
from spiffworkflow_backend.models.db import db
from SpiffWorkflow.task import Task as SpiffTask # type: ignore
from spiffworkflow_backend.models.human_task import HumanTaskModel

View File

@ -8,7 +8,7 @@ from typing import List
from typing import Optional
from typing import TypeVar
from flask_bpmn.api.api_error import ApiError
from spiffworkflow_backend.exceptions.api_error import ApiError
from spiffworkflow_backend.exceptions.process_entity_not_found_error import (
ProcessEntityNotFoundError,

View File

@ -6,7 +6,7 @@ from dataclasses import dataclass
from typing import Any
from typing import Optional
from SpiffWorkflow.bpmn.exceptions import WorkflowTaskExecException # type: ignore
from SpiffWorkflow.exceptions import WorkflowTaskException # type: ignore
from SpiffWorkflow.task import Task as SpiffTask # type: ignore
from spiffworkflow_backend.services.process_instance_processor import (
@ -54,9 +54,9 @@ class ScriptUnitTestRunner:
offset=ex.offset,
)
except Exception as ex:
if isinstance(ex, WorkflowTaskExecException):
if isinstance(ex, WorkflowTaskException):
# we never expect this to happen, so we want to know about it.
# if indeed we are always getting WorkflowTaskExecException,
# if indeed we are always getting WorkflowTaskException,
# we can simplify this error handling and replace it with the
# except block from revision cccd523ea39499c10f7f3c2e3f061852970973ac
raise ex

View File

@ -1,8 +1,8 @@
"""Secret_service."""
from typing import Optional
from flask_bpmn.api.api_error import ApiError
from flask_bpmn.models.db import db
from spiffworkflow_backend.exceptions.api_error import ApiError
from spiffworkflow_backend.models.db import db
from spiffworkflow_backend.models.secret_model import SecretModel

View File

@ -5,7 +5,7 @@ from datetime import datetime
from typing import List
from typing import Optional
from flask_bpmn.models.db import db
from spiffworkflow_backend.models.db import db
from lxml import etree # type: ignore
from SpiffWorkflow.bpmn.parser.BpmnParser import BpmnValidator # type: ignore
@ -173,10 +173,8 @@ class SpecFileService(FileSystemService):
parser = MyCustomParser(validator=validator)
try:
parser.add_bpmn_xml(etree.fromstring(binary_data), filename=file_name)
except etree.XMLSyntaxError as exception:
raise ProcessModelFileInvalidError(
f"Received error trying to parse bpmn xml: {str(exception)}"
) from exception
except Exception as exception:
raise ProcessModelFileInvalidError(str(exception))
@classmethod
def update_file(

View File

@ -4,8 +4,8 @@ from typing import Optional
from flask import current_app
from flask import g
from flask_bpmn.api.api_error import ApiError
from flask_bpmn.models.db import db
from spiffworkflow_backend.exceptions.api_error import ApiError
from spiffworkflow_backend.models.db import db
from spiffworkflow_backend.models.group import GroupModel
from spiffworkflow_backend.models.human_task import HumanTaskModel

View File

@ -9,8 +9,8 @@ from typing import Optional
from flask import current_app
from flask.testing import FlaskClient
from flask_bpmn.api.api_error import ApiError
from flask_bpmn.models.db import db
from spiffworkflow_backend.exceptions.api_error import ApiError
from spiffworkflow_backend.models.db import db
from tests.spiffworkflow_backend.helpers.test_data import load_test_spec
from werkzeug.test import TestResponse # type: ignore

View File

@ -9,7 +9,7 @@ from typing import Dict
import pytest
from flask.app import Flask
from flask.testing import FlaskClient
from flask_bpmn.models.db import db
from spiffworkflow_backend.models.db import db
from tests.spiffworkflow_backend.helpers.base_test import BaseTest
from tests.spiffworkflow_backend.helpers.test_data import load_test_spec

View File

@ -5,7 +5,7 @@ from typing import Optional
import pytest
from flask.app import Flask
from flask.testing import FlaskClient
from flask_bpmn.api.api_error import ApiError
from spiffworkflow_backend.exceptions.api_error import ApiError
from tests.spiffworkflow_backend.helpers.base_test import BaseTest
from werkzeug.test import TestResponse # type: ignore

View File

@ -1,7 +1,7 @@
"""Test_get_localtime."""
from flask.app import Flask
from flask.testing import FlaskClient
from flask_bpmn.models.db import db
from spiffworkflow_backend.models.db import db
from tests.spiffworkflow_backend.helpers.base_test import BaseTest
from tests.spiffworkflow_backend.helpers.test_data import load_test_spec

View File

@ -2,7 +2,7 @@
import pytest
from flask.app import Flask
from flask.testing import FlaskClient
from flask_bpmn.api.api_error import ApiError
from spiffworkflow_backend.exceptions.api_error import ApiError
from tests.spiffworkflow_backend.helpers.base_test import BaseTest
from tests.spiffworkflow_backend.helpers.test_data import load_test_spec

View File

@ -2,7 +2,7 @@
import pytest
from flask import Flask
from flask.testing import FlaskClient
from flask_bpmn.models.db import db
from spiffworkflow_backend.models.db import db
from tests.spiffworkflow_backend.helpers.base_test import BaseTest
from spiffworkflow_backend.models.message_instance import MessageInstanceModel

View File

@ -1,7 +1,7 @@
"""Process Model."""
import pytest
from flask.app import Flask
from flask_bpmn.models.db import db
from spiffworkflow_backend.models.db import db
from tests.spiffworkflow_backend.helpers.base_test import BaseTest
from spiffworkflow_backend.models.permission_target import (

View File

@ -1,7 +1,7 @@
"""Test Permissions."""
from flask.app import Flask
from flask.testing import FlaskClient
from flask_bpmn.models.db import db
from spiffworkflow_backend.models.db import db
from tests.spiffworkflow_backend.helpers.base_test import BaseTest
from tests.spiffworkflow_backend.helpers.test_data import load_test_spec

View File

@ -3,7 +3,7 @@ from typing import Optional
from flask import Flask
from flask.testing import FlaskClient
from flask_bpmn.models.db import db
from spiffworkflow_backend.models.db import db
from tests.spiffworkflow_backend.helpers.base_test import BaseTest
from tests.spiffworkflow_backend.helpers.test_data import load_test_spec

View File

@ -1,7 +1,7 @@
"""Process Model."""
from flask.app import Flask
from flask.testing import FlaskClient
from flask_bpmn.models.db import db
from spiffworkflow_backend.models.db import db
from tests.spiffworkflow_backend.helpers.base_test import BaseTest
from tests.spiffworkflow_backend.helpers.test_data import load_test_spec

View File

@ -2,7 +2,7 @@
import pytest
from flask.app import Flask
from flask.testing import FlaskClient
from flask_bpmn.api.api_error import ApiError
from spiffworkflow_backend.exceptions.api_error import ApiError
from tests.spiffworkflow_backend.helpers.base_test import BaseTest
from tests.spiffworkflow_backend.helpers.test_data import load_test_spec

View File

@ -4,7 +4,7 @@ import os
import pytest
from flask import Flask
from flask.testing import FlaskClient
from flask_bpmn.models.db import db
from spiffworkflow_backend.models.db import db
from tests.spiffworkflow_backend.helpers.base_test import BaseTest
from tests.spiffworkflow_backend.helpers.test_data import load_test_spec

View File

@ -2,7 +2,7 @@
from decimal import Decimal
from flask.app import Flask
from flask_bpmn.models.db import db
from spiffworkflow_backend.models.db import db
from tests.spiffworkflow_backend.helpers.base_test import BaseTest
from tests.spiffworkflow_backend.helpers.test_data import load_test_spec