From 5e5d6c4d8fbd0a1cfe8f90fd16b1bb183115a50b Mon Sep 17 00:00:00 2001 From: Joshua Kitenge Date: Tue, 26 Nov 2024 15:48:39 +0000 Subject: [PATCH] refactor: remove database backend #469 --- .flake8 | 2 +- datagateway_api/config.yaml.example | 2 - datagateway_api/src/api_start_utils.py | 48 +- datagateway_api/src/common/config.py | 54 +-- datagateway_api/src/common/date_handler.py | 3 +- .../src/common/filter_order_handler.py | 2 +- datagateway_api/src/common/filters.py | 4 +- datagateway_api/src/common/helpers.py | 4 +- .../src/datagateway_api/backend.py | 153 ------- .../src/datagateway_api/backends.py | 29 -- .../src/datagateway_api/database/__init__.py | 0 .../src/datagateway_api/database/backend.py | 125 ------ .../src/datagateway_api/database/filters.py | 220 --------- .../src/datagateway_api/database/helpers.py | 388 ---------------- .../src/datagateway_api/icat/filters.py | 6 +- .../src/datagateway_api/icat/helpers.py | 7 +- .../{database => icat}/models.py | 2 +- .../icat/{backend.py => python_icat.py} | 89 +++- .../datagateway_api/query_filter_factory.py | 43 +- .../src/resources/entities/entity_endpoint.py | 42 +- .../resources/non_entities/ping_endpoint.py | 12 +- .../non_entities/sessions_endpoints.py | 16 +- .../src/swagger/datagateway_api/openapi.yaml | 422 +++++++++--------- test/conftest.py | 2 - test/integration/conftest.py | 11 +- .../datagateway_api/db/conftest.py | 156 ------- .../datagateway_api/db/endpoints/conftest.py | 51 --- .../endpoints/test_count_with_filters_db.py | 29 -- .../db/endpoints/test_create_db.py | 114 ----- .../db/endpoints/test_delete_by_id_db.py | 40 -- .../db/endpoints/test_findone_db.py | 29 -- .../db/endpoints/test_get_by_id_db.py | 43 -- .../db/endpoints/test_get_with_filters.py | 167 ------- .../db/endpoints/test_ping_db.py | 27 -- .../db/endpoints/test_update_by_id_db.py | 66 --- .../db/endpoints/test_update_multiple_db.py | 85 ---- .../db/test_database_filter_utilities.py | 349 --------------- .../datagateway_api/db/test_entity_helper.py | 202 --------- .../db/test_requires_session_id.py | 47 -- .../icat/endpoints/test_ping_icat.py | 6 +- .../icat/test_session_handling.py | 9 +- .../datagateway_api/test_backends.py | 56 --- .../test_query_filter_factory.py | 26 +- test/integration/test_config.py | 9 - .../test_get_filters_from_query.py | 31 -- test/unit/test_config.py | 9 - test/unit/test_get_entity_object.py | 2 +- test/unit/test_query_filter.py | 12 - 48 files changed, 398 insertions(+), 2853 deletions(-) delete mode 100644 datagateway_api/src/datagateway_api/backend.py delete mode 100644 datagateway_api/src/datagateway_api/backends.py delete mode 100644 datagateway_api/src/datagateway_api/database/__init__.py delete mode 100644 datagateway_api/src/datagateway_api/database/backend.py delete mode 100644 datagateway_api/src/datagateway_api/database/filters.py delete mode 100644 datagateway_api/src/datagateway_api/database/helpers.py rename datagateway_api/src/datagateway_api/{database => icat}/models.py (99%) rename datagateway_api/src/datagateway_api/icat/{backend.py => python_icat.py} (54%) delete mode 100644 test/integration/datagateway_api/db/conftest.py delete mode 100644 test/integration/datagateway_api/db/endpoints/conftest.py delete mode 100644 test/integration/datagateway_api/db/endpoints/test_count_with_filters_db.py delete mode 100644 test/integration/datagateway_api/db/endpoints/test_create_db.py delete mode 100644 test/integration/datagateway_api/db/endpoints/test_delete_by_id_db.py delete mode 100644 test/integration/datagateway_api/db/endpoints/test_findone_db.py delete mode 100644 test/integration/datagateway_api/db/endpoints/test_get_by_id_db.py delete mode 100644 test/integration/datagateway_api/db/endpoints/test_get_with_filters.py delete mode 100644 test/integration/datagateway_api/db/endpoints/test_ping_db.py delete mode 100644 test/integration/datagateway_api/db/endpoints/test_update_by_id_db.py delete mode 100644 test/integration/datagateway_api/db/endpoints/test_update_multiple_db.py delete mode 100644 test/integration/datagateway_api/db/test_database_filter_utilities.py delete mode 100644 test/integration/datagateway_api/db/test_entity_helper.py delete mode 100644 test/integration/datagateway_api/db/test_requires_session_id.py delete mode 100644 test/integration/datagateway_api/test_backends.py delete mode 100644 test/integration/test_config.py diff --git a/.flake8 b/.flake8 index 13cd02ab..dffeb8c5 100644 --- a/.flake8 +++ b/.flake8 @@ -10,7 +10,7 @@ per-file-ignores = test/*: S101 util/icat_db_generator.py: S311 datagateway_api/wsgi.py:E402,F401 - datagateway_api/src/datagateway_api/database/models.py: N815,A003 + datagateway_api/src/datagateway_api/icat/models.py: N815,A003 datagateway_api/src/datagateway_api/icat/filters.py: C901 datagateway_api/src/search_api/models.py: B950 enable-extensions=G diff --git a/datagateway_api/config.yaml.example b/datagateway_api/config.yaml.example index 38170d43..67030dc2 100644 --- a/datagateway_api/config.yaml.example +++ b/datagateway_api/config.yaml.example @@ -1,11 +1,9 @@ --- datagateway_api: extension: "/" - backend: "python_icat" client_cache_size: 5 client_pool_init_size: 2 client_pool_max_size: 5 - db_url: "mysql+pymysql://icatdbuser:icatdbuserpw@localhost:3306/icatdb" icat_url: "https://localhost:8181" icat_check_cert: false use_reader_for_performance: diff --git a/datagateway_api/src/api_start_utils.py b/datagateway_api/src/api_start_utils.py index 6859368f..eaee0ca3 100644 --- a/datagateway_api/src/api_start_utils.py +++ b/datagateway_api/src/api_start_utils.py @@ -9,14 +9,8 @@ from flask_swagger_ui import get_swaggerui_blueprint from datagateway_api.src.common.config import Config - -# Only attempt to create a DataGateway API backend if the datagateway_api object -# is present in the config. This ensures that the API does not error on startup -# due to an AttributeError exception being thrown if the object is missing. -if Config.config.datagateway_api: - from datagateway_api.src.datagateway_api.backends import create_backend -from datagateway_api.src.datagateway_api.database.helpers import db # noqa: I202 from datagateway_api.src.datagateway_api.icat.icat_client_pool import create_client_pool +from datagateway_api.src.datagateway_api.icat.python_icat import PythonICAT from datagateway_api.src.resources.entities.entity_endpoint import ( get_count_endpoint, get_endpoint, @@ -112,21 +106,6 @@ def create_app_infrastructure(flask_app): CORS(flask_app) flask_app.url_map.strict_slashes = False api = CustomErrorHandledApi(flask_app) - - if Config.config.datagateway_api is not None: - try: - backend_type = flask_app.config["TEST_BACKEND"] - Config.config.datagateway_api.set_backend_type(backend_type) - except KeyError: - backend_type = Config.config.datagateway_api.backend - - if backend_type == "db": - flask_app.config[ - "SQLALCHEMY_DATABASE_URI" - ] = Config.config.datagateway_api.db_url - flask_app.config["SQLALCHEMY_TRACK_MODIFICATIONS"] = False - db.init_app(flask_app) - specs = [] if Config.config.datagateway_api is not None: configure_datagateway_api_swaggerui_blueprint(flask_app) @@ -148,25 +127,18 @@ def create_api_endpoints(flask_app, api, specs): datagateway_api_spec = next( (spec for spec in specs if spec.title == "DataGateway API"), None, ) - try: - backend_type = flask_app.config["TEST_BACKEND"] - Config.config.datagateway_api.set_backend_type(backend_type) - except KeyError: - backend_type = Config.config.datagateway_api.backend - backend = create_backend(backend_type) + python_icat = PythonICAT() - icat_client_pool = None - if backend_type == "python_icat": - # Create client pool - icat_client_pool = create_client_pool() + # Create client pool + icat_client_pool = create_client_pool() datagateway_api_extension = Config.config.datagateway_api.extension for entity_name in endpoints: get_endpoint_resource = get_endpoint( entity_name, endpoints[entity_name], - backend, + python_icat, client_pool=icat_client_pool, ) api.add_resource( @@ -179,7 +151,7 @@ def create_api_endpoints(flask_app, api, specs): get_id_endpoint_resource = get_id_endpoint( entity_name, endpoints[entity_name], - backend, + python_icat, client_pool=icat_client_pool, ) api.add_resource( @@ -192,7 +164,7 @@ def create_api_endpoints(flask_app, api, specs): get_count_endpoint_resource = get_count_endpoint( entity_name, endpoints[entity_name], - backend, + python_icat, client_pool=icat_client_pool, ) api.add_resource( @@ -205,7 +177,7 @@ def create_api_endpoints(flask_app, api, specs): get_find_one_endpoint_resource = get_find_one_endpoint( entity_name, endpoints[entity_name], - backend, + python_icat, client_pool=icat_client_pool, ) api.add_resource( @@ -217,7 +189,7 @@ def create_api_endpoints(flask_app, api, specs): # Session endpoint session_endpoint_resource = session_endpoints( - backend, client_pool=icat_client_pool, + python_icat, client_pool=icat_client_pool, ) api.add_resource( session_endpoint_resource, @@ -227,7 +199,7 @@ def create_api_endpoints(flask_app, api, specs): datagateway_api_spec.path(resource=session_endpoint_resource, api=api) # Ping endpoint - ping_resource = ping_endpoint(backend, client_pool=icat_client_pool) + ping_resource = ping_endpoint(python_icat, client_pool=icat_client_pool) api.add_resource(ping_resource, f"{datagateway_api_extension}/ping") datagateway_api_spec.path(resource=ping_resource, api=api) diff --git a/datagateway_api/src/common/config.py b/datagateway_api/src/common/config.py index dbc96571..e44e7031 100644 --- a/datagateway_api/src/common/config.py +++ b/datagateway_api/src/common/config.py @@ -48,16 +48,12 @@ class UseReaderForPerformance(BaseModel): class DataGatewayAPI(BaseModel): """ Configuration model class that implements pydantic's BaseModel class to allow for - validation of the DataGatewayAPI config data using Python type annotations. It takes - the backend into account, meaning only the config options for the backend used are - required. + validation of the DataGatewayAPI config data using Python type annotations. """ - backend: StrictStr client_cache_size: Optional[StrictInt] client_pool_init_size: Optional[StrictInt] client_pool_max_size: Optional[StrictInt] - db_url: Optional[StrictStr] extension: StrictStr icat_check_cert: Optional[StrictBool] icat_url: Optional[StrictStr] @@ -68,23 +64,6 @@ class DataGatewayAPI(BaseModel): def __getitem__(self, item): return getattr(self, item) - @validator("db_url", always=True) - def require_db_config_value(cls, value, values): # noqa: B902, N805 - """ - By default the `db_url` config field is optional so that it does not have to be - present in the config file if `backend` is set to `python_icat`. However, if the - `backend` is set to `db`, this validator esentially makes the `db_url` config - field mandatory. This means that an error is raised, at which point the - application exits, if a `db_url` config value is not present in the config file. - - :param cls: :class:`DataGatewayAPI` pointer - :param value: The value of the given config field - :param values: The config field values loaded before the given config field - """ - if "backend" in values and values["backend"] == "db" and value is None: - raise TypeError("field required") - return value - @validator( "client_cache_size", "client_pool_init_size", @@ -93,44 +72,25 @@ def require_db_config_value(cls, value, values): # noqa: B902, N805 "icat_url", always=True, ) - def require_icat_config_value(cls, value, values): # noqa: B902, N805 + def require_icat_config_value(cls, value): # noqa: B902, N805 """ - By default the above config fields that are passed to the `@validator` decorator - are optional so that they do not have to be present in the config file if - `backend` is set to `db`. However, if the `backend` is set to `python_icat`, - this validator esentially makes these config fields mandatory. This means that - an error is raised, at which point the application exits, if any of these config - values are not present in the config file. + Validates that the required config fields for the `python_icat` + are present and not None. If any of these config values are missing, + an error is raised, causing the application to exit. :param cls: :class:`DataGatewayAPI` pointer :param value: The value of the given config field - :param values: The config field values loaded before the given config field """ - - if "backend" in values and values["backend"] == "python_icat" and value is None: - raise TypeError("field required") + if value is None: + raise TypeError("Field required for `python_icat`.") return value - def set_backend_type(self, backend_type): - """ - This setter is used as a way for automated tests to set the backend type. The - API can detect if the Flask app setup is from an automated test by checking the - app's config for a `TEST_BACKEND`. If this value exists (a KeyError will be - raised when the API is run normally, which will then grab the backend type from - `config.yaml`), it needs to be set using this function. This is required because - creating filters in the `QueryFilterFactory` is backend-specific so the backend - type must be fetched. This must be done using this module (rather than directly - importing and checking the Flask app's config) to avoid circular import issues. - """ - self.backend = backend_type - class Config: """ The behaviour of the BaseModel class can be controlled via this class. """ # Enables assignment validation on the BaseModel fields. Useful for when the - # backend type is changed using the set_backend_type function. validate_assignment = True diff --git a/datagateway_api/src/common/date_handler.py b/datagateway_api/src/common/date_handler.py index 729695aa..15efd50f 100644 --- a/datagateway_api/src/common/date_handler.py +++ b/datagateway_api/src/common/date_handler.py @@ -32,8 +32,7 @@ def is_str_a_date(potential_date): @staticmethod def str_to_datetime_object(data): """ - Convert a string to a `datetime.datetime` object. This is commonly used when - storing user input in ICAT (using the Python ICAT backend). + Convert a string to a `datetime.datetime` object. Python 3.7+ has support for `datetime.fromisoformat()` which would be a more elegant solution to this conversion operation since dates are converted into ISO diff --git a/datagateway_api/src/common/filter_order_handler.py b/datagateway_api/src/common/filter_order_handler.py index ec9a9f22..ce0a9133 100644 --- a/datagateway_api/src/common/filter_order_handler.py +++ b/datagateway_api/src/common/filter_order_handler.py @@ -176,7 +176,7 @@ def clear_python_icat_order_filters(self): def manage_icat_filters(self, filters, query): """ Utility function to call other functions in this class, used to manage filters - when using the Python ICAT backend. These steps are the same with the different + when using the Python ICAT. These steps are the same with the different types of requests that utilise filters, therefore this function helps to reduce code duplication diff --git a/datagateway_api/src/common/filters.py b/datagateway_api/src/common/filters.py index 3b5270c3..72584bc9 100644 --- a/datagateway_api/src/common/filters.py +++ b/datagateway_api/src/common/filters.py @@ -21,8 +21,8 @@ class WhereFilter(QueryFilter): precedence = 1 def __init__(self, field, value, operation): - # The field is set to None as a precaution but this should be set by the - # individual backend since backends deal with this data differently + # The field is set to None as a precaution but this should be set + # when initialising Python ICAT self.field = None self.value = value self.operation = operation diff --git a/datagateway_api/src/common/helpers.py b/datagateway_api/src/common/helpers.py index 79d8c7ec..dd9c3616 100644 --- a/datagateway_api/src/common/helpers.py +++ b/datagateway_api/src/common/helpers.py @@ -17,7 +17,7 @@ FilterError, MissingCredentialsError, ) -from datagateway_api.src.datagateway_api.database import models +from datagateway_api.src.datagateway_api.icat import models from datagateway_api.src.resources.entities.entity_endpoint_dict import endpoints log = logging.getLogger() @@ -137,7 +137,7 @@ def get_entity_object_from_name(entity_name): :param entity_name: Name of the entity to fetch a version from this model :type entity_name: :class:`str` :return: Object of the entity requested (e.g. - :class:`.datagateway_api.database.models.INVESTIGATIONINSTRUMENT`) + :class:`.datagateway_api.icat.models.INVESTIGATIONINSTRUMENT`) :raises: KeyError: If an entity model cannot be found as a class in this model """ try: diff --git a/datagateway_api/src/datagateway_api/backend.py b/datagateway_api/src/datagateway_api/backend.py deleted file mode 100644 index ec9d8e74..00000000 --- a/datagateway_api/src/datagateway_api/backend.py +++ /dev/null @@ -1,153 +0,0 @@ -from abc import ABC, abstractmethod - - -class Backend(ABC): - """ - Abstact base class for implementations of a backend to inherit from - """ - - @abstractmethod - def ping(self): - """ - Endpoint requiring no authentication to check the API is alive and does a basic - check to ensure the connection method to ICAT is working - :returns: String to tell user the API is OK - """ - pass - - @abstractmethod - def login(self, credentials): - """ - Attempt to log a user in using the provided credentials - :param credentials: The user's credentials (including mechanism). Credentials - should take the following format in JSON: - { username: "value", password: "value", mechanism: "value"} - :returns: a session ID - """ - pass - - @abstractmethod - def get_session_details(self, session_id): - """ - Returns the details of a user's session - :param session_id: The user's session ID - :returns: The user's session details - """ - pass - - @abstractmethod - def refresh(self, session_id): - """ - Attempts to refresh a user's session - :param session_id: The user's session ID - :returns: the user's refreshed session ID - """ - pass - - @abstractmethod - def logout(self, session_id): - """ - Logs a user out - :param session_id: The user's session ID - """ - pass - - @abstractmethod - def get_with_filters(self, session_id, entity_type, filters): - """ - Given a list of filters supplied in json format, returns entities that match the - filters for the given entity type - - :param session_id: The session id of the requesting user - :param entity_type: The type of entity - :param filters: The list of filters to be applied - :return: A list of the matching entities in json format - """ - pass - - @abstractmethod - def create(self, session_id, entity_type, data): - """ - Create one or more entities, from the given list containing json. Each entity - must not contain its ID - - :param session_id: The session id of the requesting user - :param entity_type: The type of entity - :param data: The entities to be created - :return: The created entities. - """ - pass - - @abstractmethod - def update(self, session_id, entity_type, data): - """ - Update one or more entities, from the given list containing json. Each entity - must contain its ID - - :param session_id: The session id of the requesting user - :param entity_type: The type of entity - :param data: the list of updated values or a dictionary - :return: The list of updated entities. - """ - pass - - @abstractmethod - def get_one_with_filters(self, session_id, entity_type, filters): - """ - Returns the first entity that matches a given filter, for a given entity type - - :param session_id: The session id of the requesting user - :param entity_type: The type of entity - :param filters: the filter to be applied to the query - :return: the first entity matching the filter - """ - pass - - @abstractmethod - def count_with_filters(self, session_id, entity_type, filters): - """ - Returns the count of the entities that match a given filter for a given entity - type - - :param session_id: The session id of the requesting user - :param entity_type: The type of entity - :param filters: the filters to be applied to the query - :return: int: the count of the entities - """ - pass - - @abstractmethod - def get_with_id(self, session_id, entity_type, id_): - """ - Gets the entity matching the given ID for the given entity type - - :param session_id: The session id of the requesting user - :param entity_type: The type of entity - :param id_: the id of the record to find - :return: the entity retrieved - """ - pass - - @abstractmethod - def delete_with_id(self, session_id, entity_type, id_): - """ - Deletes the row matching the given ID for the given entity type - - :param session_id: The session id of the requesting user - :param table: the table to be searched - :param id_: the id of the record to delete - """ - pass - - @abstractmethod - def update_with_id(self, session_id, entity_type, id_, data): - """ - Updates the row matching the given ID for the given entity type - - :param session_id: The session id of the requesting user - :param entity_type: The type of entity - :param id_: the id of the record to update - :param data: The dictionary that the entity should be updated with - :return: The updated entity. - """ - pass diff --git a/datagateway_api/src/datagateway_api/backends.py b/datagateway_api/src/datagateway_api/backends.py deleted file mode 100644 index a5767792..00000000 --- a/datagateway_api/src/datagateway_api/backends.py +++ /dev/null @@ -1,29 +0,0 @@ -import sys - -from datagateway_api.src.datagateway_api.database.backend import DatabaseBackend -from datagateway_api.src.datagateway_api.icat.backend import PythonICATBackend - - -def create_backend(backend_type): - """ - Create an instance of a backend dependent on the value parsed into the function. The - value will typically be from the contents of `config.yaml`, however when creating a - backend during automated tests the value will be from the Flask app's config (which - will be set in the API's config at `common.config` - - The API will exit if a valid value isn't given. - - :param backend_type: The type of backend that should be created and used for the API - :type backend_type: :class:`str` - :return: Either an instance of `common.database.backend.DatabaseBackend` or - `common.icat.backend.PythonICATBackend` - """ - - if backend_type == "db": - backend = DatabaseBackend() - elif backend_type == "python_icat": - backend = PythonICATBackend() - else: - sys.exit(f"Invalid config value '{backend_type}' for config option backend") - - return backend diff --git a/datagateway_api/src/datagateway_api/database/__init__.py b/datagateway_api/src/datagateway_api/database/__init__.py deleted file mode 100644 index e69de29b..00000000 diff --git a/datagateway_api/src/datagateway_api/database/backend.py b/datagateway_api/src/datagateway_api/database/backend.py deleted file mode 100644 index 2e5e4ac8..00000000 --- a/datagateway_api/src/datagateway_api/database/backend.py +++ /dev/null @@ -1,125 +0,0 @@ -import datetime -import logging -import uuid - -from sqlalchemy import inspect -from sqlalchemy.exc import SQLAlchemyError - -from datagateway_api.src.common.constants import Constants -from datagateway_api.src.common.exceptions import AuthenticationError, DatabaseError -from datagateway_api.src.common.helpers import ( - get_entity_object_from_name, - queries_records, -) -from datagateway_api.src.datagateway_api.backend import Backend -from datagateway_api.src.datagateway_api.database.helpers import ( - create_rows_from_json, - db, - delete_row_by_id, - get_filtered_row_count, - get_first_filtered_row, - get_row_by_id, - get_rows_by_filter, - insert_row_into_table, - patch_entities, - requires_session_id, - update_row_from_id, -) -from datagateway_api.src.datagateway_api.database.models import SESSION - - -log = logging.getLogger() - - -class DatabaseBackend(Backend): - """ - Class that contains functions to access and modify data in an ICAT database directly - """ - - def ping(self, **kwargs): - log.info("Pinging DB connection to ensure API is alive and well") - - try: - inspector = inspect(db.engine) - tables = inspector.get_table_names() - log.debug("Tables on ping: %s", tables) - except SQLAlchemyError as e: - raise DatabaseError(e) - - return Constants.PING_OK_RESPONSE - - def login(self, credentials, **kwargs): - if credentials["username"] == "user" and credentials["password"] == "password": - session_id = str(uuid.uuid1()) - insert_row_into_table( - SESSION, - SESSION( - id=session_id, - username=f"{credentials['mechanism']}/root", - expireDateTime=datetime.datetime.now() + datetime.timedelta(days=1), - ), - ) - return session_id - else: - raise AuthenticationError("Username and password are incorrect") - - @requires_session_id - def get_session_details(self, session_id, **kwargs): - return get_row_by_id(SESSION, session_id).to_dict() - - @requires_session_id - def refresh(self, session_id, **kwargs): - return session_id - - @requires_session_id - @queries_records - def logout(self, session_id, **kwargs): - return delete_row_by_id(SESSION, session_id) - - @requires_session_id - @queries_records - def get_with_filters(self, session_id, entity_type, filters, **kwargs): - table = get_entity_object_from_name(entity_type) - return get_rows_by_filter(table, filters) - - @requires_session_id - @queries_records - def create(self, session_id, entity_type, data, **kwargs): - table = get_entity_object_from_name(entity_type) - return create_rows_from_json(table, data) - - @requires_session_id - @queries_records - def update(self, session_id, entity_type, data, **kwargs): - table = get_entity_object_from_name(entity_type) - return patch_entities(table, data) - - @requires_session_id - @queries_records - def get_one_with_filters(self, session_id, entity_type, filters, **kwargs): - table = get_entity_object_from_name(entity_type) - return get_first_filtered_row(table, filters) - - @requires_session_id - @queries_records - def count_with_filters(self, session_id, entity_type, filters, **kwargs): - table = get_entity_object_from_name(entity_type) - return get_filtered_row_count(table, filters) - - @requires_session_id - @queries_records - def get_with_id(self, session_id, entity_type, id_, **kwargs): - table = get_entity_object_from_name(entity_type) - return get_row_by_id(table, id_).to_dict() - - @requires_session_id - @queries_records - def delete_with_id(self, session_id, entity_type, id_, **kwargs): - table = get_entity_object_from_name(entity_type) - return delete_row_by_id(table, id_) - - @requires_session_id - @queries_records - def update_with_id(self, session_id, entity_type, id_, data, **kwargs): - table = get_entity_object_from_name(entity_type) - return update_row_from_id(table, id_, data) diff --git a/datagateway_api/src/datagateway_api/database/filters.py b/datagateway_api/src/datagateway_api/database/filters.py deleted file mode 100644 index e8a9d85b..00000000 --- a/datagateway_api/src/datagateway_api/database/filters.py +++ /dev/null @@ -1,220 +0,0 @@ -import logging - -from sqlalchemy import asc, desc - -from datagateway_api.src.common.exceptions import FilterError, MultipleIncludeError -from datagateway_api.src.common.filters import ( - DistinctFieldFilter, - IncludeFilter, - LimitFilter, - OrderFilter, - SkipFilter, - WhereFilter, -) -from datagateway_api.src.common.helpers import get_entity_object_from_name - - -log = logging.getLogger() - - -class DatabaseFilterUtilities: - """ - Class containing utility functions used in the WhereFilter and DistinctFilter - - In this class, the terminology of 'included entities' has been made more generic to - 'related entities'. When these functions are used with the WhereFilter, the related - entities are in fact included entities (entities which are also present in the input - of an include filter in the same request). However, when these functions are used - with the DistinctFilter, they are related entities, not included entities as there's - no requirement for the entity names to also be present in an include filter (this is - to match the ICAT backend) - """ - - def __init__(self): - self.field = None - self.related_field = None - self.related_related_field = None - - def extract_filter_fields(self, field): - """ - Extract the related fields names and put them into separate variables - - :param field: ICAT field names, separated by dots - :type field: :class:`str` - :raises ValueError: If the maximum related/included depth is exceeded - """ - - # Flushing fields in case they have been previously set - self.field = None - self.related_field = None - self.related_related_field = None - - fields = field.split(".") - related_depth = len(fields) - - log.debug("Fields: %s, Related Depth: %d", fields, related_depth) - - if related_depth == 1: - self.field = fields[0] - self.distinct_join_flag = True - elif related_depth == 2: - self.field = fields[0] - self.related_field = fields[1] - elif related_depth == 3: - self.field = fields[0] - self.related_field = fields[1] - self.related_related_field = fields[2] - else: - raise ValueError(f"Maximum related depth exceeded. {field}'s depth > 3") - - def add_query_join(self, query): - """ - Adds any required JOINs to the query if any related fields have been used in the - filter - - :param query: The query to have filters applied to - :type query: :class:`.common.datagateway_api.database.helpers.[QUERY]` - """ - - if self.related_related_field: - included_table = get_entity_object_from_name(self.field) - included_included_table = get_entity_object_from_name(self.related_field) - query.base_query = query.base_query.join(included_table).join( - included_included_table, - ) - elif self.related_field: - included_table = get_entity_object_from_name(self.field) - query.base_query = query.base_query.join(included_table) - - def get_entity_model_for_filter(self, query): - """ - Fetches the appropriate entity model based on the contents of the instance - variables of this class - - :param query: The query to have filters applied to - :type query: :class:`.common.datagateway_api.database.helpers.[QUERY]` - :return: Entity model of the field (usually the field relating to the endpoint - the request is coming from) - """ - if self.related_related_field: - included_included_table = get_entity_object_from_name(self.related_field) - field = self._get_field(included_included_table, self.related_related_field) - elif self.related_field: - included_table = get_entity_object_from_name(self.field) - field = self._get_field(included_table, self.related_field) - else: - # No related fields - field = self._get_field(query.table, self.field) - - return field - - def _get_field(self, table, field): - try: - return getattr(table, field) - except AttributeError: - raise FilterError(f"Unknown attribute {field} on table {table.__name__}") - - -class DatabaseWhereFilter(WhereFilter, DatabaseFilterUtilities): - def __init__(self, field, value, operation): - WhereFilter.__init__(self, field, value, operation) - DatabaseFilterUtilities.__init__(self) - - self.extract_filter_fields(field) - - def apply_filter(self, query): - self.add_query_join(query) - field = self.get_entity_model_for_filter(query) - - if self.operation == "eq": - query.base_query = query.base_query.filter(field == self.value) - elif self.operation == "ne": - query.base_query = query.base_query.filter(field != self.value) - elif self.operation == "like": - query.base_query = query.base_query.filter(field.like(f"%{self.value}%")) - elif self.operation == "nlike": - query.base_query = query.base_query.filter(field.notlike(f"%{self.value}%")) - elif self.operation == "lt": - query.base_query = query.base_query.filter(field < self.value) - elif self.operation == "lte": - query.base_query = query.base_query.filter(field <= self.value) - elif self.operation == "gt": - query.base_query = query.base_query.filter(field > self.value) - elif self.operation == "gte": - query.base_query = query.base_query.filter(field >= self.value) - elif self.operation == "in": - query.base_query = query.base_query.filter(field.in_(self.value)) - else: - raise FilterError( - f" Bad operation given to where filter. operation: {self.operation}", - ) - - -class DatabaseDistinctFieldFilter(DistinctFieldFilter, DatabaseFilterUtilities): - def __init__(self, fields): - DistinctFieldFilter.__init__(self, fields) - DatabaseFilterUtilities.__init__(self) - - def apply_filter(self, query): - query.is_distinct_fields_query = True - - try: - distinct_fields = [] - for field_name in self.fields: - self.extract_filter_fields(field_name) - distinct_fields.append(self.get_entity_model_for_filter(query)) - - # Base query must be set to a DISTINCT query before adding JOINs - if these - # actions are done in the opposite order, the JOINs will overwrite the - # SELECT multiple and effectively turn the query into a `SELECT *` - query.base_query = ( - query.session.query(*distinct_fields) - .select_from(query.table) - .distinct() - ) - - for field_name in self.fields: - self.extract_filter_fields(field_name) - self.add_query_join(query) - except AttributeError: - raise FilterError("Bad field requested") - - -class DatabaseOrderFilter(OrderFilter): - def __init__(self, field, direction): - super().__init__(field, direction) - - def apply_filter(self, query): - if self.direction.upper() == "ASC": - query.base_query = query.base_query.order_by(asc(self.field.upper())) - elif self.direction.upper() == "DESC": - query.base_query = query.base_query.order_by(desc(self.field.upper())) - else: - raise FilterError(f" Bad filter: {self.direction}") - - -class DatabaseSkipFilter(SkipFilter): - def __init__(self, skip_value): - super().__init__(skip_value) - - def apply_filter(self, query): - query.base_query = query.base_query.offset(self.skip_value) - - -class DatabaseLimitFilter(LimitFilter): - def __init__(self, limit_value): - self.limit_value = limit_value - - def apply_filter(self, query): - query.base_query = query.base_query.limit(self.limit_value) - - -class DatabaseIncludeFilter(IncludeFilter): - def __init__(self, included_filters): - super().__init__(included_filters) - - def apply_filter(self, query): - if not query.include_related_entities: - query.include_related_entities = True - else: - raise MultipleIncludeError() diff --git a/datagateway_api/src/datagateway_api/database/helpers.py b/datagateway_api/src/datagateway_api/database/helpers.py deleted file mode 100644 index debe10fa..00000000 --- a/datagateway_api/src/datagateway_api/database/helpers.py +++ /dev/null @@ -1,388 +0,0 @@ -from abc import ABC, abstractmethod -import datetime -from functools import wraps -import logging - -from flask_sqlalchemy import SQLAlchemy - -from datagateway_api.src.common.exceptions import ( - AuthenticationError, - BadRequestError, - MissingRecordError, -) -from datagateway_api.src.common.filter_order_handler import FilterOrderHandler -from datagateway_api.src.common.helpers import map_distinct_attributes_to_results -from datagateway_api.src.datagateway_api.database.filters import ( - DatabaseDistinctFieldFilter, - DatabaseIncludeFilter as IncludeFilter, - DatabaseWhereFilter as WhereFilter, -) -from datagateway_api.src.datagateway_api.database.models import SESSION - - -log = logging.getLogger() - -db = SQLAlchemy() - - -def requires_session_id(method): - """ - Decorator for database backend methods that makes sure a valid session_id is - provided. It expects that session_id is the second argument supplied to the function - - :param method: The method for the backend operation - :raises AuthenticationError, if a valid session_id is not provided with the request - """ - - @wraps(method) - def wrapper_requires_session(*args, **kwargs): - log.info(" Authenticating consumer") - 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() - log.info(" Consumer authenticated") - return method(*args, **kwargs) - else: - log.info(" Could not authenticate consumer, closing DB session") - session.close() - raise AuthenticationError("Forbidden") - - return wrapper_requires_session - - -class Query(ABC): - """ - The base query class that all other queries extend from. This defines the enter and - exit methods, used to handle sessions. It is expected that all queries would be used - with the 'with' keyword in most cases for this reason. - """ - - @abstractmethod - def __init__(self, table): - self.session = db.session - self.table = table - self.base_query = self.session.query(table) - - def __enter__(self): - return self - - def __exit__(self, exc_type, exc_val, exc_tb): - log.info("Closing DB session") - self.session.close() - - @abstractmethod - def execute_query(self): - pass - - def commit_changes(self): - """ - Commits all changes to the database and closes the session - """ - log.info(" Committing changes to %s", self.table) - try: - self.session.commit() - except Exception as e: - log.error("Error whilst committing changes to %s, rolling back", self.table) - self.session.rollback() - raise BadRequestError(f"Bad request: {e}") - - -class CountQuery(Query): - def __init__(self, table): - super().__init__(table) - self.include_related_entities = False - - def execute_query(self): - self.commit_changes() - - def get_count(self): - try: - return self.base_query.count() - finally: - self.execute_query() - - -class ReadQuery(Query): - def __init__(self, table): - super().__init__(table) - self.include_related_entities = False - self.is_distinct_fields_query = False - - def commit_changes(self): - log.info("Closing DB session") - - def execute_query(self): - self.commit_changes() - - def get_single_result(self): - result = self.base_query.first() - if result is not None: - return result - raise MissingRecordError(" No result found") - - def get_all_results(self): - results = self.base_query.all() - if results is not None: - return results - raise MissingRecordError(" No results found") - - -class CreateQuery(Query): - def __init__(self, table, row): - super().__init__(table) - self.row = row - self.inserted_row = None - - def execute_query(self): - """ - Determines if the row is a row object or dictionary then commits it to the table - """ - if type(self.row) is not dict: - record = self.row - else: - record = self.table() - record.update_from_dict(self.row) - record.createTime = datetime.datetime.now() - record.modTime = datetime.datetime.now() - record.createId = "user" - record.modId = "user" - self.session.add(record) - self.commit_changes() - self.session.refresh(record) - self.inserted_row = record - - -class UpdateQuery(Query): - def __init__(self, table, row, new_values): - super().__init__(table) - self.row = row - self.new_values = new_values - - def execute_query(self): - log.info(" Updating row in %s", self.table) - self.row.update_from_dict(self.new_values) - self.session.add(self.row) - self.commit_changes() - - -class DeleteQuery(Query): - def __init__(self, table, row): - super().__init__(table) - self.row = row - - def execute_query(self): - log.info(" Deleting row %s from %s", self.row, self.table.__tablename__) - self.session.delete(self.row) - self.commit_changes() - - -def insert_row_into_table(table, row): - """ - Insert the given row into its table - :param table: The table to be inserted to - :param row: The row to be inserted - """ - with CreateQuery(table, row) as create_query: - create_query.execute_query() - - -def create_row_from_json(table, data): - """ - Given a json dictionary create a row in the table from it - :param table: the table for the row to be inserted into - :param data: the dictionary containing the values - :return: The inserted row as a dictionary - """ - with CreateQuery(table, data) as create_query: - create_query.execute_query() - return create_query.inserted_row.to_dict() - - -def create_rows_from_json(table, data): - """ - Given a List containing dictionary representations of entities, or a dictionary - representation of an entity, insert the entities into the table and return the - created entities - - :param table: The table to insert the entities in - :param data: The entities to be inserted - :return: The inserted entities - """ - if type(data) is list: - return [create_row_from_json(table, entity) for entity in data] - return create_row_from_json(table, data) - - -def get_row_by_id(table, id_): - """ - Gets the row matching the given ID from the given table, raises MissingRecordError - if it can not be found - - :param table: the table to be searched - :param id_: the id of the record to find - :return: the record retrieved - """ - with ReadQuery(table) as read_query: - log.info(" Querying %s for record with ID: %s", table.__tablename__, id_) - where_filter = WhereFilter("id", id_, "eq") - where_filter.apply_filter(read_query) - return read_query.get_single_result() - - -def delete_row_by_id(table, id_): - """ - Deletes the row matching the given ID from the given table, raises - MissingRecordError if it can not be found - - :param table: the table to be searched - :param id_: the id of the record to delete - """ - log.info(" Deleting row from %s with ID: %s", table.__tablename__, id_) - row = get_row_by_id(table, id_) - with DeleteQuery(table, row) as delete_query: - delete_query.execute_query() - - -def update_row_from_id(table, id_, new_values): - """ - Updates a record in a table - - :param table: The table the record is in - :param id_: The id of the record - :param new_values: A JSON string containing what columns are to be updated - """ - row = get_row_by_id(table, id_) - with UpdateQuery(table, row, new_values) as update_query: - update_query.execute_query() - - -def get_filtered_read_query_results(filter_handler, filters, query): - """ - Given a filter handler, list of filters and a query. Apply the filters and execute - the query - - :param filter_handler: The filter handler to apply the filters - :param filters: The filters to be applied - :param query: The query for the filters to be applied to - :return: The results of the query as a list of dictionaries - """ - filter_handler.add_filters(filters) - filter_handler.apply_filters(query) - results = query.get_all_results() - if query.is_distinct_fields_query: - return _get_distinct_fields_as_dicts(filters, results) - if query.include_related_entities: - return _get_results_with_include(filters, results) - return list(map(lambda x: x.to_dict(), results)) - - -def _get_results_with_include(filters, results): - """ - Given a list of entities and a list of filters, use the include filter to nest the - included entities requested in the include filter given - - :param filters: The list of filters - :param results: The list of entities - :return: A list of nested dictionaries representing the entity results - """ - for query_filter in filters: - if type(query_filter) is IncludeFilter: - return [x.to_nested_dict(query_filter.included_filters) for x in results] - - -def _get_distinct_fields_as_dicts(filters, results): - """ - Given a list of column results return a list of dictionaries where each column name - is the key and the column value is the dictionary key value - - :param results: A list of sql alchemy result objects - :return: A list of dictionary representations of the sqlalchemy result objects - """ - distinct_fields = [] - for query_filter in filters: - if type(query_filter) is DatabaseDistinctFieldFilter: - distinct_fields.extend(query_filter.fields) - - dictionaries = [] - for result in results: - dictionary = map_distinct_attributes_to_results(distinct_fields, result) - dictionaries.append(dictionary) - - return dictionaries - - -def get_rows_by_filter(table, filters): - """ - Given a list of filters supplied in json format, returns entities that match the - filters from the given table - - :param table: The table to checked - :param filters: The list of filters to be applied - :return: A list of the rows returned in dictionary form - """ - with ReadQuery(table) as query: - filter_handler = FilterOrderHandler() - return get_filtered_read_query_results(filter_handler, filters, query) - - -def get_first_filtered_row(table, filters): - """ - returns the first row that matches a given filter, in a given table - :param table: the table to be checked - :param filters: the filter to be applied to the query - :return: the first row matching the filter - """ - log.info(" Getting first filtered row for %s", table.__tablename__) - try: - result = get_rows_by_filter(table, filters)[0] - except IndexError: - raise MissingRecordError() - return result - - -def get_filtered_row_count(table, filters): - """ - returns the count of the rows that match a given filter in a given table - :param table: the table to be checked - :param filters: the filters to be applied to the query - :return: int: the count of the rows - """ - - log.info(" getting count for %s", table.__tablename__) - with CountQuery(table) as count_query: - filter_handler = FilterOrderHandler() - filter_handler.add_filters(filters) - filter_handler.apply_filters(count_query) - return count_query.get_count() - - -def patch_entities(table, json_list): - """ - Update one or more rows in the given table, from the given list containing json. - Each entity must contain its ID - - :param table: The table of the entities - :param json_list: the list of updated values or a dictionary - :return: The list of updated rows. - """ - log.info(" Patching entities in %s", table.__tablename__) - results = [] - if type(json_list) is dict: - for key in json_list: - if key.upper() == "ID": - update_row_from_id(table, json_list[key], json_list) - result = get_row_by_id(table, json_list[key]) - results.append(result.to_dict()) - else: - for entity in json_list: - for key in entity: - if key.upper() == "ID": - update_row_from_id(table, entity[key], entity) - result = get_row_by_id(table, entity[key]) - results.append(result.to_dict()) - if len(results) == 0: - raise BadRequestError(f" Bad request made, request: {json_list}") - - return results diff --git a/datagateway_api/src/datagateway_api/icat/filters.py b/datagateway_api/src/datagateway_api/icat/filters.py index 80dba767..58a6a76b 100644 --- a/datagateway_api/src/datagateway_api/icat/filters.py +++ b/datagateway_api/src/datagateway_api/icat/filters.py @@ -82,8 +82,7 @@ def create_filter(self): self.value = str(self.value).replace("[", "(").replace("]", ")") # DataGateway Search can send requests with blank lists. Adding NULL to the - # filter prevents the API from returning a 500. An empty list will be - # returned instead, equivalent to the DB backend + # filter prevents the API from returning a 500. if self.value == "()": self.value = "(NULL)" @@ -95,8 +94,7 @@ def create_filter(self): self.value = str(self.value).replace("[", "(").replace("]", ")") # DataGateway Search can send requests with blank lists. Adding NULL to the - # filter prevents the API from returning a 500. An empty list will be - # returned instead, equivalent to the DB backend + # filter prevents the API from returning a 500. if self.value == "()": self.value = "(NULL)" diff --git a/datagateway_api/src/datagateway_api/icat/helpers.py b/datagateway_api/src/datagateway_api/icat/helpers.py index 35922cf1..84e6494e 100644 --- a/datagateway_api/src/datagateway_api/icat/helpers.py +++ b/datagateway_api/src/datagateway_api/icat/helpers.py @@ -37,7 +37,7 @@ def requires_session_id(method): """ - Decorator for Python ICAT backend methods that looks out for session errors when + Decorator for Python ICAT methods that looks out for session errors when using the API. The API call runs and an ICATSessionError may be raised due to an expired session, invalid session ID etc. @@ -50,7 +50,7 @@ def requires_session_id(method): decorator is applied, which is reasonable to assume considering the current method signatures of all the endpoints. - :param method: The method for the backend operation + :param method: The method for the python ICAT operation :raises AuthenticationError: If a valid session_id is not provided with the request """ @@ -61,7 +61,8 @@ def wrapper_requires_session(*args, **kwargs): client = get_cached_client(args[1], client_pool) client.sessionId = args[1] - # Client object put into kwargs so it can be accessed by backend functions + # Client object put into kwargs so it can be accessed by + # python ICAT functions kwargs["client"] = client # Find out if session has expired diff --git a/datagateway_api/src/datagateway_api/database/models.py b/datagateway_api/src/datagateway_api/icat/models.py similarity index 99% rename from datagateway_api/src/datagateway_api/database/models.py rename to datagateway_api/src/datagateway_api/icat/models.py index 33738224..74dbfbe6 100644 --- a/datagateway_api/src/datagateway_api/database/models.py +++ b/datagateway_api/src/datagateway_api/icat/models.py @@ -83,7 +83,7 @@ def _make_serializable(self, field): :return: The converted field """ if isinstance(field, datetime): - # Add timezone info to match ICAT backend + # Add timezone info to match for python ICAT field = field.replace(tzinfo=tzlocal()) return DateHandler.datetime_object_to_str(field) elif isinstance(field, Decimal): diff --git a/datagateway_api/src/datagateway_api/icat/backend.py b/datagateway_api/src/datagateway_api/icat/python_icat.py similarity index 54% rename from datagateway_api/src/datagateway_api/icat/backend.py rename to datagateway_api/src/datagateway_api/icat/python_icat.py index 83990117..f09fe37c 100644 --- a/datagateway_api/src/datagateway_api/icat/backend.py +++ b/datagateway_api/src/datagateway_api/icat/python_icat.py @@ -5,7 +5,6 @@ from datagateway_api.src.common.constants import Constants from datagateway_api.src.common.exceptions import AuthenticationError, PythonICATError from datagateway_api.src.common.helpers import queries_records -from datagateway_api.src.datagateway_api.backend import Backend from datagateway_api.src.datagateway_api.icat.helpers import ( create_entities, delete_entity_by_id, @@ -26,12 +25,17 @@ log = logging.getLogger() -class PythonICATBackend(Backend): +class PythonICAT: """ Class that contains functions to access and modify data in an ICAT database directly """ def ping(self, **kwargs): + """ + Endpoint requiring no authentication to check the API is alive and does a basic + check to ensure the connection method to ICAT is working. + :returns: String to tell user the API is OK. + """ log.info("Pinging ICAT to ensure API is alive and well") client_pool = kwargs.get("client_pool") @@ -46,6 +50,13 @@ def ping(self, **kwargs): return Constants.PING_OK_RESPONSE def login(self, credentials, **kwargs): + """ + Attempt to log a user in using the provided credentials. + :param credentials: The user's credentials (including mechanism). Credentials + should take the following format in JSON: + { username: "value", password: "value", mechanism: "value"} + :returns: a session ID. + """ log.info("Logging in to get session ID") client_pool = kwargs.get("client_pool") @@ -71,56 +82,130 @@ def login(self, credentials, **kwargs): @requires_session_id def get_session_details(self, session_id, **kwargs): + """ + Returns the details of a user's session. + :param session_id: The user's session ID. + :returns: The user's session details. + """ log.info("Getting session details for session: %s", session_id) return get_session_details_helper(kwargs.get("client")) @requires_session_id def refresh(self, session_id, **kwargs): + """ + Attempts to refresh a user's session. + :param session_id: The user's session ID. + :returns: the user's refreshed session ID. + """ log.info("Refreshing session: %s", session_id) return refresh_client_session(kwargs.get("client")) @requires_session_id @queries_records def logout(self, session_id, **kwargs): + """ + Logs a user out. + :param session_id: The user's session ID. + """ log.info("Logging out of the Python ICAT client") return logout_icat_client(kwargs.get("client")) @requires_session_id @queries_records def get_with_filters(self, session_id, entity_type, filters, **kwargs): + """ + Given a list of filters supplied in JSON format, returns entities that match + the filters for the given entity type. + :param session_id: The session ID of the requesting user. + :param entity_type: The type of entity. + :param filters: The list of filters to be applied. + :return: A list of the matching entities in JSON format. + """ return get_entity_with_filters(kwargs.get("client"), entity_type, filters) @requires_session_id @queries_records def create(self, session_id, entity_type, data, **kwargs): + """ + Create one or more entities, from the given list containing JSON. Each entity + must not contain its ID. + :param session_id: The session ID of the requesting user. + :param entity_type: The type of entity. + :param data: The entities to be created. + :return: The created entities. + """ return create_entities(kwargs.get("client"), entity_type, data) @requires_session_id @queries_records def update(self, session_id, entity_type, data, **kwargs): + """ + Update one or more entities, from the given list containing JSON. Each entity + must contain its ID. + :param session_id: The session ID of the requesting user. + :param entity_type: The type of entity. + :param data: The list of updated values or a dictionary. + :return: The list of updated entities. + """ return update_entities(kwargs.get("client"), entity_type, data) @requires_session_id @queries_records def get_one_with_filters(self, session_id, entity_type, filters, **kwargs): + """ + Returns the first entity that matches a given filter, for a given entity type. + :param session_id: The session ID of the requesting user. + :param entity_type: The type of entity. + :param filters: The filter to be applied to the query. + :return: The first entity matching the filter. + """ return get_first_result_with_filters(kwargs.get("client"), entity_type, filters) @requires_session_id @queries_records def count_with_filters(self, session_id, entity_type, filters, **kwargs): + """ + Returns the count of the entities that match a given filter for a given entity + type. + :param session_id: The session ID of the requesting user. + :param entity_type: The type of entity. + :param filters: The filters to be applied to the query. + :return: int: The count of the entities. + """ return get_count_with_filters(kwargs.get("client"), entity_type, filters) @requires_session_id @queries_records def get_with_id(self, session_id, entity_type, id_, **kwargs): + """ + Gets the entity matching the given ID for the given entity type. + :param session_id: The session ID of the requesting user. + :param entity_type: The type of entity. + :param id_: The ID of the record to find. + :return: The entity retrieved. + """ return get_entity_by_id(kwargs.get("client"), entity_type, id_, True) @requires_session_id @queries_records def delete_with_id(self, session_id, entity_type, id_, **kwargs): + """ + Deletes the row matching the given ID for the given entity type. + :param session_id: The session ID of the requesting user. + :param entity_type: The type of entity. + :param id_: The ID of the record to delete. + """ return delete_entity_by_id(kwargs.get("client"), entity_type, id_) @requires_session_id @queries_records def update_with_id(self, session_id, entity_type, id_, data, **kwargs): + """ + Updates the row matching the given ID for the given entity type. + :param session_id: The session ID of the requesting user. + :param entity_type: The type of entity. + :param id_: The ID of the record to update. + :param data: The dictionary that the entity should be updated with. + :return: The updated entity. + """ return update_entity_by_id(kwargs.get("client"), entity_type, id_, data) diff --git a/datagateway_api/src/datagateway_api/query_filter_factory.py b/datagateway_api/src/datagateway_api/query_filter_factory.py index 27241f7a..717e3398 100644 --- a/datagateway_api/src/datagateway_api/query_filter_factory.py +++ b/datagateway_api/src/datagateway_api/query_filter_factory.py @@ -1,10 +1,14 @@ import logging from datagateway_api.src.common.base_query_filter_factory import QueryFilterFactory -from datagateway_api.src.common.config import Config -from datagateway_api.src.common.exceptions import ( - ApiError, - FilterError, +from datagateway_api.src.common.exceptions import FilterError +from datagateway_api.src.datagateway_api.icat.filters import ( + PythonICATDistinctFieldFilter as DistinctFieldFilter, + PythonICATIncludeFilter as IncludeFilter, + PythonICATLimitFilter as LimitFilter, + PythonICATOrderFilter as OrderFilter, + PythonICATSkipFilter as SkipFilter, + PythonICATWhereFilter as WhereFilter, ) log = logging.getLogger() @@ -16,11 +20,6 @@ def get_query_filter(request_filter, entity_name=None): """ Given a filter, return a matching Query filter object - The filters are imported inside this method to enable the unit tests to not rely - on the contents of `config.yaml`. If they're imported at the top of the file, - the backend type won't have been updated if the Flask app has been created from - an automated test (file imports occur before `create_api_endpoints()` executes). - :param request_filter: The filter to create the QueryFilter for :type request_filter: :class:`dict` :param entity_name: Not utilised in DataGateway API implementation of this @@ -29,35 +28,9 @@ def get_query_filter(request_filter, entity_name=None): used for both implementations :type entity_name: :class:`str` :return: The QueryFilter object created - :raises ApiError: If the backend type contains an invalid value :raises FilterError: If the filter name is not recognised """ - backend_type = Config.config.datagateway_api.backend - if backend_type == "db": - from datagateway_api.src.datagateway_api.database.filters import ( - DatabaseDistinctFieldFilter as DistinctFieldFilter, - DatabaseIncludeFilter as IncludeFilter, - DatabaseLimitFilter as LimitFilter, - DatabaseOrderFilter as OrderFilter, - DatabaseSkipFilter as SkipFilter, - DatabaseWhereFilter as WhereFilter, - ) - elif backend_type == "python_icat": - from datagateway_api.src.datagateway_api.icat.filters import ( - PythonICATDistinctFieldFilter as DistinctFieldFilter, - PythonICATIncludeFilter as IncludeFilter, - PythonICATLimitFilter as LimitFilter, - PythonICATOrderFilter as OrderFilter, - PythonICATSkipFilter as SkipFilter, - PythonICATWhereFilter as WhereFilter, - ) - else: - raise ApiError( - "Cannot select which implementation of filters to import, check the" - " config file has a valid backend type", - ) - filter_name = list(request_filter)[0].lower() if filter_name == "where": field = list(request_filter[filter_name].keys())[0] diff --git a/datagateway_api/src/resources/entities/entity_endpoint.py b/datagateway_api/src/resources/entities/entity_endpoint.py index bdbc23db..13d7a42a 100644 --- a/datagateway_api/src/resources/entities/entity_endpoint.py +++ b/datagateway_api/src/resources/entities/entity_endpoint.py @@ -7,7 +7,7 @@ ) -def get_endpoint(name, entity_type, backend, **kwargs): +def get_endpoint(name, entity_type, python_icat, **kwargs): """ Given an entity name, generate a flask_restful `Resource` class. In `create_api_endpoints()`, these generated classes are registered with the API e.g. @@ -17,8 +17,8 @@ def get_endpoint(name, entity_type, backend, **kwargs): :type name: :class:`str` :param entity_type: The entity the endpoint will use in queries :type entity_type: :class:`str` - :param backend: The backend instance used for processing requests - :type backend: :class:`DatabaseBackend` or :class:`PythonICATBackend` + :param python_icat: The python ICAT instance used for processing requests + :type python_icat: :class:`PythonICAT` :return: The generated endpoint class """ @@ -27,7 +27,7 @@ def get_endpoint(name, entity_type, backend, **kwargs): class Endpoint(Resource): def get(self): return ( - backend.get_with_filters( + python_icat.get_with_filters( get_session_id_from_auth_header(), entity_type, get_filters_from_query_string("datagateway_api"), @@ -72,7 +72,7 @@ def get(self): def post(self): return ( - backend.create( + python_icat.create( get_session_id_from_auth_header(), entity_type, request.json, @@ -118,7 +118,7 @@ def post(self): def patch(self): return ( - backend.update( + python_icat.update( get_session_id_from_auth_header(), entity_type, request.json, @@ -166,7 +166,7 @@ def patch(self): return Endpoint -def get_id_endpoint(name, entity_type, backend, **kwargs): +def get_id_endpoint(name, entity_type, python_icat, **kwargs): """ Given an entity name, generate a flask_restful `Resource` class. In `create_api_endpoints()`, these generated classes are registered with the API e.g. @@ -176,8 +176,8 @@ def get_id_endpoint(name, entity_type, backend, **kwargs): :type name: :class:`str` :param entity_type: The entity the endpoint will use in queries :type entity_type: :class:`str` - :param backend: The backend instance used for processing requests - :type backend: :class:`DatabaseBackend` or :class:`PythonICATBackend` + :param python_icat: The python ICAT instance used for processing requests + :type python_icat: :class:`PythonICAT` :return: The generated id endpoint class """ @@ -186,7 +186,7 @@ def get_id_endpoint(name, entity_type, backend, **kwargs): class EndpointWithID(Resource): def get(self, id_): return ( - backend.get_with_id( + python_icat.get_with_id( get_session_id_from_auth_header(), entity_type, id_, **kwargs, ), 200, @@ -223,7 +223,7 @@ def get(self, id_): """ def delete(self, id_): - backend.delete_with_id( + python_icat.delete_with_id( get_session_id_from_auth_header(), entity_type, id_, **kwargs, ) return "", 204 @@ -257,10 +257,10 @@ def delete(self, id_): def patch(self, id_): session_id = get_session_id_from_auth_header() - backend.update_with_id( + python_icat.update_with_id( session_id, entity_type, id_, request.json, **kwargs, ) - return backend.get_with_id(session_id, entity_type, id_, **kwargs), 200 + return python_icat.get_with_id(session_id, entity_type, id_, **kwargs), 200 patch.__doc__ = f""" --- @@ -304,7 +304,7 @@ def patch(self, id_): return EndpointWithID -def get_count_endpoint(name, entity_type, backend, **kwargs): +def get_count_endpoint(name, entity_type, python_icat, **kwargs): """ Given an entity name, generate a flask_restful `Resource` class. In `create_api_endpoints()`, these generated classes are registered with the API e.g. @@ -314,8 +314,8 @@ def get_count_endpoint(name, entity_type, backend, **kwargs): :type name: :class:`str` :param entity_type: The entity the endpoint will use in queries :type entity_type: :class:`str` - :param backend: The backend instance used for processing requests - :type backend: :class:`DatabaseBackend` or :class:`PythonICATBackend` + :param python_icat: The python ICAT instance used for processing requests + :type python_icat: :class:`PythonICAT` :return: The generated count endpoint class """ @@ -323,7 +323,7 @@ class CountEndpoint(Resource): def get(self): filters = get_filters_from_query_string("datagateway_api") return ( - backend.count_with_filters( + python_icat.count_with_filters( get_session_id_from_auth_header(), entity_type, filters, **kwargs, ), 200, @@ -361,7 +361,7 @@ def get(self): return CountEndpoint -def get_find_one_endpoint(name, entity_type, backend, **kwargs): +def get_find_one_endpoint(name, entity_type, python_icat, **kwargs): """ Given an entity name, generate a flask_restful `Resource` class. In `create_api_endpoints()`, these generated classes are registered with the API e.g. @@ -371,8 +371,8 @@ def get_find_one_endpoint(name, entity_type, backend, **kwargs): :type name: :class:`str` :param entity_type: The entity the endpoint will use in queries :type entity_type: :class:`str` - :param backend: The backend instance used for processing requests - :type backend: :class:`DatabaseBackend` or :class:`PythonICATBackend` + :param python_icat: The python ICAT instance used for processing requests + :type python_icat: :class:`PythonICAT` :return: The generated findOne endpoint class """ @@ -382,7 +382,7 @@ class FindOneEndpoint(Resource): def get(self): filters = get_filters_from_query_string("datagateway_api") return ( - backend.get_one_with_filters( + python_icat.get_one_with_filters( get_session_id_from_auth_header(), entity_type, filters, **kwargs, ), 200, diff --git a/datagateway_api/src/resources/non_entities/ping_endpoint.py b/datagateway_api/src/resources/non_entities/ping_endpoint.py index 5b595095..5f69585e 100644 --- a/datagateway_api/src/resources/non_entities/ping_endpoint.py +++ b/datagateway_api/src/resources/non_entities/ping_endpoint.py @@ -1,14 +1,14 @@ from flask_restful import Resource -def ping_endpoint(backend, **kwargs): +def ping_endpoint(python_icat, **kwargs): """ - Generate a flask_restful Resource class using the configured backend. In main.py + Generate a flask_restful Resource class using python ICAT. In main.py these generated classes are registered with the api e.g. `api.add_resource(get_endpoint("Datafiles", DATAFILE), "/datafiles")` - :param backend: The backend instance used for processing requests - :type backend: :class:`DatabaseBackend` or :class:`PythonICATBackend` + :param python_icat: The python ICAT instance used for processing requests + :type python_icat: :class:`PythonICAT` :return: The generated ping endpoint class """ @@ -24,7 +24,7 @@ def get(self): - Ping responses: 200: - description: Success - the API is responsive on the backend configured + description: Success - the API is responsive on the python ICAT server content: application/json: schema: @@ -34,6 +34,6 @@ def get(self): 500: description: Pinging the API's connection method has gone wrong """ - return backend.ping(**kwargs), 200 + return python_icat.ping(**kwargs), 200 return Ping diff --git a/datagateway_api/src/resources/non_entities/sessions_endpoints.py b/datagateway_api/src/resources/non_entities/sessions_endpoints.py index 4e25f988..54a18df3 100644 --- a/datagateway_api/src/resources/non_entities/sessions_endpoints.py +++ b/datagateway_api/src/resources/non_entities/sessions_endpoints.py @@ -10,14 +10,14 @@ log = logging.getLogger() -def session_endpoints(backend, **kwargs): +def session_endpoints(python_icat, **kwargs): """ - Generate a flask_restful Resource class using the configured backend. In main.py + Generate a flask_restful Resource class using python ICAT. In main.py these generated classes are registered with the api e.g. `api.add_resource(get_endpoint("Datafiles", DATAFILE), "/datafiles")` - :param backend: The backend instance used for processing requests - :type backend: :class:`DatabaseBackend` or :class:`PythonICATBackend` + :param python_icat: The python ICAT instance used for processing requests + :type python_icat: :class:`PythonICAT` :return: The generated session endpoint class """ @@ -74,7 +74,7 @@ def post(self): if not ("mechanism" in request.json): request.json["mechanism"] = "simple" try: - return {"sessionID": backend.login(request.json, **kwargs)}, 201 + return {"sessionID": python_icat.login(request.json, **kwargs)}, 201 except AuthenticationError: return "Forbidden", 403 @@ -99,7 +99,7 @@ def delete(self): 404: description: Not Found - Unable to find session ID """ - backend.logout(get_session_id_from_auth_header(), **kwargs) + python_icat.logout(get_session_id_from_auth_header(), **kwargs) return "", 200 def get(self): @@ -137,7 +137,7 @@ def get(self): description: Forbidden - The session ID provided is invalid """ return ( - backend.get_session_details( + python_icat.get_session_details( get_session_id_from_auth_header(), **kwargs, ), 200, @@ -166,6 +166,6 @@ def put(self): 403: description: Forbidden - The session ID provided is invalid """ - return backend.refresh(get_session_id_from_auth_header(), **kwargs), 200 + return python_icat.refresh(get_session_id_from_auth_header(), **kwargs), 200 return Sessions diff --git a/datagateway_api/src/swagger/datagateway_api/openapi.yaml b/datagateway_api/src/swagger/datagateway_api/openapi.yaml index bb80fe0b..f8be981c 100644 --- a/datagateway_api/src/swagger/datagateway_api/openapi.yaml +++ b/datagateway_api/src/swagger/datagateway_api/openapi.yaml @@ -2355,7 +2355,7 @@ info: version: '1.0' openapi: 3.0.3 paths: - /datagateway-api/applications: + /applications: get: description: Retrieves a list of Application objects parameters: @@ -2449,7 +2449,7 @@ paths: summary: Create new Applications tags: - Applications - /datagateway-api/applications/{id_}: + /applications/{id_}: delete: description: Updates Application with the specified ID with details provided in the request body @@ -2536,7 +2536,7 @@ paths: summary: Update Applications by id tags: - Applications - /datagateway-api/applications/count: + /applications/count: get: description: Return the count of the Application objects that would be retrieved given the filters provided @@ -2562,7 +2562,7 @@ paths: summary: Count Applications tags: - Applications - /datagateway-api/applications/findone: + /applications/findone: get: description: Retrieves the first Application objects that satisfies the filters. parameters: @@ -2590,7 +2590,7 @@ paths: summary: Get single Application tags: - Applications - /datagateway-api/affiliations: + /affiliations: get: description: Retrieves a list of Affiliation objects parameters: @@ -2684,7 +2684,7 @@ paths: summary: Create new Affiliations tags: - Affiliations - /datagateway-api/affiliations/{id_}: + /affiliations/{id_}: delete: description: Updates Affiliation with the specified ID with details provided in the request body @@ -2771,7 +2771,7 @@ paths: summary: Update Affiliations by id tags: - Affiliations - /datagateway-api/affiliations/count: + /affiliations/count: get: description: Return the count of the Affiliation objects that would be retrieved given the filters provided @@ -2797,7 +2797,7 @@ paths: summary: Count Affiliations tags: - Affiliations - /datagateway-api/affiliations/findone: + /affiliations/findone: get: description: Retrieves the first Affiliation objects that satisfies the filters. parameters: @@ -2825,7 +2825,7 @@ paths: summary: Get single Affiliation tags: - Affiliations - /datagateway-api/datacollectioninvestigations: + /datacollectioninvestigations: get: description: Retrieves a list of DataCollectionInvestigation objects parameters: @@ -2920,7 +2920,7 @@ paths: summary: Create new DataCollectionInvestigations tags: - DataCollectionInvestigations - /datagateway-api/datacollectioninvestigations/{id_}: + /datacollectioninvestigations/{id_}: delete: description: Updates DataCollectionInvestigation with the specified ID with details provided in the request body @@ -3007,7 +3007,7 @@ paths: summary: Update DataCollectionInvestigations by id tags: - DataCollectionInvestigations - /datagateway-api/datacollectioninvestigations/count: + /datacollectioninvestigations/count: get: description: Return the count of the DataCollectionInvestigation objects that would be retrieved given the filters provided @@ -3033,7 +3033,7 @@ paths: summary: Count DataCollectionInvestigations tags: - DataCollectionInvestigations - /datagateway-api/datacollectioninvestigations/findone: + /datacollectioninvestigations/findone: get: description: Retrieves the first DataCollectionInvestigation objects that satisfies the filters. @@ -3063,7 +3063,7 @@ paths: summary: Get single DataCollectionInvestigation tags: - DataCollectionInvestigations - /datagateway-api/datacollectiondatafiles: + /datacollectiondatafiles: get: description: Retrieves a list of DataCollectionDatafile objects parameters: @@ -3157,7 +3157,7 @@ paths: summary: Create new DataCollectionDatafiles tags: - DataCollectionDatafiles - /datagateway-api/datacollectiondatafiles/{id_}: + /datacollectiondatafiles/{id_}: delete: description: Updates DataCollectionDatafile with the specified ID with details provided in the request body @@ -3244,7 +3244,7 @@ paths: summary: Update DataCollectionDatafiles by id tags: - DataCollectionDatafiles - /datagateway-api/datacollectiondatafiles/count: + /datacollectiondatafiles/count: get: description: Return the count of the DataCollectionDatafile objects that would be retrieved given the filters provided @@ -3270,7 +3270,7 @@ paths: summary: Count DataCollectionDatafiles tags: - DataCollectionDatafiles - /datagateway-api/datacollectiondatafiles/findone: + /datacollectiondatafiles/findone: get: description: Retrieves the first DataCollectionDatafile objects that satisfies the filters. @@ -3300,7 +3300,7 @@ paths: summary: Get single DataCollectionDatafile tags: - DataCollectionDatafiles - /datagateway-api/datacollectiondatasets: + /datacollectiondatasets: get: description: Retrieves a list of DataCollectionDataset objects parameters: @@ -3394,7 +3394,7 @@ paths: summary: Create new DataCollectionDatasets tags: - DataCollectionDatasets - /datagateway-api/datacollectiondatasets/{id_}: + /datacollectiondatasets/{id_}: delete: description: Updates DataCollectionDataset with the specified ID with details provided in the request body @@ -3481,7 +3481,7 @@ paths: summary: Update DataCollectionDatasets by id tags: - DataCollectionDatasets - /datagateway-api/datacollectiondatasets/count: + /datacollectiondatasets/count: get: description: Return the count of the DataCollectionDataset objects that would be retrieved given the filters provided @@ -3507,7 +3507,7 @@ paths: summary: Count DataCollectionDatasets tags: - DataCollectionDatasets - /datagateway-api/datacollectiondatasets/findone: + /datacollectiondatasets/findone: get: description: Retrieves the first DataCollectionDataset objects that satisfies the filters. @@ -3537,7 +3537,7 @@ paths: summary: Get single DataCollectionDataset tags: - DataCollectionDatasets - /datagateway-api/datacollectionparameters: + /datacollectionparameters: get: description: Retrieves a list of DataCollectionParameter objects parameters: @@ -3632,7 +3632,7 @@ paths: summary: Create new DataCollectionParameters tags: - DataCollectionParameters - /datagateway-api/datacollectionparameters/{id_}: + /datacollectionparameters/{id_}: delete: description: Updates DataCollectionParameter with the specified ID with details provided in the request body @@ -3719,7 +3719,7 @@ paths: summary: Update DataCollectionParameters by id tags: - DataCollectionParameters - /datagateway-api/datacollectionparameters/count: + /datacollectionparameters/count: get: description: Return the count of the DataCollectionParameter objects that would be retrieved given the filters provided @@ -3745,7 +3745,7 @@ paths: summary: Count DataCollectionParameters tags: - DataCollectionParameters - /datagateway-api/datacollectionparameters/findone: + /datacollectionparameters/findone: get: description: Retrieves the first DataCollectionParameter objects that satisfies the filters. @@ -3775,7 +3775,7 @@ paths: summary: Get single DataCollectionParameter tags: - DataCollectionParameters - /datagateway-api/datacollections: + /datacollections: get: description: Retrieves a list of DataCollection objects parameters: @@ -3869,7 +3869,7 @@ paths: summary: Create new DataCollections tags: - DataCollections - /datagateway-api/datacollections/{id_}: + /datacollections/{id_}: delete: description: Updates DataCollection with the specified ID with details provided in the request body @@ -3956,7 +3956,7 @@ paths: summary: Update DataCollections by id tags: - DataCollections - /datagateway-api/datacollections/count: + /datacollections/count: get: description: Return the count of the DataCollection objects that would be retrieved given the filters provided @@ -3982,7 +3982,7 @@ paths: summary: Count DataCollections tags: - DataCollections - /datagateway-api/datacollections/findone: + /datacollections/findone: get: description: Retrieves the first DataCollection objects that satisfies the filters. parameters: @@ -4010,7 +4010,7 @@ paths: summary: Get single DataCollection tags: - DataCollections - /datagateway-api/datapublications: + /datapublications: get: description: Retrieves a list of DataPublication objects parameters: @@ -4104,7 +4104,7 @@ paths: summary: Create new DataPublications tags: - DataPublications - /datagateway-api/datapublications/{id_}: + /datapublications/{id_}: delete: description: Updates DataPublication with the specified ID with details provided in the request body @@ -4191,7 +4191,7 @@ paths: summary: Update DataPublications by id tags: - DataPublications - /datagateway-api/datapublications/count: + /datapublications/count: get: description: Return the count of the DataPublication objects that would be retrieved given the filters provided @@ -4217,7 +4217,7 @@ paths: summary: Count DataPublications tags: - DataPublications - /datagateway-api/datapublications/findone: + /datapublications/findone: get: description: Retrieves the first DataPublication objects that satisfies the filters. @@ -4246,7 +4246,7 @@ paths: summary: Get single DataPublication tags: - DataPublications - /datagateway-api/datapublicationdates: + /datapublicationdates: get: description: Retrieves a list of DataPublicationDate objects parameters: @@ -4340,7 +4340,7 @@ paths: summary: Create new DataPublicationDates tags: - DataPublicationDates - /datagateway-api/datapublicationdates/{id_}: + /datapublicationdates/{id_}: delete: description: Updates DataPublicationDate with the specified ID with details provided in the request body @@ -4427,7 +4427,7 @@ paths: summary: Update DataPublicationDates by id tags: - DataPublicationDates - /datagateway-api/datapublicationdates/count: + /datapublicationdates/count: get: description: Return the count of the DataPublicationDate objects that would be retrieved given the filters provided @@ -4453,7 +4453,7 @@ paths: summary: Count DataPublicationDates tags: - DataPublicationDates - /datagateway-api/datapublicationdates/findone: + /datapublicationdates/findone: get: description: Retrieves the first DataPublicationDate objects that satisfies the filters. @@ -4482,7 +4482,7 @@ paths: summary: Get single DataPublicationDate tags: - DataPublicationDates - /datagateway-api/datapublicationfundings: + /datapublicationfundings: get: description: Retrieves a list of DataPublicationFunding objects parameters: @@ -4576,7 +4576,7 @@ paths: summary: Create new DataPublicationFundings tags: - DataPublicationFundings - /datagateway-api/datapublicationfundings/{id_}: + /datapublicationfundings/{id_}: delete: description: Updates DataPublicationFunding with the specified ID with details provided in the request body @@ -4663,7 +4663,7 @@ paths: summary: Update DataPublicationFundings by id tags: - DataPublicationFundings - /datagateway-api/datapublicationfundings/count: + /datapublicationfundings/count: get: description: Return the count of the DataPublicationFunding objects that would be retrieved given the filters provided @@ -4689,7 +4689,7 @@ paths: summary: Count DataPublicationFundings tags: - DataPublicationFundings - /datagateway-api/datapublicationfundings/findone: + /datapublicationfundings/findone: get: description: Retrieves the first DataPublicationFunding objects that satisfies the filters. @@ -4719,7 +4719,7 @@ paths: summary: Get single DataPublicationFunding tags: - DataPublicationFundings - /datagateway-api/datapublicationtypes: + /datapublicationtypes: get: description: Retrieves a list of DataPublicationType objects parameters: @@ -4813,7 +4813,7 @@ paths: summary: Create new DataPublicationTypes tags: - DataPublicationTypes - /datagateway-api/datapublicationtypes/{id_}: + /datapublicationtypes/{id_}: delete: description: Updates DataPublicationType with the specified ID with details provided in the request body @@ -4900,7 +4900,7 @@ paths: summary: Update DataPublicationTypes by id tags: - DataPublicationTypes - /datagateway-api/datapublicationtypes/count: + /datapublicationtypes/count: get: description: Return the count of the DataPublicationType objects that would be retrieved given the filters provided @@ -4926,7 +4926,7 @@ paths: summary: Count DataPublicationTypes tags: - DataPublicationTypes - /datagateway-api/datapublicationtypes/findone: + /datapublicationtypes/findone: get: description: Retrieves the first DataPublicationType objects that satisfies the filters. @@ -4955,7 +4955,7 @@ paths: summary: Get single DataPublicationType tags: - DataPublicationTypes - /datagateway-api/datapublicationusers: + /datapublicationusers: get: description: Retrieves a list of DataPublicationUser objects parameters: @@ -5049,7 +5049,7 @@ paths: summary: Create new DataPublicationUsers tags: - DataPublicationUsers - /datagateway-api/datapublicationusers/{id_}: + /datapublicationusers/{id_}: delete: description: Updates DataPublicationUser with the specified ID with details provided in the request body @@ -5136,7 +5136,7 @@ paths: summary: Update DataPublicationUsers by id tags: - DataPublicationUsers - /datagateway-api/datapublicationusers/count: + /datapublicationusers/count: get: description: Return the count of the DataPublicationUser objects that would be retrieved given the filters provided @@ -5162,7 +5162,7 @@ paths: summary: Count DataPublicationUsers tags: - DataPublicationUsers - /datagateway-api/datapublicationusers/findone: + /datapublicationusers/findone: get: description: Retrieves the first DataPublicationUser objects that satisfies the filters. @@ -5191,7 +5191,7 @@ paths: summary: Get single DataPublicationUser tags: - DataPublicationUsers - /datagateway-api/datafileformats: + /datafileformats: get: description: Retrieves a list of DatafileFormat objects parameters: @@ -5285,7 +5285,7 @@ paths: summary: Create new DatafileFormats tags: - DatafileFormats - /datagateway-api/datafileformats/{id_}: + /datafileformats/{id_}: delete: description: Updates DatafileFormat with the specified ID with details provided in the request body @@ -5372,7 +5372,7 @@ paths: summary: Update DatafileFormats by id tags: - DatafileFormats - /datagateway-api/datafileformats/count: + /datafileformats/count: get: description: Return the count of the DatafileFormat objects that would be retrieved given the filters provided @@ -5398,7 +5398,7 @@ paths: summary: Count DatafileFormats tags: - DatafileFormats - /datagateway-api/datafileformats/findone: + /datafileformats/findone: get: description: Retrieves the first DatafileFormat objects that satisfies the filters. parameters: @@ -5426,7 +5426,7 @@ paths: summary: Get single DatafileFormat tags: - DatafileFormats - /datagateway-api/datafileparameters: + /datafileparameters: get: description: Retrieves a list of DatafileParameter objects parameters: @@ -5520,7 +5520,7 @@ paths: summary: Create new DatafileParameters tags: - DatafileParameters - /datagateway-api/datafileparameters/{id_}: + /datafileparameters/{id_}: delete: description: Updates DatafileParameter with the specified ID with details provided in the request body @@ -5607,7 +5607,7 @@ paths: summary: Update DatafileParameters by id tags: - DatafileParameters - /datagateway-api/datafileparameters/count: + /datafileparameters/count: get: description: Return the count of the DatafileParameter objects that would be retrieved given the filters provided @@ -5633,7 +5633,7 @@ paths: summary: Count DatafileParameters tags: - DatafileParameters - /datagateway-api/datafileparameters/findone: + /datafileparameters/findone: get: description: Retrieves the first DatafileParameter objects that satisfies the filters. @@ -5662,7 +5662,7 @@ paths: summary: Get single DatafileParameter tags: - DatafileParameters - /datagateway-api/datafiles: + /datafiles: get: description: Retrieves a list of Datafile objects parameters: @@ -5756,7 +5756,7 @@ paths: summary: Create new Datafiles tags: - Datafiles - /datagateway-api/datafiles/{id_}: + /datafiles/{id_}: delete: description: Updates Datafile with the specified ID with details provided in the request body @@ -5843,7 +5843,7 @@ paths: summary: Update Datafiles by id tags: - Datafiles - /datagateway-api/datafiles/count: + /datafiles/count: get: description: Return the count of the Datafile objects that would be retrieved given the filters provided @@ -5869,7 +5869,7 @@ paths: summary: Count Datafiles tags: - Datafiles - /datagateway-api/datafiles/findone: + /datafiles/findone: get: description: Retrieves the first Datafile objects that satisfies the filters. parameters: @@ -5897,7 +5897,7 @@ paths: summary: Get single Datafile tags: - Datafiles - /datagateway-api/datasetparameters: + /datasetparameters: get: description: Retrieves a list of DatasetParameter objects parameters: @@ -5991,7 +5991,7 @@ paths: summary: Create new DatasetParameters tags: - DatasetParameters - /datagateway-api/datasetparameters/{id_}: + /datasetparameters/{id_}: delete: description: Updates DatasetParameter with the specified ID with details provided in the request body @@ -6078,7 +6078,7 @@ paths: summary: Update DatasetParameters by id tags: - DatasetParameters - /datagateway-api/datasetparameters/count: + /datasetparameters/count: get: description: Return the count of the DatasetParameter objects that would be retrieved given the filters provided @@ -6104,7 +6104,7 @@ paths: summary: Count DatasetParameters tags: - DatasetParameters - /datagateway-api/datasetparameters/findone: + /datasetparameters/findone: get: description: Retrieves the first DatasetParameter objects that satisfies the filters. @@ -6133,7 +6133,7 @@ paths: summary: Get single DatasetParameter tags: - DatasetParameters - /datagateway-api/datasetinstruments: + /datasetinstruments: get: description: Retrieves a list of DatasetInstrument objects parameters: @@ -6227,7 +6227,7 @@ paths: summary: Create new DatasetInstruments tags: - DatasetInstruments - /datagateway-api/datasetinstruments/{id_}: + /datasetinstruments/{id_}: delete: description: Updates DatasetInstrument with the specified ID with details provided in the request body @@ -6314,7 +6314,7 @@ paths: summary: Update DatasetInstruments by id tags: - DatasetInstruments - /datagateway-api/datasetinstruments/count: + /datasetinstruments/count: get: description: Return the count of the DatasetInstrument objects that would be retrieved given the filters provided @@ -6340,7 +6340,7 @@ paths: summary: Count DatasetInstruments tags: - DatasetInstruments - /datagateway-api/datasetinstruments/findone: + /datasetinstruments/findone: get: description: Retrieves the first DatasetInstrument objects that satisfies the filters. @@ -6369,7 +6369,7 @@ paths: summary: Get single DatasetInstrument tags: - DatasetInstruments - /datagateway-api/datasettechniques: + /datasettechniques: get: description: Retrieves a list of DatasetTechnique objects parameters: @@ -6463,7 +6463,7 @@ paths: summary: Create new DatasetTechniques tags: - DatasetTechniques - /datagateway-api/datasettechniques/{id_}: + /datasettechniques/{id_}: delete: description: Updates DatasetTechnique with the specified ID with details provided in the request body @@ -6550,7 +6550,7 @@ paths: summary: Update DatasetTechniques by id tags: - DatasetTechniques - /datagateway-api/datasettechniques/count: + /datasettechniques/count: get: description: Return the count of the DatasetTechnique objects that would be retrieved given the filters provided @@ -6576,7 +6576,7 @@ paths: summary: Count DatasetTechniques tags: - DatasetTechniques - /datagateway-api/datasettechniques/findone: + /datasettechniques/findone: get: description: Retrieves the first DatasetTechnique objects that satisfies the filters. @@ -6605,7 +6605,7 @@ paths: summary: Get single DatasetTechnique tags: - DatasetTechniques - /datagateway-api/datasettypes: + /datasettypes: get: description: Retrieves a list of DatasetType objects parameters: @@ -6699,7 +6699,7 @@ paths: summary: Create new DatasetTypes tags: - DatasetTypes - /datagateway-api/datasettypes/{id_}: + /datasettypes/{id_}: delete: description: Updates DatasetType with the specified ID with details provided in the request body @@ -6786,7 +6786,7 @@ paths: summary: Update DatasetTypes by id tags: - DatasetTypes - /datagateway-api/datasettypes/count: + /datasettypes/count: get: description: Return the count of the DatasetType objects that would be retrieved given the filters provided @@ -6812,7 +6812,7 @@ paths: summary: Count DatasetTypes tags: - DatasetTypes - /datagateway-api/datasettypes/findone: + /datasettypes/findone: get: description: Retrieves the first DatasetType objects that satisfies the filters. parameters: @@ -6840,7 +6840,7 @@ paths: summary: Get single DatasetType tags: - DatasetTypes - /datagateway-api/datasets: + /datasets: get: description: Retrieves a list of Dataset objects parameters: @@ -6934,7 +6934,7 @@ paths: summary: Create new Datasets tags: - Datasets - /datagateway-api/datasets/{id_}: + /datasets/{id_}: delete: description: Updates Dataset with the specified ID with details provided in the request body @@ -7021,7 +7021,7 @@ paths: summary: Update Datasets by id tags: - Datasets - /datagateway-api/datasets/count: + /datasets/count: get: description: Return the count of the Dataset objects that would be retrieved given the filters provided @@ -7047,7 +7047,7 @@ paths: summary: Count Datasets tags: - Datasets - /datagateway-api/datasets/findone: + /datasets/findone: get: description: Retrieves the first Dataset objects that satisfies the filters. parameters: @@ -7075,7 +7075,7 @@ paths: summary: Get single Dataset tags: - Datasets - /datagateway-api/facilities: + /facilities: get: description: Retrieves a list of Facility objects parameters: @@ -7169,7 +7169,7 @@ paths: summary: Create new Facilities tags: - Facilities - /datagateway-api/facilities/{id_}: + /facilities/{id_}: delete: description: Updates Facility with the specified ID with details provided in the request body @@ -7256,7 +7256,7 @@ paths: summary: Update Facilities by id tags: - Facilities - /datagateway-api/facilities/count: + /facilities/count: get: description: Return the count of the Facility objects that would be retrieved given the filters provided @@ -7282,7 +7282,7 @@ paths: summary: Count Facilities tags: - Facilities - /datagateway-api/facilities/findone: + /facilities/findone: get: description: Retrieves the first Facility objects that satisfies the filters. parameters: @@ -7310,7 +7310,7 @@ paths: summary: Get single Facility tags: - Facilities - /datagateway-api/facilitycycles: + /facilitycycles: get: description: Retrieves a list of FacilityCycle objects parameters: @@ -7404,7 +7404,7 @@ paths: summary: Create new FacilityCycles tags: - FacilityCycles - /datagateway-api/facilitycycles/{id_}: + /facilitycycles/{id_}: delete: description: Updates FacilityCycle with the specified ID with details provided in the request body @@ -7491,7 +7491,7 @@ paths: summary: Update FacilityCycles by id tags: - FacilityCycles - /datagateway-api/facilitycycles/count: + /facilitycycles/count: get: description: Return the count of the FacilityCycle objects that would be retrieved given the filters provided @@ -7517,7 +7517,7 @@ paths: summary: Count FacilityCycles tags: - FacilityCycles - /datagateway-api/facilitycycles/findone: + /facilitycycles/findone: get: description: Retrieves the first FacilityCycle objects that satisfies the filters. parameters: @@ -7545,7 +7545,7 @@ paths: summary: Get single FacilityCycle tags: - FacilityCycles - /datagateway-api/fundingreferences: + /fundingreferences: get: description: Retrieves a list of FundingReference objects parameters: @@ -7639,7 +7639,7 @@ paths: summary: Create new FundingReferences tags: - FundingReferences - /datagateway-api/fundingreferences/{id_}: + /fundingreferences/{id_}: delete: description: Updates FundingReference with the specified ID with details provided in the request body @@ -7726,7 +7726,7 @@ paths: summary: Update FundingReferences by id tags: - FundingReferences - /datagateway-api/fundingreferences/count: + /fundingreferences/count: get: description: Return the count of the FundingReference objects that would be retrieved given the filters provided @@ -7752,7 +7752,7 @@ paths: summary: Count FundingReferences tags: - FundingReferences - /datagateway-api/fundingreferences/findone: + /fundingreferences/findone: get: description: Retrieves the first FundingReference objects that satisfies the filters. @@ -7781,7 +7781,7 @@ paths: summary: Get single FundingReference tags: - FundingReferences - /datagateway-api/groupings: + /groupings: get: description: Retrieves a list of Grouping objects parameters: @@ -7875,7 +7875,7 @@ paths: summary: Create new Groupings tags: - Groupings - /datagateway-api/groupings/{id_}: + /groupings/{id_}: delete: description: Updates Grouping with the specified ID with details provided in the request body @@ -7962,7 +7962,7 @@ paths: summary: Update Groupings by id tags: - Groupings - /datagateway-api/groupings/count: + /groupings/count: get: description: Return the count of the Grouping objects that would be retrieved given the filters provided @@ -7988,7 +7988,7 @@ paths: summary: Count Groupings tags: - Groupings - /datagateway-api/groupings/findone: + /groupings/findone: get: description: Retrieves the first Grouping objects that satisfies the filters. parameters: @@ -8016,7 +8016,7 @@ paths: summary: Get single Grouping tags: - Groupings - /datagateway-api/instrumentscientists: + /instrumentscientists: get: description: Retrieves a list of InstrumentScientist objects parameters: @@ -8110,7 +8110,7 @@ paths: summary: Create new InstrumentScientists tags: - InstrumentScientists - /datagateway-api/instrumentscientists/{id_}: + /instrumentscientists/{id_}: delete: description: Updates InstrumentScientist with the specified ID with details provided in the request body @@ -8197,7 +8197,7 @@ paths: summary: Update InstrumentScientists by id tags: - InstrumentScientists - /datagateway-api/instrumentscientists/count: + /instrumentscientists/count: get: description: Return the count of the InstrumentScientist objects that would be retrieved given the filters provided @@ -8223,7 +8223,7 @@ paths: summary: Count InstrumentScientists tags: - InstrumentScientists - /datagateway-api/instrumentscientists/findone: + /instrumentscientists/findone: get: description: Retrieves the first InstrumentScientist objects that satisfies the filters. @@ -8252,7 +8252,7 @@ paths: summary: Get single InstrumentScientist tags: - InstrumentScientists - /datagateway-api/instruments: + /instruments: get: description: Retrieves a list of Instrument objects parameters: @@ -8346,7 +8346,7 @@ paths: summary: Create new Instruments tags: - Instruments - /datagateway-api/instruments/{id_}: + /instruments/{id_}: delete: description: Updates Instrument with the specified ID with details provided in the request body @@ -8433,7 +8433,7 @@ paths: summary: Update Instruments by id tags: - Instruments - /datagateway-api/instruments/count: + /instruments/count: get: description: Return the count of the Instrument objects that would be retrieved given the filters provided @@ -8459,7 +8459,7 @@ paths: summary: Count Instruments tags: - Instruments - /datagateway-api/instruments/findone: + /instruments/findone: get: description: Retrieves the first Instrument objects that satisfies the filters. parameters: @@ -8487,7 +8487,7 @@ paths: summary: Get single Instrument tags: - Instruments - /datagateway-api/investigationfacilitycycles: + /investigationfacilitycycles: get: description: Retrieves a list of InvestigationFacilityCycle objects parameters: @@ -8582,7 +8582,7 @@ paths: summary: Create new InvestigationFacilityCycles tags: - InvestigationFacilityCycles - /datagateway-api/investigationfacilitycycles/{id_}: + /investigationfacilitycycles/{id_}: delete: description: Updates InvestigationFacilityCycle with the specified ID with details provided in the request body @@ -8669,7 +8669,7 @@ paths: summary: Update InvestigationFacilityCycles by id tags: - InvestigationFacilityCycles - /datagateway-api/investigationfacilitycycles/count: + /investigationfacilitycycles/count: get: description: Return the count of the InvestigationFacilityCycle objects that would be retrieved given the filters provided @@ -8695,7 +8695,7 @@ paths: summary: Count InvestigationFacilityCycles tags: - InvestigationFacilityCycles - /datagateway-api/investigationfacilitycycles/findone: + /investigationfacilitycycles/findone: get: description: Retrieves the first InvestigationFacilityCycle objects that satisfies the filters. @@ -8725,7 +8725,7 @@ paths: summary: Get single InvestigationFacilityCycle tags: - InvestigationFacilityCycles - /datagateway-api/investigationfundings: + /investigationfundings: get: description: Retrieves a list of InvestigationFunding objects parameters: @@ -8819,7 +8819,7 @@ paths: summary: Create new InvestigationFundings tags: - InvestigationFundings - /datagateway-api/investigationfundings/{id_}: + /investigationfundings/{id_}: delete: description: Updates InvestigationFunding with the specified ID with details provided in the request body @@ -8906,7 +8906,7 @@ paths: summary: Update InvestigationFundings by id tags: - InvestigationFundings - /datagateway-api/investigationfundings/count: + /investigationfundings/count: get: description: Return the count of the InvestigationFunding objects that would be retrieved given the filters provided @@ -8932,7 +8932,7 @@ paths: summary: Count InvestigationFundings tags: - InvestigationFundings - /datagateway-api/investigationfundings/findone: + /investigationfundings/findone: get: description: Retrieves the first InvestigationFunding objects that satisfies the filters. @@ -8962,7 +8962,7 @@ paths: summary: Get single InvestigationFunding tags: - InvestigationFundings - /datagateway-api/investigationgroups: + /investigationgroups: get: description: Retrieves a list of InvestigationGroup objects parameters: @@ -9056,7 +9056,7 @@ paths: summary: Create new InvestigationGroups tags: - InvestigationGroups - /datagateway-api/investigationgroups/{id_}: + /investigationgroups/{id_}: delete: description: Updates InvestigationGroup with the specified ID with details provided in the request body @@ -9143,7 +9143,7 @@ paths: summary: Update InvestigationGroups by id tags: - InvestigationGroups - /datagateway-api/investigationgroups/count: + /investigationgroups/count: get: description: Return the count of the InvestigationGroup objects that would be retrieved given the filters provided @@ -9169,7 +9169,7 @@ paths: summary: Count InvestigationGroups tags: - InvestigationGroups - /datagateway-api/investigationgroups/findone: + /investigationgroups/findone: get: description: Retrieves the first InvestigationGroup objects that satisfies the filters. @@ -9198,7 +9198,7 @@ paths: summary: Get single InvestigationGroup tags: - InvestigationGroups - /datagateway-api/investigationinstruments: + /investigationinstruments: get: description: Retrieves a list of InvestigationInstrument objects parameters: @@ -9293,7 +9293,7 @@ paths: summary: Create new InvestigationInstruments tags: - InvestigationInstruments - /datagateway-api/investigationinstruments/{id_}: + /investigationinstruments/{id_}: delete: description: Updates InvestigationInstrument with the specified ID with details provided in the request body @@ -9380,7 +9380,7 @@ paths: summary: Update InvestigationInstruments by id tags: - InvestigationInstruments - /datagateway-api/investigationinstruments/count: + /investigationinstruments/count: get: description: Return the count of the InvestigationInstrument objects that would be retrieved given the filters provided @@ -9406,7 +9406,7 @@ paths: summary: Count InvestigationInstruments tags: - InvestigationInstruments - /datagateway-api/investigationinstruments/findone: + /investigationinstruments/findone: get: description: Retrieves the first InvestigationInstrument objects that satisfies the filters. @@ -9436,7 +9436,7 @@ paths: summary: Get single InvestigationInstrument tags: - InvestigationInstruments - /datagateway-api/investigationparameters: + /investigationparameters: get: description: Retrieves a list of InvestigationParameter objects parameters: @@ -9530,7 +9530,7 @@ paths: summary: Create new InvestigationParameters tags: - InvestigationParameters - /datagateway-api/investigationparameters/{id_}: + /investigationparameters/{id_}: delete: description: Updates InvestigationParameter with the specified ID with details provided in the request body @@ -9617,7 +9617,7 @@ paths: summary: Update InvestigationParameters by id tags: - InvestigationParameters - /datagateway-api/investigationparameters/count: + /investigationparameters/count: get: description: Return the count of the InvestigationParameter objects that would be retrieved given the filters provided @@ -9643,7 +9643,7 @@ paths: summary: Count InvestigationParameters tags: - InvestigationParameters - /datagateway-api/investigationparameters/findone: + /investigationparameters/findone: get: description: Retrieves the first InvestigationParameter objects that satisfies the filters. @@ -9673,7 +9673,7 @@ paths: summary: Get single InvestigationParameter tags: - InvestigationParameters - /datagateway-api/investigationtypes: + /investigationtypes: get: description: Retrieves a list of InvestigationType objects parameters: @@ -9767,7 +9767,7 @@ paths: summary: Create new InvestigationTypes tags: - InvestigationTypes - /datagateway-api/investigationtypes/{id_}: + /investigationtypes/{id_}: delete: description: Updates InvestigationType with the specified ID with details provided in the request body @@ -9854,7 +9854,7 @@ paths: summary: Update InvestigationTypes by id tags: - InvestigationTypes - /datagateway-api/investigationtypes/count: + /investigationtypes/count: get: description: Return the count of the InvestigationType objects that would be retrieved given the filters provided @@ -9880,7 +9880,7 @@ paths: summary: Count InvestigationTypes tags: - InvestigationTypes - /datagateway-api/investigationtypes/findone: + /investigationtypes/findone: get: description: Retrieves the first InvestigationType objects that satisfies the filters. @@ -9909,7 +9909,7 @@ paths: summary: Get single InvestigationType tags: - InvestigationTypes - /datagateway-api/investigationusers: + /investigationusers: get: description: Retrieves a list of InvestigationUser objects parameters: @@ -10003,7 +10003,7 @@ paths: summary: Create new InvestigationUsers tags: - InvestigationUsers - /datagateway-api/investigationusers/{id_}: + /investigationusers/{id_}: delete: description: Updates InvestigationUser with the specified ID with details provided in the request body @@ -10090,7 +10090,7 @@ paths: summary: Update InvestigationUsers by id tags: - InvestigationUsers - /datagateway-api/investigationusers/count: + /investigationusers/count: get: description: Return the count of the InvestigationUser objects that would be retrieved given the filters provided @@ -10116,7 +10116,7 @@ paths: summary: Count InvestigationUsers tags: - InvestigationUsers - /datagateway-api/investigationusers/findone: + /investigationusers/findone: get: description: Retrieves the first InvestigationUser objects that satisfies the filters. @@ -10145,7 +10145,7 @@ paths: summary: Get single InvestigationUser tags: - InvestigationUsers - /datagateway-api/investigations: + /investigations: get: description: Retrieves a list of Investigation objects parameters: @@ -10239,7 +10239,7 @@ paths: summary: Create new Investigations tags: - Investigations - /datagateway-api/investigations/{id_}: + /investigations/{id_}: delete: description: Updates Investigation with the specified ID with details provided in the request body @@ -10326,7 +10326,7 @@ paths: summary: Update Investigations by id tags: - Investigations - /datagateway-api/investigations/count: + /investigations/count: get: description: Return the count of the Investigation objects that would be retrieved given the filters provided @@ -10352,7 +10352,7 @@ paths: summary: Count Investigations tags: - Investigations - /datagateway-api/investigations/findone: + /investigations/findone: get: description: Retrieves the first Investigation objects that satisfies the filters. parameters: @@ -10380,7 +10380,7 @@ paths: summary: Get single Investigation tags: - Investigations - /datagateway-api/jobs: + /jobs: get: description: Retrieves a list of Job objects parameters: @@ -10473,7 +10473,7 @@ paths: summary: Create new Jobs tags: - Jobs - /datagateway-api/jobs/{id_}: + /jobs/{id_}: delete: description: Updates Job with the specified ID with details provided in the request body @@ -10560,7 +10560,7 @@ paths: summary: Update Jobs by id tags: - Jobs - /datagateway-api/jobs/count: + /jobs/count: get: description: Return the count of the Job objects that would be retrieved given the filters provided @@ -10586,7 +10586,7 @@ paths: summary: Count Jobs tags: - Jobs - /datagateway-api/jobs/findone: + /jobs/findone: get: description: Retrieves the first Job objects that satisfies the filters. parameters: @@ -10614,7 +10614,7 @@ paths: summary: Get single Job tags: - Jobs - /datagateway-api/keywords: + /keywords: get: description: Retrieves a list of Keyword objects parameters: @@ -10708,7 +10708,7 @@ paths: summary: Create new Keywords tags: - Keywords - /datagateway-api/keywords/{id_}: + /keywords/{id_}: delete: description: Updates Keyword with the specified ID with details provided in the request body @@ -10795,7 +10795,7 @@ paths: summary: Update Keywords by id tags: - Keywords - /datagateway-api/keywords/count: + /keywords/count: get: description: Return the count of the Keyword objects that would be retrieved given the filters provided @@ -10821,7 +10821,7 @@ paths: summary: Count Keywords tags: - Keywords - /datagateway-api/keywords/findone: + /keywords/findone: get: description: Retrieves the first Keyword objects that satisfies the filters. parameters: @@ -10849,7 +10849,7 @@ paths: summary: Get single Keyword tags: - Keywords - /datagateway-api/parametertypes: + /parametertypes: get: description: Retrieves a list of ParameterType objects parameters: @@ -10943,7 +10943,7 @@ paths: summary: Create new ParameterTypes tags: - ParameterTypes - /datagateway-api/parametertypes/{id_}: + /parametertypes/{id_}: delete: description: Updates ParameterType with the specified ID with details provided in the request body @@ -11030,7 +11030,7 @@ paths: summary: Update ParameterTypes by id tags: - ParameterTypes - /datagateway-api/parametertypes/count: + /parametertypes/count: get: description: Return the count of the ParameterType objects that would be retrieved given the filters provided @@ -11056,7 +11056,7 @@ paths: summary: Count ParameterTypes tags: - ParameterTypes - /datagateway-api/parametertypes/findone: + /parametertypes/findone: get: description: Retrieves the first ParameterType objects that satisfies the filters. parameters: @@ -11084,7 +11084,7 @@ paths: summary: Get single ParameterType tags: - ParameterTypes - /datagateway-api/permissiblestringvalues: + /permissiblestringvalues: get: description: Retrieves a list of PermissibleStringValue objects parameters: @@ -11178,7 +11178,7 @@ paths: summary: Create new PermissibleStringValues tags: - PermissibleStringValues - /datagateway-api/permissiblestringvalues/{id_}: + /permissiblestringvalues/{id_}: delete: description: Updates PermissibleStringValue with the specified ID with details provided in the request body @@ -11265,7 +11265,7 @@ paths: summary: Update PermissibleStringValues by id tags: - PermissibleStringValues - /datagateway-api/permissiblestringvalues/count: + /permissiblestringvalues/count: get: description: Return the count of the PermissibleStringValue objects that would be retrieved given the filters provided @@ -11291,7 +11291,7 @@ paths: summary: Count PermissibleStringValues tags: - PermissibleStringValues - /datagateway-api/permissiblestringvalues/findone: + /permissiblestringvalues/findone: get: description: Retrieves the first PermissibleStringValue objects that satisfies the filters. @@ -11321,7 +11321,7 @@ paths: summary: Get single PermissibleStringValue tags: - PermissibleStringValues - /datagateway-api/publicsteps: + /publicsteps: get: description: Retrieves a list of PublicStep objects parameters: @@ -11415,7 +11415,7 @@ paths: summary: Create new PublicSteps tags: - PublicSteps - /datagateway-api/publicsteps/{id_}: + /publicsteps/{id_}: delete: description: Updates PublicStep with the specified ID with details provided in the request body @@ -11502,7 +11502,7 @@ paths: summary: Update PublicSteps by id tags: - PublicSteps - /datagateway-api/publicsteps/count: + /publicsteps/count: get: description: Return the count of the PublicStep objects that would be retrieved given the filters provided @@ -11528,7 +11528,7 @@ paths: summary: Count PublicSteps tags: - PublicSteps - /datagateway-api/publicsteps/findone: + /publicsteps/findone: get: description: Retrieves the first PublicStep objects that satisfies the filters. parameters: @@ -11556,7 +11556,7 @@ paths: summary: Get single PublicStep tags: - PublicSteps - /datagateway-api/publications: + /publications: get: description: Retrieves a list of Publication objects parameters: @@ -11650,7 +11650,7 @@ paths: summary: Create new Publications tags: - Publications - /datagateway-api/publications/{id_}: + /publications/{id_}: delete: description: Updates Publication with the specified ID with details provided in the request body @@ -11737,7 +11737,7 @@ paths: summary: Update Publications by id tags: - Publications - /datagateway-api/publications/count: + /publications/count: get: description: Return the count of the Publication objects that would be retrieved given the filters provided @@ -11763,7 +11763,7 @@ paths: summary: Count Publications tags: - Publications - /datagateway-api/publications/findone: + /publications/findone: get: description: Retrieves the first Publication objects that satisfies the filters. parameters: @@ -11791,7 +11791,7 @@ paths: summary: Get single Publication tags: - Publications - /datagateway-api/relateddatafiles: + /relateddatafiles: get: description: Retrieves a list of RelatedDatafile objects parameters: @@ -11885,7 +11885,7 @@ paths: summary: Create new RelatedDatafiles tags: - RelatedDatafiles - /datagateway-api/relateddatafiles/{id_}: + /relateddatafiles/{id_}: delete: description: Updates RelatedDatafile with the specified ID with details provided in the request body @@ -11972,7 +11972,7 @@ paths: summary: Update RelatedDatafiles by id tags: - RelatedDatafiles - /datagateway-api/relateddatafiles/count: + /relateddatafiles/count: get: description: Return the count of the RelatedDatafile objects that would be retrieved given the filters provided @@ -11998,7 +11998,7 @@ paths: summary: Count RelatedDatafiles tags: - RelatedDatafiles - /datagateway-api/relateddatafiles/findone: + /relateddatafiles/findone: get: description: Retrieves the first RelatedDatafile objects that satisfies the filters. @@ -12027,7 +12027,7 @@ paths: summary: Get single RelatedDatafile tags: - RelatedDatafiles - /datagateway-api/relateditems: + /relateditems: get: description: Retrieves a list of RelatedItem objects parameters: @@ -12121,7 +12121,7 @@ paths: summary: Create new RelatedItems tags: - RelatedItems - /datagateway-api/relateditems/{id_}: + /relateditems/{id_}: delete: description: Updates RelatedItem with the specified ID with details provided in the request body @@ -12208,7 +12208,7 @@ paths: summary: Update RelatedItems by id tags: - RelatedItems - /datagateway-api/relateditems/count: + /relateditems/count: get: description: Return the count of the RelatedItem objects that would be retrieved given the filters provided @@ -12234,7 +12234,7 @@ paths: summary: Count RelatedItems tags: - RelatedItems - /datagateway-api/relateditems/findone: + /relateditems/findone: get: description: Retrieves the first RelatedItem objects that satisfies the filters. parameters: @@ -12262,7 +12262,7 @@ paths: summary: Get single RelatedItem tags: - RelatedItems - /datagateway-api/rules: + /rules: get: description: Retrieves a list of Rule objects parameters: @@ -12355,7 +12355,7 @@ paths: summary: Create new Rules tags: - Rules - /datagateway-api/rules/{id_}: + /rules/{id_}: delete: description: Updates Rule with the specified ID with details provided in the request body @@ -12442,7 +12442,7 @@ paths: summary: Update Rules by id tags: - Rules - /datagateway-api/rules/count: + /rules/count: get: description: Return the count of the Rule objects that would be retrieved given the filters provided @@ -12468,7 +12468,7 @@ paths: summary: Count Rules tags: - Rules - /datagateway-api/rules/findone: + /rules/findone: get: description: Retrieves the first Rule objects that satisfies the filters. parameters: @@ -12496,7 +12496,7 @@ paths: summary: Get single Rule tags: - Rules - /datagateway-api/sampleparameters: + /sampleparameters: get: description: Retrieves a list of SampleParameter objects parameters: @@ -12590,7 +12590,7 @@ paths: summary: Create new SampleParameters tags: - SampleParameters - /datagateway-api/sampleparameters/{id_}: + /sampleparameters/{id_}: delete: description: Updates SampleParameter with the specified ID with details provided in the request body @@ -12677,7 +12677,7 @@ paths: summary: Update SampleParameters by id tags: - SampleParameters - /datagateway-api/sampleparameters/count: + /sampleparameters/count: get: description: Return the count of the SampleParameter objects that would be retrieved given the filters provided @@ -12703,7 +12703,7 @@ paths: summary: Count SampleParameters tags: - SampleParameters - /datagateway-api/sampleparameters/findone: + /sampleparameters/findone: get: description: Retrieves the first SampleParameter objects that satisfies the filters. @@ -12732,7 +12732,7 @@ paths: summary: Get single SampleParameter tags: - SampleParameters - /datagateway-api/sampletypes: + /sampletypes: get: description: Retrieves a list of SampleType objects parameters: @@ -12826,7 +12826,7 @@ paths: summary: Create new SampleTypes tags: - SampleTypes - /datagateway-api/sampletypes/{id_}: + /sampletypes/{id_}: delete: description: Updates SampleType with the specified ID with details provided in the request body @@ -12913,7 +12913,7 @@ paths: summary: Update SampleTypes by id tags: - SampleTypes - /datagateway-api/sampletypes/count: + /sampletypes/count: get: description: Return the count of the SampleType objects that would be retrieved given the filters provided @@ -12939,7 +12939,7 @@ paths: summary: Count SampleTypes tags: - SampleTypes - /datagateway-api/sampletypes/findone: + /sampletypes/findone: get: description: Retrieves the first SampleType objects that satisfies the filters. parameters: @@ -12967,7 +12967,7 @@ paths: summary: Get single SampleType tags: - SampleTypes - /datagateway-api/samples: + /samples: get: description: Retrieves a list of Sample objects parameters: @@ -13060,7 +13060,7 @@ paths: summary: Create new Samples tags: - Samples - /datagateway-api/samples/{id_}: + /samples/{id_}: delete: description: Updates Sample with the specified ID with details provided in the request body @@ -13147,7 +13147,7 @@ paths: summary: Update Samples by id tags: - Samples - /datagateway-api/samples/count: + /samples/count: get: description: Return the count of the Sample objects that would be retrieved given the filters provided @@ -13173,7 +13173,7 @@ paths: summary: Count Samples tags: - Samples - /datagateway-api/samples/findone: + /samples/findone: get: description: Retrieves the first Sample objects that satisfies the filters. parameters: @@ -13201,7 +13201,7 @@ paths: summary: Get single Sample tags: - Samples - /datagateway-api/shifts: + /shifts: get: description: Retrieves a list of Shift objects parameters: @@ -13294,7 +13294,7 @@ paths: summary: Create new Shifts tags: - Shifts - /datagateway-api/shifts/{id_}: + /shifts/{id_}: delete: description: Updates Shift with the specified ID with details provided in the request body @@ -13381,7 +13381,7 @@ paths: summary: Update Shifts by id tags: - Shifts - /datagateway-api/shifts/count: + /shifts/count: get: description: Return the count of the Shift objects that would be retrieved given the filters provided @@ -13407,7 +13407,7 @@ paths: summary: Count Shifts tags: - Shifts - /datagateway-api/shifts/findone: + /shifts/findone: get: description: Retrieves the first Shift objects that satisfies the filters. parameters: @@ -13435,7 +13435,7 @@ paths: summary: Get single Shift tags: - Shifts - /datagateway-api/studies: + /studies: get: description: Retrieves a list of Study objects parameters: @@ -13528,7 +13528,7 @@ paths: summary: Create new Studies tags: - Studies - /datagateway-api/studies/{id_}: + /studies/{id_}: delete: description: Updates Study with the specified ID with details provided in the request body @@ -13615,7 +13615,7 @@ paths: summary: Update Studies by id tags: - Studies - /datagateway-api/studies/count: + /studies/count: get: description: Return the count of the Study objects that would be retrieved given the filters provided @@ -13641,7 +13641,7 @@ paths: summary: Count Studies tags: - Studies - /datagateway-api/studies/findone: + /studies/findone: get: description: Retrieves the first Study objects that satisfies the filters. parameters: @@ -13669,7 +13669,7 @@ paths: summary: Get single Study tags: - Studies - /datagateway-api/studyinvestigations: + /studyinvestigations: get: description: Retrieves a list of StudyInvestigation objects parameters: @@ -13763,7 +13763,7 @@ paths: summary: Create new StudyInvestigations tags: - StudyInvestigations - /datagateway-api/studyinvestigations/{id_}: + /studyinvestigations/{id_}: delete: description: Updates StudyInvestigation with the specified ID with details provided in the request body @@ -13850,7 +13850,7 @@ paths: summary: Update StudyInvestigations by id tags: - StudyInvestigations - /datagateway-api/studyinvestigations/count: + /studyinvestigations/count: get: description: Return the count of the StudyInvestigation objects that would be retrieved given the filters provided @@ -13876,7 +13876,7 @@ paths: summary: Count StudyInvestigations tags: - StudyInvestigations - /datagateway-api/studyinvestigations/findone: + /studyinvestigations/findone: get: description: Retrieves the first StudyInvestigation objects that satisfies the filters. @@ -13905,7 +13905,7 @@ paths: summary: Get single StudyInvestigation tags: - StudyInvestigations - /datagateway-api/techniques: + /techniques: get: description: Retrieves a list of Technique objects parameters: @@ -13999,7 +13999,7 @@ paths: summary: Create new Techniques tags: - Techniques - /datagateway-api/techniques/{id_}: + /techniques/{id_}: delete: description: Updates Technique with the specified ID with details provided in the request body @@ -14086,7 +14086,7 @@ paths: summary: Update Techniques by id tags: - Techniques - /datagateway-api/techniques/count: + /techniques/count: get: description: Return the count of the Technique objects that would be retrieved given the filters provided @@ -14112,7 +14112,7 @@ paths: summary: Count Techniques tags: - Techniques - /datagateway-api/techniques/findone: + /techniques/findone: get: description: Retrieves the first Technique objects that satisfies the filters. parameters: @@ -14140,7 +14140,7 @@ paths: summary: Get single Technique tags: - Techniques - /datagateway-api/usergroups: + /usergroups: get: description: Retrieves a list of UserGroup objects parameters: @@ -14234,7 +14234,7 @@ paths: summary: Create new UserGroups tags: - UserGroups - /datagateway-api/usergroups/{id_}: + /usergroups/{id_}: delete: description: Updates UserGroup with the specified ID with details provided in the request body @@ -14321,7 +14321,7 @@ paths: summary: Update UserGroups by id tags: - UserGroups - /datagateway-api/usergroups/count: + /usergroups/count: get: description: Return the count of the UserGroup objects that would be retrieved given the filters provided @@ -14347,7 +14347,7 @@ paths: summary: Count UserGroups tags: - UserGroups - /datagateway-api/usergroups/findone: + /usergroups/findone: get: description: Retrieves the first UserGroup objects that satisfies the filters. parameters: @@ -14375,7 +14375,7 @@ paths: summary: Get single UserGroup tags: - UserGroups - /datagateway-api/users: + /users: get: description: Retrieves a list of User objects parameters: @@ -14468,7 +14468,7 @@ paths: summary: Create new Users tags: - Users - /datagateway-api/users/{id_}: + /users/{id_}: delete: description: Updates User with the specified ID with details provided in the request body @@ -14555,7 +14555,7 @@ paths: summary: Update Users by id tags: - Users - /datagateway-api/users/count: + /users/count: get: description: Return the count of the User objects that would be retrieved given the filters provided @@ -14581,7 +14581,7 @@ paths: summary: Count Users tags: - Users - /datagateway-api/users/findone: + /users/findone: get: description: Retrieves the first User objects that satisfies the filters. parameters: @@ -14609,7 +14609,7 @@ paths: summary: Get single User tags: - Users - /datagateway-api/sessions: + /sessions: delete: description: Deletes a users sessionID when they logout responses: @@ -14709,7 +14709,7 @@ paths: summary: Refresh session tags: - Sessions - /datagateway-api/ping: + /ping: get: description: Pings the API's connection method to check responsiveness responses: @@ -14720,7 +14720,7 @@ paths: description: OK message example: DataGateway API OK type: string - description: Success - the API is responsive on the backend configured + description: Success - the API is responsive on the python ICAT server '500': description: Pinging the API's connection method has gone wrong summary: Ping API connection method diff --git a/test/conftest.py b/test/conftest.py index b82e9d2a..ef577839 100644 --- a/test/conftest.py +++ b/test/conftest.py @@ -27,11 +27,9 @@ def test_config_data(): return { "datagateway_api": { "extension": "/datagateway-api", - "backend": "db", "client_cache_size": 5, "client_pool_init_size": 2, "client_pool_max_size": 5, - "db_url": "mysql+pymysql://icatdbuser:icatdbuserpw@localhost:3306/icatdb", "icat_url": "https://localhost:8181", "icat_check_cert": False, }, diff --git a/test/integration/conftest.py b/test/integration/conftest.py index a8acf1c6..0f0cd56b 100644 --- a/test/integration/conftest.py +++ b/test/integration/conftest.py @@ -11,11 +11,7 @@ create_app_infrastructure, ) from datagateway_api.src.common.config import APIConfig, Config -from datagateway_api.src.datagateway_api.database.helpers import ( - delete_row_by_id, - insert_row_into_table, -) -from datagateway_api.src.datagateway_api.database.models import SESSION +from datagateway_api.src.datagateway_api.icat.models import SESSION @pytest.fixture(scope="package") @@ -48,7 +44,6 @@ def flask_test_app_db(): """ db_app = Flask(__name__) db_app.config["TESTING"] = True - db_app.config["TEST_BACKEND"] = "db" api, spec = create_app_infrastructure(db_app) create_api_endpoints(db_app, api, spec) @@ -64,11 +59,11 @@ def valid_db_credentials_header(): session.expireDateTime = datetime.now() + timedelta(hours=1) session.username = "Test User" - insert_row_into_table(SESSION, session) + # insert_row_into_table(SESSION, session) yield {"Authorization": f"Bearer {session.id}"} - delete_row_by_id(SESSION, "Test") + # delete_row_by_id(SESSION, "Test") @pytest.fixture() diff --git a/test/integration/datagateway_api/db/conftest.py b/test/integration/datagateway_api/db/conftest.py deleted file mode 100644 index 124801c9..00000000 --- a/test/integration/datagateway_api/db/conftest.py +++ /dev/null @@ -1,156 +0,0 @@ -from datetime import datetime -import uuid - -import pytest - -from datagateway_api.src.common.config import Config -from datagateway_api.src.common.constants import Constants -from datagateway_api.src.common.exceptions import MissingRecordError -from datagateway_api.src.datagateway_api.database.helpers import ( - delete_row_by_id, - insert_row_into_table, -) -from datagateway_api.src.datagateway_api.database.models import ( - INSTRUMENT, - INVESTIGATION, - INVESTIGATIONINSTRUMENT, -) -from test.integration.datagateway_api.db.endpoints.test_create_db import ( - TestDBCreateData, -) - - -def set_meta_attributes(entity): - db_meta_attributes = { - "createTime": Constants.TEST_MOD_CREATE_DATETIME, - "modTime": Constants.TEST_MOD_CREATE_DATETIME, - "createId": "test create id", - "modId": "test mod id", - } - - for attr, value in db_meta_attributes.items(): - setattr(entity, attr, value) - - -def create_investigation_db_data(num_entities=1): - test_data = [] - - for i in range(num_entities): - investigation = INVESTIGATION() - investigation.name = f"Test Data for DataGateway API Testing (DB) {i}" - investigation.title = f"Title for DataGateway API Testing (DB) {i}" - investigation.startDate = datetime( - year=2020, month=1, day=4, hour=1, minute=1, second=1, - ) - investigation.endDate = datetime( - year=2020, month=1, day=8, hour=1, minute=1, second=1, - ) - investigation.visitId = str(uuid.uuid1()) - investigation.facilityID = 1 - investigation.typeID = 1 - investigation.fileSize = 1073741824 - investigation.fileCount = 3 - - set_meta_attributes(investigation) - - insert_row_into_table(INVESTIGATION, investigation) - - test_data.append(investigation) - - if len(test_data) == 1: - return test_data[0] - else: - return test_data - - -@pytest.fixture() -def single_investigation_test_data_db(): - investigation = create_investigation_db_data() - - yield investigation - try: - delete_row_by_id(INVESTIGATION, investigation.id) - except MissingRecordError as e: - # This should occur on DELETE endpoints, normal behaviour for those tests - print(e) - - -@pytest.fixture() -def multiple_investigation_test_data_db(): - investigations = create_investigation_db_data(num_entities=5) - - yield investigations - - for investigation in investigations: - delete_row_by_id(INVESTIGATION, investigation.id) - - -@pytest.fixture() -def related_distinct_data_db(): - investigation = create_investigation_db_data() - - instrument = INSTRUMENT() - instrument.name = "Test Instrument for DataGateway API Endpoint Testing (DB)" - instrument.facilityID = 1 - set_meta_attributes(instrument) - insert_row_into_table(INSTRUMENT, instrument) - - investigation_instrument = INVESTIGATIONINSTRUMENT() - investigation_instrument.investigationID = investigation.id - investigation_instrument.instrumentID = instrument.id - set_meta_attributes(investigation_instrument) - - insert_row_into_table(INVESTIGATIONINSTRUMENT, investigation_instrument) - - yield (instrument.id, investigation) - - delete_row_by_id(INVESTIGATIONINSTRUMENT, investigation_instrument.id) - delete_row_by_id(INVESTIGATION, investigation.id) - delete_row_by_id(INSTRUMENT, instrument.id) - - -@pytest.fixture() -def final_instrument_id(flask_test_app_db, valid_db_credentials_header): - final_instrument_result = flask_test_app_db.get( - f"{Config.config.datagateway_api.extension}/instruments/findone" - '?order="id DESC"', - headers=valid_db_credentials_header, - ) - return final_instrument_result.json["id"] - - -@pytest.fixture() -def final_facilitycycle_id(flask_test_app_db, valid_db_credentials_header): - final_facilitycycle_result = flask_test_app_db.get( - f"{Config.config.datagateway_api.extension}/facilitycycles/findone" - '?order="id DESC"', - headers=valid_db_credentials_header, - ) - return final_facilitycycle_result.json["id"] - - -@pytest.fixture() -def remove_test_created_investigation_data( - flask_test_app_db, valid_db_credentials_header, -): - yield - - created_test_data = flask_test_app_db.get( - f"{Config.config.datagateway_api.extension}/investigations?where=" - '{"name":{"like":' - f'"{TestDBCreateData.investigation_name_prefix}"' - "}}", - headers=valid_db_credentials_header, - ) - - investigation_ids = [] - - for investigation in created_test_data.json: - investigation_ids.append(investigation["id"]) - - for investigation_id in investigation_ids: - flask_test_app_db.delete( - f"{Config.config.datagateway_api.extension}/investigations" - f"/{investigation_id}", - headers=valid_db_credentials_header, - ) diff --git a/test/integration/datagateway_api/db/endpoints/conftest.py b/test/integration/datagateway_api/db/endpoints/conftest.py deleted file mode 100644 index ee58e0a0..00000000 --- a/test/integration/datagateway_api/db/endpoints/conftest.py +++ /dev/null @@ -1,51 +0,0 @@ -from icat.query import Query -import pytest - - -@pytest.fixture() -def icat_query(icat_client): - return Query(icat_client, "Investigation") - - -@pytest.fixture() -def bad_credentials_header(): - return {"Authorization": "Bearer Invalid"} - - -@pytest.fixture() -def invalid_credentials_header(): - return {"Authorization": "Test"} - - -@pytest.fixture() -def test_config_data(): - return { - "datagateway_api": { - "extension": "/datagateway-api", - "backend": "db", - "client_cache_size": 5, - "client_pool_init_size": 2, - "client_pool_max_size": 5, - "db_url": "mysql+pymysql://icatdbuser:icatdbuserpw@localhost:3306/icatdb", - "icat_url": "https://localhost:8181", - "icat_check_cert": False, - }, - "search_api": { - "extension": "/search-api", - "icat_url": "https://localhost.testdomain:8181", - "icat_check_cert": True, - "mechanism": "anon", - "username": "", - "password": "", - }, - "flask_reloader": False, - "log_level": "WARN", - "log_location": "/home/runner/work/datagateway-api/datagateway-api/logs.log", - "debug_mode": False, - "generate_swagger": False, - "host": "127.0.0.1", - "port": "5000", - "test_user_credentials": {"username": "root", "password": "pw"}, - "test_mechanism": "simple", - "url_prefix": "", - } diff --git a/test/integration/datagateway_api/db/endpoints/test_count_with_filters_db.py b/test/integration/datagateway_api/db/endpoints/test_count_with_filters_db.py deleted file mode 100644 index 6005a360..00000000 --- a/test/integration/datagateway_api/db/endpoints/test_count_with_filters_db.py +++ /dev/null @@ -1,29 +0,0 @@ -import pytest - -from datagateway_api.src.common.config import Config - - -class TestDBCountWithFilters: - @pytest.mark.usefixtures("single_investigation_test_data_db") - def test_valid_count_with_filters( - self, flask_test_app_db, valid_db_credentials_header, - ): - test_response = flask_test_app_db.get( - f"{Config.config.datagateway_api.extension}/investigations/count?where=" - '{"title": {"like": "Title for DataGateway API Testing (DB)"}}', - headers=valid_db_credentials_header, - ) - - assert test_response.json == 1 - - def test_valid_no_results_count_with_filters( - self, flask_test_app_db, valid_db_credentials_header, - ): - test_response = flask_test_app_db.get( - f"{Config.config.datagateway_api.extension}/investigations/count?where=" - '{"title": {"like": "This filter should cause a404 for testing ' - 'purposes..."}}', - headers=valid_db_credentials_header, - ) - - assert test_response.json == 0 diff --git a/test/integration/datagateway_api/db/endpoints/test_create_db.py b/test/integration/datagateway_api/db/endpoints/test_create_db.py deleted file mode 100644 index 6258f020..00000000 --- a/test/integration/datagateway_api/db/endpoints/test_create_db.py +++ /dev/null @@ -1,114 +0,0 @@ -import pytest - -from datagateway_api.src.common.config import Config - - -def prepare_db_data_for_assertion(response_json): - response_json.pop("createId") - response_json.pop("createTime") - response_json.pop("id") - response_json.pop("modId") - response_json.pop("modTime") - response_json.pop("visitId") - - if response_json["releaseDate"] is None: - return response_json - - response_json["endDate"] = response_json["endDate"][:-6] - response_json["startDate"] = response_json["startDate"][:-6] - - if response_json["releaseDate"] is None: - return response_json - else: - response_json["releaseDate"] = response_json["releaseDate"][:-6] - - return response_json - - -class TestDBCreateData: - investigation_name_prefix = "DB Test Data for API Testing, Data Creation" - - @pytest.mark.usefixtures("remove_test_created_investigation_data") - def test_valid_create_data(self, flask_test_app_db, valid_db_credentials_header): - create_investigations_json = [ - { - "name": f"{self.investigation_name_prefix} {i}", - "title": "Test data for the DB Backend on DataGateway API", - "summary": "DB Test data for DataGateway API testing", - "releaseDate": "2020-03-03 08:00:08", - "startDate": "2020-02-02 09:00:09", - "endDate": "2020-02-03 10:00:10", - "visitId": "Data Creation Visit DB", - "doi": "DataGateway API DB Test DOI", - "facilityID": 1, - "typeID": 1, - "fileCount": 1, - "fileSize": 6, - } - for i in range(2) - ] - - test_response = flask_test_app_db.post( - f"{Config.config.datagateway_api.extension}/investigations", - headers=valid_db_credentials_header, - json=create_investigations_json, - ) - - response_json = test_response.json - - for investigation_request in response_json: - prepare_db_data_for_assertion(investigation_request) - - for investigation in create_investigations_json: - investigation.pop("visitId") - assert create_investigations_json == response_json - - @pytest.mark.usefixtures("remove_test_created_investigation_data") - def test_valid_boundary_create_data( - self, flask_test_app_db, valid_db_credentials_header, - ): - """Create a single investigation, as opposed to multiple""" - - create_investigation_json = { - "name": f"{self.investigation_name_prefix} 0", - "title": "Test data for the DB Backend on the API", - "summary": "Test data for DataGateway API testing", - "releaseDate": "2020-03-03 08:00:08", - "startDate": "2020-02-02 09:00:09", - "endDate": "2020-02-03 10:00:10", - "visitId": "Data Creation Visit", - "doi": "DataGateway API Test DOI", - "facilityID": 1, - "typeID": 1, - "fileCount": 3, - "fileSize": 2, - } - - test_response = flask_test_app_db.post( - f"{Config.config.datagateway_api.extension}/investigations", - headers=valid_db_credentials_header, - json=create_investigation_json, - ) - - response_json = prepare_db_data_for_assertion(test_response.json) - - create_investigation_json.pop("visitId") - - assert create_investigation_json == response_json - - def test_invalid_create_data( - self, flask_test_app_db, valid_db_credentials_header, - ): - """An investigation requires a minimum of: name, visitId, facility, type""" - - invalid_request_body = { - "title": "Test Title for DataGateway API Backend testing", - } - - test_response = flask_test_app_db.post( - f"{Config.config.datagateway_api.extension}/investigations", - headers=valid_db_credentials_header, - json=invalid_request_body, - ) - - assert test_response.status_code == 400 diff --git a/test/integration/datagateway_api/db/endpoints/test_delete_by_id_db.py b/test/integration/datagateway_api/db/endpoints/test_delete_by_id_db.py deleted file mode 100644 index 759775c6..00000000 --- a/test/integration/datagateway_api/db/endpoints/test_delete_by_id_db.py +++ /dev/null @@ -1,40 +0,0 @@ -from datagateway_api.src.common.config import Config - - -class TestDeleteById: - def test_valid_delete_with_id( - self, - flask_test_app_db, - valid_db_credentials_header, - single_investigation_test_data_db, - ): - single_investigation_test_data = single_investigation_test_data_db.to_dict() - - test_response = flask_test_app_db.delete( - f"{Config.config.datagateway_api.extension}/investigations" - f'/{single_investigation_test_data["id"]}', - headers=valid_db_credentials_header, - ) - - assert test_response.status_code == 204 - - def test_invalid_delete_with_id( - self, flask_test_app_db, valid_db_credentials_header, - ): - """Request with a non-existent ID""" - - final_investigation_result = flask_test_app_db.get( - f"{Config.config.datagateway_api.extension}/investigations" - '/findone?order="id DESC"', - headers=valid_db_credentials_header, - ) - test_data_id = final_investigation_result.json["id"] - - # Adding 100 onto the ID to the most recent result should ensure a 404 - test_response = flask_test_app_db.delete( - f"{Config.config.datagateway_api.extension}/investigations" - f"/{test_data_id + 100}", - headers=valid_db_credentials_header, - ) - - assert test_response.status_code == 404 diff --git a/test/integration/datagateway_api/db/endpoints/test_findone_db.py b/test/integration/datagateway_api/db/endpoints/test_findone_db.py deleted file mode 100644 index 167b2a63..00000000 --- a/test/integration/datagateway_api/db/endpoints/test_findone_db.py +++ /dev/null @@ -1,29 +0,0 @@ -from datagateway_api.src.common.config import Config - - -class TestDBFindone: - def test_valid_findone_with_filters( - self, - flask_test_app_db, - valid_db_credentials_header, - single_investigation_test_data_db, - ): - test_response = flask_test_app_db.get( - f"{Config.config.datagateway_api.extension}/investigations/findone?where=" - '{"title": {"like": "Title for DataGateway API Testing (DB)"}}', - headers=valid_db_credentials_header, - ) - - assert test_response.json == single_investigation_test_data_db.to_dict() - - def test_valid_no_results_findone_with_filters( - self, flask_test_app_db, valid_db_credentials_header, - ): - test_response = flask_test_app_db.get( - f"{Config.config.datagateway_api.extension}/investigations/findone?where=" - '{"title": {"eq": "This filter should cause a404 for testing ' - 'purposes..."}}', - headers=valid_db_credentials_header, - ) - - assert test_response.status_code == 404 diff --git a/test/integration/datagateway_api/db/endpoints/test_get_by_id_db.py b/test/integration/datagateway_api/db/endpoints/test_get_by_id_db.py deleted file mode 100644 index edd12244..00000000 --- a/test/integration/datagateway_api/db/endpoints/test_get_by_id_db.py +++ /dev/null @@ -1,43 +0,0 @@ -from datagateway_api.src.common.config import Config - - -class TestDBGetByID: - def test_valid_get_with_id( - self, - flask_test_app_db, - valid_db_credentials_header, - single_investigation_test_data_db, - ): - # Need to identify the ID given to the test data - investigation_data = flask_test_app_db.get( - f"{Config.config.datagateway_api.extension}/investigations?where=" - '{"title": {"like": "Title for DataGateway API Testing (DB)"}}', - headers=valid_db_credentials_header, - ) - test_data_id = investigation_data.json[0]["id"] - - test_response = flask_test_app_db.get( - f"{Config.config.datagateway_api.extension}/investigations/{test_data_id}", - headers=valid_db_credentials_header, - ) - - assert test_response.json == single_investigation_test_data_db.to_dict() - - def test_invalid_get_with_id( - self, flask_test_app_db, valid_db_credentials_header, - ): - final_investigation_result = flask_test_app_db.get( - f"{Config.config.datagateway_api.extension}/investigations/findone?order=" - '"id DESC"', - headers=valid_db_credentials_header, - ) - test_data_id = final_investigation_result.json["id"] - - # Adding 100 onto the ID to the most recent result should ensure a 404 - test_response = flask_test_app_db.get( - f"{Config.config.datagateway_api.extension}/investigations" - f"/{test_data_id + 100}", - headers=valid_db_credentials_header, - ) - - assert test_response.status_code == 404 diff --git a/test/integration/datagateway_api/db/endpoints/test_get_with_filters.py b/test/integration/datagateway_api/db/endpoints/test_get_with_filters.py deleted file mode 100644 index 3a0bb257..00000000 --- a/test/integration/datagateway_api/db/endpoints/test_get_with_filters.py +++ /dev/null @@ -1,167 +0,0 @@ -import pytest - -from datagateway_api.src.common.config import Config -from datagateway_api.src.common.constants import Constants -from datagateway_api.src.common.date_handler import DateHandler - - -class TestDBGetWithFilters: - def test_valid_get_with_filters( - self, - flask_test_app_db, - valid_db_credentials_header, - single_investigation_test_data_db, - ): - test_response = flask_test_app_db.get( - f"{Config.config.datagateway_api.extension}/investigations?where=" - '{"title": {"like": "Title for DataGateway API Testing (DB)"}}', - headers=valid_db_credentials_header, - ) - - assert test_response.json == [single_investigation_test_data_db.to_dict()] - - def test_valid_no_results_get_with_filters( - self, flask_test_app_db, valid_db_credentials_header, - ): - test_response = flask_test_app_db.get( - f"{Config.config.datagateway_api.extension}/investigations?where=" - '{"title": {"eq": "This filter should cause a 404 fortesting ' - 'purposes..."}}', - headers=valid_db_credentials_header, - ) - - assert test_response.json == [] - - @pytest.mark.usefixtures("multiple_investigation_test_data_db") - def test_valid_get_with_filters_multiple_distinct( - self, flask_test_app_db, valid_db_credentials_header, - ): - test_response = flask_test_app_db.get( - f"{Config.config.datagateway_api.extension}/investigations?where=" - '{"title": {"like": "Title for DataGateway API Testing (DB)"}}' - '&distinct="title"', - headers=valid_db_credentials_header, - ) - - expected = [ - {"title": f"Title for DataGateway API Testing (DB) {i}"} for i in range(5) - ] - - assert test_response.json == expected - - @pytest.mark.parametrize( - "distinct_param, expected_response", - [ - pytest.param( - '"title"', - [{"title": "Title for DataGateway API Testing (DB) 0"}], - id="Single unrelated distinct field", - ), - pytest.param( - '"investigationInstruments.createTime"', - [ - { - "investigationInstruments": { - "createTime": DateHandler.datetime_object_to_str( - Constants.TEST_MOD_CREATE_DATETIME, - ), - }, - }, - ], - id="Single related distinct field", - ), - pytest.param( - '["createTime", "investigationInstruments.createTime"]', - [ - { - "createTime": DateHandler.datetime_object_to_str( - Constants.TEST_MOD_CREATE_DATETIME, - ), - "investigationInstruments": { - "createTime": DateHandler.datetime_object_to_str( - Constants.TEST_MOD_CREATE_DATETIME, - ), - }, - }, - ], - id="Single related distinct field with unrelated field", - ), - pytest.param( - '["investigationInstruments.createTime", "facility.id"]', - [ - { - "facility": {"id": 1}, - "investigationInstruments": { - "createTime": DateHandler.datetime_object_to_str( - Constants.TEST_MOD_CREATE_DATETIME, - ), - }, - }, - ], - id="Multiple related distinct fields", - ), - pytest.param( - '["createTime", "investigationInstruments.createTime", "facility.id"]', - [ - { - "createTime": DateHandler.datetime_object_to_str( - Constants.TEST_MOD_CREATE_DATETIME, - ), - "facility": {"id": 1}, - "investigationInstruments": { - "createTime": DateHandler.datetime_object_to_str( - Constants.TEST_MOD_CREATE_DATETIME, - ), - }, - }, - ], - id="Multiple related distinct fields with unrelated field", - ), - ], - ) - @pytest.mark.usefixtures("related_distinct_data_db") - def test_valid_get_with_filters_related_distinct( - self, - flask_test_app_db, - valid_db_credentials_header, - distinct_param, - expected_response, - ): - test_response = flask_test_app_db.get( - f"{Config.config.datagateway_api.extension}/investigations?where=" - '{"title": {"like": "Title for DataGateway API Testing (DB)"}}' - f"&distinct={distinct_param}", - headers=valid_db_credentials_header, - ) - - print(test_response.json) - - assert test_response.json == expected_response - - def test_limit_skip_merge_get_with_filters( - self, - flask_test_app_db, - valid_db_credentials_header, - multiple_investigation_test_data_db, - ): - skip_value = 1 - limit_value = 2 - - test_response = flask_test_app_db.get( - f"{Config.config.datagateway_api.extension}/investigations?where=" - '{"title": {"like": "Title for DataGateway API Testing (DB)"}}' - f'&skip={skip_value}&limit={limit_value}&order="id ASC"', - headers=valid_db_credentials_header, - ) - - # Copy required to ensure data is deleted at the end of the test - investigation_test_data_copy = multiple_investigation_test_data_db.copy() - filtered_investigation_data = [] - filter_count = 0 - while filter_count < limit_value: - filtered_investigation_data.append( - investigation_test_data_copy.pop(skip_value).to_dict(), - ) - filter_count += 1 - - assert test_response.json == filtered_investigation_data diff --git a/test/integration/datagateway_api/db/endpoints/test_ping_db.py b/test/integration/datagateway_api/db/endpoints/test_ping_db.py deleted file mode 100644 index aa8dfa30..00000000 --- a/test/integration/datagateway_api/db/endpoints/test_ping_db.py +++ /dev/null @@ -1,27 +0,0 @@ -from unittest.mock import patch - -import pytest -from sqlalchemy.exc import SQLAlchemyError - -from datagateway_api.src.common.config import Config -from datagateway_api.src.common.constants import Constants -from datagateway_api.src.common.exceptions import DatabaseError -from datagateway_api.src.datagateway_api.backends import create_backend - - -class TestICATPing: - def test_valid_ping(self, flask_test_app_db): - test_response = flask_test_app_db.get( - f"{Config.config.datagateway_api.extension}/ping", - ) - - assert test_response.json == Constants.PING_OK_RESPONSE - - def test_invalid_ping(self): - with patch( - "sqlalchemy.engine.reflection.Inspector.get_table_names", - side_effect=SQLAlchemyError("Mocked Exception"), - ): - with pytest.raises(DatabaseError): - backend = create_backend("db") - backend.ping() diff --git a/test/integration/datagateway_api/db/endpoints/test_update_by_id_db.py b/test/integration/datagateway_api/db/endpoints/test_update_by_id_db.py deleted file mode 100644 index facb5554..00000000 --- a/test/integration/datagateway_api/db/endpoints/test_update_by_id_db.py +++ /dev/null @@ -1,66 +0,0 @@ -from datagateway_api.src.common.config import Config - - -class TestUpdateByID: - def test_valid_update_with_id( - self, - flask_test_app_db, - valid_db_credentials_header, - single_investigation_test_data_db, - ): - update_data_json = { - "doi": "DB Test Data Identifier", - "summary": "DB Test Summary", - "startDate": "2019-01-04 01:01:01", - } - - single_investigation_test_data = single_investigation_test_data_db.to_dict() - - single_investigation_test_data.update(update_data_json) - test_response = flask_test_app_db.patch( - f"{Config.config.datagateway_api.extension}/investigations" - f"/{single_investigation_test_data['id']}", - headers=valid_db_credentials_header, - json=update_data_json, - ) - - response_json = test_response.json - - # The DB returns times with timezone indicators, - # but does not accept them being created. - # This strips the timezone indicators out so that the results can be compared. - response_json["startDate"] = response_json["startDate"][:-6] - - assert response_json == single_investigation_test_data - - def test_invalid_update_with_id( - self, - flask_test_app_db, - valid_db_credentials_header, - single_investigation_test_data_db, - ): - """This test will attempt to put the DB in an invalid state""" - - invalid_update_json = { - "doi": "_" * 300, - } - - single_investigation_test_data = single_investigation_test_data_db.to_dict() - - test_response = flask_test_app_db.patch( - f"{Config.config.datagateway_api.extension}/investigations" - f"/{single_investigation_test_data['id']}", - headers=valid_db_credentials_header, - json=invalid_update_json, - ) - - print( - "If this test is failing " - "you may need to set sql_mode to " - "STRICT_ALL_TABLES", - ) - - # If this test is failing - # you may need to set sql_mode to - # STRICT_ALL_TABLES - assert test_response.status_code == 400 diff --git a/test/integration/datagateway_api/db/endpoints/test_update_multiple_db.py b/test/integration/datagateway_api/db/endpoints/test_update_multiple_db.py deleted file mode 100644 index dead98a2..00000000 --- a/test/integration/datagateway_api/db/endpoints/test_update_multiple_db.py +++ /dev/null @@ -1,85 +0,0 @@ -from datagateway_api.src.common.config import Config - - -class TestUpdateMultipleEntities: - def test_valid_multiple_update_data( - self, - flask_test_app_db, - valid_db_credentials_header, - multiple_investigation_test_data_db, - ): - expected_doi = "DB Test Data Identifier" - expected_summary = "DB Test summary" - - update_data_list = [] - test_data_list = [] - - for investigation_object in multiple_investigation_test_data_db: - investigation = investigation_object.to_dict() - investigation["doi"] = expected_doi - investigation["summary"] = expected_summary - - update_entity = { - "id": investigation["id"], - "doi": expected_doi, - "summary": expected_summary, - } - update_data_list.append(update_entity) - test_data_list.append(investigation) - - test_response = flask_test_app_db.patch( - f"{Config.config.datagateway_api.extension}/investigations", - headers=valid_db_credentials_header, - json=update_data_list, - ) - - assert test_response.json == test_data_list - - def test_valid_boundary_update_data( - self, - flask_test_app_db, - valid_db_credentials_header, - single_investigation_test_data_db, - ): - """ Request body is a dictionary, not a list of dictionaries""" - - expected_doi = "Test Data Identifier" - expected_summary = "Test Summary" - single_investigation_test_data = single_investigation_test_data_db.to_dict() - - update_data_json = { - "id": single_investigation_test_data["id"], - "doi": expected_doi, - "summary": expected_summary, - } - single_investigation_test_data["doi"] = expected_doi - single_investigation_test_data["summary"] = expected_summary - - test_response = flask_test_app_db.patch( - f"{Config.config.datagateway_api.extension}/investigations", - headers=valid_db_credentials_header, - json=update_data_json, - ) - - assert test_response.json == [single_investigation_test_data] - - def test_invalid_missing_update_data( - self, - flask_test_app_db, - valid_db_credentials_header, - single_investigation_test_data_db, - ): - """There should be an ID in the request body to know which entity to update""" - - update_data_json = { - "doi": "Test Data Identifier", - "summary": "Test Summary", - } - - test_response = flask_test_app_db.patch( - f"{Config.config.datagateway_api.extension}/investigations", - headers=valid_db_credentials_header, - json=update_data_json, - ) - - assert test_response.status_code == 400 diff --git a/test/integration/datagateway_api/db/test_database_filter_utilities.py b/test/integration/datagateway_api/db/test_database_filter_utilities.py deleted file mode 100644 index d0ce50a2..00000000 --- a/test/integration/datagateway_api/db/test_database_filter_utilities.py +++ /dev/null @@ -1,349 +0,0 @@ -import pytest - -from datagateway_api.src.common.exceptions import FilterError -from datagateway_api.src.common.helpers import get_entity_object_from_name -from datagateway_api.src.datagateway_api.database.filters import ( - DatabaseFilterUtilities, - DatabaseWhereFilter, -) -from datagateway_api.src.datagateway_api.database.helpers import ReadQuery -from test.integration.datagateway_api.db.endpoints.test_create_db import ( - prepare_db_data_for_assertion, -) - - -class TestDatabaseFilterUtilities: - @pytest.mark.parametrize( - "input_field, expected_fields", - [ - pytest.param("name", ("name", None, None), id="Unrelated field"), - pytest.param( - "facility.daysUntilRelease", - ("facility", "daysUntilRelease", None), - id="Related field matching ICAT schema name", - ), - pytest.param( - "FACILITY.daysUntilRelease", - ("FACILITY", "daysUntilRelease", None), - id="Related field matching database format (uppercase)", - ), - pytest.param( - "user.investigationUsers.role", - ("user", "investigationUsers", "role"), - id="Related related field (2 levels deep)", - ), - ], - ) - def test_valid_extract_filter_fields(self, input_field, expected_fields): - test_utility = DatabaseFilterUtilities() - test_utility.extract_filter_fields(input_field) - - assert test_utility.field == expected_fields[0] - assert test_utility.related_field == expected_fields[1] - assert test_utility.related_related_field == expected_fields[2] - - def test_invalid_extract_filter_fields(self): - test_utility = DatabaseFilterUtilities() - - with pytest.raises(ValueError): - test_utility.extract_filter_fields( - "user.investigationUsers.investigation.summary", - ) - - @pytest.mark.parametrize( - "input_field", - [ - pytest.param("name", id="No related fields"), - pytest.param("facility.daysUntilRelease", id="Related field"), - pytest.param( - "investigationUsers.user.fullName", id="Related related field", - ), - ], - ) - def test_valid_add_query_join( - self, flask_test_app_db, input_field, - ): - table = get_entity_object_from_name("Investigation") - - test_utility = DatabaseFilterUtilities() - test_utility.extract_filter_fields(input_field) - - expected_query = ReadQuery(table) - if test_utility.related_related_field: - expected_table = get_entity_object_from_name(test_utility.related_field) - - included_table = get_entity_object_from_name(test_utility.field) - expected_query.base_query = expected_query.base_query.join( - included_table, - ).join(expected_table) - elif test_utility.related_field: - expected_table = get_entity_object_from_name(test_utility.field) - - expected_query = ReadQuery(table) - expected_query.base_query = expected_query.base_query.join(expected_table) - else: - expected_table = table - - with ReadQuery(table) as test_query: - test_utility.add_query_join(test_query) - - # Check the JOIN has been applied - assert str(test_query.base_query) == str(expected_query.base_query) - - @pytest.mark.parametrize( - "input_field", - [ - pytest.param("name", id="No related fields"), - pytest.param("facility.daysUntilRelease", id="Related field"), - pytest.param( - "investigationUsers.user.fullName", id="Related related field", - ), - ], - ) - def test_valid_get_entity_model_for_filter(self, input_field): - table = get_entity_object_from_name("Investigation") - - test_utility = DatabaseFilterUtilities() - test_utility.extract_filter_fields(input_field) - - if test_utility.related_related_field: - expected_table = get_entity_object_from_name(test_utility.related_field) - elif test_utility.related_field: - expected_table = get_entity_object_from_name(test_utility.field) - else: - expected_table = table - - with ReadQuery(table) as test_query: - output_field = test_utility.get_entity_model_for_filter(test_query) - - # Check the output is correct - field_name_to_fetch = input_field.split(".")[-1] - assert output_field == getattr(expected_table, field_name_to_fetch) - - def test_valid_get_field(self, flask_test_app_db): - table = get_entity_object_from_name("Investigation") - - test_utility = DatabaseFilterUtilities() - field = test_utility._get_field(table, "name") - - assert field == table.name - - def test_invalid_get_field(self, flask_test_app_db): - table = get_entity_object_from_name("Investigation") - - test_utility = DatabaseFilterUtilities() - with pytest.raises(FilterError): - test_utility._get_field(table, "unknown") - - @pytest.mark.parametrize( - "operation, value, expected_output", - [ - pytest.param( - "eq", - "Title for DataGateway API Testing (DB) 0", - { - "doi": None, - "endDate": "2020-01-08 01:01:01+00:00", - "name": "Test Data for DataGateway API Testing (DB) 0", - "releaseDate": None, - "startDate": "2020-01-04 01:01:01+00:00", - "summary": None, - "title": "Title for DataGateway API Testing (DB) 0", - "facilityID": 1, - "typeID": 1, - "fileSize": 1073741824, - "fileCount": 3, - }, - id="equal", - ), - pytest.param( - "ne", - "Title for DataGateway API Testing (DB) 0", - { - "doi": "0-417-77631-4", - "endDate": "2000-07-09 00:00:00", - "name": "INVESTIGATION 1", - "releaseDate": "2000-07-05 00:00:00", - "startDate": "2000-04-03 00:00:00", - "summary": "Throw hope parent. Receive entire soon." - " War top air agent must voice high describe.\nMonth " - "shake voice. Do discuss despite least face again study." - " Two beyond picture rich fast sea time.", - "title": "Analysis reflect work or hour color maybe." - "\nMuch team discussion message weight.", - "facilityID": 1, - "typeID": 3, - "fileCount": 30, - "fileSize": 3118779841, - }, - id="not equal (ne)", - ), - pytest.param( - "like", - "Title for DataGateway API Testing (DB) 0", - { - "doi": None, - "endDate": "2020-01-08 01:01:01+00:00", - "name": "Test Data for DataGateway API Testing (DB) 0", - "releaseDate": None, - "startDate": "2020-01-04 01:01:01+00:00", - "summary": None, - "title": "Title for DataGateway API Testing (DB) 0", - "facilityID": 1, - "typeID": 1, - "fileSize": 1073741824, - "fileCount": 3, - }, - id="like", - ), - pytest.param( - "nlike", - "Title for DataGateway API Testing (DB) 0", - { - "doi": "0-417-77631-4", - "endDate": "2000-07-09 00:00:00", - "name": "INVESTIGATION 1", - "releaseDate": "2000-07-05 00:00:00", - "startDate": "2000-04-03 00:00:00", - "summary": "Throw hope parent. Receive entire soon. " - "War top air agent must voice high describe.\nMonth " - "shake voice. Do discuss despite least face again study. " - "Two beyond picture rich fast sea time.", - "title": "Analysis reflect work or hour color maybe." - "\nMuch team discussion message weight.", - "facilityID": 1, - "typeID": 3, - "fileSize": 3118779841, - "fileCount": 30, - }, - id="not like", - ), - pytest.param( - "lt", - "Title for DataGateway API Testing (DB) 0", - { - "doi": "0-417-77631-4", - "endDate": "2000-07-09 00:00:00", - "name": "INVESTIGATION 1", - "releaseDate": "2000-07-05 00:00:00", - "startDate": "2000-04-03 00:00:00", - "summary": "Throw hope parent. Receive entire soon. " - "War top air agent must voice high describe.\n" - "Month shake voice. " - "Do discuss despite least face again study. " - "Two beyond picture rich fast sea time.", - "title": "Analysis reflect work or hour color maybe." - "\nMuch team discussion message weight.", - "facilityID": 1, - "typeID": 3, - "fileSize": 3118779841, - "fileCount": 30, - }, - id="less than", - ), - pytest.param( - "lte", - "Title for DataGateway API Testing (DB) 0", - { - "doi": "0-417-77631-4", - "endDate": "2000-07-09 00:00:00", - "name": "INVESTIGATION 1", - "releaseDate": "2000-07-05 00:00:00", - "startDate": "2000-04-03 00:00:00", - "summary": "Throw hope parent. Receive entire soon. " - "War top air agent must voice high describe.\n" - "Month shake voice. " - "Do discuss despite least face again study. " - "Two beyond picture rich fast sea time.", - "title": "Analysis reflect work or hour color maybe." - "\nMuch team discussion message weight.", - "facilityID": 1, - "typeID": 3, - "fileSize": 3118779841, - "fileCount": 30, - }, - id="less than or equal", - ), - pytest.param( - "gt", - "Title for DataGateway API Testing (DB) 0", - { - "doi": "0-9996467-0-2", - "endDate": "2007-07-09 00:00:00", - "name": "INVESTIGATION 29", - "releaseDate": "2007-07-05 00:00:00", - "startDate": "2007-04-03 00:00:00", - "summary": "City plant especially ever eight. Wife street" - " under. Life character drive down. Bag sport benefit also" - " price.\nIncrease spring box successful travel.", - "title": "Usually water six learn bring white development " - "political. Meeting those voice hand.", - "facilityID": 1, - "typeID": 1, - "fileSize": 3700075351, - "fileCount": 30, - }, - id="greater than", - ), - pytest.param( - "gte", - "Title for DataGateway API Testing (DB) 0", - { - "doi": "0-9996467-0-2", - "endDate": "2007-07-09 00:00:00", - "name": "INVESTIGATION 29", - "releaseDate": "2007-07-05 00:00:00", - "startDate": "2007-04-03 00:00:00", - "summary": "City plant especially ever eight. " - "Wife street under. Life character drive down. Bag sport" - " benefit also price.\nIncrease spring box successful" - " travel.", - "title": "Usually water six learn bring white development " - "political. Meeting those voice hand.", - "facilityID": 1, - "typeID": 1, - "fileSize": 3700075351, - "fileCount": 30, - }, - id="greater than or equal", - ), - pytest.param( - "in", - ["Title for DataGateway API Testing (DB) 0"], - { - "doi": None, - "endDate": "2020-01-08 01:01:01+00:00", - "name": "Test Data for DataGateway API Testing (DB) 0", - "releaseDate": None, - "startDate": "2020-01-04 01:01:01+00:00", - "summary": None, - "title": "Title for DataGateway API Testing (DB) 0", - "facilityID": 1, - "typeID": 1, - "fileSize": 1073741824, - "fileCount": 3, - }, - id="in", - ), - ], - ) - def test_valid_where_operation( - self, - flask_test_app_db, - operation, - value, - expected_output, - single_investigation_test_data_db, - ): - test_utility = DatabaseWhereFilter("title", value, operation) - table = get_entity_object_from_name("Investigation") - - test_query = ReadQuery(table) - - test_utility.apply_filter(test_query) - - response_json = prepare_db_data_for_assertion( - test_query.base_query.first().to_dict(), - ) - - assert response_json == expected_output diff --git a/test/integration/datagateway_api/db/test_entity_helper.py b/test/integration/datagateway_api/db/test_entity_helper.py deleted file mode 100644 index adae61cf..00000000 --- a/test/integration/datagateway_api/db/test_entity_helper.py +++ /dev/null @@ -1,202 +0,0 @@ -import pytest - -from datagateway_api.src.common.constants import Constants -from datagateway_api.src.datagateway_api.database.models import ( - DATAFILE, - DATAFILEFORMAT, - DATASET, - INVESTIGATION, -) - - -@pytest.fixture() -def dataset_entity(): - dataset = DATASET() - investigation = INVESTIGATION() - dataset.INVESTIGATION = investigation - - return dataset - - -@pytest.fixture() -def datafile_entity(dataset_entity): - datafileformat = DATAFILEFORMAT() - datafile = DATAFILE() - datafile.id = 1 - datafile.location = "test location" - datafile.DATASET = dataset_entity - datafile.DATAFILEFORMAT = datafileformat - datafile.name = "test name" - datafile.modTime = Constants.TEST_MOD_CREATE_DATETIME - datafile.createTime = Constants.TEST_MOD_CREATE_DATETIME - datafile.checksum = "test checksum" - datafile.fileSize = 64 - datafile.datafileModTime = Constants.TEST_MOD_CREATE_DATETIME - datafile.datafileCreateTime = Constants.TEST_MOD_CREATE_DATETIME - datafile.datasetID = 1 - datafile.doi = "test doi" - datafile.description = "test description" - datafile.createId = "test create id" - datafile.modId = "test mod id" - datafile.datafileFormatID = 1 - - return datafile - - -class TestEntityHelper: - def test_valid_to_dict(self, datafile_entity): - expected_dict = { - "id": 1, - "location": "test location", - "name": "test name", - "modTime": str(Constants.TEST_MOD_CREATE_DATETIME), - "checksum": "test checksum", - "fileSize": 64, - "datafileModTime": str(Constants.TEST_MOD_CREATE_DATETIME), - "datafileCreateTime": str(Constants.TEST_MOD_CREATE_DATETIME), - "datasetID": 1, - "doi": "test doi", - "description": "test description", - "createId": "test create id", - "modId": "test mod id", - "datafileFormatID": 1, - "createTime": str(Constants.TEST_MOD_CREATE_DATETIME), - } - - test_data = datafile_entity.to_dict() - - assert expected_dict == test_data - - @pytest.mark.parametrize( - "expected_dict, entity_names", - [ - pytest.param( - { - "id": 1, - "location": "test location", - "name": "test name", - "modTime": str(Constants.TEST_MOD_CREATE_DATETIME), - "checksum": "test checksum", - "fileSize": 64, - "datafileModTime": str(Constants.TEST_MOD_CREATE_DATETIME), - "datafileCreateTime": str(Constants.TEST_MOD_CREATE_DATETIME), - "doi": "test doi", - "description": "test description", - "createId": "test create id", - "modId": "test mod id", - "datafileFormatID": 1, - "createTime": str(Constants.TEST_MOD_CREATE_DATETIME), - "datasetID": 1, - "dataset": { - "id": None, - "createTime": None, - "modTime": None, - "createId": None, - "modId": None, - "investigationID": None, - "complete": None, - "description": None, - "doi": None, - "endDate": None, - "location": None, - "name": None, - "startDate": None, - "sampleID": None, - "typeID": None, - }, - }, - "dataset", - id="Dataset", - ), - pytest.param( - { - "id": 1, - "location": "test location", - "name": "test name", - "modTime": str(Constants.TEST_MOD_CREATE_DATETIME), - "checksum": "test checksum", - "fileSize": 64, - "datafileModTime": str(Constants.TEST_MOD_CREATE_DATETIME), - "datafileCreateTime": str(Constants.TEST_MOD_CREATE_DATETIME), - "doi": "test doi", - "description": "test description", - "createId": "test create id", - "modId": "test mod id", - "datafileFormatID": 1, - "createTime": str(Constants.TEST_MOD_CREATE_DATETIME), - "datasetID": 1, - "dataset": { - "id": None, - "createTime": None, - "modTime": None, - "createId": None, - "modId": None, - "complete": None, - "description": None, - "doi": None, - "endDate": None, - "location": None, - "name": None, - "startDate": None, - "sampleID": None, - "typeID": None, - "investigationID": None, - "investigation": { - "id": None, - "createId": None, - "createTime": None, - "fileCount": None, - "fileSize": None, - "doi": None, - "endDate": None, - "modId": None, - "modTime": None, - "name": None, - "releaseDate": None, - "startDate": None, - "summary": None, - "title": None, - "visitId": None, - "facilityID": None, - "typeID": None, - }, - }, - }, - {"dataset": "investigation"}, - id="Dataset including investigation", - ), - ], - ) - def test_valid_to_nested_dict(self, datafile_entity, expected_dict, entity_names): - test_data = datafile_entity.to_nested_dict(entity_names) - - assert expected_dict == test_data - - def test_valid_get_related_entity(self, dataset_entity, datafile_entity): - assert dataset_entity == datafile_entity.get_related_entity("DATASET") - - def test_valid_update_from_dict(self, datafile_entity): - datafile = DATAFILE() - test_dict_data = { - "id": 1, - "location": "test location", - "name": "test name", - "modTime": str(Constants.TEST_MOD_CREATE_DATETIME), - "checksum": "test checksum", - "fileSize": 64, - "datafileModTime": str(Constants.TEST_MOD_CREATE_DATETIME), - "datafileCreateTime": str(Constants.TEST_MOD_CREATE_DATETIME), - "datasetID": 1, - "doi": "test doi", - "description": "test description", - "createId": "test create id", - "modId": "test mod id", - "datafileFormatID": 1, - "createTime": str(Constants.TEST_MOD_CREATE_DATETIME), - } - - datafile.update_from_dict(test_dict_data) - - expected_datafile_dict = datafile_entity.to_dict() - - assert test_dict_data == expected_datafile_dict diff --git a/test/integration/datagateway_api/db/test_requires_session_id.py b/test/integration/datagateway_api/db/test_requires_session_id.py deleted file mode 100644 index 392d5e96..00000000 --- a/test/integration/datagateway_api/db/test_requires_session_id.py +++ /dev/null @@ -1,47 +0,0 @@ -from datagateway_api.src.common.config import Config - - -class TestRequiresSessionID: - """ - This class tests the session decorator used for the database backend. The equivalent - decorator for the Python ICAT backend is tested in `test_session_handling.py` - """ - - def test_login(self, flask_test_app_db): - test_response = flask_test_app_db.post( - f"{Config.config.datagateway_api.extension}/sessions", - json={"username": "user", "password": "password", "mechanism": "simple"}, - ) - - assert test_response.status_code == 201 - - def test_invalid_missing_credentials(self, flask_test_app_db): - test_response = flask_test_app_db.get( - f"{Config.config.datagateway_api.extension}/datafiles", - ) - - assert test_response.status_code == 401 - - def test_invalid_credentials(self, flask_test_app_db, invalid_credentials_header): - test_response = flask_test_app_db.get( - f"{Config.config.datagateway_api.extension}/datafiles", - headers=invalid_credentials_header, - ) - - assert test_response.status_code == 403 - - def test_bad_credentials(self, flask_test_app_db, bad_credentials_header): - test_response = flask_test_app_db.get( - f"{Config.config.datagateway_api.extension}/datafiles", - headers=bad_credentials_header, - ) - - assert test_response.status_code == 403 - - def test_valid_credentials(self, flask_test_app_db, valid_db_credentials_header): - test_response = flask_test_app_db.get( - f"{Config.config.datagateway_api.extension}/datafiles?limit=0", - headers=valid_db_credentials_header, - ) - - assert test_response.status_code == 200 diff --git a/test/integration/datagateway_api/icat/endpoints/test_ping_icat.py b/test/integration/datagateway_api/icat/endpoints/test_ping_icat.py index faf0b42a..e9073468 100644 --- a/test/integration/datagateway_api/icat/endpoints/test_ping_icat.py +++ b/test/integration/datagateway_api/icat/endpoints/test_ping_icat.py @@ -6,8 +6,8 @@ from datagateway_api.src.common.config import Config from datagateway_api.src.common.constants import Constants from datagateway_api.src.common.exceptions import PythonICATError -from datagateway_api.src.datagateway_api.backends import create_backend from datagateway_api.src.datagateway_api.icat.icat_client_pool import create_client_pool +from datagateway_api.src.datagateway_api.icat.python_icat import PythonICAT class TestICATPing: @@ -24,6 +24,6 @@ def test_invalid_ping(self): side_effect=ICATError("Mocked Exception"), ): with pytest.raises(PythonICATError): - backend = create_backend("python_icat") + python_icat = PythonICAT() client_pool = create_client_pool() - backend.ping(client_pool=client_pool) + python_icat.ping(client_pool=client_pool) diff --git a/test/integration/datagateway_api/icat/test_session_handling.py b/test/integration/datagateway_api/icat/test_session_handling.py index 20155faf..1a89a886 100644 --- a/test/integration/datagateway_api/icat/test_session_handling.py +++ b/test/integration/datagateway_api/icat/test_session_handling.py @@ -5,12 +5,13 @@ from icat.client import Client import pytest + from datagateway_api.src.common.config import Config from datagateway_api.src.common.date_handler import DateHandler from datagateway_api.src.common.exceptions import AuthenticationError -from datagateway_api.src.datagateway_api.backends import create_backend from datagateway_api.src.datagateway_api.icat.filters import PythonICATWhereFilter from datagateway_api.src.datagateway_api.icat.icat_client_pool import create_client_pool +from datagateway_api.src.datagateway_api.icat.python_icat import PythonICAT class TestSessionHandling: @@ -141,11 +142,13 @@ def test_invalid_login( assert login_response.status_code == expected_response_code def test_expired_session(self): - test_backend = create_backend("python_icat") + test_python_icat = PythonICAT() client_pool = create_client_pool() with patch("icat.client.Client.getRemainingMinutes", return_value=-1): with pytest.raises(AuthenticationError): - test_backend.get_session_details("session id", client_pool=client_pool) + test_python_icat.get_session_details( + "session id", client_pool=client_pool, + ) def test_valid_logout(self, flask_test_app_icat): client = Client( diff --git a/test/integration/datagateway_api/test_backends.py b/test/integration/datagateway_api/test_backends.py deleted file mode 100644 index 26d597b4..00000000 --- a/test/integration/datagateway_api/test_backends.py +++ /dev/null @@ -1,56 +0,0 @@ -import pytest - -from datagateway_api.src.datagateway_api.backend import Backend -from datagateway_api.src.datagateway_api.backends import create_backend -from datagateway_api.src.datagateway_api.database.backend import DatabaseBackend -from datagateway_api.src.datagateway_api.icat.backend import PythonICATBackend - - -class TestBackends: - @pytest.mark.parametrize( - "backend_name, backend_type", - [ - pytest.param("db", DatabaseBackend, id="Database Backend"), - pytest.param("python_icat", PythonICATBackend, id="Python ICAT Backend"), - ], - ) - def test_valid_backend_creation(self, backend_name, backend_type): - test_backend = create_backend(backend_name) - - assert type(test_backend) == backend_type - - def test_invalid_backend_creation(self): - with pytest.raises(SystemExit): - create_backend("invalid_backend_name") - - def test_abstract_class(self): - """ - Test the `Backend` abstract class has all required abstract methods for the API - """ - Backend.__abstractmethods__ = set() - - class DummyBackend(Backend): - pass - - d = DummyBackend() - - credentials = "credentials" - session_id = "session_id" - entity_type = "entity_type" - filters = "filters" - data = "data" - id_ = "id_" - - assert d.ping() is None - assert d.login(credentials) is None - assert d.get_session_details(session_id) is None - assert d.refresh(session_id) is None - assert d.logout(session_id) is None - assert d.get_with_filters(session_id, entity_type, filters) is None - assert d.create(session_id, entity_type, data) is None - assert d.update(session_id, entity_type, data) is None - assert d.get_one_with_filters(session_id, entity_type, filters) is None - assert d.count_with_filters(session_id, entity_type, filters) is None - assert d.get_with_id(session_id, entity_type, id_) is None - assert d.delete_with_id(session_id, entity_type, id_) is None - assert d.update_with_id(session_id, entity_type, id_, data) is None diff --git a/test/integration/datagateway_api/test_query_filter_factory.py b/test/integration/datagateway_api/test_query_filter_factory.py index e3b0119f..b469834f 100644 --- a/test/integration/datagateway_api/test_query_filter_factory.py +++ b/test/integration/datagateway_api/test_query_filter_factory.py @@ -1,12 +1,12 @@ import pytest -from datagateway_api.src.datagateway_api.database.filters import ( - DatabaseDistinctFieldFilter, - DatabaseIncludeFilter, - DatabaseLimitFilter, - DatabaseOrderFilter, - DatabaseSkipFilter, - DatabaseWhereFilter, +from datagateway_api.src.datagateway_api.icat.filters import ( + PythonICATDistinctFieldFilter, + PythonICATIncludeFilter, + PythonICATLimitFilter, + PythonICATOrderFilter, + PythonICATSkipFilter, + PythonICATWhereFilter, ) from datagateway_api.src.datagateway_api.query_filter_factory import ( DataGatewayAPIQueryFilterFactory, @@ -19,7 +19,7 @@ def test_valid_distinct_filter(self): test_filter = DataGatewayAPIQueryFilterFactory.get_query_filter( {"distinct": "TEST"}, ) - assert isinstance(test_filter[0], DatabaseDistinctFieldFilter) + assert isinstance(test_filter[0], PythonICATDistinctFieldFilter) assert len(test_filter) == 1 @pytest.mark.usefixtures("flask_test_app_db") @@ -36,13 +36,13 @@ def test_valid_distinct_filter(self): ) def test_valid_include_filter(self, filter_input): test_filter = DataGatewayAPIQueryFilterFactory.get_query_filter(filter_input) - assert isinstance(test_filter[0], DatabaseIncludeFilter) + assert isinstance(test_filter[0], PythonICATIncludeFilter) assert len(test_filter) == 1 @pytest.mark.usefixtures("flask_test_app_db") def test_valid_limit_filter(self): test_filter = DataGatewayAPIQueryFilterFactory.get_query_filter({"limit": 10}) - assert isinstance(test_filter[0], DatabaseLimitFilter) + assert isinstance(test_filter[0], PythonICATLimitFilter) assert len(test_filter) == 1 @pytest.mark.usefixtures("flask_test_app_db") @@ -50,13 +50,13 @@ def test_valid_order_filter(self): test_filter = DataGatewayAPIQueryFilterFactory.get_query_filter( {"order": "id DESC"}, ) - assert isinstance(test_filter[0], DatabaseOrderFilter) + assert isinstance(test_filter[0], PythonICATOrderFilter) assert len(test_filter) == 1 @pytest.mark.usefixtures("flask_test_app_db") def test_valid_skip_filter(self): test_filter = DataGatewayAPIQueryFilterFactory.get_query_filter({"skip": 10}) - assert isinstance(test_filter[0], DatabaseSkipFilter) + assert isinstance(test_filter[0], PythonICATSkipFilter) assert len(test_filter) == 1 @pytest.mark.usefixtures("flask_test_app_db") @@ -75,5 +75,5 @@ def test_valid_skip_filter(self): ) def test_valid_where_filter(self, filter_input): test_filter = DataGatewayAPIQueryFilterFactory.get_query_filter(filter_input) - assert isinstance(test_filter[0], DatabaseWhereFilter) + assert isinstance(test_filter[0], PythonICATWhereFilter) assert len(test_filter) == 1 diff --git a/test/integration/test_config.py b/test/integration/test_config.py deleted file mode 100644 index 122c2f8a..00000000 --- a/test/integration/test_config.py +++ /dev/null @@ -1,9 +0,0 @@ -class TestAPIConfig: - def test_load_with_valid_config_data(self, test_config): - backend_type = test_config.datagateway_api.backend - assert backend_type == "db" - - def test_set_backend_type(self, test_config): - test_config.datagateway_api.set_backend_type("backend_name_changed") - - assert test_config.datagateway_api.backend == "backend_name_changed" diff --git a/test/integration/test_get_filters_from_query.py b/test/integration/test_get_filters_from_query.py index 02f98d47..1c43198e 100644 --- a/test/integration/test_get_filters_from_query.py +++ b/test/integration/test_get_filters_from_query.py @@ -1,14 +1,4 @@ -import pytest - -from datagateway_api.src.common.exceptions import FilterError from datagateway_api.src.common.helpers import get_filters_from_query_string -from datagateway_api.src.datagateway_api.database.filters import ( - DatabaseDistinctFieldFilter, - DatabaseIncludeFilter, - DatabaseLimitFilter, - DatabaseOrderFilter, - DatabaseSkipFilter, -) class TestGetFiltersFromQueryString: @@ -18,27 +8,6 @@ def test_valid_no_filters(self, flask_test_app_db): assert [] == get_filters_from_query_string("datagateway_api") - def test_invalid_filter(self, flask_test_app_db): - with flask_test_app_db: - flask_test_app_db.get('/?test="test"') - - with pytest.raises(FilterError): - get_filters_from_query_string("datagateway_api") - - @pytest.mark.parametrize( - "filter_input, filter_type", - [ - pytest.param( - 'distinct="id"', DatabaseDistinctFieldFilter, id="DB distinct filter", - ), - pytest.param( - 'include="TEST"', DatabaseIncludeFilter, id="DB include filter", - ), - pytest.param("limit=10", DatabaseLimitFilter, id="DB limit filter"), - pytest.param('order="id DESC"', DatabaseOrderFilter, id="DB order filter"), - pytest.param("skip=10", DatabaseSkipFilter, id="DB skip filter"), - ], - ) def test_valid_filter(self, flask_test_app_db, filter_input, filter_type): with flask_test_app_db: flask_test_app_db.get(f"/?{filter_input}") diff --git a/test/unit/test_config.py b/test/unit/test_config.py index 771ddfe9..46932274 100644 --- a/test/unit/test_config.py +++ b/test/unit/test_config.py @@ -18,18 +18,9 @@ def test_load_with_missing_mandatory_config_data(self, test_config_data): with pytest.raises(SystemExit): APIConfig.load("test/path") - def test_load_with_datagateway_api_db_backend_and_missing_db_config_data( - self, test_config_data, - ): - del test_config_data["datagateway_api"]["db_url"] - with patch("builtins.open", mock_open(read_data=json.dumps(test_config_data))): - with pytest.raises(SystemExit): - APIConfig.load("test/path") - def test_load_with_datagateway_api_icat_backend_and_missing_icat_config_data( self, test_config_data, ): - test_config_data["datagateway_api"]["backend"] = "python_icat" del test_config_data["datagateway_api"]["icat_url"] with patch("builtins.open", mock_open(read_data=json.dumps(test_config_data))): with pytest.raises(SystemExit): diff --git a/test/unit/test_get_entity_object.py b/test/unit/test_get_entity_object.py index e84bc0b0..c13f560b 100644 --- a/test/unit/test_get_entity_object.py +++ b/test/unit/test_get_entity_object.py @@ -2,7 +2,7 @@ from datagateway_api.src.common.exceptions import ApiError from datagateway_api.src.common.helpers import get_entity_object_from_name -from datagateway_api.src.datagateway_api.database.models import ( +from datagateway_api.src.datagateway_api.icat.models import ( FACILITY, INVESTIGATION, JOB, diff --git a/test/unit/test_query_filter.py b/test/unit/test_query_filter.py index d874f706..792c9584 100644 --- a/test/unit/test_query_filter.py +++ b/test/unit/test_query_filter.py @@ -1,11 +1,4 @@ -import pytest - -from datagateway_api.src.common.config import Config -from datagateway_api.src.common.exceptions import ApiError from datagateway_api.src.common.filters import QueryFilter -from datagateway_api.src.datagateway_api.query_filter_factory import ( - DataGatewayAPIQueryFilterFactory, -) class TestQueryFilter: @@ -23,8 +16,3 @@ class DummyQueryFilter(QueryFilter): assert qf.precedence is None assert qf.apply_filter(apply_filter) is None - - def test_invalid_query_filter_getter(self): - Config.config.datagateway_api.backend = "invalid_backend" - with pytest.raises(ApiError): - DataGatewayAPIQueryFilterFactory.get_query_filter({"order": "id DESC"})