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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 981cca99f9c406f1d950151ec99a59c8301f35be503a1d987419561bab0bc431
4
- data.tar.gz: fdafe0dc21b053068bb3f0abbc8556ecc8ebf73593b7fc3001fc5cd7774d97ef
3
+ metadata.gz: 37c7f01bd53d8fccce9dc5d1411a9917fca097614a2dafe8bd1498cf2f8bd255
4
+ data.tar.gz: b35468a6e2daf5749fbedb971026c264e23fe34b6d6a49c322c88cbe48a587de
5
5
  SHA512:
6
- metadata.gz: 7a5d23a302090b85a6e86f3e07da55c39bb2ce5f6cd18d10d0513046f19102c99c72dc21a6b5fccd924b4e6af12c2ab0685d2d708507a5798f6211f1b46be028
7
- data.tar.gz: 97ee27c6dff7b3452b97713058ba5a6891f1d82fe852e12e1fe39e59d17b32052ef1b5493ef94dbf9609b687b89fd2fe9fe685ec1ebbd8f9b4a3830ae75863b5
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.1)
4
+ fakeit (0.1.2)
5
5
  faker (~> 1.9)
6
- openapi_parser (= 0.2.4)
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.2.4)
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
- Add this line to your application's Gemfile:
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 http://url/to/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
- app = Fakeit.build(opts[:spec])
42
- app.use(Fakeit::Middleware::Logger) unless opts[:quiet]
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.2.4'
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', '/**/*.rb')).each { |file| require file }
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
@@ -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
- &.tap { |operation| validate(operation, request) }
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 error_response(err)
21
- { message: err.message }
22
- .to_json
23
- .then { |message| [418, { 'Content-Type' => 'application/json' }, [message]] }
24
- end
25
-
26
- def rack_response(operation)
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
- [404, {}, ['Not Found']]
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,
@@ -0,0 +1,12 @@
1
+ module Fakeit
2
+ module App
3
+ class Options
4
+ attr_reader :permissive, :use_example
5
+
6
+ def initialize(permissive: false, use_example: false)
7
+ @permissive = permissive
8
+ @use_example = use_example
9
+ end
10
+ end
11
+ end
12
+ end
@@ -1,37 +1,23 @@
1
+ require 'fakeit/openapi/schema'
2
+
1
3
  module OpenAPIParser
2
4
  module Schemas
3
5
  class Schema
4
- BIG_INT = 2**32
6
+ include Fakeit::Openapi::Schema
5
7
 
6
- def to_example
7
- case type
8
- when 'string' then string_example
9
- when 'integer' then integer_example
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 integer_example
21
- if enum
22
- enum.to_a.sample
23
- else
24
- Faker::Number.between(minimum || 1, maximum || BIG_INT)
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
@@ -0,0 +1,4 @@
1
+ module Fakeit
2
+ STDOUT.sync = true
3
+ Logger = ::Logger.new(STDOUT)
4
+ end
@@ -1,6 +1,6 @@
1
1
  module Fakeit
2
2
  module Middleware
3
- class Logger
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| puts "Request body: #{body.read}" }
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
- puts "Response body: #{response[2].first}"
24
+ Logger.info("Response body: #{response[2].first}")
25
25
  end
26
26
  end
27
27
  end
@@ -0,0 +1,13 @@
1
+ module Fakeit
2
+ module Openapi
3
+ module Example
4
+ MAX_SIZE = 50
5
+
6
+ def array_example(use_example)
7
+ Array.new(Faker::Number.between(minItems || 1, maxItems || MAX_SIZE)) do
8
+ items.to_example(use_example)
9
+ end
10
+ end
11
+ end
12
+ end
13
+ end
@@ -0,0 +1,9 @@
1
+ module Fakeit
2
+ module Openapi
3
+ module Example
4
+ def boolean_example
5
+ Faker::Boolean.boolean
6
+ end
7
+ end
8
+ end
9
+ 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,9 @@
1
+ module Fakeit
2
+ module Openapi
3
+ module Example
4
+ def object_example(use_example)
5
+ properties.each_with_object({}) { |(name, schema), obj| obj[name] = schema.to_example(use_example) }
6
+ end
7
+ end
8
+ end
9
+ 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
- openapi_response.first.to_i
11
+ response.first.to_i
11
12
  end
12
13
 
13
14
  def headers
14
- openapi_headers
15
- &.map { |k, v| [k, v.schema.to_example] }
16
- &.push(['Content-Type', openapi_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
- openapi_schema&.schema&.to_example&.then(&JSON.method(:generate)).to_s
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 openapi_content
31
- openapi_response.last.content&.find { |k, _| k =~ %r{^application/.*json} }
35
+ def response_content
36
+ response.last.content&.find { |k, _| k =~ %r{^application/.*json} }
32
37
  end
33
38
 
34
- def openapi_schema
35
- openapi_content&.last
39
+ def response_schema
40
+ response_content&.last
36
41
  end
37
42
 
38
- def openapi_content_type
39
- openapi_content&.first
43
+ def response_content_type
44
+ response_content&.first
40
45
  end
41
46
 
42
- def openapi_headers
43
- openapi_response.last.headers
47
+ def response_headers
48
+ response.last.headers
44
49
  end
45
50
 
46
- def openapi_response
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.request_operation(method, path)&.then(&Operation.method(:new))
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('application/json', JSON.parse(body)) unless body.empty?
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
@@ -1,3 +1,3 @@
1
1
  module Fakeit
2
- VERSION = '0.1.1'.freeze
2
+ VERSION = '0.1.2'.freeze
3
3
  end
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.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-26 00:00:00.000000000 Z
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.2.4
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.2.4
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/middleware/logger.rb
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