Cat Feeder  1.0.0
The Cat feeder project
Loading...
Searching...
No Matches
metadata.py
Go to the documentation of this file.
1r"""
2# +==== BEGIN CatFeeder =================+
3# LOGO:
4# ..............(..../\
5# ...............)..(.')
6# ..............(../..)
7# ...............\‍(__)|
8# Inspired by Joan Stark
9# source https://www.asciiart.eu/
10# animals/cats
11# /STOP
12# PROJECT: CatFeeder
13# FILE: metadata.py
14# CREATION DATE: 23-01-2026
15# LAST Modified: 23:49:53 24-01-2026
16# DESCRIPTION:
17# This is the project in charge of making the connected cat feeder project work.
18# /STOP
19# COPYRIGHT: (c) Cat Feeder
20# PURPOSE: The decorator allowing the user to set and specify their own tags for the endpoint.
21# Metadata decorators for API endpoint documentation.
22#
23# Provides decorators to add tags, descriptions, and other documentation metadata.
24# /STOP
25# // AR
26# +==== END CatFeeder =================+
27"""
28
29from functools import wraps
30from typing import Callable
31
32from .decorator_constants import TagCategory
33
34
35def set_tags(*tags: TagCategory) -> Callable:
36 """Set tags for endpoint categorization in API documentation.
37
38 Args:
39 tags: Tag categories to assign to the endpoint.
40
41 Returns:
42 Decorator function.
43 """
44 def decorator(func: Callable) -> Callable:
45 @wraps(func)
46 def wrapper(*args, **kwargs):
47 return func(*args, **kwargs)
48
49 # Convert enum values to strings
50 tag_values = [tag.value if isinstance(
51 tag, TagCategory) else str(tag) for tag in tags]
52 setattr(wrapper, "_tags", tag_values)
53
54 # Preserve existing metadata
55 _preserve_metadata(func, wrapper)
56 return wrapper
57 return decorator
58
59
60def set_description(description: str) -> Callable:
61 """Set description for the endpoint.
62
63 Args:
64 description: Description text for the endpoint.
65
66 Returns:
67 Decorator function.
68 """
69 def decorator(func: Callable) -> Callable:
70 @wraps(func)
71 def wrapper(*args, **kwargs):
72 return func(*args, **kwargs)
73
74 setattr(wrapper, "_description", description)
75
76 # Preserve any existing metadata
77 if hasattr(func, '_requires_auth'):
78 setattr(wrapper, "_requires_auth", getattr(func, "_requires_auth"))
79 if hasattr(func, '_requires_admin'):
80 setattr(
81 wrapper,
82 "_requires_admin",
83 getattr(func, "_requires_admin")
84 )
85 if hasattr(func, '_public'):
86 setattr(wrapper, "_public", getattr(func, "_public"))
87 if hasattr(func, '_security_level'):
88 setattr(
89 wrapper,
90 "_security_level",
91 getattr(func, "_security_level")
92 )
93 if hasattr(func, '_tags'):
94 setattr(wrapper, "_tags", getattr(func, "_tags"))
95 if hasattr(func, '_summary'):
96 setattr(wrapper, "_summary", getattr(func, "_summary"))
97 if hasattr(func, '_response_model'):
98 setattr(
99 wrapper,
100 "_response_model",
101 getattr(func, "_response_model")
102 )
103
104 return wrapper
105 return decorator
106
107
108def set_summary(summary: str) -> Callable:
109 """Set summary for the endpoint.
110
111 Args:
112 summary: Summary text for the endpoint.
113
114 Returns:
115 Decorator function.
116 """
117 def decorator(func: Callable) -> Callable:
118 @wraps(func)
119 def wrapper(*args, **kwargs):
120 return func(*args, **kwargs)
121
122 setattr(wrapper, "_summary", summary)
123
124 # Preserve any existing metadata
125 if hasattr(func, '_requires_auth'):
126 setattr(wrapper, "_requires_auth", getattr(func, "_requires_auth"))
127 if hasattr(func, '_requires_admin'):
128 setattr(
129 wrapper,
130 "_requires_admin",
131 getattr(func, "_requires_admin")
132 )
133 if hasattr(func, '_public'):
134 setattr(wrapper, "_public", getattr(func, "_public"))
135 if hasattr(func, '_security_level'):
136 setattr(
137 wrapper,
138 "_security_level",
139 getattr(func, "_security_level")
140 )
141 if hasattr(func, '_tags'):
142 setattr(wrapper, "_tags", getattr(func, "_tags"))
143 if hasattr(func, '_description'):
144 setattr(wrapper, "_description", getattr(func, "_description"))
145 if hasattr(func, '_response_model'):
146 setattr(
147 wrapper,
148 "_response_model",
149 getattr(func, "_response_model")
150 )
151
152 return wrapper
153 return decorator
154
155
156def set_operation_id(operation_id: str) -> Callable:
157 """Set custom operation ID for the endpoint.
158
159 Args:
160 operation_id: Custom operation ID for OpenAPI schema.
161
162 Returns:
163 Decorator function.
164 """
165 def decorator(func: Callable) -> Callable:
166 @wraps(func)
167 def wrapper(*args, **kwargs):
168 return func(*args, **kwargs)
169
170 # Set both __name__ and _operation_id to ensure it works
171 wrapper.__name__ = operation_id
172 setattr(wrapper, "_operation_id", operation_id)
173 # Store base for multi-method endpoints
174 setattr(wrapper, "_operation_id_base", operation_id)
175
176 # Preserve existing metadata
177 _preserve_metadata(func, wrapper)
178 return wrapper
179 return decorator
180
181
182def user_endpoint(func: Callable) -> Callable:
183 """Mark endpoint as belonging to user management category."""
184 return set_tags(TagCategory.USERS)(func)
185
186
187def cat_endpoint(func: Callable) -> Callable:
188 """Mark endpoint as belonging to cat management category."""
189 return set_tags(TagCategory.CAT_MANAGEMENT)(func)
190
191
192def front_end_endpoint(func: Callable) -> Callable:
193 """Mark endpoint as belonging to front-end management category."""
194 return set_tags(TagCategory.FRONT_END)(func)
195
196
197def front_end_assets_endpoint(func: Callable) -> Callable:
198 """Mark endpoint as belonging to front-end assets management category."""
199 return set_tags(TagCategory.FRONT_END_ASSETS)(func)
200
201
202def oauth_endpoint(func: Callable) -> Callable:
203 """Mark endpoint as belonging to OAuth category."""
204 return set_tags(TagCategory.OAUTH)(func)
205
206
207def token_endpoint(func: Callable) -> Callable:
208 """Mark endpoint as belonging to authentication category."""
209 return set_tags(TagCategory.AUTHENTICATION)(func)
210
211
212def system_endpoint(func: Callable) -> Callable:
213 """Mark endpoint as belonging to system category."""
214 return set_tags(TagCategory.SYSTEM)(func)
215
216
217def _preserve_metadata(func: Callable, wrapper: Callable) -> None:
218 """Helper function to preserve existing metadata attributes."""
219 metadata_attrs = [
220 '_requires_auth', '_requires_admin', '_public', '_testing_only',
221 '_security_level', '_environment', '_description', '_summary',
222 '_response_model', '_operation_id', '_accepts_json_body',
223 '_json_body_description', '_json_body_example', '_requires_bearer_auth'
224 ]
225
226 for attr in metadata_attrs:
227 if hasattr(func, attr):
228 setattr(wrapper, attr, getattr(func, attr))
Callable set_operation_id(str operation_id)
Definition metadata.py:156
Callable front_end_assets_endpoint(Callable func)
Definition metadata.py:197
None _preserve_metadata(Callable func, Callable wrapper)
Definition metadata.py:217