Merge branch 'development' into development_fix_mcpservers

This commit is contained in:
Mahmoud-Emad
2025-09-14 14:14:29 +03:00
86 changed files with 3294 additions and 2273 deletions

View File

@@ -1,2 +0,0 @@
.example_1_actor
.example_2_actor

View File

@@ -1,19 +0,0 @@
## Blank Actor Generation Example
This example shows how to generate a blank actor (unspecified, except for name). The generated actor module contains all the boilerplate code of an actor that can be compiled but lacks ant state or methods.
Simply run:
```
chmod +x *.vsh
example_1.vsh
example_2.vsh
```
### Examples
There are two examples of blank actor generation.
- `example_1.vsh` generates the actor from a blank specification structure.
- `example_2.vsh` generates the actor from a blank OpenAPI Specification.
<!-- TODO: write below -->
Read []() to learn how actor's are generated from specifications, and how the two example's differ.

View File

@@ -1,7 +0,0 @@
#!/usr/bin/env -S v -n -w -gc none -cc tcc -d use_openssl -enable-globals run
import freeflowuniverse.herolib.hero.generation
generation.generate_actor(
name: 'Example'
)

View File

@@ -1,8 +0,0 @@
#!/usr/bin/env -S v -n -w -gc none -cc tcc -d use_openssl -enable-globals run
import freeflowuniverse.herolib.hero.generation
generation.generate_actor(
name: 'Example'
interfaces: []
)

View File

@@ -1,23 +0,0 @@
# Hero Generation Example
## Getting started
### Step 1: Generate specification
### Step 2: Generate actor from specification
The script below generates the actor's OpenAPI handler from a given OpenAPI Specification. The generated code is written to `handler.v` in the example actor's module.
`generate_actor.vsh`
### Step 3: Run actor
The script below runs the actor's Redis RPC Queue Interface and uses the generated handler function to handle incoming RPCs. The Redis Interface listens to the RPC Queue assigned to the actor.
`run_interface_procedure.vsh`
### Step 3: Run server
The script below runs the actor's RPC Queue Listener and uses the generated handler function to handle incoming RPCs.
`run_interface_openapi.vsh`

View File

@@ -1 +0,0 @@
# Example Actor

View File

@@ -1,34 +0,0 @@
module example_actor
import os
import freeflowuniverse.herolib.hero.baobab.stage { IActor, RunParams }
import freeflowuniverse.herolib.web.openapi
import time
const openapi_spec_path = '${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 ExampleActor {
stage.Actor
}
fn new() !ExampleActor {
return ExampleActor{stage.new_actor('example')}
}
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

@@ -1,15 +0,0 @@
module example_actor
const test_port = 8101
pub fn test_new() ! {
new() or { return error('Failed to create actor:\n${err}') }
}
pub fn test_run() ! {
spawn run()
}
pub fn test_run_server() ! {
spawn run_server(port: test_port)
}

View File

@@ -1,5 +0,0 @@
module example_actor
pub fn (mut a ExampleActor) handle(method string, data string) !string {
return data
}

View File

