baobab fixes to better support openapi codegen
This commit is contained in:
68
lib/baobab/actor/client.v
Normal file
68
lib/baobab/actor/client.v
Normal file
@@ -0,0 +1,68 @@
|
||||
module actor
|
||||
|
||||
import json
|
||||
import freeflowuniverse.herolib.clients.redisclient
|
||||
import freeflowuniverse.herolib.baobab.action { ProcedureCall, ProcedureResponse }
|
||||
|
||||
// Processor struct for managing procedure calls
|
||||
pub struct Client {
|
||||
pub mut:
|
||||
rpc redisclient.RedisRpc // Redis RPC mechanism
|
||||
}
|
||||
|
||||
// Parameters for processing a procedure call
|
||||
@[params]
|
||||
pub struct Params {
|
||||
pub:
|
||||
timeout int = 60 // Timeout in seconds
|
||||
}
|
||||
|
||||
pub struct ClientConfig {
|
||||
pub:
|
||||
redis_url string // url to redis server running
|
||||
redis_queue string // name of redis queue
|
||||
}
|
||||
|
||||
pub fn new_client(config ClientConfig) !Client {
|
||||
mut redis := redisclient.new(config.redis_url)!
|
||||
mut rpc_q := redis.rpc_get(config.redis_queue)
|
||||
|
||||
return Client{
|
||||
rpc: rpc_q
|
||||
}
|
||||
}
|
||||
|
||||
// Process the procedure call
|
||||
pub fn (mut p Client) monologue(call ProcedureCall, params Params) ! {
|
||||
// Use RedisRpc's `call` to send the call and wait for the response
|
||||
response_data := p.rpc.call(redisclient.RPCArgs{
|
||||
cmd: call.method
|
||||
data: call.params
|
||||
timeout: u64(params.timeout * 1000) // Convert seconds to milliseconds
|
||||
wait: true
|
||||
})!
|
||||
// TODO: check error type
|
||||
}
|
||||
|
||||
// Process the procedure call
|
||||
pub fn (mut p Client) call_to_action(action Procedure, params Params) !ProcedureResponse {
|
||||
// Use RedisRpc's `call` to send the call and wait for the response
|
||||
response_data := p.rpc.call(redisclient.RPCArgs{
|
||||
cmd: call.method
|
||||
data: call.params
|
||||
timeout: u64(params.timeout * 1000) // Convert seconds to milliseconds
|
||||
wait: true
|
||||
}) or {
|
||||
// TODO: check error type
|
||||
return ProcedureResponse{
|
||||
error: err.msg()
|
||||
}
|
||||
// return ProcedureError{
|
||||
// reason: .timeout
|
||||
// }
|
||||
}
|
||||
|
||||
return ProcedureResponse{
|
||||
result: response_data
|
||||
}
|
||||
}
|
||||
@@ -31,18 +31,18 @@ fn openapi_param_to_example(param Parameter) ?Example {
|
||||
}
|
||||
|
||||
// Helper function: Convert OpenAPI operation to ActorMethod
|
||||
fn openapi_operation_to_actor_method(op Operation, method_name string, path string) ActorMethod {
|
||||
fn openapi_operation_to_actor_method(info openapi.OperationInfo) ActorMethod {
|
||||
mut parameters := []ContentDescriptor{}
|
||||
mut example_parameters:= []Example{}
|
||||
|
||||
for param in op.parameters {
|
||||
for param in info.operation.parameters {
|
||||
parameters << openapi_param_to_content_descriptor(param)
|
||||
example_parameters << openapi_param_to_example(param) or {
|
||||
continue
|
||||
}
|
||||
}
|
||||
|
||||
response_200 := op.responses['200'].content['application/json']
|
||||
response_200 := info.operation.responses['200'].content['application/json']
|
||||
|
||||
mut result := ContentDescriptor{
|
||||
name: "result",
|
||||
@@ -66,7 +66,7 @@ fn openapi_operation_to_actor_method(op Operation, method_name string, path stri
|
||||
} else {ExamplePairing{}}
|
||||
|
||||
mut errors := []ErrorSpec{}
|
||||
for status, response in op.responses {
|
||||
for status, response in info.operation.responses {
|
||||
if status.int() >= 400 {
|
||||
error_schema := if response.content.len > 0 {
|
||||
response.content.values()[0].schema
|
||||
@@ -80,9 +80,9 @@ fn openapi_operation_to_actor_method(op Operation, method_name string, path stri
|
||||
}
|
||||
|
||||
return ActorMethod{
|
||||
name: method_name,
|
||||
description: op.description,
|
||||
summary: op.summary,
|
||||
name: info.operation.operation_id,
|
||||
description: info.operation.description,
|
||||
summary: info.operation.summary,
|
||||
parameters: parameters,
|
||||
example: pairing
|
||||
result: result,
|
||||
@@ -99,37 +99,39 @@ fn openapi_schema_to_struct(name string, schema SchemaRef) Struct {
|
||||
}
|
||||
|
||||
// Converts OpenAPI to ActorSpecification
|
||||
pub fn from_openapi(spec OpenAPI) !ActorSpecification {
|
||||
mut methods := []ActorMethod{}
|
||||
pub fn from_openapi(spec_raw OpenAPI) !ActorSpecification {
|
||||
spec := openapi.process(spec_raw)!
|
||||
mut objects := []BaseObject{}
|
||||
|
||||
// get all operations for path as list of tuple [](path_string, http.Method, openapi.Operation)
|
||||
|
||||
// Extract methods from OpenAPI paths
|
||||
for path, item in spec.paths {
|
||||
if item.get.operation_id != '' {
|
||||
methods << openapi_operation_to_actor_method(item.get, item.get.operation_id, path)
|
||||
}
|
||||
if item.post.operation_id != '' {
|
||||
methods << openapi_operation_to_actor_method(item.post, item.post.operation_id, path)
|
||||
}
|
||||
if item.put.operation_id != '' {
|
||||
methods << openapi_operation_to_actor_method(item.put, item.put.operation_id, path)
|
||||
}
|
||||
if item.delete.operation_id != '' {
|
||||
methods << openapi_operation_to_actor_method(item.delete, item.delete.operation_id, path)
|
||||
}
|
||||
if item.patch.operation_id != '' {
|
||||
methods << openapi_operation_to_actor_method(item.patch, item.patch.operation_id, path)
|
||||
}
|
||||
if item.head.operation_id != '' {
|
||||
methods << openapi_operation_to_actor_method(item.head, item.head.operation_id, path)
|
||||
}
|
||||
if item.options.operation_id != '' {
|
||||
methods << openapi_operation_to_actor_method(item.options, item.options.operation_id, path)
|
||||
}
|
||||
if item.trace.operation_id != '' {
|
||||
methods << openapi_operation_to_actor_method(item.trace, item.trace.operation_id, path)
|
||||
}
|
||||
}
|
||||
// for path, item in spec.paths {
|
||||
// if item.get.operation_id != '' {
|
||||
// methods << openapi_operation_to_actor_method(item.get, item.get.operation_id, path)
|
||||
// }
|
||||
// if item.post.operation_id != '' {
|
||||
// methods << openapi_operation_to_actor_method(item.post, item.post.operation_id, path)
|
||||
// }
|
||||
// if item.put.operation_id != '' {
|
||||
// methods << openapi_operation_to_actor_method(item.put, item.put.operation_id, path)
|
||||
// }
|
||||
// if item.delete.operation_id != '' {
|
||||
// methods << openapi_operation_to_actor_method(item.delete, item.delete.operation_id, path)
|
||||
// }
|
||||
// if item.patch.operation_id != '' {
|
||||
// methods << openapi_operation_to_actor_method(item.patch, item.patch.operation_id, path)
|
||||
// }
|
||||
// if item.head.operation_id != '' {
|
||||
// methods << openapi_operation_to_actor_method(item.head, item.head.operation_id, path)
|
||||
// }
|
||||
// if item.options.operation_id != '' {
|
||||
// methods << openapi_operation_to_actor_method(item.options, item.options.operation_id, path)
|
||||
// }
|
||||
// if item.trace.operation_id != '' {
|
||||
// methods << openapi_operation_to_actor_method(item.trace, item.trace.operation_id, path)
|
||||
// }
|
||||
// }
|
||||
|
||||
// Extract objects from OpenAPI components.schemas
|
||||
for name, schema in spec.components.schemas {
|
||||
@@ -137,11 +139,12 @@ pub fn from_openapi(spec OpenAPI) !ActorSpecification {
|
||||
}
|
||||
|
||||
return ActorSpecification{
|
||||
openapi: spec_raw
|
||||
name: spec.info.title,
|
||||
description: spec.info.description,
|
||||
structure: Struct{}, // Assuming no top-level structure for this use case
|
||||
interfaces: [.openapi], // Default to OpenAPI for input
|
||||
methods: methods,
|
||||
methods: spec.get_operations().map(openapi_operation_to_actor_method(it)),
|
||||
objects: objects,
|
||||
}
|
||||
}
|
||||
@@ -88,7 +88,7 @@ pub fn from_openrpc(spec OpenRPC) !ActorSpecification {
|
||||
objects << BaseObject{
|
||||
schema: Schema {...schema,
|
||||
title: texttools.name_fix_pascal(key)
|
||||
id: texttools.name_fix_snake(key)
|
||||
id: texttools.snake_case(key)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,11 +1,14 @@
|
||||
module specification
|
||||
|
||||
import freeflowuniverse.herolib.core.code { Struct, Function }
|
||||
import freeflowuniverse.herolib.schemas.openapi
|
||||
import freeflowuniverse.herolib.schemas.openrpc {ExamplePairing, ContentDescriptor, ErrorSpec}
|
||||
import freeflowuniverse.herolib.schemas.jsonschema {Schema, Reference}
|
||||
|
||||
pub struct ActorSpecification {
|
||||
pub mut:
|
||||
openapi ?openapi.OpenAPI
|
||||
openrpc ?openrpc.OpenRPC
|
||||
name string @[omitempty]
|
||||
description string @[omitempty]
|
||||
structure Struct @[omitempty]
|
||||
|
||||
@@ -1,34 +1,25 @@
|
||||
module specification
|
||||
|
||||
import freeflowuniverse.herolib.schemas.jsonschema { Schema, SchemaRef }
|
||||
import freeflowuniverse.herolib.schemas.openapi { Operation, Parameter, OpenAPI, Components, Info, PathItem, ServerSpec }
|
||||
import freeflowuniverse.herolib.schemas.openapi { MediaType, ResponseSpec, Operation, Parameter, OpenAPI, Components, Info, PathItem, ServerSpec }
|
||||
import net.http
|
||||
|
||||
// Converts ActorSpecification to OpenAPI
|
||||
pub fn (s ActorSpecification) to_openapi() OpenAPI {
|
||||
if openapi_spec := s.openapi {
|
||||
return openapi_spec
|
||||
}
|
||||
mut paths := map[string]PathItem{}
|
||||
|
||||
// Map ActorMethods to paths
|
||||
for method in s.methods {
|
||||
mut op := Operation{
|
||||
summary: method.summary,
|
||||
description: method.description,
|
||||
operation_id: method.name,
|
||||
op := method.to_openapi_operation()
|
||||
paths['${method.http_path()}'] = match method.http_method() {
|
||||
.get { PathItem {get: op} }
|
||||
else { panic('unsupported http method') }
|
||||
}
|
||||
|
||||
// Convert parameters to OpenAPI format
|
||||
for param in method.parameters {
|
||||
op.parameters << Parameter{
|
||||
name: param.name,
|
||||
in_: 'query', // Default to query parameters; adjust based on function context
|
||||
description: param.description,
|
||||
required: param.required,
|
||||
schema: param.schema,
|
||||
}
|
||||
}
|
||||
|
||||
// Assign operation to corresponding HTTP method
|
||||
// TODO: what about other verbs
|
||||
paths['/${method.name}'] = PathItem{get: op}
|
||||
}
|
||||
|
||||
mut schemas := map[string]SchemaRef{}
|
||||
@@ -60,3 +51,41 @@ pub fn (s ActorSpecification) to_openapi() OpenAPI {
|
||||
fn (bo BaseObject) to_schema() Schema {
|
||||
return Schema{}
|
||||
}
|
||||
|
||||
fn (m ActorMethod) http_path() string {
|
||||
return m.name
|
||||
}
|
||||
|
||||
fn (m ActorMethod) http_method() http.Method {
|
||||
return .get
|
||||
}
|
||||
|
||||
fn (method ActorMethod) to_openapi_operation() Operation {
|
||||
mut op := Operation{
|
||||
summary: method.summary,
|
||||
description: method.description,
|
||||
operation_id: method.name,
|
||||
}
|
||||
|
||||
// Convert parameters to OpenAPI format
|
||||
for param in method.parameters {
|
||||
op.parameters << Parameter{
|
||||
name: param.name,
|
||||
in_: 'query', // Default to query parameters; adjust based on function context
|
||||
description: param.description,
|
||||
required: param.required,
|
||||
schema: param.schema,
|
||||
}
|
||||
}
|
||||
|
||||
// if method.is_void()
|
||||
op.responses['200'] = ResponseSpec {
|
||||
description: method.description
|
||||
content: {
|
||||
'application/json': MediaType {
|
||||
schema: method.result.schema
|
||||
}
|
||||
}
|
||||
}
|
||||
return op
|
||||
}
|
||||
159
lib/baobab/specification/to_openapi_test.v
Normal file
159
lib/baobab/specification/to_openapi_test.v
Normal file
@@ -0,0 +1,159 @@
|
||||
module specification
|
||||
|
||||
import freeflowuniverse.herolib.core.code
|
||||
import freeflowuniverse.herolib.schemas.jsonschema { Schema, SchemaRef }
|
||||
import freeflowuniverse.herolib.schemas.openapi { Operation, Parameter, OpenAPI, Components, Info, PathItem, ServerSpec }
|
||||
import freeflowuniverse.herolib.schemas.openrpc
|
||||
|
||||
|
||||
const actor_spec = specification.ActorSpecification{
|
||||
name: 'Petstore'
|
||||
structure: code.Struct{
|
||||
is_pub: false
|
||||
}
|
||||
interfaces: [.openrpc]
|
||||
methods: [specification.ActorMethod{
|
||||
name: 'list_pets'
|
||||
summary: 'List all pets'
|
||||
parameters: [openrpc.ContentDescriptor{
|
||||
name: 'limit'
|
||||
description: 'How many items to return at one time (max 100)'
|
||||
required: false
|
||||
schema: jsonschema.SchemaRef(jsonschema.Schema{
|
||||
typ: 'integer'
|
||||
minimum: 1
|
||||
})
|
||||
}]
|
||||
result: openrpc.ContentDescriptor{
|
||||
name: 'pets'
|
||||
description: 'A paged array of pets'
|
||||
schema: jsonschema.SchemaRef(jsonschema.Schema{
|
||||
typ: 'array'
|
||||
items: jsonschema.Items(jsonschema.SchemaRef(jsonschema.Schema{
|
||||
typ: 'object'
|
||||
properties: {
|
||||
'id': jsonschema.SchemaRef(jsonschema.Reference{
|
||||
ref: '#/components/schemas/PetId'
|
||||
}),
|
||||
'name': jsonschema.SchemaRef(jsonschema.Schema{
|
||||
typ: 'string'
|
||||
}),
|
||||
'tag': jsonschema.SchemaRef(jsonschema.Schema{
|
||||
typ: 'string'
|
||||
})
|
||||
}
|
||||
required: ['id', 'name']
|
||||
}))
|
||||
})
|
||||
}
|
||||
}, specification.ActorMethod{
|
||||
name: 'create_pet'
|
||||
summary: 'Create a pet'
|
||||
parameters: [openrpc.ContentDescriptor{
|
||||
name: 'newPetName'
|
||||
description: 'Name of pet to create'
|
||||
required: true
|
||||
schema: jsonschema.SchemaRef(jsonschema.Schema{
|
||||
typ: 'string'
|
||||
})
|
||||
}, openrpc.ContentDescriptor{
|
||||
name: 'newPetTag'
|
||||
description: 'Pet tag to create'
|
||||
schema: jsonschema.SchemaRef(jsonschema.Schema{
|
||||
typ: 'string'
|
||||
})
|
||||
}]
|
||||
}, specification.ActorMethod{
|
||||
name: 'get_pet'
|
||||
summary: 'Info for a specific pet'
|
||||
result: openrpc.ContentDescriptor{
|
||||
name: 'pet'
|
||||
description: 'Expected response to a valid request'
|
||||
schema: jsonschema.SchemaRef(jsonschema.Schema{
|
||||
typ: 'object'
|
||||
properties: {
|
||||
'id': jsonschema.SchemaRef(jsonschema.Reference{
|
||||
ref: '#/components/schemas/PetId'
|
||||
}),
|
||||
'name': jsonschema.SchemaRef(jsonschema.Schema{
|
||||
typ: 'string'
|
||||
}),
|
||||
'tag': jsonschema.SchemaRef(jsonschema.Schema{
|
||||
typ: 'string'
|
||||
})
|
||||
}
|
||||
required: ['id', 'name']
|
||||
})
|
||||
}
|
||||
}, specification.ActorMethod{
|
||||
name: 'update_pet'
|
||||
summary: 'Update a pet'
|
||||
parameters: [openrpc.ContentDescriptor{
|
||||
name: 'updatedPetName'
|
||||
description: 'New name for the pet'
|
||||
required: true
|
||||
schema: jsonschema.SchemaRef(jsonschema.Schema{
|
||||
typ: 'string'
|
||||
})
|
||||
}, openrpc.ContentDescriptor{
|
||||
name: 'updatedPetTag'
|
||||
description: 'New tag for the pet'
|
||||
schema: jsonschema.SchemaRef(jsonschema.Schema{
|
||||
typ: 'string'
|
||||
})
|
||||
}]
|
||||
result: openrpc.ContentDescriptor{
|
||||
name: 'pet'
|
||||
description: 'Updated pet object'
|
||||
schema: jsonschema.SchemaRef(jsonschema.Schema{
|
||||
typ: 'object'
|
||||
properties: {
|
||||
'id': jsonschema.SchemaRef(jsonschema.Reference{
|
||||
ref: '#/components/schemas/PetId'
|
||||
}),
|
||||
'name': jsonschema.SchemaRef(jsonschema.Schema{
|
||||
typ: 'string'
|
||||
}),
|
||||
'tag': jsonschema.SchemaRef(jsonschema.Schema{
|
||||
typ: 'string'
|
||||
})
|
||||
}
|
||||
required: ['id', 'name']
|
||||
})
|
||||
}
|
||||
}, specification.ActorMethod{
|
||||
name: 'delete_pet'
|
||||
summary: 'Delete a pet'
|
||||
result: openrpc.ContentDescriptor{
|
||||
name: 'success'
|
||||
description: 'Boolean indicating success'
|
||||
schema: jsonschema.SchemaRef(jsonschema.Schema{
|
||||
typ: 'boolean'
|
||||
})
|
||||
}
|
||||
}]
|
||||
objects: [specification.BaseObject{
|
||||
schema: jsonschema.Schema{
|
||||
id: 'pet'
|
||||
title: 'Pet'
|
||||
typ: 'object'
|
||||
properties: {
|
||||
'id': jsonschema.SchemaRef(jsonschema.Reference{
|
||||
ref: '#/components/schemas/PetId'
|
||||
}),
|
||||
'name': jsonschema.SchemaRef(jsonschema.Schema{
|
||||
typ: 'string'
|
||||
}),
|
||||
'tag': jsonschema.SchemaRef(jsonschema.Schema{
|
||||
typ: 'string'
|
||||
})
|
||||
}
|
||||
required: ['id', 'name']
|
||||
}
|
||||
}]
|
||||
}
|
||||
|
||||
// Converts ActorSpecification to OpenAPI
|
||||
pub fn test_specification_to_openapi() {
|
||||
panic(actor_spec.to_openapi())
|
||||
}
|
||||
@@ -12,6 +12,7 @@ mut:
|
||||
pub struct Actor {
|
||||
pub:
|
||||
name string
|
||||
redis_url string = 'localhost:6379'
|
||||
mut:
|
||||
osis OSIS
|
||||
}
|
||||
|
||||
@@ -18,7 +18,12 @@ pub fn new_openapi_interface(client Client) &OpenAPIInterface {
|
||||
pub fn (mut i OpenAPIInterface) handle(request openapi.Request) !openapi.Response {
|
||||
// Convert incoming OpenAPI request to a procedure call
|
||||
action := action_from_openapi_request(request)
|
||||
response := i.client.call_to_action(action)!
|
||||
println('debugzo3 ${action}')
|
||||
response := i.client.call_to_action(action) or {
|
||||
println('debugzo3.5 ${err.msg()}')
|
||||
return err
|
||||
}
|
||||
println('debugzo4 ${response}')
|
||||
return action_to_openapi_response(response)
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user