fakeit 0.5.3 → 0.7.0

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: 110dc63680d60c4b438f4d2162c7164a6306519e4852cd68dbb36de630210a20
4
- data.tar.gz: 9ccc8d513d56cb66f6ed5438841052add1206cf2f5f0710cefa1956b2d2ffb72
3
+ metadata.gz: 60b523cb9a2c8cea3454b35cace650d51f2bf8e2ed8e53e07b0f35ffe45c9288
4
+ data.tar.gz: a3340dafc8607d5de8e9cb7f00c9b60679e7f84bdd2161f8d8607dad2e6089b3
5
5
  SHA512:
6
- metadata.gz: 23e5abad04ebea0e94c2bb643f77ecd86b4daec69ca13df18713a4c2ca2e601c019110435041d510281a41edd753ac3baf013a8fe1fbd606480eb7eea232bba6
7
- data.tar.gz: fb0fd7e1a33e71b434baa3a3c8a053a41cd8f29d3d93454b73d41fd9c8bf847974553964367b03039f7905444d061843bd0557feda246e7686a50c75c6fccb75
6
+ metadata.gz: cfab31deab5494ae69b2adeecec85efff46d69104ad71ce0d637e4809a492bb7ac45062d8a73e5cd4adb34ad347ac798fecd05f4a1b3b00bab37a3b44adab511
7
+ data.tar.gz: c4ef907db3d2df3de0fed6bf5a54b904969b080f312951665b6cfe863c4c10ecf2f3d1e7bf5b712d668878aa2b9b3b1dda7539da927ac9cabea005fec2528509
@@ -1,5 +1,6 @@
1
1
  AllCops:
2
2
  TargetRubyVersion: 2.7
3
+ NewCops: enable
3
4
 
4
5
  Style/Documentation:
5
6
  Enabled: false
@@ -18,17 +19,5 @@ Metrics/BlockLength:
18
19
  Security/Open:
19
20
  Enabled: false
20
21
 
21
- Style/HashEachMethods:
22
- Enabled: true
23
-
24
22
  Style/HashTransformKeys:
25
23
  Enabled: false
