This commit is contained in:
2025-08-05 15:15:36 +02:00
parent 4bd960ed05
commit 7fabb4163a
192 changed files with 14901 additions and 0 deletions

View File

@@ -0,0 +1,188 @@
from typing import Dict, List, Set, Any
class VlangCodeGenerator:
pass
class ModelGenerator:
def __init__(
self, spec: Dict[str, Any], lang_code_generator: str
) -> None:
self.spec = spec
self.lang_code_generator = lang_code_generator
# self.processed_objects: Dict[str, Dict[str, str]] = {}
# self.ordered_objects: List[str] = []
# self.used_names: Set[str] = set()
def generate_models(self):
if self.lang_code_generator != "vlang":
raise ValueError('Unsupported language.')
if not self.spec.get('components'):
raise ValueError("No components found in spec")
components = self.spec['components']
if not components.get('schemas'):
raise ValueError("No schemas found in components")
schemas = components['schemas']
schemas_path = ["components", "schemas"]
for name, schema in schemas.items():
self.jsonschema_to_type(
path=schemas_path + [name],
jsonschema=schema,
)
objects_code = ""
for val in self.ordered_objects:
if val == "":
continue
objects_code = f"{objects_code}{val}\n\n"
print(f'debugzo4 {objects_code}')
return objects_code
# def jsonschema_to_type(
# self, path: List[str], jsonschema: SchemaObject | ReferenceObject
# ) -> str:
# if isinstance(jsonschema, ReferenceObject):
# ref: str = jsonschema.ref
# ref_schema = self.spec.ref_to_schema(ref)
# ref_path = ref.split("/")[1:]
# if isinstance(ref_schema, ContentDescriptorObject):
# # TODO: implement
# raise Exception("unimplemented")
# # return self.content_descriptor_to_type(ref_path, ref_schema)
# return self.jsonschema_to_type(ref_path, ref_schema)
# path_str = "/".join([item.lower() for item in path])
# if path_str in self.processed_objects:
# return self.processed_objects[path_str]["name"]
# type_name = self.type_name_from_path(path)
# description = getattr(jsonschema, 'description', None)
# if jsonschema.enum:
# enum = jsonschema.enum
# type_code = self.lang_code_generator.generate_enum(enum, type_name)
# if self.lang_code_generator.is_primitive(type_code):
# return type_code
# self.add_object(path_str, type_code, type_name)
# return type_name
# if jsonschema.type:
# match jsonschema.type:
# case "string":
# return self.lang_code_generator.string_primitive()
# case "integer":
# return self.lang_code_generator.integer_primitive()
# case "number":
# return self.lang_code_generator.number_primitive()
# case "array":
# if isinstance(jsonschema.items, List):
# raise Exception(
# "array of different item types is not supported"
# )
# item_type_name = self.jsonschema_to_type(
# path + ["item"], jsonschema.items
# )
# return self.lang_code_generator.array_of_type(
# item_type_name
# )
# case "boolean":
# return self.lang_code_generator.bool_primitive()
# case "object":
# # to prevent cyclic dependencies
# self.add_object(path_str, "", type_name)
# properties: Dict[str, PropertyInfo] = {}
# for (
# property_name,
# property_schema,
# ) in jsonschema.properties.items():
# schema = property_schema
# new_path = path + ["properties", property_name]
# if isinstance(property_schema, ReferenceObject):
# schema = self.spec.ref_to_schema(
# property_schema.ref
# )
# new_path = property_schema.ref.split("/")[1:]
# property_info = PropertyInfo(
# name=property_name,
# type_name=self.jsonschema_to_type(new_path, schema),
# description=schema.description,
# example=schema.example,
# )
# properties[property_name] = property_info
# type_code = self.lang_code_generator.generate_object(
# type_name, properties
# )
# self.add_object(path_str, type_code, type_name)
# return type_name
# case "null":
# return self.lang_code_generator.null_primitive()
# case _:
# raise Exception(f"type {jsonschema.type} is not supported")
# if jsonschema.anyOf:
# type_names = []
# for i, item in enumerate(jsonschema.anyOf):
# type_names.append(
# self.jsonschema_to_type(path + [f"anyOf{i}"], item)
# )
# return self.lang_code_generator.generate_multitype(type_names)
# # self.add_object(path_str, type_code, type_code)
# # return type_code
# elif jsonschema.oneOf:
# type_names = []
# for i, item in enumerate(jsonschema.oneOf):
# type_names.append(
# self.jsonschema_to_type(path + [f"oneOf{i}"], item)
# )
# return self.lang_code_generator.generate_multitype(type_names)
# # self.add_object(path_str, type_code, type_code)
# # return type_code
# elif jsonschema.allOf:
# return self.lang_code_generator.encapsulate_types(jsonschema.allOf)
# # self.add_object(path_str, type_code, type_code)
# # return type_name
# raise Exception(f"type {jsonschema.type} is not supported")
# def add_object(self, path_str: str, type_code: str, type_name: str):
# self.used_names.add(type_name)
# self.processed_objects[path_str] = {
# "code": type_code,
# "name": type_name,
# }
# print(f'debugzo21 {self.processed_objects[path_str]}')
# self.ordered_objects.append(type_code)
# def type_name_from_path(self, path: List[str]) -> str:
# type_name = ""
# for item in reversed(path):
# type_name += item.title() if item.islower() else item
# if type_name not in self.used_names:
# return type_name
# raise Exception(f"failed to generate unique name from path: {path}")

