1. Remove the mandatory dependency on MSB
[modeling/etsicatalog.git] / catalog / packages / biz / nsdm_subscription.py
1 # Copyright (C) 2019 Verizon. All Rights Reserved
2 #
3 # Licensed under the Apache License, Version 2.0 (the "License");
4 # you may not use this file except in compliance with the License.
5 # You may obtain a copy of the License at
6 #
7 #         http://www.apache.org/licenses/LICENSE-2.0
8 #
9 # Unless required by applicable law or agreed to in writing, software
10 # distributed under the License is distributed on an "AS IS" BASIS,
11 # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 # See the License for the specific language governing permissions and
13 # limitations under the License.
14
15 import ast
16 import json
17 import logging
18 import uuid
19 from collections import Counter
20
21 import requests
22 from rest_framework import status
23
24 from catalog.packages import const
25 from catalog.pub.database.models import NsdmSubscriptionModel
26 from catalog.pub.exceptions import CatalogException, \
27     NsdmBadRequestException, NsdmDuplicateSubscriptionException, SubscriptionDoesNotExistsException
28 from catalog.pub.utils.values import ignore_case_get
29
30 logger = logging.getLogger(__name__)
31
32 PARAMSBASICKEYS = ["userName", "password"]
33
34 PARAMSOAUTH2CLIENTCREDENTIALSKEYS = ["clientId", "clientPassword",
35                                      "tokenEndpoint"]
36
37
38 def is_filter_type_equal(new_filter, existing_filter):
39     return Counter(list(set(new_filter))) == Counter(existing_filter)
40
41
42 class NsdmSubscription:
43
44     def __init__(self):
45         pass
46
47     def query_single_subscription(self, subscription_id):
48         logger.debug("Start Query Subscription... ")
49         subscription = \
50             NsdmSubscriptionModel.objects.filter(
51                 subscriptionid=subscription_id)
52         if not subscription.exists():
53             raise SubscriptionDoesNotExistsException(
54                 "Subscription(%s) doesn't exist" % subscription_id)
55         logger.debug("Subscription found... ")
56         return self.fill_resp_data(subscription[0])
57
58     def delete_single_subscription(self, subscription_id):
59         logger.debug("Start Delete Subscription... ")
60         subscription = \
61             NsdmSubscriptionModel.objects.filter(
62                 subscriptionid=subscription_id)
63         if not subscription.exists():
64             raise SubscriptionDoesNotExistsException(
65                 "Subscription(%s) doesn't exist" % subscription_id)
66         subscription.delete()
67         logger.debug("Deleted Subscription... ")
68
69     def query_multi_subscriptions(self, query_params):
70         self.params = query_params
71         query_data = {}
72         logger.debug("Start QueryMultiSubscriptions get --> "
73                      "Check for filters in query params" % self.params)
74         for query, value in list(self.params.items()):
75             if query in const.NSDM_NOTIFICATION_FILTERS and value:
76                 query_data[query + '__icontains'] = json.dumps(list(set(value)))
77         # Query the database with filter if the request
78         # has fields in request params, else fetch all records
79         if query_data:
80             subscriptions = NsdmSubscriptionModel.objects.filter(**query_data)
81         else:
82             subscriptions = NsdmSubscriptionModel.objects.all()
83         if not subscriptions.exists():
84             raise SubscriptionDoesNotExistsException("Subscriptions doesn't exist")
85         return [self.fill_resp_data(subscription)
86                 for subscription in subscriptions]
87
88     def check_callbackuri_connection(self):
89         logger.debug("Create Subscription --> Test Callback URI --"
90                      "Sending GET request to %s" % self.callback_uri)
91         try:
92             response = requests.get(self.callback_uri, timeout=2, verify=False)
93             if response.status_code != status.HTTP_204_NO_CONTENT:
94                 raise CatalogException("callbackUri %s returns %s status "
95                                        "code." % (self.callback_uri,
96                                                   response.status_code))
97         except Exception:
98             raise CatalogException("callbackUri %s didn't return 204 status"
99                                    "code." % self.callback_uri)
100
101     def fill_resp_data(self, subscription):
102         subscription_filter = dict()
103         for filter_type in const.NSDM_NOTIFICATION_FILTERS:
104             if subscription.__dict__[filter_type]:
105                 subscription_filter[filter_type] = \
106                     ast.literal_eval(subscription.__dict__[filter_type])
107         resp_data = {
108             'id': subscription.subscriptionid,
109             'callbackUri': subscription.callback_uri,
110             'filter': subscription_filter,
111             '_links': json.loads(subscription.links)
112         }
113         return resp_data
114
115     def create(self, data):
116         logger.debug("Start Create Subscription... ")
117         self.filter = ignore_case_get(data, "filter", {})
118         self.callback_uri = ignore_case_get(data, "callbackUri")
119         self.authentication = ignore_case_get(data, "authentication", {})
120         self.subscription_id = str(uuid.uuid4())
121         self.check_callbackuri_connection()
122         self.check_valid_auth_info()
123         self.check_filter_types()
124         self.check_valid()
125         self.save_db()
126         subscription = \
127             NsdmSubscriptionModel.objects.get(
128                 subscriptionid=self.subscription_id)
129         return self.fill_resp_data(subscription)
130
131     def check_filter_types(self):
132         # Check if both nsdId and nsdInfoId
133         # or pnfdId and pnfdInfoId are present
134         logger.debug("Create Subscription --> Validating Filters... ")
135         if self.filter and \
136                 self.filter.get("nsdId", "") and \
137                 self.filter.get("nsdInfoId", ""):
138             raise NsdmBadRequestException("Notification Filter should contain"
139                                           " either nsdId or nsdInfoId")
140         if self.filter and \
141                 self.filter.get("pnfdId", "") and \
142                 self.filter.get("pnfdInfoIds", ""):
143             raise NsdmBadRequestException("Notification Filter should contain"
144                                           " either pnfdId or pnfdInfoIds")
145
146     def check_valid_auth_info(self):
147         logger.debug("Create Subscription --> Validating Auth "
148                      "details if provided... ")
149         if self.authentication.get("paramsBasic", {}) and \
150                 const.BASIC not in self.authentication.get("authType", ''):
151             raise NsdmBadRequestException('Auth type should be ' + const.BASIC)
152         if self.authentication.get("paramsOauth2ClientCredentials", {}) and \
153                 const.OAUTH2_CLIENT_CREDENTIALS not in \
154                 self.authentication.get("authType", ''):
155             raise NsdmBadRequestException('Auth type should '
156                                           'be ' + const.OAUTH2_CLIENT_CREDENTIALS)
157         if const.BASIC in self.authentication.get("authType", '') and \
158                 "paramsBasic" in list(self.authentication.keys()) and \
159                 not is_filter_type_equal(PARAMSBASICKEYS, list(
160                     self.authentication.get("paramsBasic").keys())):
161             raise NsdmBadRequestException('userName and password needed '
162                                           'for ' + const.BASIC)
163         if const.OAUTH2_CLIENT_CREDENTIALS in \
164                 self.authentication.get("authType", '') and \
165                 "paramsOauth2ClientCredentials" in \
166                 list(self.authentication.keys()) and \
167                 not is_filter_type_equal(PARAMSOAUTH2CLIENTCREDENTIALSKEYS, list(
168                     self.authentication.get("paramsOauth2ClientCredentials").keys())):
169             raise NsdmBadRequestException('clientId, clientPassword and '
170                                           'tokenEndpoint required '
171                                           'for ' + const.OAUTH2_CLIENT_CREDENTIALS)
172
173     def check_filter_exists(self, subscription):
174         for filter_type in const.NSDM_NOTIFICATION_FILTERS:
175             if not is_filter_type_equal(self.filter.get(filter_type, []),
176                                         ast.literal_eval(
177                                             getattr(subscription,
178                                                     filter_type))):
179                 return False
180         return True
181
182     def check_valid(self):
183         logger.debug("Create Subscription --> Checking DB if "
184                      "same subscription has already existed... ")
185         subscriptions = \
186             NsdmSubscriptionModel.objects.filter(
187                 callback_uri=self.callback_uri)
188
189         for subscription in subscriptions:
190             if self.check_filter_exists(subscription):
191                 links = json.loads(subscription.links)
192                 logger.error(
193                     "Subscription has already existed with the "
194                     "same callbackUri and filter:%s" % links)
195                 raise NsdmDuplicateSubscriptionException(
196                     "/%s" % (links["self"]["href"]))
197
198     def save_db(self):
199         logger.debug("Create Subscription --> Saving the subscription "
200                      "%s to the database" % self.subscription_id)
201         links = {
202             "self": {
203                 "href":
204                     const.NSDM_SUBSCRIPTION_ROOT_URI + self.subscription_id
205             }
206         }
207         subscription_save_db = {
208             "subscriptionid": self.subscription_id,
209             "callback_uri": self.callback_uri,
210             "auth_info": json.dumps(self.authentication),
211             "links": json.dumps(links)
212         }
213         for filter_type in const.NSDM_NOTIFICATION_FILTERS:
214             if self.filter:
215                 subscription_save_db[filter_type] = json.dumps(
216                     list(set(self.filter.get(filter_type, []))))
217         NsdmSubscriptionModel.objects.create(**subscription_save_db)
218         logger.debug('Create Subscription[%s] success', self.subscription_id)