Add Create VnfPkgmSubscription API 51/79451/6
authorBharath Thiruveedula <bharath.thiruveedula@verizon.com>
Fri, 1 Mar 2019 12:36:39 +0000 (18:06 +0530)
committerSirisha Gopigiri <sirisha.gopigiri@verizon.com>
Mon, 11 Mar 2019 11:31:02 +0000 (17:01 +0530)
Add SOL 005 Create VnfPkgmSubscription API

Issue-ID: VFC-1221
Change-Id: Id0b7d8c454a8c77cf14b9d299b5afb3e9ab29df5
Signed-off-by: Bharath Thiruveedula <bharath.thiruveedula@verizon.com>
14 files changed:
.gitignore
catalog/packages/biz/vnf_pkg_subscription.py [new file with mode: 0755]
catalog/packages/const.py [changed mode: 0644->0755]
catalog/packages/serializers/nsd_info.py
catalog/packages/serializers/response.py [new file with mode: 0644]
catalog/packages/serializers/subscription_auth_data.py [new file with mode: 0755]
catalog/packages/serializers/vnf_pkg_info.py
catalog/packages/serializers/vnf_pkg_notifications.py [new file with mode: 0755]
catalog/packages/serializers/vnf_pkg_subscription.py [new file with mode: 0755]
catalog/packages/tests/test_vnf_pkg_subscription.py [new file with mode: 0644]
catalog/packages/urls.py [changed mode: 0644->0755]
catalog/packages/views/vnf_package_subscription_views.py [new file with mode: 0755]
catalog/pub/database/models.py
catalog/pub/exceptions.py

