...
This commit is contained in:
		@@ -0,0 +1,28 @@
 | 
			
		||||
from typing import Union
 | 
			
		||||
 | 
			
		||||
from fastapi import FastAPI
 | 
			
		||||
from vm_manager__vm_start import vm_start
 | 
			
		||||
 | 
			
		||||
app = FastAPI()
 | 
			
		||||
 | 
			
		||||
#VM WOULD BE AN OBJECT of e.g. a virtual machine description
 | 
			
		||||
 | 
			
		||||
@app.get("/$circleguid/vm_manager/vm")
 | 
			
		||||
def vm_get()-> VM:
 | 
			
		||||
    return {...}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@app.post("/$circleguid/vm_manager/vm")
 | 
			
		||||
def vm_set()-> bool:
 | 
			
		||||
    return True
 | 
			
		||||
 | 
			
		||||
@app.delete("/$circleguid/vm_manager/vm")
 | 
			
		||||
def vm_delete()-> bool:
 | 
			
		||||
    ##would use osis to delete this objecc
 | 
			
		||||
    return True
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@app.get("/$circleguid/vm_manager/vm_start/{vm_guid}")
 | 
			
		||||
def vm_start(vm_guid: str) -> bool:
 | 
			
		||||
    vm_start(context=context,vm_guid=vm_guid)
 | 
			
		||||
    
 | 
			
		||||
@@ -0,0 +1,8 @@
 | 
			
		||||
 | 
			
		||||
def vm_start(context, vm_guid: str) -> bool:
 | 
			
		||||
    #context allows us to see who the user is and which groups the user is
 | 
			
		||||
    #context also gives a logging feature
 | 
			
		||||
    #context is linked to 1 circle
 | 
			
		||||
    #context is linked to a DB (OSIS)
 | 
			
		||||
    
 | 
			
		||||
    #code to be implemented e.g. using DAGU to start a vm
 | 
			
		||||
@@ -0,0 +1,256 @@
 | 
			
		||||
import os
 | 
			
		||||
from pathlib import Path
 | 
			
		||||
from typing import Dict, List, Optional, Union
 | 
			
		||||
 | 
			
		||||
from jinja2 import Environment, FileSystemLoader
 | 
			
		||||
 | 
			
		||||
from heroserver.openrpc.generator.code.python.python_code_generator import PythonCodeGenerator
 | 
			
		||||
from heroserver.openrpc.generator.model.model_generator import ModelGenerator
 | 
			
		||||
 | 
			
		||||
# Fix the issue by ensuring that the 'object' variable is properly defined and has the expected attributes.
 | 
			
		||||
# The following code will ensure that 'object' is a valid SchemaObject before calling 'print_items'.
 | 
			
		||||
from heroserver.openrpc.model.common import ContentDescriptorObject, ReferenceObject, SchemaObject
 | 
			
		||||
from heroserver.openrpc.model.openrpc_spec import OpenRPCSpec
 | 
			
		||||
from heroserver.openrpc.parser.parser import parser
 | 
			
		||||
 | 
			
		||||
script_dir = os.path.dirname(os.path.abspath(__file__))
 | 
			
		||||