@@ -1,346 +0,0 @@
{
"openapi": "3.0.3",
"info": {
"title": "Pet Store API",
"description": "A sample API for a pet store",
"version": "1.0.0"
},
"servers": [
{
"url": "https://api.petstore.example.com/v1",
"description": "Production server"
},
{
"url": "https://staging.petstore.example.com/v1",
"description": "Staging server"
}
],
"paths": {
"/pets": {
"get": {
"summary": "List all pets",
"operationId": "listPets",
"parameters": [
{
"name": "limit",
"in": "query",
"description": "Maximum number of pets to return",
"required": false,
"schema": {
"type": "integer",
"format": "int32"
}
}
],
"responses": {
"200": {
"description": "A paginated list of pets",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/Pets"
}
}
}
},
"400": {
"description": "Invalid request"
}
}
},
"post": {
"summary": "Create a new pet",
"operationId": "createPet",
"requestBody": {
"required": true,
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/NewPet"
}
}
}
},
"responses": {
"201": {
"description": "Pet created",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/Pet"
}
}
}
},
"400": {
"description": "Invalid input"
}
}
}
},
"/pets/{petId}": {
"get": {
"summary": "Get a pet by ID",
"operationId": "getPet",
"parameters": [
{
"name": "petId",
"in": "path",
"description": "ID of the pet to retrieve",
"required": true,
"schema": {
"type": "integer",
"format": "int64"
}
}
],
"responses": {
"200": {
"description": "A pet",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/Pet"
}
}
}
},
"404": {
"description": "Pet not found"
}
}
},
"delete": {
"summary": "Delete a pet by ID",
"operationId": "deletePet",
"parameters": [
{
"name": "petId",
"in": "path",
"description": "ID of the pet to delete",
"required": true,
"schema": {
"type": "integer",
"format": "int64"
}
}
],
"responses": {
"204": {
"description": "Pet deleted"
},
"404": {
"description": "Pet not found"
}
}
}
},
"/orders": {
"get": {
"summary": "List all orders",
"operationId": "listOrders",
"responses": {
"200": {
"description": "A list of orders",
"content": {
"application/json": {
"schema": {
"type": "array",
"items": {
"$ref": "#/components/schemas/Order"
}
}
}
}
}
}
}
},
"/orders/{orderId}": {
"get": {
"summary": "Get an order by ID",
"operationId": "getOrder",
"parameters": [
{
"name": "orderId",
"in": "path",
"description": "ID of the order to retrieve",
"required": true,
"schema": {
"type": "integer",
"format": "int64"
}
}
],
"responses": {
"200": {
"description": "An order",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/Order"
}
}
}
},
"404": {
"description": "Order not found"
}
}
},
"delete": {
"summary": "Delete an order by ID",
"operationId": "deleteOrder",
"parameters": [
{
"name": "orderId",
"in": "path",
"description": "ID of the order to delete",
"required": true,
"schema": {
"type": "integer",
"format": "int64"
}
}
],
"responses": {
"204": {
"description": "Order deleted"
},
"404": {
"description": "Order not found"
}
}
}
},
"/users": {
"post": {
"summary": "Create a user",
"operationId": "createUser",
"requestBody": {
"required": true,
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/NewUser"
}
}
}
},
"responses": {
"201": {
"description": "User created",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/User"
}
}
}
}
}
}
}
},
"components": {
"schemas": {
"Pet": {
"type": "object",
"required": ["id", "name"],
"properties": {
"id": {
"type": "integer",
"format": "int64"
},
"name": {
"type": "string"
},
"tag": {
"type": "string"
}
}
},
"NewPet": {
"type": "object",
"required": ["name"],
"properties": {
"name": {
"type": "string"
},
"tag": {
"type": "string"
}
}
},
"Pets": {
"type": "array",
"items": {
"$ref": "#/components/schemas/Pet"
}
},
"Order": {
"type": "object",
"required": ["id", "petId", "quantity", "shipDate"],
"properties": {
"id": {
"type": "integer",
"format": "int64"
},
"petId": {
"type": "integer",
"format": "int64"
},
"quantity": {
"type": "integer",
"format": "int32"
},
"shipDate": {
"type": "string",
"format": "date-time"
},
"status": {
"type": "string",
"enum": ["placed", "approved", "delivered"]
},
"complete": {
"type": "boolean"
}
}
},
"User": {
"type": "object",
"required": ["id", "username"],
"properties": {
"id": {
"type": "integer",
"format": "int64"
},
"username": {
"type": "string"
},
"email": {
"type": "string"
},
"phone": {
"type": "string"
}
}
},
"NewUser": {
"type": "object",
"required": ["username"],
"properties": {
"username": {
"type": "string"
},
"email": {
"type": "string"
},
"phone": {
"type": "string"
}
}
}
}
}
}

View File

@@ -1,5 +0,0 @@
#!/usr/bin/env -S v -n -w -gc none -cc tcc -d use_openssl -enable-globals run
// import example_actor
// example_actor.run_interface_procedure()

6
examples/hero/herofs/herofs_advanced.vsh Normal file → Executable file
View File

