summaryrefslogtreecommitdiffstats
path: root/app/api/responders/responder_base.py
diff options
context:
space:
mode:
Diffstat (limited to 'app/api/responders/responder_base.py')
-rw-r--r--app/api/responders/responder_base.py223
1 files changed, 223 insertions, 0 deletions
diff --git a/app/api/responders/responder_base.py b/app/api/responders/responder_base.py
new file mode 100644
index 0000000..479a897
--- /dev/null
+++ b/app/api/responders/responder_base.py
@@ -0,0 +1,223 @@
+###############################################################################
+# Copyright (c) 2017 Koren Lev (Cisco Systems), Yaron Yogev (Cisco Systems) #
+# and others #
+# #
+# All rights reserved. This program and the accompanying materials #
+# are made available under the terms of the Apache License, Version 2.0 #
+# which accompanies this distribution, and is available at #
+# http://www.apache.org/licenses/LICENSE-2.0 #
+###############################################################################
+import json
+import re
+from urllib import parse
+
+from dateutil import parser
+from pymongo import errors
+
+from api.exceptions import exceptions
+from api.validation.data_validate import DataValidate
+from utils.dict_naming_converter import DictNamingConverter
+from utils.inventory_mgr import InventoryMgr
+from utils.logging.full_logger import FullLogger
+from utils.string_utils import jsonify, stringify_object_values_by_types
+
+
+class ResponderBase(DataValidate, DictNamingConverter):
+ UNCHANGED_COLLECTIONS = ["monitoring_config_templates",
+ "environments_config",
+ "messages",
+ "scheduled_scans"]
+
+ def __init__(self):
+ super().__init__()
+ self.log = FullLogger()
+ self.inv = InventoryMgr()
+
+ def set_successful_response(self, resp, body="", status="200"):
+ if not isinstance(body, str):
+ try:
+ body = jsonify(body)
+ except Exception as e:
+ self.log.exception(e)
+ raise ValueError("The response body should be a string")
+ resp.status = status
+ resp.body = body
+
+ def set_error_response(self, title="", code="", message="", body=""):
+ if body:
+ raise exceptions.CalipsoApiException(code, body, message)
+ body = {
+ "error": {
+ "message": message,
+ "code": code,
+ "title": title
+ }
+ }
+ body = jsonify(body)
+ raise exceptions.CalipsoApiException(code, body, message)
+
+ def not_found(self, message="Requested resource not found"):
+ self.set_error_response("Not Found", "404", message)
+
+ def conflict(self,
+ message="The posted data conflicts with the existing data"):
+ self.set_error_response("Conflict", "409", message)
+
+ def bad_request(self, message="Invalid request content"):
+ self.set_error_response("Bad Request", "400", message)
+
+ def unauthorized(self, message="Request requires authorization"):
+ self.set_error_response("Unauthorized", "401", message)
+
+ def validate_query_data(self, data, data_requirements,
+ additional_key_reg=None,
+ can_be_empty_keys=[]):
+ error_message = self.validate_data(data, data_requirements,
+ additional_key_reg,
+ can_be_empty_keys)
+ if error_message:
+ self.bad_request(error_message)
+
+ def check_and_convert_datetime(self, time_key, data):
+ time = data.get(time_key)
+
+ if time:
+ time = time.replace(' ', '+')
+ try:
+ data[time_key] = parser.parse(time)
+ except Exception:
+ self.bad_request("{0} must follow ISO 8610 date and time format,"
+ "YYYY-MM-DDThh:mm:ss.sss+hhmm".format(time_key))
+
+ def check_environment_name(self, env_name):
+ query = {"name": env_name}
+ objects = self.read("environments_config", query)
+ if not objects:
+ return False
+ return True
+
+ def get_object_by_id(self, collection, query, stringify_types, id):
+ objs = self.read(collection, query)
+ if not objs:
+ env_name = query.get("environment")
+ if env_name and \
+ not self.check_environment_name(env_name):
+ self.bad_request("unkown environment: " + env_name)
+ self.not_found()
+ obj = objs[0]
+ stringify_object_values_by_types(obj, stringify_types)
+ if id is "_id":
+ obj['id'] = obj.get('_id')
+ return obj
+
+ def get_objects_list(self, collection, query, page, page_size,
+ projection, stringify_types=None):
+ objects = self.read(collection, query, projection, page, page_size)
+ if not objects:
+ env_name = query.get("environment")
+ if env_name and \
+ not self.check_environment_name(env_name):
+ self.bad_request("unkown environment: " + env_name)
+ self.not_found()
+ for obj in objects:
+ if "id" not in obj and "_id" in obj:
+ obj["id"] = str(obj["_id"])
+ if "_id" in obj:
+ del obj["_id"]
+ if stringify_types:
+ stringify_object_values_by_types(objects, stringify_types)
+
+ return objects
+
+ def parse_query_params(self, req):
+ query_string = req.query_string
+ if not query_string:
+ return {}
+ try:
+ query_params = dict((k, v if len(v) > 1 else v[0])
+ for k, v in
+ parse.parse_qs(query_string,
+ keep_blank_values=True,
+ strict_parsing=True).items())
+ return query_params
+ except ValueError as e:
+ self.bad_request(str("Invalid query string: {0}".format(str(e))))
+
+ def replace_colon_with_dot(self, s):
+ return s.replace(':', '.')
+
+ def get_pagination(self, filters):
+ page_size = filters.get('page_size', 1000)
+ page = filters.get('page', 0)
+ return page, page_size
+
+ def update_query_with_filters(self, filters, filters_keys, query):
+ for filter_key in filters_keys:
+ filter = filters.get(filter_key)
+ if filter is not None:
+ query.update({filter_key: filter})
+
+ def get_content_from_request(self, req):
+ error = ""
+ content = ""
+ if not req.content_length:
+ error = "No data found in the request body"
+ return error, content
+
+ data = req.stream.read()
+ content_string = data.decode()
+ try:
+ content = json.loads(content_string)
+ if not isinstance(content, dict):
+ error = "The data in the request body must be an object"
+ except Exception:
+ error = "The request can not be fulfilled due to bad syntax"
+
+ return error, content
+
+ def get_collection_by_name(self, name):
+ if name in self.UNCHANGED_COLLECTIONS:
+ return self.inv.db[name]
+ return self.inv.collections[name]
+
+ def get_constants_by_name(self, name):
+ constants = self.get_collection_by_name("constants").\
+ find_one({"name": name})
+ # consts = [d['value'] for d in constants['data']]
+ consts = []
+ if not constants:
+ self.log.error('constant type: ' + name +
+ 'no constants exists')
+ return consts
+ for d in constants['data']:
+ try:
+ consts.append(d['value'])
+ except KeyError:
+ self.log.error('constant type: ' + name +
+ ': no "value" key for data: ' + str(d))
+ return consts
+
+ def read(self, collection, matches={}, projection=None, skip=0, limit=1000):
+ collection = self.get_collection_by_name(collection)
+ skip *= limit
+ query = collection.find(matches, projection).skip(skip).limit(limit)
+ return list(query)
+
+ def write(self, document, collection="inventory"):
+ try:
+ self.get_collection_by_name(collection).\
+ insert_one(document)
+ except errors.DuplicateKeyError as e:
+ self.conflict("The key value ({0}) already exists".
+ format(', '.
+ join(self.get_duplicate_key_values(e.details['errmsg']))))
+ except errors.WriteError as e:
+ self.bad_request('Failed to create resource for {0}'.format(str(e)))
+
+ def get_duplicate_key_values(self, err_msg):
+ return ["'{0}'".format(key) for key in re.findall(r'"([^",]+)"', err_msg)]
+
+ def aggregate(self, pipeline, collection):
+ collection = self.get_collection_by_name(collection)
+ data = collection.aggregate(pipeline)
+ return list(data)