diff --git a/lib/baobab/generator/generate_clients.v b/lib/baobab/generator/generate_clients.v index 2798448b..16010bd4 100644 --- a/lib/baobab/generator/generate_clients.v +++ b/lib/baobab/generator/generate_clients.v @@ -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.texttools -import freeflowuniverse.herolib.schemas.openrpc 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.baobab.specification {ActorMethod, ActorSpecification} -import os -import json pub fn generate_client_file(spec ActorSpecification) !VFile { actor_name_snake := texttools.name_fix_snake(spec.name) diff --git a/lib/baobab/generator/generate_command.v b/lib/baobab/generator/generate_command.v index 62e45a70..2eff7ec4 100644 --- a/lib/baobab/generator/generate_command.v +++ b/lib/baobab/generator/generate_command.v @@ -33,7 +33,6 @@ pub fn generate_cmd_function(spec ActorSpecification) string { name: '${actor_name_snake}' usage: '' 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}()!' } return ' - fn cmd_${method_name_snake}(cmd Command) ! { + fn cmd_${method_name_snake}_execute(cmd Command) ! { ${method_call} } ' diff --git a/lib/baobab/generator/generate_model.v b/lib/baobab/generator/generate_model.v index 20dc2557..09f4bb55 100644 --- a/lib/baobab/generator/generate_model.v +++ b/lib/baobab/generator/generate_model.v @@ -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.texttools -import freeflowuniverse.herolib.schemas.openrpc -import freeflowuniverse.herolib.schemas.openrpc.codegen {content_descriptor_to_parameter} import freeflowuniverse.herolib.baobab.specification {ActorMethod, ActorSpecification} -import os -import json pub fn generate_model_file(spec ActorSpecification) !VFile { actor_name_snake := texttools.name_fix_snake(spec.name) diff --git a/lib/baobab/generator/generate_openrpc.v b/lib/baobab/generator/generate_openrpc.v index a6e67f6a..c5d9dbb8 100644 --- a/lib/baobab/generator/generate_openrpc.v +++ b/lib/baobab/generator/generate_openrpc.v @@ -2,12 +2,8 @@ module generator import json 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.codegen { generate_client_file, generate_client_test_file } -import freeflowuniverse.herolib.schemas.jsonschema { SchemaRef } pub fn generate_openrpc_file(spec OpenRPC) !File { return File { diff --git a/lib/baobab/generator/templates/actor.v.template b/lib/baobab/generator/templates/actor.v.template index 1393d3e5..dcfa6675 100644 --- a/lib/baobab/generator/templates/actor.v.template +++ b/lib/baobab/generator/templates/actor.v.template @@ -4,9 +4,6 @@ import freeflowuniverse.herolib.core.redisclient import freeflowuniverse.herolib.schemas.openapi 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] struct @{actor_name_pascal}Actor { diff --git a/lib/baobab/generator/write_object_tests.v b/lib/baobab/generator/write_object_tests.v index 47d19f08..8dfe4ba5 100644 --- a/lib/baobab/generator/write_object_tests.v +++ b/lib/baobab/generator/write_object_tests.v @@ -4,7 +4,6 @@ import freeflowuniverse.herolib.core.code { VFile, CustomCode, Function, Import, import freeflowuniverse.herolib.baobab.specification {BaseObject} import rand import freeflowuniverse.herolib.core.texttools -import os // generate_object_methods generates CRUD actor methods for a provided structure pub fn generate_object_test_code(actor Struct, object BaseObject) !VFile { diff --git a/lib/baobab/specification/to_openrpc.v b/lib/baobab/specification/to_openrpc.v index 6cd7167b..e12663eb 100644 --- a/lib/baobab/specification/to_openrpc.v +++ b/lib/baobab/specification/to_openrpc.v @@ -3,7 +3,6 @@ module specification import freeflowuniverse.herolib.schemas.openrpc {OpenRPC, Components} import freeflowuniverse.herolib.schemas.jsonschema {SchemaRef} import freeflowuniverse.herolib.schemas.jsonschema.codegen { struct_to_schema } -import freeflowuniverse.herolib.schemas.openapi {OpenAPI} // pub fn from_openrpc(spec openrpc.OpenRPC) !ActorSpecification { // // Extract Actor metadata from OpenRPC info diff --git a/lib/baobab/stage/action.v b/lib/baobab/stage/action.v index 676fdc94..b2b7f6d8 100644 --- a/lib/baobab/stage/action.v +++ b/lib/baobab/stage/action.v @@ -7,7 +7,7 @@ pub mut: id string name string 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 // run bool = true // certain actions can be defined but meant to be executed directly comments string diff --git a/lib/schemas/openapi/decode.v b/lib/schemas/openapi/decode.v index 1f378c87..179326f5 100644 --- a/lib/schemas/openapi/decode.v +++ b/lib/schemas/openapi/decode.v @@ -199,6 +199,6 @@ fn json_decode_content(content_ map[string]MediaType, content_map map[string]Any // return arr // } -fn (o OpenAPI) json_encode() string { +pub fn (o OpenAPI) encode_json() string { return json.encode(o).replace('ref', '\$ref') } \ No newline at end of file diff --git a/lib/schemas/openapi/server.v b/lib/schemas/openapi/server.v deleted file mode 100644 index 0b7e2b25..00000000 --- a/lib/schemas/openapi/server.v +++ /dev/null @@ -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 - } - } -} \ No newline at end of file diff --git a/lib/schemas/openrpc/codegen/generate.v b/lib/schemas/openrpc/codegen/generate.v index 6d52aab1..47ab7171 100644 --- a/lib/schemas/openrpc/codegen/generate.v +++ b/lib/schemas/openrpc/codegen/generate.v @@ -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 { - openrpc_json := o.encode()! - openrpc_file := File{ - name: 'openrpc' - extension: 'json' - content: openrpc_json - } + // openrpc_json := o.encode()! + // openrpc_file := File{ + // name: 'openrpc' + // extension: 'json' + // content: openrpc_json + // } client_file := generate_client_file(o, objects_map)! client_test_file := generate_client_test_file(o, methods_map, objects_map)! diff --git a/lib/schemas/openrpc/controller_http.v b/lib/schemas/openrpc/controller_http.v index bdfe19bc..158aee74 100644 --- a/lib/schemas/openrpc/controller_http.v +++ b/lib/schemas/openrpc/controller_http.v @@ -39,12 +39,12 @@ pub fn (mut c HTTPController) run(params RunParams) { pub fn (mut c HTTPController) index(mut ctx Context) veb.Result { // Decode JSONRPC Request from POST data 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 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 diff --git a/lib/schemas/openrpc/controller_ws.v b/lib/schemas/openrpc/controller_ws.v index 7d70dc8b..fc230bd3 100644 --- a/lib/schemas/openrpc/controller_ws.v +++ b/lib/schemas/openrpc/controller_ws.v @@ -1,8 +1,5 @@ module openrpc -import veb -import freeflowuniverse.herolib.schemas.jsonrpc - // Main controller for handling RPC requests pub struct WebSocketController { pub mut: diff --git a/lib/schemas/openrpc/handler.v b/lib/schemas/openrpc/handler.v index c10af784..785fdc46 100644 --- a/lib/schemas/openrpc/handler.v +++ b/lib/schemas/openrpc/handler.v @@ -1,6 +1,5 @@ module openrpc -import x.json2 import freeflowuniverse.herolib.schemas.jsonrpc pub struct Handler {