openapi json model, processing & parsing fixes
This commit is contained in:
@@ -1,10 +1,11 @@
|
||||
module jsonschema
|
||||
|
||||
import x.json2 { Any }
|
||||
import json
|
||||
|
||||
pub fn decode(data string) !Schema {
|
||||
schema_map := json2.raw_decode(data)!.as_map()
|
||||
mut schema := json2.decode[Schema](data)!
|
||||
mut schema := json.decode(Schema, data)!
|
||||
for key, value in schema_map {
|
||||
if key == 'properties' {
|
||||
schema.properties = decode_schemaref_map(value.as_map())!
|
||||
|
||||
@@ -8,7 +8,7 @@ pub type SchemaRef = Reference | Schema
|
||||
|
||||
pub struct Reference {
|
||||
pub:
|
||||
ref string @[json: 'ref']
|
||||
ref string @[json: '\$ref']
|
||||
}
|
||||
|
||||
pub type Number = int
|
||||
@@ -21,10 +21,10 @@ pub mut:
|
||||
title string @[omitempty]
|
||||
description string @[omitempty]
|
||||
typ string @[json: 'type'; omitempty]
|
||||
properties map[string]SchemaRef @[omitempty]
|
||||
properties map[string]SchemaRef @[json: '-'; omitempty]
|
||||
additional_properties SchemaRef @[json: 'additionalProperties'; omitempty]
|
||||
required []string @[omitempty]
|
||||
items Items @[omitempty]
|
||||
items Items @[json: '-'; omitempty]
|
||||
defs map[string]SchemaRef @[omitempty]
|
||||
one_of []SchemaRef @[json: 'oneOf'; omitempty]
|
||||
format string @[omitempty]
|
||||
|
||||
@@ -83,10 +83,8 @@ 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 request_body_any := operation_map['requestBody'] {
|
||||
request_body_map := request_body_any.as_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
|
||||
@@ -134,11 +132,12 @@ 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()
|
||||
mut media_type := content[key]
|
||||
if schema_any := media_type_map['schema'] {
|
||||
media_type.schema = jsonschema.decode_schemaref(schema_any.as_map())!
|
||||
if mut media_type := content[key] {
|
||||
if schema_any := media_type_map['schema'] {
|
||||
media_type.schema = jsonschema.decode_schemaref(schema_any.as_map())!
|
||||
}
|
||||
content[key] = media_type
|
||||
}
|
||||
content[key] = media_type
|
||||
}
|
||||
return content
|
||||
}
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
module openapi
|
||||
|
||||
import freeflowuniverse.herolib.schemas.jsonschema {Schema, SchemaRef, Reference}
|
||||
import freeflowuniverse.herolib.core.texttools
|
||||
import os
|
||||
|
||||
@[params]
|
||||
@@ -22,5 +24,74 @@ pub fn new(params Params) !OpenAPI {
|
||||
os.read_file(params.path)!
|
||||
} else { params.text }
|
||||
|
||||
return json_decode(text)!
|
||||
specification := json_decode(text)!
|
||||
return process(specification)!
|
||||
}
|
||||
|
||||
fn process(spec OpenAPI) !OpenAPI {
|
||||
mut processed := OpenAPI{...spec}
|
||||
|
||||
for key, schema in spec.components.schemas {
|
||||
if schema is Schema {
|
||||
mut processed_schema := Schema{
|
||||
...schema,
|
||||
id: if schema.id != '' { schema.id }
|
||||
else if schema.title != '' { schema.title }
|
||||
else { key }
|
||||
title: if schema.title != '' { schema.title }
|
||||
else if schema.id != '' { schema.id }
|
||||
else { key }
|
||||
}
|
||||
processed.components.schemas[key] = processed_schema
|
||||
}
|
||||
}
|
||||
|
||||
for path, item in spec.paths {
|
||||
mut processed_item := PathItem{...item}
|
||||
|
||||
processed_item.get = processed.process_operation(item.get, 'get', path)!
|
||||
processed_item.post = processed.process_operation(item.post, 'post', path)!
|
||||
processed_item.put = processed.process_operation(item.put, 'put', path)!
|
||||
processed_item.delete = processed.process_operation(item.delete, 'delete', path)!
|
||||
processed_item.patch = processed.process_operation(item.patch, 'patch', path)!
|
||||
processed_item.options = processed.process_operation(item.options, 'options', path)!
|
||||
processed_item.head = processed.process_operation(item.head, 'head', path)!
|
||||
processed_item.trace = processed.process_operation(item.trace, 'trace', path)!
|
||||
|
||||
processed.paths[path] = processed_item
|
||||
}
|
||||
|
||||
return processed
|
||||
}
|
||||
|
||||
fn (spec OpenAPI) process_operation(op Operation, method string, path string) !Operation {
|
||||
mut processed := Operation{...op}
|
||||
|
||||
if processed.operation_id == '' {
|
||||
processed.operation_id = generate_operation_id(method, path)
|
||||
}
|
||||
|
||||
if op.request_body is RequestBody {
|
||||
if content := op.request_body.content['application/json'] {
|
||||
if content.schema is Reference {
|
||||
mut req_body_ := RequestBody{...op.request_body}
|
||||
req_body_.content['application/json'].schema = SchemaRef(spec.dereference_schema(content.schema)!)
|
||||
processed.request_body = RequestBodyRef(req_body_)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return processed
|
||||
}
|
||||
|
||||
// Helper function to generate a unique operationId
|
||||
fn generate_operation_id(method string, path string) string {
|
||||
// Convert HTTP method and path into a camelCase string
|
||||
method_part := method.to_lower()
|
||||
path_part := path.all_before('{')
|
||||
.replace('/', '_') // Replace slashes with underscores
|
||||
.replace('{', '') // Remove braces around path parameters
|
||||
.replace('}', '') // Remove braces around path parameters
|
||||
|
||||
return texttools.camel_case('${method_part}_${path_part}')
|
||||
}
|
||||
@@ -1,5 +1,6 @@
|
||||
module openapi
|
||||
|
||||
import maps
|
||||
import x.json2 as json {Any}
|
||||
import freeflowuniverse.herolib.schemas.jsonschema {Schema, Reference, SchemaRef}
|
||||
|
||||
@@ -121,6 +122,15 @@ pub mut:
|
||||
path_items map[string]PathItemRef // An object to hold reusable Path Item Object.
|
||||
}
|
||||
|
||||
pub fn (s OpenAPI) dereference_schema(ref Reference) !Schema {
|
||||
schema_ref := s.components.schemas[ref.ref.all_after_last('/')] or {
|
||||
return error('Reference ${ref} not found in schema')
|
||||
}
|
||||
if schema_ref is Schema {
|
||||
return schema_ref
|
||||
}
|
||||
return error('Reference references another reference')
|
||||
}
|
||||
|
||||
type Items = SchemaRef | []SchemaRef
|
||||
|
||||
@@ -176,6 +186,15 @@ pub mut:
|
||||
servers []ServerSpec @[omitempty]// An alternative server array to service this operation. If an alternative server object is specified at the Path Item Object or Root level, it will be overridden by this value.
|
||||
}
|
||||
|
||||
// returns errors responses amongst a map of response specs
|
||||
pub fn (r map[string]ResponseSpec) errors() map[string]ResponseSpec {
|
||||
return maps.filter(r, fn (k string, v ResponseSpec) bool {
|
||||
return if k.is_int() {
|
||||
k.int() >= 400 && k.int()< 600
|
||||
} else { false }
|
||||
})
|
||||
}
|
||||
|
||||
// TODO: currently using map[string]ResponseSpec
|
||||
pub struct Responses {
|
||||
pub:
|
||||
|
||||
Reference in New Issue
Block a user