add more specifications necessary for example

This commit is contained in:
timurgordon
2025-02-13 03:29:36 +03:00
parent 6753b87873
commit 0fb8901ab5
8 changed files with 665 additions and 217 deletions

1
examples/baobab/generator/.gitignore vendored Normal file
View File

@@ -0,0 +1 @@
pet_store_actor

View File

@@ -1,2 +1,3 @@
merchant
merchant_actor
profiler
farmer

View File

@@ -0,0 +1,344 @@
{
"openapi": "3.0.1",
"info": {
"title": "Farmer",
"description": "API for managing farms and nodes, tracking rewards, capacity, and location.",
"version": "1.0.0"
},
"servers": [
{
"url": "http://localhost:8080",
"description": "Local development server"
}
],
"components": {
"schemas": {
"Farm": {
"type": "object",
"properties": {
"id": {
"type": "string",
"format": "uuid",
"example": "f47ac10b-58cc-4372-a567-0e02b2c3d479"
},
"name": {
"type": "string",
"example": "Amsterdam Data Center"
},
"description": {
"type": "string",
"example": "Enterprise-grade data center with renewable energy focus"
},
"owner": {
"type": "string",
"example": "0x742d35Cc6634C0532925a3b844Bc454e4438f44e"
}
},
"required": [
"id",
"name",
"owner"
]
},
"Node": {
"type": "object",
"properties": {
"id": {
"type": "string",
"format": "uuid",
"example": "n47ac10b-58cc-4372-a567-0e02b2c3d479"
},
"description": {
"type": "string",
"example": "High-performance GPU compute node with 4x NVIDIA A100"
},
"farm_id": {
"type": "string",
"format": "uuid",
"example": "f47ac10b-58cc-4372-a567-0e02b2c3d479"
},
"location": {
"$ref": "#/components/schemas/Location"
},
"capacity": {
"$ref": "#/components/schemas/Capacity"
},
"grid_version": {
"type": "string",
"example": "3.16.2"
},
"reward": {
"$ref": "#/components/schemas/Reward"
}
},
"required": [
"id",
"description",
"farm_id",
"location",
"capacity",
"reward"
]
},
"Location": {
"type": "object",
"properties": {
"coordinates": {
"type": "string",
"example": "52.3740, 4.8897"
},
"continent": {
"type": "string",
"example": "Europe"
},
"country": {
"type": "string",
"example": "Netherlands"
}
},
"required": [
"coordinates",
"continent",
"country"
]
},
"Capacity": {
"type": "object",
"properties": {
"cpu": {
"type": "integer",
"example": 128
},
"memory_gb": {
"type": "integer",
"example": 1024
},
"storage_tb": {
"type": "integer",
"example": 100
}
},
"required": [
"cpu",
"memory_gb",
"storage_tb"
]
},
"Reward": {
"type": "object",
"properties": {
"reward_promised": {
"type": "number",
"format": "double",
"example": 25000.50
},
"reward_given": {
"type": "number",
"format": "double",
"example": 12500.25
},
"duration_months": {
"type": "integer",
"example": 36
}
},
"required": [
"reward_promised",
"reward_given",
"duration_months"
]
},
"NodeStats": {
"type": "object",
"properties": {
"node_id": {
"type": "integer",
"format": "uint32",
"example": "42"
},
"uptime_hours": {
"type": "integer",
"example": 8760
},
"bandwidth_gb": {
"type": "integer",
"example": 25000
}
},
"required": [
"node_id",
"uptime_hours",
"bandwidth_gb"
]
}
}
},
"paths": {
"/farms": {
"get": {
"summary": "List all farms",
"operationId": "getFarms",
"responses": {
"200": {
"description": "List of farms",
"content": {
"application/json": {
"schema": {
"type": "array",
"items": {
"$ref": "#/components/schemas/Farm"
}
},
"example": [
{
"id": "f47ac10b-58cc-4372-a567-0e02b2c3d479",
"name": "Amsterdam Data Center",
"description": "Enterprise-grade data center with renewable energy focus",
"owner": "0x742d35Cc6634C0532925a3b844Bc454e4438f44e"
},
{
"id": "d47ac10b-58cc-4372-a567-0e02b2c3d480",
"name": "Dubai Compute Hub",
"description": "High-density compute farm with advanced cooling",
"owner": "0x842d35Cc6634C0532925a3b844Bc454e4438f55f"
}
]
}
}
}
}
}
},
"/farms/{farmId}/nodes": {
"get": {
"summary": "List nodes in a farm",
"operationId": "getNodesByFarm",
"parameters": [
{
"name": "farmId",
"in": "path",
"required": true,
"schema": {
"type": "string",
"format": "uuid"
},
"example": "f47ac10b-58cc-4372-a567-0e02b2c3d479"
}
],
"responses": {
"200": {
"description": "List of nodes in the farm",
"content": {
"application/json": {
"schema": {
"type": "array",
"items": {
"$ref": "#/components/schemas/Node"
}
},
"example": [
{
"id": "n47ac10b-58cc-4372-a567-0e02b2c3d479",
"description": "High-performance GPU compute node with 4x NVIDIA A100",
"farm_id": "f47ac10b-58cc-4372-a567-0e02b2c3d479",
"location": {
"coordinates": "52.3740, 4.8897",
"continent": "Europe",
"country": "Netherlands"
},
"capacity": {
"cpu": 128,
"memory_gb": 1024,
"storage_tb": 100
},
"grid_version": "3.16.2",
"reward": {
"reward_promised": 25000.50,
"reward_given": 12500.25,
"duration_months": 36
}
}
]
}
}
},
"404": {
"description": "Farm not found",
"content": {
"application/json": {
"schema": {
"type": "object",
"properties": {
"code": {
"type": "integer",
"example": 404
},
"message": {
"type": "string",
"example": "Farm with ID f47ac10b-58cc-4372-a567-0e02b2c3d479 not found"
}
}
}
}
}
}
}
}
},
"/nodes/{nodeId}/stats": {
"get": {
"summary": "Get node statistics",
"operationId": "getNodeStats",
"parameters": [
{
"name": "nodeId",
"in": "path",
"required": true,
"schema": {
"type": "integer",
"format": "uint32"
},
"example": "42"
}
],
"responses": {
"200": {
"description": "Node statistics",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/NodeStats"
},
"example": {
"node_id": "42",
"uptime_hours": 8760,
"bandwidth_gb": 25000
}
}
}
},
"404": {
"description": "Node not found",
"content": {
"application/json": {
"schema": {
"type": "object",
"properties": {
"code": {
"type": "integer",
"example": 404
},
"message": {
"type": "string",
"example": "Node with ID n47ac10b-58cc-4372-a567-0e02b2c3d479 not found"
}
}
}
}
}
}
}
}
}
}
}

