Discover and annotate all data types and paths in an OpenAPI specification document.
Data Type Discovery
An OpenAPI spec document has a connected graph of Schema objects.
There are a few mutually related concepts to define before we can explain what is discovered:
- A data type is either a primitive (ex.
integer), compound (ex.array) and reference data type (ex."$ref"). - Each Schema object is a primitive data type, compound data type or reference data type. A Schema object also has data type specifications and data type compositions. Each data type specification constraints a data type (ex.
maximumconstrains the allowed values of anintegerprimitive type). A data type composition (allOf,anyOf,oneOfandnot) can create polymorphic, sum and negation types from other types. - Compound data types encapsulate zero or more Schema objects.
- Reference data types reference a Schema object.
When mapping OpenAPI to a statically typed programming language, one of the mismatches is that languages like OCaml do not have a concept of data type specification. And sometimes an OpenAPI data type composition (ex. not) does not exist in a language.
Another mismatch is that some languages are nominally typed (they need names for each level of a compound data type) rather than structurally typed. Structural typing is implicit in OpenAPI, and with nomimally typed programming languages the compound data types need to be flattened. In OCaml the record type is nominally typed, so the following compound data type is invalid code:
[ type t = { x: { a: string } } ]
However, after flattening the compound data type the equivalent representation is valid:
[ type t_1234678 = { a: string } type t = { x: t_12345678 } ]
See the 2 Flattening Data Types and Compositions for more details.
More generally, when generating code for a programming language from an OpenAPI specification, the generator needs to map a uniquely identifiable, flattened Schema Object to one or more programming language types ("PL type").
Here are examples when generating OCaml source code, where the triples are (data type, data type specification, data type composition):
- The triple (
integer,("minimum",0); ("maximum", 65535), ) maps to the OCaml PL typeint32 - The triple (
array, ,anyOf (integer, string)) maps to the OCaml PL typetype somename = | Integer | String - The triple (
array, ,not boolean) cannot be mapped to an OCaml PL type
Use datatypes (datatypes ~query_selector:`FlattenedAll to enumerate all the uniquely identifiable, flattened Schema objects discovered in the OpenAPI specification document. Unlike OaDocument.fold_all_schemas, compound data types have been flattened so that you get an enumeration over all levels of a compound data type. Compositions like anyOf are also flattened.
You may also use datatypes ~query_selector:`All to do the same without any flattening when your programming language supports nested compound data types.
The unique identity is the JSON pointer path to a Schema object in the OpenAPI specification document.
Flattening Data Types and Compositions
Anonymous Data Types
Anonymous data types are given names. That way anonymous data types can be lifted to top-level types. That is, the following illegal OCaml PL type is avoided:
[ type t = { x: { a: string } } ]
Instead new top-level data types are created with names:
[ type t_1234678 = { a: string } type t = { x: t_12345678 } ]
Optional Data Types
Optional data types (either not required or a nullable field) are represented as two data types (the non-optional type and the optional type). That way anonymous data types can be made optional. That is, the following illegal OCaml PL type is avoided:
[ type t = {x:string} option ]
The valid flat representation is:
[ type t'nonopt = {x:string} type t = t'nonopt option ]
Flattened Compositions
The compositions anyOf, oneOf and allOf are flattened.
That is, the following illegal OCaml PL type induced by anyOf is avoided:
[ type t = { fruit: | Apple | Orange } ]
Instead new top-level data types are created:
[ type t_1234678 = | Apple | Orange type t = { fruit: t_12345678 } ]
A REST verb.
type param_info = {p_name : string;p_required : bool;p_explode : bool;p_parameter : OaTypes.parameter;
}type op = {user_op : string;verb : verb;segs : OaTypes.operation_seg list;summary : string option;description : string option;headers : param_info list;queries : param_info list;path_params : param_info list;request_body : OaTypes.request_body option;responses : OaTypes.responses;
}A REST operation.
val param_info :
loc:OaTypes.param_in ->
OaTypes.parameter list ->
(string * bool * bool) listval primitive_pp_f :
(module OaDocument.STANDARD) ->
(module OaDocument.VENDOR) ->
format:string option ->
enum:Json_repr.any list option ->
OaTypes.schema_kind ->
(Restapis_o.Open__.Format.formatter -> unit -> unit) optionval datatype_ref_of_schema :
(module OaDocument.STANDARD) ->
(module OaDocument.VENDOR) ->
path:Json_query.path ->
OaTypes.schema ->
datatype_refval resolve_parameter_specs :
?depth:int ->
t ->
path_template:string ->
param:string ->
OaTypes.parameter_kind ->
OaTypes.parameter_specsresolve_parameter_specs t ~path_template kind fully resolves the parameter specifications for the parameter kind kind.
path_template is the REST operation path template and param is the parameter name; they are used only for error reporting.
val resolve_header_specs :
?depth:int ->
t ->
path_template:string ->
header:string ->
OaTypes.header_kind ->
OaTypes.header_specsresolve_header_specs t ~path_template kind fully resolves the header specifications for the header kind kind.
path_template is the REST operation path template and header is the header name; they are used only for error reporting.
val resolve_request_body_specs :
?depth:int ->
t ->
path_template:string ->
OaTypes.request_body_kind ->
OaTypes.request_body_specsresolve_request_body_specs t ~path_template kind fully resolves the Request Body specifications for the request body kind kind.
path_template is the REST operation path template; it is used only for error reporting.
val resolve_datatype_ref :
?depth:JsonQueryPathMap.key list ->
t ->
path:JsonQueryPathMap.key ->
datatype_refresolve_datatype_ref t ~path fully resolves the datatype reference through $ref references to the original schema: ... definition.
val resolve_schema :
?depth:Json_query.path list ->
t ->
path:Json_query.path ->
OaTypes.schemaresolve_schema t ~path fully resolves the schema through $ref references to the original schema: ... definition.
include DkCoder_Std.SCRIPT
__init context is the entry point for running a script module. The DkCoder compiler will inject this function at the top and bottom of the script module. The top __init does nothing, while the bottom __init calls the prior __init.
That means:
- calling the
__initfunction guarantees that the script module is initialized; that is, all of the script module's side-effects (ex.let () = Format.printf "Hello world@.") are executed before the__initreturns to the caller. - you can override the
__initfunction by simply defining the__initidempotently. That will shadow the top__initand when the bottom__initis executed your__initwill be called instead of the do-nothing top__init.
Future versions of DkCoder will call __init in dependency order for all `You script modules. Your __init function may be called several times.
__repl context is the entry point for debugging a script module in a REPL. The DkCoder compiler will inject this function at the top and bottom of the script module. The top __repl does nothing, while the bottom __repl calls the prior __repl.
That means:
- you can override the
__replfunction by simply defining the__replidempotently. That will shadow the top__repland when the bottom__replis executed your__replwill be called instead of the do-nothing top__repl.
The run-time module information for the script module.

