def get_apikey_permissions(path): """ Returns a list of api keys to allow an integration to run. :param path: Location to file with api keys one per line. :return apikey_permissions: Return list of api keys. """ try: # Read the apikey_permissions.txt file into a List apikey_permissions_lines = sdk_helpers.read_file(path) except Exception as err: raise SDKException(u"Failed to parse configs from apikey_permissions.txt file\nThe apikey_permissions.txt file may " u"be corrupt. Visit the App Exchange to contact the developer\nReason: {0}".format(err)) # Raise an error if nothing found in the file if not apikey_permissions_lines: raise SDKException(u"No content found in provided apikey_permissions.txt file: {0}".format(path)) # Get permissions. Ignore comments where 1st non-whitespace character is a '#'. apikey_permissions = [p.strip() for p in apikey_permissions_lines if not p.lstrip().startswith("#")] # Do basic check on api keys to see if they are in correct format. for p in apikey_permissions: if not re.match("[_a-zA-Z]*$", p): raise SDKException(u"Value '{0}' in file '{1}' is not a valid api key value.".format(p, path)) # Ensure that the permissions includes at minimum the set of base permissions. if not all(p in apikey_permissions for p in BASE_PERMISSIONS): raise SDKException(u"'The file '{0}' is missing one of the base api key permissions.".format(path)) return apikey_permissions
def test_read_write_file(fx_mk_temp_dir): temp_file = os.path.join(mock_paths.TEST_TEMP_DIR, "mock_file.txt") sdk_helpers.write_file(temp_file, mock_data.mock_file_contents) assert os.path.isfile(temp_file) file_lines = sdk_helpers.read_file(temp_file) assert mock_data.mock_file_contents in file_lines
def test_get_setup_callable(): # Test callable section returned correctly. # Read the mock setup.py content. setup_content = sdk_helpers.read_file(mock_paths.MOCK_SETUP_PY) setup_callable = package_helpers.get_setup_callable(setup_content) # Read the mocked callable from processed setup.py with io.open(mock_paths.MOCK_SETUP_CALLABLE, mode="rt", encoding="utf-8") as the_file: mock_setup_callable = the_file.read() assert mock_setup_callable == setup_callable
def test_execute_command_no_samples(fx_copy_fn_main_mock_integration, fx_get_sub_parser, fx_cmd_line_args_package): mock_integration_name = fx_copy_fn_main_mock_integration[0] path_fn_main_mock_integration = fx_copy_fn_main_mock_integration[1] # Replace cmd line arg "fn_main_mock_integration" with path to temp dir location sys.argv[sys.argv.index( mock_integration_name)] = path_fn_main_mock_integration # Append --no-samples command line arg sys.argv.append("--no-samples") # Package the app cmd_package = CmdPackage(fx_get_sub_parser) args = cmd_package.parser.parse_known_args()[0] path_the_app_zip = cmd_package.execute_command(args) # Test app.zip contents assert zipfile.is_zipfile(path_the_app_zip) with zipfile.ZipFile((path_the_app_zip), 'r') as app_zip: assert helpers.verify_expected_list(EXPECTED_FILES_APP_ZIP[:-1], app_zip.namelist()) # Test app.zip/app.json contents app_json_contents = sdk_helpers.read_zip_file(path_the_app_zip, "app.json") mock_app_json_contents = sdk_helpers.read_file( mock_paths.MOCK_APP_ZIP_APP_JSON)[0] assert app_json_contents == mock_app_json_contents # Test app.zip/export.res contents export_res_contents = sdk_helpers.read_zip_file(path_the_app_zip, "export.res") mock_export_res_contents = sdk_helpers.read_file( mock_paths.MOCK_APP_ZIP_EXPORT_RES)[0] assert json.loads(export_res_contents) == json.loads( mock_export_res_contents)
def test_app_log_results_are_used(fx_copy_fn_main_mock_integration, fx_get_sub_parser, fx_cmd_line_args_docgen): mock_integration_name = fx_copy_fn_main_mock_integration[0] path_fn_main_mock_integration = fx_copy_fn_main_mock_integration[1] # Replace cmd line arg "fn_main_mock_integration" with path to temp dir location sys.argv[sys.argv.index( mock_integration_name)] = path_fn_main_mock_integration cmd_docgen = CmdDocgen(fx_get_sub_parser) args = cmd_docgen.parser.parse_known_args()[0] cmd_docgen.execute_command(args) readme_file = sdk_helpers.read_file( os.path.join(path_fn_main_mock_integration, package_helpers.BASE_NAME_README)) assert ' "custom_results": "these are my custom results!"' in "\n".join( readme_file)
def test_render_jinja_mapping(fx_mk_temp_dir): mock_jinja_data = { "functions": [{ "x_api_name": "fn_mock_function_1" }, { "x_api_name": "fn_mock_function_2" }], "export_data": { "server_version": { "version": "35.0.0" } } } jinja_env = sdk_helpers.setup_jinja_env( "data/codegen/templates/package_template") jinja_mapping_dict = { "MANIFEST.in": ("MANIFEST.in.jinja2", mock_jinja_data), "README.md": ("README.md.jinja2", mock_jinja_data), "setup.py": ("setup.py.jinja2", mock_jinja_data), "tox.ini": ("tox.ini.jinja2", mock_jinja_data), "Dockerfile": ("Dockerfile.jinja2", mock_jinja_data), "entrypoint.sh": ("entrypoint.sh.jinja2", mock_jinja_data), "apikey_permissions.txt": ("apikey_permissions.txt.jinja2", mock_jinja_data), "data": {}, "icons": { "company_logo.png": package_helpers.PATH_DEFAULT_ICON_COMPANY_LOGO, "app_logo.png": package_helpers.PATH_DEFAULT_ICON_EXTENSION_LOGO, }, "doc": { "screenshots": { "main.png": package_helpers.PATH_DEFAULT_SCREENSHOT } }, "test_package": { "__init__.py": ("package/__init__.py.jinja2", mock_jinja_data), "LICENSE": ("package/LICENSE.jinja2", mock_jinja_data), "components": { "__init__.py": ("package/components/__init__.py.jinja2", mock_jinja_data), }, "util": { "data": { "export.res": ("package/util/data/export.res.jinja2", mock_jinja_data) }, "__init__.py": ("package/util/__init__.py.jinja2", mock_jinja_data), "config.py": ("package/util/config.py.jinja2", mock_jinja_data), "customize.py": ("package/util/customize.py.jinja2", mock_jinja_data), "selftest.py": ("package/util/selftest.py.jinja2", mock_jinja_data), } } } CmdCodegen.render_jinja_mapping(jinja_mapping_dict, jinja_env, mock_paths.TEST_TEMP_DIR, mock_paths.TEST_TEMP_DIR) files_in_dir = sorted(os.listdir(mock_paths.TEST_TEMP_DIR)) assert files_in_dir == [ 'Dockerfile', 'MANIFEST.in', 'README.md', 'apikey_permissions.txt', 'data', 'doc', 'entrypoint.sh', 'icons', 'setup.py', 'test_package', 'tox.ini' ] files_in_icons_dir = sorted( os.listdir(os.path.join(mock_paths.TEST_TEMP_DIR, "icons"))) assert files_in_icons_dir == ['app_logo.png', 'company_logo.png'] files_in_test_package = sorted( os.listdir(os.path.join(mock_paths.TEST_TEMP_DIR, "test_package"))) assert files_in_test_package == [ 'LICENSE', '__init__.py', 'components', 'util' ] files_in_util = sorted( os.listdir( os.path.join(mock_paths.TEST_TEMP_DIR, "test_package", "util"))) assert files_in_util == [ '__init__.py', 'config.py', 'customize.py', 'data', 'selftest.py' ] files_in_util_data = sorted( os.listdir( os.path.join(mock_paths.TEST_TEMP_DIR, "test_package", package_helpers.PATH_UTIL_DATA_DIR))) assert files_in_util_data == ['export.res'] files_in_components = sorted( os.listdir( os.path.join(mock_paths.TEST_TEMP_DIR, "test_package", "components"))) assert files_in_components == ['__init__.py'] customize_py = sdk_helpers.read_file( os.path.join(mock_paths.TEST_TEMP_DIR, "test_package", "util", "customize.py")) assert ' "functions": [u"fn_mock_function_1", u"fn_mock_function_2"],\n' in customize_py
def load_customize_py_module(path_customize_py, warn=True): """ Return the path_customize_file as a Python Module. We can then access it methods like: > result = customize_py_module.codegen_reload_data() Raises an SDKException if we fail to load the module :param path_customize_py: Path to the customize.py file that contains the module :param warn: Boolean to indicate warning should happen, for codegen usage warning not required. :return: The customize Python Module, if found :rtype: module """ LINE_TO_REPLACE = u"from resilient_circuits" REPLACE_TEXT = u"from resilient import ImportDefinition" new_lines = [] current_customize_py_lines = sdk_helpers.read_file(path_customize_py) # Check if customize.py has dependencies on resilient-circuits for i, line in enumerate(current_customize_py_lines): if line.startswith(LINE_TO_REPLACE): new_lines = current_customize_py_lines[:i] + [ REPLACE_TEXT ] + current_customize_py_lines[i + 1:] if warn: LOG.warning( "WARNING: Import References to resilient-circuits are deprecated in v35.0.195. For newer " "versions of resilient-circuits, replace '%s', with '%s' in %s", line.strip(), REPLACE_TEXT, path_customize_py) break if new_lines: # The customize.py has a reference to a deprecated resilient-circuits import. Save the customize.py # file as a temporary file with deprecated resilient-circuits import altered then attempt to load # the module from the temporary location. temp_customize = u"".join(new_lines) if sys.version_info.major == 3: temp_file_obj = tempfile.NamedTemporaryFile('w', suffix=".py", encoding="utf8", delete=False) else: temp_customize = temp_customize.encode('utf-8') temp_file_obj = tempfile.NamedTemporaryFile('w+b', suffix=".py", delete=False) try: # Write the new temporary customize.py (with resilient-circuits replaced with resilient) with temp_file_obj as temp_file: temp_file.write(temp_customize) temp_file.flush() module_name = os.path.basename(temp_file.name)[:-3] # Attempt to import the module from the temporary file location. customize_py_module = sdk_helpers.load_py_module( temp_file.name, module_name) except IOError as ioerr: raise IOError("Unexpected IO error '{0}' for file '{1}".format( ioerr, temp_file.name)) except Exception as err: # An an unexpected error trying to load the module temporary customize module. raise SDKException( u"Got an error attempting to load temporary customize.py module\n{0}" .format(err)) finally: os.unlink(temp_file.name) else: try: customize_py_module = sdk_helpers.load_py_module( path_customize_py, "customize") except Exception as err: raise SDKException( u"Failed to load customize.py module\n{0}".format(err)) return customize_py_module
def parse_setup_py(path, attribute_names): """Parse the values of the given attribute_names and return a Dictionary attribute_name:attribute_value :param path: Path to setup.py for a package. :param attribute_names: List of attribute names to extract from setup.py. :return return_dict: Dict of properties from setup.py. """ return_dict = {} # Define "True" = bool(True) as value for eval built_ins if using python 2. if sys.version_info.major < 3: built_ins = {"True": bool(True), "False": bool(False)} else: built_ins = {} # Define a dummy setup function to get the dictionary of parameters returned from evaled setup.py callable. def setup(*args, **kwargs): return kwargs # Read the setup.py content. setup_content = sdk_helpers.read_file(path) # Raise an error if nothing found in the file. if not setup_content: raise SDKException( u"No content found in provided setup.py file: {0}".format(path)) # Get the callable section from contents of setup.py. setup_callable = get_setup_callable(setup_content) # Run eval on callable content to retrieve attributes. try: result = eval(setup_callable, {'__builtins__': built_ins}, {"setup": setup}) except Exception as err: raise SDKException(u"Failed to eval setup callable {0}".format(err)) # Foreach attribute_name, get/calculate its value and add to return_dict for attribute_name in attribute_names: if attribute_name == "entry_points": entry_point_paths = {} # Get the path of the top level for the package. path_package = os.path.dirname(path) for ep in SUPPORTED_EP: if result['entry_points'].get(ep): # Extract the relative dotted module reference from entry point values of following types: # ["customize = fn_func.util.customize:customization_data"] # ["gen_config = fn_func.util.config:apphost_config_section_data"] # ["gen_config = fn_func.util.config:config_section_data"] # e.g. Extract ["customize = fn_func.util.customize:customization_data"] -> fn_func.util.customize parsed_ep = ''.join(result['entry_points'].get(ep)).split( '=')[1].split(':')[0].strip() # Convert a dotted module reference to an actual python module path: # e.g. Convert fn_func.util.customize -> /path_to_package/fn_func/fn_func/util/customize.py entry_point_paths.update({ ep: os.path.join(path_package, parsed_ep.replace(".", os.path.sep)) + ".py" }) return_dict[attribute_name] = entry_point_paths else: return_dict[attribute_name] = result.get(attribute_name) return return_dict