[OCaml][Fix] Unreferenced enum + Direct recursive types (#23005)

* [OCaml] Fix bugs around enum parsing

Problem: The OCaml client generator threw IllegalArgumentException: Unreferenced enum when encountering enums inside composed schemas
  (anyOf/allOf/oneOf).

Root Causes:
1. The enum collection logic didn't traverse into composed schemas
2. The enum hashing used order-dependent string concatenation, causing lookups to fail when enum values appeared in different orders
3. Enums directly within composed schema branches (not in properties) weren't collected

Solution:

1. Added composed schema support:
  - New `collectEnumSchemasFromComposed()` method handles `anyOf/allOf/oneOf`
  - New `collectEnumSchemasFromList()` method recursively processes composed schema branches
  - Enums directly in composed schemas (not just in properties) are now collected
2. Refactored enum hashing to use Set:
  - Changed from comma-joined strings to `TreeSet<String>` for order-independent, collision-free hashing
  - Handles edge cases like empty string enums `""`
3. Added test case:
  - Tests enums in nested composed schemas
  - Tests enum with empty string value in anyOf

* OCaml: Add support for direct recursive types

* OCaml: Fix enums in anyOf

* OCaml: fix recursive types

* Fix recursion tests

* Fix recursive types, improve tests

* [OCaml] Improve title of generated README.md
This commit is contained in:
Julien Debon
2026-02-23 14:52:19 +01:00
committed by GitHub
parent 1894a07c2c
commit 4f00a34df6
54 changed files with 1450 additions and 150 deletions

View File

@@ -7,12 +7,16 @@ on:
- 'samples/client/petstore/ocaml-fake-petstore/**'
- 'samples/client/petstore/ocaml-oneOf-primitive/**'
- 'samples/client/petstore/ocaml-additional-properties/**'
- 'samples/client/petstore/ocaml-enum-in-composed-schema/**'
- 'samples/client/petstore/ocaml-recursion-test/**'
pull_request:
paths:
- 'samples/client/petstore/ocaml/**'
- 'samples/client/petstore/ocaml-fake-petstore/**'
- 'samples/client/petstore/ocaml-oneOf-primitive/**'
- 'samples/client/petstore/ocaml-additional-properties/**'
- 'samples/client/petstore/ocaml-enum-in-composed-schema/**'
- 'samples/client/petstore/ocaml-recursion-test/**'
jobs:
build:
@@ -26,12 +30,14 @@ jobs:
- 'samples/client/petstore/ocaml-fake-petstore/'
- 'samples/client/petstore/ocaml-oneOf-primitive/'
- 'samples/client/petstore/ocaml-additional-properties/'
- 'samples/client/petstore/ocaml-enum-in-composed-schema/'
- 'samples/client/petstore/ocaml-recursion-test/'
steps:
- uses: actions/checkout@v5
- name: Set-up OCaml
uses: ocaml/setup-ocaml@v3
with:
ocaml-compiler: 5
ocaml-compiler: 5.3
- name: Install
run: opam install . --deps-only --with-test
working-directory: ${{ matrix.sample }}

2
.gitignore vendored
View File

@@ -304,6 +304,8 @@ samples/client/petstore/ocaml/_build/
samples/client/petstore/ocaml-fake-petstore/_build/
samples/client/petstore/ocaml-oneOf-primitive/_build/
samples/client/petstore/ocaml-additional-properties/_build/
samples/client/petstore/ocaml-enum-in-composed-schema/_build/
samples/client/petstore/ocaml-recursion-test/_build/
# jetbrain http client
samples/client/jetbrains/adyen/checkout71/http/client/Apis/http-client.private.env.json

View File

@@ -0,0 +1,6 @@
generatorName: ocaml
outputDir: samples/client/petstore/ocaml-enum-in-composed-schema
inputSpec: modules/openapi-generator/src/test/resources/3_0/ocaml/enum-in-composed-schema.yaml
templateDir: modules/openapi-generator/src/main/resources/ocaml
additionalProperties:
packageName: petstore_client

View File

@@ -0,0 +1,6 @@
generatorName: ocaml
outputDir: samples/client/petstore/ocaml-recursion-test
inputSpec: modules/openapi-generator/src/test/resources/3_0/ocaml/direct-recursion.yaml
templateDir: modules/openapi-generator/src/main/resources/ocaml
additionalProperties:
packageName: recursion_test

View File

@@ -11,7 +11,7 @@ title: Documentation for the ocaml Generator
| generator type | CLIENT | |
| generator language | OCaml | |
| generator default templating engine | mustache | |
| helpTxt | Generates an OCaml client library (beta). | |
| helpTxt | Generates an OCaml client library. | |
## CONFIG OPTIONS
These options may be applied as additional-properties (cli) or configOptions (plugins). Refer to [configuration docs](https://openapi-generator.tech/docs/configuration) for more details.

View File

@@ -51,16 +51,18 @@ public class OCamlClientCodegen extends DefaultCodegen implements CodegenConfig
static final String X_MODEL_MODULE = "x-model-module";
@Setter protected String packageName = "openapi";
@Setter protected String packageVersion = "1.0.0";
@Setter
protected String packageName = "openapi";
@Setter
protected String packageVersion = "1.0.0";
protected String apiDocPath = "docs/";
protected String modelDocPath = "docs/";
protected String apiFolder = "src/apis";
protected String modelFolder = "src/models";
private Map<String, List<String>> enumNames = new HashMap<>();
private Map<String, Schema> enumHash = new HashMap<>();
private Map<String, String> enumUniqNames;
private Map<Set<String>, List<String>> enumNames = new HashMap<>();
private Map<Set<String>, Schema> enumHash = new HashMap<>();
private Map<Set<String>, String> enumUniqNames;
@Override
public CodegenType getTag() {
@@ -74,7 +76,7 @@ public class OCamlClientCodegen extends DefaultCodegen implements CodegenConfig
@Override
public String getHelp() {
return "Generates an OCaml client library (beta).";
return "Generates an OCaml client library.";
}
public OCamlClientCodegen() {
@@ -233,6 +235,105 @@ public class OCamlClientCodegen extends DefaultCodegen implements CodegenConfig
}
/**
* Add support for direct recursive types (e.g., A -> A).
* This does *not* support mutually recursive types (e.g., A -> B -> A), as this is a much more complex beast in OCaml (since mutually recursive types must live in the same file).
*/
@Override
public ModelsMap postProcessModels(ModelsMap objs) {
objs = super.postProcessModels(objs);
for (ModelMap mo : objs.getModels()) {
CodegenModel cm = mo.getModel();
// Check if any property is a self-reference
boolean hasSelfRef = cm.allVars.stream()
.anyMatch(prop -> prop.isSelfReference);
if (hasSelfRef) {
// Collect names of self-referencing properties
Set<String> selfRefPropNames = cm.allVars.stream()
.filter(p -> p.isSelfReference)
.map(p -> p.name)
.collect(Collectors.toSet());
// The property lists (vars, allVars, etc.) contain DIFFERENT objects
// Match by name since isSelfReference might only be set in allVars
List<List<CodegenProperty>> allPropertyLists = Arrays.asList(
cm.allVars, cm.vars, cm.requiredVars, cm.optionalVars,
cm.readOnlyVars, cm.readWriteVars, cm.parentVars
);
for (List<CodegenProperty> propList : allPropertyLists) {
for (CodegenProperty prop : propList) {
if (selfRefPropNames.contains(prop.name)) {
if (prop.isContainer && prop.items != null) {
// For containers, update items and reconstruct the container type
prop.items.dataType = "t";
prop.items.datatypeWithEnum = "t";
if (prop.items.baseType != null) {
prop.items.baseType = "t";
}
if (prop.items.complexType != null) {
prop.items.complexType = "t";
}
// Reconstruct the container type based on the updated items
if (prop.isArray) {
prop.dataType = "t list";
prop.datatypeWithEnum = "t list";
} else if (prop.isMap) {
prop.dataType = "(string * t) list";
prop.datatypeWithEnum = "(string * t) list";
}
} else {
// For non-containers, just replace the type directly
prop.dataType = "t";
prop.datatypeWithEnum = "t";
}
// Update baseType and complexType for all cases
if (prop.baseType != null) {
prop.baseType = "t";
}
if (prop.complexType != null) {
prop.complexType = "t";
}
}
}
}
}
// Fix enum references in composed schemas (anyOf, oneOf, allOf)
if (cm.getComposedSchemas() != null) {
fixEnumReferencesInComposedSchemas(cm.getComposedSchemas().getAnyOf());
fixEnumReferencesInComposedSchemas(cm.getComposedSchemas().getOneOf());
fixEnumReferencesInComposedSchemas(cm.getComposedSchemas().getAllOf());
}
}
return objs;
}
private void fixEnumReferencesInComposedSchemas(List<CodegenProperty> schemas) {
if (schemas == null) {
return;
}
for (CodegenProperty schema : schemas) {
// If this schema is an enum, add Enums. prefix to datatypeWithEnum
if (schema.isEnum) {
if (!schema.datatypeWithEnum.startsWith("Enums.")) {
schema.datatypeWithEnum = "Enums." + schema.datatypeWithEnum;
}
// Also update dataType for the variant constructor
if (!schema.dataType.startsWith("Enums.")) {
schema.dataType = "Enums." + schema.dataType;
}
}
}
}
private void enrichPropertiesWithEnumDefaultValues(List<CodegenProperty> properties) {
for (CodegenProperty property : properties) {
if (property.get_enum() != null && property.get_enum().size() == 1) {
@@ -276,8 +377,10 @@ public class OCamlClientCodegen extends DefaultCodegen implements CodegenConfig
}
@SuppressWarnings("unchecked")
private String hashEnum(Schema schema) {
return ((List<Object>) schema.getEnum()).stream().map(String::valueOf).collect(Collectors.joining(","));
private Set<String> hashEnum(Schema schema) {
return ((List<Object>) schema.getEnum()).stream()
.map(String::valueOf)
.collect(Collectors.toCollection(TreeSet::new));
}
private boolean isEnumSchema(Schema schema) {
@@ -290,7 +393,7 @@ public class OCamlClientCodegen extends DefaultCodegen implements CodegenConfig
} else if (ModelUtils.isMapSchema(schema) && schema.getAdditionalProperties() instanceof Schema) {
collectEnumSchemas(parentName, sName, (Schema) schema.getAdditionalProperties());
} else if (isEnumSchema(schema)) {
String h = hashEnum(schema);
Set<String> h = hashEnum(schema);
if (!enumHash.containsKey(h)) {
enumHash.put(h, schema);
enumNames.computeIfAbsent(h, k -> new ArrayList<>()).add(sName.toLowerCase(Locale.ROOT));
@@ -299,6 +402,8 @@ public class OCamlClientCodegen extends DefaultCodegen implements CodegenConfig
}
}
}
// Note: Composed schemas (anyOf, allOf, oneOf) are handled in the Map-based method
// via collectEnumSchemasFromComposed() which properly processes their structure
}
private void collectEnumSchemas(String sName, Schema schema) {
@@ -327,6 +432,47 @@ public class OCamlClientCodegen extends DefaultCodegen implements CodegenConfig
collectEnumSchemas(pName, ModelUtils.getSchemaItems(schema));
}
}
// Handle composed schemas (anyOf, allOf, oneOf) - recursively process their structure
collectEnumSchemasFromComposed(pName, schema);
}
}
private void collectEnumSchemasFromComposed(String parentName, Schema schema) {
if (schema.getAnyOf() != null) {
collectEnumSchemasFromList(parentName, schema.getAnyOf());
}
if (schema.getAllOf() != null) {
collectEnumSchemasFromList(parentName, schema.getAllOf());
}
if (schema.getOneOf() != null) {
collectEnumSchemasFromList(parentName, schema.getOneOf());
}
}
private void collectEnumSchemasFromList(String parentName, List<Schema> schemas) {
int index = 0;
for (Schema composedSchema : schemas) {
// Check if the composed schema itself is an enum
if (isEnumSchema(composedSchema)) {
String enumName = composedSchema.getName() != null ? composedSchema.getName() : "any_of_" + index;
collectEnumSchemas(parentName, enumName, composedSchema);
}
if (composedSchema.getProperties() != null) {
collectEnumSchemas(parentName, composedSchema.getProperties());
}
if (composedSchema.getAdditionalProperties() != null && composedSchema.getAdditionalProperties() instanceof Schema) {
collectEnumSchemas(parentName, composedSchema.getName(), (Schema) composedSchema.getAdditionalProperties());
}
if (ModelUtils.isArraySchema(composedSchema) && ModelUtils.getSchemaItems(composedSchema) != null) {
collectEnumSchemas(parentName, composedSchema.getName(), ModelUtils.getSchemaItems(composedSchema));
}
// Recursively handle nested composed schemas
collectEnumSchemasFromComposed(parentName, composedSchema);
index++;
}
}
@@ -378,8 +524,8 @@ public class OCamlClientCodegen extends DefaultCodegen implements CodegenConfig
}
private void computeEnumUniqNames() {
Map<String, String> definitiveNames = new HashMap<>();
for (String h : enumNames.keySet()) {
Map<String, Set<String>> definitiveNames = new HashMap<>();
for (Set<String> h : enumNames.keySet()) {
boolean hasDefName = false;
List<String> nameCandidates = enumNames.get(h);
for (String name : nameCandidates) {
@@ -600,13 +746,13 @@ public class OCamlClientCodegen extends DefaultCodegen implements CodegenConfig
String prefix = inner.getEnum() != null ? "Enums." : "";
return "(string * " + prefix + getTypeDeclaration(inner) + ") list";
} else if (p.getEnum() != null) {
String h = hashEnum(p);
Set<String> h = hashEnum(p);
return enumUniqNames.get(h);
}
Schema referencedSchema = ModelUtils.getReferencedSchema(openAPI, p);
if (referencedSchema != null && referencedSchema.getEnum() != null) {
String h = hashEnum(referencedSchema);
Set<String> h = hashEnum(referencedSchema);
return "Enums." + enumUniqNames.get(h);
}
@@ -739,8 +885,8 @@ public class OCamlClientCodegen extends DefaultCodegen implements CodegenConfig
}
}
for (Map.Entry<String, String> e : enumUniqNames.entrySet()) {
allModels.add(buildEnumModelWrapper(e.getValue(), e.getKey()));
for (Map.Entry<Set<String>, String> e : enumUniqNames.entrySet()) {
allModels.add(buildEnumModelWrapper(e.getValue(), String.join(",", e.getKey())));
}
enumUniqNames.clear();
@@ -770,7 +916,7 @@ public class OCamlClientCodegen extends DefaultCodegen implements CodegenConfig
@Override
public String toEnumName(CodegenProperty property) {
String hash = String.join(",", property.get_enum());
Set<String> hash = new TreeSet<>(property.get_enum());
if (enumUniqNames.containsKey(hash)) {
return enumUniqNames.get(hash);

View File

@@ -1,4 +1,4 @@
# {{{projectName}}}
# OCaml API client for {{{packageName}}}
{{#appDescriptionWithNewLines}}
{{{.}}}
{{/appDescriptionWithNewLines}}

View File

@@ -0,0 +1,99 @@
openapi: 3.0.0
info:
description: |
Test for direct recursive types in OCaml generator.
This spec only tests direct recursion (A -> A) and NOT mutual recursion (A -> B -> A).
The OCaml generator does not support mutual recursion because OCaml requires mutually
recursive types to be declared together in the same file using "type a = ... and b = ..."
syntax. The generator's architecture uses one model per file, making this infeasible.
Note: The generic test file modules/openapi-generator/src/test/resources/3_0/recursion.yaml
contains both Foo (direct recursion) and Bar/Baz (mutual recursion), which is why we
cannot use it for OCaml testing.
version: 1.0.0
title: OCaml Direct Recursion Test
paths: {}
components:
schemas:
ArrayRecursion:
type: object
description: Test recursive type within an array (optional)
properties:
value:
type: integer
format: int32
children:
type: array
items:
$ref: '#/components/schemas/ArrayRecursion'
required:
- value
DirectRecursion:
type: object
description: Test direct self-reference (not in container, optional)
properties:
id:
type: string
next:
$ref: '#/components/schemas/DirectRecursion'
required:
- id
MapRecursion:
type: object
description: Test recursive type within a map (optional)
properties:
name:
type: string
childMap:
type: object
additionalProperties:
$ref: '#/components/schemas/MapRecursion'
required:
- name
RequiredRecursion:
type: object
description: Test required self-reference field
properties:
value:
type: string
parent:
$ref: '#/components/schemas/RequiredRecursion'
required:
- value
- parent
RequiredArrayRecursion:
type: object
description: Test required array of self-references
properties:
id:
type: integer
siblings:
type: array
items:
$ref: '#/components/schemas/RequiredArrayRecursion'
required:
- id
- siblings
MultipleRecursiveFields:
type: object
description: Test multiple recursive fields in the same model
properties:
name:
type: string
left:
$ref: '#/components/schemas/MultipleRecursiveFields'
right:
$ref: '#/components/schemas/MultipleRecursiveFields'
children:
type: array
items:
$ref: '#/components/schemas/MultipleRecursiveFields'
required:
- name

View File

@@ -0,0 +1,49 @@
openapi: 3.0.0
info:
title: Test Enum in Composed Schema
version: 1.0.0
paths:
/test:
get:
operationId: getTest
responses:
'200':
description: Success
content:
application/json:
schema:
$ref: '#/components/schemas/TestModel'
components:
schemas:
TestModel:
type: object
properties:
name:
type: string
config:
anyOf:
- allOf:
- type: object
properties:
type:
type: string
enum:
- type1
- type2
- type3
options:
type: object
properties:
value:
type: string
enum:
- value1
- value2
- value3
optionalUrl:
anyOf:
- type: string
format: uri
- type: string
enum:
- ""

View File

@@ -1,4 +1,4 @@
#
# OCaml API client for petstore_client
No description provided (generated by Openapi Generator https://github.com/openapitools/openapi-generator)
This OCaml package is automatically generated by the [OpenAPI Generator](https://openapi-generator.tech) project:

View File

@@ -0,0 +1,2 @@
version=0.27.0
ocaml-version=4.14.0

View File

@@ -0,0 +1,23 @@
# OpenAPI Generator Ignore
# Generated by openapi-generator https://github.com/openapitools/openapi-generator
# Use this file to prevent files from being overwritten by the generator.
# The patterns follow closely to .gitignore or .dockerignore.
# As an example, the C# client generator defines ApiClient.cs.
# You can make changes and tell OpenAPI Generator to ignore just this file by uncommenting the following line:
#ApiClient.cs
# You can match any string of characters against a directory, file or extension with a single asterisk (*):
#foo/*/qux
# The above matches foo/bar/qux and foo/baz/qux, but not foo/bar/baz/qux
# You can recursively match patterns against a directory, file or extension with a double asterisk (**):
#foo/**/qux
# This matches foo/bar/qux, foo/baz/qux, and foo/bar/baz/qux
# You can also negate patterns with an exclamation (!).
# For example, you can ignore all files in a docs folder with the file extension .md:
#docs/*.md
# Then explicitly reverse the ignore rule for a single file:
#!docs/README.md

View File

@@ -0,0 +1,14 @@
.ocamlformat
README.md
dune
dune-project
petstore_client.opam
src/apis/default_api.ml
src/apis/default_api.mli
src/models/test_model.ml
src/models/test_model_config.ml
src/models/test_model_config_all_of_options.ml
src/models/test_model_optional_url.ml
src/support/enums.ml
src/support/jsonSupport.ml
src/support/request.ml

View File

@@ -0,0 +1 @@
7.21.0-SNAPSHOT

View File

@@ -0,0 +1,33 @@
# OCaml API client for petstore_client
No description provided (generated by Openapi Generator https://github.com/openapitools/openapi-generator)
This OCaml package is automatically generated by the [OpenAPI Generator](https://openapi-generator.tech) project:
- API version: 1.0.0
- Package version: 1.0.0
- Generator version: 7.21.0-SNAPSHOT
- Build package: org.openapitools.codegen.languages.OCamlClientCodegen
## Requirements.
OCaml 5.x
## Installation
Please run the following commands to build the package `petstore_client`:
```sh
opam install . --deps-only --with-test
eval $(opam env)
dune build
```
## Getting Started
The generated directory structure is:
- `src/apis`: contains several modules, each with several functions. Each function is an API endpoint.
- `src/models`: contains several modules. Each module contains:
- a type `t` representing an input and/or output schema of the OpenAPI spec
- a smart constructor `create` for this type
- `src/support`: various modules used by the generated APIs and Models

View File

@@ -0,0 +1,9 @@
(include_subdirs unqualified)
(library
(name petstore_client)
(public_name petstore_client)
(flags (:standard -w -27))
(libraries str cohttp-lwt-unix lwt yojson ppx_deriving_yojson.runtime)
(preprocess (pps ppx_deriving_yojson ppx_deriving.std))
(wrapped true)
)

View File

@@ -0,0 +1,2 @@
(lang dune 2.0)
(name petstore_client)

View File

@@ -0,0 +1,25 @@
opam-version: "2.0"
name: "petstore_client"
version: "1.0.0"
synopsis: ""
description: """
Longer description
"""
maintainer: "Name <email>"
authors: "Name <email>"
license: ""
homepage: ""
bug-reports: ""
dev-repo: ""
depends: [
"ocaml"
"ocamlfind"
"ocamlformat" {= "0.27.0"}
"dune"
"ppx_deriving_yojson"
"conf-libev"
"lwt"
"cohttp-lwt-unix" {< "6.0.0"}
"cohttp-async" {< "6.0.0"}
]
build: ["dune" "build" "-p" name]

View File

@@ -0,0 +1,14 @@
(*
* This file has been generated by the OCamlClientCodegen generator for openapi-generator.
*
* Generated by: https://openapi-generator.tech
*
*)
let get_test () =
let open Lwt.Infix in
let uri = Request.build_uri "/test" in
let headers = Request.default_headers in
Cohttp_lwt_unix.Client.call `GET uri ~headers >>= fun (resp, body) ->
Request.read_json_body_as (JsonSupport.unwrap Test_model.of_yojson) resp body

View File

@@ -0,0 +1,8 @@
(*
* This file has been generated by the OCamlClientCodegen generator for openapi-generator.
*
* Generated by: https://openapi-generator.tech
*
*)
val get_test : unit -> Test_model.t Lwt.t

View File

@@ -0,0 +1,38 @@
(*
* This file has been generated by the OCamlClientCodegen generator for openapi-generator.
*
* Generated by: https://openapi-generator.tech
*
*)
type t = {
name: string
option [@default None]
; [@key "name"]
config: Test_model_config.t
option [@default None]
; [@key "config"]
optional_url: Test_model_optional_url.t
option [@default None]
; [@key "optionalUrl"]
} [@@deriving yojson { strict = false }, show, eq ];;
let create () : t = {
name = None;
config = None;
optional_url = None;
}

View File

@@ -0,0 +1,31 @@
(*
* This file has been generated by the OCamlClientCodegen generator for openapi-generator.
*
* Generated by: https://openapi-generator.tech
*
*)
type t = {
_type: Enums.testmodel_config_type
option [@default
None
]
; [@key "type"]
options: Test_model_config_all_of_options.t
option [@default None]
; [@key "options"]
} [@@deriving yojson { strict = false }, show, eq ];;
let create () : t = {
_type = None;
options = None;
}

View File

@@ -0,0 +1,24 @@
(*
* This file has been generated by the OCamlClientCodegen generator for openapi-generator.
*
* Generated by: https://openapi-generator.tech
*
*)
type t = {
value: Enums.value
option [@default
None
]
; [@key "value"]
} [@@deriving yojson { strict = false }, show, eq ];;
let create () : t = {
value = None;
}

View File

@@ -0,0 +1,35 @@
(*
* This file has been generated by the OCamlClientCodegen generator for openapi-generator.
*
* Generated by: https://openapi-generator.tech
*
*)
type t =
| AnyOf0 of string
| AnyOf1 of Enums.any_of_1
[@@deriving show, eq];;
let to_yojson = function
| AnyOf0 v -> [%to_yojson: string] v
| AnyOf1 v -> [%to_yojson: Enums.any_of_1] v
(* Manual implementations because the derived one encodes into a tuple list where the first element is the constructor name. *)
let of_yojson json =
[
[%of_yojson: string] json
|> Stdlib.Result.to_option
|> Stdlib.Option.map (fun v -> AnyOf0 v);
[%of_yojson: Enums.any_of_1] json
|> Stdlib.Result.to_option
|> Stdlib.Option.map (fun v -> AnyOf1 v);
]
|> Stdlib.List.filter_map (Fun.id)
|> function
| t :: _ -> Ok t (* Return the first successful parsing. *)
| [] -> Error ("Failed to parse JSON " ^ Yojson.Safe.show json ^ " into a value of type Test_model_optional_url.t")

View File

@@ -0,0 +1,40 @@
(*
* This file has been generated by the OCamlClientCodegen generator for openapi-generator.
*
* Generated by: https://openapi-generator.tech
*
*)
type any_of_1 = [
| `Empty [@printer fun fmt _ -> Format.pp_print_string fmt "empty"] [@name "empty"]
] [@@deriving yojson, show { with_path = false }, eq];;
let any_of_1_of_yojson json = any_of_1_of_yojson (`List [json])
let any_of_1_to_yojson e =
match any_of_1_to_yojson e with
| `List [json] -> json
| json -> json
type value = [
| `Value1 [@printer fun fmt _ -> Format.pp_print_string fmt "value1"] [@name "value1"]
| `Value2 [@printer fun fmt _ -> Format.pp_print_string fmt "value2"] [@name "value2"]
| `Value3 [@printer fun fmt _ -> Format.pp_print_string fmt "value3"] [@name "value3"]
] [@@deriving yojson, show { with_path = false }, eq];;
let value_of_yojson json = value_of_yojson (`List [json])
let value_to_yojson e =
match value_to_yojson e with
| `List [json] -> json
| json -> json
type testmodel_config_type = [
| `Type1 [@printer fun fmt _ -> Format.pp_print_string fmt "type1"] [@name "type1"]
| `Type2 [@printer fun fmt _ -> Format.pp_print_string fmt "type2"] [@name "type2"]
| `Type3 [@printer fun fmt _ -> Format.pp_print_string fmt "type3"] [@name "type3"]
] [@@deriving yojson, show { with_path = false }, eq];;
let testmodel_config_type_of_yojson json = testmodel_config_type_of_yojson (`List [json])
let testmodel_config_type_to_yojson e =
match testmodel_config_type_to_yojson e with
| `List [json] -> json
| json -> json

View File

@@ -0,0 +1,68 @@
open Ppx_deriving_yojson_runtime
let unwrap to_json json =
match to_json json with
| Result.Ok json -> json
| Result.Error s -> failwith s
let to_int json =
match json with
| `Int x -> x
| `Intlit s -> int_of_string s
| _ -> failwith "JsonSupport.to_int"
let to_bool json =
match json with
| `Bool x -> x
| _ -> failwith "JsonSupport.to_bool"
let to_float json =
match json with
| `Float x -> x
| _ -> failwith "JsonSupport.to_float"
let to_string json =
match json with
| `String s -> s
| _ -> failwith "JsonSupport.to_string"
let to_int32 json : int32 =
match json with
| `Int x -> Int32.of_int x
| `Intlit s -> Int32.of_string s
| _ -> failwith "JsonSupport.to_int32"
let to_int64 json : int64 =
match json with
| `Int x -> Int64.of_int x
| `Intlit s -> Int64.of_string s
| _ -> failwith "JsonSupport.to_int64"
let of_int x = `Int x
let of_bool b = `Bool b
let of_float x = `Float x
let of_string s = `String s
let of_int32 x = `Intlit (Int32.to_string x)
let of_int64 x = `Intlit (Int64.to_string x)
let of_list_of of_f l = `List (Stdlib.List.map of_f l)
let of_map_of of_f l = `Assoc (Stdlib.List.map (fun (k, v) -> (k, of_f v)) l)
let to_map_of of_f json =
match json with
| `Assoc l ->
Stdlib.List.fold_right
(fun (k, json) acc ->
match (of_f json, acc) with
| Stdlib.Result.Ok parsed_v, Stdlib.Result.Ok tl ->
Stdlib.Result.Ok ((k, parsed_v) :: tl)
| Stdlib.Result.Error e, _ -> Stdlib.Result.Error e
| _, Stdlib.Result.Error e -> Stdlib.Result.Error e)
l (Stdlib.Result.Ok [])
| _ -> Stdlib.Result.Error "Expected"

View File

@@ -0,0 +1,110 @@
let api_key = ""
let base_url = "http://localhost"
let default_headers = Cohttp.Header.init_with "Content-Type" "application/json"
let option_fold f default o =
match o with
| Some v -> f v
| None -> default
let build_uri operation_path = Uri.of_string (base_url ^ operation_path)
let add_string_header headers key value =
Cohttp.Header.add headers key value
let add_string_header_multi headers key values =
Cohttp.Header.add_multi headers key values
let add_header headers key to_string value =
Cohttp.Header.add headers key (to_string value)
let add_header_multi headers key to_string value =
Cohttp.Header.add_multi headers key (to_string value)
let maybe_add_header headers key to_string value =
option_fold (add_header headers key to_string) headers value
let maybe_add_header_multi headers key to_string value =
option_fold (add_header_multi headers key to_string) headers value
let write_string_body s = Cohttp_lwt.Body.of_string s
let write_json_body payload =
Cohttp_lwt.Body.of_string (Yojson.Safe.to_string payload ~std:true)
let write_as_json_body to_json payload = write_json_body (to_json payload)
let handle_response resp on_success_handler =
match Cohttp.Response.status resp with
| #Cohttp.Code.success_status -> on_success_handler ()
| s -> failwith ("Server responded with status " ^ Cohttp.Code.(reason_phrase_of_code (code_of_status s)))
let handle_unit_response resp = handle_response resp (fun () -> Lwt.return ())
let read_json_body resp body =
handle_response resp (fun () ->
(Lwt.(Cohttp_lwt.Body.to_string body >|= Yojson.Safe.from_string)))
let read_json_body_as of_json resp body =
Lwt.(read_json_body resp body >|= of_json)
let read_json_body_as_list resp body =
Lwt.(read_json_body resp body >|= Yojson.Safe.Util.to_list)
let read_json_body_as_list_of of_json resp body =
Lwt.(read_json_body_as_list resp body >|= Stdlib.List.map of_json)
let read_json_body_as_map resp body =
Lwt.(read_json_body resp body >|= Yojson.Safe.Util.to_assoc)
let read_json_body_as_map_of of_json resp body =
Lwt.(read_json_body_as_map resp body >|= Stdlib.List.map (fun (s, v) -> (s, of_json v)))
let replace_string_path_param uri param_name param_value =
let regexp = Str.regexp (Str.quote ("{" ^ param_name ^ "}")) in
let path = Str.global_replace regexp param_value (Uri.pct_decode (Uri.path uri)) in
Uri.with_path uri path
let replace_path_param uri param_name to_string param_value =
replace_string_path_param uri param_name (to_string param_value)
let maybe_replace_path_param uri param_name to_string param_value =
option_fold (replace_path_param uri param_name to_string) uri param_value
let add_query_param uri param_name to_string param_value =
Uri.add_query_param' uri (param_name, to_string param_value)
let add_query_param_list uri param_name to_string param_value =
Uri.add_query_param uri (param_name, to_string param_value)
let maybe_add_query_param uri param_name to_string param_value =
option_fold (add_query_param uri param_name to_string) uri param_value
(** Corresponds to:
- [style = form]
- [explode = true]
- type [object]
See https://swagger.io/docs/specification/v3_0/serialization/#query-parameters
*)
let add_query_param_exploded_form_object uri _param_name to_string param_value =
Stdlib.List.fold_left
(fun uri (param_name, param_value) -> add_query_param uri param_name to_string param_value)
uri
param_value
let init_form_encoded_body () = ""
let add_form_encoded_body_param params param_name to_string param_value =
let new_param_enc = Printf.sprintf {|%s=%s|} (Uri.pct_encode param_name) (Uri.pct_encode (to_string param_value)) in
if params = ""
then new_param_enc
else Printf.sprintf {|%s&%s|} params new_param_enc
let add_form_encoded_body_param_list params param_name to_string new_params =
add_form_encoded_body_param params param_name (String.concat ",") (to_string new_params)
let maybe_add_form_encoded_body_param params param_name to_string param_value =
option_fold (add_form_encoded_body_param params param_name to_string) params param_value
let finalize_form_encoded_body body = Cohttp_lwt.Body.of_string body

View File

@@ -1,4 +1,4 @@
#
# OCaml API client for petstore_client
This spec is mainly for testing Petstore server and contains fake endpoints, models. Please do not use this for any other purpose. Special characters: \'' \\
This OCaml package is automatically generated by the [OpenAPI Generator](https://openapi-generator.tech) project:

View File

@@ -68,7 +68,7 @@ let find_pets_by_status ~status =
let open Lwt.Infix in
let uri = Request.build_uri "/pet/findByStatus" in
let headers = Request.default_headers in
let uri = Request.add_query_param_list uri "status" (Stdlib.List.map Enums.show_status
let uri = Request.add_query_param_list uri "status" (Stdlib.List.map Enums.show_pet_status
)
status in

View File

@@ -7,7 +7,7 @@
val add_pet : pet_t:Pet.t -> unit Lwt.t
val delete_pet : pet_id:int64 -> ?api_key:string -> unit -> unit Lwt.t
val find_pets_by_status : status:Enums.status list -> Pet.t list Lwt.t
val find_pets_by_status : status:Enums.pet_status list -> Pet.t list Lwt.t
val find_pets_by_tags : tags:string list -> Pet.t list Lwt.t
val get_pet_by_id : pet_id:int64 -> Pet.t Lwt.t
val update_pet : pet_t:Pet.t -> unit Lwt.t

View File

@@ -30,7 +30,7 @@
None
]
; [@key "enum_number"]
outer_enum: Enums.order_status
outer_enum: Enums.status
option [@default None]
@@ -42,7 +42,7 @@
; [@key "outerEnumInteger"]
outer_enum_default_value: Enums.order_status
outer_enum_default_value: Enums.status
option [@default None]

View File

@@ -33,7 +33,7 @@
; [@key "shipDate"]
status: Enums.order_status
status: Enums.status
option [@default
None

View File

@@ -35,7 +35,7 @@
[@default []]
; [@key "tags"]
status: Enums.status
status: Enums.pet_status
option [@default
None

View File

@@ -5,101 +5,9 @@
*
*)
type outerenuminteger = [
| `_0 [@printer fun fmt _ -> Format.pp_print_string fmt "0"] [@name "0"]
| `_1 [@printer fun fmt _ -> Format.pp_print_string fmt "1"] [@name "1"]
| `_2 [@printer fun fmt _ -> Format.pp_print_string fmt "2"] [@name "2"]
] [@@deriving yojson, show { with_path = false }, eq];;
let outerenuminteger_of_yojson json = outerenuminteger_of_yojson (`List [json])
let outerenuminteger_to_yojson e =
match outerenuminteger_to_yojson e with
| `List [json] -> json
| json -> json
type map_of_enum_string = [
| `UPPER [@printer fun fmt _ -> Format.pp_print_string fmt "UPPER"] [@name "UPPER"]
| `Lower [@printer fun fmt _ -> Format.pp_print_string fmt "lower"] [@name "lower"]
] [@@deriving yojson, show { with_path = false }, eq];;
let map_of_enum_string_of_yojson json = map_of_enum_string_of_yojson (`List [json])
let map_of_enum_string_to_yojson e =
match map_of_enum_string_to_yojson e with
| `List [json] -> json
| json -> json
type enum_integer = [
| `_1 [@printer fun fmt _ -> Format.pp_print_string fmt "1"] [@name "1"]
| `Minus1 [@printer fun fmt _ -> Format.pp_print_string fmt "-1"] [@name "-1"]
] [@@deriving yojson, show { with_path = false }, eq];;
let enum_integer_of_yojson json = enum_integer_of_yojson (`List [json])
let enum_integer_to_yojson e =
match enum_integer_to_yojson e with
| `List [json] -> json
| json -> json
type just_symbol = [
| `Greater_ThanEqual [@printer fun fmt _ -> Format.pp_print_string fmt ">="] [@name ">="]
| `Dollar [@printer fun fmt _ -> Format.pp_print_string fmt "$"] [@name "$"]
] [@@deriving yojson, show { with_path = false }, eq];;
let just_symbol_of_yojson json = just_symbol_of_yojson (`List [json])
let just_symbol_to_yojson e =
match just_symbol_to_yojson e with
| `List [json] -> json
| json -> json
type enumclass = [
| `_abc [@printer fun fmt _ -> Format.pp_print_string fmt "_abc"] [@name "_abc"]
| `Minusefg [@printer fun fmt _ -> Format.pp_print_string fmt "-efg"] [@name "-efg"]
| `Left_ParenthesisxyzRight_Parenthesis [@printer fun fmt _ -> Format.pp_print_string fmt "(xyz)"] [@name "(xyz)"]
] [@@deriving yojson, show { with_path = false }, eq];;
let enumclass_of_yojson json = enumclass_of_yojson (`List [json])
let enumclass_to_yojson e =
match enumclass_to_yojson e with
| `List [json] -> json
| json -> json
type status = [
| `Available [@printer fun fmt _ -> Format.pp_print_string fmt "available"] [@name "available"]
| `Pending [@printer fun fmt _ -> Format.pp_print_string fmt "pending"] [@name "pending"]
| `Sold [@printer fun fmt _ -> Format.pp_print_string fmt "sold"] [@name "sold"]
] [@@deriving yojson, show { with_path = false }, eq];;
let status_of_yojson json = status_of_yojson (`List [json])
let status_to_yojson e =
match status_to_yojson e with
| `List [json] -> json
| json -> json
type order_status = [
| `Placed [@printer fun fmt _ -> Format.pp_print_string fmt "placed"] [@name "placed"]
| `Approved [@printer fun fmt _ -> Format.pp_print_string fmt "approved"] [@name "approved"]
| `Delivered [@printer fun fmt _ -> Format.pp_print_string fmt "delivered"] [@name "delivered"]
] [@@deriving yojson, show { with_path = false }, eq];;
let order_status_of_yojson json = order_status_of_yojson (`List [json])
let order_status_to_yojson e =
match order_status_to_yojson e with
| `List [json] -> json
| json -> json
type enum_query_integer = [
| `_1 [@printer fun fmt _ -> Format.pp_print_string fmt "1"] [@name "1"]
| `Minus2 [@printer fun fmt _ -> Format.pp_print_string fmt "-2"] [@name "-2"]
] [@@deriving yojson, show { with_path = false }, eq];;
let enum_query_integer_of_yojson json = enum_query_integer_of_yojson (`List [json])
let enum_query_integer_to_yojson e =
match enum_query_integer_to_yojson e with
| `List [json] -> json
| json -> json
type enum_form_string_array = [
| `Greater_Than [@printer fun fmt _ -> Format.pp_print_string fmt ">"] [@name ">"]
| `Dollar [@printer fun fmt _ -> Format.pp_print_string fmt "$"] [@name "$"]
| `Greater_Than [@printer fun fmt _ -> Format.pp_print_string fmt ">"] [@name ">"]
] [@@deriving yojson, show { with_path = false }, eq];;
let enum_form_string_array_of_yojson json = enum_form_string_array_of_yojson (`List [json])
@@ -108,35 +16,14 @@ let enum_form_string_array_to_yojson e =
| `List [json] -> json
| json -> json
type parentwithnullable_type = [
| `ChildWithNullable [@printer fun fmt _ -> Format.pp_print_string fmt "ChildWithNullable"] [@name "ChildWithNullable"]
type just_symbol = [
| `Dollar [@printer fun fmt _ -> Format.pp_print_string fmt "$"] [@name "$"]
| `Greater_ThanEqual [@printer fun fmt _ -> Format.pp_print_string fmt ">="] [@name ">="]
] [@@deriving yojson, show { with_path = false }, eq];;
let parentwithnullable_type_of_yojson json = parentwithnullable_type_of_yojson (`List [json])
let parentwithnullable_type_to_yojson e =
match parentwithnullable_type_to_yojson e with
| `List [json] -> json
| json -> json
type enum_number = [
| `_1Period1 [@printer fun fmt _ -> Format.pp_print_string fmt "1.1"] [@name "1.1"]
| `Minus1Period2 [@printer fun fmt _ -> Format.pp_print_string fmt "-1.2"] [@name "-1.2"]
] [@@deriving yojson, show { with_path = false }, eq];;
let enum_number_of_yojson json = enum_number_of_yojson (`List [json])
let enum_number_to_yojson e =
match enum_number_to_yojson e with
| `List [json] -> json
| json -> json
type array_enum = [
| `Fish [@printer fun fmt _ -> Format.pp_print_string fmt "fish"] [@name "fish"]
| `Crab [@printer fun fmt _ -> Format.pp_print_string fmt "crab"] [@name "crab"]
] [@@deriving yojson, show { with_path = false }, eq];;
let array_enum_of_yojson json = array_enum_of_yojson (`List [json])
let array_enum_to_yojson e =
match array_enum_to_yojson e with
let just_symbol_of_yojson json = just_symbol_of_yojson (`List [json])
let just_symbol_to_yojson e =
match just_symbol_to_yojson e with
| `List [json] -> json
| json -> json
@@ -151,7 +38,109 @@ let singlereftype_to_yojson e =
| `List [json] -> json
| json -> json
type enum_number = [
| `Minus1Period2 [@printer fun fmt _ -> Format.pp_print_string fmt "-1.2"] [@name "-1.2"]
| `_1Period1 [@printer fun fmt _ -> Format.pp_print_string fmt "1.1"] [@name "1.1"]
] [@@deriving yojson, show { with_path = false }, eq];;
let enum_number_of_yojson json = enum_number_of_yojson (`List [json])
let enum_number_to_yojson e =
match enum_number_to_yojson e with
| `List [json] -> json
| json -> json
type status = [
| `Approved [@printer fun fmt _ -> Format.pp_print_string fmt "approved"] [@name "approved"]
| `Delivered [@printer fun fmt _ -> Format.pp_print_string fmt "delivered"] [@name "delivered"]
| `Placed [@printer fun fmt _ -> Format.pp_print_string fmt "placed"] [@name "placed"]
] [@@deriving yojson, show { with_path = false }, eq];;
let status_of_yojson json = status_of_yojson (`List [json])
let status_to_yojson e =
match status_to_yojson e with
| `List [json] -> json
| json -> json
type outerenuminteger = [
| `_0 [@printer fun fmt _ -> Format.pp_print_string fmt "0"] [@name "0"]
| `_1 [@printer fun fmt _ -> Format.pp_print_string fmt "1"] [@name "1"]
| `_2 [@printer fun fmt _ -> Format.pp_print_string fmt "2"] [@name "2"]
] [@@deriving yojson, show { with_path = false }, eq];;
let outerenuminteger_of_yojson json = outerenuminteger_of_yojson (`List [json])
let outerenuminteger_to_yojson e =
match outerenuminteger_to_yojson e with
| `List [json] -> json
| json -> json
type enum_integer = [
| `Minus1 [@printer fun fmt _ -> Format.pp_print_string fmt "-1"] [@name "-1"]
| `_1 [@printer fun fmt _ -> Format.pp_print_string fmt "1"] [@name "1"]
] [@@deriving yojson, show { with_path = false }, eq];;
let enum_integer_of_yojson json = enum_integer_of_yojson (`List [json])
let enum_integer_to_yojson e =
match enum_integer_to_yojson e with
| `List [json] -> json
| json -> json
type enum_query_integer = [
| `Minus2 [@printer fun fmt _ -> Format.pp_print_string fmt "-2"] [@name "-2"]
| `_1 [@printer fun fmt _ -> Format.pp_print_string fmt "1"] [@name "1"]
] [@@deriving yojson, show { with_path = false }, eq];;
let enum_query_integer_of_yojson json = enum_query_integer_of_yojson (`List [json])
let enum_query_integer_to_yojson e =
match enum_query_integer_to_yojson e with
| `List [json] -> json
| json -> json
type array_enum = [
| `Crab [@printer fun fmt _ -> Format.pp_print_string fmt "crab"] [@name "crab"]
| `Fish [@printer fun fmt _ -> Format.pp_print_string fmt "fish"] [@name "fish"]
] [@@deriving yojson, show { with_path = false }, eq];;
let array_enum_of_yojson json = array_enum_of_yojson (`List [json])
let array_enum_to_yojson e =
match array_enum_to_yojson e with
| `List [json] -> json
| json -> json
type parentwithnullable_type = [
| `ChildWithNullable [@printer fun fmt _ -> Format.pp_print_string fmt "ChildWithNullable"] [@name "ChildWithNullable"]
] [@@deriving yojson, show { with_path = false }, eq];;
let parentwithnullable_type_of_yojson json = parentwithnullable_type_of_yojson (`List [json])
let parentwithnullable_type_to_yojson e =
match parentwithnullable_type_to_yojson e with
| `List [json] -> json
| json -> json
type map_of_enum_string = [
| `UPPER [@printer fun fmt _ -> Format.pp_print_string fmt "UPPER"] [@name "UPPER"]
| `Lower [@printer fun fmt _ -> Format.pp_print_string fmt "lower"] [@name "lower"]
] [@@deriving yojson, show { with_path = false }, eq];;
let map_of_enum_string_of_yojson json = map_of_enum_string_of_yojson (`List [json])
let map_of_enum_string_to_yojson e =
match map_of_enum_string_to_yojson e with
| `List [json] -> json
| json -> json
type enumclass = [
| `Left_ParenthesisxyzRight_Parenthesis [@printer fun fmt _ -> Format.pp_print_string fmt "(xyz)"] [@name "(xyz)"]
| `Minusefg [@printer fun fmt _ -> Format.pp_print_string fmt "-efg"] [@name "-efg"]
| `_abc [@printer fun fmt _ -> Format.pp_print_string fmt "_abc"] [@name "_abc"]
] [@@deriving yojson, show { with_path = false }, eq];;
let enumclass_of_yojson json = enumclass_of_yojson (`List [json])
let enumclass_to_yojson e =
match enumclass_to_yojson e with
| `List [json] -> json
| json -> json
type enum_string = [
| `Empty [@printer fun fmt _ -> Format.pp_print_string fmt "empty"] [@name "empty"]
| `UPPER [@printer fun fmt _ -> Format.pp_print_string fmt "UPPER"] [@name "UPPER"]
| `Lower [@printer fun fmt _ -> Format.pp_print_string fmt "lower"] [@name "lower"]
] [@@deriving yojson, show { with_path = false }, eq];;
@@ -161,3 +150,15 @@ let enum_string_to_yojson e =
match enum_string_to_yojson e with
| `List [json] -> json
| json -> json
type pet_status = [
| `Available [@printer fun fmt _ -> Format.pp_print_string fmt "available"] [@name "available"]
| `Pending [@printer fun fmt _ -> Format.pp_print_string fmt "pending"] [@name "pending"]
| `Sold [@printer fun fmt _ -> Format.pp_print_string fmt "sold"] [@name "sold"]
] [@@deriving yojson, show { with_path = false }, eq];;
let pet_status_of_yojson json = pet_status_of_yojson (`List [json])
let pet_status_to_yojson e =
match pet_status_to_yojson e with
| `List [json] -> json
| json -> json

View File

@@ -1,4 +1,4 @@
#
# OCaml API client for petstore_client
No description provided (generated by Openapi Generator https://github.com/openapitools/openapi-generator)
This OCaml package is automatically generated by the [OpenAPI Generator](https://openapi-generator.tech) project:

View File

@@ -0,0 +1,2 @@
version=0.27.0
ocaml-version=4.14.0

View File

@@ -0,0 +1,23 @@
# OpenAPI Generator Ignore
# Generated by openapi-generator https://github.com/openapitools/openapi-generator
# Use this file to prevent files from being overwritten by the generator.
# The patterns follow closely to .gitignore or .dockerignore.
# As an example, the C# client generator defines ApiClient.cs.
# You can make changes and tell OpenAPI Generator to ignore just this file by uncommenting the following line:
#ApiClient.cs
# You can match any string of characters against a directory, file or extension with a single asterisk (*):
#foo/*/qux
# The above matches foo/bar/qux and foo/baz/qux, but not foo/bar/baz/qux
# You can recursively match patterns against a directory, file or extension with a double asterisk (**):
#foo/**/qux
# This matches foo/bar/qux, foo/baz/qux, and foo/bar/baz/qux
# You can also negate patterns with an exclamation (!).
# For example, you can ignore all files in a docs folder with the file extension .md:
#docs/*.md
# Then explicitly reverse the ignore rule for a single file:
#!docs/README.md

View File

@@ -0,0 +1,14 @@
.ocamlformat
README.md
dune
dune-project
recursion_test.opam
src/models/array_recursion.ml
src/models/direct_recursion.ml
src/models/map_recursion.ml
src/models/multiple_recursive_fields.ml
src/models/required_array_recursion.ml
src/models/required_recursion.ml
src/support/enums.ml
src/support/jsonSupport.ml
src/support/request.ml

View File

@@ -0,0 +1 @@
7.21.0-SNAPSHOT

View File

@@ -0,0 +1,44 @@
# OCaml API client for recursion_test
Test for direct recursive types in OCaml generator.
This spec only tests direct recursion (A -> A) and NOT mutual recursion (A -> B -> A).
The OCaml generator does not support mutual recursion because OCaml requires mutually
recursive types to be declared together in the same file using \''type a = ... and b = ...\''
syntax. The generator's architecture uses one model per file, making this infeasible.
Note: The generic test file modules/openapi-generator/src/test/resources/3_0/recursion.yaml
contains both Foo (direct recursion) and Bar/Baz (mutual recursion), which is why we
cannot use it for OCaml testing.
This OCaml package is automatically generated by the [OpenAPI Generator](https://openapi-generator.tech) project:
- API version: 1.0.0
- Package version: 1.0.0
- Generator version: 7.21.0-SNAPSHOT
- Build package: org.openapitools.codegen.languages.OCamlClientCodegen
## Requirements.
OCaml 5.x
## Installation
Please run the following commands to build the package `recursion_test`:
```sh
opam install . --deps-only --with-test
eval $(opam env)
dune build
```
## Getting Started
The generated directory structure is:
- `src/apis`: contains several modules, each with several functions. Each function is an API endpoint.
- `src/models`: contains several modules. Each module contains:
- a type `t` representing an input and/or output schema of the OpenAPI spec
- a smart constructor `create` for this type
- `src/support`: various modules used by the generated APIs and Models

View File

@@ -0,0 +1,9 @@
(include_subdirs unqualified)
(library
(name recursion_test)
(public_name recursion_test)
(flags (:standard -w -27))
(libraries str cohttp-lwt-unix lwt yojson ppx_deriving_yojson.runtime)
(preprocess (pps ppx_deriving_yojson ppx_deriving.std))
(wrapped true)
)

View File

@@ -0,0 +1,2 @@
(lang dune 2.0)
(name recursion_test)

View File

@@ -0,0 +1,25 @@
opam-version: "2.0"
name: "recursion_test"
version: "1.0.0"
synopsis: ""
description: """
Longer description
"""
maintainer: "Name <email>"
authors: "Name <email>"
license: ""
homepage: ""
bug-reports: ""
dev-repo: ""
depends: [
"ocaml"
"ocamlfind"
"ocamlformat" {= "0.27.0"}
"dune"
"ppx_deriving_yojson"
"conf-libev"
"lwt"
"cohttp-lwt-unix" {< "6.0.0"}
"cohttp-async" {< "6.0.0"}
]
build: ["dune" "build" "-p" name]

View File

@@ -0,0 +1,31 @@
(*
* This file has been generated by the OCamlClientCodegen generator for openapi-generator.
*
* Generated by: https://openapi-generator.tech
*
* Schema Array_recursion.t : Test recursive type within an array (optional)
*)
type t = {
value: int32
; [@key "value"]
children: t list
[@default []]
; [@key "children"]
} [@@deriving yojson { strict = false }, show, eq ];;
(** Test recursive type within an array (optional) *)
let create (value : int32) : t = {
value = value;
children = [];
}

View File

@@ -0,0 +1,33 @@
(*
* This file has been generated by the OCamlClientCodegen generator for openapi-generator.
*
* Generated by: https://openapi-generator.tech
*
* Schema Direct_recursion.t : Test direct self-reference (not in container, optional)
*)
type t = {
id: string
; [@key "id"]
next: t
option [@default None]
; [@key "next"]
} [@@deriving yojson { strict = false }, show, eq ];;
(** Test direct self-reference (not in container, optional) *)
let create (id : string) : t = {
id = id;
next = None;
}

View File

@@ -0,0 +1,31 @@
(*
* This file has been generated by the OCamlClientCodegen generator for openapi-generator.
*
* Generated by: https://openapi-generator.tech
*
* Schema Map_recursion.t : Test recursive type within a map (optional)
*)
type t = {
name: string
; [@key "name"]
child_map: (string * t) list
[@default []] [@to_yojson JsonSupport.of_map_of [%to_yojson: t]] [@of_yojson JsonSupport.to_map_of [%of_yojson: t]]
; [@key "childMap"]
} [@@deriving yojson { strict = false }, show, eq ];;
(** Test recursive type within a map (optional) *)
let create (name : string) : t = {
name = name;
child_map = [];
}

View File

@@ -0,0 +1,45 @@
(*
* This file has been generated by the OCamlClientCodegen generator for openapi-generator.
*
* Generated by: https://openapi-generator.tech
*
* Schema Multiple_recursive_fields.t : Test multiple recursive fields in the same model
*)
type t = {
name: string
; [@key "name"]
left: t
option [@default None]
; [@key "left"]
right: t
option [@default None]
; [@key "right"]
children: t list
[@default []]
; [@key "children"]
} [@@deriving yojson { strict = false }, show, eq ];;
(** Test multiple recursive fields in the same model *)
let create (name : string) : t = {
name = name;
left = None;
right = None;
children = [];
}

View File

@@ -0,0 +1,31 @@
(*
* This file has been generated by the OCamlClientCodegen generator for openapi-generator.
*
* Generated by: https://openapi-generator.tech
*
* Schema Required_array_recursion.t : Test required array of self-references
*)
type t = {
id: int32
; [@key "id"]
siblings: t list
; [@key "siblings"]
} [@@deriving yojson { strict = false }, show, eq ];;
(** Test required array of self-references *)
let create (id : int32) (siblings : t list) : t = {
id = id;
siblings = siblings;
}

View File

@@ -0,0 +1,33 @@
(*
* This file has been generated by the OCamlClientCodegen generator for openapi-generator.
*
* Generated by: https://openapi-generator.tech
*
* Schema Required_recursion.t : Test required self-reference field
*)
type t = {
value: string
; [@key "value"]
parent: t
; [@key "parent"]
} [@@deriving yojson { strict = false }, show, eq ];;
(** Test required self-reference field *)
let create (value : string) (parent : t) : t = {
value = value;
parent = parent;
}

View File

@@ -0,0 +1,6 @@
(*
* This file has been generated by the OCamlClientCodegen generator for openapi-generator.
*
* Generated by: https://openapi-generator.tech
*
*)

View File

@@ -0,0 +1,68 @@
open Ppx_deriving_yojson_runtime
let unwrap to_json json =
match to_json json with
| Result.Ok json -> json
| Result.Error s -> failwith s
let to_int json =
match json with
| `Int x -> x
| `Intlit s -> int_of_string s
| _ -> failwith "JsonSupport.to_int"
let to_bool json =
match json with
| `Bool x -> x
| _ -> failwith "JsonSupport.to_bool"
let to_float json =
match json with
| `Float x -> x
| _ -> failwith "JsonSupport.to_float"
let to_string json =
match json with
| `String s -> s
| _ -> failwith "JsonSupport.to_string"
let to_int32 json : int32 =
match json with
| `Int x -> Int32.of_int x
| `Intlit s -> Int32.of_string s
| _ -> failwith "JsonSupport.to_int32"
let to_int64 json : int64 =
match json with
| `Int x -> Int64.of_int x
| `Intlit s -> Int64.of_string s
| _ -> failwith "JsonSupport.to_int64"
let of_int x = `Int x
let of_bool b = `Bool b
let of_float x = `Float x
let of_string s = `String s
let of_int32 x = `Intlit (Int32.to_string x)
let of_int64 x = `Intlit (Int64.to_string x)
let of_list_of of_f l = `List (Stdlib.List.map of_f l)
let of_map_of of_f l = `Assoc (Stdlib.List.map (fun (k, v) -> (k, of_f v)) l)
let to_map_of of_f json =
match json with
| `Assoc l ->
Stdlib.List.fold_right
(fun (k, json) acc ->
match (of_f json, acc) with
| Stdlib.Result.Ok parsed_v, Stdlib.Result.Ok tl ->
Stdlib.Result.Ok ((k, parsed_v) :: tl)
| Stdlib.Result.Error e, _ -> Stdlib.Result.Error e
| _, Stdlib.Result.Error e -> Stdlib.Result.Error e)
l (Stdlib.Result.Ok [])
| _ -> Stdlib.Result.Error "Expected"

View File

@@ -0,0 +1,110 @@
let api_key = ""
let base_url = "http://localhost"
let default_headers = Cohttp.Header.init_with "Content-Type" "application/json"
let option_fold f default o =
match o with
| Some v -> f v
| None -> default
let build_uri operation_path = Uri.of_string (base_url ^ operation_path)
let add_string_header headers key value =
Cohttp.Header.add headers key value
let add_string_header_multi headers key values =
Cohttp.Header.add_multi headers key values
let add_header headers key to_string value =
Cohttp.Header.add headers key (to_string value)
let add_header_multi headers key to_string value =
Cohttp.Header.add_multi headers key (to_string value)
let maybe_add_header headers key to_string value =
option_fold (add_header headers key to_string) headers value
let maybe_add_header_multi headers key to_string value =
option_fold (add_header_multi headers key to_string) headers value
let write_string_body s = Cohttp_lwt.Body.of_string s
let write_json_body payload =
Cohttp_lwt.Body.of_string (Yojson.Safe.to_string payload ~std:true)
let write_as_json_body to_json payload = write_json_body (to_json payload)
let handle_response resp on_success_handler =
match Cohttp.Response.status resp with
| #Cohttp.Code.success_status -> on_success_handler ()
| s -> failwith ("Server responded with status " ^ Cohttp.Code.(reason_phrase_of_code (code_of_status s)))
let handle_unit_response resp = handle_response resp (fun () -> Lwt.return ())
let read_json_body resp body =
handle_response resp (fun () ->
(Lwt.(Cohttp_lwt.Body.to_string body >|= Yojson.Safe.from_string)))
let read_json_body_as of_json resp body =
Lwt.(read_json_body resp body >|= of_json)
let read_json_body_as_list resp body =
Lwt.(read_json_body resp body >|= Yojson.Safe.Util.to_list)
let read_json_body_as_list_of of_json resp body =
Lwt.(read_json_body_as_list resp body >|= Stdlib.List.map of_json)
let read_json_body_as_map resp body =
Lwt.(read_json_body resp body >|= Yojson.Safe.Util.to_assoc)
let read_json_body_as_map_of of_json resp body =
Lwt.(read_json_body_as_map resp body >|= Stdlib.List.map (fun (s, v) -> (s, of_json v)))
let replace_string_path_param uri param_name param_value =
let regexp = Str.regexp (Str.quote ("{" ^ param_name ^ "}")) in
let path = Str.global_replace regexp param_value (Uri.pct_decode (Uri.path uri)) in
Uri.with_path uri path
let replace_path_param uri param_name to_string param_value =
replace_string_path_param uri param_name (to_string param_value)
let maybe_replace_path_param uri param_name to_string param_value =
option_fold (replace_path_param uri param_name to_string) uri param_value
let add_query_param uri param_name to_string param_value =
Uri.add_query_param' uri (param_name, to_string param_value)
let add_query_param_list uri param_name to_string param_value =
Uri.add_query_param uri (param_name, to_string param_value)
let maybe_add_query_param uri param_name to_string param_value =
option_fold (add_query_param uri param_name to_string) uri param_value
(** Corresponds to:
- [style = form]
- [explode = true]
- type [object]
See https://swagger.io/docs/specification/v3_0/serialization/#query-parameters
*)
let add_query_param_exploded_form_object uri _param_name to_string param_value =
Stdlib.List.fold_left
(fun uri (param_name, param_value) -> add_query_param uri param_name to_string param_value)
uri
param_value
let init_form_encoded_body () = ""
let add_form_encoded_body_param params param_name to_string param_value =
let new_param_enc = Printf.sprintf {|%s=%s|} (Uri.pct_encode param_name) (Uri.pct_encode (to_string param_value)) in
if params = ""
then new_param_enc
else Printf.sprintf {|%s&%s|} params new_param_enc
let add_form_encoded_body_param_list params param_name to_string new_params =
add_form_encoded_body_param params param_name (String.concat ",") (to_string new_params)
let maybe_add_form_encoded_body_param params param_name to_string param_value =
option_fold (add_form_encoded_body_param params param_name to_string) params param_value
let finalize_form_encoded_body body = Cohttp_lwt.Body.of_string body

View File

@@ -1,4 +1,4 @@
#
# OCaml API client for petstore_client
This is a sample server Petstore server. For this sample, you can use the api key `special-key` to test the authorization filters.
This OCaml package is automatically generated by the [OpenAPI Generator](https://openapi-generator.tech) project:

View File

@@ -6,9 +6,9 @@
*)
type status = [
| `Placed [@printer fun fmt _ -> Format.pp_print_string fmt "placed"] [@name "placed"]
| `Approved [@printer fun fmt _ -> Format.pp_print_string fmt "approved"] [@name "approved"]
| `Delivered [@printer fun fmt _ -> Format.pp_print_string fmt "delivered"] [@name "delivered"]
| `Placed [@printer fun fmt _ -> Format.pp_print_string fmt "placed"] [@name "placed"]
] [@@deriving yojson, show { with_path = false }, eq];;
let status_of_yojson json = status_of_yojson (`List [json])