implement openrpc and http interface generation for actor

This commit is contained in:
timurgordon
2025-01-03 01:48:04 -05:00
parent 6ba89a8b9c
commit 357000ef13
7 changed files with 198 additions and 59 deletions

View File

@@ -0,0 +1,22 @@
module generator
import freeflowuniverse.herolib.core.code { Folder, IFile, VFile, CodeItem, File, Function, Import, Module, Struct, CustomCode }
import freeflowuniverse.herolib.core.texttools
import freeflowuniverse.herolib.schemas.openrpc
import freeflowuniverse.herolib.baobab.specification {ActorMethod, ActorSpecification}
import os
import json
fn generate_openrpc_interface_file() !VFile {
return VFile {
name: 'interface_openrpc'
items: [CustomCode{$tmpl('./templates/interface_openrpc.v.template')}]
}
}
fn generate_http_interface_file() !VFile {
return VFile {
name: 'interface_http'
items: [CustomCode{$tmpl('./templates/interface_http.v.template')}]
}
}

View File

@@ -0,0 +1,33 @@
import os
import freeflowuniverse.herolib.baobab.actor {IActor, RunParams}
import freeflowuniverse.herolib.schemas.openapi
const openapi_spec_path = '@{dollar}{os.dir(@@FILE)}/specs/openapi.json'
const openapi_spec_json = os.read_file(openapi_spec_path) or { panic(err) }
const openapi_specification = openapi.json_decode(openapi_spec_json)!
struct @{actor_name_pascal}Actor {
actor.Actor
}
fn new() !@{actor_name_pascal}Actor {
return @{actor_name_pascal}Actor {
actor.new('@{actor_name_snake}')
}
}
pub fn run() ! {
mut a_ := new()!
mut a := IActor(a_)
a.run()!
}
pub fn run_server(params RunParams) ! {
mut a := new()!
mut server := actor.new_server(
redis_url: 'localhost:6379'
redis_queue: a.name
openapi_spec: openapi_specification
)!
server.run(params)
}

View File

@@ -0,0 +1,17 @@
import freeflowuniverse.herolib.schemas.openapi { OpenAPI }
import freeflowuniverse.herolib.baobab.actor {Client, ClientConfig}
import freeflowuniverse.herolib.schemas.openrpc { OpenRPC }
import freeflowuniverse.herolib.baobab.interfaces { HTTPServer, Context }
import veb
pub fn new_http_server() !&HTTPServer {
mut s := interfaces.new_http_server()!
mut openrpc_controller := new_openrpc_http_controller()!
s.register_controller[openrpc.HTTPController, Context]('/openrpc', mut openrpc_controller)!
return s
}
pub fn run_http_server() ! {
mut server := new_http_server()!
veb.run[HTTPServer, Context](mut server, 8082)
}

View File

@@ -0,0 +1,20 @@
import freeflowuniverse.herolib.baobab.interfaces
import freeflowuniverse.herolib.schemas.openrpc
const specification_path = os.join_path(os.dir(@@FILE), '/testdata/openrpc.json')
const specification = openrpc.new(path: specification_path)!
pub fn new_openrpc_interface() !&interfaces.OpenRPCInterface {
// create OpenRPC Handler with actor's client
client := new_client()!
return interfaces.new_openrpc_interface(client.Client)
}
// creates HTTP controller with the actor's OpenRPC Handler
// and OpenRPC Specification
pub fn new_openrpc_http_controller() !&openrpc.HTTPController {
return openrpc.new_http_controller(
specification: specification
handler: new_openrpc_interface()!
)
}

View File

