fakeit 0.1.1 → 0.1.2
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/Gemfile.lock +3 -3
- data/README.md +6 -12
- data/bin/fakeit +5 -2
- data/fakeit.gemspec +1 -1
- data/lib/fakeit.rb +4 -3
- data/lib/fakeit/app/app.rb +23 -16
- data/lib/fakeit/app/options.rb +12 -0
- data/lib/fakeit/core_extensions/schema.rb +12 -26
- data/lib/fakeit/logger.rb +4 -0
- data/lib/fakeit/middleware/{logger.rb → recorder.rb} +3 -3
- data/lib/fakeit/openapi/example/array_example.rb +13 -0
- data/lib/fakeit/openapi/example/boolean_example.rb +9 -0
- data/lib/fakeit/openapi/example/integer_example.rb +33 -0
- data/lib/fakeit/openapi/example/number_example.rb +21 -0
- data/lib/fakeit/openapi/example/object_example.rb +9 -0
- data/lib/fakeit/openapi/example/string_example.rb +45 -0
- data/lib/fakeit/openapi/operation.rb +20 -15
- data/lib/fakeit/openapi/schema.rb +23 -0
- data/lib/fakeit/openapi/specification.rb +4 -2
- data/lib/fakeit/validation/validator.rb +22 -1
- data/lib/fakeit/version.rb +1 -1
- metadata +14 -5
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 37c7f01bd53d8fccce9dc5d1411a9917fca097614a2dafe8bd1498cf2f8bd255
|
4
|
+
data.tar.gz: b35468a6e2daf5749fbedb971026c264e23fe34b6d6a49c322c88cbe48a587de
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 929e9c035ee1ebce9b5ad833f2b846c1835242d67534a9bec5456369685273cfcb3a7554c89f8802f1592fb66a6098d1e123f9ec90764e0ffba3f1924523e89c
|
7
|
+
data.tar.gz: eb310158c2228e0ecca7ece315dc12b1e52cad915ef68472c88884d555c05bd42d9b3351a9133d1aa0e52f80c899d4ef759ba9e893eeb5ffd6cce3975eacaf51
|
data/Gemfile.lock
CHANGED
@@ -1,9 +1,9 @@
|
|
1
1
|
PATH
|
2
2
|
remote: .
|
3
3
|
specs:
|
4
|
-
fakeit (0.1.
|
4
|
+
fakeit (0.1.2)
|
5
5
|
faker (~> 1.9)
|
6
|
-
openapi_parser (= 0.
|
6
|
+
openapi_parser (= 0.3.0)
|
7
7
|
rack (~> 2.0)
|
8
8
|
slop (~> 4.6)
|
9
9
|
|
@@ -21,7 +21,7 @@ GEM
|
|
21
21
|
concurrent-ruby (~> 1.0)
|
22
22
|
jaro_winkler (1.5.2)
|
23
23
|
json (2.2.0)
|
24
|
-
openapi_parser (0.
|
24
|
+
openapi_parser (0.3.0)
|
25
25
|
parallel (1.17.0)
|
26
26
|
parser (2.6.2.1)
|
27
27
|
ast (~> 2.4.0)
|
data/README.md
CHANGED
@@ -15,23 +15,15 @@ Create mock server from Openapi specification
|
|
15
15
|
|
16
16
|
## Installation
|
17
17
|
|
18
|
-
|
19
|
-
|
20
|
-
```ruby
|
21
|
-
gem 'fakeit'
|
22
|
-
```
|
23
|
-
|
24
|
-
And then execute:
|
25
|
-
|
26
|
-
$ bundle
|
27
|
-
|
28
|
-
Or install it yourself as:
|
18
|
+
Install it with:
|
29
19
|
|
30
20
|
$ gem install fakeit
|
31
21
|
|
22
|
+
Or use the [docker image](https://hub.docker.com/r/realfengjia/fakeit)
|
23
|
+
|
32
24
|
## Usage
|
33
25
|
|
34
|
-
$ fakeit --spec
|
26
|
+
$ fakeit --spec <Local file or remote url>
|
35
27
|
|
36
28
|
Command line options:
|
37
29
|
|
@@ -40,6 +32,8 @@ Command line options:
|
|
40
32
|
--spec spec file uri (required)
|
41
33
|
-p, --port custom port
|
42
34
|
-q, --quiet mute request and response log
|
35
|
+
--permissive log validation error as warning instead of deny request
|
36
|
+
--use-example use example provided in spec
|
43
37
|
|
44
38
|
other options:
|
45
39
|
-v, --version
|
data/bin/fakeit
CHANGED
@@ -9,6 +9,8 @@ begin
|
|
9
9
|
o.string '--spec', 'spec file uri (required)', required: true
|
10
10
|
o.integer '-p', '--port', 'custom port'
|
11
11
|
o.bool '-q', '--quiet', 'mute request and response log'
|
12
|
+
o.bool '--permissive', 'log validation error as warning instead of deny request'
|
13
|
+
o.bool '--use-example', 'use example provided in spec'
|
12
14
|
o.separator ''
|
13
15
|
o.separator 'other options:'
|
14
16
|
o.on '-v', '--version' do
|
@@ -38,6 +40,7 @@ trap(:INT) do
|
|
38
40
|
end
|
39
41
|
end
|
40
42
|
|
41
|
-
|
42
|
-
app.
|
43
|
+
options = Fakeit::App::Options.new(permissive: opts.permissive?, use_example: opts.use_example?)
|
44
|
+
app = Fakeit.build(opts[:spec], options)
|
45
|
+
app.use(Fakeit::Middleware::Recorder) unless opts.quiet?
|
43
46
|
server.run(app, Port: opts[:port], Host: '0.0.0.0')
|
data/fakeit.gemspec
CHANGED
@@ -31,7 +31,7 @@ Gem::Specification.new do |spec|
|
|
31
31
|
spec.add_development_dependency 'simplecov', '~> 0.16'
|
32
32
|
|
33
33
|
spec.add_dependency 'faker', '~> 1.9'
|
34
|
-
spec.add_dependency 'openapi_parser', '0.
|
34
|
+
spec.add_dependency 'openapi_parser', '0.3.0'
|
35
35
|
spec.add_dependency 'rack', '~> 2.0'
|
36
36
|
spec.add_dependency 'slop', '~> 4.6'
|
37
37
|
end
|
data/lib/fakeit.rb
CHANGED
@@ -4,14 +4,15 @@ require 'open-uri'
|
|
4
4
|
require 'openapi_parser'
|
5
5
|
require 'faker'
|
6
6
|
require 'rack'
|
7
|
+
require 'logger'
|
7
8
|
|
8
|
-
Dir.glob(File.join(File.dirname(__FILE__), 'fakeit', '
|
9
|
+
Dir.glob(File.join(File.dirname(__FILE__), 'fakeit', '**/*.rb')).each { |file| require file }
|
9
10
|
|
10
11
|
module Fakeit
|
11
12
|
class << self
|
12
|
-
def build(spec_file)
|
13
|
+
def build(spec_file, options)
|
13
14
|
Rack::Builder.new do
|
14
|
-
run App.create(spec_file)
|
15
|
+
run App.create(spec_file, options)
|
15
16
|
end
|
16
17
|
end
|
17
18
|
end
|
data/lib/fakeit/app/app.rb
CHANGED
@@ -1,36 +1,43 @@
|
|
1
1
|
module Fakeit
|
2
2
|
module App
|
3
3
|
class << self
|
4
|
-
def create(spec_file)
|
4
|
+
def create(spec_file, options)
|
5
5
|
specification = Fakeit::Openapi.load(spec_file)
|
6
6
|
|
7
7
|
proc do |env|
|
8
8
|
request = Rack::Request.new(env)
|
9
9
|
specification
|
10
|
-
.operation(request.request_method.downcase.to_sym, request.path_info)
|
11
|
-
|
12
|
-
.then(&method(:rack_response))
|
13
|
-
rescue Fakeit::Validation::ValidationError => e
|
14
|
-
error_response(e)
|
10
|
+
.operation(request.request_method.downcase.to_sym, request.path_info, options)
|
11
|
+
.then { |operation| operation ? handle(operation, request, options) : not_found }
|
15
12
|
end
|
16
13
|
end
|
17
14
|
|
18
15
|
private
|
19
16
|
|
20
|
-
def
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
if operation
|
28
|
-
[operation.status, operation.headers, [operation.body]]
|
17
|
+
def handle(operation, request, options)
|
18
|
+
validate(operation, request)
|
19
|
+
response(operation)
|
20
|
+
rescue Fakeit::Validation::ValidationError => e
|
21
|
+
if options.permissive
|
22
|
+
Fakeit::Logger.warn(e.message)
|
23
|
+
response(operation)
|
29
24
|
else
|
30
|
-
|
25
|
+
error(e)
|
31
26
|
end
|
32
27
|
end
|
33
28
|
|
29
|
+
def error(err)
|
30
|
+
[418, { 'Content-Type' => 'application/json' }, [{ message: err.message }.to_json]]
|
31
|
+
end
|
32
|
+
|
33
|
+
def not_found
|
34
|
+
[404, {}, ['Not Found']]
|
35
|
+
end
|
36
|
+
|
37
|
+
def response(operation)
|
38
|
+
[operation.status, operation.headers, [operation.body]]
|
39
|
+
end
|
40
|
+
|
34
41
|
def validate(operation, request)
|
35
42
|
operation.validate(
|
36
43
|
body: request.body&.read.to_s,
|
@@ -1,37 +1,23 @@
|
|
1
|
+
require 'fakeit/openapi/schema'
|
2
|
+
|
1
3
|
module OpenAPIParser
|
2
4
|
module Schemas
|
3
5
|
class Schema
|
4
|
-
|
6
|
+
include Fakeit::Openapi::Schema
|
5
7
|
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
when 'number' then Faker::Number.decimal.to_f
|
11
|
-
when 'boolean' then Faker::Boolean.boolean
|
12
|
-
when 'array' then [items.to_example]
|
13
|
-
else # object
|
14
|
-
properties.each_with_object({}) { |(name, schema), obj| obj[name] = schema.to_example }
|
15
|
-
end
|
8
|
+
alias old_type type
|
9
|
+
|
10
|
+
def type
|
11
|
+
old_type || inferred_type
|
16
12
|
end
|
17
13
|
|
18
14
|
private
|
19
15
|
|
20
|
-
def
|
21
|
-
if
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
end
|
26
|
-
end
|
27
|
-
|
28
|
-
def string_example
|
29
|
-
if enum
|
30
|
-
enum.to_a.sample
|
31
|
-
elsif pattern
|
32
|
-
Faker::Base.regexify(pattern)
|
33
|
-
else
|
34
|
-
Faker::Book.title
|
16
|
+
def inferred_type
|
17
|
+
if properties
|
18
|
+
'object'
|
19
|
+
elsif items
|
20
|
+
'array'
|
35
21
|
end
|
36
22
|
end
|
37
23
|
end
|
@@ -1,6 +1,6 @@
|
|
1
1
|
module Fakeit
|
2
2
|
module Middleware
|
3
|
-
class
|
3
|
+
class Recorder
|
4
4
|
def initialize(app)
|
5
5
|
@app = app
|
6
6
|
end
|
@@ -16,12 +16,12 @@ module Fakeit
|
|
16
16
|
|
17
17
|
def log_request(env)
|
18
18
|
env['rack.input']
|
19
|
-
&.tap { |body|
|
19
|
+
&.tap { |body| Logger.info("Request body: #{body.read}") }
|
20
20
|
&.tap { |body| body.rewind }
|
21
21
|
end
|
22
22
|
|
23
23
|
def log_response(response)
|
24
|
-
|
24
|
+
Logger.info("Response body: #{response[2].first}")
|
25
25
|
end
|
26
26
|
end
|
27
27
|
end
|
@@ -0,0 +1,33 @@
|
|
1
|
+
module Fakeit
|
2
|
+
module Openapi
|
3
|
+
module Example
|
4
|
+
BIG_INT = 2**32
|
5
|
+
|
6
|
+
def integer_example
|
7
|
+
if enum
|
8
|
+
enum.to_a.sample
|
9
|
+
else
|
10
|
+
Faker::Number.between(min_int, max_int)
|
11
|
+
end
|
12
|
+
end
|
13
|
+
|
14
|
+
private
|
15
|
+
|
16
|
+
def min_int
|
17
|
+
if minimum
|
18
|
+
exclusiveMinimum ? minimum + 1 : minimum
|
19
|
+
else
|
20
|
+
1
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
def max_int
|
25
|
+
if maximum
|
26
|
+
exclusiveMaximum ? maximum - 1 : maximum
|
27
|
+
else
|
28
|
+
BIG_INT
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
@@ -0,0 +1,21 @@
|
|
1
|
+
module Fakeit
|
2
|
+
module Openapi
|
3
|
+
module Example
|
4
|
+
BIG_NUM = 2**32
|
5
|
+
|
6
|
+
def number_example
|
7
|
+
Faker::Number.between(min_num, max_num).round(2)
|
8
|
+
end
|
9
|
+
|
10
|
+
private
|
11
|
+
|
12
|
+
def min_num
|
13
|
+
(minimum || 0).to_f.ceil(2)
|
14
|
+
end
|
15
|
+
|
16
|
+
def max_num
|
17
|
+
(maximum || BIG_NUM).to_f.floor(2)
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
@@ -0,0 +1,45 @@
|
|
1
|
+
module Fakeit
|
2
|
+
module Openapi
|
3
|
+
module Example
|
4
|
+
FORMAT_HANDLERS = {
|
5
|
+
'uri' => -> { Faker::Internet.url },
|
6
|
+
'uuid' => -> { SecureRandom.uuid },
|
7
|
+
'email' => -> { Faker::Internet.email },
|
8
|
+
'date' => -> { Faker::Date.backward(100).iso8601 },
|
9
|
+
'date-time' => -> { Faker::Time.backward(100).iso8601 }
|
10
|
+
}.freeze
|
11
|
+
|
12
|
+
def string_example
|
13
|
+
if enum then enum.to_a.sample
|
14
|
+
elsif pattern then Faker::Base.regexify(pattern)
|
15
|
+
elsif format then string_format
|
16
|
+
elsif minLength || maxLength then string_with_length
|
17
|
+
else Faker::Book.title
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
private
|
22
|
+
|
23
|
+
def string_with_length
|
24
|
+
Faker::Internet.user_name(min_string_length..max_string_length)
|
25
|
+
end
|
26
|
+
|
27
|
+
def min_string_length
|
28
|
+
minLength || 0
|
29
|
+
end
|
30
|
+
|
31
|
+
def max_string_length
|
32
|
+
maxLength || min_string_length + 10
|
33
|
+
end
|
34
|
+
|
35
|
+
def string_format
|
36
|
+
(FORMAT_HANDLERS[format] || method(:unknown_format))[]
|
37
|
+
end
|
38
|
+
|
39
|
+
def unknown_format
|
40
|
+
Fakeit::Logger.warn("Unknown string format: #{format}")
|
41
|
+
'Unknown string format'
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end
|
@@ -1,24 +1,29 @@
|
|
1
1
|
module Fakeit
|
2
2
|
module Openapi
|
3
3
|
class Operation
|
4
|
-
def initialize(request_operation)
|
4
|
+
def initialize(request_operation, options)
|
5
5
|
@request_operation = request_operation
|
6
6
|
@validator = Fakeit::Validation::Validator.new(request_operation)
|
7
|
+
@options = options
|
7
8
|
end
|
8
9
|
|
9
10
|
def status
|
10
|
-
|
11
|
+
response.first.to_i
|
11
12
|
end
|
12
13
|
|
13
14
|
def headers
|
14
|
-
|
15
|
-
&.map { |k, v| [k, v.schema.to_example] }
|
16
|
-
&.push(['Content-Type',
|
15
|
+
response_headers
|
16
|
+
&.map { |k, v| [k, v.schema.to_example(@options.use_example)] }
|
17
|
+
&.tap { |headers| headers.push(['Content-Type', response_content_type]) if response_content_type }
|
17
18
|
.to_h
|
18
19
|
end
|
19
20
|
|
20
21
|
def body
|
21
|
-
|
22
|
+
response_schema
|
23
|
+
&.schema
|
24
|
+
&.to_example(@options.use_example)
|
25
|
+
&.then(&JSON.method(:generate))
|
26
|
+
.to_s
|
22
27
|
end
|
23
28
|
|
24
29
|
def validate(**request_parts)
|
@@ -27,23 +32,23 @@ module Fakeit
|
|
27
32
|
|
28
33
|
private
|
29
34
|
|
30
|
-
def
|
31
|
-
|
35
|
+
def response_content
|
36
|
+
response.last.content&.find { |k, _| k =~ %r{^application/.*json} }
|
32
37
|
end
|
33
38
|
|
34
|
-
def
|
35
|
-
|
39
|
+
def response_schema
|
40
|
+
response_content&.last
|
36
41
|
end
|
37
42
|
|
38
|
-
def
|
39
|
-
|
43
|
+
def response_content_type
|
44
|
+
response_content&.first
|
40
45
|
end
|
41
46
|
|
42
|
-
def
|
43
|
-
|
47
|
+
def response_headers
|
48
|
+
response.last.headers
|
44
49
|
end
|
45
50
|
|
46
|
-
def
|
51
|
+
def response
|
47
52
|
@request_operation.operation_object.responses.response.min
|
48
53
|
end
|
49
54
|
end
|
@@ -0,0 +1,23 @@
|
|
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
|
+
|
8
|
+
module Fakeit
|
9
|
+
module Openapi
|
10
|
+
module Schema
|
11
|
+
include Fakeit::Openapi::Example
|
12
|
+
|
13
|
+
def to_example(use_example = false)
|
14
|
+
return example if use_example && example
|
15
|
+
|
16
|
+
case type
|
17
|
+
when 'string', 'integer', 'number', 'boolean' then send("#{type}_example")
|
18
|
+
when 'array', 'object' then send("#{type}_example", use_example)
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
@@ -5,8 +5,10 @@ module Fakeit
|
|
5
5
|
@doc = doc
|
6
6
|
end
|
7
7
|
|
8
|
-
def operation(method, path)
|
9
|
-
@doc
|
8
|
+
def operation(method, path, options)
|
9
|
+
@doc
|
10
|
+
.request_operation(method, path)
|
11
|
+
&.then { |operation| Operation.new(operation, options) }
|
10
12
|
end
|
11
13
|
end
|
12
14
|
end
|
@@ -7,13 +7,34 @@ module Fakeit
|
|
7
7
|
|
8
8
|
def validate(body: '', params: {}, headers: {})
|
9
9
|
options = OpenAPIParser::SchemaValidator::Options.new(coerce_value: true)
|
10
|
+
body_data = parse(body)
|
10
11
|
|
11
|
-
@operation.validate_request_body(
|
12
|
+
@operation.validate_request_body(request_content_type, body_data) if need_validate?(body_data)
|
12
13
|
@operation.validate_path_params(options)
|
13
14
|
@operation.validate_request_parameter(params, headers, options)
|
14
15
|
rescue StandardError => e
|
15
16
|
raise ValidationError, e.message
|
16
17
|
end
|
18
|
+
|
19
|
+
private
|
20
|
+
|
21
|
+
def need_validate?(body_data)
|
22
|
+
request_content_type && body_data
|
23
|
+
end
|
24
|
+
|
25
|
+
def parse(body)
|
26
|
+
JSON.parse(body)
|
27
|
+
rescue StandardError
|
28
|
+
nil
|
29
|
+
end
|
30
|
+
|
31
|
+
def request_content_type
|
32
|
+
request_body&.content&.find { |k, _| k =~ %r{^application/.*json} }&.first
|
33
|
+
end
|
34
|
+
|
35
|
+
def request_body
|
36
|
+
@operation.operation_object.request_body
|
37
|
+
end
|
17
38
|
end
|
18
39
|
end
|
19
40
|
end
|
data/lib/fakeit/version.rb
CHANGED
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: fakeit
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.1.
|
4
|
+
version: 0.1.2
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Justin Feng
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2019-05
|
11
|
+
date: 2019-06-05 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: bundler
|
@@ -128,14 +128,14 @@ dependencies:
|
|
128
128
|
requirements:
|
129
129
|
- - '='
|
130
130
|
- !ruby/object:Gem::Version
|
131
|
-
version: 0.
|
131
|
+
version: 0.3.0
|
132
132
|
type: :runtime
|
133
133
|
prerelease: false
|
134
134
|
version_requirements: !ruby/object:Gem::Requirement
|
135
135
|
requirements:
|
136
136
|
- - '='
|
137
137
|
- !ruby/object:Gem::Version
|
138
|
-
version: 0.
|
138
|
+
version: 0.3.0
|
139
139
|
- !ruby/object:Gem::Dependency
|
140
140
|
name: rack
|
141
141
|
requirement: !ruby/object:Gem::Requirement
|
@@ -185,10 +185,19 @@ files:
|
|
185
185
|
- fakeit.gemspec
|
186
186
|
- lib/fakeit.rb
|
187
187
|
- lib/fakeit/app/app.rb
|
188
|
+
- lib/fakeit/app/options.rb
|
188
189
|
- lib/fakeit/core_extensions/schema.rb
|
189
|
-
- lib/fakeit/
|
190
|
+
- lib/fakeit/logger.rb
|
191
|
+
- lib/fakeit/middleware/recorder.rb
|
192
|
+
- lib/fakeit/openapi/example/array_example.rb
|
193
|
+
- lib/fakeit/openapi/example/boolean_example.rb
|
194
|
+
- lib/fakeit/openapi/example/integer_example.rb
|
195
|
+
- lib/fakeit/openapi/example/number_example.rb
|
196
|
+
- lib/fakeit/openapi/example/object_example.rb
|
197
|
+
- lib/fakeit/openapi/example/string_example.rb
|
190
198
|
- lib/fakeit/openapi/loader.rb
|
191
199
|
- lib/fakeit/openapi/operation.rb
|
200
|
+
- lib/fakeit/openapi/schema.rb
|
192
201
|
- lib/fakeit/openapi/specification.rb
|
193
202
|
- lib/fakeit/validation/validation_error.rb
|
194
203
|
- lib/fakeit/validation/validator.rb
|