make schema models and methods more defensive

This commit is contained in:
timurgordon
2025-01-03 01:41:42 -05:00
parent ff8ee3693c
commit d49e94e412
3 changed files with 59 additions and 48 deletions

View File

@@ -8,7 +8,7 @@ pub struct Request {
pub mut:
jsonrpc string @[required] // JSON-RPC version, e.g., "2.0"
method string @[required] // Method to invoke
params string @[required] // JSON-encoded parameters
params string // JSON-encoded parameters
id string @[required] // Unique request ID
}
@@ -32,12 +32,23 @@ pub fn (req Request) encode() string {
return json2.encode(req)
}
// Validates that the response does not contain both `result` and `error`.
pub fn (req Request) validate() ! {
if req.jsonrpc == '' {
return error('request jsonrpc version not specified')
} else if req.id == '' {
return error('request id is empty')
} else if req.method == '' {
return error('request method is empty')
}
}
// A generic JSON-RPC request struct allowing strongly-typed parameters.
pub struct RequestGeneric[T] {
pub mut:
jsonrpc string @[required]
method string @[required]
params T @[required]
params T
id string @[required]
}

View File

@@ -15,15 +15,15 @@ pub fn json_decode(data string) !OpenAPI {
// Decode all schema and schemaref fields using `jsonschema.decode_schemaref`
// 1. Process components.schemas
if 'paths' in raw_map {
mut paths := raw_map['paths'].as_map()
if paths_any := raw_map['paths'] {
mut paths := paths_any.as_map()
for key, path in paths {
spec.paths[key] = json_decode_path(spec.paths[key], path.as_map())!
}
}
if 'components' in raw_map {
components_map := raw_map['components'].as_map()
if components_any := raw_map['components'] {
components_map := components_any.as_map()
spec.components = json_decode_components(spec.components, components_map)!
}
@@ -34,8 +34,8 @@ pub fn json_decode(data string) !OpenAPI {
pub fn json_decode_components(components_ Components, components_map map[string]Any) !Components {
mut components := components_
if 'schemas' in components_map {
components.schemas = jsonschema.decode_schemaref_map(components_map['schemas'].as_map())!
if schemas_any := components_map['schemas'] {
components.schemas = jsonschema.decode_schemaref_map(schemas_any.as_map())!
}
return components
}
@@ -44,37 +44,33 @@ pub fn json_decode_path(path_ PathItem, path_map map[string]Any) !PathItem {
mut path := path_
for key in path_map.keys() {
operation_any := path_map[key] or {
panic('This should never happen')
}
operation_map := operation_any.as_map()
match key {
'get' {
operation_map := path_map[key].as_map()
path.get = json_decode_operation(path.get, operation_map)!
}
'post' {
operation_map := path_map[key].as_map()
path.post = json_decode_operation(path.post, operation_map)!
}
'put' {
operation_map := path_map[key].as_map()
path.put = json_decode_operation(path.put, operation_map)!
}
'delete' {
operation_map := path_map[key].as_map()
path.delete = json_decode_operation(path.delete, operation_map)!
}
'options' {
operation_map := path_map[key].as_map()
path.options = json_decode_operation(path.options, operation_map)!
}
'head' {
operation_map := path_map[key].as_map()
path.head = json_decode_operation(path.head, operation_map)!
}
'patch' {
operation_map := path_map[key].as_map()
path.patch = json_decode_operation(path.patch, operation_map)!
}
'trace' {
operation_map := path_map[key].as_map()
path.trace = json_decode_operation(path.trace, operation_map)!
}
else {
@@ -88,42 +84,41 @@ pub fn json_decode_path(path_ PathItem, path_map map[string]Any) !PathItem {
pub fn json_decode_operation(operation_ Operation, operation_map map[string]Any) !Operation {
mut operation := operation_
if 'requestBody' in operation_map {
request_body_any := operation_map['requestBody']
if request_body_any := operation_map['requestBody'] {
request_body_map := request_body_any.as_map()
if 'content' in request_body_map {
if content_any := request_body_map['content'] {
mut request_body := json.decode(RequestBody, request_body_any.str())!
// mut request_body := operation.request_body as RequestBody
mut content := request_body.content.clone()
content_map := request_body_map['content'].as_map()
content_map := content_any.as_map()
request_body.content = json_decode_content(content, content_map)!
operation.request_body = request_body
}
}
if 'responses' in operation_map {
responses_map := operation_map['responses'].as_map()
if responses_any := operation_map['responses'] {
responses_map := responses_any.as_map()
for key, response_any in responses_map {
response_map := response_any.as_map()
if 'content' in response_map {
if content_any := response_map['content'] {
mut response := operation.responses[key]
mut content := response.content.clone()
content_map := response_map['content'].as_map()
content_map := content_any.as_map()
response.content = json_decode_content(content, content_map)!
operation.responses[key] = response
}
}
}
if 'parameters' in operation_map {
parameters_arr := operation_map['parameters'].arr()
if parameters_any := operation_map['parameters'] {
parameters_arr := parameters_any.arr()
mut parameters := []Parameter{}
for i, parameter_any in parameters_arr {
parameter_map := parameter_any.as_map()
if 'schema' in parameter_map {
if schema_any := parameter_map['schema'] {
mut parameter := operation.parameters[i]
parameter.schema = jsonschema.decode_schemaref(parameter_map['schema'].as_map())!
parameter.schema = jsonschema.decode_schemaref(schema_any.as_map())!
parameters << parameter
} else {
parameters << operation.parameters[i]
@@ -139,9 +134,10 @@ fn json_decode_content(content_ map[string]MediaType, content_map map[string]Any
mut content := content_.clone()
for key, item in content_map {
media_type_map := item.as_map()
schema_any := media_type_map['schema']
mut media_type := content[key]
media_type.schema = jsonschema.decode_schemaref(schema_any.as_map())!
if schema_any := media_type_map['schema'] {
media_type.schema = jsonschema.decode_schemaref(schema_any.as_map())!
}
content[key] = media_type
}
return content

View File

@@ -13,19 +13,20 @@ pub fn decode(data string) !OpenRPC {
}
}
for i, method in data_map['methods'].arr() {
methods_any := data_map['methods'] or {return object}
for i, method in methods_any.arr() {
method_map := method.as_map()
params_arr := method_map['params'].arr()
result := if 'result' in method_map {
method_map['result']
} else {
''
if result_any := method_map['result'] {
object.methods[i].result = decode_content_descriptor_ref(result_any.as_map()) or {
return error('Failed to decode result\n${err}')
}
}
object.methods[i].params = params_arr.map(decode_content_descriptor_ref(it.as_map()) or {
return error('Failed to decode params\n${err}')
})
object.methods[i].result = decode_content_descriptor_ref(result.as_map()) or {
return error('Failed to decode result\n${err}')
if params_any := method_map['params'] {
params_arr := params_any.arr()
object.methods[i].params = params_arr.map(decode_content_descriptor_ref(it.as_map()) or {
return error('Failed to decode params\n${err}')
})
}
}
// object.methods = decode_method(data_map['methods'].as_array)!
@@ -50,18 +51,21 @@ pub fn decode(data string) !OpenRPC {
fn decode_components(data_map map[string]Any) !Components {
mut components := Components{}
components_map := data_map['components'].as_map()
mut components_map := map[string]Any
if components_any := data_map['components'] {
components_map = components_any.as_map()
}
if 'contentDescriptors' in components_map {
descriptors_map := components_map['contentDescriptors'].as_map()
if cd_any := components_map['contentDescriptors'] {
descriptors_map := cd_any.as_map()
for key, value in descriptors_map {
descriptor := decode_content_descriptor_ref(value.as_map())!
components.content_descriptors[key] = descriptor
}
}
if 'schemas' in components_map {
schemas_map := components_map['schemas'].as_map()
if schemas_any := components_map['schemas'] {
schemas_map := schemas_any.as_map()
for key, value in schemas_map {
schema := jsonschema.decode(value.str())!
components.schemas[key] = schema
@@ -72,9 +76,9 @@ fn decode_components(data_map map[string]Any) !Components {
}
fn decode_content_descriptor_ref(data_map map[string]Any) !ContentDescriptorRef {
if '\$ref' in data_map {
if ref_any := data_map['\$ref'] {
return Reference{
ref: data_map['\$ref'].str()
ref: ref_any.str()
}
}
mut descriptor := json.decode(ContentDescriptor, data_map.str())!