Skip to content

Commit 451e2cb

Browse files
committed
Refactor API endpoints and add docstrings
1 parent 8a0e1f2 commit 451e2cb

32 files changed

+1194
-1029
lines changed

ledfx/api/__init__.py

Lines changed: 78 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -63,6 +63,84 @@ async def json_decode_error(self) -> web.Response:
6363
}
6464
return web.json_response(data=response, status=400)
6565

66+
async def internal_error(self, type, msg) -> web.Response:
67+
"""
68+
Handle messaging for internal errors.
69+
70+
Returns:
71+
A web response with a JSON payload containing the error and a 500 status.
72+
"""
73+
response = {
74+
"status": "failed",
75+
"payload": {"type": type, "reason": msg},
76+
}
77+
return web.json_response(data=response, status=500)
78+
79+
async def invalid_request(
80+
self, reason="Invalid request", type="error", resp_code=200
81+
) -> web.Response:
82+
"""
83+
Returns a JSON response indicating an invalid request.
84+
85+
Args:
86+
reason (str): The reason for the invalid request. Defaults to 'Invalid request'.
87+
type (str): The type of error. Defaults to 'error'. Options 'default','error', 'success', 'warning','info'.
88+
resp_code (int): The response code to be returned. Defaults to 200 so that snackbar works.
89+
90+
Returns:
91+
web.Response: A JSON response with the status and reason for the invalid request.
92+
"""
93+
response = {
94+
"status": "failed",
95+
"payload": {
96+
"type": type,
97+
"reason": reason,
98+
},
99+
}
100+
return web.json_response(data=response, status=resp_code)
101+
102+
async def request_success(
103+
self, type=None, message=None, resp_code=200
104+
) -> web.Response:
105+
"""
106+
Returns a JSON response indicating a successful request.
107+
Optionally include a snackbar type and message to return to the user.
108+
109+
Args:
110+
type (str): The type of snackbar to display. Defaults to None.
111+
message (str): The message to display in the snackbar. Defaults to None.
112+
resp_code (int): The response code to be returned. Defaults to 200.
113+
114+
Returns:
115+
web.Response: A JSON response with the status and payload for the successful request.
116+
"""
117+
response = {
118+
"status": "success",
119+
}
120+
if type and message is not None:
121+
response["payload"] = {
122+
"type": type,
123+
"reason": message,
124+
}
125+
return web.json_response(data=response, status=resp_code)
126+
127+
async def bare_request_success(self, payload) -> web.Response:
128+
"""
129+
Returns a "bare" JSON response indicating a successful request - only a payload and a 200 code.
130+
131+
Args:
132+
payload (dict): The payload to be returned.
133+
resp_code (int): The response code to be returned. Defaults to 200.
134+
135+
Returns:
136+
web.Response: A JSON response with the status and payload for the successful request.
137+
"""
138+
if payload is None:
139+
raise ValueError(
140+
"Payload must be provided to the bare request_success method"
141+
)
142+
return web.json_response(data=payload, status=200)
143+
66144

67145
class RestApi(RegistryLoader):
68146
PACKAGE_NAME = "ledfx.api"

ledfx/api/audio_devices.py

Lines changed: 25 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -18,43 +18,51 @@ class AudioDevicesEndpoint(RestEndpoint):
1818
_audio = None
1919

2020
async def get(self) -> web.Response:
21-
"""Get list of audio devices using sound device"""
21+
"""
22+
Get list of audio devices using sound device
2223
24+
Returns:
25+
web.Response: The response containing the list of audio devices and the active device index.
26+
"""
2327
audio_config = AudioInputSource.AUDIO_CONFIG_SCHEMA.fget()(
24-
self._ledfx.config.get("audio", {})
28+
self._ledfx.config.get("audio_device", {})
2529
)
2630

2731
response = {}
28-
response["active_device_index"] = audio_config["device_index"]
32+
response["active_device_index"] = audio_config["audio_device"]
2933
response[
3034
"devices"
3135
] = AudioInputSource.input_devices() # dict(enumerate(input_devices))
36+
return await self.request_success(response)
3237

33-
return web.json_response(data=response, status=200)
38+
async def put(self, request: web.Request) -> web.Response:
39+
"""
40+
Set audio device to use as input.
3441
35-
async def put(self, request) -> web.Response:
36-
"""Set audio device to use as input"""
42+
Args:
43+
request (web.Request): The request object containing the new device `index`.
44+
45+
Returns:
46+
web.Response: The HTTP response object.
47+
48+
"""
3749
try:
3850
data = await request.json()
3951
except JSONDecodeError:
4052
return await self.json_decode_error()
4153

