Skip to content
Merged
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
8 changes: 4 additions & 4 deletions openapi_core/components.py
Original file line number Diff line number Diff line change
Expand Up @@ -24,10 +24,10 @@ def __init__(self, dereferencer, schemas_registry):
def create(self, components_spec):
components_deref = self.dereferencer.dereference(components_spec)

schemas_spec = components_deref.get('schemas', [])
responses_spec = components_deref.get('responses', [])
parameters_spec = components_deref.get('parameters', [])
request_bodies_spec = components_deref.get('request_bodies', [])
schemas_spec = components_deref.get('schemas', {})
responses_spec = components_deref.get('responses', {})
parameters_spec = components_deref.get('parameters', {})
request_bodies_spec = components_deref.get('request_bodies', {})

schemas = self.schemas_generator.generate(schemas_spec)
responses = self._generate_response(responses_spec)
Expand Down
3 changes: 2 additions & 1 deletion openapi_core/operations.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
from functools import lru_cache

from six import iteritems
from openapi_spec_validator.validators import PathItemValidator

from openapi_core.exceptions import InvalidResponse
from openapi_core.parameters import ParametersGenerator
Expand Down Expand Up @@ -56,7 +57,7 @@ def __init__(self, dereferencer, schemas_registry):
def generate(self, path_name, path):
path_deref = self.dereferencer.dereference(path)
for http_method, operation in iteritems(path_deref):
if http_method.startswith('x-') or http_method == 'parameters':
if http_method not in PathItemValidator.OPERATIONS:
continue

operation_deref = self.dereferencer.dereference(operation)
Expand Down
6 changes: 3 additions & 3 deletions openapi_core/servers.py
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,9 @@ def __init__(self, dereferencer):

def generate(self, servers_spec):
servers_deref = self.dereferencer.dereference(servers_spec)
if not servers_deref:
yield Server('/')
return
for server_spec in servers_deref:
url = server_spec['url']
variables_spec = server_spec.get('variables', {})
Expand All @@ -65,9 +68,6 @@ def __init__(self, dereferencer):
def generate(self, variables_spec):
variables_deref = self.dereferencer.dereference(variables_spec)

if not variables_deref:
return [Server('/'), ]

for variable_name, variable_spec in iteritems(variables_deref):
default = variable_spec['default']
enum = variable_spec.get('enum')
Expand Down
6 changes: 3 additions & 3 deletions openapi_core/specs.py
Original file line number Diff line number Diff line change
Expand Up @@ -77,10 +77,10 @@ def create(self, spec_dict, spec_url=''):

spec_dict_deref = self.dereferencer.dereference(spec_dict)

info_spec = spec_dict_deref.get('info', [])
info_spec = spec_dict_deref.get('info', {})
servers_spec = spec_dict_deref.get('servers', [])
paths = spec_dict_deref.get('paths', [])
components_spec = spec_dict_deref.get('components', [])
paths = spec_dict_deref.get('paths', {})
components_spec = spec_dict_deref.get('components', {})

info = self.info_factory.create(info_spec)
servers = self.servers_generator.generate(servers_spec)
Expand Down
21 changes: 17 additions & 4 deletions openapi_core/validators.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
"""OpenAPI core validators module"""
from six import iteritems
from yarl import URL

from openapi_core.exceptions import (
OpenAPIMappingError, MissingParameter, MissingBody, InvalidResponse,
Expand Down Expand Up @@ -51,6 +52,16 @@ def __init__(self, errors, data=None, headers=None):
self.headers = headers


def get_operation_pattern(server_url, request_url_pattern):
"""Return an updated request URL pattern with the server URL removed."""
if server_url[-1] == "/":
# operations have to start with a slash, so do not remove it
server_url = server_url[:-1]
if URL(server_url).is_absolute():
return request_url_pattern.replace(server_url, "", 1)
return URL(request_url_pattern).path_qs.replace(server_url, "", 1)


class RequestValidator(object):

def __init__(self, spec):
Expand All @@ -68,8 +79,9 @@ def validate(self, request):
errors.append(exc)
return RequestValidationResult(errors, body, parameters)

operation_pattern = request.full_url_pattern.replace(
server.default_url, '')
operation_pattern = get_operation_pattern(
server.default_url, request.full_url_pattern
)

try:
operation = self.spec.get_operation(
Expand Down Expand Up @@ -154,8 +166,9 @@ def validate(self, request, response):
errors.append(exc)
return ResponseValidationResult(errors, data, headers)

operation_pattern = request.full_url_pattern.replace(
server.default_url, '')
operation_pattern = get_operation_pattern(
server.default_url, request.full_url_pattern
)

try:
operation = self.spec.get_operation(
Expand Down
1 change: 1 addition & 0 deletions requirements.txt
Original file line number Diff line number Diff line change
@@ -1,2 +1,3 @@
openapi-spec-validator
six
yarl
10 changes: 10 additions & 0 deletions tests/integration/data/v3.0/minimal.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
openapi: "3.0.0"
info:
title: Minimal valid OpenAPI specification
version: "0.1"
paths:
/status:
get:
responses:
default:
description: Return the API status.
12 changes: 12 additions & 0 deletions tests/integration/data/v3.0/minimal_with_servers.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
openapi: "3.0.0"
info:
title: Minimal valid OpenAPI specification with explicit 'servers' array
version: "0.1"
servers:
- url: /
paths:
/status:
get:
responses:
default:
description: Return the API status.
49 changes: 49 additions & 0 deletions tests/integration/test_minimal.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
import pytest

from openapi_core.exceptions import InvalidOperation
from openapi_core.shortcuts import create_spec
from openapi_core.validators import RequestValidator
from openapi_core.wrappers import MockRequest


class TestMinimal(object):

servers = [
"http://minimal.test/",
"https://bad.remote.domain.net/",
"http://localhost",
"http://localhost:8080",
"https://u:p@a.b:1337"
]

spec_paths = [
"data/v3.0/minimal_with_servers.yaml",
"data/v3.0/minimal.yaml"
]

@pytest.mark.parametrize("server", servers)
@pytest.mark.parametrize("spec_path", spec_paths)
def test_hosts(self, factory, server, spec_path):
spec_dict = factory.spec_from_file(spec_path)
spec = create_spec(spec_dict)
validator = RequestValidator(spec)
request = MockRequest(server, "get", "/status")

result = validator.validate(request)

assert not result.errors

@pytest.mark.parametrize("server", servers)
@pytest.mark.parametrize("spec_path", spec_paths)
def test_invalid_operation(self, factory, server, spec_path):
spec_dict = factory.spec_from_file(spec_path)
spec = create_spec(spec_dict)
validator = RequestValidator(spec)
request = MockRequest(server, "get", "/nonexistent")

result = validator.validate(request)

assert len(result.errors) == 1
assert isinstance(result.errors[0], InvalidOperation)
assert result.body is None
assert result.parameters == {}