All Downloads are FREE. Search and download functionalities are using the official Maven repository.

csharp-netcore.ApiClient.mustache Maven / Gradle / Ivy

There is a newer version: 7.8.0
Show newest version
{{>partial_header}}

using System;
using System.Collections;
using System.Collections.Generic;
using System.Globalization;
using System.Text.RegularExpressions;
using System.Threading.Tasks;
using System.IO;
{{^netStandard}}
{{^supportsUWP}}
using System.Web;
{{/supportsUWP}}
{{/netStandard}}
using System.Linq;
using System.Net;
using System.Runtime.Serialization;
using System.Runtime.Serialization.Formatters;
using System.Text;
using Newtonsoft.Json;
using Newtonsoft.Json.Serialization;
using RestSharp;
using RestSharp.Deserializers;
using ErrorEventArgs = Newtonsoft.Json.Serialization.ErrorEventArgs;
using RestSharpMethod = RestSharp.Method;

namespace {{packageName}}.Client
{
    /// 
    /// Allows RestSharp to Serialize/Deserialize JSON using our custom logic, but only when ContentType is JSON. 
    /// 
    internal class CustomJsonCodec : RestSharp.Serializers.ISerializer, RestSharp.Deserializers.IDeserializer
    {
        private readonly IReadableConfiguration _configuration;
        private readonly JsonSerializer _serializer;
        private string _contentType = "application/json";
        private readonly JsonSerializerSettings _serializerSettings = new JsonSerializerSettings
        {
            // OpenAPI generated types generally hide default constructors.
            ConstructorHandling = ConstructorHandling.AllowNonPublicDefaultConstructor,
                ContractResolver = new DefaultContractResolver
                {
                    NamingStrategy = new CamelCaseNamingStrategy()
                    {
                        OverrideSpecifiedNames = true
                    }
                }
        };

        public CustomJsonCodec(IReadableConfiguration configuration)
        {
            _configuration = configuration;
            _serializer = JsonSerializer.Create(_serializerSettings);
        }

        public CustomJsonCodec(JsonSerializerSettings serializerSettings, IReadableConfiguration configuration)
        {
            _serializerSettings = serializerSettings;
            _serializer = JsonSerializer.Create(_serializerSettings);
            _configuration = configuration;
        }

        public string Serialize(object obj)
        {
            String result = JsonConvert.SerializeObject(obj, _serializerSettings);
            return result;
        }

        public T Deserialize(IRestResponse response)
        {
            var result = (T) Deserialize(response, typeof(T));
            return result;
        }

        /// 
        /// Deserialize the JSON string into a proper object.
        /// 
        /// The HTTP response.
        /// Object type.
        /// Object representation of the JSON string.
        internal object Deserialize(IRestResponse response, Type type)
        {
            IList headers = response.Headers;
            if (type == typeof(byte[])) // return byte array
            {
                return response.RawBytes;
            }

            // TODO: ? if (type.IsAssignableFrom(typeof(Stream)))
            if (type == typeof(Stream))
            {
                if (headers != null)
                {
                    var filePath = String.IsNullOrEmpty(_configuration.TempFolderPath)
                        ? Path.GetTempPath()
                        : _configuration.TempFolderPath;
                    var regex = new Regex(@"Content-Disposition=.*filename=['""]?([^'""\s]+)['""]?$");
                    foreach (var header in headers)
                    {
                        var match = regex.Match(header.ToString());
                        if (match.Success)
                        {
                            string fileName = filePath + ClientUtils.SanitizeFilename(match.Groups[1].Value.Replace("\"", "").Replace("'", ""));
                            File.WriteAllBytes(fileName, response.RawBytes);
                            return new FileStream(fileName, FileMode.Open);
                        }
                    }
                }
                var stream = new MemoryStream(response.RawBytes);
                return stream;
            }

            if (type.Name.StartsWith("System.Nullable`1[[System.DateTime")) // return a datetime object
            {
                return DateTime.Parse(response.Content,  null, System.Globalization.DateTimeStyles.RoundtripKind);
            }

            if (type == typeof(String) || type.Name.StartsWith("System.Nullable")) // return primitive type
            {
                return ClientUtils.ConvertType(response.Content, type);
            }

            // at this point, it must be a model (json)
            try
            {
                return JsonConvert.DeserializeObject(response.Content, type, _serializerSettings);
            }
            catch (Exception e)
            {
                throw new ApiException(500, e.Message);
            }
        }