env = Environment(loader=FileSystemLoader(script_dir))
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class RestServerGenerator:
 | 
			
		||||
    def __init__(
 | 
			
		||||
        self,
 | 
			
		||||
        spec: OpenRPCSpec,
 | 
			
		||||
        dir: Path,
 | 
			
		||||
    ) -> None:
 | 
			
		||||
        if not isinstance(spec, OpenRPCSpec):
 | 
			
		||||
            raise TypeError(f"Expected spec to be of type OpenRPCSpec, got {type(spec)}")
 | 
			
		||||
        if not isinstance(dir, Path):
 | 
			
		||||
            raise TypeError(f"Expected dir to be of type Path, got {type(dir)}")
 | 
			
		||||
 | 
			
		||||
        self.model_generator = ModelGenerator(spec, PythonCodeGenerator())
 | 
			
		||||
        self.spec = spec
 | 
			
		||||
        self.dir = dir
 | 
			
		||||
        self.crud_methods_template = env.get_template("templates/crud_methods.jinja")
 | 
			
		||||
        self.internal_crud_methods_template = env.get_template("templates/internal_crud_methods.jinja")
 | 
			
		||||
        self.internal_crud_mock_methods_template = env.get_template("templates/internal_crud_mock_methods.jinja")
 | 
			
		||||
        self.imports_template = env.get_template("templates/imports.jinja")
 | 
			
		||||
        self.actor_method_template = env.get_template("templates/actor_method.jinja")
 | 
			
		||||
        self.internal_actor_method_template = env.get_template("templates/internal_actor_method.jinja")
 | 
			
		||||
        self.server_template = env.get_template("templates/server.jinja")
 | 
			
		||||
 | 
			
		||||
    def generate(self):
 | 
			
		||||
        self.dir.mkdir(parents=True, exist_ok=True)
 | 
			
		||||
 | 
			
		||||
        self.generate_models()
 | 
			
		||||
        self.generate_crud()
 | 
			
		||||
        self.generate_mock_crud()
 | 
			
		||||
        self.generate_internal_actor_methods()
 | 
			
		||||
        self.generate_openapi()
 | 
			
		||||
        self.generate_openapi_mock()
 | 
			
		||||
        self.generate_server()
 | 
			
		||||
 | 
			
		||||
        print(f"Generated API code has been written to {self.dir}")
 | 
			
		||||
 | 
			
		||||
    def generate_server(self):
 | 
			
		||||
        code = self.server_template.render()
 | 
			
		||||
 | 
			
		||||
        path = self.dir.joinpath("server.py")
 | 
			
		||||
        with open(path, "w") as file:
 | 
			
		||||
            file.write(code)
 | 
			
		||||
 | 
			
		||||
    def generate_openapi(self):
 | 
			
		||||
        imports = self.imports_template.render(import_crud=True, import_models=True)
 | 
			
		||||
        app_init = "app = FastAPI()\n\n"
 | 
			
		||||
        methods = ""
 | 
			
		||||
        for path_str in self.model_generator.spec.get_root_objects().keys():
 | 
			
		||||
            object = self.model_generator.processed_objects[path_str]
 | 
			
		||||
            if object["code"] == "":
 | 
			
		||||
                continue
 | 
			
		||||
 | 
			
		||||
            type_name = object["name"]
 | 
			
		||||
            variable_name = type_name.lower()
 | 
			
		||||
            methods += self.crud_methods_template.render(variable_name=variable_name, type_name=type_name) + "\n\n"
 | 
			
		||||
 | 
			
		||||
        for method in self.spec.methods:
 | 
			
		||||
            if any(method.name.endswith(end) for end in ["get", "set", "delete"]):
 | 
			
		||||
                continue
 | 
			
		||||
 | 
			
		||||
            params: Dict[str, str] = {}
 | 
			
		||||
            for param in method.params:
 | 
			
		||||
                params[param.name] = self.model_generator.jsonschema_to_type(["methods", method.name, "params", param.name], param.schema)
 | 
			
		||||
 | 
			
		||||
            return_type = self.method_result_return_type(["methods", method.name, "result"], method.result)
 | 
			
		||||
 | 
			
		||||
            function_name = method.name.lower().replace(".", "_")
 | 
			
		||||
            imports += f"from {function_name}_internal import {function_name}_internal\n"
 | 
			
		||||
            methods += (
 | 
			
		||||
                self.actor_method_template.render(
 | 
			
		||||
                    rest_server_generator=self,
 | 
			
		||||
                    function_name=function_name,
 | 
			
		||||
                    method_params=params,
 | 
			
		||||
                    method_result=return_type,
 | 
			
		||||
                )
 | 
			
		||||
                + "\n\n"
 | 
			
		||||
            )
 | 
			
		||||
 | 
			
		||||
        path = self.dir.joinpath("open_api.py")
 | 
			
		||||
        with open(path, "w") as file:
 | 
			
		||||
            file.write(f"{imports}\n\n{app_init}\n\n{methods}")
 | 
			
		||||
 | 
			
		||||
    def generate_openapi_mock(self):
 | 
			
		||||
        imports = self.imports_template.render(mock=True, import_crud=True, import_models=True)
 | 
			
		||||
        app_init = "app = FastAPI()\n\n"
 | 
			
		||||
        methods = ""
 | 
			
		||||
        for path_str in self.model_generator.spec.get_root_objects().keys():
 | 
			
		||||
            object = self.model_generator.processed_objects[path_str]
 | 
			
		||||
            if object["code"] == "":
 | 
			
		||||
                continue
 | 
			
		||||
 | 
			
		||||
            type_name = object["name"]
 | 
			
		||||
            variable_name = type_name.lower()
 | 
			
		||||
            methods += self.crud_methods_template.render(mock=True, variable_name=variable_name, type_name=type_name) + "\n\n"
 | 
			
		||||
 | 
			
		||||
        for method in self.spec.methods:
 | 
			
		||||
            if any(method.name.endswith(end) for end in ["get", "set", "delete"]):
 | 
			
		||||
                continue
 | 
			
		||||
 | 
			
		||||
            params: Dict[str, str] = {}
 | 
			
		||||
            for param in method.params:
 | 
			
		||||
                params[param.name] = self.model_generator.jsonschema_to_type(["methods", method.name, "params", param.name], param.schema)
 | 
			
		||||
 | 
			
		||||
            return_type = self.method_result_return_type(["methods", method.name, "result"], method.result)
 | 
			
		||||
 | 
			
		||||
            function_name = method.name.lower().replace(".", "_")
 | 
			
		||||
            imports += f"from {function_name}_internal import {function_name}_internal\n"
 | 
			
		||||
            methods += (
 | 
			
		||||
                self.actor_method_template.render(
 | 
			
		||||
                    mock=True,
 | 
			
		||||
                    rest_server_generator=self,
 | 
			
		||||
                    function_name=function_name,
 | 
			
		||||
                    method_params=params,
 | 
			
		||||
                    method_result=return_type,
 | 
			
		||||
                )
 | 
			
		||||
                + "\n\n"
 | 
			
		||||
            )
 | 
			
		||||
 | 
			
		||||
        path = self.dir.joinpath("open_api_mock.py")
 | 
			
		||||
        with open(path, "w") as file:
 | 
			
		||||
            file.write(f"{imports}\n\n{app_init}\n\n{methods}")
 | 
			
		||||
 | 
			
		||||
    def generate_models(self):
 | 
			
		||||
        imports = self.imports_template.render()
 | 
			
		||||
        code = self.model_generator.generate_models()
 | 
			
		||||
        path = self.dir.joinpath("models.py")
 | 
			
		||||
 | 
			
		||||
        with open(path, "w") as file:
 | 
			
		||||
            file.write(f"{imports}\n\n{code}\n")
 | 
			
		||||
 | 
			
		||||
    def generate_crud(self):
 | 
			
		||||
        imports = self.imports_template.render(import_models=True)
 | 
			
		||||
        methods = ""
 | 
			
		||||
        for path_str in self.model_generator.spec.get_root_objects().keys():
 | 
			
		||||
            object = self.model_generator.processed_objects[path_str]
 | 
			
		||||
            if object["code"] == "":
 | 
			
		||||
                continue
 | 
			
		||||
 | 
			
		||||
            type_name = object["name"]
 | 
			
		||||
            variable_name = type_name.lower()
 | 
			
		||||
            methods += self.internal_crud_methods_template.render(variable_name=variable_name, type_name=type_name) + "\n\n"
 | 
			
		||||
 | 
			
		||||
        path = self.dir.joinpath("crud.py")
 | 
			
		||||
        with open(path, "w") as file:
 | 
			
		||||
            file.write(f"{imports}\n\n{methods}")
 | 
			
		||||
 | 
			
		||||
    def generate_mock_crud(self):
 | 
			
		||||
        imports = self.imports_template.render(import_models=True)
 | 
			
		||||
        imports += "from heroserver.openrpc.tools import create_example_object"
 | 
			
		||||
        methods = ""
 | 
			
		||||
        for path_str in self.model_generator.spec.get_root_objects().keys():
 | 
			
		||||
            object = self.model_generator.spec.get_root_objects()[path_str]
 | 
			
		||||
 | 
			
		||||
            if isinstance(object, SchemaObject):
 | 
			
		||||
                print_items(object)
 | 
			
		||||
 | 
			
		||||
            object = self.model_generator.processed_objects[path_str]
 | 
			
		||||
            if object["code"] == "":
 | 
			
		||||
                continue
 | 
			
		||||
 | 
			
		||||
            type_name = object["name"]
 | 
			
		||||
            variable_name = type_name.lower()
 | 
			
		||||
 | 
			
		||||
            methods += self.internal_crud_mock_methods_template.render(variable_name=variable_name, type_name=type_name) + "\n\n"
 | 
			
		||||
 | 
			
		||||
        path = self.dir.joinpath("crud_mock.py")
 | 
			
		||||
        with open(path, "w") as file:
 | 
			
		||||
            file.write(f"{imports}\n\n{methods}")
 | 
			
		||||
 | 
			
		||||
    def generate_internal_actor_methods(self):
 | 
			
		||||
        imports = self.imports_template.render(import_models=True)
 | 
			
		||||
        for method in self.spec.methods:
 | 
			
		||||
            function_name = method.name.lower().replace(".", "_") + "_internal"
 | 
			
		||||
            file_path = self.dir.joinpath(f"{function_name}.py")
 | 
			
		||||
            if file_path.exists():
 | 
			
		||||
                continue
 | 
			
		||||
 | 
			
		||||
            if any(method.name.endswith(end) for end in ["get", "set", "delete"]):
 | 
			
		||||
                continue
 | 
			
		||||
 | 
			
		||||
            params: Dict[str, str] = {}
 | 
			
		||||
            for param in method.params:
 | 
			
		||||
                params[param.name] = self.model_generator.jsonschema_to_type(["methods", method.name, "params", param.name], param.schema)
 | 
			
		||||
 | 
			
		||||
            return_type = self.method_result_return_type(["methods", method.name, "result"], method.result)
 | 
			
		||||
 | 
			
		||||
            code = self.internal_actor_method_template.render(
 | 
			
		||||
                rest_server_generator=self,
 | 
			
		||||
                function_name=function_name,
 | 
			
		||||
                method_params=params,
 | 
			
		||||
                method_result=return_type,
 | 
			
		||||
            )
 | 
			
		||||
 | 
			
		||||
            with open(file_path, "w") as file:
 | 
			
		||||
                file.write(f"{imports}\n\n{code}")
 | 
			
		||||
 | 
			
		||||
    def get_method_params(self, method_params: Dict[str, str]) -> str:
 | 
			
		||||
        return ", ".join([f"{param_name}: {param_type}" for param_name, param_type in method_params.items()])
 | 
			
		||||
 | 
			
		||||
    def method_result_return_type(
 | 
			
		||||
        self,
 | 
			
		||||
        path: List[str],
 | 
			
		||||
        method_result: Optional[Union[ContentDescriptorObject, ReferenceObject]],
 | 
			
		||||
    ) -> str:
 | 
			
		||||
        if not method_result:
 | 
			
		||||
            type_name = ""
 | 
			
		||||
 | 
			
		||||
        if isinstance(method_result, ContentDescriptorObject):
 | 
			
		||||
            schema = method_result.schema
 | 
			
		||||
            type_name = self.model_generator.jsonschema_to_type(path, schema)
 | 
			
		||||
 | 
			
		||||
        elif isinstance(method_result, ReferenceObject):
 | 
			
		||||
            type_name = self.model_generator.jsonschema_to_type(path, method_result)
 | 
			
		||||
 | 
			
		||||
        return type_name
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def print_items(schema_object, depth=0):
 | 
			
		||||
    print(f"prito {schema_object.items}")
 | 
			
		||||
    indent = "    " * depth
 | 
			
		||||
    if isinstance(schema_object.items, list):
 | 
			
		||||
        for item in schema_object.items:
 | 
			
		||||
            print(f"{indent}Item: {item}")
 | 
			
		||||
            if isinstance(item, SchemaObject):
 | 
			
		||||
                print_items(item, depth + 1)
 | 
			
		||||
            print(f"{indent}Example: {item.example}")
 | 
			
		||||
    elif isinstance(schema_object.items, SchemaObject):
 | 
			
		||||
        print(f"{indent}Item: {schema_object.items}")
 | 
			
		||||
        print_items(schema_object.items, depth + 1)
 | 
			
		||||
        print(f"{indent}Example: {schema_object.items.example}")
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
if __name__ == "__main__":
 | 
			
		||||
    data = parser(path="~/code/git.threefold.info/hero/hero_server_python/baobabspecs")
 | 
			
		||||
 | 
			
		||||
    spec_object = OpenRPCSpec.load(data)
 | 
			
		||||
    server_generator = RestServerGenerator(spec_object, Path("/tmp/rest2"))
 | 
			
		||||
    server_generator.generate()
 | 
			
		||||
