paradocs 1.0.23 → 1.1.3

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,17 @@
1
+ site_name: Paradocs
2
+ theme:
3
+ name: readthedocs
4
+ nav_style: dark
5
+ nav:
6
+ - 'index.md'
7
+ - 'policies.md'
8
+ - 'schema.md'
9
+ - 'struct.md'
10
+ - 'form_objects_dsl.md'
11
+ - 'subschema.md'
12
+ - 'documentation_generation.md'
13
+ - 'payload_builder.md'
14
+ - 'custom_configuration.md'
15
+ - 'faq.md'
16
+ - 'changelog.md'
17
+
@@ -10,7 +10,7 @@ Gem::Specification.new do |spec|
10
10
  spec.email = ["tkachenko.maxim.w@gmail.com", "ismaelct@gmail.com"]
11
11
  spec.description = %q{Flexible DSL for declaring allowed parameters focused on DRY validation that gives you opportunity to generate API documentation on-the-fly.}
12
12
  spec.summary = %q{A huge add-on for original gem mostly focused on retrieving the more metadata from declared schemas as possible.}
13
- spec.homepage = "https://github.com/mtkachenk0/paradocs"
13
+ spec.homepage = "https://paradocs.readthedocs.io/en/latest"
14
14
  spec.license = "MIT"
15
15
 
16
16
  spec.files = `git ls-files -z`.split("\x0")
@@ -19,7 +19,7 @@ Gem::Specification.new do |spec|
19
19
  spec.require_paths = ["lib"]
20
20
 
21
21
  spec.add_development_dependency "bundler", "~> 2.1"
22
- spec.add_development_dependency "rake", ">= 12.3.3"
22
+ spec.add_development_dependency "rake", '~> 12.3'
23
23
  spec.add_development_dependency "rspec", '3.4.0'
24
24
  spec.add_development_dependency "pry", "~> 0"
25
25
  end
