Add external config support to result_collection_api 98/1398/2
authorGuy Rodrigue Koffi <koffirodrigue@gmail.com>
Sat, 5 Sep 2015 04:54:21 +0000 (06:54 +0200)
committerGuy Rodrigue Koffi <koffirodrigue@gmail.com>
Mon, 7 Sep 2015 15:34:53 +0000 (17:34 +0200)
JIRA : RELENG-7

Change-Id: I2b68aac3e903b237f500bad91e3625aaf57bfdaf
Signed-off-by: Guy Rodrigue Koffi <koffirodrigue@gmail.com>
utils/test/result_collection_api/common/config.py
utils/test/result_collection_api/common/constants.py
utils/test/result_collection_api/config.ini [new file with mode: 0644]
utils/test/result_collection_api/result_collection_api.py

index 9f7272b..a0d0757 100644 (file)
@@ -7,15 +7,8 @@
 # http://www.apache.org/licenses/LICENSE-2.0
 ##############################################################################
 
-"""
-from ConfigParser import SafeConfigParser
 
-parser = SafeConfigParser()
-parser.read('config.ini')
-
-
-mongo_url = parser.get('default', 'mongo_url')
-"""
+from ConfigParser import SafeConfigParser, NoOptionError
 
 
 def prepare_put_request(edit_request, key, new_value, old_value):
@@ -31,3 +24,76 @@ def prepare_put_request(edit_request, key, new_value, old_value):
                 edit_request[key] = new_value
 
     return edit_request
+
+
+class ParseError(Exception):
+    """
+    Custom exception class for config file
+    """
+
+    def __init__(self, message):
+        self.msg = message
+
+    def __str__(self):
+        return 'error parsing config file : %s' % self.msg
+
+
+class APIConfig:
+    """
+    The purpose of this class is to load values correctly from the config file.
+    Each key is declared as an attribute in __init__() and linked in parse()
+    """
+
+    def __init__(self):
+        self._default_config_location = "config.ini"
+        self.mongo_url = None
+        self.api_port = None
+        self.api_debug_on = None
+        self._parser = None
+
+    def _get_parameter(self, section, param):
+        try:
+            return self._parser.get(section, param)
+        except NoOptionError:
+            raise ParseError("[%s.%s] parameter not found" % (section, param))
+
+    def _get_int_parameter(self, section, param):
+        try:
+            return int(self._get_parameter(section, param))
+        except ValueError:
+            raise ParseError("[%s.%s] not an int" % (section, param))
+
+    def _get_bool_parameter(self, section, param):
+        result = self._get_parameter(section, param)
+        if str(result).lower() == 'true':
+            return True
+        if str(result).lower() == 'false':
+            return False
+
+        raise ParseError(
+            "[%s.%s : %s] not a boolean" % (section, param, result))
+
+    @staticmethod
+    def parse(config_location=None):
+        obj = APIConfig()
+
+        if config_location is None:
+            config_location = obj._default_config_location
+
+        obj._parser = SafeConfigParser()
+        obj._parser.read(config_location)
+        if not obj._parser:
+            raise ParseError("%s not found" % config_location)
+
+        # Linking attributes to keys from file with their sections
+        obj.mongo_url = obj._get_parameter("mongo", "url")
+        obj.api_port = obj._get_int_parameter("api", "port")
+        obj.api_debug_on = obj._get_bool_parameter("api", "debug")
+        return obj
+
+    def __str__(self):
+        return "mongo_url = %s \n" \
+               "api_port = %s \n" \
+               "api_debug_on = %s \n" % (self.mongo_url,
+                                         self.api_port,
+                                         self.api_debug_on)
index 2176956..2c825c1 100644 (file)
@@ -8,10 +8,6 @@
 ##############################################################################
 
 
-API_LISTENING_PORT = 80
-
-MONGO_URL = "mongodb://127.0.0.1:27017/"
-
 DEFAULT_REPRESENTATION = "application/json"
 HTTP_BAD_REQUEST = 400
 HTTP_FORBIDDEN = 403
diff --git a/utils/test/result_collection_api/config.ini b/utils/test/result_collection_api/config.ini
new file mode 100644 (file)
index 0000000..e00b56c
--- /dev/null
@@ -0,0 +1,10 @@
+[mongo]
+# URL of the mongo DB
+# Mongo auth url => mongodb://user1:pwd1@host1/?authSource=db1
+url = mongodb://127.0.0.1:27017/test_results_collection
+
+[api]
+# Listening port
+port = 80
+# With debug_on set to true, error traces will be shown in HTTP responses
+debug = True
\ No newline at end of file
index bb26bb2..c04e034 100644 (file)
@@ -15,29 +15,40 @@ Pre-requisites:
 We can launch the API with this file
 
 TODOs :
+  - use POD name instead of id
+  - logging
   - json args validation with schemes
+  - POST/PUT/DELETE for PODs
+  - POST/PUT/GET/DELETE for installers, platforms (enrich results info)
   - count cases for GET on test_projects
   - count results for GET on cases
   - provide filtering on requests
   - include objects
-  - logging
-  - external configuration file
+  - swagger documentation
   - setup file
   - results pagination
-  - POST/PUT/DELETE for PODs
-  - POST/PUT/GET/DELETE for installers, platforms (enrich results info)
+  - unit tests
 
 """
 
 import tornado.ioloop
 import motor
+import argparse
 
 from resources.handlers import VersionHandler, PodHandler, \
     TestProjectHandler, TestCasesHandler, TestResultsHandler
-from common.constants import API_LISTENING_PORT, MONGO_URL
+from common.config import APIConfig
+
+
+# optionally get config file from command line
+parser = argparse.ArgumentParser()
+parser.add_argument("-c", "--config-file", dest='config_file',
+                    help="Config file location")
+args = parser.parse_args()
+CONF = APIConfig().parse(args.config_file)
 
 # connecting to MongoDB server, and choosing database
-db = motor.MotorClient(MONGO_URL).test_results_collection
+db = motor.MotorClient(CONF.mongo_url)
 
 
 def make_app():
@@ -76,14 +87,15 @@ def make_app():
             (r"/results/([^/]*)", TestResultsHandler),
         ],
         db=db,
-        debug=True,
+        debug=CONF.api_debug_on,
     )
 
 
 def main():
     application = make_app()
-    application.listen(API_LISTENING_PORT)
+    application.listen(CONF.api_port)
     tornado.ioloop.IOLoop.current().start()
 
+
 if __name__ == "__main__":
     main()