@@ -194,11 +194,13 @@ fn main() {
// 1. Move a file to multiple directories (hard link-like behavior)
println('Moving logo.png to both images and docs directories...')
image_file = fs_factory.fs_file.get(image_file_id)!
image_file = fs_factory.fs_file.move(image_file_id, [images_dir_id, docs_dir_id])!
fs_factory.fs_file.move(image_file_id, [images_dir_id, docs_dir_id])!
image_file = fs_factory.fs_file.get(image_file_id)!
// 2. Rename a file
println('Renaming main.v to app.v...')
code_file = fs_factory.fs_file.rename(code_file_id, 'app.v')!
fs_factory.fs_file.rename(code_file_id, 'app.v')!
code_file = fs_factory.fs_file.get(code_file_id)!
// 3. Update file metadata
println('Updating file metadata...')

1
examples/hero/herorpc/.gitignore vendored Normal file
View File

@@ -0,0 +1 @@
herorpc_example

View File

@@ -0,0 +1,22 @@
#!/usr/bin/env -S v -n -w -cg -gc none -cc tcc -d use_openssl -enable-globals run
import freeflowuniverse.herolib.hero.heromodels.rpc
println('
#to test the discover function:
echo \'\{"jsonrpc":"2.0","method":"rpc.discover","params":[],"id":1\}\' \\
| nc -U /tmp/heromodels
\'
#to test interactively:
nc -U /tmp/heromodels
then e.g. do
\{"jsonrpc":"2.0","method":"comment_set","params":{"comment":"Hello world!","parent":0,"author":42},"id":1\}
needs to be on one line for openrpc to work
')
rpc.start()!

View File

@@ -1,2 +0,0 @@
actor
server

View File

@@ -1,103 +0,0 @@
# OpenAPI Server with Redis-Based RPC and Actor
This project demonstrates how to implement a system consisting of:
1. An OpenAPI Server: Handles HTTP requests and translates them into procedure calls.
2. A Redis-Based RPC Processor: Acts as the communication layer between the server and the actor.
3. An Actor: Listens for RPC requests on a Redis queue and executes predefined procedures.
## Features
• OpenAPI server to manage HTTP requests.
• Redis-based RPC mechanism for message passing.
• Actor pattern for executing and responding to RPC tasks.
## Setup Instructions
Prerequisites
• Redis installed and running on localhost:6379.
• V programming language installed.
Steps to Run
1. OpenAPI Specification
Place the OpenAPI JSON specification file at:
`data/openapi.json`
This file defines the API endpoints and their parameters.
2. Start the Redis Server
Ensure Redis is running locally:
redis-server
3. Start the OpenAPI Server
Run the OpenAPI server:
`server.vsh`
The server listens on port 8080 by default.
4. Start the Actor
Run the actor service:
`actor.vsh`
The actor listens to the procedure_queue for RPC messages.
Usage
API Endpoints
The API supports operations like:
• Create a Pet: Adds a new pet.
• List Pets: Lists all pets or limits results.
• Get Pet by ID: Fetches a specific pet by ID.
• Delete Pet: Removes a pet by ID.
• Similar operations for users and orders.
Use tools like curl, Postman, or a browser to interact with the endpoints.
Example Requests
Create a Pet
curl -X POST http://localhost:8080/pets -d '{"name": "Buddy", "tag": "dog"}' -H "Content-Type: application/json"
List Pets
curl http://localhost:8080/pets
## Code Overview
1. OpenAPI Server
• Reads the OpenAPI JSON file.
• Maps HTTP requests to procedure calls using the operation ID.
• Sends procedure calls to the Redis RPC queue.
2. Redis-Based RPC
• Implements a simple message queue using Redis.
• Encodes requests as JSON strings for transport.
3. Actor
• Listens to the procedure_queue Redis queue.
• Executes tasks like managing pets, orders, and users.
• Responds with JSON-encoded results or errors.
## Extending the System
Add New Procedures
1. Define new methods in the Actor to handle tasks.
2. Add corresponding logic in the DataStore for storage operations.
3. Update the OpenAPI JSON file to expose new endpoints.
Modify Data Models
1. Update the Pet, Order, and User structs as needed.
2. Adjust the DataStore methods to handle the changes.
Troubleshooting
• Redis Connection Issues: Ensure Redis is running and accessible on localhost:6379.
• JSON Parsing Errors: Validate the input JSON against the OpenAPI specification.

Binary file not shown.

View File

@@ -1,233 +0,0 @@
#!/usr/bin/env -S v -w -n -enable-globals run
import os
import time
import veb
import json
import x.json2
import net.http
import freeflowuniverse.herolib.web.openapi
import freeflowuniverse.herolib.hero.processor
import freeflowuniverse.herolib.core.redisclient
@[heap]
struct Actor {
mut:
rpc redisclient.RedisRpc
data_store DataStore
}
pub struct DataStore {
mut:
pets map[int]Pet
orders map[int]Order
users map[int]User
}
struct Pet {
id int
name string
tag string
}
struct Order {
id int
pet_id int
quantity int
ship_date string
status string
complete bool
}
struct User {
id int
username string
email string
phone string
}
// Entry point for the actor
fn main() {
mut redis := redisclient.new('localhost:6379') or { panic(err) }
mut rpc := redis.rpc_get('procedure_queue')
mut actor := Actor{
rpc: rpc
data_store: DataStore{}
}
actor.listen() or { panic(err) }
}
// Actor listens to the Redis queue for method invocations
fn (mut actor Actor) listen() ! {
println('Actor started and listening for tasks...')
for {
actor.rpc.process(actor.handle_method)!
time.sleep(time.millisecond * 100) // Prevent CPU spinning
}
}
// Handle method invocations
fn (mut actor Actor) handle_method(cmd string, data string) !string {
param_anys := json2.raw_decode(data)!.arr()
match cmd {
'listPets' {
pets := if param_anys.len == 0 {
actor.data_store.list_pets()
} else {
params := json.decode(ListPetParams, param_anys[0].str())!
actor.data_store.list_pets(params)
}
return json.encode(pets)
}
'createPet' {
response := if param_anys.len == 0 {
return error('at least data expected')
} else if param_anys.len == 1 {
payload := json.decode(NewPet, param_anys[0].str())!
actor.data_store.create_pet(payload)
} else {
return error('expected 1 param, found too many')
}
// data := json.decode(NewPet, data) or { return error('Invalid pet data: $err') }
// created_pet := actor.data_store.create_pet(pet)
return json.encode(response)
}
'getPet' {
response := if param_anys.len == 0 {
return error('at least data expected')
} else if param_anys.len == 1 {
payload := param_anys[0].int()
actor.data_store.get_pet(payload)!
} else {
return error('expected 1 param, found too many')
}
return json.encode(response)
}
'deletePet' {
params := json.decode(map[string]int, data) or {
return error('Invalid params: ${err}')
}
actor.data_store.delete_pet(params['petId']) or {
return error('Pet not found: ${err}')
}
return json.encode({
'message': 'Pet deleted'
})
}
'listOrders' {
orders := actor.data_store.list_orders()
return json.encode(orders)
}
'getOrder' {
params := json.decode(map[string]int, data) or {
return error('Invalid params: ${err}')
}
order := actor.data_store.get_order(params['orderId']) or {
return error('Order not found: ${err}')
}
return json.encode(order)
}
'deleteOrder' {
params := json.decode(map[string]int, data) or {
return error('Invalid params: ${err}')
}
actor.data_store.delete_order(params['orderId']) or {
return error('Order not found: ${err}')
}
return json.encode({
'message': 'Order deleted'
})
}
'createUser' {
user := json.decode(NewUser, data) or { return error('Invalid user data: ${err}') }
created_user := actor.data_store.create_user(user)
return json.encode(created_user)
}
else {
return error('Unknown method: ${cmd}')
}
}
}
@[params]
pub struct ListPetParams {
limit u32
}
// DataStore methods for managing data
fn (mut store DataStore) list_pets(params ListPetParams) []Pet {
if params.limit > 0 {
if params.limit >= store.pets.values().len {
return store.pets.values()
}
return store.pets.values()[..params.limit]
}
return store.pets.values()
}
fn (mut store DataStore) create_pet(new_pet NewPet) Pet {
id := store.pets.keys().len + 1
pet := Pet{
id: id
name: new_pet.name
tag: new_pet.tag
}
store.pets[id] = pet
return pet
}
fn (mut store DataStore) get_pet(id int) !Pet {
return store.pets[id] or { return error('Pet with id ${id} not found.') }
}
fn (mut store DataStore) delete_pet(id int) ! {
if id in store.pets {
store.pets.delete(id)
return
}
return error('Pet not found')
}
fn (mut store DataStore) list_orders() []Order {
return store.orders.values()
}
fn (mut store DataStore) get_order(id int) !Order {
return store.orders[id] or { none }
}
fn (mut store DataStore) delete_order(id int) ! {
if id in store.orders {
store.orders.delete(id)
return
}
return error('Order not found')
}
fn (mut store DataStore) create_user(new_user NewUser) User {
id := store.users.keys().len + 1
user := User{
id: id
username: new_user.username
email: new_user.email
phone: new_user.phone
}
store.users[id] = user
return user
}
// NewPet struct for creating a pet
struct NewPet {
name string
tag string
}
// NewUser struct for creating a user
struct NewUser {
username string
email string
phone string
}

View File

@@ -1,346 +0,0 @@
{
"openapi": "3.0.3",
"info": {
"title": "Pet Store API",
"description": "A sample API for a pet store",
"version": "1.0.0"
},
"servers": [
{
"url": "https://api.petstore.example.com/v1",
"description": "Production server"
},
{
"url": "https://staging.petstore.example.com/v1",
"description": "Staging server"
}
],
"paths": {
"/pets": {
"get": {
"summary": "List all pets",
"operationId": "listPets",
"parameters": [
{
"name": "limit",
"in": "query",
"description": "Maximum number of pets to return",
"required": false,
"schema": {
"type": "integer",
"format": "int32"
}
}
],
"responses": {
"200": {
"description": "A paginated list of pets",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/Pets"
}
}
}
},
"400": {
"description": "Invalid request"
}
}
},
"post": {
"summary": "Create a new pet",
"operationId": "createPet",
"requestBody": {
"required": true,
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/NewPet"
}
}
}
},
"responses": {
"201": {
"description": "Pet created",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/Pet"
}
}
}
},
"400": {
"description": "Invalid input"
}
}
}
},
"/pets/{petId}": {
"get": {
"summary": "Get a pet by ID",
"operationId": "getPet",
"parameters": [
{
"name": "petId",
"in": "path",
"description": "ID of the pet to retrieve",
"required": true,
"schema": {
"type": "integer",
"format": "int64"
}
}
],
"responses": {
"200": {
"description": "A pet",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/Pet"
}
}
}
},
"404": {
"description": "Pet not found"
}
}
},
"delete": {
"summary": "Delete a pet by ID",
"operationId": "deletePet",
"parameters": [
{
"name": "petId",
"in": "path",
"description": "ID of the pet to delete",
"required": true,
"schema": {
"type": "integer",
"format": "int64"
}
}
],
"responses": {
"204": {
"description": "Pet deleted"
},
"404": {
"description": "Pet not found"
}
}
}
},
"/orders": {
"get": {
"summary": "List all orders",
"operationId": "listOrders",
"responses": {
"200": {
"description": "A list of orders",
"content": {
"application/json": {
"schema": {
"type": "array",
"items": {
"$ref": "#/components/schemas/Order"
}
}
}
}
}
}
}
},
"/orders/{orderId}": {
"get": {
"summary": "Get an order by ID",
"operationId": "getOrder",
"parameters": [
{
"name": "orderId",
"in": "path",
"description": "ID of the order to retrieve",
"required": true,
"schema": {
"type": "integer",
"format": "int64"
}
}
],
"responses": {
"200": {
"description": "An order",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/Order"
}
}
}
},
"404": {
"description": "Order not found"
}
}
},
"delete": {
"summary": "Delete an order by ID",
"operationId": "deleteOrder",
"parameters": [
{
"name": "orderId",
"in": "path",
"description": "ID of the order to delete",
"required": true,
"schema": {
"type": "integer",
"format": "int64"
}
}
],
"responses": {
"204": {
"description": "Order deleted"
},
"404": {
"description": "Order not found"
}
}
}
},
"/users": {
"post": {
"summary": "Create a user",
"operationId": "createUser",
"requestBody": {
"required": true,
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/NewUser"
}
}
}
},
"responses": {
"201": {
"description": "User created",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/User"
}
}
}
}
}
}
}
},
"components": {
"schemas": {
"Pet": {
"type": "object",
"required": ["id", "name"],
"properties": {
"id": {
"type": "integer",
"format": "int64"
},
"name": {
"type": "string"
},
"tag": {
"type": "string"
}
}
},
"NewPet": {
"type": "object",
"required": ["name"],
"properties": {
"name": {
"type": "string"
},
"tag": {
"type": "string"
}
}
},
"Pets": {
"type": "array",
"items": {
"$ref": "#/components/schemas/Pet"
}
},
"Order": {
"type": "object",
"required": ["id", "petId", "quantity", "shipDate"],
"properties": {
"id": {
"type": "integer",
"format": "int64"
},
"petId": {
"type": "integer",
"format": "int64"
},
"quantity": {
"type": "integer",
"format": "int32"
},
"shipDate": {
"type": "string",
"format": "date-time"
},
"status": {
"type": "string",
"enum": ["placed", "approved", "delivered"]
},
"complete": {
"type": "boolean"
}
}
},
"User": {
"type": "object",
"required": ["id", "username"],
"properties": {
"id": {
"type": "integer",
"format": "int64"
},
"username": {
"type": "string"
},
"email": {
"type": "string"
},
"phone": {
"type": "string"
}
}
},
"NewUser": {
"type": "object",
"required": ["username"],
"properties": {
"username": {
"type": "string"
},
"email": {
"type": "string"
},
"phone": {
"type": "string"
}
}
}
}
}
}

