Merge branch 'development_actions007' of github.com:freeflowuniverse/herolib into development_actions007
This commit is contained in:
@@ -1,10 +1,10 @@
|
||||
module generator
|
||||
|
||||
import freeflowuniverse.herolib.core.code {Folder, File}
|
||||
import freeflowuniverse.herolib.core.code
|
||||
import freeflowuniverse.herolib.core.texttools
|
||||
import freeflowuniverse.herolib.schemas.jsonschema.codegen { schema_to_struct }
|
||||
import freeflowuniverse.herolib.schemas.openrpc.codegen as openrpc_codegen { content_descriptor_to_parameter }
|
||||
import freeflowuniverse.herolib.baobab.specification {ActorSpecification, ActorMethod, BaseObject}
|
||||
import freeflowuniverse.herolib.schemas.jsonschema.codegen
|
||||
import freeflowuniverse.herolib.schemas.openrpc.codegen as openrpc_codegen
|
||||
import freeflowuniverse.herolib.baobab.specification
|
||||
import net.http
|
||||
|
||||
// pub enum BaseObjectMethodType {
|
||||
@@ -50,7 +50,7 @@ import net.http
|
||||
// name_snake := texttools.snake_case(object)
|
||||
// name_pascal := texttools.pascal_case(object)
|
||||
// root := get_endpoint_root(params.endpoint)
|
||||
|
||||
|
||||
// return "async list${name_pascal}(): Promise<${name_pascal}[]> {\n return this.restClient.get<${name_pascal}[]>(`/${root}/${name_snake}`);\n }"
|
||||
// }
|
||||
|
||||
@@ -58,7 +58,7 @@ fn get_endpoint_root(root string) string {
|
||||
return if root == '' {
|
||||
''
|
||||
} else {
|
||||
"/${root.trim('/')}"
|
||||
'/${root.trim('/')}'
|
||||
}
|
||||
}
|
||||
|
||||
@@ -112,7 +112,7 @@ fn get_endpoint_root(root string) string {
|
||||
// .map(content_descriptor_to_parameter(it) or {panic(err)})
|
||||
// .map(it.typescript())
|
||||
// .join(', ')
|
||||
|
||||
|
||||
// return_type := content_descriptor_to_parameter(method.result) or {panic(err)}.typ.typescript()
|
||||
// return 'async ${name}(${params}): Promise<${return_type}> {}'
|
||||
// }
|
||||
// }
|
||||
|
||||
@@ -3,198 +3,201 @@ module generator
|
||||
import x.json2 as json
|
||||
import arrays
|
||||
import freeflowuniverse.herolib.core.code
|
||||
import freeflowuniverse.herolib.baobab.specification
|
||||
import freeflowuniverse.herolib.baobab.specification
|
||||
import freeflowuniverse.herolib.schemas.openrpc
|
||||
import freeflowuniverse.herolib.schemas.jsonschema
|
||||
|
||||
const specification = specification.ActorSpecification{
|
||||
name: 'Pet Store'
|
||||
description: 'A sample API for a pet store'
|
||||
structure: code.Struct{}
|
||||
interfaces: [.openapi]
|
||||
methods: [
|
||||
specification.ActorMethod{
|
||||
name: 'listPets'
|
||||
summary: 'List all pets'
|
||||
example: openrpc.ExamplePairing{
|
||||
params: [
|
||||
openrpc.ExampleRef(openrpc.Example{
|
||||
name: 'Example limit'
|
||||
description: 'Example Maximum number of pets to return'
|
||||
value: 10
|
||||
})
|
||||
]
|
||||
result: openrpc.ExampleRef(openrpc.Example{
|
||||
name: 'Example response'
|
||||
value: json.raw_decode('[
|
||||
name: 'Pet Store'
|
||||
description: 'A sample API for a pet store'
|
||||
structure: code.Struct{}
|
||||
interfaces: [.openapi]
|
||||
methods: [
|
||||
specification.ActorMethod{
|
||||
name: 'listPets'
|
||||
summary: 'List all pets'
|
||||
example: openrpc.ExamplePairing{
|
||||
params: [
|
||||
openrpc.ExampleRef(openrpc.Example{
|
||||
name: 'Example limit'
|
||||
description: 'Example Maximum number of pets to return'
|
||||
value: 10
|
||||
}),
|
||||
]
|
||||
result: openrpc.ExampleRef(openrpc.Example{
|
||||
name: 'Example response'
|
||||
value: json.raw_decode('[
|
||||
{"id": 1, "name": "Fluffy", "tag": "dog"},
|
||||
{"id": 2, "name": "Whiskers", "tag": "cat"}
|
||||
]')!
|
||||
})
|
||||
}
|
||||
parameters: [
|
||||
openrpc.ContentDescriptor{
|
||||
name: 'limit'
|
||||
summary: 'Maximum number of pets to return'
|
||||
description: 'Maximum number of pets to return'
|
||||
required: false
|
||||
schema: jsonschema.SchemaRef(jsonschema.Schema{
|
||||
...jsonschema.schema_u32,
|
||||
example: 10
|
||||
})
|
||||
}
|
||||
]
|
||||
result: openrpc.ContentDescriptor{
|
||||
name: 'pets'
|
||||
description: 'A paged array of pets'
|
||||
schema: jsonschema.SchemaRef(jsonschema.Schema{
|
||||
typ: 'array'
|
||||
items: jsonschema.Items(jsonschema.SchemaRef(jsonschema.Schema{
|
||||
id: 'pet'
|
||||
title: 'Pet'
|
||||
typ: 'object'
|
||||
properties: {
|
||||
'id': jsonschema.SchemaRef(jsonschema.Reference{
|
||||
ref: '#/components/schemas/PetId'
|
||||
}),
|
||||
'name': jsonschema.SchemaRef(jsonschema.Schema{
|
||||
typ: 'string'
|
||||
}),
|
||||
'tag': jsonschema.SchemaRef(jsonschema.Schema{
|
||||
typ: 'string'
|
||||
})
|
||||
}
|
||||
required: ['id', 'name']
|
||||
}))
|
||||
})
|
||||
}
|
||||
errors: [
|
||||
openrpc.ErrorSpec{
|
||||
code: 400
|
||||
message: 'Invalid request'
|
||||
}
|
||||
]
|
||||
},
|
||||
specification.ActorMethod{
|
||||
name: 'createPet'
|
||||
summary: 'Create a new pet'
|
||||
example: openrpc.ExamplePairing{
|
||||
result: openrpc.ExampleRef(openrpc.Example{
|
||||
name: 'Example response'
|
||||
value: '[]'
|
||||
})
|
||||
}
|
||||
result: openrpc.ContentDescriptor{
|
||||
name: 'result'
|
||||
description: 'The response of the operation.'
|
||||
required: true
|
||||
}
|
||||
errors: [
|
||||
openrpc.ErrorSpec{
|
||||
code: 400
|
||||
message: 'Invalid input'
|
||||
}
|
||||
]
|
||||
},
|
||||
specification.ActorMethod{
|
||||
name: 'getPet'
|
||||
summary: 'Get a pet by ID'
|
||||
example: openrpc.ExamplePairing{
|
||||
params: [
|
||||
openrpc.ExampleRef(openrpc.Example{
|
||||
name: 'Example petId'
|
||||
description: 'Example ID of the pet to retrieve'
|
||||
value: 1
|
||||
})
|
||||
]
|
||||
result: openrpc.ExampleRef(openrpc.Example{
|
||||
name: 'Example response'
|
||||
value: json.raw_decode('{"id": 1, "name": "Fluffy", "tag": "dog"}')!
|
||||
})
|
||||
}
|
||||
parameters: [
|
||||
openrpc.ContentDescriptor{
|
||||
name: 'petId'
|
||||
summary: 'ID of the pet to retrieve'
|
||||
description: 'ID of the pet to retrieve'
|
||||
required: true
|
||||
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
|
||||
schema: jsonschema.SchemaRef(jsonschema.Reference{
|
||||
ref: '#/components/schemas/Pet'
|
||||
})
|
||||
}
|
||||
errors: [
|
||||
openrpc.ErrorSpec{
|
||||
code: 404
|
||||
message: 'Pet not found'
|
||||
}
|
||||
]
|
||||
},
|
||||
specification.ActorMethod{
|
||||
name: 'deletePet'
|
||||
summary: 'Delete a pet by ID'
|
||||
example: openrpc.ExamplePairing{
|
||||
params: [
|
||||
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'
|
||||
description: 'ID of the pet to delete'
|
||||
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
|
||||
}
|
||||
errors: [
|
||||
openrpc.ErrorSpec{
|
||||
code: 404
|
||||
message: 'Pet not found'
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
objects: [
|
||||
specification.BaseObject{
|
||||
schema: jsonschema.Schema{
|
||||
title: 'Pet'
|
||||
typ: 'object'
|
||||
properties: {
|
||||
'id': jsonschema.schema_u32,
|
||||
'name': jsonschema.SchemaRef(jsonschema.Schema{
|
||||
typ: 'string'
|
||||
}),
|
||||
'tag': jsonschema.SchemaRef(jsonschema.Schema{
|
||||
typ: 'string'
|
||||
})
|
||||
}
|
||||
required: ['id', 'name']
|
||||
}
|
||||
}
|
||||
]
|
||||
})
|
||||
}
|
||||
parameters: [
|
||||
openrpc.ContentDescriptor{
|
||||
name: 'limit'
|
||||
summary: 'Maximum number of pets to return'
|
||||
description: 'Maximum number of pets to return'
|
||||
required: false
|
||||
schema: jsonschema.SchemaRef(jsonschema.Schema{
|
||||
...jsonschema.schema_u32
|
||||
example: 10
|
||||
})
|
||||
},
|
||||
]
|
||||
result: openrpc.ContentDescriptor{
|
||||
name: 'pets'
|
||||
description: 'A paged array of pets'
|
||||
schema: jsonschema.SchemaRef(jsonschema.Schema{
|
||||
typ: 'array'
|
||||
items: jsonschema.Items(jsonschema.SchemaRef(jsonschema.Schema{
|
||||
id: 'pet'
|
||||
title: 'Pet'
|
||||
typ: 'object'
|
||||
properties: {
|
||||
'id': jsonschema.SchemaRef(jsonschema.Reference{
|
||||
ref: '#/components/schemas/PetId'
|
||||
})
|
||||
'name': jsonschema.SchemaRef(jsonschema.Schema{
|
||||
typ: 'string'
|
||||
})
|
||||
'tag': jsonschema.SchemaRef(jsonschema.Schema{
|
||||
typ: 'string'
|
||||
})
|
||||
}
|
||||
required: [
|
||||
'id',
|
||||
'name',
|
||||
]
|
||||
}))
|
||||
})
|
||||
}
|
||||
errors: [
|
||||
openrpc.ErrorSpec{
|
||||
code: 400
|
||||
message: 'Invalid request'
|
||||
},
|
||||
]
|
||||
},
|
||||
specification.ActorMethod{
|
||||
name: 'createPet'
|
||||
summary: 'Create a new pet'
|
||||
example: openrpc.ExamplePairing{
|
||||
result: openrpc.ExampleRef(openrpc.Example{
|
||||
name: 'Example response'
|
||||
value: '[]'
|
||||
})
|
||||
}
|
||||
result: openrpc.ContentDescriptor{
|
||||
name: 'result'
|
||||
description: 'The response of the operation.'
|
||||
required: true
|
||||
}
|
||||
errors: [
|
||||
openrpc.ErrorSpec{
|
||||
code: 400
|
||||
message: 'Invalid input'
|
||||
},
|
||||
]
|
||||
},
|
||||
specification.ActorMethod{
|
||||
name: 'getPet'
|
||||
summary: 'Get a pet by ID'
|
||||
example: openrpc.ExamplePairing{
|
||||
params: [
|
||||
openrpc.ExampleRef(openrpc.Example{
|
||||
name: 'Example petId'
|
||||
description: 'Example ID of the pet to retrieve'
|
||||
value: 1
|
||||
}),
|
||||
]
|
||||
result: openrpc.ExampleRef(openrpc.Example{
|
||||
name: 'Example response'
|
||||
value: json.raw_decode('{"id": 1, "name": "Fluffy", "tag": "dog"}')!
|
||||
})
|
||||
}
|
||||
parameters: [
|
||||
openrpc.ContentDescriptor{
|
||||
name: 'petId'
|
||||
summary: 'ID of the pet to retrieve'
|
||||
description: 'ID of the pet to retrieve'
|
||||
required: true
|
||||
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
|
||||
schema: jsonschema.SchemaRef(jsonschema.Reference{
|
||||
ref: '#/components/schemas/Pet'
|
||||
})
|
||||
}
|
||||
errors: [
|
||||
openrpc.ErrorSpec{
|
||||
code: 404
|
||||
message: 'Pet not found'
|
||||
},
|
||||
]
|
||||
},
|
||||
specification.ActorMethod{
|
||||
name: 'deletePet'
|
||||
summary: 'Delete a pet by ID'
|
||||
example: openrpc.ExamplePairing{
|
||||
params: [
|
||||
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'
|
||||
description: 'ID of the pet to delete'
|
||||
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
|
||||
}
|
||||
errors: [
|
||||
openrpc.ErrorSpec{
|
||||
code: 404
|
||||
message: 'Pet not found'
|
||||
},
|
||||
]
|
||||
},
|
||||
]
|
||||
objects: [
|
||||
specification.BaseObject{
|
||||
schema: jsonschema.Schema{
|
||||
title: 'Pet'
|
||||
typ: 'object'
|
||||
properties: {
|
||||
'id': jsonschema.schema_u32
|
||||
'name': jsonschema.SchemaRef(jsonschema.Schema{
|
||||
typ: 'string'
|
||||
})
|
||||
'tag': jsonschema.SchemaRef(jsonschema.Schema{
|
||||
typ: 'string'
|
||||
})
|
||||
}
|
||||
required: ['id', 'name']
|
||||
}
|
||||
},
|
||||
]
|
||||
}
|
||||
|
||||
fn test_typescript_client_folder() {
|
||||
|
||||
@@ -1,12 +1,12 @@
|
||||
module generator
|
||||
|
||||
import freeflowuniverse.herolib.baobab.specification {BaseObject}
|
||||
import freeflowuniverse.herolib.core.code { type_from_symbol, VFile, CodeItem, Function, Import, Param, Param, Struct, StructField, Type }
|
||||
import freeflowuniverse.herolib.baobab.specification
|
||||
import freeflowuniverse.herolib.core.code { Param, Param, type_from_symbol }
|
||||
import freeflowuniverse.herolib.core.texttools
|
||||
|
||||
const id_param = Param{
|
||||
name: 'id'
|
||||
typ: type_from_symbol('u32')
|
||||
typ: type_from_symbol('u32')
|
||||
}
|
||||
|
||||
// pub fn generate_object_code(actor Struct, object BaseObject) VFile {
|
||||
@@ -211,8 +211,8 @@ const id_param = Param{
|
||||
// only_mutable: false
|
||||
// )
|
||||
// body := 'return ${object_type}List{items:actor.backend.list[${object_type}]()!}'
|
||||
|
||||
// result_struct := generate_list_result_struct(actor, object)
|
||||
|
||||
// result_struct := generate_list_result_struct(actor, object)
|
||||
// mut result := Param{}
|
||||
// result.typ.symbol = result_struct.name
|
||||
// result.is_result = true
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
module generator
|
||||
|
||||
import freeflowuniverse.herolib.core.code { VFile, CustomCode, Function, Import, Struct }
|
||||
import freeflowuniverse.herolib.baobab.specification {BaseObject}
|
||||
import freeflowuniverse.herolib.core.code
|
||||
import freeflowuniverse.herolib.baobab.specification
|
||||
import rand
|
||||
import freeflowuniverse.herolib.core.texttools
|
||||
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
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.schemas.openrpc {Example, ContentDescriptor}
|
||||
import freeflowuniverse.herolib.schemas.jsonschema.codegen {schemaref_to_type}
|
||||
import freeflowuniverse.herolib.baobab.specification {ActorMethod, ActorSpecification}
|
||||
import freeflowuniverse.herolib.schemas.openrpc { ContentDescriptor, Example }
|
||||
import freeflowuniverse.herolib.schemas.jsonschema.codegen { schemaref_to_type }
|
||||
import freeflowuniverse.herolib.baobab.specification { ActorMethod, ActorSpecification }
|
||||
|
||||
fn generate_handle_file(spec ActorSpecification) !VFile {
|
||||
mut items := []CodeItem{}
|
||||
@@ -12,14 +12,21 @@ fn generate_handle_file(spec ActorSpecification) !VFile {
|
||||
for method in spec.methods {
|
||||
items << generate_method_handle(spec.name, method)!
|
||||
}
|
||||
return VFile {
|
||||
name: 'act'
|
||||
return VFile{
|
||||
name: 'act'
|
||||
imports: [
|
||||
Import{mod:'freeflowuniverse.herolib.baobab.stage' types:['Action']}
|
||||
Import{mod:'freeflowuniverse.herolib.core.texttools'}
|
||||
Import{mod:'x.json2 as json'}
|
||||
Import{
|
||||
mod: 'freeflowuniverse.herolib.baobab.stage'
|
||||
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)!
|
||||
body += '${call_stmt}\n'
|
||||
body += '${generate_return_stmt(method)!}\n'
|
||||
return Function {
|
||||
name: 'handle_${name_fixed}'
|
||||
return Function{
|
||||
name: 'handle_${name_fixed}'
|
||||
description: '// Handler for ${name_fixed}\n'
|
||||
receiver: Param{name: 'actor', mutable: true, typ: Object{'${actor_name_pascal}Actor'}}
|
||||
params: [Param{name: 'action', typ: Object{'Action'}}]
|
||||
result: Param{typ: Result{Object{'Action'}}}
|
||||
body: body
|
||||
receiver: Param{
|
||||
name: 'actor'
|
||||
mutable: true
|
||||
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 {
|
||||
'return Action{...action, result: json.encode(\'${method.example.result.value}\')}'
|
||||
} else {
|
||||
"return action"
|
||||
'return action'
|
||||
}
|
||||
} else { "return action" }
|
||||
return Function {
|
||||
name: 'handle_${name_fixed}_example'
|
||||
} else {
|
||||
'return action'
|
||||
}
|
||||
return Function{
|
||||
name: 'handle_${name_fixed}_example'
|
||||
description: '// Handler for ${name_fixed}\n'
|
||||
receiver: Param{name: 'actor', mutable: true, typ: Object{'${actor_name_pascal}Actor'}}
|
||||
params: [Param{name: 'action', typ: Object{'Action'}}]
|
||||
result: Param{typ: Result{Object{'Action'}}}
|
||||
body: body
|
||||
receiver: Param{
|
||||
name: 'actor'
|
||||
mutable: true
|
||||
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 {
|
||||
mut call_stmt := if schemaref_to_type(method.result.schema).vgen().trim_space() != '' {
|
||||
'${texttools.snake_case(method.result.name)} := '
|
||||
} else {''}
|
||||
} else {
|
||||
''
|
||||
}
|
||||
method_name := texttools.snake_case(method.name)
|
||||
snake_name := texttools.snake_case(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
|
||||
}
|
||||
|
||||
@@ -124,7 +153,7 @@ fn generate_return_stmt(method ActorMethod) !string {
|
||||
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"
|
||||
return 'return action'
|
||||
}
|
||||
|
||||
// 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)
|
||||
if param_type is Object {
|
||||
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})'
|
||||
}
|
||||
param_symbol := param_type.vgen()
|
||||
return if param_symbol == 'string' {
|
||||
'${name}.str()'
|
||||
} else {'${name}.${param_type.vgen()}()'}
|
||||
}
|
||||
} else {
|
||||
'${name}.${param_type.vgen()}()'
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
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.core.texttools
|
||||
import freeflowuniverse.herolib.baobab.specification {ActorMethod, ActorSpecification, ActorInterface}
|
||||
import freeflowuniverse.herolib.baobab.specification { ActorInterface, ActorSpecification }
|
||||
import json
|
||||
|
||||
@[params]
|
||||
@@ -15,7 +15,7 @@ pub:
|
||||
pub fn generate_actor_folder(spec ActorSpecification, params Params) !Folder {
|
||||
mut files := []IFile{}
|
||||
mut folders := []IFolder{}
|
||||
|
||||
|
||||
files = [generate_readme_file(spec)!]
|
||||
|
||||
mut docs_files := []IFile{}
|
||||
@@ -33,7 +33,7 @@ pub fn generate_actor_folder(spec ActorSpecification, params Params) !Folder {
|
||||
// convert actor spec to openrpc spec
|
||||
openapi_spec_raw := spec.to_openapi()
|
||||
spec_files << generate_openapi_file(openapi_spec_raw)!
|
||||
|
||||
|
||||
openapi_spec := openapi.process(openapi_spec_raw)!
|
||||
folders << generate_openapi_ts_client(openapi_spec)!
|
||||
}
|
||||
@@ -41,27 +41,27 @@ pub fn generate_actor_folder(spec ActorSpecification, params Params) !Folder {
|
||||
}
|
||||
}
|
||||
|
||||
specs_folder := Folder {
|
||||
name: 'specs'
|
||||
specs_folder := Folder{
|
||||
name: 'specs'
|
||||
files: spec_files
|
||||
}
|
||||
|
||||
// folder with docs
|
||||
folders << Folder {
|
||||
name: 'docs'
|
||||
files: docs_files
|
||||
folders << Folder{
|
||||
name: 'docs'
|
||||
files: docs_files
|
||||
folders: [specs_folder]
|
||||
}
|
||||
|
||||
folders << generate_scripts_folder(spec.name, false)
|
||||
folders << generate_examples_folder()!
|
||||
|
||||
|
||||
// create module with code files and docs folder
|
||||
name_fixed := texttools.snake_case(spec.name)
|
||||
|
||||
return code.Folder{
|
||||
name: '${name_fixed}'
|
||||
files: files
|
||||
|
||||
return Folder{
|
||||
name: '${name_fixed}'
|
||||
files: files
|
||||
folders: folders
|
||||
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 {
|
||||
return File{
|
||||
name: 'README'
|
||||
name: 'README'
|
||||
extension: 'md'
|
||||
content: '# ${spec.name}\n${spec.description}'
|
||||
content: '# ${spec.name}\n${spec.description}'
|
||||
}
|
||||
}
|
||||
|
||||
pub fn generate_examples_folder() !Folder {
|
||||
return Folder {
|
||||
return Folder{
|
||||
name: 'examples'
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
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.core.texttools
|
||||
import freeflowuniverse.herolib.baobab.specification {ActorMethod, ActorSpecification, ActorInterface}
|
||||
import freeflowuniverse.herolib.baobab.specification { ActorInterface, ActorSpecification }
|
||||
import json
|
||||
|
||||
pub fn generate_module_from_openapi(openapi_path string) !string {
|
||||
@@ -22,17 +22,17 @@ pub fn generate_module_from_openapi(openapi_path string) !string {
|
||||
pub fn generate_actor_module(spec ActorSpecification, params Params) !Module {
|
||||
mut files := []IFile{}
|
||||
mut folders := []IFolder{}
|
||||
|
||||
|
||||
files = [
|
||||
generate_actor_file(spec)!,
|
||||
generate_actor_test_file(spec)!,
|
||||
generate_specs_file(spec.name, params.interfaces)!,
|
||||
generate_handle_file(spec)!,
|
||||
generate_methods_file(spec)!
|
||||
generate_methods_interface_file(spec)!
|
||||
generate_methods_example_file(spec)!
|
||||
generate_client_file(spec)!
|
||||
generate_model_file(spec)!
|
||||
generate_methods_file(spec)!,
|
||||
generate_methods_interface_file(spec)!,
|
||||
generate_methods_example_file(spec)!,
|
||||
generate_client_file(spec)!,
|
||||
generate_model_file(spec)!,
|
||||
]
|
||||
|
||||
// generate code files for supported interfaces
|
||||
@@ -70,15 +70,15 @@ pub fn generate_actor_module(spec ActorSpecification, params Params) !Module {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// create module with code files and docs folder
|
||||
name_fixed := texttools.snake_case(spec.name)
|
||||
return code.new_module(
|
||||
name: '${name_fixed}'
|
||||
name: '${name_fixed}'
|
||||
description: spec.description
|
||||
files: files
|
||||
folders: folders
|
||||
in_src: true
|
||||
files: files
|
||||
folders: folders
|
||||
in_src: true
|
||||
)
|
||||
}
|
||||
|
||||
@@ -88,8 +88,8 @@ fn generate_actor_file(spec ActorSpecification) !VFile {
|
||||
name_snake := texttools.snake_case(spec.name)
|
||||
name_pascal := texttools.pascal_case(spec.name)
|
||||
actor_code := $tmpl('./templates/actor.v.template')
|
||||
return VFile {
|
||||
name: 'actor'
|
||||
return VFile{
|
||||
name: 'actor'
|
||||
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_pascal := texttools.pascal_case(spec.name)
|
||||
actor_test_code := $tmpl('./templates/actor_test.v.template')
|
||||
return VFile {
|
||||
name: 'actor_test'
|
||||
return VFile{
|
||||
name: 'actor_test'
|
||||
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_pascal := texttools.pascal_case(name)
|
||||
actor_code := $tmpl('./templates/specifications.v.template')
|
||||
return VFile {
|
||||
name: 'specifications'
|
||||
return VFile{
|
||||
name: 'specifications'
|
||||
items: [CustomCode{actor_code}]
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5,269 +5,272 @@ import freeflowuniverse.herolib.baobab.specification
|
||||
import freeflowuniverse.herolib.schemas.openrpc
|
||||
import freeflowuniverse.herolib.schemas.jsonschema
|
||||
import os
|
||||
import x.json2 as json {Any}
|
||||
import x.json2 as json
|
||||
|
||||
const actor_spec = specification.ActorSpecification{
|
||||
name: 'Pet Store'
|
||||
description: 'A sample API for a pet store'
|
||||
structure: code.Struct{}
|
||||
interfaces: [.openapi]
|
||||
methods: [
|
||||
specification.ActorMethod{
|
||||
name: 'listPets'
|
||||
summary: 'List all pets'
|
||||
example: openrpc.ExamplePairing{
|
||||
params: [
|
||||
openrpc.ExampleRef(openrpc.Example{
|
||||
name: 'Example limit'
|
||||
description: 'Example Maximum number of pets to return'
|
||||
value: 10
|
||||
})
|
||||
]
|
||||
result: openrpc.ExampleRef(openrpc.Example{
|
||||
name: 'Example response'
|
||||
value: json.raw_decode('[
|
||||
name: 'Pet Store'
|
||||
description: 'A sample API for a pet store'
|
||||
structure: code.Struct{}
|
||||
interfaces: [.openapi]
|
||||
methods: [
|
||||
specification.ActorMethod{
|
||||
name: 'listPets'
|
||||
summary: 'List all pets'
|
||||
example: openrpc.ExamplePairing{
|
||||
params: [
|
||||
openrpc.ExampleRef(openrpc.Example{
|
||||
name: 'Example limit'
|
||||
description: 'Example Maximum number of pets to return'
|
||||
value: 10
|
||||
}),
|
||||
]
|
||||
result: openrpc.ExampleRef(openrpc.Example{
|
||||
name: 'Example response'
|
||||
value: json.raw_decode('[
|
||||
{"id": 1, "name": "Fluffy", "tag": "dog"},
|
||||
{"id": 2, "name": "Whiskers", "tag": "cat"}
|
||||
]')!
|
||||
})
|
||||
}
|
||||
parameters: [
|
||||
openrpc.ContentDescriptor{
|
||||
name: 'limit'
|
||||
summary: 'Maximum number of pets to return'
|
||||
description: 'Maximum number of pets to return'
|
||||
required: false
|
||||
schema: jsonschema.SchemaRef(jsonschema.Schema{
|
||||
...jsonschema.schema_u32,
|
||||
example: 10
|
||||
})
|
||||
}
|
||||
]
|
||||
result: openrpc.ContentDescriptor{
|
||||
name: 'pets'
|
||||
description: 'A paged array of pets'
|
||||
schema: jsonschema.SchemaRef(jsonschema.Schema{
|
||||
typ: 'array'
|
||||
items: jsonschema.Items(jsonschema.SchemaRef(jsonschema.Schema{
|
||||
id: 'pet'
|
||||
title: 'Pet'
|
||||
typ: 'object'
|
||||
properties: {
|
||||
'id': jsonschema.SchemaRef(jsonschema.Reference{
|
||||
ref: '#/components/schemas/PetId'
|
||||
}),
|
||||
'name': jsonschema.SchemaRef(jsonschema.Schema{
|
||||
typ: 'string'
|
||||
}),
|
||||
'tag': jsonschema.SchemaRef(jsonschema.Schema{
|
||||
typ: 'string'
|
||||
})
|
||||
}
|
||||
required: ['id', 'name']
|
||||
}))
|
||||
})
|
||||
}
|
||||
errors: [
|
||||
openrpc.ErrorSpec{
|
||||
code: 400
|
||||
message: 'Invalid request'
|
||||
}
|
||||
]
|
||||
},
|
||||
specification.ActorMethod{
|
||||
name: 'newPet'
|
||||
summary: 'Create a new pet'
|
||||
parameters: [
|
||||
openrpc.ContentDescriptor{
|
||||
name: 'result'
|
||||
description: 'The response of the operation.'
|
||||
required: true
|
||||
schema: jsonschema.SchemaRef(jsonschema.Schema{
|
||||
id: 'pet'
|
||||
title: 'Pet'
|
||||
typ: 'object'
|
||||
properties: {
|
||||
'id': jsonschema.SchemaRef(jsonschema.Reference{
|
||||
ref: '#/components/schemas/PetId'
|
||||
}),
|
||||
'name': jsonschema.SchemaRef(jsonschema.Schema{
|
||||
typ: 'string'
|
||||
}),
|
||||
'tag': jsonschema.SchemaRef(jsonschema.Schema{
|
||||
typ: 'string'
|
||||
})
|
||||
}
|
||||
required: ['id', 'name']
|
||||
})
|
||||
}
|
||||
]
|
||||
example: openrpc.ExamplePairing{
|
||||
result: openrpc.ExampleRef(openrpc.Example{
|
||||
name: 'Example response'
|
||||
value: '[]'
|
||||
})
|
||||
}
|
||||
result: openrpc.ContentDescriptor{
|
||||
name: 'petId'
|
||||
summary: 'ID of the created pet'
|
||||
description: 'ID of the created pet'
|
||||
required: true
|
||||
schema: jsonschema.SchemaRef(jsonschema.Schema{
|
||||
...jsonschema.schema_u32,
|
||||
example: 1
|
||||
})
|
||||
}
|
||||
errors: [
|
||||
openrpc.ErrorSpec{
|
||||
code: 400
|
||||
message: 'Invalid input'
|
||||
}
|
||||
]
|
||||
},
|
||||
specification.ActorMethod{
|
||||
name: 'getPet'
|
||||
summary: 'Get a pet by ID'
|
||||
example: openrpc.ExamplePairing{
|
||||
params: [
|
||||
openrpc.ExampleRef(openrpc.Example{
|
||||
name: 'Example petId'
|
||||
description: 'Example ID of the pet to retrieve'
|
||||
value: 1
|
||||
})
|
||||
]
|
||||
result: openrpc.ExampleRef(openrpc.Example{
|
||||
name: 'Example response'
|
||||
value: json.raw_decode('{"id": 1, "name": "Fluffy", "tag": "dog"}')!
|
||||
})
|
||||
}
|
||||
parameters: [
|
||||
openrpc.ContentDescriptor{
|
||||
name: 'petId'
|
||||
summary: 'ID of the pet to retrieve'
|
||||
description: 'ID of the pet to retrieve'
|
||||
required: true
|
||||
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
|
||||
schema: jsonschema.SchemaRef(jsonschema.Reference{
|
||||
ref: '#/components/schemas/Pet'
|
||||
})
|
||||
}
|
||||
errors: [
|
||||
openrpc.ErrorSpec{
|
||||
code: 404
|
||||
message: 'Pet not found'
|
||||
}
|
||||
]
|
||||
},
|
||||
specification.ActorMethod{
|
||||
name: 'deletePet'
|
||||
summary: 'Delete a pet by ID'
|
||||
example: openrpc.ExamplePairing{
|
||||
params: [
|
||||
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'
|
||||
description: 'ID of the pet to delete'
|
||||
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
|
||||
}
|
||||
errors: [
|
||||
openrpc.ErrorSpec{
|
||||
code: 404
|
||||
message: 'Pet not found'
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
objects: [
|
||||
specification.BaseObject{
|
||||
schema: jsonschema.Schema{
|
||||
title: 'Pet'
|
||||
typ: 'object'
|
||||
properties: {
|
||||
'id': jsonschema.schema_u32,
|
||||
'name': jsonschema.SchemaRef(jsonschema.Schema{
|
||||
typ: 'string'
|
||||
}),
|
||||
'tag': jsonschema.SchemaRef(jsonschema.Schema{
|
||||
typ: 'string'
|
||||
})
|
||||
}
|
||||
required: ['id', 'name']
|
||||
}
|
||||
}
|
||||
]
|
||||
})
|
||||
}
|
||||
parameters: [
|
||||
openrpc.ContentDescriptor{
|
||||
name: 'limit'
|
||||
summary: 'Maximum number of pets to return'
|
||||
description: 'Maximum number of pets to return'
|
||||
required: false
|
||||
schema: jsonschema.SchemaRef(jsonschema.Schema{
|
||||
...jsonschema.schema_u32
|
||||
example: 10
|
||||
})
|
||||
},
|
||||
]
|
||||
result: openrpc.ContentDescriptor{
|
||||
name: 'pets'
|
||||
description: 'A paged array of pets'
|
||||
schema: jsonschema.SchemaRef(jsonschema.Schema{
|
||||
typ: 'array'
|
||||
items: jsonschema.Items(jsonschema.SchemaRef(jsonschema.Schema{
|
||||
id: 'pet'
|
||||
title: 'Pet'
|
||||
typ: 'object'
|
||||
properties: {
|
||||
'id': jsonschema.SchemaRef(jsonschema.Reference{
|
||||
ref: '#/components/schemas/PetId'
|
||||
})
|
||||
'name': jsonschema.SchemaRef(jsonschema.Schema{
|
||||
typ: 'string'
|
||||
})
|
||||
'tag': jsonschema.SchemaRef(jsonschema.Schema{
|
||||
typ: 'string'
|
||||
})
|
||||
}
|
||||
required: [
|
||||
'id',
|
||||
'name',
|
||||
]
|
||||
}))
|
||||
})
|
||||
}
|
||||
errors: [
|
||||
openrpc.ErrorSpec{
|
||||
code: 400
|
||||
message: 'Invalid request'
|
||||
},
|
||||
]
|
||||
},
|
||||
specification.ActorMethod{
|
||||
name: 'newPet'
|
||||
summary: 'Create a new pet'
|
||||
parameters: [
|
||||
openrpc.ContentDescriptor{
|
||||
name: 'result'
|
||||
description: 'The response of the operation.'
|
||||
required: true
|
||||
schema: jsonschema.SchemaRef(jsonschema.Schema{
|
||||
id: 'pet'
|
||||
title: 'Pet'
|
||||
typ: 'object'
|
||||
properties: {
|
||||
'id': jsonschema.SchemaRef(jsonschema.Reference{
|
||||
ref: '#/components/schemas/PetId'
|
||||
})
|
||||
'name': jsonschema.SchemaRef(jsonschema.Schema{
|
||||
typ: 'string'
|
||||
})
|
||||
'tag': jsonschema.SchemaRef(jsonschema.Schema{
|
||||
typ: 'string'
|
||||
})
|
||||
}
|
||||
required: ['id', 'name']
|
||||
})
|
||||
},
|
||||
]
|
||||
example: openrpc.ExamplePairing{
|
||||
result: openrpc.ExampleRef(openrpc.Example{
|
||||
name: 'Example response'
|
||||
value: '[]'
|
||||
})
|
||||
}
|
||||
result: openrpc.ContentDescriptor{
|
||||
name: 'petId'
|
||||
summary: 'ID of the created pet'
|
||||
description: 'ID of the created pet'
|
||||
required: true
|
||||
schema: jsonschema.SchemaRef(jsonschema.Schema{
|
||||
...jsonschema.schema_u32
|
||||
example: 1
|
||||
})
|
||||
}
|
||||
errors: [
|
||||
openrpc.ErrorSpec{
|
||||
code: 400
|
||||
message: 'Invalid input'
|
||||
},
|
||||
]
|
||||
},
|
||||
specification.ActorMethod{
|
||||
name: 'getPet'
|
||||
summary: 'Get a pet by ID'
|
||||
example: openrpc.ExamplePairing{
|
||||
params: [
|
||||
openrpc.ExampleRef(openrpc.Example{
|
||||
name: 'Example petId'
|
||||
description: 'Example ID of the pet to retrieve'
|
||||
value: 1
|
||||
}),
|
||||
]
|
||||
result: openrpc.ExampleRef(openrpc.Example{
|
||||
name: 'Example response'
|
||||
value: json.raw_decode('{"id": 1, "name": "Fluffy", "tag": "dog"}')!
|
||||
})
|
||||
}
|
||||
parameters: [
|
||||
openrpc.ContentDescriptor{
|
||||
name: 'petId'
|
||||
summary: 'ID of the pet to retrieve'
|
||||
description: 'ID of the pet to retrieve'
|
||||
required: true
|
||||
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
|
||||
schema: jsonschema.SchemaRef(jsonschema.Reference{
|
||||
ref: '#/components/schemas/Pet'
|
||||
})
|
||||
}
|
||||
errors: [
|
||||
openrpc.ErrorSpec{
|
||||
code: 404
|
||||
message: 'Pet not found'
|
||||
},
|
||||
]
|
||||
},
|
||||
specification.ActorMethod{
|
||||
name: 'deletePet'
|
||||
summary: 'Delete a pet by ID'
|
||||
example: openrpc.ExamplePairing{
|
||||
params: [
|
||||
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'
|
||||
description: 'ID of the pet to delete'
|
||||
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
|
||||
}
|
||||
errors: [
|
||||
openrpc.ErrorSpec{
|
||||
code: 404
|
||||
message: 'Pet not found'
|
||||
},
|
||||
]
|
||||
},
|
||||
]
|
||||
objects: [
|
||||
specification.BaseObject{
|
||||
schema: jsonschema.Schema{
|
||||
title: 'Pet'
|
||||
typ: 'object'
|
||||
properties: {
|
||||
'id': jsonschema.schema_u32
|
||||
'name': jsonschema.SchemaRef(jsonschema.Schema{
|
||||
typ: 'string'
|
||||
})
|
||||
'tag': jsonschema.SchemaRef(jsonschema.Schema{
|
||||
typ: 'string'
|
||||
})
|
||||
}
|
||||
required: ['id', 'name']
|
||||
}
|
||||
},
|
||||
]
|
||||
}
|
||||
|
||||
const destination = '${os.dir(@FILE)}/testdata'
|
||||
|
||||
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.write(destination,
|
||||
format: true
|
||||
overwrite: true
|
||||
test: true
|
||||
)!
|
||||
actor_module.write(destination,
|
||||
format: true
|
||||
overwrite: true
|
||||
test: true
|
||||
)!
|
||||
}
|
||||
|
||||
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.write(destination,
|
||||
format: true
|
||||
overwrite: true
|
||||
test: true
|
||||
)!
|
||||
actor_module.write(destination,
|
||||
format: true
|
||||
overwrite: true
|
||||
test: true
|
||||
)!
|
||||
}
|
||||
|
||||
fn test_generate_actor_module_with_openapi_interface() {
|
||||
// plain actor module without interfaces
|
||||
actor_module := generate_actor_module(actor_spec,
|
||||
interfaces: [.openapi]
|
||||
)!
|
||||
actor_module.write(destination,
|
||||
format: true
|
||||
overwrite: true
|
||||
test: true
|
||||
)!
|
||||
// plain actor module without interfaces
|
||||
actor_module := generate_actor_module(actor_spec,
|
||||
interfaces: [.openapi]
|
||||
)!
|
||||
actor_module.write(destination,
|
||||
format: true
|
||||
overwrite: true
|
||||
test: true
|
||||
)!
|
||||
}
|
||||
|
||||
fn test_generate_actor_module_with_all_interfaces() {
|
||||
// plain actor module without interfaces
|
||||
actor_module := generate_actor_module(actor_spec,
|
||||
interfaces: [.openapi, .openrpc, .http]
|
||||
)!
|
||||
actor_module.write(destination,
|
||||
format: true
|
||||
overwrite: true
|
||||
test: true
|
||||
)!
|
||||
// plain actor module without interfaces
|
||||
actor_module := generate_actor_module(actor_spec,
|
||||
interfaces: [.openapi, .openrpc, .http]
|
||||
)!
|
||||
actor_module.write(destination,
|
||||
format: true
|
||||
overwrite: true
|
||||
test: true
|
||||
)!
|
||||
}
|
||||
|
||||
@@ -1,18 +1,18 @@
|
||||
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.schemas.jsonschema.codegen as jsonschema_codegen {schemaref_to_type}
|
||||
import freeflowuniverse.herolib.schemas.openrpc.codegen {content_descriptor_to_parameter}
|
||||
import freeflowuniverse.herolib.baobab.specification {ActorMethod, ActorSpecification}
|
||||
import freeflowuniverse.herolib.schemas.jsonschema.codegen as jsonschema_codegen { schemaref_to_type }
|
||||
import freeflowuniverse.herolib.schemas.openrpc.codegen { content_descriptor_to_parameter }
|
||||
import freeflowuniverse.herolib.baobab.specification { ActorMethod, ActorSpecification }
|
||||
|
||||
pub fn generate_client_file(spec ActorSpecification) !VFile {
|
||||
actor_name_snake := texttools.snake_case(spec.name)
|
||||
actor_name_pascal := texttools.pascal_case(spec.name)
|
||||
|
||||
|
||||
mut items := []CodeItem{}
|
||||
|
||||
items << CustomCode {'
|
||||
items << CustomCode{'
|
||||
pub struct Client {
|
||||
stage.Client
|
||||
}
|
||||
@@ -22,12 +22,12 @@ pub fn generate_client_file(spec ActorSpecification) !VFile {
|
||||
Client: stage.new_client(config)!
|
||||
}
|
||||
}'}
|
||||
|
||||
|
||||
for method in spec.methods {
|
||||
items << generate_client_method(method)!
|
||||
}
|
||||
|
||||
return VFile {
|
||||
|
||||
return VFile{
|
||||
imports: [
|
||||
Import{
|
||||
mod: 'freeflowuniverse.herolib.baobab.stage'
|
||||
@@ -36,39 +36,39 @@ pub fn generate_client_file(spec ActorSpecification) !VFile {
|
||||
mod: 'freeflowuniverse.herolib.core.redisclient'
|
||||
},
|
||||
Import{
|
||||
mod: 'x.json2 as json'
|
||||
mod: 'x.json2 as json'
|
||||
types: ['Any']
|
||||
}
|
||||
},
|
||||
]
|
||||
name: 'client_actor'
|
||||
items: items
|
||||
name: 'client_actor'
|
||||
items: items
|
||||
}
|
||||
}
|
||||
|
||||
pub fn generate_example_client_file(spec ActorSpecification) !VFile {
|
||||
actor_name_snake := texttools.snake_case(spec.name)
|
||||
actor_name_pascal := texttools.pascal_case(spec.name)
|
||||
|
||||
|
||||
mut items := []CodeItem{}
|
||||
|
||||
items << CustomCode {'
|
||||
items << CustomCode{"
|
||||
pub struct Client {
|
||||
stage.Client
|
||||
}
|
||||
|
||||
fn new_client() !Client {
|
||||
mut redis := redisclient.new(\'localhost:6379\')!
|
||||
mut rpc_q := redis.rpc_get(\'actor_example_\${name}\')
|
||||
mut redis := redisclient.new('localhost:6379')!
|
||||
mut rpc_q := redis.rpc_get('actor_example_\${name}')
|
||||
return Client{
|
||||
rpc: rpc_q
|
||||
}
|
||||
}'}
|
||||
|
||||
}"}
|
||||
|
||||
for method in spec.methods {
|
||||
items << generate_client_method(method)!
|
||||
}
|
||||
|
||||
return VFile {
|
||||
|
||||
return VFile{
|
||||
imports: [
|
||||
Import{
|
||||
mod: 'freeflowuniverse.herolib.baobab.stage'
|
||||
@@ -77,23 +77,23 @@ pub fn generate_example_client_file(spec ActorSpecification) !VFile {
|
||||
mod: 'freeflowuniverse.herolib.core.redisclient'
|
||||
},
|
||||
Import{
|
||||
mod: 'x.json2 as json'
|
||||
mod: 'x.json2 as json'
|
||||
types: ['Any']
|
||||
}
|
||||
},
|
||||
]
|
||||
name: 'client'
|
||||
items: items
|
||||
name: 'client'
|
||||
items: items
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
pub fn generate_client_method(method ActorMethod) !Function {
|
||||
name_fixed := texttools.snake_case(method.name)
|
||||
|
||||
call_params := if method.parameters.len > 0 {
|
||||
method.parameters.map(texttools.snake_case(it.name)).map('Any(${it}.str())').join(', ')
|
||||
} else {''}
|
||||
} else {
|
||||
''
|
||||
}
|
||||
|
||||
params_stmt := if method.parameters.len == 0 {
|
||||
''
|
||||
@@ -118,16 +118,19 @@ pub fn generate_client_method(method ActorMethod) !Function {
|
||||
result_stmt := if result_type == '' {
|
||||
''
|
||||
} else {
|
||||
"return json.decode[${result_type}](action.result)!"
|
||||
'return json.decode[${result_type}](action.result)!'
|
||||
}
|
||||
result_param := content_descriptor_to_parameter(method.result)!
|
||||
return Function {
|
||||
receiver: code.new_param(v: 'mut client Client')!
|
||||
result: Param{...result_param, typ: Result{result_param.typ}}
|
||||
name: name_fixed
|
||||
body: '${params_stmt}\n${client_call_stmt}\n${result_stmt}'
|
||||
summary: method.summary
|
||||
return Function{
|
||||
receiver: code.new_param(v: 'mut client Client')!
|
||||
result: Param{
|
||||
...result_param
|
||||
typ: Result{result_param.typ}
|
||||
}
|
||||
name: name_fixed
|
||||
body: '${params_stmt}\n${client_call_stmt}\n${result_stmt}'
|
||||
summary: method.summary
|
||||
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
|
||||
|
||||
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.baobab.specification {ActorMethod, ActorSpecification}
|
||||
import freeflowuniverse.herolib.baobab.specification { ActorMethod, ActorSpecification }
|
||||
|
||||
pub fn generate_command_file(spec ActorSpecification) !VFile {
|
||||
mut items := []CodeItem{}
|
||||
@@ -64,7 +64,7 @@ pub fn generate_method_cmd_function(actor_name string, method ActorMethod) strin
|
||||
|
||||
actor_name_snake := texttools.snake_case(actor_name)
|
||||
method_name_snake := texttools.snake_case(method.name)
|
||||
|
||||
|
||||
method_call := if method.result.name == '' {
|
||||
'${actor_name_snake}.${method_name_snake}()!'
|
||||
} else {
|
||||
|
||||
@@ -1,17 +1,17 @@
|
||||
module generator
|
||||
|
||||
import freeflowuniverse.herolib.baobab.specification {ActorInterface}
|
||||
import freeflowuniverse.herolib.core.code { Folder, IFile, VFile, CodeItem, File, Function, Import, Module, Struct, CustomCode }
|
||||
import freeflowuniverse.herolib.baobab.specification { ActorInterface }
|
||||
import freeflowuniverse.herolib.core.code { CustomCode, VFile }
|
||||
|
||||
fn generate_openrpc_interface_files(interfaces []ActorInterface) (VFile, VFile) {
|
||||
http := ActorInterface.http in interfaces
|
||||
|
||||
iface_file := VFile {
|
||||
name: 'interface_openrpc'
|
||||
iface_file := VFile{
|
||||
name: 'interface_openrpc'
|
||||
items: [CustomCode{$tmpl('./templates/interface_openrpc.v.template')}]
|
||||
}
|
||||
iface_test_file := VFile {
|
||||
name: 'interface_openrpc_test'
|
||||
iface_test_file := VFile{
|
||||
name: 'interface_openrpc_test'
|
||||
items: [CustomCode{$tmpl('./templates/interface_openrpc_test.v.template')}]
|
||||
}
|
||||
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) {
|
||||
http := ActorInterface.http in interfaces
|
||||
dollar := '$'
|
||||
iface_file := VFile {
|
||||
name: 'interface_openapi'
|
||||
iface_file := VFile{
|
||||
name: 'interface_openapi'
|
||||
items: [CustomCode{$tmpl('./templates/interface_openapi.v.template')}]
|
||||
}
|
||||
iface_test_file := VFile {
|
||||
name: 'interface_openapi_test'
|
||||
iface_test_file := VFile{
|
||||
name: 'interface_openapi_test'
|
||||
items: [CustomCode{$tmpl('./templates/interface_openapi_test.v.template')}]
|
||||
}
|
||||
return iface_file, iface_test_file
|
||||
@@ -36,12 +36,12 @@ fn generate_http_interface_files(controllers []ActorInterface) (VFile, VFile) {
|
||||
openapi := ActorInterface.openapi in controllers
|
||||
openrpc := ActorInterface.openrpc in controllers
|
||||
|
||||
iface_file := VFile {
|
||||
name: 'interface_http'
|
||||
iface_file := VFile{
|
||||
name: 'interface_http'
|
||||
items: [CustomCode{$tmpl('./templates/interface_http.v.template')}]
|
||||
}
|
||||
iface_test_file := VFile {
|
||||
name: 'interface_http_test'
|
||||
iface_test_file := VFile{
|
||||
name: 'interface_http_test'
|
||||
items: [CustomCode{$tmpl('./templates/interface_http_test.v.template')}]
|
||||
}
|
||||
return iface_file, iface_test_file
|
||||
|
||||
@@ -1,62 +1,80 @@
|
||||
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.schemas.openrpc {ContentDescriptor}
|
||||
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.codegen as jsonschema_codegen {schema_to_struct}
|
||||
import freeflowuniverse.herolib.baobab.specification {ActorMethod, ActorSpecification}
|
||||
|
||||
import freeflowuniverse.herolib.schemas.openrpc
|
||||
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.codegen as jsonschema_codegen
|
||||
import freeflowuniverse.herolib.baobab.specification { ActorMethod, ActorSpecification }
|
||||
|
||||
const crud_prefixes = ['new', 'get', 'set', 'delete', 'list']
|
||||
|
||||
pub fn generate_methods_file(spec ActorSpecification) !VFile {
|
||||
name_snake := texttools.snake_case(spec.name)
|
||||
actor_name_pascal := texttools.pascal_case(spec.name)
|
||||
|
||||
|
||||
receiver := generate_methods_receiver(spec.name)
|
||||
receiver_param := Param {
|
||||
receiver_param := Param{
|
||||
mutable: true
|
||||
name: name_snake[0].ascii_str() // receiver is first letter of domain
|
||||
typ: code.Result{code.Object{receiver.name}}
|
||||
name: name_snake[0].ascii_str() // receiver is first letter of domain
|
||||
typ: Result{code.Object{receiver.name}}
|
||||
}
|
||||
|
||||
mut items := [CodeItem(receiver), CodeItem(generate_core_factory(receiver_param))]
|
||||
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 {
|
||||
name: 'methods'
|
||||
imports: [Import{mod: 'freeflowuniverse.herolib.baobab.osis', types: ['OSIS']}]
|
||||
items: items
|
||||
|
||||
return VFile{
|
||||
name: 'methods'
|
||||
imports: [
|
||||
Import{
|
||||
mod: 'freeflowuniverse.herolib.baobab.osis'
|
||||
types: ['OSIS']
|
||||
},
|
||||
]
|
||||
items: items
|
||||
}
|
||||
}
|
||||
|
||||
fn generate_methods_receiver(name string) code.Struct {
|
||||
return code.Struct {
|
||||
fn generate_methods_receiver(name string) Struct {
|
||||
return Struct{
|
||||
is_pub: true
|
||||
name: '${texttools.pascal_case(name)}'
|
||||
fields: [code.StructField{is_mut: true, name: 'osis', typ:code.Object{'OSIS'}}]
|
||||
name: '${texttools.pascal_case(name)}'
|
||||
fields: [
|
||||
code.StructField{
|
||||
is_mut: true
|
||||
name: 'osis'
|
||||
typ: code.Object{'OSIS'}
|
||||
},
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
fn generate_core_factory(receiver code.Param) code.Function {
|
||||
return code.Function {
|
||||
fn generate_core_factory(receiver Param) Function {
|
||||
return Function{
|
||||
is_pub: true
|
||||
name: 'new_${receiver.typ.symbol()}'
|
||||
body: "return ${receiver.typ.symbol().trim_left('!?')}{osis: osis.new()!}"
|
||||
name: 'new_${receiver.typ.symbol()}'
|
||||
body: 'return ${receiver.typ.symbol().trim_left('!?')}{osis: osis.new()!}'
|
||||
result: receiver
|
||||
}
|
||||
}
|
||||
|
||||
// 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)!
|
||||
|
||||
|
||||
mut method_code := []CodeItem{}
|
||||
// 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] {
|
||||
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_delete { base_object_delete_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)!
|
||||
@@ -81,15 +99,18 @@ pub fn generate_method_code(receiver code.Param, method ActorMethod) ![]CodeItem
|
||||
}
|
||||
|
||||
// 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)!
|
||||
return Function{
|
||||
name: texttools.snake_case(method.name)
|
||||
receiver: receiver
|
||||
result: Param {...result_param, typ: Result{result_param.typ}}
|
||||
summary: method.summary
|
||||
name: texttools.snake_case(method.name)
|
||||
receiver: receiver
|
||||
result: Param{
|
||||
...result_param
|
||||
typ: Result{result_param.typ}
|
||||
}
|
||||
summary: method.summary
|
||||
description: method.description
|
||||
params: method.parameters.map(content_descriptor_to_parameter(it)!)
|
||||
params: method.parameters.map(content_descriptor_to_parameter(it)!)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,60 +1,69 @@
|
||||
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.schemas.openrpc {Example}
|
||||
import freeflowuniverse.herolib.schemas.jsonschema {Schema}
|
||||
import freeflowuniverse.herolib.schemas.jsonschema.codegen as jsonschema_codegen {schema_to_struct}
|
||||
import freeflowuniverse.herolib.schemas.openrpc.codegen {content_descriptor_to_parameter}
|
||||
import freeflowuniverse.herolib.baobab.specification {ActorMethod, ActorSpecification}
|
||||
import freeflowuniverse.herolib.schemas.openrpc { Example }
|
||||
import freeflowuniverse.herolib.schemas.jsonschema
|
||||
import freeflowuniverse.herolib.schemas.jsonschema.codegen as jsonschema_codegen
|
||||
import freeflowuniverse.herolib.schemas.openrpc.codegen { content_descriptor_to_parameter }
|
||||
import freeflowuniverse.herolib.baobab.specification { ActorMethod, ActorSpecification }
|
||||
|
||||
pub fn generate_methods_example_file(spec ActorSpecification) !VFile {
|
||||
name_snake := texttools.snake_case(spec.name)
|
||||
name_pascal := texttools.pascal_case(spec.name)
|
||||
|
||||
|
||||
receiver := generate_example_methods_receiver(spec.name)
|
||||
receiver_param := Param {
|
||||
receiver_param := Param{
|
||||
mutable: true
|
||||
name: name_snake[0].ascii_str()
|
||||
typ: code.Result{code.Object{receiver.name}}
|
||||
name: name_snake[0].ascii_str()
|
||||
typ: Result{code.Object{receiver.name}}
|
||||
}
|
||||
mut items := [CodeItem(receiver), CodeItem(generate_core_example_factory(receiver_param))]
|
||||
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 {
|
||||
name: 'methods_example'
|
||||
|
||||
return VFile{
|
||||
name: 'methods_example'
|
||||
imports: [
|
||||
Import{mod: 'freeflowuniverse.herolib.baobab.osis', types: ['OSIS']},
|
||||
Import{mod: 'x.json2 as json'}
|
||||
Import{
|
||||
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 {
|
||||
return code.Function {
|
||||
fn generate_core_example_factory(receiver Param) Function {
|
||||
return Function{
|
||||
is_pub: true
|
||||
name: 'new_${texttools.snake_case(receiver.typ.symbol())}'
|
||||
body: "return ${receiver.typ.symbol().trim_left('!?')}{OSIS: osis.new()!}"
|
||||
name: 'new_${texttools.snake_case(receiver.typ.symbol())}'
|
||||
body: 'return ${receiver.typ.symbol().trim_left('!?')}{OSIS: osis.new()!}'
|
||||
result: receiver
|
||||
}
|
||||
}
|
||||
|
||||
fn generate_example_methods_receiver(name string) code.Struct {
|
||||
return code.Struct {
|
||||
fn generate_example_methods_receiver(name string) Struct {
|
||||
return Struct{
|
||||
is_pub: true
|
||||
name: '${texttools.pascal_case(name)}Example'
|
||||
embeds: [code.Struct{name:'OSIS'}]
|
||||
name: '${texttools.pascal_case(name)}Example'
|
||||
embeds: [Struct{
|
||||
name: 'OSIS'
|
||||
}]
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// 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)!
|
||||
|
||||
|
||||
mut method_code := []CodeItem{}
|
||||
// TODO: document assumption
|
||||
// obj_params := method.parameters.filter(if it.schema is Schema {it.schema.typ == 'object'} else {false}).map(schema_to_struct(it.schema as Schema))
|
||||
@@ -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
|
||||
// if so generate the method's body
|
||||
body := if !method_is_void(method)! {
|
||||
// if so generate the method's body
|
||||
body := if !method_is_void(method)! {
|
||||
if method.example.result is Example {
|
||||
"json_str := '${method.example.result.value}'
|
||||
return ${generate_decode_stmt('json_str', method.result)!}"
|
||||
return ${generate_decode_stmt('json_str',
|
||||
method.result)!}"
|
||||
} else {
|
||||
"return ${result_param.typ.empty_value()}"
|
||||
'return ${result_param.typ.empty_value()}'
|
||||
}
|
||||
} else {
|
||||
""
|
||||
''
|
||||
}
|
||||
|
||||
fn_prototype := generate_method_prototype(receiver, method)!
|
||||
@@ -81,4 +91,4 @@ pub fn generate_method_example_code(receiver code.Param, method ActorMethod) ![]
|
||||
body: body
|
||||
}
|
||||
return method_code
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,15 +1,20 @@
|
||||
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.schemas.openrpc.codegen {content_descriptor_to_parameter}
|
||||
import freeflowuniverse.herolib.baobab.specification {ActorMethod, ActorSpecification}
|
||||
import freeflowuniverse.herolib.schemas.openrpc.codegen
|
||||
import freeflowuniverse.herolib.baobab.specification { ActorSpecification }
|
||||
|
||||
pub fn generate_methods_interface_file(spec ActorSpecification) !VFile {
|
||||
return VFile {
|
||||
name: 'methods_interface'
|
||||
imports: [Import{mod: 'freeflowuniverse.herolib.baobab.osis', types: ['OSIS']}]
|
||||
items: [code.CodeItem(generate_methods_interface_declaration(spec)!)]
|
||||
return VFile{
|
||||
name: 'methods_interface'
|
||||
imports: [
|
||||
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_pascal := texttools.pascal_case(spec.name)
|
||||
receiver := generate_methods_receiver(spec.name)
|
||||
receiver_param := Param {
|
||||
receiver_param := Param{
|
||||
mutable: true
|
||||
name: name_snake[0].ascii_str()
|
||||
typ: code.Object{receiver.name}
|
||||
name: name_snake[0].ascii_str()
|
||||
typ: code.Object{receiver.name}
|
||||
}
|
||||
return code.Interface {
|
||||
is_pub: true
|
||||
name: 'I${name_pascal}'
|
||||
return code.Interface{
|
||||
is_pub: true
|
||||
name: 'I${name_pascal}'
|
||||
methods: spec.methods.map(generate_method_prototype(receiver_param, it)!)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,19 +1,19 @@
|
||||
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.schemas.jsonschema.codegen {schema_to_struct}
|
||||
import freeflowuniverse.herolib.baobab.specification {ActorMethod, ActorSpecification}
|
||||
import freeflowuniverse.herolib.schemas.jsonschema.codegen { schema_to_struct }
|
||||
import freeflowuniverse.herolib.baobab.specification { ActorSpecification }
|
||||
|
||||
pub fn generate_model_file(spec ActorSpecification) !VFile {
|
||||
actor_name_snake := texttools.snake_case(spec.name)
|
||||
actor_name_pascal := texttools.pascal_case(spec.name)
|
||||
|
||||
return VFile {
|
||||
name: 'model'
|
||||
items: spec.objects.map(CodeItem(
|
||||
Struct {...schema_to_struct(it.schema)
|
||||
is_pub: true
|
||||
}))
|
||||
|
||||
return VFile{
|
||||
name: 'model'
|
||||
items: spec.objects.map(CodeItem(Struct{
|
||||
...schema_to_struct(it.schema)
|
||||
is_pub: true
|
||||
}))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,24 +1,24 @@
|
||||
module generator
|
||||
|
||||
import json
|
||||
import freeflowuniverse.herolib.core.code { VFile, File, Folder, Function, Module, Struct }
|
||||
import freeflowuniverse.herolib.schemas.openapi { Components, OpenAPI, Operation }
|
||||
import freeflowuniverse.herolib.core.code { File, Folder }
|
||||
import freeflowuniverse.herolib.schemas.openapi { OpenAPI, Operation }
|
||||
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
|
||||
|
||||
pub fn generate_openapi_file(specification OpenAPI) !File {
|
||||
openapi_json := specification.encode_json()
|
||||
return File{
|
||||
name: 'openapi'
|
||||
name: 'openapi'
|
||||
extension: 'json'
|
||||
content: openapi_json
|
||||
content: openapi_json
|
||||
}
|
||||
}
|
||||
|
||||
pub fn generate_openapi_ts_client(specification OpenAPI) !Folder {
|
||||
return codegen.ts_client_folder(specification,
|
||||
body_generator: body_generator
|
||||
body_generator: body_generator
|
||||
custom_client_code: ' private restClient: HeroRestClient;
|
||||
|
||||
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 {
|
||||
path := path_.replace('{','\${')
|
||||
fn body_generator(op Operation, path_ string, method http.Method) string {
|
||||
path := path_.replace('{', '\${')
|
||||
return match method {
|
||||
.post {
|
||||
if schema := op.payload_schema() {
|
||||
symbol := schema_to_type(schema).typescript()
|
||||
"return this.restClient.post<${symbol}>('${path}', data);"
|
||||
} else {''}
|
||||
} else {
|
||||
''
|
||||
}
|
||||
}
|
||||
.get {
|
||||
if schema := op.response_schema() {
|
||||
// if op.params.len
|
||||
symbol := schema_to_type(schema).typescript()
|
||||
"return this.restClient.get<${symbol}>('${path}', data);"
|
||||
} else {''}
|
||||
} else {''}
|
||||
} else {
|
||||
''
|
||||
}
|
||||
}
|
||||
else {
|
||||
''
|
||||
}
|
||||
}
|
||||
// return if operation_is_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 {''}
|
||||
}
|
||||
|
||||
|
||||
// pub fn operation_is_base_object_method(op openapi.Operation, base_objs []string) BaseObjectMethod {
|
||||
// // name := texttools.pascal_case(op.operation_id)
|
||||
|
||||
@@ -81,29 +87,25 @@ 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. {
|
||||
// .new { ts_client_new_body(op, path) }
|
||||
// .get { ts_client_get_body(op, path) }
|
||||
// .set { ts_client_set_body(op, path) }
|
||||
// .delete { ts_client_delete_body(op, path) }
|
||||
// .list { ts_client_list_body(op, path) }
|
||||
// else {''}
|
||||
// }
|
||||
// } else {''}
|
||||
// }
|
||||
|
||||
// return if operation_is_base_object_method(op) {
|
||||
// bo_method := operation_to_base_object_method(op)
|
||||
// match bo_method. {
|
||||
// .new { ts_client_new_body(op, path) }
|
||||
// .get { ts_client_get_body(op, path) }
|
||||
// .set { ts_client_set_body(op, path) }
|
||||
// .delete { ts_client_delete_body(op, path) }
|
||||
// .list { ts_client_list_body(op, path) }
|
||||
// else {''}
|
||||
// }
|
||||
// } else {''}
|
||||
// }
|
||||
|
||||
fn get_endpoint(path string) string {
|
||||
return if path == '' {
|
||||
''
|
||||
} else {
|
||||
"/${path.trim('/')}"
|
||||
'/${path.trim('/')}'
|
||||
}
|
||||
}
|
||||
|
||||
@@ -155,4 +157,4 @@ fn get_endpoint(path string) string {
|
||||
// pub:
|
||||
// typ BaseObjectMethodType
|
||||
// object string // the name of the base object
|
||||
// }
|
||||
// }
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
module generator
|
||||
|
||||
import json
|
||||
import freeflowuniverse.herolib.core.code { VFile, File, Function, Module, Struct }
|
||||
import freeflowuniverse.herolib.schemas.openrpc { Components, OpenRPC }
|
||||
import freeflowuniverse.herolib.core.code { File, Function, Struct, VFile }
|
||||
import freeflowuniverse.herolib.schemas.openrpc { OpenRPC }
|
||||
import freeflowuniverse.herolib.schemas.openrpc.codegen { generate_client_file, generate_client_test_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
|
||||
// }
|
||||
client_file := generate_client_file(spec, objects_map)!
|
||||
return VFile {
|
||||
...client_file,
|
||||
return VFile{
|
||||
...client_file
|
||||
name: 'client_openrpc'
|
||||
}
|
||||
}
|
||||
@@ -35,8 +35,8 @@ pub fn generate_openrpc_client_test_file(spec OpenRPC) !VFile {
|
||||
// methods_map[method.func.name] = method.func
|
||||
// }
|
||||
file := generate_client_test_file(spec, methods_map, objects_map)!
|
||||
return VFile {
|
||||
...file,
|
||||
return VFile{
|
||||
...file
|
||||
name: 'client_openrpc_test'
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,13 +1,13 @@
|
||||
module generator
|
||||
|
||||
import freeflowuniverse.herolib.core.code { Folder, File }
|
||||
import freeflowuniverse.herolib.core.code { File, Folder }
|
||||
import freeflowuniverse.herolib.core.texttools
|
||||
|
||||
// generates the folder with runnable scripts of the actor
|
||||
pub fn generate_scripts_folder(name string, example bool) Folder {
|
||||
actor_name := '${texttools.snake_case(name)}_actor'
|
||||
return Folder {
|
||||
name: 'scripts'
|
||||
actor_name := '${texttools.snake_case(name)}_actor'
|
||||
return Folder{
|
||||
name: 'scripts'
|
||||
files: [
|
||||
generate_run_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
|
||||
fn generate_run_script(actor_name string) File {
|
||||
actor_title := texttools.title_case(actor_name)
|
||||
dollar := '$'
|
||||
return File{
|
||||
name: 'run'
|
||||
extension:'sh'
|
||||
content: $tmpl('./templates/run.sh.template')
|
||||
}
|
||||
actor_title := texttools.title_case(actor_name)
|
||||
dollar := '$'
|
||||
return File{
|
||||
name: 'run'
|
||||
extension: 'sh'
|
||||
content: $tmpl('./templates/run.sh.template')
|
||||
}
|
||||
}
|
||||
|
||||
// Function to generate a script for running an actor
|
||||
fn generate_docs_script(actor_name string) File {
|
||||
dollar := '$'
|
||||
return File{
|
||||
name: 'docs'
|
||||
extension:'vsh'
|
||||
content: $tmpl('./templates/docs.vsh.template')
|
||||
}
|
||||
dollar := '$'
|
||||
return File{
|
||||
name: 'docs'
|
||||
extension: 'vsh'
|
||||
content: $tmpl('./templates/docs.vsh.template')
|
||||
}
|
||||
}
|
||||
|
||||
// Function to generate a script for running an actor
|
||||
fn generate_run_actor_script(name string) File {
|
||||
name_snake := texttools.snake_case(name)
|
||||
name_pascal := texttools.pascal_case(name)
|
||||
return File{
|
||||
name: 'run_actor'
|
||||
extension:'vsh'
|
||||
content: $tmpl('./templates/run_actor.vsh.template')
|
||||
}
|
||||
name_snake := texttools.snake_case(name)
|
||||
name_pascal := texttools.pascal_case(name)
|
||||
return File{
|
||||
name: 'run_actor'
|
||||
extension: 'vsh'
|
||||
content: $tmpl('./templates/run_actor.vsh.template')
|
||||
}
|
||||
}
|
||||
|
||||
// Function to generate a script for running an example actor
|
||||
fn generate_run_actor_example_script(name string) File {
|
||||
name_snake := texttools.snake_case(name)
|
||||
name_pascal := texttools.pascal_case(name)
|
||||
return File{
|
||||
name: 'run_actor_example'
|
||||
extension:'vsh'
|
||||
content: $tmpl('./templates/run_actor_example.vsh.template')
|
||||
}
|
||||
name_snake := texttools.snake_case(name)
|
||||
name_pascal := texttools.pascal_case(name)
|
||||
return File{
|
||||
name: 'run_actor_example'
|
||||
extension: 'vsh'
|
||||
content: $tmpl('./templates/run_actor_example.vsh.template')
|
||||
}
|
||||
}
|
||||
|
||||
// Function to generate a script for running an HTTP server
|
||||
fn generate_run_http_server_script(name string) File {
|
||||
port := 8080
|
||||
name_snake := texttools.snake_case(name)
|
||||
return File{
|
||||
name: 'run_http_server'
|
||||
extension:'vsh'
|
||||
content: $tmpl('./templates/run_http_server.vsh.template')
|
||||
}
|
||||
}
|
||||
port := 8080
|
||||
name_snake := texttools.snake_case(name)
|
||||
return File{
|
||||
name: 'run_http_server'
|
||||
extension: 'vsh'
|
||||
content: $tmpl('./templates/run_http_server.vsh.template')
|
||||
}
|
||||
}
|
||||
|
||||
@@ -9,7 +9,7 @@ fn mock_response() ! {
|
||||
mut redis := redisclient.new('localhost:6379')!
|
||||
mut rpc_q := redis.rpc_get('actor_pet_store')
|
||||
for {
|
||||
rpc_q.process(fn(method string, data string)!string{
|
||||
rpc_q.process(fn (method string, data string) !string {
|
||||
return json.encode(method)
|
||||
})!
|
||||
time.sleep(time.millisecond * 100) // Prevent CPU spinning
|
||||
@@ -71,4 +71,4 @@ fn test_create_user() ! {
|
||||
mut client := new_client()!
|
||||
client.create_user()!
|
||||
println('test_create_user passed')
|
||||
}
|
||||
}
|
||||
|
||||
@@ -17,11 +17,11 @@ import os
|
||||
pub fn test_generate_get_method() {
|
||||
generator := ActorGenerator{'test'}
|
||||
actor_struct := code.Struct{
|
||||
name: 'TestActor'
|
||||
name: 'TestActor'
|
||||
fields: [
|
||||
code.StructField{
|
||||
name: 'test_struct_map'
|
||||
typ: code.Type{
|
||||
typ: code.Type{
|
||||
symbol: 'map[string]&TestStruct'
|
||||
}
|
||||
},
|
||||
|
||||
@@ -3,6 +3,6 @@ module osis
|
||||
pub fn new(config OSISConfig) !OSIS {
|
||||
return OSIS{
|
||||
indexer: new_indexer()!
|
||||
storer: new_storer()!
|
||||
storer: new_storer()!
|
||||
}
|
||||
}
|
||||
|
||||
@@ -12,7 +12,7 @@ pub struct Indexer {
|
||||
@[params]
|
||||
pub struct IndexerConfig {
|
||||
db_path string
|
||||
reset bool
|
||||
reset bool
|
||||
}
|
||||
|
||||
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
|
||||
fn (mut backend Indexer) delete_table(object RootObject)! {
|
||||
fn (mut backend Indexer) delete_table(object RootObject) ! {
|
||||
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
|
||||
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 {
|
||||
if field.attrs.contains('index') {
|
||||
value := object.$(field.name)
|
||||
@@ -113,4 +113,4 @@ pub fn get_indices[T](object T) map[string]string {
|
||||
}
|
||||
}
|
||||
return indices
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,14 +3,14 @@ module osis
|
||||
pub struct OSIS {
|
||||
pub mut:
|
||||
indexer Indexer // storing indeces
|
||||
storer Storer
|
||||
storer Storer
|
||||
}
|
||||
|
||||
@[params]
|
||||
pub struct OSISConfig {
|
||||
pub:
|
||||
directory string
|
||||
name string
|
||||
secret string
|
||||
reset bool
|
||||
}
|
||||
name string
|
||||
secret string
|
||||
reset bool
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
module osis
|
||||
|
||||
import freeflowuniverse.herolib.data.ourdb {OurDB}
|
||||
import freeflowuniverse.herolib.data.ourdb { OurDB }
|
||||
import os
|
||||
|
||||
pub struct Storer {
|
||||
@@ -9,7 +9,7 @@ pub mut:
|
||||
}
|
||||
|
||||
pub fn new_storer() !Storer {
|
||||
return Storer {
|
||||
return Storer{
|
||||
db: ourdb.new()!
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -20,4 +20,4 @@ pub fn (mut storer Storer) generic_set[T](obj T) ! {
|
||||
|
||||
pub fn (mut storer Storer) delete(id u32) ! {
|
||||
storer.db.delete(id)!
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,19 +1,19 @@
|
||||
module specification
|
||||
|
||||
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.openapi { Operation, Parameter, OpenAPI, Components, Info, PathItem, ServerSpec, MediaType }
|
||||
import freeflowuniverse.herolib.schemas.openrpc { ExamplePairing, Example, ExampleRef, ContentDescriptor, ErrorSpec }
|
||||
import freeflowuniverse.herolib.schemas.openapi { MediaType, OpenAPI, Parameter }
|
||||
import freeflowuniverse.herolib.schemas.openrpc { ContentDescriptor, ErrorSpec, Example, ExamplePairing, ExampleRef }
|
||||
|
||||
// Helper function: Convert OpenAPI parameter to ContentDescriptor
|
||||
fn openapi_param_to_content_descriptor(param Parameter) ContentDescriptor {
|
||||
return ContentDescriptor{
|
||||
name: param.name,
|
||||
summary: param.description,
|
||||
description: param.description,
|
||||
required: param.required,
|
||||
schema: param.schema
|
||||
name: param.name
|
||||
summary: param.description
|
||||
description: param.description
|
||||
required: param.required
|
||||
schema: param.schema
|
||||
}
|
||||
}
|
||||
|
||||
@@ -22,9 +22,9 @@ fn openapi_param_to_example(param Parameter) ?Example {
|
||||
if param.schema is Schema {
|
||||
if param.schema.example.str() != '' {
|
||||
return Example{
|
||||
name: 'Example ${param.name}',
|
||||
description: 'Example ${param.description}',
|
||||
value: param.schema.example
|
||||
name: 'Example ${param.name}'
|
||||
description: 'Example ${param.description}'
|
||||
value: param.schema.example
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -32,21 +32,25 @@ fn openapi_param_to_example(param Parameter) ?Example {
|
||||
}
|
||||
|
||||
// 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 example_parameters:= []Example{}
|
||||
|
||||
mut example_parameters := []Example{}
|
||||
|
||||
for param in info.operation.parameters {
|
||||
parameters << openapi_param_to_content_descriptor(param)
|
||||
example_parameters << openapi_param_to_example(param) or {
|
||||
continue
|
||||
}
|
||||
example_parameters << openapi_param_to_example(param) or { continue }
|
||||
}
|
||||
|
||||
if schema_ := info.operation.payload_schema() {
|
||||
// TODO: document assumption
|
||||
schema := Schema{...schema_, title: texttools.pascal_case(info.operation.operation_id)}
|
||||
parameters << ContentDescriptor {name: 'data', schema: SchemaRef(schema)}
|
||||
schema := Schema{
|
||||
...schema_
|
||||
title: texttools.pascal_case(info.operation.operation_id)
|
||||
}
|
||||
parameters << ContentDescriptor{
|
||||
name: 'data'
|
||||
schema: SchemaRef(schema)
|
||||
}
|
||||
}
|
||||
|
||||
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.')
|
||||
}
|
||||
response_success := success_responses.values()[0]
|
||||
|
||||
mut result := ContentDescriptor{
|
||||
name: "result",
|
||||
description: "The response of the operation.",
|
||||
required: true,
|
||||
schema: response_success.schema
|
||||
name: 'result'
|
||||
description: 'The response of the operation.'
|
||||
required: true
|
||||
schema: response_success.schema
|
||||
}
|
||||
|
||||
example_result := if response_success.example.str() != '' {
|
||||
Example{
|
||||
name: 'Example response',
|
||||
name: 'Example response'
|
||||
value: response_success.example
|
||||
}
|
||||
} else {Example{}}
|
||||
} else {
|
||||
Example{}
|
||||
}
|
||||
|
||||
pairing := if example_result != Example{} || example_parameters.len > 0 {
|
||||
ExamplePairing{
|
||||
params: example_parameters.map(ExampleRef(it))
|
||||
result: ExampleRef(example_result)
|
||||
}
|
||||
} else {ExamplePairing{}}
|
||||
}
|
||||
} else {
|
||||
ExamplePairing{}
|
||||
}
|
||||
|
||||
mut errors := []ErrorSpec{}
|
||||
for status, response in info.operation.responses {
|
||||
if status.int() >= 400 {
|
||||
error_schema := if response.content.len > 0 {
|
||||
response.content.values()[0].schema
|
||||
} else {Schema{}}
|
||||
} else {
|
||||
Schema{}
|
||||
}
|
||||
errors << ErrorSpec{
|
||||
code: status.int(),
|
||||
message: response.description,
|
||||
data: error_schema, // Extend if error schema is defined
|
||||
code: status.int()
|
||||
message: response.description
|
||||
data: error_schema // Extend if error schema is defined
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return ActorMethod{
|
||||
name: info.operation.operation_id,
|
||||
description: info.operation.description,
|
||||
summary: info.operation.summary,
|
||||
parameters: parameters,
|
||||
example: pairing
|
||||
result: result,
|
||||
errors: errors,
|
||||
name: info.operation.operation_id
|
||||
description: info.operation.description
|
||||
summary: info.operation.summary
|
||||
parameters: parameters
|
||||
example: pairing
|
||||
result: result
|
||||
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 {
|
||||
// Assuming schema properties can be mapped to Struct fields
|
||||
return Struct{
|
||||
name: name,
|
||||
name: name
|
||||
}
|
||||
}
|
||||
|
||||
@@ -122,7 +132,7 @@ pub fn from_openapi(spec_raw OpenAPI) !ActorSpecification {
|
||||
mut objects := []BaseObject{}
|
||||
|
||||
// get all operations for path as list of tuple [](path_string, http.Method, openapi.Operation)
|
||||
|
||||
|
||||
// Extract methods from OpenAPI paths
|
||||
// for path, item in spec.paths {
|
||||
// if item.get.operation_id != '' {
|
||||
@@ -153,16 +163,18 @@ pub fn from_openapi(spec_raw OpenAPI) !ActorSpecification {
|
||||
|
||||
// Extract objects from OpenAPI components.schemas
|
||||
for name, schema in spec.components.schemas {
|
||||
objects << BaseObject{schema: schema as Schema}
|
||||
objects << BaseObject{
|
||||
schema: schema as Schema
|
||||
}
|
||||
}
|
||||
|
||||
return ActorSpecification{
|
||||
openapi: spec_raw
|
||||
name: spec.info.title,
|
||||
description: spec.info.description,
|
||||
structure: Struct{}, // Assuming no top-level structure for this use case
|
||||
interfaces: [.openapi], // Default to OpenAPI for input
|
||||
methods: spec.get_operations().map(openapi_operation_to_actor_method(it)),
|
||||
objects: objects,
|
||||
openapi: spec_raw
|
||||
name: spec.info.title
|
||||
description: spec.info.description
|
||||
structure: Struct{} // Assuming no top-level structure for this use case
|
||||
interfaces: [.openapi] // Default to OpenAPI for input
|
||||
methods: spec.get_operations().map(openapi_operation_to_actor_method(it))
|
||||
objects: objects
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,52 +1,52 @@
|
||||
module specification
|
||||
|
||||
import x.json2 as json {Any}
|
||||
import freeflowuniverse.herolib.core.code { Struct, Function }
|
||||
import x.json2 as json
|
||||
import freeflowuniverse.herolib.core.code { Struct }
|
||||
import freeflowuniverse.herolib.schemas.openrpc { ContentDescriptor, ErrorSpec }
|
||||
import freeflowuniverse.herolib.schemas.openapi { OpenAPI, Info, ServerSpec, Components, Operation, PathItem, PathRef }
|
||||
import freeflowuniverse.herolib.schemas.jsonschema {Schema, Reference, SchemaRef}
|
||||
import freeflowuniverse.herolib.schemas.openapi { Components, Info, OpenAPI, Operation, PathItem, ServerSpec }
|
||||
import freeflowuniverse.herolib.schemas.jsonschema { Reference, Schema, SchemaRef }
|
||||
|
||||
const openapi_spec = openapi.OpenAPI{
|
||||
openapi: '3.0.3'
|
||||
info: openapi.Info{
|
||||
title: 'Pet Store API'
|
||||
const openapi_spec = OpenAPI{
|
||||
openapi: '3.0.3'
|
||||
info: Info{
|
||||
title: 'Pet Store API'
|
||||
description: 'A sample API for a pet store'
|
||||
version: '1.0.0'
|
||||
version: '1.0.0'
|
||||
}
|
||||
servers: [
|
||||
openapi.ServerSpec{
|
||||
url: 'https://api.petstore.example.com/v1'
|
||||
servers: [
|
||||
ServerSpec{
|
||||
url: 'https://api.petstore.example.com/v1'
|
||||
description: 'Production server'
|
||||
},
|
||||
openapi.ServerSpec{
|
||||
url: 'https://staging.petstore.example.com/v1'
|
||||
ServerSpec{
|
||||
url: 'https://staging.petstore.example.com/v1'
|
||||
description: 'Staging server'
|
||||
}
|
||||
},
|
||||
]
|
||||
paths: {
|
||||
'/pets': openapi.PathItem{
|
||||
get: openapi.Operation{
|
||||
summary: 'List all pets'
|
||||
paths: {
|
||||
'/pets': PathItem{
|
||||
get: Operation{
|
||||
summary: 'List all pets'
|
||||
operation_id: 'listPets'
|
||||
parameters: [
|
||||
parameters: [
|
||||
openapi.Parameter{
|
||||
name: 'limit'
|
||||
in_: 'query'
|
||||
name: 'limit'
|
||||
in_: 'query'
|
||||
description: 'Maximum number of pets to return'
|
||||
required: false
|
||||
schema: Schema{
|
||||
typ: 'integer'
|
||||
format: 'int32'
|
||||
required: false
|
||||
schema: Schema{
|
||||
typ: 'integer'
|
||||
format: 'int32'
|
||||
example: 10
|
||||
}
|
||||
}
|
||||
},
|
||||
]
|
||||
responses: {
|
||||
responses: {
|
||||
'200': openapi.ResponseSpec{
|
||||
description: 'A paginated list of pets'
|
||||
content: {
|
||||
content: {
|
||||
'application/json': openapi.MediaType{
|
||||
schema: Reference{
|
||||
schema: Reference{
|
||||
ref: '#/components/schemas/Pets'
|
||||
}
|
||||
example: json.raw_decode('[
|
||||
@@ -61,26 +61,26 @@ const openapi_spec = openapi.OpenAPI{
|
||||
}
|
||||
}
|
||||
}
|
||||
post: openapi.Operation{
|
||||
summary: 'Create a new pet'
|
||||
post: Operation{
|
||||
summary: 'Create a new pet'
|
||||
operation_id: 'createPet'
|
||||
request_body: openapi.RequestBody{
|
||||
required: true
|
||||
content: {
|
||||
content: {
|
||||
'application/json': openapi.MediaType{
|
||||
schema: Reference{
|
||||
schema: Reference{
|
||||
ref: '#/components/schemas/NewPet'
|
||||
}
|
||||
example: json.raw_decode('{ "name": "Bella", "tag": "dog" }')!
|
||||
}
|
||||
}
|
||||
}
|
||||
responses: {
|
||||
responses: {
|
||||
'201': openapi.ResponseSpec{
|
||||
description: 'Pet created'
|
||||
content: {
|
||||
content: {
|
||||
'application/json': openapi.MediaType{
|
||||
schema: Reference{
|
||||
schema: Reference{
|
||||
ref: '#/components/schemas/Pet'
|
||||
}
|
||||
example: json.raw_decode('{ "id": 3, "name": "Bella", "tag": "dog" }')!
|
||||
@@ -93,29 +93,29 @@ const openapi_spec = openapi.OpenAPI{
|
||||
}
|
||||
}
|
||||
}
|
||||
'/pets/{petId}': openapi.PathItem{
|
||||
get: openapi.Operation{
|
||||
summary: 'Get a pet by ID'
|
||||
'/pets/{petId}': PathItem{
|
||||
get: Operation{
|
||||
summary: 'Get a pet by ID'
|
||||
operation_id: 'getPet'
|
||||
parameters: [
|
||||
parameters: [
|
||||
openapi.Parameter{
|
||||
name: 'petId'
|
||||
in_: 'path'
|
||||
name: 'petId'
|
||||
in_: 'path'
|
||||
description: 'ID of the pet to retrieve'
|
||||
required: true
|
||||
schema: Schema{
|
||||
typ: 'integer'
|
||||
format: 'int64'
|
||||
required: true
|
||||
schema: Schema{
|
||||
typ: 'integer'
|
||||
format: 'int64'
|
||||
example: 1
|
||||
}
|
||||
}
|
||||
},
|
||||
]
|
||||
responses: {
|
||||
responses: {
|
||||
'200': openapi.ResponseSpec{
|
||||
description: 'A pet'
|
||||
content: {
|
||||
content: {
|
||||
'application/json': openapi.MediaType{
|
||||
schema: Reference{
|
||||
schema: Reference{
|
||||
ref: '#/components/schemas/Pet'
|
||||
}
|
||||
example: json.raw_decode('{ "id": 1, "name": "Fluffy", "tag": "dog" }')!
|
||||
@@ -127,23 +127,23 @@ const openapi_spec = openapi.OpenAPI{
|
||||
}
|
||||
}
|
||||
}
|
||||
delete: openapi.Operation{
|
||||
summary: 'Delete a pet by ID'
|
||||
delete: Operation{
|
||||
summary: 'Delete a pet by ID'
|
||||
operation_id: 'deletePet'
|
||||
parameters: [
|
||||
parameters: [
|
||||
openapi.Parameter{
|
||||
name: 'petId'
|
||||
in_: 'path'
|
||||
name: 'petId'
|
||||
in_: 'path'
|
||||
description: 'ID of the pet to delete'
|
||||
required: true
|
||||
schema: Schema{
|
||||
typ: 'integer'
|
||||
format: 'int64'
|
||||
required: true
|
||||
schema: Schema{
|
||||
typ: 'integer'
|
||||
format: 'int64'
|
||||
example: 1
|
||||
}
|
||||
}
|
||||
},
|
||||
]
|
||||
responses: {
|
||||
responses: {
|
||||
'204': openapi.ResponseSpec{
|
||||
description: 'Pet deleted'
|
||||
}
|
||||
@@ -154,38 +154,38 @@ const openapi_spec = openapi.OpenAPI{
|
||||
}
|
||||
}
|
||||
}
|
||||
components: openapi.Components{
|
||||
components: Components{
|
||||
schemas: {
|
||||
'Pet': SchemaRef(Schema{
|
||||
typ: 'object'
|
||||
required: ['id', 'name']
|
||||
'Pet': SchemaRef(Schema{
|
||||
typ: 'object'
|
||||
required: ['id', 'name']
|
||||
properties: {
|
||||
'id': SchemaRef(Schema{
|
||||
typ: 'integer'
|
||||
'id': SchemaRef(Schema{
|
||||
typ: 'integer'
|
||||
format: 'int64'
|
||||
})
|
||||
'name': SchemaRef(Schema{
|
||||
typ: 'string'
|
||||
})
|
||||
'tag': SchemaRef(Schema{
|
||||
'tag': SchemaRef(Schema{
|
||||
typ: 'string'
|
||||
})
|
||||
}
|
||||
})
|
||||
'NewPet': SchemaRef(Schema{
|
||||
typ: 'object'
|
||||
required: ['name']
|
||||
typ: 'object'
|
||||
required: ['name']
|
||||
properties: {
|
||||
'name': SchemaRef(Schema{
|
||||
typ: 'string'
|
||||
})
|
||||
'tag': SchemaRef(Schema{
|
||||
'tag': SchemaRef(Schema{
|
||||
typ: 'string'
|
||||
})
|
||||
}
|
||||
})
|
||||
'Pets': SchemaRef(Schema{
|
||||
typ: 'array'
|
||||
'Pets': SchemaRef(Schema{
|
||||
typ: 'array'
|
||||
items: SchemaRef(Reference{
|
||||
ref: '#/components/schemas/Pet'
|
||||
})
|
||||
@@ -194,207 +194,207 @@ const openapi_spec = openapi.OpenAPI{
|
||||
}
|
||||
}
|
||||
|
||||
const actor_spec = specification.ActorSpecification{
|
||||
name: 'Pet Store API'
|
||||
description: 'A sample API for a pet store'
|
||||
structure: code.Struct{}
|
||||
interfaces: [.openapi]
|
||||
methods: [
|
||||
specification.ActorMethod{
|
||||
name: 'listPets'
|
||||
summary: 'List all pets'
|
||||
example: openrpc.ExamplePairing{
|
||||
params: [
|
||||
openrpc.ExampleRef(openrpc.Example{
|
||||
name: 'Example limit'
|
||||
description: 'Example Maximum number of pets to return'
|
||||
value: 10
|
||||
})
|
||||
]
|
||||
result: openrpc.ExampleRef(openrpc.Example{
|
||||
name: 'Example response'
|
||||
value: json.raw_decode('[
|
||||
const actor_spec = ActorSpecification{
|
||||
name: 'Pet Store API'
|
||||
description: 'A sample API for a pet store'
|
||||
structure: Struct{}
|
||||
interfaces: [.openapi]
|
||||
methods: [
|
||||
ActorMethod{
|
||||
name: 'listPets'
|
||||
summary: 'List all pets'
|
||||
example: openrpc.ExamplePairing{
|
||||
params: [
|
||||
openrpc.ExampleRef(openrpc.Example{
|
||||
name: 'Example limit'
|
||||
description: 'Example Maximum number of pets to return'
|
||||
value: 10
|
||||
}),
|
||||
]
|
||||
result: openrpc.ExampleRef(openrpc.Example{
|
||||
name: 'Example response'
|
||||
value: json.raw_decode('[
|
||||
{"id": 1, "name": "Fluffy", "tag": "dog"},
|
||||
{"id": 2, "name": "Whiskers", "tag": "cat"}
|
||||
]')!
|
||||
})
|
||||
}
|
||||
parameters: [
|
||||
openrpc.ContentDescriptor{
|
||||
name: 'limit'
|
||||
summary: 'Maximum number of pets to return'
|
||||
description: 'Maximum number of pets to return'
|
||||
required: false
|
||||
schema: jsonschema.SchemaRef(jsonschema.Schema{
|
||||
typ: 'integer'
|
||||
format: 'int32'
|
||||
example: 10
|
||||
})
|
||||
}
|
||||
]
|
||||
result: openrpc.ContentDescriptor{
|
||||
name: 'result'
|
||||
description: 'The response of the operation.'
|
||||
required: true
|
||||
schema: jsonschema.SchemaRef(jsonschema.Reference{
|
||||
ref: '#/components/schemas/Pets'
|
||||
})
|
||||
}
|
||||
errors: [
|
||||
openrpc.ErrorSpec{
|
||||
code: 400
|
||||
message: 'Invalid request'
|
||||
}
|
||||
]
|
||||
},
|
||||
specification.ActorMethod{
|
||||
name: 'createPet'
|
||||
summary: 'Create a new pet'
|
||||
example: openrpc.ExamplePairing{
|
||||
result: openrpc.ExampleRef(openrpc.Example{
|
||||
name: 'Example response'
|
||||
value: '[]'
|
||||
})
|
||||
}
|
||||
result: openrpc.ContentDescriptor{
|
||||
name: 'result'
|
||||
description: 'The response of the operation.'
|
||||
required: true
|
||||
}
|
||||
errors: [
|
||||
openrpc.ErrorSpec{
|
||||
code: 400
|
||||
message: 'Invalid input'
|
||||
}
|
||||
]
|
||||
},
|
||||
specification.ActorMethod{
|
||||
name: 'getPet'
|
||||
summary: 'Get a pet by ID'
|
||||
example: openrpc.ExamplePairing{
|
||||
params: [
|
||||
openrpc.ExampleRef(openrpc.Example{
|
||||
name: 'Example petId'
|
||||
description: 'Example ID of the pet to retrieve'
|
||||
value: 1
|
||||
})
|
||||
]
|
||||
result: openrpc.ExampleRef(openrpc.Example{
|
||||
name: 'Example response'
|
||||
value: json.raw_decode('{"id": 1, "name": "Fluffy", "tag": "dog"}')!
|
||||
})
|
||||
}
|
||||
parameters: [
|
||||
openrpc.ContentDescriptor{
|
||||
name: 'petId'
|
||||
summary: 'ID of the pet to retrieve'
|
||||
description: 'ID of the pet to retrieve'
|
||||
required: true
|
||||
schema: jsonschema.SchemaRef(jsonschema.Schema{
|
||||
typ: 'integer'
|
||||
format: 'int64'
|
||||
example: 1
|
||||
})
|
||||
}
|
||||
]
|
||||
result: openrpc.ContentDescriptor{
|
||||
name: 'result'
|
||||
description: 'The response of the operation.'
|
||||
required: true
|
||||
schema: jsonschema.SchemaRef(jsonschema.Reference{
|
||||
ref: '#/components/schemas/Pet'
|
||||
})
|
||||
}
|
||||
errors: [
|
||||
openrpc.ErrorSpec{
|
||||
code: 404
|
||||
message: 'Pet not found'
|
||||
}
|
||||
]
|
||||
},
|
||||
specification.ActorMethod{
|
||||
name: 'deletePet'
|
||||
summary: 'Delete a pet by ID'
|
||||
example: openrpc.ExamplePairing{
|
||||
params: [
|
||||
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'
|
||||
description: 'ID of the pet to delete'
|
||||
required: true
|
||||
schema: jsonschema.SchemaRef(jsonschema.Schema{
|
||||
typ: 'integer'
|
||||
format: 'int64'
|
||||
example: 1
|
||||
})
|
||||
}
|
||||
]
|
||||
result: openrpc.ContentDescriptor{
|
||||
name: 'result'
|
||||
description: 'The response of the operation.'
|
||||
required: true
|
||||
}
|
||||
errors: [
|
||||
openrpc.ErrorSpec{
|
||||
code: 404
|
||||
message: 'Pet not found'
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
objects: [
|
||||
specification.BaseObject{
|
||||
schema: jsonschema.Schema{
|
||||
typ: 'object'
|
||||
properties: {
|
||||
'id': jsonschema.SchemaRef(jsonschema.Schema{
|
||||
typ: 'integer'
|
||||
format: 'int64'
|
||||
}),
|
||||
'name': jsonschema.SchemaRef(jsonschema.Schema{
|
||||
typ: 'string'
|
||||
}),
|
||||
'tag': jsonschema.SchemaRef(jsonschema.Schema{
|
||||
typ: 'string'
|
||||
})
|
||||
}
|
||||
required: ['id', 'name']
|
||||
}
|
||||
},
|
||||
specification.BaseObject{
|
||||
schema: jsonschema.Schema{
|
||||
typ: 'object'
|
||||
properties: {
|
||||
'name': jsonschema.SchemaRef(jsonschema.Schema{
|
||||
typ: 'string'
|
||||
}),
|
||||
'tag': jsonschema.SchemaRef(jsonschema.Schema{
|
||||
typ: 'string'
|
||||
})
|
||||
}
|
||||
required: ['name']
|
||||
}
|
||||
},
|
||||
specification.BaseObject{
|
||||
schema: jsonschema.Schema{
|
||||
typ: 'array'
|
||||
items: jsonschema.Items(jsonschema.SchemaRef(jsonschema.Reference{
|
||||
ref: '#/components/schemas/Pet'
|
||||
}))
|
||||
}
|
||||
}
|
||||
]
|
||||
})
|
||||
}
|
||||
parameters: [
|
||||
ContentDescriptor{
|
||||
name: 'limit'
|
||||
summary: 'Maximum number of pets to return'
|
||||
description: 'Maximum number of pets to return'
|
||||
required: false
|
||||
schema: SchemaRef(Schema{
|
||||
typ: 'integer'
|
||||
format: 'int32'
|
||||
example: 10
|
||||
})
|
||||
},
|
||||
]
|
||||
result: ContentDescriptor{
|
||||
name: 'result'
|
||||
description: 'The response of the operation.'
|
||||
required: true
|
||||
schema: SchemaRef(Reference{
|
||||
ref: '#/components/schemas/Pets'
|
||||
})
|
||||
}
|
||||
errors: [
|
||||
ErrorSpec{
|
||||
code: 400
|
||||
message: 'Invalid request'
|
||||
},
|
||||
]
|
||||
},
|
||||
ActorMethod{
|
||||
name: 'createPet'
|
||||
summary: 'Create a new pet'
|
||||
example: openrpc.ExamplePairing{
|
||||
result: openrpc.ExampleRef(openrpc.Example{
|
||||
name: 'Example response'
|
||||
value: '[]'
|
||||
})
|
||||
}
|
||||
result: ContentDescriptor{
|
||||
name: 'result'
|
||||
description: 'The response of the operation.'
|
||||
required: true
|
||||
}
|
||||
errors: [
|
||||
ErrorSpec{
|
||||
code: 400
|
||||
message: 'Invalid input'
|
||||
},
|
||||
]
|
||||
},
|
||||
ActorMethod{
|
||||
name: 'getPet'
|
||||
summary: 'Get a pet by ID'
|
||||
example: openrpc.ExamplePairing{
|
||||
params: [
|
||||
openrpc.ExampleRef(openrpc.Example{
|
||||
name: 'Example petId'
|
||||
description: 'Example ID of the pet to retrieve'
|
||||
value: 1
|
||||
}),
|
||||
]
|
||||
result: openrpc.ExampleRef(openrpc.Example{
|
||||
name: 'Example response'
|
||||
value: json.raw_decode('{"id": 1, "name": "Fluffy", "tag": "dog"}')!
|
||||
})
|
||||
}
|
||||
parameters: [
|
||||
ContentDescriptor{
|
||||
name: 'petId'
|
||||
summary: 'ID of the pet to retrieve'
|
||||
description: 'ID of the pet to retrieve'
|
||||
required: true
|
||||
schema: SchemaRef(Schema{
|
||||
typ: 'integer'
|
||||
format: 'int64'
|
||||
example: 1
|
||||
})
|
||||
},
|
||||
]
|
||||
result: ContentDescriptor{
|
||||
name: 'result'
|
||||
description: 'The response of the operation.'
|
||||
required: true
|
||||
schema: SchemaRef(Reference{
|
||||
ref: '#/components/schemas/Pet'
|
||||
})
|
||||
}
|
||||
errors: [
|
||||
ErrorSpec{
|
||||
code: 404
|
||||
message: 'Pet not found'
|
||||
},
|
||||
]
|
||||
},
|
||||
ActorMethod{
|
||||
name: 'deletePet'
|
||||
summary: 'Delete a pet by ID'
|
||||
example: openrpc.ExamplePairing{
|
||||
params: [
|
||||
openrpc.ExampleRef(openrpc.Example{
|
||||
name: 'Example petId'
|
||||
description: 'Example ID of the pet to delete'
|
||||
value: 1
|
||||
}),
|
||||
]
|
||||
}
|
||||
parameters: [
|
||||
ContentDescriptor{
|
||||
name: 'petId'
|
||||
summary: 'ID of the pet to delete'
|
||||
description: 'ID of the pet to delete'
|
||||
required: true
|
||||
schema: SchemaRef(Schema{
|
||||
typ: 'integer'
|
||||
format: 'int64'
|
||||
example: 1
|
||||
})
|
||||
},
|
||||
]
|
||||
result: ContentDescriptor{
|
||||
name: 'result'
|
||||
description: 'The response of the operation.'
|
||||
required: true
|
||||
}
|
||||
errors: [
|
||||
ErrorSpec{
|
||||
code: 404
|
||||
message: 'Pet not found'
|
||||
},
|
||||
]
|
||||
},
|
||||
]
|
||||
objects: [
|
||||
BaseObject{
|
||||
schema: Schema{
|
||||
typ: 'object'
|
||||
properties: {
|
||||
'id': SchemaRef(Schema{
|
||||
typ: 'integer'
|
||||
format: 'int64'
|
||||
})
|
||||
'name': SchemaRef(Schema{
|
||||
typ: 'string'
|
||||
})
|
||||
'tag': SchemaRef(Schema{
|
||||
typ: 'string'
|
||||
})
|
||||
}
|
||||
required: ['id', 'name']
|
||||
}
|
||||
},
|
||||
BaseObject{
|
||||
schema: Schema{
|
||||
typ: 'object'
|
||||
properties: {
|
||||
'name': SchemaRef(Schema{
|
||||
typ: 'string'
|
||||
})
|
||||
'tag': SchemaRef(Schema{
|
||||
typ: 'string'
|
||||
})
|
||||
}
|
||||
required: ['name']
|
||||
}
|
||||
},
|
||||
BaseObject{
|
||||
schema: Schema{
|
||||
typ: 'array'
|
||||
items: jsonschema.Items(SchemaRef(Reference{
|
||||
ref: '#/components/schemas/Pet'
|
||||
}))
|
||||
}
|
||||
},
|
||||
]
|
||||
}
|
||||
|
||||
pub fn test_from_openapi() ! {
|
||||
// panic(from_openapi(openapi_spec)!)
|
||||
assert from_openapi(openapi_spec)! == actor_spec
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
module specification
|
||||
|
||||
import freeflowuniverse.herolib.schemas.openrpc { OpenRPC, Method, ContentDescriptor, ErrorSpec }
|
||||
import freeflowuniverse.herolib.schemas.jsonschema { Reference, Schema, SchemaRef }
|
||||
import freeflowuniverse.herolib.schemas.openrpc { ContentDescriptor, ErrorSpec, Method, OpenRPC }
|
||||
import freeflowuniverse.herolib.schemas.jsonschema { Reference, Schema }
|
||||
import freeflowuniverse.herolib.core.texttools
|
||||
|
||||
// Helper function: Convert OpenRPC Method to ActorMethod
|
||||
@@ -12,7 +12,7 @@ fn openrpc_method_to_actor_method(method Method) ActorMethod {
|
||||
// Process parameters
|
||||
for param in method.params {
|
||||
if param is ContentDescriptor {
|
||||
parameters << param
|
||||
parameters << param
|
||||
} else {
|
||||
panic('Method param should be inflated')
|
||||
}
|
||||
@@ -32,12 +32,12 @@ fn openrpc_method_to_actor_method(method Method) ActorMethod {
|
||||
}
|
||||
|
||||
return ActorMethod{
|
||||
name: method.name
|
||||
name: method.name
|
||||
description: method.description
|
||||
summary: method.summary
|
||||
parameters: parameters
|
||||
result: method.result as ContentDescriptor
|
||||
errors: errors
|
||||
summary: method.summary
|
||||
parameters: parameters
|
||||
result: method.result as ContentDescriptor
|
||||
errors: errors
|
||||
}
|
||||
}
|
||||
|
||||
@@ -86,9 +86,10 @@ pub fn from_openrpc(spec OpenRPC) !ActorSpecification {
|
||||
if schema is Schema {
|
||||
if schema.typ == 'object' {
|
||||
objects << BaseObject{
|
||||
schema: Schema {...schema,
|
||||
schema: Schema{
|
||||
...schema
|
||||
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{
|
||||
name: spec.info.title
|
||||
name: spec.info.title
|
||||
description: spec.info.description
|
||||
interfaces: [.openrpc]
|
||||
methods: methods
|
||||
objects: objects
|
||||
interfaces: [.openrpc]
|
||||
methods: methods
|
||||
objects: objects
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,380 +1,421 @@
|
||||
module specification
|
||||
|
||||
import freeflowuniverse.herolib.core.code { Struct, Function }
|
||||
import freeflowuniverse.herolib.schemas.openrpc { ContentDescriptor, ErrorSpec }
|
||||
import freeflowuniverse.herolib.schemas.openapi { OpenAPI, Info, ServerSpec, Components, Operation, PathItem, PathRef }
|
||||
import freeflowuniverse.herolib.schemas.jsonschema {Schema, Reference, SchemaRef}
|
||||
import freeflowuniverse.herolib.core.code { Struct }
|
||||
import freeflowuniverse.herolib.schemas.openrpc { ContentDescriptor }
|
||||
import freeflowuniverse.herolib.schemas.openapi { Components, Info }
|
||||
import freeflowuniverse.herolib.schemas.jsonschema { Reference, Schema, SchemaRef }
|
||||
|
||||
const openrpc_spec = openrpc.OpenRPC{
|
||||
openrpc: '1.0.0-rc1'
|
||||
info: openrpc.Info{
|
||||
title: 'Petstore'
|
||||
license: openrpc.License{
|
||||
name: 'MIT'
|
||||
}
|
||||
version: '1.0.0'
|
||||
}
|
||||
servers: [openrpc.Server{
|
||||
name: 'localhost'
|
||||
url: openrpc.RuntimeExpression('http://localhost:8080')
|
||||
}]
|
||||
methods: [
|
||||
openrpc.Method{
|
||||
name: 'list_pets'
|
||||
summary: 'List all pets'
|
||||
params: [openrpc.ContentDescriptorRef(openrpc.ContentDescriptor{
|
||||
name: 'limit'
|
||||
description: 'How many items to return at one time (max 100)'
|
||||
required: false
|
||||
schema: jsonschema.SchemaRef(jsonschema.Schema{
|
||||
typ: 'integer'
|
||||
minimum: 1
|
||||
})
|
||||
})]
|
||||
result: openrpc.ContentDescriptorRef(openrpc.ContentDescriptor{
|
||||
name: 'pets'
|
||||
description: 'A paged array of pets'
|
||||
schema: jsonschema.SchemaRef(jsonschema.Schema{
|
||||
typ: 'array'
|
||||
items: jsonschema.Items(jsonschema.SchemaRef(jsonschema.Reference{
|
||||
ref: '#/components/schemas/Pet'
|
||||
}))
|
||||
})
|
||||
})
|
||||
examples: [openrpc.ExamplePairing{
|
||||
name: 'listPetExample'
|
||||
description: 'List pet example'
|
||||
}]
|
||||
},
|
||||
openrpc.Method{
|
||||
name: 'create_pet'
|
||||
summary: 'Create a pet'
|
||||
params: [
|
||||
openrpc.ContentDescriptorRef(openrpc.ContentDescriptor{
|
||||
name: 'newPetName'
|
||||
description: 'Name of pet to create'
|
||||
required: true
|
||||
schema: jsonschema.SchemaRef(jsonschema.Schema{
|
||||
typ: 'string'
|
||||
})
|
||||
}),
|
||||
openrpc.ContentDescriptorRef(openrpc.ContentDescriptor{
|
||||
name: 'newPetTag'
|
||||
description: 'Pet tag to create'
|
||||
schema: jsonschema.SchemaRef(jsonschema.Schema{
|
||||
typ: 'string'
|
||||
})
|
||||
})
|
||||
]
|
||||
result: openrpc.ContentDescriptorRef(jsonschema.Reference{
|
||||
ref: '#/components/contentDescriptors/PetId'
|
||||
})
|
||||
examples: [openrpc.ExamplePairing{
|
||||
name: 'createPetExample'
|
||||
description: 'Create pet example'
|
||||
}]
|
||||
},
|
||||
openrpc.Method{
|
||||
name: 'get_pet'
|
||||
summary: 'Info for a specific pet'
|
||||
params: [openrpc.ContentDescriptorRef(jsonschema.Reference{
|
||||
ref: '#/components/contentDescriptors/PetId'
|
||||
})]
|
||||
result: openrpc.ContentDescriptorRef(openrpc.ContentDescriptor{
|
||||
name: 'pet'
|
||||
description: 'Expected response to a valid request'
|
||||
schema: jsonschema.SchemaRef(jsonschema.Reference{
|
||||
ref: '#/components/schemas/Pet'
|
||||
})
|
||||
})
|
||||
examples: [openrpc.ExamplePairing{
|
||||
name: 'getPetExample'
|
||||
description: 'Get pet example'
|
||||
}]
|
||||
},
|
||||
openrpc.Method{
|
||||
name: 'update_pet'
|
||||
summary: 'Update a pet'
|
||||
params: [
|
||||
openrpc.ContentDescriptorRef(jsonschema.Reference{
|
||||
ref: '#/components/contentDescriptors/PetId'
|
||||
}),
|
||||
openrpc.ContentDescriptorRef(openrpc.ContentDescriptor{
|
||||
name: 'updatedPetName'
|
||||
description: 'New name for the pet'
|
||||
required: true
|
||||
schema: jsonschema.SchemaRef(jsonschema.Schema{
|
||||
typ: 'string'
|
||||
})
|
||||
}),
|
||||
openrpc.ContentDescriptorRef(openrpc.ContentDescriptor{
|
||||
name: 'updatedPetTag'
|
||||
description: 'New tag for the pet'
|
||||
schema: jsonschema.SchemaRef(jsonschema.Schema{
|
||||
typ: 'string'
|
||||
})
|
||||
})
|
||||
]
|
||||
result: openrpc.ContentDescriptorRef(openrpc.ContentDescriptor{
|
||||
name: 'pet'
|
||||
description: 'Updated pet object'
|
||||
schema: jsonschema.SchemaRef(jsonschema.Reference{
|
||||
ref: '#/components/schemas/Pet'
|
||||
})
|
||||
})
|
||||
examples: [openrpc.ExamplePairing{
|
||||
name: 'updatePetExample'
|
||||
description: 'Update pet example'
|
||||
}]
|
||||
},
|
||||
openrpc.Method{
|
||||
name: 'delete_pet'
|
||||
summary: 'Delete a pet'
|
||||
params: [openrpc.ContentDescriptorRef(jsonschema.Reference{
|
||||
ref: '#/components/contentDescriptors/PetId'
|
||||
})]
|
||||
result: openrpc.ContentDescriptorRef(openrpc.ContentDescriptor{
|
||||
name: 'success'
|
||||
description: 'Boolean indicating success'
|
||||
schema: jsonschema.SchemaRef(jsonschema.Schema{
|
||||
typ: 'boolean'
|
||||
})
|
||||
})
|
||||
examples: [openrpc.ExamplePairing{
|
||||
name: 'deletePetExample'
|
||||
description: 'Delete pet example'
|
||||
}]
|
||||
}
|
||||
]
|
||||
components: openrpc.Components{
|
||||
content_descriptors: {
|
||||
'PetId': openrpc.ContentDescriptorRef(openrpc.ContentDescriptor{
|
||||
name: 'petId'
|
||||
description: 'The ID of the pet'
|
||||
required: true
|
||||
schema: jsonschema.SchemaRef(jsonschema.Reference{
|
||||
ref: '#/components/schemas/PetId'
|
||||
})
|
||||
})
|
||||
}
|
||||
schemas: {
|
||||
'PetId': jsonschema.SchemaRef(jsonschema.Schema{
|
||||
typ: 'integer'
|
||||
minimum: 0
|
||||
}),
|
||||
'Pet': jsonschema.SchemaRef(jsonschema.Schema{
|
||||
typ: 'object'
|
||||
properties: {
|
||||
'id': jsonschema.SchemaRef(jsonschema.Reference{
|
||||
ref: '#/components/schemas/PetId'
|
||||
}),
|
||||
'name': jsonschema.SchemaRef(jsonschema.Schema{
|
||||
typ: 'string'
|
||||
}),
|
||||
'tag': jsonschema.SchemaRef(jsonschema.Schema{
|
||||
typ: 'string'
|
||||
})
|
||||
}
|
||||
required: ['id', 'name']
|
||||
})
|
||||
}
|
||||
}
|
||||
openrpc: '1.0.0-rc1'
|
||||
info: openrpc.Info{
|
||||
title: 'Petstore'
|
||||
license: openrpc.License{
|
||||
name: 'MIT'
|
||||
}
|
||||
version: '1.0.0'
|
||||
}
|
||||
servers: [
|
||||
openrpc.Server{
|
||||
name: 'localhost'
|
||||
url: openrpc.RuntimeExpression('http://localhost:8080')
|
||||
},
|
||||
]
|
||||
methods: [
|
||||
openrpc.Method{
|
||||
name: 'list_pets'
|
||||
summary: 'List all pets'
|
||||
params: [
|
||||
openrpc.ContentDescriptorRef(ContentDescriptor{
|
||||
name: 'limit'
|
||||
description: 'How many items to return at one time (max 100)'
|
||||
required: false
|
||||
schema: SchemaRef(Schema{
|
||||
typ: 'integer'
|
||||
minimum: 1
|
||||
})
|
||||
}),
|
||||
]
|
||||
result: openrpc.ContentDescriptorRef(ContentDescriptor{
|
||||
name: 'pets'
|
||||
description: 'A paged array of pets'
|
||||
schema: SchemaRef(Schema{
|
||||
typ: 'array'
|
||||
items: jsonschema.Items(SchemaRef(Reference{
|
||||
ref: '#/components/schemas/Pet'
|
||||
}))
|
||||
})
|
||||
})
|
||||
examples: [
|
||||
openrpc.ExamplePairing{
|
||||
name: 'listPetExample'
|
||||
description: 'List pet example'
|
||||
},
|
||||
]
|
||||
},
|
||||
openrpc.Method{
|
||||
name: 'create_pet'
|
||||
summary: 'Create a pet'
|
||||
params: [
|
||||
openrpc.ContentDescriptorRef(ContentDescriptor{
|
||||
name: 'newPetName'
|
||||
description: 'Name of pet to create'
|
||||
required: true
|
||||
schema: SchemaRef(Schema{
|
||||
typ: 'string'
|
||||
})
|
||||
}),
|
||||
openrpc.ContentDescriptorRef(ContentDescriptor{
|
||||
name: 'newPetTag'
|
||||
description: 'Pet tag to create'
|
||||
schema: SchemaRef(Schema{
|
||||
typ: 'string'
|
||||
})
|
||||
}),
|
||||
]
|
||||
result: openrpc.ContentDescriptorRef(Reference{
|
||||
ref: '#/components/contentDescriptors/PetId'
|
||||
})
|
||||
examples: [
|
||||
openrpc.ExamplePairing{
|
||||
name: 'createPetExample'
|
||||
description: 'Create pet example'
|
||||
},
|
||||
]
|
||||
},
|
||||
openrpc.Method{
|
||||
name: 'get_pet'
|
||||
summary: 'Info for a specific pet'
|
||||
params: [
|
||||
openrpc.ContentDescriptorRef(Reference{
|
||||
ref: '#/components/contentDescriptors/PetId'
|
||||
}),
|
||||
]
|
||||
result: openrpc.ContentDescriptorRef(ContentDescriptor{
|
||||
name: 'pet'
|
||||
description: 'Expected response to a valid request'
|
||||
schema: SchemaRef(Reference{
|
||||
ref: '#/components/schemas/Pet'
|
||||
})
|
||||
})
|
||||
examples: [
|
||||
openrpc.ExamplePairing{
|
||||
name: 'getPetExample'
|
||||
description: 'Get pet example'
|
||||
},
|
||||
]
|
||||
},
|
||||
openrpc.Method{
|
||||
name: 'update_pet'
|
||||
summary: 'Update a pet'
|
||||
params: [
|
||||
openrpc.ContentDescriptorRef(Reference{
|
||||
ref: '#/components/contentDescriptors/PetId'
|
||||
}),
|
||||
openrpc.ContentDescriptorRef(ContentDescriptor{
|
||||
name: 'updatedPetName'
|
||||
description: 'New name for the pet'
|
||||
required: true
|
||||
schema: SchemaRef(Schema{
|
||||
typ: 'string'
|
||||
})
|
||||
}),
|
||||
openrpc.ContentDescriptorRef(ContentDescriptor{
|
||||
name: 'updatedPetTag'
|
||||
description: 'New tag for the pet'
|
||||
schema: SchemaRef(Schema{
|
||||
typ: 'string'
|
||||
})
|
||||
}),
|
||||
]
|
||||
result: openrpc.ContentDescriptorRef(ContentDescriptor{
|
||||
name: 'pet'
|
||||
description: 'Updated pet object'
|
||||
schema: SchemaRef(Reference{
|
||||
ref: '#/components/schemas/Pet'
|
||||
})
|
||||
})
|
||||
examples: [
|
||||
openrpc.ExamplePairing{
|
||||
name: 'updatePetExample'
|
||||
description: 'Update pet example'
|
||||
},
|
||||
]
|
||||
},
|
||||
openrpc.Method{
|
||||
name: 'delete_pet'
|
||||
summary: 'Delete a pet'
|
||||
params: [
|
||||
openrpc.ContentDescriptorRef(Reference{
|
||||
ref: '#/components/contentDescriptors/PetId'
|
||||
}),
|
||||
]
|
||||
result: openrpc.ContentDescriptorRef(ContentDescriptor{
|
||||
name: 'success'
|
||||
description: 'Boolean indicating success'
|
||||
schema: SchemaRef(Schema{
|
||||
typ: 'boolean'
|
||||
})
|
||||
})
|
||||
examples: [
|
||||
openrpc.ExamplePairing{
|
||||
name: 'deletePetExample'
|
||||
description: 'Delete pet example'
|
||||
},
|
||||
]
|
||||
},
|
||||
]
|
||||
components: openrpc.Components{
|
||||
content_descriptors: {
|
||||
'PetId': openrpc.ContentDescriptorRef(ContentDescriptor{
|
||||
name: 'petId'
|
||||
description: 'The ID of the pet'
|
||||
required: true
|
||||
schema: SchemaRef(Reference{
|
||||
ref: '#/components/schemas/PetId'
|
||||
})
|
||||
})
|
||||
}
|
||||
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 = specification.ActorSpecification{
|
||||
name: 'Petstore'
|
||||
structure: code.Struct{
|
||||
is_pub: false
|
||||
}
|
||||
interfaces: [.openrpc]
|
||||
methods: [specification.ActorMethod{
|
||||
name: 'list_pets'
|
||||
summary: 'List all pets'
|
||||
parameters: [openrpc.ContentDescriptor{
|
||||
name: 'limit'
|
||||
description: 'How many items to return at one time (max 100)'
|
||||
required: false
|
||||
schema: jsonschema.SchemaRef(jsonschema.Schema{
|
||||
typ: 'integer'
|
||||
minimum: 1
|
||||
})
|
||||
}]
|
||||
result: openrpc.ContentDescriptor{
|
||||
name: 'pets'
|
||||
description: 'A paged array of pets'
|
||||
schema: jsonschema.SchemaRef(jsonschema.Schema{
|
||||
typ: 'array'
|
||||
items: jsonschema.Items(jsonschema.SchemaRef(jsonschema.Schema{
|
||||
typ: 'object'
|
||||
properties: {
|
||||
'id': jsonschema.SchemaRef(jsonschema.Reference{
|
||||
ref: '#/components/schemas/PetId'
|
||||
}),
|
||||
'name': jsonschema.SchemaRef(jsonschema.Schema{
|
||||
typ: 'string'
|
||||
}),
|
||||
'tag': jsonschema.SchemaRef(jsonschema.Schema{
|
||||
typ: 'string'
|
||||
})
|
||||
}
|
||||
required: ['id', 'name']
|
||||
}))
|
||||
})
|
||||
}
|
||||
}, specification.ActorMethod{
|
||||
name: 'create_pet'
|
||||
summary: 'Create a pet'
|
||||
parameters: [openrpc.ContentDescriptor{
|
||||
name: 'newPetName'
|
||||
description: 'Name of pet to create'
|
||||
required: true
|
||||
schema: jsonschema.SchemaRef(jsonschema.Schema{
|
||||
typ: 'string'
|
||||
})
|
||||
}, openrpc.ContentDescriptor{
|
||||
name: 'newPetTag'
|
||||
description: 'Pet tag to create'
|
||||
schema: jsonschema.SchemaRef(jsonschema.Schema{
|
||||
typ: 'string'
|
||||
})
|
||||
}]
|
||||
}, specification.ActorMethod{
|
||||
name: 'get_pet'
|
||||
summary: 'Info for a specific pet'
|
||||
result: openrpc.ContentDescriptor{
|
||||
name: 'pet'
|
||||
description: 'Expected response to a valid request'
|
||||
schema: jsonschema.SchemaRef(jsonschema.Schema{
|
||||
typ: 'object'
|
||||
properties: {
|
||||
'id': jsonschema.SchemaRef(jsonschema.Reference{
|
||||
ref: '#/components/schemas/PetId'
|
||||
}),
|
||||
'name': jsonschema.SchemaRef(jsonschema.Schema{
|
||||
typ: 'string'
|
||||
}),
|
||||
'tag': jsonschema.SchemaRef(jsonschema.Schema{
|
||||
typ: 'string'
|
||||
})
|
||||
}
|
||||
required: ['id', 'name']
|
||||
})
|
||||
}
|
||||
}, specification.ActorMethod{
|
||||
name: 'update_pet'
|
||||
summary: 'Update a pet'
|
||||
parameters: [openrpc.ContentDescriptor{
|
||||
name: 'updatedPetName'
|
||||
description: 'New name for the pet'
|
||||
required: true
|
||||
schema: jsonschema.SchemaRef(jsonschema.Schema{
|
||||
typ: 'string'
|
||||
})
|
||||
}, openrpc.ContentDescriptor{
|
||||
name: 'updatedPetTag'
|
||||
description: 'New tag for the pet'
|
||||
schema: jsonschema.SchemaRef(jsonschema.Schema{
|
||||
typ: 'string'
|
||||
})
|
||||
}]
|
||||
result: openrpc.ContentDescriptor{
|
||||
name: 'pet'
|
||||
description: 'Updated pet object'
|
||||
schema: jsonschema.SchemaRef(jsonschema.Schema{
|
||||
typ: 'object'
|
||||
properties: {
|
||||
'id': jsonschema.SchemaRef(jsonschema.Reference{
|
||||
ref: '#/components/schemas/PetId'
|
||||
}),
|
||||
'name': jsonschema.SchemaRef(jsonschema.Schema{
|
||||
typ: 'string'
|
||||
}),
|
||||
'tag': jsonschema.SchemaRef(jsonschema.Schema{
|
||||
typ: 'string'
|
||||
})
|
||||
}
|
||||
required: ['id', 'name']
|
||||
})
|
||||
}
|
||||
}, specification.ActorMethod{
|
||||
name: 'delete_pet'
|
||||
summary: 'Delete a pet'
|
||||
result: openrpc.ContentDescriptor{
|
||||
name: 'success'
|
||||
description: 'Boolean indicating success'
|
||||
schema: jsonschema.SchemaRef(jsonschema.Schema{
|
||||
typ: 'boolean'
|
||||
})
|
||||
}
|
||||
}]
|
||||
objects: [specification.BaseObject{
|
||||
schema: jsonschema.Schema{
|
||||
id: 'pet'
|
||||
title: 'Pet'
|
||||
typ: 'object'
|
||||
properties: {
|
||||
'id': jsonschema.SchemaRef(jsonschema.Reference{
|
||||
ref: '#/components/schemas/PetId'
|
||||
}),
|
||||
'name': jsonschema.SchemaRef(jsonschema.Schema{
|
||||
typ: 'string'
|
||||
}),
|
||||
'tag': jsonschema.SchemaRef(jsonschema.Schema{
|
||||
typ: 'string'
|
||||
})
|
||||
}
|
||||
required: ['id', 'name']
|
||||
}
|
||||
}]
|
||||
const actor_spec = ActorSpecification{
|
||||
name: 'Petstore'
|
||||
structure: Struct{
|
||||
is_pub: false
|
||||
}
|
||||
interfaces: [.openrpc]
|
||||
methods: [
|
||||
ActorMethod{
|
||||
name: 'list_pets'
|
||||
summary: 'List all pets'
|
||||
parameters: [
|
||||
ContentDescriptor{
|
||||
name: 'limit'
|
||||
description: 'How many items to return at one time (max 100)'
|
||||
required: false
|
||||
schema: SchemaRef(Schema{
|
||||
typ: 'integer'
|
||||
minimum: 1
|
||||
})
|
||||
},
|
||||
]
|
||||
result: ContentDescriptor{
|
||||
name: 'pets'
|
||||
description: 'A paged array of pets'
|
||||
schema: SchemaRef(Schema{
|
||||
typ: 'array'
|
||||
items: jsonschema.Items(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',
|
||||
]
|
||||
}))
|
||||
})
|
||||
}
|
||||
},
|
||||
ActorMethod{
|
||||
name: 'create_pet'
|
||||
summary: 'Create a pet'
|
||||
parameters: [
|
||||
ContentDescriptor{
|
||||
name: 'newPetName'
|
||||
description: 'Name of pet to create'
|
||||
required: true
|
||||
schema: SchemaRef(Schema{
|
||||
typ: 'string'
|
||||
})
|
||||
},
|
||||
ContentDescriptor{
|
||||
name: 'newPetTag'
|
||||
description: 'Pet tag to create'
|
||||
schema: SchemaRef(Schema{
|
||||
typ: 'string'
|
||||
})
|
||||
},
|
||||
]
|
||||
},
|
||||
ActorMethod{
|
||||
name: 'get_pet'
|
||||
summary: 'Info for a specific pet'
|
||||
result: ContentDescriptor{
|
||||
name: 'pet'
|
||||
description: 'Expected response to a valid request'
|
||||
schema: 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',
|
||||
]
|
||||
})
|
||||
}
|
||||
},
|
||||
ActorMethod{
|
||||
name: 'update_pet'
|
||||
summary: 'Update a pet'
|
||||
parameters: [
|
||||
ContentDescriptor{
|
||||
name: 'updatedPetName'
|
||||
description: 'New name for the pet'
|
||||
required: true
|
||||
schema: SchemaRef(Schema{
|
||||
typ: 'string'
|
||||
})
|
||||
},
|
||||
ContentDescriptor{
|
||||
name: 'updatedPetTag'
|
||||
description: 'New tag for the pet'
|
||||
schema: SchemaRef(Schema{
|
||||
typ: 'string'
|
||||
})
|
||||
},
|
||||
]
|
||||
result: ContentDescriptor{
|
||||
name: 'pet'
|
||||
description: 'Updated pet object'
|
||||
schema: 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',
|
||||
]
|
||||
})
|
||||
}
|
||||
},
|
||||
ActorMethod{
|
||||
name: 'delete_pet'
|
||||
summary: 'Delete a pet'
|
||||
result: ContentDescriptor{
|
||||
name: 'success'
|
||||
description: 'Boolean indicating success'
|
||||
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() ! {
|
||||
actor_spec_ := from_openrpc(openrpc_spec)!
|
||||
assert actor_spec_.methods.len == actor_spec.methods.len
|
||||
assert_methods_match(actor_spec_.methods[0], actor_spec.methods[0])
|
||||
|
||||
|
||||
// assert from_openrpc(openrpc_spec)! == actor_spec
|
||||
actor_spec_ := from_openrpc(openrpc_spec)!
|
||||
assert actor_spec_.methods.len == actor_spec.methods.len
|
||||
assert_methods_match(actor_spec_.methods[0], actor_spec.methods[0])
|
||||
|
||||
// assert from_openrpc(openrpc_spec)! == actor_spec
|
||||
}
|
||||
|
||||
fn assert_methods_match(a ActorMethod, b ActorMethod) {
|
||||
// Compare method names
|
||||
assert a.name == b.name, 'Method names do not match: ${a.name} != ${b.name}'
|
||||
|
||||
// Compare summaries
|
||||
assert a.summary == b.summary, 'Method summaries do not match for method ${a.name}.'
|
||||
// Compare method names
|
||||
assert a.name == b.name, 'Method names do not match: ${a.name} != ${b.name}'
|
||||
|
||||
// Compare descriptions
|
||||
assert a.description == b.description, 'Method descriptions do not match for method ${a.name}.'
|
||||
// Compare summaries
|
||||
assert a.summary == b.summary, 'Method summaries do not match for method ${a.name}.'
|
||||
|
||||
// Compare parameters count
|
||||
assert a.parameters.len == b.parameters.len, 'Parameter counts do not match for method ${a.name}.'
|
||||
// Compare descriptions
|
||||
assert a.description == b.description, 'Method descriptions do not match for method ${a.name}.'
|
||||
|
||||
// Compare each parameter
|
||||
for i, param_a in a.parameters {
|
||||
assert_params_match(param_a, b.parameters[i], a.name)
|
||||
}
|
||||
// Compare parameters count
|
||||
assert a.parameters.len == b.parameters.len, 'Parameter counts do not match for method ${a.name}.'
|
||||
|
||||
// Compare result
|
||||
assert_params_match(a.result, b.result, a.name)
|
||||
// Compare each parameter
|
||||
for i, param_a in a.parameters {
|
||||
assert_params_match(param_a, b.parameters[i], a.name)
|
||||
}
|
||||
|
||||
// Compare result
|
||||
assert_params_match(a.result, b.result, a.name)
|
||||
}
|
||||
|
||||
fn assert_params_match(a openrpc.ContentDescriptor, b openrpc.ContentDescriptor, method_name string) {
|
||||
// Compare parameter names
|
||||
assert a.name == b.name, 'Parameter names do not match in method ${method_name}: ${a.name} != ${b.name}'
|
||||
fn assert_params_match(a ContentDescriptor, b ContentDescriptor, method_name string) {
|
||||
// Compare parameter names
|
||||
assert a.name == b.name, 'Parameter names do not match in method ${method_name}: ${a.name} != ${b.name}'
|
||||
|
||||
// Compare summaries
|
||||
assert a.summary == b.summary, 'Parameter summaries do not match in method ${method_name}: ${a.name}'
|
||||
// Compare summaries
|
||||
assert a.summary == b.summary, 'Parameter summaries do not match in method ${method_name}: ${a.name}'
|
||||
|
||||
// Compare descriptions
|
||||
assert a.description == b.description, 'Parameter descriptions do not match in method ${method_name}: ${a.name}'
|
||||
// Compare descriptions
|
||||
assert a.description == b.description, 'Parameter descriptions do not match in method ${method_name}: ${a.name}'
|
||||
|
||||
// Compare required flags
|
||||
assert a.required == b.required, 'Required flags do not match in method ${method_name}: ${a.name}'
|
||||
// Compare required flags
|
||||
assert a.required == b.required, 'Required flags do not match in method ${method_name}: ${a.name}'
|
||||
|
||||
// Compare schemas
|
||||
// assert_schemas_match(a.schema, b.schema, method_name, a.name)
|
||||
// Compare schemas
|
||||
// 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) {
|
||||
@@ -390,4 +431,4 @@ fn assert_params_match(a openrpc.ContentDescriptor, b openrpc.ContentDescriptor,
|
||||
|
||||
// // Compare other schema fields as needed (e.g., properties, additional properties, items, etc.)
|
||||
// // Add more checks here if needed for deeper schema comparisons
|
||||
// }
|
||||
// }
|
||||
|
||||
@@ -1,21 +1,21 @@
|
||||
module specification
|
||||
|
||||
import freeflowuniverse.herolib.core.code { Struct, Function }
|
||||
import freeflowuniverse.herolib.core.code { Struct }
|
||||
import freeflowuniverse.herolib.schemas.openapi
|
||||
import freeflowuniverse.herolib.schemas.openrpc {ExamplePairing, ContentDescriptor, ErrorSpec}
|
||||
import freeflowuniverse.herolib.schemas.jsonschema {Schema, Reference}
|
||||
import freeflowuniverse.herolib.schemas.openrpc { ContentDescriptor, ErrorSpec, ExamplePairing }
|
||||
import freeflowuniverse.herolib.schemas.jsonschema { Reference, Schema }
|
||||
|
||||
pub struct ActorSpecification {
|
||||
pub mut:
|
||||
version string = '1.0.0'
|
||||
openapi ?openapi.OpenAPI
|
||||
openrpc ?openrpc.OpenRPC
|
||||
name string @[omitempty]
|
||||
description string @[omitempty]
|
||||
structure Struct @[omitempty]
|
||||
interfaces []ActorInterface @[omitempty]
|
||||
methods []ActorMethod @[omitempty]
|
||||
objects []BaseObject @[omitempty]
|
||||
version string = '1.0.0'
|
||||
openapi ?openapi.OpenAPI
|
||||
openrpc ?openrpc.OpenRPC
|
||||
name string @[omitempty]
|
||||
description string @[omitempty]
|
||||
structure Struct @[omitempty]
|
||||
interfaces []ActorInterface @[omitempty]
|
||||
methods []ActorMethod @[omitempty]
|
||||
objects []BaseObject @[omitempty]
|
||||
}
|
||||
|
||||
pub enum ActorInterface {
|
||||
@@ -28,24 +28,24 @@ pub enum ActorInterface {
|
||||
|
||||
pub struct ActorMethod {
|
||||
pub:
|
||||
name string @[omitempty]
|
||||
description string @[omitempty]
|
||||
summary string
|
||||
example ExamplePairing
|
||||
parameters []ContentDescriptor
|
||||
result ContentDescriptor
|
||||
errors []ErrorSpec
|
||||
category MethodCategory
|
||||
name string @[omitempty]
|
||||
description string @[omitempty]
|
||||
summary string
|
||||
example ExamplePairing
|
||||
parameters []ContentDescriptor
|
||||
result ContentDescriptor
|
||||
errors []ErrorSpec
|
||||
category MethodCategory
|
||||
}
|
||||
|
||||
pub struct BaseObject {
|
||||
pub mut:
|
||||
schema Schema
|
||||
new_method ?ActorMethod
|
||||
get_method ?ActorMethod
|
||||
set_method ?ActorMethod
|
||||
schema Schema
|
||||
new_method ?ActorMethod
|
||||
get_method ?ActorMethod
|
||||
set_method ?ActorMethod
|
||||
delete_method ?ActorMethod
|
||||
list_method ?ActorMethod
|
||||
list_method ?ActorMethod
|
||||
filter_method ?ActorMethod
|
||||
other_methods []ActorMethod
|
||||
}
|
||||
@@ -66,7 +66,7 @@ fn (m ActorMethod) belongs_to_object(obj BaseObject) bool {
|
||||
.filter(it.schema is Schema)
|
||||
.map(it.schema as Schema)
|
||||
.any(it.id == obj.schema.id)
|
||||
|
||||
|
||||
base_obj_is_result := if m.result.schema is Schema {
|
||||
m.result.schema.id == obj.schema.id
|
||||
} else {
|
||||
@@ -77,7 +77,7 @@ fn (m ActorMethod) belongs_to_object(obj BaseObject) bool {
|
||||
return base_obj_is_param || base_obj_is_result
|
||||
}
|
||||
|
||||
pub fn (s ActorSpecification) validate() ActorSpecification {
|
||||
pub fn (s ActorSpecification) validate() ActorSpecification {
|
||||
mut validated_objects := []BaseObject{}
|
||||
for obj_ in s.objects {
|
||||
mut obj := obj_
|
||||
@@ -101,13 +101,13 @@ pub fn (s ActorSpecification) validate() ActorSpecification {
|
||||
if m := methods.filter(it.is_list_method())[0] {
|
||||
obj.list_method = m
|
||||
}
|
||||
validated_objects << BaseObject {
|
||||
validated_objects << BaseObject{
|
||||
...obj
|
||||
other_methods: methods.filter(!it.is_crudlf_method())
|
||||
}
|
||||
}
|
||||
return ActorSpecification {
|
||||
...s,
|
||||
return ActorSpecification{
|
||||
...s
|
||||
objects: validated_objects
|
||||
}
|
||||
}
|
||||
@@ -129,14 +129,14 @@ pub fn (s ActorSpecification) method_type(method ActorMethod) MethodCategory {
|
||||
}
|
||||
}
|
||||
|
||||
// a base object method is a method that is a
|
||||
// a base object method is a method that is a
|
||||
// CRUD+list+filter method of a base object
|
||||
fn (s ActorSpecification) is_base_object_method(method ActorMethod) bool {
|
||||
base_obj_is_param := method.parameters
|
||||
.filter(it.schema is Schema)
|
||||
.map(it.schema as Schema)
|
||||
.any(it.id in s.objects.map(it.schema.id))
|
||||
|
||||
|
||||
base_obj_is_result := if method.result.schema is Schema {
|
||||
method.result.schema.id in s.objects.map(it.name())
|
||||
} else {
|
||||
@@ -150,35 +150,38 @@ fn (s ActorSpecification) is_base_object_method(method ActorMethod) bool {
|
||||
fn (m ActorMethod) is_new_method() bool {
|
||||
return m.name.starts_with('new')
|
||||
}
|
||||
|
||||
fn (m ActorMethod) is_get_method() bool {
|
||||
return m.name.starts_with('get')
|
||||
}
|
||||
|
||||
fn (m ActorMethod) is_set_method() bool {
|
||||
return m.name.starts_with('set')
|
||||
}
|
||||
|
||||
fn (m ActorMethod) is_delete_method() bool {
|
||||
return m.name.starts_with('delete')
|
||||
}
|
||||
|
||||
fn (m ActorMethod) is_list_method() bool {
|
||||
return m.name.starts_with('list')
|
||||
}
|
||||
|
||||
fn (m ActorMethod) is_filter_method() bool {
|
||||
return m.name.starts_with('filter')
|
||||
}
|
||||
|
||||
fn (m ActorMethod) is_crudlf_method() bool {
|
||||
return m.is_new_method() ||
|
||||
m.is_get_method() ||
|
||||
m.is_set_method() ||
|
||||
m.is_delete_method() ||
|
||||
m.is_list_method() ||
|
||||
m.is_filter_method()
|
||||
return m.is_new_method() || m.is_get_method() || m.is_set_method() || m.is_delete_method()
|
||||
|| m.is_list_method() || m.is_filter_method()
|
||||
}
|
||||
|
||||
pub fn (o BaseObject) name() string {
|
||||
return if 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 {
|
||||
@@ -199,4 +202,4 @@ fn (s ActorSpecification) is_base_object_delete_method(method ActorMethod) bool
|
||||
|
||||
fn (s ActorSpecification) is_base_object_list_method(method ActorMethod) bool {
|
||||
return s.is_base_object_method(method) && method.name.starts_with('list')
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
module specification
|
||||
|
||||
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
|
||||
|
||||
// Converts ActorSpecification to OpenAPI
|
||||
@@ -15,8 +15,14 @@ pub fn (s ActorSpecification) to_openapi() OpenAPI {
|
||||
for method in s.methods {
|
||||
op := method.to_openapi_operation()
|
||||
paths['${method.http_path()}'] = match method.http_method() {
|
||||
.get { PathItem {get: op} }
|
||||
else { panic('unsupported http method') }
|
||||
.get {
|
||||
PathItem{
|
||||
get: op
|
||||
}
|
||||
}
|
||||
else {
|
||||
panic('unsupported http method')
|
||||
}
|
||||
}
|
||||
// Assign operation to corresponding HTTP method
|
||||
// TODO: what about other verbs
|
||||
@@ -28,23 +34,23 @@ pub fn (s ActorSpecification) to_openapi() OpenAPI {
|
||||
}
|
||||
|
||||
return OpenAPI{
|
||||
openapi: '3.0.0',
|
||||
info: Info{
|
||||
title: s.name,
|
||||
summary: s.description,
|
||||
description: s.description,
|
||||
version: '1.0.0',
|
||||
},
|
||||
servers: [
|
||||
openapi: '3.0.0'
|
||||
info: Info{
|
||||
title: s.name
|
||||
summary: s.description
|
||||
description: s.description
|
||||
version: '1.0.0'
|
||||
}
|
||||
servers: [
|
||||
ServerSpec{
|
||||
url: 'http://localhost:8080',
|
||||
description: 'Default server',
|
||||
url: 'http://localhost:8080'
|
||||
description: 'Default server'
|
||||
},
|
||||
],
|
||||
paths: paths,
|
||||
]
|
||||
paths: paths
|
||||
components: Components{
|
||||
schemas: schemas
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -62,30 +68,30 @@ fn (m ActorMethod) http_method() http.Method {
|
||||
|
||||
fn (method ActorMethod) to_openapi_operation() Operation {
|
||||
mut op := Operation{
|
||||
summary: method.summary,
|
||||
description: method.description,
|
||||
operation_id: method.name,
|
||||
summary: method.summary
|
||||
description: method.description
|
||||
operation_id: method.name
|
||||
}
|
||||
|
||||
// Convert parameters to OpenAPI format
|
||||
for param in method.parameters {
|
||||
op.parameters << Parameter{
|
||||
name: param.name,
|
||||
in_: 'query', // Default to query parameters; adjust based on function context
|
||||
description: param.description,
|
||||
required: param.required,
|
||||
schema: param.schema,
|
||||
name: param.name
|
||||
in_: 'query' // Default to query parameters; adjust based on function context
|
||||
description: param.description
|
||||
required: param.required
|
||||
schema: param.schema
|
||||
}
|
||||
}
|
||||
|
||||
// if method.is_void()
|
||||
op.responses['200'] = ResponseSpec {
|
||||
op.responses['200'] = ResponseSpec{
|
||||
description: method.description
|
||||
content: {
|
||||
'application/json': MediaType {
|
||||
content: {
|
||||
'application/json': MediaType{
|
||||
schema: method.result.schema
|
||||
}
|
||||
}
|
||||
}
|
||||
return op
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,158 +2,182 @@ module specification
|
||||
|
||||
import freeflowuniverse.herolib.core.code
|
||||
import freeflowuniverse.herolib.schemas.jsonschema { Schema, SchemaRef }
|
||||
import freeflowuniverse.herolib.schemas.openapi { Operation, Parameter, OpenAPI, Components, Info, PathItem, ServerSpec }
|
||||
import freeflowuniverse.herolib.schemas.openapi
|
||||
import freeflowuniverse.herolib.schemas.openrpc
|
||||
|
||||
|
||||
const actor_spec = specification.ActorSpecification{
|
||||
name: 'Petstore'
|
||||
structure: code.Struct{
|
||||
is_pub: false
|
||||
}
|
||||
interfaces: [.openrpc]
|
||||
methods: [specification.ActorMethod{
|
||||
name: 'list_pets'
|
||||
summary: 'List all pets'
|
||||
parameters: [openrpc.ContentDescriptor{
|
||||
name: 'limit'
|
||||
description: 'How many items to return at one time (max 100)'
|
||||
required: false
|
||||
schema: jsonschema.SchemaRef(jsonschema.Schema{
|
||||
typ: 'integer'
|
||||
minimum: 1
|
||||
})
|
||||
}]
|
||||
result: openrpc.ContentDescriptor{
|
||||
name: 'pets'
|
||||
description: 'A paged array of pets'
|
||||
schema: jsonschema.SchemaRef(jsonschema.Schema{
|
||||
typ: 'array'
|
||||
items: jsonschema.Items(jsonschema.SchemaRef(jsonschema.Schema{
|
||||
typ: 'object'
|
||||
properties: {
|
||||
'id': jsonschema.SchemaRef(jsonschema.Reference{
|
||||
ref: '#/components/schemas/PetId'
|
||||
}),
|
||||
'name': jsonschema.SchemaRef(jsonschema.Schema{
|
||||
typ: 'string'
|
||||
}),
|
||||
'tag': jsonschema.SchemaRef(jsonschema.Schema{
|
||||
typ: 'string'
|
||||
})
|
||||
}
|
||||
required: ['id', 'name']
|
||||
}))
|
||||
})
|
||||
}
|
||||
}, specification.ActorMethod{
|
||||
name: 'create_pet'
|
||||
summary: 'Create a pet'
|
||||
parameters: [openrpc.ContentDescriptor{
|
||||
name: 'newPetName'
|
||||
description: 'Name of pet to create'
|
||||
required: true
|
||||
schema: jsonschema.SchemaRef(jsonschema.Schema{
|
||||
typ: 'string'
|
||||
})
|
||||
}, openrpc.ContentDescriptor{
|
||||
name: 'newPetTag'
|
||||
description: 'Pet tag to create'
|
||||
schema: jsonschema.SchemaRef(jsonschema.Schema{
|
||||
typ: 'string'
|
||||
})
|
||||
}]
|
||||
}, specification.ActorMethod{
|
||||
name: 'get_pet'
|
||||
summary: 'Info for a specific pet'
|
||||
result: openrpc.ContentDescriptor{
|
||||
name: 'pet'
|
||||
description: 'Expected response to a valid request'
|
||||
schema: jsonschema.SchemaRef(jsonschema.Schema{
|
||||
typ: 'object'
|
||||
properties: {
|
||||
'id': jsonschema.SchemaRef(jsonschema.Reference{
|
||||
ref: '#/components/schemas/PetId'
|
||||
}),
|
||||
'name': jsonschema.SchemaRef(jsonschema.Schema{
|
||||
typ: 'string'
|
||||
}),
|
||||
'tag': jsonschema.SchemaRef(jsonschema.Schema{
|
||||
typ: 'string'
|
||||
})
|
||||
}
|
||||
required: ['id', 'name']
|
||||
})
|
||||
}
|
||||
}, specification.ActorMethod{
|
||||
name: 'update_pet'
|
||||
summary: 'Update a pet'
|
||||
parameters: [openrpc.ContentDescriptor{
|
||||
name: 'updatedPetName'
|
||||
description: 'New name for the pet'
|
||||
required: true
|
||||
schema: jsonschema.SchemaRef(jsonschema.Schema{
|
||||
typ: 'string'
|
||||
})
|
||||
}, openrpc.ContentDescriptor{
|
||||
name: 'updatedPetTag'
|
||||
description: 'New tag for the pet'
|
||||
schema: jsonschema.SchemaRef(jsonschema.Schema{
|
||||
typ: 'string'
|
||||
})
|
||||
}]
|
||||
result: openrpc.ContentDescriptor{
|
||||
name: 'pet'
|
||||
description: 'Updated pet object'
|
||||
schema: jsonschema.SchemaRef(jsonschema.Schema{
|
||||
typ: 'object'
|
||||
properties: {
|
||||
'id': jsonschema.SchemaRef(jsonschema.Reference{
|
||||
ref: '#/components/schemas/PetId'
|
||||
}),
|
||||
'name': jsonschema.SchemaRef(jsonschema.Schema{
|
||||
typ: 'string'
|
||||
}),
|
||||
'tag': jsonschema.SchemaRef(jsonschema.Schema{
|
||||
typ: 'string'
|
||||
})
|
||||
}
|
||||
required: ['id', 'name']
|
||||
})
|
||||
}
|
||||
}, specification.ActorMethod{
|
||||
name: 'delete_pet'
|
||||
summary: 'Delete a pet'
|
||||
result: openrpc.ContentDescriptor{
|
||||
name: 'success'
|
||||
description: 'Boolean indicating success'
|
||||
schema: jsonschema.SchemaRef(jsonschema.Schema{
|
||||
typ: 'boolean'
|
||||
})
|
||||
}
|
||||
}]
|
||||
objects: [specification.BaseObject{
|
||||
schema: jsonschema.Schema{
|
||||
id: 'pet'
|
||||
title: 'Pet'
|
||||
typ: 'object'
|
||||
properties: {
|
||||
'id': jsonschema.SchemaRef(jsonschema.Reference{
|
||||
ref: '#/components/schemas/PetId'
|
||||
}),
|
||||
'name': jsonschema.SchemaRef(jsonschema.Schema{
|
||||
typ: 'string'
|
||||
}),
|
||||
'tag': jsonschema.SchemaRef(jsonschema.Schema{
|
||||
typ: 'string'
|
||||
})
|
||||
}
|
||||
required: ['id', 'name']
|
||||
}
|
||||
}]
|
||||
const actor_spec = ActorSpecification{
|
||||
name: 'Petstore'
|
||||
structure: code.Struct{
|
||||
is_pub: false
|
||||
}
|
||||
interfaces: [.openrpc]
|
||||
methods: [
|
||||
ActorMethod{
|
||||
name: 'list_pets'
|
||||
summary: 'List all pets'
|
||||
parameters: [
|
||||
openrpc.ContentDescriptor{
|
||||
name: 'limit'
|
||||
description: 'How many items to return at one time (max 100)'
|
||||
required: false
|
||||
schema: SchemaRef(Schema{
|
||||
typ: 'integer'
|
||||
minimum: 1
|
||||
})
|
||||
},
|
||||
]
|
||||
result: openrpc.ContentDescriptor{
|
||||
name: 'pets'
|
||||
description: 'A paged array of pets'
|
||||
schema: SchemaRef(Schema{
|
||||
typ: 'array'
|
||||
items: jsonschema.Items(SchemaRef(Schema{
|
||||
typ: 'object'
|
||||
properties: {
|
||||
'id': SchemaRef(jsonschema.Reference{
|
||||
ref: '#/components/schemas/PetId'
|
||||
})
|
||||
'name': SchemaRef(Schema{
|
||||
typ: 'string'
|
||||
})
|
||||
'tag': SchemaRef(Schema{
|
||||
typ: 'string'
|
||||
})
|
||||
}
|
||||
required: [
|
||||
'id',
|
||||
'name',
|
||||
]
|
||||
}))
|
||||
})
|
||||
}
|
||||
},
|
||||
ActorMethod{
|
||||
name: 'create_pet'
|
||||
summary: 'Create a pet'
|
||||
parameters: [
|
||||
openrpc.ContentDescriptor{
|
||||
name: 'newPetName'
|
||||
description: 'Name of pet to create'
|
||||
required: true
|
||||
schema: SchemaRef(Schema{
|
||||
typ: 'string'
|
||||
})
|
||||
},
|
||||
openrpc.ContentDescriptor{
|
||||
name: 'newPetTag'
|
||||
description: 'Pet tag to create'
|
||||
schema: SchemaRef(Schema{
|
||||
typ: 'string'
|
||||
})
|
||||
},
|
||||
]
|
||||
},
|
||||
ActorMethod{
|
||||
name: 'get_pet'
|
||||
summary: 'Info for a specific pet'
|
||||
result: openrpc.ContentDescriptor{
|
||||
name: 'pet'
|
||||
description: 'Expected response to a valid request'
|
||||
schema: SchemaRef(Schema{
|
||||
typ: 'object'
|
||||
properties: {
|
||||
'id': SchemaRef(jsonschema.Reference{
|
||||
ref: '#/components/schemas/PetId'
|
||||
})
|
||||
'name': SchemaRef(Schema{
|
||||
typ: 'string'
|
||||
})
|
||||
'tag': SchemaRef(Schema{
|
||||
typ: 'string'
|
||||
})
|
||||
}
|
||||
required: [
|
||||
'id',
|
||||
'name',
|
||||
]
|
||||
})
|
||||
}
|
||||
},
|
||||
ActorMethod{
|
||||
name: 'update_pet'
|
||||
summary: 'Update a pet'
|
||||
parameters: [
|
||||
openrpc.ContentDescriptor{
|
||||
name: 'updatedPetName'
|
||||
description: 'New name for the pet'
|
||||
required: true
|
||||
schema: SchemaRef(Schema{
|
||||
typ: 'string'
|
||||
})
|
||||
},
|
||||
openrpc.ContentDescriptor{
|
||||
name: 'updatedPetTag'
|
||||
description: 'New tag for the pet'
|
||||
schema: SchemaRef(Schema{
|
||||
typ: 'string'
|
||||
})
|
||||
},
|
||||
]
|
||||
result: openrpc.ContentDescriptor{
|
||||
name: 'pet'
|
||||
description: 'Updated pet object'
|
||||
schema: SchemaRef(Schema{
|
||||
typ: 'object'
|
||||
properties: {
|
||||
'id': SchemaRef(jsonschema.Reference{
|
||||
ref: '#/components/schemas/PetId'
|
||||
})
|
||||
'name': SchemaRef(Schema{
|
||||
typ: 'string'
|
||||
})
|
||||
'tag': SchemaRef(Schema{
|
||||
typ: 'string'
|
||||
})
|
||||
}
|
||||
required: [
|
||||
'id',
|
||||
'name',
|
||||
]
|
||||
})
|
||||
}
|
||||
},
|
||||
ActorMethod{
|
||||
name: 'delete_pet'
|
||||
summary: 'Delete a pet'
|
||||
result: openrpc.ContentDescriptor{
|
||||
name: 'success'
|
||||
description: 'Boolean indicating success'
|
||||
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
|
||||
pub fn test_specification_to_openapi() {
|
||||
pub fn test_specification_to_openapi() {
|
||||
panic(actor_spec.to_openapi())
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
module specification
|
||||
|
||||
import freeflowuniverse.herolib.schemas.openrpc {OpenRPC, Components}
|
||||
import freeflowuniverse.herolib.schemas.jsonschema {SchemaRef}
|
||||
import freeflowuniverse.herolib.schemas.jsonschema.codegen { struct_to_schema }
|
||||
import freeflowuniverse.herolib.schemas.openrpc { Components, OpenRPC }
|
||||
import freeflowuniverse.herolib.schemas.jsonschema { SchemaRef }
|
||||
import freeflowuniverse.herolib.schemas.jsonschema.codegen
|
||||
|
||||
// pub fn from_openrpc(spec openrpc.OpenRPC) !ActorSpecification {
|
||||
// // 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 {
|
||||
mut schemas := map[string]SchemaRef{}
|
||||
for obj in specification.objects {
|
||||
@@ -49,11 +48,11 @@ pub fn (specification ActorSpecification) to_openrpc() OpenRPC {
|
||||
// }
|
||||
}
|
||||
return OpenRPC{
|
||||
info: openrpc.Info{
|
||||
title: specification.name.title()
|
||||
info: openrpc.Info{
|
||||
title: specification.name.title()
|
||||
version: '1.0.0'
|
||||
}
|
||||
methods: specification.methods.map(method_to_openrpc_method(it))
|
||||
methods: specification.methods.map(method_to_openrpc_method(it))
|
||||
components: Components{
|
||||
schemas: schemas
|
||||
}
|
||||
@@ -61,12 +60,12 @@ pub fn (specification ActorSpecification) to_openrpc() OpenRPC {
|
||||
}
|
||||
|
||||
pub fn method_to_openrpc_method(method ActorMethod) openrpc.Method {
|
||||
return openrpc.Method {
|
||||
name: method.name
|
||||
summary: method.summary
|
||||
return openrpc.Method{
|
||||
name: method.name
|
||||
summary: method.summary
|
||||
description: method.description
|
||||
params: method.parameters.map(openrpc.ContentDescriptorRef(it))
|
||||
result: openrpc.ContentDescriptorRef(method.result)
|
||||
errors: method.errors.map(openrpc.ErrorRef(it))
|
||||
params: method.parameters.map(openrpc.ContentDescriptorRef(it))
|
||||
result: openrpc.ContentDescriptorRef(method.result)
|
||||
errors: method.errors.map(openrpc.ErrorRef(it))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -7,9 +7,9 @@ pub mut:
|
||||
id string
|
||||
name string
|
||||
priority int = 10 // 0 is highest, do 10 as default
|
||||
params string // json encoded params
|
||||
result string // can be used to remember outputs
|
||||
params string // json encoded params
|
||||
result string // can be used to remember outputs
|
||||
// run bool = true // certain actions can be defined but meant to be executed directly
|
||||
comments string
|
||||
done bool // if done then no longer need to process
|
||||
}
|
||||
comments string
|
||||
done bool // if done then no longer need to process
|
||||
}
|
||||
|
||||
@@ -18,7 +18,7 @@ pub:
|
||||
pub struct ClientConfig {
|
||||
ActorConfig
|
||||
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 {
|
||||
@@ -40,7 +40,7 @@ pub fn (mut p Client) call_to_action(action Action, params Params) !Action {
|
||||
wait: true
|
||||
})!
|
||||
|
||||
return Action {
|
||||
return Action{
|
||||
...action
|
||||
result: response_data
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
module stage
|
||||
|
||||
import freeflowuniverse.herolib.baobab.osis {OSIS}
|
||||
import freeflowuniverse.herolib.baobab.osis { OSIS }
|
||||
import freeflowuniverse.herolib.core.redisclient
|
||||
|
||||
@[heap]
|
||||
@@ -19,8 +19,8 @@ mut:
|
||||
@[params]
|
||||
pub struct ActorConfig {
|
||||
pub:
|
||||
name string
|
||||
version string
|
||||
name string
|
||||
version string
|
||||
redis_url string = 'localhost:6379'
|
||||
}
|
||||
|
||||
@@ -35,7 +35,7 @@ pub fn (config ActorConfig) redis_queue_name() string {
|
||||
pub fn new_actor(config ActorConfig) !Actor {
|
||||
return Actor{
|
||||
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 {
|
||||
return ActorConfig {...a,
|
||||
return ActorConfig{
|
||||
...a
|
||||
version: v
|
||||
}
|
||||
}
|
||||
|
||||
pub fn (a ActorConfig) example() ActorConfig {
|
||||
return ActorConfig {...a,
|
||||
return ActorConfig{
|
||||
...a
|
||||
version: 'example'
|
||||
}
|
||||
}
|
||||
|
||||
pub fn (mut a IActor) handle(method string, data string) !string {
|
||||
action := a.act(
|
||||
name: method
|
||||
name: method
|
||||
params: data
|
||||
)!
|
||||
return action.result
|
||||
|
||||
@@ -1,2 +1 @@
|
||||
module stage
|
||||
|
||||
|
||||
@@ -1,16 +1,16 @@
|
||||
module interfaces
|
||||
|
||||
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 {
|
||||
return Action{
|
||||
id: request.id
|
||||
name: request.method
|
||||
id: request.id
|
||||
name: request.method
|
||||
params: request.params
|
||||
}
|
||||
}
|
||||
|
||||
pub fn action_to_jsonrpc_response(action Action) jsonrpc.Response {
|
||||
return jsonrpc.new_response(action.id, action.result)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
module interfaces
|
||||
|
||||
import rand
|
||||
import x.json2 as json {Any}
|
||||
import freeflowuniverse.herolib.baobab.stage {Action, Client}
|
||||
import x.json2 as json { Any }
|
||||
import freeflowuniverse.herolib.baobab.stage { Action, Client }
|
||||
import freeflowuniverse.herolib.schemas.jsonrpc
|
||||
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 {
|
||||
// Convert incoming OpenAPI request to a procedure call
|
||||
action := action_from_openapi_request(request)
|
||||
response := i.client.call_to_action(action) or {
|
||||
return err
|
||||
}
|
||||
response := i.client.call_to_action(action) or { return err }
|
||||
return action_to_openapi_response(response)
|
||||
}
|
||||
|
||||
@@ -35,16 +33,16 @@ pub fn action_from_openapi_request(request openapi.Request) Action {
|
||||
if request.parameters.len > 0 {
|
||||
params << json.encode(request.parameters)
|
||||
}
|
||||
|
||||
return Action {
|
||||
id: rand.uuid_v4()
|
||||
name: request.operation.operation_id
|
||||
|
||||
return Action{
|
||||
id: rand.uuid_v4()
|
||||
name: request.operation.operation_id
|
||||
params: json.encode(params.str())
|
||||
}
|
||||
}
|
||||
|
||||
pub fn action_to_openapi_response(action Action) openapi.Response {
|
||||
return openapi.Response {
|
||||
return openapi.Response{
|
||||
body: action.result
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,15 +1,15 @@
|
||||
module interfaces
|
||||
|
||||
import freeflowuniverse.herolib.baobab.stage {Client}
|
||||
import freeflowuniverse.herolib.baobab.stage { Client }
|
||||
import freeflowuniverse.herolib.schemas.jsonrpc
|
||||
|
||||
// handler for test echoes JSONRPC Request as JSONRPC Response
|
||||
fn handler(request jsonrpc.Request) !jsonrpc.Response {
|
||||
return jsonrpc.Response {
|
||||
jsonrpc: request.jsonrpc
|
||||
id: request.id
|
||||
result: request.params
|
||||
}
|
||||
return jsonrpc.Response{
|
||||
jsonrpc: request.jsonrpc
|
||||
id: request.id
|
||||
result: request.params
|
||||
}
|
||||
}
|
||||
|
||||
pub struct OpenRPCInterface {
|
||||
@@ -26,4 +26,4 @@ pub fn (mut i OpenRPCInterface) handle(request jsonrpc.Request) !jsonrpc.Respons
|
||||
action := action_from_jsonrpc_request(request)
|
||||
response := i.client.call_to_action(action)!
|
||||
return action_to_jsonrpc_response(response)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5,87 +5,87 @@ module interfaces
|
||||
// import veb
|
||||
// import x.json2 {Any}
|
||||
// import net.http
|
||||
import freeflowuniverse.herolib.baobab.stage {Action}
|
||||
import freeflowuniverse.herolib.schemas.openapi {Request}
|
||||
import freeflowuniverse.herolib.baobab.stage { Action }
|
||||
import freeflowuniverse.herolib.schemas.openapi { Request }
|
||||
|
||||
pub fn openapi_request_to_action(request Request) Action {
|
||||
// // Convert incoming OpenAPI request to a procedure call
|
||||
// mut params := []Any{}
|
||||
// // Convert incoming OpenAPI request to a procedure call
|
||||
// mut params := []Any{}
|
||||
|
||||
// if request.arguments.len > 0 {
|
||||
// params << request.arguments.values().map(it.str()).clone()
|
||||
// }
|
||||
// if request.arguments.len > 0 {
|
||||
// params << request.arguments.values().map(it.str()).clone()
|
||||
// }
|
||||
|
||||
// if request.body != '' {
|
||||
// params << request.body
|
||||
// }
|
||||
// if request.body != '' {
|
||||
// params << request.body
|
||||
// }
|
||||
|
||||
// if request.parameters != '' {
|
||||
// params << request.body
|
||||
// }
|
||||
// if request.parameters != '' {
|
||||
// params << request.body
|
||||
// }
|
||||
|
||||
// if request.parameters.len != 0 {
|
||||
// mut param_map := map[string]Any{} // Store parameters with correct types
|
||||
// if request.parameters.len != 0 {
|
||||
// mut param_map := map[string]Any{} // Store parameters with correct types
|
||||
|
||||
// for param_name, param_value in request.parameters {
|
||||
// operation_param := request.operation.parameters.filter(it.name == param_name)
|
||||
// if operation_param.len > 0 {
|
||||
// param_schema := operation_param[0].schema as Schema
|
||||
// param_type := param_schema.typ
|
||||
// param_format := param_schema.format
|
||||
// for param_name, param_value in request.parameters {
|
||||
// operation_param := request.operation.parameters.filter(it.name == param_name)
|
||||
// if operation_param.len > 0 {
|
||||
// param_schema := operation_param[0].schema as Schema
|
||||
// param_type := param_schema.typ
|
||||
// param_format := param_schema.format
|
||||
|
||||
// // Convert parameter value to corresponding type
|
||||
// match param_type {
|
||||
// 'integer' {
|
||||
// match param_format {
|
||||
// 'int32' {
|
||||
// param_map[param_name] = param_value.int() // Convert to int
|
||||
// }
|
||||
// 'int64' {
|
||||
// param_map[param_name] = param_value.i64() // Convert to i64
|
||||
// }
|
||||
// else {
|
||||
// param_map[param_name] = param_value.int() // Default to int
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
// 'string' {
|
||||
// param_map[param_name] = param_value // Already a string
|
||||
// }
|
||||
// 'boolean' {
|
||||
// param_map[param_name] = param_value.bool() // Convert to bool
|
||||
// }
|
||||
// 'number' {
|
||||
// match param_format {
|
||||
// 'float' {
|
||||
// param_map[param_name] = param_value.f32() // Convert to float
|
||||
// }
|
||||
// 'double' {
|
||||
// param_map[param_name] = param_value.f64() // Convert to double
|
||||
// }
|
||||
// else {
|
||||
// param_map[param_name] = param_value.f64() // Default to double
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
// else {
|
||||
// param_map[param_name] = param_value // Leave as string for unknown types
|
||||
// }
|
||||
// }
|
||||
// } else {
|
||||
// // If the parameter is not defined in the OpenAPI operation, skip or log it
|
||||
// println('Unknown parameter: $param_name')
|
||||
// }
|
||||
// }
|
||||
// // Convert parameter value to corresponding type
|
||||
// match param_type {
|
||||
// 'integer' {
|
||||
// match param_format {
|
||||
// 'int32' {
|
||||
// param_map[param_name] = param_value.int() // Convert to int
|
||||
// }
|
||||
// 'int64' {
|
||||
// param_map[param_name] = param_value.i64() // Convert to i64
|
||||
// }
|
||||
// else {
|
||||
// param_map[param_name] = param_value.int() // Default to int
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
// 'string' {
|
||||
// param_map[param_name] = param_value // Already a string
|
||||
// }
|
||||
// 'boolean' {
|
||||
// param_map[param_name] = param_value.bool() // Convert to bool
|
||||
// }
|
||||
// 'number' {
|
||||
// match param_format {
|
||||
// 'float' {
|
||||
// param_map[param_name] = param_value.f32() // Convert to float
|
||||
// }
|
||||
// 'double' {
|
||||
// param_map[param_name] = param_value.f64() // Convert to double
|
||||
// }
|
||||
// else {
|
||||
// param_map[param_name] = param_value.f64() // Default to double
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
// else {
|
||||
// param_map[param_name] = param_value // Leave as string for unknown types
|
||||
// }
|
||||
// }
|
||||
// } else {
|
||||
// // If the parameter is not defined in the OpenAPI operation, skip or log it
|
||||
// println('Unknown parameter: $param_name')
|
||||
// }
|
||||
// }
|
||||
|
||||
// // Encode the parameter map to JSON if needed
|
||||
// params << json.encode(param_map.str())
|
||||
// }
|
||||
// // Encode the parameter map to JSON if needed
|
||||
// params << json.encode(param_map.str())
|
||||
// }
|
||||
|
||||
// call := Action{
|
||||
// name: request.operation.operation_id
|
||||
// params_json: json2.encode(params.str()) // Keep as a string since ProcedureCall expects a string
|
||||
// }
|
||||
// call := Action{
|
||||
// name: request.operation.operation_id
|
||||
// params_json: json2.encode(params.str()) // Keep as a string since ProcedureCall expects a string
|
||||
// }
|
||||
// return call
|
||||
return Action{}
|
||||
}
|
||||
return Action{}
|
||||
}
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
module interfaces
|
||||
|
||||
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 veb
|
||||
|
||||
|
||||
Reference in New Issue
Block a user