mirror of
https://github.com/hwchase17/langchain
synced 2024-10-29 17:07:25 +00:00
f240651bd8
This still doesn't handle the following - non-JSON media types - anyOf, allOf, oneOf's And doesn't emit the typescript definitions for referred types yet, but that can be saved for a separate PR. Also, we could have better support for Swagger 2.0 specs and OpenAPI 3.0.3 (can use the same lib for the latter) recommend offline conversion for now.
197 lines
5.9 KiB
Python
197 lines
5.9 KiB
Python
"""Test the APIOperation class."""
|
|
import json
|
|
import os
|
|
from pathlib import Path
|
|
from typing import Iterable, List, Tuple
|
|
|
|
import pytest
|
|
import yaml
|
|
from openapi_schema_pydantic import (
|
|
Components,
|
|
Info,
|
|
MediaType,
|
|
Reference,
|
|
RequestBody,
|
|
Schema,
|
|
)
|
|
|
|
from langchain.tools.openapi.utils.api_models import (
|
|
APIOperation,
|
|
APIRequestBody,
|
|
APIRequestBodyProperty,
|
|
)
|
|
from langchain.tools.openapi.utils.openapi_utils import HTTPVerb, OpenAPISpec
|
|
|
|
_DIR = Path(__file__).parent
|
|
|
|
|
|
def _get_test_specs() -> Iterable[Path]:
|
|
"""Walk the test_specs directory and collect all files with the name 'apispec'
|
|
in them.
|
|
"""
|
|
test_specs_dir = _DIR / "test_specs"
|
|
return (
|
|
Path(root) / file
|
|
for root, _, files in os.walk(test_specs_dir)
|
|
for file in files
|
|
if file.startswith("apispec")
|
|
)
|
|
|
|
|
|
def _get_paths_and_methods_from_spec_dictionary(
|
|
spec: dict,
|
|
) -> Iterable[Tuple[str, str]]:
|
|
"""Return a tuple (paths, methods) for every path in spec."""
|
|
valid_methods = [verb.value for verb in HTTPVerb]
|
|
for path_name, path_item in spec["paths"].items():
|
|
for method in valid_methods:
|
|
if method in path_item:
|
|
yield (path_name, method)
|
|
|
|
|
|
def http_paths_and_methods() -> List[Tuple[str, OpenAPISpec, str, str]]:
|
|
"""Return a args for every method in cached OpenAPI spec in test_specs."""
|
|
http_paths_and_methods = []
|
|
for test_spec in _get_test_specs():
|
|
spec_name = test_spec.parent.name
|
|
if test_spec.suffix == ".json":
|
|
with test_spec.open("r") as f:
|
|
spec = json.load(f)
|
|
else:
|
|
with test_spec.open("r") as f:
|
|
spec = yaml.safe_load(f.read())
|
|
parsed_spec = OpenAPISpec.from_file(test_spec)
|
|
for path, method in _get_paths_and_methods_from_spec_dictionary(spec):
|
|
http_paths_and_methods.append(
|
|
(
|
|
spec_name,
|
|
parsed_spec,
|
|
path,
|
|
method,
|
|
)
|
|
)
|
|
return http_paths_and_methods
|
|
|
|
|
|
@pytest.mark.parametrize(
|
|
"spec_name, spec, path, method",
|
|
http_paths_and_methods(),
|
|
)
|
|
def test_parse_api_operations(
|
|
spec_name: str, spec: OpenAPISpec, path: str, method: str
|
|
) -> None:
|
|
"""Test the APIOperation class."""
|
|
try:
|
|
APIOperation.from_openapi_spec(spec, path, method)
|
|
except Exception as e:
|
|
raise AssertionError(f"Error processong {spec_name}: {e} ") from e
|
|
|
|
|
|
@pytest.fixture
|
|
def raw_spec() -> OpenAPISpec:
|
|
"""Return a raw OpenAPI spec."""
|
|
return OpenAPISpec(
|
|
info=Info(title="Test API", version="1.0.0"),
|
|
)
|
|
|
|
|
|
def test_api_request_body_from_request_body_with_ref(raw_spec: OpenAPISpec) -> None:
|
|
"""Test instantiating APIRequestBody from RequestBody with a reference."""
|
|
raw_spec.components = Components(
|
|
schemas={
|
|
"Foo": Schema(
|
|
type="object",
|
|
properties={
|
|
"foo": Schema(type="string"),
|
|
"bar": Schema(type="number"),
|
|
},
|
|
required=["foo"],
|
|
)
|
|
}
|
|
)
|
|
media_type = MediaType(
|
|
schema=Reference(
|
|
ref="#/components/schemas/Foo",
|
|
)
|
|
)
|
|
request_body = RequestBody(content={"application/json": media_type})
|
|
api_request_body = APIRequestBody.from_request_body(request_body, raw_spec)
|
|
assert api_request_body.description is None
|
|
assert len(api_request_body.properties) == 2
|
|
foo_prop = api_request_body.properties[0]
|
|
assert foo_prop.name == "foo"
|
|
assert foo_prop.required is True
|
|
bar_prop = api_request_body.properties[1]
|
|
assert bar_prop.name == "bar"
|
|
assert bar_prop.required is False
|
|
assert api_request_body.media_type == "application/json"
|
|
|
|
|
|
def test_api_request_body_from_request_body_with_schema(raw_spec: OpenAPISpec) -> None:
|
|
"""Test instantiating APIRequestBody from RequestBody with a schema."""
|
|
request_body = RequestBody(
|
|
content={
|
|
"application/json": MediaType(
|
|
schema=Schema(type="object", properties={"foo": Schema(type="string")})
|
|
)
|
|
}
|
|
)
|
|
api_request_body = APIRequestBody.from_request_body(request_body, raw_spec)
|
|
assert api_request_body.properties == [
|
|
APIRequestBodyProperty(
|
|
name="foo",
|
|
required=False,
|
|
type="string",
|
|
default=None,
|
|
description=None,
|
|
properties=[],
|
|
references_used=[],
|
|
)
|
|
]
|
|
assert api_request_body.media_type == "application/json"
|
|
|
|
|
|
def test_api_request_body_property_from_schema(raw_spec: OpenAPISpec) -> None:
|
|
raw_spec.components = Components(
|
|
schemas={
|
|
"Bar": Schema(
|
|
type="number",
|
|
)
|
|
}
|
|
)
|
|
schema = Schema(
|
|
type="object",
|
|
properties={
|
|
"foo": Schema(type="string"),
|
|
"bar": Reference(ref="#/components/schemas/Bar"),
|
|
},
|
|
required=["bar"],
|
|
)
|
|
api_request_body_property = APIRequestBodyProperty.from_schema(
|
|
schema, "test", required=True, spec=raw_spec
|
|
)
|
|
expected_sub_properties = [
|
|
APIRequestBodyProperty(
|
|
name="foo",
|
|
required=False,
|
|
type="string",
|
|
default=None,
|
|
description=None,
|
|
properties=[],
|
|
references_used=[],
|
|
),
|
|
APIRequestBodyProperty(
|
|
name="bar",
|
|
required=True,
|
|
type="number",
|
|
default=None,
|
|
description=None,
|
|
properties=[],
|
|
references_used=["Bar"],
|
|
),
|
|
]
|
|
assert api_request_body_property.properties[0] == expected_sub_properties[0]
|
|
assert api_request_body_property.properties[1] == expected_sub_properties[1]
|
|
assert api_request_body_property.type == "object"
|
|
assert api_request_body_property.properties[1].references_used == ["Bar"]
|