Skip to content

Commit 4e17f06

Browse files
authored
Merge pull request #186 from DomainTools/IDEV-2299-validation-and-error-handling-improvements
IDEV-2299: Validation and error handling improvements
2 parents d0346ca + fe2e7d3 commit 4e17f06

File tree

7 files changed

+746
-128
lines changed

7 files changed

+746
-128
lines changed

domaintools/api.py

Lines changed: 0 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -671,7 +671,6 @@ def iris_investigate(
671671
data_updated_after=None,
672672
expiration_date=None,
673673
create_date=None,
674-
active=None,
675674
search_hash=None,
676675
risk_score=None,
677676
younger_than_date=None,
@@ -701,9 +700,6 @@ def iris_investigate(
701700
if search_hash:
702701
kwargs["search_hash"] = search_hash
703702

704-
if not (kwargs or domains):
705-
raise ValueError("Need to define investigation using kwarg filters or domains")
706-
707703
if isinstance(domains, (list, tuple)):
708704
domains = ",".join(domains)
709705
if hasattr(data_updated_after, "strftime"):
@@ -712,8 +708,6 @@ def iris_investigate(
712708
expiration_date = expiration_date.strftime("%Y-%m-%d")
713709
if hasattr(create_date, "strftime"):
714710
create_date = create_date.strftime("%Y-%m-%d")
715-
if isinstance(active, bool):
716-
kwargs["active"] = str(active).lower()
717711

718712
results = self._results(
719713
"iris-investigate",

domaintools/decorators.py

Lines changed: 50 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,39 +1,81 @@
11
import functools
2+
import inspect
23

34
from typing import List, Union
45

56
from domaintools.docstring_patcher import DocstringPatcher
7+
from domaintools.request_validator import RequestValidator
68

79

810
def api_endpoint(spec_name: str, path: str, methods: Union[str, List[str]]):
911
"""
10-
Decorator to tag a method as an API endpoint.
12+
Decorator to tag a method as an API endpoint AND validate inputs.
1113
1214
Args:
1315
spec_name: The key for the spec in api_instance.specs
1416
path: The API path (e.g., "/users")
1517
methods: A single method ("get") or list of methods (["get", "post"])
16-
that this function handles.
1718
"""
1819

1920
def decorator(func):
2021
func._api_spec_name = spec_name
2122
func._api_path = path
2223

23-
# Always store the methods as a list
24-
if isinstance(methods, str):
25-
func._api_methods = [methods]
26-
else:
27-
func._api_methods = methods
24+
# Normalize methods to a list
25+
normalized_methods = [methods] if isinstance(methods, str) else methods
26+
func._api_methods = normalized_methods
27+
28+
# Get the signature of the original function ONCE
29+
sig = inspect.signature(func)
2830

2931
@functools.wraps(func)
3032
def wrapper(self, *args, **kwargs):
33+
34+
try:
35+
bound_args = sig.bind(*args, **kwargs)
36+
except TypeError:
37+
# If arguments don't match signature, let the actual func raise the error
38+
return func(*args, **kwargs)
39+
40+
arguments = bound_args.arguments
41+
42+
# Robustly find 'self' (it's usually the first argument in bound_args)
43+
# We look for the first value in arguments, or try to get 'self' explicitly.
44+
instance = arguments.pop("self", None)
45+
if not instance and args:
46+
instance = args[0]
47+
48+
# Retrieve the Spec from the instance
49+
# We assume 'self' has a .specs attribute (like DocstringPatcher expects)
50+
spec = getattr(self, "specs", {}).get(spec_name)
51+
52+
if spec:
53+
# Determine which HTTP method is currently being executed.
54+
# If the function allows dynamic methods (e.g. method="POST"), use that.
55+
# Otherwise, default to the first method defined in the decorator.
56+
current_method = kwargs.get("method", normalized_methods[0])
57+
58+
# Run Validation
59+
# This will raise a ValueError and stop execution if validation fails.
60+
try:
61+
RequestValidator.validate(
62+
spec=spec,
63+
path=path,
64+
method=current_method,
65+
parameters=arguments,
66+
)
67+
except ValueError as e:
68+
print(f"[Validation Error] {e}")
69+
raise e
70+
71+
# Proceed with the original function call
3172
return func(*args, **kwargs)
3273

33-
# Copy all tags to the wrapper
74+
# Copy tags to wrapper for the DocstringPatcher to find
3475
wrapper._api_spec_name = func._api_spec_name
3576
wrapper._api_path = func._api_path
3677
wrapper._api_methods = func._api_methods
78+
3779
return wrapper
3880

3981
return decorator

0 commit comments

Comments
 (0)