From 7038b761302be2ec90338981830b8cd7cf887381 Mon Sep 17 00:00:00 2001 From: John Estabrook Date: Sun, 23 Oct 2022 11:06:03 -0500 Subject: graphql: T4574: reorganize directory structure for clarity --- .../api/graphql/utils/composite_function.py | 11 -- .../api/graphql/utils/config_session_function.py | 28 ---- .../api/graphql/utils/schema_from_composite.py | 119 -------------- .../graphql/utils/schema_from_config_session.py | 119 -------------- .../api/graphql/utils/schema_from_op_mode.py | 183 --------------------- src/services/api/graphql/utils/util.py | 100 ----------- 6 files changed, 560 deletions(-) delete mode 100644 src/services/api/graphql/utils/composite_function.py delete mode 100644 src/services/api/graphql/utils/config_session_function.py delete mode 100755 src/services/api/graphql/utils/schema_from_composite.py delete mode 100755 src/services/api/graphql/utils/schema_from_config_session.py delete mode 100755 src/services/api/graphql/utils/schema_from_op_mode.py delete mode 100644 src/services/api/graphql/utils/util.py (limited to 'src/services/api/graphql/utils') diff --git a/src/services/api/graphql/utils/composite_function.py b/src/services/api/graphql/utils/composite_function.py deleted file mode 100644 index bc9d80fbb..000000000 --- a/src/services/api/graphql/utils/composite_function.py +++ /dev/null @@ -1,11 +0,0 @@ -# typing information for composite functions: those that invoke several -# elementary requests, and return the result as a single dict -import typing - -def system_status(): - pass - -queries = {'system_status': system_status} - -mutations = {} - diff --git a/src/services/api/graphql/utils/config_session_function.py b/src/services/api/graphql/utils/config_session_function.py deleted file mode 100644 index fc0dd7a87..000000000 --- a/src/services/api/graphql/utils/config_session_function.py +++ /dev/null @@ -1,28 +0,0 @@ -# typing information for native configsession functions; used to generate -# schema definition files -import typing - -def show_config(path: list[str], configFormat: typing.Optional[str]): - pass - -def show(path: list[str]): - pass - -queries = {'show_config': show_config, - 'show': show} - -def save_config_file(fileName: typing.Optional[str]): - pass -def load_config_file(fileName: str): - pass -def add_system_image(location: str): - pass -def delete_system_image(name: str): - pass - -mutations = {'save_config_file': save_config_file, - 'load_config_file': load_config_file, - 'add_system_image': add_system_image, - 'delete_system_image': delete_system_image} - - diff --git a/src/services/api/graphql/utils/schema_from_composite.py b/src/services/api/graphql/utils/schema_from_composite.py deleted file mode 100755 index d5e0ecdf6..000000000 --- a/src/services/api/graphql/utils/schema_from_composite.py +++ /dev/null @@ -1,119 +0,0 @@ -#!/usr/bin/env python3 -# -# Copyright (C) 2022 VyOS maintainers and contributors -# -# This program is free software; you can redistribute it and/or modify -# it under the terms of the GNU General Public License version 2 or later as -# published by the Free Software Foundation. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. -# -# You should have received a copy of the GNU General Public License -# along with this program. If not, see . -# -# -# A utility to generate GraphQL schema defintions from typing information of -# composite functions comprising several requests. - -import os -import json -from inspect import signature, getmembers, isfunction, isclass, getmro -from jinja2 import Template - -from vyos.defaults import directories -if __package__ is None or __package__ == '': - from util import snake_to_pascal_case, map_type_name - from composite_function import queries, mutations -else: - from . util import snake_to_pascal_case, map_type_name - from . composite_function import queries, mutations - -SCHEMA_PATH = directories['api_schema'] - -schema_data: dict = {'schema_name': '', - 'schema_fields': []} - -query_template = """ -input {{ schema_name }}Input { - key: String! - {%- for field_entry in schema_fields %} - {{ field_entry }} - {%- endfor %} -} - -type {{ schema_name }} { - result: Generic -} - -type {{ schema_name }}Result { - data: {{ schema_name }} - success: Boolean! - errors: [String] -} - -extend type Query { - {{ schema_name }}(data: {{ schema_name }}Input) : {{ schema_name }}Result @compositequery -} -""" - -mutation_template = """ -input {{ schema_name }}Input { - key: String! - {%- for field_entry in schema_fields %} - {{ field_entry }} - {%- endfor %} -} - -type {{ schema_name }} { - result: Generic -} - -type {{ schema_name }}Result { - data: {{ schema_name }} - success: Boolean! - errors: [String] -} - -extend type Mutation { - {{ schema_name }}(data: {{ schema_name }}Input) : {{ schema_name }}Result @compositemutation -} -""" - -def create_schema(func_name: str, func: callable, template: str) -> str: - sig = signature(func) - - field_dict = {} - for k in sig.parameters: - field_dict[sig.parameters[k].name] = map_type_name(sig.parameters[k].annotation) - - schema_fields = [] - for k,v in field_dict.items(): - schema_fields.append(k+': '+v) - - schema_data['schema_name'] = snake_to_pascal_case(func_name) - schema_data['schema_fields'] = schema_fields - - j2_template = Template(template) - res = j2_template.render(schema_data) - - return res - -def generate_composite_definitions(): - results = [] - for name,func in queries.items(): - res = create_schema(name, func, query_template) - results.append(res) - - for name,func in mutations.items(): - res = create_schema(name, func, mutation_template) - results.append(res) - - out = '\n'.join(results) - with open(f'{SCHEMA_PATH}/composite.graphql', 'w') as f: - f.write(out) - -if __name__ == '__main__': - generate_composite_definitions() diff --git a/src/services/api/graphql/utils/schema_from_config_session.py b/src/services/api/graphql/utils/schema_from_config_session.py deleted file mode 100755 index b6609357e..000000000 --- a/src/services/api/graphql/utils/schema_from_config_session.py +++ /dev/null @@ -1,119 +0,0 @@ -#!/usr/bin/env python3 -# -# Copyright (C) 2022 VyOS maintainers and contributors -# -# This program is free software; you can redistribute it and/or modify -# it under the terms of the GNU General Public License version 2 or later as -# published by the Free Software Foundation. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. -# -# You should have received a copy of the GNU General Public License -# along with this program. If not, see . -# -# -# A utility to generate GraphQL schema defintions from typing information of -# (wrappers of) native configsession functions. - -import os -import json -from inspect import signature, getmembers, isfunction, isclass, getmro -from jinja2 import Template - -from vyos.defaults import directories -if __package__ is None or __package__ == '': - from util import snake_to_pascal_case, map_type_name - from config_session_function import queries, mutations -else: - from . util import snake_to_pascal_case, map_type_name - from . config_session_function import queries, mutations - -SCHEMA_PATH = directories['api_schema'] - -schema_data: dict = {'schema_name': '', - 'schema_fields': []} - -query_template = """ -input {{ schema_name }}Input { - key: String! - {%- for field_entry in schema_fields %} - {{ field_entry }} - {%- endfor %} -} - -type {{ schema_name }} { - result: Generic -} - -type {{ schema_name }}Result { - data: {{ schema_name }} - success: Boolean! - errors: [String] -} - -extend type Query { - {{ schema_name }}(data: {{ schema_name }}Input) : {{ schema_name }}Result @configsessionquery -} -""" - -mutation_template = """ -input {{ schema_name }}Input { - key: String! - {%- for field_entry in schema_fields %} - {{ field_entry }} - {%- endfor %} -} - -type {{ schema_name }} { - result: Generic -} - -type {{ schema_name }}Result { - data: {{ schema_name }} - success: Boolean! - errors: [String] -} - -extend type Mutation { - {{ schema_name }}(data: {{ schema_name }}Input) : {{ schema_name }}Result @configsessionmutation -} -""" - -def create_schema(func_name: str, func: callable, template: str) -> str: - sig = signature(func) - - field_dict = {} - for k in sig.parameters: - field_dict[sig.parameters[k].name] = map_type_name(sig.parameters[k].annotation) - - schema_fields = [] - for k,v in field_dict.items(): - schema_fields.append(k+': '+v) - - schema_data['schema_name'] = snake_to_pascal_case(func_name) - schema_data['schema_fields'] = schema_fields - - j2_template = Template(template) - res = j2_template.render(schema_data) - - return res - -def generate_config_session_definitions(): - results = [] - for name,func in queries.items(): - res = create_schema(name, func, query_template) - results.append(res) - - for name,func in mutations.items(): - res = create_schema(name, func, mutation_template) - results.append(res) - - out = '\n'.join(results) - with open(f'{SCHEMA_PATH}/configsession.graphql', 'w') as f: - f.write(out) - -if __name__ == '__main__': - generate_config_session_definitions() diff --git a/src/services/api/graphql/utils/schema_from_op_mode.py b/src/services/api/graphql/utils/schema_from_op_mode.py deleted file mode 100755 index 57d63628b..000000000 --- a/src/services/api/graphql/utils/schema_from_op_mode.py +++ /dev/null @@ -1,183 +0,0 @@ -#!/usr/bin/env python3 -# -# Copyright (C) 2022 VyOS maintainers and contributors -# -# This program is free software; you can redistribute it and/or modify -# it under the terms of the GNU General Public License version 2 or later as -# published by the Free Software Foundation. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. -# -# You should have received a copy of the GNU General Public License -# along with this program. If not, see . -# -# -# A utility to generate GraphQL schema defintions from standardized op-mode -# scripts. - -import os -import json -from inspect import signature, getmembers, isfunction, isclass, getmro -from jinja2 import Template - -from vyos.defaults import directories -if __package__ is None or __package__ == '': - from util import load_as_module, is_op_mode_function_name, is_show_function_name - from util import snake_to_pascal_case, map_type_name -else: - from . util import load_as_module, is_op_mode_function_name, is_show_function_name - from . util import snake_to_pascal_case, map_type_name - -OP_MODE_PATH = directories['op_mode'] -SCHEMA_PATH = directories['api_schema'] -DATA_DIR = directories['data'] - -op_mode_include_file = os.path.join(DATA_DIR, 'op-mode-standardized.json') -op_mode_error_schema = 'op_mode_error.graphql' - -schema_data: dict = {'schema_name': '', - 'schema_fields': []} - -query_template = """ -input {{ schema_name }}Input { - key: String! - {%- for field_entry in schema_fields %} - {{ field_entry }} - {%- endfor %} -} - -type {{ schema_name }} { - result: Generic -} - -type {{ schema_name }}Result { - data: {{ schema_name }} - op_mode_error: OpModeError - success: Boolean! - errors: [String] -} - -extend type Query { - {{ schema_name }}(data: {{ schema_name }}Input) : {{ schema_name }}Result @genopquery -} -""" - -mutation_template = """ -input {{ schema_name }}Input { - key: String! - {%- for field_entry in schema_fields %} - {{ field_entry }} - {%- endfor %} -} - -type {{ schema_name }} { - result: Generic -} - -type {{ schema_name }}Result { - data: {{ schema_name }} - op_mode_error: OpModeError - success: Boolean! - errors: [String] -} - -extend type Mutation { - {{ schema_name }}(data: {{ schema_name }}Input) : {{ schema_name }}Result @genopmutation -} -""" - -error_template = """ -interface OpModeError { - name: String! - message: String! - vyos_code: Int! -} -{% for name in error_names %} -type {{ name }} implements OpModeError { - name: String! - message: String! - vyos_code: Int! -} -{%- endfor %} -""" - -def create_schema(func_name: str, base_name: str, func: callable) -> str: - sig = signature(func) - - field_dict = {} - for k in sig.parameters: - field_dict[sig.parameters[k].name] = map_type_name(sig.parameters[k].annotation) - - # It is assumed that if one is generating a schema for a 'show_*' - # function, that 'get_raw_data' is present and 'raw' is desired. - if 'raw' in list(field_dict): - del field_dict['raw'] - - schema_fields = [] - for k,v in field_dict.items(): - schema_fields.append(k+': '+v) - - schema_data['schema_name'] = snake_to_pascal_case(func_name + '_' + base_name) - schema_data['schema_fields'] = schema_fields - - if is_show_function_name(func_name): - j2_template = Template(query_template) - else: - j2_template = Template(mutation_template) - - res = j2_template.render(schema_data) - - return res - -def create_error_schema(): - from vyos import opmode - - e = Exception - err_types = getmembers(opmode, isclass) - err_types = [k for k in err_types if issubclass(k[1], e)] - # drop base class, to be replaced by interface type. Find the class - # programmatically, in case the base class name changes. - for i in range(len(err_types)): - if err_types[i][1] in getmro(err_types[i-1][1]): - del err_types[i] - break - err_names = [k[0] for k in err_types] - error_data = {'error_names': err_names} - j2_template = Template(error_template) - res = j2_template.render(error_data) - - return res - -def generate_op_mode_definitions(): - out = create_error_schema() - with open(f'{SCHEMA_PATH}/{op_mode_error_schema}', 'w') as f: - f.write(out) - - with open(op_mode_include_file) as f: - op_mode_files = json.load(f) - - for file in op_mode_files: - basename = os.path.splitext(file)[0].replace('-', '_') - module = load_as_module(basename, os.path.join(OP_MODE_PATH, file)) - - funcs = getmembers(module, isfunction) - funcs = list(filter(lambda ft: is_op_mode_function_name(ft[0]), funcs)) - - funcs_dict = {} - for (name, thunk) in funcs: - funcs_dict[name] = thunk - - results = [] - for name,func in funcs_dict.items(): - res = create_schema(name, basename, func) - results.append(res) - - out = '\n'.join(results) - with open(f'{SCHEMA_PATH}/{basename}.graphql', 'w') as f: - f.write(out) - -if __name__ == '__main__': - generate_op_mode_definitions() diff --git a/src/services/api/graphql/utils/util.py b/src/services/api/graphql/utils/util.py deleted file mode 100644 index da2bcdb5b..000000000 --- a/src/services/api/graphql/utils/util.py +++ /dev/null @@ -1,100 +0,0 @@ -# Copyright 2022 VyOS maintainers and contributors -# -# This library is free software; you can redistribute it and/or -# modify it under the terms of the GNU Lesser General Public -# License as published by the Free Software Foundation; either -# version 2.1 of the License, or (at your option) any later version. -# -# This library is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU -# Lesser General Public License for more details. -# -# You should have received a copy of the GNU Lesser General Public License -# along with this library. If not, see . - -import os -import re -import typing -import importlib.util - -from vyos.defaults import directories - -def load_as_module(name: str, path: str): - spec = importlib.util.spec_from_file_location(name, path) - mod = importlib.util.module_from_spec(spec) - spec.loader.exec_module(mod) - return mod - -def load_op_mode_as_module(name: str): - path = os.path.join(directories['op_mode'], name) - name = os.path.splitext(name)[0].replace('-', '_') - return load_as_module(name, path) - -def is_op_mode_function_name(name): - if re.match(r"^(show|clear|reset|restart)", name): - return True - return False - -def is_show_function_name(name): - if re.match(r"^show", name): - return True - return False - -def _nth_split(delim: str, n: int, s: str): - groups = s.split(delim) - l = len(groups) - if n > l-1 or n < 1: - return (s, '') - return (delim.join(groups[:n]), delim.join(groups[n:])) - -def _nth_rsplit(delim: str, n: int, s: str): - groups = s.split(delim) - l = len(groups) - if n > l-1 or n < 1: - return (s, '') - return (delim.join(groups[:l-n]), delim.join(groups[l-n:])) - -# Since we have mangled possible hyphens in the file name while constructing -# the snake case of the query/mutation name, we will need to recover the -# file name by searching with mangling: -def _filter_on_mangled(test): - def func(elem): - mangle = os.path.splitext(elem)[0].replace('-', '_') - return test == mangle - return func - -# Find longest name in concatenated string that matches the basename of an -# op-mode script. Should one prefer to concatenate in the reverse order -# (script_name + '_' + function_name), use _nth_rsplit. -def split_compound_op_mode_name(name: str, files: list): - for i in range(1, name.count('_') + 1): - pair = _nth_split('_', i, name) - f = list(filter(_filter_on_mangled(pair[1]), files)) - if f: - pair = (pair[0], f[0]) - return pair - return (name, '') - -def snake_to_pascal_case(name: str) -> str: - res = ''.join(map(str.title, name.split('_'))) - return res - -def map_type_name(type_name: type, optional: bool = False) -> str: - if type_name == str: - return 'String!' if not optional else 'String = null' - if type_name == int: - return 'Int!' if not optional else 'Int = null' - if type_name == bool: - return 'Boolean!' if not optional else 'Boolean = false' - if typing.get_origin(type_name) == list: - if not optional: - return f'[{map_type_name(typing.get_args(type_name)[0])}]!' - return f'[{map_type_name(typing.get_args(type_name)[0])}]' - # typing.Optional is typing.Union[_, NoneType] - if (typing.get_origin(type_name) is typing.Union and - typing.get_args(type_name)[1] == type(None)): - return f'{map_type_name(typing.get_args(type_name)[0], optional=True)}' - - # scalar 'Generic' is defined in schema.graphql - return 'Generic' -- cgit v1.2.3