Leverage DMaaP adapter to send error code and error message

Send error code and error message to the caller if error occurs

SMO-28

Signed-off-by: santanude <santanu.de@xoriant.com>
Change-Id: I6c7cb234319581afbd431e2bcbf2bc9a9b9934fd
Signed-off-by: santanude <santanu.de@xoriant.com>
diff --git a/dmaapadapter/adapter/code/app_config.py b/dmaapadapter/adapter/code/app_config.py
index fb5be95..7eb2029 100644
--- a/dmaapadapter/adapter/code/app_config.py
+++ b/dmaapadapter/adapter/code/app_config.py
@@ -43,34 +43,43 @@
         config = configparser.ConfigParser()
         config.read(config_file)
 
-        self.kafka_broker = config.get(config_section,
-                                       'kafka_broker',
-                                       vars=overrides)
+        self.kafka_broker = config.get(config_section, 'kafka_broker', vars=overrides)
         log_file = config.get(config_section, 'log_file', vars=overrides)
         log_level = config.get(config_section, 'log_level', vars=overrides)
 
-        handler = logging.handlers.RotatingFileHandler(log_file,
-                                                       maxBytes=1000000,
-                                                       backupCount=10)
-        formatter = logging.Formatter('%(asctime)s %(name)s - '
-                                      '%(levelname)s - %(message)s',
-                                      '%Y-%m-%d %H:%M:%S.%f %z')
-        handler.setFormatter(formatter)
-        self.logger.addHandler(handler)
-
-        # we are going to set the log level
-        if (log_level == 'DEBUG'):
-            self.logger.setLevel(logging.DEBUG)
-        elif (log_level == 'ERROR'):
-            self.logger.setLevel(logging.ERROR)
-        else:
-            self.logger.setLevel(logging.INFO)
-
-        self.logger.info('Log level {} and log file {} : '
-                         .format(log_level, log_file))
+        self.setLogger(log_file, log_level)
 
     def getKafkaBroker(self):
         return self.kafka_broker
 
     def getLogger(self):
         return self.logger
+
+    def setLogger(self, log_file, log_level):
+        rfh = logging.handlers.RotatingFileHandler(
+            filename=log_file,
+            mode='w',
+            maxBytes=1000000,
+            backupCount=10,
+            encoding=None,
+            delay=0
+        )
+
+        logging.basicConfig(
+            format="%(asctime)s %(name)-8s %(levelname)-8s %(message)s",
+            datefmt="%Y-%m-%d %H:%M:%S.%f %z",
+            handlers=[rfh]
+        )
+
+        logger = logging.getLogger("DMaaP")
+
+        # we are going to set the log level
+        if (log_level == 'DEBUG'):
+            logger.setLevel(logging.DEBUG)
+        elif (log_level == 'ERROR'):
+            logger.setLevel(logging.ERROR)
+        else:
+            logger.setLevel(logging.INFO)
+
+        logger.info('Log level {} and log file {} : '.format(log_level, log_file))
+        self.logger = logger
diff --git a/dmaapadapter/adapter/code/consumer.py b/dmaapadapter/adapter/code/consumer.py
index 62d744c..06f0ba6 100644
--- a/dmaapadapter/adapter/code/consumer.py
+++ b/dmaapadapter/adapter/code/consumer.py
@@ -14,10 +14,10 @@
 #
 
 from confluent_kafka.admin import AdminClient
-from confluent_kafka import Consumer
+from confluent_kafka import Consumer, KafkaError
 from app_config import AppConfig
-import logging
 import sys
+import logging.handlers
 
 
 class EventConsumer:
@@ -29,9 +29,9 @@
         self.logger = appConfig.getLogger()
         self.broker = appConfig.getKafkaBroker()
 
