From 5d0e2f69678a6c2375a365cb13c8ce890c40f4a6 Mon Sep 17 00:00:00 2001 From: Dion Date: Thu, 9 Nov 2023 23:24:57 +0100 Subject: [PATCH 1/7] WIP KML import feature --- .../Interfaces/IKmlImport.cs | 6 +++ .../LatitudeLongitudeAltDateTimeModel.cs | 9 ++++ .../Services/KmlImport.cs | 26 ++++++++++ .../Services/KmlImportCli.cs | 19 ++++++++ .../starsky.foundation.georealtime.csproj | 15 ++++++ .../Interfaces/IHttpClientHelper.cs | 7 +++ .../Services/HttpClientHelper.cs | 5 ++ starsky/starsky.sln | 14 ++++++ starsky/starskykmlimportcli/Program.cs | 47 +++++++++++++++++++ .../starskykmlimportcli.csproj | 18 +++++++ 10 files changed, 166 insertions(+) create mode 100644 starsky/starsky.foundation.georealtime/Interfaces/IKmlImport.cs create mode 100644 starsky/starsky.foundation.georealtime/Models/LatitudeLongitudeAltDateTimeModel.cs create mode 100644 starsky/starsky.foundation.georealtime/Services/KmlImport.cs create mode 100644 starsky/starsky.foundation.georealtime/Services/KmlImportCli.cs create mode 100644 starsky/starsky.foundation.georealtime/starsky.foundation.georealtime.csproj create mode 100644 starsky/starskykmlimportcli/Program.cs create mode 100644 starsky/starskykmlimportcli/starskykmlimportcli.csproj diff --git a/starsky/starsky.foundation.georealtime/Interfaces/IKmlImport.cs b/starsky/starsky.foundation.georealtime/Interfaces/IKmlImport.cs new file mode 100644 index 0000000000..178eb91fc8 --- /dev/null +++ b/starsky/starsky.foundation.georealtime/Interfaces/IKmlImport.cs @@ -0,0 +1,6 @@ +namespace starsky.foundation.georealtime.Interfaces; + +public interface IKmlImport +{ + +} diff --git a/starsky/starsky.foundation.georealtime/Models/LatitudeLongitudeAltDateTimeModel.cs b/starsky/starsky.foundation.georealtime/Models/LatitudeLongitudeAltDateTimeModel.cs new file mode 100644 index 0000000000..0fb3f59898 --- /dev/null +++ b/starsky/starsky.foundation.georealtime/Models/LatitudeLongitudeAltDateTimeModel.cs @@ -0,0 +1,9 @@ +namespace starsky.foundation.georealtime.Models; + +public class LatitudeLongitudeAltDateTimeModel +{ + public string Latitude { get; set; } + public string Longitude { get; set; } + public string Altitude { get; set; } + public string DateTime { get; set; } +} diff --git a/starsky/starsky.foundation.georealtime/Services/KmlImport.cs b/starsky/starsky.foundation.georealtime/Services/KmlImport.cs new file mode 100644 index 0000000000..21ed72d5b9 --- /dev/null +++ b/starsky/starsky.foundation.georealtime/Services/KmlImport.cs @@ -0,0 +1,26 @@ +using starsky.foundation.georealtime.Interfaces; +using starsky.foundation.http.Interfaces; +using starsky.foundation.storage.Interfaces; +using starsky.foundation.storage.Storage; + +namespace starsky.foundation.georealtime.Services; + +public sealed class KmlImport : IKmlImport +{ + private readonly IHttpClientHelper _httpClientHelper; + private readonly IStorage _hostStorage; + + public KmlImport(IHttpClientHelper httpClientHelper, ISelectorStorage selectorStorage) + { + _httpClientHelper = httpClientHelper; + _hostStorage = + selectorStorage.Get(SelectorStorage.StorageServices.HostFilesystem); + } + + public async Task Import(string kmlPathOrUrl) + { + + var readString = await _httpClientHelper.ReadString(kmlPathOrUrl); + readString.Key + } +} diff --git a/starsky/starsky.foundation.georealtime/Services/KmlImportCli.cs b/starsky/starsky.foundation.georealtime/Services/KmlImportCli.cs new file mode 100644 index 0000000000..ae9920596e --- /dev/null +++ b/starsky/starsky.foundation.georealtime/Services/KmlImportCli.cs @@ -0,0 +1,19 @@ +using starsky.foundation.platform.Interfaces; +using starsky.foundation.platform.Models; +using starsky.foundation.storage.Interfaces; + +namespace starsky.foundation.georealtime.Services; + +public sealed class KmlImportCli +{ + public KmlImportCli(AppSettings appSettings, IConsole console, ISelectorStorage selectorStorage) + { + throw new NotImplementedException(); + } + + + public async Task ImportKml(string[] args) + { + throw new NotImplementedException(); + } +} diff --git a/starsky/starsky.foundation.georealtime/starsky.foundation.georealtime.csproj b/starsky/starsky.foundation.georealtime/starsky.foundation.georealtime.csproj new file mode 100644 index 0000000000..cea7af3a6d --- /dev/null +++ b/starsky/starsky.foundation.georealtime/starsky.foundation.georealtime.csproj @@ -0,0 +1,15 @@ + + + + net6.0 + enable + enable + + + + + + + + + diff --git a/starsky/starsky.foundation.http/Interfaces/IHttpClientHelper.cs b/starsky/starsky.foundation.http/Interfaces/IHttpClientHelper.cs index 36d4facfe4..6398c4102c 100644 --- a/starsky/starsky.foundation.http/Interfaces/IHttpClientHelper.cs +++ b/starsky/starsky.foundation.http/Interfaces/IHttpClientHelper.cs @@ -8,7 +8,14 @@ namespace starsky.foundation.http.Interfaces public interface IHttpClientHelper { Task Download(string sourceHttpUrl, string fullLocalPath, int retryAfterInSeconds = 15); + + /// + /// Get String of webPage - does check with domain whitelist + /// + /// webUrl + /// bool: success or fail and string content of result Task> ReadString(string sourceHttpUrl); + Task> PostString(string sourceHttpUrl, HttpContent? httpContent, bool verbose = true); } diff --git a/starsky/starsky.foundation.http/Services/HttpClientHelper.cs b/starsky/starsky.foundation.http/Services/HttpClientHelper.cs index d7273b9a36..58d4d92af2 100644 --- a/starsky/starsky.foundation.http/Services/HttpClientHelper.cs +++ b/starsky/starsky.foundation.http/Services/HttpClientHelper.cs @@ -63,6 +63,11 @@ public HttpClientHelper(IHttpProvider httpProvider, "api.github.com" }; + /// + /// Get String of webPage - does check with domain whitelist + /// + /// webUrl + /// bool: success or fail and string content of result public async Task> ReadString(string sourceHttpUrl) { Uri sourceUri = new Uri(sourceHttpUrl); diff --git a/starsky/starsky.sln b/starsky/starsky.sln index 0779c618c3..e17a170239 100644 --- a/starsky/starsky.sln +++ b/starsky/starsky.sln @@ -165,6 +165,10 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "starsky.foundation.native", EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "starsky.feature.trash", "starsky.feature.trash\starsky.feature.trash.csproj", "{A62C129C-5D0C-4A0A-B5AA-261E041FF55D}" EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "starskykmlimportcli", "starskykmlimportcli\starskykmlimportcli.csproj", "{FF742D5D-D4F7-4DD9-BB62-9940DEC200EC}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "starsky.foundation.georealtime", "starsky.foundation.georealtime\starsky.foundation.georealtime.csproj", "{6634554D-E26A-48CA-BF31-C494F26DAA3E}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -367,6 +371,14 @@ Global {A62C129C-5D0C-4A0A-B5AA-261E041FF55D}.Debug|Any CPU.Build.0 = Debug|Any CPU {A62C129C-5D0C-4A0A-B5AA-261E041FF55D}.Release|Any CPU.ActiveCfg = Release|Any CPU {A62C129C-5D0C-4A0A-B5AA-261E041FF55D}.Release|Any CPU.Build.0 = Release|Any CPU + {FF742D5D-D4F7-4DD9-BB62-9940DEC200EC}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {FF742D5D-D4F7-4DD9-BB62-9940DEC200EC}.Debug|Any CPU.Build.0 = Debug|Any CPU + {FF742D5D-D4F7-4DD9-BB62-9940DEC200EC}.Release|Any CPU.ActiveCfg = Release|Any CPU + {FF742D5D-D4F7-4DD9-BB62-9940DEC200EC}.Release|Any CPU.Build.0 = Release|Any CPU + {6634554D-E26A-48CA-BF31-C494F26DAA3E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {6634554D-E26A-48CA-BF31-C494F26DAA3E}.Debug|Any CPU.Build.0 = Debug|Any CPU + {6634554D-E26A-48CA-BF31-C494F26DAA3E}.Release|Any CPU.ActiveCfg = Release|Any CPU + {6634554D-E26A-48CA-BF31-C494F26DAA3E}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE @@ -427,5 +439,7 @@ Global {35222B74-4D4C-4848-A5E3-7C3E084FB2FD} = {E638B7CE-3CAC-4772-8D3B-49A202D0975A} {0072F697-4E18-4B5F-80DF-530361D3E847} = {1C1EB4A5-08D0-4014-AE1F-962642A4E5D3} {A62C129C-5D0C-4A0A-B5AA-261E041FF55D} = {4B9276C3-651E-48D3-B3A7-3F4D74F3D01A} + {FF742D5D-D4F7-4DD9-BB62-9940DEC200EC} = {B974FC20-C3EE-4EB0-AF25-F0D8DA2C28D7} + {6634554D-E26A-48CA-BF31-C494F26DAA3E} = {1C1EB4A5-08D0-4014-AE1F-962642A4E5D3} EndGlobalSection EndGlobal diff --git a/starsky/starskykmlimportcli/Program.cs b/starsky/starskykmlimportcli/Program.cs new file mode 100644 index 0000000000..d55792071b --- /dev/null +++ b/starsky/starskykmlimportcli/Program.cs @@ -0,0 +1,47 @@ + + +using Microsoft.Extensions.DependencyInjection; +using starsky.foundation.consoletelemetry.Extensions; +using starsky.foundation.georealtime.Services; +using starsky.foundation.injection; +using starsky.foundation.platform.Helpers; +using starsky.foundation.platform.Interfaces; +using starsky.foundation.platform.Models; +using starsky.foundation.storage.Interfaces; +using starsky.foundation.webtelemetry.Helpers; + +namespace starskykmlimport +{ + public static class Program + { + public static async Task Main(string[] args) + { + // Use args in application + new ArgsHelper().SetEnvironmentByArgs(args); + + var services = new ServiceCollection(); + + // Setup AppSettings + services = await SetupAppSettings.FirstStepToAddSingleton(services); + + // Inject services + RegisterDependencies.Configure(services); + + var serviceProvider = services.BuildServiceProvider(); + var appSettings = serviceProvider.GetRequiredService(); + + services.AddMonitoringWorkerService(appSettings, AppSettings.StarskyAppType.Sync); + services.AddApplicationInsightsLogging(appSettings); + + serviceProvider = services.BuildServiceProvider(); + + var console = serviceProvider.GetRequiredService(); + var selectorStorage = serviceProvider.GetRequiredService(); + + // Help and other Command Line Tools args are included in the SyncCLI + await new KmlImportCli(appSettings, console, selectorStorage).ImportKml(args); + + await new FlushApplicationInsights(serviceProvider).FlushAsync(); + } + } +} diff --git a/starsky/starskykmlimportcli/starskykmlimportcli.csproj b/starsky/starskykmlimportcli/starskykmlimportcli.csproj new file mode 100644 index 0000000000..c56b62f73c --- /dev/null +++ b/starsky/starskykmlimportcli/starskykmlimportcli.csproj @@ -0,0 +1,18 @@ + + + + Exe + net6.0 + enable + enable + starskykmlimport + + + + + + + + + + From 5d1b3438244ff58b622f580502eff62f9b5c3d15 Mon Sep 17 00:00:00 2001 From: Dion Date: Fri, 10 Nov 2023 13:39:57 +0100 Subject: [PATCH 2/7] add guids --- .../starsky.foundation.georealtime.csproj | 9 ++++++--- starsky/starskykmlimportcli/starskykmlimportcli.csproj | 2 ++ 2 files changed, 8 insertions(+), 3 deletions(-) diff --git a/starsky/starsky.foundation.georealtime/starsky.foundation.georealtime.csproj b/starsky/starsky.foundation.georealtime/starsky.foundation.georealtime.csproj index cea7af3a6d..41c87d0a8c 100644 --- a/starsky/starsky.foundation.georealtime/starsky.foundation.georealtime.csproj +++ b/starsky/starsky.foundation.georealtime/starsky.foundation.georealtime.csproj @@ -1,11 +1,14 @@ - + net6.0 - enable + + {fbf2e628-3cec-4970-8730-d32b586db13a} + Full + 0.5.10 enable - + diff --git a/starsky/starskykmlimportcli/starskykmlimportcli.csproj b/starsky/starskykmlimportcli/starskykmlimportcli.csproj index c56b62f73c..82c231eb6d 100644 --- a/starsky/starskykmlimportcli/starskykmlimportcli.csproj +++ b/starsky/starskykmlimportcli/starskykmlimportcli.csproj @@ -4,6 +4,8 @@ Exe net6.0 enable + + {5a4dd5d2-6051-4965-b771-40ea8e2f4b72} enable starskykmlimport From 5bffc565e52df29b2d6fc7cb692fc54d25477731 Mon Sep 17 00:00:00 2001 From: Dion Date: Fri, 10 Nov 2023 15:01:51 +0100 Subject: [PATCH 3/7] WIP --- .../Models/KmlImportOptions.cs | 10 +++ .../LatitudeLongitudeAltDateTimeModel.cs | 4 +- .../Services/KmlImport.cs | 61 ++++++++++++++- .../Services/KmlImportCli.cs | 2 + .../Services/HttpClientHelper.cs | 33 +++++--- .../Models/AppSettings.cs | 14 ++-- .../Storage/StorageSubPathFilesystem.cs | 4 - .../Controllers/ImportControllerTest.cs | 6 +- .../Helpers/HttpClientHelperTest.cs | 55 ++++++++++---- .../Helpers/CleanDemoDataServiceCliTest.cs | 9 ++- .../Services/CleanDemoDataServiceTest.cs | 9 ++- .../Services/GeoCliTest.cs | 9 ++- .../Helpers/CheckForUpdatesHelperTest.cs | 15 ++-- .../Services/ImportCliTest.cs | 14 +--- .../Helpers/PackageTelemetryTest.cs | 26 ++++--- .../Helpers/ExifToolDownloadTest.cs | 75 ++++++++++++------- 16 files changed, 246 insertions(+), 100 deletions(-) create mode 100644 starsky/starsky.foundation.georealtime/Models/KmlImportOptions.cs diff --git a/starsky/starsky.foundation.georealtime/Models/KmlImportOptions.cs b/starsky/starsky.foundation.georealtime/Models/KmlImportOptions.cs new file mode 100644 index 0000000000..88db58b2d8 --- /dev/null +++ b/starsky/starsky.foundation.georealtime/Models/KmlImportOptions.cs @@ -0,0 +1,10 @@ +namespace starsky.foundation.georealtime.Models; + +public class KmlImportOptions +{ + public string OutputPath { get; set; } + public bool OutputStorageSubPath { get; set; } = true; + + public bool SplitByDay { get; set; } + +} diff --git a/starsky/starsky.foundation.georealtime/Models/LatitudeLongitudeAltDateTimeModel.cs b/starsky/starsky.foundation.georealtime/Models/LatitudeLongitudeAltDateTimeModel.cs index 0fb3f59898..91b92e4169 100644 --- a/starsky/starsky.foundation.georealtime/Models/LatitudeLongitudeAltDateTimeModel.cs +++ b/starsky/starsky.foundation.georealtime/Models/LatitudeLongitudeAltDateTimeModel.cs @@ -1,3 +1,5 @@ +using System; + namespace starsky.foundation.georealtime.Models; public class LatitudeLongitudeAltDateTimeModel @@ -5,5 +7,5 @@ public class LatitudeLongitudeAltDateTimeModel public string Latitude { get; set; } public string Longitude { get; set; } public string Altitude { get; set; } - public string DateTime { get; set; } + public DateTime DateTime { get; set; } } diff --git a/starsky/starsky.foundation.georealtime/Services/KmlImport.cs b/starsky/starsky.foundation.georealtime/Services/KmlImport.cs index 21ed72d5b9..8e0c361b66 100644 --- a/starsky/starsky.foundation.georealtime/Services/KmlImport.cs +++ b/starsky/starsky.foundation.georealtime/Services/KmlImport.cs @@ -1,4 +1,11 @@ +using System; +using System.Collections.Generic; +using System.Globalization; +using System.Linq; +using System.Threading.Tasks; +using System.Xml.Linq; using starsky.foundation.georealtime.Interfaces; +using starsky.foundation.georealtime.Models; using starsky.foundation.http.Interfaces; using starsky.foundation.storage.Interfaces; using starsky.foundation.storage.Storage; @@ -21,6 +28,58 @@ public async Task Import(string kmlPathOrUrl) { var readString = await _httpClientHelper.ReadString(kmlPathOrUrl); - readString.Key + if ( !readString.Key ) + { + return; + } + + var xDocument = XmlParse(readString.Value); + ParseKml(xDocument); + } + + private static XDocument XmlParse(string content) + { + return XDocument.Parse(content); + } + + private static List ParseKml(XContainer kml) + { + var coordinates = new List(); + DateTime timeUtc = DateTime.UtcNow; + + foreach (var placemark in kml.Descendants("{http://www.opengis.net/kml/2.2}Placemark")) + { + var timeUtcElement = placemark.Descendants("{http://www.opengis.net/kml/2.2}Data") + .FirstOrDefault(e => e.Attribute("name")?.Value == "Time UTC"); + if (timeUtcElement != null) + { + var timeUtcString = timeUtcElement.Element("{http://www.opengis.net/kml/2.2}value")?.Value; + Console.WriteLine(timeUtcString); + timeUtc = DateTime.ParseExact(timeUtcString, "M/d/yyyy h:mm:ss tt", CultureInfo.InvariantCulture); + } + + // now get the coordinates + + var lineString = placemark.Element("{http://www.opengis.net/kml/2.2}LineString"); + var coordinatesElement = lineString?.Element("{http://www.opengis.net/kml/2.2}coordinates"); + + if ( coordinatesElement == null ) continue; + + var coordinatesString = coordinatesElement.Value; + var coordinatesArray = coordinatesString.Split(' '); + // ReSharper disable once LoopCanBeConvertedToQuery + foreach (var coordinate in coordinatesArray) + { + var coordinateArray = coordinate.Split(','); + coordinates.Add(new LatitudeLongitudeAltDateTimeModel + { + Longitude = coordinateArray[0], + Latitude = coordinateArray[1], + Altitude = coordinateArray[2], + DateTime = timeUtc + }); + } + } + return coordinates; } } diff --git a/starsky/starsky.foundation.georealtime/Services/KmlImportCli.cs b/starsky/starsky.foundation.georealtime/Services/KmlImportCli.cs index ae9920596e..b36cade41f 100644 --- a/starsky/starsky.foundation.georealtime/Services/KmlImportCli.cs +++ b/starsky/starsky.foundation.georealtime/Services/KmlImportCli.cs @@ -1,3 +1,5 @@ +using System; +using System.Threading.Tasks; using starsky.foundation.platform.Interfaces; using starsky.foundation.platform.Models; using starsky.foundation.storage.Interfaces; diff --git a/starsky/starsky.foundation.http/Services/HttpClientHelper.cs b/starsky/starsky.foundation.http/Services/HttpClientHelper.cs index 58d4d92af2..cb060698a3 100644 --- a/starsky/starsky.foundation.http/Services/HttpClientHelper.cs +++ b/starsky/starsky.foundation.http/Services/HttpClientHelper.cs @@ -2,6 +2,7 @@ using System; using System.Collections.Generic; using System.IO; +using System.Linq; using System.Net; using System.Net.Http; using System.Text; @@ -11,6 +12,7 @@ using starsky.foundation.injection; using starsky.foundation.platform.Helpers; using starsky.foundation.platform.Interfaces; +using starsky.foundation.platform.Models; using starsky.foundation.storage.Interfaces; using starsky.foundation.storage.Storage; @@ -27,13 +29,15 @@ public sealed class HttpClientHelper : IHttpClientHelper /// IHttpProvider /// ScopeFactory contains a IStorageSelector /// WebLogger + /// AppSettings public HttpClientHelper(IHttpProvider httpProvider, - IServiceScopeFactory? serviceScopeFactory, IWebLogger logger) + IServiceScopeFactory? serviceScopeFactory, IWebLogger logger, AppSettings appSettings) { _httpProvider = httpProvider; _logger = logger; + _appSettings = appSettings; + if ( serviceScopeFactory == null ) return; - using ( var scope = serviceScopeFactory.CreateScope() ) { // ISelectorStorage is a scoped service @@ -48,21 +52,30 @@ public HttpClientHelper(IHttpProvider httpProvider, private readonly IHttpProvider _httpProvider; private readonly IWebLogger _logger; + private readonly AppSettings _appSettings; - /// + /// /// This domains are only allowed domains to download from (and https only) /// private readonly List _allowedDomains = new List { - "dl.dropboxusercontent.com", - "qdraw.nl", // < used by test + "qdraw.nl", // < used by test "media.qdraw.nl", // < used by demo - "locker.ifttt.com", "download.geonames.org", "exiftool.org", "api.github.com" }; + /// + /// Extend the allowed domains with the appSettings + /// + /// list of unique domains + private HashSet AllowedDomains() + { + _allowedDomains.AddRange(_appSettings.AllowedHttpsDomains); + return _allowedDomains.ToHashSet(); + } + /// /// Get String of webPage - does check with domain whitelist /// @@ -70,13 +83,13 @@ public HttpClientHelper(IHttpProvider httpProvider, /// bool: success or fail and string content of result public async Task> ReadString(string sourceHttpUrl) { - Uri sourceUri = new Uri(sourceHttpUrl); + var sourceUri = new Uri(sourceHttpUrl); _logger.LogInformation("[ReadString] HttpClientHelper > " + sourceUri.Host + " ~ " + sourceHttpUrl); // allow whitelist and https only - if (!_allowedDomains.Contains(sourceUri.Host) || sourceUri.Scheme != "https") return + if (!AllowedDomains().Contains(sourceUri.Host) || sourceUri.Scheme != "https") return new KeyValuePair(false,string.Empty); try @@ -104,7 +117,7 @@ public async Task> PostString(string sourceHttpUrl, + sourceUri.Host + " ~ " + sourceHttpUrl); // // allow whitelist and https only - if (!_allowedDomains.Contains(sourceUri.Host) || sourceUri.Scheme != "https") return + if (!AllowedDomains().Contains(sourceUri.Host) || sourceUri.Scheme != "https") return new KeyValuePair(false,string.Empty); try @@ -143,7 +156,7 @@ public async Task Download(string sourceHttpUrl, string fullLocalPath, int + sourceUri.Host + " ~ " + sourceHttpUrl); // allow whitelist and https only - if ( !_allowedDomains.Contains(sourceUri.Host) || + if ( !AllowedDomains().Contains(sourceUri.Host) || sourceUri.Scheme != "https" ) { _logger.LogInformation("[Download] HttpClientHelper > " diff --git a/starsky/starsky.foundation.platform/Models/AppSettings.cs b/starsky/starsky.foundation.platform/Models/AppSettings.cs index 4d15b3a4d9..24c31f6827 100644 --- a/starsky/starsky.foundation.platform/Models/AppSettings.cs +++ b/starsky/starsky.foundation.platform/Models/AppSettings.cs @@ -639,7 +639,6 @@ public Dictionary> PublishProfiles { [JsonConverter(typeof(JsonStringEnumConverter))] [PackageTelemetry] public AccountRoles.AppAccountRoles AccountRegisterDefaultRole { get; set; } = AccountRoles.AppAccountRoles.User; - /// /// Value for AccountRolesDefaultByEmailRegisterOverwrite @@ -775,8 +774,6 @@ public string ApplicationInsightsConnectionString { /// Private storage for EnablePackageTelemetry /// private bool? EnablePackageTelemetryPrivate { get; set; } - - /// /// Disable logout buttons in UI @@ -785,7 +782,6 @@ public string ApplicationInsightsConnectionString { [PackageTelemetry] public bool? UseLocalDesktopUi { get; set; } = false; - /// /// Helps us improve the software /// Please keep this enabled @@ -839,7 +835,15 @@ public bool? EnablePackageTelemetry /// [PackageTelemetry] public bool? UseSystemTrash { get; set; } - + + /// + /// Security whitelist to allow GET requests from other domains than the default list + /// use env variable: app__AllowedHttpsDomains__0 - value + /// Always use a domain name only, no paths and exclude https:// + /// http is not supported due security reasons + /// + public List AllowedHttpsDomains { get; set; } = + new List(); // ------------------------------------------------- // ------------------- Modifiers ------------------- diff --git a/starsky/starsky.foundation.storage/Storage/StorageSubPathFilesystem.cs b/starsky/starsky.foundation.storage/Storage/StorageSubPathFilesystem.cs index 2ab505a8df..a31e741f7a 100644 --- a/starsky/starsky.foundation.storage/Storage/StorageSubPathFilesystem.cs +++ b/starsky/starsky.foundation.storage/Storage/StorageSubPathFilesystem.cs @@ -214,7 +214,6 @@ public IEnumerable GetDirectories(string path) /// Returns a list of directories // Get list of child folders /// /// subPath in dir - /// order by alphabet or last edited /// list of paths public IEnumerable> GetDirectoryRecursive(string path) { @@ -260,9 +259,6 @@ Stream LocalGet() return new RetryStream().Retry(LocalGet); } - - - /// /// Write fileStream to disk diff --git a/starsky/starskytest/Controllers/ImportControllerTest.cs b/starsky/starskytest/Controllers/ImportControllerTest.cs index f85418f026..6b577c4d36 100644 --- a/starsky/starskytest/Controllers/ImportControllerTest.cs +++ b/starsky/starskytest/Controllers/ImportControllerTest.cs @@ -182,7 +182,8 @@ public async Task FromUrl_RequestFromWhiteListedDomain_NotFound() var serviceProvider = services.BuildServiceProvider(); var httpClientHelper = new HttpClientHelper(httpProvider, - serviceProvider.GetRequiredService(), new FakeIWebLogger()); + serviceProvider.GetRequiredService(), new FakeIWebLogger(), + new AppSettings{ AllowedHttpsDomains = new List{"download.geonames.org"}}); var importController = new ImportController(_import, _appSettings, _bgTaskQueue, httpClientHelper, new FakeSelectorStorage(new FakeIStorage()), @@ -208,7 +209,8 @@ public async Task FromUrl_RequestFromWhiteListedDomain_Ok() var storageProvider = serviceProvider.GetRequiredService(); var httpClientHelper = new HttpClientHelper(httpProvider, - serviceProvider.GetRequiredService(), new FakeIWebLogger()); + serviceProvider.GetRequiredService(), new FakeIWebLogger(), + new AppSettings{ AllowedHttpsDomains = new List{"qdraw.nl"}}); var importController = new ImportController( new FakeIImport(new FakeSelectorStorage(storageProvider)), _appSettings, diff --git a/starsky/starskytest/Helpers/HttpClientHelperTest.cs b/starsky/starskytest/Helpers/HttpClientHelperTest.cs index fca4b3a40c..001786af43 100644 --- a/starsky/starskytest/Helpers/HttpClientHelperTest.cs +++ b/starsky/starskytest/Helpers/HttpClientHelperTest.cs @@ -1,3 +1,4 @@ +using System.Collections.Generic; using System.IO; using System.Linq; using System.Net.Http; @@ -30,7 +31,8 @@ public async Task Download_HttpClientHelperBadDomainDownload() var storageProvider = serviceProvider.GetRequiredService(); var scopeFactory = serviceProvider.GetRequiredService(); - var httpClientHelper = new HttpClientHelper(httpProvider, scopeFactory, new FakeIWebLogger()); + var httpClientHelper = new HttpClientHelper(httpProvider, scopeFactory, new FakeIWebLogger(), + new AppSettings{ AllowedHttpsDomains = new List{"qdraw.nl"}}); // use only whitelisted domains var path = Path.Combine(new AppSettings().TempFolder, "pathToNOTdownload.txt"); @@ -51,7 +53,8 @@ public async Task Download_HttpClientHelper_404NotFoundTest() var serviceProvider = services.BuildServiceProvider(); var scopeFactory = serviceProvider.GetRequiredService(); - var httpClientHelper = new HttpClientHelper(httpProvider, scopeFactory, new FakeIWebLogger()); + var httpClientHelper = new HttpClientHelper(httpProvider, scopeFactory, new FakeIWebLogger(), + new AppSettings{ AllowedHttpsDomains = new List{"qdraw.nl"}}); // there is an file written var path = Path.Combine(new CreateAnImage().BasePath, "file.txt"); @@ -72,7 +75,8 @@ public async Task Download_HttpClientHelper_HTTP_Not_Download() var serviceProvider = services.BuildServiceProvider(); var scopeFactory = serviceProvider.GetRequiredService(); - var httpClientHelper = new HttpClientHelper(httpProvider, scopeFactory, new FakeIWebLogger()); + var httpClientHelper = new HttpClientHelper(httpProvider, scopeFactory, new FakeIWebLogger(), + new AppSettings{ AllowedHttpsDomains = new List{"qdraw.nl"}}); // http is not used anymore var path = Path.Combine(new AppSettings().TempFolder, "pathToNOTdownload.txt"); @@ -94,7 +98,8 @@ public async Task Download_HttpClientHelper_Download() var scopeFactory = serviceProvider.GetRequiredService(); var storageProvider = serviceProvider.GetRequiredService(); - var httpClientHelper = new HttpClientHelper(httpProvider, scopeFactory, new FakeIWebLogger()); + var httpClientHelper = new HttpClientHelper(httpProvider, scopeFactory, new FakeIWebLogger(), + new AppSettings{ AllowedHttpsDomains = new List{"qdraw.nl"}}); // there is an file written var path = Path.Combine(new CreateAnImage().BasePath, "file.txt"); @@ -121,7 +126,8 @@ public async Task Download_HttpClientHelper_Download_HttpRequestException() var serviceProvider = services.BuildServiceProvider(); var scopeFactory = serviceProvider.GetRequiredService(); - var httpClientHelper = new HttpClientHelper(httpProvider, scopeFactory, new FakeIWebLogger()); + var httpClientHelper = new HttpClientHelper(httpProvider, scopeFactory, new FakeIWebLogger(), + new AppSettings{ AllowedHttpsDomains = new List{"qdraw.nl"}}); var output = await httpClientHelper.Download("https://qdraw.nl/test","/sdkflndf",1); Assert.IsFalse(output); } @@ -130,7 +136,14 @@ public async Task Download_HttpClientHelper_Download_HttpRequestException() [ExpectedException(typeof(EndOfStreamException))] public async Task Download_HttpClientHelper_Download_NoStorage() { - await new HttpClientHelper(new FakeIHttpProvider(), null, new FakeIWebLogger()).Download("t","T"); + var client = new HttpClientHelper(new FakeIHttpProvider(), + null, new FakeIWebLogger(), + new AppSettings + { + AllowedHttpsDomains = new List { "qdraw.nl" } + }); + + await client.Download("t","T"); } [TestMethod] @@ -145,9 +158,9 @@ public async Task ReadString_HttpClientHelper_ReadString() services.AddSingleton(); var serviceProvider = services.BuildServiceProvider(); var scopeFactory = serviceProvider.GetRequiredService(); - var storageProvider = serviceProvider.GetRequiredService(); - var httpClientHelper = new HttpClientHelper(httpProvider, scopeFactory, new FakeIWebLogger()); + var httpClientHelper = new HttpClientHelper(httpProvider, scopeFactory, new FakeIWebLogger(), + new AppSettings{ AllowedHttpsDomains = new List{"qdraw.nl"}}); var output = await httpClientHelper.ReadString("https://qdraw.nl/test"); @@ -169,7 +182,8 @@ public async Task ReadString_HttpClientHelper_ReadString_HttpRequestException() var serviceProvider = services.BuildServiceProvider(); var scopeFactory = serviceProvider.GetRequiredService(); - var httpClientHelper = new HttpClientHelper(httpProvider, scopeFactory, new FakeIWebLogger()); + var httpClientHelper = new HttpClientHelper(httpProvider, scopeFactory, new FakeIWebLogger(), + new AppSettings{ AllowedHttpsDomains = new List{"qdraw.nl"}}); var output = await httpClientHelper.ReadString("https://qdraw.nl/test"); Assert.IsFalse(output.Key); } @@ -187,7 +201,8 @@ public async Task ReadString_HttpClientHelper_HTTP_Not_ReadString() var serviceProvider = services.BuildServiceProvider(); var scopeFactory = serviceProvider.GetRequiredService(); - var httpClientHelper = new HttpClientHelper(httpProvider, scopeFactory, new FakeIWebLogger()); + var httpClientHelper = new HttpClientHelper(httpProvider, scopeFactory, new FakeIWebLogger(), + new AppSettings{ AllowedHttpsDomains = new List{"qdraw.nl"}}); // http is not used anymore var output = await httpClientHelper.ReadString("http://qdraw.nl"); @@ -207,7 +222,8 @@ public async Task ReadString_HttpClientHelper_404NotFound_ReadString_Test() var serviceProvider = services.BuildServiceProvider(); var scopeFactory = serviceProvider.GetRequiredService(); - var httpClientHelper = new HttpClientHelper(httpProvider, scopeFactory, new FakeIWebLogger()); + var httpClientHelper = new HttpClientHelper(httpProvider, scopeFactory, new FakeIWebLogger(), + new AppSettings{ AllowedHttpsDomains = new List{"qdraw.nl"}}); var output = await httpClientHelper.ReadString("https://download.geonames.org/404"); Assert.AreEqual(false,output.Key); @@ -226,7 +242,8 @@ public async Task PostString_HttpClientHelper() var serviceProvider = services.BuildServiceProvider(); var scopeFactory = serviceProvider.GetRequiredService(); - var httpClientHelper = new HttpClientHelper(httpProvider, scopeFactory, new FakeIWebLogger()); + var httpClientHelper = new HttpClientHelper(httpProvider, scopeFactory, new FakeIWebLogger(), + new AppSettings{ AllowedHttpsDomains = new List{"qdraw.nl"}}); var output = await httpClientHelper .PostString("https://qdraw.nl/test", new StringContent(string.Empty)); @@ -248,7 +265,8 @@ public async Task PostString_HttpClientHelper_VerboseFalse() var scopeFactory = serviceProvider.GetRequiredService(); var fakeLogger = new FakeIWebLogger(); - var httpClientHelper = new HttpClientHelper(httpProvider, scopeFactory,fakeLogger); + var httpClientHelper = new HttpClientHelper(httpProvider, scopeFactory,fakeLogger, + new AppSettings{ AllowedHttpsDomains = new List{"qdraw.nl"}}); await httpClientHelper .PostString("https://qdraw.nl/test", new StringContent(string.Empty),false); @@ -271,9 +289,12 @@ public async Task PostString_HttpRequestException() var serviceProvider = services.BuildServiceProvider(); var scopeFactory = serviceProvider.GetRequiredService(); - var httpClientHelper = new HttpClientHelper(httpProvider, scopeFactory, new FakeIWebLogger()); + var httpClientHelper = new HttpClientHelper(httpProvider, scopeFactory, new FakeIWebLogger(), + new AppSettings{ AllowedHttpsDomains = new List{"qdraw.nl"}}); + var output = await httpClientHelper .PostString("https://qdraw.nl/test", new StringContent(string.Empty)); + Assert.IsFalse(output.Key); } @@ -290,7 +311,8 @@ public async Task PostString_HTTP_Not_ReadString() var serviceProvider = services.BuildServiceProvider(); var scopeFactory = serviceProvider.GetRequiredService(); - var httpClientHelper = new HttpClientHelper(httpProvider, scopeFactory, new FakeIWebLogger()); + var httpClientHelper = new HttpClientHelper(httpProvider, scopeFactory, new FakeIWebLogger(), + new AppSettings{ AllowedHttpsDomains = new List{"qdraw.nl"}}); // http is not used anymore var output = await httpClientHelper @@ -311,7 +333,8 @@ public async Task PostString_404NotFound_Test() var serviceProvider = services.BuildServiceProvider(); var scopeFactory = serviceProvider.GetRequiredService(); - var httpClientHelper = new HttpClientHelper(httpProvider, scopeFactory, new FakeIWebLogger()); + var httpClientHelper = new HttpClientHelper(httpProvider, scopeFactory, new FakeIWebLogger(), + new AppSettings{ AllowedHttpsDomains = new List{"qdraw.nl"}}); var output = await httpClientHelper .PostString("https://download.geonames.org/404", new StringContent(string.Empty)); diff --git a/starsky/starskytest/starsky.feature.demo/Helpers/CleanDemoDataServiceCliTest.cs b/starsky/starskytest/starsky.feature.demo/Helpers/CleanDemoDataServiceCliTest.cs index 70bfd2e8c4..05a201e86e 100644 --- a/starsky/starskytest/starsky.feature.demo/Helpers/CleanDemoDataServiceCliTest.cs +++ b/starsky/starskytest/starsky.feature.demo/Helpers/CleanDemoDataServiceCliTest.cs @@ -73,7 +73,8 @@ public async Task SeedCli_IsDownloading() {"https://qdraw.nl/1000/20211117_091926_dsc00514_e_kl1k.jpg",new StringContent("test")} }); - var httpClientHelper = new HttpClientHelper(fakeIHttpClientHelper, _serviceScopeFactory, _logger); + var httpClientHelper = new HttpClientHelper(fakeIHttpClientHelper, _serviceScopeFactory, _logger, + new AppSettings{ AllowedHttpsDomains = new List{"qdraw.nl"}}); var service = new CleanDemoDataServiceCli(appSettings, httpClientHelper, _selectorStorage, _logger, _console, new FakeISynchronize()); @@ -92,7 +93,8 @@ public async Task SeedCli_Help() { var fakeIHttpClientHelper = new FakeIHttpProvider(); - var httpClientHelper = new HttpClientHelper(fakeIHttpClientHelper, _serviceScopeFactory, _logger); + var httpClientHelper = new HttpClientHelper(fakeIHttpClientHelper, _serviceScopeFactory, _logger, + new AppSettings{ AllowedHttpsDomains = new List{"qdraw.nl"}}); var service = new CleanDemoDataServiceCli(_appSettings, httpClientHelper, _selectorStorage, _logger, _console, new FakeISynchronize()); @@ -109,7 +111,8 @@ public async Task SeedCli_HelpVerbose() { var fakeIHttpClientHelper = new FakeIHttpProvider(); - var httpClientHelper = new HttpClientHelper(fakeIHttpClientHelper, _serviceScopeFactory, _logger); + var httpClientHelper = new HttpClientHelper(fakeIHttpClientHelper, _serviceScopeFactory, _logger, + new AppSettings{ AllowedHttpsDomains = new List{"qdraw.nl"}}); var appSettings = new AppSettings{Verbose = false}; var service = new CleanDemoDataServiceCli(appSettings, httpClientHelper, diff --git a/starsky/starskytest/starsky.feature.demo/Services/CleanDemoDataServiceTest.cs b/starsky/starskytest/starsky.feature.demo/Services/CleanDemoDataServiceTest.cs index 9dec31dc78..6202ca2e4b 100644 --- a/starsky/starskytest/starsky.feature.demo/Services/CleanDemoDataServiceTest.cs +++ b/starsky/starskytest/starsky.feature.demo/Services/CleanDemoDataServiceTest.cs @@ -230,7 +230,8 @@ public async Task DownloadAsync_AppSettingsMissing() var appSettings = new AppSettings(); var fakeIHttpClientHelper = new FakeIHttpProvider(new Dictionary()); - var httpClientHelper = new HttpClientHelper(fakeIHttpClientHelper, _serviceScopeFactory, _logger); + var httpClientHelper = new HttpClientHelper(fakeIHttpClientHelper, _serviceScopeFactory, _logger, + new AppSettings{ AllowedHttpsDomains = new List{"qdraw.nl"}}); var storage = new FakeIStorage(); var result = await CleanDemoDataService.DownloadAsync(appSettings, httpClientHelper, storage, storage, _logger); @@ -256,7 +257,8 @@ public async Task DownloadAsync_IsDownloading() {"https://qdraw.nl/1000/20211117_091926_dsc00514_e_kl1k.jpg",new StringContent("test")} }); - var httpClientHelper = new HttpClientHelper(fakeIHttpClientHelper, _serviceScopeFactory, _logger); + var httpClientHelper = new HttpClientHelper(fakeIHttpClientHelper, _serviceScopeFactory, _logger, + new AppSettings{ AllowedHttpsDomains = new List{"qdraw.nl"}}); var result = await CleanDemoDataService.DownloadAsync(appSettings, httpClientHelper, _storage, _storage, _logger); @@ -284,7 +286,8 @@ public async Task DownloadAsync_IsDownloading_InvalidData() {"https://qdraw.nl/1000/20211117_091926_dsc00514_e_kl1k.jpg",new StringContent("test")} }); - var httpClientHelper = new HttpClientHelper(fakeIHttpClientHelper, _serviceScopeFactory, _logger); + var httpClientHelper = new HttpClientHelper(fakeIHttpClientHelper, _serviceScopeFactory, _logger, + new AppSettings{ AllowedHttpsDomains = new List{"qdraw.nl"}}); var result = await CleanDemoDataService.DownloadAsync(appSettings, httpClientHelper, _storage, _storage, _logger); diff --git a/starsky/starskytest/starsky.feature.geolookup/Services/GeoCliTest.cs b/starsky/starskytest/starsky.feature.geolookup/Services/GeoCliTest.cs index bcc7a7d563..217eb6be4f 100644 --- a/starsky/starskytest/starsky.feature.geolookup/Services/GeoCliTest.cs +++ b/starsky/starskytest/starsky.feature.geolookup/Services/GeoCliTest.cs @@ -38,7 +38,8 @@ public async Task GeoCliInput_Notfound() var fakeIHttpProvider = new FakeIHttpProvider(new Dictionary { }); - var httpClientHelper = new HttpClientHelper(fakeIHttpProvider, _serviceScopeFactory, new FakeIWebLogger()); + var httpClientHelper = new HttpClientHelper(fakeIHttpProvider, _serviceScopeFactory, new FakeIWebLogger(), + new AppSettings{ AllowedHttpsDomains = new List{"qdraw.nl"}}); var console = new FakeConsoleWrapper(); var geoCli = new GeoCli(new FakeIGeoReverseLookup(), new FakeIGeoLocationWrite(), @@ -55,7 +56,8 @@ public async Task GeoCliInput_RelativeUrl_HappyFlow() var fakeIHttpProvider = new FakeIHttpProvider(new Dictionary { }); - var httpClientHelper = new HttpClientHelper(fakeIHttpProvider, _serviceScopeFactory, new FakeIWebLogger()); + var httpClientHelper = new HttpClientHelper(fakeIHttpProvider, _serviceScopeFactory, new FakeIWebLogger(), + new AppSettings{ AllowedHttpsDomains = new List{"qdraw.nl"}}); var relativeParentFolder = new AppSettings().DatabasePathToFilePath( new StructureService(new FakeIStorage(), new AppSettings().Structure) @@ -85,7 +87,8 @@ public async Task GeoCliInput_AbsolutePath_HappyFlow() var fakeIHttpProvider = new FakeIHttpProvider(new Dictionary { }); - var httpClientHelper = new HttpClientHelper(fakeIHttpProvider, _serviceScopeFactory, new FakeIWebLogger()); + var httpClientHelper = new HttpClientHelper(fakeIHttpProvider, _serviceScopeFactory, new FakeIWebLogger(), + new AppSettings{ AllowedHttpsDomains = new List{"qdraw.nl"}}); var storage = new FakeIStorage(new List {"/"}, new List {"/test.jpg"}, diff --git a/starsky/starskytest/starsky.feature.health/Helpers/CheckForUpdatesHelperTest.cs b/starsky/starskytest/starsky.feature.health/Helpers/CheckForUpdatesHelperTest.cs index e016d1b2f9..9cfebff7cd 100644 --- a/starsky/starskytest/starsky.feature.health/Helpers/CheckForUpdatesHelperTest.cs +++ b/starsky/starskytest/starsky.feature.health/Helpers/CheckForUpdatesHelperTest.cs @@ -62,7 +62,8 @@ public async Task QueryIsUpdateNeeded() { {CheckForUpdates.GithubApi, new StringContent(replace)}, }); - var httpClientHelper = new HttpClientHelper(fakeIHttpProvider, _serviceScopeFactory, new FakeIWebLogger()); + var httpClientHelper = new HttpClientHelper(fakeIHttpProvider, _serviceScopeFactory, new FakeIWebLogger(), + new AppSettings{ AllowedHttpsDomains = new List{"qdraw.nl"}}); var results = await new CheckForUpdates(httpClientHelper, new AppSettings(),null).QueryIsUpdateNeededAsync(); @@ -79,7 +80,8 @@ public async Task QueryIsUpdateNeeded() public async Task QueryIsUpdateNeeded_NotFound() { var fakeIHttpProvider = new FakeIHttpProvider(new Dictionary()); - var httpClientHelper = new HttpClientHelper(fakeIHttpProvider, _serviceScopeFactory, new FakeIWebLogger()); + var httpClientHelper = new HttpClientHelper(fakeIHttpProvider, _serviceScopeFactory, new FakeIWebLogger(), + new AppSettings{ AllowedHttpsDomains = new List{"qdraw.nl"}}); var results = await new CheckForUpdates(httpClientHelper, new AppSettings(),null).QueryIsUpdateNeededAsync(); @@ -204,7 +206,8 @@ public async Task IsUpdateNeeded_CacheIsFilled() { {CheckForUpdates.GithubApi, new StringContent(replace)}, }); - var httpClientHelper = new HttpClientHelper(fakeIHttpProvider, _serviceScopeFactory, new FakeIWebLogger()); + var httpClientHelper = new HttpClientHelper(fakeIHttpProvider, _serviceScopeFactory, new FakeIWebLogger(), + new AppSettings{ AllowedHttpsDomains = new List{"qdraw.nl"}}); await new CheckForUpdates(httpClientHelper, new AppSettings(),memoryCache).IsUpdateNeeded(); @@ -267,7 +270,8 @@ public async Task IsUpdateNeeded_CacheExistButDisableInSettings() { {CheckForUpdates.GithubApi, new StringContent(replace)}, }); - var httpClientHelper = new HttpClientHelper(fakeIHttpProvider, _serviceScopeFactory, new FakeIWebLogger()); + var httpClientHelper = new HttpClientHelper(fakeIHttpProvider, _serviceScopeFactory, new FakeIWebLogger(), + new AppSettings{ AllowedHttpsDomains = new List{"qdraw.nl"}}); var results = await new CheckForUpdates(httpClientHelper, new AppSettings{AddMemoryCache = false},memoryCache).IsUpdateNeeded(); @@ -284,7 +288,8 @@ public async Task IsUpdateNeeded_CacheIsNull() { {CheckForUpdates.GithubApi, new StringContent(replace)}, }); - var httpClientHelper = new HttpClientHelper(fakeIHttpProvider, _serviceScopeFactory, new FakeIWebLogger()); + var httpClientHelper = new HttpClientHelper(fakeIHttpProvider, _serviceScopeFactory, new FakeIWebLogger(), + new AppSettings{ AllowedHttpsDomains = new List{"qdraw.nl"}}); var results = await new CheckForUpdates(httpClientHelper, new AppSettings{AddMemoryCache = true},null).IsUpdateNeeded(); diff --git a/starsky/starskytest/starsky.feature.import/Services/ImportCliTest.cs b/starsky/starskytest/starsky.feature.import/Services/ImportCliTest.cs index 2422416e4c..7b4edb2504 100644 --- a/starsky/starskytest/starsky.feature.import/Services/ImportCliTest.cs +++ b/starsky/starskytest/starsky.feature.import/Services/ImportCliTest.cs @@ -4,7 +4,6 @@ using System.Threading.Tasks; using Microsoft.VisualStudio.TestTools.UnitTesting; using starsky.feature.import.Services; -using starsky.foundation.http.Services; using starsky.foundation.platform.Models; using starskytest.FakeMocks; @@ -13,13 +12,6 @@ namespace starskytest.starsky.feature.import.Services [TestClass] public sealed class ImportCliTest { - private readonly HttpClientHelper _httpClientHelper; - - public ImportCliTest() - { - _httpClientHelper = new HttpClientHelper(new FakeIHttpProvider(), null, new FakeIWebLogger()); - } - [TestMethod] public async Task ImporterCli_CheckIfExifToolIsCalled() { @@ -71,7 +63,7 @@ public async Task ImporterCli_ArgPath_1() new AppSettings(), fakeConsole, new FakeExifToolDownload()).Importer( new List{"-p", "/test", "--output" , "csv"}.ToArray()); - Assert.IsFalse(fakeConsole.WrittenLines.FirstOrDefault().Contains("Done Importing")); + Assert.IsFalse(fakeConsole.WrittenLines.FirstOrDefault()?.Contains("Done Importing")); Assert.AreEqual("Id;Status;SourceFullFilePath;SubPath;FileHash",fakeConsole.WrittenLines.FirstOrDefault() ); Assert.AreEqual("0;FileError;~/temp/test;;FAKE",fakeConsole.WrittenLines[1] ); Assert.AreEqual("0;FileError;~/temp/test;;FAKE",fakeConsole.WrittenLines[2] ); @@ -92,7 +84,7 @@ public async Task ImporterCli_ArgPath_Verbose() // verbose is entered here await cli.Importer(new List{"-p", "/test", "-v", "true"}.ToArray()); - Assert.IsTrue(fakeConsole.WrittenLines.LastOrDefault().Contains("Failed: 2")); + Assert.IsTrue(fakeConsole.WrittenLines.LastOrDefault()?.Contains("Failed: 2")); } [TestMethod] @@ -106,7 +98,7 @@ public async Task ImporterCli_ArgPath_Failed() await new ImportCli(new FakeIImport(new FakeSelectorStorage(storage)), new AppSettings{Verbose = false}, fakeConsole, new FakeExifToolDownload()) .Importer(new List{"-p", "/test"}.ToArray()); - Assert.IsTrue(fakeConsole.WrittenLines.LastOrDefault().Contains("Failed")); + Assert.IsTrue(fakeConsole.WrittenLines.LastOrDefault()?.Contains("Failed")); } } diff --git a/starsky/starskytest/starsky.feature.packagetelemetry/Helpers/PackageTelemetryTest.cs b/starsky/starskytest/starsky.feature.packagetelemetry/Helpers/PackageTelemetryTest.cs index 9c7577d8e1..6d96626f86 100644 --- a/starsky/starskytest/starsky.feature.packagetelemetry/Helpers/PackageTelemetryTest.cs +++ b/starsky/starskytest/starsky.feature.packagetelemetry/Helpers/PackageTelemetryTest.cs @@ -38,7 +38,8 @@ public void GetSystemDataTest() { var httpProvider = new FakeIHttpProvider(); var appSettings = new AppSettings(); - var httpClientHelper = new HttpClientHelper(httpProvider, null!, new FakeIWebLogger()); + var httpClientHelper = new HttpClientHelper(httpProvider, null!, new FakeIWebLogger(), + new AppSettings{ AllowedHttpsDomains = new List{"qdraw.nl"}}); var packageTelemetry = new PackageTelemetry(httpClientHelper, new AppSettings(), new FakeIWebLogger(), new FakeIQuery(), new FakeIDeviceIdService()); var systemData = packageTelemetry.GetSystemData(); @@ -63,7 +64,8 @@ public void GetSystemDataTest() public void GetSystemDataTestDocker() { var httpProvider = new FakeIHttpProvider(); - var httpClientHelper = new HttpClientHelper(httpProvider, null!, new FakeIWebLogger()); + var httpClientHelper = new HttpClientHelper(httpProvider, null!, new FakeIWebLogger(), + new AppSettings{ AllowedHttpsDomains = new List{"qdraw.nl"}}); var packageTelemetry = new PackageTelemetry(httpClientHelper, new AppSettings(), new FakeIWebLogger(), new FakeIQuery(), new FakeIDeviceIdService()); var sourceValue = Environment.GetEnvironmentVariable("DOTNET_RUNNING_IN_CONTAINER"); @@ -87,7 +89,8 @@ public void GetSystemDataTestDocker() public void GetSystemDataTestDocker_NonLinux_soFalse() { var httpProvider = new FakeIHttpProvider(); - var httpClientHelper = new HttpClientHelper(httpProvider, null!, new FakeIWebLogger()); + var httpClientHelper = new HttpClientHelper(httpProvider, null!, new FakeIWebLogger(), + new AppSettings{ AllowedHttpsDomains = new List{"qdraw.nl"}}); var packageTelemetry = new PackageTelemetry(httpClientHelper, new AppSettings(), new FakeIWebLogger(), new FakeIQuery(), new FakeIDeviceIdService()); var sourceValue = Environment.GetEnvironmentVariable("DOTNET_RUNNING_IN_CONTAINER"); @@ -112,7 +115,8 @@ public void GetSystemDataTestDocker_NonLinux_soFalse() public void GetSystemDataTestDocker_WebSiteName() { var httpProvider = new FakeIHttpProvider(); - var httpClientHelper = new HttpClientHelper(httpProvider, null!, new FakeIWebLogger()); + var httpClientHelper = new HttpClientHelper(httpProvider, null!, new FakeIWebLogger(), + new AppSettings{ AllowedHttpsDomains = new List{"qdraw.nl"}}); var packageTelemetry = new PackageTelemetry(httpClientHelper, new AppSettings(), new FakeIWebLogger(), new FakeIQuery(), new FakeIDeviceIdService()); var sourceValue = Environment.GetEnvironmentVariable("WEBSITE_SITE_NAME"); @@ -170,6 +174,7 @@ public void GetPropValue_Object_TestClass() private class PropValueTestClass { + // ReSharper disable once UnusedAutoPropertyAccessor.Local public string Test { get; set; } } @@ -179,14 +184,14 @@ public void GetPropValue_ReadValue() var result = PackageTelemetry.GetPropValue(new PropValueTestClass{Test = "1"}, "Test"); Assert.AreEqual("1",result); } - [TestMethod] public void AddAppSettingsData() { var httpProvider = new FakeIHttpProvider(); var appSettings = new AppSettings(); - var httpClientHelper = new HttpClientHelper(httpProvider, null!, new FakeIWebLogger()); + var httpClientHelper = new HttpClientHelper(httpProvider, null!, new FakeIWebLogger(), + new AppSettings{ AllowedHttpsDomains = new List{"qdraw.nl"}}); var packageTelemetry = new PackageTelemetry(httpClientHelper, appSettings, new FakeIWebLogger(), new FakeIQuery(), new FakeIDeviceIdService()); var result = packageTelemetry.AddAppSettingsData(new List>()); @@ -198,7 +203,8 @@ public async Task PackageTelemetrySend_Disabled() { var httpProvider = new FakeIHttpProvider(); var appSettings = new AppSettings{EnablePackageTelemetry = false}; - var httpClientHelper = new HttpClientHelper(httpProvider, null!, new FakeIWebLogger()); + var httpClientHelper = new HttpClientHelper(httpProvider, null!, new FakeIWebLogger(), + new AppSettings{ AllowedHttpsDomains = new List{"qdraw.nl"}}); var packageTelemetry = new PackageTelemetry(httpClientHelper, appSettings, new FakeIWebLogger(), new FakeIQuery(), new FakeIDeviceIdService()); var result = await packageTelemetry.PackageTelemetrySend(); Assert.IsNull(result); @@ -213,7 +219,8 @@ public async Task PackageTelemetrySend_HasSend() {"https://" + PackageTelemetry.PackageTelemetryUrl,new StringContent(string.Empty)} }); var appSettings = new AppSettings{EnablePackageTelemetry = true}; - var httpClientHelper = new HttpClientHelper(httpProvider, null!, new FakeIWebLogger()); + var httpClientHelper = new HttpClientHelper(httpProvider, null!, new FakeIWebLogger(), + new AppSettings{ AllowedHttpsDomains = new List{"qdraw.nl"}}); var packageTelemetry = new PackageTelemetry(httpClientHelper, appSettings, new FakeIWebLogger(), new FakeIQuery(), new FakeIDeviceIdService()); var result = await packageTelemetry.PackageTelemetrySend(); Assert.IsTrue(result); @@ -228,7 +235,8 @@ public async Task PackageTelemetrySend_False_EnablePackageTelemetryDebug() {"https://" + PackageTelemetry.PackageTelemetryUrl,new StringContent(string.Empty)} }); var appSettings = new AppSettings{EnablePackageTelemetry = true, EnablePackageTelemetryDebug = true}; - var httpClientHelper = new HttpClientHelper(httpProvider, null!, new FakeIWebLogger()); + var httpClientHelper = new HttpClientHelper(httpProvider, null!, new FakeIWebLogger(), + new AppSettings{ AllowedHttpsDomains = new List{"qdraw.nl"}}); var packageTelemetry = new PackageTelemetry(httpClientHelper, appSettings, new FakeIWebLogger(), new FakeIQuery(), new FakeIDeviceIdService()); var result = await packageTelemetry.PackageTelemetrySend(); Assert.IsNull(result); diff --git a/starsky/starskytest/starsky.foundation.writemeta/Helpers/ExifToolDownloadTest.cs b/starsky/starskytest/starsky.foundation.writemeta/Helpers/ExifToolDownloadTest.cs index 7c3dad3b00..9217109260 100644 --- a/starsky/starskytest/starsky.foundation.writemeta/Helpers/ExifToolDownloadTest.cs +++ b/starsky/starskytest/starsky.foundation.writemeta/Helpers/ExifToolDownloadTest.cs @@ -84,7 +84,8 @@ public async Task DownloadCheckSums_BaseChecksumDoesExist() { {"https://exiftool.org/checksums.txt", new StringContent(ExampleCheckSum)}, }); - var httpClientHelper = new HttpClientHelper(fakeIHttpProvider, _serviceScopeFactory, new FakeIWebLogger()); + var httpClientHelper = new HttpClientHelper(fakeIHttpProvider, _serviceScopeFactory, new FakeIWebLogger(), + new AppSettings{ AllowedHttpsDomains = new List{"qdraw.nl"}}); // Happy flow var result = await new ExifToolDownload(httpClientHelper,_appSettings, new FakeIWebLogger() ) @@ -101,7 +102,8 @@ public async Task DownloadCheckSums_BaseChecksumDoesNotExist() { {"https://qdraw.nl/special/mirror/exiftool/checksums.txt", new StringContent(ExampleCheckSum)}, }); - var httpClientHelper = new HttpClientHelper(fakeIHttpProvider, _serviceScopeFactory, new FakeIWebLogger()); + var httpClientHelper = new HttpClientHelper(fakeIHttpProvider, _serviceScopeFactory, new FakeIWebLogger(), + new AppSettings{ AllowedHttpsDomains = new List{"qdraw.nl"}}); // Main source is down, but mirror is up var result = await new ExifToolDownload(httpClientHelper,_appSettings, new FakeIWebLogger() ) @@ -116,7 +118,8 @@ public async Task DownloadCheckSums_BothServicesAreDown() { // Main & Mirror source are down var fakeIHttpProvider = new FakeIHttpProvider(); - var httpClientHelper = new HttpClientHelper(fakeIHttpProvider, _serviceScopeFactory, new FakeIWebLogger()); + var httpClientHelper = new HttpClientHelper(fakeIHttpProvider, _serviceScopeFactory, new FakeIWebLogger(), + new AppSettings{ AllowedHttpsDomains = new List{"qdraw.nl"}}); // Main & Mirror source are down var result = await new ExifToolDownload(httpClientHelper,_appSettings, new FakeIWebLogger() ) @@ -134,7 +137,8 @@ public async Task GetExifToolByOs() {"https://exiftool.org/exiftool-11.99.zip", new ByteArrayContent(CreateAnExifToolWindows.Bytes)}, {"https://exiftool.org/Image-ExifTool-11.99.tar.gz", new ByteArrayContent(CreateAnExifToolTarGz.Bytes)}, }); - var httpClientHelper = new HttpClientHelper(fakeIHttpProvider, _serviceScopeFactory, new FakeIWebLogger()); + var httpClientHelper = new HttpClientHelper(fakeIHttpProvider, _serviceScopeFactory, new FakeIWebLogger(), + new AppSettings{ AllowedHttpsDomains = new List{"exiftool.org"}}); var result = await new ExifToolDownload(httpClientHelper,_appSettings, new FakeIWebLogger() ).DownloadExifTool(_appSettings.IsWindows); @@ -196,7 +200,8 @@ public async Task RunChmodOnExifToolUnixExe_TempFolderWithSpace_UnixOnly() {"https://exiftool.org/exiftool-11.99.zip", new ByteArrayContent(CreateAnExifToolWindows.Bytes)}, {"https://exiftool.org/Image-ExifTool-11.99.tar.gz", new ByteArrayContent(CreateAnExifToolTarGz.Bytes)}, }); - var httpClientHelper = new HttpClientHelper(fakeIHttpProvider, _serviceScopeFactory, new FakeIWebLogger()); + var httpClientHelper = new HttpClientHelper(fakeIHttpProvider, _serviceScopeFactory, new FakeIWebLogger(), + new AppSettings{ AllowedHttpsDomains = new List{"qdraw.nl"}}); var appSettings = await CreateTempFolderWithExifTool(); Console.WriteLine(appSettings.DependenciesFolder); @@ -229,7 +234,8 @@ public async Task RunChmodOnExifToolUnixExe_Chmod644_UnixOnly() {"https://exiftool.org/exiftool-11.99.zip", new ByteArrayContent(CreateAnExifToolWindows.Bytes)}, {"https://exiftool.org/Image-ExifTool-11.99.tar.gz", new ByteArrayContent(CreateAnExifToolTarGz.Bytes)}, }); - var httpClientHelper = new HttpClientHelper(fakeIHttpProvider, _serviceScopeFactory, new FakeIWebLogger()); + var httpClientHelper = new HttpClientHelper(fakeIHttpProvider, _serviceScopeFactory, new FakeIWebLogger(), + new AppSettings{ AllowedHttpsDomains = new List{"qdraw.nl"}}); await CreateTempFolderWithExifTool("starsky-tmp-dependencies-4835793"); // make NOT executable 644 @@ -243,15 +249,13 @@ public async Task RunChmodOnExifToolUnixExe_Chmod644_UnixOnly() RemoveTempFolderWithExifTool("starsky-tmp-dependencies-4835793"); Assert.IsTrue(result); - - - } [TestMethod] public async Task DownloadExifTool_Windows() { - var httpClientHelper = new HttpClientHelper(new FakeIHttpProvider(), _serviceScopeFactory, new FakeIWebLogger()); + var httpClientHelper = new HttpClientHelper(new FakeIHttpProvider(), _serviceScopeFactory, new FakeIWebLogger(), + new AppSettings{ AllowedHttpsDomains = new List{"qdraw.nl"}}); var result = await new ExifToolDownload(httpClientHelper,_appSettings, new FakeIWebLogger() ).DownloadExifTool(true); @@ -262,7 +266,8 @@ public async Task DownloadExifTool_Windows() [TestMethod] public async Task DownloadExifTool_Skip_AddSwaggerExportExitAfter() { - var httpClientHelper = new HttpClientHelper(new FakeIHttpProvider(), _serviceScopeFactory, new FakeIWebLogger()); + var httpClientHelper = new HttpClientHelper(new FakeIHttpProvider(), _serviceScopeFactory, new FakeIWebLogger(), + new AppSettings{ AllowedHttpsDomains = new List{"qdraw.nl"}}); var appSettings = new AppSettings { @@ -279,7 +284,8 @@ public async Task DownloadExifTool_Skip_AddSwaggerExportExitAfter() [TestMethod] public async Task DownloadExifTool_Skip_ExiftoolSkipDownloadOnStartup() { - var httpClientHelper = new HttpClientHelper(new FakeIHttpProvider(), _serviceScopeFactory, new FakeIWebLogger()); + var httpClientHelper = new HttpClientHelper(new FakeIHttpProvider(), _serviceScopeFactory, new FakeIWebLogger(), + new AppSettings{ AllowedHttpsDomains = new List{"qdraw.nl"}}); var appSettings = new AppSettings { @@ -294,7 +300,8 @@ public async Task DownloadExifTool_Skip_ExiftoolSkipDownloadOnStartup() [TestMethod] public async Task DownloadExifTool_Unix() { - var httpClientHelper = new HttpClientHelper(new FakeIHttpProvider(), _serviceScopeFactory, new FakeIWebLogger()); + var httpClientHelper = new HttpClientHelper(new FakeIHttpProvider(), _serviceScopeFactory, new FakeIWebLogger(), + new AppSettings{ AllowedHttpsDomains = new List{"qdraw.nl"}}); Directory.Delete(_appSettings.DependenciesFolder,true); var result = await new ExifToolDownload(httpClientHelper,_appSettings, new FakeIWebLogger() ) .DownloadExifTool(false); @@ -317,7 +324,8 @@ public async Task DownloadExifTool_Windows_existVerbose() await new StorageHostFullPathFilesystem().WriteStreamAsync(stream, Path.Combine(appSettings.DependenciesFolder, "exiftool-windows", "exiftool.exe")); - var httpClientHelper = new HttpClientHelper(new FakeIHttpProvider(), _serviceScopeFactory, new FakeIWebLogger()); + var httpClientHelper = new HttpClientHelper(new FakeIHttpProvider(), _serviceScopeFactory, new FakeIWebLogger(), + new AppSettings{ AllowedHttpsDomains = new List{"qdraw.nl"}}); var logger = new FakeIWebLogger(); var result = await new ExifToolDownload(httpClientHelper,appSettings,logger).DownloadExifTool(true); @@ -342,7 +350,8 @@ public async Task DownloadExifTool_existVerbose_UnixOnly() var appSettings = await CreateTempFolderWithExifTool("test32"); appSettings.Verbose = true; - var httpClientHelper = new HttpClientHelper(new FakeIHttpProvider(), _serviceScopeFactory, new FakeIWebLogger()); + var httpClientHelper = new HttpClientHelper(new FakeIHttpProvider(), _serviceScopeFactory, new FakeIWebLogger(), + new AppSettings{ AllowedHttpsDomains = new List{"qdraw.nl"}}); var logger = new FakeIWebLogger(); var result = await new ExifToolDownload(httpClientHelper,appSettings,logger).DownloadExifTool(false,3); @@ -364,7 +373,8 @@ public async Task StartDownloadForWindows_2Times() {"https://exiftool.org/exiftool-11.99.zip", new ByteArrayContent(CreateAnExifToolWindows.Bytes)} }); - var httpClientHelper = new HttpClientHelper(fakeIHttpProvider, _serviceScopeFactory, new FakeIWebLogger()); + var httpClientHelper = new HttpClientHelper(fakeIHttpProvider, _serviceScopeFactory, new FakeIWebLogger(), + new AppSettings{ AllowedHttpsDomains = new List{"qdraw.nl"}}); var result = await new ExifToolDownload(httpClientHelper,_appSettings, new FakeIWebLogger() ).StartDownloadForWindows(); Assert.IsTrue(result); @@ -375,7 +385,8 @@ public async Task StartDownloadForWindows_2Times() {"https://exiftool.org/checksums.txt", new StringContent(ExampleCheckSum)}, {"https://exiftool.org/exiftool-11.99.zip", new ByteArrayContent(CreateAnExifToolWindows.Bytes)} }); - var httpClientHelper2 = new HttpClientHelper(fakeIHttpProvider2, _serviceScopeFactory, new FakeIWebLogger()); + var httpClientHelper2 = new HttpClientHelper(fakeIHttpProvider2, _serviceScopeFactory, new FakeIWebLogger(), + new AppSettings{ AllowedHttpsDomains = new List{"qdraw.nl"}}); var result2 = await new ExifToolDownload(httpClientHelper2,_appSettings, new FakeIWebLogger() ).StartDownloadForWindows(); Assert.IsTrue(result2); @@ -391,7 +402,8 @@ public async Task StartDownloadForWindows_Fail() {"https://exiftool.org/checksums.txt", new StringContent(ExampleCheckSum)}, }); - var httpClientHelper = new HttpClientHelper(fakeIHttpProvider, _serviceScopeFactory, new FakeIWebLogger()); + var httpClientHelper = new HttpClientHelper(fakeIHttpProvider, _serviceScopeFactory, new FakeIWebLogger(), + new AppSettings{ AllowedHttpsDomains = new List{"qdraw.nl"}}); await new ExifToolDownload(httpClientHelper, _appSettings, new FakeIWebLogger()). StartDownloadForWindows(); } @@ -405,7 +417,8 @@ public async Task StartDownloadForUnix_Fail() {"https://exiftool.org/checksums.txt", new StringContent(ExampleCheckSum)}, }); - var httpClientHelper = new HttpClientHelper(fakeIHttpProvider, _serviceScopeFactory, new FakeIWebLogger()); + var httpClientHelper = new HttpClientHelper(fakeIHttpProvider, _serviceScopeFactory, new FakeIWebLogger(), + new AppSettings{ AllowedHttpsDomains = new List{"qdraw.nl"}}); await new ExifToolDownload(httpClientHelper, _appSettings, new FakeIWebLogger()). StartDownloadForUnix(); } @@ -421,7 +434,8 @@ public async Task StartDownloadForUnix_2Times() _appSettings.Verbose = true; - var httpClientHelper = new HttpClientHelper(fakeIHttpProvider, _serviceScopeFactory, new FakeIWebLogger()); + var httpClientHelper = new HttpClientHelper(fakeIHttpProvider, _serviceScopeFactory, new FakeIWebLogger(), + new AppSettings{ AllowedHttpsDomains = new List{"qdraw.nl"}}); var result = await new ExifToolDownload(httpClientHelper,_appSettings, new FakeIWebLogger() ).StartDownloadForUnix(); Assert.IsTrue(result); @@ -432,7 +446,8 @@ public async Task StartDownloadForUnix_2Times() {"https://exiftool.org/checksums.txt", new StringContent(ExampleCheckSum)}, {"https://exiftool.org/Image-ExifTool-11.99.tar.gz", new ByteArrayContent(CreateAnExifToolTarGz.Bytes)} }); - var httpClientHelper2 = new HttpClientHelper(fakeIHttpProvider2, _serviceScopeFactory, new FakeIWebLogger()); + var httpClientHelper2 = new HttpClientHelper(fakeIHttpProvider2, _serviceScopeFactory, new FakeIWebLogger(), + new AppSettings{ AllowedHttpsDomains = new List{"qdraw.nl"}}); var result2 = await new ExifToolDownload(httpClientHelper2,_appSettings, new FakeIWebLogger() ).StartDownloadForUnix(); Assert.IsTrue(result2); @@ -472,7 +487,8 @@ public async Task StartDownloadForUnix_WrongHash() {"https://exiftool.org/checksums.txt", new StringContent(ExampleCheckSum)}, {"https://exiftool.org/Image-ExifTool-11.99.tar.gz", new StringContent("FAIL")} }); - var httpClientHelper = new HttpClientHelper(fakeIHttpProvider, _serviceScopeFactory, new FakeIWebLogger()); + var httpClientHelper = new HttpClientHelper(fakeIHttpProvider, _serviceScopeFactory, new FakeIWebLogger(), + new AppSettings{ AllowedHttpsDomains = new List{"qdraw.nl"}}); var result = await new ExifToolDownload(httpClientHelper,_appSettings, new FakeIWebLogger() ).StartDownloadForUnix(); Assert.IsFalse(result); } @@ -488,7 +504,8 @@ public async Task StartDownloadForWindows_WrongHash() "https://exiftool.org/exiftool-11.99.zip", new StringContent("FAIL") } }); - var httpClientHelper = new HttpClientHelper(fakeIHttpProvider, _serviceScopeFactory, new FakeIWebLogger()); + var httpClientHelper = new HttpClientHelper(fakeIHttpProvider, _serviceScopeFactory, new FakeIWebLogger(), + new AppSettings{ AllowedHttpsDomains = new List{"qdraw.nl"}}); var result = await new ExifToolDownload(httpClientHelper,_appSettings, new FakeIWebLogger() ).StartDownloadForWindows(); Assert.IsFalse(result); } @@ -501,7 +518,8 @@ public async Task DownloadForUnix_FromMirrorInsteadOfMainSource() {"https://qdraw.nl/special/mirror/exiftool/exiftool-11.99.zip", new StringContent("FAIL")}, {"https://qdraw.nl/special/mirror/exiftool/Image-ExifTool-11.99.tar.gz", new StringContent("FAIL")} }); - var httpClientHelper = new HttpClientHelper(fakeIHttpProvider, _serviceScopeFactory, new FakeIWebLogger()); + var httpClientHelper = new HttpClientHelper(fakeIHttpProvider, _serviceScopeFactory, new FakeIWebLogger(), + new AppSettings{ AllowedHttpsDomains = new List{"qdraw.nl"}}); try { @@ -528,7 +546,8 @@ public async Task DownloadForWindows_FromMirrorInsteadOfMainSource() {"https://qdraw.nl/special/mirror/exiftool/exiftool-11.99.zip", new StringContent("FAIL")}, {"https://qdraw.nl/special/mirror/exiftool/Image-ExifTool-11.99.tar.gz", new StringContent("FAIL")} }); - var httpClientHelper = new HttpClientHelper(fakeIHttpProvider, _serviceScopeFactory, new FakeIWebLogger()); + var httpClientHelper = new HttpClientHelper(fakeIHttpProvider, _serviceScopeFactory, new FakeIWebLogger(), + new AppSettings{ AllowedHttpsDomains = new List{"qdraw.nl"}}); try { @@ -559,7 +578,8 @@ public void GetChecksumsFromTextFile_ToManyShaResults() "MD5 (exiftool-12.40.zip) = fc834fd43d79da19fcb6461fb791b275\n" + "MD5 (ExifTool-12.40.dmg) = b30e391a4b53564de60a72f4347cade4\n"; - var httpClientHelper = new HttpClientHelper(fakeIHttpProvider, _serviceScopeFactory, new FakeIWebLogger()); + var httpClientHelper = new HttpClientHelper(fakeIHttpProvider, _serviceScopeFactory, new FakeIWebLogger(), + new AppSettings{ AllowedHttpsDomains = new List{"qdraw.nl"}}); var exifToolDownload = new ExifToolDownload(httpClientHelper, new AppSettings(), new FakeIWebLogger(), new FakeIStorage()); @@ -581,7 +601,8 @@ public void GetChecksumsFromTextFile_Good() "MD5 (exiftool-12.40.zip) = fc834fd43d79da19fcb6461fb791b275\n" + "MD5 (ExifTool-12.40.dmg) = b30e391a4b53564de60a72f4347cade4\n"; - var httpClientHelper = new HttpClientHelper(fakeIHttpProvider, _serviceScopeFactory, new FakeIWebLogger()); + var httpClientHelper = new HttpClientHelper(fakeIHttpProvider, _serviceScopeFactory, new FakeIWebLogger(), + new AppSettings{ AllowedHttpsDomains = new List{"qdraw.nl"}}); var exifToolDownload = new ExifToolDownload(httpClientHelper, new AppSettings(), new FakeIWebLogger(), new FakeIStorage()); From 8ec7c2b4c175d8872c01fd8ff7a0bb73ac83956c Mon Sep 17 00:00:00 2001 From: Dion Date: Thu, 21 Dec 2023 14:47:52 +0100 Subject: [PATCH 4/7] WIP --- .../Helpers/DefaultGeoJson.cs | 14 + .../Models/GeoJson/FeatureCollectionModel.cs | 9 + .../Models/GeoJson/FeatureCollectionType.cs | 9 + .../Models/GeoJson/FeatureModel.cs | 7 + .../Models/GeoJson/GeometryModel.cs | 27 ++ .../Models/GeoJson/GeometryType.cs | 15 + .../CreateAnGeoJson/CreateAnGeoJson.json | 26 ++ .../FakeCreateAn/CreateAnGeoJson/example.html | 108 ++++++++ .../starskytest/FakeCreateAn/CreateAnGpx.cs | 261 +++++++++--------- .../FakeCreateAn/CreateAnGpx/CreateAnGpx.gpx | 124 +++++++++ .../Services/MetaUpdateServiceTest.cs | 3 + .../Models/GeometryModelTest.cs | 55 ++++ .../Models/ModelsParserTest.cs | 60 ++++ starsky/starskytest/starskytest.csproj | 1 + 14 files changed, 588 insertions(+), 131 deletions(-) create mode 100644 starsky/starsky.foundation.georealtime/Helpers/DefaultGeoJson.cs create mode 100644 starsky/starsky.foundation.georealtime/Models/GeoJson/FeatureCollectionModel.cs create mode 100644 starsky/starsky.foundation.georealtime/Models/GeoJson/FeatureCollectionType.cs create mode 100644 starsky/starsky.foundation.georealtime/Models/GeoJson/FeatureModel.cs create mode 100644 starsky/starsky.foundation.georealtime/Models/GeoJson/GeometryModel.cs create mode 100644 starsky/starsky.foundation.georealtime/Models/GeoJson/GeometryType.cs create mode 100644 starsky/starskytest/FakeCreateAn/CreateAnGeoJson/CreateAnGeoJson.json create mode 100644 starsky/starskytest/FakeCreateAn/CreateAnGeoJson/example.html create mode 100644 starsky/starskytest/FakeCreateAn/CreateAnGpx/CreateAnGpx.gpx create mode 100644 starsky/starskytest/starsky.foundation.georealtime/Models/GeometryModelTest.cs create mode 100644 starsky/starskytest/starsky.foundation.georealtime/Models/ModelsParserTest.cs diff --git a/starsky/starsky.foundation.georealtime/Helpers/DefaultGeoJson.cs b/starsky/starsky.foundation.georealtime/Helpers/DefaultGeoJson.cs new file mode 100644 index 0000000000..9a4b6e3f50 --- /dev/null +++ b/starsky/starsky.foundation.georealtime/Helpers/DefaultGeoJson.cs @@ -0,0 +1,14 @@ +using starsky.foundation.georealtime.Models.GeoJson; + +namespace starsky.foundation.georealtime.Helpers; + +public class DefaultGeoJson +{ + public void CreateDefaultGeoJson() + { + var featureCollection = new FeatureCollectionModel + { + Type = FeatureCollectionType.FeatureCollection + }; + } +} diff --git a/starsky/starsky.foundation.georealtime/Models/GeoJson/FeatureCollectionModel.cs b/starsky/starsky.foundation.georealtime/Models/GeoJson/FeatureCollectionModel.cs new file mode 100644 index 0000000000..16fce9c0bf --- /dev/null +++ b/starsky/starsky.foundation.georealtime/Models/GeoJson/FeatureCollectionModel.cs @@ -0,0 +1,9 @@ +using System.Collections.Generic; + +namespace starsky.foundation.georealtime.Models.GeoJson; + +public class FeatureCollectionModel +{ + public FeatureCollectionType? Type { get; set; } + public List Features { get; set; } +} diff --git a/starsky/starsky.foundation.georealtime/Models/GeoJson/FeatureCollectionType.cs b/starsky/starsky.foundation.georealtime/Models/GeoJson/FeatureCollectionType.cs new file mode 100644 index 0000000000..bcdcd11b2c --- /dev/null +++ b/starsky/starsky.foundation.georealtime/Models/GeoJson/FeatureCollectionType.cs @@ -0,0 +1,9 @@ +using System.Text.Json.Serialization; + +namespace starsky.foundation.georealtime.Models.GeoJson; + +[JsonConverter(typeof(JsonStringEnumConverter))] +public enum FeatureCollectionType +{ + FeatureCollection +} diff --git a/starsky/starsky.foundation.georealtime/Models/GeoJson/FeatureModel.cs b/starsky/starsky.foundation.georealtime/Models/GeoJson/FeatureModel.cs new file mode 100644 index 0000000000..933feabfc1 --- /dev/null +++ b/starsky/starsky.foundation.georealtime/Models/GeoJson/FeatureModel.cs @@ -0,0 +1,7 @@ +namespace starsky.foundation.georealtime.Models.GeoJson; + +public class FeatureModel +{ + public string Type { get; set; } + public GeometryModel Geometry { get; set; } +} diff --git a/starsky/starsky.foundation.georealtime/Models/GeoJson/GeometryModel.cs b/starsky/starsky.foundation.georealtime/Models/GeoJson/GeometryModel.cs new file mode 100644 index 0000000000..fc01201130 --- /dev/null +++ b/starsky/starsky.foundation.georealtime/Models/GeoJson/GeometryModel.cs @@ -0,0 +1,27 @@ +using System; +using System.Collections.Generic; +using System.Linq; + +namespace starsky.foundation.georealtime.Models.GeoJson; + +public class GeometryModel +{ + public GeometryType? Type { get; set; } + + private List> CoordinatesPrivate { get; set; } + + public List> Coordinates + { + get => CoordinatesPrivate; + set + { + if ( value.Any(coordinatesArray => coordinatesArray.Count != 2 && coordinatesArray.Count != 3) ) + { + throw new ArgumentException("Coordinates must be 2 or 3 dimensional"); + } + + CoordinatesPrivate = value; + } + } + +} diff --git a/starsky/starsky.foundation.georealtime/Models/GeoJson/GeometryType.cs b/starsky/starsky.foundation.georealtime/Models/GeoJson/GeometryType.cs new file mode 100644 index 0000000000..8f6ca8d74b --- /dev/null +++ b/starsky/starsky.foundation.georealtime/Models/GeoJson/GeometryType.cs @@ -0,0 +1,15 @@ +using System.Text.Json.Serialization; + +namespace starsky.foundation.georealtime.Models.GeoJson; + +[JsonConverter(typeof(JsonStringEnumConverter))] +public enum GeometryType +{ + Point, + MultiPoint, + LineString, + MultiLineString, + Polygon, + MultiPolygon, + GeometryCollection +} diff --git a/starsky/starskytest/FakeCreateAn/CreateAnGeoJson/CreateAnGeoJson.json b/starsky/starskytest/FakeCreateAn/CreateAnGeoJson/CreateAnGeoJson.json new file mode 100644 index 0000000000..b23c0619d7 --- /dev/null +++ b/starsky/starskytest/FakeCreateAn/CreateAnGeoJson/CreateAnGeoJson.json @@ -0,0 +1,26 @@ +{ + "type": "FeatureCollection", + "features": [ + { + "type": "Feature", + "geometry": { + "type": "LineString", + "coordinates": [ + [5.485941, 51.809360, 7.263], + [5.485724, 51.807968, 7.772], + [5.485631, 51.807019, 9.957], + [5.485610, 51.806702, 10.808], + [5.485633, 51.805663, 10.866], + [5.485738, 51.805500, 9.242], + [5.486056, 51.805115, 9.122] + ] + }, + "properties": { + "name": "_20180905-fietsen-oss", + "time": "2018-09-11T19:41:10.482Z", + "activity": "biking", + "displayColor": "DarkRed" + } + } + ] +} diff --git a/starsky/starskytest/FakeCreateAn/CreateAnGeoJson/example.html b/starsky/starskytest/FakeCreateAn/CreateAnGeoJson/example.html new file mode 100644 index 0000000000..2694aa5bf2 --- /dev/null +++ b/starsky/starskytest/FakeCreateAn/CreateAnGeoJson/example.html @@ -0,0 +1,108 @@ + + + + + + + + GeoJSON tutorial - Leaflet + + + + + + + + + + +
+ + + + + + + + diff --git a/starsky/starskytest/FakeCreateAn/CreateAnGpx.cs b/starsky/starskytest/FakeCreateAn/CreateAnGpx.cs index e021a9754a..bb609bbde3 100644 --- a/starsky/starskytest/FakeCreateAn/CreateAnGpx.cs +++ b/starsky/starskytest/FakeCreateAn/CreateAnGpx.cs @@ -4,7 +4,6 @@ using System.IO; using System.Reflection; using starsky.foundation.platform.Helpers; -using starskycore.Helpers; namespace starskytest.FakeCreateAn { @@ -23,136 +22,136 @@ public class CreateAnGpx public readonly string BasePath = Path.GetDirectoryName(Assembly.GetEntryAssembly()?.Location) + Path.DirectorySeparatorChar; - private static readonly string Base64GpxString = "CjxncHggeG1sbnM9Imh0dHA6Ly93d3cudG9wb2dyYWZpeC5jb20vR1BYLzEvMSIg" + - "eG1sbnM6Z3B4eD0iaHR0cDovL3d3dy5nYXJtaW4uY29tL3htbHNjaGVtYXMvR3B4" + - "RXh0ZW5zaW9ucy92MyIgeG1sbnM6Z3B4dHB4PSJodHRwOi8vd3d3OC5nYXJtaW4u" + - "Y29tL3htbHNjaGVtYXMvVHJhY2tQb2ludEV4dGVuc2lvbnYyLnhzZCIgeG1sbnM6" + - "dHJhaWxzaW89Imh0dHA6Ly90cmFpbHMuaW8vR1BYLzEvMCIgeG1sbnM6eHNpPSJo" + - "dHRwOi8vd3d3LnczLm9yZy8yMDAxL1hNTFNjaGVtYS1pbnN0YW5jZSIgdmVyc2lv" + - "bj0iMS4xIiBjcmVhdG9yPSJBZHplIC0gaHR0cDovL2tvYm90c3cuY29tL2FwcHMv" + - "YWR6ZSIgeHNpOnNjaGVtYUxvY2F0aW9uPSJodHRwOi8vd3d3LnRvcG9ncmFmaXgu" + - "Y29tL0dQWC8xLzEgaHR0cDovL3d3dy50b3BvZ3JhZml4LmNvbS9HUFgvMS8xL2dw" + - "eC54c2QgaHR0cDovL3d3dy5nYXJtaW4uY29tL3htbHNjaGVtYXMvR3B4RXh0ZW5z" + - "aW9ucy92MyBodHRwOi8vd3d3Lmdhcm1pbi5jb20veG1sc2NoZW1hcy9HcHhFeHRl" + - "bnNpb25zdjMueHNkIGh0dHA6Ly90cmFpbHMuaW8vR1BYLzEvMCBodHRwczovL3Ry" + - "YWlscy5pby9HUFgvMS8wL3RyYWlsc18xLjAueHNkIj4KICAgIDxtZXRhZGF0YT4K" + - "ICAgICAgICA8bmFtZT5VbnRpdGxlZCBEb2N1bWVudDwvbmFtZT4KICAgICAgICA8" + - "dGltZT4yMDE4LTA5LTExVDE5OjQxOjEwLjQ4Mlo8L3RpbWU+CiAgICA8L21ldGFk" + - "YXRhPgogICAgPHRyaz4KICAgICAgICA8bmFtZT5fMjAxODA5MDUtZmlldHNlbi1v" + - "c3M8L25hbWU+CiAgICAgICAgPGV4dGVuc2lvbnM+CiAgICAgICAgICAgIDx0cmFp" + - "bHNpbzpUcmFja0V4dGVuc2lvbj4KICAgICAgICAgICAgICAgIDx0cmFpbHNpbzph" + - "Y3Rpdml0eT5iaWtpbmc8L3RyYWlsc2lvOmFjdGl2aXR5PgogICAgICAgICAgICA8" + - "L3RyYWlsc2lvOlRyYWNrRXh0ZW5zaW9uPgogICAgICAgICAgICA8Z3B4eDpUcmFj" + - "a0V4dGVuc2lvbj4KICAgICAgICAgICAgICAgIDxncHh4OkRpc3BsYXlDb2xvcj5E" + - "YXJrUmVkPC9ncHh4OkRpc3BsYXlDb2xvcj4KICAgICAgICAgICAgPC9ncHh4OlRy" + - "YWNrRXh0ZW5zaW9uPgogICAgICAgIDwvZXh0ZW5zaW9ucz4KICAgICAgICA8dHJr" + - "c2VnPgogICAgICAgICAgICA8dHJrcHQgbG9uPSI1LjQ4NTk0MSIgbGF0PSI1MS44" + - "MDkzNjAiPgogICAgICAgICAgICAgICAgPGVsZT43LjI2MzAwMDwvZWxlPgogICAg" + - "ICAgICAgICAgICAgPHRpbWU+MjAxOC0wOS0wNVQxNzozMTo1M1o8L3RpbWU+CiAg" + - "ICAgICAgICAgICAgICA8ZXh0ZW5zaW9ucz4KICAgICAgICAgICAgICAgICAgICA8" + - "Z3B4dHB4OlRyYWNrUG9pbnRFeHRlbnNpb24+CiAgICAgICAgICAgICAgICAgICAg" + - "ICAgIDxncHh0cHg6c3BlZWQ+NS40OTwvZ3B4dHB4OnNwZWVkPgogICAgICAgICAg" + - "ICAgICAgICAgICAgICA8Z3B4dHB4OmNvdXJzZT4xODkuNDk8L2dweHRweDpjb3Vy" + - "c2U+CiAgICAgICAgICAgICAgICAgICAgPC9ncHh0cHg6VHJhY2tQb2ludEV4dGVu" + - "c2lvbj4KICAgICAgICAgICAgICAgICAgICA8dHJhaWxzaW86VHJhY2tQb2ludEV4" + - "dGVuc2lvbj4KICAgICAgICAgICAgICAgICAgICAgICAgPHRyYWlsc2lvOmhhY2M+" + - "NS4wMDwvdHJhaWxzaW86aGFjYz4KICAgICAgICAgICAgICAgICAgICAgICAgPHRy" + - "YWlsc2lvOnZhY2M+NC4wMDwvdHJhaWxzaW86dmFjYz4KICAgICAgICAgICAgICAg" + - "ICAgICAgICAgPHRyYWlsc2lvOnN0ZXBzPjU1MTE8L3RyYWlsc2lvOnN0ZXBzPgog" + - "ICAgICAgICAgICAgICAgICAgIDwvdHJhaWxzaW86VHJhY2tQb2ludEV4dGVuc2lv" + - "bj4KICAgICAgICAgICAgICAgIDwvZXh0ZW5zaW9ucz4KICAgICAgICAgICAgPC90" + - "cmtwdD4KICAgICAgICAgICAgPHRya3B0IGxvbj0iNS40ODU3MjQiIGxhdD0iNTEu" + - "ODA3OTY4Ij4KICAgICAgICAgICAgICAgIDxlbGU+Ny43NzIwMDA8L2VsZT4KICAg" + - "ICAgICAgICAgICAgIDx0aW1lPjIwMTgtMDktMDVUMTc6MzI6MjFaPC90aW1lPgog" + - "ICAgICAgICAgICAgICAgPGV4dGVuc2lvbnM+CiAgICAgICAgICAgICAgICAgICAg" + - "PGdweHRweDpUcmFja1BvaW50RXh0ZW5zaW9uPgogICAgICAgICAgICAgICAgICAg" + - "ICAgICA8Z3B4dHB4OnNwZWVkPjQuNTk8L2dweHRweDpzcGVlZD4KICAgICAgICAg" + - "ICAgICAgICAgICAgICAgPGdweHRweDpjb3Vyc2U+MTg1LjYyPC9ncHh0cHg6Y291" + - "cnNlPgogICAgICAgICAgICAgICAgICAgIDwvZ3B4dHB4OlRyYWNrUG9pbnRFeHRl" + - "bnNpb24+CiAgICAgICAgICAgICAgICAgICAgPHRyYWlsc2lvOlRyYWNrUG9pbnRF" + - "eHRlbnNpb24+CiAgICAgICAgICAgICAgICAgICAgICAgIDx0cmFpbHNpbzpoYWNj" + - "PjUuMDA8L3RyYWlsc2lvOmhhY2M+CiAgICAgICAgICAgICAgICAgICAgICAgIDx0" + - "cmFpbHNpbzp2YWNjPjQuMDA8L3RyYWlsc2lvOnZhY2M+CiAgICAgICAgICAgICAg" + - "ICAgICAgICAgIDx0cmFpbHNpbzpzdGVwcz41NTUyPC90cmFpbHNpbzpzdGVwcz4K" + - "ICAgICAgICAgICAgICAgICAgICA8L3RyYWlsc2lvOlRyYWNrUG9pbnRFeHRlbnNp" + - "b24+CiAgICAgICAgICAgICAgICA8L2V4dGVuc2lvbnM+CiAgICAgICAgICAgIDwv" + - "dHJrcHQ+CiAgICAgICAgICAgIDx0cmtwdCBsb249IjUuNDg1NjMxIiBsYXQ9IjUx" + - "LjgwNzAxOSI+CiAgICAgICAgICAgICAgICA8ZWxlPjkuOTU3MDAwPC9lbGU+CiAg" + - "ICAgICAgICAgICAgICA8dGltZT4yMDE4LTA5LTA1VDE3OjMyOjQzWjwvdGltZT4K" + - "ICAgICAgICAgICAgICAgIDxleHRlbnNpb25zPgogICAgICAgICAgICAgICAgICAg" + - "IDxncHh0cHg6VHJhY2tQb2ludEV4dGVuc2lvbj4KICAgICAgICAgICAgICAgICAg" + - "ICAgICAgPGdweHRweDpzcGVlZD40LjYzPC9ncHh0cHg6c3BlZWQ+CiAgICAgICAg" + - "ICAgICAgICAgICAgICAgIDxncHh0cHg6Y291cnNlPjE4MC43MDwvZ3B4dHB4OmNv" + - "dXJzZT4KICAgICAgICAgICAgICAgICAgICA8L2dweHRweDpUcmFja1BvaW50RXh0" + - "ZW5zaW9uPgogICAgICAgICAgICAgICAgICAgIDx0cmFpbHNpbzpUcmFja1BvaW50" + - "RXh0ZW5zaW9uPgogICAgICAgICAgICAgICAgICAgICAgICA8dHJhaWxzaW86aGFj" + - "Yz41LjAwPC90cmFpbHNpbzpoYWNjPgogICAgICAgICAgICAgICAgICAgICAgICA8" + - "dHJhaWxzaW86dmFjYz4zLjAwPC90cmFpbHNpbzp2YWNjPgogICAgICAgICAgICAg" + - "ICAgICAgICAgICA8dHJhaWxzaW86c3RlcHM+NTU1NjwvdHJhaWxzaW86c3RlcHM+" + - "CiAgICAgICAgICAgICAgICAgICAgPC90cmFpbHNpbzpUcmFja1BvaW50RXh0ZW5z" + - "aW9uPgogICAgICAgICAgICAgICAgPC9leHRlbnNpb25zPgogICAgICAgICAgICA8" + - "L3Rya3B0PgogICAgICAgICAgICA8dHJrcHQgbG9uPSI1LjQ4NTYxMCIgbGF0PSI1" + - "MS44MDY3MDIiPgogICAgICAgICAgICAgICAgPGVsZT4xMC44MDgwMDA8L2VsZT4K" + - "ICAgICAgICAgICAgICAgIDx0aW1lPjIwMTgtMDktMDVUMTc6MzI6NTBaPC90aW1l" + - "PgogICAgICAgICAgICAgICAgPGV4dGVuc2lvbnM+CiAgICAgICAgICAgICAgICAg" + - "ICAgPGdweHRweDpUcmFja1BvaW50RXh0ZW5zaW9uPgogICAgICAgICAgICAgICAg" + - "ICAgICAgICA8Z3B4dHB4OnNwZWVkPjUuNjQ8L2dweHRweDpzcGVlZD4KICAgICAg" + - "ICAgICAgICAgICAgICAgICAgPGdweHRweDpjb3Vyc2U+MTg2LjMzPC9ncHh0cHg6" + - "Y291cnNlPgogICAgICAgICAgICAgICAgICAgIDwvZ3B4dHB4OlRyYWNrUG9pbnRF" + - "eHRlbnNpb24+CiAgICAgICAgICAgICAgICAgICAgPHRyYWlsc2lvOlRyYWNrUG9p" + - "bnRFeHRlbnNpb24+CiAgICAgICAgICAgICAgICAgICAgICAgIDx0cmFpbHNpbzpo" + - "YWNjPjUuMDA8L3RyYWlsc2lvOmhhY2M+CiAgICAgICAgICAgICAgICAgICAgICAg" + - "IDx0cmFpbHNpbzp2YWNjPjMuMDA8L3RyYWlsc2lvOnZhY2M+CiAgICAgICAgICAg" + - "ICAgICAgICAgICAgIDx0cmFpbHNpbzpzdGVwcz41NTY2PC90cmFpbHNpbzpzdGVw" + - "cz4KICAgICAgICAgICAgICAgICAgICA8L3RyYWlsc2lvOlRyYWNrUG9pbnRFeHRl" + - "bnNpb24+CiAgICAgICAgICAgICAgICA8L2V4dGVuc2lvbnM+CiAgICAgICAgICAg" + - "IDwvdHJrcHQ+CiAgICAgICAgICAgIDx0cmtwdCBsb249IjUuNDg1NjMzIiBsYXQ9" + - "IjUxLjgwNTY2MyI+CiAgICAgICAgICAgICAgICA8ZWxlPjEwLjg2NjAwMDwvZWxl" + - "PgogICAgICAgICAgICAgICAgPHRpbWU+MjAxOC0wOS0wNVQxNzozMzoxMVo8L3Rp" + - "bWU+CiAgICAgICAgICAgICAgICA8ZXh0ZW5zaW9ucz4KICAgICAgICAgICAgICAg" + - "ICAgICA8Z3B4dHB4OlRyYWNrUG9pbnRFeHRlbnNpb24+CiAgICAgICAgICAgICAg" + - "ICAgICAgICAgIDxncHh0cHg6c3BlZWQ+NS41ODwvZ3B4dHB4OnNwZWVkPgogICAg" + - "ICAgICAgICAgICAgICAgICAgICA8Z3B4dHB4OmNvdXJzZT4xNjkuMTA8L2dweHRw" + - "eDpjb3Vyc2U+CiAgICAgICAgICAgICAgICAgICAgPC9ncHh0cHg6VHJhY2tQb2lu" + - "dEV4dGVuc2lvbj4KICAgICAgICAgICAgICAgICAgICA8dHJhaWxzaW86VHJhY2tQ" + - "b2ludEV4dGVuc2lvbj4KICAgICAgICAgICAgICAgICAgICAgICAgPHRyYWlsc2lv" + - "OmhhY2M+NS4wMDwvdHJhaWxzaW86aGFjYz4KICAgICAgICAgICAgICAgICAgICAg" + - "ICAgPHRyYWlsc2lvOnZhY2M+My4wMDwvdHJhaWxzaW86dmFjYz4KICAgICAgICAg" + - "ICAgICAgICAgICAgICAgPHRyYWlsc2lvOnN0ZXBzPjU2MDc8L3RyYWlsc2lvOnN0" + - "ZXBzPgogICAgICAgICAgICAgICAgICAgIDwvdHJhaWxzaW86VHJhY2tQb2ludEV4" + - "dGVuc2lvbj4KICAgICAgICAgICAgICAgIDwvZXh0ZW5zaW9ucz4KICAgICAgICAg" + - "ICAgPC90cmtwdD4KICAgICAgICAgICAgPHRya3B0IGxvbj0iNS40ODU3MzgiIGxh" + - "dD0iNTEuODA1NTAwIj4KICAgICAgICAgICAgICAgIDxlbGU+OS4yNDIwMDA8L2Vs" + - "ZT4KICAgICAgICAgICAgICAgIDx0aW1lPjIwMTgtMDktMDVUMTc6MzM6MTVaPC90" + - "aW1lPgogICAgICAgICAgICAgICAgPGV4dGVuc2lvbnM+CiAgICAgICAgICAgICAg" + - "ICAgICAgPGdweHRweDpUcmFja1BvaW50RXh0ZW5zaW9uPgogICAgICAgICAgICAg" + - "ICAgICAgICAgICA8Z3B4dHB4OnNwZWVkPjUuNjY8L2dweHRweDpzcGVlZD4KICAg" + - "ICAgICAgICAgICAgICAgICAgICAgPGdweHRweDpjb3Vyc2U+MTUxLjg4PC9ncHh0" + - "cHg6Y291cnNlPgogICAgICAgICAgICAgICAgICAgIDwvZ3B4dHB4OlRyYWNrUG9p" + - "bnRFeHRlbnNpb24+CiAgICAgICAgICAgICAgICAgICAgPHRyYWlsc2lvOlRyYWNr" + - "UG9pbnRFeHRlbnNpb24+CiAgICAgICAgICAgICAgICAgICAgICAgIDx0cmFpbHNp" + - "bzpoYWNjPjUuMDA8L3RyYWlsc2lvOmhhY2M+CiAgICAgICAgICAgICAgICAgICAg" + - "ICAgIDx0cmFpbHNpbzp2YWNjPjMuMDA8L3RyYWlsc2lvOnZhY2M+CiAgICAgICAg" + - "ICAgICAgICAgICAgICAgIDx0cmFpbHNpbzpzdGVwcz41NjEyPC90cmFpbHNpbzpz" + - "dGVwcz4KICAgICAgICAgICAgICAgICAgICA8L3RyYWlsc2lvOlRyYWNrUG9pbnRF" + - "eHRlbnNpb24+CiAgICAgICAgICAgICAgICA8L2V4dGVuc2lvbnM+CiAgICAgICAg" + - "ICAgIDwvdHJrcHQ+CiAgICAgICAgICAgIDx0cmtwdCBsb249IjUuNDg2MDU2IiBs" + - "YXQ9IjUxLjgwNTExNSI+CiAgICAgICAgICAgICAgICA8ZWxlPjkuMTIyMDAwPC9l" + - "bGU+CiAgICAgICAgICAgICAgICA8dGltZT4yMDE4LTA5LTA1VDE3OjMzOjI0Wjwv" + - "dGltZT4KICAgICAgICAgICAgICAgIDxleHRlbnNpb25zPgogICAgICAgICAgICAg" + - "ICAgICAgIDxncHh0cHg6VHJhY2tQb2ludEV4dGVuc2lvbj4KICAgICAgICAgICAg" + - "ICAgICAgICAgICAgPGdweHRweDpzcGVlZD40Ljc2PC9ncHh0cHg6c3BlZWQ+CiAg" + - "ICAgICAgICAgICAgICAgICAgICAgIDxncHh0cHg6Y291cnNlPjE1NC4zNDwvZ3B4" + - "dHB4OmNvdXJzZT4KICAgICAgICAgICAgICAgICAgICA8L2dweHRweDpUcmFja1Bv" + - "aW50RXh0ZW5zaW9uPgogICAgICAgICAgICAgICAgICAgIDx0cmFpbHNpbzpUcmFj" + - "a1BvaW50RXh0ZW5zaW9uPgogICAgICAgICAgICAgICAgICAgICAgICA8dHJhaWxz" + - "aW86aGFjYz41LjAwPC90cmFpbHNpbzpoYWNjPgogICAgICAgICAgICAgICAgICAg" + - "ICAgICA8dHJhaWxzaW86dmFjYz4zLjAwPC90cmFpbHNpbzp2YWNjPgogICAgICAg" + - "ICAgICAgICAgICAgICAgICA8dHJhaWxzaW86c3RlcHM+NTYyMTwvdHJhaWxzaW86" + - "c3RlcHM+CiAgICAgICAgICAgICAgICAgICAgPC90cmFpbHNpbzpUcmFja1BvaW50" + - "RXh0ZW5zaW9uPgogICAgICAgICAgICAgICAgPC9leHRlbnNpb25zPgogICAgICAg" + - "ICAgICA8L3Rya3B0PgogICAgICAgIDwvdHJrc2VnPgogICAgPC90cms+CjwvZ3B4" + - "Pg=="; + private const string Base64GpxString = "CjxncHggeG1sbnM9Imh0dHA6Ly93d3cudG9wb2dyYWZpeC5jb20vR1BYLzEvMSIg" + + "eG1sbnM6Z3B4eD0iaHR0cDovL3d3dy5nYXJtaW4uY29tL3htbHNjaGVtYXMvR3B4" + + "RXh0ZW5zaW9ucy92MyIgeG1sbnM6Z3B4dHB4PSJodHRwOi8vd3d3OC5nYXJtaW4u" + + "Y29tL3htbHNjaGVtYXMvVHJhY2tQb2ludEV4dGVuc2lvbnYyLnhzZCIgeG1sbnM6" + + "dHJhaWxzaW89Imh0dHA6Ly90cmFpbHMuaW8vR1BYLzEvMCIgeG1sbnM6eHNpPSJo" + + "dHRwOi8vd3d3LnczLm9yZy8yMDAxL1hNTFNjaGVtYS1pbnN0YW5jZSIgdmVyc2lv" + + "bj0iMS4xIiBjcmVhdG9yPSJBZHplIC0gaHR0cDovL2tvYm90c3cuY29tL2FwcHMv" + + "YWR6ZSIgeHNpOnNjaGVtYUxvY2F0aW9uPSJodHRwOi8vd3d3LnRvcG9ncmFmaXgu" + + "Y29tL0dQWC8xLzEgaHR0cDovL3d3dy50b3BvZ3JhZml4LmNvbS9HUFgvMS8xL2dw" + + "eC54c2QgaHR0cDovL3d3dy5nYXJtaW4uY29tL3htbHNjaGVtYXMvR3B4RXh0ZW5z" + + "aW9ucy92MyBodHRwOi8vd3d3Lmdhcm1pbi5jb20veG1sc2NoZW1hcy9HcHhFeHRl" + + "bnNpb25zdjMueHNkIGh0dHA6Ly90cmFpbHMuaW8vR1BYLzEvMCBodHRwczovL3Ry" + + "YWlscy5pby9HUFgvMS8wL3RyYWlsc18xLjAueHNkIj4KICAgIDxtZXRhZGF0YT4K" + + "ICAgICAgICA8bmFtZT5VbnRpdGxlZCBEb2N1bWVudDwvbmFtZT4KICAgICAgICA8" + + "dGltZT4yMDE4LTA5LTExVDE5OjQxOjEwLjQ4Mlo8L3RpbWU+CiAgICA8L21ldGFk" + + "YXRhPgogICAgPHRyaz4KICAgICAgICA8bmFtZT5fMjAxODA5MDUtZmlldHNlbi1v" + + "c3M8L25hbWU+CiAgICAgICAgPGV4dGVuc2lvbnM+CiAgICAgICAgICAgIDx0cmFp" + + "bHNpbzpUcmFja0V4dGVuc2lvbj4KICAgICAgICAgICAgICAgIDx0cmFpbHNpbzph" + + "Y3Rpdml0eT5iaWtpbmc8L3RyYWlsc2lvOmFjdGl2aXR5PgogICAgICAgICAgICA8" + + "L3RyYWlsc2lvOlRyYWNrRXh0ZW5zaW9uPgogICAgICAgICAgICA8Z3B4eDpUcmFj" + + "a0V4dGVuc2lvbj4KICAgICAgICAgICAgICAgIDxncHh4OkRpc3BsYXlDb2xvcj5E" + + "YXJrUmVkPC9ncHh4OkRpc3BsYXlDb2xvcj4KICAgICAgICAgICAgPC9ncHh4OlRy" + + "YWNrRXh0ZW5zaW9uPgogICAgICAgIDwvZXh0ZW5zaW9ucz4KICAgICAgICA8dHJr" + + "c2VnPgogICAgICAgICAgICA8dHJrcHQgbG9uPSI1LjQ4NTk0MSIgbGF0PSI1MS44" + + "MDkzNjAiPgogICAgICAgICAgICAgICAgPGVsZT43LjI2MzAwMDwvZWxlPgogICAg" + + "ICAgICAgICAgICAgPHRpbWU+MjAxOC0wOS0wNVQxNzozMTo1M1o8L3RpbWU+CiAg" + + "ICAgICAgICAgICAgICA8ZXh0ZW5zaW9ucz4KICAgICAgICAgICAgICAgICAgICA8" + + "Z3B4dHB4OlRyYWNrUG9pbnRFeHRlbnNpb24+CiAgICAgICAgICAgICAgICAgICAg" + + "ICAgIDxncHh0cHg6c3BlZWQ+NS40OTwvZ3B4dHB4OnNwZWVkPgogICAgICAgICAg" + + "ICAgICAgICAgICAgICA8Z3B4dHB4OmNvdXJzZT4xODkuNDk8L2dweHRweDpjb3Vy" + + "c2U+CiAgICAgICAgICAgICAgICAgICAgPC9ncHh0cHg6VHJhY2tQb2ludEV4dGVu" + + "c2lvbj4KICAgICAgICAgICAgICAgICAgICA8dHJhaWxzaW86VHJhY2tQb2ludEV4" + + "dGVuc2lvbj4KICAgICAgICAgICAgICAgICAgICAgICAgPHRyYWlsc2lvOmhhY2M+" + + "NS4wMDwvdHJhaWxzaW86aGFjYz4KICAgICAgICAgICAgICAgICAgICAgICAgPHRy" + + "YWlsc2lvOnZhY2M+NC4wMDwvdHJhaWxzaW86dmFjYz4KICAgICAgICAgICAgICAg" + + "ICAgICAgICAgPHRyYWlsc2lvOnN0ZXBzPjU1MTE8L3RyYWlsc2lvOnN0ZXBzPgog" + + "ICAgICAgICAgICAgICAgICAgIDwvdHJhaWxzaW86VHJhY2tQb2ludEV4dGVuc2lv" + + "bj4KICAgICAgICAgICAgICAgIDwvZXh0ZW5zaW9ucz4KICAgICAgICAgICAgPC90" + + "cmtwdD4KICAgICAgICAgICAgPHRya3B0IGxvbj0iNS40ODU3MjQiIGxhdD0iNTEu" + + "ODA3OTY4Ij4KICAgICAgICAgICAgICAgIDxlbGU+Ny43NzIwMDA8L2VsZT4KICAg" + + "ICAgICAgICAgICAgIDx0aW1lPjIwMTgtMDktMDVUMTc6MzI6MjFaPC90aW1lPgog" + + "ICAgICAgICAgICAgICAgPGV4dGVuc2lvbnM+CiAgICAgICAgICAgICAgICAgICAg" + + "PGdweHRweDpUcmFja1BvaW50RXh0ZW5zaW9uPgogICAgICAgICAgICAgICAgICAg" + + "ICAgICA8Z3B4dHB4OnNwZWVkPjQuNTk8L2dweHRweDpzcGVlZD4KICAgICAgICAg" + + "ICAgICAgICAgICAgICAgPGdweHRweDpjb3Vyc2U+MTg1LjYyPC9ncHh0cHg6Y291" + + "cnNlPgogICAgICAgICAgICAgICAgICAgIDwvZ3B4dHB4OlRyYWNrUG9pbnRFeHRl" + + "bnNpb24+CiAgICAgICAgICAgICAgICAgICAgPHRyYWlsc2lvOlRyYWNrUG9pbnRF" + + "eHRlbnNpb24+CiAgICAgICAgICAgICAgICAgICAgICAgIDx0cmFpbHNpbzpoYWNj" + + "PjUuMDA8L3RyYWlsc2lvOmhhY2M+CiAgICAgICAgICAgICAgICAgICAgICAgIDx0" + + "cmFpbHNpbzp2YWNjPjQuMDA8L3RyYWlsc2lvOnZhY2M+CiAgICAgICAgICAgICAg" + + "ICAgICAgICAgIDx0cmFpbHNpbzpzdGVwcz41NTUyPC90cmFpbHNpbzpzdGVwcz4K" + + "ICAgICAgICAgICAgICAgICAgICA8L3RyYWlsc2lvOlRyYWNrUG9pbnRFeHRlbnNp" + + "b24+CiAgICAgICAgICAgICAgICA8L2V4dGVuc2lvbnM+CiAgICAgICAgICAgIDwv" + + "dHJrcHQ+CiAgICAgICAgICAgIDx0cmtwdCBsb249IjUuNDg1NjMxIiBsYXQ9IjUx" + + "LjgwNzAxOSI+CiAgICAgICAgICAgICAgICA8ZWxlPjkuOTU3MDAwPC9lbGU+CiAg" + + "ICAgICAgICAgICAgICA8dGltZT4yMDE4LTA5LTA1VDE3OjMyOjQzWjwvdGltZT4K" + + "ICAgICAgICAgICAgICAgIDxleHRlbnNpb25zPgogICAgICAgICAgICAgICAgICAg" + + "IDxncHh0cHg6VHJhY2tQb2ludEV4dGVuc2lvbj4KICAgICAgICAgICAgICAgICAg" + + "ICAgICAgPGdweHRweDpzcGVlZD40LjYzPC9ncHh0cHg6c3BlZWQ+CiAgICAgICAg" + + "ICAgICAgICAgICAgICAgIDxncHh0cHg6Y291cnNlPjE4MC43MDwvZ3B4dHB4OmNv" + + "dXJzZT4KICAgICAgICAgICAgICAgICAgICA8L2dweHRweDpUcmFja1BvaW50RXh0" + + "ZW5zaW9uPgogICAgICAgICAgICAgICAgICAgIDx0cmFpbHNpbzpUcmFja1BvaW50" + + "RXh0ZW5zaW9uPgogICAgICAgICAgICAgICAgICAgICAgICA8dHJhaWxzaW86aGFj" + + "Yz41LjAwPC90cmFpbHNpbzpoYWNjPgogICAgICAgICAgICAgICAgICAgICAgICA8" + + "dHJhaWxzaW86dmFjYz4zLjAwPC90cmFpbHNpbzp2YWNjPgogICAgICAgICAgICAg" + + "ICAgICAgICAgICA8dHJhaWxzaW86c3RlcHM+NTU1NjwvdHJhaWxzaW86c3RlcHM+" + + "CiAgICAgICAgICAgICAgICAgICAgPC90cmFpbHNpbzpUcmFja1BvaW50RXh0ZW5z" + + "aW9uPgogICAgICAgICAgICAgICAgPC9leHRlbnNpb25zPgogICAgICAgICAgICA8" + + "L3Rya3B0PgogICAgICAgICAgICA8dHJrcHQgbG9uPSI1LjQ4NTYxMCIgbGF0PSI1" + + "MS44MDY3MDIiPgogICAgICAgICAgICAgICAgPGVsZT4xMC44MDgwMDA8L2VsZT4K" + + "ICAgICAgICAgICAgICAgIDx0aW1lPjIwMTgtMDktMDVUMTc6MzI6NTBaPC90aW1l" + + "PgogICAgICAgICAgICAgICAgPGV4dGVuc2lvbnM+CiAgICAgICAgICAgICAgICAg" + + "ICAgPGdweHRweDpUcmFja1BvaW50RXh0ZW5zaW9uPgogICAgICAgICAgICAgICAg" + + "ICAgICAgICA8Z3B4dHB4OnNwZWVkPjUuNjQ8L2dweHRweDpzcGVlZD4KICAgICAg" + + "ICAgICAgICAgICAgICAgICAgPGdweHRweDpjb3Vyc2U+MTg2LjMzPC9ncHh0cHg6" + + "Y291cnNlPgogICAgICAgICAgICAgICAgICAgIDwvZ3B4dHB4OlRyYWNrUG9pbnRF" + + "eHRlbnNpb24+CiAgICAgICAgICAgICAgICAgICAgPHRyYWlsc2lvOlRyYWNrUG9p" + + "bnRFeHRlbnNpb24+CiAgICAgICAgICAgICAgICAgICAgICAgIDx0cmFpbHNpbzpo" + + "YWNjPjUuMDA8L3RyYWlsc2lvOmhhY2M+CiAgICAgICAgICAgICAgICAgICAgICAg" + + "IDx0cmFpbHNpbzp2YWNjPjMuMDA8L3RyYWlsc2lvOnZhY2M+CiAgICAgICAgICAg" + + "ICAgICAgICAgICAgIDx0cmFpbHNpbzpzdGVwcz41NTY2PC90cmFpbHNpbzpzdGVw" + + "cz4KICAgICAgICAgICAgICAgICAgICA8L3RyYWlsc2lvOlRyYWNrUG9pbnRFeHRl" + + "bnNpb24+CiAgICAgICAgICAgICAgICA8L2V4dGVuc2lvbnM+CiAgICAgICAgICAg" + + "IDwvdHJrcHQ+CiAgICAgICAgICAgIDx0cmtwdCBsb249IjUuNDg1NjMzIiBsYXQ9" + + "IjUxLjgwNTY2MyI+CiAgICAgICAgICAgICAgICA8ZWxlPjEwLjg2NjAwMDwvZWxl" + + "PgogICAgICAgICAgICAgICAgPHRpbWU+MjAxOC0wOS0wNVQxNzozMzoxMVo8L3Rp" + + "bWU+CiAgICAgICAgICAgICAgICA8ZXh0ZW5zaW9ucz4KICAgICAgICAgICAgICAg" + + "ICAgICA8Z3B4dHB4OlRyYWNrUG9pbnRFeHRlbnNpb24+CiAgICAgICAgICAgICAg" + + "ICAgICAgICAgIDxncHh0cHg6c3BlZWQ+NS41ODwvZ3B4dHB4OnNwZWVkPgogICAg" + + "ICAgICAgICAgICAgICAgICAgICA8Z3B4dHB4OmNvdXJzZT4xNjkuMTA8L2dweHRw" + + "eDpjb3Vyc2U+CiAgICAgICAgICAgICAgICAgICAgPC9ncHh0cHg6VHJhY2tQb2lu" + + "dEV4dGVuc2lvbj4KICAgICAgICAgICAgICAgICAgICA8dHJhaWxzaW86VHJhY2tQ" + + "b2ludEV4dGVuc2lvbj4KICAgICAgICAgICAgICAgICAgICAgICAgPHRyYWlsc2lv" + + "OmhhY2M+NS4wMDwvdHJhaWxzaW86aGFjYz4KICAgICAgICAgICAgICAgICAgICAg" + + "ICAgPHRyYWlsc2lvOnZhY2M+My4wMDwvdHJhaWxzaW86dmFjYz4KICAgICAgICAg" + + "ICAgICAgICAgICAgICAgPHRyYWlsc2lvOnN0ZXBzPjU2MDc8L3RyYWlsc2lvOnN0" + + "ZXBzPgogICAgICAgICAgICAgICAgICAgIDwvdHJhaWxzaW86VHJhY2tQb2ludEV4" + + "dGVuc2lvbj4KICAgICAgICAgICAgICAgIDwvZXh0ZW5zaW9ucz4KICAgICAgICAg" + + "ICAgPC90cmtwdD4KICAgICAgICAgICAgPHRya3B0IGxvbj0iNS40ODU3MzgiIGxh" + + "dD0iNTEuODA1NTAwIj4KICAgICAgICAgICAgICAgIDxlbGU+OS4yNDIwMDA8L2Vs" + + "ZT4KICAgICAgICAgICAgICAgIDx0aW1lPjIwMTgtMDktMDVUMTc6MzM6MTVaPC90" + + "aW1lPgogICAgICAgICAgICAgICAgPGV4dGVuc2lvbnM+CiAgICAgICAgICAgICAg" + + "ICAgICAgPGdweHRweDpUcmFja1BvaW50RXh0ZW5zaW9uPgogICAgICAgICAgICAg" + + "ICAgICAgICAgICA8Z3B4dHB4OnNwZWVkPjUuNjY8L2dweHRweDpzcGVlZD4KICAg" + + "ICAgICAgICAgICAgICAgICAgICAgPGdweHRweDpjb3Vyc2U+MTUxLjg4PC9ncHh0" + + "cHg6Y291cnNlPgogICAgICAgICAgICAgICAgICAgIDwvZ3B4dHB4OlRyYWNrUG9p" + + "bnRFeHRlbnNpb24+CiAgICAgICAgICAgICAgICAgICAgPHRyYWlsc2lvOlRyYWNr" + + "UG9pbnRFeHRlbnNpb24+CiAgICAgICAgICAgICAgICAgICAgICAgIDx0cmFpbHNp" + + "bzpoYWNjPjUuMDA8L3RyYWlsc2lvOmhhY2M+CiAgICAgICAgICAgICAgICAgICAg" + + "ICAgIDx0cmFpbHNpbzp2YWNjPjMuMDA8L3RyYWlsc2lvOnZhY2M+CiAgICAgICAg" + + "ICAgICAgICAgICAgICAgIDx0cmFpbHNpbzpzdGVwcz41NjEyPC90cmFpbHNpbzpz" + + "dGVwcz4KICAgICAgICAgICAgICAgICAgICA8L3RyYWlsc2lvOlRyYWNrUG9pbnRF" + + "eHRlbnNpb24+CiAgICAgICAgICAgICAgICA8L2V4dGVuc2lvbnM+CiAgICAgICAg" + + "ICAgIDwvdHJrcHQ+CiAgICAgICAgICAgIDx0cmtwdCBsb249IjUuNDg2MDU2IiBs" + + "YXQ9IjUxLjgwNTExNSI+CiAgICAgICAgICAgICAgICA8ZWxlPjkuMTIyMDAwPC9l" + + "bGU+CiAgICAgICAgICAgICAgICA8dGltZT4yMDE4LTA5LTA1VDE3OjMzOjI0Wjwv" + + "dGltZT4KICAgICAgICAgICAgICAgIDxleHRlbnNpb25zPgogICAgICAgICAgICAg" + + "ICAgICAgIDxncHh0cHg6VHJhY2tQb2ludEV4dGVuc2lvbj4KICAgICAgICAgICAg" + + "ICAgICAgICAgICAgPGdweHRweDpzcGVlZD40Ljc2PC9ncHh0cHg6c3BlZWQ+CiAg" + + "ICAgICAgICAgICAgICAgICAgICAgIDxncHh0cHg6Y291cnNlPjE1NC4zNDwvZ3B4" + + "dHB4OmNvdXJzZT4KICAgICAgICAgICAgICAgICAgICA8L2dweHRweDpUcmFja1Bv" + + "aW50RXh0ZW5zaW9uPgogICAgICAgICAgICAgICAgICAgIDx0cmFpbHNpbzpUcmFj" + + "a1BvaW50RXh0ZW5zaW9uPgogICAgICAgICAgICAgICAgICAgICAgICA8dHJhaWxz" + + "aW86aGFjYz41LjAwPC90cmFpbHNpbzpoYWNjPgogICAgICAgICAgICAgICAgICAg" + + "ICAgICA8dHJhaWxzaW86dmFjYz4zLjAwPC90cmFpbHNpbzp2YWNjPgogICAgICAg" + + "ICAgICAgICAgICAgICAgICA8dHJhaWxzaW86c3RlcHM+NTYyMTwvdHJhaWxzaW86" + + "c3RlcHM+CiAgICAgICAgICAgICAgICAgICAgPC90cmFpbHNpbzpUcmFja1BvaW50" + + "RXh0ZW5zaW9uPgogICAgICAgICAgICAgICAgPC9leHRlbnNpb25zPgogICAgICAg" + + "ICAgICA8L3Rya3B0PgogICAgICAgIDwvdHJrc2VnPgogICAgPC90cms+CjwvZ3B4" + + "Pg=="; public static readonly ImmutableArray Bytes = Base64Helper.TryParse(Base64GpxString).ToImmutableArray(); diff --git a/starsky/starskytest/FakeCreateAn/CreateAnGpx/CreateAnGpx.gpx b/starsky/starskytest/FakeCreateAn/CreateAnGpx/CreateAnGpx.gpx new file mode 100644 index 0000000000..bb266d2bb8 --- /dev/null +++ b/starsky/starskytest/FakeCreateAn/CreateAnGpx/CreateAnGpx.gpx @@ -0,0 +1,124 @@ + + + Untitled Document + + + + _20180905-fietsen-oss + + + biking + + + DarkRed + + + + + 7.263000 + + + + 5.49 + 189.49 + + + 5.00 + 4.00 + 5511 + + + + + 7.772000 + + + + 4.59 + 185.62 + + + 5.00 + 4.00 + 5552 + + + + + 9.957000 + + + + 4.63 + 180.70 + + + 5.00 + 3.00 + 5556 + + + + + 10.808000 + + + + 5.64 + 186.33 + + + 5.00 + 3.00 + 5566 + + + + + 10.866000 + + + + 5.58 + 169.10 + + + 5.00 + 3.00 + 5607 + + + + + 9.242000 + + + + 5.66 + 151.88 + + + 5.00 + 3.00 + 5612 + + + + + 9.122000 + + + + 4.76 + 154.34 + + + 5.00 + 3.00 + 5621 + + + + + + \ No newline at end of file diff --git a/starsky/starskytest/starsky.feature.metaupdate/Services/MetaUpdateServiceTest.cs b/starsky/starskytest/starsky.feature.metaupdate/Services/MetaUpdateServiceTest.cs index 88c76b1959..df88c43da8 100644 --- a/starsky/starskytest/starsky.feature.metaupdate/Services/MetaUpdateServiceTest.cs +++ b/starsky/starskytest/starsky.feature.metaupdate/Services/MetaUpdateServiceTest.cs @@ -237,6 +237,9 @@ public async Task Update_Write_GPX() "/test.gpx", new List{"Tags"} }}; + var t = new CreateAnGpx().FullFileGpxPath; + + await _iStorageFake.WriteStreamAsync(new MemoryStream(CreateAnGpx.Bytes.ToArray()), "/test.gpx"); var updateItem = new FileIndexItem("/test.gpx") { diff --git a/starsky/starskytest/starsky.foundation.georealtime/Models/GeometryModelTest.cs b/starsky/starskytest/starsky.foundation.georealtime/Models/GeometryModelTest.cs new file mode 100644 index 0000000000..98aa37ce6b --- /dev/null +++ b/starsky/starskytest/starsky.foundation.georealtime/Models/GeometryModelTest.cs @@ -0,0 +1,55 @@ +using System; +using System.Collections.Generic; +using Microsoft.VisualStudio.TestTools.UnitTesting; +using starsky.foundation.georealtime.Models.GeoJson; + +namespace starskytest.starsky.foundation.georealtime.Models; + +[TestClass] +public class GeometryModelTest +{ + + [TestMethod] + public void NoItemsInList() + { + var model = new GeometryModel { Coordinates = new List>() }; + Assert.AreEqual(0,model.Coordinates.Count); + } + + [TestMethod] + [ExpectedException(typeof(ArgumentException))] + public void InvalidList() + { + var geometryModel = new GeometryModel { Coordinates = new List>{new List()} }; + + Assert.IsNull(geometryModel); + } + + [TestMethod] + [ExpectedException(typeof(ArgumentException))] + public void InvalidList_OneItem() + { + var geometryModel = new GeometryModel { Coordinates = new List>{new List{0}} }; + + Assert.IsNull(geometryModel); + } + + [TestMethod] + public void InvalidList_TwoItems() + { + var geometryModel = new GeometryModel { Coordinates = new List>{new List{0,1}} }; + + Assert.AreEqual(0,geometryModel.Coordinates[0][0]); + Assert.AreEqual(1,geometryModel.Coordinates[0][1]); + } + + [TestMethod] + public void InvalidList_ThreeItems() + { + var geometryModel = new GeometryModel { Coordinates = new List>{new List{0,1,2}} }; + + Assert.AreEqual(0,geometryModel.Coordinates[0][0]); + Assert.AreEqual(1,geometryModel.Coordinates[0][1]); + Assert.AreEqual(2,geometryModel.Coordinates[0][2]); + } +} diff --git a/starsky/starskytest/starsky.foundation.georealtime/Models/ModelsParserTest.cs b/starsky/starskytest/starsky.foundation.georealtime/Models/ModelsParserTest.cs new file mode 100644 index 0000000000..eb7bd2eec3 --- /dev/null +++ b/starsky/starskytest/starsky.foundation.georealtime/Models/ModelsParserTest.cs @@ -0,0 +1,60 @@ +using Microsoft.AspNetCore.Http.Features; +using Microsoft.VisualStudio.TestTools.UnitTesting; +using starsky.foundation.georealtime.Models.GeoJson; +using starsky.foundation.platform.JsonConverter; + +namespace starskytest.starsky.foundation.georealtime.Models; + +[TestClass] +public class ModelsParserTest +{ + [TestMethod] + public void TestWithExample() + { + const string geoJson = @" + { + ""type"": ""FeatureCollection"", + ""features"": [ + { + ""type"": ""Feature"", + ""geometry"": { + ""type"": ""LineString"", + ""coordinates"": [ + [5.485941, 51.809360, 7.263], + [5.485724, 51.807968, 7.772], + [5.485631, 51.807019, 9.957], + [5.485610, 51.806702, 10.808], + [5.485633, 51.805663, 10.866], + [5.485738, 51.805500, 9.242], + [5.486056, 51.805115, 9.122] + ] + } + } + ] + }"; + + // Deserialize GeoJSON string to C# objects + var featureCollection = System.Text.Json.JsonSerializer.Deserialize(geoJson, + DefaultJsonSerializer.CamelCase); + + // Assert + Assert.IsNotNull(featureCollection); + Assert.AreEqual(FeatureCollectionType.FeatureCollection, featureCollection.Type); + + Assert.IsNotNull(featureCollection.Features); + Assert.AreEqual(1, featureCollection.Features.Count); + + var feature = featureCollection.Features[0]; + Assert.IsNotNull(feature); + Assert.AreEqual("Feature", feature.Type); + + var geometry = feature.Geometry; + Assert.IsNotNull(geometry); + Assert.AreEqual(GeometryType.LineString, geometry.Type); + + var coordinates = geometry.Coordinates; + Assert.IsNotNull(coordinates); + Assert.AreEqual(7, coordinates.Count); + } + +} diff --git a/starsky/starskytest/starskytest.csproj b/starsky/starskytest/starskytest.csproj index feaf3ed6c3..7997fe02f0 100644 --- a/starsky/starskytest/starskytest.csproj +++ b/starsky/starskytest/starskytest.csproj @@ -48,6 +48,7 @@ + From da8fafda6542967f9760323a935bbb0aa18dd46a Mon Sep 17 00:00:00 2001 From: Dion Date: Thu, 21 Dec 2023 18:48:06 +0100 Subject: [PATCH 5/7] WIP --- .../Helpers/DefaultGeoJson.cs | 21 +++++++++++++++---- .../Models/GeoJson/FeatureModel.cs | 3 ++- .../Models/GeoJson/FeatureType.cs | 9 ++++++++ .../Models/GeoJson/GeometryModel.cs | 5 +++-- .../Models/GeoJson/PropertiesModel.cs | 9 ++++++++ .../Models/KmlImportOptions.cs | 13 +++++++++--- .../LatitudeLongitudeAltDateTimeModel.cs | 4 +--- .../Models/LatitudeLongitudeModel.cs | 7 +++++++ .../CreateAnGeoJson/CreateAnGeoJson.json | 11 ++++++++++ .../FakeCreateAn/CreateAnGeoJson/example.html | 12 ++++++++++- .../Models/ModelsParserTest.cs | 2 +- 11 files changed, 81 insertions(+), 15 deletions(-) create mode 100644 starsky/starsky.foundation.georealtime/Models/GeoJson/FeatureType.cs create mode 100644 starsky/starsky.foundation.georealtime/Models/GeoJson/PropertiesModel.cs create mode 100644 starsky/starsky.foundation.georealtime/Models/LatitudeLongitudeModel.cs diff --git a/starsky/starsky.foundation.georealtime/Helpers/DefaultGeoJson.cs b/starsky/starsky.foundation.georealtime/Helpers/DefaultGeoJson.cs index 9a4b6e3f50..8c5b2a1227 100644 --- a/starsky/starsky.foundation.georealtime/Helpers/DefaultGeoJson.cs +++ b/starsky/starsky.foundation.georealtime/Helpers/DefaultGeoJson.cs @@ -1,14 +1,27 @@ +using System.Collections.Generic; using starsky.foundation.georealtime.Models.GeoJson; namespace starsky.foundation.georealtime.Helpers; -public class DefaultGeoJson +public static class DefaultGeoJson { - public void CreateDefaultGeoJson() + public static FeatureCollectionModel CreateDefaultGeoJson(List> coordinates) { - var featureCollection = new FeatureCollectionModel + return new FeatureCollectionModel { - Type = FeatureCollectionType.FeatureCollection + Type = FeatureCollectionType.FeatureCollection, + Features = new List + { + new FeatureModel + { + Type = FeatureType.Feature, + Geometry = new GeometryModel + { + Coordinates = coordinates + }, + Properties = new PropertiesModel() + } + } }; } } diff --git a/starsky/starsky.foundation.georealtime/Models/GeoJson/FeatureModel.cs b/starsky/starsky.foundation.georealtime/Models/GeoJson/FeatureModel.cs index 933feabfc1..7622e4b503 100644 --- a/starsky/starsky.foundation.georealtime/Models/GeoJson/FeatureModel.cs +++ b/starsky/starsky.foundation.georealtime/Models/GeoJson/FeatureModel.cs @@ -2,6 +2,7 @@ namespace starsky.foundation.georealtime.Models.GeoJson; public class FeatureModel { - public string Type { get; set; } + public FeatureType? Type { get; set; } public GeometryModel Geometry { get; set; } + public PropertiesModel? Properties { get; set; } } diff --git a/starsky/starsky.foundation.georealtime/Models/GeoJson/FeatureType.cs b/starsky/starsky.foundation.georealtime/Models/GeoJson/FeatureType.cs new file mode 100644 index 0000000000..bb25b4d8e8 --- /dev/null +++ b/starsky/starsky.foundation.georealtime/Models/GeoJson/FeatureType.cs @@ -0,0 +1,9 @@ +using System.Text.Json.Serialization; + +namespace starsky.foundation.georealtime.Models.GeoJson; + +[JsonConverter(typeof(JsonStringEnumConverter))] +public enum FeatureType +{ + Feature +} diff --git a/starsky/starsky.foundation.georealtime/Models/GeoJson/GeometryModel.cs b/starsky/starsky.foundation.georealtime/Models/GeoJson/GeometryModel.cs index fc01201130..efde9031ca 100644 --- a/starsky/starsky.foundation.georealtime/Models/GeoJson/GeometryModel.cs +++ b/starsky/starsky.foundation.georealtime/Models/GeoJson/GeometryModel.cs @@ -7,8 +7,9 @@ namespace starsky.foundation.georealtime.Models.GeoJson; public class GeometryModel { public GeometryType? Type { get; set; } - - private List> CoordinatesPrivate { get; set; } + + private List> CoordinatesPrivate { get; set; } = + new List>(); public List> Coordinates { diff --git a/starsky/starsky.foundation.georealtime/Models/GeoJson/PropertiesModel.cs b/starsky/starsky.foundation.georealtime/Models/GeoJson/PropertiesModel.cs new file mode 100644 index 0000000000..1131a8a3d5 --- /dev/null +++ b/starsky/starsky.foundation.georealtime/Models/GeoJson/PropertiesModel.cs @@ -0,0 +1,9 @@ +using System; + +namespace starsky.foundation.georealtime.Models.GeoJson; + +public class PropertiesModel +{ + public string? Name { get; set; } + public DateTime DateTime { get; set; } +} diff --git a/starsky/starsky.foundation.georealtime/Models/KmlImportOptions.cs b/starsky/starsky.foundation.georealtime/Models/KmlImportOptions.cs index 88db58b2d8..45a1437dc4 100644 --- a/starsky/starsky.foundation.georealtime/Models/KmlImportOptions.cs +++ b/starsky/starsky.foundation.georealtime/Models/KmlImportOptions.cs @@ -1,10 +1,17 @@ +using System.Collections.Generic; + namespace starsky.foundation.georealtime.Models; public class KmlImportOptions { - public string OutputPath { get; set; } - public bool OutputStorageSubPath { get; set; } = true; + public string OutputPath { get; set; } = string.Empty; + public bool OutputPathSubPath { get; set; } = true; public bool SplitByDay { get; set; } - + + public bool OutputGeoJson { get; set; } = false; + public bool OutputGeoJsonPoints { get; set; } = false; + public bool OutputGpx { get; set; } = true; + + public List Filter { get; set; } } diff --git a/starsky/starsky.foundation.georealtime/Models/LatitudeLongitudeAltDateTimeModel.cs b/starsky/starsky.foundation.georealtime/Models/LatitudeLongitudeAltDateTimeModel.cs index 91b92e4169..b06a019ab7 100644 --- a/starsky/starsky.foundation.georealtime/Models/LatitudeLongitudeAltDateTimeModel.cs +++ b/starsky/starsky.foundation.georealtime/Models/LatitudeLongitudeAltDateTimeModel.cs @@ -2,10 +2,8 @@ namespace starsky.foundation.georealtime.Models; -public class LatitudeLongitudeAltDateTimeModel +public class LatitudeLongitudeAltDateTimeModel : LatitudeLongitudeModel { - public string Latitude { get; set; } - public string Longitude { get; set; } public string Altitude { get; set; } public DateTime DateTime { get; set; } } diff --git a/starsky/starsky.foundation.georealtime/Models/LatitudeLongitudeModel.cs b/starsky/starsky.foundation.georealtime/Models/LatitudeLongitudeModel.cs new file mode 100644 index 0000000000..cbdd0b91fd --- /dev/null +++ b/starsky/starsky.foundation.georealtime/Models/LatitudeLongitudeModel.cs @@ -0,0 +1,7 @@ +namespace starsky.foundation.georealtime.Models; + +public class LatitudeLongitudeModel +{ + public string Latitude { get; set; } + public string Longitude { get; set; } +} diff --git a/starsky/starskytest/FakeCreateAn/CreateAnGeoJson/CreateAnGeoJson.json b/starsky/starskytest/FakeCreateAn/CreateAnGeoJson/CreateAnGeoJson.json index b23c0619d7..42885f6ac4 100644 --- a/starsky/starskytest/FakeCreateAn/CreateAnGeoJson/CreateAnGeoJson.json +++ b/starsky/starskytest/FakeCreateAn/CreateAnGeoJson/CreateAnGeoJson.json @@ -21,6 +21,17 @@ "activity": "biking", "displayColor": "DarkRed" } + }, + { + "type": "Feature", + "geometry": { + "type": "Point", + "coordinates": [5.485941, 51.809360, 7.263] + }, + "properties": { + "name": "Location 1", + "datetime": "2023-12-21T12:34:56Z" + } } ] } diff --git a/starsky/starskytest/FakeCreateAn/CreateAnGeoJson/example.html b/starsky/starskytest/FakeCreateAn/CreateAnGeoJson/example.html index 2694aa5bf2..b6ae8a893c 100644 --- a/starsky/starskytest/FakeCreateAn/CreateAnGeoJson/example.html +++ b/starsky/starskytest/FakeCreateAn/CreateAnGeoJson/example.html @@ -72,11 +72,21 @@ "activity": "biking", "displayColor": "DarkRed" } + }, + { + "type": "Feature", + "geometry": { + "type": "Point", + "coordinates": [5.485941, 51.809360, 7.263] + }, + "properties": { + "name": "Location 1", + "datetime": "2023-12-21T12:34:56Z" + } } ] }; - /* global campus, bicycleRental, freeBus, coorsField */ L.geoJSON([geoJsonData], { diff --git a/starsky/starskytest/starsky.foundation.georealtime/Models/ModelsParserTest.cs b/starsky/starskytest/starsky.foundation.georealtime/Models/ModelsParserTest.cs index eb7bd2eec3..9433ec5842 100644 --- a/starsky/starskytest/starsky.foundation.georealtime/Models/ModelsParserTest.cs +++ b/starsky/starskytest/starsky.foundation.georealtime/Models/ModelsParserTest.cs @@ -46,7 +46,7 @@ public void TestWithExample() var feature = featureCollection.Features[0]; Assert.IsNotNull(feature); - Assert.AreEqual("Feature", feature.Type); + Assert.AreEqual(FeatureType.Feature, feature.Type); var geometry = feature.Geometry; Assert.IsNotNull(geometry); From f91cf9ec5c6d8a2fb872eaba82debf1e0fb4126a Mon Sep 17 00:00:00 2001 From: Dion Date: Sat, 23 Dec 2023 23:08:05 +0100 Subject: [PATCH 6/7] WIP --- .../Converter/GeometryBaseModelConverter.cs | 52 +++++ .../Helpers/DefaultGeoJson.bak | 60 +++++ .../Helpers/DefaultGeoJson.cs | 27 --- .../Helpers/IntermediateModelConverter.cs | 127 +++++++++++ .../Helpers/Kml2IntermediateModel.cs | 81 +++++++ .../Models/GeoJson/FeatureModel.cs | 2 +- .../Models/GeoJson/GeometryBaseModel.cs | 10 + .../Models/GeoJson/GeometryLineStringModel.cs | 11 + .../Models/GeoJson/GeometryModel.cs | 28 --- .../Models/GeoJson/GeometryPointModel.cs | 11 + .../LatitudeLongitudeAltDateTimeModel.cs | 9 +- .../Models/LatitudeLongitudeModel.cs | 4 +- .../Services/KmlImport.cs | 45 +--- .../Models/AppSettings.cs | 15 +- .../starsky/Helpers/SwaggerExportHelper.cs | 1 + .../FakeCreateAn/CreateAnGeoJson/example.html | 33 +-- .../FakeCreateAn/CreateAnKml/CreateAnKml.cs | 30 +++ .../FakeCreateAn/CreateAnKml/garmin.kml | 135 +++++++++++ .../GeometryBaseModelConverterTest.cs | 108 +++++++++ .../Helpers/IntermediateModelConverterTest.cs | 210 ++++++++++++++++++ .../Models/GeometryModelTest.cs | 55 ----- .../Models/ModelsParserTest.cs | 104 ++++++++- .../Services/KmlImportTest.cs | 53 +++++ starsky/starskytest/starskytest.csproj | 4 + 24 files changed, 1035 insertions(+), 180 deletions(-) create mode 100644 starsky/starsky.foundation.georealtime/Converter/GeometryBaseModelConverter.cs create mode 100644 starsky/starsky.foundation.georealtime/Helpers/DefaultGeoJson.bak delete mode 100644 starsky/starsky.foundation.georealtime/Helpers/DefaultGeoJson.cs create mode 100644 starsky/starsky.foundation.georealtime/Helpers/IntermediateModelConverter.cs create mode 100644 starsky/starsky.foundation.georealtime/Helpers/Kml2IntermediateModel.cs create mode 100644 starsky/starsky.foundation.georealtime/Models/GeoJson/GeometryBaseModel.cs create mode 100644 starsky/starsky.foundation.georealtime/Models/GeoJson/GeometryLineStringModel.cs delete mode 100644 starsky/starsky.foundation.georealtime/Models/GeoJson/GeometryModel.cs create mode 100644 starsky/starsky.foundation.georealtime/Models/GeoJson/GeometryPointModel.cs create mode 100644 starsky/starskytest/FakeCreateAn/CreateAnKml/CreateAnKml.cs create mode 100644 starsky/starskytest/FakeCreateAn/CreateAnKml/garmin.kml create mode 100644 starsky/starskytest/starsky.foundation.georealtime/Converter/GeometryBaseModelConverterTest.cs create mode 100644 starsky/starskytest/starsky.foundation.georealtime/Helpers/IntermediateModelConverterTest.cs delete mode 100644 starsky/starskytest/starsky.foundation.georealtime/Models/GeometryModelTest.cs create mode 100644 starsky/starskytest/starsky.foundation.georealtime/Services/KmlImportTest.cs diff --git a/starsky/starsky.foundation.georealtime/Converter/GeometryBaseModelConverter.cs b/starsky/starsky.foundation.georealtime/Converter/GeometryBaseModelConverter.cs new file mode 100644 index 0000000000..45ca3fd37e --- /dev/null +++ b/starsky/starsky.foundation.georealtime/Converter/GeometryBaseModelConverter.cs @@ -0,0 +1,52 @@ +using System.Collections.Generic; +using starsky.foundation.georealtime.Models.GeoJson; +using System; +using System.Text.Json; +using System.Text.Json.Serialization; + +namespace starsky.foundation.georealtime.Converter; +public class GeometryBaseModelConverter : JsonConverter +{ + public override GeometryBaseModel Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) + { + using var doc = JsonDocument.ParseValue(ref reader); + var root = doc.RootElement; + + if ( !root.TryGetProperty("type", out var typeElement) || + !root.TryGetProperty("coordinates", out var coordinates) || + coordinates.ValueKind == JsonValueKind.Null || + coordinates.ValueKind == JsonValueKind.Number ) + { + throw new JsonException("Invalid JSON for GeometryBaseModel"); + } + + var geometryType = typeElement.GetString(); + var coordinatesRawText = coordinates.GetRawText(); + + switch (geometryType) + { + case "Point": + return new GeometryPointModel() + { + Coordinates = + JsonSerializer.Deserialize>( + coordinatesRawText) + }; + case "LineString": + return new GeometryLineStringModel() + { + Coordinates = + JsonSerializer.Deserialize>>( + coordinatesRawText) + }; + } + + throw new JsonException("Invalid JSON for GeometryBaseModel"); + } + + public override void Write(Utf8JsonWriter writer, GeometryBaseModel value, JsonSerializerOptions options) + { + JsonSerializer.Serialize(writer, value, value.GetType(), options); + } +} + diff --git a/starsky/starsky.foundation.georealtime/Helpers/DefaultGeoJson.bak b/starsky/starsky.foundation.georealtime/Helpers/DefaultGeoJson.bak new file mode 100644 index 0000000000..6121e5ef68 --- /dev/null +++ b/starsky/starsky.foundation.georealtime/Helpers/DefaultGeoJson.bak @@ -0,0 +1,60 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using starsky.foundation.georealtime.Models.GeoJson; + +namespace starsky.foundation.georealtime.Helpers; + +public static class DefaultGeoJson +{ + public static FeatureCollectionModel CreateDefaultGeoJson(List> coordinates, GeometryType geometryType = GeometryType.LineString) + { + + switch ( geometryType ) + { + case GeometryType.Point: + { + return new FeatureCollectionModel + { + Type = FeatureCollectionType.FeatureCollection, + Features = new List + { + new FeatureModel + { + Type = FeatureType.Feature, + Geometry = new GeometryPointModel() + { + Coordinates = coordinates.FirstOrDefault() ?? new List() + }, + Properties = new PropertiesModel + { + } + } + } + }; + } + case GeometryType.LineString: + { + return new FeatureCollectionModel + { + Type = FeatureCollectionType.FeatureCollection, + Features = new List + { + new FeatureModel + { + Type = FeatureType.Feature, + Geometry = new GeometryLineStringModel() + { + Coordinates = coordinates + }, + Properties = new PropertiesModel() + } + } + }; + + } + default: + throw new ArgumentOutOfRangeException(nameof(geometryType), geometryType, null); + } + } +} diff --git a/starsky/starsky.foundation.georealtime/Helpers/DefaultGeoJson.cs b/starsky/starsky.foundation.georealtime/Helpers/DefaultGeoJson.cs deleted file mode 100644 index 8c5b2a1227..0000000000 --- a/starsky/starsky.foundation.georealtime/Helpers/DefaultGeoJson.cs +++ /dev/null @@ -1,27 +0,0 @@ -using System.Collections.Generic; -using starsky.foundation.georealtime.Models.GeoJson; - -namespace starsky.foundation.georealtime.Helpers; - -public static class DefaultGeoJson -{ - public static FeatureCollectionModel CreateDefaultGeoJson(List> coordinates) - { - return new FeatureCollectionModel - { - Type = FeatureCollectionType.FeatureCollection, - Features = new List - { - new FeatureModel - { - Type = FeatureType.Feature, - Geometry = new GeometryModel - { - Coordinates = coordinates - }, - Properties = new PropertiesModel() - } - } - }; - } -} diff --git a/starsky/starsky.foundation.georealtime/Helpers/IntermediateModelConverter.cs b/starsky/starsky.foundation.georealtime/Helpers/IntermediateModelConverter.cs new file mode 100644 index 0000000000..059fb552c5 --- /dev/null +++ b/starsky/starsky.foundation.georealtime/Helpers/IntermediateModelConverter.cs @@ -0,0 +1,127 @@ +using System; +using System.Collections.Generic; +using System.Globalization; +using System.IO; +using System.Linq; +using System.Reflection; +using System.Xml; +using starsky.foundation.georealtime.Models; +using starsky.foundation.georealtime.Models.GeoJson; + +namespace starsky.foundation.georealtime.Helpers; + +public static class IntermediateModelConverter +{ + public static FeatureCollectionModel Covert2GeoJson( + List inputModel, bool addPoints = true) + { + + // todo add splliter by day - inactive + + var featureCollection = ConvertLineGeoJson(inputModel, new FeatureCollectionModel + { + Type = FeatureCollectionType.FeatureCollection, + Features = new List() + }); + + return !addPoints ? featureCollection : ConvertPointsGeoJson(inputModel, featureCollection); + } + + private static FeatureCollectionModel ConvertLineGeoJson( + List inputModel, + FeatureCollectionModel featureCollection) + { + var lineStringModel = new FeatureModel + { + Type = FeatureType.Feature, + Geometry = new GeometryLineStringModel() + { + Type = GeometryType.LineString, + Coordinates = + new List>() + }, + Properties = new PropertiesModel + { + DateTime = inputModel.FirstOrDefault()?.DateTime ?? DateTime.UtcNow + } + }; + + foreach ( var model in inputModel ) + { + (lineStringModel.Geometry as GeometryLineStringModel)!.Coordinates!.Add(new List + { + model.Latitude, + model.Longitude, + model.Altitude ?? 0 + }); + } + + featureCollection.Features.Add(lineStringModel); + + return featureCollection; + } + + private static FeatureCollectionModel ConvertPointsGeoJson(List inputModel, FeatureCollectionModel featureCollection) + { + foreach ( var model in inputModel ) + { + featureCollection.Features.Add(new FeatureModel + { + Type = FeatureType.Feature, + Geometry = new GeometryPointModel() + { + Type = GeometryType.Point, + Coordinates = + new List + { + model.Latitude, + model.Longitude, + model.Altitude ?? 0 + } + }, + Properties = new PropertiesModel + { + DateTime = model.DateTime + } + }); + } + + return featureCollection; + } + + public static string ConvertToGpx(List waypoints) + { + var settings = new XmlWriterSettings + { + Indent = true, + IndentChars = " " + }; + + using var stringWriter = new StringWriter(); + using var xmlWriter = XmlWriter.Create(stringWriter, settings); + xmlWriter.WriteStartElement("gpx", "http://www.topografix.com/GPX/1/1"); + xmlWriter.WriteAttributeString("version", "1.1"); + xmlWriter.WriteAttributeString("creator", $"Starsky {Assembly.GetExecutingAssembly().GetName().Version?.ToString()}"); + + foreach (var waypoint in waypoints) + { + xmlWriter.WriteStartElement("wpt"); + xmlWriter.WriteAttributeString("lat", waypoint.Latitude.ToString(CultureInfo.InvariantCulture)); + xmlWriter.WriteAttributeString("lon", waypoint.Longitude.ToString(CultureInfo.InvariantCulture)); + + if (waypoint.Altitude.HasValue) + { + xmlWriter.WriteElementString("ele", waypoint.Altitude.Value.ToString(CultureInfo.InvariantCulture)); + } + + xmlWriter.WriteElementString("time", waypoint.DateTime.ToString("yyyy-MM-ddTHH:mm:ssZ")); + + xmlWriter.WriteEndElement(); // Close wpt element + } + + xmlWriter.WriteEndElement(); // Close gpx element + xmlWriter.Flush(); + + return stringWriter.ToString(); + } +} diff --git a/starsky/starsky.foundation.georealtime/Helpers/Kml2IntermediateModel.cs b/starsky/starsky.foundation.georealtime/Helpers/Kml2IntermediateModel.cs new file mode 100644 index 0000000000..3f4447eade --- /dev/null +++ b/starsky/starsky.foundation.georealtime/Helpers/Kml2IntermediateModel.cs @@ -0,0 +1,81 @@ +using System; +using System.Collections.Generic; +using System.Globalization; +using System.Linq; +using System.Xml.Linq; +using starsky.foundation.georealtime.Models; +using starsky.foundation.platform.Helpers; + +namespace starsky.foundation.georealtime.Helpers; + +public static class Kml2IntermediateModelConverter +{ + + public static List ParseKml(XContainer kml) + { + var coordinateIntermediateModel = new List(); + + XNamespace kmlNamespace = "http://www.opengis.net/kml/2.2"; + var placeMarks = kml.Descendants(kmlNamespace + "Placemark"); + + foreach (var placeMark in placeMarks) + { + var timeUtc = GetOpenGisDateTime(placeMark); + + // Extract coordinates from Point element + var coordinatesNode = placeMark.Descendants(kmlNamespace + "coordinates").FirstOrDefault(); + if (coordinatesNode != null) + { + string[] coordinates = coordinatesNode.Value.Split(','); + + // Extract latitude, longitude, and altitude + var latitude = GetDoubleValue(coordinates[1].Trim()); + var longitude = GetDoubleValue(coordinates[0].Trim()); + var altitude = GetDoubleValue(coordinates[2].Trim()); + + if ( latitude == null || + longitude == null || + !ValidateLocation.ValidateLatitudeLongitude(latitude.Value, longitude.Value) || + coordinateIntermediateModel.Any(p => Equals(p.Latitude, latitude) && Equals(p.Longitude, longitude))) + { + continue; + } + + coordinateIntermediateModel.Add(new LatitudeLongitudeAltDateTimeModel + { + Longitude = longitude.Value, + Latitude = latitude.Value, + Altitude = altitude, + DateTime = timeUtc + }); + + Console.WriteLine($"Latitude: {latitude}, Longitude: {longitude}, Altitude: {altitude}"); + } + + } + return coordinateIntermediateModel; + } + + private static double? GetDoubleValue(string input) + { + if ( double.TryParse(input, NumberStyles.Any, CultureInfo.InvariantCulture, out var value) ) + { + return value; + } + return null; + } + + private static DateTime GetOpenGisDateTime(XContainer placeMark) + { + var timeUtc = DateTime.UtcNow; + var timeUtcElement = placeMark.Descendants("{http://www.opengis.net/kml/2.2}Data") + .FirstOrDefault(e => e.Attribute("name")?.Value == "Time UTC"); + + var timeUtcString = timeUtcElement?.Element("{http://www.opengis.net/kml/2.2}value")?.Value; + if ( !string.IsNullOrEmpty(timeUtcString) ) + { + timeUtc = DateTime.ParseExact(timeUtcString, "M/d/yyyy h:mm:ss tt", CultureInfo.InvariantCulture); + } + return timeUtc; + } +} diff --git a/starsky/starsky.foundation.georealtime/Models/GeoJson/FeatureModel.cs b/starsky/starsky.foundation.georealtime/Models/GeoJson/FeatureModel.cs index 7622e4b503..b3d3b48ba2 100644 --- a/starsky/starsky.foundation.georealtime/Models/GeoJson/FeatureModel.cs +++ b/starsky/starsky.foundation.georealtime/Models/GeoJson/FeatureModel.cs @@ -3,6 +3,6 @@ namespace starsky.foundation.georealtime.Models.GeoJson; public class FeatureModel { public FeatureType? Type { get; set; } - public GeometryModel Geometry { get; set; } + public GeometryBaseModel? Geometry { get; set; } public PropertiesModel? Properties { get; set; } } diff --git a/starsky/starsky.foundation.georealtime/Models/GeoJson/GeometryBaseModel.cs b/starsky/starsky.foundation.georealtime/Models/GeoJson/GeometryBaseModel.cs new file mode 100644 index 0000000000..43de35d8d1 --- /dev/null +++ b/starsky/starsky.foundation.georealtime/Models/GeoJson/GeometryBaseModel.cs @@ -0,0 +1,10 @@ +using System.Text.Json.Serialization; +using starsky.foundation.georealtime.Converter; + +namespace starsky.foundation.georealtime.Models.GeoJson; + +[JsonConverter(typeof(GeometryBaseModelConverter))] +public abstract class GeometryBaseModel +{ + public virtual GeometryType? Type { get; set; } +} diff --git a/starsky/starsky.foundation.georealtime/Models/GeoJson/GeometryLineStringModel.cs b/starsky/starsky.foundation.georealtime/Models/GeoJson/GeometryLineStringModel.cs new file mode 100644 index 0000000000..bc7dc11926 --- /dev/null +++ b/starsky/starsky.foundation.georealtime/Models/GeoJson/GeometryLineStringModel.cs @@ -0,0 +1,11 @@ +using System.Collections.Generic; + +namespace starsky.foundation.georealtime.Models.GeoJson; + +public class GeometryLineStringModel : GeometryBaseModel +{ + public override GeometryType? Type { get; set; } = GeometryType.LineString; + + public List>? Coordinates { get; set; } = new List>(); + +} diff --git a/starsky/starsky.foundation.georealtime/Models/GeoJson/GeometryModel.cs b/starsky/starsky.foundation.georealtime/Models/GeoJson/GeometryModel.cs deleted file mode 100644 index efde9031ca..0000000000 --- a/starsky/starsky.foundation.georealtime/Models/GeoJson/GeometryModel.cs +++ /dev/null @@ -1,28 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; - -namespace starsky.foundation.georealtime.Models.GeoJson; - -public class GeometryModel -{ - public GeometryType? Type { get; set; } - - private List> CoordinatesPrivate { get; set; } = - new List>(); - - public List> Coordinates - { - get => CoordinatesPrivate; - set - { - if ( value.Any(coordinatesArray => coordinatesArray.Count != 2 && coordinatesArray.Count != 3) ) - { - throw new ArgumentException("Coordinates must be 2 or 3 dimensional"); - } - - CoordinatesPrivate = value; - } - } - -} diff --git a/starsky/starsky.foundation.georealtime/Models/GeoJson/GeometryPointModel.cs b/starsky/starsky.foundation.georealtime/Models/GeoJson/GeometryPointModel.cs new file mode 100644 index 0000000000..7dce326333 --- /dev/null +++ b/starsky/starsky.foundation.georealtime/Models/GeoJson/GeometryPointModel.cs @@ -0,0 +1,11 @@ +using System.Collections.Generic; + +namespace starsky.foundation.georealtime.Models.GeoJson; + +public class GeometryPointModel : GeometryBaseModel +{ + public override GeometryType? Type { get; set; } = GeometryType.Point; + + public List? Coordinates { get; set; } = new List(); + +} diff --git a/starsky/starsky.foundation.georealtime/Models/LatitudeLongitudeAltDateTimeModel.cs b/starsky/starsky.foundation.georealtime/Models/LatitudeLongitudeAltDateTimeModel.cs index b06a019ab7..667ce0ed52 100644 --- a/starsky/starsky.foundation.georealtime/Models/LatitudeLongitudeAltDateTimeModel.cs +++ b/starsky/starsky.foundation.georealtime/Models/LatitudeLongitudeAltDateTimeModel.cs @@ -4,6 +4,13 @@ namespace starsky.foundation.georealtime.Models; public class LatitudeLongitudeAltDateTimeModel : LatitudeLongitudeModel { - public string Altitude { get; set; } + /// + /// Altitude in meters + /// + public double? Altitude { get; set; } + + /// + /// DateTime in UTC + /// public DateTime DateTime { get; set; } } diff --git a/starsky/starsky.foundation.georealtime/Models/LatitudeLongitudeModel.cs b/starsky/starsky.foundation.georealtime/Models/LatitudeLongitudeModel.cs index cbdd0b91fd..e1ee0442a8 100644 --- a/starsky/starsky.foundation.georealtime/Models/LatitudeLongitudeModel.cs +++ b/starsky/starsky.foundation.georealtime/Models/LatitudeLongitudeModel.cs @@ -2,6 +2,6 @@ namespace starsky.foundation.georealtime.Models; public class LatitudeLongitudeModel { - public string Latitude { get; set; } - public string Longitude { get; set; } + public double Latitude { get; set; } + public double Longitude { get; set; } } diff --git a/starsky/starsky.foundation.georealtime/Services/KmlImport.cs b/starsky/starsky.foundation.georealtime/Services/KmlImport.cs index 8e0c361b66..17b2c59da3 100644 --- a/starsky/starsky.foundation.georealtime/Services/KmlImport.cs +++ b/starsky/starsky.foundation.georealtime/Services/KmlImport.cs @@ -4,6 +4,7 @@ using System.Linq; using System.Threading.Tasks; using System.Xml.Linq; +using starsky.foundation.georealtime.Helpers; using starsky.foundation.georealtime.Interfaces; using starsky.foundation.georealtime.Models; using starsky.foundation.http.Interfaces; @@ -34,7 +35,8 @@ public async Task Import(string kmlPathOrUrl) } var xDocument = XmlParse(readString.Value); - ParseKml(xDocument); + var result = Kml2IntermediateModelConverter.ParseKml(xDocument); + var model = IntermediateModelConverter.Covert2GeoJson(result, true); } private static XDocument XmlParse(string content) @@ -42,44 +44,5 @@ private static XDocument XmlParse(string content) return XDocument.Parse(content); } - private static List ParseKml(XContainer kml) - { - var coordinates = new List(); - DateTime timeUtc = DateTime.UtcNow; - - foreach (var placemark in kml.Descendants("{http://www.opengis.net/kml/2.2}Placemark")) - { - var timeUtcElement = placemark.Descendants("{http://www.opengis.net/kml/2.2}Data") - .FirstOrDefault(e => e.Attribute("name")?.Value == "Time UTC"); - if (timeUtcElement != null) - { - var timeUtcString = timeUtcElement.Element("{http://www.opengis.net/kml/2.2}value")?.Value; - Console.WriteLine(timeUtcString); - timeUtc = DateTime.ParseExact(timeUtcString, "M/d/yyyy h:mm:ss tt", CultureInfo.InvariantCulture); - } - - // now get the coordinates - - var lineString = placemark.Element("{http://www.opengis.net/kml/2.2}LineString"); - var coordinatesElement = lineString?.Element("{http://www.opengis.net/kml/2.2}coordinates"); - - if ( coordinatesElement == null ) continue; - - var coordinatesString = coordinatesElement.Value; - var coordinatesArray = coordinatesString.Split(' '); - // ReSharper disable once LoopCanBeConvertedToQuery - foreach (var coordinate in coordinatesArray) - { - var coordinateArray = coordinate.Split(','); - coordinates.Add(new LatitudeLongitudeAltDateTimeModel - { - Longitude = coordinateArray[0], - Latitude = coordinateArray[1], - Altitude = coordinateArray[2], - DateTime = timeUtc - }); - } - } - return coordinates; - } + } diff --git a/starsky/starsky.foundation.platform/Models/AppSettings.cs b/starsky/starsky.foundation.platform/Models/AppSettings.cs index 24c31f6827..c9482f726f 100644 --- a/starsky/starsky.foundation.platform/Models/AppSettings.cs +++ b/starsky/starsky.foundation.platform/Models/AppSettings.cs @@ -152,16 +152,15 @@ public enum StarskyAppType /// /// Get the Application Version of Starsky /// - public string AppVersion + public string AppVersion => GetAppVersion(); + + private static string GetAppVersion() { - get - { - var assemblyVersion = Assembly.GetExecutingAssembly().GetName().Version?.ToString(); - return string.IsNullOrEmpty(assemblyVersion) ? string.Empty : - new Regex("\\.0$", RegexOptions.None, + var assemblyVersion = Assembly.GetExecutingAssembly().GetName().Version?.ToString(); + return string.IsNullOrEmpty(assemblyVersion) ? string.Empty : + new Regex("\\.0$", RegexOptions.None, TimeSpan.FromMilliseconds(100)) - .Replace(assemblyVersion, string.Empty); - } + .Replace(assemblyVersion, string.Empty); } [PackageTelemetry] diff --git a/starsky/starsky/Helpers/SwaggerExportHelper.cs b/starsky/starsky/Helpers/SwaggerExportHelper.cs index 9f55f2756c..628673bb4c 100644 --- a/starsky/starsky/Helpers/SwaggerExportHelper.cs +++ b/starsky/starsky/Helpers/SwaggerExportHelper.cs @@ -107,6 +107,7 @@ internal static string GenerateSwagger(ISwaggerProvider swaggerProvider, string { if ( swaggerProvider == null ) return string.Empty; var swaggerDocument = swaggerProvider.GetSwagger(docName, null, "/"); + // JsonConvert is newtonSoft see JsonSerializer.Serialize for System.Text.Json var stringOutput = JsonConvert.SerializeObject(swaggerDocument, Formatting.Indented, new JsonSerializerSettings diff --git a/starsky/starskytest/FakeCreateAn/CreateAnGeoJson/example.html b/starsky/starskytest/FakeCreateAn/CreateAnGeoJson/example.html index b6ae8a893c..b6254fd157 100644 --- a/starsky/starskytest/FakeCreateAn/CreateAnGeoJson/example.html +++ b/starsky/starskytest/FakeCreateAn/CreateAnGeoJson/example.html @@ -57,31 +57,36 @@ "geometry": { "type": "LineString", "coordinates": [ - [5.485941, 51.809360, 7.263], - [5.485724, 51.807968, 7.772], - [5.485631, 51.807019, 9.957], - [5.485610, 51.806702, 10.808], - [5.485633, 51.805663, 10.866], - [5.485738, 51.805500, 9.242], - [5.486056, 51.805115, 9.122] + [ + 4.89707, + 52.377956, + 7.4 + ], + [ + 4.99707, + 52.377956, + 7.4 + ] ] }, "properties": { - "name": "_20180905-fietsen-oss", - "time": "2018-09-11T19:41:10.482Z", - "activity": "biking", - "displayColor": "DarkRed" + "name": null, + "dateTime": "0001-01-01T00:00:00" } }, { "type": "Feature", "geometry": { "type": "Point", - "coordinates": [5.485941, 51.809360, 7.263] + "coordinates": [ + 4.89707, + 52.377956, + 7.4 + ] }, "properties": { - "name": "Location 1", - "datetime": "2023-12-21T12:34:56Z" + "name": null, + "dateTime": "2023-12-23T15:16:27.153601+01:00" } } ] diff --git a/starsky/starskytest/FakeCreateAn/CreateAnKml/CreateAnKml.cs b/starsky/starskytest/FakeCreateAn/CreateAnKml/CreateAnKml.cs new file mode 100644 index 0000000000..78b9b073aa --- /dev/null +++ b/starsky/starskytest/FakeCreateAn/CreateAnKml/CreateAnKml.cs @@ -0,0 +1,30 @@ +using System.IO; +using System.Reflection; +using starsky.foundation.storage.Storage; +using starskytest.FakeMocks; + +namespace starskytest.FakeCreateAn.CreateAnKml; + +public class CreateAnKml +{ + public CreateAnKml() + { + var dirName = Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location); + if ( string.IsNullOrEmpty(dirName) ) return; + var path = Path.Combine(dirName, "FakeCreateAn", + "CreateAnKml", "garmin.kml"); + + Bytes = StreamToBytes(path); + } + + private static byte[] StreamToBytes(string path) + { + var input = new StorageHostFullPathFilesystem(new FakeIWebLogger()).ReadStream(path); + using var ms = new MemoryStream(); + input.CopyTo(ms); + input.Dispose(); + return ms.ToArray(); + } + + public readonly byte[] Bytes; +} diff --git a/starsky/starskytest/FakeCreateAn/CreateAnKml/garmin.kml b/starsky/starskytest/FakeCreateAn/CreateAnKml/garmin.kml new file mode 100644 index 0000000000..8d826eb8b5 --- /dev/null +++ b/starsky/starskytest/FakeCreateAn/CreateAnKml/garmin.kml @@ -0,0 +1,135 @@ + + + + KML Export 12/23/2023 3:35:07 PM + + + + + + Owner name + + Owner name + true + + + 2023-06-29T13:15:15Z + + #style_2729459 + + + 0000000 + + + 6/29/2023 1:15:15 PM + + + 6/29/2023 3:15:15 PM + + + Owner name + + + Owner name + + + inReach Mini 2 + + + 00000000000 + + + + + + 42.448768 + + + 19.886626 + + + 942.62 m from MSL + + + 1.0 km/h + + + 90.00 ° True + + + True + + + False + + + + + + Tracking uitgeschakeld vanaf toestel. + + + + + + WGS84 + + + + false + absolute + 19.886626,42.448768,942.62 + + + + Owner name + true + my track log + #linestyle_2729459 + + true + 19.886626,42.448768,942.62 + + + + + \ No newline at end of file diff --git a/starsky/starskytest/starsky.foundation.georealtime/Converter/GeometryBaseModelConverterTest.cs b/starsky/starskytest/starsky.foundation.georealtime/Converter/GeometryBaseModelConverterTest.cs new file mode 100644 index 0000000000..a62eb3f0be --- /dev/null +++ b/starsky/starskytest/starsky.foundation.georealtime/Converter/GeometryBaseModelConverterTest.cs @@ -0,0 +1,108 @@ +using System.Text; +using System.Text.Json; +using Microsoft.VisualStudio.TestTools.UnitTesting; +using starsky.foundation.georealtime.Converter; +using starsky.foundation.georealtime.Models.GeoJson; + +namespace starskytest.starsky.foundation.georealtime.Converter +{ + [TestClass] + public class GeometryBaseModelConverterTests + { + private readonly GeometryBaseModelConverter _converter = new GeometryBaseModelConverter(); + private readonly JsonSerializerOptions _jsonOptions = new JsonSerializerOptions { Converters = { new GeometryBaseModelConverter() } }; + + [TestMethod] + public void Read_WithValidPointGeometry_ReturnsGeometryPointModel() + { + // Arrange + var jsonString = "{\"type\":\"Point\",\"coordinates\":[5.485941,51.809360,7.263]}"; + var reader = new Utf8JsonReader(Encoding.UTF8.GetBytes(jsonString)); + + // Act + var result = _converter.Read(ref reader, typeof(GeometryBaseModel), _jsonOptions); + + // Assert + Assert.IsInstanceOfType(result, typeof(GeometryPointModel)); + var pointModel = (GeometryPointModel)result; + Assert.IsNotNull(pointModel.Coordinates); + Assert.AreEqual(3, pointModel.Coordinates.Count); + } + + [TestMethod] + public void Read_WithValidLineStringGeometry_ReturnsGeometryLineStringModel() + { + // Arrange + var jsonString = "{\"type\":\"LineString\",\"coordinates\":[[5.485941,51.809360,7.263]]}"; + var reader = new Utf8JsonReader(Encoding.UTF8.GetBytes(jsonString)); + + // Act + var result = _converter.Read(ref reader, typeof(GeometryBaseModel), _jsonOptions); + + // Assert + Assert.IsInstanceOfType(result, typeof(GeometryLineStringModel)); + var lineStringModel = (GeometryLineStringModel)result; + Assert.IsNotNull(lineStringModel.Coordinates); + Assert.AreEqual(1, lineStringModel.Coordinates.Count); + } + + private GeometryBaseModel ReadJson(string jsonString) + { + using var doc = JsonDocument.Parse(jsonString); + var reader = new Utf8JsonReader(Encoding.UTF8.GetBytes(jsonString)); + return _converter.Read(ref reader, typeof(GeometryBaseModel), _jsonOptions); + } + + [TestMethod] + public void Read_WithInvalidJson_ThrowsJsonException() + { + // Arrange + var jsonString = "{\"invalid\":\"json\"}"; + + // Act & Assert + Assert.ThrowsException(() => ReadJson(jsonString)); + } + + [TestMethod] + public void Read_WithInvalidJson_DifferentType_ThrowsJsonException() + { + // Arrange + var jsonString = "{\"type\":\"WRONG_TYPE\",\"coordinates\":[[5.485941,51.809360,7.263]]}"; + + // Act & Assert + Assert.ThrowsException(() => ReadJson(jsonString)); + } + + [TestMethod] + public void Read_WithInvalidJson_MissingCoordinates_ThrowsJsonException() + { + // Arrange + var jsonString = "{\"type\":\"LineString\"}"; + + // Act & Assert + Assert.ThrowsException(() => ReadJson(jsonString)); + } + + + [TestMethod] + public void Read_WithInvalidJson_CoordinatesNoContent_ThrowsJsonException() + { + // Arrange + const string jsonString = "{\"type\":\"LineString\",\"coordinates\":null }"; + + // Act & Assert + Assert.ThrowsException(() => ReadJson(jsonString)); + } + + [TestMethod] + public void Read_WithInvalidJson_CoordinatesNumber_ThrowsJsonException() + { + // Arrange + const string jsonString = "{\"type\":\"LineString\",\"coordinates\":1 }"; + + // Act & Assert + Assert.ThrowsException(() => ReadJson(jsonString)); + } + + } +} diff --git a/starsky/starskytest/starsky.foundation.georealtime/Helpers/IntermediateModelConverterTest.cs b/starsky/starskytest/starsky.foundation.georealtime/Helpers/IntermediateModelConverterTest.cs new file mode 100644 index 0000000000..be61db6729 --- /dev/null +++ b/starsky/starskytest/starsky.foundation.georealtime/Helpers/IntermediateModelConverterTest.cs @@ -0,0 +1,210 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text.Json; +using Microsoft.VisualStudio.TestTools.UnitTesting; +using starsky.foundation.georealtime.Helpers; +using starsky.foundation.georealtime.Models; +using starsky.foundation.georealtime.Models.GeoJson; +using starsky.foundation.platform.JsonConverter; + +namespace starskytest.starsky.foundation.georealtime.Helpers +{ + [TestClass] + public class IntermediateModelConverterTests + { + [TestMethod] + public void Convert2GeoJson_ReturnsFeatureCollection_WithPoint() + { + // Arrange + var inputModel = new List + { + // Populate with test data + new LatitudeLongitudeAltDateTimeModel + { + Longitude = 52.377956, + Latitude = 4.897070, // amsterdam + Altitude = 7.4, + DateTime = DateTime.Now + }, + }; + + // Act + var result = IntermediateModelConverter.Covert2GeoJson(inputModel); + + // Assert + Assert.IsNotNull(result); + + Assert.AreEqual(2, result.Features.Count); + + Assert.AreEqual(GeometryType.LineString, result.Features.FirstOrDefault()?.Geometry?.Type); + + var lineStringModel = result.Features.FirstOrDefault()?. + Geometry as GeometryLineStringModel; + // [longitude, latitude, altitude] + Assert.AreEqual(4.897070 , lineStringModel?.Coordinates?.FirstOrDefault()?[0]); + Assert.AreEqual(52.377956, lineStringModel?.Coordinates?.FirstOrDefault()?[1]); + Assert.AreEqual(7.4, lineStringModel?.Coordinates?.FirstOrDefault()?[2]); + + var pointModel = result.Features[1]?. + Geometry as GeometryPointModel; + + Assert.AreEqual(GeometryType.Point, result.Features[1].Geometry?.Type); + Assert.AreEqual(4.89707, pointModel?.Coordinates?[0]); + Assert.AreEqual(52.377956, pointModel?.Coordinates?[1]); + Assert.AreEqual(7.4, pointModel?.Coordinates?[2]); + } + + [TestMethod] + public void Convert2GeoJson_ReturnsFeatureCollection_NoPoint() + { + // Arrange + var inputModel = new List + { + // Populate with test data + new LatitudeLongitudeAltDateTimeModel + { + Longitude = 52.377956, + Latitude = 4.897070, // amsterdam + Altitude = 7.4, + DateTime = DateTime.Now + }, + }; + + // Act + var result = IntermediateModelConverter.Covert2GeoJson(inputModel, false); + + // Assert + Assert.IsNotNull(result); + + Assert.AreEqual(1, result.Features.Count); + + Assert.AreEqual(GeometryType.LineString, result.Features.FirstOrDefault()?.Geometry?.Type); + + var lineStringModel = result.Features.FirstOrDefault()?. + Geometry as GeometryLineStringModel; + + Assert.AreEqual(4.897070 , lineStringModel?.Coordinates?.FirstOrDefault()?[0]); + Assert.AreEqual(52.377956, lineStringModel?.Coordinates?.FirstOrDefault()?[1]); + Assert.AreEqual(7.4, lineStringModel?.Coordinates?.FirstOrDefault()?[2]); + } + + [TestMethod] + public void TwoPoints() + { + var input = new List + { + new LatitudeLongitudeAltDateTimeModel + { + Longitude = 42.448770, + Latitude = 1.2, + Altitude = 170, + DateTime = new DateTime(2023, 6, 29, 13, 15, 16) + }, + new LatitudeLongitudeAltDateTimeModel + { + Longitude = 42.448769, + Latitude = 1.2, + Altitude = 170, + DateTime = new DateTime(2023, 6, 29, 13, 15, 16) + } + }; + + var result = IntermediateModelConverter.Covert2GeoJson(input, true); + + Assert.IsNotNull(result); + + Assert.AreEqual(3, result.Features.Count); + + Assert.AreEqual(GeometryType.LineString, result.Features.FirstOrDefault()?.Geometry?.Type); + + var lineStringModel = result.Features.FirstOrDefault()?. + Geometry as GeometryLineStringModel; + + // [longitude, latitude, altitude] + Assert.AreEqual(1.2 , lineStringModel?.Coordinates?.FirstOrDefault()?[0]); + Assert.AreEqual(42.448770, lineStringModel?.Coordinates?.FirstOrDefault()?[1]); + Assert.AreEqual(170, lineStringModel?.Coordinates?.FirstOrDefault()?[2]); + + Assert.AreEqual(1.2 , lineStringModel?.Coordinates?[1]?[0]); + Assert.AreEqual(42.448769, lineStringModel?.Coordinates?[1]?[1]); + Assert.AreEqual(170, lineStringModel?.Coordinates?[1]?[2]); + + var pointModel1 = result.Features[1]?. + Geometry as GeometryPointModel; + + Assert.AreEqual(GeometryType.Point, result.Features[1].Geometry?.Type); + Assert.AreEqual(1.2, pointModel1?.Coordinates?[0]); + Assert.AreEqual(42.44877, pointModel1?.Coordinates?[1]); + Assert.AreEqual(170, pointModel1?.Coordinates?[2]); + + var pointModel2 = result.Features[2]?. + Geometry as GeometryPointModel; + + Assert.AreEqual(GeometryType.Point, result.Features[1].Geometry?.Type); + Assert.AreEqual(1.2, pointModel2?.Coordinates?[0]); + Assert.AreEqual(42.448769, pointModel2?.Coordinates?[1]); + Assert.AreEqual(170, pointModel2?.Coordinates?[2]); + } + + [TestMethod] + public void TwoPointsJson() + { + var input = new List + { + new LatitudeLongitudeAltDateTimeModel + { + Longitude = 42.448770, + Latitude = 1.2, + Altitude = 170, + DateTime = new DateTime(2023, 6, 29, 13, 15, 16) + }, + new LatitudeLongitudeAltDateTimeModel + { + Longitude = 42.448769, + Latitude = 1.2, + Altitude = 170, + DateTime = new DateTime(2023, 6, 29, 13, 15, 16) + } + }; + + var result = IntermediateModelConverter.Covert2GeoJson(input, true); + var jsonResult = JsonSerializer.Serialize(result, DefaultJsonSerializer.CamelCaseNoEnters); + + Assert.AreEqual("{\"type\":\"FeatureCollection\",\"features\":" + + "[{\"type\":\"Feature\",\"geometry\":{\"type\":\"LineString\"," + + "\"coordinates\":[[1.2,42.44877,170],[1.2,42.448769,170]]}," + + "\"properties\":{\"name\":null,\"dateTime\":\"2023-06-29T13:15:16\"}}," + + "{\"type\":\"Feature\",\"geometry\":{\"type\":\"Point\",\"coordinates\":" + + "[1.2,42.44877,170]},\"properties\":{\"name\":null,\"dateTime\":\"2023-06-29T13:15:16\"}}," + + "{\"type\":\"Feature\",\"geometry\":{\"type\":\"Point\",\"coordinates\":[1.2,42.448769,170]}," + + "\"properties\":{\"name\":null,\"dateTime\":\"2023-06-29T13:15:16\"}}]}",jsonResult); + + } + + [TestMethod] + public void ConvertToGpx_ReturnsValidGpxString() + { + // Arrange + var waypoints = new List + { + // Populate with test data + new LatitudeLongitudeAltDateTimeModel + { + Longitude = 0.0, + Latitude = 0.0, + Altitude = 100.0, + DateTime = DateTime.Now + }, + // Add more test data as needed + }; + + // Act + var result = IntermediateModelConverter.ConvertToGpx(waypoints); + + // Assert + Assert.IsNotNull(result); + // Add more specific assertions based on your expectations + } + } +} diff --git a/starsky/starskytest/starsky.foundation.georealtime/Models/GeometryModelTest.cs b/starsky/starskytest/starsky.foundation.georealtime/Models/GeometryModelTest.cs deleted file mode 100644 index 98aa37ce6b..0000000000 --- a/starsky/starskytest/starsky.foundation.georealtime/Models/GeometryModelTest.cs +++ /dev/null @@ -1,55 +0,0 @@ -using System; -using System.Collections.Generic; -using Microsoft.VisualStudio.TestTools.UnitTesting; -using starsky.foundation.georealtime.Models.GeoJson; - -namespace starskytest.starsky.foundation.georealtime.Models; - -[TestClass] -public class GeometryModelTest -{ - - [TestMethod] - public void NoItemsInList() - { - var model = new GeometryModel { Coordinates = new List>() }; - Assert.AreEqual(0,model.Coordinates.Count); - } - - [TestMethod] - [ExpectedException(typeof(ArgumentException))] - public void InvalidList() - { - var geometryModel = new GeometryModel { Coordinates = new List>{new List()} }; - - Assert.IsNull(geometryModel); - } - - [TestMethod] - [ExpectedException(typeof(ArgumentException))] - public void InvalidList_OneItem() - { - var geometryModel = new GeometryModel { Coordinates = new List>{new List{0}} }; - - Assert.IsNull(geometryModel); - } - - [TestMethod] - public void InvalidList_TwoItems() - { - var geometryModel = new GeometryModel { Coordinates = new List>{new List{0,1}} }; - - Assert.AreEqual(0,geometryModel.Coordinates[0][0]); - Assert.AreEqual(1,geometryModel.Coordinates[0][1]); - } - - [TestMethod] - public void InvalidList_ThreeItems() - { - var geometryModel = new GeometryModel { Coordinates = new List>{new List{0,1,2}} }; - - Assert.AreEqual(0,geometryModel.Coordinates[0][0]); - Assert.AreEqual(1,geometryModel.Coordinates[0][1]); - Assert.AreEqual(2,geometryModel.Coordinates[0][2]); - } -} diff --git a/starsky/starskytest/starsky.foundation.georealtime/Models/ModelsParserTest.cs b/starsky/starskytest/starsky.foundation.georealtime/Models/ModelsParserTest.cs index 9433ec5842..c377a190cd 100644 --- a/starsky/starskytest/starsky.foundation.georealtime/Models/ModelsParserTest.cs +++ b/starsky/starskytest/starsky.foundation.georealtime/Models/ModelsParserTest.cs @@ -1,4 +1,3 @@ -using Microsoft.AspNetCore.Http.Features; using Microsoft.VisualStudio.TestTools.UnitTesting; using starsky.foundation.georealtime.Models.GeoJson; using starsky.foundation.platform.JsonConverter; @@ -9,8 +8,9 @@ namespace starskytest.starsky.foundation.georealtime.Models; public class ModelsParserTest { [TestMethod] - public void TestWithExample() + public void LineStringParser() { + // [longitude, latitude, altitude] const string geoJson = @" { ""type"": ""FeatureCollection"", @@ -48,7 +48,7 @@ public void TestWithExample() Assert.IsNotNull(feature); Assert.AreEqual(FeatureType.Feature, feature.Type); - var geometry = feature.Geometry; + var geometry = feature.Geometry as GeometryLineStringModel; Assert.IsNotNull(geometry); Assert.AreEqual(GeometryType.LineString, geometry.Type); @@ -57,4 +57,102 @@ public void TestWithExample() Assert.AreEqual(7, coordinates.Count); } + [TestMethod] + public void PointParser() + { + // [longitude, latitude, altitude] + const string geoJson = @" + { + ""type"": ""FeatureCollection"", + ""features"": [ + { + ""type"": ""Feature"", + ""geometry"": { + ""type"": ""Point"", + ""coordinates"": + [5.485941, 51.809360, 7.263] + } + } + ] + }"; + + // Deserialize GeoJSON string to C# objects + var featureCollection = System.Text.Json.JsonSerializer.Deserialize(geoJson, + DefaultJsonSerializer.CamelCase); + + // Assert + Assert.IsNotNull(featureCollection); + Assert.AreEqual(FeatureCollectionType.FeatureCollection, featureCollection.Type); + + Assert.IsNotNull(featureCollection.Features); + Assert.AreEqual(1, featureCollection.Features.Count); + + var feature = featureCollection.Features[0]; + Assert.IsNotNull(feature); + Assert.AreEqual(FeatureType.Feature, feature.Type); + + var geometry = feature.Geometry as GeometryPointModel; + Assert.IsNotNull(geometry); + Assert.AreEqual(GeometryType.Point, geometry.Type); + + var coordinates = geometry.Coordinates; + Assert.IsNotNull(coordinates); + Assert.AreEqual(3, coordinates.Count); + + Assert.AreEqual(5.485941, coordinates[0]); + Assert.AreEqual(51.809360, coordinates[1]); + Assert.AreEqual(7.263, coordinates[2]); + } + + [TestMethod] + public void LineStringAndPointParser() + { + // [longitude, latitude, altitude] + const string geoJson = @" + { + ""type"": ""FeatureCollection"", + ""features"": [ + { + ""type"": ""Feature"", + ""geometry"": { + ""type"": ""LineString"", + ""coordinates"": [ + [5.485941, 51.809360, 7.263], + [5.485724, 51.807968, 7.772] + ] + } + }, + { + ""type"": ""Feature"", + ""geometry"": { + ""type"": ""Point"", + ""coordinates"": + [5.485941, 51.809360, 7.263] + } + } + ] + }"; + + var featureCollection = System.Text.Json.JsonSerializer.Deserialize(geoJson, + DefaultJsonSerializer.CamelCase); + + // First LineString + var feature0 = featureCollection.Features[0]; + Assert.IsNotNull(feature0); + Assert.AreEqual(FeatureType.Feature, feature0.Type); + + var geometry0 = feature0.Geometry as GeometryLineStringModel; + Assert.IsNotNull(geometry0); + Assert.AreEqual(GeometryType.LineString, geometry0.Type); + + // Second Point + var feature1 = featureCollection.Features[1]; + Assert.IsNotNull(feature1); + Assert.AreEqual(FeatureType.Feature, feature1.Type); + + var geometry1 = feature1.Geometry as GeometryPointModel; + Assert.IsNotNull(geometry1); + Assert.AreEqual(GeometryType.Point, geometry1.Type); + + } } diff --git a/starsky/starskytest/starsky.foundation.georealtime/Services/KmlImportTest.cs b/starsky/starskytest/starsky.foundation.georealtime/Services/KmlImportTest.cs new file mode 100644 index 0000000000..7dd8966a5a --- /dev/null +++ b/starsky/starskytest/starsky.foundation.georealtime/Services/KmlImportTest.cs @@ -0,0 +1,53 @@ +using System; +using System.Collections.Generic; +using System.Net.Http; +using System.Text; +using System.Threading.Tasks; +using System.Xml.Linq; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.VisualStudio.TestTools.UnitTesting; +using starsky.foundation.georealtime.Helpers; +using starsky.foundation.georealtime.Models; +using starsky.foundation.georealtime.Services; +using starsky.foundation.http.Services; +using starsky.foundation.platform.Models; +using starsky.foundation.storage.Interfaces; +using starsky.foundation.storage.Storage; +using starskytest.FakeCreateAn.CreateAnKml; +using starskytest.FakeMocks; + +namespace starskytest.starsky.foundation.georealtime.Services; + +[TestClass] +public class KmlImportTest +{ + private readonly IServiceScopeFactory _serviceScopeFactory; + + public KmlImportTest() + { + var services = new ServiceCollection(); + services.AddSingleton(); + services.AddSingleton(); + var serviceProvider = services.BuildServiceProvider(); + _serviceScopeFactory = + serviceProvider.GetRequiredService(); + } + + [TestMethod] + public async Task Test() + { + var createAnKml = new CreateAnKml().Bytes; + var fakeIHttpProvider = new FakeIHttpProvider(new Dictionary + { + {"https://share.garmin.com/Feed/Share/test", new StringContent(Encoding.UTF8.GetString(createAnKml))}, + }); + var httpClientHelper = new HttpClientHelper(fakeIHttpProvider, _serviceScopeFactory, new FakeIWebLogger(), + new AppSettings{ AllowedHttpsDomains = new List{"share.garmin.com"}}); + + await new KmlImport(httpClientHelper, new FakeSelectorStorage()).Import("https://share.garmin.com/Feed/Share/test"); + + Console.WriteLine(); + } + + +} diff --git a/starsky/starskytest/starskytest.csproj b/starsky/starskytest/starskytest.csproj index 7997fe02f0..934120afd7 100644 --- a/starsky/starskytest/starskytest.csproj +++ b/starsky/starskytest/starskytest.csproj @@ -114,6 +114,10 @@ PreserveNewest + + + PreserveNewest + From 55b30684253b63d87a9abcbd304f764256540c3b Mon Sep 17 00:00:00 2001 From: Dion Date: Thu, 28 Dec 2023 15:53:23 +0100 Subject: [PATCH 7/7] ideas --- .../Services/GeoCli.cs | 4 +- .../Services/MoveToTrashService.cs | 4 +- .../Services/UserManager.cs | 6 +- .../Helpers/StatusCodesHelper.cs | 6 +- .../Helpers/TelemetryConfigurationHelper.cs | 4 +- .../Helpers/IntermediateModelConverter.cs | 8 +- .../Models/GeoJson/FeatureCollectionModel.cs | 2 +- .../Models/KmlImportOptions.cs | 2 +- .../Services/KmlImport.cs | 34 ++++- .../Interfaces/IHttpProvider.cs | 2 +- .../Services/HttpProvider.cs | 7 +- .../Models/AppSettings.cs | 137 ++++++++++++------ .../Models/AppSettingsExternalAccounts.cs | 12 ++ .../Storage/StorageSubPathFilesystem.cs | 30 ++-- .../FakeMocks/FakeIHttpProvider.cs | 2 +- .../Services/KmlImportTest.cs | 2 +- .../Models/AppSettingsTest.cs | 71 ++++++++- 17 files changed, 243 insertions(+), 90 deletions(-) create mode 100644 starsky/starsky.foundation.platform/Models/AppSettingsExternalAccounts.cs diff --git a/starsky/starsky.feature.geolookup/Services/GeoCli.cs b/starsky/starsky.feature.geolookup/Services/GeoCli.cs index a8412a5797..586bc3c183 100644 --- a/starsky/starsky.feature.geolookup/Services/GeoCli.cs +++ b/starsky/starsky.feature.geolookup/Services/GeoCli.cs @@ -89,7 +89,7 @@ public async Task CommandLineAsync(string[] args) { inputPath = _appSettings.DatabasePathToFilePath( ArgsHelper.GetSubPathFormArgs(args) - ); + )!; } else { @@ -105,7 +105,7 @@ public async Task CommandLineAsync(string[] args) var dateTime = DateTime.Now.AddDays(( double ) getSubPathRelative); inputPath = _appSettings.DatabasePathToFilePath( new StructureService(_iStorage, _appSettings.Structure) - .ParseSubfolders(dateTime),false); + .ParseSubfolders(dateTime),false)!; } // used in this session to find the files back diff --git a/starsky/starsky.feature.trash/Services/MoveToTrashService.cs b/starsky/starsky.feature.trash/Services/MoveToTrashService.cs index 81b504f369..b88da5031f 100644 --- a/starsky/starsky.feature.trash/Services/MoveToTrashService.cs +++ b/starsky/starsky.feature.trash/Services/MoveToTrashService.cs @@ -142,11 +142,11 @@ await _metaUpdateService.UpdateAsync(changedFileIndexItemName, return (moveToTrash,changedFileIndexItemName); } - internal async Task SystemTrashInQueue(List moveToTrash) + private async Task SystemTrashInQueue(List moveToTrash) { var fullFilePaths = moveToTrash .Where(p => p.FilePath != null) - .Select(p => _appSettings.DatabasePathToFilePath(p.FilePath, false)) + .Select(p => _appSettings.DatabasePathToFilePath(p.FilePath!, false)).Cast() .ToList(); _systemTrashService.Trash(fullFilePaths); diff --git a/starsky/starsky.foundation.accountmanagement/Services/UserManager.cs b/starsky/starsky.foundation.accountmanagement/Services/UserManager.cs index 24d2bc746d..3919274cf2 100644 --- a/starsky/starsky.foundation.accountmanagement/Services/UserManager.cs +++ b/starsky/starsky.foundation.accountmanagement/Services/UserManager.cs @@ -230,9 +230,9 @@ internal string GetRoleAddToUser(string identifier, User user) return AccountRoles.AppAccountRoles.Administrator.ToString(); } - if ( _appSettings.AccountRolesByEmailRegisterOverwrite - .TryGetValue(identifier, out var emailsForConfig) && - AccountRoles.GetAllRoles().Contains(emailsForConfig) ) + if ( _appSettings.AccountRolesByEmailRegisterOverwrite? + .TryGetValue(identifier, out var emailsForConfig) == true && + AccountRoles.GetAllRoles().Contains(emailsForConfig) ) { return emailsForConfig; } diff --git a/starsky/starsky.foundation.database/Helpers/StatusCodesHelper.cs b/starsky/starsky.foundation.database/Helpers/StatusCodesHelper.cs index f94bd19dbf..39bad585b3 100644 --- a/starsky/starsky.foundation.database/Helpers/StatusCodesHelper.cs +++ b/starsky/starsky.foundation.database/Helpers/StatusCodesHelper.cs @@ -16,12 +16,12 @@ public StatusCodesHelper(AppSettings appSettings) public FileIndexItem.ExifStatus IsReadOnlyStatus(FileIndexItem fileIndexItem) { - if (fileIndexItem.IsDirectory == true && _appSettings.IsReadOnly(fileIndexItem.FilePath)) + if (fileIndexItem.IsDirectory == true && _appSettings.IsReadOnly(fileIndexItem.FilePath!)) { return FileIndexItem.ExifStatus.DirReadOnly; } - if ( _appSettings.IsReadOnly(fileIndexItem.ParentDirectory) ) + if ( _appSettings.IsReadOnly(fileIndexItem.ParentDirectory!) ) { return FileIndexItem.ExifStatus.ReadOnly; } @@ -43,7 +43,7 @@ public FileIndexItem.ExifStatus IsReadOnlyStatus(DetailView? detailView) return FileIndexItem.ExifStatus.DirReadOnly; } - if ( _appSettings.IsReadOnly(detailView.FileIndexItem?.ParentDirectory) ) + if ( _appSettings.IsReadOnly(detailView.FileIndexItem?.ParentDirectory!) ) { return FileIndexItem.ExifStatus.ReadOnly; } diff --git a/starsky/starsky.foundation.databasetelemetry/Helpers/TelemetryConfigurationHelper.cs b/starsky/starsky.foundation.databasetelemetry/Helpers/TelemetryConfigurationHelper.cs index e1113e9bac..8f55828410 100644 --- a/starsky/starsky.foundation.databasetelemetry/Helpers/TelemetryConfigurationHelper.cs +++ b/starsky/starsky.foundation.databasetelemetry/Helpers/TelemetryConfigurationHelper.cs @@ -18,7 +18,7 @@ public static class TelemetryConfigurationHelper return null; } - public static TelemetryClient? InitTelemetryClient(string appInsightsConnectionString, string roleName, IWebLogger? logger, TelemetryClient? telemetryClient) + public static TelemetryClient? InitTelemetryClient(string? appInsightsConnectionString, string roleName, IWebLogger? logger, TelemetryClient? telemetryClient) { try { @@ -50,7 +50,7 @@ public static class TelemetryConfigurationHelper } } - private static TelemetryConfiguration? CreateTelemetryConfiguration(string appInsightsConnectionString) + private static TelemetryConfiguration? CreateTelemetryConfiguration(string? appInsightsConnectionString) { var telemetryConfiguration = TelemetryConfiguration.CreateDefault(); telemetryConfiguration.ConnectionString = appInsightsConnectionString; diff --git a/starsky/starsky.foundation.georealtime/Helpers/IntermediateModelConverter.cs b/starsky/starsky.foundation.georealtime/Helpers/IntermediateModelConverter.cs index 059fb552c5..a90e74b25e 100644 --- a/starsky/starsky.foundation.georealtime/Helpers/IntermediateModelConverter.cs +++ b/starsky/starsky.foundation.georealtime/Helpers/IntermediateModelConverter.cs @@ -4,6 +4,7 @@ using System.IO; using System.Linq; using System.Reflection; +using System.Text; using System.Xml; using starsky.foundation.georealtime.Models; using starsky.foundation.georealtime.Models.GeoJson; @@ -94,11 +95,16 @@ public static string ConvertToGpx(List waypoi var settings = new XmlWriterSettings { Indent = true, - IndentChars = " " + OmitXmlDeclaration = true, + Encoding = Encoding.UTF8 }; using var stringWriter = new StringWriter(); using var xmlWriter = XmlWriter.Create(stringWriter, settings); + + // Modify the encoding attribute in the XML declaration + xmlWriter.WriteRaw($""); + xmlWriter.WriteStartElement("gpx", "http://www.topografix.com/GPX/1/1"); xmlWriter.WriteAttributeString("version", "1.1"); xmlWriter.WriteAttributeString("creator", $"Starsky {Assembly.GetExecutingAssembly().GetName().Version?.ToString()}"); diff --git a/starsky/starsky.foundation.georealtime/Models/GeoJson/FeatureCollectionModel.cs b/starsky/starsky.foundation.georealtime/Models/GeoJson/FeatureCollectionModel.cs index 16fce9c0bf..d165cd97a0 100644 --- a/starsky/starsky.foundation.georealtime/Models/GeoJson/FeatureCollectionModel.cs +++ b/starsky/starsky.foundation.georealtime/Models/GeoJson/FeatureCollectionModel.cs @@ -5,5 +5,5 @@ namespace starsky.foundation.georealtime.Models.GeoJson; public class FeatureCollectionModel { public FeatureCollectionType? Type { get; set; } - public List Features { get; set; } + public List Features { get; set; } = new List(); } diff --git a/starsky/starsky.foundation.georealtime/Models/KmlImportOptions.cs b/starsky/starsky.foundation.georealtime/Models/KmlImportOptions.cs index 45a1437dc4..15f9bdbfd6 100644 --- a/starsky/starsky.foundation.georealtime/Models/KmlImportOptions.cs +++ b/starsky/starsky.foundation.georealtime/Models/KmlImportOptions.cs @@ -13,5 +13,5 @@ public class KmlImportOptions public bool OutputGeoJsonPoints { get; set; } = false; public bool OutputGpx { get; set; } = true; - public List Filter { get; set; } + public List Filter { get; set; } = new List(); } diff --git a/starsky/starsky.foundation.georealtime/Services/KmlImport.cs b/starsky/starsky.foundation.georealtime/Services/KmlImport.cs index 17b2c59da3..405f1d98c7 100644 --- a/starsky/starsky.foundation.georealtime/Services/KmlImport.cs +++ b/starsky/starsky.foundation.georealtime/Services/KmlImport.cs @@ -8,6 +8,8 @@ using starsky.foundation.georealtime.Interfaces; using starsky.foundation.georealtime.Models; using starsky.foundation.http.Interfaces; +using starsky.foundation.platform.Helpers; +using starsky.foundation.platform.Models; using starsky.foundation.storage.Interfaces; using starsky.foundation.storage.Storage; @@ -16,13 +18,18 @@ namespace starsky.foundation.georealtime.Services; public sealed class KmlImport : IKmlImport { private readonly IHttpClientHelper _httpClientHelper; + private readonly AppSettings _appSettings; private readonly IStorage _hostStorage; + private readonly IStorage _subStorage; - public KmlImport(IHttpClientHelper httpClientHelper, ISelectorStorage selectorStorage) + public KmlImport(IHttpClientHelper httpClientHelper, ISelectorStorage selectorStorage, AppSettings appSettings) { _httpClientHelper = httpClientHelper; + _appSettings = appSettings; _hostStorage = selectorStorage.Get(SelectorStorage.StorageServices.HostFilesystem); + _subStorage = + selectorStorage.Get(SelectorStorage.StorageServices.SubPath); } public async Task Import(string kmlPathOrUrl) @@ -31,15 +38,32 @@ public async Task Import(string kmlPathOrUrl) var readString = await _httpClientHelper.ReadString(kmlPathOrUrl); if ( !readString.Key ) { + // var existFile = _subStorage.ExistFile(kmlPathOrUrl); + // if ( existFile ) + // { + // readString = _subStorage.ReadStream(kmlPathOrUrl); + // } + // else + // { + // existFile = _hostStorage.ExistFile(kmlPathOrUrl); + // if ( existFile ) + // { + // readString = _hostStorage.ReadStream(kmlPathOrUrl); + // } + // } + return; } - var xDocument = XmlParse(readString.Value); - var result = Kml2IntermediateModelConverter.ParseKml(xDocument); - var model = IntermediateModelConverter.Covert2GeoJson(result, true); + var xDocument = XmlParseToXDocument(readString.Value); + var waypoints = Kml2IntermediateModelConverter.ParseKml(xDocument); + var model = IntermediateModelConverter.Covert2GeoJson(waypoints, true); + var resultGpx = IntermediateModelConverter.ConvertToGpx(waypoints); + + Console.WriteLine(); } - private static XDocument XmlParse(string content) + private static XDocument XmlParseToXDocument(string content) { return XDocument.Parse(content); } diff --git a/starsky/starsky.foundation.http/Interfaces/IHttpProvider.cs b/starsky/starsky.foundation.http/Interfaces/IHttpProvider.cs index 1ddbe365b0..8cfbd712cc 100644 --- a/starsky/starsky.foundation.http/Interfaces/IHttpProvider.cs +++ b/starsky/starsky.foundation.http/Interfaces/IHttpProvider.cs @@ -6,7 +6,7 @@ namespace starsky.foundation.http.Interfaces { public interface IHttpProvider { - Task GetAsync(string requestUri); + Task GetAsync(string requestUri, string authorization = ""); Task PostAsync(string requestUri, HttpContent? content); diff --git a/starsky/starsky.foundation.http/Services/HttpProvider.cs b/starsky/starsky.foundation.http/Services/HttpProvider.cs index 019ab01552..d9e3241d54 100644 --- a/starsky/starsky.foundation.http/Services/HttpProvider.cs +++ b/starsky/starsky.foundation.http/Services/HttpProvider.cs @@ -32,10 +32,15 @@ public HttpProvider(HttpClient httpClient) /// Get the Async results ///
/// https:// url + /// Excluded Basic or Bearer keyword /// Task with Response - public Task GetAsync(string requestUri) + public Task GetAsync(string requestUri, string authorization = "") { _httpClient.DefaultRequestHeaders.Add("User-Agent",UserAgent); + if ( !string.IsNullOrEmpty(authorization) ) + { + _httpClient.DefaultRequestHeaders.Add("Authorization", authorization); + } return _httpClient.GetAsync(requestUri, HttpCompletionOption.ResponseHeadersRead); } diff --git a/starsky/starsky.foundation.platform/Models/AppSettings.cs b/starsky/starsky.foundation.platform/Models/AppSettings.cs index c9482f726f..7a80bd8766 100644 --- a/starsky/starsky.foundation.platform/Models/AppSettings.cs +++ b/starsky/starsky.foundation.platform/Models/AppSettings.cs @@ -1,3 +1,4 @@ +#nullable enable using System.Text.Json.Serialization; using System; using System.Collections.Generic; @@ -167,7 +168,7 @@ private static string GetAppVersion() public DateTime AppVersionBuildDateTime => DateAssembly.GetBuildDate(Assembly.GetExecutingAssembly()); // Can be used in the cli session to select files out of the file database system - private string _storageFolder; + private string? _storageFolder; /// /// Main Storage provider on disk @@ -204,7 +205,7 @@ public bool IsVerbose() // Used in the webHtmlCli to store the log item name // used for the url - private string _name; + private string? _name; [PackageTelemetry] public string Name @@ -279,14 +280,14 @@ public enum DatabaseTypeList } // DatabaseType > above this one - private string _databaseConnection; + private string? _databaseConnection; /// /// Connection string for the database /// public string DatabaseConnection { - get { return _databaseConnection; } + get { return _databaseConnection ??= string.Empty; } set { var connection = ReplaceEnvironmentVariable(value); @@ -297,7 +298,7 @@ public string DatabaseConnection /// /// Internal Structure save location /// - private string _structure; + private string? _structure; /// /// Auto storage structure @@ -367,7 +368,7 @@ internal static TimeZoneInfo ConvertTimeZoneId(string value) } [JsonIgnore] - public TimeZoneInfo CameraTimeZoneInfo { get; set; } + public TimeZoneInfo CameraTimeZoneInfo { get; set; } = TimeZoneInfo.Utc; /// /// To Check if the structure is any good @@ -379,8 +380,8 @@ public static void StructureCheck(string structure) // Unescaped regex: // ^(\/.+)?\/([\/_ A-Z0-9*{}\.\\-]+(?=\.ext))\.ext$ - Regex structureRegex = new Regex( - "^(\\/.+)?\\/([\\/_ A-Z0-9*{}\\.\\\\-]+(?=\\.ext))\\.ext$", + var structureRegex = new Regex( + @"^(\/.+)?\/([\/_ A-Z0-9*{}\.\\-]+(?=\.ext))\.ext$", RegexOptions.IgnoreCase, TimeSpan.FromMilliseconds(300)); if (structureRegex.Match(structure).Success) return; @@ -391,14 +392,14 @@ public static void StructureCheck(string structure) /// /// Private: Location of storage of Thumbnails /// - private string _thumbnailTempFolder; + private string? _thumbnailTempFolder; /// /// Location of storage of Thumbnails /// public string ThumbnailTempFolder { - get => _thumbnailTempFolder; + get => _thumbnailTempFolder ??= string.Empty; set { var thumbnailTempFolder = ReplaceEnvironmentVariable(value); @@ -409,14 +410,14 @@ public string ThumbnailTempFolder /// /// Private: Location of temp folder /// - private string _tempFolder; + private string? _tempFolder; /// /// Location of temp folder /// public string TempFolder { - get => AssemblyDirectoryReplacer(_tempFolder); + get => AssemblyDirectoryReplacer(_tempFolder ??= string.Empty); set { var tempFolder = ReplaceEnvironmentVariable(value); @@ -427,14 +428,14 @@ public string TempFolder /// /// Private: Location of dependencies folder /// - private string _dependenciesFolder; + private string? _dependenciesFolder; /// /// Location of dependencies folder /// public string DependenciesFolder { - get => AssemblyDirectoryReplacer(_dependenciesFolder); + get => AssemblyDirectoryReplacer(_dependenciesFolder ??= string.Empty); set { var dependenciesFolder = ReplaceEnvironmentVariable(value); @@ -445,7 +446,7 @@ public string DependenciesFolder /// /// Private: Location of AppSettings Path /// - private string _appSettingsPathPrivate; + private string? _appSettingsPathPrivate; /// /// To store the settings by user in the AppData folder @@ -453,7 +454,7 @@ public string DependenciesFolder /// public string AppSettingsPath { - get => AssemblyDirectoryReplacer(_appSettingsPathPrivate); + get => AssemblyDirectoryReplacer(_appSettingsPathPrivate ??= string.Empty); // ReSharper disable once MemberCanBePrivate.Global set => _appSettingsPathPrivate = value; // set by ctor } @@ -466,7 +467,7 @@ public string AppSettingsPath /// /// Private Location of ExifTool.exe /// - private string ExifToolPathPrivate { get; set; } + private string? ExifToolPathPrivate { get; set; } /// /// Set in ctor on startup @@ -567,24 +568,20 @@ public bool IsReadOnly(string f) /// /// Internal location for webFtp credentials /// - private string _webFtp; + private string? _webFtp; /// /// Connection string for FTP /// public string WebFtp { - get - { - if ( string.IsNullOrEmpty(_webFtp) ) return string.Empty; - return _webFtp; - } + get => string.IsNullOrEmpty(_webFtp) ? string.Empty : _webFtp; set { // Anonymous FTP is not supported // Make sure that '@' in username is '%40' if ( string.IsNullOrEmpty(value) ) return; - Uri uriAddress = new Uri (value); + var uriAddress = new Uri (value); if ( uriAddress.UserInfo.Split(":".ToCharArray()).Length == 2 && uriAddress.Scheme == "ftp" && uriAddress.LocalPath.Length >= 1 ) @@ -605,7 +602,7 @@ public string WebFtp /// Publishing profiles used within the publishing module (Order by Key) /// [PackageTelemetry] - public Dictionary> PublishProfiles { + public Dictionary>? PublishProfiles { get => PublishProfilesPrivate; set { @@ -652,7 +649,7 @@ public Dictionary> PublishProfiles { /// /// { "demo@qdraw.nl": "Administrator" } /// - public Dictionary AccountRolesByEmailRegisterOverwrite { + public Dictionary? AccountRolesByEmailRegisterOverwrite { get => AccountRolesByEmailRegisterOverwritePrivate; init { @@ -674,12 +671,12 @@ public Dictionary AccountRolesByEmailRegisterOverwrite { /// /// Private storage for Application Insights InstrumentationKey /// - private string ApplicationInsightsConnectionStringPrivate { get; set; } = ""; + private string? ApplicationInsightsConnectionStringPrivate { get; set; } = ""; /// /// Insert the Application Insights Connection String here or use environment variable: APPLICATIONINSIGHTS_CONNECTION_STRING /// - public string ApplicationInsightsConnectionString { + public string? ApplicationInsightsConnectionString { get { if ( string.IsNullOrWhiteSpace(ApplicationInsightsConnectionStringPrivate) ) @@ -835,30 +832,59 @@ public bool? EnablePackageTelemetry [PackageTelemetry] public bool? UseSystemTrash { get; set; } + + /// /// Security whitelist to allow GET requests from other domains than the default list /// use env variable: app__AllowedHttpsDomains__0 - value /// Always use a domain name only, no paths and exclude https:// /// http is not supported due security reasons /// - public List AllowedHttpsDomains { get; set; } = - new List(); + public List AllowedHttpsDomains { get; set; } = new List(); - // ------------------------------------------------- - // ------------------- Modifiers ------------------- - // ------------------------------------------------- + /// + /// Stored data of external accounts + /// + private Dictionary ExternalAccountsPrivate { get; set; } = new Dictionary(); - private string AssemblyDirectoryReplacer(string value) - { - return value.Replace("{AssemblyDirectory}", BaseDirectoryProject); - } - /// - /// Used for CloneToDisplay + /// Information about external accounts /// - public const string CloneToDisplaySecurityWarning = - "warning: The field is not empty but for security reasons it is not shown"; + public Dictionary ExternalAccounts { + set + { + AppendAllowedHttpsDomains(value); + ExternalAccountsPrivate = value; + } + + get => ExternalAccountsPrivate; + } + private void AppendAllowedHttpsDomains( + Dictionary? value) + { + if(value == null) return; + + foreach ( var accountsKey in value.Keys ) + { + if ( value[accountsKey].BaseUrl == null || + !value[accountsKey].BaseUrl + .StartsWith("https://") ) + { + continue; + } + + var match = new Regex( + @"^(?:https?:)?(?:\/\/)?(?:[^@\n]+@)?(?:www\.)?([^:\/\n]+)") + .Match(value[accountsKey].BaseUrl); + var domain = match.Value.Replace("https://", string.Empty); + if ( !AllowedHttpsDomains.Contains(domain) ) + { + AllowedHttpsDomains.Add(domain); + } + } + } + /// /// For background task with lower priority e.g. thumbnails /// it skips the current task if the current process is to busy @@ -881,7 +907,22 @@ private string AssemblyDirectoryReplacer(string value) /// Recommended to to keep false /// public bool? ExiftoolSkipDownloadOnStartup { get; set; } = false; + + /// + /// Used for CloneToDisplay + /// + public const string CloneToDisplaySecurityWarning = + "warning: The field is not empty but for security reasons it is not shown"; + + // ------------------------------------------------- + // ------------------- Modifiers ------------------- + // ------------------------------------------------- + private string AssemblyDirectoryReplacer(string value) + { + return value.Replace("{AssemblyDirectory}", BaseDirectoryProject); + } + /// AppSettings duplicated /// /// Duplicate this item in memory. AND remove _databaseConnection @@ -953,11 +994,11 @@ private static void ReplaceAppSettingsPublishProfilesCloneToDisplay(AppSettingsP /// /// StorageFolders ends always with a backslash /// - /// in OS Style, StorageFolder ends with backslash + /// in OS Style, StorageFolder ends with backslash /// - public string FullPathToDatabaseStyle(string subpath) + public string FullPathToDatabaseStyle(string subPath) { - var databaseFilePath = subpath.Replace(StorageFolder, string.Empty); + var databaseFilePath = subPath.Replace(StorageFolder, string.Empty); databaseFilePath = _pathToDatabaseStyle(databaseFilePath); return PathHelper.PrefixDbSlash(databaseFilePath); @@ -989,7 +1030,7 @@ public IEnumerable> RenameListItemsToDbStyle(IEnu /// /// path to replace /// replaced output - private string _pathToDatabaseStyle(string subPath) + private static string _pathToDatabaseStyle(string subPath) { if (Path.DirectorySeparatorChar.ToString() == "\\") { @@ -1003,7 +1044,7 @@ private string _pathToDatabaseStyle(string subPath) /// /// path to replace /// replaced output - private string _pathToFilePathStyle(string subPath) + private static string _pathToFilePathStyle(string subPath) { if (Path.DirectorySeparatorChar.ToString() == "\\") { @@ -1018,7 +1059,7 @@ private string _pathToFilePathStyle(string subPath) /// databaseFilePath /// checkIfExist /// - public string DatabasePathToFilePath(string databaseFilePath, bool checkIfExist = true) + public string? DatabasePathToFilePath(string databaseFilePath, bool checkIfExist = true) { var filepath = StorageFolder + databaseFilePath; @@ -1055,7 +1096,7 @@ internal static string ReplaceEnvironmentVariable(string input) /// /// The 'DatabaseConnection' field is null or empty or /// missing Data Source in connection string - public string SqLiteFullPath(string connectionString, string baseDirectoryProject) + internal string SqLiteFullPath(string connectionString, string baseDirectoryProject) { if (DatabaseType == DatabaseTypeList.Mysql && string.IsNullOrWhiteSpace(connectionString)) throw new ArgumentException("The 'DatabaseConnection' field is null or empty"); diff --git a/starsky/starsky.foundation.platform/Models/AppSettingsExternalAccounts.cs b/starsky/starsky.foundation.platform/Models/AppSettingsExternalAccounts.cs new file mode 100644 index 0000000000..6c347603a5 --- /dev/null +++ b/starsky/starsky.foundation.platform/Models/AppSettingsExternalAccounts.cs @@ -0,0 +1,12 @@ +namespace starsky.foundation.platform.Models; + +public class AppSettingsExternalAccounts +{ + public string BaseUrl { get; set; } + + public string AuthenticationMethod { get; set; } + + public string Username { get; set; } + + public string Token { get; set; } +} diff --git a/starsky/starsky.foundation.storage/Storage/StorageSubPathFilesystem.cs b/starsky/starsky.foundation.storage/Storage/StorageSubPathFilesystem.cs index a31e741f7a..121f022fff 100644 --- a/starsky/starsky.foundation.storage/Storage/StorageSubPathFilesystem.cs +++ b/starsky/starsky.foundation.storage/Storage/StorageSubPathFilesystem.cs @@ -32,14 +32,14 @@ public StorageInfo Info(string path) { var subPath = _appSettings.DatabasePathToFilePath(path, false); - return new StorageHostFullPathFilesystem(_logger).Info(subPath); + return new StorageHostFullPathFilesystem(_logger).Info(subPath!); } public DateTime SetLastWriteTime(string path, DateTime? dateTime = null) { var subPath = _appSettings.DatabasePathToFilePath(path, false); - return new StorageHostFullPathFilesystem(_logger).SetLastWriteTime(subPath, dateTime); + return new StorageHostFullPathFilesystem(_logger).SetLastWriteTime(subPath!, dateTime); } /// @@ -73,7 +73,7 @@ public bool ExistFolder(string path) public FolderOrFileModel.FolderOrFileTypeList IsFolderOrFile(string path) { var fullFilePath = _appSettings.DatabasePathToFilePath(path,false); - return new StorageHostFullPathFilesystem(_logger).IsFolderOrFile(fullFilePath); + return new StorageHostFullPathFilesystem(_logger).IsFolderOrFile(fullFilePath!); } /// @@ -83,8 +83,8 @@ public FolderOrFileModel.FolderOrFileTypeList IsFolderOrFile(string path) /// toSubPath public void FolderMove(string fromPath, string toPath) { - var inputFileFullPath = _appSettings.DatabasePathToFilePath(fromPath, false); - var toFileFullPath = _appSettings.DatabasePathToFilePath(toPath, false); + var inputFileFullPath = _appSettings.DatabasePathToFilePath(fromPath, false)!; + var toFileFullPath = _appSettings.DatabasePathToFilePath(toPath, false)!; new StorageHostFullPathFilesystem(_logger).FolderMove(inputFileFullPath,toFileFullPath); } @@ -95,8 +95,8 @@ public void FolderMove(string fromPath, string toPath) /// toSubPath public void FileMove(string fromPath, string toPath) { - var inputFileFullPath = _appSettings.DatabasePathToFilePath(fromPath, false); - var toFileFullPath = _appSettings.DatabasePathToFilePath(toPath, false); + var inputFileFullPath = _appSettings.DatabasePathToFilePath(fromPath, false)!; + var toFileFullPath = _appSettings.DatabasePathToFilePath(toPath, false)!; new StorageHostFullPathFilesystem(_logger).FileMove(inputFileFullPath,toFileFullPath); } @@ -107,8 +107,8 @@ public void FileMove(string fromPath, string toPath) /// toSubPath public void FileCopy(string fromPath, string toPath) { - var inputFileFullPath = _appSettings.DatabasePathToFilePath(fromPath, false); - var toFileFullPath = _appSettings.DatabasePathToFilePath(toPath, false); + var inputFileFullPath = _appSettings.DatabasePathToFilePath(fromPath, false)!; + var toFileFullPath = _appSettings.DatabasePathToFilePath(toPath, false)!; new StorageHostFullPathFilesystem(_logger).FileCopy(inputFileFullPath,toFileFullPath); } @@ -119,7 +119,7 @@ public void FileCopy(string fromPath, string toPath) /// bool public bool FileDelete(string path) { - var inputFileFullPath = _appSettings.DatabasePathToFilePath(path, false); + var inputFileFullPath = _appSettings.DatabasePathToFilePath(path, false)!; return new StorageHostFullPathFilesystem(_logger).FileDelete(inputFileFullPath); } @@ -129,7 +129,7 @@ public bool FileDelete(string path) /// subPath location public void CreateDirectory(string path) { - var inputFileFullPath = _appSettings.DatabasePathToFilePath(path, false); + var inputFileFullPath = _appSettings.DatabasePathToFilePath(path, false)!; Directory.CreateDirectory(inputFileFullPath); } @@ -140,7 +140,7 @@ public void CreateDirectory(string path) /// bool public bool FolderDelete(string path) { - var inputFileFullPath = _appSettings.DatabasePathToFilePath(path, false); + var inputFileFullPath = _appSettings.DatabasePathToFilePath(path, false)!; return new StorageHostFullPathFilesystem(_logger).FolderDelete(inputFileFullPath); } @@ -241,7 +241,7 @@ public Stream ReadStream(string path, int maxRead = -1) Stream LocalGet() { - var fullFilePath = _appSettings.DatabasePathToFilePath(path,false); + var fullFilePath = _appSettings.DatabasePathToFilePath(path,false)!; var fileStream = new FileStream(fullFilePath, FileMode.Open, FileAccess.Read); if ( maxRead <= 1 ) { @@ -270,7 +270,7 @@ Stream LocalGet() public bool WriteStream(Stream stream, string path) { // should be able to write files that are not exist yet - var fullFilePath = _appSettings.DatabasePathToFilePath(path,false); + var fullFilePath = _appSettings.DatabasePathToFilePath(path,false)!; return new StorageHostFullPathFilesystem(_logger).WriteStream(stream, fullFilePath); } @@ -282,7 +282,7 @@ public bool WriteStreamOpenOrCreate(Stream stream, string path) public Task WriteStreamAsync(Stream stream, string path) { - var fullFilePath = _appSettings.DatabasePathToFilePath(path,false); + var fullFilePath = _appSettings.DatabasePathToFilePath(path,false)!; return new StorageHostFullPathFilesystem(_logger).WriteStreamAsync(stream, fullFilePath); } } diff --git a/starsky/starskytest/FakeMocks/FakeIHttpProvider.cs b/starsky/starskytest/FakeMocks/FakeIHttpProvider.cs index 5a17d0222e..af58ca8452 100644 --- a/starsky/starskytest/FakeMocks/FakeIHttpProvider.cs +++ b/starsky/starskytest/FakeMocks/FakeIHttpProvider.cs @@ -19,7 +19,7 @@ public FakeIHttpProvider(Dictionary? inputDictionary = null) _inputDictionary = inputDictionary; } - public Task GetAsync(string requestUri) + public Task GetAsync(string requestUri, string authorization = "") { UrlCalled.Add(requestUri); diff --git a/starsky/starskytest/starsky.foundation.georealtime/Services/KmlImportTest.cs b/starsky/starskytest/starsky.foundation.georealtime/Services/KmlImportTest.cs index 7dd8966a5a..be3ab9c5c1 100644 --- a/starsky/starskytest/starsky.foundation.georealtime/Services/KmlImportTest.cs +++ b/starsky/starskytest/starsky.foundation.georealtime/Services/KmlImportTest.cs @@ -44,7 +44,7 @@ public async Task Test() var httpClientHelper = new HttpClientHelper(fakeIHttpProvider, _serviceScopeFactory, new FakeIWebLogger(), new AppSettings{ AllowedHttpsDomains = new List{"share.garmin.com"}}); - await new KmlImport(httpClientHelper, new FakeSelectorStorage()).Import("https://share.garmin.com/Feed/Share/test"); + await new KmlImport(httpClientHelper, new FakeSelectorStorage(), new AppSettings()).Import("https://share.garmin.com/Feed/Share/test"); Console.WriteLine(); } diff --git a/starsky/starskytest/starsky.foundation.platform/Models/AppSettingsTest.cs b/starsky/starskytest/starsky.foundation.platform/Models/AppSettingsTest.cs index fd9ea35818..c17b52c319 100644 --- a/starsky/starskytest/starsky.foundation.platform/Models/AppSettingsTest.cs +++ b/starsky/starskytest/starsky.foundation.platform/Models/AppSettingsTest.cs @@ -1,11 +1,9 @@ using System; using System.Collections.Generic; using System.IO; -using System.Runtime.InteropServices; using Microsoft.Extensions.Configuration; using Microsoft.Extensions.DependencyInjection; using Microsoft.VisualStudio.TestTools.UnitTesting; -using Newtonsoft.Json.Linq; using starsky.foundation.platform.Extensions; using starsky.foundation.platform.Models; using starskytest.FakeCreateAn; @@ -406,7 +404,7 @@ public void AppSettings_IsReadOnly_NullNoItemTest() public void PublishProfiles_Null() { var appSettings = new AppSettings {PublishProfiles = null}; - Assert.AreEqual(0, appSettings.PublishProfiles.Count ); + Assert.AreEqual(0, appSettings.PublishProfiles?.Count ); } [TestMethod] @@ -487,5 +485,72 @@ public void AccountRolesByEmailRegisterOverwrite_ValidRole2() Assert.AreEqual(2,appSettings.AccountRolesByEmailRegisterOverwrite.Count); Assert.AreEqual("Administrator", appSettings.AccountRolesByEmailRegisterOverwrite["bogusEmail2"]); } + + [TestMethod] + public void ExternalAccountsNull() + { + var appSettings = new AppSettings {ExternalAccounts = null}; + Assert.AreEqual(0,appSettings.ExternalAccounts?.Count); + } + + [TestMethod] + public void ExternalAccounts_Should_Set_AllowedHttpsDomains() + { + + var appSettings = new AppSettings + { + ExternalAccounts = new Dictionary{{"test",new AppSettingsExternalAccounts + { + AuthenticationMethod = "Basic", + Token = "test", + Username = "test", + BaseUrl = "https://test.com" + }}} + }; + + Assert.AreEqual(1,appSettings.ExternalAccounts.Count); + + Assert.IsTrue(appSettings.AllowedHttpsDomains.Contains("test.com")); + } + + [TestMethod] + public void ExternalAccounts_Should_SkipHttp_AllowedHttpsDomains() + { + + var appSettings = new AppSettings + { + ExternalAccounts = new Dictionary{{"test",new AppSettingsExternalAccounts + { + AuthenticationMethod = "Basic", + Token = "test", + Username = "test", + BaseUrl = "http://www.w3.org/" // NO https + }}} + }; + + Assert.AreEqual(1,appSettings.ExternalAccounts.Count); + + Assert.IsFalse(appSettings.AllowedHttpsDomains.Contains("test.com")); + } + + [TestMethod] + public void ExternalAccounts_Should_SkipNull_AllowedHttpsDomains() + { + + var appSettings = new AppSettings + { + ExternalAccounts = new Dictionary{{"test",new AppSettingsExternalAccounts + { + AuthenticationMethod = "Basic", + Token = "test", + Username = "test", + BaseUrl = null + }}} + }; + + Assert.AreEqual(1,appSettings.ExternalAccounts.Count); + + Assert.IsFalse(appSettings.AllowedHttpsDomains.Contains(null)); + } } }