Feat: Add configurable discovery - #18
Conversation
|
Requires Note to self: once |
425fe39 to
43946b9
Compare
2ae01e0 to
d69ef28
Compare
5bb10f2 to
5acaca4
Compare
- Add support for datamasque-python 1.2.1 - Add a status command to rulesets, libraries, discover configs, and discover libraries - Refuse YAML of 60 KiB+ in rulesets/discover configs validate - Rework discover libraries for updated library model - Fix rulesets generate and connections update --password - Drop click dependency - Update changelog - Report server reason when library deletion is rejected
- Add validation to discover configs/libraries - Abort on empty YAML directly - Abort with not-found when a run has no discovery output
67b4915 to
fdb5852
Compare
| @@ -0,0 +1,328 @@ | |||
| """Live-instance tests for configurable discovery.""" | |||
There was a problem hiding this comment.
Nit:
-
Could we rename this file to
test_discovery_configs.py, since (from what I see?) it testsdiscovery_configs.pyanddiscovery_config_libraries.pybut notdiscovery.py -
After renaming, we will be able to remove this docstring since the file is self-explanatory.
| named = [c for c in client.list_discovery_configs() if c.name == name] | ||
| matches = [c for c in named if c.config_type is expected_type] |
There was a problem hiding this comment.
Nit: Could we use explicit variable names: named_configs and matched_configs rather than named and matches - makes it easier to review and spot bugs.
| Creates a temporary config to trigger server-side validation, | ||
| then deletes it. Reports any validation errors. | ||
|
|
||
| Note that configs over 60 KB validate asynchronously and cannot be validated here. |
There was a problem hiding this comment.
Everywhere else says KiB
| def _create_returning( | ||
| is_valid: ValidationStatus | None, | ||
| validation_errors: list[SimpleNamespace] | None = None, | ||
| ) -> Callable[[object], object]: | ||
|
|
||
| def fake_create(rs: object) -> object: | ||
| rs.id = 99 # type: ignore[attr-defined] | ||
| rs.is_valid = is_valid # type: ignore[attr-defined] | ||
| rs.validation_errors = validation_errors or [] # type: ignore[attr-defined] | ||
| return rs | ||
|
|
||
| return fake_create |
There was a problem hiding this comment.
(Claude)
The name reads as "create returning ... what?" and Callable[[object], object] hides what actually flows through here, which is what forces the three # type: ignore[attr-defined] below.
- The clone of this helper in
test_discovery_configs.pyis typedCallable[[DiscoveryConfig], DiscoveryConfig]and needs no ignores; typing this one againstRulesetdrops all three. - A third copy lives in
test_discovery_config_libraries.py, so the fake is spelled three ways across three files.
Suggestion: rename to _fake_create_with_validation, type it Callable[[Ruleset], Ruleset].
For bonus points, share one helper across the three test files.
| @patch(f"{MODULE}.get_client") | ||
| def test_status_valid_exits_0(mock_get_client: MagicMock, runner: CliRunner) -> None: | ||
| client = MagicMock() | ||
| mock_get_client.return_value = client | ||
| client.list_rulesets.return_value = [_listed_ruleset(ValidationStatus.valid)] | ||
|
|
||
| result = runner.invoke(app, ["rulesets", "status", "demo", "--json"]) | ||
|
|
||
| assert result.exit_code == 0 | ||
| assert '"status": "valid"' in result.stdout |
There was a problem hiding this comment.
Can we use pytest.mark.parametrize for this and the 2 functions below?
| dm discover configs list [--type database|file] # List configs | ||
| dm discover configs get <name> [--type database] [--yaml] # Show details or raw YAML | ||
| dm discover configs defaults [--type database|file] -o cfg.yaml # Built-in default as a starting point | ||
| dm discover configs create --name <n> --type database -f cfg.yaml # Create/update from YAML | ||
| dm discover configs delete <name> [--type database] # Delete a config | ||
| dm discover configs validate -f cfg.yaml --type database # Validate against server (YAML under 60 KiB) | ||
| dm discover configs status <name> [--type database] # Validation status; poll after creating YAML of 60 KiB+ |
There was a problem hiding this comment.
Should we be using [--type database|file] consistently?
|
|
||
| #### Discovery config libraries | ||
|
|
||
| Libraries are untyped — the same library can be imported by both database and file discovery configs. |
There was a problem hiding this comment.
| Libraries are untyped — the same library can be imported by both database and file discovery configs. | |
| The same library can be imported by both database and file discovery configs. |
| return str(match.id) | ||
|
|
||
|
|
||
| def _resolve_discovery_config_id( |
There was a problem hiding this comment.
Within this function could we use reduce code by using DM-Python's get_discovery_config_by_name(), which finds discovery configs matching the given name and type?
|
|
||
| OK = 0 | ||
| ERROR = 1 | ||
| USAGE = 2 |
There was a problem hiding this comment.
I wasn't sure what USAGE means and why it was not part of EXIT_CODE_BY_ERROR.
It seems to be an error message emitted by Typer for command-line usage errors and is emitted before any code actually runs. Could we add a comment saying that please?
|
|
||
|
|
||
| def abort_api_error(prefix: str, exc: DataMasqueApiError, *, conflict_hint: str | None = None) -> NoReturn: | ||
| """Abort with the admin server's own explanation of a failed request.""" |
There was a problem hiding this comment.
| """Abort with the admin server's own explanation of a failed request.""" | |
| """Abort with DataMasque's explanation of a failed request.""" |
|
I like this idea of However it may be easy to forget to add it in specific CLI commands - claude has found a few above. Preferably we would use these new Could we create a top-level safety net, does something like the following work? (claude) def main() -> None:
try:
app()
except DataMasqueApiError as exc:
abort_api_error("Request failed", exc)
except DataMasqueTransportError as exc:
abort(str(exc), code=ErrorCode.TRANSPORT_ERROR)Apart from consistently using |
No description provided.