Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

ref: Improve maintainability for baked request and response #729

Open
wants to merge 1 commit into
base: dev
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
38 changes: 25 additions & 13 deletions linodecli/baked/request.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,13 @@
Request details for a CLI Operation
"""

from typing import List, Optional

from openapi3.paths import MediaType
from openapi3.schemas import Schema

from linodecli.baked.parsing import simplify_description
from linodecli.baked.response import OpenAPIResponse
from linodecli.baked.util import _aggregate_schema_properties


Expand All @@ -13,16 +17,16 @@ class OpenAPIRequestArg:
A single argument to a request as defined by a Schema in the OpenAPI spec
"""

def __init__(
def __init__( # pylint: disable=too-many-arguments
self,
name,
schema,
required,
prefix=None,
is_parent=False,
parent=None,
depth=0,
): # pylint: disable=too-many-arguments
name: str,
schema: Schema,
required: bool,
prefix: Optional[str] = None,
is_parent: bool = False,
parent: Optional[str] = None,
depth: int = 0,
) -> None:
"""
Parses a single Schema node into a argument the CLI can use when making
requests.
Expand Down Expand Up @@ -120,9 +124,14 @@ def __init__(
)


def _parse_request_model(schema, prefix=None, parent=None, depth=0):
def _parse_request_model(
schema: Schema,
prefix: Optional[str] = None,
parent: Optional[str] = None,
depth: int = 0,
) -> List[OpenAPIRequestArg]:
"""
Parses a schema into a list of OpenAPIRequest objects
Parses an OpenAPI schema into a list of OpenAPIRequest objects
:param schema: The schema to parse as a request model
:type schema: openapi3.Schema
:param prefix: The prefix to add to all keys in this schema, as a json path
Expand All @@ -143,6 +152,7 @@ def _parse_request_model(schema, prefix=None, parent=None, depth=0):
return args

for k, v in properties.items():
# Handle nested objects which aren't read-only and have properties
if (
v.type == "object"
and not v.readOnly
Expand All @@ -159,6 +169,8 @@ def _parse_request_model(schema, prefix=None, parent=None, depth=0):
# parent arguments.
depth=depth,
)

# Handle arrays of objects that not marked as JSON
elif (
v.type == "array"
and v.items
Expand Down Expand Up @@ -209,7 +221,7 @@ class OpenAPIRequest:
on the MediaType object of a requestBody portion of an OpenAPI Operation
"""

def __init__(self, request):
def __init__(self, request: MediaType) -> None:
"""
:param request: The request's MediaType object in the OpenAPI spec,
corresponding to the application/json data the endpoint
Expand Down Expand Up @@ -256,7 +268,7 @@ class OpenAPIFilteringRequest:
endpoints where filters are accepted.
"""

def __init__(self, response_model):
def __init__(self, response_model: OpenAPIResponse) -> None:
"""
:param response_model: The parsed response model whose properties may be
filterable.
Expand Down
28 changes: 25 additions & 3 deletions linodecli/baked/response.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,10 @@
Converting the processed OpenAPI Responses into something the CLI can work with
"""

from typing import Optional

from openapi3.paths import MediaType
from openapi3.schemas import Schema

from linodecli.baked.util import _aggregate_schema_properties

Expand Down Expand Up @@ -30,7 +33,13 @@ class OpenAPIResponseAttr:
from it.
"""

def __init__(self, name, schema, prefix=None, nested_list_depth=0):
def __init__(
self,
name: str,
schema: Schema,
prefix: Optional[str] = None,
nested_list_depth: int = 0,
) -> None:
"""
:param name: The key that held this schema in the properties list, representing
its name in a response.
Expand Down Expand Up @@ -84,10 +93,13 @@ def __init__(self, name, schema, prefix=None, nested_list_depth=0):
self.item_type = schema.items.type

@property
def path(self):
def path(self) -> str:
"""
This is a helper for filterable fields to return the json path to this
element in a response.

:returns: The json path to the element in a response.
:rtype: str
"""
return self.name

Expand Down Expand Up @@ -129,6 +141,7 @@ def render_value(self, model, colorize=True):
value = str(value)
color = self.color_map.get(value) or self.color_map["default_"]
value = f"[{color}]{value}[/]"
# Convert None value to an empty string for better display
if value is None:
# Prints the word None if you don't change it
value = ""
Expand Down Expand Up @@ -194,12 +207,14 @@ def _parse_response_model(schema, prefix=None, nested_list_depth=0):
elif v.type == "object":
attrs += _parse_response_model(v, prefix=pref)
elif v.type == "array" and v.items.type == "object":
# Parse arrays for objects recursively and increase the nesting depth
attrs += _parse_response_model(
v.items,
prefix=pref,
nested_list_depth=nested_list_depth + 1,
)
else:
# Handle any other simple types
attrs.append(
OpenAPIResponseAttr(
k, v, prefix=prefix, nested_list_depth=nested_list_depth
Expand All @@ -215,7 +230,7 @@ class OpenAPIResponse:
responses section of an OpenAPI Operation
"""

def __init__(self, response: MediaType):
def __init__(self, response: MediaType) -> None:
"""
:param response: The response's MediaType object in the OpenAPI spec,
corresponding to the application/json response type
Expand Down Expand Up @@ -287,15 +302,22 @@ def _fix_nested_list(self, json):

nested_lists = [c.strip() for c in self.nested_list.split(",")]
result = []

for nested_list in nested_lists:
path_parts = nested_list.split(".")

if not isinstance(json, list):
json = [json]

for cur in json:
# Get the nested list using the path
nlist_path = cur
for p in path_parts:
nlist_path = nlist_path.get(p)
nlist = nlist_path

# For each item in the nested list,
# combine the parent properties with the nested item
for item in nlist:
cobj = {k: v for k, v in cur.items() if k != path_parts[0]}
cobj["_split"] = path_parts[-1]
Expand Down