@@ -0,0 +1,7 @@
 | 
			
		||||
@app.post("/$circleguid/{{function_name}}")
 | 
			
		||||
def {{ function_name }}(circleguid: int, {{ rest_server_generator.get_method_params(method_params) }}){% if method_result %} -> {{ method_result }}{% endif %}:
 | 
			
		||||
    {% if mock %}
 | 
			
		||||
        return {{function_name}}_internal_mock(context, circleguid, {{', '.join(method_params.keys())}})
 | 
			
		||||
    {% else %}
 | 
			
		||||
        return {{function_name}}_internal(context, circleguid, {{', '.join(method_params.keys())}})
 | 
			
		||||
    {% endif %}
 | 
			
		||||
@@ -0,0 +1,16 @@
 | 
			
		||||
{% if mock %}
 | 
			
		||||
    {% set suffix = '_mock' %}
 | 
			
		||||
{% else %}
 | 
			
		||||
    {% set suffix = '' %}
 | 
			
		||||
{% endif %}
 | 
			
		||||
@app.get("/{circleguid}/{{variable_name}}_manager{{suffix}}/{{variable_name}}/{id}")
 | 
			
		||||
def {{variable_name}}_get(circleguid: int, id: str)-> {{type_name}}:
 | 
			
		||||
    return {{variable_name}}_get_internal{{suffix}}(circleguid, id)
 | 
			
		||||
 | 
			
		||||
