Skip to content
This repository has been archived by the owner on Nov 19, 2023. It is now read-only.

Commit

Permalink
hotfix 1.3.1
Browse files Browse the repository at this point in the history
  • Loading branch information
Na'aman Hirschfeld authored and Na'aman Hirschfeld committed Feb 21, 2021
1 parent c1b7b40 commit 81a8479
Show file tree
Hide file tree
Showing 3 changed files with 28 additions and 17 deletions.
5 changes: 5 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,10 @@
# Changelog


## v1.3.1 2020-02-20

* Hotfix regression due to pk resolution logic

## v1.3.0 2020-02-20

* Added validators for the "format" keyword, handling the following format values generated by DRF and DRF derived libraries: "uuid", "base64", "email", "uri", "url", "ipv4", "ipv6" and "time"
Expand Down
38 changes: 22 additions & 16 deletions openapi_tester/loaders.py
Original file line number Diff line number Diff line change
Expand Up @@ -82,8 +82,11 @@ def de_reference_schema(self, schema: dict) -> dict:
def normalize_schema_paths(self, schema: dict) -> Dict[str, dict]:
normalized_paths: Dict[str, dict] = {}
for key, value in schema["paths"].items():
parameterized_path, _ = self.resolve_path(endpoint_path=key, method=list(value.keys())[0])
normalized_paths[parameterized_path] = value
try:
parameterized_path, _ = self.resolve_path(endpoint_path=key, method=list(value.keys())[0])
normalized_paths[parameterized_path] = value
except ValueError:
normalized_paths[key] = value
return {**schema, "paths": normalized_paths}

@staticmethod
Expand All @@ -110,7 +113,9 @@ def endpoints(self) -> List[str]: # pylint: disable=no-self-use
return list({endpoint[0] for endpoint in EndpointEnumerator().get_api_endpoints()})

def resolve_path(self, endpoint_path: str, method: str) -> Tuple[str, ResolverMatch]:
""" Resolves a Django path. """
"""
Resolves a Django path.
"""
url_object = urlparse(endpoint_path)
parsed_path = url_object.path if url_object.path.endswith("/") else url_object.path + "/"
if not parsed_path.startswith("/"):
Expand All @@ -124,32 +129,30 @@ def resolve_path(self, endpoint_path: str, method: str) -> Tuple[str, ResolverMa
except Resolver404:
continue
else:
for key, value in resolved_route.kwargs.items():
index = path.rfind(str(value))
path = f"{path[:index]}{{{key}}}{path[index + len(str(value)) :]}"
if "{pk}" in path and api_settings.SCHEMA_COERCE_PATH_PK:
path, resolved_route = self.handle_pk_parameter(
resolved_route=resolved_route, path=path, method=method
)
for key, value in resolved_route.kwargs.items():
# Replacing kwarg values back into the string seems to be the simplest way of bypassing complex
# regex handling. However, its important not to freely use the .replace() function, as a
# {value} of `1` would also cause the `1` in api/v1/ to be replaced
var_index = path.rfind(str(value))
path = path[:var_index] + f"{{{key}}}" + path[var_index + len(str(value)) :]
return path, resolved_route
message = f"Could not resolve path `{endpoint_path}`."
closest_matches = "".join(f"\n- {i}" for i in difflib.get_close_matches(endpoint_path, self.endpoints))
if closest_matches:
message += f"\n\nDid you mean one of these?{closest_matches}"
close_matches = difflib.get_close_matches(endpoint_path, self.endpoints)
if close_matches:
message += "\n\nDid you mean one of these?" + "\n- ".join(close_matches)
raise ValueError(message)

@staticmethod
def handle_pk_parameter(resolved_route: ResolverMatch, path: str, method: str) -> Tuple[str, ResolverMatch]:
def handle_pk_parameter( # pylint: disable=no-self-use
self, resolved_route: ResolverMatch, path: str, method: str
) -> Tuple[str, ResolverMatch]:
split_view_path = resolved_route.view_name.split(".")
view_name = split_view_path.pop()
imported_module = import_module(".".join(split_view_path))
view = getattr(imported_module, view_name)
coerced_path = BaseSchemaGenerator().coerce_path(path=path, method=method, view=view)
pk_field_name = "".join(
entry.replace("+ ", "") for entry in difflib.Differ().compare(path, coerced_path) if "+ " in entry
list([entry.replace("+ ", "") for entry in difflib.Differ().compare(path, coerced_path) if "+ " in entry])
)
resolved_route.kwargs[pk_field_name] = resolved_route.kwargs["pk"]
del resolved_route.kwargs["pk"]
Expand Down Expand Up @@ -203,7 +206,10 @@ def resolve_path(self, endpoint_path: str, method: str) -> Tuple[str, ResolverMa
from drf_spectacular.settings import spectacular_settings

de_parameterized_path, resolved_path = super().resolve_path(endpoint_path=endpoint_path, method=method)
return de_parameterized_path[len(spectacular_settings.SCHEMA_PATH_PREFIX) :], resolved_path
return (
de_parameterized_path[len(spectacular_settings.SCHEMA_PATH_PREFIX) :],
resolved_path,
)


class StaticSchemaLoader(BaseSchemaLoader):
Expand Down
2 changes: 1 addition & 1 deletion pyproject.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[tool.poetry]
name = "drf-openapi-tester"
version = "1.3.0"
version = "1.3.1"
description = "Django test utility for validating OpenAPI response documentation"
authors = ["Sondre Lillebø Gundersen <[email protected]>", "Na'aman Hirschfeld <[email protected]>"]
maintainers = ["Na'aman Hirschfeld <[email protected]>"]
Expand Down

0 comments on commit 81a8479

Please sign in to comment.