Something went wrong. Try again.
OpenAPI generator for OCaml with Requests/Eio/Jsont
Something went wrong. Try again.
37 kB · 877 lines
OCaml
at main
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878(** OpenAPI 3.x specification types with nox-json codecs.
This module defines types that mirror the OpenAPI 3.0/3.1 specification, with bidirectional JSON codecs using nox-json. *)
(** {1 Reference handling} *)
type 'a or_ref = | Ref of string (** A $ref pointer like "#/components/schemas/Pet" *) | Value of 'a (** An inline value *)
(** Find a member by name in an object's member list *)let find_member name (mems : Json.member list) : Json.t option = List.find_map (fun ((n, _meta), v) -> if n = name then Some v else None ) mems
(** Create an or_ref codec that handles $ref pointers. Uses JSON as intermediate to detect $ref field. *)let or_ref_jsont (value_jsont : 'a Json.codec) : 'a or_ref Json.codec = Json.Codec.map Json.Codec.Value.t ~kind:"or_ref" ~dec:(fun json -> match json with | Json.Object (mems, _meta) -> (match find_member "$ref" mems with | Some (Json.String (ref_str, _)) -> Ref ref_str | _ -> match Json.Codec.decode value_jsont json with | Ok v -> Value v | Error e -> Json.Error.fail_msgf Json.Meta.none "%a" Json.Error.pp e) | _ -> match Json.Codec.decode value_jsont json with | Ok v -> Value v | Error e -> Json.Error.fail_msgf Json.Meta.none "%a" Json.Error.pp e) ~enc:(function | Ref r -> Json.Object ([(("$ref", Json.Meta.none), Json.String (r, Json.Meta.none))], Json.Meta.none) | Value v -> try Json.Codec.encode value_jsont v with Json.Error _ -> Json.Value.null ())
(** {1 String Map} *)
module StringMap = Map.Make(String)
let string_map_jsont (value_jsont : 'a Json.codec) : (string * 'a) list Json.codec = let map_jsont = Json.Codec.Object.as_string_map value_jsont in Json.Codec.map ~kind:"string_map" ~dec:(fun m -> StringMap.bindings m) ~enc:(fun pairs -> List.fold_left (fun m (k, v) -> StringMap.add k v m) StringMap.empty pairs) map_jsont
(** {1 Contact} *)
type contact = { name : string option; url : string option; email : string option;}
let contact_jsont : contact Json.codec = Json.Codec.Object.map ~kind:"Contact" (fun name url email -> { name; url; email }) |> Json.Codec.Object.opt_member "name" Json.Codec.string ~enc:(fun c -> c.name) |> Json.Codec.Object.opt_member "url" Json.Codec.string ~enc:(fun c -> c.url) |> Json.Codec.Object.opt_member "email" Json.Codec.string ~enc:(fun c -> c.email) |> Json.Codec.Object.skip_unknown |> Json.Codec.Object.seal
(** {1 License} *)
type license = { name : string; url : string option;}
let license_jsont : license Json.codec = Json.Codec.Object.map ~kind:"License" (fun name url -> { name; url }) |> Json.Codec.Object.member "name" Json.Codec.string ~enc:(fun l -> l.name) |> Json.Codec.Object.opt_member "url" Json.Codec.string ~enc:(fun l -> l.url) |> Json.Codec.Object.skip_unknown |> Json.Codec.Object.seal
(** {1 Info} *)
type info = { title : string; description : string option; terms_of_service : string option; contact : contact option; license : license option; version : string;}
let info_jsont : info Json.codec = Json.Codec.Object.map ~kind:"Info" (fun title description terms_of_service contact license version -> { title; description; terms_of_service; contact; license; version }) |> Json.Codec.Object.member "title" Json.Codec.string ~enc:(fun i -> i.title) |> Json.Codec.Object.opt_member "description" Json.Codec.string ~enc:(fun i -> i.description) |> Json.Codec.Object.opt_member "termsOfService" Json.Codec.string ~enc:(fun i -> i.terms_of_service) |> Json.Codec.Object.opt_member "contact" contact_jsont ~enc:(fun i -> i.contact) |> Json.Codec.Object.opt_member "license" license_jsont ~enc:(fun i -> i.license) |> Json.Codec.Object.member "version" Json.Codec.string ~enc:(fun i -> i.version) |> Json.Codec.Object.skip_unknown |> Json.Codec.Object.seal
(** {1 Server} *)
type server_variable = { enum : string list option; default : string; description : string option;}
let server_variable_jsont : server_variable Json.codec = Json.Codec.Object.map ~kind:"ServerVariable" (fun enum default description -> { enum; default; description }) |> Json.Codec.Object.opt_member "enum" Json.Codec.(list string) ~enc:(fun sv -> sv.enum) |> Json.Codec.Object.member "default" Json.Codec.string ~enc:(fun sv -> sv.default) |> Json.Codec.Object.opt_member "description" Json.Codec.string ~enc:(fun sv -> sv.description) |> Json.Codec.Object.skip_unknown |> Json.Codec.Object.seal
type server = { url : string; description : string option; variables : (string * server_variable) list;}
let server_jsont : server Json.codec = Json.Codec.Object.map ~kind:"Server" (fun url description variables -> { url; description; variables }) |> Json.Codec.Object.member "url" Json.Codec.string ~enc:(fun s -> s.url) |> Json.Codec.Object.opt_member "description" Json.Codec.string ~enc:(fun s -> s.description) |> Json.Codec.Object.member "variables" (string_map_jsont server_variable_jsont) ~dec_absent:[] ~enc:(fun s -> s.variables) |> Json.Codec.Object.skip_unknown |> Json.Codec.Object.seal
(** {1 External Documentation} *)
type external_docs = { description : string option; url : string;}
let external_docs_jsont : external_docs Json.codec = Json.Codec.Object.map ~kind:"ExternalDocs" (fun description url -> { description; url }) |> Json.Codec.Object.opt_member "description" Json.Codec.string ~enc:(fun ed -> ed.description) |> Json.Codec.Object.member "url" Json.Codec.string ~enc:(fun ed -> ed.url) |> Json.Codec.Object.skip_unknown |> Json.Codec.Object.seal
(** {1 Tag} *)
type tag = { name : string; description : string option; external_docs : external_docs option;}
let tag_jsont : tag Json.codec = Json.Codec.Object.map ~kind:"Tag" (fun name description external_docs -> { name; description; external_docs }) |> Json.Codec.Object.member "name" Json.Codec.string ~enc:(fun t -> t.name) |> Json.Codec.Object.opt_member "description" Json.Codec.string ~enc:(fun t -> t.description) |> Json.Codec.Object.opt_member "externalDocs" external_docs_jsont ~enc:(fun t -> t.external_docs) |> Json.Codec.Object.skip_unknown |> Json.Codec.Object.seal
(** {1 Discriminator} *)
type discriminator = { property_name : string; mapping : (string * string) list;}
let discriminator_jsont : discriminator Json.codec = Json.Codec.Object.map ~kind:"Discriminator" (fun property_name mapping -> { property_name; mapping }) |> Json.Codec.Object.member "propertyName" Json.Codec.string ~enc:(fun d -> d.property_name) |> Json.Codec.Object.member "mapping" (string_map_jsont Json.Codec.string) ~dec_absent:[] ~enc:(fun d -> d.mapping) |> Json.Codec.Object.skip_unknown |> Json.Codec.Object.seal
(** {1 Schema}
JSON Schema with OpenAPI extensions. We use a simplified approach where references are stored as schema or_ref. *)
type schema = { title : string option; description : string option; type_ : string option; format : string option; default : Json.t option; nullable : bool; read_only : bool; write_only : bool; deprecated : bool; (* Validation *) enum : Json.t list option; const : Json.t option; minimum : float option; maximum : float option; exclusive_minimum : float option; exclusive_maximum : float option; multiple_of : float option; min_length : int option; max_length : int option; pattern : string option; min_items : int option; max_items : int option; unique_items : bool; min_properties : int option; max_properties : int option; (* Composition - stored as JSON for simplicity *) all_of : Json.t list option; one_of : Json.t list option; any_of : Json.t list option; not_ : Json.t option; (* Object - stored as JSON for simplicity *) properties : (string * Json.t) list; required : string list; additional_properties : Json.t option; (* Array *) items : Json.t option; (* Discriminator *) discriminator : discriminator option; (* Examples *) example : Json.t option;}
let empty_schema = { title = None; description = None; type_ = None; format = None; default = None; nullable = false; read_only = false; write_only = false; deprecated = false; enum = None; const = None; minimum = None; maximum = None; exclusive_minimum = None; exclusive_maximum = None; multiple_of = None; min_length = None; max_length = None; pattern = None; min_items = None; max_items = None; unique_items = false; min_properties = None; max_properties = None; all_of = None; one_of = None; any_of = None; not_ = None; properties = []; required = []; additional_properties = None; items = None; discriminator = None; example = None;}
let schema_jsont : schema Json.codec = Json.Codec.Object.map ~kind:"Schema" (fun title description type_ format default nullable read_only write_only deprecated enum const minimum maximum exclusive_minimum exclusive_maximum multiple_of min_length max_length pattern min_items max_items unique_items min_properties max_properties all_of one_of any_of not_ properties required additional_properties items discriminator example -> { title; description; type_; format; default; nullable; read_only; write_only; deprecated; enum; const; minimum; maximum; exclusive_minimum; exclusive_maximum; multiple_of; min_length; max_length; pattern; min_items; max_items; unique_items; min_properties; max_properties; all_of; one_of; any_of; not_; properties; required; additional_properties; items; discriminator; example }) |> Json.Codec.Object.opt_member "title" Json.Codec.string ~enc:(fun s -> s.title) |> Json.Codec.Object.opt_member "description" Json.Codec.string ~enc:(fun s -> s.description) |> Json.Codec.Object.opt_member "type" Json.Codec.string ~enc:(fun s -> s.type_) |> Json.Codec.Object.opt_member "format" Json.Codec.string ~enc:(fun s -> s.format) |> Json.Codec.Object.opt_member "default" Json.Codec.Value.t ~enc:(fun s -> s.default) |> Json.Codec.Object.member "nullable" Json.Codec.bool ~dec_absent:false ~enc:(fun s -> s.nullable) |> Json.Codec.Object.member "readOnly" Json.Codec.bool ~dec_absent:false ~enc:(fun s -> s.read_only) |> Json.Codec.Object.member "writeOnly" Json.Codec.bool ~dec_absent:false ~enc:(fun s -> s.write_only) |> Json.Codec.Object.member "deprecated" Json.Codec.bool ~dec_absent:false ~enc:(fun s -> s.deprecated) |> Json.Codec.Object.opt_member "enum" Json.Codec.(list Value.t) ~enc:(fun s -> s.enum) |> Json.Codec.Object.opt_member "const" Json.Codec.Value.t ~enc:(fun s -> s.const) |> Json.Codec.Object.opt_member "minimum" Json.Codec.number ~enc:(fun s -> s.minimum) |> Json.Codec.Object.opt_member "maximum" Json.Codec.number ~enc:(fun s -> s.maximum) |> Json.Codec.Object.opt_member "exclusiveMinimum" Json.Codec.number ~enc:(fun s -> s.exclusive_minimum) |> Json.Codec.Object.opt_member "exclusiveMaximum" Json.Codec.number ~enc:(fun s -> s.exclusive_maximum) |> Json.Codec.Object.opt_member "multipleOf" Json.Codec.number ~enc:(fun s -> s.multiple_of) |> Json.Codec.Object.opt_member "minLength" Json.Codec.int ~enc:(fun s -> s.min_length) |> Json.Codec.Object.opt_member "maxLength" Json.Codec.int ~enc:(fun s -> s.max_length) |> Json.Codec.Object.opt_member "pattern" Json.Codec.string ~enc:(fun s -> s.pattern) |> Json.Codec.Object.opt_member "minItems" Json.Codec.int ~enc:(fun s -> s.min_items) |> Json.Codec.Object.opt_member "maxItems" Json.Codec.int ~enc:(fun s -> s.max_items) |> Json.Codec.Object.member "uniqueItems" Json.Codec.bool ~dec_absent:false ~enc:(fun s -> s.unique_items) |> Json.Codec.Object.opt_member "minProperties" Json.Codec.int ~enc:(fun s -> s.min_properties) |> Json.Codec.Object.opt_member "maxProperties" Json.Codec.int ~enc:(fun s -> s.max_properties) |> Json.Codec.Object.opt_member "allOf" Json.Codec.(list Value.t) ~enc:(fun s -> s.all_of) |> Json.Codec.Object.opt_member "oneOf" Json.Codec.(list Value.t) ~enc:(fun s -> s.one_of) |> Json.Codec.Object.opt_member "anyOf" Json.Codec.(list Value.t) ~enc:(fun s -> s.any_of) |> Json.Codec.Object.opt_member "not" Json.Codec.Value.t ~enc:(fun s -> s.not_) |> Json.Codec.Object.member "properties" (string_map_jsont Json.Codec.Value.t) ~dec_absent:[] ~enc:(fun s -> s.properties) |> Json.Codec.Object.member "required" Json.Codec.(list string) ~dec_absent:[] ~enc:(fun s -> s.required) |> Json.Codec.Object.opt_member "additionalProperties" Json.Codec.Value.t ~enc:(fun s -> s.additional_properties) |> Json.Codec.Object.opt_member "items" Json.Codec.Value.t ~enc:(fun s -> s.items) |> Json.Codec.Object.opt_member "discriminator" discriminator_jsont ~enc:(fun s -> s.discriminator) |> Json.Codec.Object.opt_member "example" Json.Codec.Value.t ~enc:(fun s -> s.example) |> Json.Codec.Object.skip_unknown |> Json.Codec.Object.seal
let schema_or_ref_jsont = or_ref_jsont schema_jsont
(** {1 Parameter} *)
type parameter_location = Query | Header | Path | Cookie
let parameter_location_jsont : parameter_location Json.codec = Json.Codec.map Json.Codec.string ~kind:"parameter_location" ~dec:(function | "query" -> Query | "header" -> Header | "path" -> Path | "cookie" -> Cookie | s -> Json.Error.fail_msgf Json.Meta.none "Unknown parameter location: %s" s) ~enc:(function | Query -> "query" | Header -> "header" | Path -> "path" | Cookie -> "cookie")
type parameter_style = | Matrix | Label | Form | Simple | SpaceDelimited | PipeDelimited | DeepObject
let parameter_style_jsont : parameter_style Json.codec = Json.Codec.map Json.Codec.string ~kind:"parameter_style" ~dec:(function | "matrix" -> Matrix | "label" -> Label | "form" -> Form | "simple" -> Simple | "spaceDelimited" -> SpaceDelimited | "pipeDelimited" -> PipeDelimited | "deepObject" -> DeepObject | s -> Json.Error.fail_msgf Json.Meta.none "Unknown parameter style: %s" s) ~enc:(function | Matrix -> "matrix" | Label -> "label" | Form -> "form" | Simple -> "simple" | SpaceDelimited -> "spaceDelimited" | PipeDelimited -> "pipeDelimited" | DeepObject -> "deepObject")
(** {1 Example} *)
type example = { summary : string option; description : string option; value : Json.t option; external_value : string option;}
let example_jsont : example Json.codec = Json.Codec.Object.map ~kind:"Example" (fun summary description value external_value -> { summary; description; value; external_value }) |> Json.Codec.Object.opt_member "summary" Json.Codec.string ~enc:(fun e -> e.summary) |> Json.Codec.Object.opt_member "description" Json.Codec.string ~enc:(fun e -> e.description) |> Json.Codec.Object.opt_member "value" Json.Codec.Value.t ~enc:(fun e -> e.value) |> Json.Codec.Object.opt_member "externalValue" Json.Codec.string ~enc:(fun e -> e.external_value) |> Json.Codec.Object.skip_unknown |> Json.Codec.Object.seal
let example_or_ref_jsont = or_ref_jsont example_jsont
(** {1 Header} *)
type header = { description : string option; required : bool; deprecated : bool; schema : schema or_ref option;}
let header_jsont : header Json.codec = Json.Codec.Object.map ~kind:"Header" (fun description required deprecated schema -> { description; required; deprecated; schema }) |> Json.Codec.Object.opt_member "description" Json.Codec.string ~enc:(fun h -> h.description) |> Json.Codec.Object.member "required" Json.Codec.bool ~dec_absent:false ~enc:(fun h -> h.required) |> Json.Codec.Object.member "deprecated" Json.Codec.bool ~dec_absent:false ~enc:(fun h -> h.deprecated) |> Json.Codec.Object.opt_member "schema" schema_or_ref_jsont ~enc:(fun h -> h.schema) |> Json.Codec.Object.skip_unknown |> Json.Codec.Object.seal
let header_or_ref_jsont = or_ref_jsont header_jsont
(** {1 Encoding} *)
type encoding = { content_type : string option; headers : (string * header or_ref) list; style : parameter_style option; explode : bool option; allow_reserved : bool;}
let encoding_jsont : encoding Json.codec = Json.Codec.Object.map ~kind:"Encoding" (fun content_type headers style explode allow_reserved -> { content_type; headers; style; explode; allow_reserved }) |> Json.Codec.Object.opt_member "contentType" Json.Codec.string ~enc:(fun e -> e.content_type) |> Json.Codec.Object.member "headers" (string_map_jsont header_or_ref_jsont) ~dec_absent:[] ~enc:(fun e -> e.headers) |> Json.Codec.Object.opt_member "style" parameter_style_jsont ~enc:(fun e -> e.style) |> Json.Codec.Object.opt_member "explode" Json.Codec.bool ~enc:(fun e -> e.explode) |> Json.Codec.Object.member "allowReserved" Json.Codec.bool ~dec_absent:false ~enc:(fun e -> e.allow_reserved) |> Json.Codec.Object.skip_unknown |> Json.Codec.Object.seal
(** {1 Media Type} *)
type media_type = { schema : schema or_ref option; example : Json.t option; examples : (string * example or_ref) list; encoding : (string * encoding) list;}
let media_type_jsont : media_type Json.codec = Json.Codec.Object.map ~kind:"MediaType" (fun schema example examples encoding -> { schema; example; examples; encoding }) |> Json.Codec.Object.opt_member "schema" schema_or_ref_jsont ~enc:(fun mt -> mt.schema) |> Json.Codec.Object.opt_member "example" Json.Codec.Value.t ~enc:(fun mt -> mt.example) |> Json.Codec.Object.member "examples" (string_map_jsont example_or_ref_jsont) ~dec_absent:[] ~enc:(fun mt -> mt.examples) |> Json.Codec.Object.member "encoding" (string_map_jsont encoding_jsont) ~dec_absent:[] ~enc:(fun mt -> mt.encoding) |> Json.Codec.Object.skip_unknown |> Json.Codec.Object.seal
(** {1 Parameter} *)
type parameter = { name : string; in_ : parameter_location; description : string option; required : bool; deprecated : bool; allow_empty_value : bool; style : parameter_style option; explode : bool option; allow_reserved : bool; schema : schema or_ref option; example : Json.t option; content : (string * media_type) list;}
let parameter_jsont : parameter Json.codec = Json.Codec.Object.map ~kind:"Parameter" (fun name in_ description required deprecated allow_empty_value style explode allow_reserved schema example content -> { name; in_; description; required; deprecated; allow_empty_value; style; explode; allow_reserved; schema; example; content }) |> Json.Codec.Object.member "name" Json.Codec.string ~enc:(fun p -> p.name) |> Json.Codec.Object.member "in" parameter_location_jsont ~enc:(fun p -> p.in_) |> Json.Codec.Object.opt_member "description" Json.Codec.string ~enc:(fun p -> p.description) |> Json.Codec.Object.member "required" Json.Codec.bool ~dec_absent:false ~enc:(fun p -> p.required) |> Json.Codec.Object.member "deprecated" Json.Codec.bool ~dec_absent:false ~enc:(fun p -> p.deprecated) |> Json.Codec.Object.member "allowEmptyValue" Json.Codec.bool ~dec_absent:false ~enc:(fun p -> p.allow_empty_value) |> Json.Codec.Object.opt_member "style" parameter_style_jsont ~enc:(fun p -> p.style) |> Json.Codec.Object.opt_member "explode" Json.Codec.bool ~enc:(fun p -> p.explode) |> Json.Codec.Object.member "allowReserved" Json.Codec.bool ~dec_absent:false ~enc:(fun p -> p.allow_reserved) |> Json.Codec.Object.opt_member "schema" schema_or_ref_jsont ~enc:(fun p -> p.schema) |> Json.Codec.Object.opt_member "example" Json.Codec.Value.t ~enc:(fun p -> p.example) |> Json.Codec.Object.member "content" (string_map_jsont media_type_jsont) ~dec_absent:[] ~enc:(fun p -> p.content) |> Json.Codec.Object.skip_unknown |> Json.Codec.Object.seal
let parameter_or_ref_jsont = or_ref_jsont parameter_jsont
(** {1 Request Body} *)
type request_body = { description : string option; content : (string * media_type) list; required : bool;}
let request_body_jsont : request_body Json.codec = Json.Codec.Object.map ~kind:"RequestBody" (fun description content required -> { description; content; required }) |> Json.Codec.Object.opt_member "description" Json.Codec.string ~enc:(fun rb -> rb.description) |> Json.Codec.Object.member "content" (string_map_jsont media_type_jsont) ~dec_absent:[] ~enc:(fun rb -> rb.content) |> Json.Codec.Object.member "required" Json.Codec.bool ~dec_absent:false ~enc:(fun rb -> rb.required) |> Json.Codec.Object.skip_unknown |> Json.Codec.Object.seal
let request_body_or_ref_jsont = or_ref_jsont request_body_jsont
(** {1 Link} *)
type link = { operation_ref : string option; operation_id : string option; parameters : (string * Json.t) list; request_body : Json.t option; description : string option; server : server option;}
let link_jsont : link Json.codec = Json.Codec.Object.map ~kind:"Link" (fun operation_ref operation_id parameters request_body description server -> { operation_ref; operation_id; parameters; request_body; description; server }) |> Json.Codec.Object.opt_member "operationRef" Json.Codec.string ~enc:(fun l -> l.operation_ref) |> Json.Codec.Object.opt_member "operationId" Json.Codec.string ~enc:(fun l -> l.operation_id) |> Json.Codec.Object.member "parameters" (string_map_jsont Json.Codec.Value.t) ~dec_absent:[] ~enc:(fun l -> l.parameters) |> Json.Codec.Object.opt_member "requestBody" Json.Codec.Value.t ~enc:(fun l -> l.request_body) |> Json.Codec.Object.opt_member "description" Json.Codec.string ~enc:(fun l -> l.description) |> Json.Codec.Object.opt_member "server" server_jsont ~enc:(fun l -> l.server) |> Json.Codec.Object.skip_unknown |> Json.Codec.Object.seal
let link_or_ref_jsont = or_ref_jsont link_jsont
(** {1 Response} *)
type response = { description : string; headers : (string * header or_ref) list; content : (string * media_type) list; links : (string * link or_ref) list;}
let response_jsont : response Json.codec = Json.Codec.Object.map ~kind:"Response" (fun description headers content links -> { description; headers; content; links }) |> Json.Codec.Object.member "description" Json.Codec.string ~dec_absent:"" ~enc:(fun r -> r.description) |> Json.Codec.Object.member "headers" (string_map_jsont header_or_ref_jsont) ~dec_absent:[] ~enc:(fun r -> r.headers) |> Json.Codec.Object.member "content" (string_map_jsont media_type_jsont) ~dec_absent:[] ~enc:(fun r -> r.content) |> Json.Codec.Object.member "links" (string_map_jsont link_or_ref_jsont) ~dec_absent:[] ~enc:(fun r -> r.links) |> Json.Codec.Object.skip_unknown |> Json.Codec.Object.seal
let response_or_ref_jsont = or_ref_jsont response_jsont
(** {1 Responses} *)
type responses = { default : response or_ref option; responses : (string * response or_ref) list; (* status code -> response *)}
let responses_jsont : responses Json.codec = (* Responses is an object where keys are status codes or "default" *) Json.Codec.map (Json.Codec.Object.as_string_map response_or_ref_jsont) ~kind:"Responses" ~dec:(fun m -> let default = StringMap.find_opt "default" m in let responses = StringMap.bindings m |> List.filter (fun (k, _) -> k <> "default") in { default; responses }) ~enc:(fun r -> let m = List.fold_left (fun m (k, v) -> StringMap.add k v m) StringMap.empty r.responses in match r.default with | Some d -> StringMap.add "default" d m | None -> m)
(** {1 Security Requirement} *)
type security_requirement = (string * string list) list
let security_requirement_jsont : security_requirement Json.codec = string_map_jsont Json.Codec.(list string)
(** {1 Callback - simplified to JSON} *)
type callback = Json.t
let callback_jsont : callback Json.codec = Json.Codec.Value.tlet callback_or_ref_jsont = or_ref_jsont callback_jsont
(** {1 Operation} *)
type operation = { tags : string list; summary : string option; description : string option; external_docs : external_docs option; operation_id : string option; parameters : parameter or_ref list; request_body : request_body or_ref option; responses : responses; callbacks : (string * callback or_ref) list; deprecated : bool; security : security_requirement list option; servers : server list;}
let operation_jsont : operation Json.codec = Json.Codec.Object.map ~kind:"Operation" (fun tags summary description external_docs operation_id parameters request_body responses callbacks deprecated security servers -> { tags; summary; description; external_docs; operation_id; parameters; request_body; responses; callbacks; deprecated; security; servers }) |> Json.Codec.Object.member "tags" Json.Codec.(list string) ~dec_absent:[] ~enc:(fun o -> o.tags) |> Json.Codec.Object.opt_member "summary" Json.Codec.string ~enc:(fun o -> o.summary) |> Json.Codec.Object.opt_member "description" Json.Codec.string ~enc:(fun o -> o.description) |> Json.Codec.Object.opt_member "externalDocs" external_docs_jsont ~enc:(fun o -> o.external_docs) |> Json.Codec.Object.opt_member "operationId" Json.Codec.string ~enc:(fun o -> o.operation_id) |> Json.Codec.Object.member "parameters" Json.Codec.(list parameter_or_ref_jsont) ~dec_absent:[] ~enc:(fun o -> o.parameters) |> Json.Codec.Object.opt_member "requestBody" request_body_or_ref_jsont ~enc:(fun o -> o.request_body) |> Json.Codec.Object.member "responses" responses_jsont ~dec_absent:{ default = None; responses = [] } ~enc:(fun o -> o.responses) |> Json.Codec.Object.member "callbacks" (string_map_jsont callback_or_ref_jsont) ~dec_absent:[] ~enc:(fun o -> o.callbacks) |> Json.Codec.Object.member "deprecated" Json.Codec.bool ~dec_absent:false ~enc:(fun o -> o.deprecated) |> Json.Codec.Object.opt_member "security" Json.Codec.(list security_requirement_jsont) ~enc:(fun o -> o.security) |> Json.Codec.Object.member "servers" Json.Codec.(list server_jsont) ~dec_absent:[] ~enc:(fun o -> o.servers) |> Json.Codec.Object.skip_unknown |> Json.Codec.Object.seal
(** {1 Path Item} *)
type path_item = { ref_ : string option; summary : string option; description : string option; get : operation option; put : operation option; post : operation option; delete : operation option; options : operation option; head : operation option; patch : operation option; trace : operation option; servers : server list; parameters : parameter or_ref list;}
let path_item_jsont : path_item Json.codec = Json.Codec.Object.map ~kind:"PathItem" (fun ref_ summary description get put post delete options head patch trace servers parameters -> { ref_; summary; description; get; put; post; delete; options; head; patch; trace; servers; parameters }) |> Json.Codec.Object.opt_member "$ref" Json.Codec.string ~enc:(fun pi -> pi.ref_) |> Json.Codec.Object.opt_member "summary" Json.Codec.string ~enc:(fun pi -> pi.summary) |> Json.Codec.Object.opt_member "description" Json.Codec.string ~enc:(fun pi -> pi.description) |> Json.Codec.Object.opt_member "get" operation_jsont ~enc:(fun pi -> pi.get) |> Json.Codec.Object.opt_member "put" operation_jsont ~enc:(fun pi -> pi.put) |> Json.Codec.Object.opt_member "post" operation_jsont ~enc:(fun pi -> pi.post) |> Json.Codec.Object.opt_member "delete" operation_jsont ~enc:(fun pi -> pi.delete) |> Json.Codec.Object.opt_member "options" operation_jsont ~enc:(fun pi -> pi.options) |> Json.Codec.Object.opt_member "head" operation_jsont ~enc:(fun pi -> pi.head) |> Json.Codec.Object.opt_member "patch" operation_jsont ~enc:(fun pi -> pi.patch) |> Json.Codec.Object.opt_member "trace" operation_jsont ~enc:(fun pi -> pi.trace) |> Json.Codec.Object.member "servers" Json.Codec.(list server_jsont) ~dec_absent:[] ~enc:(fun pi -> pi.servers) |> Json.Codec.Object.member "parameters" Json.Codec.(list parameter_or_ref_jsont) ~dec_absent:[] ~enc:(fun pi -> pi.parameters) |> Json.Codec.Object.skip_unknown |> Json.Codec.Object.seal
let path_item_or_ref_jsont = or_ref_jsont path_item_jsont
(** {1 Security Scheme} *)
type security_scheme_type = | ApiKey | Http | OAuth2 | OpenIdConnect
let security_scheme_type_jsont : security_scheme_type Json.codec = Json.Codec.map Json.Codec.string ~kind:"security_scheme_type" ~dec:(function | "apiKey" -> ApiKey | "http" -> Http | "oauth2" -> OAuth2 | "openIdConnect" -> OpenIdConnect | s -> Json.Error.fail_msgf Json.Meta.none "Unknown security scheme type: %s" s) ~enc:(function | ApiKey -> "apiKey" | Http -> "http" | OAuth2 -> "oauth2" | OpenIdConnect -> "openIdConnect")
type oauth_flow = { authorization_url : string option; token_url : string option; refresh_url : string option; scopes : (string * string) list;}
let oauth_flow_jsont : oauth_flow Json.codec = Json.Codec.Object.map ~kind:"OAuthFlow" (fun authorization_url token_url refresh_url scopes -> { authorization_url; token_url; refresh_url; scopes }) |> Json.Codec.Object.opt_member "authorizationUrl" Json.Codec.string ~enc:(fun f -> f.authorization_url) |> Json.Codec.Object.opt_member "tokenUrl" Json.Codec.string ~enc:(fun f -> f.token_url) |> Json.Codec.Object.opt_member "refreshUrl" Json.Codec.string ~enc:(fun f -> f.refresh_url) |> Json.Codec.Object.member "scopes" (string_map_jsont Json.Codec.string) ~dec_absent:[] ~enc:(fun f -> f.scopes) |> Json.Codec.Object.skip_unknown |> Json.Codec.Object.seal
type oauth_flows = { implicit : oauth_flow option; password : oauth_flow option; client_credentials : oauth_flow option; authorization_code : oauth_flow option;}
let oauth_flows_jsont : oauth_flows Json.codec = Json.Codec.Object.map ~kind:"OAuthFlows" (fun implicit password client_credentials authorization_code -> { implicit; password; client_credentials; authorization_code }) |> Json.Codec.Object.opt_member "implicit" oauth_flow_jsont ~enc:(fun f -> f.implicit) |> Json.Codec.Object.opt_member "password" oauth_flow_jsont ~enc:(fun f -> f.password) |> Json.Codec.Object.opt_member "clientCredentials" oauth_flow_jsont ~enc:(fun f -> f.client_credentials) |> Json.Codec.Object.opt_member "authorizationCode" oauth_flow_jsont ~enc:(fun f -> f.authorization_code) |> Json.Codec.Object.skip_unknown |> Json.Codec.Object.seal
type security_scheme = { type_ : security_scheme_type; description : string option; name : string option; in_ : parameter_location option; scheme : string option; bearer_format : string option; flows : oauth_flows option; open_id_connect_url : string option;}
let security_scheme_jsont : security_scheme Json.codec = Json.Codec.Object.map ~kind:"SecurityScheme" (fun type_ description name in_ scheme bearer_format flows open_id_connect_url -> { type_; description; name; in_; scheme; bearer_format; flows; open_id_connect_url }) |> Json.Codec.Object.member "type" security_scheme_type_jsont ~enc:(fun ss -> ss.type_) |> Json.Codec.Object.opt_member "description" Json.Codec.string ~enc:(fun ss -> ss.description) |> Json.Codec.Object.opt_member "name" Json.Codec.string ~enc:(fun ss -> ss.name) |> Json.Codec.Object.opt_member "in" parameter_location_jsont ~enc:(fun ss -> ss.in_) |> Json.Codec.Object.opt_member "scheme" Json.Codec.string ~enc:(fun ss -> ss.scheme) |> Json.Codec.Object.opt_member "bearerFormat" Json.Codec.string ~enc:(fun ss -> ss.bearer_format) |> Json.Codec.Object.opt_member "flows" oauth_flows_jsont ~enc:(fun ss -> ss.flows) |> Json.Codec.Object.opt_member "openIdConnectUrl" Json.Codec.string ~enc:(fun ss -> ss.open_id_connect_url) |> Json.Codec.Object.skip_unknown |> Json.Codec.Object.seal
let security_scheme_or_ref_jsont = or_ref_jsont security_scheme_jsont
(** {1 Components} *)
type components = { schemas : (string * schema or_ref) list; responses : (string * response or_ref) list; parameters : (string * parameter or_ref) list; examples : (string * example or_ref) list; request_bodies : (string * request_body or_ref) list; headers : (string * header or_ref) list; security_schemes : (string * security_scheme or_ref) list; links : (string * link or_ref) list; callbacks : (string * callback or_ref) list; path_items : (string * path_item or_ref) list;}
let components_jsont : components Json.codec = Json.Codec.Object.map ~kind:"Components" (fun schemas responses parameters examples request_bodies headers security_schemes links callbacks path_items -> { schemas; responses; parameters; examples; request_bodies; headers; security_schemes; links; callbacks; path_items }) |> Json.Codec.Object.member "schemas" (string_map_jsont schema_or_ref_jsont) ~dec_absent:[] ~enc:(fun c -> c.schemas) |> Json.Codec.Object.member "responses" (string_map_jsont response_or_ref_jsont) ~dec_absent:[] ~enc:(fun c -> c.responses) |> Json.Codec.Object.member "parameters" (string_map_jsont parameter_or_ref_jsont) ~dec_absent:[] ~enc:(fun c -> c.parameters) |> Json.Codec.Object.member "examples" (string_map_jsont example_or_ref_jsont) ~dec_absent:[] ~enc:(fun c -> c.examples) |> Json.Codec.Object.member "requestBodies" (string_map_jsont request_body_or_ref_jsont) ~dec_absent:[] ~enc:(fun c -> c.request_bodies) |> Json.Codec.Object.member "headers" (string_map_jsont header_or_ref_jsont) ~dec_absent:[] ~enc:(fun c -> c.headers) |> Json.Codec.Object.member "securitySchemes" (string_map_jsont security_scheme_or_ref_jsont) ~dec_absent:[] ~enc:(fun c -> c.security_schemes) |> Json.Codec.Object.member "links" (string_map_jsont link_or_ref_jsont) ~dec_absent:[] ~enc:(fun c -> c.links) |> Json.Codec.Object.member "callbacks" (string_map_jsont callback_or_ref_jsont) ~dec_absent:[] ~enc:(fun c -> c.callbacks) |> Json.Codec.Object.member "pathItems" (string_map_jsont path_item_or_ref_jsont) ~dec_absent:[] ~enc:(fun c -> c.path_items) |> Json.Codec.Object.skip_unknown |> Json.Codec.Object.seal
(** {1 OpenAPI Document} *)
type t = { openapi : string; info : info; servers : server list; paths : (string * path_item) list; webhooks : (string * path_item or_ref) list; components : components option; security : security_requirement list; tags : tag list; external_docs : external_docs option;}
let jsont : t Json.codec = Json.Codec.Object.map ~kind:"OpenAPI" (fun openapi info servers paths webhooks components security tags external_docs -> { openapi; info; servers; paths; webhooks; components; security; tags; external_docs }) |> Json.Codec.Object.member "openapi" Json.Codec.string ~enc:(fun t -> t.openapi) |> Json.Codec.Object.member "info" info_jsont ~enc:(fun t -> t.info) |> Json.Codec.Object.member "servers" Json.Codec.(list server_jsont) ~dec_absent:[] ~enc:(fun t -> t.servers) |> Json.Codec.Object.member "paths" (string_map_jsont path_item_jsont) ~dec_absent:[] ~enc:(fun t -> t.paths) |> Json.Codec.Object.member "webhooks" (string_map_jsont path_item_or_ref_jsont) ~dec_absent:[] ~enc:(fun t -> t.webhooks) |> Json.Codec.Object.opt_member "components" components_jsont ~enc:(fun t -> t.components) |> Json.Codec.Object.member "security" Json.Codec.(list security_requirement_jsont) ~dec_absent:[] ~enc:(fun t -> t.security) |> Json.Codec.Object.member "tags" Json.Codec.(list tag_jsont) ~dec_absent:[] ~enc:(fun t -> t.tags) |> Json.Codec.Object.opt_member "externalDocs" external_docs_jsont ~enc:(fun t -> t.external_docs) |> Json.Codec.Object.skip_unknown |> Json.Codec.Object.seal
(** {1 Parsing} *)
let of_string s = match Json.of_string jsont s with | Ok v -> Ok v | Error e -> Error (Format.asprintf "%a" Json.Error.pp e)
let of_string' s = Json.of_string jsont s
let to_string t = try Ok (Json.to_string ~indent:2 jsont t) with Json.Error e -> Error (Format.asprintf "%a" Json.Error.pp e)
let to_string' t = try Ok (Json.to_string ~indent:2 jsont t) with Json.Error e -> Error e
(** {1 Reference Resolution} *)
let resolve_schema_ref (ref_str : string) (spec : t) : schema option = (* Parse $ref like "#/components/schemas/Pet" *) if not (String.length ref_str > 0 && ref_str.[0] = '#') then None else let parts = String.split_on_char '/' ref_str in match parts with | ["#"; "components"; "schemas"; name] -> (match spec.components with | None -> None | Some c -> match List.assoc_opt name c.schemas with | Some (Value s) -> Some s | Some (Ref _) -> None (* nested refs not supported yet *) | None -> None) | _ -> None