diff options
author | John Estabrook <jestabro@vyos.io> | 2021-11-22 15:29:46 -0600 |
---|---|---|
committer | John Estabrook <jestabro@vyos.io> | 2021-11-24 13:33:45 -0600 |
commit | 114be88565cd09c0a7dcbe67a753d7d2834659b9 (patch) | |
tree | 127bc9de8f4fab653920608e7591341881a37153 | |
parent | 97022cea7fe4bb56919bcaa85568afac2420aba7 (diff) | |
download | vyos-1x-114be88565cd09c0a7dcbe67a753d7d2834659b9.tar.gz vyos-1x-114be88565cd09c0a7dcbe67a753d7d2834659b9.zip |
graphql: T3993: refactor directive and mutation definitions
(cherry picked from commit ef7f5ca2fd2c0113875dbd9143342e925cf00621)
-rw-r--r-- | src/services/api/graphql/README.graphql | 5 | ||||
-rw-r--r-- | src/services/api/graphql/graphql/directives.py | 33 | ||||
-rw-r--r-- | src/services/api/graphql/graphql/mutations.py | 77 | ||||
-rw-r--r-- | src/services/api/graphql/recipes/config_file.py | 16 | ||||
-rw-r--r-- | src/services/api/graphql/recipes/create_dhcp_server.py | 13 | ||||
-rw-r--r-- | src/services/api/graphql/recipes/create_interface_ethernet.py | 13 | ||||
-rw-r--r-- | src/services/api/graphql/recipes/session.py (renamed from src/services/api/graphql/recipes/recipe.py) | 2 |
7 files changed, 44 insertions, 115 deletions
diff --git a/src/services/api/graphql/README.graphql b/src/services/api/graphql/README.graphql index 6e9e16c6b..29f58f709 100644 --- a/src/services/api/graphql/README.graphql +++ b/src/services/api/graphql/README.graphql @@ -101,11 +101,8 @@ services │ │ └── schema.graphql │ ├── README.graphql │ ├── recipes -│ │ ├── config_file.py -│ │ ├── create_dhcp_server.py -│ │ ├── create_interface_ethernet.py │ │ ├── __init__.py -│ │ ├── recipe.py +│ │ ├── session.py │ │ └── templates │ │ ├── create_dhcp_server.tmpl │ │ └── create_interface_ethernet.tmpl diff --git a/src/services/api/graphql/graphql/directives.py b/src/services/api/graphql/graphql/directives.py index a7706610c..f5cd88acd 100644 --- a/src/services/api/graphql/graphql/directives.py +++ b/src/services/api/graphql/graphql/directives.py @@ -1,34 +1,37 @@ from ariadne import SchemaDirectiveVisitor, ObjectType from . mutations import make_configure_resolver, make_config_file_resolver -class ConfigureDirective(SchemaDirectiveVisitor): - """ - Class providing implementation of 'configure' directive in schema. +def non(arg): + pass - """ - def visit_field_definition(self, field, object_type): +class VyosDirective(SchemaDirectiveVisitor): + def visit_field_definition(self, field, object_type, make_resolver=non): name = f'{field.type}' # field.type contains the return value of the mutation; trim value # to produce canonical name name = name.replace('Result', '', 1) - func = make_configure_resolver(name) + func = make_resolver(name) field.resolve = func return field -class ConfigFileDirective(SchemaDirectiveVisitor): + +class ConfigureDirective(VyosDirective): """ - Class providing implementation of 'configfile' directive in schema. + Class providing implementation of 'configure' directive in schema. """ def visit_field_definition(self, field, object_type): - name = f'{field.type}' - # field.type contains the return value of the mutation; trim value - # to produce canonical name - name = name.replace('Result', '', 1) + super().visit_field_definition(field, object_type, + make_resolver=make_configure_resolver) - func = make_config_file_resolver(name) - field.resolve = func - return field +class ConfigFileDirective(VyosDirective): + """ + Class providing implementation of 'configfile' directive in schema. + + """ + def visit_field_definition(self, field, object_type): + super().visit_field_definition(field, object_type, + make_resolver=make_config_file_resolver) directives_dict = {"configure": ConfigureDirective, "configfile": ConfigFileDirective} diff --git a/src/services/api/graphql/graphql/mutations.py b/src/services/api/graphql/graphql/mutations.py index 02d5be0ef..8a28b13d7 100644 --- a/src/services/api/graphql/graphql/mutations.py +++ b/src/services/api/graphql/graphql/mutations.py @@ -6,10 +6,11 @@ from graphql import GraphQLResolveInfo from makefun import with_signature from .. import state +from api.graphql.recipes.session import Session mutation = ObjectType("Mutation") -def make_configure_resolver(mutation_name): +def make_resolver(mutation_name, class_name, session_func): """Dynamically generate a resolver for the mutation named in the schema by 'mutation_name'. @@ -19,9 +20,9 @@ def make_configure_resolver(mutation_name): functools.wraps. :raise Exception: - encapsulating ConfigErrors, or internal errors + raising ConfigErrors, or internal errors """ - class_name = mutation_name + func_base_name = convert_camel_case_to_snake(class_name) resolver_name = f'resolve_{func_base_name}' func_sig = '(obj: Any, info: GraphQLResolveInfo, data: Dict)' @@ -40,10 +41,17 @@ def make_configure_resolver(mutation_name): data = kwargs['data'] session = state.settings['app'].state.vyos_session - mod = import_module(f'api.graphql.recipes.{func_base_name}') - klass = getattr(mod, class_name) + # one may override the session functions with a local subclass + try: + mod = import_module(f'api.graphql.recipes.{func_base_name}') + klass = getattr(mod, class_name) + except ImportError: + # otherwise, dynamically generate subclass to invoke subclass + # name based templates + klass = type(class_name, (Session,), {}) k = klass(session, data) - k.configure() + method = getattr(k, session_func) + method() return { "success": True, @@ -57,53 +65,16 @@ def make_configure_resolver(mutation_name): return func_impl +def make_configure_resolver(mutation_name): + class_name = mutation_name + return make_resolver(mutation_name, class_name, 'configure') + def make_config_file_resolver(mutation_name): - op = '' if 'Save' in mutation_name: - op = 'save' + class_name = mutation_name.replace('Save', '', 1) + return make_resolver(mutation_name, class_name, 'save') elif 'Load' in mutation_name: - op = 'load' - - class_name = mutation_name.replace('Save', '', 1).replace('Load', '', 1) - func_base_name = convert_camel_case_to_snake(class_name) - resolver_name = f'resolve_{func_base_name}' - func_sig = '(obj: Any, info: GraphQLResolveInfo, data: Dict)' - - @mutation.field(mutation_name) - @convert_kwargs_to_snake_case - @with_signature(func_sig, func_name=resolver_name) - async def func_impl(*args, **kwargs): - try: - if 'data' not in kwargs: - return { - "success": False, - "errors": ['missing data'] - } - - data = kwargs['data'] - session = state.settings['app'].state.vyos_session - - mod = import_module(f'api.graphql.recipes.{func_base_name}') - klass = getattr(mod, class_name) - k = klass(session, data) - if op == 'save': - k.save() - elif op == 'load': - k.load() - else: - return { - "success": False, - "errors": ["Input must be saveConfigFile | loadConfigFile"] - } - - return { - "success": True, - "data": data - } - except Exception as error: - return { - "success": False, - "errors": [str(error)] - } - - return func_impl + class_name = mutation_name.replace('Load', '', 1) + return make_resolver(mutation_name, class_name, 'load') + else: + raise Exception diff --git a/src/services/api/graphql/recipes/config_file.py b/src/services/api/graphql/recipes/config_file.py deleted file mode 100644 index 850e5326e..000000000 --- a/src/services/api/graphql/recipes/config_file.py +++ /dev/null @@ -1,16 +0,0 @@ - -from . recipe import Recipe - -class ConfigFile(Recipe): - def __init__(self, session, command_file): - super().__init__(session, command_file) - - # Define any custom processing of parameters here by overriding - # save/load: - # - # def save(self): - # self.data = transform_data(self.data) - # super().save() - # def load(self): - # self.data = transform_data(self.data) - # super().load() diff --git a/src/services/api/graphql/recipes/create_dhcp_server.py b/src/services/api/graphql/recipes/create_dhcp_server.py deleted file mode 100644 index ac6f15209..000000000 --- a/src/services/api/graphql/recipes/create_dhcp_server.py +++ /dev/null @@ -1,13 +0,0 @@ - -from . recipe import Recipe - -class CreateDhcpServer(Recipe): - def __init__(self, session, command_file): - super().__init__(session, command_file) - - # Define any custom processing of parameters here by overriding - # configure: - # - # def configure(self): - # self.data = transform_data(self.data) - # super().configure() diff --git a/src/services/api/graphql/recipes/create_interface_ethernet.py b/src/services/api/graphql/recipes/create_interface_ethernet.py deleted file mode 100644 index aafb4d55c..000000000 --- a/src/services/api/graphql/recipes/create_interface_ethernet.py +++ /dev/null @@ -1,13 +0,0 @@ - -from . recipe import Recipe - -class CreateInterfaceEthernet(Recipe): - def __init__(self, session, command_file): - super().__init__(session, command_file) - - # Define any custom processing of parameters here by overriding - # configure: - # - # def configure(self): - # self.data = transform_data(self.data) - # super().configure() diff --git a/src/services/api/graphql/recipes/recipe.py b/src/services/api/graphql/recipes/session.py index 91d8bd67a..aa3932ab9 100644 --- a/src/services/api/graphql/recipes/recipe.py +++ b/src/services/api/graphql/recipes/session.py @@ -2,7 +2,7 @@ from ariadne import convert_camel_case_to_snake import vyos.defaults from vyos.template import render -class Recipe(object): +class Session(object): def __init__(self, session, data): self._session = session self.data = data |