View File

@@ -7,22 +7,19 @@ import os
const example_dir = os.dir(@FILE)
const openapi_spec_path = os.join_path(example_dir, 'openapi.json')
const specs = ['merchant', 'profiler', 'farmer']
// the actor specification obtained from the OpenRPC Specification
openapi_spec := openapi.new(path: openapi_spec_path, process: true)!
actor_spec := specification.from_openapi(openapi_spec)!
actor_module := generator.generate_actor_folder(
actor_spec,
interfaces: [.openapi, .http]
)!
actor_module.write(example_dir,
format: true
overwrite: true
compile: false
)!
// os.execvp('bash', ['${example_dir}/meeting_scheduler_actor/scripts/run.sh'])!
for spec in specs {
openapi_spec_path := os.join_path(example_dir, '${spec}.json')
openapi_spec := openapi.new(path: openapi_spec_path, process: true)!
actor_spec := specification.from_openapi(openapi_spec)!
actor_module := generator.generate_actor_folder(
actor_spec,
interfaces: [.openapi, .http]
)!
actor_module.write(example_dir,
format: true
overwrite: true
compile: false
)!
}

View File

@@ -0,0 +1,286 @@
{
"openapi": "3.0.1",
"info": {
"title": "Profiler",
"description": "API for managing user profiles with name, public key, and KYC verification",
"version": "1.0.0"
},
"servers": [
{
"url": "http://localhost:8080",
"description": "Local development server"
}
],
"components": {
"schemas": {
"Profile": {
"type": "object",
"properties": {
"id": {
"type": "string",
"format": "uuid",
"example": "123e4567-e89b-12d3-a456-426614174000"
},
"name": {
"type": "string",
"example": "Alice Doe"
},
"public_key": {
"type": "string",
"example": "028a8f8b59f7283a47f9f6d4bc8176e847ad2b6c6d8bdfd041e5e7f3b4ac28c9fc"
},
"kyc_verified": {
"type": "boolean",
"example": false
}
},
"required": ["id", "name", "public_key", "kyc_verified"]
},
"Error": {
"type": "object",
"properties": {
"code": {
"type": "integer",
"example": 400
},
"message": {
"type": "string",
"example": "Invalid request"
}
},
"required": ["code", "message"]
}
}
},
"paths": {
"/profiles": {
"post": {
"summary": "Create a new profile",
"operationId": "createProfile",
"requestBody": {
"required": true,
"content": {
"application/json": {
"schema": {
"type": "object",
"properties": {
"name": {
"type": "string",
"example": "Bob Smith"
},
"public_key": {
"type": "string",
"example": "03a1b2c3d4e5f6a7b8c9d0e1f2a3b4c5d6e7f8a9b0c1d2e3f4a5b6c7d8e9f0a1b2"
}
},
"required": ["name", "public_key"]
},
"examples": {
"newProfile": {
"summary": "Example of creating a new profile",
"value": {
"name": "Bob Smith",
"public_key": "03a1b2c3d4e5f6a7b8c9d0e1f2a3b4c5d6e7f8a9b0c1d2e3f4a5b6c7d8e9f0a1b2"
}
}
}
}
}
},
"responses": {
"201": {
"description": "Profile created successfully",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/Profile"
},
"examples": {
"successResponse": {
"summary": "Example of successful profile creation",
"value": {
"id": "550e8400-e29b-41d4-a716-446655440000",
"name": "Bob Smith",
"public_key": "03a1b2c3d4e5f6a7b8c9d0e1f2a3b4c5d6e7f8a9b0c1d2e3f4a5b6c7d8e9f0a1b2",
"kyc_verified": false
}
}
}
}
}
},
"400": {
"description": "Invalid input",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/Error"
},
"examples": {
"invalidInput": {
"summary": "Example of invalid input error",
"value": {
"code": 400,
"message": "Invalid public key format"
}
}
}
}
}
}
}
}
},
"/profiles/{profileId}": {
"get": {
"summary": "Get profile details",
"operationId": "getProfile",
"parameters": [
{
"name": "profileId",
"in": "path",
"required": true,
"schema": {
"type": "integer",
"format": "uint32"
},
"example": "42"
}
],
"responses": {
"200": {
"description": "Profile retrieved successfully",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/Profile"
},
"examples": {
"existingProfile": {
"summary": "Example of retrieved profile",
"value": {
"id": "550e8400-e29b-41d4-a716-446655440000",
"name": "Bob Smith",
"public_key": "03a1b2c3d4e5f6a7b8c9d0e1f2a3b4c5d6e7f8a9b0c1d2e3f4a5b6c7d8e9f0a1b2",
"kyc_verified": true
}
}
}
}
}
},
"404": {
"description": "Profile not found",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/Error"
},
"examples": {
"notFound": {
"summary": "Example of profile not found error",
"value": {
"code": 404,
"message": "Profile with ID '550e8400-e29b-41d4-a716-446655440000' not found"
}
}
}
}
}
}
}
}
},
"/profiles/{profileId}/kyc": {
"put": {
"summary": "Update KYC verification status",
"operationId": "updateKYCStatus",
"parameters": [
{
"name": "profileId",
"in": "path",
"required": true,
"schema": {
"type": "integer",
"format": "uint32"
},
"example": "42"
}
],
"requestBody": {
"required": true,
"content": {
"application/json": {
"schema": {
"type": "object",
"properties": {
"kyc_verified": {
"type": "boolean",
"example": true
}
},
"required": ["kyc_verified"]
},
"examples": {
"verifyKYC": {
"summary": "Example of verifying KYC",
"value": {
"kyc_verified": true
}
},
"unverifyKYC": {
"summary": "Example of unverifying KYC",
"value": {
"kyc_verified": false
}
}
}
}
}
},
"responses": {
"200": {
"description": "KYC status updated successfully",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/Profile"
},
"examples": {
"updatedProfile": {
"summary": "Example of profile with updated KYC status",
"value": {
"id": "550e8400-e29b-41d4-a716-446655440000",
"name": "Bob Smith",
"public_key": "03a1b2c3d4e5f6a7b8c9d0e1f2a3b4c5d6e7f8a9b0c1d2e3f4a5b6c7d8e9f0a1b2",
"kyc_verified": true
}
}
}
}
}
},
"404": {
"description": "Profile not found",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/Error"
},
"examples": {
"notFound": {
"summary": "Example of profile not found error",
"value": {
"code": 404,
"message": "Profile with ID '550e8400-e29b-41d4-a716-446655440000' not found"
}
}
}
}
}
}
}
}
}
}
}

