This is an automated email from the ASF dual-hosted git repository. gjm pushed a commit to branch main in repository https://gitbox.apache.org/repos/asf/bloodhound-core.git
The following commit(s) were added to refs/heads/main by this push: new f1162c0 Adds components, milestones and versions to api f1162c0 is described below commit f1162c0b6f464071d812a856e68a9640262a8f42 Author: Gary Martin <g...@apache.org> AuthorDate: Thu May 27 22:00:31 2021 +0100 Adds components, milestones and versions to api --- pyproject.toml | 1 + trackers/api/serializers.py | 157 ++++++++- trackers/api/tests/test_product_api.py | 34 +- trackers/api/tests/test_ticket_api.py | 589 +++++++++++++++++++++++++++++++++ trackers/api/urls.py | 9 + trackers/api/views.py | 30 ++ trackers/models.py | 3 - 7 files changed, 792 insertions(+), 31 deletions(-) diff --git a/pyproject.toml b/pyproject.toml index fee9aad..30f3d7f 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -11,6 +11,7 @@ django = "^3.2.2" django-rest-framework = "^0.1.0" drf-yasg = "^1.20.0" drf-nested-routers = "^0.93.3" +pyyaml = "^5.4.1" [tool.poetry.dev-dependencies] selenium = "^3.141.0" diff --git a/trackers/api/serializers.py b/trackers/api/serializers.py index a48abee..58d9248 100644 --- a/trackers/api/serializers.py +++ b/trackers/api/serializers.py @@ -1,7 +1,48 @@ from django.contrib.auth.models import User, Group from django.shortcuts import get_object_or_404 from rest_framework import serializers -from ..models import Product, Ticket +from rest_framework.reverse import reverse +from ..models import Component, Milestone, Product, Ticket, Version +from rest_framework_nested.serializers import NestedHyperlinkedModelSerializer +from rest_framework_nested.relations import NestedHyperlinkedIdentityField +from functools import partial + + +def get_self_url(obj, context, obj_type): + keywords = { + 'product_prefix': obj.product.prefix, + } + if obj_type == 'ticket': + keywords['product_ticket_id'] = obj.product_ticket_id + else: + keywords['name'] = obj.name + + return reverse( + f'product-{obj_type}s-detail', + kwargs=keywords, + request=context['request'], + ) + + +class ProductChildSerializer(serializers.HyperlinkedModelSerializer): + product_url = serializers.SerializerMethodField() + + def get_product_url(self, obj): + keywords = { + 'prefix': obj.product.prefix, + } + return reverse( + 'product-detail', + kwargs=keywords, + request=self.context['request'] + ) + + def create(self, validated_data): + if 'prefix' not in self.context['view'].kwargs.keys(): + prefix = self.context['view'].kwargs['product_prefix'] + product = get_object_or_404(Product.objects.all(), prefix=prefix) + validated_data['product'] = product + return super().create(validated_data) class UserSerializer(serializers.HyperlinkedModelSerializer): @@ -16,25 +57,113 @@ class GroupSerializer(serializers.HyperlinkedModelSerializer): fields = ('url', 'name') -class ProductSerializer(serializers.ModelSerializer): - class Meta: - model = Product - fields = '__all__' - +class TicketSerializer(ProductChildSerializer): + url = serializers.SerializerMethodField() -class TicketSerializer(serializers.ModelSerializer): class Meta: model = Ticket fields = ( + 'product_url', + 'url', 'product_ticket_id', 'summary', 'description', ) - extra_kwargs = {'product_ticket_id': {'required': False}} + extra_kwargs = { + 'product_ticket_id': {'required': False}, + } - def create(self, validated_data): - if 'prefix' not in self.context['view'].kwargs.keys(): - prefix = self.context['view'].kwargs['product_prefix'] - product = get_object_or_404(Product.objects.all(), prefix=prefix) - validated_data['product'] = product - return super().create(validated_data) + def get_url(self, obj): + return get_self_url(obj, self.context, 'ticket') + + +class ComponentSerializer(ProductChildSerializer): + url = serializers.SerializerMethodField() + + class Meta: + model = Component + fields = ( + 'product_url', + 'url', + 'name', + 'description', + 'owner', + ) + + def get_url(self, obj): + return get_self_url(obj, self.context, 'component') + + +class MilestoneSerializer(ProductChildSerializer): + url = serializers.SerializerMethodField() + + class Meta: + model = Milestone + fields = ( + 'product_url', + 'url', + 'name', + 'description', + 'due', + 'completed', + ) + + def get_url(self, obj): + return get_self_url(obj, self.context, 'milestone') + + +class VersionSerializer(ProductChildSerializer): + url = serializers.SerializerMethodField() + + class Meta: + model = Version + fields = ( + 'product_url', + 'url', + 'name', + 'description', + 'time', + ) + + def get_url(self, obj): + return get_self_url(obj, self.context, 'version') + + +ProductHyperlinkedModelSerializer = partial( + serializers.HyperlinkedIdentityField, + lookup_field='prefix', + lookup_url_kwarg='product_prefix', +) + + +class ProductSerializer(serializers.HyperlinkedModelSerializer): + url = serializers.HyperlinkedIdentityField( + view_name='product-detail', + lookup_field='prefix', + ) + tickets_url = ProductHyperlinkedModelSerializer( + view_name='product-tickets-list', + ) + components_url = ProductHyperlinkedModelSerializer( + view_name='product-components-list', + ) + milestones_url = ProductHyperlinkedModelSerializer( + view_name='product-milestones-list', + ) + versions_url = ProductHyperlinkedModelSerializer( + view_name='product-versions-list', + ) + + class Meta: + model = Product + fields = ( + 'url', + 'prefix', + 'name', + 'description', + 'owner', + 'tickets_url', + 'components_url', + 'milestones_url', + 'versions_url', + ) diff --git a/trackers/api/tests/test_product_api.py b/trackers/api/tests/test_product_api.py index 39b9243..0a43226 100644 --- a/trackers/api/tests/test_product_api.py +++ b/trackers/api/tests/test_product_api.py @@ -15,17 +15,16 @@ # specific language governing permissions and limitations # under the License. -from django.contrib.auth.models import User from django.urls import reverse from rest_framework.test import APITestCase from rest_framework import status from ...models import Product -from ..serializers import ProductSerializer class ProductsApiTest(APITestCase): - """Test for GET all products API """ + """Test for GET all products API""" + def setUp(self): self.ally = Product.objects.create(prefix='ALY', name='Project Alice') self.bob = Product.objects.create(prefix='BOB', name='Project Robert') @@ -47,24 +46,27 @@ class ProductsApiTest(APITestCase): def test_get_all_products(self): response = self.client.get(reverse('product-list')) - products = Product.objects.all() - serializer = ProductSerializer(products, many=True) - self.assertEqual(response.data, serializer.data) + self.assertEqual(response.status_code, status.HTTP_200_OK) + self.assertEqual( + len(response.data), + Product.objects.count(), + ) def test_get_product(self): response = self.client.get( - reverse('product-detail', args=[self.ally.prefix]) + reverse('product-detail', args=[self.ally.prefix]), ) - product = Product.objects.get(prefix=self.ally.prefix) - serializer = ProductSerializer(product) - self.assertEqual(response.data, serializer.data) + self.assertEqual(response.status_code, status.HTTP_200_OK) + self.assertEqual(response.data['prefix'], self.ally.prefix) + self.assertEqual(response.data['name'], self.ally.name) def test_get_invalid_product(self): response = self.client.get( reverse('product-detail', args=['randomnonsense']) ) + self.assertEqual(response.status_code, status.HTTP_404_NOT_FOUND) def test_create_product(self): @@ -72,11 +74,12 @@ class ProductsApiTest(APITestCase): reverse('product-list'), self.new_product_data, ) + product = Product.objects.get(prefix=self.new_product_data['prefix']) - serializer = ProductSerializer(product) self.assertEqual(response.status_code, status.HTTP_201_CREATED) - self.assertEqual(response.data, serializer.data) + self.assertEqual(product.prefix, self.new_product_data['prefix']) + self.assertEqual(product.name, self.new_product_data['name']) def test_create_bad_product(self): response = self.client.post( @@ -91,11 +94,13 @@ class ProductsApiTest(APITestCase): reverse('product-detail', args=[self.ally.prefix]), self.product_data, ) + product = Product.objects.get(prefix=self.product_data['prefix']) - serializer = ProductSerializer(product) self.assertEqual(response.status_code, status.HTTP_200_OK) - self.assertEqual(response.data, serializer.data) + self.assertNotEqual(self.ally.name, product.name) + self.assertEqual(self.product_data['prefix'], product.prefix) + self.assertEqual(self.product_data['name'], product.name) def test_update_product_bad_data(self): response = self.client.put( @@ -109,4 +114,5 @@ class ProductsApiTest(APITestCase): response = self.client.delete( reverse('product-detail', args=[self.ally.prefix]), ) + self.assertEqual(response.status_code, status.HTTP_204_NO_CONTENT) diff --git a/trackers/api/tests/test_ticket_api.py b/trackers/api/tests/test_ticket_api.py new file mode 100644 index 0000000..b8afb0a --- /dev/null +++ b/trackers/api/tests/test_ticket_api.py @@ -0,0 +1,589 @@ +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. + +from django.contrib.auth.models import User +from django.urls import reverse +from rest_framework.test import APITestCase +from rest_framework import status + +from ...models import ( + Component, + Milestone, + Product, + Ticket, + Version, +) +from ..serializers import ( + ComponentSerializer, + MilestoneSerializer, + TicketSerializer, + VersionSerializer, +) + +class TicketApiTest(APITestCase): + """Tests for ticket API""" + + def setUp(self): + self.product = Product.objects.create(prefix="BH", name="Bloodhound") + self.record1 = Ticket.objects.create(product=self.product, summary="BH #1") + self.record2 = Ticket.objects.create(product=self.product, summary="BH #2") + + self.request_data = { + "summary": "Example Summary", + } + + self.bad_request_data = { + "summary": "", + } + + def test_get_all_tickets(self): + response = self.client.get( + reverse("product-tickets-list", kwargs={"product_prefix": "BH"}) + ) + + self.assertEqual(response.status_code, status.HTTP_200_OK) + self.assertEqual( + len(response.data), + Ticket.objects.count(), + ) + + def test_get_ticket(self): + response = self.client.get( + reverse( + "product-tickets-detail", + kwargs={ + "product_prefix": self.record1.product.prefix, + "product_ticket_id": self.record1.product_ticket_id, + }, + ) + ) + + self.assertEqual(response.status_code, status.HTTP_200_OK) + self.assertEqual(response.data['summary'], self.record1.summary) + + def test_get_invalid_ticket(self): + response = self.client.get( + reverse( + "product-tickets-detail", + kwargs={ + "product_prefix": "BH", + "product_ticket_id": 9999, + }, + ) + ) + self.assertEqual(response.status_code, status.HTTP_404_NOT_FOUND) + + def test_create_ticket(self): + response = self.client.post( + reverse("product-tickets-list", kwargs={"product_prefix": "BH"}), + self.request_data, + ) + + self.assertEqual(response.status_code, status.HTTP_201_CREATED) + + record = Ticket.objects.get( + product=self.product, + product_ticket_id=response.data['product_ticket_id'] + ) + + self.assertEqual(response.data['summary'], record.summary) + + def test_create_invalid_product(self): + response = self.client.post( + reverse( + 'product-tickets-list', + kwargs={"product_prefix": "INVALID"} + ), + self.request_data, + ) + + self.assertEqual(response.status_code, status.HTTP_404_NOT_FOUND) + + def test_create_missing_summary(self): + response = self.client.post( + reverse('product-tickets-list', kwargs={"product_prefix": "BH"}), + self.bad_request_data, + ) + + self.assertEqual(response.status_code, status.HTTP_400_BAD_REQUEST) + + def test_update_ticket(self): + response = self.client.put( + reverse( + "product-tickets-detail", + kwargs={ + "product_prefix": self.record1.product.prefix, + "product_ticket_id": self.record1.product_ticket_id, + }, + ), + {"summary": "new summary"}, + ) + + old_summary = self.record1.summary + + record = Ticket.objects.get( + product=self.product, + product_ticket_id=response.data['product_ticket_id'] + ) + + self.assertEqual(response.status_code, status.HTTP_200_OK) + self.assertEqual(response.data['summary'], record.summary) + self.assertNotEqual(old_summary, record.summary) + + def test_update_ticket_bad_data(self): + response = self.client.put( + reverse( + "product-tickets-detail", + kwargs={ + "product_prefix": self.record1.product.prefix, + "product_ticket_id": self.record1.product_ticket_id, + }, + ), + {"summary": ""}, + ) + + self.assertEqual(response.status_code, status.HTTP_400_BAD_REQUEST) + + def test_delete_ticket(self): + response = self.client.delete( + reverse( + 'product-tickets-detail', + kwargs={ + "product_prefix": self.record1.product.prefix, + "product_ticket_id": self.record1.product_ticket_id, + } + ), + ) + self.assertEqual(response.status_code, status.HTTP_204_NO_CONTENT) + with self.assertRaises(Ticket.DoesNotExist): + Ticket.objects.get( + product=self.record1.product.prefix, + product_ticket_id=self.record1.product_ticket_id, + ) + + +class ComponentApiTest(APITestCase): + """Tests for component API""" + + def setUp(self): + self.product = Product.objects.create(prefix="BH", name="Bloodhound") + self.record1 = Component.objects.create(product=self.product, name="Component 1") + self.record2 = Component.objects.create(product=self.product, name="Component 2") + + self.request_data = { + "name": "Example Name", + } + + self.bad_request_data = { + "summary": "", + } + + def test_get_all_components(self): + response = self.client.get( + reverse("product-components-list", kwargs={"product_prefix": "BH"}) + ) + + self.assertEqual(response.status_code, status.HTTP_200_OK) + self.assertEqual( + len(response.data), + Component.objects.count(), + ) + + def test_get_component(self): + response = self.client.get( + reverse( + "product-components-detail", + kwargs={ + "product_prefix": self.record1.product.prefix, + "name": self.record1.name, + }, + ) + ) + + self.assertEqual(response.status_code, status.HTTP_200_OK) + self.assertEqual(response.data['name'], self.record1.name) + + def test_get_missing_component(self): + response = self.client.get( + reverse( + "product-components-detail", + kwargs={ + "product_prefix": "BH", + "name": "not here", + }, + ) + ) + self.assertEqual(response.status_code, status.HTTP_404_NOT_FOUND) + + def test_create_component(self): + response = self.client.post( + reverse( + "product-components-list", + kwargs={"product_prefix": "BH"} + ), + self.request_data, + ) + + self.assertEqual(response.status_code, status.HTTP_201_CREATED) + + record = Component.objects.get( + product=self.product, + name=response.data['name'] + ) + + self.assertEqual(response.data['name'], record.name) + + def test_create_component_with_invalid_product(self): + response = self.client.post( + reverse( + 'product-components-list', + kwargs={"product_prefix": "INVALID"} + ), + self.request_data, + ) + + self.assertEqual(response.status_code, status.HTTP_404_NOT_FOUND) + + def test_update_component(self): + new_name = "new name" + response = self.client.put( + reverse( + "product-components-detail", + kwargs={ + "product_prefix": self.record1.product.prefix, + "name": self.record1.name, + }, + ), + {"name": new_name}, + ) + + record = Component.objects.get( + product=self.product, + name=new_name, + ) + + self.assertEqual(response.status_code, status.HTTP_200_OK) + self.assertNotEqual(new_name, self.record1.name) + self.assertEqual(record.name, new_name) + + def test_update_component_bad_data(self): + response = self.client.put( + reverse( + "product-components-detail", + kwargs={ + "product_prefix": self.record1.product.prefix, + "name": self.record1.name, + }, + ), + {"summary": ""}, + ) + + self.assertEqual(response.status_code, status.HTTP_400_BAD_REQUEST) + + def test_delete_component(self): + response = self.client.delete( + reverse( + 'product-components-detail', + kwargs={ + "product_prefix": self.record1.product.prefix, + "name": self.record1.name, + } + ), + ) + self.assertEqual(response.status_code, status.HTTP_204_NO_CONTENT) + with self.assertRaises(Component.DoesNotExist): + Component.objects.get( + product=self.record1.product.prefix, + name=self.record1.name, + ) + + +class MilestoneApiTest(APITestCase): + """Tests for component API""" + + def setUp(self): + self.product = Product.objects.create(prefix="BH", name="Bloodhound") + self.record1 = Milestone.objects.create(product=self.product, name="Milestone 1") + self.record2 = Milestone.objects.create(product=self.product, name="Milestone 2") + + self.request_data = { + "name": "Example Name", + "description": "Example Description", + } + + self.bad_request_data = { + "summary": "", + } + + def test_get_all_milestones(self): + response = self.client.get( + reverse("product-milestones-list", kwargs={"product_prefix": "BH"}) + ) + + self.assertEqual(response.status_code, status.HTTP_200_OK) + self.assertEqual( + len(response.data), + Milestone.objects.count(), + ) + + def test_get_milestone(self): + response = self.client.get( + reverse( + "product-milestones-detail", + kwargs={ + "product_prefix": self.record1.product.prefix, + "name": self.record1.name, + }, + ) + ) + + self.assertEqual(response.status_code, status.HTTP_200_OK) + self.assertEqual(response.data['name'], self.record1.name) + + def test_get_missing_milestone(self): + response = self.client.get( + reverse( + "product-milestones-detail", + kwargs={ + "product_prefix": "BH", + "name": "not here", + }, + ) + ) + self.assertEqual(response.status_code, status.HTTP_404_NOT_FOUND) + + def test_create_milestone(self): + response = self.client.post( + reverse( + "product-milestones-list", + kwargs={"product_prefix": "BH"} + ), + self.request_data, + ) + + self.assertEqual(response.status_code, status.HTTP_201_CREATED) + + record = Milestone.objects.get( + product=self.product, + name=response.data['name'] + ) + + self.assertEqual(response.data['description'], record.description) + + def test_create_milestone_with_invalid_product(self): + response = self.client.post( + reverse( + 'product-milestones-list', + kwargs={"product_prefix": "INVALID"} + ), + self.request_data, + ) + + self.assertEqual(response.status_code, status.HTTP_404_NOT_FOUND) + + def test_update_component(self): + response = self.client.put( + reverse( + "product-milestones-detail", + kwargs={ + "product_prefix": self.record1.product.prefix, + "name": self.record1.name, + }, + ), + {"name": "new name"}, + ) + + old_name = self.record1.name + + record = Milestone.objects.get( + product=self.product, + name=response.data['name'] + ) + + self.assertEqual(response.status_code, status.HTTP_200_OK) + self.assertEqual(response.data['description'], record.description) + self.assertNotEqual(old_name, record.name) + + def test_update_milestone_bad_data(self): + response = self.client.put( + reverse( + "product-milestones-detail", + kwargs={ + "product_prefix": self.record1.product.prefix, + "name": self.record1.name, + }, + ), + {"summary": ""}, + ) + + self.assertEqual(response.status_code, status.HTTP_400_BAD_REQUEST) + + def test_delete_milestone(self): + response = self.client.delete( + reverse( + 'product-milestones-detail', + kwargs={ + "product_prefix": self.record1.product.prefix, + "name": self.record1.name, + } + ), + ) + self.assertEqual(response.status_code, status.HTTP_204_NO_CONTENT) + with self.assertRaises(Milestone.DoesNotExist): + Milestone.objects.get( + product=self.record1.product.prefix, + name=self.record1.name, + ) + + +class VersionApiTest(APITestCase): + """Tests for component API""" + + def setUp(self): + self.product = Product.objects.create(prefix="BH", name="Bloodhound") + self.record1 = Version.objects.create(product=self.product, name="Version 1") + self.record2 = Version.objects.create(product=self.product, name="Version 2") + + self.request_data = { + "name": "Example Name", + "description": "Example description", + } + + self.bad_request_data = { + "summary": "", + } + + def test_get_all_versions(self): + response = self.client.get( + reverse("product-versions-list", kwargs={"product_prefix": "BH"}) + ) + + self.assertEqual(response.status_code, status.HTTP_200_OK) + self.assertEqual( + len(response.data), + Version.objects.count(), + ) + + def test_get_version(self): + response = self.client.get( + reverse( + "product-versions-detail", + kwargs={ + "product_prefix": self.record1.product.prefix, + "name": self.record1.name, + }, + ) + ) + + self.assertEqual(response.status_code, status.HTTP_200_OK) + self.assertEqual(response.data['name'], self.record1.name) + + def test_get_missing_version(self): + response = self.client.get( + reverse( + "product-versions-detail", + kwargs={ + "product_prefix": "BH", + "name": "not here", + }, + ) + ) + self.assertEqual(response.status_code, status.HTTP_404_NOT_FOUND) + + def test_create_version(self): + response = self.client.post( + reverse( + "product-versions-list", + kwargs={"product_prefix": "BH"} + ), + self.request_data, + ) + + self.assertEqual(response.status_code, status.HTTP_201_CREATED) + + record = Version.objects.get( + product=self.product, + name=response.data['name'] + ) + + self.assertEqual(response.data['description'], record.description) + + def test_create_version_with_invalid_product(self): + response = self.client.post( + reverse( + 'product-versions-list', + kwargs={"product_prefix": "INVALID"} + ), + self.request_data, + ) + + self.assertEqual(response.status_code, status.HTTP_404_NOT_FOUND) + + def test_update_version(self): + new_name = "new name" + response = self.client.put( + reverse( + "product-versions-detail", + kwargs={ + "product_prefix": self.record1.product.prefix, + "name": self.record1.name, + }, + ), + {"name": new_name}, + ) + + old_name = self.record1.name + + record = Version.objects.get( + product=self.product, + name=response.data['name'] + ) + + self.assertEqual(response.status_code, status.HTTP_200_OK) + self.assertNotEqual(new_name, old_name) + self.assertEqual(record.name, new_name) + + def test_update_version_bad_data(self): + response = self.client.put( + reverse( + "product-versions-detail", + kwargs={ + "product_prefix": self.record1.product.prefix, + "name": self.record1.name, + }, + ), + {"summary": ""}, + ) + + self.assertEqual(response.status_code, status.HTTP_400_BAD_REQUEST) + + def test_delete_version(self): + response = self.client.delete( + reverse( + 'product-versions-detail', + kwargs={ + "product_prefix": self.record1.product.prefix, + "name": self.record1.name, + } + ), + ) + self.assertEqual(response.status_code, status.HTTP_204_NO_CONTENT) + with self.assertRaises(Version.DoesNotExist): + Version.objects.get( + product=self.record1.product.prefix, + name=self.record1.name, + ) diff --git a/trackers/api/urls.py b/trackers/api/urls.py index 1ca3741..7dad774 100644 --- a/trackers/api/urls.py +++ b/trackers/api/urls.py @@ -17,6 +17,8 @@ from django.urls import path from django.conf.urls import include +from rest_framework.schemas import get_schema_view +from rest_framework.renderers import JSONOpenAPIRenderer from rest_framework_nested import routers from . import views @@ -27,10 +29,17 @@ router.register('products', views.ProductViewSet) products_router = routers.NestedDefaultRouter(router, 'products', lookup='product') products_router.register('tickets', views.TicketViewSet, basename='product-tickets') +products_router.register('components', views.ComponentViewSet, basename='product-components') +products_router.register('milestones', views.MilestoneViewSet, basename='product-milestones') +products_router.register('versions', views.VersionViewSet, basename='product-versions') urlpatterns = [ path('', include(router.urls)), path('', include(products_router.urls)), + path('openapi', get_schema_view( + title="Apache Bloodhound", + version="0.1.0", + ), name='openapi-schema'), path( 'swagger<str:format>', views.schema_view.without_ui(cache_timeout=0), diff --git a/trackers/api/views.py b/trackers/api/views.py index a1d439a..7995b5f 100644 --- a/trackers/api/views.py +++ b/trackers/api/views.py @@ -59,3 +59,33 @@ class TicketViewSet(viewsets.ModelViewSet): def get_queryset(self, *args, **kwargs): prefix = self.kwargs['product_prefix'] return models.Ticket.objects.filter(product=prefix) + + +class ComponentViewSet(viewsets.ModelViewSet): + queryset = models.Component.objects.all() + serializer_class = serializers.ComponentSerializer + lookup_field = 'name' + + def get_queryset(self, *args, **kwargs): + prefix = self.kwargs['product_prefix'] + return models.Component.objects.filter(product=prefix) + + +class MilestoneViewSet(viewsets.ModelViewSet): + queryset = models.Milestone.objects.all() + serializer_class = serializers.MilestoneSerializer + lookup_field = 'name' + + def get_queryset(self, *args, **kwargs): + prefix = self.kwargs['product_prefix'] + return models.Milestone.objects.filter(product=prefix) + + +class VersionViewSet(viewsets.ModelViewSet): + queryset = models.Version.objects.all() + serializer_class = serializers.VersionSerializer + lookup_field = 'name' + + def get_queryset(self, *args, **kwargs): + prefix = self.kwargs['product_prefix'] + return models.Version.objects.filter(product=prefix) diff --git a/trackers/models.py b/trackers/models.py index 6b48f0e..989e359 100644 --- a/trackers/models.py +++ b/trackers/models.py @@ -15,10 +15,7 @@ # specific language governing permissions and limitations # under the License. -import difflib -import functools import logging -import uuid from django.db import models from django.urls import reverse