|
| 1 | +defmodule JsonDataFaker do |
| 2 | + @moduledoc """ |
| 3 | + Generate fake data based on json schema. |
| 4 | + """ |
| 5 | + |
| 6 | + alias ExJsonSchema.Schema |
| 7 | + |
| 8 | + @doc """ |
| 9 | + generate fake data with given schema. It could be a raw json schema or ExJsonSchema.Schema.Root type. |
| 10 | +
|
| 11 | + ## Examples |
| 12 | +
|
| 13 | + iex> schema = %{ |
| 14 | + ...> "properties" => %{ |
| 15 | + ...> "body" => %{"maxLength" => 140, "minLength" => 3, "type" => "string"}, |
| 16 | + ...> "title" => %{"maxLength" => 64, "minLength" => 3, "type" => "string"} |
| 17 | + ...> }, |
| 18 | + ...> "required" => ["title"], |
| 19 | + ...> "type" => "object" |
| 20 | + ...>} |
| 21 | + iex> %{"title" => title, "body" => body} = JsonDataFaker.generate(schema) |
| 22 | + """ |
| 23 | + def generate(%Schema.Root{} = schema) do |
| 24 | + generate_by_type(schema.schema) |
| 25 | + end |
| 26 | + |
| 27 | + def generate(schema) when is_map(schema) do |
| 28 | + generate(Schema.resolve(schema)) |
| 29 | + end |
| 30 | + |
| 31 | + # private functions |
| 32 | + defp generate_by_type(%{"type" => "boolean"}) do |
| 33 | + Enum.random([true, false]) |
| 34 | + end |
| 35 | + |
| 36 | + defp generate_by_type(%{"type" => "string"} = schema) do |
| 37 | + generate_string(schema) |
| 38 | + end |
| 39 | + |
| 40 | + defp generate_by_type(%{"type" => "integer"} = schema) do |
| 41 | + min = schema["minimum"] || 10 |
| 42 | + max = schema["maximum"] || 1000 |
| 43 | + Enum.random(min..max) |
| 44 | + end |
| 45 | + |
| 46 | + defp generate_by_type(%{"type" => "array"} = schema) do |
| 47 | + inner_schema = schema["items"] |
| 48 | + count = Enum.random(5..20) |
| 49 | + |
| 50 | + Enum.map(1..count, fn _ -> |
| 51 | + generate_by_type(inner_schema) |
| 52 | + end) |
| 53 | + end |
| 54 | + |
| 55 | + defp generate_by_type(%{"type" => "object"} = schema) do |
| 56 | + Enum.reduce(schema["properties"], %{}, fn {k, inner_schema}, acc -> |
| 57 | + Map.put(acc, k, generate_by_type(inner_schema)) |
| 58 | + end) |
| 59 | + end |
| 60 | + |
| 61 | + defp generate_string(%{"format" => "date-time"}), |
| 62 | + do: DateTime.utc_now() |> DateTime.to_iso8601() |
| 63 | + |
| 64 | + defp generate_string(%{"format" => "uuid"}), do: Faker.UUID.v4() |
| 65 | + defp generate_string(%{"format" => "email"}), do: Faker.Internet.email() |
| 66 | + defp generate_string(%{"format" => "hostname"}), do: Faker.Internet.domain_name() |
| 67 | + defp generate_string(%{"format" => "ipv4"}), do: Faker.Internet.ip_v4_address() |
| 68 | + defp generate_string(%{"format" => "ipv6"}), do: Faker.Internet.ip_v6_address() |
| 69 | + defp generate_string(%{"format" => "uri"}), do: Faker.Internet.url() |
| 70 | + |
| 71 | + defp generate_string(%{"enum" => choices}), do: Enum.random(choices) |
| 72 | + |
| 73 | + defp generate_string(%{"pattern" => regex}), |
| 74 | + do: Randex.stream(Regex.compile!(regex)) |> Enum.take(1) |> List.first() |
| 75 | + |
| 76 | + defp generate_string(schema) do |
| 77 | + min = schema["minLength"] || 0 |
| 78 | + max = schema["maxLength"] || 1024 |
| 79 | + s = Faker.Lorem.Shakespeare.hamlet() |
| 80 | + |
| 81 | + case String.length(s) do |
| 82 | + v when v > max -> String.slice(s, 0, max - 1) |
| 83 | + v when v < min -> s <> " " <> List.to_string(Faker.Lorem.characters(min - v)) |
| 84 | + _ -> s |
| 85 | + end |
| 86 | + end |
| 87 | +end |
0 commit comments