fakeit 0.1.1 → 0.1.2

Sign up to get free protection for your applications and to get access to all the features.
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