@app.post("/{circleguid}/{{variable_name}}_manager{{suffix}}/{{variable_name}}")
 | 
			
		||||
def {{variable_name}}_set(circleguid: int, {{variable_name}}: {{type_name}})-> bool:
 | 
			
		||||
    return {{variable_name}}_set_internal{{suffix}}(circleguid, {{variable_name}})
 | 
			
		||||
 | 
			
		||||
@app.delete("/{circleguid}/{{variable_name}}_manager{{suffix}}/{{variable_name}}/{id}")
 | 
			
		||||
def {{variable_name}}_delete(circleguid: int, id: str)-> bool:
 | 
			
		||||
    return {{variable_name}}_delete_internal{{suffix}}(circleguid, id)
 | 
			
		||||
@@ -0,0 +1,12 @@
 | 
			
		||||
{% if mock %}
 | 
			
		||||
    {% set suffix = '_mock' %}
 | 
			
		||||
{% else %}
 | 
			
		||||
    {% set suffix = '' %}
 | 
			
		||||
{% endif %}
 | 
			
		||||
from fastapi import FastAPI
 | 
			
		||||
from pydantic import BaseModel, Field
 | 
			
		||||
from typing import List
 | 
			
		||||
from enum import Enum
 | 
			
		||||
{% if import_models %}from models import *{% endif %}
 | 
			
		||||
{% if import_crud %}from crud{{suffix}} import *{% endif %}
 | 
			
		||||
{% if import_openapi %}from open_api import *{% endif %}
 | 
			
		||||
@@ -0,0 +1,9 @@
 | 
			
		||||
from typing import List, Optional, Dict, Union
 | 
			
		||||
from enum import Enum
 | 
			
		||||
 | 
			
		||||
