Repository: rorymurdock/doby

Issue:
reqrest isn't added to the requirements.txt list

Discussion:

Patch:
diff --git a/README.md b/README.md
index 0e45a87..620fc8e 100644
--- a/README.md
+++ b/README.md
@@ -32,7 +32,7 @@ And the optional argument of
 
 |Name|Value|Description|Optional|
 |-|-|-|-|
-|debug||Enables debug logging|Yes|
+|-v||Enables debug logging|Yes|
 
 Example:
 
@@ -49,7 +49,7 @@ These basic fields will configure the library
 |description|Description of the library|`str`|||||
 |debug|Enables debug logging|`bool`|yes||||
 |requirements|A list of python requirements. you can view a list of automatically added [here](doby/build/imports.py#L14) in `import_list`|`dict`|yes||||
-|||||name|name of the requirement|`str`|
+|||||name|name of the requirement as a parent key `name:{}`|`str`|
 ||||yes|operator|Used in the requirements.txt file {`==`, `>=`, `~=`}|`str`|
 ||||yes|version|version you want for the above|`str`|
 ||||yes|builtin|Is this a built-in package? If so it won't be added to the `requirements.txt`|`bool`|
diff --git a/configs/api.json b/configs/api.json
index 4e80e1b..d911fe5 100644
--- a/configs/api.json
+++ b/configs/api.json
@@ -107,7 +107,6 @@
 			"operator": "==",
 			"version": "1.0.0"
 		},
