Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

- fixes a bug where request bodies that are collections of single items would not serialize properly #43

Merged
merged 2 commits into from
Oct 27, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,12 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0

### Changed

## [1.0.0-preview.16] - 2022-10-28

### Changed

- Fixed a bug where request bodies that are collections of single items would not serialize properly

## [1.0.0-preview.15] - 2022-10-18

### Added
Expand Down
78 changes: 78 additions & 0 deletions Microsoft.Kiota.Abstractions.Tests/RequestInformationTests.cs
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
using System;
using System.Collections.Generic;
using System.Linq;
using Microsoft.Kiota.Abstractions.Serialization;
using Microsoft.Kiota.Abstractions.Tests.Mocks;
using Moq;
using Xunit;

Expand Down Expand Up @@ -194,6 +196,82 @@ public void GetsAndSetsResponseHandlerByType()
// Assert we now have an option
Assert.NotNull(requestInfo.GetRequestOption<ResponseHandlerOption>());
}
[Fact]
public void SetsObjectContent() {
var requestAdapterMock = new Mock<IRequestAdapter>();
var serializationWriterMock = new Mock<ISerializationWriter>();
var serializationWriterFactoryMock = new Mock<ISerializationWriterFactory>();
serializationWriterFactoryMock.Setup(x => x.GetSerializationWriter(It.IsAny<string>())).Returns(serializationWriterMock.Object);
requestAdapterMock.SetupGet(x => x.SerializationWriterFactory).Returns(serializationWriterFactoryMock.Object);
var requestInfo = new RequestInformation
{
HttpMethod = Method.POST,
UrlTemplate = "{+baseurl}/users{?%24count}"
};

requestInfo.SetContentFromParsable(requestAdapterMock.Object, "application/json", new TestEntity());

// Assert we now have an option
serializationWriterMock.Verify(x => x.WriteObjectValue(It.IsAny<string>(), It.IsAny<IParsable>()), Times.Once);
serializationWriterMock.Verify(x => x.WriteCollectionOfObjectValues(It.IsAny<string>(), It.IsAny<IEnumerable<IParsable>>()), Times.Never);
}
[Fact]
public void SetsObjectCollectionContentSingleElement() {
var requestAdapterMock = new Mock<IRequestAdapter>();
var serializationWriterMock = new Mock<ISerializationWriter>();
var serializationWriterFactoryMock = new Mock<ISerializationWriterFactory>();
serializationWriterFactoryMock.Setup(x => x.GetSerializationWriter(It.IsAny<string>())).Returns(serializationWriterMock.Object);
requestAdapterMock.SetupGet(x => x.SerializationWriterFactory).Returns(serializationWriterFactoryMock.Object);
var requestInfo = new RequestInformation
{
HttpMethod = Method.POST,
UrlTemplate = "{+baseurl}/users{?%24count}"
};

requestInfo.SetContentFromParsable(requestAdapterMock.Object, "application/json", new [] {new TestEntity()});

// Assert we now have an option
serializationWriterMock.Verify(x => x.WriteObjectValue(It.IsAny<string>(), It.IsAny<IParsable>()), Times.Never);
serializationWriterMock.Verify(x => x.WriteCollectionOfObjectValues(It.IsAny<string>(), It.IsAny<IEnumerable<IParsable>>()), Times.Once);
}
[Fact]
public void SetsScalarContent() {
var requestAdapterMock = new Mock<IRequestAdapter>();
var serializationWriterMock = new Mock<ISerializationWriter>();
var serializationWriterFactoryMock = new Mock<ISerializationWriterFactory>();
serializationWriterFactoryMock.Setup(x => x.GetSerializationWriter(It.IsAny<string>())).Returns(serializationWriterMock.Object);
requestAdapterMock.SetupGet(x => x.SerializationWriterFactory).Returns(serializationWriterFactoryMock.Object);
var requestInfo = new RequestInformation
{
HttpMethod = Method.POST,
UrlTemplate = "{+baseurl}/users{?%24count}"
};

requestInfo.SetContentFromScalar(requestAdapterMock.Object, "application/json", "foo");

// Assert we now have an option
serializationWriterMock.Verify(x => x.WriteStringValue(It.IsAny<string>(), It.IsAny<string>()), Times.Once);
serializationWriterMock.Verify(x => x.WriteCollectionOfPrimitiveValues(It.IsAny<string>(), It.IsAny<IEnumerable<string>>()), Times.Never);
}
[Fact]
public void SetsScalarCollectionContent() {
var requestAdapterMock = new Mock<IRequestAdapter>();
var serializationWriterMock = new Mock<ISerializationWriter>();
var serializationWriterFactoryMock = new Mock<ISerializationWriterFactory>();
serializationWriterFactoryMock.Setup(x => x.GetSerializationWriter(It.IsAny<string>())).Returns(serializationWriterMock.Object);
requestAdapterMock.SetupGet(x => x.SerializationWriterFactory).Returns(serializationWriterFactoryMock.Object);
var requestInfo = new RequestInformation
{
HttpMethod = Method.POST,
UrlTemplate = "{+baseurl}/users{?%24count}"
};

requestInfo.SetContentFromScalarCollection(requestAdapterMock.Object, "application/json", new[] {"foo"});

// Assert we now have an option
serializationWriterMock.Verify(x => x.WriteStringValue(It.IsAny<string>(), It.IsAny<string>()), Times.Never);
serializationWriterMock.Verify(x => x.WriteCollectionOfPrimitiveValues(It.IsAny<string>(), It.IsAny<IEnumerable<string>>()), Times.Once);
}
}

