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
diff --git a/src/maasserver/api/doc.py b/src/maasserver/api/doc.py
index 4e5c3d4..6bc7ae2 100644
--- a/src/maasserver/api/doc.py
+++ b/src/maasserver/api/doc.py
@@ -36,35 +36,6 @@ def get_api_description():
36 return description36 return description
3737
3838
39def get_api_landing_page():
40 """Return the API landing page"""
41 description = {
42 "title": "MAAS API",
43 "description": "API landing page for MAAS",
44 "resources": [
45 {
46 "path": "/MAAS/api",
47 "rel": "self",
48 "type": "application/json",
49 "title": "this document",
50 },
51 {
52 "path": "/MAAS/api/2.0/openapi.yaml",
53 "rel": "service-desc",
54 "type": "application/openapi+yaml",
55 "title": "the API definition",
56 },
57 {
58 "path": "/MAAS/docs/api.html",
59 "rel": "service-doc",
60 "type": "text/html",
61 "title": "the API documentation",
62 },
63 ],
64 }
65 return description
66
67
68def find_api_resources(urlconf=None):39def find_api_resources(urlconf=None):
69 """Find the API resources defined in `urlconf`.40 """Find the API resources defined in `urlconf`.
7041
diff --git a/src/maasserver/api/doc_handler.py b/src/maasserver/api/doc_handler.py
index f8e5ba8..3dd41c6 100644
--- a/src/maasserver/api/doc_handler.py
+++ b/src/maasserver/api/doc_handler.py
@@ -71,7 +71,6 @@ from maasserver.api.doc import (
71 generate_pod_types_doc,71 generate_pod_types_doc,
72 generate_power_types_doc,72 generate_power_types_doc,
73 get_api_description,73 get_api_description,
74 get_api_landing_page,
75)74)
76from maasserver.api.templates import APITemplateRenderer75from maasserver.api.templates import APITemplateRenderer
77from maasserver.utils import build_absolute_uri76from maasserver.utils import build_absolute_uri
@@ -243,19 +242,3 @@ def describe(request):
243 json.dumps(description, sort_keys=True),242 json.dumps(description, sort_keys=True),
244 content_type="application/json",243 content_type="application/json",
245 )244 )
246
247
248def api_landing_page(request):
249 """Render a landing page with pointers for the MAAS API.
250
251 :return: An `HttpResponse` containing a JSON page with pointers to both
252 human-readable documentation and api definitions.
253 """
254 description = get_api_landing_page()
255 for link in description["resources"]:
256 link["href"] = build_absolute_uri(request, link["path"])
257 # Return as a JSON document
258 return HttpResponse(
259 json.dumps(description),
260 content_type="application/json",
261 )
diff --git a/src/maasserver/api/doc_oapi.py b/src/maasserver/api/doc_oapi.py
262new file mode 100644245new file mode 100644
index 0000000..d9dfe25
--- /dev/null
+++ b/src/maasserver/api/doc_oapi.py
@@ -0,0 +1,84 @@
1# Copyright 2014-2022 Canonical Ltd. This software is licensed under the
2# GNU Affero General Public License version 3 (see the file LICENSE).
3
4import json
5
6from django.http import HttpResponse
7import yaml
8
9from maasserver.utils import build_absolute_uri
10
11
12def landing_page(request):
13 """Render a landing page with pointers for the MAAS API.
14
15 :return: An `HttpResponse` containing a JSON page with pointers to both
16 human-readable documentation and api definitions.
17 """
18 description = get_api_landing_page()
19 for link in description["resources"]:
20 link["href"] = build_absolute_uri(request, link["path"])
21 # Return as a JSON document
22 return HttpResponse(
23 json.dumps(description),
24 content_type="application/json",
25 )
26
27
28def endpoint(request):
29 """Render the OpenApi endpoint.
30
31 :return: An `HttpResponse` containing a YAML document that complies
32 with the OpenApi spec 3.0.
33 """
34 description = get_api_endpoint()
35 doc = description["externalDocs"]
36 doc["url"] = build_absolute_uri(request, doc["url"])
37 # Return as a YAML document
38 return HttpResponse(
39 yaml.dump(description),
40 content_type="application/yaml",
41 )
42
43
44def get_api_landing_page():
45 """Return the API landing page"""
46 description = {
47 "title": "MAAS API",
48 "description": "API landing page for MAAS",
49 "resources": [
50 {
51 "path": "/MAAS/api",
52 "rel": "self",
53 "type": "application/json",
54 "title": "this document",
55 },
56 {
57 "path": "/MAAS/api/2.0/openapi.yaml",
58 "rel": "service-desc",
59 "type": "application/openapi+yaml",
60 "title": "the API definition",
61 },
62 {
63 "path": "/MAAS/docs/api.html",
64 "rel": "service-doc",
65 "type": "text/html",
66 "title": "the API documentation",
67 },
68 ],
69 }
70 return description
71
72
73def get_api_endpoint():
74 """Return the API endpoint"""
75 description = {
76 "openapi": "3.0.0",
77 "info": {"title": "MAAS OpenApi Endpoint", "version": "1.0.0"},
78 "paths": [],
79 "externalDocs": {
80 "description": "MAAS API documentation",
81 "url": "/MAAS/docs/api.html",
82 },
83 }
84 return description
diff --git a/src/maasserver/api/tests/test_doc.py b/src/maasserver/api/tests/test_doc.py
index cb9d061..ad02930 100644
--- a/src/maasserver/api/tests/test_doc.py
+++ b/src/maasserver/api/tests/test_doc.py
@@ -26,6 +26,7 @@ from testtools.matchers import (
26 MatchesStructure,26 MatchesStructure,
27 Not,27 Not,
28)28)
29import yaml
2930
30from maasserver.api import doc as doc_module31from maasserver.api import doc as doc_module
31from maasserver.api.doc import (32from maasserver.api.doc import (
@@ -41,7 +42,8 @@ from maasserver.api.doc import (
41 generate_power_types_doc,42 generate_power_types_doc,
42 get_api_description,43 get_api_description,
43)44)
44from maasserver.api.doc_handler import api_landing_page, render_api_docs45from maasserver.api.doc_handler import render_api_docs
46from maasserver.api.doc_oapi import endpoint, landing_page
45from maasserver.api.support import (47from maasserver.api.support import (
46 operation,48 operation,
47 OperationsHandler,49 OperationsHandler,
@@ -83,14 +85,28 @@ class TestGetAPIDescription(MAASTestCase):
83class TestLandingPage(MAASTestCase):85class TestLandingPage(MAASTestCase):
84 def test_links(self):86 def test_links(self):
85 request = factory.make_fake_request()87 request = factory.make_fake_request()
86 landing_page = api_landing_page(request)88 page = landing_page(request)
87 content = json.loads(landing_page.content)89 content = json.loads(page.content)
88 resources = content["resources"]90 resources = content["resources"]
89 host = request.get_host()91 host = request.get_host()
90 for link in resources:92 for link in resources:
91 href = f"http://{host}{link['path']}"93 href = f"http://{host}{link['path']}"
92 self.assertEqual(link["href"], href)94 self.assertEqual(link["href"], href)
93 self.assertEqual(resources[0]["type"], landing_page["content-type"])95 self.assertEqual(resources[0]["type"], page["content-type"])
96
97
98class TestApiEndpoint(MAASTestCase):
99 def test_required_fields(self):
100 request = factory.make_fake_request()
101 page = endpoint(request)
102 content = yaml.safe_load(page.content)
103 self.assertIn("openapi", content)
104 self.assertIn("info", content)
105 self.assertIn("paths", content)
106 info = content["info"]
107 self.assertIsInstance(info, dict)
108 self.assertIn("title", info)
109 self.assertIn("version", info)
94110
95111
96class TestFindingResources(MAASTestCase):112class TestFindingResources(MAASTestCase):
diff --git a/src/maasserver/urls.py b/src/maasserver/urls.py
index 404dc11..c2cba09 100644
--- a/src/maasserver/urls.py
+++ b/src/maasserver/urls.py
@@ -9,7 +9,7 @@ from django.urls import include, re_path
9from django.views.generic import TemplateView9from django.views.generic import TemplateView
1010
11from maasserver import urls_api11from maasserver import urls_api
12from maasserver.api.doc_handler import api_landing_page12from maasserver.api.doc_oapi import landing_page
13from maasserver.bootresources import (13from maasserver.bootresources import (
14 simplestreams_file_handler,14 simplestreams_file_handler,
15 simplestreams_stream_handler,15 simplestreams_stream_handler,
@@ -77,7 +77,7 @@ urlpatterns += [re_path(r"^accounts/logout/$", logout, name="logout")]
7777
78# API URLs. If old API requested, provide error message directing to new API.78# API URLs. If old API requested, provide error message directing to new API.
79urlpatterns += [79urlpatterns += [
80 re_path(r"^api/$", api_landing_page),80 re_path(r"^api/$", landing_page),
81 re_path(r"^api/2\.0/", include(urls_api)),81 re_path(r"^api/2\.0/", include(urls_api)),
82 re_path(82 re_path(
83 r"^api/version/",83 r"^api/version/",
diff --git a/src/maasserver/urls_api.py b/src/maasserver/urls_api.py
index bd7eb41..184263b 100644
--- a/src/maasserver/urls_api.py
+++ b/src/maasserver/urls_api.py
@@ -37,6 +37,7 @@ from maasserver.api.dnsresourcerecords import (
37)37)
38from maasserver.api.dnsresources import DNSResourceHandler, DNSResourcesHandler38from maasserver.api.dnsresources import DNSResourceHandler, DNSResourcesHandler
39from maasserver.api.doc_handler import describe39from maasserver.api.doc_handler import describe
40from maasserver.api.doc_oapi import endpoint
40from maasserver.api.domains import DomainHandler, DomainsHandler41from maasserver.api.domains import DomainHandler, DomainsHandler
41from maasserver.api.events import EventsHandler42from maasserver.api.events import EventsHandler
42from maasserver.api.fabrics import FabricHandler, FabricsHandler43from maasserver.api.fabrics import FabricHandler, FabricsHandler
@@ -330,11 +331,11 @@ license_keys_handler = AdminRestrictedResource(
330 LicenseKeysHandler, authentication=api_auth331 LicenseKeysHandler, authentication=api_auth
331)332)
332333
333
334# API URLs accessible to anonymous users.334# API URLs accessible to anonymous users.
335urlpatterns = [335urlpatterns = [
336 re_path(r"describe/$", describe, name="describe"),336 re_path(r"describe/$", describe, name="describe"),
337 re_path(r"version/$", version_handler, name="version_handler"),337 re_path(r"version/$", version_handler, name="version_handler"),
338 re_path(r"openapi.yaml$", endpoint, name="openapi_endpoint"),
338]339]
339340
340341

Subscribers

People subscribed via source and target branches