Skip to content

Commit f0cfa2d

Browse files
authored
Merge pull request #131 from MCapitani/links
add support for "links" in Response
2 parents f5d2611 + f748783 commit f0cfa2d

File tree

7 files changed

+207
-1
lines changed

7 files changed

+207
-1
lines changed

openapi_core/schema/links/__init__.py

Whitespace-only changes.
+44
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
"""OpenAPI core links generators module"""
2+
from six import iteritems
3+
4+
from openapi_core.compat import lru_cache
5+
from openapi_core.schema.links.models import Link
6+
from openapi_core.schema.parameters.generators import ParametersGenerator
7+
from openapi_core.schema.servers.generators import ServersGenerator
8+
9+
10+
class LinksGenerator(object):
11+
12+
def __init__(self, dereferencer, schemas_registry):
13+
self.dereferencer = dereferencer
14+
self.schemas_registry = schemas_registry
15+
16+
def generate(self, links):
17+
for link_name, link in iteritems(links):
18+
link_deref = self.dereferencer.dereference(link)
19+
operation_id = link_deref.get('operationId')
20+
parameters = link_deref.get('parameters', {})
21+
request_body = link_deref.get('requestBody') # string or dict
22+
description = link_deref.get('description')
23+
server_spec = link_deref.get('server')
24+
server = self.servers_generator.generate(server_spec) \
25+
if server_spec is not None \
26+
else None
27+
28+
yield link_name, Link(
29+
operation_id,
30+
parameters,
31+
request_body,
32+
description,
33+
server
34+
)
35+
36+
@property
37+
@lru_cache()
38+
def parameters_generator(self):
39+
return ParametersGenerator(self.dereferencer, self.schemas_registry)
40+
41+
@property
42+
@lru_cache()
43+
def servers_generator(self):
44+
return ServersGenerator(self.dereferencer)

openapi_core/schema/links/models.py

+26
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
"""OpenAPI core links models module"""
2+
3+
4+
class Link(object):
5+
"""Represents an OpenAPI Link."""
6+
7+
def __init__(
8+
self,
9+
operation_id,
10+
parameters,
11+
request_body,
12+
description,
13+
server
14+
):
15+
"""
16+
request_body is assumed to be either a string (JSON, YAML or
17+
runtime expression) or an object (deserialized JSON or YAML)
18+
"""
19+
self.operationId = operation_id
20+
self.description = description
21+
self.server = server
22+
self.parameters = dict(parameters) if parameters else {}
23+
self.request_body = request_body
24+
25+
def __getitem__(self, item):
26+
return self.parameters[item]

openapi_core/schema/responses/generators.py

+9-1
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
from six import iteritems
33

44
from openapi_core.compat import lru_cache
5+
from openapi_core.schema.links.generators import LinksGenerator
56
from openapi_core.schema.media_types.generators import MediaTypeGenerator
67
from openapi_core.schema.parameters.generators import ParametersGenerator
78
from openapi_core.schema.responses.models import Response
@@ -19,6 +20,8 @@ def generate(self, responses):
1920
description = response_deref['description']
2021
headers = response_deref.get('headers')
2122
content = response_deref.get('content')
23+
links_dict = response_deref.get('links', {})
24+
links = self.links_generator.generate(links_dict)
2225

2326
media_types = None
2427
if content:
@@ -30,7 +33,7 @@ def generate(self, responses):
3033

3134
yield http_status, Response(
3235
http_status, description,
33-
content=media_types, headers=parameters)
36+
content=media_types, headers=parameters, links=links)
3437

3538
@property
3639
@lru_cache()
@@ -41,3 +44,8 @@ def media_types_generator(self):
4144
@lru_cache()
4245
def parameters_generator(self):
4346
return ParametersGenerator(self.dereferencer, self.schemas_registry)
47+
48+
@property
49+
@lru_cache()
50+
def links_generator(self):
51+
return LinksGenerator(self.dereferencer, self.schemas_registry)
+48
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
openapi: "3.0.0"
2+
info:
3+
title: Minimal valid OpenAPI specification
4+
version: "0.1"
5+
paths:
6+
/linked/noParam:
7+
get:
8+
operationId: noParOp
9+
responses:
10+
default:
11+
description: the linked result
12+
/linked/withParam:
13+
get:
14+
operationId: paramOp
15+
parameters:
16+
- name: opParam
17+
in: query
18+
description: test
19+
schema:
20+
type: string
21+
responses:
22+
default:
23+
description: the linked result
24+
/status:
25+
get:
26+
responses:
27+
default:
28+
description: Return something
29+
links:
30+
noParamLink:
31+
operationId: noParOp
32+
/status/{resourceId}:
33+
get:
34+
parameters:
35+
- name: resourceId
36+
in: path
37+
required: true
38+
schema:
39+
type: string
40+
responses:
41+
default:
42+
description: Return something else
43+
links:
44+
paramLink:
45+
operationId: paramOp
46+
parameters:
47+
opParam: $request.path.resourceId
48+
requestBody: test

tests/integration/test_link_spec.py

+36
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
from openapi_core.shortcuts import create_spec
2+
3+
4+
class TestLinkSpec(object):
5+
6+
def test_no_param(self, factory):
7+
spec_dict = factory.spec_from_file("data/v3.0/links.yaml")
8+
spec = create_spec(spec_dict)
9+
resp = spec['/status']['get'].get_response()
10+
11+
assert len(resp.links) == 1
12+
13+
link = resp.links['noParamLink']
14+
15+
assert link.operationId == 'noParOp'
16+
assert link.server is None
17+
assert link.request_body is None
18+
assert len(link.parameters) == 0
19+
20+
def test_param(self, factory):
21+
spec_dict = factory.spec_from_file("data/v3.0/links.yaml")
22+
spec = create_spec(spec_dict)
23+
resp = spec['/status/{resourceId}']['get'].get_response()
24+
25+
assert len(resp.links) == 1
26+
27+
link = resp.links['paramLink']
28+
29+
assert link.operationId == 'paramOp'
30+
assert link.server is None
31+
assert link.request_body == 'test'
32+
assert len(link.parameters) == 1
33+
34+
param = link.parameters['opParam']
35+
36+
assert param == '$request.path.resourceId'

tests/unit/schema/test_links.py

+44
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
import mock
2+
import pytest
3+
4+
from openapi_core.schema.links.models import Link
5+
from openapi_core.schema.servers.models import Server
6+
7+
8+
class TestLinks(object):
9+
10+
@pytest.fixture
11+
def link_factory(self):
12+
def link_factory(request_body, server):
13+
parameters = {
14+
'par1': mock.sentinel.par1,
15+
'par2': mock.sentinel.par2,
16+
}
17+
return Link(
18+
'op_id',
19+
parameters,
20+
request_body,
21+
'Test link',
22+
server
23+
)
24+
return link_factory
25+
26+
servers = [
27+
None,
28+
Server("https://bad.remote.domain.net/"),
29+
Server("http://localhost")
30+
]
31+
32+
request_body_list = [
33+
None,
34+
"request",
35+
'{"request": "value", "opt": 2}',
36+
{"request": "value", "opt": 2}
37+
]
38+
39+
@pytest.mark.parametrize("server", servers)
40+
@pytest.mark.parametrize("request_body", request_body_list)
41+
def test_iteritems(self, link_factory, request_body, server):
42+
link = link_factory(request_body, server)
43+
for par_name in link.parameters.keys():
44+
assert link[par_name] == link.parameters[par_name]

0 commit comments

Comments
 (0)