def {{function_name}}(context, circleguid: int, {{rest_server_generator.get_method_params(method_params)}}) -> {{method_result}}:
 | 
			
		||||
    #context allows us to see who the user is and which groups the user is
 | 
			
		||||
    #context also gives a logging feature
 | 
			
		||||
    #context is linked to 1 circle
 | 
			
		||||
    #context is linked to a DB (OSIS)
 | 
			
		||||
    pass
 | 
			
		||||
@@ -0,0 +1,11 @@
 | 
			
		||||
def {{variable_name}}_get_internal(circleguid: int, id: str) -> {{type_name}}:
 | 
			
		||||
    return {{type_name}}()
 | 
			
		||||
 | 
			
		||||
def {{variable_name}}_set_internal(circleguid: int, {{variable_name}}: {{type_name}})-> bool:
 | 
			
		||||
    return True
 | 
			
		||||
 | 
			
		||||
def {{variable_name}}_delete_internal(circleguid: int, id: str)-> bool:
 | 
			
		||||
    ##would use osis to delete this objecc
 | 
			
		||||
    return True
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@@ -0,0 +1,8 @@
 | 
			
		||||
def {{variable_name}}_get_internal_mock(circleguid: int, id: str) -> {{type_name}}:
 | 
			
		||||
    return create_example_object({{type_name}})
 | 
			
		||||
 | 
			
		||||
def {{variable_name}}_set_internal_mock(circleguid: int, {{variable_name}}: {{type_name}})-> bool:
 | 
			
		||||
    return True
 | 
			
		||||
 | 
			
		||||
def {{variable_name}}_delete_internal_mock(circleguid: int, id: str)-> bool:
 | 
			
		||||
    return True
 | 
			
		||||
@@ -0,0 +1,5 @@
 | 
			
		||||
import uvicorn
 | 
			
		||||
from open_api import app
 | 
			
		||||
 | 
			
		||||
if __name__ == "__main__":
 | 
			
		||||
    uvicorn.run(app, host="0.0.0.0", port=8000)
 | 
			
		||||
@@ -0,0 +1,169 @@
 | 
			
		||||
import os
 | 
			
		||||
from pathlib import Path
 | 
			
		||||
from typing import Dict, List, Optional, Union
 | 
			
		||||
 | 
			
		||||
from jinja2 import Environment, FileSystemLoader
 | 
			
		||||
 | 
			
		||||
from heroserver.openrpc.generator.actor.vlang.vlang_code_generator import VlangGenerator
 | 
			
		||||
from heroserver.openrpc.generator.model_generator import ModelGenerator
 | 
			
		||||
from heroserver.openrpc.model.common import ContentDescriptorObject, ReferenceObject
 | 
			
		||||
from heroserver.openrpc.model.openrpc_spec import OpenRPCSpec
 | 
			
		||||
 | 
			
		||||
script_dir = os.path.dirname(os.path.abspath(__file__))
 | 
			
		||||
