summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJohn Estabrook <jestabro@vyos.io>2021-11-22 15:29:46 -0600
committerJohn Estabrook <jestabro@vyos.io>2021-11-23 10:46:55 -0600
commitef7f5ca2fd2c0113875dbd9143342e925cf00621 (patch)
tree5a5ddc62f15f429062e42625a1517aad52d55a0a
parentfb2dc58d91bd93ba3aaa63d46e49e6609c18d46f (diff)
downloadvyos-1x-ef7f5ca2fd2c0113875dbd9143342e925cf00621.tar.gz
vyos-1x-ef7f5ca2fd2c0113875dbd9143342e925cf00621.zip
graphql: T3993: refactor directive and mutation definitions
-rw-r--r--src/services/api/graphql/README.graphql5
-rw-r--r--src/services/api/graphql/graphql/directives.py33
-rw-r--r--src/services/api/graphql/graphql/mutations.py77
-rw-r--r--src/services/api/graphql/recipes/config_file.py16
-rw-r--r--src/services/api/graphql/recipes/create_dhcp_server.py13
-rw-r--r--src/services/api/graphql/recipes/create_interface_ethernet.py13
-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