Skip to content

Commit

Permalink
Add Support for customizing how nested names are generated (0b0100100…
Browse files Browse the repository at this point in the history
…1#305)

🔀 merge(types, spec, utils): add support for nested model reference names
✨ feat(types.py): add NestedNamingStrategy type alias
✨ feat(spec.py): add nested_naming_strategy parameter to SpecTree constructor and get_model_schema method
✨ feat(utils.py): add get_nested_key function to generate nested model reference names
The changes add support for nested model reference names in Spectree. A new type alias, NestedNamingStrategy, is added to _types.py. SpecTree constructor and get_model_schema method in spec.py now accept a new parameter, nested_naming_strategy, which is used to generate nested model reference names. get_nested_key function is added to utils.py to generate nested model reference names.
  • Loading branch information
AndreasBBS authored Apr 24, 2023
1 parent 3d5f31a commit 69fe8e0
Show file tree
Hide file tree
Showing 3 changed files with 33 additions and 8 deletions.
1 change: 1 addition & 0 deletions spectree/_types.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
ModelType = Type[BaseModel]
OptionalModelType = Optional[ModelType]
NamingStrategy = Callable[[ModelType], str]
NestedNamingStrategy = Callable[[str, str], str]


class MultiDict(Protocol):
Expand Down
15 changes: 12 additions & 3 deletions spectree/spec.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@
get_type_hints,
)

from ._types import FunctionDecorator, ModelType, NamingStrategy
from ._types import FunctionDecorator, ModelType, NamingStrategy, NestedNamingStrategy
from .config import Configuration, ModeEnum
from .models import Tag, ValidationError
from .plugins import PLUGINS, BasePlugin
Expand All @@ -23,6 +23,7 @@
default_before_handler,
get_model_key,
get_model_schema,
get_nested_key,
get_security,
parse_comments,
parse_name,
Expand Down Expand Up @@ -65,9 +66,11 @@ def __init__(
validation_error_status: int = 422,
validation_error_model: Optional[ModelType] = None,
naming_strategy: NamingStrategy = get_model_key,
nested_naming_strategy: NestedNamingStrategy = get_nested_key,
**kwargs: Any,
):
self.naming_strategy = naming_strategy
self.nested_naming_strategy = nested_naming_strategy
self.before = before
self.after = after
self.validation_error_status = validation_error_status
Expand Down Expand Up @@ -261,7 +264,11 @@ def _add_model(self, model: ModelType) -> str:

model_key = self.naming_strategy(model)
self.models[model_key] = deepcopy(
get_model_schema(model=model, naming_strategy=self.naming_strategy)
get_model_schema(
model=model,
naming_strategy=self.naming_strategy,
nested_naming_strategy=self.nested_naming_strategy,
)
)

return model_key
Expand Down Expand Up @@ -350,7 +357,9 @@ def _get_model_definitions(self) -> Dict[str, Any]:
for name, schema in self.models.items():
if "definitions" in schema:
for key, value in schema["definitions"].items():
definitions[f"{name}.{key}"] = value
composed_key = self.nested_naming_strategy(name, key)
if composed_key not in definitions:
definitions[composed_key] = value
del schema["definitions"]

return definitions
25 changes: 20 additions & 5 deletions spectree/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@

from pydantic import BaseModel, ValidationError

from ._types import ModelType, MultiDict, NamingStrategy
from ._types import ModelType, MultiDict, NamingStrategy, NestedNamingStrategy

# parse HTTP status code to get the code
HTTP_CODE = re.compile(r"^HTTP_(?P<code>\d{3})$")
Expand Down Expand Up @@ -229,7 +229,22 @@ def get_model_key(model: ModelType) -> str:
return f"{model.__name__}.{hash_module_path(module_path=model.__module__)}"


def get_model_schema(model: ModelType, naming_strategy: NamingStrategy = get_model_key):
def get_nested_key(parent: str, child: str) -> str:
"""
generate nested model reference name suffixed by parent model name
:param parent: string of parent name
:param child: string of child name
"""

return f"{parent}.{child}"


def get_model_schema(
model: ModelType,
naming_strategy: NamingStrategy = get_model_key,
nested_naming_strategy: NestedNamingStrategy = get_nested_key,
):
"""
return a dictionary representing the model as JSON Schema with a hashed
infix in ref to ensure name uniqueness
Expand All @@ -239,9 +254,9 @@ def get_model_schema(model: ModelType, naming_strategy: NamingStrategy = get_mod
"""
assert issubclass(model, BaseModel)

return model.schema(
ref_template=f"#/components/schemas/{naming_strategy(model)}.{{model}}"
)
nested_key = nested_naming_strategy(naming_strategy(model), "{model}")

return model.schema(ref_template=f"#/components/schemas/{nested_key}")


def get_security(security: Union[None, Mapping, Sequence[Any]]) -> List[Any]:
Expand Down

0 comments on commit 69fe8e0

Please sign in to comment.