paradocs 1.0.24 → 1.1.4

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,45 @@
1
+ module Paradocs
2
+ module Extensions
3
+ class PayloadBuilder
4
+ attr_reader :structure, :result
5
+ attr_accessor :skip_word
6
+ def initialize(schema, skip_word: :skip)
7
+ @structure = schema.structure
8
+ @skip_word = skip_word
9
+ end
10
+
11
+ def build!(sort_by_schema: false, &block)
12
+ result = structure.all_nested.map { |name, struct| [name, build_simple_structure(struct, &block)] }.to_h
13
+ sort_by_schema ? schema.resolve(result).output : result
14
+ end
15
+
16
+ private
17
+
18
+ def build_simple_structure(struct, &block)
19
+ struct.map do |key, value|
20
+ key = key.to_s
21
+ next if key.start_with?(Paradocs.config.meta_prefix) # skip all the meta fields
22
+ ex_value = restore_one(key, value, &block)
23
+ next if ex_value == @skip_word
24
+ key = value[:alias] || key
25
+ [key.to_s, ex_value]
26
+ end.compact.to_h
27
+ end
28
+
29
+ def restore_one(key, value, &block)
30
+ default = value[:default]
31
+ ex_value = if value[:structure]
32
+ data = build_simple_structure(value[:structure], &block)
33
+ value[:type] == :array ? [data] : data
34
+ elsif default
35
+ default.is_a?(Proc) ? default.call : default
36
+ elsif value[:options] && !value[:options].empty?
37
+ options = value[:options]
38
+ value[:type] == :array ? options : options.sample
39
+ end
40
+ return ex_value unless block_given?
41
+ yield(key, value, ex_value, @skip_word)
42
+ end
43
+ end
44
+ end
45
+ end
@@ -0,0 +1,119 @@
1
+ module Paradocs
2
+ module Extensions
3
+ class Structure
4
+ DEFAULT = :generic
5
+ %w(errors subschemes).each do |key|
6
+ define_method(key) { "#{Paradocs.config.meta_prefix}#{key}".to_sym }
7
+ end
8
+
9
+ attr_reader :schema, :ignore_transparent, :root
10
+ attr_accessor :ignore_transparent
11
+ def initialize(schema, ignore_transparent=true, root="")
12
+ @schema = schema
13
+ @ignore_transparent = ignore_transparent
14
+ @root = root
15
+ end
16
+
17
+ def nested(&block)
18
+ schema.fields.each_with_object({errors => [], subschemes => {}}) do |(_, field), result|
19
+ meta, sc = collect_meta(field, root)
20
+ if sc
21
+ meta[:structure] = self.class.new(sc, ignore_transparent, meta[:json_path]).nested(&block)
22
+ result[errors] += meta[:structure].delete(errors)
23
+ else
24
+ result[errors] += field.possible_errors
25
+ end
26
+
27
+ field_key = field.meta_data[:alias] || field.key
28
+ result[field_key] = meta unless ignore_transparent && field.transparent?
29
+ yield(field_key, meta) if block_given?
30
+
31
+ next unless field.mutates_schema?
32
+ schema.subschemes.each do |name, subschema|
33
+ result[subschemes][name] = self.class.new(subschema, ignore_transparent, root).nested(&block)
34
+ result[errors] += result[subschemes][name][errors]
35
+ end
36
+ end
37
+ end
38
+
39
+ def all_nested(&block)
40
+ all_flatten(&block).each_with_object({}) do |(name, struct), obj|
41
+ obj[name] = {}
42
+ # sort the flatten struct to have iterated 1lvl keys before 2lvl and so on...
43
+ struct.sort_by { |k, v| k.to_s.count(".") }.each do |key, value|
44
+ target = obj[name]
45
+ key, value = key.to_s, value.clone # clone the values, because we do mutation below
46
+ value.merge!(nested_name: key) if value.respond_to?(:merge) # it can be array (_errors)
47
+ next target[key.to_sym] = value if key.start_with?(Paradocs.config.meta_prefix) # copy meta fields
48
+
49
+ parts = key.split(".")
50
+ next target[key] ||= value if parts.size == 1 # copy 1lvl key
51
+ parts.each.with_index do |subkey, index|
52
+ target[subkey] ||= value
53
+ next if parts.size == index + 1
54
+ target[subkey][:structure] ||= {}
55
+ target = target[subkey][:structure] # target goes deeper for each part
56
+ end
57
+ end
58
+ end
59
+ end
60
+
61
+ def all_flatten(schema_structure=nil, &block)
62
+ schema_structure ||= flatten(&block)
63
+ if schema_structure[subschemes].empty?
64
+ schema_structure.delete(subschemes) # don't include redundant key
65
+ return {DEFAULT => schema_structure}
66
+ end
67
+ schema_structure[subschemes].each_with_object({}) do |(name, subschema), result|
68
+ if subschema[subschemes].empty?
69
+ result[name] = schema_structure.merge(subschema)
70
+ result[name][errors] += schema_structure[errors]
71
+ result[name][errors].uniq!
72
+ result[name].delete(subschemes)
73
+ next result[name]
74
+ end
75
+
76
+ all_flatten(subschema).each do |sub_name, schema|
77
+ result["#{name}_#{sub_name}".to_sym] = schema_structure.merge(schema)
78
+ end
79
+ end
80
+ end
81
+
82
+ def flatten(&block)
83
+ schema.fields.each_with_object({errors => [], subschemes => {}}) do |(_, field), obj|
84
+ meta, sc = collect_meta(field, root)
85
+ humanized_name = meta.delete(:nested_name)
86
+ obj[humanized_name] = meta unless ignore_transparent && field.transparent?
87
+
88
+ if sc
89
+ deep_result = self.class.new(sc, ignore_transparent, meta[:json_path]).flatten(&block)
90
+ obj[errors] += deep_result.delete(errors)
91
+ obj[subschemes].merge!(deep_result.delete(subschemes))
92
+ obj.merge!(deep_result)
93
+ else
94
+ obj[errors] += field.possible_errors
95
+ end
96
+ yield(humanized_name, meta) if block_given?
97
+ next unless field.mutates_schema?
98
+ schema.subschemes.each do |name, subschema|
99
+ obj[subschemes][name] ||= self.class.new(subschema, ignore_transparent, root).flatten(&block)
100
+ obj[errors] += obj[subschemes][name][errors]
101
+ end
102
+ end
103
+ end
104
+
105
+ private
106
+
107
+ def collect_meta(field, root)
108
+ field_key = field.meta_data[:alias] || field.key
109
+ json_path = root.empty? ? "$.#{field_key}" : "#{root}.#{field_key}"
110
+ meta = field.meta_data.merge(json_path: json_path)
111
+ sc = meta.delete(:schema)
112
+ meta[:mutates_schema] = true if meta.delete(:mutates_schema)
113
+ json_path << "[]" if meta[:type] == :array
114
+ meta[:nested_name] = json_path.gsub("[]", "")[2..-1]
115
+ [meta, sc]
116
+ end
117
+ end
118
+ end
119
+ end
@@ -32,5 +32,17 @@ module Paradocs
32
32
  def length(opts)
