[csharp] Improved apiclient.mustache (#18915)

* improved apiclient.mustache to keep it dry, sharing a single exec with Action<> delegate.

* updated samples and test

* Removed async from ApiClient.mustache

Updated samples

* Revert change to CSharpClientDeepObjectTest.java

* Fix async await (it was not waiting creating a null exception)

Updated samples

* Fix File IO namespace with using directive

* Improved comments on new methods

Added new DRY method DeserializeRestResponseFromPolicy

* Fix comments and parameters for new method DeserializeRestResponseFromPolicy

Updated samples
This commit is contained in:
Filipe Silva 2024-06-15 10:54:14 +01:00 committed by GitHub
parent 01fec76ab1
commit 8bac93e23b
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
12 changed files with 1247 additions and 1518 deletions

View File

@ -14,20 +14,22 @@ using System.Text;
using System.Threading; using System.Threading;
using System.Text.RegularExpressions; using System.Text.RegularExpressions;
using System.Threading.Tasks; using System.Threading.Tasks;
{{^netStandard}} {{^net60OrLater}}
using System.Web; using System.Web;
{{/netStandard}} {{/net60OrLater}}
using Newtonsoft.Json; using Newtonsoft.Json;
using Newtonsoft.Json.Serialization; using Newtonsoft.Json.Serialization;
using RestSharp; using RestSharp;
using RestSharp.Serializers; using RestSharp.Serializers;
using RestSharpMethod = RestSharp.Method; using RestSharpMethod = RestSharp.Method;
using FileIO = System.IO.File;
{{#supportsRetry}} {{#supportsRetry}}
using Polly; using Polly;
{{/supportsRetry}} {{/supportsRetry}}
{{#hasOAuthMethods}} {{#hasOAuthMethods}}
using {{packageName}}.Client.Auth; using {{packageName}}.Client.Auth;
{{/hasOAuthMethods}} {{/hasOAuthMethods}}
using {{packageName}}.{{modelPackage}};
namespace {{packageName}}.Client namespace {{packageName}}.Client
{ {
@ -68,10 +70,10 @@ namespace {{packageName}}.Client
/// <returns>A JSON string.</returns> /// <returns>A JSON string.</returns>
public string Serialize(object obj) public string Serialize(object obj)
{ {
if (obj != null && obj is {{{packageName}}}.{{modelPackage}}.AbstractOpenAPISchema) if (obj != null && obj is AbstractOpenAPISchema)
{ {
// the object to be serialized is an oneOf/anyOf schema // the object to be serialized is an oneOf/anyOf schema
return (({{{packageName}}}.{{modelPackage}}.AbstractOpenAPISchema)obj).ToJson(); return ((AbstractOpenAPISchema)obj).ToJson();
} }
else else
{ {
@ -116,7 +118,7 @@ namespace {{packageName}}.Client
if (match.Success) if (match.Success)
{ {
string fileName = filePath + ClientUtils.SanitizeFilename(match.Groups[1].Value.Replace("\"", "").Replace("'", "")); string fileName = filePath + ClientUtils.SanitizeFilename(match.Groups[1].Value.Replace("\"", "").Replace("'", ""));
File.WriteAllBytes(fileName, bytes); FileIO.WriteAllBytes(fileName, bytes);
return new FileStream(fileName, FileMode.Open); return new FileStream(fileName, FileMode.Open);
} }
} }
@ -127,7 +129,7 @@ namespace {{packageName}}.Client
if (type.Name.StartsWith("System.Nullable`1[[System.DateTime")) // return a datetime object if (type.Name.StartsWith("System.Nullable`1[[System.DateTime")) // return a datetime object
{ {
return DateTime.Parse(response.Content, null, System.Globalization.DateTimeStyles.RoundtripKind); return DateTime.Parse(response.Content, null, DateTimeStyles.RoundtripKind);
} }
if (type == typeof(string) || type.Name.StartsWith("System.Nullable")) // return primitive type if (type == typeof(string) || type.Name.StartsWith("System.Nullable")) // return primitive type
@ -149,13 +151,13 @@ namespace {{packageName}}.Client
public ISerializer Serializer => this; public ISerializer Serializer => this;
public IDeserializer Deserializer => this; public IDeserializer Deserializer => this;
public string[] AcceptedContentTypes => RestSharp.ContentType.JsonAccept; public string[] AcceptedContentTypes => ContentType.JsonAccept;
public SupportsContentType SupportsContentType => contentType => public SupportsContentType SupportsContentType => contentType =>
contentType.Value.EndsWith("json", StringComparison.InvariantCultureIgnoreCase) || contentType.Value.EndsWith("json", StringComparison.InvariantCultureIgnoreCase) ||
contentType.Value.EndsWith("javascript", StringComparison.InvariantCultureIgnoreCase); contentType.Value.EndsWith("javascript", StringComparison.InvariantCultureIgnoreCase);
public ContentType ContentType { get; set; } = RestSharp.ContentType.Json; public ContentType ContentType { get; set; } = ContentType.Json;
public DataFormat DataFormat => DataFormat.Json; public DataFormat DataFormat => DataFormat.Json;
} }
@ -203,7 +205,7 @@ namespace {{packageName}}.Client
/// </summary> /// </summary>
public ApiClient() public ApiClient()
{ {
_baseUrl = {{packageName}}.Client.GlobalConfiguration.Instance.BasePath; _baseUrl = GlobalConfiguration.Instance.BasePath;
} }
/// <summary> /// <summary>
@ -260,14 +262,14 @@ namespace {{packageName}}.Client
/// <summary> /// <summary>
/// Provides all logic for constructing a new RestSharp <see cref="RestRequest"/>. /// Provides all logic for constructing a new RestSharp <see cref="RestRequest"/>.
/// At this point, all information for querying the service is known. Here, it is simply /// At this point, all information for querying the service is known.
/// mapped into the RestSharp request. /// Here, it is simply mapped into the RestSharp request.
/// </summary> /// </summary>
/// <param name="method">The http verb.</param> /// <param name="method">The http verb.</param>
/// <param name="path">The target path (or resource).</param> /// <param name="path">The target path (or resource).</param>
/// <param name="options">The additional request options.</param> /// <param name="options">The additional request options.</param>
/// <param name="configuration">A per-request configuration object. It is assumed that any merge with /// <param name="configuration">A per-request configuration object.
/// GlobalConfiguration has been done before calling this method.</param> /// It is assumed that any merge with GlobalConfiguration has been done before calling this method.</param>
/// <returns>[private] A new RestRequest instance.</returns> /// <returns>[private] A new RestRequest instance.</returns>
/// <exception cref="ArgumentNullException"></exception> /// <exception cref="ArgumentNullException"></exception>
private RestRequest NewRequest( private RestRequest NewRequest(
@ -375,7 +377,7 @@ namespace {{packageName}}.Client
var bytes = ClientUtils.ReadAsBytes(file); var bytes = ClientUtils.ReadAsBytes(file);
var fileStream = file as FileStream; var fileStream = file as FileStream;
if (fileStream != null) if (fileStream != null)
request.AddFile(fileParam.Key, bytes, System.IO.Path.GetFileName(fileStream.Name)); request.AddFile(fileParam.Key, bytes, Path.GetFileName(fileStream.Name));
else else
request.AddFile(fileParam.Key, bytes, "no_file_name_provided"); request.AddFile(fileParam.Key, bytes, "no_file_name_provided");
} }
@ -385,6 +387,13 @@ namespace {{packageName}}.Client
return request; return request;
} }
/// <summary>
/// Transforms a RestResponse instance into a new ApiResponse instance.
/// At this point, we have a concrete http response from the service.
/// Here, it is simply mapped into the [public] ApiResponse object.
/// </summary>
/// <param name="response">The RestSharp response object</param>
/// <returns>A new ApiResponse instance.</returns>
private ApiResponse<T> ToApiResponse<T>(RestResponse<T> response) private ApiResponse<T> ToApiResponse<T>(RestResponse<T> response)
{ {
T result = response.Data; T result = response.Data;
@ -429,31 +438,32 @@ namespace {{packageName}}.Client
return transformed; return transformed;
} }
private ApiResponse<T> Exec<T>(RestRequest request, RequestOptions options, IReadableConfiguration configuration) /// <summary>
/// Executes the HTTP request for the current service.
/// Based on functions received it can be async or sync.
/// </summary>
/// <param name="getResponse">Local function that executes http request and returns http response.</param>
/// <param name="setOptions">Local function to specify options for the service.</param>
/// <param name="request">The RestSharp request object</param>
/// <param name="options">The RestSharp options object</param>
/// <param name="configuration">A per-request configuration object.
/// It is assumed that any merge with GlobalConfiguration has been done before calling this method.</param>
/// <returns>A new ApiResponse instance.</returns>
private ApiResponse<T> ExecClient<T>(Func<RestClient, RestResponse<T>> getResponse, Action<RestClientOptions> setOptions, RestRequest request, RequestOptions options, IReadableConfiguration configuration)
{ {
var baseUrl = configuration.GetOperationServerUrl(options.Operation, options.OperationIndex) ?? _baseUrl; var baseUrl = configuration.GetOperationServerUrl(options.Operation, options.OperationIndex) ?? _baseUrl;
var cookies = new CookieContainer();
if (options.Cookies != null && options.Cookies.Count > 0)
{
foreach (var cookie in options.Cookies)
{
cookies.Add(new Cookie(cookie.Name, cookie.Value));
}
}
var clientOptions = new RestClientOptions(baseUrl) var clientOptions = new RestClientOptions(baseUrl)
{ {
ClientCertificates = configuration.ClientCertificates, ClientCertificates = configuration.ClientCertificates,
CookieContainer = cookies,
MaxTimeout = configuration.Timeout, MaxTimeout = configuration.Timeout,
Proxy = configuration.Proxy, Proxy = configuration.Proxy,
UserAgent = configuration.UserAgent, UserAgent = configuration.UserAgent,
UseDefaultCredentials = configuration.UseDefaultCredentials, UseDefaultCredentials = configuration.UseDefaultCredentials,
RemoteCertificateValidationCallback = configuration.RemoteCertificateValidationCallback RemoteCertificateValidationCallback = configuration.RemoteCertificateValidationCallback
}; };
setOptions(clientOptions);
{{#hasOAuthMethods}} {{#hasOAuthMethods}}
if (!string.IsNullOrEmpty(configuration.OAuthTokenUrl) && if (!string.IsNullOrEmpty(configuration.OAuthTokenUrl) &&
!string.IsNullOrEmpty(configuration.OAuthClientId) && !string.IsNullOrEmpty(configuration.OAuthClientId) &&
@ -475,27 +485,14 @@ namespace {{packageName}}.Client
{ {
InterceptRequest(request); InterceptRequest(request);
RestResponse<T> response; RestResponse<T> response = getResponse(client);
if (RetryConfiguration.RetryPolicy != null)
{
var policy = RetryConfiguration.RetryPolicy;
var policyResult = policy.ExecuteAndCapture(() => client.Execute(request));
response = (policyResult.Outcome == OutcomeType.Successful) ? client.Deserialize<T>(policyResult.Result) : new RestResponse<T>(request)
{
ErrorException = policyResult.FinalException
};
}
else
{
response = client.Execute<T>(request);
}
// if the response type is oneOf/anyOf, call FromJSON to deserialize the data // if the response type is oneOf/anyOf, call FromJSON to deserialize the data
if (typeof({{{packageName}}}.{{modelPackage}}.AbstractOpenAPISchema).IsAssignableFrom(typeof(T))) if (typeof(AbstractOpenAPISchema).IsAssignableFrom(typeof(T)))
{ {
try try
{ {
response.Data = (T) typeof(T).GetMethod("FromJson").Invoke(null, new object[] { response.Content }); response.Data = (T)typeof(T).GetMethod("FromJson").Invoke(null, new object[] { response.Content });
} }
catch (Exception ex) catch (Exception ex)
{ {
@ -553,111 +550,85 @@ namespace {{packageName}}.Client
} }
} }
{{#supportsAsync}} private RestResponse<T> DeserializeRestResponseFromPolicy<T>(RestClient client, RestRequest request, PolicyResult<RestResponse> policyResult)
private async Task<ApiResponse<T>> ExecAsync<T>(RestRequest request, RequestOptions options, IReadableConfiguration configuration, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken))
{ {
var baseUrl = configuration.GetOperationServerUrl(options.Operation, options.OperationIndex) ?? _baseUrl; if (policyResult.Outcome == OutcomeType.Successful)
var clientOptions = new RestClientOptions(baseUrl)
{ {
ClientCertificates = configuration.ClientCertificates, return client.Deserialize<T>(policyResult.Result);
MaxTimeout = configuration.Timeout, }
Proxy = configuration.Proxy, else
UserAgent = configuration.UserAgent, {
UseDefaultCredentials = configuration.UseDefaultCredentials, return new RestResponse<T>(request)
RemoteCertificateValidationCallback = configuration.RemoteCertificateValidationCallback {
ErrorException = policyResult.FinalException
};
}
}
private ApiResponse<T> Exec<T>(RestRequest request, RequestOptions options, IReadableConfiguration configuration)
{
Action<RestClientOptions> setOptions = (clientOptions) =>
{
var cookies = new CookieContainer();
if (options.Cookies != null && options.Cookies.Count > 0)
{
foreach (var cookie in options.Cookies)
{
cookies.Add(new Cookie(cookie.Name, cookie.Value));
}
}
clientOptions.CookieContainer = cookies;
}; };
{{#hasOAuthMethods}} Func<RestClient, RestResponse<T>> getResponse = (client) =>
if (!string.IsNullOrEmpty(configuration.OAuthTokenUrl) &&
!string.IsNullOrEmpty(configuration.OAuthClientId) &&
!string.IsNullOrEmpty(configuration.OAuthClientSecret) &&
configuration.OAuthFlow != null)
{ {
clientOptions.Authenticator = new OAuthAuthenticator( if (RetryConfiguration.RetryPolicy != null)
configuration.OAuthTokenUrl,
configuration.OAuthClientId,
configuration.OAuthClientSecret,
configuration.OAuthFlow,
SerializerSettings,
configuration);
}
{{/hasOAuthMethods}}
using (RestClient client = new RestClient(clientOptions,
configureSerialization: serializerConfig => serializerConfig.UseSerializer(() => new CustomJsonCodec(SerializerSettings, configuration))))
{
InterceptRequest(request);
RestResponse<T> response;
{{#supportsRetry}}
if (RetryConfiguration.AsyncRetryPolicy != null)
{ {
var policy = RetryConfiguration.AsyncRetryPolicy; var policy = RetryConfiguration.RetryPolicy;
var policyResult = await policy.ExecuteAndCaptureAsync((ct) => client.ExecuteAsync(request, ct), cancellationToken).ConfigureAwait(false); var policyResult = policy.ExecuteAndCapture(() => client.Execute(request));
response = (policyResult.Outcome == OutcomeType.Successful) ? client.Deserialize<T>(policyResult.Result) : new RestResponse<T>(request) return DeserializeRestResponseFromPolicy<T>(client, request, policyResult);
{
ErrorException = policyResult.FinalException
};
} }
else else
{ {
{{/supportsRetry}} return client.Execute<T>(request);
response = await client.ExecuteAsync<T>(request, cancellationToken).ConfigureAwait(false);
{{#supportsRetry}}
} }
{{/supportsRetry}} };
// if the response type is oneOf/anyOf, call FromJSON to deserialize the data return ExecClient(getResponse, setOptions, request, options, configuration);
if (typeof({{{packageName}}}.{{modelPackage}}.AbstractOpenAPISchema).IsAssignableFrom(typeof(T))) }
{
response.Data = (T) typeof(T).GetMethod("FromJson").Invoke(null, new object[] { response.Content });
}
else if (typeof(T).Name == "Stream") // for binary response
{
response.Data = (T)(object)new MemoryStream(response.RawBytes);
}
else if (typeof(T).Name == "Byte[]") // for byte response
{
response.Data = (T)(object)response.RawBytes;
}
InterceptResponse(request, response); {{#supportsAsync}}
private Task<ApiResponse<T>> ExecAsync<T>(RestRequest request, RequestOptions options, IReadableConfiguration configuration, CancellationToken cancellationToken = default(CancellationToken))
{
Action<RestClientOptions> setOptions = (clientOptions) =>
{
//no extra options
};
var result = ToApiResponse(response); Func<RestClient, RestResponse<T>> getResponse = (client) =>
if (response.ErrorMessage != null) {
Func<Task<RestResponse<T>>> action = async () =>
{ {
result.ErrorText = response.ErrorMessage; {{#supportsRetry}}
} if (RetryConfiguration.AsyncRetryPolicy != null)
if (response.Cookies != null && response.Cookies.Count > 0)
{
if (result.Cookies == null) result.Cookies = new List<Cookie>();
foreach (var restResponseCookie in response.Cookies.Cast<Cookie>())
{ {
var cookie = new Cookie( var policy = RetryConfiguration.AsyncRetryPolicy;
restResponseCookie.Name, var policyResult = await policy.ExecuteAndCaptureAsync((ct) => client.ExecuteAsync(request, ct), cancellationToken).ConfigureAwait(false);
restResponseCookie.Value, return DeserializeRestResponseFromPolicy<T>(client, request, policyResult);
restResponseCookie.Path,
restResponseCookie.Domain
)
{
Comment = restResponseCookie.Comment,
CommentUri = restResponseCookie.CommentUri,
Discard = restResponseCookie.Discard,
Expired = restResponseCookie.Expired,
Expires = restResponseCookie.Expires,
HttpOnly = restResponseCookie.HttpOnly,
Port = restResponseCookie.Port,
Secure = restResponseCookie.Secure,
Version = restResponseCookie.Version
};
result.Cookies.Add(cookie);
} }
} else
return result; {
} {{/supportsRetry}}
return await client.ExecuteAsync<T>(request, cancellationToken).ConfigureAwait(false);
{{#supportsRetry}}
}
{{/supportsRetry}}
};
return action().Result;
};
return Task.FromResult<ApiResponse<T>>(ExecClient(getResponse, setOptions, request, options, configuration));
} }
#region IAsynchronousClient #region IAsynchronousClient
@ -670,7 +641,7 @@ namespace {{packageName}}.Client
/// GlobalConfiguration has been done before calling this method.</param> /// GlobalConfiguration has been done before calling this method.</param>
/// <param name="cancellationToken">Token that enables callers to cancel the request.</param> /// <param name="cancellationToken">Token that enables callers to cancel the request.</param>
/// <returns>A Task containing ApiResponse</returns> /// <returns>A Task containing ApiResponse</returns>
public Task<ApiResponse<T>> GetAsync<T>(string path, RequestOptions options, IReadableConfiguration configuration = null, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) public Task<ApiResponse<T>> GetAsync<T>(string path, RequestOptions options, IReadableConfiguration configuration = null, CancellationToken cancellationToken = default)
{ {
var config = configuration ?? GlobalConfiguration.Instance; var config = configuration ?? GlobalConfiguration.Instance;
return ExecAsync<T>(NewRequest(HttpMethod.Get, path, options, config), options, config, cancellationToken); return ExecAsync<T>(NewRequest(HttpMethod.Get, path, options, config), options, config, cancellationToken);
@ -685,7 +656,7 @@ namespace {{packageName}}.Client
/// GlobalConfiguration has been done before calling this method.</param> /// GlobalConfiguration has been done before calling this method.</param>
/// <param name="cancellationToken">Token that enables callers to cancel the request.</param> /// <param name="cancellationToken">Token that enables callers to cancel the request.</param>
/// <returns>A Task containing ApiResponse</returns> /// <returns>A Task containing ApiResponse</returns>
public Task<ApiResponse<T>> PostAsync<T>(string path, RequestOptions options, IReadableConfiguration configuration = null, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) public Task<ApiResponse<T>> PostAsync<T>(string path, RequestOptions options, IReadableConfiguration configuration = null, CancellationToken cancellationToken = default)
{ {
var config = configuration ?? GlobalConfiguration.Instance; var config = configuration ?? GlobalConfiguration.Instance;
return ExecAsync<T>(NewRequest(HttpMethod.Post, path, options, config), options, config, cancellationToken); return ExecAsync<T>(NewRequest(HttpMethod.Post, path, options, config), options, config, cancellationToken);
@ -700,7 +671,7 @@ namespace {{packageName}}.Client
/// GlobalConfiguration has been done before calling this method.</param> /// GlobalConfiguration has been done before calling this method.</param>
/// <param name="cancellationToken">Token that enables callers to cancel the request.</param> /// <param name="cancellationToken">Token that enables callers to cancel the request.</param>
/// <returns>A Task containing ApiResponse</returns> /// <returns>A Task containing ApiResponse</returns>
public Task<ApiResponse<T>> PutAsync<T>(string path, RequestOptions options, IReadableConfiguration configuration = null, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) public Task<ApiResponse<T>> PutAsync<T>(string path, RequestOptions options, IReadableConfiguration configuration = null, CancellationToken cancellationToken = default)
{ {
var config = configuration ?? GlobalConfiguration.Instance; var config = configuration ?? GlobalConfiguration.Instance;
return ExecAsync<T>(NewRequest(HttpMethod.Put, path, options, config), options, config, cancellationToken); return ExecAsync<T>(NewRequest(HttpMethod.Put, path, options, config), options, config, cancellationToken);
@ -715,7 +686,7 @@ namespace {{packageName}}.Client
/// GlobalConfiguration has been done before calling this method.</param> /// GlobalConfiguration has been done before calling this method.</param>
/// <param name="cancellationToken">Token that enables callers to cancel the request.</param> /// <param name="cancellationToken">Token that enables callers to cancel the request.</param>
/// <returns>A Task containing ApiResponse</returns> /// <returns>A Task containing ApiResponse</returns>
public Task<ApiResponse<T>> DeleteAsync<T>(string path, RequestOptions options, IReadableConfiguration configuration = null, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) public Task<ApiResponse<T>> DeleteAsync<T>(string path, RequestOptions options, IReadableConfiguration configuration = null, CancellationToken cancellationToken = default)
{ {
var config = configuration ?? GlobalConfiguration.Instance; var config = configuration ?? GlobalConfiguration.Instance;
return ExecAsync<T>(NewRequest(HttpMethod.Delete, path, options, config), options, config, cancellationToken); return ExecAsync<T>(NewRequest(HttpMethod.Delete, path, options, config), options, config, cancellationToken);
@ -730,7 +701,7 @@ namespace {{packageName}}.Client
/// GlobalConfiguration has been done before calling this method.</param> /// GlobalConfiguration has been done before calling this method.</param>
/// <param name="cancellationToken">Token that enables callers to cancel the request.</param> /// <param name="cancellationToken">Token that enables callers to cancel the request.</param>
/// <returns>A Task containing ApiResponse</returns> /// <returns>A Task containing ApiResponse</returns>
public Task<ApiResponse<T>> HeadAsync<T>(string path, RequestOptions options, IReadableConfiguration configuration = null, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) public Task<ApiResponse<T>> HeadAsync<T>(string path, RequestOptions options, IReadableConfiguration configuration = null, CancellationToken cancellationToken = default)
{ {
var config = configuration ?? GlobalConfiguration.Instance; var config = configuration ?? GlobalConfiguration.Instance;
return ExecAsync<T>(NewRequest(HttpMethod.Head, path, options, config), options, config, cancellationToken); return ExecAsync<T>(NewRequest(HttpMethod.Head, path, options, config), options, config, cancellationToken);
@ -745,7 +716,7 @@ namespace {{packageName}}.Client
/// GlobalConfiguration has been done before calling this method.</param> /// GlobalConfiguration has been done before calling this method.</param>
/// <param name="cancellationToken">Token that enables callers to cancel the request.</param> /// <param name="cancellationToken">Token that enables callers to cancel the request.</param>
/// <returns>A Task containing ApiResponse</returns> /// <returns>A Task containing ApiResponse</returns>
public Task<ApiResponse<T>> OptionsAsync<T>(string path, RequestOptions options, IReadableConfiguration configuration = null, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) public Task<ApiResponse<T>> OptionsAsync<T>(string path, RequestOptions options, IReadableConfiguration configuration = null, CancellationToken cancellationToken = default)
{ {
var config = configuration ?? GlobalConfiguration.Instance; var config = configuration ?? GlobalConfiguration.Instance;
return ExecAsync<T>(NewRequest(HttpMethod.Options, path, options, config), options, config, cancellationToken); return ExecAsync<T>(NewRequest(HttpMethod.Options, path, options, config), options, config, cancellationToken);
@ -760,7 +731,7 @@ namespace {{packageName}}.Client
/// GlobalConfiguration has been done before calling this method.</param> /// GlobalConfiguration has been done before calling this method.</param>
/// <param name="cancellationToken">Token that enables callers to cancel the request.</param> /// <param name="cancellationToken">Token that enables callers to cancel the request.</param>
/// <returns>A Task containing ApiResponse</returns> /// <returns>A Task containing ApiResponse</returns>
public Task<ApiResponse<T>> PatchAsync<T>(string path, RequestOptions options, IReadableConfiguration configuration = null, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) public Task<ApiResponse<T>> PatchAsync<T>(string path, RequestOptions options, IReadableConfiguration configuration = null, CancellationToken cancellationToken = default)
{ {
var config = configuration ?? GlobalConfiguration.Instance; var config = configuration ?? GlobalConfiguration.Instance;
return ExecAsync<T>(NewRequest(HttpMethod.Patch, path, options, config), options, config, cancellationToken); return ExecAsync<T>(NewRequest(HttpMethod.Patch, path, options, config), options, config, cancellationToken);

View File

@ -23,13 +23,14 @@ using System.Text;
using System.Threading; using System.Threading;
using System.Text.RegularExpressions; using System.Text.RegularExpressions;
using System.Threading.Tasks; using System.Threading.Tasks;
using System.Web;
using Newtonsoft.Json; using Newtonsoft.Json;
using Newtonsoft.Json.Serialization; using Newtonsoft.Json.Serialization;
using RestSharp; using RestSharp;
using RestSharp.Serializers; using RestSharp.Serializers;
using RestSharpMethod = RestSharp.Method; using RestSharpMethod = RestSharp.Method;
using FileIO = System.IO.File;
using Polly; using Polly;
using Org.OpenAPITools.Model;
namespace Org.OpenAPITools.Client namespace Org.OpenAPITools.Client
{ {
@ -70,10 +71,10 @@ namespace Org.OpenAPITools.Client
/// <returns>A JSON string.</returns> /// <returns>A JSON string.</returns>
public string Serialize(object obj) public string Serialize(object obj)
{ {
if (obj != null && obj is Org.OpenAPITools.Model.AbstractOpenAPISchema) if (obj != null && obj is AbstractOpenAPISchema)
{ {
// the object to be serialized is an oneOf/anyOf schema // the object to be serialized is an oneOf/anyOf schema
return ((Org.OpenAPITools.Model.AbstractOpenAPISchema)obj).ToJson(); return ((AbstractOpenAPISchema)obj).ToJson();
} }
else else
{ {
@ -118,7 +119,7 @@ namespace Org.OpenAPITools.Client
if (match.Success) if (match.Success)
{ {
string fileName = filePath + ClientUtils.SanitizeFilename(match.Groups[1].Value.Replace("\"", "").Replace("'", "")); string fileName = filePath + ClientUtils.SanitizeFilename(match.Groups[1].Value.Replace("\"", "").Replace("'", ""));
File.WriteAllBytes(fileName, bytes); FileIO.WriteAllBytes(fileName, bytes);
return new FileStream(fileName, FileMode.Open); return new FileStream(fileName, FileMode.Open);
} }
} }
@ -129,7 +130,7 @@ namespace Org.OpenAPITools.Client
if (type.Name.StartsWith("System.Nullable`1[[System.DateTime")) // return a datetime object if (type.Name.StartsWith("System.Nullable`1[[System.DateTime")) // return a datetime object
{ {
return DateTime.Parse(response.Content, null, System.Globalization.DateTimeStyles.RoundtripKind); return DateTime.Parse(response.Content, null, DateTimeStyles.RoundtripKind);
} }
if (type == typeof(string) || type.Name.StartsWith("System.Nullable")) // return primitive type if (type == typeof(string) || type.Name.StartsWith("System.Nullable")) // return primitive type
@ -151,13 +152,13 @@ namespace Org.OpenAPITools.Client
public ISerializer Serializer => this; public ISerializer Serializer => this;
public IDeserializer Deserializer => this; public IDeserializer Deserializer => this;
public string[] AcceptedContentTypes => RestSharp.ContentType.JsonAccept; public string[] AcceptedContentTypes => ContentType.JsonAccept;
public SupportsContentType SupportsContentType => contentType => public SupportsContentType SupportsContentType => contentType =>
contentType.Value.EndsWith("json", StringComparison.InvariantCultureIgnoreCase) || contentType.Value.EndsWith("json", StringComparison.InvariantCultureIgnoreCase) ||
contentType.Value.EndsWith("javascript", StringComparison.InvariantCultureIgnoreCase); contentType.Value.EndsWith("javascript", StringComparison.InvariantCultureIgnoreCase);
public ContentType ContentType { get; set; } = RestSharp.ContentType.Json; public ContentType ContentType { get; set; } = ContentType.Json;
public DataFormat DataFormat => DataFormat.Json; public DataFormat DataFormat => DataFormat.Json;
} }
@ -204,7 +205,7 @@ namespace Org.OpenAPITools.Client
/// </summary> /// </summary>
public ApiClient() public ApiClient()
{ {
_baseUrl = Org.OpenAPITools.Client.GlobalConfiguration.Instance.BasePath; _baseUrl = GlobalConfiguration.Instance.BasePath;
} }
/// <summary> /// <summary>
@ -261,14 +262,14 @@ namespace Org.OpenAPITools.Client
/// <summary> /// <summary>
/// Provides all logic for constructing a new RestSharp <see cref="RestRequest"/>. /// Provides all logic for constructing a new RestSharp <see cref="RestRequest"/>.
/// At this point, all information for querying the service is known. Here, it is simply /// At this point, all information for querying the service is known.
/// mapped into the RestSharp request. /// Here, it is simply mapped into the RestSharp request.
/// </summary> /// </summary>
/// <param name="method">The http verb.</param> /// <param name="method">The http verb.</param>
/// <param name="path">The target path (or resource).</param> /// <param name="path">The target path (or resource).</param>
/// <param name="options">The additional request options.</param> /// <param name="options">The additional request options.</param>
/// <param name="configuration">A per-request configuration object. It is assumed that any merge with /// <param name="configuration">A per-request configuration object.
/// GlobalConfiguration has been done before calling this method.</param> /// It is assumed that any merge with GlobalConfiguration has been done before calling this method.</param>
/// <returns>[private] A new RestRequest instance.</returns> /// <returns>[private] A new RestRequest instance.</returns>
/// <exception cref="ArgumentNullException"></exception> /// <exception cref="ArgumentNullException"></exception>
private RestRequest NewRequest( private RestRequest NewRequest(
@ -376,7 +377,7 @@ namespace Org.OpenAPITools.Client
var bytes = ClientUtils.ReadAsBytes(file); var bytes = ClientUtils.ReadAsBytes(file);
var fileStream = file as FileStream; var fileStream = file as FileStream;
if (fileStream != null) if (fileStream != null)
request.AddFile(fileParam.Key, bytes, System.IO.Path.GetFileName(fileStream.Name)); request.AddFile(fileParam.Key, bytes, Path.GetFileName(fileStream.Name));
else else
request.AddFile(fileParam.Key, bytes, "no_file_name_provided"); request.AddFile(fileParam.Key, bytes, "no_file_name_provided");
} }
@ -386,6 +387,13 @@ namespace Org.OpenAPITools.Client
return request; return request;
} }
/// <summary>
/// Transforms a RestResponse instance into a new ApiResponse instance.
/// At this point, we have a concrete http response from the service.
/// Here, it is simply mapped into the [public] ApiResponse object.
/// </summary>
/// <param name="response">The RestSharp response object</param>
/// <returns>A new ApiResponse instance.</returns>
private ApiResponse<T> ToApiResponse<T>(RestResponse<T> response) private ApiResponse<T> ToApiResponse<T>(RestResponse<T> response)
{ {
T result = response.Data; T result = response.Data;
@ -430,57 +438,45 @@ namespace Org.OpenAPITools.Client
return transformed; return transformed;
} }
private ApiResponse<T> Exec<T>(RestRequest request, RequestOptions options, IReadableConfiguration configuration) /// <summary>
/// Executes the HTTP request for the current service.
/// Based on functions received it can be async or sync.
/// </summary>
/// <param name="getResponse">Local function that executes http request and returns http response.</param>
/// <param name="setOptions">Local function to specify options for the service.</param>
/// <param name="request">The RestSharp request object</param>
/// <param name="options">The RestSharp options object</param>
/// <param name="configuration">A per-request configuration object.
/// It is assumed that any merge with GlobalConfiguration has been done before calling this method.</param>
/// <returns>A new ApiResponse instance.</returns>
private ApiResponse<T> ExecClient<T>(Func<RestClient, RestResponse<T>> getResponse, Action<RestClientOptions> setOptions, RestRequest request, RequestOptions options, IReadableConfiguration configuration)
{ {
var baseUrl = configuration.GetOperationServerUrl(options.Operation, options.OperationIndex) ?? _baseUrl; var baseUrl = configuration.GetOperationServerUrl(options.Operation, options.OperationIndex) ?? _baseUrl;
var cookies = new CookieContainer();
if (options.Cookies != null && options.Cookies.Count > 0)
{
foreach (var cookie in options.Cookies)
{
cookies.Add(new Cookie(cookie.Name, cookie.Value));
}
}
var clientOptions = new RestClientOptions(baseUrl) var clientOptions = new RestClientOptions(baseUrl)
{ {
ClientCertificates = configuration.ClientCertificates, ClientCertificates = configuration.ClientCertificates,
CookieContainer = cookies,
MaxTimeout = configuration.Timeout, MaxTimeout = configuration.Timeout,
Proxy = configuration.Proxy, Proxy = configuration.Proxy,
UserAgent = configuration.UserAgent, UserAgent = configuration.UserAgent,
UseDefaultCredentials = configuration.UseDefaultCredentials, UseDefaultCredentials = configuration.UseDefaultCredentials,
RemoteCertificateValidationCallback = configuration.RemoteCertificateValidationCallback RemoteCertificateValidationCallback = configuration.RemoteCertificateValidationCallback
}; };
setOptions(clientOptions);
using (RestClient client = new RestClient(clientOptions, using (RestClient client = new RestClient(clientOptions,
configureSerialization: serializerConfig => serializerConfig.UseSerializer(() => new CustomJsonCodec(SerializerSettings, configuration)))) configureSerialization: serializerConfig => serializerConfig.UseSerializer(() => new CustomJsonCodec(SerializerSettings, configuration))))
{ {
InterceptRequest(request); InterceptRequest(request);
RestResponse<T> response; RestResponse<T> response = getResponse(client);
if (RetryConfiguration.RetryPolicy != null)
{
var policy = RetryConfiguration.RetryPolicy;
var policyResult = policy.ExecuteAndCapture(() => client.Execute(request));
response = (policyResult.Outcome == OutcomeType.Successful) ? client.Deserialize<T>(policyResult.Result) : new RestResponse<T>(request)
{
ErrorException = policyResult.FinalException
};
}
else
{
response = client.Execute<T>(request);
}
// if the response type is oneOf/anyOf, call FromJSON to deserialize the data // if the response type is oneOf/anyOf, call FromJSON to deserialize the data
if (typeof(Org.OpenAPITools.Model.AbstractOpenAPISchema).IsAssignableFrom(typeof(T))) if (typeof(AbstractOpenAPISchema).IsAssignableFrom(typeof(T)))
{ {
try try
{ {
response.Data = (T) typeof(T).GetMethod("FromJson").Invoke(null, new object[] { response.Content }); response.Data = (T)typeof(T).GetMethod("FromJson").Invoke(null, new object[] { response.Content });
} }
catch (Exception ex) catch (Exception ex)
{ {
@ -538,90 +534,80 @@ namespace Org.OpenAPITools.Client
} }
} }
private async Task<ApiResponse<T>> ExecAsync<T>(RestRequest request, RequestOptions options, IReadableConfiguration configuration, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) private RestResponse<T> DeserializeRestResponseFromPolicy<T>(RestClient client, RestRequest request, PolicyResult<RestResponse> policyResult)
{ {
var baseUrl = configuration.GetOperationServerUrl(options.Operation, options.OperationIndex) ?? _baseUrl; if (policyResult.Outcome == OutcomeType.Successful)
var clientOptions = new RestClientOptions(baseUrl)
{ {
ClientCertificates = configuration.ClientCertificates, return client.Deserialize<T>(policyResult.Result);
MaxTimeout = configuration.Timeout, }
Proxy = configuration.Proxy, else
UserAgent = configuration.UserAgent, {
UseDefaultCredentials = configuration.UseDefaultCredentials, return new RestResponse<T>(request)
RemoteCertificateValidationCallback = configuration.RemoteCertificateValidationCallback {
ErrorException = policyResult.FinalException
};
}
}
private ApiResponse<T> Exec<T>(RestRequest request, RequestOptions options, IReadableConfiguration configuration)
{
Action<RestClientOptions> setOptions = (clientOptions) =>
{
var cookies = new CookieContainer();
if (options.Cookies != null && options.Cookies.Count > 0)
{
foreach (var cookie in options.Cookies)
{
cookies.Add(new Cookie(cookie.Name, cookie.Value));
}
}
clientOptions.CookieContainer = cookies;
}; };
using (RestClient client = new RestClient(clientOptions, Func<RestClient, RestResponse<T>> getResponse = (client) =>
configureSerialization: serializerConfig => serializerConfig.UseSerializer(() => new CustomJsonCodec(SerializerSettings, configuration))))
{ {
InterceptRequest(request); if (RetryConfiguration.RetryPolicy != null)
RestResponse<T> response;
if (RetryConfiguration.AsyncRetryPolicy != null)
{ {
var policy = RetryConfiguration.AsyncRetryPolicy; var policy = RetryConfiguration.RetryPolicy;
var policyResult = await policy.ExecuteAndCaptureAsync((ct) => client.ExecuteAsync(request, ct), cancellationToken).ConfigureAwait(false); var policyResult = policy.ExecuteAndCapture(() => client.Execute(request));
response = (policyResult.Outcome == OutcomeType.Successful) ? client.Deserialize<T>(policyResult.Result) : new RestResponse<T>(request) return DeserializeRestResponseFromPolicy<T>(client, request, policyResult);
{
ErrorException = policyResult.FinalException
};
} }
else else
{ {
response = await client.ExecuteAsync<T>(request, cancellationToken).ConfigureAwait(false); return client.Execute<T>(request);
} }
};
// if the response type is oneOf/anyOf, call FromJSON to deserialize the data return ExecClient(getResponse, setOptions, request, options, configuration);
if (typeof(Org.OpenAPITools.Model.AbstractOpenAPISchema).IsAssignableFrom(typeof(T))) }
{
response.Data = (T) typeof(T).GetMethod("FromJson").Invoke(null, new object[] { response.Content });
}
else if (typeof(T).Name == "Stream") // for binary response
{
response.Data = (T)(object)new MemoryStream(response.RawBytes);
}
else if (typeof(T).Name == "Byte[]") // for byte response
{
response.Data = (T)(object)response.RawBytes;
}
InterceptResponse(request, response); private Task<ApiResponse<T>> ExecAsync<T>(RestRequest request, RequestOptions options, IReadableConfiguration configuration, CancellationToken cancellationToken = default(CancellationToken))
{
Action<RestClientOptions> setOptions = (clientOptions) =>
{
//no extra options
};
var result = ToApiResponse(response); Func<RestClient, RestResponse<T>> getResponse = (client) =>
if (response.ErrorMessage != null) {
Func<Task<RestResponse<T>>> action = async () =>
{ {
result.ErrorText = response.ErrorMessage; if (RetryConfiguration.AsyncRetryPolicy != null)
}
if (response.Cookies != null && response.Cookies.Count > 0)
{
if (result.Cookies == null) result.Cookies = new List<Cookie>();
foreach (var restResponseCookie in response.Cookies.Cast<Cookie>())
{ {
var cookie = new Cookie( var policy = RetryConfiguration.AsyncRetryPolicy;
restResponseCookie.Name, var policyResult = await policy.ExecuteAndCaptureAsync((ct) => client.ExecuteAsync(request, ct), cancellationToken).ConfigureAwait(false);
restResponseCookie.Value, return DeserializeRestResponseFromPolicy<T>(client, request, policyResult);
restResponseCookie.Path,
restResponseCookie.Domain
)
{
Comment = restResponseCookie.Comment,
CommentUri = restResponseCookie.CommentUri,
Discard = restResponseCookie.Discard,
Expired = restResponseCookie.Expired,
Expires = restResponseCookie.Expires,
HttpOnly = restResponseCookie.HttpOnly,
Port = restResponseCookie.Port,
Secure = restResponseCookie.Secure,
Version = restResponseCookie.Version
};
result.Cookies.Add(cookie);
} }
} else
return result; {
} return await client.ExecuteAsync<T>(request, cancellationToken).ConfigureAwait(false);
}
};
return action().Result;
};
return Task.FromResult<ApiResponse<T>>(ExecClient(getResponse, setOptions, request, options, configuration));
} }
#region IAsynchronousClient #region IAsynchronousClient
@ -634,7 +620,7 @@ namespace Org.OpenAPITools.Client
/// GlobalConfiguration has been done before calling this method.</param> /// GlobalConfiguration has been done before calling this method.</param>
/// <param name="cancellationToken">Token that enables callers to cancel the request.</param> /// <param name="cancellationToken">Token that enables callers to cancel the request.</param>
/// <returns>A Task containing ApiResponse</returns> /// <returns>A Task containing ApiResponse</returns>
public Task<ApiResponse<T>> GetAsync<T>(string path, RequestOptions options, IReadableConfiguration configuration = null, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) public Task<ApiResponse<T>> GetAsync<T>(string path, RequestOptions options, IReadableConfiguration configuration = null, CancellationToken cancellationToken = default)
{ {
var config = configuration ?? GlobalConfiguration.Instance; var config = configuration ?? GlobalConfiguration.Instance;
return ExecAsync<T>(NewRequest(HttpMethod.Get, path, options, config), options, config, cancellationToken); return ExecAsync<T>(NewRequest(HttpMethod.Get, path, options, config), options, config, cancellationToken);
@ -649,7 +635,7 @@ namespace Org.OpenAPITools.Client
/// GlobalConfiguration has been done before calling this method.</param> /// GlobalConfiguration has been done before calling this method.</param>
/// <param name="cancellationToken">Token that enables callers to cancel the request.</param> /// <param name="cancellationToken">Token that enables callers to cancel the request.</param>
/// <returns>A Task containing ApiResponse</returns> /// <returns>A Task containing ApiResponse</returns>
public Task<ApiResponse<T>> PostAsync<T>(string path, RequestOptions options, IReadableConfiguration configuration = null, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) public Task<ApiResponse<T>> PostAsync<T>(string path, RequestOptions options, IReadableConfiguration configuration = null, CancellationToken cancellationToken = default)
{ {
var config = configuration ?? GlobalConfiguration.Instance; var config = configuration ?? GlobalConfiguration.Instance;
return ExecAsync<T>(NewRequest(HttpMethod.Post, path, options, config), options, config, cancellationToken); return ExecAsync<T>(NewRequest(HttpMethod.Post, path, options, config), options, config, cancellationToken);
@ -664,7 +650,7 @@ namespace Org.OpenAPITools.Client
/// GlobalConfiguration has been done before calling this method.</param> /// GlobalConfiguration has been done before calling this method.</param>
/// <param name="cancellationToken">Token that enables callers to cancel the request.</param> /// <param name="cancellationToken">Token that enables callers to cancel the request.</param>
/// <returns>A Task containing ApiResponse</returns> /// <returns>A Task containing ApiResponse</returns>
public Task<ApiResponse<T>> PutAsync<T>(string path, RequestOptions options, IReadableConfiguration configuration = null, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) public Task<ApiResponse<T>> PutAsync<T>(string path, RequestOptions options, IReadableConfiguration configuration = null, CancellationToken cancellationToken = default)
{ {
var config = configuration ?? GlobalConfiguration.Instance; var config = configuration ?? GlobalConfiguration.Instance;
return ExecAsync<T>(NewRequest(HttpMethod.Put, path, options, config), options, config, cancellationToken); return ExecAsync<T>(NewRequest(HttpMethod.Put, path, options, config), options, config, cancellationToken);
@ -679,7 +665,7 @@ namespace Org.OpenAPITools.Client
/// GlobalConfiguration has been done before calling this method.</param> /// GlobalConfiguration has been done before calling this method.</param>
/// <param name="cancellationToken">Token that enables callers to cancel the request.</param> /// <param name="cancellationToken">Token that enables callers to cancel the request.</param>
/// <returns>A Task containing ApiResponse</returns> /// <returns>A Task containing ApiResponse</returns>
public Task<ApiResponse<T>> DeleteAsync<T>(string path, RequestOptions options, IReadableConfiguration configuration = null, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) public Task<ApiResponse<T>> DeleteAsync<T>(string path, RequestOptions options, IReadableConfiguration configuration = null, CancellationToken cancellationToken = default)
{ {
var config = configuration ?? GlobalConfiguration.Instance; var config = configuration ?? GlobalConfiguration.Instance;
return ExecAsync<T>(NewRequest(HttpMethod.Delete, path, options, config), options, config, cancellationToken); return ExecAsync<T>(NewRequest(HttpMethod.Delete, path, options, config), options, config, cancellationToken);
@ -694,7 +680,7 @@ namespace Org.OpenAPITools.Client
/// GlobalConfiguration has been done before calling this method.</param> /// GlobalConfiguration has been done before calling this method.</param>
/// <param name="cancellationToken">Token that enables callers to cancel the request.</param> /// <param name="cancellationToken">Token that enables callers to cancel the request.</param>
/// <returns>A Task containing ApiResponse</returns> /// <returns>A Task containing ApiResponse</returns>
public Task<ApiResponse<T>> HeadAsync<T>(string path, RequestOptions options, IReadableConfiguration configuration = null, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) public Task<ApiResponse<T>> HeadAsync<T>(string path, RequestOptions options, IReadableConfiguration configuration = null, CancellationToken cancellationToken = default)
{ {
var config = configuration ?? GlobalConfiguration.Instance; var config = configuration ?? GlobalConfiguration.Instance;
return ExecAsync<T>(NewRequest(HttpMethod.Head, path, options, config), options, config, cancellationToken); return ExecAsync<T>(NewRequest(HttpMethod.Head, path, options, config), options, config, cancellationToken);
@ -709,7 +695,7 @@ namespace Org.OpenAPITools.Client
/// GlobalConfiguration has been done before calling this method.</param> /// GlobalConfiguration has been done before calling this method.</param>
/// <param name="cancellationToken">Token that enables callers to cancel the request.</param> /// <param name="cancellationToken">Token that enables callers to cancel the request.</param>
/// <returns>A Task containing ApiResponse</returns> /// <returns>A Task containing ApiResponse</returns>
public Task<ApiResponse<T>> OptionsAsync<T>(string path, RequestOptions options, IReadableConfiguration configuration = null, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) public Task<ApiResponse<T>> OptionsAsync<T>(string path, RequestOptions options, IReadableConfiguration configuration = null, CancellationToken cancellationToken = default)
{ {
var config = configuration ?? GlobalConfiguration.Instance; var config = configuration ?? GlobalConfiguration.Instance;
return ExecAsync<T>(NewRequest(HttpMethod.Options, path, options, config), options, config, cancellationToken); return ExecAsync<T>(NewRequest(HttpMethod.Options, path, options, config), options, config, cancellationToken);
@ -724,7 +710,7 @@ namespace Org.OpenAPITools.Client
/// GlobalConfiguration has been done before calling this method.</param> /// GlobalConfiguration has been done before calling this method.</param>
/// <param name="cancellationToken">Token that enables callers to cancel the request.</param> /// <param name="cancellationToken">Token that enables callers to cancel the request.</param>
/// <returns>A Task containing ApiResponse</returns> /// <returns>A Task containing ApiResponse</returns>
public Task<ApiResponse<T>> PatchAsync<T>(string path, RequestOptions options, IReadableConfiguration configuration = null, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) public Task<ApiResponse<T>> PatchAsync<T>(string path, RequestOptions options, IReadableConfiguration configuration = null, CancellationToken cancellationToken = default)
{ {
var config = configuration ?? GlobalConfiguration.Instance; var config = configuration ?? GlobalConfiguration.Instance;
return ExecAsync<T>(NewRequest(HttpMethod.Patch, path, options, config), options, config, cancellationToken); return ExecAsync<T>(NewRequest(HttpMethod.Patch, path, options, config), options, config, cancellationToken);

View File

@ -22,12 +22,15 @@ using System.Text;
using System.Threading; using System.Threading;
using System.Text.RegularExpressions; using System.Text.RegularExpressions;
using System.Threading.Tasks; using System.Threading.Tasks;
using System.Web;
using Newtonsoft.Json; using Newtonsoft.Json;
using Newtonsoft.Json.Serialization; using Newtonsoft.Json.Serialization;
using RestSharp; using RestSharp;
using RestSharp.Serializers; using RestSharp.Serializers;
using RestSharpMethod = RestSharp.Method; using RestSharpMethod = RestSharp.Method;
using FileIO = System.IO.File;
using Polly; using Polly;
using Org.OpenAPITools.Model;
namespace Org.OpenAPITools.Client namespace Org.OpenAPITools.Client
{ {
@ -68,10 +71,10 @@ namespace Org.OpenAPITools.Client
/// <returns>A JSON string.</returns> /// <returns>A JSON string.</returns>
public string Serialize(object obj) public string Serialize(object obj)
{ {
if (obj != null && obj is Org.OpenAPITools.Model.AbstractOpenAPISchema) if (obj != null && obj is AbstractOpenAPISchema)
{ {
// the object to be serialized is an oneOf/anyOf schema // the object to be serialized is an oneOf/anyOf schema
return ((Org.OpenAPITools.Model.AbstractOpenAPISchema)obj).ToJson(); return ((AbstractOpenAPISchema)obj).ToJson();
} }
else else
{ {
@ -116,7 +119,7 @@ namespace Org.OpenAPITools.Client
if (match.Success) if (match.Success)
{ {
string fileName = filePath + ClientUtils.SanitizeFilename(match.Groups[1].Value.Replace("\"", "").Replace("'", "")); string fileName = filePath + ClientUtils.SanitizeFilename(match.Groups[1].Value.Replace("\"", "").Replace("'", ""));
File.WriteAllBytes(fileName, bytes); FileIO.WriteAllBytes(fileName, bytes);
return new FileStream(fileName, FileMode.Open); return new FileStream(fileName, FileMode.Open);
} }
} }
@ -127,7 +130,7 @@ namespace Org.OpenAPITools.Client
if (type.Name.StartsWith("System.Nullable`1[[System.DateTime")) // return a datetime object if (type.Name.StartsWith("System.Nullable`1[[System.DateTime")) // return a datetime object
{ {
return DateTime.Parse(response.Content, null, System.Globalization.DateTimeStyles.RoundtripKind); return DateTime.Parse(response.Content, null, DateTimeStyles.RoundtripKind);
} }
if (type == typeof(string) || type.Name.StartsWith("System.Nullable")) // return primitive type if (type == typeof(string) || type.Name.StartsWith("System.Nullable")) // return primitive type
@ -149,13 +152,13 @@ namespace Org.OpenAPITools.Client
public ISerializer Serializer => this; public ISerializer Serializer => this;
public IDeserializer Deserializer => this; public IDeserializer Deserializer => this;
public string[] AcceptedContentTypes => RestSharp.ContentType.JsonAccept; public string[] AcceptedContentTypes => ContentType.JsonAccept;
public SupportsContentType SupportsContentType => contentType => public SupportsContentType SupportsContentType => contentType =>
contentType.Value.EndsWith("json", StringComparison.InvariantCultureIgnoreCase) || contentType.Value.EndsWith("json", StringComparison.InvariantCultureIgnoreCase) ||
contentType.Value.EndsWith("javascript", StringComparison.InvariantCultureIgnoreCase); contentType.Value.EndsWith("javascript", StringComparison.InvariantCultureIgnoreCase);
public ContentType ContentType { get; set; } = RestSharp.ContentType.Json; public ContentType ContentType { get; set; } = ContentType.Json;
public DataFormat DataFormat => DataFormat.Json; public DataFormat DataFormat => DataFormat.Json;
} }
@ -202,7 +205,7 @@ namespace Org.OpenAPITools.Client
/// </summary> /// </summary>
public ApiClient() public ApiClient()
{ {
_baseUrl = Org.OpenAPITools.Client.GlobalConfiguration.Instance.BasePath; _baseUrl = GlobalConfiguration.Instance.BasePath;
} }
/// <summary> /// <summary>
@ -259,14 +262,14 @@ namespace Org.OpenAPITools.Client
/// <summary> /// <summary>
/// Provides all logic for constructing a new RestSharp <see cref="RestRequest"/>. /// Provides all logic for constructing a new RestSharp <see cref="RestRequest"/>.
/// At this point, all information for querying the service is known. Here, it is simply /// At this point, all information for querying the service is known.
/// mapped into the RestSharp request. /// Here, it is simply mapped into the RestSharp request.
/// </summary> /// </summary>
/// <param name="method">The http verb.</param> /// <param name="method">The http verb.</param>
/// <param name="path">The target path (or resource).</param> /// <param name="path">The target path (or resource).</param>
/// <param name="options">The additional request options.</param> /// <param name="options">The additional request options.</param>
/// <param name="configuration">A per-request configuration object. It is assumed that any merge with /// <param name="configuration">A per-request configuration object.
/// GlobalConfiguration has been done before calling this method.</param> /// It is assumed that any merge with GlobalConfiguration has been done before calling this method.</param>
/// <returns>[private] A new RestRequest instance.</returns> /// <returns>[private] A new RestRequest instance.</returns>
/// <exception cref="ArgumentNullException"></exception> /// <exception cref="ArgumentNullException"></exception>
private RestRequest NewRequest( private RestRequest NewRequest(
@ -374,7 +377,7 @@ namespace Org.OpenAPITools.Client
var bytes = ClientUtils.ReadAsBytes(file); var bytes = ClientUtils.ReadAsBytes(file);
var fileStream = file as FileStream; var fileStream = file as FileStream;
if (fileStream != null) if (fileStream != null)
request.AddFile(fileParam.Key, bytes, System.IO.Path.GetFileName(fileStream.Name)); request.AddFile(fileParam.Key, bytes, Path.GetFileName(fileStream.Name));
else else
request.AddFile(fileParam.Key, bytes, "no_file_name_provided"); request.AddFile(fileParam.Key, bytes, "no_file_name_provided");
} }
@ -384,6 +387,13 @@ namespace Org.OpenAPITools.Client
return request; return request;
} }
/// <summary>
/// Transforms a RestResponse instance into a new ApiResponse instance.
/// At this point, we have a concrete http response from the service.
/// Here, it is simply mapped into the [public] ApiResponse object.
/// </summary>
/// <param name="response">The RestSharp response object</param>
/// <returns>A new ApiResponse instance.</returns>
private ApiResponse<T> ToApiResponse<T>(RestResponse<T> response) private ApiResponse<T> ToApiResponse<T>(RestResponse<T> response)
{ {
T result = response.Data; T result = response.Data;
@ -428,57 +438,45 @@ namespace Org.OpenAPITools.Client
return transformed; return transformed;
} }
private ApiResponse<T> Exec<T>(RestRequest request, RequestOptions options, IReadableConfiguration configuration) /// <summary>
/// Executes the HTTP request for the current service.
/// Based on functions received it can be async or sync.
/// </summary>
/// <param name="getResponse">Local function that executes http request and returns http response.</param>
/// <param name="setOptions">Local function to specify options for the service.</param>
/// <param name="request">The RestSharp request object</param>
/// <param name="options">The RestSharp options object</param>
/// <param name="configuration">A per-request configuration object.
/// It is assumed that any merge with GlobalConfiguration has been done before calling this method.</param>
/// <returns>A new ApiResponse instance.</returns>
private ApiResponse<T> ExecClient<T>(Func<RestClient, RestResponse<T>> getResponse, Action<RestClientOptions> setOptions, RestRequest request, RequestOptions options, IReadableConfiguration configuration)
{ {
var baseUrl = configuration.GetOperationServerUrl(options.Operation, options.OperationIndex) ?? _baseUrl; var baseUrl = configuration.GetOperationServerUrl(options.Operation, options.OperationIndex) ?? _baseUrl;
var cookies = new CookieContainer();
if (options.Cookies != null && options.Cookies.Count > 0)
{
foreach (var cookie in options.Cookies)
{
cookies.Add(new Cookie(cookie.Name, cookie.Value));
}
}
var clientOptions = new RestClientOptions(baseUrl) var clientOptions = new RestClientOptions(baseUrl)
{ {
ClientCertificates = configuration.ClientCertificates, ClientCertificates = configuration.ClientCertificates,
CookieContainer = cookies,
MaxTimeout = configuration.Timeout, MaxTimeout = configuration.Timeout,
Proxy = configuration.Proxy, Proxy = configuration.Proxy,
UserAgent = configuration.UserAgent, UserAgent = configuration.UserAgent,
UseDefaultCredentials = configuration.UseDefaultCredentials, UseDefaultCredentials = configuration.UseDefaultCredentials,
RemoteCertificateValidationCallback = configuration.RemoteCertificateValidationCallback RemoteCertificateValidationCallback = configuration.RemoteCertificateValidationCallback
}; };
setOptions(clientOptions);
using (RestClient client = new RestClient(clientOptions, using (RestClient client = new RestClient(clientOptions,
configureSerialization: serializerConfig => serializerConfig.UseSerializer(() => new CustomJsonCodec(SerializerSettings, configuration)))) configureSerialization: serializerConfig => serializerConfig.UseSerializer(() => new CustomJsonCodec(SerializerSettings, configuration))))
{ {
InterceptRequest(request); InterceptRequest(request);
RestResponse<T> response; RestResponse<T> response = getResponse(client);
if (RetryConfiguration.RetryPolicy != null)
{
var policy = RetryConfiguration.RetryPolicy;
var policyResult = policy.ExecuteAndCapture(() => client.Execute(request));
response = (policyResult.Outcome == OutcomeType.Successful) ? client.Deserialize<T>(policyResult.Result) : new RestResponse<T>(request)
{
ErrorException = policyResult.FinalException
};
}
else
{
response = client.Execute<T>(request);
}
// if the response type is oneOf/anyOf, call FromJSON to deserialize the data // if the response type is oneOf/anyOf, call FromJSON to deserialize the data
if (typeof(Org.OpenAPITools.Model.AbstractOpenAPISchema).IsAssignableFrom(typeof(T))) if (typeof(AbstractOpenAPISchema).IsAssignableFrom(typeof(T)))
{ {
try try
{ {
response.Data = (T) typeof(T).GetMethod("FromJson").Invoke(null, new object[] { response.Content }); response.Data = (T)typeof(T).GetMethod("FromJson").Invoke(null, new object[] { response.Content });
} }
catch (Exception ex) catch (Exception ex)
{ {
@ -536,90 +534,80 @@ namespace Org.OpenAPITools.Client
} }
} }
private async Task<ApiResponse<T>> ExecAsync<T>(RestRequest request, RequestOptions options, IReadableConfiguration configuration, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) private RestResponse<T> DeserializeRestResponseFromPolicy<T>(RestClient client, RestRequest request, PolicyResult<RestResponse> policyResult)
{ {
var baseUrl = configuration.GetOperationServerUrl(options.Operation, options.OperationIndex) ?? _baseUrl; if (policyResult.Outcome == OutcomeType.Successful)
var clientOptions = new RestClientOptions(baseUrl)
{ {
ClientCertificates = configuration.ClientCertificates, return client.Deserialize<T>(policyResult.Result);
MaxTimeout = configuration.Timeout, }
Proxy = configuration.Proxy, else
UserAgent = configuration.UserAgent, {
UseDefaultCredentials = configuration.UseDefaultCredentials, return new RestResponse<T>(request)
RemoteCertificateValidationCallback = configuration.RemoteCertificateValidationCallback {
ErrorException = policyResult.FinalException
};
}
}
private ApiResponse<T> Exec<T>(RestRequest request, RequestOptions options, IReadableConfiguration configuration)
{
Action<RestClientOptions> setOptions = (clientOptions) =>
{
var cookies = new CookieContainer();
if (options.Cookies != null && options.Cookies.Count > 0)
{
foreach (var cookie in options.Cookies)
{
cookies.Add(new Cookie(cookie.Name, cookie.Value));
}
}
clientOptions.CookieContainer = cookies;
}; };
using (RestClient client = new RestClient(clientOptions, Func<RestClient, RestResponse<T>> getResponse = (client) =>
configureSerialization: serializerConfig => serializerConfig.UseSerializer(() => new CustomJsonCodec(SerializerSettings, configuration))))
{ {
InterceptRequest(request); if (RetryConfiguration.RetryPolicy != null)
RestResponse<T> response;
if (RetryConfiguration.AsyncRetryPolicy != null)
{ {
var policy = RetryConfiguration.AsyncRetryPolicy; var policy = RetryConfiguration.RetryPolicy;
var policyResult = await policy.ExecuteAndCaptureAsync((ct) => client.ExecuteAsync(request, ct), cancellationToken).ConfigureAwait(false); var policyResult = policy.ExecuteAndCapture(() => client.Execute(request));
response = (policyResult.Outcome == OutcomeType.Successful) ? client.Deserialize<T>(policyResult.Result) : new RestResponse<T>(request) return DeserializeRestResponseFromPolicy<T>(client, request, policyResult);
{
ErrorException = policyResult.FinalException
};
} }
else else
{ {
response = await client.ExecuteAsync<T>(request, cancellationToken).ConfigureAwait(false); return client.Execute<T>(request);
} }
};
// if the response type is oneOf/anyOf, call FromJSON to deserialize the data return ExecClient(getResponse, setOptions, request, options, configuration);
if (typeof(Org.OpenAPITools.Model.AbstractOpenAPISchema).IsAssignableFrom(typeof(T))) }
{
response.Data = (T) typeof(T).GetMethod("FromJson").Invoke(null, new object[] { response.Content });
}
else if (typeof(T).Name == "Stream") // for binary response
{
response.Data = (T)(object)new MemoryStream(response.RawBytes);
}
else if (typeof(T).Name == "Byte[]") // for byte response
{
response.Data = (T)(object)response.RawBytes;
}
InterceptResponse(request, response); private Task<ApiResponse<T>> ExecAsync<T>(RestRequest request, RequestOptions options, IReadableConfiguration configuration, CancellationToken cancellationToken = default(CancellationToken))
{
Action<RestClientOptions> setOptions = (clientOptions) =>
{
//no extra options
};
var result = ToApiResponse(response); Func<RestClient, RestResponse<T>> getResponse = (client) =>
if (response.ErrorMessage != null) {
Func<Task<RestResponse<T>>> action = async () =>
{ {
result.ErrorText = response.ErrorMessage; if (RetryConfiguration.AsyncRetryPolicy != null)
}
if (response.Cookies != null && response.Cookies.Count > 0)
{
if (result.Cookies == null) result.Cookies = new List<Cookie>();
foreach (var restResponseCookie in response.Cookies.Cast<Cookie>())
{ {
var cookie = new Cookie( var policy = RetryConfiguration.AsyncRetryPolicy;
restResponseCookie.Name, var policyResult = await policy.ExecuteAndCaptureAsync((ct) => client.ExecuteAsync(request, ct), cancellationToken).ConfigureAwait(false);
restResponseCookie.Value, return DeserializeRestResponseFromPolicy<T>(client, request, policyResult);
restResponseCookie.Path,
restResponseCookie.Domain
)
{
Comment = restResponseCookie.Comment,
CommentUri = restResponseCookie.CommentUri,
Discard = restResponseCookie.Discard,
Expired = restResponseCookie.Expired,
Expires = restResponseCookie.Expires,
HttpOnly = restResponseCookie.HttpOnly,
Port = restResponseCookie.Port,
Secure = restResponseCookie.Secure,
Version = restResponseCookie.Version
};
result.Cookies.Add(cookie);
} }
} else
return result; {
} return await client.ExecuteAsync<T>(request, cancellationToken).ConfigureAwait(false);
}
};
return action().Result;
};
return Task.FromResult<ApiResponse<T>>(ExecClient(getResponse, setOptions, request, options, configuration));
} }
#region IAsynchronousClient #region IAsynchronousClient
@ -632,7 +620,7 @@ namespace Org.OpenAPITools.Client
/// GlobalConfiguration has been done before calling this method.</param> /// GlobalConfiguration has been done before calling this method.</param>
/// <param name="cancellationToken">Token that enables callers to cancel the request.</param> /// <param name="cancellationToken">Token that enables callers to cancel the request.</param>
/// <returns>A Task containing ApiResponse</returns> /// <returns>A Task containing ApiResponse</returns>
public Task<ApiResponse<T>> GetAsync<T>(string path, RequestOptions options, IReadableConfiguration configuration = null, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) public Task<ApiResponse<T>> GetAsync<T>(string path, RequestOptions options, IReadableConfiguration configuration = null, CancellationToken cancellationToken = default)
{ {
var config = configuration ?? GlobalConfiguration.Instance; var config = configuration ?? GlobalConfiguration.Instance;
return ExecAsync<T>(NewRequest(HttpMethod.Get, path, options, config), options, config, cancellationToken); return ExecAsync<T>(NewRequest(HttpMethod.Get, path, options, config), options, config, cancellationToken);
@ -647,7 +635,7 @@ namespace Org.OpenAPITools.Client
/// GlobalConfiguration has been done before calling this method.</param> /// GlobalConfiguration has been done before calling this method.</param>
/// <param name="cancellationToken">Token that enables callers to cancel the request.</param> /// <param name="cancellationToken">Token that enables callers to cancel the request.</param>
/// <returns>A Task containing ApiResponse</returns> /// <returns>A Task containing ApiResponse</returns>
public Task<ApiResponse<T>> PostAsync<T>(string path, RequestOptions options, IReadableConfiguration configuration = null, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) public Task<ApiResponse<T>> PostAsync<T>(string path, RequestOptions options, IReadableConfiguration configuration = null, CancellationToken cancellationToken = default)
{ {
var config = configuration ?? GlobalConfiguration.Instance; var config = configuration ?? GlobalConfiguration.Instance;
return ExecAsync<T>(NewRequest(HttpMethod.Post, path, options, config), options, config, cancellationToken); return ExecAsync<T>(NewRequest(HttpMethod.Post, path, options, config), options, config, cancellationToken);
@ -662,7 +650,7 @@ namespace Org.OpenAPITools.Client
/// GlobalConfiguration has been done before calling this method.</param> /// GlobalConfiguration has been done before calling this method.</param>
/// <param name="cancellationToken">Token that enables callers to cancel the request.</param> /// <param name="cancellationToken">Token that enables callers to cancel the request.</param>
/// <returns>A Task containing ApiResponse</returns> /// <returns>A Task containing ApiResponse</returns>
public Task<ApiResponse<T>> PutAsync<T>(string path, RequestOptions options, IReadableConfiguration configuration = null, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) public Task<ApiResponse<T>> PutAsync<T>(string path, RequestOptions options, IReadableConfiguration configuration = null, CancellationToken cancellationToken = default)
{ {
var config = configuration ?? GlobalConfiguration.Instance; var config = configuration ?? GlobalConfiguration.Instance;
return ExecAsync<T>(NewRequest(HttpMethod.Put, path, options, config), options, config, cancellationToken); return ExecAsync<T>(NewRequest(HttpMethod.Put, path, options, config), options, config, cancellationToken);
@ -677,7 +665,7 @@ namespace Org.OpenAPITools.Client
/// GlobalConfiguration has been done before calling this method.</param> /// GlobalConfiguration has been done before calling this method.</param>
/// <param name="cancellationToken">Token that enables callers to cancel the request.</param> /// <param name="cancellationToken">Token that enables callers to cancel the request.</param>
/// <returns>A Task containing ApiResponse</returns> /// <returns>A Task containing ApiResponse</returns>
public Task<ApiResponse<T>> DeleteAsync<T>(string path, RequestOptions options, IReadableConfiguration configuration = null, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) public Task<ApiResponse<T>> DeleteAsync<T>(string path, RequestOptions options, IReadableConfiguration configuration = null, CancellationToken cancellationToken = default)
{ {
var config = configuration ?? GlobalConfiguration.Instance; var config = configuration ?? GlobalConfiguration.Instance;
return ExecAsync<T>(NewRequest(HttpMethod.Delete, path, options, config), options, config, cancellationToken); return ExecAsync<T>(NewRequest(HttpMethod.Delete, path, options, config), options, config, cancellationToken);
@ -692,7 +680,7 @@ namespace Org.OpenAPITools.Client
/// GlobalConfiguration has been done before calling this method.</param> /// GlobalConfiguration has been done before calling this method.</param>
/// <param name="cancellationToken">Token that enables callers to cancel the request.</param> /// <param name="cancellationToken">Token that enables callers to cancel the request.</param>
/// <returns>A Task containing ApiResponse</returns> /// <returns>A Task containing ApiResponse</returns>
public Task<ApiResponse<T>> HeadAsync<T>(string path, RequestOptions options, IReadableConfiguration configuration = null, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) public Task<ApiResponse<T>> HeadAsync<T>(string path, RequestOptions options, IReadableConfiguration configuration = null, CancellationToken cancellationToken = default)
{ {
var config = configuration ?? GlobalConfiguration.Instance; var config = configuration ?? GlobalConfiguration.Instance;
return ExecAsync<T>(NewRequest(HttpMethod.Head, path, options, config), options, config, cancellationToken); return ExecAsync<T>(NewRequest(HttpMethod.Head, path, options, config), options, config, cancellationToken);
@ -707,7 +695,7 @@ namespace Org.OpenAPITools.Client
/// GlobalConfiguration has been done before calling this method.</param> /// GlobalConfiguration has been done before calling this method.</param>
/// <param name="cancellationToken">Token that enables callers to cancel the request.</param> /// <param name="cancellationToken">Token that enables callers to cancel the request.</param>
/// <returns>A Task containing ApiResponse</returns> /// <returns>A Task containing ApiResponse</returns>
public Task<ApiResponse<T>> OptionsAsync<T>(string path, RequestOptions options, IReadableConfiguration configuration = null, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) public Task<ApiResponse<T>> OptionsAsync<T>(string path, RequestOptions options, IReadableConfiguration configuration = null, CancellationToken cancellationToken = default)
{ {
var config = configuration ?? GlobalConfiguration.Instance; var config = configuration ?? GlobalConfiguration.Instance;
return ExecAsync<T>(NewRequest(HttpMethod.Options, path, options, config), options, config, cancellationToken); return ExecAsync<T>(NewRequest(HttpMethod.Options, path, options, config), options, config, cancellationToken);
@ -722,7 +710,7 @@ namespace Org.OpenAPITools.Client
/// GlobalConfiguration has been done before calling this method.</param> /// GlobalConfiguration has been done before calling this method.</param>
/// <param name="cancellationToken">Token that enables callers to cancel the request.</param> /// <param name="cancellationToken">Token that enables callers to cancel the request.</param>
/// <returns>A Task containing ApiResponse</returns> /// <returns>A Task containing ApiResponse</returns>
public Task<ApiResponse<T>> PatchAsync<T>(string path, RequestOptions options, IReadableConfiguration configuration = null, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) public Task<ApiResponse<T>> PatchAsync<T>(string path, RequestOptions options, IReadableConfiguration configuration = null, CancellationToken cancellationToken = default)
{ {
var config = configuration ?? GlobalConfiguration.Instance; var config = configuration ?? GlobalConfiguration.Instance;
return ExecAsync<T>(NewRequest(HttpMethod.Patch, path, options, config), options, config, cancellationToken); return ExecAsync<T>(NewRequest(HttpMethod.Patch, path, options, config), options, config, cancellationToken);

View File

@ -22,13 +22,16 @@ using System.Text;
using System.Threading; using System.Threading;
using System.Text.RegularExpressions; using System.Text.RegularExpressions;
using System.Threading.Tasks; using System.Threading.Tasks;
using System.Web;
using Newtonsoft.Json; using Newtonsoft.Json;
using Newtonsoft.Json.Serialization; using Newtonsoft.Json.Serialization;
using RestSharp; using RestSharp;
using RestSharp.Serializers; using RestSharp.Serializers;
using RestSharpMethod = RestSharp.Method; using RestSharpMethod = RestSharp.Method;
using FileIO = System.IO.File;
using Polly; using Polly;
using Org.OpenAPITools.Client.Auth; using Org.OpenAPITools.Client.Auth;
using Org.OpenAPITools.Model;
namespace Org.OpenAPITools.Client namespace Org.OpenAPITools.Client
{ {
@ -69,10 +72,10 @@ namespace Org.OpenAPITools.Client
/// <returns>A JSON string.</returns> /// <returns>A JSON string.</returns>
public string Serialize(object obj) public string Serialize(object obj)
{ {
if (obj != null && obj is Org.OpenAPITools.Model.AbstractOpenAPISchema) if (obj != null && obj is AbstractOpenAPISchema)
{ {
// the object to be serialized is an oneOf/anyOf schema // the object to be serialized is an oneOf/anyOf schema
return ((Org.OpenAPITools.Model.AbstractOpenAPISchema)obj).ToJson(); return ((AbstractOpenAPISchema)obj).ToJson();
} }
else else
{ {
@ -117,7 +120,7 @@ namespace Org.OpenAPITools.Client
if (match.Success) if (match.Success)
{ {
string fileName = filePath + ClientUtils.SanitizeFilename(match.Groups[1].Value.Replace("\"", "").Replace("'", "")); string fileName = filePath + ClientUtils.SanitizeFilename(match.Groups[1].Value.Replace("\"", "").Replace("'", ""));
File.WriteAllBytes(fileName, bytes); FileIO.WriteAllBytes(fileName, bytes);
return new FileStream(fileName, FileMode.Open); return new FileStream(fileName, FileMode.Open);
} }
} }
@ -128,7 +131,7 @@ namespace Org.OpenAPITools.Client
if (type.Name.StartsWith("System.Nullable`1[[System.DateTime")) // return a datetime object if (type.Name.StartsWith("System.Nullable`1[[System.DateTime")) // return a datetime object
{ {
return DateTime.Parse(response.Content, null, System.Globalization.DateTimeStyles.RoundtripKind); return DateTime.Parse(response.Content, null, DateTimeStyles.RoundtripKind);
} }
if (type == typeof(string) || type.Name.StartsWith("System.Nullable")) // return primitive type if (type == typeof(string) || type.Name.StartsWith("System.Nullable")) // return primitive type
@ -150,13 +153,13 @@ namespace Org.OpenAPITools.Client
public ISerializer Serializer => this; public ISerializer Serializer => this;
public IDeserializer Deserializer => this; public IDeserializer Deserializer => this;
public string[] AcceptedContentTypes => RestSharp.ContentType.JsonAccept; public string[] AcceptedContentTypes => ContentType.JsonAccept;
public SupportsContentType SupportsContentType => contentType => public SupportsContentType SupportsContentType => contentType =>
contentType.Value.EndsWith("json", StringComparison.InvariantCultureIgnoreCase) || contentType.Value.EndsWith("json", StringComparison.InvariantCultureIgnoreCase) ||
contentType.Value.EndsWith("javascript", StringComparison.InvariantCultureIgnoreCase); contentType.Value.EndsWith("javascript", StringComparison.InvariantCultureIgnoreCase);
public ContentType ContentType { get; set; } = RestSharp.ContentType.Json; public ContentType ContentType { get; set; } = ContentType.Json;
public DataFormat DataFormat => DataFormat.Json; public DataFormat DataFormat => DataFormat.Json;
} }
@ -203,7 +206,7 @@ namespace Org.OpenAPITools.Client
/// </summary> /// </summary>
public ApiClient() public ApiClient()
{ {
_baseUrl = Org.OpenAPITools.Client.GlobalConfiguration.Instance.BasePath; _baseUrl = GlobalConfiguration.Instance.BasePath;
} }
/// <summary> /// <summary>
@ -260,14 +263,14 @@ namespace Org.OpenAPITools.Client
/// <summary> /// <summary>
/// Provides all logic for constructing a new RestSharp <see cref="RestRequest"/>. /// Provides all logic for constructing a new RestSharp <see cref="RestRequest"/>.
/// At this point, all information for querying the service is known. Here, it is simply /// At this point, all information for querying the service is known.
/// mapped into the RestSharp request. /// Here, it is simply mapped into the RestSharp request.
/// </summary> /// </summary>
/// <param name="method">The http verb.</param> /// <param name="method">The http verb.</param>
/// <param name="path">The target path (or resource).</param> /// <param name="path">The target path (or resource).</param>
/// <param name="options">The additional request options.</param> /// <param name="options">The additional request options.</param>
/// <param name="configuration">A per-request configuration object. It is assumed that any merge with /// <param name="configuration">A per-request configuration object.
/// GlobalConfiguration has been done before calling this method.</param> /// It is assumed that any merge with GlobalConfiguration has been done before calling this method.</param>
/// <returns>[private] A new RestRequest instance.</returns> /// <returns>[private] A new RestRequest instance.</returns>
/// <exception cref="ArgumentNullException"></exception> /// <exception cref="ArgumentNullException"></exception>
private RestRequest NewRequest( private RestRequest NewRequest(
@ -375,7 +378,7 @@ namespace Org.OpenAPITools.Client
var bytes = ClientUtils.ReadAsBytes(file); var bytes = ClientUtils.ReadAsBytes(file);
var fileStream = file as FileStream; var fileStream = file as FileStream;
if (fileStream != null) if (fileStream != null)
request.AddFile(fileParam.Key, bytes, System.IO.Path.GetFileName(fileStream.Name)); request.AddFile(fileParam.Key, bytes, Path.GetFileName(fileStream.Name));
else else
request.AddFile(fileParam.Key, bytes, "no_file_name_provided"); request.AddFile(fileParam.Key, bytes, "no_file_name_provided");
} }
@ -385,6 +388,13 @@ namespace Org.OpenAPITools.Client
return request; return request;
} }
/// <summary>
/// Transforms a RestResponse instance into a new ApiResponse instance.
/// At this point, we have a concrete http response from the service.
/// Here, it is simply mapped into the [public] ApiResponse object.
/// </summary>
/// <param name="response">The RestSharp response object</param>
/// <returns>A new ApiResponse instance.</returns>
private ApiResponse<T> ToApiResponse<T>(RestResponse<T> response) private ApiResponse<T> ToApiResponse<T>(RestResponse<T> response)
{ {
T result = response.Data; T result = response.Data;
@ -429,31 +439,32 @@ namespace Org.OpenAPITools.Client
return transformed; return transformed;
} }
private ApiResponse<T> Exec<T>(RestRequest request, RequestOptions options, IReadableConfiguration configuration) /// <summary>
/// Executes the HTTP request for the current service.
/// Based on functions received it can be async or sync.
/// </summary>
/// <param name="getResponse">Local function that executes http request and returns http response.</param>
/// <param name="setOptions">Local function to specify options for the service.</param>
/// <param name="request">The RestSharp request object</param>
/// <param name="options">The RestSharp options object</param>
/// <param name="configuration">A per-request configuration object.
/// It is assumed that any merge with GlobalConfiguration has been done before calling this method.</param>
/// <returns>A new ApiResponse instance.</returns>
private ApiResponse<T> ExecClient<T>(Func<RestClient, RestResponse<T>> getResponse, Action<RestClientOptions> setOptions, RestRequest request, RequestOptions options, IReadableConfiguration configuration)
{ {
var baseUrl = configuration.GetOperationServerUrl(options.Operation, options.OperationIndex) ?? _baseUrl; var baseUrl = configuration.GetOperationServerUrl(options.Operation, options.OperationIndex) ?? _baseUrl;
var cookies = new CookieContainer();
if (options.Cookies != null && options.Cookies.Count > 0)
{
foreach (var cookie in options.Cookies)
{
cookies.Add(new Cookie(cookie.Name, cookie.Value));
}
}
var clientOptions = new RestClientOptions(baseUrl) var clientOptions = new RestClientOptions(baseUrl)
{ {
ClientCertificates = configuration.ClientCertificates, ClientCertificates = configuration.ClientCertificates,
CookieContainer = cookies,
MaxTimeout = configuration.Timeout, MaxTimeout = configuration.Timeout,
Proxy = configuration.Proxy, Proxy = configuration.Proxy,
UserAgent = configuration.UserAgent, UserAgent = configuration.UserAgent,
UseDefaultCredentials = configuration.UseDefaultCredentials, UseDefaultCredentials = configuration.UseDefaultCredentials,
RemoteCertificateValidationCallback = configuration.RemoteCertificateValidationCallback RemoteCertificateValidationCallback = configuration.RemoteCertificateValidationCallback
}; };
setOptions(clientOptions);
if (!string.IsNullOrEmpty(configuration.OAuthTokenUrl) && if (!string.IsNullOrEmpty(configuration.OAuthTokenUrl) &&
!string.IsNullOrEmpty(configuration.OAuthClientId) && !string.IsNullOrEmpty(configuration.OAuthClientId) &&
!string.IsNullOrEmpty(configuration.OAuthClientSecret) && !string.IsNullOrEmpty(configuration.OAuthClientSecret) &&
@ -473,27 +484,14 @@ namespace Org.OpenAPITools.Client
{ {
InterceptRequest(request); InterceptRequest(request);
RestResponse<T> response; RestResponse<T> response = getResponse(client);
if (RetryConfiguration.RetryPolicy != null)
{
var policy = RetryConfiguration.RetryPolicy;
var policyResult = policy.ExecuteAndCapture(() => client.Execute(request));
response = (policyResult.Outcome == OutcomeType.Successful) ? client.Deserialize<T>(policyResult.Result) : new RestResponse<T>(request)
{
ErrorException = policyResult.FinalException
};
}
else
{
response = client.Execute<T>(request);
}
// if the response type is oneOf/anyOf, call FromJSON to deserialize the data // if the response type is oneOf/anyOf, call FromJSON to deserialize the data
if (typeof(Org.OpenAPITools.Model.AbstractOpenAPISchema).IsAssignableFrom(typeof(T))) if (typeof(AbstractOpenAPISchema).IsAssignableFrom(typeof(T)))
{ {
try try
{ {
response.Data = (T) typeof(T).GetMethod("FromJson").Invoke(null, new object[] { response.Content }); response.Data = (T)typeof(T).GetMethod("FromJson").Invoke(null, new object[] { response.Content });
} }
catch (Exception ex) catch (Exception ex)
{ {
@ -551,104 +549,80 @@ namespace Org.OpenAPITools.Client
} }
} }
private async Task<ApiResponse<T>> ExecAsync<T>(RestRequest request, RequestOptions options, IReadableConfiguration configuration, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) private RestResponse<T> DeserializeRestResponseFromPolicy<T>(RestClient client, RestRequest request, PolicyResult<RestResponse> policyResult)
{ {
var baseUrl = configuration.GetOperationServerUrl(options.Operation, options.OperationIndex) ?? _baseUrl; if (policyResult.Outcome == OutcomeType.Successful)
var clientOptions = new RestClientOptions(baseUrl)
{ {
ClientCertificates = configuration.ClientCertificates, return client.Deserialize<T>(policyResult.Result);
MaxTimeout = configuration.Timeout, }
Proxy = configuration.Proxy, else
UserAgent = configuration.UserAgent, {
UseDefaultCredentials = configuration.UseDefaultCredentials, return new RestResponse<T>(request)
RemoteCertificateValidationCallback = configuration.RemoteCertificateValidationCallback {
ErrorException = policyResult.FinalException
};
}
}
private ApiResponse<T> Exec<T>(RestRequest request, RequestOptions options, IReadableConfiguration configuration)
{
Action<RestClientOptions> setOptions = (clientOptions) =>
{
var cookies = new CookieContainer();
if (options.Cookies != null && options.Cookies.Count > 0)
{
foreach (var cookie in options.Cookies)
{
cookies.Add(new Cookie(cookie.Name, cookie.Value));
}
}
clientOptions.CookieContainer = cookies;
}; };
if (!string.IsNullOrEmpty(configuration.OAuthTokenUrl) && Func<RestClient, RestResponse<T>> getResponse = (client) =>
!string.IsNullOrEmpty(configuration.OAuthClientId) &&
!string.IsNullOrEmpty(configuration.OAuthClientSecret) &&
configuration.OAuthFlow != null)
{ {
clientOptions.Authenticator = new OAuthAuthenticator( if (RetryConfiguration.RetryPolicy != null)
configuration.OAuthTokenUrl,
configuration.OAuthClientId,
configuration.OAuthClientSecret,
configuration.OAuthFlow,
SerializerSettings,
configuration);
}
using (RestClient client = new RestClient(clientOptions,
configureSerialization: serializerConfig => serializerConfig.UseSerializer(() => new CustomJsonCodec(SerializerSettings, configuration))))
{
InterceptRequest(request);
RestResponse<T> response;
if (RetryConfiguration.AsyncRetryPolicy != null)
{ {
var policy = RetryConfiguration.AsyncRetryPolicy; var policy = RetryConfiguration.RetryPolicy;
var policyResult = await policy.ExecuteAndCaptureAsync((ct) => client.ExecuteAsync(request, ct), cancellationToken).ConfigureAwait(false); var policyResult = policy.ExecuteAndCapture(() => client.Execute(request));
response = (policyResult.Outcome == OutcomeType.Successful) ? client.Deserialize<T>(policyResult.Result) : new RestResponse<T>(request) return DeserializeRestResponseFromPolicy<T>(client, request, policyResult);
{
ErrorException = policyResult.FinalException
};
} }
else else
{ {
response = await client.ExecuteAsync<T>(request, cancellationToken).ConfigureAwait(false); return client.Execute<T>(request);
} }
};
// if the response type is oneOf/anyOf, call FromJSON to deserialize the data return ExecClient(getResponse, setOptions, request, options, configuration);
if (typeof(Org.OpenAPITools.Model.AbstractOpenAPISchema).IsAssignableFrom(typeof(T))) }
{
response.Data = (T) typeof(T).GetMethod("FromJson").Invoke(null, new object[] { response.Content });
}
else if (typeof(T).Name == "Stream") // for binary response
{
response.Data = (T)(object)new MemoryStream(response.RawBytes);
}
else if (typeof(T).Name == "Byte[]") // for byte response
{
response.Data = (T)(object)response.RawBytes;
}
InterceptResponse(request, response); private Task<ApiResponse<T>> ExecAsync<T>(RestRequest request, RequestOptions options, IReadableConfiguration configuration, CancellationToken cancellationToken = default(CancellationToken))
{
Action<RestClientOptions> setOptions = (clientOptions) =>
{
//no extra options
};
var result = ToApiResponse(response); Func<RestClient, RestResponse<T>> getResponse = (client) =>
if (response.ErrorMessage != null) {
Func<Task<RestResponse<T>>> action = async () =>
{ {
result.ErrorText = response.ErrorMessage; if (RetryConfiguration.AsyncRetryPolicy != null)
}
if (response.Cookies != null && response.Cookies.Count > 0)
{
if (result.Cookies == null) result.Cookies = new List<Cookie>();
foreach (var restResponseCookie in response.Cookies.Cast<Cookie>())
{ {
var cookie = new Cookie( var policy = RetryConfiguration.AsyncRetryPolicy;
restResponseCookie.Name, var policyResult = await policy.ExecuteAndCaptureAsync((ct) => client.ExecuteAsync(request, ct), cancellationToken).ConfigureAwait(false);
restResponseCookie.Value, return DeserializeRestResponseFromPolicy<T>(client, request, policyResult);
restResponseCookie.Path,
restResponseCookie.Domain
)
{
Comment = restResponseCookie.Comment,
CommentUri = restResponseCookie.CommentUri,
Discard = restResponseCookie.Discard,
Expired = restResponseCookie.Expired,
Expires = restResponseCookie.Expires,
HttpOnly = restResponseCookie.HttpOnly,
Port = restResponseCookie.Port,
Secure = restResponseCookie.Secure,
Version = restResponseCookie.Version
};
result.Cookies.Add(cookie);
} }
} else
return result; {
} return await client.ExecuteAsync<T>(request, cancellationToken).ConfigureAwait(false);
}
};
return action().Result;
};
return Task.FromResult<ApiResponse<T>>(ExecClient(getResponse, setOptions, request, options, configuration));
} }
#region IAsynchronousClient #region IAsynchronousClient
@ -661,7 +635,7 @@ namespace Org.OpenAPITools.Client
/// GlobalConfiguration has been done before calling this method.</param> /// GlobalConfiguration has been done before calling this method.</param>
/// <param name="cancellationToken">Token that enables callers to cancel the request.</param> /// <param name="cancellationToken">Token that enables callers to cancel the request.</param>
/// <returns>A Task containing ApiResponse</returns> /// <returns>A Task containing ApiResponse</returns>
public Task<ApiResponse<T>> GetAsync<T>(string path, RequestOptions options, IReadableConfiguration configuration = null, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) public Task<ApiResponse<T>> GetAsync<T>(string path, RequestOptions options, IReadableConfiguration configuration = null, CancellationToken cancellationToken = default)
{ {
var config = configuration ?? GlobalConfiguration.Instance; var config = configuration ?? GlobalConfiguration.Instance;
return ExecAsync<T>(NewRequest(HttpMethod.Get, path, options, config), options, config, cancellationToken); return ExecAsync<T>(NewRequest(HttpMethod.Get, path, options, config), options, config, cancellationToken);
@ -676,7 +650,7 @@ namespace Org.OpenAPITools.Client
/// GlobalConfiguration has been done before calling this method.</param> /// GlobalConfiguration has been done before calling this method.</param>
/// <param name="cancellationToken">Token that enables callers to cancel the request.</param> /// <param name="cancellationToken">Token that enables callers to cancel the request.</param>
/// <returns>A Task containing ApiResponse</returns> /// <returns>A Task containing ApiResponse</returns>
public Task<ApiResponse<T>> PostAsync<T>(string path, RequestOptions options, IReadableConfiguration configuration = null, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) public Task<ApiResponse<T>> PostAsync<T>(string path, RequestOptions options, IReadableConfiguration configuration = null, CancellationToken cancellationToken = default)
{ {
var config = configuration ?? GlobalConfiguration.Instance; var config = configuration ?? GlobalConfiguration.Instance;
return ExecAsync<T>(NewRequest(HttpMethod.Post, path, options, config), options, config, cancellationToken); return ExecAsync<T>(NewRequest(HttpMethod.Post, path, options, config), options, config, cancellationToken);
@ -691,7 +665,7 @@ namespace Org.OpenAPITools.Client
/// GlobalConfiguration has been done before calling this method.</param> /// GlobalConfiguration has been done before calling this method.</param>
/// <param name="cancellationToken">Token that enables callers to cancel the request.</param> /// <param name="cancellationToken">Token that enables callers to cancel the request.</param>
/// <returns>A Task containing ApiResponse</returns> /// <returns>A Task containing ApiResponse</returns>
public Task<ApiResponse<T>> PutAsync<T>(string path, RequestOptions options, IReadableConfiguration configuration = null, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) public Task<ApiResponse<T>> PutAsync<T>(string path, RequestOptions options, IReadableConfiguration configuration = null, CancellationToken cancellationToken = default)
{ {
var config = configuration ?? GlobalConfiguration.Instance; var config = configuration ?? GlobalConfiguration.Instance;
return ExecAsync<T>(NewRequest(HttpMethod.Put, path, options, config), options, config, cancellationToken); return ExecAsync<T>(NewRequest(HttpMethod.Put, path, options, config), options, config, cancellationToken);
@ -706,7 +680,7 @@ namespace Org.OpenAPITools.Client
/// GlobalConfiguration has been done before calling this method.</param> /// GlobalConfiguration has been done before calling this method.</param>
/// <param name="cancellationToken">Token that enables callers to cancel the request.</param> /// <param name="cancellationToken">Token that enables callers to cancel the request.</param>
/// <returns>A Task containing ApiResponse</returns> /// <returns>A Task containing ApiResponse</returns>
public Task<ApiResponse<T>> DeleteAsync<T>(string path, RequestOptions options, IReadableConfiguration configuration = null, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) public Task<ApiResponse<T>> DeleteAsync<T>(string path, RequestOptions options, IReadableConfiguration configuration = null, CancellationToken cancellationToken = default)
{ {
var config = configuration ?? GlobalConfiguration.Instance; var config = configuration ?? GlobalConfiguration.Instance;
return ExecAsync<T>(NewRequest(HttpMethod.Delete, path, options, config), options, config, cancellationToken); return ExecAsync<T>(NewRequest(HttpMethod.Delete, path, options, config), options, config, cancellationToken);
@ -721,7 +695,7 @@ namespace Org.OpenAPITools.Client
/// GlobalConfiguration has been done before calling this method.</param> /// GlobalConfiguration has been done before calling this method.</param>
/// <param name="cancellationToken">Token that enables callers to cancel the request.</param> /// <param name="cancellationToken">Token that enables callers to cancel the request.</param>
/// <returns>A Task containing ApiResponse</returns> /// <returns>A Task containing ApiResponse</returns>
public Task<ApiResponse<T>> HeadAsync<T>(string path, RequestOptions options, IReadableConfiguration configuration = null, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) public Task<ApiResponse<T>> HeadAsync<T>(string path, RequestOptions options, IReadableConfiguration configuration = null, CancellationToken cancellationToken = default)
{ {
var config = configuration ?? GlobalConfiguration.Instance; var config = configuration ?? GlobalConfiguration.Instance;
return ExecAsync<T>(NewRequest(HttpMethod.Head, path, options, config), options, config, cancellationToken); return ExecAsync<T>(NewRequest(HttpMethod.Head, path, options, config), options, config, cancellationToken);
@ -736,7 +710,7 @@ namespace Org.OpenAPITools.Client
/// GlobalConfiguration has been done before calling this method.</param> /// GlobalConfiguration has been done before calling this method.</param>
/// <param name="cancellationToken">Token that enables callers to cancel the request.</param> /// <param name="cancellationToken">Token that enables callers to cancel the request.</param>
/// <returns>A Task containing ApiResponse</returns> /// <returns>A Task containing ApiResponse</returns>
public Task<ApiResponse<T>> OptionsAsync<T>(string path, RequestOptions options, IReadableConfiguration configuration = null, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) public Task<ApiResponse<T>> OptionsAsync<T>(string path, RequestOptions options, IReadableConfiguration configuration = null, CancellationToken cancellationToken = default)
{ {
var config = configuration ?? GlobalConfiguration.Instance; var config = configuration ?? GlobalConfiguration.Instance;
return ExecAsync<T>(NewRequest(HttpMethod.Options, path, options, config), options, config, cancellationToken); return ExecAsync<T>(NewRequest(HttpMethod.Options, path, options, config), options, config, cancellationToken);
@ -751,7 +725,7 @@ namespace Org.OpenAPITools.Client
/// GlobalConfiguration has been done before calling this method.</param> /// GlobalConfiguration has been done before calling this method.</param>
/// <param name="cancellationToken">Token that enables callers to cancel the request.</param> /// <param name="cancellationToken">Token that enables callers to cancel the request.</param>
/// <returns>A Task containing ApiResponse</returns> /// <returns>A Task containing ApiResponse</returns>
public Task<ApiResponse<T>> PatchAsync<T>(string path, RequestOptions options, IReadableConfiguration configuration = null, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) public Task<ApiResponse<T>> PatchAsync<T>(string path, RequestOptions options, IReadableConfiguration configuration = null, CancellationToken cancellationToken = default)
{ {
var config = configuration ?? GlobalConfiguration.Instance; var config = configuration ?? GlobalConfiguration.Instance;
return ExecAsync<T>(NewRequest(HttpMethod.Patch, path, options, config), options, config, cancellationToken); return ExecAsync<T>(NewRequest(HttpMethod.Patch, path, options, config), options, config, cancellationToken);

View File

@ -28,8 +28,10 @@ using Newtonsoft.Json.Serialization;
using RestSharp; using RestSharp;
using RestSharp.Serializers; using RestSharp.Serializers;
using RestSharpMethod = RestSharp.Method; using RestSharpMethod = RestSharp.Method;
using FileIO = System.IO.File;
using Polly; using Polly;
using Org.OpenAPITools.Client.Auth; using Org.OpenAPITools.Client.Auth;
using Org.OpenAPITools.Model;
namespace Org.OpenAPITools.Client namespace Org.OpenAPITools.Client
{ {
@ -70,10 +72,10 @@ namespace Org.OpenAPITools.Client
/// <returns>A JSON string.</returns> /// <returns>A JSON string.</returns>
public string Serialize(object obj) public string Serialize(object obj)
{ {
if (obj != null && obj is Org.OpenAPITools.Model.AbstractOpenAPISchema) if (obj != null && obj is AbstractOpenAPISchema)
{ {
// the object to be serialized is an oneOf/anyOf schema // the object to be serialized is an oneOf/anyOf schema
return ((Org.OpenAPITools.Model.AbstractOpenAPISchema)obj).ToJson(); return ((AbstractOpenAPISchema)obj).ToJson();
} }
else else
{ {
@ -118,7 +120,7 @@ namespace Org.OpenAPITools.Client
if (match.Success) if (match.Success)
{ {
string fileName = filePath + ClientUtils.SanitizeFilename(match.Groups[1].Value.Replace("\"", "").Replace("'", "")); string fileName = filePath + ClientUtils.SanitizeFilename(match.Groups[1].Value.Replace("\"", "").Replace("'", ""));
File.WriteAllBytes(fileName, bytes); FileIO.WriteAllBytes(fileName, bytes);
return new FileStream(fileName, FileMode.Open); return new FileStream(fileName, FileMode.Open);
} }
} }
@ -129,7 +131,7 @@ namespace Org.OpenAPITools.Client
if (type.Name.StartsWith("System.Nullable`1[[System.DateTime")) // return a datetime object if (type.Name.StartsWith("System.Nullable`1[[System.DateTime")) // return a datetime object
{ {
return DateTime.Parse(response.Content, null, System.Globalization.DateTimeStyles.RoundtripKind); return DateTime.Parse(response.Content, null, DateTimeStyles.RoundtripKind);
} }
if (type == typeof(string) || type.Name.StartsWith("System.Nullable")) // return primitive type if (type == typeof(string) || type.Name.StartsWith("System.Nullable")) // return primitive type
@ -151,13 +153,13 @@ namespace Org.OpenAPITools.Client
public ISerializer Serializer => this; public ISerializer Serializer => this;
public IDeserializer Deserializer => this; public IDeserializer Deserializer => this;
public string[] AcceptedContentTypes => RestSharp.ContentType.JsonAccept; public string[] AcceptedContentTypes => ContentType.JsonAccept;
public SupportsContentType SupportsContentType => contentType => public SupportsContentType SupportsContentType => contentType =>
contentType.Value.EndsWith("json", StringComparison.InvariantCultureIgnoreCase) || contentType.Value.EndsWith("json", StringComparison.InvariantCultureIgnoreCase) ||
contentType.Value.EndsWith("javascript", StringComparison.InvariantCultureIgnoreCase); contentType.Value.EndsWith("javascript", StringComparison.InvariantCultureIgnoreCase);
public ContentType ContentType { get; set; } = RestSharp.ContentType.Json; public ContentType ContentType { get; set; } = ContentType.Json;
public DataFormat DataFormat => DataFormat.Json; public DataFormat DataFormat => DataFormat.Json;
} }
@ -204,7 +206,7 @@ namespace Org.OpenAPITools.Client
/// </summary> /// </summary>
public ApiClient() public ApiClient()
{ {
_baseUrl = Org.OpenAPITools.Client.GlobalConfiguration.Instance.BasePath; _baseUrl = GlobalConfiguration.Instance.BasePath;
} }
/// <summary> /// <summary>
@ -261,14 +263,14 @@ namespace Org.OpenAPITools.Client
/// <summary> /// <summary>
/// Provides all logic for constructing a new RestSharp <see cref="RestRequest"/>. /// Provides all logic for constructing a new RestSharp <see cref="RestRequest"/>.
/// At this point, all information for querying the service is known. Here, it is simply /// At this point, all information for querying the service is known.
/// mapped into the RestSharp request. /// Here, it is simply mapped into the RestSharp request.
/// </summary> /// </summary>
/// <param name="method">The http verb.</param> /// <param name="method">The http verb.</param>
/// <param name="path">The target path (or resource).</param> /// <param name="path">The target path (or resource).</param>
/// <param name="options">The additional request options.</param> /// <param name="options">The additional request options.</param>
/// <param name="configuration">A per-request configuration object. It is assumed that any merge with /// <param name="configuration">A per-request configuration object.
/// GlobalConfiguration has been done before calling this method.</param> /// It is assumed that any merge with GlobalConfiguration has been done before calling this method.</param>
/// <returns>[private] A new RestRequest instance.</returns> /// <returns>[private] A new RestRequest instance.</returns>
/// <exception cref="ArgumentNullException"></exception> /// <exception cref="ArgumentNullException"></exception>
private RestRequest NewRequest( private RestRequest NewRequest(
@ -376,7 +378,7 @@ namespace Org.OpenAPITools.Client
var bytes = ClientUtils.ReadAsBytes(file); var bytes = ClientUtils.ReadAsBytes(file);
var fileStream = file as FileStream; var fileStream = file as FileStream;
if (fileStream != null) if (fileStream != null)
request.AddFile(fileParam.Key, bytes, System.IO.Path.GetFileName(fileStream.Name)); request.AddFile(fileParam.Key, bytes, Path.GetFileName(fileStream.Name));
else else
request.AddFile(fileParam.Key, bytes, "no_file_name_provided"); request.AddFile(fileParam.Key, bytes, "no_file_name_provided");
} }
@ -386,6 +388,13 @@ namespace Org.OpenAPITools.Client
return request; return request;
} }
/// <summary>
/// Transforms a RestResponse instance into a new ApiResponse instance.
/// At this point, we have a concrete http response from the service.
/// Here, it is simply mapped into the [public] ApiResponse object.
/// </summary>
/// <param name="response">The RestSharp response object</param>
/// <returns>A new ApiResponse instance.</returns>
private ApiResponse<T> ToApiResponse<T>(RestResponse<T> response) private ApiResponse<T> ToApiResponse<T>(RestResponse<T> response)
{ {
T result = response.Data; T result = response.Data;
@ -430,31 +439,32 @@ namespace Org.OpenAPITools.Client
return transformed; return transformed;
} }
private ApiResponse<T> Exec<T>(RestRequest request, RequestOptions options, IReadableConfiguration configuration) /// <summary>
/// Executes the HTTP request for the current service.
/// Based on functions received it can be async or sync.
/// </summary>
/// <param name="getResponse">Local function that executes http request and returns http response.</param>
/// <param name="setOptions">Local function to specify options for the service.</param>
/// <param name="request">The RestSharp request object</param>
/// <param name="options">The RestSharp options object</param>
/// <param name="configuration">A per-request configuration object.
/// It is assumed that any merge with GlobalConfiguration has been done before calling this method.</param>
/// <returns>A new ApiResponse instance.</returns>
private ApiResponse<T> ExecClient<T>(Func<RestClient, RestResponse<T>> getResponse, Action<RestClientOptions> setOptions, RestRequest request, RequestOptions options, IReadableConfiguration configuration)
{ {
var baseUrl = configuration.GetOperationServerUrl(options.Operation, options.OperationIndex) ?? _baseUrl; var baseUrl = configuration.GetOperationServerUrl(options.Operation, options.OperationIndex) ?? _baseUrl;
var cookies = new CookieContainer();
if (options.Cookies != null && options.Cookies.Count > 0)
{
foreach (var cookie in options.Cookies)
{
cookies.Add(new Cookie(cookie.Name, cookie.Value));
}
}
var clientOptions = new RestClientOptions(baseUrl) var clientOptions = new RestClientOptions(baseUrl)
{ {
ClientCertificates = configuration.ClientCertificates, ClientCertificates = configuration.ClientCertificates,
CookieContainer = cookies,
MaxTimeout = configuration.Timeout, MaxTimeout = configuration.Timeout,
Proxy = configuration.Proxy, Proxy = configuration.Proxy,
UserAgent = configuration.UserAgent, UserAgent = configuration.UserAgent,
UseDefaultCredentials = configuration.UseDefaultCredentials, UseDefaultCredentials = configuration.UseDefaultCredentials,
RemoteCertificateValidationCallback = configuration.RemoteCertificateValidationCallback RemoteCertificateValidationCallback = configuration.RemoteCertificateValidationCallback
}; };
setOptions(clientOptions);
if (!string.IsNullOrEmpty(configuration.OAuthTokenUrl) && if (!string.IsNullOrEmpty(configuration.OAuthTokenUrl) &&
!string.IsNullOrEmpty(configuration.OAuthClientId) && !string.IsNullOrEmpty(configuration.OAuthClientId) &&
!string.IsNullOrEmpty(configuration.OAuthClientSecret) && !string.IsNullOrEmpty(configuration.OAuthClientSecret) &&
@ -474,27 +484,14 @@ namespace Org.OpenAPITools.Client
{ {
InterceptRequest(request); InterceptRequest(request);
RestResponse<T> response; RestResponse<T> response = getResponse(client);
if (RetryConfiguration.RetryPolicy != null)
{
var policy = RetryConfiguration.RetryPolicy;
var policyResult = policy.ExecuteAndCapture(() => client.Execute(request));
response = (policyResult.Outcome == OutcomeType.Successful) ? client.Deserialize<T>(policyResult.Result) : new RestResponse<T>(request)
{
ErrorException = policyResult.FinalException
};
}
else
{
response = client.Execute<T>(request);
}
// if the response type is oneOf/anyOf, call FromJSON to deserialize the data // if the response type is oneOf/anyOf, call FromJSON to deserialize the data
if (typeof(Org.OpenAPITools.Model.AbstractOpenAPISchema).IsAssignableFrom(typeof(T))) if (typeof(AbstractOpenAPISchema).IsAssignableFrom(typeof(T)))
{ {
try try
{ {
response.Data = (T) typeof(T).GetMethod("FromJson").Invoke(null, new object[] { response.Content }); response.Data = (T)typeof(T).GetMethod("FromJson").Invoke(null, new object[] { response.Content });
} }
catch (Exception ex) catch (Exception ex)
{ {
@ -552,104 +549,80 @@ namespace Org.OpenAPITools.Client
} }
} }
private async Task<ApiResponse<T>> ExecAsync<T>(RestRequest request, RequestOptions options, IReadableConfiguration configuration, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) private RestResponse<T> DeserializeRestResponseFromPolicy<T>(RestClient client, RestRequest request, PolicyResult<RestResponse> policyResult)
{ {
var baseUrl = configuration.GetOperationServerUrl(options.Operation, options.OperationIndex) ?? _baseUrl; if (policyResult.Outcome == OutcomeType.Successful)
var clientOptions = new RestClientOptions(baseUrl)
{ {
ClientCertificates = configuration.ClientCertificates, return client.Deserialize<T>(policyResult.Result);
MaxTimeout = configuration.Timeout, }
Proxy = configuration.Proxy, else
UserAgent = configuration.UserAgent, {
UseDefaultCredentials = configuration.UseDefaultCredentials, return new RestResponse<T>(request)
RemoteCertificateValidationCallback = configuration.RemoteCertificateValidationCallback {
ErrorException = policyResult.FinalException
};
}
}
private ApiResponse<T> Exec<T>(RestRequest request, RequestOptions options, IReadableConfiguration configuration)
{
Action<RestClientOptions> setOptions = (clientOptions) =>
{
var cookies = new CookieContainer();
if (options.Cookies != null && options.Cookies.Count > 0)
{
foreach (var cookie in options.Cookies)
{
cookies.Add(new Cookie(cookie.Name, cookie.Value));
}
}
clientOptions.CookieContainer = cookies;
}; };
if (!string.IsNullOrEmpty(configuration.OAuthTokenUrl) && Func<RestClient, RestResponse<T>> getResponse = (client) =>
!string.IsNullOrEmpty(configuration.OAuthClientId) &&
!string.IsNullOrEmpty(configuration.OAuthClientSecret) &&
configuration.OAuthFlow != null)
{ {
clientOptions.Authenticator = new OAuthAuthenticator( if (RetryConfiguration.RetryPolicy != null)
configuration.OAuthTokenUrl,
configuration.OAuthClientId,
configuration.OAuthClientSecret,
configuration.OAuthFlow,
SerializerSettings,
configuration);
}
using (RestClient client = new RestClient(clientOptions,
configureSerialization: serializerConfig => serializerConfig.UseSerializer(() => new CustomJsonCodec(SerializerSettings, configuration))))
{
InterceptRequest(request);
RestResponse<T> response;
if (RetryConfiguration.AsyncRetryPolicy != null)
{ {
var policy = RetryConfiguration.AsyncRetryPolicy; var policy = RetryConfiguration.RetryPolicy;
var policyResult = await policy.ExecuteAndCaptureAsync((ct) => client.ExecuteAsync(request, ct), cancellationToken).ConfigureAwait(false); var policyResult = policy.ExecuteAndCapture(() => client.Execute(request));
response = (policyResult.Outcome == OutcomeType.Successful) ? client.Deserialize<T>(policyResult.Result) : new RestResponse<T>(request) return DeserializeRestResponseFromPolicy<T>(client, request, policyResult);
{
ErrorException = policyResult.FinalException
};
} }
else else
{ {
response = await client.ExecuteAsync<T>(request, cancellationToken).ConfigureAwait(false); return client.Execute<T>(request);
} }
};
// if the response type is oneOf/anyOf, call FromJSON to deserialize the data return ExecClient(getResponse, setOptions, request, options, configuration);
if (typeof(Org.OpenAPITools.Model.AbstractOpenAPISchema).IsAssignableFrom(typeof(T))) }
{
response.Data = (T) typeof(T).GetMethod("FromJson").Invoke(null, new object[] { response.Content });
}
else if (typeof(T).Name == "Stream") // for binary response
{
response.Data = (T)(object)new MemoryStream(response.RawBytes);
}
else if (typeof(T).Name == "Byte[]") // for byte response
{
response.Data = (T)(object)response.RawBytes;
}
InterceptResponse(request, response); private Task<ApiResponse<T>> ExecAsync<T>(RestRequest request, RequestOptions options, IReadableConfiguration configuration, CancellationToken cancellationToken = default(CancellationToken))
{
Action<RestClientOptions> setOptions = (clientOptions) =>
{
//no extra options
};
var result = ToApiResponse(response); Func<RestClient, RestResponse<T>> getResponse = (client) =>
if (response.ErrorMessage != null) {
Func<Task<RestResponse<T>>> action = async () =>
{ {
result.ErrorText = response.ErrorMessage; if (RetryConfiguration.AsyncRetryPolicy != null)
}
if (response.Cookies != null && response.Cookies.Count > 0)
{
if (result.Cookies == null) result.Cookies = new List<Cookie>();
foreach (var restResponseCookie in response.Cookies.Cast<Cookie>())
{ {
var cookie = new Cookie( var policy = RetryConfiguration.AsyncRetryPolicy;
restResponseCookie.Name, var policyResult = await policy.ExecuteAndCaptureAsync((ct) => client.ExecuteAsync(request, ct), cancellationToken).ConfigureAwait(false);
restResponseCookie.Value, return DeserializeRestResponseFromPolicy<T>(client, request, policyResult);
restResponseCookie.Path,
restResponseCookie.Domain
)
{
Comment = restResponseCookie.Comment,
CommentUri = restResponseCookie.CommentUri,
Discard = restResponseCookie.Discard,
Expired = restResponseCookie.Expired,
Expires = restResponseCookie.Expires,
HttpOnly = restResponseCookie.HttpOnly,
Port = restResponseCookie.Port,
Secure = restResponseCookie.Secure,
Version = restResponseCookie.Version
};
result.Cookies.Add(cookie);
} }
} else
return result; {
} return await client.ExecuteAsync<T>(request, cancellationToken).ConfigureAwait(false);
}
};
return action().Result;
};
return Task.FromResult<ApiResponse<T>>(ExecClient(getResponse, setOptions, request, options, configuration));
} }
#region IAsynchronousClient #region IAsynchronousClient
@ -662,7 +635,7 @@ namespace Org.OpenAPITools.Client
/// GlobalConfiguration has been done before calling this method.</param> /// GlobalConfiguration has been done before calling this method.</param>
/// <param name="cancellationToken">Token that enables callers to cancel the request.</param> /// <param name="cancellationToken">Token that enables callers to cancel the request.</param>
/// <returns>A Task containing ApiResponse</returns> /// <returns>A Task containing ApiResponse</returns>
public Task<ApiResponse<T>> GetAsync<T>(string path, RequestOptions options, IReadableConfiguration configuration = null, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) public Task<ApiResponse<T>> GetAsync<T>(string path, RequestOptions options, IReadableConfiguration configuration = null, CancellationToken cancellationToken = default)
{ {
var config = configuration ?? GlobalConfiguration.Instance; var config = configuration ?? GlobalConfiguration.Instance;
return ExecAsync<T>(NewRequest(HttpMethod.Get, path, options, config), options, config, cancellationToken); return ExecAsync<T>(NewRequest(HttpMethod.Get, path, options, config), options, config, cancellationToken);
@ -677,7 +650,7 @@ namespace Org.OpenAPITools.Client
/// GlobalConfiguration has been done before calling this method.</param> /// GlobalConfiguration has been done before calling this method.</param>
/// <param name="cancellationToken">Token that enables callers to cancel the request.</param> /// <param name="cancellationToken">Token that enables callers to cancel the request.</param>
/// <returns>A Task containing ApiResponse</returns> /// <returns>A Task containing ApiResponse</returns>
public Task<ApiResponse<T>> PostAsync<T>(string path, RequestOptions options, IReadableConfiguration configuration = null, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) public Task<ApiResponse<T>> PostAsync<T>(string path, RequestOptions options, IReadableConfiguration configuration = null, CancellationToken cancellationToken = default)
{ {
var config = configuration ?? GlobalConfiguration.Instance; var config = configuration ?? GlobalConfiguration.Instance;
return ExecAsync<T>(NewRequest(HttpMethod.Post, path, options, config), options, config, cancellationToken); return ExecAsync<T>(NewRequest(HttpMethod.Post, path, options, config), options, config, cancellationToken);
@ -692,7 +665,7 @@ namespace Org.OpenAPITools.Client
/// GlobalConfiguration has been done before calling this method.</param> /// GlobalConfiguration has been done before calling this method.</param>
/// <param name="cancellationToken">Token that enables callers to cancel the request.</param> /// <param name="cancellationToken">Token that enables callers to cancel the request.</param>
/// <returns>A Task containing ApiResponse</returns> /// <returns>A Task containing ApiResponse</returns>
public Task<ApiResponse<T>> PutAsync<T>(string path, RequestOptions options, IReadableConfiguration configuration = null, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) public Task<ApiResponse<T>> PutAsync<T>(string path, RequestOptions options, IReadableConfiguration configuration = null, CancellationToken cancellationToken = default)
{ {
var config = configuration ?? GlobalConfiguration.Instance; var config = configuration ?? GlobalConfiguration.Instance;
return ExecAsync<T>(NewRequest(HttpMethod.Put, path, options, config), options, config, cancellationToken); return ExecAsync<T>(NewRequest(HttpMethod.Put, path, options, config), options, config, cancellationToken);
@ -707,7 +680,7 @@ namespace Org.OpenAPITools.Client
/// GlobalConfiguration has been done before calling this method.</param> /// GlobalConfiguration has been done before calling this method.</param>
/// <param name="cancellationToken">Token that enables callers to cancel the request.</param> /// <param name="cancellationToken">Token that enables callers to cancel the request.</param>
/// <returns>A Task containing ApiResponse</returns> /// <returns>A Task containing ApiResponse</returns>
public Task<ApiResponse<T>> DeleteAsync<T>(string path, RequestOptions options, IReadableConfiguration configuration = null, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) public Task<ApiResponse<T>> DeleteAsync<T>(string path, RequestOptions options, IReadableConfiguration configuration = null, CancellationToken cancellationToken = default)
{ {
var config = configuration ?? GlobalConfiguration.Instance; var config = configuration ?? GlobalConfiguration.Instance;
return ExecAsync<T>(NewRequest(HttpMethod.Delete, path, options, config), options, config, cancellationToken); return ExecAsync<T>(NewRequest(HttpMethod.Delete, path, options, config), options, config, cancellationToken);
@ -722,7 +695,7 @@ namespace Org.OpenAPITools.Client
/// GlobalConfiguration has been done before calling this method.</param> /// GlobalConfiguration has been done before calling this method.</param>
/// <param name="cancellationToken">Token that enables callers to cancel the request.</param> /// <param name="cancellationToken">Token that enables callers to cancel the request.</param>
/// <returns>A Task containing ApiResponse</returns> /// <returns>A Task containing ApiResponse</returns>
public Task<ApiResponse<T>> HeadAsync<T>(string path, RequestOptions options, IReadableConfiguration configuration = null, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) public Task<ApiResponse<T>> HeadAsync<T>(string path, RequestOptions options, IReadableConfiguration configuration = null, CancellationToken cancellationToken = default)
{ {
var config = configuration ?? GlobalConfiguration.Instance; var config = configuration ?? GlobalConfiguration.Instance;
return ExecAsync<T>(NewRequest(HttpMethod.Head, path, options, config), options, config, cancellationToken); return ExecAsync<T>(NewRequest(HttpMethod.Head, path, options, config), options, config, cancellationToken);
@ -737,7 +710,7 @@ namespace Org.OpenAPITools.Client
/// GlobalConfiguration has been done before calling this method.</param> /// GlobalConfiguration has been done before calling this method.</param>
/// <param name="cancellationToken">Token that enables callers to cancel the request.</param> /// <param name="cancellationToken">Token that enables callers to cancel the request.</param>
/// <returns>A Task containing ApiResponse</returns> /// <returns>A Task containing ApiResponse</returns>
public Task<ApiResponse<T>> OptionsAsync<T>(string path, RequestOptions options, IReadableConfiguration configuration = null, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) public Task<ApiResponse<T>> OptionsAsync<T>(string path, RequestOptions options, IReadableConfiguration configuration = null, CancellationToken cancellationToken = default)
{ {
var config = configuration ?? GlobalConfiguration.Instance; var config = configuration ?? GlobalConfiguration.Instance;
return ExecAsync<T>(NewRequest(HttpMethod.Options, path, options, config), options, config, cancellationToken); return ExecAsync<T>(NewRequest(HttpMethod.Options, path, options, config), options, config, cancellationToken);
@ -752,7 +725,7 @@ namespace Org.OpenAPITools.Client
/// GlobalConfiguration has been done before calling this method.</param> /// GlobalConfiguration has been done before calling this method.</param>
/// <param name="cancellationToken">Token that enables callers to cancel the request.</param> /// <param name="cancellationToken">Token that enables callers to cancel the request.</param>
/// <returns>A Task containing ApiResponse</returns> /// <returns>A Task containing ApiResponse</returns>
public Task<ApiResponse<T>> PatchAsync<T>(string path, RequestOptions options, IReadableConfiguration configuration = null, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) public Task<ApiResponse<T>> PatchAsync<T>(string path, RequestOptions options, IReadableConfiguration configuration = null, CancellationToken cancellationToken = default)
{ {
var config = configuration ?? GlobalConfiguration.Instance; var config = configuration ?? GlobalConfiguration.Instance;
return ExecAsync<T>(NewRequest(HttpMethod.Patch, path, options, config), options, config, cancellationToken); return ExecAsync<T>(NewRequest(HttpMethod.Patch, path, options, config), options, config, cancellationToken);

View File

@ -28,8 +28,10 @@ using Newtonsoft.Json.Serialization;
using RestSharp; using RestSharp;
using RestSharp.Serializers; using RestSharp.Serializers;
using RestSharpMethod = RestSharp.Method; using RestSharpMethod = RestSharp.Method;
using FileIO = System.IO.File;
using Polly; using Polly;
using Org.OpenAPITools.Client.Auth; using Org.OpenAPITools.Client.Auth;
using Org.OpenAPITools.Model;
namespace Org.OpenAPITools.Client namespace Org.OpenAPITools.Client
{ {
@ -70,10 +72,10 @@ namespace Org.OpenAPITools.Client
/// <returns>A JSON string.</returns> /// <returns>A JSON string.</returns>
public string Serialize(object obj) public string Serialize(object obj)
{ {
if (obj != null && obj is Org.OpenAPITools.Model.AbstractOpenAPISchema) if (obj != null && obj is AbstractOpenAPISchema)
{ {
// the object to be serialized is an oneOf/anyOf schema // the object to be serialized is an oneOf/anyOf schema
return ((Org.OpenAPITools.Model.AbstractOpenAPISchema)obj).ToJson(); return ((AbstractOpenAPISchema)obj).ToJson();
} }
else else
{ {
@ -118,7 +120,7 @@ namespace Org.OpenAPITools.Client
if (match.Success) if (match.Success)
{ {
string fileName = filePath + ClientUtils.SanitizeFilename(match.Groups[1].Value.Replace("\"", "").Replace("'", "")); string fileName = filePath + ClientUtils.SanitizeFilename(match.Groups[1].Value.Replace("\"", "").Replace("'", ""));
File.WriteAllBytes(fileName, bytes); FileIO.WriteAllBytes(fileName, bytes);
return new FileStream(fileName, FileMode.Open); return new FileStream(fileName, FileMode.Open);
} }
} }
@ -129,7 +131,7 @@ namespace Org.OpenAPITools.Client
if (type.Name.StartsWith("System.Nullable`1[[System.DateTime")) // return a datetime object if (type.Name.StartsWith("System.Nullable`1[[System.DateTime")) // return a datetime object
{ {
return DateTime.Parse(response.Content, null, System.Globalization.DateTimeStyles.RoundtripKind); return DateTime.Parse(response.Content, null, DateTimeStyles.RoundtripKind);
} }
if (type == typeof(string) || type.Name.StartsWith("System.Nullable")) // return primitive type if (type == typeof(string) || type.Name.StartsWith("System.Nullable")) // return primitive type
@ -151,13 +153,13 @@ namespace Org.OpenAPITools.Client
public ISerializer Serializer => this; public ISerializer Serializer => this;
public IDeserializer Deserializer => this; public IDeserializer Deserializer => this;
public string[] AcceptedContentTypes => RestSharp.ContentType.JsonAccept; public string[] AcceptedContentTypes => ContentType.JsonAccept;
public SupportsContentType SupportsContentType => contentType => public SupportsContentType SupportsContentType => contentType =>
contentType.Value.EndsWith("json", StringComparison.InvariantCultureIgnoreCase) || contentType.Value.EndsWith("json", StringComparison.InvariantCultureIgnoreCase) ||
contentType.Value.EndsWith("javascript", StringComparison.InvariantCultureIgnoreCase); contentType.Value.EndsWith("javascript", StringComparison.InvariantCultureIgnoreCase);
public ContentType ContentType { get; set; } = RestSharp.ContentType.Json; public ContentType ContentType { get; set; } = ContentType.Json;
public DataFormat DataFormat => DataFormat.Json; public DataFormat DataFormat => DataFormat.Json;
} }
@ -204,7 +206,7 @@ namespace Org.OpenAPITools.Client
/// </summary> /// </summary>
public ApiClient() public ApiClient()
{ {
_baseUrl = Org.OpenAPITools.Client.GlobalConfiguration.Instance.BasePath; _baseUrl = GlobalConfiguration.Instance.BasePath;
} }
/// <summary> /// <summary>
@ -261,14 +263,14 @@ namespace Org.OpenAPITools.Client
/// <summary> /// <summary>
/// Provides all logic for constructing a new RestSharp <see cref="RestRequest"/>. /// Provides all logic for constructing a new RestSharp <see cref="RestRequest"/>.
/// At this point, all information for querying the service is known. Here, it is simply /// At this point, all information for querying the service is known.
/// mapped into the RestSharp request. /// Here, it is simply mapped into the RestSharp request.
/// </summary> /// </summary>
/// <param name="method">The http verb.</param> /// <param name="method">The http verb.</param>
/// <param name="path">The target path (or resource).</param> /// <param name="path">The target path (or resource).</param>
/// <param name="options">The additional request options.</param> /// <param name="options">The additional request options.</param>
/// <param name="configuration">A per-request configuration object. It is assumed that any merge with /// <param name="configuration">A per-request configuration object.
/// GlobalConfiguration has been done before calling this method.</param> /// It is assumed that any merge with GlobalConfiguration has been done before calling this method.</param>
/// <returns>[private] A new RestRequest instance.</returns> /// <returns>[private] A new RestRequest instance.</returns>
/// <exception cref="ArgumentNullException"></exception> /// <exception cref="ArgumentNullException"></exception>
private RestRequest NewRequest( private RestRequest NewRequest(
@ -376,7 +378,7 @@ namespace Org.OpenAPITools.Client
var bytes = ClientUtils.ReadAsBytes(file); var bytes = ClientUtils.ReadAsBytes(file);
var fileStream = file as FileStream; var fileStream = file as FileStream;
if (fileStream != null) if (fileStream != null)
request.AddFile(fileParam.Key, bytes, System.IO.Path.GetFileName(fileStream.Name)); request.AddFile(fileParam.Key, bytes, Path.GetFileName(fileStream.Name));
else else
request.AddFile(fileParam.Key, bytes, "no_file_name_provided"); request.AddFile(fileParam.Key, bytes, "no_file_name_provided");
} }
@ -386,6 +388,13 @@ namespace Org.OpenAPITools.Client
return request; return request;
} }
/// <summary>
/// Transforms a RestResponse instance into a new ApiResponse instance.
/// At this point, we have a concrete http response from the service.
/// Here, it is simply mapped into the [public] ApiResponse object.
/// </summary>
/// <param name="response">The RestSharp response object</param>
/// <returns>A new ApiResponse instance.</returns>
private ApiResponse<T> ToApiResponse<T>(RestResponse<T> response) private ApiResponse<T> ToApiResponse<T>(RestResponse<T> response)
{ {
T result = response.Data; T result = response.Data;
@ -430,31 +439,32 @@ namespace Org.OpenAPITools.Client
return transformed; return transformed;
} }
private ApiResponse<T> Exec<T>(RestRequest request, RequestOptions options, IReadableConfiguration configuration) /// <summary>
/// Executes the HTTP request for the current service.
/// Based on functions received it can be async or sync.
/// </summary>
/// <param name="getResponse">Local function that executes http request and returns http response.</param>
/// <param name="setOptions">Local function to specify options for the service.</param>
/// <param name="request">The RestSharp request object</param>
/// <param name="options">The RestSharp options object</param>
/// <param name="configuration">A per-request configuration object.
/// It is assumed that any merge with GlobalConfiguration has been done before calling this method.</param>
/// <returns>A new ApiResponse instance.</returns>
private ApiResponse<T> ExecClient<T>(Func<RestClient, RestResponse<T>> getResponse, Action<RestClientOptions> setOptions, RestRequest request, RequestOptions options, IReadableConfiguration configuration)
{ {
var baseUrl = configuration.GetOperationServerUrl(options.Operation, options.OperationIndex) ?? _baseUrl; var baseUrl = configuration.GetOperationServerUrl(options.Operation, options.OperationIndex) ?? _baseUrl;
var cookies = new CookieContainer();
if (options.Cookies != null && options.Cookies.Count > 0)
{
foreach (var cookie in options.Cookies)
{
cookies.Add(new Cookie(cookie.Name, cookie.Value));
}
}
var clientOptions = new RestClientOptions(baseUrl) var clientOptions = new RestClientOptions(baseUrl)
{ {
ClientCertificates = configuration.ClientCertificates, ClientCertificates = configuration.ClientCertificates,
CookieContainer = cookies,
MaxTimeout = configuration.Timeout, MaxTimeout = configuration.Timeout,
Proxy = configuration.Proxy, Proxy = configuration.Proxy,
UserAgent = configuration.UserAgent, UserAgent = configuration.UserAgent,
UseDefaultCredentials = configuration.UseDefaultCredentials, UseDefaultCredentials = configuration.UseDefaultCredentials,
RemoteCertificateValidationCallback = configuration.RemoteCertificateValidationCallback RemoteCertificateValidationCallback = configuration.RemoteCertificateValidationCallback
}; };
setOptions(clientOptions);
if (!string.IsNullOrEmpty(configuration.OAuthTokenUrl) && if (!string.IsNullOrEmpty(configuration.OAuthTokenUrl) &&
!string.IsNullOrEmpty(configuration.OAuthClientId) && !string.IsNullOrEmpty(configuration.OAuthClientId) &&
!string.IsNullOrEmpty(configuration.OAuthClientSecret) && !string.IsNullOrEmpty(configuration.OAuthClientSecret) &&
@ -474,27 +484,14 @@ namespace Org.OpenAPITools.Client
{ {
InterceptRequest(request); InterceptRequest(request);
RestResponse<T> response; RestResponse<T> response = getResponse(client);
if (RetryConfiguration.RetryPolicy != null)
{
var policy = RetryConfiguration.RetryPolicy;
var policyResult = policy.ExecuteAndCapture(() => client.Execute(request));
response = (policyResult.Outcome == OutcomeType.Successful) ? client.Deserialize<T>(policyResult.Result) : new RestResponse<T>(request)
{
ErrorException = policyResult.FinalException
};
}
else
{
response = client.Execute<T>(request);
}
// if the response type is oneOf/anyOf, call FromJSON to deserialize the data // if the response type is oneOf/anyOf, call FromJSON to deserialize the data
if (typeof(Org.OpenAPITools.Model.AbstractOpenAPISchema).IsAssignableFrom(typeof(T))) if (typeof(AbstractOpenAPISchema).IsAssignableFrom(typeof(T)))
{ {
try try
{ {
response.Data = (T) typeof(T).GetMethod("FromJson").Invoke(null, new object[] { response.Content }); response.Data = (T)typeof(T).GetMethod("FromJson").Invoke(null, new object[] { response.Content });
} }
catch (Exception ex) catch (Exception ex)
{ {
@ -552,104 +549,80 @@ namespace Org.OpenAPITools.Client
} }
} }
private async Task<ApiResponse<T>> ExecAsync<T>(RestRequest request, RequestOptions options, IReadableConfiguration configuration, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) private RestResponse<T> DeserializeRestResponseFromPolicy<T>(RestClient client, RestRequest request, PolicyResult<RestResponse> policyResult)
{ {
var baseUrl = configuration.GetOperationServerUrl(options.Operation, options.OperationIndex) ?? _baseUrl; if (policyResult.Outcome == OutcomeType.Successful)
var clientOptions = new RestClientOptions(baseUrl)
{ {
ClientCertificates = configuration.ClientCertificates, return client.Deserialize<T>(policyResult.Result);
MaxTimeout = configuration.Timeout, }
Proxy = configuration.Proxy, else
UserAgent = configuration.UserAgent, {
UseDefaultCredentials = configuration.UseDefaultCredentials, return new RestResponse<T>(request)
RemoteCertificateValidationCallback = configuration.RemoteCertificateValidationCallback {
ErrorException = policyResult.FinalException
};
}
}
private ApiResponse<T> Exec<T>(RestRequest request, RequestOptions options, IReadableConfiguration configuration)
{
Action<RestClientOptions> setOptions = (clientOptions) =>
{
var cookies = new CookieContainer();
if (options.Cookies != null && options.Cookies.Count > 0)
{
foreach (var cookie in options.Cookies)
{
cookies.Add(new Cookie(cookie.Name, cookie.Value));
}
}
clientOptions.CookieContainer = cookies;
}; };
if (!string.IsNullOrEmpty(configuration.OAuthTokenUrl) && Func<RestClient, RestResponse<T>> getResponse = (client) =>
!string.IsNullOrEmpty(configuration.OAuthClientId) &&
!string.IsNullOrEmpty(configuration.OAuthClientSecret) &&
configuration.OAuthFlow != null)
{ {
clientOptions.Authenticator = new OAuthAuthenticator( if (RetryConfiguration.RetryPolicy != null)
configuration.OAuthTokenUrl,
configuration.OAuthClientId,
configuration.OAuthClientSecret,
configuration.OAuthFlow,
SerializerSettings,
configuration);
}
using (RestClient client = new RestClient(clientOptions,
configureSerialization: serializerConfig => serializerConfig.UseSerializer(() => new CustomJsonCodec(SerializerSettings, configuration))))
{
InterceptRequest(request);
RestResponse<T> response;
if (RetryConfiguration.AsyncRetryPolicy != null)
{ {
var policy = RetryConfiguration.AsyncRetryPolicy; var policy = RetryConfiguration.RetryPolicy;
var policyResult = await policy.ExecuteAndCaptureAsync((ct) => client.ExecuteAsync(request, ct), cancellationToken).ConfigureAwait(false); var policyResult = policy.ExecuteAndCapture(() => client.Execute(request));
response = (policyResult.Outcome == OutcomeType.Successful) ? client.Deserialize<T>(policyResult.Result) : new RestResponse<T>(request) return DeserializeRestResponseFromPolicy<T>(client, request, policyResult);
{
ErrorException = policyResult.FinalException
};
} }
else else
{ {
response = await client.ExecuteAsync<T>(request, cancellationToken).ConfigureAwait(false); return client.Execute<T>(request);
} }
};
// if the response type is oneOf/anyOf, call FromJSON to deserialize the data return ExecClient(getResponse, setOptions, request, options, configuration);
if (typeof(Org.OpenAPITools.Model.AbstractOpenAPISchema).IsAssignableFrom(typeof(T))) }
{
response.Data = (T) typeof(T).GetMethod("FromJson").Invoke(null, new object[] { response.Content });
}
else if (typeof(T).Name == "Stream") // for binary response
{
response.Data = (T)(object)new MemoryStream(response.RawBytes);
}
else if (typeof(T).Name == "Byte[]") // for byte response
{
response.Data = (T)(object)response.RawBytes;
}
InterceptResponse(request, response); private Task<ApiResponse<T>> ExecAsync<T>(RestRequest request, RequestOptions options, IReadableConfiguration configuration, CancellationToken cancellationToken = default(CancellationToken))
{
Action<RestClientOptions> setOptions = (clientOptions) =>
{
//no extra options
};
var result = ToApiResponse(response); Func<RestClient, RestResponse<T>> getResponse = (client) =>
if (response.ErrorMessage != null) {
Func<Task<RestResponse<T>>> action = async () =>
{ {
result.ErrorText = response.ErrorMessage; if (RetryConfiguration.AsyncRetryPolicy != null)
}
if (response.Cookies != null && response.Cookies.Count > 0)
{
if (result.Cookies == null) result.Cookies = new List<Cookie>();
foreach (var restResponseCookie in response.Cookies.Cast<Cookie>())
{ {
var cookie = new Cookie( var policy = RetryConfiguration.AsyncRetryPolicy;
restResponseCookie.Name, var policyResult = await policy.ExecuteAndCaptureAsync((ct) => client.ExecuteAsync(request, ct), cancellationToken).ConfigureAwait(false);
restResponseCookie.Value, return DeserializeRestResponseFromPolicy<T>(client, request, policyResult);
restResponseCookie.Path,
restResponseCookie.Domain
)
{
Comment = restResponseCookie.Comment,
CommentUri = restResponseCookie.CommentUri,
Discard = restResponseCookie.Discard,
Expired = restResponseCookie.Expired,
Expires = restResponseCookie.Expires,
HttpOnly = restResponseCookie.HttpOnly,
Port = restResponseCookie.Port,
Secure = restResponseCookie.Secure,
Version = restResponseCookie.Version
};
result.Cookies.Add(cookie);
} }
} else
return result; {
} return await client.ExecuteAsync<T>(request, cancellationToken).ConfigureAwait(false);
}
};
return action().Result;
};
return Task.FromResult<ApiResponse<T>>(ExecClient(getResponse, setOptions, request, options, configuration));
} }
#region IAsynchronousClient #region IAsynchronousClient
@ -662,7 +635,7 @@ namespace Org.OpenAPITools.Client
/// GlobalConfiguration has been done before calling this method.</param> /// GlobalConfiguration has been done before calling this method.</param>
/// <param name="cancellationToken">Token that enables callers to cancel the request.</param> /// <param name="cancellationToken">Token that enables callers to cancel the request.</param>
/// <returns>A Task containing ApiResponse</returns> /// <returns>A Task containing ApiResponse</returns>
public Task<ApiResponse<T>> GetAsync<T>(string path, RequestOptions options, IReadableConfiguration configuration = null, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) public Task<ApiResponse<T>> GetAsync<T>(string path, RequestOptions options, IReadableConfiguration configuration = null, CancellationToken cancellationToken = default)
{ {
var config = configuration ?? GlobalConfiguration.Instance; var config = configuration ?? GlobalConfiguration.Instance;
return ExecAsync<T>(NewRequest(HttpMethod.Get, path, options, config), options, config, cancellationToken); return ExecAsync<T>(NewRequest(HttpMethod.Get, path, options, config), options, config, cancellationToken);
@ -677,7 +650,7 @@ namespace Org.OpenAPITools.Client
/// GlobalConfiguration has been done before calling this method.</param> /// GlobalConfiguration has been done before calling this method.</param>
/// <param name="cancellationToken">Token that enables callers to cancel the request.</param> /// <param name="cancellationToken">Token that enables callers to cancel the request.</param>
/// <returns>A Task containing ApiResponse</returns> /// <returns>A Task containing ApiResponse</returns>
public Task<ApiResponse<T>> PostAsync<T>(string path, RequestOptions options, IReadableConfiguration configuration = null, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) public Task<ApiResponse<T>> PostAsync<T>(string path, RequestOptions options, IReadableConfiguration configuration = null, CancellationToken cancellationToken = default)
{ {
var config = configuration ?? GlobalConfiguration.Instance; var config = configuration ?? GlobalConfiguration.Instance;
return ExecAsync<T>(NewRequest(HttpMethod.Post, path, options, config), options, config, cancellationToken); return ExecAsync<T>(NewRequest(HttpMethod.Post, path, options, config), options, config, cancellationToken);
@ -692,7 +665,7 @@ namespace Org.OpenAPITools.Client
/// GlobalConfiguration has been done before calling this method.</param> /// GlobalConfiguration has been done before calling this method.</param>
/// <param name="cancellationToken">Token that enables callers to cancel the request.</param> /// <param name="cancellationToken">Token that enables callers to cancel the request.</param>
/// <returns>A Task containing ApiResponse</returns> /// <returns>A Task containing ApiResponse</returns>
public Task<ApiResponse<T>> PutAsync<T>(string path, RequestOptions options, IReadableConfiguration configuration = null, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) public Task<ApiResponse<T>> PutAsync<T>(string path, RequestOptions options, IReadableConfiguration configuration = null, CancellationToken cancellationToken = default)
{ {
var config = configuration ?? GlobalConfiguration.Instance; var config = configuration ?? GlobalConfiguration.Instance;
return ExecAsync<T>(NewRequest(HttpMethod.Put, path, options, config), options, config, cancellationToken); return ExecAsync<T>(NewRequest(HttpMethod.Put, path, options, config), options, config, cancellationToken);
@ -707,7 +680,7 @@ namespace Org.OpenAPITools.Client
/// GlobalConfiguration has been done before calling this method.</param> /// GlobalConfiguration has been done before calling this method.</param>
/// <param name="cancellationToken">Token that enables callers to cancel the request.</param> /// <param name="cancellationToken">Token that enables callers to cancel the request.</param>
/// <returns>A Task containing ApiResponse</returns> /// <returns>A Task containing ApiResponse</returns>
public Task<ApiResponse<T>> DeleteAsync<T>(string path, RequestOptions options, IReadableConfiguration configuration = null, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) public Task<ApiResponse<T>> DeleteAsync<T>(string path, RequestOptions options, IReadableConfiguration configuration = null, CancellationToken cancellationToken = default)
{ {
var config = configuration ?? GlobalConfiguration.Instance; var config = configuration ?? GlobalConfiguration.Instance;
return ExecAsync<T>(NewRequest(HttpMethod.Delete, path, options, config), options, config, cancellationToken); return ExecAsync<T>(NewRequest(HttpMethod.Delete, path, options, config), options, config, cancellationToken);
@ -722,7 +695,7 @@ namespace Org.OpenAPITools.Client
/// GlobalConfiguration has been done before calling this method.</param> /// GlobalConfiguration has been done before calling this method.</param>
/// <param name="cancellationToken">Token that enables callers to cancel the request.</param> /// <param name="cancellationToken">Token that enables callers to cancel the request.</param>
/// <returns>A Task containing ApiResponse</returns> /// <returns>A Task containing ApiResponse</returns>
public Task<ApiResponse<T>> HeadAsync<T>(string path, RequestOptions options, IReadableConfiguration configuration = null, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) public Task<ApiResponse<T>> HeadAsync<T>(string path, RequestOptions options, IReadableConfiguration configuration = null, CancellationToken cancellationToken = default)
{ {
var config = configuration ?? GlobalConfiguration.Instance; var config = configuration ?? GlobalConfiguration.Instance;
return ExecAsync<T>(NewRequest(HttpMethod.Head, path, options, config), options, config, cancellationToken); return ExecAsync<T>(NewRequest(HttpMethod.Head, path, options, config), options, config, cancellationToken);
@ -737,7 +710,7 @@ namespace Org.OpenAPITools.Client
/// GlobalConfiguration has been done before calling this method.</param> /// GlobalConfiguration has been done before calling this method.</param>
/// <param name="cancellationToken">Token that enables callers to cancel the request.</param> /// <param name="cancellationToken">Token that enables callers to cancel the request.</param>
/// <returns>A Task containing ApiResponse</returns> /// <returns>A Task containing ApiResponse</returns>
public Task<ApiResponse<T>> OptionsAsync<T>(string path, RequestOptions options, IReadableConfiguration configuration = null, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) public Task<ApiResponse<T>> OptionsAsync<T>(string path, RequestOptions options, IReadableConfiguration configuration = null, CancellationToken cancellationToken = default)
{ {
var config = configuration ?? GlobalConfiguration.Instance; var config = configuration ?? GlobalConfiguration.Instance;
return ExecAsync<T>(NewRequest(HttpMethod.Options, path, options, config), options, config, cancellationToken); return ExecAsync<T>(NewRequest(HttpMethod.Options, path, options, config), options, config, cancellationToken);
@ -752,7 +725,7 @@ namespace Org.OpenAPITools.Client
/// GlobalConfiguration has been done before calling this method.</param> /// GlobalConfiguration has been done before calling this method.</param>
/// <param name="cancellationToken">Token that enables callers to cancel the request.</param> /// <param name="cancellationToken">Token that enables callers to cancel the request.</param>
/// <returns>A Task containing ApiResponse</returns> /// <returns>A Task containing ApiResponse</returns>
public Task<ApiResponse<T>> PatchAsync<T>(string path, RequestOptions options, IReadableConfiguration configuration = null, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) public Task<ApiResponse<T>> PatchAsync<T>(string path, RequestOptions options, IReadableConfiguration configuration = null, CancellationToken cancellationToken = default)
{ {
var config = configuration ?? GlobalConfiguration.Instance; var config = configuration ?? GlobalConfiguration.Instance;
return ExecAsync<T>(NewRequest(HttpMethod.Patch, path, options, config), options, config, cancellationToken); return ExecAsync<T>(NewRequest(HttpMethod.Patch, path, options, config), options, config, cancellationToken);

View File

@ -22,13 +22,14 @@ using System.Text;
using System.Threading; using System.Threading;
using System.Text.RegularExpressions; using System.Text.RegularExpressions;
using System.Threading.Tasks; using System.Threading.Tasks;
using System.Web;
using Newtonsoft.Json; using Newtonsoft.Json;
using Newtonsoft.Json.Serialization; using Newtonsoft.Json.Serialization;
using RestSharp; using RestSharp;
using RestSharp.Serializers; using RestSharp.Serializers;
using RestSharpMethod = RestSharp.Method; using RestSharpMethod = RestSharp.Method;
using FileIO = System.IO.File;
using Polly; using Polly;
using Org.OpenAPITools.Model;
namespace Org.OpenAPITools.Client namespace Org.OpenAPITools.Client
{ {
@ -69,10 +70,10 @@ namespace Org.OpenAPITools.Client
/// <returns>A JSON string.</returns> /// <returns>A JSON string.</returns>
public string Serialize(object obj) public string Serialize(object obj)
{ {
if (obj != null && obj is Org.OpenAPITools.Model.AbstractOpenAPISchema) if (obj != null && obj is AbstractOpenAPISchema)
{ {
// the object to be serialized is an oneOf/anyOf schema // the object to be serialized is an oneOf/anyOf schema
return ((Org.OpenAPITools.Model.AbstractOpenAPISchema)obj).ToJson(); return ((AbstractOpenAPISchema)obj).ToJson();
} }
else else
{ {
@ -117,7 +118,7 @@ namespace Org.OpenAPITools.Client
if (match.Success) if (match.Success)
{ {
string fileName = filePath + ClientUtils.SanitizeFilename(match.Groups[1].Value.Replace("\"", "").Replace("'", "")); string fileName = filePath + ClientUtils.SanitizeFilename(match.Groups[1].Value.Replace("\"", "").Replace("'", ""));
File.WriteAllBytes(fileName, bytes); FileIO.WriteAllBytes(fileName, bytes);
return new FileStream(fileName, FileMode.Open); return new FileStream(fileName, FileMode.Open);
} }
} }
@ -128,7 +129,7 @@ namespace Org.OpenAPITools.Client
if (type.Name.StartsWith("System.Nullable`1[[System.DateTime")) // return a datetime object if (type.Name.StartsWith("System.Nullable`1[[System.DateTime")) // return a datetime object
{ {
return DateTime.Parse(response.Content, null, System.Globalization.DateTimeStyles.RoundtripKind); return DateTime.Parse(response.Content, null, DateTimeStyles.RoundtripKind);
} }
if (type == typeof(string) || type.Name.StartsWith("System.Nullable")) // return primitive type if (type == typeof(string) || type.Name.StartsWith("System.Nullable")) // return primitive type
@ -150,13 +151,13 @@ namespace Org.OpenAPITools.Client
public ISerializer Serializer => this; public ISerializer Serializer => this;
public IDeserializer Deserializer => this; public IDeserializer Deserializer => this;
public string[] AcceptedContentTypes => RestSharp.ContentType.JsonAccept; public string[] AcceptedContentTypes => ContentType.JsonAccept;
public SupportsContentType SupportsContentType => contentType => public SupportsContentType SupportsContentType => contentType =>
contentType.Value.EndsWith("json", StringComparison.InvariantCultureIgnoreCase) || contentType.Value.EndsWith("json", StringComparison.InvariantCultureIgnoreCase) ||
contentType.Value.EndsWith("javascript", StringComparison.InvariantCultureIgnoreCase); contentType.Value.EndsWith("javascript", StringComparison.InvariantCultureIgnoreCase);
public ContentType ContentType { get; set; } = RestSharp.ContentType.Json; public ContentType ContentType { get; set; } = ContentType.Json;
public DataFormat DataFormat => DataFormat.Json; public DataFormat DataFormat => DataFormat.Json;
} }
@ -203,7 +204,7 @@ namespace Org.OpenAPITools.Client
/// </summary> /// </summary>
public ApiClient() public ApiClient()
{ {
_baseUrl = Org.OpenAPITools.Client.GlobalConfiguration.Instance.BasePath; _baseUrl = GlobalConfiguration.Instance.BasePath;
} }
/// <summary> /// <summary>
@ -260,14 +261,14 @@ namespace Org.OpenAPITools.Client
/// <summary> /// <summary>
/// Provides all logic for constructing a new RestSharp <see cref="RestRequest"/>. /// Provides all logic for constructing a new RestSharp <see cref="RestRequest"/>.
/// At this point, all information for querying the service is known. Here, it is simply /// At this point, all information for querying the service is known.
/// mapped into the RestSharp request. /// Here, it is simply mapped into the RestSharp request.
/// </summary> /// </summary>
/// <param name="method">The http verb.</param> /// <param name="method">The http verb.</param>
/// <param name="path">The target path (or resource).</param> /// <param name="path">The target path (or resource).</param>
/// <param name="options">The additional request options.</param> /// <param name="options">The additional request options.</param>
/// <param name="configuration">A per-request configuration object. It is assumed that any merge with /// <param name="configuration">A per-request configuration object.
/// GlobalConfiguration has been done before calling this method.</param> /// It is assumed that any merge with GlobalConfiguration has been done before calling this method.</param>
/// <returns>[private] A new RestRequest instance.</returns> /// <returns>[private] A new RestRequest instance.</returns>
/// <exception cref="ArgumentNullException"></exception> /// <exception cref="ArgumentNullException"></exception>
private RestRequest NewRequest( private RestRequest NewRequest(
@ -375,7 +376,7 @@ namespace Org.OpenAPITools.Client
var bytes = ClientUtils.ReadAsBytes(file); var bytes = ClientUtils.ReadAsBytes(file);
var fileStream = file as FileStream; var fileStream = file as FileStream;
if (fileStream != null) if (fileStream != null)
request.AddFile(fileParam.Key, bytes, System.IO.Path.GetFileName(fileStream.Name)); request.AddFile(fileParam.Key, bytes, Path.GetFileName(fileStream.Name));
else else
request.AddFile(fileParam.Key, bytes, "no_file_name_provided"); request.AddFile(fileParam.Key, bytes, "no_file_name_provided");
} }
@ -385,6 +386,13 @@ namespace Org.OpenAPITools.Client
return request; return request;
} }
/// <summary>
/// Transforms a RestResponse instance into a new ApiResponse instance.
/// At this point, we have a concrete http response from the service.
/// Here, it is simply mapped into the [public] ApiResponse object.
/// </summary>
/// <param name="response">The RestSharp response object</param>
/// <returns>A new ApiResponse instance.</returns>
private ApiResponse<T> ToApiResponse<T>(RestResponse<T> response) private ApiResponse<T> ToApiResponse<T>(RestResponse<T> response)
{ {
T result = response.Data; T result = response.Data;
@ -429,57 +437,45 @@ namespace Org.OpenAPITools.Client
return transformed; return transformed;
} }
private ApiResponse<T> Exec<T>(RestRequest request, RequestOptions options, IReadableConfiguration configuration) /// <summary>
/// Executes the HTTP request for the current service.
/// Based on functions received it can be async or sync.
/// </summary>
/// <param name="getResponse">Local function that executes http request and returns http response.</param>
/// <param name="setOptions">Local function to specify options for the service.</param>
/// <param name="request">The RestSharp request object</param>
/// <param name="options">The RestSharp options object</param>
/// <param name="configuration">A per-request configuration object.
/// It is assumed that any merge with GlobalConfiguration has been done before calling this method.</param>
/// <returns>A new ApiResponse instance.</returns>
private ApiResponse<T> ExecClient<T>(Func<RestClient, RestResponse<T>> getResponse, Action<RestClientOptions> setOptions, RestRequest request, RequestOptions options, IReadableConfiguration configuration)
{ {
var baseUrl = configuration.GetOperationServerUrl(options.Operation, options.OperationIndex) ?? _baseUrl; var baseUrl = configuration.GetOperationServerUrl(options.Operation, options.OperationIndex) ?? _baseUrl;
var cookies = new CookieContainer();
if (options.Cookies != null && options.Cookies.Count > 0)
{
foreach (var cookie in options.Cookies)
{
cookies.Add(new Cookie(cookie.Name, cookie.Value));
}
}
var clientOptions = new RestClientOptions(baseUrl) var clientOptions = new RestClientOptions(baseUrl)
{ {
ClientCertificates = configuration.ClientCertificates, ClientCertificates = configuration.ClientCertificates,
CookieContainer = cookies,
MaxTimeout = configuration.Timeout, MaxTimeout = configuration.Timeout,
Proxy = configuration.Proxy, Proxy = configuration.Proxy,
UserAgent = configuration.UserAgent, UserAgent = configuration.UserAgent,
UseDefaultCredentials = configuration.UseDefaultCredentials, UseDefaultCredentials = configuration.UseDefaultCredentials,
RemoteCertificateValidationCallback = configuration.RemoteCertificateValidationCallback RemoteCertificateValidationCallback = configuration.RemoteCertificateValidationCallback
}; };
setOptions(clientOptions);
using (RestClient client = new RestClient(clientOptions, using (RestClient client = new RestClient(clientOptions,
configureSerialization: serializerConfig => serializerConfig.UseSerializer(() => new CustomJsonCodec(SerializerSettings, configuration)))) configureSerialization: serializerConfig => serializerConfig.UseSerializer(() => new CustomJsonCodec(SerializerSettings, configuration))))
{ {
InterceptRequest(request); InterceptRequest(request);
RestResponse<T> response; RestResponse<T> response = getResponse(client);
if (RetryConfiguration.RetryPolicy != null)
{
var policy = RetryConfiguration.RetryPolicy;
var policyResult = policy.ExecuteAndCapture(() => client.Execute(request));
response = (policyResult.Outcome == OutcomeType.Successful) ? client.Deserialize<T>(policyResult.Result) : new RestResponse<T>(request)
{
ErrorException = policyResult.FinalException
};
}
else
{
response = client.Execute<T>(request);
}
// if the response type is oneOf/anyOf, call FromJSON to deserialize the data // if the response type is oneOf/anyOf, call FromJSON to deserialize the data
if (typeof(Org.OpenAPITools.Model.AbstractOpenAPISchema).IsAssignableFrom(typeof(T))) if (typeof(AbstractOpenAPISchema).IsAssignableFrom(typeof(T)))
{ {
try try
{ {
response.Data = (T) typeof(T).GetMethod("FromJson").Invoke(null, new object[] { response.Content }); response.Data = (T)typeof(T).GetMethod("FromJson").Invoke(null, new object[] { response.Content });
} }
catch (Exception ex) catch (Exception ex)
{ {
@ -537,90 +533,80 @@ namespace Org.OpenAPITools.Client
} }
} }
private async Task<ApiResponse<T>> ExecAsync<T>(RestRequest request, RequestOptions options, IReadableConfiguration configuration, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) private RestResponse<T> DeserializeRestResponseFromPolicy<T>(RestClient client, RestRequest request, PolicyResult<RestResponse> policyResult)
{ {
var baseUrl = configuration.GetOperationServerUrl(options.Operation, options.OperationIndex) ?? _baseUrl; if (policyResult.Outcome == OutcomeType.Successful)
var clientOptions = new RestClientOptions(baseUrl)
{ {
ClientCertificates = configuration.ClientCertificates, return client.Deserialize<T>(policyResult.Result);
MaxTimeout = configuration.Timeout, }
Proxy = configuration.Proxy, else
UserAgent = configuration.UserAgent, {
UseDefaultCredentials = configuration.UseDefaultCredentials, return new RestResponse<T>(request)
RemoteCertificateValidationCallback = configuration.RemoteCertificateValidationCallback {
ErrorException = policyResult.FinalException
};
}
}
private ApiResponse<T> Exec<T>(RestRequest request, RequestOptions options, IReadableConfiguration configuration)
{
Action<RestClientOptions> setOptions = (clientOptions) =>
{
var cookies = new CookieContainer();
if (options.Cookies != null && options.Cookies.Count > 0)
{
foreach (var cookie in options.Cookies)
{
cookies.Add(new Cookie(cookie.Name, cookie.Value));
}
}
clientOptions.CookieContainer = cookies;
}; };
using (RestClient client = new RestClient(clientOptions, Func<RestClient, RestResponse<T>> getResponse = (client) =>
configureSerialization: serializerConfig => serializerConfig.UseSerializer(() => new CustomJsonCodec(SerializerSettings, configuration))))
{ {
InterceptRequest(request); if (RetryConfiguration.RetryPolicy != null)
RestResponse<T> response;
if (RetryConfiguration.AsyncRetryPolicy != null)
{ {
var policy = RetryConfiguration.AsyncRetryPolicy; var policy = RetryConfiguration.RetryPolicy;
var policyResult = await policy.ExecuteAndCaptureAsync((ct) => client.ExecuteAsync(request, ct), cancellationToken).ConfigureAwait(false); var policyResult = policy.ExecuteAndCapture(() => client.Execute(request));
response = (policyResult.Outcome == OutcomeType.Successful) ? client.Deserialize<T>(policyResult.Result) : new RestResponse<T>(request) return DeserializeRestResponseFromPolicy<T>(client, request, policyResult);
{
ErrorException = policyResult.FinalException
};
} }
else else
{ {
response = await client.ExecuteAsync<T>(request, cancellationToken).ConfigureAwait(false); return client.Execute<T>(request);
} }
};
// if the response type is oneOf/anyOf, call FromJSON to deserialize the data return ExecClient(getResponse, setOptions, request, options, configuration);
if (typeof(Org.OpenAPITools.Model.AbstractOpenAPISchema).IsAssignableFrom(typeof(T))) }
{
response.Data = (T) typeof(T).GetMethod("FromJson").Invoke(null, new object[] { response.Content });
}
else if (typeof(T).Name == "Stream") // for binary response
{
response.Data = (T)(object)new MemoryStream(response.RawBytes);
}
else if (typeof(T).Name == "Byte[]") // for byte response
{
response.Data = (T)(object)response.RawBytes;
}
InterceptResponse(request, response); private Task<ApiResponse<T>> ExecAsync<T>(RestRequest request, RequestOptions options, IReadableConfiguration configuration, CancellationToken cancellationToken = default(CancellationToken))
{
Action<RestClientOptions> setOptions = (clientOptions) =>
{
//no extra options
};
var result = ToApiResponse(response); Func<RestClient, RestResponse<T>> getResponse = (client) =>
if (response.ErrorMessage != null) {
Func<Task<RestResponse<T>>> action = async () =>
{ {
result.ErrorText = response.ErrorMessage; if (RetryConfiguration.AsyncRetryPolicy != null)
}
if (response.Cookies != null && response.Cookies.Count > 0)
{
if (result.Cookies == null) result.Cookies = new List<Cookie>();
foreach (var restResponseCookie in response.Cookies.Cast<Cookie>())
{ {
var cookie = new Cookie( var policy = RetryConfiguration.AsyncRetryPolicy;
restResponseCookie.Name, var policyResult = await policy.ExecuteAndCaptureAsync((ct) => client.ExecuteAsync(request, ct), cancellationToken).ConfigureAwait(false);
restResponseCookie.Value, return DeserializeRestResponseFromPolicy<T>(client, request, policyResult);
restResponseCookie.Path,
restResponseCookie.Domain
)
{
Comment = restResponseCookie.Comment,
CommentUri = restResponseCookie.CommentUri,
Discard = restResponseCookie.Discard,
Expired = restResponseCookie.Expired,
Expires = restResponseCookie.Expires,
HttpOnly = restResponseCookie.HttpOnly,
Port = restResponseCookie.Port,
Secure = restResponseCookie.Secure,
Version = restResponseCookie.Version
};
result.Cookies.Add(cookie);
} }
} else
return result; {
} return await client.ExecuteAsync<T>(request, cancellationToken).ConfigureAwait(false);
}
};
return action().Result;
};
return Task.FromResult<ApiResponse<T>>(ExecClient(getResponse, setOptions, request, options, configuration));
} }
#region IAsynchronousClient #region IAsynchronousClient
@ -633,7 +619,7 @@ namespace Org.OpenAPITools.Client
/// GlobalConfiguration has been done before calling this method.</param> /// GlobalConfiguration has been done before calling this method.</param>
/// <param name="cancellationToken">Token that enables callers to cancel the request.</param> /// <param name="cancellationToken">Token that enables callers to cancel the request.</param>
/// <returns>A Task containing ApiResponse</returns> /// <returns>A Task containing ApiResponse</returns>
public Task<ApiResponse<T>> GetAsync<T>(string path, RequestOptions options, IReadableConfiguration configuration = null, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) public Task<ApiResponse<T>> GetAsync<T>(string path, RequestOptions options, IReadableConfiguration configuration = null, CancellationToken cancellationToken = default)
{ {
var config = configuration ?? GlobalConfiguration.Instance; var config = configuration ?? GlobalConfiguration.Instance;
return ExecAsync<T>(NewRequest(HttpMethod.Get, path, options, config), options, config, cancellationToken); return ExecAsync<T>(NewRequest(HttpMethod.Get, path, options, config), options, config, cancellationToken);
@ -648,7 +634,7 @@ namespace Org.OpenAPITools.Client
/// GlobalConfiguration has been done before calling this method.</param> /// GlobalConfiguration has been done before calling this method.</param>
/// <param name="cancellationToken">Token that enables callers to cancel the request.</param> /// <param name="cancellationToken">Token that enables callers to cancel the request.</param>
/// <returns>A Task containing ApiResponse</returns> /// <returns>A Task containing ApiResponse</returns>
public Task<ApiResponse<T>> PostAsync<T>(string path, RequestOptions options, IReadableConfiguration configuration = null, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) public Task<ApiResponse<T>> PostAsync<T>(string path, RequestOptions options, IReadableConfiguration configuration = null, CancellationToken cancellationToken = default)
{ {
var config = configuration ?? GlobalConfiguration.Instance; var config = configuration ?? GlobalConfiguration.Instance;
return ExecAsync<T>(NewRequest(HttpMethod.Post, path, options, config), options, config, cancellationToken); return ExecAsync<T>(NewRequest(HttpMethod.Post, path, options, config), options, config, cancellationToken);
@ -663,7 +649,7 @@ namespace Org.OpenAPITools.Client
/// GlobalConfiguration has been done before calling this method.</param> /// GlobalConfiguration has been done before calling this method.</param>
/// <param name="cancellationToken">Token that enables callers to cancel the request.</param> /// <param name="cancellationToken">Token that enables callers to cancel the request.</param>
/// <returns>A Task containing ApiResponse</returns> /// <returns>A Task containing ApiResponse</returns>
public Task<ApiResponse<T>> PutAsync<T>(string path, RequestOptions options, IReadableConfiguration configuration = null, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) public Task<ApiResponse<T>> PutAsync<T>(string path, RequestOptions options, IReadableConfiguration configuration = null, CancellationToken cancellationToken = default)
{ {
var config = configuration ?? GlobalConfiguration.Instance; var config = configuration ?? GlobalConfiguration.Instance;
return ExecAsync<T>(NewRequest(HttpMethod.Put, path, options, config), options, config, cancellationToken); return ExecAsync<T>(NewRequest(HttpMethod.Put, path, options, config), options, config, cancellationToken);
@ -678,7 +664,7 @@ namespace Org.OpenAPITools.Client
/// GlobalConfiguration has been done before calling this method.</param> /// GlobalConfiguration has been done before calling this method.</param>
/// <param name="cancellationToken">Token that enables callers to cancel the request.</param> /// <param name="cancellationToken">Token that enables callers to cancel the request.</param>
/// <returns>A Task containing ApiResponse</returns> /// <returns>A Task containing ApiResponse</returns>
public Task<ApiResponse<T>> DeleteAsync<T>(string path, RequestOptions options, IReadableConfiguration configuration = null, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) public Task<ApiResponse<T>> DeleteAsync<T>(string path, RequestOptions options, IReadableConfiguration configuration = null, CancellationToken cancellationToken = default)
{ {
var config = configuration ?? GlobalConfiguration.Instance; var config = configuration ?? GlobalConfiguration.Instance;
return ExecAsync<T>(NewRequest(HttpMethod.Delete, path, options, config), options, config, cancellationToken); return ExecAsync<T>(NewRequest(HttpMethod.Delete, path, options, config), options, config, cancellationToken);
@ -693,7 +679,7 @@ namespace Org.OpenAPITools.Client
/// GlobalConfiguration has been done before calling this method.</param> /// GlobalConfiguration has been done before calling this method.</param>
/// <param name="cancellationToken">Token that enables callers to cancel the request.</param> /// <param name="cancellationToken">Token that enables callers to cancel the request.</param>
/// <returns>A Task containing ApiResponse</returns> /// <returns>A Task containing ApiResponse</returns>
public Task<ApiResponse<T>> HeadAsync<T>(string path, RequestOptions options, IReadableConfiguration configuration = null, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) public Task<ApiResponse<T>> HeadAsync<T>(string path, RequestOptions options, IReadableConfiguration configuration = null, CancellationToken cancellationToken = default)
{ {
var config = configuration ?? GlobalConfiguration.Instance; var config = configuration ?? GlobalConfiguration.Instance;
return ExecAsync<T>(NewRequest(HttpMethod.Head, path, options, config), options, config, cancellationToken); return ExecAsync<T>(NewRequest(HttpMethod.Head, path, options, config), options, config, cancellationToken);
@ -708,7 +694,7 @@ namespace Org.OpenAPITools.Client
/// GlobalConfiguration has been done before calling this method.</param> /// GlobalConfiguration has been done before calling this method.</param>
/// <param name="cancellationToken">Token that enables callers to cancel the request.</param> /// <param name="cancellationToken">Token that enables callers to cancel the request.</param>
/// <returns>A Task containing ApiResponse</returns> /// <returns>A Task containing ApiResponse</returns>
public Task<ApiResponse<T>> OptionsAsync<T>(string path, RequestOptions options, IReadableConfiguration configuration = null, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) public Task<ApiResponse<T>> OptionsAsync<T>(string path, RequestOptions options, IReadableConfiguration configuration = null, CancellationToken cancellationToken = default)
{ {
var config = configuration ?? GlobalConfiguration.Instance; var config = configuration ?? GlobalConfiguration.Instance;
return ExecAsync<T>(NewRequest(HttpMethod.Options, path, options, config), options, config, cancellationToken); return ExecAsync<T>(NewRequest(HttpMethod.Options, path, options, config), options, config, cancellationToken);
@ -723,7 +709,7 @@ namespace Org.OpenAPITools.Client
/// GlobalConfiguration has been done before calling this method.</param> /// GlobalConfiguration has been done before calling this method.</param>
/// <param name="cancellationToken">Token that enables callers to cancel the request.</param> /// <param name="cancellationToken">Token that enables callers to cancel the request.</param>
/// <returns>A Task containing ApiResponse</returns> /// <returns>A Task containing ApiResponse</returns>
public Task<ApiResponse<T>> PatchAsync<T>(string path, RequestOptions options, IReadableConfiguration configuration = null, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) public Task<ApiResponse<T>> PatchAsync<T>(string path, RequestOptions options, IReadableConfiguration configuration = null, CancellationToken cancellationToken = default)
{ {
var config = configuration ?? GlobalConfiguration.Instance; var config = configuration ?? GlobalConfiguration.Instance;
return ExecAsync<T>(NewRequest(HttpMethod.Patch, path, options, config), options, config, cancellationToken); return ExecAsync<T>(NewRequest(HttpMethod.Patch, path, options, config), options, config, cancellationToken);

View File

@ -22,14 +22,15 @@ using System.Text;
using System.Threading; using System.Threading;
using System.Text.RegularExpressions; using System.Text.RegularExpressions;
using System.Threading.Tasks; using System.Threading.Tasks;
using System.Web;
using Newtonsoft.Json; using Newtonsoft.Json;
using Newtonsoft.Json.Serialization; using Newtonsoft.Json.Serialization;
using RestSharp; using RestSharp;
using RestSharp.Serializers; using RestSharp.Serializers;
using RestSharpMethod = RestSharp.Method; using RestSharpMethod = RestSharp.Method;
using FileIO = System.IO.File;
using Polly; using Polly;
using Org.OpenAPITools.Client.Auth; using Org.OpenAPITools.Client.Auth;
using Org.OpenAPITools.Model;
namespace Org.OpenAPITools.Client namespace Org.OpenAPITools.Client
{ {
@ -70,10 +71,10 @@ namespace Org.OpenAPITools.Client
/// <returns>A JSON string.</returns> /// <returns>A JSON string.</returns>
public string Serialize(object obj) public string Serialize(object obj)
{ {
if (obj != null && obj is Org.OpenAPITools.Model.AbstractOpenAPISchema) if (obj != null && obj is AbstractOpenAPISchema)
{ {
// the object to be serialized is an oneOf/anyOf schema // the object to be serialized is an oneOf/anyOf schema
return ((Org.OpenAPITools.Model.AbstractOpenAPISchema)obj).ToJson(); return ((AbstractOpenAPISchema)obj).ToJson();
} }
else else
{ {
@ -118,7 +119,7 @@ namespace Org.OpenAPITools.Client
if (match.Success) if (match.Success)
{ {
string fileName = filePath + ClientUtils.SanitizeFilename(match.Groups[1].Value.Replace("\"", "").Replace("'", "")); string fileName = filePath + ClientUtils.SanitizeFilename(match.Groups[1].Value.Replace("\"", "").Replace("'", ""));
File.WriteAllBytes(fileName, bytes); FileIO.WriteAllBytes(fileName, bytes);
return new FileStream(fileName, FileMode.Open); return new FileStream(fileName, FileMode.Open);
} }
} }
@ -129,7 +130,7 @@ namespace Org.OpenAPITools.Client
if (type.Name.StartsWith("System.Nullable`1[[System.DateTime")) // return a datetime object if (type.Name.StartsWith("System.Nullable`1[[System.DateTime")) // return a datetime object
{ {
return DateTime.Parse(response.Content, null, System.Globalization.DateTimeStyles.RoundtripKind); return DateTime.Parse(response.Content, null, DateTimeStyles.RoundtripKind);
} }
if (type == typeof(string) || type.Name.StartsWith("System.Nullable")) // return primitive type if (type == typeof(string) || type.Name.StartsWith("System.Nullable")) // return primitive type
@ -151,13 +152,13 @@ namespace Org.OpenAPITools.Client
public ISerializer Serializer => this; public ISerializer Serializer => this;
public IDeserializer Deserializer => this; public IDeserializer Deserializer => this;
public string[] AcceptedContentTypes => RestSharp.ContentType.JsonAccept; public string[] AcceptedContentTypes => ContentType.JsonAccept;
public SupportsContentType SupportsContentType => contentType => public SupportsContentType SupportsContentType => contentType =>
contentType.Value.EndsWith("json", StringComparison.InvariantCultureIgnoreCase) || contentType.Value.EndsWith("json", StringComparison.InvariantCultureIgnoreCase) ||
contentType.Value.EndsWith("javascript", StringComparison.InvariantCultureIgnoreCase); contentType.Value.EndsWith("javascript", StringComparison.InvariantCultureIgnoreCase);
public ContentType ContentType { get; set; } = RestSharp.ContentType.Json; public ContentType ContentType { get; set; } = ContentType.Json;
public DataFormat DataFormat => DataFormat.Json; public DataFormat DataFormat => DataFormat.Json;
} }
@ -204,7 +205,7 @@ namespace Org.OpenAPITools.Client
/// </summary> /// </summary>
public ApiClient() public ApiClient()
{ {
_baseUrl = Org.OpenAPITools.Client.GlobalConfiguration.Instance.BasePath; _baseUrl = GlobalConfiguration.Instance.BasePath;
} }
/// <summary> /// <summary>
@ -261,14 +262,14 @@ namespace Org.OpenAPITools.Client
/// <summary> /// <summary>
/// Provides all logic for constructing a new RestSharp <see cref="RestRequest"/>. /// Provides all logic for constructing a new RestSharp <see cref="RestRequest"/>.
/// At this point, all information for querying the service is known. Here, it is simply /// At this point, all information for querying the service is known.
/// mapped into the RestSharp request. /// Here, it is simply mapped into the RestSharp request.
/// </summary> /// </summary>
/// <param name="method">The http verb.</param> /// <param name="method">The http verb.</param>
/// <param name="path">The target path (or resource).</param> /// <param name="path">The target path (or resource).</param>
/// <param name="options">The additional request options.</param> /// <param name="options">The additional request options.</param>
/// <param name="configuration">A per-request configuration object. It is assumed that any merge with /// <param name="configuration">A per-request configuration object.
/// GlobalConfiguration has been done before calling this method.</param> /// It is assumed that any merge with GlobalConfiguration has been done before calling this method.</param>
/// <returns>[private] A new RestRequest instance.</returns> /// <returns>[private] A new RestRequest instance.</returns>
/// <exception cref="ArgumentNullException"></exception> /// <exception cref="ArgumentNullException"></exception>
private RestRequest NewRequest( private RestRequest NewRequest(
@ -376,7 +377,7 @@ namespace Org.OpenAPITools.Client
var bytes = ClientUtils.ReadAsBytes(file); var bytes = ClientUtils.ReadAsBytes(file);
var fileStream = file as FileStream; var fileStream = file as FileStream;
if (fileStream != null) if (fileStream != null)
request.AddFile(fileParam.Key, bytes, System.IO.Path.GetFileName(fileStream.Name)); request.AddFile(fileParam.Key, bytes, Path.GetFileName(fileStream.Name));
else else
request.AddFile(fileParam.Key, bytes, "no_file_name_provided"); request.AddFile(fileParam.Key, bytes, "no_file_name_provided");
} }
@ -386,6 +387,13 @@ namespace Org.OpenAPITools.Client
return request; return request;
} }
/// <summary>
/// Transforms a RestResponse instance into a new ApiResponse instance.
/// At this point, we have a concrete http response from the service.
/// Here, it is simply mapped into the [public] ApiResponse object.
/// </summary>
/// <param name="response">The RestSharp response object</param>
/// <returns>A new ApiResponse instance.</returns>
private ApiResponse<T> ToApiResponse<T>(RestResponse<T> response) private ApiResponse<T> ToApiResponse<T>(RestResponse<T> response)
{ {
T result = response.Data; T result = response.Data;
@ -430,31 +438,32 @@ namespace Org.OpenAPITools.Client
return transformed; return transformed;
} }
private ApiResponse<T> Exec<T>(RestRequest request, RequestOptions options, IReadableConfiguration configuration) /// <summary>
/// Executes the HTTP request for the current service.
/// Based on functions received it can be async or sync.
/// </summary>
/// <param name="getResponse">Local function that executes http request and returns http response.</param>
/// <param name="setOptions">Local function to specify options for the service.</param>
/// <param name="request">The RestSharp request object</param>
/// <param name="options">The RestSharp options object</param>
/// <param name="configuration">A per-request configuration object.
/// It is assumed that any merge with GlobalConfiguration has been done before calling this method.</param>
/// <returns>A new ApiResponse instance.</returns>
private ApiResponse<T> ExecClient<T>(Func<RestClient, RestResponse<T>> getResponse, Action<RestClientOptions> setOptions, RestRequest request, RequestOptions options, IReadableConfiguration configuration)
{ {
var baseUrl = configuration.GetOperationServerUrl(options.Operation, options.OperationIndex) ?? _baseUrl; var baseUrl = configuration.GetOperationServerUrl(options.Operation, options.OperationIndex) ?? _baseUrl;
var cookies = new CookieContainer();
if (options.Cookies != null && options.Cookies.Count > 0)
{
foreach (var cookie in options.Cookies)
{
cookies.Add(new Cookie(cookie.Name, cookie.Value));
}
}
var clientOptions = new RestClientOptions(baseUrl) var clientOptions = new RestClientOptions(baseUrl)
{ {
ClientCertificates = configuration.ClientCertificates, ClientCertificates = configuration.ClientCertificates,
CookieContainer = cookies,
MaxTimeout = configuration.Timeout, MaxTimeout = configuration.Timeout,
Proxy = configuration.Proxy, Proxy = configuration.Proxy,
UserAgent = configuration.UserAgent, UserAgent = configuration.UserAgent,
UseDefaultCredentials = configuration.UseDefaultCredentials, UseDefaultCredentials = configuration.UseDefaultCredentials,
RemoteCertificateValidationCallback = configuration.RemoteCertificateValidationCallback RemoteCertificateValidationCallback = configuration.RemoteCertificateValidationCallback
}; };
setOptions(clientOptions);
if (!string.IsNullOrEmpty(configuration.OAuthTokenUrl) && if (!string.IsNullOrEmpty(configuration.OAuthTokenUrl) &&
!string.IsNullOrEmpty(configuration.OAuthClientId) && !string.IsNullOrEmpty(configuration.OAuthClientId) &&
!string.IsNullOrEmpty(configuration.OAuthClientSecret) && !string.IsNullOrEmpty(configuration.OAuthClientSecret) &&
@ -474,27 +483,14 @@ namespace Org.OpenAPITools.Client
{ {
InterceptRequest(request); InterceptRequest(request);
RestResponse<T> response; RestResponse<T> response = getResponse(client);
if (RetryConfiguration.RetryPolicy != null)
{
var policy = RetryConfiguration.RetryPolicy;
var policyResult = policy.ExecuteAndCapture(() => client.Execute(request));
response = (policyResult.Outcome == OutcomeType.Successful) ? client.Deserialize<T>(policyResult.Result) : new RestResponse<T>(request)
{
ErrorException = policyResult.FinalException
};
}
else
{
response = client.Execute<T>(request);
}
// if the response type is oneOf/anyOf, call FromJSON to deserialize the data // if the response type is oneOf/anyOf, call FromJSON to deserialize the data
if (typeof(Org.OpenAPITools.Model.AbstractOpenAPISchema).IsAssignableFrom(typeof(T))) if (typeof(AbstractOpenAPISchema).IsAssignableFrom(typeof(T)))
{ {
try try
{ {
response.Data = (T) typeof(T).GetMethod("FromJson").Invoke(null, new object[] { response.Content }); response.Data = (T)typeof(T).GetMethod("FromJson").Invoke(null, new object[] { response.Content });
} }
catch (Exception ex) catch (Exception ex)
{ {
@ -552,104 +548,80 @@ namespace Org.OpenAPITools.Client
} }
} }
private async Task<ApiResponse<T>> ExecAsync<T>(RestRequest request, RequestOptions options, IReadableConfiguration configuration, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) private RestResponse<T> DeserializeRestResponseFromPolicy<T>(RestClient client, RestRequest request, PolicyResult<RestResponse> policyResult)
{ {
var baseUrl = configuration.GetOperationServerUrl(options.Operation, options.OperationIndex) ?? _baseUrl; if (policyResult.Outcome == OutcomeType.Successful)
var clientOptions = new RestClientOptions(baseUrl)
{ {
ClientCertificates = configuration.ClientCertificates, return client.Deserialize<T>(policyResult.Result);
MaxTimeout = configuration.Timeout, }
Proxy = configuration.Proxy, else
UserAgent = configuration.UserAgent, {
UseDefaultCredentials = configuration.UseDefaultCredentials, return new RestResponse<T>(request)
RemoteCertificateValidationCallback = configuration.RemoteCertificateValidationCallback {
ErrorException = policyResult.FinalException
};
}
}
private ApiResponse<T> Exec<T>(RestRequest request, RequestOptions options, IReadableConfiguration configuration)
{
Action<RestClientOptions> setOptions = (clientOptions) =>
{
var cookies = new CookieContainer();
if (options.Cookies != null && options.Cookies.Count > 0)
{
foreach (var cookie in options.Cookies)
{
cookies.Add(new Cookie(cookie.Name, cookie.Value));
}
}
clientOptions.CookieContainer = cookies;
}; };
if (!string.IsNullOrEmpty(configuration.OAuthTokenUrl) && Func<RestClient, RestResponse<T>> getResponse = (client) =>
!string.IsNullOrEmpty(configuration.OAuthClientId) &&
!string.IsNullOrEmpty(configuration.OAuthClientSecret) &&
configuration.OAuthFlow != null)
{ {
clientOptions.Authenticator = new OAuthAuthenticator( if (RetryConfiguration.RetryPolicy != null)
configuration.OAuthTokenUrl,
configuration.OAuthClientId,
configuration.OAuthClientSecret,
configuration.OAuthFlow,
SerializerSettings,
configuration);
}
using (RestClient client = new RestClient(clientOptions,
configureSerialization: serializerConfig => serializerConfig.UseSerializer(() => new CustomJsonCodec(SerializerSettings, configuration))))
{
InterceptRequest(request);
RestResponse<T> response;
if (RetryConfiguration.AsyncRetryPolicy != null)
{ {
var policy = RetryConfiguration.AsyncRetryPolicy; var policy = RetryConfiguration.RetryPolicy;
var policyResult = await policy.ExecuteAndCaptureAsync((ct) => client.ExecuteAsync(request, ct), cancellationToken).ConfigureAwait(false); var policyResult = policy.ExecuteAndCapture(() => client.Execute(request));
response = (policyResult.Outcome == OutcomeType.Successful) ? client.Deserialize<T>(policyResult.Result) : new RestResponse<T>(request) return DeserializeRestResponseFromPolicy<T>(client, request, policyResult);
{
ErrorException = policyResult.FinalException
};
} }
else else
{ {
response = await client.ExecuteAsync<T>(request, cancellationToken).ConfigureAwait(false); return client.Execute<T>(request);
} }
};
// if the response type is oneOf/anyOf, call FromJSON to deserialize the data return ExecClient(getResponse, setOptions, request, options, configuration);
if (typeof(Org.OpenAPITools.Model.AbstractOpenAPISchema).IsAssignableFrom(typeof(T))) }
{
response.Data = (T) typeof(T).GetMethod("FromJson").Invoke(null, new object[] { response.Content });
}
else if (typeof(T).Name == "Stream") // for binary response
{
response.Data = (T)(object)new MemoryStream(response.RawBytes);
}
else if (typeof(T).Name == "Byte[]") // for byte response
{
response.Data = (T)(object)response.RawBytes;
}
InterceptResponse(request, response); private Task<ApiResponse<T>> ExecAsync<T>(RestRequest request, RequestOptions options, IReadableConfiguration configuration, CancellationToken cancellationToken = default(CancellationToken))
{
Action<RestClientOptions> setOptions = (clientOptions) =>
{
//no extra options
};
var result = ToApiResponse(response); Func<RestClient, RestResponse<T>> getResponse = (client) =>
if (response.ErrorMessage != null) {
Func<Task<RestResponse<T>>> action = async () =>
{ {
result.ErrorText = response.ErrorMessage; if (RetryConfiguration.AsyncRetryPolicy != null)
}
if (response.Cookies != null && response.Cookies.Count > 0)
{
if (result.Cookies == null) result.Cookies = new List<Cookie>();
foreach (var restResponseCookie in response.Cookies.Cast<Cookie>())
{ {
var cookie = new Cookie( var policy = RetryConfiguration.AsyncRetryPolicy;
restResponseCookie.Name, var policyResult = await policy.ExecuteAndCaptureAsync((ct) => client.ExecuteAsync(request, ct), cancellationToken).ConfigureAwait(false);
restResponseCookie.Value, return DeserializeRestResponseFromPolicy<T>(client, request, policyResult);
restResponseCookie.Path,
restResponseCookie.Domain
)
{
Comment = restResponseCookie.Comment,
CommentUri = restResponseCookie.CommentUri,
Discard = restResponseCookie.Discard,
Expired = restResponseCookie.Expired,
Expires = restResponseCookie.Expires,
HttpOnly = restResponseCookie.HttpOnly,
Port = restResponseCookie.Port,
Secure = restResponseCookie.Secure,
Version = restResponseCookie.Version
};
result.Cookies.Add(cookie);
} }
} else
return result; {
} return await client.ExecuteAsync<T>(request, cancellationToken).ConfigureAwait(false);
}
};
return action().Result;
};
return Task.FromResult<ApiResponse<T>>(ExecClient(getResponse, setOptions, request, options, configuration));
} }
#region IAsynchronousClient #region IAsynchronousClient
@ -662,7 +634,7 @@ namespace Org.OpenAPITools.Client
/// GlobalConfiguration has been done before calling this method.</param> /// GlobalConfiguration has been done before calling this method.</param>
/// <param name="cancellationToken">Token that enables callers to cancel the request.</param> /// <param name="cancellationToken">Token that enables callers to cancel the request.</param>
/// <returns>A Task containing ApiResponse</returns> /// <returns>A Task containing ApiResponse</returns>
public Task<ApiResponse<T>> GetAsync<T>(string path, RequestOptions options, IReadableConfiguration configuration = null, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) public Task<ApiResponse<T>> GetAsync<T>(string path, RequestOptions options, IReadableConfiguration configuration = null, CancellationToken cancellationToken = default)
{ {
var config = configuration ?? GlobalConfiguration.Instance; var config = configuration ?? GlobalConfiguration.Instance;
return ExecAsync<T>(NewRequest(HttpMethod.Get, path, options, config), options, config, cancellationToken); return ExecAsync<T>(NewRequest(HttpMethod.Get, path, options, config), options, config, cancellationToken);
@ -677,7 +649,7 @@ namespace Org.OpenAPITools.Client
/// GlobalConfiguration has been done before calling this method.</param> /// GlobalConfiguration has been done before calling this method.</param>
/// <param name="cancellationToken">Token that enables callers to cancel the request.</param> /// <param name="cancellationToken">Token that enables callers to cancel the request.</param>
/// <returns>A Task containing ApiResponse</returns> /// <returns>A Task containing ApiResponse</returns>
public Task<ApiResponse<T>> PostAsync<T>(string path, RequestOptions options, IReadableConfiguration configuration = null, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) public Task<ApiResponse<T>> PostAsync<T>(string path, RequestOptions options, IReadableConfiguration configuration = null, CancellationToken cancellationToken = default)
{ {
var config = configuration ?? GlobalConfiguration.Instance; var config = configuration ?? GlobalConfiguration.Instance;
return ExecAsync<T>(NewRequest(HttpMethod.Post, path, options, config), options, config, cancellationToken); return ExecAsync<T>(NewRequest(HttpMethod.Post, path, options, config), options, config, cancellationToken);
@ -692,7 +664,7 @@ namespace Org.OpenAPITools.Client
/// GlobalConfiguration has been done before calling this method.</param> /// GlobalConfiguration has been done before calling this method.</param>
/// <param name="cancellationToken">Token that enables callers to cancel the request.</param> /// <param name="cancellationToken">Token that enables callers to cancel the request.</param>
/// <returns>A Task containing ApiResponse</returns> /// <returns>A Task containing ApiResponse</returns>
public Task<ApiResponse<T>> PutAsync<T>(string path, RequestOptions options, IReadableConfiguration configuration = null, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) public Task<ApiResponse<T>> PutAsync<T>(string path, RequestOptions options, IReadableConfiguration configuration = null, CancellationToken cancellationToken = default)
{ {
var config = configuration ?? GlobalConfiguration.Instance; var config = configuration ?? GlobalConfiguration.Instance;
return ExecAsync<T>(NewRequest(HttpMethod.Put, path, options, config), options, config, cancellationToken); return ExecAsync<T>(NewRequest(HttpMethod.Put, path, options, config), options, config, cancellationToken);
@ -707,7 +679,7 @@ namespace Org.OpenAPITools.Client
/// GlobalConfiguration has been done before calling this method.</param> /// GlobalConfiguration has been done before calling this method.</param>
/// <param name="cancellationToken">Token that enables callers to cancel the request.</param> /// <param name="cancellationToken">Token that enables callers to cancel the request.</param>
/// <returns>A Task containing ApiResponse</returns> /// <returns>A Task containing ApiResponse</returns>
public Task<ApiResponse<T>> DeleteAsync<T>(string path, RequestOptions options, IReadableConfiguration configuration = null, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) public Task<ApiResponse<T>> DeleteAsync<T>(string path, RequestOptions options, IReadableConfiguration configuration = null, CancellationToken cancellationToken = default)
{ {
var config = configuration ?? GlobalConfiguration.Instance; var config = configuration ?? GlobalConfiguration.Instance;
return ExecAsync<T>(NewRequest(HttpMethod.Delete, path, options, config), options, config, cancellationToken); return ExecAsync<T>(NewRequest(HttpMethod.Delete, path, options, config), options, config, cancellationToken);
@ -722,7 +694,7 @@ namespace Org.OpenAPITools.Client
/// GlobalConfiguration has been done before calling this method.</param> /// GlobalConfiguration has been done before calling this method.</param>
/// <param name="cancellationToken">Token that enables callers to cancel the request.</param> /// <param name="cancellationToken">Token that enables callers to cancel the request.</param>
/// <returns>A Task containing ApiResponse</returns> /// <returns>A Task containing ApiResponse</returns>
public Task<ApiResponse<T>> HeadAsync<T>(string path, RequestOptions options, IReadableConfiguration configuration = null, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) public Task<ApiResponse<T>> HeadAsync<T>(string path, RequestOptions options, IReadableConfiguration configuration = null, CancellationToken cancellationToken = default)
{ {
var config = configuration ?? GlobalConfiguration.Instance; var config = configuration ?? GlobalConfiguration.Instance;
return ExecAsync<T>(NewRequest(HttpMethod.Head, path, options, config), options, config, cancellationToken); return ExecAsync<T>(NewRequest(HttpMethod.Head, path, options, config), options, config, cancellationToken);
@ -737,7 +709,7 @@ namespace Org.OpenAPITools.Client
/// GlobalConfiguration has been done before calling this method.</param> /// GlobalConfiguration has been done before calling this method.</param>
/// <param name="cancellationToken">Token that enables callers to cancel the request.</param> /// <param name="cancellationToken">Token that enables callers to cancel the request.</param>
/// <returns>A Task containing ApiResponse</returns> /// <returns>A Task containing ApiResponse</returns>
public Task<ApiResponse<T>> OptionsAsync<T>(string path, RequestOptions options, IReadableConfiguration configuration = null, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) public Task<ApiResponse<T>> OptionsAsync<T>(string path, RequestOptions options, IReadableConfiguration configuration = null, CancellationToken cancellationToken = default)
{ {
var config = configuration ?? GlobalConfiguration.Instance; var config = configuration ?? GlobalConfiguration.Instance;
return ExecAsync<T>(NewRequest(HttpMethod.Options, path, options, config), options, config, cancellationToken); return ExecAsync<T>(NewRequest(HttpMethod.Options, path, options, config), options, config, cancellationToken);
@ -752,7 +724,7 @@ namespace Org.OpenAPITools.Client
/// GlobalConfiguration has been done before calling this method.</param> /// GlobalConfiguration has been done before calling this method.</param>
/// <param name="cancellationToken">Token that enables callers to cancel the request.</param> /// <param name="cancellationToken">Token that enables callers to cancel the request.</param>
/// <returns>A Task containing ApiResponse</returns> /// <returns>A Task containing ApiResponse</returns>
public Task<ApiResponse<T>> PatchAsync<T>(string path, RequestOptions options, IReadableConfiguration configuration = null, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) public Task<ApiResponse<T>> PatchAsync<T>(string path, RequestOptions options, IReadableConfiguration configuration = null, CancellationToken cancellationToken = default)
{ {
var config = configuration ?? GlobalConfiguration.Instance; var config = configuration ?? GlobalConfiguration.Instance;
return ExecAsync<T>(NewRequest(HttpMethod.Patch, path, options, config), options, config, cancellationToken); return ExecAsync<T>(NewRequest(HttpMethod.Patch, path, options, config), options, config, cancellationToken);

View File

@ -22,14 +22,15 @@ using System.Text;
using System.Threading; using System.Threading;
using System.Text.RegularExpressions; using System.Text.RegularExpressions;
using System.Threading.Tasks; using System.Threading.Tasks;
using System.Web;
using Newtonsoft.Json; using Newtonsoft.Json;
using Newtonsoft.Json.Serialization; using Newtonsoft.Json.Serialization;
using RestSharp; using RestSharp;
using RestSharp.Serializers; using RestSharp.Serializers;
using RestSharpMethod = RestSharp.Method; using RestSharpMethod = RestSharp.Method;
using FileIO = System.IO.File;
using Polly; using Polly;
using Org.OpenAPITools.Client.Auth; using Org.OpenAPITools.Client.Auth;
using Org.OpenAPITools.Model;
namespace Org.OpenAPITools.Client namespace Org.OpenAPITools.Client
{ {
@ -70,10 +71,10 @@ namespace Org.OpenAPITools.Client
/// <returns>A JSON string.</returns> /// <returns>A JSON string.</returns>
public string Serialize(object obj) public string Serialize(object obj)
{ {
if (obj != null && obj is Org.OpenAPITools.Model.AbstractOpenAPISchema) if (obj != null && obj is AbstractOpenAPISchema)
{ {
// the object to be serialized is an oneOf/anyOf schema // the object to be serialized is an oneOf/anyOf schema
return ((Org.OpenAPITools.Model.AbstractOpenAPISchema)obj).ToJson(); return ((AbstractOpenAPISchema)obj).ToJson();
} }
else else
{ {
@ -118,7 +119,7 @@ namespace Org.OpenAPITools.Client
if (match.Success) if (match.Success)
{ {
string fileName = filePath + ClientUtils.SanitizeFilename(match.Groups[1].Value.Replace("\"", "").Replace("'", "")); string fileName = filePath + ClientUtils.SanitizeFilename(match.Groups[1].Value.Replace("\"", "").Replace("'", ""));
File.WriteAllBytes(fileName, bytes); FileIO.WriteAllBytes(fileName, bytes);
return new FileStream(fileName, FileMode.Open); return new FileStream(fileName, FileMode.Open);
} }
} }
@ -129,7 +130,7 @@ namespace Org.OpenAPITools.Client
if (type.Name.StartsWith("System.Nullable`1[[System.DateTime")) // return a datetime object if (type.Name.StartsWith("System.Nullable`1[[System.DateTime")) // return a datetime object
{ {
return DateTime.Parse(response.Content, null, System.Globalization.DateTimeStyles.RoundtripKind); return DateTime.Parse(response.Content, null, DateTimeStyles.RoundtripKind);
} }
if (type == typeof(string) || type.Name.StartsWith("System.Nullable")) // return primitive type if (type == typeof(string) || type.Name.StartsWith("System.Nullable")) // return primitive type
@ -151,13 +152,13 @@ namespace Org.OpenAPITools.Client
public ISerializer Serializer => this; public ISerializer Serializer => this;
public IDeserializer Deserializer => this; public IDeserializer Deserializer => this;
public string[] AcceptedContentTypes => RestSharp.ContentType.JsonAccept; public string[] AcceptedContentTypes => ContentType.JsonAccept;
public SupportsContentType SupportsContentType => contentType => public SupportsContentType SupportsContentType => contentType =>
contentType.Value.EndsWith("json", StringComparison.InvariantCultureIgnoreCase) || contentType.Value.EndsWith("json", StringComparison.InvariantCultureIgnoreCase) ||
contentType.Value.EndsWith("javascript", StringComparison.InvariantCultureIgnoreCase); contentType.Value.EndsWith("javascript", StringComparison.InvariantCultureIgnoreCase);
public ContentType ContentType { get; set; } = RestSharp.ContentType.Json; public ContentType ContentType { get; set; } = ContentType.Json;
public DataFormat DataFormat => DataFormat.Json; public DataFormat DataFormat => DataFormat.Json;
} }
@ -204,7 +205,7 @@ namespace Org.OpenAPITools.Client
/// </summary> /// </summary>
public ApiClient() public ApiClient()
{ {
_baseUrl = Org.OpenAPITools.Client.GlobalConfiguration.Instance.BasePath; _baseUrl = GlobalConfiguration.Instance.BasePath;
} }
/// <summary> /// <summary>
@ -261,14 +262,14 @@ namespace Org.OpenAPITools.Client
/// <summary> /// <summary>
/// Provides all logic for constructing a new RestSharp <see cref="RestRequest"/>. /// Provides all logic for constructing a new RestSharp <see cref="RestRequest"/>.
/// At this point, all information for querying the service is known. Here, it is simply /// At this point, all information for querying the service is known.
/// mapped into the RestSharp request. /// Here, it is simply mapped into the RestSharp request.
/// </summary> /// </summary>
/// <param name="method">The http verb.</param> /// <param name="method">The http verb.</param>
/// <param name="path">The target path (or resource).</param> /// <param name="path">The target path (or resource).</param>
/// <param name="options">The additional request options.</param> /// <param name="options">The additional request options.</param>
/// <param name="configuration">A per-request configuration object. It is assumed that any merge with /// <param name="configuration">A per-request configuration object.
/// GlobalConfiguration has been done before calling this method.</param> /// It is assumed that any merge with GlobalConfiguration has been done before calling this method.</param>
/// <returns>[private] A new RestRequest instance.</returns> /// <returns>[private] A new RestRequest instance.</returns>
/// <exception cref="ArgumentNullException"></exception> /// <exception cref="ArgumentNullException"></exception>
private RestRequest NewRequest( private RestRequest NewRequest(
@ -376,7 +377,7 @@ namespace Org.OpenAPITools.Client
var bytes = ClientUtils.ReadAsBytes(file); var bytes = ClientUtils.ReadAsBytes(file);
var fileStream = file as FileStream; var fileStream = file as FileStream;
if (fileStream != null) if (fileStream != null)
request.AddFile(fileParam.Key, bytes, System.IO.Path.GetFileName(fileStream.Name)); request.AddFile(fileParam.Key, bytes, Path.GetFileName(fileStream.Name));
else else
request.AddFile(fileParam.Key, bytes, "no_file_name_provided"); request.AddFile(fileParam.Key, bytes, "no_file_name_provided");
} }
@ -386,6 +387,13 @@ namespace Org.OpenAPITools.Client
return request; return request;
} }
/// <summary>
/// Transforms a RestResponse instance into a new ApiResponse instance.
/// At this point, we have a concrete http response from the service.
/// Here, it is simply mapped into the [public] ApiResponse object.
/// </summary>
/// <param name="response">The RestSharp response object</param>
/// <returns>A new ApiResponse instance.</returns>
private ApiResponse<T> ToApiResponse<T>(RestResponse<T> response) private ApiResponse<T> ToApiResponse<T>(RestResponse<T> response)
{ {
T result = response.Data; T result = response.Data;
@ -430,31 +438,32 @@ namespace Org.OpenAPITools.Client
return transformed; return transformed;
} }
private ApiResponse<T> Exec<T>(RestRequest request, RequestOptions options, IReadableConfiguration configuration) /// <summary>
/// Executes the HTTP request for the current service.
/// Based on functions received it can be async or sync.
/// </summary>
/// <param name="getResponse">Local function that executes http request and returns http response.</param>
/// <param name="setOptions">Local function to specify options for the service.</param>
/// <param name="request">The RestSharp request object</param>
/// <param name="options">The RestSharp options object</param>
/// <param name="configuration">A per-request configuration object.
/// It is assumed that any merge with GlobalConfiguration has been done before calling this method.</param>
/// <returns>A new ApiResponse instance.</returns>
private ApiResponse<T> ExecClient<T>(Func<RestClient, RestResponse<T>> getResponse, Action<RestClientOptions> setOptions, RestRequest request, RequestOptions options, IReadableConfiguration configuration)
{ {
var baseUrl = configuration.GetOperationServerUrl(options.Operation, options.OperationIndex) ?? _baseUrl; var baseUrl = configuration.GetOperationServerUrl(options.Operation, options.OperationIndex) ?? _baseUrl;
var cookies = new CookieContainer();
if (options.Cookies != null && options.Cookies.Count > 0)
{
foreach (var cookie in options.Cookies)
{
cookies.Add(new Cookie(cookie.Name, cookie.Value));
}
}
var clientOptions = new RestClientOptions(baseUrl) var clientOptions = new RestClientOptions(baseUrl)
{ {
ClientCertificates = configuration.ClientCertificates, ClientCertificates = configuration.ClientCertificates,
CookieContainer = cookies,
MaxTimeout = configuration.Timeout, MaxTimeout = configuration.Timeout,
Proxy = configuration.Proxy, Proxy = configuration.Proxy,
UserAgent = configuration.UserAgent, UserAgent = configuration.UserAgent,
UseDefaultCredentials = configuration.UseDefaultCredentials, UseDefaultCredentials = configuration.UseDefaultCredentials,
RemoteCertificateValidationCallback = configuration.RemoteCertificateValidationCallback RemoteCertificateValidationCallback = configuration.RemoteCertificateValidationCallback
}; };
setOptions(clientOptions);
if (!string.IsNullOrEmpty(configuration.OAuthTokenUrl) && if (!string.IsNullOrEmpty(configuration.OAuthTokenUrl) &&
!string.IsNullOrEmpty(configuration.OAuthClientId) && !string.IsNullOrEmpty(configuration.OAuthClientId) &&
!string.IsNullOrEmpty(configuration.OAuthClientSecret) && !string.IsNullOrEmpty(configuration.OAuthClientSecret) &&
@ -474,27 +483,14 @@ namespace Org.OpenAPITools.Client
{ {
InterceptRequest(request); InterceptRequest(request);
RestResponse<T> response; RestResponse<T> response = getResponse(client);
if (RetryConfiguration.RetryPolicy != null)
{
var policy = RetryConfiguration.RetryPolicy;
var policyResult = policy.ExecuteAndCapture(() => client.Execute(request));
response = (policyResult.Outcome == OutcomeType.Successful) ? client.Deserialize<T>(policyResult.Result) : new RestResponse<T>(request)
{
ErrorException = policyResult.FinalException
};
}
else
{
response = client.Execute<T>(request);
}
// if the response type is oneOf/anyOf, call FromJSON to deserialize the data // if the response type is oneOf/anyOf, call FromJSON to deserialize the data
if (typeof(Org.OpenAPITools.Model.AbstractOpenAPISchema).IsAssignableFrom(typeof(T))) if (typeof(AbstractOpenAPISchema).IsAssignableFrom(typeof(T)))
{ {
try try
{ {
response.Data = (T) typeof(T).GetMethod("FromJson").Invoke(null, new object[] { response.Content }); response.Data = (T)typeof(T).GetMethod("FromJson").Invoke(null, new object[] { response.Content });
} }
catch (Exception ex) catch (Exception ex)
{ {
@ -552,104 +548,80 @@ namespace Org.OpenAPITools.Client
} }
} }
private async Task<ApiResponse<T>> ExecAsync<T>(RestRequest request, RequestOptions options, IReadableConfiguration configuration, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) private RestResponse<T> DeserializeRestResponseFromPolicy<T>(RestClient client, RestRequest request, PolicyResult<RestResponse> policyResult)
{ {
var baseUrl = configuration.GetOperationServerUrl(options.Operation, options.OperationIndex) ?? _baseUrl; if (policyResult.Outcome == OutcomeType.Successful)
var clientOptions = new RestClientOptions(baseUrl)
{ {
ClientCertificates = configuration.ClientCertificates, return client.Deserialize<T>(policyResult.Result);
MaxTimeout = configuration.Timeout, }
Proxy = configuration.Proxy, else
UserAgent = configuration.UserAgent, {
UseDefaultCredentials = configuration.UseDefaultCredentials, return new RestResponse<T>(request)
RemoteCertificateValidationCallback = configuration.RemoteCertificateValidationCallback {
ErrorException = policyResult.FinalException
};
}
}
private ApiResponse<T> Exec<T>(RestRequest request, RequestOptions options, IReadableConfiguration configuration)
{
Action<RestClientOptions> setOptions = (clientOptions) =>
{
var cookies = new CookieContainer();
if (options.Cookies != null && options.Cookies.Count > 0)
{
foreach (var cookie in options.Cookies)
{
cookies.Add(new Cookie(cookie.Name, cookie.Value));
}
}
clientOptions.CookieContainer = cookies;
}; };
if (!string.IsNullOrEmpty(configuration.OAuthTokenUrl) && Func<RestClient, RestResponse<T>> getResponse = (client) =>
!string.IsNullOrEmpty(configuration.OAuthClientId) &&
!string.IsNullOrEmpty(configuration.OAuthClientSecret) &&
configuration.OAuthFlow != null)
{ {
clientOptions.Authenticator = new OAuthAuthenticator( if (RetryConfiguration.RetryPolicy != null)
configuration.OAuthTokenUrl,
configuration.OAuthClientId,
configuration.OAuthClientSecret,
configuration.OAuthFlow,
SerializerSettings,
configuration);
}
using (RestClient client = new RestClient(clientOptions,
configureSerialization: serializerConfig => serializerConfig.UseSerializer(() => new CustomJsonCodec(SerializerSettings, configuration))))
{
InterceptRequest(request);
RestResponse<T> response;
if (RetryConfiguration.AsyncRetryPolicy != null)
{ {
var policy = RetryConfiguration.AsyncRetryPolicy; var policy = RetryConfiguration.RetryPolicy;
var policyResult = await policy.ExecuteAndCaptureAsync((ct) => client.ExecuteAsync(request, ct), cancellationToken).ConfigureAwait(false); var policyResult = policy.ExecuteAndCapture(() => client.Execute(request));
response = (policyResult.Outcome == OutcomeType.Successful) ? client.Deserialize<T>(policyResult.Result) : new RestResponse<T>(request) return DeserializeRestResponseFromPolicy<T>(client, request, policyResult);
{
ErrorException = policyResult.FinalException
};
} }
else else
{ {
response = await client.ExecuteAsync<T>(request, cancellationToken).ConfigureAwait(false); return client.Execute<T>(request);
} }
};
// if the response type is oneOf/anyOf, call FromJSON to deserialize the data return ExecClient(getResponse, setOptions, request, options, configuration);
if (typeof(Org.OpenAPITools.Model.AbstractOpenAPISchema).IsAssignableFrom(typeof(T))) }
{
response.Data = (T) typeof(T).GetMethod("FromJson").Invoke(null, new object[] { response.Content });
}
else if (typeof(T).Name == "Stream") // for binary response
{
response.Data = (T)(object)new MemoryStream(response.RawBytes);
}
else if (typeof(T).Name == "Byte[]") // for byte response
{
response.Data = (T)(object)response.RawBytes;
}
InterceptResponse(request, response); private Task<ApiResponse<T>> ExecAsync<T>(RestRequest request, RequestOptions options, IReadableConfiguration configuration, CancellationToken cancellationToken = default(CancellationToken))
{
Action<RestClientOptions> setOptions = (clientOptions) =>
{
//no extra options
};
var result = ToApiResponse(response); Func<RestClient, RestResponse<T>> getResponse = (client) =>
if (response.ErrorMessage != null) {
Func<Task<RestResponse<T>>> action = async () =>
{ {
result.ErrorText = response.ErrorMessage; if (RetryConfiguration.AsyncRetryPolicy != null)
}
if (response.Cookies != null && response.Cookies.Count > 0)
{
if (result.Cookies == null) result.Cookies = new List<Cookie>();
foreach (var restResponseCookie in response.Cookies.Cast<Cookie>())
{ {
var cookie = new Cookie( var policy = RetryConfiguration.AsyncRetryPolicy;
restResponseCookie.Name, var policyResult = await policy.ExecuteAndCaptureAsync((ct) => client.ExecuteAsync(request, ct), cancellationToken).ConfigureAwait(false);
restResponseCookie.Value, return DeserializeRestResponseFromPolicy<T>(client, request, policyResult);
restResponseCookie.Path,
restResponseCookie.Domain
)
{
Comment = restResponseCookie.Comment,
CommentUri = restResponseCookie.CommentUri,
Discard = restResponseCookie.Discard,
Expired = restResponseCookie.Expired,
Expires = restResponseCookie.Expires,
HttpOnly = restResponseCookie.HttpOnly,
Port = restResponseCookie.Port,
Secure = restResponseCookie.Secure,
Version = restResponseCookie.Version
};
result.Cookies.Add(cookie);
} }
} else
return result; {
} return await client.ExecuteAsync<T>(request, cancellationToken).ConfigureAwait(false);
}
};
return action().Result;
};
return Task.FromResult<ApiResponse<T>>(ExecClient(getResponse, setOptions, request, options, configuration));
} }
#region IAsynchronousClient #region IAsynchronousClient
@ -662,7 +634,7 @@ namespace Org.OpenAPITools.Client
/// GlobalConfiguration has been done before calling this method.</param> /// GlobalConfiguration has been done before calling this method.</param>
/// <param name="cancellationToken">Token that enables callers to cancel the request.</param> /// <param name="cancellationToken">Token that enables callers to cancel the request.</param>
/// <returns>A Task containing ApiResponse</returns> /// <returns>A Task containing ApiResponse</returns>
public Task<ApiResponse<T>> GetAsync<T>(string path, RequestOptions options, IReadableConfiguration configuration = null, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) public Task<ApiResponse<T>> GetAsync<T>(string path, RequestOptions options, IReadableConfiguration configuration = null, CancellationToken cancellationToken = default)
{ {
var config = configuration ?? GlobalConfiguration.Instance; var config = configuration ?? GlobalConfiguration.Instance;
return ExecAsync<T>(NewRequest(HttpMethod.Get, path, options, config), options, config, cancellationToken); return ExecAsync<T>(NewRequest(HttpMethod.Get, path, options, config), options, config, cancellationToken);
@ -677,7 +649,7 @@ namespace Org.OpenAPITools.Client
/// GlobalConfiguration has been done before calling this method.</param> /// GlobalConfiguration has been done before calling this method.</param>
/// <param name="cancellationToken">Token that enables callers to cancel the request.</param> /// <param name="cancellationToken">Token that enables callers to cancel the request.</param>
/// <returns>A Task containing ApiResponse</returns> /// <returns>A Task containing ApiResponse</returns>
public Task<ApiResponse<T>> PostAsync<T>(string path, RequestOptions options, IReadableConfiguration configuration = null, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) public Task<ApiResponse<T>> PostAsync<T>(string path, RequestOptions options, IReadableConfiguration configuration = null, CancellationToken cancellationToken = default)
{ {
var config = configuration ?? GlobalConfiguration.Instance; var config = configuration ?? GlobalConfiguration.Instance;
return ExecAsync<T>(NewRequest(HttpMethod.Post, path, options, config), options, config, cancellationToken); return ExecAsync<T>(NewRequest(HttpMethod.Post, path, options, config), options, config, cancellationToken);
@ -692,7 +664,7 @@ namespace Org.OpenAPITools.Client
/// GlobalConfiguration has been done before calling this method.</param> /// GlobalConfiguration has been done before calling this method.</param>
/// <param name="cancellationToken">Token that enables callers to cancel the request.</param> /// <param name="cancellationToken">Token that enables callers to cancel the request.</param>
/// <returns>A Task containing ApiResponse</returns> /// <returns>A Task containing ApiResponse</returns>
public Task<ApiResponse<T>> PutAsync<T>(string path, RequestOptions options, IReadableConfiguration configuration = null, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) public Task<ApiResponse<T>> PutAsync<T>(string path, RequestOptions options, IReadableConfiguration configuration = null, CancellationToken cancellationToken = default)
{ {
var config = configuration ?? GlobalConfiguration.Instance; var config = configuration ?? GlobalConfiguration.Instance;
return ExecAsync<T>(NewRequest(HttpMethod.Put, path, options, config), options, config, cancellationToken); return ExecAsync<T>(NewRequest(HttpMethod.Put, path, options, config), options, config, cancellationToken);
@ -707,7 +679,7 @@ namespace Org.OpenAPITools.Client
/// GlobalConfiguration has been done before calling this method.</param> /// GlobalConfiguration has been done before calling this method.</param>
/// <param name="cancellationToken">Token that enables callers to cancel the request.</param> /// <param name="cancellationToken">Token that enables callers to cancel the request.</param>
/// <returns>A Task containing ApiResponse</returns> /// <returns>A Task containing ApiResponse</returns>
public Task<ApiResponse<T>> DeleteAsync<T>(string path, RequestOptions options, IReadableConfiguration configuration = null, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) public Task<ApiResponse<T>> DeleteAsync<T>(string path, RequestOptions options, IReadableConfiguration configuration = null, CancellationToken cancellationToken = default)
{ {
var config = configuration ?? GlobalConfiguration.Instance; var config = configuration ?? GlobalConfiguration.Instance;
return ExecAsync<T>(NewRequest(HttpMethod.Delete, path, options, config), options, config, cancellationToken); return ExecAsync<T>(NewRequest(HttpMethod.Delete, path, options, config), options, config, cancellationToken);
@ -722,7 +694,7 @@ namespace Org.OpenAPITools.Client
/// GlobalConfiguration has been done before calling this method.</param> /// GlobalConfiguration has been done before calling this method.</param>
/// <param name="cancellationToken">Token that enables callers to cancel the request.</param> /// <param name="cancellationToken">Token that enables callers to cancel the request.</param>
/// <returns>A Task containing ApiResponse</returns> /// <returns>A Task containing ApiResponse</returns>
public Task<ApiResponse<T>> HeadAsync<T>(string path, RequestOptions options, IReadableConfiguration configuration = null, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) public Task<ApiResponse<T>> HeadAsync<T>(string path, RequestOptions options, IReadableConfiguration configuration = null, CancellationToken cancellationToken = default)
{ {
var config = configuration ?? GlobalConfiguration.Instance; var config = configuration ?? GlobalConfiguration.Instance;
return ExecAsync<T>(NewRequest(HttpMethod.Head, path, options, config), options, config, cancellationToken); return ExecAsync<T>(NewRequest(HttpMethod.Head, path, options, config), options, config, cancellationToken);
@ -737,7 +709,7 @@ namespace Org.OpenAPITools.Client
/// GlobalConfiguration has been done before calling this method.</param> /// GlobalConfiguration has been done before calling this method.</param>
/// <param name="cancellationToken">Token that enables callers to cancel the request.</param> /// <param name="cancellationToken">Token that enables callers to cancel the request.</param>
/// <returns>A Task containing ApiResponse</returns> /// <returns>A Task containing ApiResponse</returns>
public Task<ApiResponse<T>> OptionsAsync<T>(string path, RequestOptions options, IReadableConfiguration configuration = null, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) public Task<ApiResponse<T>> OptionsAsync<T>(string path, RequestOptions options, IReadableConfiguration configuration = null, CancellationToken cancellationToken = default)
{ {
var config = configuration ?? GlobalConfiguration.Instance; var config = configuration ?? GlobalConfiguration.Instance;
return ExecAsync<T>(NewRequest(HttpMethod.Options, path, options, config), options, config, cancellationToken); return ExecAsync<T>(NewRequest(HttpMethod.Options, path, options, config), options, config, cancellationToken);
@ -752,7 +724,7 @@ namespace Org.OpenAPITools.Client
/// GlobalConfiguration has been done before calling this method.</param> /// GlobalConfiguration has been done before calling this method.</param>
/// <param name="cancellationToken">Token that enables callers to cancel the request.</param> /// <param name="cancellationToken">Token that enables callers to cancel the request.</param>
/// <returns>A Task containing ApiResponse</returns> /// <returns>A Task containing ApiResponse</returns>
public Task<ApiResponse<T>> PatchAsync<T>(string path, RequestOptions options, IReadableConfiguration configuration = null, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) public Task<ApiResponse<T>> PatchAsync<T>(string path, RequestOptions options, IReadableConfiguration configuration = null, CancellationToken cancellationToken = default)
{ {
var config = configuration ?? GlobalConfiguration.Instance; var config = configuration ?? GlobalConfiguration.Instance;
return ExecAsync<T>(NewRequest(HttpMethod.Patch, path, options, config), options, config, cancellationToken); return ExecAsync<T>(NewRequest(HttpMethod.Patch, path, options, config), options, config, cancellationToken);

View File

@ -22,13 +22,14 @@ using System.Text;
using System.Threading; using System.Threading;
using System.Text.RegularExpressions; using System.Text.RegularExpressions;
using System.Threading.Tasks; using System.Threading.Tasks;
using System.Web;
using Newtonsoft.Json; using Newtonsoft.Json;
using Newtonsoft.Json.Serialization; using Newtonsoft.Json.Serialization;
using RestSharp; using RestSharp;
using RestSharp.Serializers; using RestSharp.Serializers;
using RestSharpMethod = RestSharp.Method; using RestSharpMethod = RestSharp.Method;
using FileIO = System.IO.File;
using Polly; using Polly;
using Org.OpenAPITools.Model;
namespace Org.OpenAPITools.Client namespace Org.OpenAPITools.Client
{ {
@ -69,10 +70,10 @@ namespace Org.OpenAPITools.Client
/// <returns>A JSON string.</returns> /// <returns>A JSON string.</returns>
public string Serialize(object obj) public string Serialize(object obj)
{ {
if (obj != null && obj is Org.OpenAPITools.Model.AbstractOpenAPISchema) if (obj != null && obj is AbstractOpenAPISchema)
{ {
// the object to be serialized is an oneOf/anyOf schema // the object to be serialized is an oneOf/anyOf schema
return ((Org.OpenAPITools.Model.AbstractOpenAPISchema)obj).ToJson(); return ((AbstractOpenAPISchema)obj).ToJson();
} }
else else
{ {
@ -117,7 +118,7 @@ namespace Org.OpenAPITools.Client
if (match.Success) if (match.Success)
{ {
string fileName = filePath + ClientUtils.SanitizeFilename(match.Groups[1].Value.Replace("\"", "").Replace("'", "")); string fileName = filePath + ClientUtils.SanitizeFilename(match.Groups[1].Value.Replace("\"", "").Replace("'", ""));
File.WriteAllBytes(fileName, bytes); FileIO.WriteAllBytes(fileName, bytes);
return new FileStream(fileName, FileMode.Open); return new FileStream(fileName, FileMode.Open);
} }
} }
@ -128,7 +129,7 @@ namespace Org.OpenAPITools.Client
if (type.Name.StartsWith("System.Nullable`1[[System.DateTime")) // return a datetime object if (type.Name.StartsWith("System.Nullable`1[[System.DateTime")) // return a datetime object
{ {
return DateTime.Parse(response.Content, null, System.Globalization.DateTimeStyles.RoundtripKind); return DateTime.Parse(response.Content, null, DateTimeStyles.RoundtripKind);
} }
if (type == typeof(string) || type.Name.StartsWith("System.Nullable")) // return primitive type if (type == typeof(string) || type.Name.StartsWith("System.Nullable")) // return primitive type
@ -150,13 +151,13 @@ namespace Org.OpenAPITools.Client
public ISerializer Serializer => this; public ISerializer Serializer => this;
public IDeserializer Deserializer => this; public IDeserializer Deserializer => this;
public string[] AcceptedContentTypes => RestSharp.ContentType.JsonAccept; public string[] AcceptedContentTypes => ContentType.JsonAccept;
public SupportsContentType SupportsContentType => contentType => public SupportsContentType SupportsContentType => contentType =>
contentType.Value.EndsWith("json", StringComparison.InvariantCultureIgnoreCase) || contentType.Value.EndsWith("json", StringComparison.InvariantCultureIgnoreCase) ||
contentType.Value.EndsWith("javascript", StringComparison.InvariantCultureIgnoreCase); contentType.Value.EndsWith("javascript", StringComparison.InvariantCultureIgnoreCase);
public ContentType ContentType { get; set; } = RestSharp.ContentType.Json; public ContentType ContentType { get; set; } = ContentType.Json;
public DataFormat DataFormat => DataFormat.Json; public DataFormat DataFormat => DataFormat.Json;
} }
@ -203,7 +204,7 @@ namespace Org.OpenAPITools.Client
/// </summary> /// </summary>
public ApiClient() public ApiClient()
{ {
_baseUrl = Org.OpenAPITools.Client.GlobalConfiguration.Instance.BasePath; _baseUrl = GlobalConfiguration.Instance.BasePath;
} }
/// <summary> /// <summary>
@ -260,14 +261,14 @@ namespace Org.OpenAPITools.Client
/// <summary> /// <summary>
/// Provides all logic for constructing a new RestSharp <see cref="RestRequest"/>. /// Provides all logic for constructing a new RestSharp <see cref="RestRequest"/>.
/// At this point, all information for querying the service is known. Here, it is simply /// At this point, all information for querying the service is known.
/// mapped into the RestSharp request. /// Here, it is simply mapped into the RestSharp request.
/// </summary> /// </summary>
/// <param name="method">The http verb.</param> /// <param name="method">The http verb.</param>
/// <param name="path">The target path (or resource).</param> /// <param name="path">The target path (or resource).</param>
/// <param name="options">The additional request options.</param> /// <param name="options">The additional request options.</param>
/// <param name="configuration">A per-request configuration object. It is assumed that any merge with /// <param name="configuration">A per-request configuration object.
/// GlobalConfiguration has been done before calling this method.</param> /// It is assumed that any merge with GlobalConfiguration has been done before calling this method.</param>
/// <returns>[private] A new RestRequest instance.</returns> /// <returns>[private] A new RestRequest instance.</returns>
/// <exception cref="ArgumentNullException"></exception> /// <exception cref="ArgumentNullException"></exception>
private RestRequest NewRequest( private RestRequest NewRequest(
@ -375,7 +376,7 @@ namespace Org.OpenAPITools.Client
var bytes = ClientUtils.ReadAsBytes(file); var bytes = ClientUtils.ReadAsBytes(file);
var fileStream = file as FileStream; var fileStream = file as FileStream;
if (fileStream != null) if (fileStream != null)
request.AddFile(fileParam.Key, bytes, System.IO.Path.GetFileName(fileStream.Name)); request.AddFile(fileParam.Key, bytes, Path.GetFileName(fileStream.Name));
else else
request.AddFile(fileParam.Key, bytes, "no_file_name_provided"); request.AddFile(fileParam.Key, bytes, "no_file_name_provided");
} }
@ -385,6 +386,13 @@ namespace Org.OpenAPITools.Client
return request; return request;
} }
/// <summary>
/// Transforms a RestResponse instance into a new ApiResponse instance.
/// At this point, we have a concrete http response from the service.
/// Here, it is simply mapped into the [public] ApiResponse object.
/// </summary>
/// <param name="response">The RestSharp response object</param>
/// <returns>A new ApiResponse instance.</returns>
private ApiResponse<T> ToApiResponse<T>(RestResponse<T> response) private ApiResponse<T> ToApiResponse<T>(RestResponse<T> response)
{ {
T result = response.Data; T result = response.Data;
@ -429,57 +437,45 @@ namespace Org.OpenAPITools.Client
return transformed; return transformed;
} }
private ApiResponse<T> Exec<T>(RestRequest request, RequestOptions options, IReadableConfiguration configuration) /// <summary>
/// Executes the HTTP request for the current service.
/// Based on functions received it can be async or sync.
/// </summary>
/// <param name="getResponse">Local function that executes http request and returns http response.</param>
/// <param name="setOptions">Local function to specify options for the service.</param>
/// <param name="request">The RestSharp request object</param>
/// <param name="options">The RestSharp options object</param>
/// <param name="configuration">A per-request configuration object.
/// It is assumed that any merge with GlobalConfiguration has been done before calling this method.</param>
/// <returns>A new ApiResponse instance.</returns>
private ApiResponse<T> ExecClient<T>(Func<RestClient, RestResponse<T>> getResponse, Action<RestClientOptions> setOptions, RestRequest request, RequestOptions options, IReadableConfiguration configuration)
{ {
var baseUrl = configuration.GetOperationServerUrl(options.Operation, options.OperationIndex) ?? _baseUrl; var baseUrl = configuration.GetOperationServerUrl(options.Operation, options.OperationIndex) ?? _baseUrl;
var cookies = new CookieContainer();
if (options.Cookies != null && options.Cookies.Count > 0)
{
foreach (var cookie in options.Cookies)
{
cookies.Add(new Cookie(cookie.Name, cookie.Value));
}
}
var clientOptions = new RestClientOptions(baseUrl) var clientOptions = new RestClientOptions(baseUrl)
{ {
ClientCertificates = configuration.ClientCertificates, ClientCertificates = configuration.ClientCertificates,
CookieContainer = cookies,
MaxTimeout = configuration.Timeout, MaxTimeout = configuration.Timeout,
Proxy = configuration.Proxy, Proxy = configuration.Proxy,
UserAgent = configuration.UserAgent, UserAgent = configuration.UserAgent,
UseDefaultCredentials = configuration.UseDefaultCredentials, UseDefaultCredentials = configuration.UseDefaultCredentials,
RemoteCertificateValidationCallback = configuration.RemoteCertificateValidationCallback RemoteCertificateValidationCallback = configuration.RemoteCertificateValidationCallback
}; };
setOptions(clientOptions);
using (RestClient client = new RestClient(clientOptions, using (RestClient client = new RestClient(clientOptions,
configureSerialization: serializerConfig => serializerConfig.UseSerializer(() => new CustomJsonCodec(SerializerSettings, configuration)))) configureSerialization: serializerConfig => serializerConfig.UseSerializer(() => new CustomJsonCodec(SerializerSettings, configuration))))
{ {
InterceptRequest(request); InterceptRequest(request);
RestResponse<T> response; RestResponse<T> response = getResponse(client);
if (RetryConfiguration.RetryPolicy != null)
{
var policy = RetryConfiguration.RetryPolicy;
var policyResult = policy.ExecuteAndCapture(() => client.Execute(request));
response = (policyResult.Outcome == OutcomeType.Successful) ? client.Deserialize<T>(policyResult.Result) : new RestResponse<T>(request)
{
ErrorException = policyResult.FinalException
};
}
else
{
response = client.Execute<T>(request);
}
// if the response type is oneOf/anyOf, call FromJSON to deserialize the data // if the response type is oneOf/anyOf, call FromJSON to deserialize the data
if (typeof(Org.OpenAPITools.Model.AbstractOpenAPISchema).IsAssignableFrom(typeof(T))) if (typeof(AbstractOpenAPISchema).IsAssignableFrom(typeof(T)))
{ {
try try
{ {
response.Data = (T) typeof(T).GetMethod("FromJson").Invoke(null, new object[] { response.Content }); response.Data = (T)typeof(T).GetMethod("FromJson").Invoke(null, new object[] { response.Content });
} }
catch (Exception ex) catch (Exception ex)
{ {
@ -537,90 +533,80 @@ namespace Org.OpenAPITools.Client
} }
} }
private async Task<ApiResponse<T>> ExecAsync<T>(RestRequest request, RequestOptions options, IReadableConfiguration configuration, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) private RestResponse<T> DeserializeRestResponseFromPolicy<T>(RestClient client, RestRequest request, PolicyResult<RestResponse> policyResult)
{ {
var baseUrl = configuration.GetOperationServerUrl(options.Operation, options.OperationIndex) ?? _baseUrl; if (policyResult.Outcome == OutcomeType.Successful)
var clientOptions = new RestClientOptions(baseUrl)
{ {
ClientCertificates = configuration.ClientCertificates, return client.Deserialize<T>(policyResult.Result);
MaxTimeout = configuration.Timeout, }
Proxy = configuration.Proxy, else
UserAgent = configuration.UserAgent, {
UseDefaultCredentials = configuration.UseDefaultCredentials, return new RestResponse<T>(request)
RemoteCertificateValidationCallback = configuration.RemoteCertificateValidationCallback {
ErrorException = policyResult.FinalException
};
}
}
private ApiResponse<T> Exec<T>(RestRequest request, RequestOptions options, IReadableConfiguration configuration)
{
Action<RestClientOptions> setOptions = (clientOptions) =>
{
var cookies = new CookieContainer();
if (options.Cookies != null && options.Cookies.Count > 0)
{
foreach (var cookie in options.Cookies)
{
cookies.Add(new Cookie(cookie.Name, cookie.Value));
}
}
clientOptions.CookieContainer = cookies;
}; };
using (RestClient client = new RestClient(clientOptions, Func<RestClient, RestResponse<T>> getResponse = (client) =>
configureSerialization: serializerConfig => serializerConfig.UseSerializer(() => new CustomJsonCodec(SerializerSettings, configuration))))
{ {
InterceptRequest(request); if (RetryConfiguration.RetryPolicy != null)
RestResponse<T> response;
if (RetryConfiguration.AsyncRetryPolicy != null)
{ {
var policy = RetryConfiguration.AsyncRetryPolicy; var policy = RetryConfiguration.RetryPolicy;
var policyResult = await policy.ExecuteAndCaptureAsync((ct) => client.ExecuteAsync(request, ct), cancellationToken).ConfigureAwait(false); var policyResult = policy.ExecuteAndCapture(() => client.Execute(request));
response = (policyResult.Outcome == OutcomeType.Successful) ? client.Deserialize<T>(policyResult.Result) : new RestResponse<T>(request) return DeserializeRestResponseFromPolicy<T>(client, request, policyResult);
{
ErrorException = policyResult.FinalException
};
} }
else else
{ {
response = await client.ExecuteAsync<T>(request, cancellationToken).ConfigureAwait(false); return client.Execute<T>(request);
} }
};
// if the response type is oneOf/anyOf, call FromJSON to deserialize the data return ExecClient(getResponse, setOptions, request, options, configuration);
if (typeof(Org.OpenAPITools.Model.AbstractOpenAPISchema).IsAssignableFrom(typeof(T))) }
{
response.Data = (T) typeof(T).GetMethod("FromJson").Invoke(null, new object[] { response.Content });
}
else if (typeof(T).Name == "Stream") // for binary response
{
response.Data = (T)(object)new MemoryStream(response.RawBytes);
}
else if (typeof(T).Name == "Byte[]") // for byte response
{
response.Data = (T)(object)response.RawBytes;
}
InterceptResponse(request, response); private Task<ApiResponse<T>> ExecAsync<T>(RestRequest request, RequestOptions options, IReadableConfiguration configuration, CancellationToken cancellationToken = default(CancellationToken))
{
Action<RestClientOptions> setOptions = (clientOptions) =>
{
//no extra options
};
var result = ToApiResponse(response); Func<RestClient, RestResponse<T>> getResponse = (client) =>
if (response.ErrorMessage != null) {
Func<Task<RestResponse<T>>> action = async () =>
{ {
result.ErrorText = response.ErrorMessage; if (RetryConfiguration.AsyncRetryPolicy != null)
}
if (response.Cookies != null && response.Cookies.Count > 0)
{
if (result.Cookies == null) result.Cookies = new List<Cookie>();
foreach (var restResponseCookie in response.Cookies.Cast<Cookie>())
{ {
var cookie = new Cookie( var policy = RetryConfiguration.AsyncRetryPolicy;
restResponseCookie.Name, var policyResult = await policy.ExecuteAndCaptureAsync((ct) => client.ExecuteAsync(request, ct), cancellationToken).ConfigureAwait(false);
restResponseCookie.Value, return DeserializeRestResponseFromPolicy<T>(client, request, policyResult);
restResponseCookie.Path,
restResponseCookie.Domain
)
{
Comment = restResponseCookie.Comment,
CommentUri = restResponseCookie.CommentUri,
Discard = restResponseCookie.Discard,
Expired = restResponseCookie.Expired,
Expires = restResponseCookie.Expires,
HttpOnly = restResponseCookie.HttpOnly,
Port = restResponseCookie.Port,
Secure = restResponseCookie.Secure,
Version = restResponseCookie.Version
};
result.Cookies.Add(cookie);
} }
} else
return result; {
} return await client.ExecuteAsync<T>(request, cancellationToken).ConfigureAwait(false);
}
};
return action().Result;
};
return Task.FromResult<ApiResponse<T>>(ExecClient(getResponse, setOptions, request, options, configuration));
} }
#region IAsynchronousClient #region IAsynchronousClient
@ -633,7 +619,7 @@ namespace Org.OpenAPITools.Client
/// GlobalConfiguration has been done before calling this method.</param> /// GlobalConfiguration has been done before calling this method.</param>
/// <param name="cancellationToken">Token that enables callers to cancel the request.</param> /// <param name="cancellationToken">Token that enables callers to cancel the request.</param>
/// <returns>A Task containing ApiResponse</returns> /// <returns>A Task containing ApiResponse</returns>
public Task<ApiResponse<T>> GetAsync<T>(string path, RequestOptions options, IReadableConfiguration configuration = null, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) public Task<ApiResponse<T>> GetAsync<T>(string path, RequestOptions options, IReadableConfiguration configuration = null, CancellationToken cancellationToken = default)
{ {
var config = configuration ?? GlobalConfiguration.Instance; var config = configuration ?? GlobalConfiguration.Instance;
return ExecAsync<T>(NewRequest(HttpMethod.Get, path, options, config), options, config, cancellationToken); return ExecAsync<T>(NewRequest(HttpMethod.Get, path, options, config), options, config, cancellationToken);
@ -648,7 +634,7 @@ namespace Org.OpenAPITools.Client
/// GlobalConfiguration has been done before calling this method.</param> /// GlobalConfiguration has been done before calling this method.</param>
/// <param name="cancellationToken">Token that enables callers to cancel the request.</param> /// <param name="cancellationToken">Token that enables callers to cancel the request.</param>
/// <returns>A Task containing ApiResponse</returns> /// <returns>A Task containing ApiResponse</returns>
public Task<ApiResponse<T>> PostAsync<T>(string path, RequestOptions options, IReadableConfiguration configuration = null, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) public Task<ApiResponse<T>> PostAsync<T>(string path, RequestOptions options, IReadableConfiguration configuration = null, CancellationToken cancellationToken = default)
{ {
var config = configuration ?? GlobalConfiguration.Instance; var config = configuration ?? GlobalConfiguration.Instance;
return ExecAsync<T>(NewRequest(HttpMethod.Post, path, options, config), options, config, cancellationToken); return ExecAsync<T>(NewRequest(HttpMethod.Post, path, options, config), options, config, cancellationToken);
@ -663,7 +649,7 @@ namespace Org.OpenAPITools.Client
/// GlobalConfiguration has been done before calling this method.</param> /// GlobalConfiguration has been done before calling this method.</param>
/// <param name="cancellationToken">Token that enables callers to cancel the request.</param> /// <param name="cancellationToken">Token that enables callers to cancel the request.</param>
/// <returns>A Task containing ApiResponse</returns> /// <returns>A Task containing ApiResponse</returns>
public Task<ApiResponse<T>> PutAsync<T>(string path, RequestOptions options, IReadableConfiguration configuration = null, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) public Task<ApiResponse<T>> PutAsync<T>(string path, RequestOptions options, IReadableConfiguration configuration = null, CancellationToken cancellationToken = default)
{ {
var config = configuration ?? GlobalConfiguration.Instance; var config = configuration ?? GlobalConfiguration.Instance;
return ExecAsync<T>(NewRequest(HttpMethod.Put, path, options, config), options, config, cancellationToken); return ExecAsync<T>(NewRequest(HttpMethod.Put, path, options, config), options, config, cancellationToken);
@ -678,7 +664,7 @@ namespace Org.OpenAPITools.Client
/// GlobalConfiguration has been done before calling this method.</param> /// GlobalConfiguration has been done before calling this method.</param>
/// <param name="cancellationToken">Token that enables callers to cancel the request.</param> /// <param name="cancellationToken">Token that enables callers to cancel the request.</param>
/// <returns>A Task containing ApiResponse</returns> /// <returns>A Task containing ApiResponse</returns>
public Task<ApiResponse<T>> DeleteAsync<T>(string path, RequestOptions options, IReadableConfiguration configuration = null, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) public Task<ApiResponse<T>> DeleteAsync<T>(string path, RequestOptions options, IReadableConfiguration configuration = null, CancellationToken cancellationToken = default)
{ {
var config = configuration ?? GlobalConfiguration.Instance; var config = configuration ?? GlobalConfiguration.Instance;
return ExecAsync<T>(NewRequest(HttpMethod.Delete, path, options, config), options, config, cancellationToken); return ExecAsync<T>(NewRequest(HttpMethod.Delete, path, options, config), options, config, cancellationToken);
@ -693,7 +679,7 @@ namespace Org.OpenAPITools.Client
/// GlobalConfiguration has been done before calling this method.</param> /// GlobalConfiguration has been done before calling this method.</param>
/// <param name="cancellationToken">Token that enables callers to cancel the request.</param> /// <param name="cancellationToken">Token that enables callers to cancel the request.</param>
/// <returns>A Task containing ApiResponse</returns> /// <returns>A Task containing ApiResponse</returns>
public Task<ApiResponse<T>> HeadAsync<T>(string path, RequestOptions options, IReadableConfiguration configuration = null, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) public Task<ApiResponse<T>> HeadAsync<T>(string path, RequestOptions options, IReadableConfiguration configuration = null, CancellationToken cancellationToken = default)
{ {
var config = configuration ?? GlobalConfiguration.Instance; var config = configuration ?? GlobalConfiguration.Instance;
return ExecAsync<T>(NewRequest(HttpMethod.Head, path, options, config), options, config, cancellationToken); return ExecAsync<T>(NewRequest(HttpMethod.Head, path, options, config), options, config, cancellationToken);
@ -708,7 +694,7 @@ namespace Org.OpenAPITools.Client
/// GlobalConfiguration has been done before calling this method.</param> /// GlobalConfiguration has been done before calling this method.</param>
/// <param name="cancellationToken">Token that enables callers to cancel the request.</param> /// <param name="cancellationToken">Token that enables callers to cancel the request.</param>
/// <returns>A Task containing ApiResponse</returns> /// <returns>A Task containing ApiResponse</returns>
public Task<ApiResponse<T>> OptionsAsync<T>(string path, RequestOptions options, IReadableConfiguration configuration = null, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) public Task<ApiResponse<T>> OptionsAsync<T>(string path, RequestOptions options, IReadableConfiguration configuration = null, CancellationToken cancellationToken = default)
{ {
var config = configuration ?? GlobalConfiguration.Instance; var config = configuration ?? GlobalConfiguration.Instance;
return ExecAsync<T>(NewRequest(HttpMethod.Options, path, options, config), options, config, cancellationToken); return ExecAsync<T>(NewRequest(HttpMethod.Options, path, options, config), options, config, cancellationToken);
@ -723,7 +709,7 @@ namespace Org.OpenAPITools.Client
/// GlobalConfiguration has been done before calling this method.</param> /// GlobalConfiguration has been done before calling this method.</param>
/// <param name="cancellationToken">Token that enables callers to cancel the request.</param> /// <param name="cancellationToken">Token that enables callers to cancel the request.</param>
/// <returns>A Task containing ApiResponse</returns> /// <returns>A Task containing ApiResponse</returns>
public Task<ApiResponse<T>> PatchAsync<T>(string path, RequestOptions options, IReadableConfiguration configuration = null, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) public Task<ApiResponse<T>> PatchAsync<T>(string path, RequestOptions options, IReadableConfiguration configuration = null, CancellationToken cancellationToken = default)
{ {
var config = configuration ?? GlobalConfiguration.Instance; var config = configuration ?? GlobalConfiguration.Instance;
return ExecAsync<T>(NewRequest(HttpMethod.Patch, path, options, config), options, config, cancellationToken); return ExecAsync<T>(NewRequest(HttpMethod.Patch, path, options, config), options, config, cancellationToken);

View File

@ -22,13 +22,16 @@ using System.Text;
using System.Threading; using System.Threading;
using System.Text.RegularExpressions; using System.Text.RegularExpressions;
using System.Threading.Tasks; using System.Threading.Tasks;
using System.Web;
using Newtonsoft.Json; using Newtonsoft.Json;
using Newtonsoft.Json.Serialization; using Newtonsoft.Json.Serialization;
using RestSharp; using RestSharp;
using RestSharp.Serializers; using RestSharp.Serializers;
using RestSharpMethod = RestSharp.Method; using RestSharpMethod = RestSharp.Method;
using FileIO = System.IO.File;
using Polly; using Polly;
using Org.OpenAPITools.Client.Auth; using Org.OpenAPITools.Client.Auth;
using Org.OpenAPITools.Model;
namespace Org.OpenAPITools.Client namespace Org.OpenAPITools.Client
{ {
@ -69,10 +72,10 @@ namespace Org.OpenAPITools.Client
/// <returns>A JSON string.</returns> /// <returns>A JSON string.</returns>
public string Serialize(object obj) public string Serialize(object obj)
{ {
if (obj != null && obj is Org.OpenAPITools.Model.AbstractOpenAPISchema) if (obj != null && obj is AbstractOpenAPISchema)
{ {
// the object to be serialized is an oneOf/anyOf schema // the object to be serialized is an oneOf/anyOf schema
return ((Org.OpenAPITools.Model.AbstractOpenAPISchema)obj).ToJson(); return ((AbstractOpenAPISchema)obj).ToJson();
} }
else else
{ {
@ -117,7 +120,7 @@ namespace Org.OpenAPITools.Client
if (match.Success) if (match.Success)
{ {
string fileName = filePath + ClientUtils.SanitizeFilename(match.Groups[1].Value.Replace("\"", "").Replace("'", "")); string fileName = filePath + ClientUtils.SanitizeFilename(match.Groups[1].Value.Replace("\"", "").Replace("'", ""));
File.WriteAllBytes(fileName, bytes); FileIO.WriteAllBytes(fileName, bytes);
return new FileStream(fileName, FileMode.Open); return new FileStream(fileName, FileMode.Open);
} }
} }
@ -128,7 +131,7 @@ namespace Org.OpenAPITools.Client
if (type.Name.StartsWith("System.Nullable`1[[System.DateTime")) // return a datetime object if (type.Name.StartsWith("System.Nullable`1[[System.DateTime")) // return a datetime object
{ {
return DateTime.Parse(response.Content, null, System.Globalization.DateTimeStyles.RoundtripKind); return DateTime.Parse(response.Content, null, DateTimeStyles.RoundtripKind);
} }
if (type == typeof(string) || type.Name.StartsWith("System.Nullable")) // return primitive type if (type == typeof(string) || type.Name.StartsWith("System.Nullable")) // return primitive type
@ -150,13 +153,13 @@ namespace Org.OpenAPITools.Client
public ISerializer Serializer => this; public ISerializer Serializer => this;
public IDeserializer Deserializer => this; public IDeserializer Deserializer => this;
public string[] AcceptedContentTypes => RestSharp.ContentType.JsonAccept; public string[] AcceptedContentTypes => ContentType.JsonAccept;
public SupportsContentType SupportsContentType => contentType => public SupportsContentType SupportsContentType => contentType =>
contentType.Value.EndsWith("json", StringComparison.InvariantCultureIgnoreCase) || contentType.Value.EndsWith("json", StringComparison.InvariantCultureIgnoreCase) ||
contentType.Value.EndsWith("javascript", StringComparison.InvariantCultureIgnoreCase); contentType.Value.EndsWith("javascript", StringComparison.InvariantCultureIgnoreCase);
public ContentType ContentType { get; set; } = RestSharp.ContentType.Json; public ContentType ContentType { get; set; } = ContentType.Json;
public DataFormat DataFormat => DataFormat.Json; public DataFormat DataFormat => DataFormat.Json;
} }
@ -203,7 +206,7 @@ namespace Org.OpenAPITools.Client
/// </summary> /// </summary>
public ApiClient() public ApiClient()
{ {
_baseUrl = Org.OpenAPITools.Client.GlobalConfiguration.Instance.BasePath; _baseUrl = GlobalConfiguration.Instance.BasePath;
} }
/// <summary> /// <summary>
@ -260,14 +263,14 @@ namespace Org.OpenAPITools.Client
/// <summary> /// <summary>
/// Provides all logic for constructing a new RestSharp <see cref="RestRequest"/>. /// Provides all logic for constructing a new RestSharp <see cref="RestRequest"/>.
/// At this point, all information for querying the service is known. Here, it is simply /// At this point, all information for querying the service is known.
/// mapped into the RestSharp request. /// Here, it is simply mapped into the RestSharp request.
/// </summary> /// </summary>
/// <param name="method">The http verb.</param> /// <param name="method">The http verb.</param>
/// <param name="path">The target path (or resource).</param> /// <param name="path">The target path (or resource).</param>
/// <param name="options">The additional request options.</param> /// <param name="options">The additional request options.</param>
/// <param name="configuration">A per-request configuration object. It is assumed that any merge with /// <param name="configuration">A per-request configuration object.
/// GlobalConfiguration has been done before calling this method.</param> /// It is assumed that any merge with GlobalConfiguration has been done before calling this method.</param>
/// <returns>[private] A new RestRequest instance.</returns> /// <returns>[private] A new RestRequest instance.</returns>
/// <exception cref="ArgumentNullException"></exception> /// <exception cref="ArgumentNullException"></exception>
private RestRequest NewRequest( private RestRequest NewRequest(
@ -375,7 +378,7 @@ namespace Org.OpenAPITools.Client
var bytes = ClientUtils.ReadAsBytes(file); var bytes = ClientUtils.ReadAsBytes(file);
var fileStream = file as FileStream; var fileStream = file as FileStream;
if (fileStream != null) if (fileStream != null)
request.AddFile(fileParam.Key, bytes, System.IO.Path.GetFileName(fileStream.Name)); request.AddFile(fileParam.Key, bytes, Path.GetFileName(fileStream.Name));
else else
request.AddFile(fileParam.Key, bytes, "no_file_name_provided"); request.AddFile(fileParam.Key, bytes, "no_file_name_provided");
} }
@ -385,6 +388,13 @@ namespace Org.OpenAPITools.Client
return request; return request;
} }
/// <summary>
/// Transforms a RestResponse instance into a new ApiResponse instance.
/// At this point, we have a concrete http response from the service.
/// Here, it is simply mapped into the [public] ApiResponse object.
/// </summary>
/// <param name="response">The RestSharp response object</param>
/// <returns>A new ApiResponse instance.</returns>
private ApiResponse<T> ToApiResponse<T>(RestResponse<T> response) private ApiResponse<T> ToApiResponse<T>(RestResponse<T> response)
{ {
T result = response.Data; T result = response.Data;
@ -429,31 +439,32 @@ namespace Org.OpenAPITools.Client
return transformed; return transformed;
} }
private ApiResponse<T> Exec<T>(RestRequest request, RequestOptions options, IReadableConfiguration configuration) /// <summary>
/// Executes the HTTP request for the current service.
/// Based on functions received it can be async or sync.
/// </summary>
/// <param name="getResponse">Local function that executes http request and returns http response.</param>
/// <param name="setOptions">Local function to specify options for the service.</param>
/// <param name="request">The RestSharp request object</param>
/// <param name="options">The RestSharp options object</param>
/// <param name="configuration">A per-request configuration object.
/// It is assumed that any merge with GlobalConfiguration has been done before calling this method.</param>
/// <returns>A new ApiResponse instance.</returns>
private ApiResponse<T> ExecClient<T>(Func<RestClient, RestResponse<T>> getResponse, Action<RestClientOptions> setOptions, RestRequest request, RequestOptions options, IReadableConfiguration configuration)
{ {
var baseUrl = configuration.GetOperationServerUrl(options.Operation, options.OperationIndex) ?? _baseUrl; var baseUrl = configuration.GetOperationServerUrl(options.Operation, options.OperationIndex) ?? _baseUrl;
var cookies = new CookieContainer();
if (options.Cookies != null && options.Cookies.Count > 0)
{
foreach (var cookie in options.Cookies)
{
cookies.Add(new Cookie(cookie.Name, cookie.Value));
}
}
var clientOptions = new RestClientOptions(baseUrl) var clientOptions = new RestClientOptions(baseUrl)
{ {
ClientCertificates = configuration.ClientCertificates, ClientCertificates = configuration.ClientCertificates,
CookieContainer = cookies,
MaxTimeout = configuration.Timeout, MaxTimeout = configuration.Timeout,
Proxy = configuration.Proxy, Proxy = configuration.Proxy,
UserAgent = configuration.UserAgent, UserAgent = configuration.UserAgent,
UseDefaultCredentials = configuration.UseDefaultCredentials, UseDefaultCredentials = configuration.UseDefaultCredentials,
RemoteCertificateValidationCallback = configuration.RemoteCertificateValidationCallback RemoteCertificateValidationCallback = configuration.RemoteCertificateValidationCallback
}; };
setOptions(clientOptions);
if (!string.IsNullOrEmpty(configuration.OAuthTokenUrl) && if (!string.IsNullOrEmpty(configuration.OAuthTokenUrl) &&
!string.IsNullOrEmpty(configuration.OAuthClientId) && !string.IsNullOrEmpty(configuration.OAuthClientId) &&
!string.IsNullOrEmpty(configuration.OAuthClientSecret) && !string.IsNullOrEmpty(configuration.OAuthClientSecret) &&
@ -473,27 +484,14 @@ namespace Org.OpenAPITools.Client
{ {
InterceptRequest(request); InterceptRequest(request);
RestResponse<T> response; RestResponse<T> response = getResponse(client);
if (RetryConfiguration.RetryPolicy != null)
{
var policy = RetryConfiguration.RetryPolicy;
var policyResult = policy.ExecuteAndCapture(() => client.Execute(request));
response = (policyResult.Outcome == OutcomeType.Successful) ? client.Deserialize<T>(policyResult.Result) : new RestResponse<T>(request)
{
ErrorException = policyResult.FinalException
};
}
else
{
response = client.Execute<T>(request);
}
// if the response type is oneOf/anyOf, call FromJSON to deserialize the data // if the response type is oneOf/anyOf, call FromJSON to deserialize the data
if (typeof(Org.OpenAPITools.Model.AbstractOpenAPISchema).IsAssignableFrom(typeof(T))) if (typeof(AbstractOpenAPISchema).IsAssignableFrom(typeof(T)))
{ {
try try
{ {
response.Data = (T) typeof(T).GetMethod("FromJson").Invoke(null, new object[] { response.Content }); response.Data = (T)typeof(T).GetMethod("FromJson").Invoke(null, new object[] { response.Content });
} }
catch (Exception ex) catch (Exception ex)
{ {
@ -551,104 +549,80 @@ namespace Org.OpenAPITools.Client
} }
} }
private async Task<ApiResponse<T>> ExecAsync<T>(RestRequest request, RequestOptions options, IReadableConfiguration configuration, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) private RestResponse<T> DeserializeRestResponseFromPolicy<T>(RestClient client, RestRequest request, PolicyResult<RestResponse> policyResult)
{ {
var baseUrl = configuration.GetOperationServerUrl(options.Operation, options.OperationIndex) ?? _baseUrl; if (policyResult.Outcome == OutcomeType.Successful)
var clientOptions = new RestClientOptions(baseUrl)
{ {
ClientCertificates = configuration.ClientCertificates, return client.Deserialize<T>(policyResult.Result);
MaxTimeout = configuration.Timeout, }
Proxy = configuration.Proxy, else
UserAgent = configuration.UserAgent, {
UseDefaultCredentials = configuration.UseDefaultCredentials, return new RestResponse<T>(request)
RemoteCertificateValidationCallback = configuration.RemoteCertificateValidationCallback {
ErrorException = policyResult.FinalException
};
}
}
private ApiResponse<T> Exec<T>(RestRequest request, RequestOptions options, IReadableConfiguration configuration)
{
Action<RestClientOptions> setOptions = (clientOptions) =>
{
var cookies = new CookieContainer();
if (options.Cookies != null && options.Cookies.Count > 0)
{
foreach (var cookie in options.Cookies)
{
cookies.Add(new Cookie(cookie.Name, cookie.Value));
}
}
clientOptions.CookieContainer = cookies;
}; };
if (!string.IsNullOrEmpty(configuration.OAuthTokenUrl) && Func<RestClient, RestResponse<T>> getResponse = (client) =>
!string.IsNullOrEmpty(configuration.OAuthClientId) &&
!string.IsNullOrEmpty(configuration.OAuthClientSecret) &&
configuration.OAuthFlow != null)
{ {
clientOptions.Authenticator = new OAuthAuthenticator( if (RetryConfiguration.RetryPolicy != null)
configuration.OAuthTokenUrl,
configuration.OAuthClientId,
configuration.OAuthClientSecret,
configuration.OAuthFlow,
SerializerSettings,
configuration);
}
using (RestClient client = new RestClient(clientOptions,
configureSerialization: serializerConfig => serializerConfig.UseSerializer(() => new CustomJsonCodec(SerializerSettings, configuration))))
{
InterceptRequest(request);
RestResponse<T> response;
if (RetryConfiguration.AsyncRetryPolicy != null)
{ {
var policy = RetryConfiguration.AsyncRetryPolicy; var policy = RetryConfiguration.RetryPolicy;
var policyResult = await policy.ExecuteAndCaptureAsync((ct) => client.ExecuteAsync(request, ct), cancellationToken).ConfigureAwait(false); var policyResult = policy.ExecuteAndCapture(() => client.Execute(request));
response = (policyResult.Outcome == OutcomeType.Successful) ? client.Deserialize<T>(policyResult.Result) : new RestResponse<T>(request) return DeserializeRestResponseFromPolicy<T>(client, request, policyResult);
{
ErrorException = policyResult.FinalException
};
} }
else else
{ {
response = await client.ExecuteAsync<T>(request, cancellationToken).ConfigureAwait(false); return client.Execute<T>(request);
} }
};
// if the response type is oneOf/anyOf, call FromJSON to deserialize the data return ExecClient(getResponse, setOptions, request, options, configuration);
if (typeof(Org.OpenAPITools.Model.AbstractOpenAPISchema).IsAssignableFrom(typeof(T))) }
{
response.Data = (T) typeof(T).GetMethod("FromJson").Invoke(null, new object[] { response.Content });
}
else if (typeof(T).Name == "Stream") // for binary response
{
response.Data = (T)(object)new MemoryStream(response.RawBytes);
}
else if (typeof(T).Name == "Byte[]") // for byte response
{
response.Data = (T)(object)response.RawBytes;
}
InterceptResponse(request, response); private Task<ApiResponse<T>> ExecAsync<T>(RestRequest request, RequestOptions options, IReadableConfiguration configuration, CancellationToken cancellationToken = default(CancellationToken))
{
Action<RestClientOptions> setOptions = (clientOptions) =>
{
//no extra options
};
var result = ToApiResponse(response); Func<RestClient, RestResponse<T>> getResponse = (client) =>
if (response.ErrorMessage != null) {
Func<Task<RestResponse<T>>> action = async () =>
{ {
result.ErrorText = response.ErrorMessage; if (RetryConfiguration.AsyncRetryPolicy != null)
}
if (response.Cookies != null && response.Cookies.Count > 0)
{
if (result.Cookies == null) result.Cookies = new List<Cookie>();
foreach (var restResponseCookie in response.Cookies.Cast<Cookie>())
{ {
var cookie = new Cookie( var policy = RetryConfiguration.AsyncRetryPolicy;
restResponseCookie.Name, var policyResult = await policy.ExecuteAndCaptureAsync((ct) => client.ExecuteAsync(request, ct), cancellationToken).ConfigureAwait(false);
restResponseCookie.Value, return DeserializeRestResponseFromPolicy<T>(client, request, policyResult);
restResponseCookie.Path,
restResponseCookie.Domain
)
{
Comment = restResponseCookie.Comment,
CommentUri = restResponseCookie.CommentUri,
Discard = restResponseCookie.Discard,
Expired = restResponseCookie.Expired,
Expires = restResponseCookie.Expires,
HttpOnly = restResponseCookie.HttpOnly,
Port = restResponseCookie.Port,
Secure = restResponseCookie.Secure,
Version = restResponseCookie.Version
};
result.Cookies.Add(cookie);
} }
} else
return result; {
} return await client.ExecuteAsync<T>(request, cancellationToken).ConfigureAwait(false);
}
};
return action().Result;
};
return Task.FromResult<ApiResponse<T>>(ExecClient(getResponse, setOptions, request, options, configuration));
} }
#region IAsynchronousClient #region IAsynchronousClient
@ -661,7 +635,7 @@ namespace Org.OpenAPITools.Client
/// GlobalConfiguration has been done before calling this method.</param> /// GlobalConfiguration has been done before calling this method.</param>
/// <param name="cancellationToken">Token that enables callers to cancel the request.</param> /// <param name="cancellationToken">Token that enables callers to cancel the request.</param>
/// <returns>A Task containing ApiResponse</returns> /// <returns>A Task containing ApiResponse</returns>
public Task<ApiResponse<T>> GetAsync<T>(string path, RequestOptions options, IReadableConfiguration configuration = null, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) public Task<ApiResponse<T>> GetAsync<T>(string path, RequestOptions options, IReadableConfiguration configuration = null, CancellationToken cancellationToken = default)
{ {
var config = configuration ?? GlobalConfiguration.Instance; var config = configuration ?? GlobalConfiguration.Instance;
return ExecAsync<T>(NewRequest(HttpMethod.Get, path, options, config), options, config, cancellationToken); return ExecAsync<T>(NewRequest(HttpMethod.Get, path, options, config), options, config, cancellationToken);
@ -676,7 +650,7 @@ namespace Org.OpenAPITools.Client
/// GlobalConfiguration has been done before calling this method.</param> /// GlobalConfiguration has been done before calling this method.</param>
/// <param name="cancellationToken">Token that enables callers to cancel the request.</param> /// <param name="cancellationToken">Token that enables callers to cancel the request.</param>
/// <returns>A Task containing ApiResponse</returns> /// <returns>A Task containing ApiResponse</returns>
public Task<ApiResponse<T>> PostAsync<T>(string path, RequestOptions options, IReadableConfiguration configuration = null, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) public Task<ApiResponse<T>> PostAsync<T>(string path, RequestOptions options, IReadableConfiguration configuration = null, CancellationToken cancellationToken = default)
{ {
var config = configuration ?? GlobalConfiguration.Instance; var config = configuration ?? GlobalConfiguration.Instance;
return ExecAsync<T>(NewRequest(HttpMethod.Post, path, options, config), options, config, cancellationToken); return ExecAsync<T>(NewRequest(HttpMethod.Post, path, options, config), options, config, cancellationToken);
@ -691,7 +665,7 @@ namespace Org.OpenAPITools.Client
/// GlobalConfiguration has been done before calling this method.</param> /// GlobalConfiguration has been done before calling this method.</param>
/// <param name="cancellationToken">Token that enables callers to cancel the request.</param> /// <param name="cancellationToken">Token that enables callers to cancel the request.</param>
/// <returns>A Task containing ApiResponse</returns> /// <returns>A Task containing ApiResponse</returns>
public Task<ApiResponse<T>> PutAsync<T>(string path, RequestOptions options, IReadableConfiguration configuration = null, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) public Task<ApiResponse<T>> PutAsync<T>(string path, RequestOptions options, IReadableConfiguration configuration = null, CancellationToken cancellationToken = default)
{ {
var config = configuration ?? GlobalConfiguration.Instance; var config = configuration ?? GlobalConfiguration.Instance;
return ExecAsync<T>(NewRequest(HttpMethod.Put, path, options, config), options, config, cancellationToken); return ExecAsync<T>(NewRequest(HttpMethod.Put, path, options, config), options, config, cancellationToken);
@ -706,7 +680,7 @@ namespace Org.OpenAPITools.Client
/// GlobalConfiguration has been done before calling this method.</param> /// GlobalConfiguration has been done before calling this method.</param>
/// <param name="cancellationToken">Token that enables callers to cancel the request.</param> /// <param name="cancellationToken">Token that enables callers to cancel the request.</param>
/// <returns>A Task containing ApiResponse</returns> /// <returns>A Task containing ApiResponse</returns>
public Task<ApiResponse<T>> DeleteAsync<T>(string path, RequestOptions options, IReadableConfiguration configuration = null, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) public Task<ApiResponse<T>> DeleteAsync<T>(string path, RequestOptions options, IReadableConfiguration configuration = null, CancellationToken cancellationToken = default)
{ {
var config = configuration ?? GlobalConfiguration.Instance; var config = configuration ?? GlobalConfiguration.Instance;
return ExecAsync<T>(NewRequest(HttpMethod.Delete, path, options, config), options, config, cancellationToken); return ExecAsync<T>(NewRequest(HttpMethod.Delete, path, options, config), options, config, cancellationToken);
@ -721,7 +695,7 @@ namespace Org.OpenAPITools.Client
/// GlobalConfiguration has been done before calling this method.</param> /// GlobalConfiguration has been done before calling this method.</param>
/// <param name="cancellationToken">Token that enables callers to cancel the request.</param> /// <param name="cancellationToken">Token that enables callers to cancel the request.</param>
/// <returns>A Task containing ApiResponse</returns> /// <returns>A Task containing ApiResponse</returns>
public Task<ApiResponse<T>> HeadAsync<T>(string path, RequestOptions options, IReadableConfiguration configuration = null, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) public Task<ApiResponse<T>> HeadAsync<T>(string path, RequestOptions options, IReadableConfiguration configuration = null, CancellationToken cancellationToken = default)
{ {
var config = configuration ?? GlobalConfiguration.Instance; var config = configuration ?? GlobalConfiguration.Instance;
return ExecAsync<T>(NewRequest(HttpMethod.Head, path, options, config), options, config, cancellationToken); return ExecAsync<T>(NewRequest(HttpMethod.Head, path, options, config), options, config, cancellationToken);
@ -736,7 +710,7 @@ namespace Org.OpenAPITools.Client
/// GlobalConfiguration has been done before calling this method.</param> /// GlobalConfiguration has been done before calling this method.</param>
/// <param name="cancellationToken">Token that enables callers to cancel the request.</param> /// <param name="cancellationToken">Token that enables callers to cancel the request.</param>
/// <returns>A Task containing ApiResponse</returns> /// <returns>A Task containing ApiResponse</returns>
public Task<ApiResponse<T>> OptionsAsync<T>(string path, RequestOptions options, IReadableConfiguration configuration = null, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) public Task<ApiResponse<T>> OptionsAsync<T>(string path, RequestOptions options, IReadableConfiguration configuration = null, CancellationToken cancellationToken = default)
{ {
var config = configuration ?? GlobalConfiguration.Instance; var config = configuration ?? GlobalConfiguration.Instance;
return ExecAsync<T>(NewRequest(HttpMethod.Options, path, options, config), options, config, cancellationToken); return ExecAsync<T>(NewRequest(HttpMethod.Options, path, options, config), options, config, cancellationToken);
@ -751,7 +725,7 @@ namespace Org.OpenAPITools.Client
/// GlobalConfiguration has been done before calling this method.</param> /// GlobalConfiguration has been done before calling this method.</param>
/// <param name="cancellationToken">Token that enables callers to cancel the request.</param> /// <param name="cancellationToken">Token that enables callers to cancel the request.</param>
/// <returns>A Task containing ApiResponse</returns> /// <returns>A Task containing ApiResponse</returns>
public Task<ApiResponse<T>> PatchAsync<T>(string path, RequestOptions options, IReadableConfiguration configuration = null, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) public Task<ApiResponse<T>> PatchAsync<T>(string path, RequestOptions options, IReadableConfiguration configuration = null, CancellationToken cancellationToken = default)
{ {
var config = configuration ?? GlobalConfiguration.Instance; var config = configuration ?? GlobalConfiguration.Instance;
return ExecAsync<T>(NewRequest(HttpMethod.Patch, path, options, config), options, config, cancellationToken); return ExecAsync<T>(NewRequest(HttpMethod.Patch, path, options, config), options, config, cancellationToken);

View File

@ -22,13 +22,16 @@ using System.Text;
using System.Threading; using System.Threading;
using System.Text.RegularExpressions; using System.Text.RegularExpressions;
using System.Threading.Tasks; using System.Threading.Tasks;
using System.Web;
using Newtonsoft.Json; using Newtonsoft.Json;
using Newtonsoft.Json.Serialization; using Newtonsoft.Json.Serialization;
using RestSharp; using RestSharp;
using RestSharp.Serializers; using RestSharp.Serializers;
using RestSharpMethod = RestSharp.Method; using RestSharpMethod = RestSharp.Method;
using FileIO = System.IO.File;
using Polly; using Polly;
using Org.OpenAPITools.Client.Auth; using Org.OpenAPITools.Client.Auth;
using Org.OpenAPITools.Model;
namespace Org.OpenAPITools.Client namespace Org.OpenAPITools.Client
{ {
@ -69,10 +72,10 @@ namespace Org.OpenAPITools.Client
/// <returns>A JSON string.</returns> /// <returns>A JSON string.</returns>
public string Serialize(object obj) public string Serialize(object obj)
{ {
if (obj != null && obj is Org.OpenAPITools.Model.AbstractOpenAPISchema) if (obj != null && obj is AbstractOpenAPISchema)
{ {
// the object to be serialized is an oneOf/anyOf schema // the object to be serialized is an oneOf/anyOf schema
return ((Org.OpenAPITools.Model.AbstractOpenAPISchema)obj).ToJson(); return ((AbstractOpenAPISchema)obj).ToJson();
} }
else else
{ {
@ -117,7 +120,7 @@ namespace Org.OpenAPITools.Client
if (match.Success) if (match.Success)
{ {
string fileName = filePath + ClientUtils.SanitizeFilename(match.Groups[1].Value.Replace("\"", "").Replace("'", "")); string fileName = filePath + ClientUtils.SanitizeFilename(match.Groups[1].Value.Replace("\"", "").Replace("'", ""));
File.WriteAllBytes(fileName, bytes); FileIO.WriteAllBytes(fileName, bytes);
return new FileStream(fileName, FileMode.Open); return new FileStream(fileName, FileMode.Open);
} }
} }
@ -128,7 +131,7 @@ namespace Org.OpenAPITools.Client
if (type.Name.StartsWith("System.Nullable`1[[System.DateTime")) // return a datetime object if (type.Name.StartsWith("System.Nullable`1[[System.DateTime")) // return a datetime object
{ {
return DateTime.Parse(response.Content, null, System.Globalization.DateTimeStyles.RoundtripKind); return DateTime.Parse(response.Content, null, DateTimeStyles.RoundtripKind);
} }
if (type == typeof(string) || type.Name.StartsWith("System.Nullable")) // return primitive type if (type == typeof(string) || type.Name.StartsWith("System.Nullable")) // return primitive type
@ -150,13 +153,13 @@ namespace Org.OpenAPITools.Client
public ISerializer Serializer => this; public ISerializer Serializer => this;
public IDeserializer Deserializer => this; public IDeserializer Deserializer => this;
public string[] AcceptedContentTypes => RestSharp.ContentType.JsonAccept; public string[] AcceptedContentTypes => ContentType.JsonAccept;
public SupportsContentType SupportsContentType => contentType => public SupportsContentType SupportsContentType => contentType =>
contentType.Value.EndsWith("json", StringComparison.InvariantCultureIgnoreCase) || contentType.Value.EndsWith("json", StringComparison.InvariantCultureIgnoreCase) ||
contentType.Value.EndsWith("javascript", StringComparison.InvariantCultureIgnoreCase); contentType.Value.EndsWith("javascript", StringComparison.InvariantCultureIgnoreCase);
public ContentType ContentType { get; set; } = RestSharp.ContentType.Json; public ContentType ContentType { get; set; } = ContentType.Json;
public DataFormat DataFormat => DataFormat.Json; public DataFormat DataFormat => DataFormat.Json;
} }
@ -203,7 +206,7 @@ namespace Org.OpenAPITools.Client
/// </summary> /// </summary>
public ApiClient() public ApiClient()
{ {
_baseUrl = Org.OpenAPITools.Client.GlobalConfiguration.Instance.BasePath; _baseUrl = GlobalConfiguration.Instance.BasePath;
} }
/// <summary> /// <summary>
@ -260,14 +263,14 @@ namespace Org.OpenAPITools.Client
/// <summary> /// <summary>
/// Provides all logic for constructing a new RestSharp <see cref="RestRequest"/>. /// Provides all logic for constructing a new RestSharp <see cref="RestRequest"/>.
/// At this point, all information for querying the service is known. Here, it is simply /// At this point, all information for querying the service is known.
/// mapped into the RestSharp request. /// Here, it is simply mapped into the RestSharp request.
/// </summary> /// </summary>
/// <param name="method">The http verb.</param> /// <param name="method">The http verb.</param>
/// <param name="path">The target path (or resource).</param> /// <param name="path">The target path (or resource).</param>
/// <param name="options">The additional request options.</param> /// <param name="options">The additional request options.</param>
/// <param name="configuration">A per-request configuration object. It is assumed that any merge with /// <param name="configuration">A per-request configuration object.
/// GlobalConfiguration has been done before calling this method.</param> /// It is assumed that any merge with GlobalConfiguration has been done before calling this method.</param>
/// <returns>[private] A new RestRequest instance.</returns> /// <returns>[private] A new RestRequest instance.</returns>
/// <exception cref="ArgumentNullException"></exception> /// <exception cref="ArgumentNullException"></exception>
private RestRequest NewRequest( private RestRequest NewRequest(
@ -375,7 +378,7 @@ namespace Org.OpenAPITools.Client
var bytes = ClientUtils.ReadAsBytes(file); var bytes = ClientUtils.ReadAsBytes(file);
var fileStream = file as FileStream; var fileStream = file as FileStream;
if (fileStream != null) if (fileStream != null)
request.AddFile(fileParam.Key, bytes, System.IO.Path.GetFileName(fileStream.Name)); request.AddFile(fileParam.Key, bytes, Path.GetFileName(fileStream.Name));
else else
request.AddFile(fileParam.Key, bytes, "no_file_name_provided"); request.AddFile(fileParam.Key, bytes, "no_file_name_provided");
} }
@ -385,6 +388,13 @@ namespace Org.OpenAPITools.Client
return request; return request;
} }
/// <summary>
/// Transforms a RestResponse instance into a new ApiResponse instance.
/// At this point, we have a concrete http response from the service.
/// Here, it is simply mapped into the [public] ApiResponse object.
/// </summary>
/// <param name="response">The RestSharp response object</param>
/// <returns>A new ApiResponse instance.</returns>
private ApiResponse<T> ToApiResponse<T>(RestResponse<T> response) private ApiResponse<T> ToApiResponse<T>(RestResponse<T> response)
{ {
T result = response.Data; T result = response.Data;
@ -429,31 +439,32 @@ namespace Org.OpenAPITools.Client
return transformed; return transformed;
} }
private ApiResponse<T> Exec<T>(RestRequest request, RequestOptions options, IReadableConfiguration configuration) /// <summary>
/// Executes the HTTP request for the current service.
/// Based on functions received it can be async or sync.
/// </summary>
/// <param name="getResponse">Local function that executes http request and returns http response.</param>
/// <param name="setOptions">Local function to specify options for the service.</param>
/// <param name="request">The RestSharp request object</param>
/// <param name="options">The RestSharp options object</param>
/// <param name="configuration">A per-request configuration object.
/// It is assumed that any merge with GlobalConfiguration has been done before calling this method.</param>
/// <returns>A new ApiResponse instance.</returns>
private ApiResponse<T> ExecClient<T>(Func<RestClient, RestResponse<T>> getResponse, Action<RestClientOptions> setOptions, RestRequest request, RequestOptions options, IReadableConfiguration configuration)
{ {
var baseUrl = configuration.GetOperationServerUrl(options.Operation, options.OperationIndex) ?? _baseUrl; var baseUrl = configuration.GetOperationServerUrl(options.Operation, options.OperationIndex) ?? _baseUrl;
var cookies = new CookieContainer();
if (options.Cookies != null && options.Cookies.Count > 0)
{
foreach (var cookie in options.Cookies)
{
cookies.Add(new Cookie(cookie.Name, cookie.Value));
}
}
var clientOptions = new RestClientOptions(baseUrl) var clientOptions = new RestClientOptions(baseUrl)
{ {
ClientCertificates = configuration.ClientCertificates, ClientCertificates = configuration.ClientCertificates,
CookieContainer = cookies,
MaxTimeout = configuration.Timeout, MaxTimeout = configuration.Timeout,
Proxy = configuration.Proxy, Proxy = configuration.Proxy,
UserAgent = configuration.UserAgent, UserAgent = configuration.UserAgent,
UseDefaultCredentials = configuration.UseDefaultCredentials, UseDefaultCredentials = configuration.UseDefaultCredentials,
RemoteCertificateValidationCallback = configuration.RemoteCertificateValidationCallback RemoteCertificateValidationCallback = configuration.RemoteCertificateValidationCallback
}; };
setOptions(clientOptions);
if (!string.IsNullOrEmpty(configuration.OAuthTokenUrl) && if (!string.IsNullOrEmpty(configuration.OAuthTokenUrl) &&
!string.IsNullOrEmpty(configuration.OAuthClientId) && !string.IsNullOrEmpty(configuration.OAuthClientId) &&
!string.IsNullOrEmpty(configuration.OAuthClientSecret) && !string.IsNullOrEmpty(configuration.OAuthClientSecret) &&
@ -473,27 +484,14 @@ namespace Org.OpenAPITools.Client
{ {
InterceptRequest(request); InterceptRequest(request);
RestResponse<T> response; RestResponse<T> response = getResponse(client);
if (RetryConfiguration.RetryPolicy != null)
{
var policy = RetryConfiguration.RetryPolicy;
var policyResult = policy.ExecuteAndCapture(() => client.Execute(request));
response = (policyResult.Outcome == OutcomeType.Successful) ? client.Deserialize<T>(policyResult.Result) : new RestResponse<T>(request)
{
ErrorException = policyResult.FinalException
};
}
else
{
response = client.Execute<T>(request);
}
// if the response type is oneOf/anyOf, call FromJSON to deserialize the data // if the response type is oneOf/anyOf, call FromJSON to deserialize the data
if (typeof(Org.OpenAPITools.Model.AbstractOpenAPISchema).IsAssignableFrom(typeof(T))) if (typeof(AbstractOpenAPISchema).IsAssignableFrom(typeof(T)))
{ {
try try
{ {
response.Data = (T) typeof(T).GetMethod("FromJson").Invoke(null, new object[] { response.Content }); response.Data = (T)typeof(T).GetMethod("FromJson").Invoke(null, new object[] { response.Content });
} }
catch (Exception ex) catch (Exception ex)
{ {
@ -551,104 +549,80 @@ namespace Org.OpenAPITools.Client
} }
} }
private async Task<ApiResponse<T>> ExecAsync<T>(RestRequest request, RequestOptions options, IReadableConfiguration configuration, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) private RestResponse<T> DeserializeRestResponseFromPolicy<T>(RestClient client, RestRequest request, PolicyResult<RestResponse> policyResult)
{ {
var baseUrl = configuration.GetOperationServerUrl(options.Operation, options.OperationIndex) ?? _baseUrl; if (policyResult.Outcome == OutcomeType.Successful)
var clientOptions = new RestClientOptions(baseUrl)
{ {
ClientCertificates = configuration.ClientCertificates, return client.Deserialize<T>(policyResult.Result);
MaxTimeout = configuration.Timeout, }
Proxy = configuration.Proxy, else
UserAgent = configuration.UserAgent, {
UseDefaultCredentials = configuration.UseDefaultCredentials, return new RestResponse<T>(request)
RemoteCertificateValidationCallback = configuration.RemoteCertificateValidationCallback {
ErrorException = policyResult.FinalException
};
}
}
private ApiResponse<T> Exec<T>(RestRequest request, RequestOptions options, IReadableConfiguration configuration)
{
Action<RestClientOptions> setOptions = (clientOptions) =>
{
var cookies = new CookieContainer();
if (options.Cookies != null && options.Cookies.Count > 0)
{
foreach (var cookie in options.Cookies)
{
cookies.Add(new Cookie(cookie.Name, cookie.Value));
}
}
clientOptions.CookieContainer = cookies;
}; };
if (!string.IsNullOrEmpty(configuration.OAuthTokenUrl) && Func<RestClient, RestResponse<T>> getResponse = (client) =>
!string.IsNullOrEmpty(configuration.OAuthClientId) &&
!string.IsNullOrEmpty(configuration.OAuthClientSecret) &&
configuration.OAuthFlow != null)
{ {
clientOptions.Authenticator = new OAuthAuthenticator( if (RetryConfiguration.RetryPolicy != null)
configuration.OAuthTokenUrl,
configuration.OAuthClientId,
configuration.OAuthClientSecret,
configuration.OAuthFlow,
SerializerSettings,
configuration);
}
using (RestClient client = new RestClient(clientOptions,
configureSerialization: serializerConfig => serializerConfig.UseSerializer(() => new CustomJsonCodec(SerializerSettings, configuration))))
{
InterceptRequest(request);
RestResponse<T> response;
if (RetryConfiguration.AsyncRetryPolicy != null)
{ {
var policy = RetryConfiguration.AsyncRetryPolicy; var policy = RetryConfiguration.RetryPolicy;
var policyResult = await policy.ExecuteAndCaptureAsync((ct) => client.ExecuteAsync(request, ct), cancellationToken).ConfigureAwait(false); var policyResult = policy.ExecuteAndCapture(() => client.Execute(request));
response = (policyResult.Outcome == OutcomeType.Successful) ? client.Deserialize<T>(policyResult.Result) : new RestResponse<T>(request) return DeserializeRestResponseFromPolicy<T>(client, request, policyResult);
{
ErrorException = policyResult.FinalException
};
} }
else else
{ {
response = await client.ExecuteAsync<T>(request, cancellationToken).ConfigureAwait(false); return client.Execute<T>(request);
} }
};
// if the response type is oneOf/anyOf, call FromJSON to deserialize the data return ExecClient(getResponse, setOptions, request, options, configuration);
if (typeof(Org.OpenAPITools.Model.AbstractOpenAPISchema).IsAssignableFrom(typeof(T))) }
{
response.Data = (T) typeof(T).GetMethod("FromJson").Invoke(null, new object[] { response.Content });
}
else if (typeof(T).Name == "Stream") // for binary response
{
response.Data = (T)(object)new MemoryStream(response.RawBytes);
}
else if (typeof(T).Name == "Byte[]") // for byte response
{
response.Data = (T)(object)response.RawBytes;
}
InterceptResponse(request, response); private Task<ApiResponse<T>> ExecAsync<T>(RestRequest request, RequestOptions options, IReadableConfiguration configuration, CancellationToken cancellationToken = default(CancellationToken))
{
Action<RestClientOptions> setOptions = (clientOptions) =>
{
//no extra options
};
var result = ToApiResponse(response); Func<RestClient, RestResponse<T>> getResponse = (client) =>
if (response.ErrorMessage != null) {
Func<Task<RestResponse<T>>> action = async () =>
{ {
result.ErrorText = response.ErrorMessage; if (RetryConfiguration.AsyncRetryPolicy != null)
}
if (response.Cookies != null && response.Cookies.Count > 0)
{
if (result.Cookies == null) result.Cookies = new List<Cookie>();
foreach (var restResponseCookie in response.Cookies.Cast<Cookie>())
{ {
var cookie = new Cookie( var policy = RetryConfiguration.AsyncRetryPolicy;
restResponseCookie.Name, var policyResult = await policy.ExecuteAndCaptureAsync((ct) => client.ExecuteAsync(request, ct), cancellationToken).ConfigureAwait(false);
restResponseCookie.Value, return DeserializeRestResponseFromPolicy<T>(client, request, policyResult);
restResponseCookie.Path,
restResponseCookie.Domain
)
{
Comment = restResponseCookie.Comment,
CommentUri = restResponseCookie.CommentUri,
Discard = restResponseCookie.Discard,
Expired = restResponseCookie.Expired,
Expires = restResponseCookie.Expires,
HttpOnly = restResponseCookie.HttpOnly,
Port = restResponseCookie.Port,
Secure = restResponseCookie.Secure,
Version = restResponseCookie.Version
};
result.Cookies.Add(cookie);
} }
} else
return result; {
} return await client.ExecuteAsync<T>(request, cancellationToken).ConfigureAwait(false);
}
};
return action().Result;
};
return Task.FromResult<ApiResponse<T>>(ExecClient(getResponse, setOptions, request, options, configuration));
} }
#region IAsynchronousClient #region IAsynchronousClient
@ -661,7 +635,7 @@ namespace Org.OpenAPITools.Client
/// GlobalConfiguration has been done before calling this method.</param> /// GlobalConfiguration has been done before calling this method.</param>
/// <param name="cancellationToken">Token that enables callers to cancel the request.</param> /// <param name="cancellationToken">Token that enables callers to cancel the request.</param>
/// <returns>A Task containing ApiResponse</returns> /// <returns>A Task containing ApiResponse</returns>
public Task<ApiResponse<T>> GetAsync<T>(string path, RequestOptions options, IReadableConfiguration configuration = null, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) public Task<ApiResponse<T>> GetAsync<T>(string path, RequestOptions options, IReadableConfiguration configuration = null, CancellationToken cancellationToken = default)
{ {
var config = configuration ?? GlobalConfiguration.Instance; var config = configuration ?? GlobalConfiguration.Instance;
return ExecAsync<T>(NewRequest(HttpMethod.Get, path, options, config), options, config, cancellationToken); return ExecAsync<T>(NewRequest(HttpMethod.Get, path, options, config), options, config, cancellationToken);
@ -676,7 +650,7 @@ namespace Org.OpenAPITools.Client
/// GlobalConfiguration has been done before calling this method.</param> /// GlobalConfiguration has been done before calling this method.</param>
/// <param name="cancellationToken">Token that enables callers to cancel the request.</param> /// <param name="cancellationToken">Token that enables callers to cancel the request.</param>
/// <returns>A Task containing ApiResponse</returns> /// <returns>A Task containing ApiResponse</returns>
public Task<ApiResponse<T>> PostAsync<T>(string path, RequestOptions options, IReadableConfiguration configuration = null, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) public Task<ApiResponse<T>> PostAsync<T>(string path, RequestOptions options, IReadableConfiguration configuration = null, CancellationToken cancellationToken = default)
{ {
var config = configuration ?? GlobalConfiguration.Instance; var config = configuration ?? GlobalConfiguration.Instance;
return ExecAsync<T>(NewRequest(HttpMethod.Post, path, options, config), options, config, cancellationToken); return ExecAsync<T>(NewRequest(HttpMethod.Post, path, options, config), options, config, cancellationToken);
@ -691,7 +665,7 @@ namespace Org.OpenAPITools.Client
/// GlobalConfiguration has been done before calling this method.</param> /// GlobalConfiguration has been done before calling this method.</param>
/// <param name="cancellationToken">Token that enables callers to cancel the request.</param> /// <param name="cancellationToken">Token that enables callers to cancel the request.</param>
/// <returns>A Task containing ApiResponse</returns> /// <returns>A Task containing ApiResponse</returns>
public Task<ApiResponse<T>> PutAsync<T>(string path, RequestOptions options, IReadableConfiguration configuration = null, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) public Task<ApiResponse<T>> PutAsync<T>(string path, RequestOptions options, IReadableConfiguration configuration = null, CancellationToken cancellationToken = default)
{ {
var config = configuration ?? GlobalConfiguration.Instance; var config = configuration ?? GlobalConfiguration.Instance;
return ExecAsync<T>(NewRequest(HttpMethod.Put, path, options, config), options, config, cancellationToken); return ExecAsync<T>(NewRequest(HttpMethod.Put, path, options, config), options, config, cancellationToken);
@ -706,7 +680,7 @@ namespace Org.OpenAPITools.Client
/// GlobalConfiguration has been done before calling this method.</param> /// GlobalConfiguration has been done before calling this method.</param>
/// <param name="cancellationToken">Token that enables callers to cancel the request.</param> /// <param name="cancellationToken">Token that enables callers to cancel the request.</param>
/// <returns>A Task containing ApiResponse</returns> /// <returns>A Task containing ApiResponse</returns>
public Task<ApiResponse<T>> DeleteAsync<T>(string path, RequestOptions options, IReadableConfiguration configuration = null, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) public Task<ApiResponse<T>> DeleteAsync<T>(string path, RequestOptions options, IReadableConfiguration configuration = null, CancellationToken cancellationToken = default)
{ {
var config = configuration ?? GlobalConfiguration.Instance; var config = configuration ?? GlobalConfiguration.Instance;
return ExecAsync<T>(NewRequest(HttpMethod.Delete, path, options, config), options, config, cancellationToken); return ExecAsync<T>(NewRequest(HttpMethod.Delete, path, options, config), options, config, cancellationToken);
@ -721,7 +695,7 @@ namespace Org.OpenAPITools.Client
/// GlobalConfiguration has been done before calling this method.</param> /// GlobalConfiguration has been done before calling this method.</param>
/// <param name="cancellationToken">Token that enables callers to cancel the request.</param> /// <param name="cancellationToken">Token that enables callers to cancel the request.</param>
/// <returns>A Task containing ApiResponse</returns> /// <returns>A Task containing ApiResponse</returns>
public Task<ApiResponse<T>> HeadAsync<T>(string path, RequestOptions options, IReadableConfiguration configuration = null, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) public Task<ApiResponse<T>> HeadAsync<T>(string path, RequestOptions options, IReadableConfiguration configuration = null, CancellationToken cancellationToken = default)
{ {
var config = configuration ?? GlobalConfiguration.Instance; var config = configuration ?? GlobalConfiguration.Instance;
return ExecAsync<T>(NewRequest(HttpMethod.Head, path, options, config), options, config, cancellationToken); return ExecAsync<T>(NewRequest(HttpMethod.Head, path, options, config), options, config, cancellationToken);
@ -736,7 +710,7 @@ namespace Org.OpenAPITools.Client
/// GlobalConfiguration has been done before calling this method.</param> /// GlobalConfiguration has been done before calling this method.</param>
/// <param name="cancellationToken">Token that enables callers to cancel the request.</param> /// <param name="cancellationToken">Token that enables callers to cancel the request.</param>
/// <returns>A Task containing ApiResponse</returns> /// <returns>A Task containing ApiResponse</returns>
public Task<ApiResponse<T>> OptionsAsync<T>(string path, RequestOptions options, IReadableConfiguration configuration = null, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) public Task<ApiResponse<T>> OptionsAsync<T>(string path, RequestOptions options, IReadableConfiguration configuration = null, CancellationToken cancellationToken = default)
{ {
var config = configuration ?? GlobalConfiguration.Instance; var config = configuration ?? GlobalConfiguration.Instance;
return ExecAsync<T>(NewRequest(HttpMethod.Options, path, options, config), options, config, cancellationToken); return ExecAsync<T>(NewRequest(HttpMethod.Options, path, options, config), options, config, cancellationToken);
@ -751,7 +725,7 @@ namespace Org.OpenAPITools.Client
/// GlobalConfiguration has been done before calling this method.</param> /// GlobalConfiguration has been done before calling this method.</param>
/// <param name="cancellationToken">Token that enables callers to cancel the request.</param> /// <param name="cancellationToken">Token that enables callers to cancel the request.</param>
/// <returns>A Task containing ApiResponse</returns> /// <returns>A Task containing ApiResponse</returns>
public Task<ApiResponse<T>> PatchAsync<T>(string path, RequestOptions options, IReadableConfiguration configuration = null, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) public Task<ApiResponse<T>> PatchAsync<T>(string path, RequestOptions options, IReadableConfiguration configuration = null, CancellationToken cancellationToken = default)
{ {
var config = configuration ?? GlobalConfiguration.Instance; var config = configuration ?? GlobalConfiguration.Instance;
return ExecAsync<T>(NewRequest(HttpMethod.Patch, path, options, config), options, config, cancellationToken); return ExecAsync<T>(NewRequest(HttpMethod.Patch, path, options, config), options, config, cancellationToken);