Skip to content

Commit

Permalink
Merge pull request #330 from microsoft/feature/middlewares
Browse files Browse the repository at this point in the history
adds support for http middlewares
  • Loading branch information
baywet authored Aug 4, 2021
2 parents 48f0fbf + 1d088a6 commit dac49f5
Show file tree
Hide file tree
Showing 38 changed files with 575 additions and 157 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0

- Better client configuration #268
- Request builders constructors for data validation #322
- Adds middleware support for http clients #330

## [0.0.5] - 2021-06-10

Expand Down
7 changes: 7 additions & 0 deletions abstractions/dotnet/src/IMiddlewareOption.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
namespace Microsoft.Kiota.Abstractions {
/// <summary>
/// Represents a middleware option.
/// </summary>
public interface IMiddlewareOption {
}
}
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.13</Version>
<Version>1.0.14</Version>
</PropertyGroup>

</Project>
25 changes: 25 additions & 0 deletions abstractions/dotnet/src/RequestInfo.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
using System;
using System.Linq;
using System.Collections.Generic;
using System.IO;
using Microsoft.Kiota.Abstractions.Serialization;
Expand Down Expand Up @@ -30,6 +31,30 @@ public class RequestInfo
/// The Request Body.
/// </summary>
public Stream Content { get; set; }
private Dictionary<string, IMiddlewareOption> _middlewareOptions = new Dictionary<string, IMiddlewareOption>(StringComparer.OrdinalIgnoreCase);
/// <summary>
/// Gets the middleware options for this request. Options are unique by type. If an option of the same type is added twice, the last one wins.
/// </summary>
public IEnumerable<IMiddlewareOption> MiddlewareOptions { get { return _middlewareOptions.Values; } }
/// <summary>
/// Adds a middleware option to the request.
/// </summary>
/// <param name="middlewareOption">The middleware option to add.</param>
public void AddMiddlewareOptions(params IMiddlewareOption[] options) {
if(!options?.Any() ?? false) return; // it's a no-op if there are no options and this avoid having to check in the code gen.
foreach(var option in options.Where(x => x != null))
if(!_middlewareOptions.TryAdd(option.GetType().FullName, option))
_middlewareOptions[option.GetType().FullName] = option;
}
/// <summary>
/// Removes given middleware options from the current request.
/// </summary>
/// <param name="options">Middleware options to remove.</param>
public void RemoveMiddlewareOptions(params IMiddlewareOption[] options) {
if(!options?.Any() ?? false) throw new ArgumentNullException(nameof(options));
foreach(var optionName in options.Where(x => x != null).Select(x => x.GetType().FullName))
_middlewareOptions.Remove(optionName);
}
private const string binaryContentType = "application/octet-stream";
private const string contentTypeHeader = "Content-Type";
/// <summary>
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.13'
version '1.0.14'
from(components.java)
}
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
package com.microsoft.kiota;