33
33
  policy :length, opts
34
34
  end
35
+
36
+ def description(text)
37
+ meta description: text
38
+ end
39
+
40
+ def as(identifier)
41
+ meta alias: identifier
42
+ end
43
+
44
+ def example(value)
45
+ meta example: value
46
+ end
35
47
  end
36
48
  end
@@ -1,14 +1,13 @@
1
1
  require "paradocs/context"
2
2
  require "paradocs/results"
3
3
  require "paradocs/field"
4
- require "paradocs/extensions/insides"
4
+ require "paradocs/extensions/structure"
5
+ require "paradocs/extensions/payload_builder"
5
6
 
6
7
  module Paradocs
7
8
  class Schema
8
- include Extensions::Insides
9
-
10
9
  attr_accessor :environment
11
- attr_reader :subschemes
10
+ attr_reader :subschemes, :structure_builder
12
11
  def initialize(options={}, &block)
13
12
  @options = options
14
13
  @fields = {}
@@ -18,6 +17,7 @@ module Paradocs
18
17
  @default_field_policies = []
19
18
  @ignored_field_keys = []
20
19
  @expansions = {}
20
+ @structure_builder = Paradocs::Extensions::Structure.new(self)
21
21
  end
22
22
 
23
23
  def schema
@@ -29,6 +29,27 @@ module Paradocs
29
29
  f.mutates_schema!(&block)
