From 2bbe1db2a7702f9beea8c564897f8fc5b60a0a86 Mon Sep 17 00:00:00 2001 From: Justin Tervala Date: Fri, 5 Jan 2018 15:15:20 -0500 Subject: [PATCH 01/15] Updated chaangelog to prepare for next release --- CHANGELOG.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index fdfaa1d9f..393cff925 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,9 @@ # Changelog +## [Unreleased] +###### unreleased + ## [0.6.2] ###### 2018-01-05 From 40a312f8eb6d357e9e2176d3bb8b20dc93e7872e Mon Sep 17 00:00:00 2001 From: Justin Tervala Date: Fri, 5 Jan 2018 15:33:32 -0500 Subject: [PATCH 02/15] Refactored make_app a bit --- scripts/make_app.py | 130 ++++++++++++++++++++++++++------------------ 1 file changed, 78 insertions(+), 52 deletions(-) diff --git a/scripts/make_app.py b/scripts/make_app.py index 1eb94ba0a..61a449b9a 100644 --- a/scripts/make_app.py +++ b/scripts/make_app.py @@ -34,36 +34,38 @@ def make_template(app_name): os.makedirs(new_app_dir) for (dirpath, dirnames, filenames) in walk(skeleton_app_dir): if '__pycache__' not in dirpath: - for fn in filenames: - if dirpath == skeleton_app_dir: - write_fp = new_app_dir + '/' + fn - else: - sub_dir = dirpath.replace(skeleton_app_dir, new_app_dir) - if not os.path.exists(sub_dir): - os.makedirs(sub_dir) - write_fp = sub_dir + '/' + fn - with open(dirpath + '/' + fn) as read_from_file: - with open(write_fp, 'w') as write_to_file: - for line in read_from_file: - if 'SkeletonApp' in line: - line = line.replace('SkeletonApp', app_name) - elif 'Skeleton App' in line: - line = line.replace('Skeleton App', _separate_camelcase(app_name)) - write_to_file.write(line) + for filename in filenames: + write_fp = get_write_filepath(dirpath, filename, new_app_dir, skeleton_app_dir) + write_app_to_file(app_name, dirpath, filename, write_fp) print('Finished generating the template at', new_app_dir) else: print('Template already exist at', new_app_dir) -def generate_methods(input_fp): - actual_fp = '' - if input_fp.count('/') > 0: - if os.path.isfile(input_fp): - actual_fp = input_fp +def write_app_to_file(app_name, dirpath, filename, write_fp): + with open(dirpath + '/' + filename) as read_from_file: + with open(write_fp, 'w') as write_to_file: + for line in read_from_file: + if 'SkeletonApp' in line: + line = line.replace('SkeletonApp', app_name) + elif 'Skeleton App' in line: + line = line.replace('Skeleton App', _separate_camelcase(app_name)) + write_to_file.write(line) + + +def get_write_filepath(dirpath, filename, new_app_dir, skeleton_app_dir): + if dirpath == skeleton_app_dir: + write_fp = new_app_dir + '/' + filename else: - for (dirpath, dirnames, filenames) in walk(main_dir): - if input_fp in filenames: - actual_fp = dirpath + '/' + input_fp + sub_dir = dirpath.replace(skeleton_app_dir, new_app_dir) + if not os.path.exists(sub_dir): + os.makedirs(sub_dir) + write_fp = sub_dir + '/' + filename + return write_fp + + +def generate_methods(input_fp): + actual_fp = get_actual_filepath(input_fp) if actual_fp == '': print('Unable to find file. Please check that the yaml file exist at', input_fp) return @@ -89,34 +91,8 @@ def generate_methods(input_fp): write_to_file.writelines(init_lines[:max(loc for loc, val in enumerate(init_lines) if val == '\n') + 1]) with open(main_py, 'a') as write_to_file: actions = yaml_dict['actions'] - for action in actions: - action_val = actions[action] - lines = [] - if 'description' in action_val: - lines.append("'''") - lines.append(action_val['description']) - parameters_args = '' - if 'parameters' in action_val: - parameters0 = action_val['parameters'][0] - lines.append('Inputs:') - parameters_str = '{0} ({1}): {2}'.format(parameters0['name'], parameters0['type'], - parameters0['description']) - parameters_args = ', ' + parameters0['name'] - required_val = parameters0['required'] - if not required_val: - parameters_str += ' (Optional)' - parameters_args += '=None' - lines.append(parameters_str) - if 'returns' in action_val: - success_val = action_val['returns']['Success'] - lines.append('Output:') - lines.append('{0}: {1}'.format(success_val['schema']['type'], success_val['description'])) - lines.append("'''") - lines.append('@action') - lines.append('def {0}(self{1}):'.format(action.replace(' ', '_'), parameters_args)) - lines.append('\tpass') - lines.append('\n') - write_to_file.writelines('\n'.join(['\t' + line for line in lines])) + for action, action_val in actions.items(): + write_action(action, action_val, write_to_file) cur_fp = new_app_dir + '/api.yaml' if actual_fp != cur_fp: with open(actual_fp) as yaml_file: @@ -127,6 +103,56 @@ def generate_methods(input_fp): print('Successfully generated methods for {0} at {1}'.format(_separate_camelcase(app_name), new_app_dir)) +def get_actual_filepath(input_fp): + actual_fp = '' + if input_fp.count('/') > 0: + if os.path.isfile(input_fp): + actual_fp = input_fp + else: + for (dirpath, dirnames, filenames) in walk(main_dir): + if input_fp in filenames: + actual_fp = dirpath + '/' + input_fp + return actual_fp + + +def write_action(action, action_val, write_to_file): + lines = [] + if 'description' in action_val: + lines.append("'''") + lines.append(action_val['description']) + parameters_args = '' + if 'parameters' in action_val: + parameters_args = add_parameters(action_val, lines) + if 'returns' in action_val: + add_returns(action_val, lines) + lines.append('@action') + lines.append('def {0}(self{1}):'.format(action.replace(' ', '_'), parameters_args)) + lines.append('\tpass') + lines.append('\n') + write_to_file.writelines('\n'.join(['\t' + line for line in lines])) + + +def add_returns(action_val, lines): + success_val = action_val['returns']['Success'] + lines.append('Output:') + lines.append('{0}: {1}'.format(success_val['schema']['type'], success_val['description'])) + lines.append("'''") + + +def add_parameters(action_val, lines): + parameters0 = action_val['parameters'][0] + lines.append('Inputs:') + parameters_str = '{0} ({1}): {2}'.format(parameters0['name'], parameters0['type'], + parameters0['description']) + parameters_args = ', ' + parameters0['name'] + required_val = parameters0['required'] + if not required_val: + parameters_str += ' (Optional)' + parameters_args += '=None' + lines.append(parameters_str) + return parameters_args + + help_msg_app_name = 'make template for app with name provided by app_name. The new app is stored under the "apps" ' \ 'directory.' help_msg_yaml = 'generate methods for app using yaml file at the filepath provided. If YAML_FILE is a filename, ' \ From ef249c372f889dd21f41157881bebf66a38cbfff Mon Sep 17 00:00:00 2001 From: Justin Tervala Date: Fri, 5 Jan 2018 15:55:40 -0500 Subject: [PATCH 03/15] Reducing code complexity --- interfaces/dispatchers.py | 46 +++++++++++++++++++++++---------------- update.py | 23 ++++++++++---------- 2 files changed, 38 insertions(+), 31 deletions(-) diff --git a/interfaces/dispatchers.py b/interfaces/dispatchers.py index 2a3c8d9df..405b379b9 100644 --- a/interfaces/dispatchers.py +++ b/interfaces/dispatchers.py @@ -308,12 +308,15 @@ def register_events(self, func, events, sender_uids=None, names=None, weak=True) if not entry_ids: raise ValueError('Either sender_uid or name must specified') for entry_id in entry_ids: - if entry_id not in self._router: - self._router[entry_id] = {} - for event in events: - if event not in self._router[entry_id]: - self._router[entry_id][event] = CallbackContainer() - self._router[entry_id][event].register(func, weak=weak) + self.__register_entry(entry_id, events, func, weak) + + def __register_entry(self, entry_id, events, func, weak): + if entry_id not in self._router: + self._router[entry_id] = {} + for event in events: + if event not in self._router[entry_id]: + self._router[entry_id][event] = CallbackContainer() + self._router[entry_id][event].register(func, weak=weak) def dispatch(self, event_, data): """Dispatches an event to all its registered callbacks @@ -325,21 +328,24 @@ def dispatch(self, event_, data): event_ (WalkoffEvent): The event to dispatch data (dict): The data to send to all the events """ + sender_name, sender_uid = self.__get_sender_ids(data, event_) + callbacks = self._get_callbacks(sender_uid, sender_name, event_) + for func in callbacks: + try: + args = (data,) if event_.event_type != EventType.controller else tuple() + func(*args) + except Exception as e: + _logger.exception('Error calling interface event handler: {}'.format(e)) + + @staticmethod + def __get_sender_ids(data, event_): if event_.event_type != EventType.controller: sender_uid = data['sender_uid'] sender_name = data['sender_name'] if 'sender_name' in data else None else: sender_uid = EventType.controller.name sender_name = None - callbacks = self._get_callbacks(sender_uid, sender_name, event_) - for func in callbacks: - try: - if event_.event_type != EventType.controller: - func(data) - else: - func() - except Exception as e: - _logger.exception('Error calling interface event handler: {}'.format(e)) + return sender_name, sender_uid def _get_callbacks(self, sender_uid, sender_name, event): """Gets all the callbacks associated with a given sender UID, name, and event @@ -353,13 +359,15 @@ def _get_callbacks(self, sender_uid, sender_name, event): set(func): The callbacks registered """ all_callbacks = set() - if sender_uid in self._router and event in self._router[sender_uid]: - all_callbacks |= set(self._router[sender_uid][event]) + for sender_id in (sender_uid, sender_name): + if self.__is_event_registered_to_sender(sender_id, event): + all_callbacks |= set(self._router[sender_id][event]) - if sender_name is not None and sender_name in self._router and event in self._router[sender_name]: - all_callbacks |= set(self._router[sender_name][event]) return all_callbacks + def __is_event_registered_to_sender(self, sender_id, event): + return sender_id is not None and sender_id in self._router and event in self._router[sender_id] + def is_registered(self, entry, event, func): """Is a function registered for a given entry ID and event? diff --git a/update.py b/update.py index 3210a0640..d3f43b7cc 100644 --- a/update.py +++ b/update.py @@ -115,7 +115,8 @@ def migrate_workflows(flagged, inter, target): mode, tgt_version = validate_version(target) while inter and (mode is None): - target = input("Enter the version target, e.g. 'u0.5.2' to upgrade to 0.5.2 or 'd0.5.0' to downgrade to 0.5.0: ") + target = input( + "Enter the version target, e.g. 'u0.5.2' to upgrade to 0.5.2 or 'd0.5.0' to downgrade to 0.5.0: ") mode, tgt_version = validate_version(target) print("{} workflows to version {}".format(mode, tgt_version)) @@ -133,7 +134,8 @@ def alembic(flagged, inter): names = ["walkoff"] for name in names: try: - r = (subprocess.check_output(["alembic", "--name", name, "current"], stderr=subprocess.STDOUT, universal_newlines=True)) + r = (subprocess.check_output(["alembic", "--name", name, "current"], stderr=subprocess.STDOUT, + universal_newlines=True)) if "(head)" in r: print("Already up to date, no alembic upgrade needed.") else: @@ -153,44 +155,41 @@ def alembic(flagged, inter): print("Could not install alembic, are you root/administrator?") -def main(): - +def create_cli_parser(): parser = argparse.ArgumentParser() parser.add_argument("-i", "--interactive", help="Interactively prompt 'yes or no' for each choice.", action="store_true") - parser.add_argument("-e", "--everything", help="Equivalent to using all flags", action="store_true") - parser.add_argument("-a", "--archive", help="Creates a backup archive of the entire WALKOFF directory in backups/", action="store_true") - parser.add_argument("-p", "--pull", help="Performs a 'git pull' from the currently set branch", action="store_true") - parser.add_argument("-c", "--clean", help="Removes all .pyc and .pyo cache files.", action="store_true") - parser.add_argument("-s", "--setup", help="Performs WALKOFF setup. Requires root/administrator privileges.", action="store_true") - parser.add_argument("-ma", "--migrateapps", help="Runs app API migration script. Not reversible at this time.", action="store_true") - parser.add_argument("-mw", "--migrateworkflows", help="Runs workflow migration script to upgrade/downgrade to the specified version," " e.g. 'u0.5.2' to upgrade to 0.5.2 or 'd0.5.0' to downgrade to 0.5.0") - parser.add_argument("-md", "--migratedatabase", help="Runs alembic database migration.", action="store_true") + return parser + + +def main(): + + parser = create_cli_parser() if not len(sys.argv) > 1: parser.print_help() From 3269b384e016169b1533fc7a3c28d03a4ecc8419 Mon Sep 17 00:00:00 2001 From: JustinTervala Date: Fri, 5 Jan 2018 16:19:01 -0500 Subject: [PATCH 04/15] Added release badge to README --- README.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index d5244575b..811eeca39 100644 --- a/README.md +++ b/README.md @@ -1,5 +1,6 @@ Linux [![Build Status](https://travis-ci.org/iadgov/WALKOFF.svg?branch=master)](https://travis-ci.org/iadgov/WALKOFF) Windows [![Build status](https://ci.appveyor.com/api/projects/status/wsuo57tb1u593hox/branch/development?svg=true)](https://ci.appveyor.com/project/JustinTervala/walkoff-u1gc1/branch/master) -[![Maintainability](https://api.codeclimate.com/v1/badges/330249e13845a07a69a2/maintainability)](https://codeclimate.com/github/iadgov/WALKOFF/maintainability) +[![Maintainability](https://api.codeclimate.com/v1/badges/330249e13845a07a69a2/maintainability)](https://codeclimate.com/github/iadgov/WALKOFF/maintainability)[![GitHub (pre-)release](https://img.shields.io/github/release/iadgov/WALKOFF/all.svg?style=flat)](release) + From 42ba25186d5a224a6b08d9ccc6f644bcb6d2462b Mon Sep 17 00:00:00 2001 From: Justin Tervala Date: Tue, 2 Jan 2018 16:54:11 -0500 Subject: [PATCH 05/15] Cache uses namedtuple for action entries --- tests/test_app_cache.py | 448 +++++++++++++++++++-------------- walkoff/appgateway/appcache.py | 74 +++--- 2 files changed, 294 insertions(+), 228 deletions(-) diff --git a/tests/test_app_cache.py b/tests/test_app_cache.py index f61a4ac5a..8f663fc8d 100644 --- a/tests/test_app_cache.py +++ b/tests/test_app_cache.py @@ -2,7 +2,7 @@ from importlib import import_module from unittest import TestCase -from walkoff.appgateway import AppCache +from walkoff.appgateway.appcache import AppCache, AppCacheActionEntry, WalkoffTag from walkoff.appgateway.decorators import action from walkoff.helpers import UnknownApp, UnknownAppAction @@ -11,9 +11,26 @@ def f1(): pass class TestAppCache(TestCase): + + @classmethod + def setUpClass(cls): + cls.action_tag = [WalkoffTag.action] + cls.condition_tag = [WalkoffTag.condition] + cls.transform_tag = [WalkoffTag.transform] + cls.maxDiff = None + def setUp(self): self.cache = AppCache() + def assert_cache_has_apps(self, apps): + self.assertSetEqual(set(self.cache._cache.keys()), set(apps)) + + def assert_cached_app_has_actions(self, app='app1', actions={}): + self.assertDictEqual(self.cache._cache[app]['actions'], actions) + + def assert_cache_has_main(self, main, app='A'): + self.assertEqual(self.cache._cache[app]['main'], main) + def test_init(self): self.assertDictEqual(self.cache._cache, {}) @@ -38,78 +55,98 @@ def test_strip_base_module_from_qualified_name_invalid_base_module(self): self.assertEqual(AppCache._strip_base_module_from_qualified_name('tests.test_app_cache.f1', 'invalid'), 'tests.test_app_cache.f1') + def cache_action(self, func, app='app1', tags=None): + tags = self.action_tag if tags is None else tags + self.cache._cache_action( + func, app, 'tests.test_app_cache.TestAppCache', tags, cls=TestAppCache) + def test_cache_action_empty_cache(self): def x(): pass - - self.cache._cache_action(x, 'app1', 'tests.test_app_cache.TestAppCache', 'action', cls=TestAppCache) - self.assertDictEqual(self.cache._cache, {'app1': {'actions': {'x': {'run': x, 'bound': False}}}}) + self.cache_action(x) + self.assert_cache_has_apps({'app1'}) + self.assert_cached_app_has_actions( + actions={'x': AppCacheActionEntry(run=x, is_bound=False, tags=self.action_tag)}) def test_cache_action_existing_app_name_entry(self): def x(): pass self.cache._cache['app1'] = {} - self.cache._cache_action(x, 'app1', 'tests.test_app_cache.TestAppCache', 'action', cls=TestAppCache) - self.assertDictEqual(self.cache._cache, {'app1': {'actions': {'x': {'run': x, 'bound': False}}}}) + self.cache_action(x) + self.assert_cache_has_apps({'app1'}) + self.assert_cached_app_has_actions( + actions={'x': AppCacheActionEntry(run=x, is_bound=False, tags=self.action_tag)}) def test_cache_action_existing_app_name_and_actions_tag(self): def x(): pass self.cache._cache['app1'] = {'actions': {}} - self.cache._cache_action(x, 'app1', 'tests.test_app_cache.TestAppCache', 'action', cls=TestAppCache) - self.assertDictEqual(self.cache._cache, {'app1': {'actions': {'x': {'run': x, 'bound': False}}}}) + self.cache_action(x) + self.assert_cache_has_apps({'app1'}) + self.assert_cached_app_has_actions( + actions={'x': AppCacheActionEntry(run=x, is_bound=False, tags=self.action_tag)}) def test_cache_action_multiple_actions_same_app(self): def x(): pass def y(): pass - - self.cache._cache_action(x, 'app1', 'tests.test_app_cache.TestAppCache', 'action', cls=TestAppCache) - self.cache._cache_action(y, 'app1', 'tests.test_app_cache.TestAppCache', 'action', cls=TestAppCache) - self.assertDictEqual(self.cache._cache, {'app1': {'actions': {'x': {'run': x, 'bound': False}, - 'y': {'run': y, 'bound': False}}}}) + self.cache_action(x) + self.cache_action(y) + self.assert_cache_has_apps({'app1'}) + self.assert_cached_app_has_actions( + actions={'x': AppCacheActionEntry(run=x, is_bound=False, tags=self.action_tag), + 'y': AppCacheActionEntry(run=y, is_bound=False, tags=self.action_tag)}) def test_cache_action_multiple_actions_different_app(self): def x(): pass def y(): pass - - self.cache._cache_action(x, 'app1', 'tests.test_app_cache.TestAppCache', 'action', cls=TestAppCache) - self.cache._cache_action(y, 'app2', 'tests.test_app_cache.TestAppCache', 'action', cls=TestAppCache) - self.assertDictEqual(self.cache._cache, {'app1': {'actions': {'x': {'run': x, 'bound': False}}}, - 'app2': {'actions': {'y': {'run': y, 'bound': False}}}}) + self.cache_action(x) + self.cache_action(y, app='app2') + self.assert_cache_has_apps({'app1', 'app2'}) + self.assert_cached_app_has_actions( + actions={'x': AppCacheActionEntry(run=x, is_bound=False, tags=self.action_tag)}) + self.assert_cached_app_has_actions(app='app2', + actions={'y': AppCacheActionEntry(run=y, is_bound=False, tags=self.action_tag)}) def test_cache_action_overwrite(self): def x(): pass original_id = id(x) - self.cache._cache_action(x, 'app1', 'tests.test_app_cache.TestAppCache', 'action', cls=TestAppCache) - + self.cache_action(x) def x(): pass - - self.cache._cache_action(x, 'app1', 'tests.test_app_cache.TestAppCache', 'action', cls=TestAppCache) - self.assertDictEqual(self.cache._cache, {'app1': {'actions': {'x': {'run': x, 'bound': False}}}}) - self.assertNotEqual(id(self.cache._cache['app1']['actions']['x']['run']), original_id) + self.cache_action(x) + self.assert_cache_has_apps({'app1'}) + self.assert_cached_app_has_actions( + actions={'x': AppCacheActionEntry(run=x, is_bound=False, tags=self.action_tag)} + ) + self.assertNotEqual(id(self.cache._cache['app1']['actions']['x'].run), original_id) def test_cache_app_no_actions_empty_cache(self): class A: pass - self.cache._cache_app(A, 'A', 'tests.test_app_cache.TestAppCache') - self.assertDictEqual(self.cache._cache, {'A': {'main': A, 'actions': {}}}) + self.cache_app(A) + self.assert_cache_has_main(A) + self.assert_cached_app_has_actions(app='A') def test_cache_app_no_actions_app_name_exists(self): class A: pass self.cache._cache['A'] = {} + self.cache_app(A) + self.assert_cache_has_main(A) + self.assert_cached_app_has_actions(app='A') + + def cache_app(self, A): self.cache._cache_app(A, 'A', 'tests.test_app_cache.TestAppCache') - self.assertDictEqual(self.cache._cache, {'A': {'main': A, 'actions': {}}}) def test_cache_app_no_actions_app_name_exists_main_is_empty(self): class A: pass self.cache._cache['A'] = {'main': None} - self.cache._cache_app(A, 'A', 'tests.test_app_cache.TestAppCache') - self.assertDictEqual(self.cache._cache, {'A': {'main': A, 'actions': {}}}) + self.cache_app(A) + self.assert_cache_has_main(A) + self.assert_cached_app_has_actions(app='A') def test_cache_app_with_actions_empty_cache(self): class A: @@ -121,12 +158,12 @@ def y(self): pass def z(self): pass - self.cache._cache_app(A, 'A', 'tests.test_app_cache.TestAppCache') - self.maxDiff = None - self.assertDictEqual(self.cache._cache, {'A': {'main': A, - 'actions': { - 'tests.test_app_cache.A.x': {'run': A.x, 'bound': True}, - 'tests.test_app_cache.A.y': {'run': A.y, 'bound': True}}}}) + self.cache_app(A) + self.assert_cache_has_main(A) + self.assert_cached_app_has_actions( + app='A', + actions={'tests.test_app_cache.A.x': AppCacheActionEntry(run=A.x, is_bound=True, tags=self.action_tag), + 'tests.test_app_cache.A.y': AppCacheActionEntry(run=A.y, is_bound=True, tags=self.action_tag)}) def test_cache_app_with_actions_app_name_exists(self): class A: @@ -139,11 +176,12 @@ def y(self): pass def z(self): pass self.cache._cache['A'] = {} - self.cache._cache_app(A, 'A', 'tests.test_app_cache.TestAppCache') - self.assertDictEqual(self.cache._cache, {'A': {'main': A, - 'actions': { - 'tests.test_app_cache.A.x': {'run': A.x, 'bound': True}, - 'tests.test_app_cache.A.y': {'run': A.y, 'bound': True}}}}) + self.cache_app(A) + self.assert_cache_has_main(A) + self.assert_cached_app_has_actions( + app='A', + actions={'tests.test_app_cache.A.x': AppCacheActionEntry(run=A.x, is_bound=True, tags=self.action_tag), + 'tests.test_app_cache.A.y': AppCacheActionEntry(run=A.y, is_bound=True, tags=self.action_tag)}) def test_cache_app_with_actions_app_name_exists_main_is_empty(self): class A: @@ -156,10 +194,12 @@ def y(self): pass def z(self): pass self.cache._cache['A'] = {'main': None} - self.cache._cache_app(A, 'A', 'tests.test_app_cache.TestAppCache') - self.assertDictEqual(self.cache._cache, - {'A': {'main': A, 'actions': {'tests.test_app_cache.A.x': {'run': A.x, 'bound': True}, - 'tests.test_app_cache.A.y': {'run': A.y, 'bound': True}}}}) + self.cache_app(A) + self.assert_cache_has_main(A) + self.assert_cached_app_has_actions( + app='A', + actions={'tests.test_app_cache.A.x': AppCacheActionEntry(run=A.x, is_bound=True, tags=self.action_tag), + 'tests.test_app_cache.A.y': AppCacheActionEntry(run=A.y, is_bound=True, tags=self.action_tag)}) def test_cache_app_with_actions_and_global_actions(self): class A: @@ -173,12 +213,14 @@ def z(self): pass def z(): pass - self.cache._cache_app(A, 'A', 'tests.test_app_cache.TestAppCache') - self.cache._cache_action(z, 'A', 'tests.test_app_cache.TestAppCache', 'action', cls=TestAppCache) - self.assertDictEqual(self.cache._cache, - {'A': {'main': A, 'actions': {'tests.test_app_cache.A.x': {'run': A.x, 'bound': True}, - 'tests.test_app_cache.A.y': {'run': A.y, 'bound': True}, - 'z': {'run': z, 'bound': False}}}}) + self.cache_app(A) + self.cache_action(z, app='A') + self.assert_cache_has_main(A) + self.assert_cached_app_has_actions( + app='A', + actions={'tests.test_app_cache.A.x': AppCacheActionEntry(run=A.x, is_bound=True, tags=self.action_tag), + 'tests.test_app_cache.A.y': AppCacheActionEntry(run=A.y, is_bound=True, tags=self.action_tag), + 'z': AppCacheActionEntry(run=z, is_bound=False, tags=self.action_tag)}) def test_cache_app_with_actions_and_global_actions_name_conflict_resolved(self): class A: @@ -193,18 +235,20 @@ def z(self): pass def z(): pass - self.cache._cache_app(A, 'A', 'tests.test_app_cache.TestAppCache') - self.cache._cache_action(z, 'A', 'tests.test_app_cache.TestAppCache', 'action', cls=TestAppCache) - self.assertDictEqual(self.cache._cache, - {'A': {'main': A, 'actions': {'tests.test_app_cache.A.x': {'run': A.x, 'bound': True}, - 'tests.test_app_cache.A.y': {'run': A.y, 'bound': True}, - 'tests.test_app_cache.A.z': {'run': A.z, 'bound': True}, - 'z': {'run': z, 'bound': False}}}}) + self.cache_app(A) + self.cache_action(z, app='A') + self.assert_cache_has_main(A) + self.assert_cached_app_has_actions( + app='A', + actions={'tests.test_app_cache.A.x': AppCacheActionEntry(run=A.x, is_bound=True, tags=self.action_tag), + 'tests.test_app_cache.A.y': AppCacheActionEntry(run=A.y, is_bound=True, tags=self.action_tag), + 'tests.test_app_cache.A.z': AppCacheActionEntry(run=A.z, is_bound=True, tags=self.action_tag), + 'z': AppCacheActionEntry(run=z, is_bound=False, tags=self.action_tag)}) def test_clear_existing_bound_functions_no_actions(self): class A: pass - self.cache._cache_app(A, 'A', 'tests.test_app_cache.TestAppCache') + self.cache_app(A) self.cache._cache['A'].pop('actions') self.cache._clear_existing_bound_functions('A') self.assertDictEqual(self.cache._cache, {'A': {'main': A}}) @@ -214,11 +258,12 @@ def x(): pass def y(): pass - self.cache._cache_action(x, 'app1', 'tests.test_app_cache.TestAppCache', 'action', cls=TestAppCache) - self.cache._cache_action(y, 'app1', 'tests.test_app_cache.TestAppCache', 'action', cls=TestAppCache) + self.cache_action(x) + self.cache_action(y) self.cache._clear_existing_bound_functions('app1') - self.assertDictEqual(self.cache._cache, {'app1': {'actions': {'x': {'run': x, 'bound': False}, - 'y': {'run': y, 'bound': False}}}}) + self.assertDictEqual(self.cache._cache, + {'app1': {'actions': {'x': AppCacheActionEntry(run=x, is_bound=False, tags=self.action_tag), + 'y': AppCacheActionEntry(run=y, is_bound=False, tags=self.action_tag)}}}) def test_clear_existing_bound_functions(self): class A: @@ -232,29 +277,35 @@ def z(self): pass def z(): pass - self.cache._cache_app(A, 'A', 'tests.test_app_cache.TestAppCache') - self.cache._cache_action(z, 'A', 'tests.test_app_cache.TestAppCache', 'action', cls=TestAppCache) + self.cache_app(A) + self.cache_action(z, app='A') self.cache._clear_existing_bound_functions('A') - self.assertDictEqual(self.cache._cache, {'A': {'main': A, 'actions': {'z': {'run': z, 'bound': False}}}}) + self.assertDictEqual( + self.cache._cache, + {'A': {'main': A, 'actions': {'z': AppCacheActionEntry(run=z, is_bound=False, tags=self.action_tag)}}}) def test_cache_module(self): module = import_module('tests.testapps.HelloWorldBounded.main') from tests.testapps.HelloWorldBounded.main import Main, global1 self.cache._cache_module(module, 'HelloWorldBounded', 'tests.testapps') - self.maxDiff = None - expected = {'HelloWorldBounded': {'main': Main, - 'actions': {'main.Main.helloWorld': {'run': Main.helloWorld, 'bound': True}, - 'main.Main.repeatBackToMe': {'run': Main.repeatBackToMe, - 'bound': True}, - 'main.Main.returnPlusOne': {'run': Main.returnPlusOne, - 'bound': True}, - 'main.Main.pause': {'run': Main.pause, 'bound': True}, - 'main.Main.addThree': {'run': Main.addThree, 'bound': True}, - 'main.Main.buggy_action': {'run': Main.buggy_action, - 'bound': True}, - 'main.Main.json_sample': {'run': Main.json_sample, 'bound': True}, - 'main.global1': {'run': global1, 'bound': False}}}} - self.assertDictEqual(self.cache._cache, expected) + self.assert_cache_has_main(Main, app='HelloWorldBounded') + self.assert_cached_app_has_actions( + app='HelloWorldBounded', + actions={'main.Main.helloWorld': + AppCacheActionEntry(run=Main.helloWorld, is_bound=True, tags=self.action_tag), + 'main.Main.repeatBackToMe': + AppCacheActionEntry(run=Main.repeatBackToMe, is_bound=True, tags=self.action_tag), + 'main.Main.returnPlusOne': + AppCacheActionEntry(run=Main.returnPlusOne, is_bound=True, tags=self.action_tag), + 'main.Main.pause': + AppCacheActionEntry(run=Main.pause, is_bound=True, tags=self.action_tag), + 'main.Main.addThree': + AppCacheActionEntry(run=Main.addThree, is_bound=True, tags=self.action_tag), + 'main.Main.buggy_action': + AppCacheActionEntry(run=Main.buggy_action, is_bound=True, tags=self.action_tag), + 'main.Main.json_sample': + AppCacheActionEntry(run=Main.json_sample, is_bound=True, tags=self.action_tag), + 'main.global1': AppCacheActionEntry(run=global1, is_bound=False, tags=self.action_tag)}) def test_cache_module_nothing_found(self): module = import_module('tests.testapps.HelloWorldBounded.display') @@ -265,8 +316,9 @@ def test_cache_module_no_class(self): module = import_module('tests.testapps.HelloWorldBounded.actions') self.cache._cache_module(module, 'HelloWorldBounded', 'tests.testapps') from tests.testapps.HelloWorldBounded.actions import global2 - self.assertDictEqual(self.cache._cache, - {'HelloWorldBounded': {'actions': {'actions.global2': {'run': global2, 'bound': False}}}}) + self.assert_cached_app_has_actions( + app='HelloWorldBounded', + actions={'actions.global2': AppCacheActionEntry(run=global2, is_bound=False, tags=self.action_tag)}) def test_import_and_cache_submodules_from_string(self): self.cache._import_and_cache_submodules('tests.testapps.HelloWorldBounded', 'HelloWorldBounded', @@ -278,35 +330,38 @@ def test_import_and_cache_submodules_from_string(self): from tests.testapps.HelloWorldBounded.transforms import (top_level_filter, filter1, filter2, filter3, complex_filter, length, json_select, sub1_top_filter) - expected = {'HelloWorldBounded': {'main': Main, - 'actions': {'main.Main.helloWorld': {'run': Main.helloWorld, 'bound': True}, - 'main.Main.repeatBackToMe': {'run': Main.repeatBackToMe, - 'bound': True}, - 'main.Main.returnPlusOne': {'run': Main.returnPlusOne, - 'bound': True}, - 'main.Main.pause': {'run': Main.pause, 'bound': True}, - 'main.Main.addThree': {'run': Main.addThree, 'bound': True}, - 'main.Main.buggy_action': {'run': Main.buggy_action, - 'bound': True}, - 'main.Main.json_sample': {'run': Main.json_sample, 'bound': True}, - 'main.global1': {'run': global1, 'bound': False}, - 'actions.global2': {'run': global2, 'bound': False}}, - 'conditions': {'conditions.top_level_flag': {'run': top_level_flag}, - 'conditions.flag1': {'run': flag1}, - 'conditions.flag2': {'run': flag2}, - 'conditions.flag3': {'run': flag3}, - 'conditions.sub1_top_flag': {'run': sub1_top_flag}, - 'conditions.regMatch': {'run': regMatch}, - 'conditions.count': {'run': count}}, - 'transforms': {'transforms.top_level_filter': {'run': top_level_filter}, - 'transforms.filter2': {'run': filter2}, - 'transforms.sub1_top_filter': {'run': sub1_top_filter}, - 'transforms.filter3': {'run': filter3}, - 'transforms.filter1': {'run': filter1}, - 'transforms.complex_filter': {'run': complex_filter}, - 'transforms.length': {'run': length}, - 'transforms.json_select': {'run': json_select}}}} - self.assertDictEqual(self.cache._cache, expected) + self.assert_cache_has_main(Main, app='HelloWorldBounded') + expected = { + 'main.Main.helloWorld': AppCacheActionEntry(run=Main.helloWorld, is_bound=True, tags=self.action_tag), + 'main.Main.repeatBackToMe': + AppCacheActionEntry(run=Main.repeatBackToMe, is_bound=True, tags=self.action_tag), + 'main.Main.returnPlusOne': AppCacheActionEntry(run=Main.returnPlusOne, is_bound=True, tags=self.action_tag), + 'main.Main.pause': AppCacheActionEntry(run=Main.pause, is_bound=True, tags=self.action_tag), + 'main.Main.addThree': AppCacheActionEntry(run=Main.addThree, is_bound=True, tags=self.action_tag), + 'main.Main.buggy_action': AppCacheActionEntry(run=Main.buggy_action, is_bound=True, tags=self.action_tag), + 'main.Main.json_sample': AppCacheActionEntry(run=Main.json_sample, is_bound=True, tags=self.action_tag), + 'main.global1': AppCacheActionEntry(run=global1, is_bound=False, tags=self.action_tag), + 'actions.global2': AppCacheActionEntry(run=global2, is_bound=False, tags=self.action_tag), + 'conditions.top_level_flag': + AppCacheActionEntry(run=top_level_flag, is_bound=False, tags=self.condition_tag), + 'conditions.flag1': AppCacheActionEntry(run=flag1, is_bound=False, tags=self.condition_tag), + 'conditions.flag2': AppCacheActionEntry(run=flag2, is_bound=False, tags=self.condition_tag), + 'conditions.flag3': AppCacheActionEntry(run=flag3, is_bound=False, tags=self.condition_tag), + 'conditions.sub1_top_flag': AppCacheActionEntry(run=sub1_top_flag, is_bound=False, tags=self.condition_tag), + 'conditions.regMatch': AppCacheActionEntry(run=regMatch, is_bound=False, tags=self.condition_tag), + 'conditions.count': AppCacheActionEntry(run=count, is_bound=False, tags=self.condition_tag), + 'transforms.top_level_filter': + AppCacheActionEntry(run=top_level_filter, is_bound=False, tags=self.transform_tag), + 'transforms.filter2': AppCacheActionEntry(run=filter2, is_bound=False, tags=self.transform_tag), + 'transforms.sub1_top_filter': + AppCacheActionEntry(run=sub1_top_filter, is_bound=False, tags=self.transform_tag), + 'transforms.filter3': AppCacheActionEntry(run=filter3, is_bound=False, tags=self.transform_tag), + 'transforms.filter1':AppCacheActionEntry(run=filter1, is_bound=False, tags=self.transform_tag), + 'transforms.complex_filter': + AppCacheActionEntry(run=complex_filter, is_bound=False, tags=self.transform_tag), + 'transforms.length': AppCacheActionEntry(run=length, is_bound=False, tags=self.transform_tag), + 'transforms.json_select': AppCacheActionEntry(run=json_select, is_bound=False, tags=self.transform_tag)} + self.assert_cached_app_has_actions(app='HelloWorldBounded', actions=expected) def test_import_and_cache_submodules_from_module(self): module = import_module('tests.testapps.HelloWorldBounded') @@ -318,35 +373,38 @@ def test_import_and_cache_submodules_from_module(self): from tests.testapps.HelloWorldBounded.transforms import (top_level_filter, filter1, filter2, filter3, complex_filter, length, json_select, sub1_top_filter) - expected = {'HelloWorldBounded': {'main': Main, - 'actions': {'main.Main.helloWorld': {'run': Main.helloWorld, 'bound': True}, - 'main.Main.repeatBackToMe': {'run': Main.repeatBackToMe, - 'bound': True}, - 'main.Main.returnPlusOne': {'run': Main.returnPlusOne, - 'bound': True}, - 'main.Main.pause': {'run': Main.pause, 'bound': True}, - 'main.Main.addThree': {'run': Main.addThree, 'bound': True}, - 'main.Main.buggy_action': {'run': Main.buggy_action, - 'bound': True}, - 'main.Main.json_sample': {'run': Main.json_sample, 'bound': True}, - 'main.global1': {'run': global1, 'bound': False}, - 'actions.global2': {'run': global2, 'bound': False}}, - 'conditions': {'conditions.top_level_flag': {'run': top_level_flag}, - 'conditions.flag1': {'run': flag1}, - 'conditions.flag2': {'run': flag2}, - 'conditions.flag3': {'run': flag3}, - 'conditions.sub1_top_flag': {'run': sub1_top_flag}, - 'conditions.regMatch': {'run': regMatch}, - 'conditions.count': {'run': count}}, - 'transforms': {'transforms.top_level_filter': {'run': top_level_filter}, - 'transforms.filter2': {'run': filter2}, - 'transforms.sub1_top_filter': {'run': sub1_top_filter}, - 'transforms.filter3': {'run': filter3}, - 'transforms.filter1': {'run': filter1}, - 'transforms.complex_filter': {'run': complex_filter}, - 'transforms.length': {'run': length}, - 'transforms.json_select': {'run': json_select}}}} - self.assertDictEqual(self.cache._cache, expected) + self.assert_cache_has_main(Main, app='HelloWorldBounded') + expected = { + 'main.Main.helloWorld': AppCacheActionEntry(run=Main.helloWorld, is_bound=True, tags=self.action_tag), + 'main.Main.repeatBackToMe': + AppCacheActionEntry(run=Main.repeatBackToMe, is_bound=True, tags=self.action_tag), + 'main.Main.returnPlusOne': AppCacheActionEntry(run=Main.returnPlusOne, is_bound=True, tags=self.action_tag), + 'main.Main.pause': AppCacheActionEntry(run=Main.pause, is_bound=True, tags=self.action_tag), + 'main.Main.addThree': AppCacheActionEntry(run=Main.addThree, is_bound=True, tags=self.action_tag), + 'main.Main.buggy_action': AppCacheActionEntry(run=Main.buggy_action, is_bound=True, tags=self.action_tag), + 'main.Main.json_sample': AppCacheActionEntry(run=Main.json_sample, is_bound=True, tags=self.action_tag), + 'main.global1': AppCacheActionEntry(run=global1, is_bound=False, tags=self.action_tag), + 'actions.global2': AppCacheActionEntry(run=global2, is_bound=False, tags=self.action_tag), + 'conditions.top_level_flag': + AppCacheActionEntry(run=top_level_flag, is_bound=False, tags=self.condition_tag), + 'conditions.flag1': AppCacheActionEntry(run=flag1, is_bound=False, tags=self.condition_tag), + 'conditions.flag2': AppCacheActionEntry(run=flag2, is_bound=False, tags=self.condition_tag), + 'conditions.flag3': AppCacheActionEntry(run=flag3, is_bound=False, tags=self.condition_tag), + 'conditions.sub1_top_flag': AppCacheActionEntry(run=sub1_top_flag, is_bound=False, tags=self.condition_tag), + 'conditions.regMatch': AppCacheActionEntry(run=regMatch, is_bound=False, tags=self.condition_tag), + 'conditions.count': AppCacheActionEntry(run=count, is_bound=False, tags=self.condition_tag), + 'transforms.top_level_filter': + AppCacheActionEntry(run=top_level_filter, is_bound=False, tags=self.transform_tag), + 'transforms.filter2': AppCacheActionEntry(run=filter2, is_bound=False, tags=self.transform_tag), + 'transforms.sub1_top_filter': + AppCacheActionEntry(run=sub1_top_filter, is_bound=False, tags=self.transform_tag), + 'transforms.filter3': AppCacheActionEntry(run=filter3, is_bound=False, tags=self.transform_tag), + 'transforms.filter1': AppCacheActionEntry(run=filter1, is_bound=False, tags=self.transform_tag), + 'transforms.complex_filter': + AppCacheActionEntry(run=complex_filter, is_bound=False, tags=self.transform_tag), + 'transforms.length': AppCacheActionEntry(run=length, is_bound=False, tags=self.transform_tag), + 'transforms.json_select': AppCacheActionEntry(run=json_select, is_bound=False, tags=self.transform_tag)} + self.assert_cached_app_has_actions(app='HelloWorldBounded', actions=expected) def test_path_to_module_no_slashes(self): self.assertEqual(AppCache._path_to_module('apppath'), 'apppath') @@ -370,44 +428,48 @@ def test_cache_apps(self): length, json_select, sub1_top_filter) from tests.testapps.HelloWorldBounded.actions import global2 from tests.testapps.DailyQuote.main import Main as DailyMain - self.maxDiff = None - expected = {'HelloWorldBounded': {'main': Main, - 'actions': {'main.Main.helloWorld': {'run': Main.helloWorld, 'bound': True}, - 'main.Main.repeatBackToMe': {'run': Main.repeatBackToMe, - 'bound': True}, - 'main.Main.returnPlusOne': {'run': Main.returnPlusOne, - 'bound': True}, - 'main.Main.pause': {'run': Main.pause, 'bound': True}, - 'main.Main.addThree': {'run': Main.addThree, 'bound': True}, - 'main.Main.buggy_action': {'run': Main.buggy_action, - 'bound': True}, - 'main.Main.json_sample': {'run': Main.json_sample, 'bound': True}, - 'main.global1': {'run': global1, 'bound': False}, - 'actions.global2': {'run': global2, 'bound': False}}, - 'conditions': {'conditions.top_level_flag': {'run': top_level_flag}, - 'conditions.flag1': {'run': flag1}, - 'conditions.flag2': {'run': flag2}, - 'conditions.flag3': {'run': flag3}, - 'conditions.sub1_top_flag': {'run': sub1_top_flag}, - 'conditions.regMatch': {'run': regMatch}, - 'conditions.count': {'run': count}}, - 'transforms': {'transforms.top_level_filter': {'run': top_level_filter}, - 'transforms.filter2': {'run': filter2}, - 'transforms.sub1_top_filter': {'run': sub1_top_filter}, - 'transforms.filter3': {'run': filter3}, - 'transforms.filter1': {'run': filter1}, - 'transforms.complex_filter': {'run': complex_filter}, - 'transforms.length': {'run': length}, - 'transforms.json_select': {'run': json_select}}}, - 'DailyQuote': {'main': DailyMain, - 'actions': {'main.Main.quoteIntro': {'run': DailyMain.quoteIntro, 'bound': True}, - 'main.Main.repeatBackToMe': {'run': DailyMain.repeatBackToMe, - 'bound': True}, - 'main.Main.forismaticQuote': {'run': DailyMain.forismaticQuote, - 'bound': True}, - 'main.Main.getQuote': {'run': DailyMain.getQuote, 'bound': True}}}} - self.cache._cache.pop("HelloWorld") - self.assertDictEqual(self.cache._cache, expected) + self.assert_cache_has_main(Main, app='HelloWorldBounded') + hello_world_expected = { + 'main.Main.helloWorld': AppCacheActionEntry(run=Main.helloWorld, is_bound=True, tags=self.action_tag), + 'main.Main.repeatBackToMe': + AppCacheActionEntry(run=Main.repeatBackToMe, is_bound=True, tags=self.action_tag), + 'main.Main.returnPlusOne': AppCacheActionEntry(run=Main.returnPlusOne, is_bound=True, tags=self.action_tag), + 'main.Main.pause': AppCacheActionEntry(run=Main.pause, is_bound=True, tags=self.action_tag), + 'main.Main.addThree': AppCacheActionEntry(run=Main.addThree, is_bound=True, tags=self.action_tag), + 'main.Main.buggy_action': AppCacheActionEntry(run=Main.buggy_action, is_bound=True, tags=self.action_tag), + 'main.Main.json_sample': AppCacheActionEntry(run=Main.json_sample, is_bound=True, tags=self.action_tag), + 'main.global1': AppCacheActionEntry(run=global1, is_bound=False, tags=self.action_tag), + 'actions.global2': AppCacheActionEntry(run=global2, is_bound=False, tags=self.action_tag), + 'conditions.top_level_flag': + AppCacheActionEntry(run=top_level_flag, is_bound=False, tags=self.condition_tag), + 'conditions.flag1': AppCacheActionEntry(run=flag1, is_bound=False, tags=self.condition_tag), + 'conditions.flag2': AppCacheActionEntry(run=flag2, is_bound=False, tags=self.condition_tag), + 'conditions.flag3': AppCacheActionEntry(run=flag3, is_bound=False, tags=self.condition_tag), + 'conditions.sub1_top_flag': AppCacheActionEntry(run=sub1_top_flag, is_bound=False, tags=self.condition_tag), + 'conditions.regMatch': AppCacheActionEntry(run=regMatch, is_bound=False, tags=self.condition_tag), + 'conditions.count': AppCacheActionEntry(run=count, is_bound=False, tags=self.condition_tag), + 'transforms.top_level_filter': + AppCacheActionEntry(run=top_level_filter, is_bound=False, tags=self.transform_tag), + 'transforms.filter2': AppCacheActionEntry(run=filter2, is_bound=False, tags=self.transform_tag), + 'transforms.sub1_top_filter': + AppCacheActionEntry(run=sub1_top_filter, is_bound=False, tags=self.transform_tag), + 'transforms.filter3': AppCacheActionEntry(run=filter3, is_bound=False, tags=self.transform_tag), + 'transforms.filter1': AppCacheActionEntry(run=filter1, is_bound=False, tags=self.transform_tag), + 'transforms.complex_filter': + AppCacheActionEntry(run=complex_filter, is_bound=False, tags=self.transform_tag), + 'transforms.length': AppCacheActionEntry(run=length, is_bound=False, tags=self.transform_tag), + 'transforms.json_select': AppCacheActionEntry(run=json_select, is_bound=False, tags=self.transform_tag)} + self.assert_cached_app_has_actions(app='HelloWorldBounded', actions=hello_world_expected) + self.assert_cache_has_main(DailyMain, app='DailyQuote') + daily_quote_expected = { + 'main.Main.quoteIntro': AppCacheActionEntry(run=DailyMain.quoteIntro, is_bound=True, tags=self.action_tag), + 'main.Main.repeatBackToMe': + AppCacheActionEntry(run=DailyMain.repeatBackToMe, is_bound=True, tags=self.action_tag), + 'main.Main.forismaticQuote': + AppCacheActionEntry(run=DailyMain.forismaticQuote, is_bound=True, tags=self.action_tag), + 'main.Main.getQuote': AppCacheActionEntry(run=DailyMain.getQuote, is_bound=True, tags=self.action_tag)} + self.assert_cache_has_apps({'HelloWorldBounded', 'HelloWorld', 'DailyQuote'}) + self.assert_cached_app_has_actions(app='DailyQuote', actions=daily_quote_expected) def test_clear_cache_empty_cache(self): self.cache.clear() @@ -423,7 +485,7 @@ class A: pass class B: pass - self.cache._cache_app(A, 'A', 'tests.test_app_cache.TestAppCache') + self.cache_app(A) self.cache._cache_app(B, 'B', 'tests.test_app_cache.TestAppCache') self.assertSetEqual(set(self.cache.get_app_names()), {'A', 'B'}) @@ -437,7 +499,7 @@ def test_get_app_empty_cache(self): def test_get_app_missing_app(self): class A: pass - self.cache._cache_app(A, 'A', 'tests.test_app_cache.TestAppCache') + self.cache_app(A) with self.assertRaises(UnknownApp): self.cache.get_app('B') @@ -453,7 +515,7 @@ class A: pass class B: pass - self.cache._cache_app(A, 'A', 'tests.test_app_cache.TestAppCache') + self.cache_app(A) self.cache._cache_app(B, 'B', 'tests.test_app_cache.TestAppCache') self.assertEqual(self.cache.get_app('A'), A) @@ -464,14 +526,14 @@ def test_get_app_action_names_empty_cache(self): def test_get_app_action_names_unknown_app(self): class A: pass - self.cache._cache_app(A, 'A', 'tests.test_app_cache.TestAppCache') + self.cache_app(A) with self.assertRaises(UnknownApp): self.cache.get_app_action_names('B') def test_get_app_action_names_no_actions(self): class A: pass - self.cache._cache_app(A, 'A', 'tests.test_app_cache.TestAppCache') + self.cache_app(A) self.assertListEqual(self.cache.get_app_action_names('A'), []) def test_get_app_action_names(self): @@ -489,7 +551,7 @@ def a(self): pass @action def b(self): pass - self.cache._cache_app(A, 'A', 'tests.test_app_cache.TestAppCache') + self.cache_app(A) self.cache._cache_app(B, 'B', 'tests.test_app_cache.TestAppCache') app_actions = self.cache.get_app_action_names('A') self.assertEqual(len(app_actions), 2) @@ -507,14 +569,14 @@ def x(self): pass @action def y(self): pass - self.cache._cache_app(A, 'A', 'tests.test_app_cache.TestAppCache') + self.cache_app(A) with self.assertRaises(UnknownApp): self.cache.get_app_action('B', 'x') def test_get_app_action_no_actions(self): class A: pass - self.cache._cache_app(A, 'A', 'tests.test_app_cache.TestAppCache') + self.cache_app(A) with self.assertRaises(UnknownAppAction): self.cache.get_app_action('A', 'x') @@ -526,7 +588,7 @@ def x(self): pass @action def y(self): pass - self.cache._cache_app(A, 'A', 'tests.test_app_cache.TestAppCache') + self.cache_app(A) with self.assertRaises(UnknownAppAction): self.cache.get_app_action('A', 'z') @@ -545,7 +607,7 @@ def a(self): pass @action def b(self): pass - self.cache._cache_app(A, 'A', 'tests.test_app_cache.TestAppCache') + self.cache_app(A) self.cache._cache_app(B, 'B', 'tests.test_app_cache.TestAppCache') self.assertEqual(self.cache.get_app_action('A', 'tests.test_app_cache.A.x'), A.x) @@ -561,14 +623,14 @@ def x(self): pass @action def y(self): pass - self.cache._cache_app(A, 'A', 'tests.test_app_cache.TestAppCache') + self.cache_app(A) with self.assertRaises(UnknownApp): self.cache.is_app_action_bound('B', 'tests.test_app_cache.B.x') def test_is_app_action_bound_no_actions(self): class A: pass - self.cache._cache_app(A, 'A', 'tests.test_app_cache.TestAppCache') + self.cache_app(A) with self.assertRaises(UnknownAppAction): self.cache.is_app_action_bound('A', 'tests.test_app_cache.A.x') @@ -580,7 +642,7 @@ def x(self): pass @action def y(self): pass - self.cache._cache_app(A, 'A', 'tests.test_app_cache.TestAppCache') + self.cache_app(A) with self.assertRaises(UnknownAppAction): self.cache.is_app_action_bound('A', 'tests.test_app_cache.A.z') @@ -599,7 +661,7 @@ def a(self): pass @action def b(self): pass - self.cache._cache_app(A, 'A', 'tests.test_app_cache.TestAppCache') + self.cache_app(A) self.cache._cache_app(B, 'B', 'tests.test_app_cache.TestAppCache') self.assertTrue(self.cache.is_app_action_bound('A', 'tests.test_app_cache.A.x')) @@ -620,7 +682,9 @@ def b(self): pass def xx(): pass - self.cache._cache_app(A, 'A', 'tests.test_app_cache.TestAppCache') + self.cache_app(A) self.cache._cache_app(B, 'B', 'tests.test_app_cache.TestAppCache') self.cache._cache_action(xx, 'A', 'tests.test_app_cache.TestAppCache', 'action', cls=TestAppCache) self.assertFalse(self.cache.is_app_action_bound('A', 'xx')) + + diff --git a/walkoff/appgateway/appcache.py b/walkoff/appgateway/appcache.py index e19d2f74a..51483fcf9 100644 --- a/walkoff/appgateway/appcache.py +++ b/walkoff/appgateway/appcache.py @@ -4,6 +4,7 @@ import pkgutil import sys from importlib import import_module +from collections import namedtuple from six import string_types @@ -12,6 +13,8 @@ _logger = logging.getLogger(__name__) +AppCacheActionEntry = namedtuple('AppCacheEntry', ['run', 'is_bound', 'tags']) +# AppCacheAppEntry = namedtuple('AppCacheAppEntry', ['main', 'actions']) class AppCache(object): """Object which caches app actions, conditions, and transforms @@ -20,9 +23,9 @@ class AppCache(object): _cache (dict): The cache of the app and functions """ # TODO: Use an enum for this? Something better than this anyways - exception_lookup = {'actions': UnknownAppAction, - 'conditions': UnknownCondition, - 'transforms': UnknownTransform} + exception_lookup = {WalkoffTag.action: UnknownAppAction, + WalkoffTag.condition: UnknownCondition, + WalkoffTag.transform: UnknownTransform} def __init__(self): self._cache = {} @@ -92,7 +95,7 @@ def get_app_action_names(self, app_name): Raises: UnknownApp: If the app is not found in the cache """ - return self._get_function_type_names(app_name, 'actions') + return self._get_function_type_names(app_name, WalkoffTag.action) def get_app_action(self, app_name, action_name): """Gets the action function for a given app and action name @@ -108,7 +111,7 @@ def get_app_action(self, app_name, action_name): UnknownApp: If the app is not found in the cache UnknownAppAction: If the app does not have the specified action """ - return self._get_function_type(app_name, action_name, 'actions') + return self._get_function_type(app_name, action_name, WalkoffTag.action) def is_app_action_bound(self, app_name, action_name): """Determines if the action is bound (meaning it's inside a class) or not @@ -133,7 +136,7 @@ def is_app_action_bound(self, app_name, action_name): _logger.error('Cannot locate app {} in cache!'.format(app_name)) raise UnknownApp(app_name) try: - return app_cache['actions'][action_name]['bound'] + return app_cache['actions'][action_name].is_bound except KeyError: _logger.error('App {0} has no action {1}'.format(app_name, action_name)) raise UnknownAppAction(app_name, action_name) @@ -150,7 +153,7 @@ def get_app_condition_names(self, app_name): Raises: UnknownApp: If the app is not found in the cache """ - return self._get_function_type_names(app_name, 'conditions') + return self._get_function_type_names(app_name, WalkoffTag.condition) def get_app_condition(self, app_name, condition_name): """Gets the condition function for a given app and action name @@ -166,7 +169,7 @@ def get_app_condition(self, app_name, condition_name): UnknownApp: If the app is not found in the cache UnknownCondition: If the app does not have the specified condition """ - return self._get_function_type(app_name, condition_name, 'conditions') + return self._get_function_type(app_name, condition_name, WalkoffTag.condition) def get_app_transform_names(self, app_name): """Gets all the names of the transforms for a given app @@ -180,7 +183,7 @@ def get_app_transform_names(self, app_name): Raises: UnknownApp: If the app is not found in the cache """ - return self._get_function_type_names(app_name, 'transforms') + return self._get_function_type_names(app_name, WalkoffTag.transform) def get_app_transform(self, app_name, transform_name): """Gets the transform function for a given app and action name @@ -196,14 +199,14 @@ def get_app_transform(self, app_name, transform_name): UnknownApp: If the app is not found in the cache UnknownCondition: If the app does not have the specified condition """ - return self._get_function_type(app_name, transform_name, 'transforms') + return self._get_function_type(app_name, transform_name, WalkoffTag.transform) def _get_function_type_names(self, app_name, function_type): - """Gets all the names for a given function type ('actions', 'conditions', 'transforms') for an app + """Gets all the names for a given function type ('action', 'condition', 'transform') for an app Args: app_name (str): The name of the app - function_type (str): 'actions', 'conditions' or 'transforms' + function_type (WalkoffTag): tag to search for Returns: list[str]: List of all the names of the functions of the given type @@ -213,9 +216,10 @@ def _get_function_type_names(self, app_name, function_type): """ try: app_cache = self._cache[app_name] - if function_type not in app_cache: + if 'actions' not in app_cache: return [] - return list(app_cache[function_type].keys()) + return [action_name for action_name, action_entry in app_cache['actions'].items() + if function_type in action_entry.tags] except KeyError: _logger.error('Cannot locate app {} in cache!'.format(app_name)) raise UnknownApp(app_name) @@ -226,7 +230,7 @@ def _get_function_type(self, app_name, function_name, function_type): Args: app_name (str): Name of the app function_name(str): Name of the action - function_type (str): Type of function, 'actions', 'conditions', or 'transforms' + function_type (WalkoffTag): Type of function, 'actions', 'conditions', or 'transforms' Returns: func: The function @@ -239,16 +243,16 @@ def _get_function_type(self, app_name, function_name, function_type): """ try: app_cache = self._cache[app_name] - if function_type not in app_cache: - _logger.warning('App {0} has no {1}.'.format(app_name, function_type)) + if 'actions' not in app_cache: + _logger.warning('App {0} has no actions.'.format(app_name)) raise self.exception_lookup[function_type](app_name, function_name) except KeyError: _logger.error('Cannot locate app {} in cache!'.format(app_name)) raise UnknownApp(app_name) try: - return app_cache[function_type][function_name]['run'] + return app_cache['actions'][function_name].run except KeyError: - _logger.error('App {0} has no {1} {2}'.format(app_name, function_type, function_name)) + _logger.error('App {0} has no {1} {2}'.format(app_name, function_type.name, function_name)) raise self.exception_lookup[function_type](app_name, function_name) @staticmethod @@ -306,8 +310,9 @@ def _cache_module(self, module, app_name, app_path): and AppCache._get_qualified_class_name(obj) != 'apps.App'): self._cache_app(obj, app_name, base_path) elif inspect.isfunction(obj): - for tag in WalkoffTag.get_tags(obj): - self._cache_action(obj, app_name, base_path, tag.name) + tags = WalkoffTag.get_tags(obj) + if tags: + self._cache_action(obj, app_name, base_path, tags) def _cache_app(self, app_class, app_name, app_path): """Caches an app @@ -328,44 +333,41 @@ def _cache_app(self, app_class, app_name, app_path): self._cache[app_name]['main'] = app_class app_actions = inspect.getmembers( app_class, (lambda field: - (inspect.ismethod(field) or inspect.isfunction(field)) and WalkoffTag.action.is_tagged(field))) + (inspect.ismethod(field) or inspect.isfunction(field)) and WalkoffTag.get_tags(field))) if 'actions' not in self._cache[app_name]: self._cache[app_name]['actions'] = {} if app_actions: new_actions = {} for _, action_method in app_actions: + tags = WalkoffTag.get_tags(action_method) qualified_name = AppCache._get_qualified_function_name(action_method, cls=app_class) qualified_name = AppCache._strip_base_module_from_qualified_name(qualified_name, app_path) - new_actions[qualified_name] = {'run': action_method, 'bound': True} + new_actions[qualified_name] = AppCacheActionEntry(run=action_method, is_bound=True, tags=tags) self._cache[app_name]['actions'].update(new_actions) - def _cache_action(self, action_method, app_name, app_path, tag, cls=None): + def _cache_action(self, action_method, app_name, app_path, tags, cls=None): """Caches an action Args: action_method (func): The action to cache app_name (str): The name of the app associated with the action """ - plural_tag = tag + 's' if app_name not in self._cache: self._cache[app_name] = {} - if plural_tag not in self._cache[app_name]: - self._cache[app_name][plural_tag] = {} + if 'actions' not in self._cache[app_name]: + self._cache[app_name]['actions'] = {} qualified_action_name = AppCache._get_qualified_function_name(action_method, cls=cls) qualified_action_name = AppCache._strip_base_module_from_qualified_name(qualified_action_name, app_path) - if qualified_action_name in self._cache[app_name][plural_tag]: + if qualified_action_name in self._cache[app_name]['actions']: _logger.warning( - 'App {0} already has {1}{2} {3} defined as {4}. Overwriting it with {5}'.format( + 'App {0} already has {1} defined as {2}. Overwriting it with {3}'.format( app_name, - ('unbound' if tag == 'action' else ''), - tag, qualified_action_name, AppCache._get_qualified_function_name( - self._cache[app_name][plural_tag][qualified_action_name]['run']), + self._cache[app_name]['actions'][qualified_action_name].run), qualified_action_name)) - self._cache[app_name][plural_tag][qualified_action_name] = {'run': action_method} - if tag == 'action': - self._cache[app_name][plural_tag][qualified_action_name]['bound'] = False + self._cache[app_name]['actions'][qualified_action_name] = AppCacheActionEntry( + run=action_method, is_bound=False, tags=tags) def _clear_existing_bound_functions(self, app_name): """Clears existing bound functions from an app @@ -376,7 +378,7 @@ def _clear_existing_bound_functions(self, app_name): if 'actions' in self._cache[app_name]: self._cache[app_name]['actions'] = { action_name: action for action_name, action in self._cache[app_name]['actions'].items() - if not action['bound']} + if not action.is_bound} @staticmethod def _get_qualified_class_name(obj): From 2ac4feff9e4b3c90ff55700adcddb1cc3deb00d8 Mon Sep 17 00:00:00 2001 From: Justin Tervala Date: Wed, 3 Jan 2018 15:25:34 -0500 Subject: [PATCH 06/15] WalkoffTag.get_tags returns a set --- tests/test_app_cache.py | 255 ++++++++++++++----------------- tests/test_app_cache_entry.py | 6 + tests/test_walkoff_tag.py | 6 +- walkoff/appgateway/appcache.py | 31 ++-- walkoff/appgateway/walkofftag.py | 2 +- 5 files changed, 143 insertions(+), 157 deletions(-) create mode 100644 tests/test_app_cache_entry.py diff --git a/tests/test_app_cache.py b/tests/test_app_cache.py index 8f663fc8d..2d51bda62 100644 --- a/tests/test_app_cache.py +++ b/tests/test_app_cache.py @@ -2,7 +2,7 @@ from importlib import import_module from unittest import TestCase -from walkoff.appgateway.appcache import AppCache, AppCacheActionEntry, WalkoffTag +from walkoff.appgateway.appcache import AppCache, FunctionEntry, WalkoffTag from walkoff.appgateway.decorators import action from walkoff.helpers import UnknownApp, UnknownAppAction @@ -14,9 +14,9 @@ class TestAppCache(TestCase): @classmethod def setUpClass(cls): - cls.action_tag = [WalkoffTag.action] - cls.condition_tag = [WalkoffTag.condition] - cls.transform_tag = [WalkoffTag.transform] + cls.action_tag = {WalkoffTag.action} + cls.condition_tag = {WalkoffTag.condition} + cls.transform_tag = {WalkoffTag.transform} cls.maxDiff = None def setUp(self): @@ -64,8 +64,7 @@ def test_cache_action_empty_cache(self): def x(): pass self.cache_action(x) self.assert_cache_has_apps({'app1'}) - self.assert_cached_app_has_actions( - actions={'x': AppCacheActionEntry(run=x, is_bound=False, tags=self.action_tag)}) + self.assert_cached_app_has_actions(actions={'x': FunctionEntry(run=x, is_bound=False, tags=self.action_tag)}) def test_cache_action_existing_app_name_entry(self): def x(): pass @@ -73,8 +72,7 @@ def x(): pass self.cache._cache['app1'] = {} self.cache_action(x) self.assert_cache_has_apps({'app1'}) - self.assert_cached_app_has_actions( - actions={'x': AppCacheActionEntry(run=x, is_bound=False, tags=self.action_tag)}) + self.assert_cached_app_has_actions(actions={'x': FunctionEntry(run=x, is_bound=False, tags=self.action_tag)}) def test_cache_action_existing_app_name_and_actions_tag(self): def x(): pass @@ -82,8 +80,7 @@ def x(): pass self.cache._cache['app1'] = {'actions': {}} self.cache_action(x) self.assert_cache_has_apps({'app1'}) - self.assert_cached_app_has_actions( - actions={'x': AppCacheActionEntry(run=x, is_bound=False, tags=self.action_tag)}) + self.assert_cached_app_has_actions(actions={'x': FunctionEntry(run=x, is_bound=False, tags=self.action_tag)}) def test_cache_action_multiple_actions_same_app(self): def x(): pass @@ -92,9 +89,8 @@ def y(): pass self.cache_action(x) self.cache_action(y) self.assert_cache_has_apps({'app1'}) - self.assert_cached_app_has_actions( - actions={'x': AppCacheActionEntry(run=x, is_bound=False, tags=self.action_tag), - 'y': AppCacheActionEntry(run=y, is_bound=False, tags=self.action_tag)}) + self.assert_cached_app_has_actions(actions={'x': FunctionEntry(run=x, is_bound=False, tags=self.action_tag), + 'y': FunctionEntry(run=y, is_bound=False, tags=self.action_tag)}) def test_cache_action_multiple_actions_different_app(self): def x(): pass @@ -104,9 +100,9 @@ def y(): pass self.cache_action(y, app='app2') self.assert_cache_has_apps({'app1', 'app2'}) self.assert_cached_app_has_actions( - actions={'x': AppCacheActionEntry(run=x, is_bound=False, tags=self.action_tag)}) + actions={'x': FunctionEntry(run=x, is_bound=False, tags=self.action_tag)}) self.assert_cached_app_has_actions(app='app2', - actions={'y': AppCacheActionEntry(run=y, is_bound=False, tags=self.action_tag)}) + actions={'y': FunctionEntry(run=y, is_bound=False, tags=self.action_tag)}) def test_cache_action_overwrite(self): def x(): pass @@ -117,9 +113,7 @@ def x(): pass def x(): pass self.cache_action(x) self.assert_cache_has_apps({'app1'}) - self.assert_cached_app_has_actions( - actions={'x': AppCacheActionEntry(run=x, is_bound=False, tags=self.action_tag)} - ) + self.assert_cached_app_has_actions(actions={'x': FunctionEntry(run=x, is_bound=False, tags=self.action_tag)}) self.assertNotEqual(id(self.cache._cache['app1']['actions']['x'].run), original_id) def test_cache_app_no_actions_empty_cache(self): @@ -162,8 +156,8 @@ def z(self): pass self.assert_cache_has_main(A) self.assert_cached_app_has_actions( app='A', - actions={'tests.test_app_cache.A.x': AppCacheActionEntry(run=A.x, is_bound=True, tags=self.action_tag), - 'tests.test_app_cache.A.y': AppCacheActionEntry(run=A.y, is_bound=True, tags=self.action_tag)}) + actions={'tests.test_app_cache.A.x': FunctionEntry(run=A.x, is_bound=True, tags=self.action_tag), + 'tests.test_app_cache.A.y': FunctionEntry(run=A.y, is_bound=True, tags=self.action_tag)}) def test_cache_app_with_actions_app_name_exists(self): class A: @@ -180,8 +174,8 @@ def z(self): pass self.assert_cache_has_main(A) self.assert_cached_app_has_actions( app='A', - actions={'tests.test_app_cache.A.x': AppCacheActionEntry(run=A.x, is_bound=True, tags=self.action_tag), - 'tests.test_app_cache.A.y': AppCacheActionEntry(run=A.y, is_bound=True, tags=self.action_tag)}) + actions={'tests.test_app_cache.A.x': FunctionEntry(run=A.x, is_bound=True, tags=self.action_tag), + 'tests.test_app_cache.A.y': FunctionEntry(run=A.y, is_bound=True, tags=self.action_tag)}) def test_cache_app_with_actions_app_name_exists_main_is_empty(self): class A: @@ -198,8 +192,8 @@ def z(self): pass self.assert_cache_has_main(A) self.assert_cached_app_has_actions( app='A', - actions={'tests.test_app_cache.A.x': AppCacheActionEntry(run=A.x, is_bound=True, tags=self.action_tag), - 'tests.test_app_cache.A.y': AppCacheActionEntry(run=A.y, is_bound=True, tags=self.action_tag)}) + actions={'tests.test_app_cache.A.x': FunctionEntry(run=A.x, is_bound=True, tags=self.action_tag), + 'tests.test_app_cache.A.y': FunctionEntry(run=A.y, is_bound=True, tags=self.action_tag)}) def test_cache_app_with_actions_and_global_actions(self): class A: @@ -218,9 +212,9 @@ def z(): pass self.assert_cache_has_main(A) self.assert_cached_app_has_actions( app='A', - actions={'tests.test_app_cache.A.x': AppCacheActionEntry(run=A.x, is_bound=True, tags=self.action_tag), - 'tests.test_app_cache.A.y': AppCacheActionEntry(run=A.y, is_bound=True, tags=self.action_tag), - 'z': AppCacheActionEntry(run=z, is_bound=False, tags=self.action_tag)}) + actions={'tests.test_app_cache.A.x': FunctionEntry(run=A.x, is_bound=True, tags=self.action_tag), + 'tests.test_app_cache.A.y': FunctionEntry(run=A.y, is_bound=True, tags=self.action_tag), + 'z': FunctionEntry(run=z, is_bound=False, tags=self.action_tag)}) def test_cache_app_with_actions_and_global_actions_name_conflict_resolved(self): class A: @@ -240,10 +234,10 @@ def z(): pass self.assert_cache_has_main(A) self.assert_cached_app_has_actions( app='A', - actions={'tests.test_app_cache.A.x': AppCacheActionEntry(run=A.x, is_bound=True, tags=self.action_tag), - 'tests.test_app_cache.A.y': AppCacheActionEntry(run=A.y, is_bound=True, tags=self.action_tag), - 'tests.test_app_cache.A.z': AppCacheActionEntry(run=A.z, is_bound=True, tags=self.action_tag), - 'z': AppCacheActionEntry(run=z, is_bound=False, tags=self.action_tag)}) + actions={'tests.test_app_cache.A.x': FunctionEntry(run=A.x, is_bound=True, tags=self.action_tag), + 'tests.test_app_cache.A.y': FunctionEntry(run=A.y, is_bound=True, tags=self.action_tag), + 'tests.test_app_cache.A.z': FunctionEntry(run=A.z, is_bound=True, tags=self.action_tag), + 'z': FunctionEntry(run=z, is_bound=False, tags=self.action_tag)}) def test_clear_existing_bound_functions_no_actions(self): class A: pass @@ -262,8 +256,8 @@ def y(): pass self.cache_action(y) self.cache._clear_existing_bound_functions('app1') self.assertDictEqual(self.cache._cache, - {'app1': {'actions': {'x': AppCacheActionEntry(run=x, is_bound=False, tags=self.action_tag), - 'y': AppCacheActionEntry(run=y, is_bound=False, tags=self.action_tag)}}}) + {'app1': {'actions': {'x': FunctionEntry(run=x, is_bound=False, tags=self.action_tag), + 'y': FunctionEntry(run=y, is_bound=False, tags=self.action_tag)}}}) def test_clear_existing_bound_functions(self): class A: @@ -282,7 +276,7 @@ def z(): pass self.cache._clear_existing_bound_functions('A') self.assertDictEqual( self.cache._cache, - {'A': {'main': A, 'actions': {'z': AppCacheActionEntry(run=z, is_bound=False, tags=self.action_tag)}}}) + {'A': {'main': A, 'actions': {'z': FunctionEntry(run=z, is_bound=False, tags=self.action_tag)}}}) def test_cache_module(self): module = import_module('tests.testapps.HelloWorldBounded.main') @@ -291,21 +285,15 @@ def test_cache_module(self): self.assert_cache_has_main(Main, app='HelloWorldBounded') self.assert_cached_app_has_actions( app='HelloWorldBounded', - actions={'main.Main.helloWorld': - AppCacheActionEntry(run=Main.helloWorld, is_bound=True, tags=self.action_tag), - 'main.Main.repeatBackToMe': - AppCacheActionEntry(run=Main.repeatBackToMe, is_bound=True, tags=self.action_tag), - 'main.Main.returnPlusOne': - AppCacheActionEntry(run=Main.returnPlusOne, is_bound=True, tags=self.action_tag), - 'main.Main.pause': - AppCacheActionEntry(run=Main.pause, is_bound=True, tags=self.action_tag), - 'main.Main.addThree': - AppCacheActionEntry(run=Main.addThree, is_bound=True, tags=self.action_tag), - 'main.Main.buggy_action': - AppCacheActionEntry(run=Main.buggy_action, is_bound=True, tags=self.action_tag), - 'main.Main.json_sample': - AppCacheActionEntry(run=Main.json_sample, is_bound=True, tags=self.action_tag), - 'main.global1': AppCacheActionEntry(run=global1, is_bound=False, tags=self.action_tag)}) + actions={ + 'main.Main.helloWorld': FunctionEntry(run=Main.helloWorld, is_bound=True, tags=self.action_tag), + 'main.Main.repeatBackToMe': FunctionEntry(run=Main.repeatBackToMe, is_bound=True, tags=self.action_tag), + 'main.Main.returnPlusOne': FunctionEntry(run=Main.returnPlusOne, is_bound=True, tags=self.action_tag), + 'main.Main.pause': FunctionEntry(run=Main.pause, is_bound=True, tags=self.action_tag), + 'main.Main.addThree': FunctionEntry(run=Main.addThree, is_bound=True, tags=self.action_tag), + 'main.Main.buggy_action': FunctionEntry(run=Main.buggy_action, is_bound=True, tags=self.action_tag), + 'main.Main.json_sample': FunctionEntry(run=Main.json_sample, is_bound=True, tags=self.action_tag), + 'main.global1': FunctionEntry(run=global1, is_bound=False, tags=self.action_tag)}) def test_cache_module_nothing_found(self): module = import_module('tests.testapps.HelloWorldBounded.display') @@ -318,7 +306,7 @@ def test_cache_module_no_class(self): from tests.testapps.HelloWorldBounded.actions import global2 self.assert_cached_app_has_actions( app='HelloWorldBounded', - actions={'actions.global2': AppCacheActionEntry(run=global2, is_bound=False, tags=self.action_tag)}) + actions={'actions.global2': FunctionEntry(run=global2, is_bound=False, tags=self.action_tag)}) def test_import_and_cache_submodules_from_string(self): self.cache._import_and_cache_submodules('tests.testapps.HelloWorldBounded', 'HelloWorldBounded', @@ -332,35 +320,30 @@ def test_import_and_cache_submodules_from_string(self): length, json_select, sub1_top_filter) self.assert_cache_has_main(Main, app='HelloWorldBounded') expected = { - 'main.Main.helloWorld': AppCacheActionEntry(run=Main.helloWorld, is_bound=True, tags=self.action_tag), - 'main.Main.repeatBackToMe': - AppCacheActionEntry(run=Main.repeatBackToMe, is_bound=True, tags=self.action_tag), - 'main.Main.returnPlusOne': AppCacheActionEntry(run=Main.returnPlusOne, is_bound=True, tags=self.action_tag), - 'main.Main.pause': AppCacheActionEntry(run=Main.pause, is_bound=True, tags=self.action_tag), - 'main.Main.addThree': AppCacheActionEntry(run=Main.addThree, is_bound=True, tags=self.action_tag), - 'main.Main.buggy_action': AppCacheActionEntry(run=Main.buggy_action, is_bound=True, tags=self.action_tag), - 'main.Main.json_sample': AppCacheActionEntry(run=Main.json_sample, is_bound=True, tags=self.action_tag), - 'main.global1': AppCacheActionEntry(run=global1, is_bound=False, tags=self.action_tag), - 'actions.global2': AppCacheActionEntry(run=global2, is_bound=False, tags=self.action_tag), - 'conditions.top_level_flag': - AppCacheActionEntry(run=top_level_flag, is_bound=False, tags=self.condition_tag), - 'conditions.flag1': AppCacheActionEntry(run=flag1, is_bound=False, tags=self.condition_tag), - 'conditions.flag2': AppCacheActionEntry(run=flag2, is_bound=False, tags=self.condition_tag), - 'conditions.flag3': AppCacheActionEntry(run=flag3, is_bound=False, tags=self.condition_tag), - 'conditions.sub1_top_flag': AppCacheActionEntry(run=sub1_top_flag, is_bound=False, tags=self.condition_tag), - 'conditions.regMatch': AppCacheActionEntry(run=regMatch, is_bound=False, tags=self.condition_tag), - 'conditions.count': AppCacheActionEntry(run=count, is_bound=False, tags=self.condition_tag), - 'transforms.top_level_filter': - AppCacheActionEntry(run=top_level_filter, is_bound=False, tags=self.transform_tag), - 'transforms.filter2': AppCacheActionEntry(run=filter2, is_bound=False, tags=self.transform_tag), - 'transforms.sub1_top_filter': - AppCacheActionEntry(run=sub1_top_filter, is_bound=False, tags=self.transform_tag), - 'transforms.filter3': AppCacheActionEntry(run=filter3, is_bound=False, tags=self.transform_tag), - 'transforms.filter1':AppCacheActionEntry(run=filter1, is_bound=False, tags=self.transform_tag), - 'transforms.complex_filter': - AppCacheActionEntry(run=complex_filter, is_bound=False, tags=self.transform_tag), - 'transforms.length': AppCacheActionEntry(run=length, is_bound=False, tags=self.transform_tag), - 'transforms.json_select': AppCacheActionEntry(run=json_select, is_bound=False, tags=self.transform_tag)} + 'main.Main.helloWorld': FunctionEntry(run=Main.helloWorld, is_bound=True, tags=self.action_tag), + 'main.Main.repeatBackToMe': FunctionEntry(run=Main.repeatBackToMe, is_bound=True, tags=self.action_tag), + 'main.Main.returnPlusOne': FunctionEntry(run=Main.returnPlusOne, is_bound=True, tags=self.action_tag), + 'main.Main.pause': FunctionEntry(run=Main.pause, is_bound=True, tags=self.action_tag), + 'main.Main.addThree': FunctionEntry(run=Main.addThree, is_bound=True, tags=self.action_tag), + 'main.Main.buggy_action': FunctionEntry(run=Main.buggy_action, is_bound=True, tags=self.action_tag), + 'main.Main.json_sample': FunctionEntry(run=Main.json_sample, is_bound=True, tags=self.action_tag), + 'main.global1': FunctionEntry(run=global1, is_bound=False, tags=self.action_tag), + 'actions.global2': FunctionEntry(run=global2, is_bound=False, tags=self.action_tag), + 'conditions.top_level_flag': FunctionEntry(run=top_level_flag, is_bound=False, tags=self.condition_tag), + 'conditions.flag1': FunctionEntry(run=flag1, is_bound=False, tags=self.condition_tag), + 'conditions.flag2': FunctionEntry(run=flag2, is_bound=False, tags=self.condition_tag), + 'conditions.flag3': FunctionEntry(run=flag3, is_bound=False, tags=self.condition_tag), + 'conditions.sub1_top_flag': FunctionEntry(run=sub1_top_flag, is_bound=False, tags=self.condition_tag), + 'conditions.regMatch': FunctionEntry(run=regMatch, is_bound=False, tags=self.condition_tag), + 'conditions.count': FunctionEntry(run=count, is_bound=False, tags=self.condition_tag), + 'transforms.top_level_filter': FunctionEntry(run=top_level_filter, is_bound=False, tags=self.transform_tag), + 'transforms.filter2': FunctionEntry(run=filter2, is_bound=False, tags=self.transform_tag), + 'transforms.sub1_top_filter': FunctionEntry(run=sub1_top_filter, is_bound=False, tags=self.transform_tag), + 'transforms.filter3': FunctionEntry(run=filter3, is_bound=False, tags=self.transform_tag), + 'transforms.filter1':FunctionEntry(run=filter1, is_bound=False, tags=self.transform_tag), + 'transforms.complex_filter': FunctionEntry(run=complex_filter, is_bound=False, tags=self.transform_tag), + 'transforms.length': FunctionEntry(run=length, is_bound=False, tags=self.transform_tag), + 'transforms.json_select': FunctionEntry(run=json_select, is_bound=False, tags=self.transform_tag)} self.assert_cached_app_has_actions(app='HelloWorldBounded', actions=expected) def test_import_and_cache_submodules_from_module(self): @@ -375,35 +358,30 @@ def test_import_and_cache_submodules_from_module(self): length, json_select, sub1_top_filter) self.assert_cache_has_main(Main, app='HelloWorldBounded') expected = { - 'main.Main.helloWorld': AppCacheActionEntry(run=Main.helloWorld, is_bound=True, tags=self.action_tag), - 'main.Main.repeatBackToMe': - AppCacheActionEntry(run=Main.repeatBackToMe, is_bound=True, tags=self.action_tag), - 'main.Main.returnPlusOne': AppCacheActionEntry(run=Main.returnPlusOne, is_bound=True, tags=self.action_tag), - 'main.Main.pause': AppCacheActionEntry(run=Main.pause, is_bound=True, tags=self.action_tag), - 'main.Main.addThree': AppCacheActionEntry(run=Main.addThree, is_bound=True, tags=self.action_tag), - 'main.Main.buggy_action': AppCacheActionEntry(run=Main.buggy_action, is_bound=True, tags=self.action_tag), - 'main.Main.json_sample': AppCacheActionEntry(run=Main.json_sample, is_bound=True, tags=self.action_tag), - 'main.global1': AppCacheActionEntry(run=global1, is_bound=False, tags=self.action_tag), - 'actions.global2': AppCacheActionEntry(run=global2, is_bound=False, tags=self.action_tag), - 'conditions.top_level_flag': - AppCacheActionEntry(run=top_level_flag, is_bound=False, tags=self.condition_tag), - 'conditions.flag1': AppCacheActionEntry(run=flag1, is_bound=False, tags=self.condition_tag), - 'conditions.flag2': AppCacheActionEntry(run=flag2, is_bound=False, tags=self.condition_tag), - 'conditions.flag3': AppCacheActionEntry(run=flag3, is_bound=False, tags=self.condition_tag), - 'conditions.sub1_top_flag': AppCacheActionEntry(run=sub1_top_flag, is_bound=False, tags=self.condition_tag), - 'conditions.regMatch': AppCacheActionEntry(run=regMatch, is_bound=False, tags=self.condition_tag), - 'conditions.count': AppCacheActionEntry(run=count, is_bound=False, tags=self.condition_tag), - 'transforms.top_level_filter': - AppCacheActionEntry(run=top_level_filter, is_bound=False, tags=self.transform_tag), - 'transforms.filter2': AppCacheActionEntry(run=filter2, is_bound=False, tags=self.transform_tag), - 'transforms.sub1_top_filter': - AppCacheActionEntry(run=sub1_top_filter, is_bound=False, tags=self.transform_tag), - 'transforms.filter3': AppCacheActionEntry(run=filter3, is_bound=False, tags=self.transform_tag), - 'transforms.filter1': AppCacheActionEntry(run=filter1, is_bound=False, tags=self.transform_tag), - 'transforms.complex_filter': - AppCacheActionEntry(run=complex_filter, is_bound=False, tags=self.transform_tag), - 'transforms.length': AppCacheActionEntry(run=length, is_bound=False, tags=self.transform_tag), - 'transforms.json_select': AppCacheActionEntry(run=json_select, is_bound=False, tags=self.transform_tag)} + 'main.Main.helloWorld': FunctionEntry(run=Main.helloWorld, is_bound=True, tags=self.action_tag), + 'main.Main.repeatBackToMe': FunctionEntry(run=Main.repeatBackToMe, is_bound=True, tags=self.action_tag), + 'main.Main.returnPlusOne': FunctionEntry(run=Main.returnPlusOne, is_bound=True, tags=self.action_tag), + 'main.Main.pause': FunctionEntry(run=Main.pause, is_bound=True, tags=self.action_tag), + 'main.Main.addThree': FunctionEntry(run=Main.addThree, is_bound=True, tags=self.action_tag), + 'main.Main.buggy_action': FunctionEntry(run=Main.buggy_action, is_bound=True, tags=self.action_tag), + 'main.Main.json_sample': FunctionEntry(run=Main.json_sample, is_bound=True, tags=self.action_tag), + 'main.global1': FunctionEntry(run=global1, is_bound=False, tags=self.action_tag), + 'actions.global2': FunctionEntry(run=global2, is_bound=False, tags=self.action_tag), + 'conditions.top_level_flag': FunctionEntry(run=top_level_flag, is_bound=False, tags=self.condition_tag), + 'conditions.flag1': FunctionEntry(run=flag1, is_bound=False, tags=self.condition_tag), + 'conditions.flag2': FunctionEntry(run=flag2, is_bound=False, tags=self.condition_tag), + 'conditions.flag3': FunctionEntry(run=flag3, is_bound=False, tags=self.condition_tag), + 'conditions.sub1_top_flag': FunctionEntry(run=sub1_top_flag, is_bound=False, tags=self.condition_tag), + 'conditions.regMatch': FunctionEntry(run=regMatch, is_bound=False, tags=self.condition_tag), + 'conditions.count': FunctionEntry(run=count, is_bound=False, tags=self.condition_tag), + 'transforms.top_level_filter': FunctionEntry(run=top_level_filter, is_bound=False, tags=self.transform_tag), + 'transforms.filter2': FunctionEntry(run=filter2, is_bound=False, tags=self.transform_tag), + 'transforms.sub1_top_filter': FunctionEntry(run=sub1_top_filter, is_bound=False, tags=self.transform_tag), + 'transforms.filter3': FunctionEntry(run=filter3, is_bound=False, tags=self.transform_tag), + 'transforms.filter1': FunctionEntry(run=filter1, is_bound=False, tags=self.transform_tag), + 'transforms.complex_filter': FunctionEntry(run=complex_filter, is_bound=False, tags=self.transform_tag), + 'transforms.length': FunctionEntry(run=length, is_bound=False, tags=self.transform_tag), + 'transforms.json_select': FunctionEntry(run=json_select, is_bound=False, tags=self.transform_tag)} self.assert_cached_app_has_actions(app='HelloWorldBounded', actions=expected) def test_path_to_module_no_slashes(self): @@ -430,44 +408,39 @@ def test_cache_apps(self): from tests.testapps.DailyQuote.main import Main as DailyMain self.assert_cache_has_main(Main, app='HelloWorldBounded') hello_world_expected = { - 'main.Main.helloWorld': AppCacheActionEntry(run=Main.helloWorld, is_bound=True, tags=self.action_tag), - 'main.Main.repeatBackToMe': - AppCacheActionEntry(run=Main.repeatBackToMe, is_bound=True, tags=self.action_tag), - 'main.Main.returnPlusOne': AppCacheActionEntry(run=Main.returnPlusOne, is_bound=True, tags=self.action_tag), - 'main.Main.pause': AppCacheActionEntry(run=Main.pause, is_bound=True, tags=self.action_tag), - 'main.Main.addThree': AppCacheActionEntry(run=Main.addThree, is_bound=True, tags=self.action_tag), - 'main.Main.buggy_action': AppCacheActionEntry(run=Main.buggy_action, is_bound=True, tags=self.action_tag), - 'main.Main.json_sample': AppCacheActionEntry(run=Main.json_sample, is_bound=True, tags=self.action_tag), - 'main.global1': AppCacheActionEntry(run=global1, is_bound=False, tags=self.action_tag), - 'actions.global2': AppCacheActionEntry(run=global2, is_bound=False, tags=self.action_tag), - 'conditions.top_level_flag': - AppCacheActionEntry(run=top_level_flag, is_bound=False, tags=self.condition_tag), - 'conditions.flag1': AppCacheActionEntry(run=flag1, is_bound=False, tags=self.condition_tag), - 'conditions.flag2': AppCacheActionEntry(run=flag2, is_bound=False, tags=self.condition_tag), - 'conditions.flag3': AppCacheActionEntry(run=flag3, is_bound=False, tags=self.condition_tag), - 'conditions.sub1_top_flag': AppCacheActionEntry(run=sub1_top_flag, is_bound=False, tags=self.condition_tag), - 'conditions.regMatch': AppCacheActionEntry(run=regMatch, is_bound=False, tags=self.condition_tag), - 'conditions.count': AppCacheActionEntry(run=count, is_bound=False, tags=self.condition_tag), - 'transforms.top_level_filter': - AppCacheActionEntry(run=top_level_filter, is_bound=False, tags=self.transform_tag), - 'transforms.filter2': AppCacheActionEntry(run=filter2, is_bound=False, tags=self.transform_tag), - 'transforms.sub1_top_filter': - AppCacheActionEntry(run=sub1_top_filter, is_bound=False, tags=self.transform_tag), - 'transforms.filter3': AppCacheActionEntry(run=filter3, is_bound=False, tags=self.transform_tag), - 'transforms.filter1': AppCacheActionEntry(run=filter1, is_bound=False, tags=self.transform_tag), - 'transforms.complex_filter': - AppCacheActionEntry(run=complex_filter, is_bound=False, tags=self.transform_tag), - 'transforms.length': AppCacheActionEntry(run=length, is_bound=False, tags=self.transform_tag), - 'transforms.json_select': AppCacheActionEntry(run=json_select, is_bound=False, tags=self.transform_tag)} + 'main.Main.helloWorld': FunctionEntry(run=Main.helloWorld, is_bound=True, tags=self.action_tag), + 'main.Main.repeatBackToMe': FunctionEntry(run=Main.repeatBackToMe, is_bound=True, tags=self.action_tag), + 'main.Main.returnPlusOne': FunctionEntry(run=Main.returnPlusOne, is_bound=True, tags=self.action_tag), + 'main.Main.pause': FunctionEntry(run=Main.pause, is_bound=True, tags=self.action_tag), + 'main.Main.addThree': FunctionEntry(run=Main.addThree, is_bound=True, tags=self.action_tag), + 'main.Main.buggy_action': FunctionEntry(run=Main.buggy_action, is_bound=True, tags=self.action_tag), + 'main.Main.json_sample': FunctionEntry(run=Main.json_sample, is_bound=True, tags=self.action_tag), + 'main.global1': FunctionEntry(run=global1, is_bound=False, tags=self.action_tag), + 'actions.global2': FunctionEntry(run=global2, is_bound=False, tags=self.action_tag), + 'conditions.top_level_flag': FunctionEntry(run=top_level_flag, is_bound=False, tags=self.condition_tag), + 'conditions.flag1': FunctionEntry(run=flag1, is_bound=False, tags=self.condition_tag), + 'conditions.flag2': FunctionEntry(run=flag2, is_bound=False, tags=self.condition_tag), + 'conditions.flag3': FunctionEntry(run=flag3, is_bound=False, tags=self.condition_tag), + 'conditions.sub1_top_flag': FunctionEntry(run=sub1_top_flag, is_bound=False, tags=self.condition_tag), + 'conditions.regMatch': FunctionEntry(run=regMatch, is_bound=False, tags=self.condition_tag), + 'conditions.count': FunctionEntry(run=count, is_bound=False, tags=self.condition_tag), + 'transforms.top_level_filter': FunctionEntry(run=top_level_filter, is_bound=False, tags=self.transform_tag), + 'transforms.filter2': FunctionEntry(run=filter2, is_bound=False, tags=self.transform_tag), + 'transforms.sub1_top_filter': FunctionEntry(run=sub1_top_filter, is_bound=False, tags=self.transform_tag), + 'transforms.filter3': FunctionEntry(run=filter3, is_bound=False, tags=self.transform_tag), + 'transforms.filter1': FunctionEntry(run=filter1, is_bound=False, tags=self.transform_tag), + 'transforms.complex_filter': FunctionEntry(run=complex_filter, is_bound=False, tags=self.transform_tag), + 'transforms.length': FunctionEntry(run=length, is_bound=False, tags=self.transform_tag), + 'transforms.json_select': FunctionEntry(run=json_select, is_bound=False, tags=self.transform_tag)} self.assert_cached_app_has_actions(app='HelloWorldBounded', actions=hello_world_expected) self.assert_cache_has_main(DailyMain, app='DailyQuote') daily_quote_expected = { - 'main.Main.quoteIntro': AppCacheActionEntry(run=DailyMain.quoteIntro, is_bound=True, tags=self.action_tag), + 'main.Main.quoteIntro': FunctionEntry(run=DailyMain.quoteIntro, is_bound=True, tags=self.action_tag), 'main.Main.repeatBackToMe': - AppCacheActionEntry(run=DailyMain.repeatBackToMe, is_bound=True, tags=self.action_tag), + FunctionEntry(run=DailyMain.repeatBackToMe, is_bound=True, tags=self.action_tag), 'main.Main.forismaticQuote': - AppCacheActionEntry(run=DailyMain.forismaticQuote, is_bound=True, tags=self.action_tag), - 'main.Main.getQuote': AppCacheActionEntry(run=DailyMain.getQuote, is_bound=True, tags=self.action_tag)} + FunctionEntry(run=DailyMain.forismaticQuote, is_bound=True, tags=self.action_tag), + 'main.Main.getQuote': FunctionEntry(run=DailyMain.getQuote, is_bound=True, tags=self.action_tag)} self.assert_cache_has_apps({'HelloWorldBounded', 'HelloWorld', 'DailyQuote'}) self.assert_cached_app_has_actions(app='DailyQuote', actions=daily_quote_expected) diff --git a/tests/test_app_cache_entry.py b/tests/test_app_cache_entry.py new file mode 100644 index 000000000..3966be9a9 --- /dev/null +++ b/tests/test_app_cache_entry.py @@ -0,0 +1,6 @@ +from unittest import TestCase +from walkoff.appgateway.appcache import AppCacheEntry + + +class TestAppCacheEntry(TestCase): + diff --git a/tests/test_walkoff_tag.py b/tests/test_walkoff_tag.py index 868fc13e3..72f6d44fa 100644 --- a/tests/test_walkoff_tag.py +++ b/tests/test_walkoff_tag.py @@ -16,12 +16,10 @@ def foo(): pass def test_get_tags_no_tags(self): def foo(): pass - self.assertListEqual(WalkoffTag.get_tags(foo), []) + self.assertSetEqual(WalkoffTag.get_tags(foo), set()) def test_get_tags(self): @action @condition def foo(): pass - self.assertSetEqual(set(WalkoffTag.get_tags(foo)), {WalkoffTag.action, WalkoffTag.condition}) - - \ No newline at end of file + self.assertSetEqual(WalkoffTag.get_tags(foo), {WalkoffTag.action, WalkoffTag.condition}) diff --git a/walkoff/appgateway/appcache.py b/walkoff/appgateway/appcache.py index 51483fcf9..d7f63e279 100644 --- a/walkoff/appgateway/appcache.py +++ b/walkoff/appgateway/appcache.py @@ -13,8 +13,16 @@ _logger = logging.getLogger(__name__) -AppCacheActionEntry = namedtuple('AppCacheEntry', ['run', 'is_bound', 'tags']) -# AppCacheAppEntry = namedtuple('AppCacheAppEntry', ['main', 'actions']) +FunctionEntry = namedtuple('AppCacheEntry', ['run', 'is_bound', 'tags']) + + +class AppCacheEntry(object): + __slots__ = ['main', 'functions'] + + def __init__(self, main=None, functions=None): + self.main = main + self.functions = functions if functions is not None else {} + class AppCache(object): """Object which caches app actions, conditions, and transforms @@ -322,27 +330,28 @@ def _cache_app(self, app_class, app_name, app_path): app_name (str): The name of the app associated with the class """ if app_name not in self._cache: - self._cache[app_name] = {} - if 'main' in self._cache[app_name] and self._cache[app_name]['main']: + self._cache[app_name] = AppCacheEntry() + if self._cache[app_name].main is not None: _logger.warning( 'App {0} already has class defined as {1}. Overwriting it with {2}'.format( app_name, - AppCache._get_qualified_class_name(self._cache[app_name]['main']), + AppCache._get_qualified_class_name(self._cache[app_name].main), AppCache._get_qualified_class_name(app_class))) self._clear_existing_bound_functions(app_name) - self._cache[app_name]['main'] = app_class - app_actions = inspect.getmembers( + self._cache[app_name].main = app_class + app_methods = inspect.getmembers( app_class, (lambda field: (inspect.ismethod(field) or inspect.isfunction(field)) and WalkoffTag.get_tags(field))) + self._cache[app_name].cache_methods(app_methods) if 'actions' not in self._cache[app_name]: self._cache[app_name]['actions'] = {} - if app_actions: + if app_methods: new_actions = {} - for _, action_method in app_actions: + for _, action_method in app_methods: tags = WalkoffTag.get_tags(action_method) qualified_name = AppCache._get_qualified_function_name(action_method, cls=app_class) qualified_name = AppCache._strip_base_module_from_qualified_name(qualified_name, app_path) - new_actions[qualified_name] = AppCacheActionEntry(run=action_method, is_bound=True, tags=tags) + new_actions[qualified_name] = FunctionEntry(run=action_method, is_bound=True, tags=tags) self._cache[app_name]['actions'].update(new_actions) def _cache_action(self, action_method, app_name, app_path, tags, cls=None): @@ -366,7 +375,7 @@ def _cache_action(self, action_method, app_name, app_path, tags, cls=None): AppCache._get_qualified_function_name( self._cache[app_name]['actions'][qualified_action_name].run), qualified_action_name)) - self._cache[app_name]['actions'][qualified_action_name] = AppCacheActionEntry( + self._cache[app_name]['actions'][qualified_action_name] = FunctionEntry( run=action_method, is_bound=False, tags=tags) def _clear_existing_bound_functions(self, app_name): diff --git a/walkoff/appgateway/walkofftag.py b/walkoff/appgateway/walkofftag.py index c4ab9f0df..ae7de11c4 100644 --- a/walkoff/appgateway/walkofftag.py +++ b/walkoff/appgateway/walkofftag.py @@ -15,4 +15,4 @@ def is_tagged(self, func): @classmethod def get_tags(cls, func): - return [tag for tag in cls if getattr(func, tag.value, False)] + return {tag for tag in cls if getattr(func, tag.value, False)} From 9a986c5acef8aba262e2dd624395540235629fcf Mon Sep 17 00:00:00 2001 From: Justin Tervala Date: Wed, 3 Jan 2018 17:20:40 -0500 Subject: [PATCH 07/15] AppCache entries encapsulated --- tests/test_app_cache.py | 412 ++++----------------------------- tests/test_app_cache_entry.py | 126 +++++++++- walkoff/appgateway/appcache.py | 224 +++++++++--------- 3 files changed, 286 insertions(+), 476 deletions(-) diff --git a/tests/test_app_cache.py b/tests/test_app_cache.py index 2d51bda62..c09b46692 100644 --- a/tests/test_app_cache.py +++ b/tests/test_app_cache.py @@ -2,7 +2,9 @@ from importlib import import_module from unittest import TestCase -from walkoff.appgateway.appcache import AppCache, FunctionEntry, WalkoffTag +from walkoff.appgateway.appcache import (AppCache, WalkoffTag, + _get_qualified_class_name, _get_qualified_function_name, + _strip_base_module_from_qualified_name) from walkoff.appgateway.decorators import action from walkoff.helpers import UnknownApp, UnknownAppAction @@ -25,275 +27,47 @@ def setUp(self): def assert_cache_has_apps(self, apps): self.assertSetEqual(set(self.cache._cache.keys()), set(apps)) - def assert_cached_app_has_actions(self, app='app1', actions={}): - self.assertDictEqual(self.cache._cache[app]['actions'], actions) + def assert_cached_app_has_actions(self, app='app1', actions=set()): + self.assertSetEqual(set(self.cache._cache[app].functions.keys()), actions) def assert_cache_has_main(self, main, app='A'): - self.assertEqual(self.cache._cache[app]['main'], main) + self.assertEqual(self.cache._cache[app].main, main) def test_init(self): self.assertDictEqual(self.cache._cache, {}) def test_get_qualified_function_name(self): - self.assertEqual(AppCache._get_qualified_function_name(f1), 'tests.test_app_cache.f1') + self.assertEqual(_get_qualified_function_name(f1), 'tests.test_app_cache.f1') def test_get_qualified_function_name_with_class(self): def f1(): pass - self.assertEqual(AppCache._get_qualified_function_name(f1, cls=TestAppCache), + self.assertEqual(_get_qualified_function_name(f1, cls=TestAppCache), 'tests.test_app_cache.TestAppCache.f1') def test_get_qualified_class_name(self): - self.assertEqual(AppCache._get_qualified_class_name(TestAppCache), + self.assertEqual(_get_qualified_class_name(TestAppCache), 'tests.test_app_cache.TestAppCache') def test_strip_base_module_from_qualified_name(self): - self.assertEqual(AppCache._strip_base_module_from_qualified_name('tests.test_app_cache.f1', 'tests'), + self.assertEqual(_strip_base_module_from_qualified_name('tests.test_app_cache.f1', 'tests'), 'test_app_cache.f1') def test_strip_base_module_from_qualified_name_invalid_base_module(self): - self.assertEqual(AppCache._strip_base_module_from_qualified_name('tests.test_app_cache.f1', 'invalid'), + self.assertEqual(_strip_base_module_from_qualified_name('tests.test_app_cache.f1', 'invalid'), 'tests.test_app_cache.f1') - def cache_action(self, func, app='app1', tags=None): - tags = self.action_tag if tags is None else tags - self.cache._cache_action( - func, app, 'tests.test_app_cache.TestAppCache', tags, cls=TestAppCache) - - def test_cache_action_empty_cache(self): - def x(): pass - self.cache_action(x) - self.assert_cache_has_apps({'app1'}) - self.assert_cached_app_has_actions(actions={'x': FunctionEntry(run=x, is_bound=False, tags=self.action_tag)}) - - def test_cache_action_existing_app_name_entry(self): - def x(): pass - - self.cache._cache['app1'] = {} - self.cache_action(x) - self.assert_cache_has_apps({'app1'}) - self.assert_cached_app_has_actions(actions={'x': FunctionEntry(run=x, is_bound=False, tags=self.action_tag)}) - - def test_cache_action_existing_app_name_and_actions_tag(self): - def x(): pass - - self.cache._cache['app1'] = {'actions': {}} - self.cache_action(x) - self.assert_cache_has_apps({'app1'}) - self.assert_cached_app_has_actions(actions={'x': FunctionEntry(run=x, is_bound=False, tags=self.action_tag)}) - - def test_cache_action_multiple_actions_same_app(self): - def x(): pass - - def y(): pass - self.cache_action(x) - self.cache_action(y) - self.assert_cache_has_apps({'app1'}) - self.assert_cached_app_has_actions(actions={'x': FunctionEntry(run=x, is_bound=False, tags=self.action_tag), - 'y': FunctionEntry(run=y, is_bound=False, tags=self.action_tag)}) - - def test_cache_action_multiple_actions_different_app(self): - def x(): pass - - def y(): pass - self.cache_action(x) - self.cache_action(y, app='app2') - self.assert_cache_has_apps({'app1', 'app2'}) - self.assert_cached_app_has_actions( - actions={'x': FunctionEntry(run=x, is_bound=False, tags=self.action_tag)}) - self.assert_cached_app_has_actions(app='app2', - actions={'y': FunctionEntry(run=y, is_bound=False, tags=self.action_tag)}) - - def test_cache_action_overwrite(self): - def x(): pass - - original_id = id(x) - - self.cache_action(x) - def x(): pass - self.cache_action(x) - self.assert_cache_has_apps({'app1'}) - self.assert_cached_app_has_actions(actions={'x': FunctionEntry(run=x, is_bound=False, tags=self.action_tag)}) - self.assertNotEqual(id(self.cache._cache['app1']['actions']['x'].run), original_id) - - def test_cache_app_no_actions_empty_cache(self): - class A: pass - - self.cache_app(A) - self.assert_cache_has_main(A) - self.assert_cached_app_has_actions(app='A') - - def test_cache_app_no_actions_app_name_exists(self): - class A: pass - - self.cache._cache['A'] = {} - self.cache_app(A) - self.assert_cache_has_main(A) - self.assert_cached_app_has_actions(app='A') - def cache_app(self, A): - self.cache._cache_app(A, 'A', 'tests.test_app_cache.TestAppCache') - - def test_cache_app_no_actions_app_name_exists_main_is_empty(self): - class A: pass - - self.cache._cache['A'] = {'main': None} - self.cache_app(A) - self.assert_cache_has_main(A) - self.assert_cached_app_has_actions(app='A') - - def test_cache_app_with_actions_empty_cache(self): - class A: - @action - def x(self): pass - - @action - def y(self): pass - - def z(self): pass - - self.cache_app(A) - self.assert_cache_has_main(A) - self.assert_cached_app_has_actions( - app='A', - actions={'tests.test_app_cache.A.x': FunctionEntry(run=A.x, is_bound=True, tags=self.action_tag), - 'tests.test_app_cache.A.y': FunctionEntry(run=A.y, is_bound=True, tags=self.action_tag)}) - - def test_cache_app_with_actions_app_name_exists(self): - class A: - @action - def x(self): pass - - @action - def y(self): pass - - def z(self): pass - - self.cache._cache['A'] = {} - self.cache_app(A) - self.assert_cache_has_main(A) - self.assert_cached_app_has_actions( - app='A', - actions={'tests.test_app_cache.A.x': FunctionEntry(run=A.x, is_bound=True, tags=self.action_tag), - 'tests.test_app_cache.A.y': FunctionEntry(run=A.y, is_bound=True, tags=self.action_tag)}) - - def test_cache_app_with_actions_app_name_exists_main_is_empty(self): - class A: - @action - def x(self): pass - - @action - def y(self): pass - - def z(self): pass - - self.cache._cache['A'] = {'main': None} - self.cache_app(A) - self.assert_cache_has_main(A) - self.assert_cached_app_has_actions( - app='A', - actions={'tests.test_app_cache.A.x': FunctionEntry(run=A.x, is_bound=True, tags=self.action_tag), - 'tests.test_app_cache.A.y': FunctionEntry(run=A.y, is_bound=True, tags=self.action_tag)}) - - def test_cache_app_with_actions_and_global_actions(self): - class A: - @action - def x(self): pass - - @action - def y(self): pass - - def z(self): pass - - def z(): pass - - self.cache_app(A) - self.cache_action(z, app='A') - self.assert_cache_has_main(A) - self.assert_cached_app_has_actions( - app='A', - actions={'tests.test_app_cache.A.x': FunctionEntry(run=A.x, is_bound=True, tags=self.action_tag), - 'tests.test_app_cache.A.y': FunctionEntry(run=A.y, is_bound=True, tags=self.action_tag), - 'z': FunctionEntry(run=z, is_bound=False, tags=self.action_tag)}) - - def test_cache_app_with_actions_and_global_actions_name_conflict_resolved(self): - class A: - @action - def x(self): pass - - @action - def y(self): pass - - @action - def z(self): pass - - def z(): pass - - self.cache_app(A) - self.cache_action(z, app='A') - self.assert_cache_has_main(A) - self.assert_cached_app_has_actions( - app='A', - actions={'tests.test_app_cache.A.x': FunctionEntry(run=A.x, is_bound=True, tags=self.action_tag), - 'tests.test_app_cache.A.y': FunctionEntry(run=A.y, is_bound=True, tags=self.action_tag), - 'tests.test_app_cache.A.z': FunctionEntry(run=A.z, is_bound=True, tags=self.action_tag), - 'z': FunctionEntry(run=z, is_bound=False, tags=self.action_tag)}) - - def test_clear_existing_bound_functions_no_actions(self): - class A: pass - - self.cache_app(A) - self.cache._cache['A'].pop('actions') - self.cache._clear_existing_bound_functions('A') - self.assertDictEqual(self.cache._cache, {'A': {'main': A}}) - - def test_clear_existing_bound_functions_no_bound_actions(self): - def x(): pass - - def y(): pass - - self.cache_action(x) - self.cache_action(y) - self.cache._clear_existing_bound_functions('app1') - self.assertDictEqual(self.cache._cache, - {'app1': {'actions': {'x': FunctionEntry(run=x, is_bound=False, tags=self.action_tag), - 'y': FunctionEntry(run=y, is_bound=False, tags=self.action_tag)}}}) - - def test_clear_existing_bound_functions(self): - class A: - @action - def x(self): pass - - @action - def y(self): pass - - def z(self): pass - - def z(): pass - - self.cache_app(A) - self.cache_action(z, app='A') - self.cache._clear_existing_bound_functions('A') - self.assertDictEqual( - self.cache._cache, - {'A': {'main': A, 'actions': {'z': FunctionEntry(run=z, is_bound=False, tags=self.action_tag)}}}) + self.cache._cache_app(A, 'A', 'tests.test_app_cache') def test_cache_module(self): module = import_module('tests.testapps.HelloWorldBounded.main') - from tests.testapps.HelloWorldBounded.main import Main, global1 + from tests.testapps.HelloWorldBounded.main import Main self.cache._cache_module(module, 'HelloWorldBounded', 'tests.testapps') self.assert_cache_has_main(Main, app='HelloWorldBounded') - self.assert_cached_app_has_actions( - app='HelloWorldBounded', - actions={ - 'main.Main.helloWorld': FunctionEntry(run=Main.helloWorld, is_bound=True, tags=self.action_tag), - 'main.Main.repeatBackToMe': FunctionEntry(run=Main.repeatBackToMe, is_bound=True, tags=self.action_tag), - 'main.Main.returnPlusOne': FunctionEntry(run=Main.returnPlusOne, is_bound=True, tags=self.action_tag), - 'main.Main.pause': FunctionEntry(run=Main.pause, is_bound=True, tags=self.action_tag), - 'main.Main.addThree': FunctionEntry(run=Main.addThree, is_bound=True, tags=self.action_tag), - 'main.Main.buggy_action': FunctionEntry(run=Main.buggy_action, is_bound=True, tags=self.action_tag), - 'main.Main.json_sample': FunctionEntry(run=Main.json_sample, is_bound=True, tags=self.action_tag), - 'main.global1': FunctionEntry(run=global1, is_bound=False, tags=self.action_tag)}) + self.assert_cached_app_has_actions(app='HelloWorldBounded', actions={ + 'main.Main.helloWorld', 'main.Main.repeatBackToMe', 'main.Main.returnPlusOne', 'main.Main.pause', + 'main.Main.addThree', 'main.Main.buggy_action', 'main.Main.json_sample','main.global1'}) def test_cache_module_nothing_found(self): module = import_module('tests.testapps.HelloWorldBounded.display') @@ -303,85 +77,34 @@ def test_cache_module_nothing_found(self): def test_cache_module_no_class(self): module = import_module('tests.testapps.HelloWorldBounded.actions') self.cache._cache_module(module, 'HelloWorldBounded', 'tests.testapps') - from tests.testapps.HelloWorldBounded.actions import global2 - self.assert_cached_app_has_actions( - app='HelloWorldBounded', - actions={'actions.global2': FunctionEntry(run=global2, is_bound=False, tags=self.action_tag)}) + self.assert_cached_app_has_actions(app='HelloWorldBounded', actions={'actions.global2'}) def test_import_and_cache_submodules_from_string(self): self.cache._import_and_cache_submodules('tests.testapps.HelloWorldBounded', 'HelloWorldBounded', 'tests.testapps') - from tests.testapps.HelloWorldBounded.main import Main, global1 - from tests.testapps.HelloWorldBounded.actions import global2 - from tests.testapps.HelloWorldBounded.conditions import (top_level_flag, flag1, flag2, flag3, sub1_top_flag, - regMatch, count) - from tests.testapps.HelloWorldBounded.transforms import (top_level_filter, filter1, filter2, filter3, - complex_filter, - length, json_select, sub1_top_filter) + from tests.testapps.HelloWorldBounded.main import Main self.assert_cache_has_main(Main, app='HelloWorldBounded') expected = { - 'main.Main.helloWorld': FunctionEntry(run=Main.helloWorld, is_bound=True, tags=self.action_tag), - 'main.Main.repeatBackToMe': FunctionEntry(run=Main.repeatBackToMe, is_bound=True, tags=self.action_tag), - 'main.Main.returnPlusOne': FunctionEntry(run=Main.returnPlusOne, is_bound=True, tags=self.action_tag), - 'main.Main.pause': FunctionEntry(run=Main.pause, is_bound=True, tags=self.action_tag), - 'main.Main.addThree': FunctionEntry(run=Main.addThree, is_bound=True, tags=self.action_tag), - 'main.Main.buggy_action': FunctionEntry(run=Main.buggy_action, is_bound=True, tags=self.action_tag), - 'main.Main.json_sample': FunctionEntry(run=Main.json_sample, is_bound=True, tags=self.action_tag), - 'main.global1': FunctionEntry(run=global1, is_bound=False, tags=self.action_tag), - 'actions.global2': FunctionEntry(run=global2, is_bound=False, tags=self.action_tag), - 'conditions.top_level_flag': FunctionEntry(run=top_level_flag, is_bound=False, tags=self.condition_tag), - 'conditions.flag1': FunctionEntry(run=flag1, is_bound=False, tags=self.condition_tag), - 'conditions.flag2': FunctionEntry(run=flag2, is_bound=False, tags=self.condition_tag), - 'conditions.flag3': FunctionEntry(run=flag3, is_bound=False, tags=self.condition_tag), - 'conditions.sub1_top_flag': FunctionEntry(run=sub1_top_flag, is_bound=False, tags=self.condition_tag), - 'conditions.regMatch': FunctionEntry(run=regMatch, is_bound=False, tags=self.condition_tag), - 'conditions.count': FunctionEntry(run=count, is_bound=False, tags=self.condition_tag), - 'transforms.top_level_filter': FunctionEntry(run=top_level_filter, is_bound=False, tags=self.transform_tag), - 'transforms.filter2': FunctionEntry(run=filter2, is_bound=False, tags=self.transform_tag), - 'transforms.sub1_top_filter': FunctionEntry(run=sub1_top_filter, is_bound=False, tags=self.transform_tag), - 'transforms.filter3': FunctionEntry(run=filter3, is_bound=False, tags=self.transform_tag), - 'transforms.filter1':FunctionEntry(run=filter1, is_bound=False, tags=self.transform_tag), - 'transforms.complex_filter': FunctionEntry(run=complex_filter, is_bound=False, tags=self.transform_tag), - 'transforms.length': FunctionEntry(run=length, is_bound=False, tags=self.transform_tag), - 'transforms.json_select': FunctionEntry(run=json_select, is_bound=False, tags=self.transform_tag)} + 'main.Main.helloWorld', 'main.Main.repeatBackToMe', 'main.Main.returnPlusOne', 'main.Main.pause', + 'main.Main.addThree', 'main.Main.buggy_action', 'main.Main.json_sample', 'main.global1', 'actions.global2', + 'conditions.top_level_flag', 'conditions.flag1', 'conditions.flag2', 'conditions.flag3', + 'conditions.sub1_top_flag', 'conditions.regMatch', 'conditions.count', 'transforms.top_level_filter', + 'transforms.filter2', 'transforms.sub1_top_filter', 'transforms.filter3', 'transforms.filter1', + 'transforms.complex_filter', 'transforms.length', 'transforms.json_select'} self.assert_cached_app_has_actions(app='HelloWorldBounded', actions=expected) def test_import_and_cache_submodules_from_module(self): module = import_module('tests.testapps.HelloWorldBounded') self.cache._import_and_cache_submodules(module, 'HelloWorldBounded', 'tests.testapps') - from tests.testapps.HelloWorldBounded.main import Main, global1 - from tests.testapps.HelloWorldBounded.actions import global2 - from tests.testapps.HelloWorldBounded.conditions import (top_level_flag, flag1, flag2, flag3, sub1_top_flag, - regMatch, count) - from tests.testapps.HelloWorldBounded.transforms import (top_level_filter, filter1, filter2, filter3, - complex_filter, - length, json_select, sub1_top_filter) + from tests.testapps.HelloWorldBounded.main import Main self.assert_cache_has_main(Main, app='HelloWorldBounded') expected = { - 'main.Main.helloWorld': FunctionEntry(run=Main.helloWorld, is_bound=True, tags=self.action_tag), - 'main.Main.repeatBackToMe': FunctionEntry(run=Main.repeatBackToMe, is_bound=True, tags=self.action_tag), - 'main.Main.returnPlusOne': FunctionEntry(run=Main.returnPlusOne, is_bound=True, tags=self.action_tag), - 'main.Main.pause': FunctionEntry(run=Main.pause, is_bound=True, tags=self.action_tag), - 'main.Main.addThree': FunctionEntry(run=Main.addThree, is_bound=True, tags=self.action_tag), - 'main.Main.buggy_action': FunctionEntry(run=Main.buggy_action, is_bound=True, tags=self.action_tag), - 'main.Main.json_sample': FunctionEntry(run=Main.json_sample, is_bound=True, tags=self.action_tag), - 'main.global1': FunctionEntry(run=global1, is_bound=False, tags=self.action_tag), - 'actions.global2': FunctionEntry(run=global2, is_bound=False, tags=self.action_tag), - 'conditions.top_level_flag': FunctionEntry(run=top_level_flag, is_bound=False, tags=self.condition_tag), - 'conditions.flag1': FunctionEntry(run=flag1, is_bound=False, tags=self.condition_tag), - 'conditions.flag2': FunctionEntry(run=flag2, is_bound=False, tags=self.condition_tag), - 'conditions.flag3': FunctionEntry(run=flag3, is_bound=False, tags=self.condition_tag), - 'conditions.sub1_top_flag': FunctionEntry(run=sub1_top_flag, is_bound=False, tags=self.condition_tag), - 'conditions.regMatch': FunctionEntry(run=regMatch, is_bound=False, tags=self.condition_tag), - 'conditions.count': FunctionEntry(run=count, is_bound=False, tags=self.condition_tag), - 'transforms.top_level_filter': FunctionEntry(run=top_level_filter, is_bound=False, tags=self.transform_tag), - 'transforms.filter2': FunctionEntry(run=filter2, is_bound=False, tags=self.transform_tag), - 'transforms.sub1_top_filter': FunctionEntry(run=sub1_top_filter, is_bound=False, tags=self.transform_tag), - 'transforms.filter3': FunctionEntry(run=filter3, is_bound=False, tags=self.transform_tag), - 'transforms.filter1': FunctionEntry(run=filter1, is_bound=False, tags=self.transform_tag), - 'transforms.complex_filter': FunctionEntry(run=complex_filter, is_bound=False, tags=self.transform_tag), - 'transforms.length': FunctionEntry(run=length, is_bound=False, tags=self.transform_tag), - 'transforms.json_select': FunctionEntry(run=json_select, is_bound=False, tags=self.transform_tag)} + 'main.Main.helloWorld', 'main.Main.repeatBackToMe', 'main.Main.returnPlusOne', 'main.Main.pause', + 'main.Main.addThree', 'main.Main.buggy_action', 'main.Main.json_sample', 'main.global1', 'actions.global2', + 'conditions.top_level_flag', 'conditions.flag1', 'conditions.flag2', 'conditions.flag3', + 'conditions.sub1_top_flag', 'conditions.regMatch', 'conditions.count', 'transforms.top_level_filter', + 'transforms.filter2', 'transforms.sub1_top_filter', 'transforms.filter3', 'transforms.filter1', + 'transforms.complex_filter', 'transforms.length', 'transforms.json_select'} self.assert_cached_app_has_actions(app='HelloWorldBounded', actions=expected) def test_path_to_module_no_slashes(self): @@ -398,49 +121,20 @@ def test_path_to_module_strange_path(self): def test_cache_apps(self): self.cache.cache_apps(os.path.join('.', 'tests', 'testapps')) - from tests.testapps.HelloWorldBounded.main import Main, global1 - from tests.testapps.HelloWorldBounded.conditions import (top_level_flag, flag1, flag2, flag3, sub1_top_flag, - regMatch, count) - from tests.testapps.HelloWorldBounded.transforms import (top_level_filter, filter1, filter2, filter3, - complex_filter, - length, json_select, sub1_top_filter) - from tests.testapps.HelloWorldBounded.actions import global2 + from tests.testapps.HelloWorldBounded.main import Main from tests.testapps.DailyQuote.main import Main as DailyMain self.assert_cache_has_main(Main, app='HelloWorldBounded') hello_world_expected = { - 'main.Main.helloWorld': FunctionEntry(run=Main.helloWorld, is_bound=True, tags=self.action_tag), - 'main.Main.repeatBackToMe': FunctionEntry(run=Main.repeatBackToMe, is_bound=True, tags=self.action_tag), - 'main.Main.returnPlusOne': FunctionEntry(run=Main.returnPlusOne, is_bound=True, tags=self.action_tag), - 'main.Main.pause': FunctionEntry(run=Main.pause, is_bound=True, tags=self.action_tag), - 'main.Main.addThree': FunctionEntry(run=Main.addThree, is_bound=True, tags=self.action_tag), - 'main.Main.buggy_action': FunctionEntry(run=Main.buggy_action, is_bound=True, tags=self.action_tag), - 'main.Main.json_sample': FunctionEntry(run=Main.json_sample, is_bound=True, tags=self.action_tag), - 'main.global1': FunctionEntry(run=global1, is_bound=False, tags=self.action_tag), - 'actions.global2': FunctionEntry(run=global2, is_bound=False, tags=self.action_tag), - 'conditions.top_level_flag': FunctionEntry(run=top_level_flag, is_bound=False, tags=self.condition_tag), - 'conditions.flag1': FunctionEntry(run=flag1, is_bound=False, tags=self.condition_tag), - 'conditions.flag2': FunctionEntry(run=flag2, is_bound=False, tags=self.condition_tag), - 'conditions.flag3': FunctionEntry(run=flag3, is_bound=False, tags=self.condition_tag), - 'conditions.sub1_top_flag': FunctionEntry(run=sub1_top_flag, is_bound=False, tags=self.condition_tag), - 'conditions.regMatch': FunctionEntry(run=regMatch, is_bound=False, tags=self.condition_tag), - 'conditions.count': FunctionEntry(run=count, is_bound=False, tags=self.condition_tag), - 'transforms.top_level_filter': FunctionEntry(run=top_level_filter, is_bound=False, tags=self.transform_tag), - 'transforms.filter2': FunctionEntry(run=filter2, is_bound=False, tags=self.transform_tag), - 'transforms.sub1_top_filter': FunctionEntry(run=sub1_top_filter, is_bound=False, tags=self.transform_tag), - 'transforms.filter3': FunctionEntry(run=filter3, is_bound=False, tags=self.transform_tag), - 'transforms.filter1': FunctionEntry(run=filter1, is_bound=False, tags=self.transform_tag), - 'transforms.complex_filter': FunctionEntry(run=complex_filter, is_bound=False, tags=self.transform_tag), - 'transforms.length': FunctionEntry(run=length, is_bound=False, tags=self.transform_tag), - 'transforms.json_select': FunctionEntry(run=json_select, is_bound=False, tags=self.transform_tag)} + 'main.Main.helloWorld', 'main.Main.repeatBackToMe', 'main.Main.returnPlusOne', 'main.Main.pause', + 'main.Main.addThree', 'main.Main.buggy_action', 'main.Main.json_sample', 'main.global1', 'actions.global2', + 'conditions.top_level_flag', 'conditions.flag1', 'conditions.flag2', 'conditions.flag3', + 'conditions.sub1_top_flag', 'conditions.regMatch', 'conditions.count', 'transforms.top_level_filter', + 'transforms.filter2', 'transforms.sub1_top_filter', 'transforms.filter3', 'transforms.filter1', + 'transforms.complex_filter', 'transforms.length', 'transforms.json_select'} self.assert_cached_app_has_actions(app='HelloWorldBounded', actions=hello_world_expected) self.assert_cache_has_main(DailyMain, app='DailyQuote') daily_quote_expected = { - 'main.Main.quoteIntro': FunctionEntry(run=DailyMain.quoteIntro, is_bound=True, tags=self.action_tag), - 'main.Main.repeatBackToMe': - FunctionEntry(run=DailyMain.repeatBackToMe, is_bound=True, tags=self.action_tag), - 'main.Main.forismaticQuote': - FunctionEntry(run=DailyMain.forismaticQuote, is_bound=True, tags=self.action_tag), - 'main.Main.getQuote': FunctionEntry(run=DailyMain.getQuote, is_bound=True, tags=self.action_tag)} + 'main.Main.quoteIntro', 'main.Main.repeatBackToMe', 'main.Main.forismaticQuote', 'main.Main.getQuote'} self.assert_cache_has_apps({'HelloWorldBounded', 'HelloWorld', 'DailyQuote'}) self.assert_cached_app_has_actions(app='DailyQuote', actions=daily_quote_expected) @@ -476,20 +170,13 @@ class A: pass with self.assertRaises(UnknownApp): self.cache.get_app('B') - def test_get_app_missing_main(self): - def x(): pass - - self.cache._cache_action(x, 'A', 'tests.test_app_cache.TestAppCache', 'action', cls=TestAppCache) - with self.assertRaises(UnknownApp): - self.cache.get_app('A') - def test_get_app(self): class A: pass class B: pass self.cache_app(A) - self.cache._cache_app(B, 'B', 'tests.test_app_cache.TestAppCache') + self.cache._cache_app(B, 'B', 'tests.test_app_cache') self.assertEqual(self.cache.get_app('A'), A) def test_get_app_action_names_empty_cache(self): @@ -528,7 +215,7 @@ def b(self): pass self.cache._cache_app(B, 'B', 'tests.test_app_cache.TestAppCache') app_actions = self.cache.get_app_action_names('A') self.assertEqual(len(app_actions), 2) - self.assertSetEqual(set(app_actions), {'tests.test_app_cache.A.x', 'tests.test_app_cache.A.y'}) + self.assertSetEqual(set(app_actions), {'A.x', 'A.y'}) def test_get_app_action_empty_cache(self): with self.assertRaises(UnknownApp): @@ -582,7 +269,7 @@ def b(self): pass self.cache_app(A) self.cache._cache_app(B, 'B', 'tests.test_app_cache.TestAppCache') - self.assertEqual(self.cache.get_app_action('A', 'tests.test_app_cache.A.x'), A.x) + self.assertEqual(self.cache.get_app_action('A', 'A.x'), A.x) def test_is_app_action_bound_empty_cache(self): with self.assertRaises(UnknownApp): @@ -598,14 +285,14 @@ def y(self): pass self.cache_app(A) with self.assertRaises(UnknownApp): - self.cache.is_app_action_bound('B', 'tests.test_app_cache.B.x') + self.cache.is_app_action_bound('B', 'B.x') def test_is_app_action_bound_no_actions(self): class A: pass self.cache_app(A) with self.assertRaises(UnknownAppAction): - self.cache.is_app_action_bound('A', 'tests.test_app_cache.A.x') + self.cache.is_app_action_bound('A', 'A.x') def test_is_app_action_bound_unknown_action(self): class A: @@ -617,7 +304,7 @@ def y(self): pass self.cache_app(A) with self.assertRaises(UnknownAppAction): - self.cache.is_app_action_bound('A', 'tests.test_app_cache.A.z') + self.cache.is_app_action_bound('A', 'A.z') def test_is_app_action_bound_true(self): class A: @@ -635,8 +322,8 @@ def a(self): pass def b(self): pass self.cache_app(A) - self.cache._cache_app(B, 'B', 'tests.test_app_cache.TestAppCache') - self.assertTrue(self.cache.is_app_action_bound('A', 'tests.test_app_cache.A.x')) + self.cache._cache_app(B, 'B', 'tests.test_app_cache') + self.assertTrue(self.cache.is_app_action_bound('A', 'A.x')) def test_is_app_action_bound_false(self): class A: @@ -656,8 +343,7 @@ def b(self): pass def xx(): pass self.cache_app(A) - self.cache._cache_app(B, 'B', 'tests.test_app_cache.TestAppCache') - self.cache._cache_action(xx, 'A', 'tests.test_app_cache.TestAppCache', 'action', cls=TestAppCache) + self.cache._cache_app(B, 'B', 'tests.test_app_cache') + self.cache._cache['A'].cache_functions([(xx, self.action_tag)], 'tests.test_app_cache') self.assertFalse(self.cache.is_app_action_bound('A', 'xx')) - diff --git a/tests/test_app_cache_entry.py b/tests/test_app_cache_entry.py index 3966be9a9..9bff21f88 100644 --- a/tests/test_app_cache_entry.py +++ b/tests/test_app_cache_entry.py @@ -1,6 +1,128 @@ from unittest import TestCase -from walkoff.appgateway.appcache import AppCacheEntry - +from walkoff.appgateway.appcache import AppCacheEntry, FunctionEntry +from walkoff.appgateway.walkofftag import WalkoffTag +from walkoff.appgateway.decorators import * class TestAppCacheEntry(TestCase): + @classmethod + def setUpClass(cls): + cls.action_tag = {WalkoffTag.action} + cls.condition_tag = {WalkoffTag.condition} + cls.transform_tag = {WalkoffTag.transform} + cls.clspath = 'tests.test_app_cache_entry' + cls.maxDiff = None + + def setUp(self): + self.entry = AppCacheEntry('app1') + + def assert_entry_has_functions(self, expected_functions, entry=None): + entry = self.entry if entry is None else entry + self.assertDictEqual(entry.functions, expected_functions) + + def test_init(self): + app_name = 'App1' + cache = AppCacheEntry(app_name) + self.assertEqual(cache.app_name, app_name) + self.assertIsNone(cache.main) + self.assertDictEqual(cache.functions, {}) + + def test_cache_action(self): + def x(): pass + self.entry.cache_functions([(x, self.action_tag)], self.clspath) + self.assert_entry_has_functions({'x': FunctionEntry(run=x, is_bound=False, tags=self.action_tag)}) + + def test_cache_action_multiple_actions(self): + def x(): pass + + def y(): pass + self.entry.cache_functions([(x, self.action_tag), (y, self.action_tag)], self.clspath) + self.assert_entry_has_functions({'x': FunctionEntry(run=x, is_bound=False, tags=self.action_tag), + 'y': FunctionEntry(run=y, is_bound=False, tags=self.action_tag)}) + + def test_cache_action_overwrite(self): + def x(): pass + + original_id = id(x) + self.entry.cache_functions([(x, self.action_tag)], self.clspath) + + def x(): pass + + self.entry.cache_functions([(x, self.action_tag)], self.clspath) + self.assert_entry_has_functions({'x': FunctionEntry(run=x, is_bound=False, tags=self.action_tag)}) + self.assertNotEqual(id(self.entry.functions['x'].run), original_id) + + def test_cache_app_no_actions_empty_cache(self): + class A: pass + self.entry.cache_app_class(A, self.clspath) + self.assertEqual(self.entry.main, A) + self.assertDictEqual(self.entry.functions, {}) + + def test_cache_app_with_actions_empty_cache(self): + class A: + @action + def x(self): pass + + @action + def y(self): pass + + def z(self): pass + + self.entry.cache_app_class(A, self.clspath) + self.assertEqual(self.entry.main, A) + self.assert_entry_has_functions( + {'A.x': FunctionEntry(run=A.x, is_bound=True, tags=self.action_tag), + 'A.y': FunctionEntry(run=A.y, is_bound=True, tags=self.action_tag)}) + + def test_cache_app_with_actions_and_global_actions(self): + class A: + @action + def x(self): pass + + @action + def y(self): pass + + def z(self): pass + + def z(): pass + + self.entry.cache_app_class(A, self.clspath) + self.entry.cache_functions([(z, self.action_tag)], self.clspath) + self.assertEqual(self.entry.main, A) + + self.assert_entry_has_functions( + {'A.x': FunctionEntry(run=A.x, is_bound=True, tags=self.action_tag), + 'A.y': FunctionEntry(run=A.y, is_bound=True, tags=self.action_tag), + 'z': FunctionEntry(run=z, is_bound=False, tags=self.action_tag)}) + + def test_clear_existing_bound_functions_no_actions(self): + class A: pass + + self.entry.cache_app_class(A, self.clspath) + self.entry.clear_bound_functions() + self.assertDictEqual(self.entry.functions, {}) + + def test_clear_existing_bound_functions_no_bound_actions(self): + def x(): pass + + def y(): pass + self.entry.cache_functions([(x, self.action_tag), (y, self.action_tag)], self.clspath) + self.entry.clear_bound_functions() + self.assert_entry_has_functions({'x': FunctionEntry(run=x, is_bound=False, tags=self.action_tag), + 'y': FunctionEntry(run=y, is_bound=False, tags=self.action_tag)}) + + def test_clear_existing_bound_functions(self): + class A: + @action + def x(self): pass + + @action + def y(self): pass + + def z(self): pass + + def z(): pass + self.entry.cache_app_class(A, self.clspath) + self.entry.cache_functions([(z, self.action_tag)], self.clspath) + self.entry.clear_bound_functions() + self.assert_entry_has_functions({'z': FunctionEntry(run=z, is_bound=False, tags=self.action_tag)}) diff --git a/walkoff/appgateway/appcache.py b/walkoff/appgateway/appcache.py index d7f63e279..952186187 100644 --- a/walkoff/appgateway/appcache.py +++ b/walkoff/appgateway/appcache.py @@ -13,15 +13,75 @@ _logger = logging.getLogger(__name__) -FunctionEntry = namedtuple('AppCacheEntry', ['run', 'is_bound', 'tags']) +FunctionEntry = namedtuple('FunctionEntry', ['run', 'is_bound', 'tags']) class AppCacheEntry(object): - __slots__ = ['main', 'functions'] + __slots__ = ['app_name', 'main', 'functions'] - def __init__(self, main=None, functions=None): - self.main = main - self.functions = functions if functions is not None else {} + def __init__(self, app_name): + self.app_name = app_name + self.main = None + self.functions = {} + + def cache_app_class(self, app_class, app_path): + if self.main is not None: + _logger.warning( + 'App {0} already has class defined as {1}. Overwriting it with {2}'.format( + self.app_name, + _get_qualified_class_name(self.main), + _get_qualified_class_name(app_class))) + self.clear_bound_functions() + self.main = app_class + app_methods = inspect.getmembers( + app_class, (lambda field: + (inspect.ismethod(field) or inspect.isfunction(field)) and WalkoffTag.get_tags(field))) + self.__cache_methods(app_methods, app_class, app_path) + + def __cache_methods(self, app_methods, app_class, app_path): + for _, action_method in app_methods: + tags = WalkoffTag.get_tags(action_method) + qualified_name = _get_qualified_function_name(action_method, cls=app_class) + qualified_name = _strip_base_module_from_qualified_name(qualified_name, app_path) + self.functions[qualified_name] = FunctionEntry(run=action_method, is_bound=True, tags=tags) + + def cache_functions(self, functions, app_path): + """Caches an action + + Args: + functions (list(tuple(func, set(WalkoffTag)))): The functions to cache + app_name (str): The name of the app associated with the function + app_path (str): Path to the app module + """ + for function_, tags in functions: + qualified_action_name = _get_qualified_function_name(function_) + qualified_action_name = _strip_base_module_from_qualified_name(qualified_action_name, app_path) + if qualified_action_name in self.functions: + _logger.warning( + 'App {0} already has {1} defined as {2}. Overwriting it with {3}'.format( + self.app_name, + qualified_action_name, + _get_qualified_function_name(self.functions[qualified_action_name].run), + qualified_action_name)) + self.functions[qualified_action_name] = FunctionEntry(run=function_, is_bound=False, tags=tags) + + def clear_bound_functions(self): + self.functions = {action_name: action for action_name, action in self.functions.items() if not action.is_bound} + + def is_bound(self, func_name): + try: + return self.functions[func_name].is_bound + except KeyError: + raise UnknownAppAction(self.app_name, func_name) + + def get_tagged_functions(self, tag): + return [function_name for function_name, entry in self.functions.items() if tag in entry.tags] + + def get_run(self, func_name, function_type): + func_entry = self.functions[func_name] + if function_type in func_entry.tags: + return func_entry.run + raise Exception() class AppCache(object): @@ -85,8 +145,8 @@ def get_app(self, app_name): _logger.error('Cannot locate app {} in cache!'.format(app_name)) raise UnknownApp(app_name) else: - if 'main' in app_cache: - return app_cache['main'] + if app_cache.main is not None: + return app_cache.main else: _logger.warning('App {} has no class.'.format(app_name)) raise UnknownApp(app_name) @@ -137,17 +197,14 @@ def is_app_action_bound(self, app_name, action_name): """ try: app_cache = self._cache[app_name] - if 'actions' not in app_cache: + if not app_cache.functions: _logger.warning('App {} has no actions'.format(app_name)) raise UnknownAppAction(app_name, action_name) except KeyError: _logger.error('Cannot locate app {} in cache!'.format(app_name)) raise UnknownApp(app_name) - try: - return app_cache['actions'][action_name].is_bound - except KeyError: - _logger.error('App {0} has no action {1}'.format(app_name, action_name)) - raise UnknownAppAction(app_name, action_name) + else: + return app_cache.is_bound(action_name) def get_app_condition_names(self, app_name): """Gets all the names of the conditions for a given app @@ -223,11 +280,7 @@ def _get_function_type_names(self, app_name, function_type): UnknownApp: If the app is not found in the cache """ try: - app_cache = self._cache[app_name] - if 'actions' not in app_cache: - return [] - return [action_name for action_name, action_entry in app_cache['actions'].items() - if function_type in action_entry.tags] + return self._cache[app_name].get_tagged_functions(function_type) except KeyError: _logger.error('Cannot locate app {} in cache!'.format(app_name)) raise UnknownApp(app_name) @@ -251,14 +304,14 @@ def _get_function_type(self, app_name, function_name, function_type): """ try: app_cache = self._cache[app_name] - if 'actions' not in app_cache: + if not app_cache.functions: _logger.warning('App {0} has no actions.'.format(app_name)) raise self.exception_lookup[function_type](app_name, function_name) except KeyError: _logger.error('Cannot locate app {} in cache!'.format(app_name)) raise UnknownApp(app_name) try: - return app_cache['actions'][function_name].run + return app_cache.get_run(function_name, function_type) except KeyError: _logger.error('App {0} has no {1} {2}'.format(app_name, function_type.name, function_name)) raise self.exception_lookup[function_type](app_name, function_name) @@ -313,14 +366,19 @@ def _cache_module(self, module, app_name, app_path): app_name (str): The name of the app associated with the module """ base_path = '.'.join([app_path, app_name]) + global_actions = [] for field, obj in inspect.getmembers(module): if (inspect.isclass(obj) and getattr(obj, '_is_walkoff_app', False) - and AppCache._get_qualified_class_name(obj) != 'apps.App'): + and _get_qualified_class_name(obj) != 'apps.App'): self._cache_app(obj, app_name, base_path) elif inspect.isfunction(obj): tags = WalkoffTag.get_tags(obj) if tags: - self._cache_action(obj, app_name, base_path, tags) + global_actions.append((obj, tags)) + if global_actions: + if app_name not in self._cache: + self._cache[app_name] = AppCacheEntry(app_name) + self._cache[app_name].cache_functions(global_actions, base_path) def _cache_app(self, app_class, app_name, app_path): """Caches an app @@ -330,103 +388,47 @@ def _cache_app(self, app_class, app_name, app_path): app_name (str): The name of the app associated with the class """ if app_name not in self._cache: - self._cache[app_name] = AppCacheEntry() - if self._cache[app_name].main is not None: - _logger.warning( - 'App {0} already has class defined as {1}. Overwriting it with {2}'.format( - app_name, - AppCache._get_qualified_class_name(self._cache[app_name].main), - AppCache._get_qualified_class_name(app_class))) - self._clear_existing_bound_functions(app_name) - self._cache[app_name].main = app_class - app_methods = inspect.getmembers( - app_class, (lambda field: - (inspect.ismethod(field) or inspect.isfunction(field)) and WalkoffTag.get_tags(field))) - self._cache[app_name].cache_methods(app_methods) - if 'actions' not in self._cache[app_name]: - self._cache[app_name]['actions'] = {} - if app_methods: - new_actions = {} - for _, action_method in app_methods: - tags = WalkoffTag.get_tags(action_method) - qualified_name = AppCache._get_qualified_function_name(action_method, cls=app_class) - qualified_name = AppCache._strip_base_module_from_qualified_name(qualified_name, app_path) - new_actions[qualified_name] = FunctionEntry(run=action_method, is_bound=True, tags=tags) - self._cache[app_name]['actions'].update(new_actions) - - def _cache_action(self, action_method, app_name, app_path, tags, cls=None): - """Caches an action + self._cache[app_name] = AppCacheEntry(app_name) + self._cache[app_name].cache_app_class(app_class, app_path) - Args: - action_method (func): The action to cache - app_name (str): The name of the app associated with the action - """ - if app_name not in self._cache: - self._cache[app_name] = {} - if 'actions' not in self._cache[app_name]: - self._cache[app_name]['actions'] = {} - qualified_action_name = AppCache._get_qualified_function_name(action_method, cls=cls) - qualified_action_name = AppCache._strip_base_module_from_qualified_name(qualified_action_name, app_path) - if qualified_action_name in self._cache[app_name]['actions']: - _logger.warning( - 'App {0} already has {1} defined as {2}. Overwriting it with {3}'.format( - app_name, - qualified_action_name, - AppCache._get_qualified_function_name( - self._cache[app_name]['actions'][qualified_action_name].run), - qualified_action_name)) - self._cache[app_name]['actions'][qualified_action_name] = FunctionEntry( - run=action_method, is_bound=False, tags=tags) - - def _clear_existing_bound_functions(self, app_name): - """Clears existing bound functions from an app - Args: - app_name (str): The name of the app to clear - """ - if 'actions' in self._cache[app_name]: - self._cache[app_name]['actions'] = { - action_name: action for action_name, action in self._cache[app_name]['actions'].items() - if not action.is_bound} +def _get_qualified_class_name(obj): + """Gets the qualified name of a class - @staticmethod - def _get_qualified_class_name(obj): - """Gets the qualified name of a class + Args: + obj (cls): The class to get the name - Args: - obj (cls): The class to get the name + Returns: + str: The qualified name of the class + """ + return '{0}.{1}'.format(obj.__module__, obj.__name__) - Returns: - str: The qualified name of the class - """ - return '{0}.{1}'.format(obj.__module__, obj.__name__) - @staticmethod - def _get_qualified_function_name(method, cls=None): - """Gets the qualified name of a function or method +def _get_qualified_function_name(method, cls=None): + """Gets the qualified name of a function or method - Args: - method (func): The function or method to get the name - cls (cls, optional): The class containing this function or method is any + Args: + method (func): The function or method to get the name + cls (cls, optional): The class containing this function or method is any - Returns: - str: The qualified name of the function or method - """ - if cls: - return '{0}.{1}.{2}'.format(method.__module__, cls.__name__, method.__name__) - else: - return '{0}.{1}'.format(method.__module__, method.__name__) + Returns: + str: The qualified name of the function or method + """ + if cls: + return '{0}.{1}.{2}'.format(method.__module__, cls.__name__, method.__name__) + else: + return '{0}.{1}'.format(method.__module__, method.__name__) - @staticmethod - def _strip_base_module_from_qualified_name(qualified_name, base_module): - """Strips a base module from a qualified name - Args: - qualified_name (str): The qualified name to strip - base_module (str): The base module path to strip from the qualified name +def _strip_base_module_from_qualified_name(qualified_name, base_module): + """Strips a base module from a qualified name - Returns: - str: The stripped qualified name - """ - base_module += '.' - return qualified_name[len(base_module):] if qualified_name.startswith(base_module) else qualified_name + Args: + qualified_name (str): The qualified name to strip + base_module (str): The base module path to strip from the qualified name + + Returns: + str: The stripped qualified name + """ + base_module += '.' + return qualified_name[len(base_module):] if qualified_name.startswith(base_module) else qualified_name From c864bad50950ae0eb369f7bfcfa1ef7bc354f9f5 Mon Sep 17 00:00:00 2001 From: JustinTervala Date: Sat, 6 Jan 2018 12:30:49 -0500 Subject: [PATCH 08/15] Travis-CI badge uses shields.io --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 811eeca39..1246c68a3 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,4 @@ -Linux [![Build Status](https://travis-ci.org/iadgov/WALKOFF.svg?branch=master)](https://travis-ci.org/iadgov/WALKOFF) Windows [![Build status](https://ci.appveyor.com/api/projects/status/wsuo57tb1u593hox/branch/development?svg=true)](https://ci.appveyor.com/project/JustinTervala/walkoff-u1gc1/branch/master) +[![Build Status](https://img.shields.io/travis/iadgov/WALKOFF/master.svg?maxAge=3600&label=Linux)](https://travis-ci.org/iadgov/WALKOFF) Windows [![Build status](https://ci.appveyor.com/api/projects/status/wsuo57tb1u593hox/branch/development?svg=true)](https://ci.appveyor.com/project/JustinTervala/walkoff-u1gc1/branch/master) [![Maintainability](https://api.codeclimate.com/v1/badges/330249e13845a07a69a2/maintainability)](https://codeclimate.com/github/iadgov/WALKOFF/maintainability)[![GitHub (pre-)release](https://img.shields.io/github/release/iadgov/WALKOFF/all.svg?style=flat)](release) From 3939f6383f7e5d76fe322b76cb5916dfa9cd7df7 Mon Sep 17 00:00:00 2001 From: iadgovuser11 Date: Mon, 8 Jan 2018 05:39:47 -0500 Subject: [PATCH 09/15] Removed resources.yaml reference in api.yaml -Name was changed to system.yaml. Reference to resources.yaml was accidently left behind. --- walkoff/api/api.yaml | 1 - 1 file changed, 1 deletion(-) diff --git a/walkoff/api/api.yaml b/walkoff/api/api.yaml index 7e61d9fb8..92eb3f9b0 100644 --- a/walkoff/api/api.yaml +++ b/walkoff/api/api.yaml @@ -66,7 +66,6 @@ paths: $ref: ./metrics.yaml $ref: ./playbooks.yaml $ref: ./roles.yaml - $ref: ./resources.yaml $ref: ./scheduler.yaml $ref: ./system.yaml $ref: ./triggers.yaml From c20270e53fc100ecc8fca2a9746e593a360f6516 Mon Sep 17 00:00:00 2001 From: Jeremy Sestok Date: Mon, 8 Jan 2018 13:09:54 -0500 Subject: [PATCH 10/15] minor updates to support testing --- walkoff/client/main/main.component.ts | 14 ++++++++++---- walkoff/client/main/main.css | 6 +++--- walkoff/client/main/main.html | 6 +++--- walkoff/client/main/main.service.ts | 14 +++++++++++++- walkoff/client/tsconfig.json | 3 ++- 5 files changed, 31 insertions(+), 12 deletions(-) diff --git a/walkoff/client/main/main.component.ts b/walkoff/client/main/main.component.ts index f729a7e2d..6220958fd 100644 --- a/walkoff/client/main/main.component.ts +++ b/walkoff/client/main/main.component.ts @@ -38,17 +38,23 @@ export class MainComponent { constructor( private mainService: MainService, private authService: AuthService, private modalService: NgbModal, private toastyService: ToastyService, private toastyConfig: ToastyConfig, - ) { - this.toastyConfig.theme = 'bootstrap'; + ) {} - this.mainService.getInterfaceNamess() - .then(interfaceNames => this.interfaceNames = interfaceNames); + ngOnInit(): void { + this.toastyConfig.theme = 'bootstrap'; this.currentUser = this.authService.getAndDecodeAccessToken().user_claims.username; + this.getInterfaceNames(); this.getInitialNotifications(); this.getNotificationsSSE(); } + getInterfaceNames(): void { + this.mainService.getInterfaceNames() + .then(interfaceNames => this.interfaceNames = interfaceNames) + .catch(e => this.toastyService.error(`Error retrieving interfaces: ${e.message}`)); + } + getInitialNotifications(): void { this.mainService.getInitialNotifications() .then(messageListings => { diff --git a/walkoff/client/main/main.css b/walkoff/client/main/main.css index da9bf0a6b..2cbca7fe1 100644 --- a/walkoff/client/main/main.css +++ b/walkoff/client/main/main.css @@ -73,15 +73,15 @@ h3 { background-color: #3c8dbc; } -#walkoffNav .navbar-nav .dropdown .dropdown-menu .installedApp { +#walkoffNav .navbar-nav .dropdown .dropdown-menu .installedInterface { color: white; } -#walkoffNav .navbar-nav .dropdown .dropdown-menu .installedApp:hover { +#walkoffNav .navbar-nav .dropdown .dropdown-menu .installedInterface:hover { color: #3c8dbc; } -#walkoffNav .navbar-nav .dropdown .dropdown-menu .installedApp span { +#walkoffNav .navbar-nav .dropdown .dropdown-menu .installedInterface span { margin-left: 5px; } diff --git a/walkoff/client/main/main.html b/walkoff/client/main/main.html index 78a0c5045..fa84e1d5d 100644 --- a/walkoff/client/main/main.html +++ b/walkoff/client/main/main.html @@ -26,7 +26,7 @@