Packages

Build API routes based on OpenAPI v3 spec.

Current section

Files

Jump to
json_data_faker lib json_data_faker.ex
Raw

lib/json_data_faker.ex

defmodule JsonDataFaker do
@moduledoc """
Generate fake data based on json schema.
"""
import StreamData
require Logger
alias ExJsonSchema.Schema
@doc """
generate fake data with given schema. It could be a raw json schema or ExJsonSchema.Schema.Root type.
## Examples
iex> schema = %{
...> "properties" => %{
...> "body" => %{"maxLength" => 140, "minLength" => 3, "type" => "string"},
...> "title" => %{"maxLength" => 64, "minLength" => 3, "type" => "string"}
...> },
...> "required" => ["title"],
...> "type" => "object"
...>}
iex> %{"title" => _title, "body" => _body} = JsonDataFaker.generate(schema) |> Enum.take(1) |> List.first()
"""
def generate(%Schema.Root{} = schema) do
generate_by_type(schema.schema)
end
def generate(schema) when is_map(schema) do
generate(Schema.resolve(schema))
rescue
e ->
Logger.error("Failed to generate data. #{inspect(e)}")
nil
end
def generate(_schema), do: nil
# private functions
defp generate_by_type(%{"type" => "boolean"}) do
boolean()
end
defp generate_by_type(%{"type" => "string"} = schema) do
generate_string(schema)
end
defp generate_by_type(%{"type" => "integer"} = schema) do
min = schema["minimum"] || 10
max = schema["maximum"] || 1000
integer(min..max)
end
defp generate_by_type(%{"type" => "array"} = schema) do
inner_schema = schema["items"]
count = Enum.random(2..5)
StreamData.list_of(generate_by_type(inner_schema), length: count)
end
defp generate_by_type(%{"type" => "object"} = schema) do
stream_gen(fn ->
Enum.reduce(schema["properties"], %{}, fn {k, inner_schema}, acc ->
v = inner_schema |> generate_by_type() |> Enum.take(1) |> List.first()
Map.put(acc, k, v)
end)
end)
end
defp generate_by_type(_schema), do: StreamData.constant(nil)
defp generate_string(%{"format" => "date-time"}),
do: stream_gen(fn -> 30 |> Faker.DateTime.backward() |> DateTime.to_iso8601() end)
defp generate_string(%{"format" => "uuid"}), do: stream_gen(&Faker.UUID.v4/0)
defp generate_string(%{"format" => "email"}), do: stream_gen(&Faker.Internet.email/0)
defp generate_string(%{"format" => "hostname"}),
do: stream_gen(&Faker.Internet.domain_name/0)
defp generate_string(%{"format" => "ipv4"}), do: stream_gen(&Faker.Internet.ip_v4_address/0)
defp generate_string(%{"format" => "ipv6"}), do: stream_gen(&Faker.Internet.ip_v6_address/0)
defp generate_string(%{"format" => "uri"}), do: stream_gen(&Faker.Internet.url/0)
defp generate_string(%{"format" => "image_uri"}) do
stream_gen(fn ->
w = Enum.random(1..4) * 400
h = Enum.random(1..4) * 400
"https://source.unsplash.com/random/#{w}x#{h}"
end)
end
defp generate_string(%{"enum" => choices}), do: StreamData.member_of(choices)
defp generate_string(%{"pattern" => regex}),
do: Randex.stream(Regex.compile!(regex), mod: Randex.Generator.StreamData)
defp generate_string(schema) do
min = schema["minLength"] || 0
max = schema["maxLength"] || 1024
stream_gen(fn ->
s = Faker.Lorem.word()
case String.length(s) do
v when v > max -> String.slice(s, 0, max - 1)
v when v < min -> String.slice(Faker.Lorem.sentence(min), 0, min)
_ -> s
end
end)
end
defp stream_gen(fun) do
StreamData.map(StreamData.constant(nil), fn _ -> fun.() end)
end
end