change(rpc): handle 'version' and 'type' arguments in constructor

Additionally, added RPC.error, which produces an RPC-compatible
error based on the version passed during construction.

Signed-off-by: Kevin Morris <kevr@0cost.org>
This commit is contained in:
Kevin Morris 2021-10-21 10:13:25 -07:00
parent a06f4ec19c
commit 6662975005
No known key found for this signature in database
GPG key ID: F7E46DED420788F3
2 changed files with 35 additions and 23 deletions

View file

@ -61,6 +61,10 @@ async def rpc(request: Request,
arg: Optional[str] = Query(None), arg: Optional[str] = Query(None),
args: Optional[List[str]] = Query(None, alias="arg[]")): args: Optional[List[str]] = Query(None, alias="arg[]")):
# Prepare output list of arguments. # Create a handle to our RPC class.
rpc = RPC(version=v, type=type)
# Prepare list of arguments for input. If 'arg' was given, it'll
# be a list with one element.
arguments = parse_args(request) arguments = parse_args(request)
return JSONResponse(RPC().handle(v=v, type=type, args=arguments)) return JSONResponse(rpc.handle(arguments))

View file

@ -63,23 +63,37 @@ class RPC:
# A mapping of aliases. # A mapping of aliases.
ALIASES = {"info": "multiinfo"} ALIASES = {"info": "multiinfo"}
def _verify_inputs(self, v: int, type: str, args: List[str] = []): def __init__(self, version: int = 0, type: str = None):
if v is None: self.version = version
self.type = type
def error(self, message: str) -> dict:
return {
"version": self.version,
"results": [],
"resultcount": 0,
"type": "error",
"error": message
}
def _verify_inputs(self, args: List[str] = []):
if self.version is None:
raise RPCError("Please specify an API version.") raise RPCError("Please specify an API version.")
if v not in RPC.EXPOSED_VERSIONS: if self.version not in RPC.EXPOSED_VERSIONS:
raise RPCError("Invalid version specified.") raise RPCError("Invalid version specified.")
if type is None or not len(args): if self.type is None or not len(args):
raise RPCError("No request type/data specified.") raise RPCError("No request type/data specified.")
if type not in RPC.EXPOSED_TYPES: if self.type not in RPC.EXPOSED_TYPES:
raise RPCError("Incorrect request type specified.") raise RPCError("Incorrect request type specified.")
try: try:
getattr(self, f"_handle_{type.replace('-', '_')}_type") getattr(self, f"_handle_{self.type.replace('-', '_')}_type")
except AttributeError: except AttributeError:
raise RPCError(f"Request type '{type}' is not yet implemented.") raise RPCError(
f"Request type '{self.type}' is not yet implemented.")
def _get_json_data(self, package: models.Package): def _get_json_data(self, package: models.Package):
""" Produce dictionary data of one Package that can be JSON-serialized. """ Produce dictionary data of one Package that can be JSON-serialized.
@ -164,7 +178,7 @@ class RPC:
).order_by(models.PackageBase.Name.asc()).limit(20) ).order_by(models.PackageBase.Name.asc()).limit(20)
return [record.Name for record in records] return [record.Name for record in records]
def handle(self, v: int = 0, type: str = None, args: List[str] = []): def handle(self, args: List[str] = []):
""" Request entrypoint. A router should pass v, type and args """ Request entrypoint. A router should pass v, type and args
to this function and expect an output dictionary to be returned. to this function and expect an output dictionary to be returned.
@ -173,32 +187,26 @@ class RPC:
:param args: Deciphered list of arguments based on arg/arg[] inputs :param args: Deciphered list of arguments based on arg/arg[] inputs
""" """
# Convert type aliased types. # Convert type aliased types.
if type in RPC.ALIASES: if self.type in RPC.ALIASES:
type = RPC.ALIASES.get(type) self.type = RPC.ALIASES.get(self.type)
# Prepare our output data dictionary with some basic keys. # Prepare our output data dictionary with some basic keys.
data = {"version": v, "type": type} data = {"version": self.version, "type": self.type}
# Run some verification on our given arguments. # Run some verification on our given arguments.
try: try:
self._verify_inputs(v, type, args) self._verify_inputs(args)
except RPCError as exc: except RPCError as exc:
data.update({ return self.error(str(exc))
"results": [],
"resultcount": 0,
"type": "error",
"error": str(exc)
})
return data
# Get a handle to our callback and trap an RPCError with # Get a handle to our callback and trap an RPCError with
# an empty list of results based on callback's execution. # an empty list of results based on callback's execution.
callback = getattr(self, f"_handle_{type.replace('-', '_')}_type") callback = getattr(self, f"_handle_{self.type.replace('-', '_')}_type")
results = callback(args) results = callback(args)
# These types are special: we produce a different kind of # These types are special: we produce a different kind of
# successful JSON output: a list of results. # successful JSON output: a list of results.
if type in ("suggest", "suggest-pkgbase"): if self.type in ("suggest", "suggest-pkgbase"):
return results return results
# Return JSON output. # Return JSON output.