30
30
  end
31
31
 
32
+ def structure(ignore_transparent: true)
33
+ flush!
34
+ structure_builder.ignore_transparent = ignore_transparent
35
+ structure_builder
36
+ end
37
+
38
+ def example_payloads(&block)
39
+ @example_payloads ||= Paradocs::Extensions::PayloadBuilder.new(self).build!(&block)
40
+ end
41
+
42
+ def walk(meta_key = nil, &visitor)
43
+ r = visit(meta_key, &visitor)
44
+ Results.new(r, {}, {})
45
+ end
46
+
47
+ def visit(meta_key = nil, &visitor)
48
+ fields.each_with_object({}) do |(_, field), m|
49
+ m[field.key] = field.visit(meta_key, &visitor)
50
+ end
51
+ end
52
+
32
53
  def subschema(*args, &block)
33
54
  options = args.last.is_a?(Hash) ? args.last : {}
34
55
  name = args.first.is_a?(Symbol) ? args.shift : Paradocs.config.default_schema_name
@@ -143,6 +164,11 @@ module Paradocs
143
164
  end
144
165
  end
145
166
 
167
+ def flush!
168
+ @fields = {}
169
+ @applied = false
170
+ end
171
+
146
172
  protected
147
173
 
148
174
  attr_reader :definitions, :options
@@ -155,7 +181,8 @@ module Paradocs
155
181
  invoke_subschemes!(val, context, flds: flds)
156
182
  flds.each_with_object({}) do |(_, field), m|
157
183
  r = field.resolve(val, context.sub(field.key))
158
- m[field.key] = r.value if r.eligible?
184
+ key = field.meta_data[:alias] || field.key
185
+ m[key] = r.value if r.eligible?
159
186
  end
160
187
  end
161
188
 
@@ -205,10 +232,5 @@ module Paradocs
205
232
  end
206
233
  @applied = true
207
234
  end
208
-
209
- def flush!
210
- @fields = {}
211
- @applied = false
212
- end
213
235
  end
214
236
  end
@@ -57,6 +57,7 @@ module Paradocs
57
57
  # this hook is called after schema definition in DSL module
58
58
  def paradocs_after_define_schema(schema)
59
59
  schema.fields.keys.each do |key|
60
+ key = schema.fields[key].meta_data[:alias] || key
60
61
  define_method key do
61
62
  _graph[key]
62
63
  end
@@ -1,3 +1,3 @@
1
1
  module Paradocs
2
- VERSION = "1.0.24"
2
+ VERSION = "1.1.4"
3
3
  end
@@ -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
+
@@ -8,9 +8,9 @@ Gem::Specification.new do |spec|
8
8
  spec.version = Paradocs::VERSION
9
9
  spec.authors = ["Maxim Tkachenko", "Ismael Celis"]
