mirror of
https://gitlab.archlinux.org/archlinux/aurweb.git
synced 2025-02-03 10:43:03 +01:00
change(fastapi): rework /rpc (get)
This reworks the base implementation of the RPC to use a class called RPC for handling of requests. Took a bit of a different approach than PHP in terms of exposed methods, but it does end up achieving the same goal, with one additional error: "Request type '{type}' is not yet implemented." For FastAPI development, we'll stick with: - If the supplied 'type' argument has an alias mapping in RPC.ALIASES, we convert the type argument over to its alias before doing anything. Example: 'info' is aliased to 'multiinfo', so when a user requests type=info, it is converted to type=multiinfo. - If the type does not exist in RPC.EXPOSED_TYPES, the following error is produced: "No request type/data specified." - If the type **does** exist in RPC.EXPOSED_TYPES, but does not have an implemented `RPC._handle_{type}_type` function, the following error is produced: "Request type '{type}' is not yet implemented." Signed-off-by: Kevin Morris <kevr@0cost.org>
This commit is contained in:
parent
30ab45f459
commit
7c4fb539d8
3 changed files with 227 additions and 255 deletions
|
@ -1,48 +1,57 @@
|
|||
from typing import List, Optional
|
||||
from urllib.parse import unquote
|
||||
|
||||
from fastapi import APIRouter, Query, Request
|
||||
from fastapi.responses import JSONResponse
|
||||
|
||||
from aurweb.rpc import RPC
|
||||
|
||||
router = APIRouter()
|
||||
|
||||
|
||||
def arg_legacy_gen(request):
|
||||
# '[]' characters in the path randomly kept getting transformed to (what
|
||||
# appears to be) their HTML-formatted variants, so we keep that behavior
|
||||
# just in case.
|
||||
arguments = request.url.query.replace("%5B%5D", "[]").split("&")
|
||||
arguments.reverse()
|
||||
def parse_args(request: Request):
|
||||
""" Handle legacy logic of 'arg' and 'arg[]' query parameter handling.
|
||||
|
||||
temp_args = []
|
||||
When 'arg' appears as the last argument given to the query string,
|
||||
that argument is used by itself as one single argument, regardless
|
||||
of any more 'arg' or 'arg[]' parameters supplied before it.
|
||||
|
||||
for i in arguments:
|
||||
# We only want to deal with 'arg' and 'arg[]' strings, so only take those.
|
||||
if i.split("=")[0] in ("arg", "arg[]"):
|
||||
temp_args += [i]
|
||||
When 'arg[]' appears as the last argument given to the query string,
|
||||
we iterate from last to first and build a list of arguments until
|
||||
we hit an 'arg'.
|
||||
|
||||
returned_arguments = []
|
||||
argument_bracketed = False
|
||||
TODO: This handling should be addressed in v6 of the RPC API. This
|
||||
was most likely a bi-product of legacy handling of versions 1-4
|
||||
which we no longer support.
|
||||
|
||||
for i in temp_args:
|
||||
# Split argument on first occurance of '='.
|
||||
current_argument = i.split("=")
|
||||
:param request: FastAPI request
|
||||
:returns: List of deduced arguments
|
||||
"""
|
||||
# Create a list of (key, value) pairs of the given 'arg' and 'arg[]'
|
||||
# query parameters from last to first.
|
||||
query = list(reversed(unquote(request.url.query).split("&")))
|
||||
parts = [
|
||||
e.split("=", 1) for e in query if e.startswith(("arg=", "arg[]="))
|
||||
]
|
||||
|
||||
argument_name = current_argument[0]
|
||||
argument_value = "".join(current_argument[1:])
|
||||
args = []
|
||||
if parts:
|
||||
# If we found 'arg' and/or 'arg[]' arguments, we begin processing
|
||||
# the set of arguments depending on the last key found.
|
||||
last = parts[0][0]
|
||||
|
||||
# Process argument.
|
||||
if argument_name == "arg[]":
|
||||
returned_arguments += [argument_value]
|
||||
argument_bracketed = True
|
||||
if last == "arg":
|
||||
# If the last key was 'arg', then it is our sole argument.
|
||||
args.append(parts[0][1])
|
||||
else:
|
||||
# Otherwise, it must be 'arg[]', so traverse backward
|
||||
# until we reach a non-'arg[]' key.
|
||||
for key, value in parts:
|
||||
if key != last:
|
||||
break
|
||||
args.append(value)
|
||||
|
||||
elif argument_name == "arg":
|
||||
# Only set this argument if 'arg[]' hasen't previously been found.
|
||||
if not argument_bracketed:
|
||||
returned_arguments = [argument_value]
|
||||
break
|
||||
|
||||
return returned_arguments
|
||||
return args
|
||||
|
||||
|
||||
@router.get("/rpc")
|
||||
|
@ -51,51 +60,7 @@ async def rpc(request: Request,
|
|||
type: Optional[str] = Query(None),
|
||||
arg: Optional[str] = Query(None),
|
||||
args: Optional[List[str]] = Query(None, alias="arg[]")):
|
||||
# Defaults for returned data
|
||||
returned_data = {}
|
||||
|
||||
returned_data["version"] = v
|
||||
returned_data["results"] = []
|
||||
returned_data["resultcount"] = 0
|
||||
|
||||
# Default the type field to "error", until we determine that
|
||||
# we're not erroneous (below).
|
||||
returned_data["type"] = "error"
|
||||
|
||||
# Ensure valid version was passed
|
||||
if v is None:
|
||||
returned_data["error"] = "Please specify an API version."
|
||||
return returned_data
|
||||
elif v != 5:
|
||||
returned_data["error"] = "Invalid version specified."
|
||||
return returned_data
|
||||
else:
|
||||
# We got past initial error cases; set the type to what
|
||||
# the user gave us.
|
||||
returned_data["type"] = type
|
||||
|
||||
# Take arguments from either 'args' or 'args[]' and put them into 'argument_list'.
|
||||
argument_list = []
|
||||
|
||||
# In the PHP implementation, aurweb uses the last 'arg' value or all the
|
||||
# last 'arg[]' values when both 'arg' and 'arg[]' are part of the query
|
||||
# request. We thus preserve that behavior here for legacy purposes.
|
||||
if arg is not None and args is not None:
|
||||
argument_list = arg_legacy_gen(request)
|
||||
elif arg is not None:
|
||||
argument_list = [arg]
|
||||
elif args is not None:
|
||||
argument_list = args
|
||||
else:
|
||||
# Abort because no package arguments were passed.
|
||||
returned_data["type"] = "error"
|
||||
returned_data["error"] = "No request type/data specified."
|
||||
return returned_data
|
||||
|
||||
# Process and return data
|
||||
returned_data = RPC(v=v,
|
||||
type=type,
|
||||
argument_list=argument_list,
|
||||
returned_data=returned_data)
|
||||
|
||||
return returned_data
|
||||
# Prepare output list of arguments.
|
||||
arguments = parse_args(request)
|
||||
return JSONResponse(RPC().handle(v=v, type=type, args=arguments))
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue