From a5e6a7ba8eac0ed90246d4e7b12850023f5154cd Mon Sep 17 00:00:00 2001 From: Fabio Pellegrini Date: Thu, 10 Apr 2025 13:03:52 +0200 Subject: [PATCH] refactor: leverage `OpenApiSpex.Schema.example/1` when possible --- lib/open_api/type.ex | 40 +++++++++---------- test/unit/command/open_api/open_api_test.exs | 41 ++++++++++---------- test/unit/command/open_api/type_test.exs | 18 ++++----- 3 files changed, 47 insertions(+), 52 deletions(-) diff --git a/lib/open_api/type.ex b/lib/open_api/type.ex index 59ba7ec..c9581b3 100644 --- a/lib/open_api/type.ex +++ b/lib/open_api/type.ex @@ -4,35 +4,39 @@ defmodule EctoCommand.OpenApi.Type do alias OpenApiSpex.Schema def uuid(options \\ []) do - [format: :uuid, description: "UUID", example: "defa2814-3686-4a73-9f64-a17cdfd7f1a1"] ++ options + [format: :uuid, description: "UUID"] ++ options end - def enum(values, options \\ []) do - [example: List.first(values)] ++ options + def email(options \\ []) do + [format: :email, description: "Email", example: "user@domain.com"] ++ options end - def datetime(options \\ []) do - [format: :"date-time", example: "2023-04-03T10:21:00Z"] ++ options + def password(options \\ []) do + [format: :password, description: "Password", example: "Abcd123!!"] ++ options end - def date(options \\ []) do - [format: :date, example: "2023-04-03"] ++ options + def phone(options \\ []) do + [format: :telephone, description: "Telephone", example: "(425) 123-4567"] ++ options end - def boolean(options \\ []) do - [example: true] ++ options + @deprecated "Automatically inferred from command schema when type is one of `:utc_datetime`, `:utc_datetime_usec`, `:naive_datetime`, `:naive_datetime_usec`" + def datetime(options \\ []) do + [format: :"date-time"] ++ options end - def email(options \\ []) do - [format: :email, description: "Email", example: "user@domain.com"] ++ options + @deprecated "Automatically inferred from command schema when type is `:date`" + def date(options \\ []) do + [format: :date] ++ options end - def password(options \\ []) do - [format: :password, description: "Password", example: "Abcd123!!"] ++ options + @deprecated "Automatically inferred from command schema when type is `Ecto.Enum` or `inclusion` opt is used" + def enum(values, options \\ []) do + [example: List.first(values)] ++ options end - def phone(options \\ []) do - [format: :telephone, description: "Telephone", example: "(425) 123-4567"] ++ options + @deprecated "Automatically inferred from command schema when type is `:boolean`" + def boolean(options \\ []) do + [example: true] ++ options end def example_for(%Schema{type: :array, items: [%{enum: values}]} = _schema) when is_list(values), @@ -40,14 +44,9 @@ defmodule EctoCommand.OpenApi.Type do def example_for(%Schema{default: default}) when not is_nil(default), do: default def example_for(%Schema{enum: values}) when is_list(values), do: List.first(values) - def example_for(%Schema{type: :string, format: :date}), do: Keyword.get(date(), :example) - def example_for(%Schema{type: :string, format: :"date-time"}), do: Keyword.get(datetime(), :example) - def example_for(%Schema{type: :string, format: :uuid}), do: Keyword.get(uuid(), :example) def example_for(%Schema{type: :string, format: :email}), do: Keyword.get(email(), :example) def example_for(%Schema{type: :string, format: :telephone}), do: Keyword.get(phone(), :example) def example_for(%Schema{type: :string, format: :password}), do: Keyword.get(password(), :example) - def example_for(%Schema{type: :string}), do: "string" - def example_for(%Schema{type: :boolean}), do: true def example_for(%Schema{type: :integer} = schema), do: trunc(number_example(schema)) def example_for(%Schema{type: :number} = schema), do: number_example(schema) def example_for(%Schema{type: :array, items: [%{example: nil}]}), do: [] @@ -59,6 +58,7 @@ defmodule EctoCommand.OpenApi.Type do end) end + def example_for(%Schema{} = schema), do: Schema.example(schema) def example_for(_schema), do: nil defp number_example(%Schema{not: %{enum: [n]}}), do: (n + 1) / 1 diff --git a/test/unit/command/open_api/open_api_test.exs b/test/unit/command/open_api/open_api_test.exs index 7709773..a474987 100644 --- a/test/unit/command/open_api/open_api_test.exs +++ b/test/unit/command/open_api/open_api_test.exs @@ -20,8 +20,7 @@ defmodule Unit.EctoCommand.OpenApi.OpenApiTest do param :mime_type, :string, required: true, - inclusion: ["image/jpeg", "image/png"], - doc: Type.enum(["image/jpeg", "image/png"]) + inclusion: ["image/jpeg", "image/png"] param :an_enum, Ecto.Enum, values: [:a, :b] param :an_enum_stored_as_int, Ecto.Enum, values: [a: 1, b: 2] @@ -32,9 +31,9 @@ defmodule Unit.EctoCommand.OpenApi.OpenApiTest do param :an_integer_c, :integer, number: [greater_than: 18, less_than_or_equal_to: 100], doc: [example: 30] param :a_float, :float, number: [greater_than: 10, less_than_or_equal_to: 100] param :type_id, :string - param :accepts, :boolean, default: false, doc: Type.boolean() - param :folder_id, :string, change: &String.valid?/1 - param :uploaded_at, :utc_datetime, doc: Type.datetime() + param :accepts, :boolean, default: false + param :folder_id, :string, change: &String.valid?/1, doc: [example: "a_folder_id"] + param :uploaded_at, :utc_datetime param :a_date, :date param :a_list_of_strings_a, {:array, :string}, default: [] param :a_list_of_strings_b, {:array, :string}, doc: [description: "A list of strings A"] @@ -50,7 +49,7 @@ defmodule Unit.EctoCommand.OpenApi.OpenApiTest do test "all properties docs are generated correctly" do assert %{ - accepts: %OpenApiSpex.Schema{example: true, type: :boolean, default: false}, + accepts: %OpenApiSpex.Schema{example: false, type: :boolean, default: false}, an_integer_a: %OpenApiSpex.Schema{ exclusiveMaximum: false, exclusiveMinimum: false, @@ -93,10 +92,10 @@ defmodule Unit.EctoCommand.OpenApi.OpenApiTest do type: :string }, extension: %OpenApiSpex.Schema{example: "png", maxLength: 3, type: :string}, - folder_id: %OpenApiSpex.Schema{type: :string, example: "string"}, + folder_id: %OpenApiSpex.Schema{type: :string, example: "a_folder_id"}, id: %OpenApiSpex.Schema{ description: "UUID", - example: "defa2814-3686-4a73-9f64-a17cdfd7f1a1", + example: "02ef9c5f-29e6-48fc-9ec3-7ed57ed351f6", format: :uuid, type: :string }, @@ -110,13 +109,13 @@ defmodule Unit.EctoCommand.OpenApi.OpenApiTest do minLength: 9, type: :string }, - type_id: %OpenApiSpex.Schema{type: :string, example: "string"}, + type_id: %OpenApiSpex.Schema{type: :string, example: ""}, uploaded_at: %OpenApiSpex.Schema{ - example: "2023-04-03T10:21:00Z", + example: "2020-04-20T16:20:00Z", format: :"date-time", type: :string }, - a_date: %OpenApiSpex.Schema{type: :string, format: :date, example: "2023-04-03"}, + a_date: %OpenApiSpex.Schema{type: :string, format: :date, example: "2020-04-20"}, a_list_of_enums: %OpenApiSpex.Schema{ type: :array, items: [%OpenApiSpex.Schema{enum: ["a", "b", "c"], type: :string, example: "a"}], @@ -125,15 +124,15 @@ defmodule Unit.EctoCommand.OpenApi.OpenApiTest do }, a_list_of_strings_a: %OpenApiSpex.Schema{ type: :array, - items: [%OpenApiSpex.Schema{type: :string, example: "string"}], + items: [%OpenApiSpex.Schema{type: :string, example: ""}], default: [], example: [] }, a_list_of_strings_b: %OpenApiSpex.Schema{ type: :array, - items: [%OpenApiSpex.Schema{type: :string, example: "string"}], + items: [%OpenApiSpex.Schema{type: :string, example: ""}], description: "A list of strings A", - example: ["string"] + example: [""] }, a_list_of_strings_c: %OpenApiSpex.Schema{ type: :array, @@ -162,15 +161,15 @@ defmodule Unit.EctoCommand.OpenApi.OpenApiTest do test "example is generated accordingly to properties" do assert %{ - a_date: "2023-04-03", + a_date: "2020-04-20", a_float: 55.5, a_list_of_enums: ["a", "b"], a_list_of_strings_a: [], - a_list_of_strings_b: ["string"], + a_list_of_strings_b: [""], a_list_of_strings_c: ["a", "b"], a_map: %{}, a_map_with_int_values: %{a: 1}, - accepts: true, + accepts: false, an_enum: "a", an_enum_stored_as_int: "a", an_integer_a: 20, @@ -179,14 +178,14 @@ defmodule Unit.EctoCommand.OpenApi.OpenApiTest do count: 58, email: "user@domain.com", extension: "png", - folder_id: "string", - id: "defa2814-3686-4a73-9f64-a17cdfd7f1a1", + folder_id: "a_folder_id", + id: "02ef9c5f-29e6-48fc-9ec3-7ed57ed351f6", numeric_id: 10, mime_type: "image/jpeg", name: "Mario", phone: "(425) 123-4567", - type_id: "string", - uploaded_at: "2023-04-03T10:21:00Z" + type_id: "", + uploaded_at: "2020-04-20T16:20:00Z" } == Sample.schema().example end diff --git a/test/unit/command/open_api/type_test.exs b/test/unit/command/open_api/type_test.exs index 415d9c1..db4e061 100644 --- a/test/unit/command/open_api/type_test.exs +++ b/test/unit/command/open_api/type_test.exs @@ -113,20 +113,16 @@ defmodule Unit.EctoCommand.OpenApi.TypeTest do assert 9.5 == Type.example_for(schema) end - test "for booleans" do - assert true == Type.example_for(%Schema{type: :boolean}) - end - test "for dates" do - assert "2023-04-03" == Type.example_for(%Schema{type: :string, format: :date}) + assert "2020-04-20" == Type.example_for(%Schema{type: :string, format: :date}) end test "for datetimes" do - assert "2023-04-03T10:21:00Z" == Type.example_for(%Schema{type: :string, format: :"date-time"}) + assert "2020-04-20T16:20:00Z" == Type.example_for(%Schema{type: :string, format: :"date-time"}) end test "for UUIDs" do - assert "defa2814-3686-4a73-9f64-a17cdfd7f1a1" == Type.example_for(%Schema{type: :string, format: :uuid}) + assert "02ef9c5f-29e6-48fc-9ec3-7ed57ed351f6" == Type.example_for(%Schema{type: :string, format: :uuid}) end test "for emails" do @@ -163,7 +159,7 @@ defmodule Unit.EctoCommand.OpenApi.TypeTest do id: %OpenApiSpex.Schema{type: :string}, name: %OpenApiSpex.Schema{type: :string}, type: %OpenApiSpex.Schema{enum: ["a", "b"], type: :string, example: "a"}, - tags: %OpenApiSpex.Schema{type: :array, items: [%OpenApiSpex.Schema{type: :string, default: []}]}, + tags: %OpenApiSpex.Schema{type: :array, items: [%OpenApiSpex.Schema{type: :string}], default: []}, non_required_id: %OpenApiSpex.Schema{type: :string} } } @@ -171,11 +167,11 @@ defmodule Unit.EctoCommand.OpenApi.TypeTest do assert %{ active: true, count: 11, - id: "string", - name: "string", + id: "", + name: "", type: "a", tags: [], - non_required_id: "string" + non_required_id: "" } == Type.example_for(schema) end end