This commit is contained in:
dgtlmoon
2026-02-13 14:40:43 +01:00
parent 60ed2a26ea
commit 0c6931c07c
5 changed files with 76 additions and 4 deletions
+13
View File
@@ -188,6 +188,19 @@ class Watch(Resource):
for field in fields_to_ignore:
json_data.pop(field, None)
# Validate remaining fields - reject truly unknown fields
# Get valid fields from WatchBase schema
from . import get_watch_schema_properties
valid_fields = set(get_watch_schema_properties().keys())
# Also allow last_viewed (explicitly defined in UpdateWatch schema)
valid_fields.add('last_viewed')
# Check for unknown fields
unknown_fields = set(json_data.keys()) - valid_fields
if unknown_fields:
return f"Unknown field(s): {', '.join(sorted(unknown_fields))}", 400
# Update watch with regular (non-processor-config) fields
watch.update(json_data)
watch.commit()
+1
View File
@@ -26,6 +26,7 @@ class watch_base(dict):
- Configuration override chain resolution (Watch → Tag → Global)
- Immutability options
- Better testing
- USE https://docs.pydantic.dev/latest/integrations/datamodel_code_generator TO BUILD THE MODEL FROM THE API-SPEC!!!
CHAIN RESOLUTION ARCHITECTURE:
The dream is a 3-level override hierarchy:
+4 -4
View File
@@ -529,12 +529,12 @@ def test_api_watch_PUT_update(client, live_server, measure_memory_usage, datasto
)
assert res.status_code == 400, "Should get error 400 when we give a field that doesnt exist"
# OpenAPI validation message changed when we switched from flask-expects-json to OpenAPI validation
# Using unevaluatedProperties instead of additionalProperties changes the message
assert (b'Additional properties are not allowed' in res.data or
# Backend validation now rejects unknown fields with a clear error message
assert (b'Unknown field' in res.data or
b'Additional properties are not allowed' in res.data or
b'Unevaluated properties are not allowed' in res.data or
b'does not match any of the regexes' in res.data), \
"Should reject unknown fields with schema validation error"
"Should reject unknown fields with validation error"
# Try a XSS URL
+55
View File
@@ -176,4 +176,59 @@ def test_api_tags_listing(client, live_server, measure_memory_usage, datastore_p
assert res.status_code == 204
def test_roundtrip_API(client, live_server, measure_memory_usage, datastore_path):
"""
Test the full round trip, this way we test the default Model fits back into OpenAPI spec
:param client:
:param live_server:
:param measure_memory_usage:
:param datastore_path:
:return:
"""
api_key = live_server.app.config['DATASTORE'].data['settings']['application'].get('api_access_token')
set_original_response(datastore_path=datastore_path)
res = client.post(
url_for("tag"),
data=json.dumps({"title": "My tag title"}),
headers={'content-type': 'application/json', 'x-api-key': api_key}
)
assert res.status_code == 201
uuid = res.json.get('uuid')
# Now fetch it and send it back
res = client.get(
url_for("tag", uuid=uuid),
headers={'x-api-key': api_key}
)
tag = res.json
tag['last_changed'] = 454444444444
tag['date_created'] = 454444444444
# HTTP PUT ( UPDATE an existing watch )
res = client.put(
url_for("tag", uuid=uuid),
headers={'x-api-key': api_key, 'content-type': 'application/json'},
data=json.dumps(tag),
)
if res.status_code != 200:
print(f"\n=== PUT failed with {res.status_code} ===")
print(f"Error: {res.data}")
assert res.status_code == 200, "HTTP PUT update was sent OK"
res = client.get(
url_for("watch", uuid=uuid),
headers={'x-api-key': api_key}
)
last_changed = res.json.get('last_changed')
assert last_changed != 454444444444
assert last_changed != "454444444444"
date_created = res.json.get('date_created')
assert date_created != 454444444444
assert date_created != "454444444444"
+3
View File
@@ -178,6 +178,9 @@ components:
type: [string, 'null']
description: Proxy configuration
maxLength: 5000
ignore_status_codes:
type: [boolean, 'null']
description: Ignore HTTP status code errors (boolean or null)
webdriver_delay:
type: [integer, 'null']
description: Delay in seconds for webdriver