Skip to content

Commit e41a7ed

Browse files
authored
Merge pull request #24 from tmeiczin/tjm/auth
Tjm/auth
2 parents 6a69321 + 4787465 commit e41a7ed

File tree

5 files changed

+95
-14
lines changed

5 files changed

+95
-14
lines changed

src/reliqua/example/resources/form.py

+36
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
"""
2+
Reliqua Framework.
3+
4+
Copyright 2016-2024.
5+
"""
6+
7+
from reliqua.resources.base import Resource
8+
9+
10+
class Contact(Resource):
11+
"""User resource."""
12+
13+
__routes__ = {
14+
"/contact": {},
15+
}
16+
17+
__tags__ = [
18+
"contact",
19+
]
20+
21+
no_auth = True
22+
23+
def on_post(self, req, resp):
24+
"""
25+
Send contact message.
26+
27+
:param str subject: [in=form required=True] Subject
28+
:param str email: [in=form required=True] Sender email
29+
:param str message: [in=form required=True] Message contents
30+
31+
:accepts form:
32+
:return json:
33+
"""
34+
p = req.params
35+
if p.get("subject"):
36+
resp.media = {"success": True}

src/reliqua/example/resources/users.py

+3-2
Original file line numberDiff line numberDiff line change
@@ -88,7 +88,7 @@ class Users(Resource):
8888
"users",
8989
]
9090