env = Environment(loader=FileSystemLoader(script_dir))
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class RestServerGenerator:
 | 
			
		||||
    def __init__(
 | 
			
		||||
        self,
 | 
			
		||||
        spec: OpenRPCSpec,
 | 
			
		||||
        dir: Path,
 | 
			
		||||
    ) -> None:
 | 
			
		||||
        self.lang_code_generator = VlangGenerator()
 | 
			
		||||
        self.model_generator = ModelGenerator(spec, VlangGenerator())
 | 
			
		||||
        self.spec = spec
 | 
			
		||||
        self.dir = dir
 | 
			
		||||
        self.crud_methods_template = env.get_template("templates/crud_methods.jinja")
 | 
			
		||||
        self.internal_crud_methods_template = env.get_template("templates/internal_crud_methods.jinja")
 | 
			
		||||
        self.imports_template = env.get_template("templates/imports.jinja")
 | 
			
		||||
        self.actor_method_template = env.get_template("templates/actor_method.jinja")
 | 
			
		||||
        self.internal_actor_method_template = env.get_template("templates/internal_actor_method.jinja")
 | 
			
		||||
        self.server_template = env.get_template("templates/server.jinja")
 | 
			
		||||
 | 
			
		||||
    def generate(self):
 | 
			
		||||
        self.dir.mkdir(parents=True, exist_ok=True)
 | 
			
		||||
 | 
			
		||||
        self.generate_models()
 | 
			
		||||
        self.generate_crud()
 | 
			
		||||
        self.generate_internal_actor_methods()
 | 
			
		||||
        self.generate_openapi()
 | 
			
		||||
        self.generate_server()
 | 
			
		||||
 | 
			
		||||
        print(f"Generated API code has been written to {self.dir}")
 | 
			
		||||
 | 
			
		||||
    def generate_server(self):
 | 
			
		||||
        imports = self.imports_template.render(import_vweb=True)
 | 
			
		||||
        code = self.server_template.render()
 | 
			
		||||
 | 
			
		||||
        path = self.dir.joinpath("server.v")
 | 
			
		||||
        with open(path, "w") as file:
 | 
			
		||||
            file.write(f"{imports}\n\n{code}")
 | 
			
		||||
 | 
			
		||||
    def generate_openapi(self):
 | 
			
		||||
        imports = self.imports_template.render(import_vweb=True)
 | 
			
		||||
        methods = ""
 | 
			
		||||
        for path_str in self.model_generator.spec.get_root_objects().keys():
 | 
			
		||||
            object = self.model_generator.processed_objects[path_str]
 | 
			
		||||
            if object["code"] == "":
 | 
			
		||||
                continue
 | 
			
		||||
 | 
			
		||||
            type_name = object["name"]
 | 
			
		||||
            variable_name = type_name.lower()
 | 
			
		||||
            methods += self.crud_methods_template.render(variable_name=variable_name, type_name=type_name) + "\n\n"
 | 
			
		||||
 | 
			
		||||
        for method in self.spec.methods:
 | 
			
		||||
            if any(method.name.endswith(end) for end in ["get", "set", "delete"]):
 | 
			
		||||
                continue
 | 
			
		||||
 | 
			
		||||
            params: Dict[str, str] = {}
 | 
			
		||||
            for param in method.params:
 | 
			
		||||
                params[param.name] = self.model_generator.jsonschema_to_type(["methods", method.name, "params", param.name], param.schema)
 | 
			
		||||
 | 
			
		||||
            return_type = self.method_result_return_type(["methods", method.name, "result"], method.result)
 | 
			
		||||
 | 
			
		||||
            function_name = method.name.lower().replace(".", "_")
 | 
			
		||||
            methods += (
 | 
			
		||||
                self.actor_method_template.render(
 | 
			
		||||
                    rest_server_generator=self,
 | 
			
		||||
                    function_name=function_name,
 | 
			
		||||
                    method_params=params,
 | 
			
		||||
                    method_result=return_type,
 | 
			
		||||
                )
 | 
			
		||||
                + "\n\n"
 | 
			
		||||
            )
 | 
			
		||||
 | 
			
		||||
        path = self.dir.joinpath("open_api.v")
 | 
			
		||||
        with open(path, "w") as file:
 | 
			
		||||
            file.write(f"{imports}\n\n{methods}")
 | 
			
		||||
 | 
			
		||||
    def generate_models(self):
 | 
			
		||||
        imports = self.imports_template.render()
 | 
			
		||||
        code = self.model_generator.generate_models()
 | 
			
		||||
        path = self.dir.joinpath("models.v")
 | 
			
		||||
 | 
			
		||||
        with open(path, "w") as file:
 | 
			
		||||
            file.write(f"{imports}\n\n{code}\n")
 | 
			
		||||
 | 
			
		||||
    def generate_crud(self):
 | 
			
		||||
        imports = self.imports_template.render(import_models=True)
 | 
			
		||||
        methods = ""
 | 
			
		||||
        for path_str in self.model_generator.spec.get_root_objects().keys():
 | 
			
		||||
            object = self.model_generator.processed_objects[path_str]
 | 
			
		||||
            if object["code"] == "":
 | 
			
		||||
                continue
 | 
			
		||||
 | 
			
		||||
            type_name = object["name"]
 | 
			
		||||
            variable_name = type_name.lower()
 | 
			
		||||
            methods += self.internal_crud_methods_template.render(variable_name=variable_name, type_name=type_name) + "\n\n"
 | 
			
		||||
 | 
			
		||||
        path = self.dir.joinpath("crud.v")
 | 
			
		||||
        with open(path, "w") as file:
 | 
			
		||||
            file.write(f"{imports}\n\n{methods}")
 | 
			
		||||
 | 
			
		||||
    def generate_internal_actor_methods(self):
 | 
			
		||||
        imports = self.imports_template.render(import_models=True)
 | 
			
		||||
        for method in self.spec.methods:
 | 
			
		||||
            function_name = method.name.lower().replace(".", "_") + "_internal"
 | 
			
		||||
            file_path = self.dir.joinpath(f"{function_name}.v")
 | 
			
		||||
            if file_path.exists():
 | 
			
		||||
                continue
 | 
			
		||||
 | 
			
		||||
            if any(method.name.endswith(end) for end in ["get", "set", "delete"]):
 | 
			
		||||
                continue
 | 
			
		||||
 | 
			
		||||
            params: Dict[str, str] = {}
 | 
			
		||||
            for param in method.params:
 | 
			
		||||
                params[param.name] = self.model_generator.jsonschema_to_type(["methods", method.name, "params", param.name], param.schema)
 | 
			
		||||
 | 
			
		||||
            return_type = self.method_result_return_type(["methods", method.name, "result"], method.result)
 | 
			
		||||
 | 
			
		||||
            code = self.internal_actor_method_template.render(
 | 
			
		||||
                rest_server_generator=self,
 | 
			
		||||
                function_name=function_name,
 | 
			
		||||
                method_params=params,
 | 
			
		||||
                method_result=return_type,
 | 
			
		||||
            )
 | 
			
		||||
 | 
			
		||||
            with open(file_path, "w") as file:
 | 
			
		||||
                file.write(f"{imports}\n\n{code}")
 | 
			
		||||
 | 
			
		||||
    def get_method_params(self, method_params: Dict[str, str]) -> str:
 | 
			
		||||
        return ", ".join([f"{param_name} {param_type}" for param_name, param_type in method_params.items()])
 | 
			
		||||
 | 
			
		||||
    def method_result_return_type(
 | 
			
		||||
        self,
 | 
			
		||||
        path: List[str],
 | 
			
		||||
        method_result: Optional[Union[ContentDescriptorObject, ReferenceObject]],
 | 
			
		||||
    ) -> str:
 | 
			
		||||
        if not method_result:
 | 
			
		||||
            type_name = ""
 | 
			
		||||
 | 
			
		||||
        if isinstance(method_result, ContentDescriptorObject):
 | 
			
		||||
            schema = method_result.schema
 | 
			
		||||
            type_name = self.model_generator.jsonschema_to_type(path, schema)
 | 
			
		||||
 | 
			
		||||
        elif isinstance(method_result, ReferenceObject):
 | 
			
		||||
            type_name = self.model_generator.jsonschema_to_type(path, method_result)
 | 
			
		||||
 | 
			
		||||
        return type_name
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
if __name__ == "__main__":
 | 
			
		||||
    from heroserver.openrpc.generator.model_generator import ModelGenerator
 | 
			
		||||
    from heroserver.openrpc.parser.parser import parser
 | 
			
		||||
 | 
			
		||||
    data = parser(path="/root/code/git.threefold.info/projectmycelium/hero_server/generatorexamples/example1/specs/storymanager")
 | 
			
		||||
 | 
			
		||||
    spec_object = OpenRPCSpec.load(data)
 | 
			
		||||
    server_generator = RestServerGenerator(spec_object, Path("/tmp/rest3"))
 | 
			
		||||
    server_generator.generate()
 | 
			
		||||