-    def consumeEvents(self, topic, consumergroup, consumerid, limit, timeout):
+    def consumeEvents(self, prepareResponse, topic, consumergroup, consumerid, limit, timeout):
         self.logger.debug("topic={}, consumergroup={}, consumerid={}, limit={}, timeout={} "
-                          .format(topic, consumergroup, consumerid, limit, timeout))
+                     .format(topic, consumergroup, consumerid, limit, timeout))
         consumer_config = {
             'bootstrap.servers': self.broker,
             'group.id': consumergroup,
@@ -47,6 +47,7 @@
         try:
             ctr = 0
             content_size = 0
+            response_code = 200
             while True:
                 if (ctr == int(limit)):
                     break
@@ -56,85 +57,103 @@
                 # read single message at a time
                 msg = consumer.poll(timeout=int(timeout))
                 if msg is None:
-                    self.logger.debug("No records ")
+                    self.logger.debug("No new records exists in topic {} of broker {}".format(topic, self.broker))
                     break
                 if msg.error():
+                    if (msg.error().code() == KafkaError.UNKNOWN_TOPIC_OR_PART):
+                        response_code = 409
                     self.logger.debug("Error reading message : {}".format(msg.error()))
                     break
-                event = msg.value().decode('utf8').replace("'", '"')
-                content_size = content_size + sys.getsizeof(event)
-                event_list.append(event)
+
+                content_size = content_size + sys.getsizeof(msg.value().decode('utf8').replace("'", '"'))
+                event_list.append(msg.value().decode('utf8').replace("'", '"'))
                 consumer.commit()
 
+            prepareResponse.setResponseCode(response_code)
+            if (response_code == 409):
+                prepareResponse.setResponseMsg("Unable to read the messages from the topic")
+            else:
+                prepareResponse.setResponseMsg(event_list)
+
         except Exception as ex:
-            self.logger.debug('Failed to get event information due to unexpected reason! {0}'.format(ex))
+            self.logger.error("Failed to get event information due to unexpected reason! {0}".format(ex))
+            prepareResponse.setResponseCode(500)
+            prepareResponse.setResponseMsg("Failed to return the events")
 
         finally:
             self.logger.debug("closing consumer")
             consumer.close()
-            return event_list
 
 
 class TopicConsumer:
 
     broker = ""
-    logger = logging.getLogger()
     timeout = 10
+    logger = logging.getLogger()
 
     def __init__(self):
         appConfig = AppConfig()
         self.logger = appConfig.getLogger()
         self.broker = appConfig.getKafkaBroker()
 
-    def getTopics(self):
+    def getTopics(self, prepareResponse):
         try:
+            topic_list = []
             adminClient = AdminClient({"bootstrap.servers": self.broker})
             ListTopicsResult = adminClient.list_topics(timeout=self.timeout)
-            topic_list = []
 
             for key, value in ListTopicsResult.topics.items():
                 topic_list.append(key)
 
-            dict = {'topics': topic_list}
-            return dict
+            dict = {"topics": topic_list}
+            prepareResponse.setResponseCode(200)
+            prepareResponse.setResponseMsg(dict)
 
         except Exception as ex:
-            self.logger.debug('Failed to get topic information due to unexpected reason! {0}'.format(ex))
+            self.logger.error('Failed to get topic information due to unexpected reason! {0}'.format(ex))
+            prepareResponse.setResponseCode(500)
+            prepareResponse.setResponseMsg("Failed to return the topics")
 
-    def listAllTopics(self):
+    def listAllTopics(self, prepareResponse):
         try:
             topic_list = []
             adminClient = AdminClient({"bootstrap.servers": self.broker})
             ListTopicsResult = adminClient.list_topics(timeout=self.timeout)
 
             for key, value in ListTopicsResult.topics.items():
-                dict = {'topicName': key,
-                        'owner': '',
-                        'txenabled': False
-                        }
+                dict = {"topicName": key, "owner": "", "txenabled": False}
                 topic_list.append(dict)
 
-            dict2 = {'topics': topic_list}
-            return dict2
+            dict2 = {"topics": topic_list}
+            prepareResponse.setResponseCode(200)
+            prepareResponse.setResponseMsg(dict2)
         except Exception as ex:
-            self.logger.debug('Failed to get list of topic information due to unexpected reason! {0}'.format(ex))
+            self.logger.error('Failed to get list of topic information due to unexpected reason! {0}'.format(ex))
+            prepareResponse.setResponseCode(500)
+            prepareResponse.setResponseMsg("Failed to return the topics")
 
-    def getTopicDetails(self, topic):
+    def getTopicDetails(self, prepareResponse, topic):
         try:
             adminClient = AdminClient({"bootstrap.servers": self.broker})
             ListTopicsResult = adminClient.list_topics(timeout=self.timeout)
 
+            topic_exists = False
             for key, value in ListTopicsResult.topics.items():
                 if (key == topic):
-                    dict = {'name': key,
-                            'owner': '',
-                            'description': '',
-                            'readerAcl': {"enabled": True, "users": []},
-                            'writerAcl': {"enabled": True, "users": []}
-                            }
-                    return dict
+                    topic_exists = True
+                    dict = {"name": key,
+                            "owner": "",
+                            "description": "",
+                            "readerAcl": {"enabled": True, "users": []},
+                            "writerAcl": {"enabled": True, "users": []}}
+                    prepareResponse.setResponseCode(200)
+                    prepareResponse.setResponseMsg(dict)
 
-            self.logger.debug("Topic {} does not exists! ".format(topic))
-            return "Topic [" + topic + "] does not exists"
+            if (topic_exists is False):
+                self.logger.debug("Topic '{}' does not exists! ".format(topic))
+                prepareResponse.setResponseCode(404)
+                prepareResponse.setResponseMsg("Topic [" + topic + "] not found")
         except Exception as ex:
-            self.logger.debug('Failed to get topic detail due to unexpected reason! {0}'.format(ex))
+            self.logger.error('Failed to get topic detail due to unexpected reason! {0}'.format(ex))
+            prepareResponse.setResponseCode(500)
+            prepareResponse.setResponseMsg("Failed to return the topics")
diff --git a/dmaapadapter/adapter/code/dmaap_adapter.py b/dmaapadapter/adapter/code/dmaap_adapter.py
index 4db2935..e3042e2 100644
--- a/dmaapadapter/adapter/code/dmaap_adapter.py
+++ b/dmaapadapter/adapter/code/dmaap_adapter.py
@@ -16,7 +16,7 @@
 import flask
 from flask import request
 from consumer import EventConsumer, TopicConsumer
-import json
+from prepare_response import PrepareResponse
 
 app = flask.Flask(__name__)
 app.config["DEBUG"] = True
@@ -30,18 +30,22 @@
 
 @app.route(api_base_url + '/topics', methods=['GET'])
 def get_all_topics():
+    prepareResponse = PrepareResponse()
     topicConsumer = TopicConsumer()
-    response = app.response_class(response=json.dumps(topicConsumer.getTopics()),
-                                  status=200,
+    topicConsumer.getTopics(prepareResponse)
+    response = app.response_class(response=prepareResponse.getResponseMsg(),
+                                  status=prepareResponse.getResponseCode(),
                                   mimetype='application/json')
     return response
 
 
 @app.route(api_base_url + '/topics/listAll', methods=['GET'])
 def listall_topics():
+    prepareResponse = PrepareResponse()
     topicConsumer = TopicConsumer()
-    response = app.response_class(response=json.dumps(topicConsumer.listAllTopics()),
-                                  status=200,
+    topicConsumer.listAllTopics(prepareResponse)
+    response = app.response_class(response=prepareResponse.getResponseMsg(),
+                                  status=prepareResponse.getResponseCode(),
                                   mimetype='application/json')
     return response
 
@@ -49,9 +53,11 @@
 @app.route(api_base_url + '/topics/<topic>', methods=['GET'])
 def topic_details(topic):
     assert topic == request.view_args['topic']
+    prepareResponse = PrepareResponse()
     topicConsumer = TopicConsumer()
-    response = app.response_class(response=json.dumps(topicConsumer.getTopicDetails(topic)),
-                                  status=200,
+    topicConsumer.getTopicDetails(prepareResponse, topic)
+    response = app.response_class(response=prepareResponse.getResponseMsg(),
+                                  status=prepareResponse.getResponseCode(),
                                   mimetype='application/json')
     return response
 
@@ -69,15 +75,11 @@
     if 'timeout' in request.args:
         timeout = request.args['timeout']
 
+    prepareResponse = PrepareResponse()
     eventConsumer = EventConsumer()
-    response = app.response_class(response=json.dumps(
-                                    eventConsumer.consumeEvents(
-                                        topic,
-                                        consumergroup,
-                                        consumerid,
-                                        getLimit(limit),
-                                        getTimeout(timeout))),
-                                  status=200,
+    eventConsumer.consumeEvents(prepareResponse, topic, consumergroup, consumerid, getLimit(limit), getTimeout(timeout))
+    response = app.response_class(response=prepareResponse.getResponseMsg(),
+                                  status=prepareResponse.getResponseCode(),
                                   mimetype='application/json')
     return response
 
@@ -94,6 +96,8 @@
 def getTimeout(timeout):
     try:
         timeout = int(timeout)
+        if (timeout < 0):
+            timeout = 15
     except Exception:
         timeout = 15
     finally:
diff --git a/dmaapadapter/adapter/code/prepare_response.py b/dmaapadapter/adapter/code/prepare_response.py
new file mode 100644
index 0000000..6cce394
--- /dev/null
+++ b/dmaapadapter/adapter/code/prepare_response.py
@@ -0,0 +1,34 @@
+# Copyright 2021 Xoriant Corporation
+#
+# 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 json
+
+
+class PrepareResponse:
+
+    responseCode = None
+    responseMsg = ""
+
+    def setResponseCode(self, responseCode):
+        self.responseCode = responseCode
+
+    def getResponseCode(self):
+        return self.responseCode
+
+    def setResponseMsg(self, responseMsg):
+        self.responseMsg = json.dumps(responseMsg)
+
+    def getResponseMsg(self):
+        return self.responseMsg