View File

@@ -0,0 +1,9 @@
pub enum {{ type_name }}{
{% for elem in enum -%}
{% if is_integer -%}
{{ number_to_words(elem) }} = {{ elem }}
{% else -%}
{{ elem }}
{% endif -%}
{% endfor %}
}

View File

@@ -0,0 +1,77 @@
pub struct {{ actor_executor_name }}{
pub mut:
db &backend.Backend
redis &redisclient.Redis
}
pub fn (mut executor {{ actor_executor_name }}) execute(rpc_msg_id string, rpc_msg_method string, rpc_msg_params_str string) {
raw_params := json2.raw_decode(rpc_msg_params_str) or{
executor.return_error(rpc_msg_id, jsonrpc.invalid_params)
return
}
params_arr := raw_params.arr()
match rpc_msg_method {
{%- for method in methods %}
'{{method.name}}' {
{%- for param in method.params %}
{%- if generator.is_primitive(generator.get_param_type(method.name, param))%}
{{param.name}} := params_arr[{{loop.index0}}] as {{generator.get_param_type(method.name, param)}}
{%- else %}
{{param.name}} := json.decode({{generator.get_param_type(method.name, param)}}, params_arr[{{loop.index0}}].json_str()) or {
executor.return_error(rpc_msg_id, jsonrpc.invalid_request)
return
}
{%- endif %}
{%- endfor %}
{%- if generator.get_method_return_type(method) == 'none' %}
executor.{{method.name}}_internal({{generator.get_method_params_as_args(method)}}) or {
executor.return_error(rpc_msg_id, jsonrpc.InnerJsonRpcError{
code: 32000
message: '${err}'
})
return
}
response := jsonrpc.JsonRpcResponse[string]{
jsonrpc: '2.0.0'
id: rpc_msg_id
result: ''
}
{%- else %}
result := executor.{{method.name}}_internal({{generator.get_method_params_as_args(method)}}) or {
executor.return_error(rpc_msg_id, jsonrpc.InnerJsonRpcError{
code: 32000
message: '${err}'
})
return
}
response := jsonrpc.JsonRpcResponse[{{generator.get_method_return_type(method)}}]{
jsonrpc: '2.0.0'
id: rpc_msg_id
result: result
}
{%- endif %}
// put response in response queue
executor.redis.lpush(rpc_msg_id, response.to_json()) or {
println('failed to push response for ${rpc_msg_id} to redis queue: ${err}')
}
}
{%- endfor %}
else {
executor.return_error(rpc_msg_id, jsonrpc.method_not_found)
return
}
}
}
pub fn (mut executor {{actor_executor_name}}) return_error(rpc_msg_id string, error jsonrpc.InnerJsonRpcError){
response := jsonrpc.new_jsonrpcerror(rpc_msg_id, error)
executor.redis.lpush(rpc_msg_id, response.to_json()) or {
println('failed to push response for ${rpc_msg_id} to redis queue: ${err}')
}
}

View File

