fakeit 0.10.0 → 0.12.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/.circleci/config.yml +1 -1
- data/.gitignore +0 -2
- data/.rubocop.yml +28 -3
- data/CHANGELOG.md +20 -17
- data/Gemfile +12 -1
- data/Gemfile.lock +104 -62
- data/README.md +3 -1
- data/Rakefile +3 -3
- data/bin/fakeit +2 -1
- data/fakeit.gemspec +28 -32
- data/lib/fakeit/app/app_builder.rb +15 -11
- data/lib/fakeit/app/helpers/body_parser.rb +7 -7
- data/lib/fakeit/app/helpers/response_builder.rb +5 -5
- data/lib/fakeit/app/options.rb +16 -2
- data/lib/fakeit/app/routes/config_route.rb +3 -3
- data/lib/fakeit/app/routes/openapi_route.rb +4 -5
- data/lib/fakeit/core_extensions/array_validator.rb +1 -1
- data/lib/fakeit/core_extensions/schema.rb +4 -4
- data/lib/fakeit/middleware/recorder.rb +3 -3
- data/lib/fakeit/openapi/example/array_example.rb +4 -4
- data/lib/fakeit/openapi/example/boolean_example.rb +1 -1
- data/lib/fakeit/openapi/example/integer_example.rb +4 -4
- data/lib/fakeit/openapi/example/number_example.rb +4 -4
- data/lib/fakeit/openapi/example/string_example.rb +24 -22
- data/lib/fakeit/openapi/loader.rb +5 -5
- data/lib/fakeit/openapi/operation.rb +4 -4
- data/lib/fakeit/openapi/schema.rb +11 -11
- data/lib/fakeit/openapi/specification.rb +3 -3
- data/lib/fakeit/validation/validator.rb +4 -4
- data/lib/fakeit/version.rb +1 -1
- data/lib/fakeit.rb +13 -11
- metadata +48 -108
- data/lib/fakeit/core_extensions/reference.rb +0 -9
- data/lib/fakeit/openapi/reference_error.rb +0 -6
@@ -7,7 +7,7 @@ module Fakeit
|
|
7
7
|
def call(request, options)
|
8
8
|
@specification
|
9
9
|
.operation(request.request_method.downcase.to_sym, request.path_info, options)
|
10
|
-
.then {
|
10
|
+
.then { it ? handle(it, request, options) : Fakeit::App::Helpers::ResponseBuilder.not_found }
|
11
11
|
end
|
12
12
|
|
13
13
|
private
|
@@ -33,9 +33,8 @@ module Fakeit
|
|
33
33
|
def headers(request)
|
34
34
|
request
|
35
35
|
.each_header
|
36
|
-
.select { |k, _| k.start_with?
|
37
|
-
.
|
38
|
-
.to_h
|
36
|
+
.select { |k, _| k.start_with? "HTTP_" }
|
37
|
+
.to_h { |k, v| [k.sub(/^HTTP_/, "").split("_").map(&:capitalize).join("-"), v] }
|
39
38
|
end
|
40
39
|
|
41
40
|
def parse_query(query_string)
|
@@ -43,7 +42,7 @@ module Fakeit
|
|
43
42
|
cgi_query = CGI.parse(query_string)
|
44
43
|
|
45
44
|
rack_query.merge(cgi_query.slice(*rack_query.keys)) do |_, oldval, newval|
|
46
|
-
newval.is_a?(Array) && newval.size > 1 ? newval : oldval
|
45
|
+
(newval.is_a?(Array) && newval.size > 1) ? newval : oldval
|
47
46
|
end
|
48
47
|
end
|
49
48
|
end
|
@@ -1,7 +1,7 @@
|
|
1
1
|
module OpenAPIParser
|
2
2
|
class SchemaValidator
|
3
3
|
class ArrayValidator
|
4
|
-
|
4
|
+
alias_method :old_impl, :coerce_and_validate
|
5
5
|
|
6
6
|
def coerce_and_validate(value, schema, **keyword_args)
|
7
7
|
coerced_value = schema.parent.is_a?(OpenAPIParser::Schemas::Parameter) ? [*value] : value
|
@@ -1,11 +1,11 @@
|
|
1
|
-
require
|
1
|
+
require "fakeit/openapi/schema"
|
2
2
|
|
3
3
|
module OpenAPIParser
|
4
4
|
module Schemas
|
5
5
|
class Schema
|
6
6
|
include Fakeit::Openapi::Schema
|
7
7
|
|
8
|
-
|
8
|
+
alias_method :old_type, :type
|
9
9
|
|
10
10
|
def type = old_type || inferred_type
|
11
11
|
|
@@ -13,9 +13,9 @@ module OpenAPIParser
|
|
13
13
|
|
14
14
|
def inferred_type
|
15
15
|
if properties
|
16
|
-
|
16
|
+
"object"
|
17
17
|
elsif items
|
18
|
-
|
18
|
+
"array"
|
19
19
|
end
|
20
20
|
end
|
21
21
|
end
|
@@ -6,16 +6,16 @@ module Fakeit
|
|
6
6
|
def call(env)
|
7
7
|
env
|
8
8
|
.tap(&method(:log_request))
|
9
|
-
.then { @app.call(
|
9
|
+
.then { @app.call(it) }
|
10
10
|
.tap(&method(:log_response))
|
11
11
|
end
|
12
12
|
|
13
13
|
private
|
14
14
|
|
15
15
|
def log_request(env)
|
16
|
-
env[
|
16
|
+
env["rack.input"]
|
17
17
|
&.tap { |body| Logger.info("Request body: #{body.read}") }
|
18
|
-
&.tap
|
18
|
+
&.tap(&:rewind)
|
19
19
|
end
|
20
20
|
|
21
21
|
def log_response(response) = Logger.info("Response body: #{response[2].first}")
|
@@ -3,7 +3,7 @@ module Fakeit
|
|
3
3
|
module Example
|
4
4
|
def array_example(options)
|
5
5
|
example_options = add_depth(options)
|
6
|
-
if example_options[:use_static][type:
|
6
|
+
if example_options[:use_static][type: "array", property: example_options[:property]]
|
7
7
|
generate_array_example(example_options, -> { non_empty_size })
|
8
8
|
else
|
9
9
|
generate_array_example(example_options, -> { random_array_size(example_options) })
|
@@ -14,7 +14,7 @@ module Fakeit
|
|
14
14
|
|
15
15
|
def generate_array_example(example_options, get_size)
|
16
16
|
size = retries = get_size[]
|
17
|
-
[].tap { generate_items(size, retries, example_options,
|
17
|
+
[].tap { generate_items(size, retries, example_options, it) }
|
18
18
|
end
|
19
19
|
|
20
20
|
def random_array_size(example_options)
|
@@ -33,7 +33,7 @@ module Fakeit
|
|
33
33
|
end
|
34
34
|
end
|
35
35
|
|
36
|
-
def add_depth(example_options) = {
|
36
|
+
def add_depth(example_options) = {**example_options, depth: example_options[:depth] + 1}
|
37
37
|
|
38
38
|
def need_retry?(item, result, retries) = uniqueItems && result.include?(item) && retries.positive?
|
39
39
|
|
@@ -41,7 +41,7 @@ module Fakeit
|
|
41
41
|
|
42
42
|
def min_array = minItems || 1
|
43
43
|
|
44
|
-
def max_array(depth) = maxItems || min_array + (depth > 1 ? 2 : 9)
|
44
|
+
def max_array(depth) = maxItems || (min_array + ((depth > 1) ? 2 : 9))
|
45
45
|
end
|
46
46
|
end
|
47
47
|
end
|
@@ -2,7 +2,7 @@ module Fakeit
|
|
2
2
|
module Openapi
|
3
3
|
module Example
|
4
4
|
def boolean_example(example_options)
|
5
|
-
example_options[:use_static][type:
|
5
|
+
example_options[:use_static][type: "boolean", property: example_options[:property]] || Faker::Boolean.boolean
|
6
6
|
end
|
7
7
|
end
|
8
8
|
end
|
@@ -4,7 +4,7 @@ module Fakeit
|
|
4
4
|
DEFAULT_BITS = 32
|
5
5
|
|
6
6
|
def integer_example(example_options)
|
7
|
-
if example_options[:use_static][type:
|
7
|
+
if example_options[:use_static][type: "integer", property: example_options[:property]]
|
8
8
|
static_integer_example
|
9
9
|
else
|
10
10
|
random_integer_example
|
@@ -29,7 +29,7 @@ module Fakeit
|
|
29
29
|
end
|
30
30
|
end
|
31
31
|
|
32
|
-
def int_rand_begin = min_int / int_multiple + int_rand_begin_adjust
|
32
|
+
def int_rand_begin = (min_int / int_multiple) + int_rand_begin_adjust
|
33
33
|
|
34
34
|
def int_rand_end = max_int / int_multiple
|
35
35
|
|
@@ -49,12 +49,12 @@ module Fakeit
|
|
49
49
|
if maximum
|
50
50
|
exclusiveMaximum ? maximum - 1 : maximum
|
51
51
|
else
|
52
|
-
2**(int_bits - 1) - 1
|
52
|
+
(2**(int_bits - 1)) - 1
|
53
53
|
end
|
54
54
|
end
|
55
55
|
|
56
56
|
def int_bits
|
57
|
-
return DEFAULT_BITS unless
|
57
|
+
return DEFAULT_BITS unless /int\d+/.match?(format)
|
58
58
|
|
59
59
|
format[/\d+/].to_i
|
60
60
|
end
|
@@ -2,10 +2,10 @@ module Fakeit
|
|
2
2
|
module Openapi
|
3
3
|
module Example
|
4
4
|
MIN_NUM = -2**31
|
5
|
-
MAX_NUM = 2**31 - 1
|
5
|
+
MAX_NUM = (2**31) - 1
|
6
6
|
|
7
7
|
def number_example(example_options)
|
8
|
-
if example_options[:use_static][type:
|
8
|
+
if example_options[:use_static][type: "number", property: example_options[:property]]
|
9
9
|
static_number_example
|
10
10
|
else
|
11
11
|
random_number_example
|
@@ -14,11 +14,11 @@ module Fakeit
|
|
14
14
|
|
15
15
|
private
|
16
16
|
|
17
|
-
def static_number_example = (num_rand_end * num_multiple).then { multipleOf ?
|
17
|
+
def static_number_example = (num_rand_end * num_multiple).then { multipleOf ? it : it.round(2) }
|
18
18
|
|
19
19
|
def random_number_example
|
20
20
|
(Faker::Number.between(from: num_rand_begin, to: num_rand_end) * num_multiple)
|
21
|
-
.then { multipleOf ?
|
21
|
+
.then { multipleOf ? it : it.round(2) }
|
22
22
|
end
|
23
23
|
|
24
24
|
def num_rand_begin = multipleOf ? (min_num / multipleOf).ceil : min_num
|
@@ -2,34 +2,34 @@ module Fakeit
|
|
2
2
|
module Openapi
|
3
3
|
module Example
|
4
4
|
STATIC_FORMAT_HANDLERS = {
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
5
|
+
"uri" => -> { "https://some.uri" },
|
6
|
+
"uuid" => -> { "11111111-1111-1111-1111-111111111111" },
|
7
|
+
"guid" => -> { "11111111-1111-1111-1111-111111111111" },
|
8
|
+
"email" => -> { "some@email.com" },
|
9
|
+
"date" => -> { Date.today.iso8601 },
|
10
|
+
"date-time" => lambda do
|
11
11
|
now = Time.now
|
12
12
|
Time.new(now.year, now.month, now.day, 0, 0, 0, now.utc_offset).iso8601
|
13
13
|
end,
|
14
|
-
|
15
|
-
|
14
|
+
"binary" => -> { "binary" },
|
15
|
+
"byte" => -> { "Ynl0ZQ==" }
|
16
16
|
}.freeze
|
17
17
|
|
18
18
|
RANDOM_FORMAT_HANDLERS = {
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
19
|
+
"uri" => -> { Faker::Internet.url },
|
20
|
+
"uuid" => -> { SecureRandom.uuid },
|
21
|
+
"guid" => -> { SecureRandom.uuid },
|
22
|
+
"email" => -> { Faker::Internet.email },
|
23
|
+
"date" => -> { Faker::Date.backward(days: 100).iso8601 },
|
24
|
+
"date-time" => -> { Faker::Time.backward(days: 100).iso8601 },
|
25
|
+
"binary" => -> { Faker::String.random(length: 1..30) },
|
26
|
+
"byte" => -> { Base64.strict_encode64(Faker::String.random(length: 1..30)) }
|
27
27
|
}.freeze
|
28
28
|
|
29
29
|
def string_example(example_options)
|
30
30
|
@string_pattern ||= Regexp.new(pattern) if pattern
|
31
31
|
|
32
|
-
if example_options[:use_static][type:
|
32
|
+
if example_options[:use_static][type: "string", property: example_options[:property]]
|
33
33
|
static_string_example
|
34
34
|
else
|
35
35
|
random_string_example
|
@@ -44,7 +44,8 @@ module Fakeit
|
|
44
44
|
elsif pattern then static_string_pattern
|
45
45
|
elsif format then static_string_format
|
46
46
|
elsif length_constraint then static_string_with_length
|
47
|
-
else
|
47
|
+
else
|
48
|
+
"string"
|
48
49
|
end
|
49
50
|
end
|
50
51
|
end
|
@@ -61,11 +62,12 @@ module Fakeit
|
|
61
62
|
elsif pattern then random_string_pattern
|
62
63
|
elsif format then random_string_format
|
63
64
|
elsif length_constraint then string_with_length
|
64
|
-
else
|
65
|
+
else
|
66
|
+
Faker::Book.title
|
65
67
|
end
|
66
68
|
end
|
67
69
|
|
68
|
-
def static_string_with_length =
|
70
|
+
def static_string_with_length = "1" * max_string_length
|
69
71
|
|
70
72
|
def static_string_format = (STATIC_FORMAT_HANDLERS[format] || method(:unknown_format))[]
|
71
73
|
|
@@ -81,7 +83,7 @@ module Fakeit
|
|
81
83
|
|
82
84
|
def min_string_length = minLength || 0
|
83
85
|
|
84
|
-
def max_string_length = maxLength || min_string_length + 10
|
86
|
+
def max_string_length = maxLength || (min_string_length + 10)
|
85
87
|
|
86
88
|
def random_string_format = (RANDOM_FORMAT_HANDLERS[format] || method(:unknown_format))[]
|
87
89
|
|
@@ -89,7 +91,7 @@ module Fakeit
|
|
89
91
|
|
90
92
|
def unknown_format
|
91
93
|
Logger.info("Unknown string format: #{format}")
|
92
|
-
|
94
|
+
"Unknown string format"
|
93
95
|
end
|
94
96
|
end
|
95
97
|
end
|
@@ -4,20 +4,20 @@ module Fakeit
|
|
4
4
|
def load(src)
|
5
5
|
URI
|
6
6
|
.open(src, &:read)
|
7
|
-
.then { parse(src,
|
8
|
-
.then
|
7
|
+
.then { parse(src, it) }
|
8
|
+
.then { OpenAPIParser.parse(it, {strict_reference_validation: true}) }
|
9
9
|
end
|
10
10
|
|
11
11
|
private
|
12
12
|
|
13
13
|
def parse(src, content)
|
14
14
|
case File.extname(src)
|
15
|
-
when
|
15
|
+
when ".json"
|
16
16
|
JSON.parse(content)
|
17
|
-
when
|
17
|
+
when ".yml", ".yaml"
|
18
18
|
YAML.safe_load(content, [Date, Time])
|
19
19
|
else
|
20
|
-
raise
|
20
|
+
raise "Invalid openapi specification file"
|
21
21
|
end
|
22
22
|
end
|
23
23
|
end
|
@@ -12,8 +12,8 @@ module Fakeit
|
|
12
12
|
def headers
|
13
13
|
response_headers
|
14
14
|
.to_h
|
15
|
-
.transform_values {
|
16
|
-
.tap {
|
15
|
+
.transform_values { it.schema.to_example(example_options) }
|
16
|
+
.tap { it["Content-Type"] = response_content_type if response_content_type }
|
17
17
|
end
|
18
18
|
|
19
19
|
def body
|
@@ -31,11 +31,11 @@ module Fakeit
|
|
31
31
|
def serialise(body) = body.is_a?(String) ? body : JSON.generate(body)
|
32
32
|
|
33
33
|
def example_options
|
34
|
-
{
|
34
|
+
{use_example: @app_options.use_example, use_static: @app_options.method(:use_static?), depth: 0}
|
35
35
|
end
|
36
36
|
|
37
37
|
def response_content
|
38
|
-
response.last.content&.find { |k, _| k =~ %r{^application/.*json} || k ==
|
38
|
+
response.last.content&.find { |k, _| k =~ %r{^application/.*json} || k == "application/pdf" }
|
39
39
|
end
|
40
40
|
|
41
41
|
def response_schema = response_content&.last
|
@@ -1,9 +1,9 @@
|
|
1
|
-
require
|
2
|
-
require
|
3
|
-
require
|
4
|
-
require
|
5
|
-
require
|
6
|
-
require
|
1
|
+
require "fakeit/openapi/example/array_example"
|
2
|
+
require "fakeit/openapi/example/boolean_example"
|
3
|
+
require "fakeit/openapi/example/integer_example"
|
4
|
+
require "fakeit/openapi/example/number_example"
|
5
|
+
require "fakeit/openapi/example/object_example"
|
6
|
+
require "fakeit/openapi/example/string_example"
|
7
7
|
|
8
8
|
module Fakeit
|
9
9
|
module Openapi
|
@@ -32,20 +32,20 @@ module Fakeit
|
|
32
32
|
|
33
33
|
def all_of_example(example_options)
|
34
34
|
all_of
|
35
|
-
.select {
|
36
|
-
.map {
|
35
|
+
.select { it.type == "object" }
|
36
|
+
.map { it.to_example(example_options) }
|
37
37
|
.reduce(&:merge)
|
38
38
|
end
|
39
39
|
|
40
40
|
def any_of_example(example_options)
|
41
41
|
any_of_options(example_options)
|
42
|
-
.map {
|
42
|
+
.map { it.to_example(example_options) }
|
43
43
|
.reduce(&:merge)
|
44
44
|
end
|
45
45
|
|
46
46
|
def any_of_options(example_options)
|
47
47
|
any_of
|
48
|
-
.select {
|
48
|
+
.select { it.type == "object" }
|
49
49
|
.then do |options|
|
50
50
|
if example_options[:use_static][property: example_options[:property]]
|
51
51
|
options
|
@@ -56,7 +56,7 @@ module Fakeit
|
|
56
56
|
end
|
57
57
|
|
58
58
|
def type_based_example(example_options)
|
59
|
-
send("#{type}_example", example_options) if %w[string integer number boolean array object].include?(type)
|
59
|
+
send(:"#{type}_example", example_options) if %w[string integer number boolean array object].include?(type)
|
60
60
|
end
|
61
61
|
end
|
62
62
|
end
|
@@ -12,7 +12,7 @@ module Fakeit
|
|
12
12
|
|
13
13
|
@doc
|
14
14
|
.request_operation(method, path)
|
15
|
-
&.then { Operation.new(
|
15
|
+
&.then { Operation.new(it, options) }
|
16
16
|
end
|
17
17
|
|
18
18
|
private
|
@@ -24,8 +24,8 @@ module Fakeit
|
|
24
24
|
|
25
25
|
@mtime = new_mtime
|
26
26
|
@doc = Fakeit::Openapi.load(@spec_file)
|
27
|
-
rescue
|
28
|
-
Logger.warn(Rainbow(
|
27
|
+
rescue
|
28
|
+
Logger.warn(Rainbow("Invalid spec file, use previous snapshot instead").red)
|
29
29
|
end
|
30
30
|
end
|
31
31
|
end
|
@@ -9,7 +9,7 @@ module Fakeit
|
|
9
9
|
validate_body(body) unless request_content_types.empty?
|
10
10
|
@operation.validate_path_params(options)
|
11
11
|
@operation.validate_request_parameter(params, headers, options)
|
12
|
-
rescue
|
12
|
+
rescue => e
|
13
13
|
raise ValidationError, e.message
|
14
14
|
end
|
15
15
|
|
@@ -19,12 +19,12 @@ module Fakeit
|
|
19
19
|
if request_content_types.include?(body[:media_type])
|
20
20
|
@operation.validate_request_body(body[:media_type], body[:data]) if can_validate?(body[:media_type])
|
21
21
|
else
|
22
|
-
raise ValidationError,
|
23
|
-
raise ValidationError,
|
22
|
+
raise ValidationError, "Invalid request content type" if body[:media_type]
|
23
|
+
raise ValidationError, "Request body is required" if request_body.required
|
24
24
|
end
|
25
25
|
end
|
26
26
|
|
27
|
-
def can_validate?(media_type) = media_type =~ %r{^application/.*json} || media_type ==
|
27
|
+
def can_validate?(media_type) = media_type =~ %r{^application/.*json} || media_type == "multipart/form-data"
|
28
28
|
|
29
29
|
def request_content_types = request_body&.content&.keys.to_a
|
30
30
|
|
data/lib/fakeit/version.rb
CHANGED
data/lib/fakeit.rb
CHANGED
@@ -1,15 +1,17 @@
|
|
1
|
-
require
|
2
|
-
require
|
3
|
-
require
|
4
|
-
require
|
5
|
-
require
|
6
|
-
require
|
7
|
-
require
|
8
|
-
require
|
9
|
-
require
|
10
|
-
require
|
1
|
+
require "base64"
|
2
|
+
require "faker"
|
3
|
+
require "json"
|
4
|
+
require "logger"
|
5
|
+
require "openapi_parser"
|
6
|
+
require "open-uri"
|
7
|
+
require "rack"
|
8
|
+
require "rackup"
|
9
|
+
require "rainbow"
|
10
|
+
require "regexp-examples"
|
11
|
+
require "webrick"
|
12
|
+
require "yaml"
|
11
13
|
|
12
|
-
Dir.glob(File.join(File.dirname(__FILE__),
|
14
|
+
Dir.glob(File.join(File.dirname(__FILE__), "fakeit", "**/*.rb")).each { require it }
|
13
15
|
|
14
16
|
module Fakeit
|
15
17
|
class << self
|