View File

@@ -1,181 +0,0 @@
module generator
import freeflowuniverse.herolib.core.code { Folder, IFolder, IFile, VFile, CodeItem, File, Function, Import, Module, Struct, CustomCode }
import freeflowuniverse.herolib.schemas.openapi
import freeflowuniverse.herolib.core.texttools
import freeflowuniverse.herolib.baobab.specification {ActorMethod, ActorSpecification, ActorInterface}
import json
@[params]
pub struct Params {
pub:
interfaces []ActorInterface // the interfaces to be supported
}
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{}
mut spec_files := []IFile{}
// generate code files for supported interfaces
for iface in params.interfaces {
match iface {
.openrpc {
// convert actor spec to openrpc spec
openrpc_spec := spec.to_openrpc()
spec_files << generate_openrpc_file(openrpc_spec)!
}
.openapi {
// 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)!
}
else {}
}
}
specs_folder := Folder {
name: 'specs'
files: spec_files
}
// folder with docs
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}_actor'
files: files
folders: folders
modules: [generate_actor_module(spec, params)!]
}
}
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 code files for supported interfaces
for iface in params.interfaces {
match iface {
.openrpc {
// convert actor spec to openrpc spec
openrpc_spec := spec.to_openrpc()
iface_file, iface_test_file := generate_openrpc_interface_files(params.interfaces)
files << iface_file
files << iface_test_file
}
.openapi {
// convert actor spec to openrpc spec
openapi_spec_raw := spec.to_openapi()
openapi_spec := openapi.process(openapi_spec_raw)!
// generate openrpc code files
iface_file, iface_test_file := generate_openapi_interface_files(params.interfaces)
files << iface_file
files << iface_test_file
}
.http {
// interfaces that have http controllers
controllers := params.interfaces.filter(it == .openrpc || it == .openapi)
// generate openrpc code files
iface_file, iface_test_file := generate_http_interface_files(controllers)
files << iface_file
files << iface_test_file
}
.command {
files << generate_command_file(spec)!
}
else {
return error('unsupported interface ${iface}')
}
}
}
// create module with code files and docs folder
name_fixed := texttools.snake_case(spec.name)
return code.new_module(
name: '${name_fixed}_actor'
description: spec.description
files: files
folders: folders
in_src: true
)
}
fn generate_readme_file(spec ActorSpecification) !File {
return File{
name: 'README'
extension: 'md'
content: '# ${spec.name}\n${spec.description}'
}
}
fn generate_actor_file(spec ActorSpecification) !VFile {
dollar := '$'
version := spec.version
name_snake := texttools.snake_case(spec.name)
name_pascal := texttools.snake_case_to_pascal(spec.name)
actor_code := $tmpl('./templates/actor.v.template')
return VFile {
name: 'actor'
items: [CustomCode{actor_code}]
}
}
fn generate_actor_test_file(spec ActorSpecification) !VFile {
dollar := '$'
actor_name_snake := texttools.snake_case(spec.name)
actor_name_pascal := texttools.snake_case_to_pascal(spec.name)
actor_test_code := $tmpl('./templates/actor_test.v.template')
return VFile {
name: 'actor_test'
items: [CustomCode{actor_test_code}]
}
}
fn generate_specs_file(name string, interfaces []ActorInterface) !VFile {
support_openrpc := ActorInterface.openrpc in interfaces
support_openapi := ActorInterface.openapi in interfaces
dollar := '$'
actor_name_snake := texttools.snake_case(name)
actor_name_pascal := texttools.snake_case_to_pascal(name)
actor_code := $tmpl('./templates/specifications.v.template')
return VFile {
name: 'specifications'
items: [CustomCode{actor_code}]
}
}
pub fn generate_examples_folder() !Folder {
return Folder {
name: 'examples'
}
}