@@ -0,0 +1,50 @@
struct Handler {
pub mut:
db &backend.Backend
redis &redisclient.Redis
{% for actor in actors %}
{{actor}}_executor {{get_actor_executor_name(actor)}}
{%- endfor %}
}
pub fn new(db_config backend.BackendConfig, redis_addr string) !Handler{
db := backend.new(db_config)!
mut redis_client := redisclient.new([redis_addr])!
redis_client.selectdb(0)!
return Handler{
db: &db
redis: &redis_client
{%- for actor in actors %}
{{actor}}_executor: {{get_actor_executor_name(actor)}}{
db: &db
redis: &redis_client
}
{%- endfor %}
}
}
// handle handles an incoming JSON-RPC encoded message and returns an encoded response
pub fn (mut handler Handler) handle(id string, method string, params_str string) {
actor := method.all_before('.')
method_name := method.all_after('.')
match actor {
{%- for actor in actors %}
'{{ actor }}' {
spawn (&handler.{{actor}}_executor).execute(id, method_name, params_str)
}
{%- endfor %}
else {
handler.return_error(id, jsonrpc.method_not_found)
return
}
}
}
pub fn (mut handler Handler) return_error(rpc_msg_id string, error jsonrpc.InnerJsonRpcError){
response := jsonrpc.new_jsonrpcerror(rpc_msg_id, error)
handler.redis.lpush(rpc_msg_id, response.to_json()) or {
println('failed to push response for ${rpc_msg_id} to redis queue: ${err}')
}
}

View File

@@ -0,0 +1,31 @@
module myhandler
import x.json2
import rand
import freeflowuniverse.crystallib.baobab.backend
fn test_handler(){
db_config := backend.BackendConfig{
name: 'myhandler'
secret: 'secret'
reset: true
db_type: .postgres
}
mut handler := new(db_config, '127.0.0.1:6379')!
{% for method_name in method_names %}
do_request(mut handler, '{{method_name}}')!
{%- endfor %}
}
fn do_request(mut handler Handler, method_name string) ! {
// TODO: edit input parameters
mut params := []json2.Any{}
params << "objid"
params << "blabla_name"
params_str := json2.Any(params).json_str()
id := rand.string(6)
handler.handle(rand.string(6), method_name, json2.Any(params).json_str())
println('request id: ${id}')
}

View File

@@ -0,0 +1,7 @@
pub fn (mut executor {{ actor_executor_name }}) {{function_name}}({{method_params}}) !{{return_type}}{
// context allows us to see who the user is and which groups the user is
// context also gives a logging feature
// context is linked to 1 circle
// context is linked to a DB (OSIS)
panic('implement')
}

View File

@@ -0,0 +1,28 @@
pub fn (mut executor {{ actor_executor_name }}) {{variable_name}}_get_internal(id string) !{{type_name}}{
json_str := executor.db.indexer.get_json(id, backend.RootObject{
name: '{{type_name}}'
})!
return json.decode({{type_name}}, json_str)!
}
pub fn (mut executor {{ actor_executor_name }}) {{variable_name}}_set_internal({{variable_name}} {{type_name}}) !{
if {{variable_name}}.oid != ''{
executor.db.indexer.set(backend.RootObject{
id: {{variable_name}}.oid
name: '{{type_name}}'
})!
}
executor.db.indexer.new(backend.RootObject{
name: '{{type_name}}'
})!
}
pub fn (mut executor {{ actor_executor_name }}) {{variable_name}}_delete_internal(id string) !{
executor.db.indexer.delete(id, backend.RootObject{
name: '{{type_name}}'
})!
}

View File

@@ -0,0 +1,5 @@
pub struct {{method_param_struct_name}}{
{% for param_name, param_type in params.items()%}
{{param_name}} {{param_type}}
{%- endfor %}
}

View File

