diff --git a/datagateway_api/common/database/helpers.py b/datagateway_api/common/database/helpers.py index 55dbb40f..0d2474c7 100644 --- a/datagateway_api/common/database/helpers.py +++ b/datagateway_api/common/database/helpers.py @@ -17,7 +17,7 @@ INVESTIGATIONINSTRUMENT, SESSION, ) -from datagateway_api.common.database.session_manager import session_manager +from datagateway_api.common.database.session_manager import db from datagateway_api.common.exceptions import ( AuthenticationError, BadRequestError, @@ -41,12 +41,11 @@ def requires_session_id(method): @wraps(method) def wrapper_requires_session(*args, **kwargs): log.info(" Authenticating consumer") - session = session_manager.get_icat_db_session() + session = db.session() query = session.query(SESSION).filter(SESSION.ID == args[1]).first() if query is not None: log.info(" Closing DB session") session.close() - session.close() log.info(" Consumer authenticated") return method(*args, **kwargs) else: @@ -66,7 +65,7 @@ class Query(ABC): @abstractmethod def __init__(self, table): - self.session = session_manager.get_icat_db_session() + self.session = db.session self.table = table self.base_query = self.session.query(table) @@ -86,7 +85,12 @@ def commit_changes(self): Commits all changes to the database and closes the session """ log.info(" Committing changes to %s", self.table) - self.session.commit() + try: + self.session.commit() + except Exception as e: + log.error(f"Error whilst committing changes to {self.table}, rolling back") + self.session.rollback() + raise e class CountQuery(Query): diff --git a/datagateway_api/common/database/session_manager.py b/datagateway_api/common/database/session_manager.py index 5353fd26..386b601b 100644 --- a/datagateway_api/common/database/session_manager.py +++ b/datagateway_api/common/database/session_manager.py @@ -2,6 +2,7 @@ from sqlalchemy.orm import scoped_session, sessionmaker from sqlalchemy.pool import QueuePool +from flask_sqlalchemy import SQLAlchemy from datagateway_api.common.constants import Constants engine = create_engine( @@ -24,3 +25,4 @@ def get_icat_db_session(self): session_manager = SessionManager() +db = SQLAlchemy() diff --git a/datagateway_api/src/main.py b/datagateway_api/src/main.py index 22eb13df..35debd92 100644 --- a/datagateway_api/src/main.py +++ b/datagateway_api/src/main.py @@ -7,10 +7,13 @@ from flask_cors import CORS from flask_restful import Api from flask_swagger_ui import get_swaggerui_blueprint +from flask_sqlalchemy import SQLAlchemy from datagateway_api.common.backends import create_backend from datagateway_api.common.config import config from datagateway_api.common.logger_setup import setup_logger +from datagateway_api.common.database.session_manager import db +from datagateway_api.common.constants import Constants from datagateway_api.src.resources.entities.entity_endpoint import ( get_count_endpoint, get_endpoint, @@ -63,6 +66,8 @@ def create_app_infrastructure(flask_app): CORS(flask_app) flask_app.url_map.strict_slashes = False api = CustomErrorHandledApi(flask_app) + app.config["SQLALCHEMY_DATABASE_URI"] = Constants.DATABASE_URL + db.init_app(app) initialise_spec(spec) @@ -168,10 +173,10 @@ def specs(): resp.mimetype = "application/json" return resp +api, spec = create_app_infrastructure(app) +create_api_endpoints(app, api, spec) if __name__ == "__main__": - api, spec = create_app_infrastructure(app) - create_api_endpoints(app, api, spec) openapi_config(spec) app.run( host=config.get_host(), port=config.get_port(), debug=config.is_debug_mode(), diff --git a/poetry.lock b/poetry.lock index 037b6641..0ec30e5d 100644 --- a/poetry.lock +++ b/poetry.lock @@ -15,12 +15,12 @@ optional = false python-versions = ">=3.5" [package.extras] -dev = ["PyYAML (>=3.10)", "prance[osv] (>=0.11)", "marshmallow (>=2.19.2)", "pytest", "mock", "flake8 (==3.7.9)", "flake8-bugbear (==20.1.4)", "pre-commit (>=1.20,<3.0)", "tox"] +tests = ["PyYAML (>=3.10)", "prance[osv] (>=0.11)", "marshmallow (>=2.19.2)", "pytest", "mock"] docs = ["marshmallow (>=2.19.2)", "pyyaml (==5.3)", "sphinx (==2.4.1)", "sphinx-issues (==1.2.0)", "sphinx-rtd-theme (==0.4.3)"] lint = ["flake8 (==3.7.9)", "flake8-bugbear (==20.1.4)", "pre-commit (>=1.20,<3.0)"] -tests = ["PyYAML (>=3.10)", "prance[osv] (>=0.11)", "marshmallow (>=2.19.2)", "pytest", "mock"] -validation = ["prance[osv] (>=0.11)"] +dev = ["PyYAML (>=3.10)", "prance[osv] (>=0.11)", "marshmallow (>=2.19.2)", "pytest", "mock", "flake8 (==3.7.9)", "flake8-bugbear (==20.1.4)", "pre-commit (>=1.20,<3.0)", "tox"] yaml = ["PyYAML (>=3.10)"] +validation = ["prance[osv] (>=0.11)"] [[package]] name = "appdirs" @@ -47,10 +47,10 @@ optional = false python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" [package.extras] -dev = ["coverage[toml] (>=5.0.2)", "hypothesis", "pympler", "pytest (>=4.3.0)", "six", "zope.interface", "furo", "sphinx", "pre-commit"] docs = ["furo", "sphinx", "zope.interface"] tests = ["coverage[toml] (>=5.0.2)", "hypothesis", "pympler", "pytest (>=4.3.0)", "six", "zope.interface"] tests_no_zope = ["coverage[toml] (>=5.0.2)", "hypothesis", "pympler", "pytest (>=4.3.0)", "six"] +dev = ["coverage[toml] (>=5.0.2)", "hypothesis", "pympler", "pytest (>=4.3.0)", "six", "zope.interface", "furo", "sphinx", "pre-commit"] [[package]] name = "bandit" @@ -61,11 +61,11 @@ optional = false python-versions = "*" [package.dependencies] -colorama = {version = ">=0.3.9", markers = "platform_system == \"Windows\""} -GitPython = ">=1.0.1" +stevedore = ">=1.20.0" PyYAML = ">=3.13" six = ">=1.10.0" -stevedore = ">=1.20.0" +colorama = {version = ">=0.3.9", markers = "platform_system == \"Windows\""} +GitPython = ">=1.0.1" [[package]] name = "black" @@ -76,13 +76,13 @@ optional = false python-versions = ">=3.6" [package.dependencies] -appdirs = "*" -attrs = ">=18.1.0" -click = ">=6.5" -pathspec = ">=0.6,<1" regex = "*" -toml = ">=0.9.4" typed-ast = ">=1.4.0" +toml = ">=0.9.4" +attrs = ">=18.1.0" +pathspec = ">=0.6,<1" +click = ">=6.5" +appdirs = "*" [package.extras] d = ["aiohttp (>=3.3.2)", "aiohttp-cors"] @@ -186,9 +186,9 @@ python-versions = "*" [package.dependencies] bandit = "*" -flake8 = "*" flake8-polyfill = "*" pycodestyle = "*" +flake8 = "*" [[package]] name = "flake8-black" @@ -259,8 +259,8 @@ optional = false python-versions = ">=3.5" [package.dependencies] -flake8 = ">=3.0,<3.2.0 || >3.2.0,<4" importlib-metadata = {version = "*", markers = "python_version < \"3.8\""} +flake8 = ">=3.0,<3.2.0 || >3.2.0,<4" [[package]] name = "flake8-import-order" @@ -301,15 +301,15 @@ optional = false python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*" [package.dependencies] -click = ">=5.1" itsdangerous = ">=0.24" Jinja2 = ">=2.10.1" +click = ">=5.1" Werkzeug = ">=0.15" [package.extras] -dev = ["pytest", "coverage", "tox", "sphinx", "pallets-sphinx-themes", "sphinxcontrib-log-cabinet", "sphinx-issues"] -docs = ["sphinx", "pallets-sphinx-themes", "sphinxcontrib-log-cabinet", "sphinx-issues"] dotenv = ["python-dotenv"] +docs = ["sphinx", "pallets-sphinx-themes", "sphinxcontrib-log-cabinet", "sphinx-issues"] +dev = ["pytest", "coverage", "tox", "sphinx", "pallets-sphinx-themes", "sphinxcontrib-log-cabinet", "sphinx-issues"] [[package]] name = "flask-cors" @@ -332,14 +332,26 @@ optional = false python-versions = "*" [package.dependencies] -aniso8601 = ">=0.82" Flask = ">=0.8" +aniso8601 = ">=0.82" pytz = "*" six = ">=1.3.0" [package.extras] docs = ["sphinx"] +[[package]] +name = "flask-sqlalchemy" +version = "2.4.4" +description = "Adds SQLAlchemy support to your Flask application." +category = "main" +optional = false +python-versions = ">= 2.7, != 3.0.*, != 3.1.*, != 3.2.*, != 3.3.*" + +[package.dependencies] +Flask = ">=0.10" +SQLAlchemy = ">=0.8.0" + [[package]] name = "flask-swagger-ui" version = "3.25.0" @@ -498,12 +510,12 @@ optional = false python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,>=2.7" [package.dependencies] -click = ">=7" six = "*" +click = ">=7" [package.extras] -coverage = ["pytest-cov"] testing = ["mock", "pytest", "pytest-rerunfailures"] +coverage = ["pytest-cov"] [[package]] name = "pluggy" @@ -579,19 +591,19 @@ optional = false python-versions = ">=3.5" [package.dependencies] -atomicwrites = {version = ">=1.0", markers = "sys_platform == \"win32\""} -attrs = ">=17.4.0" -colorama = {version = "*", markers = "sys_platform == \"win32\""} -importlib-metadata = {version = ">=0.12", markers = "python_version < \"3.8\""} -iniconfig = "*" packaging = "*" -pluggy = ">=0.12,<1.0" py = ">=1.8.2" +iniconfig = "*" +importlib-metadata = {version = ">=0.12", markers = "python_version < \"3.8\""} +colorama = {version = "*", markers = "sys_platform == \"win32\""} +attrs = ">=17.4.0" toml = "*" +pluggy = ">=0.12,<1.0" +atomicwrites = {version = ">=1.0", markers = "sys_platform == \"win32\""} [package.extras] -checkqa_mypy = ["mypy (==0.780)"] testing = ["argcomplete", "hypothesis (>=3.56)", "mock", "nose", "requests", "xmlschema"] +checkqa_mypy = ["mypy (==0.780)"] [[package]] name = "pytest-cov" @@ -602,8 +614,8 @@ optional = false python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*" [package.dependencies] -coverage = ">=4.4" pytest = ">=4.6" +coverage = ">=4.4" [package.extras] testing = ["fields", "hunter", "process-tests (==2.0.2)", "six", "pytest-xdist", "virtualenv"] @@ -673,9 +685,9 @@ optional = false python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*" [package.dependencies] +idna = ">=2.5,<3" certifi = ">=2017.4.17" chardet = ">=3.0.2,<4" -idna = ">=2.5,<3" urllib3 = ">=1.21.1,<1.25.0 || >1.25.0,<1.25.1 || >1.25.1,<1.26" [package.extras] @@ -691,10 +703,10 @@ optional = false python-versions = ">=3.5" [package.dependencies] -Click = ">=6.0" -dparse = ">=0.5.1" packaging = "*" requests = "*" +Click = ">=6.0" +dparse = ">=0.5.1" [[package]] name = "six" @@ -721,16 +733,16 @@ optional = false python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" [package.extras] -mssql = ["pyodbc"] -mssql_pymssql = ["pymssql"] -mssql_pyodbc = ["pyodbc"] -mysql = ["mysqlclient"] -oracle = ["cx-oracle"] -postgresql = ["psycopg2"] postgresql_pg8000 = ["pg8000"] -postgresql_psycopg2binary = ["psycopg2-binary"] +postgresql = ["psycopg2"] postgresql_psycopg2cffi = ["psycopg2cffi"] pymysql = ["pymysql"] +mssql_pymssql = ["pymssql"] +postgresql_psycopg2binary = ["psycopg2-binary"] +mysql = ["mysqlclient"] +oracle = ["cx-oracle"] +mssql = ["pyodbc"] +mssql_pyodbc = ["pyodbc"] [[package]] name = "stevedore" @@ -798,8 +810,8 @@ optional = false python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*" [package.extras] -dev = ["pytest", "pytest-timeout", "coverage", "tox", "sphinx", "pallets-sphinx-themes", "sphinx-issues"] watchdog = ["watchdog"] +dev = ["pytest", "pytest-timeout", "coverage", "tox", "sphinx", "pallets-sphinx-themes", "sphinx-issues"] [[package]] name = "zipp" @@ -816,7 +828,7 @@ testing = ["pytest (>=3.5,!=3.7.3)", "pytest-checkdocs (>=1.2.3)", "pytest-flake [metadata] lock-version = "1.1" python-versions = "^3.6" -content-hash = "8ce6140731b2c2e9d2ba4f591ee85ca6d805851acba95169562ba1311a252ac5" +content-hash = "270d9adcbf9a704468e4e264bf85ca1c27fe66122d402e24a1ff7193636894ce" [metadata.files] aniso8601 = [ @@ -960,6 +972,10 @@ flask-restful = [ {file = "Flask-RESTful-0.3.7.tar.gz", hash = "sha256:f8240ec12349afe8df1db168ea7c336c4e5b0271a36982bff7394f93275f2ca9"}, {file = "Flask_RESTful-0.3.7-py2.py3-none-any.whl", hash = "sha256:ecd620c5cc29f663627f99e04f17d1f16d095c83dc1d618426e2ad68b03092f8"}, ] +flask-sqlalchemy = [ + {file = "Flask-SQLAlchemy-2.4.4.tar.gz", hash = "sha256:bfc7150eaf809b1c283879302f04c42791136060c6eeb12c0c6674fb1291fae5"}, + {file = "Flask_SQLAlchemy-2.4.4-py2.py3-none-any.whl", hash = "sha256:05b31d2034dd3f2a685cbbae4cfc4ed906b2a733cff7964ada450fd5e462b84e"}, +] flask-swagger-ui = [ {file = "flask-swagger-ui-3.25.0.tar.gz", hash = "sha256:42d098997e06b04f992609c4945cc990738b269c153d8388fc59a91a5dfcee9e"}, ] @@ -1114,6 +1130,8 @@ pyyaml = [ {file = "PyYAML-5.3.1-cp37-cp37m-win_amd64.whl", hash = "sha256:73f099454b799e05e5ab51423c7bcf361c58d3206fa7b0d555426b1f4d9a3eaf"}, {file = "PyYAML-5.3.1-cp38-cp38-win32.whl", hash = "sha256:06a0d7ba600ce0b2d2fe2e78453a470b5a6e000a985dd4a4e54e436cc36b0e97"}, {file = "PyYAML-5.3.1-cp38-cp38-win_amd64.whl", hash = "sha256:95f71d2af0ff4227885f7a6605c37fd53d3a106fcab511b8860ecca9fcf400ee"}, + {file = "PyYAML-5.3.1-cp39-cp39-win32.whl", hash = "sha256:ad9c67312c84def58f3c04504727ca879cb0013b2517c85a9a253f0cb6380c0a"}, + {file = "PyYAML-5.3.1-cp39-cp39-win_amd64.whl", hash = "sha256:6034f55dab5fea9e53f436aa68fa3ace2634918e8b5994d82f3621c04ff5ed2e"}, {file = "PyYAML-5.3.1.tar.gz", hash = "sha256:b8eac752c5e14d3eca0e6dd9199cd627518cb5ec06add0de9d32baeee6fe645d"}, ] regex = [ diff --git a/pyproject.toml b/pyproject.toml index 5fea5a14..1891a50d 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -19,6 +19,7 @@ flask-swagger-ui = "3.25.0" PyYAML = "5.3.1" python-icat = "0.17.0" suds-community = "^0.8.4" +Flask-SQLAlchemy = "^2.4.4" [tool.poetry.dev-dependencies] pip-tools = "5.3.1"