Browse Source

Merge branch 'feature/rest-api-updates' into develop

Fixes #43
Antony Male 10 years ago
parent
commit
4a4aa876eb
38 changed files with 372 additions and 138 deletions
  1. 1 0
      src/SyncTrayzor/Bootstrapper.cs
  2. 2 2
      src/SyncTrayzor/NotifyIcon/NotifyIconViewModel.cs
  3. 2 2
      src/SyncTrayzor/Pages/ShellViewModel.cs
  4. 2 2
      src/SyncTrayzor/Pages/ViewerViewModel.cs
  5. 1 1
      src/SyncTrayzor/SyncThing/ApiClient/Config.cs
  6. 16 2
      src/SyncTrayzor/SyncThing/ApiClient/Connections.cs
  7. 1 1
      src/SyncTrayzor/SyncThing/ApiClient/DeviceConnectedEvent.cs
  8. 1 1
      src/SyncTrayzor/SyncThing/ApiClient/DeviceDisconnectedEvent.cs
  9. 1 1
      src/SyncTrayzor/SyncThing/ApiClient/Event.cs
  10. 1 1
      src/SyncTrayzor/SyncThing/ApiClient/EventConverter.cs
  11. 1 1
      src/SyncTrayzor/SyncThing/ApiClient/EventType.cs
  12. 1 1
      src/SyncTrayzor/SyncThing/ApiClient/GenericEvent.cs
  13. 1 1
      src/SyncTrayzor/SyncThing/ApiClient/IEventVisitor.cs
  14. 23 0
      src/SyncTrayzor/SyncThing/ApiClient/ISyncThingApiClient.cs
  15. 3 3
      src/SyncTrayzor/SyncThing/ApiClient/ISyncThingApiV0p10.cs
  16. 43 0
      src/SyncTrayzor/SyncThing/ApiClient/ISyncThingApiV0p11.cs
  17. 1 1
      src/SyncTrayzor/SyncThing/ApiClient/Ignores.cs
  18. 1 1
      src/SyncTrayzor/SyncThing/ApiClient/ItemFinishedEvent.cs
  19. 1 1
      src/SyncTrayzor/SyncThing/ApiClient/ItemStartedEvent.cs
  20. 1 1
      src/SyncTrayzor/SyncThing/ApiClient/JsonCreationConverter.cs
  21. 1 1
      src/SyncTrayzor/SyncThing/ApiClient/LocalIndexUpdatedEvent.cs
  22. 1 1
      src/SyncTrayzor/SyncThing/ApiClient/RemoteIndexUpdatedEvent.cs
  23. 1 1
      src/SyncTrayzor/SyncThing/ApiClient/StartupCompleteEvent.cs
  24. 1 1
      src/SyncTrayzor/SyncThing/ApiClient/StateChangedEvent.cs
  25. 63 0
      src/SyncTrayzor/SyncThing/ApiClient/SyncThingApiClientFactory.cs
  26. 103 0
      src/SyncTrayzor/SyncThing/ApiClient/SyncThingApiClientV0p10.cs
  27. 7 39
      src/SyncTrayzor/SyncThing/ApiClient/SyncThingApiClientV0p11.cs
  28. 1 1
      src/SyncTrayzor/SyncThing/ApiClient/SyncthingVersion.cs
  29. 1 1
      src/SyncTrayzor/SyncThing/ApiClient/SystemInfo.cs
  30. 28 0
      src/SyncTrayzor/SyncThing/AuthenticatedHttpClientHandler.cs
  31. 1 1
      src/SyncTrayzor/SyncThing/EventWatcher/SyncThingEventWatcher.cs
  32. 1 0
      src/SyncTrayzor/SyncThing/EventWatcher/SyncThingEventWatcherFactory.cs
  33. 0 21
      src/SyncTrayzor/SyncThing/SyncThingApiClientFactory.cs
  34. 1 1
      src/SyncTrayzor/SyncThing/SyncThingConnectionsWatcher.cs
  35. 1 0
      src/SyncTrayzor/SyncThing/SyncThingConnectionsWatcherFactory.cs
  36. 29 23
      src/SyncTrayzor/SyncThing/SyncThingManager.cs
  37. 26 22
      src/SyncTrayzor/SyncTrayzor.csproj
  38. 2 2
      src/SyncTrayzor/Utils/SafeSyncthingExtensions.cs

+ 1 - 0
src/SyncTrayzor/Bootstrapper.cs

@@ -12,6 +12,7 @@ using SyncTrayzor.Services;
 using SyncTrayzor.Services.Config;
 using SyncTrayzor.Services.UpdateChecker;
 using SyncTrayzor.SyncThing;
+using SyncTrayzor.SyncThing.ApiClient;
 using SyncTrayzor.SyncThing.EventWatcher;
 using SyncTrayzor.Utils;
 using System;

+ 2 - 2
src/SyncTrayzor/NotifyIcon/NotifyIconViewModel.cs

@@ -90,9 +90,9 @@ namespace SyncTrayzor.NotifyIcon
         {
             get { return this.SyncThingState == SyncThingState.Stopped; }
         }
-        public void Start()
+        public async void Start()
         {
-            this.syncThingManager.StartWithErrorDialog(this.windowManager);
+            await this.syncThingManager.StartWithErrorDialogAsync(this.windowManager);
         }
 
         public bool CanStop