10
10
  spec.email = ["tkachenko.maxim.w@gmail.com", "ismaelct@gmail.com"]
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.}
11
+ spec.description = %q{Flexible DRY validations with API docs generation done right TLDR; parametrics on steroids.}
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).as(:bar_field)
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", "bar_field" => 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, "bar_field" => 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,250 @@
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).as(:user_id)
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[:user_id]).to eq({
68
+ type: :integer,
69
+ required: true,
70
+ present: true,
71
+ policy_with_error: {errors: [ArgumentError]},
72
+ alias: :user_id,
73
+ json_path: "$.data.user_id",
74
+ nested_name: "data.user_id"
75
+ })
76
+ expect(data_structure[:name]).to eq({
77
+ type: :string,
78
+ label: "very important staff",
79
+ description: "Example description",
80
+ example: "John",
81
+ mutates_schema: true,
82
+ block_works: true,
83
+ json_path: "$.data.name",
84
+ nested_name: "data.name"
85
+ })
86
+ expect(data_structure[:role]).to eq({
87
+ type: :string,
88
+ options: ["admin", "user"],
89
+ default: "user",
90
+ mutates_schema: true,
91
+ block_works: true,
92
+ json_path: "$.data.role",
93
+ nested_name: "data.role"
94
+ })
95
+ expect(data_structure[:extra]).to eq({
96
+ type: :array,
97
+ required: true,
98
+ block_works: true,
99
+ json_path: "$.data.extra[]",
100
+ nested_name: "data.extra",
101
+ structure: {
102
+ extra: {
103
+ default: false,
104
+ block_works: true,
105
+ json_path: "$.data.extra[].extra",
106
+ nested_name: "data.extra.extra",
107
+ policy_with_silent_error: {errors: []}
108
+ },
109
+ _subschemes: {}
110
+ }
111
+ })
112
+ end
113
+ end
114
+
115
+ describe "#flatten" do
116
+ it "generates flatten data for documentation generation" do
117
+ expect(schema.structure.flatten { |key, meta| meta[:block_works] = true if key.split(".").size == 1 }).to eq({
118
+ "data" => {
119
+ type: :object,
120
+ required: true,
121
+ present: true,
122
+ block_works: true,
123
+ json_path: "$.data"
124
+ },
125
+ "data.extra" => {
126
+ type: :array,
127
+ required: true,
128
+ json_path: "$.data.extra[]"
129
+ },
130
+ "data.extra.extra" => {
131
+ default: false,
132
+ json_path: "$.data.extra[].extra",
133
+ policy_with_silent_error: {errors: []}
134
+ },
135
+ "data.user_id" => {
136
+ type: :integer,
137
+ required: true,
138
+ present: true,
139
+ alias: :user_id,
140
+ json_path: "$.data.user_id",
141
+ policy_with_error: {errors: [ArgumentError]}
142
+ },
143
+ "data.name" => {
144
+ type: :string,
145
+ json_path: "$.data.name",
146
+ label: "very important staff",
147
+ description: "Example description",
148
+ example: "John",
149
+ mutates_schema: true
150
+ },
151
+ "data.role" => {
152
+ type: :string,
153
+ options: ["admin", "user"],
154
+ default: "user",
155
+ json_path: "$.data.role",
156
+ mutates_schema: true
157
+ },
158
+ _errors: [ArgumentError],
159
+ _subschemes: {
160
+ test_subschema: {
161
+ _errors: [],
162
+ _subschemes: {},
163
+ "data.test1"=>{
164
+ required: true,
165
+ present: true,
166
+ json_path: "$.data.test1"
167
+ }
168
+ },
169
+ subschema: {
170
+ _errors: [],
171
+ _subschemes: {},
172
+ "data.test_field" => {required: true, present: true, json_path: "$.data.test_field"}
173
+ }
174
+ }
175
+ })
176
+ end
177
+ end
178
+
179
+ describe "#all_flatten" do
180
+ it "generates N structures, where N = number of unique combinations of applied subschemas" do
181
+ expect(schema.structure.all_flatten).to eq({
182
+ subschema: {
183
+ _errors: [ArgumentError],
184
+ "data" => {type: :object, required: true, present: true, json_path: "$.data"},
185
+ "data.user_id" => {type: :integer, required: true, present: true, policy_with_error: {errors: [ArgumentError]}, alias: :user_id, json_path: "$.data.user_id"},
186
+ "data.name" => {type: :string, label: "very important staff", json_path: "$.data.name", mutates_schema: true, description: "Example description", example: "John"},
187
+ "data.role" => {type: :string, options: ["admin", "user"], default: "user", json_path: "$.data.role", mutates_schema: true},
188
+ "data.extra" => {type: :array, required: true, json_path: "$.data.extra[]"},
189
+ "data.extra.extra" => {default: false, policy_with_silent_error: {errors: []}, json_path: "$.data.extra[].extra"},
190
+ "data.test_field" => {required: true, present: true, json_path: "$.data.test_field"}
191
+ },
192
+ test_subschema: {
193
+ _errors: [ArgumentError],
194
+ "data" => {type: :object, required: true, present: true, json_path: "$.data"},
195
+ "data.user_id" => {type: :integer, required: true, present: true, policy_with_error: {errors: [ArgumentError]}, alias: :user_id, json_path: "$.data.user_id"},
196
+ "data.name" => {type: :string, label: "very important staff", json_path: "$.data.name", mutates_schema: true, description: "Example description", example: "John"},
197
+ "data.role" => {type: :string, options: ["admin", "user"], default: "user", json_path: "$.data.role", mutates_schema: true},
198
+ "data.extra" => {type: :array, required: true, json_path: "$.data.extra[]"},
199
+ "data.extra.extra" => {default: false, policy_with_silent_error: {errors: []}, json_path: "$.data.extra[].extra"},
200
+ "data.test1" => {required: true, present: true, json_path: "$.data.test1"}
201
+ }
202
+ })
203
+ end
204
+ end
205
+
206
+ describe "#all_nested" do
207
+ it "generates N structures, where N = number of unique combinations of applied subschemas" do
208
+ result = schema.structure.all_nested
209
+ expect(result[:subschema]).to eq({
210
+ _errors: [ArgumentError],
211
+ "data" => {
212
+ type: :object,
213
+ required: true,
214
+ present: true,
215
+ json_path: "$.data",
216
+ nested_name: "data",
217
+ structure: {
218
+ "role" => {type: :string, options: ["admin", "user"], default: "user", json_path: "$.data.role", nested_name: "data.role", mutates_schema: true},
219
+ "test_field" => {required: true, present: true, json_path: "$.data.test_field", nested_name: "data.test_field"},
220
+ "user_id" => {type: :integer, required: true, present: true, policy_with_error: {errors: [ArgumentError]}, alias: :user_id, json_path: "$.data.user_id", nested_name: "data.user_id"},
221
+ "name" => {type: :string, label: "very important staff", json_path: "$.data.name", mutates_schema: true, nested_name: "data.name", description: "Example description", example: "John"},
222
+ "extra" => {
223
+ type: :array, required: true, json_path: "$.data.extra[]", nested_name: "data.extra",
224
+ structure: {"extra" => {default: false, policy_with_silent_error: {errors: []}, json_path: "$.data.extra[].extra", nested_name: "data.extra.extra"}}
225
+ }
226
+ }
227
+ }
228
+ })
229
+ expect(result[:test_subschema]).to eq({
230
+ _errors: [ArgumentError],
231
+ "data" => {
232
+ type: :object,
233
+ required: true,
234
+ present: true,
235
+ json_path: "$.data",
236
+ nested_name: "data",
237
+ structure: {
238
+ "role" => {type: :string, options: ["admin", "user"], default: "user", json_path: "$.data.role", nested_name: "data.role", mutates_schema: true},
239
+ "test1" => {required: true, present: true, json_path: "$.data.test1", nested_name: "data.test1"},
240
+ "user_id" => {type: :integer, required: true, present: true, policy_with_error: {errors: [ArgumentError]}, alias: :user_id, json_path: "$.data.user_id", nested_name: "data.user_id"},
241
+ "name" => {type: :string, label: "very important staff", json_path: "$.data.name", mutates_schema: true, nested_name: "data.name", description: "Example description", example: "John"},
242
+ "extra" => {
243
+ type: :array, required: true, json_path: "$.data.extra[]", nested_name: "data.extra",
244
+ structure: {"extra" => {default: false, policy_with_silent_error: {errors: []}, json_path: "$.data.extra[].extra", nested_name: "data.extra.extra"}}}
245
+ }
246
+ }
247
+ })
248
+ end
249
+ end
250
+ end