diff --git a/poetry.lock b/poetry.lock index 5059c2ff..c31efcf5 100644 --- a/poetry.lock +++ b/poetry.lock @@ -1110,6 +1110,14 @@ python-versions = ">=2.7,!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,!=3.5.*,!=3.6.* [package.dependencies] setuptools = "*" +[[package]] +name = "orjson" +version = "3.8.0" +description = "Fast, correct Python JSON library supporting dataclasses, datetimes, and numpy" +category = "main" +optional = false +python-versions = ">=3.7" + [[package]] name = "packaging" version = "21.3" @@ -2170,7 +2178,7 @@ testing = ["func-timeout", "jaraco.itertools", "pytest (>=6)", "pytest-black (>= [metadata] lock-version = "1.1" python-versions = ">=3.9,<3.11" -content-hash = "1eab1f1107af25153136aef3ef6964ed25e449b6bc27cd42a47bf485e23d4d4f" +content-hash = "5a1aa6f7788fe750e6c4f5681b71d7a8269806da061be6565746e3a23203196f" [metadata.files] alabaster = [ @@ -2825,6 +2833,50 @@ nodeenv = [ {file = "nodeenv-1.7.0-py2.py3-none-any.whl", hash = "sha256:27083a7b96a25f2f5e1d8cb4b6317ee8aeda3bdd121394e5ac54e498028a042e"}, {file = "nodeenv-1.7.0.tar.gz", hash = "sha256:e0e7f7dfb85fc5394c6fe1e8fa98131a2473e04311a45afb6508f7cf1836fa2b"}, ] +orjson = [ + {file = "orjson-3.8.0-cp310-cp310-macosx_10_7_x86_64.whl", hash = "sha256:9a93850a1bdc300177b111b4b35b35299f046148ba23020f91d6efd7bf6b9d20"}, + {file = "orjson-3.8.0-cp310-cp310-macosx_10_9_x86_64.macosx_11_0_arm64.macosx_10_9_universal2.whl", hash = "sha256:7536a2a0b41672f824912aeab545c2467a9ff5ca73a066ff04fb81043a0a177a"}, + {file = "orjson-3.8.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:66c19399bb3b058e3236af7910b57b19a4fc221459d722ed72a7dc90370ca090"}, + {file = "orjson-3.8.0-cp310-cp310-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:8b391d5c2ddc2f302d22909676b306cb6521022c3ee306c861a6935670291b2c"}, + {file = "orjson-3.8.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2bdb1042970ca5f544a047d6c235a7eb4acdb69df75441dd1dfcbc406377ab37"}, + {file = "orjson-3.8.0-cp310-cp310-manylinux_2_28_aarch64.whl", hash = "sha256:d189e2acb510e374700cb98cf11b54f0179916ee40f8453b836157ae293efa79"}, + {file = "orjson-3.8.0-cp310-cp310-manylinux_2_28_x86_64.whl", hash = "sha256:6a23b40c98889e9abac084ce5a1fb251664b41da9f6bdb40a4729e2288ed2ed4"}, + {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-manylinux_2_28_x86_64.whl", hash = "sha256:e2defd9527651ad39ec20ae03c812adf47ef7662bdd6bc07dabb10888d70dc62"}, + {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"}, + {file = "orjson-3.8.0-cp37-cp37m-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:6e3da2e4bd27c3b796519ca74132c7b9e5348fb6746315e0f6c1592bc5cf1caf"}, + {file = "orjson-3.8.0-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:896a21a07f1998648d9998e881ab2b6b80d5daac4c31188535e9d50460edfcf7"}, + {file = "orjson-3.8.0-cp37-cp37m-manylinux_2_28_aarch64.whl", hash = "sha256:4065906ce3ad6195ac4d1bddde862fe811a42d7be237a1ff762666c3a4bb2151"}, + {file = "orjson-3.8.0-cp37-cp37m-manylinux_2_28_x86_64.whl", hash = "sha256:5f856279872a4449fc629924e6a083b9821e366cf98b14c63c308269336f7c14"}, + {file = "orjson-3.8.0-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:1b1cd25acfa77935bb2e791b75211cec0cfc21227fe29387e553c545c3ff87e1"}, + {file = "orjson-3.8.0-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:3e2459d441ab8fd8b161aa305a73d5269b3cda13b5a2a39eba58b4dd3e394f49"}, + {file = "orjson-3.8.0-cp37-none-win_amd64.whl", hash = "sha256:d2b5dafbe68237a792143137cba413447f60dd5df428e05d73dcba10c1ea6fcf"}, + {file = "orjson-3.8.0-cp38-cp38-macosx_10_7_x86_64.whl", hash = "sha256:5b072ef8520cfe7bd4db4e3c9972d94336763c2253f7c4718a49e8733bada7b8"}, + {file = "orjson-3.8.0-cp38-cp38-macosx_10_9_x86_64.macosx_11_0_arm64.macosx_10_9_universal2.whl", hash = "sha256:e68c699471ea3e2dd1b35bfd71c6a0a0e4885b64abbe2d98fce1ef11e0afaff3"}, + {file = "orjson-3.8.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3c7225e8b08996d1a0c804d3a641a53e796685e8c9a9fd52bd428980032cad9a"}, + {file = "orjson-3.8.0-cp38-cp38-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:8f687776a03c19f40b982fb5c414221b7f3d19097841571be2223d1569a59877"}, + {file = "orjson-3.8.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7990a9caf3b34016ac30be5e6cfc4e7efd76aa85614a1215b0eae4f0c7e3db59"}, + {file = "orjson-3.8.0-cp38-cp38-manylinux_2_28_aarch64.whl", hash = "sha256:02d638d43951ba346a80f0abd5942a872cc87db443e073f6f6fc530fee81e19b"}, + {file = "orjson-3.8.0-cp38-cp38-manylinux_2_28_x86_64.whl", hash = "sha256:f4b46dbdda2f0bd6480c39db90b21340a19c3b0fcf34bc4c6e465332930ca539"}, + {file = "orjson-3.8.0-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:655d7387a1634a9a477c545eea92a1ee902ab28626d701c6de4914e2ed0fecd2"}, + {file = "orjson-3.8.0-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:5edb93cdd3eb32977633fa7aaa6a34b8ab54d9c49cdcc6b0d42c247a29091b22"}, + {file = "orjson-3.8.0-cp38-none-win_amd64.whl", hash = "sha256:03ed95814140ff09f550b3a42e6821f855d981c94d25b9cc83e8cca431525d70"}, + {file = "orjson-3.8.0-cp39-cp39-macosx_10_7_x86_64.whl", hash = "sha256:7b0e72974a5d3b101226899f111368ec2c9824d3e9804af0e5b31567f53ad98a"}, + {file = "orjson-3.8.0-cp39-cp39-macosx_10_9_x86_64.macosx_11_0_arm64.macosx_10_9_universal2.whl", hash = "sha256:6ea5fe20ef97545e14dd4d0263e4c5c3bc3d2248d39b4b0aed4b84d528dfc0af"}, + {file = "orjson-3.8.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6433c956f4a18112342a18281e0bec67fcd8b90be3a5271556c09226e045d805"}, + {file = "orjson-3.8.0-cp39-cp39-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:87462791dd57de2e3e53068bf4b7169c125c50960f1bdda08ed30c797cb42a56"}, + {file = "orjson-3.8.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:be02f6acee33bb63862eeff80548cd6b8a62e2d60ad2d8dfd5a8824cc43d8887"}, + {file = "orjson-3.8.0-cp39-cp39-manylinux_2_28_aarch64.whl", hash = "sha256:a709c2249c1f2955dbf879506fd43fa08c31fdb79add9aeb891e3338b648bf60"}, + {file = "orjson-3.8.0-cp39-cp39-manylinux_2_28_x86_64.whl", hash = "sha256:2065b6d280dc58f131ffd93393737961ff68ae7eb6884b68879394074cc03c13"}, + {file = "orjson-3.8.0-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:5fd6cac83136e06e538a4d17117eaeabec848c1e86f5742d4811656ad7ee475f"}, + {file = "orjson-3.8.0-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:25b5e48fbb9f0b428a5e44cf740675c9281dd67816149fc33659803399adbbe8"}, + {file = "orjson-3.8.0-cp39-none-win_amd64.whl", hash = "sha256:2058653cc12b90e482beacb5c2d52dc3d7606f9e9f5a52c1c10ef49371e76f52"}, + {file = "orjson-3.8.0.tar.gz", hash = "sha256:fb42f7cf57d5804a9daa6b624e3490ec9e2631e042415f3aebe9f35a8492ba6c"}, +] packaging = [ {file = "packaging-21.3-py3-none-any.whl", hash = "sha256:ef103e05f519cdc783ae24ea4e2e0f508a9c99b2d4969652eed6a2e1ea5bd522"}, {file = "packaging-21.3.tar.gz", hash = "sha256:dd47c42927d89ab911e606518907cc2d3a1f38bbd026385970643f9c5b8ecfeb"}, diff --git a/pyproject.toml b/pyproject.toml index 02a23b1b..72a00b38 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -54,6 +54,7 @@ types-requests = "^2.28.6" Jinja2 = "^3.1.2" RestrictedPython = "^5.2" Flask-SQLAlchemy = "^3" +orjson = "^3.8.0" [tool.poetry.dev-dependencies] @@ -103,7 +104,12 @@ filterwarnings = [ # connexion/spec.py:50 'ignore:Passing a schema to Validator.iter_errors is deprecated and will be removed in a future release', # connexion/decorators/validation.py:16 - 'ignore:Accessing jsonschema.draft4_format_checker is deprecated and will be removed in a future release.' + 'ignore:Accessing jsonschema.draft4_format_checker is deprecated and will be removed in a future release.', + # connexion/apis/flask_api.py:236 + "ignore:'_request_ctx_stack' is deprecated and will be removed in Flask 2.3", + "ignore:Setting 'json_encoder' on the app or a blueprint is deprecated and will be removed in Flask 2.3", + "ignore:'JSONEncoder' is deprecated and will be removed in Flask 2.3", + "ignore:'app.json_encoder' is deprecated and will be removed in Flask 2.3" ] [tool.coverage.paths] diff --git a/src/spiffworkflow_backend/__init__.py b/src/spiffworkflow_backend/__init__.py index 6e93fb67..c192b07e 100644 --- a/src/spiffworkflow_backend/__init__.py +++ b/src/spiffworkflow_backend/__init__.py @@ -1,10 +1,12 @@ """__init__.""" +import json import os from typing import Any import connexion # type: ignore import flask.app import flask.json +from flask import jsonify import sqlalchemy from apscheduler.schedulers.background import BackgroundScheduler # type: ignore from flask_bpmn.api.api_error import api_error_blueprint @@ -22,8 +24,9 @@ from spiffworkflow_backend.services.background_processing_service import ( BackgroundProcessingService, ) +from flask.json.provider import DefaultJSONProvider, _default -class MyJSONEncoder(flask.json.JSONEncoder): +class MyJSONEncoder(DefaultJSONProvider): """MyJSONEncoder.""" def default(self, obj: Any) -> Any: @@ -42,6 +45,9 @@ class MyJSONEncoder(flask.json.JSONEncoder): return return_dict return super().default(obj) + def dumps(self, obj, **kwargs): + kwargs.setdefault("default", self.default) + return super().dumps(obj, **kwargs) def start_scheduler(app: flask.app.Flask) -> None: """Start_scheduler.""" @@ -100,7 +106,7 @@ def create_app() -> flask.app.Flask: mail = Mail(app) app.config["MAIL_APP"] = mail - app.json_provider_class = MyJSONEncoder + app.json = MyJSONEncoder(app) if app.config["PROCESS_WAITING_MESSAGES"]: start_scheduler(app) diff --git a/src/spiffworkflow_backend/config/__init__.py b/src/spiffworkflow_backend/config/__init__.py index cc5a4ea5..f8f39a44 100644 --- a/src/spiffworkflow_backend/config/__init__.py +++ b/src/spiffworkflow_backend/config/__init__.py @@ -11,6 +11,7 @@ from spiffworkflow_backend.services.logging_service import setup_logger def setup_database_uri(app: Flask) -> None: """Setup_database_uri.""" if os.environ.get("SPIFFWORKFLOW_BACKEND_DATABASE_URI") is None: + database_name = f"spiffworkflow_backend_{app.config['ENV_IDENTIFIER']}" if os.environ.get("SPIFF_DATABASE_TYPE") == "sqlite": app.config[ "SQLALCHEMY_DATABASE_URI" @@ -18,7 +19,7 @@ def setup_database_uri(app: Flask) -> None: elif os.environ.get("SPIFF_DATABASE_TYPE") == "postgres": app.config[ "SQLALCHEMY_DATABASE_URI" - ] = f"postgresql://spiffworkflow_backend:spiffworkflow_backend@localhost:5432/spiffworkflow_backend_{app.config['ENV_IDENTIFIER']}" + ] = f"postgresql://spiffworkflow_backend:spiffworkflow_backend@localhost:5432/{database_name}" else: # use pswd to trick flake8 with hardcoded passwords db_pswd = os.environ.get("DB_PASSWORD") @@ -26,7 +27,7 @@ def setup_database_uri(app: Flask) -> None: db_pswd = "" app.config[ "SQLALCHEMY_DATABASE_URI" - ] = f"mysql+mysqlconnector://root:{db_pswd}@localhost/spiffworkflow_backend_{app.config['ENV_IDENTIFIER']}" + ] = f"mysql+mysqlconnector://root:{db_pswd}@localhost/{database_name}" else: app.config["SQLALCHEMY_DATABASE_URI"] = os.environ.get( "SPIFFWORKFLOW_BACKEND_DATABASE_URI" @@ -41,11 +42,16 @@ def setup_config(app: Flask) -> None: except OSError: pass - app.config['ENV_IDENTIFIER'] = os.environ.get("SPIFFWORKFLOW_BACKEND_ENV", "development") + app.config["ENV_IDENTIFIER"] = os.environ.get( + "SPIFFWORKFLOW_BACKEND_ENV", "development" + ) app.config["SQLALCHEMY_TRACK_MODIFICATIONS"] = False app.config.from_object("spiffworkflow_backend.config.default") # This allows config/testing.py or instance/config.py to override the default config - if "SPIFFWORKFLOW_BACKEND_ENV" in app.config and app.config["SPIFFWORKFLOW_BACKEND_ENV"] == "testing": + if ( + "SPIFFWORKFLOW_BACKEND_ENV" in app.config + and app.config["SPIFFWORKFLOW_BACKEND_ENV"] == "testing" + ): app.config.from_pyfile("config/testing.py", silent=True) else: app.config.from_pyfile(f"{app.instance_path}/config.py", silent=True) @@ -53,7 +59,7 @@ def setup_config(app: Flask) -> None: setup_database_uri(app) setup_logger(app) - env_config_module = "spiffworkflow_backend.config." + app.config['ENV_IDENTIFIER'] + env_config_module = "spiffworkflow_backend.config." + app.config["ENV_IDENTIFIER"] try: app.config.from_object(env_config_module) except ImportStringError as exception: diff --git a/src/spiffworkflow_backend/models/principal.py b/src/spiffworkflow_backend/models/principal.py index 76c0caf6..fbe05930 100644 --- a/src/spiffworkflow_backend/models/principal.py +++ b/src/spiffworkflow_backend/models/principal.py @@ -11,11 +11,11 @@ from spiffworkflow_backend.models.user import UserModel class DataValidityError(Exception): - pass + """DataValidityError.""" class MissingPrincipalError(DataValidityError): - pass + """MissingPrincipalError.""" @dataclass diff --git a/src/spiffworkflow_backend/routes/process_api_blueprint.py b/src/spiffworkflow_backend/routes/process_api_blueprint.py index fadd914c..74f6b7e0 100644 --- a/src/spiffworkflow_backend/routes/process_api_blueprint.py +++ b/src/spiffworkflow_backend/routes/process_api_blueprint.py @@ -436,7 +436,7 @@ def process_instance_log_list( .add_columns( UserModel.username, ) - .paginate(page, per_page, False) + .paginate(page=page, per_page=per_page, error_out=False) ) response_json = { diff --git a/src/spiffworkflow_backend/services/authorization_service.py b/src/spiffworkflow_backend/services/authorization_service.py index 09c1f28d..295a0664 100644 --- a/src/spiffworkflow_backend/services/authorization_service.py +++ b/src/spiffworkflow_backend/services/authorization_service.py @@ -7,7 +7,8 @@ from flask_bpmn.api.api_error import ApiError from spiffworkflow_backend.models.permission_assignment import PermissionAssignmentModel from spiffworkflow_backend.models.permission_target import PermissionTargetModel -from spiffworkflow_backend.models.principal import MissingPrincipalError, PrincipalModel +from spiffworkflow_backend.models.principal import MissingPrincipalError +from spiffworkflow_backend.models.principal import PrincipalModel from spiffworkflow_backend.models.user import UserModel @@ -21,11 +22,10 @@ class AuthorizationService: """Has_permission.""" principal_ids = [p.id for p in principals] permission_assignment = ( - PermissionAssignmentModel.query. - filter(PermissionAssignmentModel.principal_id.in_(principal_ids)) - .filter_by( - permission=permission + PermissionAssignmentModel.query.filter( + PermissionAssignmentModel.principal_id.in_(principal_ids) ) + .filter_by(permission=permission) .join(PermissionTargetModel) .filter_by(uri=target_uri) .first() @@ -40,21 +40,27 @@ class AuthorizationService: raise Exception("Unknown grant type") @classmethod - def user_has_permission(cls, user: UserModel, permission: str, target_uri: str) -> bool: + def user_has_permission( + cls, user: UserModel, permission: str, target_uri: str + ) -> bool: + """User_has_permission.""" if user.principal is None: - raise MissingPrincipalError(f"Missing principal for user with id: {user.id}") + raise MissingPrincipalError( + f"Missing principal for user with id: {user.id}" + ) principals = [user.principal] for group in user.groups: if group.principal is None: - raise MissingPrincipalError(f"Missing principal for group with id: {group.id}") + raise MissingPrincipalError( + f"Missing principal for group with id: {group.id}" + ) principals.append(group.principal) - return has_permission(principals, permission, target_uri) + return cls.has_permission(principals, permission, target_uri) # return False - # def refresh_token(self, token: str) -> str: # """Refresh_token.""" # # if isinstance(token, str): diff --git a/src/spiffworkflow_backend/services/logging_service.py b/src/spiffworkflow_backend/services/logging_service.py index 500c2d1d..af919e92 100644 --- a/src/spiffworkflow_backend/services/logging_service.py +++ b/src/spiffworkflow_backend/services/logging_service.py @@ -118,7 +118,7 @@ def setup_logger(app: Flask) -> None: # the json formatter is nice for real environments but makes # debugging locally a little more difficult - if app.config['ENV_IDENTIFIER'] != "development": + if app.config["ENV_IDENTIFIER"] != "development": json_formatter = JsonFormatter( { "level": "levelname", diff --git a/src/spiffworkflow_backend/services/process_instance_processor.py b/src/spiffworkflow_backend/services/process_instance_processor.py index b1be8092..16cdce6a 100644 --- a/src/spiffworkflow_backend/services/process_instance_processor.py +++ b/src/spiffworkflow_backend/services/process_instance_processor.py @@ -115,7 +115,9 @@ class CustomBpmnScriptEngine(PythonScriptEngine): # type: ignore def __get_augment_methods(self, task: SpiffTask) -> Dict[str, Callable]: """__get_augment_methods.""" - return Script.generate_augmented_list(task, current_app.config['ENV_IDENTIFIER']) + return Script.generate_augmented_list( + task, current_app.config["ENV_IDENTIFIER"] + ) def evaluate(self, task: SpiffTask, expression: str) -> Any: """Evaluate."""