+ 2 - 2
src/SyncTrayzor/Pages/ShellViewModel.cs

@@ -64,9 +64,9 @@ namespace SyncTrayzor.Pages
         {
             get { return this.SyncThingState == SyncThingState.Stopped; }
         }
-        public void Start()
+        public async void Start()
         {
-            this.syncThingManager.StartWithErrorDialog(this.windowManager);
+            await this.syncThingManager.StartWithErrorDialogAsync(this.windowManager);
         }
 
         public bool CanStop

+ 2 - 2
src/SyncTrayzor/Pages/ViewerViewModel.cs

@@ -123,9 +123,9 @@ namespace SyncTrayzor.Pages
             //CefSharpHelper.TerminateCefSharpProcess();
         }
 
-        public void Start()
+        public async void Start()
         {
-            this.syncThingManager.StartWithErrorDialog(this.windowManager);
+            await this.syncThingManager.StartWithErrorDialogAsync(this.windowManager);
         }
 
         bool IRequestHandler.GetAuthCredentials(IWebBrowser browser, bool isProxy, string host, int port, string realm, string scheme, ref string username, ref string password)

+ 1 - 1
src/SyncTrayzor/SyncThing/Api/Config.cs → src/SyncTrayzor/SyncThing/ApiClient/Config.cs

@@ -5,7 +5,7 @@ using System.Linq;
 using System.Text;
 using System.Threading.Tasks;
 
-namespace SyncTrayzor.SyncThing.Api
+namespace SyncTrayzor.SyncThing.ApiClient
 {
     public class ConfigFolderDevice
     {

+ 16 - 2
src/SyncTrayzor/SyncThing/Api/Connections.cs → src/SyncTrayzor/SyncThing/ApiClient/Connections.cs

@@ -6,7 +6,7 @@ using System.Linq;
 using System.Text;
 using System.Threading.Tasks;
 
-namespace SyncTrayzor.SyncThing.Api
+namespace SyncTrayzor.SyncThing.ApiClient
 {
     public class ItemConnectionData
     {
@@ -26,7 +26,7 @@ namespace SyncTrayzor.SyncThing.Api
         public string ClientVersion { get; set; }
     }
 
-    public class Connections
+    public class ConnectionsV0p10
     {
         [JsonProperty("total")]
         public ItemConnectionData Total { get; set; }
@@ -49,4 +49,18 @@ namespace SyncTrayzor.SyncThing.Api
             }
         }
     }
+
+    public class Connections
+    {
+        [JsonProperty("total")]
+        public ItemConnectionData Total { get; set; }
+
+        [JsonProperty("connections")]
+        public Dictionary<string, ItemConnectionData> DeviceConnections { get; set; }
+
+        public Connections()
+        {
+            this.DeviceConnections = new Dictionary<string, ItemConnectionData>();
+        }
+    }
 }

+ 1 - 1
src/SyncTrayzor/SyncThing/Api/DeviceConnectedEvent.cs → src/SyncTrayzor/SyncThing/ApiClient/DeviceConnectedEvent.cs

@@ -5,7 +5,7 @@ using System.Linq;
 using System.Text;
 using System.Threading.Tasks;
 
