diff --git a/api/admin/controller/collection_settings.py b/api/admin/controller/collection_settings.py index ba196058c3..dff96c4d04 100644 --- a/api/admin/controller/collection_settings.py +++ b/api/admin/controller/collection_settings.py @@ -102,7 +102,7 @@ def process_get(self): collection_dict["libraries"] = libraries collection_dict[ "settings" - ] = collection_object.integration_configuration.settings + ] = collection_object.integration_configuration.settings_dict self.load_settings( protocol["settings"], collection_object, collection_dict["settings"] ) @@ -145,7 +145,7 @@ def load_libraries(self, collection_object: Collection, user: Admin) -> List[Dic # Find and update the library settings if they exist for config in integration.library_configurations: if library.id == config.library_id: - library_info.update(config.settings) + library_info.update(config.settings_dict) break libraries.append(library_info) @@ -343,7 +343,7 @@ def process_settings(self, settings, collection): settings_class(**collection_settings) except ProblemError as ex: return ex.problem_detail - collection.integration_configuration.settings = collection_settings + collection.integration_configuration.settings_dict = collection_settings def _set_external_integration_link( self, diff --git a/api/admin/controller/patron_auth_service_self_tests.py b/api/admin/controller/patron_auth_service_self_tests.py index 15b8c6b9c1..f3608406db 100644 --- a/api/admin/controller/patron_auth_service_self_tests.py +++ b/api/admin/controller/patron_auth_service_self_tests.py @@ -107,7 +107,7 @@ def get_info(patron_auth_service: IntegrationConfiguration): name=patron_auth_service.name, protocol=patron_auth_service.protocol, goal=patron_auth_service.goal, - settings=patron_auth_service.settings, + settings=patron_auth_service.settings_dict, ) return info @@ -122,8 +122,8 @@ def run_tests(self, integration: IntegrationConfiguration) -> Dict[str, Any]: ) ) - if not isinstance(integration.settings, dict) or not isinstance( - library_configuration.settings, dict + if not isinstance(integration.settings_dict, dict) or not isinstance( + library_configuration.settings_dict, dict ): raise ProblemError( problem_detail=FAILED_TO_RUN_SELF_TESTS.detailed( @@ -132,9 +132,9 @@ def run_tests(self, integration: IntegrationConfiguration) -> Dict[str, Any]: ) protocol_class = self.get_protocol_class(integration) - settings = protocol_class.settings_class()(**integration.settings) + settings = protocol_class.settings_class()(**integration.settings_dict) library_settings = protocol_class.library_settings_class()( - **library_configuration.settings + **library_configuration.settings_dict ) value, _ = protocol_class.run_self_tests( diff --git a/api/admin/controller/patron_auth_services.py b/api/admin/controller/patron_auth_services.py index 855e08c0ea..c601c4fe86 100644 --- a/api/admin/controller/patron_auth_services.py +++ b/api/admin/controller/patron_auth_services.py @@ -91,14 +91,14 @@ def configured_services(self) -> List[Dict[str, Any]]: libraries = [] for library_settings in service.library_configurations: library_info = {"short_name": library_settings.library.short_name} - library_info.update(library_settings.settings) + library_info.update(library_settings.settings_dict) libraries.append(library_info) service_info = { "id": service.id, "name": service.name, "protocol": service.protocol, - "settings": service.settings, + "settings": service.settings_dict, "libraries": libraries, } configured_services.append(service_info) @@ -240,7 +240,7 @@ def process_libraries( # Update new and existing libraries settings for integration, settings in chain(new, updated): validated_settings = settings_class(**settings) - integration.settings = validated_settings.dict() + integration.settings_dict = validated_settings.dict() # Make sure library doesn't have multiple auth basic auth services self.check_library_integrations(integration.library) @@ -276,7 +276,7 @@ def process_post(self) -> Union[Response, ProblemDetail]: impl_cls = self.registry[protocol] settings_class = impl_cls.settings_class() validated_settings = ProcessFormData.get_settings(settings_class, form_data) - auth_service.settings = validated_settings.dict() + auth_service.settings_dict = validated_settings.dict() # Update library settings if libraries_data: diff --git a/api/admin/controller/settings.py b/api/admin/controller/settings.py index 0e7f76fa33..a07a5dac43 100644 --- a/api/admin/controller/settings.py +++ b/api/admin/controller/settings.py @@ -372,7 +372,7 @@ def _set_configuration_library( protocol_class.library_settings_class()(**info_copy) # Attach the configuration config = configuration.for_library(cast(int, library.id), create=True) - config.settings = info_copy + config.settings_dict = info_copy return config def _set_integration_library(self, integration, library_info, protocol): diff --git a/api/authenticator.py b/api/authenticator.py index d693f9da52..2808c0419c 100644 --- a/api/authenticator.py +++ b/api/authenticator.py @@ -352,18 +352,20 @@ def register_provider( f"Implementation class {impl_cls} is not an AuthenticationProvider." ) try: - if not isinstance(integration.parent.settings, dict): + if not isinstance(integration.parent.settings_dict, dict): raise CannotLoadConfiguration( f"Settings for {impl_cls.__name__} authentication provider for " f"library {self.library_short_name} are not a dictionary." ) - if not isinstance(integration.settings, dict): + if not isinstance(integration.settings_dict, dict): raise CannotLoadConfiguration( f"Library settings for {impl_cls.__name__} authentication provider for " f"library {self.library_short_name} are not a dictionary." ) - settings = impl_cls.settings_class()(**integration.parent.settings) - library_settings = impl_cls.library_settings_class()(**integration.settings) + settings = impl_cls.settings_class()(**integration.parent.settings_dict) + library_settings = impl_cls.library_settings_class()( + **integration.settings_dict + ) provider = impl_cls( self.library_id, # type: ignore[arg-type] integration.parent_id, # type: ignore[arg-type] diff --git a/api/circulation.py b/api/circulation.py index 2c9c303944..cc4e7de512 100644 --- a/api/circulation.py +++ b/api/circulation.py @@ -492,12 +492,12 @@ def integration_configuration(self) -> IntegrationConfiguration: def library_configuration(self, library_id) -> LibrarySettingsType | None: libconfig = self.integration_configuration().for_library(library_id=library_id) if libconfig: - config = self.library_settings_class()(**libconfig.settings) + config = self.library_settings_class()(**libconfig.settings_dict) return config # type: ignore [return-value] return None def configuration(self) -> SettingsType: - return self.settings_class()(**self.integration_configuration().settings) # type: ignore [return-value] + return self.settings_class()(**self.integration_configuration().settings_dict) # type: ignore [return-value] class BaseCirculationAPIProtocol(BaseCirculationAPIIntegrationProtocol): diff --git a/api/lcp/encrypt.py b/api/lcp/encrypt.py index 40d6f6c5d2..dfb158800e 100644 --- a/api/lcp/encrypt.py +++ b/api/lcp/encrypt.py @@ -299,13 +299,15 @@ def __init__( :param configuration: IntegrationConfiguration instance """ - self._lcpencrypt_location = configuration.settings.get( + self._lcpencrypt_location = configuration.settings_dict.get( "lcpencrypt_location" ) self._input_file_path = str(file_path) self._content_id = str(identifier) - output_directory = configuration.settings.get("lcpencrypt_output_directory") + output_directory = configuration.settings_dict.get( + "lcpencrypt_output_directory" + ) self._output_file_path = None @@ -388,7 +390,9 @@ def __init__(self, configuration: IntegrationConfiguration): def _lcpencrypt_exists_locally(self): """Returns a Boolean value indicating whether lcpencrypt exists locally""" - return os.path.isfile(self.configuration.settings.get("lcpencrypt_location")) + return os.path.isfile( + self.configuration.settings_dict.get("lcpencrypt_location") + ) def _parse_output(self, output): """Parses lcpencrypt's output diff --git a/api/lcp/mirror.py b/api/lcp/mirror.py index 561d0ddd1a..729daa5e36 100644 --- a/api/lcp/mirror.py +++ b/api/lcp/mirror.py @@ -68,7 +68,7 @@ def _create_lcp_importer(self, collection): credential_factory = LCPCredentialFactory() lcp_encryptor = LCPEncryptor(configuration) lcp_server = LCPServer( - lambda: LCPServerSettings(**configuration.settings), + lambda: LCPServerSettings(**configuration.settings_dict), hasher_factory, credential_factory, ) diff --git a/api/opds.py b/api/opds.py index b6165556f9..cab4ff9c5f 100644 --- a/api/opds.py +++ b/api/opds.py @@ -193,11 +193,12 @@ def _prioritized_formats_for_pool( # information _might_ contain a set of prioritized DRM schemes and # content types. prioritized_drm_schemes: list[str] = ( - config.settings.get(FormatPriorities.PRIORITIZED_DRM_SCHEMES_KEY) or [] + config.settings_dict.get(FormatPriorities.PRIORITIZED_DRM_SCHEMES_KEY) or [] ) content_setting: List[str] = ( - config.settings.get(FormatPriorities.PRIORITIZED_CONTENT_TYPES_KEY) or [] + config.settings_dict.get(FormatPriorities.PRIORITIZED_CONTENT_TYPES_KEY) + or [] ) return prioritized_drm_schemes, content_setting @@ -214,7 +215,7 @@ def _deprioritized_lcp_content( # LCP content. By default, if no configuration value is specified, then # the priority of LCP content will be left completely unchanged. - _prioritize: bool = config.settings.get( + _prioritize: bool = config.settings_dict.get( FormatPriorities.DEPRIORITIZE_LCP_NON_EPUBS_KEY, False ) return _prioritize diff --git a/api/opds2.py b/api/opds2.py index 4038c59c7d..8d6a34ef21 100644 --- a/api/opds2.py +++ b/api/opds2.py @@ -109,7 +109,7 @@ def fulfill( if "authentication_token" not in templated.variable_names: return fulfillment - token_auth = licensepool.collection.integration_configuration.settings.get( + token_auth = licensepool.collection.integration_configuration.settings_dict.get( ExternalIntegration.TOKEN_AUTH ) if token_auth is None: diff --git a/api/saml/wayfless.py b/api/saml/wayfless.py index 14187084c0..8346a1731e 100644 --- a/api/saml/wayfless.py +++ b/api/saml/wayfless.py @@ -48,7 +48,7 @@ def __init__(self, collection: Collection) -> None: external: ExternalIntegration = collection.external_integration self._wayfless_url_template: Optional[ str - ] = collection.integration_configuration.settings.get( + ] = collection.integration_configuration.settings_dict.get( SAMLWAYFlessConstants.WAYFLESS_URL_TEMPLATE_KEY ) diff --git a/api/shared_collection.py b/api/shared_collection.py index 9b7af9fe12..878b1d9301 100644 --- a/api/shared_collection.py +++ b/api/shared_collection.py @@ -131,7 +131,7 @@ def register(self, collection, auth_document_url, do_get=HTTP.get_with_timeout): ) external_library_urls = ( - collection.integration_configuration.settings.get( + collection.integration_configuration.settings_dict.get( BaseSharedCollectionAPI.EXTERNAL_LIBRARY_URLS ) or [] @@ -173,7 +173,7 @@ def register(self, collection, auth_document_url, do_get=HTTP.get_with_timeout): def check_client_authorization(self, collection, client): """Verify that an IntegrationClient is whitelisted for access to the collection.""" - external_library_urls = collection.integration_configuration.settings.get( + external_library_urls = collection.integration_configuration.settings_dict.get( BaseSharedCollectionAPI.EXTERNAL_LIBRARY_URLS, [] ) if client.url not in [ diff --git a/core/configuration/ignored_identifier.py b/core/configuration/ignored_identifier.py index 828b776c55..db7d7bdca8 100644 --- a/core/configuration/ignored_identifier.py +++ b/core/configuration/ignored_identifier.py @@ -53,7 +53,7 @@ def _get_ignored_identifier_types( :return: Set of ignored identifier types """ if self._ignored_identifier_types is None: - self._ignored_identifier_types = configuration.settings.get( + self._ignored_identifier_types = configuration.settings_dict.get( "ignored_identifier_types", [] ) @@ -83,7 +83,7 @@ def set_ignored_identifier_types( "Argument 'value' must contain string or IdentifierType enumeration's items only" ) - settings = configuration.settings.copy() + settings = configuration.settings_dict.copy() settings["ignored_identifier_types"] = ignored_identifier_types - configuration.settings = settings + configuration.settings_dict = settings self._ignored_identifier_types = None diff --git a/core/marc.py b/core/marc.py index ccb1d89472..85598035ba 100644 --- a/core/marc.py +++ b/core/marc.py @@ -616,7 +616,7 @@ def get_storage_settings(cls, _db): # Only add an integration to choose from if it has a # MARC File Bucket field in its settings. configuration_settings = [ - s for s in integration.settings if s.key == "marc_bucket" + s for s in integration.settings_dict if s.key == "marc_bucket" ] if configuration_settings: diff --git a/core/model/collection.py b/core/model/collection.py index 21c1223309..6a367720be 100644 --- a/core/model/collection.py +++ b/core/model/collection.py @@ -264,7 +264,7 @@ def by_datasource(cls, _db, data_source): cls.integration_configuration_id == IntegrationConfiguration.id, ) .filter( - IntegrationConfiguration.settings[ + IntegrationConfiguration.settings_dict[ Collection.DATA_SOURCE_NAME_SETTING ].astext == data_source @@ -297,17 +297,17 @@ def protocol(self, new_protocol): @hybrid_property def primary_identifier_source(self): """Identify if should try to use another identifier than """ - return self.integration_configuration.settings.get( + return self.integration_configuration.settings_dict.get( ExternalIntegration.PRIMARY_IDENTIFIER_SOURCE ) @primary_identifier_source.setter def primary_identifier_source(self, new_primary_identifier_source): """Modify the primary identifier source in use by this Collection.""" - self.integration_configuration.settings = ( - self.integration_configuration.settings.copy() + self.integration_configuration.settings_dict = ( + self.integration_configuration.settings_dict.copy() ) - self.integration_configuration.settings[ + self.integration_configuration.settings_dict[ ExternalIntegration.PRIMARY_IDENTIFIER_SOURCE ] = new_primary_identifier_source @@ -354,15 +354,15 @@ def default_loan_period_setting( config = self.integration_configuration if config: - return config.settings.get(key) + return config.settings_dict.get(key) DEFAULT_RESERVATION_PERIOD_KEY = "default_reservation_period" STANDARD_DEFAULT_RESERVATION_PERIOD = 3 def _set_settings(self, **kwargs): - settings = self.integration_configuration.settings.copy() - settings.update(kwargs) - self.integration_configuration.settings = settings + settings_dict = self.integration_configuration.settings_dict.copy() + settings_dict.update(kwargs) + self.integration_configuration.settings_dict = settings_dict @hybrid_property def default_reservation_period(self): @@ -371,7 +371,7 @@ def default_reservation_period(self): check it out before it goes to the next person in line. """ return ( - self.integration_configuration.settings.get( + self.integration_configuration.settings_dict.get( self.DEFAULT_RESERVATION_PERIOD_KEY ) or self.STANDARD_DEFAULT_RESERVATION_PERIOD @@ -395,7 +395,8 @@ def default_audience(self) -> str: :return: Default audience """ return ( - self.integration_configuration.settings.get(self.DEFAULT_AUDIENCE_KEY) or "" + self.integration_configuration.settings_dict.get(self.DEFAULT_AUDIENCE_KEY) + or "" ) @default_audience.setter @@ -515,7 +516,7 @@ def data_source(self): data_source = None name = ExternalIntegration.DATA_SOURCE_FOR_LICENSE_PROTOCOL.get(self.protocol) if not name: - name = self.integration_configuration.settings.get( + name = self.integration_configuration.settings_dict.get( Collection.DATA_SOURCE_NAME_SETTING ) _db = Session.object_session(self) @@ -696,8 +697,8 @@ def explain(self, include_secrets=False): lines.append('Used by library: "%s"' % library.short_name) if self.external_account_id: lines.append('External account ID: "%s"' % self.external_account_id) - for name in sorted(integration.settings): - value = integration.settings[name] + for name in sorted(integration.settings_dict): + value = integration.settings_dict[name] if ( include_secrets or not ConfigurationSetting._is_secret(name) ) and value is not None: diff --git a/core/model/integration.py b/core/model/integration.py index e7e7c02cbb..baae134f48 100644 --- a/core/model/integration.py +++ b/core/model/integration.py @@ -45,7 +45,9 @@ class IntegrationConfiguration(Base): name = Column(Unicode, nullable=False, unique=True) # The configuration settings for this integration. Stored as json. - settings: Mapped[Dict[str, Any]] = Column(JSONB, nullable=False, default=dict) + settings_dict: Mapped[Dict[str, Any]] = Column( + "settings", JSONB, nullable=False, default=dict + ) # Self test results, stored as json. self_test_results = Column(JSONB, nullable=False, default=dict) @@ -134,7 +136,9 @@ class IntegrationLibraryConfiguration(Base): library: Mapped[Library] = relationship("Library") # The configuration settings for this integration. Stored as json. - settings: Mapped[Dict[str, Any]] = Column(JSONB, nullable=False, default=dict) + settings_dict: Mapped[Dict[str, Any]] = Column( + "settings", JSONB, nullable=False, default=dict + ) def __repr__(self) -> str: return ( diff --git a/core/opds2_import.py b/core/opds2_import.py index a9cf937d9c..30882cab3a 100644 --- a/core/opds2_import.py +++ b/core/opds2_import.py @@ -953,9 +953,9 @@ def _parse_feed_links(self, links: list[core_ast.Link]) -> None: if first_or_default(link.rels) == Hyperlink.TOKEN_AUTH: # Save the collection-wide token authentication endpoint config = self.integration_configuration() - settings = config.settings.copy() + settings = config.settings_dict.copy() settings[ExternalIntegration.TOKEN_AUTH] = link.href - config.settings = settings + config.settings_dict = settings def extract_feed_data( self, feed: str | opds2_ast.OPDS2Feed, feed_url: str | None = None diff --git a/core/opds_import.py b/core/opds_import.py index d041e407a5..97e968b5c3 100644 --- a/core/opds_import.py +++ b/core/opds_import.py @@ -121,7 +121,7 @@ def from_protocol(cls, _db, protocol, goal=Goals.LICENSE_GOAL, library=None): config = config.for_library(library.id) if config is None: return None - return cls(config.settings["url"]) + return cls(config.settings_dict["url"]) def __init__(self, base_url): if not base_url.endswith("/"): diff --git a/core/overdrive.py b/core/overdrive.py index 1e5dea666e..fd8c8cd318 100644 --- a/core/overdrive.py +++ b/core/overdrive.py @@ -298,7 +298,7 @@ def __init__(self, _db: Session, collection: Collection): # from the parent (the main Overdrive account), except for the # library ID, which we already set. parent_integration = collection.parent.integration_configuration - parent_config = self.settings_class()(**parent_integration.settings) + parent_config = self.settings_class()(**parent_integration.settings_dict) for key in OverdriveConstants.OVERDRIVE_CONFIGURATION_KEYS: parent_value = getattr(parent_config, key, None) setattr(self._configuration, key, parent_value) @@ -306,7 +306,7 @@ def __init__(self, _db: Session, collection: Collection): self.parent_library_id = None # Self settings should override parent settings where available - settings = collection.integration_configuration.settings + settings = collection.integration_configuration.settings_dict for name, schema in self.settings_class().schema()["properties"].items(): if name in settings or not hasattr(self._configuration, name): setattr( @@ -401,7 +401,7 @@ def ils_name(self, library): config = self.integration_configuration().for_library(library.id) if not config: return self.ILS_NAME_DEFAULT - return config.settings.get(self.ILS_NAME_KEY, self.ILS_NAME_DEFAULT) + return config.settings_dict.get(self.ILS_NAME_KEY, self.ILS_NAME_DEFAULT) @property def advantage_library_id(self): diff --git a/core/scripts.py b/core/scripts.py index 3ba35c5a0f..eaacbb0987 100644 --- a/core/scripts.py +++ b/core/scripts.py @@ -1259,7 +1259,7 @@ def do_run(self, _db=None, cmd_args=None, output=sys.stdout): % name ) config = collection.integration_configuration - settings = config.settings.copy() + settings = config.settings_dict.copy() integration = collection.external_integration if protocol: config.protocol = protocol @@ -1278,7 +1278,7 @@ def do_run(self, _db=None, cmd_args=None, output=sys.stdout): for setting in args.setting: key, value = ConfigurationSettingScript._parse_setting(setting) settings[key] = value - config.settings = settings + config.settings_dict = settings if hasattr(args, "library"): for name in args.library: diff --git a/pyproject.toml b/pyproject.toml index 6e4c991d25..3eb04a3958 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -72,6 +72,7 @@ module = [ "core.settings.*", "core.util.authentication_for_opds", "core.util.cache", + "tests.fixtures.authenticator", "tests.migration.*", ] no_implicit_reexport = true diff --git a/scripts.py b/scripts.py index c623564a69..6d98987a93 100644 --- a/scripts.py +++ b/scripts.py @@ -1378,7 +1378,7 @@ def load_collection(self, collection_name, collection_type, data_source_name): data_source = DataSource.lookup( self._db, data_source_name, autocreate=True, offers_licenses=True ) - settings = collection.integration_configuration.settings.copy() + settings = collection.integration_configuration.settings_dict.copy() settings[Collection.DATA_SOURCE_NAME_SETTING] = data_source.name return collection, mirrors diff --git a/tests/api/admin/controller/test_collections.py b/tests/api/admin/controller/test_collections.py index 2392135f5f..1208d2895b 100644 --- a/tests/api/admin/controller/test_collections.py +++ b/tests/api/admin/controller/test_collections.py @@ -274,7 +274,7 @@ def test_collections_get_collections_with_multiple_collections( assert c2.external_account_id == settings2.get("external_account_id") assert c3.external_account_id == settings3.get("external_account_id") - assert c2.integration_configuration.settings[ + assert c2.integration_configuration.settings_dict[ "overdrive_client_secret" ] == settings2.get("overdrive_client_secret") @@ -547,11 +547,15 @@ def test_collections_post_create( assert "acctid" == collection.external_account_id assert ( "username" - == collection.integration_configuration.settings["overdrive_client_key"] + == collection.integration_configuration.settings_dict[ + "overdrive_client_key" + ] ) assert ( "password" - == collection.integration_configuration.settings["overdrive_client_secret"] + == collection.integration_configuration.settings_dict[ + "overdrive_client_secret" + ] ) # Two libraries now have access to the collection. @@ -562,19 +566,21 @@ def test_collections_post_create( # Additional settings were set on the collection. assert ( "1234" - == collection.integration_configuration.settings["overdrive_website_id"] + == collection.integration_configuration.settings_dict[ + "overdrive_website_id" + ] ) assert isinstance(l1.id, int) assert ( "l1_ils" - == collection.integration_configuration.for_library(l1.id).settings[ + == collection.integration_configuration.for_library(l1.id).settings_dict[ "ils_name" ] ) assert isinstance(l2.id, int) assert ( "l2_ils" - == collection.integration_configuration.for_library(l2.id).settings[ + == collection.integration_configuration.for_library(l2.id).settings_dict[ "ils_name" ] ) @@ -608,16 +614,18 @@ def test_collections_post_create( assert "child-acctid" == child.external_account_id # The settings that are inherited from the parent weren't set. - assert "username" not in child.integration_configuration.settings - assert "password" not in child.integration_configuration.settings - assert "website_id" not in child.integration_configuration.settings + assert "username" not in child.integration_configuration.settings_dict + assert "password" not in child.integration_configuration.settings_dict + assert "website_id" not in child.integration_configuration.settings_dict # One library has access to the collection. assert [child] == l3.collections assert isinstance(l3.id, int) assert ( "l3_ils" - == child.integration_configuration.for_library(l3.id).settings["ils_name"] + == child.integration_configuration.for_library(l3.id).settings_dict[ + "ils_name" + ] ) def test_collections_post_edit( @@ -657,7 +665,7 @@ def test_collections_post_edit( assert collection.id == int(response.response[0]) # The collection has been changed. - assert "user2" == collection.integration_configuration.settings.get( + assert "user2" == collection.integration_configuration.settings_dict.get( "overdrive_client_key" ) @@ -665,13 +673,13 @@ def test_collections_post_edit( assert [collection] == l1.collections # Additional settings were set on the collection. - assert "1234" == collection.integration_configuration.settings.get( + assert "1234" == collection.integration_configuration.settings_dict.get( "overdrive_website_id" ) assert isinstance(l1.id, int) assert "the_ils" == collection.integration_configuration.for_library( l1.id - ).settings.get("ils_name") + ).settings_dict.get("ils_name") with settings_ctrl_fixture.request_context_with_admin("/", method="POST"): flask.request.form = ImmutableMultiDict( @@ -694,7 +702,7 @@ def test_collections_post_edit( assert collection.id == int(response.response[0]) # The collection is the same. - assert "user2" == collection.integration_configuration.settings.get( + assert "user2" == collection.integration_configuration.settings_dict.get( "overdrive_client_key" ) assert ExternalIntegration.OVERDRIVE == collection.protocol @@ -876,7 +884,7 @@ def test_collections_post_edit_library_specific_configuration( assert isinstance(l1.id, int) assert "14" == collection.integration_configuration.for_library( l1.id - ).settings.get("ebook_loan_duration") + ).settings_dict.get("ebook_loan_duration") # Remove the connection between collection and library. with settings_ctrl_fixture.request_context_with_admin("/", method="POST"): diff --git a/tests/api/admin/controller/test_patron_auth.py b/tests/api/admin/controller/test_patron_auth.py index 4e63078668..c7c2844e5f 100644 --- a/tests/api/admin/controller/test_patron_auth.py +++ b/tests/api/admin/controller/test_patron_auth.py @@ -35,16 +35,21 @@ from api.sip import SIP2AuthenticationProvider from core.integration.goals import Goals from core.model import AdminRole, Library, get_one -from core.model.integration import ( - IntegrationConfiguration, - IntegrationLibraryConfiguration, -) +from core.model.integration import IntegrationConfiguration from core.util.problem_detail import ProblemDetail if TYPE_CHECKING: from tests.fixtures.api_admin import SettingsControllerFixture - from tests.fixtures.authenticator import AuthProviderFixture - from tests.fixtures.database import DatabaseTransactionFixture + from tests.fixtures.authenticator import ( + MilleniumAuthIntegrationFixture, + SamlAuthIntegrationFixture, + SimpleAuthIntegrationFixture, + Sip2AuthIntegrationFixture, + ) + from tests.fixtures.database import ( + DatabaseTransactionFixture, + IntegrationLibraryConfigurationFixture, + ) @pytest.fixture @@ -116,10 +121,8 @@ def test_patron_auth_services_get_with_simple_auth_service( self, settings_ctrl_fixture: SettingsControllerFixture, db: DatabaseTransactionFixture, - create_simple_auth_integration: Callable[..., AuthProviderFixture], - create_integration_library_configuration: Callable[ - ..., IntegrationLibraryConfiguration - ], + create_simple_auth_integration: SimpleAuthIntegrationFixture, + create_integration_library_configuration: IntegrationLibraryConfigurationFixture, get_response: Callable[[], dict[str, Any] | ProblemDetail], ): auth_service, _ = create_simple_auth_integration( @@ -163,7 +166,7 @@ def test_patron_auth_services_get_with_millenium_auth_service( self, settings_ctrl_fixture: SettingsControllerFixture, db: DatabaseTransactionFixture, - create_millenium_auth_integration: Callable[..., AuthProviderFixture], + create_millenium_auth_integration: MilleniumAuthIntegrationFixture, get_response: Callable[[], dict[str, Any] | ProblemDetail], ): auth_service, _ = create_millenium_auth_integration( @@ -194,7 +197,7 @@ def test_patron_auth_services_get_with_sip2_auth_service( self, settings_ctrl_fixture: SettingsControllerFixture, db: DatabaseTransactionFixture, - create_sip2_auth_integration: Callable[..., AuthProviderFixture], + create_sip2_auth_integration: Sip2AuthIntegrationFixture, get_response: Callable[[], dict[str, Any] | ProblemDetail], ): auth_service, _ = create_sip2_auth_integration( @@ -229,7 +232,7 @@ def test_patron_auth_services_get_with_saml_auth_service( self, settings_ctrl_fixture: SettingsControllerFixture, db: DatabaseTransactionFixture, - create_saml_auth_integration: Callable[..., AuthProviderFixture], + create_saml_auth_integration: SamlAuthIntegrationFixture, get_response: Callable[[], dict[str, Any] | ProblemDetail], ): auth_service, _ = create_saml_auth_integration( @@ -284,7 +287,7 @@ def test_patron_auth_services_post_missing_service( def test_patron_auth_services_post_cannot_change_protocol( self, post_response: Callable[..., Response | ProblemDetail], - create_simple_auth_integration: Callable[..., AuthProviderFixture], + create_simple_auth_integration: SimpleAuthIntegrationFixture, ): auth_service, _ = create_simple_auth_integration() form = ImmutableMultiDict( @@ -299,7 +302,7 @@ def test_patron_auth_services_post_cannot_change_protocol( def test_patron_auth_services_post_name_in_use( self, post_response: Callable[..., Response | ProblemDetail], - create_simple_auth_integration: Callable[..., AuthProviderFixture], + create_simple_auth_integration: SimpleAuthIntegrationFixture, ): auth_service, _ = create_simple_auth_integration() form = ImmutableMultiDict( @@ -314,7 +317,7 @@ def test_patron_auth_services_post_name_in_use( def test_patron_auth_services_post_invalid_configuration( self, post_response: Callable[..., Response | ProblemDetail], - create_millenium_auth_integration: Callable[..., AuthProviderFixture], + create_millenium_auth_integration: MilleniumAuthIntegrationFixture, common_args: list[tuple[str, str]], ): auth_service, _ = create_millenium_auth_integration() @@ -336,7 +339,7 @@ def test_patron_auth_services_post_invalid_configuration( def test_patron_auth_services_post_incomplete_configuration( self, post_response: Callable[..., Response | ProblemDetail], - create_simple_auth_integration: Callable[..., AuthProviderFixture], + create_simple_auth_integration: SimpleAuthIntegrationFixture, common_args: list[tuple[str, str]], ): auth_service, _ = create_simple_auth_integration() @@ -385,7 +388,7 @@ def test_patron_auth_services_post_missing_patron_auth_no_such_library( def test_patron_auth_services_post_missing_patron_auth_multiple_basic( self, post_response: Callable[..., Response | ProblemDetail], - create_simple_auth_integration: Callable[..., AuthProviderFixture], + create_simple_auth_integration: SimpleAuthIntegrationFixture, default_library: Library, common_args: list[tuple[str, str]], ): @@ -497,14 +500,14 @@ def test_patron_auth_services_post_create( assert auth_service.id == int(response.response[0]) # type: ignore[index] assert SimpleAuthenticationProvider.__module__ == auth_service.protocol settings = SimpleAuthenticationProvider.settings_class()( - **auth_service.settings + **auth_service.settings_dict ) assert settings.test_identifier == "user" assert settings.test_password == "pass" [library_config] = auth_service.library_configurations assert library_config.library == default_library assert ( - library_config.settings["library_identifier_restriction_criteria"] + library_config.settings_dict["library_identifier_restriction_criteria"] == "^1234" ) @@ -530,7 +533,7 @@ def test_patron_auth_services_post_create( assert auth_service2 is not None assert auth_service2 != auth_service assert auth_service2.id == int(response.response[0]) # type: ignore[index] - settings2 = MilleniumPatronAPI.settings_class()(**auth_service2.settings) + settings2 = MilleniumPatronAPI.settings_class()(**auth_service2.settings_dict) assert "https://url.com" == settings2.url assert "user" == settings2.test_identifier assert "pass" == settings2.test_password @@ -544,7 +547,7 @@ def test_patron_auth_services_post_edit( post_response: Callable[..., Response | ProblemDetail], common_args: List[Tuple[str, str]], settings_ctrl_fixture: SettingsControllerFixture, - create_simple_auth_integration: Callable[..., AuthProviderFixture], + create_simple_auth_integration: SimpleAuthIntegrationFixture, db: DatabaseTransactionFixture, monkeypatch: MonkeyPatch, ): @@ -587,17 +590,17 @@ def test_patron_auth_services_post_edit( assert auth_service.id == int(response.response[0]) # type: ignore[index] assert SimpleAuthenticationProvider.__module__ == auth_service.protocol - assert isinstance(auth_service.settings, dict) + assert isinstance(auth_service.settings_dict, dict) settings = SimpleAuthenticationProvider.settings_class()( - **auth_service.settings + **auth_service.settings_dict ) assert settings.test_identifier == "user" assert settings.test_password == "pass" [library_config] = auth_service.library_configurations assert l2 == library_config.library - assert isinstance(library_config.settings, dict) + assert isinstance(library_config.settings_dict, dict) library_settings = SimpleAuthenticationProvider.library_settings_class()( - **library_config.settings + **library_config.settings_dict ) assert ( library_settings.library_identifier_restriction_type @@ -610,7 +613,7 @@ def test_patron_auth_service_delete( self, common_args: List[Tuple[str, str]], settings_ctrl_fixture: SettingsControllerFixture, - create_simple_auth_integration: Callable[..., AuthProviderFixture], + create_simple_auth_integration: SimpleAuthIntegrationFixture, ): controller = settings_ctrl_fixture.manager.admin_patron_auth_services_controller db = settings_ctrl_fixture.ctrl.db diff --git a/tests/api/admin/controller/test_patron_auth_self_tests.py b/tests/api/admin/controller/test_patron_auth_self_tests.py index ea2e991f3d..54f615fb31 100644 --- a/tests/api/admin/controller/test_patron_auth_self_tests.py +++ b/tests/api/admin/controller/test_patron_auth_self_tests.py @@ -1,7 +1,7 @@ from __future__ import annotations import json -from typing import TYPE_CHECKING, Callable +from typing import TYPE_CHECKING from unittest.mock import MagicMock import pytest @@ -23,7 +23,7 @@ from _pytest.monkeypatch import MonkeyPatch from flask.ctx import RequestContext - from tests.fixtures.authenticator import AuthProviderFixture + from tests.fixtures.authenticator import SimpleAuthIntegrationFixture from tests.fixtures.database import DatabaseTransactionFixture @@ -56,7 +56,7 @@ def test_patron_auth_self_tests_get_with_no_libraries( self, controller: PatronAuthServiceSelfTestsController, get_request_context: RequestContext, - create_simple_auth_integration: Callable[..., AuthProviderFixture], + create_simple_auth_integration: SimpleAuthIntegrationFixture, ): auth_service, _ = create_simple_auth_integration() response_obj = controller.process_patron_auth_service_self_tests( @@ -75,7 +75,7 @@ def test_patron_auth_self_tests_test_get_no_results( self, controller: PatronAuthServiceSelfTestsController, get_request_context: RequestContext, - create_simple_auth_integration: Callable[..., AuthProviderFixture], + create_simple_auth_integration: SimpleAuthIntegrationFixture, default_library: Library, ): auth_service, _ = create_simple_auth_integration(library=default_library) @@ -99,7 +99,7 @@ def test_patron_auth_self_tests_test_get( self, controller: PatronAuthServiceSelfTestsController, get_request_context: RequestContext, - create_simple_auth_integration: Callable[..., AuthProviderFixture], + create_simple_auth_integration: SimpleAuthIntegrationFixture, monkeypatch: MonkeyPatch, default_library: Library, ): @@ -136,7 +136,7 @@ def test_patron_auth_self_tests_post_with_no_libraries( self, controller: PatronAuthServiceSelfTestsController, post_request_context: RequestContext, - create_simple_auth_integration: Callable[..., AuthProviderFixture], + create_simple_auth_integration: SimpleAuthIntegrationFixture, ): auth_service, _ = create_simple_auth_integration() response = controller.process_patron_auth_service_self_tests(auth_service.id) @@ -149,7 +149,7 @@ def test_patron_auth_self_tests_test_post( self, controller: PatronAuthServiceSelfTestsController, post_request_context: RequestContext, - create_simple_auth_integration: Callable[..., AuthProviderFixture], + create_simple_auth_integration: SimpleAuthIntegrationFixture, monkeypatch: MonkeyPatch, db: DatabaseTransactionFixture, ): diff --git a/tests/api/admin/controller/test_settings.py b/tests/api/admin/controller/test_settings.py index 91ff2e48e2..4082c9ce49 100644 --- a/tests/api/admin/controller/test_settings.py +++ b/tests/api/admin/controller/test_settings.py @@ -393,4 +393,4 @@ def settings_class(cls): config, dict(short_name="short-name", key="key", value="value"), Protocol1 ) assert config.library == library - assert config.settings == dict(key="key", value="value") + assert config.settings_dict == dict(key="key", value="value") diff --git a/tests/api/lcp/test_server.py b/tests/api/lcp/test_server.py index 06bed63e20..ddbe544ef7 100644 --- a/tests/api/lcp/test_server.py +++ b/tests/api/lcp/test_server.py @@ -39,7 +39,7 @@ def __init__(self, db: DatabaseTransactionFixture): self.hasher_factory = HasherFactory() self.credential_factory = LCPCredentialFactory() self.lcp_server = LCPServer( - lambda: LCPServerSettings(**self.configuration.settings), + lambda: LCPServerSettings(**self.configuration.settings_dict), self.hasher_factory, self.credential_factory, ) @@ -65,7 +65,7 @@ def test_add_content( ): # Arrange lcp_server = LCPServer( - lambda: LCPServerSettings(**lcp_server_fixture.configuration.settings), + lambda: LCPServerSettings(**lcp_server_fixture.configuration.settings_dict), lcp_server_fixture.hasher_factory, lcp_server_fixture.credential_factory, ) diff --git a/tests/api/mockapi/axis.py b/tests/api/mockapi/axis.py index ca1bbd58e2..0b8456fe53 100644 --- a/tests/api/mockapi/axis.py +++ b/tests/api/mockapi/axis.py @@ -27,7 +27,7 @@ def mock_collection( config = collection.create_integration_configuration( ExternalIntegration.AXIS_360 ) - config.settings = { + config.settings_dict = { "username": "a", "password": "b", "url": "http://axis.test/", diff --git a/tests/api/mockapi/bibliotheca.py b/tests/api/mockapi/bibliotheca.py index 10f6d42d34..36486512da 100644 --- a/tests/api/mockapi/bibliotheca.py +++ b/tests/api/mockapi/bibliotheca.py @@ -30,7 +30,7 @@ def mock_collection( config = collection.create_integration_configuration( ExternalIntegration.BIBLIOTHECA ) - config.settings = { + config.settings_dict = { "username": "a", "password": "b", } diff --git a/tests/api/mockapi/odilo.py b/tests/api/mockapi/odilo.py index 6ecd3df7fc..b8c23c773c 100644 --- a/tests/api/mockapi/odilo.py +++ b/tests/api/mockapi/odilo.py @@ -37,7 +37,7 @@ def mock_collection(cls, _db: Session, library: Library) -> Collection: protocol=ExternalIntegration.ODILO ) config = collection.create_integration_configuration(ExternalIntegration.ODILO) - config.settings = { + config.settings_dict = { "username": "a", "password": "b", OdiloAPI.LIBRARY_API_BASE_URL: "http://library_api_base_url.test/api/v2", diff --git a/tests/api/mockapi/opds_for_distributors.py b/tests/api/mockapi/opds_for_distributors.py index 327f8fe02f..8560573387 100644 --- a/tests/api/mockapi/opds_for_distributors.py +++ b/tests/api/mockapi/opds_for_distributors.py @@ -34,7 +34,9 @@ def mock_collection( config = collection.create_integration_configuration( OPDSForDistributorsAPI.NAME ) - config.settings = dict(username="a", password="b", data_source="data_source") + config.settings_dict = dict( + username="a", password="b", data_source="data_source" + ) config.for_library(library.id, create=True) library.collections.append(collection) return collection diff --git a/tests/api/mockapi/overdrive.py b/tests/api/mockapi/overdrive.py index 9601a9de40..ee4c33c3c7 100644 --- a/tests/api/mockapi/overdrive.py +++ b/tests/api/mockapi/overdrive.py @@ -38,7 +38,7 @@ def mock_collection( config = collection.create_integration_configuration( ExternalIntegration.OVERDRIVE ) - config.settings = { + config.settings_dict = { OverdriveConstants.OVERDRIVE_CLIENT_KEY: client_key, OverdriveConstants.OVERDRIVE_CLIENT_SECRET: client_secret, OverdriveConstants.OVERDRIVE_WEBSITE_ID: website_id, diff --git a/tests/api/test_authenticator.py b/tests/api/test_authenticator.py index b2352e0899..5dcce67a1a 100644 --- a/tests/api/test_authenticator.py +++ b/tests/api/test_authenticator.py @@ -67,7 +67,10 @@ if TYPE_CHECKING: from ..fixtures.api_controller import ControllerFixture - from ..fixtures.authenticator import AuthProviderFixture + from ..fixtures.authenticator import ( + CreateAuthIntegrationFixture, + MilleniumAuthIntegrationFixture, + ) from ..fixtures.database import DatabaseTransactionFixture from ..fixtures.vendor_id import VendorIDFixture @@ -471,7 +474,7 @@ class TestAuthenticator: def test_init( self, controller_fixture: ControllerFixture, - create_millenium_auth_integration: Callable[..., AuthProviderFixture], + create_millenium_auth_integration: MilleniumAuthIntegrationFixture, ): db = controller_fixture.db @@ -601,7 +604,7 @@ class TestLibraryAuthenticator: def test_from_config_basic_auth_only( self, db: DatabaseTransactionFixture, - create_millenium_auth_integration: Callable[..., AuthProviderFixture], + create_millenium_auth_integration: MilleniumAuthIntegrationFixture, ): # Only a basic auth provider. create_millenium_auth_integration(db.default_library()) @@ -657,8 +660,8 @@ def test_config_succeeds_when_no_providers_configured( def test_configuration_exception_during_from_config_stored( self, db: DatabaseTransactionFixture, - create_millenium_auth_integration: Callable[..., AuthProviderFixture], - create_auth_integration_configuration: Callable[..., AuthProviderFixture], + create_millenium_auth_integration: MilleniumAuthIntegrationFixture, + create_auth_integration_configuration: CreateAuthIntegrationFixture, ): # If the initialization of an AuthenticationProvider from config # raises CannotLoadConfiguration or ImportError, the exception @@ -737,9 +740,9 @@ def __init__(self, *args, **kwargs): type(integration.parent).goal = PropertyMock( return_value=Goals.PATRON_AUTH_GOAL ) - type(integration.parent).settings = PropertyMock(return_value={}) + type(integration.parent).settings_dict = PropertyMock(return_value={}) type(integration).library_id = PropertyMock(return_value=library.id) - type(integration).settings = PropertyMock(return_value={}) + type(integration).settings_dict = PropertyMock(return_value={}) auth = LibraryAuthenticator( _db=db.session, library=library, integration_registry=registry ) @@ -753,15 +756,15 @@ def __init__(self, *args, **kwargs): def test_register_provider_basic_auth( self, db: DatabaseTransactionFixture, - create_auth_integration_configuration: Callable[..., AuthProviderFixture], + create_auth_integration_configuration: CreateAuthIntegrationFixture, patron_auth_registry: PatronAuthRegistry, ): library = db.default_library() - protocol = patron_auth_registry.get_protocol(SIP2AuthenticationProvider) + protocol = patron_auth_registry.get_protocol(SIP2AuthenticationProvider, "") _, integration = create_auth_integration_configuration( protocol, library, - settings={ + settings_dict={ "url": "http://url/", "password": "secret", }, diff --git a/tests/api/test_axis.py b/tests/api/test_axis.py index ab1a5717d4..1ae132359c 100644 --- a/tests/api/test_axis.py +++ b/tests/api/test_axis.py @@ -6,7 +6,7 @@ import ssl import urllib from functools import partial -from typing import TYPE_CHECKING, Callable +from typing import TYPE_CHECKING from unittest.mock import MagicMock, Mock, PropertyMock import pytest @@ -68,7 +68,7 @@ if TYPE_CHECKING: from ..fixtures.api_axis_files import AxisFilesFixture - from ..fixtures.authenticator import AuthProviderFixture + from ..fixtures.authenticator import SimpleAuthIntegrationFixture from ..fixtures.database import DatabaseTransactionFixture @@ -140,7 +140,7 @@ def test_external_integration(self, axis360: Axis360Fixture): def test__run_self_tests( self, axis360: Axis360Fixture, - create_simple_auth_integration: Callable[..., AuthProviderFixture], + create_simple_auth_integration: SimpleAuthIntegrationFixture, ): # Verify that Axis360API._run_self_tests() calls the right # methods. @@ -755,10 +755,10 @@ def test_integration_settings( axis360: Axis360Fixture, ): config = axis360.collection.integration_configuration - settings = config.settings.copy() + settings = config.settings_dict.copy() if setting_value is not None: settings[setting] = setting_value - config.settings = settings + config.settings_dict = settings api = MockAxis360API(axis360.db.session, axis360.collection) assert getattr(api, attribute) == attribute_value @@ -780,9 +780,9 @@ def test_integration_settings_url( self, setting, setting_value, is_valid, expected, axis360: Axis360Fixture ): config = axis360.collection.integration_configuration - settings = config.settings.copy() + settings = config.settings_dict.copy() settings[setting] = setting_value - config.settings = settings + config.settings_dict = settings if is_valid: api = MockAxis360API(axis360.db.session, axis360.collection) diff --git a/tests/api/test_bibliotheca.py b/tests/api/test_bibliotheca.py index dca27836be..62231870dd 100644 --- a/tests/api/test_bibliotheca.py +++ b/tests/api/test_bibliotheca.py @@ -4,15 +4,7 @@ import random from datetime import datetime, timedelta from io import BytesIO, StringIO -from typing import ( - TYPE_CHECKING, - Any, - Callable, - ClassVar, - Optional, - Protocol, - runtime_checkable, -) +from typing import TYPE_CHECKING, Any, ClassVar, Optional, Protocol, runtime_checkable from unittest import mock from unittest.mock import MagicMock @@ -77,7 +69,7 @@ if TYPE_CHECKING: from tests.fixtures.api_bibliotheca_files import BibliothecaFilesFixture - from tests.fixtures.authenticator import AuthProviderFixture + from tests.fixtures.authenticator import SimpleAuthIntegrationFixture from tests.fixtures.database import DatabaseTransactionFixture from tests.fixtures.time import Time @@ -110,7 +102,7 @@ def test_external_integration(self, bibliotheca_fixture: BibliothecaAPITestFixtu def test__run_self_tests( self, bibliotheca_fixture: BibliothecaAPITestFixture, - create_simple_auth_integration: Callable[..., AuthProviderFixture], + create_simple_auth_integration: SimpleAuthIntegrationFixture, ): db = bibliotheca_fixture.db # Verify that BibliothecaAPI._run_self_tests() calls the right diff --git a/tests/api/test_controller_odl_notify.py b/tests/api/test_controller_odl_notify.py index 3c8520529c..eb6f1f8cbe 100644 --- a/tests/api/test_controller_odl_notify.py +++ b/tests/api/test_controller_odl_notify.py @@ -32,7 +32,7 @@ def __init__(self, db: DatabaseTransactionFixture): config = self.collection.create_integration_configuration( self.integration_protocol() ) - config.settings = { + config.settings_dict = { "username": "a", "password": "b", "url": "http://metadata", diff --git a/tests/api/test_controller_shared_collect.py b/tests/api/test_controller_shared_collect.py index 91ba1163bc..9a5f00fbbe 100644 --- a/tests/api/test_controller_shared_collect.py +++ b/tests/api/test_controller_shared_collect.py @@ -67,7 +67,7 @@ def __init__(self, db: DatabaseTransactionFixture): from api.odl import ODLAPI self.collection = db.collection(protocol=ODLAPI.NAME) - self.collection.integration_configuration.settings = dict( + self.collection.integration_configuration.settings_dict = dict( username="username", password="password", data_source="data_source", diff --git a/tests/api/test_enki.py b/tests/api/test_enki.py index 9c54da938c..390faa1cca 100644 --- a/tests/api/test_enki.py +++ b/tests/api/test_enki.py @@ -2,7 +2,7 @@ import datetime import json -from typing import TYPE_CHECKING, Callable +from typing import TYPE_CHECKING import pytest @@ -31,7 +31,7 @@ if TYPE_CHECKING: from tests.fixtures.api_enki_files import EnkiFilesFixture - from tests.fixtures.authenticator import AuthProviderFixture + from tests.fixtures.authenticator import SimpleAuthIntegrationFixture class EnkiTestFixure: @@ -96,7 +96,7 @@ def test_collection(self, enki_test_fixture: EnkiTestFixure): def test__run_self_tests( self, enki_test_fixture: EnkiTestFixure, - create_simple_auth_integration: Callable[..., AuthProviderFixture], + create_simple_auth_integration: SimpleAuthIntegrationFixture, ): db = enki_test_fixture.db diff --git a/tests/api/test_odilo.py b/tests/api/test_odilo.py index 65451566b9..f3f0c1f791 100644 --- a/tests/api/test_odilo.py +++ b/tests/api/test_odilo.py @@ -1,7 +1,7 @@ from __future__ import annotations import json -from typing import TYPE_CHECKING, Callable +from typing import TYPE_CHECKING import pytest @@ -32,7 +32,7 @@ if TYPE_CHECKING: from ..fixtures.api_odilo_files import OdiloFilesFixture - from ..fixtures.authenticator import AuthProviderFixture + from ..fixtures.authenticator import SimpleAuthIntegrationFixture from ..fixtures.database import DatabaseTransactionFixture @@ -226,7 +226,7 @@ def test_external_integration(self, odilo: OdiloFixture): def test__run_self_tests( self, odilo: OdiloFixture, - create_simple_auth_integration: Callable[..., AuthProviderFixture], + create_simple_auth_integration: SimpleAuthIntegrationFixture, ): """Verify that OdiloAPI._run_self_tests() calls the right methods. diff --git a/tests/api/test_overdrive.py b/tests/api/test_overdrive.py index 21c0cf210b..f778d19c1b 100644 --- a/tests/api/test_overdrive.py +++ b/tests/api/test_overdrive.py @@ -5,7 +5,7 @@ import os import random from datetime import timedelta -from typing import TYPE_CHECKING, Any, Callable, Dict +from typing import TYPE_CHECKING, Any, Dict from unittest.mock import MagicMock, create_autospec import pytest @@ -47,7 +47,7 @@ if TYPE_CHECKING: from ..fixtures.api_overdrive_files import OverdriveAPIFilesFixture - from ..fixtures.authenticator import AuthProviderFixture + from ..fixtures.authenticator import SimpleAuthIntegrationFixture from ..fixtures.time import Time @@ -122,7 +122,7 @@ def test_lock_in_format(self, overdrive_api_fixture: OverdriveAPIFixture): def test__run_self_tests( self, overdrive_api_fixture: OverdriveAPIFixture, - create_simple_auth_integration: Callable[..., AuthProviderFixture], + create_simple_auth_integration: SimpleAuthIntegrationFixture, ): # Verify that OverdriveAPI._run_self_tests() calls the right # methods. diff --git a/tests/api/test_scripts.py b/tests/api/test_scripts.py index 7b78f973df..9d22f285ea 100644 --- a/tests/api/test_scripts.py +++ b/tests/api/test_scripts.py @@ -4,7 +4,7 @@ import datetime from io import StringIO from pathlib import Path -from typing import TYPE_CHECKING, Callable +from typing import TYPE_CHECKING from unittest.mock import MagicMock, patch import pytest @@ -63,7 +63,7 @@ from tests.fixtures.library import LibraryFixture if TYPE_CHECKING: - from tests.fixtures.authenticator import AuthProviderFixture + from tests.fixtures.authenticator import SimpleAuthIntegrationFixture from tests.fixtures.database import DatabaseTransactionFixture from tests.fixtures.sample_covers import SampleCoversFixture from tests.fixtures.search import ExternalSearchFixture @@ -1493,7 +1493,7 @@ def patron(self, authdata, db: DatabaseTransactionFixture): def authentication_provider( self, db: DatabaseTransactionFixture, - create_simple_auth_integration: Callable[..., AuthProviderFixture], + create_simple_auth_integration: SimpleAuthIntegrationFixture, ): barcode = "12345" pin = "abcd" diff --git a/tests/api/test_selftest.py b/tests/api/test_selftest.py index 2dc72298b0..5b3f16cd75 100644 --- a/tests/api/test_selftest.py +++ b/tests/api/test_selftest.py @@ -3,7 +3,7 @@ import datetime from io import StringIO -from typing import TYPE_CHECKING, Callable +from typing import TYPE_CHECKING from unittest import mock import pytest @@ -18,7 +18,7 @@ from core.util.problem_detail import ProblemDetail if TYPE_CHECKING: - from tests.fixtures.authenticator import AuthProviderFixture + from tests.fixtures.authenticator import SimpleAuthIntegrationFixture from tests.fixtures.database import DatabaseTransactionFixture @@ -26,7 +26,7 @@ class TestHasSelfTests: def test__determine_self_test_patron( self, db: DatabaseTransactionFixture, - create_simple_auth_integration: Callable[..., AuthProviderFixture], + create_simple_auth_integration: SimpleAuthIntegrationFixture, ): """Test per-library default patron lookup for self-tests. @@ -95,7 +95,7 @@ def test__determine_self_test_patron( def test_default_patrons( self, db: DatabaseTransactionFixture, - create_simple_auth_integration: Callable[..., AuthProviderFixture], + create_simple_auth_integration: SimpleAuthIntegrationFixture, ): """Some self-tests must run with a patron's credentials. The default_patrons() method finds the default Patron for every diff --git a/tests/api/test_shared_collection.py b/tests/api/test_shared_collection.py index 8942084108..716cfc75b2 100644 --- a/tests/api/test_shared_collection.py +++ b/tests/api/test_shared_collection.py @@ -42,7 +42,7 @@ class SharedCollectionFixture: def __init__(self, db: DatabaseTransactionFixture): self.db = db self.collection = db.collection(protocol="Mock") - self.collection.integration_configuration.settings = dict( + self.collection.integration_configuration.settings_dict = dict( username="username", password="password", data_source="data_source" ) self.shared_collection = SharedCollectionAPI( @@ -97,7 +97,7 @@ def test_api_for_licensepool( db = shared_collection_fixture.db collection = db.collection(protocol=ODLAPI.NAME) - collection.integration_configuration.settings = dict( + collection.integration_configuration.settings_dict = dict( username="username", password="password", data_source="data_source" ) edition, pool = db.edition(with_license_pool=True, collection=collection) @@ -110,7 +110,7 @@ def test_api_for_collection( db = shared_collection_fixture.db collection = db.collection() - collection.integration_configuration.settings = dict( + collection.integration_configuration.settings_dict = dict( username="username", password="password", data_source="data_source" ) shared_collection = SharedCollectionAPI(db.session) diff --git a/tests/core/models/test_collection.py b/tests/core/models/test_collection.py index 503f284076..bfe229e5ef 100644 --- a/tests/core/models/test_collection.py +++ b/tests/core/models/test_collection.py @@ -432,7 +432,7 @@ def test_explain(self, example_collection_fixture: ExampleCollectionFixture): library.collections.append(test_collection) test_collection.external_account_id = "id" - test_collection.integration_configuration.settings = { + test_collection.integration_configuration.settings_dict = { "url": "url", "username": "username", "password": "password", diff --git a/tests/core/models/test_integration_configuration.py b/tests/core/models/test_integration_configuration.py index 2a0ef8e6c2..50c3c5f4ed 100644 --- a/tests/core/models/test_integration_configuration.py +++ b/tests/core/models/test_integration_configuration.py @@ -24,7 +24,7 @@ def test_for_library(seslf, db: DatabaseTransactionFixture): assert libconfig is not None assert libconfig.library == library assert libconfig.parent == config - assert libconfig.settings == {} + assert libconfig.settings_dict == {} # The same config is returned henceforth assert config.for_library(library.id) == libconfig diff --git a/tests/core/test_opds2_import.py b/tests/core/test_opds2_import.py index 0967399438..7f44c38352 100644 --- a/tests/core/test_opds2_import.py +++ b/tests/core/test_opds2_import.py @@ -441,7 +441,7 @@ def test_auth_token_feed( imported_editions, pools, works, failures = data.importer.import_from_feed( content ) - setting = data.importer.integration_configuration().settings.get( + setting = data.importer.integration_configuration().settings_dict.get( ExternalIntegration.TOKEN_AUTH ) diff --git a/tests/core/test_opds_validate.py b/tests/core/test_opds_validate.py index ebc1119664..68510b376b 100644 --- a/tests/core/test_opds_validate.py +++ b/tests/core/test_opds_validate.py @@ -38,7 +38,7 @@ def test_odl2_schema( db: DatabaseTransactionFixture, opds_files_fixture: OPDSFilesFixture, ): - db.default_collection().integration_configuration.settings = { + db.default_collection().integration_configuration.settings_dict = { "username": "username", "password": "password", } diff --git a/tests/core/test_scripts.py b/tests/core/test_scripts.py index a355da79e7..d8378b51bb 100644 --- a/tests/core/test_scripts.py +++ b/tests/core/test_scripts.py @@ -1216,10 +1216,14 @@ def test_success(self, db: DatabaseTransactionFixture): # The collection was created and configured properly. collection = get_one(db.session, Collection) assert "New Collection" == collection.name - assert "url" == collection.integration_configuration.settings["url"] + assert "url" == collection.integration_configuration.settings_dict["url"] assert "acctid" == collection.external_account_id - assert "username" == collection.integration_configuration.settings["username"] - assert "password" == collection.integration_configuration.settings["password"] + assert ( + "username" == collection.integration_configuration.settings_dict["username"] + ) + assert ( + "password" == collection.integration_configuration.settings_dict["password"] + ) # Two libraries now have access to the collection. assert [collection] == l1.collections @@ -1228,7 +1232,7 @@ def test_success(self, db: DatabaseTransactionFixture): # One CollectionSetting was set on the collection, in addition # to url, username, and password. - setting = collection.integration_configuration.settings.get("library_id") + setting = collection.integration_configuration.settings_dict.get("library_id") assert "1234" == setting # The output explains the collection settings. @@ -1258,7 +1262,7 @@ def test_reconfigure_collection(self, db: DatabaseTransactionFixture): # The collection has been changed. db.session.refresh(collection.integration_configuration) - assert "foo" == collection.integration_configuration.settings.get("url") + assert "foo" == collection.integration_configuration.settings_dict.get("url") assert ExternalIntegration.BIBLIOTHECA == collection.protocol expect = ( diff --git a/tests/fixtures/api_controller.py b/tests/fixtures/api_controller.py index 23e1bbbbf4..d08c6456f7 100644 --- a/tests/fixtures/api_controller.py +++ b/tests/fixtures/api_controller.py @@ -213,7 +213,7 @@ def library_setup(self, library): name=self.db.fresh_str(), protocol=protocol, goal=Goals.PATRON_AUTH_GOAL, - settings=settings.dict(), + settings_dict=settings.dict(), ) create( _db, diff --git a/tests/fixtures/authenticator.py b/tests/fixtures/authenticator.py index 77e8db76f8..57323f28a4 100644 --- a/tests/fixtures/authenticator.py +++ b/tests/fixtures/authenticator.py @@ -1,4 +1,4 @@ -from typing import Callable, Optional, Tuple, Type +from typing import Dict, Optional, Tuple, Type import pytest @@ -15,41 +15,56 @@ IntegrationLibraryConfiguration, ) from tests.api.saml.saml_strings import CORRECT_XML_WITH_ONE_SP +from tests.fixtures.database import ( + IntegrationConfigurationFixture, + IntegrationLibraryConfigurationFixture, +) AuthProviderFixture = Tuple[ IntegrationConfiguration, Optional[IntegrationLibraryConfiguration] ] -@pytest.fixture -def create_auth_integration_configuration( - create_integration_configuration, - create_integration_library_configuration: Callable[ - ..., IntegrationLibraryConfiguration - ], -) -> Callable[..., AuthProviderFixture]: - def create_integration( +class CreateAuthIntegrationFixture: + def __init__( + self, + integration_configuration: IntegrationConfigurationFixture, + integration_library_configuration: IntegrationLibraryConfigurationFixture, + ): + self.integration_configuration = integration_configuration + self.integration_library_configuration = integration_library_configuration + + def __call__( + self, protocol: str, library: Optional[Library], - settings: Optional[dict] = None, - library_settings: Optional[dict] = None, + settings_dict: Optional[Dict[str, str]] = None, + library_settings_dict: Optional[Dict[str, str]] = None, ) -> AuthProviderFixture: - settings = settings or {} - library_settings = library_settings or {} - integration = create_integration_configuration( + settings_dict = settings_dict or {} + library_settings_dict = library_settings_dict or {} + integration = self.integration_configuration( protocol, Goals.PATRON_AUTH_GOAL, - settings, + settings_dict, ) if library is not None: - library_integration = create_integration_library_configuration( - library, integration, library_settings + library_integration = self.integration_library_configuration( + library, integration, library_settings_dict ) else: library_integration = None return integration, library_integration - return create_integration + +@pytest.fixture +def create_auth_integration_configuration( + create_integration_configuration: IntegrationConfigurationFixture, + create_integration_library_configuration: IntegrationLibraryConfigurationFixture, +) -> CreateAuthIntegrationFixture: + return CreateAuthIntegrationFixture( + create_integration_configuration, create_integration_library_configuration + ) @pytest.fixture() @@ -57,25 +72,40 @@ def patron_auth_registry() -> PatronAuthRegistry: return PatronAuthRegistry() +class AuthProtocolFixture: + def __init__(self, registry: PatronAuthRegistry): + self.registry = registry + + def __call__(self, protocol: Type[AuthenticationProvider]) -> str: + return self.registry.get_protocol(protocol, "") + + @pytest.fixture def get_auth_protocol( patron_auth_registry: PatronAuthRegistry, -) -> Callable[[Type[AuthenticationProvider]], Optional[str]]: - return lambda x: patron_auth_registry.get_protocol(x) - +) -> AuthProtocolFixture: + return AuthProtocolFixture(patron_auth_registry) + + +class SimpleAuthIntegrationFixture: + def __init__( + self, + create_auth_integration_configuration: CreateAuthIntegrationFixture, + get_auth_protocol: AuthProtocolFixture, + ): + self.create_auth_integration_configuration = ( + create_auth_integration_configuration + ) + self.get_auth_protocol = get_auth_protocol -@pytest.fixture -def create_simple_auth_integration( - create_auth_integration_configuration: Callable[..., AuthProviderFixture], - get_auth_protocol: Callable[[Type[AuthenticationProvider]], Optional[str]], -) -> Callable[..., AuthProviderFixture]: - def create_integration( + def __call__( + self, library: Optional[Library] = None, test_identifier: str = "username1", test_password: str = "password1", ) -> AuthProviderFixture: - return create_auth_integration_configuration( - get_auth_protocol(SimpleAuthenticationProvider), + return self.create_auth_integration_configuration( + self.get_auth_protocol(SimpleAuthenticationProvider), library, dict( test_identifier=test_identifier, @@ -83,67 +113,111 @@ def create_integration( ), ) - return create_integration - @pytest.fixture -def create_millenium_auth_integration( - create_auth_integration_configuration: Callable[..., AuthProviderFixture], - get_auth_protocol: Callable[[Type[AuthenticationProvider]], Optional[str]], -) -> Callable[..., AuthProviderFixture]: - protocol = get_auth_protocol(MilleniumPatronAPI) +def create_simple_auth_integration( + create_auth_integration_configuration: CreateAuthIntegrationFixture, + get_auth_protocol: AuthProtocolFixture, +) -> SimpleAuthIntegrationFixture: + return SimpleAuthIntegrationFixture( + create_auth_integration_configuration, get_auth_protocol + ) + + +class MilleniumAuthIntegrationFixture: + def __init__( + self, + create_auth_integration_configuration: CreateAuthIntegrationFixture, + get_auth_protocol: AuthProtocolFixture, + ): + self.create_auth_integration_configuration = ( + create_auth_integration_configuration + ) + self.get_auth_protocol = get_auth_protocol - def create_integration( - library: Optional[Library] = None, **kwargs + def __call__( + self, library: Optional[Library] = None, **kwargs: str ) -> AuthProviderFixture: if "url" not in kwargs: kwargs["url"] = "http://url.com/" - return create_auth_integration_configuration( - protocol, + return self.create_auth_integration_configuration( + self.get_auth_protocol(MilleniumPatronAPI), library, kwargs, ) - return create_integration - @pytest.fixture -def create_sip2_auth_integration( - create_auth_integration_configuration: Callable[..., AuthProviderFixture], - get_auth_protocol: Callable[[Type[AuthenticationProvider]], Optional[str]], -) -> Callable[..., AuthProviderFixture]: - protocol = get_auth_protocol(SIP2AuthenticationProvider) +def create_millenium_auth_integration( + create_auth_integration_configuration: CreateAuthIntegrationFixture, + get_auth_protocol: AuthProtocolFixture, +) -> MilleniumAuthIntegrationFixture: + return MilleniumAuthIntegrationFixture( + create_auth_integration_configuration, get_auth_protocol + ) + + +class Sip2AuthIntegrationFixture: + def __init__( + self, + create_auth_integration_configuration: CreateAuthIntegrationFixture, + get_auth_protocol: AuthProtocolFixture, + ): + self.create_auth_integration_configuration = ( + create_auth_integration_configuration + ) + self.get_auth_protocol = get_auth_protocol - def create_integration( - library: Optional[Library] = None, **kwargs + def __call__( + self, library: Optional[Library] = None, **kwargs: str ) -> AuthProviderFixture: if "url" not in kwargs: kwargs["url"] = "url.com" - return create_auth_integration_configuration( - protocol, + return self.create_auth_integration_configuration( + self.get_auth_protocol(SIP2AuthenticationProvider), library, kwargs, ) - return create_integration - @pytest.fixture -def create_saml_auth_integration( - create_auth_integration_configuration: Callable[..., AuthProviderFixture], - get_auth_protocol: Callable[[Type[AuthenticationProvider]], Optional[str]], -) -> Callable[..., AuthProviderFixture]: - protocol = get_auth_protocol(SAMLWebSSOAuthenticationProvider) +def create_sip2_auth_integration( + create_auth_integration_configuration: CreateAuthIntegrationFixture, + get_auth_protocol: AuthProtocolFixture, +) -> Sip2AuthIntegrationFixture: + return Sip2AuthIntegrationFixture( + create_auth_integration_configuration, get_auth_protocol + ) + + +class SamlAuthIntegrationFixture: + def __init__( + self, + create_auth_integration_configuration: CreateAuthIntegrationFixture, + get_auth_protocol: AuthProtocolFixture, + ): + self.create_auth_integration_configuration = ( + create_auth_integration_configuration + ) + self.get_auth_protocol = get_auth_protocol - def create_integration( - library: Optional[Library] = None, **kwargs + def __call__( + self, library: Optional[Library] = None, **kwargs: str ) -> AuthProviderFixture: if "service_provider_xml_metadata" not in kwargs: kwargs["service_provider_xml_metadata"] = CORRECT_XML_WITH_ONE_SP - return create_auth_integration_configuration( - protocol, + return self.create_auth_integration_configuration( + self.get_auth_protocol(SAMLWebSSOAuthenticationProvider), library, kwargs, ) - return create_integration + +@pytest.fixture +def create_saml_auth_integration( + create_auth_integration_configuration: CreateAuthIntegrationFixture, + get_auth_protocol: AuthProtocolFixture, +) -> SamlAuthIntegrationFixture: + return SamlAuthIntegrationFixture( + create_auth_integration_configuration, get_auth_protocol + ) diff --git a/tests/fixtures/database.py b/tests/fixtures/database.py index 4b350cc8a8..c63cf6f81a 100644 --- a/tests/fixtures/database.py +++ b/tests/fixtures/database.py @@ -8,7 +8,7 @@ import time import uuid from textwrap import dedent -from typing import Callable, Generator, Iterable, List, Optional, Tuple +from typing import Generator, Iterable, List, Optional, Tuple import pytest import sqlalchemy @@ -317,7 +317,7 @@ def collection( integration.goal = ExternalIntegration.LICENSE_GOAL config = collection.create_integration_configuration(protocol) config.goal = Goals.LICENSE_GOAL - config.settings = { + config.settings_dict = { "url": url, "username": username, "password": password, @@ -788,7 +788,7 @@ def integration_configuration( for library in libraries: integration.for_library(library.id, create=True) - integration.settings = kwargs + integration.settings_dict = kwargs return integration @classmethod @@ -798,7 +798,7 @@ def set_settings( *keyvalues, **kwargs, ): - settings = config.settings.copy() + settings = config.settings_dict.copy() # Alternating key: value in the args for ix, item in enumerate(keyvalues): @@ -808,7 +808,7 @@ def set_settings( settings[key] = item settings.update(kwargs) - config.settings = settings + config.settings_dict = settings def work_coverage_record( self, work, operation=None, status=CoverageRecord.SUCCESS @@ -1025,46 +1025,59 @@ def db( tr.close() -@pytest.fixture -def create_integration_configuration( - db: DatabaseTransactionFixture, -) -> Callable[..., IntegrationConfiguration]: - def create_integration( - protocol: str, goal: Goals, settings: Optional[dict] = None +class IntegrationConfigurationFixture: + def __init__(self, db: DatabaseTransactionFixture): + self.db = db + + def __call__( + self, protocol: Optional[str], goal: Goals, settings_dict: Optional[dict] = None ) -> IntegrationConfiguration: integration, _ = create( - db.session, + self.db.session, IntegrationConfiguration, - name=db.fresh_str(), + name=self.db.fresh_str(), protocol=protocol, goal=goal, - settings=settings or {}, + settings_dict=settings_dict or {}, ) return integration - return create_integration - @pytest.fixture -def create_integration_library_configuration( +def create_integration_configuration( db: DatabaseTransactionFixture, -) -> Callable[..., IntegrationLibraryConfiguration]: - def create_library_integration( +) -> IntegrationConfigurationFixture: + fixture = IntegrationConfigurationFixture(db) + return fixture + + +class IntegrationLibraryConfigurationFixture: + def __init__(self, db: DatabaseTransactionFixture): + self.db = db + + def __call__( + self, library: Library, parent: IntegrationConfiguration, - settings: Optional[dict] = None, + settings_dict: Optional[dict] = None, ) -> IntegrationLibraryConfiguration: - settings = settings or {} + settings_dict = settings_dict or {} integration, _ = create( - db.session, + self.db.session, IntegrationLibraryConfiguration, parent=parent, library=library, - settings=settings, + settings_dict=settings_dict, ) return integration - return create_library_integration + +@pytest.fixture +def create_integration_library_configuration( + db: DatabaseTransactionFixture, +) -> IntegrationLibraryConfigurationFixture: + fixture = IntegrationLibraryConfigurationFixture(db) + return fixture class DBStatementCounter: diff --git a/tests/fixtures/odl.py b/tests/fixtures/odl.py index ade1e17042..d1c5f8b559 100644 --- a/tests/fixtures/odl.py +++ b/tests/fixtures/odl.py @@ -92,7 +92,7 @@ def collection(self, library): protocol=integration_protocol ) config = collection.create_integration_configuration(integration_protocol) - config.settings = { + config.settings_dict = { "username": "a", "password": "b", "url": "http://metadata",