index f5ecb0a..54a7acb 100644 (file)
@@ -4,4 +4,6 @@ logs/*.log
 .tox
 target
 htmlcov
-.coverage
\ No newline at end of file
+.coverage
+static/*/*
+test-reports/*
diff --git a/catalog/packages/biz/vnf_pkg_subscription.py b/catalog/packages/biz/vnf_pkg_subscription.py
new file mode 100755 (executable)
index 0000000..20bd68e
--- /dev/null
@@ -0,0 +1,138 @@
+# Copyright (C) 2019 Verizon. All Rights Reserved
+#
+# Licensed 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.
+
+import ast
+import json
+import logging
+import os
+import requests
+import uuid
+
+from collections import Counter
+from rest_framework import status
+
+from catalog.packages import const
+from catalog.pub.database.models import VnfPkgSubscriptionModel
+from catalog.pub.exceptions import VnfPkgSubscriptionException, VnfPkgDuplicateSubscriptionException
+from catalog.pub.utils.values import ignore_case_get
+
+
+logger = logging.getLogger(__name__)
+
+ROOT_FILTERS = {
+    "notificationTypes": "notification_types",
+    "vnfdId": "vnfd_id",
+    "vnfPkgId": "vnf_pkg_id",
+    "operationalState": "operation_states",
+    "usageState": "usage_states"
+}
+
+
+def is_filter_type_equal(new_filter, existing_filter):
+    return Counter(new_filter) == Counter(existing_filter)
+
+
+class CreateSubscription(object):
+
+    def __init__(self, data):
+        self.data = data
+        self.filter = ignore_case_get(self.data, "filters", {})
+        self.callback_uri = ignore_case_get(self.data, "callbackUri")
+        self.authentication = ignore_case_get(self.data, "authentication", {})
+        self.notification_types = ignore_case_get(self.filter, "notificationTypes", [])
+        self.operation_states = ignore_case_get(self.filter, "operationalState", [])
+        self.usage_states = ignore_case_get(self.filter, "usageState", [])
+        self.vnfd_id = ignore_case_get(self.filter, "vnfdId", [])
+        self.vnf_pkg_id = ignore_case_get(self.filter, "vnfPkgId", [])
+        self.vnf_products_from_provider = \
+            ignore_case_get(self.filter, "vnfProductsFromProviders", {})
+
+    def check_callbackuri_connection(self):
+        logger.debug("SubscribeNotification-post::> Sending GET request "
+                     "to %s" % self.callback_uri)
+        try:
+            response = requests.get(self.callback_uri, timeout=2)
+            if response.status_code != status.HTTP_204_NO_CONTENT:
+                raise VnfPkgSubscriptionException("callbackUri %s returns %s status "
+                                                  "code." % (self.callback_uri, response.status_code))
+        except Exception:
+            raise VnfPkgSubscriptionException("callbackUri %s didn't return 204 status"
+                                              "code." % self.callback_uri)
+
+    def do_biz(self):
+        self.subscription_id = str(uuid.uuid4())
+        self.check_callbackuri_connection()
+        self.check_valid_auth_info()
+        self.check_valid()
+        self.save_db()
+        subscription = VnfPkgSubscriptionModel.objects.get(subscription_id=self.subscription_id)
+        if subscription:
+            return subscription.toDict()
+
+    def check_valid_auth_info(self):
+        logger.debug("SubscribeNotification--post::> Validating Auth "
+                     "details if provided")
+        if self.authentication.get("paramsBasic", {}) and \
+                const.BASIC not in self.authentication.get("authType"):
+            raise VnfPkgSubscriptionException('Auth type should be ' + const.BASIC)
+        if self.authentication.get("paramsOauth2ClientCredentials", {}) and \
+                const.OAUTH2_CLIENT_CREDENTIALS not in self.authentication.get("authType"):
+            raise VnfPkgSubscriptionException('Auth type should be ' + const.OAUTH2_CLIENT_CREDENTIALS)
+
+    def check_filter_exists(self, sub):
+        # Check the usage states, operationStates
+        for filter_type in ["operation_states", "usage_states"]:
+            if not is_filter_type_equal(getattr(self, filter_type),
+                                        ast.literal_eval(getattr(sub, filter_type))):
+                return False
+        # If all the above types are same then check id filters
+        for id_filter in ["vnfd_id", "vnf_pkg_id"]:
+            if not is_filter_type_equal(getattr(self, id_filter),
+                                        ast.literal_eval(getattr(sub, id_filter))):
+                return False
+        return True
+
+    def check_valid(self):
+        logger.debug("SubscribeNotification--post::> Checking DB if "
+                     "callbackUri already exists")
+        subscriptions = VnfPkgSubscriptionModel.objects.filter(callback_uri=self.callback_uri)
+        if not subscriptions.exists():
+            return True
+        for subscription in subscriptions:
+            if self.check_filter_exists(subscription):
+                raise VnfPkgDuplicateSubscriptionException(
+                    "Already Subscription (%s) exists with the "
+                    "same callbackUri and filter" % subscription.subscription_id)
+        return True
+
+    def save_db(self):
+        logger.debug("SubscribeNotification--post::> Saving the subscription "
+                     "%s to the database" % self.subscription_id)
+        links = {
+            "self": {
+                "href": os.path.join(const.VNFPKG_SUBSCRIPTION_ROOT_URI, self.subscription_id)
+            }
+        }
+        VnfPkgSubscriptionModel.objects.create(
+            subscription_id=self.subscription_id,
+            callback_uri=self.callback_uri,
+            notification_types=json.dumps(self.notification_types),
+            auth_info=json.dumps(self.authentication),
+            usage_states=json.dumps(self.usage_states),
+            operation_states=json.dumps(self.operation_states),
+            vnf_products_from_provider=json.dumps(self.vnf_products_from_provider),
+            vnfd_id=json.dumps(self.vnfd_id),
+            vnf_pkg_id=json.dumps(self.vnf_pkg_id),
+            links=json.dumps(links))
+        logger.debug('Create Subscription[%s] success', self.subscription_id)
old mode 100644 (file)
new mode 100755 (executable)
index 9d3f6eb..e942ffd
@@ -16,3 +16,13 @@ from catalog.pub.utils.jobutil import enum
 
 PKG_STATUS = enum(CREATED="CREATED", UPLOADING="UPLOADING", PROCESSING="PROCESSING", ONBOARDED="ONBOARDED",
                   IN_USE="IN_USE", NOT_IN_USE="NOT_IN_USE", ENABLED="ENABLED", DISABLED="DISABLED")
+
+AUTH_TYPES = ["BASIC", "OAUTH2_CLIENT_CREDENTIALS", "TLS_CERT"]
+
+BASIC = "BASIC"
+
+OAUTH2_CLIENT_CREDENTIALS = "OAUTH2_CLIENT_CREDENTIALS"
+
+NOTIFICATION_TYPES = ["VnfPackageOnboardingNotification", "VnfPackageChangeNotification"]
+
+VNFPKG_SUBSCRIPTION_ROOT_URI = "api/vnfpkgm/v1/subscriptions/"
index e934a1f..59df1b6 100644 (file)
@@ -29,6 +29,9 @@ class _LinkSerializer(serializers.Serializer):
         allow_null=False
     )
 
+    class Meta:
+        ref_name = 'NSD_LinkSerializer'
+
 
 class NsdInfoSerializer(serializers.Serializer):
     id = serializers.CharField(
diff --git a/catalog/packages/serializers/response.py b/catalog/packages/serializers/response.py
new file mode 100644 (file)
index 0000000..6474078
--- /dev/null
@@ -0,0 +1,28 @@
+# Copyright (C) 2019 Verizon. All Rights Reserved.
+#
+# Licensed 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 rest_framework import serializers
+
+
+class ProblemDetailsSerializer(serializers.Serializer):
+    type = serializers.CharField(help_text="Type", required=False, allow_null=True)
+    title = serializers.CharField(help_text="Title", required=False, allow_null=True)
+    status = serializers.IntegerField(help_text="Status", required=True)
+    detail = serializers.CharField(help_text="Detail", required=True, allow_null=True)
+    instance = serializers.CharField(help_text="Instance", required=False, allow_null=True)
+    additional_details = serializers.ListField(
+        help_text="Any number of additional attributes, as defined in a "
+        "specification or by an implementation.",
+        required=False,
+        allow_null=True)
diff --git a/catalog/packages/serializers/subscription_auth_data.py b/catalog/packages/serializers/subscription_auth_data.py
new file mode 100755 (executable)
index 0000000..5d40e4d
--- /dev/null
@@ -0,0 +1,69 @@
+# Copyright (C) 2019 Verizon. All Rights Reserved
+#
+# Licensed 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 rest_framework import serializers
+
+from catalog.packages import const
+
+
+class OAuthCredentialsSerializer(serializers.Serializer):
+    clientId = serializers.CharField(
+        help_text="Client identifier to be used in the access token "
+        "request of the OAuth 2.0 client credentials grant type.",
+        required=False,
+        max_length=255,
+        allow_null=False)
+    clientPassword = serializers.CharField(
+        help_text="Client password to be used in the access token "
+        "request of the OAuth 2.0 client credentials grant type.",
+        required=False,
+        max_length=255,
+        allow_null=False)
+    tokenEndpoint = serializers.CharField(
+        help_text="The token endpoint from which the access token can "
+        "be obtained.",
+        required=False,
+        max_length=255,
+        allow_null=False)
+
+
+class BasicAuthSerializer(serializers.Serializer):
+    userName = serializers.CharField(
+        help_text="Username to be used in HTTP Basic authentication.",
+        max_length=255,
+        required=False,
+        allow_null=False)
+    password = serializers.CharField(
+        help_text="Password to be used in HTTP Basic authentication.",
+        max_length=255,
+        required=False,
+        allow_null=False)
+
+
+class SubscriptionAuthenticationSerializer(serializers.Serializer):
+    authType = serializers.ListField(
+        child=serializers.ChoiceField(required=True, choices=const.AUTH_TYPES),
+        help_text="Defines the types of Authentication / Authorization "
+        "which the API consumer is willing to accept when "
+        "receiving a notification.",
+        required=True)
+    paramsBasic = BasicAuthSerializer(
+        help_text="Parameters for authentication/authorization using BASIC.",
+        required=False,
+        allow_null=False)
+    paramsOauth2ClientCredentials = OAuthCredentialsSerializer(
+        help_text="Parameters for authentication/authorization using "
+        "OAUTH2_CLIENT_CREDENTIALS.",
+        required=False,
+        allow_null=False)
index 4c63a46..844937f 100644 (file)
@@ -33,6 +33,9 @@ class _LinkSerializer(serializers.Serializer):
         required=True,
         allow_null=False)
 
+    class Meta:
+        ref_name = 'VNF_PKGM_Link_Serializer'
+
 
 class VnfPkgInfoSerializer(serializers.Serializer):
     id = serializers.CharField(
diff --git a/catalog/packages/serializers/vnf_pkg_notifications.py b/catalog/packages/serializers/vnf_pkg_notifications.py
new file mode 100755 (executable)
index 0000000..deb0ea0
--- /dev/null
@@ -0,0 +1,99 @@
+# Copyright (C) 2019 Verizon. All Rights Reserved
+#
+# Licensed 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 rest_framework import serializers
+
+from catalog.packages.const import NOTIFICATION_TYPES
+
+PackageOperationalStateType = ["ENABLED", "DISABLED"]
+PackageUsageStateType = ["IN_USE", "NOT_IN_USE"]
+
+
+class VersionSerializer(serializers.Serializer):
+    vnfSoftwareVersion = serializers.CharField(
+        help_text="VNF software version to match.",
+        max_length=255,
+        required=True,
+        allow_null=False)
+    vnfdVersions = serializers.ListField(
+        child=serializers.CharField(),
+        help_text="Match VNF packages that contain "
+                  "VNF products with certain VNFD versions",
+        required=False,
+        allow_null=False)
+
+
+class vnfProductsSerializer(serializers.Serializer):
+    vnfProductName = serializers.CharField(
+        help_text="Name of the VNF product to match.",
+        max_length=255,
+        required=True,
+        allow_null=False)
+    versions = VersionSerializer(
+        help_text="match VNF packages that contain "
+                  "VNF products with certain versions",
+        required=False,
+        allow_null=False
+    )
+
+
+class vnfProductsProvidersSerializer(serializers.Serializer):
+    vnfProvider = serializers.CharField(
+        help_text="Name of the VNFprovider to match.",
+        max_length=255,
+        required=True,
+        allow_null=False)
+    vnfProducts = vnfProductsSerializer(
+        help_text="match VNF packages that contain "
+                  "VNF products with certain product names, "
+                  "from one particular provider",
+        required=False,
+        allow_null=False
+    )
+
+
+class PkgmNotificationsFilter(serializers.Serializer):
+    notificationTypes = serializers.ListField(
+        child=serializers.ChoiceField(required=True, choices=NOTIFICATION_TYPES),
+        help_text="Match particular notification types",
+        allow_null=False,
+        required=False)
+    vnfProductsFromProviders = vnfProductsProvidersSerializer(
+        help_text="Match VNF packages that contain "
+                  "VNF products from certain providers.",
+        allow_null=False,
+        required=False
+    )
+    vnfdId = serializers.ListField(
+        child=serializers.UUIDField(),
+        help_text="Match VNF packages with a VNFD identifier"
+                  "listed in the attribute",
+        required=False,
+        allow_null=False)
+    vnfPkgId = serializers.ListField(
+        child=serializers.UUIDField(),
+        help_text="Match VNF packages with a VNFD identifier"
+                  "listed in the attribute",
+        required=False,
+        allow_null=False)
+    operationalState = serializers.ListField(
+        child=serializers.ChoiceField(required=True, choices=PackageOperationalStateType),
+        help_text="Operational state of the VNF package.",
+        allow_null=False,
+        required=False)
+    usageState = serializers.ListField(
+        child=serializers.ChoiceField(required=True, choices=PackageUsageStateType),
+        help_text="Operational state of the VNF package.",
+        allow_null=False,
+        required=False)
diff --git a/catalog/packages/serializers/vnf_pkg_subscription.py b/catalog/packages/serializers/vnf_pkg_subscription.py
new file mode 100755 (executable)
index 0000000..8cbac75
--- /dev/null
@@ -0,0 +1,87 @@
+# Copyright (C) 2019 Verizon. All Rights Reserved
+#
+# Licensed 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 rest_framework import serializers
+
+from catalog.packages.serializers import subscription_auth_data
+from catalog.packages.serializers import vnf_pkg_notifications
+
+
+class LinkSerializer(serializers.Serializer):
+    href = serializers.CharField(
+        help_text="URI of the referenced resource.",
+        required=True,
+        allow_null=False,
+        allow_blank=False)
+
+    class Meta:
+        ref_name = 'VNF_SUBSCRIPTION_LINKSERIALIZER'
+
+
+class LinkSelfSerializer(serializers.Serializer):
+    self = LinkSerializer(
+        help_text="URI of this resource.",
+        required=True,
+        allow_null=False)
+
+
+class PkgmSubscriptionRequestSerializer(serializers.Serializer):
+    filters = vnf_pkg_notifications.PkgmNotificationsFilter(
+        help_text="Filter settings for this subscription, "
+                  "to define the subset of all notifications"
+                  " this subscription relates to",
+        required=False,
+        allow_null=False
+    )
+    callbackUri = serializers.URLField(
+        help_text="Callback URI to send"
+                  "the notification",
+        required=True,
+        allow_null=False)
+    authentication = subscription_auth_data.SubscriptionAuthenticationSerializer(
+        help_text="Authentication parameters to configure the use of "
+                  "authorization when sending notifications corresponding to"
+                  "this subscription",
+        required=False,
+        allow_null=False
+    )
+
+
+class PkgmSubscriptionSerializer(serializers.Serializer):
+    id = serializers.UUIDField(
+        help_text="Identifier of this subscription resource.",
+        required=True,
+        allow_null=False)
+    callbackUri = serializers.URLField(
+        help_text="The URI of the endpoint to send the notification to.",
+        required=True,
+        allow_null=False)
+
+    _links = LinkSelfSerializer(
+        help_text="Links to resources related to this resource.",
+        required=True,
+        allow_null=False)
+
+    filter = vnf_pkg_notifications.PkgmNotificationsFilter(
+        help_text="Filter settings for this subscription, "
+                  "to define the subset of all notifications"
+                  " this subscription relates to",
+        required=False,
+        allow_null=False
+    )
+
+
+class PkgmSubscriptionsSerializer(serializers.ListSerializer):
+    child = PkgmSubscriptionSerializer()
+    allow_empty = True
diff --git a/catalog/packages/tests/test_vnf_pkg_subscription.py b/catalog/packages/tests/test_vnf_pkg_subscription.py
new file mode 100644 (file)
index 0000000..7c8b9f1
--- /dev/null
@@ -0,0 +1,100 @@
+# Copyright (C) 2019 Verizon. All Rights Reserved
+#
+# Licensed 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.
+
+import uuid
+import mock
+from rest_framework.test import APIClient
+from django.test import TestCase
+from catalog.pub.database.models import VnfPkgSubscriptionModel
+
+
+class TestNfPackageSubscription(TestCase):
+    def setUp(self):
+        self.client = APIClient()
+        VnfPkgSubscriptionModel.objects.filter().delete()
+        self.vnf_subscription_data = {
+            "filters": {
+                "notificationTypes": [
+                    "VnfPackageOnboardingNotification"
+                ],
+                "vnfProductsFromProviders": {
+                    "vnfProvider": "string",
+                    "vnfProducts": {
+                        "vnfProductName": "string",
+                        "versions": {
+                            "vnfSoftwareVersion": "string",
+                            "vnfdVersions": [
+                                "string"
+                            ]
+                        }
+                    }
+                },
+                "vnfdId": [
+                    "3fa85f64-5717-4562-b3fc-2c963f66afa6"
+                ],
+                "vnfPkgId": [
+                    "3fa85f64-5717-4562-b3fc-2c963f66afa6"
+                ],
+                "operationalState": [
+                    "ENABLED"
+                ],
+                "usageState": [
+                    "IN_USE"
+                ]
+            },
+            "callbackUri": "http://www.vnf1.com/notification",
+            "authentication": {
+                "authType": [
+                    "BASIC"
+                ],
+                "paramsBasic": {
+                    "userName": "string",
+                    "password": "string"
+                }
+            }
+        }
+
+    def tearDown(self):
+        pass
+
+    @mock.patch("requests.get")
+    @mock.patch.object(uuid, 'uuid4')
+    def test_create_vnf_subscription(self, mock_uuid4, mock_requests):
+        temp_uuid = "99442b18-a5c7-11e8-998c-bf1755941f13"
+        mock_requests.return_value.status_code = 204
+        mock_requests.get.status_code = 204
+        mock_uuid4.return_value = temp_uuid
+        response = self.client.post("/api/vnfpkgm/v1/subscriptions", data=self.vnf_subscription_data, format='json')
+        self.assertEqual(201, response.status_code)
+        self.assertEqual(self.vnf_subscription_data["callbackUri"], response.data["callbackUri"])
+        self.assertEqual(temp_uuid, response.data["id"])
+
+    @mock.patch("requests.get")
+    @mock.patch.object(uuid, 'uuid4')
+    def test_create_duplicate_subscriptions(self, mock_uuid4, mock_requests):
+        temp_uuid = "99442b18-a5c7-11e8-998c-bf1755941f13"
+        temp1_uuid = "00342b18-a5c7-11e8-998c-bf1755941f12"
+        mock_requests.return_value.status_code = 204
+        mock_requests.get.status_code = 204
+        mock_uuid4.side_effect = [temp_uuid, temp1_uuid]
+        response = self.client.post("/api/vnfpkgm/v1/subscriptions", data=self.vnf_subscription_data, format='json')
+        self.assertEqual(201, response.status_code)
+        self.assertEqual(self.vnf_subscription_data["callbackUri"], response.data["callbackUri"])
+        self.assertEqual(temp_uuid, response.data["id"])
+        temp_uuid = "00442b18-a5c7-11e8-998c-bf1755941f12"
+        mock_requests.return_value.status_code = 204
+        mock_requests.get.status_code = 204
+        mock_uuid4.return_value = temp_uuid
+        response = self.client.post("/api/vnfpkgm/v1/subscriptions", data=self.vnf_subscription_data, format='json')
+        self.assertEqual(303, response.status_code)
old mode 100644 (file)
new mode 100755 (executable)
index e62bc76..1c20fc2
@@ -15,6 +15,7 @@
 from django.conf.urls import url
 
 from catalog.packages.views import vnf_package_views
+from catalog.packages.views.vnf_package_subscription_views import SubscriptionsView
 from catalog.packages.views import catalog_views, ns_descriptor_views, pnf_descriptor_views
 
 
@@ -48,7 +49,9 @@ urlpatterns = [
     url(r'^api/vnfpkgm/v1/vnf_packages/(?P<vnfPkgId>[0-9a-zA-Z\-\_]+)$', vnf_package_views.vnf_package_rd, name='vnf_package_rd'),
     url(r'^api/vnfpkgm/v1/vnf_packages/(?P<vnfPkgId>[0-9a-zA-Z\-\_]+)/package_content$', vnf_package_views.package_content_ru, name='package_content_ru'),
     url(r'^api/vnfpkgm/v1/vnf_packages/(?P<vnfPkgId>[0-9a-zA-Z\-\_]+)/package_content/upload_from_uri$', vnf_package_views.upload_from_uri_c, name='upload_from_uri_c'),
+    url(r'^api/vnfpkgm/v1/subscriptions$', SubscriptionsView.as_view(), name='subscriptions_create_query'),
+    # url(r'^api/vnfpkgm/v1/subscriptions/(?P<vnfPkgId>[0-9a-zA-Z\-\_]+)$', vnf_package_subscription_views.vnf_package_subscriptions_rc, name='subscriptions_rc'),
     # url(r'^api/vnfpkgm/v1/vnf_packages/(?P<vnfPkgId>[0-9a-zA-Z\-\_]+)/vnfd$', vnfd.as_view(), name='vnfd_r'),# url(r'^api/vnfpkgm/v1/vnf_packages/(?P<vnfPkgId>[0-9a-zA-Z\-\_]+)/artifacts/artifactPath$', artifacts.as_view(), name='artifacts_r'),
-    # url(r'^api/vnfpkgm/v1/subscriptions', vnfpkg_subscriptions.as_view(), name='subscriptions_rc'),
+
     # url(r'^api/vnfpkgm/v1/subscriptions/(?P<subscriptionId>[0-9a-zA-Z\-\_]+)$', vnfpkg_subscription.as_view(), name='subscription_rd'),
 ]
diff --git a/catalog/packages/views/vnf_package_subscription_views.py b/catalog/packages/views/vnf_package_subscription_views.py
new file mode 100755 (executable)
index 0000000..d7449a8
--- /dev/null
@@ -0,0 +1,72 @@
+# Copyright (C) 2019 Verizon. All Rights Reserved
+#
+# Licensed 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.
+
+
+import traceback
+import logging
+
+from drf_yasg.utils import swagger_auto_schema
+from rest_framework import status
+from rest_framework.views import APIView
+from rest_framework.response import Response
+
+from catalog.packages.serializers.vnf_pkg_subscription import PkgmSubscriptionRequestSerializer, \
+    PkgmSubscriptionSerializer
+from catalog.packages.serializers.response import ProblemDetailsSerializer
+from catalog.packages.biz.vnf_pkg_subscription import CreateSubscription
+from catalog.packages.views.common import validate_data
+from catalog.pub.exceptions import VnfPkgDuplicateSubscriptionException
+
+logger = logging.getLogger(__name__)
+VALID_FILTERS = ["callbackUri", "notificationTypes", "vnfdId", "vnfPkgId", "operationalState", "usageState"]
+
+
+def get_problem_details_serializer(status_code, error_message):
+    problem_details = {
+        "status": status_code,
+        "detail": error_message
+    }
+    problem_details_serializer = ProblemDetailsSerializer(data=problem_details)
+    problem_details_serializer.is_valid()
+    return problem_details_serializer
+
+
+class SubscriptionsView(APIView):
+
+    @swagger_auto_schema(
+        request_body=PkgmSubscriptionRequestSerializer,
+        responses={
+            status.HTTP_201_CREATED: PkgmSubscriptionSerializer(),
+            status.HTTP_500_INTERNAL_SERVER_ERROR: "Internal error"
+        }
+    )
+    def post(self, request):
+        logger.debug("Create VNF package Subscription> %s" % request.data)
+        try:
+            vnf_pkg_subscription_request = validate_data(request.data, PkgmSubscriptionRequestSerializer)
+            data = CreateSubscription(vnf_pkg_subscription_request.data).do_biz()
+            subscription_info = validate_data(data, PkgmSubscriptionSerializer)
+            return Response(data=subscription_info.data, status=status.HTTP_201_CREATED)
+        except VnfPkgDuplicateSubscriptionException as e:
+            logger.error(e.message)
+            logger.error(traceback.format_exc())
+            problem_details_serializer = get_problem_details_serializer(status.HTTP_303_SEE_OTHER,
+                                                                        traceback.format_exc())
+            return Response(data=problem_details_serializer.data, status=status.HTTP_303_SEE_OTHER)
+        except Exception as e:
+            logger.error(e.message)
+            logger.error(traceback.format_exc())
+            problem_details_serializer = get_problem_details_serializer(status.HTTP_500_INTERNAL_SERVER_ERROR,
+                                                                        traceback.format_exc())
+            return Response(data=problem_details_serializer.data, status=status.HTTP_500_INTERNAL_SERVER_ERROR)
index 04e39c2..c1e2a8a 100644 (file)
@@ -140,3 +140,38 @@ class JobStatusModel(models.Model):
     def toJSON(self):
         import json
         return json.dumps(dict([(attr, getattr(self, attr)) for attr in [f.name for f in self._meta.fields]]))
+
+
+class VnfPkgSubscriptionModel(models.Model):
+    subscription_id = models.CharField(max_length=255, primary_key=True, db_column='SUBSCRIPTION_ID')
+    callback_uri = models.URLField(db_column="CALLBACK_URI", max_length=255)
+    auth_info = models.TextField(db_column="AUTH_INFO")
+    usage_states = models.TextField(db_column="USAGE_STATES")
+    notification_types = models.TextField(db_column="NOTIFICATION_TYPES")
+    vnfd_id = models.TextField(db_column="VNFD_ID")
+    vnf_pkg_id = models.TextField(db_column="VNF_PKG_ID")
+    operation_states = models.TextField(db_column="OPERATION_STATES")
+    vnf_products_from_provider = \
+        models.TextField(db_column="VNF_PRODUCTS_FROM_PROVIDER")
+    links = models.TextField(db_column="LINKS")
+
+    class Meta:
+        db_table = 'VNF_PKG_SUBSCRIPTION'
+
+    def toDict(self):
+        import json
+        subscription_obj = {
+            "id": self.subscription_id,
+            "callbackUri": self.callback_uri,
+            "_links": json.loads(self.links)
+        }
+        filter_obj = {
+            "notificationTypes": json.loads(self.notification_types),
+            "vnfdId": json.loads(self.vnfd_id),
+            "vnfPkgId": json.loads(self.vnf_pkg_id),
+            "operationalState": json.loads(self.operation_states),
+            "usageState": json.loads(self.usage_states),
+            "vnfProductsFromProviders": json.loads(self.vnf_products_from_provider)
+        }
+        subscription_obj["filter"] = filter_obj
+        return subscription_obj
index a86775e..7f348d6 100644 (file)
@@ -19,3 +19,11 @@ class CatalogException(Exception):
 
 class ResourceNotFoundException(CatalogException):
     pass
+
+
+class VnfPkgSubscriptionException(CatalogException):
+    pass
+
+
+class VnfPkgDuplicateSubscriptionException(CatalogException):
+    pass