View File

@@ -37,7 +37,7 @@ fn generate_methods_receiver(name string) code.Struct {
return code.Struct {
is_pub: true
name: '${texttools.pascal_case(name)}'
embeds: [code.Struct{name:'OSIS'}]
fields: [code.StructField{is_mut: true, name: 'osis', typ:code.Object{'OSIS'}}]
}
}
@@ -45,7 +45,7 @@ fn generate_core_factory(receiver code.Param) code.Function {
return code.Function {
is_pub: true
name: 'new_${receiver.typ.symbol()}'
body: "return ${receiver.typ.symbol().trim_left('!?')}{OSIS: osis.new()!}"
body: "return ${receiver.typ.symbol().trim_left('!?')}{osis: osis.new()!}"
result: receiver
}
}
@@ -64,11 +64,11 @@ pub fn generate_method_code(receiver code.Param, method ActorMethod) ![]CodeItem
// check if method is a Base Object CRUD Method and
// if so generate the method's body
body := match method.category {
.base_object_new { base_object_new_body(method)! }
.base_object_get { base_object_get_body(method)! }
.base_object_set { base_object_set_body(method)! }
.base_object_delete { base_object_delete_body(method)! }
.base_object_list { base_object_list_body(method)! }
.base_object_new { base_object_new_body(receiver, method)! }
.base_object_get { base_object_get_body(receiver, method)! }
.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')"}
}
@@ -93,29 +93,29 @@ pub fn generate_method_prototype(receiver code.Param, method ActorMethod) !Funct
}
}
fn base_object_new_body(method ActorMethod) !string {
fn base_object_new_body(receiver Param, method ActorMethod) !string {
parameter := content_descriptor_to_parameter(method.parameters[0])!
return 'return actor.osis.new[${parameter.typ.vgen()}](${texttools.snake_case(parameter.name)})!'
return 'return ${receiver.name}.osis.new[${parameter.typ.vgen()}](${texttools.snake_case(parameter.name)})!'
}
fn base_object_get_body(method ActorMethod) !string {
fn base_object_get_body(receiver Param, method ActorMethod) !string {
parameter := content_descriptor_to_parameter(method.parameters[0])!
result := content_descriptor_to_parameter(method.result)!
return 'return actor.osis.get[${result.typ.vgen()}](${texttools.snake_case(parameter.name)})!'
return 'return ${receiver.name}.osis.get[${result.typ.vgen()}](${texttools.snake_case(parameter.name)})!'
}
fn base_object_set_body(method ActorMethod) !string {
fn base_object_set_body(receiver Param, method ActorMethod) !string {
parameter := content_descriptor_to_parameter(method.parameters[0])!
return 'return actor.osis.set[${parameter.typ.vgen()}](${parameter.name})!'
return 'return ${receiver.name}.osis.set[${parameter.typ.vgen()}](${parameter.name})!'
}
fn base_object_delete_body(method ActorMethod) !string {
fn base_object_delete_body(receiver Param, method ActorMethod) !string {
parameter := content_descriptor_to_parameter(method.parameters[0])!
return 'actor.osis.delete(${texttools.snake_case(parameter.name)})!'
return '${receiver.name}.osis.delete(${texttools.snake_case(parameter.name)})!'
}
fn base_object_list_body(method ActorMethod) !string {
fn base_object_list_body(receiver Param, method ActorMethod) !string {
result := content_descriptor_to_parameter(method.result)!
base_object_type := (result.typ as Array).typ
return 'return actor.osis.list[${base_object_type.symbol()}]()!'
return 'return ${receiver.name}.osis.list[${base_object_type.symbol()}]()!'
}