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:

  1. A data type is either a primitive (ex. integer), compound (ex. array) and reference data type (ex. "$ref").
  2. 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. maximum constrains the allowed values of an integer primitive type). A data type composition (allOf, anyOf, oneOf and not) can create polymorphic, sum and negation types from other types.
  3. Compound data types encapsulate zero or more Schema objects.
  4. 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):

  1. The triple (integer, ("minimum",0); ("maximum", 65535), ) maps to the OCaml PL type int32
  2. The triple (array, , anyOf (integer, string)) maps to the OCaml PL type type somename = | Integer | String
  3. 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 } ]

Sourcemodule JsonQueryPath : sig ... end
module JsonQueryPathMap : sig ... end
Sourcetype datatype_ref = [
  1. | `PrimitiveType of string
  2. | `TypeAlias of string
]
Sourcetype datatype = {
  1. datatype_ref : datatype_ref;
  2. schema : OaTypes.schema;
  3. flat_generated : bool;
}
Sourcetype uid_and_datatype = Json_query.path * datatype
Sourcetype verb =
  1. | Get
  2. | Put
  3. | Post
  4. | Delete
  5. | Options
  6. | Head
  7. | Patch
  8. | Trace

A REST verb.

Sourcetype param_info = {
  1. p_name : string;
  2. p_required : bool;
  3. p_explode : bool;
  4. p_parameter : OaTypes.parameter;
}
Sourcetype op = {
  1. user_op : string;
  2. verb : verb;
  3. segs : OaTypes.operation_seg list;
  4. summary : string option;
  5. description : string option;
  6. headers : param_info list;
  7. queries : param_info list;
  8. cookies : param_info list;
  9. path_params : param_info list;
  10. request_body : OaTypes.request_body option;
  11. responses : OaTypes.responses;
}

A REST operation.

Sourcetype t = {
  1. doc : OaDocument.t;
  2. datatypes_flat_all : datatype JsonQueryPathMap.t;
  3. datatypes_flat_unions : datatype JsonQueryPathMap.t;
  4. datatypes_nested : datatype JsonQueryPathMap.t;
  5. datatypes_params : datatype JsonQueryPathMap.t;
  6. datatypes_bodies : datatype JsonQueryPathMap.t;
  7. ops : op list;
}
Sourceval verbs : verb list
Sourceval show_verb : verb -> string
Sourceval show_method : verb -> string
Sourceval sanitize_field : string -> string
Sourceval operationname : operation_id:OaTypes.operation_id -> verb:verb -> unit -> string
Sourceval param_info : loc:OaTypes.param_in -> OaTypes.parameter list -> (string * bool * bool) list
Sourceval 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) option
Sourceval typealias : path:Json_query.path -> id:string option -> string
Sourceval datatype_ref_of_schema : (module OaDocument.STANDARD) -> (module OaDocument.VENDOR) -> path:Json_query.path -> OaTypes.schema -> datatype_ref
Sourceval generalized_object_props : OaTypes.object_specs -> OaTypes.object_props list
Sourceval create : (module OaDocument.STANDARD) -> (module OaDocument.VENDOR) -> OaDocument.t -> t
Sourceval failed_access : Json_query.path -> 'a
Sourceval resolve_parameter_specs : ?depth:int -> t -> path_template:string -> param:string -> OaTypes.parameter_kind -> OaTypes.parameter_specs

resolve_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.

Sourceval resolve_header_specs : ?depth:int -> t -> path_template:string -> header:string -> OaTypes.header_kind -> OaTypes.header_specs

resolve_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.

Sourceval resolve_request_body_specs : ?depth:int -> t -> path_template:string -> OaTypes.request_body_kind -> OaTypes.request_body_specs

resolve_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.

Sourceval resolve_datatype_ref : ?depth:JsonQueryPathMap.key list -> t -> path:JsonQueryPathMap.key -> datatype_ref

resolve_datatype_ref t ~path fully resolves the datatype reference through $ref references to the original schema: ... definition.

Sourceval resolve_schema : ?depth:Json_query.path list -> t -> path:Json_query.path -> OaTypes.schema

resolve_schema t ~path fully resolves the schema through $ref references to the original schema: ... definition.

Sourceval datatypes : query_selector: [< `All | `FlattenedAll | `FlattenedUnions | `Parameters | `Bodies ] -> t -> datatype list
include DkCoder_Std.SCRIPT
Sourceval __init : DkCoder_Std.Context.t -> unit

__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:

  1. calling the __init function 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 __init returns to the caller.
  2. you can override the __init function by simply defining the __init idempotently. That will shadow the top __init and when the bottom __init is executed your __init will 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.

Sourceval __repl : DkCoder_Std.Context.t -> unit

__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:

  1. you can override the __repl function by simply defining the __repl idempotently. That will shadow the top __repl and when the bottom __repl is executed your __repl will be called instead of the do-nothing top __repl.
Sourceval __module_info : unit -> DkCoder_Std.ModuleInfo.t

The run-time module information for the script module.