@@ -0,0 +1,20 @@
 | 
			
		||||
@['/:circleguid/{{function_name}}'; post]
 | 
			
		||||
pub fn (mut v_server_app VServerApp) {{ function_name }}(circleguid int) vweb.Result{
 | 
			
		||||
    body := json2.raw_decode(v_server_app.req.data)!.as_map()
 | 
			
		||||
    {% for param_name, param_tpe in method_params.items() %}
 | 
			
		||||
    {% if rest_server_generator.lang_code_generator.is_primitive(param_type) %}
 | 
			
		||||
    {{param_name}} := body['{{param_name}}'].{{param_type}}()
 | 
			
		||||
    {% else %}
 | 
			
		||||
    {{param_name}} := json2.decode[{{param_type}}](body['{{param_name}}'].json_str()) or {
 | 
			
		||||
        v_server_app.set_status(400, '')
 | 
			
		||||
        return v_server_app.text('HTTP 400: Bad Request')
 | 
			
		||||
    }
 | 
			
		||||
    {% endif %}
 | 
			
		||||
    {% endfor %}
 | 
			
		||||
    res := {{function_name}}_internal(context, circleguid, {{', '.join(method_params.keys())}}) or {
 | 
			
		||||
        v_server_app.set_status(500, '')
 | 
			
		||||
        return v_server_app.text('HTTP 500: Internal Server Error')
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    return v_server_app.json(res)
 | 
			
		||||
}
 | 
			
		||||
@@ -0,0 +1,32 @@
 | 
			
		||||
@['/:circleguid/{{variable_name}}_manager/{{variable_name}}/:id'; get]
 | 
			
		||||
pub fn (mut v_server_app VServerApp) {{variable_name}}_get(circleguid int, id str) vweb.Result{
 | 
			
		||||
    res := {{variable_name}}_get_internal(circleguid, id) or {
 | 
			
		||||
        v_server_app.set_status(500, '')
 | 
			
		||||
        return v_server_app.text('HTTP 500: Internal Server Error')
 | 
			
		||||
    }
 | 
			
		||||
    return v_server_app.json(res)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@['/:circleguid/{{variable_name}}_manager/{{variable_name}}'; post]
 | 
			
		||||
pub fn (mut v_server_app VServerApp) {{variable_name}}_set(circleguid int) vweb.Result{
 | 
			
		||||
    {{variable_name}} := json2.decode[{{type_name}}](v_server_app.req.data) or {
 | 
			
		||||
        v_server_app.set_status(400, '')
 | 
			
		||||
        return v_server_app.text('HTTP 400: Bad Request')
 | 
			
		||||
    }
 | 
			
		||||
    res := {{variable_name}}_set_internal(circleguid, {{variable_name}})or {
 | 
			
		||||
        v_server_app.set_status(500, '')
 | 
			
		||||
        return v_server_app.text('HTTP 500: Internal Server Error')
 | 
			
		||||
    }
 | 
			
		||||
    
 | 
			
		||||
    return v_server_app.json(res)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@['/:circleguid/{{variable_name}}_manager/{{variable_name}}/:id'; delete]
 | 
			
		||||
pub fn (mut v_server_app VServerApp) {{variable_name}}_delete(circleguid int, id str) vweb.Result{
 | 
			
		||||
    ##would use osis to delete this objecc
 | 
			
		||||
    res := {{variable_name}}_delete_internal(circleguid, id) or {
 | 
			
		||||
        v_server_app.set_status(500, '')
 | 
			
		||||
        return v_server_app.text('HTTP 500: Internal Server Error')
 | 
			
		||||
    }
 | 
			
		||||
    return v_server_app.json(res)
 | 
			
		||||
}
 | 
			
		||||
@@ -0,0 +1,4 @@
 | 
			
		||||
module main
 | 
			
		||||
 | 
			
		||||
import x.json2
 | 
			
		||||
{% if import_vweb %}import vweb{% endif %}
 | 
			
		||||
@@ -0,0 +1,11 @@
 | 
			
		||||
module main
 | 
			
		||||
 | 
			
		||||
import freeflowuniverse.crystallib.context
 | 
			
		||||
 | 
			
		||||
pub fn {{function_name}}(ctx context.Context, circleguid int, {{rest_server_generator.get_method_params(method_params)}}) !{{method_result}}{
 | 
			
		||||
    // context allows us to see who the user is and which groups the user is
 | 
			
		||||
    // context also gives a logging feature
 | 
			
		||||
    // context is linked to 1 circle
 | 
			
		||||
    // context is linked to a DB (OSIS)
 | 
			
		||||
    return 0
 | 
			
		||||
}
 | 
			
		||||
@@ -0,0 +1,28 @@
 | 
			
		||||
pub fn {{variable_name}}_get_internal(db &backend.Indexer, circleguid int, id string) !{{type_name}}{
 | 
			
		||||
    json_str := db.get(id, RootObject{
 | 
			
		||||
        name: '{{type_name}}'
 | 
			
		||||
    })!
 | 
			
		||||
    
 | 
			
		||||
    return json.decode({{type_name}}, json_str)!
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
pub fn {{variable_name}}_set_internal(db &backend.Indexer, circleguid int, id string, {{variable_name}} {{type_name}}) !{
 | 
			
		||||
    if id != ''{
 | 
			
		||||
        db.set(RootObject{
 | 
			
		||||
            id: id
 | 
			
		||||
            name: '{{type_name}}'
 | 
			
		||||
        })!
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    db.new(RootObject{
 | 
			
		||||
        name: '{{type_name}}'
 | 
			
		||||
    })!
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
pub fn {{variable_name}}_delete_internal(db &backend.Indexer, circleguid int, id string) !{
 | 
			
		||||
    db.delete(id, RootObject{
 | 
			
		||||
        name: '{{type_name}}'
 | 
			
		||||
    })!
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@@ -0,0 +1,73 @@
 | 
			
		||||
module main
 | 
			
		||||
 | 
			
		||||
import freeflowuniverse.crystallib.baobab.backend
 | 
			
		||||
import freeflowuniverse.crystallib.clients.redisclient
 | 
			
		||||
import net.http
 | 
			
		||||
 | 
			
		||||
struct RPCServer {
 | 
			
		||||
pub mut:
 | 
			
		||||
    backend &backend.Backend
 | 
			
		||||
    redis &redisclient.Redis
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
fn main() {
 | 
			
		||||
    rpc_server := new_server() or{
 | 
			
		||||
        eprintln('failed to create server: ${err}')
 | 
			
		||||
        exit(1)
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    rpc_server.listen_and_serve(8000) or {
 | 
			
		||||
        eprintln('server error: ${err}')
 | 
			
		||||
        exit(1)
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
fn new_server() !RPCServer{
 | 
			
		||||
     db := start_new_backend_conn()!
 | 
			
		||||
    redis_client := redisclient.new(['localhost:6379'])!
 | 
			
		||||
    return RPCServer{
 | 
			
		||||
        backend: &db
 | 
			
		||||
        redis: &redis_client
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
fn (mut s RPCServer) listen_and_serve(port int) !{
 | 
			
		||||
    mut server := &http.Server{
 | 
			
		||||
        addr: 'localhost:${port}'
 | 
			
		||||
        handler: s.handler
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    server.listen_and_serve()!
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
fn (mut s RPCServer) handler(req http.Request) http.Response{
 | 
			
		||||
    if req.method != .post || req.url != '/'{
 | 
			
		||||
        return http.Response{
 | 
			
		||||
            status: 400
 | 
			
		||||
            status_msg: 'Bad Request. invalid method or path'
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    body := req.data
 | 
			
		||||
    id := id := jsonrpc.decode_request_id(body) or { 
 | 
			
		||||
        return http.Response(status: 400, status_msg: 'Bad Request. Cannot decode request.id ${msg}') 
 | 
			
		||||
    }
 | 
			
		||||
    method := jsonrpc.jsonrpcrequest_decode_method(body) or { 
 | 
			
		||||
        return http.Response(status: 400, status_msg: 'Bad Request. Cannot decode request.method ${msg}') 
 | 
			
		||||
    }
 | 
			
		||||
    params_str := jsonrpc.request_params(body) or {
 | 
			
		||||
        return http.Response(status: 400, status_msg: 'Bad Request. Cannot decode request.params ${msg}')
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    s.handle_rpc(id, method, params_str)
 | 
			
		||||
 | 
			
		||||
    response_body := s.redis.brpop([id]) or {
 | 
			
		||||
        return http.Response(status: 500, status_msg: 'Internal Server Error: Serer timed-out while waiting for a response')
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    return http.Response{
 | 
			
		||||
        status: 200
 | 
			
		||||
        body: response_body
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
		Reference in New Issue
	
	Block a user