Merge branch 'development_actions007' of github.com:freeflowuniverse/herolib into development_actions007
This commit is contained in:
1
.gitignore
vendored
1
.gitignore
vendored
@@ -44,3 +44,4 @@ compile_results.log
|
|||||||
tmp
|
tmp
|
||||||
compile_summary.log
|
compile_summary.log
|
||||||
.summary_lock
|
.summary_lock
|
||||||
|
.aider*
|
||||||
|
|||||||
@@ -1,27 +1,30 @@
|
|||||||
module developer
|
module developer
|
||||||
|
import freeflowuniverse.herolib.mcp
|
||||||
|
|
||||||
@[heap]
|
@[heap]
|
||||||
pub struct Developer {}
|
pub struct Developer {}
|
||||||
|
|
||||||
|
|
||||||
pub fn result_to_mcp_tool_contents[T](result T) []mcp.ToolContent {
|
pub fn result_to_mcp_tool_contents[T](result T) []mcp.ToolContent {
|
||||||
return [result_to_mcp_tool_content(result)]
|
return [result_to_mcp_tool_content(result)]
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn result_to_mcp_tool_content[T](result T) mcp.ToolContent {
|
pub fn result_to_mcp_tool_content[T](result T) mcp.ToolContent {
|
||||||
return $if T is string {
|
return $if T is string {
|
||||||
mcp.ToolContent{
|
mcp.ToolContent
|
||||||
typ: 'text'
|
{
|
||||||
|
typ: 'text'
|
||||||
text: result.str()
|
text: result.str()
|
||||||
}
|
}
|
||||||
} $else $if T is int {
|
} $else $if T is int {
|
||||||
mcp.ToolContent{
|
mcp.ToolContent
|
||||||
typ: 'number'
|
{
|
||||||
|
typ: 'number'
|
||||||
number: result.int()
|
number: result.int()
|
||||||
}
|
}
|
||||||
} $else $if T is bool {
|
} $else $if T is bool {
|
||||||
mcp.ToolContent{
|
mcp.ToolContent
|
||||||
typ: 'boolean'
|
{
|
||||||
|
typ: 'boolean'
|
||||||
boolean: result.bool()
|
boolean: result.bool()
|
||||||
}
|
}
|
||||||
} $else $if result is $array {
|
} $else $if result is $array {
|
||||||
@@ -29,8 +32,9 @@ pub fn result_to_mcp_tool_content[T](result T) mcp.ToolContent {
|
|||||||
for item in result {
|
for item in result {
|
||||||
items << result_to_mcp_tool_content(item)
|
items << result_to_mcp_tool_content(item)
|
||||||
}
|
}
|
||||||
return mcp.ToolContent{
|
return mcp.ToolContent
|
||||||
typ: 'array'
|
{
|
||||||
|
typ: 'array'
|
||||||
items: items
|
items: items
|
||||||
}
|
}
|
||||||
} $else $if T is $struct {
|
} $else $if T is $struct {
|
||||||
@@ -38,8 +42,9 @@ pub fn result_to_mcp_tool_content[T](result T) mcp.ToolContent {
|
|||||||
$for field in T.fields {
|
$for field in T.fields {
|
||||||
properties[field.name] = result_to_mcp_tool_content(result.$(field.name))
|
properties[field.name] = result_to_mcp_tool_content(result.$(field.name))
|
||||||
}
|
}
|
||||||
return mcp.ToolContent{
|
return mcp.ToolContent
|
||||||
typ: 'object'
|
{
|
||||||
|
typ: 'object'
|
||||||
properties: properties
|
properties: properties
|
||||||
}
|
}
|
||||||
} $else {
|
} $else {
|
||||||
@@ -2,10 +2,17 @@ module developer
|
|||||||
|
|
||||||
import freeflowuniverse.herolib.core.code
|
import freeflowuniverse.herolib.core.code
|
||||||
import freeflowuniverse.herolib.mcp
|
import freeflowuniverse.herolib.mcp
|
||||||
|
import os
|
||||||
|
|
||||||
// create_mcp_tool_code receives the name of a V language function string, and the path to the module in which it exists.
|
// create_mcp_tool_code receives the name of a V language function string, and the path to the module in which it exists.
|
||||||
// returns an MCP Tool code in v for attaching the function to the mcp server
|
// returns an MCP Tool code in v for attaching the function to the mcp server
|
||||||
pub fn (d &Developer) create_mcp_tool_code(function_name string, module_path string) !string {
|
pub fn (d &Developer) create_mcp_tool_code(function_name string, module_path string) !string {
|
||||||
|
println('DEBUG: Looking for function ${function_name} in module path: ${module_path}')
|
||||||
|
if !os.exists(module_path) {
|
||||||
|
println('DEBUG: Module path does not exist: ${module_path}')
|
||||||
|
return error('Module path does not exist: ${module_path}')
|
||||||
|
}
|
||||||
|
|
||||||
function_ := get_function_from_module(module_path, function_name)!
|
function_ := get_function_from_module(module_path, function_name)!
|
||||||
println('Function string found:\n${function_}')
|
println('Function string found:\n${function_}')
|
||||||
|
|
||||||
@@ -1,7 +1,7 @@
|
|||||||
module developer
|
module developer
|
||||||
|
|
||||||
import freeflowuniverse.herolib.mcp
|
import freeflowuniverse.herolib.mcp
|
||||||
import x.json2 {Any}
|
import x.json2
|
||||||
|
|
||||||
pub fn result_to_mcp_tool_contents[T](result T) []mcp.ToolContent {
|
pub fn result_to_mcp_tool_contents[T](result T) []mcp.ToolContent {
|
||||||
return [result_to_mcp_tool_content(result)]
|
return [result_to_mcp_tool_content(result)]
|
||||||
@@ -10,17 +10,17 @@ pub fn result_to_mcp_tool_contents[T](result T) []mcp.ToolContent {
|
|||||||
pub fn result_to_mcp_tool_content[T](result T) mcp.ToolContent {
|
pub fn result_to_mcp_tool_content[T](result T) mcp.ToolContent {
|
||||||
return $if T is string {
|
return $if T is string {
|
||||||
mcp.ToolContent{
|
mcp.ToolContent{
|
||||||
typ: 'text'
|
typ: 'text'
|
||||||
text: result.str()
|
text: result.str()
|
||||||
}
|
}
|
||||||
} $else $if T is int {
|
} $else $if T is int {
|
||||||
mcp.ToolContent{
|
mcp.ToolContent{
|
||||||
typ: 'number'
|
typ: 'number'
|
||||||
number: result.int()
|
number: result.int()
|
||||||
}
|
}
|
||||||
} $else $if T is bool {
|
} $else $if T is bool {
|
||||||
mcp.ToolContent{
|
mcp.ToolContent{
|
||||||
typ: 'boolean'
|
typ: 'boolean'
|
||||||
boolean: result.bool()
|
boolean: result.bool()
|
||||||
}
|
}
|
||||||
} $else $if result is $array {
|
} $else $if result is $array {
|
||||||
@@ -29,7 +29,7 @@ pub fn result_to_mcp_tool_content[T](result T) mcp.ToolContent {
|
|||||||
items << result_to_mcp_tool_content(item)
|
items << result_to_mcp_tool_content(item)
|
||||||
}
|
}
|
||||||
return mcp.ToolContent{
|
return mcp.ToolContent{
|
||||||
typ: 'array'
|
typ: 'array'
|
||||||
items: items
|
items: items
|
||||||
}
|
}
|
||||||
} $else $if T is $struct {
|
} $else $if T is $struct {
|
||||||
@@ -38,7 +38,7 @@ pub fn result_to_mcp_tool_content[T](result T) mcp.ToolContent {
|
|||||||
properties[field.name] = result_to_mcp_tool_content(result.$(field.name))
|
properties[field.name] = result_to_mcp_tool_content(result.$(field.name))
|
||||||
}
|
}
|
||||||
return mcp.ToolContent{
|
return mcp.ToolContent{
|
||||||
typ: 'object'
|
typ: 'object'
|
||||||
properties: properties
|
properties: properties
|
||||||
}
|
}
|
||||||
} $else {
|
} $else {
|
||||||
205
TOSORT/developer/generate_mcp_test.v
Normal file
205
TOSORT/developer/generate_mcp_test.v
Normal file
@@ -0,0 +1,205 @@
|
|||||||
|
module developer
|
||||||
|
|
||||||
|
import freeflowuniverse.herolib.mcp
|
||||||
|
import json
|
||||||
|
import os
|
||||||
|
|
||||||
|
// fn test_parse_struct_fields() {
|
||||||
|
// // Test case 1: Simple struct with primitive types
|
||||||
|
// simple_struct := 'pub struct User {
|
||||||
|
// name string
|
||||||
|
// age int
|
||||||
|
// active bool
|
||||||
|
// }'
|
||||||
|
|
||||||
|
// fields := parse_struct_fields(simple_struct)
|
||||||
|
// assert fields.len == 3
|
||||||
|
// assert fields['name'] == 'string'
|
||||||
|
// assert fields['age'] == 'int'
|
||||||
|
// assert fields['active'] == 'bool'
|
||||||
|
|
||||||
|
// // Test case 2: Struct with pub: and mut: sections
|
||||||
|
// complex_struct := 'pub struct Config {
|
||||||
|
// pub:
|
||||||
|
// host string
|
||||||
|
// port int
|
||||||
|
// mut:
|
||||||
|
// connected bool
|
||||||
|
// retries int
|
||||||
|
// }'
|
||||||
|
|
||||||
|
// fields2 := parse_struct_fields(complex_struct)
|
||||||
|
// assert fields2.len == 4
|
||||||
|
// assert fields2['host'] == 'string'
|
||||||
|
// assert fields2['port'] == 'int'
|
||||||
|
// assert fields2['connected'] == 'bool'
|
||||||
|
// assert fields2['retries'] == 'int'
|
||||||
|
|
||||||
|
// // Test case 3: Struct with attributes and comments
|
||||||
|
// struct_with_attrs := 'pub struct ApiResponse {
|
||||||
|
// // User ID
|
||||||
|
// id int
|
||||||
|
// // User full name
|
||||||
|
// name string @[json: "full_name"]
|
||||||
|
// // Whether account is active
|
||||||
|
// active bool
|
||||||
|
// }'
|
||||||
|
|
||||||
|
// fields3 := parse_struct_fields(struct_with_attrs)
|
||||||
|
// assert fields3.len == 3 // All fields are included
|
||||||
|
// assert fields3['id'] == 'int'
|
||||||
|
// assert fields3['active'] == 'bool'
|
||||||
|
|
||||||
|
// // Test case 4: Empty struct
|
||||||
|
// empty_struct := 'pub struct Empty {}'
|
||||||
|
// fields4 := parse_struct_fields(empty_struct)
|
||||||
|
// assert fields4.len == 0
|
||||||
|
|
||||||
|
// println('test_parse_struct_fields passed')
|
||||||
|
// }
|
||||||
|
|
||||||
|
// fn test_create_mcp_tool_input_schema() {
|
||||||
|
// d := Developer{}
|
||||||
|
|
||||||
|
// // Test case 1: Primitive types
|
||||||
|
// string_schema := d.create_mcp_tool_input_schema('string') or { panic(err) }
|
||||||
|
// assert string_schema.typ == 'string'
|
||||||
|
|
||||||
|
// int_schema := d.create_mcp_tool_input_schema('int') or { panic(err) }
|
||||||
|
// assert int_schema.typ == 'integer'
|
||||||
|
|
||||||
|
// float_schema := d.create_mcp_tool_input_schema('float') or { panic(err) }
|
||||||
|
// assert float_schema.typ == 'number'
|
||||||
|
|
||||||
|
// bool_schema := d.create_mcp_tool_input_schema('bool') or { panic(err) }
|
||||||
|
// assert bool_schema.typ == 'boolean'
|
||||||
|
|
||||||
|
// // Test case 2: Array type
|
||||||
|
// array_schema := d.create_mcp_tool_input_schema('[]string') or { panic(err) }
|
||||||
|
// assert array_schema.typ == 'array'
|
||||||
|
// // In our implementation, arrays don't have items directly in the schema
|
||||||
|
|
||||||
|
// // Test case 3: Struct type
|
||||||
|
// struct_def := 'pub struct Person {
|
||||||
|
// name string
|
||||||
|
// age int
|
||||||
|
// }'
|
||||||
|
|
||||||
|
// struct_schema := d.create_mcp_tool_input_schema(struct_def) or { panic(err) }
|
||||||
|
// assert struct_schema.typ == 'object'
|
||||||
|
// assert struct_schema.properties.len == 2
|
||||||
|
// assert struct_schema.properties['name'].typ == 'string'
|
||||||
|
// assert struct_schema.properties['age'].typ == 'integer'
|
||||||
|
|
||||||
|
// println('test_create_mcp_tool_input_schema passed')
|
||||||
|
// }
|
||||||
|
|
||||||
|
// fn test_create_mcp_tool() {
|
||||||
|
// d := Developer{}
|
||||||
|
|
||||||
|
// // Test case 1: Simple function with primitive types
|
||||||
|
// simple_fn := '// Get user by ID
|
||||||
|
// // Returns user information
|
||||||
|
// pub fn get_user(id int, include_details bool) {
|
||||||
|
// // Implementation
|
||||||
|
// }'
|
||||||
|
|
||||||
|
// tool1 := d.create_mcp_tool(simple_fn, {}) or { panic(err) }
|
||||||
|
// assert tool1.name == 'get_user'
|
||||||
|
// expected_desc1 := 'Get user by ID\nReturns user information'
|
||||||
|
// assert tool1.description == expected_desc1
|
||||||
|
// assert tool1.input_schema.typ == 'object'
|
||||||
|
// assert tool1.input_schema.properties.len == 2
|
||||||
|
// assert tool1.input_schema.properties['id'].typ == 'integer'
|
||||||
|
// assert tool1.input_schema.properties['include_details'].typ == 'boolean'
|
||||||
|
// assert tool1.input_schema.required.len == 2
|
||||||
|
// assert 'id' in tool1.input_schema.required
|
||||||
|
// assert 'include_details' in tool1.input_schema.required
|
||||||
|
|
||||||
|
// // Test case 2: Method with receiver
|
||||||
|
// method_fn := '// Update user profile
|
||||||
|
// pub fn (u User) update_profile(name string, age int) bool {
|
||||||
|
// // Implementation
|
||||||
|
// return true
|
||||||
|
// }'
|
||||||
|
|
||||||
|
// tool2 := d.create_mcp_tool(method_fn, {}) or { panic(err) }
|
||||||
|
// assert tool2.name == 'update_profile'
|
||||||
|
// assert tool2.description == 'Update user profile'
|
||||||
|
// assert tool2.input_schema.properties.len == 2
|
||||||
|
// assert tool2.input_schema.properties['name'].typ == 'string'
|
||||||
|
// assert tool2.input_schema.properties['age'].typ == 'integer'
|
||||||
|
|
||||||
|
// // Test case 3: Function with complex types
|
||||||
|
// complex_fn := '// Create new configuration
|
||||||
|
// // Sets up system configuration
|
||||||
|
// fn create_config(name string, settings Config) !Config {
|
||||||
|
// // Implementation
|
||||||
|
// }'
|
||||||
|
|
||||||
|
// config_struct := 'pub struct Config {
|
||||||
|
// server_url string
|
||||||
|
// max_retries int
|
||||||
|
// timeout float
|
||||||
|
// }'
|
||||||
|
|
||||||
|
// tool3 := d.create_mcp_tool(complex_fn, {
|
||||||
|
// 'Config': config_struct
|
||||||
|
// }) or { panic(err) }
|
||||||
|
// assert tool3.name == 'create_config'
|
||||||
|
// expected_desc3 := 'Create new configuration\nSets up system configuration'
|
||||||
|
// assert tool3.description == expected_desc3
|
||||||
|
// assert tool3.input_schema.properties.len == 2
|
||||||
|
// assert tool3.input_schema.properties['name'].typ == 'string'
|
||||||
|
// assert tool3.input_schema.properties['settings'].typ == 'object'
|
||||||
|
|
||||||
|
// // Test case 4: Function with no parameters
|
||||||
|
// no_params_fn := '// Initialize system
|
||||||
|
// pub fn initialize() {
|
||||||
|
// // Implementation
|
||||||
|
// }'
|
||||||
|
|
||||||
|
// tool4 := d.create_mcp_tool(no_params_fn, {}) or { panic(err) }
|
||||||
|
// assert tool4.name == 'initialize'
|
||||||
|
// assert tool4.description == 'Initialize system'
|
||||||
|
// assert tool4.input_schema.properties.len == 0
|
||||||
|
// assert tool4.input_schema.required.len == 0
|
||||||
|
|
||||||
|
// println('test_create_mcp_tool passed')
|
||||||
|
// }
|
||||||
|
|
||||||
|
// fn test_create_mcp_tool_code() {
|
||||||
|
// d := Developer{}
|
||||||
|
|
||||||
|
// // Test with the complex function that has struct parameters and return type
|
||||||
|
// module_path := "${os.dir(@FILE)}/testdata/mock_module"
|
||||||
|
// function_name := 'test_function'
|
||||||
|
|
||||||
|
// code := d.create_mcp_tool_code(function_name, module_path) or {
|
||||||
|
// panic('Failed to create MCP tool code: ${err}')
|
||||||
|
// }
|
||||||
|
|
||||||
|
// // Print the code instead of panic for debugging
|
||||||
|
// println('Generated code:')
|
||||||
|
// println('----------------------------------------')
|
||||||
|
// println(code)
|
||||||
|
// println('----------------------------------------')
|
||||||
|
|
||||||
|
// // Verify the generated code contains the expected elements
|
||||||
|
// assert code.contains('test_function_tool')
|
||||||
|
// assert code.contains('TestConfig')
|
||||||
|
// assert code.contains('TestResult')
|
||||||
|
|
||||||
|
// // Test with a simple function that has primitive types
|
||||||
|
// simple_function_name := 'simple_function'
|
||||||
|
// simple_code := d.create_mcp_tool_code(simple_function_name, module_path) or {
|
||||||
|
// panic('Failed to create MCP tool code for simple function: ${err}')
|
||||||
|
// }
|
||||||
|
|
||||||
|
// // Verify the simple function code
|
||||||
|
// assert simple_code.contains('simple_function_tool')
|
||||||
|
// assert simple_code.contains('name string')
|
||||||
|
// assert simple_code.contains('count int')
|
||||||
|
|
||||||
|
// // println('test_create_mcp_tool_code passed')
|
||||||
|
// }
|
||||||
@@ -1,32 +1,35 @@
|
|||||||
module developer
|
module developer
|
||||||
|
|
||||||
import freeflowuniverse.herolib.mcp
|
import freeflowuniverse.herolib.mcp
|
||||||
import x.json2 as json {Any}
|
import x.json2 as json { Any }
|
||||||
// import json
|
// import json
|
||||||
|
|
||||||
const create_mcp_tool_code_tool = mcp.Tool{
|
const create_mcp_tool_code_tool = mcp.Tool{
|
||||||
name: 'create_mcp_tool_code'
|
name: 'create_mcp_tool_code'
|
||||||
description: 'create_mcp_tool_code receives the name of a V language function string, and the path to the module in which it exists.
|
description: 'create_mcp_tool_code receives the name of a V language function string, and the path to the module in which it exists.
|
||||||
returns an MCP Tool code in v for attaching the function to the mcp server'
|
returns an MCP Tool code in v for attaching the function to the mcp server'
|
||||||
input_schema: mcp.ToolInputSchema{
|
input_schema: mcp.ToolInputSchema{
|
||||||
typ: 'object'
|
typ: 'object'
|
||||||
properties: {'function_name': mcp.ToolProperty{
|
properties: {
|
||||||
typ: 'string'
|
'function_name': mcp.ToolProperty{
|
||||||
items: mcp.ToolItems{
|
typ: 'string'
|
||||||
typ: ''
|
items: mcp.ToolItems{
|
||||||
enum: []
|
typ: ''
|
||||||
}
|
enum: []
|
||||||
enum: []
|
}
|
||||||
}, 'module_path': mcp.ToolProperty{
|
enum: []
|
||||||
typ: 'string'
|
}
|
||||||
items: mcp.ToolItems{
|
'module_path': mcp.ToolProperty{
|
||||||
typ: ''
|
typ: 'string'
|
||||||
enum: []
|
items: mcp.ToolItems{
|
||||||
}
|
typ: ''
|
||||||
enum: []
|
enum: []
|
||||||
}}
|
}
|
||||||
required: ['function_name', 'module_path']
|
enum: []
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
required: ['function_name', 'module_path']
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn (d &Developer) create_mcp_tool_code_tool_handler(arguments map[string]Any) !mcp.ToolCallResult {
|
pub fn (d &Developer) create_mcp_tool_code_tool_handler(arguments map[string]Any) !mcp.ToolCallResult {
|
||||||
@@ -37,59 +40,56 @@ pub fn (d &Developer) create_mcp_tool_code_tool_handler(arguments map[string]Any
|
|||||||
}
|
}
|
||||||
return mcp.ToolCallResult{
|
return mcp.ToolCallResult{
|
||||||
is_error: false
|
is_error: false
|
||||||
content: result_to_mcp_tool_contents[string](result)
|
content: result_to_mcp_tool_contents[string](result)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Tool definition for the create_mcp_tool function
|
// Tool definition for the create_mcp_tool function
|
||||||
const create_mcp_tool_tool = mcp.Tool{
|
const create_mcp_tool_tool = mcp.Tool{
|
||||||
name: 'create_mcp_tool'
|
name: 'create_mcp_tool'
|
||||||
description: 'Parses a V language function string and returns an MCP Tool struct. This tool analyzes function signatures, extracts parameters, and generates the appropriate MCP Tool representation.'
|
description: 'Parses a V language function string and returns an MCP Tool struct. This tool analyzes function signatures, extracts parameters, and generates the appropriate MCP Tool representation.'
|
||||||
input_schema: mcp.ToolInputSchema{
|
input_schema: mcp.ToolInputSchema{
|
||||||
typ: 'object'
|
typ: 'object'
|
||||||
properties: {
|
properties: {
|
||||||
'function': mcp.ToolProperty{
|
'function': mcp.ToolProperty{
|
||||||
typ: 'string'
|
typ: 'string'
|
||||||
}
|
}
|
||||||
'types': mcp.ToolProperty{
|
'types': mcp.ToolProperty{
|
||||||
typ: 'object'
|
typ: 'object'
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
required: ['function']
|
required: ['function']
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn (d &Developer) create_mcp_tool_tool_handler(arguments map[string]Any) !mcp.ToolCallResult {
|
pub fn (d &Developer) create_mcp_tool_tool_handler(arguments map[string]Any) !mcp.ToolCallResult {
|
||||||
function := arguments['function'].str()
|
function := arguments['function'].str()
|
||||||
types := json.decode[map[string]string](arguments['types'].str())!
|
types := json.decode[map[string]string](arguments['types'].str())!
|
||||||
result := d.create_mcp_tool(function, types) or {
|
result := d.create_mcp_tool(function, types) or { return mcp.error_tool_call_result(err) }
|
||||||
return mcp.error_tool_call_result(err)
|
|
||||||
}
|
|
||||||
return mcp.ToolCallResult{
|
return mcp.ToolCallResult{
|
||||||
is_error: false
|
is_error: false
|
||||||
content: result_to_mcp_tool_contents[string](result.str())
|
content: result_to_mcp_tool_contents[string](result.str())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
// Tool definition for the create_mcp_tool_handler function
|
// Tool definition for the create_mcp_tool_handler function
|
||||||
const create_mcp_tool_handler_tool = mcp.Tool{
|
const create_mcp_tool_handler_tool = mcp.Tool{
|
||||||
name: 'create_mcp_tool_handler'
|
name: 'create_mcp_tool_handler'
|
||||||
description: 'Generates a tool handler for the create_mcp_tool function. This tool handler accepts function string and types map and returns an MCP ToolCallResult.'
|
description: 'Generates a tool handler for the create_mcp_tool function. This tool handler accepts function string and types map and returns an MCP ToolCallResult.'
|
||||||
input_schema: mcp.ToolInputSchema{
|
input_schema: mcp.ToolInputSchema{
|
||||||
typ: 'object'
|
typ: 'object'
|
||||||
properties: {
|
properties: {
|
||||||
'function': mcp.ToolProperty{
|
'function': mcp.ToolProperty{
|
||||||
typ: 'string'
|
typ: 'string'
|
||||||
},
|
}
|
||||||
'types': mcp.ToolProperty{
|
'types': mcp.ToolProperty{
|
||||||
typ: 'object'
|
typ: 'object'
|
||||||
},
|
}
|
||||||
'result': mcp.ToolProperty{
|
'result': mcp.ToolProperty{
|
||||||
typ: 'string'
|
typ: 'string'
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
required: ['function', 'result']
|
required: ['function', 'result']
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -103,6 +103,6 @@ pub fn (d &Developer) create_mcp_tool_handler_tool_handler(arguments map[string]
|
|||||||
}
|
}
|
||||||
return mcp.ToolCallResult{
|
return mcp.ToolCallResult{
|
||||||
is_error: false
|
is_error: false
|
||||||
content: result_to_mcp_tool_contents[string](result)
|
content: result_to_mcp_tool_contents[string](result)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
31
TOSORT/developer/mcp.v
Normal file
31
TOSORT/developer/mcp.v
Normal file
@@ -0,0 +1,31 @@
|
|||||||
|
module developer
|
||||||
|
|
||||||
|
import freeflowuniverse.herolib.mcp.logger
|
||||||
|
import freeflowuniverse.herolib.mcp
|
||||||
|
import freeflowuniverse.herolib.schemas.jsonrpc
|
||||||
|
|
||||||
|
// pub fn new_mcp_server(d &Developer) !&mcp.Server {
|
||||||
|
// logger.info('Creating new Developer MCP server')
|
||||||
|
|
||||||
|
// // Initialize the server with the empty handlers map
|
||||||
|
// mut server := mcp.new_server(mcp.MemoryBackend{
|
||||||
|
// tools: {
|
||||||
|
// 'create_mcp_tool': create_mcp_tool_tool
|
||||||
|
// 'create_mcp_tool_handler': create_mcp_tool_handler_tool
|
||||||
|
// 'create_mcp_tool_code': create_mcp_tool_code_tool
|
||||||
|
// }
|
||||||
|
// tool_handlers: {
|
||||||
|
// 'create_mcp_tool': d.create_mcp_tool_tool_handler
|
||||||
|
// 'create_mcp_tool_handler': d.create_mcp_tool_handler_tool_handler
|
||||||
|
// 'create_mcp_tool_code': d.create_mcp_tool_code_tool_handler
|
||||||
|
// }
|
||||||
|
// }, mcp.ServerParams{
|
||||||
|
// config: mcp.ServerConfiguration{
|
||||||
|
// server_info: mcp.ServerInfo{
|
||||||
|
// name: 'developer'
|
||||||
|
// version: '1.0.0'
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
// })!
|
||||||
|
// return server
|
||||||
|
// }
|
||||||
@@ -5,7 +5,6 @@ import freeflowuniverse.herolib.mcp.logger
|
|||||||
|
|
||||||
mut server := developer.new_mcp_server(&developer.Developer{})!
|
mut server := developer.new_mcp_server(&developer.Developer{})!
|
||||||
server.start() or {
|
server.start() or {
|
||||||
logger.fatal('Error starting server: $err')
|
logger.fatal('Error starting server: ${err}')
|
||||||
exit(1)
|
exit(1)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -3,10 +3,10 @@ module mock_module
|
|||||||
// TestConfig represents a configuration for testing
|
// TestConfig represents a configuration for testing
|
||||||
pub struct TestConfig {
|
pub struct TestConfig {
|
||||||
pub:
|
pub:
|
||||||
name string
|
name string
|
||||||
enabled bool
|
enabled bool
|
||||||
count int
|
count int
|
||||||
value float64
|
value float64
|
||||||
}
|
}
|
||||||
|
|
||||||
// TestResult represents the result of a test operation
|
// TestResult represents the result of a test operation
|
||||||
@@ -14,7 +14,7 @@ pub struct TestResult {
|
|||||||
pub:
|
pub:
|
||||||
success bool
|
success bool
|
||||||
message string
|
message string
|
||||||
code int
|
code int
|
||||||
}
|
}
|
||||||
|
|
||||||
// test_function is a simple function for testing the MCP tool code generation
|
// test_function is a simple function for testing the MCP tool code generation
|
||||||
@@ -28,7 +28,7 @@ pub fn test_function(config TestConfig) !TestResult {
|
|||||||
return TestResult{
|
return TestResult{
|
||||||
success: config.enabled
|
success: config.enabled
|
||||||
message: 'Test completed for ${config.name}'
|
message: 'Test completed for ${config.name}'
|
||||||
code: if config.enabled { 0 } else { 1 }
|
code: if config.enabled { 0 } else { 1 }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -12,13 +12,13 @@ fn get_type_from_module(module_path string, type_name string) !string {
|
|||||||
}
|
}
|
||||||
|
|
||||||
for v_file in v_files {
|
for v_file in v_files {
|
||||||
content := os.read_file(v_file) or {
|
content := os.read_file(v_file) or { return error('Failed to read file ${v_file}: ${err}') }
|
||||||
return error('Failed to read file ${v_file}: ${err}')
|
|
||||||
}
|
|
||||||
|
|
||||||
type_str := 'struct ${type_name} {'
|
type_str := 'struct ${type_name} {'
|
||||||
i := content.index(type_str) or { -1 }
|
i := content.index(type_str) or { -1 }
|
||||||
if i == -1 { continue }
|
if i == -1 {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
start_i := i + type_str.len
|
start_i := i + type_str.len
|
||||||
closing_i := find_closing_brace(content, start_i) or {
|
closing_i := find_closing_brace(content, start_i) or {
|
||||||
@@ -49,9 +49,7 @@ fn find_closing_brace(content string, start_i int) ?int {
|
|||||||
|
|
||||||
// Helper function to list V files
|
// Helper function to list V files
|
||||||
fn list_v_files(dir string) ![]string {
|
fn list_v_files(dir string) ![]string {
|
||||||
files := os.ls(dir) or {
|
files := os.ls(dir) or { return error('Error listing directory: ${err}') }
|
||||||
return error('Error listing directory: $err')
|
|
||||||
}
|
|
||||||
|
|
||||||
mut v_files := []string{}
|
mut v_files := []string{}
|
||||||
for file in files {
|
for file in files {
|
||||||
@@ -71,7 +69,7 @@ fn create_test_files() !(string, string, string) {
|
|||||||
test_file_path := os.join_path(test_dir, 'test_type.v')
|
test_file_path := os.join_path(test_dir, 'test_type.v')
|
||||||
|
|
||||||
// Create a test file with a simple struct
|
// Create a test file with a simple struct
|
||||||
test_content := "module test_module
|
test_content := 'module test_module
|
||||||
|
|
||||||
struct TestType {
|
struct TestType {
|
||||||
name string
|
name string
|
||||||
@@ -83,14 +81,14 @@ struct TestType {
|
|||||||
struct OtherType {
|
struct OtherType {
|
||||||
id string
|
id string
|
||||||
}
|
}
|
||||||
"
|
'
|
||||||
os.write_file(test_file_path, test_content) or {
|
os.write_file(test_file_path, test_content) or {
|
||||||
eprintln('Failed to create test file: $err')
|
eprintln('Failed to create test file: ${err}')
|
||||||
return error('Failed to create test file: $err')
|
return error('Failed to create test file: ${err}')
|
||||||
}
|
}
|
||||||
|
|
||||||
// Create a test file with a nested struct
|
// Create a test file with a nested struct
|
||||||
nested_test_content := "module test_module
|
nested_test_content := 'module test_module
|
||||||
|
|
||||||
struct NestedType {
|
struct NestedType {
|
||||||
config map[string]string {
|
config map[string]string {
|
||||||
@@ -101,11 +99,11 @@ struct NestedType {
|
|||||||
value string
|
value string
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
"
|
'
|
||||||
nested_test_file := os.join_path(test_dir, 'nested_test.v')
|
nested_test_file := os.join_path(test_dir, 'nested_test.v')
|
||||||
os.write_file(nested_test_file, nested_test_content) or {
|
os.write_file(nested_test_file, nested_test_content) or {
|
||||||
eprintln('Failed to create nested test file: $err')
|
eprintln('Failed to create nested test file: ${err}')
|
||||||
return error('Failed to create nested test file: $err')
|
return error('Failed to create nested test file: ${err}')
|
||||||
}
|
}
|
||||||
|
|
||||||
return test_dir, test_file_path, nested_test_file
|
return test_dir, test_file_path, nested_test_file
|
||||||
@@ -115,21 +113,21 @@ struct NestedType {
|
|||||||
fn test_get_type_from_module() {
|
fn test_get_type_from_module() {
|
||||||
// Create test files
|
// Create test files
|
||||||
test_dir, test_file_path, nested_test_file := create_test_files() or {
|
test_dir, test_file_path, nested_test_file := create_test_files() or {
|
||||||
eprintln('Failed to create test files: $err')
|
eprintln('Failed to create test files: ${err}')
|
||||||
assert false
|
assert false
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// Test case 1: Get a simple struct
|
// Test case 1: Get a simple struct
|
||||||
type_content := get_type_from_module(test_dir, 'TestType') or {
|
type_content := get_type_from_module(test_dir, 'TestType') or {
|
||||||
eprintln('Failed to get type: $err')
|
eprintln('Failed to get type: ${err}')
|
||||||
assert false
|
assert false
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// Verify the content matches what we expect
|
// Verify the content matches what we expect
|
||||||
expected := "\n\tname string\n\tage int\n\tactive bool\n}"
|
expected := '\n\tname string\n\tage int\n\tactive bool\n}'
|
||||||
assert type_content == expected, 'Expected: "$expected", got: "$type_content"'
|
assert type_content == expected, 'Expected: "${expected}", got: "${type_content}"'
|
||||||
|
|
||||||
// Test case 2: Try to get a non-existent type
|
// Test case 2: Try to get a non-existent type
|
||||||
non_existent := get_type_from_module(test_dir, 'NonExistentType') or {
|
non_existent := get_type_from_module(test_dir, 'NonExistentType') or {
|
||||||
@@ -141,13 +139,13 @@ fn test_get_type_from_module() {
|
|||||||
|
|
||||||
// Test case 3: Test with nested braces in the struct
|
// Test case 3: Test with nested braces in the struct
|
||||||
nested_type_content := get_type_from_module(test_dir, 'NestedType') or {
|
nested_type_content := get_type_from_module(test_dir, 'NestedType') or {
|
||||||
eprintln('Failed to get nested type: $err')
|
eprintln('Failed to get nested type: ${err}')
|
||||||
assert false
|
assert false
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
expected_nested := "\n\tconfig map[string]string {\n\t\trequired: true\n\t}\n\tdata []struct {\n\t\tkey string\n\t\tvalue string\n\t}\n}"
|
expected_nested := '\n\tconfig map[string]string {\n\t\trequired: true\n\t}\n\tdata []struct {\n\t\tkey string\n\t\tvalue string\n\t}\n}'
|
||||||
assert nested_type_content == expected_nested, 'Expected: "$expected_nested", got: "$nested_type_content"'
|
assert nested_type_content == expected_nested, 'Expected: "${expected_nested}", got: "${nested_type_content}"'
|
||||||
|
|
||||||
// Clean up test files
|
// Clean up test files
|
||||||
os.rm(test_file_path) or {}
|
os.rm(test_file_path) or {}
|
||||||
@@ -7,7 +7,7 @@ import log
|
|||||||
|
|
||||||
fn get_module_dir(mod string) string {
|
fn get_module_dir(mod string) string {
|
||||||
module_parts := mod.trim_string_left('freeflowuniverse.herolib').split('.')
|
module_parts := mod.trim_string_left('freeflowuniverse.herolib').split('.')
|
||||||
return '${os.home_dir()}/code/github/freeflowuniverse/herolib/lib/${module_parts.join("/")}'
|
return '${os.home_dir()}/code/github/freeflowuniverse/herolib/lib/${module_parts.join('/')}'
|
||||||
}
|
}
|
||||||
|
|
||||||
// given a module path and a type name, returns the type definition of that type within that module
|
// given a module path and a type name, returns the type definition of that type within that module
|
||||||
@@ -20,10 +20,7 @@ fn get_type_from_module(module_path string, type_name string) !string {
|
|||||||
|
|
||||||
for v_file in v_files {
|
for v_file in v_files {
|
||||||
println('Checking file: ${v_file}')
|
println('Checking file: ${v_file}')
|
||||||
content := os.read_file(v_file) or {
|
content := os.read_file(v_file) or { return error('Failed to read file ${v_file}: ${err}') }
|
||||||
return error('Failed to read file ${v_file}: ${err}')
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
// Look for both regular and pub struct declarations
|
// Look for both regular and pub struct declarations
|
||||||
mut type_str := 'struct ${type_name} {'
|
mut type_str := 'struct ${type_name} {'
|
||||||
@@ -38,7 +35,8 @@ fn get_type_from_module(module_path string, type_name string) !string {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if i == -1 {
|
if i == -1 {
|
||||||
type_import := content.split_into_lines().filter(it.contains('import') && it.contains(type_name))
|
type_import := content.split_into_lines().filter(it.contains('import')
|
||||||
|
&& it.contains(type_name))
|
||||||
if type_import.len > 0 {
|
if type_import.len > 0 {
|
||||||
log.debug('debugzoooo')
|
log.debug('debugzoooo')
|
||||||
mod := type_import[0].trim_space().trim_string_left('import ').all_before(' ')
|
mod := type_import[0].trim_space().trim_string_left('import ').all_before(' ')
|
||||||
@@ -54,7 +52,7 @@ fn get_type_from_module(module_path string, type_name string) !string {
|
|||||||
|
|
||||||
// Find the start of the line containing the struct definition
|
// Find the start of the line containing the struct definition
|
||||||
for j := i; j >= 0; j-- {
|
for j := i; j >= 0; j-- {
|
||||||
if j == 0 || content[j-1] == `\n` {
|
if j == 0 || content[j - 1] == `\n` {
|
||||||
line_start = j
|
line_start = j
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
@@ -134,9 +132,6 @@ fn find_closing_brace(content string, start_i int) ?int {
|
|||||||
return none
|
return none
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
// get_function_from_file parses a V file and extracts a specific function block including its comments
|
// get_function_from_file parses a V file and extracts a specific function block including its comments
|
||||||
// ARGS:
|
// ARGS:
|
||||||
// file_path string - path to the V file
|
// file_path string - path to the V file
|
||||||
@@ -167,8 +162,8 @@ fn get_function_from_file(file_path string, function_name string) !string {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Check if we found the function
|
// Check if we found the function
|
||||||
if !in_function && (trimmed.starts_with('fn ${function_name}(') ||
|
if !in_function && (trimmed.starts_with('fn ${function_name}(')
|
||||||
trimmed.starts_with('pub fn ${function_name}(')) {
|
|| trimmed.starts_with('pub fn ${function_name}(')) {
|
||||||
in_function = true
|
in_function = true
|
||||||
// Add collected comments
|
// Add collected comments
|
||||||
result << comment_block
|
result << comment_block
|
||||||
@@ -213,9 +208,7 @@ fn get_function_from_file(file_path string, function_name string) !string {
|
|||||||
|
|
||||||
// list_v_files returns all .v files in a directory (non-recursive), excluding generated files ending with _.v
|
// list_v_files returns all .v files in a directory (non-recursive), excluding generated files ending with _.v
|
||||||
fn list_v_files(dir string) ![]string {
|
fn list_v_files(dir string) ![]string {
|
||||||
files := os.ls(dir) or {
|
files := os.ls(dir) or { return error('Error listing directory: ${err}') }
|
||||||
return error('Error listing directory: $err')
|
|
||||||
}
|
|
||||||
|
|
||||||
mut v_files := []string{}
|
mut v_files := []string{}
|
||||||
for file in files {
|
for file in files {
|
||||||
@@ -230,47 +223,44 @@ fn list_v_files(dir string) ![]string {
|
|||||||
|
|
||||||
// test runs v test on the specified file or directory
|
// test runs v test on the specified file or directory
|
||||||
pub fn vtest(fullpath string) !string {
|
pub fn vtest(fullpath string) !string {
|
||||||
logger.info('test $fullpath')
|
logger.info('test ${fullpath}')
|
||||||
if !os.exists(fullpath) {
|
if !os.exists(fullpath) {
|
||||||
return error('File or directory does not exist: $fullpath')
|
return error('File or directory does not exist: ${fullpath}')
|
||||||
}
|
}
|
||||||
if os.is_dir(fullpath) {
|
if os.is_dir(fullpath) {
|
||||||
mut results:=""
|
mut results := ''
|
||||||
for item in list_v_files(fullpath)!{
|
for item in list_v_files(fullpath)! {
|
||||||
results += vtest(item)!
|
results += vtest(item)!
|
||||||
results += '\n-----------------------\n'
|
results += '\n-----------------------\n'
|
||||||
}
|
}
|
||||||
return results
|
return results
|
||||||
}else{
|
} else {
|
||||||
cmd := 'v -gc none -stats -enable-globals -show-c-output -keepc -n -w -cg -o /tmp/tester.c -g -cc tcc test ${fullpath}'
|
cmd := 'v -gc none -stats -enable-globals -show-c-output -keepc -n -w -cg -o /tmp/tester.c -g -cc tcc test ${fullpath}'
|
||||||
logger.debug('Executing command: $cmd')
|
logger.debug('Executing command: ${cmd}')
|
||||||
result := os.execute(cmd)
|
result := os.execute(cmd)
|
||||||
if result.exit_code != 0 {
|
if result.exit_code != 0 {
|
||||||
return error('Test failed for $fullpath with exit code ${result.exit_code}\n${result.output}')
|
return error('Test failed for ${fullpath} with exit code ${result.exit_code}\n${result.output}')
|
||||||
} else {
|
} else {
|
||||||
logger.info('Test completed for $fullpath')
|
logger.info('Test completed for ${fullpath}')
|
||||||
}
|
}
|
||||||
return 'Command: $cmd\nExit code: ${result.exit_code}\nOutput:\n${result.output}'
|
return 'Command: ${cmd}\nExit code: ${result.exit_code}\nOutput:\n${result.output}'
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
// vvet runs v vet on the specified file or directory
|
// vvet runs v vet on the specified file or directory
|
||||||
pub fn vvet(fullpath string) !string {
|
pub fn vvet(fullpath string) !string {
|
||||||
logger.info('vet $fullpath')
|
logger.info('vet ${fullpath}')
|
||||||
if !os.exists(fullpath) {
|
if !os.exists(fullpath) {
|
||||||
return error('File or directory does not exist: $fullpath')
|
return error('File or directory does not exist: ${fullpath}')
|
||||||
}
|
}
|
||||||
|
|
||||||
if os.is_dir(fullpath) {
|
if os.is_dir(fullpath) {
|
||||||
mut results := ""
|
mut results := ''
|
||||||
files := list_v_files(fullpath) or {
|
files := list_v_files(fullpath) or { return error('Error listing V files: ${err}') }
|
||||||
return error('Error listing V files: $err')
|
|
||||||
}
|
|
||||||
for file in files {
|
for file in files {
|
||||||
results += vet_file(file) or {
|
results += vet_file(file) or {
|
||||||
logger.error('Failed to vet $file: $err')
|
logger.error('Failed to vet ${file}: ${err}')
|
||||||
return error('Failed to vet $file: $err')
|
return error('Failed to vet ${file}: ${err}')
|
||||||
}
|
}
|
||||||
results += '\n-----------------------\n'
|
results += '\n-----------------------\n'
|
||||||
}
|
}
|
||||||
@@ -283,14 +273,14 @@ pub fn vvet(fullpath string) !string {
|
|||||||
// vet_file runs v vet on a single file
|
// vet_file runs v vet on a single file
|
||||||
fn vet_file(file string) !string {
|
fn vet_file(file string) !string {
|
||||||
cmd := 'v vet -v -w ${file}'
|
cmd := 'v vet -v -w ${file}'
|
||||||
logger.debug('Executing command: $cmd')
|
logger.debug('Executing command: ${cmd}')
|
||||||
result := os.execute(cmd)
|
result := os.execute(cmd)
|
||||||
if result.exit_code != 0 {
|
if result.exit_code != 0 {
|
||||||
return error('Vet failed for $file with exit code ${result.exit_code}\n${result.output}')
|
return error('Vet failed for ${file} with exit code ${result.exit_code}\n${result.output}')
|
||||||
} else {
|
} else {
|
||||||
logger.info('Vet completed for $file')
|
logger.info('Vet completed for ${file}')
|
||||||
}
|
}
|
||||||
return 'Command: $cmd\nExit code: ${result.exit_code}\nOutput:\n${result.output}'
|
return 'Command: ${cmd}\nExit code: ${result.exit_code}\nOutput:\n${result.output}'
|
||||||
}
|
}
|
||||||
|
|
||||||
// cmd := 'v -gc none -stats -enable-globals -show-c-output -keepc -n -w -cg -o /tmp/tester.c -g -cc tcc ${fullpath}'
|
// cmd := 'v -gc none -stats -enable-globals -show-c-output -keepc -n -w -cg -o /tmp/tester.c -g -cc tcc ${fullpath}'
|
||||||
@@ -14,7 +14,7 @@ fn create_test_files() !(string, string, string) {
|
|||||||
test_file_path := os.join_path(test_dir, 'test_type.v')
|
test_file_path := os.join_path(test_dir, 'test_type.v')
|
||||||
|
|
||||||
// Create a test file with a simple struct
|
// Create a test file with a simple struct
|
||||||
test_content := "module test_module
|
test_content := 'module test_module
|
||||||
|
|
||||||
struct TestType {
|
struct TestType {
|
||||||
name string
|
name string
|
||||||
@@ -26,14 +26,14 @@ struct TestType {
|
|||||||
struct OtherType {
|
struct OtherType {
|
||||||
id string
|
id string
|
||||||
}
|
}
|
||||||
"
|
'
|
||||||
os.write_file(test_file_path, test_content) or {
|
os.write_file(test_file_path, test_content) or {
|
||||||
eprintln('Failed to create test file: $err')
|
eprintln('Failed to create test file: ${err}')
|
||||||
return error('Failed to create test file: $err')
|
return error('Failed to create test file: ${err}')
|
||||||
}
|
}
|
||||||
|
|
||||||
// Create a test file with a nested struct
|
// Create a test file with a nested struct
|
||||||
nested_test_content := "module test_module
|
nested_test_content := 'module test_module
|
||||||
|
|
||||||
struct NestedType {
|
struct NestedType {
|
||||||
config map[string]string {
|
config map[string]string {
|
||||||
@@ -44,11 +44,11 @@ struct NestedType {
|
|||||||
value string
|
value string
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
"
|
'
|
||||||
nested_test_file := os.join_path(test_dir, 'nested_test.v')
|
nested_test_file := os.join_path(test_dir, 'nested_test.v')
|
||||||
os.write_file(nested_test_file, nested_test_content) or {
|
os.write_file(nested_test_file, nested_test_content) or {
|
||||||
eprintln('Failed to create nested test file: $err')
|
eprintln('Failed to create nested test file: ${err}')
|
||||||
return error('Failed to create nested test file: $err')
|
return error('Failed to create nested test file: ${err}')
|
||||||
}
|
}
|
||||||
|
|
||||||
return test_dir, test_file_path, nested_test_file
|
return test_dir, test_file_path, nested_test_file
|
||||||
@@ -58,21 +58,21 @@ struct NestedType {
|
|||||||
fn test_get_type_from_module() {
|
fn test_get_type_from_module() {
|
||||||
// Create test files
|
// Create test files
|
||||||
test_dir, test_file_path, nested_test_file := create_test_files() or {
|
test_dir, test_file_path, nested_test_file := create_test_files() or {
|
||||||
eprintln('Failed to create test files: $err')
|
eprintln('Failed to create test files: ${err}')
|
||||||
assert false
|
assert false
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// Test case 1: Get a simple struct
|
// Test case 1: Get a simple struct
|
||||||
type_content := get_type_from_module(test_dir, 'TestType') or {
|
type_content := get_type_from_module(test_dir, 'TestType') or {
|
||||||
eprintln('Failed to get type: $err')
|
eprintln('Failed to get type: ${err}')
|
||||||
assert false
|
assert false
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// Verify the content matches what we expect
|
// Verify the content matches what we expect
|
||||||
expected := "\n\tname string\n\tage int\n\tactive bool\n}"
|
expected := '\n\tname string\n\tage int\n\tactive bool\n}'
|
||||||
assert type_content == expected, 'Expected: "$expected", got: "$type_content"'
|
assert type_content == expected, 'Expected: "${expected}", got: "${type_content}"'
|
||||||
|
|
||||||
// Test case 2: Try to get a non-existent type
|
// Test case 2: Try to get a non-existent type
|
||||||
non_existent := get_type_from_module(test_dir, 'NonExistentType') or {
|
non_existent := get_type_from_module(test_dir, 'NonExistentType') or {
|
||||||
@@ -84,13 +84,13 @@ fn test_get_type_from_module() {
|
|||||||
|
|
||||||
// Test case 3: Test with nested braces in the struct
|
// Test case 3: Test with nested braces in the struct
|
||||||
nested_type_content := get_type_from_module(test_dir, 'NestedType') or {
|
nested_type_content := get_type_from_module(test_dir, 'NestedType') or {
|
||||||
eprintln('Failed to get nested type: $err')
|
eprintln('Failed to get nested type: ${err}')
|
||||||
assert false
|
assert false
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
expected_nested := "\n\tconfig map[string]string {\n\t\trequired: true\n\t}\n\tdata []struct {\n\t\tkey string\n\t\tvalue string\n\t}\n}"
|
expected_nested := '\n\tconfig map[string]string {\n\t\trequired: true\n\t}\n\tdata []struct {\n\t\tkey string\n\t\tvalue string\n\t}\n}'
|
||||||
assert nested_type_content == expected_nested, 'Expected: "$expected_nested", got: "$nested_type_content"'
|
assert nested_type_content == expected_nested, 'Expected: "${expected_nested}", got: "${nested_type_content}"'
|
||||||
|
|
||||||
// Clean up test files
|
// Clean up test files
|
||||||
os.rm(test_file_path) or {}
|
os.rm(test_file_path) or {}
|
||||||
34
TOSORT/developer/vlang_tools.v
Normal file
34
TOSORT/developer/vlang_tools.v
Normal file
@@ -0,0 +1,34 @@
|
|||||||
|
module developer
|
||||||
|
|
||||||
|
import freeflowuniverse.herolib.mcp
|
||||||
|
|
||||||
|
const get_function_from_file_tool = mcp.Tool{
|
||||||
|
name: 'get_function_from_file'
|
||||||
|
description: 'get_function_from_file parses a V file and extracts a specific function block including its comments
|
||||||
|
ARGS:
|
||||||
|
file_path string - path to the V file
|
||||||
|
function_name string - name of the function to extract
|
||||||
|
RETURNS: string - the function block including comments, or empty string if not found'
|
||||||
|
input_schema: mcp.ToolInputSchema{
|
||||||
|
typ: 'object'
|
||||||
|
properties: {
|
||||||
|
'file_path': mcp.ToolProperty{
|
||||||
|
typ: 'string'
|
||||||
|
items: mcp.ToolItems{
|
||||||
|
typ: ''
|
||||||
|
enum: []
|
||||||
|
}
|
||||||
|
enum: []
|
||||||
|
}
|
||||||
|
'function_name': mcp.ToolProperty{
|
||||||
|
typ: 'string'
|
||||||
|
items: mcp.ToolItems{
|
||||||
|
typ: ''
|
||||||
|
enum: []
|
||||||
|
}
|
||||||
|
enum: []
|
||||||
|
}
|
||||||
|
}
|
||||||
|
required: ['file_path', 'function_name']
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -46,17 +46,17 @@ fn main() {
|
|||||||
println('\nTraining a classifier:')
|
println('\nTraining a classifier:')
|
||||||
examples := [
|
examples := [
|
||||||
jina.TrainingExample{
|
jina.TrainingExample{
|
||||||
text: 'This movie was great!'
|
text: 'This movie was great!'
|
||||||
label: 'positive'
|
label: 'positive'
|
||||||
},
|
},
|
||||||
jina.TrainingExample{
|
jina.TrainingExample{
|
||||||
text: 'I did not like this movie.'
|
text: 'I did not like this movie.'
|
||||||
label: 'negative'
|
label: 'negative'
|
||||||
},
|
},
|
||||||
jina.TrainingExample{
|
jina.TrainingExample{
|
||||||
text: 'The movie was okay.'
|
text: 'The movie was okay.'
|
||||||
label: 'neutral'
|
label: 'neutral'
|
||||||
}
|
},
|
||||||
]
|
]
|
||||||
|
|
||||||
training_result := client.train(examples, model, 'private') or {
|
training_result := client.train(examples, model, 'private') or {
|
||||||
|
|||||||
@@ -12,12 +12,11 @@ const openrpc_spec_path = os.join_path(example_dir, 'openrpc.json')
|
|||||||
openrpc_spec := openrpc.new(path: openrpc_spec_path)!
|
openrpc_spec := openrpc.new(path: openrpc_spec_path)!
|
||||||
actor_spec := specification.from_openrpc(openrpc_spec)!
|
actor_spec := specification.from_openrpc(openrpc_spec)!
|
||||||
|
|
||||||
actor_module := generator.generate_actor_module(
|
actor_module := generator.generate_actor_module(actor_spec,
|
||||||
actor_spec,
|
|
||||||
interfaces: [.openrpc]
|
interfaces: [.openrpc]
|
||||||
)!
|
)!
|
||||||
|
|
||||||
actor_module.write(example_dir,
|
actor_module.write(example_dir,
|
||||||
format: true
|
format: true
|
||||||
overwrite: true
|
overwrite: true
|
||||||
)!
|
)!
|
||||||
@@ -14,6 +14,6 @@ actor_spec := specification.from_openrpc(openrpc_spec)!
|
|||||||
|
|
||||||
methods_file := generator.generate_methods_file(actor_spec)!
|
methods_file := generator.generate_methods_file(actor_spec)!
|
||||||
methods_file.write(example_dir,
|
methods_file.write(example_dir,
|
||||||
format: true
|
format: true
|
||||||
overwrite: true
|
overwrite: true
|
||||||
)!
|
)!
|
||||||
@@ -14,6 +14,6 @@ actor_spec := specification.from_openrpc(openrpc_spec_)!
|
|||||||
openrpc_spec := actor_spec.to_openrpc()
|
openrpc_spec := actor_spec.to_openrpc()
|
||||||
|
|
||||||
openrpc_file := generator.generate_openrpc_file(openrpc_spec)!
|
openrpc_file := generator.generate_openrpc_file(openrpc_spec)!
|
||||||
openrpc_file.write(os.join_path(example_dir,'docs'),
|
openrpc_file.write(os.join_path(example_dir, 'docs'),
|
||||||
overwrite: true
|
overwrite: true
|
||||||
)!
|
)!
|
||||||
@@ -5,7 +5,6 @@ import freeflowuniverse.herolib.baobab.specification
|
|||||||
import freeflowuniverse.herolib.schemas.openapi
|
import freeflowuniverse.herolib.schemas.openapi
|
||||||
import os
|
import os
|
||||||
|
|
||||||
|
|
||||||
const example_dir = os.dir(@FILE)
|
const example_dir = os.dir(@FILE)
|
||||||
const specs = ['merchant', 'profiler', 'farmer']
|
const specs = ['merchant', 'profiler', 'farmer']
|
||||||
|
|
||||||
@@ -13,13 +12,12 @@ for spec in specs {
|
|||||||
openapi_spec_path := os.join_path(example_dir, '${spec}.json')
|
openapi_spec_path := os.join_path(example_dir, '${spec}.json')
|
||||||
openapi_spec := openapi.new(path: openapi_spec_path, process: true)!
|
openapi_spec := openapi.new(path: openapi_spec_path, process: true)!
|
||||||
actor_spec := specification.from_openapi(openapi_spec)!
|
actor_spec := specification.from_openapi(openapi_spec)!
|
||||||
actor_module := generator.generate_actor_folder(
|
actor_module := generator.generate_actor_folder(actor_spec,
|
||||||
actor_spec,
|
|
||||||
interfaces: [.openapi, .http]
|
interfaces: [.openapi, .http]
|
||||||
)!
|
)!
|
||||||
actor_module.write(example_dir,
|
actor_module.write(example_dir,
|
||||||
format: true
|
format: true
|
||||||
overwrite: true
|
overwrite: true
|
||||||
compile: false
|
compile: false
|
||||||
)!
|
)!
|
||||||
}
|
}
|
||||||
@@ -15,67 +15,67 @@ pub:
|
|||||||
name string
|
name string
|
||||||
description string
|
description string
|
||||||
// technical specifications
|
// technical specifications
|
||||||
specs map[string]string
|
specs map[string]string
|
||||||
// price per unit
|
// price per unit
|
||||||
price f64
|
price f64
|
||||||
// currency code (e.g., 'USD', 'EUR')
|
// currency code (e.g., 'USD', 'EUR')
|
||||||
currency string
|
currency string
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct ProductTemplate {
|
pub struct ProductTemplate {
|
||||||
pub:
|
pub:
|
||||||
id string
|
id string
|
||||||
name string
|
name string
|
||||||
description string
|
description string
|
||||||
// components that make up this product template
|
// components that make up this product template
|
||||||
components []ProductComponentTemplate
|
components []ProductComponentTemplate
|
||||||
// merchant who created this template
|
// merchant who created this template
|
||||||
merchant_id string
|
merchant_id string
|
||||||
// category of the product (e.g., 'electronics', 'clothing')
|
// category of the product (e.g., 'electronics', 'clothing')
|
||||||
category string
|
category string
|
||||||
// whether this template is available for use
|
// whether this template is available for use
|
||||||
active bool
|
active bool
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct Product {
|
pub struct Product {
|
||||||
pub:
|
pub:
|
||||||
id string
|
id string
|
||||||
template_id string
|
template_id string
|
||||||
// specific instance details that may differ from template
|
// specific instance details that may differ from template
|
||||||
name string
|
name string
|
||||||
description string
|
description string
|
||||||
// actual price of this product instance
|
// actual price of this product instance
|
||||||
price f64
|
price f64
|
||||||
currency string
|
currency string
|
||||||
// merchant selling this product
|
// merchant selling this product
|
||||||
merchant_id string
|
merchant_id string
|
||||||
// current stock level
|
// current stock level
|
||||||
stock_quantity int
|
stock_quantity int
|
||||||
// whether this product is available for purchase
|
// whether this product is available for purchase
|
||||||
available bool
|
available bool
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct OrderItem {
|
pub struct OrderItem {
|
||||||
pub:
|
pub:
|
||||||
product_id string
|
product_id string
|
||||||
quantity int
|
quantity int
|
||||||
price f64
|
price f64
|
||||||
currency string
|
currency string
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct Order {
|
pub struct Order {
|
||||||
pub:
|
pub:
|
||||||
id string
|
id string
|
||||||
// customer identifier
|
// customer identifier
|
||||||
customer_id string
|
customer_id string
|
||||||
// items in the order
|
// items in the order
|
||||||
items []OrderItem
|
items []OrderItem
|
||||||
// total order amount
|
// total order amount
|
||||||
total_amount f64
|
total_amount f64
|
||||||
currency string
|
currency string
|
||||||
// order status (e.g., 'pending', 'confirmed', 'shipped', 'delivered')
|
// order status (e.g., 'pending', 'confirmed', 'shipped', 'delivered')
|
||||||
status string
|
status string
|
||||||
// timestamps
|
// timestamps
|
||||||
created_at string
|
created_at string
|
||||||
updated_at string
|
updated_at string
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,7 +3,7 @@ module geomind_poc
|
|||||||
import freeflowuniverse.crystallib.core.playbook { PlayBook }
|
import freeflowuniverse.crystallib.core.playbook { PlayBook }
|
||||||
|
|
||||||
// play_commerce processes heroscript actions for the commerce system
|
// play_commerce processes heroscript actions for the commerce system
|
||||||
pub fn play_commerce(mut plbook playbook.PlayBook) ! {
|
pub fn play_commerce(mut plbook PlayBook) ! {
|
||||||
commerce_actions := plbook.find(filter: 'commerce.')!
|
commerce_actions := plbook.find(filter: 'commerce.')!
|
||||||
mut c := Commerce{}
|
mut c := Commerce{}
|
||||||
|
|
||||||
@@ -12,20 +12,20 @@ pub fn play_commerce(mut plbook playbook.PlayBook) ! {
|
|||||||
'merchant' {
|
'merchant' {
|
||||||
mut p := action.params
|
mut p := action.params
|
||||||
merchant := c.create_merchant(
|
merchant := c.create_merchant(
|
||||||
name: p.get('name')!,
|
name: p.get('name')!
|
||||||
description: p.get_default('description', '')!,
|
description: p.get_default('description', '')!
|
||||||
contact: p.get('contact')!
|
contact: p.get('contact')!
|
||||||
)!
|
)!
|
||||||
println('Created merchant: ${merchant.name}')
|
println('Created merchant: ${merchant.name}')
|
||||||
}
|
}
|
||||||
'component' {
|
'component' {
|
||||||
mut p := action.params
|
mut p := action.params
|
||||||
component := c.create_product_component_template(
|
component := c.create_product_component_template(
|
||||||
name: p.get('name')!,
|
name: p.get('name')!
|
||||||
description: p.get_default('description', '')!,
|
description: p.get_default('description', '')!
|
||||||
specs: p.get_map(),
|
specs: p.get_map()
|
||||||
price: p.get_float('price')!,
|
price: p.get_float('price')!
|
||||||
currency: p.get('currency')!
|
currency: p.get('currency')!
|
||||||
)!
|
)!
|
||||||
println('Created component: ${component.name}')
|
println('Created component: ${component.name}')
|
||||||
}
|
}
|
||||||
@@ -39,30 +39,30 @@ pub fn play_commerce(mut plbook playbook.PlayBook) ! {
|
|||||||
// In a real implementation, you would fetch the component from storage
|
// In a real implementation, you would fetch the component from storage
|
||||||
// For this example, we create a dummy component
|
// For this example, we create a dummy component
|
||||||
component := ProductComponentTemplate{
|
component := ProductComponentTemplate{
|
||||||
id: id
|
id: id
|
||||||
name: 'Component'
|
name: 'Component'
|
||||||
description: ''
|
description: ''
|
||||||
specs: map[string]string{}
|
specs: map[string]string{}
|
||||||
price: 0
|
price: 0
|
||||||
currency: 'USD'
|
currency: 'USD'
|
||||||
}
|
}
|
||||||
components << component
|
components << component
|
||||||
}
|
}
|
||||||
|
|
||||||
template := c.create_product_template(
|
template := c.create_product_template(
|
||||||
name: p.get('name')!,
|
name: p.get('name')!
|
||||||
description: p.get_default('description', '')!,
|
description: p.get_default('description', '')!
|
||||||
components: components,
|
components: components
|
||||||
merchant_id: p.get('merchant_id')!,
|
merchant_id: p.get('merchant_id')!
|
||||||
category: p.get_default('category', 'General')!
|
category: p.get_default('category', 'General')!
|
||||||
)!
|
)!
|
||||||
println('Created template: ${template.name}')
|
println('Created template: ${template.name}')
|
||||||
}
|
}
|
||||||
'product' {
|
'product' {
|
||||||
mut p := action.params
|
mut p := action.params
|
||||||
product := c.create_product(
|
product := c.create_product(
|
||||||
template_id: p.get('template_id')!,
|
template_id: p.get('template_id')!
|
||||||
merchant_id: p.get('merchant_id')!,
|
merchant_id: p.get('merchant_id')!
|
||||||
stock_quantity: p.get_int('stock_quantity')!
|
stock_quantity: p.get_int('stock_quantity')!
|
||||||
)!
|
)!
|
||||||
println('Created product: ${product.name} with stock: ${product.stock_quantity}')
|
println('Created product: ${product.name} with stock: ${product.stock_quantity}')
|
||||||
@@ -80,23 +80,23 @@ pub fn play_commerce(mut plbook playbook.PlayBook) ! {
|
|||||||
}
|
}
|
||||||
item := OrderItem{
|
item := OrderItem{
|
||||||
product_id: parts[0]
|
product_id: parts[0]
|
||||||
quantity: parts[1].int()
|
quantity: parts[1].int()
|
||||||
price: parts[2].f64()
|
price: parts[2].f64()
|
||||||
currency: parts[3]
|
currency: parts[3]
|
||||||
}
|
}
|
||||||
items << item
|
items << item
|
||||||
}
|
}
|
||||||
|
|
||||||
order := c.create_order(
|
order := c.create_order(
|
||||||
customer_id: p.get('customer_id')!,
|
customer_id: p.get('customer_id')!
|
||||||
items: items
|
items: items
|
||||||
)!
|
)!
|
||||||
println('Created order: ${order.id} with ${order.items.len} items')
|
println('Created order: ${order.id} with ${order.items.len} items')
|
||||||
}
|
}
|
||||||
'update_order' {
|
'update_order' {
|
||||||
mut p := action.params
|
mut p := action.params
|
||||||
order := c.update_order_status(
|
order := c.update_order_status(
|
||||||
order_id: p.get('order_id')!,
|
order_id: p.get('order_id')!
|
||||||
new_status: p.get('status')!
|
new_status: p.get('status')!
|
||||||
)!
|
)!
|
||||||
println('Updated order ${order.id} status to: ${order.status}')
|
println('Updated order ${order.id} status to: ${order.status}')
|
||||||
|
|||||||
@@ -1,14 +1,15 @@
|
|||||||
module geomind_poc
|
module geomind_poc
|
||||||
|
|
||||||
import crypto.rand
|
import crypto.rand
|
||||||
|
import time
|
||||||
|
|
||||||
// Commerce represents the main e-commerce server handling all operations
|
// Commerce represents the main e-commerce server handling all operations
|
||||||
pub struct Commerce {
|
pub struct Commerce {
|
||||||
mut:
|
mut:
|
||||||
merchants map[string]Merchant
|
merchants map[string]Merchant
|
||||||
templates map[string]ProductTemplate
|
templates map[string]ProductTemplate
|
||||||
products map[string]Product
|
products map[string]Product
|
||||||
orders map[string]Order
|
orders map[string]Order
|
||||||
}
|
}
|
||||||
|
|
||||||
// generate_id creates a unique identifier
|
// generate_id creates a unique identifier
|
||||||
@@ -20,11 +21,11 @@ fn generate_id() string {
|
|||||||
pub fn (mut c Commerce) create_merchant(name string, description string, contact string) !Merchant {
|
pub fn (mut c Commerce) create_merchant(name string, description string, contact string) !Merchant {
|
||||||
merchant_id := generate_id()
|
merchant_id := generate_id()
|
||||||
merchant := Merchant{
|
merchant := Merchant{
|
||||||
id: merchant_id
|
id: merchant_id
|
||||||
name: name
|
name: name
|
||||||
description: description
|
description: description
|
||||||
contact: contact
|
contact: contact
|
||||||
active: true
|
active: true
|
||||||
}
|
}
|
||||||
c.merchants[merchant_id] = merchant
|
c.merchants[merchant_id] = merchant
|
||||||
return merchant
|
return merchant
|
||||||
@@ -33,12 +34,12 @@ pub fn (mut c Commerce) create_merchant(name string, description string, contact
|
|||||||
// create_product_component_template creates a new component template
|
// create_product_component_template creates a new component template
|
||||||
pub fn (mut c Commerce) create_product_component_template(name string, description string, specs map[string]string, price f64, currency string) !ProductComponentTemplate {
|
pub fn (mut c Commerce) create_product_component_template(name string, description string, specs map[string]string, price f64, currency string) !ProductComponentTemplate {
|
||||||
component := ProductComponentTemplate{
|
component := ProductComponentTemplate{
|
||||||
id: generate_id()
|
id: generate_id()
|
||||||
name: name
|
name: name
|
||||||
description: description
|
description: description
|
||||||
specs: specs
|
specs: specs
|
||||||
price: price
|
price: price
|
||||||
currency: currency
|
currency: currency
|
||||||
}
|
}
|
||||||
return component
|
return component
|
||||||
}
|
}
|
||||||
@@ -50,13 +51,13 @@ pub fn (mut c Commerce) create_product_template(name string, description string,
|
|||||||
}
|
}
|
||||||
|
|
||||||
template := ProductTemplate{
|
template := ProductTemplate{
|
||||||
id: generate_id()
|
id: generate_id()
|
||||||
name: name
|
name: name
|
||||||
description: description
|
description: description
|
||||||
components: components
|
components: components
|
||||||
merchant_id: merchant_id
|
merchant_id: merchant_id
|
||||||
category: category
|
category: category
|
||||||
active: true
|
active: true
|
||||||
}
|
}
|
||||||
c.templates[template.id] = template
|
c.templates[template.id] = template
|
||||||
return template
|
return template
|
||||||
@@ -78,15 +79,15 @@ pub fn (mut c Commerce) create_product(template_id string, merchant_id string, s
|
|||||||
}
|
}
|
||||||
|
|
||||||
product := Product{
|
product := Product{
|
||||||
id: generate_id()
|
id: generate_id()
|
||||||
template_id: template_id
|
template_id: template_id
|
||||||
name: template.name
|
name: template.name
|
||||||
description: template.description
|
description: template.description
|
||||||
price: total_price
|
price: total_price
|
||||||
currency: template.components[0].currency // assuming all components use same currency
|
currency: template.components[0].currency // assuming all components use same currency
|
||||||
merchant_id: merchant_id
|
merchant_id: merchant_id
|
||||||
stock_quantity: stock_quantity
|
stock_quantity: stock_quantity
|
||||||
available: true
|
available: true
|
||||||
}
|
}
|
||||||
c.products[product.id] = product
|
c.products[product.id] = product
|
||||||
return product
|
return product
|
||||||
@@ -114,14 +115,14 @@ pub fn (mut c Commerce) create_order(customer_id string, items []OrderItem) !Ord
|
|||||||
}
|
}
|
||||||
|
|
||||||
order := Order{
|
order := Order{
|
||||||
id: generate_id()
|
id: generate_id()
|
||||||
customer_id: customer_id
|
customer_id: customer_id
|
||||||
items: items
|
items: items
|
||||||
total_amount: total_amount
|
total_amount: total_amount
|
||||||
currency: currency
|
currency: currency
|
||||||
status: 'pending'
|
status: 'pending'
|
||||||
created_at: time.now().str()
|
created_at: time.now().str()
|
||||||
updated_at: time.now().str()
|
updated_at: time.now().str()
|
||||||
}
|
}
|
||||||
c.orders[order.id] = order
|
c.orders[order.id] = order
|
||||||
|
|
||||||
|
|||||||
@@ -5,20 +5,21 @@ import freeflowuniverse.herolib.baobab.specification
|
|||||||
import freeflowuniverse.herolib.schemas.openapi
|
import freeflowuniverse.herolib.schemas.openapi
|
||||||
import os
|
import os
|
||||||
|
|
||||||
const example_dir = os.join_path('${os.home_dir()}/code/github/freeflowuniverse/herolib/lib/circles/mcc', 'baobab')
|
const example_dir = os.join_path('${os.home_dir()}/code/github/freeflowuniverse/herolib/lib/circles/mcc',
|
||||||
const openapi_spec_path = os.join_path('${os.home_dir()}/code/github/freeflowuniverse/herolib/lib/circles/mcc', 'openapi.json')
|
'baobab')
|
||||||
|
const openapi_spec_path = os.join_path('${os.home_dir()}/code/github/freeflowuniverse/herolib/lib/circles/mcc',
|
||||||
|
'openapi.json')
|
||||||
|
|
||||||
// the actor specification obtained from the OpenRPC Specification
|
// the actor specification obtained from the OpenRPC Specification
|
||||||
openapi_spec := openapi.new(path: openapi_spec_path)!
|
openapi_spec := openapi.new(path: openapi_spec_path)!
|
||||||
actor_spec := specification.from_openapi(openapi_spec)!
|
actor_spec := specification.from_openapi(openapi_spec)!
|
||||||
|
|
||||||
actor_module := generator.generate_actor_module(
|
actor_module := generator.generate_actor_module(actor_spec,
|
||||||
actor_spec,
|
|
||||||
interfaces: [.openapi, .http]
|
interfaces: [.openapi, .http]
|
||||||
)!
|
)!
|
||||||
|
|
||||||
actor_module.write(example_dir,
|
actor_module.write(example_dir,
|
||||||
format: true
|
format: true
|
||||||
overwrite: true
|
overwrite: true
|
||||||
compile: false
|
compile: false
|
||||||
)!
|
)!
|
||||||
@@ -14,15 +14,14 @@ actor_spec := specification.from_openapi(openapi_spec)!
|
|||||||
|
|
||||||
println(actor_spec)
|
println(actor_spec)
|
||||||
|
|
||||||
actor_module := generator.generate_actor_module(
|
actor_module := generator.generate_actor_module(actor_spec,
|
||||||
actor_spec,
|
|
||||||
interfaces: [.openapi, .http]
|
interfaces: [.openapi, .http]
|
||||||
)!
|
)!
|
||||||
|
|
||||||
actor_module.write(example_dir,
|
actor_module.write(example_dir,
|
||||||
format: false
|
format: false
|
||||||
overwrite: true
|
overwrite: true
|
||||||
compile: false
|
compile: false
|
||||||
)!
|
)!
|
||||||
|
|
||||||
// os.execvp('bash', ['${example_dir}/meeting_scheduler_actor/scripts/run.sh'])!
|
// os.execvp('bash', ['${example_dir}/meeting_scheduler_actor/scripts/run.sh'])!
|
||||||
@@ -7,100 +7,100 @@ import freeflowuniverse.herolib.schemas.openrpc
|
|||||||
import os
|
import os
|
||||||
|
|
||||||
const actor_specification = specification.ActorSpecification{
|
const actor_specification = specification.ActorSpecification{
|
||||||
name: 'PetStore'
|
name: 'PetStore'
|
||||||
interfaces: [.openrpc]
|
interfaces: [.openrpc]
|
||||||
methods: [
|
methods: [
|
||||||
specification.ActorMethod{
|
specification.ActorMethod{
|
||||||
name: 'GetPets'
|
name: 'GetPets'
|
||||||
description: 'finds pets in the system that the user has access to by tags and within a limit'
|
description: 'finds pets in the system that the user has access to by tags and within a limit'
|
||||||
parameters: [
|
parameters: [
|
||||||
openrpc.ContentDescriptor{
|
openrpc.ContentDescriptor{
|
||||||
name: 'tags'
|
name: 'tags'
|
||||||
description: 'tags to filter by'
|
description: 'tags to filter by'
|
||||||
schema: jsonschema.SchemaRef(jsonschema.Schema{
|
schema: jsonschema.SchemaRef(jsonschema.Schema{
|
||||||
typ: 'array'
|
typ: 'array'
|
||||||
items: jsonschema.Items(jsonschema.SchemaRef(jsonschema.Schema{
|
items: jsonschema.Items(jsonschema.SchemaRef(jsonschema.Schema{
|
||||||
typ: 'string'
|
typ: 'string'
|
||||||
}))
|
}))
|
||||||
})
|
})
|
||||||
},
|
},
|
||||||
openrpc.ContentDescriptor{
|
openrpc.ContentDescriptor{
|
||||||
name: 'limit'
|
name: 'limit'
|
||||||
description: 'maximum number of results to return'
|
description: 'maximum number of results to return'
|
||||||
schema: jsonschema.SchemaRef(jsonschema.Schema{
|
schema: jsonschema.SchemaRef(jsonschema.Schema{
|
||||||
typ: 'integer'
|
typ: 'integer'
|
||||||
})
|
})
|
||||||
}
|
},
|
||||||
]
|
]
|
||||||
result: openrpc.ContentDescriptor{
|
result: openrpc.ContentDescriptor{
|
||||||
name: 'pet_list'
|
name: 'pet_list'
|
||||||
description: 'all pets from the system, that matches the tags'
|
description: 'all pets from the system, that matches the tags'
|
||||||
schema: jsonschema.SchemaRef(jsonschema.Reference{
|
schema: jsonschema.SchemaRef(jsonschema.Reference{
|
||||||
ref: '#/components/schemas/Pet'
|
ref: '#/components/schemas/Pet'
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
specification.ActorMethod{
|
specification.ActorMethod{
|
||||||
name: 'CreatePet'
|
name: 'CreatePet'
|
||||||
description: 'creates a new pet in the store. Duplicates are allowed.'
|
description: 'creates a new pet in the store. Duplicates are allowed.'
|
||||||
parameters: [
|
parameters: [
|
||||||
openrpc.ContentDescriptor{
|
openrpc.ContentDescriptor{
|
||||||
name: 'new_pet'
|
name: 'new_pet'
|
||||||
description: 'Pet to add to the store.'
|
description: 'Pet to add to the store.'
|
||||||
schema: jsonschema.SchemaRef(jsonschema.Reference{
|
schema: jsonschema.SchemaRef(jsonschema.Reference{
|
||||||
ref: '#/components/schemas/NewPet'
|
ref: '#/components/schemas/NewPet'
|
||||||
})
|
})
|
||||||
}
|
},
|
||||||
]
|
]
|
||||||
result: openrpc.ContentDescriptor{
|
result: openrpc.ContentDescriptor{
|
||||||
name: 'pet'
|
name: 'pet'
|
||||||
description: 'the newly created pet'
|
description: 'the newly created pet'
|
||||||
schema: jsonschema.SchemaRef(jsonschema.Reference{
|
schema: jsonschema.SchemaRef(jsonschema.Reference{
|
||||||
ref: '#/components/schemas/Pet'
|
ref: '#/components/schemas/Pet'
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
specification.ActorMethod{
|
specification.ActorMethod{
|
||||||
name: 'GetPetById'
|
name: 'GetPetById'
|
||||||
description: 'gets a pet based on a single ID, if the user has access to the pet'
|
description: 'gets a pet based on a single ID, if the user has access to the pet'
|
||||||
parameters: [
|
parameters: [
|
||||||
openrpc.ContentDescriptor{
|
openrpc.ContentDescriptor{
|
||||||
name: 'id'
|
name: 'id'
|
||||||
description: 'ID of pet to fetch'
|
description: 'ID of pet to fetch'
|
||||||
schema: jsonschema.SchemaRef(jsonschema.Schema{
|
schema: jsonschema.SchemaRef(jsonschema.Schema{
|
||||||
typ: 'integer'
|
typ: 'integer'
|
||||||
})
|
})
|
||||||
}
|
},
|
||||||
]
|
]
|
||||||
result: openrpc.ContentDescriptor{
|
result: openrpc.ContentDescriptor{
|
||||||
name: 'pet'
|
name: 'pet'
|
||||||
description: 'pet response'
|
description: 'pet response'
|
||||||
schema: jsonschema.SchemaRef(jsonschema.Reference{
|
schema: jsonschema.SchemaRef(jsonschema.Reference{
|
||||||
ref: '#/components/schemas/Pet'
|
ref: '#/components/schemas/Pet'
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
specification.ActorMethod{
|
specification.ActorMethod{
|
||||||
name: 'DeletePetById'
|
name: 'DeletePetById'
|
||||||
description: 'deletes a single pet based on the ID supplied'
|
description: 'deletes a single pet based on the ID supplied'
|
||||||
parameters: [
|
parameters: [
|
||||||
openrpc.ContentDescriptor{
|
openrpc.ContentDescriptor{
|
||||||
name: 'id'
|
name: 'id'
|
||||||
description: 'ID of pet to delete'
|
description: 'ID of pet to delete'
|
||||||
schema: jsonschema.SchemaRef(jsonschema.Schema{
|
schema: jsonschema.SchemaRef(jsonschema.Schema{
|
||||||
typ: 'integer'
|
typ: 'integer'
|
||||||
})
|
})
|
||||||
}
|
},
|
||||||
]
|
]
|
||||||
result: openrpc.ContentDescriptor{
|
result: openrpc.ContentDescriptor{
|
||||||
name: 'pet'
|
name: 'pet'
|
||||||
description: 'pet deleted'
|
description: 'pet deleted'
|
||||||
schema: jsonschema.SchemaRef(jsonschema.Schema{
|
schema: jsonschema.SchemaRef(jsonschema.Schema{
|
||||||
typ: 'null'
|
typ: 'null'
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
},
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|
||||||
openapi_specification := actor_specification.to_openapi()
|
openapi_specification := actor_specification.to_openapi()
|
||||||
|
|||||||
@@ -8,101 +8,101 @@ import freeflowuniverse.herolib.schemas.openrpc
|
|||||||
import os
|
import os
|
||||||
|
|
||||||
const actor_specification = specification.ActorSpecification{
|
const actor_specification = specification.ActorSpecification{
|
||||||
name: 'PetStore'
|
name: 'PetStore'
|
||||||
structure: code.Struct{}
|
structure: code.Struct{}
|
||||||
interfaces: [.openrpc]
|
interfaces: [.openrpc]
|
||||||
methods: [
|
methods: [
|
||||||
specification.ActorMethod{
|
specification.ActorMethod{
|
||||||
name: 'GetPets'
|
name: 'GetPets'
|
||||||
description: 'finds pets in the system that the user has access to by tags and within a limit'
|
description: 'finds pets in the system that the user has access to by tags and within a limit'
|
||||||
parameters: [
|
parameters: [
|
||||||
openrpc.ContentDescriptor{
|
openrpc.ContentDescriptor{
|
||||||
name: 'tags'
|
name: 'tags'
|
||||||
description: 'tags to filter by'
|
description: 'tags to filter by'
|
||||||
schema: jsonschema.SchemaRef(jsonschema.Schema{
|
schema: jsonschema.SchemaRef(jsonschema.Schema{
|
||||||
typ: 'array'
|
typ: 'array'
|
||||||
items: jsonschema.Items(jsonschema.SchemaRef(jsonschema.Schema{
|
items: jsonschema.Items(jsonschema.SchemaRef(jsonschema.Schema{
|
||||||
typ: 'string'
|
typ: 'string'
|
||||||
}))
|
}))
|
||||||
})
|
})
|
||||||
},
|
},
|
||||||
openrpc.ContentDescriptor{
|
openrpc.ContentDescriptor{
|
||||||
name: 'limit'
|
name: 'limit'
|
||||||
description: 'maximum number of results to return'
|
description: 'maximum number of results to return'
|
||||||
schema: jsonschema.SchemaRef(jsonschema.Schema{
|
schema: jsonschema.SchemaRef(jsonschema.Schema{
|
||||||
typ: 'integer'
|
typ: 'integer'
|
||||||
})
|
})
|
||||||
}
|
},
|
||||||
]
|
]
|
||||||
result: openrpc.ContentDescriptor{
|
result: openrpc.ContentDescriptor{
|
||||||
name: 'pet_list'
|
name: 'pet_list'
|
||||||
description: 'all pets from the system, that matches the tags'
|
description: 'all pets from the system, that matches the tags'
|
||||||
schema: jsonschema.SchemaRef(jsonschema.Reference{
|
schema: jsonschema.SchemaRef(jsonschema.Reference{
|
||||||
ref: '#/components/schemas/Pet'
|
ref: '#/components/schemas/Pet'
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
specification.ActorMethod{
|
specification.ActorMethod{
|
||||||
name: 'CreatePet'
|
name: 'CreatePet'
|
||||||
description: 'creates a new pet in the store. Duplicates are allowed.'
|
description: 'creates a new pet in the store. Duplicates are allowed.'
|
||||||
parameters: [
|
parameters: [
|
||||||
openrpc.ContentDescriptor{
|
openrpc.ContentDescriptor{
|
||||||
name: 'new_pet'
|
name: 'new_pet'
|
||||||
description: 'Pet to add to the store.'
|
description: 'Pet to add to the store.'
|
||||||
schema: jsonschema.SchemaRef(jsonschema.Reference{
|
schema: jsonschema.SchemaRef(jsonschema.Reference{
|
||||||
ref: '#/components/schemas/NewPet'
|
ref: '#/components/schemas/NewPet'
|
||||||
})
|
})
|
||||||
}
|
},
|
||||||
]
|
]
|
||||||
result: openrpc.ContentDescriptor{
|
result: openrpc.ContentDescriptor{
|
||||||
name: 'pet'
|
name: 'pet'
|
||||||
description: 'the newly created pet'
|
description: 'the newly created pet'
|
||||||
schema: jsonschema.SchemaRef(jsonschema.Reference{
|
schema: jsonschema.SchemaRef(jsonschema.Reference{
|
||||||
ref: '#/components/schemas/Pet'
|
ref: '#/components/schemas/Pet'
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
specification.ActorMethod{
|
specification.ActorMethod{
|
||||||
name: 'GetPetById'
|
name: 'GetPetById'
|
||||||
description: 'gets a pet based on a single ID, if the user has access to the pet'
|
description: 'gets a pet based on a single ID, if the user has access to the pet'
|
||||||
parameters: [
|
parameters: [
|
||||||
openrpc.ContentDescriptor{
|
openrpc.ContentDescriptor{
|
||||||
name: 'id'
|
name: 'id'
|
||||||
description: 'ID of pet to fetch'
|
description: 'ID of pet to fetch'
|
||||||
schema: jsonschema.SchemaRef(jsonschema.Schema{
|
schema: jsonschema.SchemaRef(jsonschema.Schema{
|
||||||
typ: 'integer'
|
typ: 'integer'
|
||||||
})
|
})
|
||||||
}
|
},
|
||||||
]
|
]
|
||||||
result: openrpc.ContentDescriptor{
|
result: openrpc.ContentDescriptor{
|
||||||
name: 'pet'
|
name: 'pet'
|
||||||
description: 'pet response'
|
description: 'pet response'
|
||||||
schema: jsonschema.SchemaRef(jsonschema.Reference{
|
schema: jsonschema.SchemaRef(jsonschema.Reference{
|
||||||
ref: '#/components/schemas/Pet'
|
ref: '#/components/schemas/Pet'
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
specification.ActorMethod{
|
specification.ActorMethod{
|
||||||
name: 'DeletePetById'
|
name: 'DeletePetById'
|
||||||
description: 'deletes a single pet based on the ID supplied'
|
description: 'deletes a single pet based on the ID supplied'
|
||||||
parameters: [
|
parameters: [
|
||||||
openrpc.ContentDescriptor{
|
openrpc.ContentDescriptor{
|
||||||
name: 'id'
|
name: 'id'
|
||||||
description: 'ID of pet to delete'
|
description: 'ID of pet to delete'
|
||||||
schema: jsonschema.SchemaRef(jsonschema.Schema{
|
schema: jsonschema.SchemaRef(jsonschema.Schema{
|
||||||
typ: 'integer'
|
typ: 'integer'
|
||||||
})
|
})
|
||||||
}
|
},
|
||||||
]
|
]
|
||||||
result: openrpc.ContentDescriptor{
|
result: openrpc.ContentDescriptor{
|
||||||
name: 'pet'
|
name: 'pet'
|
||||||
description: 'pet deleted'
|
description: 'pet deleted'
|
||||||
schema: jsonschema.SchemaRef(jsonschema.Schema{
|
schema: jsonschema.SchemaRef(jsonschema.Schema{
|
||||||
typ: 'null'
|
typ: 'null'
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
},
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|
||||||
openrpc_specification := actor_specification.to_openrpc()
|
openrpc_specification := actor_specification.to_openrpc()
|
||||||
|
|||||||
@@ -8,13 +8,10 @@ const build_path = os.join_path(os.dir(@FILE), '/docusaurus')
|
|||||||
|
|
||||||
buildpath := '${os.home_dir()}/hero/var/mdbuild/bizmodel'
|
buildpath := '${os.home_dir()}/hero/var/mdbuild/bizmodel'
|
||||||
|
|
||||||
mut model := bizmodel.generate("test", playbook_path)!
|
mut model := bizmodel.generate('test', playbook_path)!
|
||||||
|
|
||||||
println(model.sheet)
|
println(model.sheet)
|
||||||
println(model.sheet.export()!)
|
println(model.sheet.export()!)
|
||||||
|
|
||||||
model.sheet.export(path:"~/Downloads/test.csv")!
|
model.sheet.export(path: '~/Downloads/test.csv')!
|
||||||
model.sheet.export(path:"~/code/github/freeflowuniverse/starlight_template/src/content/test.csv")!
|
model.sheet.export(path: '~/code/github/freeflowuniverse/starlight_template/src/content/test.csv')!
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -6,13 +6,13 @@ import freeflowuniverse.herolib.core.playbook
|
|||||||
import freeflowuniverse.herolib.core.playcmds
|
import freeflowuniverse.herolib.core.playcmds
|
||||||
import os
|
import os
|
||||||
|
|
||||||
//TODO: need to fix wrong location
|
// TODO: need to fix wrong location
|
||||||
const playbook_path = os.dir(@FILE) + '/playbook'
|
const playbook_path = os.dir(@FILE) + '/playbook'
|
||||||
const build_path = os.join_path(os.dir(@FILE), '/docusaurus')
|
const build_path = os.join_path(os.dir(@FILE), '/docusaurus')
|
||||||
|
|
||||||
buildpath := '${os.home_dir()}/hero/var/mdbuild/bizmodel'
|
buildpath := '${os.home_dir()}/hero/var/mdbuild/bizmodel'
|
||||||
|
|
||||||
mut model := bizmodel.getset("example")!
|
mut model := bizmodel.getset('example')!
|
||||||
model.workdir = build_path
|
model.workdir = build_path
|
||||||
model.play(mut playbook.new(path: playbook_path)!)!
|
model.play(mut playbook.new(path: playbook_path)!)!
|
||||||
|
|
||||||
@@ -22,16 +22,13 @@ println(model.sheet.export()!)
|
|||||||
// model.sheet.export(path:"~/Downloads/test.csv")!
|
// model.sheet.export(path:"~/Downloads/test.csv")!
|
||||||
// model.sheet.export(path:"~/code/github/freeflowuniverse/starlight_template/src/content/test.csv")!
|
// model.sheet.export(path:"~/code/github/freeflowuniverse/starlight_template/src/content/test.csv")!
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
report := model.new_report(
|
report := model.new_report(
|
||||||
name: 'example_report'
|
name: 'example_report'
|
||||||
title: 'Example Business Model'
|
title: 'Example Business Model'
|
||||||
)!
|
)!
|
||||||
|
|
||||||
report.export(
|
report.export(
|
||||||
path: build_path
|
path: build_path
|
||||||
overwrite: true
|
overwrite: true
|
||||||
format: .docusaurus
|
format: .docusaurus
|
||||||
)!
|
)!
|
||||||
|
|||||||
@@ -15,9 +15,9 @@ fn main() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Get the configured client
|
// Get the configured client
|
||||||
mut client := openai.OpenAI {
|
mut client := openai.OpenAI{
|
||||||
name: 'groq'
|
name: 'groq'
|
||||||
api_key: key
|
api_key: key
|
||||||
server_url: 'https://api.groq.com/openai/v1'
|
server_url: 'https://api.groq.com/openai/v1'
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -29,9 +29,9 @@ fn main() {
|
|||||||
res := client.chat_completion(model, openai.Messages{
|
res := client.chat_completion(model, openai.Messages{
|
||||||
messages: [
|
messages: [
|
||||||
openai.Message{
|
openai.Message{
|
||||||
role: .user
|
role: .user
|
||||||
content: 'What are the key differences between Groq and other AI inference providers?'
|
content: 'What are the key differences between Groq and other AI inference providers?'
|
||||||
}
|
},
|
||||||
]
|
]
|
||||||
})!
|
})!
|
||||||
|
|
||||||
|
|||||||
258
examples/clients/qdrant_example.vsh
Normal file → Executable file
258
examples/clients/qdrant_example.vsh
Normal file → Executable file
@@ -1,209 +1,85 @@
|
|||||||
#!/usr/bin/env -S v -n -w -gc none -cc tcc -d use_openssl -enable-globals run
|
#!/usr/bin/env -S v -n -w -gc none -cc tcc -d use_openssl -enable-globals run
|
||||||
|
|
||||||
import freeflowuniverse.herolib.clients.qdrant
|
import freeflowuniverse.herolib.clients.qdrant
|
||||||
import os
|
import freeflowuniverse.herolib.core.httpconnection
|
||||||
import flag
|
import rand
|
||||||
|
|
||||||
mut fp := flag.new_flag_parser(os.args)
|
// 1. Get the qdrant client
|
||||||
fp.application('qdrant_example.vsh')
|
mut qdrant_client := qdrant.get()!
|
||||||
fp.version('v0.1.0')
|
|
||||||
fp.description('Example script demonstrating Qdrant client usage')
|
|
||||||
fp.skip_executable()
|
|
||||||
|
|
||||||
help_requested := fp.bool('help', `h`, false, 'Show help message')
|
// 2. Generate collection name
|
||||||
|
|
||||||
if help_requested {
|
collection_name := 'collection_' + rand.string(4)
|
||||||
println(fp.usage())
|
|
||||||
exit(0)
|
|
||||||
}
|
|
||||||
|
|
||||||
additional_args := fp.finalize() or {
|
// 2. Create a new collection
|
||||||
eprintln(err)
|
|
||||||
println(fp.usage())
|
|
||||||
exit(1)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Initialize Qdrant client
|
created_collection := qdrant_client.create_collection(
|
||||||
mut client := qdrant.get(name: 'default') or {
|
collection_name: collection_name
|
||||||
// If client doesn't exist, create a new one
|
size: 15
|
||||||
mut new_client := qdrant.QdrantClient{
|
distance: 'Cosine'
|
||||||
name: 'default'
|
)!
|
||||||
url: 'http://localhost:6333'
|
|
||||||
}
|
|
||||||
qdrant.set(new_client) or {
|
|
||||||
eprintln('Failed to set Qdrant client: ${err}')
|
|
||||||
exit(1)
|
|
||||||
}
|
|
||||||
new_client
|
|
||||||
}
|
|
||||||
|
|
||||||
println('Connected to Qdrant at ${client.url}')
|
println('Created Collection: ${created_collection}')
|
||||||
|
|
||||||
// Check if Qdrant is healthy
|
// 3. Get the created collection
|
||||||
is_healthy := client.health_check() or {
|
get_collection := qdrant_client.get_collection(
|
||||||
eprintln('Failed to check Qdrant health: ${err}')
|
collection_name: collection_name
|
||||||
exit(1)
|
)!
|
||||||
}
|
|
||||||
|
|
||||||
if !is_healthy {
|
println('Get Collection: ${get_collection}')
|
||||||
eprintln('Qdrant is not healthy')
|
|
||||||
exit(1)
|
|
||||||
}
|
|
||||||
|
|
||||||
println('Qdrant is healthy')
|
// 4. Delete the created collection
|
||||||
|
// deleted_collection := qdrant_client.delete_collection(
|
||||||
|
// collection_name: collection_name
|
||||||
|
// )!
|
||||||
|
|
||||||
// Get service info
|
// println('Deleted Collection: ${deleted_collection}')
|
||||||
service_info := client.get_service_info() or {
|
|
||||||
eprintln('Failed to get service info: ${err}')
|
|
||||||
exit(1)
|
|
||||||
}
|
|
||||||
|
|
||||||
println('Qdrant version: ${service_info.version}')
|
// 5. List all collections
|
||||||
|
list_collection := qdrant_client.list_collections()!
|
||||||
|
println('List Collection: ${list_collection}')
|
||||||
|
|
||||||
// Collection name for our example
|
// 6. Check collection existence
|
||||||
collection_name := 'example_collection'
|
collection_existence := qdrant_client.is_collection_exists(
|
||||||
|
collection_name: collection_name
|
||||||
|
)!
|
||||||
|
println('Collection Existence: ${collection_existence}')
|
||||||
|
|
||||||
// Check if collection exists and delete it if it does
|
// 7. Retrieve points
|
||||||
collections := client.list_collections() or {
|
collection_points := qdrant_client.retrieve_points(
|
||||||
eprintln('Failed to list collections: ${err}')
|
collection_name: collection_name
|
||||||
exit(1)
|
ids: [
|
||||||
}
|
0,
|
||||||
|
3,
|
||||||
|
100,
|
||||||
|
]
|
||||||
|
)!
|
||||||
|
|
||||||
if collection_name in collections.result {
|
println('Collection Points: ${collection_points}')
|
||||||
println('Collection ${collection_name} already exists, deleting it...')
|
|
||||||
client.delete_collection(collection_name: collection_name) or {
|
|
||||||
eprintln('Failed to delete collection: ${err}')
|
|
||||||
exit(1)
|
|
||||||
}
|
|
||||||
println('Collection deleted')
|
|
||||||
}
|
|
||||||
|
|
||||||
// Create a new collection
|
// 8. Upsert points
|
||||||
println('Creating collection ${collection_name}...')
|
upsert_points := qdrant_client.upsert_points(
|
||||||
vectors_config := qdrant.VectorsConfig{
|
collection_name: collection_name
|
||||||
size: 4 // Small size for example purposes
|
points: [
|
||||||
distance: .cosine
|
qdrant.Point{
|
||||||
}
|
payload: {
|
||||||
|
'key': 'value'
|
||||||
|
}
|
||||||
|
vector: [1.0, 2.0, 3.0]
|
||||||
|
},
|
||||||
|
qdrant.Point{
|
||||||
|
payload: {
|
||||||
|
'key': 'value'
|
||||||
|
}
|
||||||
|
vector: [4.0, 5.0, 6.0]
|
||||||
|
},
|
||||||
|
qdrant.Point{
|
||||||
|
payload: {
|
||||||
|
'key': 'value'
|
||||||
|
}
|
||||||
|
vector: [7.0, 8.0, 9.0]
|
||||||
|
},
|
||||||
|
]
|
||||||
|
)!
|
||||||
|
|
||||||
client.create_collection(
|
println('Upsert Points: ${upsert_points}')
|
||||||
collection_name: collection_name
|
|
||||||
vectors: vectors_config
|
|
||||||
) or {
|
|
||||||
eprintln('Failed to create collection: ${err}')
|
|
||||||
exit(1)
|
|
||||||
}
|
|
||||||
|
|
||||||
println('Collection created')
|
|
||||||
|
|
||||||
// Upsert some points
|
|
||||||
println('Upserting points...')
|
|
||||||
points := [
|
|
||||||
qdrant.PointStruct{
|
|
||||||
id: '1'
|
|
||||||
vector: [f32(0.1), 0.2, 0.3, 0.4]
|
|
||||||
payload: {
|
|
||||||
'color': 'red'
|
|
||||||
'category': 'furniture'
|
|
||||||
'name': 'chair'
|
|
||||||
}
|
|
||||||
},
|
|
||||||
qdrant.PointStruct{
|
|
||||||
id: '2'
|
|
||||||
vector: [f32(0.2), 0.3, 0.4, 0.5]
|
|
||||||
payload: {
|
|
||||||
'color': 'blue'
|
|
||||||
'category': 'electronics'
|
|
||||||
'name': 'laptop'
|
|
||||||
}
|
|
||||||
},
|
|
||||||
qdrant.PointStruct{
|
|
||||||
id: '3'
|
|
||||||
vector: [f32(0.3), 0.4, 0.5, 0.6]
|
|
||||||
payload: {
|
|
||||||
'color': 'green'
|
|
||||||
'category': 'food'
|
|
||||||
'name': 'apple'
|
|
||||||
}
|
|
||||||
}
|
|
||||||
]
|
|
||||||
|
|
||||||
client.upsert_points(
|
|
||||||
collection_name: collection_name
|
|
||||||
points: points
|
|
||||||
wait: true
|
|
||||||
) or {
|
|
||||||
eprintln('Failed to upsert points: ${err}')
|
|
||||||
exit(1)
|
|
||||||
}
|
|
||||||
|
|
||||||
println('Points upserted')
|
|
||||||
|
|
||||||
// Get collection info to verify points were added
|
|
||||||
collection_info := client.get_collection(collection_name: collection_name) or {
|
|
||||||
eprintln('Failed to get collection info: ${err}')
|
|
||||||
exit(1)
|
|
||||||
}
|
|
||||||
|
|
||||||
println('Collection has ${collection_info.vectors_count} points')
|
|
||||||
|
|
||||||
// Search for points
|
|
||||||
println('Searching for points similar to [0.1, 0.2, 0.3, 0.4]...')
|
|
||||||
search_result := client.search(
|
|
||||||
collection_name: collection_name
|
|
||||||
vector: [f32(0.1), 0.2, 0.3, 0.4]
|
|
||||||
limit: 3
|
|
||||||
) or {
|
|
||||||
eprintln('Failed to search points: ${err}')
|
|
||||||
exit(1)
|
|
||||||
}
|
|
||||||
|
|
||||||
println('Search results:')
|
|
||||||
for i, point in search_result.result {
|
|
||||||
println(' ${i+1}. ID: ${point.id}, Score: ${point.score}')
|
|
||||||
if payload := point.payload {
|
|
||||||
println(' Name: ${payload['name']}')
|
|
||||||
println(' Category: ${payload['category']}')
|
|
||||||
println(' Color: ${payload['color']}')
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Search with filter
|
|
||||||
println('\nSearching for points with category "electronics"...')
|
|
||||||
filter := qdrant.Filter{
|
|
||||||
must: [
|
|
||||||
qdrant.FieldCondition{
|
|
||||||
key: 'category'
|
|
||||||
match: 'electronics'
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
||||||
|
|
||||||
filtered_search := client.search(
|
|
||||||
collection_name: collection_name
|
|
||||||
vector: [f32(0.1), 0.2, 0.3, 0.4]
|
|
||||||
filter: filter
|
|
||||||
limit: 3
|
|
||||||
) or {
|
|
||||||
eprintln('Failed to search with filter: ${err}')
|
|
||||||
exit(1)
|
|
||||||
}
|
|
||||||
|
|
||||||
println('Filtered search results:')
|
|
||||||
for i, point in filtered_search.result {
|
|
||||||
println(' ${i+1}. ID: ${point.id}, Score: ${point.score}')
|
|
||||||
if payload := point.payload {
|
|
||||||
println(' Name: ${payload['name']}')
|
|
||||||
println(' Category: ${payload['category']}')
|
|
||||||
println(' Color: ${payload['color']}')
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Clean up - delete the collection
|
|
||||||
println('\nCleaning up - deleting collection...')
|
|
||||||
client.delete_collection(collection_name: collection_name) or {
|
|
||||||
eprintln('Failed to delete collection: ${err}')
|
|
||||||
exit(1)
|
|
||||||
}
|
|
||||||
|
|
||||||
println('Collection deleted')
|
|
||||||
println('Example completed successfully')
|
|
||||||
|
|||||||
@@ -5,44 +5,44 @@ import freeflowuniverse.herolib.core.jobs.model
|
|||||||
|
|
||||||
// Create a test agent with some sample data
|
// Create a test agent with some sample data
|
||||||
mut agent := model.Agent{
|
mut agent := model.Agent{
|
||||||
pubkey: 'ed25519:1234567890abcdef'
|
pubkey: 'ed25519:1234567890abcdef'
|
||||||
address: '192.168.1.100'
|
address: '192.168.1.100'
|
||||||
port: 9999
|
port: 9999
|
||||||
description: 'Test agent for binary encoding'
|
description: 'Test agent for binary encoding'
|
||||||
status: model.AgentStatus{
|
status: model.AgentStatus{
|
||||||
guid: 'agent-123'
|
guid: 'agent-123'
|
||||||
timestamp_first: ourtime.now()
|
timestamp_first: ourtime.now()
|
||||||
timestamp_last: ourtime.now()
|
timestamp_last: ourtime.now()
|
||||||
status: model.AgentState.ok
|
status: model.AgentState.ok
|
||||||
}
|
}
|
||||||
services: []
|
services: []
|
||||||
signature: 'signature-data-here'
|
signature: 'signature-data-here'
|
||||||
}
|
}
|
||||||
|
|
||||||
// Add a service
|
// Add a service
|
||||||
mut service := model.AgentService{
|
mut service := model.AgentService{
|
||||||
actor: 'vm'
|
actor: 'vm'
|
||||||
description: 'Virtual machine management'
|
description: 'Virtual machine management'
|
||||||
status: model.AgentServiceState.ok
|
status: model.AgentServiceState.ok
|
||||||
public: true
|
public: true
|
||||||
actions: []
|
actions: []
|
||||||
}
|
}
|
||||||
|
|
||||||
// Add an action to the service
|
// Add an action to the service
|
||||||
mut action := model.AgentServiceAction{
|
mut action := model.AgentServiceAction{
|
||||||
action: 'create'
|
action: 'create'
|
||||||
description: 'Create a new virtual machine'
|
description: 'Create a new virtual machine'
|
||||||
status: model.AgentServiceState.ok
|
status: model.AgentServiceState.ok
|
||||||
public: true
|
public: true
|
||||||
params: {
|
params: {
|
||||||
'name': 'Name of the VM'
|
'name': 'Name of the VM'
|
||||||
'memory': 'Memory in MB'
|
'memory': 'Memory in MB'
|
||||||
'cpu': 'Number of CPU cores'
|
'cpu': 'Number of CPU cores'
|
||||||
}
|
}
|
||||||
params_example: {
|
params_example: {
|
||||||
'name': 'my-test-vm'
|
'name': 'my-test-vm'
|
||||||
'memory': '2048'
|
'memory': '2048'
|
||||||
'cpu': '2'
|
'cpu': '2'
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
module example_actor
|
module example_actor
|
||||||
|
|
||||||
import os
|
import os
|
||||||
import freeflowuniverse.herolib.hero.baobab.stage {IActor, RunParams}
|
import freeflowuniverse.herolib.hero.baobab.stage { IActor, RunParams }
|
||||||
import freeflowuniverse.herolib.web.openapi
|
import freeflowuniverse.herolib.web.openapi
|
||||||
import time
|
import time
|
||||||
|
|
||||||
@@ -10,13 +10,11 @@ const openapi_spec_json = os.read_file(openapi_spec_path) or { panic(err) }
|
|||||||
const openapi_specification = openapi.json_decode(openapi_spec_json)!
|
const openapi_specification = openapi.json_decode(openapi_spec_json)!
|
||||||
|
|
||||||
struct ExampleActor {
|
struct ExampleActor {
|
||||||
stage.Actor
|
stage.Actor
|
||||||
}
|
}
|
||||||
|
|
||||||
fn new() !ExampleActor {
|
fn new() !ExampleActor {
|
||||||
return ExampleActor{
|
return ExampleActor{stage.new_actor('example')}
|
||||||
stage.new_actor('example')
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn run() ! {
|
pub fn run() ! {
|
||||||
|
|||||||
@@ -70,74 +70,86 @@ fn (mut actor Actor) listen() ! {
|
|||||||
|
|
||||||
// Handle method invocations
|
// Handle method invocations
|
||||||
fn (mut actor Actor) handle_method(cmd string, data string) !string {
|
fn (mut actor Actor) handle_method(cmd string, data string) !string {
|
||||||
param_anys := json2.raw_decode(data)!.arr()
|
param_anys := json2.raw_decode(data)!.arr()
|
||||||
match cmd {
|
match cmd {
|
||||||
'listPets' {
|
'listPets' {
|
||||||
pets := if param_anys.len == 0 {
|
pets := if param_anys.len == 0 {
|
||||||
actor.data_store.list_pets()
|
actor.data_store.list_pets()
|
||||||
} else {
|
} else {
|
||||||
params := json.decode(ListPetParams, param_anys[0].str())!
|
params := json.decode(ListPetParams, param_anys[0].str())!
|
||||||
actor.data_store.list_pets(params)
|
actor.data_store.list_pets(params)
|
||||||
}
|
}
|
||||||
return json.encode(pets)
|
return json.encode(pets)
|
||||||
}
|
}
|
||||||
'createPet' {
|
'createPet' {
|
||||||
response := if param_anys.len == 0 {
|
response := if param_anys.len == 0 {
|
||||||
return error('at least data expected')
|
return error('at least data expected')
|
||||||
} else if param_anys.len == 1 {
|
} else if param_anys.len == 1 {
|
||||||
payload := json.decode(NewPet, param_anys[0].str())!
|
payload := json.decode(NewPet, param_anys[0].str())!
|
||||||
actor.data_store.create_pet(payload)
|
actor.data_store.create_pet(payload)
|
||||||
} else {
|
} else {
|
||||||
return error('expected 1 param, found too many')
|
return error('expected 1 param, found too many')
|
||||||
}
|
}
|
||||||
// data := json.decode(NewPet, data) or { return error('Invalid pet data: $err') }
|
// data := json.decode(NewPet, data) or { return error('Invalid pet data: $err') }
|
||||||
// created_pet := actor.data_store.create_pet(pet)
|
// created_pet := actor.data_store.create_pet(pet)
|
||||||
return json.encode(response)
|
return json.encode(response)
|
||||||
}
|
}
|
||||||
'getPet' {
|
'getPet' {
|
||||||
response := if param_anys.len == 0 {
|
response := if param_anys.len == 0 {
|
||||||
return error('at least data expected')
|
return error('at least data expected')
|
||||||
} else if param_anys.len == 1 {
|
} else if param_anys.len == 1 {
|
||||||
payload := param_anys[0].int()
|
payload := param_anys[0].int()
|
||||||
actor.data_store.get_pet(payload)!
|
actor.data_store.get_pet(payload)!
|
||||||
} else {
|
} else {
|
||||||
return error('expected 1 param, found too many')
|
return error('expected 1 param, found too many')
|
||||||
}
|
}
|
||||||
|
|
||||||
return json.encode(response)
|
return json.encode(response)
|
||||||
}
|
}
|
||||||
'deletePet' {
|
'deletePet' {
|
||||||
params := json.decode(map[string]int, data) or { return error('Invalid params: $err') }
|
params := json.decode(map[string]int, data) or {
|
||||||
actor.data_store.delete_pet(params['petId']) or { return error('Pet not found: $err') }
|
return error('Invalid params: ${err}')
|
||||||
return json.encode({'message': 'Pet deleted'})
|
}
|
||||||
}
|
actor.data_store.delete_pet(params['petId']) or {
|
||||||
'listOrders' {
|
return error('Pet not found: ${err}')
|
||||||
orders := actor.data_store.list_orders()
|
}
|
||||||
return json.encode(orders)
|
return json.encode({
|
||||||
}
|
'message': 'Pet deleted'
|
||||||
'getOrder' {
|
})
|
||||||
params := json.decode(map[string]int, data) or { return error('Invalid params: $err') }
|
}
|
||||||
order := actor.data_store.get_order(params['orderId']) or {
|
'listOrders' {
|
||||||
return error('Order not found: $err')
|
orders := actor.data_store.list_orders()
|
||||||
}
|
return json.encode(orders)
|
||||||
return json.encode(order)
|
}
|
||||||
}
|
'getOrder' {
|
||||||
'deleteOrder' {
|
params := json.decode(map[string]int, data) or {
|
||||||
params := json.decode(map[string]int, data) or { return error('Invalid params: $err') }
|
return error('Invalid params: ${err}')
|
||||||
actor.data_store.delete_order(params['orderId']) or {
|
}
|
||||||
return error('Order not found: $err')
|
order := actor.data_store.get_order(params['orderId']) or {
|
||||||
}
|
return error('Order not found: ${err}')
|
||||||
return json.encode({'message': 'Order deleted'})
|
}
|
||||||
}
|
return json.encode(order)
|
||||||
'createUser' {
|
}
|
||||||
user := json.decode(NewUser, data) or { return error('Invalid user data: $err') }
|
'deleteOrder' {
|
||||||
created_user := actor.data_store.create_user(user)
|
params := json.decode(map[string]int, data) or {
|
||||||
return json.encode(created_user)
|
return error('Invalid params: ${err}')
|
||||||
}
|
}
|
||||||
else {
|
actor.data_store.delete_order(params['orderId']) or {
|
||||||
return error('Unknown method: $cmd')
|
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]
|
@[params]
|
||||||
|
|||||||
@@ -6,4 +6,3 @@ mut db := qdrant_installer.get()!
|
|||||||
|
|
||||||
db.install()!
|
db.install()!
|
||||||
db.start()!
|
db.start()!
|
||||||
|
|
||||||
|
|||||||
@@ -8,5 +8,5 @@ import freeflowuniverse.herolib.core
|
|||||||
|
|
||||||
core.interactive_set()! // make sure the sudo works so we can do things even if it requires those rights
|
core.interactive_set()! // make sure the sudo works so we can do things even if it requires those rights
|
||||||
|
|
||||||
mut i1:=golang.get()!
|
mut i1 := golang.get()!
|
||||||
i1.install()!
|
i1.install()!
|
||||||
|
|||||||
@@ -5,6 +5,4 @@ import freeflowuniverse.herolib.installers.lang.python as python_module
|
|||||||
mut python_installer := python_module.get()!
|
mut python_installer := python_module.get()!
|
||||||
python_installer.install()!
|
python_installer.install()!
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
// python_installer.destroy()!
|
// python_installer.destroy()!
|
||||||
|
|||||||
@@ -7,7 +7,7 @@ import json
|
|||||||
fn main() {
|
fn main() {
|
||||||
// Initialize Jina client
|
// Initialize Jina client
|
||||||
mut j := jina.Jina{
|
mut j := jina.Jina{
|
||||||
name: 'test_client'
|
name: 'test_client'
|
||||||
secret: os.getenv('JINAKEY')
|
secret: os.getenv('JINAKEY')
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -21,74 +21,64 @@ create_count := fp.int('create', `c`, 5, 'Number of jobs to create')
|
|||||||
help_requested := fp.bool('help', `h`, false, 'Show help message')
|
help_requested := fp.bool('help', `h`, false, 'Show help message')
|
||||||
|
|
||||||
if help_requested {
|
if help_requested {
|
||||||
println(fp.usage())
|
println(fp.usage())
|
||||||
exit(0)
|
exit(0)
|
||||||
}
|
}
|
||||||
|
|
||||||
additional_args := fp.finalize() or {
|
additional_args := fp.finalize() or {
|
||||||
eprintln(err)
|
eprintln(err)
|
||||||
println(fp.usage())
|
println(fp.usage())
|
||||||
exit(1)
|
exit(1)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Create a new HeroRunner instance
|
// Create a new HeroRunner instance
|
||||||
mut runner := model.new() or {
|
mut runner := model.new() or { panic('Failed to create HeroRunner: ${err}') }
|
||||||
panic('Failed to create HeroRunner: ${err}')
|
|
||||||
}
|
|
||||||
|
|
||||||
println('\n---------BEGIN VFS JOBS EXAMPLE')
|
println('\n---------BEGIN VFS JOBS EXAMPLE')
|
||||||
|
|
||||||
// Create some jobs
|
// Create some jobs
|
||||||
println('\n---------CREATING JOBS')
|
println('\n---------CREATING JOBS')
|
||||||
for i in 0..create_count {
|
for i in 0 .. create_count {
|
||||||
mut job := runner.jobs.new()
|
mut job := runner.jobs.new()
|
||||||
job.guid = 'job_${i}_${time.now().unix}'
|
job.guid = 'job_${i}_${time.now().unix}'
|
||||||
job.actor = 'example_actor'
|
job.actor = 'example_actor'
|
||||||
job.action = 'test_action'
|
job.action = 'test_action'
|
||||||
job.params = {
|
job.params = {
|
||||||
'param1': 'value1'
|
'param1': 'value1'
|
||||||
'param2': 'value2'
|
'param2': 'value2'
|
||||||
}
|
}
|
||||||
|
|
||||||
// For demonstration, make some jobs older by adjusting their creation time
|
// For demonstration, make some jobs older by adjusting their creation time
|
||||||
if i % 2 == 0 {
|
if i % 2 == 0 {
|
||||||
job.status.created.time = time.now().add_days(-(cleanup_days + 1))
|
job.status.created.time = time.now().add_days(-(cleanup_days + 1))
|
||||||
}
|
}
|
||||||
|
|
||||||
runner.jobs.set(job) or {
|
runner.jobs.set(job) or { panic('Failed to set job: ${err}') }
|
||||||
panic('Failed to set job: ${err}')
|
println('Created job with GUID: ${job.guid}')
|
||||||
}
|
|
||||||
println('Created job with GUID: ${job.guid}')
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// List all jobs
|
// List all jobs
|
||||||
println('\n---------LISTING ALL JOBS')
|
println('\n---------LISTING ALL JOBS')
|
||||||
jobs := runner.jobs.list() or {
|
jobs := runner.jobs.list() or { panic('Failed to list jobs: ${err}') }
|
||||||
panic('Failed to list jobs: ${err}')
|
|
||||||
}
|
|
||||||
println('Found ${jobs.len} jobs:')
|
println('Found ${jobs.len} jobs:')
|
||||||
for job in jobs {
|
for job in jobs {
|
||||||
days_ago := (time.now().unix - job.status.created.time.unix) / (60 * 60 * 24)
|
days_ago := (time.now().unix - job.status.created.time.unix) / (60 * 60 * 24)
|
||||||
println('- ${job.guid} (created ${days_ago} days ago)')
|
println('- ${job.guid} (created ${days_ago} days ago)')
|
||||||
}
|
}
|
||||||
|
|
||||||
// Clean up old jobs
|
// Clean up old jobs
|
||||||
println('\n---------CLEANING UP OLD JOBS')
|
println('\n---------CLEANING UP OLD JOBS')
|
||||||
println('Cleaning up jobs older than ${cleanup_days} days...')
|
println('Cleaning up jobs older than ${cleanup_days} days...')
|
||||||
deleted_count := runner.cleanup_jobs(cleanup_days) or {
|
deleted_count := runner.cleanup_jobs(cleanup_days) or { panic('Failed to clean up jobs: ${err}') }
|
||||||
panic('Failed to clean up jobs: ${err}')
|
|
||||||
}
|
|
||||||
println('Deleted ${deleted_count} old jobs')
|
println('Deleted ${deleted_count} old jobs')
|
||||||
|
|
||||||
// List remaining jobs
|
// List remaining jobs
|
||||||
println('\n---------LISTING REMAINING JOBS')
|
println('\n---------LISTING REMAINING JOBS')
|
||||||
remaining_jobs := runner.jobs.list() or {
|
remaining_jobs := runner.jobs.list() or { panic('Failed to list jobs: ${err}') }
|
||||||
panic('Failed to list jobs: ${err}')
|
|
||||||
}
|
|
||||||
println('Found ${remaining_jobs.len} remaining jobs:')
|
println('Found ${remaining_jobs.len} remaining jobs:')
|
||||||
for job in remaining_jobs {
|
for job in remaining_jobs {
|
||||||
days_ago := (time.now().unix - job.status.created.time.unix) / (60 * 60 * 24)
|
days_ago := (time.now().unix - job.status.created.time.unix) / (60 * 60 * 24)
|
||||||
println('- ${job.guid} (created ${days_ago} days ago)')
|
println('- ${job.guid} (created ${days_ago} days ago)')
|
||||||
}
|
}
|
||||||
|
|
||||||
println('\n---------END VFS JOBS EXAMPLE')
|
println('\n---------END VFS JOBS EXAMPLE')
|
||||||
|
|||||||
@@ -3,7 +3,6 @@
|
|||||||
// Calendar Typescript Client Generation Example
|
// Calendar Typescript Client Generation Example
|
||||||
// This example demonstrates how to generate a typescript client
|
// This example demonstrates how to generate a typescript client
|
||||||
// from a given OpenAPI Specification using the `openapi/codegen` module.
|
// from a given OpenAPI Specification using the `openapi/codegen` module.
|
||||||
|
|
||||||
import os
|
import os
|
||||||
import freeflowuniverse.herolib.schemas.openapi
|
import freeflowuniverse.herolib.schemas.openapi
|
||||||
import freeflowuniverse.herolib.schemas.openapi.codegen
|
import freeflowuniverse.herolib.schemas.openapi.codegen
|
||||||
@@ -15,5 +14,3 @@ const specification = openapi.new(path: '${dir}/meeting_api.json') or {
|
|||||||
|
|
||||||
// generate typescript client folder and write it in dir
|
// generate typescript client folder and write it in dir
|
||||||
codegen.ts_client_folder(specification)!.write(dir, overwrite: true)!
|
codegen.ts_client_folder(specification)!.write(dir, overwrite: true)!
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
20
examples/threefold/tfgrid3deployer/filter.vsh
Executable file
20
examples/threefold/tfgrid3deployer/filter.vsh
Executable file
@@ -0,0 +1,20 @@
|
|||||||
|
#!/usr/bin/env -S v -gc none -cc tcc -d use_openssl -enable-globals -cg run
|
||||||
|
|
||||||
|
import freeflowuniverse.herolib.threefold.grid3.deployer
|
||||||
|
|
||||||
|
const gigabyte = u64(1024 * 1024 * 1024)
|
||||||
|
|
||||||
|
// We can use any of the parameters for the corresponding Grid Proxy query
|
||||||
|
// https://gridproxy.grid.tf/swagger/index.html#/GridProxy/get_nodes
|
||||||
|
|
||||||
|
filter := deployer.FilterNodesArgs{
|
||||||
|
size: 5
|
||||||
|
randomize: true
|
||||||
|
free_mru: 8 * gigabyte
|
||||||
|
free_sru: 50 * gigabyte
|
||||||
|
farm_name: 'FreeFarm'
|
||||||
|
status: 'up'
|
||||||
|
}
|
||||||
|
|
||||||
|
nodes := deployer.filter_nodes(filter)!
|
||||||
|
println(nodes)
|
||||||
@@ -11,18 +11,18 @@ griddriver.install()!
|
|||||||
|
|
||||||
v := tfgrid3deployer.get()!
|
v := tfgrid3deployer.get()!
|
||||||
println('cred: ${v}')
|
println('cred: ${v}')
|
||||||
deployment_name := 'herzner_dep'
|
deployment_name := 'hetzner_dep'
|
||||||
mut deployment := tfgrid3deployer.new_deployment(deployment_name)!
|
mut deployment := tfgrid3deployer.new_deployment(deployment_name)!
|
||||||
|
|
||||||
// TODO: find a way to filter hetzner nodes
|
// TODO: find a way to filter hetzner nodes
|
||||||
deployment.add_machine(
|
deployment.add_machine(
|
||||||
name: 'hetzner_vm'
|
name: 'hetzner_vm'
|
||||||
cpu: 1
|
cpu: 2
|
||||||
memory: 2
|
memory: 5
|
||||||
planetary: false
|
planetary: false
|
||||||
public_ip4: true
|
public_ip4: false
|
||||||
size: 10 // 10 gig
|
size: 10 // 10 gig
|
||||||
mycelium: tfgrid3deployer.Mycelium{}
|
// mycelium: tfgrid3deployer.Mycelium{}
|
||||||
)
|
)
|
||||||
deployment.deploy()!
|
deployment.deploy()!
|
||||||
|
|
||||||
|
|||||||
@@ -13,8 +13,8 @@ fn main() {
|
|||||||
// mut deployment := tfgrid3deployer.get_deployment(deployment_name)!
|
// mut deployment := tfgrid3deployer.get_deployment(deployment_name)!
|
||||||
deployment.add_machine(
|
deployment.add_machine(
|
||||||
name: 'my_vm1'
|
name: 'my_vm1'
|
||||||
cpu: 1
|
cpu: 2
|
||||||
memory: 2
|
memory: 4
|
||||||
planetary: false
|
planetary: false
|
||||||
public_ip4: false
|
public_ip4: false
|
||||||
nodes: [167]
|
nodes: [167]
|
||||||
@@ -32,10 +32,10 @@ fn main() {
|
|||||||
deployment.add_webname(name: 'mywebname2', backend: 'http://37.27.132.47:8000')
|
deployment.add_webname(name: 'mywebname2', backend: 'http://37.27.132.47:8000')
|
||||||
deployment.deploy()!
|
deployment.deploy()!
|
||||||
|
|
||||||
deployment.remove_machine('my_vm1')!
|
// deployment.remove_machine('my_vm1')!
|
||||||
deployment.remove_webname('mywebname2')!
|
// deployment.remove_webname('mywebname2')!
|
||||||
deployment.remove_zdb('my_zdb')!
|
// deployment.remove_zdb('my_zdb')!
|
||||||
deployment.deploy()!
|
// deployment.deploy()!
|
||||||
|
|
||||||
tfgrid3deployer.delete_deployment(deployment_name)!
|
// tfgrid3deployer.delete_deployment(deployment_name)!
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -16,7 +16,7 @@ fn deploy_vm() ! {
|
|||||||
memory: 2
|
memory: 2
|
||||||
planetary: false
|
planetary: false
|
||||||
public_ip4: true
|
public_ip4: true
|
||||||
nodes: [node_id]
|
nodes: [node_id]
|
||||||
)
|
)
|
||||||
deployment.deploy()!
|
deployment.deploy()!
|
||||||
println(deployment)
|
println(deployment)
|
||||||
@@ -27,13 +27,13 @@ fn delete_vm() ! {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn main() {
|
fn main() {
|
||||||
if os.args.len < 2 {
|
if os.args.len < 2 {
|
||||||
println('Please provide a command: "deploy" or "delete"')
|
println('Please provide a command: "deploy" or "delete"')
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
match os.args[1] {
|
match os.args[1] {
|
||||||
'deploy' { deploy_vm()! }
|
'deploy' { deploy_vm()! }
|
||||||
'delete' { delete_vm()! }
|
'delete' { delete_vm()! }
|
||||||
else { println('Invalid command. Use "deploy" or "delete"') }
|
else { println('Invalid command. Use "deploy" or "delete"') }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,6 +1,5 @@
|
|||||||
#!/usr/bin/env -S v -gc none -d use_openssl -enable-globals -cg run
|
#!/usr/bin/env -S v -gc none -cc tcc -d use_openssl -enable-globals -cg run
|
||||||
|
|
||||||
//#!/usr/bin/env -S v -gc none -cc tcc -d use_openssl -enable-globals -cg run
|
|
||||||
import freeflowuniverse.herolib.threefold.grid3.gridproxy
|
import freeflowuniverse.herolib.threefold.grid3.gridproxy
|
||||||
import freeflowuniverse.herolib.threefold.grid3.deployer
|
import freeflowuniverse.herolib.threefold.grid3.deployer
|
||||||
import freeflowuniverse.herolib.installers.threefold.griddriver
|
import freeflowuniverse.herolib.installers.threefold.griddriver
|
||||||
@@ -26,7 +25,7 @@ deployment.add_machine(
|
|||||||
public_ip4: false
|
public_ip4: false
|
||||||
size: 10 // 10 gig
|
size: 10 // 10 gig
|
||||||
mycelium: deployer.Mycelium{}
|
mycelium: deployer.Mycelium{}
|
||||||
nodes: [vm_node]
|
nodes: [vm_node]
|
||||||
)
|
)
|
||||||
deployment.deploy()!
|
deployment.deploy()!
|
||||||
|
|
||||||
|
|||||||
@@ -11,13 +11,14 @@ pub struct VFSDedupeDB {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub fn (mut db VFSDedupeDB) set(args ourdb.OurDBSetArgs) !u32 {
|
pub fn (mut db VFSDedupeDB) set(args ourdb.OurDBSetArgs) !u32 {
|
||||||
return db.store(args.data,
|
return db.store(args.data, dedupestor.Reference{
|
||||||
dedupestor.Reference{owner: u16(1), id: args.id or {panic('VFS Must provide id')}}
|
owner: u16(1)
|
||||||
)!
|
id: args.id or { panic('VFS Must provide id') }
|
||||||
|
})!
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn (mut db VFSDedupeDB) delete(id u32) ! {
|
pub fn (mut db VFSDedupeDB) delete(id u32) ! {
|
||||||
db.DedupeStore.delete(id, dedupestor.Reference{owner: u16(1), id: id})!
|
db.DedupeStore.delete(id, dedupestor.Reference{ owner: u16(1), id: id })!
|
||||||
}
|
}
|
||||||
|
|
||||||
example_data_dir := os.join_path(os.dir(@FILE), 'example_db')
|
example_data_dir := os.join_path(os.dir(@FILE), 'example_db')
|
||||||
@@ -33,35 +34,23 @@ mut db_data := VFSDedupeDB{
|
|||||||
}
|
}
|
||||||
|
|
||||||
mut db_metadata := ourdb.new(
|
mut db_metadata := ourdb.new(
|
||||||
path: os.join_path(example_data_dir, 'metadata')
|
path: os.join_path(example_data_dir, 'metadata')
|
||||||
incremental_mode: false
|
incremental_mode: false
|
||||||
)!
|
)!
|
||||||
|
|
||||||
// Create VFS with separate databases for data and metadata
|
// Create VFS with separate databases for data and metadata
|
||||||
mut vfs := vfs_db.new(mut db_data, mut db_metadata) or {
|
mut vfs := vfs_db.new(mut db_data, mut db_metadata) or { panic('Failed to create VFS: ${err}') }
|
||||||
panic('Failed to create VFS: ${err}')
|
|
||||||
}
|
|
||||||
|
|
||||||
println('\n---------BEGIN EXAMPLE')
|
println('\n---------BEGIN EXAMPLE')
|
||||||
println('---------WRITING FILES')
|
println('---------WRITING FILES')
|
||||||
vfs.file_create('/some_file.txt') or {
|
vfs.file_create('/some_file.txt') or { panic('Failed to create file: ${err}') }
|
||||||
panic('Failed to create file: ${err}')
|
vfs.file_create('/another_file.txt') or { panic('Failed to create file: ${err}') }
|
||||||
}
|
|
||||||
vfs.file_create('/another_file.txt') or {
|
|
||||||
panic('Failed to create file: ${err}')
|
|
||||||
}
|
|
||||||
|
|
||||||
vfs.file_write('/some_file.txt', 'gibberish'.bytes()) or {
|
vfs.file_write('/some_file.txt', 'gibberish'.bytes()) or { panic('Failed to write file: ${err}') }
|
||||||
panic('Failed to write file: ${err}')
|
vfs.file_write('/another_file.txt', 'abcdefg'.bytes()) or { panic('Failed to write file: ${err}') }
|
||||||
}
|
|
||||||
vfs.file_write('/another_file.txt', 'abcdefg'.bytes()) or {
|
|
||||||
panic('Failed to write file: ${err}')
|
|
||||||
}
|
|
||||||
|
|
||||||
println('\n---------READING FILES')
|
println('\n---------READING FILES')
|
||||||
some_file_content := vfs.file_read('/some_file.txt') or {
|
some_file_content := vfs.file_read('/some_file.txt') or { panic('Failed to read file: ${err}') }
|
||||||
panic('Failed to read file: ${err}')
|
|
||||||
}
|
|
||||||
println(some_file_content.bytestr())
|
println(some_file_content.bytestr())
|
||||||
|
|
||||||
another_file_content := vfs.file_read('/another_file.txt') or {
|
another_file_content := vfs.file_read('/another_file.txt') or {
|
||||||
@@ -69,19 +58,15 @@ another_file_content := vfs.file_read('/another_file.txt') or {
|
|||||||
}
|
}
|
||||||
println(another_file_content.bytestr())
|
println(another_file_content.bytestr())
|
||||||
|
|
||||||
println("\n---------WRITING DUPLICATE FILE (DB SIZE: ${os.file_size(os.join_path(example_data_dir, 'data/0.db'))})")
|
println('\n---------WRITING DUPLICATE FILE (DB SIZE: ${os.file_size(os.join_path(example_data_dir,
|
||||||
vfs.file_create('/duplicate.txt') or {
|
'data/0.db'))})')
|
||||||
panic('Failed to create file: ${err}')
|
vfs.file_create('/duplicate.txt') or { panic('Failed to create file: ${err}') }
|
||||||
}
|
vfs.file_write('/duplicate.txt', 'gibberish'.bytes()) or { panic('Failed to write file: ${err}') }
|
||||||
vfs.file_write('/duplicate.txt', 'gibberish'.bytes()) or {
|
|
||||||
panic('Failed to write file: ${err}')
|
|
||||||
}
|
|
||||||
|
|
||||||
println("\n---------WROTE DUPLICATE FILE (DB SIZE: ${os.file_size(os.join_path(example_data_dir, 'data/0.db'))})")
|
println('\n---------WROTE DUPLICATE FILE (DB SIZE: ${os.file_size(os.join_path(example_data_dir,
|
||||||
|
'data/0.db'))})')
|
||||||
println('---------READING FILES')
|
println('---------READING FILES')
|
||||||
some_file_content3 := vfs.file_read('/some_file.txt') or {
|
some_file_content3 := vfs.file_read('/some_file.txt') or { panic('Failed to read file: ${err}') }
|
||||||
panic('Failed to read file: ${err}')
|
|
||||||
}
|
|
||||||
println(some_file_content3.bytestr())
|
println(some_file_content3.bytestr())
|
||||||
|
|
||||||
another_file_content3 := vfs.file_read('/another_file.txt') or {
|
another_file_content3 := vfs.file_read('/another_file.txt') or {
|
||||||
@@ -89,22 +74,21 @@ another_file_content3 := vfs.file_read('/another_file.txt') or {
|
|||||||
}
|
}
|
||||||
println(another_file_content3.bytestr())
|
println(another_file_content3.bytestr())
|
||||||
|
|
||||||
duplicate_content := vfs.file_read('/duplicate.txt') or {
|
duplicate_content := vfs.file_read('/duplicate.txt') or { panic('Failed to read file: ${err}') }
|
||||||
panic('Failed to read file: ${err}')
|
|
||||||
}
|
|
||||||
println(duplicate_content.bytestr())
|
println(duplicate_content.bytestr())
|
||||||
|
|
||||||
println("\n---------DELETING DUPLICATE FILE (DB SIZE: ${os.file_size(os.join_path(example_data_dir, 'data/0.db'))})")
|
println('\n---------DELETING DUPLICATE FILE (DB SIZE: ${os.file_size(os.join_path(example_data_dir,
|
||||||
vfs.file_delete('/duplicate.txt') or {
|
'data/0.db'))})')
|
||||||
panic('Failed to delete file: ${err}')
|
vfs.file_delete('/duplicate.txt') or { panic('Failed to delete file: ${err}') }
|
||||||
}
|
|
||||||
|
|
||||||
data_path := os.join_path(example_data_dir, 'data/0.db')
|
data_path2 := os.join_path(example_data_dir, 'data/0.db')
|
||||||
db_file_path := os.join_path(data_path, '0.db')
|
db_file_path := os.join_path(data_path2, '0.db')
|
||||||
println("---------READING FILES (DB SIZE: ${if os.exists(db_file_path) { os.file_size(db_file_path) } else { 0 }})")
|
println('---------READING FILES (DB SIZE: ${if os.exists(db_file_path) {
|
||||||
some_file_content2 := vfs.file_read('/some_file.txt') or {
|
os.file_size(db_file_path)
|
||||||
panic('Failed to read file: ${err}')
|
} else {
|
||||||
}
|
0
|
||||||
|
}})')
|
||||||
|
some_file_content2 := vfs.file_read('/some_file.txt') or { panic('Failed to read file: ${err}') }
|
||||||
println(some_file_content2.bytestr())
|
println(some_file_content2.bytestr())
|
||||||
|
|
||||||
another_file_content2 := vfs.file_read('/another_file.txt') or {
|
another_file_content2 := vfs.file_read('/another_file.txt') or {
|
||||||
|
|||||||
@@ -16,26 +16,24 @@ os.mkdir_all(metadata_dir)!
|
|||||||
|
|
||||||
// Create separate databases for data and metadata
|
// Create separate databases for data and metadata
|
||||||
mut db_data := ourdb.new(
|
mut db_data := ourdb.new(
|
||||||
path: data_dir
|
path: data_dir
|
||||||
incremental_mode: false
|
incremental_mode: false
|
||||||
)!
|
)!
|
||||||
|
|
||||||
mut db_metadata := ourdb.new(
|
mut db_metadata := ourdb.new(
|
||||||
path: metadata_dir
|
path: metadata_dir
|
||||||
incremental_mode: false
|
incremental_mode: false
|
||||||
)!
|
)!
|
||||||
|
|
||||||
// Create VFS with separate databases for data and metadata
|
// Create VFS with separate databases for data and metadata
|
||||||
mut vfs := vfs_db.new_with_separate_dbs(
|
mut vfs := vfs_db.new_with_separate_dbs(mut db_data, mut db_metadata,
|
||||||
mut db_data,
|
data_dir: data_dir
|
||||||
mut db_metadata,
|
|
||||||
data_dir: data_dir,
|
|
||||||
metadata_dir: metadata_dir
|
metadata_dir: metadata_dir
|
||||||
)!
|
)!
|
||||||
|
|
||||||
// Create a root directory if it doesn't exist
|
// Create a root directory if it doesn't exist
|
||||||
if !vfs.exists('/') {
|
if !vfs.exists('/') {
|
||||||
vfs.dir_create('/')!
|
vfs.dir_create('/')!
|
||||||
}
|
}
|
||||||
|
|
||||||
// Create some files and directories
|
// Create some files and directories
|
||||||
@@ -55,13 +53,13 @@ println('Nested file content: ${vfs.file_read('/test_dir/nested_file.txt')!.byte
|
|||||||
println('Root directory contents:')
|
println('Root directory contents:')
|
||||||
root_entries := vfs.dir_list('/')!
|
root_entries := vfs.dir_list('/')!
|
||||||
for entry in root_entries {
|
for entry in root_entries {
|
||||||
println('- ${entry.get_metadata().name} (${entry.get_metadata().file_type})')
|
println('- ${entry.get_metadata().name} (${entry.get_metadata().file_type})')
|
||||||
}
|
}
|
||||||
|
|
||||||
println('Test directory contents:')
|
println('Test directory contents:')
|
||||||
test_dir_entries := vfs.dir_list('/test_dir')!
|
test_dir_entries := vfs.dir_list('/test_dir')!
|
||||||
for entry in test_dir_entries {
|
for entry in test_dir_entries {
|
||||||
println('- ${entry.get_metadata().name} (${entry.get_metadata().file_type})')
|
println('- ${entry.get_metadata().name} (${entry.get_metadata().file_type})')
|
||||||
}
|
}
|
||||||
|
|
||||||
// Create a duplicate file with the same content
|
// Create a duplicate file with the same content
|
||||||
|
|||||||
@@ -16,59 +16,43 @@ os.mkdir_all(example_data_dir)!
|
|||||||
|
|
||||||
// Create separate databases for data and metadata
|
// Create separate databases for data and metadata
|
||||||
mut db_data := ourdb.new(
|
mut db_data := ourdb.new(
|
||||||
path: os.join_path(example_data_dir, 'data')
|
path: os.join_path(example_data_dir, 'data')
|
||||||
incremental_mode: false
|
incremental_mode: false
|
||||||
)!
|
)!
|
||||||
|
|
||||||
mut db_metadata := ourdb.new(
|
mut db_metadata := ourdb.new(
|
||||||
path: os.join_path(example_data_dir, 'metadata')
|
path: os.join_path(example_data_dir, 'metadata')
|
||||||
incremental_mode: false
|
incremental_mode: false
|
||||||
)!
|
)!
|
||||||
|
|
||||||
// Create VFS with separate databases for data and metadata
|
// Create VFS with separate databases for data and metadata
|
||||||
mut vfs := vfs_db.new(mut db_data, mut db_metadata) or {
|
mut vfs := vfs_db.new(mut db_data, mut db_metadata) or { panic('Failed to create VFS: ${err}') }
|
||||||
panic('Failed to create VFS: ${err}')
|
|
||||||
}
|
|
||||||
|
|
||||||
println('\n---------BEGIN DIRECTORY OPERATIONS EXAMPLE')
|
println('\n---------BEGIN DIRECTORY OPERATIONS EXAMPLE')
|
||||||
|
|
||||||
// Create directories with subdirectories
|
// Create directories with subdirectories
|
||||||
println('\n---------CREATING DIRECTORIES')
|
println('\n---------CREATING DIRECTORIES')
|
||||||
vfs.dir_create('/dir1') or {
|
vfs.dir_create('/dir1') or { panic('Failed to create directory: ${err}') }
|
||||||
panic('Failed to create directory: ${err}')
|
|
||||||
}
|
|
||||||
println('Created directory: /dir1')
|
println('Created directory: /dir1')
|
||||||
|
|
||||||
vfs.dir_create('/dir1/subdir1') or {
|
vfs.dir_create('/dir1/subdir1') or { panic('Failed to create directory: ${err}') }
|
||||||
panic('Failed to create directory: ${err}')
|
|
||||||
}
|
|
||||||
println('Created directory: /dir1/subdir1')
|
println('Created directory: /dir1/subdir1')
|
||||||
|
|
||||||
vfs.dir_create('/dir1/subdir2') or {
|
vfs.dir_create('/dir1/subdir2') or { panic('Failed to create directory: ${err}') }
|
||||||
panic('Failed to create directory: ${err}')
|
|
||||||
}
|
|
||||||
println('Created directory: /dir1/subdir2')
|
println('Created directory: /dir1/subdir2')
|
||||||
|
|
||||||
vfs.dir_create('/dir2') or {
|
vfs.dir_create('/dir2') or { panic('Failed to create directory: ${err}') }
|
||||||
panic('Failed to create directory: ${err}')
|
|
||||||
}
|
|
||||||
println('Created directory: /dir2')
|
println('Created directory: /dir2')
|
||||||
|
|
||||||
vfs.dir_create('/dir2/subdir1') or {
|
vfs.dir_create('/dir2/subdir1') or { panic('Failed to create directory: ${err}') }
|
||||||
panic('Failed to create directory: ${err}')
|
|
||||||
}
|
|
||||||
println('Created directory: /dir2/subdir1')
|
println('Created directory: /dir2/subdir1')
|
||||||
|
|
||||||
vfs.dir_create('/dir2/subdir1/subsubdir1') or {
|
vfs.dir_create('/dir2/subdir1/subsubdir1') or { panic('Failed to create directory: ${err}') }
|
||||||
panic('Failed to create directory: ${err}')
|
|
||||||
}
|
|
||||||
println('Created directory: /dir2/subdir1/subsubdir1')
|
println('Created directory: /dir2/subdir1/subsubdir1')
|
||||||
|
|
||||||
// List directories
|
// List directories
|
||||||
println('\n---------LISTING ROOT DIRECTORY')
|
println('\n---------LISTING ROOT DIRECTORY')
|
||||||
root_entries := vfs.dir_list('/') or {
|
root_entries := vfs.dir_list('/') or { panic('Failed to list directory: ${err}') }
|
||||||
panic('Failed to list directory: ${err}')
|
|
||||||
}
|
|
||||||
println('Root directory contains:')
|
println('Root directory contains:')
|
||||||
for entry in root_entries {
|
for entry in root_entries {
|
||||||
entry_type := if entry.get_metadata().file_type == .directory { 'directory' } else { 'file' }
|
entry_type := if entry.get_metadata().file_type == .directory { 'directory' } else { 'file' }
|
||||||
@@ -76,9 +60,7 @@ for entry in root_entries {
|
|||||||
}
|
}
|
||||||
|
|
||||||
println('\n---------LISTING /dir1 DIRECTORY')
|
println('\n---------LISTING /dir1 DIRECTORY')
|
||||||
dir1_entries := vfs.dir_list('/dir1') or {
|
dir1_entries := vfs.dir_list('/dir1') or { panic('Failed to list directory: ${err}') }
|
||||||
panic('Failed to list directory: ${err}')
|
|
||||||
}
|
|
||||||
println('/dir1 directory contains:')
|
println('/dir1 directory contains:')
|
||||||
for entry in dir1_entries {
|
for entry in dir1_entries {
|
||||||
entry_type := if entry.get_metadata().file_type == .directory { 'directory' } else { 'file' }
|
entry_type := if entry.get_metadata().file_type == .directory { 'directory' } else { 'file' }
|
||||||
@@ -87,9 +69,7 @@ for entry in dir1_entries {
|
|||||||
|
|
||||||
// Write a file in a subdirectory
|
// Write a file in a subdirectory
|
||||||
println('\n---------WRITING FILE IN SUBDIRECTORY')
|
println('\n---------WRITING FILE IN SUBDIRECTORY')
|
||||||
vfs.file_create('/dir1/subdir1/test_file.txt') or {
|
vfs.file_create('/dir1/subdir1/test_file.txt') or { panic('Failed to create file: ${err}') }
|
||||||
panic('Failed to create file: ${err}')
|
|
||||||
}
|
|
||||||
println('Created file: /dir1/subdir1/test_file.txt')
|
println('Created file: /dir1/subdir1/test_file.txt')
|
||||||
|
|
||||||
test_content := 'This is a test file in a subdirectory'
|
test_content := 'This is a test file in a subdirectory'
|
||||||
@@ -104,13 +84,15 @@ file_content := vfs.file_read('/dir1/subdir1/test_file.txt') or {
|
|||||||
panic('Failed to read file: ${err}')
|
panic('Failed to read file: ${err}')
|
||||||
}
|
}
|
||||||
println('File content: ${file_content.bytestr()}')
|
println('File content: ${file_content.bytestr()}')
|
||||||
println('Content verification: ${if file_content.bytestr() == test_content { 'SUCCESS' } else { 'FAILED' }}')
|
println('Content verification: ${if file_content.bytestr() == test_content {
|
||||||
|
'SUCCESS'
|
||||||
|
} else {
|
||||||
|
'FAILED'
|
||||||
|
}}')
|
||||||
|
|
||||||
// List the subdirectory to see the file
|
// List the subdirectory to see the file
|
||||||
println('\n---------LISTING /dir1/subdir1 DIRECTORY')
|
println('\n---------LISTING /dir1/subdir1 DIRECTORY')
|
||||||
subdir1_entries := vfs.dir_list('/dir1/subdir1') or {
|
subdir1_entries := vfs.dir_list('/dir1/subdir1') or { panic('Failed to list directory: ${err}') }
|
||||||
panic('Failed to list directory: ${err}')
|
|
||||||
}
|
|
||||||
println('/dir1/subdir1 directory contains:')
|
println('/dir1/subdir1 directory contains:')
|
||||||
for entry in subdir1_entries {
|
for entry in subdir1_entries {
|
||||||
entry_type := if entry.get_metadata().file_type == .directory { 'directory' } else { 'file' }
|
entry_type := if entry.get_metadata().file_type == .directory { 'directory' } else { 'file' }
|
||||||
@@ -119,9 +101,7 @@ for entry in subdir1_entries {
|
|||||||
|
|
||||||
// Delete the file
|
// Delete the file
|
||||||
println('\n---------DELETING FILE')
|
println('\n---------DELETING FILE')
|
||||||
vfs.file_delete('/dir1/subdir1/test_file.txt') or {
|
vfs.file_delete('/dir1/subdir1/test_file.txt') or { panic('Failed to delete file: ${err}') }
|
||||||
panic('Failed to delete file: ${err}')
|
|
||||||
}
|
|
||||||
println('Deleted file: /dir1/subdir1/test_file.txt')
|
println('Deleted file: /dir1/subdir1/test_file.txt')
|
||||||
|
|
||||||
// List the subdirectory again to verify the file is gone
|
// List the subdirectory again to verify the file is gone
|
||||||
@@ -158,7 +138,11 @@ deep_file_content := vfs.file_read('/dir2/subdir1/subsubdir1/deep_file.txt') or
|
|||||||
panic('Failed to read file: ${err}')
|
panic('Failed to read file: ${err}')
|
||||||
}
|
}
|
||||||
println('File content: ${deep_file_content.bytestr()}')
|
println('File content: ${deep_file_content.bytestr()}')
|
||||||
println('Content verification: ${if deep_file_content.bytestr() == deep_content { 'SUCCESS' } else { 'FAILED' }}')
|
println('Content verification: ${if deep_file_content.bytestr() == deep_content {
|
||||||
|
'SUCCESS'
|
||||||
|
} else {
|
||||||
|
'FAILED'
|
||||||
|
}}')
|
||||||
|
|
||||||
// Clean up by deleting directories (optional)
|
// Clean up by deleting directories (optional)
|
||||||
println('\n---------CLEANING UP')
|
println('\n---------CLEANING UP')
|
||||||
|
|||||||
@@ -6,14 +6,17 @@ import freeflowuniverse.herolib.data.ourdb
|
|||||||
import os
|
import os
|
||||||
import log
|
import log
|
||||||
|
|
||||||
const database_path := os.join_path(os.dir(@FILE), 'database')
|
const database_path = os.join_path(os.dir(@FILE), 'database')
|
||||||
|
|
||||||
mut metadata_db := ourdb.new(path:os.join_path(database_path, 'metadata'))!
|
mut metadata_db := ourdb.new(path: os.join_path(database_path, 'metadata'))!
|
||||||
mut data_db := ourdb.new(path:os.join_path(database_path, 'data'))!
|
mut data_db := ourdb.new(path: os.join_path(database_path, 'data'))!
|
||||||
mut vfs := vfs_db.new(mut metadata_db, mut data_db)!
|
mut vfs := vfs_db.new(mut metadata_db, mut data_db)!
|
||||||
mut server := webdav.new_server(vfs: vfs, user_db: {
|
mut server := webdav.new_server(
|
||||||
'admin': '123'
|
vfs: vfs
|
||||||
})!
|
user_db: {
|
||||||
|
'admin': '123'
|
||||||
|
}
|
||||||
|
)!
|
||||||
|
|
||||||
log.set_level(.debug)
|
log.set_level(.debug)
|
||||||
|
|
||||||
|
|||||||
@@ -91,4 +91,3 @@ println('\nFootnotes:')
|
|||||||
for id, footnote in nav.footnotes() {
|
for id, footnote in nav.footnotes() {
|
||||||
println(' [^${id}]: ${footnote.content}')
|
println(' [^${id}]: ${footnote.content}')
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -7,8 +7,8 @@ import os
|
|||||||
import markdown
|
import markdown
|
||||||
import freeflowuniverse.herolib.data.markdownparser2
|
import freeflowuniverse.herolib.data.markdownparser2
|
||||||
|
|
||||||
path2:="${os.home_dir()}/code/github/freeflowuniverse/herolib/examples/webtools/mdbook_markdown/content/links.md"
|
path2 := '${os.home_dir()}/code/github/freeflowuniverse/herolib/examples/webtools/mdbook_markdown/content/links.md'
|
||||||
path1:="${os.home_dir()}/code/github/freeflowuniverse/herolib/examples/webtools/mdbook_markdown/content/test.md"
|
path1 := '${os.home_dir()}/code/github/freeflowuniverse/herolib/examples/webtools/mdbook_markdown/content/test.md'
|
||||||
|
|
||||||
text := os.read_file(path1)!
|
text := os.read_file(path1)!
|
||||||
|
|
||||||
|
|||||||
@@ -18,10 +18,9 @@ for project in 'projectinca, legal, why'.split(',').map(it.trim_space()) {
|
|||||||
)!
|
)!
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
tree.export(
|
tree.export(
|
||||||
destination: '/tmp/mdexport'
|
destination: '/tmp/mdexport'
|
||||||
reset: true
|
reset: true
|
||||||
//keep_structure: true
|
// keep_structure: true
|
||||||
exclude_errors: false
|
exclude_errors: false
|
||||||
)!
|
)!
|
||||||
|
|||||||
@@ -10,8 +10,8 @@ mut docs := starlight.new(
|
|||||||
|
|
||||||
// Create a new starlight site
|
// Create a new starlight site
|
||||||
mut site := docs.get(
|
mut site := docs.get(
|
||||||
url: 'https://git.ourworld.tf/tfgrid/docs_aibox'
|
url: 'https://git.ourworld.tf/tfgrid/docs_aibox'
|
||||||
init:true //init means we put config files if not there
|
init: true // init means we put config files if not there
|
||||||
)!
|
)!
|
||||||
|
|
||||||
site.dev()!
|
site.dev()!
|
||||||
26
install_v.sh
26
install_v.sh
@@ -375,11 +375,29 @@ check_and_start_redis() {
|
|||||||
fi
|
fi
|
||||||
fi
|
fi
|
||||||
elif [[ "${OSNAME}" == "darwin"* ]]; then
|
elif [[ "${OSNAME}" == "darwin"* ]]; then
|
||||||
if brew services list | grep -q "^redis.*started"; then
|
# Check if we're in GitHub Actions
|
||||||
echo "redis is already running."
|
if is_github_actions; then
|
||||||
|
echo "Running in GitHub Actions on macOS. Starting redis directly..."
|
||||||
|
if pgrep redis-server > /dev/null; then
|
||||||
|
echo "redis is already running."
|
||||||
|
else
|
||||||
|
echo "redis is not running. Starting it in the background..."
|
||||||
|
redis-server --daemonize yes
|
||||||
|
if pgrep redis-server > /dev/null; then
|
||||||
|
echo "redis started successfully."
|
||||||
|
else
|
||||||
|
echo "Failed to start redis. Please check logs for details."
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
fi
|
||||||
else
|
else
|
||||||
echo "redis is not running. Starting it..."
|
# For regular macOS environments, use brew services
|
||||||
brew services start redis
|
if brew services list | grep -q "^redis.*started"; then
|
||||||
|
echo "redis is already running."
|
||||||
|
else
|
||||||
|
echo "redis is not running. Starting it..."
|
||||||
|
brew services start redis
|
||||||
|
fi
|
||||||
fi
|
fi
|
||||||
elif [[ "${OSNAME}" == "alpine"* ]]; then
|
elif [[ "${OSNAME}" == "alpine"* ]]; then
|
||||||
if rc-service "redis" status | grep -q "running"; then
|
if rc-service "redis" status | grep -q "running"; then
|
||||||
|
|||||||
@@ -1,10 +1,10 @@
|
|||||||
module generator
|
module generator
|
||||||
|
|
||||||
import freeflowuniverse.herolib.core.code {Folder, File}
|
import freeflowuniverse.herolib.core.code
|
||||||
import freeflowuniverse.herolib.core.texttools
|
import freeflowuniverse.herolib.core.texttools
|
||||||
import freeflowuniverse.herolib.schemas.jsonschema.codegen { schema_to_struct }
|
import freeflowuniverse.herolib.schemas.jsonschema.codegen
|
||||||
import freeflowuniverse.herolib.schemas.openrpc.codegen as openrpc_codegen { content_descriptor_to_parameter }
|
import freeflowuniverse.herolib.schemas.openrpc.codegen as openrpc_codegen
|
||||||
import freeflowuniverse.herolib.baobab.specification {ActorSpecification, ActorMethod, BaseObject}
|
import freeflowuniverse.herolib.baobab.specification
|
||||||
import net.http
|
import net.http
|
||||||
|
|
||||||
// pub enum BaseObjectMethodType {
|
// pub enum BaseObjectMethodType {
|
||||||
@@ -58,7 +58,7 @@ fn get_endpoint_root(root string) string {
|
|||||||
return if root == '' {
|
return if root == '' {
|
||||||
''
|
''
|
||||||
} else {
|
} else {
|
||||||
"/${root.trim('/')}"
|
'/${root.trim('/')}'
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -8,193 +8,196 @@ import freeflowuniverse.herolib.schemas.openrpc
|
|||||||
import freeflowuniverse.herolib.schemas.jsonschema
|
import freeflowuniverse.herolib.schemas.jsonschema
|
||||||
|
|
||||||
const specification = specification.ActorSpecification{
|
const specification = specification.ActorSpecification{
|
||||||
name: 'Pet Store'
|
name: 'Pet Store'
|
||||||
description: 'A sample API for a pet store'
|
description: 'A sample API for a pet store'
|
||||||
structure: code.Struct{}
|
structure: code.Struct{}
|
||||||
interfaces: [.openapi]
|
interfaces: [.openapi]
|
||||||
methods: [
|
methods: [
|
||||||
specification.ActorMethod{
|
specification.ActorMethod{
|
||||||
name: 'listPets'
|
name: 'listPets'
|
||||||
summary: 'List all pets'
|
summary: 'List all pets'
|
||||||
example: openrpc.ExamplePairing{
|
example: openrpc.ExamplePairing{
|
||||||
params: [
|
params: [
|
||||||
openrpc.ExampleRef(openrpc.Example{
|
openrpc.ExampleRef(openrpc.Example{
|
||||||
name: 'Example limit'
|
name: 'Example limit'
|
||||||
description: 'Example Maximum number of pets to return'
|
description: 'Example Maximum number of pets to return'
|
||||||
value: 10
|
value: 10
|
||||||
})
|
}),
|
||||||
]
|
]
|
||||||
result: openrpc.ExampleRef(openrpc.Example{
|
result: openrpc.ExampleRef(openrpc.Example{
|
||||||
name: 'Example response'
|
name: 'Example response'
|
||||||
value: json.raw_decode('[
|
value: json.raw_decode('[
|
||||||
{"id": 1, "name": "Fluffy", "tag": "dog"},
|
{"id": 1, "name": "Fluffy", "tag": "dog"},
|
||||||
{"id": 2, "name": "Whiskers", "tag": "cat"}
|
{"id": 2, "name": "Whiskers", "tag": "cat"}
|
||||||
]')!
|
]')!
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
parameters: [
|
parameters: [
|
||||||
openrpc.ContentDescriptor{
|
openrpc.ContentDescriptor{
|
||||||
name: 'limit'
|
name: 'limit'
|
||||||
summary: 'Maximum number of pets to return'
|
summary: 'Maximum number of pets to return'
|
||||||
description: 'Maximum number of pets to return'
|
description: 'Maximum number of pets to return'
|
||||||
required: false
|
required: false
|
||||||
schema: jsonschema.SchemaRef(jsonschema.Schema{
|
schema: jsonschema.SchemaRef(jsonschema.Schema{
|
||||||
...jsonschema.schema_u32,
|
...jsonschema.schema_u32
|
||||||
example: 10
|
example: 10
|
||||||
})
|
})
|
||||||
}
|
},
|
||||||
]
|
]
|
||||||
result: openrpc.ContentDescriptor{
|
result: openrpc.ContentDescriptor{
|
||||||
name: 'pets'
|
name: 'pets'
|
||||||
description: 'A paged array of pets'
|
description: 'A paged array of pets'
|
||||||
schema: jsonschema.SchemaRef(jsonschema.Schema{
|
schema: jsonschema.SchemaRef(jsonschema.Schema{
|
||||||
typ: 'array'
|
typ: 'array'
|
||||||
items: jsonschema.Items(jsonschema.SchemaRef(jsonschema.Schema{
|
items: jsonschema.Items(jsonschema.SchemaRef(jsonschema.Schema{
|
||||||
id: 'pet'
|
id: 'pet'
|
||||||
title: 'Pet'
|
title: 'Pet'
|
||||||
typ: 'object'
|
typ: 'object'
|
||||||
properties: {
|
properties: {
|
||||||
'id': jsonschema.SchemaRef(jsonschema.Reference{
|
'id': jsonschema.SchemaRef(jsonschema.Reference{
|
||||||
ref: '#/components/schemas/PetId'
|
ref: '#/components/schemas/PetId'
|
||||||
}),
|
})
|
||||||
'name': jsonschema.SchemaRef(jsonschema.Schema{
|
'name': jsonschema.SchemaRef(jsonschema.Schema{
|
||||||
typ: 'string'
|
typ: 'string'
|
||||||
}),
|
})
|
||||||
'tag': jsonschema.SchemaRef(jsonschema.Schema{
|
'tag': jsonschema.SchemaRef(jsonschema.Schema{
|
||||||
typ: 'string'
|
typ: 'string'
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
required: ['id', 'name']
|
required: [
|
||||||
}))
|
'id',
|
||||||
})
|
'name',
|
||||||
}
|
]
|
||||||
errors: [
|
}))
|
||||||
openrpc.ErrorSpec{
|
})
|
||||||
code: 400
|
}
|
||||||
message: 'Invalid request'
|
errors: [
|
||||||
}
|
openrpc.ErrorSpec{
|
||||||
]
|
code: 400
|
||||||
},
|
message: 'Invalid request'
|
||||||
specification.ActorMethod{
|
},
|
||||||
name: 'createPet'
|
]
|
||||||
summary: 'Create a new pet'
|
},
|
||||||
example: openrpc.ExamplePairing{
|
specification.ActorMethod{
|
||||||
result: openrpc.ExampleRef(openrpc.Example{
|
name: 'createPet'
|
||||||
name: 'Example response'
|
summary: 'Create a new pet'
|
||||||
value: '[]'
|
example: openrpc.ExamplePairing{
|
||||||
})
|
result: openrpc.ExampleRef(openrpc.Example{
|
||||||
}
|
name: 'Example response'
|
||||||
result: openrpc.ContentDescriptor{
|
value: '[]'
|
||||||
name: 'result'
|
})
|
||||||
description: 'The response of the operation.'
|
}
|
||||||
required: true
|
result: openrpc.ContentDescriptor{
|
||||||
}
|
name: 'result'
|
||||||
errors: [
|
description: 'The response of the operation.'
|
||||||
openrpc.ErrorSpec{
|
required: true
|
||||||
code: 400
|
}
|
||||||
message: 'Invalid input'
|
errors: [
|
||||||
}
|
openrpc.ErrorSpec{
|
||||||
]
|
code: 400
|
||||||
},
|
message: 'Invalid input'
|
||||||
specification.ActorMethod{
|
},
|
||||||
name: 'getPet'
|
]
|
||||||
summary: 'Get a pet by ID'
|
},
|
||||||
example: openrpc.ExamplePairing{
|
specification.ActorMethod{
|
||||||
params: [
|
name: 'getPet'
|
||||||
openrpc.ExampleRef(openrpc.Example{
|
summary: 'Get a pet by ID'
|
||||||
name: 'Example petId'
|
example: openrpc.ExamplePairing{
|
||||||
description: 'Example ID of the pet to retrieve'
|
params: [
|
||||||
value: 1
|
openrpc.ExampleRef(openrpc.Example{
|
||||||
})
|
name: 'Example petId'
|
||||||
]
|
description: 'Example ID of the pet to retrieve'
|
||||||
result: openrpc.ExampleRef(openrpc.Example{
|
value: 1
|
||||||
name: 'Example response'
|
}),
|
||||||
value: json.raw_decode('{"id": 1, "name": "Fluffy", "tag": "dog"}')!
|
]
|
||||||
})
|
result: openrpc.ExampleRef(openrpc.Example{
|
||||||
}
|
name: 'Example response'
|
||||||
parameters: [
|
value: json.raw_decode('{"id": 1, "name": "Fluffy", "tag": "dog"}')!
|
||||||
openrpc.ContentDescriptor{
|
})
|
||||||
name: 'petId'
|
}
|
||||||
summary: 'ID of the pet to retrieve'
|
parameters: [
|
||||||
description: 'ID of the pet to retrieve'
|
openrpc.ContentDescriptor{
|
||||||
required: true
|
name: 'petId'
|
||||||
schema: jsonschema.SchemaRef(jsonschema.Schema{
|
summary: 'ID of the pet to retrieve'
|
||||||
...jsonschema.schema_u32,
|
description: 'ID of the pet to retrieve'
|
||||||
format:'uint32'
|
required: true
|
||||||
example: 1
|
schema: jsonschema.SchemaRef(jsonschema.Schema{
|
||||||
})
|
...jsonschema.schema_u32
|
||||||
}
|
format: 'uint32'
|
||||||
]
|
example: 1
|
||||||
result: openrpc.ContentDescriptor{
|
})
|
||||||
name: 'result'
|
},
|
||||||
description: 'The response of the operation.'
|
]
|
||||||
required: true
|
result: openrpc.ContentDescriptor{
|
||||||
schema: jsonschema.SchemaRef(jsonschema.Reference{
|
name: 'result'
|
||||||
ref: '#/components/schemas/Pet'
|
description: 'The response of the operation.'
|
||||||
})
|
required: true
|
||||||
}
|
schema: jsonschema.SchemaRef(jsonschema.Reference{
|
||||||
errors: [
|
ref: '#/components/schemas/Pet'
|
||||||
openrpc.ErrorSpec{
|
})
|
||||||
code: 404
|
}
|
||||||
message: 'Pet not found'
|
errors: [
|
||||||
}
|
openrpc.ErrorSpec{
|
||||||
]
|
code: 404
|
||||||
},
|
message: 'Pet not found'
|
||||||
specification.ActorMethod{
|
},
|
||||||
name: 'deletePet'
|
]
|
||||||
summary: 'Delete a pet by ID'
|
},
|
||||||
example: openrpc.ExamplePairing{
|
specification.ActorMethod{
|
||||||
params: [
|
name: 'deletePet'
|
||||||
openrpc.ExampleRef(openrpc.Example{
|
summary: 'Delete a pet by ID'
|
||||||
name: 'Example petId'
|
example: openrpc.ExamplePairing{
|
||||||
description: 'Example ID of the pet to delete'
|
params: [
|
||||||
value: 1
|
openrpc.ExampleRef(openrpc.Example{
|
||||||
})
|
name: 'Example petId'
|
||||||
]
|
description: 'Example ID of the pet to delete'
|
||||||
}
|
value: 1
|
||||||
parameters: [
|
}),
|
||||||
openrpc.ContentDescriptor{
|
]
|
||||||
name: 'petId'
|
}
|
||||||
summary: 'ID of the pet to delete'
|
parameters: [
|
||||||
description: 'ID of the pet to delete'
|
openrpc.ContentDescriptor{
|
||||||
required: true
|
name: 'petId'
|
||||||
schema: jsonschema.SchemaRef(jsonschema.Schema{
|
summary: 'ID of the pet to delete'
|
||||||
...jsonschema.schema_u32,
|
description: 'ID of the pet to delete'
|
||||||
example: 1
|
required: true
|
||||||
})
|
schema: jsonschema.SchemaRef(jsonschema.Schema{
|
||||||
}
|
...jsonschema.schema_u32
|
||||||
]
|
example: 1
|
||||||
result: openrpc.ContentDescriptor{
|
})
|
||||||
name: 'result'
|
},
|
||||||
description: 'The response of the operation.'
|
]
|
||||||
required: true
|
result: openrpc.ContentDescriptor{
|
||||||
}
|
name: 'result'
|
||||||
errors: [
|
description: 'The response of the operation.'
|
||||||
openrpc.ErrorSpec{
|
required: true
|
||||||
code: 404
|
}
|
||||||
message: 'Pet not found'
|
errors: [
|
||||||
}
|
openrpc.ErrorSpec{
|
||||||
]
|
code: 404
|
||||||
}
|
message: 'Pet not found'
|
||||||
]
|
},
|
||||||
objects: [
|
]
|
||||||
specification.BaseObject{
|
},
|
||||||
schema: jsonschema.Schema{
|
]
|
||||||
title: 'Pet'
|
objects: [
|
||||||
typ: 'object'
|
specification.BaseObject{
|
||||||
properties: {
|
schema: jsonschema.Schema{
|
||||||
'id': jsonschema.schema_u32,
|
title: 'Pet'
|
||||||
'name': jsonschema.SchemaRef(jsonschema.Schema{
|
typ: 'object'
|
||||||
typ: 'string'
|
properties: {
|
||||||
}),
|
'id': jsonschema.schema_u32
|
||||||
'tag': jsonschema.SchemaRef(jsonschema.Schema{
|
'name': jsonschema.SchemaRef(jsonschema.Schema{
|
||||||
typ: 'string'
|
typ: 'string'
|
||||||
})
|
})
|
||||||
}
|
'tag': jsonschema.SchemaRef(jsonschema.Schema{
|
||||||
required: ['id', 'name']
|
typ: 'string'
|
||||||
}
|
})
|
||||||
}
|
}
|
||||||
]
|
required: ['id', 'name']
|
||||||
|
}
|
||||||
|
},
|
||||||
|
]
|
||||||
}
|
}
|
||||||
|
|
||||||
fn test_typescript_client_folder() {
|
fn test_typescript_client_folder() {
|
||||||
|
|||||||
@@ -1,12 +1,12 @@
|
|||||||
module generator
|
module generator
|
||||||
|
|
||||||
import freeflowuniverse.herolib.baobab.specification {BaseObject}
|
import freeflowuniverse.herolib.baobab.specification
|
||||||
import freeflowuniverse.herolib.core.code { type_from_symbol, VFile, CodeItem, Function, Import, Param, Param, Struct, StructField, Type }
|
import freeflowuniverse.herolib.core.code { Param, Param, type_from_symbol }
|
||||||
import freeflowuniverse.herolib.core.texttools
|
import freeflowuniverse.herolib.core.texttools
|
||||||
|
|
||||||
const id_param = Param{
|
const id_param = Param{
|
||||||
name: 'id'
|
name: 'id'
|
||||||
typ: type_from_symbol('u32')
|
typ: type_from_symbol('u32')
|
||||||
}
|
}
|
||||||
|
|
||||||
// pub fn generate_object_code(actor Struct, object BaseObject) VFile {
|
// pub fn generate_object_code(actor Struct, object BaseObject) VFile {
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
module generator
|
module generator
|
||||||
|
|
||||||
import freeflowuniverse.herolib.core.code { VFile, CustomCode, Function, Import, Struct }
|
import freeflowuniverse.herolib.core.code
|
||||||
import freeflowuniverse.herolib.baobab.specification {BaseObject}
|
import freeflowuniverse.herolib.baobab.specification
|
||||||
import rand
|
import rand
|
||||||
import freeflowuniverse.herolib.core.texttools
|
import freeflowuniverse.herolib.core.texttools
|
||||||
|
|
||||||
|
|||||||
@@ -1,10 +1,10 @@
|
|||||||
module generator
|
module generator
|
||||||
|
|
||||||
import freeflowuniverse.herolib.core.code { Result, Object, Param, Folder, IFile, VFile, CodeItem, File, Function, Import, Module, Struct, CustomCode }
|
import freeflowuniverse.herolib.core.code { CodeItem, CustomCode, Function, Import, Object, Param, Result, VFile }
|
||||||
import freeflowuniverse.herolib.core.texttools
|
import freeflowuniverse.herolib.core.texttools
|
||||||
import freeflowuniverse.herolib.schemas.openrpc {Example, ContentDescriptor}
|
import freeflowuniverse.herolib.schemas.openrpc { ContentDescriptor, Example }
|
||||||
import freeflowuniverse.herolib.schemas.jsonschema.codegen {schemaref_to_type}
|
import freeflowuniverse.herolib.schemas.jsonschema.codegen { schemaref_to_type }
|
||||||
import freeflowuniverse.herolib.baobab.specification {ActorMethod, ActorSpecification}
|
import freeflowuniverse.herolib.baobab.specification { ActorMethod, ActorSpecification }
|
||||||
|
|
||||||
fn generate_handle_file(spec ActorSpecification) !VFile {
|
fn generate_handle_file(spec ActorSpecification) !VFile {
|
||||||
mut items := []CodeItem{}
|
mut items := []CodeItem{}
|
||||||
@@ -12,14 +12,21 @@ fn generate_handle_file(spec ActorSpecification) !VFile {
|
|||||||
for method in spec.methods {
|
for method in spec.methods {
|
||||||
items << generate_method_handle(spec.name, method)!
|
items << generate_method_handle(spec.name, method)!
|
||||||
}
|
}
|
||||||
return VFile {
|
return VFile{
|
||||||
name: 'act'
|
name: 'act'
|
||||||
imports: [
|
imports: [
|
||||||
Import{mod:'freeflowuniverse.herolib.baobab.stage' types:['Action']}
|
Import{
|
||||||
Import{mod:'freeflowuniverse.herolib.core.texttools'}
|
mod: 'freeflowuniverse.herolib.baobab.stage'
|
||||||
Import{mod:'x.json2 as json'}
|
types: ['Action']
|
||||||
|
},
|
||||||
|
Import{
|
||||||
|
mod: 'freeflowuniverse.herolib.core.texttools'
|
||||||
|
},
|
||||||
|
Import{
|
||||||
|
mod: 'x.json2 as json'
|
||||||
|
},
|
||||||
]
|
]
|
||||||
items: items
|
items: items
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -74,13 +81,22 @@ pub fn generate_method_handle(actor_name string, method ActorMethod) !Function {
|
|||||||
call_stmt := generate_call_stmt(actor_name, method)!
|
call_stmt := generate_call_stmt(actor_name, method)!
|
||||||
body += '${call_stmt}\n'
|
body += '${call_stmt}\n'
|
||||||
body += '${generate_return_stmt(method)!}\n'
|
body += '${generate_return_stmt(method)!}\n'
|
||||||
return Function {
|
return Function{
|
||||||
name: 'handle_${name_fixed}'
|
name: 'handle_${name_fixed}'
|
||||||
description: '// Handler for ${name_fixed}\n'
|
description: '// Handler for ${name_fixed}\n'
|
||||||
receiver: Param{name: 'actor', mutable: true, typ: Object{'${actor_name_pascal}Actor'}}
|
receiver: Param{
|
||||||
params: [Param{name: 'action', typ: Object{'Action'}}]
|
name: 'actor'
|
||||||
result: Param{typ: Result{Object{'Action'}}}
|
mutable: true
|
||||||
body: body
|
typ: Object{'${actor_name_pascal}Actor'}
|
||||||
|
}
|
||||||
|
params: [Param{
|
||||||
|
name: 'action'
|
||||||
|
typ: Object{'Action'}
|
||||||
|
}]
|
||||||
|
result: Param{
|
||||||
|
typ: Result{Object{'Action'}}
|
||||||
|
}
|
||||||
|
body: body
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -95,28 +111,41 @@ pub fn generate_example_method_handle(actor_name string, method ActorMethod) !Fu
|
|||||||
if method.example.result is Example {
|
if method.example.result is Example {
|
||||||
'return Action{...action, result: json.encode(\'${method.example.result.value}\')}'
|
'return Action{...action, result: json.encode(\'${method.example.result.value}\')}'
|
||||||
} else {
|
} else {
|
||||||
"return action"
|
'return action'
|
||||||
}
|
}
|
||||||
} else { "return action" }
|
} else {
|
||||||
return Function {
|
'return action'
|
||||||
name: 'handle_${name_fixed}_example'
|
}
|
||||||
|
return Function{
|
||||||
|
name: 'handle_${name_fixed}_example'
|
||||||
description: '// Handler for ${name_fixed}\n'
|
description: '// Handler for ${name_fixed}\n'
|
||||||
receiver: Param{name: 'actor', mutable: true, typ: Object{'${actor_name_pascal}Actor'}}
|
receiver: Param{
|
||||||
params: [Param{name: 'action', typ: Object{'Action'}}]
|
name: 'actor'
|
||||||
result: Param{typ: Result{Object{'Action'}}}
|
mutable: true
|
||||||
body: body
|
typ: Object{'${actor_name_pascal}Actor'}
|
||||||
|
}
|
||||||
|
params: [Param{
|
||||||
|
name: 'action'
|
||||||
|
typ: Object{'Action'}
|
||||||
|
}]
|
||||||
|
result: Param{
|
||||||
|
typ: Result{Object{'Action'}}
|
||||||
|
}
|
||||||
|
body: body
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn generate_call_stmt(name string, method ActorMethod) !string {
|
fn generate_call_stmt(name string, method ActorMethod) !string {
|
||||||
mut call_stmt := if schemaref_to_type(method.result.schema).vgen().trim_space() != '' {
|
mut call_stmt := if schemaref_to_type(method.result.schema).vgen().trim_space() != '' {
|
||||||
'${texttools.snake_case(method.result.name)} := '
|
'${texttools.snake_case(method.result.name)} := '
|
||||||
} else {''}
|
} else {
|
||||||
|
''
|
||||||
|
}
|
||||||
method_name := texttools.snake_case(method.name)
|
method_name := texttools.snake_case(method.name)
|
||||||
snake_name := texttools.snake_case(name)
|
snake_name := texttools.snake_case(name)
|
||||||
|
|
||||||
param_names := method.parameters.map(texttools.snake_case(it.name))
|
param_names := method.parameters.map(texttools.snake_case(it.name))
|
||||||
call_stmt += 'actor.${snake_name}.${method_name}(${param_names.join(", ")})!'
|
call_stmt += 'actor.${snake_name}.${method_name}(${param_names.join(', ')})!'
|
||||||
return call_stmt
|
return call_stmt
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -124,7 +153,7 @@ fn generate_return_stmt(method ActorMethod) !string {
|
|||||||
if schemaref_to_type(method.result.schema).vgen().trim_space() != '' {
|
if schemaref_to_type(method.result.schema).vgen().trim_space() != '' {
|
||||||
return 'return Action{...action, result: json.encode(${texttools.snake_case(method.result.name)})}'
|
return 'return Action{...action, result: json.encode(${texttools.snake_case(method.result.name)})}'
|
||||||
}
|
}
|
||||||
return "return action"
|
return 'return action'
|
||||||
}
|
}
|
||||||
|
|
||||||
// Helper function to generate a case block for the main router
|
// Helper function to generate a case block for the main router
|
||||||
@@ -138,12 +167,13 @@ fn generate_decode_stmt(name string, param ContentDescriptor) !string {
|
|||||||
param_type := schemaref_to_type(param.schema)
|
param_type := schemaref_to_type(param.schema)
|
||||||
if param_type is Object {
|
if param_type is Object {
|
||||||
return 'json.decode[${schemaref_to_type(param.schema).vgen()}](${name})!'
|
return 'json.decode[${schemaref_to_type(param.schema).vgen()}](${name})!'
|
||||||
}
|
} else if param_type is code.Array {
|
||||||
else if param_type is code.Array {
|
|
||||||
return 'json.decode[${schemaref_to_type(param.schema).vgen()}](${name})'
|
return 'json.decode[${schemaref_to_type(param.schema).vgen()}](${name})'
|
||||||
}
|
}
|
||||||
param_symbol := param_type.vgen()
|
param_symbol := param_type.vgen()
|
||||||
return if param_symbol == 'string' {
|
return if param_symbol == 'string' {
|
||||||
'${name}.str()'
|
'${name}.str()'
|
||||||
} else {'${name}.${param_type.vgen()}()'}
|
} else {
|
||||||
|
'${name}.${param_type.vgen()}()'
|
||||||
|
}
|
||||||
}
|
}
|
||||||
@@ -1,9 +1,9 @@
|
|||||||
module generator
|
module generator
|
||||||
|
|
||||||
import freeflowuniverse.herolib.core.code { Folder, IFolder, IFile, VFile, CodeItem, File, Function, Import, Module, Struct, CustomCode }
|
import freeflowuniverse.herolib.core.code { File, Folder, IFile, IFolder }
|
||||||
import freeflowuniverse.herolib.schemas.openapi
|
import freeflowuniverse.herolib.schemas.openapi
|
||||||
import freeflowuniverse.herolib.core.texttools
|
import freeflowuniverse.herolib.core.texttools
|
||||||
import freeflowuniverse.herolib.baobab.specification {ActorMethod, ActorSpecification, ActorInterface}
|
import freeflowuniverse.herolib.baobab.specification { ActorInterface, ActorSpecification }
|
||||||
import json
|
import json
|
||||||
|
|
||||||
@[params]
|
@[params]
|
||||||
@@ -41,15 +41,15 @@ pub fn generate_actor_folder(spec ActorSpecification, params Params) !Folder {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
specs_folder := Folder {
|
specs_folder := Folder{
|
||||||
name: 'specs'
|
name: 'specs'
|
||||||
files: spec_files
|
files: spec_files
|
||||||
}
|
}
|
||||||
|
|
||||||
// folder with docs
|
// folder with docs
|
||||||
folders << Folder {
|
folders << Folder{
|
||||||
name: 'docs'
|
name: 'docs'
|
||||||
files: docs_files
|
files: docs_files
|
||||||
folders: [specs_folder]
|
folders: [specs_folder]
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -59,9 +59,9 @@ pub fn generate_actor_folder(spec ActorSpecification, params Params) !Folder {
|
|||||||
// create module with code files and docs folder
|
// create module with code files and docs folder
|
||||||
name_fixed := texttools.snake_case(spec.name)
|
name_fixed := texttools.snake_case(spec.name)
|
||||||
|
|
||||||
return code.Folder{
|
return Folder{
|
||||||
name: '${name_fixed}'
|
name: '${name_fixed}'
|
||||||
files: files
|
files: files
|
||||||
folders: folders
|
folders: folders
|
||||||
modules: [generate_actor_module(spec, params)!]
|
modules: [generate_actor_module(spec, params)!]
|
||||||
}
|
}
|
||||||
@@ -69,14 +69,14 @@ pub fn generate_actor_folder(spec ActorSpecification, params Params) !Folder {
|
|||||||
|
|
||||||
fn generate_readme_file(spec ActorSpecification) !File {
|
fn generate_readme_file(spec ActorSpecification) !File {
|
||||||
return File{
|
return File{
|
||||||
name: 'README'
|
name: 'README'
|
||||||
extension: 'md'
|
extension: 'md'
|
||||||
content: '# ${spec.name}\n${spec.description}'
|
content: '# ${spec.name}\n${spec.description}'
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn generate_examples_folder() !Folder {
|
pub fn generate_examples_folder() !Folder {
|
||||||
return Folder {
|
return Folder{
|
||||||
name: 'examples'
|
name: 'examples'
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1,9 +1,9 @@
|
|||||||
module generator
|
module generator
|
||||||
|
|
||||||
import freeflowuniverse.herolib.core.code { Folder, IFolder, IFile, VFile, CodeItem, File, Function, Import, Module, Struct, CustomCode }
|
import freeflowuniverse.herolib.core.code { CustomCode, IFile, IFolder, Module, VFile }
|
||||||
import freeflowuniverse.herolib.schemas.openapi
|
import freeflowuniverse.herolib.schemas.openapi
|
||||||
import freeflowuniverse.herolib.core.texttools
|
import freeflowuniverse.herolib.core.texttools
|
||||||
import freeflowuniverse.herolib.baobab.specification {ActorMethod, ActorSpecification, ActorInterface}
|
import freeflowuniverse.herolib.baobab.specification { ActorInterface, ActorSpecification }
|
||||||
import json
|
import json
|
||||||
|
|
||||||
pub fn generate_module_from_openapi(openapi_path string) !string {
|
pub fn generate_module_from_openapi(openapi_path string) !string {
|
||||||
@@ -28,11 +28,11 @@ pub fn generate_actor_module(spec ActorSpecification, params Params) !Module {
|
|||||||
generate_actor_test_file(spec)!,
|
generate_actor_test_file(spec)!,
|
||||||
generate_specs_file(spec.name, params.interfaces)!,
|
generate_specs_file(spec.name, params.interfaces)!,
|
||||||
generate_handle_file(spec)!,
|
generate_handle_file(spec)!,
|
||||||
generate_methods_file(spec)!
|
generate_methods_file(spec)!,
|
||||||
generate_methods_interface_file(spec)!
|
generate_methods_interface_file(spec)!,
|
||||||
generate_methods_example_file(spec)!
|
generate_methods_example_file(spec)!,
|
||||||
generate_client_file(spec)!
|
generate_client_file(spec)!,
|
||||||
generate_model_file(spec)!
|
generate_model_file(spec)!,
|
||||||
]
|
]
|
||||||
|
|
||||||
// generate code files for supported interfaces
|
// generate code files for supported interfaces
|
||||||
@@ -74,11 +74,11 @@ pub fn generate_actor_module(spec ActorSpecification, params Params) !Module {
|
|||||||
// create module with code files and docs folder
|
// create module with code files and docs folder
|
||||||
name_fixed := texttools.snake_case(spec.name)
|
name_fixed := texttools.snake_case(spec.name)
|
||||||
return code.new_module(
|
return code.new_module(
|
||||||
name: '${name_fixed}'
|
name: '${name_fixed}'
|
||||||
description: spec.description
|
description: spec.description
|
||||||
files: files
|
files: files
|
||||||
folders: folders
|
folders: folders
|
||||||
in_src: true
|
in_src: true
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -88,8 +88,8 @@ fn generate_actor_file(spec ActorSpecification) !VFile {
|
|||||||
name_snake := texttools.snake_case(spec.name)
|
name_snake := texttools.snake_case(spec.name)
|
||||||
name_pascal := texttools.pascal_case(spec.name)
|
name_pascal := texttools.pascal_case(spec.name)
|
||||||
actor_code := $tmpl('./templates/actor.v.template')
|
actor_code := $tmpl('./templates/actor.v.template')
|
||||||
return VFile {
|
return VFile{
|
||||||
name: 'actor'
|
name: 'actor'
|
||||||
items: [CustomCode{actor_code}]
|
items: [CustomCode{actor_code}]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -99,8 +99,8 @@ fn generate_actor_test_file(spec ActorSpecification) !VFile {
|
|||||||
actor_name_snake := texttools.snake_case(spec.name)
|
actor_name_snake := texttools.snake_case(spec.name)
|
||||||
actor_name_pascal := texttools.pascal_case(spec.name)
|
actor_name_pascal := texttools.pascal_case(spec.name)
|
||||||
actor_test_code := $tmpl('./templates/actor_test.v.template')
|
actor_test_code := $tmpl('./templates/actor_test.v.template')
|
||||||
return VFile {
|
return VFile{
|
||||||
name: 'actor_test'
|
name: 'actor_test'
|
||||||
items: [CustomCode{actor_test_code}]
|
items: [CustomCode{actor_test_code}]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -112,8 +112,8 @@ fn generate_specs_file(name string, interfaces []ActorInterface) !VFile {
|
|||||||
actor_name_snake := texttools.snake_case(name)
|
actor_name_snake := texttools.snake_case(name)
|
||||||
actor_name_pascal := texttools.pascal_case(name)
|
actor_name_pascal := texttools.pascal_case(name)
|
||||||
actor_code := $tmpl('./templates/specifications.v.template')
|
actor_code := $tmpl('./templates/specifications.v.template')
|
||||||
return VFile {
|
return VFile{
|
||||||
name: 'specifications'
|
name: 'specifications'
|
||||||
items: [CustomCode{actor_code}]
|
items: [CustomCode{actor_code}]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -5,269 +5,272 @@ import freeflowuniverse.herolib.baobab.specification
|
|||||||
import freeflowuniverse.herolib.schemas.openrpc
|
import freeflowuniverse.herolib.schemas.openrpc
|
||||||
import freeflowuniverse.herolib.schemas.jsonschema
|
import freeflowuniverse.herolib.schemas.jsonschema
|
||||||
import os
|
import os
|
||||||
import x.json2 as json {Any}
|
import x.json2 as json
|
||||||
|
|
||||||
const actor_spec = specification.ActorSpecification{
|
const actor_spec = specification.ActorSpecification{
|
||||||
name: 'Pet Store'
|
name: 'Pet Store'
|
||||||
description: 'A sample API for a pet store'
|
description: 'A sample API for a pet store'
|
||||||
structure: code.Struct{}
|
structure: code.Struct{}
|
||||||
interfaces: [.openapi]
|
interfaces: [.openapi]
|
||||||
methods: [
|
methods: [
|
||||||
specification.ActorMethod{
|
specification.ActorMethod{
|
||||||
name: 'listPets'
|
name: 'listPets'
|
||||||
summary: 'List all pets'
|
summary: 'List all pets'
|
||||||
example: openrpc.ExamplePairing{
|
example: openrpc.ExamplePairing{
|
||||||
params: [
|
params: [
|
||||||
openrpc.ExampleRef(openrpc.Example{
|
openrpc.ExampleRef(openrpc.Example{
|
||||||
name: 'Example limit'
|
name: 'Example limit'
|
||||||
description: 'Example Maximum number of pets to return'
|
description: 'Example Maximum number of pets to return'
|
||||||
value: 10
|
value: 10
|
||||||
})
|
}),
|
||||||
]
|
]
|
||||||
result: openrpc.ExampleRef(openrpc.Example{
|
result: openrpc.ExampleRef(openrpc.Example{
|
||||||
name: 'Example response'
|
name: 'Example response'
|
||||||
value: json.raw_decode('[
|
value: json.raw_decode('[
|
||||||
{"id": 1, "name": "Fluffy", "tag": "dog"},
|
{"id": 1, "name": "Fluffy", "tag": "dog"},
|
||||||
{"id": 2, "name": "Whiskers", "tag": "cat"}
|
{"id": 2, "name": "Whiskers", "tag": "cat"}
|
||||||
]')!
|
]')!
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
parameters: [
|
parameters: [
|
||||||
openrpc.ContentDescriptor{
|
openrpc.ContentDescriptor{
|
||||||
name: 'limit'
|
name: 'limit'
|
||||||
summary: 'Maximum number of pets to return'
|
summary: 'Maximum number of pets to return'
|
||||||
description: 'Maximum number of pets to return'
|
description: 'Maximum number of pets to return'
|
||||||
required: false
|
required: false
|
||||||
schema: jsonschema.SchemaRef(jsonschema.Schema{
|
schema: jsonschema.SchemaRef(jsonschema.Schema{
|
||||||
...jsonschema.schema_u32,
|
...jsonschema.schema_u32
|
||||||
example: 10
|
example: 10
|
||||||
})
|
})
|
||||||
}
|
},
|
||||||
]
|
]
|
||||||
result: openrpc.ContentDescriptor{
|
result: openrpc.ContentDescriptor{
|
||||||
name: 'pets'
|
name: 'pets'
|
||||||
description: 'A paged array of pets'
|
description: 'A paged array of pets'
|
||||||
schema: jsonschema.SchemaRef(jsonschema.Schema{
|
schema: jsonschema.SchemaRef(jsonschema.Schema{
|
||||||
typ: 'array'
|
typ: 'array'
|
||||||
items: jsonschema.Items(jsonschema.SchemaRef(jsonschema.Schema{
|
items: jsonschema.Items(jsonschema.SchemaRef(jsonschema.Schema{
|
||||||
id: 'pet'
|
id: 'pet'
|
||||||
title: 'Pet'
|
title: 'Pet'
|
||||||
typ: 'object'
|
typ: 'object'
|
||||||
properties: {
|
properties: {
|
||||||
'id': jsonschema.SchemaRef(jsonschema.Reference{
|
'id': jsonschema.SchemaRef(jsonschema.Reference{
|
||||||
ref: '#/components/schemas/PetId'
|
ref: '#/components/schemas/PetId'
|
||||||
}),
|
})
|
||||||
'name': jsonschema.SchemaRef(jsonschema.Schema{
|
'name': jsonschema.SchemaRef(jsonschema.Schema{
|
||||||
typ: 'string'
|
typ: 'string'
|
||||||
}),
|
})
|
||||||
'tag': jsonschema.SchemaRef(jsonschema.Schema{
|
'tag': jsonschema.SchemaRef(jsonschema.Schema{
|
||||||
typ: 'string'
|
typ: 'string'
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
required: ['id', 'name']
|
required: [
|
||||||
}))
|
'id',
|
||||||
})
|
'name',
|
||||||
}
|
]
|
||||||
errors: [
|
}))
|
||||||
openrpc.ErrorSpec{
|
})
|
||||||
code: 400
|
}
|
||||||
message: 'Invalid request'
|
errors: [
|
||||||
}
|
openrpc.ErrorSpec{
|
||||||
]
|
code: 400
|
||||||
},
|
message: 'Invalid request'
|
||||||
specification.ActorMethod{
|
},
|
||||||
name: 'newPet'
|
]
|
||||||
summary: 'Create a new pet'
|
},
|
||||||
parameters: [
|
specification.ActorMethod{
|
||||||
openrpc.ContentDescriptor{
|
name: 'newPet'
|
||||||
name: 'result'
|
summary: 'Create a new pet'
|
||||||
description: 'The response of the operation.'
|
parameters: [
|
||||||
required: true
|
openrpc.ContentDescriptor{
|
||||||
schema: jsonschema.SchemaRef(jsonschema.Schema{
|
name: 'result'
|
||||||
id: 'pet'
|
description: 'The response of the operation.'
|
||||||
title: 'Pet'
|
required: true
|
||||||
typ: 'object'
|
schema: jsonschema.SchemaRef(jsonschema.Schema{
|
||||||
properties: {
|
id: 'pet'
|
||||||
'id': jsonschema.SchemaRef(jsonschema.Reference{
|
title: 'Pet'
|
||||||
ref: '#/components/schemas/PetId'
|
typ: 'object'
|
||||||
}),
|
properties: {
|
||||||
'name': jsonschema.SchemaRef(jsonschema.Schema{
|
'id': jsonschema.SchemaRef(jsonschema.Reference{
|
||||||
typ: 'string'
|
ref: '#/components/schemas/PetId'
|
||||||
}),
|
})
|
||||||
'tag': jsonschema.SchemaRef(jsonschema.Schema{
|
'name': jsonschema.SchemaRef(jsonschema.Schema{
|
||||||
typ: 'string'
|
typ: 'string'
|
||||||
})
|
})
|
||||||
}
|
'tag': jsonschema.SchemaRef(jsonschema.Schema{
|
||||||
required: ['id', 'name']
|
typ: 'string'
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
]
|
required: ['id', 'name']
|
||||||
example: openrpc.ExamplePairing{
|
})
|
||||||
result: openrpc.ExampleRef(openrpc.Example{
|
},
|
||||||
name: 'Example response'
|
]
|
||||||
value: '[]'
|
example: openrpc.ExamplePairing{
|
||||||
})
|
result: openrpc.ExampleRef(openrpc.Example{
|
||||||
}
|
name: 'Example response'
|
||||||
result: openrpc.ContentDescriptor{
|
value: '[]'
|
||||||
name: 'petId'
|
})
|
||||||
summary: 'ID of the created pet'
|
}
|
||||||
description: 'ID of the created pet'
|
result: openrpc.ContentDescriptor{
|
||||||
required: true
|
name: 'petId'
|
||||||
schema: jsonschema.SchemaRef(jsonschema.Schema{
|
summary: 'ID of the created pet'
|
||||||
...jsonschema.schema_u32,
|
description: 'ID of the created pet'
|
||||||
example: 1
|
required: true
|
||||||
})
|
schema: jsonschema.SchemaRef(jsonschema.Schema{
|
||||||
}
|
...jsonschema.schema_u32
|
||||||
errors: [
|
example: 1
|
||||||
openrpc.ErrorSpec{
|
})
|
||||||
code: 400
|
}
|
||||||
message: 'Invalid input'
|
errors: [
|
||||||
}
|
openrpc.ErrorSpec{
|
||||||
]
|
code: 400
|
||||||
},
|
message: 'Invalid input'
|
||||||
specification.ActorMethod{
|
},
|
||||||
name: 'getPet'
|
]
|
||||||
summary: 'Get a pet by ID'
|
},
|
||||||
example: openrpc.ExamplePairing{
|
specification.ActorMethod{
|
||||||
params: [
|
name: 'getPet'
|
||||||
openrpc.ExampleRef(openrpc.Example{
|
summary: 'Get a pet by ID'
|
||||||
name: 'Example petId'
|
example: openrpc.ExamplePairing{
|
||||||
description: 'Example ID of the pet to retrieve'
|
params: [
|
||||||
value: 1
|
openrpc.ExampleRef(openrpc.Example{
|
||||||
})
|
name: 'Example petId'
|
||||||
]
|
description: 'Example ID of the pet to retrieve'
|
||||||
result: openrpc.ExampleRef(openrpc.Example{
|
value: 1
|
||||||
name: 'Example response'
|
}),
|
||||||
value: json.raw_decode('{"id": 1, "name": "Fluffy", "tag": "dog"}')!
|
]
|
||||||
})
|
result: openrpc.ExampleRef(openrpc.Example{
|
||||||
}
|
name: 'Example response'
|
||||||
parameters: [
|
value: json.raw_decode('{"id": 1, "name": "Fluffy", "tag": "dog"}')!
|
||||||
openrpc.ContentDescriptor{
|
})
|
||||||
name: 'petId'
|
}
|
||||||
summary: 'ID of the pet to retrieve'
|
parameters: [
|
||||||
description: 'ID of the pet to retrieve'
|
openrpc.ContentDescriptor{
|
||||||
required: true
|
name: 'petId'
|
||||||
schema: jsonschema.SchemaRef(jsonschema.Schema{
|
summary: 'ID of the pet to retrieve'
|
||||||
...jsonschema.schema_u32,
|
description: 'ID of the pet to retrieve'
|
||||||
format:'uint32'
|
required: true
|
||||||
example: 1
|
schema: jsonschema.SchemaRef(jsonschema.Schema{
|
||||||
})
|
...jsonschema.schema_u32
|
||||||
}
|
format: 'uint32'
|
||||||
]
|
example: 1
|
||||||
result: openrpc.ContentDescriptor{
|
})
|
||||||
name: 'result'
|
},
|
||||||
description: 'The response of the operation.'
|
]
|
||||||
required: true
|
result: openrpc.ContentDescriptor{
|
||||||
schema: jsonschema.SchemaRef(jsonschema.Reference{
|
name: 'result'
|
||||||
ref: '#/components/schemas/Pet'
|
description: 'The response of the operation.'
|
||||||
})
|
required: true
|
||||||
}
|
schema: jsonschema.SchemaRef(jsonschema.Reference{
|
||||||
errors: [
|
ref: '#/components/schemas/Pet'
|
||||||
openrpc.ErrorSpec{
|
})
|
||||||
code: 404
|
}
|
||||||
message: 'Pet not found'
|
errors: [
|
||||||
}
|
openrpc.ErrorSpec{
|
||||||
]
|
code: 404
|
||||||
},
|
message: 'Pet not found'
|
||||||
specification.ActorMethod{
|
},
|
||||||
name: 'deletePet'
|
]
|
||||||
summary: 'Delete a pet by ID'
|
},
|
||||||
example: openrpc.ExamplePairing{
|
specification.ActorMethod{
|
||||||
params: [
|
name: 'deletePet'
|
||||||
openrpc.ExampleRef(openrpc.Example{
|
summary: 'Delete a pet by ID'
|
||||||
name: 'Example petId'
|
example: openrpc.ExamplePairing{
|
||||||
description: 'Example ID of the pet to delete'
|
params: [
|
||||||
value: 1
|
openrpc.ExampleRef(openrpc.Example{
|
||||||
})
|
name: 'Example petId'
|
||||||
]
|
description: 'Example ID of the pet to delete'
|
||||||
}
|
value: 1
|
||||||
parameters: [
|
}),
|
||||||
openrpc.ContentDescriptor{
|
]
|
||||||
name: 'petId'
|
}
|
||||||
summary: 'ID of the pet to delete'
|
parameters: [
|
||||||
description: 'ID of the pet to delete'
|
openrpc.ContentDescriptor{
|
||||||
required: true
|
name: 'petId'
|
||||||
schema: jsonschema.SchemaRef(jsonschema.Schema{
|
summary: 'ID of the pet to delete'
|
||||||
...jsonschema.schema_u32,
|
description: 'ID of the pet to delete'
|
||||||
example: 1
|
required: true
|
||||||
})
|
schema: jsonschema.SchemaRef(jsonschema.Schema{
|
||||||
}
|
...jsonschema.schema_u32
|
||||||
]
|
example: 1
|
||||||
result: openrpc.ContentDescriptor{
|
})
|
||||||
name: 'result'
|
},
|
||||||
description: 'The response of the operation.'
|
]
|
||||||
required: true
|
result: openrpc.ContentDescriptor{
|
||||||
}
|
name: 'result'
|
||||||
errors: [
|
description: 'The response of the operation.'
|
||||||
openrpc.ErrorSpec{
|
required: true
|
||||||
code: 404
|
}
|
||||||
message: 'Pet not found'
|
errors: [
|
||||||
}
|
openrpc.ErrorSpec{
|
||||||
]
|
code: 404
|
||||||
}
|
message: 'Pet not found'
|
||||||
]
|
},
|
||||||
objects: [
|
]
|
||||||
specification.BaseObject{
|
},
|
||||||
schema: jsonschema.Schema{
|
]
|
||||||
title: 'Pet'
|
objects: [
|
||||||
typ: 'object'
|
specification.BaseObject{
|
||||||
properties: {
|
schema: jsonschema.Schema{
|
||||||
'id': jsonschema.schema_u32,
|
title: 'Pet'
|
||||||
'name': jsonschema.SchemaRef(jsonschema.Schema{
|
typ: 'object'
|
||||||
typ: 'string'
|
properties: {
|
||||||
}),
|
'id': jsonschema.schema_u32
|
||||||
'tag': jsonschema.SchemaRef(jsonschema.Schema{
|
'name': jsonschema.SchemaRef(jsonschema.Schema{
|
||||||
typ: 'string'
|
typ: 'string'
|
||||||
})
|
})
|
||||||
}
|
'tag': jsonschema.SchemaRef(jsonschema.Schema{
|
||||||
required: ['id', 'name']
|
typ: 'string'
|
||||||
}
|
})
|
||||||
}
|
}
|
||||||
]
|
required: ['id', 'name']
|
||||||
|
}
|
||||||
|
},
|
||||||
|
]
|
||||||
}
|
}
|
||||||
|
|
||||||
const destination = '${os.dir(@FILE)}/testdata'
|
const destination = '${os.dir(@FILE)}/testdata'
|
||||||
|
|
||||||
fn test_generate_plain_actor_module() {
|
fn test_generate_plain_actor_module() {
|
||||||
// plain actor module without interfaces
|
// plain actor module without interfaces
|
||||||
actor_module := generate_actor_module(actor_spec)!
|
actor_module := generate_actor_module(actor_spec)!
|
||||||
actor_module.write(destination,
|
actor_module.write(destination,
|
||||||
format: true
|
format: true
|
||||||
overwrite: true
|
overwrite: true
|
||||||
test: true
|
test: true
|
||||||
)!
|
)!
|
||||||
}
|
}
|
||||||
|
|
||||||
fn test_generate_actor_module_with_openrpc_interface() {
|
fn test_generate_actor_module_with_openrpc_interface() {
|
||||||
// plain actor module without interfaces
|
// plain actor module without interfaces
|
||||||
actor_module := generate_actor_module(actor_spec, interfaces: [.openrpc])!
|
actor_module := generate_actor_module(actor_spec, interfaces: [.openrpc])!
|
||||||
actor_module.write(destination,
|
actor_module.write(destination,
|
||||||
format: true
|
format: true
|
||||||
overwrite: true
|
overwrite: true
|
||||||
test: true
|
test: true
|
||||||
)!
|
)!
|
||||||
}
|
}
|
||||||
|
|
||||||
fn test_generate_actor_module_with_openapi_interface() {
|
fn test_generate_actor_module_with_openapi_interface() {
|
||||||
// plain actor module without interfaces
|
// plain actor module without interfaces
|
||||||
actor_module := generate_actor_module(actor_spec,
|
actor_module := generate_actor_module(actor_spec,
|
||||||
interfaces: [.openapi]
|
interfaces: [.openapi]
|
||||||
)!
|
)!
|
||||||
actor_module.write(destination,
|
actor_module.write(destination,
|
||||||
format: true
|
format: true
|
||||||
overwrite: true
|
overwrite: true
|
||||||
test: true
|
test: true
|
||||||
)!
|
)!
|
||||||
}
|
}
|
||||||
|
|
||||||
fn test_generate_actor_module_with_all_interfaces() {
|
fn test_generate_actor_module_with_all_interfaces() {
|
||||||
// plain actor module without interfaces
|
// plain actor module without interfaces
|
||||||
actor_module := generate_actor_module(actor_spec,
|
actor_module := generate_actor_module(actor_spec,
|
||||||
interfaces: [.openapi, .openrpc, .http]
|
interfaces: [.openapi, .openrpc, .http]
|
||||||
)!
|
)!
|
||||||
actor_module.write(destination,
|
actor_module.write(destination,
|
||||||
format: true
|
format: true
|
||||||
overwrite: true
|
overwrite: true
|
||||||
test: true
|
test: true
|
||||||
)!
|
)!
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,10 +1,10 @@
|
|||||||
module generator
|
module generator
|
||||||
|
|
||||||
import freeflowuniverse.herolib.core.code { Param, Folder, IFile, VFile, CodeItem, File, Function, Import, Module, Struct, CustomCode, Result }
|
import freeflowuniverse.herolib.core.code { CodeItem, CustomCode, Function, Import, Param, Result, VFile }
|
||||||
import freeflowuniverse.herolib.core.texttools
|
import freeflowuniverse.herolib.core.texttools
|
||||||
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 }
|
||||||
|
|
||||||
pub fn generate_client_file(spec ActorSpecification) !VFile {
|
pub fn generate_client_file(spec ActorSpecification) !VFile {
|
||||||
actor_name_snake := texttools.snake_case(spec.name)
|
actor_name_snake := texttools.snake_case(spec.name)
|
||||||
@@ -12,7 +12,7 @@ pub fn generate_client_file(spec ActorSpecification) !VFile {
|
|||||||
|
|
||||||
mut items := []CodeItem{}
|
mut items := []CodeItem{}
|
||||||
|
|
||||||
items << CustomCode {'
|
items << CustomCode{'
|
||||||
pub struct Client {
|
pub struct Client {
|
||||||
stage.Client
|
stage.Client
|
||||||
}
|
}
|
||||||
@@ -27,7 +27,7 @@ pub fn generate_client_file(spec ActorSpecification) !VFile {
|
|||||||
items << generate_client_method(method)!
|
items << generate_client_method(method)!
|
||||||
}
|
}
|
||||||
|
|
||||||
return VFile {
|
return VFile{
|
||||||
imports: [
|
imports: [
|
||||||
Import{
|
Import{
|
||||||
mod: 'freeflowuniverse.herolib.baobab.stage'
|
mod: 'freeflowuniverse.herolib.baobab.stage'
|
||||||
@@ -36,12 +36,12 @@ pub fn generate_client_file(spec ActorSpecification) !VFile {
|
|||||||
mod: 'freeflowuniverse.herolib.core.redisclient'
|
mod: 'freeflowuniverse.herolib.core.redisclient'
|
||||||
},
|
},
|
||||||
Import{
|
Import{
|
||||||
mod: 'x.json2 as json'
|
mod: 'x.json2 as json'
|
||||||
types: ['Any']
|
types: ['Any']
|
||||||
}
|
},
|
||||||
]
|
]
|
||||||
name: 'client_actor'
|
name: 'client_actor'
|
||||||
items: items
|
items: items
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -51,24 +51,24 @@ pub fn generate_example_client_file(spec ActorSpecification) !VFile {
|
|||||||
|
|
||||||
mut items := []CodeItem{}
|
mut items := []CodeItem{}
|
||||||
|
|
||||||
items << CustomCode {'
|
items << CustomCode{"
|
||||||
pub struct Client {
|
pub struct Client {
|
||||||
stage.Client
|
stage.Client
|
||||||
}
|
}
|
||||||
|
|
||||||
fn new_client() !Client {
|
fn new_client() !Client {
|
||||||
mut redis := redisclient.new(\'localhost:6379\')!
|
mut redis := redisclient.new('localhost:6379')!
|
||||||
mut rpc_q := redis.rpc_get(\'actor_example_\${name}\')
|
mut rpc_q := redis.rpc_get('actor_example_\${name}')
|
||||||
return Client{
|
return Client{
|
||||||
rpc: rpc_q
|
rpc: rpc_q
|
||||||
}
|
}
|
||||||
}'}
|
}"}
|
||||||
|
|
||||||
for method in spec.methods {
|
for method in spec.methods {
|
||||||
items << generate_client_method(method)!
|
items << generate_client_method(method)!
|
||||||
}
|
}
|
||||||
|
|
||||||
return VFile {
|
return VFile{
|
||||||
imports: [
|
imports: [
|
||||||
Import{
|
Import{
|
||||||
mod: 'freeflowuniverse.herolib.baobab.stage'
|
mod: 'freeflowuniverse.herolib.baobab.stage'
|
||||||
@@ -77,23 +77,23 @@ pub fn generate_example_client_file(spec ActorSpecification) !VFile {
|
|||||||
mod: 'freeflowuniverse.herolib.core.redisclient'
|
mod: 'freeflowuniverse.herolib.core.redisclient'
|
||||||
},
|
},
|
||||||
Import{
|
Import{
|
||||||
mod: 'x.json2 as json'
|
mod: 'x.json2 as json'
|
||||||
types: ['Any']
|
types: ['Any']
|
||||||
}
|
},
|
||||||
]
|
]
|
||||||
name: 'client'
|
name: 'client'
|
||||||
items: items
|
items: items
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
pub fn generate_client_method(method ActorMethod) !Function {
|
pub fn generate_client_method(method ActorMethod) !Function {
|
||||||
name_fixed := texttools.snake_case(method.name)
|
name_fixed := texttools.snake_case(method.name)
|
||||||
|
|
||||||
call_params := if method.parameters.len > 0 {
|
call_params := if method.parameters.len > 0 {
|
||||||
method.parameters.map(texttools.snake_case(it.name)).map('Any(${it}.str())').join(', ')
|
method.parameters.map(texttools.snake_case(it.name)).map('Any(${it}.str())').join(', ')
|
||||||
} else {''}
|
} else {
|
||||||
|
''
|
||||||
|
}
|
||||||
|
|
||||||
params_stmt := if method.parameters.len == 0 {
|
params_stmt := if method.parameters.len == 0 {
|
||||||
''
|
''
|
||||||
@@ -118,16 +118,19 @@ pub fn generate_client_method(method ActorMethod) !Function {
|
|||||||
result_stmt := if result_type == '' {
|
result_stmt := if result_type == '' {
|
||||||
''
|
''
|
||||||
} else {
|
} else {
|
||||||
"return json.decode[${result_type}](action.result)!"
|
'return json.decode[${result_type}](action.result)!'
|
||||||
}
|
}
|
||||||
result_param := content_descriptor_to_parameter(method.result)!
|
result_param := content_descriptor_to_parameter(method.result)!
|
||||||
return Function {
|
return Function{
|
||||||
receiver: code.new_param(v: 'mut client Client')!
|
receiver: code.new_param(v: 'mut client Client')!
|
||||||
result: Param{...result_param, typ: Result{result_param.typ}}
|
result: Param{
|
||||||
name: name_fixed
|
...result_param
|
||||||
body: '${params_stmt}\n${client_call_stmt}\n${result_stmt}'
|
typ: Result{result_param.typ}
|
||||||
summary: method.summary
|
}
|
||||||
|
name: name_fixed
|
||||||
|
body: '${params_stmt}\n${client_call_stmt}\n${result_stmt}'
|
||||||
|
summary: method.summary
|
||||||
description: method.description
|
description: method.description
|
||||||
params: method.parameters.map(content_descriptor_to_parameter(it)!)
|
params: method.parameters.map(content_descriptor_to_parameter(it)!)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1,8 +1,8 @@
|
|||||||
module generator
|
module generator
|
||||||
|
|
||||||
import freeflowuniverse.herolib.core.code { Folder, IFile, VFile, CodeItem, File, Function, Import, Module, Struct, CustomCode }
|
import freeflowuniverse.herolib.core.code { CodeItem, CustomCode, Import, VFile }
|
||||||
import freeflowuniverse.herolib.core.texttools
|
import freeflowuniverse.herolib.core.texttools
|
||||||
import freeflowuniverse.herolib.baobab.specification {ActorMethod, ActorSpecification}
|
import freeflowuniverse.herolib.baobab.specification { ActorMethod, ActorSpecification }
|
||||||
|
|
||||||
pub fn generate_command_file(spec ActorSpecification) !VFile {
|
pub fn generate_command_file(spec ActorSpecification) !VFile {
|
||||||
mut items := []CodeItem{}
|
mut items := []CodeItem{}
|
||||||
|
|||||||
@@ -1,17 +1,17 @@
|
|||||||
module generator
|
module generator
|
||||||
|
|
||||||
import freeflowuniverse.herolib.baobab.specification {ActorInterface}
|
import freeflowuniverse.herolib.baobab.specification { ActorInterface }
|
||||||
import freeflowuniverse.herolib.core.code { Folder, IFile, VFile, CodeItem, File, Function, Import, Module, Struct, CustomCode }
|
import freeflowuniverse.herolib.core.code { CustomCode, VFile }
|
||||||
|
|
||||||
fn generate_openrpc_interface_files(interfaces []ActorInterface) (VFile, VFile) {
|
fn generate_openrpc_interface_files(interfaces []ActorInterface) (VFile, VFile) {
|
||||||
http := ActorInterface.http in interfaces
|
http := ActorInterface.http in interfaces
|
||||||
|
|
||||||
iface_file := VFile {
|
iface_file := VFile{
|
||||||
name: 'interface_openrpc'
|
name: 'interface_openrpc'
|
||||||
items: [CustomCode{$tmpl('./templates/interface_openrpc.v.template')}]
|
items: [CustomCode{$tmpl('./templates/interface_openrpc.v.template')}]
|
||||||
}
|
}
|
||||||
iface_test_file := VFile {
|
iface_test_file := VFile{
|
||||||
name: 'interface_openrpc_test'
|
name: 'interface_openrpc_test'
|
||||||
items: [CustomCode{$tmpl('./templates/interface_openrpc_test.v.template')}]
|
items: [CustomCode{$tmpl('./templates/interface_openrpc_test.v.template')}]
|
||||||
}
|
}
|
||||||
return iface_file, iface_test_file
|
return iface_file, iface_test_file
|
||||||
@@ -20,12 +20,12 @@ fn generate_openrpc_interface_files(interfaces []ActorInterface) (VFile, VFile)
|
|||||||
fn generate_openapi_interface_files(interfaces []ActorInterface) (VFile, VFile) {
|
fn generate_openapi_interface_files(interfaces []ActorInterface) (VFile, VFile) {
|
||||||
http := ActorInterface.http in interfaces
|
http := ActorInterface.http in interfaces
|
||||||
dollar := '$'
|
dollar := '$'
|
||||||
iface_file := VFile {
|
iface_file := VFile{
|
||||||
name: 'interface_openapi'
|
name: 'interface_openapi'
|
||||||
items: [CustomCode{$tmpl('./templates/interface_openapi.v.template')}]
|
items: [CustomCode{$tmpl('./templates/interface_openapi.v.template')}]
|
||||||
}
|
}
|
||||||
iface_test_file := VFile {
|
iface_test_file := VFile{
|
||||||
name: 'interface_openapi_test'
|
name: 'interface_openapi_test'
|
||||||
items: [CustomCode{$tmpl('./templates/interface_openapi_test.v.template')}]
|
items: [CustomCode{$tmpl('./templates/interface_openapi_test.v.template')}]
|
||||||
}
|
}
|
||||||
return iface_file, iface_test_file
|
return iface_file, iface_test_file
|
||||||
@@ -36,12 +36,12 @@ fn generate_http_interface_files(controllers []ActorInterface) (VFile, VFile) {
|
|||||||
openapi := ActorInterface.openapi in controllers
|
openapi := ActorInterface.openapi in controllers
|
||||||
openrpc := ActorInterface.openrpc in controllers
|
openrpc := ActorInterface.openrpc in controllers
|
||||||
|
|
||||||
iface_file := VFile {
|
iface_file := VFile{
|
||||||
name: 'interface_http'
|
name: 'interface_http'
|
||||||
items: [CustomCode{$tmpl('./templates/interface_http.v.template')}]
|
items: [CustomCode{$tmpl('./templates/interface_http.v.template')}]
|
||||||
}
|
}
|
||||||
iface_test_file := VFile {
|
iface_test_file := VFile{
|
||||||
name: 'interface_http_test'
|
name: 'interface_http_test'
|
||||||
items: [CustomCode{$tmpl('./templates/interface_http_test.v.template')}]
|
items: [CustomCode{$tmpl('./templates/interface_http_test.v.template')}]
|
||||||
}
|
}
|
||||||
return iface_file, iface_test_file
|
return iface_file, iface_test_file
|
||||||
|
|||||||
@@ -1,12 +1,12 @@
|
|||||||
module generator
|
module generator
|
||||||
|
|
||||||
import freeflowuniverse.herolib.core.code { Array, Folder, IFile, VFile, CodeItem, File, Function, Param, Import, Module, Struct, CustomCode, Result }
|
import freeflowuniverse.herolib.core.code { Array, CodeItem, Function, Import, Param, Result, Struct, VFile }
|
||||||
import freeflowuniverse.herolib.core.texttools
|
import freeflowuniverse.herolib.core.texttools
|
||||||
import freeflowuniverse.herolib.schemas.openrpc {ContentDescriptor}
|
import freeflowuniverse.herolib.schemas.openrpc
|
||||||
import freeflowuniverse.herolib.schemas.openrpc.codegen {content_descriptor_to_parameter, content_descriptor_to_struct}
|
import freeflowuniverse.herolib.schemas.openrpc.codegen { content_descriptor_to_parameter, content_descriptor_to_struct }
|
||||||
import freeflowuniverse.herolib.schemas.jsonschema {Schema}
|
import freeflowuniverse.herolib.schemas.jsonschema { Schema }
|
||||||
import freeflowuniverse.herolib.schemas.jsonschema.codegen as jsonschema_codegen {schema_to_struct}
|
import freeflowuniverse.herolib.schemas.jsonschema.codegen as jsonschema_codegen
|
||||||
import freeflowuniverse.herolib.baobab.specification {ActorMethod, ActorSpecification}
|
import freeflowuniverse.herolib.baobab.specification { ActorMethod, ActorSpecification }
|
||||||
|
|
||||||
const crud_prefixes = ['new', 'get', 'set', 'delete', 'list']
|
const crud_prefixes = ['new', 'get', 'set', 'delete', 'list']
|
||||||
|
|
||||||
@@ -15,48 +15,66 @@ pub fn generate_methods_file(spec ActorSpecification) !VFile {
|
|||||||
actor_name_pascal := texttools.pascal_case(spec.name)
|
actor_name_pascal := texttools.pascal_case(spec.name)
|
||||||
|
|
||||||
receiver := generate_methods_receiver(spec.name)
|
receiver := generate_methods_receiver(spec.name)
|
||||||
receiver_param := Param {
|
receiver_param := Param{
|
||||||
mutable: true
|
mutable: true
|
||||||
name: name_snake[0].ascii_str() // receiver is first letter of domain
|
name: name_snake[0].ascii_str() // receiver is first letter of domain
|
||||||
typ: code.Result{code.Object{receiver.name}}
|
typ: Result{code.Object{receiver.name}}
|
||||||
}
|
}
|
||||||
|
|
||||||
mut items := [CodeItem(receiver), CodeItem(generate_core_factory(receiver_param))]
|
mut items := [CodeItem(receiver), CodeItem(generate_core_factory(receiver_param))]
|
||||||
for method in spec.methods {
|
for method in spec.methods {
|
||||||
items << generate_method_code(receiver_param, ActorMethod{...method, category: spec.method_type(method)})!
|
items << generate_method_code(receiver_param, ActorMethod{
|
||||||
|
...method
|
||||||
|
category: spec.method_type(method)
|
||||||
|
})!
|
||||||
}
|
}
|
||||||
|
|
||||||
return VFile {
|
return VFile{
|
||||||
name: 'methods'
|
name: 'methods'
|
||||||
imports: [Import{mod: 'freeflowuniverse.herolib.baobab.osis', types: ['OSIS']}]
|
imports: [
|
||||||
items: items
|
Import{
|
||||||
|
mod: 'freeflowuniverse.herolib.baobab.osis'
|
||||||
|
types: ['OSIS']
|
||||||
|
},
|
||||||
|
]
|
||||||
|
items: items
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn generate_methods_receiver(name string) code.Struct {
|
fn generate_methods_receiver(name string) Struct {
|
||||||
return code.Struct {
|
return Struct{
|
||||||
is_pub: true
|
is_pub: true
|
||||||
name: '${texttools.pascal_case(name)}'
|
name: '${texttools.pascal_case(name)}'
|
||||||
fields: [code.StructField{is_mut: true, name: 'osis', typ:code.Object{'OSIS'}}]
|
fields: [
|
||||||
|
code.StructField{
|
||||||
|
is_mut: true
|
||||||
|
name: 'osis'
|
||||||
|
typ: code.Object{'OSIS'}
|
||||||
|
},
|
||||||
|
]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn generate_core_factory(receiver code.Param) code.Function {
|
fn generate_core_factory(receiver Param) Function {
|
||||||
return code.Function {
|
return Function{
|
||||||
is_pub: true
|
is_pub: true
|
||||||
name: 'new_${receiver.typ.symbol()}'
|
name: 'new_${receiver.typ.symbol()}'
|
||||||
body: "return ${receiver.typ.symbol().trim_left('!?')}{osis: osis.new()!}"
|
body: 'return ${receiver.typ.symbol().trim_left('!?')}{osis: osis.new()!}'
|
||||||
result: receiver
|
result: receiver
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// returns bodyless method prototype
|
// returns bodyless method prototype
|
||||||
pub fn generate_method_code(receiver code.Param, method ActorMethod) ![]CodeItem {
|
pub fn generate_method_code(receiver Param, method ActorMethod) ![]CodeItem {
|
||||||
result_param := content_descriptor_to_parameter(method.result)!
|
result_param := content_descriptor_to_parameter(method.result)!
|
||||||
|
|
||||||
mut method_code := []CodeItem{}
|
mut method_code := []CodeItem{}
|
||||||
// TODO: document assumption
|
// TODO: document assumption
|
||||||
obj_params := method.parameters.filter(if it.schema is Schema {it.schema.typ == 'object'} else {false}).map(content_descriptor_to_struct(it))
|
obj_params := method.parameters.filter(if it.schema is Schema {
|
||||||
|
it.schema.typ == 'object'
|
||||||
|
} else {
|
||||||
|
false
|
||||||
|
}).map(content_descriptor_to_struct(it))
|
||||||
if obj_param := obj_params[0] {
|
if obj_param := obj_params[0] {
|
||||||
method_code << obj_param
|
method_code << obj_param
|
||||||
}
|
}
|
||||||
@@ -69,7 +87,7 @@ pub fn generate_method_code(receiver code.Param, method ActorMethod) ![]CodeItem
|
|||||||
.base_object_set { base_object_set_body(receiver, method)! }
|
.base_object_set { base_object_set_body(receiver, method)! }
|
||||||
.base_object_delete { base_object_delete_body(receiver, method)! }
|
.base_object_delete { base_object_delete_body(receiver, method)! }
|
||||||
.base_object_list { base_object_list_body(receiver, method)! }
|
.base_object_list { base_object_list_body(receiver, method)! }
|
||||||
else {"panic('implement')"}
|
else { "panic('implement')" }
|
||||||
}
|
}
|
||||||
|
|
||||||
fn_prototype := generate_method_prototype(receiver, method)!
|
fn_prototype := generate_method_prototype(receiver, method)!
|
||||||
@@ -81,15 +99,18 @@ pub fn generate_method_code(receiver code.Param, method ActorMethod) ![]CodeItem
|
|||||||
}
|
}
|
||||||
|
|
||||||
// returns bodyless method prototype
|
// returns bodyless method prototype
|
||||||
pub fn generate_method_prototype(receiver code.Param, method ActorMethod) !Function {
|
pub fn generate_method_prototype(receiver Param, method ActorMethod) !Function {
|
||||||
result_param := content_descriptor_to_parameter(method.result)!
|
result_param := content_descriptor_to_parameter(method.result)!
|
||||||
return Function{
|
return Function{
|
||||||
name: texttools.snake_case(method.name)
|
name: texttools.snake_case(method.name)
|
||||||
receiver: receiver
|
receiver: receiver
|
||||||
result: Param {...result_param, typ: Result{result_param.typ}}
|
result: Param{
|
||||||
summary: method.summary
|
...result_param
|
||||||
|
typ: Result{result_param.typ}
|
||||||
|
}
|
||||||
|
summary: method.summary
|
||||||
description: method.description
|
description: method.description
|
||||||
params: method.parameters.map(content_descriptor_to_parameter(it)!)
|
params: method.parameters.map(content_descriptor_to_parameter(it)!)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,58 +1,67 @@
|
|||||||
module generator
|
module generator
|
||||||
|
|
||||||
import freeflowuniverse.herolib.core.code { Array, Folder, IFile, VFile, CodeItem, File, Function, Param, Import, Module, Struct, CustomCode, Result }
|
import freeflowuniverse.herolib.core.code { CodeItem, Function, Import, Param, Result, Struct, VFile }
|
||||||
import freeflowuniverse.herolib.core.texttools
|
import freeflowuniverse.herolib.core.texttools
|
||||||
import freeflowuniverse.herolib.schemas.openrpc {Example}
|
import freeflowuniverse.herolib.schemas.openrpc { Example }
|
||||||
import freeflowuniverse.herolib.schemas.jsonschema {Schema}
|
import freeflowuniverse.herolib.schemas.jsonschema
|
||||||
import freeflowuniverse.herolib.schemas.jsonschema.codegen as jsonschema_codegen {schema_to_struct}
|
import freeflowuniverse.herolib.schemas.jsonschema.codegen as jsonschema_codegen
|
||||||
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 }
|
||||||
|
|
||||||
pub fn generate_methods_example_file(spec ActorSpecification) !VFile {
|
pub fn generate_methods_example_file(spec ActorSpecification) !VFile {
|
||||||
name_snake := texttools.snake_case(spec.name)
|
name_snake := texttools.snake_case(spec.name)
|
||||||
name_pascal := texttools.pascal_case(spec.name)
|
name_pascal := texttools.pascal_case(spec.name)
|
||||||
|
|
||||||
receiver := generate_example_methods_receiver(spec.name)
|
receiver := generate_example_methods_receiver(spec.name)
|
||||||
receiver_param := Param {
|
receiver_param := Param{
|
||||||
mutable: true
|
mutable: true
|
||||||
name: name_snake[0].ascii_str()
|
name: name_snake[0].ascii_str()
|
||||||
typ: code.Result{code.Object{receiver.name}}
|
typ: Result{code.Object{receiver.name}}
|
||||||
}
|
}
|
||||||
mut items := [CodeItem(receiver), CodeItem(generate_core_example_factory(receiver_param))]
|
mut items := [CodeItem(receiver), CodeItem(generate_core_example_factory(receiver_param))]
|
||||||
for method in spec.methods {
|
for method in spec.methods {
|
||||||
items << generate_method_example_code(receiver_param, ActorMethod{...method, category: spec.method_type(method)})!
|
items << generate_method_example_code(receiver_param, ActorMethod{
|
||||||
|
...method
|
||||||
|
category: spec.method_type(method)
|
||||||
|
})!
|
||||||
}
|
}
|
||||||
|
|
||||||
return VFile {
|
return VFile{
|
||||||
name: 'methods_example'
|
name: 'methods_example'
|
||||||
imports: [
|
imports: [
|
||||||
Import{mod: 'freeflowuniverse.herolib.baobab.osis', types: ['OSIS']},
|
Import{
|
||||||
Import{mod: 'x.json2 as json'}
|
mod: 'freeflowuniverse.herolib.baobab.osis'
|
||||||
|
types: ['OSIS']
|
||||||
|
},
|
||||||
|
Import{
|
||||||
|
mod: 'x.json2 as json'
|
||||||
|
},
|
||||||
]
|
]
|
||||||
items: items
|
items: items
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn generate_core_example_factory(receiver code.Param) code.Function {
|
fn generate_core_example_factory(receiver Param) Function {
|
||||||
return code.Function {
|
return Function{
|
||||||
is_pub: true
|
is_pub: true
|
||||||
name: 'new_${texttools.snake_case(receiver.typ.symbol())}'
|
name: 'new_${texttools.snake_case(receiver.typ.symbol())}'
|
||||||
body: "return ${receiver.typ.symbol().trim_left('!?')}{OSIS: osis.new()!}"
|
body: 'return ${receiver.typ.symbol().trim_left('!?')}{OSIS: osis.new()!}'
|
||||||
result: receiver
|
result: receiver
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn generate_example_methods_receiver(name string) code.Struct {
|
fn generate_example_methods_receiver(name string) Struct {
|
||||||
return code.Struct {
|
return Struct{
|
||||||
is_pub: true
|
is_pub: true
|
||||||
name: '${texttools.pascal_case(name)}Example'
|
name: '${texttools.pascal_case(name)}Example'
|
||||||
embeds: [code.Struct{name:'OSIS'}]
|
embeds: [Struct{
|
||||||
|
name: 'OSIS'
|
||||||
|
}]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
// returns bodyless method prototype
|
// returns bodyless method prototype
|
||||||
pub fn generate_method_example_code(receiver code.Param, method ActorMethod) ![]CodeItem {
|
pub fn generate_method_example_code(receiver Param, method ActorMethod) ![]CodeItem {
|
||||||
result_param := content_descriptor_to_parameter(method.result)!
|
result_param := content_descriptor_to_parameter(method.result)!
|
||||||
|
|
||||||
mut method_code := []CodeItem{}
|
mut method_code := []CodeItem{}
|
||||||
@@ -63,16 +72,17 @@ pub fn generate_method_example_code(receiver code.Param, method ActorMethod) ![]
|
|||||||
// }
|
// }
|
||||||
|
|
||||||
// check if method is a Base Object CRUD Method and
|
// check if method is a Base Object CRUD Method and
|
||||||
// if so generate the method's body
|
// if so generate the method's body
|
||||||
body := if !method_is_void(method)! {
|
body := if !method_is_void(method)! {
|
||||||
if method.example.result is Example {
|
if method.example.result is Example {
|
||||||
"json_str := '${method.example.result.value}'
|
"json_str := '${method.example.result.value}'
|
||||||
return ${generate_decode_stmt('json_str', method.result)!}"
|
return ${generate_decode_stmt('json_str',
|
||||||
|
method.result)!}"
|
||||||
} else {
|
} else {
|
||||||
"return ${result_param.typ.empty_value()}"
|
'return ${result_param.typ.empty_value()}'
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
""
|
''
|
||||||
}
|
}
|
||||||
|
|
||||||
fn_prototype := generate_method_prototype(receiver, method)!
|
fn_prototype := generate_method_prototype(receiver, method)!
|
||||||
|
|||||||
@@ -1,15 +1,20 @@
|
|||||||
module generator
|
module generator
|
||||||
|
|
||||||
import freeflowuniverse.herolib.core.code { Array, Folder, IFile, VFile, CodeItem, File, Function, Param, Import, Module, Struct, CustomCode, Result }
|
import freeflowuniverse.herolib.core.code { CodeItem, Import, Param, VFile }
|
||||||
import freeflowuniverse.herolib.core.texttools
|
import freeflowuniverse.herolib.core.texttools
|
||||||
import freeflowuniverse.herolib.schemas.openrpc.codegen {content_descriptor_to_parameter}
|
import freeflowuniverse.herolib.schemas.openrpc.codegen
|
||||||
import freeflowuniverse.herolib.baobab.specification {ActorMethod, ActorSpecification}
|
import freeflowuniverse.herolib.baobab.specification { ActorSpecification }
|
||||||
|
|
||||||
pub fn generate_methods_interface_file(spec ActorSpecification) !VFile {
|
pub fn generate_methods_interface_file(spec ActorSpecification) !VFile {
|
||||||
return VFile {
|
return VFile{
|
||||||
name: 'methods_interface'
|
name: 'methods_interface'
|
||||||
imports: [Import{mod: 'freeflowuniverse.herolib.baobab.osis', types: ['OSIS']}]
|
imports: [
|
||||||
items: [code.CodeItem(generate_methods_interface_declaration(spec)!)]
|
Import{
|
||||||
|
mod: 'freeflowuniverse.herolib.baobab.osis'
|
||||||
|
types: ['OSIS']
|
||||||
|
},
|
||||||
|
]
|
||||||
|
items: [CodeItem(generate_methods_interface_declaration(spec)!)]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -18,14 +23,14 @@ pub fn generate_methods_interface_declaration(spec ActorSpecification) !code.Int
|
|||||||
name_snake := texttools.snake_case(spec.name)
|
name_snake := texttools.snake_case(spec.name)
|
||||||
name_pascal := texttools.pascal_case(spec.name)
|
name_pascal := texttools.pascal_case(spec.name)
|
||||||
receiver := generate_methods_receiver(spec.name)
|
receiver := generate_methods_receiver(spec.name)
|
||||||
receiver_param := Param {
|
receiver_param := Param{
|
||||||
mutable: true
|
mutable: true
|
||||||
name: name_snake[0].ascii_str()
|
name: name_snake[0].ascii_str()
|
||||||
typ: code.Object{receiver.name}
|
typ: code.Object{receiver.name}
|
||||||
}
|
}
|
||||||
return code.Interface {
|
return code.Interface{
|
||||||
is_pub: true
|
is_pub: true
|
||||||
name: 'I${name_pascal}'
|
name: 'I${name_pascal}'
|
||||||
methods: spec.methods.map(generate_method_prototype(receiver_param, it)!)
|
methods: spec.methods.map(generate_method_prototype(receiver_param, it)!)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,19 +1,19 @@
|
|||||||
module generator
|
module generator
|
||||||
|
|
||||||
import freeflowuniverse.herolib.core.code { Folder, IFile, VFile, CodeItem, File, Function, Param, Import, Module, Struct, CustomCode }
|
import freeflowuniverse.herolib.core.code { CodeItem, Struct, VFile }
|
||||||
import freeflowuniverse.herolib.core.texttools
|
import freeflowuniverse.herolib.core.texttools
|
||||||
import freeflowuniverse.herolib.schemas.jsonschema.codegen {schema_to_struct}
|
import freeflowuniverse.herolib.schemas.jsonschema.codegen { schema_to_struct }
|
||||||
import freeflowuniverse.herolib.baobab.specification {ActorMethod, ActorSpecification}
|
import freeflowuniverse.herolib.baobab.specification { ActorSpecification }
|
||||||
|
|
||||||
pub fn generate_model_file(spec ActorSpecification) !VFile {
|
pub fn generate_model_file(spec ActorSpecification) !VFile {
|
||||||
actor_name_snake := texttools.snake_case(spec.name)
|
actor_name_snake := texttools.snake_case(spec.name)
|
||||||
actor_name_pascal := texttools.pascal_case(spec.name)
|
actor_name_pascal := texttools.pascal_case(spec.name)
|
||||||
|
|
||||||
return VFile {
|
return VFile{
|
||||||
name: 'model'
|
name: 'model'
|
||||||
items: spec.objects.map(CodeItem(
|
items: spec.objects.map(CodeItem(Struct{
|
||||||
Struct {...schema_to_struct(it.schema)
|
...schema_to_struct(it.schema)
|
||||||
is_pub: true
|
is_pub: true
|
||||||
}))
|
}))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1,24 +1,24 @@
|
|||||||
module generator
|
module generator
|
||||||
|
|
||||||
import json
|
import json
|
||||||
import freeflowuniverse.herolib.core.code { VFile, File, Folder, Function, Module, Struct }
|
import freeflowuniverse.herolib.core.code { File, Folder }
|
||||||
import freeflowuniverse.herolib.schemas.openapi { Components, OpenAPI, Operation }
|
import freeflowuniverse.herolib.schemas.openapi { OpenAPI, Operation }
|
||||||
import freeflowuniverse.herolib.schemas.openapi.codegen
|
import freeflowuniverse.herolib.schemas.openapi.codegen
|
||||||
import freeflowuniverse.herolib.schemas.jsonschema.codegen as jsonschema_codegen {schema_to_type}
|
import freeflowuniverse.herolib.schemas.jsonschema.codegen as jsonschema_codegen { schema_to_type }
|
||||||
import net.http
|
import net.http
|
||||||
|
|
||||||
pub fn generate_openapi_file(specification OpenAPI) !File {
|
pub fn generate_openapi_file(specification OpenAPI) !File {
|
||||||
openapi_json := specification.encode_json()
|
openapi_json := specification.encode_json()
|
||||||
return File{
|
return File{
|
||||||
name: 'openapi'
|
name: 'openapi'
|
||||||
extension: 'json'
|
extension: 'json'
|
||||||
content: openapi_json
|
content: openapi_json
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn generate_openapi_ts_client(specification OpenAPI) !Folder {
|
pub fn generate_openapi_ts_client(specification OpenAPI) !Folder {
|
||||||
return codegen.ts_client_folder(specification,
|
return codegen.ts_client_folder(specification,
|
||||||
body_generator: body_generator
|
body_generator: body_generator
|
||||||
custom_client_code: ' private restClient: HeroRestClient;
|
custom_client_code: ' private restClient: HeroRestClient;
|
||||||
|
|
||||||
constructor(heroKeysClient: any, debug: boolean = true) {
|
constructor(heroKeysClient: any, debug: boolean = true) {
|
||||||
@@ -28,22 +28,29 @@ pub fn generate_openapi_ts_client(specification OpenAPI) !Folder {
|
|||||||
)!
|
)!
|
||||||
}
|
}
|
||||||
|
|
||||||
fn body_generator(op openapi.Operation, path_ string, method http.Method) string {
|
fn body_generator(op Operation, path_ string, method http.Method) string {
|
||||||
path := path_.replace('{','\${')
|
path := path_.replace('{', '\${')
|
||||||
return match method {
|
return match method {
|
||||||
.post {
|
.post {
|
||||||
if schema := op.payload_schema() {
|
if schema := op.payload_schema() {
|
||||||
symbol := schema_to_type(schema).typescript()
|
symbol := schema_to_type(schema).typescript()
|
||||||
"return this.restClient.post<${symbol}>('${path}', data);"
|
"return this.restClient.post<${symbol}>('${path}', data);"
|
||||||
} else {''}
|
} else {
|
||||||
|
''
|
||||||
|
}
|
||||||
}
|
}
|
||||||
.get {
|
.get {
|
||||||
if schema := op.response_schema() {
|
if schema := op.response_schema() {
|
||||||
// if op.params.len
|
// if op.params.len
|
||||||
symbol := schema_to_type(schema).typescript()
|
symbol := schema_to_type(schema).typescript()
|
||||||
"return this.restClient.get<${symbol}>('${path}', data);"
|
"return this.restClient.get<${symbol}>('${path}', data);"
|
||||||
} else {''}
|
} else {
|
||||||
} else {''}
|
''
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
''
|
||||||
|
}
|
||||||
}
|
}
|
||||||
// return if operation_is_base_object_method(op) {
|
// return if operation_is_base_object_method(op) {
|
||||||
// bo_method := operation_to_base_object_method(op)
|
// bo_method := operation_to_base_object_method(op)
|
||||||
@@ -58,7 +65,6 @@ fn body_generator(op openapi.Operation, path_ string, method http.Method) string
|
|||||||
// } else {''}
|
// } else {''}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
// pub fn operation_is_base_object_method(op openapi.Operation, base_objs []string) BaseObjectMethod {
|
// pub fn operation_is_base_object_method(op openapi.Operation, base_objs []string) BaseObjectMethod {
|
||||||
// // name := texttools.pascal_case(op.operation_id)
|
// // name := texttools.pascal_case(op.operation_id)
|
||||||
|
|
||||||
@@ -82,28 +88,24 @@ fn body_generator(op openapi.Operation, path_ string, method http.Method) string
|
|||||||
// }
|
// }
|
||||||
// }
|
// }
|
||||||
|
|
||||||
|
// return if operation_is_base_object_method(op) {
|
||||||
|
// bo_method := operation_to_base_object_method(op)
|
||||||
|
// match bo_method. {
|
||||||
// return if operation_is_base_object_method(op) {
|
// .new { ts_client_new_body(op, path) }
|
||||||
// bo_method := operation_to_base_object_method(op)
|
// .get { ts_client_get_body(op, path) }
|
||||||
// match bo_method. {
|
// .set { ts_client_set_body(op, path) }
|
||||||
// .new { ts_client_new_body(op, path) }
|
// .delete { ts_client_delete_body(op, path) }
|
||||||
// .get { ts_client_get_body(op, path) }
|
// .list { ts_client_list_body(op, path) }
|
||||||
// .set { ts_client_set_body(op, path) }
|
// else {''}
|
||||||
// .delete { ts_client_delete_body(op, path) }
|
// }
|
||||||
// .list { ts_client_list_body(op, path) }
|
// } else {''}
|
||||||
// else {''}
|
|
||||||
// }
|
|
||||||
// } else {''}
|
|
||||||
// }
|
// }
|
||||||
|
|
||||||
|
|
||||||
fn get_endpoint(path string) string {
|
fn get_endpoint(path string) string {
|
||||||
return if path == '' {
|
return if path == '' {
|
||||||
''
|
''
|
||||||
} else {
|
} else {
|
||||||
"/${path.trim('/')}"
|
'/${path.trim('/')}'
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,8 +1,8 @@
|
|||||||
module generator
|
module generator
|
||||||
|
|
||||||
import json
|
import json
|
||||||
import freeflowuniverse.herolib.core.code { VFile, File, Function, Module, Struct }
|
import freeflowuniverse.herolib.core.code { File, Function, Struct, VFile }
|
||||||
import freeflowuniverse.herolib.schemas.openrpc { Components, OpenRPC }
|
import freeflowuniverse.herolib.schemas.openrpc { 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 }
|
||||||
|
|
||||||
pub fn generate_openrpc_file(spec OpenRPC) !File {
|
pub fn generate_openrpc_file(spec OpenRPC) !File {
|
||||||
@@ -19,8 +19,8 @@ pub fn generate_openrpc_client_file(spec OpenRPC) !VFile {
|
|||||||
// objects_map[object.structure.name] = object.structure
|
// objects_map[object.structure.name] = object.structure
|
||||||
// }
|
// }
|
||||||
client_file := generate_client_file(spec, objects_map)!
|
client_file := generate_client_file(spec, objects_map)!
|
||||||
return VFile {
|
return VFile{
|
||||||
...client_file,
|
...client_file
|
||||||
name: 'client_openrpc'
|
name: 'client_openrpc'
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -35,8 +35,8 @@ pub fn generate_openrpc_client_test_file(spec OpenRPC) !VFile {
|
|||||||
// methods_map[method.func.name] = method.func
|
// methods_map[method.func.name] = method.func
|
||||||
// }
|
// }
|
||||||
file := generate_client_test_file(spec, methods_map, objects_map)!
|
file := generate_client_test_file(spec, methods_map, objects_map)!
|
||||||
return VFile {
|
return VFile{
|
||||||
...file,
|
...file
|
||||||
name: 'client_openrpc_test'
|
name: 'client_openrpc_test'
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,13 +1,13 @@
|
|||||||
module generator
|
module generator
|
||||||
|
|
||||||
import freeflowuniverse.herolib.core.code { Folder, File }
|
import freeflowuniverse.herolib.core.code { File, Folder }
|
||||||
import freeflowuniverse.herolib.core.texttools
|
import freeflowuniverse.herolib.core.texttools
|
||||||
|
|
||||||
// generates the folder with runnable scripts of the actor
|
// generates the folder with runnable scripts of the actor
|
||||||
pub fn generate_scripts_folder(name string, example bool) Folder {
|
pub fn generate_scripts_folder(name string, example bool) Folder {
|
||||||
actor_name := '${texttools.snake_case(name)}_actor'
|
actor_name := '${texttools.snake_case(name)}_actor'
|
||||||
return Folder {
|
return Folder{
|
||||||
name: 'scripts'
|
name: 'scripts'
|
||||||
files: [
|
files: [
|
||||||
generate_run_script(actor_name),
|
generate_run_script(actor_name),
|
||||||
generate_docs_script(actor_name),
|
generate_docs_script(actor_name),
|
||||||
@@ -22,54 +22,54 @@ pub fn generate_scripts_folder(name string, example bool) Folder {
|
|||||||
|
|
||||||
// Function to generate a script for running an actor
|
// Function to generate a script for running an actor
|
||||||
fn generate_run_script(actor_name string) File {
|
fn generate_run_script(actor_name string) File {
|
||||||
actor_title := texttools.title_case(actor_name)
|
actor_title := texttools.title_case(actor_name)
|
||||||
dollar := '$'
|
dollar := '$'
|
||||||
return File{
|
return File{
|
||||||
name: 'run'
|
name: 'run'
|
||||||
extension:'sh'
|
extension: 'sh'
|
||||||
content: $tmpl('./templates/run.sh.template')
|
content: $tmpl('./templates/run.sh.template')
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Function to generate a script for running an actor
|
// Function to generate a script for running an actor
|
||||||
fn generate_docs_script(actor_name string) File {
|
fn generate_docs_script(actor_name string) File {
|
||||||
dollar := '$'
|
dollar := '$'
|
||||||
return File{
|
return File{
|
||||||
name: 'docs'
|
name: 'docs'
|
||||||
extension:'vsh'
|
extension: 'vsh'
|
||||||
content: $tmpl('./templates/docs.vsh.template')
|
content: $tmpl('./templates/docs.vsh.template')
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Function to generate a script for running an actor
|
// Function to generate a script for running an actor
|
||||||
fn generate_run_actor_script(name string) File {
|
fn generate_run_actor_script(name string) File {
|
||||||
name_snake := texttools.snake_case(name)
|
name_snake := texttools.snake_case(name)
|
||||||
name_pascal := texttools.pascal_case(name)
|
name_pascal := texttools.pascal_case(name)
|
||||||
return File{
|
return File{
|
||||||
name: 'run_actor'
|
name: 'run_actor'
|
||||||
extension:'vsh'
|
extension: 'vsh'
|
||||||
content: $tmpl('./templates/run_actor.vsh.template')
|
content: $tmpl('./templates/run_actor.vsh.template')
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Function to generate a script for running an example actor
|
// Function to generate a script for running an example actor
|
||||||
fn generate_run_actor_example_script(name string) File {
|
fn generate_run_actor_example_script(name string) File {
|
||||||
name_snake := texttools.snake_case(name)
|
name_snake := texttools.snake_case(name)
|
||||||
name_pascal := texttools.pascal_case(name)
|
name_pascal := texttools.pascal_case(name)
|
||||||
return File{
|
return File{
|
||||||
name: 'run_actor_example'
|
name: 'run_actor_example'
|
||||||
extension:'vsh'
|
extension: 'vsh'
|
||||||
content: $tmpl('./templates/run_actor_example.vsh.template')
|
content: $tmpl('./templates/run_actor_example.vsh.template')
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Function to generate a script for running an HTTP server
|
// Function to generate a script for running an HTTP server
|
||||||
fn generate_run_http_server_script(name string) File {
|
fn generate_run_http_server_script(name string) File {
|
||||||
port := 8080
|
port := 8080
|
||||||
name_snake := texttools.snake_case(name)
|
name_snake := texttools.snake_case(name)
|
||||||
return File{
|
return File{
|
||||||
name: 'run_http_server'
|
name: 'run_http_server'
|
||||||
extension:'vsh'
|
extension: 'vsh'
|
||||||
content: $tmpl('./templates/run_http_server.vsh.template')
|
content: $tmpl('./templates/run_http_server.vsh.template')
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -9,7 +9,7 @@ fn mock_response() ! {
|
|||||||
mut redis := redisclient.new('localhost:6379')!
|
mut redis := redisclient.new('localhost:6379')!
|
||||||
mut rpc_q := redis.rpc_get('actor_pet_store')
|
mut rpc_q := redis.rpc_get('actor_pet_store')
|
||||||
for {
|
for {
|
||||||
rpc_q.process(fn(method string, data string)!string{
|
rpc_q.process(fn (method string, data string) !string {
|
||||||
return json.encode(method)
|
return json.encode(method)
|
||||||
})!
|
})!
|
||||||
time.sleep(time.millisecond * 100) // Prevent CPU spinning
|
time.sleep(time.millisecond * 100) // Prevent CPU spinning
|
||||||
|
|||||||
@@ -17,11 +17,11 @@ import os
|
|||||||
pub fn test_generate_get_method() {
|
pub fn test_generate_get_method() {
|
||||||
generator := ActorGenerator{'test'}
|
generator := ActorGenerator{'test'}
|
||||||
actor_struct := code.Struct{
|
actor_struct := code.Struct{
|
||||||
name: 'TestActor'
|
name: 'TestActor'
|
||||||
fields: [
|
fields: [
|
||||||
code.StructField{
|
code.StructField{
|
||||||
name: 'test_struct_map'
|
name: 'test_struct_map'
|
||||||
typ: code.Type{
|
typ: code.Type{
|
||||||
symbol: 'map[string]&TestStruct'
|
symbol: 'map[string]&TestStruct'
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -3,6 +3,6 @@ module osis
|
|||||||
pub fn new(config OSISConfig) !OSIS {
|
pub fn new(config OSISConfig) !OSIS {
|
||||||
return OSIS{
|
return OSIS{
|
||||||
indexer: new_indexer()!
|
indexer: new_indexer()!
|
||||||
storer: new_storer()!
|
storer: new_storer()!
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -12,7 +12,7 @@ pub struct Indexer {
|
|||||||
@[params]
|
@[params]
|
||||||
pub struct IndexerConfig {
|
pub struct IndexerConfig {
|
||||||
db_path string
|
db_path string
|
||||||
reset bool
|
reset bool
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn new_indexer(config IndexerConfig) !Indexer {
|
pub fn new_indexer(config IndexerConfig) !Indexer {
|
||||||
@@ -81,7 +81,7 @@ fn (mut backend Indexer) create_root_object_table(object RootObject) ! {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// deletes an indexer table belonging to a root object
|
// deletes an indexer table belonging to a root object
|
||||||
fn (mut backend Indexer) delete_table(object RootObject)! {
|
fn (mut backend Indexer) delete_table(object RootObject) ! {
|
||||||
panic('implement')
|
panic('implement')
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -105,7 +105,7 @@ fn get_table[T]() string {
|
|||||||
|
|
||||||
// returns the lists of the indices of a root objects db table, and corresponding values
|
// returns the lists of the indices of a root objects db table, and corresponding values
|
||||||
pub fn get_indices[T](object T) map[string]string {
|
pub fn get_indices[T](object T) map[string]string {
|
||||||
mut indices := map[string]string
|
mut indices := map[string]string{}
|
||||||
$for field in T.fields {
|
$for field in T.fields {
|
||||||
if field.attrs.contains('index') {
|
if field.attrs.contains('index') {
|
||||||
value := object.$(field.name)
|
value := object.$(field.name)
|
||||||
|
|||||||
@@ -3,14 +3,14 @@ module osis
|
|||||||
pub struct OSIS {
|
pub struct OSIS {
|
||||||
pub mut:
|
pub mut:
|
||||||
indexer Indexer // storing indeces
|
indexer Indexer // storing indeces
|
||||||
storer Storer
|
storer Storer
|
||||||
}
|
}
|
||||||
|
|
||||||
@[params]
|
@[params]
|
||||||
pub struct OSISConfig {
|
pub struct OSISConfig {
|
||||||
pub:
|
pub:
|
||||||
directory string
|
directory string
|
||||||
name string
|
name string
|
||||||
secret string
|
secret string
|
||||||
reset bool
|
reset bool
|
||||||
}
|
}
|
||||||
@@ -1,6 +1,6 @@
|
|||||||
module osis
|
module osis
|
||||||
|
|
||||||
import freeflowuniverse.herolib.data.ourdb {OurDB}
|
import freeflowuniverse.herolib.data.ourdb { OurDB }
|
||||||
import os
|
import os
|
||||||
|
|
||||||
pub struct Storer {
|
pub struct Storer {
|
||||||
@@ -9,7 +9,7 @@ pub mut:
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub fn new_storer() !Storer {
|
pub fn new_storer() !Storer {
|
||||||
return Storer {
|
return Storer{
|
||||||
db: ourdb.new()!
|
db: ourdb.new()!
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1,19 +1,19 @@
|
|||||||
module specification
|
module specification
|
||||||
|
|
||||||
import freeflowuniverse.herolib.core.texttools
|
import freeflowuniverse.herolib.core.texttools
|
||||||
import freeflowuniverse.herolib.core.code { Struct, Function }
|
import freeflowuniverse.herolib.core.code { Struct }
|
||||||
import freeflowuniverse.herolib.schemas.jsonschema { Schema, SchemaRef }
|
import freeflowuniverse.herolib.schemas.jsonschema { Schema, SchemaRef }
|
||||||
import freeflowuniverse.herolib.schemas.openapi { Operation, Parameter, OpenAPI, Components, Info, PathItem, ServerSpec, MediaType }
|
import freeflowuniverse.herolib.schemas.openapi { MediaType, OpenAPI, Parameter }
|
||||||
import freeflowuniverse.herolib.schemas.openrpc { ExamplePairing, Example, ExampleRef, ContentDescriptor, ErrorSpec }
|
import freeflowuniverse.herolib.schemas.openrpc { ContentDescriptor, ErrorSpec, Example, ExamplePairing, ExampleRef }
|
||||||
|
|
||||||
// Helper function: Convert OpenAPI parameter to ContentDescriptor
|
// Helper function: Convert OpenAPI parameter to ContentDescriptor
|
||||||
fn openapi_param_to_content_descriptor(param Parameter) ContentDescriptor {
|
fn openapi_param_to_content_descriptor(param Parameter) ContentDescriptor {
|
||||||
return ContentDescriptor{
|
return ContentDescriptor{
|
||||||
name: param.name,
|
name: param.name
|
||||||
summary: param.description,
|
summary: param.description
|
||||||
description: param.description,
|
description: param.description
|
||||||
required: param.required,
|
required: param.required
|
||||||
schema: param.schema
|
schema: param.schema
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -22,9 +22,9 @@ fn openapi_param_to_example(param Parameter) ?Example {
|
|||||||
if param.schema is Schema {
|
if param.schema is Schema {
|
||||||
if param.schema.example.str() != '' {
|
if param.schema.example.str() != '' {
|
||||||
return Example{
|
return Example{
|
||||||
name: 'Example ${param.name}',
|
name: 'Example ${param.name}'
|
||||||
description: 'Example ${param.description}',
|
description: 'Example ${param.description}'
|
||||||
value: param.schema.example
|
value: param.schema.example
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -32,21 +32,25 @@ fn openapi_param_to_example(param Parameter) ?Example {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Helper function: Convert OpenAPI operation to ActorMethod
|
// Helper function: Convert OpenAPI operation to ActorMethod
|
||||||
fn openapi_operation_to_actor_method(info openapi.OperationInfo) ActorMethod {
|
fn openapi_operation_to_actor_method(info OperationInfo) ActorMethod {
|
||||||
mut parameters := []ContentDescriptor{}
|
mut parameters := []ContentDescriptor{}
|
||||||
mut example_parameters:= []Example{}
|
mut example_parameters := []Example{}
|
||||||
|
|
||||||
for param in info.operation.parameters {
|
for param in info.operation.parameters {
|
||||||
parameters << openapi_param_to_content_descriptor(param)
|
parameters << openapi_param_to_content_descriptor(param)
|
||||||
example_parameters << openapi_param_to_example(param) or {
|
example_parameters << openapi_param_to_example(param) or { continue }
|
||||||
continue
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if schema_ := info.operation.payload_schema() {
|
if schema_ := info.operation.payload_schema() {
|
||||||
// TODO: document assumption
|
// TODO: document assumption
|
||||||
schema := Schema{...schema_, title: texttools.pascal_case(info.operation.operation_id)}
|
schema := Schema{
|
||||||
parameters << ContentDescriptor {name: 'data', schema: SchemaRef(schema)}
|
...schema_
|
||||||
|
title: texttools.pascal_case(info.operation.operation_id)
|
||||||
|
}
|
||||||
|
parameters << ContentDescriptor{
|
||||||
|
name: 'data'
|
||||||
|
schema: SchemaRef(schema)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
mut success_responses := map[string]MediaType{}
|
mut success_responses := map[string]MediaType{}
|
||||||
@@ -57,54 +61,60 @@ fn openapi_operation_to_actor_method(info openapi.OperationInfo) ActorMethod {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if success_responses.len > 1 || success_responses.len == 0 {
|
if success_responses.len > 1 || success_responses.len == 0 {
|
||||||
panic('Actor specification must specify one successful response.')
|
panic('Actor specification must specify one successful response.')
|
||||||
}
|
}
|
||||||
response_success := success_responses.values()[0]
|
response_success := success_responses.values()[0]
|
||||||
|
|
||||||
mut result := ContentDescriptor{
|
mut result := ContentDescriptor{
|
||||||
name: "result",
|
name: 'result'
|
||||||
description: "The response of the operation.",
|
description: 'The response of the operation.'
|
||||||
required: true,
|
required: true
|
||||||
schema: response_success.schema
|
schema: response_success.schema
|
||||||
}
|
}
|
||||||
|
|
||||||
example_result := if response_success.example.str() != '' {
|
example_result := if response_success.example.str() != '' {
|
||||||
Example{
|
Example{
|
||||||
name: 'Example response',
|
name: 'Example response'
|
||||||
value: response_success.example
|
value: response_success.example
|
||||||
}
|
}
|
||||||
} else {Example{}}
|
} else {
|
||||||
|
Example{}
|
||||||
|
}
|
||||||
|
|
||||||
pairing := if example_result != Example{} || example_parameters.len > 0 {
|
pairing := if example_result != Example{} || example_parameters.len > 0 {
|
||||||
ExamplePairing{
|
ExamplePairing{
|
||||||
params: example_parameters.map(ExampleRef(it))
|
params: example_parameters.map(ExampleRef(it))
|
||||||
result: ExampleRef(example_result)
|
result: ExampleRef(example_result)
|
||||||
}
|
}
|
||||||
} else {ExamplePairing{}}
|
} else {
|
||||||
|
ExamplePairing{}
|
||||||
|
}
|
||||||
|
|
||||||
mut errors := []ErrorSpec{}
|
mut errors := []ErrorSpec{}
|
||||||
for status, response in info.operation.responses {
|
for status, response in info.operation.responses {
|
||||||
if status.int() >= 400 {
|
if status.int() >= 400 {
|
||||||
error_schema := if response.content.len > 0 {
|
error_schema := if response.content.len > 0 {
|
||||||
response.content.values()[0].schema
|
response.content.values()[0].schema
|
||||||
} else {Schema{}}
|
} else {
|
||||||
|
Schema{}
|
||||||
|
}
|
||||||
errors << ErrorSpec{
|
errors << ErrorSpec{
|
||||||
code: status.int(),
|
code: status.int()
|
||||||
message: response.description,
|
message: response.description
|
||||||
data: error_schema, // Extend if error schema is defined
|
data: error_schema // Extend if error schema is defined
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return ActorMethod{
|
return ActorMethod{
|
||||||
name: info.operation.operation_id,
|
name: info.operation.operation_id
|
||||||
description: info.operation.description,
|
description: info.operation.description
|
||||||
summary: info.operation.summary,
|
summary: info.operation.summary
|
||||||
parameters: parameters,
|
parameters: parameters
|
||||||
example: pairing
|
example: pairing
|
||||||
result: result,
|
result: result
|
||||||
errors: errors,
|
errors: errors
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -112,7 +122,7 @@ fn openapi_operation_to_actor_method(info openapi.OperationInfo) ActorMethod {
|
|||||||
fn openapi_schema_to_struct(name string, schema SchemaRef) Struct {
|
fn openapi_schema_to_struct(name string, schema SchemaRef) Struct {
|
||||||
// Assuming schema properties can be mapped to Struct fields
|
// Assuming schema properties can be mapped to Struct fields
|
||||||
return Struct{
|
return Struct{
|
||||||
name: name,
|
name: name
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -153,16 +163,18 @@ pub fn from_openapi(spec_raw OpenAPI) !ActorSpecification {
|
|||||||
|
|
||||||
// Extract objects from OpenAPI components.schemas
|
// Extract objects from OpenAPI components.schemas
|
||||||
for name, schema in spec.components.schemas {
|
for name, schema in spec.components.schemas {
|
||||||
objects << BaseObject{schema: schema as Schema}
|
objects << BaseObject{
|
||||||
|
schema: schema as Schema
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return ActorSpecification{
|
return ActorSpecification{
|
||||||
openapi: spec_raw
|
openapi: spec_raw
|
||||||
name: spec.info.title,
|
name: spec.info.title
|
||||||
description: spec.info.description,
|
description: spec.info.description
|
||||||
structure: Struct{}, // Assuming no top-level structure for this use case
|
structure: Struct{} // Assuming no top-level structure for this use case
|
||||||
interfaces: [.openapi], // Default to OpenAPI for input
|
interfaces: [.openapi] // Default to OpenAPI for input
|
||||||
methods: spec.get_operations().map(openapi_operation_to_actor_method(it)),
|
methods: spec.get_operations().map(openapi_operation_to_actor_method(it))
|
||||||
objects: objects,
|
objects: objects
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1,52 +1,52 @@
|
|||||||
module specification
|
module specification
|
||||||
|
|
||||||
import x.json2 as json {Any}
|
import x.json2 as json
|
||||||
import freeflowuniverse.herolib.core.code { Struct, Function }
|
import freeflowuniverse.herolib.core.code { Struct }
|
||||||
import freeflowuniverse.herolib.schemas.openrpc { ContentDescriptor, ErrorSpec }
|
import freeflowuniverse.herolib.schemas.openrpc { ContentDescriptor, ErrorSpec }
|
||||||
import freeflowuniverse.herolib.schemas.openapi { OpenAPI, Info, ServerSpec, Components, Operation, PathItem, PathRef }
|
import freeflowuniverse.herolib.schemas.openapi { Components, Info, OpenAPI, Operation, PathItem, ServerSpec }
|
||||||
import freeflowuniverse.herolib.schemas.jsonschema {Schema, Reference, SchemaRef}
|
import freeflowuniverse.herolib.schemas.jsonschema { Reference, Schema, SchemaRef }
|
||||||
|
|
||||||
const openapi_spec = openapi.OpenAPI{
|
const openapi_spec = OpenAPI{
|
||||||
openapi: '3.0.3'
|
openapi: '3.0.3'
|
||||||
info: openapi.Info{
|
info: Info{
|
||||||
title: 'Pet Store API'
|
title: 'Pet Store API'
|
||||||
description: 'A sample API for a pet store'
|
description: 'A sample API for a pet store'
|
||||||
version: '1.0.0'
|
version: '1.0.0'
|
||||||
}
|
}
|
||||||
servers: [
|
servers: [
|
||||||
openapi.ServerSpec{
|
ServerSpec{
|
||||||
url: 'https://api.petstore.example.com/v1'
|
url: 'https://api.petstore.example.com/v1'
|
||||||
description: 'Production server'
|
description: 'Production server'
|
||||||
},
|
},
|
||||||
openapi.ServerSpec{
|
ServerSpec{
|
||||||
url: 'https://staging.petstore.example.com/v1'
|
url: 'https://staging.petstore.example.com/v1'
|
||||||
description: 'Staging server'
|
description: 'Staging server'
|
||||||
}
|
},
|
||||||
]
|
]
|
||||||
paths: {
|
paths: {
|
||||||
'/pets': openapi.PathItem{
|
'/pets': PathItem{
|
||||||
get: openapi.Operation{
|
get: Operation{
|
||||||
summary: 'List all pets'
|
summary: 'List all pets'
|
||||||
operation_id: 'listPets'
|
operation_id: 'listPets'
|
||||||
parameters: [
|
parameters: [
|
||||||
openapi.Parameter{
|
openapi.Parameter{
|
||||||
name: 'limit'
|
name: 'limit'
|
||||||
in_: 'query'
|
in_: 'query'
|
||||||
description: 'Maximum number of pets to return'
|
description: 'Maximum number of pets to return'
|
||||||
required: false
|
required: false
|
||||||
schema: Schema{
|
schema: Schema{
|
||||||
typ: 'integer'
|
typ: 'integer'
|
||||||
format: 'int32'
|
format: 'int32'
|
||||||
example: 10
|
example: 10
|
||||||
}
|
}
|
||||||
}
|
},
|
||||||
]
|
]
|
||||||
responses: {
|
responses: {
|
||||||
'200': openapi.ResponseSpec{
|
'200': openapi.ResponseSpec{
|
||||||
description: 'A paginated list of pets'
|
description: 'A paginated list of pets'
|
||||||
content: {
|
content: {
|
||||||
'application/json': openapi.MediaType{
|
'application/json': openapi.MediaType{
|
||||||
schema: Reference{
|
schema: Reference{
|
||||||
ref: '#/components/schemas/Pets'
|
ref: '#/components/schemas/Pets'
|
||||||
}
|
}
|
||||||
example: json.raw_decode('[
|
example: json.raw_decode('[
|
||||||
@@ -61,26 +61,26 @@ const openapi_spec = openapi.OpenAPI{
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
post: openapi.Operation{
|
post: Operation{
|
||||||
summary: 'Create a new pet'
|
summary: 'Create a new pet'
|
||||||
operation_id: 'createPet'
|
operation_id: 'createPet'
|
||||||
request_body: openapi.RequestBody{
|
request_body: openapi.RequestBody{
|
||||||
required: true
|
required: true
|
||||||
content: {
|
content: {
|
||||||
'application/json': openapi.MediaType{
|
'application/json': openapi.MediaType{
|
||||||
schema: Reference{
|
schema: Reference{
|
||||||
ref: '#/components/schemas/NewPet'
|
ref: '#/components/schemas/NewPet'
|
||||||
}
|
}
|
||||||
example: json.raw_decode('{ "name": "Bella", "tag": "dog" }')!
|
example: json.raw_decode('{ "name": "Bella", "tag": "dog" }')!
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
responses: {
|
responses: {
|
||||||
'201': openapi.ResponseSpec{
|
'201': openapi.ResponseSpec{
|
||||||
description: 'Pet created'
|
description: 'Pet created'
|
||||||
content: {
|
content: {
|
||||||
'application/json': openapi.MediaType{
|
'application/json': openapi.MediaType{
|
||||||
schema: Reference{
|
schema: Reference{
|
||||||
ref: '#/components/schemas/Pet'
|
ref: '#/components/schemas/Pet'
|
||||||
}
|
}
|
||||||
example: json.raw_decode('{ "id": 3, "name": "Bella", "tag": "dog" }')!
|
example: json.raw_decode('{ "id": 3, "name": "Bella", "tag": "dog" }')!
|
||||||
@@ -93,29 +93,29 @@ const openapi_spec = openapi.OpenAPI{
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
'/pets/{petId}': openapi.PathItem{
|
'/pets/{petId}': PathItem{
|
||||||
get: openapi.Operation{
|
get: Operation{
|
||||||
summary: 'Get a pet by ID'
|
summary: 'Get a pet by ID'
|
||||||
operation_id: 'getPet'
|
operation_id: 'getPet'
|
||||||
parameters: [
|
parameters: [
|
||||||
openapi.Parameter{
|
openapi.Parameter{
|
||||||
name: 'petId'
|
name: 'petId'
|
||||||
in_: 'path'
|
in_: 'path'
|
||||||
description: 'ID of the pet to retrieve'
|
description: 'ID of the pet to retrieve'
|
||||||
required: true
|
required: true
|
||||||
schema: Schema{
|
schema: Schema{
|
||||||
typ: 'integer'
|
typ: 'integer'
|
||||||
format: 'int64'
|
format: 'int64'
|
||||||
example: 1
|
example: 1
|
||||||
}
|
}
|
||||||
}
|
},
|
||||||
]
|
]
|
||||||
responses: {
|
responses: {
|
||||||
'200': openapi.ResponseSpec{
|
'200': openapi.ResponseSpec{
|
||||||
description: 'A pet'
|
description: 'A pet'
|
||||||
content: {
|
content: {
|
||||||
'application/json': openapi.MediaType{
|
'application/json': openapi.MediaType{
|
||||||
schema: Reference{
|
schema: Reference{
|
||||||
ref: '#/components/schemas/Pet'
|
ref: '#/components/schemas/Pet'
|
||||||
}
|
}
|
||||||
example: json.raw_decode('{ "id": 1, "name": "Fluffy", "tag": "dog" }')!
|
example: json.raw_decode('{ "id": 1, "name": "Fluffy", "tag": "dog" }')!
|
||||||
@@ -127,23 +127,23 @@ const openapi_spec = openapi.OpenAPI{
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
delete: openapi.Operation{
|
delete: Operation{
|
||||||
summary: 'Delete a pet by ID'
|
summary: 'Delete a pet by ID'
|
||||||
operation_id: 'deletePet'
|
operation_id: 'deletePet'
|
||||||
parameters: [
|
parameters: [
|
||||||
openapi.Parameter{
|
openapi.Parameter{
|
||||||
name: 'petId'
|
name: 'petId'
|
||||||
in_: 'path'
|
in_: 'path'
|
||||||
description: 'ID of the pet to delete'
|
description: 'ID of the pet to delete'
|
||||||
required: true
|
required: true
|
||||||
schema: Schema{
|
schema: Schema{
|
||||||
typ: 'integer'
|
typ: 'integer'
|
||||||
format: 'int64'
|
format: 'int64'
|
||||||
example: 1
|
example: 1
|
||||||
}
|
}
|
||||||
}
|
},
|
||||||
]
|
]
|
||||||
responses: {
|
responses: {
|
||||||
'204': openapi.ResponseSpec{
|
'204': openapi.ResponseSpec{
|
||||||
description: 'Pet deleted'
|
description: 'Pet deleted'
|
||||||
}
|
}
|
||||||
@@ -154,38 +154,38 @@ const openapi_spec = openapi.OpenAPI{
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
components: openapi.Components{
|
components: Components{
|
||||||
schemas: {
|
schemas: {
|
||||||
'Pet': SchemaRef(Schema{
|
'Pet': SchemaRef(Schema{
|
||||||
typ: 'object'
|
typ: 'object'
|
||||||
required: ['id', 'name']
|
required: ['id', 'name']
|
||||||
properties: {
|
properties: {
|
||||||
'id': SchemaRef(Schema{
|
'id': SchemaRef(Schema{
|
||||||
typ: 'integer'
|
typ: 'integer'
|
||||||
format: 'int64'
|
format: 'int64'
|
||||||
})
|
})
|
||||||
'name': SchemaRef(Schema{
|
'name': SchemaRef(Schema{
|
||||||
typ: 'string'
|
typ: 'string'
|
||||||
})
|
})
|
||||||
'tag': SchemaRef(Schema{
|
'tag': SchemaRef(Schema{
|
||||||
typ: 'string'
|
typ: 'string'
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
'NewPet': SchemaRef(Schema{
|
'NewPet': SchemaRef(Schema{
|
||||||
typ: 'object'
|
typ: 'object'
|
||||||
required: ['name']
|
required: ['name']
|
||||||
properties: {
|
properties: {
|
||||||
'name': SchemaRef(Schema{
|
'name': SchemaRef(Schema{
|
||||||
typ: 'string'
|
typ: 'string'
|
||||||
})
|
})
|
||||||
'tag': SchemaRef(Schema{
|
'tag': SchemaRef(Schema{
|
||||||
typ: 'string'
|
typ: 'string'
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
'Pets': SchemaRef(Schema{
|
'Pets': SchemaRef(Schema{
|
||||||
typ: 'array'
|
typ: 'array'
|
||||||
items: SchemaRef(Reference{
|
items: SchemaRef(Reference{
|
||||||
ref: '#/components/schemas/Pet'
|
ref: '#/components/schemas/Pet'
|
||||||
})
|
})
|
||||||
@@ -194,204 +194,204 @@ const openapi_spec = openapi.OpenAPI{
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const actor_spec = specification.ActorSpecification{
|
const actor_spec = ActorSpecification{
|
||||||
name: 'Pet Store API'
|
name: 'Pet Store API'
|
||||||
description: 'A sample API for a pet store'
|
description: 'A sample API for a pet store'
|
||||||
structure: code.Struct{}
|
structure: Struct{}
|
||||||
interfaces: [.openapi]
|
interfaces: [.openapi]
|
||||||
methods: [
|
methods: [
|
||||||
specification.ActorMethod{
|
ActorMethod{
|
||||||
name: 'listPets'
|
name: 'listPets'
|
||||||
summary: 'List all pets'
|
summary: 'List all pets'
|
||||||
example: openrpc.ExamplePairing{
|
example: openrpc.ExamplePairing{
|
||||||
params: [
|
params: [
|
||||||
openrpc.ExampleRef(openrpc.Example{
|
openrpc.ExampleRef(openrpc.Example{
|
||||||
name: 'Example limit'
|
name: 'Example limit'
|
||||||
description: 'Example Maximum number of pets to return'
|
description: 'Example Maximum number of pets to return'
|
||||||
value: 10
|
value: 10
|
||||||
})
|
}),
|
||||||
]
|
]
|
||||||
result: openrpc.ExampleRef(openrpc.Example{
|
result: openrpc.ExampleRef(openrpc.Example{
|
||||||
name: 'Example response'
|
name: 'Example response'
|
||||||
value: json.raw_decode('[
|
value: json.raw_decode('[
|
||||||
{"id": 1, "name": "Fluffy", "tag": "dog"},
|
{"id": 1, "name": "Fluffy", "tag": "dog"},
|
||||||
{"id": 2, "name": "Whiskers", "tag": "cat"}
|
{"id": 2, "name": "Whiskers", "tag": "cat"}
|
||||||
]')!
|
]')!
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
parameters: [
|
parameters: [
|
||||||
openrpc.ContentDescriptor{
|
ContentDescriptor{
|
||||||
name: 'limit'
|
name: 'limit'
|
||||||
summary: 'Maximum number of pets to return'
|
summary: 'Maximum number of pets to return'
|
||||||
description: 'Maximum number of pets to return'
|
description: 'Maximum number of pets to return'
|
||||||
required: false
|
required: false
|
||||||
schema: jsonschema.SchemaRef(jsonschema.Schema{
|
schema: SchemaRef(Schema{
|
||||||
typ: 'integer'
|
typ: 'integer'
|
||||||
format: 'int32'
|
format: 'int32'
|
||||||
example: 10
|
example: 10
|
||||||
})
|
})
|
||||||
}
|
},
|
||||||
]
|
]
|
||||||
result: openrpc.ContentDescriptor{
|
result: ContentDescriptor{
|
||||||
name: 'result'
|
name: 'result'
|
||||||
description: 'The response of the operation.'
|
description: 'The response of the operation.'
|
||||||
required: true
|
required: true
|
||||||
schema: jsonschema.SchemaRef(jsonschema.Reference{
|
schema: SchemaRef(Reference{
|
||||||
ref: '#/components/schemas/Pets'
|
ref: '#/components/schemas/Pets'
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
errors: [
|
errors: [
|
||||||
openrpc.ErrorSpec{
|
ErrorSpec{
|
||||||
code: 400
|
code: 400
|
||||||
message: 'Invalid request'
|
message: 'Invalid request'
|
||||||
}
|
},
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
specification.ActorMethod{
|
ActorMethod{
|
||||||
name: 'createPet'
|
name: 'createPet'
|
||||||
summary: 'Create a new pet'
|
summary: 'Create a new pet'
|
||||||
example: openrpc.ExamplePairing{
|
example: openrpc.ExamplePairing{
|
||||||
result: openrpc.ExampleRef(openrpc.Example{
|
result: openrpc.ExampleRef(openrpc.Example{
|
||||||
name: 'Example response'
|
name: 'Example response'
|
||||||
value: '[]'
|
value: '[]'
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
result: openrpc.ContentDescriptor{
|
result: ContentDescriptor{
|
||||||
name: 'result'
|
name: 'result'
|
||||||
description: 'The response of the operation.'
|
description: 'The response of the operation.'
|
||||||
required: true
|
required: true
|
||||||
}
|
}
|
||||||
errors: [
|
errors: [
|
||||||
openrpc.ErrorSpec{
|
ErrorSpec{
|
||||||
code: 400
|
code: 400
|
||||||
message: 'Invalid input'
|
message: 'Invalid input'
|
||||||
}
|
},
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
specification.ActorMethod{
|
ActorMethod{
|
||||||
name: 'getPet'
|
name: 'getPet'
|
||||||
summary: 'Get a pet by ID'
|
summary: 'Get a pet by ID'
|
||||||
example: openrpc.ExamplePairing{
|
example: openrpc.ExamplePairing{
|
||||||
params: [
|
params: [
|
||||||
openrpc.ExampleRef(openrpc.Example{
|
openrpc.ExampleRef(openrpc.Example{
|
||||||
name: 'Example petId'
|
name: 'Example petId'
|
||||||
description: 'Example ID of the pet to retrieve'
|
description: 'Example ID of the pet to retrieve'
|
||||||
value: 1
|
value: 1
|
||||||
})
|
}),
|
||||||
]
|
]
|
||||||
result: openrpc.ExampleRef(openrpc.Example{
|
result: openrpc.ExampleRef(openrpc.Example{
|
||||||
name: 'Example response'
|
name: 'Example response'
|
||||||
value: json.raw_decode('{"id": 1, "name": "Fluffy", "tag": "dog"}')!
|
value: json.raw_decode('{"id": 1, "name": "Fluffy", "tag": "dog"}')!
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
parameters: [
|
parameters: [
|
||||||
openrpc.ContentDescriptor{
|
ContentDescriptor{
|
||||||
name: 'petId'
|
name: 'petId'
|
||||||
summary: 'ID of the pet to retrieve'
|
summary: 'ID of the pet to retrieve'
|
||||||
description: 'ID of the pet to retrieve'
|
description: 'ID of the pet to retrieve'
|
||||||
required: true
|
required: true
|
||||||
schema: jsonschema.SchemaRef(jsonschema.Schema{
|
schema: SchemaRef(Schema{
|
||||||
typ: 'integer'
|
typ: 'integer'
|
||||||
format: 'int64'
|
format: 'int64'
|
||||||
example: 1
|
example: 1
|
||||||
})
|
})
|
||||||
}
|
},
|
||||||
]
|
]
|
||||||
result: openrpc.ContentDescriptor{
|
result: ContentDescriptor{
|
||||||
name: 'result'
|
name: 'result'
|
||||||
description: 'The response of the operation.'
|
description: 'The response of the operation.'
|
||||||
required: true
|
required: true
|
||||||
schema: jsonschema.SchemaRef(jsonschema.Reference{
|
schema: SchemaRef(Reference{
|
||||||
ref: '#/components/schemas/Pet'
|
ref: '#/components/schemas/Pet'
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
errors: [
|
errors: [
|
||||||
openrpc.ErrorSpec{
|
ErrorSpec{
|
||||||
code: 404
|
code: 404
|
||||||
message: 'Pet not found'
|
message: 'Pet not found'
|
||||||
}
|
},
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
specification.ActorMethod{
|
ActorMethod{
|
||||||
name: 'deletePet'
|
name: 'deletePet'
|
||||||
summary: 'Delete a pet by ID'
|
summary: 'Delete a pet by ID'
|
||||||
example: openrpc.ExamplePairing{
|
example: openrpc.ExamplePairing{
|
||||||
params: [
|
params: [
|
||||||
openrpc.ExampleRef(openrpc.Example{
|
openrpc.ExampleRef(openrpc.Example{
|
||||||
name: 'Example petId'
|
name: 'Example petId'
|
||||||
description: 'Example ID of the pet to delete'
|
description: 'Example ID of the pet to delete'
|
||||||
value: 1
|
value: 1
|
||||||
})
|
}),
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
parameters: [
|
parameters: [
|
||||||
openrpc.ContentDescriptor{
|
ContentDescriptor{
|
||||||
name: 'petId'
|
name: 'petId'
|
||||||
summary: 'ID of the pet to delete'
|
summary: 'ID of the pet to delete'
|
||||||
description: 'ID of the pet to delete'
|
description: 'ID of the pet to delete'
|
||||||
required: true
|
required: true
|
||||||
schema: jsonschema.SchemaRef(jsonschema.Schema{
|
schema: SchemaRef(Schema{
|
||||||
typ: 'integer'
|
typ: 'integer'
|
||||||
format: 'int64'
|
format: 'int64'
|
||||||
example: 1
|
example: 1
|
||||||
})
|
})
|
||||||
}
|
},
|
||||||
]
|
]
|
||||||
result: openrpc.ContentDescriptor{
|
result: ContentDescriptor{
|
||||||
name: 'result'
|
name: 'result'
|
||||||
description: 'The response of the operation.'
|
description: 'The response of the operation.'
|
||||||
required: true
|
required: true
|
||||||
}
|
}
|
||||||
errors: [
|
errors: [
|
||||||
openrpc.ErrorSpec{
|
ErrorSpec{
|
||||||
code: 404
|
code: 404
|
||||||
message: 'Pet not found'
|
message: 'Pet not found'
|
||||||
}
|
},
|
||||||
]
|
]
|
||||||
}
|
},
|
||||||
]
|
]
|
||||||
objects: [
|
objects: [
|
||||||
specification.BaseObject{
|
BaseObject{
|
||||||
schema: jsonschema.Schema{
|
schema: Schema{
|
||||||
typ: 'object'
|
typ: 'object'
|
||||||
properties: {
|
properties: {
|
||||||
'id': jsonschema.SchemaRef(jsonschema.Schema{
|
'id': SchemaRef(Schema{
|
||||||
typ: 'integer'
|
typ: 'integer'
|
||||||
format: 'int64'
|
format: 'int64'
|
||||||
}),
|
})
|
||||||
'name': jsonschema.SchemaRef(jsonschema.Schema{
|
'name': SchemaRef(Schema{
|
||||||
typ: 'string'
|
typ: 'string'
|
||||||
}),
|
})
|
||||||
'tag': jsonschema.SchemaRef(jsonschema.Schema{
|
'tag': SchemaRef(Schema{
|
||||||
typ: 'string'
|
typ: 'string'
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
required: ['id', 'name']
|
required: ['id', 'name']
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
specification.BaseObject{
|
BaseObject{
|
||||||
schema: jsonschema.Schema{
|
schema: Schema{
|
||||||
typ: 'object'
|
typ: 'object'
|
||||||
properties: {
|
properties: {
|
||||||
'name': jsonschema.SchemaRef(jsonschema.Schema{
|
'name': SchemaRef(Schema{
|
||||||
typ: 'string'
|
typ: 'string'
|
||||||
}),
|
})
|
||||||
'tag': jsonschema.SchemaRef(jsonschema.Schema{
|
'tag': SchemaRef(Schema{
|
||||||
typ: 'string'
|
typ: 'string'
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
required: ['name']
|
required: ['name']
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
specification.BaseObject{
|
BaseObject{
|
||||||
schema: jsonschema.Schema{
|
schema: Schema{
|
||||||
typ: 'array'
|
typ: 'array'
|
||||||
items: jsonschema.Items(jsonschema.SchemaRef(jsonschema.Reference{
|
items: jsonschema.Items(SchemaRef(Reference{
|
||||||
ref: '#/components/schemas/Pet'
|
ref: '#/components/schemas/Pet'
|
||||||
}))
|
}))
|
||||||
}
|
}
|
||||||
}
|
},
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn test_from_openapi() ! {
|
pub fn test_from_openapi() ! {
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
module specification
|
module specification
|
||||||
|
|
||||||
import freeflowuniverse.herolib.schemas.openrpc { OpenRPC, Method, ContentDescriptor, ErrorSpec }
|
import freeflowuniverse.herolib.schemas.openrpc { ContentDescriptor, ErrorSpec, Method, OpenRPC }
|
||||||
import freeflowuniverse.herolib.schemas.jsonschema { Reference, Schema, SchemaRef }
|
import freeflowuniverse.herolib.schemas.jsonschema { Reference, Schema }
|
||||||
import freeflowuniverse.herolib.core.texttools
|
import freeflowuniverse.herolib.core.texttools
|
||||||
|
|
||||||
// Helper function: Convert OpenRPC Method to ActorMethod
|
// Helper function: Convert OpenRPC Method to ActorMethod
|
||||||
@@ -32,12 +32,12 @@ fn openrpc_method_to_actor_method(method Method) ActorMethod {
|
|||||||
}
|
}
|
||||||
|
|
||||||
return ActorMethod{
|
return ActorMethod{
|
||||||
name: method.name
|
name: method.name
|
||||||
description: method.description
|
description: method.description
|
||||||
summary: method.summary
|
summary: method.summary
|
||||||
parameters: parameters
|
parameters: parameters
|
||||||
result: method.result as ContentDescriptor
|
result: method.result as ContentDescriptor
|
||||||
errors: errors
|
errors: errors
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -86,9 +86,10 @@ pub fn from_openrpc(spec OpenRPC) !ActorSpecification {
|
|||||||
if schema is Schema {
|
if schema is Schema {
|
||||||
if schema.typ == 'object' {
|
if schema.typ == 'object' {
|
||||||
objects << BaseObject{
|
objects << BaseObject{
|
||||||
schema: Schema {...schema,
|
schema: Schema{
|
||||||
|
...schema
|
||||||
title: texttools.pascal_case(key)
|
title: texttools.pascal_case(key)
|
||||||
id: texttools.snake_case(key)
|
id: texttools.snake_case(key)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -96,10 +97,10 @@ pub fn from_openrpc(spec OpenRPC) !ActorSpecification {
|
|||||||
}
|
}
|
||||||
|
|
||||||
return ActorSpecification{
|
return ActorSpecification{
|
||||||
name: spec.info.title
|
name: spec.info.title
|
||||||
description: spec.info.description
|
description: spec.info.description
|
||||||
interfaces: [.openrpc]
|
interfaces: [.openrpc]
|
||||||
methods: methods
|
methods: methods
|
||||||
objects: objects
|
objects: objects
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1,380 +1,421 @@
|
|||||||
module specification
|
module specification
|
||||||
|
|
||||||
import freeflowuniverse.herolib.core.code { Struct, Function }
|
import freeflowuniverse.herolib.core.code { Struct }
|
||||||
import freeflowuniverse.herolib.schemas.openrpc { ContentDescriptor, ErrorSpec }
|
import freeflowuniverse.herolib.schemas.openrpc { ContentDescriptor }
|
||||||
import freeflowuniverse.herolib.schemas.openapi { OpenAPI, Info, ServerSpec, Components, Operation, PathItem, PathRef }
|
import freeflowuniverse.herolib.schemas.openapi { Components, Info }
|
||||||
import freeflowuniverse.herolib.schemas.jsonschema {Schema, Reference, SchemaRef}
|
import freeflowuniverse.herolib.schemas.jsonschema { Reference, Schema, SchemaRef }
|
||||||
|
|
||||||
const openrpc_spec = openrpc.OpenRPC{
|
const openrpc_spec = openrpc.OpenRPC{
|
||||||
openrpc: '1.0.0-rc1'
|
openrpc: '1.0.0-rc1'
|
||||||
info: openrpc.Info{
|
info: openrpc.Info{
|
||||||
title: 'Petstore'
|
title: 'Petstore'
|
||||||
license: openrpc.License{
|
license: openrpc.License{
|
||||||
name: 'MIT'
|
name: 'MIT'
|
||||||
}
|
}
|
||||||
version: '1.0.0'
|
version: '1.0.0'
|
||||||
}
|
}
|
||||||
servers: [openrpc.Server{
|
servers: [
|
||||||
name: 'localhost'
|
openrpc.Server{
|
||||||
url: openrpc.RuntimeExpression('http://localhost:8080')
|
name: 'localhost'
|
||||||
}]
|
url: openrpc.RuntimeExpression('http://localhost:8080')
|
||||||
methods: [
|
},
|
||||||
openrpc.Method{
|
]
|
||||||
name: 'list_pets'
|
methods: [
|
||||||
summary: 'List all pets'
|
openrpc.Method{
|
||||||
params: [openrpc.ContentDescriptorRef(openrpc.ContentDescriptor{
|
name: 'list_pets'
|
||||||
name: 'limit'
|
summary: 'List all pets'
|
||||||
description: 'How many items to return at one time (max 100)'
|
params: [
|
||||||
required: false
|
openrpc.ContentDescriptorRef(ContentDescriptor{
|
||||||
schema: jsonschema.SchemaRef(jsonschema.Schema{
|
name: 'limit'
|
||||||
typ: 'integer'
|
description: 'How many items to return at one time (max 100)'
|
||||||
minimum: 1
|
required: false
|
||||||
})
|
schema: SchemaRef(Schema{
|
||||||
})]
|
typ: 'integer'
|
||||||
result: openrpc.ContentDescriptorRef(openrpc.ContentDescriptor{
|
minimum: 1
|
||||||
name: 'pets'
|
})
|
||||||
description: 'A paged array of pets'
|
}),
|
||||||
schema: jsonschema.SchemaRef(jsonschema.Schema{
|
]
|
||||||
typ: 'array'
|
result: openrpc.ContentDescriptorRef(ContentDescriptor{
|
||||||
items: jsonschema.Items(jsonschema.SchemaRef(jsonschema.Reference{
|
name: 'pets'
|
||||||
ref: '#/components/schemas/Pet'
|
description: 'A paged array of pets'
|
||||||
}))
|
schema: SchemaRef(Schema{
|
||||||
})
|
typ: 'array'
|
||||||
})
|
items: jsonschema.Items(SchemaRef(Reference{
|
||||||
examples: [openrpc.ExamplePairing{
|
ref: '#/components/schemas/Pet'
|
||||||
name: 'listPetExample'
|
}))
|
||||||
description: 'List pet example'
|
})
|
||||||
}]
|
})
|
||||||
},
|
examples: [
|
||||||
openrpc.Method{
|
openrpc.ExamplePairing{
|
||||||
name: 'create_pet'
|
name: 'listPetExample'
|
||||||
summary: 'Create a pet'
|
description: 'List pet example'
|
||||||
params: [
|
},
|
||||||
openrpc.ContentDescriptorRef(openrpc.ContentDescriptor{
|
]
|
||||||
name: 'newPetName'
|
},
|
||||||
description: 'Name of pet to create'
|
openrpc.Method{
|
||||||
required: true
|
name: 'create_pet'
|
||||||
schema: jsonschema.SchemaRef(jsonschema.Schema{
|
summary: 'Create a pet'
|
||||||
typ: 'string'
|
params: [
|
||||||
})
|
openrpc.ContentDescriptorRef(ContentDescriptor{
|
||||||
}),
|
name: 'newPetName'
|
||||||
openrpc.ContentDescriptorRef(openrpc.ContentDescriptor{
|
description: 'Name of pet to create'
|
||||||
name: 'newPetTag'
|
required: true
|
||||||
description: 'Pet tag to create'
|
schema: SchemaRef(Schema{
|
||||||
schema: jsonschema.SchemaRef(jsonschema.Schema{
|
typ: 'string'
|
||||||
typ: 'string'
|
})
|
||||||
})
|
}),
|
||||||
})
|
openrpc.ContentDescriptorRef(ContentDescriptor{
|
||||||
]
|
name: 'newPetTag'
|
||||||
result: openrpc.ContentDescriptorRef(jsonschema.Reference{
|
description: 'Pet tag to create'
|
||||||
ref: '#/components/contentDescriptors/PetId'
|
schema: SchemaRef(Schema{
|
||||||
})
|
typ: 'string'
|
||||||
examples: [openrpc.ExamplePairing{
|
})
|
||||||
name: 'createPetExample'
|
}),
|
||||||
description: 'Create pet example'
|
]
|
||||||
}]
|
result: openrpc.ContentDescriptorRef(Reference{
|
||||||
},
|
ref: '#/components/contentDescriptors/PetId'
|
||||||
openrpc.Method{
|
})
|
||||||
name: 'get_pet'
|
examples: [
|
||||||
summary: 'Info for a specific pet'
|
openrpc.ExamplePairing{
|
||||||
params: [openrpc.ContentDescriptorRef(jsonschema.Reference{
|
name: 'createPetExample'
|
||||||
ref: '#/components/contentDescriptors/PetId'
|
description: 'Create pet example'
|
||||||
})]
|
},
|
||||||
result: openrpc.ContentDescriptorRef(openrpc.ContentDescriptor{
|
]
|
||||||
name: 'pet'
|
},
|
||||||
description: 'Expected response to a valid request'
|
openrpc.Method{
|
||||||
schema: jsonschema.SchemaRef(jsonschema.Reference{
|
name: 'get_pet'
|
||||||
ref: '#/components/schemas/Pet'
|
summary: 'Info for a specific pet'
|
||||||
})
|
params: [
|
||||||
})
|
openrpc.ContentDescriptorRef(Reference{
|
||||||
examples: [openrpc.ExamplePairing{
|
ref: '#/components/contentDescriptors/PetId'
|
||||||
name: 'getPetExample'
|
}),
|
||||||
description: 'Get pet example'
|
]
|
||||||
}]
|
result: openrpc.ContentDescriptorRef(ContentDescriptor{
|
||||||
},
|
name: 'pet'
|
||||||
openrpc.Method{
|
description: 'Expected response to a valid request'
|
||||||
name: 'update_pet'
|
schema: SchemaRef(Reference{
|
||||||
summary: 'Update a pet'
|
ref: '#/components/schemas/Pet'
|
||||||
params: [
|
})
|
||||||
openrpc.ContentDescriptorRef(jsonschema.Reference{
|
})
|
||||||
ref: '#/components/contentDescriptors/PetId'
|
examples: [
|
||||||
}),
|
openrpc.ExamplePairing{
|
||||||
openrpc.ContentDescriptorRef(openrpc.ContentDescriptor{
|
name: 'getPetExample'
|
||||||
name: 'updatedPetName'
|
description: 'Get pet example'
|
||||||
description: 'New name for the pet'
|
},
|
||||||
required: true
|
]
|
||||||
schema: jsonschema.SchemaRef(jsonschema.Schema{
|
},
|
||||||
typ: 'string'
|
openrpc.Method{
|
||||||
})
|
name: 'update_pet'
|
||||||
}),
|
summary: 'Update a pet'
|
||||||
openrpc.ContentDescriptorRef(openrpc.ContentDescriptor{
|
params: [
|
||||||
name: 'updatedPetTag'
|
openrpc.ContentDescriptorRef(Reference{
|
||||||
description: 'New tag for the pet'
|
ref: '#/components/contentDescriptors/PetId'
|
||||||
schema: jsonschema.SchemaRef(jsonschema.Schema{
|
}),
|
||||||
typ: 'string'
|
openrpc.ContentDescriptorRef(ContentDescriptor{
|
||||||
})
|
name: 'updatedPetName'
|
||||||
})
|
description: 'New name for the pet'
|
||||||
]
|
required: true
|
||||||
result: openrpc.ContentDescriptorRef(openrpc.ContentDescriptor{
|
schema: SchemaRef(Schema{
|
||||||
name: 'pet'
|
typ: 'string'
|
||||||
description: 'Updated pet object'
|
})
|
||||||
schema: jsonschema.SchemaRef(jsonschema.Reference{
|
}),
|
||||||
ref: '#/components/schemas/Pet'
|
openrpc.ContentDescriptorRef(ContentDescriptor{
|
||||||
})
|
name: 'updatedPetTag'
|
||||||
})
|
description: 'New tag for the pet'
|
||||||
examples: [openrpc.ExamplePairing{
|
schema: SchemaRef(Schema{
|
||||||
name: 'updatePetExample'
|
typ: 'string'
|
||||||
description: 'Update pet example'
|
})
|
||||||
}]
|
}),
|
||||||
},
|
]
|
||||||
openrpc.Method{
|
result: openrpc.ContentDescriptorRef(ContentDescriptor{
|
||||||
name: 'delete_pet'
|
name: 'pet'
|
||||||
summary: 'Delete a pet'
|
description: 'Updated pet object'
|
||||||
params: [openrpc.ContentDescriptorRef(jsonschema.Reference{
|
schema: SchemaRef(Reference{
|
||||||
ref: '#/components/contentDescriptors/PetId'
|
ref: '#/components/schemas/Pet'
|
||||||
})]
|
})
|
||||||
result: openrpc.ContentDescriptorRef(openrpc.ContentDescriptor{
|
})
|
||||||
name: 'success'
|
examples: [
|
||||||
description: 'Boolean indicating success'
|
openrpc.ExamplePairing{
|
||||||
schema: jsonschema.SchemaRef(jsonschema.Schema{
|
name: 'updatePetExample'
|
||||||
typ: 'boolean'
|
description: 'Update pet example'
|
||||||
})
|
},
|
||||||
})
|
]
|
||||||
examples: [openrpc.ExamplePairing{
|
},
|
||||||
name: 'deletePetExample'
|
openrpc.Method{
|
||||||
description: 'Delete pet example'
|
name: 'delete_pet'
|
||||||
}]
|
summary: 'Delete a pet'
|
||||||
}
|
params: [
|
||||||
]
|
openrpc.ContentDescriptorRef(Reference{
|
||||||
components: openrpc.Components{
|
ref: '#/components/contentDescriptors/PetId'
|
||||||
content_descriptors: {
|
}),
|
||||||
'PetId': openrpc.ContentDescriptorRef(openrpc.ContentDescriptor{
|
]
|
||||||
name: 'petId'
|
result: openrpc.ContentDescriptorRef(ContentDescriptor{
|
||||||
description: 'The ID of the pet'
|
name: 'success'
|
||||||
required: true
|
description: 'Boolean indicating success'
|
||||||
schema: jsonschema.SchemaRef(jsonschema.Reference{
|
schema: SchemaRef(Schema{
|
||||||
ref: '#/components/schemas/PetId'
|
typ: 'boolean'
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
}
|
examples: [
|
||||||
schemas: {
|
openrpc.ExamplePairing{
|
||||||
'PetId': jsonschema.SchemaRef(jsonschema.Schema{
|
name: 'deletePetExample'
|
||||||
typ: 'integer'
|
description: 'Delete pet example'
|
||||||
minimum: 0
|
},
|
||||||
}),
|
]
|
||||||
'Pet': jsonschema.SchemaRef(jsonschema.Schema{
|
},
|
||||||
typ: 'object'
|
]
|
||||||
properties: {
|
components: openrpc.Components{
|
||||||
'id': jsonschema.SchemaRef(jsonschema.Reference{
|
content_descriptors: {
|
||||||
ref: '#/components/schemas/PetId'
|
'PetId': openrpc.ContentDescriptorRef(ContentDescriptor{
|
||||||
}),
|
name: 'petId'
|
||||||
'name': jsonschema.SchemaRef(jsonschema.Schema{
|
description: 'The ID of the pet'
|
||||||
typ: 'string'
|
required: true
|
||||||
}),
|
schema: SchemaRef(Reference{
|
||||||
'tag': jsonschema.SchemaRef(jsonschema.Schema{
|
ref: '#/components/schemas/PetId'
|
||||||
typ: 'string'
|
})
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
required: ['id', 'name']
|
schemas: {
|
||||||
})
|
'PetId': SchemaRef(Schema{
|
||||||
}
|
typ: 'integer'
|
||||||
}
|
minimum: 0
|
||||||
|
})
|
||||||
|
'Pet': SchemaRef(Schema{
|
||||||
|
typ: 'object'
|
||||||
|
properties: {
|
||||||
|
'id': SchemaRef(Reference{
|
||||||
|
ref: '#/components/schemas/PetId'
|
||||||
|
})
|
||||||
|
'name': SchemaRef(Schema{
|
||||||
|
typ: 'string'
|
||||||
|
})
|
||||||
|
'tag': SchemaRef(Schema{
|
||||||
|
typ: 'string'
|
||||||
|
})
|
||||||
|
}
|
||||||
|
required: ['id', 'name']
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const actor_spec = ActorSpecification{
|
||||||
const actor_spec = specification.ActorSpecification{
|
name: 'Petstore'
|
||||||
name: 'Petstore'
|
structure: Struct{
|
||||||
structure: code.Struct{
|
is_pub: false
|
||||||
is_pub: false
|
}
|
||||||
}
|
interfaces: [.openrpc]
|
||||||
interfaces: [.openrpc]
|
methods: [
|
||||||
methods: [specification.ActorMethod{
|
ActorMethod{
|
||||||
name: 'list_pets'
|
name: 'list_pets'
|
||||||
summary: 'List all pets'
|
summary: 'List all pets'
|
||||||
parameters: [openrpc.ContentDescriptor{
|
parameters: [
|
||||||
name: 'limit'
|
ContentDescriptor{
|
||||||
description: 'How many items to return at one time (max 100)'
|
name: 'limit'
|
||||||
required: false
|
description: 'How many items to return at one time (max 100)'
|
||||||
schema: jsonschema.SchemaRef(jsonschema.Schema{
|
required: false
|
||||||
typ: 'integer'
|
schema: SchemaRef(Schema{
|
||||||
minimum: 1
|
typ: 'integer'
|
||||||
})
|
minimum: 1
|
||||||
}]
|
})
|
||||||
result: openrpc.ContentDescriptor{
|
},
|
||||||
name: 'pets'
|
]
|
||||||
description: 'A paged array of pets'
|
result: ContentDescriptor{
|
||||||
schema: jsonschema.SchemaRef(jsonschema.Schema{
|
name: 'pets'
|
||||||
typ: 'array'
|
description: 'A paged array of pets'
|
||||||
items: jsonschema.Items(jsonschema.SchemaRef(jsonschema.Schema{
|
schema: SchemaRef(Schema{
|
||||||
typ: 'object'
|
typ: 'array'
|
||||||
properties: {
|
items: jsonschema.Items(SchemaRef(Schema{
|
||||||
'id': jsonschema.SchemaRef(jsonschema.Reference{
|
typ: 'object'
|
||||||
ref: '#/components/schemas/PetId'
|
properties: {
|
||||||
}),
|
'id': SchemaRef(Reference{
|
||||||
'name': jsonschema.SchemaRef(jsonschema.Schema{
|
ref: '#/components/schemas/PetId'
|
||||||
typ: 'string'
|
})
|
||||||
}),
|
'name': SchemaRef(Schema{
|
||||||
'tag': jsonschema.SchemaRef(jsonschema.Schema{
|
typ: 'string'
|
||||||
typ: 'string'
|
})
|
||||||
})
|
'tag': SchemaRef(Schema{
|
||||||
}
|
typ: 'string'
|
||||||
required: ['id', 'name']
|
})
|
||||||
}))
|
}
|
||||||
})
|
required: [
|
||||||
}
|
'id',
|
||||||
}, specification.ActorMethod{
|
'name',
|
||||||
name: 'create_pet'
|
]
|
||||||
summary: 'Create a pet'
|
}))
|
||||||
parameters: [openrpc.ContentDescriptor{
|
})
|
||||||
name: 'newPetName'
|
}
|
||||||
description: 'Name of pet to create'
|
},
|
||||||
required: true
|
ActorMethod{
|
||||||
schema: jsonschema.SchemaRef(jsonschema.Schema{
|
name: 'create_pet'
|
||||||
typ: 'string'
|
summary: 'Create a pet'
|
||||||
})
|
parameters: [
|
||||||
}, openrpc.ContentDescriptor{
|
ContentDescriptor{
|
||||||
name: 'newPetTag'
|
name: 'newPetName'
|
||||||
description: 'Pet tag to create'
|
description: 'Name of pet to create'
|
||||||
schema: jsonschema.SchemaRef(jsonschema.Schema{
|
required: true
|
||||||
typ: 'string'
|
schema: SchemaRef(Schema{
|
||||||
})
|
typ: 'string'
|
||||||
}]
|
})
|
||||||
}, specification.ActorMethod{
|
},
|
||||||
name: 'get_pet'
|
ContentDescriptor{
|
||||||
summary: 'Info for a specific pet'
|
name: 'newPetTag'
|
||||||
result: openrpc.ContentDescriptor{
|
description: 'Pet tag to create'
|
||||||
name: 'pet'
|
schema: SchemaRef(Schema{
|
||||||
description: 'Expected response to a valid request'
|
typ: 'string'
|
||||||
schema: jsonschema.SchemaRef(jsonschema.Schema{
|
})
|
||||||
typ: 'object'
|
},
|
||||||
properties: {
|
]
|
||||||
'id': jsonschema.SchemaRef(jsonschema.Reference{
|
},
|
||||||
ref: '#/components/schemas/PetId'
|
ActorMethod{
|
||||||
}),
|
name: 'get_pet'
|
||||||
'name': jsonschema.SchemaRef(jsonschema.Schema{
|
summary: 'Info for a specific pet'
|
||||||
typ: 'string'
|
result: ContentDescriptor{
|
||||||
}),
|
name: 'pet'
|
||||||
'tag': jsonschema.SchemaRef(jsonschema.Schema{
|
description: 'Expected response to a valid request'
|
||||||
typ: 'string'
|
schema: SchemaRef(Schema{
|
||||||
})
|
typ: 'object'
|
||||||
}
|
properties: {
|
||||||
required: ['id', 'name']
|
'id': SchemaRef(Reference{
|
||||||
})
|
ref: '#/components/schemas/PetId'
|
||||||
}
|
})
|
||||||
}, specification.ActorMethod{
|
'name': SchemaRef(Schema{
|
||||||
name: 'update_pet'
|
typ: 'string'
|
||||||
summary: 'Update a pet'
|
})
|
||||||
parameters: [openrpc.ContentDescriptor{
|
'tag': SchemaRef(Schema{
|
||||||
name: 'updatedPetName'
|
typ: 'string'
|
||||||
description: 'New name for the pet'
|
})
|
||||||
required: true
|
}
|
||||||
schema: jsonschema.SchemaRef(jsonschema.Schema{
|
required: [
|
||||||
typ: 'string'
|
'id',
|
||||||
})
|
'name',
|
||||||
}, openrpc.ContentDescriptor{
|
]
|
||||||
name: 'updatedPetTag'
|
})
|
||||||
description: 'New tag for the pet'
|
}
|
||||||
schema: jsonschema.SchemaRef(jsonschema.Schema{
|
},
|
||||||
typ: 'string'
|
ActorMethod{
|
||||||
})
|
name: 'update_pet'
|
||||||
}]
|
summary: 'Update a pet'
|
||||||
result: openrpc.ContentDescriptor{
|
parameters: [
|
||||||
name: 'pet'
|
ContentDescriptor{
|
||||||
description: 'Updated pet object'
|
name: 'updatedPetName'
|
||||||
schema: jsonschema.SchemaRef(jsonschema.Schema{
|
description: 'New name for the pet'
|
||||||
typ: 'object'
|
required: true
|
||||||
properties: {
|
schema: SchemaRef(Schema{
|
||||||
'id': jsonschema.SchemaRef(jsonschema.Reference{
|
typ: 'string'
|
||||||
ref: '#/components/schemas/PetId'
|
})
|
||||||
}),
|
},
|
||||||
'name': jsonschema.SchemaRef(jsonschema.Schema{
|
ContentDescriptor{
|
||||||
typ: 'string'
|
name: 'updatedPetTag'
|
||||||
}),
|
description: 'New tag for the pet'
|
||||||
'tag': jsonschema.SchemaRef(jsonschema.Schema{
|
schema: SchemaRef(Schema{
|
||||||
typ: 'string'
|
typ: 'string'
|
||||||
})
|
})
|
||||||
}
|
},
|
||||||
required: ['id', 'name']
|
]
|
||||||
})
|
result: ContentDescriptor{
|
||||||
}
|
name: 'pet'
|
||||||
}, specification.ActorMethod{
|
description: 'Updated pet object'
|
||||||
name: 'delete_pet'
|
schema: SchemaRef(Schema{
|
||||||
summary: 'Delete a pet'
|
typ: 'object'
|
||||||
result: openrpc.ContentDescriptor{
|
properties: {
|
||||||
name: 'success'
|
'id': SchemaRef(Reference{
|
||||||
description: 'Boolean indicating success'
|
ref: '#/components/schemas/PetId'
|
||||||
schema: jsonschema.SchemaRef(jsonschema.Schema{
|
})
|
||||||
typ: 'boolean'
|
'name': SchemaRef(Schema{
|
||||||
})
|
typ: 'string'
|
||||||
}
|
})
|
||||||
}]
|
'tag': SchemaRef(Schema{
|
||||||
objects: [specification.BaseObject{
|
typ: 'string'
|
||||||
schema: jsonschema.Schema{
|
})
|
||||||
id: 'pet'
|
}
|
||||||
title: 'Pet'
|
required: [
|
||||||
typ: 'object'
|
'id',
|
||||||
properties: {
|
'name',
|
||||||
'id': jsonschema.SchemaRef(jsonschema.Reference{
|
]
|
||||||
ref: '#/components/schemas/PetId'
|
})
|
||||||
}),
|
}
|
||||||
'name': jsonschema.SchemaRef(jsonschema.Schema{
|
},
|
||||||
typ: 'string'
|
ActorMethod{
|
||||||
}),
|
name: 'delete_pet'
|
||||||
'tag': jsonschema.SchemaRef(jsonschema.Schema{
|
summary: 'Delete a pet'
|
||||||
typ: 'string'
|
result: ContentDescriptor{
|
||||||
})
|
name: 'success'
|
||||||
}
|
description: 'Boolean indicating success'
|
||||||
required: ['id', 'name']
|
schema: SchemaRef(Schema{
|
||||||
}
|
typ: 'boolean'
|
||||||
}]
|
})
|
||||||
|
}
|
||||||
|
},
|
||||||
|
]
|
||||||
|
objects: [
|
||||||
|
BaseObject{
|
||||||
|
schema: Schema{
|
||||||
|
id: 'pet'
|
||||||
|
title: 'Pet'
|
||||||
|
typ: 'object'
|
||||||
|
properties: {
|
||||||
|
'id': SchemaRef(Reference{
|
||||||
|
ref: '#/components/schemas/PetId'
|
||||||
|
})
|
||||||
|
'name': SchemaRef(Schema{
|
||||||
|
typ: 'string'
|
||||||
|
})
|
||||||
|
'tag': SchemaRef(Schema{
|
||||||
|
typ: 'string'
|
||||||
|
})
|
||||||
|
}
|
||||||
|
required: ['id', 'name']
|
||||||
|
}
|
||||||
|
},
|
||||||
|
]
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn test_from_openrpc() ! {
|
pub fn test_from_openrpc() ! {
|
||||||
actor_spec_ := from_openrpc(openrpc_spec)!
|
actor_spec_ := from_openrpc(openrpc_spec)!
|
||||||
assert actor_spec_.methods.len == actor_spec.methods.len
|
assert actor_spec_.methods.len == actor_spec.methods.len
|
||||||
assert_methods_match(actor_spec_.methods[0], actor_spec.methods[0])
|
assert_methods_match(actor_spec_.methods[0], actor_spec.methods[0])
|
||||||
|
|
||||||
|
// assert from_openrpc(openrpc_spec)! == actor_spec
|
||||||
// assert from_openrpc(openrpc_spec)! == actor_spec
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn assert_methods_match(a ActorMethod, b ActorMethod) {
|
fn assert_methods_match(a ActorMethod, b ActorMethod) {
|
||||||
// Compare method names
|
// Compare method names
|
||||||
assert a.name == b.name, 'Method names do not match: ${a.name} != ${b.name}'
|
assert a.name == b.name, 'Method names do not match: ${a.name} != ${b.name}'
|
||||||
|
|
||||||
// Compare summaries
|
// Compare summaries
|
||||||
assert a.summary == b.summary, 'Method summaries do not match for method ${a.name}.'
|
assert a.summary == b.summary, 'Method summaries do not match for method ${a.name}.'
|
||||||
|
|
||||||
// Compare descriptions
|
// Compare descriptions
|
||||||
assert a.description == b.description, 'Method descriptions do not match for method ${a.name}.'
|
assert a.description == b.description, 'Method descriptions do not match for method ${a.name}.'
|
||||||
|
|
||||||
// Compare parameters count
|
// Compare parameters count
|
||||||
assert a.parameters.len == b.parameters.len, 'Parameter counts do not match for method ${a.name}.'
|
assert a.parameters.len == b.parameters.len, 'Parameter counts do not match for method ${a.name}.'
|
||||||
|
|
||||||
// Compare each parameter
|
// Compare each parameter
|
||||||
for i, param_a in a.parameters {
|
for i, param_a in a.parameters {
|
||||||
assert_params_match(param_a, b.parameters[i], a.name)
|
assert_params_match(param_a, b.parameters[i], a.name)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Compare result
|
// Compare result
|
||||||
assert_params_match(a.result, b.result, a.name)
|
assert_params_match(a.result, b.result, a.name)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn assert_params_match(a openrpc.ContentDescriptor, b openrpc.ContentDescriptor, method_name string) {
|
fn assert_params_match(a ContentDescriptor, b ContentDescriptor, method_name string) {
|
||||||
// Compare parameter names
|
// Compare parameter names
|
||||||
assert a.name == b.name, 'Parameter names do not match in method ${method_name}: ${a.name} != ${b.name}'
|
assert a.name == b.name, 'Parameter names do not match in method ${method_name}: ${a.name} != ${b.name}'
|
||||||
|
|
||||||
// Compare summaries
|
// Compare summaries
|
||||||
assert a.summary == b.summary, 'Parameter summaries do not match in method ${method_name}: ${a.name}'
|
assert a.summary == b.summary, 'Parameter summaries do not match in method ${method_name}: ${a.name}'
|
||||||
|
|
||||||
// Compare descriptions
|
// Compare descriptions
|
||||||
assert a.description == b.description, 'Parameter descriptions do not match in method ${method_name}: ${a.name}'
|
assert a.description == b.description, 'Parameter descriptions do not match in method ${method_name}: ${a.name}'
|
||||||
|
|
||||||
// Compare required flags
|
// Compare required flags
|
||||||
assert a.required == b.required, 'Required flags do not match in method ${method_name}: ${a.name}'
|
assert a.required == b.required, 'Required flags do not match in method ${method_name}: ${a.name}'
|
||||||
|
|
||||||
// Compare schemas
|
// Compare schemas
|
||||||
// assert_schemas_match(a.schema, b.schema, method_name, a.name)
|
// assert_schemas_match(a.schema, b.schema, method_name, a.name)
|
||||||
}
|
}
|
||||||
|
|
||||||
// fn assert_schemas_match(a jsonschema.SchemaRef, b jsonschema.SchemaRef, method_name string, param_name string) {
|
// fn assert_schemas_match(a jsonschema.SchemaRef, b jsonschema.SchemaRef, method_name string, param_name string) {
|
||||||
|
|||||||
@@ -1,21 +1,21 @@
|
|||||||
module specification
|
module specification
|
||||||
|
|
||||||
import freeflowuniverse.herolib.core.code { Struct, Function }
|
import freeflowuniverse.herolib.core.code { Struct }
|
||||||
import freeflowuniverse.herolib.schemas.openapi
|
import freeflowuniverse.herolib.schemas.openapi
|
||||||
import freeflowuniverse.herolib.schemas.openrpc {ExamplePairing, ContentDescriptor, ErrorSpec}
|
import freeflowuniverse.herolib.schemas.openrpc { ContentDescriptor, ErrorSpec, ExamplePairing }
|
||||||
import freeflowuniverse.herolib.schemas.jsonschema {Schema, Reference}
|
import freeflowuniverse.herolib.schemas.jsonschema { Reference, Schema }
|
||||||
|
|
||||||
pub struct ActorSpecification {
|
pub struct ActorSpecification {
|
||||||
pub mut:
|
pub mut:
|
||||||
version string = '1.0.0'
|
version string = '1.0.0'
|
||||||
openapi ?openapi.OpenAPI
|
openapi ?openapi.OpenAPI
|
||||||
openrpc ?openrpc.OpenRPC
|
openrpc ?openrpc.OpenRPC
|
||||||
name string @[omitempty]
|
name string @[omitempty]
|
||||||
description string @[omitempty]
|
description string @[omitempty]
|
||||||
structure Struct @[omitempty]
|
structure Struct @[omitempty]
|
||||||
interfaces []ActorInterface @[omitempty]
|
interfaces []ActorInterface @[omitempty]
|
||||||
methods []ActorMethod @[omitempty]
|
methods []ActorMethod @[omitempty]
|
||||||
objects []BaseObject @[omitempty]
|
objects []BaseObject @[omitempty]
|
||||||
}
|
}
|
||||||
|
|
||||||
pub enum ActorInterface {
|
pub enum ActorInterface {
|
||||||
@@ -28,24 +28,24 @@ pub enum ActorInterface {
|
|||||||
|
|
||||||
pub struct ActorMethod {
|
pub struct ActorMethod {
|
||||||
pub:
|
pub:
|
||||||
name string @[omitempty]
|
name string @[omitempty]
|
||||||
description string @[omitempty]
|
description string @[omitempty]
|
||||||
summary string
|
summary string
|
||||||
example ExamplePairing
|
example ExamplePairing
|
||||||
parameters []ContentDescriptor
|
parameters []ContentDescriptor
|
||||||
result ContentDescriptor
|
result ContentDescriptor
|
||||||
errors []ErrorSpec
|
errors []ErrorSpec
|
||||||
category MethodCategory
|
category MethodCategory
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct BaseObject {
|
pub struct BaseObject {
|
||||||
pub mut:
|
pub mut:
|
||||||
schema Schema
|
schema Schema
|
||||||
new_method ?ActorMethod
|
new_method ?ActorMethod
|
||||||
get_method ?ActorMethod
|
get_method ?ActorMethod
|
||||||
set_method ?ActorMethod
|
set_method ?ActorMethod
|
||||||
delete_method ?ActorMethod
|
delete_method ?ActorMethod
|
||||||
list_method ?ActorMethod
|
list_method ?ActorMethod
|
||||||
filter_method ?ActorMethod
|
filter_method ?ActorMethod
|
||||||
other_methods []ActorMethod
|
other_methods []ActorMethod
|
||||||
}
|
}
|
||||||
@@ -101,13 +101,13 @@ pub fn (s ActorSpecification) validate() ActorSpecification {
|
|||||||
if m := methods.filter(it.is_list_method())[0] {
|
if m := methods.filter(it.is_list_method())[0] {
|
||||||
obj.list_method = m
|
obj.list_method = m
|
||||||
}
|
}
|
||||||
validated_objects << BaseObject {
|
validated_objects << BaseObject{
|
||||||
...obj
|
...obj
|
||||||
other_methods: methods.filter(!it.is_crudlf_method())
|
other_methods: methods.filter(!it.is_crudlf_method())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return ActorSpecification {
|
return ActorSpecification{
|
||||||
...s,
|
...s
|
||||||
objects: validated_objects
|
objects: validated_objects
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -150,35 +150,38 @@ fn (s ActorSpecification) is_base_object_method(method ActorMethod) bool {
|
|||||||
fn (m ActorMethod) is_new_method() bool {
|
fn (m ActorMethod) is_new_method() bool {
|
||||||
return m.name.starts_with('new')
|
return m.name.starts_with('new')
|
||||||
}
|
}
|
||||||
|
|
||||||
fn (m ActorMethod) is_get_method() bool {
|
fn (m ActorMethod) is_get_method() bool {
|
||||||
return m.name.starts_with('get')
|
return m.name.starts_with('get')
|
||||||
}
|
}
|
||||||
|
|
||||||
fn (m ActorMethod) is_set_method() bool {
|
fn (m ActorMethod) is_set_method() bool {
|
||||||
return m.name.starts_with('set')
|
return m.name.starts_with('set')
|
||||||
}
|
}
|
||||||
|
|
||||||
fn (m ActorMethod) is_delete_method() bool {
|
fn (m ActorMethod) is_delete_method() bool {
|
||||||
return m.name.starts_with('delete')
|
return m.name.starts_with('delete')
|
||||||
}
|
}
|
||||||
|
|
||||||
fn (m ActorMethod) is_list_method() bool {
|
fn (m ActorMethod) is_list_method() bool {
|
||||||
return m.name.starts_with('list')
|
return m.name.starts_with('list')
|
||||||
}
|
}
|
||||||
|
|
||||||
fn (m ActorMethod) is_filter_method() bool {
|
fn (m ActorMethod) is_filter_method() bool {
|
||||||
return m.name.starts_with('filter')
|
return m.name.starts_with('filter')
|
||||||
}
|
}
|
||||||
|
|
||||||
fn (m ActorMethod) is_crudlf_method() bool {
|
fn (m ActorMethod) is_crudlf_method() bool {
|
||||||
return m.is_new_method() ||
|
return m.is_new_method() || m.is_get_method() || m.is_set_method() || m.is_delete_method()
|
||||||
m.is_get_method() ||
|
|| m.is_list_method() || m.is_filter_method()
|
||||||
m.is_set_method() ||
|
|
||||||
m.is_delete_method() ||
|
|
||||||
m.is_list_method() ||
|
|
||||||
m.is_filter_method()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn (o BaseObject) name() string {
|
pub fn (o BaseObject) name() string {
|
||||||
return if o.schema.id.trim_space() != '' {
|
return if o.schema.id.trim_space() != '' {
|
||||||
o.schema.id.trim_space()
|
o.schema.id.trim_space()
|
||||||
} else {o.schema.title.trim_space()}
|
} else {
|
||||||
|
o.schema.title.trim_space()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn (s ActorSpecification) is_base_object_new_method(method ActorMethod) bool {
|
fn (s ActorSpecification) is_base_object_new_method(method ActorMethod) bool {
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
module specification
|
module specification
|
||||||
|
|
||||||
import freeflowuniverse.herolib.schemas.jsonschema { Schema, SchemaRef }
|
import freeflowuniverse.herolib.schemas.jsonschema { Schema, SchemaRef }
|
||||||
import freeflowuniverse.herolib.schemas.openapi { MediaType, ResponseSpec, Operation, Parameter, OpenAPI, Components, Info, PathItem, ServerSpec }
|
import freeflowuniverse.herolib.schemas.openapi { Components, Info, MediaType, OpenAPI, Operation, Parameter, PathItem, ResponseSpec, ServerSpec }
|
||||||
import net.http
|
import net.http
|
||||||
|
|
||||||
// Converts ActorSpecification to OpenAPI
|
// Converts ActorSpecification to OpenAPI
|
||||||
@@ -15,8 +15,14 @@ pub fn (s ActorSpecification) to_openapi() OpenAPI {
|
|||||||
for method in s.methods {
|
for method in s.methods {
|
||||||
op := method.to_openapi_operation()
|
op := method.to_openapi_operation()
|
||||||
paths['${method.http_path()}'] = match method.http_method() {
|
paths['${method.http_path()}'] = match method.http_method() {
|
||||||
.get { PathItem {get: op} }
|
.get {
|
||||||
else { panic('unsupported http method') }
|
PathItem{
|
||||||
|
get: op
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
panic('unsupported http method')
|
||||||
|
}
|
||||||
}
|
}
|
||||||
// Assign operation to corresponding HTTP method
|
// Assign operation to corresponding HTTP method
|
||||||
// TODO: what about other verbs
|
// TODO: what about other verbs
|
||||||
@@ -28,23 +34,23 @@ pub fn (s ActorSpecification) to_openapi() OpenAPI {
|
|||||||
}
|
}
|
||||||
|
|
||||||
return OpenAPI{
|
return OpenAPI{
|
||||||
openapi: '3.0.0',
|
openapi: '3.0.0'
|
||||||
info: Info{
|
info: Info{
|
||||||
title: s.name,
|
title: s.name
|
||||||
summary: s.description,
|
summary: s.description
|
||||||
description: s.description,
|
description: s.description
|
||||||
version: '1.0.0',
|
version: '1.0.0'
|
||||||
},
|
}
|
||||||
servers: [
|
servers: [
|
||||||
ServerSpec{
|
ServerSpec{
|
||||||
url: 'http://localhost:8080',
|
url: 'http://localhost:8080'
|
||||||
description: 'Default server',
|
description: 'Default server'
|
||||||
},
|
},
|
||||||
],
|
]
|
||||||
paths: paths,
|
paths: paths
|
||||||
components: Components{
|
components: Components{
|
||||||
schemas: schemas
|
schemas: schemas
|
||||||
},
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -62,27 +68,27 @@ fn (m ActorMethod) http_method() http.Method {
|
|||||||
|
|
||||||
fn (method ActorMethod) to_openapi_operation() Operation {
|
fn (method ActorMethod) to_openapi_operation() Operation {
|
||||||
mut op := Operation{
|
mut op := Operation{
|
||||||
summary: method.summary,
|
summary: method.summary
|
||||||
description: method.description,
|
description: method.description
|
||||||
operation_id: method.name,
|
operation_id: method.name
|
||||||
}
|
}
|
||||||
|
|
||||||
// Convert parameters to OpenAPI format
|
// Convert parameters to OpenAPI format
|
||||||
for param in method.parameters {
|
for param in method.parameters {
|
||||||
op.parameters << Parameter{
|
op.parameters << Parameter{
|
||||||
name: param.name,
|
name: param.name
|
||||||
in_: 'query', // Default to query parameters; adjust based on function context
|
in_: 'query' // Default to query parameters; adjust based on function context
|
||||||
description: param.description,
|
description: param.description
|
||||||
required: param.required,
|
required: param.required
|
||||||
schema: param.schema,
|
schema: param.schema
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// if method.is_void()
|
// if method.is_void()
|
||||||
op.responses['200'] = ResponseSpec {
|
op.responses['200'] = ResponseSpec{
|
||||||
description: method.description
|
description: method.description
|
||||||
content: {
|
content: {
|
||||||
'application/json': MediaType {
|
'application/json': MediaType{
|
||||||
schema: method.result.schema
|
schema: method.result.schema
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,158 +2,182 @@ module specification
|
|||||||
|
|
||||||
import freeflowuniverse.herolib.core.code
|
import freeflowuniverse.herolib.core.code
|
||||||
import freeflowuniverse.herolib.schemas.jsonschema { Schema, SchemaRef }
|
import freeflowuniverse.herolib.schemas.jsonschema { Schema, SchemaRef }
|
||||||
import freeflowuniverse.herolib.schemas.openapi { Operation, Parameter, OpenAPI, Components, Info, PathItem, ServerSpec }
|
import freeflowuniverse.herolib.schemas.openapi
|
||||||
import freeflowuniverse.herolib.schemas.openrpc
|
import freeflowuniverse.herolib.schemas.openrpc
|
||||||
|
|
||||||
|
const actor_spec = ActorSpecification{
|
||||||
const actor_spec = specification.ActorSpecification{
|
name: 'Petstore'
|
||||||
name: 'Petstore'
|
structure: code.Struct{
|
||||||
structure: code.Struct{
|
is_pub: false
|
||||||
is_pub: false
|
}
|
||||||
}
|
interfaces: [.openrpc]
|
||||||
interfaces: [.openrpc]
|
methods: [
|
||||||
methods: [specification.ActorMethod{
|
ActorMethod{
|
||||||
name: 'list_pets'
|
name: 'list_pets'
|
||||||
summary: 'List all pets'
|
summary: 'List all pets'
|
||||||
parameters: [openrpc.ContentDescriptor{
|
parameters: [
|
||||||
name: 'limit'
|
openrpc.ContentDescriptor{
|
||||||
description: 'How many items to return at one time (max 100)'
|
name: 'limit'
|
||||||
required: false
|
description: 'How many items to return at one time (max 100)'
|
||||||
schema: jsonschema.SchemaRef(jsonschema.Schema{
|
required: false
|
||||||
typ: 'integer'
|
schema: SchemaRef(Schema{
|
||||||
minimum: 1
|
typ: 'integer'
|
||||||
})
|
minimum: 1
|
||||||
}]
|
})
|
||||||
result: openrpc.ContentDescriptor{
|
},
|
||||||
name: 'pets'
|
]
|
||||||
description: 'A paged array of pets'
|
result: openrpc.ContentDescriptor{
|
||||||
schema: jsonschema.SchemaRef(jsonschema.Schema{
|
name: 'pets'
|
||||||
typ: 'array'
|
description: 'A paged array of pets'
|
||||||
items: jsonschema.Items(jsonschema.SchemaRef(jsonschema.Schema{
|
schema: SchemaRef(Schema{
|
||||||
typ: 'object'
|
typ: 'array'
|
||||||
properties: {
|
items: jsonschema.Items(SchemaRef(Schema{
|
||||||
'id': jsonschema.SchemaRef(jsonschema.Reference{
|
typ: 'object'
|
||||||
ref: '#/components/schemas/PetId'
|
properties: {
|
||||||
}),
|
'id': SchemaRef(jsonschema.Reference{
|
||||||
'name': jsonschema.SchemaRef(jsonschema.Schema{
|
ref: '#/components/schemas/PetId'
|
||||||
typ: 'string'
|
})
|
||||||
}),
|
'name': SchemaRef(Schema{
|
||||||
'tag': jsonschema.SchemaRef(jsonschema.Schema{
|
typ: 'string'
|
||||||
typ: 'string'
|
})
|
||||||
})
|
'tag': SchemaRef(Schema{
|
||||||
}
|
typ: 'string'
|
||||||
required: ['id', 'name']
|
})
|
||||||
}))
|
}
|
||||||
})
|
required: [
|
||||||
}
|
'id',
|
||||||
}, specification.ActorMethod{
|
'name',
|
||||||
name: 'create_pet'
|
]
|
||||||
summary: 'Create a pet'
|
}))
|
||||||
parameters: [openrpc.ContentDescriptor{
|
})
|
||||||
name: 'newPetName'
|
}
|
||||||
description: 'Name of pet to create'
|
},
|
||||||
required: true
|
ActorMethod{
|
||||||
schema: jsonschema.SchemaRef(jsonschema.Schema{
|
name: 'create_pet'
|
||||||
typ: 'string'
|
summary: 'Create a pet'
|
||||||
})
|
parameters: [
|
||||||
}, openrpc.ContentDescriptor{
|
openrpc.ContentDescriptor{
|
||||||
name: 'newPetTag'
|
name: 'newPetName'
|
||||||
description: 'Pet tag to create'
|
description: 'Name of pet to create'
|
||||||
schema: jsonschema.SchemaRef(jsonschema.Schema{
|
required: true
|
||||||
typ: 'string'
|
schema: SchemaRef(Schema{
|
||||||
})
|
typ: 'string'
|
||||||
}]
|
})
|
||||||
}, specification.ActorMethod{
|
},
|
||||||
name: 'get_pet'
|
openrpc.ContentDescriptor{
|
||||||
summary: 'Info for a specific pet'
|
name: 'newPetTag'
|
||||||
result: openrpc.ContentDescriptor{
|
description: 'Pet tag to create'
|
||||||
name: 'pet'
|
schema: SchemaRef(Schema{
|
||||||
description: 'Expected response to a valid request'
|
typ: 'string'
|
||||||
schema: jsonschema.SchemaRef(jsonschema.Schema{
|
})
|
||||||
typ: 'object'
|
},
|
||||||
properties: {
|
]
|
||||||
'id': jsonschema.SchemaRef(jsonschema.Reference{
|
},
|
||||||
ref: '#/components/schemas/PetId'
|
ActorMethod{
|
||||||
}),
|
name: 'get_pet'
|
||||||
'name': jsonschema.SchemaRef(jsonschema.Schema{
|
summary: 'Info for a specific pet'
|
||||||
typ: 'string'
|
result: openrpc.ContentDescriptor{
|
||||||
}),
|
name: 'pet'
|
||||||
'tag': jsonschema.SchemaRef(jsonschema.Schema{
|
description: 'Expected response to a valid request'
|
||||||
typ: 'string'
|
schema: SchemaRef(Schema{
|
||||||
})
|
typ: 'object'
|
||||||
}
|
properties: {
|
||||||
required: ['id', 'name']
|
'id': SchemaRef(jsonschema.Reference{
|
||||||
})
|
ref: '#/components/schemas/PetId'
|
||||||
}
|
})
|
||||||
}, specification.ActorMethod{
|
'name': SchemaRef(Schema{
|
||||||
name: 'update_pet'
|
typ: 'string'
|
||||||
summary: 'Update a pet'
|
})
|
||||||
parameters: [openrpc.ContentDescriptor{
|
'tag': SchemaRef(Schema{
|
||||||
name: 'updatedPetName'
|
typ: 'string'
|
||||||
description: 'New name for the pet'
|
})
|
||||||
required: true
|
}
|
||||||
schema: jsonschema.SchemaRef(jsonschema.Schema{
|
required: [
|
||||||
typ: 'string'
|
'id',
|
||||||
})
|
'name',
|
||||||
}, openrpc.ContentDescriptor{
|
]
|
||||||
name: 'updatedPetTag'
|
})
|
||||||
description: 'New tag for the pet'
|
}
|
||||||
schema: jsonschema.SchemaRef(jsonschema.Schema{
|
},
|
||||||
typ: 'string'
|
ActorMethod{
|
||||||
})
|
name: 'update_pet'
|
||||||
}]
|
summary: 'Update a pet'
|
||||||
result: openrpc.ContentDescriptor{
|
parameters: [
|
||||||
name: 'pet'
|
openrpc.ContentDescriptor{
|
||||||
description: 'Updated pet object'
|
name: 'updatedPetName'
|
||||||
schema: jsonschema.SchemaRef(jsonschema.Schema{
|
description: 'New name for the pet'
|
||||||
typ: 'object'
|
required: true
|
||||||
properties: {
|
schema: SchemaRef(Schema{
|
||||||
'id': jsonschema.SchemaRef(jsonschema.Reference{
|
typ: 'string'
|
||||||
ref: '#/components/schemas/PetId'
|
})
|
||||||
}),
|
},
|
||||||
'name': jsonschema.SchemaRef(jsonschema.Schema{
|
openrpc.ContentDescriptor{
|
||||||
typ: 'string'
|
name: 'updatedPetTag'
|
||||||
}),
|
description: 'New tag for the pet'
|
||||||
'tag': jsonschema.SchemaRef(jsonschema.Schema{
|
schema: SchemaRef(Schema{
|
||||||
typ: 'string'
|
typ: 'string'
|
||||||
})
|
})
|
||||||
}
|
},
|
||||||
required: ['id', 'name']
|
]
|
||||||
})
|
result: openrpc.ContentDescriptor{
|
||||||
}
|
name: 'pet'
|
||||||
}, specification.ActorMethod{
|
description: 'Updated pet object'
|
||||||
name: 'delete_pet'
|
schema: SchemaRef(Schema{
|
||||||
summary: 'Delete a pet'
|
typ: 'object'
|
||||||
result: openrpc.ContentDescriptor{
|
properties: {
|
||||||
name: 'success'
|
'id': SchemaRef(jsonschema.Reference{
|
||||||
description: 'Boolean indicating success'
|
ref: '#/components/schemas/PetId'
|
||||||
schema: jsonschema.SchemaRef(jsonschema.Schema{
|
})
|
||||||
typ: 'boolean'
|
'name': SchemaRef(Schema{
|
||||||
})
|
typ: 'string'
|
||||||
}
|
})
|
||||||
}]
|
'tag': SchemaRef(Schema{
|
||||||
objects: [specification.BaseObject{
|
typ: 'string'
|
||||||
schema: jsonschema.Schema{
|
})
|
||||||
id: 'pet'
|
}
|
||||||
title: 'Pet'
|
required: [
|
||||||
typ: 'object'
|
'id',
|
||||||
properties: {
|
'name',
|
||||||
'id': jsonschema.SchemaRef(jsonschema.Reference{
|
]
|
||||||
ref: '#/components/schemas/PetId'
|
})
|
||||||
}),
|
}
|
||||||
'name': jsonschema.SchemaRef(jsonschema.Schema{
|
},
|
||||||
typ: 'string'
|
ActorMethod{
|
||||||
}),
|
name: 'delete_pet'
|
||||||
'tag': jsonschema.SchemaRef(jsonschema.Schema{
|
summary: 'Delete a pet'
|
||||||
typ: 'string'
|
result: openrpc.ContentDescriptor{
|
||||||
})
|
name: 'success'
|
||||||
}
|
description: 'Boolean indicating success'
|
||||||
required: ['id', 'name']
|
schema: SchemaRef(Schema{
|
||||||
}
|
typ: 'boolean'
|
||||||
}]
|
})
|
||||||
|
}
|
||||||
|
},
|
||||||
|
]
|
||||||
|
objects: [
|
||||||
|
BaseObject{
|
||||||
|
schema: Schema{
|
||||||
|
id: 'pet'
|
||||||
|
title: 'Pet'
|
||||||
|
typ: 'object'
|
||||||
|
properties: {
|
||||||
|
'id': SchemaRef(jsonschema.Reference{
|
||||||
|
ref: '#/components/schemas/PetId'
|
||||||
|
})
|
||||||
|
'name': SchemaRef(Schema{
|
||||||
|
typ: 'string'
|
||||||
|
})
|
||||||
|
'tag': SchemaRef(Schema{
|
||||||
|
typ: 'string'
|
||||||
|
})
|
||||||
|
}
|
||||||
|
required: ['id', 'name']
|
||||||
|
}
|
||||||
|
},
|
||||||
|
]
|
||||||
}
|
}
|
||||||
|
|
||||||
// Converts ActorSpecification to OpenAPI
|
// Converts ActorSpecification to OpenAPI
|
||||||
pub fn test_specification_to_openapi() {
|
pub fn test_specification_to_openapi() {
|
||||||
panic(actor_spec.to_openapi())
|
panic(actor_spec.to_openapi())
|
||||||
}
|
}
|
||||||
@@ -1,8 +1,8 @@
|
|||||||
module specification
|
module specification
|
||||||
|
|
||||||
import freeflowuniverse.herolib.schemas.openrpc {OpenRPC, Components}
|
import freeflowuniverse.herolib.schemas.openrpc { Components, OpenRPC }
|
||||||
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
|
||||||
|
|
||||||
// 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
|
||||||
@@ -39,7 +39,6 @@ import freeflowuniverse.herolib.schemas.jsonschema.codegen { struct_to_schema }
|
|||||||
// }
|
// }
|
||||||
// }
|
// }
|
||||||
|
|
||||||
|
|
||||||
pub fn (specification ActorSpecification) to_openrpc() OpenRPC {
|
pub fn (specification ActorSpecification) to_openrpc() OpenRPC {
|
||||||
mut schemas := map[string]SchemaRef{}
|
mut schemas := map[string]SchemaRef{}
|
||||||
for obj in specification.objects {
|
for obj in specification.objects {
|
||||||
@@ -49,11 +48,11 @@ pub fn (specification ActorSpecification) to_openrpc() OpenRPC {
|
|||||||
// }
|
// }
|
||||||
}
|
}
|
||||||
return OpenRPC{
|
return OpenRPC{
|
||||||
info: openrpc.Info{
|
info: openrpc.Info{
|
||||||
title: specification.name.title()
|
title: specification.name.title()
|
||||||
version: '1.0.0'
|
version: '1.0.0'
|
||||||
}
|
}
|
||||||
methods: specification.methods.map(method_to_openrpc_method(it))
|
methods: specification.methods.map(method_to_openrpc_method(it))
|
||||||
components: Components{
|
components: Components{
|
||||||
schemas: schemas
|
schemas: schemas
|
||||||
}
|
}
|
||||||
@@ -61,12 +60,12 @@ pub fn (specification ActorSpecification) to_openrpc() OpenRPC {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub fn method_to_openrpc_method(method ActorMethod) openrpc.Method {
|
pub fn method_to_openrpc_method(method ActorMethod) openrpc.Method {
|
||||||
return openrpc.Method {
|
return openrpc.Method{
|
||||||
name: method.name
|
name: method.name
|
||||||
summary: method.summary
|
summary: method.summary
|
||||||
description: method.description
|
description: method.description
|
||||||
params: method.parameters.map(openrpc.ContentDescriptorRef(it))
|
params: method.parameters.map(openrpc.ContentDescriptorRef(it))
|
||||||
result: openrpc.ContentDescriptorRef(method.result)
|
result: openrpc.ContentDescriptorRef(method.result)
|
||||||
errors: method.errors.map(openrpc.ErrorRef(it))
|
errors: method.errors.map(openrpc.ErrorRef(it))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -7,9 +7,9 @@ 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 // json encoded params
|
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
|
||||||
done bool // if done then no longer need to process
|
done bool // if done then no longer need to process
|
||||||
}
|
}
|
||||||
@@ -18,7 +18,7 @@ pub:
|
|||||||
pub struct ClientConfig {
|
pub struct ClientConfig {
|
||||||
ActorConfig
|
ActorConfig
|
||||||
pub:
|
pub:
|
||||||
redis_url string = 'localhost:6379' // url to redis server running
|
redis_url string = 'localhost:6379' // url to redis server running
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn new_client(config ActorConfig) !Client {
|
pub fn new_client(config ActorConfig) !Client {
|
||||||
@@ -40,7 +40,7 @@ pub fn (mut p Client) call_to_action(action Action, params Params) !Action {
|
|||||||
wait: true
|
wait: true
|
||||||
})!
|
})!
|
||||||
|
|
||||||
return Action {
|
return Action{
|
||||||
...action
|
...action
|
||||||
result: response_data
|
result: response_data
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
module stage
|
module stage
|
||||||
|
|
||||||
import freeflowuniverse.herolib.baobab.osis {OSIS}
|
import freeflowuniverse.herolib.baobab.osis { OSIS }
|
||||||
import freeflowuniverse.herolib.core.redisclient
|
import freeflowuniverse.herolib.core.redisclient
|
||||||
|
|
||||||
@[heap]
|
@[heap]
|
||||||
@@ -19,8 +19,8 @@ mut:
|
|||||||
@[params]
|
@[params]
|
||||||
pub struct ActorConfig {
|
pub struct ActorConfig {
|
||||||
pub:
|
pub:
|
||||||
name string
|
name string
|
||||||
version string
|
version string
|
||||||
redis_url string = 'localhost:6379'
|
redis_url string = 'localhost:6379'
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -35,7 +35,7 @@ pub fn (config ActorConfig) redis_queue_name() string {
|
|||||||
pub fn new_actor(config ActorConfig) !Actor {
|
pub fn new_actor(config ActorConfig) !Actor {
|
||||||
return Actor{
|
return Actor{
|
||||||
ActorConfig: config
|
ActorConfig: config
|
||||||
osis: osis.new()!
|
osis: osis.new()!
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -45,20 +45,22 @@ pub fn (a ActorConfig) get_redis_rpc() !redisclient.RedisRpc {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub fn (a ActorConfig) version(v string) ActorConfig {
|
pub fn (a ActorConfig) version(v string) ActorConfig {
|
||||||
return ActorConfig {...a,
|
return ActorConfig{
|
||||||
|
...a
|
||||||
version: v
|
version: v
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn (a ActorConfig) example() ActorConfig {
|
pub fn (a ActorConfig) example() ActorConfig {
|
||||||
return ActorConfig {...a,
|
return ActorConfig{
|
||||||
|
...a
|
||||||
version: 'example'
|
version: 'example'
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn (mut a IActor) handle(method string, data string) !string {
|
pub fn (mut a IActor) handle(method string, data string) !string {
|
||||||
action := a.act(
|
action := a.act(
|
||||||
name: method
|
name: method
|
||||||
params: data
|
params: data
|
||||||
)!
|
)!
|
||||||
return action.result
|
return action.result
|
||||||
|
|||||||
@@ -1,2 +1 @@
|
|||||||
module stage
|
module stage
|
||||||
|
|
||||||
|
|||||||
@@ -1,12 +1,12 @@
|
|||||||
module interfaces
|
module interfaces
|
||||||
|
|
||||||
import freeflowuniverse.herolib.schemas.jsonrpc
|
import freeflowuniverse.herolib.schemas.jsonrpc
|
||||||
import freeflowuniverse.herolib.baobab.stage {Action}
|
import freeflowuniverse.herolib.baobab.stage { Action }
|
||||||
|
|
||||||
pub fn action_from_jsonrpc_request(request jsonrpc.Request) Action {
|
pub fn action_from_jsonrpc_request(request jsonrpc.Request) Action {
|
||||||
return Action{
|
return Action{
|
||||||
id: request.id
|
id: request.id
|
||||||
name: request.method
|
name: request.method
|
||||||
params: request.params
|
params: request.params
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,8 +1,8 @@
|
|||||||
module interfaces
|
module interfaces
|
||||||
|
|
||||||
import rand
|
import rand
|
||||||
import x.json2 as json {Any}
|
import x.json2 as json { Any }
|
||||||
import freeflowuniverse.herolib.baobab.stage {Action, Client}
|
import freeflowuniverse.herolib.baobab.stage { Action, Client }
|
||||||
import freeflowuniverse.herolib.schemas.jsonrpc
|
import freeflowuniverse.herolib.schemas.jsonrpc
|
||||||
import freeflowuniverse.herolib.schemas.openapi
|
import freeflowuniverse.herolib.schemas.openapi
|
||||||
|
|
||||||
@@ -18,9 +18,7 @@ pub fn new_openapi_interface(client Client) &OpenAPIInterface {
|
|||||||
pub fn (mut i OpenAPIInterface) handle(request openapi.Request) !openapi.Response {
|
pub fn (mut i OpenAPIInterface) handle(request openapi.Request) !openapi.Response {
|
||||||
// Convert incoming OpenAPI request to a procedure call
|
// Convert incoming OpenAPI request to a procedure call
|
||||||
action := action_from_openapi_request(request)
|
action := action_from_openapi_request(request)
|
||||||
response := i.client.call_to_action(action) or {
|
response := i.client.call_to_action(action) or { return err }
|
||||||
return err
|
|
||||||
}
|
|
||||||
return action_to_openapi_response(response)
|
return action_to_openapi_response(response)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -36,15 +34,15 @@ pub fn action_from_openapi_request(request openapi.Request) Action {
|
|||||||
params << json.encode(request.parameters)
|
params << json.encode(request.parameters)
|
||||||
}
|
}
|
||||||
|
|
||||||
return Action {
|
return Action{
|
||||||
id: rand.uuid_v4()
|
id: rand.uuid_v4()
|
||||||
name: request.operation.operation_id
|
name: request.operation.operation_id
|
||||||
params: json.encode(params.str())
|
params: json.encode(params.str())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn action_to_openapi_response(action Action) openapi.Response {
|
pub fn action_to_openapi_response(action Action) openapi.Response {
|
||||||
return openapi.Response {
|
return openapi.Response{
|
||||||
body: action.result
|
body: action.result
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,15 +1,15 @@
|
|||||||
module interfaces
|
module interfaces
|
||||||
|
|
||||||
import freeflowuniverse.herolib.baobab.stage {Client}
|
import freeflowuniverse.herolib.baobab.stage { Client }
|
||||||
import freeflowuniverse.herolib.schemas.jsonrpc
|
import freeflowuniverse.herolib.schemas.jsonrpc
|
||||||
|
|
||||||
// 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{
|
||||||
jsonrpc: request.jsonrpc
|
jsonrpc: request.jsonrpc
|
||||||
id: request.id
|
id: request.id
|
||||||
result: request.params
|
result: request.params
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct OpenRPCInterface {
|
pub struct OpenRPCInterface {
|
||||||
|
|||||||
@@ -5,87 +5,87 @@ module interfaces
|
|||||||
// import veb
|
// import veb
|
||||||
// import x.json2 {Any}
|
// import x.json2 {Any}
|
||||||
// import net.http
|
// import net.http
|
||||||
import freeflowuniverse.herolib.baobab.stage {Action}
|
import freeflowuniverse.herolib.baobab.stage { Action }
|
||||||
import freeflowuniverse.herolib.schemas.openapi {Request}
|
import freeflowuniverse.herolib.schemas.openapi { Request }
|
||||||
|
|
||||||
pub fn openapi_request_to_action(request Request) Action {
|
pub fn openapi_request_to_action(request Request) Action {
|
||||||
// // Convert incoming OpenAPI request to a procedure call
|
// // Convert incoming OpenAPI request to a procedure call
|
||||||
// mut params := []Any{}
|
// mut params := []Any{}
|
||||||
|
|
||||||
// if request.arguments.len > 0 {
|
// if request.arguments.len > 0 {
|
||||||
// params << request.arguments.values().map(it.str()).clone()
|
// params << request.arguments.values().map(it.str()).clone()
|
||||||
// }
|
// }
|
||||||
|
|
||||||
// if request.body != '' {
|
// if request.body != '' {
|
||||||
// params << request.body
|
// params << request.body
|
||||||
// }
|
// }
|
||||||
|
|
||||||
// if request.parameters != '' {
|
// if request.parameters != '' {
|
||||||
// params << request.body
|
// params << request.body
|
||||||
// }
|
// }
|
||||||
|
|
||||||
// if request.parameters.len != 0 {
|
// if request.parameters.len != 0 {
|
||||||
// mut param_map := map[string]Any{} // Store parameters with correct types
|
// mut param_map := map[string]Any{} // Store parameters with correct types
|
||||||
|
|
||||||
// for param_name, param_value in request.parameters {
|
// for param_name, param_value in request.parameters {
|
||||||
// operation_param := request.operation.parameters.filter(it.name == param_name)
|
// operation_param := request.operation.parameters.filter(it.name == param_name)
|
||||||
// if operation_param.len > 0 {
|
// if operation_param.len > 0 {
|
||||||
// param_schema := operation_param[0].schema as Schema
|
// param_schema := operation_param[0].schema as Schema
|
||||||
// param_type := param_schema.typ
|
// param_type := param_schema.typ
|
||||||
// param_format := param_schema.format
|
// param_format := param_schema.format
|
||||||
|
|
||||||
// // Convert parameter value to corresponding type
|
// // Convert parameter value to corresponding type
|
||||||
// match param_type {
|
// match param_type {
|
||||||
// 'integer' {
|
// 'integer' {
|
||||||
// match param_format {
|
// match param_format {
|
||||||
// 'int32' {
|
// 'int32' {
|
||||||
// param_map[param_name] = param_value.int() // Convert to int
|
// param_map[param_name] = param_value.int() // Convert to int
|
||||||
// }
|
// }
|
||||||
// 'int64' {
|
// 'int64' {
|
||||||
// param_map[param_name] = param_value.i64() // Convert to i64
|
// param_map[param_name] = param_value.i64() // Convert to i64
|
||||||
// }
|
// }
|
||||||
// else {
|
// else {
|
||||||
// param_map[param_name] = param_value.int() // Default to int
|
// param_map[param_name] = param_value.int() // Default to int
|
||||||
// }
|
// }
|
||||||
// }
|
// }
|
||||||
// }
|
// }
|
||||||
// 'string' {
|
// 'string' {
|
||||||
// param_map[param_name] = param_value // Already a string
|
// param_map[param_name] = param_value // Already a string
|
||||||
// }
|
// }
|
||||||
// 'boolean' {
|
// 'boolean' {
|
||||||
// param_map[param_name] = param_value.bool() // Convert to bool
|
// param_map[param_name] = param_value.bool() // Convert to bool
|
||||||
// }
|
// }
|
||||||
// 'number' {
|
// 'number' {
|
||||||
// match param_format {
|
// match param_format {
|
||||||
// 'float' {
|
// 'float' {
|
||||||
// param_map[param_name] = param_value.f32() // Convert to float
|
// param_map[param_name] = param_value.f32() // Convert to float
|
||||||
// }
|
// }
|
||||||
// 'double' {
|
// 'double' {
|
||||||
// param_map[param_name] = param_value.f64() // Convert to double
|
// param_map[param_name] = param_value.f64() // Convert to double
|
||||||
// }
|
// }
|
||||||
// else {
|
// else {
|
||||||
// param_map[param_name] = param_value.f64() // Default to double
|
// param_map[param_name] = param_value.f64() // Default to double
|
||||||
// }
|
// }
|
||||||
// }
|
// }
|
||||||
// }
|
// }
|
||||||
// else {
|
// else {
|
||||||
// param_map[param_name] = param_value // Leave as string for unknown types
|
// param_map[param_name] = param_value // Leave as string for unknown types
|
||||||
// }
|
// }
|
||||||
// }
|
// }
|
||||||
// } else {
|
// } else {
|
||||||
// // If the parameter is not defined in the OpenAPI operation, skip or log it
|
// // If the parameter is not defined in the OpenAPI operation, skip or log it
|
||||||
// println('Unknown parameter: $param_name')
|
// println('Unknown parameter: $param_name')
|
||||||
// }
|
// }
|
||||||
// }
|
// }
|
||||||
|
|
||||||
// // Encode the parameter map to JSON if needed
|
// // Encode the parameter map to JSON if needed
|
||||||
// params << json.encode(param_map.str())
|
// params << json.encode(param_map.str())
|
||||||
// }
|
// }
|
||||||
|
|
||||||
// call := Action{
|
// call := Action{
|
||||||
// name: request.operation.operation_id
|
// name: request.operation.operation_id
|
||||||
// params_json: json2.encode(params.str()) // Keep as a string since ProcedureCall expects a string
|
// params_json: json2.encode(params.str()) // Keep as a string since ProcedureCall expects a string
|
||||||
// }
|
// }
|
||||||
// return call
|
// return call
|
||||||
return Action{}
|
return Action{}
|
||||||
}
|
}
|
||||||
@@ -1,7 +1,7 @@
|
|||||||
module interfaces
|
module interfaces
|
||||||
|
|
||||||
import freeflowuniverse.herolib.schemas.openapi { OpenAPI }
|
import freeflowuniverse.herolib.schemas.openapi { OpenAPI }
|
||||||
import freeflowuniverse.herolib.baobab.stage {Client, ClientConfig}
|
import freeflowuniverse.herolib.baobab.stage { ClientConfig }
|
||||||
import freeflowuniverse.herolib.schemas.openrpc { OpenRPC }
|
import freeflowuniverse.herolib.schemas.openrpc { OpenRPC }
|
||||||
import veb
|
import veb
|
||||||
|
|
||||||
|
|||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user