refactor: Update example generation and schema handling

- Remove unused `generate_example_call` and `generate_example_response` functions
- Rename `example_call` to `example_request` in `DocMethod`
- Update schema example extraction to use `schema.example` directly
- Introduce `generate_request_example` and `generate_response_example` for dynamic example generation
- Change type of `id` from string to number in schema examples

PS: The work is still in progress
This commit is contained in:
Mahmoud-Emad
2025-09-22 14:58:22 +03:00
parent bb0b9d2ad9
commit ba48ae255b
8 changed files with 150 additions and 195 deletions

View File

@@ -37,8 +37,8 @@
"description": "The unique identifier of the comment to retrieve.",
"required": true,
"schema": {
"type": "string",
"example": "comment_12345"
"type": "number",
"example": "1"
}
},
{
@@ -57,7 +57,7 @@
"schema": {
"type": "object",
"example": {
"id": "comment_12345",
"id": 1,
"text": "This is a sample comment",
"created_at": "2024-01-15T10:30:00Z"
}

View File

@@ -29,7 +29,7 @@ pub mut:
description string
params []DocParam
result DocParam
example_call string
example_request string
example_response string
endpoint_url string
curl_example string
@@ -105,13 +105,17 @@ pub fn doc_spec_from_openrpc_with_config(openrpc_spec openrpc.OpenRPC, config Do
fn process_method(method openrpc.Method, config DocConfig) !DocMethod {
// Convert parameters
doc_params := process_parameters(method.params)!
example_request := generate_request_example(doc_params)!
// Convert result
doc_result := process_result(method.result)!
example_response := if doc_result.example.len > 0 {
doc_result.example
} else {
generate_response_example(doc_result)!
}
// Generate examples
example_call := generate_example_call(doc_params)
example_response := generate_example_response(doc_result)
// example_call := generate_example_call(doc_params)
doc_method := DocMethod{
name: method.name
@@ -119,13 +123,15 @@ fn process_method(method openrpc.Method, config DocConfig) !DocMethod {
description: method.description
params: doc_params
result: doc_result
endpoint_url: '${config.base_url}/api/${config.handler_type}'
example_call: example_call
example_response: example_response
curl_example: generate_curl_example_jsonrpc(method.name, doc_params, config.base_url,
config.handler_type)
example_request: example_request
}
// endpoint_url: '${config.base_url}/api/${config.handler_type}'
// example_call: example_call
// curl_example: generate_curl_example_jsonrpc(method.name, doc_params, config.base_url,
// config.handler_type)
return doc_method
}
@@ -136,7 +142,7 @@ fn process_parameters(params []openrpc.ContentDescriptorRef) ![]DocParam {
for param in params {
if param is openrpc.ContentDescriptor {
type_info := extract_type_from_schema(param.schema)
example := generate_example_from_schema(param.schema, param.name)
example := extract_example_from_schema(param.schema)
doc_params << DocParam{
name: param.name
@@ -157,14 +163,14 @@ fn process_result(result openrpc.ContentDescriptorRef) !DocParam {
if result is openrpc.ContentDescriptor {
type_info := extract_type_from_schema(result.schema)
example := generate_example_from_schema(result.schema, result.name)
example := extract_example_from_schema(result.schema)
doc_result = DocParam{
name: result.name
description: result.description
type_info: type_info
required: false // Results are never required
example: example
// required: false // Results are never required
example: example
}
}
@@ -201,10 +207,8 @@ fn extract_type_from_schema(schema_ref jsonschema.SchemaRef) string {
return 'unknown'
}
// generate_example_from_schema creates an example value for a parameter or result.
// Uses the jsonschema.Schema.example_value() method with parameter name customization for strings.
// Returns properly formatted JSON values based on the schema type.
fn generate_example_from_schema(schema_ref jsonschema.SchemaRef, param_name string) string {
// extract_example_from_schema extracts the example value from a SchemaRef
fn extract_example_from_schema(schema_ref jsonschema.SchemaRef) string {
schema := match schema_ref {
jsonschema.Schema {
schema_ref
@@ -214,40 +218,22 @@ fn generate_example_from_schema(schema_ref jsonschema.SchemaRef, param_name stri
}
}
// Use the improved example_value() method from jsonschema module
example := schema.example_value()
// For string types without explicit examples, customize with parameter name
if example == '"example_value"' && schema.typ == 'string' && param_name != '' {
return '"example_${param_name}"'
if schema.example.str() != '' {
return schema.example.str()
}
return example
return ''
}
// generate_example_call creates a formatted JSON example for method calls.
// Combines all parameter examples into a properly formatted JSON object.
fn generate_example_call(params []DocParam) string {
if params.len == 0 {
return '{}'
// generate_example_from_schema creates an example value for a parameter or result
fn generate_example_from_schema(schema_ref jsonschema.SchemaRef, param_name string) string {
match schema_ref {
jsonschema.Schema {
return '"example_value"'
}
jsonschema.Reference {
return '"reference_value"'
}
}
mut call_parts := []string{}
for param in params {
call_parts << '"${param.name}": ${param.example}'
}
return '{\n ${call_parts.join(',\n ')}\n}'
}
// generate_example_response creates a formatted JSON example for method responses.
// Wraps the result example in a standard {"result": ...} format.
fn generate_example_response(result DocParam) string {
if result.name == '' {
return '{"result": "success"}'
}
return '{"result": ${result.example}}'
}
// Create authentication documentation info
@@ -290,41 +276,3 @@ fn create_auth_info() AuthDocInfo {
]
}
}
// generate_jsonrpc_example_call creates a complete JSON-RPC request example
fn generate_jsonrpc_example_call(method_name string, params []DocParam) string {
params_obj := if params.len == 0 {
'{}'
} else {
mut call_parts := []string{}
for param in params {
call_parts << '"${param.name}": ${param.example}'
}
'{\n ${call_parts.join(',\n ')}\n }'
}
return '{\n "jsonrpc": "2.0",\n "method": "${method_name}",\n "params": ${params_obj},\n "id": 1\n}'
}
// generate_curl_example_jsonrpc creates a curl command with proper JSON-RPC format
fn generate_curl_example_jsonrpc(method_name string, params []DocParam, base_url string, handler_name string) string {
endpoint := '${base_url}/api/${handler_name}'
jsonrpc_request := generate_jsonrpc_example_call(method_name, params)
mut curl_cmd := 'curl -X POST ${endpoint} \\\n'
curl_cmd += ' -H "Content-Type: application/json" \\\n'
curl_cmd += ' -d \'${jsonrpc_request}\''
return curl_cmd
}
// generate_curl_example creates a curl command for the given method (legacy)
pub fn generate_curl_example(method DocMethod, base_url string, handler_name string) string {
endpoint := '${base_url}/api/${handler_name}'
mut curl_cmd := 'curl -X POST ${endpoint} \\\n'
curl_cmd += ' -H "Content-Type: application/json" \\\n'
curl_cmd += ' -d \'${method.example_call}\''
return curl_cmd
}

View File

@@ -0,0 +1,45 @@
module heroserver
import rand
fn generate_request_example[T](model T) !string {
mut field_parts := []string{} // Build JSON manually to avoid type conflicts
for param in model {
value := match param.type_info.to_lower() {
'string', 'str', 'text' {
'"${rand.string(10)}"'
}
'integer', 'int', 'number' {
'${rand.intn(1000)!}'
}
'boolean', 'bool' {
if rand.intn(2)! == 0 { 'false' } else { 'true' }
}
'array', '[]' {
'[]'
}
'object' {
'{}'
}
else {
// handle generic cases like `[int]`, `[string]`, `map[string]int`, etc.
if param.type_info.starts_with('[') {
'[]'
} else if param.type_info.starts_with('map') {
'{}'
} else {
'"example_value"'
}
}
}
field_parts << '"${param.name}": ${value}'
}
return '{${field_parts.join(', ')}}'
}
fn generate_response_example[T](model T) !string {
println('response model: ${model}')
return 'xxxx'
}

View File

@@ -58,13 +58,12 @@ pub fn (mut server HeroServer) start() ! {
}
// Start VEB server
handler_name := server.handlers.keys()[0]
console.print_item('Server starting on http://${server.host}:${server.port}')
console.print_item('HTML Homepage: http://${server.host}:${server.port}/')
console.print_item('JSON Info: http://${server.host}:${server.port}/json/${handler_name}')
console.print_item('Documentation: http://${server.host}:${server.port}/doc/${handler_name}')
console.print_item('Markdown Docs: http://${server.host}:${server.port}/md/${handler_name}')
console.print_item('API Endpoint: http://${server.host}:${server.port}/api/${handler_name}')
console.print_item('JSON Info: http://${server.host}:${server.port}/json/{handler_name}')
console.print_item('Documentation: http://${server.host}:${server.port}/doc/{handler_name}')
console.print_item('Markdown Docs: http://${server.host}:${server.port}/md/{handler_name}')
console.print_item('API Endpoint: http://${server.host}:${server.port}/api/{handler_name}')
veb.run[HeroServer, Context](mut server, server.port)
}

View File

@@ -283,7 +283,7 @@
<div class="col-md-6">
<h6>Example Request:</h6>
<div class="code-block">
<pre>${method.example_call}</pre>
<pre>${method.example_request}</pre>
</div>
</div>
<div class="col-md-6">
@@ -383,7 +383,7 @@
<div class="col-md-6">
<h6>Example Request:</h6>
<div class="code-block">
<pre>${method.example_call}</pre>
<pre>${method.example_request}</pre>
</div>
</div>
<div class="col-md-6">

View File

@@ -4,30 +4,33 @@ pub fn (schema Schema) type_() string {
return schema.typ.str()
}
// example_value generates a basic example value based on the schema type.
// Returns a JSON-formatted string appropriate for the schema type.
pub fn (schema Schema) example_value() string {
// Check if schema has an explicit example value (ignore empty arrays which indicate no example)
example_str := schema.example.str()
if example_str != '' && example_str != '[]' {
// For object examples, return the JSON string as-is
if schema.typ == 'object' || example_str.starts_with('{') {
return example_str
}
// For string types, ensure proper JSON formatting with quotes
if schema.typ == 'string' && !example_str.starts_with('"') {
return '"${example_str}"'
}
return example_str
}
// // example_value generates a basic example value based on the schema type.
// // Returns a JSON-formatted string appropriate for the schema type.
// pub fn (schema Schema) example_value[T](model T) T {
// obj := T{}
// return obj
// // // Check if schema has an explicit example value (ignore empty arrays which indicate no example)
// // example_str := schema.example.str()
// // println('example_str: ${example_str}')
// // if example_str != '' && example_str != '[]' {
// // // For object examples, return the JSON string as-is
// // if schema.typ == 'object' || example_str.starts_with('{') {
// // return example_str
// // }
// // // For string types, ensure proper JSON formatting with quotes
// // if schema.typ == 'string' && !example_str.starts_with('"') {
// // return '"${example_str}"'
// // }
// // return example_str
// // }
// Generate type-based example when no explicit example is provided
match schema.typ {
'string' { return '"example_value"' }
'integer', 'number' { return '42' }
'boolean' { return 'true' }
'array' { return '[]' }
'object' { return '{}' }
else { return '"example_value"' }
}
}
// // // Generate type-based example when no explicit example is provided
// // match schema.typ {
// // 'string' { return '"example_value"' }
// // 'integer', 'number' { return '42' }
// // 'boolean' { return 'true' }
// // 'array' { return '[]' }
// // 'object' { return '{}' }
// // else { return '"example_value"' }
// // }
// }

View File

@@ -19,7 +19,6 @@ pub fn decode(data string) !Schema {
} else if key == 'items' {
schema.items = decode_items(value)!
} else if key == 'example' {
// Manually handle example field since it's marked with @[json: '-'] in the Schema struct
schema.example = value
}
}

View File

@@ -1,82 +1,43 @@
module openrpc
import json
import x.json2
import freeflowuniverse.herolib.schemas.jsonschema
// In Method struct
pub fn (method Method) example() (string, string) {
// Extract user-provided examples from the OpenRPC specification
// // In the OpenRPC specification struct
// pub fn (spec OpenRPC) methods_by_object() map[string][]Method {
// mut grouped := map[string][]Method{}
mut example_params := map[string]json2.Any{}
for param in method.params {
if param is ContentDescriptor {
param_desc := param as ContentDescriptor
if param_desc.schema is jsonschema.Schema {
schema := param_desc.schema as jsonschema.Schema
if schema.example.str() != '' {
example_params[param_desc.name] = schema.example
}
}
}
}
// for method in spec.methods {
// // Extract root object from method name (e.g., "calendar.create" -> "calendar")
// parts := method.name.split('.')
// root_object := if parts.len > 1 { parts[0] } else { 'general' }
example_call := json.encode(example_params)
// if root_object !in grouped {
// grouped[root_object] = []Method{}
// }
// grouped[root_object] << method
// }
example_response := if method.result is ContentDescriptor {
result_desc := method.result as ContentDescriptor
if result_desc.schema is jsonschema.Schema {
schema := result_desc.schema as jsonschema.Schema
if schema.example.str() != '' {
json.encode(schema.example)
} else {
'{"result": "success"}'
}
} else {
'{"result": "success"}'
}
} else {
'{"result": "success"}'
}
// return grouped
// }
// pub fn (spec OpenRPC) get_object_description(object_name string) string {
// // Return description for object if available
// // Implementation depends on how objects are defined in the spec
// return ''
// }
// pub fn (spec OpenRPC) name_fix() string {
// return spec.info.title.replace(' ', '_').to_lower()
// }
// // In Method struct
// pub fn (method Method) example() (string, string) {
// // Generate example call and response
// // This should create realistic JSON examples based on the method schema
// mut example_params := map[string]string{}
// for param in method.params {
// param_schema_ref := param.schema
// example_params[param.name] = match param_schema_ref {
// openrpc.ContentDescriptor {
// match param_schema_ref.schema {
// jsonschema.Schema {
// param_schema_ref.schema.example_value()
// }
// jsonschema.Reference {
// ''
// }
// }
// }
// jsonschema.Reference {
// ''
// }
// }
// }
// example_call := json.encode(example_params)
// example_response := if method.result is openrpc.ContentDescriptor {
// result_schema_ref := method.result
// match result_schema_ref {
// openrpc.ContentDescriptor {
// match result_schema_ref.schema {
// jsonschema.Schema {
// result_schema_ref.schema.example_value()
// }
// jsonschema.Reference {
// '{"result": "success"}'
// }
// }
// }
// jsonschema.Reference {
// '{"result": "success"}'
// }
// }
// } else {
// '{"result": "success"}'
// }
// return example_call, example_response
// }
return example_call, example_response
}