diff --git a/lib/microsoft_kiota_serialization_json/json_serialization_writer.rb b/lib/microsoft_kiota_serialization_json/json_serialization_writer.rb index c4aeacb..5856f8c 100644 --- a/lib/microsoft_kiota_serialization_json/json_serialization_writer.rb +++ b/lib/microsoft_kiota_serialization_json/json_serialization_writer.rb @@ -147,29 +147,19 @@ def write_collection_of_primitive_values(key, values) def write_collection_of_object_values(key, values) if values if !key - return values.map do |v| - self.write_object_value(nil, v) - end - end - @writer[key] = values.map do |v| - self.write_object_value(nil, v).writer + return values.map { |v| write_object_value(nil, v) } end + @writer[key] = values.map { |v| object_value_hash(v) } end end - def write_object_value(key, value) + def write_object_value(key, value, *additional_values_to_merge) if value - if !key - temp = JsonSerializationWriter.new() - value.serialize(temp) - return temp - end - begin - temp = JsonSerializationWriter.new() - value.serialize(temp) - @writer[key] = temp.writer - rescue StandardError => e - raise e.class, "no key or value included in write_boolean_value(key, value)" + if key + @writer[key] = object_value_hash(value, *additional_values_to_merge) + else + value.serialize(self) + additional_values_to_merge.each { |v| v.serialize(self) unless v.nil? } end end end @@ -191,6 +181,17 @@ def write_additional_data(value) end end + private + + def object_value_hash(value, *additional_values_to_merge) + temp = JsonSerializationWriter.new + value.serialize(temp) + additional_values_to_merge.each { |v| v.serialize(temp) unless v.nil? } + temp.writer + end + + public + def write_any_value(key, value) if value if !!value == value diff --git a/microsoft_kiota_serialization_json.gemspec b/microsoft_kiota_serialization_json.gemspec index 5202cbf..27363e0 100644 --- a/microsoft_kiota_serialization_json.gemspec +++ b/microsoft_kiota_serialization_json.gemspec @@ -30,7 +30,7 @@ Gem::Specification.new do |spec| spec.executables = spec.files.grep(%r{\Aexe/}) { |f| File.basename(f) } spec.require_paths = ['lib'] - spec.add_runtime_dependency 'microsoft_kiota_abstractions', '>= 0.14', '< 0.16' + spec.add_runtime_dependency 'microsoft_kiota_abstractions', '>= 0.16', '< 0.18' spec.add_runtime_dependency 'uuidtools', '>= 2.2', '< 3.1' spec.add_runtime_dependency 'json', '>= 2.6.3', '< 2.22.0' spec.add_development_dependency 'rake', '~> 13.0' diff --git a/spec/json_serialization_writer_composed_type_spec.rb b/spec/json_serialization_writer_composed_type_spec.rb new file mode 100644 index 0000000..9bffd43 --- /dev/null +++ b/spec/json_serialization_writer_composed_type_spec.rb @@ -0,0 +1,272 @@ +# frozen_string_literal: true + +require_relative 'spec_helper' +require 'microsoft_kiota_abstractions' + +module TestModels + class SimpleParsable + include MicrosoftKiotaAbstractions::Parsable + + attr_accessor :name, :age + + def get_field_deserializers + { + 'name' => lambda { |n| @name = n.get_string_value }, + 'age' => lambda { |n| @age = n.get_number_value } + } + end + + def serialize(writer) + writer.write_string_value('name', @name) + writer.write_number_value('age', @age) + end + + def self.create_from_discriminator_value(_parse_node) + SimpleParsable.new + end + end + + class AnotherParsable + include MicrosoftKiotaAbstractions::Parsable + + attr_accessor :email + + def get_field_deserializers + { 'email' => lambda { |n| @email = n.get_string_value } } + end + + def serialize(writer) + writer.write_string_value('email', @email) + end + + def self.create_from_discriminator_value(_parse_node) + AnotherParsable.new + end + end + + class UnionTypeWrapper + include MicrosoftKiotaAbstractions::Parsable + include MicrosoftKiotaAbstractions::ComposedTypeWrapper + + attr_accessor :simple, :another + + def get_field_deserializers + return @simple.get_field_deserializers if @simple + return @another.get_field_deserializers if @another + {} + end + + def serialize(writer) + if @simple + writer.write_object_value(nil, @simple) + elsif @another + writer.write_object_value(nil, @another) + end + end + + def self.create_from_discriminator_value(_parse_node) + UnionTypeWrapper.new + end + end + + class IntersectionTypeWrapper + include MicrosoftKiotaAbstractions::Parsable + include MicrosoftKiotaAbstractions::ComposedTypeWrapper + + attr_accessor :simple, :another + + def get_field_deserializers + MicrosoftKiotaAbstractions::ParseNodeHelper + .merge_deserializers_for_intersection_wrapper(@simple, @another) + end + + def serialize(writer) + writer.write_object_value(nil, @simple, @another) + end + + def self.create_from_discriminator_value(_parse_node) + IntersectionTypeWrapper.new + end + end +end + +RSpec.describe MicrosoftKiotaSerializationJson::JsonSerializationWriter do + describe '#write_object_value' do + it 'serializes a regular object with a key' do + writer = MicrosoftKiotaSerializationJson::JsonSerializationWriter.new + obj = TestModels::SimpleParsable.new + obj.name = 'Alice' + obj.age = 30 + + writer.write_object_value('person', obj) + expect(writer.writer['person']).to eq({ 'name' => 'Alice', 'age' => 30 }) + end + + it 'serializes a regular object with additional values merged under a key' do + writer = MicrosoftKiotaSerializationJson::JsonSerializationWriter.new + obj1 = TestModels::SimpleParsable.new + obj1.name = 'Alice' + obj1.age = 30 + obj2 = TestModels::AnotherParsable.new + obj2.email = 'alice@example.com' + + writer.write_object_value('person', obj1, obj2) + expect(writer.writer['person']).to eq({ 'name' => 'Alice', 'age' => 30, 'email' => 'alice@example.com' }) + end + + it 'skips nil additional values' do + writer = MicrosoftKiotaSerializationJson::JsonSerializationWriter.new + obj = TestModels::SimpleParsable.new + obj.name = 'Bob' + obj.age = 22 + + writer.write_object_value('person', obj, nil, nil) + expect(writer.writer['person']).to eq({ 'name' => 'Bob', 'age' => 22 }) + end + + it 'returns nil when value is nil' do + writer = MicrosoftKiotaSerializationJson::JsonSerializationWriter.new + result = writer.write_object_value('key', nil) + expect(result).to be_nil + expect(writer.writer).to eq({}) + end + + it 'serializes into self when key is nil for a non-composed type' do + writer = MicrosoftKiotaSerializationJson::JsonSerializationWriter.new + obj = TestModels::SimpleParsable.new + obj.name = 'Eve' + obj.age = 40 + + writer.write_object_value(nil, obj) + expect(writer.writer).to eq({ 'name' => 'Eve', 'age' => 40 }) + end + end + + describe '#write_object_value with union types' do + it 'serializes the first branch of a union into the current writer' do + writer = MicrosoftKiotaSerializationJson::JsonSerializationWriter.new + union = TestModels::UnionTypeWrapper.new + inner = TestModels::SimpleParsable.new + inner.name = 'Charlie' + inner.age = 25 + union.simple = inner + + writer.write_object_value(nil, union) + expect(writer.writer).to eq({ 'name' => 'Charlie', 'age' => 25 }) + end + + it 'serializes the second branch of a union into the current writer' do + writer = MicrosoftKiotaSerializationJson::JsonSerializationWriter.new + union = TestModels::UnionTypeWrapper.new + inner = TestModels::AnotherParsable.new + inner.email = 'test@example.com' + union.another = inner + + writer.write_object_value(nil, union) + expect(writer.writer).to eq({ 'email' => 'test@example.com' }) + end + + it 'produces valid JSON for a union type' do + writer = MicrosoftKiotaSerializationJson::JsonSerializationWriter.new + union = TestModels::UnionTypeWrapper.new + inner = TestModels::SimpleParsable.new + inner.name = 'Charlie' + inner.age = 25 + union.simple = inner + + writer.write_object_value(nil, union) + json = writer.get_serialized_content + parsed = JSON.parse(json) + expect(parsed).to eq({ 'name' => 'Charlie', 'age' => 25 }) + end + end + + describe '#write_object_value with intersection types' do + it 'merges all intersection members into the current writer' do + writer = MicrosoftKiotaSerializationJson::JsonSerializationWriter.new + intersection = TestModels::IntersectionTypeWrapper.new + obj1 = TestModels::SimpleParsable.new + obj1.name = 'Dana' + obj1.age = 28 + obj2 = TestModels::AnotherParsable.new + obj2.email = 'dana@example.com' + intersection.simple = obj1 + intersection.another = obj2 + + writer.write_object_value(nil, intersection) + expect(writer.writer).to eq({ 'name' => 'Dana', 'age' => 28, 'email' => 'dana@example.com' }) + end + + it 'produces valid JSON for an intersection type' do + writer = MicrosoftKiotaSerializationJson::JsonSerializationWriter.new + intersection = TestModels::IntersectionTypeWrapper.new + obj1 = TestModels::SimpleParsable.new + obj1.name = 'Dana' + obj1.age = 28 + obj2 = TestModels::AnotherParsable.new + obj2.email = 'dana@example.com' + intersection.simple = obj1 + intersection.another = obj2 + + writer.write_object_value(nil, intersection) + json = writer.get_serialized_content + parsed = JSON.parse(json) + expect(parsed).to eq({ 'name' => 'Dana', 'age' => 28, 'email' => 'dana@example.com' }) + end + end + + describe '#write_collection_of_object_values' do + it 'serializes a collection of objects under a key' do + writer = MicrosoftKiotaSerializationJson::JsonSerializationWriter.new + obj1 = TestModels::SimpleParsable.new + obj1.name = 'Alice' + obj1.age = 30 + obj2 = TestModels::SimpleParsable.new + obj2.name = 'Bob' + obj2.age = 25 + + writer.write_collection_of_object_values('people', [obj1, obj2]) + expect(writer.writer['people']).to eq([ + { 'name' => 'Alice', 'age' => 30 }, + { 'name' => 'Bob', 'age' => 25 } + ]) + end + + it 'serializes into self when key is nil' do + writer = MicrosoftKiotaSerializationJson::JsonSerializationWriter.new + obj1 = TestModels::SimpleParsable.new + obj1.name = 'Alice' + obj1.age = 30 + + writer.write_collection_of_object_values(nil, [obj1]) + expect(writer.writer).to eq({ 'name' => 'Alice', 'age' => 30 }) + end + + it 'does not contaminate the parent writer' do + writer = MicrosoftKiotaSerializationJson::JsonSerializationWriter.new + writer.write_string_value('top_level', 'value') + obj = TestModels::SimpleParsable.new + obj.name = 'Alice' + obj.age = 30 + + writer.write_collection_of_object_values('people', [obj]) + expect(writer.writer.keys).to contain_exactly('top_level', 'people') + end + + it 'produces valid JSON with a collection' do + writer = MicrosoftKiotaSerializationJson::JsonSerializationWriter.new + obj1 = TestModels::SimpleParsable.new + obj1.name = 'Alice' + obj1.age = 30 + obj2 = TestModels::AnotherParsable.new + obj2.email = 'bob@example.com' + + writer.write_collection_of_object_values('items', [obj1]) + writer.write_object_value('contact', obj2) + json = writer.get_serialized_content + parsed = JSON.parse(json) + expect(parsed['items']).to eq([{ 'name' => 'Alice', 'age' => 30 }]) + expect(parsed['contact']).to eq({ 'email' => 'bob@example.com' }) + end + end +end