using System; using System.Collections; using System.IO; using System.Net.Http; using System.Net.Mime; using System.Text; using System.Threading; using System.Threading.Tasks; using FarmmapsApi.Models; using Google.Apis.Http; using Google.Apis.Json; using Google.Apis.Requests; using Google.Apis.Upload; using Google.Apis.Util; using Newtonsoft.Json; namespace FarmmapsApi.Services { public class FarmmapsUploader : ResumableUpload { /// Payload description headers, describing the content itself. private const string PayloadContentTypeHeader = "X-Upload-Content-Type"; /// Payload description headers, describing the content itself. private const string PayloadContentLengthHeader = "X-Upload-Content-Length"; private const int UnknownSize = -1; /// Gets or sets the service. private HttpClient HttpClient { get; } /// /// Gets or sets the path of the method (combined with /// ) to produce /// absolute Uri. /// private string Path { get; } /// Gets or sets the HTTP method of this upload (used to initialize the upload). private string HttpMethod { get; } /// Gets or sets the stream's Content-Type. private string ContentType { get; } /// Gets or sets the body of this request. private FileRequest Body { get; } private long _streamLength; /// /// Create a resumable upload instance with the required parameters. /// /// The stream containing the content to upload. /// /// Caller is responsible for maintaining the open until the upload is /// completed. /// Caller is responsible for closing the . /// public FarmmapsUploader(ConfigurableHttpClient httpClient, Stream contentStream, FileRequest body, string contentType) : base(contentStream, new ResumableUploadOptions { HttpClient = httpClient, Serializer = new NewtonsoftJsonSerializer(), ServiceName = "FarmMaps" }) { httpClient.ThrowIfNull(nameof(httpClient)); contentStream.ThrowIfNull(nameof(contentStream)); _streamLength = ContentStream.CanSeek ? ContentStream.Length : UnknownSize; Body = body; Path = "api/v1/file"; HttpClient = httpClient; HttpMethod = HttpConsts.Post; ContentType = contentType; } /// public override async Task InitiateSessionAsync(CancellationToken cancellationToken = default) { HttpRequestMessage request = CreateInitializeRequest(); Options?.ModifySessionInitiationRequest?.Invoke(request); var response = await HttpClient.SendAsync(request, cancellationToken).ConfigureAwait(false); if (!response.IsSuccessStatusCode) { throw await ExceptionForResponseAsync(response).ConfigureAwait(false); } return response.Headers.Location; } /// Creates a request to initialize a request. private HttpRequestMessage CreateInitializeRequest() { var baseAddres = HttpClient.BaseAddress; var uri = new Uri($"{baseAddres.Scheme}://{baseAddres.Host}:{baseAddres.Port}"); var builder = new RequestBuilder() { BaseUri = uri, Path = Path, Method = HttpMethod, }; SetAllPropertyValues(builder); HttpRequestMessage request = builder.CreateRequest(); if (ContentType != null) { request.Headers.Add(PayloadContentTypeHeader, ContentType); } // if the length is unknown at the time of this request, omit "X-Upload-Content-Length" header if (ContentStream.CanSeek) { request.Headers.Add(PayloadContentLengthHeader, _streamLength.ToString()); } var jsonText = JsonConvert.SerializeObject(Body); request.Content = new StringContent(jsonText, Encoding.UTF8, MediaTypeNames.Application.Json); return request; } /// /// Reflectively enumerate the properties of this object looking for all properties containing the /// RequestParameterAttribute and copy their values into the request builder. /// private void SetAllPropertyValues(RequestBuilder requestBuilder) { Type myType = this.GetType(); var properties = myType.GetProperties(); foreach (var property in properties) { var attribute = property.GetCustomAttribute(); if (attribute != null) { string name = attribute.Name ?? property.Name.ToLower(); object value = property.GetValue(this, null); if (value != null) { if (!(value is string) && value is IEnumerable valueAsEnumerable) { foreach (var elem in valueAsEnumerable) { requestBuilder.AddParameter(attribute.Type, name, Utilities.ConvertToString(elem)); } } else { // Otherwise just convert it to a string. requestBuilder.AddParameter(attribute.Type, name, Utilities.ConvertToString(value)); } } } } } } }