minor warning and compilation bug fixes
This commit is contained in:
@@ -2,12 +2,9 @@ module generator
|
|||||||
|
|
||||||
import freeflowuniverse.herolib.core.code { Param, Folder, IFile, VFile, CodeItem, File, Function, Import, Module, Struct, CustomCode }
|
import freeflowuniverse.herolib.core.code { Param, Folder, IFile, VFile, CodeItem, File, Function, Import, Module, Struct, CustomCode }
|
||||||
import freeflowuniverse.herolib.core.texttools
|
import freeflowuniverse.herolib.core.texttools
|
||||||
import freeflowuniverse.herolib.schemas.openrpc
|
|
||||||
import freeflowuniverse.herolib.schemas.jsonschema.codegen as jsonschema_codegen {schemaref_to_type}
|
import freeflowuniverse.herolib.schemas.jsonschema.codegen as jsonschema_codegen {schemaref_to_type}
|
||||||
import freeflowuniverse.herolib.schemas.openrpc.codegen {content_descriptor_to_parameter}
|
import freeflowuniverse.herolib.schemas.openrpc.codegen {content_descriptor_to_parameter}
|
||||||
import freeflowuniverse.herolib.baobab.specification {ActorMethod, ActorSpecification}
|
import freeflowuniverse.herolib.baobab.specification {ActorMethod, ActorSpecification}
|
||||||
import os
|
|
||||||
import json
|
|
||||||
|
|
||||||
pub fn generate_client_file(spec ActorSpecification) !VFile {
|
pub fn generate_client_file(spec ActorSpecification) !VFile {
|
||||||
actor_name_snake := texttools.name_fix_snake(spec.name)
|
actor_name_snake := texttools.name_fix_snake(spec.name)
|
||||||
|
|||||||
@@ -33,7 +33,6 @@ pub fn generate_cmd_function(spec ActorSpecification) string {
|
|||||||
name: '${actor_name_snake}'
|
name: '${actor_name_snake}'
|
||||||
usage: ''
|
usage: ''
|
||||||
description: '${spec.description}'
|
description: '${spec.description}'
|
||||||
execute: cmd_execute
|
|
||||||
}
|
}
|
||||||
"
|
"
|
||||||
|
|
||||||
@@ -72,7 +71,7 @@ pub fn generate_method_cmd_function(actor_name string, method ActorMethod) strin
|
|||||||
'result := ${actor_name_snake}.${method_name_snake}()!'
|
'result := ${actor_name_snake}.${method_name_snake}()!'
|
||||||
}
|
}
|
||||||
return '
|
return '
|
||||||
fn cmd_${method_name_snake}(cmd Command) ! {
|
fn cmd_${method_name_snake}_execute(cmd Command) ! {
|
||||||
${method_call}
|
${method_call}
|
||||||
}
|
}
|
||||||
'
|
'
|
||||||
|
|||||||
@@ -2,11 +2,7 @@ module generator
|
|||||||
|
|
||||||
import freeflowuniverse.herolib.core.code { Folder, IFile, VFile, CodeItem, File, Function, Param, Import, Module, Struct, CustomCode }
|
import freeflowuniverse.herolib.core.code { Folder, IFile, VFile, CodeItem, File, Function, Param, Import, Module, Struct, CustomCode }
|
||||||
import freeflowuniverse.herolib.core.texttools
|
import freeflowuniverse.herolib.core.texttools
|
||||||
import freeflowuniverse.herolib.schemas.openrpc
|
|
||||||
import freeflowuniverse.herolib.schemas.openrpc.codegen {content_descriptor_to_parameter}
|
|
||||||
import freeflowuniverse.herolib.baobab.specification {ActorMethod, ActorSpecification}
|
import freeflowuniverse.herolib.baobab.specification {ActorMethod, ActorSpecification}
|
||||||
import os
|
|
||||||
import json
|
|
||||||
|
|
||||||
pub fn generate_model_file(spec ActorSpecification) !VFile {
|
pub fn generate_model_file(spec ActorSpecification) !VFile {
|
||||||
actor_name_snake := texttools.name_fix_snake(spec.name)
|
actor_name_snake := texttools.name_fix_snake(spec.name)
|
||||||
|
|||||||
@@ -2,12 +2,8 @@ module generator
|
|||||||
|
|
||||||
import json
|
import json
|
||||||
import freeflowuniverse.herolib.core.code { VFile, File, Function, Module, Struct }
|
import freeflowuniverse.herolib.core.code { VFile, File, Function, Module, Struct }
|
||||||
import freeflowuniverse.herolib.core.pathlib
|
|
||||||
import freeflowuniverse.herolib.core.texttools
|
|
||||||
import freeflowuniverse.herolib.baobab.specification {ActorSpecification}
|
|
||||||
import freeflowuniverse.herolib.schemas.openrpc { Components, OpenRPC }
|
import freeflowuniverse.herolib.schemas.openrpc { Components, OpenRPC }
|
||||||
import freeflowuniverse.herolib.schemas.openrpc.codegen { generate_client_file, generate_client_test_file }
|
import freeflowuniverse.herolib.schemas.openrpc.codegen { generate_client_file, generate_client_test_file }
|
||||||
import freeflowuniverse.herolib.schemas.jsonschema { SchemaRef }
|
|
||||||
|
|
||||||
pub fn generate_openrpc_file(spec OpenRPC) !File {
|
pub fn generate_openrpc_file(spec OpenRPC) !File {
|
||||||
return File {
|
return File {
|
||||||
|
|||||||
@@ -4,9 +4,6 @@ import freeflowuniverse.herolib.core.redisclient
|
|||||||
import freeflowuniverse.herolib.schemas.openapi
|
import freeflowuniverse.herolib.schemas.openapi
|
||||||
|
|
||||||
const name = '@{actor_name_snake}'
|
const name = '@{actor_name_snake}'
|
||||||
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)!
|
|
||||||
|
|
||||||
@@[heap]
|
@@[heap]
|
||||||
struct @{actor_name_pascal}Actor {
|
struct @{actor_name_pascal}Actor {
|
||||||
|
|||||||
@@ -4,7 +4,6 @@ import freeflowuniverse.herolib.core.code { VFile, CustomCode, Function, Import,
|
|||||||
import freeflowuniverse.herolib.baobab.specification {BaseObject}
|
import freeflowuniverse.herolib.baobab.specification {BaseObject}
|
||||||
import rand
|
import rand
|
||||||
import freeflowuniverse.herolib.core.texttools
|
import freeflowuniverse.herolib.core.texttools
|
||||||
import os
|
|
||||||
|
|
||||||
// generate_object_methods generates CRUD actor methods for a provided structure
|
// generate_object_methods generates CRUD actor methods for a provided structure
|
||||||
pub fn generate_object_test_code(actor Struct, object BaseObject) !VFile {
|
pub fn generate_object_test_code(actor Struct, object BaseObject) !VFile {
|
||||||
|
|||||||
@@ -3,7 +3,6 @@ module specification
|
|||||||
import freeflowuniverse.herolib.schemas.openrpc {OpenRPC, Components}
|
import freeflowuniverse.herolib.schemas.openrpc {OpenRPC, Components}
|
||||||
import freeflowuniverse.herolib.schemas.jsonschema {SchemaRef}
|
import freeflowuniverse.herolib.schemas.jsonschema {SchemaRef}
|
||||||
import freeflowuniverse.herolib.schemas.jsonschema.codegen { struct_to_schema }
|
import freeflowuniverse.herolib.schemas.jsonschema.codegen { struct_to_schema }
|
||||||
import freeflowuniverse.herolib.schemas.openapi {OpenAPI}
|
|
||||||
|
|
||||||
// pub fn from_openrpc(spec openrpc.OpenRPC) !ActorSpecification {
|
// pub fn from_openrpc(spec openrpc.OpenRPC) !ActorSpecification {
|
||||||
// // Extract Actor metadata from OpenRPC info
|
// // Extract Actor metadata from OpenRPC info
|
||||||
|
|||||||
@@ -7,7 +7,7 @@ pub mut:
|
|||||||
id string
|
id string
|
||||||
name string
|
name string
|
||||||
priority int = 10 // 0 is highest, do 10 as default
|
priority int = 10 // 0 is highest, do 10 as default
|
||||||
params string
|
params string // json encoded params
|
||||||
result string // can be used to remember outputs
|
result string // can be used to remember outputs
|
||||||
// run bool = true // certain actions can be defined but meant to be executed directly
|
// run bool = true // certain actions can be defined but meant to be executed directly
|
||||||
comments string
|
comments string
|
||||||
|
|||||||
@@ -199,6 +199,6 @@ fn json_decode_content(content_ map[string]MediaType, content_map map[string]Any
|
|||||||
// return arr
|
// return arr
|
||||||
// }
|
// }
|
||||||
|
|
||||||
fn (o OpenAPI) json_encode() string {
|
pub fn (o OpenAPI) encode_json() string {
|
||||||
return json.encode(o).replace('ref', '\$ref')
|
return json.encode(o).replace('ref', '\$ref')
|
||||||
}
|
}
|
||||||
@@ -1,214 +0,0 @@
|
|||||||
module openapi
|
|
||||||
|
|
||||||
import veb
|
|
||||||
import freeflowuniverse.herolib.schemas.jsonschema {Schema}
|
|
||||||
import x.json2 {Any}
|
|
||||||
import net.http
|
|
||||||
|
|
||||||
pub struct Controller {
|
|
||||||
pub:
|
|
||||||
specification OpenAPI
|
|
||||||
pub mut:
|
|
||||||
handler IHandler
|
|
||||||
}
|
|
||||||
|
|
||||||
pub struct Context {
|
|
||||||
veb.Context
|
|
||||||
}
|
|
||||||
|
|
||||||
// Matches a request path against OpenAPI path templates in the parsed structs
|
|
||||||
// Returns the matching path key and corresponding PathItem if found
|
|
||||||
fn match_path(req_path string, spec OpenAPI) !PathItem {
|
|
||||||
// Iterate through all paths in the OpenAPI specification
|
|
||||||
for template, path_item in spec.paths {
|
|
||||||
if is_path_match(req_path, template) {
|
|
||||||
// Return the matching path template and its PathItem
|
|
||||||
return path_item
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// If no match is found, return an error
|
|
||||||
return error('Path not found')
|
|
||||||
}
|
|
||||||
|
|
||||||
// Checks if a request path matches a given OpenAPI path template
|
|
||||||
// Allows for dynamic path segments like `{petId}` in templates
|
|
||||||
fn is_path_match(req_path string, template string) bool {
|
|
||||||
// Split the request path and template into segments
|
|
||||||
req_segments := req_path.split('/')
|
|
||||||
template_segments := template.split('/')
|
|
||||||
|
|
||||||
// If the number of segments doesn't match, the paths can't match
|
|
||||||
if req_segments.len != template_segments.len {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
// Compare each segment in the template and request path
|
|
||||||
for i, segment in template_segments {
|
|
||||||
// If the segment is not dynamic (doesn't start with `{`), ensure it matches exactly
|
|
||||||
if !segment.starts_with('{') && segment != req_segments[i] {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// If all segments match or dynamic segments are valid, return true
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
|
|
||||||
@['/:path...'; get; post; put; delete; patch]
|
|
||||||
pub fn (mut server Controller) index(mut ctx Context, path string) veb.Result {
|
|
||||||
println('Requested path: $path')
|
|
||||||
|
|
||||||
// Extract the HTTP method
|
|
||||||
method := ctx.req.method.str().to_lower()
|
|
||||||
|
|
||||||
// Matches the request path against the OpenAPI specification and retrieves the corresponding PathItem
|
|
||||||
path_item := match_path(path, server.specification) or {
|
|
||||||
// Return a 404 error if no matching path is found
|
|
||||||
return ctx.not_found()
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
// // Check if the path exists in the OpenAPI specification
|
|
||||||
// path_item := server.specification.paths[path] or {
|
|
||||||
// // Return a 404 error if the path is not defined
|
|
||||||
// return ctx.not_found()
|
|
||||||
// }
|
|
||||||
|
|
||||||
// Match the HTTP method with the OpenAPI specification
|
|
||||||
operation := match method {
|
|
||||||
'get' { path_item.get }
|
|
||||||
'post' { path_item.post }
|
|
||||||
'put' { path_item.put }
|
|
||||||
'delete' { path_item.delete }
|
|
||||||
'patch' { path_item.patch }
|
|
||||||
else {
|
|
||||||
// Return 405 Method Not Allowed if the method is not supported
|
|
||||||
return ctx.method_not_allowed()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
mut arg_map := map[string]Any
|
|
||||||
path_arg := path.all_after_last('/')
|
|
||||||
// the OpenAPI Parameter specification belonging to the path argument
|
|
||||||
arg_params := operation.parameters.filter(it.in_ == 'path')
|
|
||||||
if arg_params.len > 1 {
|
|
||||||
// TODO: use path template to support multiple arguments (right now just last arg supported)
|
|
||||||
panic('implement')
|
|
||||||
} else if arg_params.len == 1 {
|
|
||||||
arg_map[arg_params[0].name] = arg_params[0].typed(path_arg)
|
|
||||||
}
|
|
||||||
|
|
||||||
mut parameters := ctx.query.clone()
|
|
||||||
// Build the Request object
|
|
||||||
request := Request{
|
|
||||||
path: path
|
|
||||||
operation: operation
|
|
||||||
method: method
|
|
||||||
arguments: arg_map
|
|
||||||
parameters: parameters
|
|
||||||
body: ctx.req.data
|
|
||||||
header: ctx.req.header
|
|
||||||
}
|
|
||||||
|
|
||||||
// Use the handler to process the request
|
|
||||||
response := server.handler.handle(request) or {
|
|
||||||
// Use OpenAPI spec to determine the response status for the error
|
|
||||||
return ctx.handle_error(operation.responses, err)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Return the response to the client
|
|
||||||
ctx.res.set_status(response.status)
|
|
||||||
|
|
||||||
// ctx.res.header = response.header
|
|
||||||
// ctx.set_content_type('application/json')
|
|
||||||
|
|
||||||
// return ctx.ok('[]')
|
|
||||||
return ctx.send_response_to_client('application/json', response.body)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Handles errors and maps them to OpenAPI-defined response statuses
|
|
||||||
fn (mut ctx Context) handle_error(possible_responses map[string]ResponseSpec, err IError) veb.Result {
|
|
||||||
// Match the error with the defined responses
|
|
||||||
for code, _ in possible_responses {
|
|
||||||
if matches_error_to_status(err, code.int()) {
|
|
||||||
ctx.res.set_status(http.status_from_int(code.int()))
|
|
||||||
ctx.set_content_type('application/json')
|
|
||||||
return ctx.send_response_to_client(
|
|
||||||
'application/json',
|
|
||||||
'{"error": "$err.msg()", "status": $code}'
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Default to 500 Internal Controller Error if no match is found
|
|
||||||
ctx.res.set_status(.internal_server_error)
|
|
||||||
ctx.set_content_type('application/json')
|
|
||||||
return ctx.send_response_to_client(
|
|
||||||
'application/json',
|
|
||||||
'{"error": "Internal Controller Error", "status": 500}'
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Helper to match an error to a specific response status
|
|
||||||
fn matches_error_to_status(err IError, status int) bool {
|
|
||||||
// This can be customized to map specific errors to statuses
|
|
||||||
// For simplicity, we'll use a direct comparison here.
|
|
||||||
return err.code() == status
|
|
||||||
}
|
|
||||||
|
|
||||||
// Helper for 405 Method Not Allowed response
|
|
||||||
fn (mut ctx Context) method_not_allowed() veb.Result {
|
|
||||||
ctx.res.set_status(.method_not_allowed)
|
|
||||||
ctx.set_content_type('application/json')
|
|
||||||
return ctx.send_response_to_client(
|
|
||||||
'application/json',
|
|
||||||
'{"error": "Method Not Allowed", "status": 405}'
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn (param Parameter) typed(value string) Any {
|
|
||||||
param_schema := param.schema as Schema
|
|
||||||
param_type := param_schema.typ
|
|
||||||
param_format := param_schema.format
|
|
||||||
|
|
||||||
// Convert parameter value to corresponding type
|
|
||||||
typ := match param_type {
|
|
||||||
'integer' {
|
|
||||||
param_format
|
|
||||||
}
|
|
||||||
'number' {
|
|
||||||
param_format
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
param_type // Leave as param type for unknown types
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return typed(value, typ)
|
|
||||||
}
|
|
||||||
|
|
||||||
// typed gets a value that is string and a desired type, and returns the typed string in Any Type.
|
|
||||||
pub fn typed(value string, typ string) Any {
|
|
||||||
match typ {
|
|
||||||
'int32' {
|
|
||||||
return value.int() // Convert to int
|
|
||||||
}
|
|
||||||
'int64' {
|
|
||||||
return value.i64() // Convert to i64
|
|
||||||
}
|
|
||||||
'string' {
|
|
||||||
return value // Already a string
|
|
||||||
}
|
|
||||||
'boolean' {
|
|
||||||
return value.bool() // Convert to bool
|
|
||||||
}
|
|
||||||
'float' {
|
|
||||||
return value.f32() // Convert to float
|
|
||||||
}
|
|
||||||
'double' {
|
|
||||||
return value.f64() // Convert to double
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
return value.f64() // Leave as string for unknown types
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -16,12 +16,12 @@ import freeflowuniverse.herolib.schemas.openrpc {OpenRPC}
|
|||||||
|
|
||||||
|
|
||||||
pub fn generate_module(o OpenRPC, receiver Struct, methods_map map[string]Function, objects_map map[string]Struct) !Module {
|
pub fn generate_module(o OpenRPC, receiver Struct, methods_map map[string]Function, objects_map map[string]Struct) !Module {
|
||||||
openrpc_json := o.encode()!
|
// openrpc_json := o.encode()!
|
||||||
openrpc_file := File{
|
// openrpc_file := File{
|
||||||
name: 'openrpc'
|
// name: 'openrpc'
|
||||||
extension: 'json'
|
// extension: 'json'
|
||||||
content: openrpc_json
|
// content: openrpc_json
|
||||||
}
|
// }
|
||||||
|
|
||||||
client_file := generate_client_file(o, objects_map)!
|
client_file := generate_client_file(o, objects_map)!
|
||||||
client_test_file := generate_client_test_file(o, methods_map, objects_map)!
|
client_test_file := generate_client_test_file(o, methods_map, objects_map)!
|
||||||
|
|||||||
@@ -39,12 +39,12 @@ pub fn (mut c HTTPController) run(params RunParams) {
|
|||||||
pub fn (mut c HTTPController) index(mut ctx Context) veb.Result {
|
pub fn (mut c HTTPController) index(mut ctx Context) veb.Result {
|
||||||
// Decode JSONRPC Request from POST data
|
// Decode JSONRPC Request from POST data
|
||||||
request := jsonrpc.decode_request(ctx.req.data) or {
|
request := jsonrpc.decode_request(ctx.req.data) or {
|
||||||
return ctx.server_error('Failed to decode JSONRPC Request ${err.msg}')
|
return ctx.server_error('Failed to decode JSONRPC Request ${err.msg()}')
|
||||||
}
|
}
|
||||||
|
|
||||||
// Process the JSONRPC request with the OpenRPC handler
|
// Process the JSONRPC request with the OpenRPC handler
|
||||||
response := c.handler.handle(request) or {
|
response := c.handler.handle(request) or {
|
||||||
return ctx.server_error('Handler error: ${err.msg}')
|
return ctx.server_error('Handler error: ${err.msg()}')
|
||||||
}
|
}
|
||||||
|
|
||||||
// Encode and return the handler's JSONRPC Response
|
// Encode and return the handler's JSONRPC Response
|
||||||
|
|||||||
@@ -1,8 +1,5 @@
|
|||||||
module openrpc
|
module openrpc
|
||||||
|
|
||||||
import veb
|
|
||||||
import freeflowuniverse.herolib.schemas.jsonrpc
|
|
||||||
|
|
||||||
// Main controller for handling RPC requests
|
// Main controller for handling RPC requests
|
||||||
pub struct WebSocketController {
|
pub struct WebSocketController {
|
||||||
pub mut:
|
pub mut:
|
||||||
|
|||||||
@@ -1,6 +1,5 @@
|
|||||||
module openrpc
|
module openrpc
|
||||||
|
|
||||||
import x.json2
|
|
||||||
import freeflowuniverse.herolib.schemas.jsonrpc
|
import freeflowuniverse.herolib.schemas.jsonrpc
|
||||||
|
|
||||||
pub struct Handler {
|
pub struct Handler {
|
||||||
|
|||||||
Reference in New Issue
Block a user