-		"reqrest": {},
 		"panda": {
 			"builtin": false,
 			"operator": "==",
diff --git a/configs/dog_facts.json b/configs/dog_facts.json
index dc08617..1245fd2 100644
--- a/configs/dog_facts.json
+++ b/configs/dog_facts.json
@@ -32,7 +32,7 @@
 		},
 		"main": {
 			"run": true,
-			"description": "Get a fact",
+			"description": "Get a fact or 2",
 			"code": [
 				"df = DogFacts()",
 				"",
@@ -46,7 +46,7 @@
 			"fact": {
 				"category": "Dogs",
 				"name": "getFact",
-				"description": "Gets System information",
+				"description": "Gets a dog fact (or many)",
 				"method": "get",
 				"path": "/api/v1/resources/dogs",
 				"header": "rest",
diff --git a/doby/build/imports.py b/doby/build/imports.py
index e4d7a87..0a2041a 100644
--- a/doby/build/imports.py
+++ b/doby/build/imports.py
@@ -10,7 +10,7 @@ def build_import(config):
     logging.info("Generating imports: %i", len(requirements))
 
     # Create arrays
-    requirement_list = []
+    requirement_list = ["reqrest"]
     import_list = ["import logging", "import reqrest"]
 
     for requirement in requirements:
@@ -50,7 +50,7 @@ def get_clean_non_built_ins(config):
     logging.info("Generating clean non built-in imports")
 
     # Create arrays
-    requirement_list = []
+    requirement_list = ["reqrest"]
 
     for requirement in config["requirements"]:
         if not utils.key_exists_get_value("builtin",
diff --git a/doby/check_config.py b/doby/check_config.py
new file mode 100644
index 0000000..e8d8fd1
--- /dev/null
+++ b/doby/check_config.py
@@ -0,0 +1,397 @@
+import os
+import sys
+import logging
+import json
+
+logging.getLogger().setLevel(logging.INFO)
+
+
+def load_config(config_filename):
+    """Load the config files merge them and return it"""
+
+    override_config_filename = config_filename.replace(".json", "_override.json")
+    logging.info("config_file: %s", config_filename)
+    logging.info("override_file: %s", override_config_filename)
+
+    try:
+        logging.info("Importing config from %s", config_filename)
+        # Import the configs
+        if os.path.isfile(config_filename):
+            with open(config_filename) as read_file:
+                config = json.load(read_file)
+            logging.info("Successfully imported %s for config", config_filename)
+        else:
+            logging.critical("File %s doesn't exist, exiting", config_filename)
+            sys.exit()
+
+        # Import the override file if it exists
+        logging.info("Importing override config from %s", override_config_filename)
+        if os.path.isfile(override_config_filename):
+            with open(override_config_filename) as read_file:
+                override_config = json.load(read_file)
+            logging.info(
+                "Successfully imported %s for override config", config_filename
+            )
+        else:
+            logging.warning("No override file found: %s", override_config_filename)
+            # Set an empty-ish dict
+            override_config = {"override": False}
+
+    except json.JSONDecodeError as error:
+        # JSON is malformed, can't continue
+        logging.critical("Unable to parse JSON config file: %s", error)
+        sys.exit()
+
+    return config, override_config
+
+
+class check_keys:
+    def __init__(self, filename) -> None:
+        self.config, self.override_config = load_config(filename)
+
+    def check_keys_in_dict(self, config, override_config, required_keys):
+        for required_key in required_keys:
+            if (
+                required_key not in config.keys()
+                and required_key not in override_config.keys()
+            ):
+                logging.error("%s required in config" % (required_key))
+
+    def check_optional_keys_in_dict(
+        self, config, override_config, required_keys, optional_keys, path
+    ):
+        for key in config.keys():
+            if key not in required_keys and key not in optional_keys:
+                logging.error("%s: Unknown key %s" % (path, key))
+
+        for key in override_config.keys():
+            if key not in required_keys and key not in optional_keys:
+                logging.error("%s: Unknown key %s" % (path, key))
+
+    def check_function_variable_static(self, config, path):
+        allowed = ["function", "variable", "static"]
+
+        for header in config.keys():
+            for key in config[header]:
+                if key not in allowed:
+                    logging.error("%s:%s:%s Unknown key %s" % (path, header, key, key))
+                else:
+                    logging.info("%s:%s:%s key is %s" % (path, header, key, type(config[header][key])))
+                # Logging info? TODO
+
+    def check_type(self, config, override_config, key, path):
+        if key in config.keys():
+            return type(config[key])
+        elif key in override_config:
+            return type(override_config[key])
+        else:
+            return None
+
+    def check_key_both(self, config, override_config, key, path, optional=False):
+
+        if optional:
+            optional_str = "optional "
+        else:
+            optional_str = ""
+
+        if key in config.keys():
+            logging.info(
+                "%s:%s %skey exists in config file" % (path, key, optional_str)
+            )
+            return True
+        elif key in override_config.keys():
+            logging.info(
+                "%s:%s %skey exists in override config file"
+                % (path, key, optional_str)
+            )
+            return True
+        else:
+            if optional:
+                logging.warning(
+                    "%s:%s %skey doesn't exist in either config file"
+                    % (path, key, optional_str)
+                )
+            else:
+                logging.error(
+                    "%s:%s %skey doesn't exist in either config file"
+                    % (path, key, optional_str)
+                )
+            return False
+
+    def think_of_a_better_name(
+        self, config, override_config, key, path, expected_type, optional
+    ) -> bool:
+
+        # If the key exists in either config
+        if self.check_key_both(config, override_config, key, path, optional):
+
+            # If it exists is it the right type
+            if self.check_type(config, override_config, key, path) == expected_type:
+                logging.info("%s:%s is correct type", path, key)
+                return True
+            else:
+                logging.error("%s:%s should be a %s", path, key, expected_type)
+                return False
+        # Doesn't exist
+        return False
+
+    def check_top_level(self):
+
+        required_keys = ["name", "description", "hostname", "headers", "functions"]
+        optional_keys = ["debug", "requirements", "setup", ".gitignore", "override"]
+
+        # Check all higher level keys
+        self.check_keys_in_dict(self.config, self.override_config, required_keys)
+
+        self.check_optional_keys_in_dict(
+            self.config, self.override_config, required_keys, optional_keys, "."
+        )
+
+    def check_name(self):
+        """Check the name keys"""
+
+        key = "name"
+        path = "."
+        expected_type = str
+        optional = False
+
+        self.think_of_a_better_name(
+            self.config, self.override_config, key, path, expected_type, optional
+        )
+
+    def check_description(self):
+        """Check the description keys"""
+
+        key = "description"
+        path = "."
+        expected_type = str
+        optional = False
+
+        self.think_of_a_better_name(
+            self.config, self.override_config, key, path, expected_type, optional
+        )
+
+    def check_debug(self):
+        """Check the debug keys"""
+
+        key = "debug"
+        path = "."
+        expected_type = bool
+        optional = True
+
+        self.think_of_a_better_name(
+            self.config, self.override_config, key, path, expected_type, optional
+        )
+
+    def check_requirements(self):
+        """Check the requirements keys"""
+
+        key = "requirements"
+        path = "."
+        expected_type = dict
+        optional = True
+
+
+        if self.think_of_a_better_name(
+            self.config, self.override_config, key, path, expected_type, optional
+        ):
+
+            if key in self.config:
+                for requirement in self.config[key]:
+                    if self.config[key][requirement] == {}:
+                        logging.info("%s:%s:%s is a valid requirement", path, key, requirement)
+                    else:
+                        optional_keys = ["operator", "version", "builtin"]
+
+                        self.check_optional_keys_in_dict(
+                            self.config[key][requirement], {}, [], optional_keys, "."
+                        )
+
+            elif key in self.override_config:
+                for requirement in self.override_config[key]:
+                    if self.override_config[key][requirement] == {}:
+                        logging.info("%s:%s:%s is a valid requirement", path, key, requirement)
+                    else:
+                        optional_keys = ["operator", "version", "builtin"]
+
+                        self.check_optional_keys_in_dict(
+                            {}, self.override_config[key][requirement], [], optional_keys, "."
+                        )
+
+        # TODO check requirments
+
+    def check_hostname(self):
+        """Check the hostname keys"""
+
+        key = "hostname"
+        path = "."
+        expected_type = dict
+        optional = True
+
+        self.think_of_a_better_name(
+            self.config, self.override_config, key, path, expected_type, optional
+        )
+
+        # TODO check hostname
+
+    def check_headers(self):
+        """Check the headers keys"""
+
+        key = "headers"
+        path = "."
+        expected_type = dict
+        optional = False
+
+        if self.think_of_a_better_name(self.config, self.override_config, key, path, expected_type, optional):
+            if key in self.config:
+                for header in self.config[key]:
+                    self.check_function_variable_static(self.config[key][header], ".:headers:"+header)
+
+            elif key in self.override_config:
+                for header in self.override_config[key]:
+                    self.check_function_variable_static(self.override_config[key][header], ".:headers:"+header)
+
+        # TODO check headers
+
+    def check_functions(self):
+        """Check the functions keys"""
+
+        key = "functions"
+        path = "."
+        expected_type = dict
+        optional = False
+
+        return self.think_of_a_better_name(
+            self.config, self.override_config, key, path, expected_type, optional
+        )
+
+    def check_init_functions(self):
+        """Check the init_functions keys"""
+
+        key = "init"
+        path = ".:functions"
+        expected_type = dict
+        optional = True
+
+        self.think_of_a_better_name(
+            self.config["functions"],
+            self.override_config["functions"],
+            key,
+            path,
+            expected_type, optional
+        )
+
+    def check_main_functions(self):
+        """Check the main_functions keys"""
+
+        key = "main"
+        path = ".:functions"
+        expected_type = dict
+        optional = True
+
+        self.think_of_a_better_name(
+            self.config["functions"],
+            self.override_config["functions"],
+            key,
+            path,
+            expected_type, optional
+        )
+
+    def check_endpoint_functions(self):
+        """Check the endpoint_functions keys"""
+
+        key = ".:endpoint"
+        path = "functions"
+        expected_type = dict
+        optional = False
+
+        self.think_of_a_better_name(
+            self.config["functions"],
+            self.override_config["functions"],
+            key,
+            path,
+            expected_type, optional
+        )
+
+    def check_custom_functions(self):
+        """Check the custom_functions keys"""
+
+        key = ".:custom"
+        path = "functions"
+        expected_type = dict
+        optional = True
+
+        self.think_of_a_better_name(
+            self.config["functions"],
+            self.override_config["functions"],
+            key,
+            path,
+            expected_type, optional
+        )
+
+    def check_setup_py(self):
+        """Check the setup_py keys"""
+
+        key = "setup"
+        path = "."
+        expected_type = dict
+        optional = True
+
+        self.think_of_a_better_name(
+            self.config, self.override_config, key, path, expected_type, optional
+        )
+
+        # TODO check setup
+
+    def check_gitignore(self):
+        """Check the gitignore keys"""
+
+        key = ".gitignore"
+        path = "."
+        expected_type = list
+        optional = True
+
+        self.think_of_a_better_name(
+            self.config, self.override_config, key, path, expected_type, optional
+        )
+
+        # TODO check headers
+
+    #     # Check all higher level keys
+
+    #     for required_key in required_keys:
+    #         if required_key not in config_dict.keys() and required_key not in override_config_dict.keys():
+    #             logging.error("%s required in config" % (required_key))
+
+    #     for key in config_dict.keys():
+    #         if key not in required_keys and key not in optional_keys:
+    #             logging.error("%s: Unknown key %s" % ("config", key))
+
+    #     for key in override_config_dict.keys():
+    #         if key not in required_keys and key not in optional_keys:
+    #             logging.error("%s: Unknown key %s" % ("override_config", key))
+
+
+def main():
+    cc = check_keys("configs/wso.json")
+
+    cc.check_top_level()
+    cc.check_name()
+    cc.check_description()
+    cc.check_debug()
+    cc.check_requirements()
+    cc.check_hostname()
+    cc.check_headers()
+    if cc.check_functions():
+        cc.check_init_functions()
+        cc.check_main_functions()
+        cc.check_endpoint_functions()
+        cc.check_custom_functions()
+    cc.check_setup_py()
+    cc.check_gitignore()
+
+    print("Check completed")
+
+
+if __name__ == "__main__":
+    main()


Test patch:
diff --git a/tests/test_build_build_all.py b/tests/test_build_build_all.py
index 734e062..dff2f73 100644
--- a/tests/test_build_build_all.py
+++ b/tests/test_build_build_all.py
@@ -24,7 +24,6 @@ def test_build_file():
             "import logging",
             "import reqrest",
             "import os",
-            "import reqrest",
             "import panda",
             "",
             "class PyTest():",
diff --git a/tests/test_build_import.py b/tests/test_build_import.py
index ef96293..c2a67c7 100644
--- a/tests/test_build_import.py
+++ b/tests/test_build_import.py
@@ -15,7 +15,7 @@ def test_build_import():
     }
 
     assert imports.build_import(config) == (
-        ["apple", "pear==1.0.0", "lime"],
+        ["reqrest", "apple", "pear==1.0.0", "lime"],
         [
             "import logging",
             "import reqrest",
diff --git a/tests/test_build_setup.py b/tests/test_build_setup.py
index d3f91f8..9e3271c 100644
--- a/tests/test_build_setup.py
+++ b/tests/test_build_setup.py
@@ -60,7 +60,7 @@ def test_build_setup_no_extras():
         '"me@example.com", url = "https://example.com", description = "fruitTest '
         'configuration library", long_description=LONG_DESCRIPTION, '
         'long_description_content_type="text/markdown"classifiers=[], '
-        "install_requires=['apple', 'pear', 'lime'], include_package_data=True)",
+        "install_requires=['reqrest', 'apple', 'pear', 'lime'], include_package_data=True)",
     ]
 
 
@@ -99,6 +99,6 @@ def test_build_setup_all():
         'long_description_content_type="text/markdown"classifiers=["Development '
         'Status :: 5 - Production/Stable","License Status :: OSI Approved :: Apache '
         'Software License","Operating System :: OS Independent","Programming Language '
-        ":: Python :: 3\",], install_requires=['apple', 'pear', 'lime'], "
+        ":: Python :: 3\",], install_requires=['reqrest', 'apple', 'pear', 'lime'], "
         "include_package_data=True)",
     ]
diff --git a/tests/test_config.py b/tests/test_config.py
index 7ede3a7..5c358b8 100644
--- a/tests/test_config.py
+++ b/tests/test_config.py
@@ -283,7 +283,6 @@ def create_test_all_config():
         },
         "requirements": {
             "os": {"builtin": True, "operator": "==", "version": "1.0.0"},
-            "reqrest": {},
             "panda": {"builtin": False, "operator": "==", "version": "1.0.0"},
         },
         ".gitignore": ["*.pyc"],
diff --git a/tests/test_doby.py b/tests/test_doby.py
index e4720de..76bc5b4 100644
--- a/tests/test_doby.py
+++ b/tests/test_doby.py
@@ -58,7 +58,7 @@ def test_get_imports():
 
     DOBY.reset_config(imports)
     assert DOBY.get_imports() == (
-        ["apple", "pear==1.0.0", "lime"],
+        ["reqrest", "apple", "pear==1.0.0", "lime"],
         [
             "import logging",
             "import reqrest",
@@ -151,7 +151,7 @@ def test_get_setup():
         '"me@example.com", url = "https://example.com", description = "fruitTest '
         'configuration library", long_description=LONG_DESCRIPTION, '
         'long_description_content_type="text/markdown"classifiers=[], '
-        "install_requires=['apple', 'pear', 'lime'], include_package_data=True)",
+        "install_requires=['reqrest', 'apple', 'pear', 'lime'], include_package_data=True)",
     ]