...
This commit is contained in:
@@ -0,0 +1,9 @@
|
||||
pub enum {{ type_name }}{
|
||||
{% for elem in enum -%}
|
||||
{% if is_integer -%}
|
||||
{{ number_to_words(elem) }} = {{ elem }}
|
||||
{% else -%}
|
||||
{{ elem }}
|
||||
{% endif -%}
|
||||
{% endfor %}
|
||||
}
|
@@ -0,0 +1,77 @@
|
||||
pub struct {{ actor_executor_name }}{
|
||||
pub mut:
|
||||
db &backend.Backend
|
||||
redis &redisclient.Redis
|
||||
}
|
||||
|
||||
pub fn (mut executor {{ actor_executor_name }}) execute(rpc_msg_id string, rpc_msg_method string, rpc_msg_params_str string) {
|
||||
raw_params := json2.raw_decode(rpc_msg_params_str) or{
|
||||
executor.return_error(rpc_msg_id, jsonrpc.invalid_params)
|
||||
return
|
||||
}
|
||||
|
||||
params_arr := raw_params.arr()
|
||||
|
||||
match rpc_msg_method {
|
||||
{%- for method in methods %}
|
||||
'{{method.name}}' {
|
||||
{%- for param in method.params %}
|
||||
{%- if generator.is_primitive(generator.get_param_type(method.name, param))%}
|
||||
{{param.name}} := params_arr[{{loop.index0}}] as {{generator.get_param_type(method.name, param)}}
|
||||
{%- else %}
|
||||
{{param.name}} := json.decode({{generator.get_param_type(method.name, param)}}, params_arr[{{loop.index0}}].json_str()) or {
|
||||
executor.return_error(rpc_msg_id, jsonrpc.invalid_request)
|
||||
return
|
||||
}
|
||||
{%- endif %}
|
||||
{%- endfor %}
|
||||
|
||||
{%- if generator.get_method_return_type(method) == 'none' %}
|
||||
executor.{{method.name}}_internal({{generator.get_method_params_as_args(method)}}) or {
|
||||
executor.return_error(rpc_msg_id, jsonrpc.InnerJsonRpcError{
|
||||
code: 32000
|
||||
message: '${err}'
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
response := jsonrpc.JsonRpcResponse[string]{
|
||||
jsonrpc: '2.0.0'
|
||||
id: rpc_msg_id
|
||||
result: ''
|
||||
}
|
||||
{%- else %}
|
||||
result := executor.{{method.name}}_internal({{generator.get_method_params_as_args(method)}}) or {
|
||||
executor.return_error(rpc_msg_id, jsonrpc.InnerJsonRpcError{
|
||||
code: 32000
|
||||
message: '${err}'
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
response := jsonrpc.JsonRpcResponse[{{generator.get_method_return_type(method)}}]{
|
||||
jsonrpc: '2.0.0'
|
||||
id: rpc_msg_id
|
||||
result: result
|
||||
}
|
||||
{%- endif %}
|
||||
|
||||
// put response in response queue
|
||||
executor.redis.lpush(rpc_msg_id, response.to_json()) or {
|
||||
println('failed to push response for ${rpc_msg_id} to redis queue: ${err}')
|
||||
}
|
||||
}
|
||||
{%- endfor %}
|
||||
else {
|
||||
executor.return_error(rpc_msg_id, jsonrpc.method_not_found)
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn (mut executor {{actor_executor_name}}) return_error(rpc_msg_id string, error jsonrpc.InnerJsonRpcError){
|
||||
response := jsonrpc.new_jsonrpcerror(rpc_msg_id, error)
|
||||
executor.redis.lpush(rpc_msg_id, response.to_json()) or {
|
||||
println('failed to push response for ${rpc_msg_id} to redis queue: ${err}')
|
||||
}
|
||||
}
|
@@ -0,0 +1,50 @@
|
||||
struct Handler {
|
||||
pub mut:
|
||||
db &backend.Backend
|
||||
redis &redisclient.Redis
|
||||
{% for actor in actors %}
|
||||
{{actor}}_executor {{get_actor_executor_name(actor)}}
|
||||
{%- endfor %}
|
||||
}
|
||||
|
||||
pub fn new(db_config backend.BackendConfig, redis_addr string) !Handler{
|
||||
db := backend.new(db_config)!
|
||||
mut redis_client := redisclient.new([redis_addr])!
|
||||
redis_client.selectdb(0)!
|
||||
|
||||
return Handler{
|
||||
db: &db
|
||||
redis: &redis_client
|
||||
{%- for actor in actors %}
|
||||
{{actor}}_executor: {{get_actor_executor_name(actor)}}{
|
||||
db: &db
|
||||
redis: &redis_client
|
||||
}
|
||||
{%- endfor %}
|
||||
}
|
||||
}
|
||||
|
||||
// handle handles an incoming JSON-RPC encoded message and returns an encoded response
|
||||
pub fn (mut handler Handler) handle(id string, method string, params_str string) {
|
||||
actor := method.all_before('.')
|
||||
method_name := method.all_after('.')
|
||||
|
||||
match actor {
|
||||
{%- for actor in actors %}
|
||||
'{{ actor }}' {
|
||||
spawn (&handler.{{actor}}_executor).execute(id, method_name, params_str)
|
||||
}
|
||||
{%- endfor %}
|
||||
else {
|
||||
handler.return_error(id, jsonrpc.method_not_found)
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn (mut handler Handler) return_error(rpc_msg_id string, error jsonrpc.InnerJsonRpcError){
|
||||
response := jsonrpc.new_jsonrpcerror(rpc_msg_id, error)
|
||||
handler.redis.lpush(rpc_msg_id, response.to_json()) or {
|
||||
println('failed to push response for ${rpc_msg_id} to redis queue: ${err}')
|
||||
}
|
||||
}
|
@@ -0,0 +1,31 @@
|
||||
module myhandler
|
||||
|
||||
import x.json2
|
||||
import rand
|
||||
import freeflowuniverse.crystallib.baobab.backend
|
||||
|
||||
fn test_handler(){
|
||||
db_config := backend.BackendConfig{
|
||||
name: 'myhandler'
|
||||
secret: 'secret'
|
||||
reset: true
|
||||
db_type: .postgres
|
||||
}
|
||||
|
||||
mut handler := new(db_config, '127.0.0.1:6379')!
|
||||
{% for method_name in method_names %}
|
||||
do_request(mut handler, '{{method_name}}')!
|
||||
{%- endfor %}
|
||||
}
|
||||
|
||||
fn do_request(mut handler Handler, method_name string) ! {
|
||||
// TODO: edit input parameters
|
||||
mut params := []json2.Any{}
|
||||
params << "objid"
|
||||
params << "blabla_name"
|
||||
params_str := json2.Any(params).json_str()
|
||||
|
||||
id := rand.string(6)
|
||||
handler.handle(rand.string(6), method_name, json2.Any(params).json_str())
|
||||
println('request id: ${id}')
|
||||
}
|
@@ -0,0 +1,7 @@
|
||||
pub fn (mut executor {{ actor_executor_name }}) {{function_name}}({{method_params}}) !{{return_type}}{
|
||||
// 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)
|
||||
panic('implement')
|
||||
}
|
@@ -0,0 +1,28 @@
|
||||
pub fn (mut executor {{ actor_executor_name }}) {{variable_name}}_get_internal(id string) !{{type_name}}{
|
||||
json_str := executor.db.indexer.get_json(id, backend.RootObject{
|
||||
name: '{{type_name}}'
|
||||
})!
|
||||
|
||||
return json.decode({{type_name}}, json_str)!
|
||||
}
|
||||
|
||||
pub fn (mut executor {{ actor_executor_name }}) {{variable_name}}_set_internal({{variable_name}} {{type_name}}) !{
|
||||
if {{variable_name}}.oid != ''{
|
||||
executor.db.indexer.set(backend.RootObject{
|
||||
id: {{variable_name}}.oid
|
||||
name: '{{type_name}}'
|
||||
})!
|
||||
}
|
||||
|
||||
executor.db.indexer.new(backend.RootObject{
|
||||
name: '{{type_name}}'
|
||||
})!
|
||||
}
|
||||
|
||||
pub fn (mut executor {{ actor_executor_name }}) {{variable_name}}_delete_internal(id string) !{
|
||||
executor.db.indexer.delete(id, backend.RootObject{
|
||||
name: '{{type_name}}'
|
||||
})!
|
||||
}
|
||||
|
||||
|
@@ -0,0 +1,5 @@
|
||||
pub struct {{method_param_struct_name}}{
|
||||
{% for param_name, param_type in params.items()%}
|
||||
{{param_name}} {{param_type}}
|
||||
{%- endfor %}
|
||||
}
|
@@ -0,0 +1,75 @@
|
||||
{% if method_example -%}
|
||||
/*
|
||||
Example:
|
||||
{{ method_example }}
|
||||
*/
|
||||
{% endif -%}
|
||||
|
||||
{% if method_description -%}
|
||||
/*
|
||||
{{ method_description }}
|
||||
*/
|
||||
{% endif -%}
|
||||
pub fn {{ function_name }}({{ vlang_code_generator.get_method_params(method_params) }}) {{ method_result }}{
|
||||
mut conn := httpconnection.new(
|
||||
name: 'openrpc_client'
|
||||
url: '{{ base_url }}'
|
||||
)!
|
||||
|
||||
mut params := map[string]json2.Any{}
|
||||
{% for param_name, param_type in method_params.items() -%}
|
||||
{% if vlang_code_generator.is_primitive(param_type) %}
|
||||
params["{{ param_name }}"] = {{ param_name }}
|
||||
{% elif vlang_code_generator.is_vlang_array(param_type) %}
|
||||
mut any_arr := []json2.Any{}
|
||||
for item in {{ param_name }}{
|
||||
{% if vlang_code_generator.is_primitive(param_type[2:]) %}
|
||||
any_arr << item
|
||||
{% else %}
|
||||
any_arr << json2.raw_decode(json2.encode(item))!
|
||||
{% endif %}
|
||||
}
|
||||
params["{{ param_name }}"] = json2.Any(any_arr)
|
||||
{%else %}
|
||||
params["{{ param_name }}"] = json2.raw_decode(json2.encode({{ param_name }}))!
|
||||
{% endif %}
|
||||
{% endfor -%}
|
||||
|
||||
mut payload := map[string]json2.Any{}
|
||||
payload['jsonrpc'] = "2.0"
|
||||
payload['id'] = 0
|
||||
payload['method'] = '{{ method_name }}'
|
||||
payload['params'] = params
|
||||
|
||||
response := conn.send(method: .post, data: json2.encode(payload){% if url_path -%}, prefix: '{{ url_path }}' {% endif -%})!
|
||||
if !response.is_ok() {
|
||||
return error('failed to make rpc request: (${response.code}) ${response.data}')
|
||||
}
|
||||
|
||||
{% if return_type != 'none' %}
|
||||
mp := json2.raw_decode(response.data)!.as_map()
|
||||
res := mp['result'] or {
|
||||
return error('invalid jsonrpc result: ${response.data}')
|
||||
}
|
||||
|
||||
if res is json2.Null{
|
||||
return error('not found')
|
||||
}
|
||||
|
||||
{% if vlang_code_generator.is_primitive(return_type) %}
|
||||
return res as {{return_type}}
|
||||
{% elif vlang_code_generator.is_vlang_array(return_type) %}
|
||||
mut res_arr := {{return_type}}
|
||||
for item in res.arr() {
|
||||
{% if vlang_code_generator.is_primitive(return_type[2:]) %}
|
||||
res_arr << item as {{return_type}}
|
||||
{% else %}
|
||||
res_arr << json2.decode[{{return_type[2:]}}](item.json_str())!
|
||||
{% endif %}
|
||||
}
|
||||
return res_arr
|
||||
{%else %}
|
||||
return json2.decode[{{return_type}}](res.json_str())!
|
||||
{% endif -%}
|
||||
{% endif %}
|
||||
}
|
@@ -0,0 +1,5 @@
|
||||
module {{module_name}}
|
||||
{% for item in imports %}
|
||||
import {{item}}
|
||||
{%- endfor %}
|
||||
|
@@ -0,0 +1,10 @@
|
||||
@[params]
|
||||
pub struct {{ struct_name }}{
|
||||
pub mut:
|
||||
{%- for property_name, property_info in properties.items() %}
|
||||
{%- if property_info.description %}
|
||||
// {{ property_info.description }}
|
||||
{%- endif %}
|
||||
{{ property_name }} {{ property_info.type_name }}
|
||||
{%- endfor %}
|
||||
}
|
231
_archive/openapi/generator/server/vlang/vlang.py
Normal file
231
_archive/openapi/generator/server/vlang/vlang.py
Normal file
@@ -0,0 +1,231 @@
|
||||
from openapi_python_client.schema import OpenAPI, Schema, Reference
|
||||
from jinja2 import Environment, FileSystemLoader
|
||||
from typing import Dict, Any
|
||||
|
||||
import os
|
||||
|
||||
|
||||
script_dir = os.path.dirname(os.path.abspath(__file__))
|
||||
env = Environment(loader=FileSystemLoader(script_dir))
|
||||
|
||||
class VlangCodeGenerator:
|
||||
def __init__(self, python_code: OpenAPI, output_dir: str) -> None:
|
||||
self.python_code = python_code
|
||||
self.output_dir = output_dir
|
||||
self.struct_template = env.get_template("templates/struct.jinja")
|
||||
|
||||
def generate(self):
|
||||
"""
|
||||
Main generation method to create V code.
|
||||
"""
|
||||
# Ensure the output directory exists
|
||||
os.makedirs(self.output_dir, exist_ok=True)
|
||||
|
||||
structs = self._generate_structs()
|
||||
print('structs: ', structs)
|
||||
# methods = self._generate_methods()
|
||||
|
||||
# # Combine structs and methods into one file
|
||||
vlang_code = structs
|
||||
output_file = f"{self.output_dir}/generated.v"
|
||||
|
||||
# Write to file
|
||||
with open(output_file, "w") as file:
|
||||
file.write(vlang_code)
|
||||
print(f"Vlang code generated at {output_file}")
|
||||
|
||||
def _generate_struct(self, struct_name: str, scheme: Schema | Reference) -> str:
|
||||
properties = {}
|
||||
code = ""
|
||||
|
||||
for field_name, field in scheme.properties.items(): # type: ignore
|
||||
v_type = self._convert_type(field.type) # type: ignore
|
||||
|
||||
if field.type == 'object': # type: ignore
|
||||
# Capitalize each part of the field name and create a nested struct name
|
||||
nested_struct_name = ''.join(part.capitalize() for part in field_name.split("_"))
|
||||
|
||||
# Generate the struct for the nested object
|
||||
code += self._generate_struct(struct_name=nested_struct_name, scheme=field)
|
||||
|
||||
# Update v_type to the newly generated nested struct name
|
||||
v_type = nested_struct_name
|
||||
|
||||
# Update the properties dictionary with type name and description
|
||||
properties[field_name] = {
|
||||
'type_name': v_type,
|
||||
'description': field.description # type: ignore
|
||||
}
|
||||
|
||||
code += "\n"
|
||||
code += self.struct_template.render(
|
||||
struct_name=struct_name,
|
||||
properties= properties # type: ignore
|
||||
)
|
||||
code += "\n"
|
||||
|
||||
return code
|
||||
|
||||
def _generate_structs(self) -> str:
|
||||
"""
|
||||
Generate V structs from OpenAPI components with support for nested objects and arrays.
|
||||
"""
|
||||
if not self.python_code.components:
|
||||
raise ValueError("No components found in spec")
|
||||
|
||||
if not self.python_code.components.schemas:
|
||||
raise ValueError("No schemas found in components")
|
||||
|
||||
code = ""
|
||||
|
||||
for struct_name, schema in self.python_code.components.schemas.items():
|
||||
code += self._generate_struct(struct_name=struct_name, scheme=schema)
|
||||
|
||||
return code
|
||||
|
||||
# structs_code = []
|
||||
# for schema_name, schema in self.python_code.components.schemas.items():
|
||||
# fields = []
|
||||
# for field_name, field in schema.properties.items(): # type: ignore
|
||||
# if field.type == "object": # type: ignore
|
||||
# # Generate a nested struct
|
||||
# parts = field_name.split("_")
|
||||
# nested_struct_name = ""
|
||||
# for part in parts:
|
||||
# nested_struct_name += part.capitalize()
|
||||
# nested_struct = self._generate_struct_from_object(nested_struct_name, field) # type: ignore
|
||||
# structs_code.append(nested_struct)
|
||||
# fields.append(f"\t{field_name} {nested_struct_name}")
|
||||
# print(f"Generated struct for {nested_struct_name}")
|
||||
# elif field.type == "array": # type: ignore
|
||||
# # Handle arrays with proper type conversion for items
|
||||
# item_type = self._convert_type(field.items.type) # type: ignore
|
||||
# fields.append(f"\t{field_name} []{item_type}")
|
||||
# else:
|
||||
# # Convert JSON schema type to V type
|
||||
# v_type = self._convert_type(field.type) # type: ignore
|
||||
# fields.append(f"\t{field_name} {v_type}")
|
||||
|
||||
# # Construct struct
|
||||
# struct_code = f"pub struct {schema_name} {{\n" + "\n".join(fields) + "\n}"
|
||||
# structs_code.append(struct_code)
|
||||
# print(f"Generated struct for {schema_name}")
|
||||
|
||||
# return "\n\n".join(structs_code)
|
||||
|
||||
# def _generate_struct_from_object(self, struct_name: str, schema: dict) -> str:
|
||||
# """
|
||||
# Generate a nested struct from an object schema.
|
||||
# """
|
||||
# fields = []
|
||||
# for field_name, field in schema.properties.items(): # type: ignore
|
||||
# v_type = self._convert_type(field.type) # type: ignore
|
||||
# fields.append(f"\t{field_name} {v_type}")
|
||||
|
||||
# return f"struct {struct_name} {{\n" + "\n".join(fields) + "\n}"
|
||||
|
||||
# def _generate_methods(self) -> str:
|
||||
# """
|
||||
# Generate V methods based on OpenAPI paths and operations.
|
||||
# """
|
||||
# if not self.python_code.paths:
|
||||
# raise ValueError("No paths found in spec")
|
||||
|
||||
# methods_code = []
|
||||
# for path, path_item in self.python_code.paths.items():
|
||||
# # Explicitly check for HTTP method attributes in PathItem
|
||||
# for http_method in ["get", "post", "put", "delete", "patch", "options", "head"]:
|
||||
# operation = getattr(path_item, http_method, None)
|
||||
# if operation:
|
||||
# # Generate method name and parameters
|
||||
# method_name = self._generate_method_name(http_method, path)
|
||||
# parameters = self._generate_method_parameters(operation.parameters)
|
||||
# request_body = self._generate_request_body(operation.request_body)
|
||||
# response_type = self._generate_response_type(operation.responses)
|
||||
|
||||
# # Combine method arguments
|
||||
# method_arguments = parameters
|
||||
# if request_body:
|
||||
# method_arguments += f", {request_body}" if parameters else request_body
|
||||
|
||||
# # Generate the method code
|
||||
# method_code = f"fn {method_name}({method_arguments}) {response_type} {{\n"
|
||||
# method_code += f"\t// TODO: Implement the {http_method.upper()} request to {path}\n"
|
||||
# method_code += "\t// Use the generated structs for request/response bodies\n"
|
||||
# method_code += "}\n"
|
||||
# methods_code.append(method_code)
|
||||
|
||||
# print(f"Generated method for {http_method.upper()} {path}")
|
||||
|
||||
# return "\n\n".join(methods_code)
|
||||
|
||||
# def _generate_method_name(self, http_method: str, path: str) -> str:
|
||||
# """
|
||||
# Generate a method name from the HTTP method and path.
|
||||
# """
|
||||
# # Remove leading/trailing slashes and replace `/` with `_`
|
||||
# sanitized_path = path.strip("/").replace("/", "_").replace("{", "").replace("}", "")
|
||||
# return f"{http_method.lower()}_{sanitized_path}"
|
||||
|
||||
# def _generate_method_parameters(self, parameters) -> str:
|
||||
# if not parameters:
|
||||
# return ""
|
||||
|
||||
# param_list = []
|
||||
# for param in parameters:
|
||||
# param_name = param.name
|
||||
# param_schema = getattr(param, "schema", None)
|
||||
# print('param_name: ', param_name)
|
||||
# print('param_schema: ', param_schema)
|
||||
# # if param_schema and param_schema.type:
|
||||
# # param_type = self._convert_type(param_schema.type)
|
||||
# # param_list.append(f"{param_name} {param_type}")
|
||||
|
||||
# return ", ".join(param_list)
|
||||
|
||||
|
||||
# def _generate_request_body(self, request_body) -> str:
|
||||
# """
|
||||
# Generate a function parameter for the request body if present.
|
||||
# """
|
||||
# if not request_body or not request_body.content:
|
||||
# return ""
|
||||
|
||||
# # Assume application/json content type
|
||||
# json_schema = request_body.content.get("application/json")
|
||||
# if not json_schema or not json_schema.schema:
|
||||
# return ""
|
||||
|
||||
# print('body_type: ', json_schema)
|
||||
# # body_type = json_schema.schema.ref.split("/")[-1] # Extract the schema name
|
||||
# return f"body {json_schema}"
|
||||
|
||||
# def _generate_response_type(self, responses) -> str:
|
||||
# """
|
||||
# Determine the return type of the method based on responses.
|
||||
# """
|
||||
# if not responses:
|
||||
# return "void"
|
||||
|
||||
# for status_code, response in responses.items():
|
||||
# if response.content and "application/json" in response.content:
|
||||
# json_schema = response.content["application/json"].schema
|
||||
# print('json_schema: ', json_schema)
|
||||
# # if json_schema and json_schema.ref:
|
||||
# # return json_schema.ref.split("/")[-1] # Extract schema name
|
||||
|
||||
# return "void"
|
||||
|
||||
def _convert_type(self, json_type: str) -> str:
|
||||
"""
|
||||
Map JSON schema types to Vlang types.
|
||||
"""
|
||||
type_mapping = {
|
||||
"string": "string",
|
||||
"integer": "int",
|
||||
"number": "f64",
|
||||
"boolean": "bool",
|
||||
"array": "[]",
|
||||
}
|
||||
return type_mapping.get(json_type, "string") # Default to `string`
|
||||
|
Reference in New Issue
Block a user