@@ -0,0 +1 @@
1
+ mkdocs==1.1.2
@@ -0,0 +1,70 @@
1
+ require "spec_helper"
2
+
3
+ describe Paradocs::Extensions::PayloadBuilder do
4
+ let(:schema) do
5
+ Paradocs::Schema.new do
6
+ field(:test).present.type(:string).mutates_schema! { :subschema1 }
7
+ subschema(:subschema1) do
8
+ field(:subtest1).declared.type(:number)
9
+ end
10
+ subschema(:subschema2) do
11
+ field(:subtest2).required.type(:array).schema do
12
+ field(:hello).type(:string).mutates_schema! { |*| :deep_schema }
13
+ subschema(:deep_schema) { field(:deep_field).type(:boolean) }
14
+ subschema(:empty) { }
15
+ end
16
+ end
17
+ # 2 mutation fields and more than 1 subschema pack work good in validation but docs
18
+ # will contain only 1 subschema at once: foo subschemes will never be mixed with test subschemes
19
+ field(:foo).required.type(:object).schema do
20
+ field(:bar).present.type(:string).options(["foo", "bar"]).mutates_schema! do |value, *|
21
+ value == "foo" ? :fooschema : :barschema
22
+ end
23
+ subschema(:fooschema) { }
24
+ subschema(:barschema) do
25
+ field(:barfield).present.type(:boolean)
26
+ end
27
+ end
28
+ end
29
+ end
30
+
31
+ it "gives an example payload and takes into account the subschemes" do
32
+ allow_any_instance_of(Array).to receive(:sample) { "bar" }
33
+ payloads = described_class.new(schema).build!
34
+ expect(payloads.keys.sort).to eq([:barschema, :fooschema, :subschema1, :subschema2_deep_schema, :subschema2_empty])
35
+ expect(payloads[:barschema]).to eq({"test" => nil, "foo" => {"bar" => "bar", "barfield" => nil}})
36
+ expect(payloads[:fooschema]).to eq({"test" => nil, "foo" => {"bar" => "bar"}})
37
+ expect(payloads[:subschema1]).to eq({"test" => nil, "foo" => {"bar" => "bar"}, "subtest1" => nil})
38
+ expect(payloads[:subschema2_deep_schema]).to eq({
39
+ "subtest2" => [{"deep_field" => nil, "hello" => nil}], "test" => nil, "foo" => {"bar" => "bar"}
40
+ })
41
+ expect(payloads[:subschema2_empty]).to eq({
42
+ "test" => nil, "foo" => {"bar" => "bar"}, "subtest2" => [{"hello" => nil}]
43
+ })
44
+ end
45
+
46
+ it "yields a usefull block that changes the result" do
47
+ payloads = described_class.new(schema).build! do |key, meta, example, skip_word|
48
+ if key == "bar"
49
+ nil
50
+ elsif meta[:type] == :boolean
51
+ true
52
+ elsif key == "subtest1"
53
+ skip_word # this key value pair will be ommited
54
+ else
55
+ example # return suggested value
56
+ end
57
+ end
58
+
59
+ expect(payloads.keys.sort).to eq([:barschema, :fooschema, :subschema1, :subschema2_deep_schema, :subschema2_empty])
60
+ expect(payloads[:barschema]).to eq({"test" => nil, "foo" => {"bar" => nil, "barfield" => true}}) # barfield is change to true and bar is nil
61
+ expect(payloads[:fooschema]).to eq({"test" => nil, "foo" => {"bar" => nil}}) # bar is nil
62
+ expect(payloads[:subschema1]).to eq({"test" => nil, "foo" => {"bar" => nil}}) # subtest is missing, bar is nil
63
+ expect(payloads[:subschema2_deep_schema]).to eq({
64
+ "subtest2" => [{"deep_field" => true, "hello" => nil}], "test" => nil, "foo" => {"bar" => nil}
65
+ })
66
+ expect(payloads[:subschema2_empty]).to eq({
67
+ "test" => nil, "foo" => {"bar" => nil}, "subtest2" => [{"hello" => nil}]
68
+ })
69
+ end
70
+ end
@@ -0,0 +1,248 @@
1
+ require 'spec_helper'
2
+
3
+ describe Paradocs::Extensions::Structure do
4
+ Paradocs.policy :policy_with_error do
5
+ register_error ArgumentError
6
+
7
+ validate do |*|
8
+ raise ArgumentError
9
+ end
10
+ end
11
+
12
+ Paradocs.policy :policy_with_silent_error do
13
+ register_silent_error RuntimeError
14
+ end
15
+
16
+ let(:schema) do
17
+ Paradocs::Schema.new do
18
+ subschema(:highest_level) { field(:test).present } # no mutations on this level -> subschema ignored
19
+
20
+ field(:data).type(:object).present.schema do
21
+ field(:id).type(:integer).present.policy(:policy_with_error)
22
+ field(:name).type(:string).meta(label: "very important staff").description("Example description").example("John")
23
+ field(:role).type(:string).declared.options(["admin", "user"]).default("user").mutates_schema! do |*|
24
+ :test_subschema
25
+ end
26
+ field(:extra).type(:array).required.schema do
27
+ field(:extra).declared.default(false).policy(:policy_with_silent_error)
28
+ end
29
+
30
+ mutation_by!(:name) { :subschema }
31
+
32
+ subschema(:subschema) do
33
+ field(:test_field).present
34
+ end
35
+ subschema(:test_subschema) do
36
+ field(:test1).present
37
+ end
38
+ end
39
+ end
40
+ end
41
+
42
+ describe "#nested" do
43
+ it "generates nested data for documentation generation" do
44
+ result = schema.structure.nested { |k, meta| meta[:block_works] = true unless meta[:present] }
45
+ expect(result[:_subschemes]).to eq({})
46
+ expect(result[:_errors]).to eq([ArgumentError])
47
+ data_structure = result[:data].delete(:structure)
48
+ expect(result[:data]).to eq({
49
+ type: :object,
50
+ required: true,
51
+ present: true,
52
+ json_path: "$.data",
53
+ nested_name: "data",
54
+ })
55
+ expect(data_structure[:_subschemes]).to eq({
56
+ test_subschema: {
57
+ _errors: [],
58
+ _subschemes: {},
59
+ test1: {required: true, present: true, json_path: "$.data.test1", nested_name: "data.test1"}
60
+ },
61
+ subschema: {
62
+ _errors: [],
63
+ _subschemes: {},
64
+ test_field: {required: true, present: true, json_path: "$.data.test_field", nested_name: "data.test_field"}
65
+ }
66
+ })
67
+ expect(data_structure[:id]).to eq({
68
+ type: :integer,
69
+ required: true,
70
+ present: true,
71
+ policy_with_error: {errors: [ArgumentError]},
72
+ json_path: "$.data.id",
73
+ nested_name: "data.id"
74
+ })
75
+ expect(data_structure[:name]).to eq({
76
+ type: :string,
77
+ label: "very important staff",
78
+ description: "Example description",
79
+ example: "John",
80
+ mutates_schema: true,
81
+ block_works: true,
82
+ json_path: "$.data.name",
83
+ nested_name: "data.name"
84
+ })
85
+ expect(data_structure[:role]).to eq({
86
+ type: :string,
87
+ options: ["admin", "user"],
88
+ default: "user",
89
+ mutates_schema: true,
90
+ block_works: true,
91
+ json_path: "$.data.role",
92
+ nested_name: "data.role"
93
+ })
94
+ expect(data_structure[:extra]).to eq({
95
+ type: :array,
96
+ required: true,
97
+ block_works: true,
98
+ json_path: "$.data.extra[]",
99
+ nested_name: "data.extra",
100
+ structure: {
101
+ extra: {
102
+ default: false,
103
+ block_works: true,
104
+ json_path: "$.data.extra[].extra",
105
+ nested_name: "data.extra.extra",
106
+ policy_with_silent_error: {errors: []}
107
+ },
108
+ _subschemes: {}
109
+ }
110
+ })
111
+ end
112
+ end
113
+
114
+ describe "#flatten" do
115
+ it "generates flatten data for documentation generation" do
116
+ expect(schema.structure.flatten { |key, meta| meta[:block_works] = true if key.split(".").size == 1 }).to eq({
117
+ "data" => {
118
+ type: :object,
119
+ required: true,
120
+ present: true,
121
+ block_works: true,
122
+ json_path: "$.data"
123
+ },
124
+ "data.extra" => {
125
+ type: :array,
126
+ required: true,
127
+ json_path: "$.data.extra[]"
128
+ },
129
+ "data.extra.extra" => {
130
+ default: false,
131
+ json_path: "$.data.extra[].extra",
132
+ policy_with_silent_error: {errors: []}
133
+ },
134
+ "data.id" => {
135
+ type: :integer,
136
+ required: true,
137
+ present: true,
138
+ json_path: "$.data.id",
139
+ policy_with_error: {errors: [ArgumentError]}
140
+ },
141
+ "data.name" => {
142
+ type: :string,
143
+ json_path: "$.data.name",
144
+ label: "very important staff",
145
+ description: "Example description",
146
+ example: "John",
147
+ mutates_schema: true
148
+ },
149
+ "data.role" => {
150
+ type: :string,
151
+ options: ["admin", "user"],
152
+ default: "user",
153
+ json_path: "$.data.role",
154
+ mutates_schema: true
155
+ },
156
+ _errors: [ArgumentError],
157
+ _subschemes: {
158
+ test_subschema: {
159
+ _errors: [],
160
+ _subschemes: {},
161
+ "data.test1"=>{
162
+ required: true,
163
+ present: true,
164
+ json_path: "$.data.test1"
165
+ }
166
+ },
167
+ subschema: {
168
+ _errors: [],
169
+ _subschemes: {},
170
+ "data.test_field" => {required: true, present: true, json_path: "$.data.test_field"}
171
+ }
172
+ }
173
+ })
174
+ end
175
+ end
176
+
177
+ describe "#all_flatten" do
178
+ it "generates N structures, where N = number of unique combinations of applied subschemas" do
179
+ expect(schema.structure.all_flatten).to eq({
180
+ subschema: {
181
+ _errors: [ArgumentError],
182
+ "data" => {type: :object, required: true, present: true, json_path: "$.data"},
183
+ "data.id" => {type: :integer, required: true, present: true, policy_with_error: {errors: [ArgumentError]}, json_path: "$.data.id"},
184
+ "data.name" => {type: :string, label: "very important staff", json_path: "$.data.name", mutates_schema: true, description: "Example description", example: "John"},
185
+ "data.role" => {type: :string, options: ["admin", "user"], default: "user", json_path: "$.data.role", mutates_schema: true},
186
+ "data.extra" => {type: :array, required: true, json_path: "$.data.extra[]"},
187
+ "data.extra.extra" => {default: false, policy_with_silent_error: {errors: []}, json_path: "$.data.extra[].extra"},
188
+ "data.test_field" => {required: true, present: true, json_path: "$.data.test_field"}
189
+ },
190
+ test_subschema: {
191
+ _errors: [ArgumentError],
192
+ "data" => {type: :object, required: true, present: true, json_path: "$.data"},
193
+ "data.id" => {type: :integer, required: true, present: true, policy_with_error: {errors: [ArgumentError]}, json_path: "$.data.id"},
194
+ "data.name" => {type: :string, label: "very important staff", json_path: "$.data.name", mutates_schema: true, description: "Example description", example: "John"},
195
+ "data.role" => {type: :string, options: ["admin", "user"], default: "user", json_path: "$.data.role", mutates_schema: true},
196
+ "data.extra" => {type: :array, required: true, json_path: "$.data.extra[]"},
197
+ "data.extra.extra" => {default: false, policy_with_silent_error: {errors: []}, json_path: "$.data.extra[].extra"},
198
+ "data.test1" => {required: true, present: true, json_path: "$.data.test1"}
199
+ }
200
+ })
201
+ end
202
+ end
203
+
204
+ describe "#all_nested" do
205
+ it "generates N structures, where N = number of unique combinations of applied subschemas" do
206
+ result = schema.structure.all_nested
207
+ expect(result[:subschema]).to eq({
208
+ _errors: [ArgumentError],
209
+ "data" => {
210
+ type: :object,
211
+ required: true,
212
+ present: true,
213
+ json_path: "$.data",
214
+ nested_name: "data",
215
+ structure: {
216
+ "role" => {type: :string, options: ["admin", "user"], default: "user", json_path: "$.data.role", nested_name: "data.role", mutates_schema: true},
217
+ "test_field" => {required: true, present: true, json_path: "$.data.test_field", nested_name: "data.test_field"},
218
+ "id" => {type: :integer, required: true, present: true, policy_with_error: {errors: [ArgumentError]}, json_path: "$.data.id", nested_name: "data.id"},
219
+ "name" => {type: :string, label: "very important staff", json_path: "$.data.name", mutates_schema: true, nested_name: "data.name", description: "Example description", example: "John"},
220
+ "extra" => {
221
+ type: :array, required: true, json_path: "$.data.extra[]", nested_name: "data.extra",
222
+ structure: {"extra" => {default: false, policy_with_silent_error: {errors: []}, json_path: "$.data.extra[].extra", nested_name: "data.extra.extra"}}
223
+ }
224
+ }
225
+ }
226
+ })
227
+ expect(result[:test_subschema]).to eq({
228
+ _errors: [ArgumentError],
229
+ "data" => {
230
+ type: :object,
231
+ required: true,
232
+ present: true,
233
+ json_path: "$.data",
234
+ nested_name: "data",
235
+ structure: {
236
+ "role" => {type: :string, options: ["admin", "user"], default: "user", json_path: "$.data.role", nested_name: "data.role", mutates_schema: true},
237
+ "test1" => {required: true, present: true, json_path: "$.data.test1", nested_name: "data.test1"},
238
+ "id" => {type: :integer, required: true, present: true, policy_with_error: {errors: [ArgumentError]}, json_path: "$.data.id", nested_name: "data.id"},
239
+ "name" => {type: :string, label: "very important staff", json_path: "$.data.name", mutates_schema: true, nested_name: "data.name", description: "Example description", example: "John"},
240
+ "extra" => {
241
+ type: :array, required: true, json_path: "$.data.extra[]", nested_name: "data.extra",
242
+ structure: {"extra" => {default: false, policy_with_silent_error: {errors: []}, json_path: "$.data.extra[].extra", nested_name: "data.extra.extra"}}}
243
+ }
244
+ }
245
+ })
246
+ end
247
+ end
248
+ end
@@ -1,7 +1,7 @@
1
1
  require "spec_helper"
