Merge ~lloydwaltersj/maas:OpenApi-Endpoint into maas:master

Proposed by Jack Lloyd-Walters
Status: Merged
Merge reported by: MAAS Lander
Merged at revision: not available
Proposed branch: ~lloydwaltersj/maas:OpenApi-Endpoint
Merge into: maas:master
Diff against target: 261 lines (+108/-53)
6 files modified
src/maasserver/api/doc.py (+0/-29)
src/maasserver/api/doc_handler.py (+0/-17)
src/maasserver/api/doc_oapi.py (+84/-0)
src/maasserver/api/tests/test_doc.py (+20/-4)
src/maasserver/urls.py (+2/-2)
src/maasserver/urls_api.py (+2/-1)
Reviewer Review Type Date Requested Status
Adam Collard (community) Approve
MAAS Lander Approve
Alberto Donato (community) Needs Information
Review via email: mp+427288@code.launchpad.net

Commit message

Introduce OpenApi endpoint

To post a comment you must log in.
Revision history for this message
Jack Lloyd-Walters (lloydwaltersj) :
Revision history for this message
MAAS Lander (maas-lander) wrote :

UNIT TESTS
-b OpenApi-Endpoint lp:~lloydwaltersj/maas/+git/maas into -b master lp:~maas-committers/maas

STATUS: SUCCESS
COMMIT: 631a5f07824410fb6559865300372e3afafca5b4

review: Approve
Revision history for this message
MAAS Lander (maas-lander) wrote :

UNIT TESTS
-b OpenApi-Endpoint lp:~lloydwaltersj/maas/+git/maas into -b master lp:~maas-committers/maas

STATUS: FAILED
LOG: http://maas-ci.internal:8080/job/maas-tester/141/consoleText
COMMIT: ddbc67b4b5e16c7408cc72ee869fde0d960e0637

review: Needs Fixing
Revision history for this message
Jack Lloyd-Walters (lloydwaltersj) wrote :

jenkins: !test

Revision history for this message
Alberto Donato (ack) wrote :

looks mostly good, a couple of comments and a question inline

review: Needs Information
Revision history for this message
MAAS Lander (maas-lander) wrote :

UNIT TESTS
-b OpenApi-Endpoint lp:~lloydwaltersj/maas/+git/maas into -b master lp:~maas-committers/maas

STATUS: SUCCESS
COMMIT: ddbc67b4b5e16c7408cc72ee869fde0d960e0637

review: Approve
Revision history for this message
MAAS Lander (maas-lander) wrote :

UNIT TESTS
-b OpenApi-Endpoint lp:~lloydwaltersj/maas/+git/maas into -b master lp:~maas-committers/maas

STATUS: SUCCESS
COMMIT: d0852abe819e5dee8afd4c08f97a508e4dd9998c

review: Approve
Revision history for this message
Alberto Donato (ack) :
Revision history for this message
MAAS Lander (maas-lander) wrote :

UNIT TESTS
-b OpenApi-Endpoint lp:~lloydwaltersj/maas/+git/maas into -b master lp:~maas-committers/maas

STATUS: SUCCESS
COMMIT: 4d1c04bdada5c8991272fd402eca18f7d7138f72

review: Approve
Revision history for this message
Alexsander de Souza (alexsander-souza) wrote :

let's move OpenAPI-specific functions to `doc_oapi.py`, keeping the ReST and the OpenAPI segregated

Revision history for this message
MAAS Lander (maas-lander) wrote :

UNIT TESTS
-b OpenApi-Endpoint lp:~lloydwaltersj/maas/+git/maas into -b master lp:~maas-committers/maas

STATUS: SUCCESS
COMMIT: 82c175c5d16198d7bc5d36755ccd6cc1b2ed3284

review: Approve
Revision history for this message
Adam Collard (adam-collard) wrote :

+1 with a question inline

