From a428916e01013235601cb2026ddad3f8ca2e1e9d Mon Sep 17 00:00:00 2001 From: Egor Chemokhonenko Date: Fri, 29 Dec 2023 01:19:50 +0200 Subject: [PATCH] Merge properties and required fields in allOf composition --- lib/generator.ml | 32 ++++++++++++++++++++++++++++++-- lib/json_schema.atd | 2 +- tests/all_of.ml | 45 +++++++++++++++++++++++---------------------- tests/dune | 1 + 4 files changed, 55 insertions(+), 25 deletions(-) diff --git a/lib/generator.ml b/lib/generator.ml index ee93f7c..f2b8c5b 100644 --- a/lib/generator.ml +++ b/lib/generator.ml @@ -15,7 +15,7 @@ let process_int_type schema = | _ -> failwith "int has unextected format" let get_ref (ref : ref_) = - match ref |> String.split_on_char '/' |> List.rev with + match ref |> String.split_on_char '/' |> List.rev with | type_name :: _ -> type_name | _ -> failwith (Printf.sprintf "%s: can't resolve ref type name" ref) @@ -36,7 +36,32 @@ let make_atd_default_value enum json_value = let nullable = Printf.sprintf "%s nullable" +let nonempty_list_opt = function + | [] -> None + | non_empty_list -> Some non_empty_list + +let rec merge_all_of schema = + match schema.all_of with + | None -> schema + | Some [] -> failwith "empty allOf is unexpected" + | Some schemas -> + let all_schemas = + schema + :: List.filter_map + (function + | Obj schema -> Some (merge_all_of schema) + | Ref _ -> None + ) + schemas + in + { + schema with + properties = all_schemas |> List.filter_map (fun schema -> schema.properties) |> List.flatten |> nonempty_list_opt; + required = all_schemas |> List.map (fun schema -> schema.required) |> List.flatten; + } + let rec process_schema_type ~ancestors (schema : schema) = + let schema = merge_all_of schema in let maybe_nullable type_ = if schema.nullable then nullable type_ else type_ in match schema.one_of with | Some schemas -> process_one_of ~ancestors schemas @@ -134,6 +159,7 @@ type int64 = int let make_atd_of_jsonschema input = let schema = Json_schema_j.schema_of_string input in let root_type_name = Option.value ~default:"root" schema.title in + Buffer.clear toplevel_definitions; base ^ "\n" ^ process_schemas [ root_type_name, Obj schema ] let make_atd_of_openapi input = @@ -142,5 +168,7 @@ let make_atd_of_openapi input = | None -> failwith "components are empty" | Some components -> match components.schemas with - | Some schemas -> base ^ "\n" ^ process_schemas schemas + | Some schemas -> + Buffer.clear toplevel_definitions; + base ^ "\n" ^ process_schemas schemas | None -> failwith "components schemas are empty" diff --git a/lib/json_schema.atd b/lib/json_schema.atd index 2ec3cb7..8001e2a 100644 --- a/lib/json_schema.atd +++ b/lib/json_schema.atd @@ -40,7 +40,7 @@ type schema = { ~schema : string nullable; (* 10.2.1 keywords for applying subschemas with logic *) - ~all_of : schema nonempty_list nullable; + ~all_of : schema or_ref nonempty_list nullable; ~any_of : schema nonempty_list nullable; ~one_of : schema or_ref nonempty_list nullable; ~not : schema nullable; diff --git a/tests/all_of.ml b/tests/all_of.ml index 5978e35..2e73d68 100644 --- a/tests/all_of.ml +++ b/tests/all_of.ml @@ -5,32 +5,32 @@ let simple_test _ = let input = {| { "dummy": { - "type": "object", - "properties": { - "field": { - "id": "string" - } - }, - "allOf": [ - { - "type": "object", - "properties": { - "field": { - "name": "string" + "type": "object", + "properties": { + "id": { + "type": "string" } - } }, - { - "type": "object", - "properties": { - "field": { - "surname": "string" + "allOf": [ + { + "type": "object", + "properties": { + "name": { + "type": "string" + } + } + }, + { + "type": "object", + "properties": { + "surname": { + "type": "string" + } + } } - } - } - ] + ] } -}|} +} |} in let output = {| @@ -38,6 +38,7 @@ let simple_test _ = ?id: string option; ?name: string option; ?surname: string option; + } |} in assert_schema input output diff --git a/tests/dune b/tests/dune index f01de6e..b1f1878 100644 --- a/tests/dune +++ b/tests/dune @@ -1,5 +1,6 @@ (tests (names + all_of base defaults enums