Skip to content

Commit

Permalink
Merge pull request #480 from microsoft/bugfix/raw-collections
Browse files Browse the repository at this point in the history
bugfix/raw collections
  • Loading branch information
baywet authored Aug 13, 2021
2 parents 8037e7e + 88d05ee commit 57a1087
Show file tree
Hide file tree
Showing 34 changed files with 234 additions and 46 deletions.
4 changes: 4 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,10 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0

- Ruby JSON serialization #429

### Changed

- Fixed a bug where raw collections requests would not be supported #467

## [0.0.7] - 2021-08-04

### Added
Expand Down
8 changes: 8 additions & 0 deletions abstractions/dotnet/src/IHttpCore.cs
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
using System.Collections.Generic;
using System.Threading.Tasks;
using Microsoft.Kiota.Abstractions.Serialization;

Expand All @@ -22,6 +23,13 @@ public interface IHttpCore {
/// <returns>The deserialized response model.</returns>
Task<ModelType> SendAsync<ModelType>(RequestInfo requestInfo, IResponseHandler responseHandler = default) where ModelType : IParsable;
/// <summary>
/// Excutes the HTTP request specified by the given RequestInfo and returns the deserialized response model collection.
/// </summary>
/// <param name="requestInfo">The RequestInfo object to use for the HTTP request.</param>
/// <param name="responseHandler">The response handler to use for the HTTP request instead of the default handler.</param>
/// <returns>The deserialized response model collection.</returns>
Task<IEnumerable<ModelType>> SendCollectionAsync<ModelType>(RequestInfo requestInfo, IResponseHandler responseHandler = default) where ModelType : IParsable;
/// <summary>
/// Excutes the HTTP request specified by the given RequestInfo and returns the deserialized primitive response model.
/// </summary>
/// <param name="requestInfo">The RequestInfo object to use for the HTTP request.</param>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
<TargetFramework>net5.0</TargetFramework>
<PublishRepositoryUrl>true</PublishRepositoryUrl>
<RepositoryUrl>https://github.com/microsoft/kiota</RepositoryUrl>
<Version>1.0.14</Version>
<Version>1.0.15</Version>
</PropertyGroup>

</Project>
10 changes: 7 additions & 3 deletions abstractions/dotnet/src/RequestInfo.cs
Original file line number Diff line number Diff line change
Expand Up @@ -69,15 +69,19 @@ public void SetStreamContent(Stream content) {
/// Sets the request body from a model with the specified content type.
/// </summary>
/// <param name="coreService">The core service to get the serialization writer from.</param>
/// <param name="item">The model to serialize.</param>
/// <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>(T item, IHttpCore coreService, string contentType) where T : IParsable {
public void SetContentFromParsable<T>(IHttpCore coreService, string contentType, params T[] items) where T : IParsable {
if(string.IsNullOrEmpty(contentType)) throw new ArgumentNullException(nameof(contentType));
if(coreService == null) throw new ArgumentNullException(nameof(coreService));
if(items == null || !items.Any()) throw new InvalidOperationException($"{nameof(items)} cannot be null or empty");

using var writer = coreService.SerializationWriterFactory.GetSerializationWriter(contentType);
writer.WriteObjectValue(null, item);
if(items.Count() == 1)
writer.WriteObjectValue(null, items[0]);
else
writer.WriteCollectionOfObjectValues(null, items);
Headers.Add(contentTypeHeader, contentType);
Content = writer.GetSerializedContent();
}
Expand Down
2 changes: 1 addition & 1 deletion abstractions/java/lib/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@ publishing {
publications {
gpr(MavenPublication) {
artifactId 'kiota-abstractions'
version '1.0.14'
version '1.0.15'
from(components.java)
}
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package com.microsoft.kiota;

import java.util.concurrent.CompletableFuture;
import java.util.List;

import javax.annotation.Nonnull;
import javax.annotation.Nullable;
Expand All @@ -27,6 +28,15 @@ public interface HttpCore {
* @return a {@link CompletableFuture} with the deserialized response model.
*/
<ModelType extends Parsable> CompletableFuture<ModelType> sendAsync(@Nonnull final RequestInfo requestInfo, @Nonnull final Class<ModelType> targetClass, @Nullable final ResponseHandler responseHandler);
/**
* Excutes the HTTP request specified by the given RequestInfo and returns the deserialized response model collection.
* @param requestInfo the request info to execute.
* @param responseHandler The response handler to use for the HTTP request instead of the default handler.
* @param targetClass the class of the response model to deserialize the response into.
* @param <ModelType> the type of the response model to deserialize the response into.
* @return a {@link CompletableFuture} with the deserialized response model collection.
*/
<ModelType extends Parsable> CompletableFuture<Iterable<ModelType>> sendCollectionAsync(@Nonnull final RequestInfo requestInfo, @Nonnull final Class<ModelType> targetClass, @Nullable final ResponseHandler responseHandler);
/**
* Excutes the HTTP request specified by the given RequestInfo and returns the deserialized primitive response model.
* @param requestInfo the request info to execute.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
import java.net.URI;
import java.io.IOException;
import java.io.InputStream;
import java.util.Arrays;
import java.util.Collection;
import java.util.HashMap;
import java.util.Objects;
Expand Down Expand Up @@ -71,18 +72,23 @@ public void setStreamContent(@Nonnull final InputStream value) {
}
/**
* Sets the request body from a model with the specified content type.
* @param value the model.
* @param values the models.
* @param contentType the content type.
* @param httpCore The core service to get the serialization writer from.
* @param <T> the model type.
*/
public <T extends Parsable> void setContentFromParsable(@Nonnull final T value, @Nonnull final HttpCore httpCore, @Nonnull final String contentType) {
public <T extends Parsable> void setContentFromParsable(@Nonnull final HttpCore httpCore, @Nonnull final String contentType, @Nonnull final T... values) {
Objects.requireNonNull(httpCore);
Objects.requireNonNull(value);
Objects.requireNonNull(values);
Objects.requireNonNull(contentType);
if(values.length == 0) throw new RuntimeException("values cannot be empty");

try(final SerializationWriter writer = httpCore.getSerializationWriterFactory().getSerializationWriter(contentType)) {
headers.put(contentTypeHeader, contentType);
writer.writeObjectValue(null, value);
if(values.length == 1)
writer.writeObjectValue(null, values[0]);
else
writer.writeCollectionOfObjectValues(null, Arrays.asList(values));
this.content = writer.getSerializedContent();
} catch (IOException ex) {
throw new RuntimeException("could not serialize payload", ex);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,11 +28,15 @@ def set_stream_content(value = $stdin)
@headers[@@content_type_header] = @@binary_content_type
end

def set_content_from_parsable(value, serializer_factory, content_type)
def set_content_from_parsable(serializer_factory, content_type, values)
begin
writer = serializer_factory.get_serialization_writer(content_type)
headers[@@content_type_header] = content_type
writer.write_object_value(nil, value);
if values != nil && values.kind_of?(Array)
writer.write_collection_of_object_values(nil, values)
else
writer.write_object_value(nil, values);
end
this.content = writer.get_serialized_content();
rescue => exception
raise Exception.new "could not serialize payload"
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
# frozen_string_literal: true

module MicrosoftKiotaAbstractions
VERSION = "0.1.1"
VERSION = "0.1.2"
end
2 changes: 1 addition & 1 deletion abstractions/typescript/package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion abstractions/typescript/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@microsoft/kiota-abstractions",
"version": "1.0.14",
"version": "1.0.15",
"description": "Core abstractions for kiota generated libraries in TypeScript and JavaScript",
"main": "dist/index.js",
"types": "dist/index.d.ts",
Expand Down
9 changes: 9 additions & 0 deletions abstractions/typescript/src/httpCore.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,15 @@ export interface HttpCore {
* @return a {@link Promise} with the deserialized response model.
*/
sendAsync<ModelType extends Parsable>(requestInfo: RequestInfo, type: new() => ModelType, responseHandler: ResponseHandler | undefined): Promise<ModelType>;
/**
* Excutes the HTTP request specified by the given RequestInfo and returns the deserialized response model collection.
* @param requestInfo the request info to execute.
* @param responseHandler The response handler to use for the HTTP request instead of the default handler.
* @param type the class of the response model to deserialize the response into.
* @typeParam ModelType the type of the response model to deserialize the response into.
* @return a {@link Promise} with the deserialized response model collection.
*/
sendCollectionAsync<ModelType extends Parsable>(requestInfo: RequestInfo, type: new() => ModelType, responseHandler: ResponseHandler | undefined): Promise<ModelType[]>;
/**
* Excutes the HTTP request specified by the given RequestInfo and returns the deserialized primitive response model.
* @param requestInfo the request info to execute.
Expand Down
10 changes: 7 additions & 3 deletions abstractions/typescript/src/requestInfo.ts
Original file line number Diff line number Diff line change
Expand Up @@ -36,18 +36,22 @@ export class RequestInfo {
private static contentTypeHeader = "Content-Type";
/**
* Sets the request body from a model with the specified content type.
* @param value the model.
* @param values the models.
* @param contentType the content type.
* @param httpCore The core service to get the serialization writer from.
* @typeParam T the model type.
*/
public setContentFromParsable = <T extends Parsable>(value?: T | undefined, httpCore?: HttpCore | undefined, contentType?: string | undefined): void => {
public setContentFromParsable = <T extends Parsable>(httpCore?: HttpCore | undefined, contentType?: string | undefined, ...values: T[]): void => {
if(!httpCore) throw new Error("httpCore cannot be undefined");
if(!contentType) throw new Error("contentType cannot be undefined");
if(!values || values.length === 0) throw new Error("values cannot be undefined or empty");

const writer = httpCore.getSerializationWriterFactory().getSerializationWriter(contentType);
this.headers.set(RequestInfo.contentTypeHeader, contentType);
writer.writeObjectValue(undefined, value);
if(values.length === 1)
writer.writeObjectValue(undefined, values[0]);
else
writer.writeCollectionOfObjectValues(undefined, values);
this.content = writer.getSerializedContent();
}
/**
Expand Down
11 changes: 11 additions & 0 deletions http/dotnet/httpclient/src/HttpCore.cs
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,17 @@ public HttpCore(IAuthenticationProvider authenticationProvider, IParseNodeFactor
}
/// <summary>Factory to use to get a serializer for payload serialization</summary>
public ISerializationWriterFactory SerializationWriterFactory { get { return sWriterFactory; } }
public async Task<IEnumerable<ModelType>> SendCollectionAsync<ModelType>(RequestInfo requestInfo, IResponseHandler responseHandler = default) where ModelType : IParsable {
var response = await GetHttpResponseMessage(requestInfo);
requestInfo.Content?.Dispose();
if(responseHandler == null) {
var rootNode = await GetRootParseNode(response);
var result = rootNode.GetCollectionOfObjectValues<ModelType>();
return result;
}
else
return await responseHandler.HandleResponseAsync<HttpResponseMessage, IEnumerable<ModelType>>(response);
}
public async Task<ModelType> SendAsync<ModelType>(RequestInfo requestInfo, IResponseHandler responseHandler = null) where ModelType : IParsable
{
var response = await GetHttpResponseMessage(requestInfo);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,11 +4,11 @@
<TargetFramework>net5.0</TargetFramework>
<PublishRepositoryUrl>true</PublishRepositoryUrl>
<RepositoryUrl>https://github.com/microsoft/kiota</RepositoryUrl>
<Version>1.0.4</Version>
<Version>1.0.5</Version>
</PropertyGroup>

<ItemGroup>
<PackageReference Include="Microsoft.Kiota.Abstractions" Version="1.0.14" />
<PackageReference Include="Microsoft.Kiota.Abstractions" Version="1.0.15" />
</ItemGroup>

</Project>
4 changes: 2 additions & 2 deletions http/java/okhttp/lib/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ dependencies {
// This dependency is used internally, and not exposed to consumers on their own compile classpath.
implementation 'com.google.guava:guava:30.1.1-jre'
api 'com.squareup.okhttp3:okhttp:4.9.1'
api 'com.microsoft.kiota:kiota-abstractions:1.0.14'
api 'com.microsoft.kiota:kiota-abstractions:1.0.15'
}

publishing {
Expand All @@ -53,7 +53,7 @@ publishing {
publications {
gpr(MavenPublication) {
artifactId 'kiota-http-okhttp'
version '1.0.4'
version '1.0.5'
from(components.java)
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,33 @@ public void enableBackingStore() {
this.sWriterFactory = Objects.requireNonNull(ApiClientBuilder.enableBackingStoreForSerializationWriterFactory(sWriterFactory));
}
@Nonnull
public <ModelType extends Parsable> CompletableFuture<Iterable<ModelType>> sendCollectionAsync(@Nonnull final RequestInfo requestInfo, @Nonnull final Class<ModelType> targetClass, @Nullable final ResponseHandler responseHandler) {
Objects.requireNonNull(requestInfo, "parameter requestInfo cannot be null");

return addBearerIfNotPresent(requestInfo).thenCompose(x -> {
final HttpCoreCallbackFutureWrapper wrapper = new HttpCoreCallbackFutureWrapper();
this.client.newCall(getRequestFromRequestInfo(requestInfo)).enqueue(wrapper);
return wrapper.future;
}).thenCompose(response -> {
if(responseHandler == null) {
final ResponseBody body = response.body();
try {
try (final InputStream rawInputStream = body.byteStream()) {
final ParseNode rootNode = pNodeFactory.getParseNode(getMediaTypeAndSubType(body.contentType()), rawInputStream);
final Iterable<ModelType> result = rootNode.getCollectionOfObjectValues(targetClass);
return CompletableFuture.completedStage(result);
}
} catch(IOException ex) {
return CompletableFuture.failedFuture(new RuntimeException("failed to read the response body", ex));
} finally {
response.close();
}
} else {
return responseHandler.handleResponseAsync(response);
}
});
}
@Nonnull
public <ModelType extends Parsable> CompletableFuture<ModelType> sendAsync(@Nonnull final RequestInfo requestInfo, @Nonnull final Class<ModelType> targetClass, @Nullable final ResponseHandler responseHandler) {
Objects.requireNonNull(requestInfo, "parameter requestInfo cannot be null");

Expand Down
2 changes: 1 addition & 1 deletion http/typescript/fetch/package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

4 changes: 2 additions & 2 deletions http/typescript/fetch/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@microsoft/kiota-http-fetch",
"version": "1.0.4",
"version": "1.0.5",
"description": "Kiota HttpCore implementation with fetch",
"main": "dist/index.js",
"types": "dist/index.d.ts",
Expand Down Expand Up @@ -29,7 +29,7 @@
"registry": "https://npm.pkg.github.com"
},
"dependencies": {
"@microsoft/kiota-abstractions": "^1.0.14",
"@microsoft/kiota-abstractions": "^1.0.15",
"cross-fetch": "^3.1.4",
"web-streams-polyfill": "^3.1.0"
},
Expand Down
21 changes: 21 additions & 0 deletions http/typescript/fetch/src/httpCore.ts
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,27 @@ export class HttpCore implements IHttpCore {
if(segments.length === 0) return undefined;
else return segments[0];
}
public sendCollectionAsync = async <ModelType extends Parsable>(requestInfo: RequestInfo, type: new() => ModelType, responseHandler: ResponseHandler | undefined): Promise<ModelType[]> => {
if(!requestInfo) {
throw new Error('requestInfo cannot be null');
}
await this.addBearerIfNotPresent(requestInfo);

const request = this.getRequestFromRequestInfo(requestInfo);
const response = await this.httpClient.fetch(this.getRequestUrl(requestInfo), request);
if(responseHandler) {
return await responseHandler.handleResponseAsync(response);
} else {
const payload = await response.arrayBuffer();
const responseContentType = this.getResponseContentType(response);
if(!responseContentType)
throw new Error("no response content type found for deserialization");

const rootNode = this.parseNodeFactory.getRootParseNode(responseContentType, payload);
const result = rootNode.getCollectionOfObjectValues(type);
return result as unknown as ModelType[];
}
}
public sendAsync = async <ModelType extends Parsable>(requestInfo: RequestInfo, type: new() => ModelType, responseHandler: ResponseHandler | undefined): Promise<ModelType> => {
if(!requestInfo) {
throw new Error('requestInfo cannot be null');
Expand Down
2 changes: 1 addition & 1 deletion samples
Submodule samples updated 258 files
3 changes: 2 additions & 1 deletion src/Kiota.Builder/CodeDOM/CodeTypeBase.cs
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,8 @@ protected CodeTypeBase(CodeElement parent) : base(parent) {
public bool ActionOf {get;set;} = false;
public bool IsNullable {get;set;} = true;
public CodeTypeCollectionKind CollectionKind {get;set;} = CodeTypeCollectionKind.None;

public bool IsCollection { get { return CollectionKind != CodeTypeCollectionKind.None; } }
public bool IsArray { get { return CollectionKind == CodeTypeCollectionKind.Array; } }
public ChildType BaseClone<ChildType>(CodeTypeBase source) where ChildType : CodeTypeBase
{
ActionOf = source.ActionOf;
Expand Down
2 changes: 1 addition & 1 deletion src/Kiota.Builder/Refiners/CommonLanguageRefiner.cs
Original file line number Diff line number Diff line change
Expand Up @@ -366,7 +366,7 @@ protected static void AddPropertiesAndMethodTypesImports(CodeElement current, bo
.Distinct();
var methodsParametersTypes = methods
.SelectMany(x => x.Parameters)
.Where(x => x.IsOfKind(CodeParameterKind.Custom))
.Where(x => x.IsOfKind(CodeParameterKind.Custom, CodeParameterKind.RequestBody))
.Select(x => x.Type)
.Distinct();
var indexerTypes = currentClass
Expand Down
5 changes: 5 additions & 0 deletions src/Kiota.Builder/Writers/CSharp/CSharpConventionService.cs
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,11 @@ public string TranslateType(string typeName)
default: return typeName?.ToFirstCharacterUpperCase() ?? "object";
}
}
public bool IsPrimitiveType(string typeName) {
return !string.IsNullOrEmpty(typeName) &&
(NullableTypes.Contains(typeName) ||
"string".Equals(typeName, StringComparison.OrdinalIgnoreCase));
}
public string GetParameterSignature(CodeParameter parameter)
{
var parameterType = GetTypeString(parameter.Type);
Expand Down
Loading

0 comments on commit 57a1087

Please sign in to comment.