From ecdfa0e3d7bacad8cf9c72d37cb874c74926bf07 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Edgar=20Ram=C3=ADrez=20Mondrag=C3=B3n?= <16805946+edgarrmondragon@users.noreply.github.com> Date: Thu, 18 Jan 2024 18:43:21 -0600 Subject: [PATCH] feat: Support validating configuration for any tap with a dynamic catalog (#937) * refactor: Use inheritance for plugins' CLI * Make mypy happy * feat: Support validating configuration for any tap with a dynamic catalog * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * Update docs * Delete leftover file * Add # pragma: no cover * Document class attributes --------- Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> --- ...dk.authenticators.APIAuthenticatorBase.rst | 2 +- singer_sdk/tap_base.py | 39 +++++++++++++++---- 2 files changed, 32 insertions(+), 9 deletions(-) diff --git a/docs/classes/singer_sdk.authenticators.APIAuthenticatorBase.rst b/docs/classes/singer_sdk.authenticators.APIAuthenticatorBase.rst index 1b3b608c5..70e9a127f 100644 --- a/docs/classes/singer_sdk.authenticators.APIAuthenticatorBase.rst +++ b/docs/classes/singer_sdk.authenticators.APIAuthenticatorBase.rst @@ -5,4 +5,4 @@ .. autoclass:: APIAuthenticatorBase :members: - :special-members: __init__, __call__ \ No newline at end of file + :special-members: __init__, __call__ diff --git a/singer_sdk/tap_base.py b/singer_sdk/tap_base.py index 03aa5e814..2ea4c8f2c 100644 --- a/singer_sdk/tap_base.py +++ b/singer_sdk/tap_base.py @@ -13,7 +13,11 @@ from singer_sdk._singerlib import Catalog, StateMessage from singer_sdk.configuration._dict_config import merge_missing_config_jsonschema -from singer_sdk.exceptions import AbortedSyncFailedException, AbortedSyncPausedException +from singer_sdk.exceptions import ( + AbortedSyncFailedException, + AbortedSyncPausedException, + ConfigValidationError, +) from singer_sdk.helpers import _state from singer_sdk.helpers._classproperty import classproperty from singer_sdk.helpers._compat import final @@ -53,6 +57,10 @@ class Tap(PluginBase, SingerWriter, metaclass=abc.ABCMeta): plugins. """ + dynamic_catalog: bool = False + """Whether the tap's catalog is dynamic. Set to True if the catalog is + generated dynamically (e.g. by querying a database's system tables).""" + # Constructor def __init__( @@ -520,12 +528,17 @@ def cb_discover( config_args = ctx.params.get("config", ()) config_files, parse_env_config = cls.config_from_cli_args(*config_args) - tap = cls( - config=config_files, # type: ignore[arg-type] - parse_env_config=parse_env_config, - validate_config=False, - setup_mapper=False, - ) + try: + tap = cls( + config=config_files, # type: ignore[arg-type] + parse_env_config=parse_env_config, + validate_config=cls.dynamic_catalog, + setup_mapper=False, + ) + except ConfigValidationError as exc: # pragma: no cover + for error in exc.errors: + cls.logger.error("Config validation error: %s", error) + ctx.exit(1) tap.run_discovery() ctx.exit() @@ -611,8 +624,18 @@ def get_singer_command(cls: type[Tap]) -> click.Command: class SQLTap(Tap): """A specialized Tap for extracting from SQL streams.""" - # Stream class used to initialize new SQL streams from their catalog declarations. default_stream_class: type[SQLStream] + """ + The default stream class used to initialize new SQL streams from their catalog + entries. + """ + + dynamic_catalog: bool = True + """ + Whether the tap's catalog is dynamic, enabling configuration validation in + discovery mode. Set to True if the catalog is generated dynamically (e.g. by + querying a database's system tables). + """ _tap_connector: SQLConnector | None = None