Skip to content

Commit 153f256

Browse files
committed
💡 Add swagger docs for the API
Signed-off-by: Shivam Sandbhor <shivam.sandbhor@gmail.com>
1 parent f5bd3bc commit 153f256

7 files changed

Lines changed: 45 additions & 30 deletions

File tree

requirements.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ Django==3.0.7
1313
django-filter==2.2.0
1414
djangorestframework==3.11.0
1515
django-widget-tweaks==1.4.8
16+
drf-yasg==1.17.1
1617
gunicorn==19.7.1
1718
importlib-metadata==1.3.0
1819
ipython==7.13.0

vulnerabilities/api.py

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -60,7 +60,7 @@ class Meta:
6060
fields = ["url", "vulnerability_id"]
6161

6262

63-
class VulnerabilitySerializer(serializers.ModelSerializer):
63+
class VulnerabilitySerializer(serializers.HyperlinkedModelSerializer):
6464
references = VulnerabilityReferenceSerializer(many=True, source="vulnerabilityreference_set")
6565
resolved_packages = HyperLinkedPackageSerializer(
6666
many=True, source="resolved_to", read_only=True
@@ -74,7 +74,7 @@ class Meta:
7474
fields = "__all__"
7575

7676

77-
class PackageSerializer(serializers.ModelSerializer):
77+
class PackageSerializer(serializers.HyperlinkedModelSerializer):
7878
unresolved_vulnerabilities = HyperLinkedVulnerabilitySerializer(
7979
many=True, source="vulnerable_to", read_only=True
8080
)
@@ -86,6 +86,7 @@ class PackageSerializer(serializers.ModelSerializer):
8686
class Meta:
8787
model = Package
8888
fields = [
89+
"url",
8990
"type",
9091
"namespace",
9192
"name",

vulnerabilities/models.py

Lines changed: 12 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -46,11 +46,15 @@ class Vulnerability(models.Model):
4646

4747
@property
4848
def vulnerable_to(self):
49-
return self.packagerelatedvulnerability_set.filter(is_vulnerable=True)
49+
return self.package_set.filter(
50+
packagerelatedvulnerability__is_vulnerable=True,
51+
)
5052

5153
@property
5254
def resolved_to(self):
53-
return self.packagerelatedvulnerability_set.filter(is_vulnerable=False)
55+
return self.package_set.filter(
56+
packagerelatedvulnerability__is_vulnerable=False,
57+
)
5458

5559
def __str__(self):
5660
return self.cve_id or self.summary
@@ -90,11 +94,15 @@ class Package(PackageURLMixin):
9094

9195
@property
9296
def vulnerable_to(self):
93-
return self.packagerelatedvulnerability_set.filter(is_vulnerable=True)
97+
return self.vulnerabilities.filter(
98+
packagerelatedvulnerability__is_vulnerable=True,
99+
)
94100

95101
@property
96102
def resolved_to(self):
97-
return self.packagerelatedvulnerability_set.filter(is_vulnerable=False)
103+
return self.vulnerabilities.filter(
104+
packagerelatedvulnerability__is_vulnerable=False,
105+
)
98106

99107
class Meta:
100108
unique_together = ("name", "namespace", "type", "version", "qualifiers", "subpath")

vulnerabilities/templates/vulnerability.html

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -63,9 +63,9 @@ <h3> Patched Packages </h3>
6363
<tr>
6464
<th> Package URL </th>
6565
</tr>
66-
{% for ref in vulnerability.resolved_to.all %}
66+
{% for package in vulnerability.resolved_to.all %}
6767
<tr>
68-
<td><a href ="{% url 'package_view' ref.package.pk %}"> {{ref.package}} </a> </td>
68+
<td><a href ="{% url 'package_view' package.pk %}"> {{package.package_url}} </a> </td>
6969
</tr>
7070
{% endfor %}
7171
</table>
@@ -79,9 +79,9 @@ <h3> Vulnerable Packages </h3>
7979
<tr>
8080
<th> Package URL </th>
8181
</tr>
82-
{% for ref in vulnerability.vulnerable_to.all %}
82+
{% for package in vulnerability.vulnerable_to.all %}
8383
<tr>
84-
<td><a href ="{% url 'package_view' ref.package.pk %}"> {{ref.package}}</a></td>
84+
<td><a href ="{% url 'package_view' package.pk %}"> {{package.package_url}}</a></td>
8585
</tr>
8686
{% endfor %}
8787
</table>

vulnerabilities/tests/test_api.py

Lines changed: 11 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -57,25 +57,25 @@ def test_query_qualifier_filtering(self):
5757

5858
# check filtering when qualifiers are not normalized
5959
test_purl = quote("pkg:deb/vlc@1.50-1.1?foo=bar&tar=ball")
60-
response = self.client.get(f"/api/packages?purl={test_purl}", format="json").data
60+
response = self.client.get(f"/api/packages/?purl={test_purl}", format="json").data
6161

6262
self.assertEqual(1, response["count"])
6363
self.assertEqual(pk_multi_qf.qualifiers, response["results"][0]["qualifiers"])
6464

6565
test_purl = quote("pkg:deb/vlc@1.50-1.1?tar=ball&foo=bar")
66-
response = self.client.get(f"/api/packages?purl={test_purl}", format="json").data
66+
response = self.client.get(f"/api/packages/?purl={test_purl}", format="json").data
6767

6868
self.assertEqual(1, response["count"])
6969
self.assertEqual(pk_multi_qf.qualifiers, response["results"][0]["qualifiers"])
7070

7171
# check filtering when there is intersection of qualifiers between packages
7272
test_purl = quote("pkg:deb/vlc@1.50-1.1?foo=bar")
73-
response = self.client.get(f"/api/packages?purl={test_purl}", format="json").data
73+
response = self.client.get(f"/api/packages/?purl={test_purl}", format="json").data
7474

7575
self.assertEqual(1, response["count"])
7676

7777
def test_query_by_name(self):
78-
response = self.client.get("/api/packages?name=mimetex", format="json").data
78+
response = self.client.get("/api/packages/?name=mimetex", format="json").data
7979

8080
self.assertEqual(3, response["count"])
8181

@@ -91,7 +91,7 @@ def test_query_by_name(self):
9191
self.assertIn("pkg:deb/debian/mimetex@1.74-1?distro=jessie", purls)
9292

9393
def test_query_by_invalid_package_url(self):
94-
url = "/api/packages?purl=invalid_purl"
94+
url = "/api/packages/?purl=invalid_purl"
9595
response = self.client.get(url, format="json")
9696

9797
self.assertEqual(400, response.status_code)
@@ -100,7 +100,7 @@ def test_query_by_invalid_package_url(self):
100100
self.assertIn("invalid_purl", error)
101101

102102
def test_query_by_package_url(self):
103-
url = "/api/packages?purl=pkg:deb/debian/mimetex@1.50-1.1?distro=jessie"
103+
url = "/api/packages/?purl=pkg:deb/debian/mimetex@1.50-1.1?distro=jessie"
104104
response = self.client.get(url, format="json").data
105105

106106
self.assertEqual(1, response["count"])
@@ -113,7 +113,7 @@ def test_query_by_package_url(self):
113113
self.assertNotIn("1.74-1", versions)
114114

115115
def test_query_by_package_url_without_namespace(self):
116-
url = "/api/packages?purl=pkg:deb/mimetex@1.50-1.1"
116+
url = "/api/packages/?purl=pkg:deb/mimetex@1.50-1.1"
117117
response = self.client.get(url, format="json").data
118118

119119
self.assertEqual(2, response["count"])
@@ -130,7 +130,7 @@ class TestUbuntuResponse(TestCase):
130130
fixtures = ["ubuntu.json"]
131131

132132
def test_ubuntu_response(self):
133-
response = self.client.get("/api/packages?name=automake", format="json")
133+
response = self.client.get("/api/packages/?name=automake", format="json")
134134

135135
result = response.data.get("results")[0]
136136
self.assertEqual("automake", result["name"])
@@ -144,7 +144,7 @@ def test_vulnerability_package_relations(self):
144144
test_pkgs = choices(Package.objects.all(), k=5)
145145
for test_pkg in test_pkgs:
146146

147-
pkg_response = self.client.get(f"/api/packages/{test_pkg.id}", format="json").data
147+
pkg_response = self.client.get(f"/api/packages/{test_pkg.id}/", format="json").data
148148
resolved_vulns = {
149149
vuln["vulnerability_id"] for vuln in pkg_response["resolved_vulnerabilities"]
150150
}
@@ -154,7 +154,7 @@ def test_vulnerability_package_relations(self):
154154

155155
for vuln in resolved_vulns:
156156
vuln_resp = self.client.get(
157-
f"/api/vulnerabilities?vulnerability_id={vuln}", format="json"
157+
f"/api/vulnerabilities/?vulnerability_id={vuln}", format="json"
158158
).data
159159
resolved_purls = {
160160
package["purl"] for package in vuln_resp["results"][0]["resolved_packages"]
@@ -163,7 +163,7 @@ def test_vulnerability_package_relations(self):
163163

164164
for vuln in unresolved_vulns:
165165
vuln_resp = self.client.get(
166-
f"/api/vulnerabilities?vulnerability_id={vuln}", format="json"
166+
f"/api/vulnerabilities/?vulnerability_id={vuln}", format="json"
167167
).data
168168
unresolved_purls = {
169169
package["purl"] for package in vuln_resp["results"][0]["unresolved_packages"]

vulnerablecode/settings.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -47,7 +47,8 @@
4747
'vulnerabilities',
4848
'rest_framework',
4949
'django_filters',
50-
'widget_tweaks'
50+
'widget_tweaks',
51+
'drf_yasg',
5152
]
5253

5354
MIDDLEWARE = [

vulnerablecode/urls.py

Lines changed: 12 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -22,11 +22,14 @@
2222
# Visit https://github.com/nexB/vulnerablecode/ for support and download.
2323

2424
from django.contrib import admin
25-
from django.urls import include, path, re_path
26-
25+
from django.urls import include, path
26+
from rest_framework import permissions
27+
from drf_yasg.views import get_schema_view
28+
from drf_yasg import openapi
2729
from rest_framework.routers import DefaultRouter
2830

2931
from vulnerabilities.api import PackageViewSet
32+
from vulnerabilities.api import VulnerabilityViewSet
3033
from vulnerabilities.views import HomePage
3134
from vulnerabilities.views import PackageSearchView
3235
from vulnerabilities.views import PackageUpdate
@@ -38,22 +41,21 @@
3841
from vulnerabilities.views import VulnerabilityCreate
3942
from vulnerabilities.views import VulnerabilityReferenceCreate
4043

44+
schema_view = get_schema_view(
45+
openapi.Info(title="VulnerableCode API", default_version="v1"),
46+
public=True,
47+
permission_classes=(permissions.AllowAny,),
48+
)
4149

4250
api_router = DefaultRouter()
4351
api_router.register(r"packages", PackageViewSet)
44-
from vulnerabilities.api import VulnerabilityView
45-
46-
47-
api_router = DefaultRouter)
48-
api_router.register(r"packages", PackageViewSet)
4952
# `DefaultRouter` requires `basename` when registering viewsets which don't
5053
# define a queryset.
5154
api_router.register(r"vulnerabilities", VulnerabilityViewSet, basename="vulnerability")
5255

5356

5457
urlpatterns = [
5558
path("admin/", admin.site.urls),
56-
path("api/", include(api_router.urls)),
5759
path("packages/search", PackageSearchView.as_view(), name="package_search"),
5860
path("packages/<int:pk>", PackageUpdate.as_view(), name="package_view"),
5961
path("vulnerabilities/<int:pk>", VulnerabilityDetails.as_view(), name="vulnerability_view"),
@@ -86,4 +88,6 @@
8688
name="vulnerability_reference_create",
8789
),
8890
path("", HomePage.as_view(), name="home"),
91+
path(r"api/", include(api_router.urls)),
92+
path(r"api/docs", schema_view.with_ui("swagger", cache_timeout=0), name="schema-swagger-ui"),
8993
]

0 commit comments

Comments
 (0)