Skip to content

Commit a3853da

Browse files
authored
Merge pull request #15 from pbasista/bugfix/empty-servers
Add the default value for the 'servers' array
2 parents 3cc1101 + a0305fb commit a3853da

File tree

6 files changed

+92
-7
lines changed

6 files changed

+92
-7
lines changed

openapi_core/servers.py

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,9 @@ def __init__(self, dereferencer):
4141

4242
def generate(self, servers_spec):
4343
servers_deref = self.dereferencer.dereference(servers_spec)
44+
if not servers_deref:
45+
yield Server('/')
46+
return
4447
for server_spec in servers_deref:
4548
url = server_spec['url']
4649
variables_spec = server_spec.get('variables', {})
@@ -65,9 +68,6 @@ def __init__(self, dereferencer):
6568
def generate(self, variables_spec):
6669
variables_deref = self.dereferencer.dereference(variables_spec)
6770

68-
if not variables_deref:
69-
return [Server('/'), ]
70-
7171
for variable_name, variable_spec in iteritems(variables_deref):
7272
default = variable_spec['default']
7373
enum = variable_spec.get('enum')

openapi_core/validators.py

Lines changed: 17 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
"""OpenAPI core validators module"""
22
from six import iteritems
3+
from yarl import URL
34

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

5354

55+
def get_operation_pattern(server_url, request_url_pattern):
56+
"""Return an updated request URL pattern with the server URL removed."""
57+
if server_url[-1] == "/":
58+
# operations have to start with a slash, so do not remove it
59+
server_url = server_url[:-1]
60+
if URL(server_url).is_absolute():
61+
return request_url_pattern.replace(server_url, "", 1)
62+
return URL(request_url_pattern).path_qs.replace(server_url, "", 1)
63+
64+
5465
class RequestValidator(object):
5566

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

71-
operation_pattern = request.full_url_pattern.replace(
72-
server.default_url, '')
82+
operation_pattern = get_operation_pattern(
83+
server.default_url, request.full_url_pattern
84+
)
7385

7486
try:
7587
operation = self.spec.get_operation(
@@ -154,8 +166,9 @@ def validate(self, request, response):
154166
errors.append(exc)
155167
return ResponseValidationResult(errors, data, headers)
156168

157-
operation_pattern = request.full_url_pattern.replace(
158-
server.default_url, '')
169+
operation_pattern = get_operation_pattern(
170+
server.default_url, request.full_url_pattern
171+
)
159172

160173
try:
161174
operation = self.spec.get_operation(

requirements.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,3 @@
11
openapi-spec-validator
22
six
3+
yarl
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
openapi: "3.0.0"
2+
info:
3+
title: Minimal valid OpenAPI specification
4+
version: "0.1"
5+
paths:
6+
/status:
7+
get:
8+
responses:
9+
default:
10+
description: Return the API status.
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
openapi: "3.0.0"
2+
info:
3+
title: Minimal valid OpenAPI specification with explicit 'servers' array
4+
version: "0.1"
5+
servers:
6+
- url: /
7+
paths:
8+
/status:
9+
get:
10+
responses:
11+
default:
12+
description: Return the API status.

tests/integration/test_minimal.py

Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
import pytest
2+
3+
from openapi_core.exceptions import InvalidOperation
4+
from openapi_core.shortcuts import create_spec
5+
from openapi_core.validators import RequestValidator
6+
from openapi_core.wrappers import MockRequest
7+
8+
9+
class TestMinimal(object):
10+
11+
servers = [
12+
"http://minimal.test/",
13+
"https://bad.remote.domain.net/",
14+
"http://localhost",
15+
"http://localhost:8080",
16+
"https://u:p@a.b:1337"
17+
]
18+
19+
spec_paths = [
20+
"data/v3.0/minimal_with_servers.yaml",
21+
"data/v3.0/minimal.yaml"
22+
]
23+
24+
@pytest.mark.parametrize("server", servers)
25+
@pytest.mark.parametrize("spec_path", spec_paths)
26+
def test_hosts(self, factory, server, spec_path):
27+
spec_dict = factory.spec_from_file(spec_path)
28+
spec = create_spec(spec_dict)
29+
validator = RequestValidator(spec)
30+
request = MockRequest(server, "get", "/status")
31+
32+
result = validator.validate(request)
33+
34+
assert not result.errors
35+
36+
@pytest.mark.parametrize("server", servers)
37+
@pytest.mark.parametrize("spec_path", spec_paths)
38+
def test_invalid_operation(self, factory, server, spec_path):
39+
spec_dict = factory.spec_from_file(spec_path)
40+
spec = create_spec(spec_dict)
41+
validator = RequestValidator(spec)
42+
request = MockRequest(server, "get", "/nonexistent")
43+
44+
result = validator.validate(request)
45+
46+
assert len(result.errors) == 1
47+
assert isinstance(result.errors[0], InvalidOperation)
48+
assert result.body is None
49+
assert result.parameters == {}

0 commit comments

Comments
 (0)