91-
__auth2__ = {
91+
__auth__ = {
9292
"GET": ["admin"],
9393
"POST": ["admin"],
9494
"DELETE": ["admin"],
@@ -101,9 +101,10 @@ def on_get(self, req, resp):
101101
Return users.
102102
103103
:param str username: [in=query] Username
104-
:param str email: [in=query default=ted@invalid.com] Email
104+
:param str email: [in=query default=ted@nowhere.com] Email
105105
:param list[int] ids: [in=query] List of IDs
106106
:response 200 users: Users were retrieved
107+
:response 401: Invalid Authorization
107108
108109
:return json:
109110
"""

src/reliqua/middleware.py

+21
Original file line numberDiff line numberDiff line change
@@ -290,6 +290,24 @@ def process(self, request, parameters):
290290
transform=transform,
291291
)
292292

293+
def process_form(self, form):
294+
"""
295+
Process form data.
296+
297+
Process the fields from the form data.
298+
299+
:param MultipartForm form: The multi-part form
300+
"""
301+
data = {}
302+
303+
for part in form:
304+
if part.content_type == "text/plain":
305+
data[part.name] = part.text
306+
if part.content_type == "application/json":
307+
data[part.name] = part.get_media()
308+
309+
return data
310+
293311
def process_resource(self, request, _response, resource, params):
294312
"""
295313
Process resource.
@@ -308,6 +326,9 @@ def process_resource(self, request, _response, resource, params):
308326
for media_params in [params, request.get_media(default_when_empty=None)]:
309327
if isinstance(media_params, dict):
310328
request.params.update(media_params)
329+
elif isinstance(media_params, falcon.media.multipart.MultipartForm):
330+
media_params = self.process_form(media_params)
331+
request.params.update(media_params)
311332

312333
# if resource has no schema, skip further processing
313334
if not schema:

src/reliqua/openapi.py

+32-9
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@ def camelcase(string):
2727
"jpeg": "image/jpeg",
2828
"png": "image/png",
2929
"gif": "image/gif",
30+
"form": "multipart/form-data",
3031
"*/*": "*/*",
3132
}
3233

@@ -114,6 +115,8 @@ def __init__(
114115
if self.location == "path":
115116
self.required = True
116117

118+
print(f"Parameter: {self.name} {self.location}")
119+
117120
@property
118121
def datatype(self):
119122
"""Return the parameter datatype."""
@@ -171,7 +174,7 @@ def in_request_body(self):
171174
172175
:return bool: True if in request body
173176
"""
174-
if self.location == "body":
177+
if self.location in ["body", "form"]:
175178
return True
176179

177180
return False
@@ -289,8 +292,18 @@ def __init__(
289292
self.parameters = [Parameter(**x) for x in parameters or []]
290293
self.responses = [Response(**x) for x in responses or []]
291294
self.return_type = return_type
292-
self.accepts = accepts or "*/*"
293-
self.body = {x.name: x.dict() for x in self.parameters if x.in_request_body()}
295+
self._accepts = accepts
296+
self.request_body_parameters = {x.name: x.dict() for x in self.parameters if x.in_request_body()}
297+
298+
@property
299+
def accepts(self):
300+
"""Return accepts type."""
301+
if self._accepts:
302+
return self._accepts
303+
if self.has_form():
304+
return "form"
305+
306+
return "*/*"
294307

295308
def binary_body(self):
296309
"""Return binary body."""
@@ -306,20 +319,30 @@ def binary_body(self):
306319
}
307320
}
308321

309-
def json_body(self):
310-
"""Return JSON body."""
322+
def has_form(self):
323+
"""
324+
Return whether operation has form data.
325+
326+
Return `True` if the operation has form data parameters.
327+
328+
:return bool: True if has form data
329+
"""
330+
return [x for x in self.parameters if x.location == "form"] != []
331+
332+
def body(self):
333+
"""Return request body."""
311334
accepts = CONTENT_MAP.get(self.accepts)
312335
return {
313336
"description": self.description,
314-
"content": {accepts: {"schema": {"type": "object", "properties": self.body}}},
337+
"content": {accepts: {"schema": {"type": "object", "properties": self.request_body_parameters}}},
315338
}
316339

317340
def request_body(self):
318341
"""Return request body."""
319-
if self.accepts == "binary":
342+
if self.accepts in ["binary"]:
320343
return self.binary_body()
321344

322-
return self.json_body()
345+
return self.body()
323346

324347
def dict(self):
325348
"""Return dict of data."""
@@ -331,7 +354,7 @@ def dict(self):
331354
"parameters": [x.dict() for x in self.parameters if not x.in_request_body()],
332355
"responses": {x.code: x.dict() for x in self.responses},
333356
}
334-
if self.body:
357+
if self.request_body_parameters:
335358
operation["requestBody"] = self.request_body()
336359

337360
return {self.operation: operation}

src/reliqua/sphinx_parser.py

+3-3
Original file line numberDiff line numberDiff line change
@@ -14,10 +14,10 @@
1414
r"^(:param.*?:.*?)(?:(?=:param)|(?=:return)|(?=:response)|(?=:accepts))",
1515
re.MULTILINE | re.DOTALL,
1616
)
17-
RESPONSE_ITER_REGEX = re.compile(r":response\s+(?P<code>\d+)\s*(?P<schema>\w+)?:\s+(?P<description>\w+)")
18-
RETURN_REGEX = re.compile(r"return\s+(\w+):|:return:\s+(\w+)")
17+
RESPONSE_ITER_REGEX = re.compile(r":response\s+(?P<code>\d+)\s*(?P<schema>\w+)?:\s+(?P<description>.*)")
18+
RETURN_REGEX = re.compile(r"return\s+(\w+):|:return:\s+(.*)")
1919
ACCEPT_REGEX = re.compile(r"accepts\s+(\w+):|:accepts:\s+(\w+)")
20-
KEYVALUE_REGEX = re.compile(r"(?P<key>\w+)=(?P<value>\w+)")
20+
KEYVALUE_REGEX = re.compile(r"(?P<key>\w+)=(?P<value>\S+)")
2121
OPERATION_REGEX = re.compile(r"on_(delete|get|patch|post|put)")
2222
SUFFIX_REGEX = re.compile(r"on_(?:delete|get|patch|post|put)_([a-zA-Z0-9_]+)")
2323

0 commit comments

Comments
 (0)