-namespace SyncTrayzor.SyncThing.Api
+namespace SyncTrayzor.SyncThing.ApiClient
 {
     public class DeviceConnectedEventData
     {

+ 1 - 1
src/SyncTrayzor/SyncThing/Api/DeviceDisconnectedEvent.cs → src/SyncTrayzor/SyncThing/ApiClient/DeviceDisconnectedEvent.cs

@@ -5,7 +5,7 @@ using System.Linq;
 using System.Text;
 using System.Threading.Tasks;
 
-namespace SyncTrayzor.SyncThing.Api
+namespace SyncTrayzor.SyncThing.ApiClient
 {
     public class DeviceDisconnectedEventData
     {

+ 1 - 1
src/SyncTrayzor/SyncThing/Api/Event.cs → src/SyncTrayzor/SyncThing/ApiClient/Event.cs

@@ -5,7 +5,7 @@ using System.Linq;
 using System.Text;
 using System.Threading.Tasks;
 
-namespace SyncTrayzor.SyncThing.Api
+namespace SyncTrayzor.SyncThing.ApiClient
 {
     public abstract class Event
     {

+ 1 - 1
src/SyncTrayzor/SyncThing/Api/EventConverter.cs → src/SyncTrayzor/SyncThing/ApiClient/EventConverter.cs

@@ -6,7 +6,7 @@ using System.Linq;
 using System.Text;
 using System.Threading.Tasks;
 
-namespace SyncTrayzor.SyncThing.Api
+namespace SyncTrayzor.SyncThing.ApiClient
 {
     public class EventConverter : JsonCreationConverter<Event>
     {

+ 1 - 1
src/SyncTrayzor/SyncThing/Api/EventType.cs → src/SyncTrayzor/SyncThing/ApiClient/EventType.cs

@@ -6,7 +6,7 @@ using System.Linq;
 using System.Text;
 using System.Threading.Tasks;
 
-namespace SyncTrayzor.SyncThing.Api
+namespace SyncTrayzor.SyncThing.ApiClient
 {
     [JsonConverter(typeof(StringEnumConverter))]
     public enum EventType

+ 1 - 1
src/SyncTrayzor/SyncThing/Api/GenericEvent.cs → src/SyncTrayzor/SyncThing/ApiClient/GenericEvent.cs

@@ -4,7 +4,7 @@ using System.Linq;
 using System.Text;
 using System.Threading.Tasks;
 
-namespace SyncTrayzor.SyncThing.Api
+namespace SyncTrayzor.SyncThing.ApiClient
 {
     public class GenericEvent : Event
     {

+ 1 - 1
src/SyncTrayzor/SyncThing/Api/IEventVisitor.cs → src/SyncTrayzor/SyncThing/ApiClient/IEventVisitor.cs

@@ -4,7 +4,7 @@ using System.Linq;
 using System.Text;
 using System.Threading.Tasks;
 
-namespace SyncTrayzor.SyncThing.Api
+namespace SyncTrayzor.SyncThing.ApiClient
 {
     public interface IEventVisitor
     {

+ 23 - 0
src/SyncTrayzor/SyncThing/ApiClient/ISyncThingApiClient.cs

@@ -0,0 +1,23 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading;
+using System.Threading.Tasks;
+
+namespace SyncTrayzor.SyncThing.ApiClient
+{
+    public interface ISyncThingApiClient
+    {
+        Task ShutdownAsync();
+        Task<List<Event>> FetchEventsAsync(int since, int limit, CancellationToken cancellationToken);
+        Task<List<Event>> FetchEventsAsync(int since, CancellationToken cancellationToken);
+        Task<Config> FetchConfigAsync();
+        Task ScanAsync(string folderId, string subPath);
+        Task<SystemInfo> FetchSystemInfoAsync();
+        Task<Connections> FetchConnectionsAsync();
+        Task<SyncthingVersion> FetchVersionAsync();
+        Task<Ignores> FetchIgnoresAsync(string folderId);
+        Task RestartAsync();
+    }
+}

+ 3 - 3
src/SyncTrayzor/SyncThing/Api/ISyncThingApi.cs → src/SyncTrayzor/SyncThing/ApiClient/ISyncThingApiV0p10.cs

@@ -6,9 +6,9 @@ using System.Text;
 using System.Threading;
 using System.Threading.Tasks;
 
-namespace SyncTrayzor.SyncThing.Api
+namespace SyncTrayzor.SyncThing.ApiClient
 {
-    public interface ISyncThingApi
+    public interface ISyncThingApiV0p10
     {
         [Get("/rest/events")]
         Task<List<Event>> FetchEventsAsync(int since, CancellationToken cancellationToken);
@@ -29,7 +29,7 @@ namespace SyncTrayzor.SyncThing.Api
         Task<SystemInfo> FetchSystemInfoAsync();
 
         [Get("/rest/connections")]
-        Task<Connections> FetchConnectionsAsync();
+        Task<ConnectionsV0p10> FetchConnectionsAsync();
 
         [Get("/rest/version")]
         Task<SyncthingVersion> FetchVersionAsync();

+ 43 - 0
src/SyncTrayzor/SyncThing/ApiClient/ISyncThingApiV0p11.cs

@@ -0,0 +1,43 @@
+using Refit;
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading;
+using System.Threading.Tasks;
+
+namespace SyncTrayzor.SyncThing.ApiClient
+{
+    public interface ISyncThingApiV0p11
+    {
+        [Get("/rest/events")]
+        Task<List<Event>> FetchEventsAsync(int since, CancellationToken cancellationToken);
+
+        [Get("/rest/events")]
+        Task<List<Event>> FetchEventsLimitAsync(int since, int limit, CancellationToken cancellationToken);
+
+        [Get("/rest/system/config")]
+        Task<Config> FetchConfigAsync();
+
+        [Post("/rest/system/shutdown")]
+        Task ShutdownAsync();
+
+        [Post("/rest/db/scan")]
+        Task ScanAsync(string folder, string sub);
+
+        [Get("/rest/system/status")]
+        Task<SystemInfo> FetchSystemInfoAsync();
+
+        [Get("/rest/system/connections")]
+        Task<Connections> FetchConnectionsAsync();
+
+        [Get("/rest/system/version")]
+        Task<SyncthingVersion> FetchVersionAsync();
+
+        [Get("/rest/db/ignores")]
+        Task<Ignores> FetchIgnoresAsync(string folder);
+
+        [Post("/rest/system/restart")]
+        Task RestartAsync();
+    }
+}

+ 1 - 1
src/SyncTrayzor/SyncThing/Api/Ignores.cs → src/SyncTrayzor/SyncThing/ApiClient/Ignores.cs

@@ -5,7 +5,7 @@ using System.Linq;
 using System.Text;
 using System.Threading.Tasks;
 
-namespace SyncTrayzor.SyncThing.Api
+namespace SyncTrayzor.SyncThing.ApiClient
 {
     public class Ignores
     {

+ 1 - 1
src/SyncTrayzor/SyncThing/Api/ItemFinishedEvent.cs → src/SyncTrayzor/SyncThing/ApiClient/ItemFinishedEvent.cs

@@ -5,7 +5,7 @@ using System.Linq;
 using System.Text;
 using System.Threading.Tasks;
 
-namespace SyncTrayzor.SyncThing.Api
+namespace SyncTrayzor.SyncThing.ApiClient
 {
     public class ItemFinishedEventDataError
     {

+ 1 - 1
src/SyncTrayzor/SyncThing/Api/ItemStartedEvent.cs → src/SyncTrayzor/SyncThing/ApiClient/ItemStartedEvent.cs

@@ -5,7 +5,7 @@ using System.Linq;
 using System.Text;
 using System.Threading.Tasks;
 
-namespace SyncTrayzor.SyncThing.Api
+namespace SyncTrayzor.SyncThing.ApiClient
 {
     public class ItemStartedEventDetails
     {

+ 1 - 1
src/SyncTrayzor/SyncThing/Api/JsonCreationConverter.cs → src/SyncTrayzor/SyncThing/ApiClient/JsonCreationConverter.cs

@@ -7,7 +7,7 @@ using System.Text;
 using System.Threading.Tasks;
 using System.Reflection;
 
-namespace SyncTrayzor.SyncThing.Api
+namespace SyncTrayzor.SyncThing.ApiClient
 {
     /// <summary>Base Generic JSON Converter that can help quickly define converters for specific types by automatically
     /// generating the CanConvert, ReadJson, and WriteJson methods, requiring the implementer only to define a strongly typed Create method.</summary>

+ 1 - 1
src/SyncTrayzor/SyncThing/Api/LocalIndexUpdatedEvent.cs → src/SyncTrayzor/SyncThing/ApiClient/LocalIndexUpdatedEvent.cs

@@ -5,7 +5,7 @@ using System.Linq;
 using System.Text;
 using System.Threading.Tasks;
 
-namespace SyncTrayzor.SyncThing.Api
+namespace SyncTrayzor.SyncThing.ApiClient
 {
     public class LocalIndexUpdatedEventData
     {

+ 1 - 1
src/SyncTrayzor/SyncThing/Api/RemoteIndexUpdatedEvent.cs → src/SyncTrayzor/SyncThing/ApiClient/RemoteIndexUpdatedEvent.cs

@@ -5,7 +5,7 @@ using System.Linq;
 using System.Text;
 using System.Threading.Tasks;
 
-namespace SyncTrayzor.SyncThing.Api
+namespace SyncTrayzor.SyncThing.ApiClient
 {
     public class RemoteIndexUpdatedEventData
     {

+ 1 - 1
src/SyncTrayzor/SyncThing/Api/StartupCompleteEvent.cs → src/SyncTrayzor/SyncThing/ApiClient/StartupCompleteEvent.cs

@@ -4,7 +4,7 @@ using System.Linq;
 using System.Text;
 using System.Threading.Tasks;
 
-namespace SyncTrayzor.SyncThing.Api
+namespace SyncTrayzor.SyncThing.ApiClient
 {
     public class StartupCompleteEvent : Event
     {

+ 1 - 1
src/SyncTrayzor/SyncThing/Api/StateChangedEvent.cs → src/SyncTrayzor/SyncThing/ApiClient/StateChangedEvent.cs

@@ -5,7 +5,7 @@ using System.Linq;
 using System.Text;
 using System.Threading.Tasks;
 
-namespace SyncTrayzor.SyncThing.Api
+namespace SyncTrayzor.SyncThing.ApiClient
 {
     public class StateChangedEventData
     {

+ 63 - 0
src/SyncTrayzor/SyncThing/ApiClient/SyncThingApiClientFactory.cs

@@ -0,0 +1,63 @@
+using NLog;
+using Refit;
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Net;
+using System.Net.Http;
+using System.Text;
+using System.Threading;
+using System.Threading.Tasks;
+
+namespace SyncTrayzor.SyncThing.ApiClient
+{
+    public interface ISyncThingApiClientFactory
+    {
+        Task<ISyncThingApiClient> CreateCorrectApiClientAsync(Uri baseAddress, string apiKey, CancellationToken cancellationToken);
+    }
+
+    public class SyncThingApiClientFactory : ISyncThingApiClientFactory
+    {
+        private static readonly Logger logger = LogManager.GetCurrentClassLogger();
+
+        public async Task<ISyncThingApiClient> CreateCorrectApiClientAsync(Uri baseAddress, string apiKey, CancellationToken cancellationToken)
+        {
+            // This is a bit fugly - there's no way to determine which one we're talking to without trying a request and have it fail...
+            ISyncThingApiClient client = new SyncThingApiClientV0p10(baseAddress, apiKey);
+
+            // Time everything so we break out in about 10 seconds
+            int retryCount = 0;
+            while (true)
+            {
+                try
+                {
+                    logger.Debug("Attempting to request API using version 0.10.x API client");
+                    await client.FetchVersionAsync();
+                    break;
+                }
+                catch (HttpRequestException)
+                {
+                    logger.Debug("HttpRequestException {0} of 20", retryCount);
+                    // Expected when Syncthing's still starting
+                    if (retryCount >= 20)
+                        throw;
+                    retryCount++;
+                }
+                catch (ApiException e)
+                {
+                    if (e.StatusCode != HttpStatusCode.NotFound)
+                        throw;
+
+                    logger.Debug("404 with 0.10.x API client - defaulting to 0.11.x");
+                    client = new SyncThingApiClientV0p11(baseAddress, apiKey);
+                    break;
+                }
+
+                await Task.Delay(500, cancellationToken);
+                cancellationToken.ThrowIfCancellationRequested();
+            }
+
+            return client;
+        }
+    }
+}

+ 103 - 0
src/SyncTrayzor/SyncThing/ApiClient/SyncThingApiClientV0p10.cs

@@ -0,0 +1,103 @@
+using Newtonsoft.Json;
+using NLog;
+using Refit;
+using SyncTrayzor.SyncThing.ApiClient;
+using SyncTrayzor.Utils;
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Net.Http;
+using System.Text;
+using System.Threading;
+using System.Threading.Tasks;
+
+namespace SyncTrayzor.SyncThing.ApiClient
+{
+    public class SyncThingApiClientV0p10 : ISyncThingApiClient
+    {
+        private static readonly Logger logger = LogManager.GetLogger("SyncTrayzor.SyncThing.ApiClient.SyncThingApiClient");
+        private ISyncThingApiV0p10 api;
+
+        public SyncThingApiClientV0p10(Uri baseAddress, string apiKey)
+        {
+            var httpClient = new HttpClient(new AuthenticatedHttpClientHandler(apiKey))
+            {
+                BaseAddress = baseAddress.NormalizeZeroHost(),
+                Timeout = TimeSpan.FromSeconds(70),
+            };
+            this.api = RestService.For<ISyncThingApiV0p10>(httpClient, new RefitSettings()
+            {
+                JsonSerializerSettings = new JsonSerializerSettings()
+                {
+                    Converters = { new EventConverter() }
+                },
+            });
+        }
+
+        public Task ShutdownAsync()
+        {
+            logger.Info("Requesting API shutdown");
+            return this.api.ShutdownAsync();
+        }
+
+        public Task<List<Event>> FetchEventsAsync(int since, int limit, CancellationToken cancellationToken)
+        {
+            return this.api.FetchEventsLimitAsync(since, limit, cancellationToken);
+        }
+
+        public Task<List<Event>> FetchEventsAsync(int since, CancellationToken cancellationToken)
+        {
+            return this.api.FetchEventsAsync(since, cancellationToken);
+        }
+
+        public async Task<Config> FetchConfigAsync()
+        {
+            var config = await this.api.FetchConfigAsync();
+            logger.Debug("Fetched configuration: {0}", config);
+            return config;
+        }
+
+        public Task ScanAsync(string folderId, string subPath)
+        {
+            logger.Debug("Scanning folder: {0} subPath: {1}", folderId, subPath);
+            return this.api.ScanAsync(folderId, subPath);
+        }
+
+        public async Task<SystemInfo> FetchSystemInfoAsync()
+        {
+            var systemInfo = await this.api.FetchSystemInfoAsync();
+            logger.Debug("Fetched system info: {0}", systemInfo);
+            return systemInfo;
+        }
+
+        public async Task<Connections> FetchConnectionsAsync()
+        {
+            var v0p10Connections = await this.api.FetchConnectionsAsync();
+            return new Connections()
+            {
+                Total = v0p10Connections.Total,
+                DeviceConnections = v0p10Connections.DeviceConnections,
+            };
+        }
+
+        public async Task<SyncthingVersion> FetchVersionAsync()
+        {
+            var version = await this.api.FetchVersionAsync();
+            logger.Debug("Fetched version: {0}", version);
+            return version;
+        }
+
+        public async Task<Ignores> FetchIgnoresAsync(string folderId)
+        {
+            var ignores = await this.api.FetchIgnoresAsync(folderId);
+            logger.Debug("Fetched ignores for folderid {0}: {1}", folderId, ignores);
+            return ignores;
+        }
+
+        public Task RestartAsync()
+        {
+            logger.Debug("Restarting Syncthing");
+            return this.api.RestartAsync();
+        }
+    }
+}

+ 7 - 39
src/SyncTrayzor/SyncThing/SyncThingApiClient.cs → src/SyncTrayzor/SyncThing/ApiClient/SyncThingApiClientV0p11.cs

@@ -1,7 +1,7 @@
 using Newtonsoft.Json;
 using NLog;
 using Refit;
-using SyncTrayzor.SyncThing.Api;
+using SyncTrayzor.SyncThing.ApiClient;
 using SyncTrayzor.Utils;
 using System;
 using System.Collections.Generic;
@@ -11,35 +11,21 @@ using System.Text;
 using System.Threading;
 using System.Threading.Tasks;
 
-namespace SyncTrayzor.SyncThing
+namespace SyncTrayzor.SyncThing.ApiClient
 {
-    public interface ISyncThingApiClient
+    public class SyncThingApiClientV0p11 : ISyncThingApiClient
     {
-        Task ShutdownAsync();
-        Task<List<Event>> FetchEventsAsync(int since, int limit, CancellationToken cancellationToken);
-        Task<List<Event>> FetchEventsAsync(int since, CancellationToken cancellationToken);
-        Task<Config> FetchConfigAsync();
-        Task ScanAsync(string folderId, string subPath);
-        Task<SystemInfo> FetchSystemInfoAsync();
-        Task<Connections> FetchConnectionsAsync();
-        Task<SyncthingVersion> FetchVersionAsync();
-        Task<Ignores> FetchIgnoresAsync(string folderId);
-        Task RestartAsync();
-    }
-
-    public class SyncThingApiClient : ISyncThingApiClient
-    {
-        private static readonly Logger logger = LogManager.GetCurrentClassLogger();
-        private ISyncThingApi api;
+        private static readonly Logger logger = LogManager.GetLogger("SyncTrayzor.SyncThing.ApiClient.SyncThingApiClient");
+        private ISyncThingApiV0p11 api;
 
-        public SyncThingApiClient(Uri baseAddress, string apiKey)
+        public SyncThingApiClientV0p11(Uri baseAddress, string apiKey)
         {
             var httpClient = new HttpClient(new AuthenticatedHttpClientHandler(apiKey))
             {
                 BaseAddress = baseAddress.NormalizeZeroHost(),
                 Timeout = TimeSpan.FromSeconds(70),
             };
-            this.api = RestService.For<ISyncThingApi>(httpClient, new RefitSettings()
+            this.api = RestService.For<ISyncThingApiV0p11>(httpClient, new RefitSettings()
             {
                 JsonSerializerSettings = new JsonSerializerSettings()
                 {
@@ -108,23 +94,5 @@ namespace SyncTrayzor.SyncThing
             logger.Debug("Restarting Syncthing");
             return this.api.RestartAsync();
         }
-
-        private class AuthenticatedHttpClientHandler : WebRequestHandler
-        {
-            private readonly string apiKey;
-
-            public AuthenticatedHttpClientHandler(string apiKey)
-            {
-                this.apiKey = apiKey;
-                // We expect Syncthing to return invalid certs
-                this.ServerCertificateValidationCallback = (sender, certificate, chain, sslPolicyErrors) => true;
-            }
-
-            protected override Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, System.Threading.CancellationToken cancellationToken)
-            {
-                request.Headers.Add("X-API-Key", this.apiKey);
-                return base.SendAsync(request, cancellationToken);
-            }
-        }
     }
 }

+ 1 - 1
src/SyncTrayzor/SyncThing/Api/SyncthingVersion.cs → src/SyncTrayzor/SyncThing/ApiClient/SyncthingVersion.cs

@@ -5,7 +5,7 @@ using System.Linq;
 using System.Text;
 using System.Threading.Tasks;
 
-namespace SyncTrayzor.SyncThing.Api
+namespace SyncTrayzor.SyncThing.ApiClient
 {
     public class SyncthingVersion
     {

+ 1 - 1
src/SyncTrayzor/SyncThing/Api/SystemInfo.cs → src/SyncTrayzor/SyncThing/ApiClient/SystemInfo.cs

@@ -5,7 +5,7 @@ using System.Linq;
 using System.Text;
 using System.Threading.Tasks;
 
-namespace SyncTrayzor.SyncThing.Api
+namespace SyncTrayzor.SyncThing.ApiClient
 {
     public class SystemInfo
     {

+ 28 - 0
src/SyncTrayzor/SyncThing/AuthenticatedHttpClientHandler.cs

@@ -0,0 +1,28 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Net.Http;
+using System.Text;
+using System.Threading;
+using System.Threading.Tasks;
+
+namespace SyncTrayzor.SyncThing
+{
+    public class AuthenticatedHttpClientHandler : WebRequestHandler
+    {
+        private readonly string apiKey;
+
+        public AuthenticatedHttpClientHandler(string apiKey)
+        {
+            this.apiKey = apiKey;
+            // We expect Syncthing to return invalid certs
+            this.ServerCertificateValidationCallback = (sender, certificate, chain, sslPolicyErrors) => true;
+        }
+
+        protected override Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)
+        {
+            request.Headers.Add("X-API-Key", this.apiKey);
+            return base.SendAsync(request, cancellationToken);
+        }
+    }
+}

+ 1 - 1
src/SyncTrayzor/SyncThing/EventWatcher/SyncThingEventWatcher.cs

@@ -1,5 +1,5 @@
 using NLog;
-using SyncTrayzor.SyncThing.Api;
+using SyncTrayzor.SyncThing.ApiClient;
 using System;
 using System.Collections.Generic;
 using System.IO;

+ 1 - 0
src/SyncTrayzor/SyncThing/EventWatcher/SyncThingEventWatcherFactory.cs

@@ -1,4 +1,5 @@
 using SyncTrayzor.SyncThing;
+using SyncTrayzor.SyncThing.ApiClient;
 using SyncTrayzor.SyncThing.EventWatcher;
 using System;
 using System.Collections.Generic;

+ 0 - 21
src/SyncTrayzor/SyncThing/SyncThingApiClientFactory.cs

@@ -1,21 +0,0 @@
-using System;
-using System.Collections.Generic;
-using System.Linq;
-using System.Text;
-using System.Threading.Tasks;
-
-namespace SyncTrayzor.SyncThing
-{
-    public interface ISyncThingApiClientFactory
-    {
-        ISyncThingApiClient CreateApiClient(Uri baseAddress, string apiKey);
-    }
-
-    public class SyncThingApiClientFactory : ISyncThingApiClientFactory
-    {
-        public ISyncThingApiClient CreateApiClient(Uri baseAddress, string apiKey)
-        {
-            return new SyncThingApiClient(baseAddress, apiKey);
-        }
-    }
-}

+ 1 - 1
src/SyncTrayzor/SyncThing/SyncThingConnectionsWatcher.cs

@@ -1,4 +1,4 @@
-using SyncTrayzor.SyncThing.Api;
+using SyncTrayzor.SyncThing.ApiClient;
 using System;
 using System.Collections.Generic;
 using System.Linq;

+ 1 - 0
src/SyncTrayzor/SyncThing/SyncThingConnectionsWatcherFactory.cs

@@ -1,5 +1,6 @@
 using StyletIoC;
 using SyncTrayzor.SyncThing;
+using SyncTrayzor.SyncThing.ApiClient;
 using System;
 using System.Collections.Generic;
 using System.Linq;

+ 29 - 23
src/SyncTrayzor/SyncThing/SyncThingManager.cs

@@ -1,5 +1,5 @@
 using NLog;
-using SyncTrayzor.SyncThing.Api;
+using SyncTrayzor.SyncThing.ApiClient;
 using SyncTrayzor.SyncThing.EventWatcher;
 using SyncTrayzor.Utils;
 using System;
@@ -41,7 +41,7 @@ namespace SyncTrayzor.SyncThing
         DateTime LastConnectivityEventTime { get; }
         SyncthingVersion Version { get; }
 
-        void Start();
+        Task StartAsync();
         Task StopAsync();
         Task RestartAsync();
         void Kill();
@@ -178,12 +178,13 @@ namespace SyncTrayzor.SyncThing
             };
         }
 
-        public void Start()
+        public async Task StartAsync()
         {
             try
             {
                 this.apiAbortCts = new CancellationTokenSource();
                 this.processRunner.Start();
+                await this.StartApiClientsAsync();
             }
             catch (Exception e)
             {
@@ -258,13 +259,13 @@ namespace SyncTrayzor.SyncThing
         private void SetState(SyncThingState state)
         {
             SyncThingState oldState;
+            bool abortApi = false;
             lock (this.stateLock)
             {
                 if (state == this._state)
                     return;
 
                 oldState = this._state;
-
                 // We really need a proper state machine here....
                 // There's a race if Syncthing can't start because the database is locked by another process on the same port
                 // In this case, we see the process as having failed, but the event watcher chimes in a split-second later with the 'Started' event.
@@ -274,20 +275,25 @@ namespace SyncTrayzor.SyncThing
                     return;
 
                 if ((this._state == SyncThingState.Running || this._state == SyncThingState.Starting) && state == SyncThingState.Stopped)
-                    this.apiAbortCts.Cancel();
+                    abortApi = true;
 
                 this._state = state;
             }
 
-            this.UpdateWatchersState(oldState, state);
+            if (abortApi)
+            {
+                this.apiAbortCts.Cancel();
+                this.StopApiClients();
+            }
+
             this.eventDispatcher.Raise(this.StateChanged, new SyncThingStateChangedEventArgs(oldState, state));
         }
 
-        private void UpdateWatchersState(SyncThingState oldState, SyncThingState newState)
+        private async Task StartApiClientsAsync()
         {
-            if (newState == SyncThingState.Starting || (oldState != SyncThingState.Starting && newState == SyncThingState.Running))
+            try
             {
-                this.apiClient = this.apiClientFactory.CreateApiClient(this.Address, this.ApiKey);
+                this.apiClient = await this.apiClientFactory.CreateCorrectApiClientAsync(this.Address, this.ApiKey, this.apiAbortCts.Token);
 
                 if (this.connectionsWatcher != null)
                     this.connectionsWatcher.Dispose();
@@ -306,18 +312,21 @@ namespace SyncTrayzor.SyncThing
                 this.eventWatcher.DeviceDisconnected += (o, e) => this.OnDeviceDisconnected(e);
                 this.eventWatcher.Running = true;
             }
-            else if (newState == SyncThingState.Stopping || newState == SyncThingState.Stopped)
-            {
-                this.apiClient = null;
+            catch (OperationCanceledException)
+            { }
+        }
 
-                if (this.connectionsWatcher != null)
-                    this.connectionsWatcher.Dispose();
-                this.connectionsWatcher = null;
+        private void StopApiClients()
+        {
+            this.apiClient = null;
 
-                if (this.eventWatcher != null)
-                    this.eventWatcher.Dispose();
-                this.eventWatcher = null;
-            }
+            if (this.connectionsWatcher != null)
+                this.connectionsWatcher.Dispose();
+            this.connectionsWatcher = null;
+
+            if (this.eventWatcher != null)
+                this.eventWatcher.Dispose();
+            this.eventWatcher = null;
         }
 
         private void ProcessStopped(SyncThingExitStatus exitStatus)
@@ -465,10 +474,7 @@ namespace SyncTrayzor.SyncThing
         public void Dispose()
         {
             this.processRunner.Dispose();
-            if (this.connectionsWatcher != null)
-                this.connectionsWatcher.Dispose();
-            if (this.eventWatcher != null)
-                this.eventWatcher.Dispose();
+            this.StopApiClients();
         }
     }
 }

+ 26 - 22
src/SyncTrayzor/SyncTrayzor.csproj

@@ -120,7 +120,11 @@
       <DependentUpon>App.xaml</DependentUpon>
       <SubType>Code</SubType>
     </Compile>
-    <Compile Include="SyncThing\SyncThingApiClientFactory.cs" />
+    <Compile Include="SyncThing\ApiClient\ISyncThingApiClient.cs" />
+    <Compile Include="SyncThing\ApiClient\ISyncThingApiV0p11.cs" />
+    <Compile Include="SyncThing\ApiClient\SyncThingApiClientFactory.cs" />
+    <Compile Include="SyncThing\ApiClient\SyncThingApiClientV0p11.cs" />
+    <Compile Include="SyncThing\AuthenticatedHttpClientHandler.cs" />
     <Compile Include="SyncThing\SyncThingConnectionsWatcherFactory.cs" />
     <Compile Include="SyncThing\EventWatcher\SyncThingEventWatcherFactory.cs" />
     <Compile Include="Localization\LocalizeConverter.cs" />
@@ -171,25 +175,25 @@
     <Compile Include="Services\UpdateChecker\Release.cs" />
     <Compile Include="Services\UpdateChecker\ReleaseResponse.cs" />
     <Compile Include="Services\WatchedFolderMonitor.cs" />
-    <Compile Include="SyncThing\Api\Config.cs" />
-    <Compile Include="SyncThing\Api\Connections.cs" />
-    <Compile Include="SyncThing\Api\DeviceConnectedEvent.cs" />
-    <Compile Include="SyncThing\Api\DeviceDisconnectedEvent.cs" />
-    <Compile Include="SyncThing\Api\Event.cs" />
-    <Compile Include="SyncThing\Api\EventConverter.cs" />
-    <Compile Include="SyncThing\Api\EventType.cs" />
-    <Compile Include="SyncThing\Api\GenericEvent.cs" />
-    <Compile Include="SyncThing\Api\IEventVisitor.cs" />
-    <Compile Include="SyncThing\Api\Ignores.cs" />
-    <Compile Include="SyncThing\Api\ItemFinishedEvent.cs" />
-    <Compile Include="SyncThing\Api\ItemStartedEvent.cs" />
-    <Compile Include="SyncThing\Api\JsonCreationConverter.cs" />
-    <Compile Include="SyncThing\Api\LocalIndexUpdatedEvent.cs" />
-    <Compile Include="SyncThing\Api\RemoteIndexUpdatedEvent.cs" />
-    <Compile Include="SyncThing\Api\StartupCompleteEvent.cs" />
-    <Compile Include="SyncThing\Api\StateChangedEvent.cs" />
-    <Compile Include="SyncThing\Api\SyncthingVersion.cs" />
-    <Compile Include="SyncThing\Api\SystemInfo.cs" />
+    <Compile Include="SyncThing\ApiClient\Config.cs" />
+    <Compile Include="SyncThing\ApiClient\Connections.cs" />
+    <Compile Include="SyncThing\ApiClient\DeviceConnectedEvent.cs" />
+    <Compile Include="SyncThing\ApiClient\DeviceDisconnectedEvent.cs" />
+    <Compile Include="SyncThing\ApiClient\Event.cs" />
+    <Compile Include="SyncThing\ApiClient\EventConverter.cs" />
+    <Compile Include="SyncThing\ApiClient\EventType.cs" />
+    <Compile Include="SyncThing\ApiClient\GenericEvent.cs" />
+    <Compile Include="SyncThing\ApiClient\IEventVisitor.cs" />
+    <Compile Include="SyncThing\ApiClient\Ignores.cs" />
+    <Compile Include="SyncThing\ApiClient\ItemFinishedEvent.cs" />
+    <Compile Include="SyncThing\ApiClient\ItemStartedEvent.cs" />
+    <Compile Include="SyncThing\ApiClient\JsonCreationConverter.cs" />
+    <Compile Include="SyncThing\ApiClient\LocalIndexUpdatedEvent.cs" />
+    <Compile Include="SyncThing\ApiClient\RemoteIndexUpdatedEvent.cs" />
+    <Compile Include="SyncThing\ApiClient\StartupCompleteEvent.cs" />
+    <Compile Include="SyncThing\ApiClient\StateChangedEvent.cs" />
+    <Compile Include="SyncThing\ApiClient\SyncthingVersion.cs" />
+    <Compile Include="SyncThing\ApiClient\SystemInfo.cs" />
     <Compile Include="SyncThing\Device.cs" />
     <Compile Include="SyncThing\DeviceConnectedEventArgs.cs" />
     <Compile Include="SyncThing\DeviceDisconnectedEventArgs.cs" />
@@ -199,9 +203,9 @@
     <Compile Include="SyncThing\Folder.cs" />
     <Compile Include="SyncThing\FolderSyncStateChangeEventArgs.cs" />
     <Compile Include="SyncThing\MessageLoggedEventArgs.cs" />
-    <Compile Include="SyncThing\Api\ISyncThingApi.cs" />
+    <Compile Include="SyncThing\ApiClient\ISyncThingApiV0p10.cs" />
     <Compile Include="SyncThing\SyncStateChangedEventArgs.cs" />
-    <Compile Include="SyncThing\SyncThingApiClient.cs" />
+    <Compile Include="SyncThing\ApiClient\SyncThingApiClientV0p10.cs" />
     <Compile Include="SyncThing\SyncThingConnectionStats.cs" />
     <Compile Include="SyncThing\SyncThingConnectionsWatcher.cs" />
     <Compile Include="SyncThing\EventWatcher\SyncThingEventWatcher.cs" />

+ 2 - 2
src/SyncTrayzor/Utils/SafeSyncthingExtensions.cs

@@ -13,11 +13,11 @@ namespace SyncTrayzor.Utils
 {
     public static class SafeSyncthingExtensions
     {
-        public static void StartWithErrorDialog(this ISyncThingManager syncThingManager, IWindowManager windowManager)
+        public static async Task StartWithErrorDialogAsync(this ISyncThingManager syncThingManager, IWindowManager windowManager)
         {
             try
             {
-                syncThingManager.Start();
+                await syncThingManager.StartAsync();
             }
             catch (Win32Exception e)
             {