@@ -0,0 +1,75 @@
{% if method_example -%}
/*
Example:
{{ method_example }}
*/
{% endif -%}
{% if method_description -%}
/*
{{ method_description }}
*/
{% endif -%}
pub fn {{ function_name }}({{ vlang_code_generator.get_method_params(method_params) }}) {{ method_result }}{
mut conn := httpconnection.new(
name: 'openrpc_client'
url: '{{ base_url }}'
)!
mut params := map[string]json2.Any{}
{% for param_name, param_type in method_params.items() -%}
{% if vlang_code_generator.is_primitive(param_type) %}
params["{{ param_name }}"] = {{ param_name }}
{% elif vlang_code_generator.is_vlang_array(param_type) %}
mut any_arr := []json2.Any{}
for item in {{ param_name }}{
{% if vlang_code_generator.is_primitive(param_type[2:]) %}
any_arr << item
{% else %}
any_arr << json2.raw_decode(json2.encode(item))!
{% endif %}
}
params["{{ param_name }}"] = json2.Any(any_arr)
{%else %}
params["{{ param_name }}"] = json2.raw_decode(json2.encode({{ param_name }}))!
{% endif %}
{% endfor -%}
mut payload := map[string]json2.Any{}
payload['jsonrpc'] = "2.0"
payload['id'] = 0
payload['method'] = '{{ method_name }}'
payload['params'] = params
response := conn.send(method: .post, data: json2.encode(payload){% if url_path -%}, prefix: '{{ url_path }}' {% endif -%})!
if !response.is_ok() {
return error('failed to make rpc request: (${response.code}) ${response.data}')
}
{% if return_type != 'none' %}
mp := json2.raw_decode(response.data)!.as_map()
res := mp['result'] or {
return error('invalid jsonrpc result: ${response.data}')
}
if res is json2.Null{
return error('not found')
}
{% if vlang_code_generator.is_primitive(return_type) %}
return res as {{return_type}}
{% elif vlang_code_generator.is_vlang_array(return_type) %}
mut res_arr := {{return_type}}
for item in res.arr() {
{% if vlang_code_generator.is_primitive(return_type[2:]) %}
res_arr << item as {{return_type}}
{% else %}
res_arr << json2.decode[{{return_type[2:]}}](item.json_str())!
{% endif %}
}
return res_arr
{%else %}
return json2.decode[{{return_type}}](res.json_str())!
{% endif -%}
{% endif %}
}

View File

@@ -0,0 +1,5 @@
module {{module_name}}
{% for item in imports %}
import {{item}}
{%- endfor %}

View File

@@ -0,0 +1,10 @@
@[params]
pub struct {{ struct_name }}{
pub mut:
{%- for property_name, property_info in properties.items() %}
{%- if property_info.description %}
// {{ property_info.description }}
{%- endif %}
{{ property_name }} {{ property_info.type_name }}
{%- endfor %}
}

View File

