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

commit dc439b7433ae52367099f2f4c4ab7a7ba47ec4f0
Author: Gary Martin <g...@apache.org>
AuthorDate: Sat May 15 16:38:53 2021 +0100

    Add product model and api tests
    
     - added product model
     - adds basic api and api tests for product
---
 bh_core/settings.py                                |   3 +-
 bh_core/urls.py                                    |   1 +
 functional_tests.py                                |  18 +++-
 trackers/{apps.py => api/__init__.py}              |   6 --
 trackers/{ => api}/serializers.py                  |   7 ++
 trackers/{apps.py => api/tests/__init__.py}        |   6 --
 trackers/api/tests/test_product_api.py             | 112 +++++++++++++++++++++
 trackers/{ => api}/urls.py                         |   7 +-
 trackers/{ => api}/views.py                        |  13 +--
 trackers/apps.py                                   |   1 +
 trackers/models.py                                 |  32 ++++++
 trackers/tests/__init__.py                         |   0
 .../tests/test_models.py                           |  40 ++++----
 trackers/{ => tests}/tests.py                      |   4 +-
 trackers/urls.py                                   |  16 +--
 trackers/views.py                                  |  41 --------
 16 files changed, 204 insertions(+), 103 deletions(-)

diff --git a/bh_core/settings.py b/bh_core/settings.py
index f0db01a..37ac64b 100644
--- a/bh_core/settings.py
+++ b/bh_core/settings.py
@@ -145,6 +145,5 @@ STATIC_URL = '/static/'
 
 REST_FRAMEWORK = {
     'DEFAULT_PERMISSION_CLASSES': [
-        'rest_framework.permissions.DjangoModelPermissionsOrAnonReadOnly',
-    ]
+    ],
 }
diff --git a/bh_core/urls.py b/bh_core/urls.py
index 408612b..a285126 100644
--- a/bh_core/urls.py
+++ b/bh_core/urls.py
@@ -36,5 +36,6 @@ from django.urls import include, path
 
 urlpatterns = [
     path('', include('trackers.urls')),
+    path('api-auth/', include('rest_framework.urls')),
     path('admin/', admin.site.urls),
 ]
diff --git a/functional_tests.py b/functional_tests.py
index df49437..50e22fb 100644
--- a/functional_tests.py
+++ b/functional_tests.py
@@ -21,7 +21,7 @@ from selenium import webdriver
 import unittest
 
 
-class TicketViewTest(unittest.TestCase):
+class HomePageViewTest(unittest.TestCase):
     def setUp(self):
         self.browser = webdriver.Firefox()
         self.browser.implicitly_wait(3)
@@ -29,11 +29,25 @@ class TicketViewTest(unittest.TestCase):
     def tearDown(self):
         self.browser.quit()
 
-    def test_user_can_add_view_and_delete_ticket(self):
+    def test_user_can_see_homepage(self):
         self.browser.get('http://localhost:8000')
 
         self.assertIn('Bloodhound', self.browser.title)
 
 
+class ApiHomePageViewTest(unittest.TestCase):
+    def setUp(self):
+        self.browser = webdriver.Firefox()
+        self.browser.implicitly_wait(3)
+
+    def tearDown(self):
+        self.browser.quit()
+
+    def test_user_can_see_api_homepage(self):
+        self.browser.get('http://localhost:8000/api')
+
+        self.assertIn('Api Root', self.browser.title)
+
+
 if __name__ == '__main__':
     unittest.main(warnings='ignore')
diff --git a/trackers/apps.py b/trackers/api/__init__.py
similarity index 89%
copy from trackers/apps.py
copy to trackers/api/__init__.py
index 7b9d013..084b296 100644
--- a/trackers/apps.py
+++ b/trackers/api/__init__.py
@@ -14,9 +14,3 @@
 #  KIND, either express or implied.  See the License for the
 #  specific language governing permissions and limitations
 #  under the License.
-
-from django.apps import AppConfig
-
-
-class TrackersConfig(AppConfig):
-    name = 'trackers'
diff --git a/trackers/serializers.py b/trackers/api/serializers.py
similarity index 91%
rename from trackers/serializers.py
rename to trackers/api/serializers.py
index 2616ff1..92db9c6 100644
--- a/trackers/serializers.py
+++ b/trackers/api/serializers.py
@@ -1,6 +1,7 @@
 from django.contrib.auth.models import User, Group
 from rest_framework import serializers
 from trackers import models
