diff --git a/.github/workflows/samples-ocaml.yaml b/.github/workflows/samples-ocaml.yaml index f74a736469b2..b60f3f6001f0 100644 --- a/.github/workflows/samples-ocaml.yaml +++ b/.github/workflows/samples-ocaml.yaml @@ -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 }} diff --git a/.gitignore b/.gitignore index a11c07212928..758dbfdbde47 100644 --- a/.gitignore +++ b/.gitignore @@ -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 diff --git a/bin/configs/ocaml-enum-in-composed-schema.yaml b/bin/configs/ocaml-enum-in-composed-schema.yaml new file mode 100644 index 000000000000..bff9e9349cdd --- /dev/null +++ b/bin/configs/ocaml-enum-in-composed-schema.yaml @@ -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 diff --git a/bin/configs/ocaml-recursion-test.yaml b/bin/configs/ocaml-recursion-test.yaml new file mode 100644 index 000000000000..f3471d83797c --- /dev/null +++ b/bin/configs/ocaml-recursion-test.yaml @@ -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 diff --git a/docs/generators/ocaml.md b/docs/generators/ocaml.md index 9f98b06ac073..113af41f91d4 100644 --- a/docs/generators/ocaml.md +++ b/docs/generators/ocaml.md @@ -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. diff --git a/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/OCamlClientCodegen.java b/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/OCamlClientCodegen.java index 1f7d6548e508..c6c8ef3ae866 100644 --- a/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/OCamlClientCodegen.java +++ b/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/OCamlClientCodegen.java @@ -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> enumNames = new HashMap<>(); - private Map enumHash = new HashMap<>(); - private Map enumUniqNames; + private Map, List> enumNames = new HashMap<>(); + private Map, Schema> enumHash = new HashMap<>(); + private Map, String> enumUniqNames; @Override public CodegenType getTag() { @@ -74,7 +76,7 @@ public String getName() { @Override public String getHelp() { - return "Generates an OCaml client library (beta)."; + return "Generates an OCaml client library."; } public OCamlClientCodegen() { @@ -233,6 +235,105 @@ public Map postProcessAllModels(Map supero } + /** + * 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 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> allPropertyLists = Arrays.asList( + cm.allVars, cm.vars, cm.requiredVars, cm.optionalVars, + cm.readOnlyVars, cm.readWriteVars, cm.parentVars + ); + + for (List 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 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 properties) { for (CodegenProperty property : properties) { if (property.get_enum() != null && property.get_enum().size() == 1) { @@ -276,8 +377,10 @@ protected void updateDataTypeWithEnumForArray(CodegenProperty property) { } @SuppressWarnings("unchecked") - private String hashEnum(Schema schema) { - return ((List) schema.getEnum()).stream().map(String::valueOf).collect(Collectors.joining(",")); + private Set hashEnum(Schema schema) { + return ((List) schema.getEnum()).stream() + .map(String::valueOf) + .collect(Collectors.toCollection(TreeSet::new)); } private boolean isEnumSchema(Schema schema) { @@ -290,7 +393,7 @@ private void collectEnumSchemas(String parentName, String sName, Schema schema) } else if (ModelUtils.isMapSchema(schema) && schema.getAdditionalProperties() instanceof Schema) { collectEnumSchemas(parentName, sName, (Schema) schema.getAdditionalProperties()); } else if (isEnumSchema(schema)) { - String h = hashEnum(schema); + Set 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 @@ private void collectEnumSchemas(String parentName, String sName, Schema schema) } } } + // 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 @@ private void collectEnumSchemas(String parentName, Map schemas) 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 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 @@ private String sanitizeOCamlTypeName(String name) { } private void computeEnumUniqNames() { - Map definitiveNames = new HashMap<>(); - for (String h : enumNames.keySet()) { + Map> definitiveNames = new HashMap<>(); + for (Set h : enumNames.keySet()) { boolean hasDefName = false; List nameCandidates = enumNames.get(h); for (String name : nameCandidates) { @@ -600,13 +746,13 @@ public String getTypeDeclaration(Schema p) { String prefix = inner.getEnum() != null ? "Enums." : ""; return "(string * " + prefix + getTypeDeclaration(inner) + ") list"; } else if (p.getEnum() != null) { - String h = hashEnum(p); + Set h = hashEnum(p); return enumUniqNames.get(h); } Schema referencedSchema = ModelUtils.getReferencedSchema(openAPI, p); if (referencedSchema != null && referencedSchema.getEnum() != null) { - String h = hashEnum(referencedSchema); + Set h = hashEnum(referencedSchema); return "Enums." + enumUniqNames.get(h); } @@ -739,8 +885,8 @@ public OperationsMap postProcessOperationsWithModels(OperationsMap objs, List e : enumUniqNames.entrySet()) { - allModels.add(buildEnumModelWrapper(e.getValue(), e.getKey())); + for (Map.Entry, String> e : enumUniqNames.entrySet()) { + allModels.add(buildEnumModelWrapper(e.getValue(), String.join(",", e.getKey()))); } enumUniqNames.clear(); @@ -770,7 +916,7 @@ public String escapeUnsafeCharacters(String input) { @Override public String toEnumName(CodegenProperty property) { - String hash = String.join(",", property.get_enum()); + Set hash = new TreeSet<>(property.get_enum()); if (enumUniqNames.containsKey(hash)) { return enumUniqNames.get(hash); diff --git a/modules/openapi-generator/src/test/resources/3_0/ocaml/direct-recursion.yaml b/modules/openapi-generator/src/test/resources/3_0/ocaml/direct-recursion.yaml new file mode 100644 index 000000000000..34f75d411e22 --- /dev/null +++ b/modules/openapi-generator/src/test/resources/3_0/ocaml/direct-recursion.yaml @@ -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 diff --git a/modules/openapi-generator/src/test/resources/3_0/ocaml/enum-in-composed-schema.yaml b/modules/openapi-generator/src/test/resources/3_0/ocaml/enum-in-composed-schema.yaml new file mode 100644 index 000000000000..bbc95db7a2c0 --- /dev/null +++ b/modules/openapi-generator/src/test/resources/3_0/ocaml/enum-in-composed-schema.yaml @@ -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: + - "" diff --git a/samples/client/petstore/ocaml-enum-in-composed-schema/.ocamlformat b/samples/client/petstore/ocaml-enum-in-composed-schema/.ocamlformat new file mode 100644 index 000000000000..402c7252e5f9 --- /dev/null +++ b/samples/client/petstore/ocaml-enum-in-composed-schema/.ocamlformat @@ -0,0 +1,2 @@ +version=0.27.0 +ocaml-version=4.14.0 diff --git a/samples/client/petstore/ocaml-enum-in-composed-schema/.openapi-generator-ignore b/samples/client/petstore/ocaml-enum-in-composed-schema/.openapi-generator-ignore new file mode 100644 index 000000000000..7484ee590a38 --- /dev/null +++ b/samples/client/petstore/ocaml-enum-in-composed-schema/.openapi-generator-ignore @@ -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 diff --git a/samples/client/petstore/ocaml-enum-in-composed-schema/.openapi-generator/FILES b/samples/client/petstore/ocaml-enum-in-composed-schema/.openapi-generator/FILES new file mode 100644 index 000000000000..0d0070c8c777 --- /dev/null +++ b/samples/client/petstore/ocaml-enum-in-composed-schema/.openapi-generator/FILES @@ -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 diff --git a/samples/client/petstore/ocaml-enum-in-composed-schema/.openapi-generator/VERSION b/samples/client/petstore/ocaml-enum-in-composed-schema/.openapi-generator/VERSION new file mode 100644 index 000000000000..0610c66bc14f --- /dev/null +++ b/samples/client/petstore/ocaml-enum-in-composed-schema/.openapi-generator/VERSION @@ -0,0 +1 @@ +7.21.0-SNAPSHOT diff --git a/samples/client/petstore/ocaml-enum-in-composed-schema/README.md b/samples/client/petstore/ocaml-enum-in-composed-schema/README.md new file mode 100644 index 000000000000..b042c51c5461 --- /dev/null +++ b/samples/client/petstore/ocaml-enum-in-composed-schema/README.md @@ -0,0 +1,33 @@ +# +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 + diff --git a/samples/client/petstore/ocaml-enum-in-composed-schema/dune b/samples/client/petstore/ocaml-enum-in-composed-schema/dune new file mode 100644 index 000000000000..ed1c4d90e3df --- /dev/null +++ b/samples/client/petstore/ocaml-enum-in-composed-schema/dune @@ -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) +) \ No newline at end of file diff --git a/samples/client/petstore/ocaml-enum-in-composed-schema/dune-project b/samples/client/petstore/ocaml-enum-in-composed-schema/dune-project new file mode 100644 index 000000000000..60a6d76962b7 --- /dev/null +++ b/samples/client/petstore/ocaml-enum-in-composed-schema/dune-project @@ -0,0 +1,2 @@ +(lang dune 2.0) +(name petstore_client) diff --git a/samples/client/petstore/ocaml-enum-in-composed-schema/petstore_client.opam b/samples/client/petstore/ocaml-enum-in-composed-schema/petstore_client.opam new file mode 100644 index 000000000000..d7c41adb6a11 --- /dev/null +++ b/samples/client/petstore/ocaml-enum-in-composed-schema/petstore_client.opam @@ -0,0 +1,25 @@ +opam-version: "2.0" +name: "petstore_client" +version: "1.0.0" +synopsis: "" +description: """ +Longer description +""" +maintainer: "Name " +authors: "Name " +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] diff --git a/samples/client/petstore/ocaml-enum-in-composed-schema/src/apis/default_api.ml b/samples/client/petstore/ocaml-enum-in-composed-schema/src/apis/default_api.ml new file mode 100644 index 000000000000..19bff83c55d2 --- /dev/null +++ b/samples/client/petstore/ocaml-enum-in-composed-schema/src/apis/default_api.ml @@ -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 + diff --git a/samples/client/petstore/ocaml-enum-in-composed-schema/src/apis/default_api.mli b/samples/client/petstore/ocaml-enum-in-composed-schema/src/apis/default_api.mli new file mode 100644 index 000000000000..57935f880d20 --- /dev/null +++ b/samples/client/petstore/ocaml-enum-in-composed-schema/src/apis/default_api.mli @@ -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 diff --git a/samples/client/petstore/ocaml-enum-in-composed-schema/src/models/test_model.ml b/samples/client/petstore/ocaml-enum-in-composed-schema/src/models/test_model.ml new file mode 100644 index 000000000000..3e62e8942e27 --- /dev/null +++ b/samples/client/petstore/ocaml-enum-in-composed-schema/src/models/test_model.ml @@ -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; + } + + diff --git a/samples/client/petstore/ocaml-enum-in-composed-schema/src/models/test_model_config.ml b/samples/client/petstore/ocaml-enum-in-composed-schema/src/models/test_model_config.ml new file mode 100644 index 000000000000..69d1df674bf2 --- /dev/null +++ b/samples/client/petstore/ocaml-enum-in-composed-schema/src/models/test_model_config.ml @@ -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; + } + + diff --git a/samples/client/petstore/ocaml-enum-in-composed-schema/src/models/test_model_config_all_of_options.ml b/samples/client/petstore/ocaml-enum-in-composed-schema/src/models/test_model_config_all_of_options.ml new file mode 100644 index 000000000000..d80716d711c6 --- /dev/null +++ b/samples/client/petstore/ocaml-enum-in-composed-schema/src/models/test_model_config_all_of_options.ml @@ -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; + } + + diff --git a/samples/client/petstore/ocaml-enum-in-composed-schema/src/models/test_model_optional_url.ml b/samples/client/petstore/ocaml-enum-in-composed-schema/src/models/test_model_optional_url.ml new file mode 100644 index 000000000000..1ab96104dc3d --- /dev/null +++ b/samples/client/petstore/ocaml-enum-in-composed-schema/src/models/test_model_optional_url.ml @@ -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") + + + diff --git a/samples/client/petstore/ocaml-enum-in-composed-schema/src/support/enums.ml b/samples/client/petstore/ocaml-enum-in-composed-schema/src/support/enums.ml new file mode 100644 index 000000000000..047200e1a97a --- /dev/null +++ b/samples/client/petstore/ocaml-enum-in-composed-schema/src/support/enums.ml @@ -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 diff --git a/samples/client/petstore/ocaml-enum-in-composed-schema/src/support/jsonSupport.ml b/samples/client/petstore/ocaml-enum-in-composed-schema/src/support/jsonSupport.ml new file mode 100644 index 000000000000..0af0c25e759a --- /dev/null +++ b/samples/client/petstore/ocaml-enum-in-composed-schema/src/support/jsonSupport.ml @@ -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" diff --git a/samples/client/petstore/ocaml-enum-in-composed-schema/src/support/request.ml b/samples/client/petstore/ocaml-enum-in-composed-schema/src/support/request.ml new file mode 100644 index 000000000000..e83aaa36c6f3 --- /dev/null +++ b/samples/client/petstore/ocaml-enum-in-composed-schema/src/support/request.ml @@ -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 diff --git a/samples/client/petstore/ocaml-fake-petstore/src/apis/pet_api.ml b/samples/client/petstore/ocaml-fake-petstore/src/apis/pet_api.ml index 95637700510d..2727487ba50b 100644 --- a/samples/client/petstore/ocaml-fake-petstore/src/apis/pet_api.ml +++ b/samples/client/petstore/ocaml-fake-petstore/src/apis/pet_api.ml @@ -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 diff --git a/samples/client/petstore/ocaml-fake-petstore/src/apis/pet_api.mli b/samples/client/petstore/ocaml-fake-petstore/src/apis/pet_api.mli index f0aa4208b358..b3f4af03ce52 100644 --- a/samples/client/petstore/ocaml-fake-petstore/src/apis/pet_api.mli +++ b/samples/client/petstore/ocaml-fake-petstore/src/apis/pet_api.mli @@ -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 diff --git a/samples/client/petstore/ocaml-fake-petstore/src/models/enum_test.ml b/samples/client/petstore/ocaml-fake-petstore/src/models/enum_test.ml index 723762807e17..361e39ae106b 100644 --- a/samples/client/petstore/ocaml-fake-petstore/src/models/enum_test.ml +++ b/samples/client/petstore/ocaml-fake-petstore/src/models/enum_test.ml @@ -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] diff --git a/samples/client/petstore/ocaml-fake-petstore/src/models/order.ml b/samples/client/petstore/ocaml-fake-petstore/src/models/order.ml index 3d40c904734f..587ad0a6fc28 100644 --- a/samples/client/petstore/ocaml-fake-petstore/src/models/order.ml +++ b/samples/client/petstore/ocaml-fake-petstore/src/models/order.ml @@ -33,7 +33,7 @@ ; [@key "shipDate"] - status: Enums.order_status + status: Enums.status option [@default None diff --git a/samples/client/petstore/ocaml-fake-petstore/src/models/pet.ml b/samples/client/petstore/ocaml-fake-petstore/src/models/pet.ml index f14dec068f53..08c1da1c2ee8 100644 --- a/samples/client/petstore/ocaml-fake-petstore/src/models/pet.ml +++ b/samples/client/petstore/ocaml-fake-petstore/src/models/pet.ml @@ -35,7 +35,7 @@ [@default []] ; [@key "tags"] - status: Enums.status + status: Enums.pet_status option [@default None diff --git a/samples/client/petstore/ocaml-fake-petstore/src/support/enums.ml b/samples/client/petstore/ocaml-fake-petstore/src/support/enums.ml index 38b8eb297499..a663b9b5e23d 100644 --- a/samples/client/petstore/ocaml-fake-petstore/src/support/enums.ml +++ b/samples/client/petstore/ocaml-fake-petstore/src/support/enums.ml @@ -5,90 +5,88 @@ * *) -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"] +type enum_form_string_array = [ +| `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 outerenuminteger_of_yojson json = outerenuminteger_of_yojson (`List [json]) -let outerenuminteger_to_yojson e = - match outerenuminteger_to_yojson e with +let enum_form_string_array_of_yojson json = enum_form_string_array_of_yojson (`List [json]) +let enum_form_string_array_to_yojson e = + match enum_form_string_array_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"] +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 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 +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 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"] +type singlereftype = [ +| `Admin [@printer fun fmt _ -> Format.pp_print_string fmt "admin"] [@name "admin"] +| `User [@printer fun fmt _ -> Format.pp_print_string fmt "user"] [@name "user"] ] [@@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 +let singlereftype_of_yojson json = singlereftype_of_yojson (`List [json]) +let singlereftype_to_yojson e = + match singlereftype_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 "$"] +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 just_symbol_of_yojson json = just_symbol_of_yojson (`List [json]) -let just_symbol_to_yojson e = - match just_symbol_to_yojson e with +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 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)"] +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 enumclass_of_yojson json = enumclass_of_yojson (`List [json]) -let enumclass_to_yojson e = - match enumclass_to_yojson e with +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 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"] +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 status_of_yojson json = status_of_yojson (`List [json]) -let status_to_yojson e = - match status_to_yojson e with +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 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"] +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 order_status_of_yojson json = order_status_of_yojson (`List [json]) -let order_status_to_yojson e = - match order_status_to_yojson e with +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 = [ -| `_1 [@printer fun fmt _ -> Format.pp_print_string fmt "1"] [@name "1"] | `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]) @@ -97,14 +95,14 @@ let enum_query_integer_to_yojson e = | `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 "$"] +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 enum_form_string_array_of_yojson json = enum_form_string_array_of_yojson (`List [json]) -let enum_form_string_array_to_yojson e = - match enum_form_string_array_to_yojson e with +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 @@ -118,40 +116,31 @@ let parentwithnullable_type_to_yojson e = | `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"] +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 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 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 singlereftype = [ -| `Admin [@printer fun fmt _ -> Format.pp_print_string fmt "admin"] [@name "admin"] -| `User [@printer fun fmt _ -> Format.pp_print_string fmt "user"] [@name "user"] +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 singlereftype_of_yojson json = singlereftype_of_yojson (`List [json]) -let singlereftype_to_yojson e = - match singlereftype_to_yojson e with +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 diff --git a/samples/client/petstore/ocaml-recursion-test/.ocamlformat b/samples/client/petstore/ocaml-recursion-test/.ocamlformat new file mode 100644 index 000000000000..402c7252e5f9 --- /dev/null +++ b/samples/client/petstore/ocaml-recursion-test/.ocamlformat @@ -0,0 +1,2 @@ +version=0.27.0 +ocaml-version=4.14.0 diff --git a/samples/client/petstore/ocaml-recursion-test/.openapi-generator-ignore b/samples/client/petstore/ocaml-recursion-test/.openapi-generator-ignore new file mode 100644 index 000000000000..7484ee590a38 --- /dev/null +++ b/samples/client/petstore/ocaml-recursion-test/.openapi-generator-ignore @@ -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 diff --git a/samples/client/petstore/ocaml-recursion-test/.openapi-generator/FILES b/samples/client/petstore/ocaml-recursion-test/.openapi-generator/FILES new file mode 100644 index 000000000000..20a67c8021f9 --- /dev/null +++ b/samples/client/petstore/ocaml-recursion-test/.openapi-generator/FILES @@ -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 diff --git a/samples/client/petstore/ocaml-recursion-test/.openapi-generator/VERSION b/samples/client/petstore/ocaml-recursion-test/.openapi-generator/VERSION new file mode 100644 index 000000000000..0610c66bc14f --- /dev/null +++ b/samples/client/petstore/ocaml-recursion-test/.openapi-generator/VERSION @@ -0,0 +1 @@ +7.21.0-SNAPSHOT diff --git a/samples/client/petstore/ocaml-recursion-test/README.md b/samples/client/petstore/ocaml-recursion-test/README.md new file mode 100644 index 000000000000..755a280628ac --- /dev/null +++ b/samples/client/petstore/ocaml-recursion-test/README.md @@ -0,0 +1,44 @@ +# +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 + diff --git a/samples/client/petstore/ocaml-recursion-test/dune b/samples/client/petstore/ocaml-recursion-test/dune new file mode 100644 index 000000000000..2e999392dab4 --- /dev/null +++ b/samples/client/petstore/ocaml-recursion-test/dune @@ -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) +) \ No newline at end of file diff --git a/samples/client/petstore/ocaml-recursion-test/dune-project b/samples/client/petstore/ocaml-recursion-test/dune-project new file mode 100644 index 000000000000..111c5d030211 --- /dev/null +++ b/samples/client/petstore/ocaml-recursion-test/dune-project @@ -0,0 +1,2 @@ +(lang dune 2.0) +(name recursion_test) diff --git a/samples/client/petstore/ocaml-recursion-test/recursion_test.opam b/samples/client/petstore/ocaml-recursion-test/recursion_test.opam new file mode 100644 index 000000000000..afd23a8a8102 --- /dev/null +++ b/samples/client/petstore/ocaml-recursion-test/recursion_test.opam @@ -0,0 +1,25 @@ +opam-version: "2.0" +name: "recursion_test" +version: "1.0.0" +synopsis: "" +description: """ +Longer description +""" +maintainer: "Name " +authors: "Name " +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] diff --git a/samples/client/petstore/ocaml-recursion-test/src/models/array_recursion.ml b/samples/client/petstore/ocaml-recursion-test/src/models/array_recursion.ml new file mode 100644 index 000000000000..51bc3f996dc3 --- /dev/null +++ b/samples/client/petstore/ocaml-recursion-test/src/models/array_recursion.ml @@ -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 = []; + } + + diff --git a/samples/client/petstore/ocaml-recursion-test/src/models/direct_recursion.ml b/samples/client/petstore/ocaml-recursion-test/src/models/direct_recursion.ml new file mode 100644 index 000000000000..c8dff396ce49 --- /dev/null +++ b/samples/client/petstore/ocaml-recursion-test/src/models/direct_recursion.ml @@ -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; + } + + diff --git a/samples/client/petstore/ocaml-recursion-test/src/models/map_recursion.ml b/samples/client/petstore/ocaml-recursion-test/src/models/map_recursion.ml new file mode 100644 index 000000000000..30755ad3f906 --- /dev/null +++ b/samples/client/petstore/ocaml-recursion-test/src/models/map_recursion.ml @@ -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 = []; + } + + diff --git a/samples/client/petstore/ocaml-recursion-test/src/models/multiple_recursive_fields.ml b/samples/client/petstore/ocaml-recursion-test/src/models/multiple_recursive_fields.ml new file mode 100644 index 000000000000..4c749234a77e --- /dev/null +++ b/samples/client/petstore/ocaml-recursion-test/src/models/multiple_recursive_fields.ml @@ -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 = []; + } + + diff --git a/samples/client/petstore/ocaml-recursion-test/src/models/required_array_recursion.ml b/samples/client/petstore/ocaml-recursion-test/src/models/required_array_recursion.ml new file mode 100644 index 000000000000..28c6b77156bf --- /dev/null +++ b/samples/client/petstore/ocaml-recursion-test/src/models/required_array_recursion.ml @@ -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; + } + + diff --git a/samples/client/petstore/ocaml-recursion-test/src/models/required_recursion.ml b/samples/client/petstore/ocaml-recursion-test/src/models/required_recursion.ml new file mode 100644 index 000000000000..932da060b1b5 --- /dev/null +++ b/samples/client/petstore/ocaml-recursion-test/src/models/required_recursion.ml @@ -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; + } + + diff --git a/samples/client/petstore/ocaml-recursion-test/src/support/enums.ml b/samples/client/petstore/ocaml-recursion-test/src/support/enums.ml new file mode 100644 index 000000000000..b30c42f110c6 --- /dev/null +++ b/samples/client/petstore/ocaml-recursion-test/src/support/enums.ml @@ -0,0 +1,6 @@ +(* + * This file has been generated by the OCamlClientCodegen generator for openapi-generator. + * + * Generated by: https://openapi-generator.tech + * + *) diff --git a/samples/client/petstore/ocaml-recursion-test/src/support/jsonSupport.ml b/samples/client/petstore/ocaml-recursion-test/src/support/jsonSupport.ml new file mode 100644 index 000000000000..0af0c25e759a --- /dev/null +++ b/samples/client/petstore/ocaml-recursion-test/src/support/jsonSupport.ml @@ -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" diff --git a/samples/client/petstore/ocaml-recursion-test/src/support/request.ml b/samples/client/petstore/ocaml-recursion-test/src/support/request.ml new file mode 100644 index 000000000000..e83aaa36c6f3 --- /dev/null +++ b/samples/client/petstore/ocaml-recursion-test/src/support/request.ml @@ -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 diff --git a/samples/client/petstore/ocaml/src/support/enums.ml b/samples/client/petstore/ocaml/src/support/enums.ml index 4e4c2d61edc0..aa95d33e3a0b 100644 --- a/samples/client/petstore/ocaml/src/support/enums.ml +++ b/samples/client/petstore/ocaml/src/support/enums.ml @@ -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])