/** Represents a middleware option. */
public interface MiddlewareOption {

}
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.Collection;
import java.util.HashMap;
import java.util.Objects;
import java.util.function.Function;
Expand Down Expand Up @@ -31,6 +32,32 @@ public class RequestInfo {
/** The Request Body. */
@Nullable
public InputStream content;
private HashMap<String, MiddlewareOption> _middlewareOptions = new HashMap<>();
/**
* Gets the middleware options for this request. Options are unique by type. If an option of the same type is added twice, the last one wins.
* @return the middleware options for this request.
*/
public Collection<MiddlewareOption> getMiddlewareOptions() { return _middlewareOptions.values(); }
/**
* Adds a middleware option to this request.
* @param option the middleware option to add.
*/
public void addMiddlewareOptions(@Nullable final MiddlewareOption... options) {
if(options == null || options.length == 0) return;
for(final MiddlewareOption option : options) {
_middlewareOptions.put(option.getClass().getCanonicalName(), option);
}
}
/**
* Removes a middleware option from this request.
* @param option the middleware option to remove.
*/
public void removeMiddlewareOptions(@Nullable final MiddlewareOption... options) {
if(options == null || options.length == 0) return;
for(final MiddlewareOption option : options) {
_middlewareOptions.remove(option.getClass().getCanonicalName());
}
}
private static String binaryContentType = "application/octet-stream";
private static String contentTypeHeader = "Content-Type";
/**
Expand Down
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.13",
"version": "1.0.14",
"description": "Core abstractions for kiota generated libraries in TypeScript and JavaScript",
"main": "dist/index.js",
"types": "dist/index.d.ts",
Expand Down
3 changes: 2 additions & 1 deletion abstractions/typescript/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,4 +8,5 @@ export * from "./nativeResponseHandler";
export * from "./nativeResponseWrapper";
export * from './serialization';
export * from './utils';
export * from './store';
export * from './store';
export * from './middlewareOption';
5 changes: 5 additions & 0 deletions abstractions/typescript/src/middlewareOption.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
/** Represents a middleware option. */
export interface MiddlewareOption {
/** Gets the option key for when adding it to a request. Must be unique. */
getKey(): string;
}
17 changes: 17 additions & 0 deletions abstractions/typescript/src/requestInfo.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import { HttpMethod } from "./httpMethod";
import { ReadableStream } from 'web-streams-polyfill/es2018';
import { Parsable } from "./serialization";
import { HttpCore } from "./httpCore";
import { MiddlewareOption } from "./middlewareOption";

/** This class represents an abstract HTTP request. */
export class RequestInfo {
Expand All @@ -15,6 +16,22 @@ export class RequestInfo {
public queryParameters: Map<string, object> = new Map<string, object>(); //TODO: case insensitive
/** The Request Headers. */
public headers: Map<string, string> = new Map<string, string>(); //TODO: case insensitive
private _middlewareOptions = new Map<string, MiddlewareOption>(); //TODO: case insensitive
/** Gets the middleware options for the request. */
public getMiddlewareOptions() { return this._middlewareOptions.values(); }
public addMiddlewareOptions(...options: MiddlewareOption[]) {
if(!options || options.length === 0) return;
options.forEach(option => {
this._middlewareOptions.set(option.getKey(), option);
});
}
/** Removes the middleware options for the request. */
public removeMiddlewareOptions(...options: MiddlewareOption[]) {
if(!options || options.length === 0) return;
options.forEach(option => {
this._middlewareOptions.delete(option.getKey());
});
}
private static binaryContentType = "application/octet-stream";
private static contentTypeHeader = "Content-Type";
/**
Expand Down
49 changes: 49 additions & 0 deletions http/dotnet/httpclient/src/HttpClientBuilder.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
using System.Linq;
using System.Collections.Generic;
using System.Net.Http;
using Microsoft.Kiota.Abstractions;

namespace Microsoft.Kiota.Http.HttpClient {
/// <summary>
/// This class is used to build the HttpClient instance used by the core service.
/// </summary>
public static class HttpClientBuilder {
/// <summary>
/// Initializes the <see cref="HttpClient"/> with the default configuration and middlewares including a authentention middleware using the <see cref="IAuthenticationProvider"/> if provided.
/// </summary>
/// <param name="authenticationProvider">The <see cref="IAuthenticationProvider"/> to use for authentention.</param>
/// <returns>The <see cref="HttpClient"/> with the default middlewares.</returns>
public static System.Net.Http.HttpClient Create(IAuthenticationProvider authenticationProvider = default) {
var defaultHandlers = CreateDefaultHandlers(authenticationProvider);
var handler = ChainHandlersCollectionAndGetFirstLink(defaultHandlers.ToArray());
return handler != null ? new System.Net.Http.HttpClient(handler) : new System.Net.Http.HttpClient(); //TODO configure the default client options
}
/// <summary>
/// Creates a default set of middleware to be used by the <see cref="HttpClient"/>.
/// </summary>
/// <param name="authenticationProvider">The <see cref="IAuthenticationProvider"/> to authenticate requests.</param>
/// <returns>A list of the default handlers used by the client.</returns>
public static IList<DelegatingHandler> CreateDefaultHandlers(IAuthenticationProvider authenticationProvider = default)
{
return new List<DelegatingHandler>(); //TODO add the default middlewares when they are ready
}
/// <summary>
/// Creates a <see cref="DelegatingHandler"/> to use for the <see cref="HttpClient" /> from the provided <see cref="DelegatingHandler"/> instances. Order matters.
/// </summary>
/// <param name="handlers">The <see cref="DelegatingHandler"/> instances to create the <see cref="DelegatingHandler"/> from.</param>
/// <returns>The created <see cref="DelegatingHandler"/>.</returns>
public static DelegatingHandler ChainHandlersCollectionAndGetFirstLink(params DelegatingHandler[] handlers) {
if(handlers == null || !handlers.Any()) return default;
var handlersCount = handlers.Count();
for(var i = 0; i < handlersCount; i++) {
var handler = handlers[i];
var previousItemIndex = i - 1;
if(previousItemIndex >= 0) {
var previousHandler = handlers[previousItemIndex];
previousHandler.InnerHandler = handler;
}
}
return handlers.First();
}
}
}
5 changes: 4 additions & 1 deletion http/dotnet/httpclient/src/HttpCore.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Net.Http;
Expand Down Expand Up @@ -28,7 +29,7 @@ public HttpCore(IAuthenticationProvider authenticationProvider, IParseNodeFactor
{
authProvider = authenticationProvider ?? throw new ArgumentNullException(nameof(authenticationProvider));
createdClient = httpClient == null;
client = httpClient ?? new System.Net.Http.HttpClient();
client = httpClient ?? HttpClientBuilder.Create(authProvider);
pNodeFactory = parseNodeFactory ?? ParseNodeFactoryRegistry.DefaultInstance;
sWriterFactory = serializationWriterFactory ?? SerializationWriterFactoryRegistry.DefaultInstance;
}
Expand Down Expand Up @@ -128,6 +129,8 @@ private HttpRequestMessage GetRequestMessageFromRequestInfo(RequestInfo requestI
string.Empty)),

};
if(requestInfo.MiddlewareOptions.Any())
requestInfo.MiddlewareOptions.ToList().ForEach(x => message.Options.Set(new HttpRequestOptionsKey<IMiddlewareOption>(x.GetType().FullName), x));
if(requestInfo.Headers?.Any() ?? false)
requestInfo.Headers.Where(x => !contentTypeHeaderName.Equals(x.Key, StringComparison.OrdinalIgnoreCase)).ToList().ForEach(x => message.Headers.Add(x.Key, x.Value));
if(requestInfo.Content != null) {
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.3</Version>
<Version>1.0.4</Version>
</PropertyGroup>

<ItemGroup>
<PackageReference Include="Microsoft.Kiota.Abstractions" Version="1.0.13" />
<PackageReference Include="Microsoft.Kiota.Abstractions" Version="1.0.14" />
</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.13'
api 'com.microsoft.kiota:kiota-abstractions:1.0.14'
}

publishing {
Expand All @@ -53,7 +53,7 @@ publishing {
publications {
gpr(MavenPublication) {
artifactId 'kiota-http-okhttp'
version '1.0.3'
version '1.0.4'
from(components.java)
}
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,3 @@
/*
* This Java source file was generated by the Gradle 'init' task.
*/
package com.microsoft.kiota.http;

import java.io.IOException;
Expand All @@ -18,6 +15,7 @@

import com.microsoft.kiota.ApiClientBuilder;
import com.microsoft.kiota.RequestInfo;
import com.microsoft.kiota.MiddlewareOption;
import com.microsoft.kiota.ResponseHandler;
import com.microsoft.kiota.AuthenticationProvider;
import com.microsoft.kiota.serialization.ParseNodeFactoryRegistry;
Expand Down Expand Up @@ -55,7 +53,7 @@ public HttpCore(@Nonnull final AuthenticationProvider authenticationProvider, @N
public HttpCore(@Nonnull final AuthenticationProvider authenticationProvider, @Nullable final ParseNodeFactory parseNodeFactory, @Nullable final SerializationWriterFactory serializationWriterFactory, @Nullable final OkHttpClient client) {
this.authProvider = Objects.requireNonNull(authenticationProvider, "parameter authenticationProvider cannot be null");
if(client == null) {
this.client = new OkHttpClient.Builder().build();
this.client = OkHttpClientBuilder.Create(this.authProvider).build();
} else {
this.client = client;
}
Expand Down Expand Up @@ -210,6 +208,9 @@ public void writeTo(BufferedSink sink) throws IOException {
for (final Map.Entry<String,String> header : requestInfo.headers.entrySet()) {
requestBuilder.addHeader(header.getKey(), header.getValue());
}
for(final MiddlewareOption option : requestInfo.getMiddlewareOptions()) {
requestBuilder.tag(option);
}
return requestBuilder.build();
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
package com.microsoft.kiota.http;

import com.microsoft.kiota.AuthenticationProvider;

import okhttp3.OkHttpClient;

import javax.annotation.Nullable;

/** This class is used to build the HttpClient instance used by the core service. */
public class OkHttpClientBuilder {
private OkHttpClientBuilder() { }
/**
* Creates an OkHttpClient Builder with the default configuration and middlewares including a authentention middleware using the {@link AuthenticationProvider} if provided.
* @param authenticationProvider the authentication provider used to authenticate the requests.
* @return an OkHttpClient Builder instance.
*/
public static OkHttpClient.Builder Create(@Nullable final AuthenticationProvider authenticationProvider) {
return new OkHttpClient.Builder(); //TODO configure the default client options.
//TODO add the default middlewares when they are ready
}
}
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.3",
"version": "1.0.4",
"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.13",
"@microsoft/kiota-abstractions": "^1.0.14",
"cross-fetch": "^3.1.4",
"web-streams-polyfill": "^3.1.0"
},
Expand Down
Loading

0 comments on commit dac49f5

Please sign in to comment.