From b315e75bf14e3bba49b93a84312a811f113f9cdc Mon Sep 17 00:00:00 2001 From: Paul Martensen Date: Tue, 5 Mar 2019 10:38:53 +0100 Subject: [PATCH 1/8] Extended example to hold another object in its anyOf that has null: true. Added that object to the specifications. --- spec/integration/builder_initialization_spec.rb | 15 +++++++++++++++ spec/integration/builder_reopening_spec.rb | 15 +++++++++++++++ spec/support/examples/builder_initialization.rb | 7 +++++++ 3 files changed, 37 insertions(+) diff --git a/spec/integration/builder_initialization_spec.rb b/spec/integration/builder_initialization_spec.rb index 774576b..dfb6ba6 100644 --- a/spec/integration/builder_initialization_spec.rb +++ b/spec/integration/builder_initialization_spec.rb @@ -17,6 +17,21 @@ } } }, + target: { + anyOf: [ + { + type: :object, + properties: { + id: { + type: :number + } + } + }, + { + type: :null + } + ] + }, preferences: { anyOf: [ { diff --git a/spec/integration/builder_reopening_spec.rb b/spec/integration/builder_reopening_spec.rb index f19653d..17c3d31 100644 --- a/spec/integration/builder_reopening_spec.rb +++ b/spec/integration/builder_reopening_spec.rb @@ -20,6 +20,21 @@ } } }, + target: { + anyOf: [ + { + type: :object, + properties: { + id: { + type: :number + } + } + }, + { + type: :null + } + ] + }, preferences: { anyOf: [ { diff --git a/spec/support/examples/builder_initialization.rb b/spec/support/examples/builder_initialization.rb index 5173a8e..9c84d4a 100644 --- a/spec/support/examples/builder_initialization.rb +++ b/spec/support/examples/builder_initialization.rb @@ -6,11 +6,18 @@ def example obj = object obj.string :name settings_for(obj) + target_for(obj) preferences_for(obj) add_ids_to(obj) obj end + def target_for(obj) + target = obj.object :target, null: true + target.number :id + target + end + def settings_for(obj) settings = obj.object :settings settings.string :email From 9deb7cf7f1ef7cc71ad65ec6edefb1be2922e29e Mon Sep 17 00:00:00 2001 From: Paul Martensen Date: Tue, 5 Mar 2019 10:41:31 +0100 Subject: [PATCH 2/8] Moved the adding of null option from extract_types to initialization to avoid endless recursion. Also added a horrible, disgusting hack to extend the initial object option (option as in possible anyOf value) by newly added attributes. This does look disguting and is probably unstable for a lot of cases. --- lib/json/schema_builder/entity.rb | 16 ++++++++++------ 1 file changed, 10 insertions(+), 6 deletions(-) diff --git a/lib/json/schema_builder/entity.rb b/lib/json/schema_builder/entity.rb index b8ac043..ea05180 100644 --- a/lib/json/schema_builder/entity.rb +++ b/lib/json/schema_builder/entity.rb @@ -43,6 +43,7 @@ def initialize(name, opts = { }, &block) initialize_parent_with opts initialize_with opts eval_block &block + any_of(null) if @nullable extract_types @initialized = true end @@ -138,13 +139,16 @@ def _reset_fragments end def extract_types - any_of(null) if @nullable - if any_of.present? - everything_else = schema.data.reject { |k, v| k == "anyOf" } - return unless everything_else.present? - schema.data.select! { |k, v| k == "anyOf" } - schema.data["anyOf"].unshift everything_else + return unless any_of.present? + everything_else = schema.data.reject { |k, v| k == "anyOf" } + return unless everything_else.present? + schema.data.select! { |k, v| k == "anyOf" } + if initial = schema.data['anyOf'].find { |opt| opt.as_json.try(:[], 'type') == 'object' } + initial.deep_merge! everything_else.as_json + else + schema.data["anyOf"].unshift everything_else end + schema.data["anyOf"].uniq! end def initialize_parent_with(opts) From 850f9393471faf68020192373c6b0d27f6056dad Mon Sep 17 00:00:00 2001 From: Paul Martensen Date: Tue, 5 Mar 2019 11:56:02 +0100 Subject: [PATCH 3/8] Cleaned up Entity#extract_types a bit --- lib/json/schema_builder/entity.rb | 23 ++++++++++++++--------- 1 file changed, 14 insertions(+), 9 deletions(-) diff --git a/lib/json/schema_builder/entity.rb b/lib/json/schema_builder/entity.rb index ea05180..a31c3b6 100644 --- a/lib/json/schema_builder/entity.rb +++ b/lib/json/schema_builder/entity.rb @@ -139,16 +139,21 @@ def _reset_fragments end def extract_types - return unless any_of.present? - everything_else = schema.data.reject { |k, v| k == "anyOf" } + build_any_of if any_of.present? + end + + def build_any_of + initial_object = any_of_options.find { |opt| opt.as_json['type'] == 'object' } + everything_else = schema.data.reject { |k| k == "anyOf" } return unless everything_else.present? - schema.data.select! { |k, v| k == "anyOf" } - if initial = schema.data['anyOf'].find { |opt| opt.as_json.try(:[], 'type') == 'object' } - initial.deep_merge! everything_else.as_json - else - schema.data["anyOf"].unshift everything_else - end - schema.data["anyOf"].uniq! + + schema.data.keep_if { |k| k == "anyOf" } + return any_of_options.unshift(everything_else) unless initial_object + initial_object.deep_merge! everything_else.as_json + end + + def any_of_options + schema.data["anyOf"] end def initialize_parent_with(opts) From c2889a11e80f00a520855b64a1d8af061ff2bc58 Mon Sep 17 00:00:00 2001 From: Paul Martensen Date: Tue, 5 Mar 2019 13:22:03 +0100 Subject: [PATCH 4/8] Added expand null spec --- spec/integration/expand_null_spec.rb | 39 +++++++++++++++++++++++ spec/support/examples/expand_null_true.rb | 13 ++++++++ 2 files changed, 52 insertions(+) create mode 100644 spec/integration/expand_null_spec.rb create mode 100644 spec/support/examples/expand_null_true.rb diff --git a/spec/integration/expand_null_spec.rb b/spec/integration/expand_null_spec.rb new file mode 100644 index 0000000..c58383d --- /dev/null +++ b/spec/integration/expand_null_spec.rb @@ -0,0 +1,39 @@ +require 'spec_helper' + +RSpec.describe Examples::ExpandNullTrue, type: :integration do + it_behaves_like 'a builder' do + let(:expected_json) do + { + "type": "object", + "required": [ + "foo" + ], + "properties": { + "foo": { + "type": "object", + "required": [ + "bar" + ], + "properties": { + "bar": { + "anyOf": [ + { + "type": "object", + "properties": { + "baz": { + "type": "string" + } + } + }, + { + "type": "null" + } + ] + } + } + } + } + } + end + end +end diff --git a/spec/support/examples/expand_null_true.rb b/spec/support/examples/expand_null_true.rb new file mode 100644 index 0000000..21f62c5 --- /dev/null +++ b/spec/support/examples/expand_null_true.rb @@ -0,0 +1,13 @@ +module Examples + class ExpandNullTrue + include JSON::SchemaBuilder + + def example + object.tap do |base_obj| + foo_obj = base_obj.object(:foo, required: true) + bar_obj = foo_obj.object(:bar, null: true, required: true) + bar_obj.string :baz + end + end + end +end From b0350603011b70f230dd2b321af48c15a42743f0 Mon Sep 17 00:00:00 2001 From: Paul Martensen Date: Wed, 6 Mar 2019 10:31:51 +0100 Subject: [PATCH 5/8] Removed as_json as its not necessary here. --- lib/json/schema_builder/entity.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/json/schema_builder/entity.rb b/lib/json/schema_builder/entity.rb index a31c3b6..4cc07cb 100644 --- a/lib/json/schema_builder/entity.rb +++ b/lib/json/schema_builder/entity.rb @@ -149,7 +149,7 @@ def build_any_of schema.data.keep_if { |k| k == "anyOf" } return any_of_options.unshift(everything_else) unless initial_object - initial_object.deep_merge! everything_else.as_json + initial_object.deep_merge! everything_else end def any_of_options From 5954a26ffc2a5775cf8c227642fe623febc5956a Mon Sep 17 00:00:00 2001 From: Paul Martensen Date: Wed, 6 Mar 2019 10:32:20 +0100 Subject: [PATCH 6/8] Added building of any_of options to initialize children so that the options there are merged into the any_of for nullables. --- lib/json/schema_builder/object.rb | 1 + 1 file changed, 1 insertion(+) diff --git a/lib/json/schema_builder/object.rb b/lib/json/schema_builder/object.rb index b9801f4..50607ae 100644 --- a/lib/json/schema_builder/object.rb +++ b/lib/json/schema_builder/object.rb @@ -23,6 +23,7 @@ def initialize_children self.properties[child.name] = child.as_json end end + build_any_of if @nullable end def extract_types From 14bdad24648373aaa1dc62e25200b71d9e20025e Mon Sep 17 00:00:00 2001 From: Paul Martensen Date: Fri, 29 Mar 2019 16:34:10 +0100 Subject: [PATCH 7/8] Added buggy example. --- spec/integration/nested_nullable_spec.rb | 35 ++++++++++++++++++++++++ spec/support/examples/nested_nullable.rb | 12 ++++++++ 2 files changed, 47 insertions(+) create mode 100644 spec/integration/nested_nullable_spec.rb create mode 100644 spec/support/examples/nested_nullable.rb diff --git a/spec/integration/nested_nullable_spec.rb b/spec/integration/nested_nullable_spec.rb new file mode 100644 index 0000000..39daf15 --- /dev/null +++ b/spec/integration/nested_nullable_spec.rb @@ -0,0 +1,35 @@ +require 'spec_helper' + +RSpec.describe Examples::NestedNullable, type: :integration do + it_behaves_like 'a builder' do + let(:expected_json) do + { + type: 'object', + properties: { + nullable_object: { + anyOf: [ + { + type: 'object', + properties: { + nullable_string: { + anyOf: [ + { + type: 'string' + }, + { + type: 'null' + } + ] + } + } + }, + { + type: 'null' + } + ] + } + } + } + end + end +end diff --git a/spec/support/examples/nested_nullable.rb b/spec/support/examples/nested_nullable.rb new file mode 100644 index 0000000..9871f50 --- /dev/null +++ b/spec/support/examples/nested_nullable.rb @@ -0,0 +1,12 @@ +module Examples + class NestedNullable + include JSON::SchemaBuilder + + def example + object.tap do |base_obj| + obj = base_obj.object :nullable_object, null: true + obj.string :nullable_string, null: true + end + end + end +end From d7fe1e61c2edb14e2c1b0ff750a8a1c9d5cfdee7 Mon Sep 17 00:00:00 2001 From: Paul Martensen Date: Mon, 1 Apr 2019 10:29:23 +0200 Subject: [PATCH 8/8] Added horrible hack to fix the issue. --- lib/json/schema_builder/entity.rb | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/lib/json/schema_builder/entity.rb b/lib/json/schema_builder/entity.rb index 4cc07cb..939db4b 100644 --- a/lib/json/schema_builder/entity.rb +++ b/lib/json/schema_builder/entity.rb @@ -144,12 +144,13 @@ def extract_types def build_any_of initial_object = any_of_options.find { |opt| opt.as_json['type'] == 'object' } - everything_else = schema.data.reject { |k| k == "anyOf" } + everything_else = schema.data.except("anyOf") return unless everything_else.present? schema.data.keep_if { |k| k == "anyOf" } return any_of_options.unshift(everything_else) unless initial_object initial_object.deep_merge! everything_else + initial_object['properties'] = children.select { |c| c.name.presence }.map { |c| [c.name, c] }.to_h end def any_of_options