/// <summary>The messages in a mailbox or folder. Read-only. Nullable.</summary>
Expand Down
4 changes: 2 additions & 2 deletions src/Microsoft.Kiota.Abstractions.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@
<EmbedUntrackedSources>true</EmbedUntrackedSources>
<Deterministic>true</Deterministic>
<VersionPrefix>1.0.0</VersionPrefix>
<VersionSuffix>preview.15</VersionSuffix>
<VersionSuffix>preview.16</VersionSuffix>
<GeneratePackageOnBuild>true</GeneratePackageOnBuild>
<SignAssembly>false</SignAssembly>
<DelaySign>false</DelaySign>
Expand All @@ -23,7 +23,7 @@
<!-- Enable this line once we go live to prevent breaking changes -->
<!-- <PackageValidationBaselineVersion>1.0.0</PackageValidationBaselineVersion> -->
<PackageReleaseNotes>
- Adds an API key authentication provider
- Fixed a bug where request bodies that are collections of single items would not serialize properly
</PackageReleaseNotes>
<PackageRequireLicenseAcceptance>true</PackageRequireLicenseAcceptance>
<PackageLicenseFile>LICENSE</PackageLicenseFile>
Expand Down
151 changes: 85 additions & 66 deletions src/RequestInformation.cs
Original file line number Diff line number Diff line change
Expand Up @@ -195,18 +195,28 @@ public void SetStreamContent(Stream content)
/// <param name="items">The models to serialize.</param>
/// <param name="contentType">The content type to set.</param>
/// <typeparam name="T">The model type to serialize.</typeparam>
public void SetContentFromParsable<T>(IRequestAdapter requestAdapter, string contentType, params T[] items) where T : IParsable
public void SetContentFromParsable<T>(IRequestAdapter requestAdapter, string contentType, IEnumerable<T> items) where T : IParsable
{
using var activity = _activitySource?.StartActivity(nameof(SetContentFromParsable));
using var writer = getSerializationWriter(requestAdapter, contentType, items);
if(items.Count() == 1) {
setRequestType(items[0], activity);
writer.WriteObjectValue(null, items[0]);
} else {
if (items.Count() > 0)
setRequestType(items[0], activity);
writer.WriteCollectionOfObjectValues(null, items);
}
setRequestType(items.FirstOrDefault(static x => x != null), activity);
writer.WriteCollectionOfObjectValues(null, items);
Headers.Add(ContentTypeHeader, contentType);
Content = writer.GetSerializedContent();
}
/// <summary>
/// Sets the request body from a model with the specified content type.
/// </summary>
/// <param name="requestAdapter">The core service to get the serialization writer from.</param>
/// <param name="item">The model to serialize.</param>
/// <param name="contentType">The content type to set.</param>
/// <typeparam name="T">The model type to serialize.</typeparam>
public void SetContentFromParsable<T>(IRequestAdapter requestAdapter, string contentType, T item) where T : IParsable
{
using var activity = _activitySource?.StartActivity(nameof(SetContentFromParsable));
using var writer = getSerializationWriter(requestAdapter, contentType, item);
setRequestType(item, activity);
writer.WriteObjectValue(null, item);
Headers.Add(ContentTypeHeader, contentType);
Content = writer.GetSerializedContent();
}
Expand All @@ -215,11 +225,11 @@ private void setRequestType(object result, Activity activity) {
if (result == null) return;
activity.SetTag("com.microsoft.kiota.request.type", result.GetType().FullName);
}
private ISerializationWriter getSerializationWriter<T>(IRequestAdapter requestAdapter, string contentType, params T[] items)
private ISerializationWriter getSerializationWriter<T>(IRequestAdapter requestAdapter, string contentType, T item)
{
if(string.IsNullOrEmpty(contentType)) throw new ArgumentNullException(nameof(contentType));
if(requestAdapter == null) throw new ArgumentNullException(nameof(requestAdapter));
if(items == null || !items.Any()) throw new InvalidOperationException($"{nameof(items)} cannot be null or empty");
if(item == null) throw new InvalidOperationException($"{nameof(item)} cannot be null");
return requestAdapter.SerializationWriterFactory.GetSerializationWriter(contentType);
}
/// <summary>
Expand All @@ -229,64 +239,73 @@ private ISerializationWriter getSerializationWriter<T>(IRequestAdapter requestAd
/// <param name="items">The scalar values to serialize.</param>
/// <param name="contentType">The content type to set.</param>
/// <typeparam name="T">The model type to serialize.</typeparam>
public void SetContentFromScalar<T>(IRequestAdapter requestAdapter, string contentType, params T[] items)
public void SetContentFromScalarCollection<T>(IRequestAdapter requestAdapter, string contentType, IEnumerable<T> items)
{
using var activity = _activitySource?.StartActivity(nameof(SetContentFromScalar));
using var activity = _activitySource?.StartActivity(nameof(SetContentFromScalarCollection));
using var writer = getSerializationWriter(requestAdapter, contentType, items);
if(items.Count() == 1)
setRequestType(items.FirstOrDefault(static x => x != null), activity);
writer.WriteCollectionOfPrimitiveValues(null, items);
Headers.Add(ContentTypeHeader, contentType);
Content = writer.GetSerializedContent();
}
/// <summary>
/// Sets the request body from a scalar value with the specified content type.
/// </summary>
/// <param name="requestAdapter">The core service to get the serialization writer from.</param>
/// <param name="item">The scalar value to serialize.</param>
/// <param name="contentType">The content type to set.</param>
/// <typeparam name="T">The model type to serialize.</typeparam>
public void SetContentFromScalar<T>(IRequestAdapter requestAdapter, string contentType, T item)
{
using var activity = _activitySource?.StartActivity(nameof(SetContentFromScalar));
using var writer = getSerializationWriter(requestAdapter, contentType, item);
setRequestType(item, activity);
switch(item)
{
setRequestType(items[0], activity);
switch(items[0])
{
case string s:
writer.WriteStringValue(null, s);
break;
case bool b:
writer.WriteBoolValue(null, b);
break;
case byte b:
writer.WriteByteValue(null, b);
break;
case sbyte b:
writer.WriteSbyteValue(null, b);
break;
case int i:
writer.WriteIntValue(null, i);
break;
case float f:
writer.WriteFloatValue(null, f);
break;
case long l:
writer.WriteLongValue(null, l);
break;
case double d:
writer.WriteDoubleValue(null, d);
break;
case Guid g:
writer.WriteGuidValue(null, g);
break;
case DateTimeOffset dto:
writer.WriteDateTimeOffsetValue(null, dto);
break;
case TimeSpan timeSpan:
writer.WriteTimeSpanValue(null, timeSpan);
break;
case Date date:
writer.WriteDateValue(null, date);
break;
case Time time:
writer.WriteTimeValue(null, time);
break;
case null:
writer.WriteNullValue(null);
break;
default:
throw new InvalidOperationException($"error serialization data value with unknown type {items[0]?.GetType()}");
}
} else {
if (items.Count() > 0)
setRequestType(items[0], activity);
writer.WriteCollectionOfPrimitiveValues(null, items);
case string s:
writer.WriteStringValue(null, s);
break;
case bool b:
writer.WriteBoolValue(null, b);
break;
case byte b:
writer.WriteByteValue(null, b);
break;
case sbyte b:
writer.WriteSbyteValue(null, b);
break;
case int i:
writer.WriteIntValue(null, i);
break;
case float f:
writer.WriteFloatValue(null, f);
break;
case long l:
writer.WriteLongValue(null, l);
break;
case double d:
writer.WriteDoubleValue(null, d);
break;
case Guid g:
writer.WriteGuidValue(null, g);
break;
case DateTimeOffset dto:
writer.WriteDateTimeOffsetValue(null, dto);
break;
case TimeSpan timeSpan:
writer.WriteTimeSpanValue(null, timeSpan);
break;
case Date date:
writer.WriteDateValue(null, date);
break;
case Time time:
writer.WriteTimeValue(null, time);
break;
case null:
writer.WriteNullValue(null);
break;
default:
throw new InvalidOperationException($"error serialization data value with unknown type {item?.GetType()}");
}
Headers.Add(ContentTypeHeader, contentType);
Content = writer.GetSerializedContent();
Expand Down