@@ -1,77 +1,77 @@
module actor module interfaces
import veb import veb
import freeflowuniverse.herolib.schemas.openapi { Context, Controller, OpenAPI, Request, Response } import freeflowuniverse.herolib.schemas.openapi { Context, Controller, OpenAPI, Request, Response }
import freeflowuniverse.herolib.baobab.action { ProcedureError }
import os import os
import time import time
import json import json
import x.json2 import x.json2
import net.http import net.http
import freeflowuniverse.herolib.schemas.jsonschema import freeflowuniverse.herolib.schemas.jsonschema
import freeflowuniverse.herolib.clients.redisclient import freeflowuniverse.herolib.core.redisclient
pub struct OpenAPIProxy { // pub struct OpenAPIProxy {
client Client // pub:
specification OpenAPI // client Client
} // specification OpenAPI
// }
// creates and OpenAPI Proxy Controller // // creates and OpenAPI Proxy Controller
pub fn new_openapi_proxy(proxy OpenAPIProxy) OpenAPIProxy { // pub fn new_openapi_proxy(proxy OpenAPIProxy) OpenAPIProxy {
return proxy // return proxy
} // }
// creates and OpenAPI Proxy Controller // // creates and OpenAPI Proxy Controller
pub fn (proxy OpenAPIProxy) controller() &Controller { // pub fn (proxy OpenAPIProxy) controller() &Controller {
// Initialize the server // // Initialize the server
mut controller := &Controller{ // mut controller := &Controller{
specification: proxy.specification // specification: proxy.specification
handler: Handler{ // handler: Handler{
client: proxy.client // client: proxy.client
} // }
} // }
return controller // return controller
} // }
@[params] // @[params]
pub struct RunParams { // pub struct RunParams {
pub: // pub:
port int = 8080 // port int = 8080
} // }
fn (proxy OpenAPIProxy) run(params RunParams) { // fn (proxy OpenAPIProxy) run(params RunParams) {
mut controller := proxy.controller() // mut controller := proxy.controller()
veb.run[Controller, Context](mut controller, params.port) // veb.run[Controller, Context](mut controller, params.port)
} // }
pub struct Handler { // pub struct Handler {
pub mut: // pub mut:
client Client // client Client
} // }
fn (mut handler Handler) handle(request Request) !Response { // fn (mut handler Handler) handle(request Request) !Response {
// Convert incoming OpenAPI request to a procedure call // // Convert incoming OpenAPI request to a procedure call
call := rpc.openapi_request_to_procedure_call(request) // call := rpc.openapi_request_to_procedure_call(request)
// Process the procedure call // // Process the procedure call
procedure_response := handler.client.dialogue(call, Params{ // procedure_response := handler.client.dialogue(call, Params{
timeout: 30 // Set timeout in seconds // timeout: 30 // Set timeout in seconds
}) or { // }) or {
// Handle ProcedureError // // Handle ProcedureError
if err is ProcedureError { // if err is ProcedureError {
return Response{ // return Response{
status: http.status_from_int(err.code()) // Map ProcedureError reason to HTTP status code // status: http.status_from_int(err.code()) // Map ProcedureError reason to HTTP status code
body: json.encode({ // body: json.encode({
'error': err.msg() // 'error': err.msg()
}) // })
} // }
} // }
return error('Unexpected error: ${err}') // return error('Unexpected error: ${err}')
} // }
// Convert returned procedure response to OpenAPI response // // Convert returned procedure response to OpenAPI response
return Response{ // return Response{
status: http.Status.ok // Assuming success if no error // status: http.Status.ok // Assuming success if no error
body: procedure_response.result // body: procedure_response.result
} // }
} // }

View File

@@ -1,5 +1,9 @@
module interfaces module interfaces
import freeflowuniverse.herolib.baobab.actor {Client}
import freeflowuniverse.herolib.schemas.jsonrpc
import freeflowuniverse.herolib.baobab.actions
// handler for test echoes JSONRPC Request as JSONRPC Response // handler for test echoes JSONRPC Request as JSONRPC Response
fn handler(request jsonrpc.Request) !jsonrpc.Response { fn handler(request jsonrpc.Request) !jsonrpc.Response {
return jsonrpc.Response { return jsonrpc.Response {

View File

@@ -0,0 +1,43 @@
module interfaces
import freeflowuniverse.herolib.schemas.openapi { OpenAPI }
import freeflowuniverse.herolib.baobab.actor {Client, ClientConfig}
import freeflowuniverse.herolib.schemas.openrpc { OpenRPC }
import veb
pub struct HTTPServer {
veb.Controller
}
pub struct Context {
veb.Context
}
pub struct HTTPServerConfig {
ClientConfig
pub:
openapi_specification OpenAPI
openrpc_specification OpenRPC
}
pub fn new_http_server() !&HTTPServer {
mut s := &HTTPServer{}
// client := actor.new_client(cfg.ClientConfig)!
// openapi_proxy := new_openapi_proxy(
// client: new_client(cfg.ClientConfig)!
// specification: cfg.openapi_spec
// )
// mut openrpc_controller := openrpc.new_http_controller(
// specification: cfg.openrpc_specification
// handler: new_openrpc_interface(client)
// )
// s.register_controller[openrpc.HTTPController, Context]('/openrpc', mut openrpc_controller)!
return s
}
pub fn (mut server HTTPServer) run() {
veb.run[HTTPServer, Context](mut server, 8082)
}