+from ..models import Product
 
 
 class UserSerializer(serializers.HyperlinkedModelSerializer):
@@ -15,6 +16,12 @@ class 
GroupSerializer(serializers.HyperlinkedModelSerializer):
         fields = ('url', 'name')
 
 
+class ProductSerializer(serializers.ModelSerializer):
+    class Meta:
+        model = Product
+        fields = '__all__'
+
+
 class TicketSerializer(serializers.ModelSerializer):
     api_url = serializers.SerializerMethodField()
     api_events_url = serializers.SerializerMethodField()
diff --git a/trackers/apps.py b/trackers/api/tests/__init__.py
similarity index 89%
copy from trackers/apps.py
copy to trackers/api/tests/__init__.py
index 7b9d013..084b296 100644
--- a/trackers/apps.py
+++ b/trackers/api/tests/__init__.py
@@ -14,9 +14,3 @@
 #  KIND, either express or implied.  See the License for the
 #  specific language governing permissions and limitations
 #  under the License.
-
-from django.apps import AppConfig
-
-
-class TrackersConfig(AppConfig):
-    name = 'trackers'
diff --git a/trackers/api/tests/test_product_api.py 
b/trackers/api/tests/test_product_api.py
new file mode 100644
index 0000000..39b9243
--- /dev/null
+++ b/trackers/api/tests/test_product_api.py
@@ -0,0 +1,112 @@
+#  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 Product
+from ..serializers import ProductSerializer
+
+
+class ProductsApiTest(APITestCase):
+    """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')
+
+        self.new_product_data = {
+            'prefix': 'CAR',
+            'name': 'Project Caroline',
+        }
+
+        self.product_data = {
+            'prefix': self.ally.prefix,
+            'name': 'Project Alan',
+        }
+
+        self.bad_product_data = {
+            'prefix': self.bob.prefix,
+            'name': '',
+        }
+
+    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)
+
+    def test_get_product(self):
+        response = self.client.get(
+            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)
+
+    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):
+        response = self.client.post(
+            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)
+
+    def test_create_bad_product(self):
+        response = self.client.post(
+            reverse('product-list'),
+            self.bad_product_data,
+        )
+
+        self.assertEqual(response.status_code, status.HTTP_400_BAD_REQUEST)
+
+    def test_update_product(self):
+        response = self.client.put(
+            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)
+
+    def test_update_product_bad_data(self):
+        response = self.client.put(
+            reverse('product-detail', args=[self.bob.prefix]),
+            self.bad_product_data,
+        )
+
+        self.assertEqual(response.status_code, status.HTTP_400_BAD_REQUEST)
+
+    def test_delete_product(self):
+        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/urls.py b/trackers/api/urls.py
similarity index 88%
copy from trackers/urls.py
copy to trackers/api/urls.py
index ffcc408..c9ccf2f 100644
--- a/trackers/urls.py
+++ b/trackers/api/urls.py
@@ -23,16 +23,15 @@ from . import views
 router = routers.DefaultRouter()
 router.register('users', views.UserViewSet)
 router.register('groups', views.GroupViewSet)
+router.register('products', views.ProductViewSet)
 router.register('tickets', views.TicketViewSet)
-router.register('ticketfields', views.TicketFieldViewSet)
 
 ticket_router = routers.DefaultRouter()
 ticket_router.register('ticketevents', views.ChangeEventViewSet)
 
 urlpatterns = [
-    path('', views.home, name='home'),
-    path('api/', include(router.urls)),
-    path('api/tickets/<uuid:id>/', include(ticket_router.urls)),
+    path('', include(router.urls)),
+    path('tickets/<uuid:id>/', include(ticket_router.urls)),
     path('swagger<str:format>', views.schema_view.without_ui(cache_timeout=0), 
name='schema-json'),
     path('swagger/', views.schema_view.with_ui('swagger', cache_timeout=0), 
name='schema-swagger-ui'),
     path('redoc/', views.schema_view.with_ui('redoc', cache_timeout=0), 
name='schema-redoc'),
diff --git a/trackers/views.py b/trackers/api/views.py
similarity index 90%
copy from trackers/views.py
copy to trackers/api/views.py
index f1a6999..34f0d57 100644
--- a/trackers/views.py
+++ b/trackers/api/views.py
@@ -16,12 +16,12 @@
 #  under the License.
 
 from django.contrib.auth.models import User, Group
-from django.http import HttpResponse
 from drf_yasg.views import get_schema_view
 from drf_yasg import openapi
 from rest_framework import permissions, viewsets
 from . import serializers
-from . import models
+from ..models import Product
+from trackers import models
 
 
 schema_view = get_schema_view(
@@ -34,10 +34,6 @@ schema_view = get_schema_view(
 )
 
 
-def home(request):
-    return HttpResponse('<html><title>Bloodhound Trackers</title></html>')
-
-
 class UserViewSet(viewsets.ModelViewSet):
     queryset = User.objects.all()
     serializer_class = serializers.UserSerializer
@@ -48,6 +44,11 @@ class GroupViewSet(viewsets.ModelViewSet):
     serializer_class = serializers.GroupSerializer
 
 
+class ProductViewSet(viewsets.ModelViewSet):
+    queryset = Product.objects.all()
+    serializer_class = serializers.ProductSerializer
+
+
 class TicketFieldViewSet(viewsets.ModelViewSet):
     queryset = models.TicketField.objects.all()
     serializer_class = serializers.TicketFieldSerializer
diff --git a/trackers/apps.py b/trackers/apps.py
index 7b9d013..9615c59 100644
--- a/trackers/apps.py
+++ b/trackers/apps.py
@@ -19,4 +19,5 @@ from django.apps import AppConfig
 
 
 class TrackersConfig(AppConfig):
+    default_auto_field = 'django.db.models.BigAutoField'
     name = 'trackers'
diff --git a/trackers/models.py b/trackers/models.py
index 7baab71..f7fb321 100644
--- a/trackers/models.py
+++ b/trackers/models.py
@@ -26,6 +26,38 @@ from django.urls import reverse
 logger = logging.getLogger(__name__)
 
 
+class Product(models.Model):
+    prefix = models.TextField(primary_key=True)
+    name = models.TextField()
+    description = models.TextField(blank=True, null=True)
+    owner = models.TextField(blank=True, null=True)
+
+    class Meta:
+        db_table = 'bloodhound_product'
+
+
+class ProductConfig(models.Model):
+    """Possibly legacy table - keeping for now"""
+    product = models.ForeignKey(Product, on_delete=models.CASCADE)
+    section = models.TextField()
+    option = models.TextField()
+    value = models.TextField(blank=True, null=True)
+
+    class Meta:
+        db_table = 'bloodhound_productconfig'
+        unique_together = (('product', 'section', 'option'),)
+
+
+class ProductResourceMap(models.Model):
+    """Possibly legacy model - keeping for now"""
+    product_id = models.ForeignKey(Product, on_delete=models.CASCADE)
+    resource_type = models.TextField(blank=True, null=True)
+    resource_id = models.TextField(blank=True, null=True)
+
+    class Meta:
+        db_table = 'bloodhound_productresourcemap'
+
+
 class ModelCommon(models.Model):
     id = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=False)
     created = models.DateTimeField(auto_now_add=True, editable=False)
diff --git a/trackers/tests/__init__.py b/trackers/tests/__init__.py
new file mode 100644
index 0000000..e69de29
diff --git a/functional_tests.py b/trackers/tests/test_models.py
similarity index 54%
copy from functional_tests.py
copy to trackers/tests/test_models.py
index df49437..13191fc 100644
--- a/functional_tests.py
+++ b/trackers/tests/test_models.py
@@ -1,5 +1,3 @@
-#!/usr/bin/env python
-
 #  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
@@ -17,23 +15,27 @@
 #  specific language governing permissions and limitations
 #  under the License.
 
-from selenium import webdriver
-import unittest
+from django.test import TestCase
+from ..models import Product
 
 
-class TicketViewTest(unittest.TestCase):
+class ProductTest(TestCase):
+    """Test modules for Product model"""
     def setUp(self):
-        self.browser = webdriver.Firefox()
-        self.browser.implicitly_wait(3)
-
-    def tearDown(self):
-        self.browser.quit()
-
-    def test_user_can_add_view_and_delete_ticket(self):
-        self.browser.get('http://localhost:8000')
-
-        self.assertIn('Bloodhound', self.browser.title)
-
-
-if __name__ == '__main__':
-    unittest.main(warnings='ignore')
+        Product.objects.create(
+            prefix='BHD',
+            name='Bloodhound Legacy',
+            description='The original Apache Bloodhound',
+        )
+        Product.objects.create(
+            prefix='BH',
+            name='Bloodhound',
+            description='The future of Apache Bloodhound',
+        )
+
+    def test_product_name(self):
+        bhd = Product.objects.get(prefix='BHD')
+        bh = Product.objects.get(prefix='BH')
+
+        self.assertEqual(bhd.name, "Bloodhound Legacy")
+        self.assertEqual(bh.name, "Bloodhound")
diff --git a/trackers/tests.py b/trackers/tests/tests.py
similarity index 97%
rename from trackers/tests.py
rename to trackers/tests/tests.py
index cbc666b..ef6bcb4 100644
--- a/trackers/tests.py
+++ b/trackers/tests/tests.py
@@ -18,7 +18,7 @@
 from django.http import HttpRequest
 from django.test import TestCase
 from django.urls import resolve
-from trackers.views import home
+from ..views import home
 
 
 class HomePageTest(TestCase):
@@ -35,7 +35,7 @@ class HomePageTest(TestCase):
         self.assertTrue(response.content.endswith(b'</html>'))
 
 
-from trackers.models import Ticket
+from ..models import Ticket
 
 class TicketModelTest(TestCase):
     def test_last_update_on_create_returns_created_date(self):
diff --git a/trackers/urls.py b/trackers/urls.py
index ffcc408..c62892f 100644
--- a/trackers/urls.py
+++ b/trackers/urls.py
@@ -17,23 +17,9 @@
 
 from django.urls import path
 from django.conf.urls import include
-from rest_framework import routers
 from . import views
 
-router = routers.DefaultRouter()
-router.register('users', views.UserViewSet)
-router.register('groups', views.GroupViewSet)
-router.register('tickets', views.TicketViewSet)
-router.register('ticketfields', views.TicketFieldViewSet)
-
-ticket_router = routers.DefaultRouter()
-ticket_router.register('ticketevents', views.ChangeEventViewSet)
-
 urlpatterns = [
     path('', views.home, name='home'),
-    path('api/', include(router.urls)),
-    path('api/tickets/<uuid:id>/', include(ticket_router.urls)),
-    path('swagger<str:format>', views.schema_view.without_ui(cache_timeout=0), 
name='schema-json'),
-    path('swagger/', views.schema_view.with_ui('swagger', cache_timeout=0), 
name='schema-swagger-ui'),
-    path('redoc/', views.schema_view.with_ui('redoc', cache_timeout=0), 
name='schema-redoc'),
+    path('api/', include('trackers.api.urls')),
 ]
diff --git a/trackers/views.py b/trackers/views.py
index f1a6999..0861c17 100644
--- a/trackers/views.py
+++ b/trackers/views.py
@@ -15,49 +15,8 @@
 #  specific language governing permissions and limitations
 #  under the License.
 
-from django.contrib.auth.models import User, Group
 from django.http import HttpResponse
-from drf_yasg.views import get_schema_view
-from drf_yasg import openapi
-from rest_framework import permissions, viewsets
-from . import serializers
-from . import models
-
-
-schema_view = get_schema_view(
-    openapi.Info(
-        title='Bloodhound Core API',
-        default_version='v1',
-    ),
-    public=True,
-    permission_classes=(permissions.AllowAny,),
-)
 
 
 def home(request):
     return HttpResponse('<html><title>Bloodhound Trackers</title></html>')
-
-
-class UserViewSet(viewsets.ModelViewSet):
-    queryset = User.objects.all()
-    serializer_class = serializers.UserSerializer
-
-
-class GroupViewSet(viewsets.ModelViewSet):
-    queryset = Group.objects.all()
-    serializer_class = serializers.GroupSerializer
-
-
-class TicketFieldViewSet(viewsets.ModelViewSet):
-    queryset = models.TicketField.objects.all()
-    serializer_class = serializers.TicketFieldSerializer
-
-
-class TicketViewSet(viewsets.ModelViewSet):
-    queryset = models.Ticket.objects.all()
-    serializer_class = serializers.TicketSerializer
-
-
-class ChangeEventViewSet(viewsets.ModelViewSet):
-    queryset = models.ChangeEvent.objects.all()
-    serializer_class = serializers.ChangeEventSerializer

Reply via email to