2
2
 
3
3
  describe Paradocs::Field do
4
- let(:context) { Paradocs::Context.new }
4
+ let(:context) { Paradocs::Context.new }
5
5
 
6
6
  subject { described_class.new(:a_key) }
7
7
 
@@ -32,7 +32,7 @@ describe Paradocs::Schema do
32
32
 
33
33
  describe "#structure" do
34
34
  it "represents data structure and meta data" do
35
- sc = subject.structure
35
+ sc = subject.structure.nested
36
36
  expect(sc[:title][:present]).to be true
37
37
  expect(sc[:title][:type]).to eq :string
38
38
  expect(sc[:price][:type]).to eq :integer
@@ -71,16 +71,16 @@ describe "schemes with subschemes" do
71
71
  result = schema.resolve({error: :here})
72
72
  expect(result.errors).to eq({"$.fail_field"=>["is required"]})
73
73
  expect(result.output).to eq({error: :here, fail_field: nil})
74
- expect(schema.structure).to eq(structure)
75
- expect(schema.structure(ignore_transparent: false)).to eq(structure.merge(
74
+ expect(schema.structure.nested).to eq(structure)
75
+ expect(schema.structure(ignore_transparent: false).nested).to eq(structure.merge(
76
76
  error: {transparent: true, mutates_schema: true, json_path: "$.error", nested_name: "error"}
77
77
  ))
78
78
 
79
79
  result = schema.resolve({})
80
80
  expect(result.errors).to eq({"$.success_field"=>["is required"]})
81
81
  expect(result.output).to eq({success_field: nil})
82
- expect(schema.structure).to eq(structure)
83
- expect(schema.structure(ignore_transparent: false)).to eq(structure.merge(
82
+ expect(schema.structure.nested).to eq(structure)
83
+ expect(schema.structure(ignore_transparent: false).nested).to eq(structure.merge(
84
84
  error: {transparent: true, mutates_schema: true, json_path: "$.error", nested_name: "error"}
85
85
  ))
86
86
  end
@@ -93,5 +93,38 @@ describe "classes including Whitelist module" do
93
93
  }
94
94
  )
95
95
  end
96
+
97
+ context "when Paradocs.config.whitelist_coercion block is set" do
98
+ before { Paradocs.config.whitelist_coercion = Proc.new { |value, meta| meta[:type] != :string ? "FILTER" : value.to_s }}
99
+
100
+ it "executes block for each value" do
101
+ whitelisted = TestWhitelist.new.filter!(input, schema)
102
+ expect(whitelisted).to eq(
103
+ {
104
+ unexpected: "[FILTERED]",
105
+ from_config: "FILTER",
106
+ data: [
107
+ {
108
+ id: "5",
109
+ name: "[EMPTY]",
110
+ unexpected: "[EMPTY]",
111
+ empty_array: [],
112
+ subschema_1: "FILTER",
113
+ subfield_1: "FILTER",
114
+ subschema_2: "[FILTERED]",
115
+ subfield_2: "FILTER",
116
+ empty_hash: {},
117
+ extra: {
118
+ id: "6",
119
+ name: "[FILTERED]",
120
+ unexpected: "[FILTERED]",
121
+ empty_string: "[EMPTY]"
122
+ }
123
+ }
124
+ ]
125
+ }
126
+ )
127
+ end
128
+ end
96
129
  end
97
130
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: paradocs
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.0.23
4
+ version: 1.1.3
5
5
  platform: ruby
6
6
  authors:
7
7
  - Maxim Tkachenko
@@ -9,7 +9,7 @@ authors:
9
9
  autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2020-09-01 00:00:00.000000000 Z
12
+ date: 2020-10-06 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: bundler
@@ -29,16 +29,16 @@ dependencies:
29
29
  name: rake
30
30
  requirement: !ruby/object:Gem::Requirement
31
31
  requirements:
32
- - - ">="
32
+ - - "~>"
33
33
  - !ruby/object:Gem::Version
34
- version: 12.3.3
34
+ version: '12.3'
35
35
  type: :development
36
36
  prerelease: false
37
37
  version_requirements: !ruby/object:Gem::Requirement
38
38
  requirements:
39
- - - ">="
39
+ - - "~>"
40
40
  - !ruby/object:Gem::Version
41
- version: 12.3.3
41
+ version: '12.3'
42
42
  - !ruby/object:Gem::Dependency
43
43
  name: rspec
44
44
  requirement: !ruby/object:Gem::Requirement
@@ -80,6 +80,7 @@ files:
80
80
  - ".github/workflows/spec.yml"
81
81
  - ".gitignore"
82
82
  - ".gitlab-ci.yml"
83
+ - ".readthedocs.yml"
83
84
  - ".rspec"
84
85
  - ".travis.yml"
85
86
  - Gemfile
@@ -87,12 +88,24 @@ files:
87
88
  - README.md
88
89
  - Rakefile
89
90
  - bin/console
91
+ - docs/changelog.md
92
+ - docs/custom_configuration.md
93
+ - docs/documentation_generation.md
94
+ - docs/faq.md
95
+ - docs/form_objects_dsl.md
96
+ - docs/index.md
97
+ - docs/payload_builder.md
98
+ - docs/policies.md
99
+ - docs/schema.md
100
+ - docs/struct.md
101
+ - docs/subschema.md
90
102
  - lib/paradocs.rb
91
103
  - lib/paradocs/base_policy.rb
92
104
  - lib/paradocs/context.rb
93
105
  - lib/paradocs/default_types.rb
94
106
  - lib/paradocs/dsl.rb
95
- - lib/paradocs/extensions/insides.rb
107
+ - lib/paradocs/extensions/payload_builder.rb
108
+ - lib/paradocs/extensions/structure.rb
96
109
  - lib/paradocs/field.rb
97
110
  - lib/paradocs/field_dsl.rb
98
111
  - lib/paradocs/policies.rb
@@ -103,23 +116,26 @@ files:
103
116
  - lib/paradocs/support.rb
104
117
  - lib/paradocs/version.rb
105
118
  - lib/paradocs/whitelist.rb
119
+ - mkdocs.yml
106
120
  - paradocs.gemspec
121
+ - requirements.txt
107
122
  - spec/custom_block_validator_spec.rb
108
123
  - spec/custom_validator.rb
109
124
  - spec/dsl_spec.rb
110
125
  - spec/expand_spec.rb
126
+ - spec/extensions/payload_builder_spec.rb
127
+ - spec/extensions/structures_spec.rb
111
128
  - spec/field_spec.rb
112
129
  - spec/helpers.rb
113
130
  - spec/policies_spec.rb
114
131
  - spec/schema_spec.rb
115
- - spec/schema_structures_spec.rb
116
132
  - spec/schema_walk_spec.rb
117
133
  - spec/spec_helper.rb
118
134
  - spec/struct_spec.rb
119
135
  - spec/subschema_spec.rb
120
136
  - spec/validators_spec.rb
121
137
  - spec/whitelist_spec.rb
122
- homepage: https://github.com/mtkachenk0/paradocs
138
+ homepage: https://paradocs.readthedocs.io/en/latest
123
139
  licenses:
124
140
  - MIT
125
141
  metadata: {}
@@ -149,11 +165,12 @@ test_files:
149
165
  - spec/custom_validator.rb
150
166
  - spec/dsl_spec.rb
151
167
  - spec/expand_spec.rb
168
+ - spec/extensions/payload_builder_spec.rb
169
+ - spec/extensions/structures_spec.rb
152
170
  - spec/field_spec.rb
153
171
  - spec/helpers.rb
154
172
  - spec/policies_spec.rb
155
173
  - spec/schema_spec.rb
156
- - spec/schema_structures_spec.rb
157
174
  - spec/schema_walk_spec.rb
158
175
  - spec/spec_helper.rb
159
176
  - spec/struct_spec.rb