Binary file not shown.

View File

@@ -1,138 +0,0 @@
#!/usr/bin/env -S v -w -n -enable-globals run
import os
import time
import veb
import json
import x.json2 { Any }
import net.http
import freeflowuniverse.herolib.data.jsonschema { Schema }
import freeflowuniverse.herolib.web.openapi { Context, Request, Response, Server }
import freeflowuniverse.herolib.hero.processor { ProcedureCall, ProcessParams, Processor }
import freeflowuniverse.herolib.core.redisclient
const spec_path = '${os.dir(@FILE)}/data/openapi.json'
const spec_json = os.read_file(spec_path) or { panic(err) }
// Main function to start the server
fn main() {
// Initialize the Redis client and RPC mechanism
mut redis := redisclient.new('localhost:6379')!
mut rpc := redis.rpc_get('procedure_queue')
// Initialize the server
mut server := &Server{
specification: openapi.json_decode(spec_json)!
handler: Handler{
processor: Processor{
rpc: rpc
}
}
}
// Start the server
veb.run[Server, Context](mut server, 8080)
}
pub struct Handler {
mut:
processor Processor
}
fn (mut handler Handler) handle(request Request) !Response {
// Convert incoming OpenAPI request to a procedure call
mut params := []string{}
if request.arguments.len > 0 {
params = request.arguments.values().map(it.str()).clone()
}
if request.body != '' {
params << request.body
}
if request.parameters.len != 0 {
mut param_map := map[string]Any{} // Store parameters with correct types
for param_name, param_value in request.parameters {
operation_param := request.operation.parameters.filter(it.name == param_name)
if operation_param.len > 0 {
param_schema := operation_param[0].schema as Schema
param_type := param_schema.typ
param_format := param_schema.format
// Convert parameter value to corresponding type
match param_type {
'integer' {
match param_format {
'int32' {
param_map[param_name] = param_value.int() // Convert to int
}
'int64' {
param_map[param_name] = param_value.i64() // Convert to i64
}
else {
param_map[param_name] = param_value.int() // Default to int
}
}
}
'string' {
param_map[param_name] = param_value // Already a string
}
'boolean' {
param_map[param_name] = param_value.bool() // Convert to bool
}
'number' {
match param_format {
'float' {
param_map[param_name] = param_value.f32() // Convert to float
}
'double' {
param_map[param_name] = param_value.f64() // Convert to double
}
else {
param_map[param_name] = param_value.f64() // Default to double
}
}
}
else {
param_map[param_name] = param_value // Leave as string for unknown types
}
}
} else {
// If the parameter is not defined in the OpenAPI operation, skip or log it
println('Unknown parameter: ${param_name}')
}
}
// Encode the parameter map to JSON if needed
params << json.encode(param_map.str())
}
call := ProcedureCall{
method: request.operation.operation_id
params: '[${params.join(',')}]' // Keep as a string since ProcedureCall expects a string
}
// Process the procedure call
procedure_response := handler.processor.process(call, ProcessParams{
timeout: 30 // Set timeout in seconds
}) or {
// Handle ProcedureError
if err is processor.ProcedureError {
return Response{
status: http.status_from_int(err.code()) // Map ProcedureError reason to HTTP status code
body: json.encode({
'error': err.msg()
})
}
}
return error('Unexpected error: ${err}')
}
// Convert returned procedure response to OpenAPI response
return Response{
status: http.Status.ok // Assuming success if no error
body: procedure_response.result
}
}

View File

@@ -13,7 +13,7 @@ println('=== HeroPods Refactored API Demo ===')
// Step 1: factory.new() now only creates a container definition/handle
// It does NOT create the actual container in the backend yet
mut container := factory.new(
name: 'myalpine'
name: 'demo_alpine'
image: .custom
custom_image_name: 'alpine_3_20'
docker_url: 'docker.io/library/alpine:3.20'

View File

@@ -8,7 +8,7 @@ mut factory := heropods.new(
) or { panic('Failed to init ContainerFactory: ${err}') }
mut container := factory.new(
name: 'myalpine'
name: 'alpine_demo'
image: .custom
custom_image_name: 'alpine_3_20'
docker_url: 'docker.io/library/alpine:3.20'