26
-
27
- Style/HashTransformValues:
28
- Enabled: true
29
-
30
- Lint/RaiseException:
31
- Enabled: true
32
-
33
- Lint/StructNewOverride:
34
- Enabled: true
@@ -0,0 +1,151 @@
1
+ # Changelog
2
+
3
+ ## [v0.6.3](https://github.com/JustinFeng/fakeit/tree/v0.6.3) (2020-09-11)
4
+
5
+ [Full Changelog](https://github.com/JustinFeng/fakeit/compare/v0.6.2...v0.6.3)
6
+
7
+ **Implemented enhancements:**
8
+
9
+ - Enhanced regex example generation
10
+
11
+ ## [v0.6.2](https://github.com/JustinFeng/fakeit/tree/v0.6.2) (2020-07-12)
12
+
13
+ [Full Changelog](https://github.com/JustinFeng/fakeit/compare/v0.6.1...v0.6.2)
14
+
15
+ ## [v0.6.1](https://github.com/JustinFeng/fakeit/tree/v0.6.1) (2020-05-02)
16
+
17
+ [Full Changelog](https://github.com/JustinFeng/fakeit/compare/v0.6.0...v0.6.1)
18
+
19
+ ## [v0.6.0](https://github.com/JustinFeng/fakeit/tree/v0.6.0) (2020-05-02)
20
+
21
+ [Full Changelog](https://github.com/JustinFeng/fakeit/compare/v0.5.3...v0.6.0)
22
+
23
+ **Implemented enhancements:**
24
+
25
+ - Added support for multipart/form-data validation
26
+
27
+ **Fixed bugs:**
28
+
29
+ - Multipart request contain special character will respond 500 [\#16](https://github.com/JustinFeng/fakeit/issues/16)
30
+
31
+ ## [v0.5.3](https://github.com/JustinFeng/fakeit/tree/v0.5.3) (2020-04-05)
32
+
33
+ [Full Changelog](https://github.com/JustinFeng/fakeit/compare/v0.5.2...v0.5.3)
34
+
35
+ ## [v0.5.2](https://github.com/JustinFeng/fakeit/tree/v0.5.2) (2020-04-03)
36
+
37
+ [Full Changelog](https://github.com/JustinFeng/fakeit/compare/v0.5.1...v0.5.2)
38
+
39
+ ## [v0.5.1](https://github.com/JustinFeng/fakeit/tree/v0.5.1) (2020-04-03)
40
+
41
+ [Full Changelog](https://github.com/JustinFeng/fakeit/compare/v0.5.0...v0.5.1)
42
+
43
+ ## [v0.5.0](https://github.com/JustinFeng/fakeit/tree/v0.5.0) (2020-04-03)
44
+
45
+ [Full Changelog](https://github.com/JustinFeng/fakeit/compare/v0.4.2...v0.5.0)
46
+
47
+ **Merged pull requests:**
48
+
49
+ - Bump rack from 2.0.7 to 2.0.8 [\#18](https://github.com/JustinFeng/fakeit/pull/18) ([dependabot[bot]](https://github.com/apps/dependabot))
50
+
51
+ ## [v0.4.2](https://github.com/JustinFeng/fakeit/tree/v0.4.2) (2019-11-22)
52
+
53
+ [Full Changelog](https://github.com/JustinFeng/fakeit/compare/v0.4.1...v0.4.2)
54
+
55
+ ## [v0.4.1](https://github.com/JustinFeng/fakeit/tree/v0.4.1) (2019-11-21)
56
+
57
+ [Full Changelog](https://github.com/JustinFeng/fakeit/compare/v0.4.0...v0.4.1)
58
+
59
+ **Merged pull requests:**
60
+
61
+ - Bump rack-cors from 1.0.3 to 1.0.6 [\#17](https://github.com/JustinFeng/fakeit/pull/17) ([dependabot[bot]](https://github.com/apps/dependabot))
62
+
63
+ ## [v0.4.0](https://github.com/JustinFeng/fakeit/tree/v0.4.0) (2019-10-09)
64
+
65
+ [Full Changelog](https://github.com/JustinFeng/fakeit/compare/v0.3.1...v0.4.0)
66
+
67
+ **Implemented enhancements:**
68
+
69
+ - Implement support for watching for changes in spec/yaml file. [\#15](https://github.com/JustinFeng/fakeit/issues/15)
70
+
71
+ ## [v0.3.1](https://github.com/JustinFeng/fakeit/tree/v0.3.1) (2019-09-01)
72
+
73
+ [Full Changelog](https://github.com/JustinFeng/fakeit/compare/v0.3.0...v0.3.1)
74
+
75
+ ## [v0.3.0](https://github.com/JustinFeng/fakeit/tree/v0.3.0) (2019-08-27)
76
+
77
+ [Full Changelog](https://github.com/JustinFeng/fakeit/compare/v0.2.1...v0.3.0)
78
+
79
+ **Closed issues:**
80
+
81
+ - Missing "motivation" section in readme [\#14](https://github.com/JustinFeng/fakeit/issues/14)
82
+
83
+ ## [v0.2.1](https://github.com/JustinFeng/fakeit/tree/v0.2.1) (2019-08-04)
84
+
85
+ [Full Changelog](https://github.com/JustinFeng/fakeit/compare/v0.2.0...v0.2.1)
86
+
87
+ **Implemented enhancements:**
88
+
89
+ - Support .yaml files [\#9](https://github.com/JustinFeng/fakeit/issues/9)
90
+
91
+ **Fixed bugs:**
92
+
93
+ - Adjust integer generation bounds [\#12](https://github.com/JustinFeng/fakeit/issues/12)
94
+
95
+ **Closed issues:**
96
+
97
+ - End to End Tests [\#8](https://github.com/JustinFeng/fakeit/issues/8)
98
+
99
+ **Merged pull requests:**
100
+
101
+ - support .yaml files [\#10](https://github.com/JustinFeng/fakeit/pull/10) ([monsterkrampe](https://github.com/monsterkrampe))
102
+
103
+ ## [v0.2.0](https://github.com/JustinFeng/fakeit/tree/v0.2.0) (2019-07-17)
104
+
105
+ [Full Changelog](https://github.com/JustinFeng/fakeit/compare/v0.1.5...v0.2.0)
106
+
107
+ **Implemented enhancements:**
108
+
109
+ - Generate more array elements with performance considered [\#7](https://github.com/JustinFeng/fakeit/issues/7)
110
+ - Non random response [\#5](https://github.com/JustinFeng/fakeit/issues/5)
111
+
112
+ **Fixed bugs:**
113
+
114
+ - Invalid example for number type with more than 2 decimal places 'multipleOf' [\#6](https://github.com/JustinFeng/fakeit/issues/6)
115
+
116
+ ## [v0.1.5](https://github.com/JustinFeng/fakeit/tree/v0.1.5) (2019-06-28)
117
+
118
+ [Full Changelog](https://github.com/JustinFeng/fakeit/compare/v0.1.4...v0.1.5)
119
+
120
+ **Implemented enhancements:**
121
+
122
+ - Allow CORS [\#4](https://github.com/JustinFeng/fakeit/pull/4) ([monsterkrampe](https://github.com/monsterkrampe))
123
+
124
+ ## [v0.1.4](https://github.com/JustinFeng/fakeit/tree/v0.1.4) (2019-06-25)
125
+
126
+ [Full Changelog](https://github.com/JustinFeng/fakeit/compare/v0.1.3...v0.1.4)
127
+
128
+ **Implemented enhancements:**
129
+
130
+ - W, \[2019-06-25T03:00:29.635993 \#1\] WARN -- : Unknown string format: guid [\#2](https://github.com/JustinFeng/fakeit/issues/2)
131
+
132
+ **Fixed bugs:**
133
+
134
+ - Request not completing [\#3](https://github.com/JustinFeng/fakeit/issues/3)
135
+ - Stack level too deep in docker container [\#1](https://github.com/JustinFeng/fakeit/issues/1)
136
+
137
+ ## [v0.1.3](https://github.com/JustinFeng/fakeit/tree/v0.1.3) (2019-06-16)
138
+
139
+ [Full Changelog](https://github.com/JustinFeng/fakeit/compare/v0.1.2...v0.1.3)
140
+
141
+ ## [v0.1.2](https://github.com/JustinFeng/fakeit/tree/v0.1.2) (2019-06-05)
142
+
143
+ [Full Changelog](https://github.com/JustinFeng/fakeit/compare/v0.1.1...v0.1.2)
144
+
145
+ ## [v0.1.1](https://github.com/JustinFeng/fakeit/tree/v0.1.1) (2019-05-26)
146
+
147
+ [Full Changelog](https://github.com/JustinFeng/fakeit/compare/4f94874eaf87300496e9de632cd659a031cb3ce2...v0.1.1)
148
+
149
+
150
+
151
+ \* *This Changelog was automatically generated by [github_changelog_generator](https://github.com/github-changelog-generator/github-changelog-generator)*
@@ -1,66 +1,71 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- fakeit (0.5.3)
5
- faker (= 2.11.0)
6
- openapi_parser (= 0.8.0)
4
+ fakeit (0.7.0)
5
+ faker (= 2.13.0)
6
+ openapi_parser (= 0.12.1)
7
7
  rack (~> 2.0)
8
8
  rack-cors (~> 1.0)
9
9
  rainbow (~> 3.0)
10
+ regexp-examples (= 1.5.1)
10
11
  slop (~> 4.8)
11
12
 
12
13
  GEM
13
14
  remote: https://rubygems.org/
14
15
  specs:
15
- ast (2.4.0)
16
- byebug (11.1.1)
17
- concurrent-ruby (1.1.6)
18
- diff-lcs (1.3)
16
+ ast (2.4.1)
17
+ byebug (11.1.3)
18
+ concurrent-ruby (1.1.7)
19
+ diff-lcs (1.4.4)
19
20
  docile (1.3.2)
20
- faker (2.11.0)
21
+ faker (2.13.0)
21
22
  i18n (>= 1.6, < 2)
22
- i18n (1.8.2)
23
+ i18n (1.8.5)
23
24
  concurrent-ruby (~> 1.0)
24
- jaro_winkler (1.5.4)
25
- openapi_parser (0.8.0)
26
- parallel (1.19.1)
27
- parser (2.7.0.5)
28
- ast (~> 2.4.0)
29
- rack (2.2.2)
25
+ openapi_parser (0.12.1)
26
+ parallel (1.19.2)
27
+ parser (2.7.1.4)
28
+ ast (~> 2.4.1)
29
+ rack (2.2.3)
30
30
  rack-cors (1.1.1)
31
31
  rack (>= 2.0.0)
32
32
  rack-test (1.1.0)
33
33
  rack (>= 1.0, < 3)
34
34
  rainbow (3.0.0)
35
35
  rake (12.3.3)
36
+ regexp-examples (1.5.1)
37
+ regexp_parser (1.7.1)
36
38
  rexml (3.2.4)
37
39
  rspec (3.9.0)
38
40
  rspec-core (~> 3.9.0)
39
41
  rspec-expectations (~> 3.9.0)
40
42
  rspec-mocks (~> 3.9.0)
41
- rspec-core (3.9.1)
42
- rspec-support (~> 3.9.1)
43
- rspec-expectations (3.9.1)
43
+ rspec-core (3.9.2)
44
+ rspec-support (~> 3.9.3)
45
+ rspec-expectations (3.9.2)
44
46
  diff-lcs (>= 1.2.0, < 2.0)
45
47
  rspec-support (~> 3.9.0)
46
48
  rspec-mocks (3.9.1)
47
49
  diff-lcs (>= 1.2.0, < 2.0)
48
50
  rspec-support (~> 3.9.0)
49
- rspec-support (3.9.2)
50
- rubocop (0.81.0)
51
- jaro_winkler (~> 1.5.1)
51
+ rspec-support (3.9.3)
52
+ rubocop (0.90.0)
52
53
  parallel (~> 1.10)
53
- parser (>= 2.7.0.1)
54
+ parser (>= 2.7.1.1)
54
55
  rainbow (>= 2.2.2, < 4.0)
56
+ regexp_parser (>= 1.7)
55
57
  rexml
58
+ rubocop-ast (>= 0.3.0, < 1.0)
56
59
  ruby-progressbar (~> 1.7)
57
60
  unicode-display_width (>= 1.4.0, < 2.0)
61
+ rubocop-ast (0.3.0)
62
+ parser (>= 2.7.1.4)
58
63
  ruby-progressbar (1.10.1)
59
- simplecov (0.18.5)
64
+ simplecov (0.19.0)
60
65
  docile (~> 1.1)
61
66
  simplecov-html (~> 0.11)
62
67
  simplecov-html (0.12.2)
63
- slop (4.8.1)
68
+ slop (4.8.2)
64
69
  unicode-display_width (1.7.0)
65
70
 
66
71
  PLATFORMS
@@ -77,4 +82,4 @@ DEPENDENCIES
77
82
  simplecov (~> 0.18)
78
83
 
79
84
  BUNDLED WITH
80
- 2.1.2
85
+ 2.1.4
data/README.md CHANGED
@@ -30,14 +30,12 @@ After tried several existing options, we cannot find a best solution to meet all
30
30
 
31
31
  ## Features
32
32
 
33
- * Randomly or statically generated response
34
- * Request validation
33
+ * Randomly or statically generated response (`application/.*json`)
34
+ * Request validation (`application/.*json`, `multipart/form-data`)
35
35
  * Load specification from local or remote
36
36
  * Support hot reload local specification
37
37
  * Support specification in yaml or json format
38
38
 
39
- **Note:** Only support json content type as of now
40
-
41
39
  ## Installation
42
40
 
43
41
  Install it with:
@@ -50,13 +48,14 @@ Or use the [docker image](https://hub.docker.com/r/realfengjia/fakeit)
50
48
 
51
49
  $ fakeit --spec <Local file or remote url>
52
50
 
53
- Command line options:
51
+ ### Command line options
54
52
 
55
53
  $ fakeit --help
56
54
  usage:
57
55
  --spec spec file uri (required)
58
56
  -p, --port custom port
59
57
  -q, --quiet mute request and response log
58
+ -l, --log-file redirect log to a file
60
59
  --permissive log validation error as warning instead of denying request
61
60
  --use-example use example provided in spec if exists
62
61
  --static generate static response
@@ -67,6 +66,29 @@ Command line options:
67
66
  -v, --version
68
67
  -h, --help
69
68
 
69
+ ### Configuration endpoint
70
+
71
+ Mock server behaviour can be changed on the fly
72
+
73
+ Retrieve current config:
74
+
75
+ GET /__fakeit_config__
76
+
77
+ Update config:
78
+
79
+ PUT /__fakeit_config__
80
+
81
+ Request and response:
82
+ ```json
83
+ {
84
+ 'permissive' => false,
85
+ 'use_example' => true,
86
+ 'static' => false,
87
+ 'static_types' => ['integer'],
88
+ 'static_properties' => ['id']
89
+ }
90
+ ```
91
+
70
92
  **Notes:**
71
93
  * See [here](docs/random.md) for Openapi properties supported in random response generation
72
94
  * See [here](docs/static.md) for default value in static response generation
data/bin/fakeit CHANGED
@@ -10,6 +10,7 @@ begin
10
10
  o.string '--spec', 'spec file uri (required)', required: true
11
11
  o.integer '-p', '--port', 'custom port'
12
12
  o.bool '-q', '--quiet', 'mute request and response log'
13
+ o.string '-l', '--log-file', 'redirect log to a file'
13
14
  o.bool '--permissive', 'log validation error as warning instead of denying request'
14
15
  o.bool '--use-example', 'use example provided in spec if exists'
15
16
  o.bool '--static', 'generate static response'
@@ -53,6 +54,9 @@ options = Fakeit::App::Options.new(
53
54
  )
54
55
  app = Fakeit.build(opts[:spec], options)
55
56
 
57
+ $stderr.reopen(File.new(opts[:log_file], File::WRONLY | File::TRUNC | File::CREAT)) if opts[:log_file]
58
+ $stderr.sync = true
59
+
56
60
  app.use Rack::Cors do
57
61
  allow do
58
62
  origins '*'
@@ -11,7 +11,7 @@ The following Openapi properties are supported in random response generation
11
11
  | |minItems|Default: `1`|
12
12
  | |maxItems|Default: `minItems + 2`|
13
13
  |string|enum| |
14
- | |pattern| |
14
+ | |pattern|Random generated once and cached for performance concern|
15
15
  | |format=uri| |
16
16
  | |format=uuid| |
17
17
  | |format=guid| |
@@ -6,8 +6,9 @@ Static value generation rule
6
6
  |---|---|---|
7
7
  |complex|oneOf|first item|
8
8
  | |anyOf|same as allOf|
9
+ |boolean|N/A|`true`|
9
10
  |array|N/A|size is `1`|
10
- | |minItems|size is minItems|
11
+ | |minItems|size is max(minItems,1)|
11
12
  |string|N/A|`string`|
12
13
  | |enum|first item|
13
14
  | |pattern|Generated but always the same|
@@ -32,10 +32,11 @@ Gem::Specification.new do |spec|
32
32
  spec.add_development_dependency 'rubocop', '~> 0.80'
33
33
  spec.add_development_dependency 'simplecov', '~> 0.18'
34
34
 
35
- spec.add_dependency 'faker', '2.11.0'
36
- spec.add_dependency 'openapi_parser', '0.8.0'
35
+ spec.add_dependency 'faker', '2.13.0'
36
+ spec.add_dependency 'openapi_parser', '0.12.1'
37
37
  spec.add_dependency 'rack', '~> 2.0'
38
38
  spec.add_dependency 'rack-cors', '~> 1.0'
39
39
  spec.add_dependency 'rainbow', '~> 3.0'
40
+ spec.add_dependency 'regexp-examples', '1.5.1'
40
41
  spec.add_dependency 'slop', '~> 4.8'
41
42
  end
@@ -4,6 +4,7 @@ require 'open-uri'
4
4
  require 'base64'
5
5
  require 'openapi_parser'
6
6
  require 'faker'
7
+ require 'regexp-examples'
7
8
  require 'rack'
8
9
  require 'logger'
9
10
  require 'rainbow'
@@ -14,7 +15,7 @@ module Fakeit
14
15
  class << self
15
16
  def build(spec_file, options)
16
17
  Rack::Builder.new do
17
- run App.create(spec_file, options)
18
+ run App::AppBuilder.new(spec_file, options).build
18
19
  end
19
20
  end
20
21
  end
@@ -0,0 +1,23 @@
1
+ module Fakeit
2
+ module App
3
+ class AppBuilder
4
+ def initialize(spec_file, options)
5
+ @config_route = Routes::ConfigRoute.new(options)
6
+ @openapi_route = Routes::OpenapiRoute.new(spec_file)
7
+ end
8
+
9
+ def build
10
+ proc do |env|
11
+ request = Rack::Request.new(env)
12
+
13
+ case request.path_info
14
+ when '/__fakeit_config__'
15
+ @config_route.call(request)
16
+ else
17
+ @openapi_route.call(request, @config_route.options)
18
+ end
19
+ end
20
+ end
21
+ end
22
+ end
23
+ end
@@ -0,0 +1,32 @@
1
+ module Fakeit
2
+ module App
3
+ module Helpers
4
+ class BodyParser
5
+ class << self
6
+ def parse(request)
7
+ case request.media_type
8
+ when %r{^application/.*json}
9
+ { media_type: request.media_type, data: parse_json(request.body.read) }
10
+ when 'multipart/form-data'
11
+ { media_type: request.media_type, data: parse_form_data(request.params) }
12
+ else
13
+ { media_type: request.media_type, data: request.body.read }
14
+ end
15
+ end
16
+
17
+ private
18
+
19
+ def parse_json(body)
20
+ body.empty? ? {} : JSON.parse(body)
21
+ rescue StandardError
22
+ raise Fakeit::Validation::ValidationError, 'Invalid json payload'
23
+ end
24
+
25
+ def parse_form_data(params)
26
+ params.transform_values { |v| v.class == Hash && v[:tempfile] ? v[:tempfile].read : v }
27
+ end
28
+ end
29
+ end
30
+ end
31
+ end
32
+ end
@@ -0,0 +1,29 @@
1
+ module Fakeit
2
+ module App
3
+ module Helpers
4
+ class ResponseBuilder
5
+ class << self
6
+ def error(code, err)
7
+ [code, { 'Content-Type' => 'application/json' }, [{ message: err.message }.to_json]]
8
+ end
9
+
10
+ def not_found
11
+ [404, {}, ['Not Found']]
12
+ end
13
+
14
+ def method_not_allowed
15
+ [405, {}, ['Method Not Allowed']]
16
+ end
17
+
18
+ def unsupported_media_type
19
+ [415, {}, ['Unsupported Media Type']]
20
+ end
21
+
22
+ def ok(body)
23
+ [200, { 'Content-Type' => 'application/json' }, [body.to_json]]
24
+ end
25
+ end
26
+ end
27
+ end
28
+ end
29
+ end
@@ -14,6 +14,16 @@ module Fakeit
14
14
  def use_static?(type: nil, property: nil)
15
15
  @static || @static_types.include?(type) || @static_properties.include?(property)
16
16
  end
17
+
18
+ def to_hash
19
+ {
20
+ permissive: @permissive,
21
+ use_example: @use_example,
22
+ static: @static,
23
+ static_types: @static_types,
24
+ static_properties: @static_properties
25
+ }
26
+ end
17
27
  end
18
28
  end
19
29
  end
@@ -0,0 +1,38 @@
1
+ module Fakeit
2
+ module App
3
+ module Routes
4
+ class ConfigRoute
5
+ attr_reader :options
6
+
7
+ def initialize(options)
8
+ @options = options
9
+ end
10
+
11
+ def call(request)
12
+ case [request.request_method, request.media_type]
13
+ in ['GET', _]
14
+ Fakeit::App::Helpers::ResponseBuilder.ok(@options.to_hash)
15
+ in ['PUT', 'application/json']
16
+ update(request)
17
+ in ['PUT', _]
18
+ Fakeit::App::Helpers::ResponseBuilder.unsupported_media_type
19
+ else
20
+ Fakeit::App::Helpers::ResponseBuilder.method_not_allowed
21
+ end
22
+ end
23
+
24
+ private
25
+
26
+ def update(request)
27
+ body = Fakeit::App::Helpers::BodyParser.parse(request)[:data]
28
+ @options = Fakeit::App::Options.new(**body.transform_keys(&:to_sym))
29
+
30
+ Fakeit::App::Helpers::ResponseBuilder.ok(@options.to_hash)
31
+ rescue ArgumentError => e
32
+ Logger.warn(Rainbow(e.message).red)
33
+ Fakeit::App::Helpers::ResponseBuilder.error(422, e)
34
+ end
35
+ end
36
+ end
37
+ end
38
+ end
@@ -0,0 +1,56 @@
1
+ module Fakeit
2
+ module App
3
+ module Routes
4
+ class OpenapiRoute
5
+ def initialize(spec_file)
6
+ @specification = Fakeit::Openapi::Specification.new(spec_file)
7
+ end
8
+
9
+ def call(request, options)
10
+ @specification
11
+ .operation(request.request_method.downcase.to_sym, request.path_info, options)
12
+ .then { _1 ? handle(_1, request, options) : Fakeit::App::Helpers::ResponseBuilder.not_found }
13
+ end
14
+
15
+ private
16
+
17
+ def handle(operation, request, options)
18
+ validate(operation, request)
19
+ response(operation)
20
+ rescue Fakeit::Validation::ValidationError => e
21
+ Logger.warn(Rainbow(e.message).red)
22
+ options.permissive ? response(operation) : Fakeit::App::Helpers::ResponseBuilder.error(418, e)
23
+ end
24
+
25
+ def response(operation)
26
+ [operation.status, operation.headers, [operation.body]]
27
+ end
28
+
29
+ def validate(operation, request)
30
+ operation.validate(
31
+ body: Helpers::BodyParser.parse(request),
32
+ params: parse_query(request.query_string),
33
+ headers: headers(request)
34
+ )
35
+ end
36
+
37
+ def headers(request)
38
+ request
39
+ .each_header
40
+ .select { |k, _| k.start_with? 'HTTP_' }
41
+ .map { |k, v| [k.sub(/^HTTP_/, '').split('_').map(&:capitalize).join('-'), v] }
42
+ .to_h
43
+ end
44
+
45
+ def parse_query(query_string)
46
+ rack_query = Rack::Utils.parse_nested_query(query_string)
47
+ cgi_query = CGI.parse(query_string)
48
+
49
+ rack_query.merge(cgi_query.slice(*rack_query.keys)) do |_, oldval, newval|
50
+ newval.is_a?(Array) && newval.size > 1 ? newval : oldval
51
+ end
52
+ end
53
+ end
54
+ end
55
+ end
56
+ end
@@ -1,4 +1,3 @@
1
1
  module Fakeit
2
- STDOUT.sync = true
3
- Logger = ::Logger.new(STDOUT)
2
+ Logger = ::Logger.new($stderr)
4
3
  end
@@ -4,7 +4,7 @@ module Fakeit
4
4
  def array_example(options)
5
5
  example_options = add_depth(options)
6
6
  if example_options[:use_static][type: 'array', property: example_options[:property]]
7
- generate_array_example(example_options, -> { min_array })
7
+ generate_array_example(example_options, -> { non_empty_size })
8
8
  else
9
9
  generate_array_example(example_options, -> { random_array_size(example_options) })
10
10
  end
@@ -18,17 +18,17 @@ module Fakeit
18
18
  end
19
19
 
20
20
  def random_array_size(example_options)
21
- uniqueItems ? min_array : Faker::Number.between(from: min_array, to: max_array(example_options[:depth]))
21
+ uniqueItems ? non_empty_size : Faker::Number.between(from: min_array, to: max_array(example_options[:depth]))
22
22
  end
23
23
 
24
24
  def generate_items(size, retries, example_options, result)
25
- loop do
25
+ while result.size < size
26
26
  item = items.to_example(example_options)
27
27
 
28
28
  if need_retry?(item, result, retries)
29
29
  retries -= 1
30
- elsif (result << item).size >= size
31
- break
30
+ else
31
+ result << item
32
32
  end
33
33
  end
34
34
  end
@@ -41,6 +41,10 @@ module Fakeit
41
41
  uniqueItems && result.include?(item) && retries.positive?
42
42
  end
43
43
 
44
+ def non_empty_size
45
+ [min_array, 1].max
46
+ end
47
+
44
48
  def min_array
45
49
  minItems || 1
46
50
  end
@@ -27,6 +27,8 @@ module Fakeit
27
27
  }.freeze
28
28
 
29
29
  def string_example(example_options)
30
+ @string_pattern ||= Regexp.new(pattern) if pattern
31
+
30
32
  if example_options[:use_static][type: 'string', property: example_options[:property]]
31
33
  static_string_example
32
34
  else
@@ -39,7 +41,7 @@ module Fakeit
39
41
  def static_string_example
40
42
  fixed_faker do
41
43
  if enum then enum.to_a.first
42
- elsif pattern then Faker::Base.regexify(pattern)
44
+ elsif pattern then static_string_pattern
43
45
  elsif format then static_string_format
44
46
  elsif length_constraint then static_string_with_length
45
47
  else 'string'
@@ -56,7 +58,7 @@ module Fakeit
56
58
 
57
59
  def random_string_example
58
60
  if enum then enum.to_a.sample
59
- elsif pattern then Faker::Base.regexify(pattern)
61
+ elsif pattern then random_string_pattern
60
62
  elsif format then random_string_format
61
63
  elsif length_constraint then string_with_length
62
64
  else Faker::Book.title
@@ -71,6 +73,12 @@ module Fakeit
71
73
  (STATIC_FORMAT_HANDLERS[format] || method(:unknown_format))[]
72
74
  end
73
75
 
76
+ def static_string_pattern
77
+ @static_string_pattern ||= @string_pattern.examples(
78
+ max_repeater_variance: 1, max_group_results: 1, max_results_limit: 1
79
+ ).first
80
+ end
81
+
74
82
  def length_constraint
75
83
  minLength || maxLength
76
84
  end
@@ -91,8 +99,12 @@ module Fakeit
91
99
  (RANDOM_FORMAT_HANDLERS[format] || method(:unknown_format))[]
92
100
  end
93
101
 
102
+ def random_string_pattern
103
+ @random_string_pattern ||= @string_pattern.random_example(max_repeater_variance: 1)
104
+ end
105
+
94
106
  def unknown_format
95
- Fakeit::Logger.info("Unknown string format: #{format}")
107
+ Logger.info("Unknown string format: #{format}")
96
108
  'Unknown string format'
97
109
  end
98
110
  end
@@ -25,7 +25,7 @@ module Fakeit
25
25
  @mtime = new_mtime
26
26
  @doc = Fakeit::Openapi.load(@spec_file)
27
27
  rescue StandardError => _e
28
- Fakeit::Logger.warn(Rainbow('Invalid spec file, use previous snapshot instead').red)
28
+ Logger.warn(Rainbow('Invalid spec file, use previous snapshot instead').red)
29
29
  end
30
30
  end
31
31
  end
@@ -5,13 +5,10 @@ module Fakeit
5
5
  @operation = operation
6
6
  end
7
7
 
8
- def validate(body: '', params: {}, headers: {})
8
+ def validate(body: {}, params: {}, headers: {})
9
9
  options = OpenAPIParser::SchemaValidator::Options.new(coerce_value: true)
10
10
 
11
- if request_content_type
12
- body_data = parse(body)
13
- @operation.validate_request_body(request_content_type, body_data)
14
- end
11
+ validate_body(body) unless request_content_types.empty?
15
12
  @operation.validate_path_params(options)
16
13
  @operation.validate_request_parameter(params, headers, options)
17
14
  rescue StandardError => e
@@ -20,14 +17,21 @@ module Fakeit
20
17
 
21
18
  private
22
19
 
23
- def parse(body)
24
- JSON.parse(body)
25
- rescue StandardError
26
- raise ValidationError, 'Invalid json payload'
20
+ def validate_body(body)
21
+ if request_content_types.include?(body[:media_type])
22
+ @operation.validate_request_body(body[:media_type], body[:data]) if can_validate?(body[:media_type])
23
+ else
24
+ raise ValidationError, 'Invalid request content type' if body[:media_type]
25
+ raise ValidationError, 'Request body is required' if request_body.required
26
+ end
27
+ end
28
+
29
+ def can_validate?(media_type)
30
+ media_type =~ %r{^application/.*json} || media_type == 'multipart/form-data'
27
31
  end
28
32
 
29
- def request_content_type
30
- request_body&.content&.find { |k, _| k =~ %r{^application/.*json} }&.first
33
+ def request_content_types
34
+ request_body&.content&.keys.to_a
31
35
  end
32
36
 
33
37
  def request_body
@@ -1,3 +1,3 @@
1
1
  module Fakeit
2
- VERSION = '0.5.3'.freeze
2
+ VERSION = '0.7.0'.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.5.3
4
+ version: 0.7.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Justin Feng
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2020-04-05 00:00:00.000000000 Z
11
+ date: 2020-10-22 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: bundler
@@ -114,28 +114,28 @@ dependencies:
114
114
  requirements:
115
115
  - - '='
116
116
  - !ruby/object:Gem::Version
117
- version: 2.11.0
117
+ version: 2.13.0
118
118
  type: :runtime
119
119
  prerelease: false
120
120
  version_requirements: !ruby/object:Gem::Requirement
121
121
  requirements:
122
122
  - - '='
123
123
  - !ruby/object:Gem::Version
124
- version: 2.11.0
124
+ version: 2.13.0
125
125
  - !ruby/object:Gem::Dependency
126
126
  name: openapi_parser
127
127
  requirement: !ruby/object:Gem::Requirement
128
128
  requirements:
129
129
  - - '='
130
130
  - !ruby/object:Gem::Version
131
- version: 0.8.0
131
+ version: 0.12.1
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.8.0
138
+ version: 0.12.1
139
139
  - !ruby/object:Gem::Dependency
140
140
  name: rack
141
141
  requirement: !ruby/object:Gem::Requirement
@@ -178,6 +178,20 @@ dependencies:
178
178
  - - "~>"
179
179
  - !ruby/object:Gem::Version
180
180
  version: '3.0'
181
+ - !ruby/object:Gem::Dependency
182
+ name: regexp-examples
183
+ requirement: !ruby/object:Gem::Requirement
184
+ requirements:
185
+ - - '='
186
+ - !ruby/object:Gem::Version
187
+ version: 1.5.1
188
+ type: :runtime
189
+ prerelease: false
190
+ version_requirements: !ruby/object:Gem::Requirement
191
+ requirements:
192
+ - - '='
193
+ - !ruby/object:Gem::Version
194
+ version: 1.5.1
181
195
  - !ruby/object:Gem::Dependency
182
196
  name: slop
183
197
  requirement: !ruby/object:Gem::Requirement
@@ -204,6 +218,7 @@ files:
204
218
  - ".gitignore"
205
219
  - ".rspec"
206
220
  - ".rubocop.yml"
221
+ - CHANGELOG.md
207
222
  - Gemfile
208
223
  - Gemfile.lock
209
224
  - LICENSE.txt
@@ -214,10 +229,12 @@ files:
214
229
  - docs/static.md
215
230
  - fakeit.gemspec
216
231
  - lib/fakeit.rb
217
- - lib/fakeit/app/app.rb
232
+ - lib/fakeit/app/app_builder.rb
233
+ - lib/fakeit/app/helpers/body_parser.rb
234
+ - lib/fakeit/app/helpers/response_builder.rb
218
235
  - lib/fakeit/app/options.rb
219
- - lib/fakeit/core_extensions/findable.rb
220
- - lib/fakeit/core_extensions/path_item_finder.rb
236
+ - lib/fakeit/app/routes/config_route.rb
237
+ - lib/fakeit/app/routes/openapi_route.rb
221
238
  - lib/fakeit/core_extensions/schema.rb
222
239
  - lib/fakeit/logger.rb
223
240
  - lib/fakeit/middleware/recorder.rb
@@ -253,7 +270,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
253
270
  - !ruby/object:Gem::Version
254
271
  version: '0'
255
272
  requirements: []
256
- rubygems_version: 3.1.2
273
+ rubygems_version: 3.1.4
257
274
  signing_key:
258
275
  specification_version: 4
259
276
  summary: Create mock server from Openapi specification
@@ -1,63 +0,0 @@
1
- module Fakeit
2
- module App
3
- class << self
4
- def create(spec_file, options)
5
- specification = Fakeit::Openapi::Specification.new(spec_file)
6
-
7
- proc do |env|
8
- request = Rack::Request.new(env)
9
- specification
10
- .operation(request.request_method.downcase.to_sym, request.path_info, options)
11
- .then { _1 ? handle(_1, request, options) : not_found }
12
- end
13
- end
14
-
15
- private
16
-
17
- def handle(operation, request, options)
18
- validate(operation, request)
19
- response(operation)
20
- rescue Fakeit::Validation::ValidationError => e
21
- Fakeit::Logger.warn(Rainbow(e.message).red)
22
- options.permissive ? response(operation) : error(e)
23
- end
24
-
25
- def error(err)
26
- [418, { 'Content-Type' => 'application/json' }, [{ message: err.message }.to_json]]
27
- end
28
-
29
- def not_found
30
- [404, {}, ['Not Found']]
31
- end
32
-
33
- def response(operation)
34
- [operation.status, operation.headers, [operation.body]]
35
- end
36
-
37
- def validate(operation, request)
38
- operation.validate(
39
- body: request.body&.read.to_s,
40
- params: parse_query(request.query_string),
41
- headers: headers(request)
42
- )
43
- end
44
-
45
- def headers(request)
46
- request
47
- .each_header
48
- .select { |k, _| k.start_with? 'HTTP_' }
49
- .map { |k, v| [k.sub(/^HTTP_/, '').split('_').map(&:capitalize).join('-'), v] }
50
- .to_h
51
- end
52
-
53
- def parse_query(query_string)
54
- rack_query = Rack::Utils.parse_nested_query(query_string)
55
- cgi_query = CGI.parse(query_string)
56
-
57
- rack_query.merge(cgi_query.slice(*rack_query.keys)) do |_, oldval, newval|
58
- newval.is_a?(Array) && newval.size > 1 ? newval : oldval
59
- end
60
- end
61
- end
62
- end
63
- end
@@ -1,13 +0,0 @@
1
- # https://github.com/ota42y/openapi_parser/issues/41
2
- module OpenAPIParser
3
- module Findable
4
- def purge_object_cache
5
- return if @purged
6
-
7
- @find_object_cache = {}
8
- @purged = true
9
-
10
- _openapi_all_child_objects.values.each(&:purge_object_cache)
11
- end
12
- end
13
- end
@@ -1,14 +0,0 @@
1
- # https://github.com/ota42y/openapi_parser/issues/75
2
- module OpenAPIParser
3
- class PathItemFinder
4
- private
5
-
6
- def find_path_and_params(http_method, request_path)
7
- return [request_path, {}] if matches_directly?(request_path, http_method)
8
-
9
- matching = matching_paths_with_params(request_path, http_method)
10
-
11
- matching.min_by { |match| match[1].size }
12
- end
13
- end
14
- end