@@ -0,0 +1,231 @@
from openapi_python_client.schema import OpenAPI, Schema, Reference
from jinja2 import Environment, FileSystemLoader
from typing import Dict, Any
import os
script_dir = os.path.dirname(os.path.abspath(__file__))
env = Environment(loader=FileSystemLoader(script_dir))
class VlangCodeGenerator:
def __init__(self, python_code: OpenAPI, output_dir: str) -> None:
self.python_code = python_code
self.output_dir = output_dir
self.struct_template = env.get_template("templates/struct.jinja")
def generate(self):
"""
Main generation method to create V code.
"""
# Ensure the output directory exists
os.makedirs(self.output_dir, exist_ok=True)
structs = self._generate_structs()
print('structs: ', structs)
# methods = self._generate_methods()
# # Combine structs and methods into one file
vlang_code = structs
output_file = f"{self.output_dir}/generated.v"
# Write to file
with open(output_file, "w") as file:
file.write(vlang_code)
print(f"Vlang code generated at {output_file}")
def _generate_struct(self, struct_name: str, scheme: Schema | Reference) -> str:
properties = {}
code = ""
for field_name, field in scheme.properties.items(): # type: ignore
v_type = self._convert_type(field.type) # type: ignore
if field.type == 'object': # type: ignore
# Capitalize each part of the field name and create a nested struct name
nested_struct_name = ''.join(part.capitalize() for part in field_name.split("_"))
# Generate the struct for the nested object
code += self._generate_struct(struct_name=nested_struct_name, scheme=field)
# Update v_type to the newly generated nested struct name
v_type = nested_struct_name
# Update the properties dictionary with type name and description
properties[field_name] = {
'type_name': v_type,
'description': field.description # type: ignore
}
code += "\n"
code += self.struct_template.render(
struct_name=struct_name,
properties= properties # type: ignore
)
code += "\n"
return code
def _generate_structs(self) -> str:
"""
Generate V structs from OpenAPI components with support for nested objects and arrays.
"""
if not self.python_code.components:
raise ValueError("No components found in spec")
if not self.python_code.components.schemas:
raise ValueError("No schemas found in components")
code = ""
for struct_name, schema in self.python_code.components.schemas.items():
code += self._generate_struct(struct_name=struct_name, scheme=schema)
return code
# structs_code = []
# for schema_name, schema in self.python_code.components.schemas.items():
# fields = []
# for field_name, field in schema.properties.items(): # type: ignore
# if field.type == "object": # type: ignore
# # Generate a nested struct
# parts = field_name.split("_")
# nested_struct_name = ""
# for part in parts:
# nested_struct_name += part.capitalize()
# nested_struct = self._generate_struct_from_object(nested_struct_name, field) # type: ignore
# structs_code.append(nested_struct)
# fields.append(f"\t{field_name} {nested_struct_name}")
# print(f"Generated struct for {nested_struct_name}")
# elif field.type == "array": # type: ignore
# # Handle arrays with proper type conversion for items
# item_type = self._convert_type(field.items.type) # type: ignore
# fields.append(f"\t{field_name} []{item_type}")
# else:
# # Convert JSON schema type to V type
# v_type = self._convert_type(field.type) # type: ignore
# fields.append(f"\t{field_name} {v_type}")
# # Construct struct
# struct_code = f"pub struct {schema_name} {{\n" + "\n".join(fields) + "\n}"
# structs_code.append(struct_code)
# print(f"Generated struct for {schema_name}")
# return "\n\n".join(structs_code)
# def _generate_struct_from_object(self, struct_name: str, schema: dict) -> str:
# """
# Generate a nested struct from an object schema.
# """
# fields = []
# for field_name, field in schema.properties.items(): # type: ignore
# v_type = self._convert_type(field.type) # type: ignore
# fields.append(f"\t{field_name} {v_type}")
# return f"struct {struct_name} {{\n" + "\n".join(fields) + "\n}"
# def _generate_methods(self) -> str:
# """
# Generate V methods based on OpenAPI paths and operations.
# """
# if not self.python_code.paths:
# raise ValueError("No paths found in spec")
# methods_code = []
# for path, path_item in self.python_code.paths.items():
# # Explicitly check for HTTP method attributes in PathItem
# for http_method in ["get", "post", "put", "delete", "patch", "options", "head"]:
# operation = getattr(path_item, http_method, None)
# if operation:
# # Generate method name and parameters
# method_name = self._generate_method_name(http_method, path)
# parameters = self._generate_method_parameters(operation.parameters)
# request_body = self._generate_request_body(operation.request_body)
# response_type = self._generate_response_type(operation.responses)
# # Combine method arguments
# method_arguments = parameters
# if request_body:
# method_arguments += f", {request_body}" if parameters else request_body
# # Generate the method code
# method_code = f"fn {method_name}({method_arguments}) {response_type} {{\n"
# method_code += f"\t// TODO: Implement the {http_method.upper()} request to {path}\n"
# method_code += "\t// Use the generated structs for request/response bodies\n"
# method_code += "}\n"
# methods_code.append(method_code)
# print(f"Generated method for {http_method.upper()} {path}")
# return "\n\n".join(methods_code)
# def _generate_method_name(self, http_method: str, path: str) -> str:
# """
# Generate a method name from the HTTP method and path.
# """
# # Remove leading/trailing slashes and replace `/` with `_`
# sanitized_path = path.strip("/").replace("/", "_").replace("{", "").replace("}", "")
# return f"{http_method.lower()}_{sanitized_path}"
# def _generate_method_parameters(self, parameters) -> str:
# if not parameters:
# return ""
# param_list = []
# for param in parameters:
# param_name = param.name
# param_schema = getattr(param, "schema", None)
# print('param_name: ', param_name)
# print('param_schema: ', param_schema)
# # if param_schema and param_schema.type:
# # param_type = self._convert_type(param_schema.type)
# # param_list.append(f"{param_name} {param_type}")
# return ", ".join(param_list)
# def _generate_request_body(self, request_body) -> str:
# """
# Generate a function parameter for the request body if present.
# """
# if not request_body or not request_body.content:
# return ""
# # Assume application/json content type
# json_schema = request_body.content.get("application/json")
# if not json_schema or not json_schema.schema:
# return ""
# print('body_type: ', json_schema)
# # body_type = json_schema.schema.ref.split("/")[-1] # Extract the schema name
# return f"body {json_schema}"
# def _generate_response_type(self, responses) -> str:
# """
# Determine the return type of the method based on responses.
# """
# if not responses:
# return "void"
# for status_code, response in responses.items():
# if response.content and "application/json" in response.content:
# json_schema = response.content["application/json"].schema
# print('json_schema: ', json_schema)
# # if json_schema and json_schema.ref:
# # return json_schema.ref.split("/")[-1] # Extract schema name
# return "void"
def _convert_type(self, json_type: str) -> str:
"""
Map JSON schema types to Vlang types.
"""
type_mapping = {
"string": "string",
"integer": "int",
"number": "f64",
"boolean": "bool",
"array": "[]",
}
return type_mapping.get(json_type, "string") # Default to `string`

