using System; using System.Collections.Generic; using System.IO; using System.Linq; using System.Net; using System.Net.Http; using System.Net.Http.Headers; using System.Net.Mime; using System.Security.Authentication; using System.Text; using System.Threading.Tasks; using System.Web; using FarmmapsApi.Models; using Google.Apis.Http; using Google.Apis.Upload; using IdentityModel; using Newtonsoft.Json; using Newtonsoft.Json.Linq; using Winista.Mime; namespace FarmmapsApi.Services { public class FarmmapsApiService { private readonly Configuration _configuration; private readonly HttpClientSettings _httpClientSettings; private readonly HttpClient _httpClient; private readonly OpenIdConnectService _openIdConnectService; public FarmmapsApiService(HttpClientSettings httpClientSettings, HttpClient httpClient, OpenIdConnectService openIdConnectService, Configuration configuration) { _configuration = configuration; _httpClientSettings = httpClientSettings; _httpClient = httpClient; _openIdConnectService = openIdConnectService; _httpClient.BaseAddress = new Uri(configuration.Endpoint); _httpClient.DefaultRequestHeaders.Add("Accept", MediaTypeNames.Application.Json); if (!string.IsNullOrEmpty(_httpClientSettings.BearerToken)) { _httpClient.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue(OidcConstants.AuthenticationSchemes.AuthorizationHeaderBearer, _httpClientSettings.BearerToken); } } public async Task AuthenticateAsync() { if (_httpClient.DefaultRequestHeaders.Authorization != null && _httpClient.DefaultRequestHeaders.Authorization.Scheme != OidcConstants.AuthenticationSchemes.AuthorizationHeaderBearer) throw new AuthenticationException("Already seems to be authenticated"); var disco = await _openIdConnectService.GetDiscoveryDocumentAsync(); var usePasswordGrant = string.IsNullOrEmpty(_configuration.ClientId) || string.IsNullOrEmpty(_configuration.ClientSecret); var token = usePasswordGrant ? await _openIdConnectService.GetTokenUsernamePasswordAsync(disco.TokenEndpoint, _configuration.GrantClientId, _configuration.Username, _configuration.Password) : await _openIdConnectService.GetTokenClientCredentialsAsync(disco.TokenEndpoint, _configuration.ClientId, _configuration.ClientSecret); if (token.IsError) throw new AuthenticationException(token.Error); _httpClientSettings.BearerToken = token.AccessToken; _httpClient.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue(OidcConstants.AuthenticationSchemes.AuthorizationHeaderBearer, token.AccessToken); } public async Task GetCurrentUserCodeAsync() { var url = $"{_configuration.BasePath}/{ResourceEndpoints.CURRENTUSER_RESOURCE}"; var response = await _httpClient.GetAsync(url); if (!response.IsSuccessStatusCode) throw new Exception(response.ReasonPhrase); var jsonString = await response.Content.ReadAsStringAsync(); var json = JsonConvert.DeserializeObject(jsonString); return json["code"].Value(); } public async Task> GetCurrentUserRootsAsync() { var url = $"{_configuration.BasePath}/{ResourceEndpoints.MYROOTS_RESOURCE}"; var response = await _httpClient.GetAsync(url); if (!response.IsSuccessStatusCode) throw new Exception(response.ReasonPhrase); var jsonString = await response.Content.ReadAsStringAsync(); var roots = JsonConvert.DeserializeObject>(jsonString); roots.ForEach(v => { v.Name = GetSuffixFromSystemItemCode(v.Code); }); return roots; } private static string GetSuffixFromSystemItemCode(string itemCode) { string[] strArray = itemCode.Split(":"); return strArray.Length == 2 ? strArray[1] : throw new ArgumentException("No system item code :" + itemCode); } public async Task GetItemAsync(string itemCode, string itemType = null, JObject dataFilter = null) { if (dataFilter == null) return await GetItemSingleAsync(itemCode, itemType); var items = await GetItemsAsync(itemCode, itemType, dataFilter); return items[0]; } public async Task> GetItemsAsync(string itemCode, string itemType = null, JObject dataFilter = null) { var url = $"{_configuration.BasePath}/{ResourceEndpoints.ITEMS_RESOURCE}"; var resourceUri = string.Format(url, itemCode); var queryString = HttpUtility.ParseQueryString(string.Empty); if (itemType != null) queryString["it"] = itemType; if (dataFilter != null) queryString["df"] = dataFilter.ToString(Formatting.None); resourceUri = (queryString.Count > 0 ? $"{resourceUri}?" : resourceUri) + queryString; var response = await _httpClient.GetAsync(resourceUri); if (!response.IsSuccessStatusCode) { if (response.StatusCode == HttpStatusCode.NotFound) return null; throw new Exception(response.ReasonPhrase); } var jsonString = await response.Content.ReadAsStringAsync(); return JsonConvert.DeserializeObject>(jsonString); } private async Task GetItemSingleAsync(string itemCode, string itemType = null) { var url = $"{_configuration.BasePath}/{ResourceEndpoints.ITEMS_RESOURCE}"; var resourceUri = string.Format(url, itemCode); if (!string.IsNullOrEmpty(itemType)) resourceUri = $"{resourceUri}/{itemType}"; var response = await _httpClient.GetAsync(resourceUri); if (!response.IsSuccessStatusCode) { if (response.StatusCode == HttpStatusCode.NotFound) return null; throw new Exception(response.ReasonPhrase); } var jsonString = await response.Content.ReadAsStringAsync(); return JsonConvert.DeserializeObject(jsonString); } public async Task> GetItemChildrenAsync(string itemCode, string itemType = null, JObject dataFilter = null) { var url = $"{_configuration.BasePath}/{ResourceEndpoints.ITEMS_CHILDREN_RESOURCE}"; var resourceUri = string.Format(url, itemCode); var queryString = HttpUtility.ParseQueryString(string.Empty); if (itemType != null) queryString["it"] = itemType; if (dataFilter != null) queryString["df"] = dataFilter.ToString(Formatting.None); resourceUri = (queryString.Count > 0 ? $"{resourceUri}?" : resourceUri) + queryString; var response = await _httpClient.GetAsync(resourceUri); if (!response.IsSuccessStatusCode) throw new Exception(response.ReasonPhrase); var jsonString = await response.Content.ReadAsStringAsync(); return JsonConvert.DeserializeObject>(jsonString); } public async Task CreateItemAsync(ItemRequest itemRequest) { var jsonString = JsonConvert.SerializeObject(itemRequest); var url = $"{_configuration.BasePath}/{ResourceEndpoints.ITEMS_CREATE_RESOURCE}"; var content = new StringContent(jsonString, Encoding.UTF8, MediaTypeNames.Application.Json); var response = await _httpClient.PostAsync(url, content); if (!response.IsSuccessStatusCode) throw new Exception(response.ReasonPhrase); var jsonStringResponse = await response.Content.ReadAsStringAsync(); return JsonConvert.DeserializeObject(jsonStringResponse); } public async Task DeleteItemAsync(string itemCode) { var url = $"{_configuration.BasePath}/{ResourceEndpoints.ITEMS_RESOURCE}"; var resourceUri = string.Format(url, itemCode); var response = await _httpClient.DeleteAsync(resourceUri); if (!response.IsSuccessStatusCode) throw new Exception(response.ReasonPhrase); } public async Task DeleteItemsAsync(IList itemCodes) { var jsonString = JsonConvert.SerializeObject(itemCodes); var content = new StringContent(jsonString); var url = $"{_configuration.BasePath}/{ResourceEndpoints.ITEMS_DELETE_RESOURCE}"; var response = await _httpClient.PostAsync(url, content); if (!response.IsSuccessStatusCode) throw new Exception(response.ReasonPhrase); } public async Task DownloadItemAsync(string itemCode, string filePath) { var url = $"{_configuration.BasePath}/{ResourceEndpoints.ITEMS_DOWNLOAD_RESOURCE}"; var resourceUri = string.Format(url, itemCode); var response = await _httpClient.GetAsync(resourceUri, HttpCompletionOption.ResponseHeadersRead); if (response.IsSuccessStatusCode) { await using Stream streamToReadFrom = await response.Content.ReadAsStreamAsync(); await using Stream streamToWriteTo = File.Open(filePath, FileMode.Create); await streamToReadFrom.CopyToAsync(streamToWriteTo); } else { throw new FileNotFoundException(response.ReasonPhrase); } } public async Task QueueTaskAsync(string itemCode, TaskRequest taskRequest) { var url = $"{_configuration.BasePath}/{ResourceEndpoints.ITEMTASK_REQUEST_RESOURCE}"; var resourceUri = string.Format(url, itemCode); var jsonString = JsonConvert.SerializeObject(taskRequest); var content = new StringContent(jsonString, Encoding.UTF8, MediaTypeNames.Application.Json); var response = await _httpClient.PostAsync(resourceUri, content); if (!response.IsSuccessStatusCode) throw new Exception(response.ReasonPhrase); var jsonStringResponse = await response.Content.ReadAsStringAsync(); var json = JsonConvert.DeserializeObject(jsonStringResponse); return json["code"].Value(); } public async Task> GetTasksStatusAsync(string itemCode) { var url = $"{_configuration.BasePath}/{ResourceEndpoints.ITEMTASKS_RESOURCE}"; var resourceUri = string.Format(url, itemCode); var response = await _httpClient.GetAsync(resourceUri); if (!response.IsSuccessStatusCode) throw new Exception(response.ReasonPhrase); var jsonString = await response.Content.ReadAsStringAsync(); return JsonConvert.DeserializeObject>(jsonString); } public async Task GetTaskStatusAsync(string itemCode, string itemTaskCode) { var url = $"{_configuration.BasePath}/{ResourceEndpoints.ITEMTASK_RESOURCE}"; var resourceUri = string.Format(url, itemCode, itemTaskCode); var response = await _httpClient.GetAsync(resourceUri); if (!response.IsSuccessStatusCode) throw new Exception(response.ReasonPhrase); var jsonString = await response.Content.ReadAsStringAsync(); return JsonConvert.DeserializeObject(jsonString); } /// /// Experimental /// /// /// /// /// /// public async Task UploadFile(string filePath, string parentItemCode, string geoJsonString = null, Action progressCallback = null) { if (!File.Exists(filePath)) throw new FileNotFoundException($"File not found {filePath}"); var mimeTypes = new MimeTypes(); var mimeType = mimeTypes.GetMimeTypeFromFile(filePath); var mimeString = mimeType?.ToString() ?? "application/json"; await using var uploadStream = new FileStream(filePath, FileMode.OpenOrCreate); var request = new FileRequest() { Name = Path.GetFileName(filePath), ParentCode = parentItemCode, Size = uploadStream.Length, Geometry = string.IsNullOrEmpty(geoJsonString) ? null : JObject.Parse(geoJsonString) }; using var httpClient = CreateConfigurableHttpClient(_httpClient); var farmmapsUploader = new FarmmapsUploader(httpClient, uploadStream, request, mimeString, ResourceEndpoints.ITEMS_UPLOAD_RESOURCE); Uri location = null; farmmapsUploader.ProgressChanged += progressCallback; farmmapsUploader.UploadSessionData += data => location = data.UploadUri; var progress = await farmmapsUploader.UploadAsync(); return new UploadResults(progress, location); } /// /// Experimental /// /// /// /// /// /// private async Task ResumeUploadFile(string filePath, Uri location, Action uploadCallback) { if (!File.Exists(filePath)) throw new FileNotFoundException($"File not found {filePath}"); await using var uploadStream = new FileStream(filePath, FileMode.OpenOrCreate); var request = new FileRequest() { Name = Path.GetFileName(filePath), ParentCode = string.Empty, Size = uploadStream.Length }; using var httpClient = CreateConfigurableHttpClient(_httpClient); var farmmapsUploader = new FarmmapsUploader(httpClient, uploadStream, request, string.Empty, ResourceEndpoints.ITEMS_UPLOAD_RESOURCE); farmmapsUploader.ProgressChanged += uploadCallback; await farmmapsUploader.ResumeAsync(location); return location; } private ConfigurableHttpClient CreateConfigurableHttpClient(HttpClient parent) { var googleHttpClient = new HttpClientFactory().CreateHttpClient(new CreateHttpClientArgs {GZipEnabled = true, ApplicationName = "FarmMaps"}); googleHttpClient.BaseAddress = parent.BaseAddress; googleHttpClient.DefaultRequestHeaders.Add("Accept", MediaTypeNames.Application.Json); var authHeader = parent.DefaultRequestHeaders.Authorization; googleHttpClient.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue(authHeader.Scheme, authHeader.Parameter); return googleHttpClient; } } }