Skip to content

Commit

Permalink
add tests and slight improvement to json path detection
Browse files Browse the repository at this point in the history
  • Loading branch information
sh-rp committed May 3, 2024
1 parent 3fa3362 commit 958d9a3
Show file tree
Hide file tree
Showing 4 changed files with 179 additions and 12 deletions.
3 changes: 1 addition & 2 deletions openapi_python_client/parser/const.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,8 +14,7 @@
RE_TOTAL_PROPERTY = re.compile(r"(?i)(total|count)", re.IGNORECASE)
RE_CURSOR_PARAM = re.compile(r"(?i)(cursor|after|since)", re.IGNORECASE)
RE_NEXT_PROPERTY = re.compile(r"(?i)(next|next_url|more)", re.IGNORECASE)


RE_MATCH_ALL = re.compile(r".*", re.IGNORECASE)
# content path discovery


Expand Down
20 changes: 10 additions & 10 deletions openapi_python_client/parser/endpoints.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
from openapi_python_client.parser.credentials import CredentialsProperty
from openapi_python_client.parser.pagination import Pagination
from openapi_python_client.parser.parameters import Parameter
from .const import RE_MATCH_ALL

TMethod = Literal["GET", "POST", "PUT", "PATCH"]
Tree = Dict[str, Union["Endpoint", "Tree"]]
Expand Down Expand Up @@ -90,25 +91,24 @@ def from_reference(

# try to discover payload path and schema
if payload_schema:
payload_path = []
payload_path: List[str] = []

if expect_list:
# TODO: improve heuristics and move into some utility function for testing
if not payload_schema.is_list:
for prop in payload_schema.properties:
if prop.schema.is_list:
payload_path.append(prop.name)
payload_schema = prop.schema
break
else:
# TODO: this needs improvement
if payload_schema.is_list:
payload = DataPropertyPath(tuple(payload_path), payload_schema)
else:
payload = payload_schema.crawled_properties.find_property(RE_MATCH_ALL, "array")

# either no list expected or no list found..
if not payload:
while len(payload_schema.properties) == 1 and payload_schema.properties[0].is_object:
# Schema contains only a single object property. The payload is inside
prop = payload_schema.properties[0]
payload_path.append(prop.name)
payload_schema = prop.schema

payload = DataPropertyPath(tuple(payload_path), payload_schema)
payload = DataPropertyPath(tuple(payload_path), payload_schema)

return cls(
status_code=status_code,
Expand Down
128 changes: 128 additions & 0 deletions tests/cases/test_specs/content_path_specs.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,128 @@
openapi: 3.0.0
info:
title: 'pagination'
version: 1.0.0
description: 'different content path'
servers:
- url: 'https://pokeapi.co/'

paths:

/unnested_collection_result/:
get:
operationId: unnested_collection_result
responses:
'200':
description: "OK"
content:
application/json:
schema:
type: array
items:
type: object
properties:
id:
type: integer
example: 3

/results_collection_json_path/:
get:
operationId: results_collection_json_path
responses:
'200':
description: "OK"
content:
application/json:
schema:
type: object
properties:
count:
type: integer
example: 3
next:
type: string
nullable: true
example: https://pokeapi.co/api/v2/pokemon/?offset=20&limit=20
previous:
type: string
nullable: true
results:
type: array

/nested_results_collection_json_path/:
get:
operationId: nested_results_collection_json_path
responses:
'200':
description: "OK"
content:
application/json:
schema:
type: object
properties:
count:
type: integer
example: 3
next:
type: string
nullable: true
example: https://pokeapi.co/api/v2/pokemon/?offset=20&limit=20
previous:
type: string
nullable: true
content:
type: object
properties:
count:
type: integer
example: 3
results:
type: array


/single_object_unnested/:
get:
operationId: single_object_unnested
responses:
'200':
description: "OK"
content:
application/json:
schema:
type: object
properties:
id:
type: integer
example: 3
name:
type: string
nullable: true
address:
type: string
nullable: true



/single_object_nested/:
get:
operationId: single_object_nested
responses:
'200':
description: "OK"
content:
application/json:
schema:
type: object
properties:
result_object:
type: object
properties:
id:
type: integer
example: 3
name:
type: string
nullable: true
address:
type: string
nullable: true
40 changes: 40 additions & 0 deletions tests/e2e/test_json_paths.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
from typing import Dict, Any

import pytest

from tests.e2e.utils import get_source_from_open_api, get_dict_from_open_api
from tests.cases import get_test_case_path


@pytest.fixture(scope="module")
def paginators() -> Dict[str, str]:
case_path = get_test_case_path("content_path_specs.yml")
# validate that source will work
get_source_from_open_api(case_path)

# get dict and save paginator info into dict
rendered_dict = get_dict_from_open_api(case_path)
return {
entry["name"]: entry.get("endpoint").get("data_selector") # type: ignore
for entry in rendered_dict["resources"] # type: ignore
}


def test_unnested_collection_result(paginators: Dict[str, Any]) -> None:
assert paginators["unnested_collection_result"] == "$"


def test_results_collection_json_path(paginators: Dict[str, Any]) -> None:
assert paginators["results_collection_json_path"] == "results"


def test_nested_results_collection_json_path(paginators: Dict[str, Any]) -> None:
assert paginators["nested_results_collection_json_path"] == "content.results"


def test_single_object_unneested(paginators: Dict[str, Any]) -> None:
assert paginators["single_object_unnested"] == "$"


def test_single_object_nested(paginators: Dict[str, Any]) -> None:
assert paginators["single_object_nested"] == "result_object"

0 comments on commit 958d9a3

Please sign in to comment.