Skip to content

Commit ace8c1d

Browse files
authored
refactor: Removed 'required' attributes and simplify validation (DenverCoder1#43)
1 parent 9f396d1 commit ace8c1d

File tree

4 files changed

+83
-44
lines changed

4 files changed

+83
-44
lines changed

api/index.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -28,10 +28,10 @@ def render():
2828
background_color = validate_color(request, "background_color", default="#0d1117")
2929
title_color = validate_color(request, "title_color", default="#ffffff")
3030
stats_color = validate_color(request, "stats_color", default="#dedede")
31-
title = trim_text(validate_string(request, "title"), (width - 20) // 8)
31+
title = trim_text(validate_string(request, "title", default=""), (width - 20) // 8)
3232
publish_timestamp = validate_int(request, "timestamp", default=0)
3333
duration_seconds = validate_int(request, "duration", default=0)
34-
video_id = validate_video_id(request, "id", required=True)
34+
video_id = validate_video_id(request, "id")
3535
thumbnail = data_uri_from_url(f"https://i.ytimg.com/vi/{video_id}/mqdefault.jpg")
3636
views = fetch_views(video_id)
3737
diff = (

api/validate.py

Lines changed: 12 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -1,65 +1,37 @@
1-
from typing import Optional
21
import re
32
from flask.wrappers import Request
43

54

6-
def validate_int(req: Request, field: str, *, default: int = 0, required: bool = False) -> int:
7-
"""Validate an integer, returns the integer if valid, otherwise the default.
8-
9-
Raises ValueError if the field is required and the valid is not valid.
10-
"""
5+
def validate_int(req: Request, field: str, default: int = 0) -> int:
6+
"""Validate an integer, returns the integer if valid, otherwise the default."""
117
value = req.args.get(field, "")
12-
if value == "" and required:
13-
raise ValueError(f"Required parameter '{field}' is missing")
148
try:
159
return int(value)
1610
except ValueError:
17-
if required:
18-
raise ValueError(f"{field} expects an integer but got '{value}'")
1911
return default
2012

2113

22-
def validate_color(
23-
req: Request, field: str, *, default: str = "#ffffff", required: bool = False
24-
) -> str:
25-
"""Validate a color, returns the color if valid hex code (3, 4, 6, or 8 characters), otherwise the default.
26-
27-
Raises ValueError if the field is required and the valid is not valid.
28-
"""
14+
def validate_color(req: Request, field: str, default: str = "#ffffff") -> str:
15+
"""Validate a color, returns the color if it's a valid hex code (3, 4, 6, or 8 characters), otherwise the default."""
2916
value = req.args.get(field, "")
30-
if value == "" and required:
31-
raise ValueError(f"Required parameter '{field}' is missing")
3217
hex_digits = re.sub(r"[^a-fA-F0-9]", "", value)
3318
if len(hex_digits) not in (3, 4, 6, 8):
34-
if required:
35-
raise ValueError(f"{field} expects a hex color but got '{value}'")
3619
return default
3720
return f"#{hex_digits}"
3821

3922

40-
def validate_video_id(
41-
req: Request, field: str, *, default: str = "", required: bool = False
42-
) -> str:
43-
"""Validate a video ID, returns the video ID if valid, otherwise the default.
23+
def validate_video_id(req: Request, field: str) -> str:
24+
"""Validate a video ID, returns the video ID if valid.
4425
45-
Raises ValueError if the field is required and the valid is not valid.
46-
"""
26+
Raises ValueError if the field is not provided or fails the validation regex."""
4727
value = req.args.get(field, "")
48-
if value == "" and required:
28+
if value == "":
4929
raise ValueError(f"Required parameter '{field}' is missing")
5030
if not re.match(r"^[a-zA-Z0-9_-]+$", value):
51-
if required:
52-
raise ValueError(f"{field} expects a video ID but got '{value}'")
53-
return default
31+
raise ValueError(f"{field} expects a video ID but got '{value}'")
5432
return value
5533

5634

57-
def validate_string(req: Request, field: str, *, required: bool = False) -> str:
58-
"""Validate a string, returns the string if valid, otherwise the default.
59-
60-
Raises ValueError if the field is required and the valid is not valid.
61-
"""
62-
value = req.args.get(field, "")
63-
if value == "" and required:
64-
raise ValueError(f"Required parameter '{field}' is missing")
65-
return value
35+
def validate_string(req: Request, field: str, default: str = "") -> str:
36+
"""Validate a string, returns the string if valid, otherwise the default."""
37+
return req.args.get(field, default)

tests/test_validate.py

Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,67 @@
1+
import pytest
2+
from api.validate import validate_color, validate_int, validate_string, validate_video_id
3+
from flask.wrappers import Request
4+
5+
6+
class MockRequest(Request):
7+
def __init__(self, **kwargs):
8+
self.args = kwargs # type: ignore
9+
10+
11+
def test_validate_int():
12+
# missing field
13+
req = MockRequest()
14+
assert validate_int(req, "width", default=250) == 250
15+
# invalid field
16+
req = MockRequest(width="abc")
17+
assert validate_int(req, "width", default=250) == 250
18+
# valid field
19+
req = MockRequest(width="100")
20+
assert validate_int(req, "width", default=250) == 100
21+
22+
23+
def test_validate_color():
24+
# missing field
25+
req = MockRequest()
26+
assert validate_color(req, "background_color", default="#0d1117") == "#0d1117"
27+
# invalid field characters
28+
req = MockRequest(background_color="#fghijk")
29+
assert validate_color(req, "background_color", default="#0d1117") == "#0d1117"
30+
# invalid field length
31+
req = MockRequest(background_color="#012345678")
32+
assert validate_color(req, "background_color", default="#0d1117") == "#0d1117"
33+
# valid field - 6 characters
34+
req = MockRequest(background_color="#ffffff")
35+
assert validate_color(req, "background_color", default="#0d1117") == "#ffffff"
36+
# valid field - 8 characters
37+
req = MockRequest(background_color="#01234567")
38+
assert validate_color(req, "background_color", default="#0d1117") == "#01234567"
39+
# valid field - 4 characters
40+
req = MockRequest(background_color="#89ab")
41+
assert validate_color(req, "background_color", default="#0d1117") == "#89ab"
42+
# valid field - 3 characters
43+
req = MockRequest(background_color="#cde")
44+
assert validate_color(req, "background_color", default="#0d1117") == "#cde"
45+
46+
47+
def test_validate_video_id():
48+
# missing field
49+
req = MockRequest()
50+
with pytest.raises(ValueError):
51+
validate_video_id(req, "id")
52+
# invalid field
53+
req = MockRequest(id="<script>alert('xss')</script>")
54+
with pytest.raises(ValueError):
55+
validate_video_id(req, "id")
56+
# valid field
57+
req = MockRequest(id="abc_123-456")
58+
assert validate_video_id(req, "id") == "abc_123-456"
59+
60+
61+
def test_validate_string():
62+
# missing field
63+
req = MockRequest()
64+
assert validate_string(req, "text", default="Hello, world!") == "Hello, world!"
65+
# valid field
66+
req = MockRequest(text="Hello, world!")
67+
assert validate_string(req, "text", default="Hello, world!") == "Hello, world!"

tox.ini

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ isolated_build = True
44

55
[testenv]
66
deps =
7-
orjson
8-
feedparser
97
pytest
8+
feedparser
9+
-rrequirements.txt
1010
commands = pytest tests -s

0 commit comments

Comments
 (0)