review: Approve
Revision history for this message
Alexsander de Souza (alexsander-souza) :

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1diff --git a/src/maasserver/api/doc.py b/src/maasserver/api/doc.py
2index 4e5c3d4..6bc7ae2 100644
3--- a/src/maasserver/api/doc.py
4+++ b/src/maasserver/api/doc.py
5@@ -36,35 +36,6 @@ def get_api_description():
6 return description
7
8
9-def get_api_landing_page():
10- """Return the API landing page"""
11- description = {
12- "title": "MAAS API",
13- "description": "API landing page for MAAS",
14- "resources": [
15- {
16- "path": "/MAAS/api",
17- "rel": "self",
18- "type": "application/json",
19- "title": "this document",
20- },
21- {
22- "path": "/MAAS/api/2.0/openapi.yaml",
23- "rel": "service-desc",
24- "type": "application/openapi+yaml",
25- "title": "the API definition",
26- },
27- {
28- "path": "/MAAS/docs/api.html",
29- "rel": "service-doc",
30- "type": "text/html",
31- "title": "the API documentation",
32- },
33- ],
34- }
35- return description
36-
37-
38 def find_api_resources(urlconf=None):
39 """Find the API resources defined in `urlconf`.
40
41diff --git a/src/maasserver/api/doc_handler.py b/src/maasserver/api/doc_handler.py
42index f8e5ba8..3dd41c6 100644
43--- a/src/maasserver/api/doc_handler.py
44+++ b/src/maasserver/api/doc_handler.py
45@@ -71,7 +71,6 @@ from maasserver.api.doc import (
46 generate_pod_types_doc,
47 generate_power_types_doc,
48 get_api_description,
49- get_api_landing_page,
50 )
51 from maasserver.api.templates import APITemplateRenderer
52 from maasserver.utils import build_absolute_uri
53@@ -243,19 +242,3 @@ def describe(request):
54 json.dumps(description, sort_keys=True),
55 content_type="application/json",
56 )
57-
58-
59-def api_landing_page(request):
60- """Render a landing page with pointers for the MAAS API.
61-
62- :return: An `HttpResponse` containing a JSON page with pointers to both
63- human-readable documentation and api definitions.
64- """
65- description = get_api_landing_page()
66- for link in description["resources"]:
67- link["href"] = build_absolute_uri(request, link["path"])
68- # Return as a JSON document
69- return HttpResponse(
70- json.dumps(description),
71- content_type="application/json",
72- )
73diff --git a/src/maasserver/api/doc_oapi.py b/src/maasserver/api/doc_oapi.py
74new file mode 100644
75index 0000000..d9dfe25
76--- /dev/null
77+++ b/src/maasserver/api/doc_oapi.py
78@@ -0,0 +1,84 @@
79+# Copyright 2014-2022 Canonical Ltd. This software is licensed under the
80+# GNU Affero General Public License version 3 (see the file LICENSE).
81+
82+import json
83+
84+from django.http import HttpResponse
85+import yaml
86+
87+from maasserver.utils import build_absolute_uri
88+
89+
90+def landing_page(request):
91+ """Render a landing page with pointers for the MAAS API.
92+
93+ :return: An `HttpResponse` containing a JSON page with pointers to both
94+ human-readable documentation and api definitions.
95+ """
96+ description = get_api_landing_page()
97+ for link in description["resources"]:
98+ link["href"] = build_absolute_uri(request, link["path"])
99+ # Return as a JSON document
100+ return HttpResponse(
101+ json.dumps(description),
102+ content_type="application/json",
103+ )
104+
105+
106+def endpoint(request):
107+ """Render the OpenApi endpoint.
108+
109+ :return: An `HttpResponse` containing a YAML document that complies
110+ with the OpenApi spec 3.0.
111+ """
112+ description = get_api_endpoint()
113+ doc = description["externalDocs"]
114+ doc["url"] = build_absolute_uri(request, doc["url"])
115+ # Return as a YAML document
116+ return HttpResponse(
117+ yaml.dump(description),
118+ content_type="application/yaml",
119+ )
120+
121+
122+def get_api_landing_page():
123+ """Return the API landing page"""
124+ description = {
125+ "title": "MAAS API",
126+ "description": "API landing page for MAAS",
127+ "resources": [
128+ {
129+ "path": "/MAAS/api",
130+ "rel": "self",
131+ "type": "application/json",
132+ "title": "this document",
133+ },
134+ {
135+ "path": "/MAAS/api/2.0/openapi.yaml",
136+ "rel": "service-desc",
137+ "type": "application/openapi+yaml",
138+ "title": "the API definition",
139+ },
140+ {
141+ "path": "/MAAS/docs/api.html",
142+ "rel": "service-doc",
143+ "type": "text/html",
144+ "title": "the API documentation",
145+ },
146+ ],
147+ }
148+ return description
149+
150+
151+def get_api_endpoint():
152+ """Return the API endpoint"""
153+ description = {
154+ "openapi": "3.0.0",
155+ "info": {"title": "MAAS OpenApi Endpoint", "version": "1.0.0"},
156+ "paths": [],
157+ "externalDocs": {
158+ "description": "MAAS API documentation",
159+ "url": "/MAAS/docs/api.html",
160+ },
161+ }
162+ return description
163diff --git a/src/maasserver/api/tests/test_doc.py b/src/maasserver/api/tests/test_doc.py
164index cb9d061..ad02930 100644
165--- a/src/maasserver/api/tests/test_doc.py
166+++ b/src/maasserver/api/tests/test_doc.py
167@@ -26,6 +26,7 @@ from testtools.matchers import (
168 MatchesStructure,
169 Not,
170 )
171+import yaml
172
173 from maasserver.api import doc as doc_module
174 from maasserver.api.doc import (
175@@ -41,7 +42,8 @@ from maasserver.api.doc import (
176 generate_power_types_doc,
177 get_api_description,
178 )
179-from maasserver.api.doc_handler import api_landing_page, render_api_docs
180+from maasserver.api.doc_handler import render_api_docs
181+from maasserver.api.doc_oapi import endpoint, landing_page
182 from maasserver.api.support import (
183 operation,
184 OperationsHandler,
185@@ -83,14 +85,28 @@ class TestGetAPIDescription(MAASTestCase):
186 class TestLandingPage(MAASTestCase):
187 def test_links(self):
188 request = factory.make_fake_request()
189- landing_page = api_landing_page(request)
190- content = json.loads(landing_page.content)
191+ page = landing_page(request)
192+ content = json.loads(page.content)
193 resources = content["resources"]
194 host = request.get_host()
195 for link in resources:
196 href = f"http://{host}{link['path']}"
197 self.assertEqual(link["href"], href)
198- self.assertEqual(resources[0]["type"], landing_page["content-type"])
199+ self.assertEqual(resources[0]["type"], page["content-type"])
200+
201+
202+class TestApiEndpoint(MAASTestCase):
203+ def test_required_fields(self):
204+ request = factory.make_fake_request()
205+ page = endpoint(request)
206+ content = yaml.safe_load(page.content)
207+ self.assertIn("openapi", content)
208+ self.assertIn("info", content)
209+ self.assertIn("paths", content)
210+ info = content["info"]
211+ self.assertIsInstance(info, dict)
212+ self.assertIn("title", info)
213+ self.assertIn("version", info)
214
215
216 class TestFindingResources(MAASTestCase):
217diff --git a/src/maasserver/urls.py b/src/maasserver/urls.py
218index 404dc11..c2cba09 100644
219--- a/src/maasserver/urls.py
220+++ b/src/maasserver/urls.py
221@@ -9,7 +9,7 @@ from django.urls import include, re_path
222 from django.views.generic import TemplateView
223
224 from maasserver import urls_api
225-from maasserver.api.doc_handler import api_landing_page
226+from maasserver.api.doc_oapi import landing_page
227 from maasserver.bootresources import (
228 simplestreams_file_handler,
229 simplestreams_stream_handler,
230@@ -77,7 +77,7 @@ urlpatterns += [re_path(r"^accounts/logout/$", logout, name="logout")]
231
232 # API URLs. If old API requested, provide error message directing to new API.
233 urlpatterns += [
234- re_path(r"^api/$", api_landing_page),
235+ re_path(r"^api/$", landing_page),
236 re_path(r"^api/2\.0/", include(urls_api)),
237 re_path(
238 r"^api/version/",
239diff --git a/src/maasserver/urls_api.py b/src/maasserver/urls_api.py
240index bd7eb41..184263b 100644
241--- a/src/maasserver/urls_api.py
242+++ b/src/maasserver/urls_api.py
243@@ -37,6 +37,7 @@ from maasserver.api.dnsresourcerecords import (
244 )
245 from maasserver.api.dnsresources import DNSResourceHandler, DNSResourcesHandler
246 from maasserver.api.doc_handler import describe
247+from maasserver.api.doc_oapi import endpoint
248 from maasserver.api.domains import DomainHandler, DomainsHandler
249 from maasserver.api.events import EventsHandler
250 from maasserver.api.fabrics import FabricHandler, FabricsHandler
251@@ -330,11 +331,11 @@ license_keys_handler = AdminRestrictedResource(
252 LicenseKeysHandler, authentication=api_auth
253 )
254
255-
256 # API URLs accessible to anonymous users.
257 urlpatterns = [
258 re_path(r"describe/$", describe, name="describe"),
259 re_path(r"version/$", version_handler, name="version_handler"),
260+ re_path(r"openapi.yaml$", endpoint, name="openapi_endpoint"),
261 ]
262
263

Subscribers

People subscribed via source and target branches