View File

@@ -0,0 +1,50 @@
from openapi_python_client.schema import OpenAPI
import json
import yaml
from generator.server.vlang.vlang import VlangCodeGenerator
class OpenApiCodeGenerator:
def __init__(self, lang: str, spec_file: str, output_dir: str):
self.lang = lang
self.spec_file = spec_file
self.output_dir = output_dir
def _read_file(self):
"""
Read the OpenAPI spec file.
"""
if self.spec_file.endswith(".json"):
with open(self.spec_file, "r") as file:
return file.read() # Return raw JSON string
elif self.spec_file.endswith(".yaml"):
with open(self.spec_file, "r") as file:
# Convert YAML to JSON string for compatibility
return json.dumps(yaml.safe_load(file))
else:
raise ValueError("Unsupported file format")
def generate(self):
"""
Main generation logic for code based on the OpenAPI spec.
"""
file_content = self._read_file()
openapi = OpenAPI.model_validate_json(file_content)
if self.lang == "vlang":
vlang_code_generator = VlangCodeGenerator(
python_code=openapi, output_dir=self.output_dir
)
vlang_code_generator.generate()
elif self.lang == "python":
print("Python code generation not implemented yet.")
if __name__ == "__main__":
s = OpenApiCodeGenerator(
lang="vlang",
spec_file="/home/thunder/work/codescalers/github/hero_server_python/lib/openapi/schema.json",
output_dir="./output"
)
s.generate()

View File

@@ -0,0 +1,136 @@
{
"openapi": "3.0.3",
"info": {
"title": "User Management API",
"version": "1.0.0",
"description": "A simple API to manage users"
},
"servers": [
{
"url": "https://api.example.com/v1",
"description": "Production server"
}
],
"paths": {
"/users": {
"get": {
"summary": "List all users",
"responses": {
"200": {
"description": "A list of users",
"content": {
"application/json": {
"schema": {
"type": "array",
"items": {
"$ref": "#/components/schemas/User"
}
}
}
}
}
}
},
"post": {
"summary": "Create a new user",
"requestBody": {
"required": true,
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/User"
}
}
}
},
"responses": {
"201": {
"description": "User created successfully"
}
}
}
},
"/users/{userId}": {
"get": {
"summary": "Get a user by ID",
"parameters": [
{
"name": "userId",
"in": "path",
"required": true,
"schema": {
"type": "string"
}
}
],
"responses": {
"200": {
"description": "User details",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/User"
}
}
}
},
"404": {
"description": "User not found"
}
}
}
}
},
"components": {
"schemas": {
"User": {
"type": "object",
"properties": {
"id": {
"type": "string",
"example": "123",
"description": "The unique identifier for the user"
},
"name": {
"type": "string",
"example": "John Doe"
},
"email": {
"type": "string",
"example": "john.doe@example.com"
},
"user_profile": {
"type": "object",
"properties": {
"age": {
"type": "integer"
},
"address": {
"type": "string"
}
}
}
},
"required": [
"id",
"name",
"email"
]
},
"UserBalance": {
"type": "object",
"properties": {
"id": {
"type": "string",
"example": "123",
"description": "The unique identifier for the user"
},
"name": {
"type": "string",
"example": "John Doe"
}
}
}
}
}
}

View File

@@ -0,0 +1,68 @@
openapi: 3.0.3
info:
title: User Management API
version: 1.0.0
description: A simple API to manage users
servers:
- url: https://api.example.com/v1
description: Production server
paths:
/users:
get:
summary: List all users
responses:
'200':
description: A list of users
content:
application/json:
schema:
type: array
items:
$ref: '#/components/schemas/User'
post:
summary: Create a new user
requestBody:
required: true
content:
application/json:
schema:
$ref: '#/components/schemas/User'
responses:
'201':
description: User created successfully
/users/{userId}:
get:
summary: Get a user by ID
parameters:
- name: userId
in: path
required: true
schema:
type: string
responses:
'200':
description: User details
content:
application/json:
schema:
$ref: '#/components/schemas/User'
'404':
description: User not found
components:
schemas:
User:
type: object
properties:
id:
type: string
example: '123'
name:
type: string
example: John Doe
email:
type: string
example: john.doe@example.com
required:
- id
- name
- email