4254
index = data.get("index")
4355
if index is None:
44-
response = {
45-
"status": "failed",
46-
"reason": 'Required attribute "index" was not provided',
47-
}
48-
return web.json_response(data=response, status=400)
56+
return await self.invalid_request(
57+
reason="Required attribute 'index' was not provided"
58+
)
4959

5060
valid_indexes = AudioInputSource.valid_device_indexes()
5161

5262
if index not in valid_indexes:
53-
response = {
54-
"status": "failed",
55-
"reason": f"Invalid device index [{index}]",
56-
}
57-
return web.json_response(data=response, status=400)
63+
return await self.invalid_request(
64+
reason=f"Invalid device index [{index}]"
65+
)
5866

5967
# Update and save config
6068
new_config = self._ledfx.config.get("audio", {})
@@ -69,5 +77,4 @@ async def put(self, request) -> web.Response:
6977
if self._ledfx.audio:
7078
self._ledfx.audio.update_config(new_config)
7179

72-
response = {"status": "success"}
73-
return web.json_response(data=response, status=200)
80+
await self.request_success()

ledfx/api/colors.py

Lines changed: 42 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import logging
2+
from json import JSONDecodeError
23

34
from aiohttp import web
45

@@ -14,6 +15,9 @@ class ColorEndpoint(RestEndpoint):
1415
async def get(self) -> web.Response:
1516
"""
1617
Get LedFx colors and gradients
18+
19+
Returns:
20+
web.Response: The response containing the colors and gradients.
1721
"""
1822
response = {
1923
"colors": dict(
@@ -23,41 +27,60 @@ async def get(self) -> web.Response:
2327
zip(("builtin", "user"), self._ledfx.gradients.get_all())
2428
),
2529
}
30+
return await self.bare_request_success(response)
2631

27-
return web.json_response(data=response, status=200)
28-
29-
async def delete(self, request) -> web.Response:
32+
async def delete(self, request: web.Request) -> web.Response:
3033
"""
3134
Deletes a user defined color or gradient
3235
Request body is a string of the color key to delete
3336
eg. ["my_red_color"]
37+
38+
Parameters:
39+
- request (web.Request): The HTTP request object
40+
41+
Returns:
42+
- web.Response: The HTTP response object
3443
"""
3544

3645
data = await request.json()
3746

3847
for key in data:
3948
del self._ledfx.colors[key]
4049
del self._ledfx.gradients[key]
50+
return await self.request_success("success", "Deleted {key}")
4151

42-
response = {
43-
"status": "success",
44-
"payload": {
45-
"type": "success",
46-
"reason": "Deleted",
47-
},
48-
}
49-
50-
return web.json_response(data=response, status=200)
51-
52-
async def post(self, request) -> web.Response:
52+
async def post(self, request: web.Request) -> web.Response:
5353
"""
5454
Creates or updates existing colors or gradients.
55-
eg. {"my_red_color": "#ffffff"}
56-
or {"my_cool_gradient": "lin..."}
57-
"""
5855
59-
data = await request.json()
56+
Parameters:
57+
- request (web.Request): The request containing the color or gradient to create or update.
58+
59+
Returns:
60+
- web.Response: The HTTP response object.
6061
62+
Example:
63+
```
64+
{
65+
"my_red_color": "#ffffff"
66+
}
67+
```
68+
or
69+
```
70+
{
71+
"my_cool_gradient": "lin..."
72+
}
73+
"""
74+
try:
75+
data = await request.json()
76+
except JSONDecodeError:
77+
return await self.json_decode_error()
78+
if data is None:
79+
return await self.invalid_request(
80+
reason="Required attribute was not provided"
81+
)
82+
83+
# TODO: Handle instances where neither color nor gradient is provided
6184
for key, val in data.items():
6285
try:
6386
is_color = validate_color(val)
@@ -68,12 +91,4 @@ async def post(self, request) -> web.Response:
6891
else:
6992
self._ledfx.gradients[key] = val
7093

71-
response = {
72-
"status": "success",
73-
"payload": {
74-
"type": "success",
75-
"reason": "Saved",
76-
},
77-
}
78-
79-
return web.json_response(data=response, status=200)
94+
return await self.request_success("success", "Saved {key}")

ledfx/api/com_ports.py

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -12,11 +12,16 @@ class InfoEndpoint(RestEndpoint):
1212
ENDPOINT_PATH = "/api/comports"
1313

1414
async def get(self) -> web.Response:
15+
"""
16+
Get the list of available COM ports.
17+
18+
Returns:
19+
web.Response: The response containing the list of available COM ports.
20+
"""
1521
ports = serial.tools.list_ports.comports()
1622

1723
available_ports = []
1824

1925
for p in ports:
2026
available_ports.append(p.device)
21-
22-
return web.json_response(data=available_ports, status=200)
27+
return await self.bare_request_success(available_ports)

0 commit comments

Comments
 (0)