        public string RootElement { get; set; }
        public string Namespace { get; set; }
        public string DateFormat { get; set; }

        public string ContentType
        {
            get { return _contentType; }
            set { throw new InvalidOperationException("Not allowed to set content type."); }
        }
    }
    {{! NOTE: Any changes related to RestSharp should be done in this class. All other client classes are for extensibility by consumers.}}
    /// 
    /// Provides a default implementation of an Api client (both synchronous and asynchronous implementatios),
    /// encapsulating general REST accessor use cases.
    /// 
    {{>visibility}} partial class ApiClient : ISynchronousClient{{#supportsAsync}}, IAsynchronousClient{{/supportsAsync}}
    {
        private readonly String _baseUrl;

        /// 
        /// Allows for extending request processing for  generated code.
        /// 
        /// The RestSharp request object
        partial void InterceptRequest(IRestRequest request);

        /// 
        /// Allows for extending response processing for  generated code.
        /// 
        /// The RestSharp request object
        /// The RestSharp response object
        partial void InterceptResponse(IRestRequest request, IRestResponse response);

        /// 
        /// Initializes a new instance of the , defaulting to the global configurations' base url.
        /// 
        public ApiClient()
        {
            _baseUrl = {{packageName}}.Client.GlobalConfiguration.Instance.BasePath;
        }

        /// 
        /// Initializes a new instance of the 
        /// 
        /// The target service's base path in URL format.
        /// 
        public ApiClient(String basePath)
        {
           if (String.IsNullOrEmpty(basePath))
                throw new ArgumentException("basePath cannot be empty");

            _baseUrl = basePath;
        }

        /// 
        /// Constructs the RestSharp version of an http method
        /// 
        /// Swagger Client Custom HttpMethod
        /// RestSharp's HttpMethod instance.
        /// 
        private RestSharpMethod Method(HttpMethod method)
        {
            RestSharpMethod other;
            switch (method)
            {
                case HttpMethod.Get:
                    other = RestSharpMethod.GET;
                    break;
                case HttpMethod.Post:
                    other = RestSharpMethod.POST;
                    break;
                case HttpMethod.Put:
                    other = RestSharpMethod.PUT;
                    break;
                case HttpMethod.Delete:
                    other = RestSharpMethod.DELETE;
                    break;
                case HttpMethod.Head:
                    other = RestSharpMethod.HEAD;
                    break;
                case HttpMethod.Options:
                    other = RestSharpMethod.OPTIONS;
                    break;
                case HttpMethod.Patch:
                    other = RestSharpMethod.PATCH;
                    break;
                default:
                    throw new ArgumentOutOfRangeException("method", method, null);
            }

            return other;
        }

        /// 
        /// Provides all logic for constructing a new RestSharp .
        /// At this point, all information for querying the service is known. Here, it is simply
        /// mapped into the RestSharp request.
        /// 
        /// The http verb.
        /// The target path (or resource).
        /// The additional request options.
        /// A per-request configuration object. It is assumed that any merge with
        /// GlobalConfiguration has been done before calling this method.
        /// [private] A new RestRequest instance.
        /// 
        private RestRequest newRequest(
            HttpMethod method,
            String path,
            RequestOptions options,
            IReadableConfiguration configuration)
        {
            if (path == null) throw new ArgumentNullException("path");
            if (options == null) throw new ArgumentNullException("options");
            if (configuration == null) throw new ArgumentNullException("configuration");
            
            RestRequest request = new RestRequest(Method(method))
            {
                Resource = path,
                JsonSerializer = new CustomJsonCodec(configuration)
            };

            if (options.PathParameters != null)
            {
                foreach (var pathParam in options.PathParameters)
                {
                    request.AddParameter(pathParam.Key, pathParam.Value, ParameterType.UrlSegment);
                }
            }
            
            if (options.QueryParameters != null)
            {
                foreach (var queryParam in options.QueryParameters)
                {
                    foreach (var value in queryParam.Value)
                    {
                        request.AddQueryParameter(queryParam.Key, value);
                    }
                }
            }

            if (options.HeaderParameters != null)
            {
                foreach (var headerParam in options.HeaderParameters)
                {
                    foreach (var value in headerParam.Value)
                    {
                        request.AddHeader(headerParam.Key, value);
                    }
                }
            }

            if (options.FormParameters != null)
            {
                foreach (var formParam in options.FormParameters)
                {
                    request.AddParameter(formParam.Key, formParam.Value);
                }
            }

            if (options.Data != null)
            {
                if (options.HeaderParameters != null)
                {
                    var contentTypes = options.HeaderParameters["Content-Type"];
                    if (contentTypes == null || contentTypes.Any(header => header.Contains("application/json")))
                    {
                        request.RequestFormat = DataFormat.Json;
                    }
                    else
                    {
                        // TODO: Generated client user should add additional handlers. RestSharp only supports XML and JSON, with XML as default.
                    }
                }
                else
                {
                    // Here, we'll assume JSON APIs are more common. XML can be forced by adding produces/consumes to openapi spec explicitly.
                    request.RequestFormat = DataFormat.Json;
                }

                request.AddBody(options.Data);
            }

            if (options.FileParameters != null)
            {
                foreach (var fileParam in options.FileParameters)
                {
                    var bytes = ClientUtils.ReadAsBytes(fileParam.Value);
                    var fileStream = fileParam.Value as FileStream;
                    if (fileStream != null)
                        FileParameter.Create(fileParam.Key, bytes, System.IO.Path.GetFileName(fileStream.Name));
                    else
                        FileParameter.Create(fileParam.Key, bytes, "no_file_name_provided");
                }
            }

            if (options.Cookies != null && options.Cookies.Count > 0)
            {
                foreach (var cookie in options.Cookies)
                {
                    request.AddCookie(cookie.Name, cookie.Value);
                }
            }
            
            return request;
        }

        private ApiResponse toApiResponse({{#supportsAsync}}IRestResponse response{{/supportsAsync}}{{^supportsAsync}}IRestResponse response, CustomJsonCodec des{{/supportsAsync}})
        {
            T result = {{#supportsAsync}}response.Data{{/supportsAsync}}{{^supportsAsync}}(T)des.Deserialize(response, typeof(T)){{/supportsAsync}};
            var transformed = new ApiResponse(response.StatusCode, new Multimap(), result)
            {
                ErrorText = response.ErrorMessage,
                Cookies = new List()
            };
            
            if (response.Headers != null)
            {
                foreach (var responseHeader in response.Headers)
                {
                    transformed.Headers.Add(responseHeader.Name, ClientUtils.ParameterToString(responseHeader.Value));
                }
            }

            if (response.Cookies != null)
            {
                foreach (var responseCookies in response.Cookies)
                {
                    transformed.Cookies.Add(
                        new Cookie(
                            responseCookies.Name, 
                            responseCookies.Value, 
                            responseCookies.Path, 
                            responseCookies.Domain)
                        );
                }
            }

            return transformed;
        }

        private {{#supportsAsync}}async Task>{{/supportsAsync}}{{^supportsAsync}}ApiResponse{{/supportsAsync}} Exec(RestRequest req, IReadableConfiguration configuration)
        {
            RestClient client = new RestClient(_baseUrl);

            client.ClearHandlers();
            var existingDeserializer = req.JsonSerializer as IDeserializer;
            if (existingDeserializer != null)
            {
                client.AddHandler(existingDeserializer, "application/json", "text/json", "text/x-json", "text/javascript", "*+json");
            }
            else
            {
                var codec = new CustomJsonCodec(configuration);
                client.AddHandler(codec, "application/json", "text/json", "text/x-json", "text/javascript", "*+json");
            }

            client.AddHandler(new XmlDeserializer(), "application/xml", "text/xml", "*+xml", "*");

            client.Timeout = configuration.Timeout;

            if (configuration.UserAgent != null)
            {
                client.UserAgent = configuration.UserAgent;
            }

            InterceptRequest(req);
            {{#supportsAsync}}
            var response = await client.ExecuteTaskAsync(req);
            {{/supportsAsync}}
            {{^supportsAsync}}
            var response = client.Execute(req);
            {{/supportsAsync}}
            InterceptResponse(req, response);

            var result = toApiResponse(response{{^supportsAsync}}, codec{{/supportsAsync}});
            if (response.ErrorMessage != null)
            {
                result.ErrorText = response.ErrorMessage;
            }

            if (response.Cookies != null && response.Cookies.Count > 0)
            {
                if(result.Cookies == null) result.Cookies = new List();
                foreach (var restResponseCookie in response.Cookies)
                {
                    var cookie = new Cookie(
                        restResponseCookie.Name,
                        restResponseCookie.Value,
                        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);
                }
            }
            return result;
        }
        
        {{#supportsAsync}}
        #region IAsynchronousClient
        /// 
        /// Make a HTTP GET request (async).
        /// 
        /// The target path (or resource).
        /// The additional request options.
        /// A per-request configuration object. It is assumed that any merge with
        /// GlobalConfiguration has been done before calling this method.
        /// A Task containing ApiResponse
        public async Task> GetAsync(string path, RequestOptions options, IReadableConfiguration configuration = null)
        {
            var config = configuration ?? GlobalConfiguration.Instance;
            return await Exec(newRequest(HttpMethod.Get, path, options, config), config);
        }

        /// 
        /// Make a HTTP POST request (async).
        /// 
        /// The target path (or resource).
        /// The additional request options.
        /// A per-request configuration object. It is assumed that any merge with
        /// GlobalConfiguration has been done before calling this method.
        /// A Task containing ApiResponse
        public async Task> PostAsync(string path, RequestOptions options, IReadableConfiguration configuration = null)
        {
            var config = configuration ?? GlobalConfiguration.Instance;
            return await Exec(newRequest(HttpMethod.Post, path, options, config), config);
        }

        /// 
        /// Make a HTTP PUT request (async).
        /// 
        /// The target path (or resource).
        /// The additional request options.
        /// A per-request configuration object. It is assumed that any merge with
        /// GlobalConfiguration has been done before calling this method.
        /// A Task containing ApiResponse
        public async Task> PutAsync(string path, RequestOptions options, IReadableConfiguration configuration = null)
        {
            var config = configuration ?? GlobalConfiguration.Instance;
            return await Exec(newRequest(HttpMethod.Put, path, options, config), config);
        }

        /// 
        /// Make a HTTP DELETE request (async).
        /// 
        /// The target path (or resource).
        /// The additional request options.
        /// A per-request configuration object. It is assumed that any merge with
        /// GlobalConfiguration has been done before calling this method.
        /// A Task containing ApiResponse
        public async Task> DeleteAsync(string path, RequestOptions options, IReadableConfiguration configuration = null)
        {
            var config = configuration ?? GlobalConfiguration.Instance;
            return await Exec(newRequest(HttpMethod.Delete, path, options, config), config);
        }

        /// 
        /// Make a HTTP HEAD request (async).
        /// 
        /// The target path (or resource).
        /// The additional request options.
        /// A per-request configuration object. It is assumed that any merge with
        /// GlobalConfiguration has been done before calling this method.
        /// A Task containing ApiResponse
        public async Task> HeadAsync(string path, RequestOptions options, IReadableConfiguration configuration = null)
        {
            var config = configuration ?? GlobalConfiguration.Instance;
            return await Exec(newRequest(HttpMethod.Head, path, options, config), config);
        }

        /// 
        /// Make a HTTP OPTION request (async).
        /// 
        /// The target path (or resource).
        /// The additional request options.
        /// A per-request configuration object. It is assumed that any merge with
        /// GlobalConfiguration has been done before calling this method.
        /// A Task containing ApiResponse
        public async Task> OptionsAsync(string path, RequestOptions options, IReadableConfiguration configuration = null)
        {
            var config = configuration ?? GlobalConfiguration.Instance;
            return await Exec(newRequest(HttpMethod.Options, path, options, config), config);
        }

        /// 
        /// Make a HTTP PATCH request (async).
        /// 
        /// The target path (or resource).
        /// The additional request options.
        /// A per-request configuration object. It is assumed that any merge with
        /// GlobalConfiguration has been done before calling this method.
        /// A Task containing ApiResponse
        public async Task> PatchAsync(string path, RequestOptions options, IReadableConfiguration configuration = null)
        {
            var config = configuration ?? GlobalConfiguration.Instance;
            return await Exec(newRequest(HttpMethod.Patch, path, options, config), config);
        }
        #endregion IAsynchronousClient
        {{/supportsAsync}}

        #region ISynchronousClient
        /// 
        /// Make a HTTP GET request (synchronous).
        /// 
        /// The target path (or resource).
        /// The additional request options.
        /// A per-request configuration object. It is assumed that any merge with
        /// GlobalConfiguration has been done before calling this method.
        /// A Task containing ApiResponse
        public ApiResponse Get(string path, RequestOptions options, IReadableConfiguration configuration = null)
        {
            {{#supportsAsync}}
            return GetAsync(path, options, configuration).Result;
            {{/supportsAsync}}
            {{^supportsAsync}}
            var config = configuration ?? GlobalConfiguration.Instance;
            return Exec(newRequest(HttpMethod.Get, path, options, config), config);
            {{/supportsAsync}}
        }

        /// 
        /// Make a HTTP POST request (synchronous).
        /// 
        /// The target path (or resource).
        /// The additional request options.
        /// A per-request configuration object. It is assumed that any merge with
        /// GlobalConfiguration has been done before calling this method.
        /// A Task containing ApiResponse
        public ApiResponse Post(string path, RequestOptions options, IReadableConfiguration configuration = null)
        {
            {{#supportsAsync}}
            return PostAsync(path, options, configuration).Result;
            {{/supportsAsync}}
            {{^supportsAsync}}
            var config = configuration ?? GlobalConfiguration.Instance;
            return Exec(newRequest(HttpMethod.Post, path, options, config), config);
            {{/supportsAsync}}
        }

        /// 
        /// Make a HTTP PUT request (synchronous).
        /// 
        /// The target path (or resource).
        /// The additional request options.
        /// A per-request configuration object. It is assumed that any merge with
        /// GlobalConfiguration has been done before calling this method.
        /// A Task containing ApiResponse
        public ApiResponse Put(string path, RequestOptions options, IReadableConfiguration configuration = null)
        {
            {{#supportsAsync}}
            return PutAsync(path, options, configuration).Result;
            {{/supportsAsync}}
            {{^supportsAsync}}
            var config = configuration ?? GlobalConfiguration.Instance;
            return Exec(newRequest(HttpMethod.Put, path, options, config), config);
            {{/supportsAsync}}
        }

        /// 
        /// Make a HTTP DELETE request (synchronous).
        /// 
        /// The target path (or resource).
        /// The additional request options.
        /// A per-request configuration object. It is assumed that any merge with
        /// GlobalConfiguration has been done before calling this method.
        /// A Task containing ApiResponse
        public ApiResponse Delete(string path, RequestOptions options, IReadableConfiguration configuration = null)
        {
            {{#supportsAsync}}
            return DeleteAsync(path, options, configuration).Result;
            {{/supportsAsync}}
            {{^supportsAsync}}
            var config = configuration ?? GlobalConfiguration.Instance;
            return Exec(newRequest(HttpMethod.Delete, path, options, config), config);
            {{/supportsAsync}}
        }

        /// 
        /// Make a HTTP HEAD request (synchronous).
        /// 
        /// The target path (or resource).
        /// The additional request options.
        /// A per-request configuration object. It is assumed that any merge with
        /// GlobalConfiguration has been done before calling this method.
        /// A Task containing ApiResponse
        public ApiResponse Head(string path, RequestOptions options, IReadableConfiguration configuration = null)
        {
            {{#supportsAsync}}
            return HeadAsync(path, options, configuration).Result;
            {{/supportsAsync}}
            {{^supportsAsync}}
            var config = configuration ?? GlobalConfiguration.Instance;
            return Exec(newRequest(HttpMethod.Head, path, options, config), config);
            {{/supportsAsync}}
        }

        /// 
        /// Make a HTTP OPTION request (synchronous).
        /// 
        /// The target path (or resource).
        /// The additional request options.
        /// A per-request configuration object. It is assumed that any merge with
        /// GlobalConfiguration has been done before calling this method.
        /// A Task containing ApiResponse
        public ApiResponse Options(string path, RequestOptions options, IReadableConfiguration configuration = null)
        {
            {{#supportsAsync}}
            return OptionsAsync(path, options, configuration).Result;
            {{/supportsAsync}}
            {{^supportsAsync}}
            var config = configuration ?? GlobalConfiguration.Instance;
            return Exec(newRequest(HttpMethod.Options, path, options, config), config);
            {{/supportsAsync}}
        }

        /// 
        /// Make a HTTP PATCH request (synchronous).
        /// 
        /// The target path (or resource).
        /// The additional request options.
        /// A per-request configuration object. It is assumed that any merge with
        /// GlobalConfiguration has been done before calling this method.
        /// A Task containing ApiResponse
        public ApiResponse Patch(string path, RequestOptions options, IReadableConfiguration configuration = null)
        {
            {{#supportsAsync}}
            return PatchAsync(path, options, configuration).Result;
            {{/supportsAsync}}
            {{^supportsAsync}}
            var config = configuration ?? GlobalConfiguration.Instance;
            return Exec(newRequest(HttpMethod.Patch, path, options, config), config);
            {{/supportsAsync}}
        }
        #endregion ISynchronousClient
    }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy